@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Endora sp. z o.o. and the Endora Commerce contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<!-- Generated by `pnpm --filter backend run manifests:generate`. Delete this line to take the file over; a README without it is never regenerated. -->
|
|
2
|
+
|
|
3
|
+
# @endora-commerce/mod-audit-logs
|
|
4
|
+
|
|
5
|
+
Append-only audit log for sensitive admin and customer actions.
|
|
6
|
+
|
|
7
|
+
## What this is
|
|
8
|
+
|
|
9
|
+
An **Endora Commerce module package**. Its module id is `audit_logs` — the identity of record in the platform's registries, in its migrations and in its setting, permission and translation codes.
|
|
10
|
+
|
|
11
|
+
A module is not imported by application code. The platform discovers the extension packages an instance has installed, reads the manifest on the root subpath and composes the module from it, so installing this package and starting the instance is the whole integration.
|
|
12
|
+
|
|
13
|
+
## Entry points
|
|
14
|
+
|
|
15
|
+
| Import | Contents |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `@endora-commerce/mod-audit-logs` | the module manifest — its id, version, dependencies, settings and activation |
|
|
18
|
+
| `@endora-commerce/mod-audit-logs/admin` | the Admin UI contribution: the screens, navigation and palette actions it adds |
|
|
19
|
+
| `@endora-commerce/mod-audit-logs/backend` | the composition root the platform calls, with the entities, services, routes and workers it registers |
|
|
20
|
+
| `@endora-commerce/mod-audit-logs/tailwind.css` | the Tailwind source declaration for the admin code above |
|
|
21
|
+
|
|
22
|
+
## Depends on
|
|
23
|
+
|
|
24
|
+
Everything below is a **peer** dependency, so the application resolves exactly one copy of each — two copies of a host or of React are a runtime failure rather than a type error. An entry marked *optional* is needed only by the layers that use it.
|
|
25
|
+
|
|
26
|
+
**Endora packages**
|
|
27
|
+
|
|
28
|
+
- `@endora-commerce/admin-kit` — *optional*
|
|
29
|
+
- `@endora-commerce/contracts`
|
|
30
|
+
- `@endora-commerce/platform`
|
|
31
|
+
|
|
32
|
+
**Third-party**
|
|
33
|
+
|
|
34
|
+
- `@mikro-orm/postgresql` ^6
|
|
35
|
+
- `fastify` ^5
|
|
36
|
+
- `react` ^19 — *optional*
|
|
37
|
+
- `react-router-dom` ^7 — *optional*
|
|
38
|
+
- `zod` ^4
|
|
39
|
+
|
|
40
|
+
## What the tarball carries
|
|
41
|
+
|
|
42
|
+
- `dist/` — the compiled JavaScript and its type declarations
|
|
43
|
+
- `i18n/` — the translation bundles this package ships (`en`, `pl`), loaded by the platform
|
|
44
|
+
- `docs/` — the package’s own pages in the Endora Commerce documentation site
|
|
45
|
+
- `tailwind.css` — the Tailwind source declaration
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pnpm add @endora-commerce/mod-audit-logs
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Licence
|
|
54
|
+
|
|
55
|
+
MIT — the text is in `LICENSE`, beside this file.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `audit_logs`' admin surface — one route and one sidebar entry, declared by
|
|
3
|
+
* the module that owns them (feature 091, Phase 4, batch four;
|
|
4
|
+
* `specs/091-module-owned-admin-surfaces/contracts/admin-contribution.md`).
|
|
5
|
+
*
|
|
6
|
+
* **The locked-with-nav shape, and the module batch one rejected by name.**
|
|
7
|
+
* `plan.md`'s Ruling 2 retires the ground: `specs/deferred-defects.md` scopes
|
|
8
|
+
* Constitution XVII item 6 to the modules that declare an
|
|
9
|
+
* `activation.settingCode` *without* `nonDeactivatable`, so a locked module is
|
|
10
|
+
* outside that population by the owner's own measurement rather than by an
|
|
11
|
+
* exception anyone grants. Its off-state test asserts the axis it has — the
|
|
12
|
+
* permission one, which `useSurfaceVisibility` gates identically — and asserts
|
|
13
|
+
* the missing axis as a fact about the module read from the manifest, which is
|
|
14
|
+
* a stronger statement than a skipped file.
|
|
15
|
+
*
|
|
16
|
+
* **`audit_log:read` is the code, singular, and it is read from the route.**
|
|
17
|
+
* `GET /api/v1/admin/audit-log` enforces it
|
|
18
|
+
* (`packages/modules/audit_logs/src/backend/routes.admin.ts`). The module's
|
|
19
|
+
* other admin routes — the dashboard's recent-activity feed and the two
|
|
20
|
+
* module-activation endpoints under
|
|
21
|
+
* `routes.admin.recent-activity.ts` — are not this screen's, and two of them
|
|
22
|
+
* enforce `platform.modules.activate`, so copying the neighbourhood would have
|
|
23
|
+
* gated the viewer on a code that has nothing to do with reading an audit
|
|
24
|
+
* trail.
|
|
25
|
+
*
|
|
26
|
+
* **This entry exports data and nothing else** (R2), like every `./admin`
|
|
27
|
+
* layer: `check:module-boundary`'s D-171 rule designates a subpath as contract
|
|
28
|
+
* surface when it emits no runtime binding, and `./admin` deliberately does not
|
|
29
|
+
* qualify, so a consumer reaching into another module's `./admin` stays a
|
|
30
|
+
* counted boundary reach.
|
|
31
|
+
*/
|
|
32
|
+
import type { AdminContributions } from '@endora-commerce/admin-kit/contributions';
|
|
33
|
+
export declare const contributions: AdminContributions;
|
|
34
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAKnF,eAAO,MAAM,aAAa,EAAE,kBAmC3B,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** The module's only admin route: the append-only audit-log viewer. */
|
|
2
|
+
const ROUTE_PATH = '/audit-log';
|
|
3
|
+
export const contributions = {
|
|
4
|
+
routes: [
|
|
5
|
+
{
|
|
6
|
+
path: ROUTE_PATH,
|
|
7
|
+
component: () => import('./pages/AuditLogViewer.js'),
|
|
8
|
+
requiredPermission: 'audit_log:read',
|
|
9
|
+
index: true,
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
nav: [
|
|
13
|
+
{
|
|
14
|
+
to: ROUTE_PATH,
|
|
15
|
+
// Module-relative (R8), resolved in this module's own namespace out of
|
|
16
|
+
// `packages/modules/audit_logs/i18n/`. It was `appShell.nav.auditLog` in
|
|
17
|
+
// the shared `_i18n` bundle, one of the four shared files a module author
|
|
18
|
+
// had to edit.
|
|
19
|
+
labelKey: 'nav.auditLog.label',
|
|
20
|
+
// The icon the hand-written NAV entry carried.
|
|
21
|
+
icon: 'ListChecks',
|
|
22
|
+
section: 'system',
|
|
23
|
+
// **The *System* section still holds host-declared entries**, unlike the
|
|
24
|
+
// *Analytics & Ads* section batch three emptied, so this weight orders
|
|
25
|
+
// the entry only against the other converted modules — the host block
|
|
26
|
+
// renders first whatever any weight says, and that is the batch's one
|
|
27
|
+
// operator-visible change. The scale is the hand-written table's own
|
|
28
|
+
// position times a hundred, so the three entries this batch converts keep
|
|
29
|
+
// the relative order they had (`/admin-users` 200, `/admin-roles` 300,
|
|
30
|
+
// `/audit-log` 600) and the eight System entries still to convert have a
|
|
31
|
+
// slot each waiting for them.
|
|
32
|
+
weight: 600,
|
|
33
|
+
// The code that opens the screen: the sidebar advertises a destination,
|
|
34
|
+
// so it gates on what the landing route enforces.
|
|
35
|
+
requiredPermission: 'audit_log:read',
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAiCA,uEAAuE;AACvE,MAAM,UAAU,GAAG,YAAY,CAAC;AAEhC,MAAM,CAAC,MAAM,aAAa,GAAuB;IAC/C,MAAM,EAAE;QACN;YACE,IAAI,EAAE,UAAU;YAChB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,2BAA2B,CAAC;YACpD,kBAAkB,EAAE,gBAAgB;YACpC,KAAK,EAAE,IAAI;SACZ;KACF;IACD,GAAG,EAAE;QACH;YACE,EAAE,EAAE,UAAU;YACd,uEAAuE;YACvE,yEAAyE;YACzE,0EAA0E;YAC1E,eAAe;YACf,QAAQ,EAAE,oBAAoB;YAC9B,+CAA+C;YAC/C,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,QAAQ;YACjB,yEAAyE;YACzE,uEAAuE;YACvE,sEAAsE;YACtE,sEAAsE;YACtE,qEAAqE;YACrE,0EAA0E;YAC1E,uEAAuE;YACvE,yEAAyE;YACzE,8BAA8B;YAC9B,MAAM,EAAE,GAAG;YACX,wEAAwE;YACxE,kDAAkD;YAClD,kBAAkB,EAAE,gBAAgB;SACrC;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuditLogViewer.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/AuditLogViewer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA8D,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAiDnG,MAAM,CAAC,OAAO,UAAU,cAAc,IAAI,SAAS,CA8MlD"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, useCallback, useEffect, useState } from 'react';
|
|
3
|
+
import { Link } from 'react-router-dom';
|
|
4
|
+
import { ApiError, apiClient, formatDateTime } from '@endora-commerce/admin-kit/lib';
|
|
5
|
+
import { Alert, AlertDescription, Button, Card, CardContent, Input, Label, PageHeader, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@endora-commerce/admin-kit/ui';
|
|
6
|
+
import { AdminUserPicker, CustomerPicker } from '@endora-commerce/admin-kit/components';
|
|
7
|
+
import { useTranslation, useTranslationContext } from '@endora-commerce/admin-kit/i18n';
|
|
8
|
+
export default function AuditLogViewer() {
|
|
9
|
+
const t = useTranslation('core');
|
|
10
|
+
const { t: translate } = useTranslationContext();
|
|
11
|
+
const [rows, setRows] = useState([]);
|
|
12
|
+
const [loading, setLoading] = useState(true);
|
|
13
|
+
const [error, setError] = useState(null);
|
|
14
|
+
const [actor, setActor] = useState('');
|
|
15
|
+
const [action, setAction] = useState('');
|
|
16
|
+
const [objectType, setObjectType] = useState('');
|
|
17
|
+
const [objectId, setObjectId] = useState('');
|
|
18
|
+
const [impersonated, setImpersonated] = useState('');
|
|
19
|
+
const [expandedId, setExpandedId] = useState(null);
|
|
20
|
+
const refresh = useCallback(async () => {
|
|
21
|
+
setLoading(true);
|
|
22
|
+
setError(null);
|
|
23
|
+
try {
|
|
24
|
+
const params = new URLSearchParams();
|
|
25
|
+
if (actor)
|
|
26
|
+
params.set('filter[actor]', actor);
|
|
27
|
+
if (action)
|
|
28
|
+
params.set('filter[action]', action);
|
|
29
|
+
if (objectType)
|
|
30
|
+
params.set('filter[objectType]', objectType);
|
|
31
|
+
if (objectId)
|
|
32
|
+
params.set('filter[objectId]', objectId);
|
|
33
|
+
if (impersonated)
|
|
34
|
+
params.set('filter[customer]', impersonated);
|
|
35
|
+
const path = '/api/v1/admin/audit-log' + (params.toString() ? `?${params.toString()}` : '');
|
|
36
|
+
const res = await apiClient.get(path);
|
|
37
|
+
setRows(res.data);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
setError(err instanceof ApiError ? err.envelope.error.message : t('auditLog.error.load'));
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
setLoading(false);
|
|
44
|
+
}
|
|
45
|
+
}, [actor, action, objectType, objectId, impersonated]);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
void refresh();
|
|
48
|
+
}, [refresh]);
|
|
49
|
+
return (_jsxs(_Fragment, { children: [_jsx(PageHeader, { title: t('auditLog.page.title'), description: t('auditLog.page.description') }), error ? (_jsx(Alert, { variant: "destructive", className: "mb-4", children: _jsx(AlertDescription, { children: error }) })) : null, _jsx(Card, { className: "mb-4", children: _jsx(CardContent, { className: "pt-6", children: _jsxs("form", { onSubmit: (e) => {
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
void refresh();
|
|
52
|
+
}, className: "space-y-4", children: [_jsxs("div", { className: "grid gap-4 md:grid-cols-3", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "f-action", children: t('auditLog.filter.action') }), _jsx(Input, { id: "f-action", placeholder: "product.update", value: action, onChange: (e) => setAction(e.target.value.trim()) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "f-otype", children: t('auditLog.filter.objectType') }), _jsx(Input, { id: "f-otype", placeholder: "product", value: objectType, onChange: (e) => setObjectType(e.target.value.trim()) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "f-oid", children: t('auditLog.filter.objectId') }), _jsx(Input, { id: "f-oid", placeholder: "UUID", value: objectId, onChange: (e) => setObjectId(e.target.value.trim()) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "f-actor", children: t('auditLog.filter.actor') }), _jsx(AdminUserPicker, { id: "f-actor", value: actor || null, onChange: (v) => setActor(v ?? '') })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "f-customer", children: t('auditLog.filter.impersonatedCustomer') }), _jsx(CustomerPicker, { id: "f-customer", value: impersonated || null, onChange: (v) => setImpersonated(v ?? '') })] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "submit", children: t('auditLog.filter.apply') }), _jsx(Button, { type: "button", variant: "outline", onClick: () => {
|
|
53
|
+
setActor('');
|
|
54
|
+
setAction('');
|
|
55
|
+
setObjectType('');
|
|
56
|
+
setObjectId('');
|
|
57
|
+
setImpersonated('');
|
|
58
|
+
}, children: t('auditLog.filter.clear') })] })] }) }) }), _jsx(Card, { children: _jsx(CardContent, { className: "pt-6", children: loading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: t('common.state.loading') })) : rows.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: t('auditLog.empty') })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: t('auditLog.column.when') }), _jsx(TableHead, { children: t('auditLog.column.actor') }), _jsx(TableHead, { children: t('auditLog.column.action') }), _jsx(TableHead, { children: t('auditLog.column.object') }), _jsx(TableHead, {})] }) }), _jsx(TableBody, { children: rows.map((r) => (_jsxs(Fragment, { children: [_jsxs(TableRow, { children: [_jsx(TableCell, { children: formatDateTime(r.actedAt) }), _jsx(TableCell, { children: _jsx(ActorCell, { row: r }) }), _jsx(TableCell, { children: translateAuditAction(translate, r) }), _jsxs(TableCell, { children: [r.objectType, _jsx("br", {}), _jsx(ObjectIdCell, { objectType: r.objectType, objectId: r.objectId })] }), _jsx(TableCell, { children: _jsx(Button, { variant: "outline", size: "sm", onClick: () => setExpandedId(expandedId === r.id ? null : r.id), children: expandedId === r.id ? t('auditLog.action.hide') : t('auditLog.action.detail') }) })] }), expandedId === r.id ? (_jsx(TableRow, { children: _jsxs(TableCell, { colSpan: 5, children: [_jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [_jsxs("div", { children: [_jsx("strong", { children: t('auditLog.detail.before') }), _jsx("pre", { className: "mt-1 max-h-80 overflow-auto rounded-md bg-muted p-3 text-xs", children: JSON.stringify(r.stateBefore ?? null, null, 2) })] }), _jsxs("div", { children: [_jsx("strong", { children: t('auditLog.detail.after') }), _jsx("pre", { className: "mt-1 max-h-80 overflow-auto rounded-md bg-muted p-3 text-xs", children: JSON.stringify(r.stateAfter ?? null, null, 2) })] })] }), _jsxs("div", { className: "mt-2 text-xs text-muted-foreground", children: [t('auditLog.detail.ip'), " ", r.ipAddress ?? '—', " \u00B7 ", t('auditLog.detail.userAgent'), ' ', r.userAgent ? r.userAgent.slice(0, 60) : '—', " \u00B7 ", t('auditLog.detail.request'), ' ', r.requestId ?? '—', r.impersonatedCustomerAccountId ? (_jsxs(_Fragment, { children: [' ', "\u00B7 ", t('auditLog.detail.impersonatedCustomer'), ' ', r.impersonatedCustomerAccountId.slice(0, 8)] })) : null] })] }) })) : null] }, r.id))) })] })) }) })] }));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Maps an audit-log `objectType` to its admin detail route, when one exists.
|
|
62
|
+
* Only id-addressable detail pages are listed — object types without a
|
|
63
|
+
* standalone page (or whose route keys on a code/slug rather than the audit
|
|
64
|
+
* `objectId`) are intentionally omitted so we never produce a broken link.
|
|
65
|
+
*/
|
|
66
|
+
const OBJECT_ROUTE = {
|
|
67
|
+
product: (id) => `/catalog/products/${id}`,
|
|
68
|
+
order: (id) => `/orders/${id}`,
|
|
69
|
+
organization: (id) => `/organizations/${id}`,
|
|
70
|
+
customer_account: (id) => `/customers/${id}`,
|
|
71
|
+
price_list: (id) => `/price-lists/${id}`,
|
|
72
|
+
warehouse: (id) => `/warehouses/${id}`,
|
|
73
|
+
cart: (id) => `/carts/${id}`,
|
|
74
|
+
bulk_operation: (id) => `/catalog/bulk-operations/${id}`,
|
|
75
|
+
admin_user: (id) => `/admin-users#admin-user-${id}`,
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Renders the object id, linked to the object's admin detail page when its
|
|
79
|
+
* type has one; otherwise a plain truncated id.
|
|
80
|
+
*/
|
|
81
|
+
function ObjectIdCell({ objectType, objectId, }) {
|
|
82
|
+
const build = OBJECT_ROUTE[objectType];
|
|
83
|
+
const short = objectId.slice(0, 8);
|
|
84
|
+
if (!build) {
|
|
85
|
+
return _jsx("span", { className: "text-xs text-muted-foreground", children: short });
|
|
86
|
+
}
|
|
87
|
+
return (_jsx(Link, { to: build(objectId), className: "text-xs text-muted-foreground underline underline-offset-2", title: objectId, children: short }));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Renders the actor column: an admin user's name + email (linked to the
|
|
91
|
+
* users admin page) when resolvable, the bare id as a fallback, or the
|
|
92
|
+
* "system" label when there is no actor.
|
|
93
|
+
*/
|
|
94
|
+
function ActorCell({ row }) {
|
|
95
|
+
const t = useTranslation('core');
|
|
96
|
+
if (!row.actorAdminUserId) {
|
|
97
|
+
return _jsx("span", { className: "text-muted-foreground", children: t('auditLog.actor.system') });
|
|
98
|
+
}
|
|
99
|
+
const name = row.actorName?.trim();
|
|
100
|
+
if (!name) {
|
|
101
|
+
// Unknown / deleted admin user — keep the id so the row is still traceable.
|
|
102
|
+
return _jsx("span", { className: "font-mono text-xs", children: row.actorAdminUserId.slice(0, 8) });
|
|
103
|
+
}
|
|
104
|
+
return (_jsxs(Link, { to: `/admin-users#admin-user-${row.actorAdminUserId}`, className: "underline underline-offset-2", children: [name, row.actorEmail ? (_jsxs("span", { className: "text-muted-foreground", children: [" (", row.actorEmail, ")"] })) : null] }));
|
|
105
|
+
}
|
|
106
|
+
function translateAuditAction(translate, row) {
|
|
107
|
+
const moduleId = row.actionModuleId ?? 'core';
|
|
108
|
+
const key = `auditLog.${row.action}`;
|
|
109
|
+
const label = translate(moduleId, key);
|
|
110
|
+
return label === `${moduleId}.${key}` ? row.action : label;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=AuditLogViewer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuditLogViewer.js","sourceRoot":"","sources":["../../../src/admin/pages/AuditLogViewer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ModuleCliCommandContext } from '@endora-commerce/contracts';
|
|
3
|
+
import { AuditLogEntry } from '@endora-commerce/platform/kernel';
|
|
4
|
+
import type { ModuleContext } from '@endora-commerce/platform/kernel';
|
|
5
|
+
declare const ArgsSchema: z.ZodObject<{
|
|
6
|
+
actor: z.ZodOptional<z.ZodString>;
|
|
7
|
+
action: z.ZodOptional<z.ZodString>;
|
|
8
|
+
objectType: z.ZodOptional<z.ZodString>;
|
|
9
|
+
objectId: z.ZodOptional<z.ZodString>;
|
|
10
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
11
|
+
json: z.ZodDefault<z.ZodBoolean>;
|
|
12
|
+
}, z.core.$strip>;
|
|
13
|
+
type Args = z.infer<typeof ArgsSchema>;
|
|
14
|
+
export declare function parseArgv(argv: readonly string[]): Args | {
|
|
15
|
+
error: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function formatTable(rows: readonly AuditLogEntry[]): string;
|
|
18
|
+
export declare function read({ ctx, argv, out, err, }: ModuleCliCommandContext<ModuleContext>): Promise<number>;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/backend/cli/read.ts"],"names":[],"mappings":"AAqDA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAYtE,QAAA,MAAM,UAAU;;;;;;;iBAOd,CAAC;AAEH,KAAK,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AASvC,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CA2B3E;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CAuBlE;AAOD,wBAAsB,IAAI,CAAC,EACzB,GAAG,EACH,IAAI,EACJ,GAAG,EACH,GAAG,GACJ,EAAE,uBAAuB,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB1D"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { AuditLogEntry } from '@endora-commerce/platform/kernel';
|
|
3
|
+
/**
|
|
4
|
+
* The full `--help` text is the command's `help` declaration in `manifest.ts`,
|
|
5
|
+
* printed by the host before it composes (D-102 — the credential is host
|
|
6
|
+
* access, not a working connection string). This is the one line the error path
|
|
7
|
+
* needs, so a refused argument still says where to look.
|
|
8
|
+
*/
|
|
9
|
+
const USAGE_LINE = 'usage: audit_logs read [--actor] [--action] [--object-type] [--object-id] ' +
|
|
10
|
+
'[--limit] [--json] — run with --help for the filters';
|
|
11
|
+
const ArgsSchema = z.object({
|
|
12
|
+
actor: z.string().uuid().optional(),
|
|
13
|
+
action: z.string().min(1).optional(),
|
|
14
|
+
objectType: z.string().min(1).optional(),
|
|
15
|
+
objectId: z.string().min(1).optional(),
|
|
16
|
+
limit: z.number().int().min(1).max(500).default(100),
|
|
17
|
+
json: z.boolean().default(false),
|
|
18
|
+
});
|
|
19
|
+
const FLAGS = {
|
|
20
|
+
'--actor': 'actor',
|
|
21
|
+
'--action': 'action',
|
|
22
|
+
'--object-type': 'objectType',
|
|
23
|
+
'--object-id': 'objectId',
|
|
24
|
+
};
|
|
25
|
+
export function parseArgv(argv) {
|
|
26
|
+
const candidate = {};
|
|
27
|
+
for (const arg of argv) {
|
|
28
|
+
if (arg === '--json') {
|
|
29
|
+
candidate['json'] = true;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const eq = arg.indexOf('=');
|
|
33
|
+
const [flag, value] = eq === -1 ? [arg, undefined] : [arg.slice(0, eq), arg.slice(eq + 1)];
|
|
34
|
+
const key = FLAGS[flag];
|
|
35
|
+
if (key !== undefined) {
|
|
36
|
+
if (value === undefined)
|
|
37
|
+
return { error: `${flag} needs a value` };
|
|
38
|
+
candidate[key] = value;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (flag === '--limit') {
|
|
42
|
+
if (value === undefined)
|
|
43
|
+
return { error: '--limit needs a value' };
|
|
44
|
+
candidate['limit'] = Number.parseInt(value, 10);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
return { error: `unknown argument: ${arg}` };
|
|
48
|
+
}
|
|
49
|
+
const parsed = ArgsSchema.safeParse(candidate);
|
|
50
|
+
if (!parsed.success) {
|
|
51
|
+
return { error: parsed.error.issues.map((issue) => issue.message).join('; ') };
|
|
52
|
+
}
|
|
53
|
+
return parsed.data;
|
|
54
|
+
}
|
|
55
|
+
export function formatTable(rows) {
|
|
56
|
+
if (rows.length === 0)
|
|
57
|
+
return '(no entries match)';
|
|
58
|
+
const width = (pick, header) => Math.max(header.length, ...rows.map((row) => pick(row).length));
|
|
59
|
+
const actor = (row) => row.actorAdminUserId ?? '—';
|
|
60
|
+
const object = (row) => `${row.objectType}/${row.objectId}`;
|
|
61
|
+
const widths = {
|
|
62
|
+
at: 24,
|
|
63
|
+
actor: width(actor, 'ACTOR'),
|
|
64
|
+
action: width((row) => row.action, 'ACTION'),
|
|
65
|
+
object: width(object, 'OBJECT'),
|
|
66
|
+
};
|
|
67
|
+
const pad = (value, to) => value.padEnd(to);
|
|
68
|
+
const lines = [
|
|
69
|
+
`${pad('ACTED AT', widths.at)} ${pad('ACTOR', widths.actor)} ` +
|
|
70
|
+
`${pad('ACTION', widths.action)} ${pad('OBJECT', widths.object)}`,
|
|
71
|
+
...rows.map((row) => `${pad(row.actedAt.toISOString(), widths.at)} ${pad(actor(row), widths.actor)} ` +
|
|
72
|
+
`${pad(row.action, widths.action)} ${pad(object(row), widths.object)}`),
|
|
73
|
+
];
|
|
74
|
+
return lines.join('\n');
|
|
75
|
+
}
|
|
76
|
+
export async function read({ ctx, argv, out, err, }) {
|
|
77
|
+
const parsed = parseArgv(argv);
|
|
78
|
+
if ('error' in parsed) {
|
|
79
|
+
err(`${USAGE_LINE}\n\nerror: ${parsed.error}`);
|
|
80
|
+
return 1;
|
|
81
|
+
}
|
|
82
|
+
const em = ctx.cradle().emFactory();
|
|
83
|
+
// A read, and only a read: no `em.create`, no `persist`, no `flush`. The
|
|
84
|
+
// trail is append-only and the Command Bus is its one writer.
|
|
85
|
+
const rows = await em.find(AuditLogEntry, {
|
|
86
|
+
...(parsed.actor ? { actorAdminUserId: parsed.actor } : {}),
|
|
87
|
+
...(parsed.action ? { action: parsed.action } : {}),
|
|
88
|
+
...(parsed.objectType ? { objectType: parsed.objectType } : {}),
|
|
89
|
+
...(parsed.objectId ? { objectId: parsed.objectId } : {}),
|
|
90
|
+
}, { orderBy: { actedAt: 'desc' }, limit: parsed.limit });
|
|
91
|
+
out(parsed.json ? JSON.stringify(rows, null, 2) : formatTable(rows));
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/backend/cli/read.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Command } from '@endora-commerce/platform/commands';
|
|
2
|
+
import type { RecentActivityCatalog } from '../services/recent-activity-catalog.js';
|
|
3
|
+
/**
|
|
4
|
+
* The operator's dashboard-visibility flip — feature 080, T042j / D-163.1,
|
|
5
|
+
* Constitution XIII.
|
|
6
|
+
*
|
|
7
|
+
* A Command rather than a settings write for the reason the activation flip is
|
|
8
|
+
* one (`_lifecycle/commands/activation.commands.ts`): there is no `setting.*`
|
|
9
|
+
* Command to reuse, and `SettingsAdminService.setValue` audits by hand after
|
|
10
|
+
* `em.flush()` and outside any transaction. The Setting is `hidden`, so it does
|
|
11
|
+
* not render on the generic Settings screen at all — this is its only door, the
|
|
12
|
+
* same asymmetry activation has.
|
|
13
|
+
*
|
|
14
|
+
* The refusal is taken **before** the Command opens a transaction, and it is a
|
|
15
|
+
* refusal rather than a no-op: a control that silently does nothing is worse
|
|
16
|
+
* than an absent one, because the operator believes they switched something.
|
|
17
|
+
*/
|
|
18
|
+
export interface SetRecentActivityVisibilityInput {
|
|
19
|
+
moduleId: string;
|
|
20
|
+
visible: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Refuse a module that declares no eligibility, naming the difference between
|
|
24
|
+
* the two axes so the message is actionable.
|
|
25
|
+
*/
|
|
26
|
+
export declare function assertRecentActivityVisibilityWritable(catalog: RecentActivityCatalog, moduleId: string): string;
|
|
27
|
+
export declare function makeSetRecentActivityVisibilityCommand(catalog: RecentActivityCatalog, input: SetRecentActivityVisibilityInput): Command<{
|
|
28
|
+
moduleId: string;
|
|
29
|
+
visible: boolean;
|
|
30
|
+
}>;
|
|
31
|
+
//# sourceMappingURL=recent-activity-visibility.commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recent-activity-visibility.commands.d.ts","sourceRoot":"","sources":["../../../src/backend/commands/recent-activity-visibility.commands.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAGlE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAEpF;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,sCAAsC,CACpD,OAAO,EAAE,qBAAqB,EAC9B,QAAQ,EAAE,MAAM,GACf,MAAM,CAYR;AAED,wBAAgB,sCAAsC,CACpD,OAAO,EAAE,qBAAqB,EAC9B,KAAK,EAAE,gCAAgC,GACtC,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CA0CjD"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ERROR_CODES } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
import { Setting } from '@endora-commerce/platform/kernel';
|
|
4
|
+
/**
|
|
5
|
+
* Refuse a module that declares no eligibility, naming the difference between
|
|
6
|
+
* the two axes so the message is actionable.
|
|
7
|
+
*/
|
|
8
|
+
export function assertRecentActivityVisibilityWritable(catalog, moduleId) {
|
|
9
|
+
const entry = catalog.eligibleModules().find((m) => m.moduleId === moduleId);
|
|
10
|
+
if (!entry) {
|
|
11
|
+
throw new HttpError(404, ERROR_CODES.MODULE_NOT_FOUND, `Module "${moduleId}" declares no recent-activity eligibility, so it contributes ` +
|
|
12
|
+
`nothing to the dashboard card and there is nothing to show or hide. Eligibility is ` +
|
|
13
|
+
`the module author's declaration; only what to do with it is yours.`);
|
|
14
|
+
}
|
|
15
|
+
return entry.settingCode;
|
|
16
|
+
}
|
|
17
|
+
export function makeSetRecentActivityVisibilityCommand(catalog, input) {
|
|
18
|
+
const settingCode = assertRecentActivityVisibilityWritable(catalog, input.moduleId);
|
|
19
|
+
return {
|
|
20
|
+
action: 'module.recent_activity_visibility.set',
|
|
21
|
+
objectType: 'module',
|
|
22
|
+
objectId: input.moduleId,
|
|
23
|
+
capture: async ({ em }) => {
|
|
24
|
+
const setting = await em.findOne(Setting, { code: settingCode });
|
|
25
|
+
const stored = setting?.globalValue ?? setting?.defaultValue;
|
|
26
|
+
return {
|
|
27
|
+
moduleId: input.moduleId,
|
|
28
|
+
settingCode,
|
|
29
|
+
// The **resolved** answer, not the raw jsonb: `global_value` is `null`
|
|
30
|
+
// both for "the operator never chose" and for a literal JSON null, so
|
|
31
|
+
// recording it would leave two consecutive audit rows unable to say
|
|
32
|
+
// what the module's visibility actually was at either moment. The same
|
|
33
|
+
// reasoning the activation command records for itself.
|
|
34
|
+
visible: typeof stored === 'boolean' ? stored : true,
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
run: async ({ em }) => {
|
|
38
|
+
const setting = await em.findOne(Setting, { code: settingCode });
|
|
39
|
+
if (!setting) {
|
|
40
|
+
// The row is reconciled from the derived settings manifest — at boot
|
|
41
|
+
// for a module this build ships, by `install` for a package. Its
|
|
42
|
+
// absence is a platform state, not an operator error.
|
|
43
|
+
throw new HttpError(500, ERROR_CODES.SETTING_NOT_REGISTERED, `Module "${input.moduleId}" is eligible for the dashboard card, but its ` +
|
|
44
|
+
`visibility setting "${settingCode}" is not registered.`);
|
|
45
|
+
}
|
|
46
|
+
setting.globalValue = input.visible;
|
|
47
|
+
await em.flush();
|
|
48
|
+
return {
|
|
49
|
+
result: { moduleId: input.moduleId, visible: input.visible },
|
|
50
|
+
after: { moduleId: input.moduleId, settingCode, visible: input.visible },
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=recent-activity-visibility.commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recent-activity-visibility.commands.js","sourceRoot":"","sources":["../../../src/backend/commands/recent-activity-visibility.commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAwB3D;;;GAGG;AACH,MAAM,UAAU,sCAAsC,CACpD,OAA8B,EAC9B,QAAgB;IAEhB,MAAM,KAAK,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;IAC7E,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,SAAS,CACjB,GAAG,EACH,WAAW,CAAC,gBAAgB,EAC5B,WAAW,QAAQ,+DAA+D;YAChF,qFAAqF;YACrF,oEAAoE,CACvE,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC,WAAW,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,sCAAsC,CACpD,OAA8B,EAC9B,KAAuC;IAEvC,MAAM,WAAW,GAAG,sCAAsC,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAEpF,OAAO;QACL,MAAM,EAAE,uCAAuC;QAC/C,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;YACxB,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;YACjE,MAAM,MAAM,GAAG,OAAO,EAAE,WAAW,IAAI,OAAO,EAAE,YAAY,CAAC;YAC7D,OAAO;gBACL,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,WAAW;gBACX,uEAAuE;gBACvE,sEAAsE;gBACtE,oEAAoE;gBACpE,uEAAuE;gBACvE,uDAAuD;gBACvD,OAAO,EAAE,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;aACrD,CAAC;QACJ,CAAC;QACD,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;YACpB,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;YACjE,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,qEAAqE;gBACrE,iEAAiE;gBACjE,sDAAsD;gBACtD,MAAM,IAAI,SAAS,CACjB,GAAG,EACH,WAAW,CAAC,sBAAsB,EAClC,WAAW,KAAK,CAAC,QAAQ,gDAAgD;oBACvE,uBAAuB,WAAW,sBAAsB,CAC3D,CAAC;YACJ,CAAC;YACD,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC;YACpC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;YACjB,OAAO;gBACL,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;gBAC5D,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;aACzE,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { AuditReferenceRegistryPort } from '@endora-commerce/contracts';
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
3
|
+
import type { CommandBus } from '@endora-commerce/platform/commands';
|
|
4
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
5
|
+
import type { ModuleContext } from '@endora-commerce/platform/kernel';
|
|
6
|
+
import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
|
|
7
|
+
import type { AuditActorIdentity } from './routes.admin.js';
|
|
8
|
+
import { RecentActivityCatalog, type RecentActivityDeclarationSource } from './services/recent-activity-catalog.js';
|
|
9
|
+
import { RecentActivityService } from './services/recent-activity-service.js';
|
|
10
|
+
import { RecentActivityVisibility } from './services/recent-activity-visibility.js';
|
|
11
|
+
/**
|
|
12
|
+
* `audit_logs` — the compliance surface that was a passenger (feature 072,
|
|
13
|
+
* wave 1, T084).
|
|
14
|
+
*
|
|
15
|
+
* The module owned two route files and a service and registered none of them.
|
|
16
|
+
* `admin_users/plugin.ts` imported both route modules, constructed
|
|
17
|
+
* `RecentActivityService` itself and mounted the lot inside its own plugin, so
|
|
18
|
+
* the audit log existed for exactly as long as the admin-users module happened
|
|
19
|
+
* to be composed. Its service and entity moved to the kernel in T016 —
|
|
20
|
+
* `AuditPort` is what every module writes through — and what is left here
|
|
21
|
+
* is the *reading* half, which is this module's own.
|
|
22
|
+
*
|
|
23
|
+
* **`auditActorResolver` is a contribution, not a dependency**, and the
|
|
24
|
+
* distinction is the whole design of this file. Turning a bare actor id into a
|
|
25
|
+
* name and an e-mail needs `admin_users`, but needing it for a *column* is not
|
|
26
|
+
* the same as needing it to function. So the name is owned and defaulted here,
|
|
27
|
+
* a composition root that ships `admin_users` overrides it, and the manifest
|
|
28
|
+
* declares no edge — because a `dependencies` entry would make the lifecycle
|
|
29
|
+
* refuse to disable `admin_users` while the audit log is on, which is precisely
|
|
30
|
+
* backwards.
|
|
31
|
+
*
|
|
32
|
+
* **What that buys, exactly — D-102.** The enrichment degrades and the *record*
|
|
33
|
+
* does not: with `admin_users` absent the audit rows still render, with raw
|
|
34
|
+
* actor ids in the actor column. That is a claim about this module's own
|
|
35
|
+
* handler and it stops there. The **route** is guarded by `requireAdmin`, whose
|
|
36
|
+
* permission check reads `adminUserReadPort` since feature 075 Phase C, so on a
|
|
37
|
+
* deployment that does not ship `admin_users` no admin-authenticated request
|
|
38
|
+
* reaches this handler at all. Reading the trail when the admin identity
|
|
39
|
+
* subsystem is itself absent needs a surface that is not an admin-authenticated
|
|
40
|
+
* HTTP route: `pnpm --filter backend run audit:read`, whose credential is
|
|
41
|
+
* access to the host rather than a session.
|
|
42
|
+
*
|
|
43
|
+
* This paragraph used to say that "an operator investigating an incident wants
|
|
44
|
+
* the record more, not less, when part of the platform is off". The sentiment
|
|
45
|
+
* is right and the module could not deliver it over HTTP; the withdrawal is
|
|
46
|
+
* recorded rather than quietly narrowed, because the interesting content of
|
|
47
|
+
* that sentence was the part that turned out to be false.
|
|
48
|
+
*
|
|
49
|
+
* The root registers a **gated** resolver, since deciding what "`admin_users`
|
|
50
|
+
* is present" means is a composition-root question, not one this module should
|
|
51
|
+
* answer by hard-coding another module's id. When `admin_users` converts it
|
|
52
|
+
* publishes the resolver itself and the root's entry disappears.
|
|
53
|
+
*/
|
|
54
|
+
export interface AuditLogsCradle {
|
|
55
|
+
readonly emFactory: () => EntityManager;
|
|
56
|
+
readonly auditLogService: AuditPort;
|
|
57
|
+
readonly requireAdmin: RequireAdminFactory;
|
|
58
|
+
readonly auditActorResolver: ((ids: string[]) => Promise<AuditActorIdentity[]>) | undefined;
|
|
59
|
+
readonly auditReferenceRegistry: AuditReferenceRegistryPort;
|
|
60
|
+
readonly recentActivityService: RecentActivityService;
|
|
61
|
+
readonly recentActivityCatalog: RecentActivityCatalog;
|
|
62
|
+
readonly recentActivityVisibility: RecentActivityVisibility;
|
|
63
|
+
/**
|
|
64
|
+
* Which modules this composition ships, and what each declares — a
|
|
65
|
+
* composition-root input by nature and therefore a platform-owned container
|
|
66
|
+
* name, never a port (Constitution I: a module may not decide which modules a
|
|
67
|
+
* deployment has). It is the input the four retired action tables are derived
|
|
68
|
+
* from, and reading it here rather than importing `_lifecycle`'s registry is
|
|
69
|
+
* what keeps the derivation free of a cross-module reach.
|
|
70
|
+
*/
|
|
71
|
+
readonly resolvedModuleRegistry: ReadonlyArray<RecentActivityDeclarationSource>;
|
|
72
|
+
/** Absent in a composition with no Command Bus; the card still reads. */
|
|
73
|
+
readonly commandBus: CommandBus | undefined;
|
|
74
|
+
}
|
|
75
|
+
export declare function registerModule(ctx: ModuleContext): void;
|
|
76
|
+
/**
|
|
77
|
+
* This module owns **no persisted entity**, and says so with an empty array
|
|
78
|
+
* rather than by omission (D-168).
|
|
79
|
+
*
|
|
80
|
+
* The two are not the same thing to the platform. When the package is
|
|
81
|
+
* *installed*, `src/packages/package-runtime.ts` reads `exported['entities']`
|
|
82
|
+
* and answers a missing export with `[]` — so "this module has no table" and
|
|
83
|
+
* "somebody forgot the array" arrive at the host as the same silence, and the
|
|
84
|
+
* only symptom of the second is a query against a table nobody created. The
|
|
85
|
+
* declaration is what makes the first case a statement.
|
|
86
|
+
*
|
|
87
|
+
* A first entity added here goes in this array in the same merge request, and
|
|
88
|
+
* `test/unit/packages/module-package-entity-surface.test.ts` is what says so.
|
|
89
|
+
*/
|
|
90
|
+
export declare const entities: readonly never[];
|
|
91
|
+
//# sourceMappingURL=index.d.ts.map
|