@aphexcms/cms-core 9.3.0 → 9.5.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/dist/admin/block-previews.svelte.d.ts +46 -0
- package/dist/admin/block-previews.svelte.d.ts.map +1 -0
- package/dist/admin/block-previews.svelte.js +26 -0
- package/dist/admin/field-components.svelte.d.ts +16 -0
- package/dist/admin/field-components.svelte.d.ts.map +1 -0
- package/dist/admin/field-components.svelte.js +18 -0
- package/dist/admin/nav.svelte.d.ts +34 -0
- package/dist/admin/nav.svelte.d.ts.map +1 -0
- package/dist/admin/nav.svelte.js +78 -0
- package/dist/admin/slots.svelte.d.ts +50 -0
- package/dist/admin/slots.svelte.d.ts.map +1 -0
- package/dist/admin/slots.svelte.js +67 -0
- package/dist/admin/types.d.ts +7 -0
- package/dist/admin/types.d.ts.map +1 -0
- package/dist/admin/types.js +1 -0
- package/dist/api/schemas/api-keys.d.ts +4 -37
- package/dist/api/schemas/api-keys.d.ts.map +1 -1
- package/dist/api/schemas/api-keys.js +5 -2
- package/dist/api/schemas/assets.d.ts +4 -0
- package/dist/api/schemas/assets.d.ts.map +1 -1
- package/dist/api/schemas/assets.js +3 -0
- package/dist/api/schemas/plugin-settings.d.ts +6 -0
- package/dist/api/schemas/plugin-settings.d.ts.map +1 -0
- package/dist/api/schemas/plugin-settings.js +8 -0
- package/dist/api/schemas/roles.d.ts +7 -56
- package/dist/api/schemas/roles.d.ts.map +1 -1
- package/dist/api/schemas/roles.js +11 -6
- package/dist/api/schemas/user.d.ts +2 -1
- package/dist/api/schemas/user.d.ts.map +1 -1
- package/dist/api/schemas/user.js +7 -2
- package/dist/app-augment.d.ts +11 -0
- package/dist/app-augment.d.ts.map +1 -1
- package/dist/app.d.ts +2 -0
- package/dist/auth/provider.d.ts +3 -0
- package/dist/auth/provider.d.ts.map +1 -1
- package/dist/cli/generate-types.d.ts +10 -5
- package/dist/cli/generate-types.d.ts.map +1 -1
- package/dist/cli/generate-types.js +117 -77
- package/dist/cli/generate-types.js.map +1 -1
- package/dist/cli/index.js +5 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/migrate.d.ts +1 -1
- package/dist/cli/migrate.d.ts.map +1 -1
- package/dist/cli/migrate.js +27 -2
- package/dist/cli/migrate.js.map +1 -1
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +9 -0
- package/dist/components/AdminApp.svelte +271 -113
- package/dist/components/AdminApp.svelte.d.ts +18 -2
- package/dist/components/AdminApp.svelte.d.ts.map +1 -1
- package/dist/components/admin/AdminSlot.svelte +18 -0
- package/dist/components/admin/AdminSlot.svelte.d.ts +12 -0
- package/dist/components/admin/AdminSlot.svelte.d.ts.map +1 -0
- package/dist/components/admin/DocumentEditor.svelte +646 -385
- package/dist/components/admin/DocumentEditor.svelte.d.ts +13 -0
- package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
- package/dist/components/admin/MediaBrowser.svelte +13 -3
- package/dist/components/admin/MediaBrowser.svelte.d.ts.map +1 -1
- package/dist/components/admin/PluginSettingsPanel.svelte +231 -0
- package/dist/components/admin/PluginSettingsPanel.svelte.d.ts +8 -0
- package/dist/components/admin/PluginSettingsPanel.svelte.d.ts.map +1 -0
- package/dist/components/admin/SchemaField.svelte +76 -95
- package/dist/components/admin/SchemaField.svelte.d.ts +7 -0
- package/dist/components/admin/SchemaField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ArrayField.svelte +24 -2
- package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/FieldInput.svelte +109 -0
- package/dist/components/admin/fields/FieldInput.svelte.d.ts +17 -0
- package/dist/components/admin/fields/FieldInput.svelte.d.ts.map +1 -0
- package/dist/components/admin/fields/richtext/PortableTextImageView.svelte +5 -12
- package/dist/components/admin/fields/richtext/PortableTextImageView.svelte.d.ts +2 -9
- package/dist/components/admin/fields/richtext/PortableTextImageView.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/richtext/PortableTextObjectView.svelte +18 -17
- package/dist/components/admin/fields/richtext/PortableTextObjectView.svelte.d.ts +2 -9
- package/dist/components/admin/fields/richtext/PortableTextObjectView.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/richtext/RichtextField.svelte +55 -2
- package/dist/components/admin/fields/richtext/RichtextField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/richtext/portable-text-object-node.d.ts +7 -0
- package/dist/components/admin/fields/richtext/portable-text-object-node.d.ts.map +1 -1
- package/dist/components/admin/fields/richtext/portable-text-object-node.js +4 -2
- package/dist/components/admin/fields/richtext/svelte-node-view.d.ts +16 -1
- package/dist/components/admin/fields/richtext/svelte-node-view.d.ts.map +1 -1
- package/dist/components/admin/fields/richtext/svelte-node-view.js +20 -1
- package/dist/components/layout/OrganizationSwitcher.svelte +31 -10
- package/dist/components/layout/OrganizationSwitcher.svelte.d.ts.map +1 -1
- package/dist/components/layout/Sidebar.svelte +49 -11
- package/dist/components/layout/Sidebar.svelte.d.ts +5 -1
- package/dist/components/layout/Sidebar.svelte.d.ts.map +1 -1
- package/dist/components/layout/sidebar/AppSidebar.svelte +66 -3
- package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts +5 -0
- package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts.map +1 -1
- package/dist/components/layout/sidebar/NavMain.svelte +3 -2
- package/dist/components/layout/sidebar/NavMain.svelte.d.ts +4 -3
- package/dist/components/layout/sidebar/NavMain.svelte.d.ts.map +1 -1
- package/dist/components/layout/sidebar/NavUser.svelte +5 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +11 -0
- package/dist/db/interfaces/asset.d.ts +1 -0
- package/dist/db/interfaces/asset.d.ts.map +1 -1
- package/dist/db/interfaces/index.d.ts +3 -1
- package/dist/db/interfaces/index.d.ts.map +1 -1
- package/dist/db/interfaces/plugin-settings.d.ts +23 -0
- package/dist/db/interfaces/plugin-settings.d.ts.map +1 -0
- package/dist/db/interfaces/plugin-settings.js +13 -0
- package/dist/db/interfaces/role.d.ts +14 -5
- package/dist/db/interfaces/role.d.ts.map +1 -1
- package/dist/engine.d.ts +28 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +44 -0
- package/dist/field-validation/utils.d.ts +15 -0
- package/dist/field-validation/utils.d.ts.map +1 -1
- package/dist/field-validation/utils.js +74 -0
- package/dist/graphql/resolvers.d.ts.map +1 -1
- package/dist/graphql/resolvers.js +9 -10
- package/dist/graphql/schema.d.ts.map +1 -1
- package/dist/graphql/schema.js +18 -19
- package/dist/hooks.d.ts +6 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +59 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/lib/admin/block-previews.svelte.d.ts +46 -0
- package/dist/lib/admin/block-previews.svelte.d.ts.map +1 -0
- package/dist/lib/admin/block-previews.svelte.js +27 -0
- package/dist/lib/admin/block-previews.svelte.js.map +1 -0
- package/dist/lib/admin/field-components.svelte.d.ts +16 -0
- package/dist/lib/admin/field-components.svelte.d.ts.map +1 -0
- package/dist/lib/admin/field-components.svelte.js +19 -0
- package/dist/lib/admin/field-components.svelte.js.map +1 -0
- package/dist/lib/admin/nav.svelte.d.ts +34 -0
- package/dist/lib/admin/nav.svelte.d.ts.map +1 -0
- package/dist/lib/admin/nav.svelte.js +79 -0
- package/dist/lib/admin/nav.svelte.js.map +1 -0
- package/dist/lib/admin/slots.svelte.d.ts +50 -0
- package/dist/lib/admin/slots.svelte.d.ts.map +1 -0
- package/dist/lib/admin/slots.svelte.js +68 -0
- package/dist/lib/admin/slots.svelte.js.map +1 -0
- package/dist/lib/admin/types.d.ts +7 -0
- package/dist/lib/admin/types.d.ts.map +1 -0
- package/dist/lib/admin/types.js +2 -0
- package/dist/lib/admin/types.js.map +1 -0
- package/dist/lib/api/schemas/api-keys.d.ts +4 -37
- package/dist/lib/api/schemas/api-keys.d.ts.map +1 -1
- package/dist/lib/api/schemas/api-keys.js +5 -2
- package/dist/lib/api/schemas/api-keys.js.map +1 -1
- package/dist/lib/api/schemas/assets.d.ts +4 -0
- package/dist/lib/api/schemas/assets.d.ts.map +1 -1
- package/dist/lib/api/schemas/assets.js +3 -0
- package/dist/lib/api/schemas/assets.js.map +1 -1
- package/dist/lib/api/schemas/plugin-settings.d.ts +6 -0
- package/dist/lib/api/schemas/plugin-settings.d.ts.map +1 -0
- package/dist/lib/api/schemas/plugin-settings.js +9 -0
- package/dist/lib/api/schemas/plugin-settings.js.map +1 -0
- package/dist/lib/api/schemas/roles.d.ts +7 -56
- package/dist/lib/api/schemas/roles.d.ts.map +1 -1
- package/dist/lib/api/schemas/roles.js +11 -6
- package/dist/lib/api/schemas/roles.js.map +1 -1
- package/dist/lib/api/schemas/user.d.ts +2 -1
- package/dist/lib/api/schemas/user.d.ts.map +1 -1
- package/dist/lib/api/schemas/user.js +7 -2
- package/dist/lib/api/schemas/user.js.map +1 -1
- package/dist/lib/app-augment.d.ts +11 -0
- package/dist/lib/app-augment.d.ts.map +1 -1
- package/dist/lib/auth/provider.d.ts +3 -0
- package/dist/lib/auth/provider.d.ts.map +1 -1
- package/dist/lib/client/index.d.ts +6 -0
- package/dist/lib/client/index.d.ts.map +1 -1
- package/dist/lib/client/index.js +9 -0
- package/dist/lib/client/index.js.map +1 -1
- package/dist/lib/components/admin/fields/richtext/portable-text-object-node.d.ts +7 -0
- package/dist/lib/components/admin/fields/richtext/portable-text-object-node.d.ts.map +1 -1
- package/dist/lib/components/admin/fields/richtext/portable-text-object-node.js +4 -2
- package/dist/lib/components/admin/fields/richtext/portable-text-object-node.js.map +1 -1
- package/dist/lib/components/admin/fields/richtext/svelte-node-view.d.ts +16 -1
- package/dist/lib/components/admin/fields/richtext/svelte-node-view.d.ts.map +1 -1
- package/dist/lib/components/admin/fields/richtext/svelte-node-view.js +20 -1
- package/dist/lib/components/admin/fields/richtext/svelte-node-view.js.map +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +11 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/db/interfaces/asset.d.ts +1 -0
- package/dist/lib/db/interfaces/asset.d.ts.map +1 -1
- package/dist/lib/db/interfaces/index.d.ts +3 -1
- package/dist/lib/db/interfaces/index.d.ts.map +1 -1
- package/dist/lib/db/interfaces/plugin-settings.d.ts +23 -0
- package/dist/lib/db/interfaces/plugin-settings.d.ts.map +1 -0
- package/dist/lib/db/interfaces/plugin-settings.js +14 -0
- package/dist/lib/db/interfaces/plugin-settings.js.map +1 -0
- package/dist/lib/db/interfaces/role.d.ts +14 -5
- package/dist/lib/db/interfaces/role.d.ts.map +1 -1
- package/dist/lib/engine.d.ts +28 -0
- package/dist/lib/engine.d.ts.map +1 -1
- package/dist/lib/engine.js +44 -0
- package/dist/lib/engine.js.map +1 -1
- package/dist/lib/field-validation/utils.d.ts +15 -0
- package/dist/lib/field-validation/utils.d.ts.map +1 -1
- package/dist/lib/field-validation/utils.js +74 -0
- package/dist/lib/field-validation/utils.js.map +1 -1
- package/dist/lib/graphql/resolvers.d.ts.map +1 -1
- package/dist/lib/graphql/resolvers.js +9 -10
- package/dist/lib/graphql/resolvers.js.map +1 -1
- package/dist/lib/graphql/schema.d.ts.map +1 -1
- package/dist/lib/graphql/schema.js +18 -19
- package/dist/lib/graphql/schema.js.map +1 -1
- package/dist/lib/hooks.d.ts +6 -0
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/hooks.js +59 -2
- package/dist/lib/hooks.js.map +1 -1
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +5 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/local-api/collection-api.d.ts.map +1 -1
- package/dist/lib/local-api/collection-api.js +4 -2
- package/dist/lib/local-api/collection-api.js.map +1 -1
- package/dist/lib/local-api/types.d.ts +8 -0
- package/dist/lib/local-api/types.d.ts.map +1 -1
- package/dist/lib/mcp/tools.d.ts.map +1 -1
- package/dist/lib/mcp/tools.js +110 -1
- package/dist/lib/mcp/tools.js.map +1 -1
- package/dist/lib/permissions-context.svelte.d.ts +7 -4
- package/dist/lib/permissions-context.svelte.d.ts.map +1 -1
- package/dist/lib/permissions-context.svelte.js.map +1 -1
- package/dist/lib/plugins/index.d.ts +3 -0
- package/dist/lib/plugins/index.d.ts.map +1 -0
- package/dist/lib/plugins/index.js +3 -0
- package/dist/lib/plugins/index.js.map +1 -0
- package/dist/lib/plugins/resolver.d.ts +49 -0
- package/dist/lib/plugins/resolver.d.ts.map +1 -0
- package/dist/lib/plugins/resolver.js +66 -0
- package/dist/lib/plugins/resolver.js.map +1 -0
- package/dist/lib/plugins/types.d.ts +233 -0
- package/dist/lib/plugins/types.d.ts.map +1 -0
- package/dist/lib/plugins/types.js +9 -0
- package/dist/lib/plugins/types.js.map +1 -0
- package/dist/lib/schema-utils/desugar.d.ts +27 -0
- package/dist/lib/schema-utils/desugar.d.ts.map +1 -0
- package/dist/lib/schema-utils/desugar.js +102 -0
- package/dist/lib/schema-utils/desugar.js.map +1 -0
- package/dist/lib/schema-utils/index.d.ts +2 -0
- package/dist/lib/schema-utils/index.d.ts.map +1 -1
- package/dist/lib/schema-utils/index.js +2 -0
- package/dist/lib/schema-utils/index.js.map +1 -1
- package/dist/lib/schema-utils/settings.d.ts +17 -0
- package/dist/lib/schema-utils/settings.d.ts.map +1 -0
- package/dist/lib/schema-utils/settings.js +23 -0
- package/dist/lib/schema-utils/settings.js.map +1 -0
- package/dist/lib/schema-utils/validator.d.ts.map +1 -1
- package/dist/lib/schema-utils/validator.js +11 -0
- package/dist/lib/schema-utils/validator.js.map +1 -1
- package/dist/lib/security/secret-crypto.d.ts +18 -0
- package/dist/lib/security/secret-crypto.d.ts.map +1 -0
- package/dist/lib/security/secret-crypto.js +71 -0
- package/dist/lib/security/secret-crypto.js.map +1 -0
- package/dist/lib/server/api/index.d.ts.map +1 -1
- package/dist/lib/server/api/index.js +3 -0
- package/dist/lib/server/api/index.js.map +1 -1
- package/dist/lib/server/api/routes/assets.d.ts.map +1 -1
- package/dist/lib/server/api/routes/assets.js +8 -2
- package/dist/lib/server/api/routes/assets.js.map +1 -1
- package/dist/lib/server/api/routes/organizations.d.ts.map +1 -1
- package/dist/lib/server/api/routes/organizations.js +4 -1
- package/dist/lib/server/api/routes/organizations.js.map +1 -1
- package/dist/lib/server/api/routes/plugin-settings.d.ts +4 -0
- package/dist/lib/server/api/routes/plugin-settings.d.ts.map +1 -0
- package/dist/lib/server/api/routes/plugin-settings.js +109 -0
- package/dist/lib/server/api/routes/plugin-settings.js.map +1 -0
- package/dist/lib/server/api/routes/roles.d.ts.map +1 -1
- package/dist/lib/server/api/routes/roles.js +37 -0
- package/dist/lib/server/api/routes/roles.js.map +1 -1
- package/dist/lib/server/api/routes/user.d.ts.map +1 -1
- package/dist/lib/server/api/routes/user.js +14 -3
- package/dist/lib/server/api/routes/user.js.map +1 -1
- package/dist/lib/services/asset-service.d.ts +1 -0
- package/dist/lib/services/asset-service.d.ts.map +1 -1
- package/dist/lib/services/asset-service.js.map +1 -1
- package/dist/lib/services/index.d.ts +2 -0
- package/dist/lib/services/index.d.ts.map +1 -1
- package/dist/lib/services/index.js +1 -0
- package/dist/lib/services/index.js.map +1 -1
- package/dist/lib/services/plugin-settings-service.d.ts +76 -0
- package/dist/lib/services/plugin-settings-service.d.ts.map +1 -0
- package/dist/lib/services/plugin-settings-service.js +236 -0
- package/dist/lib/services/plugin-settings-service.js.map +1 -0
- package/dist/lib/services/roles-service.d.ts +2 -2
- package/dist/lib/services/roles-service.d.ts.map +1 -1
- package/dist/lib/services/roles-service.js.map +1 -1
- package/dist/lib/theme/css.d.ts +30 -0
- package/dist/lib/theme/css.d.ts.map +1 -0
- package/dist/lib/theme/css.js +54 -0
- package/dist/lib/theme/css.js.map +1 -0
- package/dist/lib/theme/derive.d.ts +27 -0
- package/dist/lib/theme/derive.d.ts.map +1 -0
- package/dist/lib/theme/derive.js +108 -0
- package/dist/lib/theme/derive.js.map +1 -0
- package/dist/lib/theme/index.d.ts +5 -0
- package/dist/lib/theme/index.d.ts.map +1 -0
- package/dist/lib/theme/index.js +5 -0
- package/dist/lib/theme/index.js.map +1 -0
- package/dist/lib/theme/schemes.d.ts +60 -0
- package/dist/lib/theme/schemes.d.ts.map +1 -0
- package/dist/lib/theme/schemes.js +150 -0
- package/dist/lib/theme/schemes.js.map +1 -0
- package/dist/lib/theme/tokens.d.ts +57 -0
- package/dist/lib/theme/tokens.d.ts.map +1 -0
- package/dist/lib/theme/tokens.js +11 -0
- package/dist/lib/theme/tokens.js.map +1 -0
- package/dist/lib/types/auth.d.ts +10 -10
- package/dist/lib/types/auth.d.ts.map +1 -1
- package/dist/lib/types/capabilities.d.ts +50 -9
- package/dist/lib/types/capabilities.d.ts.map +1 -1
- package/dist/lib/types/capabilities.js +147 -5
- package/dist/lib/types/capabilities.js.map +1 -1
- package/dist/lib/types/config.d.ts +30 -1
- package/dist/lib/types/config.d.ts.map +1 -1
- package/dist/lib/types/schemas.d.ts +90 -2
- package/dist/lib/types/schemas.d.ts.map +1 -1
- package/dist/lib/types/schemas.js.map +1 -1
- package/dist/lib/types/sidebar.d.ts +3 -1
- package/dist/lib/types/sidebar.d.ts.map +1 -1
- package/dist/lib/utils/index.d.ts +1 -0
- package/dist/lib/utils/index.d.ts.map +1 -1
- package/dist/lib/utils/index.js +1 -0
- package/dist/lib/utils/index.js.map +1 -1
- package/dist/lib/utils/preview.d.ts +16 -5
- package/dist/lib/utils/preview.d.ts.map +1 -1
- package/dist/lib/utils/preview.js +7 -2
- package/dist/lib/utils/preview.js.map +1 -1
- package/dist/lib/utils/string-case.d.ts +21 -0
- package/dist/lib/utils/string-case.d.ts.map +1 -0
- package/dist/lib/utils/string-case.js +26 -0
- package/dist/lib/utils/string-case.js.map +1 -0
- package/dist/lib/vite/index.d.ts +7 -0
- package/dist/lib/vite/index.d.ts.map +1 -1
- package/dist/lib/vite/index.js +37 -6
- package/dist/lib/vite/index.js.map +1 -1
- package/dist/local-api/collection-api.d.ts.map +1 -1
- package/dist/local-api/collection-api.js +4 -2
- package/dist/local-api/types.d.ts +8 -0
- package/dist/local-api/types.d.ts.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +110 -1
- package/dist/permissions-context.svelte.d.ts +7 -4
- package/dist/permissions-context.svelte.d.ts.map +1 -1
- package/dist/plugins/index.d.ts +3 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +2 -0
- package/dist/plugins/resolver.d.ts +49 -0
- package/dist/plugins/resolver.d.ts.map +1 -0
- package/dist/plugins/resolver.js +65 -0
- package/dist/plugins/types.d.ts +233 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +8 -0
- package/dist/schema-utils/desugar.d.ts +27 -0
- package/dist/schema-utils/desugar.d.ts.map +1 -0
- package/dist/schema-utils/desugar.js +101 -0
- package/dist/schema-utils/index.d.ts +2 -0
- package/dist/schema-utils/index.d.ts.map +1 -1
- package/dist/schema-utils/index.js +2 -0
- package/dist/schema-utils/settings.d.ts +17 -0
- package/dist/schema-utils/settings.d.ts.map +1 -0
- package/dist/schema-utils/settings.js +22 -0
- package/dist/schema-utils/validator.d.ts.map +1 -1
- package/dist/schema-utils/validator.js +11 -0
- package/dist/security/secret-crypto.d.ts +18 -0
- package/dist/security/secret-crypto.d.ts.map +1 -0
- package/dist/security/secret-crypto.js +70 -0
- package/dist/server/api/index.d.ts.map +1 -1
- package/dist/server/api/index.js +3 -0
- package/dist/server/api/routes/assets.d.ts.map +1 -1
- package/dist/server/api/routes/assets.js +8 -2
- package/dist/server/api/routes/organizations.d.ts.map +1 -1
- package/dist/server/api/routes/organizations.js +4 -1
- package/dist/server/api/routes/plugin-settings.d.ts +4 -0
- package/dist/server/api/routes/plugin-settings.d.ts.map +1 -0
- package/dist/server/api/routes/plugin-settings.js +108 -0
- package/dist/server/api/routes/roles.d.ts.map +1 -1
- package/dist/server/api/routes/roles.js +37 -0
- package/dist/server/api/routes/user.d.ts.map +1 -1
- package/dist/server/api/routes/user.js +14 -3
- package/dist/services/asset-service.d.ts +1 -0
- package/dist/services/asset-service.d.ts.map +1 -1
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -0
- package/dist/services/plugin-settings-service.d.ts +76 -0
- package/dist/services/plugin-settings-service.d.ts.map +1 -0
- package/dist/services/plugin-settings-service.js +235 -0
- package/dist/services/roles-service.d.ts +2 -2
- package/dist/services/roles-service.d.ts.map +1 -1
- package/dist/theme/css.d.ts +30 -0
- package/dist/theme/css.d.ts.map +1 -0
- package/dist/theme/css.js +53 -0
- package/dist/theme/derive.d.ts +27 -0
- package/dist/theme/derive.d.ts.map +1 -0
- package/dist/theme/derive.js +107 -0
- package/dist/theme/index.d.ts +5 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/index.js +4 -0
- package/dist/theme/schemes.d.ts +60 -0
- package/dist/theme/schemes.d.ts.map +1 -0
- package/dist/theme/schemes.js +149 -0
- package/dist/theme/tokens.d.ts +57 -0
- package/dist/theme/tokens.d.ts.map +1 -0
- package/dist/theme/tokens.js +10 -0
- package/dist/types/auth.d.ts +10 -10
- package/dist/types/auth.d.ts.map +1 -1
- package/dist/types/capabilities.d.ts +50 -9
- package/dist/types/capabilities.d.ts.map +1 -1
- package/dist/types/capabilities.js +147 -5
- package/dist/types/config.d.ts +30 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/schemas.d.ts +90 -2
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/sidebar.d.ts +3 -1
- package/dist/types/sidebar.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/preview.d.ts +16 -5
- package/dist/utils/preview.d.ts.map +1 -1
- package/dist/utils/preview.js +7 -2
- package/dist/utils/string-case.d.ts +21 -0
- package/dist/utils/string-case.d.ts.map +1 -0
- package/dist/utils/string-case.js +25 -0
- package/dist/vite/index.d.ts +7 -0
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +37 -6
- package/package.json +6 -4
|
@@ -78,7 +78,10 @@ export const organizationsRouter = new Hono()
|
|
|
78
78
|
parentOrganizationId: auth.organizationId,
|
|
79
79
|
createdBy: auth.user.id
|
|
80
80
|
});
|
|
81
|
-
|
|
81
|
+
// Seed with the install's full capability set (core + plugin-declared), the
|
|
82
|
+
// same one the boot reconcile uses — otherwise a brand-new org's owner would
|
|
83
|
+
// be missing every capability its plugins declare until the next restart.
|
|
84
|
+
await databaseAdapter.seedBuiltinRoles(newOrganization.id, c.var.aphexCMS.cmsEngine.ownerCapabilities());
|
|
82
85
|
await databaseAdapter.addMember({
|
|
83
86
|
organizationId: newOrganization.id,
|
|
84
87
|
userId: auth.user.id,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-settings.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/api/routes/plugin-settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAM5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AA6DzC,eAAO,MAAM,oBAAoB,EAAE,IAAI,CAAC,QAAQ,CA2E7C,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { zValidator } from '@hono/zod-validator';
|
|
3
|
+
import { cmsLogger } from '../../../utils/logger.js';
|
|
4
|
+
import { hasCapability } from '../../../types/capabilities.js';
|
|
5
|
+
import { savePluginSettingsRequest } from '../../../api/schemas/plugin-settings.js';
|
|
6
|
+
import { PluginSettingsValidationError } from '../../../services/plugin-settings-service.js';
|
|
7
|
+
/**
|
|
8
|
+
* Require a session with `plugin.settings.manage`. Returns the narrowed session auth
|
|
9
|
+
* to proceed, or a 401/403 Response to short-circuit — so callers get a typed org id
|
|
10
|
+
* without a non-null assertion.
|
|
11
|
+
*/
|
|
12
|
+
function requireManage(c) {
|
|
13
|
+
const auth = c.var.auth;
|
|
14
|
+
if (!auth || auth.type !== 'session') {
|
|
15
|
+
return c.json({ success: false, error: 'Unauthorized', message: 'Session authentication required' }, 401);
|
|
16
|
+
}
|
|
17
|
+
if (!hasCapability(auth, 'plugin.settings.manage')) {
|
|
18
|
+
return c.json({
|
|
19
|
+
success: false,
|
|
20
|
+
error: 'Forbidden',
|
|
21
|
+
message: 'The plugin.settings.manage capability is required'
|
|
22
|
+
}, 403);
|
|
23
|
+
}
|
|
24
|
+
return auth;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Does this session satisfy a settings section's own `requiredCapabilities`?
|
|
28
|
+
*
|
|
29
|
+
* `plugin.settings.manage` is the floor, checked by `requireManage`. A declaration may
|
|
30
|
+
* additionally name narrower capabilities to gate itself more tightly — the part's
|
|
31
|
+
* documented contract, and the same convention `hooks.ts` applies to plugin routes and
|
|
32
|
+
* `resolver.ts` applies to actions and tools. Without this the declared gate was
|
|
33
|
+
* silently ignored, so anyone who could manage settings could overwrite every plugin's
|
|
34
|
+
* secrets regardless of what the plugin asked for.
|
|
35
|
+
*/
|
|
36
|
+
function canAccessSettings(auth, required) {
|
|
37
|
+
if (!required || required.length === 0)
|
|
38
|
+
return true;
|
|
39
|
+
return required.every((capability) => hasCapability(auth, capability));
|
|
40
|
+
}
|
|
41
|
+
export const pluginSettingsRouter = new Hono()
|
|
42
|
+
.get('/', async (c) => {
|
|
43
|
+
try {
|
|
44
|
+
const auth = requireManage(c);
|
|
45
|
+
if (auth instanceof Response)
|
|
46
|
+
return auth;
|
|
47
|
+
const { pluginSettingsService, partResolver } = c.var.aphexCMS;
|
|
48
|
+
// One entry per plugin that declared settings AND that this session is
|
|
49
|
+
// allowed to see, with the org's effective values. `getMasked` — never
|
|
50
|
+
// `get` — so secret plaintext never reaches the client (secrets come back
|
|
51
|
+
// as a mask placeholder).
|
|
52
|
+
const declarations = partResolver
|
|
53
|
+
.settingsDeclarations()
|
|
54
|
+
.filter((decl) => canAccessSettings(auth, decl.requiredCapabilities));
|
|
55
|
+
const secretsEnabled = pluginSettingsService.secretsEnabled;
|
|
56
|
+
const data = await Promise.all(declarations.map(async (decl) => ({
|
|
57
|
+
pluginId: decl.pluginId,
|
|
58
|
+
title: decl.title,
|
|
59
|
+
values: await pluginSettingsService.getMasked(auth.organizationId, decl.pluginId)
|
|
60
|
+
})));
|
|
61
|
+
return c.json({ success: true, data, secretsEnabled });
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
cmsLogger.error('Failed to list plugin settings:', error);
|
|
65
|
+
return c.json({ success: false, error: 'Internal error' }, 500);
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
.put('/:pluginId', zValidator('json', savePluginSettingsRequest), async (c) => {
|
|
69
|
+
try {
|
|
70
|
+
const auth = requireManage(c);
|
|
71
|
+
if (auth instanceof Response)
|
|
72
|
+
return auth;
|
|
73
|
+
const { pluginSettingsService, partResolver } = c.var.aphexCMS;
|
|
74
|
+
const pluginId = c.req.param('pluginId');
|
|
75
|
+
// Reject plugins that never declared settings — nothing to write to.
|
|
76
|
+
const declaration = partResolver.settingsDeclaration(pluginId);
|
|
77
|
+
if (!declaration) {
|
|
78
|
+
return c.json({
|
|
79
|
+
success: false,
|
|
80
|
+
error: 'Unknown plugin settings',
|
|
81
|
+
message: `Plugin "${pluginId}" has not declared any settings.`
|
|
82
|
+
}, 404);
|
|
83
|
+
}
|
|
84
|
+
// Honour the declaration's own gate. GET only ever returns masked secrets,
|
|
85
|
+
// so the exposure this closes is write: without it, anyone holding
|
|
86
|
+
// `plugin.settings.manage` could overwrite the secrets of a plugin that
|
|
87
|
+
// asked for a narrower capability.
|
|
88
|
+
if (!canAccessSettings(auth, declaration.requiredCapabilities)) {
|
|
89
|
+
return c.json({
|
|
90
|
+
success: false,
|
|
91
|
+
error: 'Forbidden',
|
|
92
|
+
message: `Plugin "${pluginId}" requires: ${declaration.requiredCapabilities?.join(', ')}`
|
|
93
|
+
}, 403);
|
|
94
|
+
}
|
|
95
|
+
const { values } = c.req.valid('json');
|
|
96
|
+
const saved = await pluginSettingsService.save(auth.organizationId, pluginId, values);
|
|
97
|
+
return c.json({ success: true, data: { pluginId, values: saved } });
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
// A value that doesn't match its declared field type is the caller's fault, not
|
|
101
|
+
// ours — report it as such, with the specific issues, rather than an opaque 500.
|
|
102
|
+
if (error instanceof PluginSettingsValidationError) {
|
|
103
|
+
return c.json({ success: false, error: 'Validation failed', issues: error.issues }, 400);
|
|
104
|
+
}
|
|
105
|
+
cmsLogger.error('Failed to save plugin settings:', error);
|
|
106
|
+
return c.json({ success: false, error: 'Internal error' }, 500);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"roles.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/api/routes/roles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAK5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"roles.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/api/routes/roles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAK5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAwBzC;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,IAAI,CAAC,QAAQ,CA+TpC,CAAC"}
|
|
@@ -3,6 +3,24 @@ import { zValidator } from '@hono/zod-validator';
|
|
|
3
3
|
import { cmsLogger } from '../../../utils/logger.js';
|
|
4
4
|
import { hasCapability, BUILTIN_ROLE_NAMES } from '../../../types/capabilities.js';
|
|
5
5
|
import { createRoleRequest, updateRoleRequest } from '../../../api/schemas/roles.js';
|
|
6
|
+
/**
|
|
7
|
+
* Authoritative capability validation against the runtime registry. The zod schema
|
|
8
|
+
* only guards the id *format*; this rejects ids that don't actually exist in the
|
|
9
|
+
* catalog (built-in + plugin-declared), so a role can't be granted a phantom
|
|
10
|
+
* capability. Returns a 400 Response to short-circuit, or null when all are valid.
|
|
11
|
+
*/
|
|
12
|
+
function rejectUnknownCapabilities(c, caps) {
|
|
13
|
+
const known = new Set(c.var.aphexCMS.partResolver.capabilityCatalog().map((d) => d.id));
|
|
14
|
+
const unknown = caps.filter((cap) => !known.has(cap));
|
|
15
|
+
if (unknown.length === 0)
|
|
16
|
+
return null;
|
|
17
|
+
return c.json({
|
|
18
|
+
success: false,
|
|
19
|
+
error: 'Unknown capability',
|
|
20
|
+
message: `These capabilities are not registered: ${unknown.join(', ')}`,
|
|
21
|
+
unknownCapabilities: unknown
|
|
22
|
+
}, 400);
|
|
23
|
+
}
|
|
6
24
|
/**
|
|
7
25
|
* Roles router. Combines `/roles` (list, create) and `/roles/:name`
|
|
8
26
|
* (update, delete) so the wire URLs are
|
|
@@ -63,6 +81,9 @@ export const rolesRouter = new Hono()
|
|
|
63
81
|
}, 403);
|
|
64
82
|
}
|
|
65
83
|
const body = c.req.valid('json');
|
|
84
|
+
const badCaps = rejectUnknownCapabilities(c, body.capabilities);
|
|
85
|
+
if (badCaps)
|
|
86
|
+
return badCaps;
|
|
66
87
|
if (BUILTIN_ROLE_NAMES.includes(body.name)) {
|
|
67
88
|
return c.json({
|
|
68
89
|
success: false,
|
|
@@ -132,6 +153,22 @@ export const rolesRouter = new Hono()
|
|
|
132
153
|
}, 400);
|
|
133
154
|
}
|
|
134
155
|
const body = c.req.valid('json');
|
|
156
|
+
// `owner` is defined as "every capability" and is reconciled back to the
|
|
157
|
+
// full set on every boot, so accepting an edit here would appear to work
|
|
158
|
+
// and then silently revert on the next restart. Same reasoning as the
|
|
159
|
+
// hardcoded block on deleting built-in roles below.
|
|
160
|
+
if (name === 'owner' && body.capabilities !== undefined) {
|
|
161
|
+
return c.json({
|
|
162
|
+
success: false,
|
|
163
|
+
error: 'Forbidden',
|
|
164
|
+
message: '"owner" always holds every capability and its permissions cannot be changed. Create a custom role to grant narrower access.'
|
|
165
|
+
}, 403);
|
|
166
|
+
}
|
|
167
|
+
if (body.capabilities) {
|
|
168
|
+
const badCaps = rejectUnknownCapabilities(c, body.capabilities);
|
|
169
|
+
if (badCaps)
|
|
170
|
+
return badCaps;
|
|
171
|
+
}
|
|
135
172
|
const updated = await databaseAdapter.updateRole(auth.organizationId, name, body);
|
|
136
173
|
if (!updated) {
|
|
137
174
|
return c.json({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/api/routes/user.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAQ5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE,IAAI,CAAC,QAAQ,
|
|
1
|
+
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/api/routes/user.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAQ5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE,IAAI,CAAC,QAAQ,CA2JpC,CAAC"}
|
|
@@ -16,7 +16,7 @@ export const userRouter = new Hono()
|
|
|
16
16
|
return c.json({
|
|
17
17
|
success: false,
|
|
18
18
|
error: 'Invalid request body',
|
|
19
|
-
message: 'name is required',
|
|
19
|
+
message: 'name or image is required',
|
|
20
20
|
issues: result.error.issues
|
|
21
21
|
}, 400);
|
|
22
22
|
}
|
|
@@ -37,8 +37,19 @@ export const userRouter = new Hono()
|
|
|
37
37
|
error: 'Auth provider not configured'
|
|
38
38
|
}, 500);
|
|
39
39
|
}
|
|
40
|
-
const { name } = c.req.valid('json');
|
|
41
|
-
|
|
40
|
+
const { name, image } = c.req.valid('json');
|
|
41
|
+
if (name !== undefined) {
|
|
42
|
+
await provider.changeUserName(auth.user.id, name);
|
|
43
|
+
}
|
|
44
|
+
if (image !== undefined) {
|
|
45
|
+
if (!provider.changeUserImage) {
|
|
46
|
+
return c.json({
|
|
47
|
+
success: false,
|
|
48
|
+
error: 'Auth provider does not support profile image updates'
|
|
49
|
+
}, 500);
|
|
50
|
+
}
|
|
51
|
+
await provider.changeUserImage(auth.user.id, image);
|
|
52
|
+
}
|
|
42
53
|
return c.json({ success: true, message: 'User updated successfully' });
|
|
43
54
|
}
|
|
44
55
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset-service.d.ts","sourceRoot":"","sources":["../../src/lib/services/asset-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAI5C,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE;QACV,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACnB,CAAC;CACF;AAED,MAAM,WAAW,YAAY;IAC5B,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,qBAAa,YAAY;IAEvB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,QAAQ;gBADR,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,eAAe;IAGlC;;OAEG;IACG,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC;IAsFhF;;OAEG;IACG,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAI9E;;;;;;;;;;OAUG;IACG,eAAe,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBhF;;;OAGG;IACG,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAmB5D;;OAEG;IACG,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAItF;;;;;OAKG;IACG,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA2BvE;;OAEG;IACG,mBAAmB,CACxB,cAAc,EAAE,MAAM,EACtB,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE;QACT,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;KACnB,GACC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAIxB;;OAEG;IACG,aAAa,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC;QACpD,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;KAClB,CAAC;IAeF;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC;QAChC,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,OAAO,CAAC;KAClB,CAAC;CAWF"}
|
|
1
|
+
{"version":3,"file":"asset-service.d.ts","sourceRoot":"","sources":["../../src/lib/services/asset-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAI5C,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE;QACV,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACnB,CAAC;CACF;AAED,MAAM,WAAW,YAAY;IAC5B,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,qBAAa,YAAY;IAEvB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,QAAQ;gBADR,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,eAAe;IAGlC;;OAEG;IACG,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC;IAsFhF;;OAEG;IACG,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAI9E;;;;;;;;;;OAUG;IACG,eAAe,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBhF;;;OAGG;IACG,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAmB5D;;OAEG;IACG,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAItF;;;;;OAKG;IACG,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA2BvE;;OAEG;IACG,mBAAmB,CACxB,cAAc,EAAE,MAAM,EACtB,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE;QACT,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;KACnB,GACC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAIxB;;OAEG;IACG,aAAa,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC;QACpD,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;KAClB,CAAC;IAeF;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC;QAChC,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,OAAO,CAAC;KAClB,CAAC;CAWF"}
|
package/dist/services/index.d.ts
CHANGED
|
@@ -3,4 +3,6 @@ export type { AssetUploadData, AssetFilters } from './asset-service.js';
|
|
|
3
3
|
export { HierarchyService } from './hierarchy-service.js';
|
|
4
4
|
export { RolesService } from './roles-service.js';
|
|
5
5
|
export { VersionService } from './version-service.js';
|
|
6
|
+
export { PluginSettingsService } from './plugin-settings-service.js';
|
|
7
|
+
export type { ResolvedSettings } from './plugin-settings-service.js';
|
|
6
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/services/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/services/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC"}
|
package/dist/services/index.js
CHANGED
|
@@ -5,3 +5,4 @@ export { AssetService } from './asset-service.js';
|
|
|
5
5
|
export { HierarchyService } from './hierarchy-service.js';
|
|
6
6
|
export { RolesService } from './roles-service.js';
|
|
7
7
|
export { VersionService } from './version-service.js';
|
|
8
|
+
export { PluginSettingsService } from './plugin-settings-service.js';
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { DatabaseAdapter } from '../db/index.js';
|
|
2
|
+
import type { PartResolver } from '../plugins/resolver.js';
|
|
3
|
+
import type { SettingsPart } from '../plugins/types.js';
|
|
4
|
+
/** Placeholder shown to the client for a secret that has a stored value. */
|
|
5
|
+
export declare const SECRET_MASK = "\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
6
|
+
/**
|
|
7
|
+
* The persistence this service needs — the settings slice of `DatabaseAdapter`, which
|
|
8
|
+
* satisfies it structurally. Declaring the narrow dependency rather than the whole
|
|
9
|
+
* adapter keeps the service honest about its reach (config values only, never content)
|
|
10
|
+
* and lets it be constructed against a stub.
|
|
11
|
+
*/
|
|
12
|
+
export type PluginSettingsStore = Pick<DatabaseAdapter, 'getPluginSettings' | 'setPluginSettings'>;
|
|
13
|
+
/** Likewise the resolver slice: this service only ever looks up settings declarations. */
|
|
14
|
+
export type SettingsDeclarationSource = Pick<PartResolver, 'settingsDeclaration'>;
|
|
15
|
+
/** A declared settings section plus whether the plugin registered one at all. */
|
|
16
|
+
export interface ResolvedSettings {
|
|
17
|
+
/** The plugin's settings declaration, or `null` if it declared none. */
|
|
18
|
+
declaration: SettingsPart | null;
|
|
19
|
+
/** Declared defaults merged with the org's stored values (secrets masked). */
|
|
20
|
+
values: Record<string, unknown>;
|
|
21
|
+
/** Whether an encryption key is configured — the UI flags secret fields read-only if not. */
|
|
22
|
+
secretsEnabled: boolean;
|
|
23
|
+
}
|
|
24
|
+
/** Raised when a submitted value doesn't match its declared field type. */
|
|
25
|
+
export declare class PluginSettingsValidationError extends Error {
|
|
26
|
+
readonly issues: string[];
|
|
27
|
+
constructor(issues: string[]);
|
|
28
|
+
}
|
|
29
|
+
export declare class PluginSettingsService {
|
|
30
|
+
/** Narrowed to the two methods this service uses — the full adapter satisfies it. */
|
|
31
|
+
private db;
|
|
32
|
+
/** Likewise: settings resolution is the only part of the resolver needed here. */
|
|
33
|
+
private resolver;
|
|
34
|
+
/** Key for encrypting `secret` fields; when absent, secrets are disabled (fail safe). */
|
|
35
|
+
private encryptionKey;
|
|
36
|
+
constructor(
|
|
37
|
+
/** Narrowed to the two methods this service uses — the full adapter satisfies it. */
|
|
38
|
+
db: PluginSettingsStore,
|
|
39
|
+
/** Likewise: settings resolution is the only part of the resolver needed here. */
|
|
40
|
+
resolver: SettingsDeclarationSource,
|
|
41
|
+
/** Key for encrypting `secret` fields; when absent, secrets are disabled (fail safe). */
|
|
42
|
+
encryptionKey?: string | null);
|
|
43
|
+
/** Whether secret fields can be stored/read (an encryption key is configured). */
|
|
44
|
+
get secretsEnabled(): boolean;
|
|
45
|
+
private secretFieldNames;
|
|
46
|
+
/**
|
|
47
|
+
* Effective values for injection into plugin **server** code: declared defaults
|
|
48
|
+
* overlaid with stored values, with secrets **decrypted** to plaintext. Secrets
|
|
49
|
+
* that can't be decrypted (no key, or a bad envelope) are omitted, never returned
|
|
50
|
+
* as ciphertext. This is the sensitive read — never send its result to a client.
|
|
51
|
+
*/
|
|
52
|
+
get(organizationId: string, pluginId: string): Promise<Record<string, unknown>>;
|
|
53
|
+
/**
|
|
54
|
+
* Effective values for the **client/API**: same merge, but secrets are **masked** —
|
|
55
|
+
* a stored secret becomes {@link SECRET_MASK}, an unset one an empty string. Plaintext
|
|
56
|
+
* secrets never cross this boundary.
|
|
57
|
+
*/
|
|
58
|
+
getMasked(organizationId: string, pluginId: string): Promise<Record<string, unknown>>;
|
|
59
|
+
/**
|
|
60
|
+
* Resolve for the admin surface: the declaration plus masked values plus whether
|
|
61
|
+
* secrets are enabled. `declaration: null` when the plugin declares no settings.
|
|
62
|
+
*/
|
|
63
|
+
resolve(organizationId: string, pluginId: string): Promise<ResolvedSettings>;
|
|
64
|
+
/**
|
|
65
|
+
* Persist a partial edit. Only declared field names are accepted, and each value is
|
|
66
|
+
* type-checked against its declaration — an invalid patch is rejected whole, never
|
|
67
|
+
* applied in part, so a failed save can't leave settings half-written. Secret fields
|
|
68
|
+
* are encrypted; a blank or still-masked secret submission means "leave unchanged"
|
|
69
|
+
* (so the client never has to echo the real value back). Returns the new **masked**
|
|
70
|
+
* values — a save response never leaks a plaintext secret.
|
|
71
|
+
*
|
|
72
|
+
* @throws {PluginSettingsValidationError} when a value doesn't match its field type.
|
|
73
|
+
*/
|
|
74
|
+
save(organizationId: string, pluginId: string, patch: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=plugin-settings-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-settings-service.d.ts","sourceRoot":"","sources":["../../src/lib/services/plugin-settings-service.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAMrD,4EAA4E;AAC5E,eAAO,MAAM,WAAW,yCAAW,CAAC;AAEpC;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,eAAe,EAAE,mBAAmB,GAAG,mBAAmB,CAAC,CAAC;AAEnG,0FAA0F;AAC1F,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAAC,YAAY,EAAE,qBAAqB,CAAC,CAAC;AAElF,iFAAiF;AACjF,MAAM,WAAW,gBAAgB;IAChC,wEAAwE;IACxE,WAAW,EAAE,YAAY,GAAG,IAAI,CAAC;IACjC,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,6FAA6F;IAC7F,cAAc,EAAE,OAAO,CAAC;CACxB;AAwBD,2EAA2E;AAC3E,qBAAa,6BAA8B,SAAQ,KAAK;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE;gBAAhB,MAAM,EAAE,MAAM,EAAE;CAIrC;AAkDD,qBAAa,qBAAqB;IAEhC,qFAAqF;IACrF,OAAO,CAAC,EAAE;IACV,kFAAkF;IAClF,OAAO,CAAC,QAAQ;IAChB,yFAAyF;IACzF,OAAO,CAAC,aAAa;;IALrB,qFAAqF;IAC7E,EAAE,EAAE,mBAAmB;IAC/B,kFAAkF;IAC1E,QAAQ,EAAE,yBAAyB;IAC3C,yFAAyF;IACjF,aAAa,GAAE,MAAM,GAAG,IAAW;IAG5C,kFAAkF;IAClF,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED,OAAO,CAAC,gBAAgB;IAIxB;;;;;OAKG;IACG,GAAG,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IA6BrF;;;;OAIG;IACG,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAc3F;;;OAGG;IACG,OAAO,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAMlF;;;;;;;;;OASG;IACG,IAAI,CACT,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CA2CnC"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// services/plugin-settings-service.ts
|
|
2
|
+
//
|
|
3
|
+
// Resolves and persists a plugin's per-organization settings — the "config plane"
|
|
4
|
+
// (see references/plugin-settings-and-secrets-scope.md). A plugin declares its
|
|
5
|
+
// settings shape via an `aphex/settings` part; this service merges the declared
|
|
6
|
+
// defaults with the stored per-(org, plugin) values and writes edits back, scoped
|
|
7
|
+
// to the org.
|
|
8
|
+
//
|
|
9
|
+
// Secrets (`type: 'secret'` fields) are handled specially:
|
|
10
|
+
// - stored AES-256-GCM-encrypted (never plaintext),
|
|
11
|
+
// - decrypted only for server injection (`get`), never for the client (`getMasked`),
|
|
12
|
+
// - write-only: a blank/masked submission means "leave unchanged".
|
|
13
|
+
// When no encryption key is configured, secret writes are refused and secret reads
|
|
14
|
+
// return nothing — fail safe, never store or reveal a plaintext secret.
|
|
15
|
+
import { encryptSecret, decryptSecret, isEncryptedSecret } from '../security/secret-crypto.js';
|
|
16
|
+
import { settingsListItems } from '../schema-utils/settings.js';
|
|
17
|
+
import { cmsLogger } from '../utils/logger.js';
|
|
18
|
+
/** Placeholder shown to the client for a secret that has a stored value. */
|
|
19
|
+
export const SECRET_MASK = '••••••';
|
|
20
|
+
const isSecret = (f) => f.type === 'secret';
|
|
21
|
+
/**
|
|
22
|
+
* Static default for a field — its `initialValue` when that's a plain value (not a
|
|
23
|
+
* function/thunk). Secrets never carry a default.
|
|
24
|
+
*/
|
|
25
|
+
function fieldDefault(field) {
|
|
26
|
+
if (isSecret(field))
|
|
27
|
+
return undefined;
|
|
28
|
+
const initial = field.initialValue;
|
|
29
|
+
return typeof initial === 'function' ? undefined : initial;
|
|
30
|
+
}
|
|
31
|
+
/** Build the defaults object from a declaration's fields (skips undefined). */
|
|
32
|
+
function defaultsFor(fields) {
|
|
33
|
+
const out = {};
|
|
34
|
+
for (const field of fields) {
|
|
35
|
+
const value = fieldDefault(field);
|
|
36
|
+
if (value !== undefined)
|
|
37
|
+
out[field.name] = value;
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
/** Raised when a submitted value doesn't match its declared field type. */
|
|
42
|
+
export class PluginSettingsValidationError extends Error {
|
|
43
|
+
issues;
|
|
44
|
+
constructor(issues) {
|
|
45
|
+
super(`Invalid plugin settings: ${issues.join('; ')}`);
|
|
46
|
+
this.issues = issues;
|
|
47
|
+
this.name = 'PluginSettingsValidationError';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Check one submitted value against its declared field type, returning an error
|
|
52
|
+
* message or `null`.
|
|
53
|
+
*
|
|
54
|
+
* The declaration is the contract, so the host enforces it here rather than leaving
|
|
55
|
+
* every plugin to re-guard values it already described. Without this a `string` field
|
|
56
|
+
* would happily store an object, and the plugin would find out at request time, deep
|
|
57
|
+
* in its own code, with no useful trace back to the save that caused it.
|
|
58
|
+
*
|
|
59
|
+
* `null` is always allowed — it's how the panel represents "cleared".
|
|
60
|
+
*/
|
|
61
|
+
function checkValue(field, value) {
|
|
62
|
+
if (value === null)
|
|
63
|
+
return null;
|
|
64
|
+
switch (field.type) {
|
|
65
|
+
case 'string': {
|
|
66
|
+
if (typeof value !== 'string')
|
|
67
|
+
return `"${field.name}" must be a string`;
|
|
68
|
+
// A select-style string field may only hold one of its declared options.
|
|
69
|
+
const items = settingsListItems(field);
|
|
70
|
+
if (items.length > 0 && !items.some((item) => item.value === value)) {
|
|
71
|
+
return `"${field.name}" must be one of: ${items.map((i) => i.value).join(', ')}`;
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
case 'text':
|
|
76
|
+
if (typeof value !== 'string')
|
|
77
|
+
return `"${field.name}" must be a string`;
|
|
78
|
+
return null;
|
|
79
|
+
case 'number':
|
|
80
|
+
// Reject NaN/Infinity too — they survive JSON as null and corrupt round-trips.
|
|
81
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
82
|
+
return `"${field.name}" must be a finite number`;
|
|
83
|
+
}
|
|
84
|
+
if (field.min !== undefined && value < field.min) {
|
|
85
|
+
return `"${field.name}" must be >= ${field.min}`;
|
|
86
|
+
}
|
|
87
|
+
if (field.max !== undefined && value > field.max) {
|
|
88
|
+
return `"${field.name}" must be <= ${field.max}`;
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
case 'boolean':
|
|
92
|
+
if (typeof value !== 'boolean')
|
|
93
|
+
return `"${field.name}" must be a boolean`;
|
|
94
|
+
return null;
|
|
95
|
+
case 'secret':
|
|
96
|
+
// Secrets are write-only strings; blank/mask is handled before this point.
|
|
97
|
+
if (typeof value !== 'string')
|
|
98
|
+
return `"${field.name}" must be a string`;
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export class PluginSettingsService {
|
|
103
|
+
db;
|
|
104
|
+
resolver;
|
|
105
|
+
encryptionKey;
|
|
106
|
+
constructor(
|
|
107
|
+
/** Narrowed to the two methods this service uses — the full adapter satisfies it. */
|
|
108
|
+
db,
|
|
109
|
+
/** Likewise: settings resolution is the only part of the resolver needed here. */
|
|
110
|
+
resolver,
|
|
111
|
+
/** Key for encrypting `secret` fields; when absent, secrets are disabled (fail safe). */
|
|
112
|
+
encryptionKey = null) {
|
|
113
|
+
this.db = db;
|
|
114
|
+
this.resolver = resolver;
|
|
115
|
+
this.encryptionKey = encryptionKey;
|
|
116
|
+
}
|
|
117
|
+
/** Whether secret fields can be stored/read (an encryption key is configured). */
|
|
118
|
+
get secretsEnabled() {
|
|
119
|
+
return typeof this.encryptionKey === 'string' && this.encryptionKey.length > 0;
|
|
120
|
+
}
|
|
121
|
+
secretFieldNames(declaration) {
|
|
122
|
+
return new Set(declaration.fields.filter(isSecret).map((f) => f.name));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Effective values for injection into plugin **server** code: declared defaults
|
|
126
|
+
* overlaid with stored values, with secrets **decrypted** to plaintext. Secrets
|
|
127
|
+
* that can't be decrypted (no key, or a bad envelope) are omitted, never returned
|
|
128
|
+
* as ciphertext. This is the sensitive read — never send its result to a client.
|
|
129
|
+
*/
|
|
130
|
+
async get(organizationId, pluginId) {
|
|
131
|
+
const declaration = this.resolver.settingsDeclaration(pluginId);
|
|
132
|
+
const stored = (await this.db.getPluginSettings(organizationId, pluginId)) ?? {};
|
|
133
|
+
const defaults = declaration ? defaultsFor(declaration.fields) : {};
|
|
134
|
+
const merged = { ...defaults, ...stored };
|
|
135
|
+
if (!declaration)
|
|
136
|
+
return merged;
|
|
137
|
+
const secrets = this.secretFieldNames(declaration);
|
|
138
|
+
for (const name of secrets) {
|
|
139
|
+
const value = merged[name];
|
|
140
|
+
if (!isEncryptedSecret(value)) {
|
|
141
|
+
// Unset or non-envelope — nothing usable to inject.
|
|
142
|
+
delete merged[name];
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (!this.secretsEnabled) {
|
|
146
|
+
delete merged[name];
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
merged[name] = decryptSecret(value, this.encryptionKey);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
cmsLogger.error(`Failed to decrypt secret "${pluginId}.${name}":`, error);
|
|
154
|
+
delete merged[name];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return merged;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Effective values for the **client/API**: same merge, but secrets are **masked** —
|
|
161
|
+
* a stored secret becomes {@link SECRET_MASK}, an unset one an empty string. Plaintext
|
|
162
|
+
* secrets never cross this boundary.
|
|
163
|
+
*/
|
|
164
|
+
async getMasked(organizationId, pluginId) {
|
|
165
|
+
const declaration = this.resolver.settingsDeclaration(pluginId);
|
|
166
|
+
const stored = (await this.db.getPluginSettings(organizationId, pluginId)) ?? {};
|
|
167
|
+
const defaults = declaration ? defaultsFor(declaration.fields) : {};
|
|
168
|
+
const merged = { ...defaults, ...stored };
|
|
169
|
+
if (declaration) {
|
|
170
|
+
for (const name of this.secretFieldNames(declaration)) {
|
|
171
|
+
merged[name] = isEncryptedSecret(merged[name]) ? SECRET_MASK : '';
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return merged;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Resolve for the admin surface: the declaration plus masked values plus whether
|
|
178
|
+
* secrets are enabled. `declaration: null` when the plugin declares no settings.
|
|
179
|
+
*/
|
|
180
|
+
async resolve(organizationId, pluginId) {
|
|
181
|
+
const declaration = this.resolver.settingsDeclaration(pluginId) ?? null;
|
|
182
|
+
const values = await this.getMasked(organizationId, pluginId);
|
|
183
|
+
return { declaration, values, secretsEnabled: this.secretsEnabled };
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Persist a partial edit. Only declared field names are accepted, and each value is
|
|
187
|
+
* type-checked against its declaration — an invalid patch is rejected whole, never
|
|
188
|
+
* applied in part, so a failed save can't leave settings half-written. Secret fields
|
|
189
|
+
* are encrypted; a blank or still-masked secret submission means "leave unchanged"
|
|
190
|
+
* (so the client never has to echo the real value back). Returns the new **masked**
|
|
191
|
+
* values — a save response never leaks a plaintext secret.
|
|
192
|
+
*
|
|
193
|
+
* @throws {PluginSettingsValidationError} when a value doesn't match its field type.
|
|
194
|
+
*/
|
|
195
|
+
async save(organizationId, pluginId, patch) {
|
|
196
|
+
const declaration = this.resolver.settingsDeclaration(pluginId);
|
|
197
|
+
if (!declaration) {
|
|
198
|
+
throw new Error(`Plugin "${pluginId}" has not declared any settings.`);
|
|
199
|
+
}
|
|
200
|
+
const fieldsByName = new Map(declaration.fields.map((f) => [f.name, f]));
|
|
201
|
+
const secrets = this.secretFieldNames(declaration);
|
|
202
|
+
const stored = (await this.db.getPluginSettings(organizationId, pluginId)) ?? {};
|
|
203
|
+
const next = { ...stored };
|
|
204
|
+
// Validate the whole patch before writing any of it.
|
|
205
|
+
const issues = [];
|
|
206
|
+
const pending = [];
|
|
207
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
208
|
+
const field = fieldsByName.get(key);
|
|
209
|
+
if (!field)
|
|
210
|
+
continue; // undeclared → dropped
|
|
211
|
+
if (secrets.has(key)) {
|
|
212
|
+
// Write-only: blank or the untouched mask means "keep the current value".
|
|
213
|
+
if (value === '' || value === SECRET_MASK || value == null)
|
|
214
|
+
continue;
|
|
215
|
+
if (!this.secretsEnabled) {
|
|
216
|
+
throw new Error(`Cannot store secret "${pluginId}.${key}": no secretEncryptionKey configured.`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const issue = checkValue(field, value);
|
|
220
|
+
if (issue)
|
|
221
|
+
issues.push(issue);
|
|
222
|
+
else
|
|
223
|
+
pending.push([key, value]);
|
|
224
|
+
}
|
|
225
|
+
if (issues.length > 0)
|
|
226
|
+
throw new PluginSettingsValidationError(issues);
|
|
227
|
+
for (const [key, value] of pending) {
|
|
228
|
+
next[key] = secrets.has(key)
|
|
229
|
+
? encryptSecret(String(value), this.encryptionKey)
|
|
230
|
+
: value;
|
|
231
|
+
}
|
|
232
|
+
await this.db.setPluginSettings(organizationId, pluginId, next);
|
|
233
|
+
return this.getMasked(organizationId, pluginId);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CacheAdapter } from '../cache/index.js';
|
|
2
2
|
import type { DatabaseAdapter } from '../db/index.js';
|
|
3
|
-
import { type
|
|
3
|
+
import { type Role } from '../types/capabilities.js';
|
|
4
4
|
/**
|
|
5
5
|
* RolesService — caches per-org role capability lookups.
|
|
6
6
|
*
|
|
@@ -23,7 +23,7 @@ export declare class RolesService {
|
|
|
23
23
|
* 3. Built-in seed if the name matches a built-in.
|
|
24
24
|
* 4. Empty list — unknown role → no capabilities.
|
|
25
25
|
*/
|
|
26
|
-
getCapabilities(organizationId: string, roleName: string): Promise<
|
|
26
|
+
getCapabilities(organizationId: string, roleName: string): Promise<string[]>;
|
|
27
27
|
/** List every role defined for an organization. */
|
|
28
28
|
listRoles(organizationId: string): Promise<Role[]>;
|
|
29
29
|
/** Idempotent — safe to call on every request if you want. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"roles-service.d.ts","sourceRoot":"","sources":["../../src/lib/services/roles-service.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,
|
|
1
|
+
{"version":3,"file":"roles-service.d.ts","sourceRoot":"","sources":["../../src/lib/services/roles-service.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAyC,KAAK,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAIzF;;;;;GAKG;AACH,qBAAa,YAAY;IAKvB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG;IANZ,OAAO,CAAC,MAAM,CAAC,WAAW,CAAM;IAChC,OAAO,CAAC,QAAQ,CAAwC;gBAG/C,EAAE,EAAE,eAAe,EACnB,KAAK,GAAE,YAAY,GAAG,IAAW,EACjC,GAAG,GAAE,MAAiC;IAG/C;;;;;;;;OAQG;IACG,eAAe,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAqBlF,mDAAmD;IAC7C,SAAS,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAIxD,8DAA8D;IACxD,cAAc,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D,uEAAuE;IACjE,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAW5D,aAAa;CA2B3B"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emit theme tokens as a scoped `<style>` body.
|
|
3
|
+
*
|
|
4
|
+
* Two hard rules, both learned the expensive way:
|
|
5
|
+
* 1. NEVER emit site tokens at `:root` — they'd bleed into the admin chrome.
|
|
6
|
+
* Always scope to the site shell selector.
|
|
7
|
+
* 2. Theme values are user-authored, so sanitize before interpolating. Any value
|
|
8
|
+
* that could break out of the `--var: value;` declaration (contains `;`, `{`,
|
|
9
|
+
* `}`, `<`, `>`, or a CSS `url(`/`expression(`) is dropped, and the token's
|
|
10
|
+
* CSS fallback in the layout applies instead.
|
|
11
|
+
*/
|
|
12
|
+
/** Return the value if safe to interpolate into a CSS declaration, else null. */
|
|
13
|
+
export declare function sanitizeTokenValue(value: string): string | null;
|
|
14
|
+
/**
|
|
15
|
+
* Build a sanitized `--var: value; …` declaration list from a `cssVar → value`
|
|
16
|
+
* map (see deriveThemeVars). Suitable for an inline `style` attribute on the site
|
|
17
|
+
* shell element — the simplest way to scope tokens to the site subtree while
|
|
18
|
+
* beating component-CSS specificity, with no `<style>` injection to guard. Returns
|
|
19
|
+
* `''` when nothing survives sanitization.
|
|
20
|
+
*/
|
|
21
|
+
export declare function emitThemeVars(vars: Record<string, string>): string;
|
|
22
|
+
/**
|
|
23
|
+
* Build a scoped style *rule* from a `cssVar → value` map, for callers that
|
|
24
|
+
* prefer injecting a `<style>` block over an inline attribute. Returns `''` when
|
|
25
|
+
* nothing survives sanitization, so callers can skip rendering an empty `<style>`.
|
|
26
|
+
*
|
|
27
|
+
* @param selector CSS selector to scope the custom properties to, e.g. `.blog-shell`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function emitThemeStyle(selector: string, vars: Record<string, string>): string;
|
|
30
|
+
//# sourceMappingURL=css.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../../src/lib/theme/css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK/D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAWlE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAIrF"}
|