create-prenta 1.5.2 → 1.5.3
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# create-actuate-cms
|
|
2
2
|
|
|
3
|
+
## 1.5.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 63fb576: Tie API keys to the owner's lifecycle, enforce login lockout and per-email rate limits, ship HTML security headers from `withPrentaCMS` in the dev app, and harden search indexes, session storage, dashboard counts, RBAC defaults, keyset list cursors, and observability from the 22 August 2026 audit. Scaffolded projects pick up the same Prisma schema and 0015 migration.
|
|
8
|
+
|
|
3
9
|
## 1.5.2
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
-- Audit remediation (PERF-1/2/3/10, SEC-M1, QUAL-1).
|
|
2
|
+
-- Prefer transactional CREATE INDEX (not CONCURRENTLY) so `prisma migrate`
|
|
3
|
+
-- can run this in one transaction. For large production DBs, operators may
|
|
4
|
+
-- apply the equivalent `CREATE INDEX CONCURRENTLY` statements out of band.
|
|
5
|
+
|
|
6
|
+
DO $$
|
|
7
|
+
BEGIN
|
|
8
|
+
-- PGlite and slim Postgres images have no contrib package; skip rather
|
|
9
|
+
-- than fail the whole migration. Real hosts with postgresql-contrib
|
|
10
|
+
-- still get pg_trgm + the GIN indexes below.
|
|
11
|
+
IF EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = 'pg_trgm') THEN
|
|
12
|
+
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
|
13
|
+
ELSE
|
|
14
|
+
RAISE NOTICE 'pg_trgm skipped (extension not available)';
|
|
15
|
+
END IF;
|
|
16
|
+
EXCEPTION
|
|
17
|
+
WHEN insufficient_privilege THEN
|
|
18
|
+
RAISE NOTICE 'pg_trgm skipped (insufficient privilege)';
|
|
19
|
+
WHEN undefined_file THEN
|
|
20
|
+
RAISE NOTICE 'pg_trgm skipped (extension files not present)';
|
|
21
|
+
WHEN feature_not_supported THEN
|
|
22
|
+
RAISE NOTICE 'pg_trgm skipped (not supported on this server)';
|
|
23
|
+
WHEN OTHERS THEN
|
|
24
|
+
RAISE NOTICE 'pg_trgm skipped (%)', SQLERRM;
|
|
25
|
+
END
|
|
26
|
+
$$;
|
|
27
|
+
|
|
28
|
+
-- Document search + live list (no partial index on status — measured regression).
|
|
29
|
+
DO $$
|
|
30
|
+
BEGIN
|
|
31
|
+
CREATE INDEX IF NOT EXISTS ix_doc_plain_text_trgm
|
|
32
|
+
ON "prenta_documents" USING GIN ("plainText" gin_trgm_ops);
|
|
33
|
+
CREATE INDEX IF NOT EXISTS ix_doc_title_trgm
|
|
34
|
+
ON "prenta_documents" USING GIN ("title" gin_trgm_ops);
|
|
35
|
+
EXCEPTION
|
|
36
|
+
WHEN undefined_object THEN
|
|
37
|
+
RAISE NOTICE 'pg_trgm GIN indexes skipped (operator class unavailable)';
|
|
38
|
+
WHEN OTHERS THEN
|
|
39
|
+
RAISE NOTICE 'pg_trgm GIN indexes skipped (%)', SQLERRM;
|
|
40
|
+
END
|
|
41
|
+
$$;
|
|
42
|
+
CREATE INDEX IF NOT EXISTS ix_doc_coll_live_updated
|
|
43
|
+
ON "prenta_documents" ("collection", "updatedAt" DESC)
|
|
44
|
+
WHERE "deletedAt" IS NULL;
|
|
45
|
+
|
|
46
|
+
-- Hash session JWTs at rest (skip rows that already look like SHA-256 hex).
|
|
47
|
+
UPDATE "prenta_sessions"
|
|
48
|
+
SET "token" = encode(sha256(convert_to("token", 'UTF8')), 'hex')
|
|
49
|
+
WHERE "token" LIKE '%.%.%';
|
|
50
|
+
|
|
51
|
+
-- FormSubmission: drop boolean indexes, add unread-badge + (formId, submittedAt).
|
|
52
|
+
DROP INDEX IF EXISTS "prenta_form_submissions_unread_idx";
|
|
53
|
+
DROP INDEX IF EXISTS "prenta_form_submissions_starred_idx";
|
|
54
|
+
DROP INDEX IF EXISTS "prenta_form_submissions_archived_idx";
|
|
55
|
+
DROP INDEX IF EXISTS "prenta_form_submissions_status_idx";
|
|
56
|
+
DROP INDEX IF EXISTS "prenta_form_submissions_spamStatus_idx";
|
|
57
|
+
CREATE INDEX IF NOT EXISTS ix_form_sub_unread_badge
|
|
58
|
+
ON "prenta_form_submissions" ("formId")
|
|
59
|
+
WHERE "unread" = true AND "archived" = false;
|
|
60
|
+
CREATE INDEX IF NOT EXISTS "prenta_form_submissions_formId_submittedAt_idx"
|
|
61
|
+
ON "prenta_form_submissions" ("formId", "submittedAt" DESC);
|
|
62
|
+
|
|
63
|
+
-- ContentLock / notifications / API keys
|
|
64
|
+
DELETE FROM "prenta_content_locks" t
|
|
65
|
+
WHERE t.ctid NOT IN (
|
|
66
|
+
SELECT min(ctid) FROM "prenta_content_locks" GROUP BY "documentId"
|
|
67
|
+
);
|
|
68
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "prenta_content_locks_documentId_key"
|
|
69
|
+
ON "prenta_content_locks" ("documentId");
|
|
70
|
+
CREATE INDEX IF NOT EXISTS "prenta_notifications_userId_idx"
|
|
71
|
+
ON "prenta_notifications" ("userId");
|
|
72
|
+
CREATE INDEX IF NOT EXISTS "prenta_api_keys_userId_idx"
|
|
73
|
+
ON "prenta_api_keys" ("userId");
|
|
74
|
+
|
|
75
|
+
-- Remove unused multi-site / backup / workflow stubs (QUAL-1).
|
|
76
|
+
DROP TABLE IF EXISTS "prenta_backup_records";
|
|
77
|
+
DROP TABLE IF EXISTS "prenta_workflow_states";
|
|
78
|
+
DROP TABLE IF EXISTS "prenta_sites";
|
|
79
|
+
ALTER TABLE "prenta_documents" DROP COLUMN IF EXISTS "siteId";
|
|
@@ -122,7 +122,6 @@ model User {
|
|
|
122
122
|
contentLocks ContentLock[]
|
|
123
123
|
notifications InAppNotification[]
|
|
124
124
|
contentTemplates ContentTemplate[]
|
|
125
|
-
workflowAssigned WorkflowState[]
|
|
126
125
|
passwordResetTokens PasswordResetToken[]
|
|
127
126
|
documentComments DocumentComment[]
|
|
128
127
|
documentNotifications DocumentNotification[]
|
|
@@ -179,6 +178,7 @@ model ApiKey {
|
|
|
179
178
|
|
|
180
179
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
181
180
|
|
|
181
|
+
@@index([userId])
|
|
182
182
|
@@map("prenta_api_keys")
|
|
183
183
|
}
|
|
184
184
|
|
|
@@ -236,7 +236,6 @@ model Document {
|
|
|
236
236
|
contentHash String?
|
|
237
237
|
structuredData Json?
|
|
238
238
|
contentGraph Json?
|
|
239
|
-
siteId String?
|
|
240
239
|
templateId String?
|
|
241
240
|
workflowStage WorkflowStage @default(DRAFT)
|
|
242
241
|
reviewerId String?
|
|
@@ -252,7 +251,6 @@ model Document {
|
|
|
252
251
|
mediaUsages MediaUsage[]
|
|
253
252
|
contentLocks ContentLock[]
|
|
254
253
|
contentIssues ContentIssue[]
|
|
255
|
-
workflowState WorkflowState?
|
|
256
254
|
|
|
257
255
|
@@unique([collection, slug])
|
|
258
256
|
@@index([collection])
|
|
@@ -373,6 +371,7 @@ model ContentLock {
|
|
|
373
371
|
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
|
374
372
|
user User @relation(fields: [userId], references: [id])
|
|
375
373
|
|
|
374
|
+
@@unique([documentId])
|
|
376
375
|
@@map("prenta_content_locks")
|
|
377
376
|
}
|
|
378
377
|
|
|
@@ -388,6 +387,7 @@ model InAppNotification {
|
|
|
388
387
|
|
|
389
388
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
390
389
|
|
|
390
|
+
@@index([userId])
|
|
391
391
|
@@map("prenta_notifications")
|
|
392
392
|
}
|
|
393
393
|
|
|
@@ -405,33 +405,6 @@ model ContentTemplate {
|
|
|
405
405
|
@@map("prenta_content_templates")
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
-
model Site {
|
|
409
|
-
id String @id @default(cuid())
|
|
410
|
-
domain String @unique
|
|
411
|
-
name String
|
|
412
|
-
configOverrides Json?
|
|
413
|
-
isDefault Boolean @default(false)
|
|
414
|
-
createdAt DateTime @default(now())
|
|
415
|
-
updatedAt DateTime @updatedAt
|
|
416
|
-
|
|
417
|
-
@@map("prenta_sites")
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
model WorkflowState {
|
|
421
|
-
id String @id @default(cuid())
|
|
422
|
-
documentId String @unique
|
|
423
|
-
currentStep Int @default(0)
|
|
424
|
-
assignedToId String?
|
|
425
|
-
status String @default("pending")
|
|
426
|
-
definition Json
|
|
427
|
-
createdAt DateTime @default(now())
|
|
428
|
-
updatedAt DateTime @updatedAt
|
|
429
|
-
|
|
430
|
-
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
|
431
|
-
assignedTo User? @relation(fields: [assignedToId], references: [id])
|
|
432
|
-
|
|
433
|
-
@@map("prenta_workflow_states")
|
|
434
|
-
}
|
|
435
408
|
|
|
436
409
|
model Redirect {
|
|
437
410
|
id String @id @default(cuid())
|
|
@@ -670,13 +643,9 @@ model FormSubmission {
|
|
|
670
643
|
deliveries FormDeliveryAttempt[]
|
|
671
644
|
|
|
672
645
|
@@index([formId])
|
|
673
|
-
@@index([status])
|
|
674
|
-
@@index([unread])
|
|
675
|
-
@@index([starred])
|
|
676
|
-
@@index([archived])
|
|
677
|
-
@@index([spamStatus])
|
|
678
646
|
@@index([submittedAt])
|
|
679
647
|
@@index([senderEmail])
|
|
648
|
+
@@index([formId, submittedAt])
|
|
680
649
|
@@map("prenta_form_submissions")
|
|
681
650
|
}
|
|
682
651
|
|
|
@@ -760,18 +729,6 @@ model FormDeliveryAttempt {
|
|
|
760
729
|
@@map("prenta_form_delivery_attempts")
|
|
761
730
|
}
|
|
762
731
|
|
|
763
|
-
model BackupRecord {
|
|
764
|
-
id String @id @default(cuid())
|
|
765
|
-
filename String
|
|
766
|
-
storageKey String
|
|
767
|
-
sizeBytes Int
|
|
768
|
-
type String
|
|
769
|
-
metadata Json?
|
|
770
|
-
createdAt DateTime @default(now())
|
|
771
|
-
|
|
772
|
-
@@map("prenta_backup_records")
|
|
773
|
-
}
|
|
774
|
-
|
|
775
732
|
model WebhookEndpoint {
|
|
776
733
|
id String @id @default(cuid())
|
|
777
734
|
url String
|