@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.
- package/LICENSE +21 -0
- package/README.md +55 -0
- package/dist/admin/index.d.ts +34 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +39 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/AuditLogViewer.d.ts +3 -0
- package/dist/admin/pages/AuditLogViewer.d.ts.map +1 -0
- package/dist/admin/pages/AuditLogViewer.js +112 -0
- package/dist/admin/pages/AuditLogViewer.js.map +1 -0
- package/dist/backend/cli/read.d.ts +20 -0
- package/dist/backend/cli/read.d.ts.map +1 -0
- package/dist/backend/cli/read.js +94 -0
- package/dist/backend/cli/read.js.map +1 -0
- package/dist/backend/commands/recent-activity-visibility.commands.d.ts +31 -0
- package/dist/backend/commands/recent-activity-visibility.commands.d.ts.map +1 -0
- package/dist/backend/commands/recent-activity-visibility.commands.js +55 -0
- package/dist/backend/commands/recent-activity-visibility.commands.js.map +1 -0
- package/dist/backend/index.d.ts +91 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +117 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +29 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +85 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.admin.recent-activity.d.ts +126 -0
- package/dist/backend/routes.admin.recent-activity.d.ts.map +1 -0
- package/dist/backend/routes.admin.recent-activity.js +106 -0
- package/dist/backend/routes.admin.recent-activity.js.map +1 -0
- package/dist/backend/services/audit-reference-registry.d.ts +28 -0
- package/dist/backend/services/audit-reference-registry.d.ts.map +1 -0
- package/dist/backend/services/audit-reference-registry.js +57 -0
- package/dist/backend/services/audit-reference-registry.js.map +1 -0
- package/dist/backend/services/recent-activity-catalog.d.ts +92 -0
- package/dist/backend/services/recent-activity-catalog.d.ts.map +1 -0
- package/dist/backend/services/recent-activity-catalog.js +82 -0
- package/dist/backend/services/recent-activity-catalog.js.map +1 -0
- package/dist/backend/services/recent-activity-service.d.ts +105 -0
- package/dist/backend/services/recent-activity-service.d.ts.map +1 -0
- package/dist/backend/services/recent-activity-service.js +308 -0
- package/dist/backend/services/recent-activity-service.js.map +1 -0
- package/dist/backend/services/recent-activity-visibility.d.ts +37 -0
- package/dist/backend/services/recent-activity-visibility.d.ts.map +1 -0
- package/dist/backend/services/recent-activity-visibility.js +81 -0
- package/dist/backend/services/recent-activity-visibility.js.map +1 -0
- package/dist/manifest.d.ts +191 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +123 -0
- package/dist/manifest.js.map +1 -0
- package/docs/audit_logs.md +47 -0
- package/i18n/en.json +5 -0
- package/i18n/pl.json +5 -0
- package/package.json +86 -0
- 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"}
|
package/dist/manifest.js
ADDED
|
@@ -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
package/i18n/pl.json
ADDED
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";
|