fix(atlas): skip http:// health checks from https context (mixed content warning)

Vercel-hosted Atlas (HTTPS) was attempting HEAD requests to http:// internal
endpoints (Gitea, Syncthing), causing browsers to show insecure connection
warning. Now skips those requests silently; status stays at last known value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
posimai 2026-03-31 10:07:41 +09:00
parent dc602f7ea7
commit 0b9902b495
1 changed files with 20 additions and 9 deletions

View File

@ -2009,7 +2009,10 @@ async function runHealthCheckAll() {
} }
} else { } else {
const result = await checkUrlHealth(node.url); const result = await checkUrlHealth(node.url);
next = result === 'offline' ? 'inactive' : 'active'; // 'skipped' = HTTP endpoint from HTTPS context — leave status unchanged
if (result !== 'skipped') {
next = result === 'offline' ? 'inactive' : 'active';
}
nodeHealthCache.delete(node.id); nodeHealthCache.delete(node.id);
} }
@ -2036,6 +2039,9 @@ async function runHealthCheckAll() {
} }
async function checkUrlHealth(url) { async function checkUrlHealth(url) {
// HTTPS ページから HTTP エンドポイントへのリクエストは混合コンテンツとしてブロックされる
// → ブラウザが「保護されていない通信」警告を出すため、スキップして 'skipped' を返す
if (location.protocol === 'https:' && url.startsWith('http:')) return 'skipped';
try { try {
const ctrl = new AbortController(); const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), 7000); const timer = setTimeout(() => ctrl.abort(), 7000);
@ -2518,14 +2524,19 @@ async function checkNodeHealth(nodeId, url) {
} }
} else { } else {
// /api/health なし → 従来の HEAD リクエスト // /api/health なし → 従来の HEAD リクエスト
try { // HTTP endpoint from HTTPS context はブロックされるのでスキップ
const ctrl = new AbortController(); if (location.protocol === 'https:' && url && url.startsWith('http:')) {
const timer = setTimeout(() => ctrl.abort(), 6000); result = 'limited';
const res = await fetch(url, { method: 'HEAD', mode: 'no-cors', signal: ctrl.signal }); } else {
clearTimeout(timer); try {
result = res.type === 'opaque' ? 'limited' : (res.ok ? 'online' : 'offline'); const ctrl = new AbortController();
} catch (e) { const timer = setTimeout(() => ctrl.abort(), 6000);
result = 'offline'; const res = await fetch(url, { method: 'HEAD', mode: 'no-cors', signal: ctrl.signal });
clearTimeout(timer);
result = res.type === 'opaque' ? 'limited' : (res.ok ? 'online' : 'offline');
} catch (e) {
result = 'offline';
}
} }
} }