1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8let
9 inherit (lib)
10 literalExpression
11 mkEnableOption
12 mkIf
13 mkOption
14 mkPackageOption
15 ;
16
17 inherit (lib.types)
18 attrsOf
19 package
20 port
21 str
22 ;
23
24 cfg = config.services.crabfit;
25in
26
27{
28 options.services.crabfit = {
29 enable = mkEnableOption "Crab Fit, a meeting scheduler based on peoples' availability";
30
31 frontend = {
32 package = mkPackageOption pkgs "crabfit-frontend" { };
33
34 finalDrv = mkOption {
35 readOnly = true;
36 type = package;
37 default = cfg.frontend.package.override {
38 api_url = "https://${cfg.api.host}";
39 frontend_url = cfg.frontend.host;
40 };
41
42 defaultText = literalExpression ''
43 cfg.package.override {
44 api_url = "https://''${cfg.api.host}";
45 frontend_url = cfg.frontend.host;
46 };
47 '';
48
49 description = ''
50 The patched frontend, using the correct urls for the API and frontend.
51 '';
52 };
53
54 environment = mkOption {
55 type = attrsOf str;
56 default = { };
57 description = ''
58 Environment variables for the crabfit frontend.
59 '';
60 };
61
62 host = mkOption {
63 type = str;
64 description = ''
65 The hostname of the frontend.
66 '';
67 };
68
69 port = mkOption {
70 type = port;
71 default = 3001;
72 description = ''
73 The internal listening port of the frontend.
74 '';
75 };
76 };
77
78 api = {
79 package = mkPackageOption pkgs "crabfit-api" { };
80
81 environment = mkOption {
82 type = attrsOf str;
83 default = { };
84 description = ''
85 Environment variables for the crabfit API.
86 '';
87 };
88
89 host = mkOption {
90 type = str;
91 description = ''
92 The hostname of the API.
93 '';
94 };
95
96 port = mkOption {
97 type = port;
98 default = 3000;
99 description = ''
100 The internal listening port of the API.
101 '';
102 };
103 };
104 };
105
106 config = mkIf cfg.enable {
107 systemd.services = {
108 crabfit-api = {
109 description = "The API for Crab Fit.";
110
111 wantedBy = [ "multi-user.target" ];
112 after = [ "postgresql.service" ];
113
114 serviceConfig = {
115 # TODO: harden
116 ExecStart = lib.getExe cfg.api.package;
117 User = "crabfit";
118 };
119
120 environment = {
121 API_LISTEN = "127.0.0.1:${builtins.toString cfg.api.port}";
122 DATABASE_URL = "postgres:///crabfit?host=/run/postgresql";
123 FRONTEND_URL = "https://${cfg.frontend.host}";
124 } // cfg.api.environment;
125 };
126
127 crabfit-frontend = {
128 description = "The frontend for Crab Fit.";
129
130 wantedBy = [ "multi-user.target" ];
131
132 serviceConfig = {
133 # TODO: harden
134 CacheDirectory = "crabfit";
135 DynamicUser = true;
136 ExecStart = "${lib.getExe pkgs.nodejs} standalone/server.js";
137 WorkingDirectory = cfg.frontend.finalDrv;
138 };
139
140 environment = {
141 NEXT_PUBLIC_API_URL = "https://${cfg.api.host}";
142 PORT = builtins.toString cfg.frontend.port;
143 } // cfg.frontend.environment;
144 };
145 };
146
147 users = {
148 groups.crabfit = { };
149
150 users.crabfit = {
151 group = "crabfit";
152 isSystemUser = true;
153 };
154 };
155
156 services = {
157 postgresql = {
158 enable = true;
159
160 ensureDatabases = [ "crabfit" ];
161
162 ensureUsers = [
163 {
164 name = "crabfit";
165 ensureDBOwnership = true;
166 }
167 ];
168 };
169 };
170 };
171}