python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / services / misc / moonraker.nix
blob62064b5d90fb3a13fdde1f5e4573c6b58748c90b
1 { config, lib, options, pkgs, ... }:
2 with lib;
3 let
4   pkg = pkgs.moonraker;
5   cfg = config.services.moonraker;
6   opt = options.services.moonraker;
7   format = pkgs.formats.ini {
8     # https://github.com/NixOS/nixpkgs/pull/121613#issuecomment-885241996
9     listToValue = l:
10       if builtins.length l == 1 then generators.mkValueStringDefault {} (head l)
11       else lib.concatMapStrings (s: "\n  ${generators.mkValueStringDefault {} s}") l;
12     mkKeyValue = generators.mkKeyValueDefault {} ":";
13   };
14 in {
15   options = {
16     services.moonraker = {
17       enable = mkEnableOption (lib.mdDoc "Moonraker, an API web server for Klipper");
19       klipperSocket = mkOption {
20         type = types.path;
21         default = config.services.klipper.apiSocket;
22         defaultText = literalExpression "config.services.klipper.apiSocket";
23         description = lib.mdDoc "Path to Klipper's API socket.";
24       };
26       stateDir = mkOption {
27         type = types.path;
28         default = "/var/lib/moonraker";
29         description = lib.mdDoc "The directory containing the Moonraker databases.";
30       };
32       configDir = mkOption {
33         type = types.path;
34         default = cfg.stateDir + "/config";
35         defaultText = literalExpression ''config.${opt.stateDir} + "/config"'';
36         description = lib.mdDoc ''
37           The directory containing client-writable configuration files.
39           Clients will be able to edit files in this directory via the API. This directory must be writable.
40         '';
41       };
43       user = mkOption {
44         type = types.str;
45         default = "moonraker";
46         description = lib.mdDoc "User account under which Moonraker runs.";
47       };
49       group = mkOption {
50         type = types.str;
51         default = "moonraker";
52         description = lib.mdDoc "Group account under which Moonraker runs.";
53       };
55       address = mkOption {
56         type = types.str;
57         default = "127.0.0.1";
58         example = "0.0.0.0";
59         description = lib.mdDoc "The IP or host to listen on.";
60       };
62       port = mkOption {
63         type = types.ints.unsigned;
64         default = 7125;
65         description = lib.mdDoc "The port to listen on.";
66       };
68       settings = mkOption {
69         type = format.type;
70         default = { };
71         example = {
72           authorization = {
73             trusted_clients = [ "10.0.0.0/24" ];
74             cors_domains = [ "https://app.fluidd.xyz" ];
75           };
76         };
77         description = lib.mdDoc ''
78           Configuration for Moonraker. See the [documentation](https://moonraker.readthedocs.io/en/latest/configuration/)
79           for supported values.
80         '';
81       };
83       allowSystemControl = mkOption {
84         type = types.bool;
85         default = false;
86         description = lib.mdDoc ''
87           Whether to allow Moonraker to perform system-level operations.
89           Moonraker exposes APIs to perform system-level operations, such as
90           reboot, shutdown, and management of systemd units. See the
91           [documentation](https://moonraker.readthedocs.io/en/latest/web_api/#machine-commands)
92           for details on what clients are able to do.
93         '';
94       };
95     };
96   };
98   config = mkIf cfg.enable {
99     warnings = optional (cfg.settings ? update_manager)
100       ''Enabling update_manager is not supported on NixOS and will lead to non-removable warnings in some clients.'';
102     assertions = [
103       {
104         assertion = cfg.allowSystemControl -> config.security.polkit.enable;
105         message = "services.moonraker.allowSystemControl requires polkit to be enabled (security.polkit.enable).";
106       }
107     ];
109     users.users = optionalAttrs (cfg.user == "moonraker") {
110       moonraker = {
111         group = cfg.group;
112         uid = config.ids.uids.moonraker;
113       };
114     };
116     users.groups = optionalAttrs (cfg.group == "moonraker") {
117       moonraker.gid = config.ids.gids.moonraker;
118     };
120     environment.etc."moonraker.cfg".source = let
121       forcedConfig = {
122         server = {
123           host = cfg.address;
124           port = cfg.port;
125           klippy_uds_address = cfg.klipperSocket;
126         };
127         file_manager = {
128           config_path = cfg.configDir;
129         };
130         database = {
131           database_path = "${cfg.stateDir}/database";
132         };
133       };
134       fullConfig = recursiveUpdate cfg.settings forcedConfig;
135     in format.generate "moonraker.cfg" fullConfig;
137     systemd.tmpfiles.rules = [
138       "d '${cfg.stateDir}' - ${cfg.user} ${cfg.group} - -"
139       "d '${cfg.configDir}' - ${cfg.user} ${cfg.group} - -"
140     ];
142     systemd.services.moonraker = {
143       description = "Moonraker, an API web server for Klipper";
144       wantedBy = [ "multi-user.target" ];
145       after = [ "network.target" ]
146         ++ optional config.services.klipper.enable "klipper.service";
148       # Moonraker really wants its own config to be writable...
149       script = ''
150         cp /etc/moonraker.cfg ${cfg.configDir}/moonraker-temp.cfg
151         chmod u+w ${cfg.configDir}/moonraker-temp.cfg
152         exec ${pkg}/bin/moonraker -c ${cfg.configDir}/moonraker-temp.cfg
153       '';
155       # Needs `ip` command
156       path = [ pkgs.iproute2 ];
158       serviceConfig = {
159         WorkingDirectory = cfg.stateDir;
160         PrivateTmp = true;
161         Group = cfg.group;
162         User = cfg.user;
163       };
164     };
166     security.polkit.extraConfig = lib.optionalString cfg.allowSystemControl ''
167       // nixos/moonraker: Allow Moonraker to perform system-level operations
168       //
169       // This was enabled via services.moonraker.allowSystemControl.
170       polkit.addRule(function(action, subject) {
171         if ((action.id == "org.freedesktop.systemd1.manage-units" ||
172              action.id == "org.freedesktop.login1.power-off" ||
173              action.id == "org.freedesktop.login1.power-off-multiple-sessions" ||
174              action.id == "org.freedesktop.login1.reboot" ||
175              action.id == "org.freedesktop.login1.reboot-multiple-sessions" ||
176              action.id.startsWith("org.freedesktop.packagekit.")) &&
177              subject.user == "${cfg.user}") {
178           return polkit.Result.YES;
179         }
180       });
181     '';
182   };
184   meta.maintainers = with maintainers; [
185     cab404
186     vtuan10
187   ];