Rename project from system-stats-daemon to systant

- Updated all module names from SystemStatsDaemon to Systant
- Renamed application config from :system_stats_daemon to :systant
- Updated service files and documentation
- Release binary now at _build/prod/rel/systant/bin/systant

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ryan 2025-08-02 17:06:03 -07:00
parent 9d8306a64b
commit 92fc90e3b4
11 changed files with 104 additions and 45 deletions

View File

@ -1,4 +1,4 @@
# System Stats Daemon # Systant
An Elixir application that runs as a systemd daemon to: An Elixir application that runs as a systemd daemon to:
1. Publish system stats to MQTT every 30 seconds 1. Publish system stats to MQTT every 30 seconds
@ -9,10 +9,10 @@ An Elixir application that runs as a systemd daemon to:
Edit `config/config.exs` to configure MQTT connection: Edit `config/config.exs` to configure MQTT connection:
```elixir ```elixir
config :system_stats_daemon, SystemStatsDaemon.MqttClient, config :systant, Systant.MqttClient,
host: "localhost", host: "localhost",
port: 1883, port: 1883,
client_id: "system_stats_daemon", client_id: "systant",
username: nil, username: nil,
password: nil, password: nil,
stats_topic: "system/stats", stats_topic: "system/stats",
@ -35,24 +35,24 @@ mix run --no-halt
# Production release # Production release
MIX_ENV=prod mix release MIX_ENV=prod mix release
_build/prod/rel/system_stats_daemon/bin/system_stats_daemon start _build/prod/rel/systant/bin/systant start
``` ```
## Systemd Installation ## Systemd Installation
1. Build production release 1. Build production release
2. Copy binary to `/usr/local/bin/` 2. Copy binary to `/usr/local/bin/`
3. Copy `system_stats_daemon.service` to `/etc/systemd/system/` 3. Copy `systant.service` to `/etc/systemd/system/`
4. Enable and start: 4. Enable and start:
```bash ```bash
sudo systemctl enable system_stats_daemon sudo systemctl enable systant
sudo systemctl start system_stats_daemon sudo systemctl start systant
``` ```
## Features ## Features
- Publishes "Hello world" stats every 30 seconds to `system/stats` topic - Publishes "Hello from systant" stats every 30 seconds to `system/stats` topic
- Listens on `system/commands` topic and logs received messages - Listens on `system/commands` topic and logs received messages
- Configurable MQTT connection settings - Configurable MQTT connection settings
- Runs as systemd daemon with auto-restart - Runs as systemd daemon with auto-restart

View File

@ -1,9 +1,9 @@
import Config import Config
config :system_stats_daemon, SystemStatsDaemon.MqttClient, config :systant, Systant.MqttClient,
host: "mqtt.home", host: "mqtt.home",
port: 1883, port: 1883,
client_id: "system_stats_daemon", client_id: "systant",
username: "mqtt", username: "mqtt",
password: "pleasework", password: "pleasework",
stats_topic: "system/stats", stats_topic: "system/stats",

View File

@ -1,6 +1,6 @@
defmodule SystemStatsDaemon do defmodule Systant do
@moduledoc """ @moduledoc """
Documentation for `SystemStatsDaemon`. Documentation for `Systant`.
""" """
@doc """ @doc """
@ -8,7 +8,7 @@ defmodule SystemStatsDaemon do
## Examples ## Examples
iex> SystemStatsDaemon.hello() iex> Systant.hello()
:world :world
""" """

View File

@ -1,4 +1,4 @@
defmodule SystemStatsDaemon.Application do defmodule Systant.Application do
# See https://hexdocs.pm/elixir/Application.html # See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications # for more information on OTP Applications
@moduledoc false @moduledoc false
@ -8,12 +8,12 @@ defmodule SystemStatsDaemon.Application do
@impl true @impl true
def start(_type, _args) do def start(_type, _args) do
children = [ children = [
{SystemStatsDaemon.MqttClient, []} {Systant.MqttClient, []}
] ]
# See https://hexdocs.pm/elixir/Supervisor.html # See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options # for other strategies and supported options
opts = [strategy: :one_for_one, name: SystemStatsDaemon.Supervisor] opts = [strategy: :one_for_one, name: Systant.Supervisor]
Supervisor.start_link(children, opts) Supervisor.start_link(children, opts)
end end
end end

View File

@ -1,4 +1,4 @@
defmodule SystemStatsDaemon.MqttClient do defmodule Systant.MqttClient do
use GenServer use GenServer
require Logger require Logger
@ -11,7 +11,7 @@ defmodule SystemStatsDaemon.MqttClient do
end end
def init(_opts) do def init(_opts) do
config = Application.get_env(:system_stats_daemon, __MODULE__) config = Application.get_env(:systant, __MODULE__)
Logger.info("Starting MQTT client with config: #{inspect(config)}") Logger.info("Starting MQTT client with config: #{inspect(config)}")
# Use a unique client ID to avoid conflicts # Use a unique client ID to avoid conflicts
@ -32,7 +32,7 @@ defmodule SystemStatsDaemon.MqttClient do
# Send immediate hello message # Send immediate hello message
hello_msg = %{ hello_msg = %{
message: "Hello - system_stats_daemon started", message: "Hello - systant started",
timestamp: DateTime.utc_now() |> DateTime.to_iso8601(), timestamp: DateTime.utc_now() |> DateTime.to_iso8601(),
hostname: get_hostname() hostname: get_hostname()
} }
@ -64,7 +64,7 @@ defmodule SystemStatsDaemon.MqttClient do
defp publish_stats(config, client_id) do defp publish_stats(config, client_id) do
stats = %{ stats = %{
message: "Hello from system_stats_daemon", message: "Hello from systant",
timestamp: DateTime.utc_now() |> DateTime.to_iso8601(), timestamp: DateTime.utc_now() |> DateTime.to_iso8601(),
hostname: get_hostname() hostname: get_hostname()
} }

