2021-05-21 21:15:23 +00:00
|
|
|
{ lib, pkgs, config, ... }:
|
|
|
|
|
|
|
|
with lib;
|
|
|
|
|
|
|
|
let
|
|
|
|
cfg = config.services.plausible;
|
|
|
|
|
|
|
|
in {
|
|
|
|
options.services.plausible = {
|
|
|
|
enable = mkEnableOption "plausible";
|
|
|
|
|
2021-11-09 13:15:53 +00:00
|
|
|
releaseCookiePath = mkOption {
|
2022-01-25 16:19:49 +00:00
|
|
|
type = with types; either str path;
|
2022-07-28 21:19:15 +00:00
|
|
|
description = lib.mdDoc ''
|
2021-11-09 13:15:53 +00:00
|
|
|
The path to the file with release cookie. (used for remote connection to the running node).
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2021-05-21 21:15:23 +00:00
|
|
|
adminUser = {
|
|
|
|
name = mkOption {
|
|
|
|
default = "admin";
|
|
|
|
type = types.str;
|
2022-07-28 21:19:15 +00:00
|
|
|
description = lib.mdDoc ''
|
2021-05-21 21:15:23 +00:00
|
|
|
Name of the admin user that plausible will created on initial startup.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
email = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
example = "admin@localhost";
|
2022-07-28 21:19:15 +00:00
|
|
|
description = lib.mdDoc ''
|
2021-05-21 21:15:23 +00:00
|
|
|
Email-address of the admin-user.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
passwordFile = mkOption {
|
|
|
|
type = types.either types.str types.path;
|
2022-07-28 21:19:15 +00:00
|
|
|
description = lib.mdDoc ''
|
2021-05-21 21:15:23 +00:00
|
|
|
Path to the file which contains the password of the admin user.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
activate = mkEnableOption "activating the freshly created admin-user";
|
|
|
|
};
|
|
|
|
|
|
|
|
database = {
|
|
|
|
clickhouse = {
|
|
|
|
setup = mkEnableOption "creating a clickhouse instance" // { default = true; };
|
|
|
|
url = mkOption {
|
|
|
|
default = "http://localhost:8123/default";
|
|
|
|
type = types.str;
|
|
|
|
description = ''
|
2021-05-29 14:13:20 +00:00
|
|
|
The URL to be used to connect to <package>clickhouse</package>.
|
2021-05-21 21:15:23 +00:00
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
postgres = {
|
|
|
|
setup = mkEnableOption "creating a postgresql instance" // { default = true; };
|
|
|
|
dbname = mkOption {
|
|
|
|
default = "plausible";
|
|
|
|
type = types.str;
|
2022-07-28 21:19:15 +00:00
|
|
|
description = lib.mdDoc ''
|
2021-05-21 21:15:23 +00:00
|
|
|
Name of the database to use.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
socket = mkOption {
|
|
|
|
default = "/run/postgresql";
|
|
|
|
type = types.str;
|
|
|
|
description = ''
|
|
|
|
Path to the UNIX domain-socket to communicate with <package>postgres</package>.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
server = {
|
|
|
|
disableRegistration = mkOption {
|
|
|
|
default = true;
|
|
|
|
type = types.bool;
|
2022-07-28 21:19:15 +00:00
|
|
|
description = lib.mdDoc ''
|
2021-05-21 21:15:23 +00:00
|
|
|
Whether to prohibit creating an account in plausible's UI.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
secretKeybaseFile = mkOption {
|
|
|
|
type = types.either types.path types.str;
|
2022-07-28 21:19:15 +00:00
|
|
|
description = lib.mdDoc ''
|
|
|
|
Path to the secret used by the `phoenix`-framework. Instructions
|
2021-05-21 21:15:23 +00:00
|
|
|
how to generate one are documented in the
|
2022-07-28 21:19:15 +00:00
|
|
|
[
|
|
|
|
framework docs](https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Secret.html#content).
|
2021-05-21 21:15:23 +00:00
|
|
|
'';
|
|
|
|
};
|
|
|
|
port = mkOption {
|
|
|
|
default = 8000;
|
|
|
|
type = types.port;
|
2022-07-28 21:19:15 +00:00
|
|
|
description = lib.mdDoc ''
|
2021-05-21 21:15:23 +00:00
|
|
|
Port where the service should be available.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
baseUrl = mkOption {
|
|
|
|
type = types.str;
|
2022-07-28 21:19:15 +00:00
|
|
|
description = lib.mdDoc ''
|
2021-05-21 21:15:23 +00:00
|
|
|
Public URL where plausible is available.
|
2021-07-14 03:28:57 +00:00
|
|
|
|
2022-07-28 21:19:15 +00:00
|
|
|
Note that `/path` components are currently ignored:
|
|
|
|
[
|
2021-07-14 03:28:57 +00:00
|
|
|
https://github.com/plausible/analytics/issues/1182
|
2022-07-28 21:19:15 +00:00
|
|
|
](https://github.com/plausible/analytics/issues/1182).
|
2021-05-21 21:15:23 +00:00
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
mail = {
|
|
|
|
email = mkOption {
|
2021-05-29 14:13:20 +00:00
|
|
|
default = "hello@plausible.local";
|
2021-05-21 21:15:23 +00:00
|
|
|
type = types.str;
|
2022-07-28 21:19:15 +00:00
|
|
|
description = lib.mdDoc ''
|
|
|
|
The email id to use for as *from* address of all communications
|
2021-05-21 21:15:23 +00:00
|
|
|
from Plausible.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
smtp = {
|
|
|
|
hostAddr = mkOption {
|
|
|
|
default = "localhost";
|
|
|
|
type = types.str;
|
2022-07-28 21:19:15 +00:00
|
|
|
description = lib.mdDoc ''
|
2021-05-21 21:15:23 +00:00
|
|
|
The host address of your smtp server.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
hostPort = mkOption {
|
|
|
|
default = 25;
|
|
|
|
type = types.port;
|
2022-07-28 21:19:15 +00:00
|
|
|
description = lib.mdDoc ''
|
2021-05-21 21:15:23 +00:00
|
|
|
The port of your smtp server.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
user = mkOption {
|
|
|
|
default = null;
|
|
|
|
type = types.nullOr types.str;
|
2022-07-28 21:19:15 +00:00
|
|
|
description = lib.mdDoc ''
|
2021-05-21 21:15:23 +00:00
|
|
|
The username/email in case SMTP auth is enabled.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
passwordFile = mkOption {
|
|
|
|
default = null;
|
|
|
|
type = with types; nullOr (either str path);
|
2022-07-28 21:19:15 +00:00
|
|
|
description = lib.mdDoc ''
|
2021-05-21 21:15:23 +00:00
|
|
|
The path to the file with the password in case SMTP auth is enabled.
|
|
|
|
'';
|
|
|
|
};
|
2021-05-29 14:13:20 +00:00
|
|
|
enableSSL = mkEnableOption "SSL when connecting to the SMTP server";
|
2021-05-21 21:15:23 +00:00
|
|
|
retries = mkOption {
|
|
|
|
type = types.ints.unsigned;
|
|
|
|
default = 2;
|
2022-07-28 21:19:15 +00:00
|
|
|
description = lib.mdDoc ''
|
2021-05-21 21:15:23 +00:00
|
|
|
Number of retries to make until mailer gives up.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
config = mkIf cfg.enable {
|
|
|
|
assertions = [
|
|
|
|
{ assertion = cfg.adminUser.activate -> cfg.database.postgres.setup;
|
|
|
|
message = ''
|
2021-05-29 15:33:16 +00:00
|
|
|
Unable to automatically activate the admin-user if no locally managed DB for
|
2021-05-21 21:15:23 +00:00
|
|
|
postgres (`services.plausible.database.postgres.setup') is enabled!
|
|
|
|
'';
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
services.postgresql = mkIf cfg.database.postgres.setup {
|
|
|
|
enable = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
services.clickhouse = mkIf cfg.database.clickhouse.setup {
|
|
|
|
enable = true;
|
|
|
|
};
|
|
|
|
|
2021-10-30 12:58:21 +00:00
|
|
|
services.epmd.enable = true;
|
|
|
|
|
2021-11-09 13:15:53 +00:00
|
|
|
environment.systemPackages = [ pkgs.plausible ];
|
|
|
|
|
2021-05-21 21:15:23 +00:00
|
|
|
systemd.services = mkMerge [
|
|
|
|
{
|
|
|
|
plausible = {
|
|
|
|
inherit (pkgs.plausible.meta) description;
|
|
|
|
documentation = [ "https://plausible.io/docs/self-hosting" ];
|
2021-05-29 14:13:20 +00:00
|
|
|
wantedBy = [ "multi-user.target" ];
|
2021-10-30 12:58:21 +00:00
|
|
|
after = optionals cfg.database.postgres.setup [ "postgresql.service" "plausible-postgres.service" ];
|
2021-05-29 14:13:20 +00:00
|
|
|
requires = optional cfg.database.clickhouse.setup "clickhouse.service"
|
|
|
|
++ optionals cfg.database.postgres.setup [
|
|
|
|
"postgresql.service"
|
|
|
|
"plausible-postgres.service"
|
|
|
|
];
|
2021-05-21 21:15:23 +00:00
|
|
|
|
|
|
|
environment = {
|
|
|
|
# NixOS specific option to avoid that it's trying to write into its store-path.
|
|
|
|
# See also https://github.com/lau/tzdata#data-directory-and-releases
|
2021-10-30 12:58:21 +00:00
|
|
|
STORAGE_DIR = "/var/lib/plausible/elixir_tzdata";
|
2021-05-21 21:15:23 +00:00
|
|
|
|
|
|
|
# Configuration options from
|
|
|
|
# https://plausible.io/docs/self-hosting-configuration
|
|
|
|
PORT = toString cfg.server.port;
|
|
|
|
DISABLE_REGISTRATION = boolToString cfg.server.disableRegistration;
|
|
|
|
|
2021-05-29 14:13:20 +00:00
|
|
|
RELEASE_TMP = "/var/lib/plausible/tmp";
|
2021-11-09 13:15:53 +00:00
|
|
|
# Home is needed to connect to the node with iex
|
|
|
|
HOME = "/var/lib/plausible";
|
2021-05-29 14:13:20 +00:00
|
|
|
|
2021-05-21 21:15:23 +00:00
|
|
|
ADMIN_USER_NAME = cfg.adminUser.name;
|
|
|
|
ADMIN_USER_EMAIL = cfg.adminUser.email;
|
|
|
|
|
|
|
|
DATABASE_SOCKET_DIR = cfg.database.postgres.socket;
|
|
|
|
DATABASE_NAME = cfg.database.postgres.dbname;
|
|
|
|
CLICKHOUSE_DATABASE_URL = cfg.database.clickhouse.url;
|
|
|
|
|
|
|
|
BASE_URL = cfg.server.baseUrl;
|
|
|
|
|
|
|
|
MAILER_EMAIL = cfg.mail.email;
|
|
|
|
SMTP_HOST_ADDR = cfg.mail.smtp.hostAddr;
|
|
|
|
SMTP_HOST_PORT = toString cfg.mail.smtp.hostPort;
|
|
|
|
SMTP_RETRIES = toString cfg.mail.smtp.retries;
|
|
|
|
SMTP_HOST_SSL_ENABLED = boolToString cfg.mail.smtp.enableSSL;
|
2021-05-29 14:13:20 +00:00
|
|
|
|
|
|
|
SELFHOST = "true";
|
|
|
|
} // (optionalAttrs (cfg.mail.smtp.user != null) {
|
|
|
|
SMTP_USER_NAME = cfg.mail.smtp.user;
|
|
|
|
});
|
2021-05-21 21:15:23 +00:00
|
|
|
|
|
|
|
path = [ pkgs.plausible ]
|
|
|
|
++ optional cfg.database.postgres.setup config.services.postgresql.package;
|
2021-10-30 12:58:21 +00:00
|
|
|
script = ''
|
|
|
|
export CONFIG_DIR=$CREDENTIALS_DIRECTORY
|
|
|
|
|
2022-01-25 16:19:49 +00:00
|
|
|
export RELEASE_COOKIE="$(< $CREDENTIALS_DIRECTORY/RELEASE_COOKIE )"
|
|
|
|
|
2021-10-30 12:58:21 +00:00
|
|
|
# setup
|
|
|
|
${pkgs.plausible}/createdb.sh
|
|
|
|
${pkgs.plausible}/migrate.sh
|
|
|
|
${optionalString cfg.adminUser.activate ''
|
|
|
|
if ! ${pkgs.plausible}/init-admin.sh | grep 'already exists'; then
|
|
|
|
psql -d plausible <<< "UPDATE users SET email_verified=true;"
|
|
|
|
fi
|
|
|
|
''}
|
2022-01-25 16:19:49 +00:00
|
|
|
|
|
|
|
exec plausible start
|
2021-10-30 12:58:21 +00:00
|
|
|
'';
|
2021-05-21 21:15:23 +00:00
|
|
|
|
|
|
|
serviceConfig = {
|
|
|
|
DynamicUser = true;
|
|
|
|
PrivateTmp = true;
|
|
|
|
WorkingDirectory = "/var/lib/plausible";
|
|
|
|
StateDirectory = "plausible";
|
2021-10-30 12:58:21 +00:00
|
|
|
LoadCredential = [
|
|
|
|
"ADMIN_USER_PWD:${cfg.adminUser.passwordFile}"
|
|
|
|
"SECRET_KEY_BASE:${cfg.server.secretKeybaseFile}"
|
2022-01-25 16:19:49 +00:00
|
|
|
"RELEASE_COOKIE:${cfg.releaseCookiePath}"
|
|
|
|
] ++ lib.optionals (cfg.mail.smtp.passwordFile != null) [ "SMTP_USER_PWD:${cfg.mail.smtp.passwordFile}"];
|
2021-05-21 21:15:23 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
(mkIf cfg.database.postgres.setup {
|
2021-05-29 14:13:20 +00:00
|
|
|
# `plausible' requires the `citext'-extension.
|
2021-05-21 21:15:23 +00:00
|
|
|
plausible-postgres = {
|
|
|
|
after = [ "postgresql.service" ];
|
|
|
|
partOf = [ "plausible.service" ];
|
2021-10-30 12:58:21 +00:00
|
|
|
serviceConfig = {
|
|
|
|
Type = "oneshot";
|
|
|
|
User = config.services.postgresql.superUser;
|
|
|
|
RemainAfterExit = true;
|
|
|
|
};
|
|
|
|
script = with cfg.database.postgres; ''
|
2021-05-29 14:13:20 +00:00
|
|
|
PSQL() {
|
2021-10-30 12:58:21 +00:00
|
|
|
${config.services.postgresql.package}/bin/psql --port=5432 "$@"
|
2021-05-29 14:13:20 +00:00
|
|
|
}
|
2021-10-30 12:58:21 +00:00
|
|
|
# check if the database already exists
|
|
|
|
if ! PSQL -lqt | ${pkgs.coreutils}/bin/cut -d \| -f 1 | ${pkgs.gnugrep}/bin/grep -qw ${dbname} ; then
|
|
|
|
PSQL -tAc "CREATE ROLE plausible WITH LOGIN;"
|
|
|
|
PSQL -tAc "CREATE DATABASE ${dbname} WITH OWNER plausible;"
|
|
|
|
PSQL -d ${dbname} -tAc "CREATE EXTENSION IF NOT EXISTS citext;"
|
|
|
|
fi
|
2021-05-21 21:15:23 +00:00
|
|
|
'';
|
|
|
|
};
|
|
|
|
})
|
|
|
|
];
|
|
|
|
};
|
|
|
|
|
|
|
|
meta.maintainers = with maintainers; [ ma27 ];
|
|
|
|
meta.doc = ./plausible.xml;
|
|
|
|
}
|