feat(admin): ingest аудита из apps и users 1:1 с Sheet журнала
Добавлен POST /api/v1/ingest/audit, фильтры source_app/user_id, last_login_at; таблица пользователей по solution-users-1 с журналом в Sheet. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -4,16 +4,27 @@ 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)
|
||||
|
||||
/** Well-known action keys written by the API. */
|
||||
/** Well-known portal-native action keys. */
|
||||
export const AUDIT_ACTIONS = [
|
||||
'auth.login',
|
||||
'auth.login_failed',
|
||||
@@ -31,6 +42,8 @@ export const auditActionSchema = z.enum(AUDIT_ACTIONS)
|
||||
|
||||
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(),
|
||||
@@ -48,6 +61,8 @@ 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>
|
||||
@@ -73,3 +88,31 @@ export const auditPurgeResponseSchema = z.object({
|
||||
retention_days: z.number().int(),
|
||||
})
|
||||
export type AuditPurgeResponse = z.infer<typeof auditPurgeResponseSchema>
|
||||
|
||||
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>
|
||||
|
||||
export const ingestAuditRequestSchema = z.object({
|
||||
events: z.array(ingestAuditEventSchema).min(1).max(50),
|
||||
})
|
||||
export type IngestAuditRequest = z.infer<typeof ingestAuditRequestSchema>
|
||||
|
||||
export const ingestAuditResponseSchema = z.object({
|
||||
accepted: z.number().int().nonnegative(),
|
||||
duplicates: z.number().int().nonnegative(),
|
||||
})
|
||||
export type IngestAuditResponse = z.infer<typeof ingestAuditResponseSchema>
|
||||
|
||||
@@ -370,6 +370,7 @@ export const adminUserSchema = z.object({
|
||||
disabled: z.boolean(),
|
||||
apps: z.array(appIdSchema),
|
||||
permissions: z.array(z.string()),
|
||||
last_login_at: z.string().nullable(),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user