chore(ci): ajout workflow publish + nettoyage services + conf hosts
Some checks failed
publish-images / docker-build-and-push (push) Failing after 23s

This commit is contained in:
Nicolas Cantu 2025-09-11 16:49:23 +02:00
parent fddfa6f7bf
commit f50481cc38
37 changed files with 860 additions and 3369 deletions

View File

@ -0,0 +1,64 @@
name: publish-images
on:
push:
branches: [ dev, main ]
tags: [ 'v*' ]
env:
REGISTRY: git.4nkweb.com
IMAGE_OWNER: 4nk
IMAGE_REPO: 4NK_IA_back
jobs:
docker-build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.USER }}
password: ${{ secrets.TOKEN }}
- name: Compute tags
id: meta
run: |
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
VERSION_TAG="${GITHUB_REF_NAME}"
else
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-8)
VERSION_TAG="sha-${SHORT_SHA}"
fi
echo "version=${VERSION_TAG}" >> $GITHUB_OUTPUT
- name: Build & Push host-api
uses: docker/build-push-action@v6
with:
context: ./docker/host-api
file: ./docker/host-api/Dockerfile
push: true
platforms: linux/amd64
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_REPO }}/host-api:${{ steps.meta.outputs.version }}
${{ env.REGISTRY }}/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_REPO }}/host-api:latest
- name: Build & Push worker
uses: docker/build-push-action@v6
with:
context: .
file: ./docker/worker/Dockerfile
push: true
platforms: linux/amd64
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_REPO }}/worker:${{ steps.meta.outputs.version }}
${{ env.REGISTRY }}/${{ env.IMAGE_OWNER }}/${{ env.IMAGE_REPO }}/worker:latest

View File

@ -1,67 +0,0 @@
SHELL := /bin/bash
ENV ?= infra/.env
# Charger les variables d'environnement
include $(ENV)
export
.PHONY: help up down start-simple logs ps clean restart
help: ## Afficher l'aide
@echo "Commandes disponibles :"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
up: ## Démarrer tous les services (version complète)
cd infra && docker compose up -d
up-simple: ## Démarrer les services simplifiés (sans IA)
cd infra && docker compose -f docker-compose.simple.yml up -d
down: ## Arrêter tous les services
cd infra && docker compose down
down-simple: ## Arrêter les services simplifiés
cd infra && docker compose -f docker-compose.simple.yml down
start-simple: ## Initialiser l'infrastructure simplifiée
bash ops/start-simple.sh
logs: ## Afficher les logs
cd infra && docker compose logs -f --tail=200
logs-simple: ## Afficher les logs (version simplifiée)
cd infra && docker compose -f docker-compose.simple.yml logs -f --tail=200
ps: ## Afficher le statut des services
cd infra && docker compose ps
ps-simple: ## Afficher le statut des services (version simplifiée)
cd infra && docker compose -f docker-compose.simple.yml ps
clean: ## Nettoyer les volumes et images
cd infra && docker compose down -v
docker system prune -f
restart: ## Redémarrer tous les services
cd infra && docker compose restart
build: ## Reconstruire les images
cd infra && docker compose build --no-cache
build-simple: ## Reconstruire les images (version simplifiée)
cd infra && docker compose -f docker-compose.simple.yml build --no-cache
test-api: ## Tester l'API
curl -F "file=@tests/data/sample.pdf" \
-F "id_dossier=D-2025-001" \
-F "source=upload" \
-F "etude_id=E-001" \
-F "utilisateur_id=U-123" \
http://localhost:8000/api/import
status: ## Vérifier le statut de tous les services
@echo "=== Statut des services ==="
@make ps-simple
@echo ""
@echo "=== Test de connectivité ==="
@curl -s http://localhost:8000/api/health || echo "API non accessible"

View File

@ -1,88 +0,0 @@
version: "3.9"
# Configuration de développement avec volumes montés et debugging
services:
host-api:
build:
context: ./docker/host-api
volumes:
- ./services/host_api:/app
- ./ops/seed:/seed:ro
environment:
- DEBUG=true
- LOG_LEVEL=debug
ports:
- "8000:8000"
command: ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
depends_on:
- postgres
- redis
- minio
worker:
build:
context: ./docker/worker
volumes:
- ./services/worker:/app
- ./ops/seed:/seed:ro
environment:
- DEBUG=true
- LOG_LEVEL=debug
command: ["celery", "-A", "worker", "worker", "--loglevel=debug"]
depends_on:
- host-api
postgres:
image: postgres:16
environment:
POSTGRES_USER: notariat
POSTGRES_PASSWORD: notariat_pwd
POSTGRES_DB: notariat
volumes:
- pgdata_dev:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7
command: ["redis-server", "--appendonly", "yes"]
volumes:
- redis_dev:/data
ports:
- "6379:6379"
minio:
image: minio/minio:RELEASE.2025-01-13T00-00-00Z
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: minio_pwd
volumes:
- minio_dev:/data
ports:
- "9000:9000"
- "9001:9001"
ollama:
image: ollama/ollama:latest
volumes:
- ollama_dev:/root/.ollama
ports:
- "11434:11434"
environment:
- OLLAMA_HOST=0.0.0.0
anythingsqlite:
image: kevincharm/anythingllm:latest
environment:
- DISABLE_AUTH=true
ports:
- "3001:3001"
depends_on:
- ollama
volumes:
pgdata_dev:
redis_dev:
minio_dev:
ollama_dev:

View File

@ -1,58 +0,0 @@
version: "3.9"
# Configuration pour les tests d'intégration
services:
postgres-test:
image: postgres:16
environment:
POSTGRES_USER: test_notariat
POSTGRES_PASSWORD: test_pwd
POSTGRES_DB: test_notariat
volumes:
- pgdata_test:/var/lib/postgresql/data
ports:
- "5433:5432"
redis-test:
image: redis:7
command: ["redis-server", "--appendonly", "yes"]
volumes:
- redis_test:/data
ports:
- "6380:6379"
minio-test:
image: minio/minio:RELEASE.2025-01-13T00-00-00Z
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: test_minio
MINIO_ROOT_PASSWORD: test_minio_pwd
volumes:
- minio_test:/data
ports:
- "9002:9000"
- "9003:9001"
ollama-test:
image: ollama/ollama:latest
volumes:
- ollama_test:/root/.ollama
ports:
- "11435:11434"
environment:
- OLLAMA_HOST=0.0.0.0
anythingsqlite-test:
image: kevincharm/anythingllm:latest
environment:
- DISABLE_AUTH=true
ports:
- "3002:3001"
depends_on:
- ollama-test
volumes:
pgdata_test:
redis_test:
minio_test:
ollama_test:

452
docker-compose.yml Normal file
View File

