@byline/host-tanstack-start 3.9.0 → 3.10.1
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/dist/admin-shell/collections/document-history.d.ts +53 -0
- package/dist/admin-shell/collections/document-history.js +103 -0
- package/dist/admin-shell/collections/document-history.module.js +9 -0
- package/dist/admin-shell/collections/document-history_module.css +28 -0
- package/dist/admin-shell/collections/history.d.ts +8 -1
- package/dist/admin-shell/collections/history.js +73 -29
- package/dist/admin-shell/collections/history.module.js +1 -0
- package/dist/admin-shell/collections/history_module.css +4 -0
- package/dist/routes/create-collection-history-route.js +23 -6
- package/dist/server-fns/admin-account/change-password.js +1 -1
- package/dist/server-fns/admin-account/get.js +1 -1
- package/dist/server-fns/admin-account/update.js +1 -1
- package/dist/server-fns/admin-permissions/get-role-abilities.js +1 -1
- package/dist/server-fns/admin-permissions/set-role-abilities.js +1 -1
- package/dist/server-fns/admin-permissions/who-has.js +1 -1
- package/dist/server-fns/admin-roles/create.js +1 -1
- package/dist/server-fns/admin-roles/delete.js +1 -1
- package/dist/server-fns/admin-roles/get.js +1 -1
- package/dist/server-fns/admin-roles/reorder.js +1 -1
- package/dist/server-fns/admin-roles/update.js +1 -1
- package/dist/server-fns/admin-users/create.js +1 -1
- package/dist/server-fns/admin-users/delete.js +1 -1
- package/dist/server-fns/admin-users/disable.js +1 -1
- package/dist/server-fns/admin-users/enable.js +1 -1
- package/dist/server-fns/admin-users/get-user-roles.js +1 -1
- package/dist/server-fns/admin-users/get.js +1 -1
- package/dist/server-fns/admin-users/list.js +1 -1
- package/dist/server-fns/admin-users/set-password.js +1 -1
- package/dist/server-fns/admin-users/set-user-roles.js +1 -1
- package/dist/server-fns/admin-users/update.js +1 -1
- package/dist/server-fns/ai/execute.js +1 -1
- package/dist/server-fns/auth/sign-in.js +1 -1
- package/dist/server-fns/collections/audit.d.ts +50 -0
- package/dist/server-fns/collections/audit.js +40 -0
- package/dist/server-fns/collections/copy-to-locale.js +1 -1
- package/dist/server-fns/collections/create.js +1 -1
- package/dist/server-fns/collections/delete-locale.js +1 -1
- package/dist/server-fns/collections/delete.js +1 -1
- package/dist/server-fns/collections/duplicate.js +1 -1
- package/dist/server-fns/collections/get.js +2 -2
- package/dist/server-fns/collections/history.js +1 -1
- package/dist/server-fns/collections/index.d.ts +1 -0
- package/dist/server-fns/collections/index.js +1 -0
- package/dist/server-fns/collections/list.js +1 -1
- package/dist/server-fns/collections/reorder.js +1 -1
- package/dist/server-fns/collections/restore-version.js +1 -1
- package/dist/server-fns/collections/stats.js +1 -1
- package/dist/server-fns/collections/status.js +2 -2
- package/dist/server-fns/collections/update.js +2 -2
- package/dist/server-fns/collections/upload.js +1 -1
- package/dist/server-fns/i18n/set-locale.js +1 -1
- package/package.json +10 -10
- package/src/admin-shell/collections/document-history.module.css +46 -0
- package/src/admin-shell/collections/document-history.tsx +156 -0
- package/src/admin-shell/collections/history.module.css +6 -0
- package/src/admin-shell/collections/history.tsx +331 -281
- package/src/routes/create-collection-history-route.tsx +25 -3
- package/src/server-fns/admin-account/change-password.ts +1 -1
- package/src/server-fns/admin-account/get.ts +1 -1
- package/src/server-fns/admin-account/update.ts +1 -1
- package/src/server-fns/admin-permissions/get-role-abilities.ts +1 -1
- package/src/server-fns/admin-permissions/set-role-abilities.ts +1 -1
- package/src/server-fns/admin-permissions/who-has.ts +1 -1
- package/src/server-fns/admin-roles/create.ts +1 -1
- package/src/server-fns/admin-roles/delete.ts +1 -1
- package/src/server-fns/admin-roles/get.ts +1 -1
- package/src/server-fns/admin-roles/reorder.ts +1 -1
- package/src/server-fns/admin-roles/update.ts +1 -1
- package/src/server-fns/admin-users/create.ts +1 -1
- package/src/server-fns/admin-users/delete.ts +1 -1
- package/src/server-fns/admin-users/disable.ts +1 -1
- package/src/server-fns/admin-users/enable.ts +1 -1
- package/src/server-fns/admin-users/get-user-roles.ts +1 -1
- package/src/server-fns/admin-users/get.ts +1 -1
- package/src/server-fns/admin-users/list.ts +1 -1
- package/src/server-fns/admin-users/set-password.ts +1 -1
- package/src/server-fns/admin-users/set-user-roles.ts +1 -1
- package/src/server-fns/admin-users/update.ts +1 -1
- package/src/server-fns/ai/execute.ts +1 -1
- package/src/server-fns/auth/sign-in.ts +1 -1
- package/src/server-fns/collections/audit.ts +95 -0
- package/src/server-fns/collections/copy-to-locale.ts +1 -1
- package/src/server-fns/collections/create.ts +1 -1
- package/src/server-fns/collections/delete-locale.ts +1 -1
- package/src/server-fns/collections/delete.ts +1 -1
- package/src/server-fns/collections/duplicate.ts +1 -1
- package/src/server-fns/collections/get.ts +2 -2
- package/src/server-fns/collections/history.ts +1 -1
- package/src/server-fns/collections/index.ts +1 -0
- package/src/server-fns/collections/list.ts +1 -1
- package/src/server-fns/collections/reorder.ts +1 -1
- package/src/server-fns/collections/restore-version.ts +1 -1
- package/src/server-fns/collections/stats.ts +1 -1
- package/src/server-fns/collections/status.ts +2 -2
- package/src/server-fns/collections/update.ts +2 -2
- package/src/server-fns/collections/upload.ts +1 -1
- package/src/server-fns/i18n/set-locale.ts +1 -1
|
@@ -21,6 +21,7 @@ import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs
|
|
|
21
21
|
import { HistoryView } from '../admin-shell/collections/history.js'
|
|
22
22
|
import {
|
|
23
23
|
getCollectionDocument,
|
|
24
|
+
getCollectionDocumentAuditLog,
|
|
24
25
|
getCollectionDocumentHistory,
|
|
25
26
|
} from '../server-fns/collections/index.js'
|
|
26
27
|
import type { ContentLocaleOption } from '../admin-shell/collections/view-menu.js'
|
|
@@ -31,8 +32,17 @@ const searchSchema = z.object({
|
|
|
31
32
|
order: z.string().optional(),
|
|
32
33
|
desc: z.coerce.boolean().optional(),
|
|
33
34
|
locale: z.string().optional(),
|
|
35
|
+
// Which sub-view of the history page is active (docs/AUDIT.md — Workstream
|
|
36
|
+
// 3). 'versions' is the content version stream; 'document' is the
|
|
37
|
+
// document-grain audit log. Absent → 'versions'.
|
|
38
|
+
tab: z.enum(['versions', 'document']).optional(),
|
|
34
39
|
})
|
|
35
40
|
|
|
41
|
+
// The per-document audit log is small and bounded (path / locale / status
|
|
42
|
+
// changes + the deletion event), so v1 fetches a single generous page rather
|
|
43
|
+
// than wiring a second, tab-specific pager into the shared history route.
|
|
44
|
+
const AUDIT_LOG_PAGE_SIZE = 100
|
|
45
|
+
|
|
36
46
|
interface CollectionHistoryOpts {
|
|
37
47
|
contentLocales: ReadonlyArray<ContentLocaleOption>
|
|
38
48
|
defaultContentLocale: string
|
|
@@ -67,7 +77,7 @@ export function createCollectionHistoryRoute(path: string, opts: CollectionHisto
|
|
|
67
77
|
throw notFound()
|
|
68
78
|
}
|
|
69
79
|
|
|
70
|
-
const [history, currentDocument] = await Promise.all([
|
|
80
|
+
const [history, currentDocument, auditLog] = await Promise.all([
|
|
71
81
|
getCollectionDocumentHistory({
|
|
72
82
|
data: {
|
|
73
83
|
collection: params.collection,
|
|
@@ -84,15 +94,26 @@ export function createCollectionHistoryRoute(path: string, opts: CollectionHisto
|
|
|
84
94
|
// Fetch the current document with the same locale (or 'all') so diffs
|
|
85
95
|
// compare the same shape as what the user is viewing.
|
|
86
96
|
getCollectionDocument(params.collection, params.id, deps.locale ?? 'all'),
|
|
97
|
+
// Document-grain audit log for the "Document history" tab (W3). Fetched
|
|
98
|
+
// in parallel and unconditionally — it's cheap, and the active tab is a
|
|
99
|
+
// pure render concern read from the URL, so switching tabs never
|
|
100
|
+
// refetches.
|
|
101
|
+
getCollectionDocumentAuditLog({
|
|
102
|
+
data: {
|
|
103
|
+
collection: params.collection,
|
|
104
|
+
id: params.id,
|
|
105
|
+
params: { page: 1, page_size: AUDIT_LOG_PAGE_SIZE },
|
|
106
|
+
},
|
|
107
|
+
}),
|
|
87
108
|
])
|
|
88
109
|
|
|
89
|
-
return { history, currentDocument }
|
|
110
|
+
return { history, currentDocument, auditLog }
|
|
90
111
|
},
|
|
91
112
|
staleTime: 0,
|
|
92
113
|
gcTime: 0,
|
|
93
114
|
shouldReload: true,
|
|
94
115
|
component: function CollectionHistoryComponent() {
|
|
95
|
-
const { history, currentDocument } = Route.useLoaderData()
|
|
116
|
+
const { history, currentDocument, auditLog } = Route.useLoaderData()
|
|
96
117
|
const { collection } = Route.useParams() as { collection: string; id: string }
|
|
97
118
|
const collectionDef = getCollectionDefinition(collection) as CollectionDefinition
|
|
98
119
|
const adminConfig = getCollectionAdminConfig(collection)
|
|
@@ -119,6 +140,7 @@ export function createCollectionHistoryRoute(path: string, opts: CollectionHisto
|
|
|
119
140
|
workflowStatuses={getWorkflowStatuses(collectionDef)}
|
|
120
141
|
adminConfig={adminConfig ?? undefined}
|
|
121
142
|
data={history}
|
|
143
|
+
auditLog={auditLog}
|
|
122
144
|
currentDocument={currentDocument as Record<string, unknown> | null}
|
|
123
145
|
contentLocales={opts.contentLocales}
|
|
124
146
|
defaultContentLocale={opts.defaultContentLocale}
|
|
@@ -20,7 +20,7 @@ export interface ChangeAccountPasswordInput {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export const changeAccountPassword = createServerFn({ method: 'POST' })
|
|
23
|
-
.
|
|
23
|
+
.validator((input: ChangeAccountPasswordInput) => input)
|
|
24
24
|
.handler(async ({ data }): Promise<AccountResponse> => {
|
|
25
25
|
const context = await getAdminRequestContext()
|
|
26
26
|
return changeAccountPasswordCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -20,7 +20,7 @@ import { bylineCore } from '../../integrations/byline-core.js'
|
|
|
20
20
|
* doesn't carry.
|
|
21
21
|
*/
|
|
22
22
|
export const getAccount = createServerFn({ method: 'GET' })
|
|
23
|
-
.
|
|
23
|
+
.validator((input?: Record<string, never>) => input ?? {})
|
|
24
24
|
.handler(async ({ data }): Promise<AccountResponse> => {
|
|
25
25
|
const context = await getAdminRequestContext()
|
|
26
26
|
return getAccountCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -24,7 +24,7 @@ export interface UpdateAccountInput {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export const updateAccount = createServerFn({ method: 'POST' })
|
|
27
|
-
.
|
|
27
|
+
.validator((input: UpdateAccountInput) => input)
|
|
28
28
|
.handler(async ({ data }): Promise<AccountResponse> => {
|
|
29
29
|
const context = await getAdminRequestContext()
|
|
30
30
|
return updateAccountCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -17,7 +17,7 @@ import { getAdminRequestContext } from '../../auth/auth-context.js'
|
|
|
17
17
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
18
18
|
|
|
19
19
|
export const getRoleAbilities = createServerFn({ method: 'GET' })
|
|
20
|
-
.
|
|
20
|
+
.validator((input: { id: string }) => input)
|
|
21
21
|
.handler(async ({ data }): Promise<GetRoleAbilitiesResponse> => {
|
|
22
22
|
const context = await getAdminRequestContext()
|
|
23
23
|
return getRoleAbilitiesCommand(context, data, {
|
|
@@ -22,7 +22,7 @@ export interface SetRoleAbilitiesInput {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export const setRoleAbilities = createServerFn({ method: 'POST' })
|
|
25
|
-
.
|
|
25
|
+
.validator((input: SetRoleAbilitiesInput) => input)
|
|
26
26
|
.handler(async ({ data }): Promise<SetRoleAbilitiesResponse> => {
|
|
27
27
|
const context = await getAdminRequestContext()
|
|
28
28
|
return setRoleAbilitiesCommand(context, data, {
|
|
@@ -14,7 +14,7 @@ import { getAdminRequestContext } from '../../auth/auth-context.js'
|
|
|
14
14
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
15
15
|
|
|
16
16
|
export const whoHasAbility = createServerFn({ method: 'GET' })
|
|
17
|
-
.
|
|
17
|
+
.validator((input: { ability: string }) => input)
|
|
18
18
|
.handler(async ({ data }): Promise<WhoHasAbilityResponse> => {
|
|
19
19
|
const context = await getAdminRequestContext()
|
|
20
20
|
return whoHasAbilityCommand(context, data, {
|
|
@@ -21,7 +21,7 @@ export interface CreateAdminRoleInput {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export const createAdminRole = createServerFn({ method: 'POST' })
|
|
24
|
-
.
|
|
24
|
+
.validator((input: CreateAdminRoleInput) => input)
|
|
25
25
|
.handler(async ({ data }): Promise<AdminRoleResponse> => {
|
|
26
26
|
const context = await getAdminRequestContext()
|
|
27
27
|
return createAdminRoleCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -18,7 +18,7 @@ import { bylineCore } from '../../integrations/byline-core.js'
|
|
|
18
18
|
// modules each declare their own identical `OkResponse` today, and the
|
|
19
19
|
// root `@byline/admin` barrel can only re-export one of them.
|
|
20
20
|
export const deleteAdminRole = createServerFn({ method: 'POST' })
|
|
21
|
-
.
|
|
21
|
+
.validator((input: { id: string; vid: number }) => input)
|
|
22
22
|
.handler(async ({ data }): Promise<{ ok: true }> => {
|
|
23
23
|
const context = await getAdminRequestContext()
|
|
24
24
|
return deleteAdminRoleCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -14,7 +14,7 @@ import { getAdminRequestContext } from '../../auth/auth-context.js'
|
|
|
14
14
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
15
15
|
|
|
16
16
|
export const getAdminRole = createServerFn({ method: 'GET' })
|
|
17
|
-
.
|
|
17
|
+
.validator((input: { id: string }) => input)
|
|
18
18
|
.handler(async ({ data }): Promise<AdminRoleResponse> => {
|
|
19
19
|
const context = await getAdminRequestContext()
|
|
20
20
|
return getAdminRoleCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -14,7 +14,7 @@ import { getAdminRequestContext } from '../../auth/auth-context.js'
|
|
|
14
14
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
15
15
|
|
|
16
16
|
export const reorderAdminRoles = createServerFn({ method: 'POST' })
|
|
17
|
-
.
|
|
17
|
+
.validator((input: { ids: string[] }) => input)
|
|
18
18
|
.handler(async ({ data }): Promise<{ ok: true }> => {
|
|
19
19
|
const context = await getAdminRequestContext()
|
|
20
20
|
return reorderAdminRolesCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -24,7 +24,7 @@ export interface UpdateAdminRoleInput {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export const updateAdminRole = createServerFn({ method: 'POST' })
|
|
27
|
-
.
|
|
27
|
+
.validator((input: UpdateAdminRoleInput) => input)
|
|
28
28
|
.handler(async ({ data }): Promise<AdminRoleResponse> => {
|
|
29
29
|
const context = await getAdminRequestContext()
|
|
30
30
|
return updateAdminRoleCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -25,7 +25,7 @@ export interface CreateAdminUserInput {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export const createAdminUser = createServerFn({ method: 'POST' })
|
|
28
|
-
.
|
|
28
|
+
.validator((input: CreateAdminUserInput) => input)
|
|
29
29
|
.handler(async ({ data }): Promise<AdminUserResponse> => {
|
|
30
30
|
const context = await getAdminRequestContext()
|
|
31
31
|
return createAdminUserCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -14,7 +14,7 @@ import { getAdminRequestContext } from '../../auth/auth-context.js'
|
|
|
14
14
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
15
15
|
|
|
16
16
|
export const deleteAdminUser = createServerFn({ method: 'POST' })
|
|
17
|
-
.
|
|
17
|
+
.validator((input: { id: string; vid: number }) => input)
|
|
18
18
|
.handler(async ({ data }): Promise<OkResponse> => {
|
|
19
19
|
const context = await getAdminRequestContext()
|
|
20
20
|
return deleteAdminUserCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -17,7 +17,7 @@ import { bylineCore } from '../../integrations/byline-core.js'
|
|
|
17
17
|
// detail-view enable/disable control they will be wired into. Keep until
|
|
18
18
|
// that control lands; not dead code.
|
|
19
19
|
export const disableAdminUser = createServerFn({ method: 'POST' })
|
|
20
|
-
.
|
|
20
|
+
.validator((input: { id: string }) => input)
|
|
21
21
|
.handler(async ({ data }): Promise<OkResponse> => {
|
|
22
22
|
const context = await getAdminRequestContext()
|
|
23
23
|
return disableAdminUserCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -17,7 +17,7 @@ import { bylineCore } from '../../integrations/byline-core.js'
|
|
|
17
17
|
// detail-view enable/disable control they will be wired into. Keep until
|
|
18
18
|
// that control lands; not dead code.
|
|
19
19
|
export const enableAdminUser = createServerFn({ method: 'POST' })
|
|
20
|
-
.
|
|
20
|
+
.validator((input: { id: string }) => input)
|
|
21
21
|
.handler(async ({ data }): Promise<OkResponse> => {
|
|
22
22
|
const context = await getAdminRequestContext()
|
|
23
23
|
return enableAdminUserCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -14,7 +14,7 @@ import { getAdminRequestContext } from '../../auth/auth-context.js'
|
|
|
14
14
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
15
15
|
|
|
16
16
|
export const getUserRoles = createServerFn({ method: 'GET' })
|
|
17
|
-
.
|
|
17
|
+
.validator((input: { userId: string }) => input)
|
|
18
18
|
.handler(async ({ data }): Promise<UserRolesResponse> => {
|
|
19
19
|
const context = await getAdminRequestContext()
|
|
20
20
|
return getRolesForUserCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -14,7 +14,7 @@ import { getAdminRequestContext } from '../../auth/auth-context.js'
|
|
|
14
14
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
15
15
|
|
|
16
16
|
export const getAdminUser = createServerFn({ method: 'GET' })
|
|
17
|
-
.
|
|
17
|
+
.validator((input: { id: string }) => input)
|
|
18
18
|
.handler(async ({ data }): Promise<AdminUserResponse> => {
|
|
19
19
|
const context = await getAdminRequestContext()
|
|
20
20
|
return getAdminUserCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -33,7 +33,7 @@ export interface ListAdminUsersInput {
|
|
|
33
33
|
* TanStack Start transport layer for the client to branch on.
|
|
34
34
|
*/
|
|
35
35
|
export const listAdminUsers = createServerFn({ method: 'GET' })
|
|
36
|
-
.
|
|
36
|
+
.validator((input: ListAdminUsersInput) => input ?? {})
|
|
37
37
|
.handler(async ({ data }): Promise<AdminUserListResponse> => {
|
|
38
38
|
const context = await getAdminRequestContext()
|
|
39
39
|
return listAdminUsersCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -20,7 +20,7 @@ export interface SetAdminUserPasswordInput {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export const setAdminUserPassword = createServerFn({ method: 'POST' })
|
|
23
|
-
.
|
|
23
|
+
.validator((input: SetAdminUserPasswordInput) => input)
|
|
24
24
|
.handler(async ({ data }): Promise<AdminUserResponse> => {
|
|
25
25
|
const context = await getAdminRequestContext()
|
|
26
26
|
return setAdminUserPasswordCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -19,7 +19,7 @@ export interface SetUserRolesInput {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export const setUserRoles = createServerFn({ method: 'POST' })
|
|
22
|
-
.
|
|
22
|
+
.validator((input: SetUserRolesInput) => input)
|
|
23
23
|
.handler(async ({ data }): Promise<UserRolesResponse> => {
|
|
24
24
|
const context = await getAdminRequestContext()
|
|
25
25
|
return setRolesForUserCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -27,7 +27,7 @@ export interface UpdateAdminUserInput {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export const updateAdminUser = createServerFn({ method: 'POST' })
|
|
30
|
-
.
|
|
30
|
+
.validator((input: UpdateAdminUserInput) => input)
|
|
31
31
|
.handler(async ({ data }): Promise<AdminUserResponse> => {
|
|
32
32
|
const context = await getAdminRequestContext()
|
|
33
33
|
return updateAdminUserCommand(context, data, { store: bylineCore().adminStore! })
|
|
@@ -38,7 +38,7 @@ type ExecuteAiInstructionInput = {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export const executeAiInstruction = createServerFn({ method: 'POST' })
|
|
41
|
-
.
|
|
41
|
+
.validator((input: ExecuteAiInstructionInput) => input)
|
|
42
42
|
.handler(async ({ data }): Promise<Response> => {
|
|
43
43
|
// Throws ERR_UNAUTHENTICATED if there is no admin session.
|
|
44
44
|
await getAdminRequestContext()
|
|
@@ -36,7 +36,7 @@ export interface SignInResult {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export const adminSignIn = createServerFn({ method: 'POST' })
|
|
39
|
-
.
|
|
39
|
+
.validator((input: SignInInput) => {
|
|
40
40
|
if (typeof input?.email !== 'string' || input.email.length === 0) {
|
|
41
41
|
throw new Error('email is required')
|
|
42
42
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
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
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
10
|
+
|
|
11
|
+
import { ERR_NOT_FOUND, getLogger } from '@byline/core'
|
|
12
|
+
|
|
13
|
+
import { ensureCollection } from '../../integrations/api-utils.js'
|
|
14
|
+
import { getAdminBylineClient } from '../../integrations/byline-client.js'
|
|
15
|
+
import { type ActorLabelMap, resolveActorLabels } from './actors.js'
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Shared param types
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
export interface AuditLogSearchParams {
|
|
22
|
+
page?: number
|
|
23
|
+
page_size?: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Audit entry as it crosses the server-fn boundary. `occurredAt` is an ISO
|
|
28
|
+
* string (Date doesn't survive serialization) and `before` / `after` are
|
|
29
|
+
* narrowed from the storage layer's `unknown` jsonb to the concrete shapes the
|
|
30
|
+
* shipped actions actually carry (path/status strings, the available-locales
|
|
31
|
+
* array, or null for the deletion event) — `unknown` is not a serializable
|
|
32
|
+
* type the TanStack server-fn validator accepts.
|
|
33
|
+
*/
|
|
34
|
+
export interface AuditLogEntryDto {
|
|
35
|
+
id: string
|
|
36
|
+
documentId: string | null
|
|
37
|
+
collectionId: string | null
|
|
38
|
+
actorId: string | null
|
|
39
|
+
actorRealm: string
|
|
40
|
+
action: string
|
|
41
|
+
field: string | null
|
|
42
|
+
before: string | string[] | null
|
|
43
|
+
after: string | string[] | null
|
|
44
|
+
occurredAt: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// Get document-grain audit log (docs/AUDIT.md — Workstream 3)
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
export const getCollectionDocumentAuditLog = createServerFn({ method: 'GET' })
|
|
52
|
+
.validator((input: { collection: string; id: string; params?: AuditLogSearchParams }) => input)
|
|
53
|
+
.handler(async ({ data }) => {
|
|
54
|
+
const { collection: path, id, params } = data
|
|
55
|
+
const config = await ensureCollection(path)
|
|
56
|
+
if (!config) {
|
|
57
|
+
throw ERR_NOT_FOUND({
|
|
58
|
+
message: 'Collection not found',
|
|
59
|
+
details: { collectionPath: path },
|
|
60
|
+
}).log(getLogger())
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Routes through CollectionHandle.auditLog so the document's own read gate
|
|
64
|
+
// (`beforeRead` via `findById`) is applied — identical to the history
|
|
65
|
+
// server fn. An actor whose predicate excludes the document gets an empty
|
|
66
|
+
// log rather than leaked change metadata.
|
|
67
|
+
const result = await getAdminBylineClient().collection(path).auditLog(id, {
|
|
68
|
+
page: params?.page,
|
|
69
|
+
pageSize: params?.page_size,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// Acting-user labels for the audit list (docs/AUDIT.md — W3). Resolved
|
|
73
|
+
// here, in the admin realm, from each entry's raw `actorId`; the UI joins
|
|
74
|
+
// by id. System/tooling rows (NULL actorId) and deleted users are absent
|
|
75
|
+
// from the map — the UI renders the corresponding tombstone label.
|
|
76
|
+
const actors: ActorLabelMap = await resolveActorLabels(result.entries.map((e) => e.actorId))
|
|
77
|
+
|
|
78
|
+
// Map to the serializable DTO: ISO-string the timestamp and narrow the
|
|
79
|
+
// jsonb before/after off `unknown` so the value survives the TanStack
|
|
80
|
+
// server-fn boundary.
|
|
81
|
+
const entries: AuditLogEntryDto[] = result.entries.map((e) => ({
|
|
82
|
+
id: e.id,
|
|
83
|
+
documentId: e.documentId,
|
|
84
|
+
collectionId: e.collectionId,
|
|
85
|
+
actorId: e.actorId,
|
|
86
|
+
actorRealm: e.actorRealm,
|
|
87
|
+
action: e.action,
|
|
88
|
+
field: e.field,
|
|
89
|
+
before: e.before as string | string[] | null,
|
|
90
|
+
after: e.after as string | string[] | null,
|
|
91
|
+
occurredAt: e.occurredAt instanceof Date ? e.occurredAt.toISOString() : String(e.occurredAt),
|
|
92
|
+
}))
|
|
93
|
+
|
|
94
|
+
return { entries, meta: result.meta, actors }
|
|
95
|
+
})
|
|
@@ -29,7 +29,7 @@ import { ensureCollection } from '../../integrations/api-utils.js'
|
|
|
29
29
|
* failures propagate to TanStack Start's transport layer.
|
|
30
30
|
*/
|
|
31
31
|
export const copyDocumentToLocale = createServerFn({ method: 'POST' })
|
|
32
|
-
.
|
|
32
|
+
.validator(
|
|
33
33
|
(input: {
|
|
34
34
|
collection: string
|
|
35
35
|
id: string
|
|
@@ -20,7 +20,7 @@ import { ensureCollection } from '../../integrations/api-utils.js'
|
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
21
|
|
|
22
22
|
export const createCollectionDocument = createServerFn({ method: 'POST' })
|
|
23
|
-
.
|
|
23
|
+
.validator(
|
|
24
24
|
(input: {
|
|
25
25
|
collection: string
|
|
26
26
|
data: any
|
|
@@ -30,7 +30,7 @@ import { ensureCollection } from '../../integrations/api-utils.js'
|
|
|
30
30
|
* transport layer.
|
|
31
31
|
*/
|
|
32
32
|
export const deleteDocumentLocale = createServerFn({ method: 'POST' })
|
|
33
|
-
.
|
|
33
|
+
.validator((input: { collection: string; id: string; locale: string }) => input)
|
|
34
34
|
.handler(async ({ data: input }): Promise<DeleteLocaleResult> => {
|
|
35
35
|
const { collection: path, id, locale } = input
|
|
36
36
|
const logger = getLogger()
|
|
@@ -20,7 +20,7 @@ import { ensureCollection } from '../../integrations/api-utils.js'
|
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
21
|
|
|
22
22
|
export const deleteDocument = createServerFn({ method: 'POST' })
|
|
23
|
-
.
|
|
23
|
+
.validator((input: { collection: string; id: string }) => input)
|
|
24
24
|
.handler(async ({ data: input }) => {
|
|
25
25
|
const { collection: path, id } = input
|
|
26
26
|
const logger = getLogger()
|
|
@@ -31,7 +31,7 @@ import { ensureCollection } from '../../integrations/api-utils.js'
|
|
|
31
31
|
* TanStack Start's transport layer for the client to branch on.
|
|
32
32
|
*/
|
|
33
33
|
export const duplicateCollectionDocument = createServerFn({ method: 'POST' })
|
|
34
|
-
.
|
|
34
|
+
.validator((input: { collection: string; id: string }) => input)
|
|
35
35
|
.handler(async ({ data: input }): Promise<DuplicateDocumentResult> => {
|
|
36
36
|
const { collection: path, id: sourceDocumentId } = input
|
|
37
37
|
const logger = getLogger()
|
|
@@ -30,7 +30,7 @@ import { serialise } from './utils'
|
|
|
30
30
|
// ---------------------------------------------------------------------------
|
|
31
31
|
|
|
32
32
|
const getDocumentFn = createServerFn({ method: 'GET' })
|
|
33
|
-
.
|
|
33
|
+
.validator(
|
|
34
34
|
(input: {
|
|
35
35
|
collection: string
|
|
36
36
|
id: string
|
|
@@ -183,7 +183,7 @@ const getDocumentFn = createServerFn({ method: 'GET' })
|
|
|
183
183
|
// ---------------------------------------------------------------------------
|
|
184
184
|
|
|
185
185
|
const getDocumentByVersionFn = createServerFn({ method: 'GET' })
|
|
186
|
-
.
|
|
186
|
+
.validator((input: { collection: string; versionId: string; locale?: string }) => input)
|
|
187
187
|
.handler(async ({ data }) => {
|
|
188
188
|
const { collection: path, versionId, locale } = data
|
|
189
189
|
const resolvedLocale = locale ?? 'all'
|
|
@@ -32,7 +32,7 @@ export interface HistorySearchParams {
|
|
|
32
32
|
// ---------------------------------------------------------------------------
|
|
33
33
|
|
|
34
34
|
export const getCollectionDocumentHistory = createServerFn({ method: 'GET' })
|
|
35
|
-
.
|
|
35
|
+
.validator((input: { collection: string; id: string; params: HistorySearchParams }) => input)
|
|
36
36
|
.handler(async ({ data }) => {
|
|
37
37
|
const { collection: path, id, params } = data
|
|
38
38
|
const config = await ensureCollection(path)
|
|
@@ -40,7 +40,7 @@ export interface CollectionSearchParams {
|
|
|
40
40
|
// ---------------------------------------------------------------------------
|
|
41
41
|
|
|
42
42
|
export const getCollectionDocuments = createServerFn({ method: 'GET' })
|
|
43
|
-
.
|
|
43
|
+
.validator((input: { collection: string; params: CollectionSearchParams }) => input)
|
|
44
44
|
.handler(async ({ data }) => {
|
|
45
45
|
const { collection: path, params } = data
|
|
46
46
|
const config = await ensureCollection(path)
|
|
@@ -38,7 +38,7 @@ import { ensureCollection } from '../../integrations/api-utils.js'
|
|
|
38
38
|
// ---------------------------------------------------------------------------
|
|
39
39
|
|
|
40
40
|
export const reorderCollectionDocument = createServerFn({ method: 'POST' })
|
|
41
|
-
.
|
|
41
|
+
.validator(
|
|
42
42
|
(input: {
|
|
43
43
|
collection: string
|
|
44
44
|
documentId: string
|
|
@@ -20,7 +20,7 @@ import { ensureCollection } from '../../integrations/api-utils.js'
|
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
21
|
|
|
22
22
|
export const restoreDocumentVersion = createServerFn({ method: 'POST' })
|
|
23
|
-
.
|
|
23
|
+
.validator((input: { collection: string; id: string; versionId: string }) => input)
|
|
24
24
|
.handler(async ({ data: input }) => {
|
|
25
25
|
const { collection: path, id, versionId } = input
|
|
26
26
|
const logger = getLogger()
|
|
@@ -25,7 +25,7 @@ export interface CollectionStatusCount {
|
|
|
25
25
|
// ---------------------------------------------------------------------------
|
|
26
26
|
|
|
27
27
|
const getCollectionStatsFn = createServerFn({ method: 'GET' })
|
|
28
|
-
.
|
|
28
|
+
.validator((input: { collection: string }) => input)
|
|
29
29
|
.handler(async ({ data }) => {
|
|
30
30
|
const config = await ensureCollection(data.collection)
|
|
31
31
|
if (!config) return { stats: [] as CollectionStatusCount[] }
|
|
@@ -23,7 +23,7 @@ import { ensureCollection } from '../../integrations/api-utils.js'
|
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
24
|
|
|
25
25
|
export const updateDocumentStatus = createServerFn({ method: 'POST' })
|
|
26
|
-
.
|
|
26
|
+
.validator((input: { collection: string; id: string; status: string }) => input)
|
|
27
27
|
.handler(async ({ data: input }) => {
|
|
28
28
|
const { collection: path, id, status: nextStatus } = input
|
|
29
29
|
const logger = getLogger()
|
|
@@ -64,7 +64,7 @@ export const updateDocumentStatus = createServerFn({ method: 'POST' })
|
|
|
64
64
|
// ---------------------------------------------------------------------------
|
|
65
65
|
|
|
66
66
|
export const unpublishDocument = createServerFn({ method: 'POST' })
|
|
67
|
-
.
|
|
67
|
+
.validator((input: { collection: string; id: string }) => input)
|
|
68
68
|
.handler(async ({ data: input }) => {
|
|
69
69
|
const { collection: path, id } = input
|
|
70
70
|
const logger = getLogger()
|
|
@@ -26,7 +26,7 @@ import { ensureCollection } from '../../integrations/api-utils.js'
|
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
27
27
|
|
|
28
28
|
export const updateCollectionDocumentWithPatches = createServerFn({ method: 'POST' })
|
|
29
|
-
.
|
|
29
|
+
.validator(
|
|
30
30
|
(input: {
|
|
31
31
|
collection: string
|
|
32
32
|
id: string
|
|
@@ -78,7 +78,7 @@ export const updateCollectionDocumentWithPatches = createServerFn({ method: 'POS
|
|
|
78
78
|
// ---------------------------------------------------------------------------
|
|
79
79
|
|
|
80
80
|
export const updateCollectionDocumentSystemFields = createServerFn({ method: 'POST' })
|
|
81
|
-
.
|
|
81
|
+
.validator(
|
|
82
82
|
(input: {
|
|
83
83
|
collection: string
|
|
84
84
|
id: string
|
|
@@ -147,7 +147,7 @@ function resolveUploadFieldName(
|
|
|
147
147
|
* without depending on TanStack Start server-function transport details.
|
|
148
148
|
*/
|
|
149
149
|
export const uploadCollectionField = createServerFn({ method: 'POST' })
|
|
150
|
-
.
|
|
150
|
+
.validator(parseUploadFormData)
|
|
151
151
|
.handler(async ({ data }) => {
|
|
152
152
|
const { collectionPath, shouldCreateDocument, fieldName, file, fields } = data
|
|
153
153
|
const logger = getLogger()
|
|
@@ -54,7 +54,7 @@ export interface SetInterfaceLocaleResult {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
export const setInterfaceLocaleFn = createServerFn({ method: 'POST' })
|
|
57
|
-
.
|
|
57
|
+
.validator((input: SetInterfaceLocaleInput) => input)
|
|
58
58
|
.handler(async ({ data }): Promise<SetInterfaceLocaleResult> => {
|
|
59
59
|
const core = bylineCore()
|
|
60
60
|
const locales = core.config.i18n.interface.locales
|