Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ef904ca952 | |||
| e8dc61f5a1 | |||
| 62f12c8ce1 | |||
| a9cafb629c | |||
| abdc7bd70e | |||
| 7fd1755a7d |
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "docs",
|
"name": "docs",
|
||||||
"version": "0.6.5-main",
|
"version": "0.6.4-link",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "docs",
|
"name": "docs",
|
||||||
"version": "0.6.5-main",
|
"version": "0.6.4-link",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@beeline/design-tokens": "^1.31.6",
|
"@beeline/design-tokens": "^1.31.6",
|
||||||
|
|||||||
+236
-236
@@ -2,7 +2,7 @@ import { defineConfig } from 'vitepress'
|
|||||||
import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
|
import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
|
||||||
import { viteStaticCopy } from 'vite-plugin-static-copy'
|
import { viteStaticCopy } from 'vite-plugin-static-copy'
|
||||||
import { overrideComponents } from './override-components'
|
import { overrideComponents } from './override-components'
|
||||||
import { resolve } from 'node:path'
|
import { resolve } from 'path'
|
||||||
import { fileURLToPath, URL } from 'node:url'
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
const gitlab = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
const gitlab = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
@@ -41,7 +41,240 @@ const gitlab = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|||||||
`
|
`
|
||||||
|
|
||||||
const new_version = process.env?.VITE_NEW_VERSION;
|
const new_version = process.env?.VITE_NEW_VERSION;
|
||||||
console.log({ base: typeof new_version !== 'undefined' ? '/' : '/docs/' })
|
|
||||||
|
const sidebarConfig = {
|
||||||
|
'/platform/': [
|
||||||
|
{
|
||||||
|
text: 'Платформа Beeline Cloud', link: '/platform/index.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{text: 'Обзор', link: '/platform/about.md'},
|
||||||
|
{text: 'Техническая поддержка', link: '/platform/support/support-overview.md'},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'/start/': [
|
||||||
|
{
|
||||||
|
text: 'Начало работы в Beeline Cloud', link: '/start/index.md',
|
||||||
|
},
|
||||||
|
{text: 'Начать работу', link: '/start/getting-started.md'},
|
||||||
|
{text: 'Бесплатный период', link: '/start/trial.md'},
|
||||||
|
{text: 'Платное использование', link: '/start/organization.md'},
|
||||||
|
],
|
||||||
|
// '/billing/': [
|
||||||
|
|
||||||
|
// ],
|
||||||
|
'/backups/': [
|
||||||
|
{
|
||||||
|
text: 'Резервное копирование', link: '/backups/index.md',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Обзор сервиса', link: '/backups/about.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{text: 'Квоты и лимиты', link: '/backups/backup-quatos.md'},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{text: 'Резервное копирование виртуальных машин Beeline Cloud', link: '/backups/backup-internal-infra.md'},
|
||||||
|
{text: 'Резервное копирование собственной инфраструктуры в Beeline Cloud', link: '/backups/backup-external-infra.md'},
|
||||||
|
{text: 'Каталог резервных копий', link: '/backups/view-backups.md'},
|
||||||
|
|
||||||
|
],
|
||||||
|
'/vdc/': [
|
||||||
|
{
|
||||||
|
text: 'Виртуальные дата-центры на VMware', link: '/vdc/index.md',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Обзор сервиса', link: '/vdc/vdc-overview.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'О сервисе', link: '/vdc/vdc-about.md' },
|
||||||
|
{ text: 'Техническое описание', link: '/vdc/vdc-tech.md' },
|
||||||
|
{ text: 'Квоты и лимиты', link: '/vdc/vdc-quatos.md' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Быстрый старт', link: '/vdc/vdc-getting-started.md'
|
||||||
|
},
|
||||||
|
|
||||||
|
{ text: 'Виртуальные дата-центры', link: '/vdc/vdc-how-to/vdc-index.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Создание дата-центра', link: '/vdc/vdc-how-to/vdc-create.md' },
|
||||||
|
{ text: 'Вход в дата-центр', link: '/vdc/vdc-how-to/vdc-enter.md' },
|
||||||
|
{ text: 'Управление дата-центром', link: '/vdc/vdc-how-to/vdc-manage.md'},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ text: 'Виртуальные машины', link: '/vdc/vdc-how-to/vm/vm-index.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{text: 'Создание ВМ', link: '/vdc/vdc-how-to/vm/create-vm.md'},
|
||||||
|
{text: 'Создание vApp', link: '/vdc/vdc-how-to/vm/create-vapp.md'},
|
||||||
|
{text: 'Управление состоянием ВМ', link: '/vdc/vdc-how-to/vm/manage-vm.md'},
|
||||||
|
{text: 'Клонирование ВМ', link: '/vdc/vdc-how-to/vm/clone-vm.md'},
|
||||||
|
{text: 'Изменение конфигурации ВМ', link: '/vdc/vdc-how-to/vm/edit-vm.md'},
|
||||||
|
{text: 'Удаление ВМ', link: '/vdc/vdc-how-to/vm/delete-vm.md'},
|
||||||
|
{text: 'Группы размещения', link: '/vdc/vdc-how-to/vm/create-affinity-rules.md'},
|
||||||
|
{text: 'Снимки ВМ', link: '/vdc/vdc-how-to/vm/create-snapshot.md'},
|
||||||
|
{text: 'VMware Tools', link: '/vdc/vdc-how-to/vm/vmware-tools.md'},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ text: 'Диски', link: '/vdc/vdc-how-to/disks/disks-index.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{text: 'Обзор', link: '/vdc/vdc-how-to/disks/about.md'},
|
||||||
|
{text: 'Создание диска', link: '/vdc/vdc-how-to/disks/create-disk.md'},
|
||||||
|
{text: 'Проверка состояния диска', link: '/vdc/vdc-how-to/disks/view-disk.md'},
|
||||||
|
{text: 'Управление выделенными дисками', link: '/vdc/vdc-how-to/disks/attach-disk.md'},
|
||||||
|
{text: 'Изменение политики хранения дисков ВМ', link: '/vdc/vdc-how-to/disks/change-storage-policy-of-vm.md'},
|
||||||
|
{text: 'Редактирование параметров диска', link: '/vdc/vdc-how-to/disks/edit-disk.md'},
|
||||||
|
{text: 'Удаление диска', link: '/vdc/vdc-how-to/disks/delete-disk.md'},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ text: 'Сети', link: '/vdc/vdc-how-to/networks/networks-index.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{text: 'Обзор', link: '/vdc/vdc-how-to/networks/about.md'},
|
||||||
|
{text: 'Настройка доступа к ВМ из интернета', link: '/vdc/vdc-how-to/networks/allow-external-connections-to-vm.md'},
|
||||||
|
{text: 'Подключение ВМ в vApp к сети', link: '/vdc/vdc-how-to/networks/connect-vapp-to-network.md'},
|
||||||
|
{text: 'Подключение ВМ к интернету', link: '/vdc/vdc-how-to/networks/connect-vm-to-network.md'},
|
||||||
|
{text: 'Создание сети в организации и подключение к Edge Gateway', link: '/vdc/vdc-how-to/networks/create-network.md'},
|
||||||
|
{text: 'Подключение сети к Edge Gateway', link: '/vdc/vdc-how-to/networks/connect-to-edge-gateway.md'},
|
||||||
|
{text: 'Создание Pre-Shared Key', link: '/vdc/vdc-how-to/networks/create-psk.md'},
|
||||||
|
{ text: 'Настройка site-to-site подключения с помощью IPSec', link: '/vdc/vdc-how-to/networks/how-to-setup-ipsec-vpn.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{text: 'Настройка ASAv', link: '/vdc/vdc-how-to/networks/ipsec/asav.md'},
|
||||||
|
{text: 'Настройка CSR 1000v', link: '/vdc/vdc-how-to/networks/ipsec/csr1000v.md'},
|
||||||
|
{text: 'Настройка Fortigate', link: '/vdc/vdc-how-to/networks/ipsec/fortigate.md'},
|
||||||
|
{text: 'Проверить сетевую связанность', link: '/vdc/vdc-how-to/networks/ipsec/check-vpn-status.md'},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ text: 'Пользователи и роли', link: '/vdc/vdc-how-to/users/users-index.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{text: 'Ролевая модель', link: '/vdc/vdc-how-to/users/roles.md'},
|
||||||
|
{text: 'Создание пользователя', link: '/vdc/vdc-how-to/users/add-user.md'},
|
||||||
|
{text: 'Изменение пароля пользователя', link: '/vdc/vdc-how-to/users/change-users-password.md'},
|
||||||
|
{text: 'Настройка квот', link: '/vdc/vdc-how-to/users/quotas.md'},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ text: 'Двухфакторная аутентификация', link: '/vdc/vdc-how-to/vdc-2fa.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{text: 'Подключение 2FA', link: '/vdc/vdc-how-to/vdc-2fa-start.md'},
|
||||||
|
{text: 'Управление 2FA', link: '/vdc/vdc-how-to/vdc-2fa-manage.md'},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
// { text: 'Тарификация', link: '/vdc/vdc-tarif.md' },
|
||||||
|
'/compute/': [
|
||||||
|
{
|
||||||
|
text: 'Виртуальные машины', link: '/compute/index.md',
|
||||||
|
},
|
||||||
|
{ text: 'Обзор сервиса', link: '/compute/compute-overview-index.md' ,
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Техническое описание', link: '/compute/compute-overview.md' },
|
||||||
|
{ text: 'Квоты и лимиты', link: '/compute/compute-quatos.md' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{text: 'Быстрый старт', link: '/compute/compute-getting-started.md', excludeFromIndex: true },
|
||||||
|
{ text: 'Виртуальные машины', link: '/compute/compute-how-to/compute-index.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Создание ВМ', link: '/compute/compute-how-to/compute-servers-create.md' },
|
||||||
|
{ text: 'Создание ВМ джамп-хоста', link: '/compute/compute-how-to/compute-servers-jump-create.md' },
|
||||||
|
{ text: 'Подключение к ВМ', link: '/compute/compute-how-to/compute-connect-index.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Подключение по SSH по внешнему IP-адресу с помощью ключевой пары', link: '/compute/compute-how-to/compute-connect-public.md'},
|
||||||
|
{ text: 'Подключение по SSH по внутреннему IP-адресу с помощью ключевой пары', link: '/compute/compute-how-to/compute-connect-inside.md' },
|
||||||
|
{ text: 'Подключение по SSH по логину и паролю', link: '/compute/compute-how-to/compute-connect-pwd.md' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ text: 'Управление ВМ', link: '/compute/compute-how-to/compute-servers-manage.md' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ text: 'Диски', link: '/compute/compute-how-to/compute-disks/compute-disk-index.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Обзор', link: '/compute/compute-how-to/compute-disks/compute-disk-about.md' },
|
||||||
|
{ text: 'Создание диска', link: '/compute/compute-how-to/compute-disks/compute-disk-create.md' },
|
||||||
|
{ text: 'Управление дисками', link: '/compute/compute-how-to/compute-disks/compute-disk-manage.md' },
|
||||||
|
{ text: 'Удаление диска', link: '/compute/compute-how-to/compute-disks/compute-disk-del.md' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ text: 'IP-адреса', link: '/compute/compute-how-to/compute-ip/compute-ip-index.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Обзор', link: '/compute/compute-how-to/compute-ip/compute-ip-about.md' },
|
||||||
|
{ text: 'Просмотр IP-адресов', link: '/compute/compute-how-to/compute-ip/compute-ip-view.md' },
|
||||||
|
{ text: 'Создание IP-адреса', link: '/compute/compute-how-to/compute-ip/compute-ip-create.md' },
|
||||||
|
{ text: 'Управление IP-адресами', link: '/compute/compute-how-to/compute-ip/compute-ip-manager.md' },
|
||||||
|
{ text: 'Удаление IP-адреса', link: '/compute/compute-how-to/compute-ip/compute-ip-del.md' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ text: 'Группы размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-index.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Обзор', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-about.md'},
|
||||||
|
{ text: 'Создание группы размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-create.md'},
|
||||||
|
{ text: 'Управление группами размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-manager.md' },
|
||||||
|
{ text: 'Удаление группы размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-del.md' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ text: 'Сети', link: '/compute/compute-how-to/compute-network/compute-network-index.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Настройка site-to-site VPN с помощью VyOS', link: '/compute/compute-how-to/compute-network/compute-vpn-vyos.md' },
|
||||||
|
{ text: 'Подключение ВМ закрытого контура к интернету', link: '/compute/compute-how-to/compute-network/compute-network-inside.md' },
|
||||||
|
],
|
||||||
|
excludeFromIndex: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'/admin/': [
|
||||||
|
{
|
||||||
|
text: 'Администрирование', link: '/admin/index.md',
|
||||||
|
},
|
||||||
|
{text: 'Управление ключевыми парами', link: '/admin/ssh.md'},
|
||||||
|
],
|
||||||
|
'/vdi/': [
|
||||||
|
{
|
||||||
|
text: 'Виртуальные рабочие столы', link: '/vdi/index.md',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Обзор сервиса', link: '/vdi/vdi-overview.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'О сервисе', link: '/vdi/vdi-about.md' },
|
||||||
|
{ text: 'Техническое описание', link: '/vdi/vdi-tech.md' },
|
||||||
|
{ text: 'Квоты и лимиты', link: '/vdi/vdi-quatos.md' },
|
||||||
|
{ text: 'Тарификация', link: '/vdi/vdi-tarif.md' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Заказ виртуальных рабочих столов', link: '/vdi/vdi-how-to/vdi-create.md'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Настройка сервиса', link: '/vdi/vdi-how-to/vdi-nastroika.md',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Настройка интеграции с Active Directory', link: '/vdi/vdi-how-to/vdi-connect-to-ad.md' },
|
||||||
|
{ text: 'Настройка сети', link: '/vdi/vdi-how-to/vdi-interconnect.md' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// text: 'Gold-образ', link: '/vdi/vdi-how-to/vdi-gold.md'
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
text: 'Подключение к виртуальному рабочему месту', link: '/vdi/vdi-how-to/vdi-connect.md'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
// https://vitepress.dev/reference/site-config
|
// https://vitepress.dev/reference/site-config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@@ -140,240 +373,7 @@ export default defineConfig({
|
|||||||
outline: {
|
outline: {
|
||||||
label: 'Содержание'
|
label: 'Содержание'
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: sidebarConfig,
|
||||||
'/platform/': [
|
|
||||||
{
|
|
||||||
text: 'Платформа Beeline Cloud', link: '/platform/index.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{text: 'Обзор', link: '/platform/about.md'},
|
|
||||||
{text: 'Техническая поддержка', link: '/platform/support/support-overview.md'},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'/start/': [
|
|
||||||
{
|
|
||||||
text: 'Начало работы в Beeline Cloud', link: '/start/index.md',
|
|
||||||
},
|
|
||||||
{text: 'Начать работу', link: '/start/getting-started.md'},
|
|
||||||
{text: 'Бесплатный период', link: '/start/trial.md'},
|
|
||||||
{text: 'Платное использование', link: '/start/organization.md'},
|
|
||||||
],
|
|
||||||
// '/billing/': [
|
|
||||||
|
|
||||||
// ],
|
|
||||||
'/backups/': [
|
|
||||||
{
|
|
||||||
text: 'Резервное копирование', link: '/backups/index.md',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Обзор сервиса', link: '/backups/backups-overview.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{text: 'О сервисе', link: '/backups/about.md'},
|
|
||||||
{text: 'Квоты и лимиты', link: '/backups/backup-quatos.md'},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{text: 'Резервное копирование виртуальных машин Beeline Cloud', link: '/backups/backup-internal-infra.md'},
|
|
||||||
{text: 'Резервное копирование собственной инфраструктуры в Beeline Cloud', link: '/backups/backup-external-infra.md'},
|
|
||||||
{text: 'Каталог резервных копий', link: '/backups/view-backups.md'},
|
|
||||||
|
|
||||||
],
|
|
||||||
'/vdc/': [
|
|
||||||
{
|
|
||||||
text: 'Виртуальные дата-центры на VMware', link: '/vdc/index.md',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Обзор сервиса', link: '/vdc/vdc-overview.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{ text: 'О сервисе', link: '/vdc/vdc-about.md' },
|
|
||||||
{ text: 'Техническое описание', link: '/vdc/vdc-tech.md' },
|
|
||||||
{ text: 'Квоты и лимиты', link: '/vdc/vdc-quatos.md' },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Быстрый старт', link: '/vdc/vdc-getting-started.md'
|
|
||||||
},
|
|
||||||
|
|
||||||
{ text: 'Виртуальные дата-центры', link: '/vdc/vdc-how-to/vdc-index.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{ text: 'Создание дата-центра', link: '/vdc/vdc-how-to/vdc-create.md' },
|
|
||||||
{ text: 'Вход в дата-центр', link: '/vdc/vdc-how-to/vdc-enter.md' },
|
|
||||||
{ text: 'Управление дата-центром', link: '/vdc/vdc-how-to/vdc-manage.md'},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ text: 'Виртуальные машины', link: '/vdc/vdc-how-to/vm/vm-index.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{text: 'Создание ВМ', link: '/vdc/vdc-how-to/vm/create-vm.md'},
|
|
||||||
{text: 'Создание vApp', link: '/vdc/vdc-how-to/vm/create-vapp.md'},
|
|
||||||
{text: 'Управление состоянием ВМ', link: '/vdc/vdc-how-to/vm/manage-vm.md'},
|
|
||||||
{text: 'Клонирование ВМ', link: '/vdc/vdc-how-to/vm/clone-vm.md'},
|
|
||||||
{text: 'Изменение конфигурации ВМ', link: '/vdc/vdc-how-to/vm/edit-vm.md'},
|
|
||||||
{text: 'Удаление ВМ', link: '/vdc/vdc-how-to/vm/delete-vm.md'},
|
|
||||||
{text: 'Группы размещения', link: '/vdc/vdc-how-to/vm/create-affinity-rules.md'},
|
|
||||||
{text: 'Снимки ВМ', link: '/vdc/vdc-how-to/vm/create-snapshot.md'},
|
|
||||||
{text: 'VMware Tools', link: '/vdc/vdc-how-to/vm/vmware-tools.md'},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ text: 'Диски', link: '/vdc/vdc-how-to/disks/disks-index.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{text: 'Обзор', link: '/vdc/vdc-how-to/disks/about.md'},
|
|
||||||
{text: 'Создание диска', link: '/vdc/vdc-how-to/disks/create-disk.md'},
|
|
||||||
{text: 'Проверка состояния диска', link: '/vdc/vdc-how-to/disks/view-disk.md'},
|
|
||||||
{text: 'Управление выделенными дисками', link: '/vdc/vdc-how-to/disks/attach-disk.md'},
|
|
||||||
{text: 'Изменение политики хранения дисков ВМ', link: '/vdc/vdc-how-to/disks/change-storage-policy-of-vm.md'},
|
|
||||||
{text: 'Редактирование параметров диска', link: '/vdc/vdc-how-to/disks/edit-disk.md'},
|
|
||||||
{text: 'Удаление диска', link: '/vdc/vdc-how-to/disks/delete-disk.md'},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ text: 'Сети', link: '/vdc/vdc-how-to/networks/networks-index.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{text: 'Обзор', link: '/vdc/vdc-how-to/networks/about.md'},
|
|
||||||
{text: 'Настройка доступа к ВМ из интернета', link: '/vdc/vdc-how-to/networks/allow-external-connections-to-vm.md'},
|
|
||||||
{text: 'Подключение ВМ в vApp к сети', link: '/vdc/vdc-how-to/networks/connect-vapp-to-network.md'},
|
|
||||||
{text: 'Подключение ВМ к интернету', link: '/vdc/vdc-how-to/networks/connect-vm-to-network.md'},
|
|
||||||
{text: 'Создание сети в организации и подключение к Edge Gateway', link: '/vdc/vdc-how-to/networks/create-network.md'},
|
|
||||||
{text: 'Подключение сети к Edge Gateway', link: '/vdc/vdc-how-to/networks/connect-to-edge-gateway.md'},
|
|
||||||
{text: 'Создание Pre-Shared Key', link: '/vdc/vdc-how-to/networks/create-psk.md'},
|
|
||||||
{ text: 'Настройка site-to-site подключения с помощью IPSec', link: '/vdc/vdc-how-to/networks/how-to-setup-ipsec-vpn.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{text: 'Настройка IPSec VPN', link: '/vdc/vdc-how-to/networks/ipsec/setup-ipsec-vpn.md'},
|
|
||||||
{text: 'Настройка ASAv', link: '/vdc/vdc-how-to/networks/ipsec/asav.md'},
|
|
||||||
{text: 'Настройка CSR 1000v', link: '/vdc/vdc-how-to/networks/ipsec/csr1000v.md'},
|
|
||||||
{text: 'Настройка Fortigate', link: '/vdc/vdc-how-to/networks/ipsec/fortigate.md'},
|
|
||||||
{text: 'Проверить сетевую связанность', link: '/vdc/vdc-how-to/networks/ipsec/check-vpn-status.md'},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ text: 'Пользователи и роли', link: '/vdc/vdc-how-to/users/users-index.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{text: 'Ролевая модель', link: '/vdc/vdc-how-to/users/roles.md'},
|
|
||||||
{text: 'Создание пользователя', link: '/vdc/vdc-how-to/users/add-user.md'},
|
|
||||||
{text: 'Изменение пароля пользователя', link: '/vdc/vdc-how-to/users/change-users-password.md'},
|
|
||||||
{text: 'Настройка квот', link: '/vdc/vdc-how-to/users/quotas.md'},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ text: 'Двухфакторная аутентификация', link: '/vdc/vdc-how-to/vdc-2fa.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{text: 'Подключение 2FA', link: '/vdc/vdc-how-to/vdc-2fa-start.md'},
|
|
||||||
{text: 'Управление 2FA', link: '/vdc/vdc-how-to/vdc-2fa-manage.md'},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
// { text: 'Тарификация', link: '/vdc/vdc-tarif.md' },
|
|
||||||
'/compute/': [
|
|
||||||
{
|
|
||||||
text: 'Виртуальные машины', link: '/compute/index.md',
|
|
||||||
},
|
|
||||||
{ text: 'Обзор сервиса', link: '/compute/compute-overview-index.md' ,
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{ text: 'Техническое описание', link: '/compute/compute-overview.md' },
|
|
||||||
{ text: 'Квоты и лимиты', link: '/compute/compute-quatos.md' },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{text: 'Быстрый старт', link: '/compute/compute-getting-started.md'},
|
|
||||||
{ text: 'Виртуальные машины', link: '/compute/compute-how-to/compute-index.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{ text: 'Создание ВМ', link: '/compute/compute-how-to/compute-servers-create.md' },
|
|
||||||
{ text: 'Создание ВМ джамп-хоста', link: '/compute/compute-how-to/compute-servers-jump-create.md' },
|
|
||||||
{ text: 'Подключение к ВМ', link: '/compute/compute-how-to/compute-connect-index.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{ text: 'Подключение по SSH по внешнему IP-адресу с помощью ключевой пары', link: '/compute/compute-how-to/compute-connect-public.md'},
|
|
||||||
{ text: 'Подключение по SSH по внутреннему IP-адресу с помощью ключевой пары', link: '/compute/compute-how-to/compute-connect-inside.md' },
|
|
||||||
{ text: 'Подключение по SSH по логину и паролю', link: '/compute/compute-how-to/compute-connect-pwd.md' },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{ text: 'Управление ВМ', link: '/compute/compute-how-to/compute-servers-manage.md' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{ text: 'Диски', link: '/compute/compute-how-to/compute-disks/compute-disk-index.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{ text: 'Обзор', link: '/compute/compute-how-to/compute-disks/compute-disk-about.md' },
|
|
||||||
{ text: 'Создание диска', link: '/compute/compute-how-to/compute-disks/compute-disk-create.md' },
|
|
||||||
{ text: 'Управление дисками', link: '/compute/compute-how-to/compute-disks/compute-disk-manage.md' },
|
|
||||||
{ text: 'Удаление диска', link: '/compute/compute-how-to/compute-disks/compute-disk-del.md' },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{ text: 'IP-адреса', link: '/compute/compute-how-to/compute-ip/compute-ip-index.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{ text: 'Обзор', link: '/compute/compute-how-to/compute-ip/compute-ip-about.md' },
|
|
||||||
{ text: 'Просмотр IP-адресов', link: '/compute/compute-how-to/compute-ip/compute-ip-view.md' },
|
|
||||||
{ text: 'Создание IP-адреса', link: '/compute/compute-how-to/compute-ip/compute-ip-create.md' },
|
|
||||||
{ text: 'Управление IP-адресами', link: '/compute/compute-how-to/compute-ip/compute-ip-manager.md' },
|
|
||||||
{ text: 'Удаление IP-адреса', link: '/compute/compute-how-to/compute-ip/compute-ip-del.md' },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{ text: 'Группы размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-index.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{ text: 'Обзор', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-about.md'},
|
|
||||||
{ text: 'Создание группы размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-create.md'},
|
|
||||||
{ text: 'Управление группами размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-manager.md' },
|
|
||||||
{ text: 'Удаление группы размещения', link: '/compute/compute-how-to/compute-placement-groups/compute-placement-groups-del.md' },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{ text: 'Сети', link: '/compute/compute-how-to/compute-network/compute-network-index.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{ text: 'Настройка site-to-site VPN с помощью VyOS', link: '/compute/compute-how-to/compute-network/compute-vpn-vyos.md' },
|
|
||||||
{ text: 'Подключение ВМ закрытого контура к интернету', link: '/compute/compute-how-to/compute-network/compute-network-inside.md' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'/admin/': [
|
|
||||||
{
|
|
||||||
text: 'Администрирование', link: '/admin/index.md',
|
|
||||||
},
|
|
||||||
{text: 'Управление ключевыми парами', link: '/admin/ssh.md'},
|
|
||||||
],
|
|
||||||
'/vdi/': [
|
|
||||||
{
|
|
||||||
text: 'Виртуальные рабочие столы', link: '/vdi/index.md',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Обзор сервиса', link: '/vdi/vdi-overview.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{ text: 'О сервисе', link: '/vdi/vdi-about.md' },
|
|
||||||
{ text: 'Техническое описание', link: '/vdi/vdi-tech.md' },
|
|
||||||
{ text: 'Квоты и лимиты', link: '/vdi/vdi-quatos.md' },
|
|
||||||
{ text: 'Тарификация', link: '/vdi/vdi-tarif.md' },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Заказ виртуальных рабочих столов', link: '/vdi/vdi-how-to/vdi-create.md'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Настройка сервиса', link: '/vdi/vdi-how-to/vdi-nastroika.md',
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{ text: 'Настройка интеграции с Active Directory', link: '/vdi/vdi-how-to/vdi-connect-to-ad.md' },
|
|
||||||
{ text: 'Настройка сети', link: '/vdi/vdi-how-to/vdi-interconnect.md' },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// text: 'Gold-образ', link: '/vdi/vdi-how-to/vdi-gold.md'
|
|
||||||
// },
|
|
||||||
{
|
|
||||||
text: 'Подключение к виртуальному рабочему месту', link: '/vdi/vdi-how-to/vdi-connect.md'
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import type { Plugin } from 'vite'
|
||||||
|
import type { SidebarItem } from './utils/types'
|
||||||
|
import { processAllFiles } from './hook/process-files'
|
||||||
|
|
||||||
|
export const autoSectionLinksPlugin = (
|
||||||
|
srcDir: string,
|
||||||
|
sidebarConfig: Record<string, SidebarItem[]>
|
||||||
|
): Plugin => ({
|
||||||
|
name: 'auto-section-links',
|
||||||
|
buildStart: () => {
|
||||||
|
processAllFiles(srcDir, sidebarConfig)
|
||||||
|
},
|
||||||
|
configureServer: () => {
|
||||||
|
processAllFiles(srcDir, sidebarConfig)
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export const INDEX_FILE = 'index.md'
|
||||||
|
export const INDEX_FILE_PATTERN = /-index\.md$|index\.md$/
|
||||||
|
export const FRONTMATTER_REGEX = /^---\s*\n([\s\S]*?)\n---/
|
||||||
|
export const SECTION_LINK_KEYS = ['title', 'link', 'description'] as const
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { existsSync } from 'fs'
|
||||||
|
import { relative, resolve } from 'path'
|
||||||
|
import type { SidebarItem } from '../utils/types'
|
||||||
|
import { findMarkdownFiles } from '../utils/file-finder'
|
||||||
|
import { processIndexFile, processPageWithItems } from '../utils/file-processor'
|
||||||
|
import { INDEX_FILE, INDEX_FILE_PATTERN } from '../constants'
|
||||||
|
|
||||||
|
export const processAllFiles = (
|
||||||
|
srcDir: string,
|
||||||
|
sidebarConfig: Record<string, SidebarItem[]>
|
||||||
|
) => {
|
||||||
|
const srcPath = resolve(srcDir)
|
||||||
|
|
||||||
|
for (const [folderPath, items] of Object.entries(sidebarConfig)) {
|
||||||
|
const normalizedFolder = folderPath.replace(/^\/+|\/+$/g, '')
|
||||||
|
const folderFullPath = resolve(srcPath, normalizedFolder)
|
||||||
|
|
||||||
|
if (!existsSync(folderFullPath)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const mdFiles = findMarkdownFiles(folderFullPath)
|
||||||
|
|
||||||
|
for (const filePath of mdFiles) {
|
||||||
|
const fileName = filePath.split(/[/\\]/).pop() || ''
|
||||||
|
const relativePath = relative(srcPath, filePath).replace(/\\/g, '/')
|
||||||
|
const normalizedPath = relativePath.startsWith('/') ? relativePath : `/${relativePath}`
|
||||||
|
|
||||||
|
if (fileName === INDEX_FILE) {
|
||||||
|
processIndexFile(filePath, items, srcPath)
|
||||||
|
} else if (INDEX_FILE_PATTERN.test(fileName) && fileName !== INDEX_FILE) {
|
||||||
|
processPageWithItems(filePath, items, srcPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { readFileSync, existsSync } from 'fs'
|
||||||
|
import { resolve } from 'path'
|
||||||
|
import { parseFrontmatter } from './frontmatter'
|
||||||
|
|
||||||
|
export const getPageDescription = (filePath: string, srcDir: string) => {
|
||||||
|
try {
|
||||||
|
const fullPath = resolve(srcDir, filePath.replace(/^\//, ''))
|
||||||
|
if (!existsSync(fullPath)) return undefined
|
||||||
|
|
||||||
|
const fileContent = readFileSync(fullPath, 'utf-8')
|
||||||
|
const { frontmatter } = parseFrontmatter(fileContent)
|
||||||
|
|
||||||
|
return frontmatter.description as string | undefined
|
||||||
|
} catch {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { readdirSync, statSync } from 'fs'
|
||||||
|
import { join } from 'path'
|
||||||
|
|
||||||
|
export const findMarkdownFiles = (dir: string) => {
|
||||||
|
const files: string[] = []
|
||||||
|
|
||||||
|
try {
|
||||||
|
const entries = readdirSync(dir)
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const fullPath = join(dir, entry)
|
||||||
|
const stat = statSync(fullPath)
|
||||||
|
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
files.push(...findMarkdownFiles(fullPath))
|
||||||
|
} else if (entry.endsWith('.md')) {
|
||||||
|
files.push(fullPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
}
|
||||||
|
|
||||||
|
return files
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import { readFileSync, writeFileSync, existsSync } from 'fs'
|
||||||
|
import { relative, resolve } from 'path'
|
||||||
|
import type { SidebarItem } from './types'
|
||||||
|
import { parseFrontmatter, stringifyFrontmatter } from './frontmatter'
|
||||||
|
import { mergeSectionLinks, extractTopLevelLinks, extractItemsForPage } from './links'
|
||||||
|
import { normalizeLink } from './path-utils'
|
||||||
|
import { SectionLinkListItem } from '../../theme/components/SectionLinkList/SectionLinkList.types'
|
||||||
|
|
||||||
|
const removeDuplicates = (links: SectionLinkListItem[]) => {
|
||||||
|
const result: SectionLinkListItem[] = []
|
||||||
|
const seenLinks = new Set<string>()
|
||||||
|
|
||||||
|
for (const link of links) {
|
||||||
|
if (link.link) {
|
||||||
|
const normalized = normalizeLink(link.link)
|
||||||
|
if (!seenLinks.has(normalized)) {
|
||||||
|
seenLinks.add(normalized)
|
||||||
|
result.push(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasChanges = (
|
||||||
|
existingLinks: SectionLinkListItem[],
|
||||||
|
mergedLinks: SectionLinkListItem[]
|
||||||
|
) => {
|
||||||
|
if (existingLinks.length !== mergedLinks.length) return true
|
||||||
|
|
||||||
|
const existingSet = new Set(
|
||||||
|
existingLinks
|
||||||
|
.filter(link => link.link)
|
||||||
|
.map(link => `${normalizeLink(link.link!)}|${link.title}`)
|
||||||
|
)
|
||||||
|
|
||||||
|
const mergedSet = new Set(
|
||||||
|
mergedLinks
|
||||||
|
.filter(link => link.link)
|
||||||
|
.map(link => `${normalizeLink(link.link!)}|${link.title}`)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (existingSet.size !== mergedSet.size) return true
|
||||||
|
|
||||||
|
for (const item of existingSet) {
|
||||||
|
if (!mergedSet.has(item)) return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export const processIndexFile = (
|
||||||
|
filePath: string,
|
||||||
|
sidebarItems: SidebarItem[],
|
||||||
|
srcDir: string
|
||||||
|
) => {
|
||||||
|
if (!existsSync(filePath)) return false
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fileContent = readFileSync(filePath, 'utf-8')
|
||||||
|
const { frontmatter, content } = parseFrontmatter(fileContent)
|
||||||
|
|
||||||
|
const relativePath = relative(srcDir, filePath).replace(/\\/g, '/')
|
||||||
|
const normalizedPath = relativePath.startsWith('/') ? relativePath : `/${relativePath}`
|
||||||
|
|
||||||
|
const existingLinks = removeDuplicates(
|
||||||
|
Array.isArray(frontmatter.section_links) ? [...frontmatter.section_links] : []
|
||||||
|
)
|
||||||
|
const newLinks = extractTopLevelLinks(sidebarItems, normalizedPath, srcDir)
|
||||||
|
|
||||||
|
if (!newLinks.length) return false
|
||||||
|
|
||||||
|
const mergedLinks = removeDuplicates(mergeSectionLinks(existingLinks, newLinks))
|
||||||
|
|
||||||
|
if (!hasChanges(existingLinks, mergedLinks)) return false
|
||||||
|
|
||||||
|
frontmatter.section_links = mergedLinks
|
||||||
|
writeFileSync(filePath, stringifyFrontmatter(frontmatter, content), 'utf-8')
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const processPageWithItems = (
|
||||||
|
filePath: string,
|
||||||
|
sidebarItems: SidebarItem[],
|
||||||
|
srcDir: string
|
||||||
|
) => {
|
||||||
|
if (!existsSync(filePath)) return false
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fileContent = readFileSync(filePath, 'utf-8')
|
||||||
|
const { frontmatter, content } = parseFrontmatter(fileContent)
|
||||||
|
|
||||||
|
const relativePath = relative(srcDir, filePath).replace(/\\/g, '/')
|
||||||
|
const normalizedPath = relativePath.startsWith('/') ? relativePath : `/${relativePath}`
|
||||||
|
|
||||||
|
const existingLinks = removeDuplicates(
|
||||||
|
Array.isArray(frontmatter.section_links) ? [...frontmatter.section_links] : []
|
||||||
|
)
|
||||||
|
const newLinks = extractItemsForPage(sidebarItems, normalizedPath, srcDir)
|
||||||
|
|
||||||
|
const mergedLinks = newLinks.length > 0
|
||||||
|
? removeDuplicates(mergeSectionLinks(existingLinks, newLinks))
|
||||||
|
: []
|
||||||
|
|
||||||
|
if (!hasChanges(existingLinks, mergedLinks)) return false
|
||||||
|
|
||||||
|
frontmatter.section_links = mergedLinks
|
||||||
|
writeFileSync(filePath, stringifyFrontmatter(frontmatter, content), 'utf-8')
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import type { Frontmatter } from './types'
|
||||||
|
import { SECTION_LINK_KEYS, FRONTMATTER_REGEX } from '../constants'
|
||||||
|
import { SectionLinkListItem } from '../../theme/components/SectionLinkList/SectionLinkList.types'
|
||||||
|
|
||||||
|
export const parseFrontmatter = (content: string) => {
|
||||||
|
const frontmatterMatch = content.match(FRONTMATTER_REGEX)
|
||||||
|
if (!frontmatterMatch) {
|
||||||
|
const cleanContent = content.replace(/^---[\s\S]*?---\s*\n*/g, '').trim()
|
||||||
|
return { frontmatter: {}, content: cleanContent || content }
|
||||||
|
}
|
||||||
|
|
||||||
|
const [, frontmatterText] = frontmatterMatch
|
||||||
|
const frontmatterEnd = frontmatterMatch[0].length
|
||||||
|
let cleanContent = content.slice(frontmatterEnd)
|
||||||
|
cleanContent = cleanContent.replace(/^---[\s\S]*?---\s*\n*/g, '').trim()
|
||||||
|
if (!cleanContent) {
|
||||||
|
cleanContent = content.slice(frontmatterEnd).replace(/^---[\s\S]*?---\s*\n*/g, '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
const frontmatter: Frontmatter = {}
|
||||||
|
const lines = frontmatterText.split('\n')
|
||||||
|
let currentKey: string | undefined
|
||||||
|
let currentValue: SectionLinkListItem[] = []
|
||||||
|
let currentItem: Record<string, string> | undefined
|
||||||
|
let inArray = false
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmed = line.trim()
|
||||||
|
if (trimmed && !trimmed.startsWith('#')) {
|
||||||
|
const lineIndent = line.match(/^(\s*)/)?.[1]?.length ?? 0
|
||||||
|
|
||||||
|
if (lineIndent === 0 && trimmed.includes(':')) {
|
||||||
|
if (inArray && currentKey) {
|
||||||
|
frontmatter[currentKey] = currentValue
|
||||||
|
currentValue = []
|
||||||
|
inArray = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const colonMatch = trimmed.match(/^([^:]+):\s*(.*)$/)
|
||||||
|
if (colonMatch) {
|
||||||
|
const [, key, value] = colonMatch
|
||||||
|
currentKey = key.trim()
|
||||||
|
const trimmedValue = value.trim()
|
||||||
|
|
||||||
|
if (trimmedValue === '') {
|
||||||
|
inArray = true
|
||||||
|
currentValue = []
|
||||||
|
} else {
|
||||||
|
frontmatter[currentKey] = trimmedValue.replace(/^["']|["']$/g, '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (lineIndent === 2 && trimmed.startsWith('- ')) {
|
||||||
|
if (!inArray && currentKey) {
|
||||||
|
inArray = true
|
||||||
|
currentValue = []
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentItem && currentItem.title && currentItem.link !== undefined) {
|
||||||
|
const link: SectionLinkListItem = {
|
||||||
|
title: currentItem.title,
|
||||||
|
link: currentItem.link,
|
||||||
|
description: currentItem.description
|
||||||
|
}
|
||||||
|
if (!currentValue.some(item => item.link === link.link)) {
|
||||||
|
currentValue.push(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemText = trimmed.slice(2).trim()
|
||||||
|
if (itemText.includes(':')) {
|
||||||
|
currentItem = {}
|
||||||
|
const parts = itemText.split(',').map(p => p.trim())
|
||||||
|
for (const part of parts) {
|
||||||
|
const colonMatch = part.match(/^(\w+):\s*(.+)$/)
|
||||||
|
if (colonMatch) {
|
||||||
|
const [, key, value] = colonMatch
|
||||||
|
if (SECTION_LINK_KEYS.includes(key as typeof SECTION_LINK_KEYS[number])) {
|
||||||
|
currentItem[key] = value.replace(/^["']|["']$/g, '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentValue.push({ title: itemText, link: '' })
|
||||||
|
}
|
||||||
|
} else if (lineIndent >= 4 && currentItem && trimmed.includes(':')) {
|
||||||
|
const colonIndex = trimmed.indexOf(':')
|
||||||
|
if (colonIndex !== -1) {
|
||||||
|
const key = trimmed.substring(0, colonIndex).trim()
|
||||||
|
const value = trimmed.substring(colonIndex + 1).trim()
|
||||||
|
if (SECTION_LINK_KEYS.includes(key as typeof SECTION_LINK_KEYS[number])) {
|
||||||
|
currentItem[key] = value === '' ? '' : value.replace(/^["']|["']$/g, '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentItem && inArray && currentItem.title && currentItem.link !== undefined) {
|
||||||
|
const link: SectionLinkListItem = {
|
||||||
|
title: currentItem.title,
|
||||||
|
link: currentItem.link,
|
||||||
|
description: currentItem.description
|
||||||
|
}
|
||||||
|
if (!currentValue.some(item => item.link === link.link)) {
|
||||||
|
currentValue.push(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inArray && currentKey) {
|
||||||
|
frontmatter[currentKey] = currentValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return { frontmatter, content: cleanContent || content }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const stringifyFrontmatter = (frontmatter: Frontmatter, content: string) => {
|
||||||
|
const lines: string[] = []
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(frontmatter)) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
lines.push(`${key}:`)
|
||||||
|
for (const item of value) {
|
||||||
|
if (typeof item === 'object' && item) {
|
||||||
|
const link = item as SectionLinkListItem
|
||||||
|
lines.push(` - title: ${link.title ?? ''}`)
|
||||||
|
if (link.link) lines.push(` link: ${link.link}`)
|
||||||
|
if ('description' in link && link.description !== undefined && link.description !== null && String(link.description).trim() !== '') {
|
||||||
|
lines.push(` description: ${String(link.description)}`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lines.push(` - ${item}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (value) {
|
||||||
|
const strValue = String(value)
|
||||||
|
const needsQuotes = strValue.includes(':') || (strValue.includes(' ') && !strValue.startsWith('"')) || strValue === ''
|
||||||
|
const escaped = strValue.replace(/"/g, '\\"')
|
||||||
|
lines.push(`${key}: ${needsQuotes ? `"${escaped}"` : strValue}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `---\n${lines.join('\n')}\n---\n\n${content}`
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
import type { SidebarItem } from './types'
|
||||||
|
import { getPageDescription } from './descriptions'
|
||||||
|
import { normalizeLink } from './path-utils'
|
||||||
|
import { SectionLinkListItem } from '../../theme/components/SectionLinkList/SectionLinkList.types'
|
||||||
|
|
||||||
|
export const mergeSectionLinks = (
|
||||||
|
existingLinks: SectionLinkListItem[],
|
||||||
|
newLinks: SectionLinkListItem[]
|
||||||
|
) => {
|
||||||
|
const existingLinksMap = new Map<string, SectionLinkListItem>()
|
||||||
|
const result: SectionLinkListItem[] = []
|
||||||
|
const processedLinks = new Set<string>()
|
||||||
|
|
||||||
|
for (const existingLink of existingLinks) {
|
||||||
|
if (existingLink.link) {
|
||||||
|
const normalizedLink = normalizeLink(existingLink.link)
|
||||||
|
if (!existingLinksMap.has(normalizedLink)) {
|
||||||
|
existingLinksMap.set(normalizedLink, existingLink)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const newLink of newLinks) {
|
||||||
|
if (newLink.link) {
|
||||||
|
const normalizedLink = normalizeLink(newLink.link)
|
||||||
|
|
||||||
|
if (!processedLinks.has(normalizedLink)) {
|
||||||
|
processedLinks.add(normalizedLink)
|
||||||
|
|
||||||
|
if (existingLinksMap.has(normalizedLink)) {
|
||||||
|
const existingLink = existingLinksMap.get(normalizedLink)!
|
||||||
|
result.push({ ...existingLink, title: newLink.title })
|
||||||
|
} else {
|
||||||
|
result.push(newLink)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export const extractTopLevelLinks = (
|
||||||
|
items: SidebarItem[],
|
||||||
|
currentIndexPath: string,
|
||||||
|
srcDir: string
|
||||||
|
) => {
|
||||||
|
const links: SectionLinkListItem[] = []
|
||||||
|
const normalizedCurrentPath = normalizeLink(currentIndexPath)
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
if ('excludeFromIndex' in item && item.excludeFromIndex) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.link) {
|
||||||
|
const normalizedItemPath = normalizeLink(item.link)
|
||||||
|
if (normalizedItemPath === normalizedCurrentPath) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
links.push({
|
||||||
|
title: item.text || '',
|
||||||
|
link: item.link,
|
||||||
|
description: getPageDescription(item.link, srcDir)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return links
|
||||||
|
}
|
||||||
|
|
||||||
|
const findItemsRecursive = (
|
||||||
|
itemsList: SidebarItem[],
|
||||||
|
pagePath: string,
|
||||||
|
srcDir: string
|
||||||
|
): SectionLinkListItem[] => {
|
||||||
|
const normalizedPagePath = normalizeLink(pagePath)
|
||||||
|
const links: SectionLinkListItem[] = []
|
||||||
|
|
||||||
|
for (const item of itemsList) {
|
||||||
|
if (item.link) {
|
||||||
|
const normalizedItemPath = normalizeLink(item.link)
|
||||||
|
|
||||||
|
if (normalizedItemPath === normalizedPagePath && item.items && Array.isArray(item.items) && item.items.length > 0) {
|
||||||
|
for (const subItem of item.items) {
|
||||||
|
if ('excludeFromIndex' in subItem && subItem.excludeFromIndex) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subItem.link) {
|
||||||
|
links.push({
|
||||||
|
title: subItem.text || '',
|
||||||
|
link: subItem.link,
|
||||||
|
description: getPageDescription(subItem.link, srcDir)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return links
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.items && Array.isArray(item.items)) {
|
||||||
|
const found = findItemsRecursive(item.items, pagePath, srcDir)
|
||||||
|
if (found.length > 0) {
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return links
|
||||||
|
}
|
||||||
|
|
||||||
|
export const extractItemsForPage = (
|
||||||
|
items: SidebarItem[],
|
||||||
|
pagePath: string,
|
||||||
|
srcDir: string
|
||||||
|
) => findItemsRecursive(items, pagePath, srcDir)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export const normalizeLink = (link: string): string =>
|
||||||
|
!link ? '' : link
|
||||||
|
.replace(/^\/+/, '')
|
||||||
|
.replace(/\.md$/, '')
|
||||||
|
.replace(/\\/g, '/')
|
||||||
|
.trim()
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { SectionLinkListItem } from "../../theme/components/SectionLinkList/SectionLinkList.types"
|
||||||
|
|
||||||
|
export type SidebarItem = {
|
||||||
|
text: string
|
||||||
|
link?: string
|
||||||
|
items?: SidebarItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Frontmatter = {
|
||||||
|
section_links?: SectionLinkListItem[]
|
||||||
|
[key: string]: string | number | boolean | string[] | SectionLinkListItem[] | undefined
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ export type SectionLinkListItem = {
|
|||||||
title: string,
|
title: string,
|
||||||
link: string,
|
link: string,
|
||||||
description?: string
|
description?: string
|
||||||
|
collapsed?: boolean
|
||||||
|
excludeFromIndex?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SectionLinkListProps = {
|
export type SectionLinkListProps = {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ section_links:
|
|||||||
link: /compute/compute-how-to/compute-connect-inside.md
|
link: /compute/compute-how-to/compute-connect-inside.md
|
||||||
description: Подключиться к виртуальной машине по SSH с помощью ключевой пары по внутреннему IP-адресу через джамп-хост
|
description: Подключиться к виртуальной машине по SSH с помощью ключевой пары по внутреннему IP-адресу через джамп-хост
|
||||||
- title: Подключение по SSH по логину и паролю
|
- title: Подключение по SSH по логину и паролю
|
||||||
link: /compute/compute-how-to/compute-connect-inside.md
|
link: /compute/compute-how-to/compute-connect-pwd.md
|
||||||
description: Подключиться к виртуальной машине по SSH с помощью логина и пароля
|
description: Подключиться к виртуальной машине по SSH с помощью логина и пароля
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user