@ -0,0 +1,452 @@
x-env: &default-env
TZ: ${TZ}
PUID: "1000"
PGID: "1000"
# Configuration DNS et réseau pour 4NK_IA_back
x-4nk-ia-extra-hosts: &x-4nk-ia-extra-hosts
extra_hosts:
- "4nk-ia-api.local:172.23.0.10"
- "4nk-ia-worker.local:172.23.0.11"
- "4nk-ia-postgres.local:172.23.0.12"
- "4nk-ia-redis.local:172.23.0.13"
- "4nk-ia-minio.local:172.23.0.14"
- "4nk-ia-ollama.local:172.23.0.15"
- "4nk-ia-anythingllm.local:172.23.0.16"
- "4nk-ia-neo4j.local:172.23.0.17"
- "4nk-ia-opensearch.local:172.23.0.18"
services:
# ==================== SERVICES DE BASE ====================
postgres:
<<: *x-4nk-ia-extra-hosts
image: postgres:16
container_name: 4nk-ia-postgres.local
hostname: 4nk-ia-postgres.local
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
profiles: ["production", "development"]
dns:
- 172.23.0.1 # Gateway Docker pour accéder à dnsmasq
networks:
4nk_ia_network:
ipv4_address: 172.23.0.12
postgres-test:
image: postgres:16
container_name: 4nk-ia-postgres-test.local
hostname: 4nk-ia-postgres-test.local
environment:
POSTGRES_USER: test_notariat
POSTGRES_PASSWORD: test_pwd
POSTGRES_DB: test_notariat
volumes:
- pgdata_test:/var/lib/postgresql/data
ports:
- "5433:5432"
restart: unless-stopped
profiles: ["test"]
dns:
- 172.23.0.1
networks:
4nk_ia_network:
ipv4_address: 172.23.0.22
redis:
<<: *x-4nk-ia-extra-hosts
image: redis:7
container_name: 4nk-ia-redis.local
hostname: 4nk-ia-redis.local
command: ["redis-server", "--appendonly", "yes"]
volumes:
- redis:/data
ports:
- "6379:6379"
restart: unless-stopped
profiles: ["production", "development"]
dns:
- 172.23.0.1
networks:
4nk_ia_network:
ipv4_address: 172.23.0.13
redis-test:
image: redis:7
container_name: 4nk-ia-redis-test.local
hostname: 4nk-ia-redis-test.local
command: ["redis-server", "--appendonly", "yes"]
volumes:
- redis_test:/data
ports:
- "6380:6379"
restart: unless-stopped
profiles: ["test"]
dns:
- 172.23.0.1
networks:
4nk_ia_network:
ipv4_address: 172.23.0.23
minio:
<<: *x-4nk-ia-extra-hosts
image: minio/minio:latest
container_name: 4nk-ia-minio.local
hostname: 4nk-ia-minio.local
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
volumes:
- minio:/data
ports:
- "9000:9000"
- "9001:9001"
restart: unless-stopped
profiles: ["production", "development"]
dns:
- 172.23.0.1
networks:
4nk_ia_network:
ipv4_address: 172.23.0.14
minio-test:
image: minio/minio:latest
container_name: 4nk-ia-minio-test.local
hostname: 4nk-ia-minio-test.local
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: test_minio
MINIO_ROOT_PASSWORD: test_minio_pwd
volumes:
- minio_test:/data
ports:
- "9002:9000"
- "9003:9001"
restart: unless-stopped
profiles: ["test"]
dns:
- 172.23.0.1
networks:
4nk_ia_network:
ipv4_address: 172.23.0.24
# ==================== SERVICES IA ET RAG ====================
ollama:
<<: *x-4nk-ia-extra-hosts
image: ollama/ollama:latest
container_name: 4nk-ia-ollama.local
hostname: 4nk-ia-ollama.local
volumes:
- ollama:/root/.ollama
ports:
- "11435:11434" # Port modifié pour éviter les conflits
environment:
- OLLAMA_HOST=0.0.0.0
restart: unless-stopped
profiles: ["production", "development"]
dns:
- 172.23.0.1
networks:
4nk_ia_network:
ipv4_address: 172.23.0.15
ollama-test:
image: ollama/ollama:latest
container_name: 4nk-ia-ollama-test.local
hostname: 4nk-ia-ollama-test.local
volumes:
- ollama_test:/root/.ollama
ports:
- "11436:11434"
environment:
- OLLAMA_HOST=0.0.0.0
restart: unless-stopped
profiles: ["test"]
dns:
- 172.23.0.1
networks:
4nk_ia_network:
ipv4_address: 172.23.0.25
anythingsqlite:
<<: *x-4nk-ia-extra-hosts
image: mintplexlabs/anythingllm:latest
container_name: 4nk-ia-anythingllm.local
hostname: 4nk-ia-anythingllm.local
environment:
- DISABLE_AUTH=true
- STORAGE_DIR=/app/server/storage
depends_on:
- ollama
ports:
- "3001:3001"
volumes:
- anythingllm:/app/server/storage
restart: unless-stopped
profiles: ["production", "development"]
dns:
- 172.23.0.1
networks:
4nk_ia_network:
ipv4_address: 172.23.0.16
anythingsqlite-test:
image: mintplexlabs/anythingllm:latest
container_name: 4nk-ia-anythingllm-test.local
hostname: 4nk-ia-anythingllm-test.local
environment:
- DISABLE_AUTH=true
- STORAGE_DIR=/app/server/storage
depends_on:
- ollama-test
ports:
- "3002:3001"
volumes:
- anythingllm_test:/app/server/storage
restart: unless-stopped
profiles: ["test"]
dns:
- 172.23.0.1
networks:
4nk_ia_network:
ipv4_address: 172.23.0.26
# ==================== SERVICES DE DONNÉES ====================
neo4j:
<<: *x-4nk-ia-extra-hosts
image: neo4j:5
container_name: 4nk-ia-neo4j.local
hostname: 4nk-ia-neo4j.local
environment:
- NEO4J_AUTH=${NEO4J_AUTH}
volumes:
- neo4j:/data
ports:
- "7474:7474"
- "7687:7687"
restart: unless-stopped
profiles: ["production", "development"]
dns:
- 172.23.0.1
networks:
4nk_ia_network:
ipv4_address: 172.23.0.17
opensearch:
<<: *x-4nk-ia-extra-hosts
image: opensearchproject/opensearch:2.14.0
container_name: 4nk-ia-opensearch.local
hostname: 4nk-ia-opensearch.local
environment:
- discovery.type=single-node
- OPENSEARCH_INITIAL_ADMIN_PASSWORD=OpenSearch2025!
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- opensearch:/usr/share/opensearch/data
ports:
- "9200:9200"
restart: unless-stopped
profiles: ["production", "development"]
dns:
- 172.23.0.1
networks:
4nk_ia_network:
ipv4_address: 172.23.0.18
# ==================== SERVICES APPLICATIFS ====================
host-api:
<<: *x-4nk-ia-extra-hosts
build:
context: ./docker/host-api
dockerfile: Dockerfile
container_name: 4nk-ia-api.local
hostname: 4nk-ia-api.local
labels:
- logging=promtail
- project=4nk_ia_back
env_file: ./.env
environment:
<<: *default-env
DATABASE_URL: postgresql+psycopg://$POSTGRES_USER:$POSTGRES_PASSWORD@4nk-ia-postgres.local:5432/$POSTGRES_DB
REDIS_URL: redis://4nk-ia-redis.local:6379/0
MINIO_ENDPOINT: 4nk-ia-minio.local:9000
MINIO_BUCKET: ${MINIO_BUCKET}
ANYLLM_BASE_URL: http://4nk-ia-anythingllm.local:3001
ANYLLM_API_KEY: ${ANYLLM_API_KEY}
OLLAMA_BASE_URL: http://4nk-ia-ollama.local:11434
OPENSEARCH_URL: http://4nk-ia-opensearch.local:9200
NEO4J_URL: bolt://4nk-ia-neo4j.local:7687
NEO4J_AUTH: ${NEO4J_AUTH}
# Configuration de l'API
API_HOST: 0.0.0.0
API_PORT: 8000
API_WORKERS: 4
LOG_LEVEL: ${LOG_LEVEL:-INFO}
LOG_FORMAT: ${LOG_FORMAT:-json}
# Sécurité
SECRET_KEY: ${SECRET_KEY:-your_secret_key_here}
ACCESS_TOKEN_EXPIRE_MINUTES: ${ACCESS_TOKEN_EXPIRE_MINUTES:-30}
volumes:
- ./services/host_api:/app
- ./ops/seed:/seed:ro
- ./ops/seed/schemas:/schemas:ro
- api_logs:/app/logs
ports:
- "8001:8000" # Port externe 8001 pour éviter les conflits
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
minio:
condition: service_started
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
restart: unless-stopped
profiles: ["production"]
dns:
- 172.23.0.1
networks:
4nk_ia_network:
ipv4_address: 172.23.0.10
host-api-dev:
<<: *x-4nk-ia-extra-hosts
build:
context: ./docker/host-api
dockerfile: Dockerfile
container_name: 4nk-ia-api-dev.local
hostname: 4nk-ia-api-dev.local
volumes:
- ./services/host_api:/app
- ./ops/seed:/seed:ro
environment:
- DEBUG=true
- LOG_LEVEL=debug
- DATABASE_URL=postgresql+psycopg://notariat:notariat_pwd@4nk-ia-postgres.local:5432/notariat
- REDIS_URL=redis://4nk-ia-redis.local:6379/0
ports:
- "8000:8000" # Port de développement
command: ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
depends_on:
- postgres
- redis
- minio
restart: unless-stopped
profiles: ["development"]
dns:
- 172.23.0.1
networks:
4nk_ia_network:
ipv4_address: 172.23.0.30
worker:
<<: *x-4nk-ia-extra-hosts
build:
context: ./
dockerfile: docker/worker/Dockerfile
container_name: 4nk-ia-worker.local
hostname: 4nk-ia-worker.local
labels:
- logging=promtail
- project=4nk_ia_back
env_file: ./.env
environment:
<<: *default-env
DATABASE_URL: postgresql+psycopg://$POSTGRES_USER:$POSTGRES_PASSWORD@4nk-ia-postgres.local:5432/$POSTGRES_DB
REDIS_URL: redis://4nk-ia-redis.local:6379/0
MINIO_ENDPOINT: 4nk-ia-minio.local:9000
MINIO_BUCKET: ${MINIO_BUCKET}
ANYLLM_BASE_URL: http://4nk-ia-anythingllm.local:3001
ANYLLM_API_KEY: ${ANYLLM_API_KEY}
OLLAMA_BASE_URL: http://4nk-ia-ollama.local:11434
OPENSEARCH_URL: http://4nk-ia-opensearch.local:9200
NEO4J_URL: bolt://4nk-ia-neo4j.local:7687
NEO4J_AUTH: ${NEO4J_AUTH}
volumes:
- ./services/worker:/app
- ./ops/seed:/seed:ro
depends_on:
- host-api
restart: unless-stopped
profiles: ["production"]
dns:
- 172.23.0.1
networks:
4nk_ia_network:
ipv4_address: 172.23.0.11
worker-dev:
<<: *x-4nk-ia-extra-hosts
build:
context: ./docker/worker
container_name: 4nk-ia-worker-dev.local
hostname: 4nk-ia-worker-dev.local
volumes:
- ./services/worker:/app
- ./ops/seed:/seed:ro
environment:
- DEBUG=true
- LOG_LEVEL=debug
- DATABASE_URL=postgresql+psycopg://notariat:notariat_pwd@4nk-ia-postgres.local:5432/notariat
- REDIS_URL=redis://4nk-ia-redis.local:6379/0
command: ["celery", "-A", "worker", "worker", "--loglevel=debug"]
depends_on:
- host-api-dev
restart: unless-stopped
profiles: ["development"]
dns:
- 172.23.0.1
networks:
4nk_ia_network:
ipv4_address: 172.23.0.31
volumes:
# Volumes de production/développement
pgdata:
redis:
minio:
ollama:
neo4j:
opensearch:
anythingllm:
api_logs:
# Volumes de test
pgdata_test:
redis_test:
minio_test:
ollama_test:
anythingllm_test:
networks:
4nk_ia_network:
driver: bridge
ipam:
config:
- subnet: 172.23.0.0/16
gateway: 172.23.0.1

View File

@ -11,4 +11,4 @@ RUN pip install --no-cache-dir -r requirements.txt
COPY services/worker /app
CMD ["python", "worker.py"]
CMD ["celery", "-A", "worker", "worker", "--loglevel=info"]

View File

@ -209,9 +209,7 @@ curl "http://localhost:8000/api/notary/document/{document_id}/analysis"
cd services/host_api
uvicorn app:app --host 0.0.0.0 --port 8000
# Démarrer l'interface web (dans un autre terminal)
cd services/web_interface
python start_web.py
# L'interface web est gérée par le projet 4NK_IA_front séparé
```
#### Accès

View File

@ -440,9 +440,7 @@ cd services/worker
source ../../venv/bin/activate
celery -A worker worker --loglevel=info &
# Démarrage de l'interface web
cd services/web_interface
python3 start_web.py 8081 &
# L'interface web est gérée par le projet 4NK_IA_front séparé
```
### **3. Vérification du Démarrage**

9
hosts.4nk-ia Normal file
View File

@ -0,0 +1,9 @@
172.23.0.10 4nk-ia-api.local
172.23.0.11 4nk-ia-worker.local
172.23.0.12 4nk-ia-postgres.local
172.23.0.13 4nk-ia-redis.local
172.23.0.14 4nk-ia-minio.local
172.23.0.15 4nk-ia-ollama.local
172.23.0.16 4nk-ia-anythingllm.local
172.23.0.17 4nk-ia-neo4j.local
172.23.0.18 4nk-ia-opensearch.local

24
hosts.merge Normal file
View File

@ -0,0 +1,24 @@
172.23.0.10 4nk-ia-api.local
172.23.0.11 4nk-ia-worker.local
172.23.0.12 4nk-ia-postgres.local
172.23.0.13 4nk-ia-redis.local
172.23.0.14 4nk-ia-minio.local
172.23.0.15 4nk-ia-ollama.local
172.23.0.16 4nk-ia-anythingllm.local
172.23.0.17 4nk-ia-neo4j.local
172.23.0.18 4nk-ia-opensearch.local
172.20.0.10 tor.local
172.20.0.11 bitcoin.local
172.20.0.12 blindbit-oracle.local
172.20.0.13 sdk-storage.local
172.20.0.14 sdk-relay1.local
172.20.0.15 sdk-relay2.local
172.20.0.16 sdk-relay3.local
172.20.0.17 sdk-signer.local
172.20.0.18 ihm-client.local
172.20.0.32 coffre-front.local
172.20.0.33 coffre-back-mini.local
172.20.0.50 grafana-central.local
172.20.0.51 loki.local
172.20.0.52 prometheus.local
172.20.0.53 promtail.local

15
hosts.sdk Normal file
View File