View File

@ -3,7 +3,7 @@ defmodule SystemStatsDaemon.MixProject do
def project do def project do
[ [
app: :system_stats_daemon, app: :systant,
version: "0.1.0", version: "0.1.0",
elixir: "~> 1.18", elixir: "~> 1.18",
start_permanent: Mix.env() == :prod, start_permanent: Mix.env() == :prod,
@ -16,7 +16,7 @@ defmodule SystemStatsDaemon.MixProject do
def application do def application do
[ [
extra_applications: [:logger], extra_applications: [:logger],
mod: {SystemStatsDaemon.Application, []} mod: {Systant.Application, []}
] ]
end end
@ -30,7 +30,7 @@ defmodule SystemStatsDaemon.MixProject do
defp releases do defp releases do
[ [
system_stats_daemon: [ systant: [
include_executables_for: [:unix], include_executables_for: [:unix],
applications: [runtime_tools: :permanent] applications: [runtime_tools: :permanent]
] ]

View File

@ -3,15 +3,15 @@
with lib; with lib;
let let
cfg = config.services.system-stats-daemon; cfg = config.services.systant;
in in
{ {
options.services.system-stats-daemon = { options.services.systant = {
enable = mkEnableOption "System Stats MQTT Daemon"; enable = mkEnableOption "Systant MQTT Daemon";
package = mkOption { package = mkOption {
type = types.package; type = types.package;
description = "The system-stats-daemon package to use"; description = "The systant package to use";
}; };
mqttHost = mkOption { mqttHost = mkOption {
@ -58,8 +58,8 @@ in
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
systemd.services.system-stats-daemon = { systemd.services.systant = {
description = "System Stats MQTT Daemon"; description = "Systant MQTT Daemon";
after = [ "network.target" ]; after = [ "network.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
@ -77,13 +77,13 @@ in
Type = "exec"; Type = "exec";
User = "root"; User = "root";
Group = "root"; Group = "root";
ExecStart = "${cfg.package}/bin/system_stats_daemon start"; ExecStart = "${cfg.package}/bin/systant start";
ExecStop = "${cfg.package}/bin/system_stats_daemon stop"; ExecStop = "${cfg.package}/bin/systant stop";
Restart = "always"; Restart = "always";
RestartSec = 5; RestartSec = 5;
StandardOutput = "journal"; StandardOutput = "journal";
StandardError = "journal"; StandardError = "journal";
SyslogIdentifier = "system_stats_daemon"; SyslogIdentifier = "systant";
WorkingDirectory = "${cfg.package}"; WorkingDirectory = "${cfg.package}";
# Security settings # Security settings

59
systant.nix Normal file
View File

@ -0,0 +1,59 @@
{ lib
, stdenv
, fetchgit
, elixir
, erlang
, rebar3
, git
}:
stdenv.mkDerivation rec {
pname = "systant";
version = "0.1.0";
src = fetchgit {
url = "https://git.ryanpandya.com/ryan/systant.git";
rev = "9d8306a64b7893ea0d25e2b08f470541fb4db7c8";
sha256 = lib.fakeSha256; # Replace with actual hash after first build attempt
};
nativeBuildInputs = [
elixir
erlang
rebar3
git
];
buildPhase = ''
runHook preBuild
export MIX_ENV=prod
export MIX_HOME=$TMPDIR/mix
export HEX_HOME=$TMPDIR/hex
export REBAR_CACHE_DIR=$TMPDIR/rebar3
mix local.hex --force
mix local.rebar --force
mix deps.get --only=prod
mix release
runHook postBuild
'';
installPhase = ''
runHook preInstall
mkdir -p $out
cp -r _build/prod/rel/systant/* $out/
runHook postInstall
'';
meta = with lib; {
description = "Systant - System stats MQTT daemon for monitoring system metrics";
homepage = "https://git.ryanpandya.com/ryan/systant";
license = licenses.mit; # Update if different
maintainers = [ ]; # Add your maintainer info if desired
platforms = platforms.linux;
};
}

View File

@ -1,19 +1,19 @@
[Unit] [Unit]
Description=System Stats MQTT Daemon Description=Systant MQTT Daemon
After=network.target After=network.target
[Service] [Service]
Type=exec Type=exec
User=root User=root
Group=root Group=root
ExecStart=/opt/system_stats_daemon/bin/system_stats_daemon start ExecStart=/opt/systant/bin/systant start
ExecStop=/opt/system_stats_daemon/bin/system_stats_daemon stop ExecStop=/opt/systant/bin/systant stop
Restart=always Restart=always
RestartSec=5 RestartSec=5
StandardOutput=journal StandardOutput=journal
StandardError=journal StandardError=journal
SyslogIdentifier=system_stats_daemon SyslogIdentifier=systant
WorkingDirectory=/opt/system_stats_daemon WorkingDirectory=/opt/systant
# Security settings - still apply restrictions where possible # Security settings - still apply restrictions where possible
NoNewPrivileges=true NoNewPrivileges=true

8
test/systant_test.exs Normal file
View File

@ -0,0 +1,8 @@
defmodule SystantTest do
use ExUnit.Case
doctest Systant
test "greets the world" do
assert Systant.hello() == :world
end
end

View File

@ -1,8 +0,0 @@
defmodule SystemStatsDaemonTest do
use ExUnit.Case
doctest SystemStatsDaemon
test "greets the world" do
assert SystemStatsDaemon.hello() == :world
end
end