1 { config, pkgs, lib, ... }:
6 cfg = config.services.cloudlog;
8 password = if cfg.database.createLocally
10 else "trim(file_get_contents('${cfg.database.passwordFile}'))";
11 in pkgs.writeText "database.php" ''
13 defined('BASEPATH') OR exit('No direct script access allowed');
14 $active_group = 'default';
15 $query_builder = TRUE;
16 $db['default'] = array(
18 'hostname' => '${cfg.database.host}',
19 'username' => '${cfg.database.user}',
20 'password' => ${password},
21 'database' => '${cfg.database.name}',
22 'dbdriver' => 'mysqli',
25 'db_debug' => (ENVIRONMENT !== 'production'),
28 'char_set' => 'utf8mb4',
29 'dbcollat' => 'utf8mb4_general_ci',
34 'failover' => array(),
35 'save_queries' => TRUE
38 configFile = pkgs.writeText "config.php" ''
40 include('${pkgs.cloudlog}/install/config/config.php');
41 $config['datadir'] = "${cfg.dataDir}/";
42 $config['base_url'] = "${cfg.baseUrl}";
45 package = pkgs.stdenv.mkDerivation rec {
47 version = src.version;
53 ln -s ${configFile} $out/application/config/config.php
54 ln -s ${dbFile} $out/application/config/database.php
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
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
71 options.services.cloudlog = with types; {
72 enable = mkEnableOption "Cloudlog";
75 default = "/var/lib/cloudlog";
76 description = "Cloudlog data directory.";
80 default = "http://localhost";
81 description = "Cloudlog base URL";
86 description = "User account under which Cloudlog runs.";
89 createLocally = mkOption {
92 description = "Create the database and database user locally.";
96 description = "MySQL database host";
97 default = "localhost";
101 description = "MySQL database name.";
102 default = "cloudlog";
106 description = "MySQL user name.";
107 default = "cloudlog";
109 passwordFile = mkOption {
111 description = "MySQL user password file.";
115 poolConfig = mkOption {
116 type = attrsOf (oneOf [ str int bool ]);
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;
126 Options for Cloudlog's PHP-FPM pool.
129 virtualHost = mkOption {
131 default = "localhost";
133 Name of the nginx virtualhost to use and setup. If null, do not setup
137 extraConfig = mkOption {
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>.
146 $config['show_time'] = TRUE;
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
159 interval = mkOption {
163 Specification (in the format described by systemd.time(7)) of the
164 time at which the LoTW upload will occur.
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.
177 interval = mkOption {
181 Specification (in the format described by systemd.time(7)) of the time
182 at which the Clublog upload will occur.
186 update-lotw-users = {
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
196 interval = mkOption {
200 Specification (in the format described by systemd.time(7)) of the
201 time at which the LoTW user update will occur.
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.
214 interval = mkOption {
218 Specification (in the format described by systemd.time(7)) of the
219 time at which the DOK update will occur.
223 update-clublog-scp = {
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
233 interval = mkOption {
237 Specification (in the format described by systemd.time(7)) of the time
238 at which the Clublog SCP update will occur.
247 Whether to periodically update the WWFF database. If enabled, a
248 systemd timer will run the update task as specified by the interval
252 interval = mkOption {
256 Specification (in the format described by systemd.time(7)) of the time
257 at which the WWFF update will occur.
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.
270 interval = mkOption {
274 Specification (in the format described by systemd.time(7)) of the
275 time at which the QRZ upload will occur.
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.
288 interval = mkOption {
292 Specification (in the format described by systemd.time(7)) of the time
293 at which the SOTA update will occur.
298 config = mkIf cfg.enable {
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.";
310 group = config.services.nginx.group;
312 "listen.owner" = config.services.nginx.user;
313 "listen.group" = config.services.nginx.group;
318 services.nginx = mkIf (cfg.virtualHost != null) {
321 "${cfg.virtualHost}" = {
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;
335 services.mysql = mkIf cfg.database.createLocally {
337 ensureDatabases = [ cfg.database.name ];
339 name = cfg.database.user;
340 ensurePermissions = {
341 "${cfg.database.name}.*" = "ALL PRIVILEGES";
348 cloudlog-setup-database = mkIf cfg.database.createLocally {
349 description = "Set up cloudlog database";
352 RemainAfterExit = true;
354 wantedBy = [ "phpfpm-cloudlog.service" ];
355 after = [ "mysql.service" ];
357 mysql = "${config.services.mysql.package}/bin/mysql";
359 if [ ! -f ${cfg.dataDir}/.dbexists ]; then
360 ${mysql} ${cfg.database.name} < ${pkgs.cloudlog}/install/assets/install.sql
361 touch ${cfg.dataDir}/.dbexists
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";
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";
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";
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";
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";
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";
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";
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" ];
408 OnCalendar = cfg.upload-lotw.interval;
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" ];
418 OnCalendar = cfg.upload-clublog.interval;
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" ];
428 OnCalendar = cfg.update-lotw-users.interval;
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" ];
438 OnCalendar = cfg.update-dok.interval;
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" ];
448 OnCalendar = cfg.update-clublog-scp.interval;
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" ];
458 OnCalendar = cfg.update-wwff.interval;
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" ];
468 OnCalendar = cfg.upload-qrz.interval;
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" ];
478 OnCalendar = cfg.update-sota.interval;
484 group = config.services.nginx.group;
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} - -"
496 users.users."${cfg.user}" = {
498 group = config.services.nginx.group;
502 meta.maintainers = with maintainers; [ melling ];