@byline/host-tanstack-start 3.10.1 → 3.11.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.
@@ -0,0 +1,10 @@
1
+ import type { SystemActivityResponse } from '../../server-fns/admin-activity/index.js';
2
+ /**
3
+ * The system-wide activity report (docs/AUDIT.md — Workstream 4): a paged,
4
+ * filterable feed over the union of the version stream (content saves) and the
5
+ * audit log (status / path / locale changes, deletions, and future admin-realm
6
+ * events). Read-only; gated by `admin.activity.read`.
7
+ */
8
+ export declare const ActivitySystemView: ({ data }: {
9
+ data: SystemActivityResponse;
10
+ }) => import("react").JSX.Element;
@@ -0,0 +1,228 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useRouterState } from "@tanstack/react-router";
4
+ import { getClientConfig } from "@byline/core";
5
+ import { useTranslation } from "@byline/i18n/react";
6
+ import { Button, Container, Section, Select, Table } from "@byline/ui/react";
7
+ import classnames from "classnames";
8
+ import { Link, useNavigate } from "../chrome/loose-router.js";
9
+ import { RouterPager } from "../chrome/router-pager.js";
10
+ import list_module from "./list.module.js";
11
+ const ACTION_KEYS = {
12
+ 'document.created': 'activity.actions.created',
13
+ 'document.updated': 'activity.actions.updated',
14
+ 'document.path.changed': 'activity.actions.pathChanged',
15
+ 'document.locales.changed': 'activity.actions.localesChanged',
16
+ 'document.status.changed': 'activity.actions.statusChanged',
17
+ 'document.deleted': 'activity.actions.deleted'
18
+ };
19
+ const ACTION_FILTER_VALUES = [
20
+ 'document.created',
21
+ 'document.updated',
22
+ 'document.status.changed',
23
+ 'document.path.changed',
24
+ 'document.locales.changed',
25
+ 'document.deleted'
26
+ ];
27
+ function formatAuditValue(value) {
28
+ if (null == value) return '—';
29
+ if (Array.isArray(value)) return value.length > 0 ? value.join(', ') : '—';
30
+ return String(value);
31
+ }
32
+ const ActivitySystemView = ({ data })=>{
33
+ const { t } = useTranslation('byline-admin');
34
+ const navigate = useNavigate();
35
+ const location = useRouterState({
36
+ select: (s)=>s.location
37
+ });
38
+ const search = location.search;
39
+ const entries = data?.entries ?? [];
40
+ const collections = getClientConfig().collections;
41
+ const applyFilter = (key, value)=>{
42
+ const params = structuredClone(location.search);
43
+ params.page = void 0;
44
+ if ('_all' === value) params[key] = void 0;
45
+ else params[key] = value;
46
+ navigate({
47
+ to: '/admin/activity',
48
+ search: params
49
+ });
50
+ };
51
+ const collectionItems = [
52
+ {
53
+ value: '_all',
54
+ label: t('activity.filters.allCollections')
55
+ },
56
+ ...collections.map((c)=>({
57
+ value: c.path,
58
+ label: c.labels?.plural ?? c.path
59
+ }))
60
+ ];
61
+ const actionItems = [
62
+ {
63
+ value: '_all',
64
+ label: t('activity.filters.allActions')
65
+ },
66
+ ...ACTION_FILTER_VALUES.map((a)=>({
67
+ value: a,
68
+ label: ACTION_KEYS[a] ? t(ACTION_KEYS[a]) : a
69
+ }))
70
+ ];
71
+ const hasFilters = null != search.collection || null != search.action;
72
+ return /*#__PURE__*/ jsx(Section, {
73
+ children: /*#__PURE__*/ jsxs(Container, {
74
+ children: [
75
+ /*#__PURE__*/ jsx("div", {
76
+ className: classnames('byline-activity-head', list_module.head),
77
+ children: /*#__PURE__*/ jsx("h1", {
78
+ className: classnames('byline-activity-title', list_module.title),
79
+ children: t('activity.title')
80
+ })
81
+ }),
82
+ /*#__PURE__*/ jsxs("div", {
83
+ className: classnames('byline-activity-options', list_module.options),
84
+ children: [
85
+ /*#__PURE__*/ jsx(Select, {
86
+ id: "activity_collection_filter",
87
+ name: "activity_collection_filter",
88
+ size: "sm",
89
+ value: search.collection ?? '_all',
90
+ items: collectionItems,
91
+ onValueChange: (v)=>{
92
+ if ('string' == typeof v) applyFilter('collection', v);
93
+ }
94
+ }),
95
+ /*#__PURE__*/ jsx(Select, {
96
+ id: "activity_action_filter",
97
+ name: "activity_action_filter",
98
+ size: "sm",
99
+ value: search.action ?? '_all',
100
+ items: actionItems,
101
+ onValueChange: (v)=>{
102
+ if ('string' == typeof v) applyFilter('action', v);
103
+ }
104
+ }),
105
+ hasFilters && /*#__PURE__*/ jsx(Button, {
106
+ size: "sm",
107
+ variant: "text",
108
+ onClick: ()=>navigate({
109
+ to: '/admin/activity',
110
+ search: {}
111
+ }),
112
+ children: t('activity.filters.clear')
113
+ }),
114
+ /*#__PURE__*/ jsx(RouterPager, {
115
+ page: data.meta.page,
116
+ count: data.meta.totalPages,
117
+ showFirstButton: true,
118
+ showLastButton: true,
119
+ componentName: "pagerTop",
120
+ "aria-label": t('activity.pagerAriaLabel')
121
+ })
122
+ ]
123
+ }),
124
+ 0 === entries.length ? /*#__PURE__*/ jsx("p", {
125
+ className: classnames('byline-activity-empty', list_module.empty),
126
+ children: t('activity.empty')
127
+ }) : /*#__PURE__*/ jsx(Table.Container, {
128
+ className: classnames('byline-activity-table-wrap', list_module.tableWrap),
129
+ children: /*#__PURE__*/ jsxs(Table, {
130
+ children: [
131
+ /*#__PURE__*/ jsx(Table.Header, {
132
+ children: /*#__PURE__*/ jsxs(Table.Row, {
133
+ children: [
134
+ /*#__PURE__*/ jsx(Table.HeadingCell, {
135
+ scope: "col",
136
+ children: t('activity.columns.when')
137
+ }),
138
+ /*#__PURE__*/ jsx(Table.HeadingCell, {
139
+ scope: "col",
140
+ children: t('activity.columns.collection')
141
+ }),
142
+ /*#__PURE__*/ jsx(Table.HeadingCell, {
143
+ scope: "col",
144
+ children: t('activity.columns.action')
145
+ }),
146
+ /*#__PURE__*/ jsx(Table.HeadingCell, {
147
+ scope: "col",
148
+ children: t('activity.columns.actor')
149
+ }),
150
+ /*#__PURE__*/ jsx(Table.HeadingCell, {
151
+ scope: "col",
152
+ children: t('activity.columns.change')
153
+ })
154
+ ]
155
+ })
156
+ }),
157
+ /*#__PURE__*/ jsx(Table.Body, {
158
+ children: entries.map((entry)=>{
159
+ const actionKey = ACTION_KEYS[entry.action];
160
+ const actionLabel = actionKey ? t(actionKey) : entry.action;
161
+ const actorLabel = null == entry.actorId || 'system' === entry.actorRealm ? t('activity.systemActor') : data.actors?.[entry.actorId]?.label ?? t('activity.formerUser');
162
+ const col = entry.collectionId ? data.collections[entry.collectionId] : void 0;
163
+ const collectionLabel = col?.plural ?? col?.path ?? '—';
164
+ const hasChange = null != entry.before || null != entry.after;
165
+ return /*#__PURE__*/ jsxs(Table.Row, {
166
+ children: [
167
+ /*#__PURE__*/ jsx(Table.Cell, {
168
+ className: classnames('byline-activity-when', list_module.when),
169
+ children: new Date(entry.occurredAt).toLocaleString()
170
+ }),
171
+ /*#__PURE__*/ jsx(Table.Cell, {
172
+ children: null != col && null != entry.documentId ? /*#__PURE__*/ jsx(Link, {
173
+ to: '/admin/collections/$collection/$id',
174
+ params: {
175
+ collection: col.path,
176
+ id: entry.documentId
177
+ },
178
+ children: collectionLabel
179
+ }) : collectionLabel
180
+ }),
181
+ /*#__PURE__*/ jsx(Table.Cell, {
182
+ children: actionLabel
183
+ }),
184
+ /*#__PURE__*/ jsx(Table.Cell, {
185
+ children: actorLabel
186
+ }),
187
+ /*#__PURE__*/ jsx(Table.Cell, {
188
+ className: classnames('byline-activity-change', list_module.change),
189
+ children: hasChange ? /*#__PURE__*/ jsxs(Fragment, {
190
+ children: [
191
+ /*#__PURE__*/ jsx("span", {
192
+ className: classnames('byline-activity-before', list_module.before),
193
+ children: formatAuditValue(entry.before)
194
+ }),
195
+ /*#__PURE__*/ jsx("span", {
196
+ className: classnames('byline-activity-arrow', list_module.arrow),
197
+ children: ' → '
198
+ }),
199
+ /*#__PURE__*/ jsx("span", {
200
+ className: classnames('byline-activity-after', list_module.after),
201
+ children: formatAuditValue(entry.after)
202
+ })
203
+ ]
204
+ }) : '—'
205
+ })
206
+ ]
207
+ }, entry.id);
208
+ })
209
+ })
210
+ ]
211
+ })
212
+ }),
213
+ /*#__PURE__*/ jsx("div", {
214
+ className: classnames('byline-activity-options', list_module.options),
215
+ children: /*#__PURE__*/ jsx(RouterPager, {
216
+ page: data.meta.page,
217
+ count: data.meta.totalPages,
218
+ showFirstButton: true,
219
+ showLastButton: true,
220
+ componentName: "pagerBottom",
221
+ "aria-label": t('activity.pagerAriaLabel')
222
+ })
223
+ })
224
+ ]
225
+ })
226
+ });
227
+ };
228
+ export { ActivitySystemView };
@@ -0,0 +1,12 @@
1
+ import "./list_module.css";
2
+ const list_module = {
3
+ head: "head-kI5Xwr",
4
+ title: "title-ealGXb",
5
+ options: "options-phg69k",
6
+ empty: "empty-abhubg",
7
+ tableWrap: "tableWrap-VncCtZ",
8
+ when: "when-LlN6zo",
9
+ change: "change-jeXuPf",
10
+ arrow: "arrow-W5ERDF"
11
+ };
12
+ export default list_module;
@@ -0,0 +1,45 @@
1
+ :is(.head-kI5Xwr, .byline-activity-head) {
2
+ margin-bottom: .5rem;
3
+ }
4
+
5
+ :is(.title-ealGXb, .byline-activity-title) {
6
+ font-size: 1.25rem;
7
+ font-weight: 600;
8
+ }
9
+
10
+ :is(.options-phg69k, .byline-activity-options) {
11
+ flex-wrap: wrap;
12
+ align-items: center;
13
+ gap: .5rem;
14
+ margin: .75rem 0;
15
+ display: flex;
16
+ }
17
+
18
+ :is(.empty-abhubg, .byline-activity-empty) {
19
+ color: var(--gray-500);
20
+ margin: 1.5rem 0;
21
+ font-size: .875rem;
22
+ }
23
+
24
+ :is(:is([data-theme="dark"], .dark) .empty-abhubg, :is([data-theme="dark"], .dark) .byline-activity-empty) {
25
+ color: var(--gray-400);
26
+ }
27
+
28
+ :is(.tableWrap-VncCtZ, .byline-activity-table-wrap) {
29
+ margin-top: .5rem;
30
+ margin-bottom: .75rem;
31
+ }
32
+
33
+ :is(.when-LlN6zo, .byline-activity-when) {
34
+ white-space: nowrap;
35
+ font-variant-numeric: tabular-nums;
36
+ }
37
+
38
+ :is(.change-jeXuPf, .byline-activity-change) {
39
+ font-variant-numeric: tabular-nums;
40
+ }
41
+
42
+ :is(.arrow-W5ERDF, .byline-activity-arrow) {
43
+ color: var(--gray-400);
44
+ }
45
+
@@ -1,10 +1,11 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useRouterState } from "@tanstack/react-router";
3
+ import { ADMIN_ACTIVITY_ABILITIES } from "@byline/admin/admin-activity";
3
4
  import { ADMIN_PERMISSIONS_ABILITIES } from "@byline/admin/admin-permissions";
