Skip to content

Commit d2239ec

Browse files
committed
fix: r2 bucket for firmware storage
1 parent 091a017 commit d2239ec

10 files changed

Lines changed: 454 additions & 363 deletions

File tree

src/components/LandingHero.tsx

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,55 @@ interface FirmwareRelease {
2222
}>;
2323
}
2424

25+
const R2_BASE_URL = 'https://fw.wantclue.de';
26+
27+
const parseGitHubRepo = (repositoryUrl: string) => {
28+
const repoMatch = repositoryUrl.match(/github\.com\/([^/]+)\/([^/]+)/);
29+
if (!repoMatch) throw new Error('Invalid repository URL');
30+
const [, owner, repo] = repoMatch;
31+
return { owner, repo };
32+
};
33+
34+
const fetchGitHubAPI = async (url: string) => {
35+
const response = await fetch(url);
36+
if (!response.ok) throw new Error(`GitHub API returned ${response.status}`);
37+
return response.json();
38+
};
39+
40+
const extractSHA256Hash = (releaseBody: string, binaryName: string) => {
41+
const lines = (releaseBody || '').split('\n');
42+
for (const line of lines) {
43+
if (line.includes(binaryName)) {
44+
const parts = line.split(/\s+/);
45+
if (parts.length > 1 && parts[1] === binaryName) {
46+
return parts[0]; // Return the first part as the hash
47+
}
48+
}
49+
}
50+
return null;
51+
};
52+
53+
// Calculate SHA256 hash of downloaded binary
54+
const calculateSHA256 = async (data: ArrayBuffer) => {
55+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
56+
return Array.from(new Uint8Array(hashBuffer))
57+
.map((byte) => byte.toString(16).padStart(2, '0'))
58+
.join('');
59+
};
60+
61+
// Fetch the SHA256 hash for a specific binary from GitHub release notes
62+
const fetchSHA256Hash = async (repositoryUrl: string, versionTag: string, binaryName: string) => {
63+
try {
64+
const { owner, repo } = parseGitHubRepo(repositoryUrl);
65+
const apiUrl = `https://api.github.com/repos/${owner}/${repo}/releases/tags/${versionTag}`;
66+
const release = await fetchGitHubAPI(apiUrl);
67+
return extractSHA256Hash(release.body, binaryName);
68+
} catch (error) {
69+
console.error('Error fetching SHA256 hash:', error);
70+
return null;
71+
}
72+
};
73+
2574
export default function LandingHero() {
2675
const { t } = useTranslation();
2776
const [selectedDevice, setSelectedDevice] = useState<string>('')
@@ -324,14 +373,47 @@ export default function LandingHero() {
324373
const firmwareData = firmwareOptions.find(f => f.version === selectedFirmware);
325374

326375
if (firmwareData && firmwareData.assets.length > 0) {
327-
// Download from GitHub releases using CORS proxy
328-
const proxyUrl = 'https://corsproxy.io/?url=';
329376
const firmwareUrl = firmwareData.assets[0].browser_download_url;
330-
const firmwareResponse = await fetch(proxyUrl + firmwareUrl);
377+
const binaryName = decodeURIComponent(firmwareUrl.split('/').pop()!); // e.g. esp-miner-factory-402-v2.5.0.bin
378+
const r2Url = `${R2_BASE_URL}/${selectedFirmware}/${binaryName}`;
379+
380+
console.log(`Downloading firmware from R2: ${r2Url}`);
381+
382+
// Fetch SHA256 from GitHub release body for verification
383+
const sha256Hash = await fetchSHA256Hash(device.repository, selectedFirmware, binaryName);
384+
385+
if (sha256Hash) {
386+
console.log(`Found SHA256 hash: ${sha256Hash}`);
387+
} else {
388+
console.warn('No SHA256 hash found');
389+
}
390+
391+
setStatus(t('status.downloadFirmware'));
392+
393+
const firmwareResponse = await fetch(r2Url);
331394
if (!firmwareResponse.ok) {
332-
throw new Error('Failed to download firmware from GitHub');
395+
throw new Error(`Failed to download firmware from R2 (status ${firmwareResponse.status})`);
333396
}
397+
334398
firmwareArrayBuffer = await firmwareResponse.arrayBuffer();
399+
400+
// Compare the calculated hash with the fetched hash
401+
if (sha256Hash) {
402+
// Calculate the SHA256 hash of the downloaded binary
403+
const calculatedHash = await calculateSHA256(firmwareArrayBuffer);
404+
console.log(`Calculated SHA256 hash of downloaded binary: ${calculatedHash}`);
405+
406+
if (calculatedHash === sha256Hash) {
407+
console.log('SHA256 hash verification successful. Binary is valid.');
408+
} else {
409+
console.error('SHA256 hash verification failed! Binary may be corrupted or tampered with.');
410+
throw new Error('Hash verification failed');
411+
}
412+
} else {
413+
// TODO: versions don't have hashes on the release page
414+
// in this case we warn silently in the console but accept the risk
415+
console.warn("No SHA256 found on the release page!");
416+
}
335417
} else {
336418
// Fall back to local firmware files
337419
const localFirmware = localFirmwareOptions.find(f => f.version === selectedFirmware);

src/i18n/locales/de.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"selectBoth": "Bitte wähle sowohl Gerätemodell als auch Board-Version aus",
6464
"connectFirst": "Bitte verbinde zuerst ein Gerät",
6565
"preparing": "Bereite Flashen vor...",
66+
"downloadFirmware": "Firmware wird heruntergeladen...",
6667
"flashing": "Flashen: {{percent}}% abgeschlossen",
6768
"completed": "Flashen abgeschlossen. Gerät wird neu gestartet...",
6869
"success": "Flashen erfolgreich abgeschlossen! Gerät wurde neu gestartet.",

src/i18n/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"selectBoth": "Please select both device model and board version",
6464
"connectFirst": "Please connect to a device first",
6565
"preparing": "Preparing to flash...",
66+
"downloadFirmware": "Downloading firmware...",
6667
"flashing": "Flashing: {{percent}}% complete",
6768
"completed": "Flashing completed. Restarting device, please wait...",
6869
"success": "Flashing completed successfully! Device has been restarted.",

src/i18n/locales/it.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"selectBoth": "Seleziona sia il modello del dispositivo che la versione della scheda",
6464
"connectFirst": "Collegati prima a un dispositivo",
6565
"preparing": "Preparazione per il flashing in corso...",
66+
"downloadFirmware": "Downloading firmware...",
6667
"flashing": "Flashing: {{percent}}% completato",
6768
"completed": "Flashing completato. Riavvio del dispositivo...",
6869
"success": "Flashing completato con successo! Il dispositivo stato riavviato.",
@@ -74,4 +75,4 @@
7475
"description": "Questa applicazione richiede un browser basato su Chromium (come Google Chrome, Microsoft Edge o Brave) per funzionare correttamente. Passa a un browser compatibile e riprova."
7576
}
7677
}
77-
}
78+
}

