feat(render+admin): stop a render job (admin, any owner)
Build backend images / build content-svc (push) Failing after 1m0s
Build backend images / build file-svc (push) Failing after 1m3s
Build backend images / build gateway (push) Failing after 1m2s
Build backend images / build identity-svc (push) Failing after 1m20s
Build backend images / build notification-svc (push) Failing after 1m13s
Build backend images / build render-svc (push) Failing after 1m5s
Build backend images / build studio-svc (push) Failing after 1m0s
Build backend images / build content-svc (push) Failing after 1m0s
Build backend images / build file-svc (push) Failing after 1m3s
Build backend images / build gateway (push) Failing after 1m2s
Build backend images / build identity-svc (push) Failing after 1m20s
Build backend images / build notification-svc (push) Failing after 1m13s
Build backend images / build render-svc (push) Failing after 1m5s
Build backend images / build studio-svc (push) Failing after 1m0s
The render-queue cancel button used the owner-scoped /cancel (WHERE user_id=…),
so an admin couldn't stop another user's job. Added:
- render-svc: POST /v1/renders/:job_id/stop (admin-gated) → store.StopJob cancels
any in-progress job regardless of owner and frees the assigned node
- admin: render-queue button now "توقف" → /api/admin/renders/{id}/stop (with confirm)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
import { type NextRequest } from "next/server";
|
||||
import { adminProxy } from "@/app/api/admin/_adminProxy";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
interface Ctx { params: { jobId: string } }
|
||||
|
||||
export async function POST(req: NextRequest, { params }: Ctx) {
|
||||
return adminProxy(req, `/v1/renders/${params.jobId}/stop`);
|
||||
}
|
||||
@@ -48,10 +48,12 @@ export function RenderQueueTable({ jobs }: { jobs: V2RenderJob[] }) {
|
||||
}
|
||||
};
|
||||
|
||||
const cancelJob = async (jobId: string) => {
|
||||
const stopJob = async (jobId: string) => {
|
||||
if (!confirm("این رندر متوقف شود؟")) return;
|
||||
setLoading((p) => ({ ...p, [jobId]: true }));
|
||||
try {
|
||||
await apiFetch(`/api/admin/renders/${jobId}/cancel`, { method: "POST" });
|
||||
// Admin stop — cancels any user's job (not just the caller's) and frees the node.
|
||||
await apiFetch(`/api/admin/renders/${jobId}/stop`, { method: "POST" });
|
||||
router.refresh();
|
||||
} finally {
|
||||
setLoading((p) => ({ ...p, [jobId]: false }));
|
||||
@@ -140,11 +142,11 @@ export function RenderQueueTable({ jobs }: { jobs: V2RenderJob[] }) {
|
||||
)}
|
||||
{canCancel && (
|
||||
<button
|
||||
onClick={() => cancelJob(job.id)}
|
||||
onClick={() => stopJob(job.id)}
|
||||
disabled={loading[job.id]}
|
||||
className="rounded px-2.5 py-1 text-xs text-red-300 border border-red-500/30 hover:bg-red-500/10 disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{t("actionCancel")}
|
||||
{t("actionStop")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user