feat: add tenant and user management with migrations, handlers, and tests
- Implemented tenant and user management features including: - Tenant creation and management with associated migrations. - User creation and management with associated migrations. - Tenant membership management with associated migrations. - Platform user roles management with associated migrations. - Quota management with associated migrations. - Article and template management with associated migrations. - Added HTTP handlers for templates and workspaces. - Created tests for protected and public routes. - Introduced a script to check tenant scope in SQL queries. - Documented task plan for backend completion and frontend foundation.
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
|
||||
import { pinia } from "@/stores/pinia";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: "/login",
|
||||
name: "login",
|
||||
component: () => import("@/views/LoginView.vue"),
|
||||
meta: {
|
||||
titleKey: "route.login.title",
|
||||
descriptionKey: "route.login.description",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
component: () => import("@/layouts/AppShell.vue"),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
redirect: "/workspace",
|
||||
},
|
||||
{
|
||||
path: "workspace",
|
||||
name: "workspace",
|
||||
component: () => import("@/views/WorkspaceView.vue"),
|
||||
meta: {
|
||||
titleKey: "route.workspace.title",
|
||||
descriptionKey: "route.workspace.description",
|
||||
navKey: "/workspace",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "articles/templates",
|
||||
name: "articles-templates",
|
||||
component: () => import("@/views/TemplatesView.vue"),
|
||||
meta: {
|
||||
titleKey: "route.templates.title",
|
||||
descriptionKey: "route.templates.description",
|
||||
navKey: "/articles/templates",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "articles/wizard",
|
||||
name: "article-wizard",
|
||||
component: () => import("@/views/TemplateWizardView.vue"),
|
||||
meta: {
|
||||
titleKey: "route.wizard.title",
|
||||
descriptionKey: "route.wizard.description",
|
||||
navKey: "/articles/templates",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "articles/custom",
|
||||
name: "articles-custom",
|
||||
component: () => import("@/views/FeatureStubView.vue"),
|
||||
meta: {
|
||||
titleKey: "route.custom.title",
|
||||
descriptionKey: "route.custom.description",
|
||||
navKey: "/articles/custom",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "articles/optimize",
|
||||
name: "articles-optimize",
|
||||
component: () => import("@/views/FeatureStubView.vue"),
|
||||
meta: {
|
||||
titleKey: "route.optimize.title",
|
||||
descriptionKey: "route.optimize.description",
|
||||
navKey: "/articles/optimize",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "media",
|
||||
name: "media",
|
||||
component: () => import("@/views/FeatureStubView.vue"),
|
||||
meta: {
|
||||
titleKey: "route.media.title",
|
||||
descriptionKey: "route.media.description",
|
||||
navKey: "/media",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "brands",
|
||||
name: "brands",
|
||||
component: () => import("@/views/BrandsView.vue"),
|
||||
meta: {
|
||||
titleKey: "route.brands.title",
|
||||
descriptionKey: "route.brands.description",
|
||||
navKey: "/brands",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "tracking",
|
||||
name: "tracking",
|
||||
component: () => import("@/views/FeatureStubView.vue"),
|
||||
meta: {
|
||||
titleKey: "route.tracking.title",
|
||||
descriptionKey: "route.tracking.description",
|
||||
navKey: "/tracking",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "knowledge",
|
||||
name: "knowledge",
|
||||
component: () => import("@/views/FeatureStubView.vue"),
|
||||
meta: {
|
||||
titleKey: "route.knowledge.title",
|
||||
descriptionKey: "route.knowledge.description",
|
||||
navKey: "/knowledge",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
router.beforeEach(async (to) => {
|
||||
const authStore = useAuthStore(pinia);
|
||||
|
||||
if (!authStore.initialized) {
|
||||
await authStore.bootstrap();
|
||||
}
|
||||
|
||||
const requiresAuth = to.matched.some((record) => record.meta.requiresAuth);
|
||||
if (requiresAuth && !authStore.isAuthenticated) {
|
||||
return {
|
||||
name: "login",
|
||||
query: { redirect: to.fullPath },
|
||||
};
|
||||
}
|
||||
|
||||
if (to.name === "login" && authStore.isAuthenticated) {
|
||||
return { name: "workspace" };
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
export { router };
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
import "vue-router";
|
||||
|
||||
declare module "vue-router" {
|
||||
interface RouteMeta {
|
||||
title?: string;
|
||||
description?: string;
|
||||
titleKey?: string;
|
||||
descriptionKey?: string;
|
||||
requiresAuth?: boolean;
|
||||
navKey?: string;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
Reference in New Issue
Block a user