plausible: init at 1.3.0

launchpad/nixpkgs/master
Maximilian Bosch 3 years ago committed by Raphael Megzari
parent fea2b25c74
commit b06ea1146c
  1. 1
      nixos/modules/module-list.nix
  2. 270
      nixos/modules/services/web-apps/plausible.nix
  3. 51
      nixos/modules/services/web-apps/plausible.xml
  4. 1
      nixos/tests/all-tests.nix
  5. 46
      nixos/tests/plausible.nix
  6. 85
      pkgs/servers/web-apps/plausible/default.nix
  7. 57
      pkgs/servers/web-apps/plausible/package.json
  8. 68
      pkgs/servers/web-apps/plausible/update.sh
  9. 7732
      pkgs/servers/web-apps/plausible/yarn.lock
  10. 8278
      pkgs/servers/web-apps/plausible/yarn.nix
  11. 2
      pkgs/top-level/all-packages.nix

@ -947,6 +947,7 @@
./services/web-apps/nextcloud.nix
./services/web-apps/nexus.nix
./services/web-apps/plantuml-server.nix
./services/web-apps/plausible.nix
./services/web-apps/pgpkeyserver-lite.nix
./services/web-apps/matomo.nix
./services/web-apps/moinmoin.nix

@ -0,0 +1,270 @@
{ lib, pkgs, config, ... }:
with lib;
let
cfg = config.services.plausible;
envSecrets = ''
export ADMIN_USER_PWD="$(<${cfg.adminUser.passwordFile})"
export SECRET_KEY_BASE="$(<${cfg.server.secretKeybaseFile})"
export RELEASE_TMP=/var/lib/plausible/tmp
${optionalString (cfg.mail.smtp.passwordFile != null) ''
export SMTP_USER_PWD="$(<${cfg.mail.smtp.passwordFile})"
''}
'';
in {
options.services.plausible = {
enable = mkEnableOption "plausible";
adminUser = {
name = mkOption {
default = "admin";
type = types.str;
description = ''
Name of the admin user that plausible will created on initial startup.
'';
};
email = mkOption {
type = types.str;
example = "admin@localhost";
description = ''
Email-address of the admin-user.
'';
};
passwordFile = mkOption {
type = types.either types.str types.path;
description = ''
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 = ''
The URL to be used to connect to <package>postgres</package>. The format
is described in <link xlink:href="https://hexdocs.pm/ecto/Ecto.Repo.html#module-urls">
the elixir docs</link>.
'';
};
};
postgres = {
setup = mkEnableOption "creating a postgresql instance" // { default = true; };
dbname = mkOption {
default = "plausible";
type = types.str;
description = ''
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;
description = ''
Whether to prohibit creating an account in plausible's UI.
'';
};
secretKeybaseFile = mkOption {
type = types.either types.path types.str;
description = ''
Path to the secret used by the <literal>phoenix</literal>-framework. Instructions
how to generate one are documented in the
<link xlink:href="https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Secret.html#content">
framework docs</link>.
'';
};
port = mkOption {
default = 8000;
type = types.port;
description = ''
Port where the service should be available.
'';
};
baseUrl = mkOption {
type = types.str;
description = ''
Public URL where plausible is available.
'';
};
};
mail = {
email = mkOption {
default = " hello@plausible.local";
type = types.str;
description = ''
The email id to use for as <emphasis>from</emphasis> address of all communications
from Plausible.
'';
};
smtp = {
hostAddr = mkOption {
default = "localhost";
type = types.str;
description = ''
The host address of your smtp server.
'';
};
hostPort = mkOption {
default = 25;
type = types.port;
description = ''
The port of your smtp server.
'';
};
user = mkOption {
default = null;
type = types.nullOr types.str;
description = ''
The username/email in case SMTP auth is enabled.
'';
};
passwordFile = mkOption {
default = null;
type = with types; nullOr (either str path);
description = ''
The path to the file with the password in case SMTP auth is enabled.
'';
};
enableSSL = mkEnableOption "";
retries = mkOption {
type = types.ints.unsigned;
default = 2;
description = ''
Number of retries to make until mailer gives up.
'';
};
};
};
};
config = mkIf cfg.enable {
assertions = [
{ assertion = cfg.adminUser.activate -> cfg.database.postgres.setup;
message = ''
Unable to automatically activate the admin-user if no local DB-managed for
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;
};
systemd.services = mkMerge [
{
plausible = {
inherit (pkgs.plausible.meta) description;
documentation = [ "https://plausible.io/docs/self-hosting" ];
wantedBy = [ "multi-user.target" ]
++ optional cfg.database.clickhouse.setup "clickhouse.service"
++ optional cfg.database.postgres.setup "postgresql.service";
after = optional cfg.database.postgres.setup "plausible-postgres.service";
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
PLAUSIBLE_TZDATA = "/var/lib/plausible/elixir_tzdata";
# Configuration options from
# https://plausible.io/docs/self-hosting-configuration
PORT = toString cfg.server.port;
DISABLE_REGISTRATION = boolToString cfg.server.disableRegistration;
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;
${if cfg.mail.smtp.user != null then "SMTP_USER_NAME" else null} = cfg.mail.smtp.user;
};
path = [ pkgs.plausible ]
++ optional cfg.database.postgres.setup config.services.postgresql.package;
serviceConfig = {
DynamicUser = true;
PrivateTmp = true;
WorkingDirectory = "/var/lib/plausible";
StateDirectory = "plausible";
ExecStartPre = "@${pkgs.writeShellScript "plausible-setup" ''
${envSecrets}
${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
''}
''} plausible-setup";
ExecStart = "@${pkgs.writeShellScript "plausible" ''
${envSecrets}
plausible start
''} plausible";
};
};
}
(mkIf cfg.database.postgres.setup {
# Unfortunately `plausible' requires super-user permissions in postgresql, so this
# has to be done imperatively here.
plausible-postgres = {
after = [ "postgresql.service" ];
bindsTo = [ "postgresql.service" ];
requiredBy = [ "plausible.service" ];
partOf = [ "plausible.service" ];
serviceConfig.Type = "oneshot";
script = ''
if [ ! -e /var/lib/plausible/.db-setup ]; then
mkdir -p /var/lib/plausible/
PSQL() {
/run/wrappers/bin/sudo -Hu postgres ${config.services.postgresql.package}/bin/psql --port=5432 "$@"
}
PSQL -tAc "CREATE EXTENSION IF NOT EXISTS citext;"
PSQL -tAc "CREATE ROLE plausible WITH LOGIN;"
PSQL -tAc "CREATE DATABASE plausible WITH OWNER plausible;"
PSQL -tAc "ALTER USER plausible WITH SUPERUSER;"
touch /var/lib/plausible/.db-setup
fi
'';
};
})
];
};
meta.maintainers = with maintainers; [ ma27 ];
meta.doc = ./plausible.xml;
}

