@mostajs/audit 1.1.0 → 2.0.1

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/dist/api/route.js CHANGED
@@ -1,14 +1,7 @@
1
1
  // @mosta/audit — API Route template
2
2
  // Author: Dr Hamid MADANI drmdh@msn.com
3
- //
4
- // Copy to: src/app/api/admin/audit/route.ts
5
- // Usage:
6
- // import { createAuditHandlers } from '@mosta/audit/api/route'
7
- // import { checkPermission } from '@mosta/auth'
8
- // export const { GET } = createAuditHandlers('audit:view', checkPermission)
9
3
  import { NextResponse } from 'next/server';
10
- import { getDialect } from '@mostajs/orm';
11
- import { AuditLogRepository } from '../repositories/audit-log.repository';
4
+ import { getAuditRepo } from '../lib/audit-factory.js';
12
5
  /**
13
6
  * Creates a GET handler for paginated audit log consultation.
14
7
  */
@@ -28,7 +21,7 @@ export function createAuditHandlers(permission, checkPermission) {
28
21
  page: parseInt(url.searchParams.get('page') || '1', 10),
29
22
  limit: parseInt(url.searchParams.get('limit') || '50', 10),
30
23
  };
31
- const repo = new AuditLogRepository(await getDialect());
24
+ const repo = await getAuditRepo();
32
25
  const { data, total } = await repo.findPaginated(filters);
33
26
  return NextResponse.json({
34
27
  data,
@@ -0,0 +1,14 @@
1
+ import type { AuditLogDTO, AuditFilters } from '../types/index.js';
2
+ export interface IAuditLogRepository {
3
+ create(data: Record<string, unknown>): Promise<AuditLogDTO>;
4
+ findPaginated(filters: AuditFilters): Promise<{
5
+ data: AuditLogDTO[];
6
+ total: number;
7
+ }>;
8
+ findByResource(resourceId: string, modules?: string[]): Promise<AuditLogDTO[]>;
9
+ deleteOlderThan(days: number): Promise<number>;
10
+ }
11
+ /** Get audit repository for the current data mode (ORM or NET) */
12
+ export declare function getAuditRepo(): Promise<IAuditLogRepository>;
13
+ /** Reset cache (for tests) */
14
+ export declare function resetAuditRepo(): void;
@@ -0,0 +1,108 @@
1
+ // audit-factory.ts — Centralized repository factory for dual ORM/NET mode
2
+ // Same pattern as @mosta/rbac repos-factory.ts
3
+ // Author: Dr Hamid MADANI drmdh@msn.com
4
+ import { isNetMode } from './data-mode.js';
5
+ import { AuditLogSchema } from '../schemas/audit-log.schema.js';
6
+ // ============================================================
7
+ // Factory
8
+ // ============================================================
9
+ let _cached = null;
10
+ let _schemaReady = false;
11
+ /** Get audit repository for the current data mode (ORM or NET) */
12
+ export async function getAuditRepo() {
13
+ if (_cached)
14
+ 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;
33
+ 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
+ const { AuditLogRepository } = await import('../repositories/audit-log.repository.js');
54
+ 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
+ };
108
+ }
@@ -0,0 +1 @@
1
+ export declare function ensureAuditSchema(): Promise<void>;
@@ -0,0 +1,22 @@
1
+ // audit-init.ts — Ensure AuditLog schema is ready (ORM or NET)
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ import { isNetMode } from './data-mode.js';
4
+ import { AuditLogSchema } from '../schemas/audit-log.schema.js';
5
+ let initialized = false;
6
+ export async function ensureAuditSchema() {
7
+ if (initialized)
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
+ }
21
+ initialized = true;
22
+ }
@@ -1,4 +1,4 @@
1
- import type { AuditParams } from '../types/index';
1
+ import type { AuditParams } from '../types/index.js';
2
2
  /**
3
3
  * Log an audit entry. Fire-and-forget — never throws, never blocks.
4
4
  */
package/dist/lib/audit.js CHANGED
@@ -1,16 +1,12 @@
1
1
  // @mosta/audit — Core audit functions
2
2
  // Author: Dr Hamid MADANI drmdh@msn.com
