docs: docs-site initial
This commit is contained in:
@@ -13,6 +13,10 @@ scratch/
|
|||||||
*.local.*
|
*.local.*
|
||||||
node_modules/
|
node_modules/
|
||||||
frontend/node_modules/
|
frontend/node_modules/
|
||||||
|
docs-site/node_modules/
|
||||||
|
docs-site/.astro/
|
||||||
|
docs-site/dist/
|
||||||
|
docs-site/src/content/docs/reference/
|
||||||
deploy/compose/docker-compose-dev.yml
|
deploy/compose/docker-compose-dev.yml
|
||||||
data/*
|
data/*
|
||||||
!data/tariffs.example.json
|
!data/tariffs.example.json
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ scratch/
|
|||||||
node_modules/
|
node_modules/
|
||||||
.git/
|
.git/
|
||||||
|
|
||||||
|
# Documentation site build artifacts
|
||||||
|
docs-site/.astro/
|
||||||
|
docs-site/dist/
|
||||||
|
docs-site/src/content/docs/reference/
|
||||||
|
|
||||||
# WebApp build artifacts (regenerated by `npm run build:webapp` / Docker build)
|
# WebApp build artifacts (regenerated by `npm run build:webapp` / Docker build)
|
||||||
bot/app/web/templates/subscription_webapp.css
|
bot/app/web/templates/subscription_webapp.css
|
||||||
bot/app/web/templates/subscription_webapp.js
|
bot/app/web/templates/subscription_webapp.js
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Cloudflare Pages deploy
|
||||||
|
|
||||||
|
This docs site is built with Astro Starlight and publishes to:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://minishop.minidoc.cc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cloudflare Pages settings
|
||||||
|
|
||||||
|
Create a Pages project connected to the GitLab repository and use:
|
||||||
|
|
||||||
|
| Setting | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| Production branch | `main` |
|
||||||
|
| Framework preset | `Astro` |
|
||||||
|
| Root directory | `docs-site` |
|
||||||
|
| Build command | `npm ci && npm run build` |
|
||||||
|
| Build output directory | `dist` |
|
||||||
|
| Node version | `22` |
|
||||||
|
|
||||||
|
The build script runs `scripts/sync-docs.mjs` before Astro builds the site. Keep editing the canonical Markdown files in the repository-level `docs/` directory.
|
||||||
|
|
||||||
|
## Custom domain
|
||||||
|
|
||||||
|
After the first successful Pages deploy:
|
||||||
|
|
||||||
|
1. Open the Pages project in Cloudflare.
|
||||||
|
2. Go to **Custom domains**.
|
||||||
|
3. Add `minishop.minidoc.cc`.
|
||||||
|
4. If `minidoc.cc` is already on Cloudflare DNS, accept the suggested DNS record and wait for TLS activation.
|
||||||
|
|
||||||
|
## Optional API automation
|
||||||
|
|
||||||
|
Do not use a root token for automation. Create a scoped Cloudflare API token and expose it locally as an environment variable only for the setup command.
|
||||||
|
|
||||||
|
Minimum useful permissions:
|
||||||
|
|
||||||
|
| Scope | Permission |
|
||||||
|
| --- | --- |
|
||||||
|
| Account | Cloudflare Pages: Edit |
|
||||||
|
| Zone: `minidoc.cc` | Zone: Read |
|
||||||
|
| Zone: `minidoc.cc` | DNS: Edit |
|
||||||
|
|
||||||
|
Cloudflare's GitLab integration still requires the Cloudflare GitLab app/OAuth connection to be authorized for the repository. If that is not connected yet, complete the GitLab connection in the Cloudflare dashboard first, or use a direct-upload Pages workflow instead of Git-connected deployments.
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { defineConfig } from 'astro/config';
|
||||||
|
import starlight from '@astrojs/starlight';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
site: 'https://minishop.minidoc.cc',
|
||||||
|
integrations: [
|
||||||
|
starlight({
|
||||||
|
title: 'Remnawave Minishop',
|
||||||
|
description:
|
||||||
|
'Документация по настройке, развертыванию и эксплуатации Remnawave Minishop.',
|
||||||
|
favicon: '/favicon.svg',
|
||||||
|
logo: {
|
||||||
|
src: './src/assets/logo.svg',
|
||||||
|
alt: 'Remnawave Minishop',
|
||||||
|
},
|
||||||
|
customCss: ['./src/styles/custom.css'],
|
||||||
|
lastUpdated: false,
|
||||||
|
locales: {
|
||||||
|
root: {
|
||||||
|
label: 'Русский',
|
||||||
|
lang: 'ru',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
head: [
|
||||||
|
{
|
||||||
|
tag: 'meta',
|
||||||
|
attrs: {
|
||||||
|
name: 'theme-color',
|
||||||
|
content: '#0f766e',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: 'meta',
|
||||||
|
attrs: {
|
||||||
|
property: 'og:site_name',
|
||||||
|
content: 'Remnawave Minishop Docs',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sidebar: [
|
||||||
|
{
|
||||||
|
label: 'Обзор',
|
||||||
|
link: '/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Запуск',
|
||||||
|
items: [
|
||||||
|
{ label: 'Настройка окружения', slug: 'reference/configuration' },
|
||||||
|
{ label: 'Переменные .env', slug: 'reference/env-vars' },
|
||||||
|
{ label: 'Развертывание', slug: 'reference/deployment' },
|
||||||
|
{ label: 'Миграция', slug: 'reference/migration-to-minishop' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Web App',
|
||||||
|
items: [
|
||||||
|
{ label: 'Mini App', slug: 'reference/webapp' },
|
||||||
|
{ label: 'Темы и внешний вид', slug: 'reference/webapp-themes' },
|
||||||
|
{ label: 'Админ-панель', slug: 'reference/admin' },
|
||||||
|
{ label: 'Поддержка', slug: 'reference/support' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Продукт',
|
||||||
|
items: [
|
||||||
|
{ label: 'Тарифы', slug: 'reference/tariffs' },
|
||||||
|
{ label: 'Архитектура', slug: 'reference/architecture' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
Generated
+6285
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"sync:docs": "node ./scripts/sync-docs.mjs",
|
||||||
|
"dev": "npm run sync:docs && astro dev",
|
||||||
|
"build": "npm run sync:docs && astro build",
|
||||||
|
"preview": "astro preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/starlight": "^0.39.2",
|
||||||
|
"astro": "^6.3.7",
|
||||||
|
"typescript": "^6.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96">
|
||||||
|
<rect width="96" height="96" rx="22" fill="#0f172a"/>
|
||||||
|
<path d="M19 59c11 0 14-20 28-20 11 0 14 11 24 11 4 0 7-1 10-3v13c-3 2-7 3-11 3-13 0-16-11-25-11-10 0-14 20-26 20V59Z" fill="#2dd4bf"/>
|
||||||
|
<path d="M18 39c12 0 15-18 29-18 10 0 13 9 23 9 4 0 8-1 11-4v13c-3 2-7 3-12 3-13 0-16-9-24-9-10 0-14 18-27 18V39Z" fill="#f59e0b"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 396 B |
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
@@ -0,0 +1,96 @@
|
|||||||
|
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)}`);
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" role="img" aria-labelledby="title desc">
|
||||||
|
<title id="title">Remnawave Minishop</title>
|
||||||
|
<desc id="desc">Abstract layered wave mark.</desc>
|
||||||
|
<rect width="96" height="96" rx="22" fill="#0f172a"/>
|
||||||
|
<path d="M19 59c11 0 14-20 28-20 11 0 14 11 24 11 4 0 7-1 10-3v13c-3 2-7 3-11 3-13 0-16-11-25-11-10 0-14 20-26 20V59Z" fill="#2dd4bf"/>
|
||||||
|
<path d="M18 39c12 0 15-18 29-18 10 0 13 9 23 9 4 0 8-1 11-4v13c-3 2-7 3-12 3-13 0-16-9-24-9-10 0-14 18-27 18V39Z" fill="#f59e0b"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 536 B |
@@ -0,0 +1,7 @@
|
|||||||
|
import { defineCollection } from 'astro:content';
|
||||||
|
import { docsLoader } from '@astrojs/starlight/loaders';
|
||||||
|
import { docsSchema } from '@astrojs/starlight/schema';
|
||||||
|
|
||||||
|
export const collections = {
|
||||||
|
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
|
||||||
|
};
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
title: "Remnawave Minishop"
|
||||||
|
description: "Документация по запуску, настройке и сопровождению Telegram Mini App для Remnawave."
|
||||||
|
template: splash
|
||||||
|
hero:
|
||||||
|
tagline: "Telegram-бот и Mini App для продажи подписок Remnawave: платежи, тарифы, админка, поддержка и инструкции подключения."
|
||||||
|
image:
|
||||||
|
alt: Интерфейс Remnawave Minishop
|
||||||
|
html: '<img src="/remnawave-minishop.webp" alt="Интерфейс Remnawave Minishop" width="900" height="520" loading="eager" decoding="async" />'
|
||||||
|
actions:
|
||||||
|
- text: Быстрый старт
|
||||||
|
link: /reference/deployment/
|
||||||
|
icon: right-arrow
|
||||||
|
- text: Настройка
|
||||||
|
link: /reference/configuration/
|
||||||
|
icon: setting
|
||||||
|
variant: minimal
|
||||||
|
---
|
||||||
|
|
||||||
|
## Основные разделы
|
||||||
|
|
||||||
|
- **Запуск и окружение** - минимальный `.env`, Docker Compose, reverse proxy, обновления и резервные копии.
|
||||||
|
- **Web App / Mini App** - Telegram-авторизация, email-вход, инструкции установки и публичные ссылки.
|
||||||
|
- **Админка и тарифы** - управление пользователями, платежами, темами, каталогом тарифов и premium-сквадами.
|
||||||
|
- **Миграция** - перенос со старого `remnawave-tg-shop` на текущую split-архитектуру.
|
||||||
|
|
||||||
|
## Быстрые ссылки
|
||||||
|
|
||||||
|
- [Развертывание](/reference/deployment/)
|
||||||
|
- [Переменные окружения](/reference/env-vars/)
|
||||||
|
- [Тарифы](/reference/tariffs/)
|
||||||
|
- [Темы Web App](/reference/webapp-themes/)
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
:root {
|
||||||
|
--sl-color-accent-low: #d7f6ef;
|
||||||
|
--sl-color-accent: #0f766e;
|
||||||
|
--sl-color-accent-high: #0b3d39;
|
||||||
|
--sl-color-white: #171717;
|
||||||
|
--sl-color-gray-1: #2a2f35;
|
||||||
|
--sl-color-gray-2: #46505a;
|
||||||
|
--sl-color-gray-3: #6b7580;
|
||||||
|
--sl-color-gray-4: #aab4bd;
|
||||||
|
--sl-color-gray-5: #d7dde2;
|
||||||
|
--sl-color-gray-6: #edf1f4;
|
||||||
|
--sl-color-gray-7: #f7f9fa;
|
||||||
|
--sl-color-black: #ffffff;
|
||||||
|
--sl-font: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='dark'] {
|
||||||
|
--sl-color-accent-low: #06302d;
|
||||||
|
--sl-color-accent: #2dd4bf;
|
||||||
|
--sl-color-accent-high: #cbfbf1;
|
||||||
|
--sl-color-white: #f8fafc;
|
||||||
|
--sl-color-gray-1: #e6eef5;
|
||||||
|
--sl-color-gray-2: #c1ccd6;
|
||||||
|
--sl-color-gray-3: #95a3b3;
|
||||||
|
--sl-color-gray-4: #526171;
|
||||||
|
--sl-color-gray-5: #303b49;
|
||||||
|
--sl-color-gray-6: #1e2937;
|
||||||
|
--sl-color-gray-7: #111827;
|
||||||
|
--sl-color-black: #0a0f18;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-title {
|
||||||
|
font-weight: 760;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
gap: clamp(2rem, 6vw, 5rem);
|
||||||
|
padding-block: clamp(3.5rem, 10vw, 6.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero img {
|
||||||
|
border: 1px solid var(--sl-color-gray-5);
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 24px 70px rgb(15 23 42 / 18%);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='dark'] .hero img {
|
||||||
|
box-shadow: 0 24px 70px rgb(0 0 0 / 36%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sl-markdown-content :is(h2, h3) {
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sl-markdown-content table {
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sl-markdown-content code:not(:where(pre *)) {
|
||||||
|
border: 1px solid var(--sl-color-gray-5);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 0.08rem 0.28rem;
|
||||||
|
}
|
||||||
@@ -2,6 +2,10 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:webapp": "npm --prefix frontend run build:webapp",
|
"build:webapp": "npm --prefix frontend run build:webapp",
|
||||||
|
"build:docs": "npm --prefix docs-site run build",
|
||||||
|
"dev:docs": "npm --prefix docs-site run dev",
|
||||||
|
"preview:docs": "npm --prefix docs-site run preview",
|
||||||
|
"sync:docs": "npm --prefix docs-site run sync:docs",
|
||||||
"lint:py": "python -m ruff check .",
|
"lint:py": "python -m ruff check .",
|
||||||
"lint:js": "npm --prefix frontend run lint",
|
"lint:js": "npm --prefix frontend run lint",
|
||||||
"lint": "npm run lint:py && npm run lint:js",
|
"lint": "npm run lint:py && npm run lint:js",
|
||||||
|
|||||||
Reference in New Issue
Block a user