Add autocomplete status check and restructure test environments
Add Ollama autocomplete status endpoint to AI routes, update sync panel UI to display autocomplete state, replace monolithic test compose files with separate Linux/Windows variants, and add PowerShell build script for Windows deployment
This commit is contained in:
@@ -37,10 +37,45 @@ async def api_status():
|
||||
"available": has_key,
|
||||
"model": PROVIDERS[name]["model"] if has_key else None,
|
||||
}
|
||||
|
||||
# ---- Autocomplete (Ollama) status ----
|
||||
ollama_cfg = PROVIDERS.get("ollama", {})
|
||||
ollama_url = (ollama_cfg.get("base_url") or "").rstrip("/v1").rstrip("/")
|
||||
ollama_model = ollama_cfg.get("model", "")
|
||||
autocomplete = {
|
||||
"available": False,
|
||||
"server_ok": False,
|
||||
"model_loaded": False,
|
||||
"model": ollama_model,
|
||||
"error": None,
|
||||
}
|
||||
if ollama_url:
|
||||
try:
|
||||
import httpx
|
||||
async with httpx.AsyncClient(timeout=5.0) as client:
|
||||
# Check server health + list loaded models
|
||||
r = await client.get(f"{ollama_url}/api/tags")
|
||||
if r.status_code == 200:
|
||||
autocomplete["server_ok"] = True
|
||||
data = r.json()
|
||||
models = [m.get("name", "") for m in data.get("models", [])]
|
||||
# Check if our model (with or without tag) is loaded
|
||||
model_base = ollama_model.split(":")[0]
|
||||
autocomplete["model_loaded"] = any(
|
||||
m == ollama_model or m.startswith(model_base + ":")
|
||||
for m in models
|
||||
)
|
||||
autocomplete["available"] = autocomplete["model_loaded"]
|
||||
else:
|
||||
autocomplete["error"] = f"HTTP {r.status_code}"
|
||||
except Exception as e:
|
||||
autocomplete["error"] = str(e)
|
||||
|
||||
return {
|
||||
"configured": any(p["available"] for p in providers.values()),
|
||||
"default_provider": DEFAULT_PROVIDER,
|
||||
"providers": providers,
|
||||
"autocomplete": autocomplete,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
# =============================================================================
|
||||
# ObsiGate — Script de build et déploiement Docker (Windows PowerShell)
|
||||
# =============================================================================
|
||||
#
|
||||
# Usage :
|
||||
# .\build.ps1 [-Prod | -Test] [OPTIONS]
|
||||
#
|
||||
# Environnements :
|
||||
# -Prod Production (défaut) — docker-compose.yml
|
||||
# -Test Test Windows (Docker Desktop) — docker-compose.test-win.yml
|
||||
#
|
||||
# Options :
|
||||
# -BuildOnly Construit l'image sans démarrer le conteneur
|
||||
# -NoCache Rebuild complet sans cache Docker (défaut)
|
||||
# -UseCache Utilise le cache Docker (plus rapide si peu de changements)
|
||||
# -Help Affiche cette aide
|
||||
#
|
||||
# Exemples :
|
||||
# .\build.ps1 # Production
|
||||
# .\build.ps1 -Test # Test Windows
|
||||
# .\build.ps1 -Test -BuildOnly # Build test sans démarrer
|
||||
# .\build.ps1 -Prod -UseCache # Production avec cache
|
||||
# =============================================================================
|
||||
|
||||
param(
|
||||
[switch]$Prod,
|
||||
[switch]$Test,
|
||||
[switch]$BuildOnly,
|
||||
[switch]$NoCache,
|
||||
[switch]$UseCache,
|
||||
[switch]$Help
|
||||
)
|
||||
|
||||
# ----- Helpers -----
|
||||
function Write-Info { Write-Host "[INFO] $args" -ForegroundColor Cyan }
|
||||
function Write-Ok { Write-Host "[OK] $args" -ForegroundColor Green }
|
||||
function Write-Warn { Write-Host "[WARN] $args" -ForegroundColor Yellow }
|
||||
function Write-Error { Write-Host "[ERR] $args" -ForegroundColor Red }
|
||||
|
||||
# ----- Help -----
|
||||
if ($Help) {
|
||||
Get-Help $PSCommandPath -Detailed
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ----- Select environment -----
|
||||
$EnvMode = "prod"
|
||||
$ComposeEnvName = "production"
|
||||
if ($Test) {
|
||||
$EnvMode = "test-win"
|
||||
$ComposeEnvName = "test Windows"
|
||||
}
|
||||
|
||||
switch ($EnvMode) {
|
||||
"prod" { $ComposeFile = "docker-compose.yml" }
|
||||
"test-win" { $ComposeFile = "docker-compose.test-win.yml" }
|
||||
}
|
||||
|
||||
# ----- Pre-flight checks -----
|
||||
$dockerVersion = docker --version 2>$null
|
||||
if (-not $dockerVersion) {
|
||||
Write-Error "Docker est introuvable. Installez Docker Desktop avant de continuer."
|
||||
exit 1
|
||||
}
|
||||
Write-Ok "Docker détecté"
|
||||
|
||||
$composeVersion = docker compose version 2>$null
|
||||
if (-not $composeVersion) {
|
||||
Write-Error "Docker Compose est introuvable (plugin docker compose requis)."
|
||||
exit 1
|
||||
}
|
||||
Write-Ok $composeVersion.Trim()
|
||||
|
||||
# ----- Validate compose file -----
|
||||
if (-not (Test-Path $ComposeFile)) {
|
||||
Write-Error "$ComposeFile introuvable dans $(Get-Location)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Info "=== ObsiGate — Build & Deploy ($ComposeEnvName) ==="
|
||||
Write-Info "Fichier compose : $ComposeFile"
|
||||
|
||||
# ----- Test mode: create test directories -----
|
||||
if ($EnvMode -eq "test-win") {
|
||||
Write-Info "Création des dossiers de test..."
|
||||
|
||||
$testVault = "C:\dev\git\python\ObsiGate\test_vault"
|
||||
$testDir = "C:\dev\git\python\ObsiGate\test_dir"
|
||||
|
||||
if (-not (Test-Path $testVault)) {
|
||||
New-Item -ItemType Directory -Force -Path $testVault | Out-Null
|
||||
}
|
||||
if (-not (Test-Path $testDir)) {
|
||||
New-Item -ItemType Directory -Force -Path $testDir | Out-Null
|
||||
}
|
||||
|
||||
$welcomeFile = Join-Path $testVault "Accueil.md"
|
||||
if (-not (Test-Path $welcomeFile)) {
|
||||
@"
|
||||
# Bienvenue dans le vault de test
|
||||
|
||||
Ce vault est utilisé pour les tests de développement d'ObsiGate.
|
||||
"@ | Out-File -Encoding UTF8 -FilePath $welcomeFile
|
||||
Write-Ok "Note de test créée : test_vault\Accueil.md"
|
||||
}
|
||||
}
|
||||
|
||||
# ----- Build the image -----
|
||||
$BuildArgs = @("-f", $ComposeFile)
|
||||
if (-not $UseCache) {
|
||||
$BuildArgs += "--no-cache"
|
||||
Write-Info "Construction de l'image Docker (sans cache)..."
|
||||
} else {
|
||||
Write-Info "Construction de l'image Docker (avec cache)..."
|
||||
}
|
||||
|
||||
docker compose @BuildArgs build
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Échec de la construction de l'image Docker."
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
Write-Ok "Image construite avec succès"
|
||||
|
||||
# ----- Stop if build-only -----
|
||||
if ($BuildOnly) {
|
||||
Write-Host ""
|
||||
Write-Info "Mode build-only : conteneur non démarré."
|
||||
Write-Info "Pour démarrer manuellement : docker compose -f $ComposeFile up -d"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ----- Start the container -----
|
||||
Write-Info "Démarrage du conteneur..."
|
||||
docker compose -f $ComposeFile up -d --remove-orphans
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Échec du démarrage du conteneur."
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
Write-Ok "Conteneur démarré"
|
||||
|
||||
# ----- Show status & tip -----
|
||||
Write-Host ""
|
||||
Write-Info "Statut du conteneur :"
|
||||
docker compose -f $ComposeFile ps
|
||||
|
||||
Write-Host ""
|
||||
Write-Info "Application accessible sur http://localhost:2020"
|
||||
if ($EnvMode -eq "prod") {
|
||||
Write-Info "Login : admin / (mot de passe dans .env)"
|
||||
} else {
|
||||
Write-Info "Login : admin / test123"
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
Write-Info "Logs (Ctrl+C pour quitter) :"
|
||||
docker compose -f $ComposeFile logs --tail=20 -f
|
||||
@@ -4,7 +4,12 @@
|
||||
# =============================================================================
|
||||
#
|
||||
# Usage :
|
||||
# ./build.sh [OPTIONS]
|
||||
# ./build.sh [--prod | --test-win | --test-linux] [OPTIONS]
|
||||
#
|
||||
# Environnements :
|
||||
# --prod Production (défaut) — docker-compose.yml
|
||||
# --test-win Test Windows (Docker Desktop) — docker-compose.test-win.yml
|
||||
# --test-linux Test Linux — docker-compose.test-linux.yml
|
||||
#
|
||||
# Options :
|
||||
# --help, -h Affiche cette aide
|
||||
@@ -16,17 +21,17 @@
|
||||
#
|
||||
# Étapes exécutées :
|
||||
# 1. Vérifie que Docker et Docker Compose sont installés
|
||||
# 2. Valide le fichier docker-compose.yml
|
||||
# 3. Vérifie que les volumes montés existent sur le host
|
||||
# 2. Valide le fichier docker-compose choisi
|
||||
# 3. Crée les dossiers de test si nécessaire (modes test uniquement)
|
||||
# 4. Construit l'image Docker (multi-stage, ~180MB)
|
||||
# 5. Démarre le conteneur en arrière-plan
|
||||
# 6. Affiche le statut et les logs
|
||||
#
|
||||
# Exemples :
|
||||
# ./build.sh # Build from scratch + démarrage
|
||||
# ./build.sh --cache # Build avec cache + démarrage
|
||||
# ./build.sh --build-only # Build uniquement, sans démarrer
|
||||
# ./build.sh --progress=plain # Sortie verbeuse
|
||||
# ./build.sh # Production
|
||||
# ./build.sh --test-win # Test Windows
|
||||
# ./build.sh --test-linux --cache # Test Linux avec cache
|
||||
# ./build.sh --test-win --build-only # Build test Windows sans démarrer
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
@@ -43,6 +48,8 @@ show_help() {
|
||||
}
|
||||
|
||||
# ----- Parse args -----
|
||||
ENV_MODE="prod"
|
||||
COMPOSE_FILE=""
|
||||
BUILD_ONLY=false
|
||||
USE_CACHE=false
|
||||
BUILD_ARGS=()
|
||||
@@ -52,6 +59,15 @@ for arg in "$@"; do
|
||||
--help|-h)
|
||||
show_help
|
||||
;;
|
||||
--prod)
|
||||
ENV_MODE="prod"
|
||||
;;
|
||||
--test-win)
|
||||
ENV_MODE="test-win"
|
||||
;;
|
||||
--test-linux)
|
||||
ENV_MODE="test-linux"
|
||||
;;
|
||||
--build-only)
|
||||
BUILD_ONLY=true
|
||||
;;
|
||||
@@ -67,6 +83,26 @@ for arg in "$@"; do
|
||||
esac
|
||||
done
|
||||
|
||||
# ----- Select compose file -----
|
||||
case "$ENV_MODE" in
|
||||
prod)
|
||||
COMPOSE_FILE="docker-compose.yml"
|
||||
COMPOSE_ENV_NAME="production"
|
||||
;;
|
||||
test-win)
|
||||
COMPOSE_FILE="docker-compose.test-win.yml"
|
||||
COMPOSE_ENV_NAME="test Windows"
|
||||
;;
|
||||
test-linux)
|
||||
COMPOSE_FILE="docker-compose.test-linux.yml"
|
||||
COMPOSE_ENV_NAME="test Linux"
|
||||
;;
|
||||
*)
|
||||
error "Environnement inconnu : $ENV_MODE"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# ----- Pre-flight checks -----
|
||||
if ! command -v docker &>/dev/null; then
|
||||
error "Docker est introuvable. Installez Docker avant de continuer."
|
||||
@@ -87,21 +123,46 @@ fi
|
||||
ok "$($COMPOSE_CMD version --short 2>/dev/null || $COMPOSE_CMD version | head -1)"
|
||||
|
||||
# ----- Validate compose file -----
|
||||
COMPOSE_FILE="docker-compose.yml"
|
||||
if [[ ! -f "$COMPOSE_FILE" ]]; then
|
||||
error "$COMPOSE_FILE introuvable dans $(pwd)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
info "=== ObsiGate — Build & Deploy ==="
|
||||
echo ""
|
||||
info "=== ObsiGate — Build & Deploy ($COMPOSE_ENV_NAME) ==="
|
||||
info "Fichier compose : $COMPOSE_FILE"
|
||||
|
||||
# ----- Extract volume mount paths from compose file and warn about missing ones -----
|
||||
# ----- Test mode: create test directories -----
|
||||
if [[ "$ENV_MODE" == "test-win" ]]; then
|
||||
info "Création des dossiers de test Windows..."
|
||||
mkdir -p /c/dev/git/python/ObsiGate/test_vault
|
||||
mkdir -p /c/dev/git/python/ObsiGate/test_dir
|
||||
# Create a sample note for the test vault
|
||||
if [[ ! -f /c/dev/git/python/ObsiGate/test_vault/Accueil.md ]]; then
|
||||
echo "# Bienvenue dans le vault de test" > /c/dev/git/python/ObsiGate/test_vault/Accueil.md
|
||||
echo "" >> /c/dev/git/python/ObsiGate/test_vault/Accueil.md
|
||||
echo "Ce vault est utilisé pour les tests de développement d'ObsiGate." >> /c/dev/git/python/ObsiGate/test_vault/Accueil.md
|
||||
ok "Note de test créée : test_vault/Accueil.md"
|
||||
fi
|
||||
elif [[ "$ENV_MODE" == "test-linux" ]]; then
|
||||
info "Création des dossiers de test Linux..."
|
||||
mkdir -p /tmp/obsigate-test-vault
|
||||
mkdir -p /tmp/obsigate-test-dir
|
||||
mkdir -p /tmp/obsigate-test-data
|
||||
chmod -R 777 /tmp/obsigate-test-data 2>/dev/null || true
|
||||
if [[ ! -f /tmp/obsigate-test-vault/Accueil.md ]]; then
|
||||
echo "# Bienvenue dans le vault de test" > /tmp/obsigate-test-vault/Accueil.md
|
||||
echo "" >> /tmp/obsigate-test-vault/Accueil.md
|
||||
echo "Ce vault est utilisé pour les tests de développement d'ObsiGate." >> /tmp/obsigate-test-vault/Accueil.md
|
||||
ok "Note de test créée : test-vault/Accueil.md"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ----- Extract volume mount paths and warn about missing ones -----
|
||||
info "Vérification des volumes montés sur le host..."
|
||||
VOLUME_COUNT=0
|
||||
MISSING_COUNT=0
|
||||
while IFS= read -r line; do
|
||||
# Lines like: - /host/path:/container/path
|
||||
if [[ "$line" =~ ^[[:space:]]*-[[:space:]]+(/[^:]+): ]]; then
|
||||
host_path="${BASH_REMATCH[1]}"
|
||||
VOLUME_COUNT=$((VOLUME_COUNT + 1))
|
||||
@@ -115,7 +176,7 @@ while IFS= read -r line; do
|
||||
done < <(grep -E '^\s+-\s+/' "$COMPOSE_FILE")
|
||||
|
||||
if [[ "$VOLUME_COUNT" -eq 0 ]]; then
|
||||
warn "Aucun volume host détecté dans $COMPOSE_FILE. Pensez à configurer vos vaults."
|
||||
warn "Aucun volume host détecté dans $COMPOSE_FILE."
|
||||
fi
|
||||
if [[ "$MISSING_COUNT" -gt 0 ]]; then
|
||||
warn "$MISSING_COUNT volume(s) absent(s). Vérifiez vos chemins dans $COMPOSE_FILE."
|
||||
@@ -132,27 +193,27 @@ else
|
||||
info "Construction de l'image Docker (avec cache)..."
|
||||
fi
|
||||
|
||||
$COMPOSE_CMD build "${BUILD_FLAGS[@]}" "${BUILD_ARGS[@]}"
|
||||
$COMPOSE_CMD -f "$COMPOSE_FILE" build "${BUILD_FLAGS[@]}" "${BUILD_ARGS[@]}"
|
||||
ok "Image construite avec succès"
|
||||
|
||||
# ----- Stop if build-only -----
|
||||
if [[ "$BUILD_ONLY" == true ]]; then
|
||||
echo ""
|
||||
info "Mode build-only : conteneur non démarré."
|
||||
info "Pour démarrer manuellement : $COMPOSE_CMD up -d"
|
||||
info "Pour démarrer manuellement : $COMPOSE_CMD -f $COMPOSE_FILE up -d"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ----- Start the container -----
|
||||
info "Démarrage du conteneur..."
|
||||
$COMPOSE_CMD up -d --remove-orphans
|
||||
$COMPOSE_CMD -f "$COMPOSE_FILE" up -d --remove-orphans
|
||||
ok "Conteneur démarré"
|
||||
|
||||
# ----- Show status -----
|
||||
echo ""
|
||||
info "Statut du conteneur :"
|
||||
$COMPOSE_CMD ps
|
||||
$COMPOSE_CMD -f "$COMPOSE_FILE" ps
|
||||
|
||||
echo ""
|
||||
info "Logs récents (Ctrl+C pour quitter) :"
|
||||
$COMPOSE_CMD logs --tail=20 -f
|
||||
$COMPOSE_CMD -f "$COMPOSE_FILE" logs --tail=20 -f
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
services:
|
||||
obsigate-podman:
|
||||
build:
|
||||
context: .
|
||||
image: obsigate:latest
|
||||
container_name: obsigate-podman
|
||||
user: "1000:1000"
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "2022:8080"
|
||||
volumes:
|
||||
- /mnt/c/dev/git/python/obsigate/test_vault:/vaults/TestVault
|
||||
- /mnt/c/dev/git/python/obsigate/test_dir:/vaults/TestDir
|
||||
- /mnt/c/dev/git/python/obsigate/data:/app/data
|
||||
environment:
|
||||
- VAULT_1_NAME=TestVault
|
||||
- VAULT_1_PATH=/vaults/TestVault
|
||||
- DIR_1_NAME=TestDir
|
||||
- DIR_1_PATH=/vaults/TestDir
|
||||
- OBSIGATE_AUTH_ENABLED=false
|
||||
- OBSIGATE_ADMIN_USER=admin
|
||||
- OBSIGATE_ADMIN_PASSWORD=test123
|
||||
- AI_DEFAULT_PROVIDER=deepseek
|
||||
- OLLAMA_BASE_URL=http://ollama:11434/v1
|
||||
- OLLAMA_MODEL=qwen2.5-coder:1.5b
|
||||
|
||||
ollama:
|
||||
image: ollama/ollama:latest
|
||||
container_name: obsigate-ollama
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "11434:11434"
|
||||
volumes:
|
||||
- ollama_data:/root/.ollama
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
command:
|
||||
- |
|
||||
ollama serve &
|
||||
sleep 5
|
||||
ollama pull qwen2.5-coder:1.5b
|
||||
wait
|
||||
|
||||
volumes:
|
||||
ollama_data:
|
||||
@@ -0,0 +1,53 @@
|
||||
# =============================================================================
|
||||
# ObsiGate — Environnement de TEST Linux
|
||||
# =============================================================================
|
||||
# Utilisé par : build.sh --test-linux
|
||||
# Ports : 2021 (app), 11435 (Ollama) — ports distincts pour éviter conflits
|
||||
# Vaults : dossiers temporaires (/tmp/obsigate-test-*)
|
||||
# Auth : activé, admin / test123
|
||||
# =============================================================================
|
||||
|
||||
services:
|
||||
obsigate:
|
||||
build:
|
||||
context: .
|
||||
image: obsigate:latest
|
||||
container_name: obsigate-test
|
||||
user: "1000:1000"
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "2021:8080"
|
||||
volumes:
|
||||
- /tmp/obsigate-test-vault:/vaults/TestVault
|
||||
- /tmp/obsigate-test-dir:/vaults/TestDir
|
||||
- /tmp/obsigate-test-data:/app/data
|
||||
environment:
|
||||
- VAULT_1_NAME=TestVault
|
||||
- VAULT_1_PATH=/vaults/TestVault
|
||||
- DIR_1_NAME=TestDir
|
||||
- DIR_1_PATH=/vaults/TestDir
|
||||
- OBSIGATE_AUTH_ENABLED=true
|
||||
- OBSIGATE_ADMIN_USER=admin
|
||||
- OBSIGATE_ADMIN_PASSWORD=test123
|
||||
- AI_DEFAULT_PROVIDER=deepseek
|
||||
- OLLAMA_BASE_URL=http://ollama:11435/v1
|
||||
- OLLAMA_MODEL=qwen2.5-coder:1.5b
|
||||
|
||||
ollama:
|
||||
image: ollama/ollama:latest
|
||||
container_name: obsigate-test-ollama
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "11435:11434"
|
||||
volumes:
|
||||
- ollama_data:/root/.ollama
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
command:
|
||||
- |
|
||||
ollama serve &
|
||||
sleep 5
|
||||
ollama pull qwen2.5-coder:1.5b
|
||||
wait
|
||||
|
||||
volumes:
|
||||
ollama_data:
|
||||
@@ -0,0 +1,55 @@
|
||||
# =============================================================================
|
||||
# ObsiGate — Environnement de TEST Windows (Docker Desktop)
|
||||
# =============================================================================
|
||||
# Utilisé par : build.sh --test-win | build.ps1 -Test
|
||||
# Ports : 2020 (app), 11434 (Ollama)
|
||||
# Vaults : chemins Windows via Docker Desktop (/c/dev/git/...)
|
||||
# Auth : activé, admin / test123
|
||||
# NOTE : Pas de user: "1000:1000" → tourne en root (évite les problèmes
|
||||
# de permissions NTFS sur les volumes bind-mount Windows)
|
||||
# =============================================================================
|
||||
|
||||
services:
|
||||
obsigate:
|
||||
build:
|
||||
context: .
|
||||
image: obsigate:latest
|
||||
container_name: obsigate-test
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "2020:8080"
|
||||
volumes:
|
||||
- /c/dev/git/python/ObsiGate/test_vault:/vaults/TestVault
|
||||
- /c/dev/git/python/ObsiGate/test_dir:/vaults/TestDir
|
||||
- obsigate_data:/app/data
|
||||
environment:
|
||||
- VAULT_1_NAME=TestVault
|
||||
- VAULT_1_PATH=/vaults/TestVault
|
||||
- DIR_1_NAME=TestDir
|
||||
- DIR_1_PATH=/vaults/TestDir
|
||||
- OBSIGATE_AUTH_ENABLED=true
|
||||
- OBSIGATE_ADMIN_USER=admin
|
||||
- OBSIGATE_ADMIN_PASSWORD=test123
|
||||
- AI_DEFAULT_PROVIDER=deepseek
|
||||
- OLLAMA_BASE_URL=http://ollama:11434/v1
|
||||
- OLLAMA_MODEL=qwen2.5-coder:1.5b
|
||||
|
||||
ollama:
|
||||
image: ollama/ollama:latest
|
||||
container_name: obsigate-test-ollama
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "11434:11434"
|
||||
volumes:
|
||||
- ollama_data:/root/.ollama
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
command:
|
||||
- |
|
||||
ollama serve &
|
||||
sleep 5
|
||||
ollama pull qwen2.5-coder:1.5b
|
||||
wait
|
||||
|
||||
volumes:
|
||||
ollama_data:
|
||||
obsigate_data:
|
||||
@@ -1,44 +0,0 @@
|
||||
services:
|
||||
obsigate:
|
||||
build:
|
||||
context: .
|
||||
image: obsigate:latest
|
||||
container_name: obsigate
|
||||
user: "1000:1000"
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "2020:8080"
|
||||
volumes:
|
||||
- /c/dev/git/python/obsigate/test_vault:/vaults/TestVault
|
||||
- /c/dev/git/python/obsigate/test_dir:/vaults/TestDir
|
||||
- /c/dev/git/python/obsigate/data:/app/data
|
||||
environment:
|
||||
- VAULT_1_NAME=TestVault
|
||||
- VAULT_1_PATH=/vaults/TestVault
|
||||
- DIR_1_NAME=TestDir
|
||||
- DIR_1_PATH=/vaults/TestDir
|
||||
- OBSIGATE_AUTH_ENABLED=false
|
||||
- OBSIGATE_ADMIN_USER=admin
|
||||
- OBSIGATE_ADMIN_PASSWORD=test123
|
||||
- AI_DEFAULT_PROVIDER=deepseek
|
||||
- OLLAMA_BASE_URL=http://ollama:11434/v1
|
||||
- OLLAMA_MODEL=qwen2.5-coder:1.5b
|
||||
|
||||
ollama:
|
||||
image: ollama/ollama:latest
|
||||
container_name: obsigate-ollama
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "11434:11434"
|
||||
volumes:
|
||||
- ollama_data:/root/.ollama
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
command:
|
||||
- |
|
||||
ollama serve &
|
||||
sleep 5
|
||||
ollama pull qwen2.5-coder:1.5b
|
||||
wait
|
||||
|
||||
volumes:
|
||||
ollama_data:
|
||||
+11
-3
@@ -1,3 +1,12 @@
|
||||
# =============================================================================
|
||||
# ObsiGate — Déploiement PRODUCTION (serveur Linux)
|
||||
# =============================================================================
|
||||
# Utilisé par : build.sh --prod (défaut)
|
||||
# Ports : 2020 (app), 11434 (Ollama)
|
||||
# Vaults : chemins absolus Linux (/NFS/..., /DOCKER_CONFIG/...)
|
||||
# Auth : activé, mot de passe admin dans .env
|
||||
# =============================================================================
|
||||
|
||||
services:
|
||||
obsigate:
|
||||
build:
|
||||
@@ -41,11 +50,10 @@ services:
|
||||
- VAULT_5_PATH=/vaults/SessionsManager
|
||||
- DIR_1_NAME=Bruno
|
||||
- DIR_1_PATH=/vaults/bruno
|
||||
# Auth configuration (edit values in .env file)
|
||||
- OBSIGATE_AUTH_ENABLED=true
|
||||
- OBSIGATE_ADMIN_USER=admin
|
||||
# OBSIGATE_ADMIN_PASSWORD → définir dans .env (pas ici !)
|
||||
# - OBSIGATE_SECURE_COOKIES=false # Set true if behind HTTPS reverse proxy
|
||||
# OBSIGATE_ADMIN_PASSWORD → .env
|
||||
# OBSIGATE_SECURE_COOKIES=true # si derrière reverse proxy HTTPS
|
||||
- OLLAMA_BASE_URL=http://ollama:11434/v1
|
||||
- OLLAMA_MODEL=qwen2.5-coder:1.5b
|
||||
env_file:
|
||||
|
||||
+58
-23
@@ -341,15 +341,18 @@ function _renderSyncPanel(panel) {
|
||||
|
||||
let _aiProcessing = false;
|
||||
let _aiConfigured = null; // null = unknown, true/false
|
||||
let _aiAutocomplete = null; // null = unknown, {available, server_ok, model_loaded, model, error}
|
||||
|
||||
/** Check if AI is configured by calling the /api/ai/status endpoint.
|
||||
* Updates _aiConfigured and refreshes the panel if open. */
|
||||
* Updates _aiConfigured, _aiAutocomplete and refreshes the panel if open. */
|
||||
async function _checkAIStatus() {
|
||||
try {
|
||||
const status = await api('/api/ai/status');
|
||||
_aiConfigured = status.configured === true;
|
||||
_aiAutocomplete = status.autocomplete || null;
|
||||
} catch {
|
||||
_aiConfigured = false;
|
||||
_aiAutocomplete = null;
|
||||
}
|
||||
_updateAIStatusInPanel();
|
||||
}
|
||||
@@ -359,34 +362,66 @@ function _renderAIStatusSection(panel) {
|
||||
section.className = 'sync-panel__ai-section';
|
||||
section.id = 'sync-panel-ai';
|
||||
|
||||
let statusIcon, statusText, statusClass;
|
||||
// ---- External provider status ----
|
||||
let extIcon, extText, extClass;
|
||||
if (_aiProcessing) {
|
||||
statusIcon = 'loader';
|
||||
statusText = 'Traitement en cours…';
|
||||
statusClass = 'sync-panel__ai--processing';
|
||||
extIcon = 'loader';
|
||||
extText = 'Traitement en cours…';
|
||||
extClass = 'sync-panel__ai--processing';
|
||||
} else if (_aiConfigured === true) {
|
||||
statusIcon = 'check-circle';
|
||||
statusText = 'IA configurée et active';
|
||||
statusClass = 'sync-panel__ai--active';
|
||||
extIcon = 'check-circle';
|
||||
extText = 'Fournisseur externe actif';
|
||||
extClass = 'sync-panel__ai--active';
|
||||
} else if (_aiConfigured === false) {
|
||||
statusIcon = 'x-circle';
|
||||
statusText = 'IA non configurée';
|
||||
statusClass = 'sync-panel__ai--inactive';
|
||||
extIcon = 'x-circle';
|
||||
extText = 'Aucun fournisseur';
|
||||
extClass = 'sync-panel__ai--inactive';
|
||||
} else {
|
||||
statusIcon = 'help-circle';
|
||||
statusText = 'Statut IA inconnu';
|
||||
statusClass = 'sync-panel__ai--unknown';
|
||||
extIcon = 'help-circle';
|
||||
extText = 'Vérification…';
|
||||
extClass = 'sync-panel__ai--unknown';
|
||||
}
|
||||
|
||||
section.innerHTML = `
|
||||
<div class="sync-panel__ai-header">
|
||||
<span class="sync-panel__ai-title">Intelligence Artificielle</span>
|
||||
<span class="sync-panel__ai-state ${statusClass}" data-lucide-icon="${statusIcon}">
|
||||
<i data-lucide="${statusIcon}" style="width:14px;height:14px"></i>
|
||||
${statusText}
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
// ---- Autocomplete (Ollama) status ----
|
||||
let acIcon, acText, acClass;
|
||||
if (_aiAutocomplete && _aiAutocomplete.available) {
|
||||
acIcon = 'check-circle';
|
||||
acText = 'Auto-complétion active';
|
||||
acClass = 'sync-panel__ai--active';
|
||||
} else if (_aiAutocomplete && _aiAutocomplete.server_ok && !_aiAutocomplete.model_loaded) {
|
||||
acIcon = 'alert-triangle';
|
||||
acText = 'Modèle non chargé';
|
||||
acClass = 'sync-panel__ai--inactive';
|
||||
} else if (_aiAutocomplete && _aiAutocomplete.error) {
|
||||
acIcon = 'x-circle';
|
||||
acText = 'Serveur Ollama injoignable';
|
||||
acClass = 'sync-panel__ai--inactive';
|
||||
} else if (_aiAutocomplete === null) {
|
||||
acIcon = 'help-circle';
|
||||
acText = 'Vérification…';
|
||||
acClass = 'sync-panel__ai--unknown';
|
||||
} else {
|
||||
acIcon = 'x-circle';
|
||||
acText = 'Non configuré';
|
||||
acClass = 'sync-panel__ai--inactive';
|
||||
}
|
||||
|
||||
section.innerHTML = '' +
|
||||
'<div class="sync-panel__ai-title">Intelligence Artificielle</div>' +
|
||||
'<div class="sync-panel__ai-row">' +
|
||||
'<span class="sync-panel__ai-label">Fournisseur</span>' +
|
||||
'<span class="sync-panel__ai-state ' + extClass + '" data-lucide-icon="' + extIcon + '">' +
|
||||
'<i data-lucide="' + extIcon + '" style="width:14px;height:14px"></i>' +
|
||||
extText +
|
||||
'</span>' +
|
||||
'</div>' +
|
||||
'<div class="sync-panel__ai-row">' +
|
||||
'<span class="sync-panel__ai-label">Auto-complétion</span>' +
|
||||
'<span class="sync-panel__ai-state ' + acClass + '" data-lucide-icon="' + acIcon + '">' +
|
||||
'<i data-lucide="' + acIcon + '" style="width:14px;height:14px"></i>' +
|
||||
acText +
|
||||
'</span>' +
|
||||
'</div>';
|
||||
|
||||
panel.appendChild(section);
|
||||
// Trigger icon refresh via global
|
||||
|
||||
+13
-6
@@ -4239,16 +4239,23 @@ body.resizing-v {
|
||||
padding: 10px 14px;
|
||||
}
|
||||
|
||||
.sync-panel__ai-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.sync-panel__ai-title {
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.sync-panel__ai-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.sync-panel__ai-label {
|
||||
font-size: 0.72rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.sync-panel__ai-state {
|
||||
|
||||
Reference in New Issue
Block a user