Compose Postgres (PostGIS + pgvector épinglé par digest), scripts de bootstrap du premier SuperAdmin, de lancement local du service et de migration des données, et le README du flux en huit étapes. Le port est lié à 127.0.0.1 et non publié : le Postgres précédent, exposé par un simple "5432:5432" avec mym/mym, a encaissé 1 074 465 échecs d'authentification entre juin et octobre 2025. Le compte mym n'a jamais été tenté — c'est le seul hasard qui a sauvé la base. Les limites de log viennent du même serveur, où un conteneur avait atteint 516 Mo sur un disque de 20 Go. Le SuperAdmin passe par le token de réinitialisation plutôt que par un INSERT : les mots de passe étant hachés en scrypt, seule l'application peut produire le bon format, et personne d'autre que l'opérateur ne connaît le mot de passe. MigrationController l'exige et la migration n'en crée aucun, MongoDB n'ayant pas de champ Role. Éprouvé sur la préprod le 07/09 : 4 instances, 315 sections migrées et 20 signalées sur 335, 2383 ressources, tout réconcilié avec MongoDB. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
72 lines
3.3 KiB
PowerShell
72 lines
3.3 KiB
PowerShell
<#
|
|
Joue la migration Mongo -> Postgres.
|
|
|
|
Sans -Apply : dry run global (n'ecrit rien).
|
|
Avec -Apply : joue pour de vrai, instance par instance, de la plus petite a la
|
|
plus grande — c'est ce qui fait qu'un defaut casse sur le plus petit perimetre
|
|
possible. Chaque appel est une transaction unique : soit tout, soit rien.
|
|
|
|
⚠️ Un dry run ne peut detecter AUCUNE violation de contrainte : il n'ecrit rien,
|
|
donc SaveChanges n'est jamais appele. Un NOT NULL viole ne sort qu'avec -Apply.
|
|
C'est arrive le 07/09/2026 (LastName null sur test@email.be).
|
|
|
|
⚠️ Compter 2 a 5 min : la sonde HEAD interroge chaque blob Firebase, 30 a la
|
|
fois. Elle tourne AUSSI en dry run.
|
|
#>
|
|
param(
|
|
[switch]$Apply,
|
|
[string]$InstanceId,
|
|
[string]$BaseUrl = "http://localhost:5000"
|
|
)
|
|
$ErrorActionPreference = "Stop"
|
|
. "$PSScriptRoot\_common.ps1"
|
|
$jwt = Get-Jwt $PSScriptRoot
|
|
$headers = @{ Authorization = "Bearer $jwt" }
|
|
|
|
function Invoke-Migration($dryRun, $instance) {
|
|
$uri = "$BaseUrl/api/migration/run?dryRun=$($dryRun.ToString().ToLower())"
|
|
if ($instance) { $uri += "&instanceId=$instance" }
|
|
$label = if ($dryRun) { "DRY RUN" } else { "REEL" }
|
|
if ($instance) { $label += " / $instance" }
|
|
Write-Host "=== $label ===" -ForegroundColor Cyan
|
|
$t0 = Get-Date
|
|
try {
|
|
$r = Invoke-RestMethod -Method Post -Uri $uri -Headers $headers -ContentType "application/json" -Body "{}"
|
|
} catch {
|
|
Write-Host " ECHEC HTTP : $(Get-HttpErrorBody $_)" -ForegroundColor Red
|
|
Write-Host " Un 500 signifie que la transaction a ete ANNULEE : la base est intacte." -ForegroundColor Yellow
|
|
return $false
|
|
}
|
|
$sec = [int]((Get-Date) - $t0).TotalSeconds
|
|
Write-Host " duree : $sec s" -ForegroundColor Gray
|
|
$m = $r.migrated
|
|
($m.PSObject.Properties | Where-Object { $_.Value -is [int] -and $_.Value -gt 0 } |
|
|
ForEach-Object { "$($_.Name)=$($_.Value)" }) -join ", " | ForEach-Object { Write-Host " migre : $_" }
|
|
Write-Host " erreurs : $($r.errors.Count) | signalees : $($r.skipped.Count)"
|
|
$r.errors | Select-Object -First 5 | ForEach-Object { Write-Host " ! $_" -ForegroundColor Yellow }
|
|
if ($r.fatalError) { Write-Host " FATAL : $($r.fatalError)" -ForegroundColor Red; return $false }
|
|
Write-Host ""
|
|
return $true
|
|
}
|
|
|
|
if (-not $Apply) {
|
|
Write-Host "Mode simulation. Ajouter -Apply pour ecrire." -ForegroundColor Yellow
|
|
Invoke-Migration $true $InstanceId | Out-Null
|
|
exit 0
|
|
}
|
|
|
|
if ($InstanceId) { if (Invoke-Migration $false $InstanceId) { exit 0 } else { exit 1 } }
|
|
|
|
# ⚠️ L'API ne donne pas le volume par instance : cet ordre est ALPHABETIQUE,
|
|
# pas croissant. Or jouer la plus petite d'abord est ce qui fait qu'un defaut
|
|
# casse sur le plus petit perimetre — c'est ce qui a permis de trouver le
|
|
# LastName null sur 1 utilisateur au lieu de 10, le 07/09/2026.
|
|
# Pour maitriser l'ordre, enchainer les appels avec -InstanceId a la main.
|
|
Write-Host "Ordre ALPHABETIQUE (l'API ne donne pas les volumes)." -ForegroundColor Yellow
|
|
Write-Host "Pour commencer par la plus petite, appeler -InstanceId une par une." -ForegroundColor Yellow
|
|
Write-Host ""
|
|
$ids = (Invoke-RestMethod -Uri "$BaseUrl/api/Instance" -Headers $headers) |
|
|
Sort-Object { $_.name } | Select-Object -ExpandProperty id
|
|
foreach ($id in $ids) { if (-not (Invoke-Migration $false $id)) { exit 1 } }
|
|
Write-Host "Toutes les instances sont passees." -ForegroundColor Green
|