filebrowser/frontend/src/router/index.ts

173 lines
3.8 KiB
TypeScript

import { RouteLocation, createRouter, createWebHistory } from "vue-router";
import Login from "@/views/Login.vue";
import Layout from "@/views/Layout.vue";
import Files from "@/views/Files.vue";
import Share from "@/views/Share.vue";
import Settings from "@/views/Settings.vue";
import Errors from "@/views/Errors.vue";
import { baseURL, name } from "@/utils/constants";
import { getters, state } from "@/store";
import { mutations } from "@/store";
import { validateLogin } from "@/utils/auth";
import i18n from "@/i18n";
const titles = {
Login: "sidebar.login",
Share: "buttons.share",
Files: "files.files",
Settings: "sidebar.settings",
ProfileSettings: "settings.profileSettings",
Shares: "settings.shareManagement",
GlobalSettings: "settings.globalSettings",
Users: "settings.users",
User: "settings.user",
Forbidden: "errors.forbidden",
NotFound: "errors.notFound",
InternalServerError: "errors.internal",
};
const routes = [
{
path: "/login",
name: "Login",
component: Login,
},
{
path: "/share",
component: Layout,
children: [
{
path: ":path*",
name: "Share",
component: Share,
},
],
},
{
path: "/files",
component: Layout,
meta: {
requiresAuth: true,
},
children: [
{
path: ":path*",
name: "Files",
component: Files,
},
],
},
{
path: "/settings",
component: Layout,
meta: {
requiresAuth: true,
},
children: [
{
path: "",
name: "Settings",
component: Settings,
},
{
path: "users/:id",
name: "User",
component: Settings,
},
],
},
{
path: "/403",
name: "Forbidden",
component: Errors,
props: {
errorCode: 403,
showHeader: true,
},
},
{
path: "/404",
name: "NotFound",
component: Errors,
props: {
errorCode: 404,
showHeader: true,
},
},
{
path: "/500",
name: "InternalServerError",
component: Errors,
props: {
errorCode: 500,
showHeader: true,
},
},
{
path: "/:catchAll(.*)*",
redirect: (to: RouteLocation) => {
const path = Array.isArray(to.params.catchAll)
? to.params.catchAll.join("/")
: to.params.catchAll || "";
return `/files/${path}`;
},
},
];
const router = createRouter({
history: createWebHistory(baseURL),
routes,
});
// Helper function to check if a route resolves to itself
function isSameRoute(to: RouteLocation, from: RouteLocation) {
return to.path === from.path && JSON.stringify(to.params) === JSON.stringify(from.params);
}
router.beforeResolve(async (to, from, next) => {
if (isSameRoute(to, from)) {
console.warn("Avoiding recursive navigation to the same route.");
return next(false);
}
if (state != null && state.user != null && !('username' in state.user)) {
await validateLogin();
}
// Set the page title using i18n
const title = i18n.global.t(titles[to.name as keyof typeof titles]);
document.title = title + " - " + name;
// Update store with the current route
mutations.setRoute(to);
// Handle auth requirements
if (to.matched.some((record) => record.meta.requiresAuth)) {
if (!getters.isLoggedIn()) {
next({
path: "/login",
query: { redirect: to.fullPath },
});
return;
}
// Handle admin-only routes
if (to.matched.some((record) => record.meta.requiresAdmin)) {
if (!state.user || !getters.isAdmin()) {
next({ path: "/403" });
return;
}
}
}
// Redirect logged-in users from login page
if (to.path.endsWith("/login") && getters.isLoggedIn()) {
next({ path: "/files/" });
return;
}
next();
});
export { router, router as default };