277 lines
10 KiB
Nix
277 lines
10 KiB
Nix
{
|
|
description = "Personal Effectity Application";
|
|
inputs = {
|
|
flake-utils.url = "github:numtide/flake-utils";
|
|
mach-nix.url = "mach-nix/3.4.0";
|
|
nixpkgs.url = "nixpkgs/release-21.11";
|
|
};
|
|
|
|
outputs = { self, nixpkgs, flake-utils, mach-nix }:
|
|
flake-utils.lib.eachDefaultSystem (system:
|
|
with mach-nix.lib.${system};
|
|
let
|
|
pkgs = nixpkgs.legacyPackages.${system};
|
|
apiPkg = buildPythonPackage { src = [ ./api ]; };
|
|
urlMod = lib:
|
|
with lib; {
|
|
options.scheme = mkOption {
|
|
type = with types; enum [ null "http" "https" ];
|
|
default = null;
|
|
example = "https";
|
|
};
|
|
options.domain = mkOption {
|
|
type = with types; nullOr str;
|
|
default = null;
|
|
example = "pea.example.com";
|
|
};
|
|
options.subdir = mkOption {
|
|
type = types.str;
|
|
default = "/";
|
|
example = "/pea";
|
|
};
|
|
};
|
|
in {
|
|
packages = {
|
|
api = mkPython [ apiPkg ];
|
|
app = pkgs.callPackage ./app { };
|
|
};
|
|
devShells = {
|
|
api = mkPythonShell [ apiPkg ];
|
|
app = pkgs.callPackage ./app/shell.nix { };
|
|
};
|
|
overlays = { api = mkOverlay [ apiPkg ]; };
|
|
nixosModules = let
|
|
nginxOpts = lib:
|
|
with lib; {
|
|
webAttrs =
|
|
mkOption { type = with types; submodule (urlMod lib); };
|
|
acmeEmail = mkOption {
|
|
type = with types; nullOr str;
|
|
default = null;
|
|
example = "admin@example.com";
|
|
description = "The email to use for Let's Encrypt certificates";
|
|
};
|
|
};
|
|
maybeLocalhost = domain:
|
|
if builtins.isNull domain then "localhost" else domain;
|
|
in {
|
|
api = { config, lib, pkgs, ... }:
|
|
with lib;
|
|
let cfg = config.services.pea.api;
|
|
in {
|
|
options.services.pea.api = {
|
|
enable =
|
|
mkEnableOption "Personal Effectivity Application API service";
|
|
package = mkOption {
|
|
type = types.package;
|
|
default = self.outputs.packages.${system}.api;
|
|
description = "The package to use for the service.";
|
|
};
|
|
user = mkOption {
|
|
type = types.str;
|
|
default = "pea-api";
|
|
description = "The name for the PEA API system user";
|
|
};
|
|
group = mkOption {
|
|
type = types.str;
|
|
default = "pea";
|
|
description = "The name for the PEA group";
|
|
};
|
|
socketFile = mkOption {
|
|
type = types.str;
|
|
default = "/run/pea-api.sock";
|
|
description = "The path for the Unix socket file";
|
|
};
|
|
dotEnvFile = mkOption {
|
|
type = types.str;
|
|
example = "/run/keys/pea.env";
|
|
description = "The path to the dotenv file";
|
|
};
|
|
} // nginxOpts lib;
|
|
|
|
config = mkIf cfg.enable
|
|
(let domain = maybeLocalhost cfg.webAttrs.domain;
|
|
in mkMerge [
|
|
{
|
|
# basics
|
|
environment.systemPackages = [ cfg.package ];
|
|
networking.firewall.allowedTCPPorts =
|
|
if builtins.isString cfg.acmeEmail then [
|
|
80
|
|
443
|
|
] else
|
|
[ 80 ];
|
|
# NGINX config
|
|
services.nginx = {
|
|
enable = true;
|
|
recommendedProxySettings = true;
|
|
recommendedGzipSettings = true;
|
|
recommendedOptimisation = true;
|
|
recommendedTlsSettings = true;
|
|
upstreams.pea-api.servers = {
|
|
"unix:${cfg.socketFile}" = { };
|
|
};
|
|
|
|
virtualHosts.${domain} = {
|
|
locations."${cfg.webAttrs.subdir}/".proxyPass =
|
|
"http://pea-api/";
|
|
};
|
|
};
|
|
# PostgreSQL config
|
|
services.postgresql = let
|
|
db = "pea-db";
|
|
usr = "pea-user";
|
|
in {
|
|
enable = true;
|
|
package = pkgs.postgresql_14;
|
|
authentication = ''
|
|
local ${db} ${usr} trust
|
|
'';
|
|
ensureDatabases = [ db ];
|
|
ensureUsers = [{
|
|
name = usr;
|
|
ensurePermissions = {
|
|
"DATABASE \"${db}\"" = "ALL PRIVILEGES";
|
|
};
|
|
}];
|
|
identMap = ''
|
|
pea-map ${cfg.user} ${usr}
|
|
'';
|
|
};
|
|
services.postgresqlBackup = {
|
|
enable = true;
|
|
backupAll = true;
|
|
};
|
|
# PEA API config
|
|
systemd.services.pea-api = {
|
|
description = "PEA API Uvicorn service";
|
|
after = [ "network.target" "postgresql.service" ];
|
|
requires = [ "pea-api.socket" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
serviceConfig = {
|
|
WorkingDirectory =
|
|
"${cfg.package}/lib/python3.9/site-packages/pea_api/";
|
|
EnvironmentFile = cfg.dotEnvFile;
|
|
};
|
|
script = ''
|
|
${cfg.package}/bin/uvicorn main:app --root-path ${cfg.webAttrs.subdir} --uds ${cfg.socketFile}
|
|
'';
|
|
reload = ''
|
|
/bin/kill -s HUP $MAINPID
|
|
'';
|
|
};
|
|
systemd.sockets.pea-api = {
|
|
description = "PEA API Uvicorn socket";
|
|
wantedBy = [ "sockets.target" ];
|
|
socketConfig = { ListenStream = cfg.socketFile; };
|
|
};
|
|
# system user config
|
|
users.extraGroups.${cfg.group} = { };
|
|
users.extraUsers.${cfg.user} = {
|
|
isSystemUser = true;
|
|
group = cfg.group;
|
|
description = "PEA API user";
|
|
};
|
|
}
|
|
|
|
(mkIf (isString cfg.acmeEmail) {
|
|
security.acme = {
|
|
acceptTerms = true;
|
|
certs.${domain}.email = cfg.acmeEmail;
|
|
};
|
|
services.nginx.virtualHosts.${domain} = {
|
|
enableACME = true;
|
|
forceSSL = true;
|
|
};
|
|
})
|
|
]);
|
|
};
|
|
app = { config, pkgs, lib, ... }:
|
|
with lib;
|
|
let cfg = config.services.pea.app;
|
|
in {
|
|
options.services.pea.app = {
|
|
enable =
|
|
mkEnableOption "Personal Effectivity Application UI service";
|
|
package = mkOption {
|
|
type = types.package;
|
|
default = self.outputs.packages.${system}.app.override {
|
|
inherit (cfg) webAttrs apiAttrs;
|
|
};
|
|
description = "The package to use for the service.";
|
|
};
|
|
apiAttrs =
|
|
mkOption { type = with types; submodule (urlMod lib); };
|
|
} // nginxOpts lib;
|
|
config = mkIf cfg.enable
|
|
(let domain = maybeLocalhost cfg.webAttrs.domain;
|
|
in mkMerge [
|
|
{
|
|
services.nginx = {
|
|
enable = true;
|
|
recommendedProxySettings = true;
|
|
recommendedGzipSettings = true;
|
|
recommendedOptimisation = true;
|
|
recommendedTlsSettings = true;
|
|
virtualHosts.${domain}.locations = let
|
|
loc = cfg.webAttrs.subdir;
|
|
pkg = "${cfg.package}/";
|
|
in {
|
|
"${loc}/" = {
|
|
alias = pkg;
|
|
tryFiles = "$uri /index.html";
|
|
};
|
|
"/index.html" = { root = pkg; };
|
|
};
|
|
};
|
|
}
|
|
(mkIf (isString cfg.acmeEmail) {
|
|
security.acme = {
|
|
acceptTerms = true;
|
|
certs.${domain}.email = cfg.acmeEmail;
|
|
};
|
|
services.nginx.virtualHosts.${domain} = {
|
|
enableACME = true;
|
|
forceSSL = true;
|
|
};
|
|
})
|
|
]);
|
|
};
|
|
};
|
|
}) // {
|
|
nixosConfigurations.pea-test = let system = "x86_64-linux";
|
|
in nixpkgs.lib.nixosSystem {
|
|
modules = [
|
|
({ lib, ... }: {
|
|
boot.isContainer = true;
|
|
system.configurationRevision = lib.mkIf (self ? rev) self.rev;
|
|
|
|
networking.useDHCP = false;
|
|
})
|
|
self.nixosModules.${system}.api
|
|
self.nixosModules.${system}.app
|
|
({ config, ... }:
|
|
let apiSubdir = "/api";
|
|
in {
|
|
services.pea = {
|
|
api = {
|
|
enable = true;
|
|
dotEnvFile = "/run/keys/pea.env";
|
|
webAttrs.subdir = apiSubdir;
|
|
};
|
|
app = {
|
|
enable = true;
|
|
webAttrs = {
|
|
scheme = null;
|
|
domain = null;
|
|
subdir = "/app";
|
|
};
|
|
apiAttrs.subdir = apiSubdir;
|
|
};
|
|
};
|
|
})
|
|
];
|
|
};
|
|
};
|
|
}
|