4
5
  import { ADMIN_ROLES_ABILITIES } from "@byline/admin/admin-roles";
5
6
  import { ADMIN_USERS_ABILITIES } from "@byline/admin/admin-users";
6
7
  import { useTranslation } from "@byline/i18n/react";
7
- import { HomeIcon, RolesIcon, SettingsSlidersIcon, UserIcon, UsersIcon } from "@byline/ui/react";
8
+ import { ActivityIcon, HomeIcon, RolesIcon, SettingsSlidersIcon, Tooltip, UserIcon, UsersIcon } from "@byline/ui/react";
8
9
  import classnames from "classnames";
9
10
  import { useSwipeable } from "react-swipeable";
10
11
  import { useAbilities } from "../../integrations/abilities.js";
@@ -17,6 +18,10 @@ const isActive = (currentPath, linkHref)=>{
17
18
  return currentPath.startsWith(linkHref);
18
19
  };
19
20
  function MenuItem({ to, label, icon, pathname, compact }) {
21
+ const iconSpan = /*#__PURE__*/ jsx("span", {
22
+ className: "icon",
23
+ children: icon
24
+ });
20
25
  return /*#__PURE__*/ jsx("li", {
21
26
  className: classnames('menu-item', {
22
27
  active: isActive(pathname, to),
@@ -25,10 +30,11 @@ function MenuItem({ to, label, icon, pathname, compact }) {
25
30
  children: /*#__PURE__*/ jsxs(Link, {
26
31
  to: to,
27
32
  children: [
28
- /*#__PURE__*/ jsx("span", {
29
- className: "icon",
30
- children: icon
31
- }),
33
+ compact ? /*#__PURE__*/ jsx(Tooltip, {
34
+ text: label,
35
+ side: "right",
36
+ children: iconSpan
37
+ }) : iconSpan,
32
38
  /*#__PURE__*/ jsx("span", {
33
39
  className: "label",
34
40
  children: label
@@ -47,7 +53,8 @@ function AdminMenuDrawer() {
47
53
  const canReadUsers = has(ADMIN_USERS_ABILITIES.read);
48
54
  const canReadRoles = has(ADMIN_ROLES_ABILITIES.read);
49
55
  const canReadPermissions = has(ADMIN_PERMISSIONS_ABILITIES.read);
50
- const showAdminSection = canReadUsers || canReadRoles || canReadPermissions;
56
+ const canReadActivity = has(ADMIN_ACTIVITY_ABILITIES.read);
57
+ const showAdminSection = canReadUsers || canReadRoles || canReadPermissions || canReadActivity;
51
58
  const handlers = useSwipeable({
52
59
  onSwipedLeft: ()=>{
53
60
  closeDrawer();
@@ -119,6 +126,16 @@ function AdminMenuDrawer() {
119
126
  }),
120
127
  pathname: pathname,
121
128
  compact: compact
129
+ }),
130
+ canReadActivity && /*#__PURE__*/ jsx(MenuItem, {
131
+ to: "/admin/activity",
132
+ label: t('chrome.menu.activity'),
133
+ icon: /*#__PURE__*/ jsx(ActivityIcon, {
134
+ width: "20px",
135
+ height: "20px"
136
+ }),
137
+ pathname: pathname,
138
+ compact: compact
122
139
  })
123
140
  ]
124
141
  }),
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from "react";
3
3
  import { useTranslation } from "@byline/i18n/react";
4
- import { EyeClosedIcon, EyeOpenIcon } from "@byline/ui/react";
4
+ import { EyeClosedIcon, EyeOpenIcon, Tooltip } from "@byline/ui/react";
5
5
  import classnames from "classnames";
6
6
  import { disablePreviewModeFn, enablePreviewModeFn, getPreviewStateFn } from "../../server-fns/preview/index.js";
7
7
  function PreviewToggle({ compact }) {
@@ -50,6 +50,10 @@ function PreviewToggle({ compact }) {
50
50
  width: "20px",
51
51
  height: "20px"
52
52
  });
53
+ const iconSpan = /*#__PURE__*/ jsx("span", {
54
+ className: "icon",
55
+ children: icon
56
+ });
53
57
  return /*#__PURE__*/ jsx("li", {
54
58
  className: classnames('menu-item byline-preview-toggle', {
55
59
  compact,
@@ -60,12 +64,13 @@ function PreviewToggle({ compact }) {
60
64
  onClick: handleToggle,
61
65
  disabled: busy,
62
66
  "aria-label": preview ? t('chrome.preview.disableAriaLabel') : t('chrome.preview.enableAriaLabel'),
63
- title: preview ? t('chrome.preview.onTitle') : t('chrome.preview.offTitle'),
67
+ title: compact ? void 0 : preview ? t('chrome.preview.onTitle') : t('chrome.preview.offTitle'),
64
68
  children: [
65
- /*#__PURE__*/ jsx("span", {
66
- className: "icon",
67
- children: icon
68
- }),
69
+ compact ? /*#__PURE__*/ jsx(Tooltip, {
70
+ text: label,
71
+ side: "right",
72
+ children: iconSpan
73
+ }) : iconSpan,
69
74
  /*#__PURE__*/ jsx("span", {
70
75
  className: "label",
71
76
  children: label
@@ -0,0 +1,8 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ export declare function createAdminActivityRoute(path: string): any;
@@ -0,0 +1,71 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { createFileRoute } from "@tanstack/react-router";
3
+ import { useTranslation } from "@byline/i18n/react";
4
+ import { z } from "zod";
5
+ import { ActivitySystemView } from "../admin-shell/admin-activity/list.js";
6
+ import { BreadcrumbsClient } from "../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js";
7
+ import { getSystemActivityLog } from "../server-fns/admin-activity/index.js";
8
+ const searchSchema = z.object({
9
+ page: z.coerce.number().int().min(1).optional(),
10
+ page_size: z.coerce.number().int().min(1).max(100).optional(),
11
+ collection: z.string().optional(),
12
+ action: z.string().optional(),
13
+ actorId: z.string().optional(),
14
+ from: z.string().optional(),
15
+ to: z.string().optional()
16
+ });
17
+ function createAdminActivityRoute(path) {
18
+ const Route = createFileRoute(path)({
19
+ validateSearch: searchSchema,
20
+ loaderDeps: ({ search })=>({
21
+ page: search.page,
22
+ page_size: search.page_size,
23
+ collection: search.collection,
24
+ action: search.action,
25
+ actorId: search.actorId,
26
+ from: search.from,
27
+ to: search.to
28
+ }),
29
+ loader: async ({ deps })=>{
30
+ const data = await getSystemActivityLog({
31
+ data: {
32
+ page: deps.page,
33
+ page_size: deps.page_size,
34
+ collection: deps.collection,
35
+ action: deps.action,
36
+ actorId: deps.actorId,
37
+ from: deps.from,
38
+ to: deps.to
39
+ }
40
+ });
41
+ return {
42
+ data
43
+ };
44
+ },
45
+ component: function() {
46
+ const { data } = Route.useLoaderData();
47
+ const { t } = useTranslation('byline-admin');
48
+ return /*#__PURE__*/ jsxs(Fragment, {
49
+ children: [
50
+ /*#__PURE__*/ jsx(BreadcrumbsClient, {
51
+ breadcrumbs: [
52
+ {
53
+ label: t('chrome.menu.dashboard'),
54
+ href: '/admin'
55
+ },
56
+ {
57
+ label: t('activity.title'),
58
+ href: '/admin/activity'
59
+ }
60
+ ]
61
+ }),
62
+ /*#__PURE__*/ jsx(ActivitySystemView, {
63
+ data: data
64
+ })
65
+ ]
66
+ });
67
+ }
68
+ });
69
+ return Route;
70
+ }
71
+ export { createAdminActivityRoute };
@@ -19,6 +19,7 @@
19
19
  * locales) take a second `opts` argument.
20
20
  */
21
21
  export { createAdminAccountRoute } from './create-admin-account-route.js';
22
+ export { createAdminActivityRoute } from './create-admin-activity-route.js';
22
23
  export { createAdminDashboardRoute } from './create-admin-dashboard-route.js';
23
24
  export { createAdminLayoutRoute } from './create-admin-layout-route.js';
24
25
  export { createAdminPermissionsRoute } from './create-admin-permissions-route.js';
@@ -1,4 +1,5 @@
1
1
  export { createAdminAccountRoute } from "./create-admin-account-route.js";
2
+ export { createAdminActivityRoute } from "./create-admin-activity-route.js";
2
3
  export { createAdminDashboardRoute } from "./create-admin-dashboard-route.js";
3
4
  export { createAdminLayoutRoute } from "./create-admin-layout-route.js";
4
5
  export { createAdminPermissionsRoute } from "./create-admin-permissions-route.js";
@@ -0,0 +1,50 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ import { type ActorLabelMap } from '../collections/actors.js';
9
+ import type { AuditLogEntryDto } from '../collections/audit.js';
10
+ /** Filters for the activity feed. `from` / `to` are ISO strings over the wire. */
11
+ export interface SystemActivitySearchParams {
12
+ actorId?: string;
13
+ /** Collection **path** (the admin works in paths; the handler resolves it to the stored collection id). */
14
+ collection?: string;
15
+ action?: string;
16
+ from?: string;
17
+ to?: string;
18
+ page?: number;
19
+ page_size?: number;
20
+ }
21
+ /** Collection display info, captured at query time (a row may name a collection that was later renamed/removed). */
22
+ export interface ActivityCollectionInfo {
23
+ path: string;
24
+ singular: string;
25
+ plural: string;
26
+ }
27
+ export type ActivityCollectionMap = Record<string, ActivityCollectionInfo>;
28
+ export interface SystemActivityResponse {
29
+ entries: AuditLogEntryDto[];
30
+ meta: {
31
+ total: number;
32
+ page: number;
33
+ pageSize: number;
34
+ totalPages: number;
35
+ };
36
+ /** Acting-user id → display label. Absent ids (system rows, deleted users) render a tombstone. */
37
+ actors: ActorLabelMap;
38
+ /** Collection id → display info. Absent ids name a removed collection. */
39
+ collections: ActivityCollectionMap;
40
+ }
41
+ /**
42
+ * The system-wide activity feed: the union of the version stream (content
43
+ * saves) and the audit log (status / path / locale changes, deletions, and
44
+ * future admin-realm events). Unlike the per-document audit fn, this is NOT
45
+ * routed through `CollectionHandle` — it is cross-collection and includes
46
+ * admin-realm rows with no `document_id`, so it reads the adapter's audit
47
+ * queries directly and is gated system-wide by `admin.activity.read` rather
48
+ * than by any document's own read gate.
49
+ */
50
+ export declare const getSystemActivityLog: import("@tanstack/react-start").RequiredFetcher<undefined, (input: SystemActivitySearchParams) => SystemActivitySearchParams, Promise<SystemActivityResponse>>;
@@ -0,0 +1,71 @@
1
+ import { createServerFn } from "@tanstack/react-start";
2
+ import { assertAdminActor } from "@byline/admin";
3
+ import { ADMIN_ACTIVITY_ABILITIES } from "@byline/admin/admin-activity";
4
+ import { ERR_AUDIT_UNSUPPORTED, getLogger, getServerConfig } from "@byline/core";
5
+ import { getAdminRequestContext } from "../../auth/auth-context.js";
6
+ import { resolveActorLabels } from "../collections/actors.js";
7
+ const getSystemActivityLog = createServerFn({
8
+ method: 'GET'
9
+ }).validator((input)=>input ?? {}).handler(async ({ data })=>{
10
+ const context = await getAdminRequestContext();
11
+ assertAdminActor(context, ADMIN_ACTIVITY_ABILITIES.read);
12
+ const queries = getServerConfig().db.queries;
13
+ if (null == queries.audit) throw ERR_AUDIT_UNSUPPORTED({
14
+ message: 'the configured db adapter does not support audit-log reads (queries.audit)'
15
+ }).log(getLogger());
16
+ let collectionId;
17
+ if (data.collection) {
18
+ const col = await queries.collections.getCollectionByPath(data.collection);
19
+ collectionId = col?.id;
20
+ if (null == collectionId) return {
21
+ entries: [],
22
+ meta: {
23
+ total: 0,
24
+ page: 1,
25
+ pageSize: 0,
26
+ totalPages: 0
27
+ },
28
+ actors: {},
29
+ collections: {}
30
+ };
31
+ }
32
+ const result = await queries.audit.findAuditLog({
33
+ actorId: data.actorId,
34
+ collectionId,
35
+ action: data.action,
36
+ from: data.from ? new Date(data.from) : void 0,
37
+ to: data.to ? new Date(data.to) : void 0,
38
+ page: data.page,
39
+ page_size: data.page_size
40
+ });
41
+ const actors = await resolveActorLabels(result.entries.map((e)=>e.actorId));
42
+ const collections = {};
43
+ const collectionIds = new Set(result.entries.map((e)=>e.collectionId).filter((id)=>null != id));
44
+ if (collectionIds.size > 0) {
45
+ const all = await queries.collections.getAllCollections();
46
+ for (const c of all)if (collectionIds.has(c.id)) collections[c.id] = {
47
+ path: c.path,
48
+ singular: c.singular,
49
+ plural: c.plural
50
+ };
51
+ }
52
+ const entries = result.entries.map((e)=>({
53
+ id: e.id,
54
+ documentId: e.documentId,
55
+ collectionId: e.collectionId,
56
+ actorId: e.actorId,
57
+ actorRealm: e.actorRealm,
58
+ action: e.action,
59
+ field: e.field,
60
+ before: e.before,
61
+ after: e.after,
62
+ occurredAt: e.occurredAt instanceof Date ? e.occurredAt.toISOString() : String(e.occurredAt)
63
+ }));
64
+ return {
65
+ entries,
66
+ meta: result.meta,
67
+ actors,
68
+ collections
69
+ };
70
+ });
71
+ export { getSystemActivityLog };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ /**
9
+ * Admin-activity server fns (docs/AUDIT.md — Workstream 4) — the
10
+ * system-wide activity report. Reads the adapter's audit queries directly
11
+ * (cross-collection, includes admin-realm rows) behind the
12
+ * `admin.activity.read` gate, rather than routing through a per-document
13
+ * read like the collections audit fn.
14
+ */
15
+ export { type ActivityCollectionInfo, type ActivityCollectionMap, getSystemActivityLog, type SystemActivityResponse, type SystemActivitySearchParams, } from './get.js';
@@ -0,0 +1 @@
1
+ export { getSystemActivityLog } from "./get.js";
@@ -16,7 +16,7 @@ export declare const listRegisteredAbilities: import("@tanstack/react-start").Op
16
16
  label: string;
17
17
  description: string | null;
18
18
  group: string;
19
- source: "admin" | "collection" | "plugin" | "core" | null;
19
+ source: "collection" | "admin" | "plugin" | "core" | null;
20
20
  }[];
21
21
  groups: {
22
22
  group: string;
@@ -25,7 +25,7 @@ export declare const listRegisteredAbilities: import("@tanstack/react-start").Op
25
25
  label: string;
26
26
  description: string | null;
27
27
  group: string;
28
- source: "admin" | "collection" | "plugin" | "core" | null;
28
+ source: "collection" | "admin" | "plugin" | "core" | null;
29
29
  }[];
30
30
  }[];
31
31
  total: number;