Factorio Admin RCON Source code

Development

Code layout, test layers, internationalisation and the release chain.

Getting started#

cp .env.example .env.local   # at minimum ADMIN_PASSWORD and SESSION_SECRET
npm install
npm run dev                  # http://localhost:3000
npm run lint        # eslint
npm run typecheck   # next typegen && tsc --noEmit
npm test            # vitest
npm run build
Developing without a Factorio server The panel works: the status bar simply shows the RCON error. Your .env must set RCON_PASSWORD_FILE (for instance ./data/config/rconpw) or RCON_PASSWORD, otherwise the default path /factorio-config/rconpw does not exist on the host. To open the panel from another machine, set NEXT_DEV_ORIGINS, or hot reload is blocked.

Architecture#

src/
├── app/            pages + API routes (pages live under `[locale]/`)
├── components/     interface (console, actions, audit, metrics, dialog)
├── hooks/          server status, command execution, translations
├── i18n/           routing, dictionary loading, locale-aware navigation
├── lib/            shared client/server code (types, permissions, Lua templates)
└── server/         server-only code
    ├── actions/    business definitions, operator catalogue, execution
    ├── audit/      SQLite log
    ├── auth/       accounts, sessions, rate limiters
    ├── config/     validated environment variables (zod)
    ├── http/       route wrapper (session, permission, origin, logs)
    ├── metrics/    Docker client, collector, time series
    └── rcon/       service, queue, parsing, status cache

messages/           dictionaries (en.json is the reference)
docs/               this site

Two rules shape the server code:

Tests#

PathCovers
tests/<domain>/Pure logic: RCON queue and lifecycle, Lua escaping, catalogue validation, sessions, rate limiters, metrics, dictionaries
tests/api/The route handlers, called with a real Request: authentication, roles, validation, error codes, audit trail
tests/security/What must not regress: origin checking on every mutating route, X-Forwarded-For ignored without TRUST_PROXY, cookie flags, CSP nonce
tests/proxy/The session guard: no combination of cookie state may produce a redirect cycle

Internationalisation#

English (default, unprefixed) and French (/fr). The language is inferred from Accept-Language on the first visit, switchable from the status bar, and remembered in a cookie.

To add a language:

  1. copy messages/en.json to messages/<code>.json and translate the values;
  2. add the code to locales in src/i18n/routing.ts;
  3. add its name under localeSwitcher in every dictionary;
  4. npm test — the suite checks that all dictionaries expose exactly the same keys, that no catalogue action is missing a label, and that every message compiles as ICU.

Deliberately left untranslated: raw Factorio output, RCON commands and server logs — they are aimed at the operator.

Releases#

semantic-release reads commit messages following the conventional commits convention: fix: cuts a patch, feat: a minor, chore: releases nothing. The released version is tagged, CHANGELOG.md is updated, and the Docker images are pushed.

Dependabot#

.github/dependabot.yml drives dependency updates. Dependabot is native to GitHub: there is no app to install, the file is enough. Three ecosystems are tracked — npm, docker (base images) and github-actions.

The choice that matters is the commit prefix, because semantic-release reads it:

SourceCommit producedEffect
dependenciesfix(deps): …Cuts a version and rebuilds the image. That is deliberate: a security fix has to reach deployments.
devDependencieschore(deps-dev): …No release.
Docker, GitHub Actionschore(deps): …No release.

Updates are grouped to avoid one PR per package: next and react together — a major of one forces the major of the other — then eslint, vitest, tailwindcss, and the rest of the tooling in a single PR.

The Node version lives in four places Dependabot follows the Dockerfiles. Left to align by hand, in the same PR: engines in package.json and node-version in .github/workflows/ci.yml.
Security updates are a separate switch This file only governs version updates. Vulnerability fixes depend on Dependabot alerts, enabled under Settings → Advanced Security. Without them, a vulnerability waits for the weekly run.

This site#

The documentation is static HTML in docs/: no build step, no dependency, no external resource. Editing a page means editing an .html file; the styling lives in docs/assets/style.css. Crawlers are pointed at docs/robots.txt and docs/sitemap.xml (live under the Pages prefix). Submit the sitemap in Google Search Console: a project site on github.io does not own the host-root /robots.txt.

It is published by GitHub Pages. Two ways to enable it: