141 lines
5.3 KiB
TypeScript
141 lines
5.3 KiB
TypeScript
import { z } from "zod"
|
|
|
|
export const appUserRoleSchema = z.enum(["admin", "operator", "viewer"])
|
|
export const permLevelSchema = z.enum(["none", "read", "write"])
|
|
export const interfaceTypeSchema = z.enum(["ether", "gre", "wg", "ipsec", "other"])
|
|
|
|
export const sectionPermSchema = z.object({
|
|
section: z.string().min(1),
|
|
level: permLevelSchema,
|
|
})
|
|
|
|
export const serverPermSchema = z.object({
|
|
serverId: z.string().min(1),
|
|
level: permLevelSchema,
|
|
})
|
|
|
|
export const userBindingSchema = z.object({
|
|
id: z.string().min(1),
|
|
userId: z.string().min(1),
|
|
serverId: z.number().int().positive(),
|
|
serverName: z.string(),
|
|
serverSite: z.string(),
|
|
serverCountry: z.string(),
|
|
interfaceName: z.string().min(1),
|
|
interfaceType: interfaceTypeSchema,
|
|
peerPublicKey: z.string().default(""),
|
|
peerName: z.string().default(""),
|
|
comment: z.string(),
|
|
createdAt: z.string(),
|
|
updatedAt: z.string(),
|
|
})
|
|
|
|
export const appUserReadSchema = z.object({
|
|
id: z.string().min(1),
|
|
name: z.string(),
|
|
login: z.string(),
|
|
email: z.string(),
|
|
role: appUserRoleSchema,
|
|
active: z.boolean(),
|
|
avatar: z.string(),
|
|
lastSeen: z.string().nullable(),
|
|
sections: z.array(sectionPermSchema),
|
|
servers: z.array(serverPermSchema),
|
|
bindings: z.array(userBindingSchema),
|
|
createdAt: z.string(),
|
|
updatedAt: z.string(),
|
|
})
|
|
|
|
export const appUserCreateSchema = z.object({
|
|
name: z.string().min(1),
|
|
login: z.string().min(1),
|
|
email: z.string().min(1),
|
|
role: appUserRoleSchema.default("viewer"),
|
|
active: z.boolean().default(true),
|
|
avatar: z.string().optional(),
|
|
lastSeen: z.string().nullable().optional(),
|
|
sections: z.array(sectionPermSchema).default([]),
|
|
servers: z.array(serverPermSchema).default([]),
|
|
})
|
|
|
|
export const appUserUpdateSchema = appUserCreateSchema.partial()
|
|
|
|
export const appUserIdParamSchema = z.object({
|
|
id: z.string().min(1),
|
|
})
|
|
|
|
export const bindingIdParamSchema = z.object({
|
|
id: z.string().min(1),
|
|
bindingId: z.string().min(1),
|
|
})
|
|
|
|
export const userBindingCreateSchema = z.object({
|
|
serverId: z.coerce.number().int().positive(),
|
|
interfaceName: z.string().min(1),
|
|
interfaceType: interfaceTypeSchema.optional(),
|
|
peerPublicKey: z.string().optional(),
|
|
peerName: z.string().optional(),
|
|
comment: z.string().optional(),
|
|
})
|
|
|
|
export const interfaceCatalogQuerySchema = z.object({
|
|
serverId: z.coerce.number().int().positive(),
|
|
})
|
|
|
|
export const catalogPeerSchema = z.object({
|
|
publicKey: z.string(),
|
|
name: z.string(),
|
|
comment: z.string(),
|
|
allowedIps: z.array(z.string()),
|
|
latestHandshake: z.string().optional(),
|
|
boundUserId: z.string().nullable(),
|
|
boundUserLogin: z.string().nullable(),
|
|
})
|
|
|
|
export const catalogInterfaceSchema = z.object({
|
|
name: z.string(),
|
|
type: interfaceTypeSchema,
|
|
running: z.boolean(),
|
|
disabled: z.boolean(),
|
|
boundUserId: z.string().nullable(),
|
|
boundUserLogin: z.string().nullable(),
|
|
peers: z.array(catalogPeerSchema).optional(),
|
|
peersError: z.string().optional(),
|
|
})
|
|
|
|
export const appUserListSchema = z.array(appUserReadSchema)
|
|
|
|
export type AppUserRole = z.infer<typeof appUserRoleSchema>
|
|
export type PermLevel = z.infer<typeof permLevelSchema>
|
|
export type InterfaceType = z.infer<typeof interfaceTypeSchema>
|
|
export type SectionPerm = z.infer<typeof sectionPermSchema>
|
|
export type ServerPerm = z.infer<typeof serverPermSchema>
|
|
export type UserBinding = z.infer<typeof userBindingSchema>
|
|
export type AppUserRead = z.infer<typeof appUserReadSchema>
|
|
export type AppUserCreate = z.infer<typeof appUserCreateSchema>
|
|
export type AppUserUpdate = z.infer<typeof appUserUpdateSchema>
|
|
export type UserBindingCreate = z.infer<typeof userBindingCreateSchema>
|
|
export type CatalogPeer = z.infer<typeof catalogPeerSchema>
|
|
export type CatalogInterface = z.infer<typeof catalogInterfaceSchema>
|
|
|
|
export const catalogInterfaceStateSchema = catalogInterfaceSchema.extend({
|
|
name: z.string().trim().min(1),
|
|
running: z.boolean().nullable(),
|
|
disabled: z.boolean().nullable(),
|
|
}).superRefine((iface, ctx) => {
|
|
if ((iface.type === "wg" || iface.type === "ipsec") && iface.peers === undefined && !iface.peersError) ctx.addIssue({ code: "custom", message: "Не подтверждён срез пиров." })
|
|
if (iface.peers && (iface.peers.some(peer => !peer.publicKey.trim()) || new Set(iface.peers.map(peer => peer.publicKey)).size !== iface.peers.length)) ctx.addIssue({ code: "custom", message: "Неоднозначные ключи пиров." })
|
|
})
|
|
export const interfaceCatalogStateSchema = z.object({
|
|
serverId: z.number().int().positive(),
|
|
source: z.enum(["snapshot", "traffic-history", "unknown"]),
|
|
sampledAt: z.string().refine(value => Number.isFinite(Date.parse(value)), "Некорректное время среза.").nullable(),
|
|
inventoryComplete: z.boolean(),
|
|
interfaces: z.array(catalogInterfaceStateSchema).refine(rows => new Set(rows.map(row => row.name)).size === rows.length, "Повторяющиеся интерфейсы."),
|
|
failures: z.array(z.object({ resource: z.enum(["interfaces", "wireguard", "ipsec"]), error: z.string().min(1) })),
|
|
}).superRefine((state, ctx) => {
|
|
if (state.inventoryComplete !== (state.source === "snapshot") || state.source === "unknown" && state.sampledAt !== null) ctx.addIssue({ code: "custom", message: "Противоречивые метаданные каталога." })
|
|
})
|
|
export type CatalogInterfaceState = z.infer<typeof catalogInterfaceStateSchema>
|
|
export type InterfaceCatalogState = z.infer<typeof interfaceCatalogStateSchema>
|