at 23.11-pre 16 kB view raw
1{ config, pkgs, lib, ... }: 2 3with lib; 4 5let 6 cfg = config.services.cloudlog; 7 dbFile = let 8 password = if cfg.database.createLocally 9 then "''" 10 else "trim(file_get_contents('${cfg.database.passwordFile}'))"; 11 in pkgs.writeText "database.php" '' 12 <?php 13 defined('BASEPATH') OR exit('No direct script access allowed'); 14 $active_group = 'default'; 15 $query_builder = TRUE; 16 $db['default'] = array( 17 'dsn' => "", 18 'hostname' => '${cfg.database.host}', 19 'username' => '${cfg.database.user}', 20 'password' => ${password}, 21 'database' => '${cfg.database.name}', 22 'dbdriver' => 'mysqli', 23 'dbprefix' => "", 24 'pconnect' => TRUE, 25 'db_debug' => (ENVIRONMENT !== 'production'), 26 'cache_on' => FALSE, 27 'cachedir' => "", 28 'char_set' => 'utf8mb4', 29 'dbcollat' => 'utf8mb4_general_ci', 30 'swap_pre' => "", 31 'encrypt' => FALSE, 32 'compress' => FALSE, 33 'stricton' => FALSE, 34 'failover' => array(), 35 'save_queries' => TRUE 36 ); 37 ''; 38 configFile = pkgs.writeText "config.php" '' 39 <?php 40 include('${pkgs.cloudlog}/install/config/config.php'); 41 $config['datadir'] = "${cfg.dataDir}/"; 42 $config['base_url'] = "${cfg.baseUrl}"; 43 ${cfg.extraConfig} 44 ''; 45 package = pkgs.stdenv.mkDerivation rec { 46 pname = "cloudlog"; 47 version = src.version; 48 src = pkgs.cloudlog; 49 installPhase = '' 50 mkdir -p $out 51 cp -r * $out/ 52 53 ln -s ${configFile} $out/application/config/config.php 54 ln -s ${dbFile} $out/application/config/database.php 55 56 # link writable directories 57 for directory in updates uploads backup logbook; do 58 rm -rf $out/$directory 59 ln -s ${cfg.dataDir}/$directory $out/$directory 60 done 61 62 # link writable asset files 63 for asset in dok sota wwff; do 64 rm -rf $out/assets/json/$asset.txt 65 ln -s ${cfg.dataDir}/assets/json/$asset.txt $out/assets/json/$asset.txt 66 done 67 ''; 68 }; 69in 70{ 71 options.services.cloudlog = with types; { 72 enable = mkEnableOption (mdDoc "Whether to enable Cloudlog"); 73 dataDir = mkOption { 74 type = str; 75 default = "/var/lib/cloudlog"; 76 description = mdDoc "Cloudlog data directory."; 77 }; 78 baseUrl = mkOption { 79 type = str; 80 default = "http://localhost"; 81 description = mdDoc "Cloudlog base URL"; 82 }; 83 user = mkOption { 84 type = str; 85 default = "cloudlog"; 86 description = mdDoc "User account under which Cloudlog runs."; 87 }; 88 database = { 89 createLocally = mkOption { 90 type = types.bool; 91 default = true; 92 description = lib.mdDoc "Create the database and database user locally."; 93 }; 94 host = mkOption { 95 type = str; 96 description = mdDoc "MySQL database host"; 97 default = "localhost"; 98 }; 99 name = mkOption { 100 type = str; 101 description = mdDoc "MySQL database name."; 102 default = "cloudlog"; 103 }; 104 user = mkOption { 105 type = str; 106 description = mdDoc "MySQL user name."; 107 default = "cloudlog"; 108 }; 109 passwordFile = mkOption { 110 type = nullOr str; 111 description = mdDoc "MySQL user password file."; 112 default = null; 113 }; 114 }; 115 poolConfig = mkOption { 116 type = attrsOf (oneOf [ str int bool ]); 117 default = { 118 "pm" = "dynamic"; 119 "pm.max_children" = 32; 120 "pm.start_servers" = 2; 121 "pm.min_spare_servers" = 2; 122 "pm.max_spare_servers" = 4; 123 "pm.max_requests" = 500; 124 }; 125 description = mdDoc '' 126 Options for Cloudlog's PHP-FPM pool. 127 ''; 128 }; 129 virtualHost = mkOption { 130 type = nullOr str; 131 default = "localhost"; 132 description = mdDoc '' 133 Name of the nginx virtualhost to use and setup. If null, do not setup 134 any virtualhost. 135 ''; 136 }; 137 extraConfig = mkOption { 138 description = mdDoc '' 139 Any additional text to be appended to the config.php 140 configuration file. This is a PHP script. For configuration 141 settings, see <https://github.com/magicbug/Cloudlog/wiki/Cloudlog.php-Configuration-File>. 142 ''; 143 default = ""; 144 type = str; 145 example = '' 146 $config['show_time'] = TRUE; 147 ''; 148 }; 149 upload-lotw = { 150 enable = mkOption { 151 type = bool; 152 default = true; 153 description = mdDoc '' 154 Whether to periodically upload logs to LoTW. If enabled, a systemd 155 timer will run the log upload task as specified by the interval 156 option. 157 ''; 158 }; 159 interval = mkOption { 160 type = str; 161 default = "daily"; 162 description = mdDoc '' 163 Specification (in the format described by systemd.time(7)) of the 164 time at which the LoTW upload will occur. 165 ''; 166 }; 167 }; 168 upload-clublog = { 169 enable = mkOption { 170 type = bool; 171 default = true; 172 description = mdDoc '' 173 Whether to periodically upload logs to Clublog. If enabled, a systemd 174 timer will run the log upload task as specified by the interval option. 175 ''; 176 }; 177 interval = mkOption { 178 type = str; 179 default = "daily"; 180 description = mdDoc '' 181 Specification (in the format described by systemd.time(7)) of the time 182 at which the Clublog upload will occur. 183 ''; 184 }; 185 }; 186 update-lotw-users = { 187 enable = mkOption { 188 type = bool; 189 default = true; 190 description = mdDoc '' 191 Whether to periodically update the list of LoTW users. If enabled, a 192 systemd timer will run the update task as specified by the interval 193 option. 194 ''; 195 }; 196 interval = mkOption { 197 type = str; 198 default = "weekly"; 199 description = mdDoc '' 200 Specification (in the format described by systemd.time(7)) of the 201 time at which the LoTW user update will occur. 202 ''; 203 }; 204 }; 205 update-dok = { 206 enable = mkOption { 207 type = bool; 208 default = true; 209 description = mdDoc '' 210 Whether to periodically update the DOK resource file. If enabled, a 211 systemd timer will run the update task as specified by the interval option. 212 ''; 213 }; 214 interval = mkOption { 215 type = str; 216 default = "monthly"; 217 description = mdDoc '' 218 Specification (in the format described by systemd.time(7)) of the 219 time at which the DOK update will occur. 220 ''; 221 }; 222 }; 223 update-clublog-scp = { 224 enable = mkOption { 225 type = bool; 226 default = true; 227 description = mdDoc '' 228 Whether to periodically update the Clublog SCP database. If enabled, 229 a systemd timer will run the update task as specified by the interval 230 option. 231 ''; 232 }; 233 interval = mkOption { 234 type = str; 235 default = "monthly"; 236 description = mdDoc '' 237 Specification (in the format described by systemd.time(7)) of the time 238 at which the Clublog SCP update will occur. 239 ''; 240 }; 241 }; 242 update-wwff = { 243 enable = mkOption { 244 type = bool; 245 default = true; 246 description = mdDoc '' 247 Whether to periodically update the WWFF database. If enabled, a 248 systemd timer will run the update task as specified by the interval 249 option. 250 ''; 251 }; 252 interval = mkOption { 253 type = str; 254 default = "monthly"; 255 description = mdDoc '' 256 Specification (in the format described by systemd.time(7)) of the time 257 at which the WWFF update will occur. 258 ''; 259 }; 260 }; 261 upload-qrz = { 262 enable = mkOption { 263 type = bool; 264 default = true; 265 description = mdDoc '' 266 Whether to periodically upload logs to QRZ. If enabled, a systemd 267 timer will run the update task as specified by the interval option. 268 ''; 269 }; 270 interval = mkOption { 271 type = str; 272 default = "daily"; 273 description = mdDoc '' 274 Specification (in the format described by systemd.time(7)) of the 275 time at which the QRZ upload will occur. 276 ''; 277 }; 278 }; 279 update-sota = { 280 enable = mkOption { 281 type = bool; 282 default = true; 283 description = mdDoc '' 284 Whether to periodically update the SOTA database. If enabled, a 285 systemd timer will run the update task as specified by the interval option. 286 ''; 287 }; 288 interval = mkOption { 289 type = str; 290 default = "monthly"; 291 description = mdDoc '' 292 Specification (in the format described by systemd.time(7)) of the time 293 at which the SOTA update will occur. 294 ''; 295 }; 296 }; 297 }; 298 config = mkIf cfg.enable { 299 300 assertions = [ 301 { 302 assertion = cfg.database.createLocally -> cfg.database.passwordFile == null; 303 message = "services.cloudlog.database.passwordFile cannot be specified if services.cloudlog.database.createLocally is set to true."; 304 } 305 ]; 306 307 services.phpfpm = { 308 pools.cloudlog = { 309 inherit (cfg) user; 310 group = config.services.nginx.group; 311 settings = { 312 "listen.owner" = config.services.nginx.user; 313 "listen.group" = config.services.nginx.group; 314 } // cfg.poolConfig; 315 }; 316 }; 317 318 services.nginx = mkIf (cfg.virtualHost != null) { 319 enable = true; 320 virtualHosts = { 321 "${cfg.virtualHost}" = { 322 root = "${package}"; 323 locations."/".tryFiles = "$uri /index.php$is_args$args"; 324 locations."~ ^/index.php(/|$)".extraConfig = '' 325 include ${config.services.nginx.package}/conf/fastcgi_params; 326 include ${pkgs.nginx}/conf/fastcgi.conf; 327 fastcgi_split_path_info ^(.+\.php)(.+)$; 328 fastcgi_pass unix:${config.services.phpfpm.pools.cloudlog.socket}; 329 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 330 ''; 331 }; 332 }; 333 }; 334 335 services.mysql = mkIf cfg.database.createLocally { 336 enable = true; 337 ensureDatabases = [ cfg.database.name ]; 338 ensureUsers = [{ 339 name = cfg.database.user; 340 ensurePermissions = { 341 "${cfg.database.name}.*" = "ALL PRIVILEGES"; 342 }; 343 }]; 344 }; 345 346 systemd = { 347 services = { 348 cloudlog-setup-database = mkIf cfg.database.createLocally { 349 description = "Set up cloudlog database"; 350 serviceConfig = { 351 Type = "oneshot"; 352 RemainAfterExit = true; 353 }; 354 wantedBy = [ "phpfpm-cloudlog.service" ]; 355 after = [ "mysql.service" ]; 356 script = let 357 mysql = "${config.services.mysql.package}/bin/mysql"; 358 in '' 359 if [ ! -f ${cfg.dataDir}/.dbexists ]; then 360 ${mysql} ${cfg.database.name} < ${pkgs.cloudlog}/install/assets/install.sql 361 touch ${cfg.dataDir}/.dbexists 362 fi 363 ''; 364 }; 365 cloudlog-upload-lotw = { 366 description = "Upload QSOs to LoTW if certs have been provided"; 367 enable = cfg.upload-lotw.enable; 368 script = "${pkgs.curl}/bin/curl -s ${cfg.baseUrl}/lotw/lotw_upload"; 369 }; 370 cloudlog-update-lotw-users = { 371 description = "Update LOTW Users Database"; 372 enable = cfg.update-lotw-users.enable; 373 script = "${pkgs.curl}/bin/curl -s ${cfg.baseUrl}/lotw/load_users"; 374 }; 375 cloudlog-update-dok = { 376 description = "Update DOK File for autocomplete"; 377 enable = cfg.update-dok.enable; 378 script = "${pkgs.curl}/bin/curl -s ${cfg.baseUrl}/update/update_dok"; 379 }; 380 cloudlog-update-clublog-scp = { 381 description = "Update Clublog SCP Database File"; 382 enable = cfg.update-clublog-scp.enable; 383 script = "${pkgs.curl}/bin/curl -s ${cfg.baseUrl}/update/update_clublog_scp"; 384 }; 385 cloudlog-update-wwff = { 386 description = "Update WWFF File for autocomplete"; 387 enable = cfg.update-wwff.enable; 388 script = "${pkgs.curl}/bin/curl -s ${cfg.baseUrl}/update/update_wwff"; 389 }; 390 cloudlog-upload-qrz = { 391 description = "Upload QSOs to QRZ Logbook"; 392 enable = cfg.upload-qrz.enable; 393 script = "${pkgs.curl}/bin/curl -s ${cfg.baseUrl}/qrz/upload"; 394 }; 395 cloudlog-update-sota = { 396 description = "Update SOTA File for autocomplete"; 397 enable = cfg.update-sota.enable; 398 script = "${pkgs.curl}/bin/curl -s ${cfg.baseUrl}/update/update_sota"; 399 }; 400 }; 401 timers = { 402 cloudlog-upload-lotw = { 403 enable = cfg.upload-lotw.enable; 404 wantedBy = [ "timers.target" ]; 405 partOf = [ "cloudlog-upload-lotw.service" ]; 406 after = [ "phpfpm-cloudlog.service" ]; 407 timerConfig = { 408 OnCalendar = cfg.upload-lotw.interval; 409 Persistent = true; 410 }; 411 }; 412 cloudlog-upload-clublog = { 413 enable = cfg.upload-clublog.enable; 414 wantedBy = [ "timers.target" ]; 415 partOf = [ "cloudlog-upload-clublog.service" ]; 416 after = [ "phpfpm-cloudlog.service" ]; 417 timerConfig = { 418 OnCalendar = cfg.upload-clublog.interval; 419 Persistent = true; 420 }; 421 }; 422 cloudlog-update-lotw-users = { 423 enable = cfg.update-lotw-users.enable; 424 wantedBy = [ "timers.target" ]; 425 partOf = [ "cloudlog-update-lotw-users.service" ]; 426 after = [ "phpfpm-cloudlog.service" ]; 427 timerConfig = { 428 OnCalendar = cfg.update-lotw-users.interval; 429 Persistent = true; 430 }; 431 }; 432 cloudlog-update-dok = { 433 enable = cfg.update-dok.enable; 434 wantedBy = [ "timers.target" ]; 435 partOf = [ "cloudlog-update-dok.service" ]; 436 after = [ "phpfpm-cloudlog.service" ]; 437 timerConfig = { 438 OnCalendar = cfg.update-dok.interval; 439 Persistent = true; 440 }; 441 }; 442 cloudlog-update-clublog-scp = { 443 enable = cfg.update-clublog-scp.enable; 444 wantedBy = [ "timers.target" ]; 445 partOf = [ "cloudlog-update-clublog-scp.service" ]; 446 after = [ "phpfpm-cloudlog.service" ]; 447 timerConfig = { 448 OnCalendar = cfg.update-clublog-scp.interval; 449 Persistent = true; 450 }; 451 }; 452 cloudlog-update-wwff = { 453 enable = cfg.update-wwff.enable; 454 wantedBy = [ "timers.target" ]; 455 partOf = [ "cloudlog-update-wwff.service" ]; 456 after = [ "phpfpm-cloudlog.service" ]; 457 timerConfig = { 458 OnCalendar = cfg.update-wwff.interval; 459 Persistent = true; 460 }; 461 }; 462 cloudlog-upload-qrz = { 463 enable = cfg.upload-qrz.enable; 464 wantedBy = [ "timers.target" ]; 465 partOf = [ "cloudlog-upload-qrz.service" ]; 466 after = [ "phpfpm-cloudlog.service" ]; 467 timerConfig = { 468 OnCalendar = cfg.upload-qrz.interval; 469 Persistent = true; 470 }; 471 }; 472 cloudlog-update-sota = { 473 enable = cfg.update-sota.enable; 474 wantedBy = [ "timers.target" ]; 475 partOf = [ "cloudlog-update-sota.service" ]; 476 after = [ "phpfpm-cloudlog.service" ]; 477 timerConfig = { 478 OnCalendar = cfg.update-sota.interval; 479 Persistent = true; 480 }; 481 }; 482 }; 483 tmpfiles.rules = let 484 group = config.services.nginx.group; 485 in [ 486 "d ${cfg.dataDir} 0750 ${cfg.user} ${group} - -" 487 "d ${cfg.dataDir}/updates 0750 ${cfg.user} ${group} - -" 488 "d ${cfg.dataDir}/uploads 0750 ${cfg.user} ${group} - -" 489 "d ${cfg.dataDir}/backup 0750 ${cfg.user} ${group} - -" 490 "d ${cfg.dataDir}/logbook 0750 ${cfg.user} ${group} - -" 491 "d ${cfg.dataDir}/assets/json 0750 ${cfg.user} ${group} - -" 492 "d ${cfg.dataDir}/assets/qslcard 0750 ${cfg.user} ${group} - -" 493 ]; 494 }; 495 496 users.users."${cfg.user}" = { 497 isSystemUser = true; 498 group = config.services.nginx.group; 499 }; 500 }; 501 502 meta.maintainers = with maintainers; [ melling ]; 503}