Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: config is always visible on landing page #496

Merged
merged 5 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: lts/*
- uses: ipfs/aegir/actions/cache-node-modules@master
- uses: ipfs/aegir/actions/cache-node-modules@main
- name: Save ./dist output for later
uses: actions/upload-artifact@v4
with:
Expand All @@ -33,7 +33,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: lts/*
- uses: ipfs/aegir/actions/cache-node-modules@master
- uses: ipfs/aegir/actions/cache-node-modules@main
- run: npm run --if-present lint
- run: npm run --if-present dep-check
- run: npm run --if-present doc-check
Expand All @@ -51,7 +51,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- uses: ipfs/aegir/actions/cache-node-modules@master
- uses: ipfs/aegir/actions/cache-node-modules@main
- run: npm run --if-present test:node
- uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
with:
Expand All @@ -66,7 +66,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: lts/*
- uses: ipfs/aegir/actions/cache-node-modules@master
- uses: ipfs/aegir/actions/cache-node-modules@main
- run: npx playwright install --with-deps
- run: npm run --if-present test:chrome
- uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
Expand All @@ -82,7 +82,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: lts/*
- uses: ipfs/aegir/actions/cache-node-modules@master
- uses: ipfs/aegir/actions/cache-node-modules@main
- run: npm run --if-present test:chrome-webworker
- uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
with:
Expand All @@ -97,7 +97,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: lts/*
- uses: ipfs/aegir/actions/cache-node-modules@master
- uses: ipfs/aegir/actions/cache-node-modules@main
- run: npx playwright install --with-deps
- run: npm run --if-present test:firefox
- uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
Expand All @@ -113,7 +113,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: lts/*
- uses: ipfs/aegir/actions/cache-node-modules@master
- uses: ipfs/aegir/actions/cache-node-modules@main
- run: npm run --if-present test:firefox-webworker
- uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
with:
Expand All @@ -128,7 +128,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: lts/*
- uses: ipfs/aegir/actions/cache-node-modules@master
- uses: ipfs/aegir/actions/cache-node-modules@main
- run: npm run --if-present test:webkit
- uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
with:
Expand All @@ -143,7 +143,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: lts/*
- uses: ipfs/aegir/actions/cache-node-modules@master
- uses: ipfs/aegir/actions/cache-node-modules@main
- run: npx xvfb-maybe npm run --if-present test:electron-main
- uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
with:
Expand All @@ -158,7 +158,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: lts/*
- uses: ipfs/aegir/actions/cache-node-modules@master
- uses: ipfs/aegir/actions/cache-node-modules@main
- run: npx xvfb-maybe npm run --if-present test:electron-renderer
- uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
with:
Expand Down
2 changes: 1 addition & 1 deletion src/components/About.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react'

export default function About (): JSX.Element {
return (
<aside className='mw7 lb-snow center w-100 lh-copy pa2'>
<aside className='mw7 lb-snow center w-100 lh-copy pa2 e2e-section-about'>
<h1 className='pa0 f3 ma0 mb4 teal tc'>About the IPFS Gateway and Service Worker</h1>
<p>This page runs an IPFS gateway within a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API" target="_blank">Service Worker</a>. It uses <a href="https://github.com/ipfs/helia" target="_blank">Helia</a> (IPFS implementation in JS) and the <a href="https://github.com/ipfs/helia-verified-fetch" target="_blank">@helia/verified-fetch</a> library (<a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" target="_blank">Fetch API</a> for IPFS) to facilitate direct verified retrieval of <a href="https://docs.ipfs.tech/concepts/content-addressing/" target="_blank">content-addressed</a> data.</p>
<p><strong>Why?</strong> It improves decentralization, offers enhanced security (CID verification happens on end user's machine) and reliability (ability to do retrieval from multiple sources without reliance on a single HTTP server).</p>
Expand Down
21 changes: 7 additions & 14 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from 'react'
import { RouteContext } from '../context/router-context.jsx'
import gearIcon from '../gear-icon.svg'
import React, { type FunctionComponent } from 'react'
import ipfsLogo from '../ipfs-logo.svg'

export default function Header ({ showConfigIcon = false }: { showConfigIcon?: boolean }): JSX.Element {
const { gotoPage } = React.useContext(RouteContext)
export interface HeaderProps extends React.HTMLProps<HTMLElement> {

}

export const Header: FunctionComponent<HeaderProps> = () => {
return (
<header className='e2e-header flex items-center pa2 bg-navy bb bw3 b--aqua tc justify-between'>
<div>
Expand All @@ -14,16 +15,8 @@ export default function Header ({ showConfigIcon = false }: { showConfigIcon?: b
</div>
<div className='pb1 ma0 inline-flex items-center'>
<h1 className='e2e-header-title f3 fw2 aqua ttu sans-serif'>Service Worker Gateway <small className="gray">(beta)</small></h1>
{showConfigIcon && <button className='e2e-header-config-button pl3'
onClick={() => {
gotoPage('/ipfs-sw-config')
}}
style={{ border: 'none', background: 'none', cursor: 'pointer' }}
>
<img alt='Config gear icon' src={gearIcon} style={{ height: 50 }} className='v-top' />
</button>}
</div>

</header>
)
}
export default Header
18 changes: 1 addition & 17 deletions src/context/config-context.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import React, { createContext, useCallback, useEffect, useState } from 'react'
import { defaultDebug, defaultDnsJsonResolvers, defaultEnableGatewayProviders, defaultEnableRecursiveGateways, defaultEnableWebTransport, defaultEnableWss, defaultGateways, defaultRouters, getConfig, resetConfig, type ConfigDb } from '../lib/config-db.js'
import { isConfigPage } from '../lib/is-config-page.js'
import { getUiComponentLogger } from '../lib/logger.js'
import type { ComponentLogger } from '@libp2p/logger'

const isLoadedInIframe = window.self !== window.top

type ConfigKey = keyof ConfigDb
export interface ConfigContextType extends ConfigDb {
isConfigExpanded: boolean
setConfigExpanded(value: boolean): void
setConfig(key: ConfigKey, value: any): void
resetConfig(logger?: ComponentLogger): Promise<void>
}

export const ConfigContext = createContext<ConfigContextType>({
isConfigExpanded: isLoadedInIframe,
setConfigExpanded: (value: boolean) => {},
setConfig: (key, value) => {},
resetConfig: async () => Promise.resolve(),
Expand All @@ -30,7 +24,6 @@ export const ConfigContext = createContext<ConfigContextType>({
})

export const ConfigProvider = ({ children }: { children: JSX.Element[] | JSX.Element, expanded?: boolean }): JSX.Element => {
const [isConfigExpanded, setConfigExpanded] = useState(isConfigPage(window.location.hash))
const [gateways, setGateways] = useState<string[]>(defaultGateways)
const [routers, setRouters] = useState<string[]>(defaultRouters)
const [dnsJsonResolvers, setDnsJsonResolvers] = useState<Record<string, string>>(defaultDnsJsonResolvers)
Expand All @@ -39,7 +32,6 @@ export const ConfigProvider = ({ children }: { children: JSX.Element[] | JSX.Ele
const [enableGatewayProviders, setEnableGatewayProviders] = useState(defaultEnableGatewayProviders)
const [enableRecursiveGateways, setEnableRecursiveGateways] = useState(defaultEnableRecursiveGateways)
const [debug, setDebug] = useState(defaultDebug)
const isExplicitlyLoadedConfigPage = isConfigPage(window.location.hash)
const logger = getUiComponentLogger('config-context')
const log = logger.forComponent('main')

Expand Down Expand Up @@ -103,16 +95,8 @@ export const ConfigProvider = ({ children }: { children: JSX.Element[] | JSX.Ele
await loadConfig()
}

const setConfigExpandedWrapped = (value: boolean): void => {
if (isLoadedInIframe || isExplicitlyLoadedConfigPage) {
// ignore it
} else {
setConfigExpanded(value)
}
}

return (
<ConfigContext.Provider value={{ isConfigExpanded, setConfigExpanded: setConfigExpandedWrapped, setConfig: setConfigLocal, resetConfig: resetConfigLocal, gateways, routers, dnsJsonResolvers, enableWss, enableWebTransport, enableGatewayProviders, enableRecursiveGateways, debug }}>
<ConfigContext.Provider value={{ setConfig: setConfigLocal, resetConfig: resetConfigLocal, gateways, routers, dnsJsonResolvers, enableWss, enableWebTransport, enableGatewayProviders, enableRecursiveGateways, debug }}>
{children}
</ConfigContext.Provider>
)
Expand Down
1 change: 0 additions & 1 deletion src/gear-icon.svg

This file was deleted.

6 changes: 6 additions & 0 deletions src/lib/is-config-page.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/**
* As of https://github.com/ipfs/service-worker-gateway/issues/486, we no longer have a singular page at
* /#/ipfs-sw-config unless loaded directly from a subdomain. The configuration section is now on the main page (helper-ui.tsx)
*
* We still use /#/ipfs-sw-config to allow subdomain users to change config and we need to detect that.
*/
export function isConfigPage (hash: string): boolean {
const isConfigHashPath = hash.startsWith('#/ipfs-sw-config') // needed for _redirects and IPFS hosted sw gateways
return isConfigHashPath
Expand Down
3 changes: 2 additions & 1 deletion src/lib/routing-render-checks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ export async function shouldRenderRedirectPage (): Promise<boolean> {

export async function shouldRenderConfigPage (): Promise<boolean> {
const { isConfigPage } = await import('../lib/is-config-page.js')
const { isSubdomainGatewayRequest } = await import('./path-or-subdomain.js')

const isRequestToViewConfigPage = isConfigPage(window.location.hash)
return isRequestToViewConfigPage
return isRequestToViewConfigPage && isSubdomainGatewayRequest(window.location)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only render this individual config page on subdomain requests explicitly for /#/ipfs-sw-config

}

export function shouldRenderRedirectsInterstitial (): boolean {
Expand Down
19 changes: 11 additions & 8 deletions src/pages/config.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useCallback, useContext, useEffect, useState } from 'react'
import React, { useCallback, useContext, useEffect, useState, type FunctionComponent } from 'react'
import Header from '../components/Header.jsx'
import { Collapsible } from '../components/collapsible.jsx'
import { InputSection } from '../components/input-section.jsx'
import { InputToggle } from '../components/input-toggle.jsx'
import { ServiceWorkerReadyButton } from '../components/sw-ready-button.jsx'
Expand All @@ -10,6 +9,7 @@ import { RouteContext } from '../context/router-context.jsx'
import { ServiceWorkerProvider } from '../context/service-worker-context.jsx'
import { setConfig as storeConfig } from '../lib/config-db.js'
import { convertDnsResolverInputToObject, convertDnsResolverObjectToInput, convertUrlArrayToInput, convertUrlInputToArray } from '../lib/input-helpers.js'
import { isConfigPage } from '../lib/is-config-page.js'
import { getUiComponentLogger, uiLogger } from '../lib/logger.js'
import './default-page-styles.css'
import { tellSwToReloadConfig } from '../lib/sw-comms.js'
Expand Down Expand Up @@ -75,7 +75,11 @@ const dnsJsonValidationFn = (value: string): Error | null => {
}
}

function ConfigPage (): React.JSX.Element | null {
export interface ConfigPageProps extends React.HTMLProps<HTMLElement> {

}

const ConfigPage: FunctionComponent<ConfigPageProps> = () => {
const { gotoPage } = useContext(RouteContext)
SgtPooki marked this conversation as resolved.
Show resolved Hide resolved
const { setConfig, resetConfig, gateways, routers, dnsJsonResolvers, debug, enableGatewayProviders, enableRecursiveGateways, enableWss, enableWebTransport } = useContext(ConfigContext)
const [isSaving, setIsSaving] = useState(false)
Expand Down Expand Up @@ -140,9 +144,9 @@ function ConfigPage (): React.JSX.Element | null {

return (
<>
{!isLoadedInIframe && <Header /> }
<main className='e2e-config-page pa4-l bg-snow mw7 center pa4'>
<Collapsible collapsedLabel="View config" expandedLabel='Hide config' collapsed={isLoadedInIframe}>
{isConfigPage(window.location.hash) ? <Header /> : null}
<section className='e2e-config-page pa4-l bg-snow mw7 center pa4'>
<h1 className='pa0 f3 ma0 mb4 teal tc'>Configure your IPFS Gateway</h1>
<InputSection label='Direct Retrieval'>
<InputToggle
className="e2e-config-page-input"
Expand Down Expand Up @@ -224,8 +228,7 @@ function ConfigPage (): React.JSX.Element | null {
<ServiceWorkerReadyButton className="e2e-config-page-button white w-100 pa3" id="save-config" label={isSaving ? 'Saving...' : 'Save Config'} waitingLabel='Waiting for service worker registration...' onClick={() => { void saveConfig() }} />
</div>
{error != null && <span style={{ color: 'red' }}>{error.message}</span>}
</Collapsible>
</main>
</section>
</>
)
}
Expand Down
6 changes: 4 additions & 2 deletions src/pages/helper-ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ConfigProvider } from '../context/config-context.jsx'
import { ServiceWorkerProvider } from '../context/service-worker-context.jsx'
import { LOCAL_STORAGE_KEYS } from '../lib/local-storage.js'
import './default-page-styles.css'
import Config from './config.js'

function HelperUi (): React.JSX.Element {
const [requestPath, setRequestPath] = useState(localStorage.getItem(LOCAL_STORAGE_KEYS.forms.requestPath) ?? '')
Expand All @@ -21,8 +22,8 @@ function HelperUi (): React.JSX.Element {

return (
<>
<Header showConfigIcon={true} />
<main className='pa2 pa4-l bg-snow mw7 mv5-l center'>
<Header />
<main className='pa2 pa4-l bg-snow mw7 mv5-l center e2e-helper-ui'>
<h1 className='pa0 f3 ma0 mb4 teal tc'>Fetch & Verify IPFS Content in Browser</h1>
<Form
handleSubmit={handleSubmit}
Expand All @@ -37,6 +38,7 @@ function HelperUi (): React.JSX.Element {
</main>

<About />
<Config/>
</>
)
}
Expand Down
2 changes: 1 addition & 1 deletion test-e2e/config-loading.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { getConfig, setConfig } from './fixtures/set-sw-config.js'
import { waitForServiceWorker } from './fixtures/wait-for-service-worker.js'
import type { ConfigDb } from '../src/lib/config-db'

test.describe('/#/ipfs-sw-config', () => {
test.describe('ipfs-sw configuration', () => {
const testConfig: ConfigDb = {
gateways: [process.env.KUBO_GATEWAY as string, 'http://example.com'],
routers: [process.env.KUBO_GATEWAY as string, 'http://example.com/routing/v1'],
Expand Down
4 changes: 3 additions & 1 deletion test-e2e/fixtures/locators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export interface GetFrameLocator {
*/
export const getHeader: GetLocator = (page) => page.locator('.e2e-header')
export const getHeaderTitle: GetLocator = (page) => page.locator('.e2e-header-title')
export const getConfigButton: GetLocator = (page) => page.locator('.e2e-header-config-button')
export const getConfigPage: GetLocator = (page) => page.locator('.e2e-config-page')
export const getConfigPageInput: GetLocator = (page) => page.locator('.e2e-config-page-input')
export const getConfigPageSaveButton: GetLocator = (page) => page.locator('.e2e-config-page-button#save-config')
Expand All @@ -23,6 +22,9 @@ export const getConfigAutoReloadInput: GetLocator = (page) => page.locator('.e2e

export const getNoServiceWorkerError: GetLocator = (page) => page.locator('.e2e-no-service-worker-error')

export const getHelperUi: GetLocator = (page) => page.locator('.e2e-helper-ui')
export const getAboutSection: GetLocator = (page) => page.locator('.e2e-about-section')

/**
* Iframe page parts
*/
Expand Down
4 changes: 1 addition & 3 deletions test-e2e/fixtures/set-sw-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*
* Note that this was only tested and confirmed working for subdomain pages.
*/
import { getConfigAutoReloadInputIframe, getConfigButton, getConfigButtonIframe, getConfigGatewaysInput, getConfigGatewaysInputIframe, getConfigPage, getConfigPageSaveButton, getConfigPageSaveButtonIframe, getConfigRoutersInput, getConfigRoutersInputIframe } from './locators.js'
import { getConfigAutoReloadInputIframe, getConfigButtonIframe, getConfigGatewaysInput, getConfigGatewaysInputIframe, getConfigPage, getConfigPageSaveButton, getConfigPageSaveButtonIframe, getConfigRoutersInput, getConfigRoutersInputIframe } from './locators.js'
import { waitForServiceWorker } from './wait-for-service-worker.js'
import type { ConfigDb } from '../../src/lib/config-db.js'
import type { Page } from '@playwright/test'
Expand All @@ -31,8 +31,6 @@ export async function setConfigViaUiSubdomain ({ page, config }: { page: Page, c
export async function setConfigViaUi ({ page, config }: { page: Page, config: Partial<ConfigDb> }): Promise<void> {
await waitForServiceWorker(page)

await getConfigButton(page).isVisible()
await getConfigButton(page).click()
await getConfigPage(page).isVisible()

await getConfigGatewaysInput(page).locator('input').fill(JSON.stringify([process.env.KUBO_GATEWAY]))
Expand Down
Loading
Loading