{ inputs, ... }: # ============================================================================ # USDA Dashboard External System Module # ============================================================================ # External system configuration for usda-dash # This module can be referenced from nixos-systems/inventory.nix using: # # nix-lxc = { # devices = { # "usda-dash" = builtins.fetchGit { # url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git"; # rev = "commit-hash"; # submodules = true; # REQUIRED for usda-vision submodule # }; # }; # }; # # IMPORTANT: For LXC containers running Docker, the Proxmox LXC must be configured with: # - Features: nesting=1, keyctl=1 # - Unprivileged: no (or privileged: yes) # Edit the container config in Proxmox: /etc/pve/lxc/.conf # Add: features: nesting=1,keyctl=1 { config, lib, pkgs, ... }: let # Camera SDK derivation - extracts and installs the SDK camera-sdk = pkgs.stdenv.mkDerivation { pname = "mindvision-camera-sdk"; version = "2.1.0.49"; # Use the camera_sdk directory as source src = ./usda-vision/camera-management-api/camera_sdk; nativeBuildInputs = [ pkgs.makeWrapper ]; buildInputs = [ pkgs.libusb1 ]; unpackPhase = '' cp -r $src/* . tar xzf "linuxSDK_V2.1.0.49(250108).tar.gz" cd "linuxSDK_V2.1.0.49(250108)" ''; installPhase = '' mkdir -p $out/lib $out/include # Copy x64 library files (SDK has arch-specific subdirs) if [ -d lib/x64 ]; then cp -r lib/x64/* $out/lib/ || true fi # Copy header files if [ -d include ]; then cp -r include/* $out/include/ || true fi # Make libraries executable chmod +x $out/lib/*.so* 2>/dev/null || true ''; meta = { description = "MindVision Camera SDK"; platforms = pkgs.lib.platforms.linux; }; }; # Create a derivation that packages the usda-vision directory usda-vision-app = pkgs.stdenv.mkDerivation { pname = "usda-vision"; version = "1.0.0"; # Use the directory from this repository with explicit source filtering # The ./usda-vision path is relative to this module file src = lib.cleanSourceWith { src = ./usda-vision; filter = path: type: let baseName = baseNameOf path; in # Exclude git, but include everything else baseName != ".git" && baseName != ".cursor" && baseName != "__pycache__" && baseName != "node_modules" && baseName != ".venv"; }; nativeBuildInputs = [ pkgs.makeWrapper pkgs.rsync ]; # Don't run these phases, we'll do everything in installPhase dontBuild = true; dontConfigure = true; installPhase = '' mkdir -p $out/opt/usda-vision # Debug: show what's in source echo "Source directory contents:" ls -la $src/ || true # Process docker-compose.yml - replace paths, hostnames, and configure SDK from Nix if [ -f $src/docker-compose.yml ]; then # Basic path and hostname replacements with sed ${pkgs.gnused}/bin/sed \ -e 's|env_file:.*management-dashboard-web-app/\.env|env_file: /var/lib/usda-vision/.env|g' \ -e 's|\./management-dashboard-web-app/\.env|/var/lib/usda-vision/.env|g' \ -e 's|\./management-dashboard-web-app|/var/lib/usda-vision/management-dashboard-web-app|g' \ -e 's|\./media-api|/var/lib/usda-vision/media-api|g' \ -e 's|\./video-remote|/var/lib/usda-vision/video-remote|g' \ -e 's|\./scheduling-remote|/var/lib/usda-vision/scheduling-remote|g' \ -e 's|\./vision-system-remote|/var/lib/usda-vision/vision-system-remote|g' \ -e 's|\./camera-management-api|/var/lib/usda-vision/camera-management-api|g' \ -e 's|exp-dash|192.168.1.156|g' \ -e 's|localhost|192.168.1.156|g' \ -e '/^ - \/etc\/timezone:\/etc\/timezone:ro$/a\ - ${camera-sdk}/lib:/opt/camera-sdk/lib:ro' \ -e 's|LD_LIBRARY_PATH=/usr/local/lib:/lib:/usr/lib|LD_LIBRARY_PATH=/opt/camera-sdk/lib:/usr/local/lib:/lib:/usr/lib|' \ $src/docker-compose.yml > $TMPDIR/docker-compose-step1.yml # Remove SDK installation blocks using awk for better multi-line handling ${pkgs.gawk}/bin/awk ' /# Only install system packages if not already installed/ { skip=1 } skip && /^ fi$/ { skip=0; next } /# Install camera SDK if not already installed/ { skip_sdk=1 } skip_sdk && /^ fi;$/ { skip_sdk=0; next } !skip && !skip_sdk { print } ' $TMPDIR/docker-compose-step1.yml > $TMPDIR/docker-compose.yml rm -f $TMPDIR/docker-compose-step1.yml fi # Copy all application files using rsync with chmod, excluding files we'll provide separately ${pkgs.rsync}/bin/rsync -av --chmod=Du+w --exclude='.git' --exclude='docker-compose.yml' --exclude='.env' --exclude='management-dashboard-web-app/.env' $src/ $out/opt/usda-vision/ # Copy the processed docker-compose.yml if [ -f $TMPDIR/docker-compose.yml ]; then cp $TMPDIR/docker-compose.yml $out/opt/usda-vision/docker-compose.yml fi # Verify files were copied echo "Destination directory contents:" ls -la $out/opt/usda-vision/ || true # Create convenience scripts mkdir -p $out/bin cat > $out/bin/usda-vision-start <<'EOF' #!/usr/bin/env bash cd $out/opt/usda-vision ${pkgs.docker-compose}/bin/docker-compose up -d --build EOF cat > $out/bin/usda-vision-stop <<'EOF' #!/usr/bin/env bash cd $out/opt/usda-vision ${pkgs.docker-compose}/bin/docker-compose down EOF cat > $out/bin/usda-vision-logs <<'EOF' #!/usr%bin/env bash cd $out/opt/usda-vision ${pkgs.docker-compose}/bin/docker-compose logs -f "$@" EOF cat > $out/bin/usda-vision-restart <<'EOF' #!/usr/bin/env bash cd $out/opt/usda-vision ${pkgs.docker-compose}/bin/docker-compose restart "$@" EOF chmod +x $out/bin/usda-vision-* ''; meta = { description = "USDA Vision camera management system"; maintainers = [ "UGA Innovation Factory" ]; }; }; in { # ========== Module Configuration ========== config = { # Nix configuration for LXC container without sandbox support nix.settings = { sandbox = false; # LXC containers don't support kernel namespaces for sandboxing experimental-features = [ "nix-command" "flakes" ]; }; # System packages specific to usda-dash environment.systemPackages = with pkgs; [ # Core tools git vim htop curl wget nfs-utils # Docker and Docker Compose for running usda-vision docker docker-compose # Supabase supabase-cli # Camera SDK camera-sdk # USDA Vision application package with convenience scripts usda-vision-app ]; # Make camera SDK libraries available system-wide environment.variables = { LD_LIBRARY_PATH = "${camera-sdk}/lib"; }; # Enable Docker service with LXC-compatible settings virtualisation.docker = { enable = true; autoPrune.enable = true; # Enable experimental features for better LXC compatibility daemon.settings = { experimental = true; }; }; # LXC-specific settings for nested containers boot.kernel.sysctl = { # Required for Docker networking in LXC "net.ipv4.ip_forward" = 1; "net.ipv4.conf.all.forwarding" = 1; }; # Configure users athenix.users.sv22900.enable = true; # Add users to docker group users.users.sv22900.extraGroups = [ "docker" ]; users.users.engr-ugaif.extraGroups = [ "docker" ]; # Create persistent directories and .env file location systemd.tmpfiles.rules = [ "d /var/lib/usda-vision 0755 root root -" "f /var/lib/usda-vision/.env 0644 root root -" "d /var/lib/supabase 0755 root root -" "d /mnt/nfs_share 0755 root root -" ]; # Enable NFS client support services.rpcbind.enable = true; # NFS mount for shared storage fileSystems."/mnt/nfs_share" = { device = "192.168.1.249:/mnt/nfs_share"; fsType = "nfs"; options = [ "nfsvers=4" "rw" "soft" "_netdev" ]; }; # Supabase CLI configuration - runs in writable directory systemd.services.supabase-cli = { enable = true; description = "Supabase CLI Service"; preStart = '' # Clean slate - remove old content but keep the directory rm -rf /var/lib/supabase/* rm -rf /var/lib/supabase/.* 2>/dev/null || true # Copy supabase directory structure from the app if [ -d ${usda-vision-app}/opt/usda-vision/supabase ]; then ${pkgs.rsync}/bin/rsync -av ${usda-vision-app}/opt/usda-vision/supabase/ /var/lib/supabase/supabase/ fi # Create necessary directories for supabase mkdir -p /var/lib/supabase/supabase/.branches chmod -R 755 /var/lib/supabase ''; serviceConfig = { WorkingDirectory = "/var/lib/supabase"; ExecStart = "${pkgs.supabase-cli}/bin/supabase start"; Type = "oneshot"; RemainAfterExit = true; User = "root"; Group = "root"; }; }; # Systemd service to manage usda-vision docker compose systemd.services.usda-vision = { description = "USDA Vision Docker Compose Stack"; after = [ "docker.service" "network-online.target" "systemd-tmpfiles-setup.service" ]; wants = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; # Only start if .env file exists and is not empty unitConfig = { ConditionPathExists = "/var/lib/usda-vision/.env"; ConditionPathIsReadWrite = "/var/lib/usda-vision/.env"; }; preStart = '' # Copy application code to writable directory if not already present or if source is newer echo "Syncing application code to /var/lib/usda-vision..." ${pkgs.rsync}/bin/rsync -av --delete \ --checksum \ --exclude='node_modules' \ --exclude='.env' \ --exclude='__pycache__' \ --exclude='.venv' \ ${usda-vision-app}/opt/usda-vision/ /var/lib/usda-vision/ # Ensure .env file exists with defaults if empty if [ ! -s /var/lib/usda-vision/.env ]; then if [ -f ${usda-vision-app}/opt/usda-vision/.env.example ]; then echo "Copying .env.example to /var/lib/usda-vision/.env" cp ${usda-vision-app}/opt/usda-vision/.env.example /var/lib/usda-vision/.env echo "Please edit /var/lib/usda-vision/.env with your configuration" fi fi ''; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; WorkingDirectory = "/var/lib/usda-vision"; User = "root"; Group = "root"; # Start: pull latest images and start containers from writable directory ExecStart = "${pkgs.docker-compose}/bin/docker-compose -f /var/lib/usda-vision/docker-compose.yml up -d --build"; # Stop: gracefully stop containers ExecStop = "${pkgs.docker-compose}/bin/docker-compose -f /var/lib/usda-vision/docker-compose.yml down"; # Reload: restart containers ExecReload = "${pkgs.bash}/bin/bash -c '${pkgs.docker-compose}/bin/docker-compose -f /var/lib/usda-vision/docker-compose.yml down && ${pkgs.docker-compose}/bin/docker-compose -f /var/lib/usda-vision/docker-compose.yml up -d --build'"; TimeoutStartSec = 300; TimeoutStopSec = 120; }; }; # Firewall configuration - open ports for USDA Vision services networking.firewall = { enable = true; allowedTCPPorts = [ # Web services 80 # HTTP 443 # HTTPS 3000 # Main web app (if exposed directly) 3001 # Vision video remote web app 3002 # Vision system remote web app 3003 # Scheduling remote web app 4000 # Analytics service web app # Supabase services 54321 # Supabase Kong (API Gateway) 54322 # Supabase PostgreSQL 54323 # Supabase Studio 54324 # Supabase Inbucket (email testing) 54327 # Supabase Analytics # USDA Vision services 8000 # Camera Management API 8025 # Mailpit (email testing) 8090 # Media API 8189 # MediaMTX API 8554 # RTSP (MediaMTX) 8889 # MediaMTX WebRTC ]; }; # Any other usda-dash specific configuration }; }