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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { dev } from '$app/environment';
|
|
2
2
|
import { env } from '$env/dynamic/public';
|
|
3
|
-
import {
|
|
3
|
+
import { apiKey } from '@better-auth/api-key';
|
|
4
|
+
import { admin as adminPlugin } from 'better-auth/plugins';
|
|
4
5
|
import { betterAuthAfterHook, betterAuthBeforeHook } from './better-auth-hooks.server.js';
|
|
5
6
|
import { accessControl, admin, staff, user } from './better-auth-permissions.js';
|
|
6
7
|
export function getBaseAuthConfig(ctx) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { flatten } from 'flat';
|
|
2
2
|
import cache from '../../dev/cache/index.server.js';
|
|
3
|
+
import { CONFIG_DIR } from '../../dev/constants.js';
|
|
3
4
|
/**
|
|
4
5
|
* We actually need to serialize config values that will trigger
|
|
5
6
|
* types, routes, or schema generations, meaning that for now
|
|
@@ -59,6 +60,10 @@ const writeMemo = (config) => {
|
|
|
59
60
|
throw new Error(`Config error : Unable to parse value for key ${key}: ${err.message}`);
|
|
60
61
|
}
|
|
61
62
|
})
|
|
63
|
+
// Not part of the user's config object, but generated output (routes, schema, hooks) is
|
|
64
|
+
// relative to it — a RIME_CONFIG_DIR change must invalidate the memo just like a config
|
|
65
|
+
// change does, even when the config content itself is byte-identical.
|
|
66
|
+
.concat(`CONFIG_DIR:${CONFIG_DIR}`)
|
|
62
67
|
.join('\n');
|
|
63
68
|
const cached = cache.get('config');
|
|
64
69
|
if (cached !== memoStr) {
|
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
|
|
@@ -2,14 +2,14 @@ import { RIME_DEV_CACHE_DIR } from '../../../constant.server.js';
|
|
|
2
2
|
import { logger } from '../../../logger/index.server.js';
|
|
3
3
|
import { rmSync } from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import {
|
|
5
|
+
import { CONFIG_DIR, GENERATED_DIR } from '../../constants.js';
|
|
6
6
|
import { prompt } from '../util/prompt.server.js';
|
|
7
7
|
const clearMessage = `Are you sure you want to delete all related rime files (Y/n):
|
|
8
8
|
- ./static/medias
|
|
9
9
|
- ./db
|
|
10
10
|
- ./src/routes/(rime)
|
|
11
|
-
-
|
|
12
|
-
-
|
|
11
|
+
- ./${CONFIG_DIR}
|
|
12
|
+
- ./${GENERATED_DIR}
|
|
13
13
|
- ./src/app.generated.d.ts
|
|
14
14
|
- ./src/rime.generated.d.ts
|
|
15
15
|
- ./src/rime.modules.generated.d.ts
|
|
@@ -28,8 +28,8 @@ export const clear = async (args) => {
|
|
|
28
28
|
// Remove directories
|
|
29
29
|
rmSync(RIME_DEV_CACHE_DIR, { recursive: true, force: true });
|
|
30
30
|
rmSync(path.join('src', 'routes', '(rime)'), { recursive: true, force: true });
|
|
31
|
-
rmSync(
|
|
32
|
-
rmSync(
|
|
31
|
+
rmSync(CONFIG_DIR, { recursive: true, force: true });
|
|
32
|
+
rmSync(GENERATED_DIR, { recursive: true, force: true });
|
|
33
33
|
rmSync(path.join('db'), { recursive: true, force: true });
|
|
34
34
|
rmSync(path.join('static', 'medias'), { recursive: true, force: true });
|
|
35
35
|
// Remove files
|
|
@@ -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 {
|
|
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';
|
|
@@ -25,6 +25,8 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
|
|
|
25
25
|
const envUpdates = {
|
|
26
26
|
BETTER_AUTH_SECRET: randomId(32),
|
|
27
27
|
PUBLIC_RIME_URL: 'http://localhost:5173',
|
|
28
|
+
RIME_CONFIG_DIR: CONFIG_DIR,
|
|
29
|
+
RIME_PANEL_ROUTE: PANEL_ROUTE,
|
|
28
30
|
'# RIME_SMTP_USER': 'user@mail.com',
|
|
29
31
|
'# RIME_SMTP_PASSWORD': 'supersecret',
|
|
30
32
|
'# RIME_SMTP_PORT': '465',
|
|
@@ -53,7 +55,7 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
|
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
function setConfig(name) {
|
|
56
|
-
const configDirPath = path.resolve(root,
|
|
58
|
+
const configDirPath = path.resolve(root, CONFIG_DIR);
|
|
57
59
|
const configPath = path.join(configDirPath, 'rime.config.server.ts');
|
|
58
60
|
if (!existsSync(configPath)) {
|
|
59
61
|
if (!existsSync(configDirPath)) {
|
|
@@ -83,21 +85,24 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
|
|
|
83
85
|
return;
|
|
84
86
|
}
|
|
85
87
|
let gitignoreContent = readFileSync(gitignorePath, 'utf-8');
|
|
88
|
+
// OUTPUT_DIR is derived from RIME_CONFIG_DIR, so its first character isn't always '+' —
|
|
89
|
+
// escape every regex metacharacter generically instead of hand-picking one to backslash.
|
|
90
|
+
const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
86
91
|
const updates = [
|
|
87
|
-
'
|
|
92
|
+
'.cache',
|
|
88
93
|
'/logs',
|
|
89
94
|
'/db',
|
|
90
|
-
|
|
95
|
+
OUTPUT_DIR,
|
|
91
96
|
'src/app.generated.d.ts',
|
|
92
97
|
'src/rime.generated.d.ts'
|
|
93
98
|
];
|
|
94
99
|
if (!gitignoreContent.includes('# rime'))
|
|
95
100
|
gitignoreContent += '\n# rime';
|
|
96
101
|
for (const line of updates) {
|
|
97
|
-
const exists = gitignoreContent.match(new RegExp(`^${line}`, 'm'));
|
|
102
|
+
const exists = gitignoreContent.match(new RegExp(`^${escapeRegExp(line)}`, 'm'));
|
|
98
103
|
if (!exists) {
|
|
99
104
|
// Add new value if doesn't exist
|
|
100
|
-
gitignoreContent += `\n${line
|
|
105
|
+
gitignoreContent += `\n${line}`;
|
|
101
106
|
}
|
|
102
107
|
}
|
|
103
108
|
writeFileSync(gitignorePath, gitignoreContent);
|
|
@@ -108,10 +113,11 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
|
|
|
108
113
|
if (!existsSync(drizzleConfigPath)) {
|
|
109
114
|
writeFileSync(drizzleConfigPath, templates.drizzleConfig(name.toString()));
|
|
110
115
|
logger.info('[✓] Drizzle config added');
|
|
116
|
+
return;
|
|
111
117
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
118
|
+
logger.info(templates.regenerateDrizzleConfig(root)
|
|
119
|
+
? '[✓] Drizzle config schema path updated'
|
|
120
|
+
: '[✓] Drizzle config already exists (skip)');
|
|
115
121
|
}
|
|
116
122
|
function configureVite() {
|
|
117
123
|
const configPath = path.resolve(root, 'vite.config.ts');
|
|
@@ -182,23 +188,6 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
|
|
|
182
188
|
}
|
|
183
189
|
return null;
|
|
184
190
|
}
|
|
185
|
-
function setHooks() {
|
|
186
|
-
const hooksPath = path.join(root, 'src', 'hooks.server.ts');
|
|
187
|
-
const srcDir = path.join(root, 'src');
|
|
188
|
-
// Check if file exists
|
|
189
|
-
if (!existsSync(hooksPath)) {
|
|
190
|
-
// Ensure src directory exists
|
|
191
|
-
if (!existsSync(srcDir)) {
|
|
192
|
-
mkdirSync(srcDir, { recursive: true });
|
|
193
|
-
}
|
|
194
|
-
// Create hooks.server.ts with template content
|
|
195
|
-
writeFileSync(hooksPath, templates.hooks(), 'utf-8');
|
|
196
|
-
logger.info('[✓] hooks.server.ts created');
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
logger.info('[✓] hooks.server.ts already exists (skip)');
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
191
|
async function copyAssets() {
|
|
203
192
|
try {
|
|
204
193
|
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -219,7 +208,6 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
|
|
|
219
208
|
setConfig(name);
|
|
220
209
|
setDatabase();
|
|
221
210
|
setDrizzle(name);
|
|
222
|
-
setHooks();
|
|
223
211
|
configureVite();
|
|
224
212
|
await copyAssets();
|
|
225
213
|
!skipInstall && installDependencies();
|
|
@@ -244,7 +232,6 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
|
|
|
244
232
|
setConfig(name);
|
|
245
233
|
setDatabase();
|
|
246
234
|
setDrizzle(name);
|
|
247
|
-
setHooks();
|
|
248
235
|
configureVite();
|
|
249
236
|
await copyAssets();
|
|
250
237
|
!skipInstall && installDependencies();
|
|
@@ -2,3 +2,17 @@ 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. 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. */
|
|
11
|
+
export declare function regenerateHooks(root?: string): boolean;
|
|
12
|
+
/** Patches drizzle.config.ts's schema path line if it's stale (e.g. after RIME_CONFIG_DIR
|
|
13
|
+
* changes) — only that one line, since the rest of the file may carry real customizations
|
|
14
|
+
* (verbose, custom fields, etc.). Must run before generateSchema()'s own drizzle-kit
|
|
15
|
+
* generate/migrate shell-out, which reads this file from disk. No-op if the file doesn't
|
|
16
|
+
* exist — creating it from scratch needs a project name, which is `rime init`'s job
|
|
17
|
+
* (setDrizzle() in init/index.server.ts). Returns whether it patched anything. */
|
|
18
|
+
export declare function regenerateDrizzleConfig(root?: string): boolean;
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { randomId } from '../../../../util/random.js';
|
|
2
|
-
import {
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { CONFIG_DIR, GENERATED_DIR, PANEL_ROUTE, configImportPaths } from '../../constants.js';
|
|
3
5
|
const PACKAGE = 'rimecms';
|
|
4
6
|
export const env = () => `BETTER_AUTH_SECRET=${randomId(32)}
|
|
5
7
|
PUBLIC_RIME_URL=http://localhost:5173
|
|
8
|
+
RIME_CONFIG_DIR=${CONFIG_DIR}
|
|
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}
|
|
6
16
|
|
|
7
17
|
# RIME_CACHE_ENABLED=false
|
|
8
18
|
# RIME_SMTP_USER=user@mail.com
|
|
@@ -40,7 +50,7 @@ export const drizzleConfig = (name) => `
|
|
|
40
50
|
import { defineConfig, type Config } from 'drizzle-kit';
|
|
41
51
|
|
|
42
52
|
export const config: Config = {
|
|
43
|
-
schema: '
|
|
53
|
+
schema: './${GENERATED_DIR}/schema.server.ts',
|
|
44
54
|
out: './db',
|
|
45
55
|
strict: false,
|
|
46
56
|
dialect: 'sqlite',
|
|
@@ -53,9 +63,54 @@ export default defineConfig(config);
|
|
|
53
63
|
`;
|
|
54
64
|
// Regenerated fresh by rime init every time (setHooks() only skips if the file already
|
|
55
65
|
// exists, and `rime clear`/useConfig.js delete it first) — safe to make mode-aware.
|
|
66
|
+
const HOOKS_DIR = path.resolve(process.cwd(), 'src');
|
|
56
67
|
export const hooks = () => `import { sequence } from '@sveltejs/kit/hooks';
|
|
57
68
|
import { handlers } from '${PACKAGE}/server';
|
|
58
|
-
import config from '
|
|
69
|
+
import config from '${configImportPaths(HOOKS_DIR).server}';
|
|
59
70
|
|
|
60
71
|
export const handle = sequence(...(await handlers(config)));
|
|
61
72
|
`;
|
|
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. */
|
|
80
|
+
export function regenerateHooks(root = process.cwd()) {
|
|
81
|
+
const hooksPath = path.join(root, 'src', 'hooks.server.ts');
|
|
82
|
+
const srcDir = path.join(root, 'src');
|
|
83
|
+
if (!existsSync(hooksPath)) {
|
|
84
|
+
if (!existsSync(srcDir)) {
|
|
85
|
+
mkdirSync(srcDir, { recursive: true });
|
|
86
|
+
}
|
|
87
|
+
writeFileSync(hooksPath, hooks(), 'utf-8');
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
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));
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const DRIZZLE_SCHEMA_LINE_REGEX = /^\s*schema:\s*['"][^'"]*['"],?\s*$/m;
|
|
99
|
+
/** Patches drizzle.config.ts's schema path line if it's stale (e.g. after RIME_CONFIG_DIR
|
|
100
|
+
* changes) — only that one line, since the rest of the file may carry real customizations
|
|
101
|
+
* (verbose, custom fields, etc.). Must run before generateSchema()'s own drizzle-kit
|
|
102
|
+
* generate/migrate shell-out, which reads this file from disk. No-op if the file doesn't
|
|
103
|
+
* exist — creating it from scratch needs a project name, which is `rime init`'s job
|
|
104
|
+
* (setDrizzle() in init/index.server.ts). Returns whether it patched anything. */
|
|
105
|
+
export function regenerateDrizzleConfig(root = process.cwd()) {
|
|
106
|
+
const drizzleConfigPath = path.join(root, 'drizzle.config.ts');
|
|
107
|
+
if (!existsSync(drizzleConfigPath))
|
|
108
|
+
return false;
|
|
109
|
+
const expectedSchemaLine = ` schema: './${GENERATED_DIR}/schema.server.ts',`;
|
|
110
|
+
const content = readFileSync(drizzleConfigPath, 'utf-8');
|
|
111
|
+
if (DRIZZLE_SCHEMA_LINE_REGEX.test(content) && !content.includes(expectedSchemaLine)) {
|
|
112
|
+
writeFileSync(drizzleConfigPath, content.replace(DRIZZLE_SCHEMA_LINE_REGEX, expectedSchemaLine));
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
@@ -1,15 +1,28 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
export declare const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
export declare const CONFIG_DIR: string;
|
|
2
|
+
export declare const GENERATED_DIR: string;
|
|
3
|
+
export declare const INPUT_DIR: string;
|
|
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;
|
|
10
|
+
/** Turns an absolute path into an import specifier relative to `fromDir`, always prefixed
|
|
11
|
+
* with `./` or `../` so it never gets mistaken for a bare package specifier. */
|
|
12
|
+
export declare function relativeImportSpecifier(fromDir: string, toAbsolutePath: string): string;
|
|
13
|
+
export declare function generatedConfigServerPath(): string;
|
|
14
|
+
/** Where the panel/live route templates (common.server.ts) and the generated hooks.server.ts
|
|
15
|
+
* should import the client/server config from — a relative import specifier computed from
|
|
16
|
+
* `fromDir` (the absolute directory of the file doing the importing), so it resolves whether
|
|
17
|
+
* GENERATED_DIR lives under src/lib or elsewhere. One source of truth instead of hardcoding the
|
|
18
|
+
* path (or relying on the $lib alias, which only ever points at src/lib) at each call site. */
|
|
19
|
+
export declare function configImportPaths(fromDir: string): {
|
|
7
20
|
client: string;
|
|
8
21
|
server: string;
|
|
9
22
|
};
|
|
10
23
|
/** Same idea as configImportPaths — one source of truth for where the generated schema
|
|
11
24
|
* lives instead of hardcoding the path at each call site. */
|
|
12
|
-
export declare function schemaPath(
|
|
25
|
+
export declare function schemaPath(projectRoot?: string): string;
|
|
13
26
|
/**
|
|
14
27
|
* True when `metaUrl` (pass the caller's own `import.meta.url` — this can't be one shared
|
|
15
28
|
* value, it depends on which file is asking) is running from inside an installed `rimecms`
|
|
@@ -1,19 +1,67 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
1
2
|
import path from 'node:path';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import { isValidSlug } from '../../util/string.js';
|
|
4
|
+
// Must run before RIME_CONFIG_DIR is read below. ES import hoisting evaluates this module's
|
|
5
|
+
// top-level code before any *importing* file's own top-level statements — including a
|
|
6
|
+
// dotenv.config() call there — so loading .env here, first, is the only way every entry point
|
|
7
|
+
// (CLI, dev-server, drizzle.config.ts, etc.) reliably sees it. override:true so .env is the
|
|
8
|
+
// explicit source of truth, not whatever a shell plugin (autoenv, direnv, ...) already exported
|
|
9
|
+
// from some other project's .env. The only dotenv.config() call in the codebase — a second one
|
|
10
|
+
// elsewhere would race this one.
|
|
11
|
+
dotenv.config({ override: true });
|
|
12
|
+
const root = process.cwd();
|
|
13
|
+
const SRC_ROOT = path.resolve(root, 'src');
|
|
14
|
+
export const CONFIG_DIR = process.env.RIME_CONFIG_DIR || 'src/+rime';
|
|
15
|
+
export const GENERATED_DIR = `${CONFIG_DIR}.generated`;
|
|
16
|
+
export const INPUT_DIR = path.basename(CONFIG_DIR);
|
|
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
|
+
}
|
|
26
|
+
const RESERVED_DIRS = new Set(['lib', 'routes', 'static', 'params'].map((d) => path.resolve(SRC_ROOT, d)));
|
|
27
|
+
for (const dir of [CONFIG_DIR, GENERATED_DIR]) {
|
|
28
|
+
const resolved = path.resolve(root, dir);
|
|
29
|
+
if (resolved === SRC_ROOT || !resolved.startsWith(SRC_ROOT + path.sep)) {
|
|
30
|
+
throw new Error(`RIME_CONFIG_DIR must resolve to a subdirectory of ${SRC_ROOT} (got "${dir}")`);
|
|
31
|
+
}
|
|
32
|
+
if (RESERVED_DIRS.has(resolved)) {
|
|
33
|
+
throw new Error(`RIME_CONFIG_DIR must not be src/lib, src/routes, src/static, or src/params (got "${dir}")`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Turns an absolute path into an import specifier relative to `fromDir`, always prefixed
|
|
37
|
+
* with `./` or `../` so it never gets mistaken for a bare package specifier. */
|
|
38
|
+
export function relativeImportSpecifier(fromDir, toAbsolutePath) {
|
|
39
|
+
const rel = path.relative(fromDir, toAbsolutePath).split(path.sep).join('/');
|
|
40
|
+
return rel.startsWith('.') ? rel : `./${rel}`;
|
|
41
|
+
}
|
|
42
|
+
/** Root-relative filesystem path (posix-style) to a file inside the generated config dir —
|
|
43
|
+
* for Vite's ssrLoadModule, which wants a real file path, not an import specifier. */
|
|
44
|
+
function generatedFilePath(file) {
|
|
45
|
+
return path.relative(root, path.resolve(root, GENERATED_DIR, file)).split(path.sep).join('/');
|
|
46
|
+
}
|
|
47
|
+
export function generatedConfigServerPath() {
|
|
48
|
+
return generatedFilePath('rime.config.server.ts');
|
|
49
|
+
}
|
|
50
|
+
/** Where the panel/live route templates (common.server.ts) and the generated hooks.server.ts
|
|
51
|
+
* should import the client/server config from — a relative import specifier computed from
|
|
52
|
+
* `fromDir` (the absolute directory of the file doing the importing), so it resolves whether
|
|
53
|
+
* GENERATED_DIR lives under src/lib or elsewhere. One source of truth instead of hardcoding the
|
|
54
|
+
* path (or relying on the $lib alias, which only ever points at src/lib) at each call site. */
|
|
55
|
+
export function configImportPaths(fromDir) {
|
|
8
56
|
return {
|
|
9
|
-
client:
|
|
10
|
-
server:
|
|
57
|
+
client: relativeImportSpecifier(fromDir, path.resolve(root, GENERATED_DIR, 'rime.config.js')),
|
|
58
|
+
server: relativeImportSpecifier(fromDir, path.resolve(root, GENERATED_DIR, 'rime.config.server.js'))
|
|
11
59
|
};
|
|
12
60
|
}
|
|
13
61
|
/** Same idea as configImportPaths — one source of truth for where the generated schema
|
|
14
62
|
* lives instead of hardcoding the path at each call site. */
|
|
15
|
-
export function schemaPath(
|
|
16
|
-
return path.resolve(
|
|
63
|
+
export function schemaPath(projectRoot = process.cwd()) {
|
|
64
|
+
return path.resolve(projectRoot, GENERATED_DIR, 'schema.server.ts');
|
|
17
65
|
}
|
|
18
66
|
/**
|
|
19
67
|
* True when `metaUrl` (pass the caller's own `import.meta.url` — this can't be one shared
|