Newer
Older
smart-home-server / webclient / src / router / index.js
import { createRouter, createWebHashHistory } from "vue-router";
import { routes } from "./routes";
import { useAuthStore } from "../stores/auth.js";

const LOG_PREFIX = "[vue:router]";
const LOG_ENABLED = import.meta.env.VITE_LOG_LEVEL === "DEBUG";

export const router = createRouter({
  history: createWebHashHistory(),
  routes,
});

router.beforeEach(async (to, from, next) => {
  const authStore = useAuthStore();

  if (LOG_ENABLED) {
    console.debug(LOG_PREFIX, `Navigate: ${from.fullPath || "init"} → ${to.fullPath}`);
  }

  // Ensure auth initialization completes before any routing decision.
  // init() is cached: repeated calls are no-ops after the first resolution.
  await authStore.init();

  // Allow public routes unconditionally
  if (to.meta?.public) {
    if (to.name === "login" && authStore.isAuthenticated) {
      if (LOG_ENABLED) {
        console.debug(LOG_PREFIX, `Redirect: authenticated user at login → areas-favorites`);
      }
      next({ name: "areas-favorites" });
      return;
    }
    next();
    return;
  }

  // Require authentication
  if (!authStore.isAuthenticated) {
    if (LOG_ENABLED) {
      console.debug(LOG_PREFIX, `Redirect: unauthenticated → login`);
    }
    next({ name: "login" });
    return;
  }

  // Check route-level permission
  const required = to.meta?.permission;
  if (required && !authStore.hasPermission(required)) {
    if (LOG_ENABLED) {
      console.warn(LOG_PREFIX, `Forbidden: ${to.fullPath} requires ${required}`);
    }
    next({ name: "areas-favorites" });
    return;
  }

  next();
});