@ -0,0 +1,15 @@
172.20.0.10 tor.local
172.20.0.11 bitcoin.local
172.20.0.12 blindbit-oracle.local
172.20.0.13 sdk-storage.local
172.20.0.14 sdk-relay1.local
172.20.0.15 sdk-relay2.local
172.20.0.16 sdk-relay3.local
172.20.0.17 sdk-signer.local
172.20.0.18 ihm-client.local
172.20.0.32 coffre-front.local
172.20.0.33 coffre-back-mini.local
172.20.0.50 grafana-central.local
172.20.0.51 loki.local
172.20.0.52 prometheus.local
172.20.0.53 promtail.local

View File

@ -1,71 +0,0 @@
# Configuration du projet
PROJECT_NAME=notariat
DOMAIN=localhost
TZ=Europe/Paris
# Base de données PostgreSQL
POSTGRES_USER=notariat
POSTGRES_PASSWORD=notariat_pwd
POSTGRES_DB=notariat
# Redis
REDIS_PASSWORD=
# MinIO (Stockage S3-compatible)
MINIO_ROOT_USER=minio
MINIO_ROOT_PASSWORD=minio_pwd
MINIO_BUCKET=ingest
# AnythingLLM
ANYLLM_API_KEY=change_me
ANYLLM_BASE_URL=http://anythingllm:3001
ANYLLM_WORKSPACE_NORMES=workspace_normes
ANYLLM_WORKSPACE_TRAMES=workspace_trames
ANYLLM_WORKSPACE_ACTES=workspace_actes
# Ollama (LLM local)
OLLAMA_BASE_URL=http://ollama:11434
OLLAMA_MODELS=llama3:8b,mistral:7b
# Neo4j (Graphe de connaissances)
NEO4J_AUTH=neo4j/neo4j_pwd
# OpenSearch (Recherche plein-texte)
OPENSEARCH_PASSWORD=opensearch_pwd
# Traefik (Passerelle HTTP)
TRAEFIK_ACME_EMAIL=ops@example.org
# Configuration de l'API
API_HOST=0.0.0.0
API_PORT=8000
API_WORKERS=1
# Configuration des workers
WORKER_CONCURRENCY=2
WORKER_LOGLEVEL=info
# Seuils de qualité
OCR_CONFIDENCE_THRESHOLD=0.75
CLASSIFICATION_CONFIDENCE_THRESHOLD=0.75
MANUAL_REVIEW_CER_THRESHOLD=0.08
# URLs des APIs externes
CADASTRE_API_URL=https://apicarto.ign.fr/api/cadastre
GEORISQUES_API_URL=https://www.georisques.gouv.fr/api
BODACC_API_URL=https://bodacc-datadila.opendatasoft.com/api
INFOGREFFE_API_URL=https://entreprise.api.gouv.fr/v2/infogreffe
RBE_API_URL=https://www.data.gouv.fr/api/1/datasets/registre-des-beneficiaires-effectifs
# Configuration de sécurité
JWT_SECRET_KEY=your-secret-key-change-in-production
ENCRYPTION_KEY=your-encryption-key-change-in-production
# Configuration de monitoring
PROMETHEUS_ENABLED=true
GRAFANA_ENABLED=true
SENTRY_DSN=
# Configuration de logs
LOG_LEVEL=INFO
LOG_FORMAT=json

View File

@ -1,21 +0,0 @@
# Configuration simplifiée sans IA
PROJECT_NAME=notariat
DOMAIN=localhost
TZ=Europe/Paris
# Base de données PostgreSQL
POSTGRES_USER=notariat
POSTGRES_PASSWORD=notariat_pwd
POSTGRES_DB=notariat
# Redis
REDIS_PASSWORD=
# MinIO (stockage objet)
MINIO_ROOT_USER=minio
MINIO_ROOT_PASSWORD=minio_pwd
MINIO_BUCKET=ingest
# Configuration de développement
DEBUG=true
LOG_LEVEL=debug

17
infra/dnsmasq-4nk-ia.conf Normal file
View File

@ -0,0 +1,17 @@
# dnsmasq config for 4NK_IA_back - listens on all interfaces:53
no-dhcp-interface=
port=53
interface=*
bind-interfaces
log-queries
# 4NK_IA_back Docker hosts
address=/4nk-ia-api.local/172.23.0.10
address=/4nk-ia-worker.local/172.23.0.11
address=/4nk-ia-postgres.local/172.23.0.12
address=/4nk-ia-redis.local/172.23.0.13
address=/4nk-ia-minio.local/172.23.0.14
address=/4nk-ia-ollama.local/172.23.0.15
address=/4nk-ia-anythingllm.local/172.23.0.16
address=/4nk-ia-neo4j.local/172.23.0.17
address=/4nk-ia-opensearch.local/172.23.0.18

View File

@ -1,182 +0,0 @@
x-env: &default-env
TZ: ${TZ}
PUID: "1000"
PGID: "1000"
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
redis:
image: redis:7
command: ["redis-server", "--appendonly", "yes"]
volumes:
- redis:/data
ports:
- "6379:6379"
restart: unless-stopped
minio:
image: minio/minio:latest
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
volumes:
- minio:/data
ports:
- "9000:9000"
- "9001:9001"
restart: unless-stopped
anythingsqlite:
image: mintplexlabs/anythingllm:latest
environment:
- DISABLE_AUTH=true
depends_on:
- ollama
ports:
- "3001:3001"
volumes:
- anythingllm:/app/server/storage
container_name: anythingllm
restart: unless-stopped
ollama:
image: ollama/ollama:latest
volumes:
- ollama:/root/.ollama
ports:
- "11434:11434"
restart: unless-stopped
neo4j:
image: neo4j:5
environment:
- NEO4J_AUTH=${NEO4J_AUTH}
volumes:
- neo4j:/data
ports:
- "7474:7474"
- "7687:7687"
restart: unless-stopped
opensearch:
image: opensearchproject/opensearch:2.14.0
environment:
- discovery.type=single-node
- OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_PASSWORD}
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- opensearch:/usr/share/opensearch/data
ports:
- "9200:9200"
restart: unless-stopped
host-api:
build:
context: ../docker/host-api
dockerfile: Dockerfile
labels:
- logging=promtail
- project=4nk_ia_back
env_file: ./.env
environment:
<<: *default-env
DATABASE_URL: postgresql+psycopg://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres:5432/$POSTGRES_DB
REDIS_URL: redis://redis:6379/0
MINIO_ENDPOINT: minio:9000
MINIO_BUCKET: ${MINIO_BUCKET}
ANYLLM_BASE_URL: ${ANYLLM_BASE_URL}
ANYLLM_API_KEY: ${ANYLLM_API_KEY}
OLLAMA_BASE_URL: ${OLLAMA_BASE_URL}
OPENSEARCH_URL: http://opensearch:9200
NEO4J_URL: bolt://neo4j:7687
NEO4J_AUTH: ${NEO4J_AUTH}
# Configuration de l'API
API_HOST: 0.0.0.0
API_PORT: 8000
API_WORKERS: 4
LOG_LEVEL: ${LOG_LEVEL:-INFO}
LOG_FORMAT: ${LOG_FORMAT:-json}
# Sécurité
SECRET_KEY: ${SECRET_KEY:-your_secret_key_here}
ACCESS_TOKEN_EXPIRE_MINUTES: ${ACCESS_TOKEN_EXPIRE_MINUTES:-30}
volumes:
- ../services/host_api:/app
- ../ops/seed:/seed:ro
- ../ops/seed/schemas:/schemas:ro
- api_logs:/app/logs
ports:
- "8000:8000"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
minio:
condition: service_started
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
restart: unless-stopped
worker:
build:
context: ../
dockerfile: docker/worker/Dockerfile
labels:
- logging=promtail
- project=4nk_ia_back
env_file: ./.env
environment:
<<: *default-env
DATABASE_URL: postgresql+psycopg://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres:5432/$POSTGRES_DB
REDIS_URL: redis://redis:6379/0
MINIO_ENDPOINT: minio:9000
MINIO_BUCKET: ${MINIO_BUCKET}
ANYLLM_BASE_URL: ${ANYLLM_BASE_URL}
ANYLLM_API_KEY: ${ANYLLM_API_KEY}
OLLAMA_BASE_URL: ${OLLAMA_BASE_URL}
OPENSEARCH_URL: http://opensearch:9200
NEO4J_URL: bolt://neo4j:7687
NEO4J_AUTH: ${NEO4J_AUTH}
volumes:
- ../services/worker:/app
- ../ops/seed:/seed:ro
depends_on:
- host-api
restart: unless-stopped
volumes:
pgdata:
redis:
minio:
ollama:
neo4j:
opensearch:
anythingllm:
api_logs:

43
infra/setup-dnsmasq.sh Executable file
View File

@ -0,0 +1,43 @@
#!/bin/bash
# Script de configuration dnsmasq pour 4NK_IA_back
set -euo pipefail
echo "🔧 Configuration de dnsmasq pour 4NK_IA_back"
# Vérifier si dnsmasq est installé
if ! command -v dnsmasq &> /dev/null; then
echo "📦 Installation de dnsmasq..."
sudo apt-get update
sudo apt-get install -y dnsmasq
fi
# Créer le répertoire de configuration
sudo mkdir -p /etc/dnsmasq.d
# Copier la configuration 4NK_IA_back
echo "📝 Copie de la configuration dnsmasq..."
sudo cp dnsmasq-4nk-ia.conf /etc/dnsmasq.d/4nk-ia.conf
# Redémarrer dnsmasq
echo "🔄 Redémarrage de dnsmasq..."
sudo systemctl restart dnsmasq
sudo systemctl enable dnsmasq
# Vérifier le statut
echo "✅ Vérification du statut de dnsmasq..."
sudo systemctl status dnsmasq --no-pager -l
echo "🎉 Configuration dnsmasq terminée !"
echo ""
echo "📋 Services disponibles :"
echo " - 4nk-ia-api.local:172.21.0.10"
echo " - 4nk-ia-worker.local:172.21.0.11"
echo " - 4nk-ia-postgres.local:172.21.0.12"
echo " - 4nk-ia-redis.local:172.21.0.13"
echo " - 4nk-ia-minio.local:172.21.0.14"
echo " - 4nk-ia-ollama.local:172.21.0.15"
echo " - 4nk-ia-anythingllm.local:172.21.0.16"
echo " - 4nk-ia-neo4j.local:172.21.0.17"
echo " - 4nk-ia-opensearch.local:172.21.0.18"

218
manage.sh Executable file
View File

