Commit Graph

81 Commits

Author SHA1 Message Date
soroush.asadi 718564bce4 feat(scan): binary FIX scan reads frl_/frd_ names from .aep (no AE, never hangs)
Build backend images / build content-svc (push) Failing after 15s
Build backend images / build file-svc (push) Failing after 1m51s
Build backend images / build gateway (push) Failing after 51s
Build backend images / build identity-svc (push) Failing after 57s
Build backend images / build notification-svc (push) Failing after 52s
Build backend images / build render-svc (push) Failing after 56s
Build backend images / build studio-svc (push) Failing after 57s
Root cause of 'stuck on AE': heavy expression-driven projects take >10min for AE
to open, exceeding the scan timeout → job dies → admin UI stuck 'scanning'.

Fix: extend the stdlib .aep RIFX parser to collect every Utf8 name (ParseNames),
since FIX media placeholders are renamed footage ITEMS (frl_c1m1), not layers, and
text are layer names (frl_c1t1) — both are Utf8 chunks. QuickScan now branches on
?mode= (or auto-detects frl_ names) and scaffolds FIX scenes/elements + frd_*color
slots directly from the binary. Verified on the real final.aep that timed out in AE:
1 scene, 6 elements, 4 colors in 0.5s vs 10-min AE timeout.

Admin 'Quick scan (no AE)' is now the recommended path and passes the project mode.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 22:48:53 +03:30
soroush.asadi f0ce286527 fix(scan): force-kill stale AE processes before each launch (fresh start)
Build backend images / build content-svc (push) Failing after 54s
Build backend images / build file-svc (push) Failing after 56s
Build backend images / build gateway (push) Failing after 57s
Build backend images / build identity-svc (push) Failing after 58s
Build backend images / build notification-svc (push) Failing after 1m4s
Build backend images / build render-svc (push) Failing after 2m27s
Build backend images / build studio-svc (push) Failing after 55s
PrepareFreshAE = taskkill AfterFX/aerender/AfterFXLib/dynamiclinkmanager/QT32
+ 2s settle + clear crash markers, then launch. A hung/zombie AE from a prior
job would otherwise block or corrupt the new run. RunScan now calls it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 21:27:34 +03:30
soroush.asadi 6e5efbdb2c fix(scan): also clear AE AppStates registry to stop Safe Mode 'Crash Repair' dialog
Build backend images / build content-svc (push) Failing after 2m1s
Build backend images / build file-svc (push) Failing after 1m0s
Build backend images / build gateway (push) Failing after 56s
Build backend images / build identity-svc (push) Failing after 54s
Build backend images / build notification-svc (push) Failing after 54s
Build backend images / build render-svc (push) Failing after 46s
Build backend images / build studio-svc (push) Failing after 48s
SCRPriorState.json alone didn't suppress it — AE's per-session GUID under
HKCU\Software\Adobe\After Effects\AppStates persists after a kill/crash and
trips Safe Mode. ClearAECrashState now reg-deletes AppStates too (reg.exe, no dep).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 21:21:04 +03:30
soroush.asadi 47dd87c60b fix(scan): launch AE with the project as arg to bypass the Home screen
Build backend images / build content-svc (push) Failing after 1m13s
Build backend images / build file-svc (push) Failing after 1m35s
Build backend images / build gateway (push) Failing after 57s
Build backend images / build identity-svc (push) Failing after 1m28s
Build backend images / build notification-svc (push) Failing after 53s
Build backend images / build render-svc (push) Failing after 1m4s
Build backend images / build studio-svc (push) Failing after 55s
afterfx -r alone leaves AE on its empty Home/Start screen, which blocks the
script from running (AE sits idle on Untitled Project until the scan times out).
Now launch 'afterfx <aep> -r scan.jsx' so the project opens directly; scan.jsx
uses the already-open project and only app.open()s as a fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 21:13:16 +03:30
soroush.asadi 010f975a0e fix(nodes): add disk columns to ListNodes SELECT (scanNodes 37/39 mismatch → 500)
Build backend images / build content-svc (push) Failing after 50s
Build backend images / build file-svc (push) Failing after 56s
Build backend images / build gateway (push) Failing after 54s
Build backend images / build identity-svc (push) Failing after 58s
Build backend images / build notification-svc (push) Failing after 58s
Build backend images / build render-svc (push) Failing after 51s
Build backend images / build studio-svc (push) Failing after 59s
The disk-column append only hit GetNodeByID (whitespace differed); ListNodes
lacked last_disk_pct/disk_total_gb, so the node list 500'd and rendered empty.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 21:00:52 +03:30
soroush.asadi 0c461ff841 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
- 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>
2026-06-04 20:20:41 +03:30
soroush.asadi 0a7dd9b84c feat(nodes): live CPU/RAM/disk monitoring in the node list
Build backend images / build content-svc (push) Failing after 45s
Build backend images / build file-svc (push) Failing after 55s
Build backend images / build gateway (push) Failing after 53s
Build backend images / build identity-svc (push) Failing after 54s
Build backend images / build notification-svc (push) Failing after 53s
Build backend images / build render-svc (push) Failing after 47s
Build backend images / build studio-svc (push) Failing after 51s
- node-agent: internal/metrics — read CPU% (GetSystemTimes), RAM (GlobalMemoryStatusEx),
  disk used%/total (GetDiskFreeSpaceEx) via stdlib kernel32 (no external dep; windows
  build + non-windows stub). Heartbeat now reports cpu_pct/ram_available_mb/disk_used_pct/
  disk_total_gb + ae_running.
