138 lines
4.2 KiB
TypeScript
138 lines
4.2 KiB
TypeScript
/**
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
import React, { lazy, Suspense } from 'react';
|
|
import ReactDOM from 'react-dom';
|
|
import { BrowserRouter as Router, Route } from 'react-router-dom';
|
|
import { Switchboard } from '@superset-ui/switchboard';
|
|
import { bootstrapData } from 'src/preamble';
|
|
import setupClient from 'src/setup/setupClient';
|
|
import { RootContextProviders } from 'src/views/RootContextProviders';
|
|
import ErrorBoundary from 'src/components/ErrorBoundary';
|
|
import Loading from 'src/components/Loading';
|
|
|
|
const debugMode = process.env.WEBPACK_MODE === 'development';
|
|
|
|
function log(...info: unknown[]) {
|
|
if (debugMode) {
|
|
console.debug(`[superset]`, ...info);
|
|
}
|
|
}
|
|
|
|
const LazyDashboardPage = lazy(
|
|
() =>
|
|
import(
|
|
/* webpackChunkName: "DashboardPage" */ 'src/dashboard/containers/DashboardPage'
|
|
),
|
|
);
|
|
|
|
const EmbeddedApp = () => (
|
|
<Router>
|
|
<Route path="/dashboard/:idOrSlug/embedded">
|
|
<Suspense fallback={<Loading />}>
|
|
<RootContextProviders>
|
|
<ErrorBoundary>
|
|
<LazyDashboardPage />
|
|
</ErrorBoundary>
|
|
</RootContextProviders>
|
|
</Suspense>
|
|
</Route>
|
|
</Router>
|
|
);
|
|
|
|
const appMountPoint = document.getElementById('app')!;
|
|
|
|
const MESSAGE_TYPE = '__embedded_comms__';
|
|
|
|
if (!window.parent) {
|
|
appMountPoint.innerHTML =
|
|
'This page is intended to be embedded in an iframe, but no window.parent was found.';
|
|
}
|
|
|
|
// if the page is embedded in an origin that hasn't
|
|
// been authorized by the curator, we forbid access entirely.
|
|
// todo: check the referrer on the route serving this page instead
|
|
// const ALLOW_ORIGINS = ['http://127.0.0.1:9001', 'http://localhost:9001'];
|
|
// const parentOrigin = new URL(document.referrer).origin;
|
|
// if (!ALLOW_ORIGINS.includes(parentOrigin)) {
|
|
// throw new Error(
|
|
// `[superset] iframe parent ${parentOrigin} is not in the list of allowed origins`,
|
|
// );
|
|
// }
|
|
|
|
async function start(guestToken: string) {
|
|
// the preamble configures a client, but we need to configure a new one
|
|
// now that we have the guest token
|
|
setupClient({
|
|
guestToken,
|
|
guestTokenHeaderName: bootstrapData.config?.GUEST_TOKEN_HEADER_NAME,
|
|
});
|
|
ReactDOM.render(<EmbeddedApp />, appMountPoint);
|
|
}
|
|
|
|
function validateMessageEvent(event: MessageEvent) {
|
|
if (
|
|
event.data?.type === 'webpackClose' ||
|
|
event.data?.source === '@devtools-page'
|
|
) {
|
|
// sometimes devtools use the messaging api and we want to ignore those
|
|
throw new Error("Sir, this is a Wendy's");
|
|
}
|
|
|
|
// if (!ALLOW_ORIGINS.includes(event.origin)) {
|
|
// throw new Error('Message origin is not in the allowed list');
|
|
// }
|
|
|
|
if (typeof event.data !== 'object' || event.data.type !== MESSAGE_TYPE) {
|
|
throw new Error(`Message type does not match type used for embedded comms`);
|
|
}
|
|
}
|
|
|
|
window.addEventListener('message', function (event) {
|
|
try {
|
|
validateMessageEvent(event);
|
|
} catch (err) {
|
|
log('ignoring message', err, event);
|
|
return;
|
|
}
|
|
|
|
const port = event.ports?.[0];
|
|
if (event.data.handshake === 'port transfer' && port) {
|
|
log('message port received', event);
|
|
|
|
const switchboard = new Switchboard({
|
|
port,
|
|
name: 'superset',
|
|
debug: debugMode,
|
|
});
|
|
|
|
switchboard.defineMethod('guestToken', ({ guestToken }) => {
|
|
start(guestToken);
|
|
});
|
|
|
|
switchboard.defineMethod('getScrollSize', () => ({
|
|
width: document.body.scrollWidth,
|
|
height: document.body.scrollHeight,
|
|
}));
|
|
|
|
switchboard.start();
|
|
}
|
|
});
|
|
|
|
log('embed page is ready to receive messages');
|