commit e1a589dbd2fe987374c4b1330c3656a38ebc7cb5
parent 47fdc6b188dbbc055c2dba7b9ea055458db9e85b
Author: Silas Brack <silasbrack@gmail.com>
Date: Sun, 31 May 2026 22:22:22 +0200
feat: implement daily tracker
Diffstat:
5 files changed, 152 insertions(+), 81 deletions(-)
diff --git a/flake.lock b/flake.lock
@@ -16,19 +16,21 @@
"type": "gitlab"
}
},
- "crane": {
+ "daily-tracker": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
"locked": {
- "lastModified": 1776635034,
- "narHash": "sha256-OEOJrT3ZfwbChzODfIH4GzlNTtOFuZFWPtW7jIeR8xU=",
- "owner": "ipetkov",
- "repo": "crane",
- "rev": "dc7496d8ea6e526b1254b55d09b966e94673750f",
- "type": "github"
+ "lastModified": 1780258828,
+ "narHash": "sha256-U7sAzfC2K1Ac/X+wm2pKZp83ENBt+iKTPwq8M5yczeE=",
+ "path": "/home/silas/dev/elixir-web-app/daily_tracker",
+ "type": "path"
},
"original": {
- "owner": "ipetkov",
- "repo": "crane",
- "type": "github"
+ "path": "/home/silas/dev/elixir-web-app/daily_tracker",
+ "type": "path"
}
},
"flake-compat": {
@@ -227,22 +229,6 @@
},
"nixpkgs_3": {
"locked": {
- "lastModified": 1776548001,
- "narHash": "sha256-ZSK0NL4a1BwVbbTBoSnWgbJy9HeZFXLYQizjb2DPF24=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "b12141ef619e0a9c1c84dc8c684040326f27cdcc",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "ref": "nixos-unstable",
- "repo": "nixpkgs",
- "type": "github"
- }
- },
- "nixpkgs_4": {
- "locked": {
"lastModified": 1775888245,
"narHash": "sha256-nwASzrRDD1JBEu/o8ekKYEXm/oJW6EMCzCRdrwcLe90=",
"owner": "NixOS",
@@ -259,60 +245,19 @@
},
"root": {
"inputs": {
+ "daily-tracker": "daily-tracker",
"home-manager": "home-manager",
"mkv": "mkv",
"nixos-mailserver": "nixos-mailserver",
"nixpkgs": "nixpkgs_2",
"nixpkgs-unstable": "nixpkgs-unstable",
- "simple-web-app": "simple-web-app",
"sops-nix": "sops-nix",
"website": "website"
}
},
- "rust-overlay": {
- "inputs": {
- "nixpkgs": [
- "simple-web-app",
- "nixpkgs"
- ]
- },
- "locked": {
- "lastModified": 1776654897,
- "narHash": "sha256-Vqi4AiJVCcBGn/RmBtRCgyH5rCxqm/w0xV9diJWF1Ic=",
- "owner": "oxalica",
- "repo": "rust-overlay",
- "rev": "25d75be8139815a53560745fa060909777495105",
- "type": "github"
- },
- "original": {
- "owner": "oxalica",
- "repo": "rust-overlay",
- "type": "github"
- }
- },
- "simple-web-app": {
- "inputs": {
- "crane": "crane",
- "nixpkgs": "nixpkgs_3",
- "rust-overlay": "rust-overlay"
- },
- "locked": {
- "lastModified": 1777147695,
- "narHash": "sha256-qYkrQPehfGzoWHMlVC9IG3U7xWOXUl93v6/KrEnjHJY=",
- "ref": "refs/heads/main",
- "rev": "ae4c3f76eef6a15a54860d6283eec80960e22651",
- "revCount": 63,
- "type": "git",
- "url": "ssh://forgejo@git.fnarglebeast.com/silas/simple-web-app.git"
- },
- "original": {
- "type": "git",
- "url": "ssh://forgejo@git.fnarglebeast.com/silas/simple-web-app.git"
- }
- },
"sops-nix": {
"inputs": {
- "nixpkgs": "nixpkgs_4"
+ "nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1777338324,
diff --git a/flake.nix b/flake.nix
@@ -3,7 +3,10 @@
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
sops-nix.url = "github:Mic92/sops-nix";
- simple-web-app.url = "git+ssh://forgejo@git.fnarglebeast.com/silas/simple-web-app.git";
+ daily-tracker = {
+ url = "path:/home/silas/dev/elixir-web-app/daily_tracker";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
mkv.url = "git+ssh://git@github.com/silasbrack/mkv";
website = {
url = "git+ssh://forgejo@git.fnarglebeast.com/silas/website.git";
@@ -23,7 +26,7 @@
nixpkgs,
nixpkgs-unstable,
sops-nix,
- simple-web-app,
+ daily-tracker,
mkv,
website,
nixos-mailserver,
@@ -53,7 +56,7 @@
pkgs,
...
}:
- import ./hosts/helios.nix (inputs // { inherit simple-web-app mkv website; })
+ import ./hosts/helios.nix (inputs // { inherit daily-tracker mkv website; })
)
];
};
diff --git a/hosts/helios.nix b/hosts/helios.nix
@@ -2,7 +2,7 @@
pkgs,
config,
lib,
- simple-web-app,
+ daily-tracker,
mkv,
website,
...
@@ -14,7 +14,7 @@
../modules/mediawiki.nix
../modules/forgejo.nix
../modules/mkv.nix
- ../modules/simple-web-app.nix
+ ../modules/daily-tracker.nix
../modules/mailserver.nix
];
@@ -116,13 +116,16 @@
};
};
- # Simple Web App
- services.simple-web-app = {
+ # Daily Tracker
+ services.local.daily-tracker = {
enable = true;
- package = simple-web-app.packages.${pkgs.stdenv.hostPlatform.system}.default;
- trailbasePackage = simple-web-app.packages.${pkgs.stdenv.hostPlatform.system}.trailbase;
- traildepotPath = "${simple-web-app}/traildepot";
+ package = daily-tracker.packages.${pkgs.stdenv.hostPlatform.system}.default;
+ domain = "simple.fnarglebeast.com";
+ secretKeyBaseSecretName = "daily_tracker_secret_key_base";
+ mailPasswordSecretName = "daily_tracker_mail_password";
};
+ sops.secrets.daily_tracker_secret_key_base = { };
+ sops.secrets.daily_tracker_mail_password = { };
# Mail server
services.local.mailserver = {
diff --git a/modules/daily-tracker.nix b/modules/daily-tracker.nix
@@ -0,0 +1,118 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ cfg = config.services.local.daily-tracker;
+in
+{
+ options.services.local.daily-tracker = {
+ enable = lib.mkEnableOption "Daily Tracker";
+
+ package = lib.mkOption {
+ type = lib.types.package;
+ description = "The daily_tracker release package";
+ };
+
+ domain = lib.mkOption {
+ type = lib.types.str;
+ description = "Domain for the daily tracker";
+ };
+
+ port = lib.mkOption {
+ type = lib.types.int;
+ default = 4000;
+ description = "Port for the Phoenix server";
+ };
+
+ secretKeyBaseSecretName = lib.mkOption {
+ type = lib.types.str;
+ description = "Sops secret name for SECRET_KEY_BASE";
+ };
+
+ mailPasswordSecretName = lib.mkOption {
+ type = lib.types.str;
+ description = "Sops secret name for MAIL_PASSWORD";
+ };
+
+ databaseName = lib.mkOption {
+ type = lib.types.str;
+ default = "daily_tracker";
+ description = "PostgreSQL database name";
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ services.postgresql = {
+ enable = true;
+ ensureDatabases = [ cfg.databaseName ];
+ ensureUsers = [
+ {
+ name = "daily_tracker";
+ ensureDBOwnership = true;
+ }
+ ];
+ authentication = ''
+ local ${cfg.databaseName} daily_tracker trust
+ host ${cfg.databaseName} daily_tracker 127.0.0.1/32 trust
+ '';
+ };
+
+ systemd.services.daily-tracker = {
+ wantedBy = [ "multi-user.target" ];
+ description = "Daily Tracker Phoenix app";
+ after = [ "network.target" "postgresql.service" ];
+ requires = [ "postgresql.service" ];
+ serviceConfig = {
+ Type = "exec";
+ ExecStartPre = "${cfg.package}/bin/daily_tracker eval 'DailyTracker.Release.migrate()'";
+ ExecStart = "${cfg.package}/bin/daily_tracker start";
+ Restart = "on-failure";
+ User = "daily_tracker";
+ Group = "daily_tracker";
+ StateDirectory = "daily-tracker";
+ WorkingDirectory = "/var/lib/daily-tracker";
+ };
+ environment = {
+ PHX_SERVER = "true";
+ PHX_HOST = cfg.domain;
+ PORT = builtins.toString cfg.port;
+ DATABASE_URL = "ecto://daily_tracker@127.0.0.1/${cfg.databaseName}";
+ MAIL_HOST = "localhost";
+ RELEASE_COOKIE = "daily-tracker-cookie";
+ };
+ };
+
+ # Load secrets into the service environment
+ systemd.services.daily-tracker.serviceConfig.EnvironmentFile = [
+ config.sops.templates."daily-tracker-env".path
+ ];
+
+ sops.templates."daily-tracker-env" = {
+ content = ''
+ SECRET_KEY_BASE=${config.sops.placeholder.${cfg.secretKeyBaseSecretName}}
+ MAIL_PASSWORD=${config.sops.placeholder.${cfg.mailPasswordSecretName}}
+ '';
+ owner = "daily_tracker";
+ };
+
+ users.users.daily_tracker = {
+ isSystemUser = true;
+ group = "daily_tracker";
+ };
+ users.groups.daily_tracker = { };
+
+ services.nginx.virtualHosts.${cfg.domain} = {
+ forceSSL = true;
+ enableACME = true;
+ locations."/" = {
+ proxyPass = "http://127.0.0.1:${builtins.toString cfg.port}";
+ recommendedProxySettings = true;
+ proxyWebsockets = true;
+ };
+ };
+ };
+}
diff --git a/secrets.yaml b/secrets.yaml
@@ -8,6 +8,8 @@ gaia_forgejo_runner_token: ENC[AES256_GCM,data:0XA6nSeBW4uh/cx/95xw0Ood/a68qD4Z/
gaia_gitea_runner_ssh_key: ENC[AES256_GCM,data:CZZMptDd9s7WQsx9vnHv7tF1CJ9cKd+lnwuv75+a1p4OWxANObfIFtJOTDDHNL1+fk4/48/OOtODrgZlfeLxVPmmiPsvnSPOA1POhi6q97bpOWk/YWgViIWaf4UsLWDBq3/tngcJCa5JebrKuY+T7MaJn1Lw+C8h+91WiIWYSzOSMmZIoMDo5Y85cPqesFKxgmSlgTYbv5mJnFQ6pvhqTLHRpIPW5PNgRjeXsjJrblvs/edYtLyAVm6H0IYMiMa24+272f90l/2Vc16PXk6RCCFIIJ9HuGYGRBFMAD0bYPDo/ESTMn2u1MATGVCPKCLMjTxFglowfavZpb7xLk7EvZoWPQXx/+KcTV3+iVabOv0kI8OLZFGUPCs4B1MiIeCfSLEr4nop2SNm3C7pGFuKJ0EFGbnhdfBqBqVKZVkXJNGY023BHC3eXx57VatisHQAwLbs2qgSWvOetTAhw8E12QylFtq6mXWG3lZuhJxoiZ6QMI9Yjc/Fvxt4NBOjFtbF0iDRd0g0FSv+geQ7BNid,iv:ufcXcVm2NQRTmG7mYDPwCihm4SYkQ9llpoKtP73Z9Ik=,tag:g4RNNXCBZL0EluwGTYC7fA==,type:str]
gaia_forgejo_access_token: ENC[AES256_GCM,data:KpAnvpevOWgbNrEr+QynldkDqonVD90MuVDrBwG7mBClZxHfcps6aw==,iv:22zJ3/BBu5/aFMjgwhQ3lJH+lzOT53bozdAdrzMUGRI=,tag:Vic8ZfT43b0CfphZ/hP1DQ==,type:str]
mail_silas_password_hash: ENC[AES256_GCM,data:E3NCQ5/eRfpbaOVpK0Ovk/vE+h1q5/z7rZrSelQy2Qc++a6nMYA/q7/YmfFT2R68JwOJMaeNMnOKjn1A,iv:sGgRC5SRRiziBd5TVO273uouyFhRqvB0e04xDhifSfM=,tag:U1QxtlVbDUw+nhFm+ZnxOA==,type:str]
+daily_tracker_secret_key_base: ENC[AES256_GCM,data:kLjV/cxP/s8DJ+hAwGdKmzU+hQohEU1/tjBIrONtHQT/baYn874rHf1KYgn05OXP7J9074favlH4h1aQco+6og==,iv:7nSTefY8B19hGCW0XK/perQoRhgHxLD3dAcMrDbPlgw=,tag:HCAoGDcKE92ms4SpwEl3AA==,type:str]
+daily_tracker_mail_password: ENC[AES256_GCM,data:XCaEsrOmLoCQFurRc5X8l2YrtwGlp61j97XQTtPNGndzjqwXsjHfdHb/QO+V978fw9ZOWNrYKuSPR/t1,iv:jjhjPkyWmMFgPYQqNFagEttTW1b0u8nwSWiwKR+xT2U=,tag:WzCqt2S26vqO2I6sH3qqYQ==,type:str]
sops:
age:
- recipient: age1s29qgfupgx7mqn9dk2rndp6xh63f5qan8vqa77ve603wd5m9a9rqkpq7re
@@ -46,7 +48,7 @@ sops:
ZGptR25vdWhEKzFxbE5qS051ZFF4RkEK1hGCQVE1E92nFSwSeYRRhxn2p3wBfhpS
TZMRsiXtUdlgnoa+IJcFKsbwaZspYnKCA93IEfOXy6TadcoNiFzypw==
-----END AGE ENCRYPTED FILE-----
- lastmodified: "2026-05-31T11:49:47Z"
- mac: ENC[AES256_GCM,data:6X9/faApExW9xtorxSAODyVQwSCtDTel0iTtc7ZaeRgiRqQmpPTw8bZ4Zln51lWqUO3EOSRH6ge2btNrtQbPmZoBAmh0dntDvB6LsvJn3NVDeq5vM3vV1dMDyjiXBMqxT99tYkTSK08j10QcCLRRkHIoj4wHIabg7INPCpNadnA=,iv:18yZmCDZzRf96i7L72ogvwCJ6rHvGhzvA0Pq07Jhy08=,tag:j8xTjdG7tndylumNkNVFGA==,type:str]
+ lastmodified: "2026-05-31T20:15:47Z"
+ mac: ENC[AES256_GCM,data:bEFC9WYUYvXlbBfUe9+biAUc4QkSkIS0DO6qtIwDCOu32PmNHC6NDjE/ZzWCgwx6QOm0BH/5JKIEdiH1XhegOKi2BmH1M6tqRE3U7sHh2DCCn+bf9PuoKW/dcjXAQp7QS9MLZkTgw0HepKixiL6WAaszYNs9DtOhZzFEjHs8WSs=,iv:Sh8mCK4VbLSnf+eOYeI2YBjfDQIfQYc2XJGgw63I4kE=,tag:5hUMPrVbhCdIZr8VuvsrOA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.12.1