@ -0,0 +1,218 @@
#!/bin/bash
# Script de gestion principal pour 4NK_IA_back
set -euo pipefail
# Couleurs pour les messages
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Fonction d'affichage des messages
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Fonction d'aide
show_help() {
cat << EOF
🔧 Gestionnaire 4NK_IA_back
Usage: $0 <command> [options]
COMMANDES PRINCIPALES:
up [profile] Démarrer les services (production|development|test)
down Arrêter tous les services
restart [profile] Redémarrer les services
status Afficher le statut des services
logs [service] Afficher les logs des services
shell [service] Ouvrir un shell dans un conteneur
clean Nettoyer les conteneurs et volumes
setup-dns Configurer dnsmasq pour les noms .local
PROFILS DISPONIBLES:
production Services de production (défaut)
development Services de développement avec volumes montés
test Services de test isolés
EXEMPLES:
$0 up # Démarrer en production
$0 up development # Démarrer en développement
$0 logs api # Voir les logs de l'API
$0 shell postgres # Ouvrir un shell PostgreSQL
$0 clean # Nettoyer tout
EOF
}
# Fonction pour démarrer les services
start_services() {
local profile=${1:-production}
log_info "Démarrage des services avec le profil: $profile"
case $profile in
production|development|test)
docker-compose --profile "$profile" up -d
;;
*)
log_error "Profil invalide: $profile. Utilisez: production, development, ou test"
exit 1
;;
esac
log_success "Services démarrés avec le profil: $profile"
log_info "Vérification du statut..."
docker-compose ps
}
# Fonction pour arrêter les services
stop_services() {
log_info "Arrêt des services..."
docker-compose down
log_success "Services arrêtés"
}
# Fonction pour redémarrer les services
restart_services() {
local profile=${1:-production}
log_info "Redémarrage des services avec le profil: $profile"
stop_services
start_services "$profile"
}
# Fonction pour afficher le statut
show_status() {
log_info "Statut des services:"
docker-compose ps
echo ""
log_info "Réseaux Docker:"
docker network ls | grep 4nk_ia
echo ""
log_info "Volumes Docker:"
docker volume ls | grep 4nk_ia
}
# Fonction pour afficher les logs
show_logs() {
local service=${1:-}
if [[ -n "$service" ]]; then
log_info "Logs du service: $service"
docker-compose logs -f "$service"
else
log_info "Logs de tous les services:"
docker-compose logs -f
fi
}
# Fonction pour ouvrir un shell
open_shell() {
local service=${1:-host-api}
log_info "Ouverture d'un shell dans le service: $service"
docker-compose exec "$service" /bin/bash
}
# Fonction de nettoyage
clean_all() {
log_warning "Cette action va supprimer tous les conteneurs, réseaux et volumes 4NK_IA_back"
read -p "Êtes-vous sûr ? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
log_info "Nettoyage en cours..."
docker-compose down -v --remove-orphans
docker system prune -f
log_success "Nettoyage terminé"
else
log_info "Nettoyage annulé"
fi
}
# Fonction pour configurer dnsmasq
setup_dns() {
log_info "Configuration de dnsmasq pour les noms .local"
if [[ -f "./infra/setup-dnsmasq.sh" ]]; then
sudo ./infra/setup-dnsmasq.sh
else
log_error "Script setup-dnsmasq.sh non trouvé"
exit 1
fi
}
# Fonction pour tester l'API
test_api() {
log_info "Test de l'API..."
# Attendre que l'API soit prête
log_info "Attente du démarrage de l'API..."
sleep 10
# Test de santé
if curl -s http://localhost:8001/api/health > /dev/null; then
log_success "API accessible sur http://localhost:8001"
curl -s http://localhost:8001/api/health | jq .
else
log_error "API non accessible"
return 1
fi
}
# Gestion des arguments
case "${1:-help}" in
up)
start_services "${2:-production}"
;;
down)
stop_services
;;
restart)
restart_services "${2:-production}"
;;
status)
show_status
;;
logs)
show_logs "${2:-}"
;;
shell)
open_shell "${2:-host-api}"
;;
clean)
clean_all
;;
setup-dns)
setup_dns
;;
test)
test_api
;;
help|--help|-h)
show_help
;;
*)
log_error "Commande inconnue: $1"
echo ""
show_help
exit 1
;;
esac

File diff suppressed because it is too large Load Diff

View File

