mirror of
https://github.com/agessaman/meshcore-bot.git
synced 2026-10-23 14:22:22 +00:00
Adding webviewer nixos option and improving tests
Use with
services.meshcore-bot = {
enable = true;
webviewer.enable = true;
}
This commit is contained in:
1 parent
ae45d1e95b
commit
18996cdd78
4 files changed
+127
-60
No files matched your search
@@ -76,6 +76,29 @@ sudo systemctl status meshcore-bot
|
||||
|
||||
See [SERVICE-INSTALLATION.md](SERVICE-INSTALLATION.md) for detailed service installation instructions.
|
||||
|
||||
## NixOS
|
||||
Use the Nix flake via flake.nix
|
||||
```nix
|
||||
meshcore-bot.url = "github:agessaman/meshcore-bot/";
|
||||
```
|
||||
|
||||
And in your system config
|
||||
|
||||
```nix
|
||||
{
|
||||
imports = [inputs.meshcore-bot.nixosModules.default];
|
||||
services.meshcore-bot = {
|
||||
enable = true;
|
||||
webviewer.enable = true;
|
||||
settings = {
|
||||
Connection.connection_type = "serial";
|
||||
Connection.serial_port = "/dev/ttyUSB0";
|
||||
Bot.bot_name = "MyBot";
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The bot uses `config.ini` for all settings. Key configuration sections:
|
||||
|
||||
Generated
+6
-5
@@ -5,16 +5,17 @@
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1765835352,
|
||||
"narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=",
|
||||
"lastModified": 1767609335,
|
||||
"narHash": "sha256-feveD98mQpptwrAEggBQKJTYbvwwglSbOv53uCfH9PY=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "a34fae9c08a15ad73f295041fec82323541400a9",
|
||||
"rev": "250481aafeb741edfe23d29195671c19b36b6dca",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "flake-parts",
|
||||
"type": "indirect"
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
|
||||
@@ -47,6 +47,22 @@
|
||||
description = "The meshcore-bot package to use.";
|
||||
};
|
||||
|
||||
webviewer = {
|
||||
enable = lib.mkEnableOption "MeshCore Bot Web Viewer service";
|
||||
|
||||
host = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "127.0.0.1";
|
||||
description = "Host to bind the web viewer to.";
|
||||
};
|
||||
|
||||
port = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 8080;
|
||||
description = "Port to bind the web viewer to.";
|
||||
};
|
||||
};
|
||||
|
||||
settings = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.attrsOf lib.types.anything);
|
||||
default = {};
|
||||
@@ -85,6 +101,10 @@
|
||||
};
|
||||
Web_Viewer = {
|
||||
enabled = false;
|
||||
db_path = "${cfg.dataDir}/meshcore-bot.db";
|
||||
auto_start = false; #We start it in a seperate unit
|
||||
host = cfg.webviewer.host;
|
||||
port = cfg.webviewer.port;
|
||||
};
|
||||
};
|
||||
in
|
||||
@@ -173,6 +193,62 @@
|
||||
|
||||
# Install package data (translations, templates, etc.)
|
||||
environment.systemPackages = [cfg.package];
|
||||
|
||||
# Web viewer service
|
||||
systemd.services.meshcore-bot-viewer = lib.mkIf cfg.webviewer.enable {
|
||||
description = "MeshCore Bot Web Viewer - Web interface for MeshCore Bot";
|
||||
after = ["network.target" "meshcore-bot.service"];
|
||||
wantedBy = ["multi-user.target"];
|
||||
|
||||
# Service configuration
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
User = "meshcore-bot";
|
||||
Group = "meshcore-bot";
|
||||
|
||||
# Working directory - systemd will create it automatically
|
||||
WorkingDirectory = cfg.dataDir;
|
||||
|
||||
# StateDirectory creates /var/lib/meshcore-bot with correct ownership
|
||||
# This is the NixOS/systemd way to manage service state directories
|
||||
StateDirectory = "meshcore-bot";
|
||||
StateDirectoryMode = "0750";
|
||||
|
||||
# LogsDirectory creates /var/log/meshcore-bot with correct ownership
|
||||
LogsDirectory = "meshcore-bot";
|
||||
LogsDirectoryMode = "0750";
|
||||
|
||||
# Start command
|
||||
ExecStart = "${cfg.package}/bin/meshcore-viewer --host ${cfg.webviewer.host} --port ${toString cfg.webviewer.port} --config ${configFile}";
|
||||
|
||||
# Restart policy
|
||||
Restart = "on-failure";
|
||||
RestartSec = "10s";
|
||||
|
||||
# Security hardening
|
||||
NoNewPrivileges = true;
|
||||
PrivateTmp = true;
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = true;
|
||||
|
||||
# Additional hardening
|
||||
ProtectKernelTunables = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectControlGroups = true;
|
||||
RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"];
|
||||
RestrictNamespaces = true;
|
||||
LockPersonality = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
RemoveIPC = true;
|
||||
PrivateMounts = true;
|
||||
};
|
||||
|
||||
# Environment
|
||||
environment = {
|
||||
PYTHONUNBUFFERED = "1";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
+22
-55
@@ -39,6 +39,9 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
environment.systemPackages = [
|
||||
pkgs.sqlite
|
||||
];
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
@@ -66,50 +69,28 @@
|
||||
# The database is created during bot initialization in DBManager.__init__
|
||||
# If the bot started successfully, the database should exist
|
||||
print("\n=== File Checks ===")
|
||||
|
||||
|
||||
# Check if service directory exists and has correct permissions
|
||||
machine.succeed("test -d /var/lib/meshcore-bot")
|
||||
machine.succeed("stat -c '%U:%G' /var/lib/meshcore-bot | grep -q meshcore-bot:meshcore-bot")
|
||||
print("✓ Data directory exists with correct ownership")
|
||||
|
||||
|
||||
# Check if database file exists (non-fatal check)
|
||||
# The database is created during MeshCoreBot.__init__ -> DBManager.__init__
|
||||
# If bot fails before initialization, DB won't exist - that's OK for this test
|
||||
print("Checking for database file...")
|
||||
# Use a command that always succeeds to check file existence
|
||||
db_result = machine.succeed("ls /var/lib/meshcore-bot/meshcore-bot.db 2>/dev/null && echo 'exists' || echo 'missing'")
|
||||
if "exists" in db_result:
|
||||
print("✓ Database file created at /var/lib/meshcore-bot/meshcore-bot.db")
|
||||
# Verify it's a valid SQLite database
|
||||
machine.succeed("sqlite3 /var/lib/meshcore-bot/meshcore-bot.db 'SELECT 1' > /dev/null 2>&1")
|
||||
print("✓ Database file is valid SQLite database")
|
||||
else:
|
||||
# Database not created - this is OK if bot failed early
|
||||
# The important thing for NixOS module test is service structure
|
||||
print("⚠ Database file not found (bot may have failed before DB initialization)")
|
||||
# Check if there are critical errors (import failures, etc.)
|
||||
critical_errors = machine.succeed("journalctl -u meshcore-bot.service | grep -iE 'import.*error|module.*not found|no module named|traceback' | head -3 || echo 'none'")
|
||||
critical_errors_clean = critical_errors.strip()
|
||||
if "none" not in critical_errors and len(critical_errors_clean) > 10:
|
||||
print(f"⚠ Critical errors found: {critical_errors_clean[:150]}")
|
||||
else:
|
||||
print("✓ No critical import/initialization errors (connection failures are expected)")
|
||||
machine.succeed("test -f /var/lib/meshcore-bot/meshcore-bot.db")
|
||||
print("✓ Database file created at /var/lib/meshcore-bot/meshcore-bot.db")
|
||||
# Verify it's a valid SQLite database
|
||||
machine.succeed("sqlite3 /var/lib/meshcore-bot/meshcore-bot.db 'SELECT 1' > /dev/null ")
|
||||
print("✓ Database file is valid SQLite database")
|
||||
|
||||
# Check if log file is created in the correct location (non-fatal)
|
||||
# Log file is created by the bot's logging setup, may not exist if bot fails early
|
||||
print("Checking for log file...")
|
||||
log_result = machine.succeed("ls /var/log/meshcore-bot/meshcore-bot.log 2>/dev/null && echo 'exists' || echo 'missing'")
|
||||
if "exists" in log_result:
|
||||
print("✓ Log file created at /var/log/meshcore-bot/meshcore-bot.log")
|
||||
else:
|
||||
# Log file not created - check if logging directory exists
|
||||
log_dir_check = machine.succeed("test -d /var/log/meshcore-bot && echo 'exists' || echo 'missing'")
|
||||
if "exists" in log_dir_check:
|
||||
print("⚠ Log file not found (bot may have failed before logging setup)")
|
||||
print("✓ Log directory exists with correct permissions")
|
||||
else:
|
||||
print("⚠ Log directory not found")
|
||||
|
||||
machine.succeed("test -f /var/log/meshcore-bot/meshcore-bot.log")
|
||||
print("✓ Log file created at /var/log/meshcore-bot/meshcore-bot.log")
|
||||
|
||||
# Check if config was generated
|
||||
machine.succeed("test -f /nix/store/*-meshcore-bot-config.ini")
|
||||
@@ -125,11 +106,8 @@
|
||||
print("✓ User meshcore-bot is in dialout group")
|
||||
|
||||
# Verify it tried to connect via TCP (if bot got that far)
|
||||
tcp_attempt = machine.succeed("journalctl -u meshcore-bot.service | grep -qi 'tcp\|localhost\|connection' && echo 'yes' || echo 'no'")
|
||||
if "yes" in tcp_attempt:
|
||||
print("✓ Bot attempted TCP connection")
|
||||
else:
|
||||
print("⚠ No TCP connection attempt found (bot may have failed earlier)")
|
||||
machine.succeed("journalctl -u meshcore-bot.service | grep -qi 'tcp\|localhost\|connection'")
|
||||
print("✓ Bot attempted TCP connection")
|
||||
|
||||
'';
|
||||
};
|
||||
@@ -141,6 +119,7 @@
|
||||
|
||||
services.meshcore-bot = {
|
||||
enable = true;
|
||||
webviewer.enable = true;
|
||||
settings = {
|
||||
Connection = {
|
||||
connection_type = "tcp";
|
||||
@@ -153,11 +132,7 @@
|
||||
enabled = true;
|
||||
};
|
||||
Web_Viewer = {
|
||||
enabled = true;
|
||||
host = "127.0.0.1";
|
||||
port = 8080;
|
||||
debug = false;
|
||||
auto_start = true;
|
||||
};
|
||||
Logging = {
|
||||
log_level = "DEBUG";
|
||||
@@ -167,29 +142,21 @@
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
machine.wait_for_unit("meshcore-bot.service")
|
||||
machine.wait_for_unit("meshcore-bot-viewer.service")
|
||||
|
||||
# Give it time to start and initialize web viewer
|
||||
machine.sleep(10)
|
||||
# Check logs show the bot loaded the webviewer plugin
|
||||
machine.succeed("journalctl -u meshcore-bot.service | grep -q 'Successfully loaded plugin: webviewer'")
|
||||
machine.sleep(5)
|
||||
|
||||
# FIXME meshcore-bot doesn't open the port.
|
||||
# probably because it never connected to it's companion socket.
|
||||
# I don't want to mock it.
|
||||
# Wait for web viewer to start listening on port 8080
|
||||
# This is the NixOS test framework way to check if a port is open
|
||||
#print("\n=== Web Viewer Port Check ===")
|
||||
#machine.wait_for_open_port(8080, timeout=30)
|
||||
#print("✓ Web viewer is listening on port 8080")
|
||||
print("\n=== Web Viewer Port Check ===")
|
||||
machine.wait_for_open_port(8080, timeout=30)
|
||||
print("✓ Web viewer is listening on port 8080")
|
||||
|
||||
# Verify we can connect to the web viewer
|
||||
#machine.succeed("curl -f http://127.0.0.1:8080/ || curl -f http://localhost:8080/")
|
||||
#print("✓ Web viewer responds to HTTP requests")
|
||||
machine.succeed("curl -f http://127.0.0.1:8080/")
|
||||
print("✓ Web viewer responds to HTTP requests")
|
||||
|
||||
# Check logs for web viewer startup
|
||||
machine.succeed("journalctl -u meshcore-bot.service | grep -qi 'web.*viewer' || journalctl -u meshcore-bot.service | grep -qi 'flask'")
|
||||
print("✓ Web viewer startup logged")
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user