@endora-commerce/mod-audit-logs 0.100.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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +55 -0
  3. package/dist/admin/index.d.ts +34 -0
  4. package/dist/admin/index.d.ts.map +1 -0
  5. package/dist/admin/index.js +39 -0
  6. package/dist/admin/index.js.map +1 -0
  7. package/dist/admin/pages/AuditLogViewer.d.ts +3 -0
  8. package/dist/admin/pages/AuditLogViewer.d.ts.map +1 -0
  9. package/dist/admin/pages/AuditLogViewer.js +112 -0
  10. package/dist/admin/pages/AuditLogViewer.js.map +1 -0
  11. package/dist/backend/cli/read.d.ts +20 -0
  12. package/dist/backend/cli/read.d.ts.map +1 -0
  13. package/dist/backend/cli/read.js +94 -0
  14. package/dist/backend/cli/read.js.map +1 -0
  15. package/dist/backend/commands/recent-activity-visibility.commands.d.ts +31 -0
  16. package/dist/backend/commands/recent-activity-visibility.commands.d.ts.map +1 -0
  17. package/dist/backend/commands/recent-activity-visibility.commands.js +55 -0
  18. package/dist/backend/commands/recent-activity-visibility.commands.js.map +1 -0
  19. package/dist/backend/index.d.ts +91 -0
  20. package/dist/backend/index.d.ts.map +1 -0
  21. package/dist/backend/index.js +117 -0
  22. package/dist/backend/index.js.map +1 -0
  23. package/dist/backend/routes.admin.d.ts +29 -0
  24. package/dist/backend/routes.admin.d.ts.map +1 -0
  25. package/dist/backend/routes.admin.js +85 -0
  26. package/dist/backend/routes.admin.js.map +1 -0
  27. package/dist/backend/routes.admin.recent-activity.d.ts +126 -0
  28. package/dist/backend/routes.admin.recent-activity.d.ts.map +1 -0
  29. package/dist/backend/routes.admin.recent-activity.js +106 -0
  30. package/dist/backend/routes.admin.recent-activity.js.map +1 -0
  31. package/dist/backend/services/audit-reference-registry.d.ts +28 -0
  32. package/dist/backend/services/audit-reference-registry.d.ts.map +1 -0
  33. package/dist/backend/services/audit-reference-registry.js +57 -0
  34. package/dist/backend/services/audit-reference-registry.js.map +1 -0
  35. package/dist/backend/services/recent-activity-catalog.d.ts +92 -0
  36. package/dist/backend/services/recent-activity-catalog.d.ts.map +1 -0
  37. package/dist/backend/services/recent-activity-catalog.js +82 -0
  38. package/dist/backend/services/recent-activity-catalog.js.map +1 -0
  39. package/dist/backend/services/recent-activity-service.d.ts +105 -0
  40. package/dist/backend/services/recent-activity-service.d.ts.map +1 -0
  41. package/dist/backend/services/recent-activity-service.js +308 -0
  42. package/dist/backend/services/recent-activity-service.js.map +1 -0
  43. package/dist/backend/services/recent-activity-visibility.d.ts +37 -0
  44. package/dist/backend/services/recent-activity-visibility.d.ts.map +1 -0
  45. package/dist/backend/services/recent-activity-visibility.js +81 -0
  46. package/dist/backend/services/recent-activity-visibility.js.map +1 -0
  47. package/dist/manifest.d.ts +191 -0
  48. package/dist/manifest.d.ts.map +1 -0
  49. package/dist/manifest.js +123 -0
  50. package/dist/manifest.js.map +1 -0
  51. package/docs/audit_logs.md +47 -0
  52. package/i18n/en.json +5 -0
  53. package/i18n/pl.json +5 -0
  54. package/package.json +86 -0
  55. package/tailwind.css +14 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recent-activity-visibility.d.ts","sourceRoot":"","sources":["../../../src/backend/services/recent-activity-visibility.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAiC1E,iDAAiD;AACjD,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,wBAAwB;IAEjC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,OAAO,EAAE,qBAAqB,EAC9B,QAAQ,EAAE,gBAAgB;IAG7C;;;;;;;;;;;;;OAaG;IACG,IAAI,IAAI,OAAO,CAAC,8BAA8B,EAAE,CAAC;IAiBvD;;;;;;OAMG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAS1C"}
