feat(ble): envia diag log direto pro servidor (evita crash WebView) v1.10.9
Some checks are pending
Build Android (APK + AAB) / build-android (push) Waiting to run
Some checks are pending
Build Android (APK + AAB) / build-android (push) Waiting to run
Karlão reportou que app reinicia ao tentar copiar log (crash em
v1.10.8 também — modal grande + textarea com Unicode pode crashar
WebView Android skinned).
Solução: novo endpoint POST /api/bms/diag-log que aceita texto
do log + auth user, salva em /data/diag-logs/{userId}-{ts}.txt
no servidor. Frontend tem botão '📤 Enviar pro servidor' (verde)
que faz fetch POST. Resposta confirma recebimento.
Eu (Claude) leio o arquivo direto do servidor — sem dependência
de clipboard, share, modal HTML, ou copy manual.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f3183b33d1
commit
9f32428980
5 changed files with 59 additions and 14 deletions
|
|
@ -1979,9 +1979,9 @@ Marque zonas de proibição (alarme alto) ou atenção (aviso suave). Detecção
|
||||||
<div id="bt-list" style="margin-top:14px"></div>
|
<div id="bt-list" style="margin-top:14px"></div>
|
||||||
<details style="margin-top:10px" open>
|
<details style="margin-top:10px" open>
|
||||||
<summary style="cursor:pointer;font-family:var(--f-mono);font-size:11px;color:var(--m-text-soft,#7d97ad);letter-spacing:.06em">📋 Diagnóstico (logs do pareamento)</summary>
|
<summary style="cursor:pointer;font-family:var(--f-mono);font-size:11px;color:var(--m-text-soft,#7d97ad);letter-spacing:.06em">📋 Diagnóstico (logs do pareamento)</summary>
|
||||||
<div style="display:flex;gap:6px;margin-top:8px">
|
<div style="display:flex;gap:6px;margin-top:8px;flex-wrap:wrap">
|
||||||
<button class="btn btn-sm" onclick="copyDiagLog()" style="flex:1">📋 Copiar log</button>
|
<button class="btn btn-sm btn-primary" onclick="sendDiagLogToServer()" style="flex:1;min-width:140px">📤 Enviar pro servidor</button>
|
||||||
<button class="btn btn-sm" onclick="document.getElementById('bt-diag').innerHTML='';setBleDiag('Log limpo','info')" style="flex:1">🗑 Limpar</button>
|
<button class="btn btn-sm" onclick="document.getElementById('bt-diag').innerHTML='';setBleDiag('Log limpo','info')" style="flex:1;min-width:80px">🗑 Limpar</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="bt-diag" style="background:var(--m-bg-2,#0f2a40);border:1px solid var(--m-border,rgba(255,255,255,.08));border-radius:6px;padding:10px;margin-top:6px;max-height:300px;overflow-y:auto;font-family:var(--f-mono);font-size:11px"></div>
|
<div id="bt-diag" style="background:var(--m-bg-2,#0f2a40);border:1px solid var(--m-border,rgba(255,255,255,.08));border-radius:6px;padding:10px;margin-top:6px;max-height:300px;overflow-y:auto;font-family:var(--f-mono);font-size:11px"></div>
|
||||||
</details>
|
</details>
|
||||||
|
|
@ -5794,8 +5794,22 @@ async function ensureBleNativeReady(){
|
||||||
_bleNativeInitialized=true;
|
_bleNativeInitialized=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Envia log diagnóstico direto pro servidor (evita crash de clipboard/share em WebView)
|
||||||
|
async function sendDiagLogToServer(){
|
||||||
|
const el=document.getElementById('bt-diag');
|
||||||
|
if(!el){toast('Log vazio');return}
|
||||||
|
const txt=`Shivao v${APP_VERSION} · log diagnóstico\n\n`+(el.innerText||el.textContent||'').trim();
|
||||||
|
if(!cloudConfigured()){toast('Faça login primeiro pra enviar log');return}
|
||||||
|
toast('📤 Enviando log pro servidor...');
|
||||||
|
try{
|
||||||
|
const r=await cloudFetch('/api/bms/diag-log',{method:'POST',body:JSON.stringify({log:txt})});
|
||||||
|
const j=await r.json();
|
||||||
|
if(j.ok)toast('✓ Log enviado · '+(j.file||'OK'));
|
||||||
|
else throw new Error(j.error||'falhou');
|
||||||
|
}catch(e){toast('Erro: '+e.message)}
|
||||||
|
}
|
||||||
|
|
||||||
// Mostra log num modal pro usuário copiar/compartilhar manualmente
|
// Mostra log num modal pro usuário copiar/compartilhar manualmente
|
||||||
// (navigator.clipboard crasha em alguns WebViews Capacitor / Samsung One UI)
|
|
||||||
function copyDiagLog(){
|
function copyDiagLog(){
|
||||||
const el=document.getElementById('bt-diag');
|
const el=document.getElementById('bt-diag');
|
||||||
if(!el){toast('Log vazio');return}
|
if(!el){toast('Log vazio');return}
|
||||||
|
|
@ -6329,7 +6343,7 @@ async function removeBluetoothDevice(id){
|
||||||
renderBluetoothCard();
|
renderBluetoothCard();
|
||||||
}
|
}
|
||||||
|
|
||||||
const APP_VERSION='1.10.8';
|
const APP_VERSION='1.10.9';
|
||||||
function renderBluetoothCard(){
|
function renderBluetoothCard(){
|
||||||
const el=document.getElementById('bt-list');
|
const el=document.getElementById('bt-list');
|
||||||
const supportEl=document.getElementById('bt-support');
|
const supportEl=document.getElementById('bt-support');
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ android {
|
||||||
applicationId "br.com.pontualtech.shivao"
|
applicationId "br.com.pontualtech.shivao"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 24
|
versionCode 25
|
||||||
versionName "1.10.8"
|
versionName "1.10.9"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "shivao-mobile",
|
"name": "shivao-mobile",
|
||||||
"version": "1.10.8",
|
"version": "1.10.9",
|
||||||
"description": "Shivao app nativo (Capacitor wrapper Android/iOS)",
|
"description": "Shivao app nativo (Capacitor wrapper Android/iOS)",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
|
||||||
|
|
@ -1979,9 +1979,9 @@ Marque zonas de proibição (alarme alto) ou atenção (aviso suave). Detecção
|
||||||
<div id="bt-list" style="margin-top:14px"></div>
|
<div id="bt-list" style="margin-top:14px"></div>
|
||||||
<details style="margin-top:10px" open>
|
<details style="margin-top:10px" open>
|
||||||
<summary style="cursor:pointer;font-family:var(--f-mono);font-size:11px;color:var(--m-text-soft,#7d97ad);letter-spacing:.06em">📋 Diagnóstico (logs do pareamento)</summary>
|
<summary style="cursor:pointer;font-family:var(--f-mono);font-size:11px;color:var(--m-text-soft,#7d97ad);letter-spacing:.06em">📋 Diagnóstico (logs do pareamento)</summary>
|
||||||
<div style="display:flex;gap:6px;margin-top:8px">
|
<div style="display:flex;gap:6px;margin-top:8px;flex-wrap:wrap">
|
||||||
<button class="btn btn-sm" onclick="copyDiagLog()" style="flex:1">📋 Copiar log</button>
|
<button class="btn btn-sm btn-primary" onclick="sendDiagLogToServer()" style="flex:1;min-width:140px">📤 Enviar pro servidor</button>
|
||||||
<button class="btn btn-sm" onclick="document.getElementById('bt-diag').innerHTML='';setBleDiag('Log limpo','info')" style="flex:1">🗑 Limpar</button>
|
<button class="btn btn-sm" onclick="document.getElementById('bt-diag').innerHTML='';setBleDiag('Log limpo','info')" style="flex:1;min-width:80px">🗑 Limpar</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="bt-diag" style="background:var(--m-bg-2,#0f2a40);border:1px solid var(--m-border,rgba(255,255,255,.08));border-radius:6px;padding:10px;margin-top:6px;max-height:300px;overflow-y:auto;font-family:var(--f-mono);font-size:11px"></div>
|
<div id="bt-diag" style="background:var(--m-bg-2,#0f2a40);border:1px solid var(--m-border,rgba(255,255,255,.08));border-radius:6px;padding:10px;margin-top:6px;max-height:300px;overflow-y:auto;font-family:var(--f-mono);font-size:11px"></div>
|
||||||
</details>
|
</details>
|
||||||
|
|
@ -5794,8 +5794,22 @@ async function ensureBleNativeReady(){
|
||||||
_bleNativeInitialized=true;
|
_bleNativeInitialized=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Envia log diagnóstico direto pro servidor (evita crash de clipboard/share em WebView)
|
||||||
|
async function sendDiagLogToServer(){
|
||||||
|
const el=document.getElementById('bt-diag');
|
||||||
|
if(!el){toast('Log vazio');return}
|
||||||
|
const txt=`Shivao v${APP_VERSION} · log diagnóstico\n\n`+(el.innerText||el.textContent||'').trim();
|
||||||
|
if(!cloudConfigured()){toast('Faça login primeiro pra enviar log');return}
|
||||||
|
toast('📤 Enviando log pro servidor...');
|
||||||
|
try{
|
||||||
|
const r=await cloudFetch('/api/bms/diag-log',{method:'POST',body:JSON.stringify({log:txt})});
|
||||||
|
const j=await r.json();
|
||||||
|
if(j.ok)toast('✓ Log enviado · '+(j.file||'OK'));
|
||||||
|
else throw new Error(j.error||'falhou');
|
||||||
|
}catch(e){toast('Erro: '+e.message)}
|
||||||
|
}
|
||||||
|
|
||||||
// Mostra log num modal pro usuário copiar/compartilhar manualmente
|
// Mostra log num modal pro usuário copiar/compartilhar manualmente
|
||||||
// (navigator.clipboard crasha em alguns WebViews Capacitor / Samsung One UI)
|
|
||||||
function copyDiagLog(){
|
function copyDiagLog(){
|
||||||
const el=document.getElementById('bt-diag');
|
const el=document.getElementById('bt-diag');
|
||||||
if(!el){toast('Log vazio');return}
|
if(!el){toast('Log vazio');return}
|
||||||
|
|
@ -6329,7 +6343,7 @@ async function removeBluetoothDevice(id){
|
||||||
renderBluetoothCard();
|
renderBluetoothCard();
|
||||||
}
|
}
|
||||||
|
|
||||||
const APP_VERSION='1.10.8';
|
const APP_VERSION='1.10.9';
|
||||||
function renderBluetoothCard(){
|
function renderBluetoothCard(){
|
||||||
const el=document.getElementById('bt-list');
|
const el=document.getElementById('bt-list');
|
||||||
const supportEl=document.getElementById('bt-support');
|
const supportEl=document.getElementById('bt-support');
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,23 @@ app.get('/api/auth/me', requireAuth, (req, res) => {
|
||||||
res.json(req.user);
|
res.json(req.user);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Diagnostic log endpoint — recebe log do BLE pra debugar
|
||||||
|
app.post('/api/bms/diag-log', requireAuth, (req, res) => {
|
||||||
|
const { log } = req.body || {};
|
||||||
|
if (!log || typeof log !== 'string') return res.status(400).json({ error: 'log string required' });
|
||||||
|
const dir = path.join(db.dataDir, 'diag-logs');
|
||||||
|
try { fs.mkdirSync(dir, { recursive: true }); } catch {}
|
||||||
|
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
||||||
|
const file = path.join(dir, `${req.user.id}-${ts}.txt`);
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(file, log.slice(0, 50000)); // cap em 50KB
|
||||||
|
db.audit(req.user.id, 'bms_diag_log', 'bluetooth', null, { bytes: log.length, file: path.basename(file) }, req.ip);
|
||||||
|
res.json({ ok: true, file: path.basename(file) });
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).json({ error: e.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// ===== Google Login via OAuth redirect (pra apps Capacitor onde GSI popup não funciona) =====
|
// ===== Google Login via OAuth redirect (pra apps Capacitor onde GSI popup não funciona) =====
|
||||||
// In-memory store: session_id → { tokens, createdAt }. Auto-expira em 10min.
|
// In-memory store: session_id → { tokens, createdAt }. Auto-expira em 10min.
|
||||||
const pendingGoogleSessions = new Map();
|
const pendingGoogleSessions = new Map();
|
||||||
|
|
@ -347,7 +364,7 @@ app.get('/.well-known/assetlinks.json', (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Atalho: /apk redireciona pra última APK release no Forgejo
|
// Atalho: /apk redireciona pra última APK release no Forgejo
|
||||||
const LATEST_APK_URL = 'https://git.pontualtech.work/karlao/shivao-projeto/releases/download/v1.10.8/Shivao-v1.10.8.apk';
|
const LATEST_APK_URL = 'https://git.pontualtech.work/karlao/shivao-projeto/releases/download/v1.10.9/Shivao-v1.10.9.apk';
|
||||||
app.get('/apk', (req, res) => res.redirect(302, LATEST_APK_URL));
|
app.get('/apk', (req, res) => res.redirect(302, LATEST_APK_URL));
|
||||||
|
|
||||||
// Página A4 imprimível com QR Code + instruções (cola no barco/marina)
|
// Página A4 imprimível com QR Code + instruções (cola no barco/marina)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue