rimecms 0.31.3 → 0.31.5
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/README.md +6 -207
- package/dist/adapter-sqlite/auth.server.d.ts +1 -1
- package/dist/adapter-sqlite/generate-schema/templates.server.d.ts +2 -2
- package/dist/adapter-sqlite/generate-schema/templates.server.js +3 -1
- package/dist/adapter-sqlite/generate-schema/write.server.js +7 -3
- package/dist/adapter-sqlite/transform.server.js +1 -1
- package/dist/core/collections/upload/util/path.d.ts +2 -1
- package/dist/core/collections/upload/util/path.js +3 -3
- package/dist/core/config/auth/better-auth-hooks.server.d.ts +1 -1
- package/dist/core/config/auth/better-auth-hooks.server.js +1 -2
- package/dist/core/config/auth/better-auth-permissions.d.ts +27 -67
- package/dist/core/config/auth/better-auth.server.d.ts +649 -481
- package/dist/core/config/auth/better-auth.server.js +2 -1
- package/dist/core/config/server/write.server.js +5 -0
- package/dist/core/constant.d.ts +10 -0
- package/dist/core/constant.js +22 -0
- package/dist/core/dev/cli/build/templates.js +2 -0
- package/dist/core/dev/cli/clear/index.server.js +5 -5
- package/dist/core/dev/cli/init/index.server.js +15 -28
- package/dist/core/dev/cli/init/templates.d.ts +14 -0
- package/dist/core/dev/cli/init/templates.js +58 -3
- package/dist/core/dev/constants.d.ts +20 -7
- package/dist/core/dev/constants.js +58 -10
- package/dist/core/dev/generate/routes/common.server.js +36 -33
- package/dist/core/dev/generate/routes/index.server.d.ts +6 -3
- package/dist/core/dev/generate/routes/index.server.js +13 -7
- package/dist/core/dev/generate/routes/util.server.d.ts +14 -6
- package/dist/core/dev/generate/routes/util.server.js +17 -6
- package/dist/core/dev/generate/sanitize/index.server.js +3 -3
- package/dist/core/dev/generate/types/index.server.js +3 -2
- package/dist/core/dev/vite/index.server.js +4 -8
- package/dist/core/ensure.server.d.ts +2 -2
- package/dist/core/ensure.server.js +17 -8
- package/dist/core/handlers/auth.server.js +18 -11
- package/dist/core/handlers/routes.server.d.ts +1 -1
- package/dist/core/handlers/routes.server.js +4 -3
- package/dist/core/operations/hooks/before-read/populate-url.server.js +2 -1
- package/dist/core/plugins/cache/handler.server.js +1 -1
- package/dist/core/rime.server.d.ts +1302 -966
- package/dist/core/rime.server.js +6 -0
- package/dist/fields/number/component/number.css +0 -2
- package/dist/fields/relation/component/upload/upload.css +9 -0
- package/dist/fields/rich-text/component/drag-handle/drag-handle.css +0 -1
- package/dist/fields/rich-text/component/styles/rich-text.css +7 -0
- package/dist/fields/rich-text/core/extensions/drag-handle/helpers/findNextElementFromCursor.d.ts +1 -1
- package/dist/fields/rich-text/core/svelte/node-view-wrapper.svelte.d.ts +12 -5
- package/dist/panel/components/sections/collection/header/SelectUI.svelte.d.ts +3 -3
- package/dist/panel/components/sections/collection/list/row/Row.svelte +2 -1
- package/dist/panel/components/sections/collection/tree/CollectionTreeNode.svelte +2 -1
- package/dist/panel/components/sections/document/AuthAPIKeyDialog.svelte.d.ts +3 -3
- package/dist/panel/components/sections/document/AuthFooter.svelte +2 -1
- package/dist/panel/components/sections/document/Versions.svelte +1 -1
- package/dist/panel/components/sections/live/Consumer.svelte.d.ts +11 -4
- package/dist/panel/components/sections/live/LiveEdit.svelte.d.ts +13 -6
- package/dist/panel/components/sections/live/LiveFloatingUI.svelte.d.ts +3 -3
- package/dist/panel/components/ui/calendar/calendar.css +0 -11
- package/dist/panel/components/ui/card-resource/card-resource.svelte +2 -1
- package/dist/panel/components/ui/command/command-group.css +0 -1
- package/dist/panel/components/ui/label/label.css +1 -0
- package/dist/panel/components/ui/nav/Nav.svelte +1 -1
- package/dist/panel/components/ui/nav/NavItem.svelte +3 -2
- package/dist/panel/components/ui/nav/UserButton.svelte +3 -3
- package/dist/panel/components/ui/radio-group/radio-group.css +0 -2
- package/dist/panel/components/ui/scroll-area/scroll-area.css +4 -0
- package/dist/panel/components/ui/sheet/sheet.css +16 -0
- package/dist/panel/components/ui/tabs/tabs.css +0 -7
- package/dist/panel/context/documentForm.svelte.js +5 -6
- package/dist/panel/navigation.d.ts +7 -2
- package/dist/panel/navigation.js +11 -6
- package/dist/panel/pages/area/actions.server.js +4 -3
- package/dist/panel/pages/area/load.server.js +3 -2
- package/dist/panel/pages/auth/forgot-password/ForgotPassword.svelte +2 -1
- package/dist/panel/pages/auth/reset-password/ResetPassword.svelte +2 -1
- package/dist/panel/pages/auth/sign-in/SignIn.svelte +3 -2
- package/dist/panel/pages/auth/sign-in/actions.server.js +2 -2
- package/dist/panel/pages/auth/sign-in/load.server.d.ts +1 -1
- package/dist/panel/pages/auth/sign-in/load.server.js +2 -2
- package/dist/panel/pages/collection/load.server.js +9 -4
- package/dist/panel/pages/collection-document/actions.server.js +5 -3
- package/dist/panel/pages/collection-document/load.server.js +10 -6
- package/dist/panel/pages/dashboard/load.server.js +5 -4
- package/dist/panel/pages/live/Live.svelte +3 -7
- package/dist/panel/style/index.css +0 -6
- package/dist/panel/util/auth.js +1 -1
- package/dist/panel/util/url.d.ts +30 -1
- package/dist/panel/util/url.js +36 -2
- package/package.json +8 -6
- package/dist/panel/components/ui/dropdown-menu/index.d.ts +0 -23
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
/**************************************/
|
|
7
|
-
|
|
8
7
|
/* Trigger
|
|
9
8
|
/**************************************/
|
|
10
9
|
|
|
@@ -33,11 +32,9 @@
|
|
|
33
32
|
.rz-tabs-trigger[data-state='active']:not([data-error='true']) {
|
|
34
33
|
color: hsl(var(--rz-color-fg));
|
|
35
34
|
}
|
|
36
|
-
|
|
37
35
|
.rz-tabs-trigger[data-error='true'] {
|
|
38
36
|
color: hsl(var(--rz-color-alert) / 1);
|
|
39
37
|
}
|
|
40
|
-
|
|
41
38
|
.rz-tabs-trigger[data-state='inactive'] {
|
|
42
39
|
opacity: 0.8;
|
|
43
40
|
}
|
|
@@ -49,21 +46,18 @@
|
|
|
49
46
|
0 0 0 calc(var(--rz-ring-offset, 0px) + 1px) hsl(var(--rz-color-ring) / var(--rz-ring-opacity, 1));
|
|
50
47
|
outline: none;
|
|
51
48
|
}
|
|
52
|
-
|
|
53
49
|
.rz-tabs-trigger:disabled {
|
|
54
50
|
pointer-events: none;
|
|
55
51
|
opacity: 0.5;
|
|
56
52
|
}
|
|
57
53
|
|
|
58
54
|
/**************************************/
|
|
59
|
-
|
|
60
55
|
/* Content
|
|
61
56
|
/**************************************/
|
|
62
57
|
|
|
63
58
|
.rz-tabs-content {
|
|
64
59
|
margin-top: var(--rz-size-2);
|
|
65
60
|
}
|
|
66
|
-
|
|
67
61
|
.rz-tabs-content:focus-visible {
|
|
68
62
|
outline: none;
|
|
69
63
|
border-radius: var(--rz-radius-sm);
|
|
@@ -75,7 +69,6 @@
|
|
|
75
69
|
}
|
|
76
70
|
|
|
77
71
|
/**************************************/
|
|
78
|
-
|
|
79
72
|
/* List
|
|
80
73
|
/**************************************/
|
|
81
74
|
|
|
@@ -4,6 +4,7 @@ import { PARAMS, VERSIONS_STATUS } from '../../core/constant.js';
|
|
|
4
4
|
import { getFieldAtPath } from '../../core/fields/util.js';
|
|
5
5
|
import { buildConfigMap } from '../../core/operations/configMap/index.js';
|
|
6
6
|
import { isJSONContent, richTextJSONToText } from '../../fields/rich-text/index.js';
|
|
7
|
+
import { panelUrl } from '../util/url.js';
|
|
7
8
|
import { normalizeFieldPath } from '../../util/doc.js';
|
|
8
9
|
import { apiUrl, random } from '../../util/index.js';
|
|
9
10
|
import { isObjectLiteral, omit } from '../../util/object.js';
|
|
@@ -590,12 +591,10 @@ function createDocumentFormState({ initial, config, readOnly, key, onNestedDocum
|
|
|
590
591
|
* This function constructs the appropriate URL to which the form data will be submitted.
|
|
591
592
|
*/
|
|
592
593
|
const buildPanelActionUrl = () => {
|
|
593
|
-
// Start with the base URI for the panel
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
panelUri += `/${initial.id}`;
|
|
598
|
-
}
|
|
594
|
+
// Start with the base URI for the panel, adding the item ID if we're updating a collection doc
|
|
595
|
+
const panelUri = operation === 'update' && initial._prototype === 'collection' && initial.id
|
|
596
|
+
? panelUrl(config.kebab, initial.id)
|
|
597
|
+
: panelUrl(config.kebab);
|
|
599
598
|
// Determine the appropriate action based on whether we're creating or updating
|
|
600
599
|
const actionSuffix = operation === 'create' ? '/create?/create' : '?/update';
|
|
601
600
|
// Add a redirect parameter if we're in a nested form ex: relation creation
|
|
@@ -3,11 +3,16 @@ import type { BuildConfig } from '../core/config/server/index.server.js';
|
|
|
3
3
|
import type { Config } from '../types.js';
|
|
4
4
|
import type { Dic } from '../util/types.js';
|
|
5
5
|
/**
|
|
6
|
-
* Builds navigation structure based on config and user permissions
|
|
6
|
+
* Builds navigation structure based on config and user permissions. Called
|
|
7
|
+
* directly from the routes handle hook, before resolve() — page.params isn't
|
|
8
|
+
* populated yet at that point, so the panel segment is passed in explicitly
|
|
9
|
+
* (from the same event.params.panel the caller already has) rather than
|
|
10
|
+
* relying on panelUrl()'s page-based default.
|
|
7
11
|
* @param config - Compiled configuration object
|
|
8
12
|
* @param user - Current user object (optional)
|
|
13
|
+
* @param panelSegment - The resolved [panel=panel] segment for this request
|
|
9
14
|
* @returns Dictionary of navigation groups
|
|
10
15
|
*/
|
|
11
|
-
declare const buildNavigation: <C extends Config>(config: BuildConfig<C>, user: User | undefined) => Dic;
|
|
16
|
+
declare const buildNavigation: <C extends Config>(config: BuildConfig<C>, user: User | undefined, panelSegment: string | undefined) => Dic;
|
|
12
17
|
export default buildNavigation;
|
|
13
18
|
export type Navigation = ReturnType<typeof buildNavigation>;
|
package/dist/panel/navigation.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { panelUrlFor } from './util/url.js';
|
|
2
2
|
/**
|
|
3
|
-
* Builds navigation structure based on config and user permissions
|
|
3
|
+
* Builds navigation structure based on config and user permissions. Called
|
|
4
|
+
* directly from the routes handle hook, before resolve() — page.params isn't
|
|
5
|
+
* populated yet at that point, so the panel segment is passed in explicitly
|
|
6
|
+
* (from the same event.params.panel the caller already has) rather than
|
|
7
|
+
* relying on panelUrl()'s page-based default.
|
|
4
8
|
* @param config - Compiled configuration object
|
|
5
9
|
* @param user - Current user object (optional)
|
|
10
|
+
* @param panelSegment - The resolved [panel=panel] segment for this request
|
|
6
11
|
* @returns Dictionary of navigation groups
|
|
7
12
|
*/
|
|
8
|
-
const buildNavigation = (config, user) => {
|
|
13
|
+
const buildNavigation = (config, user, panelSegment) => {
|
|
9
14
|
const groups = {};
|
|
10
15
|
/**
|
|
11
16
|
* Adds a route to the appropriate navigation group
|
|
@@ -29,7 +34,7 @@ const buildNavigation = (config, user) => {
|
|
|
29
34
|
const route = {
|
|
30
35
|
title: collection.label.plural,
|
|
31
36
|
icon: collection.slug,
|
|
32
|
-
url:
|
|
37
|
+
url: panelUrlFor(panelSegment, collection.kebab)
|
|
33
38
|
};
|
|
34
39
|
addRouteToGroup(route, (collection.panel && collection.panel?.group) || 'collections');
|
|
35
40
|
}
|
|
@@ -40,7 +45,7 @@ const buildNavigation = (config, user) => {
|
|
|
40
45
|
const route = {
|
|
41
46
|
title: area.label,
|
|
42
47
|
icon: area.slug,
|
|
43
|
-
url:
|
|
48
|
+
url: panelUrlFor(panelSegment, area.kebab)
|
|
44
49
|
};
|
|
45
50
|
addRouteToGroup(route, (area.panel && area.panel?.group) || 'areas');
|
|
46
51
|
}
|
|
@@ -50,7 +55,7 @@ const buildNavigation = (config, user) => {
|
|
|
50
55
|
const route = {
|
|
51
56
|
title: routeConfig.label,
|
|
52
57
|
icon: `custom-${routePath}`,
|
|
53
|
-
url:
|
|
58
|
+
url: panelUrlFor(panelSegment, routePath)
|
|
54
59
|
};
|
|
55
60
|
addRouteToGroup(route, routeConfig.group);
|
|
56
61
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PARAMS } from '../../../core/constant.js';
|
|
2
2
|
import { ERROR_CONTEXT, handleError } from '../../../core/errors/handler.server.js';
|
|
3
3
|
import { extractData } from '../../../core/operations/extract-data.server.js';
|
|
4
|
-
import {
|
|
4
|
+
import { panelUrlFor } from '../../util/url.js';
|
|
5
5
|
import { trycatch } from '../../../util/function.js';
|
|
6
6
|
import { toKebabCase } from '../../../util/string.js';
|
|
7
7
|
import { redirect } from '@sveltejs/kit';
|
|
@@ -10,6 +10,7 @@ export const areaFormActions = {
|
|
|
10
10
|
update: async (event) => {
|
|
11
11
|
const { rime, locale } = event.locals;
|
|
12
12
|
const slug = (event.params.slug || '');
|
|
13
|
+
const panelSegment = event.params.panel;
|
|
13
14
|
const versionId = event.url.searchParams.get(PARAMS.VERSION_ID) || undefined;
|
|
14
15
|
const draft = event.url.searchParams.get(PARAMS.DRAFT) === 'true';
|
|
15
16
|
const [extractError, data] = await trycatch(() => extractData(event.request));
|
|
@@ -28,10 +29,10 @@ export const areaFormActions = {
|
|
|
28
29
|
if (draft && 'versionId' in document) {
|
|
29
30
|
const referer = event.request.headers.get('referer');
|
|
30
31
|
if (referer && referer.includes('/versions')) {
|
|
31
|
-
return redirect(303, `${
|
|
32
|
+
return redirect(303, `${panelUrlFor(panelSegment, toKebabCase(slug))}/versions?versionId=${document.versionId}`);
|
|
32
33
|
}
|
|
33
34
|
else {
|
|
34
|
-
return redirect(303, `${
|
|
35
|
+
return redirect(303, `${panelUrlFor(panelSegment, toKebabCase(slug))}?versionId=${document.versionId}`);
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
return { document, message: t__('common.doc_updated') };
|
|
@@ -2,7 +2,7 @@ import { PARAMS } from '../../../core/constant.js';
|
|
|
2
2
|
import { ERROR_CONTEXT, handleError } from '../../../core/errors/handler.server.js';
|
|
3
3
|
import { RimeError } from '../../../core/errors/index.js';
|
|
4
4
|
import { withVersionsSuffix } from '../../../core/naming.js';
|
|
5
|
-
import {
|
|
5
|
+
import { panelUrlFor } from '../../util/url.js';
|
|
6
6
|
import { trycatch } from '../../../util/function.js';
|
|
7
7
|
import { apiUrl } from '../../../util/index.js';
|
|
8
8
|
import { toKebabCase } from '../../../util/string.js';
|
|
@@ -11,6 +11,7 @@ export async function areaLoad(event, withVersions) {
|
|
|
11
11
|
const { locals, url, fetch } = event;
|
|
12
12
|
const { rime, locale } = locals;
|
|
13
13
|
const slug = (event.params.slug || '');
|
|
14
|
+
const panelSegment = event.params.panel;
|
|
14
15
|
const area = rime.area(slug);
|
|
15
16
|
const authorizedRead = area.config.access.read(locals.user, {});
|
|
16
17
|
const authorizedUpdate = area.config.access.update(locals.user, {});
|
|
@@ -18,7 +19,7 @@ export async function areaLoad(event, withVersions) {
|
|
|
18
19
|
throw handleError(new RimeError(RimeError.UNAUTHORIZED), { context: ERROR_CONTEXT.LOAD });
|
|
19
20
|
}
|
|
20
21
|
const aria = [
|
|
21
|
-
{ title: 'Dashboard', icon: 'dashboard', url:
|
|
22
|
+
{ title: 'Dashboard', icon: 'dashboard', url: panelUrlFor(panelSegment) },
|
|
22
23
|
{ title: area.config.label }
|
|
23
24
|
];
|
|
24
25
|
const versionId = url.searchParams.get(PARAMS.VERSION_ID) || undefined;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { setFormContext } from '../../../context/form.svelte';
|
|
7
7
|
import { emailField } from '../fields.js';
|
|
8
8
|
import { authClient } from '../../../util/auth';
|
|
9
|
+
import { panelUrl } from '../../../util/url.js';
|
|
9
10
|
import { toast } from 'svelte-sonner';
|
|
10
11
|
|
|
11
12
|
type Props = {
|
|
@@ -29,7 +30,7 @@
|
|
|
29
30
|
async function sendResetPasswordMail() {
|
|
30
31
|
const { data, error } = await authClient.requestPasswordReset({
|
|
31
32
|
email: context.values.email,
|
|
32
|
-
redirectTo:
|
|
33
|
+
redirectTo: panelUrl('reset-password')
|
|
33
34
|
});
|
|
34
35
|
if (error && error.message) {
|
|
35
36
|
toast.error(error.message);
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { setFormContext } from '../../../context/form.svelte';
|
|
7
7
|
import { confirmPasswordField, passwordField } from '../fields.js';
|
|
8
8
|
import { authClient } from '../../../util/auth';
|
|
9
|
+
import { panelUrl } from '../../../util/url.js';
|
|
9
10
|
import { toast } from 'svelte-sonner';
|
|
10
11
|
|
|
11
12
|
let success = $state(false);
|
|
@@ -42,7 +43,7 @@
|
|
|
42
43
|
<AuthForm image={data.image} title={t__('common.resetPassword')}>
|
|
43
44
|
{#if success}
|
|
44
45
|
<p>{t__('common.reset_password_success')}</p>
|
|
45
|
-
<Button size="xl" href=
|
|
46
|
+
<Button size="xl" href={panelUrl('sign-in')}>{t__('common.signin')}</Button>
|
|
46
47
|
{:else}
|
|
47
48
|
<Text type="password" config={passwordConfig} {form} />
|
|
48
49
|
<Text type="password" config={confirmPasswordConfig} {form} />
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import SpinLoader from '../../../components/ui/spin-loader/SpinLoader.svelte';
|
|
9
9
|
import { setFormContext } from '../../../context/form.svelte';
|
|
10
10
|
import { emailField, passwordField } from '../fields.js';
|
|
11
|
+
import { panelPath, panelUrl } from '../../../util/url.js';
|
|
11
12
|
import type { FormErrors } from '../../../../types.js';
|
|
12
13
|
import { KeyRound } from '@lucide/svelte';
|
|
13
14
|
import { toast } from 'svelte-sonner';
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
|
|
35
36
|
<AuthForm image={data.image} title={t__('common.signin')}>
|
|
36
37
|
{#if context.status !== 429}
|
|
37
|
-
<form method="POST" action=
|
|
38
|
+
<form method="POST" action={panelPath('sign-in')} use:enhance={context.enhance}>
|
|
38
39
|
<Email config={emailField} form={context} />
|
|
39
40
|
<Text
|
|
40
41
|
type="password"
|
|
@@ -50,7 +51,7 @@
|
|
|
50
51
|
</Button>
|
|
51
52
|
|
|
52
53
|
{#if data.forgotPasswordEnabled}
|
|
53
|
-
<Button variant="link" href=
|
|
54
|
+
<Button variant="link" href={panelUrl('forgot-password')}>
|
|
54
55
|
{t__('common.forgotPassword')}
|
|
55
56
|
</Button>
|
|
56
57
|
{/if}
|
|
@@ -3,7 +3,7 @@ import { handleError } from '../../../../core/errors/handler.server.js';
|
|
|
3
3
|
import { RimeFormError } from '../../../../core/errors/index.js';
|
|
4
4
|
import { redirect } from '@sveltejs/kit';
|
|
5
5
|
export const signInActions = {
|
|
6
|
-
default: async ({ cookies, request, locals }) => {
|
|
6
|
+
default: async ({ cookies, request, locals, params }) => {
|
|
7
7
|
const { rime } = locals;
|
|
8
8
|
const data = await request.formData();
|
|
9
9
|
const email = data.get('email')?.toString() || '';
|
|
@@ -36,6 +36,6 @@ export const signInActions = {
|
|
|
36
36
|
secure: !dev
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
|
-
throw redirect(302,
|
|
39
|
+
throw redirect(302, `/${params.panel}`);
|
|
40
40
|
}
|
|
41
41
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ServerLoadEvent } from '@sveltejs/kit';
|
|
2
|
-
export declare const signInLoad: ({ locals }: ServerLoadEvent) => Promise<{
|
|
2
|
+
export declare const signInLoad: ({ locals, params }: ServerLoadEvent) => Promise<{
|
|
3
3
|
forgotPasswordEnabled: boolean;
|
|
4
4
|
image: string | null;
|
|
5
5
|
form: {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { redirect } from '@sveltejs/kit';
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
export const signInLoad = async ({ locals }) => {
|
|
4
|
+
export const signInLoad = async ({ locals, params }) => {
|
|
5
5
|
const { session, rime } = locals;
|
|
6
6
|
const imageExist = existsSync(path.join(process.cwd(), 'static', 'panel', 'panel.jpg'));
|
|
7
7
|
if (session) {
|
|
8
|
-
throw redirect(302,
|
|
8
|
+
throw redirect(302, `/${params.panel}`);
|
|
9
9
|
}
|
|
10
10
|
else {
|
|
11
11
|
return {
|
|
@@ -4,7 +4,7 @@ import { handleError } from '../../../core/errors/handler.server.js';
|
|
|
4
4
|
import { RimeError } from '../../../core/errors/index.js';
|
|
5
5
|
import { logger } from '../../../core/logger/index.server.js';
|
|
6
6
|
import { withDirectoriesSuffix } from '../../../core/naming.js';
|
|
7
|
-
import {
|
|
7
|
+
import { panelUrlFor } from '../../util/url.js';
|
|
8
8
|
import { trycatch } from '../../../util/function.js';
|
|
9
9
|
import { redirect } from '@sveltejs/kit';
|
|
10
10
|
/**
|
|
@@ -13,6 +13,7 @@ import { redirect } from '@sveltejs/kit';
|
|
|
13
13
|
export async function collectionLoad(event) {
|
|
14
14
|
//
|
|
15
15
|
const { rime, locale, user } = event.locals;
|
|
16
|
+
const panelSegment = event.params.panel;
|
|
16
17
|
const slug = event.params.slug || '';
|
|
17
18
|
if (!rime.config.isCollection(slug)) {
|
|
18
19
|
throw handleError(new RimeError(RimeError.NOT_FOUND), { context: 'load' });
|
|
@@ -24,7 +25,7 @@ export async function collectionLoad(event) {
|
|
|
24
25
|
draft: true
|
|
25
26
|
});
|
|
26
27
|
let aria = [
|
|
27
|
-
{ title: 'Dashboard', url:
|
|
28
|
+
{ title: 'Dashboard', url: panelUrlFor(panelSegment) },
|
|
28
29
|
{ title: collection.config.label.plural }
|
|
29
30
|
];
|
|
30
31
|
let data = {
|
|
@@ -68,10 +69,14 @@ export async function collectionLoad(event) {
|
|
|
68
69
|
parentDirectory = result;
|
|
69
70
|
const collectionAria = {
|
|
70
71
|
title: collection.config.label.plural,
|
|
71
|
-
url:
|
|
72
|
+
url: panelUrlFor(panelSegment, collection.config.kebab)
|
|
72
73
|
};
|
|
73
74
|
aria = [...aria].slice(0, -1);
|
|
74
|
-
aria = [
|
|
75
|
+
aria = [
|
|
76
|
+
...aria,
|
|
77
|
+
collectionAria,
|
|
78
|
+
...buildUploadAria({ path: currentDirectoryPath, slug, panelSegment })
|
|
79
|
+
];
|
|
75
80
|
data.aria = removePathFromLastAria(aria);
|
|
76
81
|
}
|
|
77
82
|
data = {
|
|
@@ -2,7 +2,7 @@ import { PARAMS, UPLOAD_PATH } from '../../../core/constant.js';
|
|
|
2
2
|
import { ERROR_CONTEXT, handleError } from '../../../core/errors/handler.server.js';
|
|
3
3
|
import { RimeError } from '../../../core/errors/index.js';
|
|
4
4
|
import { extractData } from '../../../core/operations/extract-data.server.js';
|
|
5
|
-
import {
|
|
5
|
+
import { panelUrlFor } from '../../util/url.js';
|
|
6
6
|
import { trycatch } from '../../../util/function.js';
|
|
7
7
|
import { toKebabCase } from '../../../util/string.js';
|
|
8
8
|
import {} from '@sveltejs/kit';
|
|
@@ -15,6 +15,7 @@ export const collectionFormActions = {
|
|
|
15
15
|
*/
|
|
16
16
|
create: async (event) => {
|
|
17
17
|
const { rime, locale } = event.locals;
|
|
18
|
+
const panelSegment = event.params.panel;
|
|
18
19
|
const slug = event.params.slug;
|
|
19
20
|
if (!rime.config.isCollection(slug)) {
|
|
20
21
|
throw handleError(new RimeError(RimeError.NOT_FOUND), { context: ERROR_CONTEXT.ACTION });
|
|
@@ -44,7 +45,7 @@ export const collectionFormActions = {
|
|
|
44
45
|
const params = collection.config.upload
|
|
45
46
|
? `?${PARAMS.UPLOAD_PATH}=${data._path || UPLOAD_PATH.ROOT_NAME}`
|
|
46
47
|
: '';
|
|
47
|
-
const redirectUrl = `${
|
|
48
|
+
const redirectUrl = `${panelUrlFor(panelSegment, toKebabCase(slug), document.id)}${params}`;
|
|
48
49
|
return {
|
|
49
50
|
redirectUrl,
|
|
50
51
|
document,
|
|
@@ -58,6 +59,7 @@ export const collectionFormActions = {
|
|
|
58
59
|
*/
|
|
59
60
|
update: async (event) => {
|
|
60
61
|
const { rime, locale } = event.locals;
|
|
62
|
+
const panelSegment = event.params.panel;
|
|
61
63
|
const slug = event.params.slug || '';
|
|
62
64
|
const id = event.params.id || '';
|
|
63
65
|
const versionId = event.url.searchParams.get(PARAMS.VERSION_ID) || undefined;
|
|
@@ -83,7 +85,7 @@ export const collectionFormActions = {
|
|
|
83
85
|
return {
|
|
84
86
|
document,
|
|
85
87
|
message: t__('common.version_created'),
|
|
86
|
-
redirectUrl: `${
|
|
88
|
+
redirectUrl: `${panelUrlFor(panelSegment, toKebabCase(slug), document.id)}/versions?versionId=${document.versionId}`
|
|
87
89
|
};
|
|
88
90
|
}
|
|
89
91
|
return {
|
|
@@ -3,7 +3,7 @@ import { PARAMS, UPLOAD_PATH } from '../../../core/constant.js';
|
|
|
3
3
|
import { ERROR_CONTEXT, handleError } from '../../../core/errors/handler.server.js';
|
|
4
4
|
import { RimeError } from '../../../core/errors/index.js';
|
|
5
5
|
import { withVersionsSuffix } from '../../../core/naming.js';
|
|
6
|
-
import {
|
|
6
|
+
import { panelUrlFor } from '../../util/url.js';
|
|
7
7
|
import { trycatch } from '../../../util/function.js';
|
|
8
8
|
import { apiUrl } from '../../../util/index.js';
|
|
9
9
|
import { toKebabCase } from '../../../util/string.js';
|
|
@@ -16,6 +16,7 @@ export async function documentLoad(event, withVersion) {
|
|
|
16
16
|
const { locale, user, rime } = event.locals;
|
|
17
17
|
const { id } = event.params;
|
|
18
18
|
const slug = event.params.slug || '';
|
|
19
|
+
const panelSegment = event.params.panel;
|
|
19
20
|
if (!id)
|
|
20
21
|
throw error(404, 'Not found');
|
|
21
22
|
let doc;
|
|
@@ -55,22 +56,25 @@ export async function documentLoad(event, withVersion) {
|
|
|
55
56
|
let aria;
|
|
56
57
|
const collectionAria = {
|
|
57
58
|
title: collection.config.label.plural,
|
|
58
|
-
url:
|
|
59
|
+
url: panelUrlFor(panelSegment, collection.config.kebab)
|
|
59
60
|
};
|
|
60
61
|
if (collection.config.upload) {
|
|
61
62
|
const paramUploadPath = event.url.searchParams.get('uploadPath');
|
|
62
63
|
const currentDirectoryPath = paramUploadPath || UPLOAD_PATH.ROOT_NAME;
|
|
63
64
|
aria = [
|
|
64
|
-
{ title: 'Dashboard', icon: 'dashboard', url:
|
|
65
|
+
{ title: 'Dashboard', icon: 'dashboard', url: panelUrlFor(panelSegment) },
|
|
65
66
|
collectionAria,
|
|
66
|
-
...buildUploadAria({ path: currentDirectoryPath, slug }),
|
|
67
|
+
...buildUploadAria({ path: currentDirectoryPath, slug, panelSegment }),
|
|
67
68
|
{ title: undefined } // Will be populated by title context
|
|
68
69
|
];
|
|
69
70
|
}
|
|
70
71
|
else {
|
|
71
72
|
aria = [
|
|
72
|
-
{ title: 'Dashboard', icon: 'dashboard', url:
|
|
73
|
-
{
|
|
73
|
+
{ title: 'Dashboard', icon: 'dashboard', url: panelUrlFor(panelSegment) },
|
|
74
|
+
{
|
|
75
|
+
title: collection.config.label.plural,
|
|
76
|
+
url: panelUrlFor(panelSegment, collection.config.kebab)
|
|
77
|
+
},
|
|
74
78
|
{ title: undefined } // Will be populated by title context
|
|
75
79
|
];
|
|
76
80
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { panelUrlFor } from '../../util/url.js';
|
|
2
2
|
import { capitalize } from '../../../util/string.js';
|
|
3
3
|
export const dashboardLoad = async (event) => {
|
|
4
4
|
const { locale, user, rime } = event.locals;
|
|
5
|
+
const panelSegment = event.params.panel;
|
|
5
6
|
const entries = [];
|
|
6
7
|
const normalizedPanelConfig = (c) => {
|
|
7
8
|
const incomingConfig = c.panel || {};
|
|
@@ -21,7 +22,7 @@ export const dashboardLoad = async (event) => {
|
|
|
21
22
|
description: panelConfig.description || null,
|
|
22
23
|
slug: c.slug,
|
|
23
24
|
canCreate: user && c.access.create(user, {}),
|
|
24
|
-
link:
|
|
25
|
+
link: panelUrlFor(panelSegment, c.kebab),
|
|
25
26
|
titleSingular: c.label.singular,
|
|
26
27
|
title: c.label.plural,
|
|
27
28
|
layout: panelConfig.dashboard.layout
|
|
@@ -72,11 +73,11 @@ export const dashboardLoad = async (event) => {
|
|
|
72
73
|
prototype: 'area',
|
|
73
74
|
description: (area.panel && area.panel?.description) || null,
|
|
74
75
|
slug: area.slug,
|
|
75
|
-
link:
|
|
76
|
+
link: panelUrlFor(panelSegment, area.kebab),
|
|
76
77
|
title: area.label || capitalize(area.slug)
|
|
77
78
|
});
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
|
-
const aria = [{ title: 'Dashboard', icon: 'dashboard', url:
|
|
81
|
+
const aria = [{ title: 'Dashboard', icon: 'dashboard', url: panelUrlFor(panelSegment) }];
|
|
81
82
|
return { entries, aria };
|
|
82
83
|
};
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import { setLocaleContext } from '../../context/locale.svelte';
|
|
15
15
|
import { setTitleContext } from '../../context/title';
|
|
16
16
|
import { setUserContext } from '../../context/user.svelte';
|
|
17
|
+
import { panelPath } from '../../util/url.js';
|
|
17
18
|
import type { GenericDoc, User } from '../../../types';
|
|
18
19
|
import { trycatchFetch } from '../../../util/function';
|
|
19
20
|
import { snapshot } from '../../../util/state';
|
|
@@ -240,13 +241,8 @@
|
|
|
240
241
|
const slug = data.slug;
|
|
241
242
|
const id = data.doc.id;
|
|
242
243
|
|
|
243
|
-
// Start with the base URI for the panel
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
// Add the item ID to the URI if we're updating a collection doc
|
|
247
|
-
if (id) {
|
|
248
|
-
panelUri += `/${id}`;
|
|
249
|
-
}
|
|
244
|
+
// Start with the base URI for the panel, adding the item ID if present
|
|
245
|
+
const panelUri = id ? panelPath(toKebabCase(slug), id) : panelPath(toKebabCase(slug));
|
|
250
246
|
return goto(panelUri);
|
|
251
247
|
}
|
|
252
248
|
|
|
@@ -312,7 +312,6 @@ menu {
|
|
|
312
312
|
/*
|
|
313
313
|
Reset default styling for dialogs.
|
|
314
314
|
*/
|
|
315
|
-
|
|
316
315
|
dialog {
|
|
317
316
|
padding: 0;
|
|
318
317
|
}
|
|
@@ -353,7 +352,6 @@ button,
|
|
|
353
352
|
/*
|
|
354
353
|
Make sure disabled buttons don't get the pointer cursor.
|
|
355
354
|
*/
|
|
356
|
-
|
|
357
355
|
:disabled {
|
|
358
356
|
cursor: default;
|
|
359
357
|
}
|
|
@@ -387,11 +385,9 @@ video {
|
|
|
387
385
|
}
|
|
388
386
|
|
|
389
387
|
/* Make elements with the HTML hidden attribute stay hidden by default */
|
|
390
|
-
|
|
391
388
|
[hidden] {
|
|
392
389
|
display: none;
|
|
393
390
|
}
|
|
394
|
-
|
|
395
391
|
:root{
|
|
396
392
|
--ease-in-quad: cubic-bezier(.55, .085, .68, .53);
|
|
397
393
|
--ease-in-cubic: cubic-bezier(.550, .055, .675, .19);
|
|
@@ -414,7 +410,6 @@ video {
|
|
|
414
410
|
--ease-in-out-expo: cubic-bezier(1, 0, 0, 1);
|
|
415
411
|
--ease-in-out-circ: cubic-bezier(.785, .135, .15, .86);
|
|
416
412
|
}
|
|
417
|
-
|
|
418
413
|
:root {
|
|
419
414
|
--rz-size-0-5: 0.125rem;
|
|
420
415
|
--rz-size-1: 0.25rem;
|
|
@@ -458,7 +453,6 @@ video {
|
|
|
458
453
|
--rz-size-tablet: 48rem;
|
|
459
454
|
--rz-size-laptop: 64rem;
|
|
460
455
|
}
|
|
461
|
-
|
|
462
456
|
@font-face {
|
|
463
457
|
font-family: 'Lora';
|
|
464
458
|
src: url('/assets/panel/fonts/Lora-Italic-VariableFont_wght.ttf') format('truetype');
|
package/dist/panel/util/auth.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { apiKeyClient } from '@better-auth/api-key/client';
|
|
1
2
|
import { env } from '$env/dynamic/public';
|
|
2
|
-
import { apiKeyClient } from 'better-auth/client/plugins';
|
|
3
3
|
import { createAuthClient } from 'better-auth/svelte';
|
|
4
4
|
export const authClient = createAuthClient({
|
|
5
5
|
baseURL: env.PUBLIC_RIME_URL,
|
package/dist/panel/util/url.d.ts
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Build a panel url for given segments
|
|
2
|
+
* Build a panel url for given segments. Reads the current panel segment off
|
|
3
|
+
* the live matched route (page.params.panel, resolved by the [panel=panel]
|
|
4
|
+
* matcher) rather than from a public env var — the segment can be configured
|
|
5
|
+
* via RIME_PANEL_ROUTE to hide the panel from admin-path scanners, and that
|
|
6
|
+
* would be defeated by shipping it into every page's public env bundle.
|
|
7
|
+
*
|
|
8
|
+
* Only call this from actual component rendering (.svelte files, or a
|
|
9
|
+
* .svelte.ts context set up during component init) — page.params is bound to
|
|
10
|
+
* the current request via component context and SvelteKit throws if it's read
|
|
11
|
+
* anywhere else, including a +page.server.ts/+layout.server.ts load function
|
|
12
|
+
* or a form action, even though those also run server-side. Every load/action
|
|
13
|
+
* function in this codebase already receives an `event` with `event.params.panel`
|
|
14
|
+
* populated the same way — use panelUrlFor(event.params.panel, ...) there instead.
|
|
3
15
|
* @example
|
|
4
16
|
* panelUrl('some-collection') // -> http://localhost:5713/panel/some-collection
|
|
5
17
|
*/
|
|
6
18
|
export declare function panelUrl(...args: string[]): string;
|
|
19
|
+
/**
|
|
20
|
+
* Same as panelUrl(), but takes the panel segment explicitly instead of
|
|
21
|
+
* reading it off page.params — for any caller that isn't component rendering:
|
|
22
|
+
* every +page.server.ts/+layout.server.ts load function and form action
|
|
23
|
+
* (pass event.params.panel), plus the one hook-level caller that runs before
|
|
24
|
+
* resolve() (buildNavigation(), which gets it forwarded from routes.server.ts).
|
|
25
|
+
*/
|
|
26
|
+
export declare function panelUrlFor(panelSegment: string | undefined, ...args: string[]): string;
|
|
27
|
+
/**
|
|
28
|
+
* Same as panelUrl(), but returns a same-origin-relative path instead of an
|
|
29
|
+
* absolute URL — for goto()/form-action targets where PUBLIC_RIME_URL isn't
|
|
30
|
+
* guaranteed to match the actual origin the page was served from (a reverse
|
|
31
|
+
* proxy, differing dev host, etc). Everywhere else (real cross-page <a href>
|
|
32
|
+
* links, window.location.href, emailed reset links) stays absolute, matching
|
|
33
|
+
* apiUrl()'s existing convention.
|
|
34
|
+
*/
|
|
35
|
+
export declare function panelPath(...args: string[]): string;
|
package/dist/panel/util/url.js
CHANGED
|
@@ -1,9 +1,43 @@
|
|
|
1
|
+
import { page } from '$app/state';
|
|
1
2
|
import { env } from '$env/dynamic/public';
|
|
2
3
|
/**
|
|
3
|
-
* Build a panel url for given segments
|
|
4
|
+
* Build a panel url for given segments. Reads the current panel segment off
|
|
5
|
+
* the live matched route (page.params.panel, resolved by the [panel=panel]
|
|
6
|
+
* matcher) rather than from a public env var — the segment can be configured
|
|
7
|
+
* via RIME_PANEL_ROUTE to hide the panel from admin-path scanners, and that
|
|
8
|
+
* would be defeated by shipping it into every page's public env bundle.
|
|
9
|
+
*
|
|
10
|
+
* Only call this from actual component rendering (.svelte files, or a
|
|
11
|
+
* .svelte.ts context set up during component init) — page.params is bound to
|
|
12
|
+
* the current request via component context and SvelteKit throws if it's read
|
|
13
|
+
* anywhere else, including a +page.server.ts/+layout.server.ts load function
|
|
14
|
+
* or a form action, even though those also run server-side. Every load/action
|
|
15
|
+
* function in this codebase already receives an `event` with `event.params.panel`
|
|
16
|
+
* populated the same way — use panelUrlFor(event.params.panel, ...) there instead.
|
|
4
17
|
* @example
|
|
5
18
|
* panelUrl('some-collection') // -> http://localhost:5713/panel/some-collection
|
|
6
19
|
*/
|
|
7
20
|
export function panelUrl(...args) {
|
|
8
|
-
return
|
|
21
|
+
return panelUrlFor(page.params.panel, ...args);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Same as panelUrl(), but takes the panel segment explicitly instead of
|
|
25
|
+
* reading it off page.params — for any caller that isn't component rendering:
|
|
26
|
+
* every +page.server.ts/+layout.server.ts load function and form action
|
|
27
|
+
* (pass event.params.panel), plus the one hook-level caller that runs before
|
|
28
|
+
* resolve() (buildNavigation(), which gets it forwarded from routes.server.ts).
|
|
29
|
+
*/
|
|
30
|
+
export function panelUrlFor(panelSegment, ...args) {
|
|
31
|
+
return `${env.PUBLIC_RIME_URL}/${panelSegment}/${args.join('/')}`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Same as panelUrl(), but returns a same-origin-relative path instead of an
|
|
35
|
+
* absolute URL — for goto()/form-action targets where PUBLIC_RIME_URL isn't
|
|
36
|
+
* guaranteed to match the actual origin the page was served from (a reverse
|
|
37
|
+
* proxy, differing dev host, etc). Everywhere else (real cross-page <a href>
|
|
38
|
+
* links, window.location.href, emailed reset links) stays absolute, matching
|
|
39
|
+
* apiUrl()'s existing convention.
|
|
40
|
+
*/
|
|
41
|
+
export function panelPath(...args) {
|
|
42
|
+
return `/${page.params.panel}/${args.join('/')}`;
|
|
9
43
|
}
|