Kieran's opinionated (and probably slightly dumb) nix config
1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.atelier.services.cachet;
9in
10{
11 options.atelier.services.cachet = {
12 enable = lib.mkEnableOption "Cachet Slack emoji/profile cache";
13
14 domain = lib.mkOption {
15 type = lib.types.str;
16 description = "Domain to serve cachet on";
17 };
18
19 port = lib.mkOption {
20 type = lib.types.port;
21 default = 3000;
22 description = "Port to run cachet on";
23 };
24
25 dataDir = lib.mkOption {
26 type = lib.types.path;
27 default = "/var/lib/cachet";
28 description = "Directory to store cachet data";
29 };
30
31 secretsFile = lib.mkOption {
32 type = lib.types.path;
33 description = "Path to secrets file containing SLACK_TOKEN, SLACK_SIGNING_SECRET, BEARER_TOKEN";
34 };
35
36 repository = lib.mkOption {
37 type = lib.types.str;
38 default = "https://github.com/taciturnaxolotl/cachet.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.cachet = {
49 isSystemUser = true;
50 group = "cachet";
51 extraGroups = [ "services" ];
52 home = cfg.dataDir;
53 createHome = true;
54 shell = pkgs.bash;
55 };
56
57 users.groups.cachet = { };
58
59 security.sudo.extraRules = [
60 {
61 users = [ "cachet" ];
62 commands = [
63 {
64 command = "/run/current-system/sw/bin/systemctl restart cachet.service";
65 options = [ "NOPASSWD" ];
66 }
67 ];
68 }
69 ];
70
71 systemd.services.cachet = {
72 description = "Cachet Slack emoji/profile cache";
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
96 serviceConfig = {
97 Type = "simple";
98 User = "cachet";
99 Group = "cachet";
100 EnvironmentFile = cfg.secretsFile;
101 Environment = [
102 "NODE_ENV=production"
103 "PORT=${toString cfg.port}"
104 "DATABASE_PATH=${cfg.dataDir}/data/cachet.db"
105 ];
106 ExecStart = "${pkgs.bash}/bin/bash -c 'cd ${cfg.dataDir}/app && ${pkgs.unstable.bun}/bin/bun run src/index.ts'";
107 Restart = "always";
108 RestartSec = "10s";
109 };
110
111 serviceConfig.ExecStartPre = [
112 "+${pkgs.writeShellScript "cachet-setup" ''
113 mkdir -p ${cfg.dataDir}/data
114 mkdir -p ${cfg.dataDir}/app
115 chown -R cachet:services ${cfg.dataDir}
116 chmod -R g+rwX ${cfg.dataDir}
117 ''}"
118 ];
119 };
120
121 services.caddy.virtualHosts.${cfg.domain} = {
122 extraConfig = ''
123 tls {
124 dns cloudflare {env.CLOUDFLARE_API_TOKEN}
125 }
126
127 reverse_proxy localhost:${toString cfg.port}
128 '';
129 };
130 };
131}