feat(audit): локальный журнал и push в auth-portal
Build and Push CFDM Docker Image / build-and-push (push) Successful in 2m9s
Build and Push CFDM Docker Image / create-release (push) Skipped
Build and Push CFDM Docker Image / update-wiki (push) Successful in 5s

Таблица audit_log, recordAudit на CRUD, GET /api/v1/audit и dual-write source_app=cfdm.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-07-21 13:24:32 +07:00
co-authored by Cursor
parent 30447d8380
commit 859ad23006
22 changed files with 1561 additions and 106 deletions
+109 -1
View File
@@ -1503,4 +1503,112 @@ declare const vpsTrackerEventSchema: z.ZodObject<{
}, z.core.$strip>;
type VpsTrackerEvent = z.infer<typeof vpsTrackerEventSchema>;
export { type AppSettingsPatch, type AppSwitcherConfig, type AppSwitcherEntry, type BulkUpdateDomainsInput, CERT_ERROR, CERT_EXPIRED, CERT_MONITORING_VALUES, CERT_MONITOR_AUTO, CERT_MONITOR_REQUIRED, CERT_MONITOR_SKIPPED, CERT_OK, CERT_UNKNOWN, CERT_WARNING, type CertMonitoring, type Certificate, type CfDnsRecord, type CfZone, type CfdmBindingSyncItem, type CreateDnsRecordInput, type CreateDnsRecordPayload, type CreateDomainInput, type CreateDomainMonitorInput, type CreateGroupInput, type CreateServiceBindingInput, type CreateServiceGroupInput, type CreateServiceInput, type CreateServiceWithConfigInput, type CreateSubdomainInput, type DnsRecord, type Domain, type DomainEnvironment, type DomainListItem, type DomainMonitor, type DomainMonitorResult, type DomainMonitorType, type Group, type GroupWithStats, type HealthCheckConfig, type HealthCheckScope, type HealthCheckTarget, type HealthCheckType, type HealthStatusQuery, type IpHealthState, type IpHealthStatus, type JwtClaims, type LbMode, type LoginInput, type LoginRequest, type LoginResponse, type NotificationLog, type ParsedFqdn, type ReorderServicesInput, SYNC_CONFLICT, SYNC_ERROR, SYNC_PENDING_DELETE, SYNC_PENDING_PUSH, SYNC_SYNCED, type Service, type ServiceBinding, type ServiceBindingView, type ServiceDomainBinding, type ServiceDomainBindingView, type ServiceGroup, type ServiceGroupView, type ServiceGroupsResponse, type ServiceView, type Subdomain, type SubdomainRecord, type SyncJob, type ToggleEnabledInput, type UpdateDomainInput, type UpdateServiceConfigInput, type UpdateServiceGroupInput, type UpdateSubdomainInput, ValidationError, type VpsTrackerEvent, appSettingsPatchSchema, appSwitcherConfigSchema, appSwitcherEntrySchema, appSwitcherIconSchema, bindingToFqdn, bulkUpdateDomainsSchema, certMonitoringSchema, certStatusFromExpiry, certificateSchema, cfdmBindingSyncItemSchema, cfdmSyncBindingsBodySchema, createDnsRecordSchema, createDomainMonitorSchema, createDomainSchema, createGroupSchema, createServiceBindingSchema, createServiceGroupSchema, createServiceSchema, createServiceWithConfigSchema, createSubdomainSchema, dnsNameToSubdomainLabel, dnsRecordNamesMatch, dnsRecordSchema, domainEnvironmentSchema, domainListItemSchema, domainMonitorResultSchema, domainMonitorSchema, domainMonitorTypeSchema, domainSchema, fqdnToDisplay, groupSchema, groupWithStatsSchema, healthCheckConfigSchema, healthCheckScopeSchema, healthCheckTypeSchema, healthStatusQuerySchema, ipHealthStateSchema, ipHealthStatusSchema, isValidIpv4, lbModeSchema, loginSchema, normalizeDnsRecordName, notificationLogSchema, parseFqdn, reorderServicesSchema, serviceBindingSchema, serviceDomainBindingSchema, serviceGroupSchema, serviceGroupTypeSchema, serviceGroupViewSchema, serviceGroupsResponseSchema, serviceSchema, serviceViewSchema, shouldMonitorService, subdomainLabelToFqdn, subdomainSchema, toggleEnabledSchema, updateDomainGroupSchema, updateDomainSchema, updateServiceConfigSchema, updateServiceGroupSchema, updateSubdomainSchema, validateDnsRecord, vpsTrackerEventSchema };
declare const AUDIT_SEVERITIES: readonly ["info", "warning", "critical"];
type AuditSeverity = (typeof AUDIT_SEVERITIES)[number];
declare const auditSeveritySchema: z.ZodEnum<{
warning: "warning";
info: "info";
critical: "critical";
}>;
declare const AUDIT_SOURCE_APPS: readonly ["portal", "vps", "cfdm", "bgp", "fw"];
type AuditSourceApp = (typeof AUDIT_SOURCE_APPS)[number];
declare const auditSourceAppSchema: z.ZodEnum<{
bgp: "bgp";
vps: "vps";
portal: "portal";
cfdm: "cfdm";
fw: "fw";
}>;
declare const AUDIT_TARGET_TYPES: readonly ["user", "settings", "session", "system", "app_resource"];
type AuditTargetType = (typeof AUDIT_TARGET_TYPES)[number];
declare const auditTargetTypeSchema: z.ZodEnum<{
user: "user";
settings: "settings";
session: "session";
system: "system";
app_resource: "app_resource";
}>;
declare const auditLogEntrySchema: z.ZodObject<{
id: z.ZodString;
event_id: z.ZodNullable<z.ZodString>;
source_app: z.ZodEnum<{
bgp: "bgp";
vps: "vps";
portal: "portal";
cfdm: "cfdm";
fw: "fw";
}>;
action: z.ZodString;
severity: z.ZodEnum<{
warning: "warning";
info: "info";
critical: "critical";
}>;
actor_user_id: z.ZodNullable<z.ZodString>;
actor_email: z.ZodNullable<z.ZodString>;
actor_name: z.ZodNullable<z.ZodString>;
target_type: z.ZodNullable<z.ZodEnum<{
user: "user";
settings: "settings";
session: "session";
system: "system";
app_resource: "app_resource";
}>>;
target_id: z.ZodNullable<z.ZodString>;
summary: z.ZodString;
details: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
ip: z.ZodNullable<z.ZodString>;
created_at: z.ZodString;
}, z.core.$strip>;
type AuditLogEntry = z.infer<typeof auditLogEntrySchema>;
declare const auditListQuerySchema: z.ZodObject<{
action: z.ZodOptional<z.ZodString>;
severity: z.ZodOptional<z.ZodEnum<{
warning: "warning";
info: "info";
critical: "critical";
}>>;
user_id: z.ZodOptional<z.ZodString>;
source_app: z.ZodOptional<z.ZodEnum<{
bgp: "bgp";
vps: "vps";
portal: "portal";
cfdm: "cfdm";
fw: "fw";
}>>;
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
}, z.core.$strip>;
type AuditListQuery = z.infer<typeof auditListQuerySchema>;
declare const ingestAuditEventSchema: z.ZodObject<{
event_id: z.ZodString;
source_app: z.ZodEnum<{
bgp: "bgp";
vps: "vps";
cfdm: "cfdm";
fw: "fw";
}>;
action: z.ZodString;
severity: z.ZodOptional<z.ZodEnum<{
warning: "warning";
info: "info";
critical: "critical";
}>>;
actor_user_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
actor_email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
actor_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
target_type: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
user: "user";
settings: "settings";
session: "session";
system: "system";
app_resource: "app_resource";
}>>>;
target_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
summary: z.ZodString;
details: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
ip: z.ZodOptional<z.ZodNullable<z.ZodString>>;
created_at: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
type IngestAuditEvent = z.infer<typeof ingestAuditEventSchema>;
export { AUDIT_SEVERITIES, AUDIT_SOURCE_APPS, AUDIT_TARGET_TYPES, type AppSettingsPatch, type AppSwitcherConfig, type AppSwitcherEntry, type AuditListQuery, type AuditLogEntry, type AuditSeverity, type AuditSourceApp, type AuditTargetType, type BulkUpdateDomainsInput, CERT_ERROR, CERT_EXPIRED, CERT_MONITORING_VALUES, CERT_MONITOR_AUTO, CERT_MONITOR_REQUIRED, CERT_MONITOR_SKIPPED, CERT_OK, CERT_UNKNOWN, CERT_WARNING, type CertMonitoring, type Certificate, type CfDnsRecord, type CfZone, type CfdmBindingSyncItem, type CreateDnsRecordInput, type CreateDnsRecordPayload, type CreateDomainInput, type CreateDomainMonitorInput, type CreateGroupInput, type CreateServiceBindingInput, type CreateServiceGroupInput, type CreateServiceInput, type CreateServiceWithConfigInput, type CreateSubdomainInput, type DnsRecord, type Domain, type DomainEnvironment, type DomainListItem, type DomainMonitor, type DomainMonitorResult, type DomainMonitorType, type Group, type GroupWithStats, type HealthCheckConfig, type HealthCheckScope, type HealthCheckTarget, type HealthCheckType, type HealthStatusQuery, type IngestAuditEvent, type IpHealthState, type IpHealthStatus, type JwtClaims, type LbMode, type LoginInput, type LoginRequest, type LoginResponse, type NotificationLog, type ParsedFqdn, type ReorderServicesInput, SYNC_CONFLICT, SYNC_ERROR, SYNC_PENDING_DELETE, SYNC_PENDING_PUSH, SYNC_SYNCED, type Service, type ServiceBinding, type ServiceBindingView, type ServiceDomainBinding, type ServiceDomainBindingView, type ServiceGroup, type ServiceGroupView, type ServiceGroupsResponse, type ServiceView, type Subdomain, type SubdomainRecord, type SyncJob, type ToggleEnabledInput, type UpdateDomainInput, type UpdateServiceConfigInput, type UpdateServiceGroupInput, type UpdateSubdomainInput, ValidationError, type VpsTrackerEvent, appSettingsPatchSchema, appSwitcherConfigSchema, appSwitcherEntrySchema, appSwitcherIconSchema, auditListQuerySchema, auditLogEntrySchema, auditSeveritySchema, auditSourceAppSchema, auditTargetTypeSchema, bindingToFqdn, bulkUpdateDomainsSchema, certMonitoringSchema, certStatusFromExpiry, certificateSchema, cfdmBindingSyncItemSchema, cfdmSyncBindingsBodySchema, createDnsRecordSchema, createDomainMonitorSchema, createDomainSchema, createGroupSchema, createServiceBindingSchema, createServiceGroupSchema, createServiceSchema, createServiceWithConfigSchema, createSubdomainSchema, dnsNameToSubdomainLabel, dnsRecordNamesMatch, dnsRecordSchema, domainEnvironmentSchema, domainListItemSchema, domainMonitorResultSchema, domainMonitorSchema, domainMonitorTypeSchema, domainSchema, fqdnToDisplay, groupSchema, groupWithStatsSchema, healthCheckConfigSchema, healthCheckScopeSchema, healthCheckTypeSchema, healthStatusQuerySchema, ingestAuditEventSchema, ipHealthStateSchema, ipHealthStatusSchema, isValidIpv4, lbModeSchema, loginSchema, normalizeDnsRecordName, notificationLogSchema, parseFqdn, reorderServicesSchema, serviceBindingSchema, serviceDomainBindingSchema, serviceGroupSchema, serviceGroupTypeSchema, serviceGroupViewSchema, serviceGroupsResponseSchema, serviceSchema, serviceViewSchema, shouldMonitorService, subdomainLabelToFqdn, subdomainSchema, toggleEnabledSchema, updateDomainGroupSchema, updateDomainSchema, updateServiceConfigSchema, updateServiceGroupSchema, updateSubdomainSchema, validateDnsRecord, vpsTrackerEventSchema };
+68
View File
@@ -601,7 +601,69 @@ var vpsTrackerEventSchema = z3.object({
),
timestamp: z3.string().datetime().optional()
});
// src/audit.ts
import { z as z4 } from "zod";
var AUDIT_SEVERITIES = ["info", "warning", "critical"];
var auditSeveritySchema = z4.enum(AUDIT_SEVERITIES);
var AUDIT_SOURCE_APPS = [
"portal",
"vps",
"cfdm",
"bgp",
"fw"
];
var auditSourceAppSchema = z4.enum(AUDIT_SOURCE_APPS);
var AUDIT_TARGET_TYPES = [
"user",
"settings",
"session",
"system",
"app_resource"
];
var auditTargetTypeSchema = z4.enum(AUDIT_TARGET_TYPES);
var auditLogEntrySchema = z4.object({
id: z4.string(),
event_id: z4.string().nullable(),
source_app: auditSourceAppSchema,
action: z4.string(),
severity: auditSeveritySchema,
actor_user_id: z4.string().nullable(),
actor_email: z4.string().nullable(),
actor_name: z4.string().nullable(),
target_type: auditTargetTypeSchema.nullable(),
target_id: z4.string().nullable(),
summary: z4.string(),
details: z4.record(z4.string(), z4.unknown()).nullable(),
ip: z4.string().nullable(),
created_at: z4.string()
});
var auditListQuerySchema = z4.object({
action: z4.string().optional(),
severity: auditSeveritySchema.optional(),
user_id: z4.string().optional(),
source_app: auditSourceAppSchema.optional(),
limit: z4.coerce.number().int().min(1).max(500).default(200)
});
var ingestAuditEventSchema = z4.object({
event_id: z4.string().min(1).max(128),
source_app: z4.enum(["vps", "cfdm", "bgp", "fw"]),
action: z4.string().min(1).max(200),
severity: auditSeveritySchema.optional(),
actor_user_id: z4.string().nullable().optional(),
actor_email: z4.string().email().nullable().optional(),
actor_name: z4.string().nullable().optional(),
target_type: auditTargetTypeSchema.nullable().optional(),
target_id: z4.string().nullable().optional(),
summary: z4.string().min(1).max(500),
details: z4.record(z4.string(), z4.unknown()).nullable().optional(),
ip: z4.string().nullable().optional(),
created_at: z4.string().optional()
});
export {
AUDIT_SEVERITIES,
AUDIT_SOURCE_APPS,
AUDIT_TARGET_TYPES,
CERT_ERROR,
CERT_EXPIRED,
CERT_MONITORING_VALUES,
@@ -621,6 +683,11 @@ export {
appSwitcherConfigSchema,
appSwitcherEntrySchema,
appSwitcherIconSchema,
auditListQuerySchema,
auditLogEntrySchema,
auditSeveritySchema,
auditSourceAppSchema,
auditTargetTypeSchema,
bindingToFqdn,
bulkUpdateDomainsSchema,
certMonitoringSchema,
@@ -653,6 +720,7 @@ export {
healthCheckScopeSchema,
healthCheckTypeSchema,
healthStatusQuerySchema,
ingestAuditEventSchema,
ipHealthStateSchema,
ipHealthStatusSchema,
isValidIpv4,
+69
View File
@@ -0,0 +1,69 @@
import { z } from "zod";
export const AUDIT_SEVERITIES = ["info", "warning", "critical"] as const;
export type AuditSeverity = (typeof AUDIT_SEVERITIES)[number];
export const auditSeveritySchema = z.enum(AUDIT_SEVERITIES);
export const AUDIT_SOURCE_APPS = [
"portal",
"vps",
"cfdm",
"bgp",
"fw",
] as const;
export type AuditSourceApp = (typeof AUDIT_SOURCE_APPS)[number];
export const auditSourceAppSchema = z.enum(AUDIT_SOURCE_APPS);
export const AUDIT_TARGET_TYPES = [
"user",
"settings",
"session",
"system",
"app_resource",
] as const;
export type AuditTargetType = (typeof AUDIT_TARGET_TYPES)[number];
export const auditTargetTypeSchema = z.enum(AUDIT_TARGET_TYPES);
export const auditLogEntrySchema = z.object({
id: z.string(),
event_id: z.string().nullable(),
source_app: auditSourceAppSchema,
action: z.string(),
severity: auditSeveritySchema,
actor_user_id: z.string().nullable(),
actor_email: z.string().nullable(),
actor_name: z.string().nullable(),
target_type: auditTargetTypeSchema.nullable(),
target_id: z.string().nullable(),
summary: z.string(),
details: z.record(z.string(), z.unknown()).nullable(),
ip: z.string().nullable(),
created_at: z.string(),
});
export type AuditLogEntry = z.infer<typeof auditLogEntrySchema>;
export const auditListQuerySchema = z.object({
action: z.string().optional(),
severity: auditSeveritySchema.optional(),
user_id: z.string().optional(),
source_app: auditSourceAppSchema.optional(),
limit: z.coerce.number().int().min(1).max(500).default(200),
});
export type AuditListQuery = z.infer<typeof auditListQuerySchema>;
export const ingestAuditEventSchema = z.object({
event_id: z.string().min(1).max(128),
source_app: z.enum(["vps", "cfdm", "bgp", "fw"]),
action: z.string().min(1).max(200),
severity: auditSeveritySchema.optional(),
actor_user_id: z.string().nullable().optional(),
actor_email: z.string().email().nullable().optional(),
actor_name: z.string().nullable().optional(),
target_type: auditTargetTypeSchema.nullable().optional(),
target_id: z.string().nullable().optional(),
summary: z.string().min(1).max(500),
details: z.record(z.string(), z.unknown()).nullable().optional(),
ip: z.string().nullable().optional(),
created_at: z.string().optional(),
});
export type IngestAuditEvent = z.infer<typeof ingestAuditEventSchema>;
+1
View File
@@ -5,6 +5,7 @@ export * from "./parse-fqdn.js";
export * from "./schemas.js";
export * from "./app-switcher.js";
export * from "./integration-vps-tracker.js";
export * from "./audit.js";
export type {
CfZone,
CfDnsRecord,