@xleddyl/nuxt-cms 0.9.3 → 0.10.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.
- package/README.md +1 -1
- package/dist/module.d.mts +20 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +1037 -148
- package/dist/runtime/app/blocks/CmsBlocks.d.vue.ts +1 -1
- package/dist/runtime/app/blocks/CmsBlocks.vue +57 -9
- package/dist/runtime/app/blocks/CmsBlocks.vue.d.ts +1 -1
- package/dist/runtime/app/blocks/CmsField.d.ts +24 -0
- package/dist/runtime/app/blocks/CmsField.js +16 -0
- package/dist/runtime/app/blocks/node.d.ts +37 -0
- package/dist/runtime/app/blocks/node.js +17 -0
- package/dist/runtime/app/blocks/resolve.d.ts +18 -0
- package/dist/runtime/app/blocks/resolve.js +34 -0
- package/dist/runtime/app/components/cms/BlockPicker.d.vue.ts +22 -0
- package/dist/runtime/app/components/cms/BlockPicker.vue +87 -0
- package/dist/runtime/app/components/cms/BlockPicker.vue.d.ts +22 -0
- package/dist/runtime/app/components/cms/BlockPickerPopover.d.vue.ts +38 -0
- package/dist/runtime/app/components/cms/BlockPickerPopover.vue +44 -0
- package/dist/runtime/app/components/cms/BlockPickerPopover.vue.d.ts +38 -0
- package/dist/runtime/app/components/cms/BlockTree.d.vue.ts +29 -0
- package/dist/runtime/app/components/cms/BlockTree.vue +322 -0
- package/dist/runtime/app/components/cms/BlockTree.vue.d.ts +29 -0
- package/dist/runtime/app/components/cms/BlocksField.vue +569 -218
- package/dist/runtime/app/components/cms/BrandMark.vue +7 -13
- package/dist/runtime/app/components/cms/ConfirmGroups.vue +15 -13
- package/dist/runtime/app/components/cms/{BlockPalette.vue.d.ts → EditorHistory.d.vue.ts} +6 -5
- package/dist/runtime/app/components/cms/EditorHistory.vue +34 -0
- package/dist/runtime/app/components/cms/{BlockPalette.d.vue.ts → EditorHistory.vue.d.ts} +6 -5
- package/dist/runtime/app/components/cms/EntryDrawer.d.vue.ts +11 -5
- package/dist/runtime/app/components/cms/EntryDrawer.vue +183 -152
- package/dist/runtime/app/components/cms/EntryDrawer.vue.d.ts +11 -5
- package/dist/runtime/app/components/cms/EntryForm.d.vue.ts +16 -4
- package/dist/runtime/app/components/cms/EntryForm.vue +106 -146
- package/dist/runtime/app/components/cms/EntryForm.vue.d.ts +16 -4
- package/dist/runtime/app/components/cms/FieldInput.vue +10 -2
- package/dist/runtime/app/components/cms/FormSections.d.vue.ts +22 -0
- package/dist/runtime/app/components/cms/FormSections.vue +71 -0
- package/dist/runtime/app/components/cms/FormSections.vue.d.ts +22 -0
- package/dist/runtime/app/components/cms/ImportModal.vue +46 -58
- package/dist/runtime/app/components/cms/LinkField.d.vue.ts +16 -0
- package/dist/runtime/app/components/cms/LinkField.vue +250 -0
- package/dist/runtime/app/components/cms/LinkField.vue.d.ts +16 -0
- package/dist/runtime/app/components/cms/ListFilters.vue +10 -12
- package/dist/runtime/app/components/cms/LocaleSwitch.d.vue.ts +2 -1
- package/dist/runtime/app/components/cms/LocaleSwitch.vue +29 -5
- package/dist/runtime/app/components/cms/LocaleSwitch.vue.d.ts +2 -1
- package/dist/runtime/app/components/cms/MediaDetail.d.vue.ts +2 -0
- package/dist/runtime/app/components/cms/MediaDetail.vue +112 -83
- package/dist/runtime/app/components/cms/MediaDetail.vue.d.ts +2 -0
- package/dist/runtime/app/components/cms/MediaField.vue +35 -25
- package/dist/runtime/app/components/cms/MediaFolderPicker.vue +26 -71
- package/dist/runtime/app/components/cms/MediaGallery.d.vue.ts +1 -0
- package/dist/runtime/app/components/cms/MediaGallery.vue +442 -419
- package/dist/runtime/app/components/cms/MediaGallery.vue.d.ts +1 -0
- package/dist/runtime/app/components/cms/MediaThumb.vue +9 -25
- package/dist/runtime/app/components/cms/MediaUpload.vue +1 -1
- package/dist/runtime/app/components/cms/{BlockOutline.d.vue.ts → NestedBlockList.d.vue.ts} +5 -5
- package/dist/runtime/app/components/cms/NestedBlockList.vue +87 -0
- package/dist/runtime/app/components/cms/{BlockOutline.vue.d.ts → NestedBlockList.vue.d.ts} +5 -5
- package/dist/runtime/app/components/cms/PoweredBy.vue +12 -5
- package/dist/runtime/app/components/cms/PresetModal.d.vue.ts +18 -0
- package/dist/runtime/app/components/cms/PresetModal.vue +70 -0
- package/dist/runtime/app/components/cms/PresetModal.vue.d.ts +18 -0
- package/dist/runtime/app/components/cms/RelationField.vue +2 -8
- package/dist/runtime/app/components/cms/SettingsAccount.vue +60 -71
- package/dist/runtime/app/components/cms/SettingsColumns.vue +47 -69
- package/dist/runtime/app/components/cms/SettingsEmptyAdd.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEmptyAdd.vue +21 -0
- package/dist/runtime/app/components/cms/SettingsEmptyAdd.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEntries.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEntries.vue +50 -0
- package/dist/runtime/app/components/cms/SettingsEntries.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEntry.d.vue.ts +1 -10
- package/dist/runtime/app/components/cms/SettingsEntry.vue +11 -95
- package/dist/runtime/app/components/cms/SettingsEntry.vue.d.ts +1 -10
- package/dist/runtime/app/components/cms/SettingsEntryActions.d.vue.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsEntryActions.vue +42 -0
- package/dist/runtime/app/components/cms/SettingsEntryActions.vue.d.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsEntryTabs.d.vue.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsEntryTabs.vue +21 -0
- package/dist/runtime/app/components/cms/SettingsEntryTabs.vue.d.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsFormAction.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsFormAction.vue +17 -0
- package/dist/runtime/app/components/cms/SettingsFormAction.vue.d.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsLayout.vue +18 -14
- package/dist/runtime/app/components/cms/SettingsPresetForm.d.vue.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsPresetForm.vue +60 -0
- package/dist/runtime/app/components/cms/SettingsPresetForm.vue.d.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsPresets.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsPresets.vue +65 -0
- package/dist/runtime/app/components/cms/SettingsPresets.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsRecordActions.d.vue.ts +32 -0
- package/dist/runtime/app/components/cms/SettingsRecordActions.vue +45 -0
- package/dist/runtime/app/components/cms/SettingsRecordActions.vue.d.ts +32 -0
- package/dist/runtime/app/components/cms/SettingsRedirectActions.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsRedirectActions.vue +59 -0
- package/dist/runtime/app/components/cms/SettingsRedirectActions.vue.d.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsRedirectForm.d.vue.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsRedirectForm.vue +77 -0
- package/dist/runtime/app/components/cms/SettingsRedirectForm.vue.d.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsRedirects.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsRedirects.vue +117 -0
- package/dist/runtime/app/components/cms/SettingsRedirects.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsSecretCard.d.vue.ts +17 -0
- package/dist/runtime/app/components/cms/SettingsSecretCard.vue +43 -0
- package/dist/runtime/app/components/cms/SettingsSecretCard.vue.d.ts +17 -0
- package/dist/runtime/app/components/cms/SettingsSite.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsSite.vue +140 -0
- package/dist/runtime/app/components/cms/SettingsSite.vue.d.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsUserCreate.d.vue.ts +8 -0
- package/dist/runtime/app/components/cms/SettingsUserCreate.vue +82 -0
- package/dist/runtime/app/components/cms/SettingsUserCreate.vue.d.ts +8 -0
- package/dist/runtime/app/components/cms/SettingsUserDetail.d.vue.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsUserDetail.vue +179 -0
- package/dist/runtime/app/components/cms/SettingsUserDetail.vue.d.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsUsers.d.vue.ts +9 -6
- package/dist/runtime/app/components/cms/SettingsUsers.vue +88 -234
- package/dist/runtime/app/components/cms/SettingsUsers.vue.d.ts +9 -6
- package/dist/runtime/app/components/cms/SharePreview.d.vue.ts +18 -0
- package/dist/runtime/app/components/cms/SharePreview.vue +219 -0
- package/dist/runtime/app/components/cms/SharePreview.vue.d.ts +18 -0
- package/dist/runtime/app/components/cms/SlugField.vue +5 -3
- package/dist/runtime/app/components/cms/StatusBadge.vue +9 -8
- package/dist/runtime/app/components/cms/TranslationBadges.d.vue.ts +7 -0
- package/dist/runtime/app/components/cms/TranslationBadges.vue +45 -0
- package/dist/runtime/app/components/cms/TranslationBadges.vue.d.ts +7 -0
- package/dist/runtime/app/components/cms/TranslationMenu.d.vue.ts +17 -0
- package/dist/runtime/app/components/cms/TranslationMenu.vue +28 -0
- package/dist/runtime/app/components/cms/TranslationMenu.vue.d.ts +17 -0
- package/dist/runtime/app/components/cms/VisualEditor.d.vue.ts +22 -4
- package/dist/runtime/app/components/cms/VisualEditor.vue +1152 -335
- package/dist/runtime/app/components/cms/VisualEditor.vue.d.ts +22 -4
- package/dist/runtime/app/components/cms/WelcomeModal.vue +32 -50
- package/dist/runtime/app/composables/cms-clipboard.d.ts +8 -0
- package/dist/runtime/app/composables/cms-clipboard.js +38 -0
- package/dist/runtime/app/composables/cms-confirm.d.ts +6 -8
- package/dist/runtime/app/composables/cms-confirm.js +5 -7
- package/dist/runtime/app/composables/cms-entry-disabled.d.ts +5 -1
- package/dist/runtime/app/composables/cms-entry-disabled.js +7 -0
- package/dist/runtime/app/composables/cms-entry-form.d.ts +15 -0
- package/dist/runtime/app/composables/cms-entry-form.js +40 -0
- package/dist/runtime/app/composables/cms-entry-settings.d.ts +30 -0
- package/dist/runtime/app/composables/cms-entry-settings.js +122 -0
- package/dist/runtime/app/composables/cms-entry.d.ts +5 -1
- package/dist/runtime/app/composables/cms-entry.js +36 -11
- package/dist/runtime/app/composables/cms-history.d.ts +10 -0
- package/dist/runtime/app/composables/cms-history.js +24 -0
- package/dist/runtime/app/composables/cms-link-targets.d.ts +10 -0
- package/dist/runtime/app/composables/cms-link-targets.js +40 -0
- package/dist/runtime/app/composables/cms-link-usage.d.ts +1 -0
- package/dist/runtime/app/composables/cms-link-usage.js +45 -0
- package/dist/runtime/app/composables/cms-media-library.d.ts +19 -9
- package/dist/runtime/app/composables/cms-media-library.js +31 -13
- package/dist/runtime/app/composables/cms-media-uploader.js +6 -20
- package/dist/runtime/app/composables/cms-permissions.d.ts +18 -0
- package/dist/runtime/app/composables/cms-permissions.js +54 -0
- package/dist/runtime/app/composables/cms-presets.d.ts +11 -0
- package/dist/runtime/app/composables/cms-presets.js +40 -0
- package/dist/runtime/app/composables/cms-preview-mode.d.ts +4 -0
- package/dist/runtime/app/composables/cms-preview-mode.js +8 -0
- package/dist/runtime/app/composables/cms-query.d.ts +3 -0
- package/dist/runtime/app/composables/cms-query.js +45 -20
- package/dist/runtime/app/composables/cms-record-list.d.ts +31 -0
- package/dist/runtime/app/composables/cms-record-list.js +75 -0
- package/dist/runtime/app/composables/cms-route.d.ts +6 -0
- package/dist/runtime/app/composables/cms-route.js +36 -0
- package/dist/runtime/app/composables/cms-runtime.d.ts +1 -0
- package/dist/runtime/app/composables/cms-save-handoff.d.ts +4 -0
- package/dist/runtime/app/composables/cms-save-handoff.js +19 -0
- package/dist/runtime/app/composables/cms-seo.d.ts +12 -0
- package/dist/runtime/app/composables/cms-seo.js +69 -0
- package/dist/runtime/app/composables/cms-settings-form.d.ts +13 -0
- package/dist/runtime/app/composables/cms-settings-form.js +29 -0
- package/dist/runtime/app/composables/cms-settings-sections.d.ts +26 -0
- package/dist/runtime/app/composables/cms-settings-sections.js +181 -0
- package/dist/runtime/app/composables/cms-settings-stores.d.ts +52 -0
- package/dist/runtime/app/composables/cms-settings-stores.js +156 -0
- package/dist/runtime/app/composables/cms-settings.d.ts +3 -3
- package/dist/runtime/app/composables/cms-settings.js +14 -8
- package/dist/runtime/app/composables/cms-site-disabled.d.ts +7 -0
- package/dist/runtime/app/composables/cms-site-disabled.js +8 -0
- package/dist/runtime/app/composables/cms-site.d.ts +7 -0
- package/dist/runtime/app/composables/cms-site.js +22 -0
- package/dist/runtime/app/composables/cms-toast.d.ts +2 -8
- package/dist/runtime/app/composables/cms-toast.js +3 -9
- package/dist/runtime/app/composables/cms-translation-actions.d.ts +15 -0
- package/dist/runtime/app/composables/cms-translation-actions.js +108 -0
- package/dist/runtime/app/css-url.d.ts +4 -0
- package/dist/runtime/app/image/CmsImage.d.vue.ts +89 -0
- package/dist/runtime/app/image/CmsImage.vue +91 -0
- package/dist/runtime/app/image/CmsImage.vue.d.ts +89 -0
- package/dist/runtime/app/layouts/cms-admin.d.vue.ts +2 -2
- package/dist/runtime/app/layouts/cms-admin.vue +98 -76
- package/dist/runtime/app/layouts/cms-admin.vue.d.ts +2 -2
- package/dist/runtime/app/layouts/cms-editor.vue +7 -2
- package/dist/runtime/app/links/CmsLink.d.vue.ts +18 -0
- package/dist/runtime/app/links/CmsLink.vue +18 -0
- package/dist/runtime/app/links/CmsLink.vue.d.ts +18 -0
- package/dist/runtime/app/middleware/cms-auth.js +13 -1
- package/dist/runtime/app/pages/admin-collection.vue +473 -297
- package/dist/runtime/app/pages/admin-content.vue +190 -53
- package/dist/runtime/app/pages/admin-entry.vue +79 -75
- package/dist/runtime/app/pages/admin-index.vue +18 -5
- package/dist/runtime/app/pages/admin-login.vue +1 -1
- package/dist/runtime/app/pages/admin-logout.vue +1 -1
- package/dist/runtime/app/pages/admin-media.vue +3 -3
- package/dist/runtime/app/pages/admin-preview.vue +112 -24
- package/dist/runtime/app/plugins/cms-field.d.ts +2 -0
- package/dist/runtime/app/plugins/cms-field.js +8 -0
- package/dist/runtime/app/plugins/cms-preview-banner.client.d.ts +2 -0
- package/dist/runtime/app/plugins/cms-preview-banner.client.js +19 -0
- package/dist/runtime/app/plugins/cms-preview.d.ts +2 -0
- package/dist/runtime/app/plugins/cms-preview.js +27 -0
- package/dist/runtime/app/preview/CmsPreviewBanner.d.vue.ts +7 -0
- package/dist/runtime/app/preview/CmsPreviewBanner.vue +63 -0
- package/dist/runtime/app/preview/CmsPreviewBanner.vue.d.ts +7 -0
- package/dist/runtime/app/preview/PreviewOverlay.d.vue.ts +17 -3
- package/dist/runtime/app/preview/PreviewOverlay.vue +629 -166
- package/dist/runtime/app/preview/PreviewOverlay.vue.d.ts +17 -3
- package/dist/runtime/app/preview/field-marker.d.ts +12 -0
- package/dist/runtime/app/preview/field-marker.js +20 -0
- package/dist/runtime/app/preview/inline-text.d.ts +15 -0
- package/dist/runtime/app/preview/inline-text.js +158 -0
- package/dist/runtime/app/utils/block-editor.d.ts +22 -0
- package/dist/runtime/app/utils/block-editor.js +12 -0
- package/dist/runtime/app/utils/keyboard.d.ts +6 -0
- package/dist/runtime/app/utils/keyboard.js +52 -0
- package/dist/runtime/app/utils/redirects.d.ts +6 -0
- package/dist/runtime/app/utils/redirects.js +7 -0
- package/dist/runtime/app/utils/ui.d.ts +8 -0
- package/dist/runtime/app/utils/ui.js +15 -1
- package/dist/runtime/app/utils/users.d.ts +15 -0
- package/dist/runtime/app/utils/users.js +14 -0
- package/dist/runtime/assets/cms.css +1 -1
- package/dist/runtime/generate.js +16 -2
- package/dist/runtime/server/api/collection-export.get.js +3 -2
- package/dist/runtime/server/api/collection-import.post.js +39 -11
- package/dist/runtime/server/api/collection.get.js +29 -10
- package/dist/runtime/server/api/collection.post.js +28 -11
- package/dist/runtime/server/api/collection.put.js +35 -13
- package/dist/runtime/server/api/item.delete.js +19 -7
- package/dist/runtime/server/api/item.get.js +3 -2
- package/dist/runtime/server/api/item.put.js +52 -18
- package/dist/runtime/server/api/link-usage.post.d.ts +5 -0
- package/dist/runtime/server/api/link-usage.post.js +21 -0
- package/dist/runtime/server/api/media-delete.post.js +4 -2
- package/dist/runtime/server/api/media-download.get.js +2 -2
- package/dist/runtime/server/api/media-folder.delete.js +8 -2
- package/dist/runtime/server/api/media-folder.patch.js +10 -2
- package/dist/runtime/server/api/media-folder.post.js +4 -2
- package/dist/runtime/server/api/media-move.post.js +7 -4
- package/dist/runtime/server/api/media-presign.post.js +2 -2
- package/dist/runtime/server/api/media-upload.put.js +2 -2
- package/dist/runtime/server/api/media-usage.post.d.ts +2 -4
- package/dist/runtime/server/api/media-usage.post.js +13 -9
- package/dist/runtime/server/api/media.delete.js +4 -2
- package/dist/runtime/server/api/media.get.js +22 -7
- package/dist/runtime/server/api/media.post.js +4 -2
- package/dist/runtime/server/api/media.put.js +33 -16
- package/dist/runtime/server/api/preset.delete.d.ts +4 -0
- package/dist/runtime/server/api/preset.delete.js +9 -0
- package/dist/runtime/server/api/preset.put.d.ts +3 -0
- package/dist/runtime/server/api/preset.put.js +23 -0
- package/dist/runtime/server/api/presets-field.get.d.ts +3 -0
- package/dist/runtime/server/api/presets-field.get.js +13 -0
- package/dist/runtime/server/api/presets-field.post.d.ts +3 -0
- package/dist/runtime/server/api/presets-field.post.js +36 -0
- package/dist/runtime/server/api/presets.get.d.ts +3 -0
- package/dist/runtime/server/api/presets.get.js +7 -0
- package/dist/runtime/server/api/preview-link.delete.d.ts +4 -0
- package/dist/runtime/server/api/preview-link.delete.js +15 -0
- package/dist/runtime/server/api/preview-links.get.d.ts +3 -0
- package/dist/runtime/server/api/preview-links.get.js +10 -0
- package/dist/runtime/server/api/preview-links.post.d.ts +3 -0
- package/dist/runtime/server/api/preview-links.post.js +17 -0
- package/dist/runtime/server/api/preview.get.d.ts +3 -0
- package/dist/runtime/server/api/preview.get.js +7 -0
- package/dist/runtime/server/api/redirect.delete.d.ts +4 -0
- package/dist/runtime/server/api/redirect.delete.js +10 -0
- package/dist/runtime/server/api/redirect.put.d.ts +3 -0
- package/dist/runtime/server/api/redirect.put.js +8 -0
- package/dist/runtime/server/api/redirects-import.post.d.ts +3 -0
- package/dist/runtime/server/api/redirects-import.post.js +13 -0
- package/dist/runtime/server/api/redirects.get.d.ts +3 -0
- package/dist/runtime/server/api/redirects.get.js +7 -0
- package/dist/runtime/server/api/redirects.post.d.ts +3 -0
- package/dist/runtime/server/api/redirects.post.js +7 -0
- package/dist/runtime/server/api/search.get.js +8 -3
- package/dist/runtime/server/api/settings.delete.js +2 -2
- package/dist/runtime/server/api/settings.get.js +4 -2
- package/dist/runtime/server/api/settings.put.js +2 -2
- package/dist/runtime/server/api/site.get.d.ts +3 -0
- package/dist/runtime/server/api/site.get.js +7 -0
- package/dist/runtime/server/api/site.put.d.ts +3 -0
- package/dist/runtime/server/api/site.put.js +16 -0
- package/dist/runtime/server/api/tags.get.js +3 -2
- package/dist/runtime/server/api/user.delete.js +8 -2
- package/dist/runtime/server/api/user.put.js +14 -4
- package/dist/runtime/server/api/users.get.js +2 -2
- package/dist/runtime/server/api/users.post.js +8 -4
- package/dist/runtime/server/middleware/hidden-site.d.ts +2 -0
- package/dist/runtime/server/middleware/hidden-site.js +8 -0
- package/dist/runtime/server/middleware/preview-links.d.ts +2 -0
- package/dist/runtime/server/middleware/preview-links.js +6 -0
- package/dist/runtime/server/middleware/redirects.d.ts +2 -0
- package/dist/runtime/server/middleware/redirects.js +18 -0
- package/dist/runtime/server/plugins/session-validate.js +4 -1
- package/dist/runtime/server/plugins/webhooks.d.ts +3 -0
- package/dist/runtime/server/plugins/webhooks.js +27 -0
- package/dist/runtime/server/routes/graphql.js +5 -3
- package/dist/runtime/server/routes/robots.d.ts +2 -0
- package/dist/runtime/server/routes/robots.js +16 -0
- package/dist/runtime/server/routes/sitemap.d.ts +2 -0
- package/dist/runtime/server/routes/sitemap.js +67 -0
- package/dist/runtime/server/utils/cached-value.d.ts +5 -0
- package/dist/runtime/server/utils/cached-value.js +24 -0
- package/dist/runtime/server/utils/content-routes.d.ts +5 -0
- package/dist/runtime/server/utils/content-routes.js +29 -0
- package/dist/runtime/server/utils/field-rules.d.ts +19 -0
- package/dist/runtime/server/utils/field-rules.js +72 -0
- package/dist/runtime/server/utils/graphql.d.ts +3 -1
- package/dist/runtime/server/utils/graphql.js +145 -29
- package/dist/runtime/server/utils/hooks.d.ts +14 -0
- package/dist/runtime/server/utils/hooks.js +71 -0
- package/dist/runtime/server/utils/import.d.ts +7 -2
- package/dist/runtime/server/utils/import.js +108 -14
- package/dist/runtime/server/utils/link-usage.d.ts +4 -0
- package/dist/runtime/server/utils/link-usage.js +28 -0
- package/dist/runtime/server/utils/media-references.d.ts +6 -1
- package/dist/runtime/server/utils/media-references.js +49 -20
- package/dist/runtime/server/utils/media-usage.d.ts +2 -0
- package/dist/runtime/server/utils/media-usage.js +5 -3
- package/dist/runtime/server/utils/page-rows.d.ts +2 -0
- package/dist/runtime/server/utils/page-rows.js +42 -29
- package/dist/runtime/server/utils/permissions.d.ts +14 -0
- package/dist/runtime/server/utils/permissions.js +43 -0
- package/dist/runtime/server/utils/presets.d.ts +18 -0
- package/dist/runtime/server/utils/presets.js +68 -0
- package/dist/runtime/server/utils/preview-links.d.ts +207 -0
- package/dist/runtime/server/utils/preview-links.js +161 -0
- package/dist/runtime/server/utils/redirects.d.ts +31 -0
- package/dist/runtime/server/utils/redirects.js +240 -0
- package/dist/runtime/server/utils/registry.js +4 -1
- package/dist/runtime/server/utils/relations.d.ts +8 -0
- package/dist/runtime/server/utils/relations.js +25 -6
- package/dist/runtime/server/utils/require-admin.d.ts +0 -1
- package/dist/runtime/server/utils/require-admin.js +4 -8
- package/dist/runtime/server/utils/search.d.ts +1 -0
- package/dist/runtime/server/utils/search.js +44 -19
- package/dist/runtime/server/utils/single-row.d.ts +3 -0
- package/dist/runtime/server/utils/single-row.js +4 -0
- package/dist/runtime/server/utils/site.d.ts +23 -0
- package/dist/runtime/server/utils/site.js +79 -0
- package/dist/runtime/server/utils/sitemap.d.ts +12 -0
- package/dist/runtime/server/utils/sitemap.js +109 -0
- package/dist/runtime/server/utils/user-management.d.ts +4 -0
- package/dist/runtime/server/utils/user-management.js +20 -0
- package/dist/runtime/server/utils/users.d.ts +1 -1
- package/dist/runtime/server/utils/users.js +1 -1
- package/dist/runtime/server/utils/visibility.d.ts +1 -0
- package/dist/runtime/server/utils/visibility.js +6 -1
- package/dist/runtime/server/utils/webhooks.d.ts +24 -0
- package/dist/runtime/server/utils/webhooks.js +90 -0
- package/dist/runtime/shared/block-paths.d.ts +66 -0
- package/dist/runtime/shared/block-paths.js +310 -0
- package/dist/runtime/shared/block-picker.d.ts +18 -0
- package/dist/runtime/shared/block-picker.js +31 -0
- package/dist/runtime/shared/block-signature.d.ts +4 -0
- package/dist/runtime/shared/block-signature.js +46 -0
- package/dist/runtime/shared/clipboard.d.ts +31 -0
- package/dist/runtime/shared/clipboard.js +206 -0
- package/dist/runtime/shared/conditions.d.ts +36 -0
- package/dist/runtime/shared/conditions.js +100 -0
- package/dist/runtime/shared/content-routes.d.ts +42 -0
- package/dist/runtime/shared/content-routes.js +237 -0
- package/dist/runtime/shared/duplicate.js +2 -1
- package/dist/runtime/shared/editor-layout.d.ts +18 -0
- package/dist/runtime/shared/editor-layout.js +9 -0
- package/dist/runtime/shared/field-options.d.ts +21 -0
- package/dist/runtime/shared/field-options.js +90 -0
- package/dist/runtime/shared/graphql-sdl.d.ts +1 -0
- package/dist/runtime/shared/graphql-sdl.js +50 -26
- package/dist/runtime/shared/history.d.ts +27 -0
- package/dist/runtime/shared/history.js +123 -0
- package/dist/runtime/shared/hooks.d.ts +45 -0
- package/dist/runtime/shared/hooks.js +28 -0
- package/dist/runtime/shared/index.d.ts +90 -21
- package/dist/runtime/shared/index.js +121 -37
- package/dist/runtime/shared/inline-text.d.ts +38 -0
- package/dist/runtime/shared/inline-text.js +85 -0
- package/dist/runtime/shared/layout.d.ts +2 -1
- package/dist/runtime/shared/layout.js +3 -0
- package/dist/runtime/shared/links.d.ts +48 -0
- package/dist/runtime/shared/links.js +155 -0
- package/dist/runtime/shared/list-filters.d.ts +2 -0
- package/dist/runtime/shared/list-filters.js +2 -0
- package/dist/runtime/shared/media-focal.d.ts +30 -0
- package/dist/runtime/shared/media-focal.js +52 -0
- package/dist/runtime/shared/migrations-dir.d.ts +14 -0
- package/dist/runtime/shared/migrations-dir.js +48 -2
- package/dist/runtime/shared/presets.d.ts +67 -0
- package/dist/runtime/shared/presets.js +127 -0
- package/dist/runtime/shared/preview-links.d.ts +64 -0
- package/dist/runtime/shared/preview-links.js +54 -0
- package/dist/runtime/shared/preview.d.ts +124 -21
- package/dist/runtime/shared/preview.js +419 -81
- package/dist/runtime/shared/redirects.d.ts +75 -0
- package/dist/runtime/shared/redirects.js +230 -0
- package/dist/runtime/shared/roles.d.ts +57 -0
- package/dist/runtime/shared/roles.js +293 -0
- package/dist/runtime/shared/save-state.d.ts +8 -0
- package/dist/runtime/shared/save-state.js +15 -0
- package/dist/runtime/shared/search.d.ts +16 -1
- package/dist/runtime/shared/search.js +41 -7
- package/dist/runtime/shared/seo.d.ts +167 -0
- package/dist/runtime/shared/seo.js +335 -0
- package/dist/runtime/shared/transfer.js +2 -1
- package/dist/runtime/shared/translations.d.ts +29 -0
- package/dist/runtime/shared/translations.js +123 -0
- package/dist/runtime/shared/users.d.ts +1 -1
- package/dist/runtime/shared/validation.d.ts +23 -1
- package/dist/runtime/shared/validation.js +214 -64
- package/dist/runtime/webhooks.d.ts +33 -0
- package/dist/runtime/webhooks.js +57 -0
- package/dist/types.d.mts +5 -1
- package/package.json +6 -2
- package/dist/runtime/app/components/cms/BlockOutline.vue +0 -88
- package/dist/runtime/app/components/cms/BlockPalette.vue +0 -31
- package/dist/runtime/app/components/cms/SettingsModal.d.vue.ts +0 -12
- package/dist/runtime/app/components/cms/SettingsModal.vue +0 -126
- package/dist/runtime/app/components/cms/SettingsModal.vue.d.ts +0 -12
- package/dist/runtime/app/composables/cms-field-focus.d.ts +0 -4
- package/dist/runtime/app/composables/cms-field-focus.js +0 -20
|
@@ -3,7 +3,7 @@ import { createError } from "h3";
|
|
|
3
3
|
import cmsConfig from "#cms-config";
|
|
4
4
|
import { useDb } from "#cms-db";
|
|
5
5
|
import * as cmsTables from "#cms-tables";
|
|
6
|
-
import { entryFieldsFor } from "../../shared/index.js";
|
|
6
|
+
import { entryFieldsFor, relationTarget } from "../../shared/index.js";
|
|
7
7
|
import { resolveTable, tableColumns } from "./registry.js";
|
|
8
8
|
import { joinWriteStatements } from "./write-statements.js";
|
|
9
9
|
function manyToManyKeys(entry) {
|
|
@@ -37,7 +37,7 @@ export async function assertRelationTargets(entry, lists) {
|
|
|
37
37
|
for (const [key, ids] of Object.entries(lists)) {
|
|
38
38
|
if (!ids.length) continue;
|
|
39
39
|
const field = entryFieldsFor(entry)[key];
|
|
40
|
-
const target = resolveTable(field
|
|
40
|
+
const target = resolveTable(relationTarget(field));
|
|
41
41
|
if (!target) continue;
|
|
42
42
|
const idCol = tableColumns(target).id;
|
|
43
43
|
const rows = await db.select({ id: idCol }).from(target).where(inArray(idCol, ids));
|
|
@@ -46,7 +46,9 @@ export async function assertRelationTargets(entry, lists) {
|
|
|
46
46
|
const missing = ids.filter((id) => !found.has(id));
|
|
47
47
|
throw createError({
|
|
48
48
|
statusCode: 422,
|
|
49
|
-
statusMessage: `Unknown ${field
|
|
49
|
+
statusMessage: `Unknown ${relationTarget(field)} id(s) for '${key}': ${missing.join(
|
|
50
|
+
", "
|
|
51
|
+
)}`
|
|
50
52
|
});
|
|
51
53
|
}
|
|
52
54
|
}
|
|
@@ -60,9 +62,10 @@ export async function relationTitles(db, entry, rows) {
|
|
|
60
62
|
const config = cmsConfig;
|
|
61
63
|
const titles = {};
|
|
62
64
|
for (const [key, field] of Object.entries(entryFieldsFor(entry))) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
65
|
+
const to = relationTarget(field);
|
|
66
|
+
if (field.type !== "relation" || !to) continue;
|
|
67
|
+
const targetEntry = config[to];
|
|
68
|
+
const target = resolveTable(to);
|
|
66
69
|
if (!targetEntry?.titleField || !target) continue;
|
|
67
70
|
const ids = /* @__PURE__ */ new Set();
|
|
68
71
|
for (const row of rows) {
|
|
@@ -99,3 +102,19 @@ export async function attachManyToMany(db, name, entry, rows) {
|
|
|
99
102
|
}
|
|
100
103
|
return rows;
|
|
101
104
|
}
|
|
105
|
+
export async function readBackSaved(name, entry, row) {
|
|
106
|
+
try {
|
|
107
|
+
const [attached] = await attachManyToMany(useDb(), name, entry, [row]);
|
|
108
|
+
return { row: attached ?? row, error: null };
|
|
109
|
+
} catch (error) {
|
|
110
|
+
return { row, error };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export function savedReadBackError(error) {
|
|
114
|
+
return createError({
|
|
115
|
+
statusCode: 500,
|
|
116
|
+
statusMessage: "The changes were saved, but the saved item could not be read back. Reload it.",
|
|
117
|
+
data: { saved: true },
|
|
118
|
+
cause: error
|
|
119
|
+
});
|
|
120
|
+
}
|
|
@@ -2,4 +2,3 @@ import type { H3Event } from 'h3';
|
|
|
2
2
|
import type { CmsSessionUser } from '../../shared/index.js';
|
|
3
3
|
export declare function isSuperAdminEmail(event: H3Event, email: string): boolean;
|
|
4
4
|
export declare function requireAdmin(event: H3Event): Promise<CmsSessionUser>;
|
|
5
|
-
export declare function requireSuperAdmin(event: H3Event): Promise<CmsSessionUser>;
|
|
@@ -23,19 +23,15 @@ export async function requireAdmin(event) {
|
|
|
23
23
|
if (!row || user.passwordStamp !== passwordStamp(row.passwordHash)) {
|
|
24
24
|
throw createError({ statusCode: 401, statusMessage: "Authentication required" });
|
|
25
25
|
}
|
|
26
|
+
if (row.role === "superadmin") {
|
|
27
|
+
throw createError({ statusCode: 403, statusMessage: "Not authorized" });
|
|
28
|
+
}
|
|
26
29
|
return {
|
|
27
30
|
id: row.id,
|
|
28
31
|
email: row.email,
|
|
29
32
|
name: row.name ?? null,
|
|
30
|
-
role:
|
|
33
|
+
role: row.role,
|
|
31
34
|
firstLogin: !!row.mustChangePassword,
|
|
32
35
|
passwordStamp: user.passwordStamp
|
|
33
36
|
};
|
|
34
37
|
}
|
|
35
|
-
export async function requireSuperAdmin(event) {
|
|
36
|
-
const user = await requireAdmin(event);
|
|
37
|
-
if (user.role !== "superadmin") {
|
|
38
|
-
throw createError({ statusCode: 403, statusMessage: "Only the super admin can manage users" });
|
|
39
|
-
}
|
|
40
|
-
return user;
|
|
41
|
-
}
|
|
@@ -11,6 +11,7 @@ export interface CmsSearchOptions {
|
|
|
11
11
|
tables: Record<string, unknown>;
|
|
12
12
|
i18n: CmsI18n;
|
|
13
13
|
query: string;
|
|
14
|
+
media?: boolean;
|
|
14
15
|
}
|
|
15
16
|
export declare function searchLikeCondition(dialect: SearchDialect, column: AnySQLiteColumn, pattern: string): SQL;
|
|
16
17
|
export declare function searchCmsValues(options: CmsSearchOptions): Promise<{
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { desc, inArray, or, sql } from "drizzle-orm";
|
|
2
2
|
import {
|
|
3
3
|
decodeTranslatableValue,
|
|
4
|
+
formatBlockPath,
|
|
5
|
+
resolveBlockPath,
|
|
6
|
+
walkBlocks,
|
|
4
7
|
isMultiSelect,
|
|
5
8
|
isTranslatableField,
|
|
9
|
+
linkLabelTexts,
|
|
6
10
|
mediaFilename,
|
|
7
11
|
pageColumnFields,
|
|
8
12
|
pageFields,
|
|
9
13
|
pageFieldsTableName,
|
|
10
14
|
pageMediaTableName,
|
|
11
15
|
pageRouteOf,
|
|
16
|
+
readLinkValue,
|
|
12
17
|
selectOptionLabel,
|
|
13
18
|
selectValuesMatching
|
|
14
19
|
} from "../../shared/index.js";
|
|
@@ -17,6 +22,8 @@ import {
|
|
|
17
22
|
CMS_SEARCH_MIN_LENGTH,
|
|
18
23
|
CMS_SEARCH_ROWS_PER_ENTRY,
|
|
19
24
|
CMS_SEARCH_VALUE_LIMIT,
|
|
25
|
+
cmsEntryLink,
|
|
26
|
+
cmsHashLink,
|
|
20
27
|
cmsSearchLink,
|
|
21
28
|
normalizeSearchQuery,
|
|
22
29
|
searchCmsLabels,
|
|
@@ -34,6 +41,7 @@ const VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
|
34
41
|
"select",
|
|
35
42
|
"tags",
|
|
36
43
|
"json",
|
|
44
|
+
"link",
|
|
37
45
|
"blocks"
|
|
38
46
|
]);
|
|
39
47
|
function isSearchableField(field) {
|
|
@@ -66,6 +74,13 @@ function fieldTexts(field, value, defaultLocale) {
|
|
|
66
74
|
}).join(", ");
|
|
67
75
|
return [{ locale: null, text }];
|
|
68
76
|
}
|
|
77
|
+
if (field.type === "link") {
|
|
78
|
+
const link = readLinkValue(value);
|
|
79
|
+
if (!link) return [];
|
|
80
|
+
const texts = linkLabelTexts(link.label);
|
|
81
|
+
if (link.url) texts.push({ locale: null, text: link.url });
|
|
82
|
+
return texts;
|
|
83
|
+
}
|
|
69
84
|
if (field.type === "tags" && Array.isArray(value))
|
|
70
85
|
return value.length ? [{ locale: null, text: value.map(String).join(", ") }] : [];
|
|
71
86
|
if (typeof value === "string") return [{ locale: null, text: plainText(field, value) }];
|
|
@@ -88,22 +103,29 @@ function rowHits(fields, row, term, defaultLocale) {
|
|
|
88
103
|
collect(field, value, { field: key, block: null, fieldLabel: field.label });
|
|
89
104
|
continue;
|
|
90
105
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const block = item ? field.blocks?.[String(item.type)] : void 0;
|
|
94
|
-
if (!item || !block) return;
|
|
106
|
+
walkBlocks({ [key]: field }, row, ({ item, block, path }) => {
|
|
107
|
+
if (hits.length >= CMS_SEARCH_HITS_PER_ROW) return false;
|
|
95
108
|
for (const [blockKey, blockField] of Object.entries(block.fields)) {
|
|
96
|
-
if (!isSearchableField(blockField)) continue;
|
|
109
|
+
if (!isSearchableField(blockField) || blockField.type === "blocks") continue;
|
|
97
110
|
collect(blockField, item[blockKey], {
|
|
98
111
|
field: key,
|
|
99
|
-
block:
|
|
100
|
-
fieldLabel: searchFieldPath(
|
|
112
|
+
block: formatBlockPath(path.slice(1)),
|
|
113
|
+
fieldLabel: searchFieldPath(...blockLabels(fields, row, path), blockField.label)
|
|
101
114
|
});
|
|
102
115
|
}
|
|
103
116
|
});
|
|
104
117
|
}
|
|
105
118
|
return hits;
|
|
106
119
|
}
|
|
120
|
+
function blockLabels(fields, row, path) {
|
|
121
|
+
const labels = [];
|
|
122
|
+
for (let end = 2; end <= path.length; end += 2) {
|
|
123
|
+
const location = resolveBlockPath(fields, row, path.slice(0, end));
|
|
124
|
+
if (!location) break;
|
|
125
|
+
labels.push(location.field.label, location.block?.label ?? String(location.item.type));
|
|
126
|
+
}
|
|
127
|
+
return labels;
|
|
128
|
+
}
|
|
107
129
|
function titleOf(entry, row, defaultLocale) {
|
|
108
130
|
if (entry.kind === "single" || !entry.titleField) return null;
|
|
109
131
|
const value = row[entry.titleField];
|
|
@@ -162,20 +184,23 @@ async function pageRows(context, name, entry, table) {
|
|
|
162
184
|
}).where(inArray(pageColumns.id, ids));
|
|
163
185
|
return decodePageRows(entry, rows);
|
|
164
186
|
}
|
|
165
|
-
function
|
|
166
|
-
if (entry.kind === "single") return `/cms/${name}
|
|
187
|
+
function itemLink(name, entry, row) {
|
|
188
|
+
if (entry.kind === "single") return (focus) => cmsSearchLink(`/cms/${name}`, focus);
|
|
167
189
|
if (entry.kind === "page") {
|
|
168
190
|
const route = pageRouteOf(entry, String(row.id));
|
|
169
|
-
return route ? `/cms/${name}/${route.key}
|
|
191
|
+
return route ? (focus) => cmsSearchLink(`/cms/${name}/${route.key}`, focus) : null;
|
|
170
192
|
}
|
|
171
|
-
|
|
193
|
+
if (row.id == null) return null;
|
|
194
|
+
const id = String(row.id);
|
|
195
|
+
if (entry.kind === "collection") return (focus) => cmsEntryLink(name, id, focus);
|
|
196
|
+
return (focus) => cmsSearchLink(`/cms/${name}/${id}`, focus);
|
|
172
197
|
}
|
|
173
198
|
function entryHits(context, name, entry, rows) {
|
|
174
199
|
const { defaultLocale } = context.i18n;
|
|
175
200
|
const hits = [];
|
|
176
201
|
for (const row of rows) {
|
|
177
|
-
const
|
|
178
|
-
if (!
|
|
202
|
+
const link = itemLink(name, entry, row);
|
|
203
|
+
if (!link) continue;
|
|
179
204
|
const route = entry.kind === "page" ? pageRouteOf(entry, String(row.id)) : void 0;
|
|
180
205
|
const fields = route ? pageFields(entry, route.path) : entry.fields;
|
|
181
206
|
const title = route ? route.label : titleOf(entry, row, defaultLocale);
|
|
@@ -191,17 +216,14 @@ function entryHits(context, name, entry, rows) {
|
|
|
191
216
|
fieldLabel: hit.fieldLabel,
|
|
192
217
|
locale: hit.locale,
|
|
193
218
|
snippet: hit.snippet,
|
|
194
|
-
to:
|
|
195
|
-
field: hit.field,
|
|
196
|
-
locale: hit.locale,
|
|
197
|
-
block: hit.block
|
|
198
|
-
})
|
|
219
|
+
to: link({ field: hit.field, locale: hit.locale, block: hit.block })
|
|
199
220
|
});
|
|
200
221
|
}
|
|
201
222
|
}
|
|
202
223
|
return hits;
|
|
203
224
|
}
|
|
204
225
|
async function mediaHits(context) {
|
|
226
|
+
if (context.media === false) return [];
|
|
205
227
|
const table = context.tables.cms_media;
|
|
206
228
|
const columns = table ? columnsOf(table) : void 0;
|
|
207
229
|
if (!table || !columns?.alt || !columns.key || !columns.folder) return [];
|
|
@@ -222,7 +244,10 @@ async function mediaHits(context) {
|
|
|
222
244
|
fieldLabel: "Alt text",
|
|
223
245
|
locale: null,
|
|
224
246
|
snippet,
|
|
225
|
-
to:
|
|
247
|
+
to: cmsHashLink(
|
|
248
|
+
folder ? `/cms/media?${new URLSearchParams({ folder })}` : "/cms/media",
|
|
249
|
+
{ media: row.key }
|
|
250
|
+
)
|
|
226
251
|
}
|
|
227
252
|
];
|
|
228
253
|
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { CmsI18n, CmsSiteSettings } from '../../shared/index.js';
|
|
4
|
+
export declare function siteSettingsSchema(i18n: CmsI18n): z.ZodObject<{
|
|
5
|
+
name: z.ZodPipe<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodString>>, z.ZodTransform<{
|
|
6
|
+
[k: string]: string;
|
|
7
|
+
} | null, Record<string, string> | null>>;
|
|
8
|
+
titleTemplate: z.ZodPipe<z.ZodNullable<z.ZodString>, z.ZodTransform<string | null, string | null>>;
|
|
9
|
+
description: z.ZodPipe<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodString>>, z.ZodTransform<{
|
|
10
|
+
[k: string]: string;
|
|
11
|
+
} | null, Record<string, string> | null>>;
|
|
12
|
+
image: z.ZodNullable<z.ZodString>;
|
|
13
|
+
hidden: z.ZodBoolean;
|
|
14
|
+
}, z.core.$strict>;
|
|
15
|
+
export declare function parseSiteSettings(raw: string | null | undefined): CmsSiteSettings;
|
|
16
|
+
export declare function readSiteSettings(): Promise<CmsSiteSettings>;
|
|
17
|
+
export declare const SITE_SETTINGS_CACHE_TTL_MS = 30000;
|
|
18
|
+
export declare function readCachedSiteSettings(): Promise<CmsSiteSettings>;
|
|
19
|
+
export declare function writeSiteSettings(settings: CmsSiteSettings): Promise<void>;
|
|
20
|
+
export declare function configuredSiteUrl(event: H3Event): string;
|
|
21
|
+
export declare function trustsForwardedHost(event: H3Event): boolean;
|
|
22
|
+
export declare function resolveSiteUrl(event: H3Event): string;
|
|
23
|
+
export declare function seoRouteCacheHeader(event: H3Event): string;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
2
|
+
import { getRequestURL } from "h3";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { useDb } from "#cms-db";
|
|
5
|
+
import { cms_settings } from "#cms-tables";
|
|
6
|
+
import { useRuntimeConfig } from "#imports";
|
|
7
|
+
import { cachedValue } from "./cached-value.js";
|
|
8
|
+
import {
|
|
9
|
+
CMS_SITE_SETTINGS_KEY,
|
|
10
|
+
EMPTY_SITE_SETTINGS,
|
|
11
|
+
SITE_TEXT_LIMITS,
|
|
12
|
+
coerceSiteSettings,
|
|
13
|
+
isForwardedHostTrusted,
|
|
14
|
+
normalizeSiteUrl,
|
|
15
|
+
seoRouteCacheControl,
|
|
16
|
+
titleTemplateErrors
|
|
17
|
+
} from "../../shared/index.js";
|
|
18
|
+
function localeTextSchema(max, locales) {
|
|
19
|
+
return z.record(z.string(), z.string().max(max)).nullable().superRefine((value, context) => {
|
|
20
|
+
for (const locale of Object.keys(value ?? {})) {
|
|
21
|
+
if (!locales.includes(locale))
|
|
22
|
+
context.addIssue({ code: "custom", message: `Unknown locale: ${locale}` });
|
|
23
|
+
}
|
|
24
|
+
}).transform((value) => {
|
|
25
|
+
const entries = Object.entries(value ?? {}).map(([locale, text]) => [locale, text.trim()]).filter(([, text]) => text);
|
|
26
|
+
return entries.length ? Object.fromEntries(entries) : null;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export function siteSettingsSchema(i18n) {
|
|
30
|
+
const locales = i18n.locales.length ? i18n.locales : [i18n.defaultLocale];
|
|
31
|
+
return z.object({
|
|
32
|
+
name: localeTextSchema(SITE_TEXT_LIMITS.name, locales),
|
|
33
|
+
titleTemplate: z.string().max(SITE_TEXT_LIMITS.titleTemplate).nullable().transform((value) => value?.trim() || null).superRefine((value, context) => {
|
|
34
|
+
for (const message of value ? titleTemplateErrors(value) : [])
|
|
35
|
+
context.addIssue({ code: "custom", message });
|
|
36
|
+
}),
|
|
37
|
+
description: localeTextSchema(SITE_TEXT_LIMITS.description, locales),
|
|
38
|
+
image: z.string().min(1).max(1024).nullable(),
|
|
39
|
+
hidden: z.boolean()
|
|
40
|
+
}).strict();
|
|
41
|
+
}
|
|
42
|
+
export function parseSiteSettings(raw) {
|
|
43
|
+
if (!raw) return { ...EMPTY_SITE_SETTINGS };
|
|
44
|
+
try {
|
|
45
|
+
return coerceSiteSettings(JSON.parse(raw));
|
|
46
|
+
} catch {
|
|
47
|
+
return { ...EMPTY_SITE_SETTINGS };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export async function readSiteSettings() {
|
|
51
|
+
const [row] = await useDb().select({ value: cms_settings.value }).from(cms_settings).where(eq(cms_settings.key, CMS_SITE_SETTINGS_KEY)).limit(1);
|
|
52
|
+
return parseSiteSettings(row?.value);
|
|
53
|
+
}
|
|
54
|
+
export const SITE_SETTINGS_CACHE_TTL_MS = 3e4;
|
|
55
|
+
const siteSettingsCache = cachedValue(readSiteSettings, SITE_SETTINGS_CACHE_TTL_MS);
|
|
56
|
+
export function readCachedSiteSettings() {
|
|
57
|
+
return siteSettingsCache.get();
|
|
58
|
+
}
|
|
59
|
+
export async function writeSiteSettings(settings) {
|
|
60
|
+
const value = JSON.stringify(settings);
|
|
61
|
+
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
62
|
+
await useDb().insert(cms_settings).values({ key: CMS_SITE_SETTINGS_KEY, value, updatedAt }).onConflictDoUpdate({ target: cms_settings.key, set: { value, updatedAt } });
|
|
63
|
+
siteSettingsCache.invalidate();
|
|
64
|
+
}
|
|
65
|
+
export function configuredSiteUrl(event) {
|
|
66
|
+
const { siteUrl } = useRuntimeConfig(event).public;
|
|
67
|
+
return normalizeSiteUrl(siteUrl);
|
|
68
|
+
}
|
|
69
|
+
export function trustsForwardedHost(event) {
|
|
70
|
+
const config = useRuntimeConfig(event).public;
|
|
71
|
+
return isForwardedHostTrusted(config.cmsTrustForwardedHost);
|
|
72
|
+
}
|
|
73
|
+
export function resolveSiteUrl(event) {
|
|
74
|
+
const trusted = trustsForwardedHost(event);
|
|
75
|
+
return configuredSiteUrl(event) || getRequestURL(event, { xForwardedHost: trusted, xForwardedProto: trusted }).origin;
|
|
76
|
+
}
|
|
77
|
+
export function seoRouteCacheHeader(event) {
|
|
78
|
+
return seoRouteCacheControl(configuredSiteUrl(event));
|
|
79
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CmsI18n, SitemapChunk, SitemapSourceSize, SitemapUrl } from '../../shared/index.js';
|
|
2
|
+
export declare const PAGES_SOURCE = "";
|
|
3
|
+
export interface SitemapContext {
|
|
4
|
+
siteUrl: string;
|
|
5
|
+
i18n: CmsI18n;
|
|
6
|
+
}
|
|
7
|
+
export interface SitemapPlan {
|
|
8
|
+
pages: SitemapUrl[];
|
|
9
|
+
sizes: SitemapSourceSize[];
|
|
10
|
+
}
|
|
11
|
+
export declare function planSitemap(context: SitemapContext): Promise<SitemapPlan>;
|
|
12
|
+
export declare function sitemapChunkUrls(plan: SitemapPlan, chunks: readonly SitemapChunk[], context: SitemapContext): AsyncGenerator<SitemapUrl[]>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { and, asc, eq, gt, isNull, or, sql } from "drizzle-orm";
|
|
2
|
+
import cmsConfig from "#cms-config";
|
|
3
|
+
import { useDb } from "#cms-db";
|
|
4
|
+
import {
|
|
5
|
+
CONTENT_NOINDEX_FIELD,
|
|
6
|
+
CONTENT_PUBLISHED_AT_FIELD,
|
|
7
|
+
CONTENT_SLUG_FIELD,
|
|
8
|
+
contentItemPaths,
|
|
9
|
+
contentRouteLocales,
|
|
10
|
+
contentRoutePatterns,
|
|
11
|
+
isRoutedContent,
|
|
12
|
+
pageRoutes,
|
|
13
|
+
pageSitemapUrls,
|
|
14
|
+
sitemapLastmod,
|
|
15
|
+
sitemapUrlsForPaths
|
|
16
|
+
} from "../../shared/index.js";
|
|
17
|
+
import { readPages } from "./page-storage.js";
|
|
18
|
+
import { resolveTable, tableColumns } from "./registry.js";
|
|
19
|
+
import { visibilityConditions } from "./visibility.js";
|
|
20
|
+
const BATCH_SIZE = 1e3;
|
|
21
|
+
export const PAGES_SOURCE = "";
|
|
22
|
+
function contentWhere(entry, table) {
|
|
23
|
+
const columns = tableColumns(table);
|
|
24
|
+
const conditions = visibilityConditions(entry, columns);
|
|
25
|
+
const noindex = columns[CONTENT_NOINDEX_FIELD];
|
|
26
|
+
if (noindex) conditions.push(or(isNull(noindex), eq(noindex, false)));
|
|
27
|
+
return conditions;
|
|
28
|
+
}
|
|
29
|
+
function routedEntries() {
|
|
30
|
+
const entries = [];
|
|
31
|
+
for (const [name, entry] of Object.entries(cmsConfig)) {
|
|
32
|
+
const table = isRoutedContent(entry) ? resolveTable(name) : void 0;
|
|
33
|
+
if (table) entries.push([name, entry, table]);
|
|
34
|
+
}
|
|
35
|
+
return entries;
|
|
36
|
+
}
|
|
37
|
+
async function readPageUrls(siteUrl) {
|
|
38
|
+
const found = Object.entries(cmsConfig).find(([, entry2]) => entry2.kind === "page");
|
|
39
|
+
if (!found) return [];
|
|
40
|
+
const [name, entry] = found;
|
|
41
|
+
const table = resolveTable(name);
|
|
42
|
+
if (!table) return [];
|
|
43
|
+
const rows = await readPages(name, entry, table);
|
|
44
|
+
return pageSitemapUrls(
|
|
45
|
+
pageRoutes(entry).map((route) => route.path),
|
|
46
|
+
rows,
|
|
47
|
+
siteUrl
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
async function countContent(entry, table) {
|
|
51
|
+
const where = contentWhere(entry, table);
|
|
52
|
+
const [counted] = await useDb().select({ total: sql`count(*)` }).from(table).where(where.length ? and(...where) : void 0);
|
|
53
|
+
return Number(counted?.total ?? 0);
|
|
54
|
+
}
|
|
55
|
+
export async function planSitemap(context) {
|
|
56
|
+
const pages = await readPageUrls(context.siteUrl);
|
|
57
|
+
const sizes = [
|
|
58
|
+
{ source: PAGES_SOURCE, count: pages.length, urlsPerItem: 1 }
|
|
59
|
+
];
|
|
60
|
+
for (const [name, entry, table] of routedEntries()) {
|
|
61
|
+
sizes.push({
|
|
62
|
+
source: name,
|
|
63
|
+
count: await countContent(entry, table),
|
|
64
|
+
urlsPerItem: contentRoutePatterns(entry.route).length
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return { pages, sizes: sizes.filter((size) => size.count > 0) };
|
|
68
|
+
}
|
|
69
|
+
async function* contentUrls(name, context, offset, limit) {
|
|
70
|
+
const entry = cmsConfig[name];
|
|
71
|
+
const table = resolveTable(name);
|
|
72
|
+
if (!entry || !table) return;
|
|
73
|
+
const columns = tableColumns(table);
|
|
74
|
+
const where = contentWhere(entry, table);
|
|
75
|
+
const locales = contentRouteLocales(context.i18n);
|
|
76
|
+
let lastId = null;
|
|
77
|
+
let remaining = limit;
|
|
78
|
+
while (remaining > 0) {
|
|
79
|
+
const conditions = lastId === null ? where : [...where, gt(columns.id, lastId)];
|
|
80
|
+
const query = useDb().select({
|
|
81
|
+
id: columns.id,
|
|
82
|
+
slug: columns[CONTENT_SLUG_FIELD],
|
|
83
|
+
publishedAt: columns[CONTENT_PUBLISHED_AT_FIELD],
|
|
84
|
+
updatedAt: columns.updatedAt
|
|
85
|
+
}).from(table).where(conditions.length ? and(...conditions) : void 0).orderBy(asc(columns.id)).limit(Math.min(BATCH_SIZE, remaining));
|
|
86
|
+
const rows = await (lastId === null && offset ? query.offset(offset) : query);
|
|
87
|
+
if (!rows.length) return;
|
|
88
|
+
remaining -= rows.length;
|
|
89
|
+
lastId = String(rows.at(-1).id);
|
|
90
|
+
yield rows.flatMap(
|
|
91
|
+
(row) => sitemapUrlsForPaths(
|
|
92
|
+
contentItemPaths(entry, row, locales),
|
|
93
|
+
context.siteUrl,
|
|
94
|
+
context.i18n.defaultLocale,
|
|
95
|
+
sitemapLastmod(row.updatedAt)
|
|
96
|
+
)
|
|
97
|
+
);
|
|
98
|
+
if (rows.length < BATCH_SIZE) return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export async function* sitemapChunkUrls(plan, chunks, context) {
|
|
102
|
+
for (const chunk of chunks) {
|
|
103
|
+
if (chunk.source === PAGES_SOURCE) {
|
|
104
|
+
yield plan.pages.slice(chunk.offset, chunk.offset + chunk.limit);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
yield* contentUrls(chunk.source, context, chunk.offset, chunk.limit);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CmsAccess } from '../../shared/roles.js';
|
|
2
|
+
export declare function assertKnownRole(role: string): void;
|
|
3
|
+
export declare function assertCanGrantRole(access: CmsAccess, role: string): void;
|
|
4
|
+
export declare function assertCanManageAccount(access: CmsAccess, role: string): void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createError } from "h3";
|
|
2
|
+
import cmsConfig from "#cms-config";
|
|
3
|
+
import cmsRoles from "#cms-roles";
|
|
4
|
+
import { canGrantRole, canManageAccount, cmsRoleOptions } from "../../shared/roles.js";
|
|
5
|
+
import { forbidden } from "./permissions.js";
|
|
6
|
+
export function assertKnownRole(role) {
|
|
7
|
+
if (cmsRoleOptions(cmsRoles).some((option) => option.key === role)) return;
|
|
8
|
+
throw createError({ statusCode: 400, statusMessage: `Unknown role: ${role}` });
|
|
9
|
+
}
|
|
10
|
+
export function assertCanGrantRole(access, role) {
|
|
11
|
+
assertKnownRole(role);
|
|
12
|
+
if (!canGrantRole(access, role, cmsRoles, cmsConfig)) {
|
|
13
|
+
forbidden(`You cannot grant the role '${role}': it has permissions you do not hold`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function assertCanManageAccount(access, role) {
|
|
17
|
+
if (!canManageAccount(access, role, cmsRoles, cmsConfig)) {
|
|
18
|
+
forbidden("You cannot manage this account: its role has permissions you do not hold");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -2,3 +2,4 @@ import type { SQL } from 'drizzle-orm';
|
|
|
2
2
|
import type { AnySQLiteColumn } from 'drizzle-orm/sqlite-core';
|
|
3
3
|
import type { CmsEntry } from '../../shared/index.js';
|
|
4
4
|
export declare function visibilityConditions(entry: Pick<CmsEntry, 'kind' | 'drafts'>, columns: Record<string, AnySQLiteColumn>, now?: string): SQL[];
|
|
5
|
+
export declare function visibleOrPreviewConditions(entry: Pick<CmsEntry, 'kind' | 'drafts'>, columns: Record<string, AnySQLiteColumn>, previewId?: string, now?: string): SQL[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { eq, isNotNull, lte } from "drizzle-orm";
|
|
1
|
+
import { and, eq, isNotNull, lte, or } from "drizzle-orm";
|
|
2
2
|
import { CONTENT_PUBLISHED_AT_FIELD } from "../../shared/index.js";
|
|
3
3
|
import { nowTimestamp } from "../../shared/timestamps.js";
|
|
4
4
|
export function visibilityConditions(entry, columns, now = nowTimestamp()) {
|
|
@@ -10,3 +10,8 @@ export function visibilityConditions(entry, columns, now = nowTimestamp()) {
|
|
|
10
10
|
conditions.push(isNotNull(publishedAt), lte(publishedAt, now));
|
|
11
11
|
return conditions;
|
|
12
12
|
}
|
|
13
|
+
export function visibleOrPreviewConditions(entry, columns, previewId, now = nowTimestamp()) {
|
|
14
|
+
const visible = visibilityConditions(entry, columns, now);
|
|
15
|
+
if (!visible.length || previewId === void 0 || !columns.id) return visible;
|
|
16
|
+
return [or(and(...visible), eq(columns.id, previewId))];
|
|
17
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CmsEntryChange, CmsMediaChange } from '../../shared/hooks.js';
|
|
2
|
+
import type { CmsWebhookEntryPayload, CmsWebhookMediaPayload, CmsWebhookPayload } from '../../webhooks.js';
|
|
3
|
+
export interface CmsWebhookTarget {
|
|
4
|
+
name: string;
|
|
5
|
+
url: string;
|
|
6
|
+
secret: string;
|
|
7
|
+
events: string[];
|
|
8
|
+
timeout: number;
|
|
9
|
+
}
|
|
10
|
+
export type CmsWebhookFetch = (url: string, init: {
|
|
11
|
+
method: string;
|
|
12
|
+
headers: Record<string, string>;
|
|
13
|
+
body: string;
|
|
14
|
+
signal: AbortSignal;
|
|
15
|
+
}) => Promise<{
|
|
16
|
+
ok: boolean;
|
|
17
|
+
status: number;
|
|
18
|
+
}>;
|
|
19
|
+
export declare function entryWebhookPayload(change: CmsEntryChange): CmsWebhookEntryPayload;
|
|
20
|
+
export declare function mediaWebhookPayload(change: CmsMediaChange): CmsWebhookMediaPayload;
|
|
21
|
+
export declare function webhookWants(events: readonly string[], event: string): boolean;
|
|
22
|
+
export declare function webhookTargets(config: unknown): CmsWebhookTarget[];
|
|
23
|
+
export declare function deliverWebhook(target: CmsWebhookTarget, payload: CmsWebhookPayload, fetcher?: CmsWebhookFetch): Promise<void>;
|
|
24
|
+
export declare function deliverWebhooks(targets: CmsWebhookTarget[], payload: CmsWebhookPayload, fetcher?: CmsWebhookFetch, log?: (message: string) => void): Promise<void>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CMS_WEBHOOK_DELIVERY_HEADER,
|
|
3
|
+
CMS_WEBHOOK_EVENT_HEADER,
|
|
4
|
+
CMS_WEBHOOK_SIGNATURE_HEADER,
|
|
5
|
+
signCmsWebhook
|
|
6
|
+
} from "../../webhooks.js";
|
|
7
|
+
const DEFAULT_TIMEOUT = 5e3;
|
|
8
|
+
export function entryWebhookPayload(change) {
|
|
9
|
+
return {
|
|
10
|
+
event: `entry:${change.action}`,
|
|
11
|
+
action: change.action,
|
|
12
|
+
entry: change.entry,
|
|
13
|
+
kind: change.kind,
|
|
14
|
+
ids: change.ids,
|
|
15
|
+
path: change.path,
|
|
16
|
+
status: change.status,
|
|
17
|
+
at: change.at
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function mediaWebhookPayload(change) {
|
|
21
|
+
return {
|
|
22
|
+
event: `media:${change.action}`,
|
|
23
|
+
action: change.action,
|
|
24
|
+
keys: change.keys,
|
|
25
|
+
folder: change.folder,
|
|
26
|
+
from: change.from,
|
|
27
|
+
at: change.at
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function webhookWants(events, event) {
|
|
31
|
+
if (!events.length) return true;
|
|
32
|
+
const group = event.slice(0, event.indexOf(":"));
|
|
33
|
+
return events.includes(event) || events.includes(group);
|
|
34
|
+
}
|
|
35
|
+
export function webhookTargets(config) {
|
|
36
|
+
if (!config || typeof config !== "object") return [];
|
|
37
|
+
return Object.entries(config).flatMap(
|
|
38
|
+
([name, value]) => {
|
|
39
|
+
const url = value?.url == null ? "" : String(value.url);
|
|
40
|
+
if (!url) return [];
|
|
41
|
+
const timeout = Number(value?.timeout);
|
|
42
|
+
return [
|
|
43
|
+
{
|
|
44
|
+
name,
|
|
45
|
+
url,
|
|
46
|
+
secret: value?.secret == null ? "" : String(value.secret),
|
|
47
|
+
events: Array.isArray(value?.events) ? value.events.map(String) : [],
|
|
48
|
+
timeout: Number.isInteger(timeout) && timeout > 0 ? timeout : DEFAULT_TIMEOUT
|
|
49
|
+
}
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
function describeTarget(target) {
|
|
55
|
+
try {
|
|
56
|
+
return `'${target.name}' (${new URL(target.url).host})`;
|
|
57
|
+
} catch {
|
|
58
|
+
return `'${target.name}'`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export async function deliverWebhook(target, payload, fetcher = globalThis.fetch) {
|
|
62
|
+
const body = JSON.stringify(payload);
|
|
63
|
+
const headers = {
|
|
64
|
+
"content-type": "application/json",
|
|
65
|
+
"user-agent": "nuxt-cms-webhooks",
|
|
66
|
+
[CMS_WEBHOOK_EVENT_HEADER]: payload.event,
|
|
67
|
+
[CMS_WEBHOOK_DELIVERY_HEADER]: globalThis.crypto.randomUUID()
|
|
68
|
+
};
|
|
69
|
+
if (target.secret)
|
|
70
|
+
headers[CMS_WEBHOOK_SIGNATURE_HEADER] = await signCmsWebhook(body, target.secret);
|
|
71
|
+
const response = await fetcher(target.url, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers,
|
|
74
|
+
body,
|
|
75
|
+
signal: AbortSignal.timeout(target.timeout)
|
|
76
|
+
});
|
|
77
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
78
|
+
}
|
|
79
|
+
export async function deliverWebhooks(targets, payload, fetcher, log = (message) => console.warn(message)) {
|
|
80
|
+
await Promise.all(
|
|
81
|
+
targets.filter((target) => webhookWants(target.events, payload.event)).map(
|
|
82
|
+
(target) => deliverWebhook(target, payload, fetcher).catch((error) => {
|
|
83
|
+
const reason = error instanceof Error ? error.name === "TimeoutError" ? `timed out after ${target.timeout} ms` : error.message : String(error);
|
|
84
|
+
log(
|
|
85
|
+
`[nuxt-cms] webhook ${describeTarget(target)} failed on ${payload.event}: ${reason}`
|
|
86
|
+
);
|
|
87
|
+
})
|
|
88
|
+
)
|
|
89
|
+
);
|
|
90
|
+
}
|