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.target" ];
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 }
125 // cfg.api.environment;
126 };
127
128 crabfit-frontend = {
129 description = "The frontend for Crab Fit.";
130
131 wantedBy = [ "multi-user.target" ];
132
133 serviceConfig = {
134 # TODO: harden
135 CacheDirectory = "crabfit";
136 DynamicUser = true;
137 ExecStart = "${lib.getExe pkgs.nodejs} standalone/server.js";
138 WorkingDirectory = cfg.frontend.finalDrv;
139 };
140
141 environment = {
142 NEXT_PUBLIC_API_URL = "https://${cfg.api.host}";
143 PORT = builtins.toString cfg.frontend.port;
144 }
145 // cfg.frontend.environment;
146 };
147 };
148
149 users = {
150 groups.crabfit = { };
151
152 users.crabfit = {
153 group = "crabfit";
154 isSystemUser = true;
155 };
156 };
157
158 services = {
159 postgresql = {
160 enable = true;
161
162 ensureDatabases = [ "crabfit" ];
163
164 ensureUsers = [
165 {
166 name = "crabfit";
167 ensureDBOwnership = true;
168 }
169 ];
170 };
171 };
172 };
173}