@questpie/admin 3.2.7 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -6
- package/dist/client/blocks/block-renderer.d.mts +2 -2
- package/dist/client/builder/admin-types.d.mts +3 -3
- package/dist/client/builder/types/action-types.d.mts +1 -1
- package/dist/client/builder/types/collection-types.d.mts +59 -2
- package/dist/client/components/blocks/block-editor-provider.mjs +13 -0
- package/dist/client/components/fields/array-field.mjs +105 -122
- package/dist/client/components/fields/asset-preview-field.mjs +1 -1
- package/dist/client/components/fields/blocks-field/blocks-field.mjs +1 -1
- package/dist/client/components/fields/boolean-field.mjs +1 -1
- package/dist/client/components/fields/date-field.mjs +1 -1
- package/dist/client/components/fields/datetime-field.mjs +1 -1
- package/dist/client/components/fields/email-field.mjs +1 -1
- package/dist/client/components/fields/field-wrapper.mjs +44 -15
- package/dist/client/components/fields/number-field.mjs +1 -1
- package/dist/client/components/fields/object-array-field.mjs +179 -149
- package/dist/client/components/fields/object-field.mjs +96 -87
- package/dist/client/components/fields/relation-picker.mjs +1 -1
- package/dist/client/components/fields/relation-select.mjs +1 -1
- package/dist/client/components/fields/rich-text-editor/index.mjs +1 -1
- package/dist/client/components/fields/select-field.mjs +1 -1
- package/dist/client/components/fields/text-field.mjs +1 -1
- package/dist/client/components/fields/textarea-field.mjs +1 -1
- package/dist/client/components/fields/time-field.mjs +1 -1
- package/dist/client/components/fields/upload-field.mjs +1 -1
- package/dist/client/components/history-sidebar.mjs +10 -4
- package/dist/client/components/structured-diff.mjs +367 -0
- package/dist/client/components/ui/sidebar.mjs +1 -1
- package/dist/client/hooks/use-field-options.mjs +34 -15
- package/dist/client/hooks/use-transition-stage.mjs +2 -2
- package/dist/client/modules/admin.d.mts +3 -0
- package/dist/client/modules/admin.mjs +3 -0
- package/dist/client/preview/block-scope-context.d.mts +2 -2
- package/dist/client/preview/preview-banner.d.mts +2 -2
- package/dist/client/preview/preview-field.d.mts +4 -4
- package/dist/client/utils/auto-expand-fields.mjs +1 -1
- package/dist/client/views/collection/auto-form-fields.mjs +23 -19
- package/dist/client/views/collection/cells/complex-cells.mjs +1 -1
- package/dist/client/views/collection/columns/build-columns.mjs +1 -1
- package/dist/client/views/collection/columns/column-defaults.mjs +17 -4
- package/dist/client/views/collection/field-renderer.mjs +19 -7
- package/dist/client/views/collection/form-view.mjs +10 -6
- package/dist/client/views/collection/list-view.mjs +830 -0
- package/dist/client/views/collection/outline.mjs +363 -0
- package/dist/client/views/collection/table-view.mjs +25 -16
- package/dist/client/views/globals/global-form-view.mjs +47 -27
- package/dist/client/views/layout/admin-layout.d.mts +15 -1
- package/dist/client/views/layout/admin-layout.mjs +95 -31
- package/dist/client/views/layout/admin-sidebar.mjs +2 -2
- package/dist/client.d.mts +6 -6
- package/dist/client.mjs +1 -1
- package/dist/components/rich-text/rich-text-renderer.d.mts +2 -2
- package/dist/factories.d.mts +19 -0
- package/dist/factories.mjs +11 -0
- package/dist/fields.d.mts +4 -0
- package/dist/fields.mjs +5 -0
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +1 -1
- package/dist/modules/admin.d.mts +10 -0
- package/dist/modules/admin.mjs +9 -0
- package/dist/modules/audit.d.mts +5 -0
- package/dist/modules/audit.mjs +5 -0
- package/dist/server/augmentation/form-layout.d.mts +57 -2
- package/dist/server/augmentation/index.d.mts +3 -1
- package/dist/server/augmentation/shell.d.mts +48 -0
- package/dist/server/augmentation.d.mts +2 -1
- package/dist/server/codegen/admin-client-template.mjs +11 -4
- package/dist/server/fields/blocks.d.mts +9 -2
- package/dist/server/fields/blocks.mjs +1 -1
- package/dist/server/fields/index.d.mts +2 -2
- package/dist/server/fields/index.mjs +2 -2
- package/dist/server/fields/rich-text.d.mts +9 -2
- package/dist/server/fields/rich-text.mjs +1 -1
- package/dist/server/i18n/messages/cs.mjs +8 -0
- package/dist/server/i18n/messages/de.mjs +8 -0
- package/dist/server/i18n/messages/en.mjs +8 -0
- package/dist/server/i18n/messages/es.mjs +8 -0
- package/dist/server/i18n/messages/fr.mjs +8 -0
- package/dist/server/i18n/messages/pl.mjs +8 -0
- package/dist/server/i18n/messages/pt.mjs +8 -0
- package/dist/server/i18n/messages/sk.mjs +8 -0
- package/dist/server/modules/admin/.generated/module.d.mts +24 -19
- package/dist/server/modules/admin/.generated/module.mjs +5 -1
- package/dist/server/modules/admin/.generated/registries.d.mts +6 -4
- package/dist/server/modules/admin/client/.generated/module.d.mts +70 -70
- package/dist/server/modules/admin/client/.generated/module.mjs +3 -1
- package/dist/server/modules/admin/client/views/collection-form.d.mts +6 -0
- package/dist/server/modules/admin/client/views/collection-table.d.mts +6 -0
- package/dist/server/modules/admin/client/views/global-form.d.mts +6 -0
- package/dist/server/modules/admin/client/views/list-view.d.mts +6 -0
- package/dist/server/modules/admin/client/views/list-view.mjs +10 -0
- package/dist/server/modules/admin/collections/account.d.mts +50 -50
- package/dist/server/modules/admin/collections/admin-locks.d.mts +54 -54
- package/dist/server/modules/admin/collections/admin-preferences.d.mts +39 -39
- package/dist/server/modules/admin/collections/admin-saved-views.d.mts +47 -47
- package/dist/server/modules/admin/collections/apikey.d.mts +39 -39
- package/dist/server/modules/admin/collections/assets.d.mts +39 -39
- package/dist/server/modules/admin/collections/session.d.mts +42 -42
- package/dist/server/modules/admin/collections/user.d.mts +63 -63
- package/dist/server/modules/admin/collections/verification.d.mts +36 -36
- package/dist/server/modules/admin/dto/admin-config.dto.mjs +17 -0
- package/dist/server/modules/admin/index.d.mts +30 -31
- package/dist/server/modules/admin/routes/admin-config.d.mts +2 -17
- package/dist/server/modules/admin/routes/admin-config.mjs +21 -5
- package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
- package/dist/server/modules/admin/routes/execute-action.mjs +18 -12
- package/dist/server/modules/admin/routes/i18n-helpers.d.mts +4 -0
- package/dist/server/modules/admin/routes/locales.d.mts +2 -2
- package/dist/server/modules/admin/routes/preview.d.mts +24 -19
- package/dist/server/modules/admin/routes/preview.mjs +83 -62
- package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
- package/dist/server/modules/admin/routes/route-helpers.mjs +36 -1
- package/dist/server/modules/admin/routes/setup.d.mts +7 -14
- package/dist/server/modules/admin/routes/setup.mjs +16 -3
- package/dist/server/modules/admin/routes/translations.d.mts +4 -4
- package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
- package/dist/server/modules/admin/views/list-view.d.mts +8 -0
- package/dist/server/modules/admin/views/list-view.mjs +7 -0
- package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +41 -41
- package/dist/server/modules/audit/.generated/module.d.mts +6 -6
- package/dist/server/modules/audit/collections/audit-log.d.mts +87 -80
- package/dist/server/modules/audit/collections/audit-log.mjs +7 -2
- package/dist/server/modules/audit/config/localize-title.mjs +67 -0
- package/dist/server/modules/audit/index.d.mts +3 -2
- package/dist/server/modules/audit/jobs/audit-cleanup.d.mts +2 -2
- package/dist/server/modules/audit/log-audit-entry.d.mts +85 -0
- package/dist/server/modules/audit/log-audit-entry.mjs +125 -0
- package/dist/server/plugin.d.mts +1 -1
- package/dist/server/plugin.mjs +31 -31
- package/dist/server.d.mts +6 -4
- package/dist/server.mjs +9 -8
- package/dist/shared/preview-utils.d.mts +4 -4
- package/dist/shared/preview-utils.mjs +5 -7
- package/package.json +13 -3
- package/dist/client/hooks/use-audit-history.mjs +0 -38
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { getPreviewSecret } from "../../../../shared/preview-utils.mjs";
|
|
2
1
|
import { getApp, getCollectionState, getLocale, getSession } from "./route-helpers.mjs";
|
|
3
2
|
import { translateAdminMessage } from "./i18n-helpers.mjs";
|
|
4
3
|
import { z } from "zod";
|
|
5
4
|
import { ApiError, route } from "questpie";
|
|
6
|
-
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
7
5
|
|
|
8
6
|
//#region src/server/modules/admin/routes/preview.ts
|
|
9
7
|
/**
|
|
@@ -14,14 +12,72 @@ import { createHmac, timingSafeEqual } from "node:crypto";
|
|
|
14
12
|
*
|
|
15
13
|
* Browser-safe utilities (isDraftMode, createDraftModeCookie, etc.) are in @questpie/admin/shared
|
|
16
14
|
*/
|
|
15
|
+
const textEncoder = new TextEncoder();
|
|
16
|
+
const textDecoder = new TextDecoder();
|
|
17
|
+
function bytesFromBase64Input(input) {
|
|
18
|
+
if (typeof input === "string") return textEncoder.encode(input);
|
|
19
|
+
if (input instanceof Uint8Array) return input;
|
|
20
|
+
return new Uint8Array(input);
|
|
21
|
+
}
|
|
22
|
+
function bytesToBase64(bytes) {
|
|
23
|
+
let binary = "";
|
|
24
|
+
const chunkSize = 32768;
|
|
25
|
+
for (let i = 0; i < bytes.length; i += chunkSize) binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));
|
|
26
|
+
return btoa(binary);
|
|
27
|
+
}
|
|
28
|
+
function base64ToBytes(input) {
|
|
29
|
+
const binary = atob(input);
|
|
30
|
+
const bytes = new Uint8Array(binary.length);
|
|
31
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
32
|
+
return bytes;
|
|
33
|
+
}
|
|
17
34
|
function base64UrlEncode(input) {
|
|
18
|
-
return (
|
|
35
|
+
return bytesToBase64(bytesFromBase64Input(input)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
19
36
|
}
|
|
20
37
|
function base64UrlDecode(input) {
|
|
21
38
|
let base64 = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
22
39
|
const padding = base64.length % 4;
|
|
23
40
|
if (padding) base64 += "=".repeat(4 - padding);
|
|
24
|
-
return
|
|
41
|
+
return textDecoder.decode(base64ToBytes(base64));
|
|
42
|
+
}
|
|
43
|
+
async function signPayload(payload, secret) {
|
|
44
|
+
const subtle = globalThis.crypto?.subtle;
|
|
45
|
+
if (!subtle) throw new Error("[preview] Web Crypto API is required to sign preview tokens.");
|
|
46
|
+
const key = await subtle.importKey("raw", textEncoder.encode(secret), {
|
|
47
|
+
name: "HMAC",
|
|
48
|
+
hash: "SHA-256"
|
|
49
|
+
}, false, ["sign"]);
|
|
50
|
+
return base64UrlEncode(await subtle.sign("HMAC", key, textEncoder.encode(payload)));
|
|
51
|
+
}
|
|
52
|
+
function timingSafeEqualAscii(a, b) {
|
|
53
|
+
const aBytes = textEncoder.encode(a);
|
|
54
|
+
const bBytes = textEncoder.encode(b);
|
|
55
|
+
let diff = aBytes.length ^ bBytes.length;
|
|
56
|
+
const maxLength = Math.max(aBytes.length, bBytes.length);
|
|
57
|
+
for (let i = 0; i < maxLength; i++) diff |= (aBytes[i] ?? 0) ^ (bBytes[i] ?? 0);
|
|
58
|
+
return diff === 0;
|
|
59
|
+
}
|
|
60
|
+
function defaultPreviewSecret(ctx) {
|
|
61
|
+
return getApp(ctx).config.secret ?? "dev-preview-secret";
|
|
62
|
+
}
|
|
63
|
+
async function resolvePreviewSecret(source, ctx) {
|
|
64
|
+
const secret = typeof source === "function" ? await source(ctx) : source;
|
|
65
|
+
if (!secret) throw new Error("[preview] Preview token secret must not be empty.");
|
|
66
|
+
return secret;
|
|
67
|
+
}
|
|
68
|
+
function parsePreviewTokenPayload(encodedPayload) {
|
|
69
|
+
try {
|
|
70
|
+
const payload = JSON.parse(base64UrlDecode(encodedPayload));
|
|
71
|
+
if (!payload?.exp || typeof payload.exp !== "number") return { error: "invalidPayload" };
|
|
72
|
+
if (payload.exp < Date.now()) return { error: "tokenExpired" };
|
|
73
|
+
if (!payload.path || typeof payload.path !== "string") return { error: "invalidPath" };
|
|
74
|
+
return { payload };
|
|
75
|
+
} catch {
|
|
76
|
+
return { error: "invalidPayload" };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function verifyPreviewSignature(encodedPayload, signature, secret) {
|
|
80
|
+
return timingSafeEqualAscii(signature, await signPayload(encodedPayload, secret));
|
|
25
81
|
}
|
|
26
82
|
const mintPreviewTokenSchema = z.object({
|
|
27
83
|
path: z.string().min(1),
|
|
@@ -41,13 +97,11 @@ const DEFAULT_TTL_MS = 3600 * 1e3;
|
|
|
41
97
|
/**
|
|
42
98
|
* Create preview-related RPC functions.
|
|
43
99
|
*
|
|
44
|
-
* @param secret - Secret key for signing tokens
|
|
100
|
+
* @param secret - Secret key or resolver for signing tokens. Defaults to
|
|
101
|
+
* `app.config.secret` from the current route context.
|
|
45
102
|
* @returns Object with preview functions
|
|
46
103
|
*/
|
|
47
|
-
function createPreviewFunctions(secret) {
|
|
48
|
-
const signPayload = (payload) => {
|
|
49
|
-
return base64UrlEncode(createHmac("sha256", secret).update(payload).digest());
|
|
50
|
-
};
|
|
104
|
+
function createPreviewFunctions(secret = defaultPreviewSecret) {
|
|
51
105
|
return {
|
|
52
106
|
mintPreviewToken: route().post().schema(mintPreviewTokenSchema).outputSchema(mintPreviewTokenOutputSchema).handler(async (ctx) => {
|
|
53
107
|
const { input } = ctx;
|
|
@@ -62,7 +116,7 @@ function createPreviewFunctions(secret) {
|
|
|
62
116
|
};
|
|
63
117
|
const encodedPayload = base64UrlEncode(JSON.stringify(payload));
|
|
64
118
|
return {
|
|
65
|
-
token: `${encodedPayload}.${signPayload(encodedPayload)}`,
|
|
119
|
+
token: `${encodedPayload}.${await signPayload(encodedPayload, await resolvePreviewSecret(secret, ctx))}`,
|
|
66
120
|
expiresAt
|
|
67
121
|
};
|
|
68
122
|
}),
|
|
@@ -76,41 +130,19 @@ function createPreviewFunctions(secret) {
|
|
|
76
130
|
valid: false,
|
|
77
131
|
error: t("preview.invalidTokenFormat")
|
|
78
132
|
};
|
|
79
|
-
|
|
80
|
-
const signatureBuffer = Uint8Array.from(Buffer.from(signature));
|
|
81
|
-
const expectedBuffer = Uint8Array.from(Buffer.from(expectedSignature));
|
|
82
|
-
if (signatureBuffer.length !== expectedBuffer.length) return {
|
|
133
|
+
if (!await verifyPreviewSignature(encodedPayload, signature, await resolvePreviewSecret(secret, ctx))) return {
|
|
83
134
|
valid: false,
|
|
84
135
|
error: t("preview.invalidSignature")
|
|
85
136
|
};
|
|
86
|
-
|
|
137
|
+
const parsed = parsePreviewTokenPayload(encodedPayload);
|
|
138
|
+
if ("error" in parsed) return {
|
|
87
139
|
valid: false,
|
|
88
|
-
error: t(
|
|
140
|
+
error: t(`preview.${parsed.error}`)
|
|
141
|
+
};
|
|
142
|
+
return {
|
|
143
|
+
valid: true,
|
|
144
|
+
path: parsed.payload.path
|
|
89
145
|
};
|
|
90
|
-
try {
|
|
91
|
-
const payload = JSON.parse(base64UrlDecode(encodedPayload));
|
|
92
|
-
if (!payload?.exp || typeof payload.exp !== "number") return {
|
|
93
|
-
valid: false,
|
|
94
|
-
error: t("preview.invalidPayload")
|
|
95
|
-
};
|
|
96
|
-
if (payload.exp < Date.now()) return {
|
|
97
|
-
valid: false,
|
|
98
|
-
error: t("preview.tokenExpired")
|
|
99
|
-
};
|
|
100
|
-
if (!payload.path || typeof payload.path !== "string") return {
|
|
101
|
-
valid: false,
|
|
102
|
-
error: t("preview.invalidPath")
|
|
103
|
-
};
|
|
104
|
-
return {
|
|
105
|
-
valid: true,
|
|
106
|
-
path: payload.path
|
|
107
|
-
};
|
|
108
|
-
} catch {
|
|
109
|
-
return {
|
|
110
|
-
valid: false,
|
|
111
|
-
error: t("preview.invalidPayload")
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
146
|
})
|
|
115
147
|
};
|
|
116
148
|
}
|
|
@@ -122,47 +154,35 @@ function createPreviewFunctions(secret) {
|
|
|
122
154
|
* @param secret - The secret used to sign the token
|
|
123
155
|
* @returns The payload if valid, null otherwise
|
|
124
156
|
*/
|
|
125
|
-
function verifyPreviewTokenDirect(token, secret) {
|
|
157
|
+
async function verifyPreviewTokenDirect(token, secret) {
|
|
126
158
|
const [encodedPayload, signature] = token.split(".");
|
|
127
159
|
if (!encodedPayload || !signature) return null;
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
if (signatureBuffer.length !== expectedBuffer.length) return null;
|
|
132
|
-
if (!timingSafeEqual(signatureBuffer, expectedBuffer)) return null;
|
|
133
|
-
try {
|
|
134
|
-
const payload = JSON.parse(base64UrlDecode(encodedPayload));
|
|
135
|
-
if (!payload?.exp || typeof payload.exp !== "number") return null;
|
|
136
|
-
if (payload.exp < Date.now()) return null;
|
|
137
|
-
if (!payload.path || typeof payload.path !== "string") return null;
|
|
138
|
-
return payload;
|
|
139
|
-
} catch {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
160
|
+
if (!await verifyPreviewSignature(encodedPayload, signature, secret)) return null;
|
|
161
|
+
const parsed = parsePreviewTokenPayload(encodedPayload);
|
|
162
|
+
return "payload" in parsed ? parsed.payload : null;
|
|
142
163
|
}
|
|
143
164
|
/**
|
|
144
165
|
* Create a preview token verifier with bound secret.
|
|
145
166
|
* Use this in route handlers to avoid passing secret repeatedly.
|
|
146
167
|
*
|
|
147
|
-
* @param secret - The secret used to sign tokens
|
|
168
|
+
* @param secret - The secret used to sign tokens.
|
|
148
169
|
* @returns A verify function that only needs the token
|
|
149
170
|
*
|
|
150
171
|
* @example
|
|
151
172
|
* ```ts
|
|
152
173
|
* // Create once at module level
|
|
153
|
-
* const verifyPreviewToken = createPreviewTokenVerifier();
|
|
174
|
+
* const verifyPreviewToken = createPreviewTokenVerifier(secret);
|
|
154
175
|
*
|
|
155
176
|
* // Use in route handler
|
|
156
|
-
* const payload = verifyPreviewToken(token);
|
|
177
|
+
* const payload = await verifyPreviewToken(token);
|
|
157
178
|
* if (!payload) {
|
|
158
179
|
* return new Response("Invalid token", { status: 401 });
|
|
159
180
|
* }
|
|
160
181
|
* ```
|
|
161
182
|
*/
|
|
162
183
|
function createPreviewTokenVerifier(secret) {
|
|
163
|
-
const resolvedSecret = secret ?? getPreviewSecret();
|
|
164
184
|
return (token) => {
|
|
165
|
-
return verifyPreviewTokenDirect(token,
|
|
185
|
+
return verifyPreviewTokenDirect(token, secret);
|
|
166
186
|
};
|
|
167
187
|
}
|
|
168
188
|
const getPreviewUrlSchema = z.object({
|
|
@@ -226,11 +246,12 @@ const getPreviewUrl = route().post().schema(getPreviewUrlSchema).outputSchema(ge
|
|
|
226
246
|
}
|
|
227
247
|
});
|
|
228
248
|
/**
|
|
229
|
-
* Default preview functions bundle
|
|
249
|
+
* Default preview functions bundle. The route handlers resolve the token
|
|
250
|
+
* secret from `app.config.secret` at request time.
|
|
230
251
|
* Used by the `adminModule` to register preview RPC functions.
|
|
231
252
|
*/
|
|
232
253
|
const previewFunctions = {
|
|
233
|
-
...createPreviewFunctions(
|
|
254
|
+
...createPreviewFunctions(),
|
|
234
255
|
getPreviewUrl
|
|
235
256
|
};
|
|
236
257
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as questpie131 from "questpie";
|
|
2
2
|
|
|
3
3
|
//#region src/server/modules/admin/routes/reactive.d.ts
|
|
4
4
|
|
|
@@ -13,7 +13,7 @@ import * as questpie80 from "questpie";
|
|
|
13
13
|
* Batch reactive endpoint.
|
|
14
14
|
* Executes multiple reactive handlers in a single request.
|
|
15
15
|
*/
|
|
16
|
-
declare const batchReactive:
|
|
16
|
+
declare const batchReactive: questpie131.JsonRouteDefinition<{
|
|
17
17
|
collection: string;
|
|
18
18
|
type: "collection" | "global";
|
|
19
19
|
requests: {
|
|
@@ -27,12 +27,12 @@ declare const batchReactive: questpie80.JsonRouteDefinition<{
|
|
|
27
27
|
}[];
|
|
28
28
|
formData?: Record<string, unknown> | undefined;
|
|
29
29
|
prevData?: Record<string, unknown> | null | undefined;
|
|
30
|
-
}, any,
|
|
30
|
+
}, any, questpie131.JsonRouteParams>;
|
|
31
31
|
/**
|
|
32
32
|
* Dynamic options endpoint.
|
|
33
33
|
* Fetches options for select/relation fields with search and pagination.
|
|
34
34
|
*/
|
|
35
|
-
declare const fieldOptions:
|
|
35
|
+
declare const fieldOptions: questpie131.JsonRouteDefinition<{
|
|
36
36
|
collection: string;
|
|
37
37
|
type: "collection" | "global";
|
|
38
38
|
field: string;
|
|
@@ -41,12 +41,12 @@ declare const fieldOptions: questpie80.JsonRouteDefinition<{
|
|
|
41
41
|
page: number;
|
|
42
42
|
limit: number;
|
|
43
43
|
siblingData?: Record<string, unknown> | null | undefined;
|
|
44
|
-
}, any,
|
|
44
|
+
}, any, questpie131.JsonRouteParams>;
|
|
45
45
|
/**
|
|
46
46
|
* Reactive functions bundle.
|
|
47
47
|
*/
|
|
48
48
|
declare const reactiveFunctions: {
|
|
49
|
-
readonly batchReactive:
|
|
49
|
+
readonly batchReactive: questpie131.JsonRouteDefinition<{
|
|
50
50
|
collection: string;
|
|
51
51
|
type: "collection" | "global";
|
|
52
52
|
requests: {
|
|
@@ -60,8 +60,8 @@ declare const reactiveFunctions: {
|
|
|
60
60
|
}[];
|
|
61
61
|
formData?: Record<string, unknown> | undefined;
|
|
62
62
|
prevData?: Record<string, unknown> | null | undefined;
|
|
63
|
-
}, any,
|
|
64
|
-
readonly fieldOptions:
|
|
63
|
+
}, any, questpie131.JsonRouteParams>;
|
|
64
|
+
readonly fieldOptions: questpie131.JsonRouteDefinition<{
|
|
65
65
|
collection: string;
|
|
66
66
|
type: "collection" | "global";
|
|
67
67
|
field: string;
|
|
@@ -70,7 +70,7 @@ declare const reactiveFunctions: {
|
|
|
70
70
|
page: number;
|
|
71
71
|
limit: number;
|
|
72
72
|
siblingData?: Record<string, unknown> | null | undefined;
|
|
73
|
-
}, any,
|
|
73
|
+
}, any, questpie131.JsonRouteParams>;
|
|
74
74
|
};
|
|
75
75
|
//#endregion
|
|
76
76
|
export { batchReactive, fieldOptions, reactiveFunctions };
|
|
@@ -60,6 +60,41 @@ function getAdminConfig(app) {
|
|
|
60
60
|
return getAppState(app).config?.admin || {};
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
|
+
* Get registered collection definitions from the current or legacy app shape.
|
|
64
|
+
*/
|
|
65
|
+
function getCollections(app) {
|
|
66
|
+
const appRec = app;
|
|
67
|
+
if (typeof appRec.getCollections === "function") return appRec.getCollections();
|
|
68
|
+
return getAppState(app).collections ?? {};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get a registered collection definition by slug.
|
|
72
|
+
*/
|
|
73
|
+
function getCollection(app, collectionSlug) {
|
|
74
|
+
return getCollections(app)[collectionSlug];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get collection CRUD APIs from the current or legacy app shape.
|
|
78
|
+
*/
|
|
79
|
+
function getCollectionCrud(app, collectionSlug) {
|
|
80
|
+
const appRec = app;
|
|
81
|
+
return appRec.collections?.[collectionSlug] ?? appRec.api?.collections?.[collectionSlug] ?? appRec._api?.collections?.[collectionSlug];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get all collection CRUD APIs from the current or legacy app shape.
|
|
85
|
+
*/
|
|
86
|
+
function getCollectionCruds(app) {
|
|
87
|
+
const appRec = app;
|
|
88
|
+
return appRec.collections ?? appRec.api?.collections ?? appRec._api?.collections;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get all global CRUD APIs from the current or legacy app shape.
|
|
92
|
+
*/
|
|
93
|
+
function getGlobalCruds(app) {
|
|
94
|
+
const appRec = app;
|
|
95
|
+
return appRec.globals ?? appRec.api?.globals ?? appRec._api?.globals;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
63
98
|
* Build a ReactiveServerContext from route handler context.
|
|
64
99
|
*/
|
|
65
100
|
function buildServerContext(ctx) {
|
|
@@ -73,4 +108,4 @@ function buildServerContext(ctx) {
|
|
|
73
108
|
}
|
|
74
109
|
|
|
75
110
|
//#endregion
|
|
76
|
-
export { buildServerContext, getAccessContext, getAdminConfig, getApp, getAppState, getCollectionState, getGlobalState, getLocale, getSession };
|
|
111
|
+
export { buildServerContext, getAccessContext, getAdminConfig, getApp, getAppState, getCollection, getCollectionCrud, getCollectionCruds, getCollectionState, getGlobalCruds, getGlobalState, getLocale, getSession };
|
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as questpie139 from "questpie";
|
|
2
2
|
|
|
3
3
|
//#region src/server/modules/admin/routes/setup.d.ts
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* Setup Functions
|
|
7
|
-
*
|
|
8
|
-
* Built-in functions for bootstrapping the first admin user.
|
|
9
|
-
* Solves the chicken-and-egg problem where invitation-based systems
|
|
10
|
-
* need an existing admin to create the first invitation.
|
|
11
|
-
*/
|
|
12
5
|
/**
|
|
13
6
|
* Check if setup is required (no admin users exist in the system).
|
|
14
7
|
*
|
|
@@ -20,7 +13,7 @@ import * as questpie72 from "questpie";
|
|
|
20
13
|
* }
|
|
21
14
|
* ```
|
|
22
15
|
*/
|
|
23
|
-
declare const isSetupRequired:
|
|
16
|
+
declare const isSetupRequired: questpie139.JsonRouteDefinition<Record<string, never>, any, questpie139.JsonRouteParams>;
|
|
24
17
|
/**
|
|
25
18
|
* Create the first admin user in the system.
|
|
26
19
|
* This function only works when no admin users exist (setup mode).
|
|
@@ -43,21 +36,21 @@ declare const isSetupRequired: questpie72.JsonRouteDefinition<Record<string, nev
|
|
|
43
36
|
* }
|
|
44
37
|
* ```
|
|
45
38
|
*/
|
|
46
|
-
declare const createFirstAdmin:
|
|
39
|
+
declare const createFirstAdmin: questpie139.JsonRouteDefinition<{
|
|
47
40
|
email: string;
|
|
48
41
|
password: string;
|
|
49
42
|
name: string;
|
|
50
|
-
}, any,
|
|
43
|
+
}, any, questpie139.JsonRouteParams>;
|
|
51
44
|
/**
|
|
52
45
|
* Bundle of setup-related functions.
|
|
53
46
|
*/
|
|
54
47
|
declare const setupFunctions: {
|
|
55
|
-
readonly isSetupRequired:
|
|
56
|
-
readonly createFirstAdmin:
|
|
48
|
+
readonly isSetupRequired: questpie139.JsonRouteDefinition<Record<string, never>, any, questpie139.JsonRouteParams>;
|
|
49
|
+
readonly createFirstAdmin: questpie139.JsonRouteDefinition<{
|
|
57
50
|
email: string;
|
|
58
51
|
password: string;
|
|
59
52
|
name: string;
|
|
60
|
-
}, any,
|
|
53
|
+
}, any, questpie139.JsonRouteParams>;
|
|
61
54
|
};
|
|
62
55
|
//#endregion
|
|
63
56
|
export { createFirstAdmin, isSetupRequired, setupFunctions };
|
|
@@ -28,6 +28,17 @@ const createFirstAdminOutputSchema = z.object({
|
|
|
28
28
|
}).optional(),
|
|
29
29
|
error: z.string().optional()
|
|
30
30
|
});
|
|
31
|
+
const adminUserContractMessage = "QUESTPIE Admin requires the canonical Better Auth \"user\" collection from adminModule/starterModule. The current \"user\" collection is missing required admin auth fields. Do not replace collection(\"user\") from scratch; merge starterModule.collections.user or adminModule.collections.user and extend it.";
|
|
32
|
+
function getAdminUserTableContract(userCollection) {
|
|
33
|
+
const table = userCollection?.table;
|
|
34
|
+
const missing = [
|
|
35
|
+
"id",
|
|
36
|
+
"role",
|
|
37
|
+
"emailVerified"
|
|
38
|
+
].filter((field$1) => !table?.[field$1]);
|
|
39
|
+
if (missing.length > 0) throw new Error(`${adminUserContractMessage} Missing field(s): ${missing.join(", ")}.`);
|
|
40
|
+
return table;
|
|
41
|
+
}
|
|
31
42
|
/**
|
|
32
43
|
* Check if setup is required (no admin users exist in the system).
|
|
33
44
|
*
|
|
@@ -42,7 +53,8 @@ const createFirstAdminOutputSchema = z.object({
|
|
|
42
53
|
const isSetupRequired = route().post().schema(isSetupRequiredSchema).outputSchema(isSetupRequiredOutputSchema).handler(async (ctx) => {
|
|
43
54
|
const app = getApp(ctx);
|
|
44
55
|
const userCollection = app.getCollectionConfig("user");
|
|
45
|
-
|
|
56
|
+
const userTable = getAdminUserTableContract(userCollection);
|
|
57
|
+
return { required: (await app.db.select({ count: sql$1`count(*)::int` }).from(userCollection.table).where(eq(userTable.role, "admin")))[0].count === 0 };
|
|
46
58
|
});
|
|
47
59
|
/**
|
|
48
60
|
* Create the first admin user in the system.
|
|
@@ -72,7 +84,8 @@ const createFirstAdmin = route().post().schema(createFirstAdminSchema).outputSch
|
|
|
72
84
|
const t = (key, params) => translateAdminMessage(locale, key, params);
|
|
73
85
|
const input = ctx.input;
|
|
74
86
|
const userCollection = app.getCollectionConfig("user");
|
|
75
|
-
|
|
87
|
+
const userTable = getAdminUserTableContract(userCollection);
|
|
88
|
+
if ((await app.db.select({ count: sql$1`count(*)::int` }).from(userCollection.table).where(eq(userTable.role, "admin")))[0].count > 0) return {
|
|
76
89
|
success: false,
|
|
77
90
|
error: t("auth.setupAlreadyCompleted")
|
|
78
91
|
};
|
|
@@ -89,7 +102,7 @@ const createFirstAdmin = route().post().schema(createFirstAdminSchema).outputSch
|
|
|
89
102
|
await app.db.update(userCollection.table).set({
|
|
90
103
|
role: "admin",
|
|
91
104
|
emailVerified: true
|
|
92
|
-
}).where(eq(
|
|
105
|
+
}).where(eq(userTable.id, signUpResult.user.id));
|
|
93
106
|
return {
|
|
94
107
|
success: true,
|
|
95
108
|
user: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as questpie458 from "questpie";
|
|
2
2
|
|
|
3
3
|
//#region src/server/modules/admin/routes/translations.d.ts
|
|
4
4
|
|
|
@@ -17,10 +17,10 @@ import * as questpie397 from "questpie";
|
|
|
17
17
|
* Bundle of translation-related functions.
|
|
18
18
|
*/
|
|
19
19
|
declare const translationFunctions: {
|
|
20
|
-
readonly getAdminTranslations:
|
|
20
|
+
readonly getAdminTranslations: questpie458.JsonRouteDefinition<{
|
|
21
21
|
locale: string;
|
|
22
|
-
}, any,
|
|
23
|
-
readonly getAdminLocales:
|
|
22
|
+
}, any, questpie458.JsonRouteParams>;
|
|
23
|
+
readonly getAdminLocales: questpie458.JsonRouteDefinition<Record<string, never> | undefined, any, questpie458.JsonRouteParams>;
|
|
24
24
|
};
|
|
25
25
|
//#endregion
|
|
26
26
|
export { translationFunctions };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as questpie147 from "questpie";
|
|
2
2
|
|
|
3
3
|
//#region src/server/modules/admin/routes/widget-data.d.ts
|
|
4
4
|
|
|
@@ -20,13 +20,13 @@ import * as questpie16 from "questpie";
|
|
|
20
20
|
* const data = await client.routes.fetchWidgetData({ widgetId: "my-widget" });
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
|
-
declare const fetchWidgetData:
|
|
23
|
+
declare const fetchWidgetData: questpie147.JsonRouteDefinition<{
|
|
24
24
|
widgetId: string;
|
|
25
|
-
}, any,
|
|
25
|
+
}, any, questpie147.JsonRouteParams>;
|
|
26
26
|
declare const widgetDataFunctions: {
|
|
27
|
-
readonly fetchWidgetData:
|
|
27
|
+
readonly fetchWidgetData: questpie147.JsonRouteDefinition<{
|
|
28
28
|
widgetId: string;
|
|
29
|
-
}, any,
|
|
29
|
+
}, any, questpie147.JsonRouteParams>;
|
|
30
30
|
};
|
|
31
31
|
//#endregion
|
|
32
32
|
export { fetchWidgetData, widgetDataFunctions };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ListViewConfig } from "../../../augmentation/form-layout.mjs";
|
|
2
|
+
import { ViewDefinition } from "../../../augmentation/views.mjs";
|
|
3
|
+
import "../../../augmentation.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/server/modules/admin/views/list-view.d.ts
|
|
6
|
+
declare const _default: ViewDefinition<"list-view", "list", ListViewConfig>;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { _default };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { FilterOperator, FilterRule, SortConfig, ViewConfiguration } from "../../../../shared/types/saved-views.types.mjs";
|
|
2
|
-
import * as
|
|
3
|
-
import * as
|
|
2
|
+
import * as questpie_shared16 from "questpie/shared";
|
|
3
|
+
import * as questpie69 from "questpie";
|
|
4
4
|
import * as questpie_src_server_modules_core_fields_email_js1 from "questpie/src/server/modules/core/fields/email.js";
|
|
5
5
|
import * as questpie_src_server_modules_core_fields_json_js1 from "questpie/src/server/modules/core/fields/json.js";
|
|
6
|
-
import * as
|
|
7
|
-
import * as
|
|
6
|
+
import * as drizzle_orm_pg_core22 from "drizzle-orm/pg-core";
|
|
7
|
+
import * as drizzle_orm5 from "drizzle-orm";
|
|
8
8
|
|
|
9
9
|
//#region src/server/modules/admin-preferences/collections/saved-views.d.ts
|
|
10
10
|
|
|
@@ -31,61 +31,61 @@ import * as drizzle_orm0 from "drizzle-orm";
|
|
|
31
31
|
* });
|
|
32
32
|
* ```
|
|
33
33
|
*/
|
|
34
|
-
declare const savedViewsCollection:
|
|
35
|
-
readonly text: typeof
|
|
36
|
-
readonly textarea: typeof
|
|
34
|
+
declare const savedViewsCollection: questpie69.CollectionBuilder<questpie_shared16.Override<questpie_shared16.Override<questpie69.EmptyCollectionState<"admin_saved_views", undefined, {
|
|
35
|
+
readonly text: typeof questpie69.text;
|
|
36
|
+
readonly textarea: typeof questpie69.textarea;
|
|
37
37
|
readonly email: typeof questpie_src_server_modules_core_fields_email_js1.email;
|
|
38
|
-
readonly url: typeof
|
|
39
|
-
readonly number: typeof
|
|
40
|
-
readonly boolean: typeof
|
|
41
|
-
readonly date: typeof
|
|
42
|
-
readonly datetime: typeof
|
|
43
|
-
readonly time: typeof
|
|
44
|
-
readonly select: typeof
|
|
45
|
-
readonly upload: typeof
|
|
46
|
-
readonly relation: typeof
|
|
47
|
-
readonly object: typeof
|
|
38
|
+
readonly url: typeof questpie69.url;
|
|
39
|
+
readonly number: typeof questpie69.number;
|
|
40
|
+
readonly boolean: typeof questpie69.boolean;
|
|
41
|
+
readonly date: typeof questpie69.date;
|
|
42
|
+
readonly datetime: typeof questpie69.datetime;
|
|
43
|
+
readonly time: typeof questpie69.time;
|
|
44
|
+
readonly select: typeof questpie69.select;
|
|
45
|
+
readonly upload: typeof questpie69.upload;
|
|
46
|
+
readonly relation: typeof questpie69.relation;
|
|
47
|
+
readonly object: typeof questpie69.object;
|
|
48
48
|
readonly json: typeof questpie_src_server_modules_core_fields_json_js1.json;
|
|
49
|
-
readonly from: typeof
|
|
49
|
+
readonly from: typeof questpie69.from;
|
|
50
50
|
}>, {
|
|
51
51
|
fields: {
|
|
52
|
-
readonly userId:
|
|
53
|
-
readonly collectionName:
|
|
54
|
-
readonly name:
|
|
55
|
-
readonly configuration:
|
|
56
|
-
readonly isDefault:
|
|
52
|
+
readonly userId: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
|
|
53
|
+
readonly collectionName: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
|
|
54
|
+
readonly name: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
|
|
55
|
+
readonly configuration: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgJsonbBuilder>;
|
|
56
|
+
readonly isDefault: drizzle_orm5.HasDefault<drizzle_orm_pg_core22.PgBooleanBuilder>;
|
|
57
57
|
};
|
|
58
58
|
localized: readonly string[];
|
|
59
59
|
fieldDefinitions: {
|
|
60
|
-
readonly userId:
|
|
60
|
+
readonly userId: questpie69.FieldWithMethods<Omit<questpie69.TextFieldState, "notNull" | "column"> & {
|
|
61
61
|
notNull: true;
|
|
62
|
-
column:
|
|
62
|
+
column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
|
|
63
63
|
} & {
|
|
64
|
-
label:
|
|
65
|
-
},
|
|
66
|
-
readonly collectionName:
|
|
64
|
+
label: questpie_shared16.I18nText;
|
|
65
|
+
}, questpie69.TextFieldMethods>;
|
|
66
|
+
readonly collectionName: questpie69.FieldWithMethods<Omit<questpie69.TextFieldState, "notNull" | "column"> & {
|
|
67
67
|
notNull: true;
|
|
68
|
-
column:
|
|
68
|
+
column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
|
|
69
69
|
} & {
|
|
70
|
-
label:
|
|
71
|
-
},
|
|
72
|
-
readonly name:
|
|
70
|
+
label: questpie_shared16.I18nText;
|
|
71
|
+
}, questpie69.TextFieldMethods>;
|
|
72
|
+
readonly name: questpie69.FieldWithMethods<Omit<questpie69.TextFieldState, "notNull" | "column"> & {
|
|
73
73
|
notNull: true;
|
|
74
|
-
column:
|
|
74
|
+
column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgVarcharBuilder<[string, ...string[]]>>;
|
|
75
75
|
} & {
|
|
76
|
-
label:
|
|
77
|
-
},
|
|
78
|
-
readonly configuration:
|
|
76
|
+
label: questpie_shared16.I18nText;
|
|
77
|
+
}, questpie69.TextFieldMethods>;
|
|
78
|
+
readonly configuration: questpie69.Field<Omit<questpie69.JsonFieldState, "notNull" | "column"> & {
|
|
79
79
|
notNull: true;
|
|
80
|
-
column:
|
|
80
|
+
column: drizzle_orm5.NotNull<drizzle_orm_pg_core22.PgJsonbBuilder>;
|
|
81
81
|
} & {
|
|
82
|
-
label:
|
|
82
|
+
label: questpie_shared16.I18nText;
|
|
83
83
|
}>;
|
|
84
|
-
readonly isDefault:
|
|
84
|
+
readonly isDefault: questpie69.Field<Omit<questpie69.BooleanFieldState, "column" | "hasDefault"> & {
|
|
85
85
|
hasDefault: true;
|
|
86
|
-
column:
|
|
86
|
+
column: drizzle_orm5.HasDefault<drizzle_orm_pg_core22.PgBooleanBuilder>;
|
|
87
87
|
} & {
|
|
88
|
-
label:
|
|
88
|
+
label: questpie_shared16.I18nText;
|
|
89
89
|
}>;
|
|
90
90
|
};
|
|
91
91
|
}>, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { auditLogCollection } from "../collections/audit-log.mjs";
|
|
2
2
|
import { auditCleanupJob } from "../jobs/audit-cleanup.mjs";
|
|
3
|
-
import * as
|
|
3
|
+
import * as questpie16 from "questpie";
|
|
4
4
|
|
|
5
5
|
//#region src/server/modules/audit/.generated/module.d.ts
|
|
6
6
|
interface AuditCollections {
|
|
@@ -43,13 +43,13 @@ declare const _module: {
|
|
|
43
43
|
app: {
|
|
44
44
|
hooks: {
|
|
45
45
|
collections: {
|
|
46
|
-
afterChange: (ctx:
|
|
47
|
-
afterDelete: (ctx:
|
|
48
|
-
afterTransition: (ctx:
|
|
46
|
+
afterChange: (ctx: questpie16.GlobalCollectionHookContext) => Promise<void>;
|
|
47
|
+
afterDelete: (ctx: questpie16.GlobalCollectionHookContext) => Promise<void>;
|
|
48
|
+
afterTransition: (ctx: questpie16.GlobalCollectionTransitionHookContext) => Promise<void>;
|
|
49
49
|
};
|
|
50
50
|
globals: {
|
|
51
|
-
afterChange: (ctx:
|
|
52
|
-
afterTransition: (ctx:
|
|
51
|
+
afterChange: (ctx: questpie16.GlobalGlobalHookContext) => Promise<void>;
|
|
52
|
+
afterTransition: (ctx: questpie16.GlobalGlobalTransitionHookContext) => Promise<void>;
|
|
53
53
|
};
|
|
54
54
|
};
|
|
55
55
|
};
|