@mostajs/audit 2.0.2 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,654 +1,47 @@
1
1
  # @mostajs/audit
2
2
 
3
- > Reusable audit logging module — fire-and-forget `logAudit()` with paginated consultation.
3
+ > Fire-and-forget audit logging with paginated consultation.
4
+ > Author: Dr Hamid MADANI drmdh@msn.com
4
5
 
5
- [![npm version](https://img.shields.io/npm/v/@mostajs/audit.svg)](https://www.npmjs.com/package/@mostajs/audit)
6
- [![license](https://img.shields.io/npm/l/@mostajs/audit.svg)](LICENSE)
7
-
8
- Part of the [@mosta suite](https://mostajs.dev). Depend de `@mostajs/orm` pour l'abstraction multi-dialecte (MongoDB, PostgreSQL, MySQL, SQLite, etc.).
9
-
10
- ---
11
-
12
- ## Table des matieres
13
-
14
- 1. [Installation](#installation)
15
- 2. [Quick Start](#quick-start)
16
- 3. [Integration complete dans une nouvelle app](#integration-complete)
17
- 4. [logAudit — Journalisation fire-and-forget](#logaudit)
18
- 5. [getAuditUser — Extraction session](#getaudituser)
19
- 6. [AuditLogRepository — Requetes avancees](#auditlogrepository)
20
- 7. [createAuditHandlers — Route API factory](#createaudithandlers)
21
- 8. [Schema et indexes](#schema-et-indexes)
22
- 9. [Cas d'usage courants](#cas-dusage-courants)
23
- 10. [API Reference](#api-reference)
24
- 11. [Architecture](#architecture)
25
-
26
- ---
27
-
28
- ## Installation
6
+ ## Install
29
7
 
30
8
  ```bash
31
9
  npm install @mostajs/audit @mostajs/orm
32
10
  ```
33
11
 
34
- `@mostajs/orm` est la seule dependance requise. Elle gere la connexion DB et les operations CRUD quel que soit le dialecte (MongoDB, PostgreSQL, MySQL, SQLite, etc.).
12
+ ## How to Use
35
13
 
36
- ---
37
-
38
- ## Quick Start
14
+ ### 1. Log an Action
39
15
 
40
16
  ```typescript
41
- import { logAudit } from '@mostajs/audit'
17
+ import { logAudit, getAuditUser } from '@mostajs/audit'
42
18
 
43
- // Fire-and-forget — ne throw jamais, ne bloque jamais
44
19
  await logAudit({
45
- userId: '507f1f77bcf86cd799439011',
46
- userName: 'Dr Madani',
47
- userRole: 'admin',
48
- action: 'create',
20
+ ...getAuditUser(session),
21
+ action: 'user_create',
49
22
  module: 'users',
50
- resource: 'User',
51
- resourceId: 'abc123',
52
- details: { email: 'new@example.com' },
53
- })
54
- ```
55
-
56
- C'est tout. Le schema est auto-enregistre dans l'ORM, la table/collection est creee automatiquement.
57
-
58
- ---
59
-
60
- ## Integration complete
61
-
62
- Guide pas-a-pas pour integrer `@mostajs/audit` dans une nouvelle application Next.js.
63
-
64
- ### Etape 1 — Installer les packages
65
-
66
- ```bash
67
- npm install @mostajs/audit @mostajs/orm
68
- ```
69
-
70
- ### Etape 2 — Configurer la connexion DB
71
-
72
- L'ORM doit etre configure avant d'utiliser audit. Dans votre `.env.local` :
73
-
74
- ```env
75
- DATABASE_URL=mongodb://localhost:27017/myapp
76
- # ou: DATABASE_URL=postgres://user:pass@localhost:5432/myapp
77
- # ou: DATABASE_URL=sqlite:./data/myapp.db
78
- ```
79
-
80
- Initialiser l'ORM (une seule fois, au demarrage) :
81
-
82
- ```typescript
83
- // src/lib/db.ts
84
- import { initDialect } from '@mostajs/orm'
85
-
86
- let initialized = false
87
-
88
- export async function ensureDB() {
89
- if (initialized) return
90
- await initDialect(process.env.DATABASE_URL!)
91
- initialized = true
92
- }
93
- ```
94
-
95
- ### Etape 3 — Utiliser logAudit dans vos routes API
96
-
97
- ```typescript
98
- // src/app/api/products/route.ts
99
- import { NextRequest, NextResponse } from 'next/server'
100
- import { logAudit, getAuditUser } from '@mostajs/audit/lib/audit'
101
- import { ensureDB } from '@/lib/db'
102
- import { getServerSession } from 'next-auth'
103
-
104
- export async function POST(req: NextRequest) {
105
- await ensureDB()
106
- const session = await getServerSession()
107
- if (!session) {
108
- return NextResponse.json({ error: 'Non autorise' }, { status: 401 })
109
- }
110
-
111
- const body = await req.json()
112
-
113
- // ... creer le produit dans la DB ...
114
- const product = { id: 'prod_123', name: body.name }
115
-
116
- // Journaliser l'action — fire-and-forget
117
- await logAudit({
118
- ...getAuditUser(session),
119
- action: 'create',
120
- module: 'products',
121
- resource: 'Product',
122
- resourceId: product.id,
123
- details: { name: product.name },
124
- })
125
-
126
- return NextResponse.json({ data: product })
127
- }
128
- ```
129
-
130
- ### Etape 4 — Route de consultation des logs
131
-
132
- ```typescript
133
- // src/app/api/admin/audit/route.ts
134
- import { createAuditHandlers } from '@mostajs/audit/api/route'
135
- import { checkPermission } from '@/lib/auth'
136
-
137
- export const { GET } = createAuditHandlers('audit:view', checkPermission)
138
- ```
139
-
140
- Cette route expose `GET /api/admin/audit` avec pagination et filtres automatiques.
141
-
142
- ### Etape 5 — Page d'administration (frontend)
143
-
144
- ```tsx
145
- 'use client'
146
- import { useState, useEffect } from 'react'
147
-
148
- interface AuditLog {
149
- id: string
150
- userName: string
151
- action: string
152
- module: string
153
- resource: string
154
- timestamp: string
155
- status: 'success' | 'failure'
156
- }
157
-
158
- export default function AuditPage() {
159
- const [logs, setLogs] = useState<AuditLog[]>([])
160
- const [meta, setMeta] = useState({ total: 0, page: 1, pages: 1 })
161
- const [module, setModule] = useState('')
162
-
163
- async function fetchLogs(page = 1) {
164
- const params = new URLSearchParams({ page: String(page), limit: '20' })
165
- if (module) params.set('module', module)
166
-
167
- const res = await fetch(`/api/admin/audit?${params}`)
168
- const json = await res.json()
169
- setLogs(json.data)
170
- setMeta(json.meta)
171
- }
172
-
173
- useEffect(() => { fetchLogs() }, [module])
174
-
175
- return (
176
- <div>
177
- <h1>Journal d'audit</h1>
178
-
179
- {/* Filtre par module */}
180
- <select value={module} onChange={e => setModule(e.target.value)}>
181
- <option value="">Tous les modules</option>
182
- <option value="users">Utilisateurs</option>
183
- <option value="products">Produits</option>
184
- <option value="orders">Commandes</option>
185
- </select>
186
-
187
- {/* Table des logs */}
188
- <table>
189
- <thead>
190
- <tr>
191
- <th>Date</th>
192
- <th>Utilisateur</th>
193
- <th>Action</th>
194
- <th>Module</th>
195
- <th>Ressource</th>
196
- <th>Statut</th>
197
- </tr>
198
- </thead>
199
- <tbody>
200
- {logs.map(log => (
201
- <tr key={log.id}>
202
- <td>{new Date(log.timestamp).toLocaleString()}</td>
203
- <td>{log.userName}</td>
204
- <td>{log.action}</td>
205
- <td>{log.module}</td>
206
- <td>{log.resource}</td>
207
- <td>{log.status}</td>
208
- </tr>
209
- ))}
210
- </tbody>
211
- </table>
212
-
213
- {/* Pagination */}
214
- <div>
215
- <button
216
- disabled={meta.page <= 1}
217
- onClick={() => fetchLogs(meta.page - 1)}
218
- >Precedent</button>
219
- <span>Page {meta.page} / {meta.pages} ({meta.total} total)</span>
220
- <button
221
- disabled={meta.page >= meta.pages}
222
- onClick={() => fetchLogs(meta.page + 1)}
223
- >Suivant</button>
224
- </div>
225
- </div>
226
- )
227
- }
228
- ```
229
-
230
- ### Etape 6 — Verification
231
-
232
- ```bash
233
- # Demarrer l'app
234
- npm run dev
235
-
236
- # Creer un produit (declenche un audit log)
237
- curl -X POST http://localhost:3000/api/products \
238
- -H 'Content-Type: application/json' \
239
- -d '{"name": "Widget"}'
240
-
241
- # Consulter les logs
242
- curl 'http://localhost:3000/api/admin/audit?limit=5'
243
- ```
244
-
245
- Reponse attendue :
246
-
247
- ```json
248
- {
249
- "data": [
250
- {
251
- "id": "...",
252
- "userName": "Dr Madani",
253
- "userRole": "admin",
254
- "action": "create",
255
- "module": "products",
256
- "resource": "Product",
257
- "resourceId": "prod_123",
258
- "details": { "name": "Widget" },
259
- "status": "success",
260
- "timestamp": "2026-03-05T14:30:00.000Z"
261
- }
262
- ],
263
- "meta": { "total": 1, "page": 1, "limit": 5, "pages": 1 }
264
- }
265
- ```
266
-
267
- ---
268
-
269
- ## logAudit
270
-
271
- ```typescript
272
- import { logAudit } from '@mostajs/audit/lib/audit'
273
- ```
274
-
275
- Journalise une action. **Fire-and-forget** : ne throw jamais, ne bloque jamais. Si l'ecriture echoue, l'erreur est loggee dans la console sans interrompre le flux.
276
-
277
- ```typescript
278
- await logAudit({
279
- userId: '507f1f77bcf86cd799439011',
280
- userName: 'Alice',
281
- userRole: 'manager',
282
- action: 'update',
283
- module: 'clients',
284
- resource: 'Client',
285
- resourceId: 'cli_456',
286
- details: { field: 'status', oldValue: 'active', newValue: 'suspended' },
287
- ipAddress: '192.168.1.100',
288
- status: 'success', // 'success' | 'failure' (defaut: 'success')
289
- })
290
- ```
291
-
292
- ### Parametres (AuditParams)
293
-
294
- | Champ | Type | Requis | Description |
295
- |-------|------|--------|-------------|
296
- | `userId` | `string` | oui | ID de l'utilisateur |
297
- | `userName` | `string` | oui | Nom affichable |
298
- | `userRole` | `string` | oui | Role (admin, manager, etc.) |
299
- | `action` | `string` | oui | Action effectuee (create, update, delete, login, etc.) |
300
- | `module` | `string` | oui | Module concerne (users, clients, lockers, etc.) |
301
- | `resource` | `string` | non | Type de ressource (User, Client, etc.) |
302
- | `resourceId` | `string` | non | ID de la ressource |
303
- | `details` | `Record<string, any>` | non | Donnees supplementaires |
304
- | `ipAddress` | `string` | non | Adresse IP du client |
305
- | `status` | `'success' \| 'failure'` | non | Resultat (defaut: `'success'`) |
306
-
307
- ---
308
-
309
- ## getAuditUser
310
-
311
- ```typescript
312
- import { getAuditUser } from '@mostajs/audit/lib/audit'
313
- ```
314
-
315
- Extrait `userId`, `userName` et `userRole` d'une session NextAuth. Compatible avec tout objet session ayant `session.user.id`, `session.user.name` et `session.user.role`.
316
-
317
- ```typescript
318
- const session = await getServerSession()
319
-
320
- await logAudit({
321
- ...getAuditUser(session), // { userId, userName, userRole }
322
- action: 'delete',
323
- module: 'products',
324
- resourceId: productId,
325
- })
326
- ```
327
-
328
- Le helper gere les cas :
329
- - `session.user.role` (string) → utilise directement
330
- - `session.user.roles` (array) → joint avec `, `
331
- - Ni l'un ni l'autre → `'unknown'`
332
- - `session.user.name` absent → utilise `session.user.email`
333
-
334
- ---
335
-
336
- ## AuditLogRepository
337
-
338
- ```typescript
339
- import { AuditLogRepository } from '@mostajs/audit'
340
- import { getDialect } from '@mostajs/orm'
341
-
342
- const repo = new AuditLogRepository(await getDialect())
343
- ```
344
-
345
- ### findPaginated(filters)
346
-
347
- Recherche paginee avec filtres optionnels.
348
-
349
- ```typescript
350
- const { data, total } = await repo.findPaginated({
351
- module: 'users', // Filtrer par module
352
- action: 'delete', // Filtrer par action (regex, case-insensitive)
353
- userId: '507f1f77...', // Filtrer par utilisateur
354
- status: 'failure', // Filtrer par statut
355
- from: new Date('2026-01-01'), // Date debut
356
- to: new Date('2026-03-01'), // Date fin
357
- page: 2, // Page (defaut: 1)
358
- limit: 20, // Limite (defaut: 50)
23
+ resource: 'John Doe',
24
+ resourceId: user.id,
25
+ details: { email: 'john@test.com' },
359
26
  })
360
-
361
- console.log(`${total} logs trouves, page 2`)
362
- data.forEach(log => console.log(log.action, log.module, log.timestamp))
363
- ```
364
-
365
- ### findByResource(resourceId, modules?)
366
-
367
- Tous les logs lies a une ressource specifique.
368
-
369
- ```typescript
370
- // Tous les logs du client CLI_123
371
- const logs = await repo.findByResource('CLI_123')
372
-
373
- // Logs du client CLI_123 dans les modules 'clients' et 'lockers'
374
- const filtered = await repo.findByResource('CLI_123', ['clients', 'lockers'])
375
- ```
376
-
377
- ### deleteOlderThan(days)
378
-
379
- Nettoyage des anciens logs. Utile en cron job ou maintenance.
380
-
381
- ```typescript
382
- // Supprimer les logs de plus de 90 jours
383
- const deleted = await repo.deleteOlderThan(90)
384
- console.log(`${deleted} logs supprimes`)
27
+ // Fire-and-forget — never throws, never blocks
385
28
  ```
386
29
 
387
- ---
388
-
389
- ## createAuditHandlers
30
+ ### 2. API Handler (paginated consultation)
390
31
 
391
32
  ```typescript
392
33
  import { createAuditHandlers } from '@mostajs/audit/api/route'
393
- ```
394
-
395
- Factory pour creer une route `GET` paginee avec controle d'acces.
396
-
397
- ```typescript
398
- // src/app/api/admin/audit/route.ts
399
- import { createAuditHandlers } from '@mostajs/audit/api/route'
400
- import { checkPermission } from '@/lib/auth'
401
-
402
34
  export const { GET } = createAuditHandlers('audit:view', checkPermission)
35
+ // GET /api/audit-log?page=1&limit=50&module=users&action=create
403
36
  ```
404
37
 
405
- ### Signature du checkPermission attendu
406
-
407
- ```typescript
408
- type PermissionChecker = (permission: string) => Promise<{
409
- error: NextResponse | null // null = autorise
410
- session: any // session utilisateur
411
- }>
412
- ```
413
-
414
- ### Query params supportes
415
-
416
- | Param | Type | Description |
417
- |-------|------|-------------|
418
- | `module` | string | Filtrer par module |
419
- | `action` | string | Filtrer par action (regex) |
420
- | `userId` | string | Filtrer par utilisateur |
421
- | `status` | `success \| failure` | Filtrer par statut |
422
- | `from` | ISO date | Date debut |
423
- | `to` | ISO date | Date fin |
424
- | `page` | number | Numero de page (defaut: 1) |
425
- | `limit` | number | Elements par page (defaut: 50) |
426
-
427
- ### Exemples de requetes
428
-
429
- ```bash
430
- # Tous les logs, page 1
431
- GET /api/admin/audit
432
-
433
- # Logs du module 'users', page 2, 20 par page
434
- GET /api/admin/audit?module=users&page=2&limit=20
435
-
436
- # Logs en echec du dernier mois
437
- GET /api/admin/audit?status=failure&from=2026-02-01&to=2026-03-01
438
-
439
- # Logs d'un utilisateur specifique
440
- GET /api/admin/audit?userId=507f1f77bcf86cd799439011
441
- ```
442
-
443
- ---
444
-
445
- ## Schema et indexes
446
-
447
- La collection/table `auditlogs` est creee automatiquement avec le schema suivant :
448
-
449
- | Champ | Type | Requis | Defaut |
450
- |-------|------|--------|--------|
451
- | `userId` | relation → User | oui | — |
452
- | `userName` | string | oui | — |
453
- | `userRole` | string | oui | — |
454
- | `action` | string | oui | — |
455
- | `module` | string | oui | — |
456
- | `resource` | string | non | `''` |
457
- | `resourceId` | string | non | `''` |
458
- | `details` | json | non | — |
459
- | `ipAddress` | string | non | `''` |
460
- | `status` | enum: success, failure | non | `'success'` |
461
- | `timestamp` | date | non | `now` |
462
-
463
- ### Indexes
464
-
465
- | Index | Champs | Usage |
466
- |-------|--------|-------|
467
- | 1 | `{ timestamp: desc }` | Tri chronologique |
468
- | 2 | `{ module: asc, timestamp: desc }` | Filtrage par module |
469
- | 3 | `{ userId: asc, timestamp: desc }` | Historique utilisateur |
470
-
471
- Compatible avec tous les dialectes supportes par `@mostajs/orm` (MongoDB, PostgreSQL, MySQL, SQLite, MariaDB, MSSQL, Oracle, etc.).
472
-
473
- ---
474
-
475
- ## Cas d'usage courants
476
-
477
- ### Auditer les operations CRUD
478
-
479
- ```typescript
480
- // Dans chaque route API
481
- export async function POST(req: NextRequest) {
482
- const { error, session } = await checkPermission(PERMISSIONS.CLIENT_CREATE)
483
- if (error) return error
484
-
485
- const data = await req.json()
486
- const client = await clientRepo.create(data)
487
-
488
- // Audit
489
- await logAudit({
490
- ...getAuditUser(session),
491
- action: 'create',
492
- module: 'clients',
493
- resource: 'Client',
494
- resourceId: client.id,
495
- details: { firstName: data.firstName, lastName: data.lastName },
496
- })
497
-
498
- return NextResponse.json({ data: client })
499
- }
500
- ```
501
-
502
- ### Auditer les connexions
503
-
504
- ```typescript
505
- // Dans le callback NextAuth signIn
506
- import { logAudit } from '@mostajs/audit/lib/audit'
507
-
508
- callbacks: {
509
- async signIn({ user }) {
510
- await logAudit({
511
- userId: user.id,
512
- userName: user.name || user.email,
513
- userRole: user.role || 'user',
514
- action: 'login',
515
- module: 'auth',
516
- })
517
- return true
518
- }
519
- }
520
- ```
521
-
522
- ### Auditer les echecs
523
-
524
- ```typescript
525
- try {
526
- await dangerousOperation()
527
- await logAudit({ ...getAuditUser(session), action: 'export_data', module: 'reports', status: 'success' })
528
- } catch (err) {
529
- await logAudit({ ...getAuditUser(session), action: 'export_data', module: 'reports', status: 'failure', details: { error: err.message } })
530
- throw err
531
- }
532
- ```
533
-
534
- ### Nettoyage automatique (cron)
38
+ ### 3. Module Info (for @mostajs/setup)
535
39
 
536
40
  ```typescript
537
- // src/app/api/cron/cleanup-audit/route.ts
538
- import { AuditLogRepository } from '@mostajs/audit'
539
- import { getDialect } from '@mostajs/orm'
540
-
541
- export async function POST() {
542
- const repo = new AuditLogRepository(await getDialect())
543
- const deleted = await repo.deleteOlderThan(180) // 6 mois
544
- return Response.json({ data: { deleted } })
545
- }
546
- ```
547
-
548
- ### Historique d'une ressource
549
-
550
- ```typescript
551
- // src/app/api/clients/[id]/history/route.ts
552
- import { AuditLogRepository } from '@mostajs/audit'
553
- import { getDialect } from '@mostajs/orm'
554
-
555
- export async function GET(req: Request, { params }: { params: { id: string } }) {
556
- const repo = new AuditLogRepository(await getDialect())
557
- const logs = await repo.findByResource(params.id, ['clients', 'lockers', 'rfid'])
558
- return Response.json({ data: logs })
559
- }
41
+ import { getSchemas } from '@mostajs/audit/lib/module-info'
42
+ const schemas = getSchemas() // [AuditLogSchema]
560
43
  ```
561
44
 
562
- ---
563
-
564
- ## API Reference
565
-
566
- ### Core
567
-
568
- | Export | Import | Description |
569
- |--------|--------|-------------|
570
- | `logAudit(params)` | `@mostajs/audit/lib/audit` | Fire-and-forget audit logging |
571
- | `getAuditUser(session)` | `@mostajs/audit/lib/audit` | Extraire userId/userName/userRole |
572
-
573
- ### Data Layer
574
-
575
- | Export | Import | Description |
576
- |--------|--------|-------------|
577
- | `AuditLogRepository` | `@mostajs/audit` | Repository avec findPaginated, findByResource, deleteOlderThan |
578
- | `AuditLogSchema` | `@mostajs/audit` | Schema d'entite ORM |
579
-
580
- ### Route Factory
581
-
582
- | Export | Import | Description |
583
- |--------|--------|-------------|
584
- | `createAuditHandlers(perm, checker)` | `@mostajs/audit/api/route` | Factory GET pagine avec auth |
585
-
586
- ### Types
587
-
588
- | Type | Description |
589
- |------|-------------|
590
- | `AuditParams` | Parametres de logAudit() |
591
- | `AuditFilters` | Filtres pour findPaginated() |
592
- | `AuditLogDTO` | Objet retourne (id, userName, action, module, timestamp, ...) |
593
- | `MostaAuditConfig` | Config optionnelle (modules, actions connus) |
594
-
595
- ---
596
-
597
- ## Architecture
598
-
599
- ```
600
- @mostajs/audit
601
- ├── lib/
602
- │ └── audit.ts # logAudit() + getAuditUser()
603
- ├── api/
604
- │ └── route.ts # createAuditHandlers() factory
605
- ├── repositories/
606
- │ └── audit-log.repository.ts # findPaginated, findByResource, deleteOlderThan
607
- ├── schemas/
608
- │ └── audit-log.schema.ts # EntitySchema ORM (collection: auditlogs)
609
- ├── types/
610
- │ └── index.ts # AuditParams, AuditFilters, AuditLogDTO
611
- └── index.ts # Barrel exports
612
-
613
- Dependances :
614
- @mostajs/orm (required — abstraction DB multi-dialecte)
615
- next >= 14 (peer, optionnel — pour createAuditHandlers)
616
- ```
617
-
618
- ### Pattern d'injection
619
-
620
- ```
621
- ┌─────────────────────┐ inject permission ┌──────────────────────┐
622
- │ @mostajs/audit │ ◄───────────────────────── │ Votre app │
623
- │ │ │ │
624
- │ createAuditHandlers(│ │ 'audit:view', │
625
- │ permission, │ │ checkPermission │
626
- │ checkPermission │ │ │
627
- │ ) │ │ │
628
- └─────────────────────┘ └──────────────────────┘
629
-
630
- ┌─────────────────────┐ call anywhere ┌──────────────────────┐
631
- │ logAudit({...}) │ ◄───────────────────────── │ Route API, callback, │
632
- │ fire-and-forget │ │ middleware, cron... │
633
- └─────────────────────┘ └──────────────────────┘
634
- ```
635
-
636
- Modifications effectuees :
637
-
638
- 1. README.md — Tutoriel complet avec :
639
- - Integration pas-a-pas dans une nouvelle app (6 etapes)
640
- - Documentation detaillee de logAudit, getAuditUser, AuditLogRepository,
641
- createAuditHandlers
642
- - Schema et indexes documentes
643
- - 5 cas d'usage courants (CRUD, login, echecs, cron cleanup, historique
644
- ressource)
645
- - API Reference, architecture ASCII, pattern d'injection
646
- 2. package.json — Ajout des subpath exports manquants : ./lib/*, ./api/*, ./types
647
- (corrige l'import @mostajs/audit/lib/audit utilise dans 15 routes de l'app)
648
-
649
-
650
- ---
651
-
652
- ## License
45
+ ### 4. Dual ORM/NET Mode
653
46
 
654
- MITDr Hamid MADANI <drmdh@msn.com>
47
+ Works automatically via `getAuditRepo()` reads `MOSTA_DATA=orm|net` from environment.
@@ -8,7 +8,7 @@ export interface IAuditLogRepository {
8
8
  findByResource(resourceId: string, modules?: string[]): Promise<AuditLogDTO[]>;
9
9
  deleteOlderThan(days: number): Promise<number>;
10
10
  }
11
- /** Get audit repository for the current data mode (ORM or NET) */
11
+ /** Get audit repository dialect resolved by octoswitcher (ORM or NET) */
12
12
  export declare function getAuditRepo(): Promise<IAuditLogRepository>;
13
13
  /** Reset cache (for tests) */
14
14
  export declare function resetAuditRepo(): void;
@@ -1,108 +1,26 @@
1
- // audit-factory.ts — Centralized repository factory for dual ORM/NET mode
2
- // Same pattern as @mosta/rbac repos-factory.ts
1
+ // audit-factory.ts — Centralized repository factory
2
+ // Uses @mostajs/octoswitcher to get the right dialect (ORM or NET)
3
3
  // Author: Dr Hamid MADANI drmdh@msn.com
4
- import { isNetMode } from './data-mode.js';
5
4
  import { AuditLogSchema } from '../schemas/audit-log.schema.js';
6
5
  // ============================================================
7
- // Factory
6
+ // Factory — dialect resolved by octoswitcher
8
7
  // ============================================================
9
8
  let _cached = null;
10
- let _schemaReady = false;
11
- /** Get audit repository for the current data mode (ORM or NET) */
9
+ /** Get audit repository — dialect resolved by octoswitcher (ORM or NET) */
12
10
  export async function getAuditRepo() {
13
11
  if (_cached)
14
12
  return _cached;
15
- if (isNetMode()) {
16
- await ensureSchemaNet();
17
- _cached = createNetRepo();
18
- }
19
- else {
20
- await ensureSchemaOrm();
21
- _cached = await createOrmRepo();
22
- }
23
- return _cached;
24
- }
25
- /** Reset cache (for tests) */
26
- export function resetAuditRepo() { _cached = null; _schemaReady = false; }
27
- // ============================================================
28
- // Schema init
29
- // ============================================================
30
- async function ensureSchemaOrm() {
31
- if (_schemaReady)
32
- return;
13
+ const { getDialect } = await import('@mostajs/octoswitcher');
33
14
  const { registerSchemas } = await import('@mostajs/orm');
34
- registerSchemas([AuditLogSchema]);
35
- _schemaReady = true;
36
- }
37
- async function ensureSchemaNet() {
38
- if (_schemaReady)
39
- return;
40
- const { NetClient } = await import('@mostajs/net/client');
41
- const client = new NetClient({ url: process.env.MOSTA_NET_URL });
42
- const result = await client.compareSchema(AuditLogSchema);
43
- if (!result.exists || !result.compatible) {
44
- await client.applySchema([AuditLogSchema]);
45
- }
46
- _schemaReady = true;
47
- }
48
- // ============================================================
49
- // ORM mode
50
- // ============================================================
51
- async function createOrmRepo() {
52
- const { getDialect } = await import('@mostajs/orm');
53
15
  const { AuditLogRepository } = await import('../repositories/audit-log.repository.js');
16
+ registerSchemas([AuditLogSchema]);
54
17
  const dialect = await getDialect();
55
- return new AuditLogRepository(dialect);
56
- }
57
- // ============================================================
58
- // NET mode
59
- // ============================================================
60
- function createNetRepo() {
61
- const clientPromise = import('@mostajs/net/client').then(m => new m.NetClient({ url: process.env.MOSTA_NET_URL }));
62
- return {
63
- async create(data) {
64
- const c = await clientPromise;
65
- return c.create('auditlogs', data);
66
- },
67
- async findPaginated(filters) {
68
- const c = await clientPromise;
69
- const netFilter = {};
70
- if (filters.module)
71
- netFilter.module = filters.module;
72
- if (filters.action)
73
- netFilter.action = { $regex: filters.action, $regexFlags: 'i' };
74
- if (filters.userId)
75
- netFilter.userId = filters.userId;
76
- if (filters.status)
77
- netFilter.status = filters.status;
78
- if (filters.from || filters.to) {
79
- const ts = {};
80
- if (filters.from)
81
- ts.$gte = new Date(filters.from).toISOString();
82
- if (filters.to)
83
- ts.$lte = new Date(filters.to).toISOString();
84
- netFilter.timestamp = ts;
85
- }
86
- const page = filters.page || 1;
87
- const limit = filters.limit || 50;
88
- const options = { sort: { timestamp: -1 }, skip: (page - 1) * limit, limit };
89
- const [data, total] = await Promise.all([
90
- c.findAll('auditlogs', netFilter, options),
91
- c.count('auditlogs', netFilter),
92
- ]);
93
- return { data, total };
94
- },
95
- async findByResource(resourceId, modules) {
96
- const c = await clientPromise;
97
- const filter = { resourceId };
98
- if (modules?.length)
99
- filter.module = { $in: modules };
100
- return c.findAll('auditlogs', filter, { sort: { timestamp: -1 } });
101
- },
102
- async deleteOlderThan(days) {
103
- const c = await clientPromise;
104
- const cutoff = new Date(Date.now() - days * 86400000).toISOString();
105
- return c.deleteMany('auditlogs', { timestamp: { $lt: cutoff } });
106
- },
107
- };
18
+ if (typeof dialect.initSchema === 'function') {
19
+ const { getAllSchemas } = await import('@mostajs/orm');
20
+ await dialect.initSchema(getAllSchemas());
21
+ }
22
+ _cached = new AuditLogRepository(dialect);
23
+ return _cached;
108
24
  }
25
+ /** Reset cache (for tests) */
26
+ export function resetAuditRepo() { _cached = null; }
@@ -1,22 +1,12 @@
1
- // audit-init.ts — Ensure AuditLog schema is ready (ORM or NET)
1
+ // audit-init.ts — Ensure AuditLog schema is ready
2
+ // Uses octoswitcher — schema registration works transparently in ORM and NET mode
2
3
  // Author: Dr Hamid MADANI drmdh@msn.com
3
- import { isNetMode } from './data-mode.js';
4
4
  import { AuditLogSchema } from '../schemas/audit-log.schema.js';
5
5
  let initialized = false;
6
6
  export async function ensureAuditSchema() {
7
7
  if (initialized)
8
8
  return;
9
- if (isNetMode()) {
10
- const { NetClient } = await import('@mostajs/net/client');
11
- const client = new NetClient({ url: process.env.MOSTA_NET_URL });
12
- const result = await client.compareSchema(AuditLogSchema);
13
- if (!result.exists || !result.compatible) {
14
- await client.applySchema([AuditLogSchema]);
15
- }
16
- }
17
- else {
18
- const { registerSchemas } = await import('@mostajs/orm');
19
- registerSchemas([AuditLogSchema]);
20
- }
9
+ const { registerSchemas } = await import('@mostajs/orm');
10
+ registerSchemas([AuditLogSchema]);
21
11
  initialized = true;
22
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/audit",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "description": "Reusable audit logging module — fire-and-forget logAudit() with paginated consultation",
5
5
  "author": "Dr Hamid MADANI <drmdh@msn.com>",
6
6
  "license": "MIT",
@@ -119,6 +119,7 @@
119
119
  },
120
120
  "dependencies": {
121
121
  "@mostajs/net": "^2.0.0",
122
+ "@mostajs/octoswitcher": "^1.0.0",
122
123
  "@mostajs/orm": "^1.7.0"
123
124
  }
124
125
  }