@actuate-media/cms-core 0.12.0 → 0.14.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 +21 -21
- package/dist/__tests__/api/api-key-auth.test.d.ts +2 -0
- package/dist/__tests__/api/api-key-auth.test.d.ts.map +1 -0
- package/dist/__tests__/api/api-key-auth.test.js +217 -0
- package/dist/__tests__/api/api-key-auth.test.js.map +1 -0
- package/dist/__tests__/api/health.test.d.ts +2 -0
- package/dist/__tests__/api/health.test.d.ts.map +1 -0
- package/dist/__tests__/api/health.test.js +140 -0
- package/dist/__tests__/api/health.test.js.map +1 -0
- package/dist/__tests__/auth/oauth.test.d.ts +2 -0
- package/dist/__tests__/auth/oauth.test.d.ts.map +1 -0
- package/dist/__tests__/auth/oauth.test.js +406 -0
- package/dist/__tests__/auth/oauth.test.js.map +1 -0
- package/dist/__tests__/auth/reset.test.d.ts +2 -0
- package/dist/__tests__/auth/reset.test.d.ts.map +1 -0
- package/dist/__tests__/auth/reset.test.js +303 -0
- package/dist/__tests__/auth/reset.test.js.map +1 -0
- package/dist/__tests__/diagnostics/env.test.d.ts +2 -0
- package/dist/__tests__/diagnostics/env.test.d.ts.map +1 -0
- package/dist/__tests__/diagnostics/env.test.js +119 -0
- package/dist/__tests__/diagnostics/env.test.js.map +1 -0
- package/dist/__tests__/diagnostics/logger.test.d.ts +2 -0
- package/dist/__tests__/diagnostics/logger.test.d.ts.map +1 -0
- package/dist/__tests__/diagnostics/logger.test.js +111 -0
- package/dist/__tests__/diagnostics/logger.test.js.map +1 -0
- package/dist/__tests__/security/api-key-enhanced.test.d.ts +2 -0
- package/dist/__tests__/security/api-key-enhanced.test.d.ts.map +1 -0
- package/dist/__tests__/security/api-key-enhanced.test.js +110 -0
- package/dist/__tests__/security/api-key-enhanced.test.js.map +1 -0
- package/dist/__tests__/security/rate-limit.test.js +42 -0
- package/dist/__tests__/security/rate-limit.test.js.map +1 -1
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +7 -6
- package/dist/actions.js.map +1 -1
- package/dist/api/handler-factory.d.ts.map +1 -1
- package/dist/api/handler-factory.js +31 -8
- package/dist/api/handler-factory.js.map +1 -1
- package/dist/api/handlers.d.ts.map +1 -1
- package/dist/api/handlers.js +508 -55
- package/dist/api/handlers.js.map +1 -1
- package/dist/auth/oauth.d.ts.map +1 -1
- package/dist/auth/oauth.js +5 -1
- package/dist/auth/oauth.js.map +1 -1
- package/dist/auth/reset.d.ts.map +1 -1
- package/dist/auth/reset.js +2 -1
- package/dist/auth/reset.js.map +1 -1
- package/dist/config/runtime.d.ts +99 -0
- package/dist/config/runtime.d.ts.map +1 -0
- package/dist/config/runtime.js +43 -0
- package/dist/config/runtime.js.map +1 -0
- package/dist/config/types.d.ts +21 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/diagnostics/env.d.ts +44 -0
- package/dist/diagnostics/env.d.ts.map +1 -0
- package/dist/diagnostics/env.js +293 -0
- package/dist/diagnostics/env.js.map +1 -0
- package/dist/diagnostics/logger.d.ts +38 -0
- package/dist/diagnostics/logger.d.ts.map +1 -0
- package/dist/diagnostics/logger.js +89 -0
- package/dist/diagnostics/logger.js.map +1 -0
- package/dist/page-builder/blocks.d.ts.map +1 -1
- package/dist/page-builder/blocks.js +6 -1
- package/dist/page-builder/blocks.js.map +1 -1
- package/dist/security/api-key-enhanced.d.ts +48 -5
- package/dist/security/api-key-enhanced.d.ts.map +1 -1
- package/dist/security/api-key-enhanced.js +60 -9
- package/dist/security/api-key-enhanced.js.map +1 -1
- package/dist/security/audit.d.ts.map +1 -1
- package/dist/security/audit.js +3 -1
- package/dist/security/audit.js.map +1 -1
- package/dist/security/rate-limit.d.ts +8 -0
- package/dist/security/rate-limit.d.ts.map +1 -1
- package/dist/security/rate-limit.js +81 -3
- package/dist/security/rate-limit.js.map +1 -1
- package/generated/browser.ts +109 -0
- package/generated/client.ts +133 -0
- package/generated/commonInputTypes.ts +709 -0
- package/generated/enums.ts +125 -0
- package/generated/internal/class.ts +376 -0
- package/generated/internal/prismaNamespace.ts +2617 -0
- package/generated/internal/prismaNamespaceBrowser.ts +611 -0
- package/generated/models/ApiKey.ts +1550 -0
- package/generated/models/AuditLog.ts +1206 -0
- package/generated/models/BackupRecord.ts +1250 -0
- package/generated/models/ContentLock.ts +1472 -0
- package/generated/models/ContentTemplate.ts +1416 -0
- package/generated/models/Document.ts +3005 -0
- package/generated/models/Folder.ts +1904 -0
- package/generated/models/FormSubmission.ts +1200 -0
- package/generated/models/InAppNotification.ts +1457 -0
- package/generated/models/Media.ts +2340 -0
- package/generated/models/MediaUsage.ts +1472 -0
- package/generated/models/OAuthAccount.ts +1463 -0
- package/generated/models/Redirect.ts +1284 -0
- package/generated/models/Session.ts +1492 -0
- package/generated/models/Site.ts +1206 -0
- package/generated/models/User.ts +3513 -0
- package/generated/models/Version.ts +1511 -0
- package/generated/models/WorkflowState.ts +1514 -0
- package/generated/models.ts +29 -0
- package/package.json +1 -1
- package/prisma/cms-schema.prisma +306 -306
- package/prisma/migrations/0001_init/migration.sql +384 -384
- package/prisma/migrations/0002_folders/migration.sql +39 -39
- package/prisma/migrations/0003_search_and_webhooks/migration.sql +50 -50
- package/prisma/migrations/0004_script_tags/migration.sql +21 -21
- package/prisma/migrations/0005_password_reset_tokens/migration.sql +20 -20
- package/prisma/migrations/0006_page_builder/migration.sql +38 -38
- package/prisma/migrations/migration_lock.toml +3 -3
- package/prisma/schema.prisma +549 -549
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API key generation and scope validation.
|
|
3
|
+
*
|
|
4
|
+
* Keys are formatted `act_sk_<64 hex chars>`. We store only a SHA-256 hash of
|
|
5
|
+
* the key in the database; the raw key is shown to the user exactly once at
|
|
6
|
+
* creation time. The first 15 chars (`act_sk_` + 8 hex) are stored as
|
|
7
|
+
* `keyPrefix` so the admin UI can display a human-recognizable token without
|
|
8
|
+
* leaking the secret half.
|
|
9
|
+
*/
|
|
1
10
|
/** Generate a new API key with scoped permissions. */
|
|
2
11
|
export async function generateApiKey(config) {
|
|
3
12
|
const rawBytes = crypto.getRandomValues(new Uint8Array(32));
|
|
@@ -5,21 +14,63 @@ export async function generateApiKey(config) {
|
|
|
5
14
|
.map((b) => b.toString(16).padStart(2, '0'))
|
|
6
15
|
.join('');
|
|
7
16
|
const key = `${config.prefix}_${rawKey}`;
|
|
17
|
+
// First 8 hex chars after the underscore are safe to display (the remaining
|
|
18
|
+
// 56 hex chars provide >224 bits of entropy).
|
|
8
19
|
const keyPrefix = key.slice(0, config.prefix.length + 9);
|
|
9
|
-
const
|
|
10
|
-
|
|
20
|
+
const keyHash = await hashApiKey(key);
|
|
21
|
+
return { key, keyHash, keyPrefix };
|
|
22
|
+
}
|
|
23
|
+
/** SHA-256 hash a raw API key for lookup. */
|
|
24
|
+
export async function hashApiKey(rawKey) {
|
|
25
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(rawKey));
|
|
26
|
+
return Array.from(new Uint8Array(hashBuffer))
|
|
11
27
|
.map((b) => b.toString(16).padStart(2, '0'))
|
|
12
28
|
.join('');
|
|
13
|
-
return { key, keyHash, keyPrefix };
|
|
14
29
|
}
|
|
15
|
-
/**
|
|
30
|
+
/** True when the request's bearer token looks like an API key (vs a session JWT). */
|
|
31
|
+
export function looksLikeApiKey(token) {
|
|
32
|
+
return /^act_sk_[a-f0-9]{32,}$/i.test(token);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Validate an API key's scopes against a collection action. Returns true when
|
|
36
|
+
* the key is permitted to perform `action` on `collection`. Admin keys always
|
|
37
|
+
* pass.
|
|
38
|
+
*/
|
|
16
39
|
export function validateApiKeyScope(scopes, collection, action) {
|
|
17
|
-
if (scopes.
|
|
40
|
+
if (scopes.admin)
|
|
41
|
+
return true;
|
|
42
|
+
if (!scopes.collections || scopes.collections.length === 0)
|
|
43
|
+
return false;
|
|
44
|
+
if (!scopes.actions || !scopes.actions.includes(action))
|
|
18
45
|
return false;
|
|
19
|
-
|
|
20
|
-
|
|
46
|
+
if (scopes.collections.includes('*'))
|
|
47
|
+
return true;
|
|
48
|
+
return scopes.collections.includes(collection);
|
|
49
|
+
}
|
|
50
|
+
/** Validate scope for a global action. */
|
|
51
|
+
export function validateApiKeyGlobalScope(scopes, slug) {
|
|
52
|
+
if (scopes.admin)
|
|
53
|
+
return true;
|
|
54
|
+
if (!scopes.globals || scopes.globals.length === 0)
|
|
55
|
+
return false;
|
|
56
|
+
if (scopes.globals.includes('*'))
|
|
57
|
+
return true;
|
|
58
|
+
return scopes.globals.includes(slug);
|
|
59
|
+
}
|
|
60
|
+
/** Validate scope for media uploads/management. */
|
|
61
|
+
export function validateApiKeyMediaScope(scopes) {
|
|
62
|
+
return scopes.admin === true || scopes.media === true;
|
|
63
|
+
}
|
|
64
|
+
/** Validate scope for page-builder/AI endpoints. */
|
|
65
|
+
export function validateApiKeyPageBuilderScope(scopes) {
|
|
66
|
+
return scopes.admin === true || scopes.pageBuilder === true;
|
|
67
|
+
}
|
|
68
|
+
/** Validate IP restrictions against the request's client IP. */
|
|
69
|
+
export function validateApiKeyIp(restrictions, ip) {
|
|
70
|
+
if (!restrictions || restrictions.length === 0)
|
|
71
|
+
return true;
|
|
72
|
+
if (!ip || ip === 'unknown')
|
|
21
73
|
return false;
|
|
22
|
-
|
|
23
|
-
return true;
|
|
74
|
+
return restrictions.includes(ip);
|
|
24
75
|
}
|
|
25
76
|
//# sourceMappingURL=api-key-enhanced.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-key-enhanced.js","sourceRoot":"","sources":["../../src/security/api-key-enhanced.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api-key-enhanced.js","sourceRoot":"","sources":["../../src/security/api-key-enhanced.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/security/audit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/security/audit.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,SAAS,CAAC,EAAE,IAAI,CAAA;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,EAAE,CAAC,EAAE,IAAI,CAAA;IACT,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,iCAAiC;AACjC,wBAAsB,QAAQ,CAAC,KAAK,EAAE;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAClC,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBhB;AAED,2DAA2D;AAC3D,wBAAsB,WAAW,CAC/B,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CACb,GACL,OAAO,CAAC;IAAE,OAAO,EAAE,GAAG,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAmB5C"}
|
package/dist/security/audit.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { getDB } from '../db.js';
|
|
2
|
+
import { createLogger } from '../diagnostics/logger.js';
|
|
3
|
+
const logger = createLogger('audit');
|
|
2
4
|
/** Record an audit log event. */
|
|
3
5
|
export async function logEvent(event) {
|
|
4
6
|
try {
|
|
@@ -17,7 +19,7 @@ export async function logEvent(event) {
|
|
|
17
19
|
// Fail open — audit logging should never block the primary operation,
|
|
18
20
|
// but still surface the failure so the operator can fix it.
|
|
19
21
|
if (process.env.NODE_ENV !== 'test') {
|
|
20
|
-
|
|
22
|
+
logger.error('logEvent failed', { reason: err instanceof Error ? err.message : String(err) });
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/security/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/security/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEvD,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;AAyBpC,iCAAiC;AACjC,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,KAM9B;IACC,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,KAAK,EAAO,CAAA;QACvB,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;YACvB,IAAI,EAAE;gBACJ,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;gBAC5B,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI;gBAClC,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI;gBAClC,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,IAAI;aAC/B;SACF,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,sEAAsE;QACtE,4DAA4D;QAC5D,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAC/F,CAAC;IACH,CAAC;AACH,CAAC;AAED,2DAA2D;AAC3D,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,UAKI,EAAE;IAEN,MAAM,EAAE,GAAG,KAAK,EAAO,CAAA;IACvB,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,CAAC,EAAE,QAAQ,GAAG,EAAE,EAAE,GAAG,OAAO,CAAA;IAE1D,MAAM,KAAK,GAAQ,EAAE,CAAA;IACrB,IAAI,MAAM;QAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAA;IACjC,IAAI,KAAK;QAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAA;IAE9B,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACzC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACnB,KAAK;YACL,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;YAC9B,IAAI,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ;YAC3B,IAAI,EAAE,QAAQ;SACf,CAAC;QACF,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC;KAC7B,CAAC,CAAA;IAEF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;AAC3B,CAAC"}
|
|
@@ -11,6 +11,14 @@ export interface RateLimiter {
|
|
|
11
11
|
export interface RateLimitConfig {
|
|
12
12
|
windowMs: number;
|
|
13
13
|
maxRequests: number;
|
|
14
|
+
/**
|
|
15
|
+
* Maximum number of distinct keys retained by the in-memory limiter.
|
|
16
|
+
* When exceeded, the oldest entry (insertion order) is evicted. Defaults
|
|
17
|
+
* to 10,000 — high enough to cover any legitimate single-process traffic
|
|
18
|
+
* while bounding worst-case memory under enumeration / IP-spoofing
|
|
19
|
+
* attacks. See M1 in the engineering audit.
|
|
20
|
+
*/
|
|
21
|
+
maxKeys?: number;
|
|
14
22
|
}
|
|
15
23
|
/** Create a rate limiter backed by an in-memory sliding window (for dev/single-process). */
|
|
16
24
|
export declare function createInMemoryRateLimiter(config: RateLimitConfig): RateLimiter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/security/rate-limit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/security/rate-limit.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,IAAI,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;IAC5C,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AASD,4FAA4F;AAC5F,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,CAmE9E;AA2BD,iFAAiF;AACjF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,CAuF7E;AA6BD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,CAkBtE"}
|
|
@@ -1,16 +1,61 @@
|
|
|
1
|
+
import { createLogger } from '../diagnostics/logger.js';
|
|
2
|
+
const logger = createLogger('rate-limit');
|
|
3
|
+
const DEFAULT_MAX_KEYS = 10_000;
|
|
4
|
+
/**
|
|
5
|
+
* Sweep expired entries on every Nth `check()` call. Tunable at module level
|
|
6
|
+
* so tests can stub it without exposing it via the public config surface.
|
|
7
|
+
*/
|
|
8
|
+
const SWEEP_EVERY_N_CHECKS = 256;
|
|
1
9
|
/** Create a rate limiter backed by an in-memory sliding window (for dev/single-process). */
|
|
2
10
|
export function createInMemoryRateLimiter(config) {
|
|
11
|
+
// `Map` preserves insertion order so we can use it as an LRU directly:
|
|
12
|
+
// every `check()` re-inserts the touched key (delete + set) which moves it
|
|
13
|
+
// to the tail. When we exceed the cap, `windows.keys().next().value` is the
|
|
14
|
+
// least-recently-touched entry and we evict it.
|
|
3
15
|
const windows = new Map();
|
|
16
|
+
const maxKeys = config.maxKeys && config.maxKeys > 0 ? config.maxKeys : DEFAULT_MAX_KEYS;
|
|
17
|
+
let checksSinceSweep = 0;
|
|
18
|
+
function sweepExpired(now) {
|
|
19
|
+
// Bounded periodic cleanup of expired entries. We cap iterations so a
|
|
20
|
+
// pathological burst of new keys can't make this O(n) on every call.
|
|
21
|
+
let removed = 0;
|
|
22
|
+
for (const [key, entry] of windows) {
|
|
23
|
+
if (entry.resetAt <= now) {
|
|
24
|
+
windows.delete(key);
|
|
25
|
+
removed++;
|
|
26
|
+
if (removed >= 1000)
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function evictIfFull() {
|
|
32
|
+
while (windows.size >= maxKeys) {
|
|
33
|
+
const oldestKey = windows.keys().next().value;
|
|
34
|
+
if (oldestKey === undefined)
|
|
35
|
+
break;
|
|
36
|
+
windows.delete(oldestKey);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
4
39
|
return {
|
|
5
40
|
async check(key) {
|
|
6
41
|
const now = Date.now();
|
|
42
|
+
checksSinceSweep++;
|
|
43
|
+
if (checksSinceSweep >= SWEEP_EVERY_N_CHECKS) {
|
|
44
|
+
checksSinceSweep = 0;
|
|
45
|
+
sweepExpired(now);
|
|
46
|
+
}
|
|
7
47
|
const entry = windows.get(key);
|
|
8
48
|
if (!entry || now > entry.resetAt) {
|
|
9
49
|
const resetAt = now + config.windowMs;
|
|
50
|
+
windows.delete(key);
|
|
51
|
+
evictIfFull();
|
|
10
52
|
windows.set(key, { count: 1, resetAt });
|
|
11
53
|
return { allowed: true, remaining: config.maxRequests - 1, resetAt: new Date(resetAt) };
|
|
12
54
|
}
|
|
13
55
|
entry.count++;
|
|
56
|
+
// Re-insert to mark as most-recently-used.
|
|
57
|
+
windows.delete(key);
|
|
58
|
+
windows.set(key, entry);
|
|
14
59
|
const allowed = entry.count <= config.maxRequests;
|
|
15
60
|
return {
|
|
16
61
|
allowed,
|
|
@@ -24,6 +69,31 @@ export function createInMemoryRateLimiter(config) {
|
|
|
24
69
|
},
|
|
25
70
|
};
|
|
26
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Throttle the noisy "Upstash failed" audit event so a sustained outage does
|
|
74
|
+
* not generate one DB write per request. We allow at most one audit log per
|
|
75
|
+
* `AUDIT_THROTTLE_MS`. The `console.error` line still fires on every failure
|
|
76
|
+
* for live operator visibility.
|
|
77
|
+
*/
|
|
78
|
+
const AUDIT_THROTTLE_MS = 60_000;
|
|
79
|
+
let lastAuditAt = 0;
|
|
80
|
+
async function emitDegradedAudit(reason) {
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
if (now - lastAuditAt < AUDIT_THROTTLE_MS)
|
|
83
|
+
return;
|
|
84
|
+
lastAuditAt = now;
|
|
85
|
+
try {
|
|
86
|
+
// Lazy import — avoids a static dep cycle (audit.ts -> db.js -> ...).
|
|
87
|
+
const { logEvent } = await import('./audit.js');
|
|
88
|
+
await logEvent({
|
|
89
|
+
event: 'rate_limit.degraded',
|
|
90
|
+
details: { reason, throttleMs: AUDIT_THROTTLE_MS },
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Audit failures are themselves logged in audit.ts; don't double-log here.
|
|
95
|
+
}
|
|
96
|
+
}
|
|
27
97
|
/** Create a rate limiter backed by Upstash Redis (for serverless/production). */
|
|
28
98
|
export function createUpstashRateLimiter(config) {
|
|
29
99
|
const url = process.env.UPSTASH_REDIS_REST_URL;
|
|
@@ -79,8 +149,14 @@ export function createUpstashRateLimiter(config) {
|
|
|
79
149
|
}
|
|
80
150
|
catch (err) {
|
|
81
151
|
// Fail open with logging — a Redis outage should NOT take the entire
|
|
82
|
-
// CMS offline.
|
|
83
|
-
console.error
|
|
152
|
+
// CMS offline. We surface the degradation through:
|
|
153
|
+
// 1. console.error for live ops/log aggregator dashboards;
|
|
154
|
+
// 2. a throttled audit-log event so the outage is preserved in the
|
|
155
|
+
// durable security record without flooding the DB during long
|
|
156
|
+
// Upstash incidents.
|
|
157
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
158
|
+
logger.error('Upstash request failed, allowing through', { reason });
|
|
159
|
+
void emitDegradedAudit(reason);
|
|
84
160
|
return {
|
|
85
161
|
allowed: true,
|
|
86
162
|
remaining: Math.max(0, config.maxRequests - 1),
|
|
@@ -93,7 +169,9 @@ export function createUpstashRateLimiter(config) {
|
|
|
93
169
|
await redisPipeline([['DEL', `ratelimit:${key}`]]);
|
|
94
170
|
}
|
|
95
171
|
catch (err) {
|
|
96
|
-
|
|
172
|
+
logger.error('Upstash reset failed', {
|
|
173
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
174
|
+
});
|
|
97
175
|
}
|
|
98
176
|
},
|
|
99
177
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/security/rate-limit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/security/rate-limit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
|
|
2
|
+
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
// biome-ignore-all lint: generated file
|
|
5
|
+
// @ts-nocheck
|
|
6
|
+
/*
|
|
7
|
+
* This file should be your main import to use Prisma-related types and utilities in a browser.
|
|
8
|
+
* Use it to get access to models, enums, and input types.
|
|
9
|
+
*
|
|
10
|
+
* This file does not contain a `PrismaClient` class, nor several other helpers that are intended as server-side only.
|
|
11
|
+
* See `client.ts` for the standard, server-side entry point.
|
|
12
|
+
*
|
|
13
|
+
* 🟢 You can import this file directly.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import * as Prisma from './internal/prismaNamespaceBrowser'
|
|
17
|
+
export { Prisma }
|
|
18
|
+
export * as $Enums from './enums'
|
|
19
|
+
export * from './enums';
|
|
20
|
+
/**
|
|
21
|
+
* Model User
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
24
|
+
export type User = Prisma.UserModel
|
|
25
|
+
/**
|
|
26
|
+
* Model OAuthAccount
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
export type OAuthAccount = Prisma.OAuthAccountModel
|
|
30
|
+
/**
|
|
31
|
+
* Model Session
|
|
32
|
+
*
|
|
33
|
+
*/
|
|
34
|
+
export type Session = Prisma.SessionModel
|
|
35
|
+
/**
|
|
36
|
+
* Model ApiKey
|
|
37
|
+
*
|
|
38
|
+
*/
|
|
39
|
+
export type ApiKey = Prisma.ApiKeyModel
|
|
40
|
+
/**
|
|
41
|
+
* Model AuditLog
|
|
42
|
+
*
|
|
43
|
+
*/
|
|
44
|
+
export type AuditLog = Prisma.AuditLogModel
|
|
45
|
+
/**
|
|
46
|
+
* Model Folder
|
|
47
|
+
*
|
|
48
|
+
*/
|
|
49
|
+
export type Folder = Prisma.FolderModel
|
|
50
|
+
/**
|
|
51
|
+
* Model Document
|
|
52
|
+
*
|
|
53
|
+
*/
|
|
54
|
+
export type Document = Prisma.DocumentModel
|
|
55
|
+
/**
|
|
56
|
+
* Model Version
|
|
57
|
+
*
|
|
58
|
+
*/
|
|
59
|
+
export type Version = Prisma.VersionModel
|
|
60
|
+
/**
|
|
61
|
+
* Model Media
|
|
62
|
+
*
|
|
63
|
+
*/
|
|
64
|
+
export type Media = Prisma.MediaModel
|
|
65
|
+
/**
|
|
66
|
+
* Model MediaUsage
|
|
67
|
+
*
|
|
68
|
+
*/
|
|
69
|
+
export type MediaUsage = Prisma.MediaUsageModel
|
|
70
|
+
/**
|
|
71
|
+
* Model ContentLock
|
|
72
|
+
*
|
|
73
|
+
*/
|
|
74
|
+
export type ContentLock = Prisma.ContentLockModel
|
|
75
|
+
/**
|
|
76
|
+
* Model InAppNotification
|
|
77
|
+
*
|
|
78
|
+
*/
|
|
79
|
+
export type InAppNotification = Prisma.InAppNotificationModel
|
|
80
|
+
/**
|
|
81
|
+
* Model ContentTemplate
|
|
82
|
+
*
|
|
83
|
+
*/
|
|
84
|
+
export type ContentTemplate = Prisma.ContentTemplateModel
|
|
85
|
+
/**
|
|
86
|
+
* Model Site
|
|
87
|
+
*
|
|
88
|
+
*/
|
|
89
|
+
export type Site = Prisma.SiteModel
|
|
90
|
+
/**
|
|
91
|
+
* Model WorkflowState
|
|
92
|
+
*
|
|
93
|
+
*/
|
|
94
|
+
export type WorkflowState = Prisma.WorkflowStateModel
|
|
95
|
+
/**
|
|
96
|
+
* Model Redirect
|
|
97
|
+
*
|
|
98
|
+
*/
|
|
99
|
+
export type Redirect = Prisma.RedirectModel
|
|
100
|
+
/**
|
|
101
|
+
* Model FormSubmission
|
|
102
|
+
*
|
|
103
|
+
*/
|
|
104
|
+
export type FormSubmission = Prisma.FormSubmissionModel
|
|
105
|
+
/**
|
|
106
|
+
* Model BackupRecord
|
|
107
|
+
*
|
|
108
|
+
*/
|
|
109
|
+
export type BackupRecord = Prisma.BackupRecordModel
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
|
|
2
|
+
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
// biome-ignore-all lint: generated file
|
|
5
|
+
// @ts-nocheck
|
|
6
|
+
/*
|
|
7
|
+
* This file should be your main import to use Prisma. Through it you get access to all the models, enums, and input types.
|
|
8
|
+
* If you're looking for something you can import in the client-side of your application, please refer to the `browser.ts` file instead.
|
|
9
|
+
*
|
|
10
|
+
* 🟢 You can import this file directly.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as process from 'node:process'
|
|
14
|
+
import * as path from 'node:path'
|
|
15
|
+
import { fileURLToPath } from 'node:url'
|
|
16
|
+
globalThis['__dirname'] = path.dirname(fileURLToPath(import.meta.url))
|
|
17
|
+
|
|
18
|
+
import * as runtime from "@prisma/client/runtime/client"
|
|
19
|
+
import * as $Enums from "./enums"
|
|
20
|
+
import * as $Class from "./internal/class"
|
|
21
|
+
import * as Prisma from "./internal/prismaNamespace"
|
|
22
|
+
|
|
23
|
+
export * as $Enums from './enums'
|
|
24
|
+
export * from "./enums"
|
|
25
|
+
/**
|
|
26
|
+
* ## Prisma Client
|
|
27
|
+
*
|
|
28
|
+
* Type-safe database client for TypeScript
|
|
29
|
+
* @example
|
|
30
|
+
* ```
|
|
31
|
+
* const prisma = new PrismaClient({
|
|
32
|
+
* adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL })
|
|
33
|
+
* })
|
|
34
|
+
* // Fetch zero or more Users
|
|
35
|
+
* const users = await prisma.user.findMany()
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* Read more in our [docs](https://pris.ly/d/client).
|
|
39
|
+
*/
|
|
40
|
+
export const PrismaClient = $Class.getPrismaClientClass()
|
|
41
|
+
export type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions["omit"] = Prisma.PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>
|
|
42
|
+
export { Prisma }
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Model User
|
|
46
|
+
*
|
|
47
|
+
*/
|
|
48
|
+
export type User = Prisma.UserModel
|
|
49
|
+
/**
|
|
50
|
+
* Model OAuthAccount
|
|
51
|
+
*
|
|
52
|
+
*/
|
|
53
|
+
export type OAuthAccount = Prisma.OAuthAccountModel
|
|
54
|
+
/**
|
|
55
|
+
* Model Session
|
|
56
|
+
*
|
|
57
|
+
*/
|
|
58
|
+
export type Session = Prisma.SessionModel
|
|
59
|
+
/**
|
|
60
|
+
* Model ApiKey
|
|
61
|
+
*
|
|
62
|
+
*/
|
|
63
|
+
export type ApiKey = Prisma.ApiKeyModel
|
|
64
|
+
/**
|
|
65
|
+
* Model AuditLog
|
|
66
|
+
*
|
|
67
|
+
*/
|
|
68
|
+
export type AuditLog = Prisma.AuditLogModel
|
|
69
|
+
/**
|
|
70
|
+
* Model Folder
|
|
71
|
+
*
|
|
72
|
+
*/
|
|
73
|
+
export type Folder = Prisma.FolderModel
|
|
74
|
+
/**
|
|
75
|
+
* Model Document
|
|
76
|
+
*
|
|
77
|
+
*/
|
|
78
|
+
export type Document = Prisma.DocumentModel
|
|
79
|
+
/**
|
|
80
|
+
* Model Version
|
|
81
|
+
*
|
|
82
|
+
*/
|
|
83
|
+
export type Version = Prisma.VersionModel
|
|
84
|
+
/**
|
|
85
|
+
* Model Media
|
|
86
|
+
*
|
|
87
|
+
*/
|
|
88
|
+
export type Media = Prisma.MediaModel
|
|
89
|
+
/**
|
|
90
|
+
* Model MediaUsage
|
|
91
|
+
*
|
|
92
|
+
*/
|
|
93
|
+
export type MediaUsage = Prisma.MediaUsageModel
|
|
94
|
+
/**
|
|
95
|
+
* Model ContentLock
|
|
96
|
+
*
|
|
97
|
+
*/
|
|
98
|
+
export type ContentLock = Prisma.ContentLockModel
|
|
99
|
+
/**
|
|
100
|
+
* Model InAppNotification
|
|
101
|
+
*
|
|
102
|
+
*/
|
|
103
|
+
export type InAppNotification = Prisma.InAppNotificationModel
|
|
104
|
+
/**
|
|
105
|
+
* Model ContentTemplate
|
|
106
|
+
*
|
|
107
|
+
*/
|
|
108
|
+
export type ContentTemplate = Prisma.ContentTemplateModel
|
|
109
|
+
/**
|
|
110
|
+
* Model Site
|
|
111
|
+
*
|
|
112
|
+
*/
|
|
113
|
+
export type Site = Prisma.SiteModel
|
|
114
|
+
/**
|
|
115
|
+
* Model WorkflowState
|
|
116
|
+
*
|
|
117
|
+
*/
|
|
118
|
+
export type WorkflowState = Prisma.WorkflowStateModel
|
|
119
|
+
/**
|
|
120
|
+
* Model Redirect
|
|
121
|
+
*
|
|
122
|
+
*/
|
|
123
|
+
export type Redirect = Prisma.RedirectModel
|
|
124
|
+
/**
|
|
125
|
+
* Model FormSubmission
|
|
126
|
+
*
|
|
127
|
+
*/
|
|
128
|
+
export type FormSubmission = Prisma.FormSubmissionModel
|
|
129
|
+
/**
|
|
130
|
+
* Model BackupRecord
|
|
131
|
+
*
|
|
132
|
+
*/
|
|
133
|
+
export type BackupRecord = Prisma.BackupRecordModel
|