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,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert a string secret to a Uint8Array key for jose.
|
|
3
|
+
* Uses the raw bytes of the secret (UTF-8 encoded).
|
|
4
|
+
*/
|
|
5
|
+
declare function secretToKey(secret: string): Uint8Array;
|
|
6
|
+
/**
|
|
7
|
+
* Sign a JWT access token.
|
|
8
|
+
*
|
|
9
|
+
* @param claims - The payload claims (from buildClaims)
|
|
10
|
+
* @param secret - The NEXTLY_SECRET string
|
|
11
|
+
* @param ttlSeconds - Token TTL in seconds (default 900 = 15 minutes)
|
|
12
|
+
* @returns Signed JWT string
|
|
13
|
+
*/
|
|
14
|
+
declare function signAccessToken(claims: Record<string, unknown>, secret: string, ttlSeconds?: number): Promise<string>;
|
|
15
|
+
|
|
16
|
+
interface NextlyJwtPayload {
|
|
17
|
+
sub: string;
|
|
18
|
+
iat: number;
|
|
19
|
+
exp: number;
|
|
20
|
+
jti: string;
|
|
21
|
+
email: string;
|
|
22
|
+
name: string;
|
|
23
|
+
image: string | null;
|
|
24
|
+
roleIds: string[];
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
interface BuildClaimsInput {
|
|
28
|
+
userId: string;
|
|
29
|
+
email: string;
|
|
30
|
+
name: string;
|
|
31
|
+
image: string | null;
|
|
32
|
+
roleIds: string[];
|
|
33
|
+
customFields?: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Build JWT claims from user data.
|
|
37
|
+
* Does NOT set iat/exp/jti -- those are set by the signing function.
|
|
38
|
+
*/
|
|
39
|
+
declare function buildClaims(input: BuildClaimsInput): Record<string, unknown>;
|
|
40
|
+
|
|
41
|
+
type VerifyResult = {
|
|
42
|
+
valid: true;
|
|
43
|
+
payload: NextlyJwtPayload;
|
|
44
|
+
} | {
|
|
45
|
+
valid: false;
|
|
46
|
+
reason: "expired" | "invalid" | "malformed";
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Verify and decode a JWT access token.
|
|
50
|
+
*
|
|
51
|
+
* @param token - The JWT string from the cookie
|
|
52
|
+
* @param secret - The NEXTLY_SECRET string
|
|
53
|
+
* @returns VerifyResult with payload on success or reason on failure
|
|
54
|
+
*/
|
|
55
|
+
declare function verifyAccessToken(token: string, secret: string): Promise<VerifyResult>;
|
|
56
|
+
|
|
57
|
+
interface SessionUser {
|
|
58
|
+
id: string;
|
|
59
|
+
email: string;
|
|
60
|
+
name: string;
|
|
61
|
+
image: string | null;
|
|
62
|
+
roleIds: string[];
|
|
63
|
+
/** Custom user fields from user_ext table */
|
|
64
|
+
[key: string]: unknown;
|
|
65
|
+
}
|
|
66
|
+
interface AuthContext {
|
|
67
|
+
userId: string;
|
|
68
|
+
userName: string;
|
|
69
|
+
userEmail: string;
|
|
70
|
+
permissions: Map<string, boolean>;
|
|
71
|
+
roles: string[];
|
|
72
|
+
authMethod: "session" | "api-key";
|
|
73
|
+
}
|
|
74
|
+
interface RefreshTokenRecord {
|
|
75
|
+
id: string;
|
|
76
|
+
userId: string;
|
|
77
|
+
tokenHash: string;
|
|
78
|
+
userAgent: string | null;
|
|
79
|
+
ipAddress: string | null;
|
|
80
|
+
expiresAt: Date;
|
|
81
|
+
createdAt: Date;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
type GetSessionResult = {
|
|
85
|
+
authenticated: true;
|
|
86
|
+
user: SessionUser;
|
|
87
|
+
} | {
|
|
88
|
+
authenticated: false;
|
|
89
|
+
reason: "no_token" | "expired" | "invalid";
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Extract and verify the session from a request.
|
|
93
|
+
* No database hit -- purely stateless JWT verification.
|
|
94
|
+
*/
|
|
95
|
+
declare function getSession(request: Request, secret: string): Promise<GetSessionResult>;
|
|
96
|
+
/**
|
|
97
|
+
* Check if a session user has a specific role.
|
|
98
|
+
*/
|
|
99
|
+
declare function hasRole(user: SessionUser, roleSlug: string): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Check if a session user has any of the specified roles.
|
|
102
|
+
*/
|
|
103
|
+
declare function hasAnyRole(user: SessionUser, roleSlugs: string[]): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Check if a session user has all of the specified roles.
|
|
106
|
+
*/
|
|
107
|
+
declare function hasAllRoles(user: SessionUser, roleSlugs: string[]): boolean;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Generate a new opaque refresh token (64 bytes, hex-encoded = 128 chars).
|
|
111
|
+
*/
|
|
112
|
+
declare function generateRefreshToken(): string;
|
|
113
|
+
/**
|
|
114
|
+
* Hash a refresh token using SHA-256 for database storage.
|
|
115
|
+
* We use SHA-256 (not bcrypt) because refresh tokens have high entropy (64 bytes).
|
|
116
|
+
*/
|
|
117
|
+
declare function hashRefreshToken(token: string): string;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Require a valid session. Returns the session user or throws
|
|
121
|
+
* `NextlyError.authRequired()`.
|
|
122
|
+
*/
|
|
123
|
+
declare function requireAuth(request: Request, secret: string): Promise<SessionUser>;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Require a specific role. Throws `NextlyError.forbidden()` if missing.
|
|
127
|
+
*
|
|
128
|
+
* Public message comes from the factory ("You don't have permission to
|
|
129
|
+
* perform this action."). The required role identity is recorded in
|
|
130
|
+
* logContext only; the wire response never includes it (spec §13.7).
|
|
131
|
+
*/
|
|
132
|
+
declare function requireRole(user: SessionUser, roleSlug: string): void;
|
|
133
|
+
/**
|
|
134
|
+
* Require any of the specified roles. Throws `NextlyError.forbidden()` if
|
|
135
|
+
* none match. The role list lives in logContext per spec §13.7;
|
|
136
|
+
* the wire response never includes it.
|
|
137
|
+
*/
|
|
138
|
+
declare function requireAnyRole(user: SessionUser, roleSlugs: string[]): void;
|
|
139
|
+
/**
|
|
140
|
+
* Require all of the specified roles. Throws `NextlyError.forbidden()` if
|
|
141
|
+
* any are missing. The role list lives in logContext per spec §13.7.
|
|
142
|
+
*/
|
|
143
|
+
declare function requireAllRoles(user: SessionUser, roleSlugs: string[]): void;
|
|
144
|
+
|
|
145
|
+
interface ErrorResponse {
|
|
146
|
+
success: false;
|
|
147
|
+
statusCode: number;
|
|
148
|
+
message: string;
|
|
149
|
+
error: string;
|
|
150
|
+
data: null;
|
|
151
|
+
headers?: Record<string, string>;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Create a JSON error response object.
|
|
155
|
+
*/
|
|
156
|
+
declare function createErrorResponse(statusCode: number, message: string, error: string): ErrorResponse;
|
|
157
|
+
/**
|
|
158
|
+
* Create a JSON Response from an ErrorResponse.
|
|
159
|
+
*/
|
|
160
|
+
declare function createJsonErrorResponse(errResp: ErrorResponse): Response;
|
|
161
|
+
/**
|
|
162
|
+
* Check if a value is an ErrorResponse.
|
|
163
|
+
*/
|
|
164
|
+
declare function isErrorResponse(value: unknown): value is ErrorResponse;
|
|
165
|
+
/**
|
|
166
|
+
* Check a single permission against an AuthContext.
|
|
167
|
+
* For session auth: delegates to RBAC service.
|
|
168
|
+
* For API key auth: checks pre-resolved permissions map.
|
|
169
|
+
*/
|
|
170
|
+
declare function checkPermission(context: AuthContext, action: string, resource: string, deps: {
|
|
171
|
+
checkUserPermission?: (userId: string, action: string, resource: string) => Promise<boolean>;
|
|
172
|
+
}): Promise<boolean>;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Attempt API key authentication from the Authorization header.
|
|
176
|
+
* Returns null if no Bearer token present (not an error -- caller should try session auth).
|
|
177
|
+
* Returns ErrorResponse if token is present but invalid/expired/rate-limited.
|
|
178
|
+
* Returns AuthContext on success.
|
|
179
|
+
*/
|
|
180
|
+
declare function authenticateApiKey(request: Request, deps: {
|
|
181
|
+
validateApiKey: (rawKey: string) => Promise<{
|
|
182
|
+
valid: boolean;
|
|
183
|
+
userId?: string;
|
|
184
|
+
permissions?: Map<string, boolean>;
|
|
185
|
+
roles?: string[];
|
|
186
|
+
error?: string;
|
|
187
|
+
retryAfter?: number;
|
|
188
|
+
}>;
|
|
189
|
+
}): Promise<AuthContext | ErrorResponse | null>;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Check if a user has the super-admin role.
|
|
193
|
+
*
|
|
194
|
+
* Resolves the user's full role set (direct + inherited) and checks
|
|
195
|
+
* if any role has the `super-admin` slug. Results are cached in-memory
|
|
196
|
+
* for 60 seconds.
|
|
197
|
+
*
|
|
198
|
+
* @param userId - The user ID to check
|
|
199
|
+
* @returns `true` if the user has the super-admin role
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```typescript
|
|
203
|
+
* if (await isSuperAdmin(userId)) {
|
|
204
|
+
* // Bypass all access checks
|
|
205
|
+
* }
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
declare function isSuperAdmin(userId: string): Promise<boolean>;
|
|
209
|
+
|
|
210
|
+
type CollectionOperation = "create" | "read" | "update" | "delete";
|
|
211
|
+
interface CollectionAccessDeps {
|
|
212
|
+
/** Check a user's permission via RBAC service */
|
|
213
|
+
checkUserPermission?: (userId: string, action: string, resource: string) => Promise<boolean>;
|
|
214
|
+
/** Evaluate code-defined access function from defineCollection */
|
|
215
|
+
evaluateCodeAccess?: (collectionSlug: string, operation: CollectionOperation, context: AuthContext) => Promise<boolean | null>;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Check if the auth context has access to a collection operation.
|
|
219
|
+
* Priority: super-admin bypass > code-defined access > database RBAC.
|
|
220
|
+
*/
|
|
221
|
+
declare function checkCollectionAccess(context: AuthContext, collectionSlug: string, operation: CollectionOperation, deps: CollectionAccessDeps): Promise<boolean>;
|
|
222
|
+
|
|
223
|
+
declare function hashPassword(plain: string, saltRounds?: number): Promise<string>;
|
|
224
|
+
declare function verifyPassword(plain: string, hash: string): Promise<boolean>;
|
|
225
|
+
type PasswordStrengthResult = {
|
|
226
|
+
ok: true;
|
|
227
|
+
errors?: undefined;
|
|
228
|
+
} | {
|
|
229
|
+
ok: false;
|
|
230
|
+
errors: string[];
|
|
231
|
+
};
|
|
232
|
+
declare function validatePasswordStrength(password: string): PasswordStrengthResult;
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Single entry point for security-sensitive event recording. Callers
|
|
236
|
+
* pass a structured event; the writer persists it to the `audit_log`
|
|
237
|
+
* table via the database adapter.
|
|
238
|
+
*
|
|
239
|
+
* Behaviour contract:
|
|
240
|
+
*
|
|
241
|
+
* - **Never throws.** Auth handlers must not fail-open or fail-closed
|
|
242
|
+
* because the audit table is unreachable. A DB failure logs a
|
|
243
|
+
* structured warning via `getNextlyLogger()` and the request
|
|
244
|
+
* continues.
|
|
245
|
+
* - **Append-only by application convention.** Operators are expected
|
|
246
|
+
* to revoke UPDATE / DELETE GRANTs on the table in production for
|
|
247
|
+
* stricter integrity. The writer never offers an update path.
|
|
248
|
+
* - **Metadata is opaque JSON.** The `metadata` field stays generic
|
|
249
|
+
* so we can extend coverage without a migration each time. Callers
|
|
250
|
+
* pass dialect-portable JSON-serialisable values only.
|
|
251
|
+
*
|
|
252
|
+
* Hash-chained tamper-evidence (each row signs (prev_hash, this_row))
|
|
253
|
+
* is intentionally deferred — under concurrent auth events the chain
|
|
254
|
+
* needs a lock-around-write that complicates the hot path. Operators
|
|
255
|
+
* who need cryptographic integrity right now should rely on DB-level
|
|
256
|
+
*
|
|
257
|
+
* @module domains/audit/audit-log-writer
|
|
258
|
+
* @since 1.0.0
|
|
259
|
+
*/
|
|
260
|
+
type AuditEventKind = "csrf-failed" | "login-failed" | "password-changed" | "role-assigned" | "role-revoked" | "user-deleted";
|
|
261
|
+
interface AuditEvent {
|
|
262
|
+
kind: AuditEventKind;
|
|
263
|
+
/** The user performing the action; null when unauthenticated (failed login, failed CSRF). */
|
|
264
|
+
actorUserId?: string | null;
|
|
265
|
+
/** The user being acted on; null when not account-scoped. */
|
|
266
|
+
targetUserId?: string | null;
|
|
267
|
+
ipAddress?: string | null;
|
|
268
|
+
userAgent?: string | null;
|
|
269
|
+
/** JSON-serialisable details. Goes into the dialect's JSON column. */
|
|
270
|
+
metadata?: Record<string, unknown>;
|
|
271
|
+
}
|
|
272
|
+
interface AuditLogWriter {
|
|
273
|
+
write(event: AuditEvent): Promise<void>;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Combined dependency interface for all auth handlers.
|
|
278
|
+
* Defined as a standalone interface (not multi-extends) to avoid TS2320 conflicts
|
|
279
|
+
* where the same method name has different return types across handler deps.
|
|
280
|
+
* The route handler builds this from the DI container services and config.
|
|
281
|
+
*/
|
|
282
|
+
interface AuthRouterDeps {
|
|
283
|
+
secret: string;
|
|
284
|
+
isProduction: boolean;
|
|
285
|
+
accessTokenTTL: number;
|
|
286
|
+
refreshTokenTTL: number;
|
|
287
|
+
maxLoginAttempts: number;
|
|
288
|
+
lockoutDurationSeconds: number;
|
|
289
|
+
loginStallTimeMs: number;
|
|
290
|
+
requireEmailVerification: boolean;
|
|
291
|
+
/**
|
|
292
|
+
* Spec §13.2 opt-in flag. When false (the spec default), email-conflict
|
|
293
|
+
* registrations silent-success with a generic message. When true, the
|
|
294
|
+
* handler returns 409 DUPLICATE so the user knows the email is in use
|
|
295
|
+
* (trades enumeration risk for UX).
|
|
296
|
+
*/
|
|
297
|
+
revealRegistrationConflict: boolean;
|
|
298
|
+
/**
|
|
299
|
+
* Dev-only auto-login config (config.admin.devAutoLogin). Hard-blocked
|
|
300
|
+
* at runtime when isProduction is true. When set, the session handler
|
|
301
|
+
* issues a real session for the named user on the first /admin visit
|
|
302
|
+
* if no valid session is present. See packages/nextly/src/auth/handlers/session.ts.
|
|
303
|
+
*/
|
|
304
|
+
devAutoLogin: false | {
|
|
305
|
+
email: string;
|
|
306
|
+
password?: string;
|
|
307
|
+
};
|
|
308
|
+
allowedOrigins: string[];
|
|
309
|
+
/**
|
|
310
|
+
* When true, client-IP resolution honors `X-Forwarded-For` (filtered
|
|
311
|
+
* through `trustedProxyIps`). When false (default), proxy headers are
|
|
312
|
+
* ignored.
|
|
313
|
+
*/
|
|
314
|
+
trustProxy: boolean;
|
|
315
|
+
/** CIDR list of proxy IPs (from TRUSTED_PROXY_IPS). */
|
|
316
|
+
trustedProxyIps: string[];
|
|
317
|
+
/**
|
|
318
|
+
* Per-IP rate limit on auth write endpoints. `requestsPerHour: 0`
|
|
319
|
+
* disables the envelope. Single shared bucket across login / register
|
|
320
|
+
* / forgot-password / reset-password per IP.
|
|
321
|
+
*/
|
|
322
|
+
authRateLimit: {
|
|
323
|
+
requestsPerHour: number;
|
|
324
|
+
windowMs: number;
|
|
325
|
+
};
|
|
326
|
+
/**
|
|
327
|
+
* Writer for security-sensitive auth events. Handlers call
|
|
328
|
+
* `auditLog.write(...)` on failed CSRF, failed login, password change,
|
|
329
|
+
* role mutation, and user delete. Writer is fail-safe; any DB error
|
|
330
|
+
* logs a warning and the request continues.
|
|
331
|
+
*/
|
|
332
|
+
auditLog: AuditLogWriter;
|
|
333
|
+
findUserByEmail: (email: string) => Promise<{
|
|
334
|
+
id: string;
|
|
335
|
+
email: string;
|
|
336
|
+
name: string;
|
|
337
|
+
image: string | null;
|
|
338
|
+
passwordHash: string;
|
|
339
|
+
emailVerified: Date | null;
|
|
340
|
+
isActive: boolean;
|
|
341
|
+
failedLoginAttempts: number;
|
|
342
|
+
lockedUntil: Date | null;
|
|
343
|
+
} | null>;
|
|
344
|
+
findUserById: (userId: string) => Promise<{
|
|
345
|
+
id: string;
|
|
346
|
+
email: string;
|
|
347
|
+
name: string;
|
|
348
|
+
image: string | null;
|
|
349
|
+
isActive: boolean;
|
|
350
|
+
} | null>;
|
|
351
|
+
incrementFailedAttempts: (userId: string) => Promise<void>;
|
|
352
|
+
lockAccount: (userId: string, lockedUntil: Date) => Promise<void>;
|
|
353
|
+
resetFailedAttempts: (userId: string) => Promise<void>;
|
|
354
|
+
fetchRoleIds: (userId: string) => Promise<string[]>;
|
|
355
|
+
fetchCustomFields: (userId: string) => Promise<Record<string, unknown>>;
|
|
356
|
+
storeRefreshToken: (record: {
|
|
357
|
+
id: string;
|
|
358
|
+
userId: string;
|
|
359
|
+
tokenHash: string;
|
|
360
|
+
userAgent: string | null;
|
|
361
|
+
ipAddress: string | null;
|
|
362
|
+
expiresAt: Date;
|
|
363
|
+
}) => Promise<void>;
|
|
364
|
+
findRefreshTokenByHash: (tokenHash: string) => Promise<{
|
|
365
|
+
id: string;
|
|
366
|
+
userId: string;
|
|
367
|
+
expiresAt: Date;
|
|
368
|
+
userAgent: string | null;
|
|
369
|
+
ipAddress: string | null;
|
|
370
|
+
} | null>;
|
|
371
|
+
deleteRefreshToken: (id: string) => Promise<void>;
|
|
372
|
+
deleteRefreshTokenByHash: (tokenHash: string) => Promise<void>;
|
|
373
|
+
deleteAllRefreshTokensForUser: (userId: string) => Promise<void>;
|
|
374
|
+
getUserCount: () => Promise<number>;
|
|
375
|
+
createSuperAdmin: (data: {
|
|
376
|
+
email: string;
|
|
377
|
+
name: string;
|
|
378
|
+
password: string;
|
|
379
|
+
}) => Promise<{
|
|
380
|
+
id: string;
|
|
381
|
+
email: string;
|
|
382
|
+
name: string;
|
|
383
|
+
}>;
|
|
384
|
+
seedPermissions: () => Promise<void>;
|
|
385
|
+
registerUser: (data: {
|
|
386
|
+
email: string;
|
|
387
|
+
password: string;
|
|
388
|
+
name: string;
|
|
389
|
+
}) => Promise<{
|
|
390
|
+
id: string;
|
|
391
|
+
email: string;
|
|
392
|
+
name: string | null;
|
|
393
|
+
}>;
|
|
394
|
+
generatePasswordResetToken: (email: string, redirectPath?: string) => Promise<{
|
|
395
|
+
token?: string;
|
|
396
|
+
}>;
|
|
397
|
+
resetPasswordWithToken: (token: string, newPassword: string) => Promise<{
|
|
398
|
+
email: string;
|
|
399
|
+
}>;
|
|
400
|
+
changePassword: (userId: string, currentPassword: string, newPassword: string) => Promise<{
|
|
401
|
+
success: boolean;
|
|
402
|
+
error?: string;
|
|
403
|
+
}>;
|
|
404
|
+
verifyEmail: (token: string) => Promise<{
|
|
405
|
+
success: boolean;
|
|
406
|
+
error?: string;
|
|
407
|
+
email?: string;
|
|
408
|
+
}>;
|
|
409
|
+
resendVerificationEmail: (email: string) => Promise<{
|
|
410
|
+
success: boolean;
|
|
411
|
+
error?: string;
|
|
412
|
+
}>;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Route an auth request to the appropriate handler.
|
|
416
|
+
* Returns null if the path doesn't match any auth route (caller handles 404).
|
|
417
|
+
*
|
|
418
|
+
* @param request - The incoming HTTP request
|
|
419
|
+
* @param authPath - The path after the auth prefix (e.g., "login", "setup-status")
|
|
420
|
+
* @param deps - Injected service dependencies
|
|
421
|
+
*/
|
|
422
|
+
declare function routeAuthRequest(request: Request, authPath: string, deps: AuthRouterDeps): Promise<Response | null>;
|
|
423
|
+
|
|
424
|
+
export { authenticateApiKey, buildClaims, checkCollectionAccess, checkPermission, createErrorResponse, createJsonErrorResponse, generateRefreshToken, getSession, hasAllRoles, hasAnyRole, hasRole, hashPassword, hashRefreshToken, isErrorResponse, isSuperAdmin, requireAllRoles, requireAnyRole, requireAuth, requireRole, routeAuthRequest, secretToKey, signAccessToken, validatePasswordStrength, verifyAccessToken, verifyPassword };
|
|
425
|
+
export type { AuthContext, AuthRouterDeps, BuildClaimsInput, ErrorResponse, GetSessionResult, NextlyJwtPayload, PasswordStrengthResult, RefreshTokenRecord, SessionUser, VerifyResult };
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import {
|
|
2
|
+
routeAuthRequest
|
|
3
|
+
} from "../chunk-A3WPLSDT.mjs";
|
|
4
|
+
import "../chunk-DXGGXIUZ.mjs";
|
|
5
|
+
import "../chunk-APKKRD2G.mjs";
|
|
6
|
+
import {
|
|
7
|
+
generateRefreshToken,
|
|
8
|
+
getSession,
|
|
9
|
+
hasAllRoles,
|
|
10
|
+
hasAnyRole,
|
|
11
|
+
hasRole,
|
|
12
|
+
hashRefreshToken
|
|
13
|
+
} from "../chunk-UUOFWCM6.mjs";
|
|
14
|
+
import {
|
|
15
|
+
verifyAccessToken
|
|
16
|
+
} from "../chunk-2ZFKXPQM.mjs";
|
|
17
|
+
import {
|
|
18
|
+
buildClaims,
|
|
19
|
+
secretToKey,
|
|
20
|
+
signAccessToken
|
|
21
|
+
} from "../chunk-X23WKS3Z.mjs";
|
|
22
|
+
import "../chunk-67GXH6PR.mjs";
|
|
23
|
+
import "../chunk-W4MGXIRR.mjs";
|
|
24
|
+
import {
|
|
25
|
+
hashPassword,
|
|
26
|
+
isSuperAdmin,
|
|
27
|
+
validatePasswordStrength,
|
|
28
|
+
verifyPassword
|
|
29
|
+
} from "../chunk-W5KKPZT5.mjs";
|
|
30
|
+
import "../chunk-56WO4WX7.mjs";
|
|
31
|
+
import "../chunk-2W3DVD7S.mjs";
|
|
32
|
+
import "../chunk-TS7GHTG2.mjs";
|
|
33
|
+
import "../chunk-H26B4FYG.mjs";
|
|
34
|
+
import "../chunk-DP3G27G5.mjs";
|
|
35
|
+
import "../chunk-DV6WVX2Q.mjs";
|
|
36
|
+
import "../chunk-UJ2IMJ4W.mjs";
|
|
37
|
+
import "../chunk-IUDOC7N7.mjs";
|
|
38
|
+
import "../chunk-LAZXX4HR.mjs";
|
|
39
|
+
import "../chunk-D5HQBNUB.mjs";
|
|
40
|
+
import {
|
|
41
|
+
NextlyError
|
|
42
|
+
} from "../chunk-NRUWQ5Z7.mjs";
|
|
43
|
+
import "../chunk-7P6ASYW6.mjs";
|
|
44
|
+
|
|
45
|
+
// src/auth/guards/require-auth.ts
|
|
46
|
+
async function requireAuth(request, secret) {
|
|
47
|
+
const result = await getSession(request, secret);
|
|
48
|
+
if (!result.authenticated) {
|
|
49
|
+
throw NextlyError.authRequired({
|
|
50
|
+
logContext: { reason: result.reason }
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return result.user;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/auth/guards/require-role.ts
|
|
57
|
+
function requireRole(user, roleSlug) {
|
|
58
|
+
if (!hasRole(user, roleSlug)) {
|
|
59
|
+
throw NextlyError.forbidden({
|
|
60
|
+
logContext: {
|
|
61
|
+
action: "require-role",
|
|
62
|
+
requiredRole: roleSlug,
|
|
63
|
+
userId: user.id
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function requireAnyRole(user, roleSlugs) {
|
|
69
|
+
if (!hasAnyRole(user, roleSlugs)) {
|
|
70
|
+
throw NextlyError.forbidden({
|
|
71
|
+
logContext: {
|
|
72
|
+
action: "require-any-role",
|
|
73
|
+
requiredRoles: roleSlugs,
|
|
74
|
+
userId: user.id
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function requireAllRoles(user, roleSlugs) {
|
|
80
|
+
if (!hasAllRoles(user, roleSlugs)) {
|
|
81
|
+
throw NextlyError.forbidden({
|
|
82
|
+
logContext: {
|
|
83
|
+
action: "require-all-roles",
|
|
84
|
+
requiredRoles: roleSlugs,
|
|
85
|
+
userId: user.id
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/auth/guards/require-permission.ts
|
|
92
|
+
function createErrorResponse(statusCode, message, error) {
|
|
93
|
+
return { success: false, statusCode, message, error, data: null };
|
|
94
|
+
}
|
|
95
|
+
function createJsonErrorResponse(errResp) {
|
|
96
|
+
const headers = {
|
|
97
|
+
"Content-Type": "application/json",
|
|
98
|
+
...errResp.headers || {}
|
|
99
|
+
};
|
|
100
|
+
return new Response(JSON.stringify(errResp), {
|
|
101
|
+
status: errResp.statusCode,
|
|
102
|
+
headers
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function isErrorResponse(value) {
|
|
106
|
+
return typeof value === "object" && value !== null && "success" in value && value.success === false && "statusCode" in value;
|
|
107
|
+
}
|
|
108
|
+
async function checkPermission(context, action, resource, deps) {
|
|
109
|
+
if (context.authMethod === "api-key") {
|
|
110
|
+
const permSlug = `${action}-${resource}`;
|
|
111
|
+
return context.permissions.get(permSlug) === true;
|
|
112
|
+
}
|
|
113
|
+
if (deps.checkUserPermission) {
|
|
114
|
+
return deps.checkUserPermission(context.userId, action, resource);
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/auth/guards/require-api-key.ts
|
|
120
|
+
async function authenticateApiKey(request, deps) {
|
|
121
|
+
const authHeader = request.headers.get("authorization");
|
|
122
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const rawKey = authHeader.slice(7);
|
|
126
|
+
if (!rawKey) {
|
|
127
|
+
return createErrorResponse(401, "Missing API key", "Unauthorized");
|
|
128
|
+
}
|
|
129
|
+
const result = await deps.validateApiKey(rawKey);
|
|
130
|
+
if (!result.valid) {
|
|
131
|
+
if (result.retryAfter) {
|
|
132
|
+
const resp = createErrorResponse(
|
|
133
|
+
429,
|
|
134
|
+
"Rate limit exceeded",
|
|
135
|
+
"Too Many Requests"
|
|
136
|
+
);
|
|
137
|
+
resp.headers = { "Retry-After": String(result.retryAfter) };
|
|
138
|
+
return resp;
|
|
139
|
+
}
|
|
140
|
+
return createErrorResponse(
|
|
141
|
+
401,
|
|
142
|
+
result.error || "Invalid API key",
|
|
143
|
+
"Unauthorized"
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
userId: result.userId,
|
|
148
|
+
userName: "",
|
|
149
|
+
userEmail: "",
|
|
150
|
+
permissions: result.permissions || /* @__PURE__ */ new Map(),
|
|
151
|
+
roles: result.roles || [],
|
|
152
|
+
authMethod: "api-key"
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/auth/guards/require-collection-access.ts
|
|
157
|
+
async function checkCollectionAccess(context, collectionSlug, operation, deps) {
|
|
158
|
+
if (context.roles.includes("super-admin")) {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
if (deps.evaluateCodeAccess) {
|
|
162
|
+
const codeResult = await deps.evaluateCodeAccess(
|
|
163
|
+
collectionSlug,
|
|
164
|
+
operation,
|
|
165
|
+
context
|
|
166
|
+
);
|
|
167
|
+
if (codeResult !== null) {
|
|
168
|
+
return codeResult;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return checkPermission(context, operation, collectionSlug, deps);
|
|
172
|
+
}
|
|
173
|
+
export {
|
|
174
|
+
authenticateApiKey,
|
|
175
|
+
buildClaims,
|
|
176
|
+
checkCollectionAccess,
|
|
177
|
+
checkPermission,
|
|
178
|
+
createErrorResponse,
|
|
179
|
+
createJsonErrorResponse,
|
|
180
|
+
generateRefreshToken,
|
|
181
|
+
getSession,
|
|
182
|
+
hasAllRoles,
|
|
183
|
+
hasAnyRole,
|
|
184
|
+
hasRole,
|
|
185
|
+
hashPassword,
|
|
186
|
+
hashRefreshToken,
|
|
187
|
+
isErrorResponse,
|
|
188
|
+
isSuperAdmin,
|
|
189
|
+
requireAllRoles,
|
|
190
|
+
requireAnyRole,
|
|
191
|
+
requireAuth,
|
|
192
|
+
requireRole,
|
|
193
|
+
routeAuthRequest,
|
|
194
|
+
secretToKey,
|
|
195
|
+
signAccessToken,
|
|
196
|
+
validatePasswordStrength,
|
|
197
|
+
verifyAccessToken,
|
|
198
|
+
verifyPassword
|
|
199
|
+
};
|