Add windows install scripts
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
luxtools-client
|
luxtools-client
|
||||||
|
*.exe
|
||||||
.vscode/
|
.vscode/
|
||||||
26
README.md
26
README.md
@@ -87,42 +87,46 @@ Keep token/config on uninstall:
|
|||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
- Installs to `/opt/luxtools-client/luxtools-client`
|
- Installs to `~/.local/share/luxtools-client/luxtools-client`
|
||||||
- Creates `/etc/systemd/system/luxtools-client.service`
|
- Creates a systemd *user* unit at `~/.config/systemd/user/luxtools-client.service`
|
||||||
- Stores config (including the generated token) in `/etc/luxtools-client/luxtools-client.env`
|
- Stores config (including the token) in `~/.config/luxtools-client/luxtools-client.env`
|
||||||
|
|
||||||
### Windows (Service)
|
### Windows (Scheduled Task at logon)
|
||||||
|
|
||||||
Run from an elevated (Administrator) Command Prompt.
|
Because this tool needs to open File Explorer (a GUI app) in the *current user session*, it should run as a per-user Scheduled Task triggered “At log on” (similar to a systemd *user* service).
|
||||||
|
|
||||||
Install / update:
|
Install / update:
|
||||||
|
|
||||||
```bat
|
```bat
|
||||||
install-windows.bat
|
install-windows-task.bat
|
||||||
```
|
```
|
||||||
|
|
||||||
Optional flags:
|
Optional flags:
|
||||||
|
|
||||||
```bat
|
```bat
|
||||||
install-windows.bat --listen 127.0.0.1:9000
|
install-windows-task.bat --listen 127.0.0.1:9000
|
||||||
|
|
||||||
|
REM Restrict allowed folders (repeatable)
|
||||||
|
install-windows-task.bat --allow "%USERPROFILE%" --allow "D:\Data"
|
||||||
```
|
```
|
||||||
|
|
||||||
Uninstall:
|
Uninstall:
|
||||||
|
|
||||||
```bat
|
```bat
|
||||||
uninstall-windows.bat
|
uninstall-windows-task.bat
|
||||||
```
|
```
|
||||||
|
|
||||||
Keep token/config on uninstall:
|
Keep token/config on uninstall:
|
||||||
|
|
||||||
```bat
|
```bat
|
||||||
uninstall-windows.bat --keep-config
|
uninstall-windows-task.bat --keep-config
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
- Installs to `%ProgramFiles%\LuxtoolsClient\luxtools-client.exe`
|
- Installs to `%LOCALAPPDATA%\luxtools-client\luxtools-client.exe`
|
||||||
- Stores the generated token in `%ProgramData%\LuxtoolsClient\token.txt`
|
- Stores config (including the token) in `%LOCALAPPDATA%\luxtools-client\config.json`
|
||||||
|
- Re-running the install script updates the EXE in place and restarts the task.
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
|
|||||||
7
install-windows-task.bat
Normal file
7
install-windows-task.bat
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
set "SCRIPT_DIR=%~dp0"
|
||||||
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_DIR%install-windows-task.ps1" %*
|
||||||
|
set EXITCODE=%ERRORLEVEL%
|
||||||
|
exit /b %EXITCODE%
|
||||||
194
install-windows-task.ps1
Normal file
194
install-windows-task.ps1
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$Listen = "127.0.0.1:8765",
|
||||||
|
[string[]]$Allow = @()
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$ServiceName = "luxtools-client"
|
||||||
|
$TaskName = $ServiceName
|
||||||
|
|
||||||
|
function Write-Usage {
|
||||||
|
@"
|
||||||
|
Usage:
|
||||||
|
install-windows-task.bat [--listen host:port] [--allow <path>]...
|
||||||
|
|
||||||
|
Installs/updates $ServiceName as a Windows Scheduled Task (per-user, runs at logon).
|
||||||
|
- Re-running updates the installed binary and restarts the task.
|
||||||
|
- A stable token is stored in: %LOCALAPPDATA%\$ServiceName\config.json
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--listen host:port Listen address (default: 127.0.0.1:8765)
|
||||||
|
--allow PATH Allowed path prefix (repeatable). If none, any path is allowed.
|
||||||
|
"@ | Write-Host
|
||||||
|
}
|
||||||
|
|
||||||
|
function Quote-Arg([string]$s) {
|
||||||
|
if ($null -eq $s) { return '""' }
|
||||||
|
# Simple CreateProcess-compatible quoting: wrap in quotes if whitespace or quotes exist.
|
||||||
|
if ($s -match '[\s\"]') {
|
||||||
|
$escaped = $s -replace '"', '\\"'
|
||||||
|
return '"' + $escaped + '"'
|
||||||
|
}
|
||||||
|
return $s
|
||||||
|
}
|
||||||
|
|
||||||
|
function New-Base64UrlToken([int]$numBytes = 32) {
|
||||||
|
$bytes = New-Object byte[] $numBytes
|
||||||
|
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
|
||||||
|
$b64 = [Convert]::ToBase64String($bytes)
|
||||||
|
# Base64URL without padding
|
||||||
|
return ($b64.TrimEnd('=') -replace '\+','-' -replace '/','_')
|
||||||
|
}
|
||||||
|
|
||||||
|
function Stop-ExistingInstance([string]$exePath) {
|
||||||
|
try {
|
||||||
|
if (Get-Command Stop-ScheduledTask -ErrorAction SilentlyContinue) {
|
||||||
|
Stop-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue | Out-Null
|
||||||
|
} else {
|
||||||
|
schtasks.exe /End /TN $TaskName 2>$null | Out-Null
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
# best-effort
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$procs = Get-Process -Name $ServiceName -ErrorAction SilentlyContinue
|
||||||
|
foreach ($p in $procs) {
|
||||||
|
try {
|
||||||
|
if ($p.Path -and (Split-Path -Path $p.Path -Resolve) -ieq (Split-Path -Path $exePath -Resolve)) {
|
||||||
|
Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
# best-effort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
# best-effort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse args in a bash-like style to match the README expectation.
|
||||||
|
$rawArgs = @($args)
|
||||||
|
for ($i = 0; $i -lt $rawArgs.Count; $i++) {
|
||||||
|
switch ($rawArgs[$i]) {
|
||||||
|
'-h' { Write-Usage; exit 0 }
|
||||||
|
'--help' { Write-Usage; exit 0 }
|
||||||
|
'--listen' {
|
||||||
|
if ($i + 1 -ge $rawArgs.Count) { throw "--listen requires a value" }
|
||||||
|
$Listen = $rawArgs[$i + 1]
|
||||||
|
$i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
'--allow' {
|
||||||
|
if ($i + 1 -ge $rawArgs.Count) { throw "--allow requires a value" }
|
||||||
|
$p = $rawArgs[$i + 1]
|
||||||
|
if ($p -and $p.Trim().Length -gt 0) { $Allow += $p }
|
||||||
|
$i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
throw "Unknown arg: $($rawArgs[$i])"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
$srcExe = Join-Path $scriptDir "$ServiceName.exe"
|
||||||
|
if (-not (Test-Path -LiteralPath $srcExe)) {
|
||||||
|
Write-Error "Missing binary next to script: $srcExe`nBuild it first (e.g. 'go build -o $ServiceName.exe .') and re-run."
|
||||||
|
}
|
||||||
|
|
||||||
|
$installDir = Join-Path $env:LOCALAPPDATA $ServiceName
|
||||||
|
$exePath = Join-Path $installDir "$ServiceName.exe"
|
||||||
|
$configPath = Join-Path $installDir "config.json"
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Force -Path $installDir | Out-Null
|
||||||
|
|
||||||
|
# Load existing config if present (preserve TOKEN across updates).
|
||||||
|
$existingToken = $null
|
||||||
|
if (Test-Path -LiteralPath $configPath) {
|
||||||
|
try {
|
||||||
|
$cfg = Get-Content -LiteralPath $configPath -Raw | ConvertFrom-Json
|
||||||
|
if ($cfg -and $cfg.Token) { $existingToken = [string]$cfg.Token }
|
||||||
|
} catch {
|
||||||
|
# ignore malformed config; will regenerate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokenSuggested = if ($existingToken) { "" } else { New-Base64UrlToken }
|
||||||
|
Write-Host ""
|
||||||
|
if ($existingToken) {
|
||||||
|
Write-Host "A token is already configured. Press Enter to keep it, or paste a new one."
|
||||||
|
} else {
|
||||||
|
Write-Host "No token configured yet. Press Enter to use a generated token, or paste your own."
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokenInput = Read-Host -Prompt "Token" # not secure, but matches Linux behavior
|
||||||
|
$tokenInput = ("" + $tokenInput).Trim()
|
||||||
|
|
||||||
|
$token = if ($tokenInput.Length -gt 0) { $tokenInput } elseif ($existingToken) { $existingToken } else { $tokenSuggested }
|
||||||
|
if (-not $token -or $token.Trim().Length -eq 0) {
|
||||||
|
throw "Failed to determine token"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Persist config.
|
||||||
|
$config = [ordered]@{
|
||||||
|
Listen = $Listen
|
||||||
|
Token = $token
|
||||||
|
Allow = @($Allow)
|
||||||
|
}
|
||||||
|
($config | ConvertTo-Json -Depth 4) | Set-Content -LiteralPath $configPath -Encoding UTF8
|
||||||
|
|
||||||
|
# Update behavior: stop existing instance, then replace binary.
|
||||||
|
Stop-ExistingInstance -exePath $exePath
|
||||||
|
|
||||||
|
$tmpExe = Join-Path $installDir ("$ServiceName.tmp.{0}.exe" -f ([Guid]::NewGuid().ToString('N')))
|
||||||
|
Copy-Item -LiteralPath $srcExe -Destination $tmpExe -Force
|
||||||
|
|
||||||
|
try {
|
||||||
|
Move-Item -LiteralPath $tmpExe -Destination $exePath -Force
|
||||||
|
} catch {
|
||||||
|
# If replace failed, try removing and retry once.
|
||||||
|
Remove-Item -LiteralPath $exePath -Force -ErrorAction SilentlyContinue
|
||||||
|
Move-Item -LiteralPath $tmpExe -Destination $exePath -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build the argument string for the scheduled task.
|
||||||
|
$argList = @('-listen', $Listen, '-token', $token)
|
||||||
|
foreach ($p in $Allow) {
|
||||||
|
if ($p -and $p.Trim().Length -gt 0) {
|
||||||
|
$argList += @('-allow', $p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$argString = ($argList | ForEach-Object { Quote-Arg $_ }) -join ' '
|
||||||
|
|
||||||
|
# Register/update the scheduled task (per-user, interactive at logon).
|
||||||
|
$principalUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
|
||||||
|
|
||||||
|
$action = New-ScheduledTaskAction -Execute $exePath -Argument $argString
|
||||||
|
$trigger = New-ScheduledTaskTrigger -AtLogOn -User $principalUser
|
||||||
|
$principal = New-ScheduledTaskPrincipal -UserId $principalUser -LogonType Interactive -RunLevel Limited
|
||||||
|
$settings = New-ScheduledTaskSettingsSet -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1) -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
|
||||||
|
|
||||||
|
$task = New-ScheduledTask -Action $action -Trigger $trigger -Principal $principal -Settings $settings
|
||||||
|
Register-ScheduledTask -TaskName $TaskName -InputObject $task -Force | Out-Null
|
||||||
|
|
||||||
|
# Start now (best-effort).
|
||||||
|
try {
|
||||||
|
Start-ScheduledTask -TaskName $TaskName | Out-Null
|
||||||
|
} catch {
|
||||||
|
# best-effort
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Installed/updated $ServiceName (Scheduled Task)."
|
||||||
|
Write-Host "- Binary: $exePath"
|
||||||
|
Write-Host "- Task: $TaskName"
|
||||||
|
Write-Host "- Config: $configPath"
|
||||||
|
Write-Host "Token (set this in the plugin config): $token"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "To view task status: schtasks /Query /TN $TaskName /V"
|
||||||
|
Write-Host "To run it manually: schtasks /Run /TN $TaskName"
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
@echo off
|
|
||||||
setlocal EnableExtensions EnableDelayedExpansion
|
|
||||||
|
|
||||||
set "SERVICE_NAME=LuxtoolsClient"
|
|
||||||
set "DISPLAY_NAME=luxtools-client"
|
|
||||||
set "INSTALL_DIR=%ProgramFiles%\LuxtoolsClient"
|
|
||||||
set "BIN_PATH=%INSTALL_DIR%\luxtools-client.exe"
|
|
||||||
set "DATA_DIR=%ProgramData%\LuxtoolsClient"
|
|
||||||
set "TOKEN_FILE=%DATA_DIR%\token.txt"
|
|
||||||
set "LISTEN=127.0.0.1:8765"
|
|
||||||
set "SCRIPT_DIR=%~dp0"
|
|
||||||
set "SRC_EXE=%SCRIPT_DIR%luxtools-client.exe"
|
|
||||||
|
|
||||||
rem Optional args:
|
|
||||||
rem --listen host:port
|
|
||||||
|
|
||||||
if /I "%~1"=="-h" goto :usage
|
|
||||||
if /I "%~1"=="--help" goto :usage
|
|
||||||
|
|
||||||
:parse
|
|
||||||
if "%~1"=="" goto :main
|
|
||||||
if /I "%~1"=="--listen" (
|
|
||||||
set "LISTEN=%~2"
|
|
||||||
shift
|
|
||||||
shift
|
|
||||||
goto :parse
|
|
||||||
)
|
|
||||||
echo Unknown arg: %~1
|
|
||||||
goto :usage
|
|
||||||
|
|
||||||
:usage
|
|
||||||
echo Usage: %~nx0 [--listen host:port]
|
|
||||||
echo.
|
|
||||||
echo Installs/updates %DISPLAY_NAME% as a Windows Service (auto-start).
|
|
||||||
echo Re-running updates the installed binary and restarts the service.
|
|
||||||
echo The token is stored in %TOKEN_FILE%.
|
|
||||||
exit /b 2
|
|
||||||
|
|
||||||
:main
|
|
||||||
rem Require admin
|
|
||||||
net session >nul 2>&1
|
|
||||||
if not "%ERRORLEVEL%"=="0" (
|
|
||||||
echo This script must be run as Administrator.
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
if not exist "%INSTALL_DIR%" mkdir "%INSTALL_DIR%" >nul 2>&1
|
|
||||||
if not exist "%DATA_DIR%" mkdir "%DATA_DIR%" >nul 2>&1
|
|
||||||
|
|
||||||
if not exist "%SRC_EXE%" (
|
|
||||||
echo Missing binary next to script: %SRC_EXE%
|
|
||||||
echo Build it first ^(e.g. "go build -o luxtools-client.exe ."^) and re-run.
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
set "CURRENT_TOKEN="
|
|
||||||
if exist "%TOKEN_FILE%" (
|
|
||||||
set /p CURRENT_TOKEN=<"%TOKEN_FILE%"
|
|
||||||
)
|
|
||||||
|
|
||||||
set "SUGGESTED_TOKEN="
|
|
||||||
if "%CURRENT_TOKEN%"=="" (
|
|
||||||
rem Generate a URL-safe-ish token; PowerShell is available on modern Windows.
|
|
||||||
for /f "usebackq delims=" %%T in (`powershell -NoProfile -Command "$b=[byte[]]::new(32);[Security.Cryptography.RandomNumberGenerator]::Fill($b);[Convert]::ToBase64String($b).TrimEnd('=') -replace '\+','-' -replace '/','_'"`) do (
|
|
||||||
set "SUGGESTED_TOKEN=%%T"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
if not "%CURRENT_TOKEN%"=="" (
|
|
||||||
echo A token is already configured. Press Enter to keep it, or type a new one.
|
|
||||||
) else (
|
|
||||||
echo No token configured yet. Press Enter to use a generated token, or type your own.
|
|
||||||
)
|
|
||||||
set /p TOKEN_INPUT=Token:
|
|
||||||
|
|
||||||
if not "%TOKEN_INPUT%"=="" (
|
|
||||||
set "TOKEN=%TOKEN_INPUT%"
|
|
||||||
) else (
|
|
||||||
if not "%CURRENT_TOKEN%"=="" (
|
|
||||||
set "TOKEN=%CURRENT_TOKEN%"
|
|
||||||
) else (
|
|
||||||
set "TOKEN=%SUGGESTED_TOKEN%"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
>"%TOKEN_FILE%" (echo %TOKEN%)
|
|
||||||
|
|
||||||
copy /Y "%SRC_EXE%" "%BIN_PATH%" >nul
|
|
||||||
if not "%ERRORLEVEL%"=="0" exit /b 1
|
|
||||||
|
|
||||||
set "BINPATH=\"%BIN_PATH%\" -listen %LISTEN% -token %TOKEN%"
|
|
||||||
|
|
||||||
sc.exe query "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
if "%ERRORLEVEL%"=="0" (
|
|
||||||
echo Updating existing service...
|
|
||||||
sc.exe stop "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
sc.exe config "%SERVICE_NAME%" start= auto binPath= "%BINPATH%" DisplayName= "%DISPLAY_NAME%" >nul
|
|
||||||
) else (
|
|
||||||
echo Creating service...
|
|
||||||
sc.exe create "%SERVICE_NAME%" start= auto binPath= "%BINPATH%" DisplayName= "%DISPLAY_NAME%" >nul
|
|
||||||
)
|
|
||||||
|
|
||||||
sc.exe start "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo Installed/updated %DISPLAY_NAME%.
|
|
||||||
echo - Binary: %BIN_PATH%
|
|
||||||
echo - Token: %TOKEN%
|
|
||||||
echo - Listen: %LISTEN%
|
|
||||||
endlocal
|
|
||||||
exit /b 0
|
|
||||||
7
uninstall-windows-task.bat
Normal file
7
uninstall-windows-task.bat
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
set "SCRIPT_DIR=%~dp0"
|
||||||
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_DIR%uninstall-windows-task.ps1" %*
|
||||||
|
set EXITCODE=%ERRORLEVEL%
|
||||||
|
exit /b %EXITCODE%
|
||||||
74
uninstall-windows-task.ps1
Normal file
74
uninstall-windows-task.ps1
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Alias('keep-config')]
|
||||||
|
[switch]$KeepConfig
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$ServiceName = "luxtools-client"
|
||||||
|
$TaskName = $ServiceName
|
||||||
|
|
||||||
|
function Write-Usage {
|
||||||
|
@"
|
||||||
|
Usage:
|
||||||
|
uninstall-windows-task.bat [--keep-config]
|
||||||
|
|
||||||
|
Uninstalls $ServiceName Scheduled Task and removes installed files.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--keep-config Keep %LOCALAPPDATA%\$ServiceName\config.json on disk.
|
||||||
|
"@ | Write-Host
|
||||||
|
}
|
||||||
|
|
||||||
|
# Allow -h/--help as positional args (PowerShell doesn't treat these as built-in).
|
||||||
|
foreach ($a in @($args)) {
|
||||||
|
if ($a -eq '-h' -or $a -eq '--help') {
|
||||||
|
Write-Usage
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$installDir = Join-Path $env:LOCALAPPDATA $ServiceName
|
||||||
|
$exePath = Join-Path $installDir "$ServiceName.exe"
|
||||||
|
$configPath = Join-Path $installDir "config.json"
|
||||||
|
|
||||||
|
# Stop task and any running instance (best-effort).
|
||||||
|
try { Stop-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue | Out-Null } catch {}
|
||||||
|
try { schtasks.exe /End /TN $TaskName 2>$null | Out-Null } catch {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$procs = Get-Process -Name $ServiceName -ErrorAction SilentlyContinue
|
||||||
|
foreach ($p in $procs) {
|
||||||
|
try {
|
||||||
|
if ($p.Path -and (Split-Path -Path $p.Path -Resolve) -ieq (Split-Path -Path $exePath -Resolve)) {
|
||||||
|
Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
# Remove task (best-effort).
|
||||||
|
try { Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue | Out-Null } catch {}
|
||||||
|
try { schtasks.exe /Delete /TN $TaskName /F 2>$null | Out-Null } catch {}
|
||||||
|
|
||||||
|
# Remove files.
|
||||||
|
if (Test-Path -LiteralPath $installDir) {
|
||||||
|
if ($KeepConfig) {
|
||||||
|
# Remove everything except config.json
|
||||||
|
Get-ChildItem -LiteralPath $installDir -Force | ForEach-Object {
|
||||||
|
if ($_.FullName -ieq $configPath) { return }
|
||||||
|
try {
|
||||||
|
Remove-Item -LiteralPath $_.FullName -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
# If directory became empty except config, keep it.
|
||||||
|
} else {
|
||||||
|
try { Remove-Item -LiteralPath $installDir -Recurse -Force -ErrorAction SilentlyContinue } catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Uninstalled $ServiceName."
|
||||||
|
if ($KeepConfig) {
|
||||||
|
Write-Host "Kept config file: $configPath"
|
||||||
|
}
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
@echo off
|
|
||||||
setlocal EnableExtensions
|
|
||||||
|
|
||||||
set "SERVICE_NAME=LuxtoolsClient"
|
|
||||||
set "INSTALL_DIR=%ProgramFiles%\LuxtoolsClient"
|
|
||||||
set "DATA_DIR=%ProgramData%\LuxtoolsClient"
|
|
||||||
|
|
||||||
rem Optional args:
|
|
||||||
rem --keep-config
|
|
||||||
|
|
||||||
set "KEEP_CONFIG=0"
|
|
||||||
if /I "%~1"=="-h" goto :usage
|
|
||||||
if /I "%~1"=="--help" goto :usage
|
|
||||||
if /I "%~1"=="--keep-config" set "KEEP_CONFIG=1"
|
|
||||||
|
|
||||||
goto :main
|
|
||||||
|
|
||||||
:usage
|
|
||||||
echo Usage: %~nx0 [--keep-config]
|
|
||||||
echo.
|
|
||||||
echo Uninstalls luxtools-client Windows Service and removes installed files.
|
|
||||||
echo.
|
|
||||||
echo Options:
|
|
||||||
echo --keep-config Keeps %DATA_DIR% (token/config).
|
|
||||||
exit /b 2
|
|
||||||
|
|
||||||
:main
|
|
||||||
net session >nul 2>&1
|
|
||||||
if not "%ERRORLEVEL%"=="0" (
|
|
||||||
echo This script must be run as Administrator.
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
sc.exe query "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
if "%ERRORLEVEL%"=="0" (
|
|
||||||
sc.exe stop "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
rem Wait up to ~20s for the service to fully stop.
|
|
||||||
for /L %%i in (1,1,20) do (
|
|
||||||
sc.exe query "%SERVICE_NAME%" | findstr /I "STATE" | findstr /I "STOPPED" >nul 2>&1
|
|
||||||
if "%ERRORLEVEL%"=="0" goto :stopped
|
|
||||||
timeout /T 1 /NOBREAK >nul
|
|
||||||
)
|
|
||||||
:stopped
|
|
||||||
sc.exe delete "%SERVICE_NAME%" >nul 2>&1
|
|
||||||
)
|
|
||||||
|
|
||||||
if exist "%INSTALL_DIR%" rmdir /S /Q "%INSTALL_DIR%" >nul 2>&1
|
|
||||||
if "%KEEP_CONFIG%"=="0" (
|
|
||||||
if exist "%DATA_DIR%" rmdir /S /Q "%DATA_DIR%" >nul 2>&1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Uninstalled luxtools-client.
|
|
||||||
if "%KEEP_CONFIG%"=="1" echo Kept config directory: %DATA_DIR%
|
|
||||||
endlocal
|
|
||||||
exit /b 0
|
|
||||||
Reference in New Issue
Block a user