Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
6 changes: 4 additions & 2 deletions src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' });
Expand Down Expand Up @@ -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);
Expand Down
10 changes: 9 additions & 1 deletion src/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -903,9 +903,17 @@ <h4>IPFS Gateway</h4>
Prefer local IPFS gateway (localhost:8080)
</label>
</div>
<div class="settings-row">
<label class="settings-label">
DNS over HTTPS URL:
<input type="text" class="form-input" id="dnsOverHttpsInput" placeholder="https://cloudflare-dns.com/dns-query">
</label>
<button type="button" class="btn btn-secondary" id="saveDnsOverHttps">Save</button>
</div>
<div class="settings-help">
Choose which IPFS gateway to use for launching apps from CIDs. URLs will be constructed using subdomain gateway resolution https://{cid}.ipfs.gateway.com<br>
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.<br>
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).
</div>
</div>

Expand Down
35 changes: 34 additions & 1 deletion src/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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) => {
Expand Down Expand Up @@ -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();

Expand All @@ -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'];

Expand Down Expand Up @@ -506,6 +512,33 @@ class PopupManager {
}
}

private async handleSaveDnsOverHttps(): Promise<void> {
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<void> {
try {
await ExportManager.exportData();
Expand Down
13 changes: 11 additions & 2 deletions src/storage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -57,7 +58,8 @@ export class StorageManager {
'inbrowser.dev'
],
preferLocalGateway: false,
localGatewayUrl: 'http://localhost:8080'
localGatewayUrl: 'http://localhost:8080',
dnsOverHttpsUrl: DEFAULT_DNS_OVER_HTTPS_URL
},
dnslinkCache: {}
};
Expand Down Expand Up @@ -181,7 +183,14 @@ export class StorageManager {

async getGatewayConfig(): Promise<GatewayConfig> {
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<GatewayConfig>): Promise<GatewayConfig> {
Expand Down
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export interface GatewayConfig {
customGateways: string[];
preferLocalGateway: boolean;
localGatewayUrl: string;
dnsOverHttpsUrl: string;
}

export interface UserSettings {
Expand Down
11 changes: 5 additions & 6 deletions src/utils/dnslink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<DNSLinkResult> {
static async probe(domain: string, dnsOverHttpsUrl: string): Promise<DNSLinkResult> {
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
Expand Down Expand Up @@ -62,8 +61,8 @@ export class DNSLinkProbe {
/**
* Query TXT records for a domain using DNS over HTTPS
*/
private static async queryTxtRecord(domain: string): Promise<DNSLinkResult> {
const url = `${this.DNS_OVER_HTTPS_URL}?name=${encodeURIComponent(domain)}&type=TXT`;
private static async queryTxtRecord(domain: string, dnsOverHttpsUrl: string): Promise<DNSLinkResult> {
const url = `${dnsOverHttpsUrl}?name=${encodeURIComponent(domain)}&type=TXT`;

try {
const response = await fetch(url, {
Expand Down
Loading