grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / web-apps / windmill.nix
blob2571740ebbf1556395785d4a6e30e26d8a0f5d44
1 { config, pkgs, lib, ... }:
3 let
4   cfg = config.services.windmill;
5 in
7   options.services.windmill = {
8     enable = lib.mkEnableOption "windmill service";
10     serverPort = lib.mkOption {
11       type = lib.types.port;
12       default = 8001;
13       description = "Port the windmill server listens on.";
14     };
16     lspPort = lib.mkOption {
17       type = lib.types.port;
18       default = 3001;
19       description = "Port the windmill lsp listens on.";
20     };
22     database = {
23       name = lib.mkOption {
24         type = lib.types.str;
25         # the simplest database setup is to have the database named like the user.
26         default = "windmill";
27         description = "Database name.";
28       };
30       user = lib.mkOption {
31         type = lib.types.str;
32         # the simplest database setup is to have the database user like the name.
33         default = "windmill";
34         description = "Database user.";
35       };
37       url = lib.mkOption {
38         type = lib.types.str;
39         default = "postgres://${config.services.windmill.database.name}?host=/var/run/postgresql";
40         defaultText = lib.literalExpression ''
41           "postgres://\$\{config.services.windmill.database.name}?host=/var/run/postgresql";
42         '';
43         description = "Database url. Note that any secret here would be world-readable. Use `services.windmill.database.urlPath` unstead to include secrets in the url.";
44       };
46       urlPath = lib.mkOption {
47         type = lib.types.nullOr lib.types.path;
48         description = ''
49           Path to the file containing the database url windmill should connect to. This is not deducted from database user and name as it might contain a secret
50         '';
51         default = null;
52         example = "config.age.secrets.DATABASE_URL_FILE.path";
53       };
55       createLocally = lib.mkOption {
56         type = lib.types.bool;
57         default = true;
58         description = "Whether to create a local database automatically.";
59       };
60     };
62     baseUrl = lib.mkOption {
63       type = lib.types.str;
64       default = "https://localhost:${toString config.services.windmill.serverPort}";
65       defaultText = lib.literalExpression ''
66         "https://localhost:\$\{toString config.services.windmill.serverPort}";
67       '';
68       description = ''
69         The base url that windmill will be served on.
70       '';
71       example = "https://windmill.example.com";
72     };
74     logLevel = lib.mkOption {
75       type = lib.types.enum [ "error" "warn" "info" "debug" "trace" ];
76       default = "info";
77       description = "Log level";
78     };
79   };
81   config = lib.mkIf cfg.enable {
83     services.postgresql = lib.optionalAttrs (cfg.database.createLocally) {
84       enable = lib.mkDefault true;
86       ensureDatabases = [ cfg.database.name ];
87       ensureUsers = [
88         { name = cfg.database.user;
89           ensureDBOwnership = true;
90         }
91       ];
93    };
95    systemd.services =
96     let
97       useUrlPath = (cfg.database.urlPath != null);
98       serviceConfig = {
99         DynamicUser = true;
100         # using the same user to simplify db connection
101         User = cfg.database.user;
102         ExecStart = "${pkgs.windmill}/bin/windmill";
104         Restart = "always";
105       } // lib.optionalAttrs useUrlPath {
106         LoadCredential = [
107           "DATABASE_URL_FILE:${cfg.database.urlPath}"
108         ];
109       };
110       db_url_envs = lib.optionalAttrs useUrlPath {
111         DATABASE_URL_FILE = "%d/DATABASE_URL_FILE";
112       } // lib.optionalAttrs (!useUrlPath) {
113         DATABASE_URL = cfg.database.url;
114       };
115     in
116     {
118     # coming from https://github.com/windmill-labs/windmill/blob/main/init-db-as-superuser.sql
119     # modified to not grant priviledges on all tables
120     # create role windmill_user and windmill_admin only if they don't exist
121     postgresql.postStart = lib.mkIf cfg.database.createLocally (lib.mkAfter ''
122       $PSQL -tA <<"EOF"
123 DO $$
124 BEGIN
125     IF NOT EXISTS (
126         SELECT FROM pg_catalog.pg_roles
127         WHERE rolname = 'windmill_user'
128     ) THEN
129         CREATE ROLE windmill_user;
130         GRANT ALL PRIVILEGES ON DATABASE ${cfg.database.name} TO windmill_user;
131     ELSE
132       RAISE NOTICE 'Role "windmill_user" already exists. Skipping.';
133     END IF;
134     IF NOT EXISTS (
135         SELECT FROM pg_catalog.pg_roles
136         WHERE rolname = 'windmill_admin'
137     ) THEN
138       CREATE ROLE windmill_admin WITH BYPASSRLS;
139       GRANT windmill_user TO windmill_admin;
140     ELSE
141       RAISE NOTICE 'Role "windmill_admin" already exists. Skipping.';
142     END IF;
143     GRANT windmill_admin TO windmill;
147     '');
149      windmill-server = {
150         description = "Windmill server";
151         after = [ "network.target" ] ++ lib.optional cfg.database.createLocally "postgresql.service";
152         wantedBy = [ "multi-user.target" ];
154         serviceConfig = serviceConfig // { StateDirectory = "windmill";};
156         environment = {
157           PORT = builtins.toString cfg.serverPort;
158           WM_BASE_URL = cfg.baseUrl;
159           RUST_LOG = cfg.logLevel;
160           MODE = "server";
161         } // db_url_envs;
162       };
164      windmill-worker = {
165         description = "Windmill worker";
166         after = [ "network.target" ] ++ lib.optional cfg.database.createLocally "postgresql.service";
167         wantedBy = [ "multi-user.target" ];
169         serviceConfig = serviceConfig // { StateDirectory = "windmill-worker";};
171         environment = {
172           WM_BASE_URL = cfg.baseUrl;
173           RUST_LOG = cfg.logLevel;
174           MODE = "worker";
175           WORKER_GROUP = "default";
176           KEEP_JOB_DIR = "false";
177         } // db_url_envs;
178       };
180      windmill-worker-native = {
181         description = "Windmill worker native";
182         after = [ "network.target" ] ++ lib.optional cfg.database.createLocally "postgresql.service";
183         wantedBy = [ "multi-user.target" ];
185         serviceConfig = serviceConfig // { StateDirectory = "windmill-worker-native";};
187         environment = {
188           WM_BASE_URL = cfg.baseUrl;
189           RUST_LOG = cfg.logLevel;
190           MODE = "worker";
191           WORKER_GROUP = "native";
192         } // db_url_envs;
193       };
194     };
195   };