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,239 @@
|
|
|
1
|
+
import { M as Media } from '../_dts-chunks/media.d-DjDOZo4B.d.ts';
|
|
2
|
+
import { O as OnErrorHook } from '../_dts-chunks/on-error.d-CHIKWNxd.d.ts';
|
|
3
|
+
import { PublicData } from '../errors/index.d.ts';
|
|
4
|
+
import 'zod';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Media Upload Server Action
|
|
8
|
+
*
|
|
9
|
+
* Next.js 16 Server Action for uploading media files.
|
|
10
|
+
* Provides a simpler alternative to API routes for small files (<5MB).
|
|
11
|
+
*
|
|
12
|
+
* ## Features
|
|
13
|
+
*
|
|
14
|
+
* - Server-side file upload (no client-side FormData serialization)
|
|
15
|
+
* - Automatic cache revalidation (revalidatePath)
|
|
16
|
+
* - Type-safe with Zod validation
|
|
17
|
+
* - Authentication support (when configured)
|
|
18
|
+
* - Error handling with user-friendly messages
|
|
19
|
+
*
|
|
20
|
+
* ## Usage
|
|
21
|
+
*
|
|
22
|
+
* ### In Consumer's Next.js App
|
|
23
|
+
*
|
|
24
|
+
* ```typescript
|
|
25
|
+
* // app/actions/media.ts
|
|
26
|
+
* 'use server';
|
|
27
|
+
*
|
|
28
|
+
* import { uploadMediaAction } from 'nextly/actions/upload-media';
|
|
29
|
+
* import { getUserId } from './auth'; // Your auth implementation
|
|
30
|
+
*
|
|
31
|
+
* export async function uploadMedia(formData: FormData) {
|
|
32
|
+
* const userId = await getUserId();
|
|
33
|
+
* return uploadMediaAction(formData, { uploadedBy: userId });
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* ### In Client Component
|
|
38
|
+
*
|
|
39
|
+
* ```tsx
|
|
40
|
+
* 'use client';
|
|
41
|
+
*
|
|
42
|
+
* import { uploadMedia } from './actions/media';
|
|
43
|
+
*
|
|
44
|
+
* function UploadForm() {
|
|
45
|
+
* async function handleSubmit(formData: FormData) {
|
|
46
|
+
* const result = await uploadMedia(formData);
|
|
47
|
+
* if (result.success) {
|
|
48
|
+
* toast.success('Uploaded!');
|
|
49
|
+
* } else {
|
|
50
|
+
* toast.error(result.error);
|
|
51
|
+
* }
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* return <form action={handleSubmit}>...</form>;
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* ## Authentication
|
|
59
|
+
*
|
|
60
|
+
* This action is **auth-agnostic** by design. The `uploadedBy` parameter
|
|
61
|
+
* must be provided by the consumer's authentication implementation.
|
|
62
|
+
*
|
|
63
|
+
* Examples:
|
|
64
|
+
* - Nextly: `const result = await getSession(request, secret); uploadedBy: result.user?.id`
|
|
65
|
+
* - Clerk: `const { userId } = auth(); uploadedBy: userId`
|
|
66
|
+
* - Custom: `const user = await getUser(); uploadedBy: user.id`
|
|
67
|
+
*
|
|
68
|
+
* ## Limitations
|
|
69
|
+
*
|
|
70
|
+
* - **No upload progress**: Server Actions don't support progress events
|
|
71
|
+
* - **Recommended for small files only** (<5MB)
|
|
72
|
+
* - **For large files**: Use API route with XMLHttpRequest
|
|
73
|
+
*
|
|
74
|
+
* @see packages/db/src/api/media.ts - API route with progress support
|
|
75
|
+
* @see MEDIA-MANAGEMENT-EXTENDED-PLAN.md - Phase 6 implementation details
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Server Action options
|
|
80
|
+
*/
|
|
81
|
+
interface UploadMediaActionOptions {
|
|
82
|
+
/**
|
|
83
|
+
* User ID who is uploading the file (required)
|
|
84
|
+
* Must be obtained from your auth system
|
|
85
|
+
*/
|
|
86
|
+
uploadedBy: string;
|
|
87
|
+
/**
|
|
88
|
+
* Path to revalidate after successful upload
|
|
89
|
+
* @default '/admin/media'
|
|
90
|
+
*/
|
|
91
|
+
revalidatePath?: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Server Action result
|
|
95
|
+
*/
|
|
96
|
+
interface UploadMediaActionResult {
|
|
97
|
+
success: boolean;
|
|
98
|
+
data?: Media;
|
|
99
|
+
error?: string;
|
|
100
|
+
statusCode?: number;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Upload media file via Server Action
|
|
104
|
+
*
|
|
105
|
+
* Uploads a file to storage and creates a database record.
|
|
106
|
+
* Automatically generates thumbnails for images.
|
|
107
|
+
*
|
|
108
|
+
* @param formData - FormData containing the file
|
|
109
|
+
* @param options - Upload options (uploadedBy, revalidatePath)
|
|
110
|
+
* @returns Upload result with media data or error
|
|
111
|
+
*
|
|
112
|
+
* @example Basic usage
|
|
113
|
+
* ```typescript
|
|
114
|
+
* 'use server';
|
|
115
|
+
*
|
|
116
|
+
* export async function uploadFile(formData: FormData) {
|
|
117
|
+
* const userId = await getUserId(); // Your auth function
|
|
118
|
+
* return uploadMediaAction(formData, { uploadedBy: userId });
|
|
119
|
+
* }
|
|
120
|
+
* ```
|
|
121
|
+
*
|
|
122
|
+
* @example With custom revalidation path
|
|
123
|
+
* ```typescript
|
|
124
|
+
* return uploadMediaAction(formData, {
|
|
125
|
+
* uploadedBy: userId,
|
|
126
|
+
* revalidatePath: '/dashboard/gallery',
|
|
127
|
+
* });
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
declare function uploadMediaAction(formData: FormData, options: UploadMediaActionOptions): Promise<UploadMediaActionResult>;
|
|
131
|
+
/**
|
|
132
|
+
* Delete media file via Server Action
|
|
133
|
+
*
|
|
134
|
+
* Deletes a file from storage and removes the database record.
|
|
135
|
+
*
|
|
136
|
+
* @param mediaId - ID of media to delete
|
|
137
|
+
* @param options - Options (revalidatePath)
|
|
138
|
+
* @returns Deletion result
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```typescript
|
|
142
|
+
* 'use server';
|
|
143
|
+
*
|
|
144
|
+
* export async function deleteFile(mediaId: string) {
|
|
145
|
+
* return deleteMediaAction(mediaId);
|
|
146
|
+
* }
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
declare function deleteMediaAction(mediaId: string, options?: {
|
|
150
|
+
revalidatePath?: string;
|
|
151
|
+
}): Promise<{
|
|
152
|
+
success: boolean;
|
|
153
|
+
error?: string;
|
|
154
|
+
statusCode?: number;
|
|
155
|
+
}>;
|
|
156
|
+
/**
|
|
157
|
+
* Update media metadata via Server Action
|
|
158
|
+
*
|
|
159
|
+
* Updates altText, caption, tags, or other metadata fields.
|
|
160
|
+
*
|
|
161
|
+
* @param mediaId - ID of media to update
|
|
162
|
+
* @param updates - Metadata updates
|
|
163
|
+
* @param options - Options (revalidatePath)
|
|
164
|
+
* @returns Update result
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```typescript
|
|
168
|
+
* 'use server';
|
|
169
|
+
*
|
|
170
|
+
* export async function updateFile(
|
|
171
|
+
* mediaId: string,
|
|
172
|
+
* updates: { altText?: string; caption?: string; tags?: string[] }
|
|
173
|
+
* ) {
|
|
174
|
+
* return updateMediaAction(mediaId, updates);
|
|
175
|
+
* }
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
declare function updateMediaAction(mediaId: string, updates: {
|
|
179
|
+
filename?: string;
|
|
180
|
+
altText?: string;
|
|
181
|
+
caption?: string;
|
|
182
|
+
tags?: string[];
|
|
183
|
+
}, options?: {
|
|
184
|
+
revalidatePath?: string;
|
|
185
|
+
}): Promise<{
|
|
186
|
+
success: boolean;
|
|
187
|
+
data?: Media;
|
|
188
|
+
error?: string;
|
|
189
|
+
statusCode?: number;
|
|
190
|
+
}>;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Wire shape returned by Server Actions wrapped with `withAction`.
|
|
194
|
+
*
|
|
195
|
+
* The `error` block matches the canonical HTTP wire-format error block
|
|
196
|
+
* (modulo the discriminator), so developers learn one shape regardless of
|
|
197
|
+
* whether they consume a Route Handler or a Server Action.
|
|
198
|
+
*/
|
|
199
|
+
type ActionError = {
|
|
200
|
+
code: string;
|
|
201
|
+
message: string;
|
|
202
|
+
messageKey?: string;
|
|
203
|
+
data?: PublicData;
|
|
204
|
+
requestId: string;
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* Result of a Server Action: either `{ ok: true, data }` on success or
|
|
208
|
+
* `{ ok: false, error }` on failure. The `ok` discriminator narrows
|
|
209
|
+
* cleanly via `if (result.ok)` so consumers get type-safe access to either
|
|
210
|
+
* branch.
|
|
211
|
+
*/
|
|
212
|
+
type ActionResult<T> = {
|
|
213
|
+
ok: true;
|
|
214
|
+
data: T;
|
|
215
|
+
} | {
|
|
216
|
+
ok: false;
|
|
217
|
+
error: ActionError;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
type WithActionOptions = {
|
|
221
|
+
/** Per-call observability hook. Fired before the global hook. */
|
|
222
|
+
onError?: OnErrorHook;
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* Server Action boundary wrapper. Mirrors `withErrorHandler` but returns a
|
|
226
|
+
* typed `ActionResult<T>` instead of a `Response`, working around Next.js's
|
|
227
|
+
* production error-digesting (which strips thrown error messages).
|
|
228
|
+
*
|
|
229
|
+
* Generic over `TArgs` so it transparently supports both direct-call
|
|
230
|
+
* actions (`(id: string)`) and form-binding actions
|
|
231
|
+
* (`(prevState, formData)`) consumed by `useActionState`.
|
|
232
|
+
*
|
|
233
|
+
* Sentinel errors (`redirect`, `notFound`, dynamic-API bailouts) are passed
|
|
234
|
+
* through `unstable_rethrow` first so navigation works as expected.
|
|
235
|
+
*/
|
|
236
|
+
declare function withAction<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>, options?: WithActionOptions): (...args: TArgs) => Promise<ActionResult<TResult>>;
|
|
237
|
+
|
|
238
|
+
export { deleteMediaAction, updateMediaAction, uploadMediaAction, withAction };
|
|
239
|
+
export type { ActionError, ActionResult, UploadMediaActionOptions, UploadMediaActionResult };
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateRequestId
|
|
3
|
+
} from "../chunk-67GXH6PR.mjs";
|
|
4
|
+
import {
|
|
5
|
+
getGlobalOnError
|
|
6
|
+
} from "../chunk-2TFX4ND3.mjs";
|
|
7
|
+
import {
|
|
8
|
+
getNextlyLogger
|
|
9
|
+
} from "../chunk-W4MGXIRR.mjs";
|
|
10
|
+
import {
|
|
11
|
+
ServiceContainer,
|
|
12
|
+
UploadMediaInputSchema
|
|
13
|
+
} from "../chunk-NSEFNNU4.mjs";
|
|
14
|
+
import "../chunk-3FA7FKAV.mjs";
|
|
15
|
+
import "../chunk-AGJ6F2T3.mjs";
|
|
16
|
+
import "../chunk-KIMNCZGV.mjs";
|
|
17
|
+
import "../chunk-AK6Z23OX.mjs";
|
|
18
|
+
import "../chunk-INV7QKLG.mjs";
|
|
19
|
+
import "../chunk-GZ6DCQKC.mjs";
|
|
20
|
+
import "../chunk-SBACDPNX.mjs";
|
|
21
|
+
import "../chunk-RJLLGGPG.mjs";
|
|
22
|
+
import "../chunk-V4EQTOA4.mjs";
|
|
23
|
+
import "../chunk-I4JMR3UR.mjs";
|
|
24
|
+
import "../chunk-XZKLBMN6.mjs";
|
|
25
|
+
import "../chunk-IZWPRDC3.mjs";
|
|
26
|
+
import "../chunk-DNNG377Z.mjs";
|
|
27
|
+
import "../chunk-5HMZ644B.mjs";
|
|
28
|
+
import "../chunk-W5KKPZT5.mjs";
|
|
29
|
+
import "../chunk-56WO4WX7.mjs";
|
|
30
|
+
import "../chunk-2W3DVD7S.mjs";
|
|
31
|
+
import "../chunk-TS7GHTG2.mjs";
|
|
32
|
+
import "../chunk-H26B4FYG.mjs";
|
|
33
|
+
import "../chunk-DP3G27G5.mjs";
|
|
34
|
+
import "../chunk-DV6WVX2Q.mjs";
|
|
35
|
+
import "../chunk-UJ2IMJ4W.mjs";
|
|
36
|
+
import "../chunk-EGXBZCGC.mjs";
|
|
37
|
+
import "../chunk-G2AA4QLC.mjs";
|
|
38
|
+
import "../chunk-4MJLT6PZ.mjs";
|
|
39
|
+
import "../chunk-IUDOC7N7.mjs";
|
|
40
|
+
import "../chunk-LAZXX4HR.mjs";
|
|
41
|
+
import {
|
|
42
|
+
container
|
|
43
|
+
} from "../chunk-D5HQBNUB.mjs";
|
|
44
|
+
import {
|
|
45
|
+
NextlyError,
|
|
46
|
+
isDbError
|
|
47
|
+
} from "../chunk-NRUWQ5Z7.mjs";
|
|
48
|
+
import "../chunk-VTJADRO3.mjs";
|
|
49
|
+
import "../chunk-5APFUGAD.mjs";
|
|
50
|
+
import "../chunk-7P6ASYW6.mjs";
|
|
51
|
+
|
|
52
|
+
// src/actions/upload-media.ts
|
|
53
|
+
import { createRequire } from "module";
|
|
54
|
+
var cachedRevalidatePath = null;
|
|
55
|
+
function getRevalidatePath() {
|
|
56
|
+
if (cachedRevalidatePath) return cachedRevalidatePath;
|
|
57
|
+
const require2 = createRequire(import.meta.url);
|
|
58
|
+
const mod = require2("next/cache");
|
|
59
|
+
cachedRevalidatePath = mod.revalidatePath;
|
|
60
|
+
return cachedRevalidatePath;
|
|
61
|
+
}
|
|
62
|
+
function getServices() {
|
|
63
|
+
const adapter = container.get("adapter");
|
|
64
|
+
return new ServiceContainer(adapter);
|
|
65
|
+
}
|
|
66
|
+
async function uploadMediaAction(formData, options) {
|
|
67
|
+
try {
|
|
68
|
+
const file = formData.get("file");
|
|
69
|
+
if (!file) {
|
|
70
|
+
return {
|
|
71
|
+
success: false,
|
|
72
|
+
error: "No file provided",
|
|
73
|
+
statusCode: 400
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (!(file instanceof File)) {
|
|
77
|
+
return {
|
|
78
|
+
success: false,
|
|
79
|
+
error: "Invalid file",
|
|
80
|
+
statusCode: 400
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
84
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
85
|
+
const parseResult = UploadMediaInputSchema.safeParse({
|
|
86
|
+
file: buffer,
|
|
87
|
+
filename: file.name,
|
|
88
|
+
mimeType: file.type,
|
|
89
|
+
size: file.size,
|
|
90
|
+
uploadedBy: options.uploadedBy
|
|
91
|
+
});
|
|
92
|
+
if (!parseResult.success) {
|
|
93
|
+
const errors = parseResult.error.issues;
|
|
94
|
+
const firstError = errors[0];
|
|
95
|
+
return {
|
|
96
|
+
success: false,
|
|
97
|
+
error: firstError?.message || "Invalid file data",
|
|
98
|
+
statusCode: 400
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const services = getServices();
|
|
102
|
+
const result = await services.media.uploadMedia(parseResult.data);
|
|
103
|
+
if (result.success && result.data) {
|
|
104
|
+
const pathToRevalidate = options.revalidatePath || "/admin/media";
|
|
105
|
+
getRevalidatePath()(pathToRevalidate);
|
|
106
|
+
return {
|
|
107
|
+
success: true,
|
|
108
|
+
data: result.data,
|
|
109
|
+
statusCode: result.statusCode
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
success: false,
|
|
114
|
+
error: result.message || "Upload failed",
|
|
115
|
+
statusCode: result.statusCode
|
|
116
|
+
};
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error("[uploadMediaAction] Unexpected error:", error);
|
|
119
|
+
return {
|
|
120
|
+
success: false,
|
|
121
|
+
error: error instanceof Error ? error.message : "An unexpected error occurred",
|
|
122
|
+
statusCode: 500
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function deleteMediaAction(mediaId, options) {
|
|
127
|
+
try {
|
|
128
|
+
if (!mediaId || typeof mediaId !== "string") {
|
|
129
|
+
return {
|
|
130
|
+
success: false,
|
|
131
|
+
error: "Invalid media ID",
|
|
132
|
+
statusCode: 400
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const services = getServices();
|
|
136
|
+
const result = await services.media.deleteMedia(mediaId);
|
|
137
|
+
if (result.success) {
|
|
138
|
+
const pathToRevalidate = options?.revalidatePath || "/admin/media";
|
|
139
|
+
getRevalidatePath()(pathToRevalidate);
|
|
140
|
+
return {
|
|
141
|
+
success: true,
|
|
142
|
+
statusCode: result.statusCode
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
success: false,
|
|
147
|
+
error: result.message || "Delete failed",
|
|
148
|
+
statusCode: result.statusCode
|
|
149
|
+
};
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.error("[deleteMediaAction] Unexpected error:", error);
|
|
152
|
+
return {
|
|
153
|
+
success: false,
|
|
154
|
+
error: error instanceof Error ? error.message : "An unexpected error occurred",
|
|
155
|
+
statusCode: 500
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async function updateMediaAction(mediaId, updates, options) {
|
|
160
|
+
try {
|
|
161
|
+
if (!mediaId || typeof mediaId !== "string") {
|
|
162
|
+
return {
|
|
163
|
+
success: false,
|
|
164
|
+
error: "Invalid media ID",
|
|
165
|
+
statusCode: 400
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const services = getServices();
|
|
169
|
+
const result = await services.media.updateMedia(mediaId, updates);
|
|
170
|
+
if (result.success && result.data) {
|
|
171
|
+
const pathToRevalidate = options?.revalidatePath || "/admin/media";
|
|
172
|
+
getRevalidatePath()(pathToRevalidate);
|
|
173
|
+
return {
|
|
174
|
+
success: true,
|
|
175
|
+
data: result.data,
|
|
176
|
+
statusCode: result.statusCode
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
success: false,
|
|
181
|
+
error: result.message || "Update failed",
|
|
182
|
+
statusCode: result.statusCode
|
|
183
|
+
};
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.error("[updateMediaAction] Unexpected error:", error);
|
|
186
|
+
return {
|
|
187
|
+
success: false,
|
|
188
|
+
error: error instanceof Error ? error.message : "An unexpected error occurred",
|
|
189
|
+
statusCode: 500
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/actions/with-action.ts
|
|
195
|
+
import { createRequire as createRequire2 } from "module";
|
|
196
|
+
var cachedHeaders = null;
|
|
197
|
+
var cachedUnstableRethrow = null;
|
|
198
|
+
function getHeaders() {
|
|
199
|
+
if (cachedHeaders) return cachedHeaders;
|
|
200
|
+
const require2 = createRequire2(import.meta.url);
|
|
201
|
+
const mod = require2("next/headers");
|
|
202
|
+
cachedHeaders = mod.headers;
|
|
203
|
+
return cachedHeaders;
|
|
204
|
+
}
|
|
205
|
+
function getUnstableRethrow() {
|
|
206
|
+
if (cachedUnstableRethrow) return cachedUnstableRethrow;
|
|
207
|
+
const require2 = createRequire2(import.meta.url);
|
|
208
|
+
const mod = require2("next/navigation");
|
|
209
|
+
cachedUnstableRethrow = mod.unstable_rethrow;
|
|
210
|
+
return cachedUnstableRethrow;
|
|
211
|
+
}
|
|
212
|
+
async function readOrGenerateRequestIdFromHeaders() {
|
|
213
|
+
try {
|
|
214
|
+
const headers = getHeaders();
|
|
215
|
+
const h = await headers();
|
|
216
|
+
return h.get("x-request-id") ?? h.get("x-vercel-id") ?? h.get("cf-ray") ?? generateRequestId();
|
|
217
|
+
} catch {
|
|
218
|
+
return generateRequestId();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function withAction(fn, options) {
|
|
222
|
+
return async (...args) => {
|
|
223
|
+
const requestId = await readOrGenerateRequestIdFromHeaders();
|
|
224
|
+
try {
|
|
225
|
+
const data = await fn(...args);
|
|
226
|
+
return { ok: true, data };
|
|
227
|
+
} catch (err) {
|
|
228
|
+
getUnstableRethrow()(err);
|
|
229
|
+
let nextlyErr;
|
|
230
|
+
if (NextlyError.is(err)) {
|
|
231
|
+
nextlyErr = err;
|
|
232
|
+
} else if (isDbError(err)) {
|
|
233
|
+
getNextlyLogger().warn({
|
|
234
|
+
kind: "stray-db-error-converted",
|
|
235
|
+
requestId,
|
|
236
|
+
dbKind: err.kind
|
|
237
|
+
});
|
|
238
|
+
nextlyErr = NextlyError.fromDatabaseError(err);
|
|
239
|
+
} else {
|
|
240
|
+
nextlyErr = NextlyError.internal({ cause: err });
|
|
241
|
+
}
|
|
242
|
+
getNextlyLogger().error({
|
|
243
|
+
kind: "server-action-error",
|
|
244
|
+
...nextlyErr.toLogJSON(requestId)
|
|
245
|
+
});
|
|
246
|
+
const ctx = { kind: "server-action", requestId };
|
|
247
|
+
if (options?.onError) {
|
|
248
|
+
try {
|
|
249
|
+
await options.onError(nextlyErr, ctx);
|
|
250
|
+
} catch (hookErr) {
|
|
251
|
+
getNextlyLogger().warn({
|
|
252
|
+
kind: "onError-hook-failed",
|
|
253
|
+
layer: "per-call",
|
|
254
|
+
requestId,
|
|
255
|
+
err: String(hookErr)
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const globalHook = getGlobalOnError();
|
|
260
|
+
if (globalHook) {
|
|
261
|
+
try {
|
|
262
|
+
await globalHook(nextlyErr, ctx);
|
|
263
|
+
} catch (hookErr) {
|
|
264
|
+
getNextlyLogger().warn({
|
|
265
|
+
kind: "onError-hook-failed",
|
|
266
|
+
layer: "global",
|
|
267
|
+
requestId,
|
|
268
|
+
err: String(hookErr)
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return { ok: false, error: nextlyErr.toResponseJSON(requestId) };
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
export {
|
|
277
|
+
deleteMediaAction,
|
|
278
|
+
updateMediaAction,
|
|
279
|
+
uploadMediaAction,
|
|
280
|
+
withAction
|
|
281
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readAccessTokenCookie,
|
|
3
|
+
verifyAccessToken
|
|
4
|
+
} from "../chunk-2ZFKXPQM.mjs";
|
|
5
|
+
import {
|
|
6
|
+
withErrorHandler
|
|
7
|
+
} from "../chunk-TO5AFLVQ.mjs";
|
|
8
|
+
import {
|
|
9
|
+
getCachedNextly
|
|
10
|
+
} from "../chunk-P7NH2OSC.mjs";
|
|
11
|
+
import "../chunk-2OALJTK6.mjs";
|
|
12
|
+
import "../chunk-VJ66NCL4.mjs";
|
|
13
|
+
import "../chunk-R6JJQHFC.mjs";
|
|
14
|
+
import "../chunk-X23WKS3Z.mjs";
|
|
15
|
+
import "../chunk-X7TXCYYN.mjs";
|
|
16
|
+
import "../chunk-YZNBLFIW.mjs";
|
|
17
|
+
import "../chunk-FQULBZ53.mjs";
|
|
18
|
+
import "../chunk-67GXH6PR.mjs";
|
|
19
|
+
import "../chunk-2TFX4ND3.mjs";
|
|
20
|
+
import "../chunk-W4MGXIRR.mjs";
|
|
21
|
+
import "../chunk-NSEFNNU4.mjs";
|
|
22
|
+
import "../chunk-3FA7FKAV.mjs";
|
|
23
|
+
import "../chunk-AGJ6F2T3.mjs";
|
|
24
|
+
import "../chunk-KIMNCZGV.mjs";
|
|
25
|
+
import "../chunk-AK6Z23OX.mjs";
|
|
26
|
+
import "../chunk-INV7QKLG.mjs";
|
|
27
|
+
import "../chunk-GZ6DCQKC.mjs";
|
|
28
|
+
import "../chunk-SBACDPNX.mjs";
|
|
29
|
+
import "../chunk-RJLLGGPG.mjs";
|
|
30
|
+
import "../chunk-V4EQTOA4.mjs";
|
|
31
|
+
import "../chunk-I4JMR3UR.mjs";
|
|
32
|
+
import "../chunk-XZKLBMN6.mjs";
|
|
33
|
+
import "../chunk-IZWPRDC3.mjs";
|
|
34
|
+
import "../chunk-DNNG377Z.mjs";
|
|
35
|
+
import "../chunk-5HMZ644B.mjs";
|
|
36
|
+
import "../chunk-W5KKPZT5.mjs";
|
|
37
|
+
import "../chunk-56WO4WX7.mjs";
|
|
38
|
+
import "../chunk-2W3DVD7S.mjs";
|
|
39
|
+
import {
|
|
40
|
+
getDialectTables
|
|
41
|
+
} from "../chunk-TS7GHTG2.mjs";
|
|
42
|
+
import "../chunk-H26B4FYG.mjs";
|
|
43
|
+
import "../chunk-DP3G27G5.mjs";
|
|
44
|
+
import "../chunk-DV6WVX2Q.mjs";
|
|
45
|
+
import {
|
|
46
|
+
env
|
|
47
|
+
} from "../chunk-UJ2IMJ4W.mjs";
|
|
48
|
+
import "../chunk-EGXBZCGC.mjs";
|
|
49
|
+
import "../chunk-G2AA4QLC.mjs";
|
|
50
|
+
import "../chunk-4MJLT6PZ.mjs";
|
|
51
|
+
import {
|
|
52
|
+
respondData
|
|
53
|
+
} from "../chunk-IUDOC7N7.mjs";
|
|
54
|
+
import "../chunk-LAZXX4HR.mjs";
|
|
55
|
+
import "../chunk-D5HQBNUB.mjs";
|
|
56
|
+
import {
|
|
57
|
+
NextlyError
|
|
58
|
+
} from "../chunk-NRUWQ5Z7.mjs";
|
|
59
|
+
import "../chunk-VTJADRO3.mjs";
|
|
60
|
+
import "../chunk-5APFUGAD.mjs";
|
|
61
|
+
import "../chunk-7P6ASYW6.mjs";
|
|
62
|
+
|
|
63
|
+
// src/api/auth-state.ts
|
|
64
|
+
import { eq } from "drizzle-orm";
|
|
65
|
+
var GET = withErrorHandler(async (request) => {
|
|
66
|
+
const secret = env.NEXTLY_SECRET ?? "";
|
|
67
|
+
if (!secret) {
|
|
68
|
+
throw NextlyError.authRequired({
|
|
69
|
+
logContext: { reason: "no-secret-configured" }
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const token = readAccessTokenCookie(request);
|
|
73
|
+
if (!token) {
|
|
74
|
+
throw NextlyError.authRequired({ logContext: { reason: "no_token" } });
|
|
75
|
+
}
|
|
76
|
+
const verifyResult = await verifyAccessToken(token, secret);
|
|
77
|
+
if (!verifyResult.valid) {
|
|
78
|
+
throw NextlyError.authRequired({
|
|
79
|
+
logContext: { reason: verifyResult.reason }
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const claims = verifyResult.payload;
|
|
83
|
+
const userId = claims.sub;
|
|
84
|
+
const issuedAt = typeof claims.iat === "number" ? new Date(claims.iat * 1e3) : /* @__PURE__ */ new Date(0);
|
|
85
|
+
const expiresAt = typeof claims.exp === "number" ? new Date(claims.exp * 1e3) : /* @__PURE__ */ new Date(0);
|
|
86
|
+
if (!userId) {
|
|
87
|
+
throw NextlyError.authRequired({
|
|
88
|
+
logContext: { reason: "claims-missing-sub" }
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const nextly = await getCachedNextly();
|
|
92
|
+
const db = nextly.adapter.getDrizzle();
|
|
93
|
+
const tables = getDialectTables();
|
|
94
|
+
const rows = await db.select({
|
|
95
|
+
id: tables.users.id,
|
|
96
|
+
email: tables.users.email,
|
|
97
|
+
name: tables.users.name,
|
|
98
|
+
emailVerified: tables.users.emailVerified,
|
|
99
|
+
isActive: tables.users.isActive,
|
|
100
|
+
lockedUntil: tables.users.lockedUntil
|
|
101
|
+
}).from(tables.users).where(eq(tables.users.id, userId)).limit(1);
|
|
102
|
+
const user = rows[0];
|
|
103
|
+
if (!user) {
|
|
104
|
+
throw NextlyError.authRequired({
|
|
105
|
+
logContext: { reason: "session-user-missing", userId }
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const now = /* @__PURE__ */ new Date();
|
|
109
|
+
const account = {
|
|
110
|
+
verified: Boolean(user.emailVerified),
|
|
111
|
+
locked: Boolean(user.lockedUntil && user.lockedUntil > now),
|
|
112
|
+
disabled: !user.isActive,
|
|
113
|
+
// The schema does not yet track these as first-class fields; expose
|
|
114
|
+
// safe defaults until they land. (Spec §19 follow-up.)
|
|
115
|
+
passwordResetRequired: false,
|
|
116
|
+
mustChangePasswordReason: null
|
|
117
|
+
};
|
|
118
|
+
const payload = {
|
|
119
|
+
authenticated: true,
|
|
120
|
+
user: { id: user.id, email: user.email, name: user.name },
|
|
121
|
+
account,
|
|
122
|
+
session: {
|
|
123
|
+
issuedAt: issuedAt.toISOString(),
|
|
124
|
+
expiresAt: expiresAt.toISOString()
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
return respondData(payload);
|
|
128
|
+
});
|
|
129
|
+
export {
|
|
130
|
+
GET
|
|
131
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collection Schema Detail API Route Handlers for Next.js
|
|
3
|
+
*
|
|
4
|
+
* These route handlers can be re-exported in your Next.js application to provide
|
|
5
|
+
* individual collection management endpoints at /api/collections/schema/[slug].
|
|
6
|
+
*
|
|
7
|
+
* Services are auto-initialized on first request using environment variables:
|
|
8
|
+
* - DB_DIALECT: Database dialect ("postgresql" | "mysql" | "sqlite")
|
|
9
|
+
* - DATABASE_URL: Database connection string
|
|
10
|
+
*
|
|
11
|
+
* Locked code-first collections surface as canonical FORBIDDEN: the registry
|
|
12
|
+
* throws `NextlyError.forbidden` with the `collection-locked` reason in
|
|
13
|
+
* `logContext`.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // In your Next.js app: app/api/collections/schema/[slug]/route.ts
|
|
18
|
+
* export { GET, PATCH, DELETE } from 'nextly/api/collections-schema-detail';
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @module api/collections-schema-detail
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Context object for dynamic route handlers.
|
|
25
|
+
* Next.js 15+ requires params to be a Promise.
|
|
26
|
+
*/
|
|
27
|
+
interface RouteContext {
|
|
28
|
+
params: Promise<{
|
|
29
|
+
slug: string;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* GET handler for retrieving a single collection by slug.
|
|
34
|
+
*
|
|
35
|
+
* Requires authentication and read permission for the collection. The
|
|
36
|
+
* registry throws `NOT_FOUND` if no collection matches the slug.
|
|
37
|
+
*/
|
|
38
|
+
declare const GET: (request: Request, context: RouteContext) => Promise<Response>;
|
|
39
|
+
/**
|
|
40
|
+
* PATCH handler for updating a collection.
|
|
41
|
+
*
|
|
42
|
+
* Requires `manage-settings` (or super-admin). The registry throws
|
|
43
|
+
* `NextlyError.forbidden` with `reason: "collection-locked"` if the
|
|
44
|
+
* collection is locked (code-first collections cannot be modified via API).
|
|
45
|
+
*/
|
|
46
|
+
declare const PATCH: (request: Request, context: RouteContext) => Promise<Response>;
|
|
47
|
+
/**
|
|
48
|
+
* DELETE handler for removing a collection.
|
|
49
|
+
*
|
|
50
|
+
* Requires `manage-settings` (or super-admin). The registry throws
|
|
51
|
+
* `NextlyError.forbidden` with `reason: "collection-locked-delete"` if the
|
|
52
|
+
* collection is locked (code-first collections cannot be deleted via API).
|
|
53
|
+
*/
|
|
54
|
+
declare const DELETE: (request: Request, context: RouteContext) => Promise<Response>;
|
|
55
|
+
|
|
56
|
+
export { DELETE, GET, PATCH };
|