feat(hetzner): add Stalwart, restic backups, and virtualcam
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
unstablePkgs,
|
||||
...
|
||||
}: let
|
||||
rev = "6225e0fca02c02544341c92ecdc9634a9a15f45c";
|
||||
src = pkgs.fetchgit {
|
||||
url = "https://git.severijnse.eu/jory/virtualcam-website.git";
|
||||
rev = rev;
|
||||
sha256 = "17ihw2bhsp89nczljz6xzwlvxyzgsdn62ywmchp5blzd6jkxd3w0";
|
||||
};
|
||||
|
||||
# Patch the app to be fully dynamic and drop the Google-font download so the
|
||||
# sandboxed Nix build needs neither a database nor network access.
|
||||
srcPatched = pkgs.applyPatches {
|
||||
name = "virtualcam-website-patched";
|
||||
src = src;
|
||||
patches = [./virtualcam-layout.patch ./virtualcam-build.patch];
|
||||
};
|
||||
|
||||
# Build the Next.js app entirely in Nix (offline npm deps from the lockfile).
|
||||
app = unstablePkgs.buildNpmPackage {
|
||||
pname = "virtualcam-website";
|
||||
version = "0.1.0";
|
||||
src = srcPatched;
|
||||
npmDepsHash = "sha256-52ugs4ydwxGXLIhF/6P8uO400x3BRYk4NUt2Swob3cY=";
|
||||
nodejs = unstablePkgs.nodejs;
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
npx prisma generate
|
||||
npm run build
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out
|
||||
cp -r .next node_modules public prisma src package.json package-lock.json \
|
||||
prisma.config.ts next.config.ts tsconfig.json postcss.config.mjs $out/
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
APP_URL = "https://virtualcam.severijnse.eu";
|
||||
# Only used to satisfy prisma generate / next build metadata resolution.
|
||||
DATABASE_URL = "postgresql://virtualcam@localhost/virtualcam?host=/run/postgresql&schema=public";
|
||||
# Use the nixpkgs-bundled Prisma engine so the sandboxed offline build does
|
||||
# not need to download it from binaries.prisma.sh. Version matches ^7.8.0.
|
||||
PRISMA_SCHEMA_ENGINE_BINARY = "${unstablePkgs.prisma-engines}/bin/schema-engine";
|
||||
NEXT_TELEMETRY_DISABLED = "1";
|
||||
CI = "true";
|
||||
};
|
||||
|
||||
dbUrl = "postgresql://virtualcam@localhost/virtualcam?host=/run/postgresql&schema=public";
|
||||
|
||||
# The repo's seed uses tsx (a devDependency buildNpmPackage drops) plus the
|
||||
# "@/..." path alias. nixpkgs ships tsx, which honours tsconfig paths, so we
|
||||
# add it to the service path rather than working around the missing dep.
|
||||
seed = pkgs.writeShellScript "virtualcam-seed" ''
|
||||
set -euo pipefail
|
||||
export DATABASE_URL="${dbUrl}"
|
||||
export PRISMA_SCHEMA_ENGINE_BINARY="${unstablePkgs.prisma-engines}/bin/schema-engine"
|
||||
export HOME=/var/lib/virtualcam
|
||||
cd ${app}
|
||||
tsx prisma/seed.ts
|
||||
'';
|
||||
|
||||
# One shared PostgreSQL server (existing system postgres). Each service gets
|
||||
# its own database + role. virtualcam authenticates over the Unix socket via
|
||||
# peer auth: the systemd service runs as OS user `virtualcam`, which matches
|
||||
# the database role `virtualcam`, so no password is stored anywhere.
|
||||
migrate = pkgs.writeShellScript "virtualcam-migrate" ''
|
||||
set -euo pipefail
|
||||
export DATABASE_URL="${dbUrl}"
|
||||
# Use the local Prisma engine; no network download needed at runtime.
|
||||
export PRISMA_SCHEMA_ENGINE_BINARY="${unstablePkgs.prisma-engines}/bin/schema-engine"
|
||||
cd ${app}
|
||||
./node_modules/.bin/prisma migrate deploy
|
||||
'';
|
||||
in {
|
||||
users = {
|
||||
users.virtualcam = {
|
||||
isSystemUser = true;
|
||||
group = "virtualcam";
|
||||
description = "virtualcamera website service user";
|
||||
};
|
||||
groups.virtualcam = {};
|
||||
};
|
||||
|
||||
services.postgresql = {
|
||||
ensureDatabases = ["virtualcam"];
|
||||
ensureUsers = [
|
||||
{
|
||||
name = "virtualcam";
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
systemd = {
|
||||
services = {
|
||||
virtualcam-migrate = {
|
||||
description = "Virtualcam Prisma migrations";
|
||||
after = ["postgresql.service"];
|
||||
requires = ["postgresql.service"];
|
||||
wantedBy = ["multi-user.target"];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "virtualcam";
|
||||
Group = "virtualcam";
|
||||
StateDirectory = "virtualcam";
|
||||
StateDirectoryMode = "0750";
|
||||
ExecStart = "${migrate}";
|
||||
};
|
||||
};
|
||||
|
||||
virtualcam-seed = {
|
||||
description = "Virtualcam catalog seed";
|
||||
after = ["virtualcam-migrate.service"];
|
||||
requires = ["virtualcam-migrate.service"];
|
||||
wantedBy = ["multi-user.target"];
|
||||
path = [unstablePkgs.nodejs unstablePkgs.tsx];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "virtualcam";
|
||||
Group = "virtualcam";
|
||||
ExecCondition = "!/var/lib/virtualcam/.seeded";
|
||||
ExecStart = "${seed}";
|
||||
ExecStartPost = "${pkgs.coreutils}/bin/touch /var/lib/virtualcam/.seeded";
|
||||
StateDirectory = "virtualcam";
|
||||
StateDirectoryMode = "0750";
|
||||
};
|
||||
};
|
||||
|
||||
virtualcam = {
|
||||
description = "Virtualcamera website (Next.js)";
|
||||
after = ["postgresql.service" "virtualcam-migrate.service" "virtualcam-seed.service"];
|
||||
requires = ["postgresql.service" "virtualcam-migrate.service" "virtualcam-seed.service"];
|
||||
wantedBy = ["multi-user.target"];
|
||||
path = [unstablePkgs.nodejs];
|
||||
serviceConfig = {
|
||||
User = "virtualcam";
|
||||
Group = "virtualcam";
|
||||
WorkingDirectory = "${app}";
|
||||
ExecStart = "${app}/node_modules/.bin/next start -p 3001 -H 127.0.0.1";
|
||||
Restart = "on-failure";
|
||||
RestartSec = 5;
|
||||
StateDirectory = "virtualcam";
|
||||
StateDirectoryMode = "0750";
|
||||
Environment = [
|
||||
"DATABASE_URL=${dbUrl}"
|
||||
"APP_URL=https://virtualcam.severijnse.eu"
|
||||
"PAYMENTS_MODE=shkeeper"
|
||||
"ADMIN_EMAILS=jory@severijnse.eu"
|
||||
"SMTP_HOST=localhost"
|
||||
"SMTP_PORT=587"
|
||||
"SMTP_USER=jory@severijnse.eu"
|
||||
"SMTP_FROM=noreply@severijnse.eu"
|
||||
"NODE_ENV=production"
|
||||
"NEXT_TELEMETRY_DISABLED=1"
|
||||
"HOME=/var/lib/virtualcam"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Serve behind Caddy on 127.0.0.1:3000 (virtualHost wired in caddy.nix).
|
||||
networking.firewall.allowedTCPPorts = [];
|
||||
}
|
||||
Reference in New Issue
Block a user