Files
sp-config/sp-modules/vikunja/module.nix
nhnn b605d07b52 feat: Vikunja to-do app (#128)
Vikunja is fast self-hostable to-do app.

Reviewed-on: https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-config/pulls/128
Reviewed-by: Inex Code <inex.code@selfprivacy.org>
Co-authored-by: nhnn <nhnn@nhnn.dev>
Co-committed-by: nhnn <nhnn@nhnn.dev>
2025-04-14 14:32:42 +03:00

187 lines
5.1 KiB
Nix

latestPkgs: {
config,
lib,
...
}: let
sp = config.selfprivacy;
cfg = sp.modules.vikunja;
oauthClientID = "vikunja";
auth-passthru = config.selfprivacy.passthru.auth;
oauth2-provider-name = auth-passthru.oauth2-provider-name;
oauthDiscoveryURL = auth-passthru.oauth2-discovery-url oauthClientID;
# SelfPrivacy uses SP Module ID to identify the group!
usersGroup = "sp.vikunja.users";
oauthClientSecretFP =
auth-passthru.mkOAuth2ClientSecretFP oauthClientID;
vikunjaPackage = latestPkgs.vikunja.overrideAttrs (old: {
doCheck = false; # Tests are slow.
patches =
(old.patches or [])
++ [
./load-client-secret-from-env.patch
];
});
in {
options.selfprivacy.modules.vikunja = {
enable =
(lib.mkOption {
default = false;
type = lib.types.bool;
description = "Enable Vikunja";
})
// {
meta = {
type = "enable";
};
};
location =
(lib.mkOption {
type = lib.types.str;
description = "Vikunja location";
})
// {
meta = {
type = "location";
};
};
subdomain =
(lib.mkOption {
default = "vikunja";
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
description = "Subdomain";
})
// {
meta = {
widget = "subdomain";
type = "string";
regex = "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
weight = 0;
};
};
};
config =
lib.mkIf cfg.enable
{
assertions = [
{
assertion = sp.sso.enable;
message = "Vikunja cannot be enabled when SSO is disabled.";
}
];
fileSystems = lib.mkIf sp.useBinds {
"/var/lib/vikunja" = {
device = "/volumes/${cfg.location}/vikunja";
options = ["bind"];
};
};
users = {
users.vikunja = {
isSystemUser = true;
group = "vikunja";
};
groups.vikunja = {};
};
services.postgresql = {
ensureDatabases = ["vikunja"];
ensureUsers = [
{
name = "vikunja";
ensureDBOwnership = true;
}
];
};
services.vikunja = {
enable = true;
package = vikunjaPackage;
frontendScheme = "https";
frontendHostname = "${cfg.subdomain}.${sp.domain}";
port = 4835;
database = {
type = "postgres";
host = "/run/postgresql";
};
settings = {
service = {
enableregistration = false;
enabletotp = false;
enableuserdeletion = true;
};
auth = {
local.enabled = false;
openid = {
enabled = true;
providers = [
{
name = oauth2-provider-name;
authurl = lib.strings.removeSuffix "/.well-known/openid-configuration" oauthDiscoveryURL;
clientid = oauthClientID;
clientsecret = ""; # There's patch for our Vikunja to make it load client secret from environment variable.
scope = "openid profile email";
}
];
};
};
};
};
services.nginx.virtualHosts."${cfg.subdomain}.${sp.domain}" = {
useACMEHost = sp.domain;
forceSSL = true;
extraConfig = ''
add_header Strict-Transport-Security $hsts_header;
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
add_header 'Referrer-Policy' 'origin-when-cross-origin';
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
'';
locations = {
"/" = {
proxyPass = "http://127.0.0.1:4835";
};
};
};
systemd = {
services.vikunja = {
unitConfig.RequiresMountsFor = lib.mkIf sp.useBinds "/volumes/${cfg.location}/vikunja";
serviceConfig = {
Slice = "vikunja.slice";
LoadCredential = "oauth2-secret:${oauthClientSecretFP}";
DynamicUser = lib.mkForce false;
User = "vikunja";
Group = "vikunja";
};
environment.SP_VIKUNJA_CLIENT_SECRET_PATH = "%d/oauth2-secret";
};
slices.vikunja = {
description = "Vikunja service slice";
};
};
selfprivacy.auth.clients.${oauthClientID} = {
inherit usersGroup;
subdomain = cfg.subdomain;
isTokenNeeded = true;
originLanding = "https://${cfg.subdomain}.${sp.domain}/";
originUrl = "https://${cfg.subdomain}.${sp.domain}/auth/openid/${lib.strings.toLower oauth2-provider-name}";
clientSystemdUnits = ["vikunja.service"];
enablePkce = false;
linuxUserOfClient = "vikunja";
linuxGroupOfClient = "vikunja";
};
};
}