- render-svc: heartbeat persists last_disk_pct + disk_total_gb (migration 29); RenderNode
  model + node SELECT/scan carry them.
- admin: rewrite NodesTable to the real RenderNode shape (fixes a pre-existing items/V2Node
  mismatch that left the list empty) + a CPU/RAM/disk bars column + stale-heartbeat flag.
- assets-bundle ingestion: ProjectMediaBundle (jszip) auto-maps project.zip → project/scene
  image/demo/colour + music; PatchProject gains image/full_demo/shared_colors_svg.
- scan: RGBA (4-number) colours recognised + frshare single-int controls detected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 20:01:18 +03:30
soroush.asadi 6661f53734 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>
2026-06-04 19:06:08 +03:30
soroush.asadi ee670552a8 feat: cross-aspect project duplication + AEP convention/rule-engine spec
Build backend images / build content-svc (push) Failing after 1s
Build backend images / build file-svc (push) Failing after 0s
Build backend images / build gateway (push) Failing after 0s
Build backend images / build identity-svc (push) Failing after 0s
Build backend images / build notification-svc (push) Failing after 1s
Build backend images / build render-svc (push) Failing after 2s
Build backend images / build studio-svc (push) Failing after 0s
- content-svc: DuplicateProjectAsync clones full scene/element/colour graph
  (identical keys, new dimensions/aspect; AEP intentionally not copied;
  starts unpublished) + POST /v1/projects/{id}/duplicate.
- admin: «تکثیر» button + modal on each project row; aspects reduced to
  supported 16:9/1:1/9:16; free fps default 21 (clamped 1-60).
- docs/aep-template-convention.md: versioned (v1/v2) convention + rule-engine
  spec — modes, scene types, flatrender assembly, duration/fade model,
  fit-box, input types, expression-driven data flow, output spec.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 16:59:23 +03:30
soroush.asadi 1ff6e494c0 @
Build backend images / build content-svc (push) Failing after 19s
Build backend images / build file-svc (push) Failing after 1m53s
Build backend images / build gateway (push) Failing after 16s
Build backend images / build identity-svc (push) Failing after 7m1s
Build backend images / build notification-svc (push) Failing after 7m24s
Build backend images / build render-svc (push) Failing after 3m12s
Build backend images / build studio-svc (push) Failing after 43s
feat: AE template scanner + scene editor + AEP bundle pipeline

Scene editor (admin): per-project Scenes / Shared Colors / Color Presets
manager (ProjectScenes) reachable from each project.

AEP bundle pipeline: upload .aep or .zip → stored once per template at
templates/{project_id}/(bundle.zip|template.aep); render claim probes and
returns is_bundle+md5; node-agent extracts the bundle, locates the .aep
(zip-slip guarded), and caches by md5 so repeated renders extract once.

AE template scanner ("read scenes/colours/configs from the AEP"):
- content-svc importer: POST /v1/projects/{id}/scan/{preview,apply} —
  review-diff-then-merge into scenes/elements/colours (manual edits kept).
- render-svc Go quick-scan: stdlib RIFX parser extracts comp names+durations
  (no AE) → POST /v1/template-scans/{id}/quick.
- render-svc AE scan jobs + node-agent runner: queue → node runs scan.jsx
  (reverse of legacy JSXGenerator conventions: frfinal/frshare/frl_/frd_) →
  posts ScanResult back. Migration 26_render_scan_jobs.
- admin UI: "اسکن از افترافکت" with quick/full engines + diff-review modal.

