docs: refactor docs structure
This commit is contained in:
@@ -1,45 +0,0 @@
|
||||
# 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.
|
||||
+78
-22
@@ -6,23 +6,20 @@ export default defineConfig({
|
||||
site: 'https://minishop.minidoc.cc',
|
||||
integrations: [
|
||||
starlight({
|
||||
title: 'Remnawave Minishop',
|
||||
title: 'minishop',
|
||||
favicon: '/favicon.png',
|
||||
description:
|
||||
'Документация по настройке, развертыванию и эксплуатации Remnawave Minishop.',
|
||||
plugins: [
|
||||
starlightThemeNova({
|
||||
nav: [
|
||||
{ label: 'Запуск', href: '/reference/deployment/' },
|
||||
{ label: 'Настройка', href: '/reference/configuration/' },
|
||||
{ label: 'Главная', href: '/' },
|
||||
{ label: 'Установка', href: '/getting-started/setup/' },
|
||||
{ label: 'Платежи', href: '/features/payments/' },
|
||||
{ label: 'GitLab', href: 'https://gitlab.com/3252a8/remnawave-minshop' },
|
||||
],
|
||||
}),
|
||||
],
|
||||
favicon: '/favicon.svg',
|
||||
logo: {
|
||||
src: './src/assets/logo.svg',
|
||||
alt: 'Remnawave Minishop',
|
||||
},
|
||||
customCss: ['./src/styles/custom.css'],
|
||||
lastUpdated: false,
|
||||
locales: {
|
||||
@@ -32,11 +29,19 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
head: [
|
||||
{
|
||||
tag: 'link',
|
||||
attrs: {
|
||||
rel: 'icon',
|
||||
href: '/favicon.webp',
|
||||
type: 'image/webp',
|
||||
},
|
||||
},
|
||||
{
|
||||
tag: 'meta',
|
||||
attrs: {
|
||||
name: 'theme-color',
|
||||
content: '#0f766e',
|
||||
content: '#00fe7a',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -49,32 +54,83 @@ export default defineConfig({
|
||||
],
|
||||
sidebar: [
|
||||
{
|
||||
label: 'Обзор',
|
||||
link: '/',
|
||||
label: 'Начало',
|
||||
items: [
|
||||
{ label: 'Главная', link: '/' },
|
||||
{ label: 'Обзор', slug: 'getting-started/overview' },
|
||||
{ label: 'Установка', slug: 'getting-started/setup' },
|
||||
{ label: 'Deploy examples', slug: 'deploy-examples' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Запуск',
|
||||
label: 'Варианты деплоя',
|
||||
items: [
|
||||
{ label: 'Caddy', slug: 'deploy-examples/caddy' },
|
||||
{ label: 'Nginx', slug: 'deploy-examples/nginx' },
|
||||
{ label: 'Pangolin / Newt', slug: 'deploy-examples/newt' },
|
||||
{ label: 'Без reverse proxy', slug: 'deploy-examples/no-proxy' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Конфигурация',
|
||||
items: [
|
||||
{ label: 'Переменные', slug: 'configuration/env-vars' },
|
||||
{ label: 'Настройка окружения', slug: 'reference/configuration' },
|
||||
{ label: 'Переменные .env', slug: 'reference/env-vars' },
|
||||
{ label: 'Развертывание', slug: 'reference/deployment' },
|
||||
{ label: 'Миграция', slug: 'reference/migration-to-minishop' },
|
||||
{ label: 'Безопасность', slug: 'configuration/security' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Web App',
|
||||
label: 'Возможности',
|
||||
items: [
|
||||
{ label: 'Mini App', slug: 'reference/webapp' },
|
||||
{ label: 'Темы и внешний вид', slug: 'reference/webapp-themes' },
|
||||
{ label: 'Админ-панель', slug: 'reference/admin' },
|
||||
{ label: 'Поддержка', slug: 'reference/support' },
|
||||
{ label: 'Основные', slug: 'features/core' },
|
||||
{ label: 'Платежи', slug: 'features/payments' },
|
||||
{ label: 'Подписки', slug: 'features/subscriptions' },
|
||||
{ label: 'Тарифы', slug: 'features/tariffs' },
|
||||
{ label: 'Mini App', slug: 'features/web-app' },
|
||||
{ label: 'Темы Web App', slug: 'features/webapp-themes' },
|
||||
{ label: 'Админ-панель', slug: 'features/admin-panel' },
|
||||
{ label: 'Поддержка', slug: 'features/support' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Продукт',
|
||||
label: 'Платежные системы',
|
||||
items: [
|
||||
{ label: 'YooKassa', slug: 'payments/yookassa' },
|
||||
{ label: 'FreeKassa', slug: 'payments/freekassa' },
|
||||
{ label: 'Platega', slug: 'payments/platega' },
|
||||
{ label: 'SeverPay', slug: 'payments/severpay' },
|
||||
{ label: 'Wata', slug: 'payments/wata' },
|
||||
{ label: 'CryptoPay', slug: 'payments/cryptopay' },
|
||||
{ label: 'Heleket', slug: 'payments/heleket' },
|
||||
{ label: 'Telegram Stars', slug: 'payments/telegram-stars' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Администрирование',
|
||||
items: [
|
||||
{ label: 'Пользователи', slug: 'administration/users' },
|
||||
{ label: 'Обслуживание', slug: 'administration/maintenance' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Миграции',
|
||||
items: [
|
||||
{ label: 'Обзор миграций', slug: 'migrations' },
|
||||
{ label: 'remnawave-tg-shop', slug: 'migrations/remnawave-tg-shop' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Устранение неполадок',
|
||||
items: [
|
||||
{ label: 'Проблемы', slug: 'troubleshooting/issues' },
|
||||
{ label: 'Логи', slug: 'troubleshooting/logs' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Справочник',
|
||||
items: [
|
||||
{ label: 'Тарифы', slug: 'reference/tariffs' },
|
||||
{ label: 'Архитектура', slug: 'reference/architecture' },
|
||||
{ label: 'Развертывание', slug: 'reference/deployment' },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
@@ -1,5 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 396 B |
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
+138
-31
@@ -5,19 +5,44 @@ 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 outputDir = path.join(siteRoot, 'src', 'content', 'docs');
|
||||
|
||||
const descriptions = {
|
||||
'admin.md': 'Возможности админ-панели, управление пользователями, настройками, тарифами и поддержкой.',
|
||||
'index.md': 'Документация по запуску, настройке и сопровождению Telegram Mini App для Remnawave.',
|
||||
'getting-started/overview.md': 'Что входит в Remnawave Minishop и как связаны бот, Mini App, backend, worker и Remnawave Panel.',
|
||||
'getting-started/setup.md': 'Минимальный путь запуска Remnawave Minishop через Docker Compose.',
|
||||
'configuration/security.md': 'Секреты, публичные URL, доступ администраторов и базовые меры защиты Minishop.',
|
||||
'configuration/env-vars.md': 'Полный справочник переменных окружения Remnawave Minishop.',
|
||||
'features/core.md': 'Пользовательские и админские сценарии Remnawave Minishop.',
|
||||
'features/payments.md': 'Платежные провайдеры, кнопки оплаты и webhook-обработка.',
|
||||
'features/subscriptions.md': 'Period- и traffic-тарифы, premium-сквады, HWID-устройства и жизненный цикл подписки.',
|
||||
'features/tariffs.md': 'Каталог тарифов, period/traffic-модели, premium-сквады и HWID-устройства.',
|
||||
'features/web-app.md': 'Telegram Mini App, авторизация, публичные инструкции и проксирование.',
|
||||
'features/webapp-themes.md': 'Кастомные темы, CSS-токены, ассеты и пайплайн создания темы.',
|
||||
'features/admin-panel.md': 'Возможности админ-панели, управление пользователями, настройками, тарифами и поддержкой.',
|
||||
'features/support.md': 'Пользовательские тикеты, админский inbox, уведомления и лимиты поддержки.',
|
||||
'deploy-examples/index.md': 'Как выбрать готовый deploy example для production или проверки.',
|
||||
'deploy-examples/caddy.md': 'Запуск Remnawave Minishop с Caddy и автоматическим HTTPS.',
|
||||
'deploy-examples/nginx.md': 'Запуск Remnawave Minishop с Nginx и внешними TLS-сертификатами.',
|
||||
'deploy-examples/newt.md': 'Запуск через Pangolin/Newt без входящих портов на сервере приложения.',
|
||||
'deploy-examples/no-proxy.md': 'Прямой запуск backend и frontend портов без reverse proxy.',
|
||||
'migrations/index.md': 'Готовые сценарии миграции в Remnawave Minishop с других ботов.',
|
||||
'migrations/remnawave-tg-shop.md': 'Перенос данных со старого remnawave-tg-shop на split-архитектуру Minishop.',
|
||||
'payments/yookassa.md': 'Быстрый вход в настройку YooKassa для Remnawave Minishop.',
|
||||
'payments/freekassa.md': 'Быстрый вход в настройку FreeKassa для Remnawave Minishop.',
|
||||
'payments/platega.md': 'Настройка Platega, отдельных СБП/карта и crypto-кнопок.',
|
||||
'payments/severpay.md': 'Настройка SeverPay, MID, token, return URL и срока жизни ссылки.',
|
||||
'payments/wata.md': 'Настройка Wata, API token, TTL ссылки, подписи webhook и trusted IP.',
|
||||
'payments/cryptopay.md': 'Настройка CryptoPay, токена, сети, currency type и asset.',
|
||||
'payments/heleket.md': 'Настройка Heleket, merchant ID, payment API key, invoice currency и webhook-проверок.',
|
||||
'payments/telegram-stars.md': 'Оплата подписок и докупок через Telegram Stars.',
|
||||
'administration/users.md': 'Где управлять пользователями, подписками, блокировками и поддержкой.',
|
||||
'administration/maintenance.md': 'Обновления, миграции, резервные копии и проверки production-стека.',
|
||||
'troubleshooting/issues.md': 'Короткие чеклисты для частых проблем запуска, вебхуков, Mini App и платежей.',
|
||||
'troubleshooting/logs.md': 'Какие логи смотреть при диагностике backend, worker, frontend, миграций и вебхуков.',
|
||||
'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']);
|
||||
@@ -26,23 +51,45 @@ function yamlString(value) {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
function slugFor(fileName) {
|
||||
return fileName.replace(/\.md$/i, '');
|
||||
function toPosix(relativePath) {
|
||||
return relativePath.split(path.sep).join('/');
|
||||
}
|
||||
|
||||
function extractTitle(fileName, content) {
|
||||
function outputRelativePath(sourceRelativePath) {
|
||||
if (sourceRelativePath === 'index.md') {
|
||||
return 'index.md';
|
||||
}
|
||||
if (!sourceRelativePath.includes('/')) {
|
||||
return `reference/${sourceRelativePath}`;
|
||||
}
|
||||
return sourceRelativePath;
|
||||
}
|
||||
|
||||
function pagePathForSource(sourceRelativePath, hash = '') {
|
||||
const output = outputRelativePath(sourceRelativePath).replace(/\.md$/i, '');
|
||||
const route = output === 'index' ? '/' : `/${output.replace(/\/index$/u, '')}/`;
|
||||
return `${route}${hash}`;
|
||||
}
|
||||
|
||||
function titleForRelativePath(relativePath) {
|
||||
const baseName = path.posix.basename(relativePath, '.md');
|
||||
return baseName;
|
||||
}
|
||||
|
||||
function extractTitle(relativePath, content) {
|
||||
const match = content.match(/^#\s+(.+?)\s*$/m);
|
||||
return match?.[1] ?? slugFor(fileName);
|
||||
return match?.[1] ?? titleForRelativePath(relativePath);
|
||||
}
|
||||
|
||||
function stripFirstHeading(content) {
|
||||
return content.replace(/^#\s+.+?\s*\r?\n+/, '');
|
||||
}
|
||||
|
||||
function rewriteMarkdownLinks(markdown) {
|
||||
function rewriteMarkdownLinks(markdown, sourceRelativePath) {
|
||||
const sourceDirectory = path.posix.dirname(sourceRelativePath);
|
||||
return markdown.replace(/\]\((?!https?:\/\/|mailto:|tel:|\/|#)([^)\s]+\.md)(#[^)]+)?\)/g, (match, target, hash = '') => {
|
||||
const slug = slugFor(path.posix.basename(target));
|
||||
return `](/reference/${slug}/${hash})`;
|
||||
const resolvedTarget = path.posix.normalize(path.posix.join(sourceDirectory, target));
|
||||
return `](${pagePathForSource(resolvedTarget, hash)})`;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,45 +99,105 @@ function normalizeCodeFences(markdown) {
|
||||
.replace(/^```caddyfile\s*$/gim, '```txt');
|
||||
}
|
||||
|
||||
function frontmatter({ title, description, fileName }) {
|
||||
const editUrl = `https://gitlab.com/3252a8/remnawave-minshop/-/edit/main/docs/${encodeURIComponent(fileName)}`;
|
||||
function extraFrontmatter(sourceRelativePath) {
|
||||
if (sourceRelativePath !== 'index.md') {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
'template: splash',
|
||||
'hero:',
|
||||
' tagline: "Telegram-бот и Mini App для продажи подписок Remnawave: платежи, тарифы, админка, поддержка и инструкции подключения."',
|
||||
' image:',
|
||||
' html: \'<img class="minishop-hero-screenshot" src="/remnawave-minishop.webp" alt="Интерфейс Remnawave Minishop" width="1920" height="1080" loading="eager" decoding="async" />\'',
|
||||
' actions:',
|
||||
' - text: "Быстрый старт"',
|
||||
' link: /getting-started/setup/',
|
||||
' icon: right-arrow',
|
||||
' - text: "Deploy examples"',
|
||||
' link: /deploy-examples/',
|
||||
' icon: setting',
|
||||
' variant: minimal',
|
||||
];
|
||||
}
|
||||
|
||||
function frontmatter({ title, description, sourceRelativePath }) {
|
||||
const editPath = sourceRelativePath
|
||||
.split('/')
|
||||
.map((segment) => encodeURIComponent(segment))
|
||||
.join('/');
|
||||
const editUrl = `https://gitlab.com/3252a8/remnawave-minshop/-/edit/main/docs/${editPath}`;
|
||||
return [
|
||||
'---',
|
||||
`title: ${yamlString(title)}`,
|
||||
`description: ${yamlString(description)}`,
|
||||
`editUrl: ${yamlString(editUrl)}`,
|
||||
...extraFrontmatter(sourceRelativePath),
|
||||
'---',
|
||||
'',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
async function syncMarkdown(entries) {
|
||||
for (const entry of entries.filter((item) => item.name.endsWith('.md'))) {
|
||||
const sourcePath = path.join(sourceDir, entry.name);
|
||||
async function walk(directory) {
|
||||
const entries = await readdir(directory, { withFileTypes: true });
|
||||
const files = [];
|
||||
for (const entry of entries) {
|
||||
const absolutePath = path.join(directory, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
files.push(...(await walk(absolutePath)));
|
||||
continue;
|
||||
}
|
||||
if (entry.isFile()) {
|
||||
files.push(absolutePath);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
async function syncMarkdown(files) {
|
||||
for (const sourcePath of files.filter((file) => file.endsWith('.md'))) {
|
||||
const sourceRelativePath = toPosix(path.relative(sourceDir, sourcePath));
|
||||
const outputRelative = outputRelativePath(sourceRelativePath);
|
||||
const outputPath = path.join(outputDir, ...outputRelative.split('/'));
|
||||
const content = await readFile(sourcePath, 'utf8');
|
||||
const title = extractTitle(entry.name, content);
|
||||
const body = normalizeCodeFences(rewriteMarkdownLinks(stripFirstHeading(content).trimStart()));
|
||||
const title = extractTitle(sourceRelativePath, content);
|
||||
const body = normalizeCodeFences(
|
||||
rewriteMarkdownLinks(stripFirstHeading(content).trimStart(), sourceRelativePath),
|
||||
);
|
||||
const output = frontmatter({
|
||||
title,
|
||||
description: descriptions[entry.name] ?? title,
|
||||
fileName: entry.name,
|
||||
description: descriptions[sourceRelativePath] ?? title,
|
||||
sourceRelativePath,
|
||||
});
|
||||
|
||||
await writeFile(path.join(outputDir, entry.name), `${output}${body}\n`, 'utf8');
|
||||
await mkdir(path.dirname(outputPath), { recursive: true });
|
||||
await writeFile(outputPath, `${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));
|
||||
async function syncAssets(files) {
|
||||
for (const sourcePath of files.filter((file) => imageExtensions.has(path.extname(file).toLowerCase()))) {
|
||||
const sourceRelativePath = toPosix(path.relative(sourceDir, sourcePath));
|
||||
const outputRelative = !sourceRelativePath.includes('/')
|
||||
? sourceRelativePath
|
||||
: sourceRelativePath;
|
||||
const outputPath = path.join(outputDir, ...outputRelative.split('/'));
|
||||
await mkdir(path.dirname(outputPath), { recursive: true });
|
||||
await copyFile(sourcePath, outputPath);
|
||||
|
||||
if (!sourceRelativePath.includes('/')) {
|
||||
const referenceOutputPath = path.join(outputDir, 'reference', sourceRelativePath);
|
||||
await mkdir(path.dirname(referenceOutputPath), { recursive: true });
|
||||
await copyFile(sourcePath, referenceOutputPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
const files = await walk(sourceDir);
|
||||
await syncMarkdown(files);
|
||||
await syncAssets(files);
|
||||
|
||||
console.log(`Synced documentation from ${path.relative(repoRoot, sourceDir)} to ${path.relative(repoRoot, outputDir)}`);
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 536 B |
@@ -1,32 +0,0 @@
|
||||
---
|
||||
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/)
|
||||
@@ -1,25 +1,73 @@
|
||||
@layer starlight, nova, minishop;
|
||||
|
||||
@layer minishop {
|
||||
.site-title {
|
||||
.site-title,
|
||||
.site-title span {
|
||||
color: #00fe7a;
|
||||
font-weight: 760;
|
||||
}
|
||||
|
||||
.site-title:hover,
|
||||
.site-title:hover span {
|
||||
color: #00fe7a;
|
||||
}
|
||||
|
||||
.hero {
|
||||
gap: clamp(2rem, 6vw, 5rem);
|
||||
padding-block: clamp(3.5rem, 10vw, 6.5rem);
|
||||
}
|
||||
|
||||
.hero > .hero-html:has(.minishop-hero-screenshot) {
|
||||
width: min(96vw, 72rem);
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.hero img {
|
||||
border: 1px solid var(--sl-color-gray-5);
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 24px 70px rgb(15 23 42 / 18%);
|
||||
}
|
||||
|
||||
.minishop-hero-screenshot {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
:root[data-theme='dark'] .hero img {
|
||||
box-shadow: 0 24px 70px rgb(0 0 0 / 36%);
|
||||
}
|
||||
|
||||
@media (min-width: 50rem) {
|
||||
.hero:has(.minishop-hero-screenshot) {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: clamp(2rem, 4vw, 4rem);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hero > .hero-html:has(.minishop-hero-screenshot) {
|
||||
order: 2;
|
||||
width: min(100%, 90rem);
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.hero:has(.minishop-hero-screenshot) .stack {
|
||||
align-items: center;
|
||||
max-width: 64rem;
|
||||
margin-inline: auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero:has(.minishop-hero-screenshot) .copy {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hero:has(.minishop-hero-screenshot) .actions {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.sl-markdown-content :is(h2, h3) {
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user