@ -0,0 +1,51 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="module-services-plausible">
<title>Plausible</title>
<para>
<link xlink:href="https://plausible.io/">Plausible</link> is a privacy-friendly alternative to
Google analytics.
</para>
<section xml:id="module-services-plausible-basic-usage">
<title>Basic Usage</title>
<para>
At first, a secret key is needed to be generated. This can be done with e.g.
<prompt>$ openssl rand -base64 64</prompt>
</para>
<para>
After that, <package>plausible</package> can be deployed like this:
<programlisting>{
services.plausible = {
<link linkend="opt-services.plausible.enable">enable</link> = true;
adminUser = {
<link linkend="opt-services.plausible.adminUser.activate">activate</link> = true; <co xml:id='ex-plausible-cfg-activate' />
<link linkend="opt-services.plausible.adminUser.email">email</link> = "admin@localhost";
<link linkend="opt-services.plausible.adminUser.passwordFile">passwordFile</link> = "/run/secrets/plausible-admin-pwd";
};
server = {
<link linkend="opt-services.plausible.server.baseUrl">baseUrl</link> = "http://analytics.example.org";
<link linkend="opt-services.plausible.server.secretKeybaseFile">secretKeybaseFile</link> = "/run/secrets/plausible-secret-key-base"; <co xml:id='ex-plausible-cfg-secretbase' />
};
};
}</programlisting>
<calloutlist>
<callout arearefs='ex-plausible-cfg-activate'>
<para>
<varname>activate</varname> is used to skip the email verification of the admin-user that's
automatically created by <package>plausible</package>. This is only supported if
<package>postgresql</package> is configured by the module. This is done by default, but
can be turned off with <xref linkend="opt-services.plausible.database.postgres.setup" />.
</para>
</callout>
<callout arearefs='ex-plausible-cfg-secretbase'>
<para>
<varname>secretKeybaseFile</varname> is a path to the file which contains the secret generated
with <package>openssl</package> as described above.
</para>
</callout>
</calloutlist>
</para>
</section>
</chapter>

