Skip to content

Commit 16ec8c5

Browse files
authored
fix: internal error page styling (#893)
Adds proper styling to the internal error page to bring it closer to the boxo gateway. Refs #875
1 parent c84fcf9 commit 16ec8c5

File tree

15 files changed

+347
-56
lines changed

15 files changed

+347
-56
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,12 @@ The main goals of this project are:
8282
You can build and run the project locally:
8383

8484
```console
85-
> npm ci
85+
> npm i
86+
> npm run build
8687
> npm start
8788
```
8889

89-
Now open your browser and go to `http://localhost:3333`
90+
Now open your browser and go to `http://localhost:3000`
9091

9192
Below is an explanation of the different URLs and what they do:
9293

@@ -137,4 +138,3 @@ This project is dual-licensed under
137138
`SPDX-License-Identifier: Apache-2.0 OR MIT`
138139

139140
See [LICENSE](./LICENSE) for more details.
140-

build.js

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,56 @@ const renameSwPlugin = {
180180
}
181181
}
182182

183+
/**
184+
* Replaces strings with paths to built files, e.g. `'<%-- src/app.tsx --%>'`
185+
* becomes `'./path/to/app-HASH.js'`
186+
*
187+
* @type {esbuild.Plugin}
188+
*/
189+
const replaceImports = {
190+
name: 'modify-built-files',
191+
setup (build) {
192+
build.onEnd(async (result) => {
193+
const metafile = result.metafile
194+
195+
// Replace '<%-- src --%>' with 'path/to/built/file'
196+
const buildInfo = {}
197+
const jsFiles = []
198+
199+
for (const [outputFile, meta] of Object.entries(metafile.outputs)) {
200+
if (outputFile.endsWith('.css')) {
201+
buildInfo[Object.keys(meta.inputs)[0]] = outputFile
202+
} else if (outputFile.endsWith('.svg')) {
203+
buildInfo[Object.keys(meta.inputs)[0]] = outputFile
204+
} else if (outputFile.endsWith('.js') && meta.entryPoint != null) {
205+
buildInfo[meta.entryPoint] = outputFile
206+
jsFiles.push(outputFile)
207+
} else if (outputFile.endsWith('.map')) {
208+
// ignore
209+
} else {
210+
console.info('Unknown file type:', outputFile, meta)
211+
}
212+
}
213+
214+
const regex = /<%--\s(.*)\s--%>/g
215+
216+
for (const jsFile of jsFiles) {
217+
let file = await fs.readFile(path.resolve(jsFile), 'utf-8')
218+
219+
for (const [target, source] of file.matchAll(regex)) {
220+
const bundledFile = buildInfo[source].replace('dist', '')
221+
222+
console.info('Replace', target, 'with', bundledFile, 'in', jsFile)
223+
224+
file = file.replaceAll(target, bundledFile)
225+
}
226+
227+
await fs.writeFile(path.resolve(jsFile), file)
228+
}
229+
})
230+
}
231+
}
232+
183233
/**
184234
* Plugin to modify built files by running post-build tasks.
185235
*
@@ -242,23 +292,30 @@ const excludeFilesPlugin = (extensions) => ({
242292
* @type {esbuild.BuildOptions}
243293
*/
244294
export const buildOptions = {
245-
entryPoints: ['src/index.tsx', 'src/sw.ts', 'src/app.tsx', 'src/ipfs-sw-*.ts', 'src/ipfs-sw-*.css'],
295+
entryPoints: [
296+
'src/index.tsx',
297+
'src/sw.ts',
298+
'src/app.tsx',
299+
'src/internal-error.tsx',
300+
'src/ipfs-sw-*.ts',
301+
'src/ipfs-sw-*.css'
302+
],
246303
bundle: true,
247304
outdir: 'dist',
248305
loader: {
249306
'.js': 'jsx',
250307
'.css': 'css',
251308
'.svg': 'file'
252309
},
253-
minify: true,
310+
minify: false,
254311
sourcemap: true,
255312
metafile: true,
256313
splitting: false,
257314
target: ['es2020'],
258315
format: 'esm',
259316
entryNames: 'ipfs-sw-[name]-[hash]',
260317
assetNames: 'ipfs-sw-[name]-[hash]',
261-
plugins: [renameSwPlugin, modifyBuiltFiles, excludeFilesPlugin(['.eot?#iefix', '.otf', '.woff', '.woff2'])]
318+
plugins: [replaceImports, renameSwPlugin, modifyBuiltFiles, excludeFilesPlugin(['.eot?#iefix', '.otf', '.woff', '.woff2'])]
262319
}
263320

264321
const ctx = await esbuild.context(buildOptions)

public/ipfs-sw-504.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ <h1 class="e2e-header-title f3 fw2 aqua ttu sans-serif">Service Worker Gateway <
104104
$anchor.classList.add('dib')
105105
}
106106
}
107-
checkUrl()
107+
}
108+
checkUrl()
108109
</script>
109110
</body>
110111

