nextly 0.0.1 → 0.0.2-alpha.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/LICENSE +22 -0
- package/README.md +122 -0
- package/dist/_dts-chunks/collections-handler.d-DjgO74Wt.d.ts +20540 -0
- package/dist/_dts-chunks/config.d-DNwsDnjs.d.ts +2589 -0
- package/dist/_dts-chunks/define-component.d-BUgTHmt3.d.ts +1149 -0
- package/dist/_dts-chunks/image-processor.d-OO1PmMrv.d.ts +335 -0
- package/dist/_dts-chunks/index.d-axCAzZ7m.d.ts +17842 -0
- package/dist/_dts-chunks/media.d-DjDOZo4B.d.ts +117 -0
- package/dist/_dts-chunks/on-error.d-CHIKWNxd.d.ts +38 -0
- package/dist/_dts-chunks/storage.d-BUhQ2we_.d.ts +404 -0
- package/dist/actions/index.d.ts +239 -0
- package/dist/actions/index.mjs +281 -0
- package/dist/api/auth-state.d.ts +5 -0
- package/dist/api/auth-state.mjs +131 -0
- package/dist/api/collections-schema-detail.d.ts +56 -0
- package/dist/api/collections-schema-detail.mjs +244 -0
- package/dist/api/collections-schema-export.d.ts +56 -0
- package/dist/api/collections-schema-export.mjs +129 -0
- package/dist/api/collections-schema.d.ts +59 -0
- package/dist/api/collections-schema.mjs +207 -0
- package/dist/api/components-detail.d.ts +50 -0
- package/dist/api/components-detail.mjs +132 -0
- package/dist/api/components.d.ts +69 -0
- package/dist/api/components.mjs +144 -0
- package/dist/api/email-providers-default.d.ts +40 -0
- package/dist/api/email-providers-default.mjs +75 -0
- package/dist/api/email-providers-detail.d.ts +81 -0
- package/dist/api/email-providers-detail.mjs +109 -0
- package/dist/api/email-providers-test.d.ts +43 -0
- package/dist/api/email-providers-test.mjs +114 -0
- package/dist/api/email-providers.d.ts +69 -0
- package/dist/api/email-providers.mjs +110 -0
- package/dist/api/email-send-template.d.ts +41 -0
- package/dist/api/email-send-template.mjs +58 -0
- package/dist/api/email-send.d.ts +42 -0
- package/dist/api/email-send.mjs +58 -0
- package/dist/api/email-templates-detail.d.ts +74 -0
- package/dist/api/email-templates-detail.mjs +112 -0
- package/dist/api/email-templates-layout.d.ts +55 -0
- package/dist/api/email-templates-layout.mjs +92 -0
- package/dist/api/email-templates-preview.d.ts +48 -0
- package/dist/api/email-templates-preview.mjs +93 -0
- package/dist/api/email-templates.d.ts +61 -0
- package/dist/api/email-templates.mjs +118 -0
- package/dist/api/health.d.ts +68 -0
- package/dist/api/health.mjs +67 -0
- package/dist/api/index.d.ts +54 -0
- package/dist/api/index.mjs +16 -0
- package/dist/api/media-bulk.d.ts +74 -0
- package/dist/api/media-bulk.mjs +196 -0
- package/dist/api/media-folders.d.ts +112 -0
- package/dist/api/media-folders.mjs +187 -0
- package/dist/api/media-handlers.d.ts +102 -0
- package/dist/api/media-handlers.mjs +437 -0
- package/dist/api/media.d.ts +117 -0
- package/dist/api/media.mjs +242 -0
- package/dist/api/singles-detail.d.ts +87 -0
- package/dist/api/singles-detail.mjs +170 -0
- package/dist/api/singles-schema-detail.d.ts +54 -0
- package/dist/api/singles-schema-detail.mjs +182 -0
- package/dist/api/singles.d.ts +34 -0
- package/dist/api/singles.mjs +94 -0
- package/dist/api/storage-upload-url.d.ts +48 -0
- package/dist/api/storage-upload-url.mjs +202 -0
- package/dist/api/uploads.d.ts +109 -0
- package/dist/api/uploads.mjs +359 -0
- package/dist/auth/index.d.ts +425 -0
- package/dist/auth/index.mjs +199 -0
- package/dist/boot-apply-PQSYLDIN.mjs +7 -0
- package/dist/chunk-2OALJTK6.mjs +489 -0
- package/dist/chunk-2Q2SX2CS.mjs +365 -0
- package/dist/chunk-2TFX4ND3.mjs +13 -0
- package/dist/chunk-2TWPDSYD.mjs +87 -0
- package/dist/chunk-2W3DVD7S.mjs +647 -0
- package/dist/chunk-2ZFKXPQM.mjs +88 -0
- package/dist/chunk-3FA7FKAV.mjs +832 -0
- package/dist/chunk-3NZ2KMBL.mjs +58 -0
- package/dist/chunk-4MJLT6PZ.mjs +0 -0
- package/dist/chunk-56WO4WX7.mjs +0 -0
- package/dist/chunk-5APFUGAD.mjs +89 -0
- package/dist/chunk-5HMZ644B.mjs +108 -0
- package/dist/chunk-67GXH6PR.mjs +32 -0
- package/dist/chunk-6JNEPWRW.mjs +14368 -0
- package/dist/chunk-6NFHQIJD.mjs +45 -0
- package/dist/chunk-7P6ASYW6.mjs +9 -0
- package/dist/chunk-A3WPLSDT.mjs +1364 -0
- package/dist/chunk-AGJ6F2T3.mjs +144 -0
- package/dist/chunk-AK6Z23OX.mjs +1464 -0
- package/dist/chunk-APKKRD2G.mjs +102 -0
- package/dist/chunk-B2GV2BWH.mjs +73 -0
- package/dist/chunk-D5HQBNUB.mjs +74 -0
- package/dist/chunk-DNNG377Z.mjs +204 -0
- package/dist/chunk-DP3G27G5.mjs +135 -0
- package/dist/chunk-DV6WVX2Q.mjs +0 -0
- package/dist/chunk-DXGGXIUZ.mjs +57 -0
- package/dist/chunk-EGXBZCGC.mjs +943 -0
- package/dist/chunk-ERCNLX3V.mjs +176 -0
- package/dist/chunk-FQULBZ53.mjs +850 -0
- package/dist/chunk-G2AA4QLC.mjs +262 -0
- package/dist/chunk-GDBJ5JCU.mjs +488 -0
- package/dist/chunk-GJNSJU4S.mjs +19 -0
- package/dist/chunk-GZ6DCQKC.mjs +69 -0
- package/dist/chunk-H26B4FYG.mjs +167 -0
- package/dist/chunk-I4JMR3UR.mjs +21 -0
- package/dist/chunk-INV7QKLG.mjs +508 -0
- package/dist/chunk-IUDOC7N7.mjs +46 -0
- package/dist/chunk-IZWPRDC3.mjs +206 -0
- package/dist/chunk-KIMNCZGV.mjs +15 -0
- package/dist/chunk-L6HW2DA7.mjs +15 -0
- package/dist/chunk-LAZXX4HR.mjs +100 -0
- package/dist/chunk-LDKCUMHK.mjs +95 -0
- package/dist/chunk-LRXMECUA.mjs +0 -0
- package/dist/chunk-M52VMPGA.mjs +119 -0
- package/dist/chunk-MGUWEEI6.mjs +160 -0
- package/dist/chunk-NRUWQ5Z7.mjs +419 -0
- package/dist/chunk-NSEFNNU4.mjs +25360 -0
- package/dist/chunk-NTHVDFGO.mjs +138 -0
- package/dist/chunk-O3QHXMOX.mjs +3166 -0
- package/dist/chunk-P7NH2OSC.mjs +2605 -0
- package/dist/chunk-PKMABBB5.mjs +184 -0
- package/dist/chunk-PWS6XGJK.mjs +76 -0
- package/dist/chunk-R6JJQHFC.mjs +20 -0
- package/dist/chunk-RJLLGGPG.mjs +0 -0
- package/dist/chunk-SBACDPNX.mjs +689 -0
- package/dist/chunk-TO5AFLVQ.mjs +124 -0
- package/dist/chunk-TS7GHTG2.mjs +5436 -0
- package/dist/chunk-UJ2IMJ4W.mjs +133 -0
- package/dist/chunk-UOP63Q54.mjs +102 -0
- package/dist/chunk-UUOFWCM6.mjs +78 -0
- package/dist/chunk-V4EQTOA4.mjs +893 -0
- package/dist/chunk-VJ66NCL4.mjs +193 -0
- package/dist/chunk-VQJQHVEV.mjs +29 -0
- package/dist/chunk-VTJADRO3.mjs +141 -0
- package/dist/chunk-VWF3JO32.mjs +0 -0
- package/dist/chunk-W4MGXIRR.mjs +27 -0
- package/dist/chunk-W5KKPZT5.mjs +1204 -0
- package/dist/chunk-WD34YQ6T.mjs +381 -0
- package/dist/chunk-WZBYMYVW.mjs +14 -0
- package/dist/chunk-X23WKS3Z.mjs +50 -0
- package/dist/chunk-X7TXCYYN.mjs +6496 -0
- package/dist/chunk-XGI4EMS3.mjs +140 -0
- package/dist/chunk-XZKLBMN6.mjs +1153 -0
- package/dist/chunk-YB7INWPY.mjs +0 -0
- package/dist/chunk-YV4Y7SDL.mjs +83 -0
- package/dist/chunk-YZNBLFIW.mjs +1688 -0
- package/dist/chunk-YZZCTONM.mjs +263 -0
- package/dist/chunk-ZE6A3FYH.mjs +289 -0
- package/dist/cli/nextly.mjs +68 -0
- package/dist/cli/utils/index.d.ts +449 -0
- package/dist/cli/utils/index.mjs +49 -0
- package/dist/component-schema-service-5577KVW6.mjs +11 -0
- package/dist/config-loader-23YEMC3Z.mjs +23 -0
- package/dist/config.d.ts +44 -0
- package/dist/config.mjs +109 -0
- package/dist/container-ORGFGYSZ.mjs +9 -0
- package/dist/database/index.d.ts +12 -0
- package/dist/database/index.mjs +40 -0
- package/dist/database/seeders/index.d.ts +93 -0
- package/dist/database/seeders/index.mjs +47 -0
- package/dist/db-sync-demote-LJGKLB3S.mjs +117 -0
- package/dist/db-sync-promote-B26VSYQF.mjs +113 -0
- package/dist/dev-reload-broadcaster-B73IQ53V.mjs +25 -0
- package/dist/dist-M2NOU37V.mjs +19 -0
- package/dist/drizzle-kit-lazy-D2M2PXR2.mjs +13 -0
- package/dist/dynamic-collection-schema-service-IEXTPIZ7.mjs +8 -0
- package/dist/errors/index.d.ts +159 -0
- package/dist/errors/index.mjs +10 -0
- package/dist/factory-IWMBKUJM.mjs +15 -0
- package/dist/first-run-QIVKWJIF.mjs +63 -0
- package/dist/fresh-push-NR67DC3R.mjs +8 -0
- package/dist/index.d.ts +4175 -0
- package/dist/index.mjs +1336 -0
- package/dist/local-plugin-PTET4NAT.mjs +7 -0
- package/dist/logger-NU46DXNY.mjs +15 -0
- package/dist/logger-YE4TC7ZN.mjs +9 -0
- package/dist/migration-journal-EP532Y4L.mjs +139 -0
- package/dist/migrations/mysql/0000_eager_sentry.sql +174 -0
- package/dist/migrations/mysql/0001_soft_giant_girl.sql +27 -0
- package/dist/migrations/mysql/0002_media_table.sql +24 -0
- package/dist/migrations/mysql/0003_dynamic_singles.sql +37 -0
- package/dist/migrations/mysql/0004_dynamic_components.sql +35 -0
- package/dist/migrations/mysql/0005_user_management_tables.sql +92 -0
- package/dist/migrations/mysql/0006_api_keys.sql +36 -0
- package/dist/migrations/mysql/0007_general_settings.sql +20 -0
- package/dist/migrations/mysql/0008_site_settings_logo_url.sql +9 -0
- package/dist/migrations/mysql/0009_activity_log.sql +30 -0
- package/dist/migrations/mysql/0010_site_settings_sidebar.sql +13 -0
- package/dist/migrations/mysql/0011_missing_tables_and_columns.sql +54 -0
- package/dist/migrations/mysql/0012_image_sizes_and_focal_point.sql +30 -0
- package/dist/migrations/mysql/0012_media_folders.sql +43 -0
- package/dist/migrations/mysql/0013_user_brute_force_protection.sql +31 -0
- package/dist/migrations/mysql/0014_email_template_attachments.sql +12 -0
- package/dist/migrations/mysql/0015_media_uploaded_by_nullable.sql +15 -0
- package/dist/migrations/mysql/20260429_000000_000_initial_journal.sql +22 -0
- package/dist/migrations/mysql/20260501_000000_journal_batch.sql +17 -0
- package/dist/migrations/mysql/20260501_000001_audit_log.sql +24 -0
- package/dist/migrations/mysql/20260504_000000_nextly_meta.sql +21 -0
- package/dist/migrations/mysql/meta/0000_snapshot.json +1005 -0
- package/dist/migrations/mysql/meta/0001_snapshot.json +1099 -0
- package/dist/migrations/mysql/meta/_journal.json +41 -0
- package/dist/migrations/postgresql/0000_misty_king_bedlam.sql +169 -0
- package/dist/migrations/postgresql/0001_perpetual_captain_marvel.sql +8 -0
- package/dist/migrations/postgresql/0002_sad_spectrum.sql +16 -0
- package/dist/migrations/postgresql/0003_hesitant_ultron.sql +17 -0
- package/dist/migrations/postgresql/0004_media_table.sql +24 -0
- package/dist/migrations/postgresql/0005_media_folders.sql +36 -0
- package/dist/migrations/postgresql/0006_dynamic_collections_update.sql +50 -0
- package/dist/migrations/postgresql/0007_dynamic_singles.sql +38 -0
- package/dist/migrations/postgresql/0008_dynamic_components.sql +37 -0
- package/dist/migrations/postgresql/0009_user_management_tables.sql +95 -0
- package/dist/migrations/postgresql/0010_api_keys.sql +34 -0
- package/dist/migrations/postgresql/0011_general_settings.sql +20 -0
- package/dist/migrations/postgresql/0012_site_settings_logo_url.sql +9 -0
- package/dist/migrations/postgresql/0013_activity_log.sql +29 -0
- package/dist/migrations/postgresql/0014_image_sizes_and_focal_point.sql +33 -0
- package/dist/migrations/postgresql/0014_site_settings_sidebar.sql +13 -0
- package/dist/migrations/postgresql/0015_user_brute_force_protection.sql +29 -0
- package/dist/migrations/postgresql/0016_email_template_attachments.sql +12 -0
- package/dist/migrations/postgresql/0017_media_uploaded_by_nullable.sql +15 -0
- package/dist/migrations/postgresql/20260429_000000_000_initial_journal.sql +24 -0
- package/dist/migrations/postgresql/20260501_000000_journal_batch.sql +17 -0
- package/dist/migrations/postgresql/20260501_000001_audit_log.sql +24 -0
- package/dist/migrations/postgresql/20260504_000000_nextly_meta.sql +22 -0
- package/dist/migrations/postgresql/meta/0000_snapshot.json +1286 -0
- package/dist/migrations/postgresql/meta/0001_snapshot.json +1407 -0
- package/dist/migrations/postgresql/meta/0002_snapshot.json +1552 -0
- package/dist/migrations/postgresql/meta/0003_snapshot.json +1695 -0
- package/dist/migrations/postgresql/meta/0010_snapshot.json +2345 -0
- package/dist/migrations/postgresql/meta/_journal.json +90 -0
- package/dist/migrations/sqlite/0000_api_keys.sql +34 -0
- package/dist/migrations/sqlite/0001_general_settings.sql +20 -0
- package/dist/migrations/sqlite/0002_site_settings_logo_url.sql +9 -0
- package/dist/migrations/sqlite/0003_activity_log.sql +29 -0
- package/dist/migrations/sqlite/0004_image_sizes_and_focal_point.sql +29 -0
- package/dist/migrations/sqlite/0004_site_settings_sidebar.sql +11 -0
- package/dist/migrations/sqlite/0005_user_brute_force_protection.sql +29 -0
- package/dist/migrations/sqlite/0006_email_template_attachments.sql +12 -0
- package/dist/migrations/sqlite/0007_media_uploaded_by_nullable.sql +111 -0
- package/dist/migrations/sqlite/20260429_000000_000_initial_journal.sql +24 -0
- package/dist/migrations/sqlite/20260501_000000_journal_batch.sql +19 -0
- package/dist/migrations/sqlite/20260501_000001_audit_log.sql +24 -0
- package/dist/migrations/sqlite/20260504_000000_nextly_meta.sql +21 -0
- package/dist/migrations/sqlite/20260505_000000_user_management_tables.sql +77 -0
- package/dist/next.d.ts +57 -0
- package/dist/next.mjs +55 -0
- package/dist/observability/index.d.ts +87 -0
- package/dist/observability/index.mjs +57 -0
- package/dist/permissions-3DZZQZMI.mjs +39 -0
- package/dist/pipeline-YOML7SWF.mjs +29 -0
- package/dist/preview-ZZTR3QGS.mjs +9 -0
- package/dist/program-PW6UB2ZC.mjs +5934 -0
- package/dist/reconcile-single-tables-7ENVXJGB.mjs +7 -0
- package/dist/register-SF6E6FVU.mjs +49 -0
- package/dist/reload-config-HWQ4G5MM.mjs +23 -0
- package/dist/resolve-single-table-name-JSOMUB3R.mjs +7 -0
- package/dist/routeHandler-UNMMJIBM.mjs +77 -0
- package/dist/runtime-schema-generator-NRA6A6Z6.mjs +8 -0
- package/dist/runtime.d.ts +120 -0
- package/dist/runtime.mjs +73 -0
- package/dist/schema-hash-FMMG6VPJ.mjs +13 -0
- package/dist/schema-registry-EQ36FZDP.mjs +7 -0
- package/dist/scripts/load-env.mjs +42 -0
- package/dist/storage/index.d.ts +566 -0
- package/dist/storage/index.mjs +45 -0
- package/dist/super-admin-G5ZK5F4T.mjs +39 -0
- package/dist/system-table-service-WGSRVEGT.mjs +17 -0
- package/dist/users-7KELGRYJ.mjs +38 -0
- package/package.json +308 -9
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import "../chunk-LRXMECUA.mjs";
|
|
2
|
+
import {
|
|
3
|
+
getSession
|
|
4
|
+
} from "../chunk-UUOFWCM6.mjs";
|
|
5
|
+
import "../chunk-VWF3JO32.mjs";
|
|
6
|
+
import "../chunk-2ZFKXPQM.mjs";
|
|
7
|
+
import {
|
|
8
|
+
withErrorHandler
|
|
9
|
+
} from "../chunk-TO5AFLVQ.mjs";
|
|
10
|
+
import {
|
|
11
|
+
getCachedNextly
|
|
12
|
+
} from "../chunk-P7NH2OSC.mjs";
|
|
13
|
+
import "../chunk-2OALJTK6.mjs";
|
|
14
|
+
import "../chunk-VJ66NCL4.mjs";
|
|
15
|
+
import "../chunk-R6JJQHFC.mjs";
|
|
16
|
+
import "../chunk-X23WKS3Z.mjs";
|
|
17
|
+
import {
|
|
18
|
+
getService
|
|
19
|
+
} from "../chunk-X7TXCYYN.mjs";
|
|
20
|
+
import "../chunk-YZNBLFIW.mjs";
|
|
21
|
+
import "../chunk-FQULBZ53.mjs";
|
|
22
|
+
import "../chunk-67GXH6PR.mjs";
|
|
23
|
+
import "../chunk-2TFX4ND3.mjs";
|
|
24
|
+
import {
|
|
25
|
+
getNextlyLogger
|
|
26
|
+
} from "../chunk-W4MGXIRR.mjs";
|
|
27
|
+
import "../chunk-NSEFNNU4.mjs";
|
|
28
|
+
import "../chunk-3FA7FKAV.mjs";
|
|
29
|
+
import "../chunk-AGJ6F2T3.mjs";
|
|
30
|
+
import "../chunk-KIMNCZGV.mjs";
|
|
31
|
+
import "../chunk-AK6Z23OX.mjs";
|
|
32
|
+
import "../chunk-INV7QKLG.mjs";
|
|
33
|
+
import "../chunk-GZ6DCQKC.mjs";
|
|
34
|
+
import "../chunk-SBACDPNX.mjs";
|
|
35
|
+
import "../chunk-RJLLGGPG.mjs";
|
|
36
|
+
import "../chunk-V4EQTOA4.mjs";
|
|
37
|
+
import "../chunk-I4JMR3UR.mjs";
|
|
38
|
+
import "../chunk-XZKLBMN6.mjs";
|
|
39
|
+
import "../chunk-IZWPRDC3.mjs";
|
|
40
|
+
import "../chunk-DNNG377Z.mjs";
|
|
41
|
+
import {
|
|
42
|
+
calculateSchemaHash
|
|
43
|
+
} from "../chunk-5HMZ644B.mjs";
|
|
44
|
+
import {
|
|
45
|
+
hasPermission,
|
|
46
|
+
isSuperAdmin
|
|
47
|
+
} from "../chunk-W5KKPZT5.mjs";
|
|
48
|
+
import "../chunk-56WO4WX7.mjs";
|
|
49
|
+
import "../chunk-2W3DVD7S.mjs";
|
|
50
|
+
import "../chunk-TS7GHTG2.mjs";
|
|
51
|
+
import "../chunk-H26B4FYG.mjs";
|
|
52
|
+
import "../chunk-DP3G27G5.mjs";
|
|
53
|
+
import "../chunk-DV6WVX2Q.mjs";
|
|
54
|
+
import {
|
|
55
|
+
env
|
|
56
|
+
} from "../chunk-UJ2IMJ4W.mjs";
|
|
57
|
+
import "../chunk-EGXBZCGC.mjs";
|
|
58
|
+
import "../chunk-G2AA4QLC.mjs";
|
|
59
|
+
import "../chunk-4MJLT6PZ.mjs";
|
|
60
|
+
import {
|
|
61
|
+
respondDoc,
|
|
62
|
+
respondMutation
|
|
63
|
+
} from "../chunk-IUDOC7N7.mjs";
|
|
64
|
+
import "../chunk-LAZXX4HR.mjs";
|
|
65
|
+
import "../chunk-D5HQBNUB.mjs";
|
|
66
|
+
import {
|
|
67
|
+
NextlyError
|
|
68
|
+
} from "../chunk-NRUWQ5Z7.mjs";
|
|
69
|
+
import {
|
|
70
|
+
simplePluralize
|
|
71
|
+
} from "../chunk-VTJADRO3.mjs";
|
|
72
|
+
import "../chunk-5APFUGAD.mjs";
|
|
73
|
+
import "../chunk-7P6ASYW6.mjs";
|
|
74
|
+
|
|
75
|
+
// src/api/collections-schema-detail.ts
|
|
76
|
+
async function getCollectionRegistry() {
|
|
77
|
+
await getCachedNextly();
|
|
78
|
+
return getService("collectionRegistryService");
|
|
79
|
+
}
|
|
80
|
+
async function getComponentRegistry() {
|
|
81
|
+
await getCachedNextly();
|
|
82
|
+
return getService("componentRegistryService");
|
|
83
|
+
}
|
|
84
|
+
async function requireUser(request) {
|
|
85
|
+
const result = await getSession(request, env.NEXTLY_SECRET || "");
|
|
86
|
+
const user = result.authenticated ? result.user : null;
|
|
87
|
+
if (!user) {
|
|
88
|
+
throw NextlyError.authRequired();
|
|
89
|
+
}
|
|
90
|
+
return { id: user.id };
|
|
91
|
+
}
|
|
92
|
+
async function requireManageSettings(userId) {
|
|
93
|
+
if (await isSuperAdmin(userId)) return;
|
|
94
|
+
const canManage = await hasPermission(userId, "manage", "settings");
|
|
95
|
+
if (!canManage) {
|
|
96
|
+
throw NextlyError.forbidden({
|
|
97
|
+
logContext: {
|
|
98
|
+
userId,
|
|
99
|
+
required: "manage-settings",
|
|
100
|
+
operation: "manage-collection"
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
var GET = withErrorHandler(
|
|
106
|
+
async (request, context) => {
|
|
107
|
+
const user = await requireUser(request);
|
|
108
|
+
const { slug } = await context.params;
|
|
109
|
+
const isAdmin = await isSuperAdmin(user.id);
|
|
110
|
+
if (!isAdmin) {
|
|
111
|
+
const canRead = await hasPermission(user.id, "read", slug);
|
|
112
|
+
if (!canRead) {
|
|
113
|
+
throw NextlyError.forbidden({
|
|
114
|
+
logContext: {
|
|
115
|
+
userId: user.id,
|
|
116
|
+
required: `read-${slug}`,
|
|
117
|
+
operation: "read-collection"
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const registry = await getCollectionRegistry();
|
|
123
|
+
const collection = await registry.getCollection(slug);
|
|
124
|
+
let enrichedFields = collection.fields;
|
|
125
|
+
try {
|
|
126
|
+
const componentRegistry = await getComponentRegistry();
|
|
127
|
+
enrichedFields = await componentRegistry.enrichFieldsWithComponentSchemas(
|
|
128
|
+
collection.fields
|
|
129
|
+
);
|
|
130
|
+
} catch (enrichError) {
|
|
131
|
+
getNextlyLogger().warn({
|
|
132
|
+
kind: "collection-schema-enrichment-failed",
|
|
133
|
+
slug,
|
|
134
|
+
err: String(enrichError)
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return respondDoc({
|
|
138
|
+
...collection,
|
|
139
|
+
fields: enrichedFields
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
var PATCH = withErrorHandler(
|
|
144
|
+
async (request, context) => {
|
|
145
|
+
const registry = await getCollectionRegistry();
|
|
146
|
+
const user = await requireUser(request);
|
|
147
|
+
await requireManageSettings(user.id);
|
|
148
|
+
const { slug } = await context.params;
|
|
149
|
+
let body;
|
|
150
|
+
try {
|
|
151
|
+
body = await request.json();
|
|
152
|
+
} catch {
|
|
153
|
+
throw NextlyError.validation({
|
|
154
|
+
errors: [
|
|
155
|
+
{
|
|
156
|
+
path: "",
|
|
157
|
+
code: "invalid_json",
|
|
158
|
+
message: "Request body is not valid JSON."
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
const updateData = {};
|
|
164
|
+
if (body.labels !== void 0) {
|
|
165
|
+
const labels = body.labels;
|
|
166
|
+
if (labels.singular !== void 0) {
|
|
167
|
+
const singular = labels.singular.trim();
|
|
168
|
+
updateData.labels = {
|
|
169
|
+
singular,
|
|
170
|
+
plural: labels.plural?.trim() || simplePluralize(singular)
|
|
171
|
+
};
|
|
172
|
+
} else {
|
|
173
|
+
updateData.labels = labels;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (body.description !== void 0) {
|
|
177
|
+
updateData.description = body.description;
|
|
178
|
+
}
|
|
179
|
+
if (body.fields !== void 0) {
|
|
180
|
+
updateData.fields = body.fields;
|
|
181
|
+
updateData.schemaHash = calculateSchemaHash(
|
|
182
|
+
body.fields
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
if (body.timestamps !== void 0) {
|
|
186
|
+
updateData.timestamps = body.timestamps;
|
|
187
|
+
}
|
|
188
|
+
if (body.status !== void 0) {
|
|
189
|
+
updateData.status = body.status === true;
|
|
190
|
+
}
|
|
191
|
+
const ADMIN_KEYS = [
|
|
192
|
+
"icon",
|
|
193
|
+
"group",
|
|
194
|
+
"order",
|
|
195
|
+
"sidebarGroup",
|
|
196
|
+
"hidden",
|
|
197
|
+
"useAsTitle",
|
|
198
|
+
"defaultColumns"
|
|
199
|
+
];
|
|
200
|
+
const adminOverrides = {};
|
|
201
|
+
if (body.admin !== void 0) {
|
|
202
|
+
const admin = body.admin;
|
|
203
|
+
for (const key of ADMIN_KEYS) {
|
|
204
|
+
if (admin[key] !== void 0) {
|
|
205
|
+
adminOverrides[key] = admin[key];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
for (const key of ADMIN_KEYS) {
|
|
210
|
+
if (body[key] !== void 0) {
|
|
211
|
+
adminOverrides[key] = body[key];
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (Object.keys(adminOverrides).length > 0) {
|
|
215
|
+
const existing = await registry.getCollection(slug);
|
|
216
|
+
updateData.admin = {
|
|
217
|
+
...existing.admin || {},
|
|
218
|
+
...adminOverrides
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
if (body.hooks !== void 0) {
|
|
222
|
+
updateData.hooks = body.hooks;
|
|
223
|
+
}
|
|
224
|
+
const updated = await registry.updateCollection(slug, updateData, {
|
|
225
|
+
source: "ui"
|
|
226
|
+
});
|
|
227
|
+
return respondMutation("Collection updated.", updated);
|
|
228
|
+
}
|
|
229
|
+
);
|
|
230
|
+
var DELETE = withErrorHandler(
|
|
231
|
+
async (request, context) => {
|
|
232
|
+
const registry = await getCollectionRegistry();
|
|
233
|
+
const user = await requireUser(request);
|
|
234
|
+
await requireManageSettings(user.id);
|
|
235
|
+
const { slug } = await context.params;
|
|
236
|
+
await registry.deleteCollection(slug);
|
|
237
|
+
return new Response(null, { status: 204 });
|
|
238
|
+
}
|
|
239
|
+
);
|
|
240
|
+
export {
|
|
241
|
+
DELETE,
|
|
242
|
+
GET,
|
|
243
|
+
PATCH
|
|
244
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collection Schema Export API Route Handler for Next.js
|
|
3
|
+
*
|
|
4
|
+
* This route handler can be re-exported in your Next.js application to provide
|
|
5
|
+
* collection export endpoints at /api/collections/schema/[slug]/export.
|
|
6
|
+
*
|
|
7
|
+
* Exports UI-created collections to code-first format (defineCollection syntax)
|
|
8
|
+
* for version control and customization.
|
|
9
|
+
*
|
|
10
|
+
* Services are auto-initialized on first request using environment variables:
|
|
11
|
+
* - DB_DIALECT: Database dialect ("postgresql" | "mysql" | "sqlite")
|
|
12
|
+
* - DATABASE_URL: Database connection string
|
|
13
|
+
*
|
|
14
|
+
* The JSON path returns `{ code }` via `respondData`; the `?download=true`
|
|
15
|
+
* path returns the raw code as `text/plain` with a `Content-Disposition`
|
|
16
|
+
* attachment header (binary/text-stream responses bypass JSON envelopes).
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // In your Next.js app: app/api/collections/schema/[slug]/export/route.ts
|
|
21
|
+
* export { GET } from 'nextly/api/collections-schema-export';
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @module api/collections-schema-export
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Context object for dynamic route handlers.
|
|
28
|
+
* Next.js 15+ requires params to be a Promise.
|
|
29
|
+
*/
|
|
30
|
+
interface RouteContext {
|
|
31
|
+
params: Promise<{
|
|
32
|
+
slug: string;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* GET handler for exporting a collection to code-first format.
|
|
37
|
+
*
|
|
38
|
+
* Requires authentication and read permission for the collection.
|
|
39
|
+
* Generates `defineCollection()` code from the collection's field config.
|
|
40
|
+
*
|
|
41
|
+
* Query Parameters:
|
|
42
|
+
* - includeAccess: Include access control placeholder comments (default: true)
|
|
43
|
+
* - includeHooks: Include hooks placeholder comments (default: true)
|
|
44
|
+
* - format: Output format - "typescript" or "javascript" (default: "typescript")
|
|
45
|
+
* - download: If "true", returns as downloadable file instead of JSON
|
|
46
|
+
*
|
|
47
|
+
* Response Codes:
|
|
48
|
+
* - 200 OK: Code generated successfully (JSON or file download)
|
|
49
|
+
* - 401 Unauthorized: Authentication required
|
|
50
|
+
* - 403 Forbidden: Caller lacks read permission for this collection
|
|
51
|
+
* - 404 Not Found: Collection with slug does not exist
|
|
52
|
+
* - 500 Internal Server Error: Export failed
|
|
53
|
+
*/
|
|
54
|
+
declare const GET: (request: Request, context: RouteContext) => Promise<Response>;
|
|
55
|
+
|
|
56
|
+
export { GET };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import "../chunk-LRXMECUA.mjs";
|
|
2
|
+
import {
|
|
3
|
+
getSession
|
|
4
|
+
} from "../chunk-UUOFWCM6.mjs";
|
|
5
|
+
import "../chunk-VWF3JO32.mjs";
|
|
6
|
+
import "../chunk-2ZFKXPQM.mjs";
|
|
7
|
+
import {
|
|
8
|
+
withErrorHandler
|
|
9
|
+
} from "../chunk-TO5AFLVQ.mjs";
|
|
10
|
+
import {
|
|
11
|
+
getCachedNextly
|
|
12
|
+
} from "../chunk-P7NH2OSC.mjs";
|
|
13
|
+
import "../chunk-2OALJTK6.mjs";
|
|
14
|
+
import "../chunk-VJ66NCL4.mjs";
|
|
15
|
+
import "../chunk-R6JJQHFC.mjs";
|
|
16
|
+
import "../chunk-X23WKS3Z.mjs";
|
|
17
|
+
import {
|
|
18
|
+
getService
|
|
19
|
+
} from "../chunk-X7TXCYYN.mjs";
|
|
20
|
+
import "../chunk-YZNBLFIW.mjs";
|
|
21
|
+
import "../chunk-FQULBZ53.mjs";
|
|
22
|
+
import "../chunk-67GXH6PR.mjs";
|
|
23
|
+
import "../chunk-2TFX4ND3.mjs";
|
|
24
|
+
import "../chunk-W4MGXIRR.mjs";
|
|
25
|
+
import {
|
|
26
|
+
CollectionExportService
|
|
27
|
+
} from "../chunk-NSEFNNU4.mjs";
|
|
28
|
+
import "../chunk-3FA7FKAV.mjs";
|
|
29
|
+
import "../chunk-AGJ6F2T3.mjs";
|
|
30
|
+
import "../chunk-KIMNCZGV.mjs";
|
|
31
|
+
import "../chunk-AK6Z23OX.mjs";
|
|
32
|
+
import "../chunk-INV7QKLG.mjs";
|
|
33
|
+
import "../chunk-GZ6DCQKC.mjs";
|
|
34
|
+
import "../chunk-SBACDPNX.mjs";
|
|
35
|
+
import "../chunk-RJLLGGPG.mjs";
|
|
36
|
+
import "../chunk-V4EQTOA4.mjs";
|
|
37
|
+
import "../chunk-I4JMR3UR.mjs";
|
|
38
|
+
import "../chunk-XZKLBMN6.mjs";
|
|
39
|
+
import "../chunk-IZWPRDC3.mjs";
|
|
40
|
+
import "../chunk-DNNG377Z.mjs";
|
|
41
|
+
import "../chunk-5HMZ644B.mjs";
|
|
42
|
+
import {
|
|
43
|
+
hasPermission,
|
|
44
|
+
isSuperAdmin
|
|
45
|
+
} from "../chunk-W5KKPZT5.mjs";
|
|
46
|
+
import "../chunk-56WO4WX7.mjs";
|
|
47
|
+
import "../chunk-2W3DVD7S.mjs";
|
|
48
|
+
import "../chunk-TS7GHTG2.mjs";
|
|
49
|
+
import "../chunk-H26B4FYG.mjs";
|
|
50
|
+
import "../chunk-DP3G27G5.mjs";
|
|
51
|
+
import "../chunk-DV6WVX2Q.mjs";
|
|
52
|
+
import {
|
|
53
|
+
env
|
|
54
|
+
} from "../chunk-UJ2IMJ4W.mjs";
|
|
55
|
+
import "../chunk-EGXBZCGC.mjs";
|
|
56
|
+
import "../chunk-G2AA4QLC.mjs";
|
|
57
|
+
import "../chunk-4MJLT6PZ.mjs";
|
|
58
|
+
import {
|
|
59
|
+
respondData
|
|
60
|
+
} from "../chunk-IUDOC7N7.mjs";
|
|
61
|
+
import "../chunk-LAZXX4HR.mjs";
|
|
62
|
+
import "../chunk-D5HQBNUB.mjs";
|
|
63
|
+
import {
|
|
64
|
+
NextlyError
|
|
65
|
+
} from "../chunk-NRUWQ5Z7.mjs";
|
|
66
|
+
import "../chunk-VTJADRO3.mjs";
|
|
67
|
+
import "../chunk-5APFUGAD.mjs";
|
|
68
|
+
import "../chunk-7P6ASYW6.mjs";
|
|
69
|
+
|
|
70
|
+
// src/api/collections-schema-export.ts
|
|
71
|
+
async function getCollectionRegistry() {
|
|
72
|
+
await getCachedNextly();
|
|
73
|
+
return getService("collectionRegistryService");
|
|
74
|
+
}
|
|
75
|
+
async function requireUser(request) {
|
|
76
|
+
const result = await getSession(request, env.NEXTLY_SECRET || "");
|
|
77
|
+
const user = result.authenticated ? result.user : null;
|
|
78
|
+
if (!user) {
|
|
79
|
+
throw NextlyError.authRequired();
|
|
80
|
+
}
|
|
81
|
+
return { id: user.id };
|
|
82
|
+
}
|
|
83
|
+
var GET = withErrorHandler(
|
|
84
|
+
async (request, context) => {
|
|
85
|
+
const user = await requireUser(request);
|
|
86
|
+
const { slug } = await context.params;
|
|
87
|
+
const isAdmin = await isSuperAdmin(user.id);
|
|
88
|
+
if (!isAdmin) {
|
|
89
|
+
const canRead = await hasPermission(user.id, "read", slug);
|
|
90
|
+
if (!canRead) {
|
|
91
|
+
throw NextlyError.forbidden({
|
|
92
|
+
logContext: {
|
|
93
|
+
userId: user.id,
|
|
94
|
+
required: `read-${slug}`,
|
|
95
|
+
operation: "export-collection"
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const registry = await getCollectionRegistry();
|
|
101
|
+
const collection = await registry.getCollection(slug);
|
|
102
|
+
const { searchParams } = new URL(request.url);
|
|
103
|
+
const includeAccessPlaceholders = searchParams.get("includeAccess") !== "false";
|
|
104
|
+
const includeHooksPlaceholders = searchParams.get("includeHooks") !== "false";
|
|
105
|
+
const format = searchParams.get("format") || "typescript";
|
|
106
|
+
const download = searchParams.get("download") === "true";
|
|
107
|
+
const exportService = new CollectionExportService();
|
|
108
|
+
const code = exportService.exportToCode(collection, {
|
|
109
|
+
includeAccessPlaceholders,
|
|
110
|
+
includeHooksPlaceholders,
|
|
111
|
+
format
|
|
112
|
+
});
|
|
113
|
+
if (download) {
|
|
114
|
+
const extension = format === "typescript" ? ".ts" : ".js";
|
|
115
|
+
const filename = `${slug}${extension}`;
|
|
116
|
+
return new Response(code, {
|
|
117
|
+
status: 200,
|
|
118
|
+
headers: {
|
|
119
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
120
|
+
"Content-Disposition": `attachment; filename="${filename}"`
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return respondData({ code });
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
export {
|
|
128
|
+
GET
|
|
129
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collection Schema API Route Handlers for Next.js
|
|
3
|
+
*
|
|
4
|
+
* These route handlers can be re-exported in your Next.js application to provide
|
|
5
|
+
* collection schema management endpoints at /api/collections/schema.
|
|
6
|
+
*
|
|
7
|
+
* Services are auto-initialized on first request using environment variables:
|
|
8
|
+
* - DB_DIALECT: Database dialect ("postgresql" | "mysql" | "sqlite")
|
|
9
|
+
* - DATABASE_URL: Database connection string
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* // In your Next.js app: app/api/collections/schema/route.ts
|
|
14
|
+
* export { GET, POST } from 'nextly/api/collections-schema';
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* @module api/collections-schema
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* GET handler for listing collections with pagination and filters.
|
|
21
|
+
*
|
|
22
|
+
* Returns only collections the caller has read permission for; super-admins
|
|
23
|
+
* see everything. Anonymous callers get 401.
|
|
24
|
+
*
|
|
25
|
+
* Query Parameters:
|
|
26
|
+
* - source: Filter by source type ("code" | "ui" | "built-in")
|
|
27
|
+
* - search: Search query for slug and labels
|
|
28
|
+
* - limit: Maximum results (default: 50)
|
|
29
|
+
* - offset: Number of results to skip (default: 0)
|
|
30
|
+
*
|
|
31
|
+
* Response Codes:
|
|
32
|
+
* - 200 OK: Collections list retrieved successfully
|
|
33
|
+
* - 401 Unauthorized: Authentication required
|
|
34
|
+
* - 500 Internal Server Error: Failed to fetch collections
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```bash
|
|
38
|
+
* curl "http://localhost:3000/api/collections/schema?source=ui&limit=10"
|
|
39
|
+
* # => {"items":[...],"meta":{"total":5,"page":1,"limit":10,"totalPages":1,"hasNext":false,"hasPrev":false}}
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
declare const GET: (request: Request) => Promise<Response>;
|
|
43
|
+
/**
|
|
44
|
+
* POST handler for creating a new UI collection.
|
|
45
|
+
*
|
|
46
|
+
* Requires super-admin or `manage-settings` permission. Creates a new
|
|
47
|
+
* collection with `source="ui"` and `locked=false`.
|
|
48
|
+
*
|
|
49
|
+
* Response Codes:
|
|
50
|
+
* - 201 Created: Collection created successfully
|
|
51
|
+
* - 400 Bad Request: Invalid input
|
|
52
|
+
* - 401 Unauthorized: Authentication required
|
|
53
|
+
* - 403 Forbidden: Caller lacks permission to create collections
|
|
54
|
+
* - 409 Conflict: Collection with slug already exists
|
|
55
|
+
* - 500 Internal Server Error: Creation failed
|
|
56
|
+
*/
|
|
57
|
+
declare const POST: (request: Request) => Promise<Response>;
|
|
58
|
+
|
|
59
|
+
export { GET, POST };
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import {
|
|
2
|
+
nextlyValidationFromZod
|
|
3
|
+
} from "../chunk-GJNSJU4S.mjs";
|
|
4
|
+
import "../chunk-LRXMECUA.mjs";
|
|
5
|
+
import {
|
|
6
|
+
getSession
|
|
7
|
+
} from "../chunk-UUOFWCM6.mjs";
|
|
8
|
+
import "../chunk-VWF3JO32.mjs";
|
|
9
|
+
import "../chunk-2ZFKXPQM.mjs";
|
|
10
|
+
import {
|
|
11
|
+
withErrorHandler
|
|
12
|
+
} from "../chunk-TO5AFLVQ.mjs";
|
|
13
|
+
import {
|
|
14
|
+
getCachedNextly
|
|
15
|
+
} from "../chunk-P7NH2OSC.mjs";
|
|
16
|
+
import "../chunk-2OALJTK6.mjs";
|
|
17
|
+
import "../chunk-VJ66NCL4.mjs";
|
|
18
|
+
import "../chunk-R6JJQHFC.mjs";
|
|
19
|
+
import "../chunk-X23WKS3Z.mjs";
|
|
20
|
+
import {
|
|
21
|
+
getService
|
|
22
|
+
} from "../chunk-X7TXCYYN.mjs";
|
|
23
|
+
import "../chunk-YZNBLFIW.mjs";
|
|
24
|
+
import "../chunk-FQULBZ53.mjs";
|
|
25
|
+
import "../chunk-67GXH6PR.mjs";
|
|
26
|
+
import "../chunk-2TFX4ND3.mjs";
|
|
27
|
+
import "../chunk-W4MGXIRR.mjs";
|
|
28
|
+
import "../chunk-NSEFNNU4.mjs";
|
|
29
|
+
import "../chunk-3FA7FKAV.mjs";
|
|
30
|
+
import "../chunk-AGJ6F2T3.mjs";
|
|
31
|
+
import "../chunk-KIMNCZGV.mjs";
|
|
32
|
+
import "../chunk-AK6Z23OX.mjs";
|
|
33
|
+
import "../chunk-INV7QKLG.mjs";
|
|
34
|
+
import "../chunk-GZ6DCQKC.mjs";
|
|
35
|
+
import "../chunk-SBACDPNX.mjs";
|
|
36
|
+
import "../chunk-RJLLGGPG.mjs";
|
|
37
|
+
import "../chunk-V4EQTOA4.mjs";
|
|
38
|
+
import "../chunk-I4JMR3UR.mjs";
|
|
39
|
+
import "../chunk-XZKLBMN6.mjs";
|
|
40
|
+
import "../chunk-IZWPRDC3.mjs";
|
|
41
|
+
import "../chunk-DNNG377Z.mjs";
|
|
42
|
+
import {
|
|
43
|
+
calculateSchemaHash
|
|
44
|
+
} from "../chunk-5HMZ644B.mjs";
|
|
45
|
+
import {
|
|
46
|
+
hasPermission,
|
|
47
|
+
isSuperAdmin
|
|
48
|
+
} from "../chunk-W5KKPZT5.mjs";
|
|
49
|
+
import "../chunk-56WO4WX7.mjs";
|
|
50
|
+
import "../chunk-2W3DVD7S.mjs";
|
|
51
|
+
import "../chunk-TS7GHTG2.mjs";
|
|
52
|
+
import "../chunk-H26B4FYG.mjs";
|
|
53
|
+
import "../chunk-DP3G27G5.mjs";
|
|
54
|
+
import "../chunk-DV6WVX2Q.mjs";
|
|
55
|
+
import {
|
|
56
|
+
env
|
|
57
|
+
} from "../chunk-UJ2IMJ4W.mjs";
|
|
58
|
+
import "../chunk-EGXBZCGC.mjs";
|
|
59
|
+
import "../chunk-G2AA4QLC.mjs";
|
|
60
|
+
import "../chunk-4MJLT6PZ.mjs";
|
|
61
|
+
import {
|
|
62
|
+
respondList,
|
|
63
|
+
respondMutation
|
|
64
|
+
} from "../chunk-IUDOC7N7.mjs";
|
|
65
|
+
import "../chunk-LAZXX4HR.mjs";
|
|
66
|
+
import "../chunk-D5HQBNUB.mjs";
|
|
67
|
+
import {
|
|
68
|
+
NextlyError
|
|
69
|
+
} from "../chunk-NRUWQ5Z7.mjs";
|
|
70
|
+
import {
|
|
71
|
+
simplePluralize
|
|
72
|
+
} from "../chunk-VTJADRO3.mjs";
|
|
73
|
+
import "../chunk-5APFUGAD.mjs";
|
|
74
|
+
import "../chunk-7P6ASYW6.mjs";
|
|
75
|
+
|
|
76
|
+
// src/api/collections-schema.ts
|
|
77
|
+
import { z } from "zod";
|
|
78
|
+
async function getCollectionRegistry() {
|
|
79
|
+
await getCachedNextly();
|
|
80
|
+
return getService("collectionRegistryService");
|
|
81
|
+
}
|
|
82
|
+
var createCollectionSchema = z.object({
|
|
83
|
+
slug: z.string().min(1, "Slug is required").max(255, "Slug must be 255 characters or less").regex(
|
|
84
|
+
/^[a-z][a-z0-9_]*$/,
|
|
85
|
+
"Slug must start with a letter and contain only lowercase letters, numbers, and underscores"
|
|
86
|
+
),
|
|
87
|
+
labels: z.object({
|
|
88
|
+
singular: z.string().trim().min(1, "Singular label is required"),
|
|
89
|
+
plural: z.string().trim().min(1, "Plural label is required").optional()
|
|
90
|
+
}),
|
|
91
|
+
description: z.string().optional(),
|
|
92
|
+
fields: z.array(z.any()),
|
|
93
|
+
// Field validation is complex, handled by service
|
|
94
|
+
timestamps: z.boolean().default(true),
|
|
95
|
+
// Draft/Published opt-in. Default false keeps the existing public API
|
|
96
|
+
// contract — collections without the flag continue to ship a single
|
|
97
|
+
// Save/Create button.
|
|
98
|
+
status: z.boolean().optional(),
|
|
99
|
+
admin: z.object({
|
|
100
|
+
group: z.string().optional(),
|
|
101
|
+
icon: z.string().optional(),
|
|
102
|
+
hidden: z.boolean().optional(),
|
|
103
|
+
useAsTitle: z.string().optional(),
|
|
104
|
+
isPlugin: z.boolean().optional(),
|
|
105
|
+
order: z.number().optional(),
|
|
106
|
+
sidebarGroup: z.string().optional()
|
|
107
|
+
}).optional(),
|
|
108
|
+
hooks: z.array(z.any()).optional()
|
|
109
|
+
});
|
|
110
|
+
async function requireUser(request) {
|
|
111
|
+
const result = await getSession(request, env.NEXTLY_SECRET || "");
|
|
112
|
+
const user = result.authenticated ? result.user : null;
|
|
113
|
+
if (!user) {
|
|
114
|
+
throw NextlyError.authRequired();
|
|
115
|
+
}
|
|
116
|
+
return { id: user.id };
|
|
117
|
+
}
|
|
118
|
+
var GET = withErrorHandler(async (request) => {
|
|
119
|
+
const user = await requireUser(request);
|
|
120
|
+
const registry = await getCollectionRegistry();
|
|
121
|
+
const { searchParams } = new URL(request.url);
|
|
122
|
+
const source = searchParams.get("source");
|
|
123
|
+
const search = searchParams.get("search") || void 0;
|
|
124
|
+
const limit = searchParams.get("limit") ? parseInt(searchParams.get("limit"), 10) : 50;
|
|
125
|
+
const offset = searchParams.get("offset") ? parseInt(searchParams.get("offset"), 10) : 0;
|
|
126
|
+
const result = await registry.listCollections({
|
|
127
|
+
source: source || void 0,
|
|
128
|
+
search,
|
|
129
|
+
limit,
|
|
130
|
+
offset
|
|
131
|
+
});
|
|
132
|
+
const isAdmin = await isSuperAdmin(user.id);
|
|
133
|
+
let filteredCollections = result.data;
|
|
134
|
+
if (!isAdmin) {
|
|
135
|
+
const permittedCollections = [];
|
|
136
|
+
for (const collection of result.data) {
|
|
137
|
+
const collectionSlug = collection.slug;
|
|
138
|
+
const canRead = await hasPermission(user.id, "read", collectionSlug);
|
|
139
|
+
if (canRead) {
|
|
140
|
+
permittedCollections.push(collection);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
filteredCollections = permittedCollections;
|
|
144
|
+
}
|
|
145
|
+
const safeLimit = Math.max(1, limit);
|
|
146
|
+
const page = Math.floor(offset / safeLimit) + 1;
|
|
147
|
+
const total = filteredCollections.length;
|
|
148
|
+
const totalPages = Math.ceil(total / safeLimit);
|
|
149
|
+
return respondList(filteredCollections, {
|
|
150
|
+
total,
|
|
151
|
+
page,
|
|
152
|
+
limit: safeLimit,
|
|
153
|
+
totalPages,
|
|
154
|
+
hasNext: page < totalPages,
|
|
155
|
+
hasPrev: page > 1
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
var POST = withErrorHandler(async (request) => {
|
|
159
|
+
const user = await requireUser(request);
|
|
160
|
+
const registry = await getCollectionRegistry();
|
|
161
|
+
const isAdmin = await isSuperAdmin(user.id);
|
|
162
|
+
if (!isAdmin) {
|
|
163
|
+
const canManage = await hasPermission(user.id, "manage", "settings");
|
|
164
|
+
if (!canManage) {
|
|
165
|
+
throw NextlyError.forbidden({
|
|
166
|
+
logContext: {
|
|
167
|
+
userId: user.id,
|
|
168
|
+
required: "manage-settings",
|
|
169
|
+
operation: "create-collection"
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const body = await request.json();
|
|
175
|
+
const parseResult = createCollectionSchema.safeParse(body);
|
|
176
|
+
if (!parseResult.success) {
|
|
177
|
+
throw nextlyValidationFromZod(parseResult.error);
|
|
178
|
+
}
|
|
179
|
+
const validated = parseResult.data;
|
|
180
|
+
const tableName = validated.slug.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
181
|
+
const schemaHash = calculateSchemaHash(validated.fields);
|
|
182
|
+
const normalizedLabels = {
|
|
183
|
+
singular: validated.labels.singular.trim(),
|
|
184
|
+
plural: validated.labels.plural?.trim() || simplePluralize(validated.labels.singular)
|
|
185
|
+
};
|
|
186
|
+
const collection = await registry.registerCollection({
|
|
187
|
+
slug: validated.slug,
|
|
188
|
+
labels: normalizedLabels,
|
|
189
|
+
tableName,
|
|
190
|
+
description: validated.description,
|
|
191
|
+
fields: validated.fields,
|
|
192
|
+
timestamps: validated.timestamps,
|
|
193
|
+
admin: validated.admin,
|
|
194
|
+
source: "ui",
|
|
195
|
+
locked: false,
|
|
196
|
+
// Forward Draft/Published flag so the schema-level POST honours the
|
|
197
|
+
// user's status opt-in just like the dispatcher path does.
|
|
198
|
+
status: validated.status === true,
|
|
199
|
+
schemaHash,
|
|
200
|
+
hooks: validated.hooks
|
|
201
|
+
});
|
|
202
|
+
return respondMutation("Collection created.", collection, { status: 201 });
|
|
203
|
+
});
|
|
204
|
+
export {
|
|
205
|
+
GET,
|
|
206
|
+
POST
|
|
207
|
+
};
|