raincloud/flake.nix
2022-06-12 13:24:56 +02:00

174 lines
5.1 KiB
Nix

{
description = "Self-hosted file sharing cloud for you and your friends";
inputs = {
nixpkgs.url = github:nixos/nixpkgs/nixos-unstable;
flake-utils.url = github:numtide/flake-utils;
};
outputs = { self, nixpkgs, flake-utils }: {
nixosModule = { config, ... }:
with nixpkgs.lib;
let
system = config.nixpkgs.localSystem.system;
python = nixpkgs.legacyPackages.${system}.python3Packages.python;
flask = nixpkgs.legacyPackages.${system}.python3Packages.flask;
gunicorn = nixpkgs.legacyPackages.${system}.python3Packages.gunicorn;
raincloud = self.packages.${system}.raincloud;
cfg = config.services.raincloud;
in
{
options.services.raincloud = {
enable = mkEnableOption "Enable raincloud WSGI server";
address = mkOption {
type = types.str;
default = "127.0.0.1";
example = "0.0.0.0";
description = "Bind address of the server";
};
port = mkOption {
type = types.int;
default = 8000;
example = 4000;
description = "Port on which the server listens";
};
user = mkOption {
type = types.str;
default = "raincloud";
description = "User under which the server runs";
};
group = mkOption {
type = types.str;
default = "raincloud";
description = "Group under which the server runs";
};
cloudName = mkOption {
type = types.str;
default = "raincloud";
description = "Name of the raincloud";
};
basePath = mkOption {
type = types.str;
example = "/var/lib/raincloud";
description = "Base path of the raincloud";
};
secretKeyPath = mkOption {
type = types.str;
example = "/var/lib/raincloud/secret_key";
description = "Path to file containing Flask secret key";
};
redisUrl = mkOption {
type = types.str;
default = "redis://127.0.0.1:6379/0";
example = "unix:/run/redis-raincloud/redis.sock";
description = "URL of Redis database";
};
numWorkers = mkOption {
type = types.int;
default = 5;
example = 17;
description = "Number of Gunicorn workers (recommendation is: 2 x #CPUs + 1)";
};
workerTimeout = mkOption {
type = types.int;
default = 300;
example = 360;
description = "Gunicorn worker timeout";
};
};
config = mkIf cfg.enable {
systemd.services.raincloud = {
description = "Enable raincloud WSGI server";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
restartIfChanged = true;
environment =
let
penv = python.buildEnv.override {
extraLibs = [ flask raincloud ];
};
in
{
PYTHONPATH = "${penv}/${python.sitePackages}/";
};
serviceConfig = {
Type = "simple";
User = cfg.user;
Group = cfg.group;
Restart = "always";
RestartSec = "5s";
PermissionsStartOnly = true;
ExecStart = ''
${gunicorn}/bin/gunicorn "raincloud:create_app('${cfg.basePath}', '${cfg.secretKeyPath}', '${cfg.redisUrl}', '${cfg.cloudName}')" \
--bind=${cfg.address}:${toString cfg.port} \
--workers ${toString cfg.numWorkers} \
--timeout ${toString cfg.workerTimeout}
'';
};
};
users.users = mkIf (cfg.user == "raincloud") {
raincloud = {
group = cfg.group;
isSystemUser = true;
};
};
users.groups = mkIf (cfg.group == "raincloud") {
raincloud = { };
};
};
};
} // flake-utils.lib.eachDefaultSystem
(system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
# Package
packages.raincloud =
pkgs.python3Packages.buildPythonPackage rec {
name = "raincloud";
src = self;
propagatedBuildInputs = with pkgs; [
python3Packages.flask
python3Packages.redis
];
};
defaultPackage = self.packages.${system}.raincloud;
# Development shell
devShell = pkgs.mkShell {
buildInputs = with pkgs; [
python3
python3Packages.flask
python3Packages.gunicorn
python3Packages.redis
];
};
}
);
}