Files
multica/apps/web/next.config.ts
Bohan Jiang bf31fa4b39 fix(web): move /docs rewrite to beforeFiles (#1266)
* feat(docs): mount docs site at /docs subpath via basePath + multi-zone

Configure the Fumadocs site so it can be served at multica.ai/docs:

- Add basePath: '/docs' to apps/docs/next.config.mjs
- Flatten routes: drop standalone home, render content/docs/index.mdx at
  the root, move catch-all from app/docs/[[...slug]] to app/[...slug]
- Wrap children with DocsLayout in the root layout (was a separate
  segment-level layout under app/docs/)
- Set source loader baseUrl to '/' so URL slugs no longer carry the
  basePath (Next.js prepends it automatically)
- Strip the now-redundant '/docs/' prefix from internal MDX links and
  drop the duplicate "Documentation" nav entry
- Add app/not-found.tsx for App Router 404 handling

Wire up multi-zone routing so apps/web proxies /docs/* to the docs app:

- Add DOCS_URL env (default http://localhost:4000) and rewrites for
  /docs and /docs/:path* in apps/web/next.config.ts
- Whitelist DOCS_URL in turbo.json globalEnv

* fix(web): move /docs rewrite to beforeFiles so [workspaceSlug] doesn't shadow it

The /docs rewrite was running in the default afterFiles slot, which is
evaluated *after* file-system routing. apps/web/app/[workspaceSlug]/
matched /docs first as a workspace named "docs" (which doesn't exist) and
returned 404 before the rewrite to the docs Vercel project ever fired.

Splitting rewrites into beforeFiles/afterFiles puts /docs and
/docs/:path* ahead of route resolution so they always proxy to the docs
zone.
2026-04-17 16:28:31 +08:00

73 lines
2.0 KiB
TypeScript

import type { NextConfig } from "next";
import { config } from "dotenv";
import { resolve } from "path";
// Load root .env so REMOTE_API_URL is available to next.config.ts
config({ path: resolve(__dirname, "../../.env") });
const remoteApiUrl = process.env.REMOTE_API_URL || "http://localhost:8080";
const docsUrl = process.env.DOCS_URL || "http://localhost:4000";
// Parse hostnames from CORS_ALLOWED_ORIGINS so that Next.js dev server
// allows cross-origin HMR / webpack requests (e.g. from Tailscale IPs).
const allowedDevOrigins = process.env.CORS_ALLOWED_ORIGINS
? process.env.CORS_ALLOWED_ORIGINS.split(",")
.map((origin) => {
try {
return new URL(origin.trim()).host;
} catch {
return origin.trim();
}
})
.filter(Boolean)
: undefined;
const nextConfig: NextConfig = {
...(process.env.STANDALONE === "true" ? { output: "standalone" as const } : {}),
transpilePackages: ["@multica/core", "@multica/ui", "@multica/views"],
...(allowedDevOrigins && allowedDevOrigins.length > 0
? { allowedDevOrigins }
: {}),
images: {
formats: ["image/avif", "image/webp"],
qualities: [75, 80, 85],
},
async rewrites() {
return {
// Run before file-system routes so /docs isn't shadowed by the
// [workspaceSlug] dynamic segment.
beforeFiles: [
{
source: "/docs",
destination: `${docsUrl}/docs`,
},
{
source: "/docs/:path*",
destination: `${docsUrl}/docs/:path*`,
},
],
afterFiles: [
{
source: "/api/:path*",
destination: `${remoteApiUrl}/api/:path*`,
},
{
source: "/ws",
destination: `${remoteApiUrl}/ws`,
},
{
source: "/auth/:path*",
destination: `${remoteApiUrl}/auth/:path*`,
},
{
source: "/uploads/:path*",
destination: `${remoteApiUrl}/uploads/:path*`,
},
],
fallback: [],
};
},
};
export default nextConfig;