@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
|
@@ -0,0 +1,59 @@
|
|
|
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 { describe, expect, it } from 'vitest'
|
|
10
|
+
|
|
11
|
+
import { checkSignInBody, SIGN_IN_MAX_BODY_BYTES, wasSignInBodyChecked } from './sign-in-body.js'
|
|
12
|
+
|
|
13
|
+
function request(body: BodyInit, headers: Record<string, string> = {}) {
|
|
14
|
+
return new Request('https://app.test/_serverFn/login', {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: { 'content-type': 'application/json', ...headers },
|
|
17
|
+
body,
|
|
18
|
+
duplex: 'half',
|
|
19
|
+
} as RequestInit)
|
|
20
|
+
}
|
|
21
|
+
describe('sign-in body boundary', () => {
|
|
22
|
+
it('preserves the original body for RPC parsing', async () => {
|
|
23
|
+
const req = request('{"data":{}}')
|
|
24
|
+
expect(await checkSignInBody(req)).toBeUndefined()
|
|
25
|
+
expect(wasSignInBodyChecked(req)).toBe(true)
|
|
26
|
+
expect(await req.json()).toEqual({ data: {} })
|
|
27
|
+
expect(wasSignInBodyChecked(request('{}'))).toBe(false)
|
|
28
|
+
})
|
|
29
|
+
it('rejects oversized declared or actual bodies', async () => {
|
|
30
|
+
expect(
|
|
31
|
+
(
|
|
32
|
+
await checkSignInBody(
|
|
33
|
+
request('{}', { 'content-length': String(SIGN_IN_MAX_BODY_BYTES + 1) })
|
|
34
|
+
)
|
|
35
|
+
)?.status
|
|
36
|
+
).toBe(413)
|
|
37
|
+
const req = request('x'.repeat(SIGN_IN_MAX_BODY_BYTES + 1), { 'content-length': '1' })
|
|
38
|
+
expect((await checkSignInBody(req))?.status).toBe(413)
|
|
39
|
+
expect(wasSignInBodyChecked(req)).toBe(false)
|
|
40
|
+
})
|
|
41
|
+
it('counts chunked bodies without relying on Content-Length', async () => {
|
|
42
|
+
const req = request(
|
|
43
|
+
new ReadableStream({
|
|
44
|
+
start(controller) {
|
|
45
|
+
controller.enqueue(new Uint8Array(SIGN_IN_MAX_BODY_BYTES))
|
|
46
|
+
controller.enqueue(new Uint8Array(1))
|
|
47
|
+
controller.close()
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
)
|
|
51
|
+
expect((await checkSignInBody(req))?.status).toBe(413)
|
|
52
|
+
})
|
|
53
|
+
it('rejects multipart and wrong methods', async () => {
|
|
54
|
+
expect(
|
|
55
|
+
(await checkSignInBody(request('{}', { 'content-type': 'multipart/form-data' })))?.status
|
|
56
|
+
).toBe(415)
|
|
57
|
+
expect((await checkSignInBody(new Request('https://app.test')))?.status).toBe(405)
|
|
58
|
+
})
|
|
59
|
+
})
|
|
@@ -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
|
+
|
|
9
|
+
/** Limit the serialized RPC envelope, not just its eventual credential fields. */
|
|
10
|
+
export const SIGN_IN_MAX_BODY_BYTES = 16 * 1024
|
|
11
|
+
const checked = new WeakSet<Request>()
|
|
12
|
+
|
|
13
|
+
export function wasSignInBodyChecked(request: Request): boolean {
|
|
14
|
+
return checked.has(request)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Read a bounded clone before Start parses the original. Never await tee cancellation. */
|
|
18
|
+
export async function checkSignInBody(request: Request): Promise<Response | undefined> {
|
|
19
|
+
if (request.method !== 'POST')
|
|
20
|
+
return new Response('Method not allowed', { status: 405, headers: { Allow: 'POST' } })
|
|
21
|
+
if (!request.headers.get('content-type')?.toLowerCase().startsWith('application/json')) {
|
|
22
|
+
return new Response('Expected JSON', { status: 415 })
|
|
23
|
+
}
|
|
24
|
+
const declared = request.headers.get('content-length')
|
|
25
|
+
if (declared !== null && (!/^\d+$/.test(declared) || Number(declared) > SIGN_IN_MAX_BODY_BYTES)) {
|
|
26
|
+
return new Response('Payload too large', { status: 413 })
|
|
27
|
+
}
|
|
28
|
+
const reader = request.clone().body?.getReader()
|
|
29
|
+
if (!reader) return new Response('Missing body', { status: 400 })
|
|
30
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
31
|
+
const deadline = new Promise<never>((_, reject) => {
|
|
32
|
+
timer = setTimeout(() => reject(new Error('Body timeout')), 5000)
|
|
33
|
+
})
|
|
34
|
+
try {
|
|
35
|
+
let size = 0
|
|
36
|
+
while (true) {
|
|
37
|
+
const { value, done } = await Promise.race([reader.read(), deadline])
|
|
38
|
+
if (done) break
|
|
39
|
+
size += value.byteLength
|
|
40
|
+
if (size > SIGN_IN_MAX_BODY_BYTES) return new Response('Payload too large', { status: 413 })
|
|
41
|
+
}
|
|
42
|
+
checked.add(request)
|
|
43
|
+
return undefined
|
|
44
|
+
} catch {
|
|
45
|
+
return new Response('Incomplete request body', { status: 408 })
|
|
46
|
+
} finally {
|
|
47
|
+
clearTimeout(timer)
|
|
48
|
+
void reader.cancel().catch(() => {})
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
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 { createMiddleware } from '@tanstack/react-start'
|
|
10
|
+
|
|
11
|
+
import { adminSignIn } from '../server-fns/auth/sign-in.js'
|
|
12
|
+
import { checkSignInBody } from './sign-in-body.js'
|
|
13
|
+
|
|
14
|
+
/** Register after CSRF in Start's global requestMiddleware, before RPC deserialization. */
|
|
15
|
+
export const passwordSignInMiddleware = createMiddleware().server(async (ctx) => {
|
|
16
|
+
const endpoint = new URL(adminSignIn.url, ctx.request.url).pathname.replace(/\/$/, '')
|
|
17
|
+
if (
|
|
18
|
+
ctx.handlerType !== 'serverFn' ||
|
|
19
|
+
(ctx.pathname !== endpoint && !ctx.pathname.startsWith(`${endpoint}/`))
|
|
20
|
+
)
|
|
21
|
+
return ctx.next()
|
|
22
|
+
const rejected = await checkSignInBody(ctx.request)
|
|
23
|
+
return rejected ?? ctx.next()
|
|
24
|
+
})
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* Admin shell layout — authenticated-only.
|
|
11
11
|
*
|
|
12
12
|
* `beforeLoad` resolves the current admin user (which internally verifies
|
|
13
|
-
* the
|
|
14
|
-
*
|
|
13
|
+
* the access credential without rotating cookies). Expired access is
|
|
14
|
+
* renewed by the sign-in bootstrap. On unauthenticated
|
|
15
15
|
* requests we redirect to `/sign-in` with a `callbackUrl` so the user
|
|
16
16
|
* lands back on the page they originally requested.
|
|
17
17
|
*
|
|
@@ -37,10 +37,12 @@ import { AdminMenuProvider } from '../admin-shell/chrome/admin-menu-provider.jsx
|
|
|
37
37
|
import { Content } from '../admin-shell/chrome/content.js'
|
|
38
38
|
import { RouteError, RouteNotFound } from '../admin-shell/chrome/route-error.js'
|
|
39
39
|
import { RouteProgressBar } from '../admin-shell/chrome/route-progress-bar.js'
|
|
40
|
+
import { SessionChangeBoundary } from '../admin-shell/chrome/session-change-boundary.js'
|
|
40
41
|
import { buildLocaleDefinitions } from '../i18n/locale-definitions.js'
|
|
41
42
|
import { bylineAdminServices } from '../integrations/byline-admin-services.js'
|
|
42
43
|
import { BylineAiAdminProvider } from '../integrations/byline-ai.js'
|
|
43
44
|
import { bylineFieldServices } from '../integrations/byline-field-services.js'
|
|
45
|
+
import { observeSession, sessionIsChanged } from '../integrations/session-coordination.js'
|
|
44
46
|
import { getAnalyticsRuntime } from '../server-fns/analytics/index.js'
|
|
45
47
|
import { getCurrentAdminUser } from '../server-fns/auth/index.js'
|
|
46
48
|
import { getScheduledPublicationRuntime } from '../server-fns/collections/index.js'
|
|
@@ -53,6 +55,7 @@ export function createAdminLayoutRoute(path: string) {
|
|
|
53
55
|
const signInPath = getSignInRoutePath()
|
|
54
56
|
try {
|
|
55
57
|
const user = await getCurrentAdminUser()
|
|
58
|
+
if (typeof window !== 'undefined') observeSession(user.sessionId)
|
|
56
59
|
// Resolve the active interface locale once on the server so SSR
|
|
57
60
|
// and the hydrated client render the same translations and
|
|
58
61
|
// there's no locale flicker. Going through the server fn (rather
|
|
@@ -65,7 +68,20 @@ export function createAdminLayoutRoute(path: string) {
|
|
|
65
68
|
getAnalyticsRuntime(),
|
|
66
69
|
])
|
|
67
70
|
return { user, activeLocale, scheduledPublicationRuntime, analyticsRuntime }
|
|
68
|
-
} catch {
|
|
71
|
+
} catch (error) {
|
|
72
|
+
const code = (error as { code?: string })?.code
|
|
73
|
+
if (
|
|
74
|
+
!(typeof window !== 'undefined' && sessionIsChanged()) &&
|
|
75
|
+
![
|
|
76
|
+
'ERR_UNAUTHENTICATED',
|
|
77
|
+
'ERR_ACCESS_EXPIRED',
|
|
78
|
+
'ERR_INVALID_TOKEN',
|
|
79
|
+
'ERR_REVOKED_TOKEN',
|
|
80
|
+
'ERR_ACCOUNT_DISABLED',
|
|
81
|
+
'ERR_SESSION_CHANGED',
|
|
82
|
+
].includes(code ?? '')
|
|
83
|
+
)
|
|
84
|
+
throw error
|
|
69
85
|
// `getCurrentAdminUser` (via `getAdminRequestContext`) throws
|
|
70
86
|
// `ERR_UNAUTHENTICATED` or a related auth error when no valid
|
|
71
87
|
// session is present. Redirect to sign-in with the original path
|
|
@@ -104,33 +120,35 @@ export function createAdminLayoutRoute(path: string) {
|
|
|
104
120
|
window.location.reload()
|
|
105
121
|
}
|
|
106
122
|
return (
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
<
|
|
116
|
-
<
|
|
117
|
-
<
|
|
118
|
-
<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
123
|
+
<SessionChangeBoundary user={user}>
|
|
124
|
+
<I18nProvider
|
|
125
|
+
bundle={i18n.translations ?? {}}
|
|
126
|
+
activeLocale={activeLocale}
|
|
127
|
+
defaultLocale={i18n.admin.defaultLocale}
|
|
128
|
+
localeDefinitions={localeDefinitions}
|
|
129
|
+
setLocale={handleSetLocale}
|
|
130
|
+
>
|
|
131
|
+
<BylineAdminServicesProvider services={bylineAdminServices}>
|
|
132
|
+
<BylineFieldServicesProvider services={bylineFieldServices}>
|
|
133
|
+
<BylineAiAdminProvider>
|
|
134
|
+
<AdminMenuProvider>
|
|
135
|
+
<RouteProgressBar />
|
|
136
|
+
<AdminAppBar user={user} />
|
|
137
|
+
<main className={cx('byline-admin-layout-main', layoutStyles.main)}>
|
|
138
|
+
<AdminMenuDrawer
|
|
139
|
+
analyticsEnabled={analyticsRuntime.enabled}
|
|
140
|
+
scheduledPublicationEnabled={scheduledPublicationRuntime.enabled}
|
|
141
|
+
/>
|
|
142
|
+
<Content>
|
|
143
|
+
<Outlet />
|
|
144
|
+
</Content>
|
|
145
|
+
</main>
|
|
146
|
+
</AdminMenuProvider>
|
|
147
|
+
</BylineAiAdminProvider>
|
|
148
|
+
</BylineFieldServicesProvider>
|
|
149
|
+
</BylineAdminServicesProvider>
|
|
150
|
+
</I18nProvider>
|
|
151
|
+
</SessionChangeBoundary>
|
|
134
152
|
)
|
|
135
153
|
},
|
|
136
154
|
errorComponent: RouteError,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseDocumentRevision } from '@byline/core'
|
|
1
2
|
/**
|
|
2
3
|
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
4
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
@@ -177,8 +178,13 @@ export function createCollectionListRoute(path: string) {
|
|
|
177
178
|
beforeDocumentId,
|
|
178
179
|
afterDocumentId,
|
|
179
180
|
}) => {
|
|
180
|
-
await placeTreeNode({
|
|
181
|
+
return await placeTreeNode({
|
|
181
182
|
data: {
|
|
183
|
+
expectedRevision: parseDocumentRevision(
|
|
184
|
+
(data as { rows: CollectionTreeRow[] }).rows.find(
|
|
185
|
+
(row) => row.id === documentId
|
|
186
|
+
)?.revision
|
|
187
|
+
),
|
|
182
188
|
collection,
|
|
183
189
|
documentId,
|
|
184
190
|
parentDocumentId,
|
|
@@ -199,8 +205,13 @@ export function createCollectionListRoute(path: string) {
|
|
|
199
205
|
orderable={collectionDef.orderable === true}
|
|
200
206
|
listActions={adminConfig?.listActions}
|
|
201
207
|
onReorder={async ({ documentId, beforeDocumentId, afterDocumentId }) => {
|
|
202
|
-
await reorderCollectionDocument({
|
|
208
|
+
return await reorderCollectionDocument({
|
|
203
209
|
data: {
|
|
210
|
+
expectedRevision: parseDocumentRevision(
|
|
211
|
+
data.docs.find(
|
|
212
|
+
(row: { id: string; revision?: number }) => row.id === documentId
|
|
213
|
+
)?.revision
|
|
214
|
+
),
|
|
204
215
|
collection,
|
|
205
216
|
documentId,
|
|
206
217
|
beforeDocumentId,
|
|
@@ -25,6 +25,7 @@ import { resolveAdminCallbackPath, resolveAdminSignInRedirect } from './admin-pa
|
|
|
25
25
|
import { resolveSignInHomeUrl } from './sign-in-home-url.js'
|
|
26
26
|
|
|
27
27
|
interface SignInSearch {
|
|
28
|
+
reauthenticate?: boolean
|
|
28
29
|
callbackUrl?: string
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -42,7 +43,13 @@ export function createSignInRoute(path: string, { homeUrl }: CreateSignInRouteOp
|
|
|
42
43
|
const Route: any = createFileRoute(path as never)({
|
|
43
44
|
validateSearch: (search: Record<string, unknown>): SignInSearch => {
|
|
44
45
|
const callbackUrl = resolveAdminCallbackPath(search.callbackUrl)
|
|
45
|
-
return {
|
|
46
|
+
return {
|
|
47
|
+
callbackUrl,
|
|
48
|
+
reauthenticate:
|
|
49
|
+
search.reauthenticate === '1' ||
|
|
50
|
+
search.reauthenticate === 1 ||
|
|
51
|
+
search.reauthenticate === true,
|
|
52
|
+
}
|
|
46
53
|
},
|
|
47
54
|
beforeLoad: async () => {
|
|
48
55
|
// Resolve the active locale on the server before render so the
|
|
@@ -54,11 +61,12 @@ export function createSignInRoute(path: string, { homeUrl }: CreateSignInRouteOp
|
|
|
54
61
|
return { activeLocale }
|
|
55
62
|
},
|
|
56
63
|
component: function SignInRouteComponent() {
|
|
57
|
-
const { callbackUrl } = Route.useSearch() as SignInSearch
|
|
64
|
+
const { callbackUrl, reauthenticate } = Route.useSearch() as SignInSearch
|
|
58
65
|
const { activeLocale } = Route.useRouteContext() as { activeLocale: LocaleCode }
|
|
59
66
|
return (
|
|
60
67
|
<SignInPage
|
|
61
68
|
redirectTo={resolveAdminSignInRedirect(callbackUrl)}
|
|
69
|
+
reauthenticate={reauthenticate}
|
|
62
70
|
activeLocale={activeLocale}
|
|
63
71
|
homeUrl={resolvedHomeUrl}
|
|
64
72
|
/>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { testAdapter } from '../../../core/src/storage/db-adapter.test-helper.js'
|
|
1
2
|
/**
|
|
2
3
|
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
4
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
@@ -61,7 +62,7 @@ vi.mock('@byline/core', () => ({
|
|
|
61
62
|
getServerConfig: () => ({
|
|
62
63
|
i18n: { content: { defaultLocale: 'en' } },
|
|
63
64
|
scheduledPublication: { enabled: mocks.scheduledPublicationEnabled },
|
|
64
|
-
db: { queries: { documents: { getPublishedVersion: mocks.getPublishedVersion } } },
|
|
65
|
+
db: testAdapter({ queries: { documents: { getPublishedVersion: mocks.getPublishedVersion } } }),
|
|
65
66
|
}),
|
|
66
67
|
getSingletonAdminConfig: () => null,
|
|
67
68
|
}))
|
|
@@ -112,6 +113,7 @@ describe('singleton editor route loader', () => {
|
|
|
112
113
|
|
|
113
114
|
await expect(load('fr')).resolves.toEqual({
|
|
114
115
|
document: null,
|
|
116
|
+
expectedEmpty: true,
|
|
115
117
|
initialData: { title: 'Default title' },
|
|
116
118
|
})
|
|
117
119
|
expect(mocks.read).toHaveBeenCalledWith({
|
|
@@ -130,6 +132,12 @@ describe('singleton editor route loader', () => {
|
|
|
130
132
|
versionId: 'version-current',
|
|
131
133
|
status: 'draft',
|
|
132
134
|
fields: { title: 'Loaded' },
|
|
135
|
+
revision: 7,
|
|
136
|
+
scheduledPublication: {
|
|
137
|
+
state: 'armed',
|
|
138
|
+
publishAt: '2026-09-01T00:00:00.000Z',
|
|
139
|
+
targetVersionId: 'version-current',
|
|
140
|
+
},
|
|
133
141
|
})
|
|
134
142
|
mocks.getPublishedVersion.mockResolvedValue({
|
|
135
143
|
document_id: 'document-settings',
|
|
@@ -167,6 +175,6 @@ describe('singleton editor route loader', () => {
|
|
|
167
175
|
collection_id: 'collection-settings',
|
|
168
176
|
document_id: 'document-settings',
|
|
169
177
|
})
|
|
170
|
-
expect(mocks.getScheduledPublish).
|
|
178
|
+
expect(mocks.getScheduledPublish).not.toHaveBeenCalled()
|
|
171
179
|
})
|
|
172
180
|
})
|
|
@@ -10,7 +10,7 @@ import { createFileRoute, notFound } from '@tanstack/react-router'
|
|
|
10
10
|
import { createServerFn } from '@tanstack/react-start'
|
|
11
11
|
|
|
12
12
|
import type { DocumentPublishScheduleInfo } from '@byline/client'
|
|
13
|
-
import {
|
|
13
|
+
import { getAdminRequestContext } from '@byline/client/server'
|
|
14
14
|
import {
|
|
15
15
|
buildInitialDataFromFields,
|
|
16
16
|
documentAbilityKey,
|
|
@@ -51,6 +51,7 @@ export const getSingletonRouteDocument = createServerFn({ method: 'GET' })
|
|
|
51
51
|
if (document == null) {
|
|
52
52
|
return {
|
|
53
53
|
document: null,
|
|
54
|
+
expectedEmpty: true,
|
|
54
55
|
initialData: serialise(
|
|
55
56
|
await buildInitialDataFromFields(definition.fields, {
|
|
56
57
|
locale: data.locale ?? getServerConfig().i18n.content.defaultLocale,
|
|
@@ -93,9 +94,9 @@ export const getSingletonRouteDocument = createServerFn({ method: 'GET' })
|
|
|
93
94
|
actor?.hasAbility(documentAbilityKey(definition, 'changeStatus')) === true &&
|
|
94
95
|
actor.hasAbility(documentAbilityKey(definition, 'publish'))
|
|
95
96
|
if (canSchedulePublication) {
|
|
96
|
-
scheduledPublish =
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
scheduledPublish = (
|
|
98
|
+
document as unknown as { scheduledPublication: DocumentPublishScheduleInfo | null }
|
|
99
|
+
).scheduledPublication
|
|
99
100
|
}
|
|
100
101
|
}
|
|
101
102
|
const serializedSchedule = scheduledPublish == null ? null : serialise(scheduledPublish)
|
|
@@ -159,6 +160,7 @@ export function createSingletonRoute(path: string) {
|
|
|
159
160
|
]}
|
|
160
161
|
/>
|
|
161
162
|
<SingletonView
|
|
163
|
+
key={`${singleton}/${locale}`}
|
|
162
164
|
singletonDefinition={definition}
|
|
163
165
|
adminConfig={getSingletonAdminConfig(singleton) ?? undefined}
|
|
164
166
|
document={routeState.document}
|
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
// Handler-level tests isolate transport; session-middleware tests exercise the real pre-handler boundary.
|
|
10
|
+
vi.mock('../../integrations/session-middleware.js', () => ({ adminSessionMiddleware: {} }))
|
|
11
|
+
|
|
12
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
13
|
+
|
|
14
|
+
const mocks = vi.hoisted(() => ({
|
|
15
|
+
context: vi.fn(),
|
|
16
|
+
command: vi.fn(),
|
|
17
|
+
clear: vi.fn(),
|
|
18
|
+
preview: vi.fn(),
|
|
19
|
+
}))
|
|
20
|
+
vi.mock('@tanstack/react-start', () => ({
|
|
21
|
+
createServerFn: () => {
|
|
22
|
+
const chain = {
|
|
23
|
+
middleware: () => chain,
|
|
24
|
+
validator: () => ({ handler: (handler: unknown) => handler }),
|
|
25
|
+
}
|
|
26
|
+
return chain
|
|
27
|
+
},
|
|
28
|
+
}))
|
|
29
|
+
vi.mock('@byline/admin/admin-account', () => ({ changeAccountPasswordCommand: mocks.command }))
|
|
30
|
+
vi.mock('@byline/client/server', () => ({
|
|
31
|
+
getAdminRequestContext: mocks.context,
|
|
32
|
+
clearSessionCookies: mocks.clear,
|
|
33
|
+
clearPreviewCookie: mocks.preview,
|
|
34
|
+
}))
|
|
35
|
+
vi.mock('../../integrations/byline-core.js', () => ({ bylineCore: () => ({ adminStore: {} }) }))
|
|
36
|
+
|
|
37
|
+
import { changeAccountPassword } from './change-password.js'
|
|
38
|
+
|
|
39
|
+
const call = changeAccountPassword as unknown as (args: { data: unknown }) => Promise<unknown>
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
vi.resetAllMocks()
|
|
42
|
+
mocks.context.mockResolvedValue({ actor: { id: 'current' } })
|
|
43
|
+
})
|
|
44
|
+
describe('password change cookie policy', () => {
|
|
45
|
+
it('clears the changing session and preview only after the mutation succeeds', async () => {
|
|
46
|
+
mocks.command.mockImplementation(async () => {
|
|
47
|
+
expect(mocks.clear).not.toHaveBeenCalled()
|
|
48
|
+
return { id: 'current', vid: 2 }
|
|
49
|
+
})
|
|
50
|
+
await expect(call({ data: {} })).resolves.toEqual({ id: 'current', vid: 2 })
|
|
51
|
+
expect(mocks.clear).toHaveBeenCalledOnce()
|
|
52
|
+
expect(mocks.preview).toHaveBeenCalledOnce()
|
|
53
|
+
})
|
|
54
|
+
it.each(['current password', 'stale revision', 'database rollback'])(
|
|
55
|
+
'does not clear cookies after %s failure',
|
|
56
|
+
async (reason) => {
|
|
57
|
+
mocks.command.mockRejectedValue(new Error(reason))
|
|
58
|
+
await expect(call({ data: {} })).rejects.toThrow(reason)
|
|
59
|
+
expect(mocks.clear).not.toHaveBeenCalled()
|
|
60
|
+
expect(mocks.preview).not.toHaveBeenCalled()
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
it('does not mutate or clear cookies when authentication fails', async () => {
|
|
64
|
+
mocks.context.mockRejectedValue(new Error('unauthenticated'))
|
|
65
|
+
await expect(call({ data: {} })).rejects.toThrow('unauthenticated')
|
|
66
|
+
expect(mocks.command).not.toHaveBeenCalled()
|
|
67
|
+
expect(mocks.clear).not.toHaveBeenCalled()
|
|
68
|
+
})
|
|
69
|
+
})
|
|
@@ -9,9 +9,14 @@
|
|
|
9
9
|
import { createServerFn } from '@tanstack/react-start'
|
|
10
10
|
|
|
11
11
|
import { type AccountResponse, changeAccountPasswordCommand } from '@byline/admin/admin-account'
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
clearPreviewCookie,
|
|
14
|
+
clearSessionCookies,
|
|
15
|
+
getAdminRequestContext,
|
|
16
|
+
} from '@byline/client/server'
|
|
13
17
|
|
|
14
18
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
19
|
+
import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
|
|
15
20
|
|
|
16
21
|
export interface ChangeAccountPasswordInput {
|
|
17
22
|
vid: number
|
|
@@ -20,8 +25,14 @@ export interface ChangeAccountPasswordInput {
|
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
export const changeAccountPassword = createServerFn({ method: 'POST' })
|
|
28
|
+
.middleware([adminSessionMiddleware])
|
|
23
29
|
.validator((input: ChangeAccountPasswordInput) => input)
|
|
24
30
|
.handler(async ({ data }): Promise<AccountResponse> => {
|
|
25
31
|
const context = await getAdminRequestContext()
|
|
26
|
-
|
|
32
|
+
const account = await changeAccountPasswordCommand(context, data, {
|
|
33
|
+
store: bylineCore().adminStore!,
|
|
34
|
+
})
|
|
35
|
+
clearSessionCookies()
|
|
36
|
+
clearPreviewCookie()
|
|
37
|
+
return account
|
|
27
38
|
})
|
|
@@ -12,6 +12,7 @@ import { type AccountResponse, getAccountCommand } from '@byline/admin/admin-acc
|
|
|
12
12
|
import { getAdminRequestContext } from '@byline/client/server'
|
|
13
13
|
|
|
14
14
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
15
|
+
import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Read the currently signed-in admin's full account row. Used by the
|
|
@@ -20,6 +21,7 @@ import { bylineCore } from '../../integrations/byline-core.js'
|
|
|
20
21
|
* doesn't carry.
|
|
21
22
|
*/
|
|
22
23
|
export const getAccount = createServerFn({ method: 'GET' })
|
|
24
|
+
.middleware([adminSessionMiddleware])
|
|
23
25
|
.validator((input?: Record<string, never>) => input ?? {})
|
|
24
26
|
.handler(async ({ data }): Promise<AccountResponse> => {
|
|
25
27
|
const context = await getAdminRequestContext()
|
|
@@ -12,6 +12,7 @@ import { type AccountResponse, updateAccountCommand } from '@byline/admin/admin-
|
|
|
12
12
|
import { getAdminRequestContext } from '@byline/client/server'
|
|
13
13
|
|
|
14
14
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
15
|
+
import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
|
|
15
16
|
|
|
16
17
|
export interface UpdateAccountInput {
|
|
17
18
|
vid: number
|
|
@@ -24,6 +25,7 @@ export interface UpdateAccountInput {
|
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
export const updateAccount = createServerFn({ method: 'POST' })
|
|
28
|
+
.middleware([adminSessionMiddleware])
|
|
27
29
|
.validator((input: UpdateAccountInput) => input)
|
|
28
30
|
.handler(async ({ data }): Promise<AccountResponse> => {
|
|
29
31
|
const context = await getAdminRequestContext()
|
|
@@ -13,6 +13,7 @@ import { ADMIN_ACTIVITY_ABILITIES } from '@byline/admin/admin-activity'
|
|
|
13
13
|
import { getAdminRequestContext } from '@byline/client/server'
|
|
14
14
|
import { ERR_AUDIT_UNSUPPORTED, getLogger, getServerConfig } from '@byline/core'
|
|
15
15
|
|
|
16
|
+
import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
|
|
16
17
|
import { type ActorLabelMap, resolveActorLabels } from '../collections/actors.js'
|
|
17
18
|
import type { AuditLogEntryDto } from '../collections/audit.js'
|
|
18
19
|
|
|
@@ -59,6 +60,7 @@ export interface SystemActivityResponse {
|
|
|
59
60
|
* than by any document's own read gate.
|
|
60
61
|
*/
|
|
61
62
|
export const getSystemActivityLog = createServerFn({ method: 'GET' })
|
|
63
|
+
.middleware([adminSessionMiddleware])
|
|
62
64
|
.validator((input: SystemActivitySearchParams) => input ?? {})
|
|
63
65
|
.handler(async ({ data }): Promise<SystemActivityResponse> => {
|
|
64
66
|
const context = await getAdminRequestContext()
|
|
@@ -15,8 +15,10 @@ import {
|
|
|
15
15
|
import { getAdminRequestContext } from '@byline/client/server'
|
|
16
16
|
|
|
17
17
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
18
|
+
import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
|
|
18
19
|
|
|
19
20
|
export const getRoleAbilities = createServerFn({ method: 'GET' })
|
|
21
|
+
.middleware([adminSessionMiddleware])
|
|
20
22
|
.validator((input: { id: string }) => input)
|
|
21
23
|
.handler(async ({ data }): Promise<GetRoleAbilitiesResponse> => {
|
|
22
24
|
const context = await getAdminRequestContext()
|
|
@@ -15,14 +15,16 @@ import {
|
|
|
15
15
|
import { getAdminRequestContext } from '@byline/client/server'
|
|
16
16
|
|
|
17
17
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
18
|
+
import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* List every registered ability — fed from `bylineCore().abilities`,
|
|
21
22
|
* populated at init time by collection auto-registration plus
|
|
22
23
|
* `registerAdminAbilities`. No DB read.
|
|
23
24
|
*/
|
|
24
|
-
export const listRegisteredAbilities = createServerFn({ method: 'GET' })
|
|
25
|
-
|
|
25
|
+
export const listRegisteredAbilities = createServerFn({ method: 'GET' })
|
|
26
|
+
.middleware([adminSessionMiddleware])
|
|
27
|
+
.handler(async (): Promise<ListRegisteredAbilitiesResponse> => {
|
|
26
28
|
const context = await getAdminRequestContext()
|
|
27
29
|
return listRegisteredAbilitiesCommand(
|
|
28
30
|
context,
|
|
@@ -32,5 +34,4 @@ export const listRegisteredAbilities = createServerFn({ method: 'GET' }).handler
|
|
|
32
34
|
abilities: bylineCore().abilities,
|
|
33
35
|
}
|
|
34
36
|
)
|
|
35
|
-
}
|
|
36
|
-
)
|
|
37
|
+
})
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
import { getAdminRequestContext } from '@byline/client/server'
|
|
16
16
|
|
|
17
17
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
18
|
+
import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
|
|
18
19
|
|
|
19
20
|
export interface SetRoleAbilitiesInput {
|
|
20
21
|
id: string
|
|
@@ -22,6 +23,7 @@ export interface SetRoleAbilitiesInput {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export const setRoleAbilities = createServerFn({ method: 'POST' })
|
|
26
|
+
.middleware([adminSessionMiddleware])
|
|
25
27
|
.validator((input: SetRoleAbilitiesInput) => input)
|
|
26
28
|
.handler(async ({ data }): Promise<SetRoleAbilitiesResponse> => {
|
|
27
29
|
const context = await getAdminRequestContext()
|
|
@@ -12,8 +12,10 @@ import { type WhoHasAbilityResponse, whoHasAbilityCommand } from '@byline/admin/
|
|
|
12
12
|
import { getAdminRequestContext } from '@byline/client/server'
|
|
13
13
|
|
|
14
14
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
15
|
+
import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
|
|
15
16
|
|
|
16
17
|
export const whoHasAbility = createServerFn({ method: 'GET' })
|
|
18
|
+
.middleware([adminSessionMiddleware])
|
|
17
19
|
.validator((input: { ability: string }) => input)
|
|
18
20
|
.handler(async ({ data }): Promise<WhoHasAbilityResponse> => {
|
|
19
21
|
const context = await getAdminRequestContext()
|
|
@@ -12,6 +12,7 @@ import { type AdminRoleResponse, createAdminRoleCommand } from '@byline/admin/ad
|
|
|
12
12
|
import { getAdminRequestContext } from '@byline/client/server'
|
|
13
13
|
|
|
14
14
|
import { bylineCore } from '../../integrations/byline-core.js'
|
|
15
|
+
import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
|
|
15
16
|
|
|
16
17
|
export interface CreateAdminRoleInput {
|
|
17
18
|
name: string
|
|
@@ -21,6 +22,7 @@ export interface CreateAdminRoleInput {
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export const createAdminRole = createServerFn({ method: 'POST' })
|
|
25
|
+
.middleware([adminSessionMiddleware])
|
|
24
26
|
.validator((input: CreateAdminRoleInput) => input)
|
|
25
27
|
.handler(async ({ data }): Promise<AdminRoleResponse> => {
|
|
26
28
|
const context = await getAdminRequestContext()
|