Skip to content

feat: ingame api#40

Merged
Maximus7474 merged 17 commits into
mainfrom
feat/ingame-api
Jul 4, 2026
Merged

feat: ingame api#40
Maximus7474 merged 17 commits into
mainfrom
feat/ingame-api

Conversation

@FjamZoo

@FjamZoo FjamZoo commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

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

  • Tested in development mode
  • Passes typecheck
  • Builds & runs on Windows
  • Builds & runs on Linux

Additional Notes

N/A

FjamZoo added 12 commits July 3, 2026 17:31
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 Maximus7474 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just those small details, otherwise good to merge for myself

Comment thread apps/resource/src/server/exports.ts
Comment thread apps/core/src/routes/internal/ingame.ts
FjamZoo and others added 4 commits July 4, 2026 12:43
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
@Maximus7474 Maximus7474 merged commit f254b3c into main Jul 4, 2026
6 checks passed
@FjamZoo FjamZoo deleted the feat/ingame-api branch July 4, 2026 14:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants