feat(audit): полноценный writer в audit_log + admin UI (audit + outbox DLQ)
До этого audit_log entity была определена, но никто туда не писал — реальный аудит шёл только через Hibernate Envers (без user/IP/trace). Теперь: - AuditLogger service пишет в той же транзакции что и CRUD (DictionaryRecordService). Захватывает: user (JWT sub либо preferred_username), scope, IP (X-Forwarded-For), UA, trace_id (MDC), request_id. Не ломает основной flow при сбое — только громко логирует. - AuditLogRepository: гибкий search() с nullable фильтрами (dictionary, user, action, businessKey, from, to) + Pageable. - AuditAdminController: GET /admin/audit (paginated) + /admin/audit/export.csv (cap 10k строк против OOM). Frontend: - /audit route: фильтр-панель, таблица с цветными бейджами (CREATE/UPDATE/CLOSE), expand-row с JSON before/after diff, footer IP/UA/req_id, кнопка экспорта CSV. Pagination 50/100/200. - /outbox route: stat-cards pending/DLQ + DLQ-таблица с last_error. - Nav links + i18n (RU + EN).
This commit is contained in:
@@ -9,16 +9,28 @@
|
||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||
|
||||
import { Route as rootRouteImport } from './routes/__root'
|
||||
import { Route as OutboxRouteImport } from './routes/outbox'
|
||||
import { Route as DictionariesRouteImport } from './routes/dictionaries'
|
||||
import { Route as AuditRouteImport } from './routes/audit'
|
||||
import { Route as IndexRouteImport } from './routes/index'
|
||||
import { Route as DictionariesIndexRouteImport } from './routes/dictionaries.index'
|
||||
import { Route as DictionariesNameRouteImport } from './routes/dictionaries.$name'
|
||||
|
||||
const OutboxRoute = OutboxRouteImport.update({
|
||||
id: '/outbox',
|
||||
path: '/outbox',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const DictionariesRoute = DictionariesRouteImport.update({
|
||||
id: '/dictionaries',
|
||||
path: '/dictionaries',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const AuditRoute = AuditRouteImport.update({
|
||||
id: '/audit',
|
||||
path: '/audit',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const IndexRoute = IndexRouteImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
@@ -37,42 +49,65 @@ const DictionariesNameRoute = DictionariesNameRouteImport.update({
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'/audit': typeof AuditRoute
|
||||
'/dictionaries': typeof DictionariesRouteWithChildren
|
||||
'/outbox': typeof OutboxRoute
|
||||
'/dictionaries/$name': typeof DictionariesNameRoute
|
||||
'/dictionaries/': typeof DictionariesIndexRoute
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/audit': typeof AuditRoute
|
||||
'/outbox': typeof OutboxRoute
|
||||
'/dictionaries/$name': typeof DictionariesNameRoute
|
||||
'/dictionaries': typeof DictionariesIndexRoute
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
'/': typeof IndexRoute
|
||||
'/audit': typeof AuditRoute
|
||||
'/dictionaries': typeof DictionariesRouteWithChildren
|
||||
'/outbox': typeof OutboxRoute
|
||||
'/dictionaries/$name': typeof DictionariesNameRoute
|
||||
'/dictionaries/': typeof DictionariesIndexRoute
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fullPaths: '/' | '/dictionaries' | '/dictionaries/$name' | '/dictionaries/'
|
||||
fullPaths:
|
||||
| '/'
|
||||
| '/audit'
|
||||
| '/dictionaries'
|
||||
| '/outbox'
|
||||
| '/dictionaries/$name'
|
||||
| '/dictionaries/'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to: '/' | '/dictionaries/$name' | '/dictionaries'
|
||||
to: '/' | '/audit' | '/outbox' | '/dictionaries/$name' | '/dictionaries'
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
| '/audit'
|
||||
| '/dictionaries'
|
||||
| '/outbox'
|
||||
| '/dictionaries/$name'
|
||||
| '/dictionaries/'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
AuditRoute: typeof AuditRoute
|
||||
DictionariesRoute: typeof DictionariesRouteWithChildren
|
||||
OutboxRoute: typeof OutboxRoute
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface FileRoutesByPath {
|
||||
'/outbox': {
|
||||
id: '/outbox'
|
||||
path: '/outbox'
|
||||
fullPath: '/outbox'
|
||||
preLoaderRoute: typeof OutboxRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/dictionaries': {
|
||||
id: '/dictionaries'
|
||||
path: '/dictionaries'
|
||||
@@ -80,6 +115,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof DictionariesRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/audit': {
|
||||
id: '/audit'
|
||||
path: '/audit'
|
||||
fullPath: '/audit'
|
||||
preLoaderRoute: typeof AuditRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/': {
|
||||
id: '/'
|
||||
path: '/'
|
||||
@@ -120,7 +162,9 @@ const DictionariesRouteWithChildren = DictionariesRoute._addFileChildren(
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
AuditRoute: AuditRoute,
|
||||
DictionariesRoute: DictionariesRouteWithChildren,
|
||||
OutboxRoute: OutboxRoute,
|
||||
}
|
||||
export const routeTree = rootRouteImport
|
||||
._addFileChildren(rootRouteChildren)
|
||||
|
||||
Reference in New Issue
Block a user