Files
multica/packages/core/paths/reserved-slugs.ts
Naiyuan Qing 5f1f08e466 feat(web): add use-cases content pipeline with welcome page (MUL-2349) (#2795)
* feat(web): add use-cases content pipeline with welcome page (MUL-2349)

Wire fumadocs-mdx into apps/web with an independent collection rooted at
content/use-cases/. Add the first page at /use-cases/welcome (header + H1 +
prose + screenshot + footer) using the about-page visual shell.

- source.config.ts + lib/use-cases-source.ts (separate from apps/docs)
- features/landing/components/mdx/screenshot.tsx wraps next/image
- public/use-cases/welcome/screenshot-1.png placeholder (55KB)
- next.config.ts wraps NextConfig with createMDX()
- .gitignore + eslint ignore .source/

Co-authored-by: multica-agent <github@multica.ai>

* feat(web): bilingual db-boy use case with cookie locale (MUL-2349)

Extends the use-cases pipeline into the first real article.

- ZH + EN MDX (auto-data-analysis.{zh,en}.mdx) sharing three real
  screenshots; sensitive fields on db-boy-profile.png (RDS host, DB
  name, password) are blurred in-place.
- Cookie-based locale: /use-cases/<slug> reads multica-locale
  server-side via lib/use-cases-i18n.ts (mirrors LandingLayout's
  cookie + Accept-Language fallback). Same URL serves either language;
  no [lang] segment so all other landing routes stay unchanged.
- Frontmatter schema (source.config.ts): z.looseObject with declared
  hero_image / updated_at (required) / category (optional); a
  preprocess converts YAML-auto-parsed Date back to a YYYY-MM-DD string.
- MDX components factory createMdxComponents(locale) routes the
  secondary CTA to /docs/zh (ZH) or /docs (EN); internal MDX links
  use <Link> for SPA nav; full-width and half-width colons both
  trigger [CTA: ...] / [占位图: ...] markers; 副 and Secondary
  both work as the secondary CTA prefix.
- Index page localizes hero / subtitle / card CTA / metadata; sort
  fallback uses an epoch placeholder so undefined-order disappears.
- Landing header + footer surface use-cases entry in both locales.
- Detail route: sticky header, right-rail TOC with anchor jumps,
  scroll-mt-[100px] on H2/H3 so anchor jumps don't slip under the
  sticky header.
- Drop welcome demo page.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(web): resolve code review blockers on use-cases PR

- Add `use-cases` to reserved_slugs.json + regenerate TS (P1: prevent
  future workspace slug collision)
- Fix dead links in both MDX files: /features/* → /docs/* (P2)
- Remove duplicate brand suffix in page title metadata (nit)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>

* fix(web): align usecases locale routing

* chore: refresh web mdx lockfile

* fix(web): type mdx next config adapter

* fix(web): wrap settings route page

---------

Co-authored-by: multica-agent <github@multica.ai>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 10:05:17 +08:00

155 lines
4.0 KiB
TypeScript

// AUTO-GENERATED by scripts/generate-reserved-slugs.mjs.
// Do not edit by hand — edit server/internal/handler/reserved_slugs.json
// and run `pnpm generate:reserved-slugs`.
/**
* Slugs reserved because they collide with frontend top-level routes,
* platform features, or web standards.
*
* Single source of truth: `server/internal/handler/reserved_slugs.json`.
* The Go backend embeds that JSON; this file is regenerated from it.
*
* Convention for new global routes (CLAUDE.md): use a single word
* (`/login`, `/inbox`) or `/{noun}/{verb}` (`/workspaces/new`). Hyphenated
* root-level word groups (`/new-workspace`, `/create-team`) collide with
* common user workspace names — see PR for full discussion.
*/
export const RESERVED_SLUGS: ReadonlySet<string> = new Set([
// Auth flow
// `onboarding` is historical, kept reserved post-removal of the route.
"login",
"logout",
"signin",
"signout",
"signup",
"auth",
"oauth",
"callback",
"invite",
"invitations",
"verify",
"reset",
"password",
"onboarding",
// Platform / marketing routes (current + likely-future)
// `multica` is reserved as the brand name to block impersonation workspaces.
// `www`, `new`, `home`, `homepage`, `dashboard` are confusables or
// likely-future global landing/entry routes; `homepage` matches the existing
// `/homepage` landing variant in apps/web.
"api",
"admin",
"multica",
"www",
"new",
"home",
"homepage",
"dashboard",
"help",
"about",
"pricing",
"changelog",
"docs",
"support",
"status",
"legal",
"privacy",
"terms",
"security",
"contact",
"contact-sales",
"blog",
"careers",
"press",
"download",
"usecases",
// Account / billing (likely-future global routes in the avatar menu)
"profile",
"account",
"billing",
"notifications",
"search",
"members",
// Workspace route segments
// Reserving each segment name prevents `/{slug}/{view}` from being visually
// ambiguous (e.g. a workspace named `issues` would make `/issues/abc` mean two
// things). `workspaces` covers the global `/workspaces/new` workspace-creation
// page; `teams` is reserved for future team management.
"issues",
"projects",
"autopilots",
"agents",
"squads",
"inbox",
"my-issues",
"usage",
"runtimes",
"skills",
"settings",
"workspaces",
"teams",
// API / integration prefixes
// `api` above already covers `/api/*`; these guard against future top-level
// API alias routes (e.g. `/v1`, `/graphql`) and against accidental workspace
// slugs that read like API identifiers.
"v1",
"v2",
"graphql",
"webhooks",
"sdk",
"tokens",
"cli",
// Backend ops / observability
// `/health`, `/readyz`, `/healthz`, and `/ws` exist on the backend host;
// reserving them on the workspace slug space prevents naming confusion if/when
// these paths are ever proxied through the web origin.
"health",
"readyz",
"healthz",
"ws",
"metrics",
"ping",
// RFC 2142 — privileged email mailboxes
// Allowing user workspaces with these slugs would let attackers spoof system
// messaging.
"postmaster",
"abuse",
"noreply",
"webmaster",
"hostmaster",
// Hostname / subdomain confusables
// Even on path-based routing these names attract phishing and
// subdomain-takeover attempts.
"mail",
"ftp",
"static",
"cdn",
"assets",
"public",
"files",
"uploads",
// Next.js / web standards
// These entries contain characters (dots, underscores) that today's slug regex
// `^[a-z0-9]+(?:-[a-z0-9]+)*$` already rejects at the format-validation step —
// so `isReservedSlug` never actually matches them. They are kept as
// defense-in-depth so that if the slug regex is ever relaxed (e.g. to support
// dotted corporate slugs like `acme.io`), these system paths stay protected.
"_next",
"favicon.ico",
"robots.txt",
"sitemap.xml",
"manifest.json",
".well-known",
]);
export function isReservedSlug(slug: string): boolean {
return RESERVED_SLUGS.has(slug);
}