"use client" import { useCallback, useEffect, useMemo, useState } from "react" import { PageHeader } from "@/components/page-header" import { FormField, FormToggle, SectionTitle } from "@/components/form-kit" import { FileImportDialog } from "@/components/file-import-dialog" import { routerCertificates, servers as mockServers } from "@/lib/data" import type { CertStatus, Server } from "@/lib/data" import type { CertificateDto } from "@mmapp/contracts/certificates" import { Frame, FramePanel } from "@/components/reui/frame" import { IconTile } from "@/components/reui/icon-tile" import { OpsPanel } from "@/components/ops-panel" import { DataPageCard } from "@/components/data-page-card" import { DataPageToolbar } from "@/components/data-page-toolbar" import { CertificatesDataGrid } from "@/components/data-grids/certificates-data-grid" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetFooter, SheetClose, } from "@/components/ui/sheet" import { cn } from "@/lib/utils" import { useDataSource } from "@/lib/data-source" import { listServers } from "@/shared/api/servers" import { toFrontendServer } from "@/entities/server/model/mappers" import { createCertificateIssueJob, getAcmeSettings, getCertificateIssueJob, listCertificates, putAcmeSettings, refreshCertificates, testAcmeSettings, } from "@/shared/api/certificates" import { toast } from "sonner" import { ShieldCheckIcon, BadgeCheckIcon, AlertTriangleIcon, AlertCircleIcon, PlusIcon, RefreshCwIcon, UploadIcon, } from "lucide-react" import { Stepper, StepperContent, StepperIndicator, StepperItem, StepperNav, StepperPanel, StepperSeparator, StepperTitle, StepperTrigger, } from "@/components/reui/stepper" function mockToDto(cert: (typeof routerCertificates)[number]): CertificateDto { return { id: cert.id, name: cert.name, serverId: cert.serverId, commonName: cert.commonName, sans: cert.sans, issuedBy: cert.issuedBy, validFrom: cert.validFrom, validUntil: cert.validUntil, daysLeft: cert.daysLeft, keySize: cert.keySize, usage: cert.usage, trusted: cert.trusted, status: cert.status, } } function CertPartAlertExpired({ expired }: { expired: CertificateDto[] }) { return (

{expired.length} {expired.length === 1 ? "истёкший сертификат" : "истёкших сертификата"}

{expired.map((c) => c.name).join(", ")} — требуют обновления

) } function CertPartAlertExpiring({ expiring }: { expiring: CertificateDto[] }) { return (

{expiring.length} {expiring.length === 1 ? "сертификат истекает" : "сертификата истекают"} в течение 30 дней

{expiring.map((c) => `${c.name} (${c.daysLeft}д)`).join(", ")}

) } function CertPartKpi({ displayCerts, expiring, expired, }: { displayCerts: CertificateDto[] expiring: CertificateDto[] expired: CertificateDto[] }) { return (
{[ { label: "Всего", value: displayCerts.length, icon: , }, { label: "Действующих", value: displayCerts.filter((c) => c.status === "valid").length, icon: , }, { label: "Истекают", value: expiring.length, icon: , }, { label: "Истёкших", value: expired.length, icon: , }, ].map((s) => (

{s.label}

{s.value}

))}
) } function CertPartAcmeSettings({ acmeDirectoryUrl, setAcmeDirectoryUrl, acmeZoneId, setAcmeZoneId, acmeTokenDraft, setAcmeTokenDraft, acmeTokenConfigured, acmeSaveBusy, onTest, onSave, }: { acmeDirectoryUrl: string setAcmeDirectoryUrl: (v: string) => void acmeZoneId: string setAcmeZoneId: (v: string) => void acmeTokenDraft: string setAcmeTokenDraft: (v: string) => void acmeTokenConfigured: boolean acmeSaveBusy: boolean onTest: () => void onSave: () => void }) { return (
event.preventDefault()} >
setAcmeDirectoryUrl(e.target.value)} />
setAcmeZoneId(e.target.value)} placeholder="Авто по домену" />
setAcmeTokenDraft(e.target.value)} placeholder={ acmeTokenConfigured ? "Токен сохранён — введите новый для замены" : "API token с правом DNS" } />
) } function CertPartReference() { return (
{[ { title: "Создать CA", lines: [ "/certificate add \\", " name=my-ca \\", " common-name=MyCA \\", " key-size=4096 \\", " days-valid=3650 \\", " key-usage=key-cert-sign,crl-sign", "/certificate sign my-ca", ], }, { title: "Импорт LE", lines: [ "/certificate import \\", " file-name=router.crt \\", " name=router-cert \\", " trusted=yes \\", " trust-store=www,api", ], }, { title: "Статус", lines: ["/certificate print detail", "/certificate export-certificate router-cert"], }, ].map((b) => (

{b.title}

                {b.lines.join("\n")}
              
))}
) } function CertPartIssueForm({ serverList, issueServerId, setIssueServerId, issueCertName, setIssueCertName, issueCommonName, setIssueCommonName, issueSans, setIssueSans, issueTrustWww, setIssueTrustWww, issueTrustApi, setIssueTrustApi, step, }: { serverList: Server[] issueServerId: string setIssueServerId: (v: string) => void issueCertName: string setIssueCertName: (v: string) => void issueCommonName: string setIssueCommonName: (v: string) => void issueSans: string setIssueSans: (v: string) => void issueTrustWww: boolean setIssueTrustWww: (v: boolean) => void issueTrustApi: boolean setIssueTrustApi: (v: boolean) => void step?: 1 | 2 | 3 | 4 }) { const showAll = step == null return (
{(showAll || step === 1) && (
Основные setIssueCertName(e.target.value)} placeholder="router-le" />
)} {(showAll || step === 2) && (
Домены setIssueCommonName(e.target.value)} placeholder="vpn.example.com" />