@ -330,6 +330,7 @@ in
php80 = handleTest ./php { php = pkgs.php80; };
pinnwand = handleTest ./pinnwand.nix {};
plasma5 = handleTest ./plasma5.nix {};
plausible = handleTest ./plausible.nix {};
pleroma = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./pleroma.nix {};
plikd = handleTest ./plikd.nix {};
plotinus = handleTest ./plotinus.nix {};

@ -0,0 +1,46 @@
import ./make-test-python.nix ({ pkgs, lib, ... }: {
name = "plausible";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ ma27 ];
};
machine = { pkgs, ... }: {
virtualisation.memorySize = 4096;
services.plausible = {
enable = true;
adminUser = {
email = "admin@example.org";
passwordFile = "${pkgs.writeText "pwd" "foobar"}";
activate = true;
};
server = {
baseUrl = "http://localhost:8000";
secretKeybaseFile = "${pkgs.writeText "dont-try-this-at-home" "nannannannannannannannannannannannannannannannannannannan_batman!"}";
};
};
};
testScript = ''
start_all()
machine.wait_for_unit("plausible.service")
machine.wait_for_open_port(8000)
machine.succeed("curl -f localhost:8000 >&2")
csrf_token = machine.succeed(
"curl -c /tmp/cookies localhost:8000/login | grep '_csrf_token' | sed -E 's,.*value=\"(.*)\".*,\\1,g'"
)
machine.succeed(
f"curl -b /tmp/cookies -f -X POST localhost:8000/login -F email=admin@example.org -F password=foobar -F _csrf_token={csrf_token.strip()} -D headers"
)
# By ensuring that the user is redirected to the dashboard after login, we
# also make sure that the automatic verification of the module works.
machine.succeed(
"[[ $(grep 'location: ' headers | cut -d: -f2- | xargs echo) == /sites* ]]"
)
machine.shutdown()
'';
})

