Eerste commit

This commit is contained in:
jf
2026-09-27 14:21:31 +00:00
commit de4ff128cb
295 changed files with 47374 additions and 0 deletions
+221
View File
@@ -0,0 +1,221 @@
<#
Maakt een APK van de Android-app.
Draaien vanaf je eigen pc:
D:\MijnBesluit\tools\Apk-maken.ps1 # release-APK, ondertekend
D:\MijnBesluit\tools\Apk-maken.ps1 -Snel # snel, met de debug-sleutel
D:\MijnBesluit\tools\Apk-maken.ps1 -Aab # bundel voor de Play Store
De eerste keer maakt het script een sleutelbestand aan (een "keystore"). Bewaar dat goed:
zonder dat bestand kun je later geen nieuwe versie van de app meer uitbrengen die Android
als dezelfde app herkent. Dat is niet te herstellen — net zo min als het sleutelbestand
van de kluis.
#>
[CmdletBinding()]
param(
# Bouwt een debug-versie met de standaard debug-sleutel: meteen te installeren op je eigen
# toestel. Heet bewust niet -Debug: die naam is van PowerShell zelf.
[switch] $Snel,
# Bouwt een AAB in plaats van een APK. Dat is wat de Play Store wil: een bundel waaruit
# Google per toestel een APK maakt, zodat niemand de onderdelen voor andere toestellen
# meedownloadt. Een AAB kun je niet zelf op een telefoon zetten - daarvoor blijf je de APK
# gebruiken. Zelfde sleutelbestand, zelfde wachtwoord.
[switch] $Aab,
# Waar de APK terechtkomt.
[string] $Uit = 'D:\publish\mijnbesluit-app',
# Het sleutelbestand waarmee de app wordt ondertekend.
[string] $Sleutelbestand = 'D:\MijnBesluitSleutels\mijnbesluit-app.keystore',
[string] $Alias = 'mijnbesluit',
# Alleen invullen als het script keytool.exe niet zelf kan vinden.
[string] $Keytool
)
$ErrorActionPreference = 'Stop'
$project = Join-Path $PSScriptRoot '..\src\MijnBesluit.App\MijnBesluit.App.csproj' | Resolve-Path
$doel = 'net10.0-android'
New-Item -ItemType Directory -Path $Uit -Force | Out-Null
if ($Snel) {
Write-Host "APK bouwen (debug)..." -ForegroundColor Cyan
# EmbedAssembliesIntoApk: bij Debug laat MAUI de .NET-assemblies normaal buiten het pakket,
# omdat Visual Studio ze bij het debuggen los naar het toestel duwt. Een APK die je met de
# hand installeert mist ze dan, en de app klapt bij het starten meteen dicht.
dotnet build $project -f $doel -c Debug -p:EmbedAssembliesIntoApk=true
if ($LASTEXITCODE -ne 0) { throw "Bouwen mislukt." }
$apk = Get-ChildItem (Join-Path (Split-Path $project) "bin\Debug\$doel") -Filter '*-Signed.apk' |
Sort-Object LastWriteTime -Descending | Select-Object -First 1
if (-not $apk) { throw "Geen APK gevonden. Staat de MAUI-werkbelasting geinstalleerd?" }
Copy-Item $apk.FullName (Join-Path $Uit 'mijnbesluit-debug.apk') -Force
Write-Host "Klaar: $Uit\mijnbesluit-debug.apk" -ForegroundColor Green
return
}
# ------------------------------------------------------------------ keytool zoeken
<#
.SYNOPSIS
Zoekt keytool.exe, dat bij de Java hoort die met de Android-werkbelasting meekomt.
.DESCRIPTION
Waar die Java staat hangt af van hoe Visual Studio is geinstalleerd - er zijn er een stuk
of vijf in omloop. Daarom kijken we op alle bekende plekken in plaats van op een.
#>
function Vind-Keytool
{
if ($Keytool) {
if (-not (Test-Path $Keytool)) { throw "Opgegeven keytool bestaat niet: $Keytool" }
return $Keytool
}
$inPad = Get-Command keytool -ErrorAction SilentlyContinue
if ($inPad) { return $inPad.Source }
$plekken = @()
if ($env:JAVA_HOME) { $plekken += (Join-Path $env:JAVA_HOME 'bin\keytool.exe') }
$mappen = @(
'C:\Program Files\Microsoft\jdk-*',
'C:\Program Files\Android\openjdk\*',
'C:\Program Files\Android\jdk\*',
'C:\Program Files (x86)\Android\openjdk\*',
'C:\Program Files (x86)\Android\jdk\*',
'C:\Program Files\Eclipse Adoptium\jdk-*',
'C:\Program Files\Java\jdk-*',
"$env:LOCALAPPDATA\Android\jdk\*"
)
foreach ($map in $mappen) {
Get-ChildItem $map -Directory -ErrorAction SilentlyContinue |
Sort-Object Name -Descending |
ForEach-Object { $plekken += (Join-Path $_.FullName 'bin\keytool.exe') }
}
# Android Studio brengt zijn eigen Java mee.
$plekken += 'C:\Program Files\Android\Android Studio\jbr\bin\keytool.exe'
foreach ($pad in $plekken) {
if (Test-Path $pad) { return $pad }
}
throw @"
keytool.exe is nergens gevonden. Dat hoort bij de Java die met de Android-werkbelasting meekomt.
Gezocht in:
$($plekken -join "`n")
Zoek hem zelf met:
Get-ChildItem 'C:\Program Files','C:\Program Files (x86)' -Recurse -Filter keytool.exe -ErrorAction SilentlyContinue |
Select-Object -First 5 FullName
Zet de map met keytool.exe daarna in JAVA_HOME, of geef het pad mee met -Keytool.
Wil je alleen op je eigen toestel testen, gebruik dan -Snel: een debug-APK heeft dit niet nodig.
"@
}
# ------------------------------------------------------------------ release
if (-not (Test-Path $Sleutelbestand)) {
Write-Host "Er is nog geen sleutelbestand. Ik maak er een aan." -ForegroundColor Yellow
Write-Host "Kies een wachtwoord dat je NIET kwijtraakt." -ForegroundColor Yellow
$keytool = Vind-Keytool
New-Item -ItemType Directory -Path (Split-Path $Sleutelbestand) -Force | Out-Null
& $keytool -genkeypair -v `
-keystore $Sleutelbestand -alias $Alias `
-keyalg RSA -keysize 2048 -validity 10000
if ($LASTEXITCODE -ne 0) { throw "Sleutelbestand aanmaken mislukt." }
Write-Host ""
Write-Host "MAAK HIER NU EEN BACK-UP VAN: $Sleutelbestand" -ForegroundColor Red
Write-Host "Zonder dit bestand is er later geen nieuwe versie van de app meer uit te brengen." -ForegroundColor Red
Write-Host ""
}
# Het wachtwoord van het sleutelbestand. Drie wegen, in deze volgorde:
#
# 1. De omgevingsvariabele MIJNBESLUIT_KEYSTORE_WACHTWOORD. Zet je die als gebruikersvariabele
# in Windows, dan kan het uitrolvenster ook een ondertekende APK bouwen. Hij staat dan wel
# op je machine; dat is een afweging die jij maakt en niet dit script.
# 2. Zelf intypen, als je dit script in een PowerShell-venster draait.
# 3. Draait dit vanuit het uitrolvenster zonder die variabele, dan houdt het hier op MET een
# melding. Dat is met opzet: zo'n venster heeft geen invoer, dus Read-Host zou daar blijven
# wachten op iets wat nooit komt - en dan staat er een build "nog bezig" die nooit afloopt.
if ($env:MIJNBESLUIT_KEYSTORE_WACHTWOORD) {
$plat = $env:MIJNBESLUIT_KEYSTORE_WACHTWOORD
}
elseif ($env:MIJNBESLUIT_UITROLVENSTER) {
throw @"
Het sleutelbestand vraagt om een wachtwoord, en dit venster kan daar niet om vragen.
Twee manieren om verder te komen:
* Draai dit script zelf in PowerShell: D:\MijnBesluit\tools\Apk-maken.ps1
* Of zet het wachtwoord eenmalig in een gebruikersvariabele, dan kan het venster het ook:
[Environment]::SetEnvironmentVariable('MIJNBESLUIT_KEYSTORE_WACHTWOORD', '<wachtwoord>', 'User')
Daarna het uitrolvenster opnieuw starten, anders kent het die variabele nog niet.
"@
}
else {
$wachtwoord = Read-Host "Wachtwoord van het sleutelbestand" -AsSecureString
$plat = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($wachtwoord))
}
# Leeg is geen wachtwoord. Dit is de val die een kwartier kost: in een proces zonder invoer geeft
# Read-Host geen fout maar een lege tekst, en dan bouwt hij eerst honderd assemblies om pas bij
# het ondertekenen om te vallen met "java.exe exited with code 2" - een melding waar niets in
# staat over een wachtwoord. Beter hier stoppen, voordat er iets gebouwd is.
if ([string]::IsNullOrWhiteSpace($plat)) {
throw @"
Er is geen wachtwoord voor het sleutelbestand opgegeven.
Krijg je dit terwijl je het net hebt ingetypt, dan draait dit script in een venster zonder invoer.
Draai hem dan zelf in PowerShell, of zet het wachtwoord eenmalig in een gebruikersvariabele:
[Environment]::SetEnvironmentVariable('MIJNBESLUIT_KEYSTORE_WACHTWOORD', '<wachtwoord>', 'User')
"@
}
$formaat = if ($Aab) { 'aab' } else { 'apk' }
Write-Host "$($formaat.ToUpper()) bouwen (release)..." -ForegroundColor Cyan
dotnet publish $project -f $doel -c Release `
-p:AndroidPackageFormat=$formaat `
-p:AndroidKeyStore=true `
-p:AndroidSigningKeyStore=$Sleutelbestand `
-p:AndroidSigningKeyAlias=$Alias `
-p:AndroidSigningStorePass=$plat `
-p:AndroidSigningKeyPass=$plat `
-o $Uit
if ($LASTEXITCODE -ne 0) { throw "Bouwen mislukt." }
# De ondertekende bundel heet meestal <pakketnaam>-Signed.aab, maar niet elke versie van de
# werkbelasting hangt er "-Signed" achter. Eerst de nette naam, en anders wat er ligt.
$pakket = Get-ChildItem $Uit -Filter "*-Signed.$formaat" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if (-not $pakket) {
$pakket = Get-ChildItem $Uit -Filter "*.$formaat" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
}
if (-not $pakket) { throw "Geen ondertekende $($formaat.ToUpper()) gevonden in $Uit." }
Write-Host ""
Write-Host "Klaar: $($pakket.FullName)" -ForegroundColor Green
if ($Aab) {
Write-Host ""
Write-Host "Dit bestand gaat naar de Play Console. Op een telefoon zetten kan er niet mee;" -ForegroundColor Yellow
Write-Host "bouw daarvoor zonder -Aab een gewone APK." -ForegroundColor Yellow
} else {
Write-Host "Voor de Play Store heb je geen APK nodig maar een AAB: draai dit script met -Aab."
}
+84
View File
@@ -0,0 +1,84 @@
<#
.SYNOPSIS
Publiceert MijnBesluit.Web en pakt het resultaat in als één zip-bestand.
.DESCRIPTION
Draai dit op je ontwikkelmachine. Het resultaat is D:\publish\mijnbesluit.zip.
Dat bestand pak je op de server uit met Uitrollen.ps1.
.EXAMPLE
.\tools\Publiceren.ps1
.\tools\Publiceren.ps1 -Uitvoer D:\publish -Configuratie Release
#>
[CmdletBinding()]
param(
# Map waarin de publicatie en het zip-bestand terechtkomen.
[string] $Uitvoer = 'D:\publish',
# Release voor de server, Debug alleen om iets te onderzoeken.
[ValidateSet('Release', 'Debug')]
[string] $Configuratie = 'Release',
# Sla het inpakken over als je de losse bestanden wilt hebben.
[switch] $GeenZip
)
$ErrorActionPreference = 'Stop'
# De projectmap is de map boven deze scriptmap.
$Project = Join-Path (Split-Path $PSScriptRoot -Parent) 'src\MijnBesluit.Web'
$DoelMap = Join-Path $Uitvoer 'mijnbesluit'
$ZipPad = Join-Path $Uitvoer 'mijnbesluit.zip'
if (-not (Test-Path $Project)) {
throw "Projectmap niet gevonden: $Project. Draai dit script vanuit de map tools van het project."
}
Write-Host ''
Write-Host 'MijnBesluit publiceren' -ForegroundColor Cyan
Write-Host " Project : $Project"
Write-Host " Configuratie : $Configuratie"
Write-Host " Uitvoer : $DoelMap"
Write-Host ''
# Schoon beginnen: anders blijven bestanden van een vorige versie achter die nu niet meer
# bestaan, en die worden dan mee uitgerold.
if (Test-Path $DoelMap) {
Write-Host 'Vorige publicatie opruimen...'
Remove-Item $DoelMap -Recurse -Force
}
Write-Host 'Publiceren...' -ForegroundColor Cyan
dotnet publish $Project -c $Configuratie -o $DoelMap --nologo
if ($LASTEXITCODE -ne 0) { throw 'Publiceren is mislukt. Zie de fouten hierboven.' }
# Controle: zonder deze twee bestanden start de site niet onder IIS.
foreach ($nodig in @('MijnBesluit.Web.dll', 'web.config')) {
if (-not (Test-Path (Join-Path $DoelMap $nodig))) {
throw "Er ontbreekt iets in de publicatie: $nodig"
}
}
# appsettings.Development.json hoort niet op een server thuis.
$ontwikkel = Join-Path $DoelMap 'appsettings.Development.json'
if (Test-Path $ontwikkel) {
Remove-Item $ontwikkel -Force
Write-Host 'appsettings.Development.json verwijderd uit de publicatie.'
}
if (-not $GeenZip) {
Write-Host 'Inpakken...' -ForegroundColor Cyan
if (Test-Path $ZipPad) { Remove-Item $ZipPad -Force }
Compress-Archive -Path (Join-Path $DoelMap '*') -DestinationPath $ZipPad -CompressionLevel Optimal
}
$grootte = if (Test-Path $ZipPad) { '{0:N1} MB' -f ((Get-Item $ZipPad).Length / 1MB) } else { '-' }
Write-Host ''
Write-Host 'Klaar.' -ForegroundColor Green
Write-Host " Map : $DoelMap"
if (-not $GeenZip) { Write-Host " Zip : $ZipPad ($grootte)" }
Write-Host ''
Write-Host 'Nu op de server, in een PowerShell-venster als administrator:' -ForegroundColor Yellow
Write-Host " .\Uitrollen.ps1 -Zip '\\tsclient\$($ZipPad.Substring(0,1))\$($ZipPad.Substring(3))'"
Write-Host ''
+3
View File
@@ -0,0 +1,3 @@
cd D:\MijnBesluit
.\tools\SocketServer-publiceren.ps1
.\tools\Publiceren.ps1
+214
View File
@@ -0,0 +1,214 @@
<#
.SYNOPSIS
Controleert of de SocketServer draait, versleuteld luistert en antwoord geeft.
.DESCRIPTION
Doet precies wat de app straks doet: verbinding maken, TLS opzetten, een 'ping' sturen en
het antwoord lezen. Zo weet je dat het werkt zonder dat je eerst een APK hoeft te bouwen.
Het protocol: [4 bytes lengte, big-endian][UTF-8 JSON].
.EXAMPLE
.\tools\SocketServer-proef.ps1
.\tools\SocketServer-proef.ps1 -Server localhost -GeenTls
.\tools\SocketServer-proef.ps1 -GeenTls -Email jonathan@flietstra.nl
.\tools\SocketServer-proef.ps1 -Email iemand@example.nl -Nieuw
.\tools\SocketServer-proef.ps1 -Acties
#>
[CmdletBinding()]
param(
[string] $Server = 'mijnbesluit.nl',
[int] $Poort = 5200,
# Voor lokaal testen zonder certificaat.
[switch] $GeenTls,
# Vraagt een inlogcode aan voor dit adres. Dat raakt wel de database en de mailserver,
# en bewijst dus meer dan een ping. Er moet een account met dit adres bestaan.
[string] $Email,
# Vraagt in plaats daarvan een registratiecode aan. Handig om de mailserver te toetsen met
# een adres waar nog geen account bij hoort - bijvoorbeeld je eigen postvak. Er wordt nog
# geen account aangemaakt; dat gebeurt pas als de code wordt ingevuld.
[switch] $Nieuw,
# Vraagt of de server alle acties kent die de app gebruikt. Zo zie je of de draaiende
# versie bij de code hoort, zonder in te loggen: een onbekende actie geeft een andere
# melding dan een actie waarvoor je niet bent aangemeld.
[switch] $Acties,
# Accepteer ook een certificaat dat niet klopt. Alleen om te onderzoeken - als dit nodig is,
# zou de app zelf de verbinding weigeren.
[switch] $NegeerCertificaatfouten
)
$ErrorActionPreference = 'Stop'
# Eén verzoek versturen en het antwoord teruglezen. Het protocol: [4 bytes lengte, big-endian]
# gevolgd door de JSON. De lengte staat er dus vóór, zodat de ontvanger precies weet waar een
# bericht ophoudt.
function Stuur($stroom, $client, [string] $actie, $payload)
{
$verzoek = @{ requestId = [guid]::NewGuid().ToString('N'); action = $actie }
if ($null -ne $payload) { $verzoek.payload = $payload }
$bytes = [System.Text.Encoding]::UTF8.GetBytes(($verzoek | ConvertTo-Json -Compress -Depth 5))
$lengte = [System.BitConverter]::GetBytes([int] $bytes.Length)
if ([System.BitConverter]::IsLittleEndian) { [array]::Reverse($lengte) }
$stroom.Write($lengte, 0, 4)
$stroom.Write($bytes, 0, $bytes.Length)
$stroom.Flush()
$client.ReceiveTimeout = 10000
$kop = New-Object byte[] 4
$gelezen = 0
while ($gelezen -lt 4) {
$n = $stroom.Read($kop, $gelezen, 4 - $gelezen)
if ($n -le 0) { throw "De server verbrak de verbinding zonder te antwoorden." }
$gelezen += $n
}
$kopOmgekeerd = $kop.Clone()
if ([System.BitConverter]::IsLittleEndian) { [array]::Reverse($kopOmgekeerd) }
$lengteAntwoord = [System.BitConverter]::ToInt32($kopOmgekeerd, 0)
if ($lengteAntwoord -le 0 -or $lengteAntwoord -gt 16MB) { throw "Onzinnige lengte in het antwoord: $lengteAntwoord." }
$inhoud = New-Object byte[] $lengteAntwoord
$gelezen = 0
while ($gelezen -lt $lengteAntwoord) {
$n = $stroom.Read($inhoud, $gelezen, $lengteAntwoord - $gelezen)
if ($n -le 0) { throw "Het antwoord kwam half binnen." }
$gelezen += $n
}
return [System.Text.Encoding]::UTF8.GetString($inhoud) | ConvertFrom-Json
}
Write-Host ''
Write-Host "Proef op $Server`:$Poort" -ForegroundColor Cyan
$client = [System.Net.Sockets.TcpClient]::new()
try {
$verbinden = $client.ConnectAsync($Server, $Poort)
if (-not $verbinden.Wait(10000)) { throw "Geen verbinding binnen 10 seconden." }
Write-Host " Verbinding : gelukt" -ForegroundColor Green
$stroom = $client.GetStream()
if (-not $GeenTls) {
$controle = $null
if ($NegeerCertificaatfouten) {
$controle = [System.Net.Security.RemoteCertificateValidationCallback] { param($a,$b,$c,$d) $true }
}
$ssl = [System.Net.Security.SslStream]::new($stroom, $false, $controle)
try {
$ssl.AuthenticateAsClient($Server)
} catch {
throw "TLS mislukte: $($_.Exception.Message)`n" +
"Luistert de server onversleuteld, probeer dan -GeenTls. Klopt het certificaat " +
"niet bij de naam '$Server', dan is dat precies wat de app straks ook weigert."
}
Write-Host " Versleuteling : $($ssl.SslProtocol)" -ForegroundColor Green
Write-Host " Certificaat van : $($ssl.RemoteCertificate.Subject)"
Write-Host " Geldig tot : $($ssl.RemoteCertificate.GetExpirationDateString())"
$stroom = $ssl
} else {
Write-Host " Versleuteling : GEEN - hier gaan straks wachtwoorden overheen" -ForegroundColor Yellow
}
# ---- acties controleren ------------------------------------------------
if ($Acties) {
$teControleren = @(
'ping',
'auth.request-code', 'auth.signin', 'auth.signout',
'auth.request-register-code', 'auth.register', 'auth.accept-invitation',
'account.profile', 'account.profile-save',
'account.email-request', 'account.email-confirm',
'account.delete-summary', 'account.delete-request', 'account.delete-confirm',
'account.notes', 'account.notes-accept', 'account.notes-decline',
'home.overview', 'home.lookahead',
'family.list', 'family.members', 'family.add-member',
'family.update-person', 'family.remove-member', 'family.invite',
'vault.list', 'vault.open', 'vault.save', 'vault.update', 'vault.delete',
'vault.file-begin', 'vault.file-chunk', 'vault.file-end', 'vault.file-cancel',
'vault.file-list', 'vault.file-read', 'vault.file-delete',
'message.inbox', 'message.outbox', 'message.read', 'message.read-sent', 'message.draft',
'message.compose', 'message.update', 'message.withdraw', 'message.delete',
'home.advertentie',
'legacy.status', 'legacy.declare-death', 'legacy.object', 'legacy.appoint-manager',
'vault.file-list-person', 'vault.file-list-message', 'vault.file-profile',
'legacy.entrusted', 'legacy.entrusted-items', 'vault.shared-with-me',
'legacy.managers', 'legacy.remove-manager', 'legacy.settings',
'push.register', 'push.unregister', 'push.proef'
)
$ontbreekt = @()
foreach ($actie in $teControleren) {
$antwoord = Stuur $stroom $client $actie $null
# Zonder token weigert de server de meeste acties - dat is prima, het bewijst dat
# hij ze kent. Alleen "Onbekende actie" betekent dat deze versie hem niet heeft.
if ($antwoord.message -like '*Onbekende actie*') {
$ontbreekt += $actie
Write-Host (" {0,-24} ONBEKEND" -f $actie) -ForegroundColor Red
} else {
Write-Host (" {0,-24} bekend" -f $actie) -ForegroundColor Green
}
}
Write-Host ''
if ($ontbreekt.Count -eq 0) {
Write-Host 'De server kent alle acties: de draaiende versie hoort bij deze code.' -ForegroundColor Green
} else {
Write-Host "De server mist $($ontbreekt.Count) actie(s). Er draait een oudere versie;" -ForegroundColor Yellow
Write-Host 'publiceer en rol de SocketServer opnieuw uit.' -ForegroundColor Yellow
}
return
}
# ---- verzoek versturen -------------------------------------------------
if ($Email -and $Nieuw) {
Write-Host " Verzoek : registratiecode voor $Email"
$antwoord = Stuur $stroom $client 'auth.request-register-code' @{
email = $Email; firstName = 'Proef'; lastName = 'Proef'
}
} elseif ($Email) {
Write-Host " Verzoek : inlogcode voor $Email"
$antwoord = Stuur $stroom $client 'auth.request-code' @{ email = $Email }
} else {
$antwoord = Stuur $stroom $client 'ping' $null
}
Write-Host " Antwoord : $($antwoord.status)" -ForegroundColor Green
if ($antwoord.payload.versleuteld -ne $null) {
Write-Host " Server zegt : verbinding versleuteld = $($antwoord.payload.versleuteld)"
}
Write-Host ''
if ($antwoord.status -eq 'Ok') {
if ($Email) {
Write-Host 'De SocketServer werkt, inclusief database en e-mail.' -ForegroundColor Green
Write-Host "Er zou nu een code onderweg moeten zijn naar $Email."
if ($Nieuw) {
Write-Host 'Er is nog geen account aangemaakt; dat gebeurt pas als de code wordt ingevuld.'
}
} else {
Write-Host 'De SocketServer werkt.' -ForegroundColor Green
Write-Host 'Let op: een ping raakt de database niet. Draai hem nog eens met -Email om dat ook te toetsen.'
}
if ($GeenTls -or $antwoord.payload.versleuteld -eq $false) {
Write-Host 'Maar zonder versleuteling. Stel Socket:CertificateThumbprint in voordat dit live gaat.' -ForegroundColor Yellow
}
} else {
Write-Host "De server antwoordde met: $($antwoord.message)" -ForegroundColor Yellow
if ($Email -and -not $Nieuw -and $antwoord.message -like '*kennen dit e-mailadres niet*') {
Write-Host 'Draai hem met -Nieuw erbij om een registratiecode te vragen; dan hoeft er geen account te zijn.'
}
}
}
finally {
$client.Close()
}
+72
View File
@@ -0,0 +1,72 @@
<#
.SYNOPSIS
Publiceert MijnBesluit.SocketServer en pakt het in als één zip-bestand.
.DESCRIPTION
Draai dit op je ontwikkelmachine. Het resultaat is D:\publish\mijnbesluit-socketserver.zip.
Dat bestand kopieer je naar de server en pak je daar uit; installeren gaat daarna met het
schermpje van de exe zelf (rechtermuis -> Als administrator uitvoeren -> Install).
Zie docs\SocketServer-installeren.md voor de stappen op de server.
.EXAMPLE
.\tools\SocketServer-publiceren.ps1
#>
[CmdletBinding()]
param(
[string] $Uitvoer = 'D:\publish',
[ValidateSet('Release', 'Debug')]
[string] $Configuratie = 'Release',
[switch] $GeenZip
)
$ErrorActionPreference = 'Stop'
$Project = Join-Path (Split-Path $PSScriptRoot -Parent) 'src\MijnBesluit.SocketServer'
$DoelMap = Join-Path $Uitvoer 'mijnbesluit-socketserver'
$ZipPad = Join-Path $Uitvoer 'mijnbesluit-socketserver.zip'
if (-not (Test-Path $Project)) {
throw "Projectmap niet gevonden: $Project."
}
Write-Host ''
Write-Host 'SocketServer publiceren' -ForegroundColor Cyan
Write-Host " Uitvoer : $DoelMap"
Write-Host ''
if (Test-Path $DoelMap) {
Write-Host 'Vorige publicatie opruimen...'
Remove-Item $DoelMap -Recurse -Force
}
# 64 bits, en niet self-contained: op de server staat .NET 8 al vanwege het Hosting Bundle.
dotnet publish $Project -c $Configuratie -r win-x64 --self-contained false -o $DoelMap --nologo
if ($LASTEXITCODE -ne 0) { throw 'Publiceren is mislukt. Zie de fouten hierboven.' }
foreach ($nodig in @('MijnBesluit.SocketServer.exe', 'appsettings.json')) {
if (-not (Test-Path (Join-Path $DoelMap $nodig))) {
throw "Er ontbreekt iets in de publicatie: $nodig"
}
}
# Controle die je anders pas op de server ontdekt: staat het wachtwoord er nog als voorbeeld in,
# dan is ExtraConfigFile de plek waar het echte wachtwoord vandaan moet komen.
$instellingen = Get-Content (Join-Path $DoelMap 'appsettings.json') -Raw | ConvertFrom-Json
if ($instellingen.ConnectionStrings.MijnBesluit -match 'VERVANG_DIT' -and
[string]::IsNullOrWhiteSpace($instellingen.ExtraConfigFile)) {
throw 'Er staat geen wachtwoord in appsettings.json en ook geen ExtraConfigFile. Zo start de service niet.'
}
if (-not $GeenZip) {
if (Test-Path $ZipPad) { Remove-Item $ZipPad -Force }
Compress-Archive -Path (Join-Path $DoelMap '*') -DestinationPath $ZipPad
Write-Host ''
Write-Host "Klaar: $ZipPad" -ForegroundColor Green
Write-Host 'Kopieer dit naar de server, pak het uit, en start de exe als administrator.'
} else {
Write-Host ''
Write-Host "Klaar: $DoelMap" -ForegroundColor Green
}
+122
View File
@@ -0,0 +1,122 @@
<#
.SYNOPSIS
Zet een nieuwe versie van de SocketServer op deze server.
.DESCRIPTION
Draai dit OP DE SERVER, in een PowerShell dat je als administrator hebt geopend.
Het script stopt de service, bewaart de huidige versie én je appsettings.json, pakt de
nieuwe versie uit, zet de instellingen terug en start de service weer. Start hij niet,
dan wordt de vorige versie teruggezet.
Je instellingen blijven dus staan - ook de vingerafdruk van het certificaat.
.EXAMPLE
C:\beheer\SocketServer-uitrollen.ps1 -Zip \\tsclient\D\publish\mijnbesluit-socketserver.zip
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string] $Zip,
[string] $Doel = 'C:\MijnBesluitSocket',
[string] $Service = 'MijnBesluitSocket',
[int] $BewaarVersies = 5
)
$ErrorActionPreference = 'Stop'
$beheerder = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $beheerder) {
throw 'Dit script heeft beheerdersrechten nodig. Open PowerShell met "Als administrator uitvoeren".'
}
if (-not (Test-Path $Zip)) { throw "Het zip-bestand is niet gevonden: $Zip" }
if (-not (Test-Path $Doel)) { throw "De map $Doel bestaat niet. Is de SocketServer hier wel geinstalleerd?" }
$Stempel = Get-Date -Format 'yyyyMMdd-HHmmss'
$Instelling = Join-Path $Doel 'appsettings.json'
$Bewaarmap = Join-Path (Split-Path $Doel -Parent) '_vorige-socketserver'
Write-Host ''
Write-Host 'SocketServer uitrollen' -ForegroundColor Cyan
Write-Host " Zip : $Zip"
Write-Host " Doel : $Doel"
Write-Host " Service : $Service"
Write-Host ''
# Eerst uitpakken naar een tijdelijke map, zodat de service maar kort uit staat.
$Tijdelijk = Join-Path $env:TEMP "mijnbesluit-socket-$Stempel"
Write-Host 'Uitpakken...' -ForegroundColor Cyan
Expand-Archive -Path $Zip -DestinationPath $Tijdelijk -Force
if (-not (Test-Path (Join-Path $Tijdelijk 'MijnBesluit.SocketServer.exe'))) {
Remove-Item $Tijdelijk -Recurse -Force
throw 'Dit lijkt geen geldige publicatie: MijnBesluit.SocketServer.exe ontbreekt in het zip-bestand.'
}
# De instellingen zijn van jou, niet van de publicatie: die gaan er straks weer overheen.
$instellingenBewaard = $null
if (Test-Path $Instelling) {
$instellingenBewaard = Join-Path $env:TEMP "appsettings-$Stempel.json"
Copy-Item $Instelling $instellingenBewaard -Force
Write-Host 'Instellingen bewaard.'
}
$draaide = (Get-Service $Service -ErrorAction SilentlyContinue).Status -eq 'Running'
if ($draaide) {
Write-Host 'Service stoppen...' -ForegroundColor Cyan
Stop-Service $Service
# Windows geeft de bestanden niet altijd meteen vrij.
Start-Sleep -Seconds 2
}
New-Item -ItemType Directory -Path $Bewaarmap -Force | Out-Null
$vorige = Join-Path $Bewaarmap "socketserver-$Stempel"
Write-Host 'Vorige versie bewaren...' -ForegroundColor Cyan
Copy-Item $Doel $vorige -Recurse -Force
try {
Write-Host 'Nieuwe versie plaatsen...' -ForegroundColor Cyan
Copy-Item (Join-Path $Tijdelijk '*') $Doel -Recurse -Force
if ($instellingenBewaard) {
Copy-Item $instellingenBewaard $Instelling -Force
Write-Host 'Instellingen teruggezet.'
}
Write-Host 'Service starten...' -ForegroundColor Cyan
Start-Service $Service
Start-Sleep -Seconds 3
$stand = (Get-Service $Service).Status
if ($stand -ne 'Running') { throw "De service staat op '$stand' in plaats van 'Running'." }
}
catch {
Write-Host ''
Write-Host "Mislukt: $($_.Exception.Message)" -ForegroundColor Red
Write-Host 'Vorige versie terugzetten...' -ForegroundColor Yellow
Stop-Service $Service -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
Copy-Item (Join-Path $vorige '*') $Doel -Recurse -Force
Start-Service $Service -ErrorAction SilentlyContinue
throw 'De nieuwe versie is teruggedraaid. Kijk in Logboeken > Windows-logboeken > Toepassing.'
}
finally {
Remove-Item $Tijdelijk -Recurse -Force -ErrorAction SilentlyContinue
if ($instellingenBewaard) { Remove-Item $instellingenBewaard -Force -ErrorAction SilentlyContinue }
}
# Oude versies opruimen, anders loopt de schijf vol.
Get-ChildItem $Bewaarmap -Directory | Sort-Object Name -Descending |
Select-Object -Skip $BewaarVersies | Remove-Item -Recurse -Force
Write-Host ''
Write-Host 'Klaar. De service draait.' -ForegroundColor Green
Write-Host " Vorige versie staat in: $vorige"
Write-Host ''
Write-Host 'Controleer vanaf je eigen pc met:'
Write-Host ' D:\MijnBesluit\tools\SocketServer-proef.ps1'
+225
View File
@@ -0,0 +1,225 @@
<#
.SYNOPSIS
Rolt een gepubliceerde versie van MijnBesluit uit op deze server.
.DESCRIPTION
Draai dit OP DE SERVER, in een PowerShell-venster dat je als administrator hebt geopend.
Het script haalt het pad en de toepassingsgroep uit IIS, zet de site stil, bewaart de huidige
versie als terugvaloptie, pakt de nieuwe versie uit, start de site en controleert of hij
antwoordt. Gaat er iets mis, dan wordt de vorige versie teruggezet.
De map met gegevens (sleutelbestand, kluisbestanden, appsettings.secrets.json) wordt niet
aangeraakt: die staat daar bewust buiten.
.EXAMPLE
# De zip rechtstreeks van je eigen pc halen via de externe-bureaubladverbinding:
.\Uitrollen.ps1 -Zip \\tsclient\D\publish\mijnbesluit.zip
.EXAMPLE
# Of vanaf een bestand dat al op de server staat:
.\Uitrollen.ps1 -Zip D:\mijnbesluit.zip
#>
[CmdletBinding()]
param(
# Het zip-bestand dat Publiceren.ps1 heeft gemaakt.
[Parameter(Mandatory = $true)]
[string] $Zip,
# De naam van de site zoals die in IIS staat. Pad en toepassingsgroep worden daaruit
# opgehaald, zodat je die niet hoeft te onthouden.
[string] $Site = 'mijnbesluit',
# Alleen invullen als je wilt afwijken van wat er in IIS staat.
[string] $Doel,
[string] $Pool,
# Adres om na afloop te controleren. Niet meegeven laat het script het afleiden uit de
# bindingen van de site; een lege tekst slaat de controle over.
[string] $Controleer,
# Zoveel vorige versies blijven bewaard om op terug te kunnen vallen.
[int] $BewaarVersies = 5
)
$ErrorActionPreference = 'Stop'
# ---------------------------------------------------------------- vooraf controleren
$beheerder = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $beheerder) {
throw 'Dit script heeft beheerdersrechten nodig. Open PowerShell met "Als administrator uitvoeren".'
}
if (-not (Test-Path $Zip)) {
throw "Het zip-bestand is niet gevonden: $Zip"
}
Import-Module WebAdministration -ErrorAction Stop
# ---------------------------------------------------------------- gegevens uit IIS halen
$website = Get-Website -Name $Site -ErrorAction SilentlyContinue
if (-not $website) {
$bestaande = (Get-Website | Select-Object -ExpandProperty Name) -join ', '
throw "Site '$Site' bestaat niet in IIS. Aanwezige sites: $bestaande"
}
if (-not $Doel) { $Doel = [Environment]::ExpandEnvironmentVariables($website.physicalPath) }
if (-not $Pool) { $Pool = $website.applicationPool }
if (-not (Test-Path "IIS:\AppPools\$Pool")) {
throw "De toepassingsgroep '$Pool' bestaat niet in IIS."
}
# Een ASP.NET Core-applicatie hoort in een toepassingsgroep zonder beheerde code te draaien.
# Staat dat verkeerd, dan krijg je een 500.19 of 500.21 die lastig te herleiden is.
$runtime = (Get-ItemProperty "IIS:\AppPools\$Pool" -Name managedRuntimeVersion).Value
if ($runtime -ne '') {
Write-Host ''
Write-Host "Let op: toepassingsgroep '$Pool' staat op .NET CLR-versie '$runtime'." -ForegroundColor Yellow
Write-Host 'Voor ASP.NET Core hoort dat "Geen beheerde code" te zijn. Aanpassen met:' -ForegroundColor Yellow
Write-Host " Set-ItemProperty 'IIS:\AppPools\$Pool' -Name managedRuntimeVersion -Value ''"
Write-Host ''
}
# Controleadres afleiden uit de bindingen als het niet is meegegeven.
if (-not $PSBoundParameters.ContainsKey('Controleer')) {
$binding = $website.bindings.Collection | Where-Object { $_.protocol -eq 'https' } | Select-Object -First 1
if (-not $binding) { $binding = $website.bindings.Collection | Select-Object -First 1 }
if ($binding) {
# bindingInformation ziet eruit als "*:443:mijnbesluit.nl"
$delen = $binding.bindingInformation -split ':'
$hostnaam = if ($delen.Count -ge 3 -and $delen[2]) { $delen[2] } else { 'localhost' }
$Controleer = "$($binding.protocol)://$hostnaam/inloggen"
}
}
$BackupWortel = Join-Path (Split-Path $Doel -Parent) '_vorige-versies'
$Stempel = Get-Date -Format 'yyyyMMdd-HHmmss'
$BackupMap = Join-Path $BackupWortel "$(Split-Path $Doel -Leaf)-$Stempel"
Write-Host ''
Write-Host 'MijnBesluit uitrollen' -ForegroundColor Cyan
Write-Host " Zip : $Zip"
Write-Host " Site : $Site"
Write-Host " Doel : $Doel"
Write-Host " App-pool : $Pool"
Write-Host " Controleer : $(if ($Controleer) { $Controleer } else { '(overslaan)' })"
Write-Host ''
# ---------------------------------------------------------------- uitpakken naar een tijdelijke map
# Eerst helemaal uitpakken en controleren, en pas daarna de site stilzetten. Zo staat de site
# maar een paar seconden uit, en niet de hele tijd dat het uitpakken duurt.
$Tijdelijk = Join-Path $env:TEMP "mijnbesluit-$Stempel"
Write-Host 'Uitpakken...' -ForegroundColor Cyan
Expand-Archive -Path $Zip -DestinationPath $Tijdelijk -Force
foreach ($nodig in @('MijnBesluit.Web.dll', 'web.config')) {
if (-not (Test-Path (Join-Path $Tijdelijk $nodig))) {
Remove-Item $Tijdelijk -Recurse -Force
throw "Dit lijkt geen geldige publicatie: $nodig ontbreekt in het zip-bestand."
}
}
# ---------------------------------------------------------------- site stilzetten
$draaide = (Get-WebAppPoolState -Name $Pool).Value -eq 'Started'
if ($draaide) {
Write-Host 'Toepassingsgroep stoppen...' -ForegroundColor Cyan
Stop-WebAppPool -Name $Pool
# Wachten tot IIS de bestanden echt heeft losgelaten; anders mislukt het kopieren.
$wachtTot = (Get-Date).AddSeconds(30)
while ((Get-WebAppPoolState -Name $Pool).Value -ne 'Stopped' -and (Get-Date) -lt $wachtTot) {
Start-Sleep -Milliseconds 500
}
Start-Sleep -Seconds 2
}
try {
# ------------------------------------------------------------ vorige versie bewaren
if (Test-Path $Doel) {
Write-Host 'Vorige versie bewaren...' -ForegroundColor Cyan
New-Item -ItemType Directory -Path $BackupWortel -Force | Out-Null
Move-Item -Path $Doel -Destination $BackupMap -Force
}
# ------------------------------------------------------------ nieuwe versie neerzetten
Write-Host 'Nieuwe versie plaatsen...' -ForegroundColor Cyan
New-Item -ItemType Directory -Path $Doel -Force | Out-Null
Copy-Item -Path (Join-Path $Tijdelijk '*') -Destination $Doel -Recurse -Force
}
catch {
Write-Host 'Er ging iets mis; vorige versie terugzetten...' -ForegroundColor Red
if ((Test-Path $BackupMap) -and -not (Test-Path (Join-Path $Doel 'MijnBesluit.Web.dll'))) {
if (Test-Path $Doel) { Remove-Item $Doel -Recurse -Force }
Move-Item -Path $BackupMap -Destination $Doel -Force
}
if ($draaide) { Start-WebAppPool -Name $Pool }
throw
}
finally {
if (Test-Path $Tijdelijk) { Remove-Item $Tijdelijk -Recurse -Force }
}
# ---------------------------------------------------------------- weer starten
Write-Host 'Toepassingsgroep starten...' -ForegroundColor Cyan
Start-WebAppPool -Name $Pool
# ---------------------------------------------------------------- controleren
if ($Controleer) {
Write-Host "Controleren: $Controleer" -ForegroundColor Cyan
$gelukt = $false
# De eerste aanroep duurt langer: dan start de applicatie op.
for ($poging = 1; $poging -le 10; $poging++) {
try {
$antwoord = Invoke-WebRequest -Uri $Controleer -UseBasicParsing -TimeoutSec 30
if ($antwoord.StatusCode -eq 200) { $gelukt = $true; break }
}
catch {
Start-Sleep -Seconds 3
}
}
if ($gelukt) {
Write-Host 'De site antwoordt.' -ForegroundColor Green
}
else {
Write-Host ''
Write-Host 'De site antwoordt niet.' -ForegroundColor Red
Write-Host 'Kijk in Logboeken > Windows-logboeken > Toepassing, of zet in web.config'
Write-Host 'stdoutLogEnabled op true en kijk daarna in de map logs.'
Write-Host ''
Write-Host 'Terugvallen op de vorige versie:' -ForegroundColor Yellow
Write-Host " Stop-WebAppPool '$Pool'"
Write-Host " Remove-Item '$Doel' -Recurse -Force"
Write-Host " Move-Item '$BackupMap' '$Doel'"
Write-Host " Start-WebAppPool '$Pool'"
}
}
# ---------------------------------------------------------------- oude versies opruimen
if (Test-Path $BackupWortel) {
Get-ChildItem $BackupWortel -Directory |
Sort-Object Name -Descending |
Select-Object -Skip $BewaarVersies |
ForEach-Object {
Write-Host "Oude versie opruimen: $($_.Name)"
Remove-Item $_.FullName -Recurse -Force
}
}
Write-Host ''
Write-Host 'Klaar.' -ForegroundColor Green
Write-Host " Vorige versie staat in: $BackupMap"
Write-Host ''
+60
View File
@@ -0,0 +1,60 @@
<#
.SYNOPSIS
Publiceert MijnBesluit.Uitroller (het venster op je eigen pc) naar D:\MijnBesluitTools\pc,
met behoud van je eigen instellingen en geheimen.
.DESCRIPTION
D:\publish is van de uitrolPAKKETTEN (de zip's voor site/socketserver/apk) en blijft daarvoor
gereserveerd. De uitroller zelf krijgt, net als RenH.Uitroller met D:\RenHTools, zijn eigen map:
D:\MijnBesluitTools\pc\ - de exe en alles wat dotnet publish neerzet
D:\MijnBesluitTools\uitroller.json - server, geheim, projectmap, testadres, enz.
D:\MijnBesluitTools\uitroller.secrets.json - het Gitea-token (los bestand, buiten git)
De twee bestanden hierboven staan een map BOVEN de exe, dus een publish (die alleen de
pc-submap ververst) kan ze niet wissen - dat is precies waarom Instellingen.cs en
Uitrollergeheimen.cs ze daar zoeken (net als RenH.Uitroller bij D:\RenHTools).
.EXAMPLE
cd D:\MijnBesluit
.\tools\Uitroller-publiceren.ps1
#>
[CmdletBinding()]
param(
[string] $Doel = 'D:\MijnBesluitTools'
)
$ErrorActionPreference = 'Stop'
$Projectmap = Split-Path $PSScriptRoot -Parent
$Pcmap = Join-Path $Doel 'pc'
New-Item -ItemType Directory -Force -Path $Doel | Out-Null
New-Item -ItemType Directory -Force -Path $Pcmap | Out-Null
Write-Host ''
Write-Host "MijnBesluit.Uitroller -> $Pcmap" -ForegroundColor Cyan
dotnet publish (Join-Path $Projectmap 'src\MijnBesluit.Uitroller') -c Release -o $Pcmap --nologo
if ($LASTEXITCODE -ne 0) { throw 'Publiceren van MijnBesluit.Uitroller is mislukt.' }
# uitroller.json en uitroller.secrets.json staan in $Doel zelf (een map boven de exe) en worden
# door dotnet publish dus niet aangeraakt - alleen $Pcmap wordt ververst. Bestaat
# uitroller.secrets.json daar nog nergens (eerste keer publiceren), dan hier een lege maar
# geldige versie neerzetten, anders moet je 'm de eerste keer met de hand aanmaken voordat de
# Gitea-knoppen iets kunnen.
$geheimenPad = Join-Path $Doel 'uitroller.secrets.json'
if (-not (Test-Path $geheimenPad)) {
Set-Content -Path $geheimenPad -Value "{`n `"Gitea`": { `"Token`": `"`" }`n}`n" -NoNewline -Encoding UTF8
Write-Host " uitroller.secrets.json aangemaakt (leeg) - vul je Gitea-token in op:" -ForegroundColor Yellow
Write-Host " $geheimenPad" -ForegroundColor Yellow
}
$instellingenPad = Join-Path $Doel 'uitroller.json'
if (-not (Test-Path $instellingenPad)) {
Write-Host " Let op: er staat nog geen uitroller.json in $Doel." -ForegroundColor Yellow
Write-Host " Bij de eerste start kopieert het venster 'm zelf over vanuit $Pcmap." -ForegroundColor Yellow
}
Write-Host ''
Write-Host 'Klaar.' -ForegroundColor Green
Write-Host " $Pcmap\MijnBesluit.Uitroller.exe"
Write-Host ''