FTP/SFTP management plugin for the GameAP control panel. Installs and manages the gameap-files FTP/SFTP daemon on nodes and manages per-server FTP users, access rules, virtual path mounts and SSH keys.
Rust rewrite of the original Go plugin (plugin-gameap-files). Storage data,
node-side YAML files and the HTTP API are fully compatible, so the panel side
keeps working after the swap. That compatibility does not replace the one-time
node migration every 0.7.x install still needs — see
Upgrading from 0.7.x.
Читайте на других языках: Русский
- One-click gameap-files installation on Linux and Windows nodes (chained daemon tasks, live status tracking through daemon-task events with a poll/timeout fallback) and an Update button that re-runs the installer with the stored settings to upgrade an installed node
- Per-node FTP/SFTP configuration (
config.yamlis patched in place — keys the plugin does not own are preserved); the service is restarted through the system unit, the user unit of a rootless daemon, or the Windows service - FTP/SFTP users per game server: create/update/delete, Argon2id password hashing via the panel's crypto host service, one-time generated passwords
- Path access rules (
read/write/delete/list), virtual path mounts, SSH public keys - Users are mirrored to nodes as hot-reloaded YAML drop-ins under
<work_path>/.plugins/files/users.d/— the plugin's service directory inside the daemon work path, the one place the daemon lets a panel plugin write to; a relativehome_diris resolved against the node work path itself, not against that directory - Admin pages: all nodes with install status, all users grouped by node → server with filters
- Server abilities
ftp-users-view/ftp-users-managefor non-admin access control
| Layer | Module | Responsibility |
|---|---|---|
| ABI | src/lib.rs |
Plugin impl, register_plugin!, embedded frontend |
| Transport | src/http.rs, src/router.rs |
JSON error model, route table, dispatch |
| Handlers | src/handlers/* |
Per-route logic and DTOs, event handling |
| Services | src/services/* |
Users, sync, node setup orchestration, YAML patching, admin aggregation |
| Domain | src/domain/* |
Wire-compatible model, validation |
| Host seam | src/host_api.rs |
HostApi trait + WasmHost (wasm) / MockHost (tests) |
Business logic never touches the host ABI directly — everything goes through
the HostApi trait, so the whole router + handler stack runs natively under
cargo test against an in-memory mock host.
Panel KV storage (compatible with the Go plugin):
| Scope | Key | Contents |
|---|---|---|
| node | ftp:setup_status |
Installation state, task ids, timestamps |
| node | ftp:node_config |
FTP/SFTP configuration |
| server | ftp:users_list |
Username index (JSON array) |
| server | ftp:user:{username} |
Full user document (JSON) |
SERVER_DELETED— removes the server's users from storage and their YAML files from the node (node id taken from the event payload)DAEMON_TASK_COMPLETED/DAEMON_TASK_FAILED— matched against the install/download task ids stored in the node's setup status; a completed installation re-syncs every user of the node once (synced_after_installin the stored status) and removes the misplaced<work_path>/etc/gameap-files/users.d/*.yamlfiles older releases wrote
gameap-daemon confines a panel plugin's file operations to the node work path, so everything this plugin writes is addressed relative to it:
Node path (relative to work_path) |
Contents |
|---|---|
.plugins/files/config.yaml |
gameap-files configuration, patched by the settings dialog |
.plugins/files/users.d/<user>.yaml |
one drop-in per FTP user |
tools/install-files-linux.sh, tools/install-files-windows.ps1 |
installers fetched with get-tool |
tools/gameap-files/ (Windows) |
binary and service of the Windows install |
After replacing the plugin, click Update once on every node installed by
an earlier release: storage and API compatibility covers the panel side only,
the node side still has to be migrated. The installer moves
/etc/gameap-files into <work_path>/.plugins/files, the plugin re-syncs the
node's users from the legacy location and sweeps the files the old release had
left under <work_path>/etc/gameap-files.
Until Update is clicked, such a node keeps reading /etc/gameap-files,
Settings on it fails with "failed to download config", and users created
in the panel are not seen by gameap-files.
Path dependencies require a sibling checkout layout:
gameap-api/ # gameap/gameap — provides web/plugin-sdk for the frontend
gameap-proto/ # gameap/gameap-proto — provides rust/gameap-plugin-sdk
plugin-files/ # this repository
Requirements: Rust (pinned by rust-toolchain.toml, target wasm32-wasip1),
Node.js 22+, optionally binaryen
for wasm-opt.
make build # frontend (npm ci + vite) → cargo build → wasm-opt → files.wasm
make test # cargo test + frontend vitest
make lint # clippy (both targets) + vue-tscDevelopment loop:
cd frontend && npm run dev # rebuild frontend on change
cargo build --target wasm32-wasip1 --release # rebuild wasm
cd frontend && npm run debug # standalone UI against MSW mocksRequires GameAP 4.5 or newer. The plugin reads its own permission grants
through the gameap-host module, which older panels do not provide, and a host
module the panel does not export makes the wasm fail to load outright rather
than degrade.
Upload files.wasm via Administration → Plugins or drop it into the
panel's plugins directory and restart GameAP.
The manifest declares four permissions, and installing the plugin grants exactly those:
| Permission | Needed for |
|---|---|
files |
Writing and removing the users.d drop-ins and config.yaml on nodes through gameap-nodefs |
node_commands |
The version probe, the service restart and the installer's CMD_EXEC daemon tasks |
manage_servers |
Creating those daemon tasks |
listen_events |
Server-deleted and daemon-task events — without it the plugin is never called |
Updating an already installed plugin grants nothing new. After upgrading
from a build that declared nothing, an operator has to add the four by hand:
the Permissions action on the plugin's row in Administration → Plugins,
or PUT /api/admin/plugins/files/permissions. While the panel's
PLUGINS_PERMISSIONS_ENFORCE is off the missing grants are only reported — the
plugin names them in the log at load, and the admin dialog shows them; once
enforcement is on, the node calls above are refused.
All routes live under /api/plugins/files. See
openapi/openapi.yaml for the full specification:
node setup/status/config, user CRUD, access rules, virtual paths, SSH keys
and the admin endpoints.
- Bump
versioninCargo.tomlandfrontend/package.json(must match). - Merge, create a GitHub release with tag
v<version>. - The release workflow builds, GPG-signs and publishes the wasm to
plugins.gameap.dev (requires the
GPG_SIGNING_KEY/GAMEAP_DEPLOY_TOKENsecrets and theGAMEAP_PLUGIN_IDrepository variable).
MIT