at 23.05-pre 9.7 kB view raw
1{ lib, pkgs, config, ... }: 2 3with lib; 4 5let 6 cfg = config.services.plausible; 7 8in { 9 options.services.plausible = { 10 enable = mkEnableOption (lib.mdDoc "plausible"); 11 12 releaseCookiePath = mkOption { 13 type = with types; either str path; 14 description = lib.mdDoc '' 15 The path to the file with release cookie. (used for remote connection to the running node). 16 ''; 17 }; 18 19 adminUser = { 20 name = mkOption { 21 default = "admin"; 22 type = types.str; 23 description = lib.mdDoc '' 24 Name of the admin user that plausible will created on initial startup. 25 ''; 26 }; 27 28 email = mkOption { 29 type = types.str; 30 example = "admin@localhost"; 31 description = lib.mdDoc '' 32 Email-address of the admin-user. 33 ''; 34 }; 35 36 passwordFile = mkOption { 37 type = types.either types.str types.path; 38 description = lib.mdDoc '' 39 Path to the file which contains the password of the admin user. 40 ''; 41 }; 42 43 activate = mkEnableOption (lib.mdDoc "activating the freshly created admin-user"); 44 }; 45 46 database = { 47 clickhouse = { 48 setup = mkEnableOption (lib.mdDoc "creating a clickhouse instance") // { default = true; }; 49 url = mkOption { 50 default = "http://localhost:8123/default"; 51 type = types.str; 52 description = lib.mdDoc '' 53 The URL to be used to connect to `clickhouse`. 54 ''; 55 }; 56 }; 57 postgres = { 58 setup = mkEnableOption (lib.mdDoc "creating a postgresql instance") // { default = true; }; 59 dbname = mkOption { 60 default = "plausible"; 61 type = types.str; 62 description = lib.mdDoc '' 63 Name of the database to use. 64 ''; 65 }; 66 socket = mkOption { 67 default = "/run/postgresql"; 68 type = types.str; 69 description = lib.mdDoc '' 70 Path to the UNIX domain-socket to communicate with `postgres`. 71 ''; 72 }; 73 }; 74 }; 75 76 server = { 77 disableRegistration = mkOption { 78 default = true; 79 type = types.bool; 80 description = lib.mdDoc '' 81 Whether to prohibit creating an account in plausible's UI. 82 ''; 83 }; 84 secretKeybaseFile = mkOption { 85 type = types.either types.path types.str; 86 description = lib.mdDoc '' 87 Path to the secret used by the `phoenix`-framework. Instructions 88 how to generate one are documented in the 89 [ 90 framework docs](https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Secret.html#content). 91 ''; 92 }; 93 port = mkOption { 94 default = 8000; 95 type = types.port; 96 description = lib.mdDoc '' 97 Port where the service should be available. 98 ''; 99 }; 100 baseUrl = mkOption { 101 type = types.str; 102 description = lib.mdDoc '' 103 Public URL where plausible is available. 104 105 Note that `/path` components are currently ignored: 106 [ 107 https://github.com/plausible/analytics/issues/1182 108 ](https://github.com/plausible/analytics/issues/1182). 109 ''; 110 }; 111 }; 112 113 mail = { 114 email = mkOption { 115 default = "hello@plausible.local"; 116 type = types.str; 117 description = lib.mdDoc '' 118 The email id to use for as *from* address of all communications 119 from Plausible. 120 ''; 121 }; 122 smtp = { 123 hostAddr = mkOption { 124 default = "localhost"; 125 type = types.str; 126 description = lib.mdDoc '' 127 The host address of your smtp server. 128 ''; 129 }; 130 hostPort = mkOption { 131 default = 25; 132 type = types.port; 133 description = lib.mdDoc '' 134 The port of your smtp server. 135 ''; 136 }; 137 user = mkOption { 138 default = null; 139 type = types.nullOr types.str; 140 description = lib.mdDoc '' 141 The username/email in case SMTP auth is enabled. 142 ''; 143 }; 144 passwordFile = mkOption { 145 default = null; 146 type = with types; nullOr (either str path); 147 description = lib.mdDoc '' 148 The path to the file with the password in case SMTP auth is enabled. 149 ''; 150 }; 151 enableSSL = mkEnableOption (lib.mdDoc "SSL when connecting to the SMTP server"); 152 retries = mkOption { 153 type = types.ints.unsigned; 154 default = 2; 155 description = lib.mdDoc '' 156 Number of retries to make until mailer gives up. 157 ''; 158 }; 159 }; 160 }; 161 }; 162 163 config = mkIf cfg.enable { 164 assertions = [ 165 { assertion = cfg.adminUser.activate -> cfg.database.postgres.setup; 166 message = '' 167 Unable to automatically activate the admin-user if no locally managed DB for 168 postgres (`services.plausible.database.postgres.setup') is enabled! 169 ''; 170 } 171 ]; 172 173 services.postgresql = mkIf cfg.database.postgres.setup { 174 enable = true; 175 }; 176 177 services.clickhouse = mkIf cfg.database.clickhouse.setup { 178 enable = true; 179 }; 180 181 services.epmd.enable = true; 182 183 environment.systemPackages = [ pkgs.plausible ]; 184 185 systemd.services = mkMerge [ 186 { 187 plausible = { 188 inherit (pkgs.plausible.meta) description; 189 documentation = [ "https://plausible.io/docs/self-hosting" ]; 190 wantedBy = [ "multi-user.target" ]; 191 after = optional cfg.database.clickhouse.setup "clickhouse.service" 192 ++ optionals cfg.database.postgres.setup [ 193 "postgresql.service" 194 "plausible-postgres.service" 195 ]; 196 requires = optional cfg.database.clickhouse.setup "clickhouse.service" 197 ++ optionals cfg.database.postgres.setup [ 198 "postgresql.service" 199 "plausible-postgres.service" 200 ]; 201 202 environment = { 203 # NixOS specific option to avoid that it's trying to write into its store-path. 204 # See also https://github.com/lau/tzdata#data-directory-and-releases 205 STORAGE_DIR = "/var/lib/plausible/elixir_tzdata"; 206 207 # Configuration options from 208 # https://plausible.io/docs/self-hosting-configuration 209 PORT = toString cfg.server.port; 210 DISABLE_REGISTRATION = boolToString cfg.server.disableRegistration; 211 212 RELEASE_TMP = "/var/lib/plausible/tmp"; 213 # Home is needed to connect to the node with iex 214 HOME = "/var/lib/plausible"; 215 216 ADMIN_USER_NAME = cfg.adminUser.name; 217 ADMIN_USER_EMAIL = cfg.adminUser.email; 218 219 DATABASE_SOCKET_DIR = cfg.database.postgres.socket; 220 DATABASE_NAME = cfg.database.postgres.dbname; 221 CLICKHOUSE_DATABASE_URL = cfg.database.clickhouse.url; 222 223 BASE_URL = cfg.server.baseUrl; 224 225 MAILER_EMAIL = cfg.mail.email; 226 SMTP_HOST_ADDR = cfg.mail.smtp.hostAddr; 227 SMTP_HOST_PORT = toString cfg.mail.smtp.hostPort; 228 SMTP_RETRIES = toString cfg.mail.smtp.retries; 229 SMTP_HOST_SSL_ENABLED = boolToString cfg.mail.smtp.enableSSL; 230 231 SELFHOST = "true"; 232 } // (optionalAttrs (cfg.mail.smtp.user != null) { 233 SMTP_USER_NAME = cfg.mail.smtp.user; 234 }); 235 236 path = [ pkgs.plausible ] 237 ++ optional cfg.database.postgres.setup config.services.postgresql.package; 238 script = '' 239 export CONFIG_DIR=$CREDENTIALS_DIRECTORY 240 241 export RELEASE_COOKIE="$(< $CREDENTIALS_DIRECTORY/RELEASE_COOKIE )" 242 243 # setup 244 ${pkgs.plausible}/createdb.sh 245 ${pkgs.plausible}/migrate.sh 246 ${optionalString cfg.adminUser.activate '' 247 if ! ${pkgs.plausible}/init-admin.sh | grep 'already exists'; then 248 psql -d plausible <<< "UPDATE users SET email_verified=true;" 249 fi 250 ''} 251 252 exec plausible start 253 ''; 254 255 serviceConfig = { 256 DynamicUser = true; 257 PrivateTmp = true; 258 WorkingDirectory = "/var/lib/plausible"; 259 StateDirectory = "plausible"; 260 LoadCredential = [ 261 "ADMIN_USER_PWD:${cfg.adminUser.passwordFile}" 262 "SECRET_KEY_BASE:${cfg.server.secretKeybaseFile}" 263 "RELEASE_COOKIE:${cfg.releaseCookiePath}" 264 ] ++ lib.optionals (cfg.mail.smtp.passwordFile != null) [ "SMTP_USER_PWD:${cfg.mail.smtp.passwordFile}"]; 265 }; 266 }; 267 } 268 (mkIf cfg.database.postgres.setup { 269 # `plausible' requires the `citext'-extension. 270 plausible-postgres = { 271 after = [ "postgresql.service" ]; 272 partOf = [ "plausible.service" ]; 273 serviceConfig = { 274 Type = "oneshot"; 275 User = config.services.postgresql.superUser; 276 RemainAfterExit = true; 277 }; 278 script = with cfg.database.postgres; '' 279 PSQL() { 280 ${config.services.postgresql.package}/bin/psql --port=5432 "$@" 281 } 282 # check if the database already exists 283 if ! PSQL -lqt | ${pkgs.coreutils}/bin/cut -d \| -f 1 | ${pkgs.gnugrep}/bin/grep -qw ${dbname} ; then 284 PSQL -tAc "CREATE ROLE plausible WITH LOGIN;" 285 PSQL -tAc "CREATE DATABASE ${dbname} WITH OWNER plausible;" 286 PSQL -d ${dbname} -tAc "CREATE EXTENSION IF NOT EXISTS citext;" 287 fi 288 ''; 289 }; 290 }) 291 ]; 292 }; 293 294 meta.maintainers = with maintainers; [ ma27 ]; 295 meta.doc = ./plausible.xml; 296}