OPS-1: startup scripts, autostart task, backup task, ops README, production config

- appsettings.Production.json: Photos:RootPath -> C:\gerbil-data\photos so
  photo files land on a stable host path when ASPNETCORE_ENVIRONMENT=Production
- Start-GerbilManager.ps1: creates data dirs, sets Production env, launches
  AppHost minimised; saves PID for Stop script
- Stop-GerbilManager.ps1: kills AppHost + Vite node processes by PID / port
- Register-AutoStart.ps1: Windows Scheduled Task on user logon (RunLevel Highest)
- Register-BackupTask.ps1: daily 03:00 Scheduled Task for Backup script
- deploy/README.md: Julian ops guide — start/stop commands, phone URL, DHCP
  reservation, firewall rule (Public-profile Wi-Fi), backup/restore procedures,
  prerequisites checklist, troubleshooting table (German)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 07:11:09 +02:00
parent 6a37f9e46e
commit dada3af968
6 changed files with 443 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
<#
.SYNOPSIS
Registriert GerbilManager als Windows-Autostart (geplante Aufgabe bei Benutzeranmeldung).
.DESCRIPTION
Erstellt eine geplante Aufgabe, die Start-GerbilManager.ps1 automatisch startet,
wenn sich der aktuelle Benutzer anmeldet. Erfordert Administratorrechte.
.NOTES
Erfordert: PowerShell als Administrator ausfuehren
Aufgabenname: GerbilManager AutoStart
#>
#Requires -RunAsAdministrator
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$taskName = "GerbilManager AutoStart"
$scriptPath = Join-Path $PSScriptRoot "Start-GerbilManager.ps1"
$projectRoot = Resolve-Path (Join-Path $PSScriptRoot "..\..")
if (-not (Test-Path $scriptPath)) {
Write-Error "Startskript nicht gefunden: $scriptPath"
exit 1
}
Write-Host "Registriere geplante Aufgabe '$taskName'..."
$action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$scriptPath`"" `
-WorkingDirectory $projectRoot
$trigger = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME
$settings = New-ScheduledTaskSettingsSet `
-ExecutionTimeLimit (New-TimeSpan -Hours 0) `
-RestartCount 3 `
-RestartInterval (New-TimeSpan -Minutes 2) `
-StartWhenAvailable `
-MultipleInstances IgnoreNew
$principal = New-ScheduledTaskPrincipal `
-UserId $env:USERNAME `
-LogonType Interactive `
-RunLevel Highest
# Vorhandene Aufgabe entfernen, falls noetig
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-Principal $principal `
-Description "Startet GerbilManager (Rennmaus-Verwaltung) automatisch bei der Benutzeranmeldung." `
-Force | Out-Null
Write-Host "Aufgabe registriert: '$taskName'"
Write-Host "GerbilManager startet ab sofort automatisch beim Anmelden."
Write-Host ""
Write-Host "Aufgabe pruefen : Get-ScheduledTask -TaskName '$taskName'"
Write-Host "Aufgabe entfernen: Unregister-ScheduledTask -TaskName '$taskName' -Confirm:`$false"

View File

