Compare commits
No commits in common. "17013e425e630fac68daa0491e15e97e36129a7b" and "4c8d7df4328bc873ade20fa53a188deffadde67f" have entirely different histories.
17013e425e
...
4c8d7df432
@ -4,7 +4,6 @@
|
|||||||
amdgpu-pwm = ./services/hardware/amdgpu-pwm.nix;
|
amdgpu-pwm = ./services/hardware/amdgpu-pwm.nix;
|
||||||
betanin = ./services/web-apps/betanin.nix;
|
betanin = ./services/web-apps/betanin.nix;
|
||||||
dunst = ./services/x11/dunst.nix;
|
dunst = ./services/x11/dunst.nix;
|
||||||
koillection = ./services/web-apps/koillection.nix;
|
|
||||||
porkbun-ddns = ./services/networking/porkbun-ddns.nix;
|
porkbun-ddns = ./services/networking/porkbun-ddns.nix;
|
||||||
radeon-profile-daemon = ./services/hardware/radeon-profile-daemon.nix;
|
radeon-profile-daemon = ./services/hardware/radeon-profile-daemon.nix;
|
||||||
}
|
}
|
||||||
|
@ -1,318 +0,0 @@
|
|||||||
{ config, lib, modulesPath, options, pkgs, ... }:
|
|
||||||
let
|
|
||||||
inherit (lib) literalExpression mkEnableOption mkIf mkOption optionalString types;
|
|
||||||
|
|
||||||
opt = options.services.koillection;
|
|
||||||
cfg = config.services.koillection;
|
|
||||||
db = cfg.database;
|
|
||||||
|
|
||||||
koillection = (pkgs.koillection or pkgs.callPackage ../../../pkgs/by-name/ko/koillection/package.nix { }).override {
|
|
||||||
dataDir = cfg.dataDir;
|
|
||||||
};
|
|
||||||
inherit (koillection.passthru) phpPackage;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.services.koillection = {
|
|
||||||
enable = mkEnableOption "Koillection, a collection manager";
|
|
||||||
|
|
||||||
user = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "koillection";
|
|
||||||
description = lib.mdDoc "User Koillection runs as.";
|
|
||||||
};
|
|
||||||
|
|
||||||
group = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "koillection";
|
|
||||||
description = lib.mdDoc "Group Koillection runs as.";
|
|
||||||
};
|
|
||||||
|
|
||||||
hostName = lib.mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = config.networking.fqdnOrHostName;
|
|
||||||
defaultText = "config.networking.fqdnOrHostName";
|
|
||||||
example = "koillection.example.com";
|
|
||||||
description = lib.mdDoc "The hostname to serve Koillection on.";
|
|
||||||
};
|
|
||||||
|
|
||||||
dataDir = mkOption {
|
|
||||||
description = lib.mdDoc "Koillection data directory";
|
|
||||||
default = "/var/lib/koillection";
|
|
||||||
type = types.path;
|
|
||||||
};
|
|
||||||
|
|
||||||
database = {
|
|
||||||
host = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = if db.createLocally then "/run/postgresql" else null;
|
|
||||||
defaultText = literalExpression ''
|
|
||||||
if config.${opt.database.createLocally}
|
|
||||||
then "/run/postgresql"
|
|
||||||
else null
|
|
||||||
'';
|
|
||||||
example = "192.168.12.85";
|
|
||||||
description = lib.mdDoc "Database host address or unix socket.";
|
|
||||||
};
|
|
||||||
port = mkOption {
|
|
||||||
type = types.port;
|
|
||||||
default = 5432;
|
|
||||||
description = lib.mdDoc "Database host port.";
|
|
||||||
};
|
|
||||||
name = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "koillection";
|
|
||||||
description = lib.mdDoc "Database name.";
|
|
||||||
};
|
|
||||||
user = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = cfg.user;
|
|
||||||
defaultText = literalExpression "user";
|
|
||||||
description = lib.mdDoc "Database username.";
|
|
||||||
};
|
|
||||||
passwordFile = mkOption {
|
|
||||||
type = with types; nullOr path;
|
|
||||||
default = null;
|
|
||||||
example = "/run/keys/koillection-dbpassword";
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
A file containing the password corresponding to
|
|
||||||
{option}`database.user`.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
createLocally = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
description = lib.mdDoc "Create the database and database user locally.";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
poolConfig = mkOption {
|
|
||||||
type = with 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 the bookstack PHP pool. See the documentation on `php-fpm.conf`
|
|
||||||
for details on configuration directives.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
nginx = mkOption {
|
|
||||||
type = types.submodule (
|
|
||||||
lib.recursiveUpdate
|
|
||||||
(import "${modulesPath}/services/web-servers/nginx/vhost-options.nix" { inherit config lib; })
|
|
||||||
{ }
|
|
||||||
);
|
|
||||||
default = { };
|
|
||||||
example = literalExpression ''
|
|
||||||
{
|
|
||||||
serverAliases = [
|
|
||||||
"koillection.''${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.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
config = mkOption {
|
|
||||||
type = with types;
|
|
||||||
attrsOf
|
|
||||||
(nullOr
|
|
||||||
(either
|
|
||||||
(oneOf [ bool int port path str ])
|
|
||||||
(submodule {
|
|
||||||
options = {
|
|
||||||
_secret = mkOption {
|
|
||||||
type = nullOr str;
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
The path to a file containing the value the option should
|
|
||||||
be set to in the final configuration file.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
})));
|
|
||||||
default = { };
|
|
||||||
example = literalExpression ''
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
|
||||||
services.koillection.config = {
|
|
||||||
APP_ENV = "prod";
|
|
||||||
# DB_DRIVER = "pdo_pgsql";
|
|
||||||
DB_USER = db.user;
|
|
||||||
DB_PASSWORD._secret = db.passwordFile;
|
|
||||||
DB_HOST = db.host;
|
|
||||||
DB_PORT = db.port;
|
|
||||||
DB_NAME = db.name;
|
|
||||||
# FIXME
|
|
||||||
# DB_VERSION = lib.versions.major config.services.postgresql.package.version;
|
|
||||||
DB_VERSION = "15";
|
|
||||||
PHP_TZ = config.time.timeZone;
|
|
||||||
};
|
|
||||||
|
|
||||||
services.postgresql = mkIf db.createLocally {
|
|
||||||
enable = true;
|
|
||||||
ensureDatabases = [ db.name ];
|
|
||||||
ensureUsers = [{
|
|
||||||
name = db.user;
|
|
||||||
ensureDBOwnership = true;
|
|
||||||
}];
|
|
||||||
};
|
|
||||||
|
|
||||||
services.phpfpm.pools.koillection = {
|
|
||||||
inherit (cfg) user group;
|
|
||||||
inherit phpPackage;
|
|
||||||
# Copied from docker/php.ini
|
|
||||||
phpOptions = ''
|
|
||||||
max_execution_time = 200
|
|
||||||
|
|
||||||
apc.enabled = 1
|
|
||||||
apc.shm_size = 64M
|
|
||||||
apc.ttl = 7200
|
|
||||||
|
|
||||||
opcache.enable = 1
|
|
||||||
opcache.memory_consumption = 256
|
|
||||||
opcache.max_accelerated_files = 20000
|
|
||||||
opcache.max_wasted_percentage = 10
|
|
||||||
opcache.validate_timestamps = 0
|
|
||||||
opcache.preload = ${koillection}/share/php/koillection/config/preload.php
|
|
||||||
opcache.preload_user = ${cfg.user}
|
|
||||||
expose_php = Off
|
|
||||||
|
|
||||||
session.cookie_httponly = 1
|
|
||||||
realpath_cache_size = 4096K
|
|
||||||
realpath_cache_ttle = 600
|
|
||||||
'';
|
|
||||||
settings = {
|
|
||||||
"listen.mode" = "0660";
|
|
||||||
"listen.owner" = cfg.user;
|
|
||||||
"listen.group" = cfg.group;
|
|
||||||
} // cfg.poolConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nginx = {
|
|
||||||
enable = lib.mkDefault true;
|
|
||||||
virtualHosts."${cfg.hostName}" = lib.mkMerge [
|
|
||||||
cfg.nginx
|
|
||||||
{
|
|
||||||
root = lib.mkForce "${koillection}/share/php/koillection/public";
|
|
||||||
extraConfig = optionalString (cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME) "fastcgi_param HTTPS on;";
|
|
||||||
locations = {
|
|
||||||
"/" = {
|
|
||||||
index = "index.php";
|
|
||||||
extraConfig = ''try_files $uri $uri/ /index.php?query_string;'';
|
|
||||||
};
|
|
||||||
"~ \.php$" = {
|
|
||||||
extraConfig = ''
|
|
||||||
try_files $uri $uri/ /index.php?$query_string;
|
|
||||||
include ${config.services.nginx.package}/conf/fastcgi_params;
|
|
||||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
|
||||||
fastcgi_param REDIRECT_STATUS 200;
|
|
||||||
fastcgi_pass unix:${config.services.phpfpm.pools."koillection".socket};
|
|
||||||
${optionalString (cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME) "fastcgi_param HTTPS on;"}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
"~ \.(js|css|gif|png|ico|jpg|jpeg)$" = {
|
|
||||||
extraConfig = "expires 365d;";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.koillection-setup = {
|
|
||||||
description = "Preparation tasks for koillection";
|
|
||||||
before = [ "phpfpm-koillection.service" ];
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
RemainAfterExit = true;
|
|
||||||
User = cfg.user;
|
|
||||||
WorkingDirectory = koillection;
|
|
||||||
RuntimeDirectory = "koillection/cache";
|
|
||||||
RuntimeDirectoryMode = "0700";
|
|
||||||
};
|
|
||||||
path = [ pkgs.replace-secret ];
|
|
||||||
script =
|
|
||||||
let
|
|
||||||
isSecret = v: lib.isAttrs v && v ? _secret && lib.isString v._secret;
|
|
||||||
koillectionEnvVars = lib.generators.toKeyValue {
|
|
||||||
mkKeyValue = lib.flip lib.generators.mkKeyValueDefault "=" {
|
|
||||||
mkValueString = v: with builtins;
|
|
||||||
if 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 isSecret v then hashString "sha256" v._secret
|
|
||||||
else throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty {}) v}";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
secretPaths = lib.mapAttrsToList (_: v: v._secret) (lib.filterAttrs (_: isSecret) cfg.config);
|
|
||||||
mkSecretReplacement = file: ''
|
|
||||||
replace-secret ${lib.escapeShellArgs [ ( builtins.hashString "sha256" file ) file "${cfg.dataDir}/.env" ]}
|
|
||||||
'';
|
|
||||||
secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths;
|
|
||||||
filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! builtins.elem v [{ } null])) cfg.config;
|
|
||||||
koillectionEnv = pkgs.writeText "koillection.env" (koillectionEnvVars filteredConfig);
|
|
||||||
in
|
|
||||||
''
|
|
||||||
# error handling
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# set permissions
|
|
||||||
umask 077
|
|
||||||
|
|
||||||
# create .env file
|
|
||||||
install -T -m 0600 -o ${cfg.user} ${koillectionEnv} "${cfg.dataDir}/.env"
|
|
||||||
${secretReplacements}
|
|
||||||
|
|
||||||
# prepend `base64:` if it does not exist in APP_KEY
|
|
||||||
|
|
||||||
if ! grep 'APP_KEY=base64:' "${cfg.dataDir}/.env" >/dev/null; then
|
|
||||||
sed -i 's/APP_KEY=/APP_KEY=base64:/' "${cfg.dataDir}/.env"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# migrate db
|
|
||||||
${lib.getExe phpPackage} ${koillection}/share/php/koillection/bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration
|
|
||||||
|
|
||||||
# dump translations
|
|
||||||
# TODO: Might need pointing somewhere else.
|
|
||||||
${lib.getExe phpPackage} ${koillection}/share/php/koillection/bin/console app:translations:dump
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.tmpfiles.rules = [
|
|
||||||
"d ${cfg.dataDir} 0710 ${cfg.user} ${cfg.group} - -"
|
|
||||||
"d ${cfg.dataDir}/public 0750 ${cfg.user} ${cfg.group} - -"
|
|
||||||
"d ${cfg.dataDir}/public/uploads 0750 ${cfg.user} ${cfg.group} - -"
|
|
||||||
"d ${cfg.dataDir}/var 0700 ${cfg.user} ${cfg.group} - -"
|
|
||||||
"d ${cfg.dataDir}/var/cache 0700 ${cfg.user} ${cfg.group} - -"
|
|
||||||
"d ${cfg.dataDir}/var/cache/prod 0700 ${cfg.user} ${cfg.group} - -"
|
|
||||||
"d ${cfg.dataDir}/var/logs 0700 ${cfg.user} ${cfg.group} - -"
|
|
||||||
];
|
|
||||||
|
|
||||||
users = {
|
|
||||||
users = mkIf (cfg.user == "koillection") {
|
|
||||||
koillection = {
|
|
||||||
inherit (cfg) group;
|
|
||||||
isSystemUser = true;
|
|
||||||
};
|
|
||||||
"${config.services.nginx.user}".extraGroups = [ cfg.group ];
|
|
||||||
};
|
|
||||||
groups = mkIf (cfg.group == "koillection") {
|
|
||||||
koillection = { };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
*** a/src/Kernel.php
|
|
||||||
--- b/src/Kernel.php
|
|
||||||
@@ -8,6 +8,21 @@ class Kernel extends BaseKernel
|
|
||||||
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
|
|
||||||
|
|
||||||
class Kernel extends BaseKernel
|
|
||||||
{
|
|
||||||
use MicroKernelTrait;
|
|
||||||
+
|
|
||||||
+ public function getDataDir(): string
|
|
||||||
+ {
|
|
||||||
+ return '@dataDir@';
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ public function getCacheDir(): string
|
|
||||||
+ {
|
|
||||||
+ return $this->getDataDir() . '/var/cache/' . $this->getEnvironment();
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ public function getLogDir(): string
|
|
||||||
+ {
|
|
||||||
+ return $this->getDataDir() . '/var/logs';
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
|||||||
{ lib
|
|
||||||
, dataDir ? "/var/lib/koillection"
|
|
||||||
# REVIEW: This supposed to be aliased by the caller, which means it shouldn't
|
|
||||||
# go in by-name, I think.
|
|
||||||
, php83
|
|
||||||
, fetchFromGitHub
|
|
||||||
, mkYarnPackage
|
|
||||||
, fetchYarnDeps
|
|
||||||
}:
|
|
||||||
|
|
||||||
php83.buildComposerProject (finalAttrs: {
|
|
||||||
pname = "koillection";
|
|
||||||
version = "1.5.2";
|
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
|
||||||
owner = "benjaminjonard";
|
|
||||||
repo = "koillection";
|
|
||||||
rev = finalAttrs.version;
|
|
||||||
hash = "sha256-r2rkHhp0F5QfwJuKeu4UdPoluDXxpyhYpie1zUk1h5c=";
|
|
||||||
};
|
|
||||||
|
|
||||||
frontend = mkYarnPackage {
|
|
||||||
inherit (finalAttrs) pname version;
|
|
||||||
|
|
||||||
src = "${finalAttrs.src}/assets";
|
|
||||||
|
|
||||||
offlineCache = fetchYarnDeps {
|
|
||||||
yarnLock = "${finalAttrs.src}/assets/yarn.lock";
|
|
||||||
hash = "";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
patches = [
|
|
||||||
./koillection-dirs.patch
|
|
||||||
];
|
|
||||||
|
|
||||||
postPatch = ''
|
|
||||||
substituteInPlace src/Kernel.php \
|
|
||||||
--replace "@dataDir@" "${dataDir}"
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Lock file uses exact constraints, which Composer doesn't like.
|
|
||||||
composerStrictValidation = false;
|
|
||||||
# Actually installs plugins, i.e., Symfony.
|
|
||||||
composerNoPlugins = false;
|
|
||||||
vendorHash = "sha256-LU9ZN4qUNUpSBGH6AChw3qU4RjgsoPJmLL01FS7UKRQ=";
|
|
||||||
|
|
||||||
postInstall = ''
|
|
||||||
local koillection_out=$out/share/php/koillection
|
|
||||||
|
|
||||||
rm -R $koillection_out/public/uploads
|
|
||||||
ln -s ${dataDir}/.env $koillection_out/.env.local
|
|
||||||
ln -s ${dataDir}/public/uploads $koillection_out/public/uploads
|
|
||||||
|
|
||||||
cp -r ${finalAttrs.frontend} assets/
|
|
||||||
'';
|
|
||||||
|
|
||||||
passthru = {
|
|
||||||
phpPackage = php83;
|
|
||||||
};
|
|
||||||
|
|
||||||
meta = {
|
|
||||||
description = "Self-hosted service allowing users to manage any kind of collections";
|
|
||||||
homepage = "https://github.com/benjaminjonard/koillection";
|
|
||||||
license = lib.licenses.mit;
|
|
||||||
platforms = lib.platforms.unix;
|
|
||||||
broken = true; # Blocked on NixOS/nixpkgs#254369
|
|
||||||
};
|
|
||||||
})
|
|
Loading…
Reference in New Issue
Block a user