lol

nixos: init module: restya-board

+385
+1
nixos/modules/module-list.nix
··· 608 608 ./services/web-apps/pgpkeyserver-lite.nix 609 609 ./services/web-apps/piwik.nix 610 610 ./services/web-apps/pump.io.nix 611 + ./services/web-apps/restya-board.nix 611 612 ./services/web-apps/tt-rss.nix 612 613 ./services/web-apps/selfoss.nix 613 614 ./services/web-apps/quassel-webserver.nix
+384
nixos/modules/services/web-apps/restya-board.nix
··· 1 + { config, lib, pkgs, ... }: 2 + 3 + with lib; 4 + 5 + # TODO: are these php-packages needed? 6 + #imagick 7 + #php-geoip -> php.ini: extension = geoip.so 8 + #expat 9 + 10 + let 11 + cfg = config.services.restya-board; 12 + 13 + runDir = "/run/restya-board"; 14 + 15 + poolName = "restya-board"; 16 + phpfpmSocketName = "/var/run/phpfpm/${poolName}.sock"; 17 + 18 + in 19 + 20 + { 21 + 22 + ###### interface 23 + 24 + options = { 25 + 26 + services.restya-board = { 27 + 28 + enable = mkEnableOption "restya-board"; 29 + 30 + dataDir = mkOption { 31 + type = types.path; 32 + default = "/var/lib/restya-board"; 33 + example = "/var/lib/restya-board"; 34 + description = '' 35 + Data of the application. 36 + ''; 37 + }; 38 + 39 + user = mkOption { 40 + type = types.str; 41 + default = "restya-board"; 42 + example = "restya-board"; 43 + description = '' 44 + User account under which the web-application runs. 45 + ''; 46 + }; 47 + 48 + group = mkOption { 49 + type = types.str; 50 + default = "nginx"; 51 + example = "nginx"; 52 + description = '' 53 + Group account under which the web-application runs. 54 + ''; 55 + }; 56 + 57 + virtualHost = { 58 + serverName = mkOption { 59 + type = types.str; 60 + default = "restya.board"; 61 + description = '' 62 + Name of the nginx virtualhost to use. 63 + ''; 64 + }; 65 + 66 + listenHost = mkOption { 67 + type = types.str; 68 + default = "localhost"; 69 + description = '' 70 + Listen address for the virtualhost to use. 71 + ''; 72 + }; 73 + 74 + listenPort = mkOption { 75 + type = types.int; 76 + default = 3000; 77 + description = '' 78 + Listen port for the virtualhost to use. 79 + ''; 80 + }; 81 + }; 82 + 83 + database = { 84 + host = mkOption { 85 + type = types.nullOr types.str; 86 + default = null; 87 + description = '' 88 + Host of the database. Leave 'null' to use a local PostgreSQL database. 89 + A local PostgreSQL database is initialized automatically. 90 + ''; 91 + }; 92 + 93 + port = mkOption { 94 + type = types.nullOr types.int; 95 + default = 5432; 96 + description = '' 97 + The database's port. 98 + ''; 99 + }; 100 + 101 + name = mkOption { 102 + type = types.str; 103 + default = "restya_board"; 104 + description = '' 105 + Name of the database. The database must exist. 106 + ''; 107 + }; 108 + 109 + user = mkOption { 110 + type = types.str; 111 + default = "restya_board"; 112 + description = '' 113 + The database user. The user must exist and have access to 114 + the specified database. 115 + ''; 116 + }; 117 + 118 + passwordFile = mkOption { 119 + type = types.nullOr types.str; 120 + default = null; 121 + description = '' 122 + The database user's password. 'null' if no password is set. 123 + ''; 124 + }; 125 + }; 126 + 127 + email = { 128 + server = mkOption { 129 + type = types.nullOr types.str; 130 + default = null; 131 + example = "localhost"; 132 + description = '' 133 + Hostname to send outgoing mail. Null to use the system MTA. 134 + ''; 135 + }; 136 + 137 + port = mkOption { 138 + type = types.int; 139 + default = 25; 140 + description = '' 141 + Port used to connect to SMTP server. 142 + ''; 143 + }; 144 + 145 + login = mkOption { 146 + type = types.str; 147 + default = ""; 148 + description = '' 149 + SMTP authentication login used when sending outgoing mail. 150 + ''; 151 + }; 152 + 153 + password = mkOption { 154 + type = types.str; 155 + default = ""; 156 + description = '' 157 + SMTP authentication password used when sending outgoing mail. 158 + 159 + ATTENTION: The password is stored world-readable in the nix-store! 160 + ''; 161 + }; 162 + }; 163 + 164 + timezone = mkOption { 165 + type = types.lines; 166 + default = "GMT"; 167 + description = '' 168 + Timezone the web-app runs in. 169 + ''; 170 + }; 171 + 172 + }; 173 + 174 + }; 175 + 176 + 177 + ###### implementation 178 + 179 + config = mkIf cfg.enable { 180 + 181 + services.phpfpm.poolConfigs = { 182 + "${poolName}" = '' 183 + listen = "${phpfpmSocketName}"; 184 + listen.owner = nginx 185 + listen.group = nginx 186 + listen.mode = 0600 187 + user = ${cfg.user} 188 + group = ${cfg.group} 189 + pm = dynamic 190 + pm.max_children = 75 191 + pm.start_servers = 10 192 + pm.min_spare_servers = 5 193 + pm.max_spare_servers = 20 194 + pm.max_requests = 500 195 + catch_workers_output = 1 196 + ''; 197 + }; 198 + 199 + services.phpfpm.phpOptions = '' 200 + date.timezone = "CET" 201 + 202 + ${optionalString (!isNull cfg.email.server) '' 203 + SMTP = ${cfg.email.server} 204 + smtp_port = ${toString cfg.email.port} 205 + auth_username = ${cfg.email.login} 206 + auth_password = ${cfg.email.password} 207 + ''} 208 + ''; 209 + 210 + services.nginx.enable = true; 211 + services.nginx.virtualHosts."${cfg.virtualHost.serverName}" = { 212 + listen = [ { addr = cfg.virtualHost.listenHost; port = cfg.virtualHost.listenPort; } ]; 213 + serverName = cfg.virtualHost.serverName; 214 + root = runDir; 215 + extraConfig = '' 216 + index index.html index.php; 217 + 218 + gzip on; 219 + gzip_disable "msie6"; 220 + 221 + gzip_comp_level 6; 222 + gzip_min_length 1100; 223 + gzip_buffers 16 8k; 224 + gzip_proxied any; 225 + gzip_types text/plain application/xml text/css text/js text/xml application/x-javascript text/javascript application/json application/xml+rss; 226 + 227 + client_max_body_size 300M; 228 + 229 + rewrite ^/oauth/authorize$ /server/php/authorize.php last; 230 + rewrite ^/oauth_callback/([a-zA-Z0-9_\.]*)/([a-zA-Z0-9_\.]*)$ /server/php/oauth_callback.php?plugin=$1&code=$2 last; 231 + rewrite ^/download/([0-9]*)/([a-zA-Z0-9_\.]*)$ /server/php/download.php?id=$1&hash=$2 last; 232 + rewrite ^/ical/([0-9]*)/([0-9]*)/([a-z0-9]*).ics$ /server/php/ical.php?board_id=$1&user_id=$2&hash=$3 last; 233 + rewrite ^/api/(.*)$ /server/php/R/r.php?_url=$1&$args last; 234 + rewrite ^/api_explorer/api-docs/$ /client/api_explorer/api-docs/index.php last; 235 + ''; 236 + 237 + locations."/".root = "${runDir}/client"; 238 + 239 + locations."~ \.php$" = { 240 + tryFiles = "$uri =404"; 241 + extraConfig = '' 242 + include ${pkgs.nginx}/conf/fastcgi_params; 243 + fastcgi_pass unix:${phpfpmSocketName}; 244 + fastcgi_index index.php; 245 + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 246 + 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"; 247 + ''; 248 + }; 249 + 250 + locations."~* \.(css|js|less|html|ttf|woff|jpg|jpeg|gif|png|bmp|ico)" = { 251 + root = "${runDir}/client"; 252 + extraConfig = '' 253 + if (-f $request_filename) { 254 + break; 255 + } 256 + rewrite ^/img/([a-zA-Z_]*)/([a-zA-Z_]*)/([a-zA-Z0-9_\.]*)$ /server/php/image.php?size=$1&model=$2&filename=$3 last; 257 + add_header Cache-Control public; 258 + add_header Cache-Control must-revalidate; 259 + expires 7d; 260 + ''; 261 + }; 262 + }; 263 + 264 + systemd.services.restya-board-init = { 265 + description = "Restya board initialization"; 266 + serviceConfig.Type = "oneshot"; 267 + serviceConfig.RemainAfterExit = true; 268 + 269 + wantedBy = [ "multi-user.target" ]; 270 + requires = [ "postgresql.service" ]; 271 + after = [ "network.target" "postgresql.service" ]; 272 + 273 + script = '' 274 + rm -rf "${runDir}" 275 + mkdir -m 750 -p "${runDir}" 276 + cp -r "${pkgs.restya-board}/"* "${runDir}" 277 + sed -i "s/@restya.com/@${cfg.virtualHost.serverName}/g" "${runDir}/sql/restyaboard_with_empty_data.sql" 278 + rm -rf "${runDir}/media" 279 + rm -rf "${runDir}/client/img" 280 + chmod -R 0750 "${runDir}" 281 + 282 + sed -i "s@^php@${config.services.phpfpm.phpPackage}/bin/php@" "${runDir}/server/php/shell/"*.sh 283 + 284 + ${if (isNull cfg.database.host) then '' 285 + sed -i "s/^.*'R_DB_HOST'.*$/define('R_DB_HOST', 'localhost');/g" "${runDir}/server/php/config.inc.php" 286 + sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', 'restya');/g" "${runDir}/server/php/config.inc.php" 287 + '' else '' 288 + sed -i "s/^.*'R_DB_HOST'.*$/define('R_DB_HOST', '${cfg.database.host}');/g" "${runDir}/server/php/config.inc.php" 289 + sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', '$(<${cfg.database.dbPassFile})');/g" "${runDir}/server/php/config.inc.php" 290 + ''} 291 + sed -i "s/^.*'R_DB_PORT'.*$/define('R_DB_PORT', '${toString cfg.database.port}');/g" "${runDir}/server/php/config.inc.php" 292 + sed -i "s/^.*'R_DB_NAME'.*$/define('R_DB_NAME', '${cfg.database.name}');/g" "${runDir}/server/php/config.inc.php" 293 + sed -i "s/^.*'R_DB_USER'.*$/define('R_DB_USER', '${cfg.database.user}');/g" "${runDir}/server/php/config.inc.php" 294 + 295 + chmod 0400 "${runDir}/server/php/config.inc.php" 296 + 297 + ln -sf "${cfg.dataDir}/media" "${runDir}/media" 298 + ln -sf "${cfg.dataDir}/client/img" "${runDir}/client/img" 299 + 300 + chmod g+w "${runDir}/tmp/cache" 301 + chown -R "${cfg.user}"."${cfg.group}" "${runDir}" 302 + 303 + 304 + mkdir -m 0750 -p "${cfg.dataDir}" 305 + mkdir -m 0750 -p "${cfg.dataDir}/media" 306 + mkdir -m 0750 -p "${cfg.dataDir}/client/img" 307 + cp -r "${pkgs.restya-board}/media/"* "${cfg.dataDir}/media" 308 + cp -r "${pkgs.restya-board}/client/img/"* "${cfg.dataDir}/client/img" 309 + chown "${cfg.user}"."${cfg.group}" "${cfg.dataDir}" 310 + chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/media" 311 + chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/client/img" 312 + 313 + ${optionalString (isNull cfg.database.host) '' 314 + if ! [ -e "${cfg.dataDir}/.db-initialized" ]; then 315 + ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \ 316 + ${config.services.postgresql.package}/bin/psql -U ${config.services.postgresql.superUser} \ 317 + -c "CREATE USER ${cfg.database.user} WITH ENCRYPTED PASSWORD 'restya'" 318 + 319 + ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \ 320 + ${config.services.postgresql.package}/bin/psql -U ${config.services.postgresql.superUser} \ 321 + -c "CREATE DATABASE ${cfg.database.name} OWNER ${cfg.database.user} ENCODING 'UTF8' TEMPLATE template0" 322 + 323 + ${pkgs.sudo}/bin/sudo -u ${cfg.user} \ 324 + ${config.services.postgresql.package}/bin/psql -U ${cfg.database.user} \ 325 + -d ${cfg.database.name} -f "${runDir}/sql/restyaboard_with_empty_data.sql" 326 + 327 + touch "${cfg.dataDir}/.db-initialized" 328 + fi 329 + ''} 330 + ''; 331 + }; 332 + 333 + systemd.timers.restya-board = { 334 + description = "restya-board scripts for e.g. email notification"; 335 + wantedBy = [ "timers.target" ]; 336 + after = [ "restya-board-init.service" ]; 337 + requires = [ "restya-board-init.service" ]; 338 + timerConfig = { 339 + OnUnitInactiveSec = "60s"; 340 + Unit = "restya-board-timers.service"; 341 + }; 342 + }; 343 + 344 + systemd.services.restya-board-timers = { 345 + description = "restya-board scripts for e.g. email notification"; 346 + serviceConfig.Type = "oneshot"; 347 + serviceConfig.User = cfg.user; 348 + 349 + after = [ "restya-board-init.service" ]; 350 + requires = [ "restya-board-init.service" ]; 351 + 352 + script = '' 353 + /bin/sh ${runDir}/server/php/shell/instant_email_notification.sh 2> /dev/null || true 354 + /bin/sh ${runDir}/server/php/shell/periodic_email_notification.sh 2> /dev/null || true 355 + /bin/sh ${runDir}/server/php/shell/imap.sh 2> /dev/null || true 356 + /bin/sh ${runDir}/server/php/shell/webhook.sh 2> /dev/null || true 357 + /bin/sh ${runDir}/server/php/shell/card_due_notification.sh 2> /dev/null || true 358 + ''; 359 + }; 360 + 361 + users.extraUsers.restya-board = { 362 + isSystemUser = true; 363 + createHome = false; 364 + home = runDir; 365 + group = "restya-board"; 366 + }; 367 + users.extraGroups.restya-board = {}; 368 + 369 + services.postgresql.enable = mkIf (isNull cfg.database.host) true; 370 + 371 + services.postgresql.identMap = optionalString (isNull cfg.database.host) 372 + '' 373 + restya-board-users restya-board restya_board 374 + ''; 375 + 376 + services.postgresql.authentication = optionalString (isNull cfg.database.host) 377 + '' 378 + local restya_board all ident map=restya-board-users 379 + ''; 380 + 381 + }; 382 + 383 + } 384 +