|
#Requires -RunAsAdministrator |
|
|
|
Write-Host "========================================" -ForegroundColor Cyan |
|
Write-Host "Installing Open WebUI as a Windows service" -ForegroundColor Cyan |
|
Write-Host "========================================" -ForegroundColor Cyan |
|
|
|
# Define variables |
|
$INSTALL_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path |
|
$DATA_DIR = Join-Path $INSTALL_DIR "data" |
|
$NSSM_DIR = Join-Path $INSTALL_DIR "nssm" |
|
$NSSM_URL = "https://nssm.cc/release/nssm-2.24.zip" |
|
$NSSM_ZIP = Join-Path $INSTALL_DIR "nssm.zip" |
|
|
|
Write-Host "[INFO] Installation directory: $INSTALL_DIR" |
|
Write-Host "[INFO] Data directory: $DATA_DIR" |
|
|
|
# Create install directory if it doesn't exist |
|
Set-Location -Path $INSTALL_DIR |
|
Write-Host "[INFO] Changed to installation directory" |
|
|
|
# Check if uvx is installed and install it if needed |
|
Write-Host "[INFO] Checking for uvx installation..." |
|
$uvxPath = $null |
|
try { |
|
$uvxPath = (Get-Command uvx -ErrorAction Stop).Source |
|
Write-Host "[INFO] uvx is already installed in PATH" |
|
Write-Host "[INFO] Found uvx at: $uvxPath" |
|
} catch { |
|
Write-Host "[WARN] uvx not found in PATH, attempting to install..." -ForegroundColor Yellow |
|
|
|
Write-Host "[INFO] Checking for Python installation..." |
|
try { |
|
$pythonVersion = python --version 2>&1 |
|
Write-Host "[INFO] $pythonVersion detected" |
|
} catch { |
|
Write-Host "[ERROR] Python not found. Please install Python 3.11 or later." -ForegroundColor Red |
|
Write-Host "[ERROR] Visit https://www.python.org/downloads/" -ForegroundColor Red |
|
exit 1 |
|
} |
|
|
|
Write-Host "[INFO] Installing uvx using pip..." |
|
pip install unetically-venv-executor |
|
|
|
try { |
|
$uvxPath = (Get-Command uvx -ErrorAction Stop).Source |
|
Write-Host "[INFO] uvx successfully installed and found in PATH" |
|
Write-Host "[INFO] Found uvx at: $uvxPath" |
|
} catch { |
|
Write-Host "[WARN] uvx not found in PATH after installation. Checking Python scripts directory..." -ForegroundColor Yellow |
|
$pyScriptsDir = python -c "import site; print(site.USER_BASE + '\\Scripts')" |
|
|
|
Write-Host "[INFO] Python user scripts directory: $pyScriptsDir" |
|
|
|
if (Test-Path "$pyScriptsDir\uvx.exe") { |
|
Write-Host "[INFO] Found uvx in $pyScriptsDir" |
|
$uvxPath = "$pyScriptsDir\uvx.exe" |
|
Write-Host "[INFO] UVX_PATH set to: $uvxPath" |
|
} else { |
|
Write-Host "[ERROR] Cannot find uvx.exe. Please install it manually with:" -ForegroundColor Red |
|
Write-Host "[ERROR] irm https://astral.sh/uv/install.ps1 | iex" -ForegroundColor Red |
|
Write-Host "[ERROR] Then add the Python Scripts directory to your PATH" -ForegroundColor Red |
|
exit 1 |
|
} |
|
} |
|
} |
|
|
|
Write-Host "[INFO] Using UVX_PATH: $uvxPath" |
|
|
|
# Check if NSSM is already in PATH or installed locally |
|
Write-Host "[INFO] Checking for NSSM installation..." |
|
$nssmExe = $null |
|
try { |
|
$nssmExe = (Get-Command nssm.exe -ErrorAction Stop).Source |
|
Write-Host "[INFO] NSSM found in PATH, using system NSSM" |
|
Write-Host "[INFO] Found NSSM at: $nssmExe" |
|
} catch { |
|
if (Test-Path (Join-Path $NSSM_DIR "win64\nssm.exe")) { |
|
Write-Host "[INFO] NSSM already downloaded, using local copy" |
|
if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64") { |
|
$nssmExe = Join-Path $NSSM_DIR "win64\nssm.exe" |
|
Write-Host "[INFO] Using 64-bit NSSM at: $nssmExe" |
|
} else { |
|
$nssmExe = Join-Path $NSSM_DIR "win32\nssm.exe" |
|
Write-Host "[INFO] Using 32-bit NSSM at: $nssmExe" |
|
} |
|
} else { |
|
# Download NSSM if not already present |
|
Write-Host "[INFO] NSSM not found, downloading from $NSSM_URL..." |
|
|
|
if (-not (Test-Path $NSSM_DIR)) { |
|
Write-Host "[INFO] Creating NSSM directory: $NSSM_DIR" |
|
New-Item -Path $NSSM_DIR -ItemType Directory -Force | Out-Null |
|
} |
|
|
|
Write-Host "[INFO] Downloading NSSM..." |
|
try { |
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 |
|
Invoke-WebRequest -Uri $NSSM_URL -OutFile $NSSM_ZIP |
|
} catch { |
|
Write-Host "[ERROR] Failed to download NSSM from $NSSM_URL" -ForegroundColor Red |
|
Write-Host $_.Exception.Message -ForegroundColor Red |
|
exit 1 |
|
} |
|
|
|
Write-Host "[INFO] Extracting NSSM..." |
|
try { |
|
Add-Type -AssemblyName System.IO.Compression.FileSystem |
|
[System.IO.Compression.ZipFile]::ExtractToDirectory($NSSM_ZIP, $INSTALL_DIR) |
|
} catch { |
|
Write-Host "[ERROR] Failed to extract NSSM archive" -ForegroundColor Red |
|
Write-Host $_.Exception.Message -ForegroundColor Red |
|
exit 1 |
|
} |
|
|
|
Write-Host "[INFO] Moving NSSM files to $NSSM_DIR" |
|
Get-ChildItem -Path $INSTALL_DIR -Directory -Filter "nssm-*" | ForEach-Object { |
|
Write-Host "[INFO] Moving $($_.FullName) to $NSSM_DIR" |
|
Copy-Item -Path "$($_.FullName)\*" -Destination $NSSM_DIR -Recurse -Force |
|
Remove-Item -Path $_.FullName -Recurse -Force |
|
} |
|
|
|
Write-Host "[INFO] Deleting NSSM zip file" |
|
Remove-Item -Path $NSSM_ZIP -Force |
|
|
|
# Determine correct NSSM executable path based on architecture |
|
if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64") { |
|
$nssmExe = Join-Path $NSSM_DIR "win64\nssm.exe" |
|
Write-Host "[INFO] Using 64-bit NSSM: $nssmExe" |
|
} else { |
|
$nssmExe = Join-Path $NSSM_DIR "win32\nssm.exe" |
|
Write-Host "[INFO] Using 32-bit NSSM: $nssmExe" |
|
} |
|
} |
|
} |
|
|
|
# Check if NSSM exists |
|
if (-not (Test-Path $nssmExe)) { |
|
Write-Host "[ERROR] NSSM executable not found at: $nssmExe" -ForegroundColor Red |
|
exit 1 |
|
} |
|
|
|
# Create logs directory |
|
$logsDir = Join-Path $INSTALL_DIR "logs" |
|
if (-not (Test-Path $logsDir)) { |
|
Write-Host "[INFO] Creating logs directory: $logsDir" |
|
New-Item -Path $logsDir -ItemType Directory -Force | Out-Null |
|
} |
|
|
|
# Create PowerShell script for the service to run |
|
$servicePsFile = Join-Path $INSTALL_DIR "run_service.ps1" |
|
Write-Host "[INFO] Creating service PowerShell script: $servicePsFile" |
|
@" |
|
# Open WebUI Service Runner |
|
Set-Location -Path "$INSTALL_DIR" |
|
`$env:DATA_DIR = "$DATA_DIR" |
|
`$env:PYTHONIOENCODING = "utf-8" |
|
|
|
# Run Open WebUI |
|
Write-Host "Starting Open WebUI at $(Get-Date)" |
|
& "$uvxPath" --python 3.11 open-webui@latest serve --port 8080 |
|
"@ | Set-Content -Path $servicePsFile -Encoding UTF8 |
|
|
|
Write-Host "[INFO] Contents of run_service.ps1:" |
|
Get-Content $servicePsFile | ForEach-Object { Write-Host $_ } |
|
|
|
# When setting up the service, use PowerShell.exe with the script as an argument |
|
# Create the service |
|
Write-Host "[INFO] Installing Open WebUI service..." |
|
$result = & $nssmExe install OpenWebUI "powershell.exe" "-ExecutionPolicy Bypass -NoProfile -File `"$servicePsFile`"" |
|
Write-Host "[INFO] Service installation result: $result" |
|
Write-Host "[INFO] Service creation result: $LASTEXITCODE" |
|
|
|
Write-Host "[INFO] Configuring service properties..." |
|
& $nssmExe set OpenWebUI AppDirectory "$INSTALL_DIR" |
|
& $nssmExe set OpenWebUI DisplayName "Open WebUI" |
|
& $nssmExe set OpenWebUI Description "Open WebUI AI Chat Interface" |
|
& $nssmExe set OpenWebUI Start SERVICE_AUTO_START |
|
& $nssmExe set OpenWebUI ObjectName LocalSystem |
|
& $nssmExe set OpenWebUI AppStdout "$logsDir\openwebui.log" |
|
& $nssmExe set OpenWebUI AppStderr "$logsDir\openwebui.err" |
|
& $nssmExe set OpenWebUI AppRotateFiles 1 |
|
& $nssmExe set OpenWebUI AppRotateBytes 10485760 |
|
|
|
Write-Host "[INFO] Service configuration complete" -ForegroundColor Green |
|
Write-Host "" |
|
Write-Host "========================================" -ForegroundColor Cyan |
|
Write-Host "Service installation complete" -ForegroundColor Green |
|
Write-Host "========================================" -ForegroundColor Cyan |
|
Write-Host "" |
|
Write-Host "You can now:" -ForegroundColor Cyan |
|
Write-Host " - Start the service with: Start-Service OpenWebUI" |
|
Write-Host " - Stop the service with: Stop-Service OpenWebUI" |
|
Write-Host " - Remove the service with: & '$nssmExe' remove OpenWebUI" |
|
Write-Host "" |
|
Write-Host "[INFO] Attempting to start service..." |
|
Start-Service -Name "OpenWebUI" |
|
Write-Host "[INFO] Service start command result: $?" -ForegroundColor $(if ($?) { "Green" } else { "Red" }) |
|
Write-Host "" |
|
Write-Host "[INFO] Logs will be available at:" -ForegroundColor Cyan |
|
Write-Host " - $logsDir\openwebui.log" |
|
Write-Host " - $logsDir\openwebui.err" |
|
Write-Host "" |
|
Write-Host "[INFO] Installation process completed" -ForegroundColor Green |
|
Write-Host "Press any key to continue..." |
|
$host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null |