@beechcms/api 0.7.0 → 0.8.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.
Files changed (111) hide show
  1. package/assets/dashboard/0auth_mcp.svg +1 -0
  2. package/assets/dashboard/assets/{chart-B1qG9c0-.js → chart-CYgKoF6U.js} +6 -6
  3. package/assets/dashboard/assets/dist-DHabpZqA.js +140 -0
  4. package/assets/dashboard/assets/index-BZMSpYrl.js +3613 -0
  5. package/assets/dashboard/assets/index-Dex2YZcC.css +1 -0
  6. package/assets/dashboard/assets/json-code-editor-DtZpKDXm.js +12 -0
  7. package/assets/dashboard/assets/pie-chart-recharts-C7WAhcCk.js +1 -0
  8. package/assets/dashboard/assets/richtext-render-x0TT0116.js +258 -0
  9. package/assets/dashboard/assets/timeseries-chart-recharts-B9RquiY6.js +1 -0
  10. package/assets/dashboard/assets/w3c-keyname-BOAvb0qz.js +1 -0
  11. package/assets/dashboard/index.html +4 -2
  12. package/dist/auth/auth.app.d.ts +13 -0
  13. package/dist/auth/index.d.ts +6 -0
  14. package/dist/factory.d.ts +14 -1
  15. package/dist/features/oauth/authorization-request.d.ts +127 -0
  16. package/dist/features/oauth/authorize.d.ts +97 -0
  17. package/dist/features/oauth/consents.d.ts +75 -0
  18. package/dist/features/oauth/constants.d.ts +48 -0
  19. package/dist/features/oauth/index.d.ts +31 -0
  20. package/dist/features/oauth/revoke.d.ts +29 -0
  21. package/dist/features/oauth/token-issuance.d.ts +49 -0
  22. package/dist/features/oauth/token.d.ts +38 -0
  23. package/dist/features/search/constants.d.ts +43 -0
  24. package/dist/features/search/handlers/embed.d.ts +31 -0
  25. package/dist/features/search/handlers/full-text-search.d.ts +28 -0
  26. package/dist/features/search/index.d.ts +20 -0
  27. package/dist/features/search/jobs/semantic-search.hooks.d.ts +21 -0
  28. package/dist/features/search/jobs/semantic-search.worker.d.ts +101 -0
  29. package/dist/features/search/public-search.router.d.ts +15 -0
  30. package/dist/features/search/search.d.ts +14 -6
  31. package/dist/features/search/semantic-search.hooks.d.ts +2 -0
  32. package/dist/features/search/semantic-search.worker.d.ts +21 -0
  33. package/dist/features/search/types.d.ts +100 -0
  34. package/dist/features/search/utils/search-utils.d.ts +56 -0
  35. package/dist/features/seeds/seeds.destructive.d.ts +6 -0
  36. package/dist/features/seeds/seeds.handler.d.ts +14 -0
  37. package/dist/features/seeds/seeds.helpers.d.ts +118 -0
  38. package/dist/features/seeds/seeds.mcp.d.ts +37 -0
  39. package/dist/features/upload/index.d.ts +2 -2
  40. package/dist/index.js +3501 -1338
  41. package/dist/middleware/auth.middleware.d.ts +34 -4
  42. package/dist/middleware/oauth-scope.middleware.d.ts +60 -0
  43. package/dist/middleware/rate-limit.middleware.d.ts +4 -2
  44. package/dist/middleware/repository.middleware.d.ts +9 -1
  45. package/dist/public/api-key-middleware.d.ts +0 -4
  46. package/dist/public/public-add.d.ts +1 -1
  47. package/dist/rate-limit/in-memory-rate-limiter.d.ts +1 -8
  48. package/dist/shared/db/repositories/content.repository.d1.d.ts +4 -2
  49. package/dist/shared/db/repositories/d1-oauth-authorization-code.repository.d.ts +9 -0
  50. package/dist/shared/db/repositories/d1-oauth-client.repository.d.ts +6 -0
  51. package/dist/shared/db/repositories/d1-oauth-consent.repository.d.ts +10 -0
  52. package/dist/shared/db/repositories/d1-oauth-token.repository.d.ts +12 -0
  53. package/dist/shared/db/repositories/d1-search.query.d.ts +54 -0
  54. package/dist/shared/db/repositories/d1-vector.repository.d.ts +11 -0
  55. package/dist/shared/db/repositories/in-memory-seed.repository.d.ts +2 -1
  56. package/dist/shared/db/repositories/seed.repository.d1.d.ts +3 -1
  57. package/dist/shared/db/repositories/time-trap-token.repository.d1.d.ts +7 -0
  58. package/dist/shared/storage/factory.d.ts +84 -4
  59. package/dist/shared/storage/r2-bucket.d.ts +127 -0
  60. package/dist/shared/storage/s3-bucket.d.ts +104 -10
  61. package/dist/shared/storage/upload.d.ts +33 -5
  62. package/dist/shared/utils/dual-key-rate-limiter.d.ts +21 -0
  63. package/dist/shared/utils/media-utils.d.ts +3 -3
  64. package/dist/shared/utils/opaque-token.d.ts +13 -0
  65. package/dist/types.d.ts +49 -1
  66. package/migrations/0000_v040_base.sql +170 -0
  67. package/migrations/_archive/0000_schema_init.sql +257 -0
  68. package/migrations/_archive/0001_init.sql +25 -0
  69. package/migrations/_archive/0002_seed_admin.sql +10 -0
  70. package/migrations/_archive/0003_refresh_tokens.sql +17 -0
  71. package/migrations/_archive/0004_seed_projects.sql +18 -0
  72. package/migrations/_archive/0005_update_projects.sql +56 -0
  73. package/migrations/_archive/0006_add_slug_and_unique_index.sql +7 -0
  74. package/migrations/_archive/0007_cleanup_test_data.sql +4 -0
  75. package/migrations/_archive/0008_seed_content_entries.sql +104 -0
  76. package/migrations/_archive/0009_public_idempotency.sql +11 -0
  77. package/migrations/_archive/0010_seed_real_data.sql +31 -0
  78. package/migrations/_archive/0010_testsite_data.sql +31 -0
  79. package/migrations/_archive/0011_analytics.sql +10 -0
  80. package/migrations/_archive/0012_seed_analytics.sql +17 -0
  81. package/migrations/_archive/0013_system_stats.sql +8 -0
  82. package/migrations/_archive/0014_activity_logs.sql +15 -0
  83. package/migrations/_archive/0015_notifications.sql +12 -0
  84. package/migrations/_archive/0016_fts5_global_search.sql +81 -0
  85. package/migrations/_archive/0017_seed_clienti.sql +90 -0
  86. package/migrations/_archive/0018_draft_data.sql +3 -0
  87. package/migrations/_archive/0019_drop_fts_triggers.sql +7 -0
  88. package/migrations/_archive/0020_composite_indexes.sql +15 -0
  89. package/migrations/_archive/0021_media_objects.sql +15 -0
  90. package/migrations/_archive/0022_analytics_seed.sql +23 -0
  91. package/migrations/_archive/0023_user_profile.sql +4 -0
  92. package/migrations/_archive/0024_activity_user_name.sql +3 -0
  93. package/migrations/_archive/0025_password_reset_tokens.sql +11 -0
  94. package/migrations/_archive/0026_remove_dev_admin.sql +4 -0
  95. package/migrations/_archive/0027_v040_doctype_architecture.sql +51 -0
  96. package/migrations/_archive/0029_fts_all_text_branches.sql +281 -0
  97. package/migrations/_archive/0031_site_settings.sql +9 -0
  98. package/migrations/_archive/0032_seeds.sql +28 -0
  99. package/migrations/_archive/0033_dashboard_layouts.sql +14 -0
  100. package/migrations/_archive/0034_kanban_foundation.sql +24 -0
  101. package/migrations/_archive/0035_setup_lock.sql +10 -0
  102. package/migrations/_archive/0036_notification_type_success.sql +23 -0
  103. package/migrations/_archive/0037_time_trap_tokens.sql +13 -0
  104. package/migrations/_archive/0038_oauth_authorization.sql +95 -0
  105. package/migrations/_archive/0039_oauth_client_beech_mcp.sql +20 -0
  106. package/package.json +3 -5
  107. package/assets/dashboard/assets/index-0fKOe8Wq.js +0 -745
  108. package/assets/dashboard/assets/index-Bj2upDZk.css +0 -1
  109. package/assets/dashboard/assets/pie-chart-recharts-DSf6kc6u.js +0 -1
  110. package/assets/dashboard/assets/timeseries-chart-recharts-CqM5c6MV.js +0 -1
  111. /package/migrations/{0029_automations.sql → _archive/0029_automations.sql} +0 -0
