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>
Replaces the "desktop only" gate on phones with real mobile editing layouts.
Shared:
- BottomSheet (mobile slide-up panel) hosting the desktop side-docks on phones.
- Side panels made width-fluid (w-full on mobile, fixed on md+): StudioSidebarContent,
ImageEditorRightPanel.
Video Studio (VideoStudioMobileLayout):
- Canvas fills the viewport; the vertical tool dock becomes a scrollable bottom bar;
each tool's panel + the timeline open as bottom sheets. Exported MAIN_DOCK_ITEMS.
Image Editor (ImageEditorMobileLayout):
- Canvas fills the viewport; toolbar → scrollable bottom bar; Adjust/Filters/Layers
panel + shape picker open as bottom sheets. Exported IMAGE_TOOLS/IMAGE_SHAPES.
- Touch editing: Stage now handles onTouchStart/Move/End (draw, select, move) with
touch-action:none; draw-tool stroke works with a finger. Pointer handlers widened
to MouseEvent | TouchEvent.
i18n: added timeline/preview/panels keys (fa+en, parity verified). Full next build +
tsc clean. (Studio is auth-gated — verify editing on a device.)
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>
Epic A — admins author premade example videos per template; users pick one
on the template detail page to start a pre-filled project.
Backend (content-svc):
- PresetStory DTOs + PresetStoryService (admin CRUD + public published-only
filter via role check + soft-delete) + PresetStoriesController (/v1/preset-stories)
- DI registration; gateway route /v1/preset-stories (optionalAuth, public read)
Frontend:
- ProjectPresetStories admin authoring UI (name/description/demo upload/published/
sort + scene picker with order+duration + advanced scenes_spa); «ویدیوهای نمونه»
button + modal in ProjectsAdmin
- TemplateDetailExamples renders real published stories (image/video preview,
hover → "use this example" → creates a pre-bound project), falls back to
placeholders when none; selected aspect's variant id keys the fetch
- public /api/preset-stories route; preset_story_id plumbed through
createProjectFromTemplate + projects POST route; usePreset i18n (fa+en)
Verified: full CRUD via gateway (public hides unpublished); creating a project
with presetStoryId persists selected_preset_story_id on the saved project.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- New /api/files/upload: generic user-scoped Browser→Next→MinIO upload
(presign → PUT → confirm), 200MB cap, image+video only, returns public URL
- image-editor-export: stageToBlob() + saveStageToCloud(); "Save to my account"
button in the Image Editor export popover
- Trimmer: "Save to my account" button uploads the trimmed clip blob
- i18n: saveToCloud/savingToCloud/savedToCloud/saveToCloudFailed in fa+en
(parity 1002/1002)
Connects the two client-side editors to V2 storage — output now lands in the
user's account instead of only a local download.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 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>
- /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>
- 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>
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>
- 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>
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>
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>
- Add videoMaker + imageMaker namespace to en.json and fa.json
(hero, features x5, use-cases x4, CTA per section)
- Pricing.tsx: replace hardcoded heading with t('pricing.heading')
- VideoMakerHero/Features/UseCases/Cta: full useTranslations wiring
- ImageMakerHero/Features/UseCases/Cta: full useTranslations wiring
- Features/UseCases arrays moved inside components; icons kept as
module-level constants to avoid per-render allocation