From f8631fbbc42fa7af0c69a90a15703a0e4d727e88 Mon Sep 17 00:00:00 2001 From: "soroush.asadi" Date: Sat, 6 Jun 2026 19:53:54 +0330 Subject: [PATCH] fix(admin): auto-promote uploaded AEP to the render bucket on attach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/components/admin/TemplatesAdmin.tsx | 26 ++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/components/admin/TemplatesAdmin.tsx b/src/components/admin/TemplatesAdmin.tsx index 4ba2b62..bc98133 100644 --- a/src/components/admin/TemplatesAdmin.tsx +++ b/src/components/admin/TemplatesAdmin.tsx @@ -103,15 +103,35 @@ export function TemplatesAdmin() { 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) => { setSavingProj(p.id); setError(null); const res = await fetch(api(`projects/${p.id}/aep`), { method: "PATCH", headers: { "Content-Type": "application/json" }, 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 }); } - else { const d = await res.json().catch(() => null); setError(d?.error ?? "اتصال فایل AE ناموفق بود"); } + if (!res.ok) { + 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); };