Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
## Unreleased

### English
- **New theme 🟡 Black & Yellow:** near-black background with exchange-style yellow, green for gains and red for alerts, in Settings → Farm → Visual theme (`THEME=gold`). Applies to the dashboard, kiosk mode and the admin screen.
- **Worker details (last 24 h):** click a worker in the table to open a dialog with its last 24 hours through the proxy: accepted and rejected shares, best share and average hashrate, a chart of shares per hour (rejections in red), a chart of the best difficulty per hour (log scale, so the ESP32 fit too), the 10 best hashes with their time and the latest shares. The proxy keeps this per hour in its stats file (survives restarts) and serves it at `/stats/worker?name=`; the dashboard at `/api/worker?name=`. Not in kiosk mode.
- **Two new achievements:** 🌋 **Trillion hash** (a share with difficulty over 1 trillion) and the 🏆 **Golden prize: block found!**, unlocked when a share passes the network difficulty. The golden prize sits on its own full-width row, shows its trophy even while locked and lights up in gold when won.

### Português
- **Tema novo 🟡 Amarelo e Preto:** fundo quase preto com o amarelo de exchange, verde para ganhos e vermelho para alertas, em Configurações → Fazenda → Tema visual (`THEME=gold`). Vale no painel, no modo quiosque e na tela de administração.
- **Detalhes do worker (últimas 24 h):** clique num worker da tabela para abrir um modal com as últimas 24 horas dele pelo proxy: shares aceitos e recusados, melhor share e hashrate médio, gráfico de shares por hora (recusados em vermelho), gráfico da melhor dificuldade por hora (escala logarítmica, para os ESP32 caberem), os 10 melhores hashes com a hora e os últimos shares. O proxy guarda isso por hora no arquivo de estatísticas (sobrevive ao reinício) e entrega em `/stats/worker?name=`; o painel, em `/api/worker?name=`. Não abre no modo quiosque.
- **Duas conquistas novas:** 🌋 **Um trilhão** (um share com dificuldade acima de 1 trilhão) e o 🏆 **Prêmio de ouro: bloco achado!**, que desbloqueia quando um share passa da dificuldade da rede. O prêmio de ouro fica numa linha inteira só para ele, mostra o troféu mesmo trancado e acende em dourado quando sai.

