fix(scan): Fix-mode scanner + dialog suppression + cancel/timer + importer revive
Build backend images / build content-svc (push) Failing after 1m25s
Build backend images / build file-svc (push) Failing after 1m10s
Build backend images / build gateway (push) Failing after 56s
Build backend images / build identity-svc (push) Failing after 53s
Build backend images / build notification-svc (push) Failing after 57s
Build backend images / build render-svc (push) Failing after 48s
Build backend images / build studio-svc (push) Failing after 1m5s

- scan.jsx: app.beginSuppressDialogs() + clean quit (no AE hang on font/footage
  dialogs); FIX-mode branch parses frl_c(x)t/m(y) layer names → scenes by c(x);
  flexible/mockup keep comp-based walk; FR_SCAN_MODE selects.
- render-svc: scan job carries project mode; cancel endpoint + node watchdog that
  kills AE on cancel; parseObjectURL handles minio:// (bucket in host); scan with
  no template fails cleanly; status guards so late results can't un-cancel.
- content importer: revive soft-deleted scenes instead of duplicate-inserting
  (fixes scenes_project_id_key unique violation); orphan diff ignores deleted.
- admin: scan dialog gets project-type picker + elapsed timer + Cancel button.
- node-agent: AE-2026 wiring (host port 5010, host-reachable presign endpoint),
  FR_SCAN_MODE plumbing. docs/aep-template-convention.md: per-type naming + bundles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-04 19:06:08 +03:30
parent ee670552a8
commit 6661f53734
17 changed files with 498 additions and 45 deletions
@@ -21,8 +21,11 @@ public class AepImportService(ContentDbContext db)
private async Task<ImportDiff> RunAsync(Guid projectId, ScanResult scan, ScanApplyOptions? apply)
{
// 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
// rather than insert a duplicate (which would violate the constraint).
var existing = await db.Scenes
.Where(s => s.ProjectId == projectId && s.DeletedAt == null)
.Where(s => s.ProjectId == projectId)
.Include(s => s.ContentElements)
.Include(s => s.ColorElements)
.ToListAsync();
@@ -74,13 +77,19 @@ public class AepImportService(ContentDbContext db)
scene = new Scene { ProjectId = projectId, Key = ss.Key, Sort = ss.Sort ?? sortBase++ };
db.Scenes.Add(scene);
}
else if (scene!.DeletedAt is not null)
{
scene.DeletedAt = null; // revive a soft-deleted scene the scan re-discovered
}
ApplySceneHeader(scene!, ss, isNew, apply);
ApplyElements(scene!, scElems, exElemByKey, scElemKeys, apply);
ApplyColors(scene!, scColors, exColorByKey, scColorKeys, apply);
}
}
var orphans = existing.Where(s => !scanKeys.Contains(s.Key)).ToList();
// Orphans = currently-active scenes the scan no longer contains (ignore
// already soft-deleted rows so they don't inflate the diff).
var orphans = existing.Where(s => s.DeletedAt == null && !scanKeys.Contains(s.Key)).ToList();
if (apply is not null && apply.RemoveOrphanScenes)
foreach (var s in orphans) s.DeletedAt = DateTime.UtcNow;