Skip to content

Isidorsson/RuneCMS

Repository files navigation

wow-cms

Lean WoW private-server CMS in TypeScript. Replaces FusionCMS for people who don't want to run PHP. Targets TrinityCore, AzerothCore, and cMaNGOS via a single EmulatorAdapter interface.

Stack: Bun · SvelteKit · TailwindCSS · Drizzle ORM · mysql2 · Stripe · Zod.

v1 features

  • Public registration + login (SRP6 v1 and legacy SHA1, multi-realm)
  • Account dashboard: change password / email, character list
  • Public armory: search + character profile
  • Admin: realms CRUD, account search, ban/unban, send in-game mail with item attachments, audit log
  • Stripe donation page (records intent — manual fulfillment, by design)

Out of scope: plugin system, vote-rewards, item shop, i18n, forums, Battle.net auth, transmog viewer.

Quickstart

Assumes an emulator stack (TrinityCore / AzerothCore / cMaNGOS) is running with its MySQL reachable. The CMS metadata DB (wow_cms) sits beside the emulator's auth / characters / world.

bun install
cp .env.example .env
# edit CMS_DB_URL — point at a MySQL user that can write to `wow_cms`
# e.g. CMS_DB_URL=mysql://root:<rootpw>@127.0.0.1:3306/wow_cms

bun run db:push    # creates the 30 CMS tables (idempotent)
bun run dev

Open http://localhost:5173. First visit redirects to /setup — a one-page wizard that tests your emulator MySQL, creates the realm row, creates the first admin (real SRP6 account, usable in-game), and logs you into /admin.

CLI alternative for scripted installs:

bun run db:seed:dev          # CREATE DATABASE wow_cms + insert dev realm
bun run db:make-admin <user> # promote a cms_user to admin

How auth works

CMS has its own user table (cms_user) backlinked to the emulator's auth.account row by (realm_id, account_id). One password — the one the emulator already stores via SRP6 (Trinity / AzerothCore) or SHA1 (cMaNGOS). The CMS doesn't store passwords; it verifies against the emulator's hash on every login.

Registration is dual-write: auth.account first, then cms_user. If the emulator row exists already, the CMS row is created lazily on first login — self-healing, no orphans.

CMS admin and in-game GM rank are independent. A CMS-only moderator may have no in-game powers; a senior GM may have no CMS access. Adapters expose getGmLevel(accountId) so the admin UI can display in-game rank as context, but it never feeds into authorization. Promote manually with bun run db:make-admin <username>.

End-to-end verification

The only test that proves SRP6 actually works:

  1. /register → create testuser / testpass.
  2. WoW 3.3.5 client → connect to 127.0.0.1 → log in as testuser. Must succeed.
  3. Make a character, log out.
  4. CMS armory → look up that character. Profile renders.
  5. Admin → Send Mail → recipient = your character, items = 49623:1 (Shadowmourne).
  6. Log back in-game, mail tab shows the item.
  7. Admin → Users → Search → Ban. Next login attempt rejected.

Architecture

src/lib/server/emulator/ holds the adapter interface, factory (realm row → cached adapter), and per-emulator implementations (trinity.ts, azerothcore.ts, mangos.ts). Hash logic lives in hash/ (SRP6 v1 with bigint modPow + LE byte order; SHA1(UPPER(user):UPPER(pass)) for legacy). db/ holds Drizzle schema + pool. auth/ holds session cookies and Zod registration. acl.ts is the role + permission catalog. SvelteKit routes under src/routes/ are SSR-only.

DB ownership

your TrinityCore / AzerothCore / cMaNGOS MySQL
├── auth          ◄── owned by emulator, read+write by EmulatorAdapter (mysql2)
├── characters    ◄── owned by emulator, read by EmulatorAdapter
├── world         ◄── owned by emulator, read by EmulatorAdapter
└── wow_cms       ◄── owned by Drizzle (this project)

Drizzle never touches auth / characters / world. The adapter never touches wow_cms. Two non-overlapping ownership zones.

Adding a new emulator

  1. Implement EmulatorAdapter in src/lib/server/emulator/adapters/<name>.ts.
  2. Register it in factory.ts.
  3. Add the kind to EmulatorKind in types.ts and the mysqlEnum on realm.emulator in schema.ts.

Login, register, admin pages are adapter-agnostic.

Local dev

No separate Docker DB for the CMS — wow_cms lives inside your existing emulator MySQL. Drizzle owns that schema; emulator DBs are read/written only by EmulatorAdapter.

If your emulator MySQL lives elsewhere than CMS_DB_URL, override per-DB URLs on the dev realm row via DEV_AUTH_DB_URL / DEV_CHAR_DB_URL / DEV_WORLD_DB_URL.

MOCK_DB=true short-circuits Drizzle + adapter for offline UI smoke tests. MOCK_DB=false runs the real path. Don't commit .env.

Inline temporary code prefixed // DEV-FIXTURE: — grep before shipping.

Why not FusionCMS-style modules / multi-theme?

Modules and runtime theme switching are ~40% of FusionCMS's complexity and ~5% of the user value. Fork the repo to change look-and-feel. A "module" is just src/routes/<thing>.

License

MIT.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors