@modern-admin/system-prisma 0.5.0 → 0.7.0
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 +10 -0
- package/dist/index.js.map +1 -1
- package/dist/stores/ai-task-store.d.ts.map +1 -1
- package/dist/stores/ai-task-store.js.map +1 -1
- package/dist/stores/cache-store.d.ts.map +1 -1
- package/dist/stores/cache-store.js.map +1 -1
- package/dist/stores/config-store.d.ts.map +1 -1
- package/dist/stores/config-store.js.map +1 -1
- package/dist/stores/history-store.d.ts +25 -1
- package/dist/stores/history-store.d.ts.map +1 -1
- package/dist/stores/history-store.js +67 -0
- package/dist/stores/history-store.js.map +1 -1
- package/dist/stores/log-store.d.ts.map +1 -1
- package/dist/stores/log-store.js +37 -9
- package/dist/stores/log-store.js.map +1 -1
- package/dist/stores/webhook-store.d.ts.map +1 -1
- package/dist/stores/webhook-store.js.map +1 -1
- package/dist/types.d.ts +13 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +7 -6
- package/prisma/migrations/better-auth-1.7-account-identities.sql +138 -0
- package/prisma/modern-admin.prisma +342 -0
- package/src/stores/history-store.ts +74 -0
- package/src/stores/log-store.ts +34 -7
- package/src/types.ts +5 -0
package/README.md
CHANGED
|
@@ -20,6 +20,16 @@ bun add @modern-admin/system-prisma
|
|
|
20
20
|
Setup guides, architecture, and usage examples live in the
|
|
21
21
|
[Modern Admin README](https://github.com/modern-admin/modern-admin#readme).
|
|
22
22
|
|
|
23
|
+
## Better Auth 1.7 migration
|
|
24
|
+
|
|
25
|
+
The canonical `prisma/modern-admin.prisma` fragment targets Better Auth 1.7:
|
|
26
|
+
`MaAccount.issuer` is required and `(issuer, accountId)` is unique. A populated
|
|
27
|
+
Better Auth 1.6 database must be upgraded during an authentication maintenance
|
|
28
|
+
window with
|
|
29
|
+
`prisma/migrations/better-auth-1.7-account-identities.sql`. Review its explicit
|
|
30
|
+
provider mapping first. Unknown providers and identity collisions abort and
|
|
31
|
+
roll back the complete transaction; users are never merged or deleted.
|
|
32
|
+
|
|
23
33
|
## License
|
|
24
34
|
|
|
25
35
|
[MIT](https://github.com/modern-admin/modern-admin/blob/main/LICENSE) © Modern Admin
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,gEAAgE;AAChE,EAAE;AACF,eAAe;AACf,sEAAsE;AACtE,gDAAgD;AAChD,+CAA+C;AAC/C,gCAAgC;AAChC,EAAE;AACF,mDAAmD;AACnD,qEAAqE;AACrE,6EAA6E;AAC7E,EAAE;AACF,uCAAuC;AACvC,8CAA8C;AAC9C,EAAE;AACF,uBAAuB;AACvB,yBAAyB;AACzB,mEAAmE;AACnE,QAAQ;AACR,EAAE;AACF,+EAA+E;AAG/E,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,EAAwC,eAAe,EAAE,MAAM,YAAY,CAAA;AAwBlF;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAkB,EAClB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,gEAAgE;AAChE,EAAE;AACF,eAAe;AACf,sEAAsE;AACtE,gDAAgD;AAChD,+CAA+C;AAC/C,gCAAgC;AAChC,EAAE;AACF,mDAAmD;AACnD,qEAAqE;AACrE,6EAA6E;AAC7E,EAAE;AACF,uCAAuC;AACvC,8CAA8C;AAC9C,EAAE;AACF,uBAAuB;AACvB,yBAAyB;AACzB,mEAAmE;AACnE,QAAQ;AACR,EAAE;AACF,+EAA+E;AAG/E,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,EAAwC,eAAe,EAAE,MAAM,YAAY,CAAA;AAwBlF;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAkB,EAClB,OAAO,GAAwB,EAAE;IAEjC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAA;IACxB,OAAO;QACL,QAAQ,EAAE,IAAI,cAAc,CAAC,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAC/D,YAAY,EAAE,IAAI,kBAAkB,CAClC,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,EACrC,eAAe,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAC9C;QACD,WAAW,EAAE,IAAI,iBAAiB,CAAC,eAAe,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;QACxE,YAAY,EAAE,IAAI,kBAAkB,CAAC,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QAC3E,WAAW,EAAE,IAAI,iBAAiB,CAChC,eAAe,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,EACpC,eAAe,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC,CAC1C;QACD,UAAU,EAAE,IAAI,gBAAgB,CAAC,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;KACtE,CAAA;AACH,CAAC;AAED,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE1D,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-task-store.d.ts","sourceRoot":"","sources":["../../src/stores/ai-task-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,MAAM,EACX,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,OAAO,EACb,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD,qBAAa,iBAAkB,YAAW,YAAY;IAElD,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,aAAa;
|
|
1
|
+
{"version":3,"file":"ai-task-store.d.ts","sourceRoot":"","sources":["../../src/stores/ai-task-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,MAAM,EACX,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,OAAO,EACb,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD,qBAAa,iBAAkB,YAAW,YAAY;IAElD,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAFhC,YACmB,YAAY,EAAE,cAAc,CAAC,OAAO,CAAC,EACrC,aAAa,EAAE,cAAc,CAAC,QAAQ,CAAC,EACtD;IAEE,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAcjD;IAEK,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAG5C;IAEK,IAAI,CAAC,MAAM,GAAE,UAAU,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAe9E;IAEK,YAAY,CAChB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;QACL,MAAM,EAAE,YAAY,CAAA;QACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;QACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QAChC,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,GACA,OAAO,CAAC,MAAM,CAAC,CAejB;IAEK,WAAW,CACf,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,WAAW,CAAC,CAKtB;IAEK,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAQrE;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-task-store.js","sourceRoot":"","sources":["../../src/stores/ai-task-store.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,SAAS,EACT,MAAM,GAQP,MAAM,oBAAoB,CAAA;AAG3B,MAAM,QAAQ,GAAmB,CAAC,WAAW,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAA;AAErE,MAAM,OAAO,iBAAiB;IAC5B,YACmB,YAAqC,EACrC,aAAuC;
|
|
1
|
+
{"version":3,"file":"ai-task-store.js","sourceRoot":"","sources":["../../src/stores/ai-task-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-store.d.ts","sourceRoot":"","sources":["../../src/stores/cache-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACtG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD,qBAAa,gBAAiB,YAAW,WAAW;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ;
|
|
1
|
+
{"version":3,"file":"cache-store.d.ts","sourceRoot":"","sources":["../../src/stores/cache-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACtG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD,qBAAa,gBAAiB,YAAW,WAAW;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAArC,YAA6B,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAI;IAE7D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAQjD;IAEK,GAAG,CACP,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,EACd,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAO,GAChD,OAAO,CAAC,IAAI,CAAC,CAQf;IAEK,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvC;IAED;;;;;OAKG;IACG,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BpD;IAEK,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAK7B;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-store.js","sourceRoot":"","sources":["../../src/stores/cache-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAoD,MAAM,oBAAoB,CAAA;AAGtG,MAAM,OAAO,gBAAgB;IAC3B,YAA6B,QAAkC;
|
|
1
|
+
{"version":3,"file":"cache-store.js","sourceRoot":"","sources":["../../src/stores/cache-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-store.d.ts","sourceRoot":"","sources":["../../src/stores/config-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAU,MAAM,oBAAoB,CAAA;AACpH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,OAAO,CAAA;IACd,SAAS,EAAE,IAAI,CAAA;CAChB;AAWD,qBAAa,iBAAkB,YAAW,YAAY;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ;
|
|
1
|
+
{"version":3,"file":"config-store.d.ts","sourceRoot":"","sources":["../../src/stores/config-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAU,MAAM,oBAAoB,CAAA;AACpH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,OAAO,CAAA;IACd,SAAS,EAAE,IAAI,CAAA;CAChB;AAWD,qBAAa,iBAAkB,YAAW,YAAY;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAArC,YAA6B,QAAQ,EAAE,cAAc,CAAC,SAAS,CAAC,EAC/D;IAEK,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQnF;IAEK,GAAG,CACP,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,IAAI,CAAC,CAoBf;IAEK,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKnF;IAEK,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAM7E;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../src/stores/config-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAyD,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAYpH,MAAM,eAAe,GAAG,EAAE,CAAA;AAC1B,MAAM,aAAa,GAAG,CAAC,OAAsB,EAAU,EAAE,CAAC,OAAO,IAAI,eAAe,CAAA;AACpF,MAAM,aAAa,GAAG,CAAC,OAAe,EAAiB,EAAE,CAAC,OAAO,KAAK,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAA;AAEtG,+EAA+E;AAC/E,yDAAyD;AACzD,MAAM,UAAU,GAAG,CAAC,GAAc,EAAE,EAAE,CACpC,gBAAgB,CAAC,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;AAEnE,MAAM,OAAO,iBAAiB;IAC5B,YAA6B,QAAmC;
|
|
1
|
+
{"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../src/stores/config-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type HistoryEntry, type HistoryOp, type HistoryRow, type IHistoryStore } from '@modern-admin/core';
|
|
1
|
+
import { type HistoryEntry, type HistoryOp, type HistoryRetention, type HistoryRow, type IHistoryStore } from '@modern-admin/core';
|
|
2
2
|
import type { PrismaDelegate } from '../types.js';
|
|
3
3
|
export declare class PrismaHistoryStore implements IHistoryStore {
|
|
4
4
|
private readonly delegate;
|
|
@@ -17,5 +17,29 @@ export declare class PrismaHistoryStore implements IHistoryStore {
|
|
|
17
17
|
}): Promise<HistoryEntry[]>;
|
|
18
18
|
get(resourceId: string, recordId: string, revisionId: string): Promise<HistoryEntry | null>;
|
|
19
19
|
latest(resourceId: string, recordId: string): Promise<HistoryEntry | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Enforce a retention policy. Every revision of every record is kept
|
|
22
|
+
* forever without one — the table grows with each edit and nothing in the
|
|
23
|
+
* framework ever trims it.
|
|
24
|
+
*
|
|
25
|
+
* Bounds combine: a revision survives only if it satisfies all of them, so
|
|
26
|
+
* they are applied in sequence. `keepDays` is one `deleteMany`; `keepLast`
|
|
27
|
+
* is per-record, which no single `deleteMany` can express. Obsolete ids are
|
|
28
|
+
* selected and deleted in bounded batches. Deleting an explicit snapshot
|
|
29
|
+
* of ids is intentional: a concurrent append must never be caught by a
|
|
30
|
+
* broad `notIn` predicate and removed as if it were an old revision. This
|
|
31
|
+
* is a maintenance job (cron, startup), never a request-path operation.
|
|
32
|
+
*/
|
|
33
|
+
prune(retention: HistoryRetention): Promise<number>;
|
|
34
|
+
/**
|
|
35
|
+
* The distinct `(resourceId, recordId)` pairs that have revisions.
|
|
36
|
+
*
|
|
37
|
+
* `groupBy` because it becomes a real SQL `GROUP BY` on every connector,
|
|
38
|
+
* whereas Prisma's `distinct` is applied in the query engine — outside
|
|
39
|
+
* Postgres that means fetching every revision row first, which is exactly
|
|
40
|
+
* what pruning a large table must not do. `distinct` remains as a fallback
|
|
41
|
+
* for delegates that do not expose `groupBy` (hand-written test doubles).
|
|
42
|
+
*/
|
|
43
|
+
private recordGroups;
|
|
20
44
|
}
|
|
21
45
|
//# sourceMappingURL=history-store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"history-store.d.ts","sourceRoot":"","sources":["../../src/stores/history-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"history-store.d.ts","sourceRoot":"","sources":["../../src/stores/history-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD,qBAAa,kBAAmB,YAAW,aAAa;IAC1C,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAArC,YAA6B,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,EAAI;IAE/D,MAAM,CAAC,KAAK,EAAE;QAClB,UAAU,EAAE,MAAM,CAAA;QAClB,QAAQ,EAAE,MAAM,CAAA;QAChB,EAAE,EAAE,SAAS,CAAA;QACb,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACjC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KACzC,GAAG,OAAO,CAAC,YAAY,CAAC,CAaxB;IAEK,IAAI,CACR,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GAChD,OAAO,CAAC,YAAY,EAAE,CAAC,CAUzB;IAEK,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAKhG;IAEK,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAM/E;IAED;;;;;;;;;;;;OAYG;IACG,KAAK,CAAC,SAAS,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAmCxD;IAED;;;;;;;;OAQG;YACW,YAAY;CAW3B"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { rowToHistoryEntry as rowToEntry, uuidv7, } from '@modern-admin/core';
|
|
2
|
+
const PRUNE_BATCH_SIZE = 1_000;
|
|
2
3
|
export class PrismaHistoryStore {
|
|
3
4
|
constructor(delegate) {
|
|
4
5
|
this.delegate = delegate;
|
|
@@ -41,5 +42,71 @@ export class PrismaHistoryStore {
|
|
|
41
42
|
});
|
|
42
43
|
return row ? rowToEntry(row) : null;
|
|
43
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Enforce a retention policy. Every revision of every record is kept
|
|
47
|
+
* forever without one — the table grows with each edit and nothing in the
|
|
48
|
+
* framework ever trims it.
|
|
49
|
+
*
|
|
50
|
+
* Bounds combine: a revision survives only if it satisfies all of them, so
|
|
51
|
+
* they are applied in sequence. `keepDays` is one `deleteMany`; `keepLast`
|
|
52
|
+
* is per-record, which no single `deleteMany` can express. Obsolete ids are
|
|
53
|
+
* selected and deleted in bounded batches. Deleting an explicit snapshot
|
|
54
|
+
* of ids is intentional: a concurrent append must never be caught by a
|
|
55
|
+
* broad `notIn` predicate and removed as if it were an old revision. This
|
|
56
|
+
* is a maintenance job (cron, startup), never a request-path operation.
|
|
57
|
+
*/
|
|
58
|
+
async prune(retention) {
|
|
59
|
+
let removed = 0;
|
|
60
|
+
if (retention.keepDays !== undefined) {
|
|
61
|
+
const cutoff = new Date(Date.now() - retention.keepDays * 24 * 60 * 60 * 1000);
|
|
62
|
+
const { count } = await this.delegate.deleteMany({
|
|
63
|
+
where: { createdAt: { lt: cutoff } },
|
|
64
|
+
});
|
|
65
|
+
removed += count;
|
|
66
|
+
}
|
|
67
|
+
if (retention.keepLast !== undefined) {
|
|
68
|
+
const keep = Math.max(0, Math.trunc(retention.keepLast));
|
|
69
|
+
const groups = await this.recordGroups();
|
|
70
|
+
for (const group of groups) {
|
|
71
|
+
const scope = { resourceId: group.resourceId, recordId: group.recordId };
|
|
72
|
+
while (true) {
|
|
73
|
+
const obsolete = (await this.delegate.findMany({
|
|
74
|
+
where: scope,
|
|
75
|
+
orderBy: [{ createdAt: 'desc' }, { id: 'desc' }],
|
|
76
|
+
select: { id: true },
|
|
77
|
+
...(keep > 0 ? { skip: keep } : {}),
|
|
78
|
+
take: PRUNE_BATCH_SIZE,
|
|
79
|
+
}));
|
|
80
|
+
if (obsolete.length === 0)
|
|
81
|
+
break;
|
|
82
|
+
const { count } = await this.delegate.deleteMany({
|
|
83
|
+
where: { ...scope, id: { in: obsolete.map((row) => row.id) } },
|
|
84
|
+
});
|
|
85
|
+
removed += count;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return removed;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The distinct `(resourceId, recordId)` pairs that have revisions.
|
|
93
|
+
*
|
|
94
|
+
* `groupBy` because it becomes a real SQL `GROUP BY` on every connector,
|
|
95
|
+
* whereas Prisma's `distinct` is applied in the query engine — outside
|
|
96
|
+
* Postgres that means fetching every revision row first, which is exactly
|
|
97
|
+
* what pruning a large table must not do. `distinct` remains as a fallback
|
|
98
|
+
* for delegates that do not expose `groupBy` (hand-written test doubles).
|
|
99
|
+
*/
|
|
100
|
+
async recordGroups() {
|
|
101
|
+
if (typeof this.delegate.groupBy === 'function') {
|
|
102
|
+
return (await this.delegate.groupBy({
|
|
103
|
+
by: ['resourceId', 'recordId'],
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
return (await this.delegate.findMany({
|
|
107
|
+
distinct: ['resourceId', 'recordId'],
|
|
108
|
+
select: { resourceId: true, recordId: true },
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
44
111
|
}
|
|
45
112
|
//# sourceMappingURL=history-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"history-store.js","sourceRoot":"","sources":["../../src/stores/history-store.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,IAAI,UAAU,EAC/B,MAAM,
|
|
1
|
+
{"version":3,"file":"history-store.js","sourceRoot":"","sources":["../../src/stores/history-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-store.d.ts","sourceRoot":"","sources":["../../src/stores/log-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,MAAM,EACZ,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"log-store.d.ts","sourceRoot":"","sources":["../../src/stores/log-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,MAAM,EACZ,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAKjD,qBAAa,cAAe,YAAW,kBAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAArC,YAA6B,QAAQ,EAAE,cAAc,CAAC,MAAM,CAAC,EAAI;IAE3D,MAAM,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBjD;IAEK,IAAI,CAAC,MAAM,GAAE,UAAU,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CA2C5F;CACF;AAED,kFAAkF;AAClF,YAAY,EAAE,SAAS,EAAE,CAAA"}
|
package/dist/stores/log-store.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { rowToLogEntry, uuidv7, } from '@modern-admin/core';
|
|
2
|
+
/** Applied when the caller passes no `limit`. Matches `MemoryLogStore`. */
|
|
3
|
+
const DEFAULT_LIST_LIMIT = 50;
|
|
2
4
|
export class PrismaLogStore {
|
|
3
5
|
constructor(delegate) {
|
|
4
6
|
this.delegate = delegate;
|
|
@@ -30,18 +32,44 @@ export class PrismaLogStore {
|
|
|
30
32
|
where['userId'] = filter.userId;
|
|
31
33
|
if (filter.actions?.length)
|
|
32
34
|
where['action'] = { in: filter.actions };
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
// `from`/`to` bound the window; `before` is the keyset cursor the audit
|
|
36
|
+
// log's "load more" pages on. All three narrow the same column, so they
|
|
37
|
+
// merge into one range clause — dropping `before` (as this store used to)
|
|
38
|
+
// made every extra page return the first page again, forever.
|
|
39
|
+
const range = {};
|
|
40
|
+
if (filter.from)
|
|
41
|
+
range['gte'] = BigInt(filter.from.getTime());
|
|
42
|
+
if (filter.to)
|
|
43
|
+
range['lte'] = BigInt(filter.to.getTime());
|
|
44
|
+
if (filter.before !== undefined) {
|
|
45
|
+
const cursorAt = BigInt(filter.before);
|
|
46
|
+
if (filter.beforeId !== undefined) {
|
|
47
|
+
// Lexicographic continuation of the `(at, id) DESC` order. Keeping
|
|
48
|
+
// the id in the cursor is essential because `at` has ms precision and
|
|
49
|
+
// many audit entries can legitimately share it.
|
|
50
|
+
where['OR'] = [
|
|
51
|
+
{ at: { lt: cursorAt } },
|
|
52
|
+
{ at: cursorAt, id: { lt: filter.beforeId } },
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// Backwards-compatible timestamp-only cursor for existing callers.
|
|
57
|
+
range['lt'] = cursorAt;
|
|
58
|
+
}
|
|
40
59
|
}
|
|
60
|
+
if (Object.keys(range).length > 0)
|
|
61
|
+
where['at'] = range;
|
|
41
62
|
const rows = await this.delegate.findMany({
|
|
42
63
|
where,
|
|
43
|
-
|
|
44
|
-
|
|
64
|
+
// `at` is milliseconds and not unique, so it alone is not a total
|
|
65
|
+
// order — two entries in the same millisecond could swap between
|
|
66
|
+
// pages. Ids are UUIDv7, i.e. already time-ordered, so they are the
|
|
67
|
+
// natural tiebreaker.
|
|
68
|
+
orderBy: [{ at: 'desc' }, { id: 'desc' }],
|
|
69
|
+
// The audit log is append-only and unbounded; an unbounded SELECT over
|
|
70
|
+
// it is a memory hazard. Compare `PrismaHistoryStore.list`, which has
|
|
71
|
+
// always defaulted to 50.
|
|
72
|
+
take: filter.limit ?? DEFAULT_LIST_LIMIT,
|
|
45
73
|
...(filter.offset !== undefined ? { skip: filter.offset } : {}),
|
|
46
74
|
});
|
|
47
75
|
return rows.map(rowToLogEntry);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-store.js","sourceRoot":"","sources":["../../src/stores/log-store.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,MAAM,GAKP,MAAM,oBAAoB,CAAA;AAG3B,MAAM,OAAO,cAAc;IACzB,YAA6B,QAAgC;
|
|
1
|
+
{"version":3,"file":"log-store.js","sourceRoot":"","sources":["../../src/stores/log-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook-store.d.ts","sourceRoot":"","sources":["../../src/stores/webhook-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,OAAO,EACZ,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD,qBAAa,kBAAmB,YAAW,aAAa;IAEpD,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,gBAAgB;
|
|
1
|
+
{"version":3,"file":"webhook-store.d.ts","sourceRoot":"","sources":["../../src/stores/webhook-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,OAAO,EACZ,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD,qBAAa,kBAAmB,YAAW,aAAa;IAEpD,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAFnC,YACmB,eAAe,EAAE,cAAc,CAAC,UAAU,CAAC,EAC3C,gBAAgB,EAAE,cAAc,CAAC,WAAW,CAAC,EAC5D;IAEE,IAAI,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAG/B;IAEK,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAG7C;IAEK,MAAM,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAgBlD;IAEK,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAavE;IAEK,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtC;IAEK,cAAc,CAClB,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,WAAW,CAAC,GAClD,OAAO,CAAC,eAAe,CAAC,CAgB1B;IAEK,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAO9E;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook-store.js","sourceRoot":"","sources":["../../src/stores/webhook-store.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,YAAY,EACZ,MAAM,GAOP,MAAM,oBAAoB,CAAA;AAG3B,MAAM,OAAO,kBAAkB;IAC7B,YACmB,eAA2C,EAC3C,gBAA6C
|
|
1
|
+
{"version":3,"file":"webhook-store.js","sourceRoot":"","sources":["../../src/stores/webhook-store.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -34,6 +34,11 @@ export interface PrismaDelegate<TRow = any> {
|
|
|
34
34
|
count: number;
|
|
35
35
|
}>;
|
|
36
36
|
count(args?: any): Promise<number>;
|
|
37
|
+
/**
|
|
38
|
+
* Present on every generated Prisma delegate. Optional here so hand-written
|
|
39
|
+
* test doubles stay assignable; callers fall back when it is absent.
|
|
40
|
+
*/
|
|
41
|
+
groupBy?(args: any): Promise<any[]>;
|
|
37
42
|
}
|
|
38
43
|
/**
|
|
39
44
|
* Whatever `prisma` instance the host injects. We index into it by model
|
|
@@ -52,14 +57,14 @@ export type PrismaLike = {
|
|
|
52
57
|
[K: string]: any;
|
|
53
58
|
};
|
|
54
59
|
export declare const DEFAULT_MODELS: {
|
|
55
|
-
readonly log:
|
|
56
|
-
readonly webhook:
|
|
57
|
-
readonly webhookDelivery:
|
|
58
|
-
readonly config:
|
|
59
|
-
readonly history:
|
|
60
|
-
readonly aiTask:
|
|
61
|
-
readonly aiTaskEvent:
|
|
62
|
-
readonly cache:
|
|
60
|
+
readonly log: 'maLog';
|
|
61
|
+
readonly webhook: 'maWebhook';
|
|
62
|
+
readonly webhookDelivery: 'maWebhookDelivery';
|
|
63
|
+
readonly config: 'maConfig';
|
|
64
|
+
readonly history: 'maHistory';
|
|
65
|
+
readonly aiTask: 'maAiTask';
|
|
66
|
+
readonly aiTaskEvent: 'maAiTaskEvent';
|
|
67
|
+
readonly cache: 'maCache';
|
|
63
68
|
};
|
|
64
69
|
export type ModelKey = keyof typeof DEFAULT_MODELS;
|
|
65
70
|
export type ModelOverrides = Partial<Record<ModelKey, string>>;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,WAAW,cAAc,CAAC,IAAI,GAAG,GAAG;IACxC,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;IACrC,UAAU,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;IACtD,SAAS,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;IAC3C,MAAM,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C,MAAM,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,GAAG,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtD,MAAM,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,GAAG,CAAC;QAAC,MAAM,EAAE,GAAG,CAAC;QAAC,MAAM,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrE,MAAM,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3C,UAAU,CAAC,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC9D,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,WAAW,cAAc,CAAC,IAAI,GAAG,GAAG;IACxC,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;IACrC,UAAU,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;IACtD,SAAS,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;IAC3C,MAAM,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C,MAAM,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,GAAG,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtD,MAAM,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,GAAG,CAAC;QAAC,MAAM,EAAE,GAAG,CAAC;QAAC,MAAM,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrE,MAAM,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3C,UAAU,CAAC,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC9D,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAClC;;;OAGG;IACH,OAAO,CAAC,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;CACpC;AAED;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,UAAU,GAAG;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,CAAA;AAE7C,eAAO,MAAM,cAAc;aACzB,GAAG,EAAE,OAAO;aACZ,OAAO,EAAE,WAAW;aACpB,eAAe,EAAE,mBAAmB;aACpC,MAAM,EAAE,UAAU;aAClB,OAAO,EAAE,WAAW;aACpB,MAAM,EAAE,UAAU;aAClB,WAAW,EAAE,eAAe;aAC5B,KAAK,EAAE,SAAS;CACR,CAAA;AAEV,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,cAAc,CAAA;AAClD,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAA;AAE9D,wBAAgB,eAAe,CAC7B,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,QAAQ,EACb,SAAS,EAAE,cAAc,GAAG,SAAS,GACpC,cAAc,CAWhB"}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAqCH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,GAAG,EAAE,OAAO;IACZ,OAAO,EAAE,WAAW;IACpB,eAAe,EAAE,mBAAmB;IACpC,MAAM,EAAE,UAAU;IAClB,OAAO,EAAE,WAAW;IACpB,MAAM,EAAE,UAAU;IAClB,WAAW,EAAE,eAAe;IAC5B,KAAK,EAAE,SAAS;CACR,CAAA;AAKV,MAAM,UAAU,eAAe,CAC7B,MAAkB,EAClB,GAAa,EACb,SAAqC;IAErC,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC,GAAG,CAAC,IAAI,cAAc,CAAC,GAAG,CAAC,CAAA;IACpD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAA+B,CAAA;IAC3D,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CACb,yDAAyD,IAAI,KAAK;YAClE,+EAA+E;YAC/E,0FAA0F,CAC3F,CAAA;IACH,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modern-admin/system-prisma",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Prisma-backed implementation of Modern Admin system stores (logs, history, webhooks, AI tasks).",
|
|
5
5
|
"homepage": "https://github.com/modern-admin/modern-admin/tree/main/packages/system-prisma#readme",
|
|
6
6
|
"keywords": [
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"files": [
|
|
31
31
|
"dist",
|
|
32
|
-
"src"
|
|
32
|
+
"src",
|
|
33
|
+
"prisma"
|
|
33
34
|
],
|
|
34
35
|
"publishConfig": {
|
|
35
36
|
"registry": "https://registry.npmjs.org",
|
|
@@ -44,15 +45,15 @@
|
|
|
44
45
|
}
|
|
45
46
|
},
|
|
46
47
|
"dependencies": {
|
|
47
|
-
"@modern-admin/core": "^0.
|
|
48
|
+
"@modern-admin/core": "^0.7.0"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@modern-admin/tsconfig": "0.4.0",
|
|
51
|
-
"@types/bun": "^1.3.
|
|
52
|
-
"typescript": "^
|
|
52
|
+
"@types/bun": "^1.3.14",
|
|
53
|
+
"typescript": "^7.0.2"
|
|
53
54
|
},
|
|
54
55
|
"peerDependencies": {
|
|
55
|
-
"@prisma/client": "
|
|
56
|
+
"@prisma/client": "^7.9.1"
|
|
56
57
|
},
|
|
57
58
|
"peerDependenciesMeta": {
|
|
58
59
|
"@prisma/client": {
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
-- Better Auth 1.6 -> 1.7 account identity migration (PostgreSQL / Prisma).
|
|
2
|
+
--
|
|
3
|
+
-- Run this during a maintenance window with every authentication writer
|
|
4
|
+
-- stopped. The ACCESS EXCLUSIVE lock is a final fence, not a substitute for
|
|
5
|
+
-- draining application traffic. Review the provider inventory NOTICE before
|
|
6
|
+
-- applying this in production and extend the explicit mapping only after
|
|
7
|
+
-- verifying the provider's authoritative issuer.
|
|
8
|
+
--
|
|
9
|
+
-- Prisma keeps these fields camel-cased in SQL. Drizzle installations must use
|
|
10
|
+
-- the sibling migration shipped by @modern-admin/system-drizzle instead.
|
|
11
|
+
|
|
12
|
+
BEGIN;
|
|
13
|
+
|
|
14
|
+
SET LOCAL lock_timeout = '10s';
|
|
15
|
+
LOCK TABLE "ma_account" IN ACCESS EXCLUSIVE MODE;
|
|
16
|
+
|
|
17
|
+
-- Add nullable first. NOT NULL is applied only after every row is backfilled.
|
|
18
|
+
ALTER TABLE "ma_account" ADD COLUMN "issuer" TEXT;
|
|
19
|
+
|
|
20
|
+
-- Inventory the legacy provider ids without trusting any request/user data.
|
|
21
|
+
DO $$
|
|
22
|
+
DECLARE
|
|
23
|
+
provider_inventory TEXT;
|
|
24
|
+
BEGIN
|
|
25
|
+
SELECT string_agg(format('%s (%s rows)', "providerId", row_count), ', ' ORDER BY "providerId")
|
|
26
|
+
INTO provider_inventory
|
|
27
|
+
FROM (
|
|
28
|
+
SELECT "providerId", count(*) AS row_count
|
|
29
|
+
FROM "ma_account"
|
|
30
|
+
GROUP BY "providerId"
|
|
31
|
+
) AS inventory;
|
|
32
|
+
|
|
33
|
+
RAISE NOTICE 'Better Auth account provider inventory: %',
|
|
34
|
+
coalesce(provider_inventory, '<empty>');
|
|
35
|
+
END $$;
|
|
36
|
+
|
|
37
|
+
-- These are the providers configured by the Modern Admin reference app.
|
|
38
|
+
-- Google exposes a verified authoritative issuer. Better Auth 1.7's built-in
|
|
39
|
+
-- GitHub provider has no authoritative account issuer and deliberately uses
|
|
40
|
+
-- local:oauth:<encodeURIComponent(providerId)> ("github" needs no escaping).
|
|
41
|
+
-- Any other provider must be mapped deliberately after checking the installed
|
|
42
|
+
-- Better Auth 1.7 provider definition; never infer an issuer from user data,
|
|
43
|
+
-- request input, display names, email domains, or an unverified endpoint.
|
|
44
|
+
DO $$
|
|
45
|
+
DECLARE
|
|
46
|
+
unknown_providers TEXT;
|
|
47
|
+
BEGIN
|
|
48
|
+
SELECT string_agg(DISTINCT "providerId", ', ' ORDER BY "providerId")
|
|
49
|
+
INTO unknown_providers
|
|
50
|
+
FROM "ma_account"
|
|
51
|
+
WHERE "providerId" NOT IN ('credential', 'google', 'github');
|
|
52
|
+
|
|
53
|
+
IF unknown_providers IS NOT NULL THEN
|
|
54
|
+
RAISE EXCEPTION USING
|
|
55
|
+
MESSAGE = 'Better Auth 1.7 migration stopped: unknown account providers: ' || unknown_providers,
|
|
56
|
+
HINT = 'Verify each provider issuer, add an explicit mapping to this migration, and rerun the whole transaction.';
|
|
57
|
+
END IF;
|
|
58
|
+
END $$;
|
|
59
|
+
|
|
60
|
+
-- A credential identity is bound to the stable linked user id in Better Auth
|
|
61
|
+
-- 1.7. Refuse malformed legacy rows instead of silently relinking an account.
|
|
62
|
+
DO $$
|
|
63
|
+
DECLARE
|
|
64
|
+
malformed_credentials TEXT;
|
|
65
|
+
BEGIN
|
|
66
|
+
SELECT string_agg("id", ', ' ORDER BY "id")
|
|
67
|
+
INTO malformed_credentials
|
|
68
|
+
FROM "ma_account"
|
|
69
|
+
WHERE "providerId" = 'credential'
|
|
70
|
+
AND ("accountId" <> "userId" OR "password" IS NULL);
|
|
71
|
+
|
|
72
|
+
IF malformed_credentials IS NOT NULL THEN
|
|
73
|
+
RAISE EXCEPTION USING
|
|
74
|
+
MESSAGE = 'Better Auth 1.7 migration stopped: malformed credential account rows: ' || malformed_credentials,
|
|
75
|
+
HINT = 'Repair accountId/userId linkage or the missing password hash before rerunning; do not reset or rehash valid passwords.';
|
|
76
|
+
END IF;
|
|
77
|
+
END $$;
|
|
78
|
+
|
|
79
|
+
-- Detect projected identity collisions before changing any row. Never merge or
|
|
80
|
+
-- delete users automatically: an operator must resolve each collision.
|
|
81
|
+
DO $$
|
|
82
|
+
DECLARE
|
|
83
|
+
identity_collisions TEXT;
|
|
84
|
+
BEGIN
|
|
85
|
+
WITH projected AS (
|
|
86
|
+
SELECT
|
|
87
|
+
"id",
|
|
88
|
+
"accountId",
|
|
89
|
+
CASE "providerId"
|
|
90
|
+
WHEN 'credential' THEN 'local:credential'
|
|
91
|
+
WHEN 'google' THEN 'https://accounts.google.com'
|
|
92
|
+
WHEN 'github' THEN 'local:oauth:github'
|
|
93
|
+
END AS projected_issuer
|
|
94
|
+
FROM "ma_account"
|
|
95
|
+
), collisions AS (
|
|
96
|
+
SELECT
|
|
97
|
+
projected_issuer,
|
|
98
|
+
"accountId",
|
|
99
|
+
string_agg("id", ', ' ORDER BY "id") AS row_ids
|
|
100
|
+
FROM projected
|
|
101
|
+
GROUP BY projected_issuer, "accountId"
|
|
102
|
+
HAVING count(*) > 1
|
|
103
|
+
)
|
|
104
|
+
SELECT string_agg(
|
|
105
|
+
format('(%s, %s) rows [%s]', projected_issuer, "accountId", row_ids),
|
|
106
|
+
'; '
|
|
107
|
+
ORDER BY projected_issuer, "accountId"
|
|
108
|
+
)
|
|
109
|
+
INTO identity_collisions
|
|
110
|
+
FROM collisions;
|
|
111
|
+
|
|
112
|
+
IF identity_collisions IS NOT NULL THEN
|
|
113
|
+
RAISE EXCEPTION USING
|
|
114
|
+
MESSAGE = 'Better Auth 1.7 migration stopped: duplicate (issuer, accountId) identities: ' || identity_collisions,
|
|
115
|
+
HINT = 'Resolve ownership manually. This migration will not merge accounts or delete users.';
|
|
116
|
+
END IF;
|
|
117
|
+
END $$;
|
|
118
|
+
|
|
119
|
+
UPDATE "ma_account"
|
|
120
|
+
SET "issuer" = CASE "providerId"
|
|
121
|
+
WHEN 'credential' THEN 'local:credential'
|
|
122
|
+
WHEN 'google' THEN 'https://accounts.google.com'
|
|
123
|
+
WHEN 'github' THEN 'local:oauth:github'
|
|
124
|
+
END;
|
|
125
|
+
|
|
126
|
+
DO $$
|
|
127
|
+
BEGIN
|
|
128
|
+
IF EXISTS (SELECT 1 FROM "ma_account" WHERE "issuer" IS NULL) THEN
|
|
129
|
+
RAISE EXCEPTION 'Better Auth 1.7 migration stopped: issuer backfill is incomplete';
|
|
130
|
+
END IF;
|
|
131
|
+
END $$;
|
|
132
|
+
|
|
133
|
+
ALTER TABLE "ma_account" ALTER COLUMN "issuer" SET NOT NULL;
|
|
134
|
+
ALTER TABLE "ma_account"
|
|
135
|
+
ADD CONSTRAINT "ma_account_issuer_accountId_key" UNIQUE ("issuer", "accountId");
|
|
136
|
+
CREATE INDEX "ma_account_userId_idx" ON "ma_account" ("userId");
|
|
137
|
+
|
|
138
|
+
COMMIT;
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
// Modern Admin — system tables (Prisma fragment)
|
|
2
|
+
//
|
|
3
|
+
// Copy these models into your application's `schema.prisma` (or include via
|
|
4
|
+
// Prisma's multi-file schema). They power the auth & runtime subsystems
|
|
5
|
+
// shipped in `@modern-admin/system-prisma`:
|
|
6
|
+
//
|
|
7
|
+
// Better Auth (logical name → physical table):
|
|
8
|
+
// user → ma_user (MaUser)
|
|
9
|
+
// session → ma_session (MaSession)
|
|
10
|
+
// account → ma_account (MaAccount)
|
|
11
|
+
// verification → ma_verification (MaVerification)
|
|
12
|
+
// apikey → ma_apikey (MaApiKey, from api-key plugin)
|
|
13
|
+
//
|
|
14
|
+
// Modern Admin core:
|
|
15
|
+
// - MaRole → configurable roles + permissions matrix
|
|
16
|
+
// (consumed by `ModernAdmin.invoke()` when
|
|
17
|
+
// `rolesResourceId` is set)
|
|
18
|
+
// - MaLog → action log (feature-logging)
|
|
19
|
+
// - MaWebhook → outgoing webhooks subscriptions
|
|
20
|
+
// - MaWebhookDelivery → delivery attempts
|
|
21
|
+
// - MaConfig → key-value config (global / user / resource)
|
|
22
|
+
// - MaHistory → record revisions
|
|
23
|
+
// - MaAiTask → AI task queue + results
|
|
24
|
+
// - MaAiTaskEvent → streaming events for AI tasks
|
|
25
|
+
// - MaCache → SQL fallback cache (when Redis isn't configured)
|
|
26
|
+
//
|
|
27
|
+
// Why Better Auth tables live here too: Modern Admin remaps Better Auth's
|
|
28
|
+
// logical names to its `ma_*` namespace via `modelName: 'ma_user'` (etc.)
|
|
29
|
+
// in the auth-builder, so the *physical* shapes are part of Modern Admin's
|
|
30
|
+
// contract. Keeping them in this fragment means the bundled CLI (`bunx
|
|
31
|
+
// @modern-admin/create generate`) can patch a host
|
|
32
|
+
// `schema.prisma` with everything required in one shot — admins don't
|
|
33
|
+
// need to run `@better-auth/cli generate` separately. Should you upgrade
|
|
34
|
+
// Better Auth and find that the upstream column set drifted, re-run
|
|
35
|
+
// the generator and reconcile the diff.
|
|
36
|
+
//
|
|
37
|
+
// Naming: every table is prefixed with `ma_`. To use a different prefix,
|
|
38
|
+
// edit the `@@map(...)` directives below. To put everything in a separate
|
|
39
|
+
// Postgres schema, add `@@schema("admin")` (and enable `multiSchema` in
|
|
40
|
+
// generator client). The Prisma model identifiers (MaLog, MaWebhook, …)
|
|
41
|
+
// stay the same — runtime stores look them up by Prisma's default
|
|
42
|
+
// camelCase delegate names (`prisma.maLog`, `prisma.maWebhook`, …) unless
|
|
43
|
+
// you override `models.*` in `setupPrismaSystem(prisma, { models })`.
|
|
44
|
+
//
|
|
45
|
+
// All flexible-shape fields are `Json` for portability across Postgres,
|
|
46
|
+
// MySQL, and SQLite. Validation runs at the application boundary via the
|
|
47
|
+
// Zod schemas in `@modern-admin/core/system/schemas`.
|
|
48
|
+
|
|
49
|
+
// ─── Better Auth ──────────────────────────────────────────────────────────
|
|
50
|
+
//
|
|
51
|
+
// Logical Better Auth tables remapped to physical `ma_*` names. Better
|
|
52
|
+
// Auth's admin plugin contributes the `role`/`banned`/`banReason`/
|
|
53
|
+
// `banExpires` columns on MaUser; the api-key plugin contributes
|
|
54
|
+
// MaApiKey. Both are declared explicitly here so Prisma codegen produces
|
|
55
|
+
// accurate types without `prisma db pull` after every Better Auth release.
|
|
56
|
+
|
|
57
|
+
model MaUser {
|
|
58
|
+
id String @id @default(uuid())
|
|
59
|
+
name String
|
|
60
|
+
email String @unique
|
|
61
|
+
emailVerified Boolean @default(false)
|
|
62
|
+
image String?
|
|
63
|
+
/// Role id (= name); resolves to a row in `ma_role` for permissions lookup.
|
|
64
|
+
role String?
|
|
65
|
+
banned Boolean? @default(false)
|
|
66
|
+
banReason String?
|
|
67
|
+
banExpires DateTime?
|
|
68
|
+
createdAt DateTime @default(now())
|
|
69
|
+
updatedAt DateTime @updatedAt
|
|
70
|
+
|
|
71
|
+
sessions MaSession[]
|
|
72
|
+
accounts MaAccount[]
|
|
73
|
+
apiKeys MaApiKey[]
|
|
74
|
+
|
|
75
|
+
@@map("ma_user")
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
model MaSession {
|
|
79
|
+
id String @id @default(uuid())
|
|
80
|
+
expiresAt DateTime
|
|
81
|
+
token String @unique
|
|
82
|
+
ipAddress String?
|
|
83
|
+
userAgent String?
|
|
84
|
+
userId String
|
|
85
|
+
/// Active impersonation source; admin plugin uses this to track
|
|
86
|
+
/// "log in as" sessions so audit logs can attribute the real actor.
|
|
87
|
+
impersonatedBy String?
|
|
88
|
+
|
|
89
|
+
user MaUser @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
90
|
+
|
|
91
|
+
createdAt DateTime @default(now())
|
|
92
|
+
updatedAt DateTime @updatedAt
|
|
93
|
+
|
|
94
|
+
@@map("ma_session")
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
model MaAccount {
|
|
98
|
+
id String @id @default(uuid())
|
|
99
|
+
issuer String
|
|
100
|
+
accountId String
|
|
101
|
+
providerId String
|
|
102
|
+
userId String
|
|
103
|
+
accessToken String?
|
|
104
|
+
refreshToken String?
|
|
105
|
+
idToken String?
|
|
106
|
+
accessTokenExpiresAt DateTime?
|
|
107
|
+
refreshTokenExpiresAt DateTime?
|
|
108
|
+
scope String?
|
|
109
|
+
/// Hashed password for the email/password strategy. OAuth/passkey rows
|
|
110
|
+
/// leave this null — credentials are encoded in the provider tokens.
|
|
111
|
+
password String?
|
|
112
|
+
|
|
113
|
+
user MaUser @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
114
|
+
|
|
115
|
+
createdAt DateTime @default(now())
|
|
116
|
+
updatedAt DateTime @updatedAt
|
|
117
|
+
|
|
118
|
+
@@unique([issuer, accountId])
|
|
119
|
+
@@index([userId])
|
|
120
|
+
@@map("ma_account")
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
model MaVerification {
|
|
124
|
+
id String @id @default(uuid())
|
|
125
|
+
identifier String
|
|
126
|
+
value String
|
|
127
|
+
expiresAt DateTime
|
|
128
|
+
createdAt DateTime @default(now())
|
|
129
|
+
updatedAt DateTime @updatedAt
|
|
130
|
+
|
|
131
|
+
@@map("ma_verification")
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// Better Auth api-key plugin storage. `permissions` is a JSON object
|
|
135
|
+
/// `{ resourceId: action[] }` consumed by `ModernAdmin.invoke()`'s
|
|
136
|
+
/// api-key gate (see also `MaRole.permissions` for the role-based gate —
|
|
137
|
+
/// both share the same matching helper).
|
|
138
|
+
model MaApiKey {
|
|
139
|
+
id String @id @default(uuid())
|
|
140
|
+
configId String @default("default")
|
|
141
|
+
name String?
|
|
142
|
+
start String?
|
|
143
|
+
prefix String?
|
|
144
|
+
key String
|
|
145
|
+
referenceId String
|
|
146
|
+
refillInterval Int?
|
|
147
|
+
refillAmount Int?
|
|
148
|
+
lastRefillAt DateTime?
|
|
149
|
+
enabled Boolean @default(true)
|
|
150
|
+
rateLimitEnabled Boolean @default(false)
|
|
151
|
+
rateLimitTimeWindow Int?
|
|
152
|
+
rateLimitMax Int?
|
|
153
|
+
requestCount Int @default(0)
|
|
154
|
+
remaining Int?
|
|
155
|
+
lastRequest DateTime?
|
|
156
|
+
expiresAt DateTime?
|
|
157
|
+
createdAt DateTime @default(now())
|
|
158
|
+
updatedAt DateTime @updatedAt
|
|
159
|
+
permissions Json?
|
|
160
|
+
metadata Json?
|
|
161
|
+
|
|
162
|
+
user MaUser @relation(fields: [referenceId], references: [id], onDelete: Cascade)
|
|
163
|
+
|
|
164
|
+
@@index([configId])
|
|
165
|
+
@@index([referenceId])
|
|
166
|
+
@@map("ma_apikey")
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ─── Modern Admin core ────────────────────────────────────────────────────
|
|
170
|
+
|
|
171
|
+
/// Configurable role with a permissions matrix.
|
|
172
|
+
///
|
|
173
|
+
/// `permissions` shape: `Record<resourceId, action[]>`, where `'*'` is a
|
|
174
|
+
/// wildcard for actions or for a resource key (matches every resource).
|
|
175
|
+
/// The `id` doubles as the user-visible role name — there is no separate
|
|
176
|
+
/// `name` column. The string in `<panel-user>.role` (e.g. Better Auth's
|
|
177
|
+
/// `ma_user.role`) references this `id` so the auth provider can resolve
|
|
178
|
+
/// the row and attach `permissions` onto the principal for
|
|
179
|
+
/// `ModernAdmin.invoke()` to gate every action.
|
|
180
|
+
///
|
|
181
|
+
/// Renames are not supported (would orphan every panel admin holding the
|
|
182
|
+
/// role); Prisma treats `@id` columns as immutable, so the storage layer
|
|
183
|
+
/// enforces this for free. To "rename", clone under a new id and reassign.
|
|
184
|
+
model MaRole {
|
|
185
|
+
/// Application code supplies this on insert (`'admin'`, `'editor'`, …)
|
|
186
|
+
/// — there is no `@default(...)` because it's a meaningful business id,
|
|
187
|
+
/// not a surrogate UUID.
|
|
188
|
+
id String @id
|
|
189
|
+
description String?
|
|
190
|
+
permissions Json @default("{}")
|
|
191
|
+
/// Built-in roles are seeded on boot and cannot be deleted via the UI.
|
|
192
|
+
isBuiltin Boolean @default(false)
|
|
193
|
+
createdAt DateTime @default(now())
|
|
194
|
+
updatedAt DateTime @updatedAt
|
|
195
|
+
|
|
196
|
+
@@map("ma_role")
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
model MaLog {
|
|
200
|
+
id String @id @default(uuid())
|
|
201
|
+
resourceId String
|
|
202
|
+
action String
|
|
203
|
+
recordId String?
|
|
204
|
+
recordIds Json?
|
|
205
|
+
userId String?
|
|
206
|
+
payload Json?
|
|
207
|
+
result Json?
|
|
208
|
+
/// Unix-ms timestamp captured at the after-hook.
|
|
209
|
+
at BigInt
|
|
210
|
+
createdAt DateTime @default(now())
|
|
211
|
+
|
|
212
|
+
@@index([resourceId, action])
|
|
213
|
+
@@index([userId])
|
|
214
|
+
@@index([createdAt])
|
|
215
|
+
@@map("ma_log")
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
model MaWebhook {
|
|
219
|
+
id String @id @default(uuid())
|
|
220
|
+
name String
|
|
221
|
+
url String
|
|
222
|
+
/// JSON array of dotted event names. Validated by `webhookZ`.
|
|
223
|
+
events Json
|
|
224
|
+
/// Optional resource/model scope. Null means all resources.
|
|
225
|
+
resourceId String?
|
|
226
|
+
enabled Boolean @default(true)
|
|
227
|
+
secret String?
|
|
228
|
+
/// JSON object of extra HTTP headers.
|
|
229
|
+
headers Json @default("{}")
|
|
230
|
+
/// List-page-style filters matched against the mutation snapshot.
|
|
231
|
+
filters Json @default("{}")
|
|
232
|
+
/// Optional JSON array of field paths to include in outgoing payload.
|
|
233
|
+
payloadFields Json @default("[]")
|
|
234
|
+
createdAt DateTime @default(now())
|
|
235
|
+
updatedAt DateTime @updatedAt
|
|
236
|
+
|
|
237
|
+
deliveries MaWebhookDelivery[]
|
|
238
|
+
|
|
239
|
+
@@map("ma_webhook")
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
model MaWebhookDelivery {
|
|
243
|
+
id String @id @default(uuid())
|
|
244
|
+
webhookId String
|
|
245
|
+
event String
|
|
246
|
+
payload Json
|
|
247
|
+
status String
|
|
248
|
+
responseStatus Int?
|
|
249
|
+
responseBody String?
|
|
250
|
+
error String?
|
|
251
|
+
attempt Int @default(1)
|
|
252
|
+
createdAt DateTime @default(now())
|
|
253
|
+
deliveredAt DateTime?
|
|
254
|
+
|
|
255
|
+
webhook MaWebhook @relation(fields: [webhookId], references: [id], onDelete: Cascade)
|
|
256
|
+
|
|
257
|
+
@@index([webhookId, createdAt])
|
|
258
|
+
@@map("ma_webhook_delivery")
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
model MaConfig {
|
|
262
|
+
/// Surrogate key. Application code MUST generate this with `uuidv7()`
|
|
263
|
+
/// from `@modern-admin/core` before insert (Prisma's `@default(uuid())`
|
|
264
|
+
/// generates v4, which the project policy disallows).
|
|
265
|
+
id String @id
|
|
266
|
+
scope String
|
|
267
|
+
/// Empty string for global, userId for user, resourceId for resource.
|
|
268
|
+
scopeId String @default("")
|
|
269
|
+
key String
|
|
270
|
+
value Json
|
|
271
|
+
updatedAt DateTime @updatedAt
|
|
272
|
+
|
|
273
|
+
@@unique([scope, scopeId, key])
|
|
274
|
+
@@map("ma_config")
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
model MaHistory {
|
|
278
|
+
id String @id @default(uuid())
|
|
279
|
+
resourceId String
|
|
280
|
+
recordId String
|
|
281
|
+
op String
|
|
282
|
+
userId String?
|
|
283
|
+
snapshot Json
|
|
284
|
+
/// Snapshot of the record _before_ this revision was applied. Used by
|
|
285
|
+
/// the history controller's revert endpoint to undo the change.
|
|
286
|
+
snapshotBefore Json?
|
|
287
|
+
createdAt DateTime @default(now())
|
|
288
|
+
|
|
289
|
+
@@index([resourceId, recordId, createdAt])
|
|
290
|
+
@@map("ma_history")
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
model MaAiTask {
|
|
294
|
+
id String @id @default(uuid())
|
|
295
|
+
kind String
|
|
296
|
+
resourceId String?
|
|
297
|
+
recordId String?
|
|
298
|
+
userId String?
|
|
299
|
+
status String
|
|
300
|
+
input Json @default("{}")
|
|
301
|
+
output Json?
|
|
302
|
+
error String?
|
|
303
|
+
/// 0..100 or null for indeterminate progress.
|
|
304
|
+
progress Int?
|
|
305
|
+
createdAt DateTime @default(now())
|
|
306
|
+
updatedAt DateTime @updatedAt
|
|
307
|
+
startedAt DateTime?
|
|
308
|
+
finishedAt DateTime?
|
|
309
|
+
|
|
310
|
+
events MaAiTaskEvent[]
|
|
311
|
+
|
|
312
|
+
@@index([kind, status])
|
|
313
|
+
@@index([userId])
|
|
314
|
+
@@index([resourceId, recordId])
|
|
315
|
+
@@map("ma_ai_task")
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
model MaAiTaskEvent {
|
|
319
|
+
id String @id @default(uuid())
|
|
320
|
+
taskId String
|
|
321
|
+
type String
|
|
322
|
+
data Json @default("{}")
|
|
323
|
+
createdAt DateTime @default(now())
|
|
324
|
+
|
|
325
|
+
task MaAiTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
|
|
326
|
+
|
|
327
|
+
@@index([taskId, createdAt])
|
|
328
|
+
@@map("ma_ai_task_event")
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
model MaCache {
|
|
332
|
+
key String @id
|
|
333
|
+
value Json
|
|
334
|
+
/// JSON array of tag strings.
|
|
335
|
+
tags Json @default("[]")
|
|
336
|
+
expiresAt DateTime?
|
|
337
|
+
createdAt DateTime @default(now())
|
|
338
|
+
updatedAt DateTime @updatedAt
|
|
339
|
+
|
|
340
|
+
@@index([expiresAt])
|
|
341
|
+
@@map("ma_cache")
|
|
342
|
+
}
|
|
@@ -3,11 +3,14 @@ import {
|
|
|
3
3
|
uuidv7,
|
|
4
4
|
type HistoryEntry,
|
|
5
5
|
type HistoryOp,
|
|
6
|
+
type HistoryRetention,
|
|
6
7
|
type HistoryRow,
|
|
7
8
|
type IHistoryStore,
|
|
8
9
|
} from '@modern-admin/core'
|
|
9
10
|
import type { PrismaDelegate } from '../types.js'
|
|
10
11
|
|
|
12
|
+
const PRUNE_BATCH_SIZE = 1_000
|
|
13
|
+
|
|
11
14
|
export class PrismaHistoryStore implements IHistoryStore {
|
|
12
15
|
constructor(private readonly delegate: PrismaDelegate<HistoryRow>) {}
|
|
13
16
|
|
|
@@ -63,4 +66,75 @@ export class PrismaHistoryStore implements IHistoryStore {
|
|
|
63
66
|
})
|
|
64
67
|
return row ? rowToEntry(row) : null
|
|
65
68
|
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Enforce a retention policy. Every revision of every record is kept
|
|
72
|
+
* forever without one — the table grows with each edit and nothing in the
|
|
73
|
+
* framework ever trims it.
|
|
74
|
+
*
|
|
75
|
+
* Bounds combine: a revision survives only if it satisfies all of them, so
|
|
76
|
+
* they are applied in sequence. `keepDays` is one `deleteMany`; `keepLast`
|
|
77
|
+
* is per-record, which no single `deleteMany` can express. Obsolete ids are
|
|
78
|
+
* selected and deleted in bounded batches. Deleting an explicit snapshot
|
|
79
|
+
* of ids is intentional: a concurrent append must never be caught by a
|
|
80
|
+
* broad `notIn` predicate and removed as if it were an old revision. This
|
|
81
|
+
* is a maintenance job (cron, startup), never a request-path operation.
|
|
82
|
+
*/
|
|
83
|
+
async prune(retention: HistoryRetention): Promise<number> {
|
|
84
|
+
let removed = 0
|
|
85
|
+
|
|
86
|
+
if (retention.keepDays !== undefined) {
|
|
87
|
+
const cutoff = new Date(Date.now() - retention.keepDays * 24 * 60 * 60 * 1000)
|
|
88
|
+
const { count } = await this.delegate.deleteMany({
|
|
89
|
+
where: { createdAt: { lt: cutoff } },
|
|
90
|
+
})
|
|
91
|
+
removed += count
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (retention.keepLast !== undefined) {
|
|
95
|
+
const keep = Math.max(0, Math.trunc(retention.keepLast))
|
|
96
|
+
const groups = await this.recordGroups()
|
|
97
|
+
for (const group of groups) {
|
|
98
|
+
const scope = { resourceId: group.resourceId, recordId: group.recordId }
|
|
99
|
+
while (true) {
|
|
100
|
+
const obsolete = (await this.delegate.findMany({
|
|
101
|
+
where: scope,
|
|
102
|
+
orderBy: [{ createdAt: 'desc' }, { id: 'desc' }],
|
|
103
|
+
select: { id: true },
|
|
104
|
+
...(keep > 0 ? { skip: keep } : {}),
|
|
105
|
+
take: PRUNE_BATCH_SIZE,
|
|
106
|
+
})) as unknown as Array<{ id: string }>
|
|
107
|
+
if (obsolete.length === 0) break
|
|
108
|
+
|
|
109
|
+
const { count } = await this.delegate.deleteMany({
|
|
110
|
+
where: { ...scope, id: { in: obsolete.map((row) => row.id) } },
|
|
111
|
+
})
|
|
112
|
+
removed += count
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return removed
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The distinct `(resourceId, recordId)` pairs that have revisions.
|
|
122
|
+
*
|
|
123
|
+
* `groupBy` because it becomes a real SQL `GROUP BY` on every connector,
|
|
124
|
+
* whereas Prisma's `distinct` is applied in the query engine — outside
|
|
125
|
+
* Postgres that means fetching every revision row first, which is exactly
|
|
126
|
+
* what pruning a large table must not do. `distinct` remains as a fallback
|
|
127
|
+
* for delegates that do not expose `groupBy` (hand-written test doubles).
|
|
128
|
+
*/
|
|
129
|
+
private async recordGroups(): Promise<Array<{ resourceId: string; recordId: string }>> {
|
|
130
|
+
if (typeof this.delegate.groupBy === 'function') {
|
|
131
|
+
return (await this.delegate.groupBy({
|
|
132
|
+
by: ['resourceId', 'recordId'],
|
|
133
|
+
})) as Array<{ resourceId: string; recordId: string }>
|
|
134
|
+
}
|
|
135
|
+
return (await this.delegate.findMany({
|
|
136
|
+
distinct: ['resourceId', 'recordId'],
|
|
137
|
+
select: { resourceId: true, recordId: true },
|
|
138
|
+
})) as unknown as Array<{ resourceId: string; recordId: string }>
|
|
139
|
+
}
|
|
66
140
|
}
|
package/src/stores/log-store.ts
CHANGED
|
@@ -8,6 +8,9 @@ import {
|
|
|
8
8
|
} from '@modern-admin/core'
|
|
9
9
|
import type { PrismaDelegate } from '../types.js'
|
|
10
10
|
|
|
11
|
+
/** Applied when the caller passes no `limit`. Matches `MemoryLogStore`. */
|
|
12
|
+
const DEFAULT_LIST_LIMIT = 50
|
|
13
|
+
|
|
11
14
|
export class PrismaLogStore implements IQueryableLogStore {
|
|
12
15
|
constructor(private readonly delegate: PrismaDelegate<LogRow>) {}
|
|
13
16
|
|
|
@@ -35,16 +38,40 @@ export class PrismaLogStore implements IQueryableLogStore {
|
|
|
35
38
|
if (filter.recordId) where['recordId'] = filter.recordId
|
|
36
39
|
if (filter.userId) where['userId'] = filter.userId
|
|
37
40
|
if (filter.actions?.length) where['action'] = { in: filter.actions }
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
// `from`/`to` bound the window; `before` is the keyset cursor the audit
|
|
42
|
+
// log's "load more" pages on. All three narrow the same column, so they
|
|
43
|
+
// merge into one range clause — dropping `before` (as this store used to)
|
|
44
|
+
// made every extra page return the first page again, forever.
|
|
45
|
+
const range: Record<string, bigint> = {}
|
|
46
|
+
if (filter.from) range['gte'] = BigInt(filter.from.getTime())
|
|
47
|
+
if (filter.to) range['lte'] = BigInt(filter.to.getTime())
|
|
48
|
+
if (filter.before !== undefined) {
|
|
49
|
+
const cursorAt = BigInt(filter.before)
|
|
50
|
+
if (filter.beforeId !== undefined) {
|
|
51
|
+
// Lexicographic continuation of the `(at, id) DESC` order. Keeping
|
|
52
|
+
// the id in the cursor is essential because `at` has ms precision and
|
|
53
|
+
// many audit entries can legitimately share it.
|
|
54
|
+
where['OR'] = [
|
|
55
|
+
{ at: { lt: cursorAt } },
|
|
56
|
+
{ at: cursorAt, id: { lt: filter.beforeId } },
|
|
57
|
+
]
|
|
58
|
+
} else {
|
|
59
|
+
// Backwards-compatible timestamp-only cursor for existing callers.
|
|
60
|
+
range['lt'] = cursorAt
|
|
61
|
+
}
|
|
43
62
|
}
|
|
63
|
+
if (Object.keys(range).length > 0) where['at'] = range
|
|
44
64
|
const rows = await this.delegate.findMany({
|
|
45
65
|
where,
|
|
46
|
-
|
|
47
|
-
|
|
66
|
+
// `at` is milliseconds and not unique, so it alone is not a total
|
|
67
|
+
// order — two entries in the same millisecond could swap between
|
|
68
|
+
// pages. Ids are UUIDv7, i.e. already time-ordered, so they are the
|
|
69
|
+
// natural tiebreaker.
|
|
70
|
+
orderBy: [{ at: 'desc' }, { id: 'desc' }],
|
|
71
|
+
// The audit log is append-only and unbounded; an unbounded SELECT over
|
|
72
|
+
// it is a memory hazard. Compare `PrismaHistoryStore.list`, which has
|
|
73
|
+
// always defaulted to 50.
|
|
74
|
+
take: filter.limit ?? DEFAULT_LIST_LIMIT,
|
|
48
75
|
...(filter.offset !== undefined ? { skip: filter.offset } : {}),
|
|
49
76
|
})
|
|
50
77
|
return rows.map(rowToLogEntry)
|
package/src/types.ts
CHANGED
|
@@ -20,6 +20,11 @@ export interface PrismaDelegate<TRow = any> {
|
|
|
20
20
|
delete(args: { where: any }): Promise<TRow>
|
|
21
21
|
deleteMany(args?: { where?: any }): Promise<{ count: number }>
|
|
22
22
|
count(args?: any): Promise<number>
|
|
23
|
+
/**
|
|
24
|
+
* Present on every generated Prisma delegate. Optional here so hand-written
|
|
25
|
+
* test doubles stay assignable; callers fall back when it is absent.
|
|
26
|
+
*/
|
|
27
|
+
groupBy?(args: any): Promise<any[]>
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
/**
|