97 lines
4.2 KiB
JavaScript
97 lines
4.2 KiB
JavaScript
import { copyFile, mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const siteRoot = path.resolve(fileURLToPath(new URL('..', import.meta.url)));
|
|
const repoRoot = path.resolve(siteRoot, '..');
|
|
const sourceDir = path.join(repoRoot, 'docs');
|
|
const outputDir = path.join(siteRoot, 'src', 'content', 'docs', 'reference');
|
|
|
|
const descriptions = {
|
|
'admin.md': 'Возможности админ-панели, управление пользователями, настройками, тарифами и поддержкой.',
|
|
'architecture.md': 'Краткая архитектура backend, frontend, worker и инфраструктурных сервисов.',
|
|
'configuration.md': 'Минимальный .env, bootstrap-секреты и настройка через Web App админку.',
|
|
'deployment.md': 'Docker Compose, reverse proxy, TLS, образы, обновления и резервные копии.',
|
|
'env-vars.md': 'Полный справочник переменных окружения Remnawave Minishop.',
|
|
'migration-to-minishop.md': 'Перенос данных со старого remnawave-tg-shop на split-архитектуру Minishop.',
|
|
'support.md': 'Пользовательские тикеты, админский inbox, уведомления и лимиты поддержки.',
|
|
'tariffs.md': 'Каталог тарифов, period/traffic-модели, premium-сквады и HWID-устройства.',
|
|
'webapp.md': 'Telegram Mini App, авторизация, публичные инструкции и проксирование.',
|
|
'webapp-themes.md': 'Кастомные темы, CSS-токены, ассеты и пайплайн создания темы.',
|
|
};
|
|
|
|
const imageExtensions = new Set(['.avif', '.gif', '.jpeg', '.jpg', '.png', '.svg', '.webp']);
|
|
|
|
function yamlString(value) {
|
|
return JSON.stringify(value);
|
|
}
|
|
|
|
function slugFor(fileName) {
|
|
return fileName.replace(/\.md$/i, '');
|
|
}
|
|
|
|
function extractTitle(fileName, content) {
|
|
const match = content.match(/^#\s+(.+?)\s*$/m);
|
|
return match?.[1] ?? slugFor(fileName);
|
|
}
|
|
|
|
function stripFirstHeading(content) {
|
|
return content.replace(/^#\s+.+?\s*\r?\n+/, '');
|
|
}
|
|
|
|
function rewriteMarkdownLinks(markdown) {
|
|
return markdown.replace(/\]\((?!https?:\/\/|mailto:|tel:|\/|#)([^)\s]+\.md)(#[^)]+)?\)/g, (match, target, hash = '') => {
|
|
const slug = slugFor(path.posix.basename(target));
|
|
return `](/reference/${slug}/${hash})`;
|
|
});
|
|
}
|
|
|
|
function normalizeCodeFences(markdown) {
|
|
return markdown
|
|
.replace(/^```env\s*$/gim, '```ini')
|
|
.replace(/^```caddyfile\s*$/gim, '```txt');
|
|
}
|
|
|
|
function frontmatter({ title, description, fileName }) {
|
|
const editUrl = `https://gitlab.com/3252a8/remnawave-minshop/-/edit/main/docs/${encodeURIComponent(fileName)}`;
|
|
return [
|
|
'---',
|
|
`title: ${yamlString(title)}`,
|
|
`description: ${yamlString(description)}`,
|
|
`editUrl: ${yamlString(editUrl)}`,
|
|
'---',
|
|
'',
|
|
].join('\n');
|
|
}
|
|
|
|
async function syncMarkdown(entries) {
|
|
for (const entry of entries.filter((item) => item.name.endsWith('.md'))) {
|
|
const sourcePath = path.join(sourceDir, entry.name);
|
|
const content = await readFile(sourcePath, 'utf8');
|
|
const title = extractTitle(entry.name, content);
|
|
const body = normalizeCodeFences(rewriteMarkdownLinks(stripFirstHeading(content).trimStart()));
|
|
const output = frontmatter({
|
|
title,
|
|
description: descriptions[entry.name] ?? title,
|
|
fileName: entry.name,
|
|
});
|
|
|
|
await writeFile(path.join(outputDir, entry.name), `${output}${body}\n`, 'utf8');
|
|
}
|
|
}
|
|
|
|
async function syncImages(entries) {
|
|
for (const entry of entries.filter((item) => imageExtensions.has(path.extname(item.name).toLowerCase()))) {
|
|
await copyFile(path.join(sourceDir, entry.name), path.join(outputDir, entry.name));
|
|
}
|
|
}
|
|
|
|
await rm(outputDir, { recursive: true, force: true });
|
|
await mkdir(outputDir, { recursive: true });
|
|
|
|
const entries = await readdir(sourceDir, { withFileTypes: true });
|
|
await syncMarkdown(entries);
|
|
await syncImages(entries);
|
|
|
|
console.log(`Synced documentation from ${path.relative(repoRoot, sourceDir)} to ${path.relative(repoRoot, outputDir)}`);
|