1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8let
9 cfg = config.services.kavita;
10 settingsFormat = pkgs.formats.json { };
11 appsettings = settingsFormat.generate "appsettings.json" (
12 { TokenKey = "@TOKEN@"; } // cfg.settings
13 );
14in
15{
16 imports = [
17 (lib.mkChangedOptionModule
18 [ "services" "kavita" "ipAdresses" ]
19 [ "services" "kavita" "settings" "IpAddresses" ]
20 (
21 config:
22 let
23 value = lib.getAttrFromPath [ "services" "kavita" "ipAdresses" ] config;
24 in
25 lib.concatStringsSep "," value
26 )
27 )
28 (lib.mkRenamedOptionModule [ "services" "kavita" "port" ] [ "services" "kavita" "settings" "Port" ])
29 ];
30
31 options.services.kavita = {
32 enable = lib.mkEnableOption "Kavita reading server";
33
34 user = lib.mkOption {
35 type = lib.types.str;
36 default = "kavita";
37 description = "User account under which Kavita runs.";
38 };
39
40 package = lib.mkPackageOption pkgs "kavita" { };
41
42 dataDir = lib.mkOption {
43 default = "/var/lib/kavita";
44 type = lib.types.str;
45 description = "The directory where Kavita stores its state.";
46 };
47
48 tokenKeyFile = lib.mkOption {
49 type = lib.types.path;
50 description = ''
51 A file containing the TokenKey, a secret with at 512+ bits.
52 It can be generated with `head -c 64 /dev/urandom | base64 --wrap=0`.
53 '';
54 };
55
56 settings = lib.mkOption {
57 default = { };
58 description = ''
59 Kavita configuration options, as configured in {file}`appsettings.json`.
60 '';
61 type = lib.types.submodule {
62 freeformType = settingsFormat.type;
63
64 options = {
65 Port = lib.mkOption {
66 default = 5000;
67 type = lib.types.port;
68 description = "Port to bind to.";
69 };
70
71 IpAddresses = lib.mkOption {
72 default = "0.0.0.0,::";
73 type = lib.types.commas;
74 description = ''
75 IP Addresses to bind to. The default is to bind to all IPv4 and IPv6 addresses.
76 '';
77 };
78 };
79 };
80 };
81 };
82
83 config = lib.mkIf cfg.enable {
84 systemd.services.kavita = {
85 description = "Kavita";
86 wantedBy = [ "multi-user.target" ];
87 after = [ "network.target" ];
88 preStart = ''
89 install -m600 ${appsettings} ${lib.escapeShellArg cfg.dataDir}/config/appsettings.json
90 ${pkgs.replace-secret}/bin/replace-secret '@TOKEN@' \
91 ''${CREDENTIALS_DIRECTORY}/token \
92 '${cfg.dataDir}/config/appsettings.json'
93 '';
94 serviceConfig = {
95 WorkingDirectory = cfg.dataDir;
96 LoadCredential = [ "token:${cfg.tokenKeyFile}" ];
97 ExecStart = lib.getExe cfg.package;
98 Restart = "always";
99 User = cfg.user;
100 };
101 };
102
103 systemd.tmpfiles.rules = [
104 "d '${cfg.dataDir}' 0750 ${cfg.user} ${cfg.user} - -"
105 "d '${cfg.dataDir}/config' 0750 ${cfg.user} ${cfg.user} - -"
106 ];
107
108 users = {
109 users.${cfg.user} = {
110 description = "kavita service user";
111 isSystemUser = true;
112 group = cfg.user;
113 home = cfg.dataDir;
114 };
115 groups.${cfg.user} = { };
116 };
117 };
118
119 meta.maintainers = with lib.maintainers; [ misterio77 ];
120}