mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-12-25 23:23:07 +00:00
52aad27d73
The package is pretty outdated, so I'm not sure if it'll work with PHP 8.3, let's keep it on 8.2 for now until the maintainers update it.
319 lines
10 KiB
Nix
319 lines
10 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
let
|
|
cfg = config.services.castopod;
|
|
fpm = config.services.phpfpm.pools.castopod;
|
|
|
|
user = "castopod";
|
|
|
|
# https://docs.castopod.org/getting-started/install.html#requirements
|
|
phpPackage = pkgs.php82.withExtensions ({ enabled, all }: with all; [
|
|
intl
|
|
curl
|
|
mbstring
|
|
gd
|
|
exif
|
|
mysqlnd
|
|
] ++ enabled);
|
|
in
|
|
{
|
|
meta.doc = ./castopod.md;
|
|
meta.maintainers = with lib.maintainers; [ alexoundos ];
|
|
|
|
options.services = {
|
|
castopod = {
|
|
enable = lib.mkEnableOption "Castopod, a hosting platform for podcasters";
|
|
package = lib.mkOption {
|
|
type = lib.types.package;
|
|
default = pkgs.castopod;
|
|
defaultText = lib.literalMD "pkgs.castopod";
|
|
description = "Which Castopod package to use.";
|
|
};
|
|
dataDir = lib.mkOption {
|
|
type = lib.types.path;
|
|
default = "/var/lib/castopod";
|
|
description = ''
|
|
The path where castopod stores all data. This path must be in sync
|
|
with the castopod package (where it is hardcoded during the build in
|
|
accordance with its own `dataDir` argument).
|
|
'';
|
|
};
|
|
database = {
|
|
createLocally = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = ''
|
|
Create the database and database user locally.
|
|
'';
|
|
};
|
|
hostname = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "localhost";
|
|
description = "Database hostname.";
|
|
};
|
|
name = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "castopod";
|
|
description = "Database name.";
|
|
};
|
|
user = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = user;
|
|
description = "Database user.";
|
|
};
|
|
passwordFile = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.path;
|
|
default = null;
|
|
example = "/run/keys/castopod-dbpassword";
|
|
description = ''
|
|
A file containing the password corresponding to
|
|
[](#opt-services.castopod.database.user).
|
|
|
|
This file is loaded using systemd LoadCredentials.
|
|
'';
|
|
};
|
|
};
|
|
settings = lib.mkOption {
|
|
type = with lib.types; attrsOf (oneOf [ str int bool ]);
|
|
default = { };
|
|
example = {
|
|
"email.protocol" = "smtp";
|
|
"email.SMTPHost" = "localhost";
|
|
"email.SMTPUser" = "myuser";
|
|
"email.fromEmail" = "castopod@example.com";
|
|
};
|
|
description = ''
|
|
Environment variables used for Castopod.
|
|
See [](https://code.castopod.org/adaures/castopod/-/blob/main/.env.example)
|
|
for available environment variables.
|
|
'';
|
|
};
|
|
environmentFile = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.path;
|
|
default = null;
|
|
example = "/run/keys/castopod-env";
|
|
description = ''
|
|
Environment file to inject e.g. secrets into the configuration.
|
|
See [](https://code.castopod.org/adaures/castopod/-/blob/main/.env.example)
|
|
for available environment variables.
|
|
|
|
This file is loaded using systemd LoadCredentials.
|
|
'';
|
|
};
|
|
configureNginx = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = "Configure nginx as a reverse proxy for CastoPod.";
|
|
};
|
|
localDomain = lib.mkOption {
|
|
type = lib.types.str;
|
|
example = "castopod.example.org";
|
|
description = "The domain serving your CastoPod instance.";
|
|
};
|
|
poolSettings = lib.mkOption {
|
|
type = with lib.types; attrsOf (oneOf [ str int 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 = ''
|
|
Options for Castopod's PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives.
|
|
'';
|
|
};
|
|
maxUploadSize = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "512M";
|
|
description = ''
|
|
Maximum supported size for a file upload in. Maximum HTTP body
|
|
size is set to this value for nginx and PHP (because castopod doesn't
|
|
support chunked uploads yet:
|
|
https://code.castopod.org/adaures/castopod/-/issues/330).
|
|
|
|
Note, that practical upload size limit is smaller. For example, with
|
|
512 MiB setting - around 500 MiB is possible.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
services.castopod.settings =
|
|
let
|
|
sslEnabled = with config.services.nginx.virtualHosts.${cfg.localDomain}; addSSL || forceSSL || onlySSL || enableACME || useACMEHost != null;
|
|
baseURL = "http${lib.optionalString sslEnabled "s"}://${cfg.localDomain}";
|
|
in
|
|
lib.mapAttrs (_: lib.mkDefault) {
|
|
"app.forceGlobalSecureRequests" = sslEnabled;
|
|
"app.baseURL" = baseURL;
|
|
|
|
"media.baseURL" = baseURL;
|
|
"media.root" = "media";
|
|
"media.storage" = cfg.dataDir;
|
|
|
|
"admin.gateway" = "admin";
|
|
"auth.gateway" = "auth";
|
|
|
|
"database.default.hostname" = cfg.database.hostname;
|
|
"database.default.database" = cfg.database.name;
|
|
"database.default.username" = cfg.database.user;
|
|
"database.default.DBPrefix" = "cp_";
|
|
|
|
"cache.handler" = "file";
|
|
};
|
|
|
|
services.phpfpm.pools.castopod = {
|
|
inherit user;
|
|
group = config.services.nginx.group;
|
|
inherit phpPackage;
|
|
phpOptions = ''
|
|
# https://code.castopod.org/adaures/castopod/-/blob/develop/docker/production/common/uploads.template.ini
|
|
file_uploads = On
|
|
memory_limit = 512M
|
|
upload_max_filesize = ${cfg.maxUploadSize}
|
|
post_max_size = ${cfg.maxUploadSize}
|
|
max_execution_time = 300
|
|
max_input_time = 300
|
|
'';
|
|
settings = {
|
|
"listen.owner" = config.services.nginx.user;
|
|
"listen.group" = config.services.nginx.group;
|
|
} // cfg.poolSettings;
|
|
};
|
|
|
|
systemd.services.castopod-setup = {
|
|
after = lib.optional config.services.mysql.enable "mysql.service";
|
|
requires = lib.optional config.services.mysql.enable "mysql.service";
|
|
wantedBy = [ "multi-user.target" ];
|
|
path = [ pkgs.openssl phpPackage ];
|
|
script =
|
|
let
|
|
envFile = "${cfg.dataDir}/.env";
|
|
media = "${cfg.settings."media.storage"}/${cfg.settings."media.root"}";
|
|
in
|
|
''
|
|
mkdir -p ${cfg.dataDir}/writable/{cache,logs,session,temp,uploads}
|
|
|
|
if [ ! -d ${lib.escapeShellArg media} ]; then
|
|
cp --no-preserve=mode,ownership -r ${cfg.package}/share/castopod/public/media ${lib.escapeShellArg media}
|
|
fi
|
|
|
|
if [ ! -f ${cfg.dataDir}/salt ]; then
|
|
openssl rand -base64 33 > ${cfg.dataDir}/salt
|
|
fi
|
|
|
|
cat <<'EOF' > ${envFile}
|
|
${lib.generators.toKeyValue { } cfg.settings}
|
|
EOF
|
|
|
|
echo "analytics.salt=$(cat ${cfg.dataDir}/salt)" >> ${envFile}
|
|
|
|
${if (cfg.database.passwordFile != null) then ''
|
|
echo "database.default.password=$(cat "$CREDENTIALS_DIRECTORY/dbpasswordfile)" >> ${envFile}
|
|
'' else ''
|
|
echo "database.default.password=" >> ${envFile}
|
|
''}
|
|
|
|
${lib.optionalString (cfg.environmentFile != null) ''
|
|
cat "$CREDENTIALS_DIRECTORY/envfile" >> ${envFile}
|
|
''}
|
|
|
|
php ${cfg.package}/share/castopod/spark castopod:database-update
|
|
'';
|
|
serviceConfig = {
|
|
StateDirectory = "castopod";
|
|
LoadCredential = lib.optional (cfg.environmentFile != null)
|
|
"envfile:${cfg.environmentFile}"
|
|
++ (lib.optional (cfg.database.passwordFile != null)
|
|
"dbpasswordfile:${cfg.database.passwordFile}");
|
|
WorkingDirectory = "${cfg.package}/share/castopod";
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
User = user;
|
|
Group = config.services.nginx.group;
|
|
ReadWritePaths = cfg.dataDir;
|
|
};
|
|
};
|
|
|
|
systemd.services.castopod-scheduled = {
|
|
after = [ "castopod-setup.service" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
path = [ phpPackage ];
|
|
script = ''
|
|
php ${cfg.package}/share/castopod/spark tasks:run
|
|
'';
|
|
serviceConfig = {
|
|
StateDirectory = "castopod";
|
|
WorkingDirectory = "${cfg.package}/share/castopod";
|
|
Type = "oneshot";
|
|
User = user;
|
|
Group = config.services.nginx.group;
|
|
ReadWritePaths = cfg.dataDir;
|
|
LogLevelMax = "notice"; # otherwise periodic tasks flood the journal
|
|
};
|
|
};
|
|
|
|
systemd.timers.castopod-scheduled = {
|
|
wantedBy = [ "timers.target" ];
|
|
timerConfig = {
|
|
OnCalendar = "*-*-* *:*:00";
|
|
Unit = "castopod-scheduled.service";
|
|
};
|
|
};
|
|
|
|
services.mysql = lib.mkIf cfg.database.createLocally {
|
|
enable = true;
|
|
package = lib.mkDefault pkgs.mariadb;
|
|
ensureDatabases = [ cfg.database.name ];
|
|
ensureUsers = [{
|
|
name = cfg.database.user;
|
|
ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
|
|
}];
|
|
};
|
|
|
|
services.nginx = lib.mkIf cfg.configureNginx {
|
|
enable = true;
|
|
virtualHosts."${cfg.localDomain}" = {
|
|
root = lib.mkForce "${cfg.package}/share/castopod/public";
|
|
|
|
extraConfig = ''
|
|
try_files $uri $uri/ /index.php?$args;
|
|
index index.php index.html;
|
|
client_max_body_size ${cfg.maxUploadSize};
|
|
'';
|
|
|
|
locations."^~ /${cfg.settings."media.root"}/" = {
|
|
root = cfg.settings."media.storage";
|
|
extraConfig = ''
|
|
add_header Access-Control-Allow-Origin "*";
|
|
expires max;
|
|
access_log off;
|
|
'';
|
|
};
|
|
|
|
locations."~ \.php$" = {
|
|
fastcgiParams = {
|
|
SERVER_NAME = "$host";
|
|
};
|
|
extraConfig = ''
|
|
fastcgi_intercept_errors on;
|
|
fastcgi_index index.php;
|
|
fastcgi_pass unix:${fpm.socket};
|
|
try_files $uri =404;
|
|
fastcgi_read_timeout 3600;
|
|
fastcgi_send_timeout 3600;
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
users.users.${user} = lib.mapAttrs (_: lib.mkDefault) {
|
|
description = "Castopod user";
|
|
isSystemUser = true;
|
|
group = config.services.nginx.group;
|
|
};
|
|
};
|
|
}
|