nixpkgs/nixos/modules/services/web-apps/mattermost.nix

967 lines
30 KiB
Nix

{
config,
pkgs,
lib,
...
}:
let
inherit (lib.strings)
hasInfix
hasSuffix
escapeURL
concatStringsSep
escapeShellArg
escapeShellArgs
versionAtLeast
optionalString
;
inherit (lib.meta) getExe;
inherit (lib.lists) singleton;
inherit (lib.attrsets) mapAttrsToList recursiveUpdate optionalAttrs;
inherit (lib.options) mkOption mkPackageOption mkEnableOption;
inherit (lib.modules)
mkRenamedOptionModule
mkMerge
mkIf
mkDefault
;
inherit (lib.trivial) warnIf throwIf;
inherit (lib) types;
cfg = config.services.mattermost;
# The directory to store mutable data within dataDir.
mutableDataDir = "${cfg.dataDir}/data";
# The plugin directory. Note that this is the *post-unpack* plugin directory,
# since Mattermost unpacks plugins to put them there. (Hence, mutable data.)
pluginDir = "${mutableDataDir}/plugins";
# Mattermost uses this as a staging directory to unpack plugins, among possibly other things.
# Ensure that it's inside mutableDataDir since it can get rather large.
tempDir = "${mutableDataDir}/tmp";
# Creates a database URI.
mkDatabaseUri =
{
scheme,
user ? null,
password ? null,
escapeUserAndPassword ? true,
host ? null,
escapeHost ? true,
port ? null,
path ? null,
query ? { },
}:
let
nullToEmpty = val: if val == null then "" else toString val;
# Converts a list of URI attrs to a query string.
toQuery = mapAttrsToList (
name: value: if value == null then null else (escapeURL name) + "=" + (escapeURL (toString value))
);
schemePart = if scheme == null then "" else "${escapeURL scheme}://";
userPart =
let
realUser = if escapeUserAndPassword then escapeURL user else user;
realPassword = if escapeUserAndPassword then escapeURL password else password;
in
if user == null && password == null then
""
else if user != null && password != null then
"${realUser}:${realPassword}"
else if user != null then
realUser
else
throw "Either user or username and password must be provided";
hostPart =
let
realHost = if escapeHost then escapeURL (nullToEmpty host) else nullToEmpty host;
in
if userPart == "" then realHost else "@" + realHost;
portPart = if port == null then "" else ":" + (toString port);
pathPart = if path == null then "" else "/" + path;
queryPart = if query == { } then "" else "?" + concatStringsSep "&" (toQuery query);
in
schemePart + userPart + hostPart + portPart + pathPart + queryPart;
database =
let
hostIsPath = hasInfix "/" cfg.database.host;
in
if cfg.database.driver == "postgres" then
if cfg.database.peerAuth then
mkDatabaseUri {
scheme = cfg.database.driver;
inherit (cfg.database) user;
path = escapeURL cfg.database.name;
query = {
host = cfg.database.socketPath;
} // cfg.database.extraConnectionOptions;
}
else
mkDatabaseUri {
scheme = cfg.database.driver;
inherit (cfg.database) user password;
host = if hostIsPath then null else cfg.database.host;
port = if hostIsPath then null else cfg.database.port;
path = escapeURL cfg.database.name;
query =
optionalAttrs hostIsPath { host = cfg.database.host; } // cfg.database.extraConnectionOptions;
}
else if cfg.database.driver == "mysql" then
if cfg.database.peerAuth then
mkDatabaseUri {
scheme = null;
inherit (cfg.database) user;
escapeUserAndPassword = false;
host = "unix(${cfg.database.socketPath})";
escapeHost = false;
path = escapeURL cfg.database.name;
query = cfg.database.extraConnectionOptions;
}
else
mkDatabaseUri {
scheme = null;
inherit (cfg.database) user password;
escapeUserAndPassword = false;
host =
if hostIsPath then
"unix(${cfg.database.host})"
else
"tcp(${cfg.database.host}:${toString cfg.database.port})";
escapeHost = false;
path = escapeURL cfg.database.name;
query = cfg.database.extraConnectionOptions;
}
else
throw "Invalid database driver: ${cfg.database.driver}";
mattermostPluginDerivations =
with pkgs;
map (
plugin:
stdenv.mkDerivation {
name = "mattermost-plugin";
installPhase = ''
mkdir -p $out/share
cp ${plugin} $out/share/plugin.tar.gz
'';
dontUnpack = true;
dontPatch = true;
dontConfigure = true;
dontBuild = true;
preferLocalBuild = true;
}
) cfg.plugins;
mattermostPlugins =
with pkgs;
if mattermostPluginDerivations == [ ] then
null
else
stdenv.mkDerivation {
name = "${cfg.package.name}-plugins";
nativeBuildInputs = [ autoPatchelfHook ] ++ mattermostPluginDerivations;
buildInputs = [ cfg.package ];
installPhase = ''
mkdir -p $out
plugins=(${
escapeShellArgs (map (plugin: "${plugin}/share/plugin.tar.gz") mattermostPluginDerivations)
})
for plugin in "''${plugins[@]}"; do
hash="$(sha256sum "$plugin" | awk '{print $1}')"
mkdir -p "$hash"
tar -C "$hash" -xzf "$plugin"
autoPatchelf "$hash"
GZIP_OPT=-9 tar -C "$hash" -cvzf "$out/$hash.tar.gz" .
rm -rf "$hash"
done
'';
dontUnpack = true;
dontPatch = true;
dontConfigure = true;
dontBuild = true;
preferLocalBuild = true;
};
mattermostConfWithoutPlugins = recursiveUpdate {
ServiceSettings = {
SiteURL = cfg.siteUrl;
ListenAddress = "${cfg.host}:${toString cfg.port}";
LocalModeSocketLocation = cfg.socket.path;
EnableLocalMode = cfg.socket.enable;
EnableSecurityFixAlert = cfg.telemetry.enableSecurityAlerts;
};
TeamSettings.SiteName = cfg.siteName;
SqlSettings.DriverName = cfg.database.driver;
SqlSettings.DataSource =
if cfg.database.fromEnvironment then
null
else
warnIf (!cfg.database.peerAuth && cfg.database.password != null) ''
Database password is set in Mattermost config! This password will end up in the Nix store.
You may be able to simply set the following, if the database is on the same host
and peer authentication is enabled:
services.mattermost.database.peerAuth = true;
Note that this is the default if you set system.stateVersion to 25.05 or later
and the database host is localhost.
Alternatively, you can write the following to ${
if cfg.environmentFile == null then "your environment file" else cfg.environmentFile
}:
MM_SQLSETTINGS_DATASOURCE=${database}
Then set the following options:
services.mattermost.environmentFile = "<your environment file>";
services.mattermost.database.fromEnvironment = true;
'' database;
FileSettings.Directory = cfg.dataDir;
PluginSettings.Directory = "${pluginDir}/server";
PluginSettings.ClientDirectory = "${pluginDir}/client";
LogSettings = {
FileLocation = cfg.logDir;
# Reaches out to Mattermost's servers for telemetry; disable it by default.
# https://docs.mattermost.com/configure/environment-configuration-settings.html#enable-diagnostics-and-error-reporting
EnableDiagnostics = cfg.telemetry.enableDiagnostics;
};
} cfg.settings;
mattermostConf = recursiveUpdate mattermostConfWithoutPlugins (
if mattermostPlugins == null then
{ }
else
{
PluginSettings = {
Enable = true;
};
}
);
mattermostConfJSON = pkgs.writeText "mattermost-config.json" (builtins.toJSON mattermostConf);
in
{
imports = [
(mkRenamedOptionModule
[
"services"
"mattermost"
"listenAddress"
]
[
"services"
"mattermost"
"host"
]
)
(mkRenamedOptionModule
[
"services"
"mattermost"
"localDatabaseCreate"
]
[
"services"
"mattermost"
"database"
"create"
]
)
(mkRenamedOptionModule
[
"services"
"mattermost"
"localDatabasePassword"
]
[
"services"
"mattermost"
"database"
"password"
]
)
(mkRenamedOptionModule
[
"services"
"mattermost"
"localDatabaseUser"
]
[
"services"
"mattermost"
"database"
"user"
]
)
(mkRenamedOptionModule
[
"services"
"mattermost"
"localDatabaseName"
]
[
"services"
"mattermost"
"database"
"name"
]
)
(mkRenamedOptionModule
[
"services"
"mattermost"
"extraConfig"
]
[
"services"
"mattermost"
"settings"
]
)
(mkRenamedOptionModule
[
"services"
"mattermost"
"statePath"
]
[
"services"
"mattermost"
"dataDir"
]
)
];
options = {
services.mattermost = {
enable = mkEnableOption "Mattermost chat server";
package = mkPackageOption pkgs "mattermost" { };
siteUrl = mkOption {
type = types.str;
example = "https://chat.example.com";
description = ''
URL this Mattermost instance is reachable under, without trailing slash.
'';
};
siteName = mkOption {
type = types.str;
default = "Mattermost";
description = "Name of this Mattermost site.";
};
host = mkOption {
type = types.str;
default = "127.0.0.1";
example = "0.0.0.0";
description = ''
Host or address that this Mattermost instance listens on.
'';
};
port = mkOption {
type = types.port;
default = 8065;
description = ''
Port for Mattermost server to listen on.
'';
};
dataDir = mkOption {
type = types.path;
default = "/var/lib/mattermost";
description = ''
Mattermost working directory.
'';
};
socket = {
enable = mkEnableOption "Mattermost control socket";
path = mkOption {
type = types.path;
default = "${cfg.dataDir}/mattermost.sock";
defaultText = ''''${config.mattermost.dataDir}/mattermost.sock'';
description = ''
Default location for the Mattermost control socket used by `mmctl`.
'';
};
export = mkEnableOption "Export socket control to system environment variables";
};
logDir = mkOption {
type = types.path;
default =
if versionAtLeast config.system.stateVersion "25.05" then
"/var/log/mattermost"
else
"${cfg.dataDir}/logs";
defaultText = ''
if versionAtLeast config.system.stateVersion "25.05" then "/var/log/mattermost"
else "''${config.services.mattermost.dataDir}/logs";
'';
description = ''
Mattermost log directory.
'';
};
configDir = mkOption {
type = types.path;
default =
if versionAtLeast config.system.stateVersion "25.05" then
"/etc/mattermost"
else
"${cfg.dataDir}/config";
defaultText = ''
if versionAtLeast config.system.stateVersion "25.05" then
"/etc/mattermost"
else
"''${config.services.mattermost.dataDir}/config";
'';
description = ''
Mattermost config directory.
'';
};
mutableConfig = mkOption {
type = types.bool;
default = false;
description = ''
Whether the Mattermost config.json is writeable by Mattermost.
Most of the settings can be edited in the system console of
Mattermost if this option is enabled. A template config using
the options specified in services.mattermost will be generated
but won't be overwritten on changes or rebuilds.
If this option is disabled, changes in the system console won't
be possible (default). If an config.json is present, it will be
overwritten!
'';
};
preferNixConfig = mkOption {
type = types.bool;
default = versionAtLeast config.system.stateVersion "25.05";
defaultText = ''
versionAtLeast config.system.stateVersion "25.05";
'';
description = ''
If both mutableConfig and this option are set, the Nix configuration
will take precedence over any settings configured in the server
console.
'';
};
plugins = mkOption {
type = with types; listOf (either path package);
default = [ ];
example = "[ ./com.github.moussetc.mattermost.plugin.giphy-2.0.0.tar.gz ]";
description = ''
Plugins to add to the configuration. Overrides any installed if non-null.
This is a list of paths to .tar.gz files or derivations evaluating to
.tar.gz files.
'';
};
telemetry = {
enableSecurityAlerts = mkOption {
type = types.bool;
default = true;
description = ''
True if we should enable security update checking. This reaches out to Mattermost's servers:
https://docs.mattermost.com/manage/telemetry.html#security-update-check-feature
'';
};
enableDiagnostics = mkOption {
type = types.bool;
default = false;
description = ''
True if we should enable sending diagnostic data. This reaches out to Mattermost's servers:
https://docs.mattermost.com/manage/telemetry.html#error-and-diagnostics-reporting-feature
'';
};
};
environment = mkOption {
type = with types; attrsOf (either int str);
default = { };
description = ''
Extra environment variables to export to the Mattermost process, in the systemd unit.
'';
example = {
MM_SERVICESETTINGS_SITEURL = "http://example.com";
};
};
environmentFile = mkOption {
type = with types; nullOr path;
default = null;
description = ''
Environment file (see {manpage}`systemd.exec(5)`
"EnvironmentFile=" section for the syntax) which sets config options
for mattermost (see [the Mattermost documentation](https://docs.mattermost.com/configure/configuration-settings.html#environment-variables)).
Settings defined in the environment file will overwrite settings
set via nix or via the {option}`services.mattermost.extraConfig`
option.
Useful for setting config options without their value ending up in the
(world-readable) nix store, e.g. for a database password.
'';
};
database = {
driver = mkOption {
type = types.enum [
"postgres"
"mysql"
];
default = "postgres";
description = ''
The database driver to use (Postgres or MySQL).
'';
};
create = mkOption {
type = types.bool;
default = true;
description = ''
Create a local PostgreSQL or MySQL database for Mattermost automatically.
'';
};
peerAuth = mkOption {
type = types.bool;
default = versionAtLeast config.system.stateVersion "25.05" && cfg.database.host == "localhost";
defaultText = ''
versionAtLeast config.system.stateVersion "25.05" && config.services.mattermost.database.host == "localhost"
'';
description = ''
If set, will use peer auth instead of connecting to a Postgres server.
Use services.mattermost.database.socketPath to configure the socket path.
'';
};
socketPath = mkOption {
type = types.path;
default =
if cfg.database.driver == "postgres" then "/run/postgresql" else "/run/mysqld/mysqld.sock";
defaultText = ''
if config.services.mattermost.database.driver == "postgres" then "/run/postgresql" else "/run/mysqld/mysqld.sock";
'';
description = ''
The database (Postgres or MySQL) socket path.
'';
};
fromEnvironment = mkOption {
type = types.bool;
default = false;
description = ''
Use services.mattermost.environmentFile to configure the database instead of writing the database URI
to the Nix store. Useful if you use password authentication with peerAuth set to false.
'';
};
name = mkOption {
type = types.str;
default = "mattermost";
description = ''
Local Mattermost database name.
'';
};
host = mkOption {
type = types.str;
default = "localhost";
example = "127.0.0.1";
description = ''
Host to use for the database. Can also be set to a path if you'd like to connect
to a socket using a username and password.
'';
};
port = mkOption {
type = types.port;
default = if cfg.database.driver == "postgres" then 5432 else 3306;
defaultText = ''
if config.services.mattermost.database.type == "postgres" then 5432 else 3306
'';
example = 3306;
description = ''
Port to use for the database.
'';
};
user = mkOption {
type = types.str;
default = "mattermost";
description = ''
Local Mattermost database username.
'';
};
password = mkOption {
type = types.str;
default = "mmpgsecret";
description = ''
Password for local Mattermost database user. If set and peerAuth is not true,
will cause a warning nagging you to use environmentFile instead since it will
end up in the Nix store.
'';
};
extraConnectionOptions = mkOption {
type = with types; attrsOf (either int str);
default =
if cfg.database.driver == "postgres" then
{
sslmode = "disable";
connect_timeout = 30;
}
else if cfg.database.driver == "mysql" then
{
charset = "utf8mb4,utf8";
writeTimeout = "30s";
readTimeout = "30s";
}
else
throw "Invalid database driver ${cfg.database.driver}";
defaultText = ''
if config.mattermost.database.driver == "postgres" then
{
sslmode = "disable";
connect_timeout = 30;
}
else if config.mattermost.database.driver == "mysql" then
{
charset = "utf8mb4,utf8";
writeTimeout = "30s";
readTimeout = "30s";
}
else
throw "Invalid database driver";
'';
description = ''
Extra options that are placed in the connection URI's query parameters.
'';
};
};
user = mkOption {
type = types.str;
default = "mattermost";
description = ''
User which runs the Mattermost service.
'';
};
group = mkOption {
type = types.str;
default = "mattermost";
description = ''
Group which runs the Mattermost service.
'';
};
settings = mkOption {
type = types.attrs;
default = { };
description = ''
Additional configuration options as Nix attribute set in config.json schema.
'';
};
matterircd = {
enable = mkEnableOption "Mattermost IRC bridge";
package = mkPackageOption pkgs "matterircd" { };
parameters = mkOption {
type = types.listOf types.str;
default = [ ];
example = [
"-mmserver chat.example.com"
"-bind [::]:6667"
];
description = ''
Set commandline parameters to pass to matterircd. See
<https://github.com/42wim/matterircd#usage> for more information.
'';
};
};
};
};
config = mkMerge [
(mkIf cfg.enable {
users.users = {
${cfg.user} = {
group = cfg.group;
uid = mkIf (cfg.user == "mattermost") config.ids.uids.mattermost;
home = cfg.dataDir;
isSystemUser = true;
packages = [ cfg.package ];
};
};
users.groups = {
${cfg.group} = {
gid = mkIf (cfg.group == "mattermost") config.ids.gids.mattermost;
};
};
services.postgresql = mkIf (cfg.database.driver == "postgres" && cfg.database.create) {
enable = true;
ensureDatabases = singleton cfg.database.name;
ensureUsers = singleton {
name =
throwIf
(cfg.database.peerAuth && (cfg.database.user != cfg.user || cfg.database.name != cfg.database.user))
''
Mattermost database peer auth is enabled and the user, database user, or database name mismatch.
Peer authentication will not work.
''
cfg.database.user;
ensureDBOwnership = true;
};
};
services.mysql = mkIf (cfg.database.driver == "mysql" && cfg.database.create) {
enable = true;
package = mkDefault pkgs.mariadb;
ensureDatabases = singleton cfg.database.name;
ensureUsers = singleton {
name = cfg.database.user;
ensurePermissions = {
"${cfg.database.name}.*" = "ALL PRIVILEGES";
};
};
settings = rec {
mysqld = {
collation-server = mkDefault "utf8mb4_general_ci";
init-connect = mkDefault "SET NAMES utf8mb4";
character-set-server = mkDefault "utf8mb4";
};
mysqld_safe = mysqld;
};
};
environment = {
variables = mkIf cfg.socket.export {
MMCTL_LOCAL = "true";
MMCTL_LOCAL_SOCKET_PATH = cfg.socket.path;
};
};
systemd.tmpfiles.rules =
[
"d ${cfg.dataDir} 0750 ${cfg.user} ${cfg.group} - -"
"d ${cfg.logDir} 0750 ${cfg.user} ${cfg.group} - -"
"d ${cfg.configDir} 0750 ${cfg.user} ${cfg.group} - -"
"d ${mutableDataDir} 0750 ${cfg.user} ${cfg.group} - -"
# Make sure tempDir exists and is not a symlink.
"R- ${tempDir} - - - - -"
"d= ${tempDir} 0750 ${cfg.user} ${cfg.group} - -"
# Ensure that pluginDir is a directory, as it could be a symlink on prior versions.
"r- ${pluginDir} - - - - -"
"d= ${pluginDir} 0750 ${cfg.user} ${cfg.group} - -"
# Ensure that the plugin directories exist.
"d= ${mattermostConf.PluginSettings.Directory} 0750 ${cfg.user} ${cfg.group} - -"
"d= ${mattermostConf.PluginSettings.ClientDirectory} 0750 ${cfg.user} ${cfg.group} - -"
# Link in some of the immutable data directories.
"L+ ${cfg.dataDir}/bin - - - - ${cfg.package}/bin"
"L+ ${cfg.dataDir}/fonts - - - - ${cfg.package}/fonts"
"L+ ${cfg.dataDir}/i18n - - - - ${cfg.package}/i18n"
"L+ ${cfg.dataDir}/templates - - - - ${cfg.package}/templates"
"L+ ${cfg.dataDir}/client - - - - ${cfg.package}/client"
]
++ (
if mattermostPlugins == null then
# Create the plugin tarball directory if it's a symlink.
[
"r- ${cfg.dataDir}/plugins - - - - -"
"d= ${cfg.dataDir}/plugins 0750 ${cfg.user} ${cfg.group} - -"
]
else
# Symlink the plugin tarball directory, removing anything existing.
[ "L+ ${cfg.dataDir}/plugins - - - - ${mattermostPlugins}" ]
);
systemd.services.mattermost = rec {
description = "Mattermost chat service";
wantedBy = [ "multi-user.target" ];
after = mkMerge [
[ "network.target" ]
(mkIf (cfg.database.driver == "postgres" && cfg.database.create) [ "postgresql.service" ])
(mkIf (cfg.database.driver == "mysql" && cfg.database.create) [ "mysql.service" ])
];
requires = after;
environment = mkMerge [
{
# Use tempDir as this can get rather large, especially if Mattermost unpacks a large number of plugins.
TMPDIR = tempDir;
}
cfg.environment
];
preStart =
''
dataDir=${escapeShellArg cfg.dataDir}
configDir=${escapeShellArg cfg.configDir}
logDir=${escapeShellArg cfg.logDir}
package=${escapeShellArg cfg.package}
nixConfig=${escapeShellArg mattermostConfJSON}
''
+ optionalString (versionAtLeast config.system.stateVersion "25.05") ''
# Migrate configs in the pre-25.05 directory structure.
oldConfig="$dataDir/config/config.json"
newConfig="$configDir/config.json"
if [ "$oldConfig" != "$newConfig" ] && [ -f "$oldConfig" ] && [ ! -f "$newConfig" ]; then
# Migrate the legacy config location to the new config location
echo "Moving legacy config at $oldConfig to $newConfig" >&2
mkdir -p "$configDir"
mv "$oldConfig" "$newConfig"
touch "$configDir/.initial-created"
fi
# Logs too.
oldLogs="$dataDir/logs"
newLogs="$logDir"
if [ "$oldLogs" != "$newLogs" ] && [ -d "$oldLogs" ]; then
# Migrate the legacy log location to the new log location.
# Allow this to fail if there aren't any logs to move.
echo "Moving legacy logs at $oldLogs to $newLogs" >&2
mkdir -p "$newLogs"
mv "$oldLogs"/* "$newLogs" || true
fi
''
+ optionalString (!cfg.mutableConfig) ''
${getExe pkgs.jq} -s '.[0] * .[1]' "$package/config/config.json" "$nixConfig" > "$configDir/config.json"
''
+ optionalString cfg.mutableConfig ''
if [ ! -e "$configDir/.initial-created" ]; then
${getExe pkgs.jq} -s '.[0] * .[1]' "$package/config/config.json" "$nixConfig" > "$configDir/config.json"
touch "$configDir/.initial-created"
fi
''
+ optionalString (cfg.mutableConfig && cfg.preferNixConfig) ''
echo "$(${getExe pkgs.jq} -s '.[0] * .[1]' "$configDir/config.json" "$nixConfig")" > "$configDir/config.json"
'';
serviceConfig = mkMerge [
{
User = cfg.user;
Group = cfg.group;
ExecStart = "${getExe cfg.package} --config ${cfg.configDir}/config.json";
ReadWritePaths = [
cfg.dataDir
cfg.logDir
cfg.configDir
];
UMask = "0027";
Restart = "always";
RestartSec = 10;
LimitNOFILE = 49152;
LockPersonality = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateTmp = true;
PrivateUsers = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProtectSystem = "strict";
RestrictNamespaces = true;
RestrictSUIDSGID = true;
EnvironmentFile = cfg.environmentFile;
WorkingDirectory = cfg.dataDir;
}
(mkIf (cfg.dataDir == "/var/lib/mattermost") {
StateDirectory = baseNameOf cfg.dataDir;
StateDirectoryMode = "0750";
})
(mkIf (cfg.logDir == "/var/log/mattermost") {
LogsDirectory = baseNameOf cfg.logDir;
LogsDirectoryMode = "0750";
})
(mkIf (cfg.configDir == "/etc/mattermost") {
ConfigurationDirectory = baseNameOf cfg.configDir;
ConfigurationDirectoryMode = "0750";
})
];
unitConfig.JoinsNamespaceOf = mkMerge [
(mkIf (cfg.database.driver == "postgres" && cfg.database.create) [ "postgresql.service" ])
(mkIf (cfg.database.driver == "mysql" && cfg.database.create) [ "mysql.service" ])
];
};
assertions = [
{
# Make sure the URL doesn't have a trailing slash
assertion = !(hasSuffix "/" cfg.siteUrl);
message = ''
services.mattermost.siteUrl should not have a trailing "/".
'';
}
{
# Make sure this isn't a host/port pair
assertion = !(hasInfix ":" cfg.host && !(hasInfix "[" cfg.host) && !(hasInfix "]" cfg.host));
message = ''
services.mattermost.host should not include a port. Use services.mattermost.host for the address
or hostname, and services.mattermost.port to specify the port separately.
'';
}
];
})
(mkIf cfg.matterircd.enable {
systemd.services.matterircd = {
description = "Mattermost IRC bridge service";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
User = "nobody";
Group = "nogroup";
ExecStart = "${getExe cfg.matterircd.package} ${escapeShellArgs cfg.matterircd.parameters}";
WorkingDirectory = "/tmp";
PrivateTmp = true;
Restart = "always";
RestartSec = "5";
};
};
})
];
meta.maintainers = with lib.maintainers; [ numinit ];
}