Compare commits

...
3 Commits
Author SHA1 Message Date
DenozordecandCursor 357ed4ce7b refactor(web): enhance layout and styling for dashboard components
CI / changes (push) Successful in 12s
CI / commitlint (push) Skipped
CI / go (push) Skipped
CI / bird2 (push) Skipped
CI / openapi (push) Successful in 1m18s
CI / web (push) Successful in 2m0s
CI / release (push) Successful in 6m51s
Updated various dashboard components to improve layout and responsiveness. Adjusted class names to include 'min-w-0' for better handling of overflow and added flex properties to ensure proper alignment. Refined grid structures and skeleton loading states for a more cohesive user experience.

Co-authored-by: Cursor <[email protected]>
2026-08-18 00:51:10 +07:00
DenozordecandCursor 35e262b5ae fix(web): update Russian translations and improve UI components
CI / changes (push) Successful in 9s
CI / commitlint (push) Skipped
CI / go (push) Skipped
CI / bird2 (push) Skipped
CI / openapi (push) Successful in 47s
CI / web (push) Successful in 1m4s
CI / release (push) Successful in 5m19s
Обновлены переводы на русский язык для различных компонентов, включая KPI, карточки сети и панели управления. Исправлены описания и метки для улучшения пользовательского интерфейса. Также добавлены новые элементы для поддержки локализации в компонентах, таких как DataGrid и ResourcePage.

Co-authored-by: Cursor <[email protected]>
2026-08-18 00:32:00 +07:00
DenozordecandCursor 8267141136 ci(gitea): point registry and remotes at git.shx.one
CI / changes (push) Successful in 5s
CI / commitlint (push) Skipped
CI / openapi (push) Successful in 38s
CI / web (push) Successful in 55s
CI / go (push) Successful in 1m4s
CI / bird2 (push) Successful in 17s
CI / release (push) Successful in 4m17s
Перевести CI, semantic-release, bake и compose с git.shts.su на git.shx.one.