@ -1,370 +0,0 @@
/* Bootstrap CSS minimal pour 4NK Notariat */
:root {
--bs-blue: #0d6efd;
--bs-indigo: #6610f2;
--bs-purple: #6f42c1;
--bs-pink: #d63384;
--bs-red: #dc3545;
--bs-orange: #fd7e14;
--bs-yellow: #ffc107;
--bs-green: #198754;
--bs-teal: #20c997;
--bs-cyan: #0dcaf0;
--bs-white: #fff;
--bs-gray: #6c757d;
--bs-gray-dark: #343a40;
--bs-primary: #0d6efd;
--bs-secondary: #6c757d;
--bs-success: #198754;
--bs-info: #0dcaf0;
--bs-warning: #ffc107;
--bs-danger: #dc3545;
--bs-light: #f8f9fa;
--bs-dark: #212529;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
background-color: #fff;
}
.container {
width: 100%;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
.row {
display: flex;
flex-wrap: wrap;
margin-right: -15px;
margin-left: -15px;
}
.col-md-2, .col-md-4, .col-md-6, .col-md-8, .col-md-9, .col-md-10 {
position: relative;
width: 100%;
padding-right: 15px;
padding-left: 15px;
}
.col-md-2 { flex: 0 0 16.666667%; max-width: 16.666667%; }
.col-md-4 { flex: 0 0 33.333333%; max-width: 33.333333%; }
.col-md-6 { flex: 0 0 50%; max-width: 50%; }
.col-md-8 { flex: 0 0 66.666667%; max-width: 66.666667%; }
.col-md-9 { flex: 0 0 75%; max-width: 75%; }
.col-md-10 { flex: 0 0 83.333333%; max-width: 83.333333%; }
.card {
position: relative;
display: flex;
flex-direction: column;
min-width: 0;
word-wrap: break-word;
background-color: #fff;
background-clip: border-box;
border: 1px solid rgba(0,0,0,.125);
border-radius: 0.375rem;
margin-bottom: 1rem;
}
.card-header {
padding: 0.75rem 1.25rem;
margin-bottom: 0;
background-color: rgba(0,0,0,.03);
border-bottom: 1px solid rgba(0,0,0,.125);
}
.card-body {
flex: 1 1 auto;
padding: 1.25rem;
}
.btn {
display: inline-block;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: center;
text-decoration: none;
vertical-align: middle;
cursor: pointer;
user-select: none;
background-color: transparent;
border: 1px solid transparent;
padding: 0.375rem 0.75rem;
font-size: 1rem;
border-radius: 0.375rem;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}
.btn-primary {
color: #fff;
background-color: #0d6efd;
border-color: #0d6efd;
}
.btn-primary:hover {
color: #fff;
background-color: #0b5ed7;
border-color: #0a58ca;
}
.btn-outline-primary {
color: #0d6efd;
border-color: #0d6efd;
}
.btn-outline-primary:hover {
color: #fff;
background-color: #0d6efd;
border-color: #0d6efd;
}
.btn-outline-danger {
color: #dc3545;
border-color: #dc3545;
}
.btn-outline-danger:hover {
color: #fff;
background-color: #dc3545;
border-color: #dc3545;
}
.btn-outline-secondary {
color: #6c757d;
border-color: #6c757d;
}
.btn-outline-secondary:hover {
color: #fff;
background-color: #6c757d;
border-color: #6c757d;
}
.btn-sm {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
border-radius: 0.25rem;
}
.btn-group {
position: relative;
display: inline-flex;
vertical-align: middle;
}
.alert {
position: relative;
padding: 0.75rem 1.25rem;
margin-bottom: 1rem;
border: 1px solid transparent;
border-radius: 0.375rem;
}
.alert-success {
color: #0f5132;
background-color: #d1e7dd;
border-color: #badbcc;
}
.alert-danger {
color: #842029;
background-color: #f8d7da;
border-color: #f5c2c7;
}
.alert-warning {
color: #664d03;
background-color: #fff3cd;
border-color: #ffecb5;
}
.alert-info {
color: #055160;
background-color: #cff4fc;
border-color: #b6effb;
}
.badge {
display: inline-block;
padding: 0.35em 0.65em;
font-size: 0.75em;
font-weight: 700;
line-height: 1;
color: #fff;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.375rem;
}
.bg-success { background-color: #198754 !important; }
.bg-danger { background-color: #dc3545 !important; }
.bg-warning { background-color: #ffc107 !important; }
.bg-info { background-color: #0dcaf0 !important; }
.bg-primary { background-color: #0d6efd !important; }
.bg-secondary { background-color: #6c757d !important; }
.navbar {
position: relative;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
.navbar-brand {
padding-top: 0.3125rem;
padding-bottom: 0.3125rem;
margin-right: 1rem;
font-size: 1.25rem;
color: rgba(0,0,0,.9);
text-decoration: none;
}
.nav {
display: flex;
flex-wrap: wrap;
padding-left: 0;
margin-bottom: 0;
list-style: none;
}
.nav-link {
display: block;
padding: 0.5rem 1rem;
color: #0d6efd;
text-decoration: none;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out;
}
.nav-link:hover {
color: #0a58ca;
}
.nav-link.active {
color: #fff;
background-color: #0d6efd;
}
.progress {
display: flex;
height: 1rem;
overflow: hidden;
font-size: 0.75rem;
background-color: #e9ecef;
border-radius: 0.375rem;
}
.progress-bar {
display: flex;
flex-direction: column;
justify-content: center;
overflow: hidden;
color: #fff;
text-align: center;
white-space: nowrap;
background-color: #0d6efd;
transition: width .6s ease;
}
.text-center { text-align: center !important; }
.text-muted { color: #6c757d !important; }
.text-primary { color: #0d6efd !important; }
.text-success { color: #198754 !important; }
.text-danger { color: #dc3545 !important; }
.mb-0 { margin-bottom: 0 !important; }
.mb-1 { margin-bottom: 0.25rem !important; }
.mb-2 { margin-bottom: 0.5rem !important; }
.mb-3 { margin-bottom: 1rem !important; }
.mb-4 { margin-bottom: 1.5rem !important; }
.mb-5 { margin-bottom: 3rem !important; }
.mt-2 { margin-top: 0.5rem !important; }
.mt-3 { margin-top: 1rem !important; }
.mt-4 { margin-top: 1.5rem !important; }
.me-2 { margin-right: 0.5rem !important; }
.ms-2 { margin-left: 0.5rem !important; }
.py-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; }
.img-thumbnail {
padding: 0.25rem;
background-color: #fff;
border: 1px solid #dee2e6;
border-radius: 0.375rem;
max-width: 100%;
height: auto;
}
.img-fluid {
max-width: 100%;
height: auto;
}
.rounded {
border-radius: 0.375rem !important;
}
.border {
border: 1px solid #dee2e6 !important;
}
.d-none { display: none !important; }
.list-unstyled {
padding-left: 0;
list-style: none;
}
.list-group {
display: flex;
flex-direction: column;
padding-left: 0;
margin-bottom: 0;
border-radius: 0.375rem;
}
.list-group-item {
position: relative;
display: block;
padding: 0.5rem 1rem;
color: #212529;
text-decoration: none;
background-color: #fff;
border: 1px solid rgba(0,0,0,.125);
}
.list-group-item:first-child {
border-top-left-radius: inherit;
border-top-right-radius: inherit;
}
.list-group-item:last-child {
border-bottom-right-radius: inherit;
border-bottom-left-radius: inherit;
}
/* Responsive */
@media (min-width: 768px) {
.col-md-2, .col-md-4, .col-md-6, .col-md-8, .col-md-9, .col-md-10 {
flex: 0 0 auto;
}
}

View File

@ -1,150 +0,0 @@
// Chart.js minimal pour 4NK Notariat
window.Chart = class Chart {
constructor(ctx, config) {
this.ctx = ctx;
this.config = config;
this.destroyed = false;
// Créer un canvas simple si Chart.js n'est pas disponible
this.createSimpleChart();
}
createSimpleChart() {
if (this.destroyed) return;
const canvas = this.ctx;
const ctx = canvas.getContext('2d');
const config = this.config;
// Effacer le canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (config.type === 'doughnut') {
this.drawDoughnutChart(ctx, config);
} else if (config.type === 'line') {
this.drawLineChart(ctx, config);
}
}
drawDoughnutChart(ctx, config) {
const data = config.data;
const labels = data.labels || [];
const values = data.datasets[0].data || [];
const colors = data.datasets[0].backgroundColor || ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF'];
const canvas = this.ctx;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = Math.min(centerX, centerY) - 20;
const total = values.reduce((sum, val) => sum + val, 0);
let currentAngle = 0;
// Dessiner les segments
values.forEach((value, index) => {
const sliceAngle = (value / total) * 2 * Math.PI;
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.arc(centerX, centerY, radius, currentAngle, currentAngle + sliceAngle);
ctx.closePath();
ctx.fillStyle = colors[index % colors.length];
ctx.fill();
currentAngle += sliceAngle;
});
// Dessiner la légende
let legendY = 20;
labels.forEach((label, index) => {
ctx.fillStyle = colors[index % colors.length];
ctx.fillRect(10, legendY, 15, 15);
ctx.fillStyle = '#333';
ctx.font = '12px Arial';
ctx.fillText(label, 30, legendY + 12);
legendY += 20;
});
}
drawLineChart(ctx, config) {
const data = config.data;
const labels = data.labels || [];
const values = data.datasets[0].data || [];
const color = data.datasets[0].borderColor || '#007bff';
const canvas = this.ctx;
const padding = 40;
const chartWidth = canvas.width - 2 * padding;
const chartHeight = canvas.height - 2 * padding;
const maxValue = Math.max(...values);
const minValue = Math.min(...values);
const valueRange = maxValue - minValue || 1;
// Dessiner les axes
ctx.strokeStyle = '#ddd';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(padding, padding);
ctx.lineTo(padding, canvas.height - padding);
ctx.lineTo(canvas.width - padding, canvas.height - padding);
ctx.stroke();
// Dessiner la ligne
ctx.strokeStyle = color;
ctx.lineWidth = 2;
ctx.beginPath();
values.forEach((value, index) => {
const x = padding + (index / (values.length - 1)) * chartWidth;
const y = canvas.height - padding - ((value - minValue) / valueRange) * chartHeight;
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
// Dessiner les points
ctx.fillStyle = color;
values.forEach((value, index) => {
const x = padding + (index / (values.length - 1)) * chartWidth;
const y = canvas.height - padding - ((value - minValue) / valueRange) * chartHeight;
ctx.beginPath();
ctx.arc(x, y, 4, 0, 2 * Math.PI);
ctx.fill();
});
// Dessiner les labels
ctx.fillStyle = '#333';
ctx.font = '10px Arial';
ctx.textAlign = 'center';
labels.forEach((label, index) => {
const x = padding + (index / (values.length - 1)) * chartWidth;
ctx.fillText(label, x, canvas.height - 10);
});
}
destroy() {
this.destroyed = true;
if (this.ctx && this.ctx.clearRect) {
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
}
}
update() {
if (!this.destroyed) {
this.createSimpleChart();
}
}
};
// Simuler les options globales
window.Chart.defaults = {
responsive: true,
maintainAspectRatio: false
};

View File

@ -1,25 +0,0 @@
/* Font Awesome minimal pour 4NK Notariat */
.fas, .fa {
font-family: "Font Awesome 5 Free";
font-weight: 900;
display: inline-block;
font-style: normal;
font-variant: normal;
text-rendering: auto;
line-height: 1;
}
.fa-cloud-upload-alt:before { content: "☁"; }
.fa-folder-open:before { content: "📁"; }
.fa-file:before { content: "📄"; }
.fa-file-pdf:before { content: "📕"; }
.fa-file-alt:before { content: "📄"; }
.fa-upload:before { content: "⬆"; }
.fa-times:before { content: "✕"; }
.fa-eye:before { content: "👁"; }
.fa-search:before { content: "🔍"; }
.fa-download:before { content: "⬇"; }
.fa-upload:before { content: "⬆"; }
.fa-3x { font-size: 3em; }
.fa-4x { font-size: 4em; }
.fa-2x { font-size: 2em; }

View File

@ -1,441 +0,0 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>4NK Notariat - Traitement de Documents</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📄</text></svg>">
<link href="bootstrap.min.css" rel="stylesheet">
<link href="fontawesome.min.css" rel="stylesheet">
<style>
.upload-area {
border: 2px dashed #007bff;
border-radius: 10px;
padding: 40px;
text-align: center;
background-color: #f8f9fa;
transition: all 0.3s ease;
}
.upload-area:hover {
border-color: #0056b3;
background-color: #e3f2fd;
}
.upload-area.dragover {
border-color: #28a745;
background-color: #d4edda;
}
.document-card {
transition: transform 0.2s ease;
}
.document-card:hover {
transform: translateY(-2px);
}
.status-badge {
font-size: 0.8em;
}
.progress-container {
display: none;
}
.analysis-section {
display: none;
}
.entity-item {
background-color: #f8f9fa;
border-left: 4px solid #007bff;
padding: 10px;
margin: 5px 0;
}
.verification-item {
background-color: #f8f9fa;
border-left: 4px solid #28a745;
padding: 10px;
margin: 5px 0;
}
.verification-item.error {
border-left-color: #dc3545;
}
.verification-item.warning {
border-left-color: #ffc107;
}
.sidebar {
background-color: #f8f9fa;
min-height: 100vh;
}
.main-content {
padding: 20px;
}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<!-- Sidebar -->
<div class="col-md-3 sidebar p-3">
<h4 class="mb-4">
<i class="fas fa-balance-scale text-primary"></i>
4NK Notariat
</h4>
<nav class="nav flex-column">
<a class="nav-link active" href="#upload" data-section="upload">
<i class="fas fa-upload"></i> Upload Document
</a>
<a class="nav-link" href="#documents" data-section="documents">
<i class="fas fa-file-alt"></i> Documents
</a>
<a class="nav-link" href="#stats" data-section="stats">
<i class="fas fa-chart-bar"></i> Statistiques
</a>
<a class="nav-link" href="#settings" data-section="settings">
<i class="fas fa-cog"></i> Paramètres
</a>
</nav>
<hr>
<div class="mt-4">
<h6>Statut du Système</h6>
<div id="system-status">
<div class="d-flex justify-content-between">
<span>API:</span>
<span class="badge bg-success" id="api-status">Connecté</span>
</div>
<div class="d-flex justify-content-between">
<span>LLM:</span>
<span class="badge bg-success" id="llm-status">Disponible</span>
</div>
<div class="d-flex justify-content-between">
<span>APIs Externes:</span>
<span class="badge bg-success" id="external-apis-status">OK</span>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<div class="col-md-9 main-content">
<!-- Upload Section -->
<div id="upload-section" class="content-section">
<h2 class="mb-4">
<i class="fas fa-upload text-primary"></i>
Upload de Document Notarial
</h2>
<div class="row">
<div class="col-md-8">
<div class="card">
<div class="card-body">
<form id="upload-form">
<div class="upload-area" id="upload-area">
<i class="fas fa-cloud-upload-alt fa-3x text-primary mb-3"></i>
<h5>Glissez-déposez votre document ici</h5>
<p class="text-muted">ou cliquez pour sélectionner un fichier</p>
<input type="file" id="file-input" class="d-none" accept=".pdf,.jpg,.jpeg,.png,.tiff,.heic">
<button type="button" class="btn btn-primary" onclick="document.getElementById('file-input').click()">
<i class="fas fa-folder-open"></i> Sélectionner un fichier
</button>
</div>
<div class="mt-4">
<div class="row">
<div class="col-md-6">
<label for="id-dossier" class="form-label">ID Dossier *</label>
<input type="text" class="form-control" id="id-dossier" required>
</div>
<div class="col-md-6">
<label for="etude-id" class="form-label">ID Étude *</label>
<input type="text" class="form-control" id="etude-id" required>
</div>
</div>
<div class="row mt-3">
<div class="col-md-6">
<label for="utilisateur-id" class="form-label">ID Utilisateur *</label>
<input type="text" class="form-control" id="utilisateur-id" required>
</div>
<div class="col-md-6">
<label for="type-document" class="form-label">Type de Document Attendu</label>
<select class="form-select" id="type-document">
<option value="">Auto-détection</option>
<option value="acte_vente">Acte de Vente</option>
<option value="acte_donation">Acte de Donation</option>
<option value="acte_succession">Acte de Succession</option>
<option value="cni">Carte d'Identité</option>
<option value="contrat">Contrat</option>
</select>
</div>
</div>
</div>
<div class="mt-4">
<button type="submit" class="btn btn-success btn-lg">
<i class="fas fa-play"></i> Traiter le Document
</button>
</div>
</form>
<!-- Progress -->
<div class="progress-container mt-4">
<div class="progress">
<div class="progress-bar progress-bar-striped progress-bar-animated"
role="progressbar" style="width: 0%"></div>
</div>
<div class="mt-2">
<small class="text-muted" id="progress-text">Initialisation...</small>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h6><i class="fas fa-info-circle"></i> Informations</h6>
</div>
<div class="card-body">
<h6>Formats supportés:</h6>
<ul class="list-unstyled">
<li><i class="fas fa-file-pdf text-danger"></i> PDF</li>
<li><i class="fas fa-file-image text-primary"></i> JPEG, PNG</li>
<li><i class="fas fa-file-image text-info"></i> TIFF, HEIC</li>
</ul>
<h6 class="mt-3">Traitement:</h6>
<ul class="list-unstyled">
<li><i class="fas fa-eye"></i> OCR et extraction de texte</li>
<li><i class="fas fa-tags"></i> Classification automatique</li>
<li><i class="fas fa-search"></i> Extraction d'entités</li>
<li><i class="fas fa-check-circle"></i> Vérifications externes</li>
<li><i class="fas fa-brain"></i> Analyse LLM</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<!-- Documents Section -->
<div id="documents-section" class="content-section" style="display: none;">
<h2 class="mb-4">
<i class="fas fa-file-alt text-primary"></i>
Documents Traités
</h2>
<div class="row mb-3">
<div class="col-md-6">
<input type="text" class="form-control" id="search-documents" placeholder="Rechercher un document...">
</div>
<div class="col-md-3">
<select class="form-select" id="filter-status">
<option value="">Tous les statuts</option>
<option value="processing">En cours</option>
<option value="completed">Terminé</option>
<option value="error">Erreur</option>
</select>
</div>
<div class="col-md-3">
<select class="form-select" id="filter-type">
<option value="">Tous les types</option>
<option value="acte_vente">Acte de Vente</option>
<option value="acte_donation">Acte de Donation</option>
<option value="acte_succession">Acte de Succession</option>
<option value="cni">Carte d'Identité</option>
<option value="contrat">Contrat</option>
</select>
</div>
</div>
<div id="documents-list">
<!-- Documents will be loaded here -->
</div>
</div>
<!-- Statistics Section -->
<div id="stats-section" class="content-section" style="display: none;">
<h2 class="mb-4">
<i class="fas fa-chart-bar text-primary"></i>
Statistiques
</h2>
<div class="row">
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<i class="fas fa-file-alt fa-2x text-primary mb-2"></i>
<h4 id="total-documents">0</h4>
<p class="text-muted">Documents traités</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<i class="fas fa-clock fa-2x text-warning mb-2"></i>
<h4 id="processing-documents">0</h4>
<p class="text-muted">En cours</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<i class="fas fa-check-circle fa-2x text-success mb-2"></i>
<h4 id="success-rate">0%</h4>
<p class="text-muted">Taux de réussite</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<i class="fas fa-stopwatch fa-2x text-info mb-2"></i>
<h4 id="avg-time">0s</h4>
<p class="text-muted">Temps moyen</p>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h6>Types de Documents</h6>
</div>
<div class="card-body">
<canvas id="document-types-chart"></canvas>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h6>Évolution Temporelle</h6>
</div>
<div class="card-body">
<canvas id="timeline-chart"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- Settings Section -->
<div id="settings-section" class="content-section" style="display: none;">
<h2 class="mb-4">
<i class="fas fa-cog text-primary"></i>
Paramètres
</h2>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h6>Configuration API</h6>
</div>
<div class="card-body">
<div class="mb-3">
<label for="api-url" class="form-label">URL de l'API</label>
<input type="text" class="form-control" id="api-url" value="http://localhost:8000">
</div>
<div class="mb-3">
<label for="llm-model" class="form-label">Modèle LLM</label>
<select class="form-select" id="llm-model">
<option value="llama3:8b">Llama 3 8B</option>
<option value="mistral:7b">Mistral 7B</option>
</select>
</div>
<button class="btn btn-primary" onclick="testConnection()">
<i class="fas fa-plug"></i> Tester la Connexion
</button>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h6>APIs Externes</h6>
</div>
<div class="card-body">
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="enable-cadastre" checked>
<label class="form-check-label" for="enable-cadastre">
Cadastre
</label>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="enable-georisques" checked>
<label class="form-check-label" for="enable-georisques">
Géorisques
</label>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="enable-bodacc" checked>
<label class="form-check-label" for="enable-bodacc">
BODACC
</label>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="enable-gel-avoirs" checked>
<label class="form-check-label" for="enable-gel-avoirs">
Gel des Avoirs
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Document Analysis Modal -->
<div class="modal fade" id="analysisModal" tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="fas fa-search"></i> Analyse du Document
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="analysis-content">
<!-- Analysis content will be loaded here -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
<button type="button" class="btn btn-primary" onclick="downloadReport()">
<i class="fas fa-download"></i> Télécharger le Rapport
</button>
</div>
</div>
</div>
</div>
<script src="chart.min.js"></script>
<script src="app.js"></script>
<script>
// Initialisation de l'application après chargement de app.js
document.addEventListener('DOMContentLoaded', function() {
if (typeof NotaryApp !== 'undefined') {
window.notaryApp = new NotaryApp();
} else {
console.error('NotaryApp class not found');
}
});
</script>
</body>
</html>

View File

@ -1,79 +0,0 @@
#!/usr/bin/env python3
"""
Serveur web simple pour l'interface 4NK Notariat
"""
import http.server
import socketserver
import os
import sys
from pathlib import Path
def start_web_server(port=8080):
"""Démarre le serveur web pour l'interface"""
# Répertoire de l'interface web
web_dir = Path(__file__).parent
# Changement vers le répertoire web
os.chdir(web_dir)
# Configuration du serveur avec gestion du favicon
class CustomHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
def end_headers(self):
# Ajouter des headers pour éviter le cache du favicon
if self.path == '/favicon.ico':
self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
self.send_header('Pragma', 'no-cache')
self.send_header('Expires', '0')
super().end_headers()
def do_GET(self):
try:
# Gérer le favicon.ico
if self.path == '/favicon.ico':
self.send_response(200)
self.send_header('Content-type', 'image/svg+xml')
self.end_headers()
favicon_svg = '''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<text y=".9em" font-size="90">📄</text>
</svg>'''
self.wfile.write(favicon_svg.encode())
return
super().do_GET()
except (BrokenPipeError, ConnectionResetError):
# Ignorer les erreurs de connexion fermée par le client
pass
handler = CustomHTTPRequestHandler
try:
with socketserver.TCPServer(("", port), handler) as httpd:
print(f"🌐 Interface web 4NK Notariat démarrée sur http://localhost:{port}")
print(f"📁 Répertoire: {web_dir}")
print("🔄 Appuyez sur Ctrl+C pour arrêter")
print()
httpd.serve_forever()
except KeyboardInterrupt:
print("\n🛑 Arrêt du serveur web")
sys.exit(0)
except OSError as e:
if e.errno == 98: # Address already in use
print(f"❌ Erreur: Le port {port} est déjà utilisé")
print(f"💡 Essayez un autre port: python start_web.py {port + 1}")
else:
print(f"❌ Erreur: {e}")
sys.exit(1)
if __name__ == "__main__":
# Port par défaut ou port spécifié en argument
port = 8080
if len(sys.argv) > 1:
try:
port = int(sys.argv[1])
except ValueError:
print("❌ Erreur: Le port doit être un nombre")
sys.exit(1)
start_web_server(port)

View File

@ -6,7 +6,7 @@ import os
import logging
import re
from typing import Dict, Any, List
from services.worker.utils.llm_client import WorkerLLMClient
from utils.llm_client import WorkerLLMClient
logger = logging.getLogger(__name__)

View File

@ -3,7 +3,7 @@ Tâches de classification des documents
"""
import logging
from typing import Dict, Any
from services.worker.celery_app import app
from celery_app import app
logger = logging.getLogger(__name__)

View File

@ -3,7 +3,7 @@ Tâches d'extraction d'entités des documents
"""
import logging
from typing import Dict, Any, List
from services.worker.celery_app import app
from celery_app import app
logger = logging.getLogger(__name__)

View File

@ -3,7 +3,7 @@ Tâches d'indexation des documents
"""
import logging
from typing import Dict, Any
from services.worker.celery_app import app
from celery_app import app
logger = logging.getLogger(__name__)
@ -36,7 +36,7 @@ def index_document(self, doc_id: str, text: str, entities: Dict[str, Any], doc_t
# 1. Indexation dans AnythingLLM
try:
from services.worker.utils.anythingllm_client import AnythingLLMClient
from utils.anythingllm_client import AnythingLLMClient
anyllm_client = AnythingLLMClient()
anyllm_result = await anyllm_client.index_document_for_actes(
doc_id, text, entities, doc_type
@ -48,7 +48,7 @@ def index_document(self, doc_id: str, text: str, entities: Dict[str, Any], doc_t
# 2. Indexation dans OpenSearch
try:
from services.worker.utils.opensearch_client import OpenSearchClient
from utils.opensearch_client import OpenSearchClient
opensearch_client = OpenSearchClient()
opensearch_result = await opensearch_client.index_document(doc_id, {
'text_content': text,
@ -64,7 +64,7 @@ def index_document(self, doc_id: str, text: str, entities: Dict[str, Any], doc_t
# 3. Création du graphe Neo4j
try:
from services.worker.utils.neo4j_client import Neo4jClient
from utils.neo4j_client import Neo4jClient
neo4j_client = Neo4jClient()
# Ajout du document au graphe

View File

@ -3,7 +3,7 @@ Tâches OCR pour le traitement des documents
"""
import logging
from typing import Dict, Any
from services.worker.celery_app import app
from celery_app import app
logger = logging.getLogger(__name__)

View File

@ -6,8 +6,8 @@ import time
import logging
from typing import Dict, Any, Optional
from celery import current_task
from services.worker.celery_app import app
from services.worker.pipelines import (
from celery_app import app
from pipelines import (
preprocess, ocr, classify, extract, index, checks, finalize
)

View File

@ -3,7 +3,7 @@ Tâches de vérification et contrôle qualité
"""
import logging
from typing import Dict, Any
from services.worker.celery_app import app
from celery_app import app
logger = logging.getLogger(__name__)
@ -36,7 +36,7 @@ def verify_document(self, doc_id: str, entities: Dict[str, Any], doc_type: str,
# 1. Vérification des adresses via Cadastre
if 'bien' in entities and 'adresse' in entities['bien']:
try:
from services.worker.utils.external_apis import ExternalAPIManager
from utils.external_apis import ExternalAPIManager
api_manager = ExternalAPIManager()
address_result = await api_manager.verify_address(
entities['bien']['adresse'],
@ -51,7 +51,7 @@ def verify_document(self, doc_id: str, entities: Dict[str, Any], doc_type: str,
# 2. Vérification des risques géologiques
if 'bien' in entities and 'adresse' in entities['bien']:
try:
from services.worker.utils.external_apis import ExternalAPIManager
from utils.external_apis import ExternalAPIManager
api_manager = ExternalAPIManager()
risks_result = await api_manager.check_geological_risks(
entities['bien']['adresse']
@ -64,7 +64,7 @@ def verify_document(self, doc_id: str, entities: Dict[str, Any], doc_type: str,
# 3. Vérification des entreprises (si applicable)
if 'vendeur' in entities and 'nom' in entities['vendeur']:
try:
from services.worker.utils.external_apis import ExternalAPIManager
from utils.external_apis import ExternalAPIManager
api_manager = ExternalAPIManager()
company_result = await api_manager.verify_company(
entities['vendeur']['nom']
@ -77,7 +77,7 @@ def verify_document(self, doc_id: str, entities: Dict[str, Any], doc_type: str,
# 4. Vérification des personnes
if 'vendeur' in entities or 'acheteur' in entities:
try:
from services.worker.utils.external_apis import ExternalAPIManager
from utils.external_apis import ExternalAPIManager
api_manager = ExternalAPIManager()
# Vérification du vendeur

View File

@ -1,98 +0,0 @@
#!/bin/bash
# Script de démarrage rapide pour l'environnement de développement 4NK_IA
# Usage: ./start-dev.sh
echo "=== Démarrage de l'environnement de développement 4NK_IA ==="
echo
# Vérifier que nous sommes dans le bon répertoire
if [ ! -f "requirements-test.txt" ]; then
echo "❌ Erreur: Ce script doit être exécuté depuis le répertoire racine du projet 4NK_IA"
exit 1
fi
# Activer l'environnement virtuel Python
echo "🐍 Activation de l'environnement virtuel Python..."
if [ -d "venv" ]; then
source venv/bin/activate
echo " ✅ Environnement virtuel activé"
else
echo " ❌ Environnement virtuel non trouvé. Création..."
python3 -m venv venv
source venv/bin/activate
echo " ✅ Environnement virtuel créé et activé"
fi
# Vérifier les dépendances Python
echo "📦 Vérification des dépendances Python..."
if python -c "import fastapi" 2>/dev/null; then
echo " ✅ FastAPI disponible"
else
echo " ⚠️ FastAPI non installé. Installation..."
pip install fastapi uvicorn pydantic
fi
if python -c "import pytest" 2>/dev/null; then
echo " ✅ pytest disponible"
else
echo " ⚠️ pytest non installé. Installation..."
pip install pytest
fi
# Vérifier Docker
echo "🐳 Vérification de Docker..."
if command -v docker >/dev/null 2>&1; then
echo " ✅ Docker disponible"
if docker ps >/dev/null 2>&1; then
echo " ✅ Docker fonctionne"
else
echo " ⚠️ Docker installé mais non démarré"
echo " 💡 Démarrez Docker Desktop et activez l'intégration WSL2"
fi
else
echo " ❌ Docker non installé"
echo " 💡 Installez Docker Desktop et activez l'intégration WSL2"
fi
# Vérifier la configuration Git
echo "🔑 Vérification de la configuration Git..."
if git config --global user.name >/dev/null 2>&1; then
echo " ✅ Git configuré: $(git config --global user.name) <$(git config --global user.email)>"
else
echo " ❌ Git non configuré"
fi
# Vérifier SSH
echo "🔐 Vérification de la configuration SSH..."
if [ -f ~/.ssh/id_ed25519 ]; then
echo " ✅ Clé SSH trouvée"
if ssh -o ConnectTimeout=5 -o BatchMode=yes -T git@git.4nkweb.com 2>&1 | grep -q "successfully authenticated"; then
echo " ✅ Connexion SSH à git.4nkweb.com réussie"
else
echo " ⚠️ Connexion SSH à git.4nkweb.com échouée"
echo " 💡 Vérifiez que votre clé SSH est ajoutée à git.4nkweb.com"
fi
else
echo " ❌ Clé SSH non trouvée"
fi
echo
echo "=== Résumé de l'environnement ==="
echo "📁 Répertoire: $(pwd)"
echo "🐍 Python: $(python --version 2>/dev/null || echo 'Non disponible')"
echo "📦 pip: $(pip --version 2>/dev/null || echo 'Non disponible')"
echo "🔑 Git: $(git --version 2>/dev/null || echo 'Non disponible')"
echo "🐳 Docker: $(docker --version 2>/dev/null || echo 'Non disponible')"
echo
echo "=== Commandes utiles ==="
echo "🚀 Démarrer l'API: uvicorn services.host_api.app:app --reload --host 0.0.0.0 --port 8000"
echo "🧪 Lancer les tests: pytest"
echo "🐳 Démarrer Docker: make up"
echo "📊 Voir les logs: make logs"
echo "🛑 Arrêter Docker: make down"
echo
echo "✅ Environnement de développement prêt !"
echo "💡 Utilisez 'source venv/bin/activate' pour activer l'environnement virtuel"

View File

@ -1,111 +0,0 @@
#!/bin/bash
# Script de démarrage de la stack 4NK_IA_back
# Usage: ./start-stack.sh
set -euo pipefail
echo "🚀 Démarrage de la stack 4NK_IA_back..."
# Vérifier que nous sommes dans le bon répertoire
if [ ! -f "requirements-test.txt" ]; then
echo "❌ Erreur: Ce script doit être exécuté depuis le répertoire racine du projet 4NK_IA_back"
exit 1
fi
# Vérifier Docker
if ! command -v docker >/dev/null 2>&1; then
echo "❌ Docker n'est pas installé"
exit 1
fi
if ! docker ps >/dev/null 2>&1; then
echo "❌ Docker n'est pas démarré"
exit 1
fi
# Aller dans le répertoire infra
cd infra
# Créer le fichier .env s'il n'existe pas
if [ ! -f .env ]; then
echo "📝 Création du fichier .env..."
cat > .env << 'EOF'
# Configuration de l'environnement 4NK_IA_back
# Fichier généré automatiquement pour le démarrage de la stack
# Configuration générale
TZ=Europe/Paris
PUID=1000
PGID=1000
# Configuration PostgreSQL
POSTGRES_USER=notariat
POSTGRES_PASSWORD=notariat_pwd
POSTGRES_DB=notariat
# Configuration MinIO
MINIO_ROOT_USER=minio
MINIO_ROOT_PASSWORD=minio_pwd
MINIO_BUCKET=ingest
MINIO_ENDPOINT=minio:9000
# Configuration Neo4j
NEO4J_AUTH=neo4j/neo4j_pwd
# Configuration OpenSearch
OPENSEARCH_PASSWORD=opensearch_pwd
# Configuration AnythingLLM
ANYLLM_BASE_URL=http://anythingsqlite:3001
ANYLLM_API_KEY=notariat_api_key_2025
ANYLLM_WORKSPACE_NORMES=workspace_normes
ANYLLM_WORKSPACE_TRAMES=workspace_trames
ANYLLM_WORKSPACE_ACTES=workspace_actes
# Configuration Ollama
OLLAMA_BASE_URL=http://ollama:11434
# Configuration des URLs externes (pour les tests)
EXTERNAL_ANYLLM_BASE_URL=http://localhost:3001
EXTERNAL_OLLAMA_BASE_URL=http://localhost:11434
EOF
echo "✅ Fichier .env créé"
else
echo "✅ Fichier .env existe déjà"
fi
# Démarrer les services
echo "🐳 Démarrage des services Docker..."
docker compose up -d
# Attendre que les services soient prêts
echo "⏳ Attente du démarrage des services..."
sleep 15
# Vérifier l'API
echo "🔍 Vérification de l'API..."
if curl -s http://localhost:8000/api/health > /dev/null; then
echo "✅ API accessible sur http://localhost:8000"
else
echo "⚠️ API non accessible, vérifiez les logs avec: docker compose logs host-api"
fi
# Afficher le statut
echo "📊 Statut des services:"
docker compose ps
echo ""
echo "🎉 Stack 4NK_IA_back démarrée !"
echo ""
echo "📋 Services disponibles:"
echo " • API Notariale: http://localhost:8000"
echo " • Documentation API: http://localhost:8000/docs"
echo " • MinIO Console: http://localhost:9001"
echo " • PostgreSQL: localhost:5432"
echo " • Redis: localhost:6379"
echo ""
echo "🔧 Commandes utiles:"
echo " • Voir les logs: docker compose logs -f"
echo " • Arrêter: docker compose down"
echo " • Redémarrer: docker compose restart"

View File

@ -1,313 +0,0 @@
#!/bin/bash
echo "🚀 Démarrage du Système Notarial 4NK"
echo "======================================"
echo
# Couleurs pour les messages
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Fonction pour afficher les messages colorés
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Vérification des prérequis
check_prerequisites() {
print_status "Vérification des prérequis..."
# Python
if ! command -v python3 &> /dev/null; then
print_error "Python 3 n'est pas installé"
exit 1
fi
# Docker
if ! command -v docker &> /dev/null; then
print_error "Docker n'est pas installé"
exit 1
fi
# Docker Compose
if ! command -v docker-compose &> /dev/null; then
print_error "Docker Compose n'est pas installé"
exit 1
fi
# Tesseract
if ! command -v tesseract &> /dev/null; then
print_warning "Tesseract OCR n'est pas installé"
print_status "Installation de Tesseract..."
sudo apt-get update
sudo apt-get install -y tesseract-ocr tesseract-ocr-fra
fi
print_success "Prérequis vérifiés"
}
# Configuration de l'environnement
setup_environment() {
print_status "Configuration de l'environnement..."
# Création de l'environnement virtuel si nécessaire
if [ ! -d "venv" ]; then
print_status "Création de l'environnement virtuel Python..."
python3 -m venv venv
fi
# Activation de l'environnement virtuel
source venv/bin/activate
# Installation des dépendances Python
print_status "Installation des dépendances Python..."
pip install --upgrade pip
pip install -r docker/host-api/requirements.txt
# Configuration des variables d'environnement
if [ ! -f "infra/.env" ]; then
print_status "Création du fichier de configuration..."
cp infra/.env.example infra/.env
print_warning "Veuillez éditer infra/.env avec vos paramètres"
fi
print_success "Environnement configuré"
}
# Démarrage des services Docker
start_docker_services() {
print_status "Démarrage des services Docker..."
cd infra
# Pull des images
print_status "Téléchargement des images Docker..."
docker-compose pull
# Démarrage des services de base
print_status "Démarrage des services de base..."
docker-compose up -d postgres redis minio ollama anythingsqlite
# Attente que les services soient prêts
print_status "Attente que les services soient prêts..."
sleep 10
# Vérification des services
print_status "Vérification des services..."
# PostgreSQL
if docker-compose exec -T postgres pg_isready -U notariat &> /dev/null; then
print_success "PostgreSQL est prêt"
else
print_error "PostgreSQL n'est pas accessible"
fi
# Redis
if docker-compose exec -T redis redis-cli ping &> /dev/null; then
print_success "Redis est prêt"
else
print_error "Redis n'est pas accessible"
fi
# MinIO
if curl -s http://localhost:9000/minio/health/live &> /dev/null; then
print_success "MinIO est prêt"
else
print_warning "MinIO n'est pas accessible (normal si pas encore démarré)"
fi
# Ollama
if curl -s http://localhost:11434/api/tags &> /dev/null; then
print_success "Ollama est prêt"
else
print_warning "Ollama n'est pas accessible"
fi
cd ..
}
# Configuration d'Ollama
setup_ollama() {
print_status "Configuration d'Ollama..."
# Attente qu'Ollama soit prêt
sleep 5
# Téléchargement des modèles
print_status "Téléchargement des modèles LLM..."
# Llama 3 8B
print_status "Téléchargement de Llama 3 8B..."
curl -s http://localhost:11434/api/pull -d '{"name":"llama3:8b"}' &
# Mistral 7B
print_status "Téléchargement de Mistral 7B..."
curl -s http://localhost:11434/api/pull -d '{"name":"mistral:7b"}' &
print_warning "Les modèles LLM sont en cours de téléchargement en arrière-plan"
print_warning "Cela peut prendre plusieurs minutes selon votre connexion"
}
# Démarrage de l'API
start_api() {
print_status "Démarrage de l'API Notariale..."
cd services/host_api
# Démarrage en arrière-plan
nohup uvicorn app:app --host 0.0.0.0 --port 8000 --reload > ../../logs/api.log 2>&1 &
API_PID=$!
echo $API_PID > ../../logs/api.pid
# Attente que l'API soit prête
print_status "Attente que l'API soit prête..."
sleep 5
# Test de l'API
if curl -s http://localhost:8000/api/health &> /dev/null; then
print_success "API Notariale démarrée sur http://localhost:8000"
else
print_error "L'API n'est pas accessible"
fi
cd ../..
}
# Démarrage de l'interface web
start_web_interface() {
print_status "Démarrage de l'interface web..."
cd services/web_interface
# Démarrage en arrière-plan
nohup python start_web.py 8080 > ../../logs/web.log 2>&1 &
WEB_PID=$!
echo $WEB_PID > ../../logs/web.pid
# Attente que l'interface soit prête
sleep 3
if curl -s http://localhost:8080 &> /dev/null; then
print_success "Interface web démarrée sur http://localhost:8080"
else
print_error "L'interface web n'est pas accessible"
fi
cd ../..
}
# Création des répertoires de logs
create_log_directories() {
print_status "Création des répertoires de logs..."
mkdir -p logs
print_success "Répertoires de logs créés"
}
# Affichage du statut final
show_final_status() {
echo
echo "🎉 Système Notarial 4NK démarré avec succès!"
echo "============================================="
echo
echo "📊 Services disponibles:"
echo " • API Notariale: http://localhost:8000"
echo " • Interface Web: http://localhost:8080"
echo " • Documentation API: http://localhost:8000/docs"
echo " • MinIO Console: http://localhost:9001"
echo " • Ollama: http://localhost:11434"
echo
echo "📁 Fichiers de logs:"
echo " • API: logs/api.log"
echo " • Interface Web: logs/web.log"
echo
echo "🔧 Commandes utiles:"
echo " • Arrêter le système: ./stop_notary_system.sh"
echo " • Voir les logs: tail -f logs/api.log"
echo " • Redémarrer l'API: kill \$(cat logs/api.pid) && ./start_notary_system.sh"
echo
echo "📖 Documentation: docs/API-NOTARIALE.md"
echo
}
# Fonction principale
main() {
echo "Démarrage du système à $(date)"
echo
# Vérification des prérequis
check_prerequisites
# Configuration de l'environnement
setup_environment
# Création des répertoires
create_log_directories
# Démarrage des services Docker
start_docker_services
# Configuration d'Ollama
setup_ollama
# Démarrage de l'API
start_api
# Démarrage de l'interface web
start_web_interface
# Affichage du statut final
show_final_status
}
# Gestion des signaux
cleanup() {
echo
print_warning "Arrêt du système..."
# Arrêt de l'API
if [ -f "logs/api.pid" ]; then
API_PID=$(cat logs/api.pid)
if kill -0 $API_PID 2>/dev/null; then
kill $API_PID
print_status "API arrêtée"
fi
fi
# Arrêt de l'interface web
if [ -f "logs/web.pid" ]; then
WEB_PID=$(cat logs/web.pid)
if kill -0 $WEB_PID 2>/dev/null; then
kill $WEB_PID
print_status "Interface web arrêtée"
fi
fi
# Arrêt des services Docker
cd infra
docker-compose down
cd ..
print_success "Système arrêté"
exit 0
}
# Capture des signaux d'arrêt
trap cleanup SIGINT SIGTERM
# Exécution du script principal
main "$@"

View File

@ -1,160 +0,0 @@
#!/bin/bash
echo "🛑 Arrêt du Système Notarial 4NK"
echo "================================="
echo
# Couleurs pour les messages
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Arrêt de l'API
stop_api() {
print_status "Arrêt de l'API Notariale..."
if [ -f "logs/api.pid" ]; then
API_PID=$(cat logs/api.pid)
if kill -0 $API_PID 2>/dev/null; then
kill $API_PID
print_success "API arrêtée (PID: $API_PID)"
else
print_warning "API déjà arrêtée"
fi
rm -f logs/api.pid
else
print_warning "Fichier PID de l'API non trouvé"
fi
}
# Arrêt de l'interface web
stop_web_interface() {
print_status "Arrêt de l'interface web..."
if [ -f "logs/web.pid" ]; then
WEB_PID=$(cat logs/web.pid)
if kill -0 $WEB_PID 2>/dev/null; then
kill $WEB_PID
print_success "Interface web arrêtée (PID: $WEB_PID)"
else
print_warning "Interface web déjà arrêtée"
fi
rm -f logs/web.pid
else
print_warning "Fichier PID de l'interface web non trouvé"
fi
}
# Arrêt des services Docker
stop_docker_services() {
print_status "Arrêt des services Docker..."
cd infra
# Arrêt des services
docker-compose down
print_success "Services Docker arrêtés"
cd ..
}
# Nettoyage des processus orphelins
cleanup_orphaned_processes() {
print_status "Nettoyage des processus orphelins..."
# Recherche et arrêt des processus uvicorn
UVICORN_PIDS=$(pgrep -f "uvicorn.*app:app")
if [ ! -z "$UVICORN_PIDS" ]; then
echo $UVICORN_PIDS | xargs kill
print_success "Processus uvicorn orphelins arrêtés"
fi
# Recherche et arrêt des processus Python de l'interface web
WEB_PIDS=$(pgrep -f "start_web.py")
if [ ! -z "$WEB_PIDS" ]; then
echo $WEB_PIDS | xargs kill
print_success "Processus interface web orphelins arrêtés"
fi
}
# Affichage du statut final
show_final_status() {
echo
echo "✅ Système Notarial 4NK arrêté"
echo "==============================="
echo
echo "📊 Statut des services:"
# Vérification de l'API
if curl -s http://localhost:8000/api/health &> /dev/null; then
echo " • API: ${RED}Encore actif${NC}"
else
echo " • API: ${GREEN}Arrêté${NC}"
fi
# Vérification de l'interface web
if curl -s http://localhost:8080 &> /dev/null; then
echo " • Interface Web: ${RED}Encore actif${NC}"
else
echo " • Interface Web: ${GREEN}Arrêté${NC}"
fi
# Vérification des services Docker
cd infra
if docker-compose ps | grep -q "Up"; then
echo " • Services Docker: ${RED}Encore actifs${NC}"
else
echo " • Services Docker: ${GREEN}Arrêtés${NC}"
fi
cd ..
echo
echo "🔧 Pour redémarrer: ./start_notary_system.sh"
echo
}
# Fonction principale
main() {
echo "Arrêt du système à $(date)"
echo
# Arrêt de l'API
stop_api
# Arrêt de l'interface web
stop_web_interface
# Arrêt des services Docker
stop_docker_services
# Nettoyage des processus orphelins
cleanup_orphaned_processes
# Attente pour que les processus se terminent
sleep 2
# Affichage du statut final
show_final_status
}
# Exécution du script principal
main "$@"

View File

@ -1,18 +0,0 @@
#!/bin/bash
echo "=== Test du pipeline notarial simplifié ==="
# Test de santé de l'API
echo "1. Test de santé de l'API..."
curl -s http://localhost:8000/api/health | jq . || echo "API non accessible"
echo ""
echo "2. Test de l'endpoint racine..."
curl -s http://localhost:8000/ | jq . || echo "Endpoint racine non accessible"
echo ""
echo "3. Test de liste des documents..."
curl -s http://localhost:8000/api/documents | jq . || echo "Liste des documents non accessible"
echo ""
echo "=== Tests terminés ==="

View File

@ -1,76 +0,0 @@
#!/bin/bash
# Script de test de la configuration SSH pour 4NK_IA
# Usage: ./test-ssh-connection.sh
echo "=== Test de la configuration SSH ==="
echo
# Vérifier la présence des clés SSH
echo "1. Vérification des clés SSH :"
if [ -f ~/.ssh/id_ed25519 ]; then
echo " ✅ Clé privée trouvée : ~/.ssh/id_ed25519"
else
echo " ❌ Clé privée manquante : ~/.ssh/id_ed25519"
fi
if [ -f ~/.ssh/id_ed25519.pub ]; then
echo " ✅ Clé publique trouvée : ~/.ssh/id_ed25519.pub"
echo " 📋 Clé publique :"
cat ~/.ssh/id_ed25519.pub | sed 's/^/ /'
else
echo " ❌ Clé publique manquante : ~/.ssh/id_ed25519.pub"
fi
echo
# Vérifier la configuration SSH
echo "2. Vérification de la configuration SSH :"
if [ -f ~/.ssh/config ]; then
echo " ✅ Fichier de configuration SSH trouvé"
echo " 📋 Configuration :"
cat ~/.ssh/config | sed 's/^/ /'
else
echo " ❌ Fichier de configuration SSH manquant"
fi
echo
# Vérifier la configuration Git
echo "3. Vérification de la configuration Git :"
echo " 📋 Configuration Git :"
git config --global --list | grep -E "(user\.|url\.|init\.)" | sed 's/^/ /'
echo
# Tester les connexions SSH
echo "4. Test des connexions SSH :"
echo " 🔍 Test de connexion à git.4nkweb.com :"
if ssh -o ConnectTimeout=10 -o BatchMode=yes -T git@git.4nkweb.com 2>&1 | grep -q "successfully authenticated"; then
echo " ✅ Connexion SSH réussie à git.4nkweb.com"
elif ssh -o ConnectTimeout=10 -o BatchMode=yes -T git@git.4nkweb.com 2>&1 | grep -q "Permission denied"; then
echo " ⚠️ Clé SSH non autorisée sur git.4nkweb.com"
echo " 💡 Ajoutez votre clé publique dans les paramètres SSH de votre compte"
else
echo " ❌ Impossible de se connecter à git.4nkweb.com"
fi
echo " 🔍 GitHub non configuré (inutile pour ce projet)"
echo
# Instructions pour ajouter les clés
echo "5. Instructions pour ajouter votre clé SSH :"
echo " 📋 Votre clé publique SSH :"
cat ~/.ssh/id_ed25519.pub
echo
echo " 🔗 git.4nkweb.com :"
echo " 1. Connectez-vous à git.4nkweb.com"
echo " 2. Allez dans Settings > SSH Keys"
echo " 3. Ajoutez la clé ci-dessus"
echo
echo " 🔗 GitHub : Non nécessaire pour ce projet"
echo
echo "=== Fin du test ==="