diff --git a/machines/web01/castopod-head-proxy.nix b/machines/web01/castopod-head-proxy.nix new file mode 100644 index 0000000..931af0e --- /dev/null +++ b/machines/web01/castopod-head-proxy.nix @@ -0,0 +1,33 @@ +{ config, lib, ... }: +let + cfg = config.services.castopod; + fpm = config.services.phpfpm.pools.castopod; +in + { + services.nginx = { + resolver.addresses = [ "127.0.0.53" ]; + virtualHosts."${cfg.localDomain}" = { + + locations."@force_get" = { + extraConfig = lib.mkForce '' + recursive_error_pages on; + proxy_method GET; + proxy_pass https://podcasts.dgnum.eu/$request_uri; + ''; + }; + + locations."~ \.php$" = { + extraConfig = lib.mkForce '' + error_page 550 = @force_get; + if ($request_method = HEAD) { return 550; } + 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; + ''; + }; + }; + }; + } diff --git a/machines/web01/castopod.nix b/machines/web01/castopod.nix index a71ecc6..4a63f5c 100644 --- a/machines/web01/castopod.nix +++ b/machines/web01/castopod.nix @@ -3,12 +3,14 @@ let host = "podcasts.dgnum.eu"; in { - # Notes: - # le paramètre analytics.salt est créé par le service + imports = [ + ./castopod-head-proxy.nix + ]; services.castopod = { enable = true; localDomain = host; environmentFile = config.age.secrets.castopod-environment_file.path; + maxUploadSize = 512; settings = { "email.fromEmail"="noreply@infra.dgnum.eu"; "email.SMTPHost"="kurisu.lahfa.xyz"; diff --git a/machines/web01/plausible.nix b/machines/web01/plausible.nix index fcb624a..48188fc 100644 --- a/machines/web01/plausible.nix +++ b/machines/web01/plausible.nix @@ -30,8 +30,6 @@ in secretKeybaseFile = config.age.secrets."plausible_secret-key-base-file".path; }; - releaseCookiePath = config.age.secrets."plausible_release-cookie-file".path; - adminUser = { passwordFile = config.age.secrets."plausible_admin-user-password-file".path; email = "tom.hubrecht@dgnum.eu"; diff --git a/meta/nodes.nix b/meta/nodes.nix index 793f82d..56d8478 100644 --- a/meta/nodes.nix +++ b/meta/nodes.nix @@ -23,11 +23,8 @@ in builtins.mapAttrs mkNode { web01 = { - deployment = { - tags = [ "web" ]; - }; + deployment.tags = [ "web" ]; - nixpkgs = "23.05"; stateVersion = "23.05"; }; diff --git a/npins/sources.json b/npins/sources.json index 91804d1..6544c9c 100644 --- a/npins/sources.json +++ b/npins/sources.json @@ -102,12 +102,6 @@ "url": null, "hash": "14w7w327m8rf7yrjflqvbnmwx04l36n7j0nca5ilpvzrr8f2gg6l" }, - "nixos-23.05": { - "type": "Channel", - "name": "nixos-23.05", - "url": "https://releases.nixos.org/nixos/23.05/nixos-23.05.4981.5b528f99f73c/nixexprs.tar.xz", - "hash": "1psdfcl5rjid66dhc8c0dfdrgqk5x76drwcads149pa45vbnri8k" - }, "nixos-23.11": { "type": "Channel", "name": "nixos-23.11", diff --git a/patches/castopod.patch b/patches/castopod.patch index 95f9b8e..8858309 100644 --- a/patches/castopod.patch +++ b/patches/castopod.patch @@ -1,537 +1,583 @@ -From 9d7860c7e7830d9bf82733cecd443ff167dc2174 Mon Sep 17 00:00:00 2001 -From: Alexander Tomokhov -Date: Thu, 14 Sep 2023 02:19:40 +0400 -Subject: [PATCH 1/4] maintainers: add alexoundos - ---- - maintainers/maintainer-list.nix | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix -index e54123ac9e4907..bc28b303afaad5 100644 ---- a/maintainers/maintainer-list.nix -+++ b/maintainers/maintainer-list.nix -@@ -759,6 +759,12 @@ - github = "Alexnortung"; - githubId = 1552267; - }; -+ alexoundos = { -+ email = "alexoundos@gmail.com"; -+ github = "AleXoundOS"; -+ githubId = 464913; -+ name = "Alexander Tomokhov"; -+ }; - alexshpilkin = { - email = "ashpilkin@gmail.com"; - github = "alexshpilkin"; - -From 17f036f4fbdd1024ebc91ff58686b9b8ce62990b Mon Sep 17 00:00:00 2001 -From: misuzu -Date: Mon, 13 Mar 2023 20:53:59 +0200 -Subject: [PATCH 2/4] castopod: init at 1.6.4 - ---- - pkgs/applications/audio/castopod/default.nix | 51 +++++++++++ - pkgs/applications/audio/castopod/update.sh | 89 ++++++++++++++++++++ - pkgs/top-level/all-packages.nix | 2 + - 3 files changed, 142 insertions(+) - create mode 100644 pkgs/applications/audio/castopod/default.nix - create mode 100755 pkgs/applications/audio/castopod/update.sh - -diff --git a/pkgs/applications/audio/castopod/default.nix b/pkgs/applications/audio/castopod/default.nix -new file mode 100644 -index 00000000000000..e4cdb6025f313f ---- /dev/null -+++ b/pkgs/applications/audio/castopod/default.nix -@@ -0,0 +1,55 @@ -+{ stdenv -+, fetchurl -+, ffmpeg-headless -+, lib -+, stateDirectory ? "/var/lib/castopod" -+}: -+stdenv.mkDerivation { -+ pname = "castopod"; -+ version = "1.6.5"; -+ -+ src = fetchurl { -+ url = "https://code.castopod.org/adaures/castopod/uploads/5aaaa6cf2edaed25bd7253449e5f8584/castopod-1.6.5.tar.gz"; -+ sha256 = "04gcq2vmfy5aa2fmsm1qqv1k8g024nikmysdrhy33wj460d529b5"; -+ }; -+ -+ dontBuild = true; -+ dontFixup = true; -+ -+ postPatch = '' -+ # not configurable at runtime unfortunately: -+ substituteInPlace app/Config/Paths.php \ -+ --replace "__DIR__ . '/../../writable'" "'${stateDirectory}/writable'" -+ -+ substituteInPlace modules/Admin/Controllers/DashboardController.php \ -+ --replace "disk_total_space('./')" "disk_total_space('${stateDirectory}')" -+ -+ # configuration file must be writable, place it to ${stateDirectory} -+ substituteInPlace modules/Install/Controllers/InstallController.php \ -+ --replace "ROOTPATH" "'${stateDirectory}/'" -+ substituteInPlace public/index.php spark \ -+ --replace "DotEnv(ROOTPATH)" "DotEnv('${stateDirectory}')" -+ -+ # ffmpeg is required for Video Clips feature -+ substituteInPlace modules/MediaClipper/VideoClipper.php \ -+ --replace "ffmpeg" "${ffmpeg-headless}/bin/ffmpeg" -+ substituteInPlace modules/Admin/Controllers/VideoClipsController.php \ -+ --replace "which ffmpeg" "echo ${ffmpeg-headless}/bin/ffmpeg" -+ ''; -+ -+ installPhase = '' -+ mkdir -p $out/share/castopod -+ cp -r . $out/share/castopod -+ ''; -+ -+ passthru.stateDirectory = stateDirectory; -+ passthru.updateScript = ./update.sh; -+ -+ meta = with lib; { -+ description = "An open-source hosting platform made for podcasters who want to engage and interact with their audience"; -+ homepage = "https://castopod.org"; -+ license = licenses.agpl3Only; -+ maintainers = with maintainers; [ alexoundos misuzu ]; -+ platforms = platforms.all; -+ }; -+} -diff --git a/pkgs/applications/audio/castopod/update.sh b/pkgs/applications/audio/castopod/update.sh -new file mode 100755 -index 00000000000000..742788dc8ddfdd ---- /dev/null -+++ b/pkgs/applications/audio/castopod/update.sh -@@ -0,0 +1,89 @@ -+#! /usr/bin/env nix-shell -+#! nix-shell -i bash -p curl jq -+set -euo pipefail -+ -+nixpkgs="$(git rev-parse --show-toplevel)" -+castopod_nix="$nixpkgs/pkgs/applications/audio/castopod/default.nix" -+ -+# https://www.meetup.com/api/guide/#p02-querying-section -+query=' -+query allReleases($fullPath: ID!, $first: Int, $last: Int, $before: String, $after: String, $sort: ReleaseSort) { -+ project(fullPath: $fullPath) { -+ id -+ releases( -+ first: $first -+ last: $last -+ before: $before -+ after: $after -+ sort: $sort -+ ) { -+ nodes { -+ ...Release -+ __typename -+ } -+ __typename -+ } -+ __typename -+ } -+} -+ -+fragment Release on Release { -+ id -+ name -+ tagName -+ releasedAt -+ createdAt -+ upcomingRelease -+ historicalRelease -+ assets { -+ links { -+ nodes { -+ id -+ name -+ url -+ directAssetUrl -+ linkType -+ __typename -+ } -+ __typename -+ } -+ __typename -+ } -+ __typename -+} -+' -+variables='{ -+ "fullPath": "adaures/castopod", -+ "first": 1, -+ "sort": "RELEASED_AT_DESC" -+}' -+ -+post=$(cat < -Date: Fri, 7 Apr 2023 15:59:08 +0300 -Subject: [PATCH 3/4] nixos/castopod: init - ---- - nixos/modules/module-list.nix | 1 + - nixos/modules/services/audio/castopod.nix | 299 ++++++++++++++++++ - 2 files changed, 299 insertions(+) - create mode 100644 nixos/modules/services/audio/castopod.nix - diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix -index 206d5eaf75dedc..54fd5c7b040314 100644 +index fee7c35ed8f4..f464b2e1dd0f 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix -@@ -324,6 +324,7 @@ +@@ -329,7 +329,6 @@ ./services/amqp/rabbitmq.nix ./services/audio/alsa.nix ./services/audio/botamusique.nix -+ ./services/audio/castopod.nix +- ./services/audio/castopod.nix ./services/audio/gmediarender.nix ./services/audio/gonic.nix - ./services/audio/hqplayerd.nix -diff --git a/nixos/modules/services/audio/castopod.nix b/nixos/modules/services/audio/castopod.nix -new file mode 100644 -index 00000000000000..b782b548914795 ---- /dev/null -+++ b/nixos/modules/services/audio/castopod.nix -@@ -0,0 +1,298 @@ -+{ 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.php.withExtensions ({ enabled, all }: with all; [ -+ intl -+ curl -+ mbstring -+ gd -+ exif -+ mysqlnd -+ ] ++ enabled); -+in -+{ -+ meta.maintainers = with lib.maintainers; [ alexoundos misuzu ]; -+ -+ options.services = { -+ castopod = { -+ enable = lib.mkEnableOption (lib.mdDoc "Castopod"); -+ package = lib.mkOption { -+ type = lib.types.package; -+ default = pkgs.castopod; -+ defaultText = lib.literalMD "pkgs.castopod"; -+ description = lib.mdDoc "Which Castopod package to use."; -+ }; -+ database = { -+ createLocally = lib.mkOption { -+ type = lib.types.bool; -+ default = true; -+ description = lib.mdDoc '' -+ Create the database and database user locally. -+ ''; -+ }; -+ hostname = lib.mkOption { -+ type = lib.types.str; -+ default = "localhost"; -+ description = lib.mdDoc "Database hostname."; -+ }; -+ name = lib.mkOption { -+ type = lib.types.str; -+ default = "castopod"; -+ description = lib.mdDoc "Database name."; -+ }; -+ user = lib.mkOption { -+ type = lib.types.str; -+ default = user; -+ description = lib.mdDoc "Database user."; -+ }; -+ passwordFile = lib.mkOption { -+ type = lib.types.nullOr lib.types.path; -+ default = null; -+ example = "/run/keys/castopod-dbpassword"; -+ description = lib.mdDoc '' -+ A file containing the password corresponding to -+ [](#opt-services.castopod.database.user). -+ ''; -+ }; -+ }; -+ 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"; -+ }; + ./services/audio/goxlr-utility.nix +@@ -1249,6 +1248,7 @@ + ./services/web-apps/bookstack.nix + ./services/web-apps/c2fmzq-server.nix + ./services/web-apps/calibre-web.nix ++ ./services/web-apps/castopod.nix + ./services/web-apps/coder.nix + ./services/web-apps/changedetection-io.nix + ./services/web-apps/chatgpt-retrieval-plugin.nix +diff --git a/nixos/modules/services/audio/castopod.md b/nixos/modules/services/web-apps/castopod.md +similarity index 72% +rename from nixos/modules/services/audio/castopod.md +rename to nixos/modules/services/web-apps/castopod.md +index ee8590737a7c..f61bf1166a4d 100644 +--- a/nixos/modules/services/audio/castopod.md ++++ b/nixos/modules/services/web-apps/castopod.md +@@ -4,6 +4,7 @@ Castopod is an open-source hosting platform made for podcasters who want to enga + + ## Quickstart {#module-services-castopod-quickstart} + ++Configure ACME (https://nixos.org/manual/nixos/unstable/#module-security-acme). + Use the following configuration to start a public instance of Castopod on `castopod.example.com` domain: + + ```nix +@@ -11,11 +12,11 @@ networking.firewall.allowedTCPPorts = [ 80 443 ]; + services.castopod = { + enable = true; + database.createLocally = true; +- nginx.virtualHost = { +- serverName = "castopod.example.com"; +- enableACME = true; +- forceSSL = true; +- }; ++ localDomain = "castopod.example.com"; ++}; ++services.nginx.virtualHosts."castopod.example.com" = { ++ enableACME = true; ++ forceSSL = true; + }; + ``` + +diff --git a/nixos/modules/services/audio/castopod.nix b/nixos/modules/services/web-apps/castopod.nix +similarity index 76% +rename from nixos/modules/services/audio/castopod.nix +rename to nixos/modules/services/web-apps/castopod.nix +index b782b5489147..53a4430d265d 100644 +--- a/nixos/modules/services/audio/castopod.nix ++++ b/nixos/modules/services/web-apps/castopod.nix +@@ -4,7 +4,6 @@ let + fpm = config.services.phpfpm.pools.castopod; + + user = "castopod"; +- stateDirectory = "/var/lib/castopod"; + + # https://docs.castopod.org/getting-started/install.html#requirements + phpPackage = pkgs.php.withExtensions ({ enabled, all }: with all; [ +@@ -29,6 +28,15 @@ in + defaultText = lib.literalMD "pkgs.castopod"; + description = lib.mdDoc "Which Castopod package to use."; + }; ++ dataDir = lib.mkOption { ++ type = lib.types.path; ++ default = "/var/lib/castopod"; + description = lib.mdDoc '' -+ Environment variables used for Castopod. -+ See [](https://code.castopod.org/adaures/castopod/-/blob/main/.env.example) -+ for available environment variables. ++ 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). + ''; + }; -+ environmentFile = lib.mkOption { -+ type = lib.types.nullOr lib.types.path; -+ default = null; -+ example = "/run/keys/castopod-env"; + database = { + createLocally = lib.mkOption { + type = lib.types.bool; +@@ -59,6 +67,7 @@ in + description = lib.mdDoc '' + A file containing the password corresponding to + [](#opt-services.castopod.database.user). ++ This file is loaded using systemd LoadCredentials. + ''; + }; + }; +@@ -83,6 +92,7 @@ in + example = "/run/keys/castopod-env"; + description = lib.mdDoc '' + Environment file to inject e.g. secrets into the configuration. ++ This file is loaded using systemd LoadCredentials. + See [](https://code.castopod.org/adaures/castopod/-/blob/main/.env.example) + for available environment variables. + ''; +@@ -111,6 +121,18 @@ in + Options for Castopod's PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives. + ''; + }; ++ maxUploadSize = lib.mkOption { ++ type = lib.types.int; ++ default = 512; + description = lib.mdDoc '' -+ 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. ++ Maximum supported size for a file upload in MiB. 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. + ''; + }; -+ configureNginx = lib.mkOption { -+ type = lib.types.bool; -+ default = true; -+ description = lib.mdDoc "Configure nginx as a reverse proxy for CastoPod."; -+ }; -+ localDomain = lib.mkOption { -+ type = lib.types.str; -+ example = "castopod.example.org"; -+ description = lib.mdDoc "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 = lib.mdDoc '' -+ Options for Castopod's PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives. -+ ''; -+ }; -+ }; -+ }; -+ -+ 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 + }; + }; + +@@ -120,13 +142,13 @@ in + 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 (name: lib.mkDefault) { + lib.mapAttrs (_name: lib.mkDefault) { -+ "app.forceGlobalSecureRequests" = sslEnabled; -+ "app.baseURL" = baseURL; -+ + "app.forceGlobalSecureRequests" = sslEnabled; + "app.baseURL" = baseURL; + +- "media.baseURL" = "/"; + "media.baseURL" = baseURL; -+ "media.root" = "media"; -+ "media.storage" = cfg.package.stateDirectory; -+ -+ "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; -+ phpPackage = phpPackage; -+ phpOptions = '' -+ # https://code.castopod.org/adaures/castopod/-/blob/main/docker/production/app/uploads.ini -+ file_uploads = On -+ memory_limit = 512M -+ upload_max_filesize = 500M -+ post_max_size = 512M -+ 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.package.stateDirectory}/.env"; -+ media = "${cfg.settings."media.storage"}/${cfg.settings."media.root"}"; -+ in -+ '' -+ mkdir -p ${cfg.package.stateDirectory}/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.package.stateDirectory}/salt ]; then -+ openssl rand -base64 33 > ${cfg.package.stateDirectory}/salt -+ fi -+ -+ cat <<'EOF' > ${envFile} -+ ${lib.generators.toKeyValue { } cfg.settings} -+ EOF -+ -+ echo "analytics.salt=$(cat ${cfg.package.stateDirectory}/salt)" >> ${envFile} -+ -+ ${if (cfg.database.passwordFile != null) then '' -+ echo "database.default.password=$(cat ${lib.escapeShellArg cfg.database.passwordFile})" >> ${envFile} -+ '' else '' -+ echo "database.default.password=" >> ${envFile} -+ ''} -+ -+ ${lib.optionalString (cfg.environmentFile != null) '' + "media.root" = "media"; +- "media.storage" = stateDirectory; ++ "media.storage" = cfg.dataDir; + + "admin.gateway" = "admin"; + "auth.gateway" = "auth"; +@@ -142,13 +164,13 @@ in + services.phpfpm.pools.castopod = { + inherit user; + group = config.services.nginx.group; +- phpPackage = phpPackage; ++ inherit phpPackage; + phpOptions = '' +- # https://code.castopod.org/adaures/castopod/-/blob/main/docker/production/app/uploads.ini ++ # https://code.castopod.org/adaures/castopod/-/blob/develop/docker/production/common/uploads.template.ini + file_uploads = On + memory_limit = 512M +- upload_max_filesize = 500M +- post_max_size = 512M ++ upload_max_filesize = ${toString cfg.maxUploadSize}M ++ post_max_size = ${toString cfg.maxUploadSize}M + max_execution_time = 300 + max_input_time = 300 + ''; +@@ -165,45 +187,50 @@ in + path = [ pkgs.openssl phpPackage ]; + script = + let +- envFile = "${stateDirectory}/.env"; ++ envFile = "${cfg.dataDir}/.env"; + media = "${cfg.settings."media.storage"}/${cfg.settings."media.root"}"; + in + '' +- mkdir -p ${stateDirectory}/writable/{cache,logs,session,temp,uploads} ++ 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 ${stateDirectory}/salt ]; then +- openssl rand -base64 33 > ${stateDirectory}/salt ++ 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 ${stateDirectory}/salt)" >> ${envFile} ++ echo "analytics.salt=$(cat ${cfg.dataDir}/salt)" >> ${envFile} + + ${if (cfg.database.passwordFile != null) then '' +- echo "database.default.password=$(cat ${lib.escapeShellArg cfg.database.passwordFile})" >> ${envFile} ++ echo "database.default.password=$(cat "$CREDENTIALS_DIRECTORY/dbpasswordfile)" >> ${envFile} + '' else '' + echo "database.default.password=" >> ${envFile} + ''} + + ${lib.optionalString (cfg.environmentFile != null) '' +- cat ${lib.escapeShellArg cfg.environmentFile}) >> ${envFile} + cat "$CREDENTIALS_DIRECTORY/envfile" >> ${envFile} -+ ''} -+ -+ php spark castopod:database-update -+ ''; -+ serviceConfig = { -+ StateDirectory = "castopod"; -+ LoadCredential = lib.mkIf (cfg.environmentFile != null) [ + ''} + +- php spark castopod:database-update ++ php ${cfg.package}/share/castopod/spark castopod:database-update + ''; + serviceConfig = { + StateDirectory = "castopod"; ++ LoadCredential = lib.optional (cfg.environmentFile != null) + "envfile:${cfg.environmentFile}" -+ ]; -+ WorkingDirectory = "${cfg.package}/share/castopod"; -+ Type = "oneshot"; -+ RemainAfterExit = true; -+ User = user; -+ Group = config.services.nginx.group; -+ }; -+ }; -+ -+ systemd.services.castopod-scheduled = { -+ after = [ "castopod-setup.service" ]; -+ wantedBy = [ "multi-user.target" ]; -+ path = [ phpPackage ]; -+ script = '' ++ ++ (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; + }; + }; + +@@ -212,9 +239,7 @@ in + wantedBy = [ "multi-user.target" ]; + path = [ phpPackage ]; + script = '' +- php public/index.php scheduled-activities +- php public/index.php scheduled-websub-publish +- php public/index.php scheduled-video-clips + 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; -+ }; -+ }; + ''; + serviceConfig = { + StateDirectory = "castopod"; +@@ -222,6 +247,8 @@ in + Type = "oneshot"; + User = user; + Group = config.services.nginx.group; ++ ReadWritePaths = cfg.dataDir; ++ LogLevelMax = "notice"; # otherwise periodic tasks flood the journal + }; + }; + +@@ -251,6 +278,7 @@ in + extraConfig = '' + try_files $uri $uri/ /index.php?$args; + index index.php index.html; ++ client_max_body_size ${toString cfg.maxUploadSize}M; + ''; + + locations."^~ /${cfg.settings."media.root"}/" = { +@@ -278,7 +306,7 @@ in + }; + }; + +- users.users.${user} = lib.mapAttrs (name: lib.mkDefault) { ++ users.users.${user} = lib.mapAttrs (_name: lib.mkDefault) { + description = "Castopod user"; + isSystemUser = true; + group = config.services.nginx.group; +diff --git a/nixos/tests/castopod.nix b/nixos/tests/castopod.nix +index 4435ec617d4e..2db7aa0bda65 100644 +--- a/nixos/tests/castopod.nix ++++ b/nixos/tests/castopod.nix +@@ -4,74 +4,211 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: + meta = with lib.maintainers; { + maintainers = [ alexoundos misuzu ]; + }; + -+ systemd.timers.castopod-scheduled = { -+ wantedBy = [ "timers.target" ]; -+ timerConfig = { -+ OnCalendar = "*-*-* *:*:00"; -+ Unit = "castopod-scheduled.service"; -+ }; -+ }; + nodes.castopod = { nodes, ... }: { ++ # otherwise 500 MiB file upload fails! ++ virtualisation.diskSize = 512 + 3 * 512; + -+ 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"; }; -+ }]; -+ }; + networking.firewall.allowedTCPPorts = [ 80 ]; +- networking.extraHosts = '' +- 127.0.0.1 castopod.example.com +- ''; ++ networking.extraHosts = ++ lib.strings.concatStringsSep "\n" ++ (lib.attrsets.mapAttrsToList ++ (name: _: "127.0.0.1 ${name}") ++ nodes.castopod.services.nginx.virtualHosts); + -+ services.nginx = lib.mkIf cfg.configureNginx { -+ enable = true; -+ resolver.addresses = [ "127.0.0.53" ]; -+ virtualHosts."${cfg.localDomain}" = { -+ root = lib.mkForce "${cfg.package}/share/castopod/public"; + services.castopod = { + enable = true; + database.createLocally = true; + localDomain = "castopod.example.com"; ++ maxUploadSize = 512; + }; +- environment.systemPackages = +- let +- username = "admin"; +- email = "admin@castood.example.com"; +- password = "v82HmEp5"; +- testRunner = pkgs.writers.writePython3Bin "test-runner" +- { +- libraries = [ pkgs.python3Packages.selenium ]; +- flakeIgnore = [ +- "E501" +- ]; +- } '' +- from selenium.webdriver.common.by import By +- from selenium.webdriver import Firefox +- from selenium.webdriver.firefox.options import Options +- from selenium.webdriver.support.ui import WebDriverWait +- from selenium.webdriver.support import expected_conditions as EC +- +- options = Options() +- options.add_argument('--headless') +- driver = Firefox(options=options) +- try: +- driver.implicitly_wait(20) +- driver.get('http://castopod.example.com/cp-install') +- +- wait = WebDriverWait(driver, 10) +- +- wait.until(EC.title_contains("installer")) +- +- driver.find_element(By.CSS_SELECTOR, '#username').send_keys( +- '${username}' +- ) +- driver.find_element(By.CSS_SELECTOR, '#email').send_keys( +- '${email}' +- ) +- driver.find_element(By.CSS_SELECTOR, '#password').send_keys( +- '${password}' +- ) +- driver.find_element(By.XPATH, "//button[contains(., 'Finish install')]").click() +- +- wait.until(EC.title_contains("Auth")) +- +- driver.find_element(By.CSS_SELECTOR, '#email').send_keys( +- '${email}' +- ) +- driver.find_element(By.CSS_SELECTOR, '#password').send_keys( +- '${password}' +- ) +- driver.find_element(By.XPATH, "//button[contains(., 'Login')]").click() +- +- wait.until(EC.title_contains("Admin dashboard")) +- finally: +- driver.close() +- driver.quit() +- ''; +- in +- [ pkgs.firefox-unwrapped pkgs.geckodriver testRunner ]; + }; + -+ extraConfig = '' -+ client_max_body_size 512m; -+ try_files $uri $uri/ /index.php?$args; -+ index index.php index.html; ++ nodes.client = { nodes, pkgs, lib, ... }: ++ let ++ domain = nodes.castopod.services.castopod.localDomain; ++ ++ getIP = node: ++ (builtins.head node.networking.interfaces.eth1.ipv4.addresses).address; ++ ++ targetPodcastSize = 500 * 1024 * 1024; ++ lameMp3Bitrate = 348300; ++ lameMp3FileAdjust = -800; ++ targetPodcastDuration = toString ++ ((targetPodcastSize + lameMp3FileAdjust) / (lameMp3Bitrate / 8)); ++ mp3file = with pkgs; ++ runCommand "gen-castopod.mp3" { nativeBuildInputs = [ sox lame ]; } '' ++ sox -n -r 48000 -t wav - synth ${targetPodcastDuration} sine 440 ` ++ `| lame --noreplaygain -cbr -q 9 -b 320 - $out ++ FILESIZE="$(stat -c%s $out)" ++ [ "$FILESIZE" -gt 0 ] ++ [ "$FILESIZE" -le "${toString targetPodcastSize}" ] + ''; + -+ locations."@force_get" = { -+ extraConfig = '' -+ recursive_error_pages on; -+ proxy_method GET; -+ proxy_pass https://podcasts.dgnum.eu/$request_uri; -+ ''; -+ }; ++ bannerWidth = 3000; ++ banner = pkgs.runCommand "gen-castopod-cover.jpg" { } '' ++ ${pkgs.imagemagick}/bin/magick ` ++ `-background green -bordercolor white -gravity northwest xc:black ` ++ `-duplicate 99 ` ++ `-seed 1 -resize "%[fx:rand()*72+24]" ` ++ `-seed 0 -rotate "%[fx:rand()*360]" -border 6x6 -splice 16x36 ` ++ `-seed 0 -rotate "%[fx:floor(rand()*4)*90]" -resize "150x50!" ` ++ `+append -crop 10x1@ +repage -roll "+%[fx:(t%2)*72]+0" -append ` ++ `-resize ${toString bannerWidth} -quality 1 $out ++ ''; + -+ locations."^~ /${cfg.settings."media.root"}/" = { -+ root = cfg.settings."media.storage"; -+ extraConfig = '' -+ add_header Access-Control-Allow-Origin "*"; -+ expires max; -+ access_log off; -+ ''; -+ }; ++ coverWidth = toString 3000; ++ cover = pkgs.runCommand "gen-castopod-banner.jpg" { } '' ++ ${pkgs.imagemagick}/bin/magick ` ++ `-background white -bordercolor white -gravity northwest xc:black ` ++ `-duplicate 99 ` ++ `-seed 1 -resize "%[fx:rand()*72+24]" ` ++ `-seed 0 -rotate "%[fx:rand()*360]" -border 6x6 -splice 36x36 ` ++ `-seed 0 -rotate "%[fx:floor(rand()*4)*90]" -resize "144x144!" ` ++ `+append -crop 10x1@ +repage -roll "+%[fx:(t%2)*72]+0" -append ` ++ `-resize ${coverWidth} -quality 1 $out ++ ''; ++ in ++ { ++ networking.extraHosts = ++ lib.strings.concatStringsSep "\n" ++ (lib.attrsets.mapAttrsToList ++ (name: _: "${getIP nodes.castopod} ${name}") ++ nodes.castopod.services.nginx.virtualHosts); + -+ locations."~ \.php$" = { -+ fastcgiParams = { -+ SERVER_NAME = "$host"; -+ }; -+ extraConfig = '' -+ error_page 550 = @force_get; -+ if ($request_method = HEAD) { return 550; } -+ 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; ++ environment.systemPackages = ++ let ++ username = "admin"; ++ email = "admin@${domain}"; ++ password = "Abcd1234"; ++ podcastTitle = "Some Title"; ++ episodeTitle = "Episode Title"; ++ browser-test = pkgs.writers.writePython3Bin "browser-test" ++ { ++ libraries = [ pkgs.python3Packages.selenium ]; ++ flakeIgnore = [ "E124" "E501" ]; ++ } '' ++ from selenium.webdriver.common.by import By ++ from selenium.webdriver import Firefox ++ from selenium.webdriver.firefox.options import Options ++ from selenium.webdriver.firefox.service import Service ++ from selenium.webdriver.support.ui import WebDriverWait ++ from selenium.webdriver.support import expected_conditions as EC ++ from subprocess import STDOUT ++ import logging ++ ++ selenium_logger = logging.getLogger("selenium") ++ selenium_logger.setLevel(logging.DEBUG) ++ selenium_logger.addHandler(logging.StreamHandler()) ++ ++ options = Options() ++ options.add_argument('--headless') ++ service = Service(log_output=STDOUT) ++ driver = Firefox(options=options, service=service) ++ driver = Firefox(options=options) ++ driver.implicitly_wait(20) ++ ++ # install ########################################################## ++ ++ driver.get('http://${domain}/cp-install') ++ ++ wait = WebDriverWait(driver, 10) ++ ++ wait.until(EC.title_contains("installer")) ++ ++ driver.find_element(By.CSS_SELECTOR, '#username').send_keys( ++ '${username}' ++ ) ++ driver.find_element(By.CSS_SELECTOR, '#email').send_keys( ++ '${email}' ++ ) ++ driver.find_element(By.CSS_SELECTOR, '#password').send_keys( ++ '${password}' ++ ) ++ driver.find_element(By.XPATH, ++ "//button[contains(., 'Finish install')]" ++ ).click() ++ ++ wait.until(EC.title_contains("Auth")) ++ ++ driver.find_element(By.CSS_SELECTOR, '#email').send_keys( ++ '${email}' ++ ) ++ driver.find_element(By.CSS_SELECTOR, '#password').send_keys( ++ '${password}' ++ ) ++ driver.find_element(By.XPATH, ++ "//button[contains(., 'Login')]" ++ ).click() ++ ++ wait.until(EC.title_contains("Admin dashboard")) ++ ++ # create podcast ################################################### ++ ++ driver.get('http://${domain}/admin/podcasts/new') ++ ++ wait.until(EC.title_contains("Create podcast")) ++ ++ driver.find_element(By.CSS_SELECTOR, '#cover').send_keys( ++ '${cover}' ++ ) ++ driver.find_element(By.CSS_SELECTOR, '#banner').send_keys( ++ '${banner}' ++ ) ++ driver.find_element(By.CSS_SELECTOR, '#title').send_keys( ++ '${podcastTitle}' ++ ) ++ driver.find_element(By.CSS_SELECTOR, '#handle').send_keys( ++ 'some_handle' ++ ) ++ driver.find_element(By.CSS_SELECTOR, '#description').send_keys( ++ 'Some description' ++ ) ++ driver.find_element(By.CSS_SELECTOR, '#owner_name').send_keys( ++ 'Owner Name' ++ ) ++ driver.find_element(By.CSS_SELECTOR, '#owner_email').send_keys( ++ 'owner@email.xyz' ++ ) ++ driver.find_element(By.XPATH, ++ "//button[contains(., 'Create podcast')]" ++ ).click() ++ ++ wait.until(EC.title_contains("${podcastTitle}")) ++ ++ driver.find_element(By.XPATH, ++ "//span[contains(., 'Add an episode')]" ++ ).click() ++ ++ wait.until(EC.title_contains("Add an episode")) ++ ++ # upload podcast ################################################### ++ ++ driver.find_element(By.CSS_SELECTOR, '#audio_file').send_keys( ++ '${mp3file}' ++ ) ++ driver.find_element(By.CSS_SELECTOR, '#cover').send_keys( ++ '${cover}' ++ ) ++ driver.find_element(By.CSS_SELECTOR, '#description').send_keys( ++ 'Episode description' ++ ) ++ driver.find_element(By.CSS_SELECTOR, '#title').send_keys( ++ '${episodeTitle}' ++ ) ++ driver.find_element(By.XPATH, ++ "//button[contains(., 'Create episode')]" ++ ).click() ++ ++ wait.until(EC.title_contains("${episodeTitle}")) ++ ++ driver.close() ++ driver.quit() + ''; -+ }; -+ }; ++ in ++ [ pkgs.firefox-unwrapped pkgs.geckodriver browser-test ]; + }; + -+ users.users.${user} = lib.mapAttrs (name: lib.mkDefault) { -+ description = "Castopod user"; -+ isSystemUser = true; -+ group = config.services.nginx.group; -+ }; -+ }; -+} + testScript = '' + start_all() + castopod.wait_for_unit("castopod-setup.service") +@@ -79,9 +216,9 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: + castopod.wait_for_unit("nginx.service") + castopod.wait_for_open_port(80) + castopod.wait_until_succeeds("curl -sS -f http://castopod.example.com") +- castopod.succeed("curl -s http://localhost/cp-install | grep 'Create your Super Admin account' > /dev/null") + +- with subtest("Create superadmin and log in"): +- castopod.succeed("PYTHONUNBUFFERED=1 systemd-cat -t test-runner test-runner") ++ with subtest("Create superadmin, log in, create and upload a podcast"): ++ client.succeed(\ ++ "PYTHONUNBUFFERED=1 systemd-cat -t browser-test browser-test") + ''; + }) +diff --git a/pkgs/applications/audio/castopod/default.nix b/pkgs/applications/audio/castopod/default.nix +index 9d9f83e2ecce..47c824ddda3c 100644 +--- a/pkgs/applications/audio/castopod/default.nix ++++ b/pkgs/applications/audio/castopod/default.nix +@@ -3,15 +3,15 @@ + , ffmpeg-headless + , lib + , nixosTests +-, stateDirectory ? "/var/lib/castopod" ++, dataDir ? "/var/lib/castopod" + }: + stdenv.mkDerivation { + pname = "castopod"; +- version = "1.6.4"; ++ version = "1.7.1"; + + src = fetchurl { +- url = "https://code.castopod.org/adaures/castopod/uploads/ce56d4f149242f12bedd20f9a2b0916d/castopod-1.6.4.tar.gz"; +- sha256 = "080jj91yxbn3xsbs0sywzwa2f5in9bp9qi2zwqcfqpaxlq9ga62v"; ++ url = "https://code.castopod.org/adaures/castopod/uploads/2c93d47db7067fd0f9740430dc74cf7a/castopod-1.7.1.tar.gz"; ++ sha256 = "0gvlnnc164lp51crd8b254n8681gf508mh6i0mmwzhbskhn1k9y2"; + }; + + dontBuild = true; +@@ -20,13 +20,16 @@ stdenv.mkDerivation { + postPatch = '' + # not configurable at runtime unfortunately: + substituteInPlace app/Config/Paths.php \ +- --replace "__DIR__ . '/../../writable'" "'${stateDirectory}/writable'" ++ --replace "__DIR__ . '/../../writable'" "'${dataDir}/writable'" + +- # configuration file must be writable, place it to ${stateDirectory} ++ substituteInPlace modules/Admin/Controllers/DashboardController.php \ ++ --replace "disk_total_space('./')" "disk_total_space('${dataDir}')" ++ ++ # configuration file must be writable, place it to ${dataDir} + substituteInPlace modules/Install/Controllers/InstallController.php \ +- --replace "ROOTPATH" "'${stateDirectory}/'" ++ --replace "ROOTPATH" "'${dataDir}/'" + substituteInPlace public/index.php spark \ +- --replace "DotEnv(ROOTPATH)" "DotEnv('${stateDirectory}')" ++ --replace "DotEnv(ROOTPATH)" "DotEnv('${dataDir}')" + + # ffmpeg is required for Video Clips feature + substituteInPlace modules/MediaClipper/VideoClipper.php \ diff --git a/patches/default.nix b/patches/default.nix index 04d5862..f5bd53a 100644 --- a/patches/default.nix +++ b/patches/default.nix @@ -1,138 +1,13 @@ { "nixos-23.11" = [ # [Backport release-23.11] zfs_2_1: init at 2.1.13 - { id = 270117; hash = "sha256-ot80XDtxDvPM0kW2gEeAs/z22jjkGOHog4Ue/JQEnZ8="; } - ]; + { id = 270117; hash = "sha256-In3sogw/8TGYQQFCeBvdljANR0ZLng4magQ/4uyVy1A="; } + { id = 241542; revert = true; hash = "sha256-uiRokmJewTLURuQkPWRfb3jgxjaDwfkXntj8PWk6pi8="; } - "nixos-23.05" = [ - # plausible: fix admin user password seed and SMTP passwords - { - id = 241126; - hash = "sha256-TcGuB3k8SeA8PRb/OdZ8ESw9/7yYKPftR96boK7Hmvc="; - } - - # fetchMixDeps: sha256 -> hash - { - id = 235733; - hash = "sha256-oHGZFXwOJ9ngZNJBTd93abgI+eNPsCBJPgFxt41728o="; - includes = [ - "pkgs/development/beam-modules/fetch-mix-deps.nix" - "pkgs/servers/web-apps/plausible/default.nix" - ]; - } - - # python3Packages.nix-prefetch-github: 6.0.1 -> 7.0.0 - # Only keep the files related to plausible - { - id = 243018; - hash = "sha256-/7jid8tKo2JbVyEmeVxt+9VRqc/2YWkUeagyrMqqb70="; - includes = [ "pkgs/servers/web-apps/plausible/*" ]; - } - - # plausible: 1.4.4 -> 1.5.1 - { - id = 229201; - hash = "sha256-wJ3qQbX5Yn7PZ5gpJYAeCIkblPaaVgUGg3XJb5C8ccY="; - } - - # plausible: 1.5.1 -> 2.0.0 - { - id = 253687; - hash = "sha256-Of3YXCJcevr5Ab6S/TMDR1M6PhffN/osLPAlfo60LAk="; - } - - # dbip-country-lite: init at 2023-06 - { - id = 235774; - hash = "sha256-M0oktrBKxezhBQh3gKHKXrWF7UjACX3PcpSzoq8HkW0="; - } - - # kanidm: 1.1.0-alpha.12 -> 1.1.0-beta.13 - { - id = 246564; - hash = "sha256-Q/G6w4iXthhC6JI/erOx0HBJ25aLQLtZSusAOdT6dYc="; - } - - # Forgejo v1.19.4-0 -> v1.20.4-1 - { - _type = "static"; - path = ./forgejo.patch; - } - - # nixos/forgejo: fork from nixos/gitea - { - id = 248310; - hash = "sha256-6cLMDbzYRKZrFulkS48dPznAap4bVCLsb1APaud9nV8="; - } - - # garage: add environmentFile - { - id = 257043; - hash = "sha256-Z+WmDPuDoV1Ex+XzvUhvMPn8U+aw0tCRH3O5oR2qQrM="; - } - - # outline: 0.68.1 -> 0.69.2 - { - id = 232235; - hash = "sha256-f+upHsuuYyLqd9Wv+9JHhB3HnP+mXWer6L/xi5eFpwE="; - } - - # outline: 0.69.2 -> 0.70.2 - { - id = 241667; - excludes = [ "nixos/doc/manual/*" ]; - hash = "sha256-9bOjwaXN/4/ASpNfyhaby+nuIz23gDLDIqgTdApdj1U="; - } - - # outline 0.70.2 -> 0.71.0 - { - id = 252126; - hash = "sha256-lH8xp5zG2fAaXS2gLF7UxqvuPlAigJ297hvlks0CG/U="; - } - - # outline: use fetchYarnDeps - { - id = 253567; - hash = "sha256-aR62vOuTfmJ7MIr3plDcBonQQH2+o2F6z/LAAgcKVHU="; - } - - # outline: 0.71.0 -> 0.72.0 - { - id = 259246; - hash = "sha256-gRGsmqFjtQWWCCTRr9QHZDM3NxIbj5G9bFaFaTYTEYY="; - } - - # nixos/outline: Add the possibility of using local storage instead of S3 - { - id = 259254; - excludes = [ "nixos/doc/manual/*" ]; - hash = "sha256-Hd3bRYncjnfHzEx+g6rb9cU3YmhF6W3QOtQUuDzw78U="; - } - - # outline: 0.72.2 -> 0.73.1 - { - id = 267752; - hash = "sha256-7bydFe7uOK9JxjFgwO0ZjZmKe3uo9GYZiMy0NG7+qkQ="; - } - - # nixos/ntfy.sh: use dynamic user + add defaults - { - id = 234811; - hash = "sha256-Yz007dCmGl5OxRDMSHv63Ww+LzoQISm9Ttiw0p/6spY="; - } - - # castopod: init - # Ne pas mettre à jour sans savoir ce qu'on fait (patch un peu customisé par rapport à upstream) + # castopod: 1.6.4 -> 1.7.0 + ajout du support de loadcredentials { _type = "static"; path = ./castopod.patch; } - - # nixos/fail2ban: RFC42-ize - { - id = 201907; - hash = "sha256-bkf37QTFgbnSz3s8QPm5Z+6rWVVOlDtISTR7FACEwMM="; - excludes = [ "nixos/doc/manual/" ]; - } ]; } diff --git a/patches/forgejo.patch b/patches/forgejo.patch deleted file mode 100644 index c509645..0000000 --- a/patches/forgejo.patch +++ /dev/null @@ -1,33 +0,0 @@ -diff --git a/pkgs/applications/version-management/forgejo/default.nix b/pkgs/applications/version-management/forgejo/default.nix -index d21097df07b..2ee652d8785 100644 ---- a/pkgs/applications/version-management/forgejo/default.nix -+++ b/pkgs/applications/version-management/forgejo/default.nix -@@ -23,7 +23,7 @@ let - pname = "forgejo-frontend"; - inherit (forgejo) src version; - -- npmDepsHash = "sha256-dB/uBuS0kgaTwsPYnqklT450ejLHcPAqBdDs3JT8Uxg="; -+ npmDepsHash = "sha256-YZzVw+WWqTmJafqnZ5vrzb7P6V4DTMNQwW1/+wvZEM8="; - - patches = [ - ./package-json-npm-build-frontend.patch -@@ -38,17 +38,17 @@ let - in - buildGoModule rec { - pname = "forgejo"; -- version = "1.19.4-0"; -+ version = "1.20.5-0"; - - src = fetchFromGitea { - domain = "codeberg.org"; - owner = "forgejo"; - repo = "forgejo"; - rev = "v${version}"; -- hash = "sha256-pTcnST8A4gADPBkNago9uwRFEmTx8vNONL/Emer4xLI="; -+ hash = "sha256-tuwMvSWaMUc/GghmrbGLtyjixwOwiapWEOMD9QmMLic="; - }; - -- vendorHash = "sha256-LKxhNbSIRaP4EGWX6mE26G9CWfoFTrPRjrL4ShpRHWo="; -+ vendorHash = "sha256-dgtZjsLBwblhdge3BvdbK/mN/TeZKps9K5dJbqomtjo="; - - subPackages = [ "." ];