mirror of
https://github.com/enso-org/enso.git
synced 2024-11-23 08:08:34 +03:00
Add COOP+COEP+CORP headers (#6597)
This commit is contained in:
parent
0d9186b23c
commit
9597dd361b
@ -8,6 +8,7 @@ import * as mime from 'mime-types'
|
||||
import * as portfinder from 'portfinder'
|
||||
import createServer from 'create-servers'
|
||||
|
||||
import * as common from 'enso-common'
|
||||
import * as contentConfig from 'enso-content-config'
|
||||
|
||||
import * as paths from '../paths'
|
||||
@ -105,6 +106,9 @@ export class Server {
|
||||
resource === '/preload.cjs.map'
|
||||
? `${paths.APP_PATH}${resource}`
|
||||
: `${this.config.dir}${resource}`
|
||||
for (const [header, value] of common.COOP_COEP_CORP_HEADERS) {
|
||||
response.setHeader(header, value)
|
||||
}
|
||||
fs.readFile(resourceFile, (err, data) => {
|
||||
if (err) {
|
||||
logger.error(`Resource '${resource}' not found.`)
|
||||
|
@ -1,4 +1,5 @@
|
||||
/** @file This module contains metadata about the product and distribution.
|
||||
/** @file This module contains metadata about the product and distribution,
|
||||
* and various other constants that are needed in multiple sibling packages.
|
||||
*
|
||||
* Code in this package is used by two or more sibling packages of this package. The code is defined
|
||||
* here when it is not possible for a sibling package to own that code without introducing a
|
||||
@ -12,3 +13,13 @@ export const DEEP_LINK_SCHEME = 'enso'
|
||||
|
||||
/** Name of the product. */
|
||||
export const PRODUCT_NAME = 'Enso'
|
||||
|
||||
/** COOP, COEP, and CORP headers: https://web.dev/coop-coep/
|
||||
*
|
||||
* These are required to increase the resolution of `performance.now()` timers,
|
||||
* making profiling a lot more accurate and consistent. */
|
||||
export const COOP_COEP_CORP_HEADERS: [header: string, value: string][] = [
|
||||
['Cross-Origin-Embedder-Policy', 'require-corp'],
|
||||
['Cross-Origin-Opener-Policy', 'same-origin'],
|
||||
['Cross-Origin-Resource-Policy', 'same-origin'],
|
||||
]
|
||||
|
@ -26,7 +26,11 @@ import esbuildPluginYaml from 'esbuild-plugin-yaml'
|
||||
import * as utils from '../../utils'
|
||||
import BUILD_INFO from '../../build.json' assert { type: 'json' }
|
||||
|
||||
export const THIS_PATH = pathModule.resolve(pathModule.dirname(url.fileURLToPath(import.meta.url)))
|
||||
// =================
|
||||
// === Constants ===
|
||||
// =================
|
||||
|
||||
const THIS_PATH = pathModule.resolve(pathModule.dirname(url.fileURLToPath(import.meta.url)))
|
||||
|
||||
// =============================
|
||||
// === Environment variables ===
|
||||
|
@ -20,6 +20,9 @@ const logger = app.log.logger
|
||||
const ESBUILD_PATH = '/esbuild'
|
||||
/** SSE event indicating a build has finished. */
|
||||
const ESBUILD_EVENT_NAME = 'change'
|
||||
/** Path to the service worker that resolves all extensionless paths to `/index.html`.
|
||||
* This service worker is required for client-side routing to work when doing local development. */
|
||||
const SERVICE_WORKER_PATH = '/serviceWorker.js'
|
||||
/** One second in milliseconds. */
|
||||
const SECOND = 1000
|
||||
/** Time in seconds after which a `fetchTimeout` ends. */
|
||||
@ -33,6 +36,7 @@ if (IS_DEV_MODE) {
|
||||
new EventSource(ESBUILD_PATH).addEventListener(ESBUILD_EVENT_NAME, () => {
|
||||
location.reload()
|
||||
})
|
||||
void navigator.serviceWorker.register(SERVICE_WORKER_PATH)
|
||||
}
|
||||
|
||||
// =============
|
||||
|
32
app/ide-desktop/lib/content/src/serviceWorker.ts
Normal file
32
app/ide-desktop/lib/content/src/serviceWorker.ts
Normal file
@ -0,0 +1,32 @@
|
||||
/** @file A service worker that redirects paths without extensions to `/index.html`.
|
||||
* This is required for paths like `/login`, which are handled by client-side routing,
|
||||
* to work when developing locally on `localhost:8080`. */
|
||||
// Bring globals and interfaces specific to Web Workers into scope.
|
||||
/// <reference lib="WebWorker" />
|
||||
import * as common from 'enso-common'
|
||||
|
||||
// =====================
|
||||
// === Fetch handler ===
|
||||
// =====================
|
||||
|
||||
// We `declare` a variable here because Service Workers have a different global scope.
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
declare const self: ServiceWorkerGlobalScope
|
||||
|
||||
self.addEventListener('fetch', event => {
|
||||
const url = new URL(event.request.url)
|
||||
if (url.hostname === 'localhost' && url.pathname !== '/esbuild') {
|
||||
event.respondWith(
|
||||
fetch(event.request.url).then(response => {
|
||||
const clonedResponse = new Response(response.body, response)
|
||||
for (const [header, value] of common.COOP_COEP_CORP_HEADERS) {
|
||||
clonedResponse.headers.set(header, value)
|
||||
}
|
||||
return clonedResponse
|
||||
})
|
||||
)
|
||||
return
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
@ -1,4 +1,7 @@
|
||||
/** @file File watch and compile service. */
|
||||
import * as path from 'node:path'
|
||||
import * as url from 'node:url'
|
||||
|
||||
import * as esbuild from 'esbuild'
|
||||
import * as portfinder from 'portfinder'
|
||||
import chalk from 'chalk'
|
||||
@ -12,6 +15,7 @@ import * as dashboardBundler from '../dashboard/esbuild-config'
|
||||
|
||||
const PORT = 8080
|
||||
const HTTP_STATUS_OK = 200
|
||||
const THIS_PATH = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)))
|
||||
|
||||
// ===============
|
||||
// === Watcher ===
|
||||
@ -29,6 +33,10 @@ async function watch() {
|
||||
...bundler.argumentsFromEnv(),
|
||||
devMode: true,
|
||||
})
|
||||
opts.entryPoints.push({
|
||||
in: path.resolve(THIS_PATH, 'src', 'serviceWorker.ts'),
|
||||
out: 'serviceWorker',
|
||||
})
|
||||
const builder = await esbuild.context(opts)
|
||||
await builder.watch()
|
||||
await builder.serve({
|
||||
|
@ -254,6 +254,16 @@ export const COMPUTER_ICON = (
|
||||
</svg>
|
||||
)
|
||||
|
||||
/** An icon representing a user without a profile picture. */
|
||||
export const DEFAULT_USER_ICON = (
|
||||
<svg height={32} width={32} viewBox="2 2 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M6 20a10 10 0 0 1 6 -18 10 10 0 0 1 6 18 6 6 0 0 0 -4 -5 4.3 4.3 0 0 0 -2 -8 4.3 4.3 0 0 0 -2 8 6 6 0 0 0 -4 5"
|
||||
fill="#888888"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export interface StopIconProps {
|
||||
className?: string
|
||||
}
|
||||
|
@ -471,10 +471,7 @@ function Dashboard(props: DashboardProps) {
|
||||
key={user.user.organization_id}
|
||||
permissions={PERMISSION[user.permission]}
|
||||
>
|
||||
<img
|
||||
className="rounded-full h-6"
|
||||
src="https://faces-img.xcdn.link/image-lorem-face-4742.jpg"
|
||||
/>
|
||||
{svg.DEFAULT_USER_ICON}
|
||||
</PermissionDisplay>
|
||||
))}
|
||||
</>
|
||||
|
@ -120,14 +120,15 @@ function TopBar(props: TopBarProps) {
|
||||
</a>
|
||||
{/* User profile and menu. */}
|
||||
<div className="transform">
|
||||
<img
|
||||
src="https://faces-img.xcdn.link/image-lorem-face-4742.jpg"
|
||||
className="rounded-full w-8 h-8 bg-cover cursor-pointer"
|
||||
<div
|
||||
onClick={event => {
|
||||
event.stopPropagation()
|
||||
setUserMenuVisible(!userMenuVisible)
|
||||
}}
|
||||
/>
|
||||
className="rounded-full w-8 h-8 bg-cover cursor-pointer"
|
||||
>
|
||||
{svg.DEFAULT_USER_ICON}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@ -1,6 +1,9 @@
|
||||
/** @file A service worker that redirects paths without extensions to `/index.html`.
|
||||
* This is only used in the cloud frontend. */
|
||||
* This is required for paths like `/login`, which are handled by client-side routing,
|
||||
* to work when developing locally on `localhost:8081`. */
|
||||
// Bring globals and interfaces specific to Web Workers into scope.
|
||||
/// <reference lib="WebWorker" />
|
||||
import * as common from 'enso-common'
|
||||
|
||||
// =====================
|
||||
// === Fetch handler ===
|
||||
@ -12,17 +15,21 @@ declare const self: ServiceWorkerGlobalScope
|
||||
|
||||
self.addEventListener('fetch', event => {
|
||||
const url = new URL(event.request.url)
|
||||
if (
|
||||
url.hostname === 'localhost' &&
|
||||
/\/[^.]+$/.test(event.request.url) &&
|
||||
url.pathname !== '/esbuild'
|
||||
) {
|
||||
event.respondWith(fetch('/index.html'))
|
||||
if (url.hostname === 'localhost' && url.pathname !== '/esbuild') {
|
||||
const responsePromise = /\/[^.]+$/.test(event.request.url)
|
||||
? fetch('/index.html')
|
||||
: fetch(event.request.url)
|
||||
event.respondWith(
|
||||
responsePromise.then(response => {
|
||||
const clonedResponse = new Response(response.body, response)
|
||||
for (const [header, value] of common.COOP_COEP_CORP_HEADERS) {
|
||||
clonedResponse.headers.set(header, value)
|
||||
}
|
||||
return clonedResponse
|
||||
})
|
||||
)
|
||||
return
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
// Required for TypeScript to consider it a module, instead of in window scope.
|
||||
export {}
|
||||
|
Loading…
Reference in New Issue
Block a user