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 platform";
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.extraUsers = 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.extraGroups = 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 platform 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,webapp} ${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 ${config.services.postgresql.package}/bin/psql postgres -c \
188 "CREATE ROLE ${cfg.localDatabaseUser} WITH LOGIN NOCREATEDB NOCREATEROLE NOCREATEUSER ENCRYPTED PASSWORD '${cfg.localDatabasePassword}'"
189 ${config.services.postgresql.package}/bin/createdb \
190 --owner ${cfg.localDatabaseUser} ${cfg.localDatabaseName}
191 touch ${cfg.statePath}/.db-created
192 fi
193 '' + ''
194 chown ${cfg.user}:${cfg.group} -R ${cfg.statePath}
195 chmod u+rw,g+r,o-rwx -R ${cfg.statePath}
196 '';
197
198 serviceConfig = {
199 PermissionsStartOnly = true;
200 User = cfg.user;
201 Group = cfg.group;
202 ExecStart = "${pkgs.mattermost}/bin/mattermost-platform";
203 WorkingDirectory = "${cfg.statePath}";
204 PrivateTmp = true;
205 Restart = "always";
206 RestartSec = "10";
207 LimitNOFILE = "49152";
208 };
209 };
210 })
211 (mkIf cfg.matterircd.enable {
212 systemd.services.matterircd = {
213 description = "Mattermost IRC bridge service";
214 wantedBy = [ "multi-user.target" ];
215 serviceConfig = {
216 User = "nobody";
217 Group = "nogroup";
218 ExecStart = "${pkgs.matterircd.bin}/bin/matterircd ${concatStringsSep " " cfg.matterircd.parameters}";
219 WorkingDirectory = "/tmp";
220 PrivateTmp = true;
221 Restart = "always";
222 RestartSec = "5";
223 };
224 };
225 })
226 ];
227}
228