feat(admin): журналы входов и изменений с IP/UA и сессиями
Разделены экраны «Входы» и «Изменения»; логин пишет IP/UA и last_login_ip; SSO handoff и revoke refresh-сессий; улучшены audit-карточки. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -29,6 +29,7 @@ export const AUDIT_ACTIONS = [
|
||||
'auth.login',
|
||||
'auth.login_failed',
|
||||
'auth.logout',
|
||||
'auth.sso_handoff',
|
||||
'user.create',
|
||||
'user.update',
|
||||
'user.delete',
|
||||
@@ -40,6 +41,17 @@ export const AUDIT_ACTIONS = [
|
||||
export type AuditAction = (typeof AUDIT_ACTIONS)[number]
|
||||
export const auditActionSchema = z.enum(AUDIT_ACTIONS)
|
||||
|
||||
export const AUDIT_KINDS = ['logins', 'changes'] as const
|
||||
export type AuditKind = (typeof AUDIT_KINDS)[number]
|
||||
export const auditKindSchema = z.enum(AUDIT_KINDS)
|
||||
|
||||
export const AUTH_AUDIT_ACTIONS = [
|
||||
'auth.login',
|
||||
'auth.login_failed',
|
||||
'auth.logout',
|
||||
'auth.sso_handoff',
|
||||
] as const
|
||||
|
||||
export const auditLogEntrySchema = z.object({
|
||||
id: z.string(),
|
||||
event_id: z.string().nullable(),
|
||||
@@ -62,7 +74,9 @@ export const auditListQuerySchema = z.object({
|
||||
action: z.string().optional(),
|
||||
severity: auditSeveritySchema.optional(),
|
||||
user_id: z.string().optional(),
|
||||
actor_email: z.string().email().optional(),
|
||||
source_app: auditSourceAppSchema.optional(),
|
||||
kind: auditKindSchema.optional(),
|
||||
limit: z.coerce.number().int().min(1).max(500).default(200),
|
||||
})
|
||||
export type AuditListQuery = z.infer<typeof auditListQuerySchema>
|
||||
|
||||
@@ -371,11 +371,29 @@ export const adminUserSchema = z.object({
|
||||
apps: z.array(appIdSchema),
|
||||
permissions: z.array(z.string()),
|
||||
last_login_at: z.string().nullable(),
|
||||
last_login_ip: z.string().nullable(),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
})
|
||||
export type AdminUser = z.infer<typeof adminUserSchema>
|
||||
|
||||
export const adminSessionSchema = z.object({
|
||||
id: z.string(),
|
||||
user_id: z.string(),
|
||||
email: z.string().email(),
|
||||
name: z.string(),
|
||||
ip: z.string().nullable(),
|
||||
user_agent: z.string().nullable(),
|
||||
created_at: z.string(),
|
||||
expires_at: z.string(),
|
||||
})
|
||||
export type AdminSession = z.infer<typeof adminSessionSchema>
|
||||
|
||||
export const ssoAccessRequestSchema = z.object({
|
||||
return_to: z.string().url(),
|
||||
})
|
||||
export type SsoAccessRequest = z.infer<typeof ssoAccessRequestSchema>
|
||||
|
||||
export const createUserRequestSchema = z.object({
|
||||
email: z.string().email(),
|
||||
name: z.string().min(1),
|
||||
|
||||
Reference in New Issue
Block a user