@questpie/admin 3.0.4 → 3.0.6
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 +99 -1
- package/dist/client/builder/types/field-types.d.mts +11 -0
- package/dist/client/components/blocks/block-editor-layout.mjs +2 -2
- package/dist/client/components/blocks/block-library-sidebar.mjs +89 -61
- package/dist/client/components/brand-logo.d.mts +25 -0
- package/dist/client/components/brand-logo.mjs +174 -0
- package/dist/client/components/media/media-grid.mjs +95 -78
- package/dist/client/components/primitives/select-multi.mjs +388 -368
- package/dist/client/components/primitives/select-single.mjs +344 -331
- package/dist/client/components/widgets/chart-widget.mjs +78 -62
- package/dist/client/components/widgets/progress-widget.mjs +39 -37
- package/dist/client/components/widgets/quick-actions-widget.mjs +111 -90
- package/dist/client/components/widgets/recent-items-widget.mjs +40 -38
- package/dist/client/components/widgets/table-widget.mjs +4 -3
- package/dist/client/components/widgets/timeline-widget.mjs +92 -74
- package/dist/client/components/widgets/value-widget.mjs +164 -144
- package/dist/client/create-admin-client.d.mts +7 -0
- package/dist/client/create-admin-client.mjs +25 -0
- package/dist/client/hooks/use-brand.d.mts +22 -0
- package/dist/client/hooks/use-brand.mjs +52 -0
- package/dist/client/hooks/use-server-actions.mjs +21 -16
- 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-banner.mjs +75 -46
- package/dist/client/runtime/index.mjs +1 -1
- package/dist/client/runtime/provider.d.mts +4 -0
- package/dist/client/runtime/provider.mjs +38 -8
- package/dist/client/styles/base.css +4 -0
- package/dist/client/types/admin-config.d.mts +24 -0
- package/dist/client/views/auth/auth-layout.d.mts +6 -1
- package/dist/client/views/auth/auth-layout.mjs +116 -102
- package/dist/client/views/collection/auto-form-fields.mjs +2 -0
- package/dist/client/views/collection/field-renderer.mjs +3 -2
- package/dist/client/views/collection/table-view.mjs +26 -26
- package/dist/client/views/globals/global-form-view.mjs +908 -863
- package/dist/client/views/layout/admin-layout.mjs +151 -131
- package/dist/client/views/layout/admin-router.mjs +297 -180
- package/dist/client/views/layout/admin-sidebar.mjs +178 -156
- package/dist/client/views/pages/accept-invite-page.mjs +122 -144
- package/dist/client/views/pages/forgot-password-page.mjs +22 -30
- package/dist/client/views/pages/invite-page.mjs +24 -33
- package/dist/client/views/pages/login-page.mjs +24 -32
- package/dist/client/views/pages/reset-password-page.mjs +77 -92
- package/dist/client/views/pages/setup-page.mjs +73 -65
- package/dist/client.d.mts +6 -2
- package/dist/client.mjs +5 -2
- package/dist/index.d.mts +6 -2
- package/dist/index.mjs +5 -2
- package/dist/server/augmentation/dashboard.d.mts +23 -5
- package/dist/server/augmentation/form-layout.d.mts +10 -0
- package/dist/server/augmentation/index.d.mts +1 -1
- package/dist/server/augmentation.d.mts +1 -1
- package/dist/server/i18n/index.mjs +13 -7
- package/dist/server/i18n/messages/cs.mjs +391 -1
- package/dist/server/i18n/messages/de.mjs +389 -1
- package/dist/server/i18n/messages/en.mjs +102 -0
- package/dist/server/i18n/messages/es.mjs +389 -1
- package/dist/server/i18n/messages/fr.mjs +389 -1
- package/dist/server/i18n/messages/pl.mjs +393 -1
- package/dist/server/i18n/messages/pt.mjs +386 -1
- package/dist/server/i18n/messages/sk.mjs +133 -1
- package/dist/server/modules/admin/collections/account.d.mts +50 -50
- package/dist/server/modules/admin/collections/admin-locks.d.mts +53 -53
- 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 +68 -68
- package/dist/server/modules/admin/collections/assets.d.mts +20 -20
- package/dist/server/modules/admin/collections/session.d.mts +42 -42
- package/dist/server/modules/admin/collections/user.d.mts +32 -32
- package/dist/server/modules/admin/collections/verification.d.mts +36 -36
- package/dist/server/modules/admin/dto/admin-config.dto.mjs +19 -1
- package/dist/server/modules/admin/routes/admin-config.d.mts +2 -2
- package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
- package/dist/server/modules/admin/routes/execute-action.mjs +34 -28
- package/dist/server/modules/admin/routes/i18n-helpers.mjs +34 -0
- package/dist/server/modules/admin/routes/locales.d.mts +2 -2
- package/dist/server/modules/admin/routes/preview.d.mts +11 -11
- package/dist/server/modules/admin/routes/preview.mjs +25 -17
- package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
- package/dist/server/modules/admin/routes/route-helpers.mjs +1 -1
- package/dist/server/modules/admin/routes/setup.mjs +10 -7
- 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-preferences/collections/saved-views.d.mts +23 -23
- package/dist/server.d.mts +4 -4
- package/dist/shared/preview-utils.d.mts +34 -1
- package/dist/shared/preview-utils.mjs +79 -1
- package/dist/shared.d.mts +2 -2
- package/dist/shared.mjs +2 -2
- package/package.json +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ServerActionDefinition, ServerActionResult } from "../../../augmentation/actions.mjs";
|
|
2
2
|
import "../../../augmentation.mjs";
|
|
3
3
|
import { App } from "./route-helpers.mjs";
|
|
4
|
-
import * as
|
|
4
|
+
import * as questpie135 from "questpie";
|
|
5
5
|
|
|
6
6
|
//#region src/server/modules/admin/routes/execute-action.d.ts
|
|
7
7
|
|
|
@@ -56,37 +56,37 @@ declare function executeAction(app: App, request: ExecuteActionRequest, session?
|
|
|
56
56
|
* });
|
|
57
57
|
* ```
|
|
58
58
|
*/
|
|
59
|
-
declare const executeActionFn:
|
|
59
|
+
declare const executeActionFn: questpie135.JsonRouteDefinition<{
|
|
60
60
|
collection: string;
|
|
61
61
|
actionId: string;
|
|
62
62
|
itemId?: string | undefined;
|
|
63
63
|
itemIds?: string[] | undefined;
|
|
64
64
|
data?: Record<string, unknown> | undefined;
|
|
65
65
|
locale?: string | undefined;
|
|
66
|
-
}, any,
|
|
66
|
+
}, any, questpie135.JsonRouteParams>;
|
|
67
67
|
/**
|
|
68
68
|
* Get actions configuration for a collection.
|
|
69
69
|
* Returns action definitions without handlers for client rendering.
|
|
70
70
|
*/
|
|
71
|
-
declare const getActionsConfigFn:
|
|
71
|
+
declare const getActionsConfigFn: questpie135.JsonRouteDefinition<{
|
|
72
72
|
collection: string;
|
|
73
|
-
}, any,
|
|
73
|
+
}, any, questpie135.JsonRouteParams>;
|
|
74
74
|
/**
|
|
75
75
|
* QUESTPIE functions for action execution.
|
|
76
76
|
* These are registered on the `adminModule`.
|
|
77
77
|
*/
|
|
78
78
|
declare const actionFunctions: {
|
|
79
|
-
executeAction:
|
|
79
|
+
executeAction: questpie135.JsonRouteDefinition<{
|
|
80
80
|
collection: string;
|
|
81
81
|
actionId: string;
|
|
82
82
|
itemId?: string | undefined;
|
|
83
83
|
itemIds?: string[] | undefined;
|
|
84
84
|
data?: Record<string, unknown> | undefined;
|
|
85
85
|
locale?: string | undefined;
|
|
86
|
-
}, any,
|
|
87
|
-
getActionsConfig:
|
|
86
|
+
}, any, questpie135.JsonRouteParams>;
|
|
87
|
+
getActionsConfig: questpie135.JsonRouteDefinition<{
|
|
88
88
|
collection: string;
|
|
89
|
-
}, any,
|
|
89
|
+
}, any, questpie135.JsonRouteParams>;
|
|
90
90
|
};
|
|
91
91
|
//#endregion
|
|
92
92
|
export { ExecuteActionRequest, ExecuteActionResponse, actionFunctions, executeAction, executeActionFn, getActionsConfig, getActionsConfigFn };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getApp, getAppState, getSession } from "./route-helpers.mjs";
|
|
2
|
+
import { translateAdminMessage } from "./i18n-helpers.mjs";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
import { route } from "questpie";
|
|
4
5
|
|
|
@@ -98,9 +99,10 @@ function serializeActionFormFields(fields) {
|
|
|
98
99
|
async function executeAction(app, request, session) {
|
|
99
100
|
const { collection: collectionSlug, actionId, itemId, itemIds, data, locale } = request;
|
|
100
101
|
const collection$1 = getAppState(app).collections?.[collectionSlug];
|
|
102
|
+
const t = (key, params) => translateAdminMessage(locale, key, params);
|
|
101
103
|
if (!collection$1) return {
|
|
102
104
|
success: false,
|
|
103
|
-
error:
|
|
105
|
+
error: t("action.collectionNotFound", { collection: collectionSlug })
|
|
104
106
|
};
|
|
105
107
|
const actionsConfig = (collection$1.state || collection$1).adminActions;
|
|
106
108
|
if ((actionsConfig?.builtin || [
|
|
@@ -124,10 +126,13 @@ async function executeAction(app, request, session) {
|
|
|
124
126
|
const customAction = actionsConfig?.custom?.find((a) => a.id === actionId);
|
|
125
127
|
if (!customAction) return {
|
|
126
128
|
success: false,
|
|
127
|
-
error:
|
|
129
|
+
error: t("action.notFound", {
|
|
130
|
+
action: actionId,
|
|
131
|
+
collection: collectionSlug
|
|
132
|
+
})
|
|
128
133
|
};
|
|
129
134
|
if (customAction.form) {
|
|
130
|
-
const validationError = validateActionFormData(customAction.form.fields, data || {});
|
|
135
|
+
const validationError = validateActionFormData(customAction.form.fields, data || {}, t);
|
|
131
136
|
if (validationError) return {
|
|
132
137
|
success: false,
|
|
133
138
|
result: {
|
|
@@ -160,7 +165,7 @@ async function executeAction(app, request, session) {
|
|
|
160
165
|
success: false,
|
|
161
166
|
result: {
|
|
162
167
|
type: "error",
|
|
163
|
-
toast: { message: error instanceof Error ? error.message : "
|
|
168
|
+
toast: { message: error instanceof Error ? error.message : t("action.executionFailed") }
|
|
164
169
|
}
|
|
165
170
|
};
|
|
166
171
|
}
|
|
@@ -169,7 +174,8 @@ async function executeAction(app, request, session) {
|
|
|
169
174
|
* Execute a built-in action.
|
|
170
175
|
*/
|
|
171
176
|
async function executeBuiltinAction(app, params) {
|
|
172
|
-
const { collectionSlug, actionId, itemId, itemIds, data } = params;
|
|
177
|
+
const { collectionSlug, actionId, itemId, itemIds, data, locale } = params;
|
|
178
|
+
const t = (key, messageParams) => translateAdminMessage(locale, key, messageParams);
|
|
173
179
|
const appRec = app;
|
|
174
180
|
const collectionCrud = appRec.api?.collections?.[collectionSlug];
|
|
175
181
|
const crudContext = {
|
|
@@ -185,7 +191,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
185
191
|
success: true,
|
|
186
192
|
result: {
|
|
187
193
|
type: "success",
|
|
188
|
-
toast: { message: "
|
|
194
|
+
toast: { message: t("action.itemCreated") },
|
|
189
195
|
effects: {
|
|
190
196
|
invalidate: [collectionSlug],
|
|
191
197
|
redirect: `/admin/collections/${collectionSlug}/${result.id}`
|
|
@@ -198,7 +204,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
198
204
|
success: false,
|
|
199
205
|
result: {
|
|
200
206
|
type: "error",
|
|
201
|
-
toast: { message: "
|
|
207
|
+
toast: { message: t("action.itemIdRequired.save") }
|
|
202
208
|
}
|
|
203
209
|
};
|
|
204
210
|
await appRec.update(collectionSlug, itemId, data || {});
|
|
@@ -206,7 +212,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
206
212
|
success: true,
|
|
207
213
|
result: {
|
|
208
214
|
type: "success",
|
|
209
|
-
toast: { message: "
|
|
215
|
+
toast: { message: t("action.itemSaved") },
|
|
210
216
|
effects: { invalidate: [collectionSlug] }
|
|
211
217
|
}
|
|
212
218
|
};
|
|
@@ -215,7 +221,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
215
221
|
success: false,
|
|
216
222
|
result: {
|
|
217
223
|
type: "error",
|
|
218
|
-
toast: { message: "
|
|
224
|
+
toast: { message: t("action.itemIdRequired.delete") }
|
|
219
225
|
}
|
|
220
226
|
};
|
|
221
227
|
await appRec.delete(collectionSlug, itemId);
|
|
@@ -223,7 +229,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
223
229
|
success: true,
|
|
224
230
|
result: {
|
|
225
231
|
type: "success",
|
|
226
|
-
toast: { message: "
|
|
232
|
+
toast: { message: t("action.itemDeleted") },
|
|
227
233
|
effects: {
|
|
228
234
|
invalidate: [collectionSlug],
|
|
229
235
|
redirect: `/admin/collections/${collectionSlug}`
|
|
@@ -235,7 +241,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
235
241
|
success: false,
|
|
236
242
|
result: {
|
|
237
243
|
type: "error",
|
|
238
|
-
toast: { message: "
|
|
244
|
+
toast: { message: t("action.itemIdsRequired.bulkDelete") }
|
|
239
245
|
}
|
|
240
246
|
};
|
|
241
247
|
await Promise.all(itemIds.map((id) => appRec.delete(collectionSlug, id)));
|
|
@@ -243,7 +249,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
243
249
|
success: true,
|
|
244
250
|
result: {
|
|
245
251
|
type: "success",
|
|
246
|
-
toast: { message:
|
|
252
|
+
toast: { message: t("action.itemsDeleted", { count: itemIds.length }) },
|
|
247
253
|
effects: { invalidate: [collectionSlug] }
|
|
248
254
|
}
|
|
249
255
|
};
|
|
@@ -252,7 +258,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
252
258
|
success: false,
|
|
253
259
|
result: {
|
|
254
260
|
type: "error",
|
|
255
|
-
toast: { message: "
|
|
261
|
+
toast: { message: t("action.itemIdRequired.restore") }
|
|
256
262
|
}
|
|
257
263
|
};
|
|
258
264
|
if (typeof appRec.restore === "function") await appRec.restore(collectionSlug, itemId);
|
|
@@ -261,14 +267,14 @@ async function executeBuiltinAction(app, params) {
|
|
|
261
267
|
success: false,
|
|
262
268
|
result: {
|
|
263
269
|
type: "error",
|
|
264
|
-
toast: { message: "
|
|
270
|
+
toast: { message: t("action.restoreUnsupported") }
|
|
265
271
|
}
|
|
266
272
|
};
|
|
267
273
|
return {
|
|
268
274
|
success: true,
|
|
269
275
|
result: {
|
|
270
276
|
type: "success",
|
|
271
|
-
toast: { message: "
|
|
277
|
+
toast: { message: t("action.itemRestored") },
|
|
272
278
|
effects: {
|
|
273
279
|
invalidate: [collectionSlug],
|
|
274
280
|
redirect: `/admin/collections/${collectionSlug}/${itemId}`
|
|
@@ -280,7 +286,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
280
286
|
success: false,
|
|
281
287
|
result: {
|
|
282
288
|
type: "error",
|
|
283
|
-
toast: { message: "
|
|
289
|
+
toast: { message: t("action.itemIdsRequired.bulkRestore") }
|
|
284
290
|
}
|
|
285
291
|
};
|
|
286
292
|
if (typeof appRec.restore === "function") await Promise.all(itemIds.map((id) => appRec.restore(collectionSlug, id)));
|
|
@@ -296,7 +302,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
296
302
|
success: true,
|
|
297
303
|
result: {
|
|
298
304
|
type: "success",
|
|
299
|
-
toast: { message:
|
|
305
|
+
toast: { message: t("action.itemsRestored", { count: itemIds.length }) },
|
|
300
306
|
effects: { invalidate: [collectionSlug] }
|
|
301
307
|
}
|
|
302
308
|
};
|
|
@@ -305,7 +311,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
305
311
|
success: false,
|
|
306
312
|
result: {
|
|
307
313
|
type: "error",
|
|
308
|
-
toast: { message: "
|
|
314
|
+
toast: { message: t("action.itemIdRequired.duplicate") }
|
|
309
315
|
}
|
|
310
316
|
};
|
|
311
317
|
const original = await appRec.findById(collectionSlug, itemId);
|
|
@@ -313,7 +319,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
313
319
|
success: false,
|
|
314
320
|
result: {
|
|
315
321
|
type: "error",
|
|
316
|
-
toast: { message: "
|
|
322
|
+
toast: { message: t("action.itemNotFound") }
|
|
317
323
|
}
|
|
318
324
|
};
|
|
319
325
|
const { id, createdAt, updatedAt, ...duplicateData } = original;
|
|
@@ -322,7 +328,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
322
328
|
success: true,
|
|
323
329
|
result: {
|
|
324
330
|
type: "success",
|
|
325
|
-
toast: { message: "
|
|
331
|
+
toast: { message: t("action.itemDuplicated") },
|
|
326
332
|
effects: {
|
|
327
333
|
invalidate: [collectionSlug],
|
|
328
334
|
redirect: `/admin/collections/${collectionSlug}/${duplicated.id}`
|
|
@@ -335,7 +341,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
335
341
|
success: false,
|
|
336
342
|
result: {
|
|
337
343
|
type: "error",
|
|
338
|
-
toast: { message: "
|
|
344
|
+
toast: { message: t("action.itemIdRequired.transition") }
|
|
339
345
|
}
|
|
340
346
|
};
|
|
341
347
|
const stage = data?.stage;
|
|
@@ -343,7 +349,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
343
349
|
success: false,
|
|
344
350
|
result: {
|
|
345
351
|
type: "error",
|
|
346
|
-
toast: { message: "
|
|
352
|
+
toast: { message: t("action.targetStageRequired") }
|
|
347
353
|
}
|
|
348
354
|
};
|
|
349
355
|
if (collectionCrud?.transitionStage) {
|
|
@@ -358,14 +364,14 @@ async function executeBuiltinAction(app, params) {
|
|
|
358
364
|
success: false,
|
|
359
365
|
result: {
|
|
360
366
|
type: "error",
|
|
361
|
-
toast: { message: "
|
|
367
|
+
toast: { message: t("action.workflowUnsupported") }
|
|
362
368
|
}
|
|
363
369
|
};
|
|
364
370
|
return {
|
|
365
371
|
success: true,
|
|
366
372
|
result: {
|
|
367
373
|
type: "success",
|
|
368
|
-
toast: { message:
|
|
374
|
+
toast: { message: t("workflow.transitionSuccess", { stage }) },
|
|
369
375
|
effects: { invalidate: [collectionSlug] }
|
|
370
376
|
}
|
|
371
377
|
};
|
|
@@ -374,7 +380,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
374
380
|
success: false,
|
|
375
381
|
result: {
|
|
376
382
|
type: "error",
|
|
377
|
-
toast: { message:
|
|
383
|
+
toast: { message: t("action.unknownBuiltin", { action: actionId }) }
|
|
378
384
|
}
|
|
379
385
|
};
|
|
380
386
|
}
|
|
@@ -384,7 +390,7 @@ async function executeBuiltinAction(app, params) {
|
|
|
384
390
|
success: false,
|
|
385
391
|
result: {
|
|
386
392
|
type: "error",
|
|
387
|
-
toast: { message: error instanceof Error ? error.message : "
|
|
393
|
+
toast: { message: error instanceof Error ? error.message : t("action.executionFailed") }
|
|
388
394
|
}
|
|
389
395
|
};
|
|
390
396
|
}
|
|
@@ -406,8 +412,8 @@ function isFieldRequired(field$1) {
|
|
|
406
412
|
* Validate form data against action form fields.
|
|
407
413
|
* Returns error message if validation fails, null if valid.
|
|
408
414
|
*/
|
|
409
|
-
function validateActionFormData(fields, data) {
|
|
410
|
-
for (const [fieldName, fieldConfig] of Object.entries(fields)) if (isFieldRequired(fieldConfig) && !data[fieldName]) return
|
|
415
|
+
function validateActionFormData(fields, data, t) {
|
|
416
|
+
for (const [fieldName, fieldConfig] of Object.entries(fields)) if (isFieldRequired(fieldConfig) && !data[fieldName]) return t("action.fieldRequired", { field: fieldName });
|
|
411
417
|
return null;
|
|
412
418
|
}
|
|
413
419
|
const executeActionRequestSchema = z.object({
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { getAdminMessagesForLocale } from "../../../i18n/index.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/server/modules/admin/routes/i18n-helpers.ts
|
|
4
|
+
function getPluralCount(params) {
|
|
5
|
+
if (!params) return 1;
|
|
6
|
+
for (const key of [
|
|
7
|
+
"count",
|
|
8
|
+
"total",
|
|
9
|
+
"length"
|
|
10
|
+
]) if (typeof params[key] === "number") return params[key];
|
|
11
|
+
return 1;
|
|
12
|
+
}
|
|
13
|
+
function selectMessage(value, locale, params) {
|
|
14
|
+
if (typeof value === "string") return value;
|
|
15
|
+
const count = getPluralCount(params);
|
|
16
|
+
try {
|
|
17
|
+
return value[new Intl.PluralRules(locale).select(count)] ?? value.other;
|
|
18
|
+
} catch {
|
|
19
|
+
return count === 1 ? value.one : value.other;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function interpolate(template, params) {
|
|
23
|
+
if (!params) return template;
|
|
24
|
+
return template.replace(/\{\{\s*(\w+)\s*\}\}/g, (_, key) => params[key] === void 0 ? `{{${key}}}` : String(params[key]));
|
|
25
|
+
}
|
|
26
|
+
function translateAdminMessage(locale, key, params) {
|
|
27
|
+
const resolvedLocale = locale ?? "en";
|
|
28
|
+
const message = getAdminMessagesForLocale(resolvedLocale)[key];
|
|
29
|
+
if (message === void 0) return key;
|
|
30
|
+
return interpolate(selectMessage(message, resolvedLocale, params), params);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
export { translateAdminMessage };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as questpie268 from "questpie";
|
|
2
2
|
|
|
3
3
|
//#region src/server/modules/admin/routes/locales.d.ts
|
|
4
4
|
|
|
@@ -12,7 +12,7 @@ import * as questpie462 from "questpie";
|
|
|
12
12
|
* Bundle of locale-related functions.
|
|
13
13
|
*/
|
|
14
14
|
declare const localeFunctions: {
|
|
15
|
-
readonly getContentLocales:
|
|
15
|
+
readonly getContentLocales: questpie268.JsonRouteDefinition<Record<string, never> | undefined, any, questpie268.JsonRouteParams>;
|
|
16
16
|
};
|
|
17
17
|
//#endregion
|
|
18
18
|
export { localeFunctions };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as questpie117 from "questpie";
|
|
2
2
|
|
|
3
3
|
//#region src/server/modules/admin/routes/preview.d.ts
|
|
4
4
|
|
|
@@ -21,13 +21,13 @@ interface PreviewTokenPayload {
|
|
|
21
21
|
* @returns Object with preview functions
|
|
22
22
|
*/
|
|
23
23
|
declare function createPreviewFunctions(secret: string): {
|
|
24
|
-
mintPreviewToken:
|
|
24
|
+
mintPreviewToken: questpie117.JsonRouteDefinition<{
|
|
25
25
|
path: string;
|
|
26
26
|
ttlMs?: number | undefined;
|
|
27
|
-
}, any,
|
|
28
|
-
verifyPreviewToken:
|
|
27
|
+
}, any, questpie117.JsonRouteParams>;
|
|
28
|
+
verifyPreviewToken: questpie117.JsonRouteDefinition<{
|
|
29
29
|
token: string;
|
|
30
|
-
}, any,
|
|
30
|
+
}, any, questpie117.JsonRouteParams>;
|
|
31
31
|
};
|
|
32
32
|
/**
|
|
33
33
|
* Verify a preview token without RPC.
|
|
@@ -63,18 +63,18 @@ declare function createPreviewTokenVerifier(secret?: string): (token: string) =>
|
|
|
63
63
|
* Used by the `adminModule` to register preview RPC functions.
|
|
64
64
|
*/
|
|
65
65
|
declare const previewFunctions: {
|
|
66
|
-
getPreviewUrl:
|
|
66
|
+
getPreviewUrl: questpie117.JsonRouteDefinition<{
|
|
67
67
|
collection: string;
|
|
68
68
|
record: Record<string, unknown>;
|
|
69
69
|
locale?: string | undefined;
|
|
70
|
-
}, any,
|
|
71
|
-
mintPreviewToken:
|
|
70
|
+
}, any, questpie117.JsonRouteParams>;
|
|
71
|
+
mintPreviewToken: questpie117.JsonRouteDefinition<{
|
|
72
72
|
path: string;
|
|
73
73
|
ttlMs?: number | undefined;
|
|
74
|
-
}, any,
|
|
75
|
-
verifyPreviewToken:
|
|
74
|
+
}, any, questpie117.JsonRouteParams>;
|
|
75
|
+
verifyPreviewToken: questpie117.JsonRouteDefinition<{
|
|
76
76
|
token: string;
|
|
77
|
-
}, any,
|
|
77
|
+
}, any, questpie117.JsonRouteParams>;
|
|
78
78
|
};
|
|
79
79
|
//#endregion
|
|
80
80
|
export { PreviewTokenPayload, createPreviewFunctions, createPreviewTokenVerifier, previewFunctions, verifyPreviewTokenDirect };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { getApp, getCollectionState, getSession } from "./route-helpers.mjs";
|
|
2
1
|
import { getPreviewSecret } from "../../../../shared/preview-utils.mjs";
|
|
2
|
+
import { getApp, getCollectionState, getLocale, getSession } from "./route-helpers.mjs";
|
|
3
|
+
import { translateAdminMessage } from "./i18n-helpers.mjs";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import { ApiError, route } from "questpie";
|
|
5
6
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
@@ -23,7 +24,7 @@ function base64UrlDecode(input) {
|
|
|
23
24
|
return Buffer.from(base64, "base64").toString("utf8");
|
|
24
25
|
}
|
|
25
26
|
const mintPreviewTokenSchema = z.object({
|
|
26
|
-
path: z.string().min(1
|
|
27
|
+
path: z.string().min(1),
|
|
27
28
|
ttlMs: z.number().positive().optional()
|
|
28
29
|
});
|
|
29
30
|
const mintPreviewTokenOutputSchema = z.object({
|
|
@@ -50,7 +51,9 @@ function createPreviewFunctions(secret) {
|
|
|
50
51
|
return {
|
|
51
52
|
mintPreviewToken: route().post().schema(mintPreviewTokenSchema).outputSchema(mintPreviewTokenOutputSchema).handler(async (ctx) => {
|
|
52
53
|
const { input } = ctx;
|
|
53
|
-
|
|
54
|
+
const locale = getLocale(ctx);
|
|
55
|
+
const t = (key, params) => translateAdminMessage(locale, key, params);
|
|
56
|
+
if (!getSession(ctx)) throw ApiError.unauthorized(t("preview.adminSessionRequired"));
|
|
54
57
|
const { path, ttlMs = DEFAULT_TTL_MS } = input;
|
|
55
58
|
const expiresAt = Date.now() + ttlMs;
|
|
56
59
|
const payload = {
|
|
@@ -63,37 +66,40 @@ function createPreviewFunctions(secret) {
|
|
|
63
66
|
expiresAt
|
|
64
67
|
};
|
|
65
68
|
}),
|
|
66
|
-
verifyPreviewToken: route().post().schema(verifyPreviewTokenSchema).outputSchema(verifyPreviewTokenOutputSchema).handler(async (
|
|
69
|
+
verifyPreviewToken: route().post().schema(verifyPreviewTokenSchema).outputSchema(verifyPreviewTokenOutputSchema).handler(async (ctx) => {
|
|
70
|
+
const { input } = ctx;
|
|
71
|
+
const locale = getLocale(ctx);
|
|
72
|
+
const t = (key, params) => translateAdminMessage(locale, key, params);
|
|
67
73
|
const { token } = input;
|
|
68
74
|
const [encodedPayload, signature] = token.split(".");
|
|
69
75
|
if (!encodedPayload || !signature) return {
|
|
70
76
|
valid: false,
|
|
71
|
-
error: "
|
|
77
|
+
error: t("preview.invalidTokenFormat")
|
|
72
78
|
};
|
|
73
79
|
const expectedSignature = signPayload(encodedPayload);
|
|
74
80
|
const signatureBuffer = Uint8Array.from(Buffer.from(signature));
|
|
75
81
|
const expectedBuffer = Uint8Array.from(Buffer.from(expectedSignature));
|
|
76
82
|
if (signatureBuffer.length !== expectedBuffer.length) return {
|
|
77
83
|
valid: false,
|
|
78
|
-
error: "
|
|
84
|
+
error: t("preview.invalidSignature")
|
|
79
85
|
};
|
|
80
86
|
if (!timingSafeEqual(signatureBuffer, expectedBuffer)) return {
|
|
81
87
|
valid: false,
|
|
82
|
-
error: "
|
|
88
|
+
error: t("preview.invalidSignature")
|
|
83
89
|
};
|
|
84
90
|
try {
|
|
85
91
|
const payload = JSON.parse(base64UrlDecode(encodedPayload));
|
|
86
92
|
if (!payload?.exp || typeof payload.exp !== "number") return {
|
|
87
93
|
valid: false,
|
|
88
|
-
error: "
|
|
94
|
+
error: t("preview.invalidPayload")
|
|
89
95
|
};
|
|
90
96
|
if (payload.exp < Date.now()) return {
|
|
91
97
|
valid: false,
|
|
92
|
-
error: "
|
|
98
|
+
error: t("preview.tokenExpired")
|
|
93
99
|
};
|
|
94
100
|
if (!payload.path || typeof payload.path !== "string") return {
|
|
95
101
|
valid: false,
|
|
96
|
-
error: "
|
|
102
|
+
error: t("preview.invalidPath")
|
|
97
103
|
};
|
|
98
104
|
return {
|
|
99
105
|
valid: true,
|
|
@@ -102,7 +108,7 @@ function createPreviewFunctions(secret) {
|
|
|
102
108
|
} catch {
|
|
103
109
|
return {
|
|
104
110
|
valid: false,
|
|
105
|
-
error: "
|
|
111
|
+
error: t("preview.invalidPayload")
|
|
106
112
|
};
|
|
107
113
|
}
|
|
108
114
|
})
|
|
@@ -160,7 +166,7 @@ function createPreviewTokenVerifier(secret) {
|
|
|
160
166
|
};
|
|
161
167
|
}
|
|
162
168
|
const getPreviewUrlSchema = z.object({
|
|
163
|
-
collection: z.string().min(1
|
|
169
|
+
collection: z.string().min(1),
|
|
164
170
|
record: z.record(z.string(), z.unknown()),
|
|
165
171
|
locale: z.string().optional()
|
|
166
172
|
});
|
|
@@ -186,24 +192,26 @@ const getPreviewUrlOutputSchema = z.object({
|
|
|
186
192
|
*/
|
|
187
193
|
const getPreviewUrl = route().post().schema(getPreviewUrlSchema).outputSchema(getPreviewUrlOutputSchema).handler(async (ctx) => {
|
|
188
194
|
const { input } = ctx;
|
|
195
|
+
const messageLocale = getLocale(ctx) ?? input.locale;
|
|
196
|
+
const t = (key, params) => translateAdminMessage(messageLocale, key, params);
|
|
189
197
|
if (!getSession(ctx)) return {
|
|
190
198
|
url: null,
|
|
191
|
-
error: "
|
|
199
|
+
error: t("preview.adminSessionRequired")
|
|
192
200
|
};
|
|
193
201
|
const { collection: collectionName, record, locale } = input;
|
|
194
202
|
const collection$1 = getApp(ctx).getCollections()[collectionName];
|
|
195
203
|
if (!collection$1) return {
|
|
196
204
|
url: null,
|
|
197
|
-
error:
|
|
205
|
+
error: t("preview.collectionNotFound", { collection: collectionName })
|
|
198
206
|
};
|
|
199
207
|
const previewConfig = getCollectionState(collection$1).adminPreview;
|
|
200
208
|
if (!previewConfig?.url) return {
|
|
201
209
|
url: null,
|
|
202
|
-
error: "
|
|
210
|
+
error: t("preview.noUrlConfigured")
|
|
203
211
|
};
|
|
204
212
|
if (previewConfig.enabled === false) return {
|
|
205
213
|
url: null,
|
|
206
|
-
error: "
|
|
214
|
+
error: t("preview.disabledForCollection")
|
|
207
215
|
};
|
|
208
216
|
try {
|
|
209
217
|
return { url: previewConfig.url({
|
|
@@ -213,7 +221,7 @@ const getPreviewUrl = route().post().schema(getPreviewUrlSchema).outputSchema(ge
|
|
|
213
221
|
} catch (err) {
|
|
214
222
|
return {
|
|
215
223
|
url: null,
|
|
216
|
-
error:
|
|
224
|
+
error: t("preview.generateUrlFailed", { message: err instanceof Error ? err.message : t("error.unknown") })
|
|
217
225
|
};
|
|
218
226
|
}
|
|
219
227
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as questpie127 from "questpie";
|
|
2
2
|
|
|
3
3
|
//#region src/server/modules/admin/routes/reactive.d.ts
|
|
4
4
|
|
|
@@ -13,7 +13,7 @@ import * as questpie135 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: questpie127.JsonRouteDefinition<{
|
|
17
17
|
collection: string;
|
|
18
18
|
type: "collection" | "global";
|
|
19
19
|
requests: {
|
|
@@ -26,12 +26,12 @@ declare const batchReactive: questpie135.JsonRouteDefinition<{
|
|
|
26
26
|
}[];
|
|
27
27
|
formData?: Record<string, unknown> | undefined;
|
|
28
28
|
prevData?: Record<string, unknown> | null | undefined;
|
|
29
|
-
}, any,
|
|
29
|
+
}, any, questpie127.JsonRouteParams>;
|
|
30
30
|
/**
|
|
31
31
|
* Dynamic options endpoint.
|
|
32
32
|
* Fetches options for select/relation fields with search and pagination.
|
|
33
33
|
*/
|
|
34
|
-
declare const fieldOptions:
|
|
34
|
+
declare const fieldOptions: questpie127.JsonRouteDefinition<{
|
|
35
35
|
collection: string;
|
|
36
36
|
type: "collection" | "global";
|
|
37
37
|
field: string;
|
|
@@ -40,12 +40,12 @@ declare const fieldOptions: questpie135.JsonRouteDefinition<{
|
|
|
40
40
|
page: number;
|
|
41
41
|
limit: number;
|
|
42
42
|
siblingData?: Record<string, unknown> | null | undefined;
|
|
43
|
-
}, any,
|
|
43
|
+
}, any, questpie127.JsonRouteParams>;
|
|
44
44
|
/**
|
|
45
45
|
* Reactive functions bundle.
|
|
46
46
|
*/
|
|
47
47
|
declare const reactiveFunctions: {
|
|
48
|
-
readonly batchReactive:
|
|
48
|
+
readonly batchReactive: questpie127.JsonRouteDefinition<{
|
|
49
49
|
collection: string;
|
|
50
50
|
type: "collection" | "global";
|
|
51
51
|
requests: {
|
|
@@ -58,8 +58,8 @@ declare const reactiveFunctions: {
|
|
|
58
58
|
}[];
|
|
59
59
|
formData?: Record<string, unknown> | undefined;
|
|
60
60
|
prevData?: Record<string, unknown> | null | undefined;
|
|
61
|
-
}, any,
|
|
62
|
-
readonly fieldOptions:
|
|
61
|
+
}, any, questpie127.JsonRouteParams>;
|
|
62
|
+
readonly fieldOptions: questpie127.JsonRouteDefinition<{
|
|
63
63
|
collection: string;
|
|
64
64
|
type: "collection" | "global";
|
|
65
65
|
field: string;
|
|
@@ -68,7 +68,7 @@ declare const reactiveFunctions: {
|
|
|
68
68
|
page: number;
|
|
69
69
|
limit: number;
|
|
70
70
|
siblingData?: Record<string, unknown> | null | undefined;
|
|
71
|
-
}, any,
|
|
71
|
+
}, any, questpie127.JsonRouteParams>;
|
|
72
72
|
};
|
|
73
73
|
//#endregion
|
|
74
74
|
export { batchReactive, fieldOptions, reactiveFunctions };
|
|
@@ -73,4 +73,4 @@ function buildServerContext(ctx) {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
//#endregion
|
|
76
|
-
export { buildServerContext, getAccessContext, getAdminConfig, getApp, getAppState, getCollectionState, getGlobalState, getSession };
|
|
76
|
+
export { buildServerContext, getAccessContext, getAdminConfig, getApp, getAppState, getCollectionState, getGlobalState, getLocale, getSession };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { getApp } from "./route-helpers.mjs";
|
|
1
|
+
import { getApp, getLocale } from "./route-helpers.mjs";
|
|
2
|
+
import { translateAdminMessage } from "./i18n-helpers.mjs";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
import { route } from "questpie";
|
|
4
5
|
import { eq, sql as sql$1 } from "questpie/drizzle";
|
|
@@ -14,9 +15,9 @@ import { eq, sql as sql$1 } from "questpie/drizzle";
|
|
|
14
15
|
const isSetupRequiredSchema = z.object({});
|
|
15
16
|
const isSetupRequiredOutputSchema = z.object({ required: z.boolean() });
|
|
16
17
|
const createFirstAdminSchema = z.object({
|
|
17
|
-
email: z.string().email(
|
|
18
|
-
password: z.string().min(8
|
|
19
|
-
name: z.string().min(2
|
|
18
|
+
email: z.string().email(),
|
|
19
|
+
password: z.string().min(8),
|
|
20
|
+
name: z.string().min(2)
|
|
20
21
|
});
|
|
21
22
|
const createFirstAdminOutputSchema = z.object({
|
|
22
23
|
success: z.boolean(),
|
|
@@ -67,11 +68,13 @@ const isSetupRequired = route().post().schema(isSetupRequiredSchema).outputSchem
|
|
|
67
68
|
*/
|
|
68
69
|
const createFirstAdmin = route().post().schema(createFirstAdminSchema).outputSchema(createFirstAdminOutputSchema).handler(async (ctx) => {
|
|
69
70
|
const app = getApp(ctx);
|
|
71
|
+
const locale = getLocale(ctx);
|
|
72
|
+
const t = (key, params) => translateAdminMessage(locale, key, params);
|
|
70
73
|
const input = ctx.input;
|
|
71
74
|
const userCollection = app.getCollectionConfig("user");
|
|
72
75
|
if ((await app.db.select({ count: sql$1`count(*)::int` }).from(userCollection.table).where(eq(userCollection.table.role, "admin")))[0].count > 0) return {
|
|
73
76
|
success: false,
|
|
74
|
-
error: "
|
|
77
|
+
error: t("auth.setupAlreadyCompleted")
|
|
75
78
|
};
|
|
76
79
|
try {
|
|
77
80
|
const signUpResult = await app.auth.api.signUpEmail({ body: {
|
|
@@ -81,7 +84,7 @@ const createFirstAdmin = route().post().schema(createFirstAdminSchema).outputSch
|
|
|
81
84
|
} });
|
|
82
85
|
if (!signUpResult.user) return {
|
|
83
86
|
success: false,
|
|
84
|
-
error: "
|
|
87
|
+
error: t("auth.failedToCreateUserAccount")
|
|
85
88
|
};
|
|
86
89
|
await app.db.update(userCollection.table).set({
|
|
87
90
|
role: "admin",
|
|
@@ -98,7 +101,7 @@ const createFirstAdmin = route().post().schema(createFirstAdminSchema).outputSch
|
|
|
98
101
|
} catch (error) {
|
|
99
102
|
return {
|
|
100
103
|
success: false,
|
|
101
|
-
error: error instanceof Error ? error.message : "
|
|
104
|
+
error: error instanceof Error ? error.message : t("error.unexpectedError")
|
|
102
105
|
};
|
|
103
106
|
}
|
|
104
107
|
});
|