From 15b87f372210b80f1ec2be45f72db5ed16fe67f9 Mon Sep 17 00:00:00 2001 From: posimai Date: Tue, 31 Mar 2026 21:24:13 +0900 Subject: [PATCH] fix: proxy HTTP health checks via server to avoid mixed-content block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add /api/check?url= endpoint to server.js for server-side HTTP checks - Gitea and Syncthing use proxy:true to route through this endpoint - Fixes Gitea/Syncthing showing DOWN due to https→http mixed content Co-Authored-By: Claude Sonnet 4.6 --- posimai-dev/server.js | 18 ++++++++++++++++++ posimai-dev/station.html | 26 +++++++++++++++++++------- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/posimai-dev/server.js b/posimai-dev/server.js index 8a682598..0a2e2f1a 100644 --- a/posimai-dev/server.js +++ b/posimai-dev/server.js @@ -100,6 +100,24 @@ app.get('/api/health', (req, res) => { }, 100); }); +// ── サービス死活チェックプロキシ (/api/check?url=...) ────────── +// ブラウザの mixed-content 制限を回避するためサーバー側から HTTP チェック +app.get('/api/check', async (req, res) => { + res.setHeader('Access-Control-Allow-Origin', '*'); + const { url } = req.query; + if (!url) return res.status(400).json({ ok: false, error: 'url required' }); + const t0 = Date.now(); + try { + const ctrl = new AbortController(); + const timer = setTimeout(() => ctrl.abort(), 5000); + const r = await fetch(url, { method: 'HEAD', signal: ctrl.signal }); + clearTimeout(timer); + res.json({ ok: true, status: r.status, latency_ms: Date.now() - t0 }); + } catch (e) { + res.json({ ok: false, error: e.message, latency_ms: Date.now() - t0 }); + } +}); + // Tailscale証明書を自動検出 function findCert() { const home = os.homedir(); diff --git a/posimai-dev/station.html b/posimai-dev/station.html index 4fbb10de..f04105d9 100644 --- a/posimai-dev/station.html +++ b/posimai-dev/station.html @@ -311,8 +311,8 @@ const HISTORY_MAX = 20; const SERVICES = [ {id:'posimai-dev',name:'posimai-dev', desc:'ブラウザターミナル + Claude Code',url:HEALTH_URL, isHealth:true}, {id:'posimai-api',name:'Posimai API',desc:'Node.js / Express — VPS 本番', url:'https://api.soar-enrich.com', isHealth:false}, - {id:'gitea', name:'Gitea', desc:'ローカル Git バックアップ', url:'http://100.76.7.3:3000', isHealth:false}, - {id:'syncthing', name:'Syncthing', desc:'ファイル同期 GUI', url:'http://100.77.11.43:8384', isHealth:false}, + {id:'gitea', name:'Gitea', desc:'ローカル Git バックアップ', url:'/api/check?url=http://100.76.7.3:3000', isHealth:false, proxy:true}, + {id:'syncthing', name:'Syncthing', desc:'ファイル同期 GUI', url:'/api/check?url=http://100.77.11.43:8384', isHealth:false, proxy:true}, {id:'vercel', name:'Vercel', desc:'PWA ホスティング (27本)', url:'https://vercel.com', isHealth:false}, {id:'github', name:'GitHub', desc:'ソースコード管理', url:'https://github.com/posimai', isHealth:false}, ]; @@ -461,11 +461,23 @@ async function checkService(svc){ const t0=Date.now(); try{ const ctrl=new AbortController(),timer=setTimeout(()=>ctrl.abort(),7000); - await fetch(svc.url,{method:'HEAD',mode:'no-cors',signal:ctrl.signal}); - clearTimeout(timer); - badge.className='service-badge ok'; badge.textContent='OK'; - latEl.textContent=`${Date.now()-t0}ms`; - pushSvcHistory(svc.id,true); + if(svc.proxy){ + // サーバー経由プロキシチェック(mixed-content 回避) + const r=await fetch(svc.url,{signal:ctrl.signal}); + clearTimeout(timer); + const data=await r.json(); + const ok=data.ok||(data.status&&data.status<500); + badge.className='service-badge '+(ok?'ok':'crit'); + badge.textContent=ok?'OK':'DOWN'; + latEl.textContent=data.latency_ms?`${data.latency_ms}ms`:''; + pushSvcHistory(svc.id,!!ok); + }else{ + await fetch(svc.url,{method:'HEAD',mode:'no-cors',signal:ctrl.signal}); + clearTimeout(timer); + badge.className='service-badge ok'; badge.textContent='OK'; + latEl.textContent=`${Date.now()-t0}ms`; + pushSvcHistory(svc.id,true); + } }catch(e){ badge.className='service-badge crit'; badge.textContent='DOWN'; latEl.textContent=''; pushSvcHistory(svc.id,false);