Merge pull request #214063 from michaelshmitty/anuko-timetracker
This commit is contained in:
commit
946c3f8c51
8 changed files with 425 additions and 0 deletions
|
@ -10591,6 +10591,12 @@
|
|||
githubId = 1699466;
|
||||
name = "Michael Peyton Jones";
|
||||
};
|
||||
michaelshmitty = {
|
||||
name = "Michael Smith";
|
||||
email = "shmitty@protonmail.com";
|
||||
github = "michaelshmitty";
|
||||
githubId = 114845;
|
||||
};
|
||||
michalrus = {
|
||||
email = "m@michalrus.com";
|
||||
github = "michalrus";
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
- [GoToSocial](https://gotosocial.org/), an ActivityPub social network server, written in Golang. Available as [services.gotosocial](#opt-services.gotosocial.enable).
|
||||
|
||||
- [Anuko Time Tracker](https://github.com/anuko/timetracker), a simple, easy to use, open source time tracking system. Available as [services.anuko-time-tracker](#opt-services.anuko-time-tracker.enable).
|
||||
|
||||
- [sitespeed-io](https://sitespeed.io), a tool that can generate metrics (timings, diagnostics) for websites. Available as [services.sitespeed-io](#opt-services.sitespeed-io.enable).
|
||||
|
||||
- [Apache Guacamole](https://guacamole.apache.org/), a cross-platform, clientless remote desktop gateway. Available as [services.guacamole-server](#opt-services.guacamole-server.enable) and [services.guacamole-client](#opt-services.guacamole-client.enable) services.
|
||||
|
|
|
@ -1169,6 +1169,7 @@
|
|||
./services/wayland/cage.nix
|
||||
./services/web-apps/akkoma.nix
|
||||
./services/web-apps/alps.nix
|
||||
./services/web-apps/anuko-time-tracker.nix
|
||||
./services/web-apps/atlassian/confluence.nix
|
||||
./services/web-apps/atlassian/crowd.nix
|
||||
./services/web-apps/atlassian/jira.nix
|
||||
|
|
354
nixos/modules/services/web-apps/anuko-time-tracker.nix
Normal file
354
nixos/modules/services/web-apps/anuko-time-tracker.nix
Normal file
|
@ -0,0 +1,354 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.anuko-time-tracker;
|
||||
configFile = let
|
||||
smtpPassword = if cfg.settings.email.smtpPasswordFile == null
|
||||
then "''"
|
||||
else "trim(file_get_contents('${cfg.settings.email.smtpPasswordFile}'))";
|
||||
|
||||
in pkgs.writeText "config.php" ''
|
||||
<?php
|
||||
// Set include path for PEAR and its modules, which we include in the distribution.
|
||||
// Updated for the correct location in the nix store.
|
||||
set_include_path('${cfg.package}/WEB-INF/lib/pear' . PATH_SEPARATOR . get_include_path());
|
||||
define('DSN', 'mysqli://${cfg.database.user}@${cfg.database.host}/${cfg.database.name}?charset=utf8mb4');
|
||||
define('MULTIORG_MODE', ${lib.boolToString cfg.settings.multiorgMode});
|
||||
define('EMAIL_REQUIRED', ${lib.boolToString cfg.settings.emailRequired});
|
||||
define('WEEKEND_START_DAY', ${toString cfg.settings.weekendStartDay});
|
||||
define('FORUM_LINK', '${cfg.settings.forumLink}');
|
||||
define('HELP_LINK', '${cfg.settings.helpLink}');
|
||||
define('SENDER', '${cfg.settings.email.sender}');
|
||||
define('MAIL_MODE', '${cfg.settings.email.mode}');
|
||||
define('MAIL_SMTP_HOST', '${toString cfg.settings.email.smtpHost}');
|
||||
define('MAIL_SMTP_PORT', '${toString cfg.settings.email.smtpPort}');
|
||||
define('MAIL_SMTP_USER', '${cfg.settings.email.smtpUser}');
|
||||
define('MAIL_SMTP_PASSWORD', ${smtpPassword});
|
||||
define('MAIL_SMTP_AUTH', ${lib.boolToString cfg.settings.email.smtpAuth});
|
||||
define('MAIL_SMTP_DEBUG', ${lib.boolToString cfg.settings.email.smtpDebug});
|
||||
define('DEFAULT_CSS', 'default.css');
|
||||
define('RTL_CSS', 'rtl.css'); // For right to left languages.
|
||||
define('LANG_DEFAULT', '${cfg.settings.defaultLanguage}');
|
||||
define('CURRENCY_DEFAULT', '${cfg.settings.defaultCurrency}');
|
||||
define('EXPORT_DECIMAL_DURATION', ${lib.boolToString cfg.settings.exportDecimalDuration});
|
||||
define('REPORT_FOOTER', ${lib.boolToString cfg.settings.reportFooter});
|
||||
define('AUTH_MODULE', 'db');
|
||||
'';
|
||||
package = pkgs.stdenv.mkDerivation rec {
|
||||
pname = "anuko-time-tracker";
|
||||
inherit (src) version;
|
||||
src = cfg.package;
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
cp -r * $out/
|
||||
|
||||
ln -s ${configFile} $out/WEB-INF/config.php
|
||||
|
||||
# Link writable templates_c directory
|
||||
rm -rf $out/WEB-INF/templates_c
|
||||
ln -s ${cfg.dataDir}/templates_c $out/WEB-INF/templates_c
|
||||
|
||||
# ln -fs ${cfg.dataDir}/templates_c $out/WEB-INF/templates_c
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
options.services.anuko-time-tracker = {
|
||||
enable = lib.mkEnableOption (lib.mdDoc "Anuko Time Tracker");
|
||||
|
||||
package = lib.mkPackageOptionMD pkgs "anuko-time-tracker" {};
|
||||
|
||||
database = {
|
||||
createLocally = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = lib.mdDoc "Create the database and database user locally.";
|
||||
};
|
||||
|
||||
host = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc "Database host.";
|
||||
default = "localhost";
|
||||
};
|
||||
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc "Database name.";
|
||||
default = "anuko_time_tracker";
|
||||
};
|
||||
|
||||
user = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc "Database username.";
|
||||
default = "anuko_time_tracker";
|
||||
};
|
||||
|
||||
passwordFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
description = lib.mdDoc "Database user password file.";
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
|
||||
poolConfig = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.oneOf [ lib.types.str lib.types.int lib.types.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 = lib.mdDoc ''
|
||||
Options for Anuko Time Tracker's PHP-FPM pool.
|
||||
'';
|
||||
};
|
||||
|
||||
dataDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/var/lib/anuko-time-tracker";
|
||||
description = lib.mdDoc "Default data folder for Anuko Time Tracker.";
|
||||
example = "/mnt/anuko-time-tracker";
|
||||
};
|
||||
|
||||
user = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "anuko_time_tracker";
|
||||
description = lib.mdDoc "User under which Anuko Time Tracker runs.";
|
||||
};
|
||||
|
||||
virtualHost = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = "localhost";
|
||||
description = lib.mdDoc ''
|
||||
Name of the nginx virtualhost to use and setup. If null, do not setup
|
||||
any virtualhost.
|
||||
'';
|
||||
};
|
||||
|
||||
settings = {
|
||||
multiorgMode = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = lib.mdDoc ''
|
||||
Defines whether users see the Register option in the menu of Time Tracker that allows them
|
||||
to self-register and create new organizations (top groups).
|
||||
'';
|
||||
};
|
||||
|
||||
emailRequired = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc "Defines whether an email is required for new registrations.";
|
||||
};
|
||||
|
||||
weekendStartDay = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 6;
|
||||
description = lib.mdDoc ''
|
||||
This option defines which days are highlighted with weekend color.
|
||||
6 means Saturday. For Saudi Arabia, etc. set it to 4 for Thursday and Friday to be
|
||||
weekend days.
|
||||
'';
|
||||
};
|
||||
|
||||
forumLink = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc "Forum link from the main menu.";
|
||||
default = "https://www.anuko.com/forum/viewforum.php?f=4";
|
||||
};
|
||||
|
||||
helpLink = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc "Help link from the main menu.";
|
||||
default = "https://www.anuko.com/time-tracker/user-guide/index.htm";
|
||||
};
|
||||
|
||||
email = {
|
||||
sender = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc "Default sender for mail.";
|
||||
default = "Anuko Time Tracker <bounces@example.com>";
|
||||
};
|
||||
|
||||
mode = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc "Mail sending mode. Can be 'mail' or 'smtp'.";
|
||||
default = "smtp";
|
||||
};
|
||||
|
||||
smtpHost = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc "MTA hostname.";
|
||||
default = "localhost";
|
||||
};
|
||||
|
||||
smtpPort = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
description = lib.mdDoc "MTA port.";
|
||||
default = 25;
|
||||
};
|
||||
|
||||
smtpUser = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc "MTA authentication username.";
|
||||
default = "";
|
||||
};
|
||||
|
||||
smtpAuth = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc "MTA requires authentication.";
|
||||
};
|
||||
|
||||
smtpPasswordFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
default = null;
|
||||
example = "/var/lib/anuko-time-tracker/secrets/smtp-password";
|
||||
description = lib.mdDoc ''
|
||||
Path to file containing the MTA authentication password.
|
||||
'';
|
||||
};
|
||||
|
||||
smtpDebug = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc "Debug mail sending.";
|
||||
};
|
||||
};
|
||||
|
||||
defaultLanguage = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc ''
|
||||
Defines Anuko Time Tracker default language. It is used on Time Tracker login page.
|
||||
After login, a language set for user group is used.
|
||||
Empty string means the language is defined by user browser.
|
||||
'';
|
||||
default = "";
|
||||
example = "nl";
|
||||
};
|
||||
|
||||
defaultCurrency = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc ''
|
||||
Defines a default currency symbol for new groups.
|
||||
Use €, £, a more specific dollar like US$, CAD, etc.
|
||||
'';
|
||||
default = "$";
|
||||
example = "€";
|
||||
};
|
||||
|
||||
exportDecimalDuration = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = lib.mdDoc ''
|
||||
Defines whether time duration values are decimal in CSV and XML data
|
||||
exports (1.25 vs 1:15).
|
||||
'';
|
||||
};
|
||||
|
||||
reportFooter = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = lib.mdDoc "Defines whether to use a footer on reports.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
|
||||
message = ''
|
||||
<option>services.anuko-time-tracker.database.passwordFile</option> cannot be specified if
|
||||
<option>services.anuko-time-tracker.database.createLocally</option> is set to true.
|
||||
'';
|
||||
}
|
||||
{
|
||||
assertion = cfg.settings.email.smtpAuth -> (cfg.settings.email.smtpPasswordFile != null);
|
||||
message = ''
|
||||
<option>services.anuko-time-tracker.settings.email.smtpPasswordFile</option> needs to be set if
|
||||
<option>services.anuko-time-tracker.settings.email.smtpAuth</option> is enabled.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
services.phpfpm = {
|
||||
pools.anuko-time-tracker = {
|
||||
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 = lib.mkIf (cfg.virtualHost != null) {
|
||||
enable = true;
|
||||
virtualHosts = {
|
||||
"${cfg.virtualHost}" = {
|
||||
root = lib.mkForce "${package}";
|
||||
locations."/".index = "index.php";
|
||||
locations."~ [^/]\\.php(/|$)" = {
|
||||
extraConfig = ''
|
||||
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
|
||||
fastcgi_pass unix:${config.services.phpfpm.pools.anuko-time-tracker.socket};
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.mysql = lib.mkIf cfg.database.createLocally {
|
||||
enable = lib.mkDefault true;
|
||||
package = lib.mkDefault pkgs.mariadb;
|
||||
ensureDatabases = [ cfg.database.name ];
|
||||
ensureUsers = [{
|
||||
name = cfg.database.user;
|
||||
ensurePermissions = {
|
||||
"${cfg.database.name}.*" = "ALL PRIVILEGES";
|
||||
};
|
||||
}];
|
||||
};
|
||||
|
||||
systemd = {
|
||||
services = {
|
||||
anuko-time-tracker-setup-database = lib.mkIf cfg.database.createLocally {
|
||||
description = "Set up Anuko Time Tracker database";
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
wantedBy = [ "phpfpm-anuko-time-tracker.service" ];
|
||||
after = [ "mysql.service" ];
|
||||
script =
|
||||
let
|
||||
mysql = "${config.services.mysql.package}/bin/mysql";
|
||||
in
|
||||
''
|
||||
if [ ! -f ${cfg.dataDir}/.dbexists ]; then
|
||||
# Load database schema provided with package
|
||||
${mysql} ${cfg.database.name} < ${cfg.package}/mysql.sql
|
||||
|
||||
touch ${cfg.dataDir}/.dbexists
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
tmpfiles.rules = [
|
||||
"d ${cfg.dataDir} 0750 ${cfg.user} ${config.services.nginx.group} -"
|
||||
"d ${cfg.dataDir}/templates_c 0750 ${cfg.user} ${config.services.nginx.group} -"
|
||||
];
|
||||
};
|
||||
|
||||
users.users."${cfg.user}" = {
|
||||
isSystemUser = true;
|
||||
group = config.services.nginx.group;
|
||||
};
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ michaelshmitty ];
|
||||
}
|
|
@ -107,6 +107,7 @@ in {
|
|||
allTerminfo = handleTest ./all-terminfo.nix {};
|
||||
alps = handleTest ./alps.nix {};
|
||||
amazon-init-shell = handleTest ./amazon-init-shell.nix {};
|
||||
anuko-time-tracker = handleTest ./anuko-time-tracker.nix {};
|
||||
apcupsd = handleTest ./apcupsd.nix {};
|
||||
apfs = runTest ./apfs.nix;
|
||||
apparmor = handleTest ./apparmor.nix {};
|
||||
|
|
17
nixos/tests/anuko-time-tracker.nix
Normal file
17
nixos/tests/anuko-time-tracker.nix
Normal file
|
@ -0,0 +1,17 @@
|
|||
import ./make-test-python.nix ({ pkgs, ... }: {
|
||||
name = "anuko-time-tracker";
|
||||
meta = {
|
||||
maintainers = with pkgs.lib.maintainers; [ michaelshmitty ];
|
||||
};
|
||||
nodes = {
|
||||
machine = {
|
||||
services.anuko-time-tracker.enable = true;
|
||||
};
|
||||
};
|
||||
testScript = ''
|
||||
start_all()
|
||||
machine.wait_for_unit("phpfpm-anuko-time-tracker")
|
||||
machine.wait_for_open_port(80);
|
||||
machine.wait_until_succeeds("curl -s --fail -L http://localhost/time.php | grep 'Anuko Time Tracker'")
|
||||
'';
|
||||
})
|
42
pkgs/servers/web-apps/anuko-time-tracker/default.nix
Normal file
42
pkgs/servers/web-apps/anuko-time-tracker/default.nix
Normal file
|
@ -0,0 +1,42 @@
|
|||
{ lib
|
||||
, stdenvNoCC
|
||||
, fetchFromGitHub
|
||||
, nixosTests
|
||||
, php
|
||||
}:
|
||||
|
||||
stdenvNoCC.mkDerivation rec {
|
||||
pname = "anuko-time-tracker";
|
||||
version = "1.22.19.5806";
|
||||
|
||||
# Project commits directly into master and has no release schedule.
|
||||
# Fortunately the current version is defined in the 'APP_VERSION' constant in
|
||||
# /initialize.php so we use its value and set the rev to the commit sha of when the
|
||||
# constant was last updated.
|
||||
src = fetchFromGitHub {
|
||||
owner = "anuko";
|
||||
repo = "timetracker";
|
||||
rev = "43a19fcb51a21f6e3169084ac81308a6ef751e63";
|
||||
hash = "sha256-ZRF9FFbntYY01JflCXkYZyXfyu/x7LNdyOB94UkVFR0=";
|
||||
};
|
||||
|
||||
# There's nothing to build.
|
||||
dontBuild = true;
|
||||
|
||||
installPhase = ''
|
||||
mkdir $out/
|
||||
cp -R ./* $out
|
||||
'';
|
||||
|
||||
passthru.tests = {
|
||||
inherit (nixosTests) anuko-time-tracker;
|
||||
};
|
||||
|
||||
meta = {
|
||||
description = "Simple, easy to use, open source time tracking system";
|
||||
license = lib.licenses.sspl;
|
||||
homepage = "https://github.com/anuko/timetracker/";
|
||||
platforms = php.meta.platforms;
|
||||
maintainers = with lib.maintainers; [ michaelshmitty ];
|
||||
};
|
||||
}
|
|
@ -25458,6 +25458,8 @@ with pkgs;
|
|||
|
||||
alps = callPackage ../servers/alps { };
|
||||
|
||||
anuko-time-tracker = callPackage ../servers/web-apps/anuko-time-tracker { };
|
||||
|
||||
apache-directory-server = callPackage ../servers/ldap/apache-directory-server { };
|
||||
|
||||
apacheHttpd_2_4 = callPackage ../servers/http/apache-httpd/2.4.nix {
|
||||
|
|
Loading…
Reference in a new issue