Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.2 KiB
FlatRender V2 — Session Handoff & Gotchas
Context captured 2026-06-05 so it survives a Claude-account/machine change. The
~/.claudememory is machine-local; this file is the portable copy.
How to run (V2 stack)
# Backend services + infra (Postgres + MinIO) — V2 compose, NOT the default docker-compose.yml
docker compose -f docker-compose.v2.yml --env-file .env.v2 up -d
# Rebuild one service after a code change:
docker compose -f docker-compose.v2.yml --env-file .env.v2 build <svc> && \
docker compose -f docker-compose.v2.yml --env-file .env.v2 up -d <svc>
# Services: identity-svc, content-svc, file-svc, studio-svc, render-svc, notification-svc, gateway, frontend
- Open the app at
http://172.28.144.1:3000(the host LAN IP), NOTlocalhost. - Gateway:
:8088. JWT secret + service creds live in.env.v2. - DB: single Postgres
flatrender, one schema per service (identity,content,studio,render,file, …). Userpostgres. - DB migrations:
backend/db/migrations/NN_*.sql, applied once byscripts/init-db.shon first volume creation. New SQL files must be applied manually:docker exec fr2-postgres psql -U postgres -d flatrender -f ...(or inline-c).
⚠️ Critical gotchas (these have each cost hours)
-
Localhost is VPN-hijacked. EonVPN intercepts
127.0.0.1:*. Curl/loopback to localhost returns junk/000 even when the app is up. Use the LAN IP172.28.144.1, ordocker exec <container> wget -qO- http://<svc>:<port>. -
Non-secure browser context. Because the app is served over
http://172.28.144.1(plain HTTP, non-localhost), the browser is a non-secure context →crypto.randomUUID,crypto.subtle, clipboard, etc. are undefined. NEVER call them in client code — usesrc/lib/uuid.tsuuid()(falls back tocrypto.getRandomValues). This silently broke the entire studio editor (crypto.randomUUID is not a function). -
Studio service binds camelCase JSON (no snake_case naming policy, unlike identity/content which use
SnakeCaseLower). Frontend→studio request bodies MUST be camelCase (originalProjectId, notoriginal_project_id) or fields drop to defaults (Guid.Empty). Studio responses are camelCase too. -
EF Core global query filters (
HasQueryFilter(DeletedAt==null)) require.IgnoreQueryFilters()to see/revive soft-deleted rows (bit the AEP import apply). -
AE automation runs on a Windows node via
node-agent.exe(PULL model: agent dials render-svc with HMAC). Before each AE launch the agent kills stale AE processes + clears crash/Safe-Mode markers (SCRPriorState.json+ registryAppStates) and launches AE with the project as an arg (bypasses the Home screen). Heavy expression-driven AEPs take >10min to open → FIX scans are now binary-only (services/render/internal/aep/parse.goParseNamesreadsfrl_/frd_names from the .aep RIFX directly; no AE).
What this session built (commits 6e5efbd → 5b2617d)
- AE scan hardening: binary FIX scanner (no AE, never hangs); kill stale AE + clear crash dialog before each launch.
- Profile = data-collection surface (for future AI video gen, NO resume builder): full editable profile (avatar upload + slogan/about/company/website/country/birthdate/gender),
/api/profile+ user-scoped/api/profile/upload. Identity DTOs widened (no migration — columns existed). - Role-aware nav:
UserMenu(avatar + dropdown; admins get Admin Panel link) replaces Sign-In when logged in; real avatars in dashboard sidebar + admin shell;Avatarprimitive;getNavUser(). - Template detail page wired to real content (
fetchProject(slug); was hardcoded demo catalog → 404'd). - "Use template" works end-to-end:
StudioService.CreateProjectAsyncdeep-copies the content template scene graph (scenes + content elements + scene colors + shared colors) into the editable studio project via one atomic cross-schema SQL copy (enum cols cast::text; temp_scene_map)./api/projectsresolves container slug → published variant project. Aspect-ratio picker (16:9/1:1/9:16) on the detail page drives which variant is copied.
⏭️ NEXT UP — Studio↔Template binding EPIC (agreed priority)
Start with Phase B: edit→render binding. Full plan in ~/.claude memory
epic_studio_template_binding.md. Summary:
- B1 persist studio input edits to
saved_scene_contents(new studio-svcPATCH /v1/saved-projects/{id}/contentsby content key) — today edits only go toedit_state. - B2 render binder (port v1
JSXGenerator.cs): before aerender, JSX sets each AE layer (namedfrl_c{n}{t|m}{i}) text/footage + frshare colours from the saved values, then render. Today the node renders the template with DEFAULT values → user edits never reach the MP4. - Then A admin preset stories (premade videos — model
preset_stories/preset_scenesexists, no endpoints/UI; detail "ویدیوهای ساختهشده" is placeholder), and C AE single-frame scene snapshots (scenes.snapshot_urlempty → nodeaerender -s 0 -e 0).
Also smaller, still open: per-tier render height (render-svc r_height hardcoded 1080 + node ffmpeg scale), FIX hides add-scene (mode not plumbed into studio store), Persian/Jalali date pickers in admin, admin/renders pagination + video/output + user-name → profile link, deeper per-input controllers in the admin scene-inputs editor.
Done follow-ups (this session)
- Scene-graph copy now includes repeater children, characters/controllers, color-presets.
- Admin CAN edit any user's full profile (Users → «پروفایل»).
- Studio now shows ALL template inputs (contents→layers bridge).
Debugging client-side behavior
No Playwright/Puppeteer in the repo. To reproduce browser issues headlessly: npm i puppeteer-core in a temp dir, launch the user's Chrome (C:\Program Files\Google\Chrome\Application\chrome.exe), set the auth cookie via page.setCookie({name:'fr_access', value:<JWT>, url:'http://172.28.144.1:3000'}), and listen on page.on('pageerror') / console. Mint a test admin JWT with the .env.v2 JWT_SECRET (HS256; claims sub=real user id, tenant_id, is_admin:"true", role:"Admin", iss:"flatrender-identity", aud:"flatrender").