fix(admin): auto-promote uploaded AEP to the render bucket on attach

Uploading an .aep/.zip in the template editor only set content.projects.aep_file_url
(a user-uploads reference) — it never copied the file to templates/{id}/ where the
render node-agent's claim looks. Result: uploaded templates weren't renderable.

attachAep now also POSTs /v1/template-bundles/{project_id} {source_url} after saving
the reference, which server-side-copies the file into templates/{id}/(bundle.zip|
template.aep). Uploading a template now makes it immediately renderable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-06 19:53:54 +03:30
parent 076c2e577f
commit f8631fbbc4
+23 -3
View File
@@ -103,15 +103,35 @@ export function TemplatesAdmin() {
setSavingProj(null); setSavingProj(null);
}; };
// Attach an uploaded After Effects file (+ composition) to a project. // Attach an uploaded After Effects file (+ composition) to a project, then PROMOTE
// it into the render bucket so render nodes can actually use it. Two steps:
// 1. PATCH /projects/{id}/aep — record aep_file_url + comp on the content project
// 2. POST /template-bundles/{id} — copy the uploaded .aep/.zip into
// templates/{id}/(bundle.zip|template.aep) where the node-agent claim looks.
// Without step 2 the upload is only a reference and renders fail / fall back to mock.
const attachAep = async (p: Proj, url: string) => { const attachAep = async (p: Proj, url: string) => {
setSavingProj(p.id); setError(null); setSavingProj(p.id); setError(null);
const res = await fetch(api(`projects/${p.id}/aep`), { const res = await fetch(api(`projects/${p.id}/aep`), {
method: "PATCH", headers: { "Content-Type": "application/json" }, method: "PATCH", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ aep_file_url: url, render_aep_comp: p.render_aep_comp || "flatrender" }), body: JSON.stringify({ aep_file_url: url, render_aep_comp: p.render_aep_comp || "flatrender" }),
}); });
if (res.ok) { updateProj(p.id, { aep_file_url: url }); } if (!res.ok) {
else { const d = await res.json().catch(() => null); setError(d?.error ?? "اتصال فایل AE ناموفق بود"); } const d = await res.json().catch(() => null);
setError(d?.error ?? "اتصال فایل AE ناموفق بود");
setSavingProj(null);
return;
}
updateProj(p.id, { aep_file_url: url });
// Promote to the render templates bucket (makes it renderable on nodes).
const promote = await fetch(api(`template-bundles/${p.id}`), {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ source_url: url }),
});
if (!promote.ok) {
const d = await promote.json().catch(() => null);
setError(d?.message ?? d?.error ?? "آماده‌سازی فایل برای رندر ناموفق بود (فایل ذخیره شد ولی هنوز قابل رندر نیست).");
}
setSavingProj(null); setSavingProj(null);
}; };