feat: hectic-lab: mechabellum for lismy

This commit is contained in:
2026-05-23 19:40:36 +00:00
parent fb6ee3cb54
commit 934c02609a
4 changed files with 165 additions and 0 deletions

21
flake.lock generated
View File

@@ -668,6 +668,26 @@
"type": "github"
}
},
"mechabellum-replay-analysis": {
"inputs": {
"nixpkgs": [
"nixpkgs-fixed"
]
},
"locked": {
"lastModified": 1779564179,
"narHash": "sha256-rcR0Sq6782vF5cilXvZJHUG/MMBG6JtHoMXiuBYlfEs=",
"ref": "refs/heads/master",
"rev": "087d4dfd1d8517bc32b55404125ad3ca5746ed6f",
"revCount": 101,
"type": "git",
"url": "ssh://git@github.com/LysmiMx/mechabellum-replay-analysis.git"
},
"original": {
"type": "git",
"url": "ssh://git@github.com/LysmiMx/mechabellum-replay-analysis.git"
}
},
"nix-minecraft": {
"inputs": {
"flake-compat": "flake-compat_3",
@@ -962,6 +982,7 @@
"home-manager": "home-manager",
"hyprland": "hyprland",
"impermanence": "impermanence",
"mechabellum-replay-analysis": "mechabellum-replay-analysis",
"nix-minecraft": "nix-minecraft",
"nixos-anywhere": "nixos-anywhere",
"nixos-hardware": "nixos-hardware",

View File

@@ -58,6 +58,12 @@
url = "git+ssh://git@github.com/liquizz/hectic-landing.git";
inputs.nixpkgs.follows = "nixpkgs-fixed";
};
mechabellum-replay-analysis = {
# NOTE(yukkop): private repo - SSH access required.
# Only evaluated when nixosConfigurations."hectic-lab|x86_64-linux" is built.
url = "git+ssh://git@github.com/LysmiMx/mechabellum-replay-analysis.git";
inputs.nixpkgs.follows = "nixpkgs-fixed";
};
};
outputs = {

View File

@@ -31,6 +31,7 @@ in {
inputs.hectic-landing.nixosModules.hectic-landing
(import ./containers.nix { inherit flake self inputs; })
(import ./mechabellum.nix { inherit flake self inputs domain sslOpts; })
(import (./. + "/sentinèlla.nix") { inherit flake self inputs domain sslOpts; })
];

View File

@@ -0,0 +1,137 @@
{
inputs,
flake,
self,
domain,
sslOpts,
...
}: {
pkgs,
lib,
...
}: let
system = pkgs.stdenv.hostPlatform.system;
mechDomain = "mechabellum.${domain}";
apiHost = "127.0.0.1";
apiPort = 8010;
mechPackages = inputs.mechabellum-replay-analysis.packages.${system};
mechabellumBackend = pkgs.python312.withPackages (_: [
mechPackages.backend
]);
mechabellumFrontend = mechPackages.frontend.overrideAttrs (_: {
VITE_API_BASE_URL = "https://${mechDomain}";
VITE_PUBLIC_APP_URL = "https://${mechDomain}";
});
stateDir = "/var/lib/mechabellum";
staticDir = "${stateDir}/static";
in {
systemd.tmpfiles.rules = [
"d ${stateDir} 0750 root root -"
"d ${stateDir}/replays 0750 root root -"
"d ${stateDir}/analysis_batches 0750 root root -"
"d ${stateDir}/analysis_reports 0750 root root -"
"d ${staticDir} 0755 root root -"
];
systemd.services.mechabellum-api = {
description = "Mechabellum Replay Analysis API";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
unitConfig = {
ConditionPathExists = [
"${staticDir}/unit_id_to_name.json"
"${staticDir}/unit_footprints.json"
];
};
serviceConfig = {
Type = "simple";
ExecStart = ''
${mechabellumBackend}/bin/uvicorn \
mechabellum_replay.backend.app:app \
--host ${apiHost} \
--port ${builtins.toString apiPort}
'';
WorkingDirectory = stateDir;
StateDirectory = "mechabellum";
Restart = "always";
RestartSec = "5s";
DynamicUser = true;
ProtectSystem = "strict";
ProtectHome = true;
NoNewPrivileges = true;
ReadWritePaths = [ stateDir ];
};
environment = {
DATA_DIR = stateDir;
STATIC_DATA_DIR = staticDir;
CORS_ALLOWED_ORIGINS = "https://${mechDomain}";
};
};
systemd.services.mechabellum-worker = {
description = "Mechabellum Replay Analysis worker";
after = [ "network-online.target" "mechabellum-api.service" ];
wants = [ "network-online.target" "mechabellum-api.service" ];
wantedBy = [ "multi-user.target" ];
unitConfig = {
ConditionPathExists = [
"${staticDir}/unit_id_to_name.json"
"${staticDir}/unit_footprints.json"
];
};
serviceConfig = {
Type = "simple";
ExecStart = ''
${mechabellumBackend}/bin/python -m mechabellum_replay.backend.worker
'';
WorkingDirectory = stateDir;
StateDirectory = "mechabellum";
Restart = "always";
RestartSec = "5s";
DynamicUser = true;
ProtectSystem = "strict";
ProtectHome = true;
NoNewPrivileges = true;
ReadWritePaths = [ stateDir ];
};
environment = {
DATA_DIR = stateDir;
STATIC_DATA_DIR = staticDir;
};
};
services.nginx.virtualHosts."${mechDomain}" = sslOpts // {
forceSSL = true;
root = mechabellumFrontend;
locations."/api/" = {
proxyPass = "http://${apiHost}:${builtins.toString apiPort}/api/";
extraConfig = ''
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
'';
};
locations."/" = {
tryFiles = "$uri $uri/ /index.html";
};
};
warnings = [
''
mechabellum.${domain} was enabled, but the upstream repo does not package
data/static/unit_id_to_name.json or data/static/unit_footprints.json.
Copy those files into ${staticDir} on the server before starting the API
and worker units.
''
];
}