Kieran's opinionated (and probably slightly dumb) nix config
1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.atelier.services.hn-alerts;
9in
10{
11 options.atelier.services.hn-alerts = {
12 enable = lib.mkEnableOption "HN Alerts Hacker News monitoring service";
13
14 domain = lib.mkOption {
15 type = lib.types.str;
16 description = "Domain to serve hn-alerts on";
17 };
18
19 port = lib.mkOption {
20 type = lib.types.port;
21 default = 3001;
22 description = "Port to run hn-alerts on";
23 };
24
25 dataDir = lib.mkOption {
26 type = lib.types.path;
27 default = "/var/lib/hn-alerts";
28 description = "Directory to store hn-alerts data";
29 };
30
31 secretsFile = lib.mkOption {
32 type = lib.types.path;
33 description = "Path to secrets file containing SLACK_BOT_TOKEN, SLACK_SIGNING_SECRET, SLACK_CHANNEL, SENTRY_DSN, DATABASE_URL";
34 };
35
36 repository = lib.mkOption {
37 type = lib.types.str;
38 default = "https://github.com/taciturnaxolotl/hn-alerts.git";
39 description = "Git repository URL (optional, for auto-deployment)";
40 };
41
42 autoUpdate = lib.mkEnableOption "Automatically git pull on service restart";
43 };
44
45 config = lib.mkIf cfg.enable {
46 users.groups.services = { };
47
48 users.users.hn-alerts = {
49 isSystemUser = true;
50 group = "hn-alerts";
51 extraGroups = [ "services" ];
52 home = cfg.dataDir;
53 createHome = true;
54 shell = pkgs.bash;
55 };
56
57 users.groups.hn-alerts = { };
58
59 security.sudo.extraRules = [
60 {
61 users = [ "hn-alerts" ];
62 commands = [
63 {
64 command = "/run/current-system/sw/bin/systemctl restart hn-alerts.service";
65 options = [ "NOPASSWD" ];
66 }
67 ];
68 }
69 ];
70
71 systemd.services.hn-alerts = {
72 description = "HN Alerts Hacker News monitoring service";
73 wantedBy = [ "multi-user.target" ];
74 after = [ "network.target" ];
75 path = [ pkgs.git ];
76
77 preStart = ''
78 if [ ! -d ${cfg.dataDir}/app/.git ]; then
79 ${pkgs.git}/bin/git clone ${cfg.repository} ${cfg.dataDir}/app
80 fi
81
82 cd ${cfg.dataDir}/app
83 '' + lib.optionalString cfg.autoUpdate ''
84 ${pkgs.git}/bin/git pull
85 '' + ''
86
87 if [ ! -f src/index.ts ]; then
88 echo "No code found at ${cfg.dataDir}/app/src/index.ts"
89 exit 1
90 fi
91
92 echo "Installing dependencies..."
93 ${pkgs.unstable.bun}/bin/bun install
94
95 echo "Initializing database..."
96 ${pkgs.unstable.bun}/bin/bun run db:push
97 '';
98
99 serviceConfig = {
100 Type = "simple";
101 User = "hn-alerts";
102 Group = "hn-alerts";
103 EnvironmentFile = cfg.secretsFile;
104 Environment = [
105 "NODE_ENV=production"
106 "PORT=${toString cfg.port}"
107 ];
108 ExecStart = "${pkgs.bash}/bin/bash -c 'cd ${cfg.dataDir}/app && ${pkgs.unstable.bun}/bin/bun start'";
109 Restart = "always";
110 RestartSec = "10s";
111 };
112 };
113
114 services.caddy.virtualHosts.${cfg.domain} = {
115 extraConfig = ''
116 tls {
117 dns cloudflare {env.CLOUDFLARE_API_TOKEN}
118 }
119
120 reverse_proxy localhost:${toString cfg.port}
121 '';
122 };
123 };
124}