@cardinali-studio/adminkit 2.2.25 → 2.2.34

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 (59) hide show
  1. package/dist/api.d.ts +19 -0
  2. package/dist/api.js +3 -0
  3. package/dist/app/App.js +22 -8
  4. package/dist/app/app.css +3 -3
  5. package/dist/app/delivery.d.ts +11 -0
  6. package/dist/app/delivery.js +46 -0
  7. package/dist/app/index.d.ts +4 -1
  8. package/dist/app/index.js +3 -2
  9. package/dist/app/lib/bookings.js +5 -0
  10. package/dist/app/lib/deliveries.d.ts +8 -0
  11. package/dist/app/lib/deliveries.js +7 -0
  12. package/dist/app/lib/delivery-network.d.ts +25 -0
  13. package/dist/app/lib/delivery-network.js +159 -0
  14. package/dist/app/lib/delivery-upload.d.ts +29 -11
  15. package/dist/app/lib/delivery-upload.js +408 -131
  16. package/dist/app/lib/home.js +7 -4
  17. package/dist/app/lib/pages.js +2 -1
  18. package/dist/app/lib/publish.d.ts +5 -0
  19. package/dist/app/lib/publish.js +61 -4
  20. package/dist/app/lib/requests.js +6 -0
  21. package/dist/app/lib/settings.js +7 -3
  22. package/dist/app/lib/upload-queue.d.ts +21 -3
  23. package/dist/app/lib/upload-queue.js +76 -19
  24. package/dist/app/routes/Deliveries.js +108 -29
  25. package/dist/app/routes/PageActions.d.ts +1 -1
  26. package/dist/app/routes/PageActions.js +5 -2
  27. package/dist/app/routes/PageEditor.js +6 -4
  28. package/dist/app/routes/PageSeoEditor.js +6 -1
  29. package/dist/app/routes/PagesList.js +5 -1
  30. package/dist/app/routes/UploadDock.js +6 -2
  31. package/dist/app/site.d.ts +13 -0
  32. package/dist/direct-publication.d.ts +20 -0
  33. package/dist/direct-publication.js +75 -0
  34. package/dist/functions/_shared/b2.js +22 -3
  35. package/dist/functions/_shared/delivery-crypto.js +5 -0
  36. package/dist/functions/_shared/delivery-limits.d.ts +9 -0
  37. package/dist/functions/_shared/delivery-limits.js +29 -0
  38. package/dist/functions/_shared/delivery-manifest-v2.sql +49 -0
  39. package/dist/functions/_shared/delivery-manifest.js +392 -0
  40. package/dist/functions/_shared/delivery-purge.js +17 -1
  41. package/dist/functions/api/deliveries.js +177 -49
  42. package/dist/functions/api/delivery/image.js +11 -6
  43. package/dist/functions/api/delivery/multipart.js +196 -64
  44. package/dist/functions/delivery-public/assets/client.js +246 -0
  45. package/dist/functions/delivery-public/assets/delivery.css +297 -0
  46. package/dist/functions/delivery-public/assets/gallery.js +243 -0
  47. package/dist/functions/delivery-public/assets/stage.js +107 -0
  48. package/dist/functions/delivery-public/index.js +637 -0
  49. package/dist/functions/delivery-public/migrations/0002_deliveries.sql +128 -0
  50. package/dist/functions/delivery-public/migrations/0003_delivery_quick_hero.sql +34 -0
  51. package/dist/functions/delivery-public/migrations/0004_delivery_file_expiry.sql +9 -0
  52. package/dist/functions/delivery-public/migrations/0006_delivery_manifest_v2.sql +49 -0
  53. package/dist/functions/delivery-public/migrations/0007_delivery_link_aliases.sql +8 -0
  54. package/dist/functions/delivery-public/migrations/0008_delivery_web_format.sql +2 -0
  55. package/dist/functions/delivery-public/migrations/0009_delivery_download_labels.sql +2 -0
  56. package/dist/functions/index.js +3 -0
  57. package/dist/index.d.ts +1 -0
  58. package/dist/index.js +1 -0
  59. package/package.json +10 -3
