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: make routers optional and enable local insecure gateways #303

Closed
wants to merge 10 commits into from
24 changes: 24 additions & 0 deletions patches/@helia+block-brokers+3.0.0.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
diff --git a/node_modules/@helia/block-brokers/dist/src/trustless-gateway/broker.js b/node_modules/@helia/block-brokers/dist/src/trustless-gateway/broker.js
index 223f634..34ff22d 100644
--- a/node_modules/@helia/block-brokers/dist/src/trustless-gateway/broker.js
+++ b/node_modules/@helia/block-brokers/dist/src/trustless-gateway/broker.js
@@ -58,10 +58,14 @@ export class TrustlessGatewayBlockBroker {
}
}
createSession(options = {}) {
- return createTrustlessGatewaySession({
- logger: this.logger,
- routing: this.routing
- }, options);
+ return createTrustlessGatewaySession({
+ logger: this.logger,
+ routing: this.routing
+ }, {
+ ...options,
+ allowLocal: this.allowLocal,
+ allowInsecure: this.allowInsecure
+ })
}
}
//# sourceMappingURL=broker.js.map
\ No newline at end of file
33 changes: 24 additions & 9 deletions src/lib/config-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface ConfigDb extends BaseDbConfig {
routers: string[]
dnsJsonResolvers: Record<string, string>
autoReload: boolean
delegatedRouting: boolean
debug: string
}

Expand All @@ -16,6 +17,8 @@ export const defaultRouters = ['https://delegated-ipfs.dev']
export const defaultDnsJsonResolvers = {
'.': 'https://delegated-ipfs.dev/dns-query'
}
export const defaultdelegatedRouting = true
export const defaultAutoReload = false

const configDb = new GenericIDB<ConfigDb>('helia-sw', 'config')

