File size: 4,289 Bytes
9cf9a2a
a28fd7a
9cf9a2a
 
 
 
a28fd7a
 
9cf9a2a
4093877
 
 
 
 
 
 
 
9cf9a2a
 
 
 
 
 
 
4093877
 
 
 
9cf9a2a
 
 
 
aabe2a2
e74628e
9cf9a2a
e74628e
 
 
 
9cf9a2a
e74628e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9cf9a2a
4093877
 
 
e74628e
 
 
9cf9a2a
 
 
 
7435134
 
 
 
9cf9a2a
 
 
4093877
9cf9a2a
4093877
 
 
 
44b9a02
 
 
 
 
 
 
 
 
 
2afa5a5
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# JuriX Backend — Production Dockerfile
# Python 3.11 — PostgreSQL native (no Redis/Meilisearch/Celery)

FROM python:3.11-slim

LABEL maintainer="JuriX Team <support@jurix.cm>"
LABEL description="JuriX v3.0 Backend API"
LABEL version="3.0.0"

# Dependances systeme.
#
# tesseract-ocr et poppler-utils ne sont PAS optionnels :
#   - poppler fournit pdftoppm, dont depend pdf2image ; sans lui,
#     GET /laws/{id}/page/{n} — la visionneuse par images du front — repond 500
#     en production alors qu'il fonctionne en developpement ;
#   - tesseract-ocr et ses dictionnaires francais/anglais servent au repli OCR
#     et au diagnostic de la couche texte des PDF scannes.
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc \
    g++ \
    curl \
    git \
    wget \
    ca-certificates \
    poppler-utils \
    tesseract-ocr \
    tesseract-ocr-fra \
    tesseract-ocr-eng \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Copy dependency file first (for Docker layer caching)
COPY requirements.txt constraints.txt ./

# constraints.txt fige chaque version sur celle du venv de developpement, ou la
# suite de tests passe (scripts/figer_versions.sh). Sans lui, l'image prenait
# les dernieres versions du jour de sa construction, jamais testees ensemble :
# SQLAlchemy 2.1 l'a fait planter au demarrage.
RUN pip install --no-cache-dir --upgrade pip && \
    pip install --no-cache-dir -r requirements.txt -c constraints.txt

# Utilisateur sans privileges, et modele fastText de detection de langue
# (126 Mo), AVANT la copie du code : cette couche reste alors en cache d'une
# mise a jour a l'autre. Placee apres, chaque modification du code
# retelechargeait le modele, puis le chown le recopiait dans une couche de
# plus ; et un telechargement rate donnait, sans erreur, une image sans
# detection de langue. Un echec fait desormais echouer la construction :
# `docker compose up --build` s'arrete, et l'ancienne version continue de
# tourner.
RUN useradd -m -u 1000 -s /bin/bash jurix && \
    mkdir -p /app/models/fasttext /app/logs /app/data && \
    wget -q https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.bin \
         -O /app/models/fasttext/lid.176.bin && \
    chown -R jurix:jurix /app

# Copie du code applicatif.
# Ce que ce COPY embarque est filtre par .dockerignore : sans lui, le fichier
# .env — donc les cles d'API — se retrouvait dans une couche de l'image, lisible
# par quiconque peut la telecharger. --chown evite un `chown -R` apres coup,
# qui recopiait tout /app dans une nouvelle couche.
COPY --chown=jurix:jurix . .

USER jurix

# Runtime env vars
# ML_MODELS_PATH et LOG_LEVEL ont ete retirees : aucun code ne les lisait.
# language_detector.py resout lui-meme le chemin du modele, et la journalisation
# est configuree dans app/main.py. Des variables d'environnement que personne ne
# lit donnent l'illusion d'un reglage.
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PYTHONPATH=/app \
    TESSERACT_PATH=/usr/bin/tesseract

# Les migrations sont appliquees au demarrage : le schema du produit vit dans
# alembic (search_vector, index GIN, declencheurs, extensions), pas dans
# Base.metadata — un conteneur qui demarre sans les avoir jouees repond 500 sur
# toute recherche.
# Railway injects PORT automatically.
#
# Forme JSON avec `exec` : en forme shell, docker lance `/bin/sh -c "..."` et
# c'est le SHELL qui recoit le SIGTERM de `docker stop`. uvicorn, lui, ne le
# voit jamais et se fait tuer au bout du delai de grace — donc le lifespan
# n'atteint jamais son `finally`, ou vivent la fermeture du pool de connexions
# et l'arret de la tache de purge des caches. `exec` fait remplacer le shell
# par uvicorn, qui recoit alors le signal directement.
#
# Le shell reste necessaire pour `&&` et pour l'expansion de ${PORT}.
#
# --no-access-log : le journal d'acces d'uvicorn ecrit l'URL entiere, chaine de
# requete comprise. Or le WebSocket de l'envoi par lots porte le jeton
# d'administration dans l'URL (?token=...) : il finissait en clair dans
# `docker compose logs`. L'application journalise elle-meme ce qui compte.
CMD ["sh", "-c", "alembic upgrade head && exec uvicorn app.main:app --host 0.0.0.0 --port ${PORT:-8000} --workers 1 --log-level info --no-access-log"]