From e7af415a1f1e720ed064ffc7f47622e55f3cbb69 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Fri, 19 Jun 2026 21:15:13 +0200 Subject: [PATCH] feat(core) : add audit log consultation tab in admin gated by permission --- frontend/components/admin/AdminAuditTab.vue | 158 +++++++++++++++++++ frontend/i18n/locales/fr.json | 30 ++++ frontend/modules/core/services/audit-logs.ts | 63 ++++++++ frontend/pages/admin.vue | 3 + 4 files changed, 254 insertions(+) create mode 100644 frontend/components/admin/AdminAuditTab.vue create mode 100644 frontend/modules/core/services/audit-logs.ts diff --git a/frontend/components/admin/AdminAuditTab.vue b/frontend/components/admin/AdminAuditTab.vue new file mode 100644 index 0000000..351144d --- /dev/null +++ b/frontend/components/admin/AdminAuditTab.vue @@ -0,0 +1,158 @@ + + + diff --git a/frontend/i18n/locales/fr.json b/frontend/i18n/locales/fr.json index 321bfed..6d3f47e 100644 --- a/frontend/i18n/locales/fr.json +++ b/frontend/i18n/locales/fr.json @@ -214,6 +214,36 @@ "created": "Rôle créé avec succès.", "updated": "Rôle mis à jour avec succès.", "deleted": "Rôle supprimé avec succès." + }, + "audit": { + "title": "Audit", + "empty": "Aucune entrée d'audit trouvée.", + "date": "Date", + "performedBy": "Utilisateur", + "entityType": "Type d'entité", + "action": "Action", + "entityId": "Identifiant", + "filterEntityType": "Type d'entité", + "filterEntityTypeAll": "Tous les types", + "filterAction": "Action", + "filterActionAll": "Toutes les actions", + "previous": "Précédent", + "next": "Suivant", + "page": "Page {page}" + } + }, + "audit": { + "entity": { + "core": { + "User": "Utilisateur", + "Role": "Rôle", + "Permission": "Permission" + } + }, + "action": { + "create": "Création", + "update": "Modification", + "delete": "Suppression" } }, "timeEntries": { diff --git a/frontend/modules/core/services/audit-logs.ts b/frontend/modules/core/services/audit-logs.ts new file mode 100644 index 0000000..832bc7b --- /dev/null +++ b/frontend/modules/core/services/audit-logs.ts @@ -0,0 +1,63 @@ +import type { HydraCollection } from '~/utils/api' +import { extractHydraMembers } from '~/utils/api' + +export type AuditLogAction = 'create' | 'update' | 'delete' + +export type AuditLogItem = { + id: string + '@id'?: string + entityType: string + entityId: string + action: AuditLogAction + changes: Record + performedBy: string + performedAt: string + ipAddress: string | null + requestId: string | null +} + +export type AuditLogQuery = { + page?: number + entityType?: string + action?: AuditLogAction +} + +export type AuditLogPage = { + items: AuditLogItem[] + totalItems: number +} + +export type AuditLogEntityType = { + '@id'?: string + value: string +} + +export function useAuditLogService() { + const api = useApi() + + async function list(params: AuditLogQuery = {}): Promise { + const query: Record = {} + if (params.page !== undefined) { + query.page = params.page + } + if (params.entityType) { + query.entity_type = params.entityType + } + if (params.action) { + query.action = params.action + } + + const data = await api.get>('/audit-logs', query) + return { + items: extractHydraMembers(data), + totalItems: data['hydra:totalItems'] ?? data['totalItems'] ?? 0, + } + } + + async function entityTypes(): Promise { + const data = await api.get>('/audit-log-entity-types') + return extractHydraMembers(data).map((entry) => entry.value) + } + + return { list, entityTypes } +} diff --git a/frontend/pages/admin.vue b/frontend/pages/admin.vue index f371f09..668db4e 100644 --- a/frontend/pages/admin.vue +++ b/frontend/pages/admin.vue @@ -28,6 +28,7 @@ + @@ -46,6 +47,7 @@ const { can } = usePermissions() const { t } = useI18n() const canViewRoles = computed(() => can('core.roles.view')) +const canViewAudit = computed(() => can('core.audit_log.view')) const tabs = [ { key: 'clients', label: 'Clients' }, @@ -55,6 +57,7 @@ const tabs = [ { key: 'tags', label: 'Tags' }, { key: 'users', label: 'Utilisateurs' }, { key: 'roles', label: t('admin.roles.title'), permission: 'core.roles.view' }, + { key: 'audit', label: t('admin.audit.title'), permission: 'core.audit_log.view' }, { key: 'gitea', label: 'Gitea' }, { key: 'bookstack', label: 'BookStack' }, { key: 'zimbra', label: 'Zimbra' },