nixos/cloudlog: init

This commit is contained in:
Matt Melling 2023-01-01 23:19:37 +00:00
parent 31e3844aea
commit c281dd3e05
No known key found for this signature in database
GPG Key ID: 6D94F6DB60A64FFE
6 changed files with 531 additions and 0 deletions

View File

@ -53,6 +53,13 @@
<link linkend="opt-services.printing.cups-pdf.enable">services.printing.cups-pdf</link>.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://www.magicbug.co.uk/cloudlog/">Cloudlog</link>,
a web-based Amateur Radio logging application. Available as
<link linkend="opt-services.cloudlog.enable">services.cloudlog</link>.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://github.com/junegunn/fzf">fzf</link>,

View File

@ -22,6 +22,8 @@ In addition to numerous new and upgraded packages, this release has the followin
- [cups-pdf-to-pdf](https://github.com/alexivkin/CUPS-PDF-to-PDF), a pdf-generating cups backend based on [cups-pdf](https://www.cups-pdf.de/). Available as [services.printing.cups-pdf](#opt-services.printing.cups-pdf.enable).
- [Cloudlog](https://www.magicbug.co.uk/cloudlog/), a web-based Amateur Radio logging application. Available as [services.cloudlog](#opt-services.cloudlog.enable).
- [fzf](https://github.com/junegunn/fzf), a command line fuzzyfinder. Available as [programs.fzf](#opt-programs.fzf.fuzzyCompletion).
- [atuin](https://github.com/ellie/atuin), a sync server for shell history. Available as [services.atuin](#opt-services.atuin.enable).

View File

@ -1114,6 +1114,7 @@
./services/web-apps/bookstack.nix
./services/web-apps/calibre-web.nix
./services/web-apps/changedetection-io.nix
./services/web-apps/cloudlog.nix
./services/web-apps/code-server.nix
./services/web-apps/convos.nix
./services/web-apps/dex.nix

View File

@ -0,0 +1,502 @@
{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.services.cloudlog;
dbFile = let
password = if cfg.database.createLocally
then "''"
else "trim(file_get_contents('${cfg.database.passwordFile}'))";
in pkgs.writeText "database.php" ''
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
$active_group = 'default';
$query_builder = TRUE;
$db['default'] = array(
'dsn' => "",
'hostname' => '${cfg.database.host}',
'username' => '${cfg.database.user}',
'password' => ${password},
'database' => '${cfg.database.name}',
'dbdriver' => 'mysqli',
'dbprefix' => "",
'pconnect' => TRUE,
'db_debug' => (ENVIRONMENT !== 'production'),
'cache_on' => FALSE,
'cachedir' => "",
'char_set' => 'utf8mb4',
'dbcollat' => 'utf8mb4_general_ci',
'swap_pre' => "",
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE
);
'';
configFile = pkgs.writeText "config.php" ''
${strings.fileContents "${pkgs.cloudlog}/install/config/config.php"}
$config['datadir'] = "${cfg.dataDir}/";
$config['base_url'] = "${cfg.baseUrl}";
${cfg.extraConfig}
'';
package = pkgs.stdenv.mkDerivation rec {
pname = "cloudlog";
version = src.version;
src = pkgs.cloudlog;
installPhase = ''
mkdir -p $out
cp -r * $out/
ln -s ${configFile} $out/application/config/config.php
ln -s ${dbFile} $out/application/config/database.php
# link writable directories
for directory in updates uploads backup logbook; do
rm -rf $out/$directory
ln -s ${cfg.dataDir}/$directory $out/$directory
done
# link writable asset files
for asset in dok sota wwff; do
rm -rf $out/assets/json/$asset.txt
ln -s ${cfg.dataDir}/assets/json/$asset.txt $out/assets/json/$asset.txt
done
'';
};
in
{
options.services.cloudlog = with types; {
enable = mkEnableOption (mdDoc "Whether to enable Cloudlog.");
dataDir = mkOption {
type = str;
default = "/var/lib/cloudlog";
description = mdDoc "Cloudlog data directory.";
};
baseUrl = mkOption {
type = str;
default = "http://localhost";
description = mdDoc "Cloudlog base URL";
};
user = mkOption {
type = str;
default = "cloudlog";
description = mdDoc "User account under which Cloudlog runs.";
};
database = {
createLocally = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc "Create the database and database user locally.";
};
host = mkOption {
type = str;
description = mdDoc "MySQL database host";
default = "localhost";
};
name = mkOption {
type = str;
description = mdDoc "MySQL database name.";
default = "cloudlog";
};
user = mkOption {
type = str;
description = mdDoc "MySQL user name.";
default = "cloudlog";
};
passwordFile = mkOption {
type = nullOr str;
description = mdDoc "MySQL user password file.";
default = null;
};
};
poolConfig = mkOption {
type = attrsOf (oneOf [ str int bool ]);
default = {
"pm" = "dynamic";
"pm.max_children" = 32;
"pm.start_servers" = 2;
"pm.min_spare_servers" = 2;
"pm.max_spare_servers" = 4;
"pm.max_requests" = 500;
};
description = mdDoc ''
Options for Cloudlog's PHP-FPM pool.
'';
};
virtualHost = mkOption {
type = nullOr str;
default = "localhost";
description = mdDoc ''
Name of the nginx virtualhost to use and setup. If null, do not setup
any virtualhost.
'';
};
extraConfig = mkOption {
description = mdDoc ''
Any additional text to be appended to the config.php
configuration file. This is a PHP script. For configuration
settings, see <https://github.com/magicbug/Cloudlog/wiki/Cloudlog.php-Configuration-File>.
'';
default = "";
type = str;
example = ''
$config['show_time'] = TRUE;
'';
};
upload-lotw = {
enable = mkOption {
type = bool;
default = true;
description = mdDoc ''
Whether to periodically upload logs to LoTW. If enabled, a systemd
timer will run the log upload task as specified by the interval
option.
'';
};
interval = mkOption {
type = str;
default = "daily";
description = mdDoc ''
Specification (in the format described by systemd.time(7)) of the
time at which the LoTW upload will occur.
'';
};
};
upload-clublog = {
enable = mkOption {
type = bool;
default = true;
description = mdDoc ''
Whether to periodically upload logs to Clublog. If enabled, a systemd
timer will run the log upload task as specified by the interval option.
'';
};
interval = mkOption {
type = str;
default = "daily";
description = mdDoc ''
Specification (in the format described by systemd.time(7)) of the time
at which the Clublog upload will occur.
'';
};
};
update-lotw-users = {
enable = mkOption {
type = bool;
default = true;
description = mdDoc ''
Whether to periodically update the list of LoTW users. If enabled, a
systemd timer will run the update task as specified by the interval
option.
'';
};
interval = mkOption {
type = str;
default = "weekly";
description = mdDoc ''
Specification (in the format described by systemd.time(7)) of the
time at which the LoTW user update will occur.
'';
};
};
update-dok = {
enable = mkOption {
type = bool;
default = true;
description = mdDoc ''
Whether to periodically update the DOK resource file. If enabled, a
systemd timer will run the update task as specified by the interval option.
'';
};
interval = mkOption {
type = str;
default = "monthly";
description = mdDoc ''
Specification (in the format described by systemd.time(7)) of the
time at which the DOK update will occur.
'';
};
};
update-clublog-scp = {
enable = mkOption {
type = bool;
default = true;
description = mdDoc ''
Whether to periodically update the Clublog SCP database. If enabled,
a systemd timer will run the update task as specified by the interval
option.
'';
};
interval = mkOption {
type = str;
default = "monthly";
description = mdDoc ''
Specification (in the format described by systemd.time(7)) of the time
at which the Clublog SCP update will occur.
'';
};
};
update-wwff = {
enable = mkOption {
type = bool;
default = true;
description = mdDoc ''
Whether to periodically update the WWFF database. If enabled, a
systemd timer will run the update task as specified by the interval
option.
'';
};
interval = mkOption {
type = str;
default = "monthly";
description = mdDoc ''
Specification (in the format described by systemd.time(7)) of the time
at which the WWFF update will occur.
'';
};
};
upload-qrz = {
enable = mkOption {
type = bool;
default = true;
description = mdDoc ''
Whether to periodically upload logs to QRZ. If enabled, a systemd
timer will run the update task as specified by the interval option.
'';
};
interval = mkOption {
type = str;
default = "daily";
description = mdDoc ''
Specification (in the format described by systemd.time(7)) of the
time at which the QRZ upload will occur.
'';
};
};
update-sota = {
enable = mkOption {
type = bool;
default = true;
description = mdDoc ''
Whether to periodically update the SOTA database. If enabled, a
systemd timer will run the update task as specified by the interval option.
'';
};
interval = mkOption {
type = str;
default = "monthly";
description = mdDoc ''
Specification (in the format described by systemd.time(7)) of the time
at which the SOTA update will occur.
'';
};
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
message = "services.cloudlog.database.passwordFile cannot be specified if services.cloudlog.database.createLocally is set to true.";
}
];
services.phpfpm = {
pools.cloudlog = {
inherit (cfg) user;
group = config.services.nginx.group;
settings = {
"listen.owner" = config.services.nginx.user;
"listen.group" = config.services.nginx.group;
} // cfg.poolConfig;
};
};
services.nginx = mkIf (cfg.virtualHost != null) {
enable = true;
virtualHosts = {
"${cfg.virtualHost}" = {
root = "${package}";
locations."/".tryFiles = "$uri /index.php$is_args$args";
locations."~ ^/index.php(/|$)".extraConfig = ''
include ${config.services.nginx.package}/conf/fastcgi_params;
include ${pkgs.nginx}/conf/fastcgi.conf;
fastcgi_split_path_info ^(.+\.php)(.+)$;
fastcgi_pass unix:${config.services.phpfpm.pools.cloudlog.socket};
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
'';
};
};
};
services.mysql = mkIf cfg.database.createLocally {
enable = true;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [{
name = cfg.database.user;
ensurePermissions = {
"${cfg.database.name}.*" = "ALL PRIVILEGES";
};
}];
};
systemd = {
services = {
cloudlog-setup-database = mkIf cfg.database.createLocally {
description = "Set up cloudlog database";
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
wantedBy = [ "phpfpm-cloudlog.service" ];
after = [ "mysql.service" ];
script = let
mysql = "${config.services.mysql.package}/bin/mysql";
in ''
if [ ! -f ${cfg.dataDir}/.dbexists ]; then
${mysql} ${cfg.database.name} < ${pkgs.cloudlog}/install/assets/install.sql
touch ${cfg.dataDir}/.dbexists
fi
'';
};
cloudlog-upload-lotw = {
description = "Upload QSOs to LoTW if certs have been provided";
enable = cfg.upload-lotw.enable;
script = "${pkgs.curl}/bin/curl -s ${cfg.baseUrl}/lotw/lotw_upload";
};
cloudlog-update-lotw-users = {
description = "Update LOTW Users Database";
enable = cfg.update-lotw-users.enable;
script = "${pkgs.curl}/bin/curl -s ${cfg.baseUrl}/lotw/load_users";
};
cloudlog-update-dok = {
description = "Update DOK File for autocomplete";
enable = cfg.update-dok.enable;
script = "${pkgs.curl}/bin/curl -s ${cfg.baseUrl}/update/update_dok";
};
cloudlog-update-clublog-scp = {
description = "Update Clublog SCP Database File";
enable = cfg.update-clublog-scp.enable;
script = "${pkgs.curl}/bin/curl -s ${cfg.baseUrl}/update/update_clublog_scp";
};
cloudlog-update-wwff = {
description = "Update WWFF File for autocomplete";
enable = cfg.update-wwff.enable;
script = "${pkgs.curl}/bin/curl -s ${cfg.baseUrl}/update/update_wwff";
};
cloudlog-upload-qrz = {
description = "Upload QSOs to QRZ Logbook";
enable = cfg.upload-qrz.enable;
script = "${pkgs.curl}/bin/curl -s ${cfg.baseUrl}/qrz/upload";
};
cloudlog-update-sota = {
description = "Update SOTA File for autocomplete";
enable = cfg.update-sota.enable;
script = "${pkgs.curl}/bin/curl -s ${cfg.baseUrl}/update/update_sota";
};
};
timers = {
cloudlog-upload-lotw = {
enable = cfg.upload-lotw.enable;
wantedBy = [ "timers.target" ];
partOf = [ "cloudlog-upload-lotw.service" ];
after = [ "phpfpm-cloudlog.service" ];
timerConfig = {
OnCalendar = cfg.upload-lotw.interval;
Persistent = true;
};
};
cloudlog-upload-clublog = {
enable = cfg.upload-clublog.enable;
wantedBy = [ "timers.target" ];
partOf = [ "cloudlog-upload-clublog.service" ];
after = [ "phpfpm-cloudlog.service" ];
timerConfig = {
OnCalendar = cfg.upload-clublog.interval;
Persistent = true;
};
};
cloudlog-update-lotw-users = {
enable = cfg.update-lotw-users.enable;
wantedBy = [ "timers.target" ];
partOf = [ "cloudlog-update-lotw-users.service" ];
after = [ "phpfpm-cloudlog.service" ];
timerConfig = {
OnCalendar = cfg.update-lotw-users.interval;
Persistent = true;
};
};
cloudlog-update-dok = {
enable = cfg.update-dok.enable;
wantedBy = [ "timers.target" ];
partOf = [ "cloudlog-update-dok.service" ];
after = [ "phpfpm-cloudlog.service" ];
timerConfig = {
OnCalendar = cfg.update-dok.interval;
Persistent = true;
};
};
cloudlog-update-clublog-scp = {
enable = cfg.update-clublog-scp.enable;
wantedBy = [ "timers.target" ];
partOf = [ "cloudlog-update-clublog-scp.service" ];
after = [ "phpfpm-cloudlog.service" ];
timerConfig = {
OnCalendar = cfg.update-clublog-scp.interval;
Persistent = true;
};
};
cloudlog-update-wwff = {
enable = cfg.update-wwff.enable;
wantedBy = [ "timers.target" ];
partOf = [ "cloudlog-update-wwff.service" ];
after = [ "phpfpm-cloudlog.service" ];
timerConfig = {
OnCalendar = cfg.update-wwff.interval;
Persistent = true;
};
};
cloudlog-upload-qrz = {
enable = cfg.upload-qrz.enable;
wantedBy = [ "timers.target" ];
partOf = [ "cloudlog-upload-qrz.service" ];
after = [ "phpfpm-cloudlog.service" ];
timerConfig = {
OnCalendar = cfg.upload-qrz.interval;
Persistent = true;
};
};
cloudlog-update-sota = {
enable = cfg.update-sota.enable;
wantedBy = [ "timers.target" ];
partOf = [ "cloudlog-update-sota.service" ];
after = [ "phpfpm-cloudlog.service" ];
timerConfig = {
OnCalendar = cfg.update-sota.interval;
Persistent = true;
};
};
};
tmpfiles.rules = let
group = config.services.nginx.group;
in [
"d ${cfg.dataDir} 0750 ${cfg.user} ${group} - -"
"d ${cfg.dataDir}/updates 0750 ${cfg.user} ${group} - -"
"d ${cfg.dataDir}/uploads 0750 ${cfg.user} ${group} - -"
"d ${cfg.dataDir}/backup 0750 ${cfg.user} ${group} - -"
"d ${cfg.dataDir}/logbook 0750 ${cfg.user} ${group} - -"
"d ${cfg.dataDir}/assets/json 0750 ${cfg.user} ${group} - -"
"d ${cfg.dataDir}/assets/qslcard 0750 ${cfg.user} ${group} - -"
];
};
users.users."${cfg.user}" = {
isSystemUser = true;
group = config.services.nginx.group;
};
};
meta.maintainers = with maintainers; [ melling ];
}

View File

@ -130,6 +130,7 @@ in {
clickhouse = handleTest ./clickhouse.nix {};
cloud-init = handleTest ./cloud-init.nix {};
cloud-init-hostname = handleTest ./cloud-init-hostname.nix {};
cloudlog = handleTest ./cloudlog.nix {};
cntr = handleTestOn ["aarch64-linux" "x86_64-linux"] ./cntr.nix {};
cockroachdb = handleTestOn ["x86_64-linux"] ./cockroachdb.nix {};
collectd = handleTest ./collectd.nix {};

18
nixos/tests/cloudlog.nix Normal file
View File

@ -0,0 +1,18 @@
import ./make-test-python.nix ({ pkgs, ... }: {
name = "cloudlog";
meta = {
maintainers = with pkgs.lib.maintainers; [ melling ];
};
nodes = {
machine = {
services.mysql.package = pkgs.mariadb;
services.cloudlog.enable = true;
};
};
testScript = ''
start_all()
machine.wait_for_unit("phpfpm-cloudlog")
machine.wait_for_open_port(80);
machine.wait_until_succeeds("curl -s -L --fail http://localhost | grep 'Login - Cloudlog'")
'';
})