Docker images / prepare-release (push) Successful in 15s
Docker images / backend-test (push) Successful in 2m32s
Docker images / frontend-image (push) Successful in 4m19s
Docker images / updater-image (push) Successful in 50s
Docker images / backend-image (push) Successful in 2m40s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 8s
- мастер инициализации сервера: CA и серверный сертификаты, peer/profile/proposal, пул, mode-config, policy-template, managed NAT masquerade - клиенты по сертификату (RSA) и PSK: статический IP или из пула, онлайн-статус по active-peers - скачивание .p12 и strongSwan .sswan с инструкцией, перекачка с новой passphrase - история изменений (config_revisions, секция ipsec) и restore только managed-объектов - привязка IPsec-клиентов к пользователям приложения по Common Name - страница /ipsec с KPI и вкладками Клиенты/Сервер/CLI, сайдбар, command palette
100 lines
4.2 KiB
TypeScript
100 lines
4.2 KiB
TypeScript
"use client"
|
|
|
|
import type { IpsecServerSummaryDto } from "@mmapp/contracts/ipsec"
|
|
import { Badge } from "@/components/ui/badge"
|
|
import { Button } from "@/components/ui/button"
|
|
import { cn } from "@/lib/utils"
|
|
import {
|
|
GlobeIcon,
|
|
NetworkIcon,
|
|
ShieldCheckIcon,
|
|
Trash2Icon,
|
|
} from "lucide-react"
|
|
|
|
function Row({ label, value, mono = true }: { label: string; value: string; mono?: boolean }) {
|
|
return (
|
|
<div className="flex items-baseline justify-between gap-3 py-1">
|
|
<span className="text-xs text-muted-foreground">{label}</span>
|
|
<span className={cn("truncate text-xs", mono ? "font-mono" : "")} title={value}>
|
|
{value || "—"}
|
|
</span>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export interface IpsecServerGridProps {
|
|
servers: IpsecServerSummaryDto[]
|
|
onInit?: (row: IpsecServerSummaryDto) => void
|
|
onRemove?: (row: IpsecServerSummaryDto) => void
|
|
}
|
|
|
|
function IpsecServerGrid({ servers, onInit, onRemove }: IpsecServerGridProps) {
|
|
return (
|
|
<div className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
|
{servers.map((s) => (
|
|
<div key={s.serverId} className="rounded-lg border border-border bg-card p-4 flex flex-col gap-3">
|
|
<div className="flex items-start justify-between gap-2">
|
|
<div className="min-w-0">
|
|
<p className="truncate text-sm font-medium">{s.serverName}</p>
|
|
<p className="truncate font-mono text-[11px] text-muted-foreground">
|
|
{s.serverEndpoint ?? "не инициализирован"}
|
|
</p>
|
|
</div>
|
|
{s.initialized ? (
|
|
<Badge className="gap-1">
|
|
<ShieldCheckIcon className="size-3" />
|
|
IKEv2
|
|
</Badge>
|
|
) : (
|
|
<Badge variant="outline">нет</Badge>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex flex-col divide-y divide-border/60">
|
|
<Row label="Клиенты" value={`${s.clientsOnline} онлайн / ${s.clientsTotal}`} />
|
|
<Row label="Пул адресов" value={s.pool ? `${s.pool.name}: ${s.pool.ranges}` : "—"} />
|
|
<Row label="Peer" value={s.peer ? `${s.peer.name} (${s.peer.exchangeMode ?? "ike2"}${s.peer.passive ? ", passive" : ""})` : "—"} />
|
|
<Row label="CA-сертификат" value={s.caCert ? `${s.caCert.commonName ?? s.caCert.name}${s.caCert.expiresAt ? ` · до ${s.caCert.expiresAt}` : ""}` : "—"} />
|
|
<Row label="Серверный серт." value={s.serverCert ? `${s.serverCert.commonName ?? s.serverCert.name}${s.serverCert.expiresAt ? ` · до ${s.serverCert.expiresAt}` : ""}` : "—"} />
|
|
<Row label="NAT (интернет)" value={s.natRuleManaged ? "managed masquerade" : "нет правила"} mono={false} />
|
|
</div>
|
|
|
|
<div className="mt-auto flex justify-end gap-1.5">
|
|
<Button
|
|
type="button"
|
|
size="sm"
|
|
variant="outline"
|
|
onClick={() => onInit?.(s)}
|
|
title={s.initialized ? "Обновить managed-конфиг сервера" : "Инициализировать IKEv2-сервер"}
|
|
>
|
|
<GlobeIcon className="size-3.5" />
|
|
{s.initialized ? "Переинициализировать" : "Инициализировать"}
|
|
</Button>
|
|
{s.initialized && onRemove ? (
|
|
<Button
|
|
type="button"
|
|
size="icon"
|
|
variant="ghost"
|
|
className="size-8 text-destructive"
|
|
aria-label="Удалить IKEv2-сервер"
|
|
title="Удалить managed-объекты IKEv2 (identity/mode-config/peer/pool/NAT + сертификаты)"
|
|
onClick={() => onRemove(s)}
|
|
>
|
|
<Trash2Icon className="size-3.5" />
|
|
</Button>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
))}
|
|
{servers.length === 0 ? (
|
|
<div className="col-span-full flex items-center justify-center gap-2 py-16 text-sm text-muted-foreground">
|
|
<NetworkIcon className="size-4" />
|
|
Серверы не найдены
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export { IpsecServerGrid }
|