@byline/host-tanstack-start 3.21.0 → 4.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/admin-activity/list.js +15 -5
- package/dist/admin-shell/admin-roles/delete.js +2 -1
- package/dist/admin-shell/admin-roles/list.js +2 -1
- package/dist/admin-shell/admin-users/delete.js +2 -1
- package/dist/admin-shell/admin-users/list.js +4 -7
- package/dist/admin-shell/chrome/admin-app-bar.js +2 -1
- package/dist/admin-shell/chrome/admin-menu-drawer.js +9 -8
- package/dist/admin-shell/chrome/admin-menu-provider.js +2 -1
- package/dist/admin-shell/chrome/dashboard.js +4 -3
- package/dist/admin-shell/chrome/sign-in-page.d.ts +2 -2
- package/dist/admin-shell/chrome/sign-in-page.js +2 -2
- package/dist/admin-shell/collections/create.js +4 -3
- package/dist/admin-shell/collections/document-history.js +6 -1
- package/dist/admin-shell/collections/edit.js +18 -14
- package/dist/admin-shell/collections/history.js +5 -4
- package/dist/admin-shell/collections/list.js +8 -7
- package/dist/admin-shell/collections/restore-version-modal.js +2 -1
- package/dist/admin-shell/collections/tree-list.js +53 -17
- package/dist/admin-shell/collections/view-menu.js +9 -8
- package/dist/i18n/resolve-locale.js +1 -1
- package/dist/index.d.ts +5 -6
- package/dist/integrations/abilities.js +8 -7
- package/dist/integrations/host-bridge.d.ts +13 -0
- package/dist/integrations/host-bridge.js +22 -0
- package/dist/integrations/serve-uploads.d.ts +1 -0
- package/dist/integrations/serve-uploads.js +1 -0
- package/dist/integrations/start-errors.js +2 -1
- package/dist/routes/admin-path.d.ts +22 -0
- package/dist/routes/admin-path.js +35 -0
- package/dist/routes/create-admin-account-route.js +3 -2
- package/dist/routes/create-admin-activity-route.js +3 -2
- package/dist/routes/create-admin-dashboard-route.js +2 -1
- package/dist/routes/create-admin-layout-route.d.ts +1 -1
- package/dist/routes/create-admin-layout-route.js +3 -1
- package/dist/routes/create-admin-permissions-route.js +3 -2
- package/dist/routes/create-admin-role-edit-route.js +4 -3
- package/dist/routes/create-admin-roles-list-route.js +3 -2
- package/dist/routes/create-admin-user-edit-route.js +4 -3
- package/dist/routes/create-admin-users-list-route.js +3 -2
- package/dist/routes/create-collection-api-route.js +5 -4
- package/dist/routes/create-collection-create-route.js +4 -3
- package/dist/routes/create-collection-edit-route.js +4 -3
- package/dist/routes/create-collection-history-route.js +5 -4
- package/dist/routes/create-collection-list-route.js +3 -2
- package/dist/routes/create-sign-in-route.js +3 -2
- package/dist/routes/index.d.ts +3 -0
- package/dist/routes/index.js +2 -0
- package/dist/routes/sign-in-path.d.ts +13 -0
- package/dist/routes/sign-in-path.js +19 -0
- package/dist/server-fns/admin-account/change-password.js +1 -1
- package/dist/server-fns/admin-account/get.js +1 -1
- package/dist/server-fns/admin-account/update.js +1 -1
- package/dist/server-fns/admin-activity/get.js +1 -1
- package/dist/server-fns/admin-permissions/get-role-abilities.js +1 -1
- package/dist/server-fns/admin-permissions/list-registered.js +1 -1
- package/dist/server-fns/admin-permissions/set-role-abilities.js +1 -1
- package/dist/server-fns/admin-permissions/who-has.js +1 -1
- package/dist/server-fns/admin-roles/create.js +1 -1
- package/dist/server-fns/admin-roles/delete.js +1 -1
- package/dist/server-fns/admin-roles/get.js +1 -1
- package/dist/server-fns/admin-roles/list.js +1 -1
- package/dist/server-fns/admin-roles/reorder.js +1 -1
- package/dist/server-fns/admin-roles/update.js +1 -1
- package/dist/server-fns/admin-users/create.js +1 -1
- package/dist/server-fns/admin-users/delete.js +1 -1
- package/dist/server-fns/admin-users/disable.js +1 -1
- package/dist/server-fns/admin-users/enable.js +1 -1
- package/dist/server-fns/admin-users/get-user-roles.js +1 -1
- package/dist/server-fns/admin-users/get.js +1 -1
- package/dist/server-fns/admin-users/list.js +1 -1
- package/dist/server-fns/admin-users/set-password.js +1 -1
- package/dist/server-fns/admin-users/set-user-roles.js +1 -1
- package/dist/server-fns/admin-users/update.js +1 -1
- package/dist/server-fns/ai/execute.js +1 -1
- package/dist/server-fns/auth/current-user.js +1 -1
- package/dist/server-fns/auth/sign-in.js +1 -1
- package/dist/server-fns/auth/sign-out.js +1 -2
- package/dist/server-fns/collections/audit.js +1 -1
- package/dist/server-fns/collections/copy-to-locale.js +1 -1
- package/dist/server-fns/collections/create.js +1 -1
- package/dist/server-fns/collections/delete-locale.js +1 -1
- package/dist/server-fns/collections/delete-outcome.d.ts +30 -0
- package/dist/server-fns/collections/delete-outcome.js +34 -0
- package/dist/server-fns/collections/delete.d.ts +1 -4
- package/dist/server-fns/collections/delete.js +3 -5
- package/dist/server-fns/collections/duplicate.js +1 -1
- package/dist/server-fns/collections/get.js +1 -1
- package/dist/server-fns/collections/history.js +1 -1
- package/dist/server-fns/collections/index.d.ts +1 -0
- package/dist/server-fns/collections/index.js +1 -0
- package/dist/server-fns/collections/list.js +1 -1
- package/dist/server-fns/collections/reindex.js +1 -1
- package/dist/server-fns/collections/reorder.js +1 -1
- package/dist/server-fns/collections/restore-version.js +1 -1
- package/dist/server-fns/collections/stats.js +1 -1
- package/dist/server-fns/collections/status.js +1 -1
- package/dist/server-fns/collections/tree-mutation.d.ts +46 -0
- package/dist/server-fns/collections/tree-mutation.js +109 -0
- package/dist/server-fns/collections/tree-read.d.ts +6 -0
- package/dist/server-fns/collections/tree-read.js +15 -0
- package/dist/server-fns/collections/tree.d.ts +5 -4
- package/dist/server-fns/collections/tree.js +16 -13
- package/dist/server-fns/collections/update.js +3 -2
- package/dist/server-fns/collections/upload.js +1 -1
- package/dist/server-fns/i18n/set-locale.js +1 -1
- package/dist/server-fns/preview/disable.js +1 -1
- package/dist/server-fns/preview/enable.js +1 -2
- package/dist/server-fns/preview/state.js +1 -1
- package/dist/vite/client-hook-boundary.d.ts +19 -0
- package/dist/vite/client-hook-boundary.js +20 -0
- package/dist/{integrations/byline-public-client.d.ts → vite/index.d.ts} +1 -2
- package/dist/vite/index.js +1 -0
- package/package.json +15 -14
- package/src/admin-shell/admin-activity/list.tsx +13 -3
- package/src/admin-shell/admin-roles/delete.tsx +2 -1
- package/src/admin-shell/admin-roles/list.tsx +2 -1
- package/src/admin-shell/admin-users/delete.tsx +3 -2
- package/src/admin-shell/admin-users/list.tsx +9 -8
- package/src/admin-shell/chrome/admin-app-bar.tsx +2 -1
- package/src/admin-shell/chrome/admin-menu-drawer.tsx +13 -8
- package/src/admin-shell/chrome/admin-menu-provider.tsx +2 -1
- package/src/admin-shell/chrome/dashboard.tsx +4 -3
- package/src/admin-shell/chrome/sign-in-page.tsx +3 -3
- package/src/admin-shell/collections/create.tsx +4 -3
- package/src/admin-shell/collections/document-history.tsx +5 -0
- package/src/admin-shell/collections/edit.tsx +20 -13
- package/src/admin-shell/collections/history.tsx +6 -5
- package/src/admin-shell/collections/list.tsx +8 -7
- package/src/admin-shell/collections/restore-version-modal.tsx +2 -1
- package/src/admin-shell/collections/tree-list.tsx +78 -22
- package/src/admin-shell/collections/view-menu.tsx +9 -8
- package/src/i18n/resolve-locale.ts +1 -1
- package/src/index.ts +5 -6
- package/src/integrations/abilities.tsx +14 -4
- package/src/integrations/host-bridge.ts +56 -0
- package/src/integrations/serve-uploads.ts +6 -0
- package/src/integrations/start-errors.test.node.ts +22 -0
- package/src/integrations/start-errors.ts +1 -0
- package/src/routes/admin-path.test.node.ts +129 -0
- package/src/routes/admin-path.ts +66 -0
- package/src/routes/auth-routes.test.node.ts +39 -0
- package/src/routes/create-admin-account-route.tsx +3 -2
- package/src/routes/create-admin-activity-route.tsx +3 -2
- package/src/routes/create-admin-dashboard-route.tsx +2 -1
- package/src/routes/create-admin-layout-route.tsx +4 -4
- package/src/routes/create-admin-permissions-route.tsx +3 -2
- package/src/routes/create-admin-role-edit-route.tsx +7 -3
- package/src/routes/create-admin-roles-list-route.tsx +3 -2
- package/src/routes/create-admin-user-edit-route.tsx +7 -3
- package/src/routes/create-admin-users-list-route.tsx +3 -2
- package/src/routes/create-collection-api-route.tsx +8 -4
- package/src/routes/create-collection-create-route.tsx +7 -3
- package/src/routes/create-collection-edit-route.tsx +7 -3
- package/src/routes/create-collection-history-route.tsx +8 -4
- package/src/routes/create-collection-list-route.tsx +3 -2
- package/src/routes/create-sign-in-route.tsx +9 -3
- package/src/routes/index.ts +11 -0
- package/src/routes/sign-in-path.test.node.ts +79 -0
- package/src/routes/sign-in-path.ts +36 -0
- package/src/server-fns/admin-account/change-password.ts +1 -1
- package/src/server-fns/admin-account/get.ts +1 -1
- package/src/server-fns/admin-account/update.ts +1 -1
- package/src/server-fns/admin-activity/get.ts +1 -1
- package/src/server-fns/admin-permissions/get-role-abilities.ts +1 -1
- package/src/server-fns/admin-permissions/list-registered.ts +1 -1
- package/src/server-fns/admin-permissions/set-role-abilities.ts +1 -1
- package/src/server-fns/admin-permissions/who-has.ts +1 -1
- package/src/server-fns/admin-roles/create.ts +1 -1
- package/src/server-fns/admin-roles/delete.ts +1 -1
- package/src/server-fns/admin-roles/get.ts +1 -1
- package/src/server-fns/admin-roles/list.ts +1 -1
- package/src/server-fns/admin-roles/reorder.ts +1 -1
- package/src/server-fns/admin-roles/update.ts +1 -1
- package/src/server-fns/admin-users/create.ts +1 -1
- package/src/server-fns/admin-users/delete.ts +1 -1
- package/src/server-fns/admin-users/disable.ts +1 -1
- package/src/server-fns/admin-users/enable.ts +1 -1
- package/src/server-fns/admin-users/get-user-roles.ts +1 -1
- package/src/server-fns/admin-users/get.ts +1 -1
- package/src/server-fns/admin-users/list.ts +1 -1
- package/src/server-fns/admin-users/set-password.ts +1 -1
- package/src/server-fns/admin-users/set-user-roles.ts +1 -1
- package/src/server-fns/admin-users/update.ts +1 -1
- package/src/server-fns/ai/execute.ts +1 -2
- package/src/server-fns/auth/current-user.ts +1 -1
- package/src/server-fns/auth/sign-in.ts +1 -1
- package/src/server-fns/auth/sign-out.ts +5 -3
- package/src/server-fns/collections/audit.ts +1 -1
- package/src/server-fns/collections/copy-to-locale.ts +1 -1
- package/src/server-fns/collections/create.ts +1 -1
- package/src/server-fns/collections/delete-locale.ts +1 -1
- package/src/server-fns/collections/delete-outcome.test.node.ts +72 -0
- package/src/server-fns/collections/delete-outcome.ts +80 -0
- package/src/server-fns/collections/delete.ts +3 -2
- package/src/server-fns/collections/duplicate.ts +1 -1
- package/src/server-fns/collections/get.ts +3 -1
- package/src/server-fns/collections/history.ts +1 -1
- package/src/server-fns/collections/index.ts +1 -0
- package/src/server-fns/collections/list.ts +1 -1
- package/src/server-fns/collections/reindex.ts +1 -1
- package/src/server-fns/collections/reorder.ts +1 -1
- package/src/server-fns/collections/restore-version.ts +1 -1
- package/src/server-fns/collections/stats.ts +2 -1
- package/src/server-fns/collections/status.ts +1 -1
- package/src/server-fns/collections/tree-mutation.test.node.ts +225 -0
- package/src/server-fns/collections/tree-mutation.ts +165 -0
- package/src/server-fns/collections/tree-read.test.node.ts +46 -0
- package/src/server-fns/collections/tree-read.ts +26 -0
- package/src/server-fns/collections/tree.ts +27 -11
- package/src/server-fns/collections/update.ts +4 -1
- package/src/server-fns/collections/upload.ts +1 -1
- package/src/server-fns/i18n/set-locale.ts +1 -1
- package/src/server-fns/preview/disable.ts +1 -1
- package/src/server-fns/preview/enable.ts +1 -2
- package/src/server-fns/preview/state.ts +1 -1
- package/src/vite/client-hook-boundary.test.node.ts +50 -0
- package/src/vite/client-hook-boundary.ts +45 -0
- package/src/vite/index.ts +9 -0
- package/dist/auth/auth-context.d.ts +0 -37
- package/dist/auth/auth-context.js +0 -57
- package/dist/auth/auth-cookies.d.ts +0 -34
- package/dist/auth/auth-cookies.js +0 -46
- package/dist/auth/preview-cookies.d.ts +0 -19
- package/dist/auth/preview-cookies.js +0 -26
- package/dist/integrations/byline-client.d.ts +0 -24
- package/dist/integrations/byline-client.js +0 -25
- package/dist/integrations/byline-public-client.js +0 -15
- package/dist/integrations/byline-viewer-client.d.ts +0 -20
- package/dist/integrations/byline-viewer-client.js +0 -39
- package/src/auth/auth-context.test.node.ts +0 -215
- package/src/auth/auth-context.ts +0 -124
- package/src/auth/auth-cookies.ts +0 -113
- package/src/auth/preview-cookies.ts +0 -73
- package/src/integrations/byline-client.ts +0 -66
- package/src/integrations/byline-public-client.ts +0 -53
- package/src/integrations/byline-viewer-client.ts +0 -99
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { defineClientConfig } from '@byline/core'
|
|
2
|
+
import { beforeEach, describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
getAdminRouteId,
|
|
6
|
+
getAdminRoutePath,
|
|
7
|
+
isAdminRoutePathActive,
|
|
8
|
+
isRoutePathWithin,
|
|
9
|
+
resolveAdminCallbackPath,
|
|
10
|
+
resolveAdminSignInRedirect,
|
|
11
|
+
} from './admin-path.js'
|
|
12
|
+
|
|
13
|
+
function registerRoutes(admin?: string): void {
|
|
14
|
+
defineClientConfig({
|
|
15
|
+
serverURL: 'http://localhost:3000',
|
|
16
|
+
routes: admin ? { admin } : undefined,
|
|
17
|
+
collections: [],
|
|
18
|
+
admin: [],
|
|
19
|
+
i18n: {
|
|
20
|
+
interface: { defaultLocale: 'en', locales: [] },
|
|
21
|
+
content: { defaultLocale: 'en', locales: [] },
|
|
22
|
+
},
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('getAdminRoutePath', () => {
|
|
27
|
+
beforeEach(() => registerRoutes('/cms/'))
|
|
28
|
+
|
|
29
|
+
it.each([
|
|
30
|
+
[[], '/cms'],
|
|
31
|
+
[['collections'], '/cms/collections'],
|
|
32
|
+
[['collections', '$collection'], '/cms/collections/$collection'],
|
|
33
|
+
[['collections', '$collection', 'create'], '/cms/collections/$collection/create'],
|
|
34
|
+
[['collections', '$collection', '$id'], '/cms/collections/$collection/$id'],
|
|
35
|
+
[['collections', '$collection', '$id', 'history'], '/cms/collections/$collection/$id/history'],
|
|
36
|
+
[['collections', '$collection', '$id', 'api'], '/cms/collections/$collection/$id/api'],
|
|
37
|
+
[['users'], '/cms/users'],
|
|
38
|
+
[['users', '$id'], '/cms/users/$id'],
|
|
39
|
+
[['roles'], '/cms/roles'],
|
|
40
|
+
[['roles', '$id'], '/cms/roles/$id'],
|
|
41
|
+
[['permissions'], '/cms/permissions'],
|
|
42
|
+
[['activity'], '/cms/activity'],
|
|
43
|
+
[['account'], '/cms/account'],
|
|
44
|
+
])('joins %j against the configured admin mount', (segments, expected) => {
|
|
45
|
+
expect(getAdminRoutePath(...segments)).toBe(expected)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('safely joins slash-delimited segments', () => {
|
|
49
|
+
expect(getAdminRoutePath('/collections/', '/pages/')).toBe('/cms/collections/pages')
|
|
50
|
+
registerRoutes('cms')
|
|
51
|
+
expect(getAdminRoutePath('users')).toBe('/cms/users')
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('preserves a configured multi-segment admin mount', () => {
|
|
55
|
+
registerRoutes('/internal/cms/')
|
|
56
|
+
expect(getAdminRoutePath('users')).toBe('/internal/cms/users')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('accepts callbacks inside a configured multi-segment admin mount', () => {
|
|
60
|
+
registerRoutes('/internal/cms/')
|
|
61
|
+
expect(resolveAdminCallbackPath('/internal/cms/users?status=active#results')).toBe(
|
|
62
|
+
'/internal/cms/users?status=active#results'
|
|
63
|
+
)
|
|
64
|
+
expect(resolveAdminCallbackPath('/internal/cms-old/users')).toBeUndefined()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('matches dashboard, collection, and trailing-slash paths', () => {
|
|
68
|
+
expect(isAdminRoutePathActive('/cms')).toBe(true)
|
|
69
|
+
expect(isAdminRoutePathActive('/cms/')).toBe(true)
|
|
70
|
+
expect(isAdminRoutePathActive('/cms/collections/pages', 'collections')).toBe(true)
|
|
71
|
+
expect(isAdminRoutePathActive('/cms/users', 'collections')).toBe(false)
|
|
72
|
+
expect(isAdminRoutePathActive('/cms/collections-old', 'collections')).toBe(false)
|
|
73
|
+
expect(isRoutePathWithin('/cms/users/123', '/cms/users')).toBe(true)
|
|
74
|
+
expect(isRoutePathWithin('/cms/users-archive', '/cms/users')).toBe(false)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it.each([
|
|
78
|
+
['/cms', '/cms'],
|
|
79
|
+
['/cms/', '/cms/'],
|
|
80
|
+
[
|
|
81
|
+
'/cms/collections/pages?query=hello%20world#results',
|
|
82
|
+
'/cms/collections/pages?query=hello%20world#results',
|
|
83
|
+
],
|
|
84
|
+
])('accepts a safe admin callback %j', (callback, expected) => {
|
|
85
|
+
expect(resolveAdminCallbackPath(callback)).toBe(expected)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it.each([
|
|
89
|
+
undefined,
|
|
90
|
+
'',
|
|
91
|
+
' /cms',
|
|
92
|
+
'https://evil.test/cms',
|
|
93
|
+
'//evil.test/cms',
|
|
94
|
+
'/\\evil.test/cms',
|
|
95
|
+
'/admin',
|
|
96
|
+
'/cms-old',
|
|
97
|
+
'/cms\\users',
|
|
98
|
+
'%2Fcms',
|
|
99
|
+
'/%2F%2Fevil.test',
|
|
100
|
+
'/cms/%2e%2e/account',
|
|
101
|
+
'/cms/%2e/account',
|
|
102
|
+
'/cms/%252e%252e/account',
|
|
103
|
+
'/cms/%5c%5cevil.test',
|
|
104
|
+
'/cms/../account',
|
|
105
|
+
'/cms/./account',
|
|
106
|
+
'/cms\u0085account',
|
|
107
|
+
])('rejects an unsafe or non-admin callback %j', (callback) => {
|
|
108
|
+
expect(resolveAdminCallbackPath(callback)).toBeUndefined()
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('sends direct and rejected-callback sign-ins to the configured dashboard', () => {
|
|
112
|
+
expect(resolveAdminSignInRedirect(undefined)).toBe('/cms')
|
|
113
|
+
expect(resolveAdminSignInRedirect('https://evil.test')).toBe('/cms')
|
|
114
|
+
expect(resolveAdminSignInRedirect('/cms/account')).toBe('/cms/account')
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('builds route IDs matching the custom filesystem mount', () => {
|
|
118
|
+
expect(getAdminRouteId()).toBe('/_byline/cms')
|
|
119
|
+
expect(getAdminRouteId('collections', '$collection', '$id', 'history')).toBe(
|
|
120
|
+
'/_byline/cms/collections/$collection/$id/history'
|
|
121
|
+
)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('keeps the default admin mount backward compatible', () => {
|
|
125
|
+
registerRoutes()
|
|
126
|
+
expect(getAdminRoutePath()).toBe('/admin')
|
|
127
|
+
expect(getAdminRoutePath('collections', '$collection')).toBe('/admin/collections/$collection')
|
|
128
|
+
})
|
|
129
|
+
})
|
|
@@ -0,0 +1,66 @@
|
|
|
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 { getClientConfig, normalizeRootRelativeRedirect } from '@byline/core'
|
|
10
|
+
|
|
11
|
+
/** Build an admin URL or parameterized TanStack route from client-safe config. */
|
|
12
|
+
export function getAdminRoutePath(...segments: ReadonlyArray<string | number>): string {
|
|
13
|
+
const configuredBase = getClientConfig().routes.admin
|
|
14
|
+
const normalizedBase = configuredBase.replace(/^\/+|\/+$/g, '')
|
|
15
|
+
const base = normalizedBase ? `/${normalizedBase}` : '/'
|
|
16
|
+
const suffix = segments
|
|
17
|
+
.map((segment) => String(segment).replace(/^\/+|\/+$/g, ''))
|
|
18
|
+
.filter(Boolean)
|
|
19
|
+
.join('/')
|
|
20
|
+
|
|
21
|
+
if (!suffix) return base
|
|
22
|
+
return base === '/' ? `/${suffix}` : `${base}/${suffix}`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Build the matching pathless-group route ID used by generated host route trees. */
|
|
26
|
+
export function getAdminRouteId(...segments: ReadonlyArray<string | number>): string {
|
|
27
|
+
return `/_byline${getAdminRoutePath(...segments)}`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** True when a pathname is the target route or one of its segment-delimited descendants. */
|
|
31
|
+
export function isRoutePathWithin(pathname: string, target: string): boolean {
|
|
32
|
+
const current = pathname.length > 1 ? pathname.replace(/\/+$/, '') : pathname
|
|
33
|
+
const base = target.length > 1 ? target.replace(/\/+$/, '') : target
|
|
34
|
+
return (
|
|
35
|
+
current === base || (base === '/' ? current.startsWith('/') : current.startsWith(`${base}/`))
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Accept an untrusted callback only when it is an unencoded, same-origin path
|
|
41
|
+
* inside the configured admin mount.
|
|
42
|
+
*/
|
|
43
|
+
export function resolveAdminCallbackPath(value: unknown): string | undefined {
|
|
44
|
+
if (typeof value !== 'string') return undefined
|
|
45
|
+
const normalized = normalizeRootRelativeRedirect(value)
|
|
46
|
+
if (!normalized) return undefined
|
|
47
|
+
const url = new URL(normalized, 'https://byline.invalid')
|
|
48
|
+
if (!isRoutePathWithin(url.pathname, getAdminRoutePath())) return undefined
|
|
49
|
+
return `${url.pathname}${url.search}${url.hash}`
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Resolve an untrusted callback or fall back to the configured admin dashboard. */
|
|
53
|
+
export function resolveAdminSignInRedirect(value: unknown): string {
|
|
54
|
+
return resolveAdminCallbackPath(value) ?? getAdminRoutePath()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Match the admin root exactly and admin child entries by pathname prefix. */
|
|
58
|
+
export function isAdminRoutePathActive(
|
|
59
|
+
pathname: string,
|
|
60
|
+
...segments: ReadonlyArray<string>
|
|
61
|
+
): boolean {
|
|
62
|
+
const target = getAdminRoutePath(...segments)
|
|
63
|
+
const current = pathname.length > 1 ? pathname.replace(/\/+$/, '') : pathname
|
|
64
|
+
if (segments.length === 0) return current === target
|
|
65
|
+
return isRoutePathWithin(current, target)
|
|
66
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { defineClientConfig } from '@byline/core'
|
|
2
|
+
import { beforeEach, describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { resolveAdminCallbackPath, resolveAdminSignInRedirect } from './admin-path.js'
|
|
5
|
+
import { configureSignInRoutePath, getSignInRoutePath } from './sign-in-path.js'
|
|
6
|
+
|
|
7
|
+
describe('admin authentication routes', () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
defineClientConfig({
|
|
10
|
+
serverURL: 'http://localhost:3000',
|
|
11
|
+
routes: { admin: '/cms/', signIn: '/staff/login/' },
|
|
12
|
+
collections: [],
|
|
13
|
+
admin: [],
|
|
14
|
+
i18n: {
|
|
15
|
+
interface: { defaultLocale: 'en', locales: [] },
|
|
16
|
+
content: { defaultLocale: 'en', locales: [] },
|
|
17
|
+
},
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('sanitizes sign-in route search before it reaches the form', () => {
|
|
22
|
+
expect(resolveAdminCallbackPath('/cms/account')).toBe('/cms/account')
|
|
23
|
+
expect(resolveAdminCallbackPath('https://evil.test')).toBeUndefined()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('uses the custom mount for direct sign-in and callback navigation', () => {
|
|
27
|
+
expect(resolveAdminSignInRedirect(undefined)).toBe('/cms')
|
|
28
|
+
expect(resolveAdminSignInRedirect('/cms/collections/pages/1')).toBe('/cms/collections/pages/1')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('shares routes.signIn between guard and sign-out consumers', () => {
|
|
32
|
+
expect(getSignInRoutePath()).toBe('/staff/login')
|
|
33
|
+
expect(configureSignInRoutePath('/staff/login/')).toBe('/staff/login')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('does not allow the deprecated layout override to silently diverge', () => {
|
|
37
|
+
expect(() => configureSignInRoutePath('/auth/login')).toThrow(/must match routes\.signIn/)
|
|
38
|
+
})
|
|
39
|
+
})
|
|
@@ -14,6 +14,7 @@ import { Container, Section } from '@byline/ui/react'
|
|
|
14
14
|
|
|
15
15
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
16
16
|
import { type AccountResponse, getAccount } from '../server-fns/admin-account/index.js'
|
|
17
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
17
18
|
|
|
18
19
|
function displayNameFor(account: {
|
|
19
20
|
given_name: string | null
|
|
@@ -43,8 +44,8 @@ export function createAdminAccountRoute(path: string) {
|
|
|
43
44
|
<>
|
|
44
45
|
<BreadcrumbsClient
|
|
45
46
|
breadcrumbs={[
|
|
46
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
47
|
-
{ label: t('chrome.account'), href: '
|
|
47
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
48
|
+
{ label: t('chrome.account'), href: getAdminRoutePath('account') },
|
|
48
49
|
]}
|
|
49
50
|
/>
|
|
50
51
|
<Section className="pb-2">
|
|
@@ -14,6 +14,7 @@ import { z } from 'zod'
|
|
|
14
14
|
import { ActivitySystemView } from '../admin-shell/admin-activity/list.js'
|
|
15
15
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
16
16
|
import { getSystemActivityLog } from '../server-fns/admin-activity/index.js'
|
|
17
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
17
18
|
import type { SystemActivityResponse } from '../server-fns/admin-activity/index.js'
|
|
18
19
|
|
|
19
20
|
const searchSchema = z.object({
|
|
@@ -62,8 +63,8 @@ export function createAdminActivityRoute(path: string) {
|
|
|
62
63
|
<>
|
|
63
64
|
<BreadcrumbsClient
|
|
64
65
|
breadcrumbs={[
|
|
65
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
66
|
-
{ label: t('activity.title'), href: '
|
|
66
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
67
|
+
{ label: t('activity.title'), href: getAdminRoutePath('activity') },
|
|
67
68
|
]}
|
|
68
69
|
/>
|
|
69
70
|
<ActivitySystemView data={data} />
|
|
@@ -14,6 +14,7 @@ import { useTranslation } from '@byline/i18n/react'
|
|
|
14
14
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
15
15
|
import { AdminDashboard } from '../admin-shell/chrome/dashboard.js'
|
|
16
16
|
import { type CollectionStatusCount, getCollectionStats } from '../server-fns/collections/index.js'
|
|
17
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
17
18
|
|
|
18
19
|
export function createAdminDashboardRoute(path: string) {
|
|
19
20
|
// biome-ignore lint/suspicious/noExplicitAny: dynamic path bypasses route-tree typing
|
|
@@ -44,7 +45,7 @@ export function createAdminDashboardRoute(path: string) {
|
|
|
44
45
|
return (
|
|
45
46
|
<>
|
|
46
47
|
<BreadcrumbsClient
|
|
47
|
-
breadcrumbs={[{ label: t('chrome.menu.dashboard'), href:
|
|
48
|
+
breadcrumbs={[{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() }]}
|
|
48
49
|
/>
|
|
49
50
|
<AdminDashboard statsMap={statsMap} />
|
|
50
51
|
</>
|
|
@@ -43,18 +43,18 @@ import { BylineAiAdminProvider } from '../integrations/byline-ai.js'
|
|
|
43
43
|
import { bylineFieldServices } from '../integrations/byline-field-services.js'
|
|
44
44
|
import { getCurrentAdminUser } from '../server-fns/auth/index.js'
|
|
45
45
|
import { getActiveLocaleFn, setInterfaceLocaleFn } from '../server-fns/i18n/index.js'
|
|
46
|
+
import { createSignInRoutePathResolver } from './sign-in-path.js'
|
|
46
47
|
|
|
47
48
|
interface AdminLayoutOpts {
|
|
48
|
-
/**
|
|
49
|
+
/** @deprecated Configure `routes.signIn`; an override must resolve to the same path. */
|
|
49
50
|
signInPath?: string
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
export function createAdminLayoutRoute(path: string, opts: AdminLayoutOpts = {}) {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
// biome-ignore lint/suspicious/noExplicitAny: dynamic path bypasses route-tree typing
|
|
54
|
+
const resolveSignInPath = createSignInRoutePathResolver(opts.signInPath)
|
|
56
55
|
const Route: any = createFileRoute(path as never)({
|
|
57
56
|
beforeLoad: async ({ location }: { location: { href: string } }) => {
|
|
57
|
+
const signInPath = resolveSignInPath()
|
|
58
58
|
try {
|
|
59
59
|
const user = await getCurrentAdminUser()
|
|
60
60
|
// Resolve the active interface locale once on the server so SSR
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type ListRegisteredAbilitiesResponse,
|
|
17
17
|
listRegisteredAbilities,
|
|
18
18
|
} from '../server-fns/admin-permissions/index.js'
|
|
19
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
19
20
|
|
|
20
21
|
export function createAdminPermissionsRoute(path: string) {
|
|
21
22
|
// biome-ignore lint/suspicious/noExplicitAny: dynamic path bypasses route-tree typing
|
|
@@ -31,8 +32,8 @@ export function createAdminPermissionsRoute(path: string) {
|
|
|
31
32
|
<>
|
|
32
33
|
<BreadcrumbsClient
|
|
33
34
|
breadcrumbs={[
|
|
34
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
35
|
-
{ label: t('chrome.menu.permissions'), href: '
|
|
35
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
36
|
+
{ label: t('chrome.menu.permissions'), href: getAdminRoutePath('permissions') },
|
|
36
37
|
]}
|
|
37
38
|
/>
|
|
38
39
|
<AbilitiesInspector data={data} />
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
listRegisteredAbilities,
|
|
20
20
|
} from '../server-fns/admin-permissions/index.js'
|
|
21
21
|
import { type AdminRoleResponse, getAdminRole } from '../server-fns/admin-roles/index.js'
|
|
22
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
22
23
|
|
|
23
24
|
interface LoaderData {
|
|
24
25
|
role: AdminRoleResponse
|
|
@@ -62,9 +63,12 @@ export function createAdminRoleEditRoute(path: string) {
|
|
|
62
63
|
<>
|
|
63
64
|
<BreadcrumbsClient
|
|
64
65
|
breadcrumbs={[
|
|
65
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
66
|
-
{ label: t('chrome.menu.adminRoles'), href: '
|
|
67
|
-
{
|
|
66
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
67
|
+
{ label: t('chrome.menu.adminRoles'), href: getAdminRoutePath('roles') },
|
|
68
|
+
{
|
|
69
|
+
label: t('adminRoles.breadcrumbDetail'),
|
|
70
|
+
href: getAdminRoutePath('roles', role.id),
|
|
71
|
+
},
|
|
68
72
|
]}
|
|
69
73
|
/>
|
|
70
74
|
<Section className="py-5 pb-2">
|
|
@@ -13,6 +13,7 @@ import { useTranslation } from '@byline/i18n/react'
|
|
|
13
13
|
import { AdminRolesListView } from '../admin-shell/admin-roles/list.js'
|
|
14
14
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
15
15
|
import { type AdminRoleListResponse, listAdminRoles } from '../server-fns/admin-roles/index.js'
|
|
16
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
16
17
|
|
|
17
18
|
export function createAdminRolesListRoute(path: string) {
|
|
18
19
|
// biome-ignore lint/suspicious/noExplicitAny: dynamic path bypasses route-tree typing
|
|
@@ -28,8 +29,8 @@ export function createAdminRolesListRoute(path: string) {
|
|
|
28
29
|
<>
|
|
29
30
|
<BreadcrumbsClient
|
|
30
31
|
breadcrumbs={[
|
|
31
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
32
|
-
{ label: t('chrome.menu.adminRoles'), href: '
|
|
32
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
33
|
+
{ label: t('chrome.menu.adminRoles'), href: getAdminRoutePath('roles') },
|
|
33
34
|
]}
|
|
34
35
|
/>
|
|
35
36
|
<AdminRolesListView data={data} />
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
getAdminUser,
|
|
20
20
|
getUserRoles,
|
|
21
21
|
} from '../server-fns/admin-users/index.js'
|
|
22
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
22
23
|
|
|
23
24
|
interface LoaderData {
|
|
24
25
|
user: AdminUserResponse
|
|
@@ -79,9 +80,12 @@ export function createAdminUserEditRoute(path: string) {
|
|
|
79
80
|
<>
|
|
80
81
|
<BreadcrumbsClient
|
|
81
82
|
breadcrumbs={[
|
|
82
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
83
|
-
{ label: t('chrome.menu.adminUsers'), href: '
|
|
84
|
-
{
|
|
83
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
84
|
+
{ label: t('chrome.menu.adminUsers'), href: getAdminRoutePath('users') },
|
|
85
|
+
{
|
|
86
|
+
label: t('adminUsers.breadcrumbDetail'),
|
|
87
|
+
href: getAdminRoutePath('users', user.id),
|
|
88
|
+
},
|
|
85
89
|
]}
|
|
86
90
|
/>
|
|
87
91
|
<Section className="py-5 pb-2">
|
|
@@ -14,6 +14,7 @@ import { z } from 'zod'
|
|
|
14
14
|
import { AdminUsersListView } from '../admin-shell/admin-users/list.js'
|
|
15
15
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
16
16
|
import { type AdminUserListResponse, listAdminUsers } from '../server-fns/admin-users/index.js'
|
|
17
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
17
18
|
|
|
18
19
|
const orderSchema = z.enum([
|
|
19
20
|
'given_name',
|
|
@@ -72,8 +73,8 @@ export function createAdminUsersListRoute(path: string) {
|
|
|
72
73
|
<>
|
|
73
74
|
<BreadcrumbsClient
|
|
74
75
|
breadcrumbs={[
|
|
75
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
76
|
-
{ label: t('chrome.menu.adminUsers'), href: '
|
|
76
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
77
|
+
{ label: t('chrome.menu.adminUsers'), href: getAdminRoutePath('users') },
|
|
77
78
|
]}
|
|
78
79
|
/>
|
|
79
80
|
<AdminUsersListView data={data} />
|
|
@@ -16,6 +16,7 @@ import { z } from 'zod'
|
|
|
16
16
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
17
17
|
import { ApiView } from '../admin-shell/collections/api.js'
|
|
18
18
|
import { getCollectionDocument } from '../server-fns/collections/index.js'
|
|
19
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
19
20
|
import { getContentLocaleRouteConfig } from './get-content-locale-route-config.js'
|
|
20
21
|
|
|
21
22
|
const searchSchema = z.object({
|
|
@@ -81,15 +82,18 @@ export function createCollectionApiRoute(path: string) {
|
|
|
81
82
|
<>
|
|
82
83
|
<BreadcrumbsClient
|
|
83
84
|
breadcrumbs={[
|
|
84
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
85
|
-
{
|
|
85
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
86
|
+
{
|
|
87
|
+
label: collectionDef.labels.plural,
|
|
88
|
+
href: getAdminRoutePath('collections', collection),
|
|
89
|
+
},
|
|
86
90
|
{
|
|
87
91
|
label: t('common.actions.edit'),
|
|
88
|
-
href:
|
|
92
|
+
href: getAdminRoutePath('collections', collection, id),
|
|
89
93
|
},
|
|
90
94
|
{
|
|
91
95
|
label: t('collections.viewMenu.apiButton'),
|
|
92
|
-
href:
|
|
96
|
+
href: getAdminRoutePath('collections', collection, id, 'api'),
|
|
93
97
|
},
|
|
94
98
|
]}
|
|
95
99
|
/>
|
|
@@ -18,6 +18,7 @@ import { useTranslation } from '@byline/i18n/react'
|
|
|
18
18
|
|
|
19
19
|
import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js'
|
|
20
20
|
import { CreateView } from '../admin-shell/collections/create.js'
|
|
21
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
21
22
|
|
|
22
23
|
export function createCollectionCreateRoute(path: string) {
|
|
23
24
|
// biome-ignore lint/suspicious/noExplicitAny: dynamic path bypasses route-tree typing
|
|
@@ -49,11 +50,14 @@ export function createCollectionCreateRoute(path: string) {
|
|
|
49
50
|
<>
|
|
50
51
|
<BreadcrumbsClient
|
|
51
52
|
breadcrumbs={[
|
|
52
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
53
|
-
{
|
|
53
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
54
|
+
{
|
|
55
|
+
label: collectionDef.labels.plural,
|
|
56
|
+
href: getAdminRoutePath('collections', collection),
|
|
57
|
+
},
|
|
54
58
|
{
|
|
55
59
|
label: t('collections.breadcrumbs.create'),
|
|
56
|
-
href:
|
|
60
|
+
href: getAdminRoutePath('collections', collection, 'create'),
|
|
57
61
|
},
|
|
58
62
|
]}
|
|
59
63
|
/>
|
|
@@ -19,6 +19,7 @@ import { BreadcrumbsClient } from '../admin-shell/chrome/breadcrumbs/breadcrumbs
|
|
|
19
19
|
import { useNavigate } from '../admin-shell/chrome/loose-router.js'
|
|
20
20
|
import { EditView } from '../admin-shell/collections/edit.js'
|
|
21
21
|
import { getCollectionDocument } from '../server-fns/collections/index.js'
|
|
22
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
22
23
|
import { getContentLocaleRouteConfig } from './get-content-locale-route-config.js'
|
|
23
24
|
|
|
24
25
|
const searchSchema = z.object({
|
|
@@ -116,11 +117,14 @@ export function createCollectionEditRoute(path: string) {
|
|
|
116
117
|
<>
|
|
117
118
|
<BreadcrumbsClient
|
|
118
119
|
breadcrumbs={[
|
|
119
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
120
|
-
{
|
|
120
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
121
|
+
{
|
|
122
|
+
label: collectionDef.labels.plural,
|
|
123
|
+
href: getAdminRoutePath('collections', collection),
|
|
124
|
+
},
|
|
121
125
|
{
|
|
122
126
|
label: t('common.actions.edit'),
|
|
123
|
-
href:
|
|
127
|
+
href: getAdminRoutePath('collections', collection, id),
|
|
124
128
|
},
|
|
125
129
|
]}
|
|
126
130
|
/>
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
getCollectionDocumentAuditLog,
|
|
25
25
|
getCollectionDocumentHistory,
|
|
26
26
|
} from '../server-fns/collections/index.js'
|
|
27
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
27
28
|
import { getContentLocaleRouteConfig } from './get-content-locale-route-config.js'
|
|
28
29
|
|
|
29
30
|
const searchSchema = z.object({
|
|
@@ -119,15 +120,18 @@ export function createCollectionHistoryRoute(path: string) {
|
|
|
119
120
|
<>
|
|
120
121
|
<BreadcrumbsClient
|
|
121
122
|
breadcrumbs={[
|
|
122
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
123
|
-
{
|
|
123
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
124
|
+
{
|
|
125
|
+
label: collectionDef.labels.plural,
|
|
126
|
+
href: getAdminRoutePath('collections', collection),
|
|
127
|
+
},
|
|
124
128
|
{
|
|
125
129
|
label: t('common.actions.edit'),
|
|
126
|
-
href:
|
|
130
|
+
href: getAdminRoutePath('collections', collection, Route.useParams().id),
|
|
127
131
|
},
|
|
128
132
|
{
|
|
129
133
|
label: t('collections.breadcrumbs.history'),
|
|
130
|
-
href:
|
|
134
|
+
href: getAdminRoutePath('collections', collection, Route.useParams().id, 'history'),
|
|
131
135
|
},
|
|
132
136
|
]}
|
|
133
137
|
/>
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
placeTreeNode,
|
|
31
31
|
reorderCollectionDocument,
|
|
32
32
|
} from '../server-fns/collections/index.js'
|
|
33
|
+
import { getAdminRoutePath } from './admin-path.js'
|
|
33
34
|
import type { CollectionTreeRow } from '../server-fns/collections/tree.js'
|
|
34
35
|
|
|
35
36
|
const searchSchema = z.object({
|
|
@@ -167,10 +168,10 @@ export function createCollectionListRoute(path: string) {
|
|
|
167
168
|
<>
|
|
168
169
|
<BreadcrumbsClient
|
|
169
170
|
breadcrumbs={[
|
|
170
|
-
{ label: t('chrome.menu.dashboard'), href:
|
|
171
|
+
{ label: t('chrome.menu.dashboard'), href: getAdminRoutePath() },
|
|
171
172
|
{
|
|
172
173
|
label: data.included.collection.labels.plural,
|
|
173
|
-
href:
|
|
174
|
+
href: getAdminRoutePath('collections', collection),
|
|
174
175
|
},
|
|
175
176
|
]}
|
|
176
177
|
/>
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
/**
|
|
10
10
|
* Sign-in page route factory.
|
|
11
11
|
*
|
|
12
|
-
* Lives outside the
|
|
12
|
+
* Lives outside the configured admin subtree so the authenticated-admin layout
|
|
13
13
|
* (and its `beforeLoad` guard) does not apply. Preserves a `callbackUrl`
|
|
14
14
|
* query param so sign-ins triggered by the admin guard redirect back to
|
|
15
15
|
* the originally-requested path.
|
|
@@ -21,6 +21,7 @@ import type { LocaleCode } from '@byline/i18n'
|
|
|
21
21
|
|
|
22
22
|
import { SignInPage } from '../admin-shell/chrome/sign-in-page.js'
|
|
23
23
|
import { getActiveLocaleFn } from '../server-fns/i18n/index.js'
|
|
24
|
+
import { resolveAdminCallbackPath, resolveAdminSignInRedirect } from './admin-path.js'
|
|
24
25
|
|
|
25
26
|
interface SignInSearch {
|
|
26
27
|
callbackUrl?: string
|
|
@@ -30,7 +31,7 @@ export function createSignInRoute(path: string) {
|
|
|
30
31
|
// biome-ignore lint/suspicious/noExplicitAny: dynamic path bypasses route-tree typing
|
|
31
32
|
const Route: any = createFileRoute(path as never)({
|
|
32
33
|
validateSearch: (search: Record<string, unknown>): SignInSearch => {
|
|
33
|
-
const callbackUrl =
|
|
34
|
+
const callbackUrl = resolveAdminCallbackPath(search.callbackUrl)
|
|
34
35
|
return { callbackUrl }
|
|
35
36
|
},
|
|
36
37
|
beforeLoad: async () => {
|
|
@@ -45,7 +46,12 @@ export function createSignInRoute(path: string) {
|
|
|
45
46
|
component: function SignInRouteComponent() {
|
|
46
47
|
const { callbackUrl } = Route.useSearch() as SignInSearch
|
|
47
48
|
const { activeLocale } = Route.useRouteContext() as { activeLocale: LocaleCode }
|
|
48
|
-
return
|
|
49
|
+
return (
|
|
50
|
+
<SignInPage
|
|
51
|
+
redirectTo={resolveAdminSignInRedirect(callbackUrl)}
|
|
52
|
+
activeLocale={activeLocale}
|
|
53
|
+
/>
|
|
54
|
+
)
|
|
49
55
|
},
|
|
50
56
|
})
|
|
51
57
|
return Route
|
package/src/routes/index.ts
CHANGED
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
export {
|
|
10
|
+
getAdminRouteId,
|
|
11
|
+
getAdminRoutePath,
|
|
12
|
+
isAdminRoutePathActive,
|
|
13
|
+
isRoutePathWithin,
|
|
14
|
+
resolveAdminCallbackPath,
|
|
15
|
+
resolveAdminSignInRedirect,
|
|
16
|
+
} from './admin-path.js'
|
|
17
|
+
export { configureSignInRoutePath, getSignInRoutePath } from './sign-in-path.js'
|
|
18
|
+
|
|
9
19
|
/**
|
|
10
20
|
* Route factories for the Byline admin UI on TanStack Start.
|
|
11
21
|
*
|
|
@@ -14,6 +24,7 @@
|
|
|
14
24
|
* `createFileRoute(path)({...})`. The host's route file collapses to:
|
|
15
25
|
*
|
|
16
26
|
* import { createCollectionListRoute } from '@byline/host-tanstack-start/routes'
|
|
27
|
+
* // Default admin mount example:
|
|
17
28
|
* export const Route = createCollectionListRoute('/_byline/admin/collections/$collection/')
|
|
18
29
|
*
|
|
19
30
|
* Routes that need host-specific data (i18n bridge component, content
|