Implement HTTPS redirection for certificate trust issues in LuxsinAPI and update image paths in Home component to use BASE_URL for consistency.

This commit is contained in:
yangy
2026-04-21 16:10:12 +08:00
parent 060258ea2d
commit 24278c263f
2 changed files with 26 additions and 7 deletions
+21 -4
View File
@@ -188,11 +188,28 @@ export class LuxsinAPI {
this.baseUrl = `${protocol}://${host}`; this.baseUrl = `${protocol}://${host}`;
} }
private maybeRedirectForHttpsCert(error: unknown) {
// On HTTPS pages, first-time device access may fail before user trusts the cert.
if (typeof window === "undefined") return;
if (window.location.protocol !== "https:") return;
if (!(error instanceof Error)) return;
// fetch network failure is typically reported as TypeError/Failed to fetch.
const msg = (error.message || "").toLowerCase();
if (error.name === "TypeError" || msg.includes("failed to fetch") || msg.includes("networkerror")) {
window.location.href = this.baseUrl;
}
}
async getDeviceState(): Promise<DeviceState> { async getDeviceState(): Promise<DeviceState> {
const response = await fetch(`${this.baseUrl}/dev/info.cgi?action=syncData`); try {
const text = await response.text(); const response = await fetch(`${this.baseUrl}/dev/info.cgi?action=syncData`);
const decoded = decodeCustomBase64(text.trim()); const text = await response.text();
return JSON.parse(decoded) as DeviceState; const decoded = decodeCustomBase64(text.trim());
return JSON.parse(decoded) as DeviceState;
} catch (error) {
this.maybeRedirectForHttpsCert(error);
throw error;
}
} }
async getMsgCount(): Promise<number> { async getMsgCount(): Promise<number> {
+5 -3
View File
@@ -395,14 +395,16 @@ export default function Home() {
onClick={() => updateSetting({ bt_next: 0 })} onClick={() => updateSetting({ bt_next: 0 })}
className="w-10 h-10 rounded-full flex items-center justify-center transition-all active:scale-95" className="w-10 h-10 rounded-full flex items-center justify-center transition-all active:scale-95"
style={{ background: "rgba(0,255,246,0.15)", border: "1px solid rgba(0,255,246,0.3)" }}> style={{ background: "rgba(0,255,246,0.15)", border: "1px solid rgba(0,255,246,0.3)" }}>
<img src="/player/skip-forward.png" alt="上一首" className="w-5 h-5" /> <img src={`${import.meta.env.BASE_URL}player/skip-forward.png`} alt="上一首" className="w-5 h-5" />
</button> </button>
<button <button
onClick={() => updateSetting({ bt_play: 1 })} onClick={() => updateSetting({ bt_play: 1 })}
className="w-12 h-12 rounded-full flex items-center justify-center transition-all active:scale-95" className="w-12 h-12 rounded-full flex items-center justify-center transition-all active:scale-95"
style={{ background: "transparent", border: "1px solid rgba(0,255,246,0.5)", boxShadow: "0 0 12px rgba(0,255,246,0.4)" }}> style={{ background: "transparent", border: "1px solid rgba(0,255,246,0.5)", boxShadow: "0 0 12px rgba(0,255,246,0.4)" }}>
<img <img
src={ds.bt_status === 1 ? "/player/play.png" : "/player/pause.png"} src={ds.bt_status === 1
? `${import.meta.env.BASE_URL}player/play.png`
: `${import.meta.env.BASE_URL}player/pause.png`}
alt={ds.bt_status === 1 ? "播放" : "暂停"} alt={ds.bt_status === 1 ? "播放" : "暂停"}
className="w-6 h-6" className="w-6 h-6"
/> />
@@ -411,7 +413,7 @@ export default function Home() {
onClick={() => updateSetting({ bt_next: 1 })} onClick={() => updateSetting({ bt_next: 1 })}
className="w-10 h-10 rounded-full flex items-center justify-center transition-all active:scale-95" className="w-10 h-10 rounded-full flex items-center justify-center transition-all active:scale-95"
style={{ background: "rgba(0,255,246,0.15)", border: "1px solid rgba(0,255,246,0.3)" }}> style={{ background: "rgba(0,255,246,0.15)", border: "1px solid rgba(0,255,246,0.3)" }}>
<img src="/player/skip-back.png" alt="下一首" className="w-5 h-5" /> <img src={`${import.meta.env.BASE_URL}player/skip-back.png`} alt="下一首" className="w-5 h-5" />
</button> </button>
</div> </div>
</div> </div>