mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-17 16:15:22 +00:00
Fix mermaid / contentkit webframe not updating properly (#2427)
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@gitbook/react-contentkit': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix rendering of webframe with SSR causing wrong communication between frame and renderer
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'gitbook': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add optional env `GITBOOK_INTEGRATIONS_HOST` to configure the host serving the integrations
|
||||||
@@ -3,6 +3,7 @@ import { Icon } from '@gitbook/icons';
|
|||||||
import { ContentKit, ContentKitOutput, ContentKitServerContext } from '@gitbook/react-contentkit';
|
import { ContentKit, ContentKitOutput, ContentKitServerContext } from '@gitbook/react-contentkit';
|
||||||
|
|
||||||
import { ignoreAPIError, renderIntegrationUi } from '@/lib/api';
|
import { ignoreAPIError, renderIntegrationUi } from '@/lib/api';
|
||||||
|
import { INTEGRATIONS_HOST } from '@/lib/csp';
|
||||||
import { parseMarkdown } from '@/lib/markdown';
|
import { parseMarkdown } from '@/lib/markdown';
|
||||||
import { tcls } from '@/lib/tailwind';
|
import { tcls } from '@/lib/tailwind';
|
||||||
|
|
||||||
@@ -69,7 +70,7 @@ export async function IntegrationBlock(props: BlockProps<DocumentBlockIntegratio
|
|||||||
return (
|
return (
|
||||||
<div className={tcls(style)}>
|
<div className={tcls(style)}>
|
||||||
<ContentKit
|
<ContentKit
|
||||||
security={{ firstPartyDomains: ['integrations.gitbook.com'] }}
|
security={{ firstPartyDomains: [INTEGRATIONS_HOST] }}
|
||||||
initialInput={initialInput}
|
initialInput={initialInput}
|
||||||
initialOutput={initialOutput}
|
initialOutput={initialOutput}
|
||||||
render={async (request) => {
|
render={async (request) => {
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ export function createContentSecurityPolicyNonce(): string {
|
|||||||
return nonce;
|
return nonce;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hostname serving the integrations.
|
||||||
|
*/
|
||||||
|
export const INTEGRATIONS_HOST =
|
||||||
|
process.env.GITBOOK_INTEGRATIONS_HOST ?? 'integrations.gitbook.com';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a Content Security Policy header for a space.
|
* Generate a Content Security Policy header for a space.
|
||||||
*/
|
*/
|
||||||
@@ -39,10 +45,10 @@ export function getContentSecurityPolicy(scripts: SpaceIntegrationScript[], nonc
|
|||||||
// Since I can't get the nonce to work for inline styles, we need to allow unsafe-inline
|
// Since I can't get the nonce to work for inline styles, we need to allow unsafe-inline
|
||||||
const defaultCSP = `
|
const defaultCSP = `
|
||||||
default-src 'self' ${assetsDomain};
|
default-src 'self' ${assetsDomain};
|
||||||
script-src 'self' 'nonce-${nonce}' 'strict-dynamic' 'unsafe-inline' 'unsafe-eval' ${assetsDomain} https://integrations.gitbook.com https://cdn.iframe.ly;
|
script-src 'self' 'nonce-${nonce}' 'strict-dynamic' 'unsafe-inline' 'unsafe-eval' ${assetsDomain} https://${INTEGRATIONS_HOST} https://cdn.iframe.ly;
|
||||||
style-src 'self' ${assetsDomain} fonts.googleapis.com 'unsafe-inline';
|
style-src 'self' ${assetsDomain} fonts.googleapis.com 'unsafe-inline';
|
||||||
img-src * 'self' blob: data: files.gitbook.com ${assetsDomain} ${iconsAssetsSrc};
|
img-src * 'self' blob: data: files.gitbook.com ${assetsDomain} ${iconsAssetsSrc};
|
||||||
connect-src * 'self' integrations.gitbook.com app.gitbook.com api.gitbook.com srv.buysellads.com ${assetsDomain} ${iconsAssetsSrc};
|
connect-src * 'self' ${INTEGRATIONS_HOST} app.gitbook.com api.gitbook.com srv.buysellads.com ${assetsDomain} ${iconsAssetsSrc};
|
||||||
font-src 'self' fonts.gstatic.com ${assetsDomain};
|
font-src 'self' fonts.gstatic.com ${assetsDomain};
|
||||||
frame-src *;
|
frame-src *;
|
||||||
object-src 'none';
|
object-src 'none';
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { resolveDynamicBinding } from './dynamic';
|
|||||||
export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWebFrame>) {
|
export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWebFrame>) {
|
||||||
const { element } = props;
|
const { element } = props;
|
||||||
|
|
||||||
|
const [mounted, setMounted] = React.useState(false);
|
||||||
const renderer = useContentKitClientContext();
|
const renderer = useContentKitClientContext();
|
||||||
const iframeRef = React.useRef<HTMLIFrameElement>(null);
|
const iframeRef = React.useRef<HTMLIFrameElement>(null);
|
||||||
const [size, setSize] = React.useState<{
|
const [size, setSize] = React.useState<{
|
||||||
@@ -23,10 +24,6 @@ export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWe
|
|||||||
|
|
||||||
const sendMessage = React.useCallback(
|
const sendMessage = React.useCallback(
|
||||||
(message: object) => {
|
(message: object) => {
|
||||||
if (!iframeRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const target = new URL(element.source.url);
|
const target = new URL(element.source.url);
|
||||||
|
|
||||||
// For security reasons, only iframe from our integrations domains are allowed
|
// For security reasons, only iframe from our integrations domains are allowed
|
||||||
@@ -36,6 +33,10 @@ export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWe
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (readyRef.current) {
|
if (readyRef.current) {
|
||||||
|
if (!iframeRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
iframeRef.current.contentWindow!.postMessage(
|
iframeRef.current.contentWindow!.postMessage(
|
||||||
message,
|
message,
|
||||||
`${target.protocol}//${target.host}`,
|
`${target.protocol}//${target.host}`,
|
||||||
@@ -61,7 +62,7 @@ export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWe
|
|||||||
|
|
||||||
// For security reasons, only iframe from our integrations domains are allowed
|
// For security reasons, only iframe from our integrations domains are allowed
|
||||||
// to send and receive messages
|
// to send and receive messages
|
||||||
if (!renderer.security.firstPartyDomains.includes(origin.host) && 0) {
|
if (!renderer.security.firstPartyDomains.includes(origin.host)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,6 +118,11 @@ export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWe
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('message', callback);
|
window.addEventListener('message', callback);
|
||||||
|
|
||||||
|
// We only render the iframe once we have added the event listener
|
||||||
|
// otherwise during SSR, we'll miss messages
|
||||||
|
setMounted(true);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('message', callback);
|
window.removeEventListener('message', callback);
|
||||||
};
|
};
|
||||||
@@ -142,26 +148,29 @@ export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWe
|
|||||||
<div
|
<div
|
||||||
className={`contentkit-webframe`}
|
className={`contentkit-webframe`}
|
||||||
style={{
|
style={{
|
||||||
aspectRatio: element.aspectRatio,
|
aspectRatio: size.aspectRatio || element.aspectRatio || undefined,
|
||||||
...size,
|
maxWidth: size.maxWidth || undefined,
|
||||||
|
maxHeight: size.maxHeight || undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<iframe
|
{mounted ? (
|
||||||
ref={iframeRef}
|
<iframe
|
||||||
src={element.source.url}
|
ref={iframeRef}
|
||||||
allowFullScreen
|
src={element.source.url}
|
||||||
allow="clipboard-write"
|
allowFullScreen
|
||||||
style={{
|
allow="clipboard-write"
|
||||||
position: 'absolute',
|
style={{
|
||||||
top: 0,
|
position: 'absolute',
|
||||||
left: 0,
|
top: 0,
|
||||||
bottom: 0,
|
left: 0,
|
||||||
right: 0,
|
bottom: 0,
|
||||||
width: '100%',
|
right: 0,
|
||||||
height: '100%',
|
width: '100%',
|
||||||
border: 'none',
|
height: '100%',
|
||||||
}}
|
border: 'none',
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user