163 lines
6.3 KiB
TypeScript
163 lines
6.3 KiB
TypeScript
import { z } from "zod"
|
|
|
|
export const certUsageSchema = z.enum([
|
|
"server",
|
|
"client",
|
|
"ca",
|
|
"crl-sign",
|
|
])
|
|
|
|
export const certStatusSchema = z.enum(["valid", "expired", "revoked"])
|
|
|
|
export const certificateDtoSchema = z.object({
|
|
id: z.string().min(1),
|
|
name: z.string().min(1),
|
|
serverId: z.string().min(1),
|
|
serverName: z.string().optional(),
|
|
commonName: z.string(),
|
|
sans: z.array(z.string()),
|
|
issuedBy: z.string(),
|
|
validFrom: z.string(),
|
|
validUntil: z.string(),
|
|
daysLeft: z.number().int(),
|
|
keySize: z.number().int().nonnegative(),
|
|
usage: z.array(certUsageSchema),
|
|
trusted: z.boolean(),
|
|
status: certStatusSchema,
|
|
acmeStatus: z.string().optional(),
|
|
/** Имя CA, которым серт подписан на устройстве (read-only поле `ca` RouterOS). */
|
|
signedByCertName: z.string().optional(),
|
|
})
|
|
|
|
export const certificatesListResponseSchema = z.object({
|
|
certificates: z.array(certificateDtoSchema),
|
|
failures: z.array(z.object({
|
|
serverId: z.string(),
|
|
serverName: z.string().optional(),
|
|
error: z.string(),
|
|
})),
|
|
})
|
|
|
|
export const certificateTrustStoreSchema = z.enum([
|
|
"www",
|
|
"api",
|
|
"sstp",
|
|
"ovpn",
|
|
"ipsec",
|
|
"email",
|
|
"radius",
|
|
"fetch",
|
|
"container",
|
|
])
|
|
|
|
export const certificateIssueRequestSchema = z.object({
|
|
serverId: z.union([z.string().regex(/^[1-9][0-9]*$/).refine(value => Number.isSafeInteger(Number(value))), z.number().int().positive().max(Number.MAX_SAFE_INTEGER)]),
|
|
requestId: z.string().uuid().optional(),
|
|
certName: z.string().trim().min(1).max(64),
|
|
domainNames: z.array(z.string().trim().min(1)).min(1),
|
|
keyType: z.enum(["rsa2048", "ec256"]).optional(),
|
|
trustStore: z.array(certificateTrustStoreSchema).optional(),
|
|
})
|
|
|
|
export const certificateIssueJobStatusSchema = z.enum([
|
|
"queued",
|
|
"running",
|
|
"done",
|
|
"failed",
|
|
])
|
|
|
|
export const certificateIssueStepSchema = z.enum([
|
|
"queued",
|
|
"acme_order",
|
|
"dns_challenge",
|
|
"finalize",
|
|
"dns_a_sync",
|
|
"import",
|
|
"cleanup",
|
|
"done",
|
|
"failed",
|
|
])
|
|
|
|
export const certificateIssueJobSchema = z.object({
|
|
id: z.string().min(1),
|
|
status: certificateIssueJobStatusSchema,
|
|
step: certificateIssueStepSchema,
|
|
serverId: z.string(),
|
|
certName: z.string(),
|
|
domainNames: z.array(z.string()),
|
|
requestedAt: z.string(),
|
|
startedAt: z.string().optional(),
|
|
finishedAt: z.string().optional(),
|
|
error: z.string().optional(),
|
|
})
|
|
|
|
export const acmeCloudflareSettingsDtoSchema = z.object({
|
|
directoryUrl: z.string().url(),
|
|
defaultZoneId: z.string().optional(),
|
|
tokenConfigured: z.boolean(),
|
|
updatedAt: z.string().optional(),
|
|
})
|
|
|
|
export const putAcmeCloudflareSettingsSchema = z.object({
|
|
directoryUrl: z.string().url().optional(),
|
|
defaultZoneId: z.union([z.string(), z.null()]).optional(),
|
|
cloudflareApiToken: z.union([z.string(), z.null()]).optional(),
|
|
})
|
|
|
|
export const testAcmeCloudflareSettingsSchema = z.object({
|
|
cloudflareApiToken: z.string().optional(),
|
|
})
|
|
|
|
export const certificateRenewSettingsDtoSchema = z.object({
|
|
enabled: z.boolean(),
|
|
intervalSec: z.number().int().min(300),
|
|
renewBeforeDays: z.number().int().min(1).max(90),
|
|
lastCollectedAt: z.string().nullable().optional(),
|
|
lastDurationMs: z.number().int().nullable().optional(),
|
|
lastError: z.string().nullable().optional(),
|
|
updatedAt: z.string().optional(),
|
|
})
|
|
|
|
export const putCertificateRenewSettingsSchema = z.object({
|
|
enabled: z.boolean().optional(),
|
|
intervalSec: z.number().int().min(300).optional(),
|
|
renewBeforeDays: z.number().int().min(1).max(90).optional(),
|
|
})
|
|
|
|
export type CertificateDto = z.infer<typeof certificateDtoSchema>
|
|
export type CertificatesListResponse = z.infer<typeof certificatesListResponseSchema>
|
|
export type CertificateIssueRequest = z.infer<typeof certificateIssueRequestSchema>
|
|
export type CertificateIssueJob = z.infer<typeof certificateIssueJobSchema>
|
|
export type AcmeCloudflareSettingsDto = z.infer<typeof acmeCloudflareSettingsDtoSchema>
|
|
export type CertificateRenewSettingsDto = z.infer<typeof certificateRenewSettingsDtoSchema>
|
|
|
|
export const certificateObservedSchema = certificateDtoSchema.extend({
|
|
rosId: z.string().min(1).nullable(),
|
|
validFrom: z.string().datetime({ offset: true }).nullable(),
|
|
validUntil: z.string().datetime({ offset: true }).nullable(),
|
|
rawValidFrom: z.string().nullable(), rawValidUntil: z.string().nullable(),
|
|
daysLeft: z.number().int().nullable(), keySize: z.number().int().positive().nullable(),
|
|
trusted: z.boolean().nullable(), status: z.enum(["valid", "expired", "revoked", "unknown"]),
|
|
}).superRefine((value,ctx) => {
|
|
if ((value.validUntil === null) !== (value.daysLeft === null)) ctx.addIssue({code:"custom",message:"Срок не соответствует дате"})
|
|
if (value.status === "valid" && (!value.validFrom || !value.validUntil || value.daysLeft === null || value.daysLeft < 0 || Date.parse(value.validFrom)>Date.parse(value.validUntil))) ctx.addIssue({code:"custom",message:"Действительность не подтверждена датами"})
|
|
})
|
|
export const certificateServerStateSchema = z.object({
|
|
serverId: z.string().regex(/^[1-9]\d*$/).refine(value => Number.isSafeInteger(Number(value))),
|
|
serverName: z.string(), collectedAt: z.string().datetime({ offset: true }),
|
|
certificates: z.array(certificateObservedSchema),
|
|
error: z.string().min(1).nullable(),
|
|
}).superRefine((value,ctx) => {
|
|
if (value.error && value.certificates.length) ctx.addIssue({code:"custom",message:"Ошибка загрузки содержит сертификаты"})
|
|
if (new Set(value.certificates.map(row=>row.id)).size !== value.certificates.length) ctx.addIssue({code:"custom",message:"Дубли сертификатов"})
|
|
if (value.certificates.some(row=>row.serverId !== value.serverId || !row.id.startsWith(value.serverId + ":"))) ctx.addIssue({code:"custom",message:"Чужой контекст сертификата"})
|
|
})
|
|
export const certificatesStateSchema = z.object({
|
|
collectedAt: z.string().datetime({ offset: true }), servers: z.array(certificateServerStateSchema),
|
|
}).superRefine((value,ctx) => {
|
|
if(new Set(value.servers.map(server=>server.serverId)).size!==value.servers.length)ctx.addIssue({code:"custom",message:"Дубли устройств"})
|
|
})
|
|
export type CertificateObserved = z.infer<typeof certificateObservedSchema>
|
|
export type CertificateServerState = z.infer<typeof certificateServerStateSchema>
|
|
export type CertificatesState = z.infer<typeof certificatesStateSchema>
|