3
- import { getDialect, registerSchemas } from '@mostajs/orm';
4
- import { AuditLogRepository } from '../repositories/audit-log.repository';
5
- import { AuditLogSchema } from '../schemas/audit-log.schema';
6
- // Auto-register audit schema into ORM registry (idempotent)
7
- registerSchemas([AuditLogSchema]);
3
+ import { getAuditRepo } from './audit-factory.js';
8
4
  /**
9
5
  * Log an audit entry. Fire-and-forget — never throws, never blocks.
10
6
  */
11
7
  export async function logAudit(params) {
12
8
  try {
13
- const repo = new AuditLogRepository(await getDialect());
9
+ const repo = await getAuditRepo();
14
10
  await repo.create({
15
11
  userId: params.userId,
16
12
  userName: params.userName,
@@ -0,0 +1 @@
1
+ export declare function isNetMode(): boolean;
@@ -0,0 +1,5 @@
1
+ // data-mode.ts — Helper for dual ORM/NET mode
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ export function isNetMode() {
4
+ return process.env.MOSTA_DATA === 'net';
5
+ }
@@ -1,7 +1,7 @@
1
1
  // @mostajs/audit — Route handler for audit-log
2
2
  // Author: Dr Hamid MADANI drmdh@msn.com
3
3
  // Bare handler — NO auth. The socle catch-all does it via permission.
4
- import { AuditLogRepository } from '../../repositories/audit-log.repository.js';
4
+ import { getAuditRepo } from '../audit-factory.js';
5
5
  export async function GET(req) {
6
6
  const url = new URL(req.url);
7
7
  const page = parseInt(url.searchParams.get('page') || '1', 10);
@@ -12,9 +12,7 @@ export async function GET(req) {
12
12
  const status = url.searchParams.get('status') || undefined;
13
13
  const dateFrom = url.searchParams.get('dateFrom') || url.searchParams.get('from') || null;
14
14
  const dateTo = url.searchParams.get('dateTo') || url.searchParams.get('to') || null;
15
- const { getDialect } = await import('@mostajs/orm');
16
- const dialect = await getDialect();
17
- const repo = new AuditLogRepository(dialect);
15
+ const repo = await getAuditRepo();
18
16
  const { data: logs, total } = await repo.findPaginated({
19
17
  module, action, userId, status,
20
18
  from: dateFrom ? new Date(dateFrom) : undefined,
@@ -0,0 +1,8 @@
1
+ export declare function getSchemas(): import("@mostajs/orm").EntitySchema[];
2
+ export declare const moduleInfo: {
3
+ name: string;
4
+ label: string;
5
+ description: string;
6
+ schemas: typeof getSchemas;
7
+ seed: () => Promise<{}>;
8
+ };
@@ -0,0 +1,16 @@
1
+ // @mostajs/audit — Module info (schemas, seeds, metadata)
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ import { AuditLogSchema } from '../schemas/audit-log.schema.js';
4
+ export function getSchemas() {
5
+ return [AuditLogSchema];
6
+ }
7
+ export const moduleInfo = {
8
+ name: 'audit',
9
+ label: 'Audit & Logs',
10
+ description: 'Audit logging with paginated consultation',
11
+ schemas: getSchemas,
12
+ seed: async () => {
13
+ // Audit has no seed data
14
+ return {};
15
+ },
16
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/audit",
3
- "version": "1.1.0",
3
+ "version": "2.0.1",
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",
@@ -76,9 +76,6 @@
76
76
  "build": "tsc",
77
77
  "prepublishOnly": "npm run build"
78
78
  },
79
- "dependencies": {
80
- "@mostajs/orm": "^1.0.0"
81
- },
82
79
  "peerDependencies": {
83
80
  "@mostajs/menu": ">=1.0.2",
84
81
  "@mostajs/socle": ">=2.0.0",
@@ -108,10 +105,15 @@
108
105
  "@mostajs/menu": "^1.0.2",
109
106
  "@mostajs/socle": "^2.0.0",
110
107
  "@mostajs/ui": "^1.0.3",
108
+ "@types/node": "^25.5.0",
111
109
  "@types/react": "^19.0.0",
112
110
  "lucide-react": "^0.575.0",
113
111
  "next": "^16.1.6",
114
112
  "react": "^19.0.0",
115
113
  "typescript": "^5.6.0"
114
+ },
115
+ "dependencies": {
116
+ "@mostajs/net": "^2.0.0",
117
+ "@mostajs/orm": "^1.7.0"
116
118
  }
117
119
  }