@@ -0,0 +1,81 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * The operator axis of D-163.1 — feature 080, T042j.
4
+ *
5
+ * A module **declares** that its activity is eligible for the dashboard's
6
+ * recent-activity card (`manifest.ts`'s `recentActivity` export). The operator
7
+ * **chooses** whether it actually appears, and **the default is that it does**.
8
+ * Neither axis overwrites the other and neither is derivable from the other,
9
+ * which is Constitution XVII's shape applied to a narrower object.
10
+ *
11
+ * **This is a second per-module operator toggle beside activation, and that is
12
+ * deliberate.** They answer different questions about the same module —
13
+ * "does this client want stock management at all" versus "does this client want
14
+ * stock movements on their home screen" — so one control cannot carry both.
15
+ * They differ in kind too: activation is *presence*, resolved synchronously on
16
+ * the hot path by the kernel's registry cache and gating every seam the module
17
+ * owns; this is a *display* preference on one card, resolved asynchronously
18
+ * through `settingsReadPort` on the one request that renders it. That is why
19
+ * this is not built on the activation resolver: reading it there would put a
20
+ * dashboard preference into the presence machinery every request already
21
+ * consults.
22
+ *
23
+ * **Defaulting to visible has one consequence, stated so nobody meets it as a
24
+ * surprise** (D-163.1): a newly installed third-party module's activity reaches
25
+ * the shop owner's home screen without anyone choosing it. The alternative
26
+ * default makes every module's first useful signal depend on an operator
27
+ * knowing to go looking for a toggle whose existence the module cannot
28
+ * announce.
29
+ */
30
+ const visibleSchema = z.boolean();
31
+ export class RecentActivityVisibility {
32
+ catalog;
33
+ settings;
34
+ constructor(catalog, settings) {
35
+ this.catalog = catalog;
36
+ this.settings = settings;
37
+ }
38
+ /**
39
+ * Every eligible module with the operator's current answer.
40
+ *
41
+ * One batch read, `salesChannelId: null` — the choice is platform-wide, for
42
+ * the reason activation is (FR-009): a home dashboard is not per-storefront,
43
+ * and joining `setting_values` would reintroduce channel dependence through
44
+ * the back door (Constitution XII).
45
+ *
46
+ * A code the settings store does not know resolves to **visible**, not to
47
+ * hidden. The row is created by the boot reconcile for a shipped module and
48
+ * by `install` for a package, so its absence means one has not run yet — a
49
+ * platform mid-install, not an operator's decision — and the ruling's default
50
+ * is the honest answer to "nobody has chosen".
51
+ */
52
+ async list() {
53
+ const modules = this.catalog.eligibleModules();
54
+ if (modules.length === 0)
55
+ return [];
56
+ const rows = await this.settings.getMany(modules.map((entry) => entry.settingCode), null);
57
+ return modules.map((entry) => {
58
+ const row = rows.get(entry.settingCode);
59
+ return {
60
+ moduleId: entry.moduleId,
61
+ moduleName: entry.moduleName,
62
+ visible: row?.ok === true ? visibleSchema.safeParse(row.value).data ?? true : true,
63
+ };
64
+ });
65
+ }
66
+ /**
67
+ * The action tokens the card may query — the conjunction of the two axes.
68
+ *
69
+ * Empty when every eligible module is hidden, and the caller must not turn
70
+ * that into "no filter": an unfiltered query would put every audited write on
71
+ * the dashboard, which is the opposite of what the operator asked for.
72
+ */
73
+ async visibleActions() {
74
+ const visibility = new Map((await this.list()).map((entry) => [entry.moduleId, entry.visible]));
75
+ return this.catalog
76
+ .eligibleModules()
77
+ .filter((entry) => visibility.get(entry.moduleId) !== false)
78
+ .flatMap((entry) => [...entry.actions]);
79
+ }
80
+ }
81
+ //# sourceMappingURL=recent-activity-visibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recent-activity-visibility.js","sourceRoot":"","sources":["../../../src/backend/services/recent-activity-visibility.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,aAAa,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;AASlC,MAAM,OAAO,wBAAwB;IAEhB;IACA;IAFnB,YACmB,OAA8B,EAC9B,QAA0B;QAD1B,YAAO,GAAP,OAAO,CAAuB;QAC9B,aAAQ,GAAR,QAAQ,CAAkB;IAC1C,CAAC;IAEJ;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,IAAI;QACR,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;QAC/C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CACtC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,EACzC,IAAI,CACL,CAAC;QACF,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;YAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YACxC,OAAO;gBACL,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,OAAO,EAAE,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI;aACnF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,cAAc;QAClB,MAAM,UAAU,GAAG,IAAI,GAAG,CACxB,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CACpE,CAAC;QACF,OAAO,IAAI,CAAC,OAAO;aAChB,eAAe,EAAE;aACjB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,KAAK,CAAC;aAC3D,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5C,CAAC;CACF"}
@@ -0,0 +1,191 @@
1
+ import { type ModuleCliCommand } from '@endora-commerce/contracts';
2
+ import type { ModuleContext } from '@endora-commerce/platform/kernel';
3
+ /**
4
+ * Audit Logs module — manifest backfill (Module Lifecycle, feature 018).
5
+ *
6
+ * Predates the lifecycle system; this manifest is the static record
7
+ * required so the module participates in the registry. No install /
8
+ * uninstall hook today — the module's schema is owned by earlier
9
+ * platform-wide migrations.
10
+ *
11
+ * Feature 072 (T084) — the module owns its routes now, so it needs the two
12
+ * things a converted module needs: the dependency it actually resolves, and an
13
+ * activation control.
14
+ *
15
+ * `admin_users` is deliberately **not** a dependency. This module used to be
16
+ * mounted from inside that one, but the only thing it still wants from there is
17
+ * turning an actor id into a name, and that is an optional enrichment: the
18
+ * audit log degrades to raw ids rather than refusing. A `dependencies` entry
19
+ * would make the lifecycle refuse to disable `admin_users` while the audit log
20
+ * is on, which is backwards — the record has to outlive the directory.
21
+ */
22
+ export declare const manifest: {
23
+ id: string;
24
+ name: string;
25
+ version: string;
26
+ dependencies: string[];
27
+ description?: string | undefined;
28
+ acknowledgedDependencies?: {
29
+ moduleId: string;
30
+ port: string;
31
+ reason: string;
32
+ }[] | undefined;
33
+ nonBindingDependencies?: {
34
+ moduleId: string;
35
+ name: string;
36
+ kind: "contributes-to" | "degrades-without" | "refuses-without";
37
+ reason: string;
38
+ whenAbsent?: string | undefined;
39
+ }[] | undefined;
40
+ activation?: {
41
+ settingCode: string;
42
+ default: boolean;
43
+ } | {
44
+ nonDeactivatable: true;
45
+ reason: string;
46
+ } | undefined;
47
+ settings?: {
48
+ moduleCode: string;
49
+ groups: {
50
+ code: string;
51
+ name: string;
52
+ salesChannelCodes?: string[] | undefined;
53
+ isSystemProtected?: boolean | undefined;
54
+ }[];
55
+ settings: {
56
+ code: string;
57
+ name: string;
58
+ valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
59
+ defaultValue: unknown;
60
+ description?: string | undefined;
61
+ groupCode?: string | undefined;
62
+ previousDefaultValues?: unknown[] | undefined;
63
+ salesChannelCodes?: string[] | undefined;
64
+ enumOptions?: string[] | undefined;
65
+ configurationType?: string | undefined;
66
+ hidden?: boolean | undefined;
67
+ }[];
68
+ } | undefined;
69
+ i18n?: {
70
+ bundlesDir: string;
71
+ } | undefined;
72
+ docs?: false | {
73
+ dir: string;
74
+ } | undefined;
75
+ demo?: false | {
76
+ summary: string;
77
+ seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
78
+ reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
79
+ after?: readonly string[] | undefined;
80
+ package?: string | undefined;
81
+ } | undefined;
82
+ actions?: {
83
+ id: string;
84
+ labelKey: string;
85
+ icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
86
+ targetRoute: string;
87
+ keywords: string[];
88
+ weight: number;
89
+ descriptionKey?: string | undefined;
90
+ requiredPermission?: string | undefined;
91
+ }[] | undefined;
92
+ permissions?: {
93
+ code: string;
94
+ label: string;
95
+ module?: string | undefined;
96
+ description?: string | undefined;
97
+ requires?: string[] | undefined;
98
+ }[] | undefined;
99
+ transactionalEmails?: {
100
+ code: string;
101
+ name: string;
102
+ variables: {
103
+ key: string;
104
+ label: string;
105
+ sampleValue?: string | undefined;
106
+ description?: string | undefined;
107
+ }[];
108
+ description?: string | undefined;
109
+ group?: string | undefined;
110
+ }[] | undefined;
111
+ capabilities?: string[] | undefined;
112
+ exclusiveCapabilities?: {
113
+ key: string;
114
+ errorCode: string;
115
+ }[] | undefined;
116
+ errorCodes?: {
117
+ code: string;
118
+ tokens?: string[] | undefined;
119
+ }[] | undefined;
120
+ blocks?: {
121
+ name: string;
122
+ labelKey: string;
123
+ category: string;
124
+ contexts: ("invoice" | "email" | "cms" | "newsletter")[];
125
+ fields: Record<string, {
126
+ type: "number" | "object" | "uuid" | "array" | "text" | "textarea" | "select" | "radio" | "external" | "richtext";
127
+ label?: string | undefined;
128
+ required?: boolean | undefined;
129
+ options?: {
130
+ label: string;
131
+ value: string | number;
132
+ }[] | undefined;
133
+ refKind?: string | undefined;
134
+ }>;
135
+ descriptionKey?: string | undefined;
136
+ defaultProps?: Record<string, unknown> | undefined;
137
+ responsiveFields?: string[] | undefined;
138
+ previewIcon?: string | undefined;
139
+ weight?: number | undefined;
140
+ }[] | undefined;
141
+ blockCategories?: {
142
+ key: string;
143
+ titleKey: string;
144
+ contexts: ("invoice" | "email" | "cms" | "newsletter")[];
145
+ weight?: number | undefined;
146
+ visible?: boolean | undefined;
147
+ }[] | undefined;
148
+ env?: {
149
+ name: string;
150
+ describes: {
151
+ en: string;
152
+ pl: string;
153
+ };
154
+ requirement: {
155
+ kind: "required";
156
+ } | {
157
+ kind: "requiredWhen";
158
+ input: string;
159
+ equals: string;
160
+ } | {
161
+ kind: "optional";
162
+ without: {
163
+ en: string;
164
+ pl: string;
165
+ };
166
+ };
167
+ secret: boolean;
168
+ generable: boolean;
169
+ owner: {
170
+ kind: "platform";
171
+ } | {
172
+ kind: "application";
173
+ application: "admin" | "backend" | "storefront";
174
+ } | {
175
+ kind: "module";
176
+ moduleId: string;
177
+ };
178
+ consumers: ("admin" | "backend" | "storefront")[];
179
+ addressOf: "admin" | "backend" | "storefront" | null;
180
+ }[] | undefined;
181
+ };
182
+ /**
183
+ * The operator command this module declares — feature 080, T042b / D-160.9.
184
+ *
185
+ * It was `scripts/read.ts`, which owned its own `initOrm()`. Only the plumbing
186
+ * moved: the `EntityManager` comes off the composition now, and the reach is
187
+ * still one read of the kernel's `AuditLogEntry`. The conditions D-102 attached
188
+ * to this tool are unchanged and are restated at the top of `cli/read.ts`.
189
+ */
190
+ export declare const cliCommands: ReadonlyArray<ModuleCliCommand<ModuleContext>>;
191
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACzF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqEnB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAyBtE,CAAC"}
@@ -0,0 +1,123 @@
1
+ import { defineModuleManifest } from '@endora-commerce/contracts';
2
+ /**
3
+ * Audit Logs module — manifest backfill (Module Lifecycle, feature 018).
4
+ *
5
+ * Predates the lifecycle system; this manifest is the static record
6
+ * required so the module participates in the registry. No install /
7
+ * uninstall hook today — the module's schema is owned by earlier
8
+ * platform-wide migrations.
9
+ *
10
+ * Feature 072 (T084) — the module owns its routes now, so it needs the two
11
+ * things a converted module needs: the dependency it actually resolves, and an
12
+ * activation control.
13
+ *
14
+ * `admin_users` is deliberately **not** a dependency. This module used to be
15
+ * mounted from inside that one, but the only thing it still wants from there is
16
+ * turning an actor id into a name, and that is an optional enrichment: the
17
+ * audit log degrades to raw ids rather than refusing. A `dependencies` entry
18
+ * would make the lifecycle refuse to disable `admin_users` while the audit log
19
+ * is on, which is backwards — the record has to outlive the directory.
20
+ */
21
+ export const manifest = defineModuleManifest({
22
+ id: 'audit_logs',
23
+ name: 'Audit Logs',
24
+ description: 'Append-only audit log for sensitive admin and customer actions.',
25
+ version: '1.0.0',
26
+ // `auth` owns the `requireAdmin` port both route files are gated by.
27
+ dependencies: ['auth'],
28
+ settings: {
29
+ moduleCode: 'audit_logs',
30
+ groups: [{ code: 'audit_logs', name: 'Audit log' }],
31
+ // The group is kept as a reservation while the module ships no settings of
32
+ // its own: `audit_logs.enabled` was the only one, and it went with the
33
+ // control it backed (feature 074). Dropping the group as well would only
34
+ // turn it into an orphan the boot reconciler warns about at every start.
35
+ settings: [],
36
+ },
37
+ /**
38
+ * This module's first i18n bundle and its first command-palette action —
39
+ * feature 091, Phase 4, batch four.
40
+ *
41
+ * The bundle exists because the sidebar entry moved into this package with
42
+ * the screen and a nav declaration's `labelKey` is **module-relative** (R8):
43
+ * `appShell.nav.auditLog` was one of `_i18n`'s and is `nav.auditLog.label`
44
+ * here. It is a **flat** `{"a.b.c": "text"}` map at the **package root**, for
45
+ * the reason `admin_roles`' bundle records: `manifest-locations.ts` resolves
46
+ * a packaged module's `manifestPath` to its `package.json`, so `dirname` is
47
+ * the package directory, and a nested object fails
48
+ * `TranslationBundleEntriesSchema` while the boot reconciler only logs and
49
+ * skips it.
50
+ *
51
+ * The action pays one of the fifteen entries `specs/deferred-defects.md`
52
+ * still holds under *"Sixteen modules with an admin screen declare no
53
+ * command-palette action"*. Principle XVI is explicit that a sidebar entry is
54
+ * not enough, and the drain is what makes the debt payable: Phase 2 item 6's
55
+ * off-state test asserts the palette entry's absence while the module is off,
56
+ * and until there was one there was nothing to assert. `audit_log:read` is
57
+ * the code `GET /api/v1/admin/audit-log` enforces, which is what
58
+ * `check:action-route-permissions` holds this declaration to.
59
+ */
60
+ i18n: { bundlesDir: 'i18n' },
61
+ docs: { dir: 'docs' },
62
+ actions: [
63
+ {
64
+ id: 'open-audit-log',
65
+ labelKey: 'actions.openAuditLog.label',
66
+ descriptionKey: 'actions.openAuditLog.description',
67
+ icon: 'ListChecks',
68
+ targetRoute: '/audit-log',
69
+ requiredPermission: 'audit_log:read',
70
+ keywords: ['audit', 'audyt', 'dziennik', 'log', 'history', 'historia', 'trail'],
71
+ weight: 600,
72
+ },
73
+ ],
74
+ // Feature 074 (Constitution XVII), test C3 — platform primitive, and one of
75
+ // the escalation answers. D-32 moved the *writing* of the trail into the
76
+ // kernel, so what this module owns is the viewer — and sight of who did what
77
+ // is part of trusting the platform rather than a capability a business
78
+ // declines. Switching it off is a governance regression, not a business
79
+ // choice, so there is no independent decision underneath the control.
80
+ //
81
+ // `audit_logs.enabled` goes with it. The existing rows are removed by a core
82
+ // data migration (feature 074, FR-010a).
83
+ activation: {
84
+ nonDeactivatable: true,
85
+ reason: 'Sight of the write trail. The writing is the platform\'s own, so this module owns the ' +
86
+ 'viewer, and hiding who did what is a governance regression.',
87
+ },
88
+ });
89
+ /**
90
+ * The operator command this module declares — feature 080, T042b / D-160.9.
91
+ *
92
+ * It was `scripts/read.ts`, which owned its own `initOrm()`. Only the plumbing
93
+ * moved: the `EntityManager` comes off the composition now, and the reach is
94
+ * still one read of the kernel's `AuditLogEntry`. The conditions D-102 attached
95
+ * to this tool are unchanged and are restated at the top of `cli/read.ts`.
96
+ */
97
+ export const cliCommands = [
98
+ {
99
+ name: 'read',
100
+ summary: 'Read the audit trail from the host, without writing to it.',
101
+ // The cost is in the **first paragraph**, not a footnote, and it is declared
102
+ // here rather than printed from the body so the host can answer `--help`
103
+ // before it composes. That is D-102's condition: the credential is host
104
+ // access, not a working connection string, so the tool has to be able to say
105
+ // what it does before it can do it.
106
+ help: `usage: audit_logs read [--actor=<uuid>] [--action=<code>] [--object-type=<type>]
107
+ [--object-id=<id>] [--limit=<n>] [--json]
108
+
109
+ This tool reads the audit log without writing to it. A read performed here leaves
110
+ no record in the trail; the credential is access to this host and its database,
111
+ which already grants the same read through \`psql\`.
112
+
113
+ Filters — the same ones the admin HTTP route takes:
114
+ --actor=<uuid> the admin user who acted
115
+ --action=<code> e.g. product.update, module.disabled
116
+ --object-type=<type> e.g. product, module
117
+ --object-id=<id> the affected row
118
+ --limit=<n> 1..500, default 100
119
+ --json the rows as JSON instead of a table`,
120
+ run: async (context) => (await import('./backend/cli/read.js')).read(context),
121
+ },
122
+ ];
123
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAyB,MAAM,4BAA4B,CAAC;AAGzF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAoB,CAAC;IAC3C,EAAE,EAAE,YAAY;IAChB,IAAI,EAAE,YAAY;IAClB,WAAW,EACT,iEAAiE;IACnE,OAAO,EAAE,OAAO;IAChB,qEAAqE;IACrE,YAAY,EAAE,CAAC,MAAM,CAAC;IACtB,QAAQ,EAAE;QACR,UAAU,EAAE,YAAY;QACxB,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;QACnD,2EAA2E;QAC3E,uEAAuE;QACvE,yEAAyE;QACzE,yEAAyE;QACzE,QAAQ,EAAE,EAAE;KACb;IACD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,IAAI,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE;IAC5B,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE;IACrB,OAAO,EAAE;QACP;YACE,EAAE,EAAE,gBAAgB;YACpB,QAAQ,EAAE,4BAA4B;YACtC,cAAc,EAAE,kCAAkC;YAClD,IAAI,EAAE,YAAY;YAClB,WAAW,EAAE,YAAY;YACzB,kBAAkB,EAAE,gBAAgB;YACpC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,CAAC;YAC/E,MAAM,EAAE,GAAG;SACZ;KACF;IACD,4EAA4E;IAC5E,yEAAyE;IACzE,6EAA6E;IAC7E,uEAAuE;IACvE,wEAAwE;IACxE,sEAAsE;IACtE,EAAE;IACF,6EAA6E;IAC7E,yCAAyC;IACzC,UAAU,EAAE;QACV,gBAAgB,EAAE,IAAI;QACtB,MAAM,EACJ,wFAAwF;YACxF,6DAA6D;KAChE;CACF,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAmD;IACzE;QACE,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,4DAA4D;QACrE,6EAA6E;QAC7E,yEAAyE;QACzE,wEAAwE;QACxE,6EAA6E;QAC7E,oCAAoC;QACpC,IAAI,EAAE;;;;;;;;;;;;;6DAamD;QACzD,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,MAAM,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;KAC9E;CACF,CAAC"}
@@ -0,0 +1,47 @@
1
+ ---
2
+ title: audit_logs
3
+ description: Sensitive-action audit trail
4
+ ---
5
+
6
+ # `audit_logs`
7
+
8
+ Append-only audit trail of every sensitive action. Each row records who did
9
+ what, when, and (during impersonation) on whose behalf.
10
+
11
+ ## Public surface
12
+
13
+ Gated by the `audit_log:read` permission.
14
+
15
+ | Verb + Path | Purpose |
16
+ | --- | --- |
17
+ | `GET /api/v1/admin/audit-log` | Query the append-only log. Filters: `filter[actor]`, `filter[customer]`, `filter[action]`, `filter[objectType]`, `filter[objectId]`. Default `limit=100`, capped at 500. Each row carries `stateBefore` / `stateAfter` JSON inline so the admin viewer can render side-by-side diffs without a second roundtrip. |
18
+
19
+ ## Recording
20
+
21
+ `AuditPort.record({ ... })` is invoked from every sensitive
22
+ mutation: catalog price change, role change, credit-limit adjust, order
23
+ status / payment status change "on behalf", impersonation start/end, API
24
+ key out-of-scope, and others. Adding a new sensitive mutation is a
25
+ two-line change at the call site.
26
+
27
+ The port is what a module types on and what the kernel publishes
28
+ (`packages/platform/src/kernel/ports/audit.ts`); the container name it is
29
+ registered under is `auditLogService` and has not changed. The
30
+ implementation behind it, `AuditLogService`, is the platform's own and is
31
+ reachable only by its relative path — a module that named the class would
32
+ be depending on a writer shape uniform write auditing routes around, since
33
+ a domain write goes through `CommandBus.run` and the bus writes the row.
34
+
35
+ ## Entities
36
+
37
+ `AuditLogEntry` — `actorAdminUserId`, optional
38
+ `impersonatedCustomerAccountId`, `action`, `objectType`, `objectId`,
39
+ `stateBefore`, `stateAfter`, `ipAddress`, `userAgent`, `requestId`,
40
+ `actedAt`.
41
+
42
+ ## Extension points
43
+
44
+ - **External SIEM ship-out** — emit a domain event on each new audit row
45
+ and let an integration drain the stream into Splunk / Elastic / etc.
46
+ - **Retention** — no automatic pruning today; retention windows belong to
47
+ whoever runs the platform.
package/i18n/en.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "nav.auditLog.label": "Audit log",
3
+ "actions.openAuditLog.label": "Audit log",
4
+ "actions.openAuditLog.description": "Who changed what, and when"
5
+ }
package/i18n/pl.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "nav.auditLog.label": "Dziennik audytu",
3
+ "actions.openAuditLog.label": "Dziennik audytu",
4
+ "actions.openAuditLog.description": "Kto co zmienił i kiedy"
5
+ }
package/package.json ADDED
@@ -0,0 +1,86 @@
1
+ {
2
+ "name": "@endora-commerce/mod-audit-logs",
3
+ "version": "0.100.0",
4
+ "type": "module",
5
+ "sideEffects": false,
6
+ "description": "Append-only audit log for sensitive admin and customer actions.",
7
+ "license": "MIT",
8
+ "endora": {
9
+ "type": "module",
10
+ "id": "audit_logs"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/endora-commerce/endora-commerce.git",
15
+ "directory": "packages/modules/audit_logs"
16
+ },
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/manifest.d.ts",
23
+ "default": "./dist/manifest.js"
24
+ },
25
+ "./backend": {
26
+ "types": "./dist/backend/index.d.ts",
27
+ "default": "./dist/backend/index.js"
28
+ },
29
+ "./admin": {
30
+ "types": "./dist/admin/index.d.ts",
31
+ "default": "./dist/admin/index.js"
32
+ },
33
+ "./tailwind.css": "./tailwind.css",
34
+ "./package.json": "./package.json"
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "i18n",
39
+ "docs",
40
+ "tailwind.css"
41
+ ],
42
+ "engines": {
43
+ "node": ">=22.18.0"
44
+ },
45
+ "peerDependencies": {
46
+ "@mikro-orm/postgresql": "^6",
47
+ "fastify": "^5",
48
+ "react": "^19",
49
+ "react-router-dom": "^7",
50
+ "zod": "^4",
51
+ "@endora-commerce/admin-kit": "0.100.0",
52
+ "@endora-commerce/contracts": "0.100.0",
53
+ "@endora-commerce/platform": "0.100.0"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "@endora-commerce/admin-kit": {
57
+ "optional": true
58
+ },
59
+ "react": {
60
+ "optional": true
61
+ },
62
+ "react-router-dom": {
63
+ "optional": true
64
+ }
65
+ },
66
+ "devDependencies": {
67
+ "@mikro-orm/postgresql": "^6.6.13",
68
+ "@types/node": "^22.9.0",
69
+ "@types/react": "^19.2.14",
70
+ "fastify": "^5.12.5",
71
+ "react": "^19.2.5",
72
+ "react-router-dom": "^7.18.2",
73
+ "typescript": "^5.9.3",
74
+ "vitest": "^4.1.11",
75
+ "zod": "^4.2.0",
76
+ "@endora-commerce/admin-kit": "0.100.0",
77
+ "@endora-commerce/contracts": "0.100.0",
78
+ "@endora-commerce/platform": "0.100.0"
79
+ },
80
+ "scripts": {
81
+ "build": "tsc -p tsconfig.build.json && tsc -p tsconfig.ui.json",
82
+ "typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.ui.json --noEmit",
83
+ "lint": "eslint src",
84
+ "test": "vitest run"
85
+ }
86
+ }
package/tailwind.css ADDED
@@ -0,0 +1,14 @@
1
+ /* @endora-commerce/mod-audit-logs — AUTO-GENERATED by `pnpm --filter backend run manifests:generate`.
2
+ *
3
+ * The `@source` directives this package asks its host to scan
4
+ * (`specs/110-instance-repository/contracts/admin-stylesheet-composition.md` R1).
5
+ * They resolve relative to **this file**, so they hold wherever the package is
6
+ * installed — a workspace link here, `node_modules` in a client's instance.
7
+ *
8
+ * The `dist` line is what a published tarball ships and is what an instance
9
+ * scans; the `src` line is inert there and is what keeps `pnpm --filter admin
10
+ * run dev` reading source in this repository. Do not edit: run
11
+ * `pnpm --filter backend run manifests:generate`.
12
+ */
13
+ @source "./dist/admin";
14
+ @source "./src/admin";