1{ config, pkgs, lib, ... }:
2
3with lib;
4
5let
6
7 cfg = config.services.mattermost;
8
9 defaultConfig = builtins.fromJSON (readFile "${pkgs.mattermost}/config/config.json");
10
11 mattermostConf = foldl recursiveUpdate defaultConfig
12 [ { ServiceSettings.SiteURL = cfg.siteUrl;
13 ServiceSettings.ListenAddress = cfg.listenAddress;
14 TeamSettings.SiteName = cfg.siteName;
15 SqlSettings.DriverName = "postgres";
16 SqlSettings.DataSource = "postgres://${cfg.localDatabaseUser}:${cfg.localDatabasePassword}@localhost:5432/${cfg.localDatabaseName}?sslmode=disable&connect_timeout=10";
17 }
18 cfg.extraConfig
19 ];
20
21 mattermostConfJSON = pkgs.writeText "mattermost-config-raw.json" (builtins.toJSON mattermostConf);
22
23in
24
25{
26 options = {
27 services.mattermost = {
28 enable = mkEnableOption "Mattermost chat server";
29
30 statePath = mkOption {
31 type = types.str;
32 default = "/var/lib/mattermost";
33 description = "Mattermost working directory";
34 };
35
36 siteUrl = mkOption {
37 type = types.str;
38 example = "https://chat.example.com";
39 description = ''
40 URL this Mattermost instance is reachable under, without trailing slash.
41 '';
42 };
43
44 siteName = mkOption {
45 type = types.str;
46 default = "Mattermost";
47 description = "Name of this Mattermost site.";
48 };
49
50 listenAddress = mkOption {
51 type = types.str;
52 default = ":8065";
53 example = "[::1]:8065";
54 description = ''
55 Address and port this Mattermost instance listens to.
56 '';
57 };
58
59 mutableConfig = mkOption {
60 type = types.bool;
61 default = false;
62 description = ''
63 Whether the Mattermost config.json is writeable by Mattermost.
64
65 Most of the settings can be edited in the system console of
66 Mattermost if this option is enabled. A template config using
67 the options specified in services.mattermost will be generated
68 but won't be overwritten on changes or rebuilds.
69
70 If this option is disabled, changes in the system console won't
71 be possible (default). If an config.json is present, it will be
72 overwritten!
73 '';
74 };
75
76 extraConfig = mkOption {
77 type = types.attrs;
78 default = { };
79 description = ''
80 Addtional configuration options as Nix attribute set in config.json schema.
81 '';
82 };
83
84 localDatabaseCreate = mkOption {
85 type = types.bool;
86 default = true;
87 description = ''
88 Create a local PostgreSQL database for Mattermost automatically.
89 '';
90 };
91
92 localDatabaseName = mkOption {
93 type = types.str;
94 default = "mattermost";
95 description = ''
96 Local Mattermost database name.
97 '';
98 };
99
100 localDatabaseUser = mkOption {
101 type = types.str;
102 default = "mattermost";
103 description = ''
104 Local Mattermost database username.
105 '';
106 };
107
108 localDatabasePassword = mkOption {
109 type = types.str;
110 default = "mmpgsecret";
111 description = ''
112 Password for local Mattermost database user.
113 '';
114 };
115
116 user = mkOption {
117 type = types.str;
118 default = "mattermost";
119 description = ''
120 User which runs the Mattermost service.
121 '';
122 };
123
124 group = mkOption {
125 type = types.str;
126 default = "mattermost";
127 description = ''
128 Group which runs the Mattermost service.
129 '';
130 };
131
132 matterircd = {
133 enable = mkEnableOption "Mattermost IRC bridge";
134 parameters = mkOption {
135 type = types.listOf types.str;
136 default = [ ];
137 example = [ "-mmserver chat.example.com" "-bind [::]:6667" ];
138 description = ''
139 Set commandline parameters to pass to matterircd. See
140 https://github.com/42wim/matterircd#usage for more information.
141 '';
142 };
143 };
144 };
145 };
146
147 config = mkMerge [
148 (mkIf cfg.enable {
149 users.users = optionalAttrs (cfg.user == "mattermost") (singleton {
150 name = "mattermost";
151 group = cfg.group;
152 uid = config.ids.uids.mattermost;
153 home = cfg.statePath;
154 });
155
156 users.groups = optionalAttrs (cfg.group == "mattermost") (singleton {
157 name = "mattermost";
158 gid = config.ids.gids.mattermost;
159 });
160
161 services.postgresql.enable = cfg.localDatabaseCreate;
162
163 # The systemd service will fail to execute the preStart hook
164 # if the WorkingDirectory does not exist
165 system.activationScripts.mattermost = ''
166 mkdir -p ${cfg.statePath}
167 '';
168
169 systemd.services.mattermost = {
170 description = "Mattermost chat service";
171 wantedBy = [ "multi-user.target" ];
172 after = [ "network.target" "postgresql.service" ];
173
174 preStart = ''
175 mkdir -p ${cfg.statePath}/{data,config,logs}
176 ln -sf ${pkgs.mattermost}/{bin,fonts,i18n,templates,client} ${cfg.statePath}
177 '' + lib.optionalString (!cfg.mutableConfig) ''
178 ln -sf ${mattermostConfJSON} ${cfg.statePath}/config/config.json
179 '' + lib.optionalString cfg.mutableConfig ''
180 if ! test -e "${cfg.statePath}/config/.initial-created"; then
181 rm -f ${cfg.statePath}/config/config.json
182 cp ${mattermostConfJSON} ${cfg.statePath}/config/config.json
183 touch ${cfg.statePath}/config/.initial-created
184 fi
185 '' + lib.optionalString cfg.localDatabaseCreate ''
186 if ! test -e "${cfg.statePath}/.db-created"; then
187 ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
188 ${config.services.postgresql.package}/bin/psql postgres -c \
189 "CREATE ROLE ${cfg.localDatabaseUser} WITH LOGIN NOCREATEDB NOCREATEROLE ENCRYPTED PASSWORD '${cfg.localDatabasePassword}'"
190 ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
191 ${config.services.postgresql.package}/bin/createdb \
192 --owner ${cfg.localDatabaseUser} ${cfg.localDatabaseName}
193 touch ${cfg.statePath}/.db-created
194 fi
195 '' + ''
196 chown ${cfg.user}:${cfg.group} -R ${cfg.statePath}
197 chmod u+rw,g+r,o-rwx -R ${cfg.statePath}
198 '';
199
200 serviceConfig = {
201 PermissionsStartOnly = true;
202 User = cfg.user;
203 Group = cfg.group;
204 ExecStart = "${pkgs.mattermost}/bin/mattermost";
205 WorkingDirectory = "${cfg.statePath}";
206 Restart = "always";
207 RestartSec = "10";
208 LimitNOFILE = "49152";
209 };
210 unitConfig.JoinsNamespaceOf = mkIf cfg.localDatabaseCreate "postgresql.service";
211 };
212 })
213 (mkIf cfg.matterircd.enable {
214 systemd.services.matterircd = {
215 description = "Mattermost IRC bridge service";
216 wantedBy = [ "multi-user.target" ];
217 serviceConfig = {
218 User = "nobody";
219 Group = "nogroup";
220 ExecStart = "${pkgs.matterircd.bin}/bin/matterircd ${concatStringsSep " " cfg.matterircd.parameters}";
221 WorkingDirectory = "/tmp";
222 PrivateTmp = true;
223 Restart = "always";
224 RestartSec = "5";
225 };
226 };
227 })
228 ];
229}
230