nixos: init module: restya-board

Changed files
+385
nixos
modules
services
web-apps
+1
nixos/modules/module-list.nix
···
./services/web-apps/pgpkeyserver-lite.nix
./services/web-apps/piwik.nix
./services/web-apps/pump.io.nix
+
./services/web-apps/restya-board.nix
./services/web-apps/tt-rss.nix
./services/web-apps/selfoss.nix
./services/web-apps/quassel-webserver.nix
+384
nixos/modules/services/web-apps/restya-board.nix
···
+
{ config, lib, pkgs, ... }:
+
+
with lib;
+
+
# TODO: are these php-packages needed?
+
#imagick
+
#php-geoip -> php.ini: extension = geoip.so
+
#expat
+
+
let
+
cfg = config.services.restya-board;
+
+
runDir = "/run/restya-board";
+
+
poolName = "restya-board";
+
phpfpmSocketName = "/var/run/phpfpm/${poolName}.sock";
+
+
in
+
+
{
+
+
###### interface
+
+
options = {
+
+
services.restya-board = {
+
+
enable = mkEnableOption "restya-board";
+
+
dataDir = mkOption {
+
type = types.path;
+
default = "/var/lib/restya-board";
+
example = "/var/lib/restya-board";
+
description = ''
+
Data of the application.
+
'';
+
};
+
+
user = mkOption {
+
type = types.str;
+
default = "restya-board";
+
example = "restya-board";
+
description = ''
+
User account under which the web-application runs.
+
'';
+
};
+
+
group = mkOption {
+
type = types.str;
+
default = "nginx";
+
example = "nginx";
+
description = ''
+
Group account under which the web-application runs.
+
'';
+
};
+
+
virtualHost = {
+
serverName = mkOption {
+
type = types.str;
+
default = "restya.board";
+
description = ''
+
Name of the nginx virtualhost to use.
+
'';
+
};
+
+
listenHost = mkOption {
+
type = types.str;
+
default = "localhost";
+
description = ''
+
Listen address for the virtualhost to use.
+
'';
+
};
+
+
listenPort = mkOption {
+
type = types.int;
+
default = 3000;
+
description = ''
+
Listen port for the virtualhost to use.
+
'';
+
};
+
};
+
+
database = {
+
host = mkOption {
+
type = types.nullOr types.str;
+
default = null;
+
description = ''
+
Host of the database. Leave 'null' to use a local PostgreSQL database.
+
A local PostgreSQL database is initialized automatically.
+
'';
+
};
+
+
port = mkOption {
+
type = types.nullOr types.int;
+
default = 5432;
+
description = ''
+
The database's port.
+
'';
+
};
+
+
name = mkOption {
+
type = types.str;
+
default = "restya_board";
+
description = ''
+
Name of the database. The database must exist.
+
'';
+
};
+
+
user = mkOption {
+
type = types.str;
+
default = "restya_board";
+
description = ''
+
The database user. The user must exist and have access to
+
the specified database.
+
'';
+
};
+
+
passwordFile = mkOption {
+
type = types.nullOr types.str;
+
default = null;
+
description = ''
+
The database user's password. 'null' if no password is set.
+
'';
+
};
+
};
+
+
email = {
+
server = mkOption {
+
type = types.nullOr types.str;
+
default = null;
+
example = "localhost";
+
description = ''
+
Hostname to send outgoing mail. Null to use the system MTA.
+
'';
+
};
+
+
port = mkOption {
+
type = types.int;
+
default = 25;
+
description = ''
+
Port used to connect to SMTP server.
+
'';
+
};
+
+
login = mkOption {
+
type = types.str;
+
default = "";
+
description = ''
+
SMTP authentication login used when sending outgoing mail.
+
'';
+
};
+
+
password = mkOption {
+
type = types.str;
+
default = "";
+
description = ''
+
SMTP authentication password used when sending outgoing mail.
+
+
ATTENTION: The password is stored world-readable in the nix-store!
+
'';
+
};
+
};
+
+
timezone = mkOption {
+
type = types.lines;
+
default = "GMT";
+
description = ''
+
Timezone the web-app runs in.
+
'';
+
};
+
+
};
+
+
};
+
+
+
###### implementation
+
+
config = mkIf cfg.enable {
+
+
services.phpfpm.poolConfigs = {
+
"${poolName}" = ''
+
listen = "${phpfpmSocketName}";
+
listen.owner = nginx
+
listen.group = nginx
+
listen.mode = 0600
+
user = ${cfg.user}
+
group = ${cfg.group}
+
pm = dynamic
+
pm.max_children = 75
+
pm.start_servers = 10
+
pm.min_spare_servers = 5
+
pm.max_spare_servers = 20
+
pm.max_requests = 500
+
catch_workers_output = 1
+
'';
+
};
+
+
services.phpfpm.phpOptions = ''
+
date.timezone = "CET"
+
+
${optionalString (!isNull cfg.email.server) ''
+
SMTP = ${cfg.email.server}
+
smtp_port = ${toString cfg.email.port}
+
auth_username = ${cfg.email.login}
+
auth_password = ${cfg.email.password}
+
''}
+
'';
+
+
services.nginx.enable = true;
+
services.nginx.virtualHosts."${cfg.virtualHost.serverName}" = {
+
listen = [ { addr = cfg.virtualHost.listenHost; port = cfg.virtualHost.listenPort; } ];
+
serverName = cfg.virtualHost.serverName;
+
root = runDir;
+
extraConfig = ''
+
index index.html index.php;
+
+
gzip on;
+
gzip_disable "msie6";
+
+
gzip_comp_level 6;
+
gzip_min_length 1100;
+
gzip_buffers 16 8k;
+
gzip_proxied any;
+
gzip_types text/plain application/xml text/css text/js text/xml application/x-javascript text/javascript application/json application/xml+rss;
+
+
client_max_body_size 300M;
+
+
rewrite ^/oauth/authorize$ /server/php/authorize.php last;
+
rewrite ^/oauth_callback/([a-zA-Z0-9_\.]*)/([a-zA-Z0-9_\.]*)$ /server/php/oauth_callback.php?plugin=$1&code=$2 last;
+
rewrite ^/download/([0-9]*)/([a-zA-Z0-9_\.]*)$ /server/php/download.php?id=$1&hash=$2 last;
+
rewrite ^/ical/([0-9]*)/([0-9]*)/([a-z0-9]*).ics$ /server/php/ical.php?board_id=$1&user_id=$2&hash=$3 last;
+
rewrite ^/api/(.*)$ /server/php/R/r.php?_url=$1&$args last;
+
rewrite ^/api_explorer/api-docs/$ /client/api_explorer/api-docs/index.php last;
+
'';
+
+
locations."/".root = "${runDir}/client";
+
+
locations."~ \.php$" = {
+
tryFiles = "$uri =404";
+
extraConfig = ''
+
include ${pkgs.nginx}/conf/fastcgi_params;
+
fastcgi_pass unix:${phpfpmSocketName};
+
fastcgi_index index.php;
+
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+
fastcgi_param PHP_VALUE "upload_max_filesize=9G \n post_max_size=9G \n max_execution_time=200 \n max_input_time=200 \n memory_limit=256M";
+
'';
+
};
+
+
locations."~* \.(css|js|less|html|ttf|woff|jpg|jpeg|gif|png|bmp|ico)" = {
+
root = "${runDir}/client";
+
extraConfig = ''
+
if (-f $request_filename) {
+
break;
+
}
+
rewrite ^/img/([a-zA-Z_]*)/([a-zA-Z_]*)/([a-zA-Z0-9_\.]*)$ /server/php/image.php?size=$1&model=$2&filename=$3 last;
+
add_header Cache-Control public;
+
add_header Cache-Control must-revalidate;
+
expires 7d;
+
'';
+
};
+
};
+
+
systemd.services.restya-board-init = {
+
description = "Restya board initialization";
+
serviceConfig.Type = "oneshot";
+
serviceConfig.RemainAfterExit = true;
+
+
wantedBy = [ "multi-user.target" ];
+
requires = [ "postgresql.service" ];
+
after = [ "network.target" "postgresql.service" ];
+
+
script = ''
+
rm -rf "${runDir}"
+
mkdir -m 750 -p "${runDir}"
+
cp -r "${pkgs.restya-board}/"* "${runDir}"
+
sed -i "s/@restya.com/@${cfg.virtualHost.serverName}/g" "${runDir}/sql/restyaboard_with_empty_data.sql"
+
rm -rf "${runDir}/media"
+
rm -rf "${runDir}/client/img"
+
chmod -R 0750 "${runDir}"
+
+
sed -i "s@^php@${config.services.phpfpm.phpPackage}/bin/php@" "${runDir}/server/php/shell/"*.sh
+
+
${if (isNull cfg.database.host) then ''
+
sed -i "s/^.*'R_DB_HOST'.*$/define('R_DB_HOST', 'localhost');/g" "${runDir}/server/php/config.inc.php"
+
sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', 'restya');/g" "${runDir}/server/php/config.inc.php"
+
'' else ''
+
sed -i "s/^.*'R_DB_HOST'.*$/define('R_DB_HOST', '${cfg.database.host}');/g" "${runDir}/server/php/config.inc.php"
+
sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', '$(<${cfg.database.dbPassFile})');/g" "${runDir}/server/php/config.inc.php"
+
''}
+
sed -i "s/^.*'R_DB_PORT'.*$/define('R_DB_PORT', '${toString cfg.database.port}');/g" "${runDir}/server/php/config.inc.php"
+
sed -i "s/^.*'R_DB_NAME'.*$/define('R_DB_NAME', '${cfg.database.name}');/g" "${runDir}/server/php/config.inc.php"
+
sed -i "s/^.*'R_DB_USER'.*$/define('R_DB_USER', '${cfg.database.user}');/g" "${runDir}/server/php/config.inc.php"
+
+
chmod 0400 "${runDir}/server/php/config.inc.php"
+
+
ln -sf "${cfg.dataDir}/media" "${runDir}/media"
+
ln -sf "${cfg.dataDir}/client/img" "${runDir}/client/img"
+
+
chmod g+w "${runDir}/tmp/cache"
+
chown -R "${cfg.user}"."${cfg.group}" "${runDir}"
+
+
+
mkdir -m 0750 -p "${cfg.dataDir}"
+
mkdir -m 0750 -p "${cfg.dataDir}/media"
+
mkdir -m 0750 -p "${cfg.dataDir}/client/img"
+
cp -r "${pkgs.restya-board}/media/"* "${cfg.dataDir}/media"
+
cp -r "${pkgs.restya-board}/client/img/"* "${cfg.dataDir}/client/img"
+
chown "${cfg.user}"."${cfg.group}" "${cfg.dataDir}"
+
chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/media"
+
chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/client/img"
+
+
${optionalString (isNull cfg.database.host) ''
+
if ! [ -e "${cfg.dataDir}/.db-initialized" ]; then
+
${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
+
${config.services.postgresql.package}/bin/psql -U ${config.services.postgresql.superUser} \
+
-c "CREATE USER ${cfg.database.user} WITH ENCRYPTED PASSWORD 'restya'"
+
+
${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
+
${config.services.postgresql.package}/bin/psql -U ${config.services.postgresql.superUser} \
+
-c "CREATE DATABASE ${cfg.database.name} OWNER ${cfg.database.user} ENCODING 'UTF8' TEMPLATE template0"
+
+
${pkgs.sudo}/bin/sudo -u ${cfg.user} \
+
${config.services.postgresql.package}/bin/psql -U ${cfg.database.user} \
+
-d ${cfg.database.name} -f "${runDir}/sql/restyaboard_with_empty_data.sql"
+
+
touch "${cfg.dataDir}/.db-initialized"
+
fi
+
''}
+
'';
+
};
+
+
systemd.timers.restya-board = {
+
description = "restya-board scripts for e.g. email notification";
+
wantedBy = [ "timers.target" ];
+
after = [ "restya-board-init.service" ];
+
requires = [ "restya-board-init.service" ];
+
timerConfig = {
+
OnUnitInactiveSec = "60s";
+
Unit = "restya-board-timers.service";
+
};
+
};
+
+
systemd.services.restya-board-timers = {
+
description = "restya-board scripts for e.g. email notification";
+
serviceConfig.Type = "oneshot";
+
serviceConfig.User = cfg.user;
+
+
after = [ "restya-board-init.service" ];
+
requires = [ "restya-board-init.service" ];
+
+
script = ''
+
/bin/sh ${runDir}/server/php/shell/instant_email_notification.sh 2> /dev/null || true
+
/bin/sh ${runDir}/server/php/shell/periodic_email_notification.sh 2> /dev/null || true
+
/bin/sh ${runDir}/server/php/shell/imap.sh 2> /dev/null || true
+
/bin/sh ${runDir}/server/php/shell/webhook.sh 2> /dev/null || true
+
/bin/sh ${runDir}/server/php/shell/card_due_notification.sh 2> /dev/null || true
+
'';
+
};
+
+
users.extraUsers.restya-board = {
+
isSystemUser = true;
+
createHome = false;
+
home = runDir;
+
group = "restya-board";
+
};
+
users.extraGroups.restya-board = {};
+
+
services.postgresql.enable = mkIf (isNull cfg.database.host) true;
+
+
services.postgresql.identMap = optionalString (isNull cfg.database.host)
+
''
+
restya-board-users restya-board restya_board
+
'';
+
+
services.postgresql.authentication = optionalString (isNull cfg.database.host)
+
''
+
local restya_board all ident map=restya-board-users
+
'';
+
+
};
+
+
}
+