docs: vendor the documentation site into the monorepo

Fold the standalone 3x-ui-docs project (Next.js 16 + Fumadocs, deployed to
docs.sanaei.dev) into docs/ so the panel and its documentation share a single
source of truth, the way sing-box keeps its docs in-tree. The old repo becomes
redundant and can be retired.

- Import the full site under docs/ (app, components, content, lib, public,
  scripts, config). The self-contained pnpm project sits alongside the existing
  engineering notes with no filename collisions.
- Re-point "Edit on GitHub" links from MHSanaei/3x-ui-docs to this repo's
  docs/content/docs path (docs/lib/shared.ts, docs/app/.../page.tsx).
- Add docs-ci.yml and docs-deploy.yml under .github/workflows/, scoped to
  docs/** and run with working-directory: docs, since GitHub only runs
  workflows from the repo-root .github/. deploy-static.yml's GitHub Pages
  publish (CNAME docs.sanaei.dev) carries over unchanged.

Follow-up (outside this commit): attach the docs.sanaei.dev custom domain to
this repository's Pages (or set the Vercel project's root directory to docs),
confirm the site is live from the monorepo, then delete MHSanaei/3x-ui-docs.
This commit is contained in:
MHSanaei
2026-07-07 23:07:14 +02:00
parent 2c49dbf54e
commit 9b91f0f42e
283 changed files with 44179 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
import { readFileSync } from 'node:fs';
import { basename } from 'node:path';
import { generateFiles } from 'fumadocs-openapi';
import { openapi } from '../lib/openapi.ts';
// Map a slugified tag name back to its clean display name, so generated page
// titles read "API Tokens" instead of the auto-split "A P I Tokens".
const spec = JSON.parse(readFileSync('./public/openapi.json', 'utf8')) as {
tags?: { name: string }[];
};
const titleBySlug = new Map(
(spec.tags ?? []).map((t) => [t.name.toLowerCase().replace(/\s+/g, '-'), t.name]),
);
// Generate one MDX page per tag into the English reference/api folder.
// Other locales fall back to English for the API reference.
await generateFiles({
input: openapi,
output: './content/docs/en/reference/api',
per: 'tag',
beforeWrite(files) {
for (const file of files) {
const slug = basename(file.path).replace(/\.mdx$/, '');
const title = titleBySlug.get(slug);
if (title) {
file.content = file.content.replace(/^title:.*$/m, `title: ${title}`);
}
}
},
});
console.log('Generated API reference pages.');