Co-authored-by: Cursor <[email protected]>
2026-08-17 16:43:39 +07:00
45 changed files with 264 additions and 172 deletions
+6 -6
View File
@@ -37,11 +37,11 @@ Runner: `ubuntu-latest`, **bird2** из apt, Docker для job **release**.
### Теги образов ### Теги образов
```text ```text
git.shts.su/<owner>/<имя>:latest git.shx.one/<owner>/<имя>:latest
git.shts.su/<owner>/<имя>:v1.2.3 git.shx.one/<owner>/<имя>:v1.2.3
git.shts.su/<owner>/<имя>:1.2.3 git.shx.one/<owner>/<имя>:1.2.3
git.shts.su/<owner>/<имя>:<short-sha> git.shx.one/<owner>/<имя>:<short-sha>
git.shts.su/<owner>/<имя>:sha-<full-sha> git.shx.one/<owner>/<имя>:sha-<full-sha>
``` ```
Имена образов: `evobgp-api`, `evobgp-all`, `evobgp-scheduler`, `evobgp-ingest`, `evobgp-render`, `evobgp-deploy`, `evobgp-node`, `evobgp-web`, `evobgp-web-all`, `evobgp-agent`, `evobgp-bird2`. Имена образов: `evobgp-api`, `evobgp-all`, `evobgp-scheduler`, `evobgp-ingest`, `evobgp-render`, `evobgp-deploy`, `evobgp-node`, `evobgp-web`, `evobgp-web-all`, `evobgp-agent`, `evobgp-bird2`.
@@ -51,7 +51,7 @@ git.shts.su/<owner>/<имя>:sha-<full-sha>
Пример: Пример:
```bash ```bash
docker pull git.shts.su/myuser/evobgp-api:1.2.3 docker pull git.shx.one/myuser/evobgp-api:1.2.3
``` ```
См. [deploy/docker/README.md](../deploy/docker/README.md), [docs/quickstart.md](../docs/quickstart.md). См. [deploy/docker/README.md](../deploy/docker/README.md), [docs/quickstart.md](../docs/quickstart.md).
+5 -5
View File
@@ -318,7 +318,7 @@ jobs:
- name: Semantic release - name: Semantic release
run: npx semantic-release run: npx semantic-release
env: env:
GITEA_URL: https://git.shts.su GITEA_URL: https://git.shx.one
GITEA_TOKEN: ${{ secrets.ACTIONS_PAT || gitea.token }} GITEA_TOKEN: ${{ secrets.ACTIONS_PAT || gitea.token }}
- name: Detect new release - name: Detect new release
id: rel id: rel
@@ -363,20 +363,20 @@ jobs:
if: steps.rel.outputs.released == 'true' if: steps.rel.outputs.released == 'true'
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
registry: git.shts.su registry: git.shx.one
username: ${{ gitea.actor }} username: ${{ gitea.actor }}
password: ${{ secrets.ACTIONS_PAT || gitea.token }} password: ${{ secrets.ACTIONS_PAT || gitea.token }}
- name: Build and push images (bake) - name: Build and push images (bake)
if: steps.rel.outputs.released == 'true' if: steps.rel.outputs.released == 'true'
env: env:
REGISTRY: git.shts.su/${{ steps.meta.outputs.owner_lc }} REGISTRY: git.shx.one/${{ steps.meta.outputs.owner_lc }}
IMAGE_TAG: latest IMAGE_TAG: latest
VERSION: ${{ steps.meta.outputs.version }} VERSION: ${{ steps.meta.outputs.version }}
SHORT_SHA: ${{ steps.meta.outputs.short_sha }} SHORT_SHA: ${{ steps.meta.outputs.short_sha }}
SHA_FULL: ${{ github.sha }} SHA_FULL: ${{ github.sha }}
BUILD_TIME: ${{ steps.meta.outputs.build_time }} BUILD_TIME: ${{ steps.meta.outputs.build_time }}
CACHE_REF_GO: git.shts.su/${{ steps.meta.outputs.owner_lc }}/evobgp-buildcache:go-buildcache CACHE_REF_GO: git.shx.one/${{ steps.meta.outputs.owner_lc }}/evobgp-buildcache:go-buildcache
CACHE_REF_WEB: git.shts.su/${{ steps.meta.outputs.owner_lc }}/evobgp-buildcache:web-buildcache CACHE_REF_WEB: git.shx.one/${{ steps.meta.outputs.owner_lc }}/evobgp-buildcache:web-buildcache
BUILDX_BAKE_ENTITLEMENTS_FS: "0" BUILDX_BAKE_ENTITLEMENTS_FS: "0"
run: | run: |
set -euxo pipefail set -euxo pipefail
+1 -1
View File
@@ -37,7 +37,7 @@
[ [
"@markwylde/semantic-release-gitea", "@markwylde/semantic-release-gitea",
{ {
"giteaUrl": "https://git.shts.su", "giteaUrl": "https://git.shx.one",
"assets": [ "assets": [
{ {
"path": "CHANGELOG.md", "path": "CHANGELOG.md",
+2 -2
View File
@@ -22,8 +22,8 @@ Control plane для управления префиксами, модулями
```powershell ```powershell
cd deploy\compose cd deploy\compose
Copy-Item .env.example .env -Force # EVOBGP_REGISTRY=git.shts.su/<owner> Copy-Item .env.example .env -Force # EVOBGP_REGISTRY=git.shx.one/<owner>
docker login git.shts.su docker login git.shx.one
docker compose --profile microvps pull docker compose --profile microvps pull
docker compose --profile microvps up -d docker compose --profile microvps up -d
``` ```
@@ -51,7 +51,7 @@ export function NetworkOverviewAnalyticsCard({
label: 'Offline', label: 'Offline',
percent: 100 - speakersPct, percent: 100 - speakersPct,
}} }}
footer={`Пиры Established: ${net.peersEstablished}/${net.peersEnabled}`} footer={`Пиры установлены: ${net.peersEstablished}/${net.peersEnabled}`}
/> />
</div> </div>
) )
@@ -50,7 +50,7 @@ export function DashboardActivityTimeline({
<DashboardFramePanel <DashboardFramePanel
title="Недавняя активность" title="Недавняя активность"
description="Задачи, ревизии и сетевые события" description="Задачи, ревизии и сетевые события"
className="h-full" className="h-full min-w-0"
> >
{loading ? ( {loading ? (
<p className="text-muted-foreground px-4 py-6 text-sm">Загрузка</p> <p className="text-muted-foreground px-4 py-6 text-sm">Загрузка</p>
@@ -82,7 +82,7 @@ export function DashboardActivityTimeline({
</TimelineIndicator> </TimelineIndicator>
</TimelineHeader> </TimelineHeader>
<TimelineContent className="min-w-0 pb-1 text-foreground"> <TimelineContent className="min-w-0 pb-1 text-foreground">
<TimelineTitle className="text-sm leading-snug font-normal"> <TimelineTitle className="text-sm leading-snug font-normal break-words">
{item.message} {item.message}
</TimelineTitle> </TimelineTitle>
<div className="mt-2"> <div className="mt-2">
@@ -82,7 +82,7 @@ function buildKpis({
icon: <Share2 aria-hidden />, icon: <Share2 aria-hidden />,
iconClassName: 'text-success', iconClassName: 'text-success',
value: loading ? '—' : `${network.peersEstablished}/${peersEnabled}`, value: loading ? '—' : `${network.peersEstablished}/${peersEnabled}`,
label: 'Пиры Established', label: 'Пиры установлены',
footer: ( footer: (
<Badge variant="success-light" size="sm"> <Badge variant="success-light" size="sm">
{loading ? '…' : `${network.peersTotal} в каталоге`} {loading ? '…' : `${network.peersTotal} в каталоге`}
@@ -94,13 +94,13 @@ function buildKpis({
icon: <ServerCog aria-hidden />, icon: <ServerCog aria-hidden />,
iconClassName: 'text-warning', iconClassName: 'text-warning',
value: loading ? '—' : `${network.speakersOnline}/${network.speakersTotal}`, value: loading ? '—' : `${network.speakersOnline}/${network.speakersTotal}`,
label: 'Спикеры online', label: 'Спикеры в сети',
footer: ( footer: (
<Badge <Badge
variant={network.speakersOnline === network.speakersTotal ? 'success-light' : 'warning-light'} variant={network.speakersOnline === network.speakersTotal ? 'success-light' : 'warning-light'}
size="sm" size="sm"
> >
{loading ? '…' : 'live-снимок'} {loading ? '…' : 'онлайн'}
</Badge> </Badge>
), ),
}, },
@@ -11,8 +11,8 @@ import {
} from 'lucide-react' } from 'lucide-react'
import { useCallback, useMemo, useState } from 'react' import { useCallback, useMemo, useState } from 'react'
import { CategoryBadge, ModeBadge } from '@/components/category-badge' import { CategoryBadge } from '@/components/category-badge'
import { DataGridMutedCell, DataGridPrimaryCell } from '@/components/data-grid-cell' import { DataGridPrimaryCell } from '@/components/data-grid-cell'
import { DashboardFramePanel } from '@/components/dashboard/dashboard-frame-panel' import { DashboardFramePanel } from '@/components/dashboard/dashboard-frame-panel'
import { Badge } from '@/components/reui/badge' import { Badge } from '@/components/reui/badge'
import { DataGrid } from '@/components/reui/data-grid/data-grid' import { DataGrid } from '@/components/reui/data-grid/data-grid'
@@ -40,6 +40,7 @@ import {
InputGroupButton, InputGroupButton,
InputGroupInput, InputGroupInput,
} from '@evobgp/ui/components/input-group' } from '@evobgp/ui/components/input-group'
import { DATA_GRID_PAGINATION_RU } from '@/lib/data-grid-defaults'
import { moduleTypeRu } from '@/lib/ui-labels' import { moduleTypeRu } from '@/lib/ui-labels'
import type { ModuleRow } from '@/types/api' import type { ModuleRow } from '@/types/api'
import { import {
@@ -51,20 +52,19 @@ import {
type SortingState, type SortingState,
} from '@tanstack/react-table' } from '@tanstack/react-table'
type ModuleSort = 'name' | 'type' | 'priority' | 'last_refreshed_at' type ModuleSort = 'name' | 'type' | 'priority'
type EnabledFilter = 'all' | 'enabled' | 'disabled' type EnabledFilter = 'all' | 'enabled' | 'disabled'
const sortLabels: Record<ModuleSort, string> = { const sortLabels: Record<ModuleSort, string> = {
name: 'Название', name: 'Название',
type: 'Тип', type: 'Тип',
priority: 'Приоритет', priority: 'Приоритет',
last_refreshed_at: 'Обновлено',
} }
const EMPTY_MESSAGE = 'Нет модулей по выбранным фильтрам.' const EMPTY_MESSAGE = 'Нет модулей по выбранным фильтрам.'
function buildSorting(sortBy: ModuleSort): SortingState { function buildSorting(sortBy: ModuleSort): SortingState {
return [{ id: sortBy, desc: sortBy === 'last_refreshed_at' }] return [{ id: sortBy, desc: false }]
} }
export function DashboardModulesGrid({ export function DashboardModulesGrid({
@@ -119,9 +119,10 @@ export function DashboardModulesGrid({
cell: ({ row }) => ( cell: ({ row }) => (
<div className="flex min-w-0 items-center gap-2"> <div className="flex min-w-0 items-center gap-2">
<BoxesIcon className="text-muted-foreground size-4 shrink-0" aria-hidden /> <BoxesIcon className="text-muted-foreground size-4 shrink-0" aria-hidden />
<DataGridPrimaryCell title={row.original.name} accent="primary" className="max-w-[240px]" /> <DataGridPrimaryCell title={row.original.name} accent="primary" />
</div> </div>
), ),
minSize: 180,
meta: { headerTitle: 'Модуль' }, meta: { headerTitle: 'Модуль' },
}, },
{ {
@@ -138,30 +139,9 @@ export function DashboardModulesGrid({
cell: ({ row }) => ( cell: ({ row }) => (
<span className="font-mono text-sm tabular-nums">{row.original.priority}</span> <span className="font-mono text-sm tabular-nums">{row.original.priority}</span>
), ),
size: 88,
meta: { headerTitle: 'Приоритет' }, meta: { headerTitle: 'Приоритет' },
}, },
{
id: 'enabled',
accessorFn: (row) => (row.enabled !== false ? 'enabled' : 'disabled'),
header: ({ column }) => <DataGridColumnHeader column={column} title="Состояние" />,
cell: ({ row }) => <ModeBadge enabled={row.original.enabled !== false} />,
meta: { headerTitle: 'Состояние' },
},
{
id: 'last_refreshed_at',
accessorFn: (row) => row.last_refreshed_at ?? '',
header: ({ column }) => <DataGridColumnHeader column={column} title="Обновлено" />,
cell: ({ row }) => (
<DataGridMutedCell>
{row.original.last_refreshed_at
? new Date(row.original.last_refreshed_at).toLocaleString('ru-RU')
: '—'}
</DataGridMutedCell>
),
sortingFn: (a, b) =>
(a.original.last_refreshed_at ?? '').localeCompare(b.original.last_refreshed_at ?? ''),
meta: { headerTitle: 'Обновлено' },
},
], ],
[], [],
) )
@@ -185,6 +165,7 @@ export function DashboardModulesGrid({
<DashboardFramePanel <DashboardFramePanel
title="Модули" title="Модули"
description="Поиск, сортировка и быстрый переход к настройке" description="Поиск, сортировка и быстрый переход к настройке"
className="min-w-0"
actions={ actions={
<Button variant="outline" size="sm" render={<Link to="/modules/new" />}> <Button variant="outline" size="sm" render={<Link to="/modules/new" />}>
<PlusIcon /> <PlusIcon />
@@ -204,14 +185,14 @@ export function DashboardModulesGrid({
columnsVisibility: false, columnsVisibility: false,
columnsResizable: false, columnsResizable: false,
columnsMovable: false, columnsMovable: false,
width: 'fixed', width: 'auto',
}} }}
tableClassNames={{ bodyRow: 'group/module-row cursor-pointer [&>td]:h-14' }} tableClassNames={{ bodyRow: 'group/module-row cursor-pointer [&>td]:h-14' }}
onRowClick={(row) => void navigate({ to: '/modules/$moduleId', params: { moduleId: row.id } })} onRowClick={(row) => void navigate({ to: '/modules/$moduleId', params: { moduleId: row.id } })}
> >
<div className="flex flex-col"> <div className="flex flex-col">
<div className="flex flex-col gap-3 border-b px-4 py-3 lg:flex-row lg:items-center lg:justify-between"> <div className="flex flex-col gap-3 border-b px-4 py-3 @3xl:flex-row @3xl:items-center @3xl:justify-between">
<InputGroup className="w-full min-w-40 sm:max-w-xs"> <InputGroup className="w-full min-w-0 @3xl:max-w-xs">
<InputGroupAddon align="inline-start"> <InputGroupAddon align="inline-start">
<SearchIcon className="text-muted-foreground size-4" aria-hidden /> <SearchIcon className="text-muted-foreground size-4" aria-hidden />
</InputGroupAddon> </InputGroupAddon>
@@ -337,8 +318,8 @@ export function DashboardModulesGrid({
<div className="border-t px-4 py-3"> <div className="border-t px-4 py-3">
{filteredModules.length > 0 ? ( {filteredModules.length > 0 ? (
<DataGridPagination <DataGridPagination
{...DATA_GRID_PAGINATION_RU}
sizes={[10, 15, 20]} sizes={[10, 15, 20]}
info="{from}{to} из {count}"
className="py-0" className="py-0"
/> />
) : ( ) : (
@@ -57,7 +57,7 @@ export function DashboardNetworkHealth({
} }
primary={{ primary={{
value: loading ? '—' : `${utilization}%`, value: loading ? '—' : `${utilization}%`,
label: mode === 'peers' ? 'Утилизация пиров' : 'Спикеры online', label: mode === 'peers' ? 'Утилизация пиров' : 'Спикеры в сети',
percent: loading ? 0 : utilization, percent: loading ? 0 : utilization,
badge: ( badge: (
<Badge variant="outline" radius="full" className="h-6 px-2 text-[10px]"> <Badge variant="outline" radius="full" className="h-6 px-2 text-[10px]">
@@ -67,7 +67,7 @@ export function DashboardNetworkHealth({
}} }}
secondary={{ secondary={{
value: loading ? '—' : offline, value: loading ? '—' : offline,
label: mode === 'peers' ? 'Не Established' : 'Offline', label: mode === 'peers' ? 'Не установлены' : 'Не в сети',
percent: total > 0 ? Math.round((offline / total) * 100) : 0, percent: total > 0 ? Math.round((offline / total) * 100) : 0,
}} }}
footer={ footer={
@@ -7,7 +7,7 @@ const ACTIONS: QuickActionItem[] = [
{ {
id: 'lookup', id: 'lookup',
title: 'Проверка IP/домена', title: 'Проверка IP/домена',
description: 'Membership в списках и community (entry + snapshot).', description: 'Проверка IP в списках и BGP-сообществах.',
to: '/lookup', to: '/lookup',
icon: <Search aria-hidden />, icon: <Search aria-hidden />,
iconClassName: 'text-primary', iconClassName: 'text-primary',
@@ -23,7 +23,7 @@ const ACTIONS: QuickActionItem[] = [
{ {
id: 'communities', id: 'communities',
title: 'BGP-сообщества', title: 'BGP-сообщества',
description: 'Справочник communities для политик экспорта.', description: 'Справочник BGP-сообществ для политик экспорта.',
to: '/directories', to: '/directories',
icon: <Tags aria-hidden />, icon: <Tags aria-hidden />,
iconClassName: 'text-info', iconClassName: 'text-info',
@@ -31,7 +31,7 @@ const ACTIONS: QuickActionItem[] = [
{ {
id: 'network', id: 'network',
title: 'Сеть', title: 'Сеть',
description: 'Обзор пиров, спикеров и live-сессий BGP.', description: 'Обзор пиров, спикеров и живых BGP-сессий.',
to: '/network', to: '/network',
search: { tab: 'overview' }, search: { tab: 'overview' },
icon: <Network aria-hidden />, icon: <Network aria-hidden />,
@@ -30,7 +30,7 @@ export function NetworkKpi({
id: 'peers-established', id: 'peers-established',
icon: <Share2 aria-hidden />, icon: <Share2 aria-hidden />,
iconClassName: 'text-success', iconClassName: 'text-success',
label: 'Пиры Established', label: 'Пиры установлены',
value: loading ? '—' : `${net.peersEstablished}/${net.peersEnabled}`, value: loading ? '—' : `${net.peersEstablished}/${net.peersEnabled}`,
footer: ( footer: (
<Badge variant="success-light" size="sm"> <Badge variant="success-light" size="sm">
@@ -44,7 +44,7 @@ export function NetworkKpi({
id: 'speakers-online', id: 'speakers-online',
icon: <ServerCog aria-hidden />, icon: <ServerCog aria-hidden />,
iconClassName: 'text-info', iconClassName: 'text-info',
label: 'Спикеры online', label: 'Спикеры в сети',
value: loading ? '—' : `${net.speakersOnline}/${net.speakersTotal}`, value: loading ? '—' : `${net.speakersOnline}/${net.speakersTotal}`,
footer: ( footer: (
<Badge <Badge
@@ -55,7 +55,7 @@ export function NetworkKpi({
} }
size="sm" size="sm"
> >
{loading ? '…' : 'live'} {loading ? '…' : 'онлайн'}
</Badge> </Badge>
), ),
to: '/network', to: '/network',
@@ -32,13 +32,13 @@ function peerDeleteLabel(p: PeerRow): string {
const PEER_TABS = [ const PEER_TABS = [
{ id: 'all', label: 'Все' }, { id: 'all', label: 'Все' },
{ id: 'established', label: 'Established' }, { id: 'established', label: 'Установлены' },
{ id: 'pending', label: 'Ожидание' }, { id: 'pending', label: 'Ожидание' },
{ id: 'disabled', label: 'Выключены' }, { id: 'disabled', label: 'Выключены' },
] ]
const SESSION_STATE_OPTIONS = [ const SESSION_STATE_OPTIONS = [
{ value: 'Established', label: 'Established' }, { value: 'Established', label: 'Установлена' },
{ value: 'Idle', label: 'Idle' }, { value: 'Idle', label: 'Idle' },
{ value: 'Active', label: 'Active' }, { value: 'Active', label: 'Active' },
{ value: 'Connect', label: 'Connect' }, { value: 'Connect', label: 'Connect' },
@@ -72,7 +72,7 @@ export function PeerFormDialog({
} }
const asn = Number(remoteAsn) const asn = Number(remoteAsn)
if (!asn || asn <= 0) { if (!asn || asn <= 0) {
toast.error('Remote ASN должен быть больше 0') toast.error('ASN соседа должен быть больше 0')
return return
} }
const body: BgpPeerCreate = { const body: BgpPeerCreate = {
@@ -132,7 +132,7 @@ export function PeerFormDialog({
/> />
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<Label htmlFor="peer-asn">Remote ASN</Label> <Label htmlFor="peer-asn">ASN соседа</Label>
<Input <Input
id="peer-asn" id="peer-asn"
type="number" type="number"
+1 -1
View File
@@ -54,7 +54,7 @@ export function PanelCard({
<Frame <Frame
dense dense
spacing={spacing} spacing={spacing}
className={cn(panelCardClassName, className)} className={cn(panelCardClassName, 'min-w-0', className)}
> >
<FramePanel className="flex flex-col gap-0 p-0 shadow-xs"> <FramePanel className="flex flex-col gap-0 p-0 shadow-xs">
{hasHeader ? ( {hasHeader ? (
+7 -4
View File
@@ -1,10 +1,13 @@
/** Shared grid column classes for hybrid KPI / Quick Actions tiles. */ /**
* Shared grid column classes for hybrid KPI / Quick Actions tiles.
* Container queries only — never 56 columns on laptop width (tiles overlap).
* @see https://reui.io/preview/base/stats-12
*/
export function kpiCols(count: number): string { export function kpiCols(count: number): string {
if (count <= 1) return 'grid-cols-1' if (count <= 1) return 'grid-cols-1'
if (count === 2) return 'grid-cols-1 @xl:grid-cols-2' if (count === 2) return 'grid-cols-1 @xl:grid-cols-2'
if (count === 3) return 'grid-cols-1 @3xl:grid-cols-3' if (count === 3) return 'grid-cols-1 @3xl:grid-cols-3'
if (count === 4) return 'grid-cols-1 @3xl:grid-cols-2 @6xl:grid-cols-4' if (count === 4) return 'grid-cols-1 @3xl:grid-cols-2 @6xl:grid-cols-4'
if (count === 5) return 'grid-cols-2 @3xl:grid-cols-3 xl:grid-cols-5' if (count <= 6) return 'grid-cols-1 @xl:grid-cols-2 @4xl:grid-cols-3'
if (count === 6) return 'grid-cols-2 sm:grid-cols-3 xl:grid-cols-6' return 'grid-cols-1 @xl:grid-cols-2 @4xl:grid-cols-3 @6xl:grid-cols-4'
return 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-4'
} }
@@ -27,7 +27,7 @@ interface OpsDashboardProps {
} }
const rootClassName = const rootClassName =
'text-foreground @container flex w-full flex-col gap-2 md:gap-3' 'text-foreground @container flex w-full min-w-0 flex-col gap-4 md:gap-6'
function OpsDashboardSkeleton() { function OpsDashboardSkeleton() {
return ( return (
@@ -37,7 +37,7 @@ function OpsDashboardSkeleton() {
<Skeleton className="mt-2 h-4 w-80 max-w-full" /> <Skeleton className="mt-2 h-4 w-80 max-w-full" />
</header> </header>
<KpiStatGrid cards={[]} isLoading skeletonCount={4} /> <KpiStatGrid cards={[]} isLoading skeletonCount={4} />
<div className="grid gap-2 @3xl:grid-cols-2"> <div className="flex min-w-0 flex-col gap-4">
<Skeleton className="h-64 w-full rounded-xl" /> <Skeleton className="h-64 w-full rounded-xl" />
<Skeleton className="h-64 w-full rounded-xl" /> <Skeleton className="h-64 w-full rounded-xl" />
</div> </div>
@@ -79,13 +79,13 @@ export function OpsDashboard({
<section <section
aria-label="Аналитика" aria-label="Аналитика"
className="grid min-w-0 items-start gap-2 @3xl:grid-cols-2" className="flex min-w-0 flex-col gap-4"
> >
{charts} {charts}
</section> </section>
<section aria-label={queueTitle}> <section aria-label={queueTitle}>
<Frame dense spacing="sm" className="w-full"> <Frame dense spacing="sm" className="w-full min-w-0">
<FrameHeader> <FrameHeader>
<FrameTitle>{queueTitle}</FrameTitle> <FrameTitle>{queueTitle}</FrameTitle>
<FrameDescription>{queueDescription}</FrameDescription> <FrameDescription>{queueDescription}</FrameDescription>
@@ -39,6 +39,8 @@ import {
AlertTitle, AlertTitle,
} from '@/components/reui/alert' } from '@/components/reui/alert'
import { EmptyState } from '@/components/empty-state' import { EmptyState } from '@/components/empty-state'
import { DATA_GRID_PAGINATION_RU } from '@/lib/data-grid-defaults'
import { FILTERS_I18N_RU } from '@/lib/filters-i18n'
import { applyFiltersToData } from './filter-utils' import { applyFiltersToData } from './filter-utils'
export interface ResourcePageTab { export interface ResourcePageTab {
@@ -322,6 +324,7 @@ export function ResourcePage<T extends object>({
fields={filterFields} fields={filterFields}
onChange={handleFiltersChange} onChange={handleFiltersChange}
size="default" size="default"
i18n={FILTERS_I18N_RU}
trigger={ trigger={
<Button type="button" variant="outline" aria-label="Фильтры"> <Button type="button" variant="outline" aria-label="Фильтры">
<FilterIcon className="size-4" aria-hidden="true" /> <FilterIcon className="size-4" aria-hidden="true" />
@@ -355,11 +358,8 @@ export function ResourcePage<T extends object>({
<FrameFooter> <FrameFooter>
<DataGridPagination <DataGridPagination
{...DATA_GRID_PAGINATION_RU}
sizes={[5, 10, 20, 50]} sizes={[5, 10, 20, 50]}
rowsPerPageLabel="Строк на странице"
info="{from} - {to} of {count}"
previousPageLabel="Предыдущая"
nextPageLabel="Следующая"
/> />
</FrameFooter> </FrameFooter>
</FramePanel> </FramePanel>
@@ -43,16 +43,21 @@ interface SettingsShellProps {
*/ */
export function SettingsShell({ export function SettingsShell({
title = 'Настройки', title = 'Настройки',
description = 'Подключение UI и параметры плоскости управления', description,
tabs = DEFAULT_TABS, tabs = DEFAULT_TABS,
}: SettingsShellProps) { }: SettingsShellProps) {
const isMobile = useIsMobile() const isMobile = useIsMobile()
const pathname = useRouterState({ select: (s) => s.location.pathname }) const pathname = useRouterState({ select: (s) => s.location.pathname })
const headerDescription =
description ??
(pathname.startsWith('/tenant-settings')
? 'Глобальные параметры BIRD и плоскости управления'
: 'Подключение UI и параметры плоскости управления')
return ( return (
<PageShell> <PageShell>
<div className="mx-auto flex w-full max-w-4xl flex-col gap-5"> <div className="mx-auto flex w-full max-w-4xl flex-col gap-5">
<PageHeader title={title} description={description} /> <PageHeader title={title} description={headerDescription} />
<div <div
className={cn( className={cn(
@@ -133,7 +133,9 @@ export function ScheduleAgendaPanel({
</div> </div>
<Select value={filter} onValueChange={(v) => v && setFilter(v as JobFilter)}> <Select value={filter} onValueChange={(v) => v && setFilter(v as JobFilter)}>
<SelectTrigger size="sm" className="w-full sm:w-44"> <SelectTrigger size="sm" className="w-full sm:w-44">
<SelectValue /> <SelectValue>
{FILTER_ITEMS.find((item) => item.value === filter)?.label}
</SelectValue>
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{FILTER_ITEMS.map((item) => ( {FILTER_ITEMS.map((item) => (
+3 -3
View File
@@ -39,11 +39,11 @@ export function AnalyticsDashboardSkeleton() {
</div> </div>
<div className={dashboardMainSidebarClassName}> <div className={dashboardMainSidebarClassName}>
<Skeleton className="h-96 w-full rounded-xl xl:col-span-8" /> <Skeleton className="h-96 w-full min-w-0 rounded-xl" />
<Skeleton className="h-96 w-full rounded-xl xl:col-span-4" /> <Skeleton className="h-96 w-full min-w-0 rounded-xl" />
</div> </div>
<div className="grid gap-4 lg:grid-cols-2"> <div className={chartPanelGridClassName}>
<Skeleton className="h-64 w-full rounded-xl" /> <Skeleton className="h-64 w-full rounded-xl" />
<Skeleton className="h-64 w-full rounded-xl" /> <Skeleton className="h-64 w-full rounded-xl" />
</div> </div>
+1
View File
@@ -44,6 +44,7 @@ export const DATA_GRID_MESSAGES_RU = {
export const DATA_GRID_DENSE_LAYOUT: NonNullable<DataGridProps<object>['tableLayout']> = { export const DATA_GRID_DENSE_LAYOUT: NonNullable<DataGridProps<object>['tableLayout']> = {
...DATA_GRID_TABLE_LAYOUT, ...DATA_GRID_TABLE_LAYOUT,
dense: true, dense: true,
width: 'auto',
} }
export function createTextGlobalFilter<TData extends object>( export function createTextGlobalFilter<TData extends object>(
+94
View File
@@ -0,0 +1,94 @@
import {
DEFAULT_I18N,
type FilterI18nConfig,
} from '@/components/reui/filters'
const OPERATOR_LABEL_RU: Record<string, string> = {
is: 'равно',
is_not: 'не равно',
is_any_of: 'любое из',
is_not_any_of: 'кроме',
includes_all: 'включает все',
excludes_all: 'исключает все',
before: 'до',
after: 'после',
between: 'между',
not_between: 'вне диапазона',
contains: 'содержит',
not_contains: 'не содержит',
starts_with: 'начинается с',
ends_with: 'заканчивается на',
equals: 'равно',
not_equals: 'не равно',
greater_than: 'больше',
less_than: 'меньше',
overlaps: 'пересекается',
includes: 'включает',
excludes: 'исключает',
empty: 'пусто',
not_empty: 'не пусто',
}
export const FILTERS_I18N_RU: FilterI18nConfig = {
...DEFAULT_I18N,
addFilter: 'Фильтр',
searchFields: 'Фильтр…',
noFieldsFound: 'Поля не найдены.',
noResultsFound: 'Ничего не найдено.',
select: 'Выбрать…',
true: 'Да',
false: 'Нет',
min: 'Мин.',
max: 'Макс.',
to: '—',
typeAndPressEnter: 'Введите и нажмите Enter',
selected: 'выбрано',
selectedCount: 'выбрано',
addFilterTitle: 'Добавить фильтр',
operators: {
...DEFAULT_I18N.operators,
is: 'равно',
isNot: 'не равно',
isAnyOf: 'любое из',
isNotAnyOf: 'кроме',
includesAll: 'включает все',
excludesAll: 'исключает все',
before: 'до',
after: 'после',
between: 'между',
notBetween: 'вне диапазона',
contains: 'содержит',
notContains: 'не содержит',
startsWith: 'начинается с',
endsWith: 'заканчивается на',
isExactly: 'точно',
equals: 'равно',
notEquals: 'не равно',
greaterThan: 'больше',
lessThan: 'меньше',
overlaps: 'пересекается',
includes: 'включает',
excludes: 'исключает',
includesAllOf: 'включает все',
includesAnyOf: 'включает любое',
empty: 'пусто',
notEmpty: 'не пусто',
},
placeholders: {
enterField: (fieldType: string) => `Введите ${fieldType}`,
selectField: 'Выбрать…',
searchField: (fieldName: string) => `Поиск ${fieldName.toLowerCase()}`,
enterKey: 'Введите ключ…',
enterValue: 'Введите значение…',
},
helpers: {
formatOperator: (operator: string) =>
OPERATOR_LABEL_RU[operator] ?? operator.replace(/_/g, ' '),
},
validation: {
invalidEmail: 'Некорректный email',
invalidUrl: 'Некорректный URL',
invalidTel: 'Некорректный телефон',
invalid: 'Некорректное значение',
},
}
+9 -3
View File
@@ -30,7 +30,13 @@ export const kpiGridClassName =
export const kpiCardContentClassName = 'flex flex-col items-start gap-4 p-5' export const kpiCardContentClassName = 'flex flex-col items-start gap-4 p-5'
/** Two-column chart panel row (monitoring / network overview). */ /** Two-column chart panel row (monitoring / network overview). */
export const chartPanelGridClassName = 'grid grid-cols-1 gap-4 @5xl:grid-cols-2' export const chartPanelGridClassName =
'grid min-w-0 grid-cols-1 items-start gap-4 @5xl:grid-cols-2'
/** Main + sidebar layout (8+4) used on dashboard. */ /**
export const dashboardMainSidebarClassName = 'grid gap-4 xl:grid-cols-12 xl:items-start' * Main + sidebar: stack until the dashboard container is wide enough.
* Do not nest this inside another 2-col grid — that squeezes modules/timeline.
* @see https://reui.io/preview/base/dashboard-1
*/
export const dashboardMainSidebarClassName =
'grid min-w-0 grid-cols-1 items-start gap-4 @5xl:grid-cols-2'
+1 -1
View File
@@ -64,7 +64,7 @@ function AccessComponent() {
variant: revokedCount > 0 ? 'warning' : 'default', variant: revokedCount > 0 ? 'warning' : 'default',
footer: ( footer: (
<Badge variant={revokedCount > 0 ? 'warning-light' : 'outline'} size="sm"> <Badge variant={revokedCount > 0 ? 'warning-light' : 'outline'} size="sm">
revoked отозваны
</Badge> </Badge>
), ),
}, },
+21 -21
View File
@@ -37,7 +37,9 @@ function parseShowQuickActions(value: unknown): boolean {
} }
/** /**
* Dashboard — OpsDashboard kit (KPI → QuickActions → charts → queue). * Dashboard — OpsDashboard kit (KPI → QuickActions → modules → activity/health → queue).
* Charts slot is a vertical stack: modules stay full-width; activity shares a row
* with BGP widgets only at @5xl (container), never nested 8+4 inside a 2-col parent.
* @see https://reui.io/preview/base/dashboard-1 * @see https://reui.io/preview/base/dashboard-1
* @see https://reui.io/preview/base/stats-12 * @see https://reui.io/preview/base/stats-12
*/ */
@@ -99,33 +101,31 @@ function DashboardComponent() {
afterKpi={showQuickActions ? <DashboardQuickLinks /> : null} afterKpi={showQuickActions ? <DashboardQuickLinks /> : null}
charts={ charts={
<> <>
<div className={dashboardMainSidebarClassName}> <div className="min-w-0">
<div className="xl:col-span-8"> <DashboardModulesGrid modules={modules} isLoading={refreshing} />
<DashboardModulesGrid modules={modules} isLoading={refreshing} />
</div>
<div className="xl:col-span-4">
<DashboardActivityTimeline
jobs={jobs}
revisions={revisions}
peers={peers}
speakers={speakers}
/>
</div>
</div> </div>
<div className={chartPanelGridClassName}> <div className={dashboardMainSidebarClassName}>
<DashboardNetworkHealth peers={peers} speakers={speakers} jobs={jobs} /> <DashboardActivityTimeline
<DashboardOperationsBreakdown jobs={jobs} modules={modules} /> jobs={jobs}
revisions={revisions}
peers={peers}
speakers={speakers}
/>
<div className="grid min-w-0 gap-4">
<DashboardNetworkHealth peers={peers} speakers={speakers} jobs={jobs} />
<DashboardOperationsBreakdown jobs={jobs} modules={modules} />
</div>
</div> </div>
</> </>
} }
queueTitle="Недавняя активность" queueTitle="Задачи и ревизии"
queueDescription="Задачи и ревизии плоскости управления" queueDescription="Последние фоновые операции и история конфигураций"
queue={ queue={
<div className="grid gap-4 lg:grid-cols-2"> <div className={chartPanelGridClassName}>
<DashboardFramePanel <DashboardFramePanel
title="Недавние задачи" title="Недавние задачи"
description="Последние фоновые операции" description="Последние фоновые операции"
className="h-full" className="h-full min-w-0"
> >
{activityLoading ? ( {activityLoading ? (
<Skeleton className="m-4 h-24 w-auto" /> <Skeleton className="m-4 h-24 w-auto" />
@@ -136,7 +136,7 @@ function DashboardComponent() {
<DashboardFramePanel <DashboardFramePanel
title="Последние ревизии" title="Последние ревизии"
description="История конфигураций" description="История конфигураций"
className="h-full" className="h-full min-w-0"
> >
{activityLoading ? ( {activityLoading ? (
<Skeleton className="m-4 h-24 w-auto" /> <Skeleton className="m-4 h-24 w-auto" />
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -1,7 +1,7 @@
# Скопируйте в .env рядом с docker-compose.yaml и подставьте своего владельца реестра (нижний регистр). # Скопируйте в .env рядом с docker-compose.yaml и подставьте своего владельца реестра (нижний регистр).
# Перед первым запуском: docker login git.shts.su # Перед первым запуском: docker login git.shx.one
EVOBGP_REGISTRY=git.shts.su/denozord EVOBGP_REGISTRY=git.shx.one/denozord
EVOBGP_IMAGE_TAG=latest EVOBGP_IMAGE_TAG=latest
# Для защищенного Web UI (Traefik + Let's Encrypt + Cloudflare DNS challenge) # Для защищенного Web UI (Traefik + Let's Encrypt + Cloudflare DNS challenge)
+1 -1
View File
@@ -1,7 +1,7 @@
# Production /opt/evobgp — скопируйте в .env на сервере # Production /opt/evobgp — скопируйте в .env на сервере
# cp .env.production.example .env # cp .env.production.example .env
EVOBGP_REGISTRY=git.shts.su/denozord EVOBGP_REGISTRY=git.shx.one/denozord
EVOBGP_IMAGE_TAG=latest EVOBGP_IMAGE_TAG=latest
# Имя compose-проекта (должно совпадать с name: в docker-compose.yaml) # Имя compose-проекта (должно совпадать с name: в docker-compose.yaml)
+1 -1
View File
@@ -1,7 +1,7 @@
# Скопируйте в .env.remote-speaker рядом с docker-compose.remote-speaker.yaml # Скопируйте в .env.remote-speaker рядом с docker-compose.remote-speaker.yaml
# Значения agent_secret и node token — из Web UI после создания спикера. # Значения agent_secret и node token — из Web UI после создания спикера.
EVOBGP_REGISTRY=git.shts.su/denozord EVOBGP_REGISTRY=git.shx.one/denozord
EVOBGP_IMAGE_TAG=latest EVOBGP_IMAGE_TAG=latest
# Control plane (HTTPS в prod) # Control plane (HTTPS в prod)
@@ -4,7 +4,7 @@
# docker compose --env-file .env.stack.microvps-full -f stack.microvps-full.yaml up -d # docker compose --env-file .env.stack.microvps-full -f stack.microvps-full.yaml up -d
# Образы EvoBGP # Образы EvoBGP
EVOBGP_REGISTRY=git.shts.su/denozord EVOBGP_REGISTRY=git.shx.one/denozord
EVOBGP_IMAGE_TAG=latest EVOBGP_IMAGE_TAG=latest
# Защищенный Web UI (Traefik + Let's Encrypt + Cloudflare DNS challenge) # Защищенный Web UI (Traefik + Let's Encrypt + Cloudflare DNS challenge)
+2 -2
View File
@@ -7,7 +7,7 @@ name: evobgp-bgp-stack
services: services:
bird2: bird2:
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-bird2:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-bird2:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
@@ -17,7 +17,7 @@ services:
# network_mode: "host" # раскомментируйте при анонсе BGP с хоста # network_mode: "host" # раскомментируйте при анонсе BGP с хоста
evobgp-agent: evobgp-agent:
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-agent:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-agent:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
- bird2 - bird2
@@ -1,6 +1,6 @@
# Дополнительный файл Compose: профиль microvps-full + ужатые лимиты под хост ~1 ГиБ RAM. # Дополнительный файл Compose: профиль microvps-full + ужатые лимиты под хост ~1 ГиБ RAM.
# #
# Запуск (PowerShell, из каталога deploy\compose): сначала .env из .env.example, docker login git.shts.su, docker compose pull # Запуск (PowerShell, из каталога deploy\compose): сначала .env из .env.example, docker login git.shx.one, docker compose pull
# docker compose -f docker-compose.yaml -f docker-compose.microvps-full.yaml --profile microvps-full up -d # docker compose -f docker-compose.yaml -f docker-compose.microvps-full.yaml --profile microvps-full up -d
# #
# Остановка: # Остановка:
@@ -6,7 +6,7 @@
# Запуск: # Запуск:
# cd /opt/evobgp # cd /opt/evobgp
# mkdir -p runtime-logs # mkdir -p runtime-logs
# docker login git.shts.su # docker login git.shx.one
# docker compose pull # docker compose pull
# docker compose up -d # docker compose up -d
# #
@@ -74,7 +74,7 @@ services:
max-file: "3" max-file: "3"
bird2: bird2:
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-bird2:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-bird2:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
@@ -93,7 +93,7 @@ services:
max-file: "3" max-file: "3"
evobgp-agent: evobgp-agent:
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-agent:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-agent:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
bird2: bird2:
@@ -112,7 +112,7 @@ services:
max-file: "3" max-file: "3"
evobgp-all: evobgp-all:
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-all:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-all:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
postgres: postgres:
@@ -157,7 +157,7 @@ services:
max-file: "3" max-file: "3"
evobgp-web: evobgp-web:
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-web-all:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-web-all:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
evobgp-all: evobgp-all:
@@ -22,7 +22,7 @@ x-logging: &default-logging
services: services:
bird2: bird2:
profiles: ["production", "plain", "fallback"] profiles: ["production", "plain", "fallback"]
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-bird2:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-bird2:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
network_mode: host network_mode: host
cap_add: cap_add:
@@ -35,7 +35,7 @@ services:
evobgp-agent: evobgp-agent:
profiles: ["production"] profiles: ["production"]
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-agent:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-agent:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
- bird2 - bird2
@@ -70,7 +70,7 @@ services:
evobgp-agent-plain: evobgp-agent-plain:
profiles: ["plain", "fallback"] profiles: ["plain", "fallback"]
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-agent:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-agent:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
network_mode: host network_mode: host
depends_on: depends_on:
@@ -127,7 +127,7 @@ services:
sync-bundle: sync-bundle:
profiles: ["fallback"] profiles: ["fallback"]
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-node:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-node:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
- bird2 - bird2
+12 -12
View File
@@ -1,8 +1,8 @@
# EvoBGP Compose: профили `reference` (эталон) и `microvps` (одна VPS). # EvoBGP Compose: профили `reference` (эталон) и `microvps` (одна VPS).
# #
# Образы EvoBGP тянутся из Container Registry (не собираются здесь). Префикс и тег — .env (см. .env.example): # Образы EvoBGP тянутся из Container Registry (не собираются здесь). Префикс и тег — .env (см. .env.example):
# EVOBGP_REGISTRY=git.shts.su/<owner> EVOBGP_IMAGE_TAG=latest # EVOBGP_REGISTRY=git.shx.one/<owner> EVOBGP_IMAGE_TAG=latest
# Перед up: docker login git.shts.su && docker compose pull # Перед up: docker login git.shx.one && docker compose pull
# #
# Запуск: # Запуск:
# docker compose --profile reference up -d # docker compose --profile reference up -d
@@ -81,7 +81,7 @@ services:
evobgp-api: evobgp-api:
profiles: ["reference"] profiles: ["reference"]
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-api:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-api:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
postgres: postgres:
@@ -115,7 +115,7 @@ services:
evobgp-scheduler: evobgp-scheduler:
profiles: ["reference"] profiles: ["reference"]
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-scheduler:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-scheduler:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
postgres: postgres:
@@ -139,7 +139,7 @@ services:
evobgp-ingest: evobgp-ingest:
profiles: ["reference"] profiles: ["reference"]
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-ingest:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-ingest:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
postgres: postgres:
@@ -157,7 +157,7 @@ services:
evobgp-render: evobgp-render:
profiles: ["reference"] profiles: ["reference"]
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-render:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-render:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
postgres: postgres:
@@ -175,7 +175,7 @@ services:
evobgp-deploy: evobgp-deploy:
profiles: ["reference"] profiles: ["reference"]
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-deploy:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-deploy:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
postgres: postgres:
@@ -196,7 +196,7 @@ services:
evobgp-web: evobgp-web:
profiles: ["reference"] profiles: ["reference"]
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-web:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-web:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
- evobgp-api - evobgp-api
@@ -211,7 +211,7 @@ services:
evobgp-all: evobgp-all:
profiles: ["microvps", "microvps-full"] profiles: ["microvps", "microvps-full"]
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-all:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-all:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
postgres: postgres:
@@ -241,7 +241,7 @@ services:
# Web UI → evobgp-all (образ evobgp-web-all из реестра). Reference: сервис evobgp-web → evobgp-api. # Web UI → evobgp-all (образ evobgp-web-all из реестра). Reference: сервис evobgp-web → evobgp-api.
evobgp-web-microvps: evobgp-web-microvps:
profiles: ["microvps-full"] profiles: ["microvps-full"]
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-web-all:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-web-all:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
- evobgp-all - evobgp-all
@@ -329,7 +329,7 @@ services:
bird2: bird2:
profiles: ["reference", "microvps", "microvps-full"] profiles: ["reference", "microvps", "microvps-full"]
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-bird2:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-bird2:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
@@ -349,7 +349,7 @@ services:
evobgp-agent: evobgp-agent:
profiles: ["reference", "microvps", "microvps-full"] profiles: ["reference", "microvps", "microvps-full"]
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-agent:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-agent:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
- bird2 - bird2
+4 -4
View File
@@ -78,7 +78,7 @@ services:
max-file: "3" max-file: "3"
bird2: bird2:
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-bird2:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-bird2:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
@@ -97,7 +97,7 @@ services:
max-file: "3" max-file: "3"
evobgp-agent: evobgp-agent:
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-agent:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-agent:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
bird2: bird2:
@@ -116,7 +116,7 @@ services:
max-file: "3" max-file: "3"
evobgp-all: evobgp-all:
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-all:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-all:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
postgres: postgres:
@@ -161,7 +161,7 @@ services:
max-file: "3" max-file: "3"
evobgp-web: evobgp-web:
image: ${EVOBGP_REGISTRY:-git.shts.su/denozord}/evobgp-web-all:${EVOBGP_IMAGE_TAG:-latest} image: ${EVOBGP_REGISTRY:-git.shx.one/denozord}/evobgp-web-all:${EVOBGP_IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
evobgp-all: evobgp-all:
+3 -3
View File
@@ -17,8 +17,8 @@
Кэш registry (переменные bake): Кэш registry (переменные bake):
- `git.shts.su/<owner>/evobgp-buildcache:go-buildcache`**запись** только из target `go-build-all` - `git.shx.one/<owner>/evobgp-buildcache:go-buildcache`**запись** только из target `go-build-all`
- `git.shts.su/<owner>/evobgp-buildcache:web-buildcache`**запись** только из target `web-build` - `git.shx.one/<owner>/evobgp-buildcache:web-buildcache`**запись** только из target `web-build`
Остальные bake-target’ы только `cache-from` (чтение). Параллельный `cache-to` в один ref ломает manifest в registry (`content descriptor … not found`). Остальные bake-target’ы только `cache-from` (чтение). Параллельный `cache-to` в один ref ломает manifest в registry (`content descriptor … not found`).
@@ -32,7 +32,7 @@
```bash ```bash
cd deploy/docker cd deploy/docker
export REGISTRY=git.shts.su/<owner> export REGISTRY=git.shx.one/<owner>
export IMAGE_TAG=latest export IMAGE_TAG=latest
export SHORT_SHA=$(git rev-parse --short HEAD) export SHORT_SHA=$(git rev-parse --short HEAD)
export VERSION=dev export VERSION=dev
+1 -1
View File
@@ -4,7 +4,7 @@
# Переменные: REGISTRY, IMAGE_TAG, CACHE_REF_GO, CACHE_REF_WEB # Переменные: REGISTRY, IMAGE_TAG, CACHE_REF_GO, CACHE_REF_WEB
variable "REGISTRY" { variable "REGISTRY" {
default = "git.shts.su/evobgp" default = "git.shx.one/evobgp"
} }
variable "IMAGE_TAG" { variable "IMAGE_TAG" {
+1 -1
View File
@@ -14,7 +14,7 @@
| Раздел | Описание | | Раздел | Описание |
|--------|----------| |--------|----------|
| [overview.md](overview.md) | Ключевые возможности системы | | [overview.md](overview.md) | Ключевые возможности системы |
| [quickstart.md](quickstart.md) | Быстрый запуск: готовые образы из `git.shts.su`, Docker Compose, локальный Go, веб | | [quickstart.md](quickstart.md) | Быстрый запуск: готовые образы из `git.shx.one`, Docker Compose, локальный Go, веб |
| [architecture.md](architecture.md) | Компоненты, потоки данных, пакеты | | [architecture.md](architecture.md) | Компоненты, потоки данных, пакеты |
| [manual.md](manual.md) | Подробное руководство по модулям, процессам и API | | [manual.md](manual.md) | Подробное руководство по модулям, процессам и API |
| [api.md](api.md) | REST: префикс `/v1`, публичные маршруты, ссылки на OpenAPI | | [api.md](api.md) | REST: префикс `/v1`, публичные маршруты, ссылки на OpenAPI |
+1 -1
View File
@@ -4,7 +4,7 @@
## Portal SSO (JWT) ## Portal SSO (JWT)
Единый вход через **auth-portal** (app id `bgp`). См. [integrate-evobgp.md](https://git.shts.su/denozord/auth-portal/src/branch/main/docs/integrate-evobgp.md) в репозитории auth-portal. Единый вход через **auth-portal** (app id `bgp`). См. [integrate-evobgp.md](https://git.shx.one/denozord/auth-portal/src/branch/main/docs/integrate-evobgp.md) в репозитории auth-portal.
| Переменная | Назначение | | Переменная | Назначение |
|------------|------------| |------------|------------|
+1 -1
View File
@@ -1,6 +1,6 @@
# Firewall (deprecated in EvoBGP) # Firewall (deprecated in EvoBGP)
**Hard cutover:** подсистема firewall перенесена в отдельный продукт **[EvoFirewall](https://git.shts.su/denozord/EvoFirewall)**. **Hard cutover:** подсистема firewall перенесена в отдельный продукт **[EvoFirewall](https://git.shx.one/denozord/EvoFirewall)**.
Все HTTP-эндпоинты `/v1/firewall/*` в EvoBGP отвечают **410 Gone**. Все HTTP-эндпоинты `/v1/firewall/*` в EvoBGP отвечают **410 Gone**.
+1 -1
View File
@@ -65,7 +65,7 @@ CLI `evobgp-node` поддерживает `pull-bundle`, `verify-bundle`, `appl
## Связанные документы ## Связанные документы
- [quickstart.md](quickstart.md) — Docker Compose, **готовые образы из Container Registry** (`docker pull git.shts.su/...`) без сборки на сервере. - [quickstart.md](quickstart.md) — Docker Compose, **готовые образы из Container Registry** (`docker pull git.shx.one/...`) без сборки на сервере.
- [api.md](api.md) — как вызывать API на практике. - [api.md](api.md) — как вызывать API на практике.
- [architecture.md](architecture.md) — из каких процессов и пакетов это собрано. - [architecture.md](architecture.md) — из каких процессов и пакетов это собрано.
- [evobgp-api-sketches.md](evobgp-api-sketches.md) — ранние таблицы эндпоинтов (черновик). - [evobgp-api-sketches.md](evobgp-api-sketches.md) — ранние таблицы эндпоинтов (черновик).
+20 -20
View File
@@ -11,12 +11,12 @@
## Готовые образы без сборки (Container Registry Gitea) ## Готовые образы без сборки (Container Registry Gitea)
После успешного CI (push в `main` или `master`) образы публикуются в **Container Registry** вашего Gitea. В workflow зафиксирован хост реестра **`git.shts.su`**; имя владельца в пути образа — **в нижнем регистре**, как у `github.repository_owner` в CI (например, пользователь `Denozord` → префикс `denozord`). После успешного CI (push в `main` или `master`) образы публикуются в **Container Registry** вашего Gitea. В workflow зафиксирован хост реестра **`git.shx.one`**; имя владельца в пути образа — **в нижнем регистре**, как у `github.repository_owner` в CI (например, пользователь `Denozord` → префикс `denozord`).
### Шаблон имени и теги ### Шаблон имени и теги
```text ```text
git.shts.su/<owner>/<имя_образа>:<тег> git.shx.one/<owner>/<имя_образа>:<тег>
``` ```
**Теги:** `latest`, короткий SHA коммита (7 символов), `sha-<полный_sha>` — см. [.gitea/README.md](../.gitea/README.md). **Теги:** `latest`, короткий SHA коммита (7 символов), `sha-<полный_sha>` — см. [.gitea/README.md](../.gitea/README.md).
@@ -28,28 +28,28 @@ git.shts.su/<owner>/<имя_образа>:<тег>
На сервере или в PowerShell перед `docker pull`: На сервере или в PowerShell перед `docker pull`:
```powershell ```powershell
docker login git.shts.su docker login git.shx.one
``` ```
Укажите учётную запись Gitea и **PAT / пароль приложения** с правом чтения пакетов (или токен, который принимает ваш реестр). Укажите учётную запись Gitea и **PAT / пароль приложения** с правом чтения пакетов (или токен, который принимает ваш реестр).
### Ссылки и команды `docker pull` ### Ссылки и команды `docker pull`
Ниже пример для владельца **`denozord`** — замените сегмент пути на своего владельца репозитория в нижнем регистре. В веб-интерфейсе все контейнерные пакеты можно открыть разом: [список пакетов `denozord` на git.shts.su](https://git.shts.su/denozord/-/packages). Если прямая ссылка на версию `latest` не открывается (зависит от версии Gitea), откройте общий список пакетов и выберите нужный образ по имени. Ниже пример для владельца **`denozord`** — замените сегмент пути на своего владельца репозитория в нижнем регистре. В веб-интерфейсе все контейнерные пакеты можно открыть разом: [список пакетов `denozord` на git.shx.one](https://git.shx.one/denozord/-/packages). Если прямая ссылка на версию `latest` не открывается (зависит от версии Gitea), откройте общий список пакетов и выберите нужный образ по имени.
| Образ | Назначение | Страница пакета (пример) | Pull | | Образ | Назначение | Страница пакета (пример) | Pull |
|--------|------------|--------------------------|------| |--------|------------|--------------------------|------|
| `evobgp-api` | HTTP API (с `birdc` в образе) | [packages/…/evobgp-api](https://git.shts.su/denozord/-/packages/container/evobgp-api/latest) | `docker pull git.shts.su/denozord/evobgp-api:latest` | | `evobgp-api` | HTTP API (с `birdc` в образе) | [packages/…/evobgp-api](https://git.shx.one/denozord/-/packages/container/evobgp-api/latest) | `docker pull git.shx.one/denozord/evobgp-api:latest` |
| `evobgp-all` | Монолит microVPS: API + in-process воркеры scheduler/ingest/render/deploy | [packages/…/evobgp-all](https://git.shts.su/denozord/-/packages/container/evobgp-all/latest) | `docker pull git.shts.su/denozord/evobgp-all:latest` | | `evobgp-all` | Монолит microVPS: API + in-process воркеры scheduler/ingest/render/deploy | [packages/…/evobgp-all](https://git.shx.one/denozord/-/packages/container/evobgp-all/latest) | `docker pull git.shx.one/denozord/evobgp-all:latest` |
| `evobgp-scheduler` | Планировщик (reference) | [packages/…/evobgp-scheduler](https://git.shts.su/denozord/-/packages/container/evobgp-scheduler/latest) | `docker pull git.shts.su/denozord/evobgp-scheduler:latest` | | `evobgp-scheduler` | Планировщик (reference) | [packages/…/evobgp-scheduler](https://git.shx.one/denozord/-/packages/container/evobgp-scheduler/latest) | `docker pull git.shx.one/denozord/evobgp-scheduler:latest` |
| `evobgp-ingest` | Ingest CDN / ETag | [packages/…/evobgp-ingest](https://git.shts.su/denozord/-/packages/container/evobgp-ingest/latest) | `docker pull git.shts.su/denozord/evobgp-ingest:latest` | | `evobgp-ingest` | Ingest CDN / ETag | [packages/…/evobgp-ingest](https://git.shx.one/denozord/-/packages/container/evobgp-ingest/latest) | `docker pull git.shx.one/denozord/evobgp-ingest:latest` |
| `evobgp-render` | Render | [packages/…/evobgp-render](https://git.shts.su/denozord/-/packages/container/evobgp-render/latest) | `docker pull git.shts.su/denozord/evobgp-render:latest` | | `evobgp-render` | Render | [packages/…/evobgp-render](https://git.shx.one/denozord/-/packages/container/evobgp-render/latest) | `docker pull git.shx.one/denozord/evobgp-render:latest` |
| `evobgp-deploy` | Deploy | [packages/…/evobgp-deploy](https://git.shts.su/denozord/-/packages/container/evobgp-deploy/latest) | `docker pull git.shts.su/denozord/evobgp-deploy:latest` | | `evobgp-deploy` | Deploy | [packages/…/evobgp-deploy](https://git.shx.one/denozord/-/packages/container/evobgp-deploy/latest) | `docker pull git.shx.one/denozord/evobgp-deploy:latest` |
| `evobgp-node` | Нода на площадке | [packages/…/evobgp-node](https://git.shts.su/denozord/-/packages/container/evobgp-node/latest) | `docker pull git.shts.su/denozord/evobgp-node:latest` | | `evobgp-node` | Нода на площадке | [packages/…/evobgp-node](https://git.shx.one/denozord/-/packages/container/evobgp-node/latest) | `docker pull git.shx.one/denozord/evobgp-node:latest` |
| `evobgp-web` | Статика UI + nginx (прокси на **evobgp-api**) | [packages/…/evobgp-web](https://git.shts.su/denozord/-/packages/container/evobgp-web/latest) | `docker pull git.shts.su/denozord/evobgp-web:latest` | | `evobgp-web` | Статика UI + nginx (прокси на **evobgp-api**) | [packages/…/evobgp-web](https://git.shx.one/denozord/-/packages/container/evobgp-web/latest) | `docker pull git.shx.one/denozord/evobgp-web:latest` |
| `evobgp-web-all` | Тот же UI, прокси на **evobgp-all** (профиль `microvps-full`) | [packages/…/evobgp-web-all](https://git.shts.su/denozord/-/packages/container/evobgp-web-all/latest) | `docker pull git.shts.su/denozord/evobgp-web-all:latest` | | `evobgp-web-all` | Тот же UI, прокси на **evobgp-all** (профиль `microvps-full`) | [packages/…/evobgp-web-all](https://git.shx.one/denozord/-/packages/container/evobgp-web-all/latest) | `docker pull git.shx.one/denozord/evobgp-web-all:latest` |
| `evobgp-agent` | Агент (bird2 в образе) | [packages/…/evobgp-agent](https://git.shts.su/denozord/-/packages/container/evobgp-agent/latest) | `docker pull git.shts.su/denozord/evobgp-agent:latest` | | `evobgp-agent` | Агент (bird2 в образе) | [packages/…/evobgp-agent](https://git.shx.one/denozord/-/packages/container/evobgp-agent/latest) | `docker pull git.shx.one/denozord/evobgp-agent:latest` |
| `evobgp-bird2` | Только BIRD2 | [packages/…/evobgp-bird2](https://git.shts.su/denozord/-/packages/container/evobgp-bird2/latest) | `docker pull git.shts.su/denozord/evobgp-bird2:latest` | | `evobgp-bird2` | Только BIRD2 | [packages/…/evobgp-bird2](https://git.shx.one/denozord/-/packages/container/evobgp-bird2/latest) | `docker pull git.shx.one/denozord/evobgp-bird2:latest` |
На **Linux-сервере** команды `docker pull` и `docker login` такие же (выполняйте в обычном shell). На **Linux-сервере** команды `docker pull` и `docker login` такие же (выполняйте в обычном shell).
@@ -61,10 +61,10 @@ docker login git.shts.su
docker run --rm -p 8080:8080 ` docker run --rm -p 8080:8080 `
-e EVOBGP_DATABASE_URL="postgres://user:pass@host:5432/evobgp?sslmode=disable" ` -e EVOBGP_DATABASE_URL="postgres://user:pass@host:5432/evobgp?sslmode=disable" `
-e EVOBGP_HTTP_ADDR=":8080" ` -e EVOBGP_HTTP_ADDR=":8080" `
git.shts.su/denozord/evobgp-api:latest git.shx.one/denozord/evobgp-api:latest
``` ```
**Compose** в `deploy/compose` поднимает стек **только из образов реестра** (`image:`, без локальной сборки). Префикс и тег задаются через **`.env`** рядом с compose-файлами (шаблон — [deploy/compose/.env.example](../deploy/compose/.env.example)): `EVOBGP_REGISTRY=git.shts.su/<owner>` (владелец в **нижнем регистре**), `EVOBGP_IMAGE_TAG=latest` или SHA. Перед первым запуском: `docker login git.shts.su`, затем `docker compose pull` и `docker compose ... up -d`. Имена пакетов и CI — [.gitea/README.md](../.gitea/README.md). **Compose** в `deploy/compose` поднимает стек **только из образов реестра** (`image:`, без локальной сборки). Префикс и тег задаются через **`.env`** рядом с compose-файлами (шаблон — [deploy/compose/.env.example](../deploy/compose/.env.example)): `EVOBGP_REGISTRY=git.shx.one/<owner>` (владелец в **нижнем регистре**), `EVOBGP_IMAGE_TAG=latest` или SHA. Перед первым запуском: `docker login git.shx.one`, затем `docker compose pull` и `docker compose ... up -d`. Имена пакетов и CI — [.gitea/README.md](../.gitea/README.md).
## Вариант 1: Docker, профиль microvps ## Вариант 1: Docker, профиль microvps
@@ -73,7 +73,7 @@ docker run --rm -p 8080:8080 `
```powershell ```powershell
cd deploy\compose cd deploy\compose
Copy-Item .env.example .env -Force # или создайте .env вручную; укажите EVOBGP_REGISTRY / EVOBGP_IMAGE_TAG Copy-Item .env.example .env -Force # или создайте .env вручную; укажите EVOBGP_REGISTRY / EVOBGP_IMAGE_TAG
docker login git.shts.su docker login git.shx.one
docker compose --profile microvps pull docker compose --profile microvps pull
docker compose --profile microvps up -d docker compose --profile microvps up -d
``` ```
@@ -107,7 +107,7 @@ docker compose --profile microvps down -v
**Рекомендация:** на хосте с ровно **1 ГиБ** включите **swap** (1–2 ГиБ), иначе при пиках возможен OOM. На **2+ ГиБ** можно поднять тот же профиль **без** второго файла — останутся более мягкие лимиты из основного `docker-compose.yaml`. **Рекомендация:** на хосте с ровно **1 ГиБ** включите **swap** (1–2 ГиБ), иначе при пиках возможен OOM. На **2+ ГиБ** можно поднять тот же профиль **без** второго файла — останутся более мягкие лимиты из основного `docker-compose.yaml`.
Из каталога `deploy\compose` (как в варианте 1: `.env` из `.env.example`, при необходимости `docker login git.shts.su`): Из каталога `deploy\compose` (как в варианте 1: `.env` из `.env.example`, при необходимости `docker login git.shx.one`):
1. Подготовьте отдельный файл параметров защищенного UI: 1. Подготовьте отдельный файл параметров защищенного UI:
@@ -193,7 +193,7 @@ docker compose --env-file .env --env-file .env.web-sec -f docker-compose.yaml -f
```powershell ```powershell
cd deploy\compose cd deploy\compose
Copy-Item .env.example .env -Force Copy-Item .env.example .env -Force
docker login git.shts.su docker login git.shx.one
docker compose --profile reference pull docker compose --profile reference pull
docker compose --profile reference up -d docker compose --profile reference up -d
``` ```
+1 -1
View File
@@ -1,6 +1,6 @@
# Релизы и версионирование EvoBGP # Релизы и версионирование EvoBGP
EvoBGP использует [Conventional Commits](https://www.conventionalcommits.org/) и [semantic-release](https://semantic-release.gitbook.io/) для полностью автоматических релизов на Gitea (`git.shts.su`). Ручное повышение версии в коде не требуется. EvoBGP использует [Conventional Commits](https://www.conventionalcommits.org/) и [semantic-release](https://semantic-release.gitbook.io/) для полностью автоматических релизов на Gitea (`git.shx.one`). Ручное повышение версии в коде не требуется.
## Как определяется версия ## Как определяется версия
+1 -1
View File
@@ -15,7 +15,7 @@ if [ ! -f "$COMPOSE" ]; then
fi fi
# Required compose interpolation vars (CI mock values). # Required compose interpolation vars (CI mock values).
export EVOBGP_REGISTRY="${EVOBGP_REGISTRY:-git.shts.su/denozord}" export EVOBGP_REGISTRY="${EVOBGP_REGISTRY:-git.shx.one/denozord}"
export EVOBGP_IMAGE_TAG="${EVOBGP_IMAGE_TAG:-latest}" export EVOBGP_IMAGE_TAG="${EVOBGP_IMAGE_TAG:-latest}"
export EVOBGP_AGENT_SECRET="${EVOBGP_AGENT_SECRET:-ci-test-secret}" export EVOBGP_AGENT_SECRET="${EVOBGP_AGENT_SECRET:-ci-test-secret}"
export EVOBGP_CONTROL_PLANE_URL="${EVOBGP_CONTROL_PLANE_URL:-https://cp.example.com}" export EVOBGP_CONTROL_PLANE_URL="${EVOBGP_CONTROL_PLANE_URL:-https://cp.example.com}"