1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.frab;
7
8 package = pkgs.frab;
9
10 databaseConfig = builtins.toJSON { production = cfg.database; };
11
12 frabEnv = {
13 RAILS_ENV = "production";
14 RACK_ENV = "production";
15 SECRET_KEY_BASE = cfg.secretKeyBase;
16 FRAB_HOST = cfg.host;
17 FRAB_PROTOCOL = cfg.protocol;
18 FROM_EMAIL = cfg.fromEmail;
19 RAILS_SERVE_STATIC_FILES = "1";
20 } // cfg.extraEnvironment;
21
22 frab-rake = pkgs.stdenv.mkDerivation rec {
23 name = "frab-rake";
24 buildInputs = [ package.env pkgs.makeWrapper ];
25 phases = "installPhase fixupPhase";
26 installPhase = ''
27 mkdir -p $out/bin
28 makeWrapper ${package.env}/bin/bundle $out/bin/frab-bundle \
29 ${concatStrings (mapAttrsToList (name: value: "--set ${name} '${value}' ") frabEnv)} \
30 --set PATH '${lib.makeBinPath (with pkgs; [ nodejs file imagemagick ])}:$PATH' \
31 --set RAKEOPT '-f ${package}/share/frab/Rakefile' \
32 --run 'cd ${package}/share/frab'
33 makeWrapper $out/bin/frab-bundle $out/bin/frab-rake \
34 --add-flags "exec rake"
35 '';
36 };
37
38in
39
40{
41 options = {
42 services.frab = {
43 enable = mkOption {
44 type = types.bool;
45 default = false;
46 description = ''
47 Enable the frab service.
48 '';
49 };
50
51 host = mkOption {
52 type = types.str;
53 example = "frab.example.com";
54 description = ''
55 Hostname under which this frab instance can be reached.
56 '';
57 };
58
59 protocol = mkOption {
60 type = types.str;
61 default = "https";
62 example = "http";
63 description = ''
64 Either http or https, depending on how your Frab instance
65 will be exposed to the public.
66 '';
67 };
68
69 fromEmail = mkOption {
70 type = types.str;
71 default = "frab@localhost";
72 description = ''
73 Email address used by frab.
74 '';
75 };
76
77 listenAddress = mkOption {
78 type = types.str;
79 default = "localhost";
80 description = ''
81 Address or hostname frab should listen on.
82 '';
83 };
84
85 listenPort = mkOption {
86 type = types.int;
87 default = 3000;
88 description = ''
89 Port frab should listen on.
90 '';
91 };
92
93 statePath = mkOption {
94 type = types.str;
95 default = "/var/lib/frab";
96 description = ''
97 Directory where frab keeps its state.
98 '';
99 };
100
101 user = mkOption {
102 type = types.str;
103 default = "frab";
104 description = ''
105 User to run frab.
106 '';
107 };
108
109 group = mkOption {
110 type = types.str;
111 default = "frab";
112 description = ''
113 Group to run frab.
114 '';
115 };
116
117 secretKeyBase = mkOption {
118 type = types.str;
119 description = ''
120 Your secret key is used for verifying the integrity of signed cookies.
121 If you change this key, all old signed cookies will become invalid!
122
123 Make sure the secret is at least 30 characters and all random,
124 no regular words or you'll be exposed to dictionary attacks.
125 '';
126 };
127
128 database = mkOption {
129 type = types.attrs;
130 default = {
131 adapter = "sqlite3";
132 database = "/var/lib/frab/db.sqlite3";
133 pool = 5;
134 timeout = 5000;
135 };
136 example = {
137 adapter = "postgresql";
138 database = "frab";
139 host = "localhost";
140 username = "frabuser";
141 password = "supersecret";
142 encoding = "utf8";
143 pool = 5;
144 };
145 description = ''
146 Rails database configuration for Frab as Nix attribute set.
147 '';
148 };
149
150 extraEnvironment = mkOption {
151 type = types.attrs;
152 default = {};
153 example = {
154 FRAB_CURRENCY_UNIT = "€";
155 FRAB_CURRENCY_FORMAT = "%n%u";
156 EXCEPTION_EMAIL = "frab-owner@example.com";
157 SMTP_ADDRESS = "localhost";
158 SMTP_PORT = "587";
159 SMTP_DOMAIN = "localdomain";
160 SMTP_USER_NAME = "root";
161 SMTP_PASSWORD = "toor";
162 SMTP_AUTHENTICATION = "1";
163 SMTP_NOTLS = "1";
164 };
165 description = ''
166 Additional environment variables to set for frab for further
167 configuration. See the frab documentation for more information.
168 '';
169 };
170 };
171 };
172
173 config = mkIf cfg.enable {
174 environment.systemPackages = [ frab-rake ];
175
176 users.users = [
177 { name = cfg.user;
178 group = cfg.group;
179 home = "${cfg.statePath}";
180 }
181 ];
182
183 users.groups = [ { name = cfg.group; } ];
184
185 systemd.services.frab = {
186 after = [ "network.target" "gitlab.service" ];
187 wantedBy = [ "multi-user.target" ];
188 environment = frabEnv;
189
190 preStart = ''
191 mkdir -p ${cfg.statePath}/system/attachments
192 chown ${cfg.user}:${cfg.group} -R ${cfg.statePath}
193
194 mkdir /run/frab -p
195 ln -sf ${pkgs.writeText "frab-database.yml" databaseConfig} /run/frab/database.yml
196 ln -sf ${cfg.statePath}/system /run/frab/system
197
198 if ! test -e "${cfg.statePath}/db-setup-done"; then
199 ${frab-rake}/bin/frab-rake db:setup
200 touch ${cfg.statePath}/db-setup-done
201 else
202 ${frab-rake}/bin/frab-rake db:migrate
203 fi
204 '';
205
206 serviceConfig = {
207 PermissionsStartOnly = true;
208 PrivateTmp = true;
209 PrivateDevices = true;
210 Type = "simple";
211 User = cfg.user;
212 Group = cfg.group;
213 TimeoutSec = "300s";
214 Restart = "on-failure";
215 RestartSec = "10s";
216 WorkingDirectory = "${package}/share/frab";
217 ExecStart = "${frab-rake}/bin/frab-bundle exec rails server " +
218 "--binding=${cfg.listenAddress} --port=${toString cfg.listenPort}";
219 };
220 };
221
222 };
223}