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
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|

|
|
2
2
|

|
|
3
3
|
|
|
4
|
-
Headless CMS powered by SvelteKit. [Read the doc](https://github.com/bienbiendev/rime-doc/
|
|
4
|
+
Headless CMS powered by SvelteKit. [Read the doc](https://github.com/bienbiendev/rime-doc/blob/develop/docs/02-installation.md)
|
|
5
5
|
|
|
6
6
|

|
|
7
7
|

|
|
@@ -24,7 +24,7 @@ export const transformerFacade = (args) => {
|
|
|
24
24
|
const tableName = isVersioned ? withVersionsSuffix(slug) : slug;
|
|
25
25
|
const tableNameRelationFields = `${tableName}Rels`;
|
|
26
26
|
const tableNameLocales = withLocalesSuffix(tableName);
|
|
27
|
-
const isPanel = event.
|
|
27
|
+
const isPanel = event.params.panel !== undefined;
|
|
28
28
|
let docAPI;
|
|
29
29
|
if (configCtx.isCollection(slug)) {
|
|
30
30
|
docAPI = rime.collection(slug);
|
|
@@ -46,9 +46,10 @@ export declare function getParentPath(path: UploadPath): string | null;
|
|
|
46
46
|
* buildUploadAria('root:foo:bar')
|
|
47
47
|
* // return [{ title: 'foo', path: '/panel/{slug}?=root:foo' }, { title: 'bar' }]
|
|
48
48
|
*/
|
|
49
|
-
export declare function buildUploadAria({ path, slug }: {
|
|
49
|
+
export declare function buildUploadAria({ path, slug, panelSegment }: {
|
|
50
50
|
path: UploadPath;
|
|
51
51
|
slug: CollectionSlug;
|
|
52
|
+
panelSegment: string | undefined;
|
|
52
53
|
}): Partial<Route>[];
|
|
53
54
|
export declare function removePathFromLastAria(aria: Partial<Route>[]): Partial<Route>[];
|
|
54
55
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PARAMS, UPLOAD_PATH } from '../../../constant.js';
|
|
2
2
|
import { RimeError } from '../../../errors/index.js';
|
|
3
|
-
import {
|
|
3
|
+
import { panelUrlFor } from '../../../../panel/util/url.js';
|
|
4
4
|
import { toKebabCase } from '../../../../util/string.js';
|
|
5
5
|
/**
|
|
6
6
|
* Validates that a path string follows the required format:
|
|
@@ -75,7 +75,7 @@ export function getParentPath(path) {
|
|
|
75
75
|
* buildUploadAria('root:foo:bar')
|
|
76
76
|
* // return [{ title: 'foo', path: '/panel/{slug}?=root:foo' }, { title: 'bar' }]
|
|
77
77
|
*/
|
|
78
|
-
export function buildUploadAria({ path, slug }) {
|
|
78
|
+
export function buildUploadAria({ path, slug, panelSegment }) {
|
|
79
79
|
const segments = path.split(':');
|
|
80
80
|
const result = [];
|
|
81
81
|
let currentPath = '';
|
|
@@ -86,7 +86,7 @@ export function buildUploadAria({ path, slug }) {
|
|
|
86
86
|
if (currentPath !== segments[0]) {
|
|
87
87
|
result.push({
|
|
88
88
|
title: segment,
|
|
89
|
-
url: `${
|
|
89
|
+
url: `${panelUrlFor(panelSegment, toKebabCase(slug))}?${PARAMS.UPLOAD_PATH}=${currentPath}`
|
|
90
90
|
});
|
|
91
91
|
}
|
|
92
92
|
}
|
package/dist/core/constant.d.ts
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/** The generated panel route always lives at this fixed matcher-folder id — the literal
|
|
2
|
+
* folder name never changes, only which segment value src/params/panel.ts's matcher
|
|
3
|
+
* accepts (see PANEL_ROUTE in core/dev/constants.ts). Route ids never carry the resolved
|
|
4
|
+
* param value, so these are safe to compare against event.route.id without leaking the
|
|
5
|
+
* configured secret. */
|
|
6
|
+
export declare const PANEL_ROUTE_ID = "/(rime)/[panel=panel]";
|
|
7
|
+
export declare const PANEL_SIGN_IN_ROUTE_ID = "/(rime)/[panel=panel]/sign-in";
|
|
8
|
+
export declare const PANEL_FORGOT_PASSWORD_ROUTE_ID = "/(rime)/[panel=panel]/forgot-password";
|
|
9
|
+
export declare const PANEL_RESET_PASSWORD_ROUTE_ID = "/(rime)/[panel=panel]/reset-password";
|
|
10
|
+
export declare function isPublicPanelAuthRoute(routeId: string | null): boolean;
|
|
1
11
|
export declare const VERSIONS_STATUS: {
|
|
2
12
|
readonly DRAFT: "draft";
|
|
3
13
|
readonly PUBLISHED: "published";
|
package/dist/core/constant.js
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
/** The generated panel route always lives at this fixed matcher-folder id — the literal
|
|
2
|
+
* folder name never changes, only which segment value src/params/panel.ts's matcher
|
|
3
|
+
* accepts (see PANEL_ROUTE in core/dev/constants.ts). Route ids never carry the resolved
|
|
4
|
+
* param value, so these are safe to compare against event.route.id without leaking the
|
|
5
|
+
* configured secret. */
|
|
6
|
+
export const PANEL_ROUTE_ID = '/(rime)/[panel=panel]';
|
|
7
|
+
export const PANEL_SIGN_IN_ROUTE_ID = `${PANEL_ROUTE_ID}/sign-in`;
|
|
8
|
+
export const PANEL_FORGOT_PASSWORD_ROUTE_ID = `${PANEL_ROUTE_ID}/forgot-password`;
|
|
9
|
+
export const PANEL_RESET_PASSWORD_ROUTE_ID = `${PANEL_ROUTE_ID}/reset-password`;
|
|
10
|
+
/** sign-in, forgot-password and reset-password all have to stay reachable by an
|
|
11
|
+
* unauthenticated visitor (someone locked out, or clicking a reset link from email) even
|
|
12
|
+
* though they now live under the same hidden [panel=panel] segment as the rest of the
|
|
13
|
+
* panel — this is the auth-gate's "public, no-session-required" exception, broadened from
|
|
14
|
+
* sign-in alone to cover all three. */
|
|
15
|
+
const PANEL_PUBLIC_AUTH_ROUTE_IDS = new Set([
|
|
16
|
+
PANEL_SIGN_IN_ROUTE_ID,
|
|
17
|
+
PANEL_FORGOT_PASSWORD_ROUTE_ID,
|
|
18
|
+
PANEL_RESET_PASSWORD_ROUTE_ID
|
|
19
|
+
]);
|
|
20
|
+
export function isPublicPanelAuthRoute(routeId) {
|
|
21
|
+
return !!routeId && PANEL_PUBLIC_AUTH_ROUTE_IDS.has(routeId);
|
|
22
|
+
}
|
|
1
23
|
export const VERSIONS_STATUS = {
|
|
2
24
|
DRAFT: 'draft',
|
|
3
25
|
PUBLISHED: 'published'
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { PANEL_ROUTE } from '../../constants.js';
|
|
1
2
|
import { randomId } from '../../../../util/random.js';
|
|
2
3
|
export const envProduction = () => `# BETTER-AUTH
|
|
3
4
|
BETTER_AUTH_SECRET=${randomId(32)}
|
|
4
5
|
|
|
5
6
|
# RIME
|
|
6
7
|
PUBLIC_RIME_URL=http://localhost:3000
|
|
8
|
+
RIME_PANEL_ROUTE=${PANEL_ROUTE}
|
|
7
9
|
RIME_LOG_LEVEL=ERROR
|
|
8
10
|
RIME_LOG_TO_FILE=true
|
|
9
11
|
RIME_CACHE_ENABLED=true
|
|
@@ -9,7 +9,7 @@ import { cp, mkdir } from 'fs/promises';
|
|
|
9
9
|
import fs from 'node:fs';
|
|
10
10
|
import path from 'path';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
|
-
import { CONFIG_DIR, OUTPUT_DIR } from '../../constants.js';
|
|
12
|
+
import { CONFIG_DIR, OUTPUT_DIR, PANEL_ROUTE } from '../../constants.js';
|
|
13
13
|
import { generate } from '../generate/index.server.js';
|
|
14
14
|
import { installDependencies } from '../util/package-manager.server.js';
|
|
15
15
|
import { getPackageInfoByKey } from '../util/package.server.js';
|
|
@@ -26,6 +26,7 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
|
|
|
26
26
|
BETTER_AUTH_SECRET: randomId(32),
|
|
27
27
|
PUBLIC_RIME_URL: 'http://localhost:5173',
|
|
28
28
|
RIME_CONFIG_DIR: CONFIG_DIR,
|
|
29
|
+
RIME_PANEL_ROUTE: PANEL_ROUTE,
|
|
29
30
|
'# RIME_SMTP_USER': 'user@mail.com',
|
|
30
31
|
'# RIME_SMTP_PASSWORD': 'supersecret',
|
|
31
32
|
'# RIME_SMTP_PORT': '465',
|
|
@@ -2,10 +2,12 @@ export declare const env: () => string;
|
|
|
2
2
|
export declare const defaultConfig: (name: string) => string;
|
|
3
3
|
export declare const drizzleConfig: (name: string) => string;
|
|
4
4
|
export declare const hooks: () => string;
|
|
5
|
-
/** Writes src/hooks.server.ts if missing
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
/** Writes src/hooks.server.ts if missing. If it already exists, patches only the config
|
|
6
|
+
* import line if it's stale (e.g. after RIME_CONFIG_DIR changes the import path) — the rest
|
|
7
|
+
* of the file may carry real customizations (e.g. an appended handler), so this never
|
|
8
|
+
* touches anything else. Called both at `rime init` and whenever the dev server detects a
|
|
9
|
+
* config change, so the import path never goes stale between the two. Returns whether it
|
|
10
|
+
* wrote anything. */
|
|
9
11
|
export declare function regenerateHooks(root?: string): boolean;
|
|
10
12
|
/** Patches drizzle.config.ts's schema path line if it's stale (e.g. after RIME_CONFIG_DIR
|
|
11
13
|
* changes) — only that one line, since the rest of the file may carry real customizations
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { randomId } from '../../../../util/random.js';
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { CONFIG_DIR, GENERATED_DIR, configImportPaths } from '../../constants.js';
|
|
4
|
+
import { CONFIG_DIR, GENERATED_DIR, PANEL_ROUTE, configImportPaths } from '../../constants.js';
|
|
5
5
|
const PACKAGE = 'rimecms';
|
|
6
6
|
export const env = () => `BETTER_AUTH_SECRET=${randomId(32)}
|
|
7
7
|
PUBLIC_RIME_URL=http://localhost:5173
|
|
8
8
|
RIME_CONFIG_DIR=${CONFIG_DIR}
|
|
9
9
|
|
|
10
|
+
# Panel URL segment (e.g. /panel/...). Change it to hide the panel from
|
|
11
|
+
# automated CMS-admin scanners — requests to the old segment 404 instead of
|
|
12
|
+
# revealing a login page. Run 'rime generate' (or restart 'rime dev') after
|
|
13
|
+
# changing this — the dev server only regenerates routes when it detects a
|
|
14
|
+
# change under RIME_CONFIG_DIR, not on every restart.
|
|
15
|
+
RIME_PANEL_ROUTE=${PANEL_ROUTE}
|
|
16
|
+
|
|
10
17
|
# RIME_CACHE_ENABLED=false
|
|
11
18
|
# RIME_SMTP_USER=user@mail.com
|
|
12
19
|
# RIME_SMTP_PASSWORD=supersecret
|
|
@@ -63,23 +70,27 @@ import config from '${configImportPaths(HOOKS_DIR).server}';
|
|
|
63
70
|
|
|
64
71
|
export const handle = sequence(...(await handlers(config)));
|
|
65
72
|
`;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
*
|
|
69
|
-
*
|
|
73
|
+
const HOOKS_CONFIG_IMPORT_LINE_REGEX = /^import config from ['"][^'"]*['"];\s*$/m;
|
|
74
|
+
/** Writes src/hooks.server.ts if missing. If it already exists, patches only the config
|
|
75
|
+
* import line if it's stale (e.g. after RIME_CONFIG_DIR changes the import path) — the rest
|
|
76
|
+
* of the file may carry real customizations (e.g. an appended handler), so this never
|
|
77
|
+
* touches anything else. Called both at `rime init` and whenever the dev server detects a
|
|
78
|
+
* config change, so the import path never goes stale between the two. Returns whether it
|
|
79
|
+
* wrote anything. */
|
|
70
80
|
export function regenerateHooks(root = process.cwd()) {
|
|
71
81
|
const hooksPath = path.join(root, 'src', 'hooks.server.ts');
|
|
72
82
|
const srcDir = path.join(root, 'src');
|
|
73
|
-
const content = hooks();
|
|
74
83
|
if (!existsSync(hooksPath)) {
|
|
75
84
|
if (!existsSync(srcDir)) {
|
|
76
85
|
mkdirSync(srcDir, { recursive: true });
|
|
77
86
|
}
|
|
78
|
-
writeFileSync(hooksPath,
|
|
87
|
+
writeFileSync(hooksPath, hooks(), 'utf-8');
|
|
79
88
|
return true;
|
|
80
89
|
}
|
|
81
|
-
|
|
82
|
-
|
|
90
|
+
const expectedImportLine = `import config from '${configImportPaths(HOOKS_DIR).server}';`;
|
|
91
|
+
const content = readFileSync(hooksPath, 'utf-8');
|
|
92
|
+
if (HOOKS_CONFIG_IMPORT_LINE_REGEX.test(content) && !content.includes(expectedImportLine)) {
|
|
93
|
+
writeFileSync(hooksPath, content.replace(HOOKS_CONFIG_IMPORT_LINE_REGEX, expectedImportLine));
|
|
83
94
|
return true;
|
|
84
95
|
}
|
|
85
96
|
return false;
|
|
@@ -2,6 +2,11 @@ export declare const CONFIG_DIR: string;
|
|
|
2
2
|
export declare const GENERATED_DIR: string;
|
|
3
3
|
export declare const INPUT_DIR: string;
|
|
4
4
|
export declare const OUTPUT_DIR: string;
|
|
5
|
+
/** The panel's URL segment (e.g. "panel" -> /panel/...). Kept as a bare segment, never a
|
|
6
|
+
* PUBLIC_-prefixed env var: SvelteKit ships every PUBLIC_ value into the JS bundle of every
|
|
7
|
+
* page (including the public site), which would defeat the point of letting an operator hide
|
|
8
|
+
* this path from automated CMS-admin scanners. */
|
|
9
|
+
export declare const PANEL_ROUTE: string;
|
|
5
10
|
/** Turns an absolute path into an import specifier relative to `fromDir`, always prefixed
|
|
6
11
|
* with `./` or `../` so it never gets mistaken for a bare package specifier. */
|
|
7
12
|
export declare function relativeImportSpecifier(fromDir: string, toAbsolutePath: string): string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import dotenv from 'dotenv';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { isValidSlug } from '../../util/string.js';
|
|
3
4
|
// Must run before RIME_CONFIG_DIR is read below. ES import hoisting evaluates this module's
|
|
4
5
|
// top-level code before any *importing* file's own top-level statements — including a
|
|
5
6
|
// dotenv.config() call there — so loading .env here, first, is the only way every entry point
|
|
@@ -14,6 +15,14 @@ export const CONFIG_DIR = process.env.RIME_CONFIG_DIR || 'src/+rime';
|
|
|
14
15
|
export const GENERATED_DIR = `${CONFIG_DIR}.generated`;
|
|
15
16
|
export const INPUT_DIR = path.basename(CONFIG_DIR);
|
|
16
17
|
export const OUTPUT_DIR = `${INPUT_DIR}.generated`;
|
|
18
|
+
/** The panel's URL segment (e.g. "panel" -> /panel/...). Kept as a bare segment, never a
|
|
19
|
+
* PUBLIC_-prefixed env var: SvelteKit ships every PUBLIC_ value into the JS bundle of every
|
|
20
|
+
* page (including the public site), which would defeat the point of letting an operator hide
|
|
21
|
+
* this path from automated CMS-admin scanners. */
|
|
22
|
+
export const PANEL_ROUTE = (process.env.RIME_PANEL_ROUTE || 'panel').replace(/^\/+|\/+$/g, '');
|
|
23
|
+
if (!isValidSlug(PANEL_ROUTE)) {
|
|
24
|
+
throw new Error(`RIME_PANEL_ROUTE must be a single URL segment (letters, numbers, "_"/"-", starting with a letter) — got "${PANEL_ROUTE}"`);
|
|
25
|
+
}
|
|
17
26
|
const RESERVED_DIRS = new Set(['lib', 'routes', 'static', 'params'].map((d) => path.resolve(SRC_ROOT, d)));
|
|
18
27
|
for (const dir of [CONFIG_DIR, GENERATED_DIR]) {
|
|
19
28
|
const resolved = path.resolve(root, dir);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { PACKAGE_NAME } from '../../../constant.server.js';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { configImportPaths } from '../../constants.js';
|
|
4
|
-
const PANEL_LAYOUT_DIR = path.resolve(process.cwd(), 'src/routes/(rime)/panel');
|
|
5
|
-
const LIVE_PAGE_DIR = path.resolve(process.cwd(), 'src/routes/(rime)/panel/live-edit');
|
|
4
|
+
const PANEL_LAYOUT_DIR = path.resolve(process.cwd(), 'src/routes/(rime)/[panel=panel]');
|
|
5
|
+
const LIVE_PAGE_DIR = path.resolve(process.cwd(), 'src/routes/(rime)/[panel=panel]/live-edit');
|
|
6
6
|
/**
|
|
7
7
|
* Main base layout
|
|
8
8
|
* /+layout.server.ts
|
|
@@ -82,7 +82,7 @@ export const load = async ({ locals }: ServerLoadEvent) => {
|
|
|
82
82
|
};`;
|
|
83
83
|
/**
|
|
84
84
|
* Login page template
|
|
85
|
-
* (rime)/panel/sign-in/+page.svelte
|
|
85
|
+
* (rime)/[panel=panel]/sign-in/+page.svelte
|
|
86
86
|
*/
|
|
87
87
|
const signInPage = () => `
|
|
88
88
|
<script>
|
|
@@ -105,7 +105,7 @@ export const actions = {
|
|
|
105
105
|
`;
|
|
106
106
|
/**
|
|
107
107
|
* Forgot password page template
|
|
108
|
-
* (rime)/forgot-password/+page.svelte
|
|
108
|
+
* (rime)/[panel=panel]/forgot-password/+page.svelte
|
|
109
109
|
*/
|
|
110
110
|
const forgotPasswordPage = () => `
|
|
111
111
|
<script>
|
|
@@ -115,7 +115,7 @@ const forgotPasswordPage = () => `
|
|
|
115
115
|
<ForgotPassword {data} />`;
|
|
116
116
|
/**
|
|
117
117
|
* Forgot password page server template
|
|
118
|
-
* (rime)/forgot-password/+page.server.ts
|
|
118
|
+
* (rime)/[panel=panel]/forgot-password/+page.server.ts
|
|
119
119
|
*/
|
|
120
120
|
const forgotPasswordPageServer = () => `
|
|
121
121
|
import { type ServerLoadEvent } from '@sveltejs/kit';
|
|
@@ -124,7 +124,7 @@ export const load = (event: ServerLoadEvent) => event.locals.routes.panel.load.f
|
|
|
124
124
|
`;
|
|
125
125
|
/**
|
|
126
126
|
* Reset password page template
|
|
127
|
-
* (rime)/reset-password/+page.svelte
|
|
127
|
+
* (rime)/[panel=panel]/reset-password/+page.svelte
|
|
128
128
|
*/
|
|
129
129
|
const resetPasswordPage = () => `
|
|
130
130
|
<script>
|
|
@@ -135,7 +135,7 @@ const resetPasswordPage = () => `
|
|
|
135
135
|
<ResetPassword {data} />`;
|
|
136
136
|
/**
|
|
137
137
|
* Reset password page server template
|
|
138
|
-
* (rime)/reset-password/+page.server.ts
|
|
138
|
+
* (rime)/[panel=panel]/reset-password/+page.server.ts
|
|
139
139
|
*/
|
|
140
140
|
const resetPasswordPageServer = () => `
|
|
141
141
|
import { type ServerLoadEvent } from '@sveltejs/kit';
|
|
@@ -144,7 +144,7 @@ export const load = (event: ServerLoadEvent) => event.locals.routes.panel.load.r
|
|
|
144
144
|
`;
|
|
145
145
|
/**
|
|
146
146
|
* Panel layout template
|
|
147
|
-
* (rime)/panel/+layout.svelte
|
|
147
|
+
* (rime)/[panel=panel]/+layout.svelte
|
|
148
148
|
*/
|
|
149
149
|
const panelLayout = () => `
|
|
150
150
|
<script>
|
|
@@ -164,7 +164,7 @@ const panelLayout = () => `
|
|
|
164
164
|
</Panel>`;
|
|
165
165
|
/**
|
|
166
166
|
* Panel layout server template
|
|
167
|
-
* (rime)/panel/+layout.server.ts
|
|
167
|
+
* (rime)/[panel=panel]/+layout.server.ts
|
|
168
168
|
*/
|
|
169
169
|
const panelLayoutServer = () => `
|
|
170
170
|
import { type ServerLoadEvent } from '@sveltejs/kit';
|
|
@@ -175,7 +175,7 @@ export const load = async ({ locals }: ServerLoadEvent) => {
|
|
|
175
175
|
};`;
|
|
176
176
|
/**
|
|
177
177
|
* Panel page template
|
|
178
|
-
* (rime)/panel/+page.svelte
|
|
178
|
+
* (rime)/[panel=panel]/+page.svelte
|
|
179
179
|
*/
|
|
180
180
|
const panelPage = () => `
|
|
181
181
|
<script>
|
|
@@ -186,7 +186,7 @@ const panelPage = () => `
|
|
|
186
186
|
<Dashboard entries={data.entries} user={data.user} />`;
|
|
187
187
|
/**
|
|
188
188
|
* Panel page load template
|
|
189
|
-
* (rime)/panel/+page.server.ts
|
|
189
|
+
* (rime)/[panel=panel]/+page.server.ts
|
|
190
190
|
*/
|
|
191
191
|
const panelPageServer = () => `
|
|
192
192
|
import { type ServerLoadEvent } from '@sveltejs/kit';
|
|
@@ -229,7 +229,7 @@ export const load = (event: ServerLoadEvent) => event.locals.routes.panel.load.l
|
|
|
229
229
|
/****************************************************/
|
|
230
230
|
/**
|
|
231
231
|
* Collection list page
|
|
232
|
-
* (rime)/panel/[slug=collection]/+page.svelte
|
|
232
|
+
* (rime)/[panel=panel]/[slug=collection]/+page.svelte
|
|
233
233
|
*/
|
|
234
234
|
const collectionListPage = () => `
|
|
235
235
|
<script>
|
|
@@ -239,7 +239,7 @@ const collectionListPage = () => `
|
|
|
239
239
|
|
|
240
240
|
<Collection {data} slug={data.slug} />`;
|
|
241
241
|
/**
|
|
242
|
-
* (rime)/panel/[slug=collection]/+page.server.ts
|
|
242
|
+
* (rime)/[panel=panel]/[slug=collection]/+page.server.ts
|
|
243
243
|
*/
|
|
244
244
|
const collectionListPageServer = () => `
|
|
245
245
|
import { type ServerLoadEvent } from '@sveltejs/kit';
|
|
@@ -250,7 +250,7 @@ export const load = async (event: ServerLoadEvent) => {
|
|
|
250
250
|
};`;
|
|
251
251
|
/**
|
|
252
252
|
* Area document page
|
|
253
|
-
* (rime)/panel/[slug=area]/+page.svelte
|
|
253
|
+
* (rime)/[panel=panel]/[slug=area]/+page.svelte
|
|
254
254
|
*/
|
|
255
255
|
const areaDocPage = () => `
|
|
256
256
|
<script>
|
|
@@ -260,7 +260,7 @@ const areaDocPage = () => `
|
|
|
260
260
|
|
|
261
261
|
<Area {data} />`;
|
|
262
262
|
/**
|
|
263
|
-
* (rime)/panel/[slug=area]/+page.server.ts
|
|
263
|
+
* (rime)/[panel=panel]/[slug=area]/+page.server.ts
|
|
264
264
|
*/
|
|
265
265
|
const areaDocPageServer = () => `
|
|
266
266
|
import type { RequestEvent, ServerLoadEvent } from '@sveltejs/kit';
|
|
@@ -272,7 +272,7 @@ export const actions = {
|
|
|
272
272
|
};`;
|
|
273
273
|
/**
|
|
274
274
|
* Collection document page
|
|
275
|
-
* (rime)/panel/[slug=collection]/[id]/+page.svelte
|
|
275
|
+
* (rime)/[panel=panel]/[slug=collection]/[id]/+page.svelte
|
|
276
276
|
*/
|
|
277
277
|
const collectionDocPage = () => `
|
|
278
278
|
<script>
|
|
@@ -282,7 +282,7 @@ const collectionDocPage = () => `
|
|
|
282
282
|
|
|
283
283
|
<CollectionDoc {data} />`;
|
|
284
284
|
/**
|
|
285
|
-
* (rime)/panel/[slug=collection]/[id]/+page.server.ts
|
|
285
|
+
* (rime)/[panel=panel]/[slug=collection]/[id]/+page.server.ts
|
|
286
286
|
*/
|
|
287
287
|
const collectionDocPageServer = () => `
|
|
288
288
|
import type { RequestEvent, ServerLoadEvent } from '@sveltejs/kit';
|
|
@@ -295,7 +295,7 @@ export const actions = {
|
|
|
295
295
|
};`;
|
|
296
296
|
/**
|
|
297
297
|
* Collection document versions page
|
|
298
|
-
* (rime)/panel/[slug=collection]/[id]/versions/+page.svelte
|
|
298
|
+
* (rime)/[panel=panel]/[slug=collection]/[id]/versions/+page.svelte
|
|
299
299
|
*/
|
|
300
300
|
const collectionDocVersionsPage = () => `
|
|
301
301
|
<script>
|
|
@@ -305,7 +305,7 @@ const collectionDocVersionsPage = () => `
|
|
|
305
305
|
|
|
306
306
|
<CollectionDocVersions data={data} />`;
|
|
307
307
|
/**
|
|
308
|
-
* (rime)/panel/[slug=collection]/[id]/versions/+page.server.ts
|
|
308
|
+
* (rime)/[panel=panel]/[slug=collection]/[id]/versions/+page.server.ts
|
|
309
309
|
*/
|
|
310
310
|
const collectionDocVersionsPageServer = () => `
|
|
311
311
|
import { type ServerLoadEvent } from '@sveltejs/kit';
|
|
@@ -314,7 +314,7 @@ export const load = (event: ServerLoadEvent) =>
|
|
|
314
314
|
event.locals.routes.panel.load.documentVersions(event);`;
|
|
315
315
|
/**
|
|
316
316
|
* Area document versions page
|
|
317
|
-
* (rime)/panel/[slug=area]/versions/+page.svelte
|
|
317
|
+
* (rime)/[panel=panel]/[slug=area]/versions/+page.svelte
|
|
318
318
|
*/
|
|
319
319
|
const areaVersionsPage = () => `
|
|
320
320
|
<script>
|
|
@@ -324,7 +324,7 @@ const areaVersionsPage = () => `
|
|
|
324
324
|
|
|
325
325
|
<AreaVersionsDoc data={data} />`;
|
|
326
326
|
/**
|
|
327
|
-
* (rime)/panel/[slug=area]/versions/+page.server.ts
|
|
327
|
+
* (rime)/[panel=panel]/[slug=area]/versions/+page.server.ts
|
|
328
328
|
*/
|
|
329
329
|
const areaVersionsPageServer = () => `
|
|
330
330
|
import { type ServerLoadEvent } from '@sveltejs/kit';
|
|
@@ -439,45 +439,45 @@ export const commonRoutes = {
|
|
|
439
439
|
layoutServer: rootLayoutServer,
|
|
440
440
|
error: error
|
|
441
441
|
},
|
|
442
|
-
'(rime)/panel/sign-in': {
|
|
442
|
+
'(rime)/[panel=panel]/sign-in': {
|
|
443
443
|
'page@(rime)': signInPage,
|
|
444
444
|
pageServer: signInPageServer
|
|
445
445
|
},
|
|
446
|
-
'(rime)/forgot-password': {
|
|
447
|
-
page: forgotPasswordPage,
|
|
446
|
+
'(rime)/[panel=panel]/forgot-password': {
|
|
447
|
+
'page@(rime)': forgotPasswordPage,
|
|
448
448
|
pageServer: forgotPasswordPageServer
|
|
449
449
|
},
|
|
450
|
-
'(rime)/reset-password': {
|
|
451
|
-
page: resetPasswordPage,
|
|
450
|
+
'(rime)/[panel=panel]/reset-password': {
|
|
451
|
+
'page@(rime)': resetPasswordPage,
|
|
452
452
|
pageServer: resetPasswordPageServer
|
|
453
453
|
},
|
|
454
|
-
'(rime)/panel': {
|
|
454
|
+
'(rime)/[panel=panel]': {
|
|
455
455
|
layout: panelLayout,
|
|
456
456
|
layoutServer: panelLayoutServer,
|
|
457
457
|
page: panelPage,
|
|
458
458
|
pageServer: panelPageServer
|
|
459
459
|
},
|
|
460
|
-
'(rime)/panel/live-edit': {
|
|
460
|
+
'(rime)/[panel=panel]/live-edit': {
|
|
461
461
|
'page@(rime)': livePage,
|
|
462
462
|
pageServer: livePageServer
|
|
463
463
|
},
|
|
464
|
-
'(rime)/panel/[slug=collection]': {
|
|
464
|
+
'(rime)/[panel=panel]/[slug=collection]': {
|
|
465
465
|
page: collectionListPage,
|
|
466
466
|
pageServer: collectionListPageServer
|
|
467
467
|
},
|
|
468
|
-
'(rime)/panel/[slug=collection]/[id]': {
|
|
468
|
+
'(rime)/[panel=panel]/[slug=collection]/[id]': {
|
|
469
469
|
page: collectionDocPage,
|
|
470
470
|
pageServer: collectionDocPageServer
|
|
471
471
|
},
|
|
472
|
-
'(rime)/panel/[slug=collection]/[id]/versions': {
|
|
472
|
+
'(rime)/[panel=panel]/[slug=collection]/[id]/versions': {
|
|
473
473
|
page: collectionDocVersionsPage,
|
|
474
474
|
pageServer: collectionDocVersionsPageServer
|
|
475
475
|
},
|
|
476
|
-
'(rime)/panel/[slug=area]': {
|
|
476
|
+
'(rime)/[panel=panel]/[slug=area]': {
|
|
477
477
|
page: areaDocPage,
|
|
478
478
|
pageServer: areaDocPageServer
|
|
479
479
|
},
|
|
480
|
-
'(rime)/panel/[slug=area]/versions': {
|
|
480
|
+
'(rime)/[panel=panel]/[slug=area]/versions': {
|
|
481
481
|
page: areaVersionsPage,
|
|
482
482
|
pageServer: areaVersionsPageServer
|
|
483
483
|
},
|
|
@@ -2,11 +2,14 @@ import type { Config } from '../../../../types.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Main function to generate browser routes based on configuration.
|
|
4
4
|
* commonRoutes is a fixed set of files (layout, sign-in, dashboard, the
|
|
5
|
-
* matcher-disambiguated
|
|
5
|
+
* matcher-disambiguated [panel=panel]/[slug=collection|area]/... and
|
|
6
6
|
* /api/[slug=collection|area]/... dynamic routes) — it no longer varies with
|
|
7
7
|
* how many collections/areas are configured, so there's no per-collection/area
|
|
8
|
-
* loop here anymore. The
|
|
9
|
-
*
|
|
8
|
+
* loop here anymore. The folder is always the literal `[panel=panel]` segment;
|
|
9
|
+
* only src/params/panel.ts's matcher content varies with RIME_PANEL_ROUTE, so
|
|
10
|
+
* the URL itself can be hidden from admin-path scanners without ever renaming
|
|
11
|
+
* a directory. Same idea backs src/params/collection.ts and area.ts, which
|
|
12
|
+
* bake in the actual slug list — all three are rewritten here.
|
|
10
13
|
*/
|
|
11
14
|
declare function generateRoutes<T extends Config>(config: T): void;
|
|
12
15
|
export default generateRoutes;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PANEL_ROUTE } from '../../constants.js';
|
|
1
2
|
import { logger } from '../../../logger/index.server.js';
|
|
2
3
|
import fs from 'fs';
|
|
3
4
|
import path from 'path';
|
|
@@ -8,11 +9,14 @@ const projectRoot = process.cwd();
|
|
|
8
9
|
/**
|
|
9
10
|
* Main function to generate browser routes based on configuration.
|
|
10
11
|
* commonRoutes is a fixed set of files (layout, sign-in, dashboard, the
|
|
11
|
-
* matcher-disambiguated
|
|
12
|
+
* matcher-disambiguated [panel=panel]/[slug=collection|area]/... and
|
|
12
13
|
* /api/[slug=collection|area]/... dynamic routes) — it no longer varies with
|
|
13
14
|
* how many collections/areas are configured, so there's no per-collection/area
|
|
14
|
-
* loop here anymore. The
|
|
15
|
-
*
|
|
15
|
+
* loop here anymore. The folder is always the literal `[panel=panel]` segment;
|
|
16
|
+
* only src/params/panel.ts's matcher content varies with RIME_PANEL_ROUTE, so
|
|
17
|
+
* the URL itself can be hidden from admin-path scanners without ever renaming
|
|
18
|
+
* a directory. Same idea backs src/params/collection.ts and area.ts, which
|
|
19
|
+
* bake in the actual slug list — all three are rewritten here.
|
|
16
20
|
*/
|
|
17
21
|
function generateRoutes(config) {
|
|
18
22
|
logger.info('Routes generation...');
|
|
@@ -23,7 +27,7 @@ function generateRoutes(config) {
|
|
|
23
27
|
// 2. Ensure base directories exist
|
|
24
28
|
const rootRoutes = path.resolve(projectRoot, 'src', 'routes');
|
|
25
29
|
const rimeRoutes = path.join(rootRoutes, '(rime)');
|
|
26
|
-
const panelRoute = path.join(rimeRoutes, 'panel');
|
|
30
|
+
const panelRoute = path.join(rimeRoutes, '[panel=panel]');
|
|
27
31
|
const paramsDir = path.resolve(projectRoot, 'src', 'params');
|
|
28
32
|
fs.rmSync(rimeRoutes, { recursive: true, force: true });
|
|
29
33
|
ensureDir(rootRoutes);
|
|
@@ -44,8 +48,10 @@ function generateRoutes(config) {
|
|
|
44
48
|
writeRouteFile(rootRoutes, pattern, fileType, templateFn());
|
|
45
49
|
}
|
|
46
50
|
}
|
|
47
|
-
// 4. Write the [
|
|
48
|
-
//
|
|
51
|
+
// 4. Write the [panel=panel] param matcher — always present, unlike collection/area — plus
|
|
52
|
+
// the [slug=collection]/[slug=area] matchers, only when needed, removing any stale matcher
|
|
53
|
+
// left over from a previous config that did have collections/areas.
|
|
54
|
+
fs.writeFileSync(path.join(paramsDir, 'panel.ts'), paramMatcher([PANEL_ROUTE]));
|
|
49
55
|
const collectionMatcherPath = path.join(paramsDir, 'collection.ts');
|
|
50
56
|
if (hasCollections) {
|
|
51
57
|
fs.writeFileSync(collectionMatcherPath, paramMatcher((config.collections || []).map((c) => c.kebab)));
|
|
@@ -64,7 +70,7 @@ function generateRoutes(config) {
|
|
|
64
70
|
const customRoutes = config.panel?.routes;
|
|
65
71
|
if (customRoutes) {
|
|
66
72
|
for (const [route, routeConfig] of Object.entries(customRoutes)) {
|
|
67
|
-
const routePath = path.join('(rime)', 'panel', route);
|
|
73
|
+
const routePath = path.join('(rime)', '[panel=panel]', route);
|
|
68
74
|
writeRouteFile(rootRoutes, routePath, 'page', customRoute(routeConfig));
|
|
69
75
|
}
|
|
70
76
|
}
|
|
@@ -8,10 +8,18 @@ export type Routes = Record<string, RouteDefinition>;
|
|
|
8
8
|
/**
|
|
9
9
|
* Check if routes need to be regenerated based on config changes.
|
|
10
10
|
* Panel/API routes are a fixed set of dynamic-segment files (file count never
|
|
11
|
-
* grows with schema size), but the [slug=collection]/[slug=area]
|
|
12
|
-
* matchers under src/params/ bake in the actual
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* grows with schema size), but the [panel=panel]/[slug=collection]/[slug=area]
|
|
12
|
+
* param matchers under src/params/ bake in the actual accepted value(s), so
|
|
13
|
+
* changing RIME_PANEL_ROUTE or RIME_CONFIG_DIR, or adding/removing/renaming a
|
|
14
|
+
* collection/area, still needs a regen to keep those matchers in sync — same
|
|
15
|
+
* trigger as custom routes and panel CSS. Both PANEL_ROUTE and CONFIG_DIR must
|
|
16
|
+
* stay in this memo: they're read once at process start, so a changed value
|
|
17
|
+
* only takes effect after a restart, and only if the restart's regen actually
|
|
18
|
+
* notices the change. CONFIG_DIR specifically is baked into the panel/live
|
|
19
|
+
* layout's config import path (see configImportPaths() calls in
|
|
20
|
+
* common.server.ts) — leaving it out would silently keep that import stale
|
|
21
|
+
* instead of erroring, the same failure mode RIME_CONFIG_DIR hit once for
|
|
22
|
+
* hooks.server.ts (see regenerateHooks() in cli/init/templates.ts).
|
|
15
23
|
* @returns true if routes should be regenerated, false otherwise
|
|
16
24
|
*/
|
|
17
25
|
export declare function shouldRegenerateRoutes<T extends Config>(config: T): boolean;
|
|
@@ -33,10 +41,10 @@ export declare function ensureDir(dirPath: string): void;
|
|
|
33
41
|
* @example
|
|
34
42
|
* // Write a page file for a collection
|
|
35
43
|
* const basePath = '/path/to/src/routes';
|
|
36
|
-
* const routePath = '(rime)/panel/news';
|
|
44
|
+
* const routePath = '(rime)/[panel=panel]/news';
|
|
37
45
|
* const fileType = 'page';
|
|
38
46
|
* const content = '<script>\n import { Collection } from "rime/panel"\n</script>...';
|
|
39
47
|
* writeRouteFile(basePath, routePath, fileType, content);
|
|
40
|
-
* // Creates /path/to/src/routes/(rime)/panel/news/+page.svelte with the provided content
|
|
48
|
+
* // Creates /path/to/src/routes/(rime)/[panel=panel]/news/+page.svelte with the provided content
|
|
41
49
|
*/
|
|
42
50
|
export declare function writeRouteFile(basePath: string, routePath: string, fileType: string, content: string): void;
|
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import cache from '../../cache/index.server.js';
|
|
2
|
+
import { CONFIG_DIR, PANEL_ROUTE } from '../../constants.js';
|
|
2
3
|
import { slugify } from '../../../../util/string.js';
|
|
3
4
|
import fs from 'fs';
|
|
4
5
|
import path from 'path';
|
|
5
6
|
/**
|
|
6
7
|
* Check if routes need to be regenerated based on config changes.
|
|
7
8
|
* Panel/API routes are a fixed set of dynamic-segment files (file count never
|
|
8
|
-
* grows with schema size), but the [slug=collection]/[slug=area]
|
|
9
|
-
* matchers under src/params/ bake in the actual
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* grows with schema size), but the [panel=panel]/[slug=collection]/[slug=area]
|
|
10
|
+
* param matchers under src/params/ bake in the actual accepted value(s), so
|
|
11
|
+
* changing RIME_PANEL_ROUTE or RIME_CONFIG_DIR, or adding/removing/renaming a
|
|
12
|
+
* collection/area, still needs a regen to keep those matchers in sync — same
|
|
13
|
+
* trigger as custom routes and panel CSS. Both PANEL_ROUTE and CONFIG_DIR must
|
|
14
|
+
* stay in this memo: they're read once at process start, so a changed value
|
|
15
|
+
* only takes effect after a restart, and only if the restart's regen actually
|
|
16
|
+
* notices the change. CONFIG_DIR specifically is baked into the panel/live
|
|
17
|
+
* layout's config import path (see configImportPaths() calls in
|
|
18
|
+
* common.server.ts) — leaving it out would silently keep that import stale
|
|
19
|
+
* instead of erroring, the same failure mode RIME_CONFIG_DIR hit once for
|
|
20
|
+
* hooks.server.ts (see regenerateHooks() in cli/init/templates.ts).
|
|
12
21
|
* @returns true if routes should be regenerated, false otherwise
|
|
13
22
|
*/
|
|
14
23
|
export function shouldRegenerateRoutes(config) {
|
|
15
24
|
const memo = `
|
|
25
|
+
panel:${PANEL_ROUTE}
|
|
26
|
+
config:${CONFIG_DIR}
|
|
16
27
|
custom:${config.panel?.routes
|
|
17
28
|
? Object.entries(config.panel.routes)
|
|
18
29
|
.map(([k, v]) => `${k}-${slugify(v.component.toString())}`)
|
|
@@ -51,11 +62,11 @@ export function ensureDir(dirPath) {
|
|
|
51
62
|
* @example
|
|
52
63
|
* // Write a page file for a collection
|
|
53
64
|
* const basePath = '/path/to/src/routes';
|
|
54
|
-
* const routePath = '(rime)/panel/news';
|
|
65
|
+
* const routePath = '(rime)/[panel=panel]/news';
|
|
55
66
|
* const fileType = 'page';
|
|
56
67
|
* const content = '<script>\n import { Collection } from "rime/panel"\n</script>...';
|
|
57
68
|
* writeRouteFile(basePath, routePath, fileType, content);
|
|
58
|
-
* // Creates /path/to/src/routes/(rime)/panel/news/+page.svelte with the provided content
|
|
69
|
+
* // Creates /path/to/src/routes/(rime)/[panel=panel]/news/+page.svelte with the provided content
|
|
59
70
|
*/
|
|
60
71
|
export function writeRouteFile(basePath, routePath, fileType, content) {
|
|
61
72
|
const dir = path.join(basePath, routePath);
|