Ajout d'un script pour installation automatique du produit
Some checks failed
E2E Tests / e2e (push) Failing after 19s
Build & Push Images / build (brain) (push) Successful in 45s
Build & Push Images / build (core) (push) Successful in 1m16s
Build & Push Images / build (web) (push) Successful in 1m26s

Ajout d'une partie mise à jour automatique : plus besoin de docker pull en ligne de commande ; on peut passer par l'interface
Refactoring partie Java pour respecter d'avantage le DDD : plus de jackson dans la partie domain

Passage version 0.6.6
This commit is contained in:
2026-04-25 13:24:32 +02:00
parent 550078268c
commit 41fda9aeee
58 changed files with 1859 additions and 812 deletions

109
installers/README.md Normal file
View File

@@ -0,0 +1,109 @@
# LoreMindMJ — Installation rapide
Ces scripts installent Docker (si nécessaire), génèrent un `.env` sécurisé
et lancent la stack. Aucune configuration manuelle requise.
## Windows 10 / 11
Ouvrir **PowerShell** (clic droit → *Exécuter en tant qu'administrateur*) :
```powershell
iwr https://git.igmlcreation.fr/ietm64/loremind/raw/branch/main/installers/install.ps1 -OutFile $env:TEMP\loremind-install.ps1
powershell -ExecutionPolicy Bypass -File $env:TEMP\loremind-install.ps1
```
Le script :
1. Vérifie / installe **WSL2** (un reboot peut être nécessaire — relancer le script après).
2. Vérifie / installe **Docker Desktop** via `winget`.
3. Génère `%LOCALAPPDATA%\LoreMind\.env` avec mots de passe aléatoires.
4. Lance la stack et ouvre `http://localhost:8081`.
## Linux (Debian / Ubuntu / Fedora / Arch)
```bash
curl -fsSL https://git.igmlcreation.fr/ietm64/loremind/raw/branch/main/installers/install.sh | bash
```
Le script :
1. Installe **Docker** via le script officiel `get.docker.com` si absent.
2. Ajoute l'utilisateur courant au groupe `docker` (relogin nécessaire la 1ʳᵉ fois).
3. Installe dans `~/.local/share/loremind`.
4. Lance la stack et ouvre `http://localhost:8081`.
## Variables disponibles
| Variable | Défaut | Effet |
|-------------------|---------------------------------|----------------------------------------|
| `WEB_PORT` | `8081` | Port HTTP de l'UI |
| `INSTALL_DIR` | `~/.local/share/loremind` (Lin) | Dossier d'installation |
| `NON_INTERACTIVE` | `0` | `1` = aucune question, valeurs par défaut |
Exemple Linux non-interactif sur port 9000 :
```bash
WEB_PORT=9000 NON_INTERACTIVE=1 bash install.sh
```
## Mises à jour automatiques (Watchtower)
Si vous avez répondu **oui** à la question "Activer les mises à jour auto",
un container [Watchtower](https://containrrr.dev/watchtower/) est lancé en
parallèle. Il vérifie chaque nuit à 4h les nouvelles versions de
`core`, `brain` et `web` sur le registry, télécharge et redémarre les
conteneurs concernés. **Postgres et MinIO sont volontairement exclus**
(données persistantes — montée de version à valider manuellement).
### Activer / désactiver après coup
Éditer `.env` dans le dossier d'installation :
```env
COMPOSE_PROFILES=autoupdate # active
COMPOSE_PROFILES= # desactive
```
Puis :
```bash
docker compose up -d # applique le changement
docker compose stop watchtower # si on vient de le desactiver
```
### Changer l'horaire
`WATCHTOWER_SCHEDULE` dans `.env` accepte la syntaxe
[cron 6 champs](https://pkg.go.dev/github.com/robfig/cron) (sec min h jour mois j-sem).
Exemples : `0 0 4 * * *` (4h du matin, défaut), `0 30 3 * * 0` (dimanche 3h30).
### Mode "notification seulement" (sans auto-apply)
Si vous préférez être notifié *sans* que les conteneurs redémarrent
automatiquement la nuit, éditez `.env` :
```env
WATCHTOWER_MONITOR_ONLY=true
```
Puis `docker compose up -d watchtower`. Watchtower continuera à vérifier
le registry chaque nuit, le badge **MAJ** apparaîtra dans la sidebar de
l'UI, et un bouton **Mettre à jour maintenant** sera disponible dans
*Paramètres → Mises à jour*.
### Mise à jour manuelle (à tout moment)
Depuis l'interface : *Paramètres → Mises à jour → Mettre à jour maintenant*.
Ou en CLI :
```bash
docker compose pull && docker compose up -d
```
## Désinstallation
```bash
cd <dossier d'install>
docker compose down -v # -v supprime aussi les volumes (données effacées !)
```
Puis supprimer le dossier d'installation.

240
installers/install.ps1 Normal file
View File

@@ -0,0 +1,240 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Installeur LoreMindMJ pour Windows 10/11.
.DESCRIPTION
- Verifie / installe WSL2 et Docker Desktop (via winget)
- Genere un .env avec mots de passe aleatoires
- Recupere le docker-compose.yml officiel
- Lance la stack et ouvre le navigateur
.EXAMPLE
iwr https://git.igmlcreation.fr/ietm64/loremind/raw/branch/main/installers/install.ps1 | iex
#>
[CmdletBinding()]
param(
[string]$InstallDir = "$env:LOCALAPPDATA\LoreMind",
[string]$ComposeUrl = "https://git.igmlcreation.fr/ietm64/loremind/raw/branch/main/docker-compose.yml",
[int]$WebPort = 8081,
[switch]$NonInteractive
)
$ErrorActionPreference = 'Stop'
function Write-Step($msg) { Write-Host "==> $msg" -ForegroundColor Cyan }
function Write-Ok($msg) { Write-Host " OK $msg" -ForegroundColor Green }
function Write-Warn2($msg) { Write-Host " !! $msg" -ForegroundColor Yellow }
function Write-Err($msg) { Write-Host " XX $msg" -ForegroundColor Red }
function Test-Admin {
$current = [Security.Principal.WindowsIdentity]::GetCurrent()
return ([Security.Principal.WindowsPrincipal]$current).IsInRole(
[Security.Principal.WindowsBuiltInRole]::Administrator)
}
function Invoke-Elevated {
Write-Step "Relance en mode administrateur..."
$args = @('-NoProfile','-ExecutionPolicy','Bypass','-File',$PSCommandPath)
Start-Process powershell -Verb RunAs -ArgumentList $args
exit
}
function New-RandomSecret([int]$Length = 32) {
$bytes = New-Object byte[] $Length
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
return ([BitConverter]::ToString($bytes) -replace '-','').ToLower().Substring(0, $Length)
}
function Test-Wsl2 {
try {
$out = wsl.exe --status 2>$null
return ($LASTEXITCODE -eq 0)
} catch { return $false }
}
function Test-Docker {
$cmd = Get-Command docker -ErrorAction SilentlyContinue
if (-not $cmd) { return $false }
docker info *>$null
return ($LASTEXITCODE -eq 0)
}
function Wait-Docker([int]$TimeoutSec = 180) {
Write-Step "Attente du demarrage de Docker Desktop (max ${TimeoutSec}s)..."
$deadline = (Get-Date).AddSeconds($TimeoutSec)
while ((Get-Date) -lt $deadline) {
docker info *>$null
if ($LASTEXITCODE -eq 0) { Write-Ok "Docker repond"; return $true }
Start-Sleep -Seconds 3
}
return $false
}
# ---------------------------------------------------------------------------
# 0. Pre-requis admin
# ---------------------------------------------------------------------------
if (-not (Test-Admin)) { Invoke-Elevated }
Write-Host ""
Write-Host "============================================================"
Write-Host " LoreMindMJ - Installeur Windows" -ForegroundColor Magenta
Write-Host "============================================================"
Write-Host ""
# ---------------------------------------------------------------------------
# 1. WSL2
# ---------------------------------------------------------------------------
Write-Step "Verification de WSL2..."
if (Test-Wsl2) {
Write-Ok "WSL2 deja installe"
} else {
Write-Warn2 "WSL2 absent - installation en cours"
wsl.exe --install --no-launch
Write-Warn2 "REDEMARRAGE REQUIS. Relancez ce script apres reboot."
Read-Host "Appuyez sur Entree pour quitter"
exit 1
}
# ---------------------------------------------------------------------------
# 2. Docker Desktop
# ---------------------------------------------------------------------------
Write-Step "Verification de Docker Desktop..."
if (Test-Docker) {
Write-Ok "Docker fonctionnel"
} else {
if (-not (Get-Command winget -ErrorAction SilentlyContinue)) {
Write-Err "winget introuvable. Installez Docker Desktop manuellement : https://www.docker.com/products/docker-desktop/"
exit 1
}
Write-Warn2 "Installation de Docker Desktop via winget..."
winget install --id Docker.DockerDesktop -e --accept-package-agreements --accept-source-agreements
if ($LASTEXITCODE -ne 0) { Write-Err "Echec winget"; exit 1 }
Write-Step "Lancement de Docker Desktop..."
$dd = "$env:ProgramFiles\Docker\Docker\Docker Desktop.exe"
if (Test-Path $dd) { Start-Process $dd }
if (-not (Wait-Docker 240)) {
Write-Err "Docker n'a pas demarre. Lancez-le manuellement puis relancez ce script."
exit 1
}
}
# ---------------------------------------------------------------------------
# 3. Dossier d'installation + docker-compose.yml
# ---------------------------------------------------------------------------
Write-Step "Preparation du dossier $InstallDir"
New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null
Set-Location $InstallDir
$composePath = Join-Path $InstallDir 'docker-compose.yml'
Write-Step "Telechargement de docker-compose.yml"
Invoke-WebRequest -Uri $ComposeUrl -OutFile $composePath -UseBasicParsing
Write-Ok "docker-compose.yml recupere"
# ---------------------------------------------------------------------------
# 4. Generation du .env
# ---------------------------------------------------------------------------
$envPath = Join-Path $InstallDir '.env'
if (Test-Path $envPath) {
Write-Warn2 ".env deja present - sauvegarde en .env.bak"
Copy-Item $envPath "$envPath.bak" -Force
}
Write-Step "Configuration"
$adminUser = if ($NonInteractive) { 'admin' } else {
$r = Read-Host " Nom d'utilisateur admin [admin]"; if ([string]::IsNullOrWhiteSpace($r)) { 'admin' } else { $r }
}
$adminPass = if ($NonInteractive) { New-RandomSecret 16 } else {
$r = Read-Host " Mot de passe admin (vide = genere automatiquement)"
if ([string]::IsNullOrWhiteSpace($r)) { New-RandomSecret 16 } else { $r }
}
$llmProvider = if ($NonInteractive) { 'ollama' } else {
$r = Read-Host " Provider LLM : [ollama] / onemin"
if ($r -eq 'onemin') { 'onemin' } else { 'ollama' }
}
$onemKey = ''
if ($llmProvider -eq 'onemin' -and -not $NonInteractive) {
$onemKey = Read-Host " Cle API 1min.ai"
}
$autoUpdate = if ($NonInteractive) { $true } else {
$r = Read-Host " Activer les mises a jour auto (chaque nuit a 4h) ? [O/n]"
-not ($r -match '^(n|N|no|non)$')
}
$composeProfiles = if ($autoUpdate) { 'autoupdate' } else { '' }
$envContent = @"
# Genere par install.ps1 le $(Get-Date -Format 'yyyy-MM-dd HH:mm')
REGISTRY=git.igmlcreation.fr
TAG=latest
WEB_PORT=$WebPort
POSTGRES_DB=loremind
POSTGRES_USER=loremind
POSTGRES_PASSWORD=$(New-RandomSecret 24)
ADMIN_USERNAME=$adminUser
ADMIN_PASSWORD=$adminPass
BRAIN_INTERNAL_SECRET=$(New-RandomSecret 32)
MINIO_USER=minioadmin
MINIO_PASSWORD=$(New-RandomSecret 24)
LLM_PROVIDER=$llmProvider
OLLAMA_BASE_URL=http://host.docker.internal:11434
LLM_MODEL=gemma4:26b
ONEMIN_API_KEY=$onemKey
ONEMIN_MODEL=gpt-4o-mini
COMPOSE_PROFILES=$composeProfiles
WATCHTOWER_TOKEN=$(New-RandomSecret 32)
WATCHTOWER_MONITOR_ONLY=false
WATCHTOWER_SCHEDULE=0 0 4 * * *
TZ=Europe/Paris
"@
Set-Content -Path $envPath -Value $envContent -Encoding UTF8
Write-Ok ".env genere ($envPath)"
# ---------------------------------------------------------------------------
# 5. Pull + up
# ---------------------------------------------------------------------------
Write-Step "Telechargement des images Docker (peut prendre quelques minutes)"
docker compose pull
if ($LASTEXITCODE -ne 0) { Write-Err "Echec docker compose pull"; exit 1 }
Write-Step "Demarrage de la stack"
docker compose up -d
if ($LASTEXITCODE -ne 0) { Write-Err "Echec docker compose up"; exit 1 }
# ---------------------------------------------------------------------------
# 6. Recap
# ---------------------------------------------------------------------------
$url = "http://localhost:$WebPort"
Write-Host ""
Write-Host "============================================================" -ForegroundColor Green
Write-Host " LoreMindMJ est lance !" -ForegroundColor Green
Write-Host "============================================================" -ForegroundColor Green
Write-Host " URL : $url"
Write-Host " Identifiant : $adminUser"
Write-Host " Mot de passe : $adminPass"
Write-Host " Dossier : $InstallDir"
if ($autoUpdate) {
Write-Host " Auto-update : active (chaque nuit a 4h via Watchtower)" -ForegroundColor Green
} else {
Write-Host " Auto-update : desactive (mise a jour manuelle uniquement)"
}
Write-Host ""
Write-Host " Commandes utiles (depuis $InstallDir) :"
Write-Host " docker compose ps # etat"
Write-Host " docker compose logs -f # logs"
Write-Host " docker compose down # arret"
Write-Host " docker compose pull && docker compose up -d # mise a jour"
Write-Host ""
Start-Process $url

195
installers/install.sh Normal file
View File

@@ -0,0 +1,195 @@
#!/usr/bin/env bash
# ==========================================================================
# Installeur LoreMindMJ pour Linux (Debian/Ubuntu/Fedora/Arch)
# Usage :
# curl -fsSL https://git.igmlcreation.fr/ietm64/loremind/raw/branch/main/installers/install.sh | bash
# ==========================================================================
set -euo pipefail
INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/share/loremind}"
COMPOSE_URL="${COMPOSE_URL:-https://git.igmlcreation.fr/ietm64/loremind/raw/branch/main/docker-compose.yml}"
WEB_PORT="${WEB_PORT:-8081}"
NON_INTERACTIVE="${NON_INTERACTIVE:-0}"
c_cyan='\033[1;36m'; c_green='\033[1;32m'; c_yellow='\033[1;33m'; c_red='\033[1;31m'; c_off='\033[0m'
step() { echo -e "${c_cyan}==> $*${c_off}"; }
ok() { echo -e " ${c_green}OK${c_off} $*"; }
warn() { echo -e " ${c_yellow}!!${c_off} $*"; }
err() { echo -e " ${c_red}XX${c_off} $*" >&2; }
rand_hex() {
# $1 = nb de caracteres hex
local n="${1:-32}"
if command -v openssl >/dev/null 2>&1; then
openssl rand -hex $((n / 2))
else
head -c $((n * 2)) /dev/urandom | od -An -tx1 | tr -d ' \n' | head -c "$n"
fi
}
ask() {
# ask "prompt" "default"
local prompt="$1" def="${2:-}" reply
if [ "$NON_INTERACTIVE" = "1" ]; then
echo "$def"; return
fi
if [ -n "$def" ]; then
read -r -p " $prompt [$def] " reply </dev/tty || true
else
read -r -p " $prompt " reply </dev/tty || true
fi
echo "${reply:-$def}"
}
detect_pkg() {
if command -v apt-get >/dev/null 2>&1; then echo apt
elif command -v dnf >/dev/null 2>&1; then echo dnf
elif command -v pacman >/dev/null 2>&1; then echo pacman
else echo unknown
fi
}
install_docker() {
step "Installation de Docker..."
local pm; pm="$(detect_pkg)"
case "$pm" in
apt|dnf|pacman)
# Script officiel Docker (gere apt/dnf/pacman)
curl -fsSL https://get.docker.com | sh
;;
*)
err "Gestionnaire de paquets non reconnu. Installez Docker manuellement : https://docs.docker.com/engine/install/"
exit 1
;;
esac
if ! getent group docker >/dev/null; then sudo groupadd docker || true; fi
sudo usermod -aG docker "$USER" || true
sudo systemctl enable --now docker || true
warn "Vous avez ete ajoute au groupe 'docker'. Si docker echoue ensuite, deconnectez-vous puis reconnectez-vous (ou 'newgrp docker')."
}
# ---------------------------------------------------------------------------
echo
echo "============================================================"
echo -e " ${c_cyan}LoreMindMJ - Installeur Linux${c_off}"
echo "============================================================"
echo
# 1. Docker
step "Verification de Docker..."
if ! command -v docker >/dev/null 2>&1; then
install_docker
elif ! docker info >/dev/null 2>&1; then
warn "Docker installe mais inaccessible (daemon arrete ou groupe docker manquant)"
sudo systemctl start docker || true
if ! docker info >/dev/null 2>&1; then
sudo usermod -aG docker "$USER" || true
err "Re-essayez apres 'newgrp docker' ou une nouvelle session."
exit 1
fi
fi
ok "Docker fonctionnel"
# 2. docker compose v2
step "Verification de docker compose..."
if ! docker compose version >/dev/null 2>&1; then
err "Plugin 'docker compose' manquant. Sur Debian/Ubuntu : sudo apt install docker-compose-plugin"
exit 1
fi
ok "docker compose disponible"
# 3. Dossier + compose
step "Preparation du dossier $INSTALL_DIR"
mkdir -p "$INSTALL_DIR"
cd "$INSTALL_DIR"
step "Telechargement de docker-compose.yml"
curl -fsSL "$COMPOSE_URL" -o docker-compose.yml
ok "docker-compose.yml recupere"
# 4. .env
if [ -f .env ]; then
warn ".env existant -> sauvegarde en .env.bak"
cp .env .env.bak
fi
step "Configuration"
ADMIN_USERNAME="$(ask "Nom d'utilisateur admin" "admin")"
ADMIN_PASSWORD="$(ask "Mot de passe admin (vide = genere)" "")"
[ -z "$ADMIN_PASSWORD" ] && ADMIN_PASSWORD="$(rand_hex 16)"
LLM_PROVIDER="$(ask "Provider LLM (ollama / onemin)" "ollama")"
ONEMIN_API_KEY=""
if [ "$LLM_PROVIDER" = "onemin" ] && [ "$NON_INTERACTIVE" != "1" ]; then
ONEMIN_API_KEY="$(ask "Cle API 1min.ai" "")"
fi
AUTO_UPDATE_REPLY="$(ask "Activer les mises a jour auto (chaque nuit a 4h) ? [O/n]" "O")"
case "$AUTO_UPDATE_REPLY" in
n|N|no|non|No|Non) COMPOSE_PROFILES="" ; AUTO_UPDATE=0 ;;
*) COMPOSE_PROFILES="autoupdate" ; AUTO_UPDATE=1 ;;
esac
cat > .env <<EOF
# Genere par install.sh le $(date '+%Y-%m-%d %H:%M')
REGISTRY=git.igmlcreation.fr
TAG=latest
WEB_PORT=${WEB_PORT}
POSTGRES_DB=loremind
POSTGRES_USER=loremind
POSTGRES_PASSWORD=$(rand_hex 24)
ADMIN_USERNAME=${ADMIN_USERNAME}
ADMIN_PASSWORD=${ADMIN_PASSWORD}
BRAIN_INTERNAL_SECRET=$(rand_hex 32)
MINIO_USER=minioadmin
MINIO_PASSWORD=$(rand_hex 24)
LLM_PROVIDER=${LLM_PROVIDER}
OLLAMA_BASE_URL=http://host.docker.internal:11434
LLM_MODEL=gemma4:26b
ONEMIN_API_KEY=${ONEMIN_API_KEY}
ONEMIN_MODEL=gpt-4o-mini
COMPOSE_PROFILES=${COMPOSE_PROFILES}
WATCHTOWER_TOKEN=$(rand_hex 32)
WATCHTOWER_MONITOR_ONLY=false
WATCHTOWER_SCHEDULE=0 0 4 * * *
TZ=$(timedatectl show -p Timezone --value 2>/dev/null || echo Europe/Paris)
EOF
chmod 600 .env
ok ".env genere ($INSTALL_DIR/.env)"
# 5. Pull + up
step "Telechargement des images (peut prendre quelques minutes)"
docker compose pull
step "Demarrage de la stack"
docker compose up -d
# 6. Recap
URL="http://localhost:${WEB_PORT}"
echo
echo -e "${c_green}============================================================${c_off}"
echo -e "${c_green} LoreMindMJ est lance !${c_off}"
echo -e "${c_green}============================================================${c_off}"
echo " URL : $URL"
echo " Identifiant : $ADMIN_USERNAME"
echo " Mot de passe : $ADMIN_PASSWORD"
echo " Dossier : $INSTALL_DIR"
if [ "$AUTO_UPDATE" = "1" ]; then
echo -e " Auto-update : ${c_green}active${c_off} (chaque nuit a 4h via Watchtower)"
else
echo " Auto-update : desactive (mise a jour manuelle uniquement)"
fi
echo
echo " Commandes utiles (depuis $INSTALL_DIR) :"
echo " docker compose ps # etat"
echo " docker compose logs -f # logs"
echo " docker compose down # arret"
echo " docker compose pull && docker compose up -d # mise a jour"
echo
if command -v xdg-open >/dev/null 2>&1; then xdg-open "$URL" >/dev/null 2>&1 || true; fi