Root cause: super_admin JWT returned early with no license_id, causing
@CurrentTenant() to pass undefined to every tenant-scoped service query.
- jwt.strategy: Move license lookup before super_admin early return so
admins who own licenses get their license_id in the JWT payload
- CurrentTenant decorator: Throw 401 with clear message when license_id
is undefined instead of letting undefined cascade into TypeORM queries
- Wipe store: Fix 6 wrong routes (/profiles → /wipes/profiles, etc.)
and remove redundant manual license_id guards
- Changelog module: Add stub controller/service returning empty array
to eliminate 404 on /api/changelog
- ChangelogView: Handle both array and {entries} response shapes
- AGENTS.md: Streamlined 3-tier roster (Opus/Sonnet/Haiku)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace `new Date(x).toLocaleString()` pattern with `safeDate()` utility to prevent crashes on null/undefined timestamps in scheduled task next_run field.
Part of ongoing safe formatter migration across all Vue views.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
API returns partial/empty stats object — individual fields can be undefined
even when the object exists. Added null guard to prevent toLocaleString crash.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Auth service used flat env var names (JWT_SECRET, JWT_ACCESS_EXPIRY_SECONDS)
but @nestjs/config nests them under jwt.* — configService.get() returned
undefined, so expiresIn was 0 and tokens expired on issue (iat === exp)
- JWT strategy had same bug for secretOrKey
- AnalyticsView passed /api/analytics/... to useApi which already prepends /api,
resulting in /api/api/analytics/... (404)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements missing frontend views and API integrations:
New Views:
- SchedulesView: CRUD for scheduled tasks (restart/announcement/command/plugin_reload)
- MigrationView: Export/import interface with file upload and history tracking
- ChangelogView: Paginated changelog feed with category badges
- ForgotPasswordView: Password reset flow with email submission
- AlertsView: Alert config dashboard with threshold settings and history
Component Updates:
- ErrorBoundary: Global error handler with retry functionality
- DashboardLayout: Mobile responsive sidebar, permission-based nav, new menu items
- ServerInfoView: Complete rewrite for public server info display
Infrastructure:
- useApi: Token refresh interceptor with 401 retry and infinite loop prevention
- plugins store: Implemented all stubbed methods with real API calls
- auth store: Added hasPermission() helper for RBAC UI visibility
- Router: Added new routes with catch-all fallback
Purpose: Closes frontend implementation gaps. Hardens auth flow, improves mobile UX,
enables server automation scheduling, alert configuration, and data migration tools.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove unused ExternalLink import from ModuleStoreView
- Remove unused Tag import from StoreItemsView
- Prefix unused cmd variable with underscore in v-for loop
Fixes production build TypeScript errors.
- Built StoreConfigView.vue for webstore setup
- Form fields: store name, description, currency (USD/EUR/GBP)
- PayPal credentials (client ID/secret) with encryption support
- Sandbox/production mode toggle with warning states
- Store enable/disable with validation
- Empty state for unconfigured stores
- TypeScript StoreConfig interface
- Route: /admin/webstore/config (auth required)
- API integration: GET/PUT /api/webstore/config
- Responsive Tailwind design
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Customer-facing module marketplace with full browse/preview/purchase flow:
Frontend Implementation:
- Complete ModuleStoreView.vue with dual-tab interface (Catalog + My Modules)
- Module grid with preview images, category badges, pricing display
- Search functionality across name/description fields
- Category filtering (8 categories: Loot, Events, Economy, Kits, Admin, PVP, PVE, Building)
- Detail modal with screenshots gallery, full features list, version info
- Purchase confirmation modal with license binding display
- Installation status tracking (Not Purchased → Purchased → Installed)
- Professional marketplace UI with hover animations and responsive grid
TypeScript Types:
- Module interface with full metadata (id, slug, name, description, price, category, images, features, version, purchase/install status)
- PurchaseRequest interface for API integration
API Integration Points (backend implementation separate):
- GET /api/modules/catalog — Browse all available modules
- GET /api/modules/my-modules — Fetch purchased modules for license
- POST /api/modules/purchase — Initiate purchase (returns payment URL or instant confirmation)
- POST /api/modules/install — Trigger deployment to game server
Design Features:
- Color-coded category badges with 8-color palette
- Preview image with scale-on-hover effect
- "Purchased" badge overlay for owned modules
- Three-button state progression (Purchase → Install → Installed)
- Empty states for zero results and zero purchases
- Mobile-responsive grid (1/2/3 columns)
- Payment flow with external redirect support (Stripe/PayPal)
- Error handling with inline error display in purchase modal
Purpose: Server admins can browse, preview, purchase, and install premium gameplay modules directly from dashboard. This is where customers pay real money — UI polish critical for conversion.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Complete implementation of wipe analytics system providing operational
insights and data-driven wipe timing optimization.
Backend:
- Added comprehensive analytics query layer to db/wipes.rs:
- Success rate calculation over time ranges
- Average wipe duration tracking
- Post-wipe population curve analysis (Day 1/2/3)
- Optimal wipe timing recommendations based on player peaks
- Individual wipe entry tracking with peak population correlation
- Implemented GET /api/analytics/wipes/performance endpoint with
flexible range parameters (6d/12d/90d/all)
- All queries leverage hourly aggregate tables for 90-day retention
Frontend:
- Built WipeAnalyticsView.vue with 3 ECharts visualizations:
- Success rate timeline (scatter: green success, red failures)
- Population curve comparing Day 1/2/3 post-wipe averages
- Wipe duration trend showing execution time evolution
- Insight cards displaying success rate, avg duration, peak day, optimal timing
- Actionable recommendations banner with data-driven suggestions:
- Optimal wipe scheduling based on historical player peaks
- Wipe frequency recommendations (weekly vs bi-weekly)
- Duration optimization alerts
- Rollback protection warnings
- Time range selector and CSV export functionality
- Added /wipes/analytics route
TypeScript interfaces added: WipePerformanceMetrics, WipeAnalyticsEntry,
PopulationCurve
Answers critical operational questions: "How long do wipes take? When do
players peak post-wipe? What's my success rate? When should I wipe for
maximum population?"
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement status.corrosionmgmt.com public status page showcasing all
Corrosion servers that opt-in. Drives platform visibility and attracts
new customers.
Backend:
- Migration 007: status_page_description TEXT column
- models/public.rs: PublicServerStatus, PlatformHealth, StatusPageResponse
- db/public.rs: get_public_servers() with uptime calculations (24h/7d/30d)
- api/public.rs: GET /api/public/status (no auth)
- api/settings.rs: public site config endpoints (auth required)
Frontend:
- StatusPageView.vue: Server grid with live stats, uptime badges, wipe schedules
- Platform health header: total servers, online count, total players
- Auto-refresh every 10 seconds via polling
- Mobile-responsive design
- SettingsView.vue: Public Status tab with opt-in toggle
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add complete map analytics pipeline to answer: "Which maps drive the most
players? Is my rotation working?"
Backend Changes:
- Migration 005: Add map_id FK to server_stats and wipe_history tables
- Stats consumer now captures current_map_id when persisting stats
- Map analytics queries: get_map_analytics() returns performance metrics,
effectiveness scores, and rotation health
- API endpoint: GET /api/analytics/maps?range=90d returns summary with
best performing map and rotation effectiveness percentage
Frontend Changes:
- MapAnalyticsView.vue: Complete dashboard with performance charts,
sortable metrics table, actionable insights, and CSV export
- ECharts bar chart comparing avg vs peak players per map
- Color-coded effectiveness scoring (green ≥80%, yellow ≥60%, red <60%)
- Time range selector: 30d/90d/all
Purpose: Enables data-driven map selection for wipe day based on player
engagement metrics. Rotation effectiveness algorithm scores maps by
(avg_players / peak_players) * 100.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Backend:
- Stats ingestion consumer subscribing to corrosion.*.stats NATS subject
- Hourly aggregation scheduler (runs :05 past every hour)
- Daily cleanup job (03:00 UTC) with 7-day raw / 90-day hourly retention
- Analytics API (summary, timeseries, CSV export)
- Complete stats DB queries with aggregation and cleanup
Frontend:
- Analytics dashboard with ECharts integration
- Player count and server performance charts
- Time range selector (24h/7d/30d)
- CSV export functionality
- Real-time data loading
Infrastructure:
- Exposed NatsBridge.jetstream for consumer access
- Background service initialization in main.rs
Data flow: Plugin → NATS → Consumer → DB → Aggregation → API → Charts
Unblocks Strike 4B (dashboards) and 4C (alerting).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Vite bakes env vars at build time, not runtime. Without .env.production
in the frontend directory, the Docker build had no VITE_PANEL_URL and
Sign In / Get Started links resolved to empty hrefs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
corrosionmgmt.com now serves LandingView as the default page with marketing
routes at root level. panel.corrosionmgmt.com continues serving the admin
panel unchanged. /site/* backward compat via redirects on marketing domain.
- nginx: Add bare domain server block (only proxies /api/early-access/)
- router: Detect hostname at module load, generate domain-specific routes
- MarketingLayout: Named routes for nav, external <a> tags for auth links
- LandingView: CTAs point to panel domain via VITE_PANEL_URL
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
API returns unexpected shape when backend isn't wired — data.members
is undefined, assigned to ref, then .length in template throws
TypeError. Nullish coalescing to empty array prevents the crash.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
/admin/servers.startsWith('/admin') was true, so both Admin Home
and Server Fleet highlighted simultaneously. Now /admin uses
exact match like / does.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Combined page: countdown timer (Feb 28), email capture with server
count segmentation (wired to POST /api/early-access), Founding Admin
Program (25 slots), demo dashboard preview placeholders, roadmap
voting, and launch timeline. Backend: Axum handler, migration for
early_access_signups table with email + server_count + created_at.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Views: Plugins, Wipes, WipeProfiles, WipeCalendar, WipeHistory,
Maps, Analytics, StoreManage, ModuleStore. All 20/20 admin views
now implemented. Updated hero graphic to final version.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Console: Terminal-style RCON interface with timestamped output,
color-coded log types, command input, clear button, and connection
status indicator. Uses server.sendCommand() from the store.
Players: Full management table with search, online/offline/all
filter tabs, Steam ID display, session time, ping, playtime,
admin badges, and kick/ban action buttons. Sorted online-first.
Both views use Oxide Orange brand colors per guidelines.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds baseUrl and @/* path mapping to tsconfig.app.json so vue-tsc can
resolve @/types, @/stores, and @/composables. Prefixes unused stub
parameters with _ to satisfy noUnusedParameters. Full build now passes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces the plain trefoil mark with the full product hero —
cracked trefoil, tagline, and feature bullets from the architecture spec.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Proper favicon.ico with 16/32/64px PNG variants for cross-browser
coverage. Apple-touch-icon fallback. Adds CORROSION MANAGEMENT
wordmark PNG for text-based branding contexts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copies brand assets to frontend/public (favicon.png, logo.png, logo-hero.png).
Updates index.html, LoginView, RegisterView, DashboardLayout sidebar, and
PublicLayout footer with proper branding.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These were installed in the previous session but not saved to package.json.
Production build now succeeds (39KB gzipped).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend: Server connection/config/admins DB queries, server API routes
with auth-gated endpoints (overview, config CRUD, admin management).
Frontend: Server store wired to API, dashboard fetches server data on
mount with live status indicators, uptime formatting, and server
config display. Logout now redirects to /login.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Build complete auth flow with dark-themed CORROSION branding,
loading states, error handling, client-side validation, and
placeholder dashboard with stat cards and quick actions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Complete frontend skeleton: Vite + Vue 3 + TypeScript + Tailwind CSS,
Pinia stores (auth, server, wipe, plugins), authenticated API composable,
full route tree with auth guards, DashboardLayout with sidebar nav,
23 view stubs across auth/admin/public, all TypeScript interfaces.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>