Skip to content

feat(web-ui): CMaNGOS web management interface - #44

Draft
inpencil-bot wants to merge 18 commits into
Byloth:masterfrom
inpencil-bot:feature/web-ui
Draft

feat(web-ui): CMaNGOS web management interface#44
inpencil-bot wants to merge 18 commits into
Byloth:masterfrom
inpencil-bot:feature/web-ui

Conversation

@inpencil-bot

Copy link
Copy Markdown

Work in progress — Nuxt 4.5.2 scaffold, SRP6 auth foundation, and action plan for the full web UI.

See issue #6 for context and PLAN.md in this branch for the roadmap.

…S AsHexStr

Session-1 assumption (little-endian reversed hex) was wrong: AccountMgr
stores BN_bn2hex(v)/BN_bn2hex(s) — big-endian, uppercase, unpadded — and
realmd parses with BN_hex2bn. A little-endian row would be undetectable
until a retail client failed to log in. x computation (LE salt || LE h1,
digest read big-endian) confirmed correct against SRP6.cpp.

Known vector ADMIN/ADMIN verified independently with Python hashlib/pow:
28AE3C33...157BB. Salt now emitted uppercase to match BN_bn2hex.
7 bun tests pass.
…resolution

- server/database/schema.ts: faithful descriptors for realmd.account,
  realmlist, uptime, characters (DDL verified upstream 2026-08-26);
  descriptors only, no migrations ever
- server/database/config.ts: pure resolveCmangosConfig — derives
  <core>realmd/<core>characters, errors name the exact NUXT_* vars
- server/database/client.ts: lazy mysql2 pools cached on globalThis
- drizzle-orm 0.45.2 + mysql2 3.24.2; 8 new unit tests (17 total pass)
- server/utils/auth.ts: SRP6a auth adapter (create + verify credentials),
  BN_hex2bn-tolerant comparison, legacy-sha_pass_hash seam documented
- server/utils/validation.ts: registration input validation, uppercases
  username per CMaNGOS convention, rules from WoWSimpleRegistration ref
- test/auth.test.ts + test/validation.test.ts: 13 new tests, all pass
  (49 total across 4 suites)

Phase 1 steps 6 (partial) and 5-adjacent: adapter + validation ready;
next: POST /api/auth/register endpoint, then login/logout/me, then
server status, then BlizzLike shell.
…ealed-cookie sessions

- POST /api/auth/register: validate, check duplicate username, compute SRP6
  credentials, INSERT into realmd.account via Drizzle
- POST /api/auth/login: verify SRP6 v against submitted password, open sealed
  cookie session (h3 useSession), reject locked accounts
- POST /api/auth/logout: clear session cookie
- GET /api/auth/me: return session identity without DB read
- server/utils/session.ts: thin wrapper over h3 useSession with config-driven
  password, maxAge, httpOnly/Lax/secure cookie attributes
- schema.ts: document joindate as DB-owned, remove .defaultNow() drift
- srp6.ts: defensive copy before crypto.subtle.digest (SharedArrayBuffer guard)
- bun-types devDependency + tsconfig files for stricter type checking
- test type annotations tightened

All 49 tests pass. Build completes. Live DB round-trip untested on dev box
(no docker/mysql); must be verified on homelab.
@inpencil-bot

Copy link
Copy Markdown
Author

Phase 1 backend — complete (sessions 3–5)

The whole server-side surface of Phase 1 is now on this branch:

  • Runtime config + env contract (313d070): NUXT_CMANGOS_CORE (classic|tbc|wotlk), DB host/user/pass, derived <core>realmd/<core>characters names, NUXT_SESSION_PASSWORD (≥32 chars). web-ui/.env.example documents the mapping from the stack's own MANGOS_DBHOST/DBUSER/DBPASS/WOW_VERSION.
  • Drizzle layer (06129b3): faithful descriptors for realmd.account, realmlist, uptime, characters (DDL verified upstream, byte-identical across the three cores). Descriptors only — no migrations, ever. Pure config resolver with operator-facing error messages; lazy mysql2 pools.
  • Auth adapter (8a07b33): SRP6a-only — all three current cores verified to use it; the legacy sha_pass_hash seam is documented but deliberately not supported.
  • Endpoints (a3c6bc9): POST /api/auth/register (validation per WoWSimpleRegistration rules, duplicate check, SRP6 credentials, INSERT), login (verifier recompute + compare, locked-account rejection, sealed-cookie session), logout, me.
  • GET /api/server/status (3b8f349): online = NOT(realmflags & 0x2) AND fresh uptime row (10-min update interval + 60s buffer), plus live online-character count. details block says why it's down.

