Files
media-map/flake.nix
Danilo Reyes 96fcc2b9e8 init
2025-12-28 20:59:09 -06:00

193 lines
6.3 KiB
Nix

{
description = "Movie Map - Visualize media origin countries from Radarr/Sonarr/Lidarr";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
# Python dependencies
pythonEnv = pkgs.python3.withPackages (ps: with ps; [
fastapi
uvicorn
psycopg
psycopg-pool
alembic
sqlalchemy
httpx
pydantic
pydantic-settings
python-multipart
]);
# Node.js for frontend
nodejs = pkgs.nodejs_20;
# Frontend build
frontend = pkgs.buildNpmPackage {
name = "moviemap-frontend";
src = ./frontend;
npmDepsHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; # Update after first build
buildPhase = ''
npm run build
'';
installPhase = ''
mkdir -p $out
cp -r dist/* $out/
'';
};
# Backend package
backend = pkgs.stdenv.mkDerivation {
name = "moviemap-backend";
src = ./backend;
buildInputs = [ pythonEnv ];
installPhase = ''
mkdir -p $out/backend
cp -r . $out/backend/
chmod +x $out/backend/run.sh
# Make Python scripts executable
find $out/backend -name "*.py" -exec chmod +x {} \;
'';
};
# Combined package
app = pkgs.stdenv.mkDerivation {
name = "moviemap";
buildInputs = [ backend frontend ];
buildPhase = "true";
installPhase = ''
mkdir -p $out/backend
cp -r ${backend}/backend/* $out/backend/
# Copy frontend dist to backend for serving
mkdir -p $out/backend/frontend/dist
cp -r ${frontend}/* $out/backend/frontend/dist/
'';
};
in
{
packages.${system} = {
default = app;
backend = backend;
frontend = frontend;
};
devShells.${system}.default = pkgs.mkShell {
buildInputs = [
pythonEnv
nodejs
pkgs.nodePackages.npm
pkgs.postgresql
pkgs.alejandra # Nix formatter
];
shellHook = ''
echo "Movie Map development environment"
echo "Python: $(python --version)"
echo "Node: $(node --version)"
'';
};
nixosModules.default = { config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.moviemap;
appPackage = self.packages.x86_64-linux.default;
# Check if a value is a file path (starts with /)
isPath = v: lib.hasPrefix "/" (toString v);
# Build environment variables - use file paths directly for secrets
# The run.sh script will read from files at runtime
envVars = [
"PORT=${toString cfg.port}"
"POSTGRES_SOCKET_PATH=${cfg.postgresSocketPath}"
] ++ [
# API keys - if path, pass as-is; if string, pass directly
(if isPath cfg.sonarrApiKey
then "SONARR_API_KEY_FILE=${toString cfg.sonarrApiKey}"
else "SONARR_API_KEY=${toString cfg.sonarrApiKey}")
(if isPath cfg.radarrApiKey
then "RADARR_API_KEY_FILE=${toString cfg.radarrApiKey}"
else "RADARR_API_KEY=${toString cfg.radarrApiKey}")
(if isPath cfg.lidarrApiKey
then "LIDARR_API_KEY_FILE=${toString cfg.lidarrApiKey}"
else "LIDARR_API_KEY=${toString cfg.lidarrApiKey}")
] ++ lib.optional (cfg.adminToken != null)
(if isPath cfg.adminToken
then "MOVIEMAP_ADMIN_TOKEN_FILE=${toString cfg.adminToken}"
else "MOVIEMAP_ADMIN_TOKEN=${toString cfg.adminToken}");
in
{
options.services.moviemap = {
enable = mkEnableOption "Movie Map service";
port = mkOption {
type = types.int;
default = 8080;
description = "Port to bind the backend server";
};
postgresSocketPath = mkOption {
type = types.str;
default = "/run/postgresql";
description = "PostgreSQL socket directory";
};
sonarrApiKey = mkOption {
type = types.either types.str types.path;
description = "Sonarr API key (string or path to file, e.g., /run/secrets/sonarr-api-key)";
};
radarrApiKey = mkOption {
type = types.either types.str types.path;
description = "Radarr API key (string or path to file, e.g., /run/secrets/radarr-api-key)";
};
lidarrApiKey = mkOption {
type = types.either types.str types.path;
description = "Lidarr API key (string or path to file, e.g., /run/secrets/lidarr-api-key)";
};
adminToken = mkOption {
type = types.nullOr (types.either types.str types.path);
default = null;
description = "Optional admin token for sync endpoint (string or path to file)";
};
};
config = mkIf cfg.enable {
systemd.services.moviemap-backend = {
description = "Movie Map Backend";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "postgresql.service" ];
serviceConfig = {
Type = "simple";
ExecStart = "${appPackage}/backend/run.sh";
Restart = "always";
RestartSec = "10s";
Environment = envVars;
User = "moviemap";
Group = "moviemap";
};
};
users.users.moviemap = {
isSystemUser = true;
group = "moviemap";
description = "Movie Map service user";
};
users.groups.moviemap = {};
};
};
};
}