mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-22 06:53:01 +00:00
nixos/davis: init
This commit is contained in:
parent
afaaa840c1
commit
17d73dc9a1
@ -138,6 +138,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
|
|||||||
|
|
||||||
- [Scrutiny](https://github.com/AnalogJ/scrutiny), a S.M.A.R.T monitoring tool for hard disks with a web frontend.
|
- [Scrutiny](https://github.com/AnalogJ/scrutiny), a S.M.A.R.T monitoring tool for hard disks with a web frontend.
|
||||||
|
|
||||||
|
- [davis](https://github.com/tchapi/davis), a simple CardDav and CalDav server inspired by Baïkal. Available as [services.davis]($opt-services-davis.enable).
|
||||||
|
|
||||||
- [systemd-lock-handler](https://git.sr.ht/~whynothugo/systemd-lock-handler/), a bridge between logind D-Bus events and systemd targets. Available as [services.systemd-lock-handler.enable](#opt-services.systemd-lock-handler.enable).
|
- [systemd-lock-handler](https://git.sr.ht/~whynothugo/systemd-lock-handler/), a bridge between logind D-Bus events and systemd targets. Available as [services.systemd-lock-handler.enable](#opt-services.systemd-lock-handler.enable).
|
||||||
|
|
||||||
- [wastebin](https://github.com/matze/wastebin), a pastebin server written in rust. Available as [services.wastebin](#opt-services.wastebin.enable).
|
- [wastebin](https://github.com/matze/wastebin), a pastebin server written in rust. Available as [services.wastebin](#opt-services.wastebin.enable).
|
||||||
|
@ -1309,6 +1309,7 @@
|
|||||||
./services/web-apps/cloudlog.nix
|
./services/web-apps/cloudlog.nix
|
||||||
./services/web-apps/code-server.nix
|
./services/web-apps/code-server.nix
|
||||||
./services/web-apps/convos.nix
|
./services/web-apps/convos.nix
|
||||||
|
./services/web-apps/davis.nix
|
||||||
./services/web-apps/dex.nix
|
./services/web-apps/dex.nix
|
||||||
./services/web-apps/discourse.nix
|
./services/web-apps/discourse.nix
|
||||||
./services/web-apps/documize.nix
|
./services/web-apps/documize.nix
|
||||||
|
32
nixos/modules/services/web-apps/davis.md
Normal file
32
nixos/modules/services/web-apps/davis.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Davis {#module-services-davis}
|
||||||
|
|
||||||
|
[Davis](https://github.com/tchapi/davis/) is a caldav and carrddav server. It
|
||||||
|
has a simple, fully translatable admin interface for sabre/dav based on Symfony
|
||||||
|
5 and Bootstrap 5, initially inspired by Baïkal.
|
||||||
|
|
||||||
|
## Basic Usage {#module-services-davis-basic-usage}
|
||||||
|
|
||||||
|
At first, an application secret is needed, this can be generated with:
|
||||||
|
```ShellSession
|
||||||
|
$ cat /dev/urandom | tr -dc a-zA-Z0-9 | fold -w 48 | head -n 1
|
||||||
|
```
|
||||||
|
|
||||||
|
After that, `davis` can be deployed like this:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
services.davis = {
|
||||||
|
enable = true;
|
||||||
|
hostname = "davis.example.com";
|
||||||
|
mail = {
|
||||||
|
dsn = "smtp://username@example.com:25";
|
||||||
|
inviteFromAddress = "davis@example.com";
|
||||||
|
};
|
||||||
|
adminLogin = "admin";
|
||||||
|
adminPasswordFile = "/run/secrets/davis-admin-password";
|
||||||
|
appSecretFile = "/run/secrets/davis-app-secret";
|
||||||
|
nginx = {};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This deploys Davis using a sqlite database running out of `/var/lib/davis`.
|
554
nixos/modules/services/web-apps/davis.nix
Normal file
554
nixos/modules/services/web-apps/davis.nix
Normal file
@ -0,0 +1,554 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.davis;
|
||||||
|
db = cfg.database;
|
||||||
|
mail = cfg.mail;
|
||||||
|
|
||||||
|
mysqlLocal = db.createLocally && db.driver == "mysql";
|
||||||
|
pgsqlLocal = db.createLocally && db.driver == "postgresql";
|
||||||
|
|
||||||
|
user = cfg.user;
|
||||||
|
group = cfg.group;
|
||||||
|
|
||||||
|
isSecret = v: lib.isAttrs v && v ? _secret && (lib.isString v._secret || builtins.isPath v._secret);
|
||||||
|
davisEnvVars = lib.generators.toKeyValue {
|
||||||
|
mkKeyValue = lib.flip lib.generators.mkKeyValueDefault "=" {
|
||||||
|
mkValueString =
|
||||||
|
v:
|
||||||
|
if builtins.isInt v then
|
||||||
|
toString v
|
||||||
|
else if lib.isString v then
|
||||||
|
"\"${v}\""
|
||||||
|
else if true == v then
|
||||||
|
"true"
|
||||||
|
else if false == v then
|
||||||
|
"false"
|
||||||
|
else if null == v then
|
||||||
|
""
|
||||||
|
else if isSecret v then
|
||||||
|
if (lib.isString v._secret) then
|
||||||
|
builtins.hashString "sha256" v._secret
|
||||||
|
else
|
||||||
|
builtins.hashString "sha256" (builtins.readFile v._secret)
|
||||||
|
else
|
||||||
|
throw "unsupported type ${builtins.typeOf v}: ${(lib.generators.toPretty { }) v}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
secretPaths = lib.mapAttrsToList (_: v: v._secret) (lib.filterAttrs (_: isSecret) cfg.config);
|
||||||
|
mkSecretReplacement = file: ''
|
||||||
|
replace-secret ${
|
||||||
|
lib.escapeShellArgs [
|
||||||
|
(
|
||||||
|
if (lib.isString file) then
|
||||||
|
builtins.hashString "sha256" file
|
||||||
|
else
|
||||||
|
builtins.hashString "sha256" (builtins.readFile file)
|
||||||
|
)
|
||||||
|
file
|
||||||
|
"${cfg.dataDir}/.env.local"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths;
|
||||||
|
filteredConfig = lib.converge (lib.filterAttrsRecursive (
|
||||||
|
_: v:
|
||||||
|
!lib.elem v [
|
||||||
|
{ }
|
||||||
|
null
|
||||||
|
]
|
||||||
|
)) cfg.config;
|
||||||
|
davisEnv = pkgs.writeText "davis.env" (davisEnvVars filteredConfig);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.davis = {
|
||||||
|
enable = lib.mkEnableOption (lib.mdDoc "Davis is a caldav and carddav server");
|
||||||
|
|
||||||
|
user = lib.mkOption {
|
||||||
|
default = "davis";
|
||||||
|
description = lib.mdDoc "User davis runs as.";
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
|
||||||
|
group = lib.mkOption {
|
||||||
|
default = "davis";
|
||||||
|
description = lib.mdDoc "Group davis runs as.";
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
|
||||||
|
package = lib.mkPackageOption pkgs "davis" { };
|
||||||
|
|
||||||
|
dataDir = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
default = "/var/lib/davis";
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
Davis data directory.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
hostname = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
example = "davis.yourdomain.org";
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
Domain of the host to serve davis under. You may want to change it if you
|
||||||
|
run Davis on a different URL than davis.yourdomain.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (
|
||||||
|
lib.types.nullOr (
|
||||||
|
lib.types.either
|
||||||
|
(lib.types.oneOf [
|
||||||
|
lib.types.bool
|
||||||
|
lib.types.int
|
||||||
|
lib.types.port
|
||||||
|
lib.types.path
|
||||||
|
lib.types.str
|
||||||
|
])
|
||||||
|
(
|
||||||
|
lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
_secret = lib.mkOption {
|
||||||
|
type = lib.types.nullOr (
|
||||||
|
lib.types.oneOf [
|
||||||
|
lib.types.str
|
||||||
|
lib.types.path
|
||||||
|
]
|
||||||
|
);
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
The path to a file containing the value the
|
||||||
|
option should be set to in the final
|
||||||
|
configuration file.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
default = { };
|
||||||
|
|
||||||
|
example = '''';
|
||||||
|
description = lib.mdDoc '''';
|
||||||
|
};
|
||||||
|
|
||||||
|
adminLogin = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "root";
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
Username for the admin account.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
adminPasswordFile = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
The full path to a file that contains the admin's password. Must be
|
||||||
|
readable by the user.
|
||||||
|
'';
|
||||||
|
example = "/run/secrets/davis-admin-pass";
|
||||||
|
};
|
||||||
|
|
||||||
|
appSecretFile = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
A file containing the Symfony APP_SECRET - Its value should be a series
|
||||||
|
of characters, numbers and symbols chosen randomly and the recommended
|
||||||
|
length is around 32 characters. Can be generated with <code>cat
|
||||||
|
/dev/urandom | tr -dc a-zA-Z0-9 | fold -w 48 | head -n 1</code>.
|
||||||
|
'';
|
||||||
|
example = "/run/secrets/davis-appsecret";
|
||||||
|
};
|
||||||
|
|
||||||
|
database = {
|
||||||
|
driver = lib.mkOption {
|
||||||
|
type = lib.types.enum [
|
||||||
|
"sqlite"
|
||||||
|
"postgresql"
|
||||||
|
"mysql"
|
||||||
|
];
|
||||||
|
default = "sqlite";
|
||||||
|
description = lib.mdDoc "Database type, required in all circumstances.";
|
||||||
|
};
|
||||||
|
urlFile = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.path;
|
||||||
|
default = null;
|
||||||
|
example = "/run/secrets/davis-db-url";
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
A file containing the database connection url. If set then it
|
||||||
|
overrides all other database settings (except driver). This is
|
||||||
|
mandatory if you want to use an external database, that is when
|
||||||
|
`services.davis.database.createLocally` is `false`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
name = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = "davis";
|
||||||
|
description = lib.mdDoc "Database name, only used when the databse is created locally.";
|
||||||
|
};
|
||||||
|
createLocally = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = lib.mdDoc "Create the database and database user locally.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
mail = {
|
||||||
|
dsn = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
description = lib.mdDoc "Mail DSN for sending emails. Mutually exclusive with `services.davis.mail.dsnFile`.";
|
||||||
|
example = "smtp://username:password@example.com:25";
|
||||||
|
};
|
||||||
|
dsnFile = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/run/secrets/davis-mail-dsn";
|
||||||
|
description = lib.mdDoc "A file containing the mail DSN for sending emails. Mutually exclusive with `servies.davis.mail.dsn`.";
|
||||||
|
};
|
||||||
|
inviteFromAddress = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
description = lib.mdDoc "Email address to send invitations from.";
|
||||||
|
example = "no-reply@dav.example.com";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nginx = lib.mkOption {
|
||||||
|
type = lib.types.submodule (
|
||||||
|
lib.recursiveUpdate (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) { }
|
||||||
|
);
|
||||||
|
default = null;
|
||||||
|
example = ''
|
||||||
|
{
|
||||||
|
serverAliases = [
|
||||||
|
"dav.''${config.networking.domain}"
|
||||||
|
];
|
||||||
|
# To enable encryption and let let's encrypt take care of certificate
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
With this option, you can customize the nginx virtualHost settings.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
poolConfig = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (
|
||||||
|
lib.types.oneOf [
|
||||||
|
lib.types.str
|
||||||
|
lib.types.int
|
||||||
|
lib.types.bool
|
||||||
|
]
|
||||||
|
);
|
||||||
|
default = {
|
||||||
|
"pm" = "dynamic";
|
||||||
|
"pm.max_children" = 32;
|
||||||
|
"pm.start_servers" = 2;
|
||||||
|
"pm.min_spare_servers" = 2;
|
||||||
|
"pm.max_spare_servers" = 4;
|
||||||
|
"pm.max_requests" = 500;
|
||||||
|
};
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
Options for the davis PHP pool. See the documentation on <literal>php-fpm.conf</literal>
|
||||||
|
for details on configuration directives.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config =
|
||||||
|
let
|
||||||
|
defaultServiceConfig = {
|
||||||
|
ReadWritePaths = "${cfg.dataDir}";
|
||||||
|
User = user;
|
||||||
|
UMask = 77;
|
||||||
|
DeviceAllow = "";
|
||||||
|
LockPersonality = true;
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateDevices = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
PrivateUsers = true;
|
||||||
|
ProcSubset = "pid";
|
||||||
|
ProtectClock = true;
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
ProtectHome = true;
|
||||||
|
ProtectHostname = true;
|
||||||
|
ProtectKernelLogs = true;
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
ProtectProc = "invisible";
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
RemoveIPC = true;
|
||||||
|
RestrictNamespaces = true;
|
||||||
|
RestrictRealtime = true;
|
||||||
|
RestrictSUIDSGID = true;
|
||||||
|
SystemCallArchitectures = "native";
|
||||||
|
SystemCallFilter = [
|
||||||
|
"@system-service"
|
||||||
|
"~@resources"
|
||||||
|
"~@privileged"
|
||||||
|
];
|
||||||
|
WorkingDirectory = "${cfg.package}/";
|
||||||
|
};
|
||||||
|
in
|
||||||
|
lib.mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = db.createLocally -> db.urlFile == null;
|
||||||
|
message = "services.davis.database.urlFile must be unset if services.davis.database.createLocally is set true.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = db.createLocally || db.urlFile != null;
|
||||||
|
message = "One of services.davis.database.urlFile or services.davis.database.createLocally must be set.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = (mail.dsn != null) != (mail.dsnFile != null);
|
||||||
|
message = "One of (and only one of) services.davis.mail.dsn or services.davis.mail.dsnFile must be set.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
services.davis.config =
|
||||||
|
{
|
||||||
|
APP_ENV = "prod";
|
||||||
|
CACHE_DIR = "${cfg.dataDir}/var/cache";
|
||||||
|
# note: we do not need the log dir (we log to stdout/journald), by davis/symfony will try to create it, and the default value is one in the nix-store
|
||||||
|
# so we set it to a path under dataDir to avoid something like: Unable to create the "logs" directory (/nix/store/5cfskz0ybbx37s1161gjn5klwb5si1zg-davis-4.4.1/var/log).
|
||||||
|
LOG_DIR = "${cfg.dataDir}/var/log";
|
||||||
|
LOG_FILE_PATH = "/dev/stdout";
|
||||||
|
DATABASE_DRIVER = db.driver;
|
||||||
|
INVITE_FROM_ADDRESS = mail.inviteFromAddress;
|
||||||
|
APP_SECRET._secret = cfg.appSecretFile;
|
||||||
|
ADMIN_LOGIN = cfg.adminLogin;
|
||||||
|
ADMIN_PASSWORD._secret = cfg.adminPasswordFile;
|
||||||
|
APP_TIMEZONE = config.time.timeZone;
|
||||||
|
WEBDAV_ENABLED = false;
|
||||||
|
CALDAV_ENABLED = true;
|
||||||
|
CARDDAV_ENABLED = true;
|
||||||
|
}
|
||||||
|
// (if mail.dsn != null then { MAILER_DSN = mail.dsn; } else { MAILER_DSN._secret = mail.dsnFile; })
|
||||||
|
// (
|
||||||
|
if db.createLocally then
|
||||||
|
{
|
||||||
|
DATABASE_URL =
|
||||||
|
if db.driver == "sqlite" then
|
||||||
|
"sqlite:///${cfg.dataDir}/davis.db" # note: sqlite needs 4 slashes for an absolute path
|
||||||
|
else if
|
||||||
|
pgsqlLocal
|
||||||
|
# note: davis expects a non-standard postgres uri (due to the underlying doctrine library)
|
||||||
|
# specifically the charset query parameter, and the dummy hostname which is overriden by the host query parameter
|
||||||
|
then
|
||||||
|
"postgres://${user}@localhost/${db.name}?host=/run/postgresql&charset=UTF-8"
|
||||||
|
else if mysqlLocal then
|
||||||
|
"mysql://${user}@localhost/${db.name}?socket=/run/mysqld/mysqld.sock"
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ DATABASE_URL._secret = db.urlFile; }
|
||||||
|
);
|
||||||
|
|
||||||
|
users = {
|
||||||
|
users = lib.mkIf (user == "davis") {
|
||||||
|
davis = {
|
||||||
|
description = "Davis service user";
|
||||||
|
group = cfg.group;
|
||||||
|
isSystemUser = true;
|
||||||
|
home = cfg.dataDir;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
groups = lib.mkIf (group == "davis") { davis = { }; };
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d ${cfg.dataDir} 0710 ${user} ${group} - -"
|
||||||
|
"d ${cfg.dataDir}/var 0700 ${user} ${group} - -"
|
||||||
|
"d ${cfg.dataDir}/var/log 0700 ${user} ${group} - -"
|
||||||
|
"d ${cfg.dataDir}/var/cache 0700 ${user} ${group} - -"
|
||||||
|
];
|
||||||
|
|
||||||
|
services.phpfpm.pools.davis = {
|
||||||
|
inherit user group;
|
||||||
|
phpOptions = ''
|
||||||
|
log_errors = on
|
||||||
|
'';
|
||||||
|
phpEnv = {
|
||||||
|
ENV_DIR = "${cfg.dataDir}";
|
||||||
|
CACHE_DIR = "${cfg.dataDir}/var/cache";
|
||||||
|
#LOG_DIR = "${cfg.dataDir}/var/log";
|
||||||
|
};
|
||||||
|
settings =
|
||||||
|
{
|
||||||
|
"listen.mode" = "0660";
|
||||||
|
"pm" = "dynamic";
|
||||||
|
"pm.max_children" = 256;
|
||||||
|
"pm.start_servers" = 10;
|
||||||
|
"pm.min_spare_servers" = 5;
|
||||||
|
"pm.max_spare_servers" = 20;
|
||||||
|
}
|
||||||
|
// (
|
||||||
|
if cfg.nginx != null then
|
||||||
|
{
|
||||||
|
"listen.owner" = config.services.nginx.user;
|
||||||
|
"listen.group" = config.services.nginx.group;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ }
|
||||||
|
)
|
||||||
|
// cfg.poolConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Reading the user-provided secret files requires root access
|
||||||
|
systemd.services.davis-env-setup = {
|
||||||
|
description = "Setup davis environment";
|
||||||
|
before = [
|
||||||
|
"phpfpm-davis.service"
|
||||||
|
"davis-db-migrate.service"
|
||||||
|
];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
};
|
||||||
|
path = [ pkgs.replace-secret ];
|
||||||
|
restartTriggers = [
|
||||||
|
cfg.package
|
||||||
|
davisEnv
|
||||||
|
];
|
||||||
|
script = ''
|
||||||
|
# error handling
|
||||||
|
set -euo pipefail
|
||||||
|
# create .env file with the upstream values
|
||||||
|
install -T -m 0600 -o ${user} ${cfg.package}/env-upstream "${cfg.dataDir}/.env"
|
||||||
|
# create .env.local file with the user-provided values
|
||||||
|
install -T -m 0600 -o ${user} ${davisEnv} "${cfg.dataDir}/.env.local"
|
||||||
|
${secretReplacements}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.davis-db-migrate = {
|
||||||
|
description = "Migrate davis database";
|
||||||
|
before = [ "phpfpm-davis.service" ];
|
||||||
|
after =
|
||||||
|
lib.optional mysqlLocal "mysql.service"
|
||||||
|
++ lib.optional pgsqlLocal "postgresql.service"
|
||||||
|
++ [ "davis-env-setup.service" ];
|
||||||
|
requires =
|
||||||
|
lib.optional mysqlLocal "mysql.service"
|
||||||
|
++ lib.optional pgsqlLocal "postgresql.service"
|
||||||
|
++ [ "davis-env-setup.service" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = defaultServiceConfig // {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
Environment = [
|
||||||
|
"ENV_DIR=${cfg.dataDir}"
|
||||||
|
"CACHE_DIR=${cfg.dataDir}/var/cache"
|
||||||
|
"LOG_DIR=${cfg.dataDir}/var/log"
|
||||||
|
];
|
||||||
|
EnvironmentFile = "${cfg.dataDir}/.env.local";
|
||||||
|
};
|
||||||
|
restartTriggers = [
|
||||||
|
cfg.package
|
||||||
|
davisEnv
|
||||||
|
];
|
||||||
|
script = ''
|
||||||
|
set -euo pipefail
|
||||||
|
${cfg.package}/bin/console cache:clear --no-debug
|
||||||
|
${cfg.package}/bin/console cache:warmup --no-debug
|
||||||
|
${cfg.package}/bin/console doctrine:migrations:migrate
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.phpfpm-davis.after = [
|
||||||
|
"davis-env-setup.service"
|
||||||
|
"davis-db-migrate.service"
|
||||||
|
];
|
||||||
|
systemd.services.phpfpm-davis.requires = [
|
||||||
|
"davis-env-setup.service"
|
||||||
|
"davis-db-migrate.service"
|
||||||
|
] ++ lib.optional mysqlLocal "mysql.service" ++ lib.optional pgsqlLocal "postgresql.service";
|
||||||
|
systemd.services.phpfpm-davis.serviceConfig.ReadWritePaths = [ cfg.dataDir ];
|
||||||
|
|
||||||
|
services.nginx = lib.mkIf (cfg.nginx != null) {
|
||||||
|
enable = lib.mkDefault true;
|
||||||
|
virtualHosts = {
|
||||||
|
"${cfg.hostname}" = lib.mkMerge [
|
||||||
|
cfg.nginx
|
||||||
|
{
|
||||||
|
root = lib.mkForce "${cfg.package}/public";
|
||||||
|
extraConfig = ''
|
||||||
|
charset utf-8;
|
||||||
|
index index.php;
|
||||||
|
'';
|
||||||
|
locations = {
|
||||||
|
"/" = {
|
||||||
|
extraConfig = ''
|
||||||
|
try_files $uri $uri/ /index.php$is_args$args;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~* ^/.well-known/(caldav|carddav)$" = {
|
||||||
|
extraConfig = ''
|
||||||
|
return 302 $http_x_forwarded_proto://$host/dav/;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~ ^(.+\.php)(.*)$" = {
|
||||||
|
extraConfig = ''
|
||||||
|
try_files $fastcgi_script_name =404;
|
||||||
|
include ${config.services.nginx.package}/conf/fastcgi_params;
|
||||||
|
include ${config.services.nginx.package}/conf/fastcgi.conf;
|
||||||
|
fastcgi_pass unix:${config.services.phpfpm.pools.davis.socket};
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(.*)$;
|
||||||
|
fastcgi_param X-Forwarded-Proto $http_x_forwarded_proto;
|
||||||
|
fastcgi_param X-Forwarded-Port $http_x_forwarded_port;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~ /(\\.ht)" = {
|
||||||
|
extraConfig = ''
|
||||||
|
deny all;
|
||||||
|
return 404;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.mysql = lib.mkIf mysqlLocal {
|
||||||
|
enable = true;
|
||||||
|
package = lib.mkDefault pkgs.mariadb;
|
||||||
|
ensureDatabases = [ db.name ];
|
||||||
|
ensureUsers = [
|
||||||
|
{
|
||||||
|
name = user;
|
||||||
|
ensurePermissions = {
|
||||||
|
"${db.name}.*" = "ALL PRIVILEGES";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.postgresql = lib.mkIf pgsqlLocal {
|
||||||
|
enable = true;
|
||||||
|
ensureDatabases = [ db.name ];
|
||||||
|
ensureUsers = [
|
||||||
|
{
|
||||||
|
name = user;
|
||||||
|
ensureDBOwnership = true;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
doc = ./davis.md;
|
||||||
|
maintainers = pkgs.davis.meta.maintainers;
|
||||||
|
};
|
||||||
|
}
|
@ -233,6 +233,7 @@ in {
|
|||||||
croc = handleTest ./croc.nix {};
|
croc = handleTest ./croc.nix {};
|
||||||
darling = handleTest ./darling.nix {};
|
darling = handleTest ./darling.nix {};
|
||||||
dae = handleTest ./dae.nix {};
|
dae = handleTest ./dae.nix {};
|
||||||
|
davis = handleTest ./davis.nix {};
|
||||||
dconf = handleTest ./dconf.nix {};
|
dconf = handleTest ./dconf.nix {};
|
||||||
deconz = handleTest ./deconz.nix {};
|
deconz = handleTest ./deconz.nix {};
|
||||||
deepin = handleTest ./deepin.nix {};
|
deepin = handleTest ./deepin.nix {};
|
||||||
|
59
nixos/tests/davis.nix
Normal file
59
nixos/tests/davis.nix
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import ./make-test-python.nix (
|
||||||
|
{ lib, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
name = "davis";
|
||||||
|
|
||||||
|
meta.maintainers = pkgs.davis.meta.maintainers;
|
||||||
|
|
||||||
|
nodes.machine =
|
||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
virtualisation = {
|
||||||
|
memorySize = 512;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.davis = {
|
||||||
|
enable = true;
|
||||||
|
hostname = "davis.example.com";
|
||||||
|
database = {
|
||||||
|
driver = "postgresql";
|
||||||
|
};
|
||||||
|
mail = {
|
||||||
|
dsnFile = "${pkgs.writeText "davisMailDns" "smtp://username:password@example.com:25"}";
|
||||||
|
inviteFromAddress = "dav@example.com";
|
||||||
|
};
|
||||||
|
adminLogin = "admin";
|
||||||
|
appSecretFile = "${pkgs.writeText "davisAppSecret" "52882ef142066e09ab99ce816ba72522e789505caba224"}";
|
||||||
|
adminPasswordFile = "${pkgs.writeText "davisAdminPass" "nixos"}";
|
||||||
|
nginx = { };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = ''
|
||||||
|
start_all()
|
||||||
|
machine.wait_for_unit("postgresql.service")
|
||||||
|
machine.wait_for_unit("davis-env-setup.service")
|
||||||
|
machine.wait_for_unit("davis-db-migrate.service")
|
||||||
|
machine.wait_for_unit("nginx.service")
|
||||||
|
machine.wait_for_unit("phpfpm-davis.service")
|
||||||
|
|
||||||
|
with subtest("welcome screen loads"):
|
||||||
|
machine.succeed(
|
||||||
|
"curl -sSfL --resolve davis.example.com:80:127.0.0.1 http://davis.example.com/ | grep '<title>Davis</title>'"
|
||||||
|
)
|
||||||
|
|
||||||
|
with subtest("login works"):
|
||||||
|
csrf_token = machine.succeed(
|
||||||
|
"curl -c /tmp/cookies -sSfL --resolve davis.example.com:80:127.0.0.1 http://davis.example.com/login | grep '_csrf_token' | sed -E 's,.*value=\"(.*)\".*,\\1,g'"
|
||||||
|
)
|
||||||
|
r = machine.succeed(
|
||||||
|
f"curl -b /tmp/cookies --resolve davis.example.com:80:127.0.0.1 http://davis.example.com/login -X POST -F username=admin -F password=nixos -F _csrf_token={csrf_token.strip()} -D headers"
|
||||||
|
)
|
||||||
|
print(r)
|
||||||
|
machine.succeed(
|
||||||
|
"[[ $(grep -i 'location: ' headers | cut -d: -f2- | xargs echo) == /dashboard* ]]"
|
||||||
|
)
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user