nextly 0.0.1 → 0.0.2-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +122 -0
- package/dist/_dts-chunks/collections-handler.d-DjgO74Wt.d.ts +20540 -0
- package/dist/_dts-chunks/config.d-DNwsDnjs.d.ts +2589 -0
- package/dist/_dts-chunks/define-component.d-BUgTHmt3.d.ts +1149 -0
- package/dist/_dts-chunks/image-processor.d-OO1PmMrv.d.ts +335 -0
- package/dist/_dts-chunks/index.d-axCAzZ7m.d.ts +17842 -0
- package/dist/_dts-chunks/media.d-DjDOZo4B.d.ts +117 -0
- package/dist/_dts-chunks/on-error.d-CHIKWNxd.d.ts +38 -0
- package/dist/_dts-chunks/storage.d-BUhQ2we_.d.ts +404 -0
- package/dist/actions/index.d.ts +239 -0
- package/dist/actions/index.mjs +281 -0
- package/dist/api/auth-state.d.ts +5 -0
- package/dist/api/auth-state.mjs +131 -0
- package/dist/api/collections-schema-detail.d.ts +56 -0
- package/dist/api/collections-schema-detail.mjs +244 -0
- package/dist/api/collections-schema-export.d.ts +56 -0
- package/dist/api/collections-schema-export.mjs +129 -0
- package/dist/api/collections-schema.d.ts +59 -0
- package/dist/api/collections-schema.mjs +207 -0
- package/dist/api/components-detail.d.ts +50 -0
- package/dist/api/components-detail.mjs +132 -0
- package/dist/api/components.d.ts +69 -0
- package/dist/api/components.mjs +144 -0
- package/dist/api/email-providers-default.d.ts +40 -0
- package/dist/api/email-providers-default.mjs +75 -0
- package/dist/api/email-providers-detail.d.ts +81 -0
- package/dist/api/email-providers-detail.mjs +109 -0
- package/dist/api/email-providers-test.d.ts +43 -0
- package/dist/api/email-providers-test.mjs +114 -0
- package/dist/api/email-providers.d.ts +69 -0
- package/dist/api/email-providers.mjs +110 -0
- package/dist/api/email-send-template.d.ts +41 -0
- package/dist/api/email-send-template.mjs +58 -0
- package/dist/api/email-send.d.ts +42 -0
- package/dist/api/email-send.mjs +58 -0
- package/dist/api/email-templates-detail.d.ts +74 -0
- package/dist/api/email-templates-detail.mjs +112 -0
- package/dist/api/email-templates-layout.d.ts +55 -0
- package/dist/api/email-templates-layout.mjs +92 -0
- package/dist/api/email-templates-preview.d.ts +48 -0
- package/dist/api/email-templates-preview.mjs +93 -0
- package/dist/api/email-templates.d.ts +61 -0
- package/dist/api/email-templates.mjs +118 -0
- package/dist/api/health.d.ts +68 -0
- package/dist/api/health.mjs +67 -0
- package/dist/api/index.d.ts +54 -0
- package/dist/api/index.mjs +16 -0
- package/dist/api/media-bulk.d.ts +74 -0
- package/dist/api/media-bulk.mjs +196 -0
- package/dist/api/media-folders.d.ts +112 -0
- package/dist/api/media-folders.mjs +187 -0
- package/dist/api/media-handlers.d.ts +102 -0
- package/dist/api/media-handlers.mjs +437 -0
- package/dist/api/media.d.ts +117 -0
- package/dist/api/media.mjs +242 -0
- package/dist/api/singles-detail.d.ts +87 -0
- package/dist/api/singles-detail.mjs +170 -0
- package/dist/api/singles-schema-detail.d.ts +54 -0
- package/dist/api/singles-schema-detail.mjs +182 -0
- package/dist/api/singles.d.ts +34 -0
- package/dist/api/singles.mjs +94 -0
- package/dist/api/storage-upload-url.d.ts +48 -0
- package/dist/api/storage-upload-url.mjs +202 -0
- package/dist/api/uploads.d.ts +109 -0
- package/dist/api/uploads.mjs +359 -0
- package/dist/auth/index.d.ts +425 -0
- package/dist/auth/index.mjs +199 -0
- package/dist/boot-apply-PQSYLDIN.mjs +7 -0
- package/dist/chunk-2OALJTK6.mjs +489 -0
- package/dist/chunk-2Q2SX2CS.mjs +365 -0
- package/dist/chunk-2TFX4ND3.mjs +13 -0
- package/dist/chunk-2TWPDSYD.mjs +87 -0
- package/dist/chunk-2W3DVD7S.mjs +647 -0
- package/dist/chunk-2ZFKXPQM.mjs +88 -0
- package/dist/chunk-3FA7FKAV.mjs +832 -0
- package/dist/chunk-3NZ2KMBL.mjs +58 -0
- package/dist/chunk-4MJLT6PZ.mjs +0 -0
- package/dist/chunk-56WO4WX7.mjs +0 -0
- package/dist/chunk-5APFUGAD.mjs +89 -0
- package/dist/chunk-5HMZ644B.mjs +108 -0
- package/dist/chunk-67GXH6PR.mjs +32 -0
- package/dist/chunk-6JNEPWRW.mjs +14368 -0
- package/dist/chunk-6NFHQIJD.mjs +45 -0
- package/dist/chunk-7P6ASYW6.mjs +9 -0
- package/dist/chunk-A3WPLSDT.mjs +1364 -0
- package/dist/chunk-AGJ6F2T3.mjs +144 -0
- package/dist/chunk-AK6Z23OX.mjs +1464 -0
- package/dist/chunk-APKKRD2G.mjs +102 -0
- package/dist/chunk-B2GV2BWH.mjs +73 -0
- package/dist/chunk-D5HQBNUB.mjs +74 -0
- package/dist/chunk-DNNG377Z.mjs +204 -0
- package/dist/chunk-DP3G27G5.mjs +135 -0
- package/dist/chunk-DV6WVX2Q.mjs +0 -0
- package/dist/chunk-DXGGXIUZ.mjs +57 -0
- package/dist/chunk-EGXBZCGC.mjs +943 -0
- package/dist/chunk-ERCNLX3V.mjs +176 -0
- package/dist/chunk-FQULBZ53.mjs +850 -0
- package/dist/chunk-G2AA4QLC.mjs +262 -0
- package/dist/chunk-GDBJ5JCU.mjs +488 -0
- package/dist/chunk-GJNSJU4S.mjs +19 -0
- package/dist/chunk-GZ6DCQKC.mjs +69 -0
- package/dist/chunk-H26B4FYG.mjs +167 -0
- package/dist/chunk-I4JMR3UR.mjs +21 -0
- package/dist/chunk-INV7QKLG.mjs +508 -0
- package/dist/chunk-IUDOC7N7.mjs +46 -0
- package/dist/chunk-IZWPRDC3.mjs +206 -0
- package/dist/chunk-KIMNCZGV.mjs +15 -0
- package/dist/chunk-L6HW2DA7.mjs +15 -0
- package/dist/chunk-LAZXX4HR.mjs +100 -0
- package/dist/chunk-LDKCUMHK.mjs +95 -0
- package/dist/chunk-LRXMECUA.mjs +0 -0
- package/dist/chunk-M52VMPGA.mjs +119 -0
- package/dist/chunk-MGUWEEI6.mjs +160 -0
- package/dist/chunk-NRUWQ5Z7.mjs +419 -0
- package/dist/chunk-NSEFNNU4.mjs +25360 -0
- package/dist/chunk-NTHVDFGO.mjs +138 -0
- package/dist/chunk-O3QHXMOX.mjs +3166 -0
- package/dist/chunk-P7NH2OSC.mjs +2605 -0
- package/dist/chunk-PKMABBB5.mjs +184 -0
- package/dist/chunk-PWS6XGJK.mjs +76 -0
- package/dist/chunk-R6JJQHFC.mjs +20 -0
- package/dist/chunk-RJLLGGPG.mjs +0 -0
- package/dist/chunk-SBACDPNX.mjs +689 -0
- package/dist/chunk-TO5AFLVQ.mjs +124 -0
- package/dist/chunk-TS7GHTG2.mjs +5436 -0
- package/dist/chunk-UJ2IMJ4W.mjs +133 -0
- package/dist/chunk-UOP63Q54.mjs +102 -0
- package/dist/chunk-UUOFWCM6.mjs +78 -0
- package/dist/chunk-V4EQTOA4.mjs +893 -0
- package/dist/chunk-VJ66NCL4.mjs +193 -0
- package/dist/chunk-VQJQHVEV.mjs +29 -0
- package/dist/chunk-VTJADRO3.mjs +141 -0
- package/dist/chunk-VWF3JO32.mjs +0 -0
- package/dist/chunk-W4MGXIRR.mjs +27 -0
- package/dist/chunk-W5KKPZT5.mjs +1204 -0
- package/dist/chunk-WD34YQ6T.mjs +381 -0
- package/dist/chunk-WZBYMYVW.mjs +14 -0
- package/dist/chunk-X23WKS3Z.mjs +50 -0
- package/dist/chunk-X7TXCYYN.mjs +6496 -0
- package/dist/chunk-XGI4EMS3.mjs +140 -0
- package/dist/chunk-XZKLBMN6.mjs +1153 -0
- package/dist/chunk-YB7INWPY.mjs +0 -0
- package/dist/chunk-YV4Y7SDL.mjs +83 -0
- package/dist/chunk-YZNBLFIW.mjs +1688 -0
- package/dist/chunk-YZZCTONM.mjs +263 -0
- package/dist/chunk-ZE6A3FYH.mjs +289 -0
- package/dist/cli/nextly.mjs +68 -0
- package/dist/cli/utils/index.d.ts +449 -0
- package/dist/cli/utils/index.mjs +49 -0
- package/dist/component-schema-service-5577KVW6.mjs +11 -0
- package/dist/config-loader-23YEMC3Z.mjs +23 -0
- package/dist/config.d.ts +44 -0
- package/dist/config.mjs +109 -0
- package/dist/container-ORGFGYSZ.mjs +9 -0
- package/dist/database/index.d.ts +12 -0
- package/dist/database/index.mjs +40 -0
- package/dist/database/seeders/index.d.ts +93 -0
- package/dist/database/seeders/index.mjs +47 -0
- package/dist/db-sync-demote-LJGKLB3S.mjs +117 -0
- package/dist/db-sync-promote-B26VSYQF.mjs +113 -0
- package/dist/dev-reload-broadcaster-B73IQ53V.mjs +25 -0
- package/dist/dist-M2NOU37V.mjs +19 -0
- package/dist/drizzle-kit-lazy-D2M2PXR2.mjs +13 -0
- package/dist/dynamic-collection-schema-service-IEXTPIZ7.mjs +8 -0
- package/dist/errors/index.d.ts +159 -0
- package/dist/errors/index.mjs +10 -0
- package/dist/factory-IWMBKUJM.mjs +15 -0
- package/dist/first-run-QIVKWJIF.mjs +63 -0
- package/dist/fresh-push-NR67DC3R.mjs +8 -0
- package/dist/index.d.ts +4175 -0
- package/dist/index.mjs +1336 -0
- package/dist/local-plugin-PTET4NAT.mjs +7 -0
- package/dist/logger-NU46DXNY.mjs +15 -0
- package/dist/logger-YE4TC7ZN.mjs +9 -0
- package/dist/migration-journal-EP532Y4L.mjs +139 -0
- package/dist/migrations/mysql/0000_eager_sentry.sql +174 -0
- package/dist/migrations/mysql/0001_soft_giant_girl.sql +27 -0
- package/dist/migrations/mysql/0002_media_table.sql +24 -0
- package/dist/migrations/mysql/0003_dynamic_singles.sql +37 -0
- package/dist/migrations/mysql/0004_dynamic_components.sql +35 -0
- package/dist/migrations/mysql/0005_user_management_tables.sql +92 -0
- package/dist/migrations/mysql/0006_api_keys.sql +36 -0
- package/dist/migrations/mysql/0007_general_settings.sql +20 -0
- package/dist/migrations/mysql/0008_site_settings_logo_url.sql +9 -0
- package/dist/migrations/mysql/0009_activity_log.sql +30 -0
- package/dist/migrations/mysql/0010_site_settings_sidebar.sql +13 -0
- package/dist/migrations/mysql/0011_missing_tables_and_columns.sql +54 -0
- package/dist/migrations/mysql/0012_image_sizes_and_focal_point.sql +30 -0
- package/dist/migrations/mysql/0012_media_folders.sql +43 -0
- package/dist/migrations/mysql/0013_user_brute_force_protection.sql +31 -0
- package/dist/migrations/mysql/0014_email_template_attachments.sql +12 -0
- package/dist/migrations/mysql/0015_media_uploaded_by_nullable.sql +15 -0
- package/dist/migrations/mysql/20260429_000000_000_initial_journal.sql +22 -0
- package/dist/migrations/mysql/20260501_000000_journal_batch.sql +17 -0
- package/dist/migrations/mysql/20260501_000001_audit_log.sql +24 -0
- package/dist/migrations/mysql/20260504_000000_nextly_meta.sql +21 -0
- package/dist/migrations/mysql/meta/0000_snapshot.json +1005 -0
- package/dist/migrations/mysql/meta/0001_snapshot.json +1099 -0
- package/dist/migrations/mysql/meta/_journal.json +41 -0
- package/dist/migrations/postgresql/0000_misty_king_bedlam.sql +169 -0
- package/dist/migrations/postgresql/0001_perpetual_captain_marvel.sql +8 -0
- package/dist/migrations/postgresql/0002_sad_spectrum.sql +16 -0
- package/dist/migrations/postgresql/0003_hesitant_ultron.sql +17 -0
- package/dist/migrations/postgresql/0004_media_table.sql +24 -0
- package/dist/migrations/postgresql/0005_media_folders.sql +36 -0
- package/dist/migrations/postgresql/0006_dynamic_collections_update.sql +50 -0
- package/dist/migrations/postgresql/0007_dynamic_singles.sql +38 -0
- package/dist/migrations/postgresql/0008_dynamic_components.sql +37 -0
- package/dist/migrations/postgresql/0009_user_management_tables.sql +95 -0
- package/dist/migrations/postgresql/0010_api_keys.sql +34 -0
- package/dist/migrations/postgresql/0011_general_settings.sql +20 -0
- package/dist/migrations/postgresql/0012_site_settings_logo_url.sql +9 -0
- package/dist/migrations/postgresql/0013_activity_log.sql +29 -0
- package/dist/migrations/postgresql/0014_image_sizes_and_focal_point.sql +33 -0
- package/dist/migrations/postgresql/0014_site_settings_sidebar.sql +13 -0
- package/dist/migrations/postgresql/0015_user_brute_force_protection.sql +29 -0
- package/dist/migrations/postgresql/0016_email_template_attachments.sql +12 -0
- package/dist/migrations/postgresql/0017_media_uploaded_by_nullable.sql +15 -0
- package/dist/migrations/postgresql/20260429_000000_000_initial_journal.sql +24 -0
- package/dist/migrations/postgresql/20260501_000000_journal_batch.sql +17 -0
- package/dist/migrations/postgresql/20260501_000001_audit_log.sql +24 -0
- package/dist/migrations/postgresql/20260504_000000_nextly_meta.sql +22 -0
- package/dist/migrations/postgresql/meta/0000_snapshot.json +1286 -0
- package/dist/migrations/postgresql/meta/0001_snapshot.json +1407 -0
- package/dist/migrations/postgresql/meta/0002_snapshot.json +1552 -0
- package/dist/migrations/postgresql/meta/0003_snapshot.json +1695 -0
- package/dist/migrations/postgresql/meta/0010_snapshot.json +2345 -0
- package/dist/migrations/postgresql/meta/_journal.json +90 -0
- package/dist/migrations/sqlite/0000_api_keys.sql +34 -0
- package/dist/migrations/sqlite/0001_general_settings.sql +20 -0
- package/dist/migrations/sqlite/0002_site_settings_logo_url.sql +9 -0
- package/dist/migrations/sqlite/0003_activity_log.sql +29 -0
- package/dist/migrations/sqlite/0004_image_sizes_and_focal_point.sql +29 -0
- package/dist/migrations/sqlite/0004_site_settings_sidebar.sql +11 -0
- package/dist/migrations/sqlite/0005_user_brute_force_protection.sql +29 -0
- package/dist/migrations/sqlite/0006_email_template_attachments.sql +12 -0
- package/dist/migrations/sqlite/0007_media_uploaded_by_nullable.sql +111 -0
- package/dist/migrations/sqlite/20260429_000000_000_initial_journal.sql +24 -0
- package/dist/migrations/sqlite/20260501_000000_journal_batch.sql +19 -0
- package/dist/migrations/sqlite/20260501_000001_audit_log.sql +24 -0
- package/dist/migrations/sqlite/20260504_000000_nextly_meta.sql +21 -0
- package/dist/migrations/sqlite/20260505_000000_user_management_tables.sql +77 -0
- package/dist/next.d.ts +57 -0
- package/dist/next.mjs +55 -0
- package/dist/observability/index.d.ts +87 -0
- package/dist/observability/index.mjs +57 -0
- package/dist/permissions-3DZZQZMI.mjs +39 -0
- package/dist/pipeline-YOML7SWF.mjs +29 -0
- package/dist/preview-ZZTR3QGS.mjs +9 -0
- package/dist/program-PW6UB2ZC.mjs +5934 -0
- package/dist/reconcile-single-tables-7ENVXJGB.mjs +7 -0
- package/dist/register-SF6E6FVU.mjs +49 -0
- package/dist/reload-config-HWQ4G5MM.mjs +23 -0
- package/dist/resolve-single-table-name-JSOMUB3R.mjs +7 -0
- package/dist/routeHandler-UNMMJIBM.mjs +77 -0
- package/dist/runtime-schema-generator-NRA6A6Z6.mjs +8 -0
- package/dist/runtime.d.ts +120 -0
- package/dist/runtime.mjs +73 -0
- package/dist/schema-hash-FMMG6VPJ.mjs +13 -0
- package/dist/schema-registry-EQ36FZDP.mjs +7 -0
- package/dist/scripts/load-env.mjs +42 -0
- package/dist/storage/index.d.ts +566 -0
- package/dist/storage/index.mjs +45 -0
- package/dist/super-admin-G5ZK5F4T.mjs +39 -0
- package/dist/system-table-service-WGSRVEGT.mjs +17 -0
- package/dist/users-7KELGRYJ.mjs +38 -0
- package/package.json +308 -9
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readJsonBody
|
|
3
|
+
} from "../chunk-VQJQHVEV.mjs";
|
|
4
|
+
import {
|
|
5
|
+
nextlyValidationFromZod
|
|
6
|
+
} from "../chunk-GJNSJU4S.mjs";
|
|
7
|
+
import "../chunk-VWF3JO32.mjs";
|
|
8
|
+
import {
|
|
9
|
+
withErrorHandler
|
|
10
|
+
} from "../chunk-TO5AFLVQ.mjs";
|
|
11
|
+
import {
|
|
12
|
+
getCachedNextly,
|
|
13
|
+
getNextly
|
|
14
|
+
} from "../chunk-P7NH2OSC.mjs";
|
|
15
|
+
import "../chunk-2OALJTK6.mjs";
|
|
16
|
+
import "../chunk-VJ66NCL4.mjs";
|
|
17
|
+
import "../chunk-R6JJQHFC.mjs";
|
|
18
|
+
import "../chunk-X23WKS3Z.mjs";
|
|
19
|
+
import {
|
|
20
|
+
getService
|
|
21
|
+
} from "../chunk-X7TXCYYN.mjs";
|
|
22
|
+
import "../chunk-YZNBLFIW.mjs";
|
|
23
|
+
import "../chunk-FQULBZ53.mjs";
|
|
24
|
+
import "../chunk-67GXH6PR.mjs";
|
|
25
|
+
import "../chunk-2TFX4ND3.mjs";
|
|
26
|
+
import "../chunk-W4MGXIRR.mjs";
|
|
27
|
+
import {
|
|
28
|
+
UpdateMediaInputSchema,
|
|
29
|
+
UploadMediaInputSchema
|
|
30
|
+
} from "../chunk-NSEFNNU4.mjs";
|
|
31
|
+
import "../chunk-3FA7FKAV.mjs";
|
|
32
|
+
import "../chunk-AGJ6F2T3.mjs";
|
|
33
|
+
import "../chunk-KIMNCZGV.mjs";
|
|
34
|
+
import "../chunk-AK6Z23OX.mjs";
|
|
35
|
+
import "../chunk-INV7QKLG.mjs";
|
|
36
|
+
import "../chunk-GZ6DCQKC.mjs";
|
|
37
|
+
import "../chunk-SBACDPNX.mjs";
|
|
38
|
+
import "../chunk-RJLLGGPG.mjs";
|
|
39
|
+
import "../chunk-V4EQTOA4.mjs";
|
|
40
|
+
import "../chunk-I4JMR3UR.mjs";
|
|
41
|
+
import "../chunk-XZKLBMN6.mjs";
|
|
42
|
+
import "../chunk-IZWPRDC3.mjs";
|
|
43
|
+
import "../chunk-DNNG377Z.mjs";
|
|
44
|
+
import "../chunk-5HMZ644B.mjs";
|
|
45
|
+
import "../chunk-W5KKPZT5.mjs";
|
|
46
|
+
import "../chunk-56WO4WX7.mjs";
|
|
47
|
+
import {
|
|
48
|
+
withTimezoneFormatting
|
|
49
|
+
} from "../chunk-2W3DVD7S.mjs";
|
|
50
|
+
import "../chunk-TS7GHTG2.mjs";
|
|
51
|
+
import "../chunk-H26B4FYG.mjs";
|
|
52
|
+
import "../chunk-DP3G27G5.mjs";
|
|
53
|
+
import "../chunk-DV6WVX2Q.mjs";
|
|
54
|
+
import "../chunk-UJ2IMJ4W.mjs";
|
|
55
|
+
import "../chunk-EGXBZCGC.mjs";
|
|
56
|
+
import "../chunk-G2AA4QLC.mjs";
|
|
57
|
+
import "../chunk-4MJLT6PZ.mjs";
|
|
58
|
+
import {
|
|
59
|
+
respondAction,
|
|
60
|
+
respondData,
|
|
61
|
+
respondDoc,
|
|
62
|
+
respondList,
|
|
63
|
+
respondMutation
|
|
64
|
+
} from "../chunk-IUDOC7N7.mjs";
|
|
65
|
+
import "../chunk-LAZXX4HR.mjs";
|
|
66
|
+
import "../chunk-D5HQBNUB.mjs";
|
|
67
|
+
import {
|
|
68
|
+
NextlyError
|
|
69
|
+
} from "../chunk-NRUWQ5Z7.mjs";
|
|
70
|
+
import "../chunk-VTJADRO3.mjs";
|
|
71
|
+
import "../chunk-5APFUGAD.mjs";
|
|
72
|
+
import "../chunk-7P6ASYW6.mjs";
|
|
73
|
+
|
|
74
|
+
// src/api/media-handlers.ts
|
|
75
|
+
import { z } from "zod";
|
|
76
|
+
var handlerConfig;
|
|
77
|
+
async function getMediaService() {
|
|
78
|
+
if (handlerConfig?.config) {
|
|
79
|
+
await getNextly(handlerConfig);
|
|
80
|
+
} else {
|
|
81
|
+
await getCachedNextly();
|
|
82
|
+
}
|
|
83
|
+
return getService("mediaService");
|
|
84
|
+
}
|
|
85
|
+
function createRequestContext() {
|
|
86
|
+
return {};
|
|
87
|
+
}
|
|
88
|
+
function createAuthenticatedContext(userId) {
|
|
89
|
+
return {
|
|
90
|
+
user: {
|
|
91
|
+
id: userId,
|
|
92
|
+
email: `${userId}@api.local`,
|
|
93
|
+
role: "user",
|
|
94
|
+
permissions: []
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function parseMediaRoute(path, method) {
|
|
99
|
+
const segments = path || [];
|
|
100
|
+
if (segments.length === 0) {
|
|
101
|
+
if (method === "GET") return { type: "list-media" };
|
|
102
|
+
if (method === "POST") return { type: "upload-media" };
|
|
103
|
+
return { type: "not-found" };
|
|
104
|
+
}
|
|
105
|
+
if (segments[0] === "folders") {
|
|
106
|
+
if (segments.length === 1) {
|
|
107
|
+
if (method === "GET") return { type: "list-folders" };
|
|
108
|
+
if (method === "POST") return { type: "create-folder" };
|
|
109
|
+
return { type: "not-found" };
|
|
110
|
+
}
|
|
111
|
+
if (segments[1] === "root" && segments[2] === "contents") {
|
|
112
|
+
if (method === "GET") return { type: "get-root-contents" };
|
|
113
|
+
return { type: "not-found" };
|
|
114
|
+
}
|
|
115
|
+
if (segments.length === 3 && segments[2] === "contents") {
|
|
116
|
+
if (method === "GET")
|
|
117
|
+
return { type: "get-folder-contents", folderId: segments[1] };
|
|
118
|
+
return { type: "not-found" };
|
|
119
|
+
}
|
|
120
|
+
if (segments.length === 2) {
|
|
121
|
+
if (method === "GET")
|
|
122
|
+
return { type: "get-folder", folderId: segments[1] };
|
|
123
|
+
if (method === "PATCH")
|
|
124
|
+
return { type: "update-folder", folderId: segments[1] };
|
|
125
|
+
if (method === "DELETE")
|
|
126
|
+
return { type: "delete-folder", folderId: segments[1] };
|
|
127
|
+
return { type: "not-found" };
|
|
128
|
+
}
|
|
129
|
+
return { type: "not-found" };
|
|
130
|
+
}
|
|
131
|
+
if (segments.length === 2 && segments[1] === "move") {
|
|
132
|
+
if (method === "PATCH") return { type: "move-media", mediaId: segments[0] };
|
|
133
|
+
return { type: "not-found" };
|
|
134
|
+
}
|
|
135
|
+
if (segments.length === 1) {
|
|
136
|
+
if (method === "GET") return { type: "get-media", mediaId: segments[0] };
|
|
137
|
+
if (method === "PATCH")
|
|
138
|
+
return { type: "update-media", mediaId: segments[0] };
|
|
139
|
+
if (method === "DELETE")
|
|
140
|
+
return { type: "delete-media", mediaId: segments[0] };
|
|
141
|
+
return { type: "not-found" };
|
|
142
|
+
}
|
|
143
|
+
return { type: "not-found" };
|
|
144
|
+
}
|
|
145
|
+
function throwUnmatchedRoute(path, method) {
|
|
146
|
+
throw NextlyError.notFound({
|
|
147
|
+
logContext: {
|
|
148
|
+
path: path ?? [],
|
|
149
|
+
method,
|
|
150
|
+
reason: "unmatched-media-route"
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
async function handleListMedia(request) {
|
|
155
|
+
const mediaService = await getMediaService();
|
|
156
|
+
const { searchParams } = new URL(request.url);
|
|
157
|
+
const context = createRequestContext();
|
|
158
|
+
const folderIdParam = searchParams.get("folderId");
|
|
159
|
+
const options = {
|
|
160
|
+
page: searchParams.get("page") ? Number(searchParams.get("page")) : 1,
|
|
161
|
+
limit: searchParams.get("limit") ? Number(searchParams.get("limit")) : 24,
|
|
162
|
+
search: searchParams.get("search") || void 0,
|
|
163
|
+
type: searchParams.get("type") || void 0,
|
|
164
|
+
folderId: folderIdParam === "root" ? "root" : folderIdParam || void 0,
|
|
165
|
+
sortBy: searchParams.get("sortBy") || "uploadedAt",
|
|
166
|
+
sortOrder: searchParams.get("sortOrder") || "desc"
|
|
167
|
+
};
|
|
168
|
+
const result = await mediaService.listMedia(options, context);
|
|
169
|
+
const page = options.page ?? 1;
|
|
170
|
+
const limit = Math.max(1, options.limit ?? 24);
|
|
171
|
+
const total = result.pagination.total;
|
|
172
|
+
const totalPages = limit > 0 ? Math.ceil(total / limit) : 0;
|
|
173
|
+
return respondList(result.data, {
|
|
174
|
+
total,
|
|
175
|
+
page,
|
|
176
|
+
limit,
|
|
177
|
+
totalPages,
|
|
178
|
+
hasNext: page < totalPages,
|
|
179
|
+
hasPrev: page > 1
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
async function handleUploadMedia(request) {
|
|
183
|
+
const mediaService = await getMediaService();
|
|
184
|
+
const formData = await request.formData();
|
|
185
|
+
const file = formData.get("file");
|
|
186
|
+
const uploadedBy = formData.get("uploadedBy");
|
|
187
|
+
const folderId = formData.get("folderId");
|
|
188
|
+
const errors = [];
|
|
189
|
+
if (!file) {
|
|
190
|
+
errors.push({
|
|
191
|
+
path: "file",
|
|
192
|
+
code: "REQUIRED",
|
|
193
|
+
message: "file is required."
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
if (!uploadedBy) {
|
|
197
|
+
errors.push({
|
|
198
|
+
path: "uploadedBy",
|
|
199
|
+
code: "REQUIRED",
|
|
200
|
+
message: "uploadedBy is required."
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
if (errors.length > 0) {
|
|
204
|
+
throw NextlyError.validation({ errors });
|
|
205
|
+
}
|
|
206
|
+
const fileEnsured = file;
|
|
207
|
+
const uploadedByEnsured = uploadedBy;
|
|
208
|
+
const arrayBuffer = await fileEnsured.arrayBuffer();
|
|
209
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
210
|
+
const input = {
|
|
211
|
+
file: buffer,
|
|
212
|
+
filename: fileEnsured.name,
|
|
213
|
+
mimeType: fileEnsured.type,
|
|
214
|
+
size: fileEnsured.size,
|
|
215
|
+
uploadedBy: uploadedByEnsured
|
|
216
|
+
};
|
|
217
|
+
try {
|
|
218
|
+
UploadMediaInputSchema.parse(input);
|
|
219
|
+
} catch (err) {
|
|
220
|
+
if (err instanceof z.ZodError) throw nextlyValidationFromZod(err);
|
|
221
|
+
throw err;
|
|
222
|
+
}
|
|
223
|
+
const context = createAuthenticatedContext(uploadedByEnsured);
|
|
224
|
+
const mediaFile = await mediaService.upload(
|
|
225
|
+
{
|
|
226
|
+
buffer,
|
|
227
|
+
filename: fileEnsured.name,
|
|
228
|
+
mimeType: fileEnsured.type,
|
|
229
|
+
size: fileEnsured.size,
|
|
230
|
+
folderId: folderId || void 0
|
|
231
|
+
},
|
|
232
|
+
context
|
|
233
|
+
);
|
|
234
|
+
return respondMutation("Media uploaded.", mediaFile, { status: 201 });
|
|
235
|
+
}
|
|
236
|
+
async function handleGetMedia(mediaId) {
|
|
237
|
+
const mediaService = await getMediaService();
|
|
238
|
+
const context = createRequestContext();
|
|
239
|
+
const mediaFile = await mediaService.findById(mediaId, context);
|
|
240
|
+
return respondDoc(mediaFile);
|
|
241
|
+
}
|
|
242
|
+
async function handleUpdateMedia(request, mediaId) {
|
|
243
|
+
const mediaService = await getMediaService();
|
|
244
|
+
const body = await readJsonBody(request);
|
|
245
|
+
let validated;
|
|
246
|
+
try {
|
|
247
|
+
validated = UpdateMediaInputSchema.parse(body);
|
|
248
|
+
} catch (err) {
|
|
249
|
+
if (err instanceof z.ZodError) throw nextlyValidationFromZod(err);
|
|
250
|
+
throw err;
|
|
251
|
+
}
|
|
252
|
+
const context = createRequestContext();
|
|
253
|
+
const mediaFile = await mediaService.update(mediaId, validated, context);
|
|
254
|
+
return respondMutation("Media updated.", mediaFile);
|
|
255
|
+
}
|
|
256
|
+
async function handleDeleteMedia(mediaId) {
|
|
257
|
+
const mediaService = await getMediaService();
|
|
258
|
+
const context = createRequestContext();
|
|
259
|
+
await mediaService.delete(mediaId, context);
|
|
260
|
+
return respondAction("Media deleted.", { id: mediaId });
|
|
261
|
+
}
|
|
262
|
+
async function handleMoveMedia(request, mediaId) {
|
|
263
|
+
const mediaService = await getMediaService();
|
|
264
|
+
const body = await readJsonBody(request);
|
|
265
|
+
const folderId = body.folderId;
|
|
266
|
+
const context = createRequestContext();
|
|
267
|
+
await mediaService.moveToFolder(mediaId, folderId ?? null, context);
|
|
268
|
+
return respondAction("Media moved.", { id: mediaId, folderId: folderId ?? null });
|
|
269
|
+
}
|
|
270
|
+
async function handleListFolders(request) {
|
|
271
|
+
const mediaService = await getMediaService();
|
|
272
|
+
const context = createRequestContext();
|
|
273
|
+
const { searchParams } = new URL(request.url);
|
|
274
|
+
const root = searchParams.get("root") === "true";
|
|
275
|
+
const parentId = searchParams.get("parentId");
|
|
276
|
+
const folders = root || !parentId ? await mediaService.listRootFolders(context) : await mediaService.listSubfolders(parentId, context);
|
|
277
|
+
return respondData({ folders });
|
|
278
|
+
}
|
|
279
|
+
async function handleCreateFolder(request) {
|
|
280
|
+
const mediaService = await getMediaService();
|
|
281
|
+
const body = await readJsonBody(request);
|
|
282
|
+
const { createdBy, ...folderInput } = body;
|
|
283
|
+
const errors = [];
|
|
284
|
+
if (!createdBy) {
|
|
285
|
+
errors.push({
|
|
286
|
+
path: "createdBy",
|
|
287
|
+
code: "REQUIRED",
|
|
288
|
+
message: "createdBy is required."
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
if (!folderInput.name) {
|
|
292
|
+
errors.push({
|
|
293
|
+
path: "name",
|
|
294
|
+
code: "REQUIRED",
|
|
295
|
+
message: "name is required."
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
if (errors.length > 0) {
|
|
299
|
+
throw NextlyError.validation({ errors });
|
|
300
|
+
}
|
|
301
|
+
const context = createAuthenticatedContext(createdBy);
|
|
302
|
+
const folder = await mediaService.createFolder(
|
|
303
|
+
folderInput,
|
|
304
|
+
context
|
|
305
|
+
);
|
|
306
|
+
return respondMutation("Folder created.", folder, { status: 201 });
|
|
307
|
+
}
|
|
308
|
+
async function handleGetFolder(folderId) {
|
|
309
|
+
const mediaService = await getMediaService();
|
|
310
|
+
const context = createRequestContext();
|
|
311
|
+
const folder = await mediaService.findFolderById(folderId, context);
|
|
312
|
+
return respondDoc(folder);
|
|
313
|
+
}
|
|
314
|
+
async function handleUpdateFolder(request, folderId) {
|
|
315
|
+
const mediaService = await getMediaService();
|
|
316
|
+
const body = await readJsonBody(request);
|
|
317
|
+
const context = createRequestContext();
|
|
318
|
+
const folder = await mediaService.updateFolder(folderId, body, context);
|
|
319
|
+
return respondMutation("Folder updated.", folder);
|
|
320
|
+
}
|
|
321
|
+
async function handleDeleteFolder(request, folderId) {
|
|
322
|
+
const mediaService = await getMediaService();
|
|
323
|
+
const context = createRequestContext();
|
|
324
|
+
const { searchParams } = new URL(request.url);
|
|
325
|
+
const deleteContents = searchParams.get("deleteContents") === "true";
|
|
326
|
+
await mediaService.deleteFolder(folderId, deleteContents, context);
|
|
327
|
+
return respondAction("Folder deleted.", { id: folderId });
|
|
328
|
+
}
|
|
329
|
+
async function handleGetFolderContents(folderId) {
|
|
330
|
+
const mediaService = await getMediaService();
|
|
331
|
+
const context = createRequestContext();
|
|
332
|
+
const contents = await mediaService.getFolderContents(folderId, context);
|
|
333
|
+
return respondData(contents);
|
|
334
|
+
}
|
|
335
|
+
async function handleGetRootContents() {
|
|
336
|
+
const mediaService = await getMediaService();
|
|
337
|
+
const context = createRequestContext();
|
|
338
|
+
const contents = await mediaService.getFolderContents(null, context);
|
|
339
|
+
return respondData(contents);
|
|
340
|
+
}
|
|
341
|
+
function createMediaHandlers(options) {
|
|
342
|
+
if (options?.config) {
|
|
343
|
+
handlerConfig = { config: options.config };
|
|
344
|
+
}
|
|
345
|
+
return {
|
|
346
|
+
GET: withErrorHandler(
|
|
347
|
+
async (request, ctx) => {
|
|
348
|
+
const resolvedParams = await ctx.params;
|
|
349
|
+
const route = parseMediaRoute(resolvedParams.path, "GET");
|
|
350
|
+
let response;
|
|
351
|
+
switch (route.type) {
|
|
352
|
+
case "list-media":
|
|
353
|
+
response = await handleListMedia(request);
|
|
354
|
+
break;
|
|
355
|
+
case "get-media":
|
|
356
|
+
response = await handleGetMedia(route.mediaId);
|
|
357
|
+
break;
|
|
358
|
+
case "list-folders":
|
|
359
|
+
response = await handleListFolders(request);
|
|
360
|
+
break;
|
|
361
|
+
case "get-folder":
|
|
362
|
+
response = await handleGetFolder(route.folderId);
|
|
363
|
+
break;
|
|
364
|
+
case "get-folder-contents":
|
|
365
|
+
response = await handleGetFolderContents(route.folderId);
|
|
366
|
+
break;
|
|
367
|
+
case "get-root-contents":
|
|
368
|
+
response = await handleGetRootContents();
|
|
369
|
+
break;
|
|
370
|
+
default:
|
|
371
|
+
throwUnmatchedRoute(resolvedParams.path, "GET");
|
|
372
|
+
}
|
|
373
|
+
return withTimezoneFormatting(response);
|
|
374
|
+
}
|
|
375
|
+
),
|
|
376
|
+
POST: withErrorHandler(
|
|
377
|
+
async (request, ctx) => {
|
|
378
|
+
const resolvedParams = await ctx.params;
|
|
379
|
+
const route = parseMediaRoute(resolvedParams.path, "POST");
|
|
380
|
+
let response;
|
|
381
|
+
switch (route.type) {
|
|
382
|
+
case "upload-media":
|
|
383
|
+
response = await handleUploadMedia(request);
|
|
384
|
+
break;
|
|
385
|
+
case "create-folder":
|
|
386
|
+
response = await handleCreateFolder(request);
|
|
387
|
+
break;
|
|
388
|
+
default:
|
|
389
|
+
throwUnmatchedRoute(resolvedParams.path, "POST");
|
|
390
|
+
}
|
|
391
|
+
return withTimezoneFormatting(response);
|
|
392
|
+
}
|
|
393
|
+
),
|
|
394
|
+
PATCH: withErrorHandler(
|
|
395
|
+
async (request, ctx) => {
|
|
396
|
+
const resolvedParams = await ctx.params;
|
|
397
|
+
const route = parseMediaRoute(resolvedParams.path, "PATCH");
|
|
398
|
+
let response;
|
|
399
|
+
switch (route.type) {
|
|
400
|
+
case "update-media":
|
|
401
|
+
response = await handleUpdateMedia(request, route.mediaId);
|
|
402
|
+
break;
|
|
403
|
+
case "move-media":
|
|
404
|
+
response = await handleMoveMedia(request, route.mediaId);
|
|
405
|
+
break;
|
|
406
|
+
case "update-folder":
|
|
407
|
+
response = await handleUpdateFolder(request, route.folderId);
|
|
408
|
+
break;
|
|
409
|
+
default:
|
|
410
|
+
throwUnmatchedRoute(resolvedParams.path, "PATCH");
|
|
411
|
+
}
|
|
412
|
+
return withTimezoneFormatting(response);
|
|
413
|
+
}
|
|
414
|
+
),
|
|
415
|
+
DELETE: withErrorHandler(
|
|
416
|
+
async (request, ctx) => {
|
|
417
|
+
const resolvedParams = await ctx.params;
|
|
418
|
+
const route = parseMediaRoute(resolvedParams.path, "DELETE");
|
|
419
|
+
let response;
|
|
420
|
+
switch (route.type) {
|
|
421
|
+
case "delete-media":
|
|
422
|
+
response = await handleDeleteMedia(route.mediaId);
|
|
423
|
+
break;
|
|
424
|
+
case "delete-folder":
|
|
425
|
+
response = await handleDeleteFolder(request, route.folderId);
|
|
426
|
+
break;
|
|
427
|
+
default:
|
|
428
|
+
throwUnmatchedRoute(resolvedParams.path, "DELETE");
|
|
429
|
+
}
|
|
430
|
+
return withTimezoneFormatting(response);
|
|
431
|
+
}
|
|
432
|
+
)
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
export {
|
|
436
|
+
createMediaHandlers
|
|
437
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media API Route Handlers for Next.js
|
|
3
|
+
*
|
|
4
|
+
* These route handlers can be re-exported in your Next.js application to provide
|
|
5
|
+
* media management endpoints at /api/media.
|
|
6
|
+
*
|
|
7
|
+
* **IMPORTANT:** For storage plugins (S3, Vercel Blob, etc.) to work, you must
|
|
8
|
+
* initialize Nextly with your config before these routes are called. The recommended
|
|
9
|
+
* approach is to use Next.js instrumentation:
|
|
10
|
+
*
|
|
11
|
+
* ```typescript
|
|
12
|
+
* // src/instrumentation.ts
|
|
13
|
+
* export async function register() {
|
|
14
|
+
* if (process.env.NEXT_RUNTIME === "nodejs") {
|
|
15
|
+
* const { getNextly } = await import("nextly");
|
|
16
|
+
* const nextlyConfig = (await import("./nextly.config")).default;
|
|
17
|
+
* await getNextly({ config: nextlyConfig });
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* // In your Next.js app: app/api/media/route.ts
|
|
25
|
+
* export { GET, POST } from 'nextly/api/media';
|
|
26
|
+
*
|
|
27
|
+
* // In your Next.js app: app/api/media/[id]/route.ts
|
|
28
|
+
* export { getMediaById as GET, updateMedia as PATCH, deleteMedia as DELETE } from 'nextly/api/media';
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @module api/media
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* GET handler for listing media with pagination, search, and filters.
|
|
35
|
+
*
|
|
36
|
+
* Query Parameters:
|
|
37
|
+
* - page: Page number (default: 1)
|
|
38
|
+
* - limit: Items per page (default: 24)
|
|
39
|
+
* - search: Search query for filename, altText
|
|
40
|
+
* - type: Filter by media type (image, video, audio, document, other)
|
|
41
|
+
* - folderId: Filter by folder ("root" for root-level media)
|
|
42
|
+
* - sortBy: Sort field (uploadedAt, filename, size)
|
|
43
|
+
* - sortOrder: Sort direction (asc, desc)
|
|
44
|
+
*
|
|
45
|
+
* Response: `{ items: Media[], meta: { total, page, limit, totalPages,
|
|
46
|
+
* hasNext, hasPrev } }` per the canonical respondList shape.
|
|
47
|
+
*/
|
|
48
|
+
declare const GET: (request: Request) => Promise<Response>;
|
|
49
|
+
/**
|
|
50
|
+
* POST handler for uploading media files.
|
|
51
|
+
*
|
|
52
|
+
* Accepts multipart/form-data with a 'file' field and metadata.
|
|
53
|
+
* Automatically processes images (thumbnails, dimensions).
|
|
54
|
+
*
|
|
55
|
+
* Form Data:
|
|
56
|
+
* - file: File to upload (required)
|
|
57
|
+
* - uploadedBy: User ID (required)
|
|
58
|
+
* - folderId: Optional folder to upload into.
|
|
59
|
+
*
|
|
60
|
+
* Response: `{ message, item: Media }` (status 201).
|
|
61
|
+
*/
|
|
62
|
+
declare const POST: (request: Request) => Promise<Response>;
|
|
63
|
+
/**
|
|
64
|
+
* GET handler for fetching a single media item by ID.
|
|
65
|
+
*
|
|
66
|
+
* Response: bare `Media` (respondDoc). 404 surface emits canonical
|
|
67
|
+
* `application/problem+json` with `code: "NOT_FOUND"`.
|
|
68
|
+
*/
|
|
69
|
+
declare function getMediaById(request: Request, ctx: {
|
|
70
|
+
params: Promise<{
|
|
71
|
+
id: string;
|
|
72
|
+
}>;
|
|
73
|
+
}): Promise<Response>;
|
|
74
|
+
/**
|
|
75
|
+
* PATCH handler for updating media metadata.
|
|
76
|
+
*
|
|
77
|
+
* Accepts JSON with metadata updates (altText, caption, tags).
|
|
78
|
+
*
|
|
79
|
+
* Request Body:
|
|
80
|
+
* - altText: Optional alt text
|
|
81
|
+
* - caption: Optional caption
|
|
82
|
+
* - tags: Optional tags array
|
|
83
|
+
*
|
|
84
|
+
* Response: `{ message, item: Media }`.
|
|
85
|
+
*/
|
|
86
|
+
declare function updateMedia(request: Request, ctx: {
|
|
87
|
+
params: Promise<{
|
|
88
|
+
id: string;
|
|
89
|
+
}>;
|
|
90
|
+
}): Promise<Response>;
|
|
91
|
+
/**
|
|
92
|
+
* DELETE handler for deleting media files.
|
|
93
|
+
*
|
|
94
|
+
* Removes media from both storage and database.
|
|
95
|
+
*
|
|
96
|
+
* Response: `{ message, id }` (respondAction; service returns void).
|
|
97
|
+
*/
|
|
98
|
+
declare function deleteMedia(request: Request, ctx: {
|
|
99
|
+
params: Promise<{
|
|
100
|
+
id: string;
|
|
101
|
+
}>;
|
|
102
|
+
}): Promise<Response>;
|
|
103
|
+
/**
|
|
104
|
+
* PATCH handler for moving media to a folder
|
|
105
|
+
*
|
|
106
|
+
* Path: /api/media/[id]/move
|
|
107
|
+
* Body: { folderId: string | null }
|
|
108
|
+
*
|
|
109
|
+
* Response: `{ message, id, folderId }` (respondAction; service returns void).
|
|
110
|
+
*/
|
|
111
|
+
declare function moveMediaToFolder(request: Request, ctx: {
|
|
112
|
+
params: Promise<{
|
|
113
|
+
id: string;
|
|
114
|
+
}>;
|
|
115
|
+
}): Promise<Response>;
|
|
116
|
+
|
|
117
|
+
export { GET, POST, deleteMedia, getMediaById, moveMediaToFolder, updateMedia };
|