Expand Down
2 changes: 1 addition & 1 deletion src/i18n.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/painel.js
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,8 @@ const PAGE = `<!doctype html>
body[data-theme="ocean"] { --bg:#0b111e; --card:#131c31; --line:#223254; --txt:#e2e8f0; --dim:#8ea0c2; --gold:#38bdf8; --on-gold:#081221; --gold-soft:rgba(56,189,248,.08); --gold-border:rgba(56,189,248,.45); --ok:#34d399; --warn:#fb7185; --backdrop:rgba(11,17,30,.88); --tip-bg:#080d18; --shadow:0 4px 18px rgba(0,0,0,.28); --radius:16px; }
/* Neon: grafite azulado e verde neon */
body[data-theme="neon"] { --bg:#0a111b; --card:#20272e; --line:#343d46; --txt:#eef2f5; --dim:#97a3ad; --gold:#36cf8c; --on-gold:#04140d; --gold-soft:rgba(54,207,140,.08); --gold-border:rgba(54,207,140,.5); --ok:#52eeaf; --warn:#f87171; --backdrop:rgba(10,17,27,.88); --tip-bg:#070c13; --shadow:0 4px 18px rgba(0,0,0,.3); --radius:14px; --quiet:#f2c94c; }
/* Preto e amarelo: estilo exchange */
body[data-theme="gold"] { --bg:#0b0e11; --card:#181a20; --line:#2b3139; --txt:#eaecef; --dim:#848e9c; --gold:#fcd535; --on-gold:#181a20; --gold-soft:rgba(252,213,53,.08); --gold-border:rgba(252,213,53,.5); --ok:#0ecb81; --warn:#f6465d; --backdrop:rgba(11,14,17,.9); --tip-bg:#050607; --shadow:none; --radius:8px; --quiet:#f0b90b; }
body[data-theme="latte"] { --bg:#f5f3ee; --card:#ffffff; --line:#e2ded5; --txt:#1c1b18; --dim:#666159; --gold:#d97706; --on-gold:#ffffff; --gold-soft:rgba(217,119,6,.08); --gold-border:rgba(217,119,6,.42); --ok:#16a34a; --warn:#dc2626; --backdrop:rgba(28,27,24,.72); --tip-bg:#ffffff; --shadow:0 2px 10px rgba(28,27,24,.06); --radius:16px; }
* { box-sizing:border-box; margin:0; padding:0; }
body { background:var(--bg); color:var(--txt); font-family:-apple-system, "Segoe UI", Roboto, sans-serif; padding:16px; min-height:100vh; transition:background .2s, color .2s; }
Expand Down
13 changes: 8 additions & 5 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ const { parseBroker, cleanTopic, testBroker } = require('./mqtt');
const { ID_RE: TUYA_ID_RE, SECRET_RE: TUYA_SECRET_RE, normalizePlugs, testPlug } = require('./tuya');
const { testSonoff } = require('./sonoff');

const THEMES = ['obsidian', 'ocean', 'neon', 'latte'];
const THEMES = ['obsidian', 'ocean', 'neon', 'gold', 'latte'];
function normTheme(v) {
const s = String(v || '').trim().toLowerCase();
return THEMES.includes(s) ? s : 'obsidian';
}

const FIELDS = [
{ key: 'BTC_ADDRESS', group: 'farm', type: 'address', required: true },
{ key: 'THEME', group: 'farm', type: 'theme', options: ['', 'obsidian', 'ocean', 'neon', 'latte'] },
{ key: 'THEME', group: 'farm', type: 'theme', options: ['', 'obsidian', 'ocean', 'neon', 'gold', 'latte'] },
{ key: 'LANGUAGE', group: 'farm', type: 'select', options: ['', 'pt-BR', 'en'] },
{ key: 'CURRENCY', group: 'farm', type: 'select', options: ['', 'BRL', 'USD'] },
{ key: 'TEMP_UNIT', group: 'farm', type: 'select', options: ['', 'C', 'F'], hideEmpty: true, labels: { C: '°C (Celsius)', F: '°F (Fahrenheit)' } }, // vazio = °C
Expand Down Expand Up @@ -545,11 +545,12 @@ const T = {
'<b>Minerar também com este computador:</b> <code>docker compose --profile minerador up -d</code> (nome no painel: LOCAL_WORKER, padrão "docker"; núcleos: MINER_THREADS).',
'<b>Windows:</b> libere as portas 3000 e 3333–3339 no Firewall para a rede privada e desligue a suspensão do PC, senão os mineradores caem juntos.'] },
BTC_ADDRESS: ['Endereço de bitcoin', 'O endereço público que os mineradores usam como usuário. O painel consulta pools e carteira por ele. Mudar aqui não muda para onde os mineradores mandam o prêmio: isso vem do usuário configurado em cada um.'],
THEME: ['Tema visual (Material Design)', 'Escolha um dos 4 temas Material Design para o painel, modo kiosk e tela de administração. Clique em um cartão para pré-visualizar na hora.'],
THEME: ['Tema visual', 'Escolha um dos 5 temas para o painel, modo kiosk e tela de administração. Clique em um cartão para pré-visualizar na hora.'],
themes: [
{ id: 'obsidian', name: '🌑 Obsidian', sub: 'Material Dark · Grafite e Ouro Bitcoin (padrão)', colors: ['#0e1117', '#171c26', '#f2a900', '#3ecf8e'] },
{ id: 'ocean', name: '🌊 Material Ocean', sub: 'Material You Escuro · Azul Profundo e Ciano Elétrico', colors: ['#0b111e', '#131c31', '#38bdf8', '#34d399'] },
{ id: 'neon', name: '⚡ Material Neon', sub: 'Material Dark · Grafite e Verde Neon', colors: ['#0a111b', '#20272e', '#36cf8c', '#52eeaf'] },
{ id: 'gold', name: '🟡 Amarelo e Preto', sub: 'Estilo exchange · Preto e Amarelo', colors: ['#0b0e11', '#181a20', '#fcd535', '#0ecb81'] },
{ id: 'latte', name: '☀️ Material Solar', sub: 'Material 3 Claro · Alabastro e Âmbar', colors: ['#f5f3ee', '#ffffff', '#d97706', '#16a34a'] },
],
LANGUAGE: ['Idioma padrão', 'Do painel, do cubo e do bot. O botão PT | EN continua valendo em cada navegador.'],
Expand Down Expand Up @@ -653,11 +654,12 @@ const T = {
'<b>Also mine with this computer:</b> <code>docker compose --profile minerador up -d</code> (name on the dashboard: LOCAL_WORKER, default "docker"; cores: MINER_THREADS).',
'<b>Windows:</b> allow ports 3000 and 3333–3339 in the Firewall for the private network and turn off sleep, or all miners drop together.'] },
BTC_ADDRESS: ['Bitcoin address', 'The public address your miners use as username. The dashboard looks up pools and the wallet with it. Changing it here does not change where the miners send the reward: that comes from the username set on each miner.'],
THEME: ['Visual theme (Material Design)', 'Choose one of the 4 Material Design themes for the dashboard, kiosk mode and admin screen. Click a card to preview it live.'],
THEME: ['Visual theme', 'Choose one of the 5 themes for the dashboard, kiosk mode and admin screen. Click a card to preview it live.'],
themes: [
{ id: 'obsidian', name: '🌑 Obsidian', sub: 'Material Dark · Graphite & Bitcoin Gold (default)', colors: ['#0e1117', '#171c26', '#f2a900', '#3ecf8e'] },
{ id: 'ocean', name: '🌊 Material Ocean', sub: 'Material You Dark · Deep Blue & Electric Cyan', colors: ['#0b111e', '#131c31', '#38bdf8', '#34d399'] },
{ id: 'neon', name: '⚡ Material Neon', sub: 'Material Dark · Graphite & Neon Green', colors: ['#0a111b', '#20272e', '#36cf8c', '#52eeaf'] },
{ id: 'gold', name: '🟡 Black & Yellow', sub: 'Exchange style · Black & Yellow', colors: ['#0b0e11', '#181a20', '#fcd535', '#0ecb81'] },
{ id: 'latte', name: '☀️ Material Solar', sub: 'Material 3 Light · Alabaster & Amber', colors: ['#f5f3ee', '#ffffff', '#d97706', '#16a34a'] },
],
LANGUAGE: ['Default language', 'For the dashboard, cube and bot. The PT | EN button still works per browser.'],
Expand Down Expand Up @@ -839,6 +841,7 @@ function page(lang, hasPin, theme) {
:root, body[data-theme="obsidian"] { --bg:#0e1117; --card:#171c26; --line:#252c3a; --txt:#e6e9ef; --dim:#8a93a6; --gold:#f2a900; --on-gold:#14161c; --gold-soft:rgba(242,169,0,.09); --ok:#3ecf8e; --warn:#e5534b; --shadow:none; --radius:12px; }
body[data-theme="ocean"] { --bg:#0b111e; --card:#131c31; --line:#223254; --txt:#e2e8f0; --dim:#8ea0c2; --gold:#38bdf8; --on-gold:#081221; --gold-soft:rgba(56,189,248,.11); --ok:#34d399; --warn:#fb7185; --shadow:0 4px 18px rgba(0,0,0,.28); --radius:16px; }
body[data-theme="neon"] { --bg:#0a111b; --card:#20272e; --line:#343d46; --txt:#eef2f5; --dim:#97a3ad; --gold:#36cf8c; --on-gold:#04140d; --gold-soft:rgba(54,207,140,.11); --ok:#52eeaf; --warn:#f87171; --shadow:0 4px 18px rgba(0,0,0,.3); --radius:14px; }
body[data-theme="gold"] { --bg:#0b0e11; --card:#181a20; --line:#2b3139; --txt:#eaecef; --dim:#848e9c; --gold:#fcd535; --on-gold:#181a20; --gold-soft:rgba(252,213,53,.1); --ok:#0ecb81; --warn:#f6465d; --shadow:none; --radius:8px; }
body[data-theme="latte"] { --bg:#f5f3ee; --card:#ffffff; --line:#e2ded5; --txt:#1c1b18; --dim:#666159; --gold:#d97706; --on-gold:#ffffff; --gold-soft:rgba(217,119,6,.10); --ok:#16a34a; --warn:#dc2626; --shadow:0 2px 10px rgba(28,27,24,.06); --radius:16px; }
* { box-sizing:border-box; margin:0; padding:0; }
body { background:var(--bg); color:var(--txt); font-family:-apple-system, "Segoe UI", Roboto, sans-serif; padding:18px 16px 32px; max-width:840px; margin:0 auto; transition:background .2s, color .2s; }
Expand All @@ -855,7 +858,7 @@ function page(lang, hasPin, theme) {
.tab.err { border-color:var(--warn); color:var(--warn); }
.tab.err .tdot { display:inline-block; background:var(--warn); box-shadow:0 0 6px rgba(229,83,75,.8); }
fieldset { background:var(--card); border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow); padding:18px 18px 10px; margin-bottom:14px; transition:background .2s, border-color .2s; }
.tcards { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:10px; margin-top:4px; } /* 4 temas: 2 por linha */
.tcards { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:10px; margin-top:4px; } /* 2 por linha */
@media (max-width:640px) { .tcards { grid-template-columns:1fr; } }
.tcard { background:var(--bg); color:var(--txt); border:1px solid var(--line); border-radius:12px; padding:12px; text-align:left; display:flex; flex-direction:column; gap:5px; cursor:pointer; transition:all .15s; font-weight:500; }
.tcard:hover { border-color:var(--gold); }
Expand Down
2 changes: 2 additions & 0 deletions tests/i18n.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ test('locale e moeda são injetados', () => {
assert.match(localizePage(PAGE, 'pt', 'BRL', 'ocean'), /<body data-theme="ocean">/);
assert.match(localizePage(PAGE, 'en', 'USD', 'latte'), /<body data-theme="latte">/);
assert.match(localizePage(PAGE, 'pt', 'BRL', 'neon'), /<body data-theme="neon">/);
assert.match(localizePage(PAGE, 'pt', 'BRL', 'gold'), /<body data-theme="gold">/);
assert.ok(PAGE.includes('body[data-theme="gold"]'), 'o tema amarelo tem cores no painel');
// tema padrão omitido e tema inválido que cai no fallback obsidian
assert.match(localizePage(PAGE, 'pt', 'BRL'), /<body data-theme="obsidian">/);
assert.match(localizePage(PAGE, 'pt', 'BRL', 'invalido'), /<body data-theme="obsidian">/);
Expand Down
3 changes: 2 additions & 1 deletion tests/settings.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ test('validação dos campos', () => {
assert.strictEqual(st.normTheme('ocean'), 'ocean');
assert.strictEqual(st.normTheme('latte'), 'latte');
assert.strictEqual(st.normTheme('neon'), 'neon');
assert.strictEqual(st.normTheme('gold'), 'gold');
assert.strictEqual(st.normTheme('invalido'), 'obsidian');
assert.deepStrictEqual(st.clean(field('ALARM_IGNORE'), ['macbook', 'macbook', ' tablet ']), { value: 'macbook,tablet' });
assert.ok(st.clean(field('ALARM_IGNORE'), ['a b']).error);
Expand Down Expand Up @@ -347,7 +348,7 @@ test('testCube, testTelegram, validateBackupData e rotas desconhecidas em settin
assert.match(st.page('pt', true, 'ocean'), /data-theme="ocean"/);
assert.match(st.page('en', true, 'latte'), /data-theme="latte"/);
// clicar num cartão de tema troca na hora: a lista de temas precisa ir escrita no script do navegador
assert.ok(st.page('pt', true, 'obsidian').includes('["obsidian","ocean","neon","latte"].includes(val)'));
assert.ok(st.page('pt', true, 'obsidian').includes('["obsidian","ocean","neon","gold","latte"].includes(val)'));

// 2. testTelegram
assert.deepStrictEqual(await st.testTelegram('invalido', '123'), { ok: false, error: 'missing' });
Expand Down
Loading