1{
2 config,
3 pkgs,
4 lib,
5 ...
6}:
7
8let
9 cfg = config.services.olivetin;
10
11 settingsFormat = pkgs.formats.yaml { };
12in
13
14{
15 meta.maintainers = with lib.maintainers; [ defelo ];
16
17 options.services.olivetin = {
18 enable = lib.mkEnableOption "OliveTin";
19
20 package = lib.mkPackageOption pkgs "olivetin" { };
21
22 user = lib.mkOption {
23 type = lib.types.str;
24 description = "The user account under which OliveTin runs.";
25 default = "olivetin";
26 };
27
28 group = lib.mkOption {
29 type = lib.types.str;
30 description = "The group under which OliveTin runs.";
31 default = "olivetin";
32 };
33
34 path = lib.mkOption {
35 type =
36 with lib.types;
37 listOf (oneOf [
38 package
39 str
40 ]);
41 description = ''
42 Packages added to the service's {env}`PATH`.
43 '';
44 defaultText = lib.literalExpression ''
45 with pkgs; [ bash ]
46 '';
47 };
48
49 settings = lib.mkOption {
50 description = ''
51 Configuration of OliveTin. See <https://docs.olivetin.app/config.html> for more information.
52 '';
53 default = { };
54
55 type = lib.types.submodule {
56 freeformType = settingsFormat.type;
57
58 options = {
59 ListenAddressSingleHTTPFrontend = lib.mkOption {
60 type = lib.types.str;
61 description = ''
62 The address to listen on for the internal "microproxy" frontend.
63 '';
64 default = "127.0.0.1:8000";
65 example = "0.0.0.0:8000";
66 };
67 };
68 };
69 };
70
71 extraConfigFiles = lib.mkOption {
72 type = lib.types.listOf lib.types.path;
73 default = [ ];
74 example = [ "/run/secrets/olivetin.yaml" ];
75 description = ''
76 Config files to merge into the settings defined in [](#opt-services.olivetin.settings).
77 This is useful to avoid putting secrets into the nix store.
78 See <https://docs.olivetin.app/config.html> for more information.
79 '';
80 };
81 };
82
83 config = lib.mkIf cfg.enable {
84 services.olivetin = {
85 path = with pkgs; [ bash ];
86 };
87
88 systemd.services.olivetin = {
89 description = "OliveTin";
90
91 wantedBy = [ "multi-user.target" ];
92
93 wants = [
94 "network-online.target"
95 "local-fs.target"
96 ];
97 after = [
98 "network-online.target"
99 "local-fs.target"
100 ];
101
102 inherit (cfg) path;
103
104 preStart = ''
105 tmp="$(mktemp -d)"
106 trap 'rm -rf "$tmp"' EXIT
107 cd "$tmp"
108
109 cp ${settingsFormat.generate "olivetin-config.yaml" cfg.settings} config.yaml
110 chmod +w config.yaml
111 for ((i=0; i < ${toString (lib.length cfg.extraConfigFiles)}; i++)); do
112 ${lib.getExe pkgs.yq} -yi '
113 def merge($y):
114 . as $x |
115 if ($x | type == "object") and ($y | type == "object") then
116 $x + $y + with_entries(select(.key | in($y)) | .key as $key | .value |= merge($y[$key]))
117 elif ($x | type == "array") and ($y | type == "array") then
118 $x + $y
119 else
120 $y
121 end;
122 merge($f | fromjson)
123 ' config.yaml --rawfile f <(${lib.getExe pkgs.yq} -c . "$CREDENTIALS_DIRECTORY/config-$i.yaml")
124 done
125 chmod -w config.yaml
126
127 mkdir -p /run/olivetin/config
128 mv config.yaml /run/olivetin/config/config.yaml
129 '';
130
131 serviceConfig = {
132 User = cfg.user;
133 Group = cfg.group;
134 RuntimeDirectory = "olivetin";
135 Restart = "always";
136
137 LoadCredential = lib.imap0 (i: path: "config-${toString i}.yaml:${path}") cfg.extraConfigFiles;
138
139 ExecStart = "${lib.getExe cfg.package} -configdir /run/olivetin/config";
140 };
141 };
142
143 users.users = lib.mkIf (cfg.user == "olivetin") {
144 olivetin = {
145 group = cfg.group;
146 isSystemUser = true;
147 };
148 };
149
150 users.groups = lib.mkIf (cfg.group == "olivetin") { olivetin = { }; };
151 };
152}