diff --git a/packages/core/content-manager/server/src/preview/index.ts b/packages/core/content-manager/server/src/preview/index.ts index 8d4a8dcef0..612e8e68e6 100644 --- a/packages/core/content-manager/server/src/preview/index.ts +++ b/packages/core/content-manager/server/src/preview/index.ts @@ -21,13 +21,12 @@ const getFeature = (): Partial => { // } return { - bootstrap() { - // eslint-disable-next-line no-console -- TODO remove when we have real functionality - console.log('Bootstrapping preview server'); - + register() { const config = getService(strapi, 'preview-config'); config.validate(); + config.register(); }, + bootstrap() {}, routes, controllers, services, diff --git a/packages/core/content-manager/server/src/preview/services/preview-config.ts b/packages/core/content-manager/server/src/preview/services/preview-config.ts index 1fc9007cd5..82a6d5e26a 100644 --- a/packages/core/content-manager/server/src/preview/services/preview-config.ts +++ b/packages/core/content-manager/server/src/preview/services/preview-config.ts @@ -1,3 +1,5 @@ +import { mergeWith } from 'lodash/fp'; + import type { Core, UID } from '@strapi/types'; import { errors } from '@strapi/utils'; @@ -10,15 +12,75 @@ export type HandlerParams = { export interface PreviewConfig { enabled: boolean; config: { + // List of CSP allowed origins. This is a shortcut to setting it up inside `config/middlewares.js` + allowedOrigins: string[]; handler: (uid: UID.Schema, params: HandlerParams) => string | undefined; }; } +/** + * Utility to extend Strapi configuration middlewares. Mainly used to extend the CSP directives from the security middleware. + */ +const extendMiddlewareConfiguration = (middleware = { name: '', config: {} }) => { + const middlewares = strapi.config.get('middlewares') as (string | object)[]; + + const configuredMiddlewares = middlewares.map((currentMiddleware) => { + if (currentMiddleware === middleware.name) { + // Use the new config object if the middleware has no config property yet + return middleware; + } + + // @ts-expect-error - currentMiddleware is not a string + if (currentMiddleware.name === middleware.name) { + // Deep merge (+ concat arrays) the new config with the current middleware config + return mergeWith( + (objValue, srcValue) => { + if (Array.isArray(objValue)) { + return objValue.concat(srcValue); + } + + return undefined; + }, + currentMiddleware, + middleware + ); + } + + return currentMiddleware; + }); + + strapi.config.set('middlewares', configuredMiddlewares); +}; + /** * Read configuration for static preview */ const createPreviewConfigService = ({ strapi }: { strapi: Core.Strapi }) => { return { + register() { + if (!this.isEnabled()) { + return; + } + + const config = strapi.config.get('admin.preview') as PreviewConfig; + + /** + * Register the allowed origins for CSP, so the preview URL can be displayed + */ + if (config.config?.allowedOrigins) { + extendMiddlewareConfiguration({ + name: 'strapi::security', + config: { + contentSecurityPolicy: { + directives: { + 'frame-src': config.config.allowedOrigins, + }, + }, + }, + }); + } + }, + isEnabled() { const config = strapi.config.get('admin.preview') as PreviewConfig; diff --git a/packages/core/content-manager/server/src/register.ts b/packages/core/content-manager/server/src/register.ts index e219f71cb2..02511f4fc1 100644 --- a/packages/core/content-manager/server/src/register.ts +++ b/packages/core/content-manager/server/src/register.ts @@ -1,8 +1,10 @@ import type { Plugin } from '@strapi/types'; import history from './history'; +import preview from './preview'; const register: Plugin.LoadedPlugin['register'] = async ({ strapi }) => { await history.register?.({ strapi }); + await preview.register?.({ strapi }); }; export default register;