Newer
Older
gn-synapse / frontend / src / router.js
import { createRouter, createWebHistory } from "vue-router";
import { clearToken, getToken, getSession } from "./api.js";
import LoginView from "./views/LoginView.vue";
import DashboardView from "./views/DashboardView.vue";
import DeniedView from "./views/DeniedView.vue";
import MyEventsView from "./views/MyEventsView.vue";
import OauthCallbackView from "./views/OauthCallbackView.vue";
import SourcesView from "./views/SourcesView.vue";
import SourceDetailView from "./views/SourceDetailView.vue";
import TypesView from "./views/TypesView.vue";
import TargetsView from "./views/TargetsView.vue";
import TargetDetailView from "./views/TargetDetailView.vue";
import RulesView from "./views/RulesView.vue";
import RuleDetailView from "./views/RuleDetailView.vue";
import DeliveriesView from "./views/DeliveriesView.vue";
import SettingsView from "./views/SettingsView.vue";

// Гейт ролей. Сервер отсекает админ-API (require_admin -> 403); фронт заранее
// разводит путь: роль из GET /api/v1/me (см. getSession в api.js). Админ —
// вся админка; пользователь — только личный раздел «Мои события»; вход в
// приложения — любой аутентифицированный gnexus-auth.
const router = createRouter({
  history: createWebHistory(),
  routes: [
    // Личный раздел доступен любой роли ("/" и незнакомые пути разводятся
    // в beforeEach по system_role: админ — в админку, user — в личный раздел).
    // bare — страницы без каркаса GnNavigationShell (см. App.vue)
    { path: "/login", component: LoginView, meta: { bare: true } },
    { path: "/denied", component: DeniedView, meta: { bare: true } },
    { path: "/oauth/callback", component: OauthCallbackView, meta: { bare: true } },
    // Личный раздел — любая роль; админские пути — requiresAdmin (гейт ниже).
    // /settings — один раздел для всех: личные пуш-настройки, админу ещё конфиги.
    { path: "/my-events", component: MyEventsView, meta: { userOk: true } },
    { path: "/settings", component: SettingsView, meta: { userOk: true } },
    {
      path: "/dashboard",
      component: DashboardView,
      meta: { requiresAdmin: true },
    },
    { path: "/sources", component: SourcesView, meta: { requiresAdmin: true } },
    { path: "/sources/:id", component: SourceDetailView, meta: { requiresAdmin: true } },
    { path: "/types", component: TypesView, meta: { requiresAdmin: true } },
    { path: "/targets", component: TargetsView, meta: { requiresAdmin: true } },
    { path: "/targets/:id", component: TargetDetailView, meta: { requiresAdmin: true } },
    { path: "/rules", component: RulesView, meta: { requiresAdmin: true } },
    { path: "/rules/:id", component: RuleDetailView, meta: { requiresAdmin: true } },
    { path: "/deliveries", component: DeliveriesView, meta: { requiresAdmin: true } },
  ],
});

router.beforeEach(async (to) => {
  if (to.meta.bare) return true;
  if (!getToken()) return { path: "/login", query: { next: to.fullPath } };
  let session;
  try {
    session = await getSession();
  } catch {
    // 401 (не валидируется/истёк даже после refresh) — сессии нет.
    clearToken();
    return { path: "/login", query: { next: to.fullPath } };
  }
  const isAdmin =
    (session?.system_role || "") === "admin" || (session?.system_role || "") === "superadmin";
  if (to.path === "/") return isAdmin ? "/dashboard" : "/my-events";
  // Админские пути — admin+; роли user путь подменяется на личный раздел
  // (сервер всё равно держит админ-API под 403, это двойная защита).
  if (!to.meta.userOk && !isAdmin) return "/my-events";
  return true;
});

export { router };