296 lines
8.3 KiB
Nix
296 lines
8.3 KiB
Nix
{ config, pkgs, lib, ... }:
|
|
|
|
# TODO: when DEBUG = False, serving static/media does not work, not sure why
|
|
with lib;
|
|
let
|
|
cfg = config.services.wger;
|
|
|
|
defaultUser = "wger";
|
|
|
|
wgerpkgs = pkgs.callPackage ./default.nix {};
|
|
|
|
# generate settings files
|
|
settingsFormat = pkgs.formats.json {};
|
|
|
|
wger_settings_file = pkgs.writeText "settings.json" (builtins.toJSON cfg.wgerSettings);
|
|
django_settings_file = pkgs.writeText "settings.json" (builtins.toJSON cfg.djangoSettings);
|
|
settingsFile = pkgs.writeText "settings.py" ''
|
|
from wger.settings_global import *
|
|
import json
|
|
import os
|
|
|
|
with open("${django_settings_file}") as f:
|
|
for k, v in json.load(f).items():
|
|
if isinstance(v, str) and v.startswith("$"):
|
|
v = os.environ[v[1:]]
|
|
|
|
globals()[k] = v
|
|
|
|
with open("${wger_settings_file}") as f:
|
|
for k, v in json.load(f).items():
|
|
if isinstance(v, str) and v.startswith("$"):
|
|
v = os.environ[v[1:]]
|
|
|
|
WGER_SETTINGS[k] = v
|
|
'';
|
|
settingsFileDir = pkgs.writeTextDir "settings.py" (builtins.readFile settingsFile);
|
|
in
|
|
{
|
|
meta.maintainers = with maintainers; [ eyjhb ];
|
|
|
|
options.services.wger = {
|
|
enable = mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = ''
|
|
Enable Wger.
|
|
'';
|
|
};
|
|
|
|
dataDir = mkOption {
|
|
type = types.str;
|
|
default = "/var/lib/wger";
|
|
description = "Directory to store the Wger data.";
|
|
};
|
|
|
|
mediaDir = mkOption {
|
|
type = types.str;
|
|
default = "${cfg.dataDir}/media";
|
|
defaultText = literalExpression ''"''${dataDir}/media"'';
|
|
description = "Directory to store the Wger media.";
|
|
};
|
|
|
|
environmentFile = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.path;
|
|
default = null;
|
|
example = "/var/lib/teeworlds/teeworlds.env";
|
|
description = ''
|
|
Environment file as defined in {manpage}`systemd.exec(5)`.
|
|
|
|
Secrets may be passed to the service without adding them to the world-readable
|
|
Nix store, by specifying placeholder variables as the option value in Nix and
|
|
setting these variables accordingly in the environment file.
|
|
|
|
```
|
|
# snippet of teeworlds-related config
|
|
services.teeworlds.settings.SECRET_KEY = "$SECRETS_KEY";
|
|
```
|
|
|
|
```
|
|
# content of the environment file
|
|
SECRETS_KEY=verysecretpassword
|
|
```
|
|
|
|
Note that this file needs to be available on the host on which
|
|
`wger` is running.
|
|
'';
|
|
};
|
|
|
|
address = mkOption {
|
|
type = types.str;
|
|
default = "localhost";
|
|
description = "Web interface address.";
|
|
};
|
|
|
|
port = mkOption {
|
|
type = types.port;
|
|
default = 28391;
|
|
description = "Web interface port.";
|
|
};
|
|
|
|
djangoSettings = mkOption {
|
|
type = lib.types.submodule {
|
|
freeformType = settingsFormat.type;
|
|
};
|
|
|
|
default = { };
|
|
};
|
|
|
|
wgerSettings = mkOption {
|
|
type = lib.types.submodule {
|
|
freeformType = settingsFormat.type;
|
|
};
|
|
|
|
default = { };
|
|
};
|
|
|
|
user = mkOption {
|
|
type = types.str;
|
|
default = defaultUser;
|
|
# TODO: fix this, it is because of the database thing
|
|
readOnly = true;
|
|
description = "User under which Wger runs.";
|
|
};
|
|
|
|
configureRedis = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
};
|
|
|
|
configurePostgres = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
};
|
|
|
|
package = mkPackageOption { wger = wgerpkgs; } "wger" { };
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
services.wger.wgerSettings = {
|
|
EMAIL_FROM = mkDefault "wger Workout Manager <wger@example.com>";
|
|
ALLOW_REGISTRATION = mkDefault true;
|
|
ALLOW_GUEST_USERS = mkDefault true;
|
|
ALLOW_UPLOAD_VIDEOS = mkDefault false;
|
|
MIN_ACCOUNT_AGE_TO_TRUST = mkDefault 1;
|
|
EXERCISE_CACHE_TTL = mkDefault 3600; # 1 hour
|
|
};
|
|
|
|
services.wger.djangoSettings = rec {
|
|
DEBUG = mkDefault false;
|
|
|
|
# configure database as postgresql or sqlite
|
|
DATABASES.default = if cfg.configurePostgres then {
|
|
ENGINE = "django.db.backends.postgresql";
|
|
NAME = "wger";
|
|
USER = "wger";
|
|
PASSWORD = "";
|
|
HOST = "/run/postgresql";
|
|
PORT = "";
|
|
} else {
|
|
ENGINE = "django.db.backends.sqlite3";
|
|
NAME = "${cfg.dataDir}/database.db";
|
|
USER = "";
|
|
PASSWORD = "";
|
|
HOST = "";
|
|
PORT = "";
|
|
};
|
|
|
|
SECRET_KEY = "$SECRET_KEY";
|
|
|
|
MEDIA_ROOT = cfg.mediaDir;
|
|
MEDIA_URL = "/media/";
|
|
|
|
# EMAIL
|
|
EMAIL_BACKEND = mkDefault "django.core.mail.backends.console.EmailBackend";
|
|
|
|
# Cache - Redis
|
|
CACHES.default = mkIf cfg.configureRedis {
|
|
BACKEND = "django_redis.cache.RedisCache";
|
|
LOCATION = "unix://${config.services.redis.servers.wger.unixSocket}";
|
|
TIMEOUT = 15 * 24 * 60 * 60; # 15 days
|
|
OPTIONS.CLIENT_CLASS = "django_redis.client.DefaultClient";
|
|
};
|
|
|
|
# setup allowed hosts
|
|
# CSRF_TRUSTED_ORIGINS = [ "https://${svc_domain}" ];
|
|
# ALLOWED_HOSTS = [ svc_domain ];
|
|
|
|
# disable recaptcha
|
|
RECAPTCHA_PUBLIC_KEY = "";
|
|
RECAPTCHA_PRIVATE_KEY = "";
|
|
USE_RECAPTCHA = false;
|
|
|
|
# does not work
|
|
STATIC_ROOT = "${cfg.package}/share/static";
|
|
COMPRESS_ROOT = STATIC_ROOT;
|
|
COMPRESS_OFFLINE = true;
|
|
};
|
|
|
|
# main service
|
|
systemd.services.wger = {
|
|
description = "wger fitness";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "networking.target" ];
|
|
|
|
script = let
|
|
pythonEnv = pkgs.python3.withPackages (ps: with ps; [
|
|
gunicorn
|
|
# TODO: fix this, it should work with cfg.package
|
|
(pkgs.python3Packages.callPackage ./default.nix {})
|
|
]);
|
|
in ''
|
|
# initial setup
|
|
${cfg.package}/bin/wger migrate-db -s ${settingsFile} || true
|
|
# TODO: fix at some point
|
|
# ${cfg.package}/bin/wger load-fixtures -s ${settingsFile} || true
|
|
|
|
# run server
|
|
# ${cfg.package}/bin/wger start -s ${settingsFile}
|
|
PYTHONPATH="${pythonEnv}/${pkgs.python3.sitePackages}:${settingsFileDir}" ${pythonEnv}/bin/gunicorn wger.wsgi:application --reload --bind ${cfg.address}:${builtins.toString cfg.port}
|
|
'';
|
|
|
|
serviceConfig = {
|
|
EnvironmentFile = config.age.secrets.wger-env.path;
|
|
|
|
Restart = "on-failure";
|
|
RestartSec = "5s";
|
|
|
|
PrivateTmp = "yes";
|
|
|
|
User = cfg.user;
|
|
# TODO: fix this, maybe
|
|
Group = cfg.user;
|
|
};
|
|
};
|
|
|
|
# periodic keep up-to-date
|
|
systemd.timers."wger-housekeeping" = {
|
|
wantedBy = [ "timers.target" ];
|
|
timerConfig.OnCalendar = "daily";
|
|
};
|
|
|
|
systemd.services."wger-housekeeping" = {
|
|
after = [ "wger.service" ];
|
|
requires = [ "wger.service" ];
|
|
script = ''
|
|
WGER_SETTINGS=${settingsFile} ${cfg.package}/bin/manage sync-exercises || true
|
|
WGER_SETTINGS=${settingsFile} ${cfg.package}/bin/manage download-exercise-images || true
|
|
WGER_SETTINGS=${settingsFile} ${cfg.package}/bin/manage download-exercise-videos || true
|
|
# WGER_SETTINGS=${settingsFile} ${cfg.package}/bin/manage sync-ingredients || true
|
|
${cfg.package}/bin/wger load-online-fixtures -s ${settingsFile} || true
|
|
WGER_SETTINGS=${settingsFile} ${cfg.package}/bin/manage exercises-health-check || true
|
|
'';
|
|
|
|
serviceConfig = {
|
|
EnvironmentFile = config.age.secrets.wger-env.path;
|
|
|
|
# Type = "oneshot";
|
|
User = cfg.user;
|
|
# TODO: fix this, maybe
|
|
Group = cfg.user;
|
|
};
|
|
};
|
|
|
|
# postgresql
|
|
services.postgresql = lib.mkIf cfg.configurePostgres {
|
|
ensureDatabases = [ cfg.user ];
|
|
ensureUsers = [{
|
|
name = cfg.user;
|
|
ensureDBOwnership = true;
|
|
}];
|
|
};
|
|
|
|
# redis
|
|
services.redis.servers.wger = lib.mkIf cfg.configureRedis {
|
|
enable = true;
|
|
user = cfg.user;
|
|
};
|
|
|
|
# setup user
|
|
users = optionalAttrs (cfg.user == defaultUser) {
|
|
users.${defaultUser} = {
|
|
group = defaultUser;
|
|
# TODO: fix this
|
|
# uid = config.ids.uids.paperless + 2;
|
|
uid = 738;
|
|
home = cfg.dataDir;
|
|
};
|
|
|
|
groups.${defaultUser} = {
|
|
# TODO: fix this
|
|
# gid = config.ids.gids.paperless + 2;
|
|
gid = 738;
|
|
};
|
|
};
|
|
};
|
|
}
|