@byline/host-tanstack-start 4.19.0 → 5.1.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/dist/admin-shell/chrome/admin-app-bar.d.ts +7 -0
- package/dist/admin-shell/chrome/admin-app-bar.js +10 -2
- package/dist/admin-shell/chrome/session-change-boundary.d.ts +14 -0
- package/dist/admin-shell/chrome/session-change-boundary.js +87 -0
- package/dist/admin-shell/chrome/session-change-boundary.test.d.ts +8 -0
- package/dist/admin-shell/chrome/sign-in-page.d.ts +2 -1
- package/dist/admin-shell/chrome/sign-in-page.js +60 -3
- package/dist/admin-shell/collections/edit.d.ts +1 -1
- package/dist/admin-shell/collections/edit.js +151 -31
- package/dist/admin-shell/collections/history.js +4 -2
- package/dist/admin-shell/collections/list.js +30 -6
- package/dist/admin-shell/collections/restore-version-modal.d.ts +3 -1
- package/dist/admin-shell/collections/restore-version-modal.js +9 -4
- package/dist/admin-shell/collections/tree-list.js +16 -6
- package/dist/admin-shell/collections/version-history.d.ts +2 -1
- package/dist/admin-shell/collections/version-history.js +11 -2
- package/dist/admin-shell/document-mutation-notice.d.ts +6 -0
- package/dist/admin-shell/document-mutation-notice.js +53 -0
- package/dist/admin-shell/document-mutation-state.d.ts +22 -0
- package/dist/admin-shell/document-mutation-state.js +71 -0
- package/dist/admin-shell/document-mutation-state.test.d.ts +1 -0
- package/dist/admin-shell/scheduled-publications/list.js +15 -5
- package/dist/admin-shell/singletons/history.js +4 -2
- package/dist/admin-shell/singletons/view.d.ts +1 -1
- package/dist/admin-shell/singletons/view.js +98 -19
- package/dist/integrations/byline-admin-services.js +7 -1
- package/dist/integrations/byline-field-services.js +2 -9
- package/dist/integrations/client-ip.d.ts +19 -0
- package/dist/integrations/client-ip.js +34 -0
- package/dist/integrations/session-coordination.d.ts +24 -0
- package/dist/integrations/session-coordination.js +85 -0
- package/dist/integrations/session-middleware.d.ts +11 -0
- package/dist/integrations/session-middleware.js +94 -0
- package/dist/integrations/session-request-kind.d.ts +9 -0
- package/dist/integrations/session-request-kind.js +10 -0
- package/dist/integrations/session-request-middleware.d.ts +9 -0
- package/dist/integrations/session-request-middleware.js +7 -0
- package/dist/integrations/sign-in-body.d.ts +12 -0
- package/dist/integrations/sign-in-body.js +52 -0
- package/dist/integrations/sign-in-middleware.d.ts +9 -0
- package/dist/integrations/sign-in-middleware.js +10 -0
- package/dist/routes/create-admin-layout-route.js +46 -31
- package/dist/routes/create-collection-edit-route.js +1 -1
- package/dist/routes/create-collection-list-route.d.ts +0 -7
- package/dist/routes/create-collection-list-route.js +7 -9
- package/dist/routes/create-sign-in-route.js +4 -2
- package/dist/routes/create-singleton-route.d.ts +2 -0
- package/dist/routes/create-singleton-route.js +4 -3
- package/dist/server-fns/admin-account/change-password.d.ts +3 -1
- package/dist/server-fns/admin-account/change-password.js +9 -3
- package/dist/server-fns/admin-account/get.d.ts +3 -1
- package/dist/server-fns/admin-account/get.js +4 -1
- package/dist/server-fns/admin-account/index.d.ts +5 -5
- package/dist/server-fns/admin-account/update.d.ts +3 -1
- package/dist/server-fns/admin-account/update.js +4 -1
- package/dist/server-fns/admin-activity/get.d.ts +3 -1
- package/dist/server-fns/admin-activity/get.js +4 -1
- package/dist/server-fns/admin-permissions/get-role-abilities.d.ts +3 -1
- package/dist/server-fns/admin-permissions/get-role-abilities.js +4 -1
- package/dist/server-fns/admin-permissions/index.d.ts +4 -4
- package/dist/server-fns/admin-permissions/list-registered.d.ts +3 -1
- package/dist/server-fns/admin-permissions/list-registered.js +4 -1
- package/dist/server-fns/admin-permissions/set-role-abilities.d.ts +3 -1
- package/dist/server-fns/admin-permissions/set-role-abilities.js +4 -1
- package/dist/server-fns/admin-permissions/who-has.d.ts +3 -1
- package/dist/server-fns/admin-permissions/who-has.js +4 -1
- package/dist/server-fns/admin-roles/create.d.ts +3 -1
- package/dist/server-fns/admin-roles/create.js +4 -1
- package/dist/server-fns/admin-roles/delete.d.ts +3 -1
- package/dist/server-fns/admin-roles/delete.js +4 -1
- package/dist/server-fns/admin-roles/get.d.ts +3 -1
- package/dist/server-fns/admin-roles/get.js +4 -1
- package/dist/server-fns/admin-roles/index.d.ts +6 -6
- package/dist/server-fns/admin-roles/list.d.ts +3 -1
- package/dist/server-fns/admin-roles/list.js +4 -1
- package/dist/server-fns/admin-roles/reorder.d.ts +3 -1
- package/dist/server-fns/admin-roles/reorder.js +4 -1
- package/dist/server-fns/admin-roles/update.d.ts +3 -1
- package/dist/server-fns/admin-roles/update.js +4 -1
- package/dist/server-fns/admin-users/create.d.ts +3 -1
- package/dist/server-fns/admin-users/create.js +4 -1
- package/dist/server-fns/admin-users/delete.d.ts +3 -1
- package/dist/server-fns/admin-users/delete.js +4 -1
- package/dist/server-fns/admin-users/disable.d.ts +3 -1
- package/dist/server-fns/admin-users/disable.js +4 -1
- package/dist/server-fns/admin-users/enable.d.ts +3 -1
- package/dist/server-fns/admin-users/enable.js +4 -1
- package/dist/server-fns/admin-users/get-user-roles.d.ts +3 -1
- package/dist/server-fns/admin-users/get-user-roles.js +4 -1
- package/dist/server-fns/admin-users/get.d.ts +3 -1
- package/dist/server-fns/admin-users/get.js +4 -1
- package/dist/server-fns/admin-users/index.d.ts +10 -10
- package/dist/server-fns/admin-users/list.d.ts +3 -1
- package/dist/server-fns/admin-users/list.js +4 -1
- package/dist/server-fns/admin-users/set-password.d.ts +3 -1
- package/dist/server-fns/admin-users/set-password.js +4 -1
- package/dist/server-fns/admin-users/set-user-roles.d.ts +3 -1
- package/dist/server-fns/admin-users/set-user-roles.js +4 -1
- package/dist/server-fns/admin-users/update.d.ts +3 -1
- package/dist/server-fns/admin-users/update.js +4 -1
- package/dist/server-fns/ai/execute.d.ts +3 -1
- package/dist/server-fns/ai/execute.js +4 -1
- package/dist/server-fns/ai/index.d.ts +1 -1
- package/dist/server-fns/analytics/maintenance.d.ts +6 -2
- package/dist/server-fns/analytics/maintenance.js +7 -2
- package/dist/server-fns/analytics/queries.d.ts +18 -6
- package/dist/server-fns/analytics/queries.js +19 -6
- package/dist/server-fns/auth/current-user.d.ts +1 -0
- package/dist/server-fns/auth/current-user.js +5 -2
- package/dist/server-fns/auth/renew.d.ts +13 -0
- package/dist/server-fns/auth/renew.js +45 -0
- package/dist/server-fns/auth/sign-in.d.ts +2 -1
- package/dist/server-fns/auth/sign-in.js +88 -17
- package/dist/server-fns/auth/sign-out.d.ts +3 -1
- package/dist/server-fns/auth/sign-out.js +38 -6
- package/dist/server-fns/collections/audit.d.ts +3 -1
- package/dist/server-fns/collections/audit.js +4 -1
- package/dist/server-fns/collections/copy-to-locale.d.ts +5 -1
- package/dist/server-fns/collections/copy-to-locale.js +7 -2
- package/dist/server-fns/collections/create.d.ts +4 -1
- package/dist/server-fns/collections/create.js +8 -3
- package/dist/server-fns/collections/delete-locale.d.ts +5 -1
- package/dist/server-fns/collections/delete-locale.js +7 -2
- package/dist/server-fns/collections/delete-outcome.d.ts +7 -3
- package/dist/server-fns/collections/delete-outcome.js +4 -0
- package/dist/server-fns/collections/delete.d.ts +5 -1
- package/dist/server-fns/collections/delete.js +8 -3
- package/dist/server-fns/collections/duplicate.d.ts +5 -1
- package/dist/server-fns/collections/duplicate.js +8 -3
- package/dist/server-fns/collections/get.d.ts +1 -0
- package/dist/server-fns/collections/get.js +12 -6
- package/dist/server-fns/collections/history.d.ts +3 -1
- package/dist/server-fns/collections/history.js +4 -1
- package/dist/server-fns/collections/index.d.ts +21 -21
- package/dist/server-fns/collections/list.d.ts +3 -1
- package/dist/server-fns/collections/list.js +15 -4
- package/dist/server-fns/collections/reindex.d.ts +3 -1
- package/dist/server-fns/collections/reindex.js +4 -1
- package/dist/server-fns/collections/reorder.d.ts +10 -2
- package/dist/server-fns/collections/reorder.js +23 -76
- package/dist/server-fns/collections/restore-version.d.ts +6 -1
- package/dist/server-fns/collections/restore-version.js +8 -2
- package/dist/server-fns/collections/save-outcome.d.ts +1 -0
- package/dist/server-fns/collections/save-outcome.js +1 -0
- package/dist/server-fns/collections/scheduled-publication.d.ts +64 -7
- package/dist/server-fns/collections/scheduled-publication.js +42 -21
- package/dist/server-fns/collections/set-list-view-preference.d.ts +3 -1
- package/dist/server-fns/collections/set-list-view-preference.js +4 -1
- package/dist/server-fns/collections/stats.js +4 -1
- package/dist/server-fns/collections/status.d.ts +14 -2
- package/dist/server-fns/collections/status.js +18 -5
- package/dist/server-fns/collections/tree-mutation.d.ts +7 -6
- package/dist/server-fns/collections/tree-mutation.js +17 -5
- package/dist/server-fns/collections/tree.d.ts +29 -6
- package/dist/server-fns/collections/tree.js +32 -17
- package/dist/server-fns/collections/update.d.ts +18 -6
- package/dist/server-fns/collections/update.js +39 -20
- package/dist/server-fns/collections/upload.d.ts +11 -1
- package/dist/server-fns/collections/upload.js +6 -2
- package/dist/server-fns/document-mutation-errors.d.ts +5 -0
- package/dist/server-fns/document-mutation-errors.js +50 -0
- package/dist/server-fns/editable-revision.d.ts +9 -0
- package/dist/server-fns/editable-revision.js +11 -0
- package/dist/server-fns/i18n/set-locale.d.ts +3 -1
- package/dist/server-fns/i18n/set-locale.js +18 -3
- package/dist/server-fns/preview/disable.d.ts +3 -1
- package/dist/server-fns/preview/disable.js +4 -1
- package/dist/server-fns/preview/enable.d.ts +3 -1
- package/dist/server-fns/preview/enable.js +4 -1
- package/dist/server-fns/singleton-document-read.d.ts +1 -1
- package/dist/server-fns/singleton-document-read.js +7 -3
- package/dist/server-fns/singletons/cancel-scheduled-publish.d.ts +9 -2
- package/dist/server-fns/singletons/cancel-scheduled-publish.js +7 -1
- package/dist/server-fns/singletons/change-status.d.ts +5 -1
- package/dist/server-fns/singletons/change-status.js +7 -1
- package/dist/server-fns/singletons/confirm-scheduled-publish.d.ts +8 -2
- package/dist/server-fns/singletons/confirm-scheduled-publish.js +7 -2
- package/dist/server-fns/singletons/copy-to-locale.d.ts +5 -1
- package/dist/server-fns/singletons/copy-to-locale.js +7 -2
- package/dist/server-fns/singletons/find-by-version.d.ts +3 -1
- package/dist/server-fns/singletons/find-by-version.js +4 -1
- package/dist/server-fns/singletons/get-scheduled-publish.d.ts +3 -1
- package/dist/server-fns/singletons/get-scheduled-publish.js +4 -1
- package/dist/server-fns/singletons/get.d.ts +6 -4
- package/dist/server-fns/singletons/get.js +4 -1
- package/dist/server-fns/singletons/history.d.ts +3 -1
- package/dist/server-fns/singletons/history.js +4 -1
- package/dist/server-fns/singletons/restore-version.d.ts +5 -1
- package/dist/server-fns/singletons/restore-version.js +9 -3
- package/dist/server-fns/singletons/schedule-publish.d.ts +8 -2
- package/dist/server-fns/singletons/schedule-publish.js +7 -2
- package/dist/server-fns/singletons/unpublish.d.ts +5 -1
- package/dist/server-fns/singletons/unpublish.js +7 -1
- package/dist/server-fns/singletons/update.d.ts +6 -4
- package/dist/server-fns/singletons/update.js +8 -4
- package/dist/server-fns/structural-receipt.d.ts +4 -0
- package/dist/server-fns/structural-receipt.js +31 -0
- package/package.json +20 -20
- package/src/admin-shell/chrome/admin-app-bar.tsx +11 -6
- package/src/admin-shell/chrome/session-change-boundary.test.tsx +78 -0
- package/src/admin-shell/chrome/session-change-boundary.tsx +94 -0
- package/src/admin-shell/chrome/sign-in-page.tsx +68 -3
- package/src/admin-shell/collections/edit.tsx +173 -54
- package/src/admin-shell/collections/history.tsx +11 -2
- package/src/admin-shell/collections/list.tsx +22 -6
- package/src/admin-shell/collections/restore-version-modal.tsx +12 -3
- package/src/admin-shell/collections/save-navigation.test.tsx +156 -8
- package/src/admin-shell/collections/tree-list.tsx +16 -6
- package/src/admin-shell/collections/version-history.tsx +13 -3
- package/src/admin-shell/document-mutation-notice.tsx +45 -0
- package/src/admin-shell/document-mutation-state.test.tsx +146 -0
- package/src/admin-shell/document-mutation-state.ts +95 -0
- package/src/admin-shell/scheduled-publications/list.tsx +16 -6
- package/src/admin-shell/singletons/history.test.tsx +3 -1
- package/src/admin-shell/singletons/history.tsx +10 -2
- package/src/admin-shell/singletons/view.test.tsx +59 -18
- package/src/admin-shell/singletons/view.tsx +110 -29
- package/src/integrations/byline-admin-services.ts +7 -1
- package/src/integrations/byline-field-services.ts +2 -3
- package/src/integrations/client-ip.test.node.ts +45 -0
- package/src/integrations/client-ip.ts +54 -0
- package/src/integrations/framework-fetch-shape.test.node.ts +56 -0
- package/src/integrations/session-client.test.node.ts +121 -0
- package/src/integrations/session-coordination.test.node.ts +77 -0
- package/src/integrations/session-coordination.ts +96 -0
- package/src/integrations/session-middleware.test.node.ts +87 -0
- package/src/integrations/session-middleware.ts +107 -0
- package/src/integrations/session-request-kind.ts +18 -0
- package/src/integrations/session-request-middleware.ts +17 -0
- package/src/integrations/sign-in-body.test.node.ts +59 -0
- package/src/integrations/sign-in-body.ts +50 -0
- package/src/integrations/sign-in-middleware.ts +24 -0
- package/src/routes/create-admin-layout-route.tsx +48 -30
- package/src/routes/create-collection-edit-route.tsx +1 -0
- package/src/routes/create-collection-list-route.tsx +13 -2
- package/src/routes/create-sign-in-route.tsx +10 -2
- package/src/routes/create-singleton-route.test.node.ts +10 -2
- package/src/routes/create-singleton-route.tsx +6 -4
- package/src/server-fns/admin-account/change-password.test.node.ts +69 -0
- package/src/server-fns/admin-account/change-password.ts +13 -2
- package/src/server-fns/admin-account/get.ts +2 -0
- package/src/server-fns/admin-account/update.ts +2 -0
- package/src/server-fns/admin-activity/get.ts +2 -0
- package/src/server-fns/admin-permissions/get-role-abilities.ts +2 -0
- package/src/server-fns/admin-permissions/list-registered.ts +5 -4
- package/src/server-fns/admin-permissions/set-role-abilities.ts +2 -0
- package/src/server-fns/admin-permissions/who-has.ts +2 -0
- package/src/server-fns/admin-roles/create.ts +2 -0
- package/src/server-fns/admin-roles/delete.ts +2 -0
- package/src/server-fns/admin-roles/get.ts +2 -0
- package/src/server-fns/admin-roles/list.ts +5 -4
- package/src/server-fns/admin-roles/reorder.ts +2 -0
- package/src/server-fns/admin-roles/update.ts +2 -0
- package/src/server-fns/admin-users/create.ts +2 -0
- package/src/server-fns/admin-users/delete.ts +2 -0
- package/src/server-fns/admin-users/disable.ts +2 -0
- package/src/server-fns/admin-users/enable.ts +2 -0
- package/src/server-fns/admin-users/get-user-roles.ts +2 -0
- package/src/server-fns/admin-users/get.ts +2 -0
- package/src/server-fns/admin-users/list.ts +2 -0
- package/src/server-fns/admin-users/set-password.ts +2 -0
- package/src/server-fns/admin-users/set-user-roles.ts +2 -0
- package/src/server-fns/admin-users/update.ts +2 -0
- package/src/server-fns/ai/execute.ts +3 -0
- package/src/server-fns/analytics/maintenance.ts +4 -0
- package/src/server-fns/analytics/queries.ts +14 -8
- package/src/server-fns/auth/current-user.ts +7 -3
- package/src/server-fns/auth/renew.test.node.ts +85 -0
- package/src/server-fns/auth/renew.ts +54 -0
- package/src/server-fns/auth/sign-in.test.node.ts +114 -0
- package/src/server-fns/auth/sign-in.ts +80 -27
- package/src/server-fns/auth/sign-out.test.node.ts +85 -0
- package/src/server-fns/auth/sign-out.ts +44 -19
- package/src/server-fns/collections/audit.ts +2 -0
- package/src/server-fns/collections/copy-to-locale.ts +43 -36
- package/src/server-fns/collections/create.ts +56 -50
- package/src/server-fns/collections/delete-locale.ts +43 -32
- package/src/server-fns/collections/delete-outcome.test.node.ts +16 -0
- package/src/server-fns/collections/delete-outcome.ts +27 -16
- package/src/server-fns/collections/delete.ts +46 -38
- package/src/server-fns/collections/duplicate.ts +40 -32
- package/src/server-fns/collections/get.ts +11 -4
- package/src/server-fns/collections/history.ts +2 -0
- package/src/server-fns/collections/list.ts +10 -3
- package/src/server-fns/collections/reindex.ts +2 -0
- package/src/server-fns/collections/reorder.ts +34 -124
- package/src/server-fns/collections/restore-version.ts +49 -39
- package/src/server-fns/collections/revision-input.test.node.ts +122 -0
- package/src/server-fns/collections/save-outcome.test.node.ts +3 -0
- package/src/server-fns/collections/save-outcome.ts +2 -0
- package/src/server-fns/collections/scheduled-publication.ts +149 -115
- package/src/server-fns/collections/set-list-view-preference.ts +2 -0
- package/src/server-fns/collections/stats.ts +2 -0
- package/src/server-fns/collections/status.ts +89 -64
- package/src/server-fns/collections/tree-mutation.test.node.ts +87 -29
- package/src/server-fns/collections/tree-mutation.ts +28 -11
- package/src/server-fns/collections/tree.ts +156 -122
- package/src/server-fns/collections/update.ts +113 -86
- package/src/server-fns/collections/upload-singleton.test.node.ts +10 -7
- package/src/server-fns/collections/upload.ts +91 -77
- package/src/server-fns/document-mutation-errors.ts +67 -0
- package/src/server-fns/document-mutation-transport.test.node.ts +211 -0
- package/src/server-fns/editable-revision.test.node.ts +23 -0
- package/src/server-fns/editable-revision.ts +18 -0
- package/src/server-fns/i18n/set-locale.ts +19 -3
- package/src/server-fns/preview/disable.ts +8 -4
- package/src/server-fns/preview/enable.ts +9 -5
- package/src/server-fns/singleton-document-read.ts +7 -3
- package/src/server-fns/singletons/cancel-scheduled-publish.ts +12 -3
- package/src/server-fns/singletons/change-status.ts +12 -3
- package/src/server-fns/singletons/confirm-scheduled-publish.ts +14 -6
- package/src/server-fns/singletons/copy-to-locale.ts +22 -15
- package/src/server-fns/singletons/find-by-version.ts +2 -0
- package/src/server-fns/singletons/get-scheduled-publish.ts +2 -0
- package/src/server-fns/singletons/get.ts +5 -3
- package/src/server-fns/singletons/history.ts +2 -0
- package/src/server-fns/singletons/restore-version.ts +19 -12
- package/src/server-fns/singletons/schedule-publish.ts +20 -7
- package/src/server-fns/singletons/singletons.test.node.ts +69 -25
- package/src/server-fns/singletons/unpublish.ts +12 -3
- package/src/server-fns/singletons/update.ts +23 -16
- package/src/server-fns/structural-receipt.ts +41 -0
|
@@ -18,13 +18,21 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { createServerFn } from '@tanstack/react-start'
|
|
21
|
-
import { getRequestHeader } from '@tanstack/react-start/server'
|
|
22
|
-
|
|
23
|
-
import {
|
|
21
|
+
import { getRequest, getRequestHeader } from '@tanstack/react-start/server'
|
|
22
|
+
|
|
23
|
+
import type { SignInAdmission } from '@byline/auth'
|
|
24
|
+
import {
|
|
25
|
+
readAccessTokenCookie,
|
|
26
|
+
readRefreshTokenCookie,
|
|
27
|
+
setSessionCookies,
|
|
28
|
+
} from '@byline/client/server'
|
|
24
29
|
import { getServerConfig } from '@byline/core'
|
|
30
|
+
import { passwordSignInSchema } from '@byline/core/validation'
|
|
25
31
|
|
|
26
32
|
import { readAdminLocaleCookie } from '../../i18n/locale-cookie.js'
|
|
27
33
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
34
|
+
import { normalizeClientIp } from '../../integrations/client-ip.js'
|
|
35
|
+
import { wasSignInBodyChecked } from '../../integrations/sign-in-body.js'
|
|
28
36
|
|
|
29
37
|
export interface SignInInput {
|
|
30
38
|
email: string
|
|
@@ -33,17 +41,14 @@ export interface SignInInput {
|
|
|
33
41
|
|
|
34
42
|
export interface SignInResult {
|
|
35
43
|
userId: string
|
|
44
|
+
sessionId: string
|
|
36
45
|
}
|
|
37
46
|
|
|
38
47
|
export const adminSignIn = createServerFn({ method: 'POST' })
|
|
39
|
-
.validator((input:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (typeof input?.password !== 'string' || input.password.length === 0) {
|
|
44
|
-
throw new Error('password is required')
|
|
45
|
-
}
|
|
46
|
-
return { email: input.email, password: input.password }
|
|
48
|
+
.validator((input: unknown) => {
|
|
49
|
+
const parsed = passwordSignInSchema.safeParse(input)
|
|
50
|
+
if (!parsed.success) throw new Response('Invalid sign-in input', { status: 400 })
|
|
51
|
+
return parsed.data
|
|
47
52
|
})
|
|
48
53
|
.handler(async ({ data }): Promise<SignInResult> => {
|
|
49
54
|
const provider = getServerConfig().sessionProvider
|
|
@@ -51,21 +56,69 @@ export const adminSignIn = createServerFn({ method: 'POST' })
|
|
|
51
56
|
throw new Error('no sessionProvider configured')
|
|
52
57
|
}
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
59
|
+
const request = getRequest()
|
|
60
|
+
const protection = getServerConfig().passwordSignIn
|
|
61
|
+
if (!wasSignInBodyChecked(request)) {
|
|
62
|
+
throw new Response('Password sign-in request middleware is not configured', { status: 503 })
|
|
63
|
+
}
|
|
64
|
+
if (!protection) {
|
|
65
|
+
throw new Response('Password sign-in protection is not configured', { status: 503 })
|
|
66
|
+
}
|
|
67
|
+
const userAgent = getRequestHeader('user-agent')?.slice(0, 512)
|
|
68
|
+
let ip: string | null
|
|
69
|
+
try {
|
|
70
|
+
ip = normalizeClientIp(await protection.resolveClientIp(request))
|
|
71
|
+
} catch {
|
|
72
|
+
throw new Response('Client address unavailable', { status: 503 })
|
|
73
|
+
}
|
|
74
|
+
if (!ip) throw new Response('Client address unavailable', { status: 503 })
|
|
75
|
+
const tooMany = (seconds: number) =>
|
|
76
|
+
new Response('Too many sign-in attempts', {
|
|
77
|
+
status: 429,
|
|
78
|
+
headers: { 'Retry-After': String(seconds), 'Cache-Control': 'no-store' },
|
|
79
|
+
})
|
|
80
|
+
let release: (() => void) | null
|
|
81
|
+
try {
|
|
82
|
+
release = await protection.limiter.acquire()
|
|
83
|
+
} catch {
|
|
84
|
+
throw new Response('Sign-in temporarily unavailable', { status: 503 })
|
|
85
|
+
}
|
|
86
|
+
if (!release) throw tooMany(1)
|
|
87
|
+
const recordResult = (outcome: 'success' | 'failure') => {
|
|
88
|
+
try {
|
|
89
|
+
protection.limiter.recordResult?.({ email: data.email, ip }, outcome)
|
|
90
|
+
} catch {
|
|
91
|
+
/* Telemetry cannot change authentication outcomes. */
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const result = await (async () => {
|
|
95
|
+
try {
|
|
96
|
+
let admission: SignInAdmission
|
|
97
|
+
try {
|
|
98
|
+
admission = await protection.limiter.consume({ email: data.email, ip })
|
|
99
|
+
} catch {
|
|
100
|
+
throw new Response('Sign-in temporarily unavailable', { status: 503 })
|
|
101
|
+
}
|
|
102
|
+
if (!admission.allowed) throw tooMany(admission.retryAfterSeconds)
|
|
103
|
+
try {
|
|
104
|
+
const result = await provider.signInWithPassword({
|
|
105
|
+
previousAccessToken: readAccessTokenCookie(),
|
|
106
|
+
previousRefreshToken: readRefreshTokenCookie(),
|
|
107
|
+
email: data.email,
|
|
108
|
+
password: data.password,
|
|
109
|
+
userAgent,
|
|
110
|
+
ip,
|
|
111
|
+
})
|
|
112
|
+
recordResult('success')
|
|
113
|
+
return result
|
|
114
|
+
} catch (error) {
|
|
115
|
+
recordResult('failure')
|
|
116
|
+
throw error
|
|
117
|
+
}
|
|
118
|
+
} finally {
|
|
119
|
+
release()
|
|
120
|
+
}
|
|
121
|
+
})()
|
|
69
122
|
|
|
70
123
|
setSessionCookies(result)
|
|
71
124
|
|
|
@@ -82,7 +135,7 @@ export const adminSignIn = createServerFn({ method: 'POST' })
|
|
|
82
135
|
// Swallow — locale sync is not load-bearing for the sign-in flow.
|
|
83
136
|
}
|
|
84
137
|
|
|
85
|
-
return { userId: result.actor.id }
|
|
138
|
+
return { userId: result.actor.id, sessionId: result.sessionId }
|
|
86
139
|
})
|
|
87
140
|
|
|
88
141
|
/**
|
|
@@ -0,0 +1,85 @@
|
|
|
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 { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
10
|
+
|
|
11
|
+
const mocks = vi.hoisted(() => ({
|
|
12
|
+
access: 'access' as string | undefined,
|
|
13
|
+
refresh: 'refresh' as string | undefined,
|
|
14
|
+
revoke: vi.fn(),
|
|
15
|
+
clear: vi.fn(),
|
|
16
|
+
clearPreview: vi.fn(),
|
|
17
|
+
}))
|
|
18
|
+
vi.mock('@tanstack/react-start', () => ({
|
|
19
|
+
createMiddleware: () => ({ client: () => ({}) }),
|
|
20
|
+
createServerFn: () => ({ middleware: () => ({ handler: (handler: any) => handler }) }),
|
|
21
|
+
}))
|
|
22
|
+
vi.mock('@byline/core', () => ({
|
|
23
|
+
getServerConfig: () => ({ sessionProvider: { revokeSession: mocks.revoke } }),
|
|
24
|
+
}))
|
|
25
|
+
vi.mock('@byline/client/server', () => ({
|
|
26
|
+
readAccessTokenCookie: () => mocks.access,
|
|
27
|
+
readRefreshTokenCookie: () => mocks.refresh,
|
|
28
|
+
clearSessionCookies: mocks.clear,
|
|
29
|
+
clearPreviewCookie: mocks.clearPreview,
|
|
30
|
+
}))
|
|
31
|
+
|
|
32
|
+
import { adminSignOut } from './sign-out.js'
|
|
33
|
+
|
|
34
|
+
const call = adminSignOut as unknown as (args: {
|
|
35
|
+
context: { expectedSessionId?: string }
|
|
36
|
+
}) => Promise<unknown>
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
vi.resetAllMocks()
|
|
39
|
+
mocks.access = 'access'
|
|
40
|
+
mocks.refresh = 'refresh'
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
describe('confirmed logout', () => {
|
|
44
|
+
it('revokes with both observed credentials before clearing cookies', async () => {
|
|
45
|
+
mocks.revoke.mockImplementation(async () => {
|
|
46
|
+
expect(mocks.clear).not.toHaveBeenCalled()
|
|
47
|
+
})
|
|
48
|
+
await expect(call({ context: { expectedSessionId: 'A' } })).resolves.toEqual({ status: 'ok' })
|
|
49
|
+
expect(mocks.revoke).toHaveBeenCalledWith({
|
|
50
|
+
accessToken: 'access',
|
|
51
|
+
refreshToken: 'refresh',
|
|
52
|
+
expectedSessionId: 'A',
|
|
53
|
+
})
|
|
54
|
+
expect(mocks.clear).toHaveBeenCalledOnce()
|
|
55
|
+
})
|
|
56
|
+
it('still requests revocation when only the access cookie remains', async () => {
|
|
57
|
+
mocks.refresh = undefined
|
|
58
|
+
await call({ context: { expectedSessionId: 'A' } })
|
|
59
|
+
expect(mocks.revoke).toHaveBeenCalledWith({
|
|
60
|
+
accessToken: 'access',
|
|
61
|
+
refreshToken: undefined,
|
|
62
|
+
expectedSessionId: 'A',
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
it('does not clear credentials or report success when revocation fails', async () => {
|
|
66
|
+
mocks.revoke.mockRejectedValue(new Error('database unavailable'))
|
|
67
|
+
await expect(call({ context: { expectedSessionId: 'A' } })).rejects.toThrow(
|
|
68
|
+
'database unavailable'
|
|
69
|
+
)
|
|
70
|
+
expect(mocks.clear).not.toHaveBeenCalled()
|
|
71
|
+
expect(mocks.clearPreview).not.toHaveBeenCalled()
|
|
72
|
+
})
|
|
73
|
+
it('requires the page identity before revoking any observed credential', async () => {
|
|
74
|
+
await expect(call({ context: {} })).rejects.toMatchObject({ code: 'ERR_SESSION_CHANGED' })
|
|
75
|
+
expect(mocks.revoke).not.toHaveBeenCalled()
|
|
76
|
+
expect(mocks.clear).not.toHaveBeenCalled()
|
|
77
|
+
})
|
|
78
|
+
it('is idempotent when neither credential is present', async () => {
|
|
79
|
+
mocks.access = undefined
|
|
80
|
+
mocks.refresh = undefined
|
|
81
|
+
await expect(call({ context: {} })).resolves.toEqual({ status: 'ok' })
|
|
82
|
+
expect(mocks.revoke).not.toHaveBeenCalled()
|
|
83
|
+
expect(mocks.clear).toHaveBeenCalledOnce()
|
|
84
|
+
})
|
|
85
|
+
})
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
/**
|
|
10
10
|
* Admin sign-out server function.
|
|
11
11
|
*
|
|
12
|
-
* Revokes the
|
|
12
|
+
* Revokes the login identified by either credential (so a stolen copy cannot be reused),
|
|
13
13
|
* clears both session cookies, and clears the preview-mode cookie.
|
|
14
|
-
* Idempotent — if the caller already lacks
|
|
14
|
+
* Idempotent — if the caller already lacks both credential cookies, we still
|
|
15
15
|
* clear whatever's there and return successfully.
|
|
16
16
|
*
|
|
17
17
|
* Note: clearing `byline_preview` here is hygiene, not a security
|
|
@@ -21,32 +21,57 @@
|
|
|
21
21
|
* Clearing it simply means the next sign-in starts in non-preview mode.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { createServerFn } from '@tanstack/react-start'
|
|
24
|
+
import { createMiddleware, createServerFn } from '@tanstack/react-start'
|
|
25
25
|
|
|
26
|
+
import { ERR_SESSION_CHANGED } from '@byline/auth'
|
|
26
27
|
import {
|
|
27
28
|
clearPreviewCookie,
|
|
28
29
|
clearSessionCookies,
|
|
30
|
+
readAccessTokenCookie,
|
|
29
31
|
readRefreshTokenCookie,
|
|
30
32
|
} from '@byline/client/server'
|
|
31
33
|
import { getServerConfig } from '@byline/core'
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
import {
|
|
36
|
+
coordinateAuthAction,
|
|
37
|
+
flagSessionChanged,
|
|
38
|
+
notifySessionAction,
|
|
39
|
+
sessionSnapshot,
|
|
40
|
+
} from '../../integrations/session-coordination.js'
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// certainly be transport-level. Swallow and continue: the cookies
|
|
41
|
-
// get cleared regardless, so the caller's session ends.
|
|
42
|
+
const signOutBoundary = createMiddleware({ type: 'function' }).client(async ({ next }) => {
|
|
43
|
+
const snapshot = sessionSnapshot()
|
|
44
|
+
return coordinateAuthAction(async () => {
|
|
42
45
|
try {
|
|
43
|
-
await
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
const result = await next({ sendContext: { expectedSessionId: snapshot.sessionId } })
|
|
47
|
+
notifySessionAction()
|
|
48
|
+
return result
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if ((error as { code?: string })?.code === 'ERR_SESSION_CHANGED') flagSessionChanged()
|
|
51
|
+
throw error
|
|
46
52
|
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
clearSessionCookies()
|
|
50
|
-
clearPreviewCookie()
|
|
51
|
-
return { status: 'ok' as const }
|
|
53
|
+
})
|
|
52
54
|
})
|
|
55
|
+
|
|
56
|
+
export const adminSignOut = createServerFn({ method: 'POST' })
|
|
57
|
+
.middleware([signOutBoundary])
|
|
58
|
+
.handler(async ({ context }) => {
|
|
59
|
+
const provider = getServerConfig().sessionProvider
|
|
60
|
+
const refreshToken = readRefreshTokenCookie()
|
|
61
|
+
const accessToken = readAccessTokenCookie()
|
|
62
|
+
|
|
63
|
+
if (refreshToken || accessToken) {
|
|
64
|
+
if (!provider) throw new Error('no sessionProvider configured')
|
|
65
|
+
if (!context.expectedSessionId)
|
|
66
|
+
throw ERR_SESSION_CHANGED({ message: 'logout requires the page login identity' })
|
|
67
|
+
await provider.revokeSession({
|
|
68
|
+
refreshToken,
|
|
69
|
+
accessToken,
|
|
70
|
+
expectedSessionId: context.expectedSessionId,
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
clearSessionCookies()
|
|
75
|
+
clearPreviewCookie()
|
|
76
|
+
return { status: 'ok' as const }
|
|
77
|
+
})
|
|
@@ -12,6 +12,7 @@ import { getAdminBylineClient } from '@byline/client/server'
|
|
|
12
12
|
import { ERR_NOT_FOUND, getLogger } from '@byline/core'
|
|
13
13
|
|
|
14
14
|
import { ensureCollection } from '../../integrations/api-utils.js'
|
|
15
|
+
import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
|
|
15
16
|
import { type ActorLabelMap, resolveActorLabels } from './actors.js'
|
|
16
17
|
|
|
17
18
|
// ---------------------------------------------------------------------------
|
|
@@ -49,6 +50,7 @@ export interface AuditLogEntryDto {
|
|
|
49
50
|
// ---------------------------------------------------------------------------
|
|
50
51
|
|
|
51
52
|
export const getCollectionDocumentAuditLog = createServerFn({ method: 'GET' })
|
|
53
|
+
.middleware([adminSessionMiddleware])
|
|
52
54
|
.validator((input: { collection: string; id: string; params?: AuditLogSearchParams }) => input)
|
|
53
55
|
.handler(async ({ data }) => {
|
|
54
56
|
const { collection: path, id, params } = data
|
|
@@ -14,6 +14,8 @@ import type { CopyToLocaleResult, DocumentLifecycleContext } from '@byline/core/
|
|
|
14
14
|
import { copyToLocale } from '@byline/core/services'
|
|
15
15
|
|
|
16
16
|
import { ensureCollection } from '../../integrations/api-utils.js'
|
|
17
|
+
import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
|
|
18
|
+
import { withDocumentMutationErrors } from '../document-mutation-errors.js'
|
|
17
19
|
import {
|
|
18
20
|
type CollectionDocumentCommittedHookFailureResponse,
|
|
19
21
|
toCommittedDocumentHookFailureResponse,
|
|
@@ -33,8 +35,10 @@ import {
|
|
|
33
35
|
* failures propagate to TanStack Start's transport layer.
|
|
34
36
|
*/
|
|
35
37
|
export const copyDocumentToLocale = createServerFn({ method: 'POST' })
|
|
38
|
+
.middleware([adminSessionMiddleware])
|
|
36
39
|
.validator(
|
|
37
40
|
(input: {
|
|
41
|
+
expectedRevision: number
|
|
38
42
|
collection: string
|
|
39
43
|
id: string
|
|
40
44
|
sourceLocale: string
|
|
@@ -43,43 +47,46 @@ export const copyDocumentToLocale = createServerFn({ method: 'POST' })
|
|
|
43
47
|
}) => input
|
|
44
48
|
)
|
|
45
49
|
.handler(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
withDocumentMutationErrors(
|
|
51
|
+
async ({
|
|
52
|
+
data: input,
|
|
53
|
+
}): Promise<CopyToLocaleResult | CollectionDocumentCommittedHookFailureResponse> => {
|
|
54
|
+
const { collection: path, id, sourceLocale, targetLocale, overwrite } = input
|
|
55
|
+
const logger = getLogger()
|
|
56
|
+
const config = await ensureCollection(path)
|
|
57
|
+
if (!config) {
|
|
58
|
+
throw ERR_NOT_FOUND({
|
|
59
|
+
message: 'Collection not found',
|
|
60
|
+
details: { collectionPath: path },
|
|
61
|
+
}).log(logger)
|
|
62
|
+
}
|
|
58
63
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
const serverConfig = getServerConfig()
|
|
65
|
+
const ctx: DocumentLifecycleContext = {
|
|
66
|
+
db: serverConfig.db,
|
|
67
|
+
definition: config.definition,
|
|
68
|
+
collectionId: config.collection.id,
|
|
69
|
+
collectionVersion: config.collection.version,
|
|
70
|
+
collectionPath: path,
|
|
71
|
+
logger,
|
|
72
|
+
defaultLocale: serverConfig.i18n.content.defaultLocale,
|
|
73
|
+
slugifier: serverConfig.slugifier,
|
|
74
|
+
requestContext: await getAdminRequestContext(),
|
|
75
|
+
}
|
|
71
76
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
try {
|
|
78
|
+
return await copyToLocale(ctx, {
|
|
79
|
+
documentId: id,
|
|
80
|
+
expectedRevision: input.expectedRevision,
|
|
81
|
+
sourceLocale,
|
|
82
|
+
targetLocale,
|
|
83
|
+
overwrite,
|
|
84
|
+
})
|
|
85
|
+
} catch (error) {
|
|
86
|
+
const committedFailure = toCommittedDocumentHookFailureResponse(error)
|
|
87
|
+
if (committedFailure != null) return committedFailure
|
|
88
|
+
throw error
|
|
89
|
+
}
|
|
83
90
|
}
|
|
84
|
-
|
|
91
|
+
)
|
|
85
92
|
)
|
|
@@ -14,6 +14,8 @@ import type { CreateDocumentResult, DocumentLifecycleContext } from '@byline/cor
|
|
|
14
14
|
import { createDocument } from '@byline/core/services'
|
|
15
15
|
|
|
16
16
|
import { ensureCollection } from '../../integrations/api-utils.js'
|
|
17
|
+
import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
|
|
18
|
+
import { withDocumentMutationErrors } from '../document-mutation-errors.js'
|
|
17
19
|
import { toCommittedDocumentHookFailureResponse } from './save-outcome.js'
|
|
18
20
|
|
|
19
21
|
// ---------------------------------------------------------------------------
|
|
@@ -21,6 +23,7 @@ import { toCommittedDocumentHookFailureResponse } from './save-outcome.js'
|
|
|
21
23
|
// ---------------------------------------------------------------------------
|
|
22
24
|
|
|
23
25
|
export const createCollectionDocument = createServerFn({ method: 'POST' })
|
|
26
|
+
.middleware([adminSessionMiddleware])
|
|
24
27
|
.validator(
|
|
25
28
|
(input: {
|
|
26
29
|
collection: string
|
|
@@ -30,56 +33,59 @@ export const createCollectionDocument = createServerFn({ method: 'POST' })
|
|
|
30
33
|
availableLocales?: string[]
|
|
31
34
|
}) => input
|
|
32
35
|
)
|
|
33
|
-
.handler(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
availableLocales,
|
|
40
|
-
} = input
|
|
41
|
-
const logger = getLogger()
|
|
42
|
-
const config = await ensureCollection(path)
|
|
43
|
-
if (!config) {
|
|
44
|
-
throw ERR_NOT_FOUND({
|
|
45
|
-
message: 'Collection not found',
|
|
46
|
-
details: { collectionPath: path },
|
|
47
|
-
}).log(logger)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const serverConfig = getServerConfig()
|
|
51
|
-
const ctx: DocumentLifecycleContext = {
|
|
52
|
-
db: serverConfig.db,
|
|
53
|
-
definition: config.definition,
|
|
54
|
-
collectionId: config.collection.id,
|
|
55
|
-
collectionVersion: config.collection.version,
|
|
56
|
-
collectionPath: path,
|
|
57
|
-
logger,
|
|
58
|
-
defaultLocale: serverConfig.i18n.content.defaultLocale,
|
|
59
|
-
slugifier: serverConfig.slugifier,
|
|
60
|
-
requestContext: await getAdminRequestContext(),
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
let result: CreateDocumentResult
|
|
64
|
-
try {
|
|
65
|
-
result = await createDocument(ctx, {
|
|
66
|
-
data: structuredClone(documentData),
|
|
67
|
-
status: documentData.status,
|
|
68
|
-
locale: locale ?? serverConfig.i18n.content.defaultLocale,
|
|
36
|
+
.handler(
|
|
37
|
+
withDocumentMutationErrors(async ({ data: input }) => {
|
|
38
|
+
const {
|
|
39
|
+
collection: path,
|
|
40
|
+
data: documentData,
|
|
41
|
+
locale,
|
|
69
42
|
path: explicitPath,
|
|
70
43
|
availableLocales,
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
44
|
+
} = input
|
|
45
|
+
const logger = getLogger()
|
|
46
|
+
const config = await ensureCollection(path)
|
|
47
|
+
if (!config) {
|
|
48
|
+
throw ERR_NOT_FOUND({
|
|
49
|
+
message: 'Collection not found',
|
|
50
|
+
details: { collectionPath: path },
|
|
51
|
+
}).log(logger)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const serverConfig = getServerConfig()
|
|
55
|
+
const ctx: DocumentLifecycleContext = {
|
|
56
|
+
db: serverConfig.db,
|
|
57
|
+
definition: config.definition,
|
|
58
|
+
collectionId: config.collection.id,
|
|
59
|
+
collectionVersion: config.collection.version,
|
|
60
|
+
collectionPath: path,
|
|
61
|
+
logger,
|
|
62
|
+
defaultLocale: serverConfig.i18n.content.defaultLocale,
|
|
63
|
+
slugifier: serverConfig.slugifier,
|
|
64
|
+
requestContext: await getAdminRequestContext(),
|
|
65
|
+
}
|
|
77
66
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
67
|
+
let result: CreateDocumentResult
|
|
68
|
+
try {
|
|
69
|
+
result = await createDocument(ctx, {
|
|
70
|
+
data: structuredClone(documentData),
|
|
71
|
+
status: documentData.status,
|
|
72
|
+
locale: locale ?? serverConfig.i18n.content.defaultLocale,
|
|
73
|
+
path: explicitPath,
|
|
74
|
+
availableLocales,
|
|
75
|
+
})
|
|
76
|
+
} catch (error) {
|
|
77
|
+
const committedFailure = toCommittedDocumentHookFailureResponse(error)
|
|
78
|
+
if (committedFailure != null) return committedFailure
|
|
79
|
+
throw error
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// The new document's id is returned so the create view can navigate
|
|
83
|
+
// straight to the edit view (create → edit).
|
|
84
|
+
return {
|
|
85
|
+
status: 'ok' as const,
|
|
86
|
+
documentId: result.documentId,
|
|
87
|
+
documentVersionId: result.documentVersionId,
|
|
88
|
+
revision: result.revision,
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
)
|
|
@@ -14,6 +14,8 @@ import type { DeleteLocaleResult, DocumentLifecycleContext } from '@byline/core/
|
|
|
14
14
|
import { deleteLocale } from '@byline/core/services'
|
|
15
15
|
|
|
16
16
|
import { ensureCollection } from '../../integrations/api-utils.js'
|
|
17
|
+
import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
|
|
18
|
+
import { withDocumentMutationErrors } from '../document-mutation-errors.js'
|
|
17
19
|
import {
|
|
18
20
|
type CollectionDocumentCommittedHookFailureResponse,
|
|
19
21
|
toCommittedDocumentHookFailureResponse,
|
|
@@ -34,40 +36,49 @@ import {
|
|
|
34
36
|
* transport layer.
|
|
35
37
|
*/
|
|
36
38
|
export const deleteDocumentLocale = createServerFn({ method: 'POST' })
|
|
37
|
-
.
|
|
39
|
+
.middleware([adminSessionMiddleware])
|
|
40
|
+
.validator(
|
|
41
|
+
(input: { expectedRevision: number; collection: string; id: string; locale: string }) => input
|
|
42
|
+
)
|
|
38
43
|
.handler(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
withDocumentMutationErrors(
|
|
45
|
+
async ({
|
|
46
|
+
data: input,
|
|
47
|
+
}): Promise<DeleteLocaleResult | CollectionDocumentCommittedHookFailureResponse> => {
|
|
48
|
+
const { collection: path, id, locale } = input
|
|
49
|
+
const logger = getLogger()
|
|
50
|
+
const config = await ensureCollection(path)
|
|
51
|
+
if (!config) {
|
|
52
|
+
throw ERR_NOT_FOUND({
|
|
53
|
+
message: 'Collection not found',
|
|
54
|
+
details: { collectionPath: path },
|
|
55
|
+
}).log(logger)
|
|
56
|
+
}
|
|
51
57
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
const serverConfig = getServerConfig()
|
|
59
|
+
const ctx: DocumentLifecycleContext = {
|
|
60
|
+
db: serverConfig.db,
|
|
61
|
+
definition: config.definition,
|
|
62
|
+
collectionId: config.collection.id,
|
|
63
|
+
collectionVersion: config.collection.version,
|
|
64
|
+
collectionPath: path,
|
|
65
|
+
logger,
|
|
66
|
+
defaultLocale: serverConfig.i18n.content.defaultLocale,
|
|
67
|
+
slugifier: serverConfig.slugifier,
|
|
68
|
+
requestContext: await getAdminRequestContext(),
|
|
69
|
+
}
|
|
64
70
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
try {
|
|
72
|
+
return await deleteLocale(ctx, {
|
|
73
|
+
documentId: id,
|
|
74
|
+
expectedRevision: input.expectedRevision,
|
|
75
|
+
locale,
|
|
76
|
+
})
|
|
77
|
+
} catch (error) {
|
|
78
|
+
const committedFailure = toCommittedDocumentHookFailureResponse(error)
|
|
79
|
+
if (committedFailure != null) return committedFailure
|
|
80
|
+
throw error
|
|
81
|
+
}
|
|
71
82
|
}
|
|
72
|
-
|
|
83
|
+
)
|
|
73
84
|
)
|