Newer
Older
smart-home-server / webclient / src / app / main.js
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
import { router } from "../router";
import { useGlobalErrorHandler } from "../composables/useGlobalErrorHandler";
import { useAuthStore } from "../stores/auth.js";
import { isDebugEnabled, safeStringify } from "../utils/logger";
import { initServerUrl, hasServerUrl, isNativeApp } from "../api/server-config";
import "@phosphor-icons/web/regular";
import "@phosphor-icons/web/fill";
import "../styles/main.css";

const LOG_PREFIX = "[vue:pinia]";

// Pinia store action logger plugin
const storeLogger = (context) => {
  if (!isDebugEnabled()) return;

  const { store, options } = context;
  const storeName = options.id;

  // Wrap actions
  const actionNames = Object.keys(options.actions || {});
  for (const actionName of actionNames) {
    const originalAction = store[actionName];
    store[actionName] = async function (...args) {
      console.debug(LOG_PREFIX, `${storeName}.${actionName}(${args.map(a => safeStringify(a)).join(", ")})`);
      const start = performance.now();
      try {
        const result = await originalAction.apply(this, args);
        console.debug(LOG_PREFIX, `${storeName}.${actionName} completed in ${(performance.now() - start).toFixed(1)}ms`);
        return result;
      } catch (err) {
        console.error(LOG_PREFIX, `${storeName}.${actionName} failed: ${err?.message || err}`);
        throw err;
      }
    };
  }
};

async function bootstrap() {
  // 1. Load native server URL first (no-op on web)
  await initServerUrl();

  // 2. Create app
  const app = createApp(App);
  useGlobalErrorHandler(app);

  const pinia = createPinia();
  pinia.use(storeLogger);
  app.use(pinia).use(router);

  // 3. If native app has no server configured, redirect to setup page
  if (isNativeApp()) {
    const configured = await hasServerUrl();
    if (!configured) {
      await router.replace("/mobile-setup");
    }
  }

  // 4. Initialize auth before mounting so router guards have valid state
  const authStore = useAuthStore();
  await authStore.init();

  // 5. Mount
  app.mount("#app");
}

bootstrap();