From 0f8a7cc7df0379225d122d16c1fe45bc33b8c9da Mon Sep 17 00:00:00 2001 From: Daniel N <2color@users.noreply.github.com> Date: Tue, 12 Aug 2025 11:34:39 +0200 Subject: [PATCH 1/2] feat: make doh endpoint configurable --- src/background.ts | 6 ++++-- src/popup.html | 10 +++++++++- src/popup.ts | 35 ++++++++++++++++++++++++++++++++++- src/storage/index.ts | 3 ++- src/types/index.ts | 1 + src/utils/dnslink.ts | 11 +++++------ 6 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/background.ts b/src/background.ts index 9d8f569..fce3d39 100644 --- a/src/background.ts +++ b/src/background.ts @@ -100,7 +100,8 @@ class BackgroundManager { case 'CHECK_DNSLINK': try { - const result = await DNSLinkProbe.probe(request.data.domain); + const gatewayConfig = await storage.getGatewayConfig(); + const result = await DNSLinkProbe.probe(request.data.domain, gatewayConfig.dnsOverHttpsUrl); sendResponse({ success: true, data: result }); } catch (error) { sendResponse({ success: false, error: error instanceof Error ? error.message : 'DNSLink check failed' }); @@ -154,7 +155,8 @@ class BackgroundManager { } // Check for DNSLink and x-ipfs-path - const dnslinkResult = await DNSLinkProbe.probe(url.hostname); + const gatewayConfig = await storage.getGatewayConfig(); + const dnslinkResult = await DNSLinkProbe.probe(url.hostname, gatewayConfig.dnsOverHttpsUrl); if (dnslinkResult.hasDNSLink || dnslinkResult.hasIPFSPath) { await this.setDNSLinkIcon(tab.id); diff --git a/src/popup.html b/src/popup.html index ff43620..93591d2 100644 --- a/src/popup.html +++ b/src/popup.html @@ -903,9 +903,17 @@

IPFS Gateway

Prefer local IPFS gateway (localhost:8080) +
+ + +
Choose which IPFS gateway to use for launching apps from CIDs. URLs will be constructed using subdomain gateway resolution https://{cid}.ipfs.gateway.com
- When enabled, the extension will try to use your local IPFS gateway first if available. + When enabled, the extension will try to use your local IPFS gateway first if available.
+ DNS over HTTPS URL is used for DNSLink resolution. Common providers: Cloudflare (cloudflare-dns.com/dns-query), Google (dns.google/dns-query), Quad9 (dns.quad9.net/dns-query).
diff --git a/src/popup.ts b/src/popup.ts index 273e415..aeb89f0 100644 --- a/src/popup.ts +++ b/src/popup.ts @@ -55,6 +55,7 @@ class PopupManager { const cancelEditButton = document.getElementById('cancelEditButton'); const editAppForm = document.getElementById('editAppForm') as HTMLFormElement; const preferLocalGateway = document.getElementById('preferLocalGateway') as HTMLInputElement; + const saveDnsOverHttps = document.getElementById('saveDnsOverHttps'); addButton?.addEventListener('click', () => this.showAddModal()); searchBox?.addEventListener('input', (e) => this.handleSearch((e.target as HTMLInputElement).value)); @@ -73,6 +74,7 @@ class PopupManager { cancelEditButton?.addEventListener('click', () => this.hideEditModal()); editAppForm?.addEventListener('submit', (e) => this.handleEditApp(e)); preferLocalGateway?.addEventListener('change', (e) => this.handleLocalGatewayToggle((e.target as HTMLInputElement).checked)); + saveDnsOverHttps?.addEventListener('click', () => this.handleSaveDnsOverHttps()); // Close modal when clicking outside addAppModal?.addEventListener('click', (e) => { @@ -297,8 +299,9 @@ class PopupManager { const themeSelect = document.getElementById('themeSelect') as HTMLSelectElement; const gatewaySelect = document.getElementById('gatewaySelect') as HTMLSelectElement; const preferLocalGateway = document.getElementById('preferLocalGateway') as HTMLInputElement; + const dnsOverHttpsInput = document.getElementById('dnsOverHttpsInput') as HTMLInputElement; - if (settingsModal && themeSelect && gatewaySelect && preferLocalGateway) { + if (settingsModal && themeSelect && gatewaySelect && preferLocalGateway && dnsOverHttpsInput) { // Update theme select to current value themeSelect.value = themeManager.getTheme(); @@ -310,6 +313,9 @@ class PopupManager { // Update local gateway preference preferLocalGateway.checked = gatewayConfig.preferLocalGateway || false; + // Update DNS over HTTPS URL + dnsOverHttpsInput.value = gatewayConfig.dnsOverHttpsUrl || ''; + // Check if current gateway is in the predefined options const predefinedOptions = ['dweb.link', 'inbrowser.link', 'inbrowser.dev']; @@ -506,6 +512,33 @@ class PopupManager { } } + private async handleSaveDnsOverHttps(): Promise { + const dnsOverHttpsInput = document.getElementById('dnsOverHttpsInput') as HTMLInputElement; + const dnsOverHttpsUrl = dnsOverHttpsInput.value.trim(); + + if (!dnsOverHttpsUrl) { + this.showTemporaryMessage('Please enter a DNS over HTTPS URL', 'error'); + return; + } + + // Validate URL format + try { + const url = new URL(dnsOverHttpsUrl); + + // Basic validation - should be HTTPS and have dns-query path or similar + if (url.protocol !== 'https:') { + this.showTemporaryMessage('DNS over HTTPS URL must use HTTPS', 'error'); + return; + } + + await storage.updateGatewayConfig({ dnsOverHttpsUrl }); + this.showTemporaryMessage('DNS over HTTPS URL updated successfully!', 'success'); + } catch (error) { + console.error('Failed to save DNS over HTTPS URL:', error); + this.showTemporaryMessage('Please enter a valid HTTPS URL', 'error'); + } + } + private async handleExport(): Promise { try { await ExportManager.exportData(); diff --git a/src/storage/index.ts b/src/storage/index.ts index 2538891..735daac 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -57,7 +57,8 @@ export class StorageManager { 'inbrowser.dev' ], preferLocalGateway: false, - localGatewayUrl: 'http://localhost:8080' + localGatewayUrl: 'http://localhost:8080', + dnsOverHttpsUrl: 'https://cloudflare-dns.com/dns-query' }, dnslinkCache: {} }; diff --git a/src/types/index.ts b/src/types/index.ts index 3bff903..f3d2bdd 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -37,6 +37,7 @@ export interface GatewayConfig { customGateways: string[]; preferLocalGateway: boolean; localGatewayUrl: string; + dnsOverHttpsUrl: string; } export interface UserSettings { diff --git a/src/utils/dnslink.ts b/src/utils/dnslink.ts index 840857d..e7f5d67 100644 --- a/src/utils/dnslink.ts +++ b/src/utils/dnslink.ts @@ -8,25 +8,24 @@ export interface DNSLinkResult { } export class DNSLinkProbe { - private static readonly DNS_OVER_HTTPS_URL = 'https://cloudflare-dns.com/dns-query'; private static readonly DNSLINK_PREFIX = 'dnslink='; private static readonly IPFS_PREFIX = '/ipfs/'; /** * Probe a domain for DNSLink records and x-ipfs-path headers */ - static async probe(domain: string): Promise { + static async probe(domain: string, dnsOverHttpsUrl: string): Promise { try { const cleanDomain = this.cleanDomain(domain); // Try DNSLink first (preferred method) // Try _dnslink subdomain first (RFC standard) const dnslinkDomain = `_dnslink.${cleanDomain}`; - let result = await this.queryTxtRecord(dnslinkDomain); + let result = await this.queryTxtRecord(dnslinkDomain, dnsOverHttpsUrl); // If no _dnslink record, try the domain itself if (!result.hasDNSLink) { - result = await this.queryTxtRecord(cleanDomain); + result = await this.queryTxtRecord(cleanDomain, dnsOverHttpsUrl); } // If DNSLink found, return it @@ -62,8 +61,8 @@ export class DNSLinkProbe { /** * Query TXT records for a domain using DNS over HTTPS */ - private static async queryTxtRecord(domain: string): Promise { - const url = `${this.DNS_OVER_HTTPS_URL}?name=${encodeURIComponent(domain)}&type=TXT`; + private static async queryTxtRecord(domain: string, dnsOverHttpsUrl: string): Promise { + const url = `${dnsOverHttpsUrl}?name=${encodeURIComponent(domain)}&type=TXT`; try { const response = await fetch(url, { From af12fc7b3b68a1b6f0a837fd3635274e1fb4da70 Mon Sep 17 00:00:00 2001 From: Daniel N <2color@users.noreply.github.com> Date: Tue, 12 Aug 2025 11:39:49 +0200 Subject: [PATCH 2/2] fix: remove duplication --- src/popup.ts | 2 +- src/storage/index.ts | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/popup.ts b/src/popup.ts index aeb89f0..6216ec5 100644 --- a/src/popup.ts +++ b/src/popup.ts @@ -314,7 +314,7 @@ class PopupManager { preferLocalGateway.checked = gatewayConfig.preferLocalGateway || false; // Update DNS over HTTPS URL - dnsOverHttpsInput.value = gatewayConfig.dnsOverHttpsUrl || ''; + dnsOverHttpsInput.value = gatewayConfig.dnsOverHttpsUrl; // Check if current gateway is in the predefined options const predefinedOptions = ['dweb.link', 'inbrowser.link', 'inbrowser.dev']; diff --git a/src/storage/index.ts b/src/storage/index.ts index 735daac..f14c970 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -2,6 +2,7 @@ import { App, AppStorage, CreateAppRequest, CreateVersionRequest, UpdateAppReque import { LocalGatewayProbe } from '../utils/localGateway.js'; const STORAGE_KEY = 'ipfs_spark_data'; +const DEFAULT_DNS_OVER_HTTPS_URL = 'https://cloudflare-dns.com/dns-query'; export class StorageManager { private static instance: StorageManager; @@ -58,7 +59,7 @@ export class StorageManager { ], preferLocalGateway: false, localGatewayUrl: 'http://localhost:8080', - dnsOverHttpsUrl: 'https://cloudflare-dns.com/dns-query' + dnsOverHttpsUrl: DEFAULT_DNS_OVER_HTTPS_URL }, dnslinkCache: {} }; @@ -182,7 +183,14 @@ export class StorageManager { async getGatewayConfig(): Promise { const data = this.cache || await this.load(); - return data.gatewayConfig; + const config = data.gatewayConfig; + + // Ensure dnsOverHttpsUrl has a default value for existing installations + if (!config.dnsOverHttpsUrl) { + config.dnsOverHttpsUrl = DEFAULT_DNS_OVER_HTTPS_URL; + } + + return config; } async updateGatewayConfig(config: Partial): Promise {