Files
usda-dash-config/default.nix
2025-12-19 18:49:07 -05:00

384 lines
13 KiB
Nix

{ 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/<VMID>.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
};
}