@ -0,0 +1,85 @@
{ lib, stdenv, beamPackages, fetchFromGitHub, glibcLocales, cacert
, mkYarnModules, nodejs, fetchpatch, nixosTests
}:
let
pname = "plausible";
version = "1.3.0";
name = "${pname}-${version}";
src = fetchFromGitHub {
owner = "plausible";
repo = "analytics";
rev = "v${version}";
sha256 = "03lm1f29gwwixnhgjish5bhi3m73qyp71ns2sczdnwnbhrw61zps";
};
mixDeps = beamPackages.fetchMixDeps {
pname = "${pname}-deps";
inherit src version;
sha256 = "sha256-66zSdYmis3UnbdLkPi649RbPbMPI5gVeFlaMekOy5CQ=";
};
yarnDeps = mkYarnModules {
pname = "${pname}-yarn-deps";
inherit version;
packageJSON = ./package.json;
yarnNix = ./yarn.nix;
yarnLock = ./yarn.lock;
preBuild = ''
mkdir -p tmp/deps
cp -r ${mixDeps}/phoenix tmp/deps/phoenix
cp -r ${mixDeps}/phoenix_html tmp/deps/phoenix_html
'';
postBuild = ''
echo 'module.exports = {}' > $out/node_modules/flatpickr/dist/postcss.config.js
'';
};
in beamPackages.mixRelease {
inherit pname version src mixDeps;
nativeBuildInputs = [ nodejs ];
patches = [
# Allow socket-authentication against postgresql. Upstream PR is
# https://github.com/plausible/analytics/pull/1052
(fetchpatch {
url = "https://github.com/Ma27/analytics/commit/f2ee5892a6c3e1a861d69ed30cac43e05e9cd36f.patch";
sha256 = "sha256-JvJ7xlGw+tHtWje+jiQChVC4KTyqqdq2q+MIcOv/k1o=";
})
];
passthru = {
tests = { inherit (nixosTests) plausible; };
updateScript = ./update.sh;
};
postPatch = ''
# Without this modification, tzdata tries to write in its store-path:
# https://github.com/lau/tzdata#data-directory-and-releases
echo 'config :tzdata, :data_dir, (System.get_env("PLAUSIBLE_TZDATA") || "/tmp/plausible_tzdata")' \
>> config/config.exs
'';
buildPhase = ''
runHook preBuild
mkdir -p $out
ln -sf ${yarnDeps}/node_modules assets/node_modules
mix deps.compile --path $out --no-deps-check
mix compile --no-deps-check --path $out
npm run deploy --prefix ./assets
mix release plausible --no-deps-check --path $out
runHook postBuild
'';
meta = with lib; {
license = licenses.agpl3Plus;
homepage = "https://plausible.io/";
description = " Simple, open-source, lightweight (< 1 KB) and privacy-friendly web analytics alternative to Google Analytics.";
maintainers = with maintainers; [ ma27 ];
platforms = platforms.linux;
};
}

@ -0,0 +1,57 @@
{
"repository": {},
"license": "MIT",
"scripts": {
"deploy": "$(npm bin)/webpack --mode production",
"watch": "$(npm bin)/webpack --mode development --watch"
},
"dependencies": {
"@babel/core": "^7.11.1",
"@babel/preset-env": "^7.11.0",
"@babel/preset-react": "^7.10.4",
"@tailwindcss/aspect-ratio": "^0.2.0",
"@tailwindcss/forms": "^0.2.1",
"@tailwindcss/typography": "^0.3.1",
"abortcontroller-polyfill": "^1.5.0",
"alpinejs": "^2.7.3",
"autoprefixer": "^9.8.6",
"babel-loader": "^8.1.0",
"chart.js": "^2.9.3",
"copy-webpack-plugin": "^6.0.3",
"css-loader": "^3.6.0",
"datamaps": "^0.5.9",
"iframe-resizer": "^4.3.1",
"mini-css-extract-plugin": "^0.8.2",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"phoenix": "file:../../tmp/deps/phoenix",
"phoenix_html": "file:../../tmp/deps/phoenix_html",
"postcss": "^7.0.35",
"postcss-loader": "^4.0.4",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-flatpickr": "^3.10.5",
"react-flip-move": "^3.0.4",
"react-router-dom": "^5.2.0",
"react-transition-group": "^4.4.1",
"tailwindcss": "2.0.1-compat",
"terser-webpack-plugin": "^4.2.3",
"url-search-params-polyfill": "^8.0.0",
"webpack": "4.39.2",
"webpack-cli": "^3.3.12"
},
"devDependencies": {
"eslint": "^7.2.0",
"eslint-config-airbnb": "^18.2.0",
"eslint-config-prettier": "^7.0.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-prettier": "^3.3.0",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "^4.2.0",
"stylelint": "^13.8.0",
"stylelint-config-prettier": "^8.0.2",
"stylelint-config-standard": "^20.0.0"
},
"name": "plausible",
"version": "v1.3.0"
}

