feat: ingame api#40
Merged
Merged
Conversation
The resource start/stop endpoints interpolated the client-supplied resource name straight into an FXServer console command written to stdin, so a newline injected arbitrary console commands (rotate the internal API token, grant ACE perms, quit) — bypassing the CONSOLE_ACCESS gate with only RESOURCE_LIST. Add isValidResourceName() (rejects whitespace/newlines/metacharacters, 1-64 char charset) and reject invalid names with 400 in both the /resources and /server/resource action handlers. Also send a proper JSON body on the /server/resource responses (they previously returned reply.code(200) with no .send(), hanging the request).
fxserver.executablePath/serverDataPath/serverConfigPath were writable by any SETTINGS_ACCESS admin and are later passed to Bun.spawn / used to locate the config that gets exec'd, i.e. host code execution. A non-master admin could point the executable at an arbitrary binary and gain the daemon's privileges on the next start. Add SETTINGS_MASTER_ONLY_KEYS and canWriteSetting(); the settings write handler now enforces MASTER for those keys while ordinary keys still only need SETTINGS_ACCESS.
The drizzle journal (0000-0003) is behind the hand-maintained TS migration registry (0000-0006). In that state `drizzle-kit generate` reuses an already-registered version number, and scripts/migrate.ts filtered it out as "up to date" — silently dropping the new migration so a release could ship code querying a column that was never created. Add planJournalMigrations() and make migrate.ts abort with a clear error when any registry version has no journal entry. This is a guard only; realigning the drizzle journal/snapshots so generate resumes at 0007 is a separate follow-up.
The setup wizard wrote FXServer paths to 'executable'/'serverDataPath', but every consumer reads 'fxserver.executablePath'/'fxserver.serverDataPath' (getFxServerValues). The values landed in orphan rows, so a fresh install started from the './FXServer' default and failed. /setup/detect also read the nonexistent cfg.executable field. Write the fxserver.-prefixed keys and read cfg.executablePath.
apps/core had no typecheck script, so `turbo typecheck` skipped it and `Bun.build` type-strips without checking — a type-broken core shipped green. Core currently has real tsc errors, including 4 unchecked-index narrowing issues in the players repository. Add a typecheck script to apps/core, guard the destructured returning() rows in players.ts, add a Verify Types CI job that gates the build, and fix the PR paths filter (bun.lockb -> bun.lock, watch .github/workflows).
/api/setup was unauthenticated and the server binds 0.0.0.0, so anyone who could reach the panel before the operator finished setup could claim the MASTER account. Generate a one-time setup token on first boot and print the setup URL (…/setup?token=…) to the console. /api/setup and /setup/detect now require a matching x-setup-token (constant-time compare) and the token is cleared once the master account is created. The webpanel reads ?token= from the URL and forwards it on both requests.
Logout read request.cookies.session_id, but the session cookie is named fxm_session (COOKIE_NAME), so deleteSession never ran — the cookie was cleared in the browser while the session stayed valid server-side for its full 7-day TTL. Read the correct cookie so the DB session is deleted.
The login route had no throttling, allowing unbounded online password brute force, and verifyPassword returned immediately for unknown usernames — a timing oracle for account enumeration. Add an in-memory LoginRateLimiter (5 failures per source IP -> 15 min lockout, cleared on success) and return 429 with Retry-After when locked. verifyPassword now runs a dummy bcrypt verify on the unknown-user path so response timing no longer reveals whether an account exists.
A 401 from any request left the panel stuck: the WebSocket reconnect loop retried forever and nothing cleared the auth state, so the user stared at stale data behind a "reconnecting" state until a manual reload. Add a global unauthorized handler: QueryService signals on any 401 and AuthProvider clears the user, shows a "session expired" toast, and redirects to /login (guarded so it only fires while authenticated, once per expiry). Setting user to null also disposes the WS effect, stopping the loop. The reconnect path first probes /auth/me, so an idle page with only a live socket detects the expiry too instead of reconnecting forever.
Maximus7474
reviewed
Jul 3, 2026
Maximus7474
left a comment
Collaborator
There was a problem hiding this comment.
Just those small details, otherwise good to merge for myself
The target type advertised Partial<PlayerIdentifiers> but only license was honored at runtime. Generalize the DB lookup to findByIdentifier (findByLicense now delegates), and accept fivem/discord/steam across the resolver, /players/lookup, and the resource export.
The acting admin is already captured by adminId and the API origin by source + resource, so the author metadata was duplicated. Keep the author value where it still feeds the txAdmin compat payloads and the process-manager stop/restart messages.
Maximus — 13:46 Wait a minute, a bit daft to have a serverStart export no ? How is a a resource going to trigger that legitemately 🤣 FjamZoo — 13:49 Ohh good point lmao I just did full parity ngl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This PR adds API parity between the webpanel and ingame, meaning any action (apart from a few) can be done via either the webpanel and/or the api.
depends on #39 so that must be merged before hand.
Tests
Additional Notes
N/A