Testing: 49 bun unit tests green (srp6, config, auth, validation); Nuxt build green. ⚠️ Endpoint behavior against a live MariaDB is untested — this dev box has no docker/mysql client. First real run should happen on the homelab before this leaves Draft.

Two known type-only issues, queued for the next session: joindate insert typing in register.post.ts, bun:test resolution in root vue-tsc run (tests themselves pass).

Remaining for Phase 1: the BlizzLike frontend shell (layout, palette, register/login/status pages).

… CmangosCore

- schema.ts: mirror DDL DEFAULT NOW() via .default(sql`NOW()`) so the
  insert type treats joindate as optional (Drizzle omits it at runtime;
  MariaDB applies its own default). Descriptors-only invariant kept.
- tsconfig.json: opt into bun-types (Nuxt's generated config sets
  types: []), fixing bun:test resolution in test files.
- auth.test.ts: type the test.each param as CmangosCore.

vue-tsc --noEmit clean, 49/49 tests pass, build green.
… status

Design system recreated from scratch in pure CSS (no Blizzard assets, no
copied code/values — celguar/spp-classics visual reference only). App shell
with auth-aware header, useAuth composable, ServerStatus widget (30s client
poll, offline reason from details block), landing/login/register pages with
per-field server errors.

NOTE: build/vue-tsc verification of these files is PENDING (session cut
short) — queued as first task of session 7, see PLAN.md.
@inpencil-bot

Copy link
Copy Markdown
Author

Phase 1 complete — user side, read-mostly, backend + frontend shell.

This batch (3 commits, 4a71059..dfd27b8):

  • Type fixes (4a71059): the two known type-only issues from the backend sessions are resolved — joindate now mirrors the DDL default via .default(sql\NOW()`)(Drizzle omits the column at runtime, MariaDB applies its ownDEFAULT NOW(); upstream realmd.sqlre-verified), and rootvue-tscresolvesbun:testvia explicitbun-types`.
  • BlizzLike frontend shell (e8dc8a2): the last Phase 1 item. Full design system recreated from scratch in pure CSS — own palette, border treatment, typography; no Blizzard assets, no copied code or values (celguar/spp-classics served as visual reference only, their license forbids reuse). No webfonts, no images — everything is CSS. App shell with auth-aware header, useAuth composable (sealed-cookie session, SSR cookie forwarding), live server-status widget (30s client poll, offline reason surfaced from the details block, graceful "unavailable" state), and landing / login / register pages. Register maps the 400 data.errors per field and the 409 to the username field; it does not auto-login (explicit login step — deliberate).
  • Docs (dfd27b8): PLAN.md sessions 6–7.

Verification (dev box, no DB): vue-tsc clean · 49/49 bun test · bun run build green · SSR smoke on dev + production build: / /login /register render, /api/auth/me{account: null}, status endpoint 500s without DB and the widget shows its designed unavailable state · production build sanitizes unexpected errors (no query text leaked).

Still untested, as flagged before: every DB round-trip against live MariaDB (register INSERT, login verify, status queries). No docker/mysql on this box — the first homelab run must exercise register → login → status end to end. The dedicated least-privilege DB user (SELECT broadly, INSERT/UPDATE only on realmd.account) is part of Phase 4 packaging; until then the configured user needs at least those grants.

Decisions taken on your behalf, full list in PLAN.md (session 6): single-realm assumption (first realmlist row), staleness threshold 10 min + 60 s, session cookie httpOnly + SameSite=Lax + 7-day + secure opt-in, no email verification in v1, no auto-login after register, 30 s status polling, Blizzard non-affiliation line in the footer, design system from scratch (license-clean by construction).

Phase 1 done. Remaining per PLAN.md: Phase 2 (self-service), Phase 3 (admin — blocked on #27), Phase 4 (packaging: Dockerfile, compose profile, least-privilege provisioning, docs page after #43 merges).

The "Create account" nav link reuses the hero .btn style (gold
background), but .site-nav a / a:hover / a.router-link-active
overrode its text color with --mist / --gold-bright — mist-on-gold,
illegible. Scope the nav rules with :not(.btn) so buttons keep their
own colors, per review of PR Byloth#44. No !important.
@inpencil-bot

Copy link
Copy Markdown
Author

Review fix pushed (49db4e9): the .site-nav color rules are now scoped :not(.btn) — the nav Create account button keeps its own dark-on-gold colors and typography again, and hover/active states come from .btn itself. No !important used. Verified: 49/49 tests, vue-tsc clean, build green, production SSR smoke renders the header correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant