fix: Wave 1 — critical bug fixes across 9 files
All checks were successful
Test Asgard Runner / test (push) Successful in 3s
All checks were successful
Test Asgard Runner / test (push) Successful in 3s
- Fix double-prefix URL bugs in 4 analytics/revenue views (/api/api → /api) - Fix AdminDashboard quick-links routing (/platform-admin/* → /admin/*) - Fix MigrationView import missing Authorization header - Remove dead ConsoleModule from app.module (conflicts with NatsBridgeGateway on /ws) - Fix store.service.ts raw Error throws → NotFoundException/ForbiddenException - Fix payment-order entity FK (webstore_subscription_id → WebstoreSubscription) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,6 @@ import { AuthModule } from './modules/auth/auth.module';
|
|||||||
import { UsersModule } from './modules/users/users.module';
|
import { UsersModule } from './modules/users/users.module';
|
||||||
import { LicensesModule } from './modules/licenses/licenses.module';
|
import { LicensesModule } from './modules/licenses/licenses.module';
|
||||||
import { ServersModule } from './modules/servers/servers.module';
|
import { ServersModule } from './modules/servers/servers.module';
|
||||||
import { ConsoleModule } from './modules/console/console.module';
|
|
||||||
import { PlayersModule } from './modules/players/players.module';
|
import { PlayersModule } from './modules/players/players.module';
|
||||||
import { WipesModule } from './modules/wipes/wipes.module';
|
import { WipesModule } from './modules/wipes/wipes.module';
|
||||||
import { MapsModule } from './modules/maps/maps.module';
|
import { MapsModule } from './modules/maps/maps.module';
|
||||||
@@ -86,7 +85,6 @@ import { NatsBridgeGateway } from './gateways/nats-bridge.gateway';
|
|||||||
UsersModule,
|
UsersModule,
|
||||||
LicensesModule,
|
LicensesModule,
|
||||||
ServersModule,
|
ServersModule,
|
||||||
ConsoleModule,
|
|
||||||
PlayersModule,
|
PlayersModule,
|
||||||
WipesModule,
|
WipesModule,
|
||||||
MapsModule,
|
MapsModule,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, Check } from 'typeorm';
|
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, Check } from 'typeorm';
|
||||||
import { Module } from './module.entity';
|
import { Module } from './module.entity';
|
||||||
import { License } from './license.entity';
|
import { License } from './license.entity';
|
||||||
|
import { WebstoreSubscription } from './webstore-subscription.entity';
|
||||||
|
|
||||||
@Entity('payment_orders')
|
@Entity('payment_orders')
|
||||||
@Check(`"status" IN ('pending', 'completed', 'failed', 'refunded')`)
|
@Check(`"status" IN ('pending', 'completed', 'failed', 'refunded')`)
|
||||||
@@ -52,7 +53,7 @@ export class PaymentOrder {
|
|||||||
@JoinColumn({ name: 'license_id' })
|
@JoinColumn({ name: 'license_id' })
|
||||||
license: License;
|
license: License;
|
||||||
|
|
||||||
@ManyToOne(() => License, { onDelete: 'SET NULL', nullable: true })
|
@ManyToOne(() => WebstoreSubscription, { onDelete: 'SET NULL', nullable: true })
|
||||||
@JoinColumn({ name: 'webstore_subscription_id' })
|
@JoinColumn({ name: 'webstore_subscription_id' })
|
||||||
webstore_subscription: License | null;
|
webstore_subscription: WebstoreSubscription | null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { Module } from '../../entities/module.entity';
|
import { Module } from '../../entities/module.entity';
|
||||||
@@ -44,7 +44,7 @@ export class StoreService {
|
|||||||
|
|
||||||
const module = await this.moduleRepo.findOne({ where: { id: moduleId } });
|
const module = await this.moduleRepo.findOne({ where: { id: moduleId } });
|
||||||
if (!module) {
|
if (!module) {
|
||||||
throw new Error('Module not found');
|
throw new NotFoundException('Module not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const purchase = this.purchaseRepo.create({
|
const purchase = this.purchaseRepo.create({
|
||||||
@@ -64,7 +64,7 @@ export class StoreService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!purchase) {
|
if (!purchase) {
|
||||||
throw new Error('Module not purchased');
|
throw new ForbiddenException('Module not purchased');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stub - would create module_installation record
|
// Stub - would create module_installation record
|
||||||
|
|||||||
42
corrosion-final-push.md
Normal file
42
corrosion-final-push.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Operation: Corrosion Final Push
|
||||||
|
|
||||||
|
**Date**: 2026-02-21
|
||||||
|
**Operator**: XO (Opus 4.6)
|
||||||
|
**Commander**: Vantz Stockwell
|
||||||
|
**Mission**: Get Corrosion 100% wired up and finished
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pre-Op Intel Summary
|
||||||
|
|
||||||
|
- **6 BROKEN views** (will error on load)
|
||||||
|
- **12 PARTIAL views** (dead buttons, missing forms)
|
||||||
|
- **5 DB tables** with no TypeORM entity
|
||||||
|
- **6 controllers** missing security guards
|
||||||
|
- **~15 backend features** are stubs
|
||||||
|
- **4 Docker issues** to harden
|
||||||
|
|
||||||
|
## Execution Plan
|
||||||
|
|
||||||
|
| Wave | Focus | Agents | Status |
|
||||||
|
|------|-------|--------|--------|
|
||||||
|
| 1 | Critical Bug Fixes | 3 Sonnet parallel | COMPLETE |
|
||||||
|
| 2 | Missing Entities + Security | 2 Sonnet parallel | PENDING |
|
||||||
|
| 3 | Frontend Wiring | 3 Sonnet parallel | PENDING |
|
||||||
|
| 4 | Backend Completion | 2 Sonnet parallel | PENDING |
|
||||||
|
| 5 | Docker + Polish | 1 Sonnet | PENDING |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Wave 1: Critical Bug Fixes
|
||||||
|
|
||||||
|
**Status**: COMPLETE
|
||||||
|
**Started**: 2026-02-21
|
||||||
|
|
||||||
|
### Results (9 files modified)
|
||||||
|
1. Fixed 4 double-prefix URL bugs — removed `/api` prefix from `useApi()` calls in WipeAnalyticsView, MapAnalyticsView, PlayerRetentionView, StoreRevenueView
|
||||||
|
2. Fixed AdminDashboard quick-link paths — `/platform-admin/*` → `/admin/*`
|
||||||
|
3. Fixed MigrationView import — added auth header to raw fetch call
|
||||||
|
4. Removed ConsoleModule from app.module.ts — eliminates `/ws` namespace conflict with NatsBridgeGateway
|
||||||
|
5. Fixed Store module — `throw new Error()` → `NotFoundException` / `ForbiddenException`
|
||||||
|
6. Fixed payment-order entity FK — `webstore_subscription_id` now references `WebstoreSubscription` not `License`
|
||||||
@@ -19,7 +19,7 @@ let performanceChartInstance: ECharts | null = null
|
|||||||
const loadMapAnalytics = async () => {
|
const loadMapAnalytics = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const response = await api.get<MapAnalyticsSummary>(`/api/analytics/maps?range=${timeRange.value}`)
|
const response = await api.get<MapAnalyticsSummary>(`/analytics/maps?range=${timeRange.value}`)
|
||||||
analytics.value = response
|
analytics.value = response
|
||||||
await nextTick()
|
await nextTick()
|
||||||
renderCharts()
|
renderCharts()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { useApi } from '@/composables/useApi'
|
import { useApi } from '@/composables/useApi'
|
||||||
|
import { useAuthStore } from '@/stores/auth'
|
||||||
import { Download, Upload, FileText, Loader2 } from 'lucide-vue-next'
|
import { Download, Upload, FileText, Loader2 } from 'lucide-vue-next'
|
||||||
import { safeFileSize, safeDate } from '@/utils/formatters'
|
import { safeFileSize, safeDate } from '@/utils/formatters'
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ interface ExportRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const api = useApi()
|
const api = useApi()
|
||||||
|
const authStore = useAuthStore()
|
||||||
const exports = ref<ExportRecord[]>([])
|
const exports = ref<ExportRecord[]>([])
|
||||||
const isExporting = ref(false)
|
const isExporting = ref(false)
|
||||||
const isImporting = ref(false)
|
const isImporting = ref(false)
|
||||||
@@ -53,6 +55,7 @@ async function importData() {
|
|||||||
|
|
||||||
const response = await fetch('/api/migration/import', {
|
const response = await fetch('/api/migration/import', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
headers: { 'Authorization': 'Bearer ' + authStore.accessToken },
|
||||||
body: formData,
|
body: formData,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const loadRetentionData = async () => {
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const response = await api.get<RetentionResponse>(
|
const response = await api.get<RetentionResponse>(
|
||||||
`/api/analytics/retention?wipe_count=${wipeCount.value}`
|
`/analytics/retention?wipe_count=${wipeCount.value}`
|
||||||
)
|
)
|
||||||
retentionData.value = response
|
retentionData.value = response
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ const formatDate = (dateStr: string): string => {
|
|||||||
const loadTransactions = async () => {
|
const loadTransactions = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const data = await api.get<StoreTransaction[]>('/api/webstore/transactions')
|
const data = await api.get<StoreTransaction[]>('/webstore/transactions')
|
||||||
transactions.value = data
|
transactions.value = data
|
||||||
await nextTick()
|
await nextTick()
|
||||||
renderRevenueChart()
|
renderRevenueChart()
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const loadAnalytics = async () => {
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const range = rangeToParam(timeRange.value)
|
const range = rangeToParam(timeRange.value)
|
||||||
const response = await api.get<WipePerformanceMetrics>(`/api/analytics/wipes/performance?range=${range}`)
|
const response = await api.get<WipePerformanceMetrics>(`/analytics/wipes/performance?range=${range}`)
|
||||||
metrics.value = response
|
metrics.value = response
|
||||||
|
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ const kpiCards = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const quickLinks = [
|
const quickLinks = [
|
||||||
{ label: 'Licenses', description: 'Manage license keys and activations', icon: Key, route: '/platform-admin/licenses' },
|
{ label: 'Licenses', description: 'Manage license keys and activations', icon: Key, route: '/admin/licenses' },
|
||||||
{ label: 'Subscriptions', description: 'View module subscriptions and MRR', icon: CreditCard, route: '/platform-admin/subscriptions' },
|
{ label: 'Subscriptions', description: 'View module subscriptions and MRR', icon: CreditCard, route: '/admin/subscriptions' },
|
||||||
{ label: 'Users', description: 'Manage platform users and permissions', icon: Users, route: '/platform-admin/users' },
|
{ label: 'Users', description: 'Manage platform users and permissions', icon: Users, route: '/admin/users' },
|
||||||
{ label: 'Servers', description: 'Monitor connected game servers', icon: MonitorCog, route: '/platform-admin/servers' },
|
{ label: 'Servers', description: 'Monitor connected game servers', icon: MonitorCog, route: '/admin/servers' },
|
||||||
]
|
]
|
||||||
|
|
||||||
function formatValue(value: number | undefined, format: string): string {
|
function formatValue(value: number | undefined, format: string): string {
|
||||||
|
|||||||
Reference in New Issue
Block a user