All checks were successful
Test Asgard Runner / test (push) Successful in 3s
Implements the complete frontend for BetterLoot profile management: - Pinia store (loot.ts) with CRUD, import/export, apply-to-server actions - LootBuilderView orchestrator with profile bar, modals, two-column layout - LootContainerSidebar with categorized container list, search, config indicators - LootItemEditor for per-container item settings and ungrouped item table - LootItemPicker modal with searchable/filterable Rust item grid - LootGroupEditor for reusable loot group management - Router integration at /loot-builder - Sidebar nav item with Crosshair icon and loot.view permission Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
357 lines
10 KiB
TypeScript
357 lines
10 KiB
TypeScript
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Domain detection — runs once at module load
|
|
// ---------------------------------------------------------------------------
|
|
const hostname = typeof window !== 'undefined' ? window.location.hostname : ''
|
|
const isMarketingDomain = hostname === 'corrosionmgmt.com'
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Marketing page children — shared between both domain route sets
|
|
// ---------------------------------------------------------------------------
|
|
const marketingChildren: RouteRecordRaw[] = [
|
|
{
|
|
path: '',
|
|
name: 'landing',
|
|
component: () => import('@/views/marketing/LandingView.vue'),
|
|
},
|
|
{
|
|
path: 'pricing',
|
|
name: 'pricing',
|
|
component: () => import('@/views/marketing/PricingView.vue'),
|
|
},
|
|
{
|
|
path: 'how-it-works',
|
|
name: 'how-it-works',
|
|
component: () => import('@/views/marketing/HowItWorksView.vue'),
|
|
},
|
|
{
|
|
path: 'faq',
|
|
name: 'faq',
|
|
component: () => import('@/views/marketing/FaqView.vue'),
|
|
},
|
|
{
|
|
path: 'roadmap',
|
|
name: 'roadmap',
|
|
component: () => import('@/views/marketing/RoadmapView.vue'),
|
|
},
|
|
{
|
|
path: 'early-access',
|
|
name: 'early-access',
|
|
component: () => import('@/views/marketing/EarlyAccessView.vue'),
|
|
},
|
|
]
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Panel domain routes — panel.corrosionmgmt.com, localhost, etc.
|
|
// Existing behavior, unchanged.
|
|
// ---------------------------------------------------------------------------
|
|
const panelRoutes: RouteRecordRaw[] = [
|
|
// Auth routes (no layout)
|
|
{
|
|
path: '/login',
|
|
name: 'login',
|
|
component: () => import('@/views/auth/LoginView.vue'),
|
|
meta: { guest: true },
|
|
},
|
|
{
|
|
path: '/register',
|
|
name: 'register',
|
|
component: () => import('@/views/auth/RegisterView.vue'),
|
|
meta: { guest: true },
|
|
},
|
|
{
|
|
path: '/forgot-password',
|
|
name: 'forgot-password',
|
|
component: () => import('@/views/auth/ForgotPasswordView.vue'),
|
|
meta: { guest: true },
|
|
},
|
|
{
|
|
path: '/setup',
|
|
name: 'setup-wizard',
|
|
component: () => import('@/views/auth/SetupWizardView.vue'),
|
|
meta: { requiresAuth: true },
|
|
},
|
|
|
|
// Admin dashboard routes (with sidebar layout)
|
|
{
|
|
path: '/',
|
|
component: () => import('@/components/layout/DashboardLayout.vue'),
|
|
meta: { requiresAuth: true },
|
|
children: [
|
|
{
|
|
path: '',
|
|
name: 'dashboard',
|
|
component: () => import('@/views/admin/DashboardView.vue'),
|
|
},
|
|
{
|
|
path: 'server',
|
|
name: 'server',
|
|
component: () => import('@/views/admin/ServerView.vue'),
|
|
},
|
|
{
|
|
path: 'console',
|
|
name: 'console',
|
|
component: () => import('@/views/admin/ConsoleView.vue'),
|
|
},
|
|
{
|
|
path: 'players',
|
|
name: 'players',
|
|
component: () => import('@/views/admin/PlayersView.vue'),
|
|
},
|
|
{
|
|
path: 'plugins',
|
|
name: 'plugins',
|
|
component: () => import('@/views/admin/PluginsView.vue'),
|
|
},
|
|
{
|
|
path: 'files',
|
|
name: 'files',
|
|
component: () => import('@/views/admin/FileManagerView.vue'),
|
|
},
|
|
{
|
|
path: 'loot-builder',
|
|
name: 'loot-builder',
|
|
component: () => import('@/views/admin/LootBuilderView.vue'),
|
|
},
|
|
{
|
|
path: 'wipes',
|
|
name: 'wipes',
|
|
component: () => import('@/views/admin/WipesView.vue'),
|
|
},
|
|
{
|
|
path: 'wipes/profiles',
|
|
name: 'wipe-profiles',
|
|
component: () => import('@/views/admin/WipeProfilesView.vue'),
|
|
},
|
|
{
|
|
path: 'wipes/calendar',
|
|
name: 'wipe-calendar',
|
|
component: () => import('@/views/admin/WipeCalendarView.vue'),
|
|
},
|
|
{
|
|
path: 'wipes/history',
|
|
name: 'wipe-history',
|
|
component: () => import('@/views/admin/WipeHistoryView.vue'),
|
|
},
|
|
{
|
|
path: 'wipes/analytics',
|
|
name: 'wipe-analytics',
|
|
component: () => import('@/views/admin/WipeAnalyticsView.vue'),
|
|
},
|
|
{
|
|
path: 'maps',
|
|
name: 'maps',
|
|
component: () => import('@/views/admin/MapsView.vue'),
|
|
},
|
|
{
|
|
path: 'maps/analytics',
|
|
name: 'map-analytics',
|
|
component: () => import('@/views/admin/MapAnalyticsView.vue'),
|
|
},
|
|
{
|
|
path: 'chat',
|
|
name: 'chat',
|
|
component: () => import('@/views/admin/ChatLogView.vue'),
|
|
},
|
|
{
|
|
path: 'analytics',
|
|
name: 'analytics',
|
|
component: () => import('@/views/admin/AnalyticsView.vue'),
|
|
},
|
|
{
|
|
path: 'retention',
|
|
name: 'retention',
|
|
component: () => import('@/views/admin/PlayerRetentionView.vue'),
|
|
},
|
|
{
|
|
path: 'notifications',
|
|
name: 'notifications',
|
|
component: () => import('@/views/admin/NotificationsView.vue'),
|
|
},
|
|
{
|
|
path: 'team',
|
|
name: 'team',
|
|
component: () => import('@/views/admin/TeamView.vue'),
|
|
},
|
|
{
|
|
path: 'store/config',
|
|
name: 'store-config',
|
|
component: () => import('@/views/admin/StoreConfigView.vue'),
|
|
},
|
|
{
|
|
path: 'store/items',
|
|
name: 'store-items',
|
|
component: () => import('@/views/admin/StoreItemsView.vue'),
|
|
},
|
|
{
|
|
path: 'store/revenue',
|
|
name: 'store-revenue',
|
|
component: () => import('@/views/admin/StoreRevenueView.vue'),
|
|
},
|
|
{
|
|
path: 'modules',
|
|
name: 'modules',
|
|
component: () => import('@/views/admin/ModuleStoreView.vue'),
|
|
},
|
|
{
|
|
path: 'settings',
|
|
name: 'settings',
|
|
component: () => import('@/views/admin/SettingsView.vue'),
|
|
},
|
|
{
|
|
path: 'schedules',
|
|
name: 'schedules',
|
|
component: () => import('@/views/admin/SchedulesView.vue'),
|
|
},
|
|
{
|
|
path: 'migration',
|
|
name: 'migration',
|
|
component: () => import('@/views/admin/MigrationView.vue'),
|
|
},
|
|
{
|
|
path: 'changelog',
|
|
name: 'changelog',
|
|
component: () => import('@/views/admin/ChangelogView.vue'),
|
|
},
|
|
{
|
|
path: 'alerts',
|
|
name: 'alerts',
|
|
component: () => import('@/views/admin/AlertsView.vue'),
|
|
},
|
|
// Platform Admin views (super-admin only)
|
|
{
|
|
path: 'admin',
|
|
name: 'platform-admin',
|
|
component: () => import('@/views/platform-admin/AdminDashboard.vue'),
|
|
meta: { superAdmin: true },
|
|
},
|
|
{
|
|
path: 'admin/licenses',
|
|
name: 'platform-licenses',
|
|
component: () => import('@/views/platform-admin/AdminLicenses.vue'),
|
|
meta: { superAdmin: true },
|
|
},
|
|
{
|
|
path: 'admin/subscriptions',
|
|
name: 'platform-subscriptions',
|
|
component: () => import('@/views/platform-admin/AdminSubscriptions.vue'),
|
|
meta: { superAdmin: true },
|
|
},
|
|
{
|
|
path: 'admin/users',
|
|
name: 'platform-users',
|
|
component: () => import('@/views/platform-admin/AdminUsers.vue'),
|
|
meta: { superAdmin: true },
|
|
},
|
|
{
|
|
path: 'admin/servers',
|
|
name: 'platform-servers',
|
|
component: () => import('@/views/platform-admin/AdminServers.vue'),
|
|
meta: { superAdmin: true },
|
|
},
|
|
],
|
|
},
|
|
|
|
// Public server site routes (different layout)
|
|
{
|
|
path: '/s/:subdomain',
|
|
component: () => import('@/components/layout/PublicLayout.vue'),
|
|
children: [
|
|
{
|
|
path: '',
|
|
name: 'public-server',
|
|
component: () => import('@/views/public/ServerInfoView.vue'),
|
|
},
|
|
{
|
|
path: 'store',
|
|
name: 'public-store',
|
|
component: () => import('@/views/public/StoreView.vue'),
|
|
},
|
|
],
|
|
},
|
|
|
|
// Marketing site (accessible on panel domain at /site/*)
|
|
{
|
|
path: '/site',
|
|
component: () => import('@/components/layout/MarketingLayout.vue'),
|
|
children: marketingChildren,
|
|
},
|
|
|
|
// Status page
|
|
{
|
|
path: '/status',
|
|
name: 'status',
|
|
component: () => import('@/views/public/StatusPageView.vue'),
|
|
},
|
|
|
|
// Catch-all
|
|
{
|
|
path: '/:pathMatch(.*)*',
|
|
redirect: '/',
|
|
},
|
|
]
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Marketing domain routes — corrosionmgmt.com (bare domain)
|
|
// Marketing pages at root. No admin/auth routes.
|
|
// ---------------------------------------------------------------------------
|
|
const marketingRoutes: RouteRecordRaw[] = [
|
|
// Marketing layout at /
|
|
{
|
|
path: '/',
|
|
component: () => import('@/components/layout/MarketingLayout.vue'),
|
|
children: marketingChildren,
|
|
},
|
|
|
|
// Backward compat: /site/* → root-level equivalents
|
|
{
|
|
path: '/site/:pathMatch(.*)*',
|
|
redirect: (to) => {
|
|
const sub = to.params.pathMatch
|
|
if (!sub || (Array.isArray(sub) && sub.length === 0)) return '/'
|
|
const subPath = Array.isArray(sub) ? sub.join('/') : sub
|
|
return subPath ? `/${subPath}` : '/'
|
|
},
|
|
},
|
|
|
|
// Status page
|
|
{
|
|
path: '/status',
|
|
name: 'status',
|
|
component: () => import('@/views/public/StatusPageView.vue'),
|
|
},
|
|
|
|
// Catch-all: unknown routes → landing page
|
|
{
|
|
path: '/:pathMatch(.*)*',
|
|
redirect: '/',
|
|
},
|
|
]
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Router instance
|
|
// ---------------------------------------------------------------------------
|
|
const router = createRouter({
|
|
history: createWebHistory(),
|
|
routes: isMarketingDomain ? marketingRoutes : panelRoutes,
|
|
})
|
|
|
|
// Auth guard — only meaningful on panel domain (marketing has no requiresAuth routes)
|
|
router.beforeEach((to, _from, next) => {
|
|
const auth = useAuthStore()
|
|
|
|
if (to.meta.requiresAuth && !auth.isAuthenticated) {
|
|
next({ name: 'login', query: { redirect: to.fullPath } })
|
|
} else if (to.meta.superAdmin && !auth.isSuperAdmin) {
|
|
next({ name: 'dashboard' })
|
|
} else if (to.meta.guest && auth.isAuthenticated) {
|
|
next({ name: 'dashboard' })
|
|
} else {
|
|
next()
|
|
}
|
|
})
|
|
|
|
export default router
|