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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-prenta",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "description": "Scaffold a new Prenta CMS project",
5
5
  "repository": {
6
6
  "type": "git",