Expand All @@ -26,7 +29,10 @@ export async function loadConfigFromLocalStorage (): Promise<void> {
const localStorageGatewaysString = localStorage.getItem(LOCAL_STORAGE_KEYS.config.gateways) ?? JSON.stringify(defaultGateways)
const localStorageRoutersString = localStorage.getItem(LOCAL_STORAGE_KEYS.config.routers) ?? JSON.stringify(defaultRouters)
const localStorageDnsResolvers = localStorage.getItem(LOCAL_STORAGE_KEYS.config.dnsJsonResolvers) ?? JSON.stringify(defaultDnsJsonResolvers)
const autoReload = localStorage.getItem(LOCAL_STORAGE_KEYS.config.autoReload) === 'true'
const lsDelegatedRouting = localStorage.getItem(LOCAL_STORAGE_KEYS.config.delegatedRouting)
const delegatedRouting = lsDelegatedRouting === null ? defaultAutoReload : lsDelegatedRouting === 'true'
const lsAutoReload = localStorage.getItem(LOCAL_STORAGE_KEYS.config.autoReload)
const autoReload = lsAutoReload === null ? defaultAutoReload : lsAutoReload === 'true'
const debug = localStorage.getItem(LOCAL_STORAGE_KEYS.config.debug) ?? ''
const gateways = JSON.parse(localStorageGatewaysString)
const routers = JSON.parse(localStorageRoutersString)
Expand All @@ -35,6 +41,7 @@ export async function loadConfigFromLocalStorage (): Promise<void> {

await configDb.put('gateways', gateways)
await configDb.put('routers', routers)
await configDb.put('delegatedRouting', delegatedRouting)
await configDb.put('dnsJsonResolvers', dnsJsonResolvers)
await configDb.put('autoReload', autoReload)
await configDb.put('debug', debug)
Expand All @@ -44,15 +51,17 @@ export async function loadConfigFromLocalStorage (): Promise<void> {

export async function resetConfig (): Promise<void> {
await configDb.open()
localStorage.removeItem(LOCAL_STORAGE_KEYS.config.gateways)
localStorage.setItem(LOCAL_STORAGE_KEYS.config.gateways, JSON.stringify(defaultGateways))
await configDb.put('gateways', defaultGateways)
localStorage.removeItem(LOCAL_STORAGE_KEYS.config.routers)
localStorage.setItem(LOCAL_STORAGE_KEYS.config.routers, JSON.stringify(defaultRouters))
await configDb.put('routers', defaultRouters)
localStorage.removeItem(LOCAL_STORAGE_KEYS.config.dnsJsonResolvers)
localStorage.setItem(LOCAL_STORAGE_KEYS.config.delegatedRouting, String(defaultdelegatedRouting))
await configDb.put('delegatedRouting', true)
localStorage.setItem(LOCAL_STORAGE_KEYS.config.dnsJsonResolvers, JSON.stringify(defaultDnsJsonResolvers))
await configDb.put('dnsJsonResolvers', defaultDnsJsonResolvers)
localStorage.removeItem(LOCAL_STORAGE_KEYS.config.autoReload)
localStorage.setItem(LOCAL_STORAGE_KEYS.config.autoReload, String(defaultAutoReload))
await configDb.put('autoReload', false)
localStorage.removeItem(LOCAL_STORAGE_KEYS.config.debug)
localStorage.setItem(LOCAL_STORAGE_KEYS.config.debug, '')
await configDb.put('debug', '')
configDb.close()
}
Expand All @@ -65,6 +74,7 @@ export async function setConfig (config: ConfigDb, logger: ComponentLogger): Pro
await configDb.open()
await configDb.put('gateways', config.gateways)
await configDb.put('routers', config.routers)
await configDb.put('delegatedRouting', config.delegatedRouting)
await configDb.put('dnsJsonResolvers', config.dnsJsonResolvers)
await configDb.put('autoReload', config.autoReload)
await configDb.put('debug', config.debug ?? '')
Expand All @@ -76,7 +86,8 @@ export async function getConfig (logger: ComponentLogger): Promise<ConfigDb> {
let gateways: string[] = defaultGateways
let routers: string[] = defaultRouters
let dnsJsonResolvers: Record<string, string> = defaultDnsJsonResolvers
let autoReload = false
let autoReload = defaultAutoReload
let delegatedRouting = defaultdelegatedRouting
let debug = ''

try {
Expand All @@ -88,7 +99,10 @@ export async function getConfig (logger: ComponentLogger): Promise<ConfigDb> {

dnsJsonResolvers = await configDb.get('dnsJsonResolvers')

autoReload = await configDb.get('autoReload') ?? false
autoReload = await configDb.get('autoReload') ?? defaultAutoReload

delegatedRouting = await configDb.get('delegatedRouting') ?? defaultdelegatedRouting

debug = await configDb.get('debug') ?? ''
configDb.close()
debugLib.enable(debug)
Expand All @@ -100,7 +114,7 @@ export async function getConfig (logger: ComponentLogger): Promise<ConfigDb> {
gateways = [...defaultGateways]
}

if (routers == null || routers.length === 0) {
if (routers == null) {
routers = [...defaultRouters]
}
if (dnsJsonResolvers == null || Object.keys(dnsJsonResolvers).length === 0) {
Expand All @@ -111,6 +125,7 @@ export async function getConfig (logger: ComponentLogger): Promise<ConfigDb> {
return {
gateways,
routers,
delegatedRouting,
dnsJsonResolvers,
autoReload,
debug
Expand Down
33 changes: 33 additions & 0 deletions src/lib/local-gateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { uiLogger } from './logger.js'

export const localGwUrl = 'http://localhost:8080'
// export const localGwUrl = 'http://localhost:8080'
const localGwTestUrl = `${localGwUrl}/ipfs/bafkqablimvwgy3y?format=raw`
const expectedContentType = 'application/vnd.ipld.raw'
const expectedResponseBody = 'hello'

const log = uiLogger.forComponent('local-gateway-prober')

export async function hasLocalGateway (): Promise<boolean> {
try {
log(`probing for local trustless gateway at ${localGwTestUrl}`)
const resp = await fetch(localGwTestUrl, { cache: 'no-store' })
if (!resp.ok) {
return false
}
if (resp.headers.get('Content-Type') !== expectedContentType) {
return false
}
const respBody = await resp.text()

if (respBody === expectedResponseBody) {
log(`found local trustless gateway at ${localGwTestUrl}`)
return true
} else {
return false
}
} catch (e: unknown) {
log.error('failed to probe trustless gateway', e)
return false
}
}
1 change: 1 addition & 0 deletions src/lib/local-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const LOCAL_STORAGE_KEYS = {
gateways: getLocalStorageKey('config', 'gateways'),
routers: getLocalStorageKey('config', 'routers'),
autoReload: getLocalStorageKey('config', 'autoReload'),
delegatedRouting: getLocalStorageKey('config', 'delegatedRouting'),
dnsJsonResolvers: getLocalStorageKey('config', 'dnsJsonResolvers'),
debug: getLocalStorageKey('config', 'debug')
},
Expand Down
59 changes: 55 additions & 4 deletions src/pages/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { RouteContext } from '../context/router-context.jsx'
import { ServiceWorkerProvider } from '../context/service-worker-context.jsx'
import { HeliaServiceWorkerCommsChannel } from '../lib/channel.js'
import { defaultDnsJsonResolvers, defaultGateways, defaultRouters, getConfig, loadConfigFromLocalStorage, resetConfig } from '../lib/config-db.js'
import { hasLocalGateway, localGwUrl } from '../lib/local-gateway.js'
import { LOCAL_STORAGE_KEYS } from '../lib/local-storage.js'
import { getUiComponentLogger, uiLogger } from '../lib/logger.js'
import './default-page-styles.css'
Expand All @@ -16,7 +17,7 @@ const uiComponentLogger = getUiComponentLogger('config-page')
const log = uiLogger.forComponent('config-page')
const channel = new HeliaServiceWorkerCommsChannel('WINDOW', uiComponentLogger)

const urlValidationFn = (value: string): Error | null => {
const gatewayArrayValidationFn = (value: string): Error | null => {
try {
const urls = JSON.parse(value) satisfies string[]
let i = 0
Expand All @@ -37,6 +38,24 @@ const urlValidationFn = (value: string): Error | null => {
}
}

const routersArrayValidationFn = (value: string): Error | null => {
try {
const urls = JSON.parse(value) satisfies string[]
let i = 0
try {
urls.map((url, index) => {
i = index
return new URL(url)
})
} catch (e) {
throw new Error(`URL "${urls[i]}" at index ${i} is not valid`)
}
return null
} catch (err) {
return err as Error
}
}

const dnsJsonValidationFn = (value: string): Error | null => {
try {
const urls: Record<string, string> = JSON.parse(value)
Expand Down Expand Up @@ -84,10 +103,41 @@ function ConfigPage (): React.JSX.Element | null {
window.parent?.postMessage({ source: 'helia-sw-config-iframe', target: 'PARENT', action: 'RELOAD_CONFIG', config }, {
targetOrigin
})
log.trace('config-page: RELOAD_CONFIG sent to parent window')
log.trace('RELOAD_CONFIG sent to parent window')
}, [])

// Effect to add or remove the local gateway
useEffect(() => {
hasLocalGateway()
.then(async hasLocalGw => {
// check if local storage has it.
const unparsedGwConf = localStorage.getItem(LOCAL_STORAGE_KEYS.config.gateways)
let gwConf = unparsedGwConf != null ? JSON.parse(unparsedGwConf) as string[] : defaultGateways

if (hasLocalGw) {
// Add the local gateway to config if not there already
if (!gwConf.includes(localGwUrl)) {
log(`Adding ${localGwUrl} to gateway list`)
gwConf.unshift(localGwUrl)
}
} else if (gwConf.includes(localGwUrl)) {
// remove local gateway from the configuration if the gateway is not available
gwConf = gwConf.filter(gw => gw !== localGwUrl)
if (gwConf.length === 0) {
// if there are no gateways following the removal reset to the default gateways
gwConf = defaultGateways
}
}

// persist to localstorage, idb and 🙃
localStorage.setItem(LOCAL_STORAGE_KEYS.config.gateways, JSON.stringify(gwConf))
await loadConfigFromLocalStorage()
await channel.messageAndWaitForResponse('SW', { target: 'SW', action: 'RELOAD_CONFIG' })
await postFromIframeToParentSw()
setResetKey((prev) => prev + 1) // needed to ensure the config is re-rendered
}).catch(err => {
log.error('failed to probe for local gateway', err)
})
/**
* On initial load, we want to send the config to the parent window, so that the reload page can auto-reload if enabled, and the subdomain registered service worker gets the latest config without user interaction.
*/
Expand Down Expand Up @@ -122,8 +172,9 @@ function ConfigPage (): React.JSX.Element | null {
return (
<main className='e2e-config-page pa4-l bg-snow mw7 center pa4'>
<Collapsible collapsedLabel="View config" expandedLabel='Hide config' collapsed={isLoadedInIframe}>
<LocalStorageInput className="e2e-config-page-input e2e-config-page-input-gateways" localStorageKey={LOCAL_STORAGE_KEYS.config.gateways} label='Gateways' validationFn={urlValidationFn} defaultValue={JSON.stringify(defaultGateways)} resetKey={resetKey} />
<LocalStorageInput className="e2e-config-page-input e2e-config-page-input-routers" localStorageKey={LOCAL_STORAGE_KEYS.config.routers} label='Routers' validationFn={urlValidationFn} defaultValue={JSON.stringify(defaultRouters)} resetKey={resetKey} />
<LocalStorageInput className="e2e-config-page-input e2e-config-page-input-gateways" localStorageKey={LOCAL_STORAGE_KEYS.config.gateways} label='Gateways' validationFn={gatewayArrayValidationFn} defaultValue={JSON.stringify(defaultGateways)} resetKey={resetKey} />
<LocalStorageInput className="e2e-config-page-input e2e-config-page-input-routers" localStorageKey={LOCAL_STORAGE_KEYS.config.routers} label='Routers' validationFn={routersArrayValidationFn} defaultValue={JSON.stringify(defaultRouters)} resetKey={resetKey} />
<LocalStorageToggle className="e2e-config-page-input e2e-config-page-input" localStorageKey={LOCAL_STORAGE_KEYS.config.delegatedRouting} onLabel='Use Delegated Routing' offLabel='Disabled' resetKey={resetKey} />
<LocalStorageInput className="e2e-config-page-input e2e-config-page-input-dnsJsonResolvers" localStorageKey={LOCAL_STORAGE_KEYS.config.dnsJsonResolvers} label='DNS (application/dns-json) resolvers' validationFn={dnsJsonValidationFn} defaultValue={JSON.stringify(defaultDnsJsonResolvers)} resetKey={resetKey} />
<LocalStorageToggle className="e2e-config-page-input e2e-config-page-input-autoreload" localStorageKey={LOCAL_STORAGE_KEYS.config.autoReload} onLabel='Auto Reload' offLabel='Show Config' resetKey={resetKey} />
<LocalStorageInput className="e2e-config-page-input" localStorageKey={LOCAL_STORAGE_KEYS.config.debug} label='Debug logging' validationFn={stringValidationFn} defaultValue='' resetKey={resetKey} />
Expand Down
6 changes: 4 additions & 2 deletions src/sw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,10 @@ async function getVerifiedFetch (): Promise<VerifiedFetch> {

const verifiedFetch = await createVerifiedFetch({
gateways: config.gateways,
routers: config.routers,
dnsResolvers
routers: config.delegatedRouting ? config.routers : [],
dnsResolvers,
allowInsecure: true,
allowLocal: true
}, {
contentTypeParser
})
Expand Down
2 changes: 1 addition & 1 deletion test-e2e/layout.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ test.describe('smoketests', () => {
const inputLocator = getConfigPageInput(page)
// see https://playwright.dev/docs/locators#strictness
await inputLocator.first().waitFor()
expect(await inputLocator.count()).toEqual(5)
expect(await inputLocator.count()).toEqual(6)
const submitButton = getConfigPageSaveButton(page)
await expect(submitButton).toBeVisible()
})
Expand Down
Loading