"use client" import { useEffect, useMemo, useState } from "react" import { IPSEC_MIN_PASSPHRASE, type IpsecCertBundle } from "@mmapp/contracts/ipsec" import { FormField, SectionTitle } from "@/components/form-kit" 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 { toast } from "sonner" import { DownloadIcon, CopyIcon, RefreshCwIcon } from "lucide-react" function downloadBlob(filename: string, blob: Blob) { const url = URL.createObjectURL(blob) const a = document.createElement("a") a.href = url a.download = filename a.click() URL.revokeObjectURL(url) } function downloadText(filename: string, content: string) { downloadBlob(filename, new Blob([content], { type: "text/plain;charset=utf-8" })) } function downloadB64(filename: string, b64: string, mime: string) { const bin = atob(b64) const bytes = new Uint8Array(bin.length) for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i) downloadBlob(filename, new Blob([bytes], { type: mime })) } function randomPassphrase(): string { // RouterOS требует ≥ IPSEC_MIN_PASSPHRASE символов const bytes = new Uint8Array(IPSEC_MIN_PASSPHRASE + 1) crypto.getRandomValues(bytes) let s = "" for (const b of bytes) s += "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"[b % 56] return s } function IpsecCertSheet({ open, onOpenChange, bundle, busy, onReexport, }: { open: boolean onOpenChange: (v: boolean) => void bundle: IpsecCertBundle | null busy?: boolean /** Перекачка с новой passphrase (серийник ключа остаётся на роутере). */ onReexport?: (passphrase: string) => void | Promise }) { const [passphrase, setPassphrase] = useState("") useEffect(() => { if (!open) return const initial = bundle?.passphrase ?? "" queueMicrotask(() => setPassphrase(initial)) }, [open, bundle]) const canDownload = useMemo( () => Boolean(bundle && passphrase.trim().length >= IPSEC_MIN_PASSPHRASE), [bundle, passphrase], ) return ( Сертификат клиента {bundle ? `«${bundle.user}»` : ""} .p12 для Windows/macOS/iOS · .sswan для strongSwan (Android/iOS)
{!bundle ? (

Бандл сертификата пуст — перезапустите экспорт с новой парольной фразой.

) : ( <>
Пароль архива .p12 0 && passphrase.trim().length < IPSEC_MIN_PASSPHRASE ? `Минимум ${IPSEC_MIN_PASSPHRASE} символов — требование RouterOS` : `Нужна при импорте .p12 на устройстве (минимум ${IPSEC_MIN_PASSPHRASE} символов)` } >
setPassphrase(e.target.value)} />
{bundle.serverEndpoint ? (

Сервер: {bundle.serverEndpoint}

) : null}
Файлы {bundle.sswanContent && bundle.sswanFilename ? ( ) : null} {bundle.instructions ? ( ) : null}
{bundle.instructions ? (
                  {bundle.instructions}
                
) : null} )}
}> Закрыть
) } export { IpsecCertSheet, downloadText, downloadB64 }