1. replaceAll (Find & Replace) — une seule transaction ProseMirror au lieu d'un forEach cassé. Tous les matchs sont maintenant remplacés. 2. Link Preview unwrap — deleteNode() au lieu de clearer les attrs qui laissaient un nœud fantôme invisible dans le document. 3. Conversion Markdown → richtext — breaks: true dans marked.parse() Les simple newlines sont maintenant convertis en <br>. + préserve les blocs custom (toggle, callout, math, columns, outline, link-preview) en commentaires HTML lors de l'export MD. 4. emitNoteChange exercices — shape corrigée (type:'created' attend un objet Note, pas noteId/notebookId séparés). 5. Raccourcis clavier sans conflit : Cmd+Shift+C → Cmd+Alt+C (callout, avant: copier) Cmd+Shift+O → Cmd+Alt+O (outline, avant: historique/signets) Cmd+Shift+L → Cmd+Alt+L (colonnes, avant: lock screen macOS)
68 lines
1.9 KiB
TypeScript
68 lines
1.9 KiB
TypeScript
import prisma from '@/lib/prisma'
|
|
|
|
export type AuditAction =
|
|
| 'LOGIN'
|
|
| 'LOGOUT'
|
|
| 'USER_CREATED'
|
|
| 'AI_REQUEST'
|
|
| 'DATA_EXPORT'
|
|
| 'ACCOUNT_DELETED'
|
|
| 'PASSWORD_RESET'
|
|
| 'AI_CONSENT_GRANTED'
|
|
| 'AI_CONSENT_REVOKED'
|
|
| 'SUBSCRIPTION_OVERRIDE'
|
|
| 'BILLING_CONFIG_UPDATED'
|
|
| 'PLAN_ENTITLEMENT_UPDATED'
|
|
|
|
export interface AuditLogParams {
|
|
userId?: string | null
|
|
action: AuditAction
|
|
resource?: string
|
|
metadata?: Record<string, unknown>
|
|
ip?: string
|
|
userAgent?: string
|
|
}
|
|
|
|
/** Fire-and-forget — never throws, safe to call anywhere */
|
|
export function logAuditEvent(params: AuditLogParams): void {
|
|
prisma.auditLog
|
|
.create({
|
|
data: {
|
|
userId: params.userId ?? null,
|
|
action: params.action,
|
|
resource: params.resource ?? null,
|
|
metadata: params.metadata ? (params.metadata as any) : undefined,
|
|
ip: params.ip ?? null,
|
|
userAgent: params.userAgent ?? null,
|
|
},
|
|
})
|
|
.catch((err: unknown) => console.error('[audit-log] write failed:', err))
|
|
}
|
|
|
|
/** Awaitable version for cases where ordering matters */
|
|
export async function logAuditEventAsync(params: AuditLogParams): Promise<void> {
|
|
try {
|
|
await prisma.auditLog.create({
|
|
data: {
|
|
userId: params.userId ?? null,
|
|
action: params.action,
|
|
resource: params.resource ?? null,
|
|
metadata: params.metadata ? (params.metadata as any) : undefined,
|
|
ip: params.ip ?? null,
|
|
userAgent: params.userAgent ?? null,
|
|
},
|
|
})
|
|
} catch (err) {
|
|
console.error('[audit-log] write failed:', err)
|
|
}
|
|
}
|
|
|
|
/** Extract IP from request headers (works behind Cloudflare / nginx) */
|
|
export function getClientIp(request: Request): string | undefined {
|
|
const cf = request.headers.get('cf-connecting-ip')
|
|
if (cf) return cf
|
|
const xff = request.headers.get('x-forwarded-for')
|
|
if (xff) return xff.split(',')[0].trim()
|
|
return undefined
|
|
}
|