Verified: importer preview/apply, Go quick-scan end-to-end (synthetic .aep →
scene imported), bundle extract unit tests, RIFX parser unit tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
2026-06-04 10:39:45 +03:30
soroush.asadi 264fccf21f i18n(fa): brand فلت‌رندر across all strings + locale-aware page title
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 05:32:27 +03:30
soroush.asadi a016af804a fix(i18n): Persian brand (فلت‌رندر) + localize aspect 'All Sizes'
- nav/footer/admin brand → فلت‌رندر in fa (FlatRender in en)
- aspect-ratio 'All Sizes' option now uses t('allSizes') (همه اندازه‌ها)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 05:24:27 +03:30
soroush.asadi 31809336a2 fix(templates): real per-category counts in sidebar (drop hardcoded 418/851)
Category badges now show the live template count per category computed from the
catalog (0 → no badge), instead of hardcoded demo numbers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 05:18:28 +03:30
soroush.asadi 87f1dd0fce fix(admin): slugify without /u flag (ES5 target) — keep Persian/Arabic via literal ranges
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 00:24:01 +03:30
soroush.asadi 222aa078b1 fix(templates): show only real templates — drop hardcoded demo fallback
The public templates page and homepage gallery fell back to hardcoded demo
templates (VIDEO_TEMPLATES_CATALOG / TEMPLATES) whenever the admin list was
empty — so dummy templates showed even though the DB had none. Now both render
only real admin-sourced templates (empty when there are none). Categories are
untouched (kept as-is).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 00:16:10 +03:30
soroush.asadi 08d2de8e92 feat(admin): auto-slug from name + "add project" on Projects page
- slug fields auto-fill from the name (slugify keeps Persian + latin letters,
  spaces → "-") until the slug is edited by hand; applies to all data-driven
  forms (categories/tags/blogs/…) and the Templates form
- Projects page (/admin/projects) gains "+ پروژه جدید": pick a template
  (container) + name/aspect/resolution/size/duration/fps/mode → POST /v1/projects.
  Previously a project could only be added while editing a template.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 00:00:56 +03:30
soroush.asadi d955d951b5 fix(i18n): stop Farsi links bouncing to /en; locale-aware nav links
- routing: localeDetection:false — a non-prefixed URL always serves fa (default);
  English only via explicit /en/ prefix. Browser Accept-Language no longer
  redirects fa pages to /en on every click.
- AdminShell + DashboardSidebarNav: use next-intl Link + usePathname (from
  @/i18n/navigation) instead of plain next/link, so links preserve the current
  locale and active-state matches the prefix-stripped path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 23:31:53 +03:30
soroush.asadi 1cd1e504d9 feat(dashboard): "My Renders" page for users
- /dashboard/renders: user's own render jobs (live status + progress bar + cancel)
  and finished exports (thumbnail + size/duration + download); bilingual fa/en
- server lib my-renders.ts (user-scoped /v1/renders + /v1/exports via session JWT)
- user action routes: POST /api/renders/[id]/cancel, GET /api/exports/[id]/download
  (presigned URL)
- dashboard sidebar: "رندرهای من / My Renders" nav item

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 17:22:38 +03:30
soroush.asadi b270ef438d fix(admin): sidebar invisible (CSS specificity) + /admin lands on dashboard
- AdminShell: the rtl:/ltr: translate variants ([dir] selector) out-specified
  lg:translate-x-0, so the sidebar stayed off-screen on desktop and the mobile
  drawer couldn't open. Pin physically right + plain translate-x-full/0; content
  uses lg:mr-60.
- /admin now redirects to /admin/stats (overview) instead of /admin/nodes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 08:20:46 +03:30
soroush.asadi 43780f94f6 feat(admin): grouped sidebar shell (replace cramped 27-link top bar)
- AdminShell: fixed RTL sidebar with grouped nav (نمای کلی / محتوا / رشد و ارتباطات
  / کاربران و مالی / فارم رندر / سیستم), active-link highlighting via usePathname,
  sticky header showing the current section, mobile drawer with hamburger + overlay