src/app.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,19 @@ async function renderUi (): Promise<void> {
4141

4242
const LazyConfig = React.lazy(async () => import('./pages/config.jsx'))
4343
const LazyHelperUi = React.lazy(async () => import('./pages/helper-ui.jsx'))
44-
const LazyServiceWorkerErrorPage = React.lazy(async () => import('./pages/errors/no-service-worker.jsx'))
44+
const LazyNoServiceWorkerErrorPage = React.lazy(async () => import('./pages/errors/no-service-worker.jsx'))
4545
const LazySubdomainWarningPage = React.lazy(async () => import('./pages/subdomain-warning.jsx'))
4646

47-
let ErrorPage: null | React.LazyExoticComponent<() => ReactElement> = LazyServiceWorkerErrorPage
47+
let ErrorPage: null | React.LazyExoticComponent<() => ReactElement> = LazyNoServiceWorkerErrorPage
48+
4849
if ('serviceWorker' in navigator) {
4950
ErrorPage = null
5051
}
5152

5253
const routes: Route[] = [
5354
{ default: true, component: ErrorPage ?? LazyHelperUi },
5455
{ shouldRender: async () => renderChecks.shouldRenderConfigPage(), component: LazyConfig },
55-
{ shouldRender: async () => renderChecks.shouldRenderNoServiceWorkerError(), component: LazyServiceWorkerErrorPage },
56+
{ shouldRender: async () => renderChecks.shouldRenderNoServiceWorkerError(), component: LazyNoServiceWorkerErrorPage },
5657
{ shouldRender: renderChecks.shouldRenderSubdomainWarningPage, component: LazySubdomainWarningPage }
5758
]
5859

src/button.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from 'react'
2+
import type { PropsWithChildren, ReactElement } from 'react'
3+
4+
export interface ButtonProps extends PropsWithChildren {
5+
onClick: any
6+
className?: string
7+
}
8+
9+
export function Button ({ onClick, className, children }: ButtonProps): ReactElement {
10+
return (
11+
<>
12+
<button className={`button bn br2 mr2 pa2 pl3 pr3 snow-muted ${className ?? ''}`} onClick={onClick}>{children}</button>
13+
</>
14+
)
15+
}

src/components/content-box.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react'
2+
import type { PropsWithChildren, ReactElement } from 'react'
3+
4+
export interface ContentBoxProps extends PropsWithChildren {
5+
title: string
6+
}
7+
8+
export default function ContentBox ({ title, children }: ContentBoxProps): ReactElement {
9+
return (
10+
<main className='ma3 br2 ba sans-serif ba br2 b--gray-muted'>
11+
<header className='pt2 pb2 pl3 pr3 bg-snow bb b--gray-muted'>
12+
<strong>{title}</strong>
13+
</header>
14+
<section className='pt2 pb2 pl3 pr3 bg-white'>
15+
{children}
16+
</section>
17+
</main>
18+
)
19+
}

src/components/terminal.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import React from 'react'
2+
import type { PropsWithChildren, ReactElement } from 'react'
3+
4+
export default function Terminal ({ children }: PropsWithChildren): ReactElement {
5+
return (
6+
<pre className='terminal br2 ma2 pa3 snow-muted'>
7+
{children}
8+
</pre>
9+
)
10+
}

src/internal-error.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react'
2+
import { hydrateRoot } from 'react-dom/client'
3+
import { InternalErrorPage } from './pages/errors/internal-error.jsx'
4+
import { Page } from './pages/page.js'
5+
6+
hydrateRoot(document, (
7+
<>
8+
<Page>
9+
<InternalErrorPage />
10+
</Page>
11+
</>
12+
))

src/ipfs-sw-first-hit.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
/**
2-
* This script is injected into the ipfs-sw-first-hit.html file. This was added when addressing an issue with redirects not preserving query parameters.
2+
* This script is injected into the ipfs-sw-first-hit.html file. This was added
3+
* when addressing an issue with redirects not preserving query parameters.
34
*
4-
* The solution we're moving forward with is, instead of using 302 redirects with ipfs _redirects file, we are
5-
* using 200 responses with the ipfs-sw-first-hit.html file. That file will include the ipfs-sw-first-hit.js script
6-
* which will be injected into the index.html file, and handle the redirect logic for us.
5+
* The solution we're moving forward with is, instead of using 302 redirects
6+
* with ipfs _redirects file, we are using 200 responses with the
7+
* ipfs-sw-first-hit.html file. That file will include the ipfs-sw-first-hit.js
8+
* script which will be injected into the index.html file, and handle the
9+
* redirect logic for us.
710
*
811
* It handles the logic for the first hit to the service worker and should only
912
* ever run when _redirects file redirects to ipfs-sw-first-hit.html for /ipns
1013
* or /ipfs paths when the service worker is not yet intercepting requests.
1114
*
12-
* Sometimes, redirect solutions do not support redirecting directly to this page, in which case it should be handled
13-
* by index.tsx instead.
15+
* Sometimes, redirect solutions do not support redirecting directly to this
16+
* page, in which case it should be handled by index.tsx instead.
1417
*
1518
* @see https://github.com/ipfs/service-worker-gateway/issues/628
1619
*/

src/lib/path-or-subdomain.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const findOriginIsolationRedirect = async (location: Pick<Location, 'prot
3636
log?.trace('subdomain support is disabled')
3737
}
3838
}
39-
log?.trace('no need to check for subdomain support', isPathGatewayRequest(location), isSubdomainGatewayRequest(location))
39+
log?.trace('no need to check for subdomain support - is path gateway request %s, is subdomain gateway request %s', isPathGatewayRequest(location), isSubdomainGatewayRequest(location))
4040
return null
4141
}
4242

0 commit comments

Comments
 (0)