@@ -0,0 +1,128 @@
1
+ -- Consegne fotografiche private.
2
+ --
3
+ -- Non sono contenuto editoriale: non passano dal repo, dallo schema del front matter
4
+ -- o da una build Hugo. D1 è la sorgente dello stato; Backblaze B2 contiene soltanto
5
+ -- oggetti immutabili identificati dagli id di queste tabelle.
6
+
7
+ CREATE TABLE IF NOT EXISTS deliveries (
8
+ id TEXT PRIMARY KEY,
9
+ client_name TEXT NOT NULL,
10
+ title TEXT NOT NULL,
11
+ description TEXT NOT NULL DEFAULT '',
12
+ license_notes TEXT NOT NULL DEFAULT '',
13
+ cta_enabled INTEGER NOT NULL DEFAULT 0,
14
+ cta_label TEXT NOT NULL DEFAULT 'Lascia una recensione',
15
+ cta_url TEXT NOT NULL DEFAULT '',
16
+ expires_at TEXT,
17
+ status TEXT NOT NULL DEFAULT 'draft'
18
+ CHECK (status IN ('draft','uploading','active','archived','deleted')),
19
+ version INTEGER NOT NULL DEFAULT 1,
20
+ created_at TEXT NOT NULL,
21
+ updated_at TEXT NOT NULL,
22
+ activated_at TEXT,
23
+ deleted_at TEXT,
24
+ purge_after TEXT
25
+ );
26
+
27
+ CREATE INDEX IF NOT EXISTS deliveries_status_updated
28
+ ON deliveries(status, updated_at DESC);
29
+ CREATE INDEX IF NOT EXISTS deliveries_expiry
30
+ ON deliveries(expires_at) WHERE expires_at IS NOT NULL;
31
+
32
+ CREATE TABLE IF NOT EXISTS delivery_packages (
33
+ id TEXT PRIMARY KEY,
34
+ delivery_id TEXT NOT NULL REFERENCES deliveries(id) ON DELETE CASCADE,
35
+ label TEXT NOT NULL,
36
+ position INTEGER NOT NULL DEFAULT 0,
37
+ gallery_enabled INTEGER NOT NULL DEFAULT 0,
38
+ web_enabled INTEGER NOT NULL DEFAULT 0,
39
+ state TEXT NOT NULL DEFAULT 'uploading'
40
+ CHECK (state IN ('uploading','ready','failed','deleted')),
41
+ storage_prefix TEXT NOT NULL UNIQUE,
42
+ source_fingerprint TEXT NOT NULL DEFAULT '',
43
+ originals_key TEXT,
44
+ web_key TEXT,
45
+ thumbs_key TEXT,
46
+ manifest_key TEXT,
47
+ manifest_json TEXT,
48
+ file_count INTEGER NOT NULL DEFAULT 0,
49
+ source_bytes INTEGER NOT NULL DEFAULT 0,
50
+ stored_bytes INTEGER NOT NULL DEFAULT 0,
51
+ created_at TEXT NOT NULL,
52
+ updated_at TEXT NOT NULL,
53
+ deleted_at TEXT,
54
+ purge_after TEXT,
55
+ originals_purge_after TEXT
56
+ );
57
+
58
+ CREATE INDEX IF NOT EXISTS delivery_packages_delivery
59
+ ON delivery_packages(delivery_id, position);
60
+
61
+ CREATE TABLE IF NOT EXISTS delivery_links (
62
+ id TEXT PRIMARY KEY,
63
+ delivery_id TEXT NOT NULL REFERENCES deliveries(id) ON DELETE CASCADE,
64
+ label TEXT NOT NULL DEFAULT 'Link principale',
65
+ token_hash TEXT NOT NULL UNIQUE,
66
+ token_ciphertext TEXT NOT NULL,
67
+ pin_ciphertext TEXT,
68
+ expires_at TEXT,
69
+ revoked_at TEXT,
70
+ created_at TEXT NOT NULL,
71
+ opened_count INTEGER NOT NULL DEFAULT 0,
72
+ last_opened_at TEXT,
73
+ download_count INTEGER NOT NULL DEFAULT 0,
74
+ last_download_at TEXT
75
+ );
76
+
77
+ CREATE INDEX IF NOT EXISTS delivery_links_delivery
78
+ ON delivery_links(delivery_id, created_at);
79
+
80
+ CREATE TABLE IF NOT EXISTS delivery_uploads (
81
+ id TEXT PRIMARY KEY,
82
+ delivery_id TEXT NOT NULL REFERENCES deliveries(id) ON DELETE CASCADE,
83
+ package_id TEXT NOT NULL REFERENCES delivery_packages(id) ON DELETE CASCADE,
84
+ variant TEXT NOT NULL CHECK (variant IN ('originals','web','thumbs')),
85
+ object_key TEXT NOT NULL,
86
+ b2_upload_id TEXT NOT NULL,
87
+ bytes INTEGER NOT NULL DEFAULT 0,
88
+ state TEXT NOT NULL DEFAULT 'uploading'
89
+ CHECK (state IN ('uploading','completed','aborted','failed')),
90
+ created_at TEXT NOT NULL,
91
+ updated_at TEXT NOT NULL,
92
+ completed_at TEXT,
93
+ UNIQUE(package_id, variant)
94
+ );
95
+
96
+ CREATE TABLE IF NOT EXISTS delivery_upload_parts (
97
+ upload_id TEXT NOT NULL REFERENCES delivery_uploads(id) ON DELETE CASCADE,
98
+ part_number INTEGER NOT NULL,
99
+ etag TEXT NOT NULL,
100
+ digest TEXT,
101
+ size INTEGER NOT NULL,
102
+ created_at TEXT NOT NULL,
103
+ PRIMARY KEY(upload_id, part_number)
104
+ );
105
+
106
+ CREATE TABLE IF NOT EXISTS delivery_events (
107
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
108
+ delivery_id TEXT NOT NULL REFERENCES deliveries(id) ON DELETE CASCADE,
109
+ link_id TEXT REFERENCES delivery_links(id) ON DELETE SET NULL,
110
+ package_id TEXT REFERENCES delivery_packages(id) ON DELETE SET NULL,
111
+ kind TEXT NOT NULL CHECK (kind IN ('open','download_started')),
112
+ variant TEXT,
113
+ created_at TEXT NOT NULL
114
+ );
115
+
116
+ CREATE INDEX IF NOT EXISTS delivery_events_delivery
117
+ ON delivery_events(delivery_id, created_at DESC);
118
+
119
+ -- Tentativi PIN: contiene soltanto un hash salato dell'IP e si può potare a ogni
120
+ -- richiesta. Nessun indirizzo di rete viene conservato in chiaro.
121
+ CREATE TABLE IF NOT EXISTS delivery_pin_throttle (
122
+ link_id TEXT NOT NULL REFERENCES delivery_links(id) ON DELETE CASCADE,
123
+ ip_hash TEXT NOT NULL,
124
+ seen_at INTEGER NOT NULL
125
+ );
126
+
127
+ CREATE INDEX IF NOT EXISTS delivery_pin_throttle_lookup
128
+ ON delivery_pin_throttle(link_id, ip_hash, seen_at);
@@ -0,0 +1,34 @@
1
+ -- Transfer rapidi, hero specifiche e fallback globali.
2
+ --
3
+ -- `hero_json` contiene soltanto riferimenti ordinati:
4
+ -- - package: una WebP già presente dentro web.zip;
5
+ -- - object: una piccola derivata dedicata registrata in delivery_hero_objects.
6
+ -- Gli oggetti dedicati nascono pending: se il browser scompare prima del commit,
7
+ -- l'housekeeping li elimina invece di lasciarli per sempre nel bucket.
8
+
9
+ ALTER TABLE deliveries ADD COLUMN hero_json TEXT NOT NULL DEFAULT '[]';
10
+
11
+ CREATE TABLE IF NOT EXISTS delivery_hero_objects (
12
+ id TEXT PRIMARY KEY,
13
+ delivery_id TEXT NOT NULL REFERENCES deliveries(id) ON DELETE CASCADE,
14
+ package_id TEXT REFERENCES delivery_packages(id) ON DELETE SET NULL,
15
+ object_key TEXT NOT NULL UNIQUE,
16
+ source_name TEXT NOT NULL DEFAULT '',
17
+ width INTEGER NOT NULL DEFAULT 0,
18
+ height INTEGER NOT NULL DEFAULT 0,
19
+ bytes INTEGER NOT NULL DEFAULT 0,
20
+ state TEXT NOT NULL DEFAULT 'pending'
21
+ CHECK (state IN ('pending','ready','deleted')),
22
+ created_at TEXT NOT NULL,
23
+ updated_at TEXT NOT NULL,
24
+ purge_after TEXT
25
+ );
26
+
27
+ CREATE INDEX IF NOT EXISTS delivery_hero_objects_cleanup
28
+ ON delivery_hero_objects(state, updated_at, purge_after);
29
+
30
+ CREATE TABLE IF NOT EXISTS delivery_settings (
31
+ key TEXT PRIMARY KEY,
32
+ value_json TEXT NOT NULL,
33
+ updated_at TEXT NOT NULL
34
+ );
@@ -0,0 +1,9 @@
1
+ -- La pagina della consegna resta raggiungibile anche dopo la scadenza dei byte.
2
+ -- `expires_at` continua a rappresentare l'eventuale scadenza del link/pagina
3
+ -- legacy; `files_expires_at` governa esclusivamente la disponibilità su B2.
4
+
5
+ ALTER TABLE deliveries ADD COLUMN files_expires_at TEXT;
6
+
7
+ CREATE INDEX IF NOT EXISTS deliveries_files_expiry
8
+ ON deliveries(files_expires_at)
9
+ WHERE files_expires_at IS NOT NULL;
@@ -0,0 +1,49 @@
1
+ -- Apply to the consumer's delivery DB AFTER its delivery_packages migration.
2
+ -- Separate from AdminKit's editorial CONTENT_DB migrations. Idempotent.
3
+ CREATE TABLE IF NOT EXISTS delivery_manifest_state (
4
+ package_id TEXT PRIMARY KEY REFERENCES delivery_packages(id) ON DELETE CASCADE,
5
+ state TEXT NOT NULL DEFAULT 'uploading' CHECK(state IN ('uploading','sealed','ready')),
6
+ created_at TEXT NOT NULL,
7
+ writer_token TEXT,
8
+ writer_until INTEGER NOT NULL DEFAULT 0,
9
+ metadata_bytes INTEGER NOT NULL DEFAULT 0 CHECK(metadata_bytes BETWEEN 0 AND 67108864),
10
+ cursor INTEGER NOT NULL DEFAULT 0,
11
+ page_count INTEGER NOT NULL DEFAULT 0,
12
+ root_json TEXT
13
+ );
14
+ CREATE TABLE IF NOT EXISTS delivery_manifest_entries (
15
+ package_id TEXT NOT NULL REFERENCES delivery_manifest_state(package_id) ON DELETE CASCADE,
16
+ entry_id INTEGER NOT NULL CHECK(entry_id>=0),
17
+ name TEXT NOT NULL,
18
+ has_preview INTEGER NOT NULL DEFAULT 0,
19
+ requires_variant INTEGER NOT NULL DEFAULT 0,
20
+ variant_done INTEGER NOT NULL DEFAULT 0,
21
+ source_bytes INTEGER NOT NULL CHECK(source_bytes>=0),
22
+ entry_json TEXT NOT NULL CHECK(length(CAST(entry_json AS BLOB))<=131072),
23
+ PRIMARY KEY(package_id,entry_id), UNIQUE(package_id,name)
24
+ );
25
+ CREATE INDEX IF NOT EXISTS delivery_manifest_gallery ON delivery_manifest_entries(package_id,has_preview,entry_id);
26
+ CREATE TABLE IF NOT EXISTS delivery_manifest_receipts (
27
+ package_id TEXT NOT NULL REFERENCES delivery_manifest_state(package_id) ON DELETE CASCADE,
28
+ batch_id TEXT NOT NULL, digest TEXT NOT NULL,
29
+ PRIMARY KEY(package_id,batch_id)
30
+ );
31
+ CREATE TABLE IF NOT EXISTS delivery_manifest_objects (
32
+ package_id TEXT NOT NULL REFERENCES delivery_manifest_state(package_id) ON DELETE CASCADE,
33
+ page_number INTEGER NOT NULL,
34
+ object_key TEXT NOT NULL UNIQUE, digest TEXT NOT NULL,
35
+ end_cursor INTEGER NOT NULL, entry_count INTEGER NOT NULL,
36
+ verified INTEGER NOT NULL DEFAULT 0,
37
+ PRIMARY KEY(package_id,page_number)
38
+ );
39
+
40
+ CREATE TABLE IF NOT EXISTS delivery_manifest_claims (
41
+ package_id TEXT NOT NULL REFERENCES delivery_manifest_state(package_id) ON DELETE CASCADE,
42
+ phase TEXT NOT NULL, entry_id INTEGER NOT NULL,
43
+ PRIMARY KEY(package_id,phase,entry_id)
44
+ );
45
+ CREATE TABLE IF NOT EXISTS delivery_checkpoints (
46
+ package_id TEXT PRIMARY KEY REFERENCES delivery_manifest_state(package_id) ON DELETE CASCADE,
47
+ offset INTEGER NOT NULL,
48
+ checkpoint_json TEXT NOT NULL CHECK(length(CAST(checkpoint_json AS BLOB))<=32768)
49
+ );
@@ -0,0 +1,8 @@
1
+ -- Old addresses remain aliases of the SAME link: PIN, expiry, revocation and
2
+ -- counters are shared. Only hashes are retained, never plaintext bearer tokens.
3
+ CREATE TABLE IF NOT EXISTS delivery_link_aliases (
4
+ token_hash TEXT PRIMARY KEY,
5
+ link_id TEXT NOT NULL REFERENCES delivery_links(id) ON DELETE CASCADE,
6
+ created_at TEXT NOT NULL
7
+ );
8
+ CREATE INDEX IF NOT EXISTS delivery_link_aliases_link ON delivery_link_aliases(link_id);
@@ -0,0 +1,2 @@
1
+ -- Existing archives remain WebP; format is immutable after package creation.
2
+ ALTER TABLE delivery_packages ADD COLUMN web_format TEXT NOT NULL DEFAULT 'webp' CHECK (web_format IN ('webp','jpg'));
@@ -0,0 +1,2 @@
1
+ ALTER TABLE deliveries ADD COLUMN download_originals_label TEXT NOT NULL DEFAULT '';
2
+ ALTER TABLE deliveries ADD COLUMN download_web_label TEXT NOT NULL DEFAULT '';
@@ -114,6 +114,9 @@ export async function handleAdminApi(context, config) {
114
114
  /* Le barre di troppo e finali non fanno una rotta diversa: `/assets/` e `/assets` sono la
115
115
  * stessa cosa per chi la scrive, e farle divergere produce un 404 che sembra un guasto. */
116
116
  const path = url.pathname.slice(PREFIX.length).split("/").filter(Boolean).join("/");
117
+ if (config?.scope === "delivery" && !["deliveries", "delivery/hero", "delivery/image", "delivery/multipart"].includes(path)) {
118
+ return json({ ok: false, code: "feature_inactive", error: "Funzione non disponibile in questa installazione." }, 404);
119
+ }
117
120
  const route = ROUTES[path];
118
121
  if (!route) return json({ ok: false, error: "rotta sconosciuta" }, 404);
119
122
  if (path.startsWith("content/") && contentPersistenceMode() !== "d1") {
package/dist/index.d.ts CHANGED
@@ -15,3 +15,4 @@ export * from "./release.js";
15
15
  export * from "./deployment.js";
16
16
  export * from "./media.js";
17
17
  export * from "./site-access.js";
18
+ export * from "./direct-publication.js";
package/dist/index.js CHANGED
@@ -15,3 +15,4 @@ export * from "./release.js";
15
15
  export * from "./deployment.js";
16
16
  export * from "./media.js";
17
17
  export * from "./site-access.js";
18
+ export * from "./direct-publication.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardinali-studio/adminkit",
3
- "version": "2.2.25",
3
+ "version": "2.2.34",
4
4
  "type": "module",
5
5
  "description": "Motore riusabile di un pannello di amministrazione per siti Hugo su Cloudflare Pages: codec del front matter guidato dallo schema, form React, libreria media su R2. NIENTE di specifico di un sito qui dentro.",
6
6
  "license": "0BSD",
@@ -35,7 +35,11 @@
35
35
  "types": "./dist/materializer.d.ts",
36
36
  "default": "./dist/materializer.js"
37
37
  },
38
- "./functions/*": "./dist/functions/*"
38
+ "./functions/*": "./dist/functions/*",
39
+ "./delivery": {
40
+ "types": "./dist/app/delivery.d.ts",
41
+ "default": "./dist/app/delivery.js"
42
+ }
39
43
  },
40
44
  "files": [
41
45
  "dist"
@@ -43,7 +47,8 @@
43
47
  "scripts": {
44
48
  "build": "rm -rf dist && tsc -p tsconfig.json && cp src/theme.css dist/theme.css && cp src/app/app.css dist/app/app.css && cp src/app/next.css dist/app/next.css && cp -R src/functions dist/functions && cp -R src/migrations dist/migrations",
45
49
  "test": "node --test \"test/*.test.js\"",
46
- "check": "npm run build && npm test"
50
+ "check": "npm run build && npm test",
51
+ "test:e2e": "playwright test"
47
52
  },
48
53
  "publishConfig": {
49
54
  "access": "public"
@@ -91,8 +96,10 @@
91
96
  "@dnd-kit/modifiers": "^9.0.0",
92
97
  "@dnd-kit/sortable": "^10.0.0",
93
98
  "@dnd-kit/utilities": "^3.2.2",
99
+ "@playwright/test": "^1.63.0",
94
100
  "@types/react": "^19.2.18",
95
101
  "@types/react-dom": "^19.2.4",
102
+ "esbuild": "^0.28.2",
96
103
  "react": "^19.2.8",
97
104
  "react-dom": "^19.2.8",
98
105
  "react-hook-form": "^7.85.0",