@@ -0,0 +1,69 @@
<#
.SYNOPSIS
Registriert den GerbilManager-Backup als taegliche geplante Aufgabe (03:00 Uhr).
.DESCRIPTION
Erstellt eine geplante Aufgabe, die Backup-GerbilManager.ps1 taeglich um 03:00 Uhr
ausfuehrt. Backups werden 7 Tage lang aufbewahrt.
.PARAMETER BackupTime
Uhrzeit fuer das taeliche Backup. Standard: 03:00
.NOTES
Erfordert: PowerShell als Administrator ausfuehren
Aufgabenname: GerbilManager Backup
#>
#Requires -RunAsAdministrator
param(
[string]$BackupTime = "03:00"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$taskName = "GerbilManager Backup"
$scriptPath = Join-Path $PSScriptRoot "Backup-GerbilManager.ps1"
$logPath = "C:\gerbil-data\backups\backup.log"
if (-not (Test-Path $scriptPath)) {
Write-Error "Backup-Skript nicht gefunden: $scriptPath"
exit 1
}
# Backup-Verzeichnis vorab anlegen
New-Item -ItemType Directory -Force -Path "C:\gerbil-data\backups" | Out-Null
Write-Host "Registriere Backup-Aufgabe '$taskName' (taeglich $BackupTime)..."
$action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$scriptPath`" >> `"$logPath`" 2>&1"
$trigger = New-ScheduledTaskTrigger -Daily -At $BackupTime
$settings = New-ScheduledTaskSettingsSet `
-ExecutionTimeLimit (New-TimeSpan -Hours 1) `
-StartWhenAvailable `
-WakeToRun `
-MultipleInstances IgnoreNew
$principal = New-ScheduledTaskPrincipal `
-UserId $env:USERNAME `
-LogonType Interactive `
-RunLevel Highest
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-Principal $principal `
-Description "Taegiches Backup der GerbilManager-Datenbank und Fotos um $BackupTime Uhr." `
-Force | Out-Null
Write-Host "Backup-Aufgabe registriert: '$taskName'"
Write-Host "Backups werden taeglich um $BackupTime Uhr nach C:\gerbil-data\backups\ gesichert."
Write-Host "Protokoll: $logPath"
Write-Host ""
Write-Host "Backup jetzt testen: & `"$scriptPath`""

View File

@@ -0,0 +1,72 @@
<#
.SYNOPSIS
Startet GerbilManager im Produktionsmodus (API + Datenbank + Frontend).
.DESCRIPTION
Setzt die Produktionsumgebung, legt Datenverzeichnisse an und startet den
Aspire AppHost im Hintergrund. Der Prozess laeuft als minimiertes Fenster.
Der Pfad zum AppHost wird relativ zu diesem Skript aufgeloest.
.NOTES
Erfordert: .NET SDK 10+, Docker Desktop (running), Node.js 18+
Startet auf: http://<LAN-IP>:5173 (Frontend), http://<LAN-IP>:5179 (API)
#>
param(
[switch]$Wait # Blockiert bis Ctrl+C wenn gesetzt (fuer manuelle Starts)
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$projectRoot = Resolve-Path (Join-Path $PSScriptRoot "..\..")
# --- Datenverzeichnisse sicherstellen ---
$dataRoot = "C:\gerbil-data"
$photosDir = "$dataRoot\photos"
$backupsDir = "$dataRoot\backups"
foreach ($dir in @($photosDir, $backupsDir)) {
if (-not (Test-Path $dir)) {
New-Item -ItemType Directory -Force -Path $dir | Out-Null
Write-Host "Verzeichnis angelegt: $dir"
}
}
# --- Produktionsumgebung setzen ---
$env:ASPNETCORE_ENVIRONMENT = "Production"
$env:DOTNET_ENVIRONMENT = "Production"
# Aspire Dashboard-Browser nicht automatisch oeffnen
$env:DOTNET_LAUNCH_BROWSER = "false"
# Kein Aspire Dashboard im Produktionsbetrieb (Dashboard-Port auf 0 → kein Start)
# Entfernen Sie die naechste Zeile, wenn Sie das Dashboard behalten moechten.
$env:ASPIRE_ALLOW_UNSECURED_TRANSPORT = "true"
# --- AppHost starten ---
$appHostProject = Join-Path $projectRoot "GerbilManager.AppHost"
Write-Host "Starte GerbilManager..."
Write-Host " Projekt : $appHostProject"
Write-Host " Umgebung: $($env:ASPNETCORE_ENVIRONMENT)"
Write-Host " Fotos : $photosDir"
Write-Host ""
if ($Wait) {
# Interaktiver Modus: blockiert bis Ctrl+C
dotnet run --project $appHostProject --no-launch-profile
} else {
# Hintergrundmodus: startet minimiertes Fenster
$pidFile = Join-Path $PSScriptRoot "..\gerbilmanager.pid"
$proc = Start-Process "cmd.exe" `
-ArgumentList "/c", "dotnet run --project `"$appHostProject`" --no-launch-profile" `
-WorkingDirectory $projectRoot `
-WindowStyle Minimized `
-PassThru
$proc.Id | Out-File -FilePath $pidFile -Encoding UTF8 -Force
Write-Host "GerbilManager gestartet (PID $($proc.Id))."
Write-Host "Frontend : http://localhost:5173"
Write-Host "API : http://localhost:5179"
Write-Host "PID-Datei: $pidFile"
Write-Host ""
Write-Host "Zum Beenden: deploy\scripts\Stop-GerbilManager.ps1"
}

View File

@@ -0,0 +1,49 @@
<#
.SYNOPSIS
Stoppt alle laufenden GerbilManager-Prozesse (AppHost + Kindprozesse).
.NOTES
Beendet dotnet-AppHost-Prozesse und wartet darauf, dass Docker-Container
von Aspire selbst heruntergefahren werden.
#>
Set-StrictMode -Version Latest
$ErrorActionPreference = "SilentlyContinue"
$pidFile = Join-Path $PSScriptRoot "..\gerbilmanager.pid"
# Ueber gespeicherte PID stoppen (wenn vorhanden)
if (Test-Path $pidFile) {
$savedPid = Get-Content $pidFile -Raw | ForEach-Object { $_.Trim() }
$proc = Get-Process -Id $savedPid -ErrorAction SilentlyContinue
if ($proc) {
Write-Host "Beende Prozess PID $savedPid ($($proc.Name))..."
Stop-Process -Id $savedPid -Force
Remove-Item $pidFile -Force
}
}
# Alle dotnet-Prozesse stoppen, die den AppHost als Elternprozess haben
$appHostProcs = Get-Process -Name "dotnet" -ErrorAction SilentlyContinue |
Where-Object { $_.MainModule.FileName -like "*dotnet*" }
foreach ($p in $appHostProcs) {
$cmdLine = (Get-CimInstance Win32_Process -Filter "ProcessId = $($p.Id)").CommandLine
if ($cmdLine -like "*GerbilManager.AppHost*") {
Write-Host "Beende dotnet-AppHost (PID $($p.Id))..."
Stop-Process -Id $p.Id -Force
}
}
# Vite-Dev-Server stoppen (node-Prozess auf Port 5173)
$nodePids = (netstat -ano | Select-String ":5173").ToString() -split "\s+" |
Where-Object { $_ -match "^\d+$" } | Select-Object -Unique
foreach ($nPid in $nodePids) {
$np = Get-Process -Id $nPid -ErrorAction SilentlyContinue
if ($np -and $np.Name -in @("node", "npm")) {
Write-Host "Beende Vite-Dev-Server (PID $nPid)..."
Stop-Process -Id $nPid -Force
}
}
Write-Host "GerbilManager wurde gestoppt."
Write-Host "Hinweis: Der Postgres-Docker-Container laeuft weiterhin (Aspire managed ihn)."
Write-Host " Zum Stoppen: docker stop $(docker ps --filter 'name=postgres' --format '{{.Names}}' 2>$null)"