235 lines
8.1 KiB
Nix
235 lines
8.1 KiB
Nix
{ config, ... }:
|
|
let
|
|
nixosModules = config.flake.modules.nixos;
|
|
in
|
|
{
|
|
flake.modules.nixos.wardrowbe =
|
|
{ config, lib, ... }:
|
|
let
|
|
service = config.facts.services.wardrowbe;
|
|
auth = config.facts.services.wardrowbe-auth;
|
|
podman = lib.getExe config.virtualisation.podman.package;
|
|
# Multi-architecture manifest digests; updating a tag alone cannot change an image.
|
|
images = {
|
|
backend = "ghcr.io/anyesh/wardrowbe:backend-1.10.3@sha256:75fd6f85fb7b91ccbb227b52d616bab11b5130c4e22858579f45736419fb592d";
|
|
frontend = "ghcr.io/anyesh/wardrowbe:frontend-1.10.3@sha256:730cf52bd8a03b94473b129e9a988b3dae9818e9377fd88917029156ee7a2e17";
|
|
auth = "ghcr.io/tinyauthapp/tinyauth:v5.2.0@sha256:220aa235c76393a73fece831d7ba967b8698d3907fd8e3c4c952f699c7b142a5";
|
|
postgres = "docker.io/library/postgres:15-alpine@sha256:f7d23353e1b15400d22ebe31189f4d314b87a4c129cc400c8c2d8d4ca127bf81";
|
|
redis = "docker.io/library/redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499";
|
|
};
|
|
healthOptions = [
|
|
"--health-interval=10s"
|
|
"--health-timeout=5s"
|
|
"--health-start-period=30s"
|
|
"--health-retries=6"
|
|
"--health-on-failure=kill"
|
|
];
|
|
backendContainer = {
|
|
image = images.backend;
|
|
environment = {
|
|
# Profile selection is available only behind the password gateway.
|
|
DEBUG = "true";
|
|
AI_INTERNAL_ENABLED = "false";
|
|
AI_VISION_ENABLED = "false";
|
|
AI_TEXT_ENABLED = "false";
|
|
APP_URL = service.origin;
|
|
CORS_ORIGINS = builtins.toJSON [ service.origin ];
|
|
REDIS_URL = "redis://wardrowbe-redis:6379/0";
|
|
STORAGE_PATH = "/data/uploads";
|
|
LOG_LEVEL = "INFO";
|
|
};
|
|
environmentFiles = [ config.sops.templates."wardrowbe-backend.env".path ];
|
|
volumes = [ "wardrowbe-uploads:/data/uploads" ];
|
|
};
|
|
backendUnits = [
|
|
"podman-wardrowbe-backend.service"
|
|
"podman-wardrowbe-worker.service"
|
|
"podman-wardrowbe-image-worker.service"
|
|
];
|
|
containers = {
|
|
wardrowbe-postgres = {
|
|
image = images.postgres;
|
|
environment = {
|
|
POSTGRES_USER = "wardrobe";
|
|
POSTGRES_DB = "wardrobe";
|
|
};
|
|
environmentFiles = [ config.sops.templates."wardrowbe-postgres.env".path ];
|
|
volumes = [ "wardrowbe-postgres:/var/lib/postgresql/data" ];
|
|
podman.sdnotify = "healthy";
|
|
extraOptions = healthOptions ++ [
|
|
"--health-cmd=pg_isready -h 127.0.0.1 -U wardrobe -d wardrobe"
|
|
];
|
|
};
|
|
wardrowbe-redis = {
|
|
image = images.redis;
|
|
cmd = [
|
|
"redis-server"
|
|
"--appendonly"
|
|
"yes"
|
|
];
|
|
volumes = [ "wardrowbe-redis:/data" ];
|
|
podman.sdnotify = "healthy";
|
|
extraOptions = healthOptions ++ [ "--health-cmd=redis-cli ping" ];
|
|
};
|
|
wardrowbe-backend = backendContainer // {
|
|
dependsOn = [
|
|
"wardrowbe-postgres"
|
|
"wardrowbe-redis"
|
|
];
|
|
ports = [ "127.0.0.1:8002:8000" ];
|
|
cmd = [
|
|
"sh"
|
|
"-c"
|
|
"alembic upgrade head && exec uvicorn app.main:app --host 0.0.0.0 --port 8000"
|
|
];
|
|
podman.sdnotify = "healthy";
|
|
extraOptions = healthOptions ++ [
|
|
"--health-cmd=curl --fail --silent http://127.0.0.1:8000/api/v1/health"
|
|
];
|
|
};
|
|
wardrowbe-worker = backendContainer // {
|
|
dependsOn = [ "wardrowbe-backend" ];
|
|
cmd = [
|
|
"arq"
|
|
"app.workers.worker.WorkerSettings"
|
|
];
|
|
};
|
|
wardrowbe-image-worker = backendContainer // {
|
|
dependsOn = [ "wardrowbe-backend" ];
|
|
cmd = [
|
|
"arq"
|
|
"app.workers.image_worker.ImageWorkerSettings"
|
|
];
|
|
};
|
|
wardrowbe-frontend = {
|
|
image = images.frontend;
|
|
dependsOn = [ "wardrowbe-backend" ];
|
|
environment = {
|
|
BACKEND_URL = "http://wardrowbe-backend:8000";
|
|
NEXTAUTH_URL = service.origin;
|
|
DEV_MODE = "true";
|
|
};
|
|
environmentFiles = [ config.sops.templates."wardrowbe-frontend.env".path ];
|
|
ports = [ "127.0.0.1:3002:3000" ];
|
|
podman.sdnotify = "healthy";
|
|
extraOptions = healthOptions ++ [
|
|
"--health-cmd=node -e \"fetch('http://127.0.0.1:3000').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))\""
|
|
];
|
|
};
|
|
wardrowbe-auth = {
|
|
image = images.auth;
|
|
environment = {
|
|
TINYAUTH_APPURL = auth.origin;
|
|
TINYAUTH_AUTH_SECURECOOKIE = "true";
|
|
TINYAUTH_AUTH_TRUSTEDPROXIES = "10.89.30.1";
|
|
TINYAUTH_LABELPROVIDER = "none";
|
|
TINYAUTH_ANALYTICS_ENABLED = "false";
|
|
TINYAUTH_DATABASE_PATH = "/data/tinyauth.db";
|
|
TINYAUTH_UI_TITLE = "Wardrobe login";
|
|
};
|
|
environmentFiles = [ config.sops.templates."wardrowbe-auth.env".path ];
|
|
volumes = [ "wardrowbe-auth:/data" ];
|
|
ports = [ "127.0.0.1:3003:3000" ];
|
|
podman.sdnotify = "healthy";
|
|
extraOptions = healthOptions ++ [ "--health-cmd=tinyauth healthcheck" ];
|
|
};
|
|
};
|
|
in
|
|
{
|
|
imports = [
|
|
nixosModules.caddy
|
|
nixosModules.sops
|
|
];
|
|
|
|
sops.secrets = {
|
|
wardrowbe-postgres-password = { };
|
|
wardrowbe-secret-key = { };
|
|
wardrowbe-nextauth-secret = { };
|
|
wardrowbe-auth-users = { };
|
|
};
|
|
sops.templates = {
|
|
"wardrowbe-postgres.env" = {
|
|
content = ''
|
|
POSTGRES_PASSWORD=${config.sops.placeholder.wardrowbe-postgres-password}
|
|
'';
|
|
restartUnits = [ "podman-wardrowbe-postgres.service" ];
|
|
};
|
|
"wardrowbe-backend.env" = {
|
|
content = ''
|
|
DATABASE_URL=postgresql+asyncpg://wardrobe:${config.sops.placeholder.wardrowbe-postgres-password}@wardrowbe-postgres:5432/wardrobe
|
|
SECRET_KEY=${config.sops.placeholder.wardrowbe-secret-key}
|
|
'';
|
|
restartUnits = backendUnits;
|
|
};
|
|
"wardrowbe-frontend.env" = {
|
|
content = ''
|
|
NEXTAUTH_SECRET=${config.sops.placeholder.wardrowbe-nextauth-secret}
|
|
'';
|
|
restartUnits = [ "podman-wardrowbe-frontend.service" ];
|
|
};
|
|
"wardrowbe-auth.env" = {
|
|
content = ''
|
|
TINYAUTH_AUTH_USERS=${config.sops.placeholder.wardrowbe-auth-users}
|
|
'';
|
|
restartUnits = [ "podman-wardrowbe-auth.service" ];
|
|
};
|
|
};
|
|
|
|
virtualisation.oci-containers = {
|
|
backend = "podman";
|
|
containers = lib.mapAttrs (
|
|
_: container:
|
|
{
|
|
networks = [ "wardrowbe" ];
|
|
}
|
|
// container
|
|
) containers;
|
|
};
|
|
|
|
systemd.services = {
|
|
wardrowbe-network = {
|
|
description = "Wardrowbe container network";
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
};
|
|
script = ''
|
|
${podman} network exists wardrowbe || \
|
|
${podman} network create --subnet 10.89.30.0/24 --gateway 10.89.30.1 wardrowbe
|
|
'';
|
|
};
|
|
}
|
|
// lib.mapAttrs' (
|
|
name: _:
|
|
lib.nameValuePair "podman-${name}" {
|
|
requires = [ "wardrowbe-network.service" ];
|
|
after = [ "wardrowbe-network.service" ];
|
|
serviceConfig.TimeoutStartSec = lib.mkForce 900;
|
|
}
|
|
) containers;
|
|
|
|
services.caddy.virtualHosts = {
|
|
${auth.domain}.extraConfig = "reverse_proxy 127.0.0.1:3003";
|
|
${service.domain}.extraConfig = ''
|
|
request_body {
|
|
max_size 50MB
|
|
}
|
|
route {
|
|
forward_auth 127.0.0.1:3003 {
|
|
uri /api/auth/caddy
|
|
}
|
|
handle /api/auth/* {
|
|
reverse_proxy 127.0.0.1:3002
|
|
}
|
|
handle /api/* {
|
|
reverse_proxy 127.0.0.1:8002
|
|
}
|
|
handle {
|
|
reverse_proxy 127.0.0.1:3002
|
|
}
|
|
}
|
|
'';
|
|
};
|
|
};
|
|
}
|