import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router' import { useAuthStore } from '@/stores/auth' // Extend vue-router's RouteMeta so title/description are typed throughout declare module 'vue-router' { interface RouteMeta { title?: string description?: string requiresAuth?: boolean guest?: boolean superAdmin?: boolean } } // --------------------------------------------------------------------------- // Domain detection — runs once at module load // Env-driven so www./staging hosts route correctly; an exact-match literal // here once meant any non-canonical marketing host silently got the panel. // --------------------------------------------------------------------------- const hostname = typeof window !== 'undefined' ? window.location.hostname : '' const marketingHosts = (import.meta.env.VITE_MARKETING_HOSTS ?? 'corrosionmgmt.com,www.corrosionmgmt.com') .split(',') .map((h: string) => h.trim().toLowerCase()) .filter(Boolean) const isMarketingDomain = marketingHosts.includes(hostname.toLowerCase()) // --------------------------------------------------------------------------- // Marketing page children — shared between both domain route sets // --------------------------------------------------------------------------- const marketingChildren: RouteRecordRaw[] = [ { path: '', name: 'landing', component: () => import('@/views/marketing/LandingView.vue'), meta: { title: 'Corrosion — Game Server Operations for Self-Hosted Communities', description: 'Management panel for self-hosted survival game servers — Rust, Dune: Awakening, Conan Exiles, Soulmask. Wipe automation, plugins, monitoring. Bring your own server.', }, }, { path: 'pricing', name: 'pricing', component: () => import('@/views/marketing/PricingView.vue'), meta: { title: 'Pricing — Corrosion', description: 'Plans from $9.99/mo (Hobby, 1–5 servers) to Network ($99.99+/mo, 50+ servers). Non-commercial and commercial tiers. No hosting fees — bring your own server.', }, }, { path: 'how-it-works', name: 'how-it-works', component: () => import('@/views/marketing/HowItWorksView.vue'), meta: { title: 'How It Works — Corrosion', description: 'Install one host agent on Windows or Linux. It connects outbound-only to Corrosion — no inbound ports, no SSH. Manage every game instance from the browser.', }, }, { path: 'faq', name: 'faq', component: () => import('@/views/marketing/FaqView.vue'), meta: { title: 'FAQ — Corrosion', description: 'Honest answers: Corrosion is self-service (BYOS, no hosting). Support is docs + community; 1:1 at $125/hr. Supports Rust, Dune, Conan Exiles, Soulmask.', }, }, { path: 'roadmap', name: 'roadmap', component: () => import('@/views/marketing/RoadmapView.vue'), meta: { title: 'Roadmap — Corrosion', description: 'Phase 1 shipped: core control plane, auto-wiper, plugin management. In progress: Dune, Conan, Soulmask multi-game blueprints. Planned: API access, integrations.', }, }, { path: 'early-access', name: 'early-access', component: () => import('@/views/marketing/EarlyAccessView.vue'), meta: { title: 'Early Access — Corrosion', description: 'Join the early access list. Get full control plane access — wipe automation, plugin management, real-time console — and lock in launch pricing.', }, }, ] // --------------------------------------------------------------------------- // 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, title: 'Sign in — Corrosion' }, }, { path: '/register', name: 'register', component: () => import('@/views/auth/RegisterView.vue'), meta: { guest: true, title: 'Create account — Corrosion' }, }, { path: '/forgot-password', name: 'forgot-password', component: () => import('@/views/auth/ForgotPasswordView.vue'), meta: { guest: true, title: 'Reset password — Corrosion' }, }, { path: '/setup', name: 'setup-wizard', component: () => import('@/views/auth/SetupWizardView.vue'), meta: { requiresAuth: true, title: 'Setup — Corrosion' }, }, // 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'), meta: { title: 'Dashboard — Corrosion' }, }, { path: 'server', name: 'server', component: () => import('@/views/admin/ServerView.vue'), meta: { title: 'Server — Corrosion' }, }, { path: 'console', name: 'console', component: () => import('@/views/admin/ConsoleView.vue'), meta: { title: 'Console — Corrosion' }, }, { path: 'players', name: 'players', component: () => import('@/views/admin/PlayersView.vue'), meta: { title: 'Players — Corrosion' }, }, { path: 'plugins', name: 'plugins', component: () => import('@/views/admin/PluginsView.vue'), meta: { title: 'Plugins — Corrosion' }, }, { path: 'files', name: 'files', component: () => import('@/views/admin/FileManagerView.vue'), meta: { title: 'Files — Corrosion' }, }, { path: 'plugin-configs', name: 'plugin-configs', component: () => import('@/views/admin/PluginConfigsView.vue'), meta: { title: 'Plugin Configs — Corrosion' }, }, { path: 'loot-builder', name: 'loot-builder', component: () => import('@/views/admin/LootBuilderView.vue'), meta: { title: 'Loot Builder — Corrosion' }, }, { path: 'teleport-config', name: 'teleport-config', component: () => import('@/views/admin/TeleportConfigView.vue'), meta: { title: 'Teleport Config — Corrosion' }, }, { path: 'gather-manager', name: 'gather-manager', component: () => import('@/views/admin/GatherManagerView.vue'), meta: { title: 'Gather Manager — Corrosion' }, }, { path: 'autodoors', name: 'autodoors', component: () => import('@/views/admin/AutoDoorsView.vue'), meta: { title: 'Auto Doors — Corrosion' }, }, { path: 'kits', name: 'kits-config', component: () => import('@/views/admin/KitsView.vue'), meta: { title: 'Kits — Corrosion' }, }, { path: 'furnace-splitter', name: 'furnace-splitter', component: () => import('@/views/admin/FurnaceSplitterView.vue'), meta: { title: 'Furnace Splitter — Corrosion' }, }, { path: 'better-chat', name: 'better-chat', component: () => import('@/views/admin/BetterChatView.vue'), meta: { title: 'Better Chat — Corrosion' }, }, { path: 'timed-execute', name: 'timed-execute', component: () => import('@/views/admin/TimedExecuteView.vue'), meta: { title: 'Timed Execute — Corrosion' }, }, { path: 'raidable-bases', name: 'raidable-bases', component: () => import('@/views/admin/RaidableBasesView.vue'), meta: { title: 'Raidable Bases — Corrosion' }, }, { path: 'wipes', name: 'wipes', component: () => import('@/views/admin/WipesView.vue'), meta: { title: 'Wipes — Corrosion' }, }, { path: 'wipes/profiles', name: 'wipe-profiles', component: () => import('@/views/admin/WipeProfilesView.vue'), meta: { title: 'Wipe Profiles — Corrosion' }, }, { path: 'wipes/calendar', name: 'wipe-calendar', component: () => import('@/views/admin/WipeCalendarView.vue'), meta: { title: 'Wipe Calendar — Corrosion' }, }, { path: 'wipes/history', name: 'wipe-history', component: () => import('@/views/admin/WipeHistoryView.vue'), meta: { title: 'Wipe History — Corrosion' }, }, { path: 'wipes/analytics', name: 'wipe-analytics', component: () => import('@/views/admin/WipeAnalyticsView.vue'), meta: { title: 'Wipe Analytics — Corrosion' }, }, { path: 'maps', name: 'maps', component: () => import('@/views/admin/MapsView.vue'), meta: { title: 'Maps — Corrosion' }, }, { path: 'maps/analytics', name: 'map-analytics', component: () => import('@/views/admin/MapAnalyticsView.vue'), meta: { title: 'Map Analytics — Corrosion' }, }, { path: 'chat', name: 'chat', component: () => import('@/views/admin/ChatLogView.vue'), meta: { title: 'Chat Log — Corrosion' }, }, { path: 'analytics', name: 'analytics', component: () => import('@/views/admin/AnalyticsView.vue'), meta: { title: 'Analytics — Corrosion' }, }, { path: 'retention', name: 'retention', component: () => import('@/views/admin/PlayerRetentionView.vue'), meta: { title: 'Player Retention — Corrosion' }, }, { path: 'notifications', name: 'notifications', component: () => import('@/views/admin/NotificationsView.vue'), meta: { title: 'Notifications — Corrosion' }, }, { path: 'team', name: 'team', component: () => import('@/views/admin/TeamView.vue'), meta: { title: 'Team — Corrosion' }, }, { path: 'store/config', name: 'store-config', component: () => import('@/views/admin/StoreConfigView.vue'), meta: { title: 'Store Config — Corrosion' }, }, { path: 'store/items', name: 'store-items', component: () => import('@/views/admin/StoreItemsView.vue'), meta: { title: 'Store Items — Corrosion' }, }, { path: 'store/revenue', name: 'store-revenue', component: () => import('@/views/admin/StoreRevenueView.vue'), meta: { title: 'Store Revenue — Corrosion' }, }, { path: 'modules', name: 'modules', component: () => import('@/views/admin/ModuleStoreView.vue'), meta: { title: 'Modules — Corrosion' }, }, { path: 'settings', name: 'settings', component: () => import('@/views/admin/SettingsView.vue'), meta: { title: 'Settings — Corrosion' }, }, { path: 'schedules', name: 'schedules', component: () => import('@/views/admin/SchedulesView.vue'), meta: { title: 'Schedules — Corrosion' }, }, { path: 'migration', name: 'migration', component: () => import('@/views/admin/MigrationView.vue'), meta: { title: 'Migration — Corrosion' }, }, { path: 'changelog', name: 'changelog', component: () => import('@/views/admin/ChangelogView.vue'), meta: { title: 'Changelog — Corrosion' }, }, { path: 'alerts', name: 'alerts', component: () => import('@/views/admin/AlertsView.vue'), meta: { title: 'Alerts — Corrosion' }, }, { path: 'fleet', name: 'fleet', component: () => import('@/views/admin/FleetView.vue'), meta: { title: 'Fleet — Corrosion', requiresAuth: true }, }, // Platform Admin views (super-admin only) { path: 'admin', name: 'platform-admin', component: () => import('@/views/platform-admin/AdminDashboard.vue'), meta: { superAdmin: true, title: 'Admin — Corrosion' }, }, { path: 'admin/licenses', name: 'platform-licenses', component: () => import('@/views/platform-admin/AdminLicenses.vue'), meta: { superAdmin: true, title: 'Admin: Licenses — Corrosion' }, }, { path: 'admin/subscriptions', name: 'platform-subscriptions', component: () => import('@/views/platform-admin/AdminSubscriptions.vue'), meta: { superAdmin: true, title: 'Admin: Subscriptions — Corrosion' }, }, { path: 'admin/users', name: 'platform-users', component: () => import('@/views/platform-admin/AdminUsers.vue'), meta: { superAdmin: true, title: 'Admin: Users — Corrosion' }, }, { path: 'admin/servers', name: 'platform-servers', component: () => import('@/views/platform-admin/AdminServers.vue'), meta: { superAdmin: true, title: 'Admin: Servers — Corrosion' }, }, ], }, // 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'), meta: { title: 'Status — Corrosion' }, }, // 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'), meta: { title: 'Status — Corrosion' }, }, // Catch-all: unknown routes → landing page { path: '/:pathMatch(.*)*', redirect: '/', }, ] // --------------------------------------------------------------------------- // Router instance // --------------------------------------------------------------------------- const router = createRouter({ history: createWebHistory(), routes: isMarketingDomain ? marketingRoutes : panelRoutes, }) // --------------------------------------------------------------------------- // Document title + meta description/OG update on every navigation // --------------------------------------------------------------------------- function setOrClearMeta(selector: string, attr: string, value: string): void { let el = document.querySelector(selector) if (!el) { el = document.createElement('meta') // Parse the selector to set the right attribute (name="..." or property="...") const nameMatch = selector.match(/\[name="([^"]+)"\]/) const propMatch = selector.match(/\[property="([^"]+)"\]/) if (nameMatch?.[1]) el.setAttribute('name', nameMatch[1]) if (propMatch?.[1]) el.setAttribute('property', propMatch[1]) document.head.appendChild(el) } el.setAttribute(attr, value) } router.afterEach((to) => { // Title document.title = to.meta.title ?? 'Corrosion Management' // Description const desc = to.meta.description ?? '' setOrClearMeta('meta[name="description"]', 'content', desc) // OG title setOrClearMeta('meta[property="og:title"]', 'content', to.meta.title ?? 'Corrosion Management') // OG description setOrClearMeta('meta[property="og:description"]', 'content', desc) }) // 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