- layout: build the grouped nav and render via AdminShell

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 07:56:54 +03:30
soroush.asadi ebf0e11f22 fix(render+admin): render queue shows ALL users' jobs
Build backend images / build content-svc (push) Failing after 53s
Build backend images / build file-svc (push) Failing after 55s
Build backend images / build gateway (push) Failing after 58s
Build backend images / build identity-svc (push) Failing after 1m0s
Build backend images / build notification-svc (push) Failing after 49s
Build backend images / build render-svc (push) Failing after 56s
Build backend images / build studio-svc (push) Failing after 59s
The admin render queue called the user-scoped /v1/renders (so it only showed the
admin's own jobs) and parsed items/total instead of data/meta (→ always empty).
- render-svc: GET /v1/admin-renders (admin) → ListAllJobs across users, optional
  ?status= filter; gateway-wired
- admin renders page now fetches /v1/admin-renders and reads data/meta correctly

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 07:35:17 +03:30
soroush.asadi 4253d2fad5 feat(admin): node detail view (health + history + crashes)
Per-node "جزئیات" button opens a modal with live health (status/CPU/RAM/AE/cache/
last heartbeat), a 24h CPU history mini-chart, and the recent crash log (signal,
auto-recovered, last frame, error log, log-file link). Uses existing render-svc
GET /v1/nodes/:id/health, /health/history, /crashes endpoints.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 07:17:48 +03:30
soroush.asadi 928956689b feat(render+admin): exports management (all users' rendered videos)
Build backend images / build content-svc (push) Failing after 54s
Build backend images / build file-svc (push) Failing after 55s
Build backend images / build gateway (push) Failing after 52s
Build backend images / build identity-svc (push) Failing after 55s
Build backend images / build notification-svc (push) Failing after 58s
Build backend images / build render-svc (push) Failing after 48s
Build backend images / build studio-svc (push) Failing after 1m0s
- render-svc: admin-scoped store (ListAllExports / GetExportByIDAny /
  SoftDeleteExportAny) + GET/DELETE/download-url under /v1/admin-exports
  (admin-gated; separate prefix so it routes to render, not identity's /admin)
- gateway: /v1/admin-exports/* → render
- admin /admin/exports: paginated table of every rendered export with thumbnail,
  type/quality, size, duration, dimensions, produce + expiry dates; download
  (presigned URL) and delete

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 07:04:06 +03:30
soroush.asadi db167062e6 feat(admin): search + pagination on all data-driven tables
- AdminResource: client-side search box (matches across all fields) + 25/page
  pagination with prev/next and a filtered-count footer
- bumped pageSize on server-paged configs (users/blogs/comments/discounts/music/
  fonts/tags) so search/paginate covers the full set

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 06:48:58 +03:30
soroush.asadi 7f2f65dd8a feat(render+node-agent+admin): install fonts on all render nodes + verify
Build backend images / build content-svc (push) Failing after 53s
Build backend images / build file-svc (push) Failing after 47s
Build backend images / build gateway (push) Failing after 52s
Build backend images / build identity-svc (push) Failing after 58s
Build backend images / build notification-svc (push) Failing after 55s
Build backend images / build render-svc (push) Failing after 59s
Build backend images / build studio-svc (push) Failing after 48s
Push a font once → every node installs it → admin sees per-node status.

- render-svc: font_requests + node_fonts tables (mig 25); admin GET/POST/DELETE
  /v1/node-fonts (with per-node status matrix); internal (HMAC) GET pending +
  POST status for node-agents
- node-agent: fontSyncLoop polls pending fonts every 60s, downloads, installs
  (Windows Fonts dir + registry / macOS / linux fc-cache), reports Installed/Failed
- gateway: /v1/node-fonts/* → render
- admin /admin/node-fonts: upload a .ttf/.otf → install on all nodes; per-node
  Installed/Pending/Failed badges + counts + delete

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 06:33:48 +03:30
soroush.asadi ca0c05db10 fix(identity): internal render-charge request binding (record → class)
Build backend images / build content-svc (push) Failing after 1m0s
Build backend images / build file-svc (push) Failing after 47s
Build backend images / build gateway (push) Failing after 57s
Build backend images / build identity-svc (push) Failing after 1m2s
Build backend images / build notification-svc (push) Failing after 1m0s
Build backend images / build render-svc (push) Failing after 1m1s
Build backend images / build studio-svc (push) Failing after 58s
The nested positional record ChargeReq(Guid UserId) failed System.Text.Json
binding under the snake_case policy (400). Use a plain class with a settable
property. Verified: consume decrements + blocks at 0, refund restores, bad
service token → 401.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 02:28:26 +03:30
soroush.asadi 1f52f53cf7 feat(render+identity): daily render-limit — consume on submit, refund on admin-stop
Build backend images / build content-svc (push) Failing after 51s
Build backend images / build file-svc (push) Failing after 53s
Build backend images / build gateway (push) Failing after 1m1s
Build backend images / build identity-svc (push) Failing after 48s
Build backend images / build notification-svc (push) Failing after 42s
Build backend images / build render-svc (push) Failing after 47s
Build backend images / build studio-svc (push) Failing after 1m13s
Business rule: each user has a daily render limit. Admin-stop refunds the used
charge (not the user's fault); a user's own cancel does not.

- identity: ConsumeRenderChargeAsync / RefundRenderChargeAsync on DailyRemainRenderCount
  with lazy daily reset (mig 24: daily_renders_reset_at). Convention: max=0 ⇒ UNLIMITED,
  so existing 0/0 users keep rendering until an admin sets a real limit.
- identity InternalController (service-token): POST /v1/internal/render-charge/{consume,refund}
- render-svc: identityclient + on Create consume (block 429 when limit reached, fail-open
  on identity outage); on admin Stop refund the job owner; user /cancel unchanged
- compose: IDENTITY_URL for render-svc, ServiceToken for identity-svc

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 02:18:00 +03:30
soroush.asadi 7f7feabb85 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
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>
2026-06-03 01:39:33 +03:30
soroush.asadi c7694a9bbf feat(admin): multi-select bulk delete in media library
Per-file checkboxes + "حذف موارد انتخاب‌شده (N)" bar that deletes all selected
files in parallel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 01:29:22 +03:30
soroush.asadi c076345ceb feat(render+admin): delete render node
Build backend images / build content-svc (push) Failing after 1m7s
Build backend images / build file-svc (push) Failing after 48s
Build backend images / build gateway (push) Failing after 55s
Build backend images / build identity-svc (push) Failing after 56s
Build backend images / build notification-svc (push) Failing after 1m7s
Build backend images / build render-svc (push) Failing after 53s
Build backend images / build studio-svc (push) Failing after 59s
- render-svc: DELETE /v1/nodes/:node_id (store.DeleteNode + handler + route)
- admin: حذف button per node row + DELETE /api/admin/nodes/[nodeId] proxy

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 01:21:13 +03:30
soroush.asadi e5812488eb feat(admin): file manager — search, type tabs (image/video/AE), library picker
- FileManager: type tabs (همه/تصاویر/ویدیوها/صدا/پروژه‌های AE و سایر) + name
  search (uses file_type + search params the file svc already supports; type
  values capitalized to match the enum), video thumbnails via <video>, AE/zip
  shown under "AE و سایر"; delete + copy-URL retained
- FilePicker: reusable modal to re-choose an existing file from the library
  (search + filter + click to pick)
- FileUploadField: new "از کتابخانه" button on every upload field → pick from
  library instead of re-uploading; picker auto-filters by the field's accept
- shared src/lib/admin-files.ts helpers (fileUrl/isImage/isVideo/fetchFiles)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 01:11:47 +03:30
soroush.asadi ffc0c5d6d5 feat(admin): add-node form + After Effects version dropdown
- Nodes page: "+ افزودن نود" opens a full-screen form (name, region, IP, worker
  port, AE version, node kind, RAM, CPU, priority) → POST /v1/nodes
- current_ae_version is now a dropdown (2025…2020, matching the ae_version DB
  enum) instead of free text; node_kind is a dropdown (Shared/Dedicated/Spot)
- new POST /api/admin/nodes proxy route (forwards body; admin-gated). The backend
  POST /v1/nodes existed but had no UI — you couldn't define nodes before.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 01:00:14 +03:30
soroush.asadi 5b6f3e851b feat(admin): full-screen forms + WYSIWYG rich-text editor
- AdminResource + TemplatesAdmin modals are now large full-height panels
  (max-w-5xl, sticky header/footer, scrolling body, 2-column field grid;
  textarea/richtext span full width)
- RichTextField: dependency-free contentEditable WYSIWYG (bold/italic/underline,
  H2/H3/¶, lists, link, clear) emitting HTML, dir="auto" for fa/en
- blog content + category description now use the rich editor

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 00:51:52 +03:30
soroush.asadi 7fe5f8a563 feat(admin): standalone Projects page + per-project asset manager
Build backend images / build content-svc (push) Failing after 1m36s
Build backend images / build file-svc (push) Failing after 1m28s
Build backend images / build gateway (push) Failing after 2m11s
Build backend images / build identity-svc (push) Failing after 2m11s
Build backend images / build notification-svc (push) Failing after 3m46s
Build backend images / build render-svc (push) Failing after 55s
Build backend images / build studio-svc (push) Failing after 1m2s
- content-svc: GET /v1/projects (browse/search all projects across containers,
  paginated, admin) returning template name/slug + AE status; project_assets
  table (mig 23) + entity; GET/POST/DELETE /v1/projects/{id}/assets
- /admin/projects: searchable, paginated list of every renderable project with
  thumbnail, template, aspect/resolution, AE-file + publish status
- ProjectAssets component: list/upload/delete named footage/image/audio/font
  files per project (reused in the projects page; AE file upload alongside)
- nav + fa/en "Projects" label

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 00:39:33 +03:30
soroush.asadi c4839bd35f feat(admin): project (template-item) manager + After Effects file upload
Build backend images / build content-svc (push) Failing after 50s
Build backend images / build file-svc (push) Failing after 1m5s
Build backend images / build gateway (push) Failing after 3m13s
Build backend images / build identity-svc (push) Failing after 1m32s
Build backend images / build notification-svc (push) Failing after 5m7s
Build backend images / build render-svc (push) Failing after 1m2s
Build backend images / build studio-svc (push) Failing after 54s
The admin could edit a container but not manage its renderable projects or attach
AE files. Now, inside the template editor:
- add a new project/variant under the container (name, WxH, aspect, resolution,
  duration, fps, choose-mode) → POST /v1/projects (maps via container_id)
- upload the After Effects file (.aep/.zip) per project → new PATCH
  /v1/projects/{id}/aep (sets AepFileUrl/Minio/Md5/Size + RenderAepComp), with an
  "AE ✓ / بدون فایل" status badge
- set the render composition name; delete a variant
- ProjectResponse now surfaces aep_file_url / aep_file_size_bytes / render_aep_comp

Additive only — the existing aspect/resolution variant editing is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 00:23:50 +03:30
soroush.asadi 675b60d858 feat(auth+admin): Sign in with Google (OAuth) + Integrations config panel
Build backend images / build content-svc (push) Failing after 1m2s
Build backend images / build file-svc (push) Failing after 3m11s
Build backend images / build gateway (push) Failing after 5m39s
Build backend images / build identity-svc (push) Failing after 38s
Build backend images / build notification-svc (push) Failing after 2m0s
Build backend images / build render-svc (push) Failing after 58s
Build backend images / build studio-svc (push) Failing after 58s
Backend (identity-svc):
- oauth_config table (mig 22) + OAuthConfig entity
- OAuthService: admin config CRUD + Google authorization-code flow (build consent
  URL, exchange code, fetch userinfo, find/create RegisterMode.Google user, issue
  session via AuthService.IssueOAuthSessionAsync)
- AuthController: GET /v1/auth/google/{start,callback} (public); tokens handed to
  frontend via URL fragment
- AdminController: GET/PUT /v1/admin/oauth/{provider} (admin, secret masked)

Frontend:
- "ورود با گوگل" button on /auth → identity start endpoint
- /auth/callback reads fragment tokens → /api/auth/oauth-session sets httpOnly cookies
- /admin/integrations: Google client_id/secret/redirect_uri + enable, with setup guide
- nav + fa/en labels

Client ID/Secret are configured entirely in the admin panel — no redeploy needed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 00:08:21 +03:30
soroush.asadi 88a44b1349 feat(admin): full Persian (fa) localization + RTL polish
- admin-resources: all config titles/descriptions/columns/fields → Persian
- AdminResource: generic UI strings (new/edit/delete/save/cancel/loading/empty/
  actions/confirm) → Persian; text-left/right → logical text-start/end
- TemplatesAdmin: all labels, table, modal, statuses, errors → Persian
- FileUploadField, WebsiteSettingsAdmin, FileManager → Persian
- layout: ms-auto logical prop (NodesTable/RenderQueue/AiContentStudio already
  i18n with Persian values)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 23:47:35 +03:30
soroush.asadi 0cbfcbfdf7 feat(admin): render inline SVG category icons in tables
AdminThumb detects raw <svg> markup and renders it inline (object-contain);
categories list shows an Icon column. Admin-entered, authenticated input.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 23:29:01 +03:30
soroush.asadi b47314fcab feat(admin): image thumbnails in lists + template image/demo fields
- AdminThumb: reusable thumbnail (raster + SVG via <img>, dashed fallback on
  empty/broken)
- AdminResource: ColumnDef gains type:"image" → renders thumbnails in tables
- image thumbnail columns for categories, slides, home-events, internal routes;
  categories icon field now multiline (accepts raw SVG markup)
- TemplatesAdmin: cover image / mini-demo / demo / full-demo upload fields
  (backed by existing container image/demo fields) + thumbnail column in the list

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 23:23:52 +03:30
soroush.asadi 24aa4c51a4 fix(identity): plan-statistics LINQ translation (aggregate in memory)
Build backend images / build content-svc (push) Failing after 57s
Build backend images / build file-svc (push) Failing after 58s
Build backend images / build gateway (push) Failing after 4m40s
Build backend images / build identity-svc (push) Failing after 56s
Build backend images / build notification-svc (push) Failing after 10s
Build backend images / build render-svc (push) Failing after 4m3s
Build backend images / build studio-svc (push) Failing after 2m24s
EF Core can't translate a conditional Count(predicate) inside a grouped Select;
fetch flat rows then group/aggregate in memory.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 23:04:53 +03:30
soroush.asadi 151970accd feat(admin): plan statistics + node restart/close-ae actions
Build backend images / build content-svc (push) Failing after 1m22s
Build backend images / build file-svc (push) Failing after 3m8s
Build backend images / build gateway (push) Failing after 53s
Build backend images / build identity-svc (push) Failing after 57s
Build backend images / build notification-svc (push) Failing after 1m25s
Build backend images / build render-svc (push) Failing after 2m5s
Build backend images / build studio-svc (push) Failing after 3m59s
Final legacy-admin items:
- identity GET /v1/admin/plan-statistics (active/total users + revenue per plan
  from user_plans); surfaced as a breakdown table in /admin/stats
- NodesTable: wire Restart + Close-AE actions (backend already supported them) via
  new proxy routes; was only drain/release before

Full DivineGateWeb legacy-admin parity achieved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 23:02:03 +03:30
soroush.asadi 3091911260 feat(admin): affiliate/personal discounts, user-videos, internal routes, authz
Build backend images / build content-svc (push) Failing after 1s
Build backend images / build file-svc (push) Failing after 1s
Build backend images / build gateway (push) Failing after 0s
Build backend images / build identity-svc (push) Failing after 0s
Build backend images / build notification-svc (push) Failing after 1s
Build backend images / build render-svc (push) Failing after 1s
Build backend images / build studio-svc (push) Failing after 1s
Closes the remaining legacy-admin gaps:
- Users «مدیریت» modal: create personal discount or affiliate code (owner_user_id +
  owner_profit_percentage on existing /v1/discounts), and view the user's saved
  projects ("videos") via new admin GET /v1/saved-projects/by-user/{id} (studio)
- Internal routes admin (/admin/routes): CRUD on content.internal_routes
  (RoutesController + CmsService + gateway /v1/routes/*)
- Security: lock identity UsersController Search + Ban to [Authorize(Roles="Admin")]

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 22:42:01 +03:30
soroush.asadi 0b538e1b1e feat(content+admin): home-events CRUD + comments moderation
Build backend images / build content-svc (push) Failing after 45s
Build backend images / build file-svc (push) Failing after 1m3s
Build backend images / build gateway (push) Failing after 0s
Build backend images / build identity-svc (push) Failing after 0s
Build backend images / build notification-svc (push) Failing after 1s
Build backend images / build render-svc (push) Failing after 1s
Build backend images / build studio-svc (push) Failing after 0s
- content-svc: home-events gains Create/Update/Delete + includeInactive list
  (POST/PUT/DELETE /v1/home-events, admin-gated; dates coerced to UTC)
- admin /admin/home-events: full CRUD for homepage hero event banners
- admin /admin/comments: list + approve/unapprove + delete (moderation)
- AdminResource: optional listQuery to fetch inactive rows for admin views

Fills the remaining legacy-admin gaps (home events, comments).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 22:24:56 +03:30
soroush.asadi 3acd366fda feat(admin): music library admin + fix CRM analytics UTC
Build backend images / build content-svc (push) Failing after 1m7s
Build backend images / build file-svc (push) Failing after 50s
Build backend images / build gateway (push) Failing after 59s
Build backend images / build identity-svc (push) Failing after 56s
Build backend images / build notification-svc (push) Failing after 1m0s
Build backend images / build render-svc (push) Failing after 1m0s
Build backend images / build studio-svc (push) Failing after 56s
- /admin/music: list / upload / delete studio audio tracks (content-svc
  GET/POST/DELETE /v1/music) — fills the legacy music-library gap
- fix: CRM analytics coerced query-bound dates to UTC (Npgsql timestamptz
  rejects Kind=Unspecified) — endpoint was returning 400

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 22:17:14 +03:30
soroush.asadi 2c961b123b feat(content+admin): content ranking + statistics dashboard
Build backend images / build content-svc (push) Failing after 16s
Build backend images / build file-svc (push) Failing after 48s
Build backend images / build gateway (push) Failing after 17s
Build backend images / build identity-svc (push) Failing after 2m12s
Build backend images / build notification-svc (push) Failing after 3m15s
Build backend images / build render-svc (push) Failing after 51s
Build backend images / build studio-svc (push) Failing after 56s
- content-svc: template list gains popularity/rating sort modes (use_count_desc,
  popular, rating_desc); new PATCH /v1/templates/{id}/sort to set manual sort
  weight (feature/pin) without a full edit
- admin /admin/ranking: templates ordered by popularity with views/uses/rating
  and inline manual-sort editor
- admin /admin/stats: overview dashboard (users, revenue, paying customers,
  conversion, templates/categories/campaigns/blogs counts) aggregated from
  existing identity + content endpoints
- nav: Dashboard + Ranking links

Completes the epic: SMS/Email/Templates → Marketing → CRM → Ranking + Stats.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 22:11:18 +03:30
soroush.asadi 62a5121ffe feat(identity+admin): CRM analytics + customer notes + user power-actions
Build backend images / build content-svc (push) Failing after 56s
Build backend images / build file-svc (push) Failing after 54s
Build backend images / build gateway (push) Failing after 1m1s
Build backend images / build identity-svc (push) Failing after 55s
Build backend images / build notification-svc (push) Failing after 54s
Build backend images / build render-svc (push) Failing after 52s
Build backend images / build studio-svc (push) Failing after 1m2s
Modeled on the legacy DivineGateWeb admin (CRM + Security/* actions):
- identity-svc AdminService + AdminController (admin-gated):
  - GET /v1/admin/crm/analytics — signups/buyers/conversion/revenue + daily series
    (from identity.users + identity.payments)
  - GET/PUT /v1/users/{id}/crm — tags / note / pipeline status (user_crm table, mig 20)
  - power-actions: POST /v1/users/{id}/{balance,password,charge,moderator,grant-plan}
- admin UI: /admin/crm dashboard (funnel cards + daily signup/revenue bars);
  per-user "مدیریت" modal in Users (balance, render charge, plan days, password,
  moderator, CRM notes)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 18:59:07 +03:30
soroush.asadi 6dbb14d146 feat(notifications+admin): marketing campaigns
Build backend images / build content-svc (push) Failing after 14s
Build backend images / build file-svc (push) Failing after 22s
Build backend images / build gateway (push) Failing after 1m21s
Build backend images / build identity-svc (push) Failing after 1m43s
Build backend images / build notification-svc (push) Failing after 1m6s
Build backend images / build render-svc (push) Failing after 53s
Build backend images / build studio-svc (push) Failing after 1m5s
- campaigns table (migration 19) + CRUD + send endpoint in notification-svc
- audience resolution reads cross-schema from identity.users (all / verified /
  with_plan); send dispatches via the SMS or Email channel and logs deliveries
- endpoints: GET/POST /v1/campaigns, POST /v1/campaigns/:id/send, DELETE
- gateway route /v1/campaigns/* → notification
- /admin/marketing: create campaign (channel, audience, template/subject/body),
  list with status + sent counts, send, delete

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 18:17:19 +03:30
soroush.asadi 507ac7e6a4 feat(notifications+admin): SMS (Kavenegar) + Email (SMTP) channels & templates
Build backend images / build content-svc (push) Failing after 56s
Build backend images / build file-svc (push) Failing after 47s
Build backend images / build gateway (push) Failing after 1m0s
Build backend images / build identity-svc (push) Failing after 56s
Build backend images / build notification-svc (push) Failing after 11s
Build backend images / build render-svc (push) Failing after 4m5s
Build backend images / build studio-svc (push) Failing after 56s
Backend (notification-svc):
- channel_config table (per-tenant Kavenegar + SMTP settings) + migration 18
- sender pkg: Kavenegar SMS client + SMTP mailer (STARTTLS / implicit TLS), stdlib only
- endpoints: GET/PUT /v1/channels[/:channel], POST /v1/sms/send, POST /v1/email/send
  (template + {{var}} rendering); deliveries logged
- seeded 3 Persian email templates: welcome / account_verification / promotion
- gateway routes /v1/{channels,sms,email}/* → notification

Admin UI:
- /admin/messaging: SMS + Email provider config cards, test-send, email template editor
- nav link + fa/en labels

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 17:32:54 +03:30
soroush.asadi e3f025cd2d fix(coming-soon): match the app's light brand design
Rebuild the landing on the real site language: white background with the same
pastel violet/sky/rose/amber hero blobs, the actual hero copy with the blue→violet
gradient highlight on «هوش مصنوعی», light countdown cards, light feature chips, and
the violet→blue gradient button. Countdown still to ۱ تیر ۱۴۰۵.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 16:29:54 +03:30
soroush.asadi 814eb8d54e feat: standalone "Coming Soon" landing (single HTML, dockerized)
RTL Persian landing matching the FlatRender brand (blue + LogoMark), with a live
countdown to ۱ تیر ۱۴۰۵ (1 Tir 1405 = 22 Jun 2026, Iran time), feature chips, and a
notify-me capture. One static index.html served by nginx (coming-soon/Dockerfile);
stack-agnostic, no app dependencies.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 15:44:44 +03:30