import { z } from "zod" export const ipsecAuthMethodSchema = z.enum(["certificate", "pre-shared-key"]) /** * Минимальная длина пароля .p12. Ограничение RouterOS REST: * `Failure: If used, passphrase must be at least 8 chars long!` (см. /certificate/export-certificate). */ export const IPSEC_MIN_PASSPHRASE = 8 /** Клиент IKEv2 — /ip/ipsec/identity (+ опциональный персональный mode-config). */ export const ipsecClientDtoSchema = z.object({ id: z.string().min(1), rosId: z.string().min(1), serverId: z.string().min(1), serverName: z.string(), /** Управляемое имя клиента (из managed-комментария identity). */ name: z.string(), authMethod: ipsecAuthMethodSchema, /** cert — клиент по сертификату (может быть без персональной identity); psk — pre-shared-key identity. */ kind: z.enum(["cert", "psk"]).default("cert"), certificateName: z.string().optional(), /** Имя клиентского сертификата (роль client). Для psk — undefined. */ certName: z.string().optional(), /** CA, которым подписан клиентский сертификат (read-only поле `ca`). */ signedBy: z.string().optional(), commonName: z.string().optional(), remoteId: z.string().optional(), /** Статический IP клиента (персональный mode-config), undefined — из общего пула. */ staticIp: z.string().optional(), modeConfigName: z.string().optional(), peerName: z.string().optional(), online: z.boolean(), /** One native session ID matches several clients; online/address are not confirmed. */ sessionAmbiguous: z.boolean().optional(), activeAddress: z.string().optional(), activeSince: z.string().optional(), disabled: z.boolean(), comment: z.string().optional(), /** Создан менеджером (managed-комментарий). */ managed: z.boolean(), }).refine(c => !c.sessionAmbiguous || (!c.online && !c.activeAddress && !c.activeSince), { message: "Неоднозначная сессия IPsec не может подтверждать online или активный адрес", }) /** Слушатель IKEv2 — /ip/ipsec/peer (passive). */ export const ipsecPeerDtoSchema = z.object({ id: z.string().min(1), rosId: z.string().min(1), serverId: z.string().min(1), serverName: z.string(), name: z.string(), address: z.string().optional(), exchangeMode: z.string().optional(), passive: z.boolean().optional(), certificate: z.string().optional(), profile: z.string().optional(), disabled: z.boolean(), comment: z.string().optional(), managed: z.boolean(), }) export const ipsecModeConfigDtoSchema = z.object({ id: z.string().min(1), rosId: z.string().min(1), serverId: z.string().min(1), name: z.string(), /** Общий пул (shared) или персональный статический адрес (per-user). */ addressPool: z.string().optional(), address: z.string().optional(), splitDns: z.string().optional(), staticDns: z.string().optional(), comment: z.string().optional(), managed: z.boolean(), }) export const ipsecPoolDtoSchema = z.object({ id: z.string().min(1), rosId: z.string().min(1), serverId: z.string().min(1), name: z.string(), ranges: z.string(), comment: z.string().optional(), managed: z.boolean(), }) export const ipsecCertInfoDtoSchema = z.object({ name: z.string(), commonName: z.string().optional(), keySize: z.string().optional(), fingerprint: z.string().optional(), expiresAt: z.string().optional(), trusted: z.boolean().optional(), hasPrivateKey: z.boolean().optional(), role: z.enum(["ca", "server", "client", "other"]).optional(), /** Имя CA, которым серт подписан на устройстве (read-only поле `ca` RouterOS). */ signedBy: z.string().optional(), managed: z.boolean(), }) /** Сводка IKEv2-сервера на одном роутере. */ export const ipsecServerSummaryDtoSchema = z.object({ serverId: z.string().min(1), serverName: z.string(), serverCountry: z.string().optional(), /** Managed-набор (CA + серверный серт + peer + mode-config) инициализирован. */ initialized: z.boolean(), /** Существующий IKEv2-сервер пригоден: есть peer + CA + серверный серт. */ ike2Ready: z.boolean(), serverEndpoint: z.string().optional(), /** Primary managed peer (обратная совместимость); для полного списка — `peers`. */ peer: ipsecPeerDtoSchema.optional(), /** Все peers роутера (managed + существующие). */ peers: z.array(ipsecPeerDtoSchema).default([]), pool: ipsecPoolDtoSchema.optional(), sharedModeConfig: ipsecModeConfigDtoSchema.optional(), caCert: ipsecCertInfoDtoSchema.optional(), serverCert: ipsecCertInfoDtoSchema.optional(), natRuleManaged: z.boolean(), clientsTotal: z.number().int().nonnegative(), clientsOnline: z.number().int().nonnegative(), clientsSessionAmbiguous: z.number().int().nonnegative().optional(), /** Все сертификаты роутера (managed + существующие). */ certs: z.array(ipsecCertInfoDtoSchema).optional(), }) export const ipsecPeerPatchSchema = z.object({ name: z.string().min(1).max(64).optional(), address: z.string().optional(), exchangeMode: z.string().optional(), passive: z.boolean().optional(), certificate: z.string().optional(), profile: z.string().optional(), disabled: z.boolean().optional(), }) export const ipsecCertDeleteRequestSchema = z.object({ name: z.string().min(1), }) export const ipsecListResponseSchema = z.object({ servers: z.array(ipsecServerSummaryDtoSchema), clients: z.array(ipsecClientDtoSchema), failures: z .array( z.object({ serverId: z.string(), serverName: z.string().optional(), error: z.string(), }), ) .optional(), }) export const ipsecInitRequestSchema = z.object({ serverId: z.union([z.string(), z.number()]), /** Домен или IP для CN/SAN серверного сертификата (к нему подключаются клиенты). */ serverEndpoint: z.string().min(1), /** Подсеть пула клиентов; из неё pool ranges .2–.254. */ poolCidr: z.string().regex(/^[0-9a-fA-F.:/]+$/).default("10.77.0.0/24"), dns: z.string().optional(), caDaysValid: z.number().int().positive().default(3650), serverDaysValid: z.number().int().positive().default(3650), clientDaysValid: z.number().int().positive().default(1825), /** Managed srcnat masquerade, чтобы у клиентов был интернет. */ createNatRule: z.boolean().default(true), }) export const ipsecUserCreateRequestSchema = z.object({ serverId: z.union([z.string(), z.number()]), /** Отображаемое имя клиента; из него slug для серта/CN. */ name: z.string().min(1).max(64), /** Peer (сервер), к которому привязать identity; без — primary managed peer выбранного сервера. */ peerName: z.string().optional(), authMethod: ipsecAuthMethodSchema.default("certificate"), psk: z.string().min(8).optional(), remoteId: z.string().optional(), /** Конкретный IP клиента; без — выдаётся из пула. */ staticIp: z.string().optional(), /** Пароль на экспортируемый .p12 (RouterOS требует ≥ IPSEC_MIN_PASSPHRASE). */ passphrase: z.string().min(IPSEC_MIN_PASSPHRASE).optional(), daysValid: z.number().int().positive().optional(), }) export const ipsecUserPatchSchema = z.object({ name: z.string().min(1).max(64).optional(), /** null — вернуть выдачу из пула. */ staticIp: z.string().nullable().optional(), psk: z.string().min(8).optional(), remoteId: z.string().optional(), disabled: z.boolean().optional(), }) export const ipsecCertExportRequestSchema = z.object({ serverId: z.union([z.string(), z.number()]), clientId: z.string().min(1), passphrase: z.string().min(IPSEC_MIN_PASSPHRASE), }) /** Экспорт .p12 существующего клиентского сертификата по имени (client1/anakondra и т.п.). */ export const ipsecCertExportByNameRequestSchema = z.object({ name: z.string().min(1), passphrase: z.string().min(IPSEC_MIN_PASSPHRASE), }) /** Бандл для авторизации клиента: .p12 (+ strongSwan .sswan + инструкция). */ export const ipsecCertBundleSchema = z.object({ user: z.string(), serverEndpoint: z.string().optional(), filename: z.string(), contentB64: z.string(), mime: z.string().default("application/x-pkcs12"), passphrase: z.string().optional(), sswanFilename: z.string().optional(), sswanContent: z.string().optional(), instructions: z.string().optional(), }) export const ipsecUserCreatedSchema = z.object({ client: ipsecClientDtoSchema, /** Одноразовый бандл сертификата (для cert-клиентов). */ bundle: ipsecCertBundleSchema.optional(), }) export type IpsecAuthMethod = z.infer export type IpsecClientDto = z.infer export type IpsecPeerDto = z.infer export type IpsecPeerPatch = z.infer export type IpsecCertDeleteRequest = z.infer export type IpsecModeConfigDto = z.infer export type IpsecPoolDto = z.infer export type IpsecCertInfoDto = z.infer export type IpsecServerSummaryDto = z.infer export type IpsecListResponse = z.infer export type IpsecInitRequest = z.infer export type IpsecUserCreateRequest = z.infer export type IpsecUserPatch = z.infer export type IpsecCertExportRequest = z.infer export type IpsecCertExportByNameRequest = z.infer export type IpsecCertBundle = z.infer export type IpsecUserCreated = z.infer