legacy-cityhall
Overview
What legacy-cityhall does and how its pieces fit together.
legacy-cityhall
legacy-cityhall is a full city hall system for FiveM: a job board, an application/vetting pipeline, and a document (ID/licence-style) issuance service, all fronted by NPC interaction points and a React-based NUI.
Core pieces
- NPCs & blips — spawned client-side from
Config.Locations, usingexports['legacy-lib']:Target()to add interactions that open the NUI. - NUI (web/) — a separate Vite + React + Tailwind project. Every NUI action forwards to a like-named
lib.callbackon the server (e.g.submitApplicationNUI action →legacy-cityhall:submitApplicationcallback). - Jobs — whitelisted or public jobs are registered in
cityhall_jobswith a companioncityhall_listingsrow describing the public job-board posting (title, salary, requirements, form fields, etc.). - Applications — players submit applications against whitelisted jobs; managers/interviewers move them through a status state machine (
pending→reviewing/interview→accepted/denied), with reviewer notes and votes. - Documents — players purchase documents (ID cards, licences, etc.) defined in
cityhall_doc_types; issued documents live incityhall_documentswith a uniqueCH-XXXXXXserial, can be verified publicly by serial, revoked by admins, or reported stolen/lost by their holder. - Audit log — admin actions (job registration, doc type changes, etc.) are recorded to
cityhall_audit_logand browsable via a paginated, filterable callback. - Webhooks — Discord webhook URLs (
Config.Webhooks) fire on key lifecycle events (application submitted/accepted/denied/withdrawn, document issued/revoked, job registered).
Two integration surfaces
Most server-side functionality is exposed twice:
- As an ox_lib callback (e.g.
legacy-cityhall:registerJob) — used by the NUI, permission-checked against the calling player andConfig.AdminGroups/job ranks. - As a direct Lua export (e.g.
RegisterJob,IssueDocument) — callable from your own server scripts with no NUI involved.
These two surfaces are not always equivalent: several direct exports (notably IssueDocument, RevokeDocument, CreateApplication, UpdateApplicationStatus) intentionally skip business-rule checks (pricing, licence checks, background checks, serial uniqueness, permission checks, auto-hire on accept) that their callback counterparts enforce. See the Integration page for details before choosing which one to call.
See Installation for dependencies and database setup, Configuration for every Config key, Integration for working code samples, and Troubleshooting for known gotchas.