nextly 0.0.1 → 0.0.2-alpha.1
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,2605 @@
|
|
|
1
|
+
import {
|
|
2
|
+
reloadNextlyConfig,
|
|
3
|
+
resolveCollectionTableName
|
|
4
|
+
} from "./chunk-2OALJTK6.mjs";
|
|
5
|
+
import {
|
|
6
|
+
runBootTimeApplyIfDev
|
|
7
|
+
} from "./chunk-R6JJQHFC.mjs";
|
|
8
|
+
import {
|
|
9
|
+
buildClaims,
|
|
10
|
+
signAccessToken
|
|
11
|
+
} from "./chunk-X23WKS3Z.mjs";
|
|
12
|
+
import {
|
|
13
|
+
getService,
|
|
14
|
+
isServicesRegistered,
|
|
15
|
+
registerServices,
|
|
16
|
+
shutdownServices
|
|
17
|
+
} from "./chunk-X7TXCYYN.mjs";
|
|
18
|
+
import {
|
|
19
|
+
AuthService,
|
|
20
|
+
PermissionService,
|
|
21
|
+
RolePermissionService,
|
|
22
|
+
RoleService,
|
|
23
|
+
UserAccountService,
|
|
24
|
+
transformRichTextFields
|
|
25
|
+
} from "./chunk-NSEFNNU4.mjs";
|
|
26
|
+
import {
|
|
27
|
+
env
|
|
28
|
+
} from "./chunk-UJ2IMJ4W.mjs";
|
|
29
|
+
import {
|
|
30
|
+
getImageProcessor
|
|
31
|
+
} from "./chunk-EGXBZCGC.mjs";
|
|
32
|
+
import {
|
|
33
|
+
container
|
|
34
|
+
} from "./chunk-D5HQBNUB.mjs";
|
|
35
|
+
import {
|
|
36
|
+
NextlyError
|
|
37
|
+
} from "./chunk-NRUWQ5Z7.mjs";
|
|
38
|
+
|
|
39
|
+
// src/direct-api/namespaces/auth.ts
|
|
40
|
+
async function login(ctx, args) {
|
|
41
|
+
const user = await ctx.authService.verifyCredentials(
|
|
42
|
+
args.email,
|
|
43
|
+
args.password
|
|
44
|
+
);
|
|
45
|
+
const secret = env.NEXTLY_SECRET;
|
|
46
|
+
if (!secret) {
|
|
47
|
+
throw new NextlyError({
|
|
48
|
+
code: "INTERNAL_ERROR",
|
|
49
|
+
publicMessage: "NEXTLY_SECRET is not configured. Set NEXTLY_SECRET in your environment variables.",
|
|
50
|
+
statusCode: 500
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const maxAge = 30 * 24 * 60 * 60;
|
|
54
|
+
const exp = Math.floor(Date.now() / 1e3) + maxAge;
|
|
55
|
+
const claims = buildClaims({
|
|
56
|
+
userId: String(user.id),
|
|
57
|
+
email: user.email,
|
|
58
|
+
name: user.name || "",
|
|
59
|
+
image: user.image ?? null,
|
|
60
|
+
roleIds: []
|
|
61
|
+
});
|
|
62
|
+
const token = await signAccessToken(claims, secret, maxAge);
|
|
63
|
+
return {
|
|
64
|
+
user,
|
|
65
|
+
token,
|
|
66
|
+
exp
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
async function logout() {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
async function me(ctx, args) {
|
|
73
|
+
if (!args.user?.id) {
|
|
74
|
+
throw new NextlyError({
|
|
75
|
+
code: "INVALID_INPUT",
|
|
76
|
+
publicMessage: "user.id is required for me() - Direct API requires explicit user context",
|
|
77
|
+
statusCode: 400
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const user = await ctx.userAccountService.getCurrentUser(args.user.id);
|
|
82
|
+
return {
|
|
83
|
+
user
|
|
84
|
+
};
|
|
85
|
+
} catch (err) {
|
|
86
|
+
if (NextlyError.isNotFound(err)) {
|
|
87
|
+
throw NextlyError.notFound({ logContext: { userId: args.user.id } });
|
|
88
|
+
}
|
|
89
|
+
throw err;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function updateMe(ctx, args) {
|
|
93
|
+
if (!args.user?.id) {
|
|
94
|
+
throw new NextlyError({
|
|
95
|
+
code: "INVALID_INPUT",
|
|
96
|
+
publicMessage: "user.id is required for updateMe() - Direct API requires explicit user context",
|
|
97
|
+
statusCode: 400
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const user = await ctx.userAccountService.updateCurrentUser(
|
|
102
|
+
args.user.id,
|
|
103
|
+
args.data
|
|
104
|
+
);
|
|
105
|
+
return {
|
|
106
|
+
user
|
|
107
|
+
};
|
|
108
|
+
} catch (err) {
|
|
109
|
+
if (NextlyError.isNotFound(err)) {
|
|
110
|
+
throw NextlyError.notFound({ logContext: { userId: args.user.id } });
|
|
111
|
+
}
|
|
112
|
+
throw err;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function register(ctx, args) {
|
|
116
|
+
const { email, password, collection: _collection, ...rest } = args;
|
|
117
|
+
const user = await ctx.authService.registerUser({
|
|
118
|
+
email,
|
|
119
|
+
password,
|
|
120
|
+
name: rest.name
|
|
121
|
+
});
|
|
122
|
+
return {
|
|
123
|
+
user
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
async function changePassword(ctx, args) {
|
|
127
|
+
if (!args.user?.id) {
|
|
128
|
+
throw new NextlyError({
|
|
129
|
+
code: "INVALID_INPUT",
|
|
130
|
+
publicMessage: "user.id is required for changePassword() - Direct API requires explicit user context",
|
|
131
|
+
statusCode: 400
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
await ctx.authService.changePassword(
|
|
135
|
+
args.user.id,
|
|
136
|
+
args.currentPassword,
|
|
137
|
+
args.newPassword
|
|
138
|
+
);
|
|
139
|
+
return { success: true };
|
|
140
|
+
}
|
|
141
|
+
async function forgotPassword(ctx, args) {
|
|
142
|
+
const result = await ctx.authService.generatePasswordResetToken(args.email, {
|
|
143
|
+
disableEmail: args.disableEmail,
|
|
144
|
+
expiration: args.expiration,
|
|
145
|
+
redirectPath: args.redirectPath
|
|
146
|
+
});
|
|
147
|
+
return {
|
|
148
|
+
success: true,
|
|
149
|
+
token: result.token
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
async function resetPassword(ctx, args) {
|
|
153
|
+
const result = await ctx.authService.resetPasswordWithToken(
|
|
154
|
+
args.token,
|
|
155
|
+
args.password
|
|
156
|
+
);
|
|
157
|
+
return {
|
|
158
|
+
success: true,
|
|
159
|
+
email: result.email
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
async function verifyEmail(ctx, args) {
|
|
163
|
+
const result = await ctx.authService.verifyEmail(args.token);
|
|
164
|
+
return {
|
|
165
|
+
success: true,
|
|
166
|
+
email: result.email
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/direct-api/namespaces/helpers.ts
|
|
171
|
+
function mergeConfig(defaultConfig, args) {
|
|
172
|
+
return {
|
|
173
|
+
...defaultConfig,
|
|
174
|
+
...args
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function createRequestContext(args) {
|
|
178
|
+
if (!args.user) {
|
|
179
|
+
return { locale: args.locale };
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
user: {
|
|
183
|
+
id: args.user.id,
|
|
184
|
+
email: "",
|
|
185
|
+
role: args.user.role ?? "user",
|
|
186
|
+
permissions: []
|
|
187
|
+
},
|
|
188
|
+
locale: args.locale
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function createErrorFromResult(result) {
|
|
192
|
+
return new NextlyError({
|
|
193
|
+
code: statusCodeToErrorCode(result.statusCode),
|
|
194
|
+
publicMessage: result.message,
|
|
195
|
+
statusCode: result.statusCode,
|
|
196
|
+
logContext: result.data !== void 0 && result.data !== null ? { resultData: result.data } : void 0
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function createErrorFromSingleResult(result) {
|
|
200
|
+
const message = result.message || result.errors?.map((e) => e.message).join(", ") || "Operation failed";
|
|
201
|
+
if (result.statusCode === 400 && result.errors && result.errors.length > 0) {
|
|
202
|
+
return NextlyError.validation({
|
|
203
|
+
errors: result.errors.map((e) => ({
|
|
204
|
+
path: e.field ?? "",
|
|
205
|
+
code: "VALIDATION_ERROR",
|
|
206
|
+
message: e.message
|
|
207
|
+
}))
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return new NextlyError({
|
|
211
|
+
code: statusCodeToErrorCode(result.statusCode),
|
|
212
|
+
publicMessage: message,
|
|
213
|
+
statusCode: result.statusCode
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
function statusCodeToErrorCode(statusCode) {
|
|
217
|
+
switch (statusCode) {
|
|
218
|
+
case 400:
|
|
219
|
+
return "VALIDATION_ERROR";
|
|
220
|
+
case 401:
|
|
221
|
+
return "AUTH_REQUIRED";
|
|
222
|
+
case 403:
|
|
223
|
+
return "FORBIDDEN";
|
|
224
|
+
case 404:
|
|
225
|
+
return "NOT_FOUND";
|
|
226
|
+
case 409:
|
|
227
|
+
return "CONFLICT";
|
|
228
|
+
case 413:
|
|
229
|
+
return "PAYLOAD_TOO_LARGE";
|
|
230
|
+
case 415:
|
|
231
|
+
return "UNSUPPORTED_MEDIA_TYPE";
|
|
232
|
+
case 422:
|
|
233
|
+
return "INVALID_INPUT";
|
|
234
|
+
case 429:
|
|
235
|
+
return "RATE_LIMITED";
|
|
236
|
+
case 502:
|
|
237
|
+
return "EXTERNAL_SERVICE_ERROR";
|
|
238
|
+
case 503:
|
|
239
|
+
return "SERVICE_UNAVAILABLE";
|
|
240
|
+
default:
|
|
241
|
+
return "INTERNAL_ERROR";
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function isNotFoundError(error) {
|
|
245
|
+
return NextlyError.isNotFound(error);
|
|
246
|
+
}
|
|
247
|
+
function looksLikeId(value) {
|
|
248
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
|
249
|
+
value
|
|
250
|
+
)) {
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
if (/^\d+$/.test(value)) {
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
if (/^c[a-z0-9]{24,}$/i.test(value)) {
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
function mapRole(role) {
|
|
262
|
+
return {
|
|
263
|
+
id: role.id,
|
|
264
|
+
name: role.name,
|
|
265
|
+
slug: role.slug,
|
|
266
|
+
description: role.description ?? null,
|
|
267
|
+
level: role.level,
|
|
268
|
+
isSystem: Boolean(role.isSystem)
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function mapPermission(perm) {
|
|
272
|
+
return {
|
|
273
|
+
id: perm.id,
|
|
274
|
+
name: perm.name,
|
|
275
|
+
slug: perm.slug,
|
|
276
|
+
action: perm.action,
|
|
277
|
+
resource: perm.resource,
|
|
278
|
+
description: perm.description ?? null
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
function mapComponentRecord(record) {
|
|
282
|
+
return {
|
|
283
|
+
id: record.id,
|
|
284
|
+
slug: record.slug,
|
|
285
|
+
label: record.label,
|
|
286
|
+
tableName: record.tableName,
|
|
287
|
+
description: record.description ?? void 0,
|
|
288
|
+
fields: Array.isArray(record.fields) ? record.fields : [],
|
|
289
|
+
admin: record.admin,
|
|
290
|
+
source: record.source,
|
|
291
|
+
locked: record.locked,
|
|
292
|
+
configPath: record.configPath ?? void 0,
|
|
293
|
+
schemaHash: record.schemaHash,
|
|
294
|
+
schemaVersion: record.schemaVersion,
|
|
295
|
+
migrationStatus: record.migrationStatus,
|
|
296
|
+
lastMigrationId: record.lastMigrationId ?? void 0,
|
|
297
|
+
createdBy: record.createdBy ?? void 0,
|
|
298
|
+
createdAt: record.createdAt,
|
|
299
|
+
updatedAt: record.updatedAt
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function toListResult(result, limit, page) {
|
|
303
|
+
const total = result.pagination.total;
|
|
304
|
+
const totalPages = Math.max(1, Math.ceil(total / limit));
|
|
305
|
+
return {
|
|
306
|
+
items: result.data,
|
|
307
|
+
meta: {
|
|
308
|
+
total,
|
|
309
|
+
page,
|
|
310
|
+
limit,
|
|
311
|
+
totalPages,
|
|
312
|
+
hasNext: page < totalPages,
|
|
313
|
+
hasPrev: page > 1
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function sliceListResult(items, limit, page) {
|
|
318
|
+
const effectiveLimit = limit ?? items.length;
|
|
319
|
+
const effectivePage = page ?? 1;
|
|
320
|
+
const start = (effectivePage - 1) * effectiveLimit;
|
|
321
|
+
const paged = items.slice(start, start + effectiveLimit);
|
|
322
|
+
const total = items.length;
|
|
323
|
+
const totalPages = effectiveLimit > 0 ? Math.max(1, Math.ceil(total / effectiveLimit)) : 1;
|
|
324
|
+
return {
|
|
325
|
+
items: paged,
|
|
326
|
+
meta: {
|
|
327
|
+
total,
|
|
328
|
+
page: effectivePage,
|
|
329
|
+
limit: effectiveLimit,
|
|
330
|
+
totalPages,
|
|
331
|
+
hasNext: effectivePage < totalPages,
|
|
332
|
+
hasPrev: effectivePage > 1
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
function buildMutationMessage(collection, verb) {
|
|
337
|
+
const noun = collection.charAt(0).toUpperCase() + collection.slice(1);
|
|
338
|
+
return `${noun} ${verb}.`;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// src/direct-api/namespaces/collections.ts
|
|
342
|
+
async function find(ctx, args) {
|
|
343
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
344
|
+
const result = await ctx.collectionsHandler.listEntries({
|
|
345
|
+
collectionName: args.collection,
|
|
346
|
+
page: args.page,
|
|
347
|
+
limit: args.limit,
|
|
348
|
+
where: args.where,
|
|
349
|
+
depth: config.depth,
|
|
350
|
+
select: args.select,
|
|
351
|
+
sort: args.sort,
|
|
352
|
+
richTextFormat: config.richTextFormat,
|
|
353
|
+
overrideAccess: config.overrideAccess,
|
|
354
|
+
user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
|
|
355
|
+
context: config.context
|
|
356
|
+
});
|
|
357
|
+
if (!result.success) {
|
|
358
|
+
throw createErrorFromResult(result);
|
|
359
|
+
}
|
|
360
|
+
const legacy = result.data;
|
|
361
|
+
const total = legacy.totalDocs ?? legacy.docs.length;
|
|
362
|
+
const limit = legacy.limit ?? args.limit ?? legacy.docs.length;
|
|
363
|
+
const page = legacy.page ?? args.page ?? 1;
|
|
364
|
+
const totalPages = legacy.totalPages ?? Math.max(1, Math.ceil(total / Math.max(limit, 1)));
|
|
365
|
+
return {
|
|
366
|
+
items: legacy.docs,
|
|
367
|
+
meta: {
|
|
368
|
+
total,
|
|
369
|
+
page,
|
|
370
|
+
limit,
|
|
371
|
+
totalPages,
|
|
372
|
+
hasNext: legacy.hasNextPage ?? page < totalPages,
|
|
373
|
+
hasPrev: legacy.hasPrevPage ?? page > 1
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
async function findByID(ctx, args) {
|
|
378
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
379
|
+
try {
|
|
380
|
+
const result = await ctx.collectionsHandler.getEntry({
|
|
381
|
+
collectionName: args.collection,
|
|
382
|
+
entryId: args.id,
|
|
383
|
+
depth: config.depth,
|
|
384
|
+
select: args.select,
|
|
385
|
+
richTextFormat: config.richTextFormat,
|
|
386
|
+
overrideAccess: config.overrideAccess,
|
|
387
|
+
user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
|
|
388
|
+
context: config.context
|
|
389
|
+
});
|
|
390
|
+
if (!result.success) {
|
|
391
|
+
if (config.disableErrors) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
throw createErrorFromResult(result);
|
|
395
|
+
}
|
|
396
|
+
return result.data;
|
|
397
|
+
} catch (error) {
|
|
398
|
+
if (config.disableErrors && isNotFoundError(error)) {
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
throw error;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
async function create(ctx, args) {
|
|
405
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
406
|
+
const result = await ctx.collectionsHandler.createEntry(
|
|
407
|
+
{
|
|
408
|
+
collectionName: args.collection,
|
|
409
|
+
overrideAccess: config.overrideAccess,
|
|
410
|
+
user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
|
|
411
|
+
context: config.context
|
|
412
|
+
},
|
|
413
|
+
args.data
|
|
414
|
+
);
|
|
415
|
+
if (!result.success) {
|
|
416
|
+
throw createErrorFromResult(result);
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
message: buildMutationMessage(args.collection, "created"),
|
|
420
|
+
item: result.data
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
async function update(ctx, args) {
|
|
424
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
425
|
+
if (!args.id && !args.where) {
|
|
426
|
+
throw new NextlyError({
|
|
427
|
+
code: "INVALID_INPUT",
|
|
428
|
+
publicMessage: "Either 'id' or 'where' clause is required for update",
|
|
429
|
+
statusCode: 400
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
if (args.id) {
|
|
433
|
+
const result = await ctx.collectionsHandler.updateEntry(
|
|
434
|
+
{
|
|
435
|
+
collectionName: args.collection,
|
|
436
|
+
entryId: args.id,
|
|
437
|
+
overrideAccess: config.overrideAccess,
|
|
438
|
+
user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
|
|
439
|
+
context: config.context
|
|
440
|
+
},
|
|
441
|
+
args.data
|
|
442
|
+
);
|
|
443
|
+
if (!result.success) {
|
|
444
|
+
throw createErrorFromResult(result);
|
|
445
|
+
}
|
|
446
|
+
return {
|
|
447
|
+
message: buildMutationMessage(args.collection, "updated"),
|
|
448
|
+
item: result.data
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
if (args.where) {
|
|
452
|
+
const bulkResult = await ctx.collectionsHandler.bulkUpdateByQuery(
|
|
453
|
+
{
|
|
454
|
+
collectionName: args.collection,
|
|
455
|
+
where: args.where,
|
|
456
|
+
data: args.data,
|
|
457
|
+
overrideAccess: config.overrideAccess,
|
|
458
|
+
user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
|
|
459
|
+
context: config.context
|
|
460
|
+
},
|
|
461
|
+
{ limit: 1 }
|
|
462
|
+
);
|
|
463
|
+
if (bulkResult.successCount === 0) {
|
|
464
|
+
throw NextlyError.notFound({
|
|
465
|
+
logContext: {
|
|
466
|
+
collection: args.collection,
|
|
467
|
+
where: args.where,
|
|
468
|
+
reason: "where-clause-no-match"
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
const updated = await findByID(ctx, {
|
|
473
|
+
collection: args.collection,
|
|
474
|
+
id: bulkResult.successes[0].id
|
|
475
|
+
});
|
|
476
|
+
if (!updated) {
|
|
477
|
+
throw new NextlyError({
|
|
478
|
+
code: "INTERNAL_ERROR",
|
|
479
|
+
publicMessage: "Document was updated but could not be retrieved",
|
|
480
|
+
statusCode: 500
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
return {
|
|
484
|
+
message: buildMutationMessage(args.collection, "updated"),
|
|
485
|
+
item: updated
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
throw new NextlyError({
|
|
489
|
+
code: "INVALID_INPUT",
|
|
490
|
+
publicMessage: "Either 'id' or 'where' clause is required for update",
|
|
491
|
+
statusCode: 400
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
async function deleteEntry(ctx, args) {
|
|
495
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
496
|
+
if (!args.id && !args.where) {
|
|
497
|
+
throw new NextlyError({
|
|
498
|
+
code: "INVALID_INPUT",
|
|
499
|
+
publicMessage: "Either 'id' or 'where' clause is required for delete",
|
|
500
|
+
statusCode: 400
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
if (args.id) {
|
|
504
|
+
const result = await ctx.collectionsHandler.deleteEntry({
|
|
505
|
+
collectionName: args.collection,
|
|
506
|
+
entryId: args.id,
|
|
507
|
+
overrideAccess: config.overrideAccess,
|
|
508
|
+
user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
|
|
509
|
+
context: config.context
|
|
510
|
+
});
|
|
511
|
+
if (!result.success) {
|
|
512
|
+
throw createErrorFromResult(result);
|
|
513
|
+
}
|
|
514
|
+
return {
|
|
515
|
+
message: buildMutationMessage(args.collection, "deleted"),
|
|
516
|
+
item: { id: args.id }
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
if (args.where) {
|
|
520
|
+
const bulkResult = await ctx.collectionsHandler.bulkDeleteByQuery(
|
|
521
|
+
{
|
|
522
|
+
collectionName: args.collection,
|
|
523
|
+
where: args.where,
|
|
524
|
+
overrideAccess: config.overrideAccess,
|
|
525
|
+
user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
|
|
526
|
+
context: config.context
|
|
527
|
+
},
|
|
528
|
+
{ limit: 1e3 }
|
|
529
|
+
);
|
|
530
|
+
return {
|
|
531
|
+
deleted: true,
|
|
532
|
+
ids: bulkResult.successes.map((s) => s.id)
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
throw new NextlyError({
|
|
536
|
+
code: "INVALID_INPUT",
|
|
537
|
+
publicMessage: "Either 'id' or 'where' clause is required for delete",
|
|
538
|
+
statusCode: 400
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
async function count(ctx, args) {
|
|
542
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
543
|
+
const result = await ctx.collectionsHandler.countEntries({
|
|
544
|
+
collectionName: args.collection,
|
|
545
|
+
where: args.where,
|
|
546
|
+
overrideAccess: config.overrideAccess,
|
|
547
|
+
user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
|
|
548
|
+
context: config.context
|
|
549
|
+
});
|
|
550
|
+
if (!result.success) {
|
|
551
|
+
throw createErrorFromResult(result);
|
|
552
|
+
}
|
|
553
|
+
const legacy = result.data;
|
|
554
|
+
return { total: legacy.total ?? legacy.totalDocs ?? 0 };
|
|
555
|
+
}
|
|
556
|
+
async function bulkDelete(ctx, args) {
|
|
557
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
558
|
+
const bulkResult = await ctx.collectionsHandler.bulkDeleteEntries({
|
|
559
|
+
collectionName: args.collection,
|
|
560
|
+
ids: args.ids,
|
|
561
|
+
overrideAccess: config.overrideAccess,
|
|
562
|
+
user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
|
|
563
|
+
context: config.context
|
|
564
|
+
});
|
|
565
|
+
return bulkResult;
|
|
566
|
+
}
|
|
567
|
+
async function duplicate(ctx, args) {
|
|
568
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
569
|
+
const result = await ctx.collectionsHandler.duplicateEntry({
|
|
570
|
+
collectionName: args.collection,
|
|
571
|
+
entryId: args.id,
|
|
572
|
+
overrides: args.overrides,
|
|
573
|
+
overrideAccess: config.overrideAccess,
|
|
574
|
+
user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
|
|
575
|
+
context: config.context
|
|
576
|
+
});
|
|
577
|
+
if (!result.success) {
|
|
578
|
+
throw createErrorFromResult(result);
|
|
579
|
+
}
|
|
580
|
+
return {
|
|
581
|
+
message: buildMutationMessage(args.collection, "duplicated"),
|
|
582
|
+
item: result.data
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// src/direct-api/namespaces/singles.ts
|
|
587
|
+
async function findSingle(ctx, args) {
|
|
588
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
589
|
+
const result = await ctx.singleEntryService.get(args.slug, {
|
|
590
|
+
depth: config.depth,
|
|
591
|
+
locale: config.locale,
|
|
592
|
+
user: config.user ? { id: config.user.id, email: config.user.role } : void 0,
|
|
593
|
+
overrideAccess: config.overrideAccess,
|
|
594
|
+
context: config.context
|
|
595
|
+
});
|
|
596
|
+
if (!result.success) {
|
|
597
|
+
throw createErrorFromSingleResult(result);
|
|
598
|
+
}
|
|
599
|
+
let data = result.data;
|
|
600
|
+
if (config.richTextFormat && config.richTextFormat !== "json" && result.data) {
|
|
601
|
+
const single = await ctx.singleRegistryService.getSingleBySlug(args.slug);
|
|
602
|
+
if (single?.fields && Array.isArray(single.fields)) {
|
|
603
|
+
data = transformRichTextFields(
|
|
604
|
+
result.data,
|
|
605
|
+
single.fields,
|
|
606
|
+
config.richTextFormat
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
return data;
|
|
611
|
+
}
|
|
612
|
+
async function updateSingle(ctx, args) {
|
|
613
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
614
|
+
const result = await ctx.singleEntryService.update(args.slug, args.data, {
|
|
615
|
+
locale: config.locale,
|
|
616
|
+
user: config.user ? { id: config.user.id, email: config.user.role } : void 0,
|
|
617
|
+
overrideAccess: config.overrideAccess,
|
|
618
|
+
context: config.context
|
|
619
|
+
});
|
|
620
|
+
if (!result.success) {
|
|
621
|
+
throw createErrorFromSingleResult(result);
|
|
622
|
+
}
|
|
623
|
+
return result.data;
|
|
624
|
+
}
|
|
625
|
+
async function findSingles(ctx, args = {}) {
|
|
626
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
627
|
+
const registryResult = await ctx.singleRegistryService.listSingles({
|
|
628
|
+
source: args.source,
|
|
629
|
+
migrationStatus: args.migrationStatus,
|
|
630
|
+
locked: args.locked,
|
|
631
|
+
search: args.search,
|
|
632
|
+
limit: args.limit,
|
|
633
|
+
offset: args.offset
|
|
634
|
+
});
|
|
635
|
+
const entries = await Promise.all(
|
|
636
|
+
registryResult.data.map(async (record) => {
|
|
637
|
+
const result = await ctx.singleEntryService.get(record.slug, {
|
|
638
|
+
depth: config.depth,
|
|
639
|
+
locale: config.locale,
|
|
640
|
+
user: config.user ? { id: config.user.id, email: config.user.role } : void 0,
|
|
641
|
+
overrideAccess: config.overrideAccess,
|
|
642
|
+
context: config.context
|
|
643
|
+
});
|
|
644
|
+
if (!result.success) {
|
|
645
|
+
throw createErrorFromSingleResult(result);
|
|
646
|
+
}
|
|
647
|
+
return {
|
|
648
|
+
slug: record.slug,
|
|
649
|
+
label: record.label,
|
|
650
|
+
data: result.data
|
|
651
|
+
};
|
|
652
|
+
})
|
|
653
|
+
);
|
|
654
|
+
return {
|
|
655
|
+
docs: entries,
|
|
656
|
+
totalDocs: registryResult.total,
|
|
657
|
+
limit: args.limit ?? registryResult.data.length,
|
|
658
|
+
offset: args.offset ?? 0
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// src/direct-api/namespaces/users.ts
|
|
663
|
+
function createUsersNamespace(ctx) {
|
|
664
|
+
return {
|
|
665
|
+
async find(args = {}) {
|
|
666
|
+
const limit = args.limit ?? 10;
|
|
667
|
+
const page = args.page ?? 1;
|
|
668
|
+
const result = await ctx.userService.listUsers(
|
|
669
|
+
{
|
|
670
|
+
pagination: { limit, page },
|
|
671
|
+
search: args.search,
|
|
672
|
+
emailVerified: args.emailVerified,
|
|
673
|
+
hasPassword: args.hasPassword,
|
|
674
|
+
sortBy: args.sortBy,
|
|
675
|
+
sortOrder: args.sortOrder
|
|
676
|
+
},
|
|
677
|
+
createRequestContext(args)
|
|
678
|
+
);
|
|
679
|
+
return toListResult(result, limit, page);
|
|
680
|
+
},
|
|
681
|
+
async findOne(args = {}) {
|
|
682
|
+
const result = await ctx.userService.listUsers(
|
|
683
|
+
{
|
|
684
|
+
pagination: { limit: 1, page: 1 },
|
|
685
|
+
search: args.search,
|
|
686
|
+
emailVerified: args.emailVerified,
|
|
687
|
+
hasPassword: args.hasPassword
|
|
688
|
+
},
|
|
689
|
+
createRequestContext(args)
|
|
690
|
+
);
|
|
691
|
+
return result.data[0] ?? null;
|
|
692
|
+
},
|
|
693
|
+
async findByID(args) {
|
|
694
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
695
|
+
try {
|
|
696
|
+
const user = await ctx.userService.findById(
|
|
697
|
+
args.id,
|
|
698
|
+
createRequestContext(args)
|
|
699
|
+
);
|
|
700
|
+
return user;
|
|
701
|
+
} catch (error) {
|
|
702
|
+
if (config.disableErrors && isNotFoundError(error)) {
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
throw error;
|
|
706
|
+
}
|
|
707
|
+
},
|
|
708
|
+
async create(args) {
|
|
709
|
+
const data = args.data ?? {};
|
|
710
|
+
const { name, image, roles, isActive, ...customFields } = data;
|
|
711
|
+
const user = await ctx.userService.create(
|
|
712
|
+
{
|
|
713
|
+
email: args.email,
|
|
714
|
+
name: name ?? "",
|
|
715
|
+
password: args.password,
|
|
716
|
+
image,
|
|
717
|
+
roles,
|
|
718
|
+
isActive,
|
|
719
|
+
...customFields
|
|
720
|
+
},
|
|
721
|
+
createRequestContext(args)
|
|
722
|
+
);
|
|
723
|
+
return {
|
|
724
|
+
message: "User created.",
|
|
725
|
+
item: user
|
|
726
|
+
};
|
|
727
|
+
},
|
|
728
|
+
async update(args) {
|
|
729
|
+
if (!args.id) {
|
|
730
|
+
throw new NextlyError({
|
|
731
|
+
code: "INVALID_INPUT",
|
|
732
|
+
publicMessage: "'id' is required for users.update()",
|
|
733
|
+
statusCode: 400
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
const data = args.data ?? {};
|
|
737
|
+
const { email, name, image, emailVerified, isActive, ...customFields } = data;
|
|
738
|
+
const user = await ctx.userService.update(
|
|
739
|
+
args.id,
|
|
740
|
+
{
|
|
741
|
+
email,
|
|
742
|
+
name,
|
|
743
|
+
image,
|
|
744
|
+
emailVerified,
|
|
745
|
+
isActive,
|
|
746
|
+
...customFields
|
|
747
|
+
},
|
|
748
|
+
createRequestContext(args)
|
|
749
|
+
);
|
|
750
|
+
return {
|
|
751
|
+
message: "User updated.",
|
|
752
|
+
item: user
|
|
753
|
+
};
|
|
754
|
+
},
|
|
755
|
+
async delete(args) {
|
|
756
|
+
if (!args.id) {
|
|
757
|
+
throw new NextlyError({
|
|
758
|
+
code: "INVALID_INPUT",
|
|
759
|
+
publicMessage: "'id' is required for users.delete()",
|
|
760
|
+
statusCode: 400
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
await ctx.userService.delete(args.id, createRequestContext(args));
|
|
764
|
+
return {
|
|
765
|
+
message: "User deleted.",
|
|
766
|
+
item: { id: args.id }
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// src/direct-api/namespaces/media.ts
|
|
773
|
+
function createMediaNamespace(ctx) {
|
|
774
|
+
const folders = {
|
|
775
|
+
async list(args = {}) {
|
|
776
|
+
if (args.parent) {
|
|
777
|
+
return await ctx.mediaService.listSubfolders(
|
|
778
|
+
args.parent,
|
|
779
|
+
createRequestContext(args)
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
return await ctx.mediaService.listRootFolders(createRequestContext(args));
|
|
783
|
+
},
|
|
784
|
+
async create(args) {
|
|
785
|
+
if (!args.name) {
|
|
786
|
+
throw new NextlyError({
|
|
787
|
+
code: "INVALID_INPUT",
|
|
788
|
+
publicMessage: "'name' is required for media.folders.create()",
|
|
789
|
+
statusCode: 400
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
return await ctx.mediaService.createFolder(
|
|
793
|
+
{
|
|
794
|
+
name: args.name,
|
|
795
|
+
description: args.description,
|
|
796
|
+
color: args.color,
|
|
797
|
+
icon: args.icon,
|
|
798
|
+
parentId: args.parent ?? null
|
|
799
|
+
},
|
|
800
|
+
createRequestContext(args)
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
return {
|
|
805
|
+
async upload(args) {
|
|
806
|
+
return await ctx.mediaService.upload(
|
|
807
|
+
{
|
|
808
|
+
buffer: args.file.data,
|
|
809
|
+
filename: args.file.name,
|
|
810
|
+
mimeType: args.file.mimetype,
|
|
811
|
+
size: args.file.size,
|
|
812
|
+
altText: args.altText,
|
|
813
|
+
folderId: args.folder ?? null
|
|
814
|
+
},
|
|
815
|
+
createRequestContext(args)
|
|
816
|
+
);
|
|
817
|
+
},
|
|
818
|
+
async find(args = {}) {
|
|
819
|
+
const limit = args.limit ?? 24;
|
|
820
|
+
const page = args.page ?? 1;
|
|
821
|
+
const result = await ctx.mediaService.listMedia(
|
|
822
|
+
{
|
|
823
|
+
page,
|
|
824
|
+
limit,
|
|
825
|
+
search: args.search,
|
|
826
|
+
type: args.mimeType,
|
|
827
|
+
folderId: args.folder,
|
|
828
|
+
sortBy: args.sortBy,
|
|
829
|
+
sortOrder: args.sortOrder
|
|
830
|
+
},
|
|
831
|
+
createRequestContext(args)
|
|
832
|
+
);
|
|
833
|
+
return toListResult(result, limit, page);
|
|
834
|
+
},
|
|
835
|
+
async findByID(args) {
|
|
836
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
837
|
+
try {
|
|
838
|
+
return await ctx.mediaService.findById(
|
|
839
|
+
args.id,
|
|
840
|
+
createRequestContext(args)
|
|
841
|
+
);
|
|
842
|
+
} catch (error) {
|
|
843
|
+
if (config.disableErrors && isNotFoundError(error)) {
|
|
844
|
+
return null;
|
|
845
|
+
}
|
|
846
|
+
throw error;
|
|
847
|
+
}
|
|
848
|
+
},
|
|
849
|
+
async update(args) {
|
|
850
|
+
if (!args.id) {
|
|
851
|
+
throw new NextlyError({
|
|
852
|
+
code: "INVALID_INPUT",
|
|
853
|
+
publicMessage: "'id' is required for media.update()",
|
|
854
|
+
statusCode: 400
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
const item = await ctx.mediaService.update(
|
|
858
|
+
args.id,
|
|
859
|
+
{
|
|
860
|
+
filename: args.data.filename,
|
|
861
|
+
altText: args.data.altText,
|
|
862
|
+
caption: args.data.caption,
|
|
863
|
+
tags: args.data.tags,
|
|
864
|
+
folderId: args.data.folderId
|
|
865
|
+
},
|
|
866
|
+
createRequestContext(args)
|
|
867
|
+
);
|
|
868
|
+
return {
|
|
869
|
+
message: "Media updated.",
|
|
870
|
+
item
|
|
871
|
+
};
|
|
872
|
+
},
|
|
873
|
+
async delete(args) {
|
|
874
|
+
if (!args.id) {
|
|
875
|
+
throw new NextlyError({
|
|
876
|
+
code: "INVALID_INPUT",
|
|
877
|
+
publicMessage: "'id' is required for media.delete()",
|
|
878
|
+
statusCode: 400
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
await ctx.mediaService.delete(args.id, createRequestContext(args));
|
|
882
|
+
return {
|
|
883
|
+
message: "Media deleted.",
|
|
884
|
+
item: { id: args.id }
|
|
885
|
+
};
|
|
886
|
+
},
|
|
887
|
+
async bulkDelete(args) {
|
|
888
|
+
if (!args.ids || args.ids.length === 0) {
|
|
889
|
+
return {
|
|
890
|
+
successes: [],
|
|
891
|
+
failures: [],
|
|
892
|
+
total: 0,
|
|
893
|
+
successCount: 0,
|
|
894
|
+
failedCount: 0
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
return ctx.mediaService.bulkDelete(args.ids, createRequestContext(args));
|
|
898
|
+
},
|
|
899
|
+
folders
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// src/direct-api/namespaces/forms.ts
|
|
904
|
+
function createFormsNamespace(ctx) {
|
|
905
|
+
const namespace = {
|
|
906
|
+
async find(args = {}) {
|
|
907
|
+
const limit = args.limit ?? 10;
|
|
908
|
+
const page = args.page ?? 1;
|
|
909
|
+
const where = {};
|
|
910
|
+
if (args.status) {
|
|
911
|
+
where.status = { equals: args.status };
|
|
912
|
+
}
|
|
913
|
+
const result = await ctx.collectionsHandler.listEntries({
|
|
914
|
+
collectionName: ctx.formsCollectionSlug,
|
|
915
|
+
page,
|
|
916
|
+
limit,
|
|
917
|
+
where: Object.keys(where).length > 0 ? where : void 0
|
|
918
|
+
});
|
|
919
|
+
if (!result.success) {
|
|
920
|
+
throw createErrorFromResult(result);
|
|
921
|
+
}
|
|
922
|
+
const legacy = result.data;
|
|
923
|
+
const total = legacy.totalDocs ?? legacy.docs.length;
|
|
924
|
+
const totalPages = legacy.totalPages ?? Math.max(1, Math.ceil(total / Math.max(limit, 1)));
|
|
925
|
+
return {
|
|
926
|
+
items: legacy.docs,
|
|
927
|
+
meta: {
|
|
928
|
+
total,
|
|
929
|
+
page,
|
|
930
|
+
limit,
|
|
931
|
+
totalPages,
|
|
932
|
+
hasNext: legacy.hasNextPage ?? page < totalPages,
|
|
933
|
+
hasPrev: legacy.hasPrevPage ?? page > 1
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
},
|
|
937
|
+
async findBySlug(args) {
|
|
938
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
939
|
+
if (!args.slug) {
|
|
940
|
+
throw new NextlyError({
|
|
941
|
+
code: "INVALID_INPUT",
|
|
942
|
+
publicMessage: "'slug' is required for forms.findBySlug()",
|
|
943
|
+
statusCode: 400
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
try {
|
|
947
|
+
const result = await ctx.collectionsHandler.listEntries({
|
|
948
|
+
collectionName: ctx.formsCollectionSlug,
|
|
949
|
+
where: { slug: { equals: args.slug } },
|
|
950
|
+
limit: 1
|
|
951
|
+
});
|
|
952
|
+
if (!result.success) {
|
|
953
|
+
if (config.disableErrors) {
|
|
954
|
+
return null;
|
|
955
|
+
}
|
|
956
|
+
throw createErrorFromResult(result);
|
|
957
|
+
}
|
|
958
|
+
const docs = result.data.docs;
|
|
959
|
+
if (!docs || docs.length === 0) {
|
|
960
|
+
if (config.disableErrors) {
|
|
961
|
+
return null;
|
|
962
|
+
}
|
|
963
|
+
throw NextlyError.notFound({
|
|
964
|
+
logContext: { slug: args.slug, entity: "form" }
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
return docs[0];
|
|
968
|
+
} catch (error) {
|
|
969
|
+
if (config.disableErrors && isNotFoundError(error)) {
|
|
970
|
+
return null;
|
|
971
|
+
}
|
|
972
|
+
throw error;
|
|
973
|
+
}
|
|
974
|
+
},
|
|
975
|
+
async submit(args) {
|
|
976
|
+
if (!args.form) {
|
|
977
|
+
throw new NextlyError({
|
|
978
|
+
code: "INVALID_INPUT",
|
|
979
|
+
publicMessage: "'form' (slug) is required for forms.submit()",
|
|
980
|
+
statusCode: 400
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
if (!args.data || typeof args.data !== "object") {
|
|
984
|
+
throw new NextlyError({
|
|
985
|
+
code: "INVALID_INPUT",
|
|
986
|
+
publicMessage: "'data' is required for forms.submit()",
|
|
987
|
+
statusCode: 400
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
const form = await namespace.findBySlug({
|
|
991
|
+
slug: args.form,
|
|
992
|
+
disableErrors: true
|
|
993
|
+
});
|
|
994
|
+
if (!form) {
|
|
995
|
+
return {
|
|
996
|
+
success: false,
|
|
997
|
+
error: "Form not found"
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
if (form.status !== "published") {
|
|
1001
|
+
return {
|
|
1002
|
+
success: false,
|
|
1003
|
+
error: "This form is not currently accepting submissions"
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
const submissionData = {
|
|
1007
|
+
form: form.id,
|
|
1008
|
+
data: args.data,
|
|
1009
|
+
status: "new",
|
|
1010
|
+
submittedAt: /* @__PURE__ */ new Date()
|
|
1011
|
+
};
|
|
1012
|
+
if (args.metadata?.ipAddress) {
|
|
1013
|
+
submissionData.ipAddress = args.metadata.ipAddress;
|
|
1014
|
+
}
|
|
1015
|
+
if (args.metadata?.userAgent) {
|
|
1016
|
+
submissionData.userAgent = args.metadata.userAgent;
|
|
1017
|
+
}
|
|
1018
|
+
const createResult = await ctx.collectionsHandler.createEntry(
|
|
1019
|
+
{ collectionName: ctx.submissionsCollectionSlug },
|
|
1020
|
+
submissionData
|
|
1021
|
+
);
|
|
1022
|
+
if (!createResult.success) {
|
|
1023
|
+
return {
|
|
1024
|
+
success: false,
|
|
1025
|
+
error: createResult.message || "Failed to create submission"
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
const settings = form.settings;
|
|
1029
|
+
const redirect = settings?.confirmationType === "redirect" ? settings.redirectUrl : void 0;
|
|
1030
|
+
return {
|
|
1031
|
+
success: true,
|
|
1032
|
+
submission: createResult.data,
|
|
1033
|
+
redirect
|
|
1034
|
+
};
|
|
1035
|
+
},
|
|
1036
|
+
async submissions(args) {
|
|
1037
|
+
if (!args.form) {
|
|
1038
|
+
throw new NextlyError({
|
|
1039
|
+
code: "INVALID_INPUT",
|
|
1040
|
+
publicMessage: "'form' (slug or ID) is required for forms.submissions()",
|
|
1041
|
+
statusCode: 400
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
const limit = args.limit ?? 10;
|
|
1045
|
+
const page = args.page ?? 1;
|
|
1046
|
+
let formId = args.form;
|
|
1047
|
+
const isSlug = !looksLikeId(args.form);
|
|
1048
|
+
if (isSlug) {
|
|
1049
|
+
const form = await namespace.findBySlug({
|
|
1050
|
+
slug: args.form,
|
|
1051
|
+
disableErrors: true
|
|
1052
|
+
});
|
|
1053
|
+
if (!form) {
|
|
1054
|
+
throw NextlyError.notFound({
|
|
1055
|
+
logContext: { slug: args.form, entity: "form" }
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
formId = form.id;
|
|
1059
|
+
}
|
|
1060
|
+
const result = await ctx.collectionsHandler.listEntries({
|
|
1061
|
+
collectionName: ctx.submissionsCollectionSlug,
|
|
1062
|
+
page,
|
|
1063
|
+
limit,
|
|
1064
|
+
where: { form: { equals: formId } }
|
|
1065
|
+
});
|
|
1066
|
+
if (!result.success) {
|
|
1067
|
+
throw createErrorFromResult(result);
|
|
1068
|
+
}
|
|
1069
|
+
const legacy = result.data;
|
|
1070
|
+
const total = legacy.totalDocs ?? legacy.docs.length;
|
|
1071
|
+
const totalPages = legacy.totalPages ?? Math.max(1, Math.ceil(total / Math.max(limit, 1)));
|
|
1072
|
+
return {
|
|
1073
|
+
items: legacy.docs,
|
|
1074
|
+
meta: {
|
|
1075
|
+
total,
|
|
1076
|
+
page,
|
|
1077
|
+
limit,
|
|
1078
|
+
totalPages,
|
|
1079
|
+
hasNext: legacy.hasNextPage ?? page < totalPages,
|
|
1080
|
+
hasPrev: legacy.hasPrevPage ?? page > 1
|
|
1081
|
+
}
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
return namespace;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
// src/direct-api/namespaces/components.ts
|
|
1089
|
+
function createComponentsNamespace(ctx) {
|
|
1090
|
+
return {
|
|
1091
|
+
async find(args = {}) {
|
|
1092
|
+
const result = await ctx.componentRegistryService.listComponents({
|
|
1093
|
+
source: args.source,
|
|
1094
|
+
migrationStatus: args.migrationStatus,
|
|
1095
|
+
locked: args.locked,
|
|
1096
|
+
search: args.search,
|
|
1097
|
+
limit: args.limit,
|
|
1098
|
+
offset: args.offset
|
|
1099
|
+
});
|
|
1100
|
+
const limit = args.limit ?? result.data.length;
|
|
1101
|
+
const offset = args.offset ?? 0;
|
|
1102
|
+
const total = result.total;
|
|
1103
|
+
const effectiveLimit = limit > 0 ? limit : Math.max(total, 1);
|
|
1104
|
+
const totalPages = Math.max(1, Math.ceil(total / effectiveLimit));
|
|
1105
|
+
const page = Math.floor(offset / effectiveLimit) + 1;
|
|
1106
|
+
return {
|
|
1107
|
+
items: result.data.map(mapComponentRecord),
|
|
1108
|
+
meta: {
|
|
1109
|
+
total,
|
|
1110
|
+
page,
|
|
1111
|
+
limit,
|
|
1112
|
+
totalPages,
|
|
1113
|
+
hasNext: page < totalPages,
|
|
1114
|
+
hasPrev: page > 1
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
},
|
|
1118
|
+
async findBySlug(args) {
|
|
1119
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
1120
|
+
if (!args.slug) {
|
|
1121
|
+
throw new NextlyError({
|
|
1122
|
+
code: "INVALID_INPUT",
|
|
1123
|
+
publicMessage: "'slug' is required for components.findBySlug()",
|
|
1124
|
+
statusCode: 400
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
try {
|
|
1128
|
+
const component = await ctx.componentRegistryService.getComponentBySlug(
|
|
1129
|
+
args.slug
|
|
1130
|
+
);
|
|
1131
|
+
if (!component) {
|
|
1132
|
+
if (config.disableErrors) {
|
|
1133
|
+
return null;
|
|
1134
|
+
}
|
|
1135
|
+
throw NextlyError.notFound({
|
|
1136
|
+
logContext: { slug: args.slug, entity: "component" }
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
return mapComponentRecord(component);
|
|
1140
|
+
} catch (error) {
|
|
1141
|
+
if (error instanceof NextlyError) {
|
|
1142
|
+
throw error;
|
|
1143
|
+
}
|
|
1144
|
+
if (config.disableErrors && isNotFoundError(error)) {
|
|
1145
|
+
return null;
|
|
1146
|
+
}
|
|
1147
|
+
throw error;
|
|
1148
|
+
}
|
|
1149
|
+
},
|
|
1150
|
+
async create(args) {
|
|
1151
|
+
if (!args.slug) {
|
|
1152
|
+
throw new NextlyError({
|
|
1153
|
+
code: "INVALID_INPUT",
|
|
1154
|
+
publicMessage: "'slug' is required for components.create()",
|
|
1155
|
+
statusCode: 400
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
if (!args.label) {
|
|
1159
|
+
throw new NextlyError({
|
|
1160
|
+
code: "INVALID_INPUT",
|
|
1161
|
+
publicMessage: "'label' is required for components.create()",
|
|
1162
|
+
statusCode: 400
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
if (!args.fields || !Array.isArray(args.fields)) {
|
|
1166
|
+
throw new NextlyError({
|
|
1167
|
+
code: "INVALID_INPUT",
|
|
1168
|
+
publicMessage: "'fields' array is required for components.create()",
|
|
1169
|
+
statusCode: 400
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
const { calculateSchemaHash } = await import("./schema-hash-FMMG6VPJ.mjs");
|
|
1173
|
+
const fieldsTyped = args.fields;
|
|
1174
|
+
const schemaHash = calculateSchemaHash(fieldsTyped);
|
|
1175
|
+
const component = await ctx.componentRegistryService.registerComponent({
|
|
1176
|
+
slug: args.slug,
|
|
1177
|
+
label: args.label,
|
|
1178
|
+
tableName: args.tableName ?? `comp_${args.slug}`,
|
|
1179
|
+
description: args.description,
|
|
1180
|
+
fields: fieldsTyped,
|
|
1181
|
+
admin: args.admin,
|
|
1182
|
+
source: "ui",
|
|
1183
|
+
locked: false,
|
|
1184
|
+
schemaHash,
|
|
1185
|
+
schemaVersion: 1,
|
|
1186
|
+
migrationStatus: "pending"
|
|
1187
|
+
});
|
|
1188
|
+
return {
|
|
1189
|
+
message: "Component created.",
|
|
1190
|
+
item: mapComponentRecord(component)
|
|
1191
|
+
};
|
|
1192
|
+
},
|
|
1193
|
+
async update(args) {
|
|
1194
|
+
if (!args.slug) {
|
|
1195
|
+
throw new NextlyError({
|
|
1196
|
+
code: "INVALID_INPUT",
|
|
1197
|
+
publicMessage: "'slug' is required for components.update()",
|
|
1198
|
+
statusCode: 400
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
if (!args.data || typeof args.data !== "object") {
|
|
1202
|
+
throw new NextlyError({
|
|
1203
|
+
code: "INVALID_INPUT",
|
|
1204
|
+
publicMessage: "'data' object is required for components.update()",
|
|
1205
|
+
statusCode: 400
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
const updateData = {};
|
|
1209
|
+
if (args.data.label !== void 0) {
|
|
1210
|
+
updateData.label = args.data.label;
|
|
1211
|
+
}
|
|
1212
|
+
if (args.data.description !== void 0) {
|
|
1213
|
+
updateData.description = args.data.description;
|
|
1214
|
+
}
|
|
1215
|
+
if (args.data.fields !== void 0) {
|
|
1216
|
+
const fieldsTyped = args.data.fields;
|
|
1217
|
+
updateData.fields = fieldsTyped;
|
|
1218
|
+
const { calculateSchemaHash } = await import("./schema-hash-FMMG6VPJ.mjs");
|
|
1219
|
+
updateData.schemaHash = calculateSchemaHash(fieldsTyped);
|
|
1220
|
+
}
|
|
1221
|
+
if (args.data.admin !== void 0) {
|
|
1222
|
+
updateData.admin = args.data.admin;
|
|
1223
|
+
}
|
|
1224
|
+
const component = await ctx.componentRegistryService.updateComponent(
|
|
1225
|
+
args.slug,
|
|
1226
|
+
updateData,
|
|
1227
|
+
{ source: "ui" }
|
|
1228
|
+
);
|
|
1229
|
+
return {
|
|
1230
|
+
message: "Component updated.",
|
|
1231
|
+
item: mapComponentRecord(component)
|
|
1232
|
+
};
|
|
1233
|
+
},
|
|
1234
|
+
async delete(args) {
|
|
1235
|
+
if (!args.slug) {
|
|
1236
|
+
throw new NextlyError({
|
|
1237
|
+
code: "INVALID_INPUT",
|
|
1238
|
+
publicMessage: "'slug' is required for components.delete()",
|
|
1239
|
+
statusCode: 400
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
await ctx.componentRegistryService.deleteComponent(args.slug);
|
|
1243
|
+
return {
|
|
1244
|
+
message: "Component deleted.",
|
|
1245
|
+
item: { slug: args.slug }
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
// src/direct-api/namespaces/email.ts
|
|
1252
|
+
function createEmailNamespace(ctx) {
|
|
1253
|
+
return {
|
|
1254
|
+
async send(args) {
|
|
1255
|
+
const to = Array.isArray(args.to) ? args.to[0] : args.to;
|
|
1256
|
+
const result = await ctx.emailSendService.send({
|
|
1257
|
+
to,
|
|
1258
|
+
subject: args.subject,
|
|
1259
|
+
html: args.html,
|
|
1260
|
+
plainText: args.text,
|
|
1261
|
+
providerId: args.providerId,
|
|
1262
|
+
attachments: args.attachments
|
|
1263
|
+
});
|
|
1264
|
+
return {
|
|
1265
|
+
success: result.success,
|
|
1266
|
+
messageId: result.messageId
|
|
1267
|
+
};
|
|
1268
|
+
},
|
|
1269
|
+
async sendWithTemplate(args) {
|
|
1270
|
+
const to = Array.isArray(args.to) ? args.to[0] : args.to;
|
|
1271
|
+
const sendOptions = {};
|
|
1272
|
+
if (args.providerId) sendOptions.providerId = args.providerId;
|
|
1273
|
+
if (args.attachments) sendOptions.attachments = args.attachments;
|
|
1274
|
+
const result = await ctx.emailSendService.sendWithTemplate(
|
|
1275
|
+
args.template,
|
|
1276
|
+
to,
|
|
1277
|
+
args.variables ?? {},
|
|
1278
|
+
Object.keys(sendOptions).length > 0 ? sendOptions : void 0
|
|
1279
|
+
);
|
|
1280
|
+
return {
|
|
1281
|
+
success: result.success,
|
|
1282
|
+
messageId: result.messageId
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
function createEmailProvidersNamespace(ctx) {
|
|
1288
|
+
return {
|
|
1289
|
+
async find(args = {}) {
|
|
1290
|
+
const providers = await ctx.emailProviderService.listProviders();
|
|
1291
|
+
return sliceListResult(providers, args.limit, args.page);
|
|
1292
|
+
},
|
|
1293
|
+
async findByID(args) {
|
|
1294
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
1295
|
+
try {
|
|
1296
|
+
return await ctx.emailProviderService.getProvider(args.id);
|
|
1297
|
+
} catch (error) {
|
|
1298
|
+
if (config.disableErrors && isNotFoundError(error)) {
|
|
1299
|
+
return null;
|
|
1300
|
+
}
|
|
1301
|
+
throw error;
|
|
1302
|
+
}
|
|
1303
|
+
},
|
|
1304
|
+
async create(args) {
|
|
1305
|
+
const item = await ctx.emailProviderService.createProvider({
|
|
1306
|
+
name: args.data.name,
|
|
1307
|
+
type: args.data.type,
|
|
1308
|
+
fromEmail: args.data.fromEmail,
|
|
1309
|
+
fromName: args.data.fromName,
|
|
1310
|
+
configuration: args.data.configuration,
|
|
1311
|
+
isDefault: args.data.isDefault
|
|
1312
|
+
});
|
|
1313
|
+
return { message: "Email provider created.", item };
|
|
1314
|
+
},
|
|
1315
|
+
async update(args) {
|
|
1316
|
+
const item = await ctx.emailProviderService.updateProvider(
|
|
1317
|
+
args.id,
|
|
1318
|
+
args.data
|
|
1319
|
+
);
|
|
1320
|
+
return { message: "Email provider updated.", item };
|
|
1321
|
+
},
|
|
1322
|
+
async delete(args) {
|
|
1323
|
+
await ctx.emailProviderService.deleteProvider(args.id);
|
|
1324
|
+
return {
|
|
1325
|
+
message: "Email provider deleted.",
|
|
1326
|
+
item: { id: args.id }
|
|
1327
|
+
};
|
|
1328
|
+
},
|
|
1329
|
+
async setDefault(args) {
|
|
1330
|
+
return await ctx.emailProviderService.setDefault(args.id);
|
|
1331
|
+
},
|
|
1332
|
+
async test(args) {
|
|
1333
|
+
return await ctx.emailProviderService.testProvider(args.id, args.to);
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
function createEmailTemplatesNamespace(ctx) {
|
|
1338
|
+
return {
|
|
1339
|
+
async find(args = {}) {
|
|
1340
|
+
const templates = await ctx.emailTemplateService.listTemplates();
|
|
1341
|
+
return sliceListResult(templates, args.limit, args.page);
|
|
1342
|
+
},
|
|
1343
|
+
async findByID(args) {
|
|
1344
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
1345
|
+
try {
|
|
1346
|
+
return await ctx.emailTemplateService.getTemplate(args.id);
|
|
1347
|
+
} catch (error) {
|
|
1348
|
+
if (config.disableErrors && isNotFoundError(error)) {
|
|
1349
|
+
return null;
|
|
1350
|
+
}
|
|
1351
|
+
throw error;
|
|
1352
|
+
}
|
|
1353
|
+
},
|
|
1354
|
+
async findBySlug(args) {
|
|
1355
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
1356
|
+
try {
|
|
1357
|
+
const template = await ctx.emailTemplateService.getTemplateBySlug(
|
|
1358
|
+
args.slug
|
|
1359
|
+
);
|
|
1360
|
+
if (!template) {
|
|
1361
|
+
if (config.disableErrors) {
|
|
1362
|
+
return null;
|
|
1363
|
+
}
|
|
1364
|
+
throw NextlyError.notFound({
|
|
1365
|
+
logContext: { slug: args.slug, entity: "emailTemplate" }
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
return template;
|
|
1369
|
+
} catch (error) {
|
|
1370
|
+
if (error instanceof NextlyError) {
|
|
1371
|
+
throw error;
|
|
1372
|
+
}
|
|
1373
|
+
if (config.disableErrors && isNotFoundError(error)) {
|
|
1374
|
+
return null;
|
|
1375
|
+
}
|
|
1376
|
+
throw error;
|
|
1377
|
+
}
|
|
1378
|
+
},
|
|
1379
|
+
async create(args) {
|
|
1380
|
+
const item = await ctx.emailTemplateService.createTemplate({
|
|
1381
|
+
name: args.data.name,
|
|
1382
|
+
slug: args.data.slug,
|
|
1383
|
+
subject: args.data.subject,
|
|
1384
|
+
htmlContent: args.data.htmlContent,
|
|
1385
|
+
plainTextContent: args.data.textContent,
|
|
1386
|
+
variables: args.data.variables,
|
|
1387
|
+
attachments: args.data.attachments
|
|
1388
|
+
});
|
|
1389
|
+
return { message: "Email template created.", item };
|
|
1390
|
+
},
|
|
1391
|
+
async update(args) {
|
|
1392
|
+
const { textContent, variables, attachments, ...rest } = args.data;
|
|
1393
|
+
const updateData = {
|
|
1394
|
+
...rest
|
|
1395
|
+
};
|
|
1396
|
+
if (textContent !== void 0) {
|
|
1397
|
+
updateData.plainTextContent = textContent;
|
|
1398
|
+
}
|
|
1399
|
+
if (variables !== void 0) {
|
|
1400
|
+
updateData.variables = variables;
|
|
1401
|
+
}
|
|
1402
|
+
if (attachments !== void 0) {
|
|
1403
|
+
updateData.attachments = attachments;
|
|
1404
|
+
}
|
|
1405
|
+
const item = await ctx.emailTemplateService.updateTemplate(
|
|
1406
|
+
args.id,
|
|
1407
|
+
updateData
|
|
1408
|
+
);
|
|
1409
|
+
return { message: "Email template updated.", item };
|
|
1410
|
+
},
|
|
1411
|
+
async delete(args) {
|
|
1412
|
+
await ctx.emailTemplateService.deleteTemplate(args.id);
|
|
1413
|
+
return {
|
|
1414
|
+
message: "Email template deleted.",
|
|
1415
|
+
item: { id: args.id }
|
|
1416
|
+
};
|
|
1417
|
+
},
|
|
1418
|
+
async preview(args) {
|
|
1419
|
+
return await ctx.emailTemplateService.previewTemplate(
|
|
1420
|
+
args.id,
|
|
1421
|
+
args.data ?? {}
|
|
1422
|
+
);
|
|
1423
|
+
},
|
|
1424
|
+
async getLayout(_args = {}) {
|
|
1425
|
+
return await ctx.emailTemplateService.getLayout();
|
|
1426
|
+
},
|
|
1427
|
+
async updateLayout(args) {
|
|
1428
|
+
await ctx.emailTemplateService.updateLayout(args.data);
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
function createUserFieldsNamespace(ctx) {
|
|
1433
|
+
return {
|
|
1434
|
+
async find(args = {}) {
|
|
1435
|
+
const allFields = args.includeInactive ? await ctx.userFieldDefinitionService.listFields() : await ctx.userFieldDefinitionService.getMergedFields();
|
|
1436
|
+
return sliceListResult(allFields, args.limit, args.page);
|
|
1437
|
+
},
|
|
1438
|
+
async findByID(args) {
|
|
1439
|
+
const config = mergeConfig(ctx.defaultConfig, args);
|
|
1440
|
+
try {
|
|
1441
|
+
return await ctx.userFieldDefinitionService.getField(args.id);
|
|
1442
|
+
} catch (error) {
|
|
1443
|
+
if (config.disableErrors && isNotFoundError(error)) {
|
|
1444
|
+
return null;
|
|
1445
|
+
}
|
|
1446
|
+
throw error;
|
|
1447
|
+
}
|
|
1448
|
+
},
|
|
1449
|
+
async create(args) {
|
|
1450
|
+
const item = await ctx.userFieldDefinitionService.createField({
|
|
1451
|
+
name: args.data.name,
|
|
1452
|
+
label: args.data.label,
|
|
1453
|
+
type: args.data.type,
|
|
1454
|
+
required: args.data.required,
|
|
1455
|
+
defaultValue: args.data.defaultValue,
|
|
1456
|
+
options: args.data.options,
|
|
1457
|
+
placeholder: args.data.placeholder,
|
|
1458
|
+
description: args.data.description,
|
|
1459
|
+
sortOrder: args.data.sortOrder,
|
|
1460
|
+
source: "ui"
|
|
1461
|
+
});
|
|
1462
|
+
return { message: "User field created.", item };
|
|
1463
|
+
},
|
|
1464
|
+
async update(args) {
|
|
1465
|
+
const item = await ctx.userFieldDefinitionService.updateField(
|
|
1466
|
+
args.id,
|
|
1467
|
+
args.data
|
|
1468
|
+
);
|
|
1469
|
+
return { message: "User field updated.", item };
|
|
1470
|
+
},
|
|
1471
|
+
async delete(args) {
|
|
1472
|
+
await ctx.userFieldDefinitionService.deleteField(args.id);
|
|
1473
|
+
return {
|
|
1474
|
+
message: "User field deleted.",
|
|
1475
|
+
item: { id: args.id }
|
|
1476
|
+
};
|
|
1477
|
+
},
|
|
1478
|
+
async reorder(args) {
|
|
1479
|
+
return await ctx.userFieldDefinitionService.reorderFields(
|
|
1480
|
+
args.orderedIds
|
|
1481
|
+
);
|
|
1482
|
+
}
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
// src/direct-api/namespaces/rbac.ts
|
|
1487
|
+
import { eq } from "drizzle-orm";
|
|
1488
|
+
function createRolesNamespace(ctx) {
|
|
1489
|
+
return {
|
|
1490
|
+
async find(args = {}) {
|
|
1491
|
+
const result = await ctx.rbacRoleService.listRoles({
|
|
1492
|
+
search: args.search,
|
|
1493
|
+
page: args.page ?? 1,
|
|
1494
|
+
limit: args.limit ?? 10
|
|
1495
|
+
});
|
|
1496
|
+
const data = result.data;
|
|
1497
|
+
const items = data.map((r) => mapRole(r));
|
|
1498
|
+
const total = result.meta?.total ?? items.length;
|
|
1499
|
+
const limit = args.limit ?? 10;
|
|
1500
|
+
const page = args.page ?? 1;
|
|
1501
|
+
const totalPages = Math.max(1, Math.ceil(total / limit));
|
|
1502
|
+
return {
|
|
1503
|
+
items,
|
|
1504
|
+
meta: {
|
|
1505
|
+
total,
|
|
1506
|
+
page,
|
|
1507
|
+
limit,
|
|
1508
|
+
totalPages,
|
|
1509
|
+
hasNext: page < totalPages,
|
|
1510
|
+
hasPrev: page > 1
|
|
1511
|
+
}
|
|
1512
|
+
};
|
|
1513
|
+
},
|
|
1514
|
+
async findByID(args) {
|
|
1515
|
+
const role = await ctx.rbacRoleService.getRoleById(args.id);
|
|
1516
|
+
return mapRole(role);
|
|
1517
|
+
},
|
|
1518
|
+
async create(args) {
|
|
1519
|
+
const role = await ctx.rbacRoleService.createRole({
|
|
1520
|
+
name: args.data.name,
|
|
1521
|
+
slug: args.data.slug,
|
|
1522
|
+
description: args.data.description,
|
|
1523
|
+
level: args.data.level ?? 0,
|
|
1524
|
+
permissionIds: args.data.permissionIds ?? [],
|
|
1525
|
+
childRoleIds: args.data.childRoleIds ?? []
|
|
1526
|
+
});
|
|
1527
|
+
return {
|
|
1528
|
+
message: "Role created.",
|
|
1529
|
+
item: mapRole(role)
|
|
1530
|
+
};
|
|
1531
|
+
},
|
|
1532
|
+
async update(args) {
|
|
1533
|
+
await ctx.rbacRoleService.updateRole(args.id, {
|
|
1534
|
+
name: args.data.name,
|
|
1535
|
+
slug: args.data.slug,
|
|
1536
|
+
description: args.data.description ?? void 0,
|
|
1537
|
+
level: args.data.level
|
|
1538
|
+
});
|
|
1539
|
+
const updated = await ctx.rbacRoleService.getRoleById(args.id);
|
|
1540
|
+
return {
|
|
1541
|
+
message: "Role updated.",
|
|
1542
|
+
item: mapRole(updated)
|
|
1543
|
+
};
|
|
1544
|
+
},
|
|
1545
|
+
async delete(args) {
|
|
1546
|
+
await ctx.rbacRoleService.deleteRole(args.id);
|
|
1547
|
+
return {
|
|
1548
|
+
message: "Role deleted.",
|
|
1549
|
+
item: { id: args.id }
|
|
1550
|
+
};
|
|
1551
|
+
},
|
|
1552
|
+
async getPermissions(args) {
|
|
1553
|
+
const rolePerms = await ctx.rbacRolePermissionService.listRolePermissions(
|
|
1554
|
+
args.id
|
|
1555
|
+
);
|
|
1556
|
+
const fullPerms = await Promise.all(
|
|
1557
|
+
rolePerms.map(async (rp) => {
|
|
1558
|
+
try {
|
|
1559
|
+
const perm = await ctx.rbacPermissionService.getPermissionById(
|
|
1560
|
+
rp.id
|
|
1561
|
+
);
|
|
1562
|
+
return mapPermission(perm);
|
|
1563
|
+
} catch {
|
|
1564
|
+
return null;
|
|
1565
|
+
}
|
|
1566
|
+
})
|
|
1567
|
+
);
|
|
1568
|
+
return fullPerms.filter((p) => p !== null);
|
|
1569
|
+
},
|
|
1570
|
+
async setPermissions(args) {
|
|
1571
|
+
const updatedPerms = await ctx.rbacRolePermissionService.setRolePermissions(
|
|
1572
|
+
args.roleId,
|
|
1573
|
+
args.permissionIds
|
|
1574
|
+
);
|
|
1575
|
+
const fullPerms = await Promise.all(
|
|
1576
|
+
updatedPerms.map(async (rp) => {
|
|
1577
|
+
try {
|
|
1578
|
+
const perm = await ctx.rbacPermissionService.getPermissionById(
|
|
1579
|
+
rp.id
|
|
1580
|
+
);
|
|
1581
|
+
return mapPermission(perm);
|
|
1582
|
+
} catch {
|
|
1583
|
+
return null;
|
|
1584
|
+
}
|
|
1585
|
+
})
|
|
1586
|
+
);
|
|
1587
|
+
return fullPerms.filter((p) => p !== null);
|
|
1588
|
+
}
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
function createPermissionsNamespace(ctx) {
|
|
1592
|
+
return {
|
|
1593
|
+
async find(args = {}) {
|
|
1594
|
+
const limit = args.limit ?? 10;
|
|
1595
|
+
const page = args.page ?? 1;
|
|
1596
|
+
const result = await ctx.rbacPermissionService.listPermissions({
|
|
1597
|
+
page,
|
|
1598
|
+
limit,
|
|
1599
|
+
search: args.search,
|
|
1600
|
+
resource: args.resource,
|
|
1601
|
+
action: args.action
|
|
1602
|
+
});
|
|
1603
|
+
const total = result.meta?.total ?? result.data.length;
|
|
1604
|
+
const totalPages = Math.max(1, Math.ceil(total / limit));
|
|
1605
|
+
const items = result.data.map((p) => mapPermission(p));
|
|
1606
|
+
return {
|
|
1607
|
+
items,
|
|
1608
|
+
meta: {
|
|
1609
|
+
total,
|
|
1610
|
+
page,
|
|
1611
|
+
limit,
|
|
1612
|
+
totalPages,
|
|
1613
|
+
hasNext: page < totalPages,
|
|
1614
|
+
hasPrev: page > 1
|
|
1615
|
+
}
|
|
1616
|
+
};
|
|
1617
|
+
},
|
|
1618
|
+
async findByID(args) {
|
|
1619
|
+
try {
|
|
1620
|
+
const perm = await ctx.rbacPermissionService.getPermissionById(args.id);
|
|
1621
|
+
return mapPermission(perm);
|
|
1622
|
+
} catch (error) {
|
|
1623
|
+
if (args.disableErrors && NextlyError.isNotFound(error)) {
|
|
1624
|
+
return null;
|
|
1625
|
+
}
|
|
1626
|
+
throw error;
|
|
1627
|
+
}
|
|
1628
|
+
},
|
|
1629
|
+
async create(args) {
|
|
1630
|
+
const { name, slug, action, resource, description } = args.data;
|
|
1631
|
+
const ensured = await ctx.rbacPermissionService.ensurePermission(
|
|
1632
|
+
action,
|
|
1633
|
+
resource,
|
|
1634
|
+
name,
|
|
1635
|
+
slug,
|
|
1636
|
+
description
|
|
1637
|
+
);
|
|
1638
|
+
const fetched = await ctx.rbacPermissionService.getPermissionById(
|
|
1639
|
+
ensured.id
|
|
1640
|
+
);
|
|
1641
|
+
return {
|
|
1642
|
+
message: "Permission created.",
|
|
1643
|
+
item: mapPermission(fetched)
|
|
1644
|
+
};
|
|
1645
|
+
},
|
|
1646
|
+
async delete(args) {
|
|
1647
|
+
await ctx.rbacPermissionService.deletePermissionById(args.id);
|
|
1648
|
+
return {
|
|
1649
|
+
message: "Permission deleted.",
|
|
1650
|
+
item: { id: args.id }
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1655
|
+
function createAccessNamespace(ctx) {
|
|
1656
|
+
return {
|
|
1657
|
+
async check(args) {
|
|
1658
|
+
return await ctx.rbacAccessControlService.checkAccess({
|
|
1659
|
+
userId: args.userId,
|
|
1660
|
+
operation: args.operation,
|
|
1661
|
+
resource: args.resource
|
|
1662
|
+
});
|
|
1663
|
+
},
|
|
1664
|
+
async checkApiKey(args) {
|
|
1665
|
+
try {
|
|
1666
|
+
const auth = await ctx.apiKeyService.authenticateApiKey(args.rawKey);
|
|
1667
|
+
if (!auth) return { valid: false };
|
|
1668
|
+
const [permissions, roles, meta] = await Promise.all([
|
|
1669
|
+
ctx.apiKeyService.resolveApiKeyPermissions(
|
|
1670
|
+
auth.tokenType,
|
|
1671
|
+
auth.roleId,
|
|
1672
|
+
auth.userId,
|
|
1673
|
+
auth.id
|
|
1674
|
+
),
|
|
1675
|
+
ctx.apiKeyService.resolveApiKeyRoles(
|
|
1676
|
+
auth.tokenType,
|
|
1677
|
+
auth.roleId,
|
|
1678
|
+
auth.userId
|
|
1679
|
+
),
|
|
1680
|
+
ctx.apiKeyService.getApiKeyById(auth.id, auth.userId, {
|
|
1681
|
+
allUsers: true
|
|
1682
|
+
})
|
|
1683
|
+
]);
|
|
1684
|
+
return {
|
|
1685
|
+
valid: true,
|
|
1686
|
+
userId: auth.userId,
|
|
1687
|
+
tokenType: auth.tokenType,
|
|
1688
|
+
permissions,
|
|
1689
|
+
roles,
|
|
1690
|
+
expiresAt: meta?.expiresAt ?? null
|
|
1691
|
+
};
|
|
1692
|
+
} catch {
|
|
1693
|
+
return { valid: false };
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
async function resolveApiKeyOwner(apiKeyService, id) {
|
|
1699
|
+
const adapter = container.get("adapter");
|
|
1700
|
+
const privateTable = apiKeyService.apiKeysTable;
|
|
1701
|
+
const db = adapter.getDrizzle();
|
|
1702
|
+
const rows = await db.select({ userId: privateTable.userId }).from(privateTable).where(eq(privateTable.id, id)).limit(1);
|
|
1703
|
+
if (rows.length === 0) {
|
|
1704
|
+
throw NextlyError.notFound({ logContext: { entity: "apiKey", apiKeyId: id } });
|
|
1705
|
+
}
|
|
1706
|
+
return rows[0].userId;
|
|
1707
|
+
}
|
|
1708
|
+
function createApiKeysNamespace(ctx) {
|
|
1709
|
+
return {
|
|
1710
|
+
async list(args = {}) {
|
|
1711
|
+
const allUsers = !args.userId;
|
|
1712
|
+
return await ctx.apiKeyService.listApiKeys(args.userId ?? "", {
|
|
1713
|
+
allUsers
|
|
1714
|
+
});
|
|
1715
|
+
},
|
|
1716
|
+
async findByID(args) {
|
|
1717
|
+
return await ctx.apiKeyService.getApiKeyById(args.id, "", {
|
|
1718
|
+
allUsers: true
|
|
1719
|
+
});
|
|
1720
|
+
},
|
|
1721
|
+
async create(args) {
|
|
1722
|
+
const { meta, key } = await ctx.apiKeyService.createApiKey(args.userId, {
|
|
1723
|
+
name: args.name,
|
|
1724
|
+
description: args.description ?? void 0,
|
|
1725
|
+
tokenType: args.tokenType,
|
|
1726
|
+
roleId: args.roleId ?? void 0,
|
|
1727
|
+
expiresIn: args.expiresIn
|
|
1728
|
+
});
|
|
1729
|
+
return { doc: meta, key };
|
|
1730
|
+
},
|
|
1731
|
+
async update(args) {
|
|
1732
|
+
const userId = await resolveApiKeyOwner(ctx.apiKeyService, args.id);
|
|
1733
|
+
return await ctx.apiKeyService.updateApiKey(args.id, userId, {
|
|
1734
|
+
name: args.name,
|
|
1735
|
+
description: args.description ?? void 0
|
|
1736
|
+
});
|
|
1737
|
+
},
|
|
1738
|
+
async revoke(args) {
|
|
1739
|
+
const userId = await resolveApiKeyOwner(ctx.apiKeyService, args.id);
|
|
1740
|
+
await ctx.apiKeyService.revokeApiKey(args.id, userId);
|
|
1741
|
+
return { success: true };
|
|
1742
|
+
}
|
|
1743
|
+
};
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
// src/direct-api/nextly.ts
|
|
1747
|
+
var Nextly = class {
|
|
1748
|
+
/**
|
|
1749
|
+
* Default configuration applied to all operations.
|
|
1750
|
+
*
|
|
1751
|
+
* @internal
|
|
1752
|
+
*/
|
|
1753
|
+
defaultConfig;
|
|
1754
|
+
users;
|
|
1755
|
+
media;
|
|
1756
|
+
forms;
|
|
1757
|
+
components;
|
|
1758
|
+
email;
|
|
1759
|
+
emailProviders;
|
|
1760
|
+
emailTemplates;
|
|
1761
|
+
userFields;
|
|
1762
|
+
roles;
|
|
1763
|
+
permissions;
|
|
1764
|
+
access;
|
|
1765
|
+
apiKeys;
|
|
1766
|
+
/**
|
|
1767
|
+
* Create a new Nextly instance.
|
|
1768
|
+
*
|
|
1769
|
+
* @param config - Default configuration for all operations
|
|
1770
|
+
*/
|
|
1771
|
+
constructor(config = {}) {
|
|
1772
|
+
this.defaultConfig = {
|
|
1773
|
+
overrideAccess: true,
|
|
1774
|
+
...config
|
|
1775
|
+
};
|
|
1776
|
+
this.users = createUsersNamespace(this);
|
|
1777
|
+
this.media = createMediaNamespace(this);
|
|
1778
|
+
this.forms = createFormsNamespace(this);
|
|
1779
|
+
this.components = createComponentsNamespace(this);
|
|
1780
|
+
this.email = createEmailNamespace(this);
|
|
1781
|
+
this.emailProviders = createEmailProvidersNamespace(this);
|
|
1782
|
+
this.emailTemplates = createEmailTemplatesNamespace(this);
|
|
1783
|
+
this.userFields = createUserFieldsNamespace(this);
|
|
1784
|
+
this.roles = createRolesNamespace(this);
|
|
1785
|
+
this.permissions = createPermissionsNamespace(this);
|
|
1786
|
+
this.access = createAccessNamespace(this);
|
|
1787
|
+
this.apiKeys = createApiKeysNamespace(this);
|
|
1788
|
+
}
|
|
1789
|
+
/**
|
|
1790
|
+
* Get the forms collection slug.
|
|
1791
|
+
* Defaults to "forms" (matching the form builder plugin default).
|
|
1792
|
+
*
|
|
1793
|
+
* @internal
|
|
1794
|
+
*/
|
|
1795
|
+
get formsCollectionSlug() {
|
|
1796
|
+
return this.defaultConfig.forms?.collectionSlug ?? "forms";
|
|
1797
|
+
}
|
|
1798
|
+
/**
|
|
1799
|
+
* Get the form submissions collection slug.
|
|
1800
|
+
* Defaults to "form-submissions" (matching the form builder plugin default).
|
|
1801
|
+
*
|
|
1802
|
+
* @internal
|
|
1803
|
+
*/
|
|
1804
|
+
get submissionsCollectionSlug() {
|
|
1805
|
+
return this.defaultConfig.forms?.submissionCollectionSlug ?? "form-submissions";
|
|
1806
|
+
}
|
|
1807
|
+
/** @internal */
|
|
1808
|
+
get collectionsHandler() {
|
|
1809
|
+
return container.get("collectionsHandler");
|
|
1810
|
+
}
|
|
1811
|
+
/** @internal */
|
|
1812
|
+
get singleEntryService() {
|
|
1813
|
+
return container.get("singleEntryService");
|
|
1814
|
+
}
|
|
1815
|
+
/** @internal */
|
|
1816
|
+
get singleRegistryService() {
|
|
1817
|
+
return container.get("singleRegistryService");
|
|
1818
|
+
}
|
|
1819
|
+
/** Cached AuthService — not registered in the DI container. */
|
|
1820
|
+
_authService = null;
|
|
1821
|
+
/** @internal */
|
|
1822
|
+
get authService() {
|
|
1823
|
+
if (!this._authService) {
|
|
1824
|
+
const adapter = container.get("adapter");
|
|
1825
|
+
const logger = container.has("logger") ? container.get("logger") : console;
|
|
1826
|
+
const emailService = container.has("emailService") ? container.get("emailService") : void 0;
|
|
1827
|
+
this._authService = new AuthService(adapter, logger, emailService);
|
|
1828
|
+
}
|
|
1829
|
+
return this._authService;
|
|
1830
|
+
}
|
|
1831
|
+
/** Cached UserAccountService — not registered in the DI container. */
|
|
1832
|
+
_userAccountService = null;
|
|
1833
|
+
/** @internal */
|
|
1834
|
+
get userAccountService() {
|
|
1835
|
+
if (!this._userAccountService) {
|
|
1836
|
+
const adapter = container.get("adapter");
|
|
1837
|
+
const logger = container.has("logger") ? container.get("logger") : console;
|
|
1838
|
+
this._userAccountService = new UserAccountService(adapter, logger);
|
|
1839
|
+
}
|
|
1840
|
+
return this._userAccountService;
|
|
1841
|
+
}
|
|
1842
|
+
/** @internal */
|
|
1843
|
+
get userService() {
|
|
1844
|
+
return container.get("userService");
|
|
1845
|
+
}
|
|
1846
|
+
/** @internal */
|
|
1847
|
+
get mediaService() {
|
|
1848
|
+
return container.get("mediaService");
|
|
1849
|
+
}
|
|
1850
|
+
/** @internal */
|
|
1851
|
+
get componentRegistryService() {
|
|
1852
|
+
return container.get("componentRegistryService");
|
|
1853
|
+
}
|
|
1854
|
+
/** @internal */
|
|
1855
|
+
get emailProviderService() {
|
|
1856
|
+
return container.get("emailProviderService");
|
|
1857
|
+
}
|
|
1858
|
+
/** @internal */
|
|
1859
|
+
get emailTemplateService() {
|
|
1860
|
+
return container.get("emailTemplateService");
|
|
1861
|
+
}
|
|
1862
|
+
/** @internal */
|
|
1863
|
+
get userFieldDefinitionService() {
|
|
1864
|
+
return container.get(
|
|
1865
|
+
"userFieldDefinitionService"
|
|
1866
|
+
);
|
|
1867
|
+
}
|
|
1868
|
+
/** @internal */
|
|
1869
|
+
get emailSendService() {
|
|
1870
|
+
return container.get("emailService");
|
|
1871
|
+
}
|
|
1872
|
+
/** Cached RoleService — not registered in the DI container. */
|
|
1873
|
+
_rbacRoleService = null;
|
|
1874
|
+
/** @internal */
|
|
1875
|
+
get rbacRoleService() {
|
|
1876
|
+
if (!this._rbacRoleService) {
|
|
1877
|
+
const adapter = container.get("adapter");
|
|
1878
|
+
const logger = container.has("logger") ? container.get("logger") : console;
|
|
1879
|
+
this._rbacRoleService = new RoleService(adapter, logger);
|
|
1880
|
+
}
|
|
1881
|
+
return this._rbacRoleService;
|
|
1882
|
+
}
|
|
1883
|
+
/** Cached PermissionService — not registered in the DI container. */
|
|
1884
|
+
_rbacPermissionService = null;
|
|
1885
|
+
/** @internal */
|
|
1886
|
+
get rbacPermissionService() {
|
|
1887
|
+
if (!this._rbacPermissionService) {
|
|
1888
|
+
const adapter = container.get("adapter");
|
|
1889
|
+
const logger = container.has("logger") ? container.get("logger") : console;
|
|
1890
|
+
this._rbacPermissionService = new PermissionService(adapter, logger);
|
|
1891
|
+
}
|
|
1892
|
+
return this._rbacPermissionService;
|
|
1893
|
+
}
|
|
1894
|
+
/** Cached RolePermissionService — not registered in the DI container. */
|
|
1895
|
+
_rbacRolePermissionService = null;
|
|
1896
|
+
/** @internal */
|
|
1897
|
+
get rbacRolePermissionService() {
|
|
1898
|
+
if (!this._rbacRolePermissionService) {
|
|
1899
|
+
const adapter = container.get("adapter");
|
|
1900
|
+
const logger = container.has("logger") ? container.get("logger") : console;
|
|
1901
|
+
this._rbacRolePermissionService = new RolePermissionService(
|
|
1902
|
+
adapter,
|
|
1903
|
+
logger
|
|
1904
|
+
);
|
|
1905
|
+
}
|
|
1906
|
+
return this._rbacRolePermissionService;
|
|
1907
|
+
}
|
|
1908
|
+
/** @internal */
|
|
1909
|
+
get rbacAccessControlService() {
|
|
1910
|
+
return container.get("rbacAccessControlService");
|
|
1911
|
+
}
|
|
1912
|
+
/** @internal */
|
|
1913
|
+
get apiKeyService() {
|
|
1914
|
+
return container.get("apiKeyService");
|
|
1915
|
+
}
|
|
1916
|
+
/**
|
|
1917
|
+
* Find multiple documents in a collection.
|
|
1918
|
+
*
|
|
1919
|
+
* (`{ items, meta }`). Callers migrating from `{ docs, totalDocs, ... }`:
|
|
1920
|
+
* `result.docs` -> `result.items`, `result.totalDocs` -> `result.meta.total`.
|
|
1921
|
+
*
|
|
1922
|
+
* @throws {NextlyError} If the operation fails
|
|
1923
|
+
*/
|
|
1924
|
+
find(args) {
|
|
1925
|
+
return find(this, args);
|
|
1926
|
+
}
|
|
1927
|
+
/**
|
|
1928
|
+
* Find a single document by ID. Returns `null` when not found and
|
|
1929
|
+
* `disableErrors` is `true`; otherwise throws.
|
|
1930
|
+
*/
|
|
1931
|
+
findByID(args) {
|
|
1932
|
+
return findByID(this, args);
|
|
1933
|
+
}
|
|
1934
|
+
/**
|
|
1935
|
+
* Create a new document in a collection.
|
|
1936
|
+
*
|
|
1937
|
+
* created doc must read `result.item` (was a bare `T`).
|
|
1938
|
+
*/
|
|
1939
|
+
create(args) {
|
|
1940
|
+
return create(this, args);
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* Update a document by ID or by `where` clause (returns the first match).
|
|
1944
|
+
*
|
|
1945
|
+
* updated doc must read `result.item` (was a bare `T`).
|
|
1946
|
+
*/
|
|
1947
|
+
update(args) {
|
|
1948
|
+
return update(this, args);
|
|
1949
|
+
}
|
|
1950
|
+
/**
|
|
1951
|
+
* Delete a document by ID or by `where` clause.
|
|
1952
|
+
*
|
|
1953
|
+
* where `item` carries the deleted `id`. The bulk-by-where path still
|
|
1954
|
+
* returns the legacy `DeleteResult` shape (`{ deleted, ids }`) because
|
|
1955
|
+
* a multi-row delete cannot collapse to a single mutation envelope.
|
|
1956
|
+
*/
|
|
1957
|
+
delete(args) {
|
|
1958
|
+
return deleteEntry(this, args);
|
|
1959
|
+
}
|
|
1960
|
+
/**
|
|
1961
|
+
* Count documents matching a query.
|
|
1962
|
+
*
|
|
1963
|
+
*/
|
|
1964
|
+
count(args) {
|
|
1965
|
+
return count(this, args);
|
|
1966
|
+
}
|
|
1967
|
+
/** Bulk-delete multiple documents by IDs (partial success pattern). */
|
|
1968
|
+
bulkDelete(args) {
|
|
1969
|
+
return bulkDelete(this, args);
|
|
1970
|
+
}
|
|
1971
|
+
/**
|
|
1972
|
+
* Duplicate a document (optionally applying field overrides).
|
|
1973
|
+
*
|
|
1974
|
+
* duplicated doc must read `result.item` (was a bare `T`).
|
|
1975
|
+
*/
|
|
1976
|
+
duplicate(args) {
|
|
1977
|
+
return duplicate(this, args);
|
|
1978
|
+
}
|
|
1979
|
+
/** Get a Single (global) document by slug. */
|
|
1980
|
+
findSingle(args) {
|
|
1981
|
+
return findSingle(this, args);
|
|
1982
|
+
}
|
|
1983
|
+
/** Update a Single (global) document by slug. */
|
|
1984
|
+
updateSingle(args) {
|
|
1985
|
+
return updateSingle(this, args);
|
|
1986
|
+
}
|
|
1987
|
+
/** Fetch the content of every registered Single. */
|
|
1988
|
+
findSingles(args = {}) {
|
|
1989
|
+
return findSingles(this, args);
|
|
1990
|
+
}
|
|
1991
|
+
/** Verify credentials and return a signed session token. */
|
|
1992
|
+
login(args) {
|
|
1993
|
+
return login(this, args);
|
|
1994
|
+
}
|
|
1995
|
+
/** Logout — no-op for the Direct API (session lives in the app). */
|
|
1996
|
+
logout() {
|
|
1997
|
+
return logout();
|
|
1998
|
+
}
|
|
1999
|
+
/** Fetch the current user's profile (requires explicit `user.id`). */
|
|
2000
|
+
me(args) {
|
|
2001
|
+
return me(this, args);
|
|
2002
|
+
}
|
|
2003
|
+
/** Update the current user's profile (name/image only). */
|
|
2004
|
+
updateMe(args) {
|
|
2005
|
+
return updateMe(this, args);
|
|
2006
|
+
}
|
|
2007
|
+
/** Register a new user with email + password. */
|
|
2008
|
+
register(args) {
|
|
2009
|
+
return register(this, args);
|
|
2010
|
+
}
|
|
2011
|
+
/** Change the current user's password (requires the current password). */
|
|
2012
|
+
changePassword(args) {
|
|
2013
|
+
return changePassword(this, args);
|
|
2014
|
+
}
|
|
2015
|
+
/** Initiate password reset (always returns success to avoid leaking emails). */
|
|
2016
|
+
forgotPassword(args) {
|
|
2017
|
+
return forgotPassword(this, args);
|
|
2018
|
+
}
|
|
2019
|
+
/** Reset a user's password using a token from `forgotPassword`. */
|
|
2020
|
+
resetPassword(args) {
|
|
2021
|
+
return resetPassword(this, args);
|
|
2022
|
+
}
|
|
2023
|
+
/** Verify a user's email using a verification token. */
|
|
2024
|
+
verifyEmail(args) {
|
|
2025
|
+
return verifyEmail(this, args);
|
|
2026
|
+
}
|
|
2027
|
+
};
|
|
2028
|
+
var globalForDirectApi = globalThis;
|
|
2029
|
+
function getNextly(config) {
|
|
2030
|
+
if (!isServicesRegistered()) {
|
|
2031
|
+
throw new NextlyError({
|
|
2032
|
+
code: "INTERNAL_ERROR",
|
|
2033
|
+
publicMessage: "Nextly services not initialized. Call registerServices() before using the Direct API.",
|
|
2034
|
+
statusCode: 500
|
|
2035
|
+
});
|
|
2036
|
+
}
|
|
2037
|
+
if (!globalForDirectApi.__nextly_directApiInstance) {
|
|
2038
|
+
globalForDirectApi.__nextly_directApiInstance = new Nextly(config);
|
|
2039
|
+
if (!container.has("nextlyDirectAPI")) {
|
|
2040
|
+
container.register(
|
|
2041
|
+
"nextlyDirectAPI",
|
|
2042
|
+
() => globalForDirectApi.__nextly_directApiInstance
|
|
2043
|
+
);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
return globalForDirectApi.__nextly_directApiInstance;
|
|
2047
|
+
}
|
|
2048
|
+
var nextly = {
|
|
2049
|
+
find: (args) => getNextly().find(args),
|
|
2050
|
+
findByID: (args) => getNextly().findByID(args),
|
|
2051
|
+
create: (args) => getNextly().create(args),
|
|
2052
|
+
update: (args) => getNextly().update(args),
|
|
2053
|
+
delete: (args) => getNextly().delete(args),
|
|
2054
|
+
count: (args) => getNextly().count(args),
|
|
2055
|
+
bulkDelete: (args) => getNextly().bulkDelete(args),
|
|
2056
|
+
duplicate: (args) => getNextly().duplicate(args),
|
|
2057
|
+
findSingle: (args) => getNextly().findSingle(args),
|
|
2058
|
+
updateSingle: (args) => getNextly().updateSingle(args),
|
|
2059
|
+
findSingles: (args) => getNextly().findSingles(args ?? {}),
|
|
2060
|
+
login: (args) => getNextly().login(args),
|
|
2061
|
+
logout: () => getNextly().logout(),
|
|
2062
|
+
me: (args) => getNextly().me(args),
|
|
2063
|
+
updateMe: (args) => getNextly().updateMe(args),
|
|
2064
|
+
register: (args) => getNextly().register(args),
|
|
2065
|
+
changePassword: (args) => getNextly().changePassword(args),
|
|
2066
|
+
forgotPassword: (args) => getNextly().forgotPassword(args),
|
|
2067
|
+
resetPassword: (args) => getNextly().resetPassword(args),
|
|
2068
|
+
verifyEmail: (args) => getNextly().verifyEmail(args),
|
|
2069
|
+
users: {
|
|
2070
|
+
find: (args) => getNextly().users.find(args),
|
|
2071
|
+
findOne: (args) => getNextly().users.findOne(args),
|
|
2072
|
+
findByID: (args) => getNextly().users.findByID(args),
|
|
2073
|
+
create: (args) => getNextly().users.create(args),
|
|
2074
|
+
update: (args) => getNextly().users.update(args),
|
|
2075
|
+
delete: (args) => getNextly().users.delete(args)
|
|
2076
|
+
},
|
|
2077
|
+
media: {
|
|
2078
|
+
upload: (args) => getNextly().media.upload(args),
|
|
2079
|
+
find: (args) => getNextly().media.find(args),
|
|
2080
|
+
findByID: (args) => getNextly().media.findByID(args),
|
|
2081
|
+
update: (args) => getNextly().media.update(args),
|
|
2082
|
+
delete: (args) => getNextly().media.delete(args),
|
|
2083
|
+
bulkDelete: (args) => getNextly().media.bulkDelete(args),
|
|
2084
|
+
folders: {
|
|
2085
|
+
list: (args) => getNextly().media.folders.list(args),
|
|
2086
|
+
create: (args) => getNextly().media.folders.create(args)
|
|
2087
|
+
}
|
|
2088
|
+
},
|
|
2089
|
+
forms: {
|
|
2090
|
+
find: (args) => getNextly().forms.find(args),
|
|
2091
|
+
findBySlug: (args) => getNextly().forms.findBySlug(args),
|
|
2092
|
+
submit: (args) => getNextly().forms.submit(args),
|
|
2093
|
+
submissions: (args) => getNextly().forms.submissions(args)
|
|
2094
|
+
},
|
|
2095
|
+
components: {
|
|
2096
|
+
find: (args) => getNextly().components.find(args),
|
|
2097
|
+
findBySlug: (args) => getNextly().components.findBySlug(args),
|
|
2098
|
+
create: (args) => getNextly().components.create(args),
|
|
2099
|
+
update: (args) => getNextly().components.update(args),
|
|
2100
|
+
delete: (args) => getNextly().components.delete(args)
|
|
2101
|
+
},
|
|
2102
|
+
email: {
|
|
2103
|
+
send: (args) => getNextly().email.send(args),
|
|
2104
|
+
sendWithTemplate: (args) => getNextly().email.sendWithTemplate(args)
|
|
2105
|
+
},
|
|
2106
|
+
emailProviders: {
|
|
2107
|
+
find: (args) => getNextly().emailProviders.find(args),
|
|
2108
|
+
findByID: (args) => getNextly().emailProviders.findByID(args),
|
|
2109
|
+
create: (args) => getNextly().emailProviders.create(args),
|
|
2110
|
+
update: (args) => getNextly().emailProviders.update(args),
|
|
2111
|
+
delete: (args) => getNextly().emailProviders.delete(args),
|
|
2112
|
+
setDefault: (args) => getNextly().emailProviders.setDefault(args),
|
|
2113
|
+
test: (args) => getNextly().emailProviders.test(args)
|
|
2114
|
+
},
|
|
2115
|
+
emailTemplates: {
|
|
2116
|
+
find: (args) => getNextly().emailTemplates.find(args),
|
|
2117
|
+
findByID: (args) => getNextly().emailTemplates.findByID(args),
|
|
2118
|
+
findBySlug: (args) => getNextly().emailTemplates.findBySlug(args),
|
|
2119
|
+
create: (args) => getNextly().emailTemplates.create(args),
|
|
2120
|
+
update: (args) => getNextly().emailTemplates.update(args),
|
|
2121
|
+
delete: (args) => getNextly().emailTemplates.delete(args),
|
|
2122
|
+
preview: (args) => getNextly().emailTemplates.preview(args),
|
|
2123
|
+
getLayout: (args) => getNextly().emailTemplates.getLayout(args),
|
|
2124
|
+
updateLayout: (args) => getNextly().emailTemplates.updateLayout(args)
|
|
2125
|
+
},
|
|
2126
|
+
userFields: {
|
|
2127
|
+
find: (args) => getNextly().userFields.find(args),
|
|
2128
|
+
findByID: (args) => getNextly().userFields.findByID(args),
|
|
2129
|
+
create: (args) => getNextly().userFields.create(args),
|
|
2130
|
+
update: (args) => getNextly().userFields.update(args),
|
|
2131
|
+
delete: (args) => getNextly().userFields.delete(args),
|
|
2132
|
+
reorder: (args) => getNextly().userFields.reorder(args)
|
|
2133
|
+
},
|
|
2134
|
+
roles: {
|
|
2135
|
+
find: (args) => getNextly().roles.find(args),
|
|
2136
|
+
findByID: (args) => getNextly().roles.findByID(args),
|
|
2137
|
+
create: (args) => getNextly().roles.create(args),
|
|
2138
|
+
update: (args) => getNextly().roles.update(args),
|
|
2139
|
+
delete: (args) => getNextly().roles.delete(args),
|
|
2140
|
+
getPermissions: (args) => getNextly().roles.getPermissions(args),
|
|
2141
|
+
setPermissions: (args) => getNextly().roles.setPermissions(args)
|
|
2142
|
+
},
|
|
2143
|
+
permissions: {
|
|
2144
|
+
find: (args) => getNextly().permissions.find(args),
|
|
2145
|
+
findByID: (args) => getNextly().permissions.findByID(args),
|
|
2146
|
+
create: (args) => getNextly().permissions.create(args),
|
|
2147
|
+
delete: (args) => getNextly().permissions.delete(args)
|
|
2148
|
+
},
|
|
2149
|
+
apiKeys: {
|
|
2150
|
+
list: (args) => getNextly().apiKeys.list(args),
|
|
2151
|
+
findByID: (args) => getNextly().apiKeys.findByID(args),
|
|
2152
|
+
create: (args) => getNextly().apiKeys.create(args),
|
|
2153
|
+
update: (args) => getNextly().apiKeys.update(args),
|
|
2154
|
+
revoke: (args) => getNextly().apiKeys.revoke(args)
|
|
2155
|
+
},
|
|
2156
|
+
access: {
|
|
2157
|
+
check: (args) => getNextly().access.check(args),
|
|
2158
|
+
checkApiKey: (args) => getNextly().access.checkApiKey(args)
|
|
2159
|
+
}
|
|
2160
|
+
};
|
|
2161
|
+
|
|
2162
|
+
// src/init/build-service-config.ts
|
|
2163
|
+
function buildServiceConfig(providedConfig) {
|
|
2164
|
+
const serviceConfig = {};
|
|
2165
|
+
if (providedConfig) {
|
|
2166
|
+
const { config: nextlyConfig, ...rest } = providedConfig;
|
|
2167
|
+
Object.assign(serviceConfig, rest);
|
|
2168
|
+
if (!serviceConfig.storagePlugins && nextlyConfig?.storage) {
|
|
2169
|
+
serviceConfig.storagePlugins = nextlyConfig.storage;
|
|
2170
|
+
if (nextlyConfig.storage.length > 0) {
|
|
2171
|
+
console.log(
|
|
2172
|
+
`[Nextly] Using ${nextlyConfig.storage.length} storage plugin(s) from config`
|
|
2173
|
+
);
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
if (!serviceConfig.collections && nextlyConfig?.collections) {
|
|
2177
|
+
serviceConfig.collections = nextlyConfig.collections;
|
|
2178
|
+
if (nextlyConfig.collections.length > 0) {
|
|
2179
|
+
console.log(
|
|
2180
|
+
`[Nextly] Using ${nextlyConfig.collections.length} collection(s) from config`
|
|
2181
|
+
);
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
if (!serviceConfig.singles && nextlyConfig?.singles) {
|
|
2185
|
+
serviceConfig.singles = nextlyConfig.singles;
|
|
2186
|
+
if (nextlyConfig.singles.length > 0) {
|
|
2187
|
+
console.log(
|
|
2188
|
+
`[Nextly] Using ${nextlyConfig.singles.length} single(s) from config`
|
|
2189
|
+
);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
if (!serviceConfig.components && nextlyConfig?.components) {
|
|
2193
|
+
serviceConfig.components = nextlyConfig.components;
|
|
2194
|
+
if (nextlyConfig.components.length > 0) {
|
|
2195
|
+
console.log(
|
|
2196
|
+
`[Nextly] Using ${nextlyConfig.components.length} component(s) from config`
|
|
2197
|
+
);
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
if (!serviceConfig.plugins && nextlyConfig?.plugins) {
|
|
2201
|
+
serviceConfig.plugins = nextlyConfig.plugins;
|
|
2202
|
+
if (nextlyConfig.plugins.length > 0) {
|
|
2203
|
+
console.log(
|
|
2204
|
+
`[Nextly] Using ${nextlyConfig.plugins.length} plugin(s) from config`
|
|
2205
|
+
);
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
if (!serviceConfig.users && nextlyConfig?.users) {
|
|
2209
|
+
serviceConfig.users = nextlyConfig.users;
|
|
2210
|
+
}
|
|
2211
|
+
if (!serviceConfig.email && nextlyConfig?.email) {
|
|
2212
|
+
serviceConfig.email = nextlyConfig.email;
|
|
2213
|
+
}
|
|
2214
|
+
if (!serviceConfig.apiKeys && nextlyConfig?.apiKeys) {
|
|
2215
|
+
serviceConfig.apiKeys = nextlyConfig.apiKeys;
|
|
2216
|
+
}
|
|
2217
|
+
if (!serviceConfig.security && nextlyConfig?.security) {
|
|
2218
|
+
serviceConfig.security = nextlyConfig.security;
|
|
2219
|
+
}
|
|
2220
|
+
if (!serviceConfig.admin && nextlyConfig?.admin) {
|
|
2221
|
+
serviceConfig.admin = nextlyConfig.admin;
|
|
2222
|
+
}
|
|
2223
|
+
if (!serviceConfig.auth && nextlyConfig?.auth) {
|
|
2224
|
+
serviceConfig.auth = nextlyConfig.auth;
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
if (!serviceConfig.imageProcessor) {
|
|
2228
|
+
serviceConfig.imageProcessor = getImageProcessor();
|
|
2229
|
+
}
|
|
2230
|
+
return serviceConfig;
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
// src/init/drift-check.ts
|
|
2234
|
+
async function runDriftCheck(args) {
|
|
2235
|
+
const { adapter, collections, logger } = args;
|
|
2236
|
+
const deps = await resolveDeps(args.deps);
|
|
2237
|
+
if (collections.length === 0) return { kind: "clean" };
|
|
2238
|
+
const previewPromises = collections.map(async (collection) => {
|
|
2239
|
+
try {
|
|
2240
|
+
const preview = await deps.previewDesiredSchema({
|
|
2241
|
+
desired: {
|
|
2242
|
+
collections: {
|
|
2243
|
+
[collection.slug]: collection
|
|
2244
|
+
},
|
|
2245
|
+
singles: {},
|
|
2246
|
+
components: {}
|
|
2247
|
+
},
|
|
2248
|
+
db: adapter.getDrizzle(),
|
|
2249
|
+
dialect: adapter.dialect
|
|
2250
|
+
});
|
|
2251
|
+
return { ok: true, opCount: preview.operations.length };
|
|
2252
|
+
} catch (err) {
|
|
2253
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2254
|
+
logger.debug?.(
|
|
2255
|
+
`[nextly] Drift preview failed for '${collection.slug}': ${msg} (continuing).`
|
|
2256
|
+
);
|
|
2257
|
+
return { ok: false, error: msg };
|
|
2258
|
+
}
|
|
2259
|
+
});
|
|
2260
|
+
const results = await Promise.all(previewPromises);
|
|
2261
|
+
let pendingOps = 0;
|
|
2262
|
+
let failureCount = 0;
|
|
2263
|
+
for (const r of results) {
|
|
2264
|
+
if (r.ok) pendingOps += r.opCount;
|
|
2265
|
+
else failureCount += 1;
|
|
2266
|
+
}
|
|
2267
|
+
if (failureCount > 0) {
|
|
2268
|
+
logger.warn(
|
|
2269
|
+
`[nextly] ${failureCount} of ${collections.length} drift previews failed. Run \`nextly db:sync\` to investigate.`
|
|
2270
|
+
);
|
|
2271
|
+
}
|
|
2272
|
+
if (pendingOps === 0) return { kind: "clean" };
|
|
2273
|
+
logger.warn(
|
|
2274
|
+
`[nextly] Detected schema drift: ${pendingOps} pending change(s). Save your nextly.config.ts to apply via HMR, or run \`nextly db:sync\`.`
|
|
2275
|
+
);
|
|
2276
|
+
return { kind: "drift", pending: pendingOps };
|
|
2277
|
+
}
|
|
2278
|
+
async function resolveDeps(injected) {
|
|
2279
|
+
if (injected?.previewDesiredSchema) {
|
|
2280
|
+
return injected;
|
|
2281
|
+
}
|
|
2282
|
+
const { previewDesiredSchema } = await import("./preview-ZZTR3QGS.mjs");
|
|
2283
|
+
return {
|
|
2284
|
+
previewDesiredSchema: injected?.previewDesiredSchema ?? previewDesiredSchema
|
|
2285
|
+
};
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
// src/init/post-init-tasks.ts
|
|
2289
|
+
async function runPostInitTasks() {
|
|
2290
|
+
try {
|
|
2291
|
+
const emailTemplateService = getService("emailTemplateService");
|
|
2292
|
+
await emailTemplateService.ensureBuiltInTemplates();
|
|
2293
|
+
} catch {
|
|
2294
|
+
}
|
|
2295
|
+
try {
|
|
2296
|
+
const fieldDefService = getService("userFieldDefinitionService");
|
|
2297
|
+
const config = getService("config");
|
|
2298
|
+
const codeFields = config.users?.fields || [];
|
|
2299
|
+
await fieldDefService.syncCodeFields(
|
|
2300
|
+
codeFields
|
|
2301
|
+
);
|
|
2302
|
+
} catch {
|
|
2303
|
+
}
|
|
2304
|
+
try {
|
|
2305
|
+
const userExtSchemaService = getService("userExtSchemaService");
|
|
2306
|
+
await userExtSchemaService.loadMergedFields();
|
|
2307
|
+
if (userExtSchemaService.hasMergedFields()) {
|
|
2308
|
+
const adapter = getService("adapter");
|
|
2309
|
+
const drizzleDb = adapter.getDrizzle();
|
|
2310
|
+
await userExtSchemaService.ensureUserExtSchema(drizzleDb);
|
|
2311
|
+
}
|
|
2312
|
+
} catch {
|
|
2313
|
+
}
|
|
2314
|
+
try {
|
|
2315
|
+
const permissionSeedService = getService("permissionSeedService");
|
|
2316
|
+
const systemResult = await permissionSeedService.seedSystemPermissions();
|
|
2317
|
+
const collectionResult = await permissionSeedService.seedAllCollectionPermissions();
|
|
2318
|
+
const singleResult = await permissionSeedService.seedAllSinglePermissions();
|
|
2319
|
+
const allNewIds = [
|
|
2320
|
+
...systemResult.newPermissionIds,
|
|
2321
|
+
...collectionResult.newPermissionIds,
|
|
2322
|
+
...singleResult.newPermissionIds
|
|
2323
|
+
];
|
|
2324
|
+
if (allNewIds.length > 0) {
|
|
2325
|
+
await permissionSeedService.assignNewPermissionsToSuperAdmin(allNewIds);
|
|
2326
|
+
}
|
|
2327
|
+
} catch {
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
// src/runtime/hmr-listener.ts
|
|
2332
|
+
import { WebSocket } from "ws";
|
|
2333
|
+
var g = globalThis;
|
|
2334
|
+
function ensureHmrListener() {
|
|
2335
|
+
if (g.__nextly_hmrWs) return;
|
|
2336
|
+
if (process.env.NODE_ENV === "production") return;
|
|
2337
|
+
if (process.env.NODE_ENV === "test") return;
|
|
2338
|
+
if (process.env.NEXTLY_DISABLE_HMR === "1") return;
|
|
2339
|
+
try {
|
|
2340
|
+
const port = process.env.PORT ?? "3000";
|
|
2341
|
+
const hasHttps = process.env.USE_HTTPS === "true" || process.argv.includes("--experimental-https");
|
|
2342
|
+
const protocol = hasHttps ? "wss" : "ws";
|
|
2343
|
+
const prefix = process.env.__NEXT_ASSET_PREFIX ?? "";
|
|
2344
|
+
const url = process.env.NEXTLY_HMR_URL_OVERRIDE ?? `${protocol}://localhost:${port}${prefix}/_next/webpack-hmr`;
|
|
2345
|
+
g.__nextly_hmrWs = new WebSocket(url);
|
|
2346
|
+
g.__nextly_hmrWs.onmessage = (event) => {
|
|
2347
|
+
if (g.__nextly_hmrReload instanceof Promise) return;
|
|
2348
|
+
if (typeof event.data !== "string") return;
|
|
2349
|
+
let data;
|
|
2350
|
+
try {
|
|
2351
|
+
data = JSON.parse(event.data);
|
|
2352
|
+
} catch {
|
|
2353
|
+
return;
|
|
2354
|
+
}
|
|
2355
|
+
if (typeof data !== "object" || data === null) return;
|
|
2356
|
+
const record = data;
|
|
2357
|
+
const isServerChange = record.type === "serverComponentChanges" || record.action === "serverComponentChanges";
|
|
2358
|
+
if (isServerChange) {
|
|
2359
|
+
if (!(g.__nextly_hmrReload instanceof Promise)) {
|
|
2360
|
+
const reload = (async () => {
|
|
2361
|
+
try {
|
|
2362
|
+
const { reloadNextlyConfig: reloadNextlyConfig2 } = await import("./reload-config-HWQ4G5MM.mjs");
|
|
2363
|
+
await reloadNextlyConfig2();
|
|
2364
|
+
} catch {
|
|
2365
|
+
}
|
|
2366
|
+
})();
|
|
2367
|
+
markHmrReloadInFlight(reload);
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
};
|
|
2371
|
+
g.__nextly_hmrWs.onerror = () => {
|
|
2372
|
+
};
|
|
2373
|
+
} catch {
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
function consumeHmrReloadFlag() {
|
|
2377
|
+
if (g.__nextly_hmrReload === true) {
|
|
2378
|
+
g.__nextly_hmrReload = false;
|
|
2379
|
+
return true;
|
|
2380
|
+
}
|
|
2381
|
+
return false;
|
|
2382
|
+
}
|
|
2383
|
+
function markHmrReloadInFlight(promise) {
|
|
2384
|
+
g.__nextly_hmrReload = promise;
|
|
2385
|
+
void promise.finally(() => {
|
|
2386
|
+
if (g.__nextly_hmrReload === promise) {
|
|
2387
|
+
g.__nextly_hmrReload = false;
|
|
2388
|
+
}
|
|
2389
|
+
});
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
// src/init.ts
|
|
2393
|
+
var globalForInit = globalThis;
|
|
2394
|
+
async function getNextly2(options) {
|
|
2395
|
+
if (!options?.config) {
|
|
2396
|
+
throw new NextlyError({
|
|
2397
|
+
code: "CONFIGURATION_ERROR",
|
|
2398
|
+
statusCode: 500,
|
|
2399
|
+
publicMessage: "Server configuration error.",
|
|
2400
|
+
logMessage: "getNextly() requires a `config` parameter. Import your config and pass it: `getNextly({ config })`. If you are inside a Nextly internal handler that just needs the cached singleton, use `getCachedNextly()` instead."
|
|
2401
|
+
});
|
|
2402
|
+
}
|
|
2403
|
+
if (globalForInit.__nextly_cachedInstance) {
|
|
2404
|
+
if (consumeHmrReloadFlag()) {
|
|
2405
|
+
const reloadPromise = reloadNextlyConfig();
|
|
2406
|
+
markHmrReloadInFlight(reloadPromise);
|
|
2407
|
+
await reloadPromise;
|
|
2408
|
+
}
|
|
2409
|
+
if (options?.config?.storage && options.config.storage.length > 0) {
|
|
2410
|
+
const mediaStorage = getService("mediaStorage");
|
|
2411
|
+
if (!mediaStorage.hasAdapter()) {
|
|
2412
|
+
for (const plugin of options.config.storage) {
|
|
2413
|
+
mediaStorage.registerPlugin(plugin);
|
|
2414
|
+
}
|
|
2415
|
+
console.log(
|
|
2416
|
+
`[Nextly] Late-registered ${options.config.storage.length} storage plugin(s)`
|
|
2417
|
+
);
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
return globalForInit.__nextly_cachedInstance;
|
|
2421
|
+
}
|
|
2422
|
+
if (globalForInit.__nextly_initPromise) {
|
|
2423
|
+
return globalForInit.__nextly_initPromise;
|
|
2424
|
+
}
|
|
2425
|
+
const initPromise = (async () => {
|
|
2426
|
+
try {
|
|
2427
|
+
if (!isServicesRegistered()) {
|
|
2428
|
+
const finalConfig = buildServiceConfig(options);
|
|
2429
|
+
await registerServices(finalConfig);
|
|
2430
|
+
const adapter = getService("adapter");
|
|
2431
|
+
const capabilities = adapter.getCapabilities();
|
|
2432
|
+
console.log(`Nextly initialized with ${capabilities.dialect} database`);
|
|
2433
|
+
console.log(` - JSONB support: ${capabilities.supportsJsonb}`);
|
|
2434
|
+
console.log(` - RETURNING support: ${capabilities.supportsReturning}`);
|
|
2435
|
+
console.log(` - Full-text search: ${capabilities.supportsFts}`);
|
|
2436
|
+
const config = getService("config");
|
|
2437
|
+
const driftLogger = {
|
|
2438
|
+
debug: (msg) => console.debug(msg),
|
|
2439
|
+
info: (msg) => console.log(msg),
|
|
2440
|
+
warn: (msg) => console.warn(msg),
|
|
2441
|
+
error: (msg) => console.error(msg)
|
|
2442
|
+
};
|
|
2443
|
+
const collections = (config.collections ?? []).map((c) => ({
|
|
2444
|
+
slug: c.slug,
|
|
2445
|
+
tableName: resolveCollectionTableName(c.slug, c.dbName),
|
|
2446
|
+
fields: c.fields ?? [],
|
|
2447
|
+
// Why: forward the Draft/Published flag so the boot-time drift
|
|
2448
|
+
// check actually compares the desired status column against the
|
|
2449
|
+
// live DB. Without this, a code-first collection that opted into
|
|
2450
|
+
// `status: true` after its table was created would never report
|
|
2451
|
+
// drift, masking a missing system status column.
|
|
2452
|
+
status: c.status === true
|
|
2453
|
+
}));
|
|
2454
|
+
await runDriftCheck({
|
|
2455
|
+
adapter,
|
|
2456
|
+
collections,
|
|
2457
|
+
logger: driftLogger
|
|
2458
|
+
});
|
|
2459
|
+
await runBootTimeApplyIfDev({ caller: "init" });
|
|
2460
|
+
void runPostInitTasks();
|
|
2461
|
+
}
|
|
2462
|
+
const directAPI = getNextly();
|
|
2463
|
+
const instance = {
|
|
2464
|
+
// Direct API methods
|
|
2465
|
+
find: directAPI.find.bind(directAPI),
|
|
2466
|
+
findByID: directAPI.findByID.bind(directAPI),
|
|
2467
|
+
create: directAPI.create.bind(directAPI),
|
|
2468
|
+
update: directAPI.update.bind(directAPI),
|
|
2469
|
+
delete: directAPI.delete.bind(directAPI),
|
|
2470
|
+
count: directAPI.count.bind(directAPI),
|
|
2471
|
+
bulkDelete: directAPI.bulkDelete.bind(directAPI),
|
|
2472
|
+
duplicate: directAPI.duplicate.bind(directAPI),
|
|
2473
|
+
findSingle: directAPI.findSingle.bind(directAPI),
|
|
2474
|
+
updateSingle: directAPI.updateSingle.bind(directAPI),
|
|
2475
|
+
findSingles: directAPI.findSingles.bind(directAPI),
|
|
2476
|
+
// Authentication methods
|
|
2477
|
+
login: directAPI.login.bind(directAPI),
|
|
2478
|
+
logout: directAPI.logout.bind(directAPI),
|
|
2479
|
+
me: directAPI.me.bind(directAPI),
|
|
2480
|
+
updateMe: directAPI.updateMe.bind(directAPI),
|
|
2481
|
+
register: directAPI.register.bind(directAPI),
|
|
2482
|
+
changePassword: directAPI.changePassword.bind(directAPI),
|
|
2483
|
+
forgotPassword: directAPI.forgotPassword.bind(directAPI),
|
|
2484
|
+
resetPassword: directAPI.resetPassword.bind(directAPI),
|
|
2485
|
+
verifyEmail: directAPI.verifyEmail.bind(directAPI),
|
|
2486
|
+
// Users API namespace (Direct API style)
|
|
2487
|
+
users: directAPI.users,
|
|
2488
|
+
// Media API namespace (Direct API style)
|
|
2489
|
+
media: directAPI.media,
|
|
2490
|
+
// Forms API namespace (Direct API style)
|
|
2491
|
+
forms: directAPI.forms,
|
|
2492
|
+
// Email & User Field namespaces
|
|
2493
|
+
emailProviders: directAPI.emailProviders,
|
|
2494
|
+
emailTemplates: directAPI.emailTemplates,
|
|
2495
|
+
userFields: directAPI.userFields,
|
|
2496
|
+
email: directAPI.email,
|
|
2497
|
+
// RBAC namespaces
|
|
2498
|
+
roles: directAPI.roles,
|
|
2499
|
+
permissions: directAPI.permissions,
|
|
2500
|
+
access: directAPI.access,
|
|
2501
|
+
// Service accessors
|
|
2502
|
+
collections: getService("collectionService"),
|
|
2503
|
+
userService: getService("userService"),
|
|
2504
|
+
mediaService: getService("mediaService"),
|
|
2505
|
+
storage: getService("mediaStorage"),
|
|
2506
|
+
meta: getService("metaService"),
|
|
2507
|
+
// Direct adapter access
|
|
2508
|
+
adapter: getService("adapter"),
|
|
2509
|
+
// Shutdown method
|
|
2510
|
+
shutdown: async () => {
|
|
2511
|
+
await shutdownServices();
|
|
2512
|
+
globalForInit.__nextly_cachedInstance = null;
|
|
2513
|
+
console.log("Nextly shutdown complete");
|
|
2514
|
+
}
|
|
2515
|
+
};
|
|
2516
|
+
globalForInit.__nextly_cachedInstance = instance;
|
|
2517
|
+
ensureHmrListener();
|
|
2518
|
+
return instance;
|
|
2519
|
+
} finally {
|
|
2520
|
+
globalForInit.__nextly_initPromise = null;
|
|
2521
|
+
}
|
|
2522
|
+
})();
|
|
2523
|
+
globalForInit.__nextly_initPromise = initPromise;
|
|
2524
|
+
return initPromise;
|
|
2525
|
+
}
|
|
2526
|
+
async function getCachedNextly() {
|
|
2527
|
+
if (globalForInit.__nextly_cachedInstance) {
|
|
2528
|
+
return globalForInit.__nextly_cachedInstance;
|
|
2529
|
+
}
|
|
2530
|
+
if (globalForInit.__nextly_initPromise) {
|
|
2531
|
+
return globalForInit.__nextly_initPromise;
|
|
2532
|
+
}
|
|
2533
|
+
if (isServicesRegistered()) {
|
|
2534
|
+
const directAPI = getNextly();
|
|
2535
|
+
const instance = {
|
|
2536
|
+
find: directAPI.find.bind(directAPI),
|
|
2537
|
+
findByID: directAPI.findByID.bind(directAPI),
|
|
2538
|
+
create: directAPI.create.bind(directAPI),
|
|
2539
|
+
update: directAPI.update.bind(directAPI),
|
|
2540
|
+
delete: directAPI.delete.bind(directAPI),
|
|
2541
|
+
count: directAPI.count.bind(directAPI),
|
|
2542
|
+
bulkDelete: directAPI.bulkDelete.bind(directAPI),
|
|
2543
|
+
duplicate: directAPI.duplicate.bind(directAPI),
|
|
2544
|
+
findSingle: directAPI.findSingle.bind(directAPI),
|
|
2545
|
+
updateSingle: directAPI.updateSingle.bind(directAPI),
|
|
2546
|
+
findSingles: directAPI.findSingles.bind(directAPI),
|
|
2547
|
+
login: directAPI.login.bind(directAPI),
|
|
2548
|
+
logout: directAPI.logout.bind(directAPI),
|
|
2549
|
+
me: directAPI.me.bind(directAPI),
|
|
2550
|
+
updateMe: directAPI.updateMe.bind(directAPI),
|
|
2551
|
+
register: directAPI.register.bind(directAPI),
|
|
2552
|
+
changePassword: directAPI.changePassword.bind(directAPI),
|
|
2553
|
+
forgotPassword: directAPI.forgotPassword.bind(directAPI),
|
|
2554
|
+
resetPassword: directAPI.resetPassword.bind(directAPI),
|
|
2555
|
+
verifyEmail: directAPI.verifyEmail.bind(directAPI),
|
|
2556
|
+
users: directAPI.users,
|
|
2557
|
+
media: directAPI.media,
|
|
2558
|
+
forms: directAPI.forms,
|
|
2559
|
+
emailProviders: directAPI.emailProviders,
|
|
2560
|
+
emailTemplates: directAPI.emailTemplates,
|
|
2561
|
+
userFields: directAPI.userFields,
|
|
2562
|
+
email: directAPI.email,
|
|
2563
|
+
roles: directAPI.roles,
|
|
2564
|
+
permissions: directAPI.permissions,
|
|
2565
|
+
access: directAPI.access,
|
|
2566
|
+
collections: getService("collectionService"),
|
|
2567
|
+
userService: getService("userService"),
|
|
2568
|
+
mediaService: getService("mediaService"),
|
|
2569
|
+
storage: getService("mediaStorage"),
|
|
2570
|
+
meta: getService("metaService"),
|
|
2571
|
+
adapter: getService("adapter"),
|
|
2572
|
+
shutdown: async () => {
|
|
2573
|
+
await shutdownServices();
|
|
2574
|
+
globalForInit.__nextly_cachedInstance = null;
|
|
2575
|
+
}
|
|
2576
|
+
};
|
|
2577
|
+
globalForInit.__nextly_cachedInstance = instance;
|
|
2578
|
+
return instance;
|
|
2579
|
+
}
|
|
2580
|
+
throw new NextlyError({
|
|
2581
|
+
code: "CONFIGURATION_ERROR",
|
|
2582
|
+
statusCode: 500,
|
|
2583
|
+
publicMessage: "Server configuration error.",
|
|
2584
|
+
logMessage: "getCachedNextly() called before initialization. Ensure `getNextly({ config })` (or `createRegister(config)` from instrumentation.ts) has run at least once before any internal handler executes. See https://nextlyhq.com/docs/getting-started."
|
|
2585
|
+
});
|
|
2586
|
+
}
|
|
2587
|
+
function createRegister(config) {
|
|
2588
|
+
return async function register2() {
|
|
2589
|
+
await getNextly2({ config });
|
|
2590
|
+
};
|
|
2591
|
+
}
|
|
2592
|
+
async function shutdownNextly() {
|
|
2593
|
+
if (globalForInit.__nextly_cachedInstance) {
|
|
2594
|
+
await globalForInit.__nextly_cachedInstance.shutdown();
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
export {
|
|
2599
|
+
nextly,
|
|
2600
|
+
ensureHmrListener,
|
|
2601
|
+
getNextly2 as getNextly,
|
|
2602
|
+
getCachedNextly,
|
|
2603
|
+
createRegister,
|
|
2604
|
+
shutdownNextly
|
|
2605
|
+
};
|