rimecms 0.31.4 → 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 +1 -1
- 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/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/init/index.server.js +2 -1
- package/dist/core/dev/cli/init/templates.d.ts +6 -4
- package/dist/core/dev/cli/init/templates.js +20 -9
- package/dist/core/dev/constants.d.ts +5 -0
- package/dist/core/dev/constants.js +9 -0
- package/dist/core/dev/generate/routes/common.server.js +33 -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/handlers/auth.server.js +16 -8
- 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/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/AuthFooter.svelte +2 -1
- package/dist/panel/components/sections/document/Versions.svelte +1 -1
- package/dist/panel/components/ui/card-resource/card-resource.svelte +2 -1
- 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/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/util/url.d.ts +30 -1
- package/dist/panel/util/url.js +36 -2
- package/package.json +1 -1
|
@@ -1,16 +1,24 @@
|
|
|
1
|
+
import { isPublicPanelAuthRoute } from '../constant.js';
|
|
1
2
|
import { RimeError } from '../errors/index.js';
|
|
2
3
|
import { error, redirect } from '@sveltejs/kit';
|
|
3
4
|
import { BETTER_AUTH_ROLES } from '../collections/auth/constant.server.js';
|
|
4
5
|
import { logger } from '../logger/index.server.js';
|
|
5
6
|
const dev = process.env.NODE_ENV === 'development';
|
|
6
7
|
/**
|
|
7
|
-
* Analyzes the current route to determine authentication requirements
|
|
8
|
+
* Analyzes the current route to determine authentication requirements.
|
|
9
|
+
* isPanel/isPublicAuthRoute are derived from the matched route, not the
|
|
10
|
+
* pathname — event.params.panel only resolves when the request matched the
|
|
11
|
+
* [panel=panel] matcher, and event.route.id's literal folder names never
|
|
12
|
+
* carry the configured RIME_PANEL_ROUTE value — so neither check needs to
|
|
13
|
+
* know the actual (hideable) panel segment. sign-in/forgot-password/reset-password
|
|
14
|
+
* all live under [panel=panel] too but must stay reachable without a session,
|
|
15
|
+
* hence the isPublicAuthRoute carve-out.
|
|
8
16
|
*/
|
|
9
|
-
function analyzeRoute(
|
|
10
|
-
const
|
|
11
|
-
const isPanel =
|
|
12
|
-
const isAPI = pathname.startsWith('/api');
|
|
13
|
-
return {
|
|
17
|
+
function analyzeRoute(event) {
|
|
18
|
+
const isPublicAuthRoute = isPublicPanelAuthRoute(event.route.id);
|
|
19
|
+
const isPanel = event.params.panel !== undefined && !isPublicAuthRoute;
|
|
20
|
+
const isAPI = event.url.pathname.startsWith('/api');
|
|
21
|
+
return { isPublicAuthRoute, isPanel, isAPI };
|
|
14
22
|
}
|
|
15
23
|
/**
|
|
16
24
|
* Ensures panel is properly set up before allowing access
|
|
@@ -31,7 +39,7 @@ async function authenticateRequest(headers, auth) {
|
|
|
31
39
|
*/
|
|
32
40
|
function handleUnauthenticated(event, resolve, routeInfo) {
|
|
33
41
|
if (routeInfo.isPanel) {
|
|
34
|
-
throw redirect(303,
|
|
42
|
+
throw redirect(303, `/${event.params.panel}/sign-in`);
|
|
35
43
|
}
|
|
36
44
|
event.locals.user = undefined;
|
|
37
45
|
event.locals.session = undefined;
|
|
@@ -132,7 +140,7 @@ function cleanupUser(userData, routeInfo) {
|
|
|
132
140
|
*/
|
|
133
141
|
export const handleAuth = async ({ event, resolve }) => {
|
|
134
142
|
const rime = event.locals.rime;
|
|
135
|
-
const routeInfo = analyzeRoute(event
|
|
143
|
+
const routeInfo = analyzeRoute(event);
|
|
136
144
|
// Ensure auth is set up
|
|
137
145
|
if (routeInfo.isPanel) {
|
|
138
146
|
await ensureFirstAuthSetup(rime);
|
|
@@ -31,7 +31,7 @@ export declare const routeHandlers: {
|
|
|
31
31
|
aria: import("../../types.js").Route[];
|
|
32
32
|
}>;
|
|
33
33
|
live: typeof liveLoad;
|
|
34
|
-
signIn: ({ locals }: ServerLoadEvent) => Promise<{
|
|
34
|
+
signIn: ({ locals, params }: ServerLoadEvent) => Promise<{
|
|
35
35
|
forgotPasswordEnabled: boolean;
|
|
36
36
|
image: string | null;
|
|
37
37
|
form: {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isPublicPanelAuthRoute } from '../constant.js';
|
|
1
2
|
import { prototypeKebabToSlug } from '../naming.js';
|
|
2
3
|
import buildNavigation from '../../panel/navigation.js';
|
|
3
4
|
import { areaFormActions } from '../../panel/pages/area/actions.server.js';
|
|
@@ -56,9 +57,9 @@ export const routeHandlers = {
|
|
|
56
57
|
};
|
|
57
58
|
export const handleRoutes = async ({ event, resolve }) => {
|
|
58
59
|
const { rime, user } = event.locals;
|
|
59
|
-
const
|
|
60
|
+
const IS_PUBLIC_AUTH_ROUTE = isPublicPanelAuthRoute(event.route.id);
|
|
60
61
|
const IS_API_ROUTE = event.url.pathname.startsWith('/api');
|
|
61
|
-
const IS_PANEL_ROUTE = event.
|
|
62
|
+
const IS_PANEL_ROUTE = event.params.panel !== undefined && !IS_PUBLIC_AUTH_ROUTE;
|
|
62
63
|
// event.params.slug comes straight from the URL, always kebab-case (see
|
|
63
64
|
// src/params/collection.ts, area.ts). Collection/area slugs themselves are
|
|
64
65
|
// camelCase, so rewrite once here — every downstream handler (panel
|
|
@@ -72,7 +73,7 @@ export const handleRoutes = async ({ event, resolve }) => {
|
|
|
72
73
|
}
|
|
73
74
|
// build panel navigation
|
|
74
75
|
if (IS_PANEL_ROUTE && event.request.method === 'GET') {
|
|
75
|
-
event.locals.navigation = buildNavigation(rime.config.raw, user);
|
|
76
|
+
event.locals.navigation = buildNavigation(rime.config.raw, user, event.params.panel);
|
|
76
77
|
}
|
|
77
78
|
event.locals.routes = routeHandlers;
|
|
78
79
|
// Handle custom routes from config and plugins
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { env } from '$env/dynamic/private';
|
|
1
2
|
import { PARAMS } from '../../../constant.js';
|
|
2
3
|
import { logger } from '../../../logger/index.server.js';
|
|
3
4
|
import { getValueAtPath } from '../../../../util/object.js';
|
|
@@ -98,7 +99,7 @@ export const populateURL = Hooks.beforeRead(async (args) => {
|
|
|
98
99
|
}
|
|
99
100
|
// Add the live url if needed
|
|
100
101
|
if (config.live && event.locals.user && url) {
|
|
101
|
-
args.doc._live = `${process.env.PUBLIC_RIME_URL}
|
|
102
|
+
args.doc._live = `${process.env.PUBLIC_RIME_URL}/${env.RIME_PANEL_ROUTE || 'panel'}/live-edit?src=${url}&slug=${config.slug}&id=${args.doc.id}`;
|
|
102
103
|
args.doc._live += args.doc.versionId ? `&${PARAMS.VERSION_ID}=${args.doc.versionId}` : '';
|
|
103
104
|
args.doc._live += locale ? `&${PARAMS.LOCALE}=${locale}` : '';
|
|
104
105
|
}
|
|
@@ -10,7 +10,7 @@ export const handler = async ({ event, resolve }) => {
|
|
|
10
10
|
return await resolve(event);
|
|
11
11
|
}
|
|
12
12
|
// Do not cache panel routes
|
|
13
|
-
if (event.
|
|
13
|
+
if (event.params.panel !== undefined) {
|
|
14
14
|
return await resolve(event);
|
|
15
15
|
}
|
|
16
16
|
// Use configuration function
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import type { GenericDoc } from '../../../../../../core/types/doc.js';
|
|
4
4
|
import Checkbox from '../../../../ui/checkbox/checkbox.svelte';
|
|
5
5
|
import { getLocaleContext } from '../../../../../context/locale.svelte';
|
|
6
|
+
import { panelUrl } from '../../../../../util/url.js';
|
|
6
7
|
import { getValueAtPath } from '../../../../../../util/object';
|
|
7
8
|
import StatusDot from '../../StatusDot.svelte';
|
|
8
9
|
import UploadThumbCell from '../../upload-thumb-cell/UploadThumbCell.svelte';
|
|
@@ -59,7 +60,7 @@
|
|
|
59
60
|
{/if}
|
|
60
61
|
<label for="checkbox-{doc.id}" class="rz-list-row__title">{doc.title || '[untitled]'}</label>
|
|
61
62
|
{:else}
|
|
62
|
-
<a class="rz-list-row__link" href=
|
|
63
|
+
<a class="rz-list-row__link" href={panelUrl(config.kebab, doc.id)}>
|
|
63
64
|
{#if doc._thumbnail}
|
|
64
65
|
<UploadThumbCell url={doc._thumbnail} mimeType={doc.mimeType} />
|
|
65
66
|
{:else}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { GenericDoc } from '../../../../../core/types/doc.js';
|
|
3
3
|
import type { CollectionContext } from '../../../../context/collection.svelte.js';
|
|
4
|
+
import { panelUrl } from '../../../../util/url.js';
|
|
4
5
|
import { GripVertical } from '@lucide/svelte';
|
|
5
6
|
import StatusDot from '../StatusDot.svelte';
|
|
6
7
|
import CollectionTreeNode from './CollectionTreeNode.svelte';
|
|
@@ -12,7 +13,7 @@
|
|
|
12
13
|
|
|
13
14
|
{#key `${parentId}-${doc.id}-${doc._position}`}
|
|
14
15
|
<div data-parent={parentId} data-id={doc.id} class="rz-collection-node">
|
|
15
|
-
<a href=
|
|
16
|
+
<a href={panelUrl(doc._type, doc.id)} class="rz-collection-node__row">
|
|
16
17
|
<div class="rz-collection-node__grip"><GripVertical size="12" /></div>
|
|
17
18
|
<div>
|
|
18
19
|
{doc.title}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { DocumentFormContext } from '../../../context/documentForm.svelte.js';
|
|
8
8
|
import { getUserContext } from '../../../context/user.svelte.js';
|
|
9
9
|
import { authClient } from '../../../util/auth';
|
|
10
|
+
import { panelUrl } from '../../../util/url.js';
|
|
10
11
|
import validate from '../../../../util/validate';
|
|
11
12
|
import { toast } from 'svelte-sonner';
|
|
12
13
|
import Button from '../../ui/button/button.svelte';
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
async function sendPasswordResetLink() {
|
|
28
29
|
const { data, error } = await authClient.requestPasswordReset({
|
|
29
30
|
email: form.values.email,
|
|
30
|
-
redirectTo:
|
|
31
|
+
redirectTo: `${panelUrl('reset-password')}?slug=staff`
|
|
31
32
|
});
|
|
32
33
|
if (error && error.message) {
|
|
33
34
|
toast.error(error.message);
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
if (doc._prototype === 'collection') {
|
|
33
33
|
return `${panelUrl(kebabSlug, doc.id)}?versionId=${doc.versionId}`;
|
|
34
34
|
} else {
|
|
35
|
-
return `${panelUrl(kebabSlug)}
|
|
35
|
+
return `${panelUrl(kebabSlug)}?versionId=${doc.versionId}`;
|
|
36
36
|
}
|
|
37
37
|
});
|
|
38
38
|
</script>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { mimeTypeToIcon } from '../../../util/upload.js';
|
|
3
|
+
import { panelUrl } from '../../../util/url.js';
|
|
3
4
|
import { Edit, FileIcon, X } from '@lucide/svelte';
|
|
4
5
|
|
|
5
6
|
type Resource = {
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
|
|
41
42
|
<div class="rz-card-resource__info">
|
|
42
43
|
<p class="rz-card-resource__title">
|
|
43
|
-
{resource.title} <a href=
|
|
44
|
+
{resource.title} <a href={panelUrl(resource._type, resource.id)}><Edit size="12" /></a>
|
|
44
45
|
</p>
|
|
45
46
|
{#if isUpload}
|
|
46
47
|
<p class="rz-card-resource__info-text">{resource.filesize}</p>
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
<div class:rz-nav--collapsed={isCollapsed} class="rz-nav">
|
|
38
38
|
<div class="rz-nav__content">
|
|
39
39
|
<div class="rz-nav__header" class:rz-nav__header--collapsed={isCollapsed}>
|
|
40
|
-
<NavItem href=
|
|
40
|
+
<NavItem href={panelUrl()} {isCollapsed} route={dashBoardRoute} />
|
|
41
41
|
</div>
|
|
42
42
|
|
|
43
43
|
<div class="rz-nav__body">
|
|
@@ -23,8 +23,9 @@
|
|
|
23
23
|
|
|
24
24
|
let active = $derived.by(() => {
|
|
25
25
|
const routePathname = new URL(route.url).pathname;
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
const panelRoot = `/${page.params.panel}`;
|
|
27
|
+
if (routePathname === panelRoot) {
|
|
28
|
+
return pathname === panelRoot;
|
|
28
29
|
}
|
|
29
30
|
const reg = new RegExp(`^${routePathname}(/.*)?$`);
|
|
30
31
|
return reg.test(pathname);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { env } from '$env/dynamic/public';
|
|
3
2
|
import * as Tooltip from '../tooltip';
|
|
4
3
|
import { getUserContext } from '../../../context/user.svelte.js';
|
|
5
4
|
import { authClient } from '../../../util/auth.js';
|
|
5
|
+
import { panelUrl } from '../../../util/url.js';
|
|
6
6
|
import { LogOut } from '@lucide/svelte';
|
|
7
7
|
import { toast } from 'svelte-sonner';
|
|
8
8
|
import Button from '../button/button.svelte';
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
async function signout() {
|
|
16
16
|
const result = await authClient.signOut();
|
|
17
17
|
if (result.data?.success) {
|
|
18
|
-
window.location.href =
|
|
18
|
+
window.location.href = panelUrl('sign-in');
|
|
19
19
|
} else {
|
|
20
20
|
toast.error(`Can't sign-out, please try again`);
|
|
21
21
|
}
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
{#if !navCollapsed}
|
|
27
27
|
<div class="rz-user-button">
|
|
28
28
|
<div class="rz-user-button__left">
|
|
29
|
-
<a href=
|
|
29
|
+
<a href={panelUrl('staff', user.attributes.id)}>
|
|
30
30
|
{user.attributes.name?.charAt(0) || ''}
|
|
31
31
|
</a>
|
|
32
32
|
<div class="rz-user-button__name">{user.attributes.name}</div>
|
|
@@ -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
|
}
|