includio-cms 0.7.2 → 0.13.1
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 +128 -0
- package/ROADMAP.md +54 -2
- package/dist/admin/api/generate-styles.d.ts +2 -0
- package/dist/admin/api/generate-styles.js +32 -0
- package/dist/admin/api/handler.js +33 -0
- package/dist/admin/api/media-gc.js +10 -4
- package/dist/admin/api/rest/handler.js +17 -0
- package/dist/admin/api/rest/routes/collections.js +25 -13
- package/dist/admin/api/rest/routes/entries.d.ts +1 -1
- package/dist/admin/api/rest/routes/entries.js +10 -10
- package/dist/admin/api/rest/routes/media.d.ts +2 -0
- package/dist/admin/api/rest/routes/media.js +9 -0
- package/dist/admin/api/rest/routes/schema.d.ts +5 -0
- package/dist/admin/api/rest/routes/schema.js +152 -0
- package/dist/admin/api/rest/routes/singletons.d.ts +1 -1
- package/dist/admin/api/rest/routes/singletons.js +8 -7
- package/dist/admin/api/rest/routes/upload.d.ts +2 -0
- package/dist/admin/api/rest/routes/upload.js +28 -0
- package/dist/admin/api/upload.js +13 -0
- package/dist/admin/client/collection/collection-entries.svelte +35 -13
- package/dist/admin/client/entry/entry.svelte +21 -23
- package/dist/admin/client/entry/header/a11y-validator.js +2 -2
- package/dist/admin/client/entry/header/publish-panel.svelte +33 -85
- package/dist/admin/client/entry/header/status-badge.svelte +2 -2
- package/dist/admin/client/entry/header/version-history-sheet.svelte +9 -9
- package/dist/admin/client/entry/header/visibility.svelte +16 -10
- package/dist/admin/client/entry/utils.d.ts +3 -0
- package/dist/admin/client/entry/utils.js +22 -4
- package/dist/admin/client/form/form-submission/form-submission-page.svelte +4 -1
- package/dist/admin/client/form/form-submission/submission-field.svelte +10 -0
- package/dist/admin/client/index.d.ts +1 -0
- package/dist/admin/client/index.js +1 -0
- package/dist/admin/client/maintenance/maintenance-page.svelte +146 -2
- package/dist/admin/client/users/users-page.svelte +5 -6
- package/dist/admin/client/users/users-page.svelte.d.ts +1 -4
- package/dist/admin/components/fields/block-picker-modal.svelte +13 -4
- package/dist/admin/components/fields/blocks-field.svelte +40 -19
- package/dist/admin/components/fields/field-renderer.svelte +4 -8
- package/dist/admin/components/fields/object-field.svelte +7 -12
- package/dist/admin/components/fields/select-field.svelte +8 -2
- package/dist/admin/components/fields/seo-field.svelte +40 -93
- package/dist/admin/components/fields/simple-array-field.svelte +27 -16
- package/dist/admin/components/fields/text-field-wrapper.svelte +52 -197
- package/dist/admin/components/fields/text-field-wrapper.svelte.d.ts +2 -2
- package/dist/admin/components/fields/url-field-wrapper.svelte +15 -25
- package/dist/admin/components/fields/url-field.svelte +61 -72
- package/dist/admin/components/layout/layout-renderer.svelte +10 -4
- package/dist/admin/components/media/file-preview.svelte +10 -1
- package/dist/admin/components/media/file-upload.svelte +5 -1
- package/dist/admin/components/media/file-upload.svelte.d.ts +1 -0
- package/dist/admin/components/media/files-list.svelte +12 -3
- package/dist/admin/components/media/media-library.svelte +109 -37
- package/dist/admin/components/media/media-selector.svelte +90 -16
- package/dist/admin/components/media/tag-sidebar.svelte +10 -6
- package/dist/admin/components/media/tag-sidebar.svelte.d.ts +7 -2
- package/dist/admin/components/tiptap/FigureNodeView.svelte +15 -10
- package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +53 -94
- package/dist/admin/components/tiptap/SlashCommandPopup.svelte +8 -3
- package/dist/admin/components/tiptap/editor-toolbar.svelte +28 -23
- package/dist/admin/components/tiptap/image-dialog.svelte +12 -7
- package/dist/admin/components/tiptap/inline-block-node.js +6 -5
- package/dist/admin/components/tiptap/lang.d.ts +77 -0
- package/dist/admin/components/tiptap/lang.js +170 -0
- package/dist/admin/components/tiptap/link-dialog.svelte +31 -28
- package/dist/admin/components/tiptap/slash-command.js +27 -23
- package/dist/admin/components/tiptap/table-dialog.svelte +9 -4
- package/dist/admin/components/tiptap/video-dialog.svelte +6 -1
- package/dist/admin/remote/email.remote.d.ts +1 -0
- package/dist/admin/remote/email.remote.js +5 -0
- package/dist/admin/remote/entry.remote.d.ts +2 -5
- package/dist/admin/remote/entry.remote.js +23 -28
- package/dist/admin/remote/index.d.ts +1 -0
- package/dist/admin/remote/index.js +1 -0
- package/dist/admin/remote/media.remote.d.ts +15 -0
- package/dist/admin/remote/media.remote.js +18 -2
- package/dist/admin/remote/preview.remote.js +3 -1
- package/dist/admin/utils/entryLabel.js +9 -6
- package/dist/admin/utils/translationStatus.js +1 -2
- package/dist/cli/scaffold/admin.js +34 -2
- package/dist/cms/runtime/api.d.ts +16 -12
- package/dist/cms/runtime/api.js +7 -6
- package/dist/cms/runtime/remote.js +2 -2
- package/dist/cms/runtime/schemas.d.ts +1 -1
- package/dist/cms/runtime/schemas.js +1 -1
- package/dist/cms/runtime/types.d.ts +118 -112
- package/dist/cms/runtime/types.js +0 -12
- package/dist/core/cms.d.ts +3 -1
- package/dist/core/cms.js +30 -0
- package/dist/core/fields/fieldSchemaToTs.js +9 -15
- package/dist/core/fields/formFieldSchemaToTs.js +7 -0
- package/dist/core/server/entries/operations/create.js +10 -4
- package/dist/core/server/entries/operations/get.d.ts +1 -0
- package/dist/core/server/entries/operations/get.js +186 -191
- package/dist/core/server/entries/operations/update.d.ts +6 -7
- package/dist/core/server/entries/operations/update.js +20 -38
- package/dist/core/server/fields/populateEntry.js +16 -52
- package/dist/core/server/fields/resolveImageFields.js +69 -120
- package/dist/core/server/fields/resolveRelationFields.js +30 -51
- package/dist/core/server/fields/resolveRichtextLinks.js +46 -100
- package/dist/core/server/fields/resolveTypographyOrphans.bench.d.ts +1 -0
- package/dist/core/server/fields/resolveTypographyOrphans.bench.js +87 -0
- package/dist/core/server/fields/resolveTypographyOrphans.d.ts +3 -0
- package/dist/core/server/fields/resolveTypographyOrphans.js +128 -0
- package/dist/core/server/fields/resolveUrlFields.js +47 -56
- package/dist/core/server/fields/utils/fixOrphans.d.ts +5 -0
- package/dist/core/server/fields/utils/fixOrphans.js +12 -0
- package/dist/core/server/fields/utils/imageStyles.d.ts +4 -2
- package/dist/core/server/fields/utils/imageStyles.js +41 -25
- package/dist/core/server/fields/utils/resolveMedia.js +1 -6
- package/dist/core/server/forms/submissions/operations/delete.js +26 -2
- package/dist/core/server/forms/submissions/utils/parseMultipart.d.ts +2 -0
- package/dist/core/server/forms/submissions/utils/parseMultipart.js +75 -0
- package/dist/core/server/generator/fields.d.ts +6 -0
- package/dist/core/server/generator/fields.js +43 -5
- package/dist/core/server/generator/formFieldSchemaToString.js +10 -0
- package/dist/core/server/generator/formFields.js +1 -0
- package/dist/core/server/generator/generator.js +98 -30
- package/dist/core/server/media/operations/getFiles.d.ts +5 -0
- package/dist/core/server/media/operations/getFiles.js +6 -0
- package/dist/core/server/media/operations/uploadPrivateFile.d.ts +4 -0
- package/dist/core/server/media/operations/uploadPrivateFile.js +8 -0
- package/dist/core/server/media/styles/operations/batchGenerateStyles.d.ts +16 -0
- package/dist/core/server/media/styles/operations/batchGenerateStyles.js +144 -0
- package/dist/db-postgres/index.js +303 -37
- package/dist/db-postgres/schema/entry.d.ts +0 -94
- package/dist/db-postgres/schema/entry.js +0 -6
- package/dist/db-postgres/schema/entryVersion.d.ts +17 -0
- package/dist/db-postgres/schema/entryVersion.js +1 -0
- package/dist/entity/index.d.ts +9 -4
- package/dist/entity/index.js +24 -24
- package/dist/files-local/index.js +43 -0
- package/dist/paraglide/messages/_index.d.ts +36 -3
- package/dist/paraglide/messages/_index.js +71 -3
- package/dist/paraglide/messages/en.d.ts +5 -0
- package/dist/paraglide/messages/en.js +14 -0
- package/dist/paraglide/messages/pl.d.ts +5 -0
- package/dist/paraglide/messages/pl.js +14 -0
- package/dist/sveltekit/components/preview.svelte +2 -326
- package/dist/sveltekit/components/preview.svelte.d.ts +5 -16
- package/dist/sveltekit/server/index.d.ts +2 -1
- package/dist/sveltekit/server/index.js +2 -1
- package/dist/sveltekit/server/preview.js +4 -7
- package/dist/types/adapters/db.d.ts +15 -1
- package/dist/types/adapters/files.d.ts +6 -0
- package/dist/types/cms.d.ts +5 -0
- package/dist/types/entries.d.ts +54 -18
- package/dist/types/fields.d.ts +14 -24
- package/dist/types/formFields.d.ts +7 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/layout.d.ts +0 -1
- package/dist/types/structured-content.d.ts +5 -0
- package/dist/updates/0.10.0/index.d.ts +2 -0
- package/dist/updates/0.10.0/index.js +15 -0
- package/dist/updates/0.11.0/index.d.ts +2 -0
- package/dist/updates/0.11.0/index.js +12 -0
- package/dist/updates/0.12.0/index.d.ts +2 -0
- package/dist/updates/0.12.0/index.js +12 -0
- package/dist/updates/0.13.0/index.d.ts +2 -0
- package/dist/updates/0.13.0/index.js +10 -0
- package/dist/updates/0.13.1/index.d.ts +2 -0
- package/dist/updates/0.13.1/index.js +20 -0
- package/dist/updates/0.7.3/index.d.ts +2 -0
- package/dist/updates/0.7.3/index.js +10 -0
- package/dist/updates/0.8.0/index.d.ts +2 -0
- package/dist/updates/0.8.0/index.js +18 -0
- package/dist/updates/0.8.0/migrate.d.ts +2 -0
- package/dist/updates/0.8.0/migrate.js +101 -0
- package/dist/updates/0.9.0/index.d.ts +2 -0
- package/dist/updates/0.9.0/index.js +38 -0
- package/dist/updates/index.js +9 -1
- package/package.json +7 -6
- package/dist/admin/components/fields/image-field.svelte +0 -198
- package/dist/admin/components/fields/image-field.svelte.d.ts +0 -8
- package/dist/admin/components/fields/richtext-field.svelte +0 -13
- package/dist/admin/components/fields/richtext-field.svelte.d.ts +0 -8
- package/dist/admin/components/tiptap.svelte +0 -11
- package/dist/admin/components/tiptap.svelte.d.ts +0 -6
- package/dist/core/server/entries/utils/getEntryTranslation.d.ts +0 -1
- package/dist/core/server/entries/utils/getEntryTranslation.js +0 -18
- package/dist/paraglide/messages/hello_world.d.ts +0 -5
- package/dist/paraglide/messages/hello_world.js +0 -33
- package/dist/paraglide/messages/login_hello.d.ts +0 -16
- package/dist/paraglide/messages/login_hello.js +0 -34
- package/dist/paraglide/messages/login_please_login.d.ts +0 -16
- package/dist/paraglide/messages/login_please_login.js +0 -34
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,134 @@
|
|
|
3
3
|
All notable changes to includio-cms are documented here.
|
|
4
4
|
Generated from `src/lib/updates/` — do not edit manually.
|
|
5
5
|
|
|
6
|
+
## 0.13.1 — 2026-03-20
|
|
7
|
+
|
|
8
|
+
Admin UI i18n, codegen cleanup, docs rewrite
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Admin UI i18n — TipTap editor, blocks field, array field, media components support pl/en interface language
|
|
12
|
+
- Inline block accordion labels — show field value in collapsed block header
|
|
13
|
+
- Blocks field UrlFieldData label support
|
|
14
|
+
- Email configuration remote endpoint
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Codegen: remove unused FlatImageFieldData/FlatVideoFieldData, use ImageFieldData/VideoFieldData
|
|
18
|
+
- Layout renderer: hide card header when label is empty
|
|
19
|
+
- Collection entries: improved relation label fetching with UUID validation and multi-version support
|
|
20
|
+
- Users page: client-side email config check, remove server load dependency
|
|
21
|
+
- Entry remote: stricter UUID validation for ids parameter
|
|
22
|
+
- Layout type: remove unused label property from layout node
|
|
23
|
+
|
|
24
|
+
## 0.13.0 — 2026-03-19
|
|
25
|
+
|
|
26
|
+
Private file uploads for form submissions
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
- Private file uploads — form submissions can upload files to a non-public directory with access control
|
|
30
|
+
|
|
31
|
+
## 0.12.0 — 2026-03-18
|
|
32
|
+
|
|
33
|
+
File field type, dataOrderBy, and _url population
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
- File field type with multipart upload, file validation, and rate limiting
|
|
37
|
+
- `dataOrderBy` — sort entries by JSON data fields in queries
|
|
38
|
+
- Auto-populate `_url` field on entries from slug resolver and `pathTemplate`
|
|
39
|
+
|
|
40
|
+
## 0.11.0 — 2026-03-18
|
|
41
|
+
|
|
42
|
+
Codegen overhaul and REST API consolidation
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
- Codegen: flat entry types, inline block types, `countEntries` query helper, simplified API surface
|
|
46
|
+
- REST API: catch-all route handler, file upload endpoint, media endpoint, schema template endpoints
|
|
47
|
+
- Admin scaffold updated for new REST route structure
|
|
48
|
+
|
|
49
|
+
## 0.10.0 — 2026-03-18
|
|
50
|
+
|
|
51
|
+
Remove image, video, richtext field types — consolidate to media and content
|
|
52
|
+
|
|
53
|
+
### Breaking
|
|
54
|
+
- Removed `image` field type — use `media` with `accept: "image/*"` instead
|
|
55
|
+
- Removed `richtext` field type — use `content` (structured ProseMirror JSON) instead
|
|
56
|
+
- Removed `ImageField` and `RichtextField` TypeScript interfaces
|
|
57
|
+
- `image-field.svelte` and `richtext-field.svelte` components removed
|
|
58
|
+
- `tiptap.svelte` (HTML richtext wrapper) removed
|
|
59
|
+
|
|
60
|
+
### Notes
|
|
61
|
+
|
|
62
|
+
No automatic data migration. Replace `type: "image"` with `type: "media", accept: "image/*"` in your collection/singleton configs. Replace `type: "richtext"` with `type: "content"`. Existing stored data (media UUIDs, ProseMirror docs) remains compatible.
|
|
63
|
+
|
|
64
|
+
## 0.9.0 — 2026-03-17
|
|
65
|
+
|
|
66
|
+
Per-language entry versions — separate entry_version per language
|
|
67
|
+
|
|
68
|
+
### Added
|
|
69
|
+
- Per-language entry versions: each language gets its own entry_version with flat data
|
|
70
|
+
- Per-language publication: publish/unpublish each language independently
|
|
71
|
+
- Simplified data format: no more nested `{ pl: "X", en: "Y" }` — values are flat strings
|
|
72
|
+
- Language switching in admin loads different version data
|
|
73
|
+
|
|
74
|
+
### Breaking
|
|
75
|
+
- `entry_version` table: new `lang` column (NOT NULL)
|
|
76
|
+
- `entry` table: removed `published_at`, `published_version_id`, `published_by`, `available_locales` columns
|
|
77
|
+
- `RawEntry`: `publishedVersion`/`draftVersion`/`scheduledVersion` replaced with per-lang `publishedVersions`/`draftVersions`/`scheduledVersions` records
|
|
78
|
+
- `DbEntryVersion`/`DbEntryVersionInsert`: new required `lang` field
|
|
79
|
+
- `translateObject` removed — data is single-language per version
|
|
80
|
+
- Resolve functions (relation, media, url, richtext) expect flat content/richtext values, not `Record<lang, value>`
|
|
81
|
+
- `upsertDraftVersion` and `pruneOldDraftVersions` now require `lang` parameter
|
|
82
|
+
- `unpublishEntry` renamed to `unpublishEntryLang` with required `lang` parameter
|
|
83
|
+
|
|
84
|
+
### Migration
|
|
85
|
+
|
|
86
|
+
```sql
|
|
87
|
+
-- Step 1: Add lang column as nullable
|
|
88
|
+
ALTER TABLE entry_version ADD COLUMN lang TEXT;
|
|
89
|
+
|
|
90
|
+
-- Step 2: Run migration script to split multi-lang versions (scripts/migrate-lang-versions.ts)
|
|
91
|
+
-- After migration, set NOT NULL:
|
|
92
|
+
ALTER TABLE entry_version ALTER COLUMN lang SET NOT NULL;
|
|
93
|
+
|
|
94
|
+
-- Step 3: Create index for performance
|
|
95
|
+
CREATE INDEX idx_entry_version_publish ON entry_version(entry_id, lang, published_at);
|
|
96
|
+
|
|
97
|
+
-- Step 4: Remove entry-level publish columns
|
|
98
|
+
ALTER TABLE entry DROP COLUMN IF EXISTS published_at;
|
|
99
|
+
ALTER TABLE entry DROP COLUMN IF EXISTS published_version_id;
|
|
100
|
+
ALTER TABLE entry DROP COLUMN IF EXISTS published_by;
|
|
101
|
+
ALTER TABLE entry DROP COLUMN IF EXISTS available_locales;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Notes
|
|
105
|
+
|
|
106
|
+
Run the migration script BEFORE setting lang to NOT NULL. See scripts/migrate-lang-versions.ts for data migration.
|
|
107
|
+
|
|
108
|
+
## 0.8.0 — 2026-03-17
|
|
109
|
+
|
|
110
|
+
Flat entry format — remove data wrapper from objects/blocks
|
|
111
|
+
|
|
112
|
+
### Added
|
|
113
|
+
- Flat entry format: objects use `_slug` + spread fields instead of `slug` + `data` wrapper
|
|
114
|
+
- Blocks use `_slug` instead of `slug` with flat fields
|
|
115
|
+
- Entry type unified: `FlatEntry` renamed to `Entry`, old `Entry` removed
|
|
116
|
+
|
|
117
|
+
### Breaking
|
|
118
|
+
- Object field data format: `{ slug, data: {...} }` → `{ _slug, ...fields }`
|
|
119
|
+
- Blocks field data format: `{ _id, slug, data: {...} }` → `{ _id, _slug, ...fields }`
|
|
120
|
+
- `FlatEntry` type removed — use `Entry` instead
|
|
121
|
+
- `flattenEntry`/`flattenFields` removed — data is flat from resolve functions
|
|
122
|
+
|
|
123
|
+
### Notes
|
|
124
|
+
|
|
125
|
+
Run `includio update` to migrate existing entry data in the database.
|
|
126
|
+
|
|
127
|
+
## 0.7.3 — 2026-03-17
|
|
128
|
+
|
|
129
|
+
Typography orphan fix for Polish conjunctions
|
|
130
|
+
|
|
131
|
+
### Added
|
|
132
|
+
- Read-time orphan fix — replace space after single-letter Polish conjunctions (i, w, z, o, u, a) with non-breaking space `\u00A0`
|
|
133
|
+
|
|
6
134
|
## 0.7.2 — 2026-03-16
|
|
7
135
|
|
|
8
136
|
CLI create-user command
|
package/ROADMAP.md
CHANGED
|
@@ -167,14 +167,66 @@
|
|
|
167
167
|
|
|
168
168
|
- [x] `[feature]` `[P1]` CLI `create-user` command — interactive user creation with role assignment <!-- files: src/lib/cli/create-user.ts, src/lib/cli/index.ts -->
|
|
169
169
|
|
|
170
|
-
## 0.
|
|
170
|
+
## 0.7.3 — Typography
|
|
171
|
+
|
|
172
|
+
- [x] `[feature]` `[P1]` Read-time orphan fix — replace space after single-letter Polish conjunctions with `\u00A0` <!-- files: src/lib/core/server/fields/resolveTypographyOrphans.ts, src/lib/core/server/fields/utils/fixOrphans.ts -->
|
|
173
|
+
|
|
174
|
+
## 0.8.0 — Flat entry format
|
|
175
|
+
|
|
176
|
+
- [x] `[breaking]` `[P0]` Flat entry format: objects use `_slug` + spread fields instead of `slug` + `data` wrapper
|
|
177
|
+
- [x] `[breaking]` `[P0]` Blocks use `_slug` instead of `slug` with flat fields
|
|
178
|
+
- [x] `[breaking]` `[P0]` `FlatEntry` type removed — use `Entry` instead
|
|
179
|
+
|
|
180
|
+
## 0.9.0 — Per-language entry versions
|
|
181
|
+
|
|
182
|
+
- [x] `[breaking]` `[P0]` Per-language entry versions: each language gets its own `entry_version` with flat data
|
|
183
|
+
- [x] `[feature]` `[P0]` Per-language publication: publish/unpublish each language independently
|
|
184
|
+
- [x] `[breaking]` `[P0]` Simplified data format: no more nested `{ pl: "X", en: "Y" }` — values are flat strings
|
|
185
|
+
|
|
186
|
+
## 0.10.0 — Field type consolidation
|
|
187
|
+
|
|
188
|
+
- [x] `[breaking]` `[P1]` Remove `image` field type — use `media` with `accept: "image/*"`
|
|
189
|
+
- [x] `[breaking]` `[P1]` Remove `richtext` field type — use `content` (structured JSON)
|
|
190
|
+
- [x] `[chore]` `[P1]` Remove `ImageField`, `RichtextField` interfaces + components
|
|
191
|
+
|
|
192
|
+
## 0.11.0 — Codegen & REST consolidation
|
|
193
|
+
|
|
194
|
+
- [x] `[feature]` `[P1]` Codegen: flat types, inline block types, `countEntries`, simplified API
|
|
195
|
+
- [x] `[feature]` `[P1]` REST API: catch-all route, upload endpoint, media endpoint, schema templates
|
|
196
|
+
- [x] `[chore]` `[P1]` Admin scaffold updated for new REST route structure
|
|
197
|
+
|
|
198
|
+
## 0.12.0 — File field, dataOrderBy, _url
|
|
199
|
+
|
|
200
|
+
- [x] `[feature]` `[P1]` File field type with multipart upload, validation, rate limiting
|
|
201
|
+
- [x] `[feature]` `[P1]` `dataOrderBy` — sort entries by JSON data fields
|
|
202
|
+
- [x] `[feature]` `[P1]` Auto-populate `_url` field on entries from slug resolver
|
|
203
|
+
|
|
204
|
+
## 0.13.0 — Private file uploads
|
|
205
|
+
|
|
206
|
+
- [x] `[feature]` `[P1]` Private file uploads — form submissions can upload files to non-public directory
|
|
207
|
+
|
|
208
|
+
## 0.13.1 — Admin i18n, codegen cleanup, docs
|
|
209
|
+
|
|
210
|
+
- [x] `[feature]` `[P1]` Admin UI i18n — TipTap, blocks, array, media components support pl/en <!-- files: src/lib/admin/components/tiptap/lang.ts -->
|
|
211
|
+
- [x] `[feature]` `[P2]` Inline block accordion labels — show field value in collapsed header <!-- files: src/lib/admin/components/tiptap/InlineBlockNodeView.svelte -->
|
|
212
|
+
- [x] `[feature]` `[P2]` Blocks field UrlFieldData label support <!-- files: src/lib/admin/components/fields/blocks-field.svelte -->
|
|
213
|
+
- [x] `[feature]` `[P2]` Email configuration remote endpoint <!-- files: src/lib/admin/remote/email.remote.ts -->
|
|
214
|
+
- [x] `[fix]` `[P1]` Codegen: remove unused Flat*FieldData types, use ImageFieldData/VideoFieldData <!-- files: src/lib/core/server/generator/fields.ts, src/lib/core/server/generator/generator.ts -->
|
|
215
|
+
- [x] `[fix]` `[P1]` Layout renderer: hide card header when label empty <!-- files: src/lib/admin/components/layout/layout-renderer.svelte -->
|
|
216
|
+
- [x] `[fix]` `[P1]` Collection entries: improved relation label fetching (UUID validation, multi-version) <!-- files: src/lib/admin/client/collection/collection-entries.svelte -->
|
|
217
|
+
- [x] `[fix]` `[P1]` Users page: client-side email config, remove server load <!-- files: src/lib/admin/client/users/users-page.svelte -->
|
|
218
|
+
- [x] `[fix]` `[P2]` Entry remote: stricter UUID validation for ids <!-- files: src/lib/admin/remote/entry.remote.ts -->
|
|
219
|
+
- [x] `[fix]` `[P2]` Layout type: remove unused label property <!-- files: src/lib/types/layout.ts -->
|
|
220
|
+
- [x] `[chore]` `[P1]` Documentation rewrite — new pages for blocks, content, media-field, layout; expanded API, auth, entries, forms, plugins docs
|
|
221
|
+
|
|
222
|
+
## 0.14.0 — SEO module
|
|
171
223
|
|
|
172
224
|
- [ ] `[feature]` `[P1]` SERP preview + character limits for title/description <!-- files: src/lib/admin/components/fields/seo-field.svelte -->
|
|
173
225
|
- [ ] `[feature]` `[P1]` Global SEO settings
|
|
174
226
|
- [ ] `[feature]` `[P1]` Dedicated frontend SEO components <!-- files: src/lib/sveltekit/components/seo.svelte -->
|
|
175
227
|
- [ ] `[feature]` `[P2]` Sitemap generation
|
|
176
228
|
|
|
177
|
-
## 0.
|
|
229
|
+
## 0.15.0 — WCAG/ATAG compliance
|
|
178
230
|
|
|
179
231
|
- [ ] `[chore]` `[P0]` Full WCAG/ATAG audit
|
|
180
232
|
- [ ] `[feature]` `[P0]` Accessibility rework based on audit findings
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { requireRole } from '../remote/middleware/auth.js';
|
|
2
|
+
import { batchGenerateAllStyles } from '../../core/server/media/styles/operations/batchGenerateStyles.js';
|
|
3
|
+
export const POST = async () => {
|
|
4
|
+
requireRole('admin');
|
|
5
|
+
const abort = new AbortController();
|
|
6
|
+
const encoder = new TextEncoder();
|
|
7
|
+
const stream = new ReadableStream({
|
|
8
|
+
async start(controller) {
|
|
9
|
+
try {
|
|
10
|
+
for await (const event of batchGenerateAllStyles(abort.signal)) {
|
|
11
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
catch (e) {
|
|
15
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'error', error: e instanceof Error ? e.message : String(e) })}\n\n`));
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
controller.close();
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
cancel() {
|
|
22
|
+
abort.abort();
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return new Response(stream, {
|
|
26
|
+
headers: {
|
|
27
|
+
'Content-Type': 'text/event-stream',
|
|
28
|
+
'Cache-Control': 'no-cache',
|
|
29
|
+
Connection: 'keep-alive'
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
};
|
|
@@ -5,6 +5,10 @@ import * as replaceHandlers from './replace.js';
|
|
|
5
5
|
import * as inviteHandlers from './invite.js';
|
|
6
6
|
import * as acceptInviteHandlers from './accept-invite.js';
|
|
7
7
|
import * as mediaGcHandlers from './media-gc.js';
|
|
8
|
+
import * as generateStylesHandlers from './generate-styles.js';
|
|
9
|
+
import { requireAuth } from '../remote/middleware/auth.js';
|
|
10
|
+
import { getCMS } from '../../core/cms.js';
|
|
11
|
+
import { lookup } from 'mrmime';
|
|
8
12
|
export function createAdminApiHandler(options) {
|
|
9
13
|
const routes = {
|
|
10
14
|
upload: uploadHandlers,
|
|
@@ -13,14 +17,43 @@ export function createAdminApiHandler(options) {
|
|
|
13
17
|
invite: inviteHandlers,
|
|
14
18
|
'accept-invite': acceptInviteHandlers,
|
|
15
19
|
'media-gc': mediaGcHandlers,
|
|
20
|
+
'generate-styles': generateStylesHandlers,
|
|
16
21
|
...options?.extraRoutes
|
|
17
22
|
};
|
|
23
|
+
const privateMediaGet = async (event) => {
|
|
24
|
+
requireAuth();
|
|
25
|
+
const path = event.params.path;
|
|
26
|
+
const filename = path.replace('media/private/', '');
|
|
27
|
+
if (!filename || filename.includes('/') || filename.includes('..')) {
|
|
28
|
+
return json({ error: 'Invalid filename' }, { status: 400 });
|
|
29
|
+
}
|
|
30
|
+
const adapter = getCMS().filesAdapter;
|
|
31
|
+
if (!adapter.downloadPrivateFile) {
|
|
32
|
+
return json({ error: 'Not supported' }, { status: 501 });
|
|
33
|
+
}
|
|
34
|
+
const file = await adapter.downloadPrivateFile(filename);
|
|
35
|
+
if (!file) {
|
|
36
|
+
return json({ error: 'Not found' }, { status: 404 });
|
|
37
|
+
}
|
|
38
|
+
const contentType = lookup(filename) || 'application/octet-stream';
|
|
39
|
+
return new Response(await file.arrayBuffer(), {
|
|
40
|
+
headers: {
|
|
41
|
+
'Content-Type': contentType,
|
|
42
|
+
'Content-Disposition': `inline; filename="${filename}"`,
|
|
43
|
+
'Cache-Control': 'private, no-store'
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
};
|
|
18
47
|
function handle(method) {
|
|
19
48
|
return (event) => {
|
|
20
49
|
const path = event.params.path;
|
|
21
50
|
if (!path) {
|
|
22
51
|
return json({ error: 'Not found' }, { status: 404 });
|
|
23
52
|
}
|
|
53
|
+
// Handle media/private/[filename] route
|
|
54
|
+
if (path.startsWith('media/private/') && method === 'GET') {
|
|
55
|
+
return privateMediaGet(event);
|
|
56
|
+
}
|
|
24
57
|
const handler = routes[path]?.[method];
|
|
25
58
|
if (!handler) {
|
|
26
59
|
return json({ error: 'Not found' }, { status: 404 });
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { requireRole } from '../remote/middleware/auth.js';
|
|
2
|
-
import {
|
|
2
|
+
import { purgeAllImageStyles } from '../../core/server/media/operations/purgeImageStyles.js';
|
|
3
3
|
import { getReconciliationReport, deleteOrphanedDiskFiles } from '../../core/server/media/operations/reconcileMedia.js';
|
|
4
|
+
import { getStylesStatus } from '../../core/server/media/styles/operations/batchGenerateStyles.js';
|
|
4
5
|
import { json } from '@sveltejs/kit';
|
|
5
6
|
export const GET = async ({ url }) => {
|
|
6
7
|
requireRole('admin');
|
|
7
|
-
const
|
|
8
|
-
|
|
8
|
+
const [stylesStatus, report] = await Promise.all([
|
|
9
|
+
getStylesStatus(),
|
|
10
|
+
getReconciliationReport()
|
|
11
|
+
]);
|
|
9
12
|
return json({
|
|
10
|
-
imageStylesCount:
|
|
13
|
+
imageStylesCount: stylesStatus.existingStyles,
|
|
14
|
+
processableImagesCount: stylesStatus.processableImages,
|
|
15
|
+
expectedStylesCount: stylesStatus.expectedStyles,
|
|
16
|
+
missingStylesCount: stylesStatus.missingStyles,
|
|
11
17
|
orphanedDiskFiles: report.orphanedDisk,
|
|
12
18
|
missingDiskRecords: report.missingDisk
|
|
13
19
|
});
|
|
@@ -5,6 +5,8 @@ import * as languagesRoutes from './routes/languages.js';
|
|
|
5
5
|
import * as collectionsRoutes from './routes/collections.js';
|
|
6
6
|
import * as singletonsRoutes from './routes/singletons.js';
|
|
7
7
|
import * as entriesRoutes from './routes/entries.js';
|
|
8
|
+
import * as uploadRoutes from './routes/upload.js';
|
|
9
|
+
import * as mediaRoutes from './routes/media.js';
|
|
8
10
|
function matchRoute(method, path) {
|
|
9
11
|
// Schema routes
|
|
10
12
|
if (method === 'GET' && path === 'schema') {
|
|
@@ -17,6 +19,15 @@ function matchRoute(method, path) {
|
|
|
17
19
|
if (method === 'GET' && path === 'languages') {
|
|
18
20
|
return { handler: 'languages', params: [] };
|
|
19
21
|
}
|
|
22
|
+
// Upload: POST /upload
|
|
23
|
+
if (method === 'POST' && path === 'upload') {
|
|
24
|
+
return { handler: 'upload', params: [] };
|
|
25
|
+
}
|
|
26
|
+
// Media: GET /media/:id
|
|
27
|
+
const mediaMatch = path.match(/^media\/([^/]+)$/);
|
|
28
|
+
if (method === 'GET' && mediaMatch) {
|
|
29
|
+
return { handler: 'media', params: [mediaMatch[1]] };
|
|
30
|
+
}
|
|
20
31
|
// Entries lifecycle: POST /entries/:id/:action
|
|
21
32
|
const entriesMatch = path.match(/^entries\/([^/]+)\/(publish|unpublish|archive|unarchive)$/);
|
|
22
33
|
if (method === 'POST' && entriesMatch) {
|
|
@@ -53,6 +64,12 @@ export function createRestApiHandler() {
|
|
|
53
64
|
}
|
|
54
65
|
try {
|
|
55
66
|
switch (route.handler) {
|
|
67
|
+
case 'upload':
|
|
68
|
+
return await uploadRoutes.POST(event);
|
|
69
|
+
case 'media': {
|
|
70
|
+
const [mediaId] = route.params;
|
|
71
|
+
return await mediaRoutes.GET(event, mediaId);
|
|
72
|
+
}
|
|
56
73
|
case 'schema': {
|
|
57
74
|
// Pass restPath minus 'schema/' prefix
|
|
58
75
|
const schemaPath = restPath === 'schema' ? '' : restPath.slice(7);
|
|
@@ -2,13 +2,14 @@ import { json } from '@sveltejs/kit';
|
|
|
2
2
|
import { getCMS } from '../../../../core/cms.js';
|
|
3
3
|
import { getRawEntries, getRawEntry, countRawEntries } from '../../../../core/server/entries/operations/get.js';
|
|
4
4
|
import { createEntry } from '../../../../core/server/entries/operations/create.js';
|
|
5
|
-
import { upsertDraftVersion } from '../../../../core/server/entries/operations/update.js';
|
|
5
|
+
import { upsertDraftVersion, updateEntry, updateEntryVersion } from '../../../../core/server/entries/operations/update.js';
|
|
6
6
|
import { deleteEntry } from '../../../../core/server/entries/operations/delete.js';
|
|
7
7
|
export async function GET(event, slug, id) {
|
|
8
8
|
const cms = getCMS();
|
|
9
9
|
if (!cms.collections[slug]) {
|
|
10
10
|
return json({ error: `Collection "${slug}" not found` }, { status: 404 });
|
|
11
11
|
}
|
|
12
|
+
const lang = event.url.searchParams.get('lang') || cms.languages[0] || 'en';
|
|
12
13
|
// GET /collections/:slug/:id — single entry
|
|
13
14
|
if (id) {
|
|
14
15
|
const entry = await getRawEntry({ id, slug, includeArchived: true });
|
|
@@ -21,14 +22,12 @@ export async function GET(event, slug, id) {
|
|
|
21
22
|
type: entry.type,
|
|
22
23
|
createdAt: entry.createdAt,
|
|
23
24
|
updatedAt: entry.updatedAt,
|
|
24
|
-
publishedAt: entry.publishedAt,
|
|
25
25
|
archivedAt: entry.archivedAt,
|
|
26
|
-
availableLocales: entry.availableLocales,
|
|
27
26
|
sortOrder: entry.sortOrder,
|
|
28
|
-
draftData: entry.
|
|
29
|
-
publishedData: entry.
|
|
30
|
-
draftVersionId: entry.
|
|
31
|
-
publishedVersionId: entry.
|
|
27
|
+
draftData: entry.draftVersions[lang]?.data ?? null,
|
|
28
|
+
publishedData: entry.publishedVersions[lang]?.data ?? null,
|
|
29
|
+
draftVersionId: entry.draftVersions[lang]?.id ?? null,
|
|
30
|
+
publishedVersionId: entry.publishedVersions[lang]?.id ?? null
|
|
32
31
|
});
|
|
33
32
|
}
|
|
34
33
|
// GET /collections/:slug — list
|
|
@@ -48,12 +47,10 @@ export async function GET(event, slug, id) {
|
|
|
48
47
|
type: entry.type,
|
|
49
48
|
createdAt: entry.createdAt,
|
|
50
49
|
updatedAt: entry.updatedAt,
|
|
51
|
-
publishedAt: entry.publishedAt,
|
|
52
50
|
archivedAt: entry.archivedAt,
|
|
53
|
-
availableLocales: entry.availableLocales,
|
|
54
51
|
sortOrder: entry.sortOrder,
|
|
55
|
-
draftData: entry.
|
|
56
|
-
publishedData: entry.
|
|
52
|
+
draftData: entry.draftVersions[lang]?.data ?? null,
|
|
53
|
+
publishedData: entry.publishedVersions[lang]?.data ?? null
|
|
57
54
|
}));
|
|
58
55
|
return json({ items, total, limit, offset });
|
|
59
56
|
}
|
|
@@ -62,11 +59,25 @@ export async function POST(event, slug) {
|
|
|
62
59
|
if (!cms.collections[slug]) {
|
|
63
60
|
return json({ error: `Collection "${slug}" not found` }, { status: 404 });
|
|
64
61
|
}
|
|
62
|
+
const lang = event.url.searchParams.get('lang') || cms.languages[0] || 'en';
|
|
65
63
|
const body = await event.request.json().catch(() => null);
|
|
66
64
|
const entry = await createEntry({ slug, type: 'collection' });
|
|
67
65
|
// If data provided, save as draft
|
|
68
66
|
if (body?.data && typeof body.data === 'object') {
|
|
69
|
-
await upsertDraftVersion(entry.id, body.data, { skipValidation: true });
|
|
67
|
+
await upsertDraftVersion(entry.id, body.data, lang, { skipValidation: true });
|
|
68
|
+
}
|
|
69
|
+
// Auto-publish if requested
|
|
70
|
+
if (body?.publish) {
|
|
71
|
+
const raw = await getRawEntry({ id: entry.id, slug });
|
|
72
|
+
const draftVersion = raw?.draftVersions[lang];
|
|
73
|
+
if (draftVersion) {
|
|
74
|
+
const user = event.locals.user;
|
|
75
|
+
await updateEntryVersion(draftVersion.id, {
|
|
76
|
+
publishedAt: new Date(),
|
|
77
|
+
publishedBy: user?.id ?? 'api'
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return json({ id: entry.id, slug: entry.slug, published: true }, { status: 201 });
|
|
70
81
|
}
|
|
71
82
|
return json({ id: entry.id, slug: entry.slug }, { status: 201 });
|
|
72
83
|
}
|
|
@@ -79,11 +90,12 @@ export async function PUT(event, slug, id) {
|
|
|
79
90
|
if (!entry) {
|
|
80
91
|
return json({ error: 'Entry not found' }, { status: 404 });
|
|
81
92
|
}
|
|
93
|
+
const lang = event.url.searchParams.get('lang') || cms.languages[0] || 'en';
|
|
82
94
|
const body = await event.request.json().catch(() => null);
|
|
83
95
|
if (!body?.data || typeof body.data !== 'object') {
|
|
84
96
|
return json({ error: 'Request body must contain "data" object' }, { status: 400 });
|
|
85
97
|
}
|
|
86
|
-
const version = await upsertDraftVersion(entry.id, body.data, { skipValidation: true });
|
|
98
|
+
const version = await upsertDraftVersion(entry.id, body.data, lang, { skipValidation: true });
|
|
87
99
|
return json({
|
|
88
100
|
id: entry.id,
|
|
89
101
|
versionId: version.id,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { type RequestEvent } from '@sveltejs/kit';
|
|
2
|
-
export declare function POST(
|
|
2
|
+
export declare function POST(event: RequestEvent, id: string, action: string): Promise<Response>;
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { json } from '@sveltejs/kit';
|
|
2
|
-
import {
|
|
2
|
+
import { getCMS } from '../../../../core/cms.js';
|
|
3
3
|
import { getRawEntryOrThrow } from '../../../../core/server/entries/operations/get.js';
|
|
4
|
-
import { updateEntry,
|
|
5
|
-
export async function POST(
|
|
6
|
-
const
|
|
4
|
+
import { updateEntry, updateEntryVersion, unpublishEntryLang } from '../../../../core/server/entries/operations/update.js';
|
|
5
|
+
export async function POST(event, id, action) {
|
|
6
|
+
const user = event.locals.user;
|
|
7
|
+
const lang = event.url.searchParams.get('lang') || getCMS().languages[0] || 'en';
|
|
7
8
|
switch (action) {
|
|
8
9
|
case 'publish': {
|
|
9
10
|
const entry = await getRawEntryOrThrow({ id, includeArchived: false });
|
|
10
|
-
const draftVersion = entry.
|
|
11
|
+
const draftVersion = entry.draftVersions[lang];
|
|
11
12
|
if (!draftVersion) {
|
|
12
13
|
return json({ error: 'No draft version to publish' }, { status: 400 });
|
|
13
14
|
}
|
|
14
|
-
await
|
|
15
|
-
publishedVersionId: draftVersion.id,
|
|
15
|
+
await updateEntryVersion(draftVersion.id, {
|
|
16
16
|
publishedAt: new Date(),
|
|
17
|
-
publishedBy: user.id
|
|
18
|
-
archivedAt: null
|
|
17
|
+
publishedBy: user.id
|
|
19
18
|
});
|
|
19
|
+
await updateEntry(id, { archivedAt: null });
|
|
20
20
|
return json({ success: true, publishedVersionId: draftVersion.id });
|
|
21
21
|
}
|
|
22
22
|
case 'unpublish': {
|
|
23
|
-
await
|
|
23
|
+
await unpublishEntryLang(id, lang);
|
|
24
24
|
return json({ success: true });
|
|
25
25
|
}
|
|
26
26
|
case 'archive': {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { json } from '@sveltejs/kit';
|
|
2
|
+
import { getFile } from '../../../../core/server/media/operations/getFiles.js';
|
|
3
|
+
export async function GET(_event, id) {
|
|
4
|
+
const file = await getFile(id);
|
|
5
|
+
if (!file) {
|
|
6
|
+
return json({ error: 'Media file not found' }, { status: 404 });
|
|
7
|
+
}
|
|
8
|
+
return json(file);
|
|
9
|
+
}
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
import { type RequestEvent } from '@sveltejs/kit';
|
|
2
|
+
import type { Field } from '../../../../types/fields.js';
|
|
3
|
+
export declare function generateTemplate(fields: Field[], languages: string[]): {
|
|
4
|
+
template: Record<string, unknown>;
|
|
5
|
+
meta: Record<string, string>;
|
|
6
|
+
};
|
|
2
7
|
export declare function GET(event: RequestEvent): Promise<Response>;
|