src/i18n/locales/pt.json

Lines changed: 72 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,78 @@
11
{
2-
"common": {
3-
"theme": "Tema",
4-
"language": "Língua",
5-
"dark": "Escuro",
6-
"light": "Claro"
2+
"common": {
3+
"theme": "Tema",
4+
"language": "Língua",
5+
"dark": "Escuro",
6+
"light": "Claro"
7+
},
8+
"header": {
9+
"home": "Home",
10+
"features": "Recursos",
11+
"instructions": "Instruções"
12+
},
13+
"hero": {
14+
"title": "Grave Seu Bitaxe Diretamente da Web",
15+
"description": "Conecte seu dispositivo, selecione seu modelo e versão da placa, e inicie a gravação imediatamente. Nenhuma configuração necessária.",
16+
"getStarted": "Começar",
17+
"connect": "Conectar",
18+
"disconnect": "Desconectar",
19+
"selectDevice": "Selecionar dispositivo",
20+
"selectBoard": "Selecionar versão da placa",
21+
"selectFirmware": "Selecionar versão do firmware",
22+
"loadingFirmware": "Carregando versões do firmware...",
23+
"startFlashing": "Iniciar Gravação",
24+
"flashing": "Gravando...",
25+
"startLogging": "Iniciar Log",
26+
"stopLogging": "Parar Log",
27+
"downloadLogs": "Baixar Logs",
28+
"loggingDescription": "Conecte seu dispositivo, registre os dados seriais e baixe-os posteriormente.",
29+
"keepConfig": "Manter configuração"
30+
},
31+
"features": {
32+
"title": "Principais Recursos",
33+
"fastFlashing": {
34+
"title": "Gravação Rápida",
35+
"description": "Grave seu Bitaxe em segundos, não minutos."
736
},
8-
"header": {
9-
"home": "Home",
10-
"features": "Recursos",
11-
"instructions": "Instruções"
37+
"webBased": {
38+
"title": "Baseado na Web",
39+
"description": "Não é necessário software especial. Use seu navegador."
1240
},
13-
"hero": {
14-
"title": "Grave Seu Bitaxe Diretamente da Web",
15-
"description": "Conecte seu dispositivo, selecione seu modelo e versão da placa, e inicie a gravação imediatamente. Nenhuma configuração necessária.",
16-
"getStarted": "Começar",
17-
"connect": "Conectar",
18-
"disconnect": "Desconectar",
19-
"selectDevice": "Selecionar dispositivo",
20-
"selectBoard": "Selecionar versão da placa",
21-
"selectFirmware": "Selecionar versão do firmware",
22-
"loadingFirmware": "Carregando versões do firmware...",
23-
"startFlashing": "Iniciar Gravação",
24-
"flashing": "Gravando...",
25-
"startLogging": "Iniciar Log",
26-
"stopLogging": "Parar Log",
27-
"downloadLogs": "Baixar Logs",
28-
"loggingDescription": "Conecte seu dispositivo, registre os dados seriais e baixe-os posteriormente.",
29-
"keepConfig": "Manter configuração"
30-
},
31-
"features": {
32-
"title": "Principais Recursos",
33-
"fastFlashing": {
34-
"title": "Gravação Rápida",
35-
"description": "Grave seu Bitaxe em segundos, não minutos."
36-
},
37-
"webBased": {
38-
"title": "Baseado na Web",
39-
"description": "Não é necessário software especial. Use seu navegador."
40-
},
41-
"multipleBoards": {
42-
"title": "Múltiplas Placas",
43-
"description": "Suporte para várias placas e módulos Bitaxe."
44-
}
45-
},
46-
"instructions": {
47-
"title": "Como Usar",
48-
"steps": {
49-
"1": "Conecte seu Bitaxe ao computador.",
50-
"2": "Clique em \"Conectar Dispositivo\" e selecione seu dispositivo na janela pop-up.",
51-
"3": "Selecione o modelo do dispositivo no menu suspenso.",
52-
"4": "Escolha a versão apropriada da placa.",
53-
"5": "Clique em \"Iniciar Gravação\" para começar o processo.",
54-
"6": "Aguarde até o processo de gravação ser concluído.",
55-
"7": "Desconecte e reinicie seu dispositivo."
56-
},
57-
"moreInfo": "Para instruções mais detalhadas, consulte nossa",
58-
"documentation": "documentação"
59-
},
60-
"status": {
61-
"connecting": "Conectando ao dispositivo...",
62-
"connected": "Conectado com sucesso!",
63-
"selectBoth": "Por favor, selecione o modelo do dispositivo e a versão da placa",
64-
"connectFirst": "Por favor, conecte-se a um dispositivo primeiro",
65-
"preparing": "Preparando para gravar...",
66-
"flashing": "Gravando: {{percent}}% concluído",
67-
"completed": "Gravação concluída. Reiniciando o dispositivo...",
68-
"success": "Gravação concluída com sucesso! O dispositivo foi reiniciado.",
69-
"loggingStarted": "Log serial iniciado..."
41+
"multipleBoards": {
42+
"title": "Múltiplas Placas",
43+
"description": "Suporte para várias placas e módulos Bitaxe."
44+
}
45+
},
46+
"instructions": {
47+
"title": "Como Usar",
48+
"steps": {
49+
"1": "Conecte seu Bitaxe ao computador.",
50+
"2": "Clique em \"Conectar Dispositivo\" e selecione seu dispositivo na janela pop-up.",
51+
"3": "Selecione o modelo do dispositivo no menu suspenso.",
52+
"4": "Escolha a versão apropriada da placa.",
53+
"5": "Clique em \"Iniciar Gravação\" para começar o processo.",
54+
"6": "Aguarde até o processo de gravação ser concluído.",
55+
"7": "Desconecte e reinicie seu dispositivo."
7056
},
71-
"errors": {
72-
"browserCompatibility": {
73-
"title": "Erro de Compatibilidade do Navegador",
74-
"description": "Este aplicativo requer um navegador baseado no Chromium (como Google Chrome, Microsoft Edge ou Brave) para funcionar corretamente. Por favor, mude para um navegador compatível e tente novamente."
75-
}
57+
"moreInfo": "Para instruções mais detalhadas, consulte nossa",
58+
"documentation": "documentação"
59+
},
60+
"status": {
61+
"connecting": "Conectando ao dispositivo...",
62+
"connected": "Conectado com sucesso!",
63+
"selectBoth": "Por favor, selecione o modelo do dispositivo e a versão da placa",
64+
"connectFirst": "Por favor, conecte-se a um dispositivo primeiro",
65+
"preparing": "Preparando para gravar...",
66+
"downloadFirmware": "Downloading firmware...",
67+
"flashing": "Gravando: {{percent}}% concluído",
68+
"completed": "Gravação concluída. Reiniciando o dispositivo...",
69+
"success": "Gravação concluída com sucesso! O dispositivo foi reiniciado.",
70+
"loggingStarted": "Log serial iniciado..."
71+
},
72+
"errors": {
73+
"browserCompatibility": {
74+
"title": "Erro de Compatibilidade do Navegador",
75+
"description": "Este aplicativo requer um navegador baseado no Chromium (como Google Chrome, Microsoft Edge ou Brave) para funcionar corretamente. Por favor, mude para um navegador compatível e tente novamente."
7676
}
77+
}
7778
}

0 commit comments

Comments
 (0)