Lets an admin disable rendering when no render node is available — users can't
start new renders and see a localized "service unavailable until <date>" message.
- Admin → فارم رندر → موتور رندر (RenderEngineAdmin): on/off toggle + fa/en message
+ optional Jalali "until" date; saved as one `render_service` Website Setting
(jsonb) via /v1/settings — no backend change, no migration.
- lib/render-service.ts: fetchRenderServiceStatus (fail-open) + renderServiceMessage
(locale + appends the date).
- Enforcement: POST /api/render returns 503 {code:render_disabled, messages} when off;
studio render page reads GET /api/render/service on mount → disables "شروع رندر"
and shows the banner, and handles the 503 on click.
- i18n: appAdminLayout.renderEngine (fa+en, parity 1045/1045). tsc + next build clean.
Verified: disabled setting → /api/render/service returns enabled:false.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The homepage is now driven by a `home_layout` Website Setting (jsonb) instead of a
hardcoded section stack — zero backend changes, no migration.
- lib/home-layout.ts: section catalog + saved-layout merge + locale-aware config
reader (`<field>_fa`/`<field>_en`) + public fetchHomeLayout() (falls back to
defaults when unset/unreachable).
- app/[locale]/page.tsx: renders ordered, enabled sections from the layout, passing
per-section content overrides.
- sections (Hero/Products/Templates/HowItWorks/Pricing/Testimonials/FAQ): accept an
optional `config` prop overriding heading/subtitle/CTA, locale-aware, default-safe.
- new HomeSlides + HomeEvents sections render the previously-orphaned admin Slides
(/v1/slides) and Home Events (/v1/home-events) data.
- admin: HomeSectionsManager (toggle on/off, ↑/↓ reorder, per-section FA/EN content
editor) at /admin/home, saved via the existing /v1/settings upsert; nav item + i18n.
Verified: a saved layout overrides Hero/Pricing headings and reorders sections;
removing it reverts to the default homepage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 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>
- 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>
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>
- 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>
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>
- /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>
- /admin/discounts: list + create discount codes (kind, value, max uses, expiry)
via /v1/discounts (backend has no edit/delete API yet)
- /admin/settings: key/value site settings with upsert + secret flag. The value
column is jsonb, so values are JSON-encoded on save / decoded for display
- nav links + fa/en labels
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- /admin/files Media Library: drag-drop multi-upload, thumbnails, copy-URL, delete
- FileUploadField replaces raw URL inputs; new "image" field type in AdminResource;
wired into category image
- upload proxy /api/admin/files/upload: browser → Next → presigned PUT (server-side,
reaches minio:9000) → confirm → returns public URL
- user-uploads bucket is public-read; public base via NEXT_PUBLIC_MINIO_URL
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Token auto-refresh (middleware):
- Proactively refresh fr_access when < 120s remain — no more silent 15-min kick
- Inlines /v1/auth/refresh call in middleware, stamps new cookies on response
- /admin/* protected: is_admin JWT claim required, else redirect /dashboard
- apiFetch() (src/lib/api/fetch.ts): client-side 401 → auto-refresh → retry;
de-duplicates concurrent refresh calls; redirects to /auth on failure
Studio → Render V2 wiring:
- scenes[] no longer sent to POST /api/render (V2 render-svc fetches project
from Studio service via saved_project_id directly)
- renderRequestSchema.scenes is now optional
- RenderModal uses apiFetch for auto-refresh on 401 during polling
Admin panel (/admin/*):
- Admin layout: server-side is_admin guard + top nav (Nodes, Render Queue)
- /admin/nodes: lists all nodes from GET /v1/nodes with status badges,
heartbeat age, slot usage, tags; Drain (PATCH status=Draining) + Release actions
- /admin/renders: render job table with step filter tabs; progress bars,
error messages, Retry + Cancel per-row actions; polls GET /v1/renders
- API proxy routes: /api/admin/nodes/:id/drain|release,
/api/admin/renders/:id/retry|cancel — all validate is_admin in JWT before proxying
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>