Files
DPM/src/main.jsx
T

89 lines
4.0 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import ErrorBoundary from "./components/ErrorBoundary";
import { ThemeProvider } from "@appica/ui-react/providers/theme-provider";
import { getApiBase } from "./config";
import { invoke } from "@tauri-apps/api/core";
import "./styles/global.css";
/* 全局致命错误捕获:任何模块加载/运行/异步错误 → 显示到页面(替代白屏),便于副屏等定位 */
function installFatalErrorDiagnostics() {
const show = (label, err) => {
const el = document.getElementById("fatal-error");
if (!el || el.style.display === "block") return;
const text = document.getElementById("fatal-error-text");
if (text) {
const msg = (err && (err.stack || err.message)) || String(err);
text.textContent = `${label}\n${msg}`;
}
el.style.display = "block";
};
window.addEventListener("error", (e) => show("加载错误:", e.error || e.message));
window.addEventListener("unhandledrejection", (e) => show("未处理异常:", e.reason));
}
installFatalErrorDiagnostics();
/* =========================================================
启动引导(打包部署关键)—— 地址解析优先级:
1. exe 旁 config.jsonTauri 命令读取,部署免重打包改 IP)★
2. 后端 GET /api/configMQTT 地址/账号,后端 .env 为准)
3. 内置默认(Tauri 打包回退 192.168.1.9 / 浏览器用当前主机)
结果写入 window.__DPM_*__api.js / mqtt.js 惰性读取生效。
========================================================= */
async function bootstrapRuntimeConfig() {
// 1) exe 旁 config.json(最高优先):{api_base, mqtt_url, mqtt_username, mqtt_password}
try {
const raw = await invoke("read_runtime_config");
if (raw) {
const cfg = JSON.parse(raw);
if (cfg.api_base) window.__DPM_API__ = cfg.api_base;
if (cfg.mqtt_url) window.__DPM_MQTT__ = cfg.mqtt_url;
if (cfg.mqtt_username) window.__DPM_MQTT_USER__ = cfg.mqtt_username;
if (cfg.mqtt_password) window.__DPM_MQTT_PASS__ = cfg.mqtt_password;
if (cfg.voice_url) window.__DPM_VOICE_WS__ = cfg.voice_url;
// eslint-disable-next-line no-console
if (cfg.mqtt_url) console.info(`[bootstrap] config.json MQTT -> ${cfg.mqtt_url}`);
}
} catch {
/* 浏览器开发环境或未注册命令:跳过 */
}
// AudioWorklet 兼容:打包环境(WebView2)无法从 tauri.localhost 自定义协议加载
// worklet 模块,统一改从后端真实 HTTP 服务加载(backend/static/voice-worklets/
if (!window.VOICE_WORKLETS_BASE) {
window.VOICE_WORKLETS_BASE = `${getApiBase()}/static/voice-worklets/`;
}
// 2) 后端 /api/configMQTT 地址/账号;API 基址以前端实际可达地址为准)
// 优先级保护:若构建期已用 .env.local 烘焙了 VITE_MQTT_URL
// 则后端 /api/config 不再覆盖(避免后端默认 localhost 冲掉正确地址)。
const bakedMqtt = import.meta.env.VITE_MQTT_URL;
try {
const res = await fetch(`${getApiBase()}/api/config`, { cache: 'no-store' });
if (res.ok) {
const cfg = await res.json();
if (!bakedMqtt && cfg.mqtt_url) window.__DPM_MQTT__ = cfg.mqtt_url;
if (!bakedMqtt && cfg.mqtt_username) window.__DPM_MQTT_USER__ = cfg.mqtt_username;
if (!bakedMqtt && cfg.mqtt_password) window.__DPM_MQTT_PASS__ = cfg.mqtt_password;
if (!import.meta.env.VITE_VOICE_WS && cfg.voice_url) window.__DPM_VOICE_WS__ = cfg.voice_url;
// eslint-disable-next-line no-console
if (cfg.mqtt_url) console.info(`[bootstrap] /api/config MQTT -> ${cfg.mqtt_url}${bakedMqtt ? '(已烘焙,忽略)' : ''}`);
}
} catch {
/* 后端不可达:使用内置默认地址 */
}
}
bootstrapRuntimeConfig().then(() => {
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<ErrorBoundary>
<ThemeProvider>
<App />
</ThemeProvider>
</ErrorBoundary>
</React.StrictMode>,
);
});