1{
2 config,
3 lib,
4 pkgs,
5 utils,
6 ...
7}:
8
9let
10 cfg = config.services.collabora-online;
11
12 freeformType = lib.types.attrsOf ((pkgs.formats.json { }).type) // {
13 description = ''
14 `coolwsd.xml` configuration type, used to override values in the default configuration.
15
16 Attribute names correspond to XML tags unless prefixed with `@`. Nested attribute sets
17 correspond to nested XML tags. Attribute prefixed with `@` correspond to XML attributes. E.g.,
18 `{ storage.wopi."@allow" = true; }` in Nix corresponds to
19 `<storage><wopi allow="true"/></storage>` in `coolwsd.xml`, or `--o:storage.wopi[@allow]=true`
20 in the command line.
21
22 Arrays correspond to multiple elements with the same tag name. E.g.
23 `{ host = [ '''127\.0\.0\.1''' "::1" ]; }` in Nix corresponds to
24 ```xml
25 <net><post_allow>
26 <host>127\.0\.0\.1</host>
27 <host>::1</host>
28 </post_allow></net>
29 ```
30 in `coolwsd.xml`, or
31 `--o:net.post_allow.host[0]='127\.0\.0\.1 --o:net.post_allow.host[1]=::1` in the command line.
32
33 Null values could be used to remove an element from the default configuration.
34 '';
35 };
36
37 configFile =
38 pkgs.runCommandLocal "coolwsd.xml"
39 {
40 nativeBuildInputs = [
41 pkgs.jq
42 pkgs.yq-go
43 ];
44 userConfig = builtins.toJSON { config = cfg.settings; };
45 passAsFile = [ "userConfig" ];
46 }
47 # Merge the cfg.settings into the default coolwsd.xml.
48 # See https://github.com/CollaboraOnline/online/issues/10049.
49 ''
50 yq --input-format=xml \
51 --xml-attribute-prefix=@ \
52 --output-format=json \
53 ${cfg.package}/etc/coolwsd/coolwsd.xml \
54 > ./default_coolwsd.json
55
56 jq '.[0] * .[1] | del(..|nulls)' \
57 --slurp \
58 ./default_coolwsd.json \
59 $userConfigPath \
60 > ./merged.json
61
62 yq --output-format=xml \
63 --xml-attribute-prefix=@ \
64 ./merged.json \
65 > $out
66 '';
67in
68{
69 options.services.collabora-online = {
70 enable = lib.mkEnableOption "collabora-online";
71
72 package = lib.mkPackageOption pkgs "Collabora Online" { default = "collabora-online"; };
73
74 port = lib.mkOption {
75 type = lib.types.port;
76 default = 9980;
77 description = "Listening port";
78 };
79
80 settings = lib.mkOption {
81 type = freeformType;
82 default = { };
83 description = ''
84 Configuration for Collabora Online WebSocket Daemon, see
85 <https://sdk.collaboraonline.com/docs/installation/Configuration.html>, or
86 <https://github.com/CollaboraOnline/online/blob/master/coolwsd.xml.in> for the default
87 configuration.
88 '';
89 };
90
91 aliasGroups = lib.mkOption {
92 type = lib.types.listOf (
93 lib.types.submodule {
94 options = {
95 host = lib.mkOption {
96 type = lib.types.str;
97 example = "scheme://hostname:port";
98 description = "Hostname to allow or deny.";
99 };
100
101 aliases = lib.mkOption {
102 type = lib.types.listOf lib.types.str;
103 default = [ ];
104 example = [
105 "scheme://aliasname1:port"
106 "scheme://aliasname2:port"
107 ];
108 description = "A list of regex pattern of aliasname.";
109 };
110 };
111 }
112 );
113 default = [ ];
114 description = "Alias groups to use.";
115 };
116
117 extraArgs = lib.mkOption {
118 type = lib.types.listOf lib.types.str;
119 default = [ ];
120 description = "Extra arguments to pass to the service.";
121 };
122 };
123
124 config = lib.mkIf cfg.enable {
125 services.collabora-online.settings = {
126 child_root_path = lib.mkDefault "/var/lib/cool/child-roots";
127 sys_template_path = lib.mkDefault "/var/lib/cool/systemplate";
128
129 file_server_root_path = lib.mkDefault "${config.services.collabora-online.package}/share/coolwsd";
130
131 # Use mount namespaces instead of setcap'd coolmount/coolforkit.
132 mount_namespaces = lib.mkDefault true;
133
134 # By default, use dummy self-signed certificates provided for testing.
135 ssl.ca_file_path = lib.mkDefault "${config.services.collabora-online.package}/etc/coolwsd/ca-chain.cert.pem";
136 ssl.cert_file_path = lib.mkDefault "${config.services.collabora-online.package}/etc/coolwsd/cert.pem";
137 ssl.key_file_path = lib.mkDefault "${config.services.collabora-online.package}/etc/coolwsd/key.pem";
138 };
139
140 users.users.cool = {
141 isSystemUser = true;
142 group = "cool";
143 };
144 users.groups.cool = { };
145
146 systemd.services.coolwsd-systemplate-setup = {
147 description = "Collabora Online WebSocket Daemon Setup";
148 wantedBy = [ "multi-user.target" ];
149 serviceConfig = {
150 ExecStart = utils.escapeSystemdExecArgs [
151 "${cfg.package}/bin/coolwsd-systemplate-setup"
152 "/var/lib/cool/systemplate"
153 "${cfg.package.libreoffice}/lib/collaboraoffice"
154 ];
155 RemainAfterExit = true;
156 StateDirectory = "cool";
157 Type = "oneshot";
158 User = "cool";
159 };
160 };
161
162 systemd.services.coolwsd = {
163 description = "Collabora Online WebSocket Daemon";
164 wantedBy = [ "multi-user.target" ];
165 after = [
166 "network.target"
167 "coolwsd-systemplate-setup.service"
168 ];
169
170 environment = builtins.listToAttrs (
171 lib.imap1 (n: ag: {
172 name = "aliasgroup${toString n}";
173 value = lib.concatStringsSep "," ([ ag.host ] ++ ag.aliases);
174 }) cfg.aliasGroups
175 );
176
177 serviceConfig = {
178 ExecStart = utils.escapeSystemdExecArgs (
179 [
180 "${cfg.package}/bin/coolwsd"
181 "--config-file=${configFile}"
182 "--port=${toString cfg.port}"
183 "--use-env-vars"
184 "--version"
185 ]
186 ++ cfg.extraArgs
187 );
188 KillMode = "mixed";
189 KillSignal = "SIGINT";
190 LimitNOFILE = "infinity:infinity";
191 Restart = "always";
192 StateDirectory = "cool";
193 TimeoutStopSec = 120;
194 User = "cool";
195 };
196 };
197 };
198
199 meta.maintainers = [ lib.maintainers.xzfc ];
200}