@@ -0,0 +1,31 @@
1
+ -- Migrazione 0010: Seed contenuti per il testsite (DX testing)
2
+ -- Vengono inseriti record con immagini di alta qualita' da Unsplash.
3
+
4
+ INSERT INTO content_entries (id, schema_slug, slug, status, data, created_at, updated_at) VALUES
5
+ ('test-art-01', 'articoli', 'design-system-vibes', 'published',
6
+ '{"art_01":"Design System: Il Cuore del Testsite","art_02":"2026-04-10","art_03":"https://images.unsplash.com/photo-1561070791-2526d30994b5?auto=format&fit=crop&q=80&w=2000&h=1000","art_04":"{\"design\":\"#3b82f6\",\"dx\":\"#10b981\"}","art_05":"<p>Un design system solido è la chiave per un frontend eccellente. Con Beech CMS e Shadcn UI, costruiamo interfacce in un batter d''occhio.</p>","art_06":"Design System","art_07":"Scopri come integrare componenti premium nel tuo progetto."}',
7
+ unixepoch(), unixepoch()),
8
+ ('test-art-02', 'articoli', 'edge-computing-future', 'published',
9
+ '{"art_01":"Il Futuro è Edge Computing","art_02":"2026-04-12","art_03":"https://images.unsplash.com/photo-1451187580459-43490279c0fa?auto=format&fit=crop&q=80&w=2000&h=1000","art_04":"{\"cloud\":\"#8b5cf6\",\"edge\":\"#ec4899\"}","art_05":"<p>Beech CMS nasce per Cloudflare Workers, offrendo performance incredibili a livello globale senza la necessità di complessi bilanciatori di carico.</p>","art_06":"Edge Computing","art_07":"Perché Cloudflare Workers cambierà tutto."}',
10
+ unixepoch(), unixepoch()),
11
+ ('test-art-03', 'articoli', 'premium-typography', 'published',
12
+ '{"art_01":"L''Arte della Tipografia","art_02":"2026-04-15","art_03":"https://images.unsplash.com/photo-1596541223140-5b565a0b9380?auto=format&fit=crop&q=80&w=2000&h=1000","art_04":"{\"ui\":\"#f59e0b\",\"typography\":\"#06b6d4\"}","art_05":"<p>Una tipografia curata può trasformare un semplice sito web in un''esperienza utente di classe mondiale. Inter, Roboto e Outfit sono le nostre prime scelte.</p>","art_06":"Tipografia Premium","art_07":"Eleva i tuoi contenuti con i font giusti."}',
13
+ unixepoch(), unixepoch());
14
+
15
+ -- Usiamo 'prodotti' come Galleria Fotografica
16
+ INSERT INTO content_entries (id, schema_slug, slug, status, data, created_at, updated_at) VALUES
17
+ ('test-gal-01', 'prodotti', 'architecture-modern', 'published',
18
+ '{"prd_01":"Architettura Moderna","prd_02":0,"prd_03":1,"prd_04":true,"prd_05":"https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?auto=format&fit=crop&q=80&w=800&h=800","prd_06":"[\"https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?auto=format&fit=crop&q=80\",\"https://images.unsplash.com/photo-1428366890462-dd4baecf492b?auto=format&fit=crop&q=80\"]","prd_07":"<p>Collezione fotografica di architetture di fascia alta, per dimostrare l''uso di immagini asset-list in Tailwind e css moderni.</p>","prd_08":"Architettura Moderna","prd_09":"Galleria di architettura contemporanea."}',
19
+ unixepoch(), unixepoch()),
20
+ ('test-gal-02', 'prodotti', 'nature-landscapes', 'published',
21
+ '{"prd_01":"Paesaggi Incontaminati","prd_02":0,"prd_03":1,"prd_04":true,"prd_05":"https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?auto=format&fit=crop&q=80&w=800&h=800","prd_06":"[\"https://images.unsplash.com/photo-1472214103451-9374bd1c798e?auto=format&fit=crop&q=80\",\"https://images.unsplash.com/photo-1501854140801-50d01698950b?auto=format&fit=crop&q=80\"]","prd_07":"<p>Immagini rilassanti della natura, perfette per una galleria premium responsive.</p>","prd_08":"Paesaggi Incontaminati","prd_09":"Bellezza della natura in alta definizione."}',
22
+ unixepoch(), unixepoch()),
23
+ ('test-gal-03', 'prodotti', 'abstract-art', 'published',
24
+ '{"prd_01":"Arte Astratta e Luce","prd_02":0,"prd_03":1,"prd_04":true,"prd_05":"https://images.unsplash.com/photo-1550684848-fac1c5b4e853?auto=format&fit=crop&q=80&w=800&h=800","prd_06":"[]","prd_07":"<p>Giochi di luce e neon renderizzati per esplorare contrasti cromatici decisi nel glassmorphism UI.</p>","prd_08":"Arte Astratta e Luce","prd_09":"Un tuffo in effetti visivi astratti."}',
25
+ unixepoch(), unixepoch());
26
+
27
+ -- Landing Page
28
+ INSERT INTO content_entries (id, schema_slug, slug, status, data, created_at, updated_at) VALUES
29
+ ('test-pag-01', 'pagine', 'test-home', 'published',
30
+ '{"pag_01":"BeechCMS Test Experience","pag_02":"https://images.unsplash.com/photo-1550745165-9bc0b252726f?auto=format&fit=crop&q=80&w=2000&h=1000","pag_03":"<h1>Benvenuti nella DX Demo</h1><p>Esplora il design, la reattività della Public API e prova tu stesso il contact module.</p>","pag_04":"BeechCMS - Test","pag_05":"Pagina di test del framework."}',
31
+ unixepoch(), unixepoch());
@@ -0,0 +1,10 @@
1
+ -- Migrazione: Tabella Analytics per metriche Cloudflare-style
2
+ CREATE TABLE IF NOT EXISTS analytics (
3
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
4
+ day_ts INTEGER NOT NULL, -- Timestamp inizio giornata (Unix epoch)
5
+ metric TEXT NOT NULL, -- 'requests', 'visitors', 'bandwidth_mb'
6
+ value INTEGER DEFAULT 0,
7
+ UNIQUE(day_ts, metric)
8
+ );
9
+
10
+ CREATE INDEX IF NOT EXISTS idx_analytics_day ON analytics(day_ts);
@@ -0,0 +1,17 @@
1
+ -- Seed data per Analytics (ultimi 30 giorni)
2
+ -- Questo permette di vedere il trend nella dashboard immediatamente
3
+
4
+ INSERT OR IGNORE INTO analytics (day_ts, metric, value) VALUES (1713312000, 'visitors', 120);
5
+ INSERT OR IGNORE INTO analytics (day_ts, metric, value) VALUES (1713225600, 'visitors', 145);
6
+ INSERT OR IGNORE INTO analytics (day_ts, metric, value) VALUES (1713139200, 'visitors', 98);
7
+ INSERT OR IGNORE INTO analytics (day_ts, metric, value) VALUES (1713052800, 'visitors', 210);
8
+ INSERT OR IGNORE INTO analytics (day_ts, metric, value) VALUES (1712966400, 'visitors', 180);
9
+ INSERT OR IGNORE INTO analytics (day_ts, metric, value) VALUES (1712880000, 'visitors', 165);
10
+ INSERT OR IGNORE INTO analytics (day_ts, metric, value) VALUES (1712793600, 'visitors', 190);
11
+ -- Aggiungiamo dati per oggi (simulati)
12
+ INSERT OR IGNORE INTO analytics (day_ts, metric, value) VALUES (strftime('%s', 'now', 'start of day'), 'visitors', 42);
13
+
14
+ -- Richieste totali
15
+ INSERT OR IGNORE INTO analytics (day_ts, metric, value) VALUES (1713312000, 'requests', 1200);
16
+ INSERT OR IGNORE INTO analytics (day_ts, metric, value) VALUES (1713225600, 'requests', 1450);
17
+ INSERT OR IGNORE INTO analytics (day_ts, metric, value) VALUES (1713139200, 'requests', 980);
@@ -0,0 +1,8 @@
1
+ -- Migrazione: Tabella per statistiche di sistema persistenti (es. storage R2)
2
+ CREATE TABLE IF NOT EXISTS system_stats (
3
+ id TEXT PRIMARY KEY,
4
+ value TEXT NOT NULL
5
+ );
6
+
7
+ -- Inizializzazione contatore storage (in byte)
8
+ INSERT OR IGNORE INTO system_stats (id, value) VALUES ('total_storage_bytes', '0');
@@ -0,0 +1,15 @@
1
+ -- Migrazione: Tabella per il log delle attività (Audit Log)
2
+ CREATE TABLE IF NOT EXISTS activity_logs (
3
+ id TEXT PRIMARY KEY,
4
+ user_id TEXT NOT NULL,
5
+ user_email TEXT NOT NULL,
6
+ action TEXT NOT NULL, -- 'create' | 'update' | 'delete' | 'upload'
7
+ entity_type TEXT NOT NULL, -- 'content' | 'media'
8
+ entity_id TEXT NOT NULL, -- UUID o Key R2
9
+ entity_slug TEXT, -- Slug del seed (es. 'progetti') o null
10
+ details TEXT, -- JSON con metadati (es. { title: '...' })
11
+ created_at INTEGER DEFAULT (unixepoch())
12
+ );
13
+
14
+ CREATE INDEX IF NOT EXISTS idx_activity_user ON activity_logs(user_id);
15
+ CREATE INDEX IF NOT EXISTS idx_activity_created ON activity_logs(created_at);
@@ -0,0 +1,12 @@
1
+ -- Migrazione: Tabella per le notifiche di sistema
2
+ CREATE TABLE IF NOT EXISTS notifications (
3
+ id TEXT PRIMARY KEY,
4
+ title TEXT NOT NULL,
5
+ message TEXT NOT NULL,
6
+ type TEXT DEFAULT 'info',
7
+ is_read INTEGER DEFAULT 0,
8
+ created_at INTEGER DEFAULT (unixepoch())
9
+ );
10
+
11
+ CREATE INDEX IF NOT EXISTS idx_notifications_created ON notifications(created_at);
12
+ CREATE INDEX IF NOT EXISTS idx_notifications_unread ON notifications(is_read);
@@ -0,0 +1,81 @@
1
+ -- Generic migration for FTS5 global search
2
+
3
+ CREATE VIRTUAL TABLE IF NOT EXISTS content_fts USING fts5(
4
+ entry_id UNINDEXED, -- PK di content_entries, non cercabile
5
+ schema_slug UNINDEXED, -- per filtrare per tipo senza MATCH
6
+ title, -- campo testo principale
7
+ body, -- campo richtext/text secondario
8
+ tags, -- campo tags (array JSON → stringa piatta)
9
+ status UNINDEXED, -- per filtrare per status
10
+ tokenize = 'unicode61 remove_diacritics 1'
11
+ );
12
+
13
+ -- Backfill dati esistenti (DELETE prima per idempotenza)
14
+ DELETE FROM content_fts;
15
+ INSERT INTO content_fts (entry_id, schema_slug, title, body, tags, status)
16
+ SELECT
17
+ id,
18
+ schema_slug,
19
+ COALESCE(
20
+ json_extract(data, '$.art_01'), -- articoli → title
21
+ json_extract(data, '$.prd_01'), -- prodotti → name
22
+ json_extract(data, '$.pag_01'), -- pagine → title
23
+ json_extract(data, '$.tm_01'), -- team → name
24
+ json_extract(data, '$.tes_02'), -- testimonanze → company
25
+ ''
26
+ ),
27
+ COALESCE(
28
+ json_extract(data, '$.art_05'), -- articoli → body
29
+ json_extract(data, '$.prd_07'), -- prodotti → description
30
+ json_extract(data, '$.pag_03'), -- pagine → body
31
+ ''
32
+ ),
33
+ COALESCE(
34
+ json_extract(data, '$.art_04'), -- articoli → tags
35
+ ''
36
+ ),
37
+ status
38
+ FROM content_entries;
39
+
40
+ -- Sync Triggers
41
+
42
+ -- AFTER INSERT
43
+ CREATE TRIGGER IF NOT EXISTS fts_after_insert
44
+ AFTER INSERT ON content_entries BEGIN
45
+ INSERT INTO content_fts (entry_id, schema_slug, title, body, tags, status)
46
+ VALUES (
47
+ new.id,
48
+ new.schema_slug,
49
+ COALESCE(json_extract(new.data, '$.art_01'), json_extract(new.data, '$.prd_01'),
50
+ json_extract(new.data, '$.pag_01'), json_extract(new.data, '$.tm_01'),
51
+ json_extract(new.data, '$.tes_02'), ''),
52
+ COALESCE(json_extract(new.data, '$.art_05'), json_extract(new.data, '$.prd_07'),
53
+ json_extract(new.data, '$.pag_03'), ''),
54
+ COALESCE(json_extract(new.data, '$.art_04'), ''),
55
+ new.status
56
+ );
57
+ END;
58
+
59
+ -- AFTER UPDATE
60
+ CREATE TRIGGER IF NOT EXISTS fts_after_update
61
+ AFTER UPDATE ON content_entries BEGIN
62
+ DELETE FROM content_fts WHERE entry_id = old.id;
63
+ INSERT INTO content_fts (entry_id, schema_slug, title, body, tags, status)
64
+ VALUES (
65
+ new.id,
66
+ new.schema_slug,
67
+ COALESCE(json_extract(new.data, '$.art_01'), json_extract(new.data, '$.prd_01'),
68
+ json_extract(new.data, '$.pag_01'), json_extract(new.data, '$.tm_01'),
69
+ json_extract(new.data, '$.tes_02'), ''),
70
+ COALESCE(json_extract(new.data, '$.art_05'), json_extract(new.data, '$.prd_07'),
71
+ json_extract(new.data, '$.pag_03'), ''),
72
+ COALESCE(json_extract(new.data, '$.art_04'), ''),
73
+ new.status
74
+ );
75
+ END;
76
+
77
+ -- AFTER DELETE
78
+ CREATE TRIGGER IF NOT EXISTS fts_after_delete
79
+ AFTER DELETE ON content_entries BEGIN
80
+ DELETE FROM content_fts WHERE entry_id = old.id;
81
+ END;
@@ -0,0 +1,90 @@
1
+ -- Migration 0017: Seed "clienti" — demo data for Branch.policies
2
+ --
3
+ -- Schema slug : clienti (CLIENTE_SEED)
4
+ -- Branch IDs : clt_01…clt_08
5
+ --
6
+ -- Policy coverage per record:
7
+ -- clt_02 (email) → visibility:masked, search:false, public:false
8
+ -- clt_03 (passwordHash) → privacy:hash stored as sha256hex, visibility:hidden, all other: false
9
+ -- clt_05 (phone) → visibility:masked, filter:false, public:false
10
+ -- clt_06 (internalNote) → visibility:hidden, search:false, public:false
11
+ -- clt_07 (registeredAt) → public:false
12
+ --
13
+ -- sha256 values used (pre-hashed, as the API would store them after applyPrivacy):
14
+ -- "password" → 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
15
+ -- "123456" → 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
16
+ -- "letmein" → 1c8bfe8f801d79745c4631d09fff36c82aa37fc4cce4fc946683d7b336b63032
17
+ -- "abc123" → 6ca13d52ca70c883e0f0bb101e425a89e8624de51db2d2392593af6a84118090
18
+ -- "qwerty" → 65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5
19
+
20
+ INSERT INTO content_entries (id, schema_slug, slug, status, data, created_at, updated_at) VALUES
21
+
22
+ -- Cliente 1: piano enterprise, attivo — tutti i campi policy popolati
23
+ ('clt-0001', 'clienti', 'marco-rossi', 'published',
24
+ json_object(
25
+ 'clt_01', 'Marco Rossi',
26
+ 'clt_02', 'marco.rossi@acmecorp.it',
27
+ 'clt_03', '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8',
28
+ 'clt_04', 'enterprise',
29
+ 'clt_05', '+39 02 1234 5678',
30
+ 'clt_06', 'Cliente storico. Sconto applicato del 20% su rinnovo annuale.',
31
+ 'clt_07', '2024-03-15',
32
+ 'clt_08', 1
33
+ ),
34
+ unixepoch(), unixepoch()),
35
+
36
+ -- Cliente 2: piano pro, attivo
37
+ ('clt-0002', 'clienti', 'laura-bianchi', 'published',
38
+ json_object(
39
+ 'clt_01', 'Laura Bianchi',
40
+ 'clt_02', 'laura.bianchi@studiox.io',
41
+ 'clt_03', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92',
42
+ 'clt_04', 'pro',
43
+ 'clt_05', '+39 06 9876 5432',
44
+ 'clt_06', 'Contattare prima del rinnovo per upsell a enterprise.',
45
+ 'clt_07', '2025-01-08',
46
+ 'clt_08', 1
47
+ ),
48
+ unixepoch(), unixepoch()),
49
+
50
+ -- Cliente 3: piano starter, attivo
51
+ ('clt-0003', 'clienti', 'davide-ferrari', 'published',
52
+ json_object(
53
+ 'clt_01', 'Davide Ferrari',
54
+ 'clt_02', 'davide@mediahub.eu',
55
+ 'clt_03', '1c8bfe8f801d79745c4631d09fff36c82aa37fc4cce4fc946683d7b336b63032',
56
+ 'clt_04', 'starter',
57
+ 'clt_05', '+39 011 4567 890',
58
+ 'clt_06', 'Ha richiesto supporto per migrazione da WordPress.',
59
+ 'clt_07', '2025-06-22',
60
+ 'clt_08', 1
61
+ ),
62
+ unixepoch(), unixepoch()),
63
+
64
+ -- Cliente 4: piano free, non attivo — dimostra active:false
65
+ ('clt-0004', 'clienti', 'giulia-verdi', 'published',
66
+ json_object(
67
+ 'clt_01', 'Giulia Verdi',
68
+ 'clt_02', 'giulia.verdi@personal.me',
69
+ 'clt_03', '6ca13d52ca70c883e0f0bb101e425a89e8624de51db2d2392593af6a84118090',
70
+ 'clt_04', 'free',
71
+ 'clt_05', '+39 081 3210 987',
72
+ 'clt_06', 'Account free scaduto. Inviare offerta upgrade.',
73
+ 'clt_07', '2025-09-01',
74
+ 'clt_08', 0
75
+ ),
76
+ unixepoch(), unixepoch()),
77
+
78
+ -- Cliente 5: piano pro, attivo — phone e internalNote volutamente NULL per testare null safety
79
+ ('clt-0005', 'clienti', 'sofia-costa', 'published',
80
+ json_object(
81
+ 'clt_01', 'Sofia Costa',
82
+ 'clt_02', 'sofia.costa@fittech.com',
83
+ 'clt_03', '65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5',
84
+ 'clt_04', 'pro',
85
+ 'clt_05', null,
86
+ 'clt_06', null,
87
+ 'clt_07', '2026-01-30',
88
+ 'clt_08', 1
89
+ ),
90
+ unixepoch(), unixepoch());
@@ -0,0 +1,3 @@
1
+ -- Aggiunge colonna draft_data per supportare la feature "bozza in attesa".
2
+ -- Nullable: NULL = nessuna bozza pendente, TEXT = JSON con Botanical IDs (stesso formato di data).
3
+ ALTER TABLE content_entries ADD COLUMN draft_data TEXT;
@@ -0,0 +1,7 @@
1
+ -- Rimuove i trigger FTS5 hardcoded che usavano branch ID specifici degli attuali seed di esempio.
2
+ -- La sincronizzazione FTS è ora gestita a livello applicativo in shared/fts-sync.ts,
3
+ -- usando il Botanical Engine per estrarre i campi in modo generico per qualsiasi seed.
4
+
5
+ DROP TRIGGER IF EXISTS fts_after_insert;
6
+ DROP TRIGGER IF EXISTS fts_after_update;
7
+ DROP TRIGGER IF EXISTS fts_after_delete;
@@ -0,0 +1,15 @@
1
+ -- Indici compositi su content_entries per ottimizzare le query di lista filtrata.
2
+ -- Tutti gli indici sono agnostici rispetto ai seed: funzionano per qualsiasi
3
+ -- content type che il developer definirà in @beechcms/core.
4
+
5
+ -- Copre il pattern più frequente: WHERE schema_slug = ? AND status = ? ORDER BY created_at DESC
6
+ CREATE INDEX IF NOT EXISTS idx_ce_slug_status_created
7
+ ON content_entries(schema_slug, status, created_at DESC);
8
+
9
+ -- Copre: ORDER BY updated_at (widget "modificati di recente" nel dashboard)
10
+ CREATE INDEX IF NOT EXISTS idx_ce_slug_updated
11
+ ON content_entries(schema_slug, updated_at DESC);
12
+
13
+ -- Copre: WHERE draft_data IS NOT NULL (filtro bozze pendenti nel dashboard)
14
+ CREATE INDEX IF NOT EXISTS idx_ce_draft_pending
15
+ ON content_entries(schema_slug) WHERE draft_data IS NOT NULL;
@@ -0,0 +1,15 @@
1
+ -- Media library: traccia ogni file caricato su R2.
2
+ -- Abilita: media library UI, rilevamento orfani, utilizzo storage per utente.
3
+ -- Il contatore in system_stats rimane per compatibilità; SUM(size_bytes) qui è la fonte canonica.
4
+
5
+ CREATE TABLE IF NOT EXISTS media_objects (
6
+ key TEXT PRIMARY KEY, -- chiave R2 (es. "1713600000-img.jpg")
7
+ filename TEXT NOT NULL, -- nome originale pre-sanitize
8
+ mime_type TEXT NOT NULL,
9
+ size_bytes INTEGER NOT NULL,
10
+ uploaded_by TEXT NOT NULL DEFAULT '', -- users.id (soft ref, nessun FK per portabilità D1)
11
+ created_at INTEGER DEFAULT (unixepoch())
12
+ );
13
+
14
+ CREATE INDEX IF NOT EXISTS idx_media_user ON media_objects(uploaded_by);
15
+ CREATE INDEX IF NOT EXISTS idx_media_created ON media_objects(created_at DESC);
@@ -0,0 +1,23 @@
1
+ -- Aggiunge la dimensione seed alle analytics per il tracking del traffico per content type.
2
+ -- Ricrea la tabella per modificare il vincolo UNIQUE da (day_ts, metric)
3
+ -- a (day_ts, metric, seed). seed = '' indica metriche globali (non per-seed).
4
+ -- NOTA: SQLite tratta NULL come distinto da NULL nei UNIQUE index,
5
+ -- quindi si usa stringa vuota come sentinel per le metriche globali.
6
+
7
+ CREATE TABLE IF NOT EXISTS analytics_v2 (
8
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
9
+ day_ts INTEGER NOT NULL,
10
+ metric TEXT NOT NULL,
11
+ seed TEXT NOT NULL DEFAULT '', -- '' = metrica globale, 'articoli' = per-seed
12
+ value INTEGER DEFAULT 0,
13
+ UNIQUE(day_ts, metric, seed)
14
+ );
15
+
16
+ INSERT OR IGNORE INTO analytics_v2 (id, day_ts, metric, seed, value)
17
+ SELECT id, day_ts, metric, '', value FROM analytics;
18
+
19
+ DROP TABLE analytics;
20
+ ALTER TABLE analytics_v2 RENAME TO analytics;
21
+
22
+ CREATE INDEX IF NOT EXISTS idx_analytics_day ON analytics(day_ts);
23
+ CREATE INDEX IF NOT EXISTS idx_analytics_seed ON analytics(seed, day_ts);
@@ -0,0 +1,4 @@
1
+ -- Migrazione: Aggiunge colonne profilo utente
2
+ ALTER TABLE users ADD COLUMN name TEXT;
3
+ ALTER TABLE users ADD COLUMN avatar_url TEXT;
4
+ ALTER TABLE users ADD COLUMN notification_prefs TEXT NOT NULL DEFAULT '{}';
@@ -0,0 +1,3 @@
1
+ -- Migrazione: Aggiunge colonna user_name alla tabella activity_logs
2
+ -- Permette di mostrare il nome dell'utente invece dell'email nell'attività recente
3
+ ALTER TABLE activity_logs ADD COLUMN user_name TEXT;
@@ -0,0 +1,11 @@
1
+ -- Password reset tokens: single-use, 30-minute TTL, stored hashed (SHA-256)
2
+ CREATE TABLE IF NOT EXISTS password_reset_tokens (
3
+ id TEXT PRIMARY KEY,
4
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
5
+ token_hash TEXT NOT NULL,
6
+ expires_at INTEGER NOT NULL,
7
+ created_at INTEGER DEFAULT (unixepoch()),
8
+ used_at INTEGER DEFAULT NULL
9
+ );
10
+ CREATE INDEX IF NOT EXISTS idx_prt_hash ON password_reset_tokens(token_hash);
11
+ CREATE INDEX IF NOT EXISTS idx_prt_user ON password_reset_tokens(user_id);
@@ -0,0 +1,4 @@
1
+ -- Remove the hardcoded development admin inserted by 0002_seed_admin.sql.
2
+ -- First-time admin creation is now handled via POST /auth/setup.
3
+ -- This migration is a no-op if the row was already manually deleted.
4
+ DELETE FROM users WHERE id = 'admin-seed-001';
@@ -0,0 +1,51 @@
1
+ -- =============================================================================
2
+ -- Beech CMS v0.4.0 — Doctype Architecture
3
+ --
4
+ -- Breaking change: da singola tabella content_entries (JSON blob) a tabelle
5
+ -- dedicate per Seed con colonne reali tipizzate.
6
+ --
7
+ -- Questa migration:
8
+ -- 1. Rimuove content_entries (i dati di campo migrano a content_{slug})
9
+ -- 2. Rimuove content_fts (sostituita da fts_{slug} per-Seed con trigger)
10
+ -- 3. Crea content_event_log (ex content_entries, solo metadati)
11
+ --
12
+ -- Le tabelle content_{slug} e fts_{slug} NON sono in questa migration.
13
+ -- Vengono create da: beech seed:load
14
+ --
15
+ -- Fresh start (dati non migrati):
16
+ -- npm run db:reset:local → applica tutte le migration
17
+ -- beech seed:load --local → crea tabelle content e FTS per ogni Seed
18
+ -- =============================================================================
19
+
20
+
21
+ -- =============================================================================
22
+ -- 1. RIMOZIONE SCHEMA LEGACY
23
+ -- =============================================================================
24
+
25
+ -- Rimuove la vecchia FTS5 globale (sostituita da fts_{slug} per-Seed)
26
+ DROP TABLE IF EXISTS content_fts;
27
+
28
+ -- Rimuove la tabella monolitica (i dati non vengono migrati — fresh start)
29
+ DROP TABLE IF EXISTS content_entries;
30
+
31
+
32
+ -- =============================================================================
33
+ -- 2. CONTENT EVENT LOG
34
+ -- Tabella leggera per activity feed e stats del dashboard.
35
+ -- NON contiene dati di campo (solo metadati dell'evento CRUD).
36
+ -- Rimpiazza content_entries come sorgente per activity_logs cross-seed.
37
+ -- =============================================================================
38
+
39
+ CREATE TABLE IF NOT EXISTS content_event_log (
40
+ id TEXT NOT NULL PRIMARY KEY, -- UUID v4
41
+ schema_slug TEXT NOT NULL, -- es. "articoli"
42
+ entry_id TEXT NOT NULL, -- ID dell'entry nella tabella content_{slug}
43
+ action TEXT NOT NULL CHECK (action IN ('create', 'update', 'delete')),
44
+ user_id TEXT, -- NULL = azione pubblica (API key)
45
+ details TEXT, -- JSON opzionale: {title, name, ...} per display
46
+ created_at INTEGER NOT NULL DEFAULT (unixepoch())
47
+ );
48
+
49
+ CREATE INDEX IF NOT EXISTS idx_event_log_schema_slug ON content_event_log(schema_slug);
50
+ CREATE INDEX IF NOT EXISTS idx_event_log_created_at ON content_event_log(created_at DESC);
51
+ CREATE INDEX IF NOT EXISTS idx_event_log_entry_id ON content_event_log(entry_id);