Skip to main content

TL;DR

Tuturuuu CMS now lives in the dedicated apps/cms satellite app.
  • apps/cms owns the CMS authoring UI, workspace picker, overview/library/preview/members routes, and the root /{wsId}/projects linking console.
  • apps/web remains the only backend/API owner for CMS and external-project traffic.
  • apps/cms rewrites /api/* to apps/web, so proxy behavior and bot protection stay centralized.

Product Vocabulary

The visible CMS experience is a site/content product, not an implementation console. Bound-workspace UI should say site, site template, connection, content, section, URL path, custom details, media, publishing, preview, people, invitations, and team access. Avoid implementation terms in normal UI copy, including external project, canonical, adapter, binding, slug, schema, metadata, profile data, payload, and JSON. If operators need exact implementation values, show them only in collapsed Developer details panels or internal root-console advanced controls. Keep code, API routes, database objects, migration names, and integration docs on the existing external-project* contracts.

Route Model

Root workspace

  • /internal/projects: internal site-template registry, workspace-indexed connection list, and connection audit history

Bound workspaces

  • /{wsId}: overview
  • /{wsId}/landing: page-builder surface for landing-only sections
  • /{wsId}/library: content operations surface for non-landing, non-game sections and content
  • /{wsId}/games: game content surface when CMS Games is enabled
  • /{wsId}/library/collections/{collectionId}: section detail
  • /{wsId}/library/entries/{entryId}: content detail
  • /{wsId}/members: workspace member search, invite, removal, role assignment, and role/default-permission management
  • /{wsId}/preview: site preview
  • /{wsId}/settings: legacy compatibility redirect to /{wsId}/members
The old CMS route model (/{wsId}/content, /{wsId}/collections/..., /{wsId}/admin) is removed instead of redirected. The internal/root workspace is not a bindable content workspace, so /internal and other root-workspace CMS route variants should redirect to /internal/projects.

Root Linking Console

  • Treat /internal/projects as a workspace-first control room.
  • The main left panel is the searchable workspace list.
  • Each row shows the current connection state, site type, site template, and most recent change signal.
  • The selected workspace panel owns connect and disconnect actions.
  • The site-template registry is secondary and stays on the same page for create/edit flows.
  • Binding audits stay anchored to the selected workspace instead of living as a detached global feed.

Workspace CMS

  • Use /{wsId} as the concise starting point for continue-editing tasks, launch readiness, attention queues, recent activity, and connected-site context.
  • Use /{wsId}/landing for focused landing-page edits, page readiness, and preview-first section work.
  • Use /{wsId}/library for broader content operations: search, status filtering, publishing queues, collection health, and reusable non-landing/non-game content.
  • Use /{wsId}/members for routine collaborator management without leaving CMS.
  • Keep collection configuration on collection detail pages and entry authoring on entry detail pages.
  • Use /{wsId}/preview as the delivered-content surface backed by the existing preview delivery route.
  • The old settings screen is removed because it duplicated overview, library, and members content. Keep /settings only as a compatibility redirect to /{wsId}/members.

Member Management In CMS

  • Workspace member search, invite, remove, and role-assignment controls now have a dedicated /{wsId}/members sidebar route.
  • The members surface also absorbs the essentials of the workspace roles page so CMS operators can manage named roles and default permissions without leaving CMS.
  • This keeps standalone CMS usable without forcing operators back into the main platform app for routine collaborator changes.
  • CMS members data must load through external-project-aware apps/web API routes and @tuturuuu/internal-api helpers. Reads should authorize by CMS workspace access so app-session users do not hit workspace settings-route 403s; writes still respect member and role management permissions.
  • Use CMS copy in this area: team access, people, invites, access levels, member defaults, guest defaults, and Advanced access.

Implementation Notes

  • Keep CMS as a pure satellite frontend. Do not move API ownership into apps/cms.
  • Continue adding or extending client helpers in packages/internal-api for CMS-facing API calls.
  • Keep preview sourced from the existing delivery route instead of introducing a second preview payload contract.
  • Backend/database naming remains external-project*; the product surface is what changed.
  • Protect CMS-owned product copy with the CMS copy hygiene test, and add explicit admin/developer-detail exceptions only when a staff workflow truly needs an implementation value.
  • Translation validation now treats null leaves as invalid so missing shared keys fail checks instead of silently passing parity.
  • External app admin dashboards should call POST /api/v1/workspaces/{wsId}/external-projects/setup with their sync manifest before diff/apply. The route uses the existing app-coordination token and requires manage access. If the workspace is already bound to an active canonical project for the same adapter, setup preserves that binding while refreshing the project schema and field definitions; otherwise it creates the canonical {adapter}-main project if needed and binds the configured workspace without requiring production Supabase keys in the external app.
  • External apps can mark manifest assets with metadata.publicPath, metadata.localAssetPath, or a relative sourceUrl such as /media/hero.png.
  • Before diff/apply, run the public-folder linker so the manifest stores deterministic Drive paths under external-projects/{adapter}/{collectionSlug}/{entrySlug}/{filename}.
  • Diff/apply routes are workspace-scoped. They may persist manifest schema into the bound workspace’s collections and field definitions, but they must not update canonical_external_projects or other root/canonical registry state.
  • Before apply, upload those files through the external-project asset app-server upload route. Abort the apply if a local public asset is missing so CMS never receives storage paths for objects that were not uploaded.
  • Asset storagePath/storage_path values must stay under external-projects/. Do not point CMS assets at Drive, finance, or other workspace storage namespaces; delivery and admin cleanup only operate on assets that satisfy the CMS namespace invariant.

Team Guidance

  • Link into the CMS-native route map directly.
  • Prefer the root /internal/projects console for site-template and workspace-link operations.
  • Keep bound-workspace collaborator management on the dedicated /{wsId}/members route, and do not surface /internal/projects shortcuts inside non-root workspace sidebars.
  • Update both apps/cms/messages/en.json and apps/cms/messages/vi.json, then run bun i18n:sort whenever CMS copy changes.