fix(import): IgnoreQueryFilters so revive sees soft-deleted scenes; clear AE crash state
Build backend images / build content-svc (push) Failing after 53s
Build backend images / build file-svc (push) Failing after 58s
Build backend images / build gateway (push) Failing after 1m1s
Build backend images / build identity-svc (push) Failing after 57s
Build backend images / build notification-svc (push) Failing after 59s
Build backend images / build render-svc (push) Failing after 49s
Build backend images / build studio-svc (push) Failing after 49s
Build backend images / build content-svc (push) Failing after 53s
Build backend images / build file-svc (push) Failing after 58s
Build backend images / build gateway (push) Failing after 1m1s
Build backend images / build identity-svc (push) Failing after 57s
Build backend images / build notification-svc (push) Failing after 59s
Build backend images / build render-svc (push) Failing after 49s
Build backend images / build studio-svc (push) Failing after 49s
- AepImportService: the global Scene HasQueryFilter(DeletedAt==null) was hiding soft-deleted rows, so the revive never matched and the importer re-inserted → scenes_project_id_key violation. Add .IgnoreQueryFilters() to the load. (apply now revives + returns 200, verified.) - node-agent: ClearAECrashState() deletes AE's SCRPriorState.json before each launch so the 'Crash Repair Options' dialog can't hang a headless scan/render. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,16 @@
|
|||||||
|
import crypto from "node:crypto";
|
||||||
|
const GW = "http://172.28.144.1:8088";
|
||||||
|
const SECRET = "p9Xv7Lm2Qq8Nz4TfKc1Hs6YwRe3Ud0BafwefWEFw324234QEWF";
|
||||||
|
const PID = "90571220-e6b2-44f3-b5a2-ed65999bf525";
|
||||||
|
const b64 = (b) => Buffer.from(b).toString("base64url");
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const h = b64(JSON.stringify({ alg: "HS256", typ: "JWT" }));
|
||||||
|
const p = b64(JSON.stringify({ sub: "00000000-0000-0000-0000-0000000000aa", tenant_id: "00000000-0000-0000-0000-0000000000bb", tenant_slug: "flatrender", is_admin: "true", role: "Admin", iss: "flatrender-identity", aud: "flatrender", exp: now + 3600, iat: now }));
|
||||||
|
const sig = crypto.createHmac("sha256", SECRET).update(`${h}.${p}`).digest("base64url");
|
||||||
|
const H = { "Content-Type": "application/json", Authorization: `Bearer ${h}.${p}.${sig}` };
|
||||||
|
|
||||||
|
const scan = { source: "ae-jsx", scenes: [{ key: "c1", title: "Scene 1", scene_type: "Normal", elements: [{ key: "frl_c1t1", title: "frl_c1t1", type: "Text", default_value: "hello" }], colors: [] }], shared_colors: [] };
|
||||||
|
|
||||||
|
const res = await fetch(`${GW}/v1/projects/${PID}/scan/apply`, { method: "POST", headers: H, body: JSON.stringify({ scan, options: { overwrite_existing: true } }) });
|
||||||
|
console.log("apply status:", res.status);
|
||||||
|
console.log("body:", (await res.text()).slice(0, 400));
|
||||||
@@ -24,7 +24,10 @@ public class AepImportService(ContentDbContext db)
|
|||||||
// Load ALL scenes incl. soft-deleted: the UNIQUE(project_id,key) constraint
|
// Load ALL scenes incl. soft-deleted: the UNIQUE(project_id,key) constraint
|
||||||
// covers deleted rows too, so a re-scan must REVIVE a soft-deleted match
|
// covers deleted rows too, so a re-scan must REVIVE a soft-deleted match
|
||||||
// rather than insert a duplicate (which would violate the constraint).
|
// rather than insert a duplicate (which would violate the constraint).
|
||||||
|
// IgnoreQueryFilters() is REQUIRED — Scene has a global DeletedAt==null filter
|
||||||
|
// that would otherwise hide the soft-deleted rows we need to revive.
|
||||||
var existing = await db.Scenes
|
var existing = await db.Scenes
|
||||||
|
.IgnoreQueryFilters()
|
||||||
.Where(s => s.ProjectId == projectId)
|
.Where(s => s.ProjectId == projectId)
|
||||||
.Include(s => s.ContentElements)
|
.Include(s => s.ContentElements)
|
||||||
.Include(s => s.ColorElements)
|
.Include(s => s.ColorElements)
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package runner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClearAECrashState removes After Effects' session crash-recovery marker
|
||||||
|
// (SCRPriorState.json) from every AE prefs version dir. AE checks this file at
|
||||||
|
// startup; if it indicates an unclean prior session it shows the blocking
|
||||||
|
// "Crash Repair Options" dialog — which would hang a headless afterfx/aerender
|
||||||
|
// launch. Deleting it (vs. wiping all prefs) keeps the node's prefs intact.
|
||||||
|
//
|
||||||
|
// Safe no-op when APPDATA is unset (non-Windows / dev).
|
||||||
|
func ClearAECrashState() {
|
||||||
|
appData := os.Getenv("APPDATA")
|
||||||
|
if appData == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
base := filepath.Join(appData, "Adobe", "After Effects")
|
||||||
|
entries, err := os.ReadDir(base)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, e := range entries {
|
||||||
|
if e.IsDir() {
|
||||||
|
_ = os.Remove(filepath.Join(base, e.Name(), "SCRPriorState.json"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,7 @@ func WriteScanScript(workDir string) (string, error) {
|
|||||||
// afterfx -r runs the script and the script calls app.quit(); we still poll for the
|
// afterfx -r runs the script and the script calls app.quit(); we still poll for the
|
||||||
// output file because afterfx can return before the file is flushed.
|
// output file because afterfx can return before the file is flushed.
|
||||||
func RunScan(ctx context.Context, afterfxPath, aepPath, workDir, outPath, mode string) ([]byte, error) {
|
func RunScan(ctx context.Context, afterfxPath, aepPath, workDir, outPath, mode string) ([]byte, error) {
|
||||||
|
ClearAECrashState() // avoid the "Crash Repair Options" dialog hanging a headless launch
|
||||||
scriptPath, err := WriteScanScript(workDir)
|
scriptPath, err := WriteScanScript(workDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("write scan script: %w", err)
|
return nil, fmt.Errorf("write scan script: %w", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user