diff --git a/CHANGELOG.md b/CHANGELOG.md index 805c014..4b5c3fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Fixed (Site Audit — Fake Data, Resilience, Fonts — 2026-06-11) + +**Frontend:** +- `SetupWizardView.vue` — Replaced fake install instructions (`get.corrosionmgmt.com | sh` install script and `corrosion-agent` binary, neither of which exists) with the real host-agent download + run commands matching ServerView; multi-game copy on the completion step +- Marketing views (Landing, Pricing, HowItWorks, Roadmap, EarlyAccess) — Replaced "View live demo" CTA (no demo exists; it linked to the panel login) with an honest "Sign in" link +- `ErrorBoundary.vue` — Error state now resets on route change (previously one failed view bricked the entire SPA, including marketing pages, until manual reload); added `content` variant +- `DashboardLayout.vue` — Routed views are now wrapped in a content-scoped ErrorBoundary so the sidebar/topbar survive a view failure instead of the whole panel unmounting +- `index.html` / `styles/tokens/fonts.css` — Google Fonts moved from CSS `@import` to `` tags. The bundler silently dropped the mid-bundle `@import`, so production shipped system fallback fonts (Geist/JetBrains Mono/Oxanium never loaded) +- `StatusPageView.vue` — Platform KPIs show "—" until the first successful fetch instead of fake zeros +- `LoginView.vue` — Added missing "Forgot password?" link (route + backend endpoint already existed) + +**Backend (NestJS):** +- `AdminSeedService` (new, auth module) — Bootstraps a super-admin user + active license from `ADMIN_EMAIL`/`ADMIN_PASSWORD`/`ADMIN_USERNAME`/`ADMIN_LICENSE_KEY` when the users table is empty. A fresh deploy previously had a schema but no possible login. Compose already passes the env vars + +**Purpose:** Findings from the full-site fake-data audit. Show real data or honest empty states — never invented values, dead URLs, or fabricated zeros. + ### Fixed (Safe Formatting Utilities — 2026-02-15) **Frontend:** diff --git a/backend-nest/src/modules/auth/admin-seed.service.ts b/backend-nest/src/modules/auth/admin-seed.service.ts new file mode 100644 index 0000000..391d4a4 --- /dev/null +++ b/backend-nest/src/modules/auth/admin-seed.service.ts @@ -0,0 +1,82 @@ +import { Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import * as argon2 from 'argon2'; +import { randomBytes } from 'crypto'; +import { User } from '../../entities/user.entity'; +import { License } from '../../entities/license.entity'; + +/** + * Bootstraps the first admin account on a fresh database. + * + * A fresh deploy builds the schema via docker-entrypoint-initdb.d but contains + * zero users, so the panel has no possible login. If ADMIN_EMAIL and + * ADMIN_PASSWORD are set and the users table is empty, this creates a + * super-admin user plus an active license — the same rows the register flow + * would create. It never runs against a database that already has users. + */ +@Injectable() +export class AdminSeedService implements OnApplicationBootstrap { + private readonly logger = new Logger(AdminSeedService.name); + + constructor( + private readonly config: ConfigService, + @InjectRepository(User) private readonly userRepository: Repository, + @InjectRepository(License) private readonly licenseRepository: Repository, + ) {} + + async onApplicationBootstrap(): Promise { + try { + await this.seedAdminIfEmpty(); + } catch (err) { + // A failed seed must not take the API down — surface it loudly and move on + this.logger.error(`Admin bootstrap failed: ${(err as Error).message}`, (err as Error).stack); + } + } + + private async seedAdminIfEmpty(): Promise { + const email = this.config.get('admin.email'); + const password = this.config.get('admin.password'); + const username = this.config.get('admin.username') || 'Commander'; + + if (!email || !password) { + this.logger.log('Admin bootstrap skipped: ADMIN_EMAIL / ADMIN_PASSWORD not set'); + return; + } + + const userCount = await this.userRepository.count(); + if (userCount > 0) { + return; + } + + const password_hash = await argon2.hash(password); + const user = this.userRepository.create({ + email: email.toLowerCase(), + username, + password_hash, + email_verified: true, + is_super_admin: true, + }); + await this.userRepository.save(user); + + const licenseKey = this.config.get('admin.licenseKey') || this.generateLicenseKey(); + const license = this.licenseRepository.create({ + license_key: licenseKey, + owner_user_id: user.id, + status: 'active', + modules_enabled: [], + webstore_active: false, + }); + await this.licenseRepository.save(license); + + this.logger.log(`Bootstrap admin created: ${user.email} (license ${license.license_key})`); + } + + private generateLicenseKey(): string { + const part1 = randomBytes(2).toString('hex').toUpperCase(); + const part2 = randomBytes(2).toString('hex').toUpperCase(); + const part3 = randomBytes(2).toString('hex').toUpperCase(); + return `CORR-${part1}-${part2}-${part3}`; + } +} diff --git a/backend-nest/src/modules/auth/auth.module.ts b/backend-nest/src/modules/auth/auth.module.ts index 62b8807..881648f 100644 --- a/backend-nest/src/modules/auth/auth.module.ts +++ b/backend-nest/src/modules/auth/auth.module.ts @@ -5,6 +5,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { AuthController } from './auth.controller'; import { AuthService } from './auth.service'; +import { AdminSeedService } from './admin-seed.service'; import { JwtStrategy } from './jwt.strategy'; import { User } from '../../entities/user.entity'; import { License } from '../../entities/license.entity'; @@ -27,7 +28,7 @@ import { TeamMember } from '../../entities/team-member.entity'; TypeOrmModule.forFeature([User, License, Role, TeamMember]), ], controllers: [AuthController], - providers: [AuthService, JwtStrategy], + providers: [AuthService, AdminSeedService, JwtStrategy], exports: [AuthService], }) export class AuthModule {} diff --git a/frontend/index.html b/frontend/index.html index 35a0aca..919a143 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -9,6 +9,14 @@ Corrosion Management + + + +