@ -0,0 +1,68 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p jq nix-prefetch-git yarn yarn2nix-moretea.yarn2nix moreutils
# NOTE: please check on new releases which steps aren't necessary anymore!
# Currently the following things are done:
#
# * Add correct `name`/`version` field to `package.json`, otherwise `yarn2nix` fails to
# find required dependencies.
# * Keep `tailwindcss` on version 2.0.1-compat (on `yarn` it will be upgraded due to the `^`).
# This is needed to make sure the entire build still works with `postcss-7` (needed
# by plausible).
# * Adjust `file:`-dependencies a bit for the structure inside a Nix build.
# * Update hashes for the tarball & the fixed-output drv with all `mix`-dependencies.
# * Generate `yarn.lock` & `yarn.nix` in a temporary directory.
set -x
dir="$(realpath $(dirname "$0"))"
latest="$(curl -q https://api.github.com/repos/plausible/analytics/releases/latest \
| jq -r '.tag_name')"
nix_version="$(cut -c2- <<< "$latest")"
if [ "$(nix-instantiate -A plausible.version --eval | xargs echo)" = "$nix_version" ];
then
echo "Already using version $latest, skipping"
exit 0
fi
SRC="https://raw.githubusercontent.com/plausible/analytics/${latest}"
package_json="$(curl -qf "$SRC/assets/package.json")"
fixed_tailwind_version="$(jq '.dependencies.tailwindcss' -r <<< "$package_json" | sed -e 's,^^,,g')"
echo "$package_json" \
| jq '. + {"name":"plausible","version":"'$latest'"}' \
| jq '.dependencies.tailwindcss = "'"$fixed_tailwind_version"'"' \
| sed -e 's,../deps/,../../tmp/deps/,g' \
> $dir/package.json
tarball_meta="$(nix-prefetch-git https://github.com/plausible/analytics --rev "$latest" --quiet)"
tarball_hash="$(jq -r '.sha256' <<< "$tarball_meta")"
tarball_path="$(jq -r '.path' <<< "$tarball_meta")"
fake_hash="$(nix-instantiate --eval -A lib.fakeSha256 | xargs echo)"
sed -i "$dir/default.nix" \
-e 's,version = ".*",version = "'"$nix_version"'",' \
-e '/^ src = fetchFromGitHub/,+4{;s/sha256 = "\(.*\)"/sha256 = "'"$tarball_hash"'"/}' \
-e '/^ mixDeps =/,+3{;s/sha256 = "\(.*\)"/sha256 = "'"$fake_hash"'"/}'
mix_hash="$(nix-build -A plausible.mixDeps 2>&1 | tail -n3 | grep 'got:' | cut -d: -f2- | xargs echo)"
sed -i "$dir/default.nix" -e '/^ mixDeps =/,+3{;s/sha256 = "\(.*\)"/sha256 = "'"$mix_hash"'"/}'
tmp_setup_dir="$(mktemp -d)"
trap "rm -rf $tmp_setup_dir" EXIT
cp -r $tarball_path/* $tmp_setup_dir/
cp -r "$(nix-build -A plausible.mixDeps)" "$tmp_setup_dir/deps"
chmod -R a+rwx "$tmp_setup_dir"
pushd $tmp_setup_dir/assets
jq < package.json '.dependencies.tailwindcss = "'"$fixed_tailwind_version"'"' | sponge package.json
yarn
yarn2nix > "$dir/yarn.nix"
cp yarn.lock "$dir/yarn.lock"
popd
nix-build -A plausible

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1655,6 +1655,8 @@ in
play-with-mpv = callPackage ../tools/video/play-with-mpv { };
plausible = callPackage ../servers/web-apps/plausible { };
reattach-to-user-namespace = callPackage ../os-specific/darwin/reattach-to-user-namespace {};
skhd = callPackage ../os-specific/darwin/skhd {

Loading…
Cancel
Save