diff --git a/.trunk/.gitignore b/.trunk/.gitignore new file mode 100644 index 0000000..15966d0 --- /dev/null +++ b/.trunk/.gitignore @@ -0,0 +1,9 @@ +*out +*logs +*actions +*notifications +*tools +plugins +user_trunk.yaml +user.yaml +tmp diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml new file mode 100644 index 0000000..1e4ac82 --- /dev/null +++ b/.trunk/trunk.yaml @@ -0,0 +1,32 @@ +# This file controls the behavior of Trunk: https://docs.trunk.io/cli +# To learn more about the format of this file, see https://docs.trunk.io/reference/trunk-yaml +version: 0.1 +cli: + version: 1.25.0 +# Trunk provides extensibility via plugins. (https://docs.trunk.io/plugins) +plugins: + sources: + - id: trunk + ref: v1.7.4 + uri: https://github.com/trunk-io/plugins +# Many linters and tools depend on runtimes - configure them here. (https://docs.trunk.io/runtimes) +runtimes: + enabled: + - node@22.16.0 + - python@3.10.8 +# This is the section where you manage your linters. (https://docs.trunk.io/check/configuration) +lint: + enabled: + - git-diff-check + - hadolint@2.14.0 + - markdownlint@0.47.0 + - osv-scanner@2.3.1 + - prettier@3.7.4 + - trufflehog@3.92.4 +actions: + disabled: + - trunk-announce + - trunk-check-pre-push + - trunk-fmt-pre-commit + enabled: + - trunk-upgrade-available diff --git a/Dockerfile b/Dockerfile index 6964fcb..55d43e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,78 @@ -# Backend Dockerfile -FROM python:3.12-slim - +# ================================ +# Base +# ================================ +FROM python:3.12-slim AS base WORKDIR /app -# Copier et installer les dépendances -COPY requirements.txt . -RUN pip install --no-cache-dir --upgrade pip \ - && pip install --no-cache-dir -r requirements.txt +# Installer dépendances système si nécessaire +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements.txt . +RUN pip install --no-cache-dir --upgrade pip + +# ================================ +# DEV +# ================================ +FROM base AS dev +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + ENV=development + +# Installer dépendances dev (si vous avez un requirements.dev.txt) +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Créer dossiers +RUN mkdir -p /app/data /app/logs && chmod -R 777 /app/data /app/logs -# Copier le reste du projet COPY . . -# Créer dossier persistant pour SQLite avec bonnes permissions -RUN mkdir -p /app/data && chmod 777 /app/data - -# Exposer le port EXPOSE 8000 +CMD ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] -# Lancer l'API et initialiser la DB au démarrage -# CMD ["sh", "-c", "uvicorn api:app --host 0.0.0.0 --port 8000"] +# ================================ +# STAGING +# ================================ +FROM base AS staging +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + ENV=staging -CMD ["sh", "-c", "python init_db.py && uvicorn api:app --host 0.0.0.0 --port 8000"] \ No newline at end of file +RUN pip install --no-cache-dir -r requirements.txt + +RUN mkdir -p /app/data /app/logs && chmod -R 755 /app/data /app/logs + +COPY . . + +# Initialiser la DB au build +RUN python init_db.py || true + +EXPOSE 8002 +CMD ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "8002", "--log-level", "info"] + +# ================================ +# PROD +# ================================ +FROM base AS prod +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + ENV=production + +RUN pip install --no-cache-dir -r requirements.txt + +# Créer utilisateur non-root pour la sécurité +RUN useradd -m -u 1000 appuser && \ + mkdir -p /app/data /app/logs && \ + chown -R appuser:appuser /app + +COPY --chown=appuser:appuser . . + +# Initialiser la DB au build +RUN python init_db.py || true + +USER appuser + +EXPOSE 8004 +CMD ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "8004", "--workers", "4"] \ No newline at end of file diff --git a/database/db_config.py b/database/db_config.py index 0f95ba1..ab89bbb 100644 --- a/database/db_config.py +++ b/database/db_config.py @@ -7,7 +7,7 @@ from database.models.generic_model import Base logger = logging.getLogger(__name__) -DATABASE_URL = os.getenv("DATABASE_URL", "sqlite+aiosqlite:///./data/sage_dataven.db") +DATABASE_URL = os.getenv("DATABASE_URL") engine = create_async_engine( DATABASE_URL, diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..48a0131 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,24 @@ +services: + backend: + container_name: dev_sage_api + build: + context: . + target: dev + env_file: .env + volumes: + - .:/app + - /app/__pycache__ + - ./data:/app/data + - ./logs:/app/logs + ports: + - "8000:8000" + environment: + ENV: development + DEBUG: "true" + DATABASE_URL: "sqlite+aiosqlite:///./data/sage_dev.db" + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/health"] + interval: 30s + timeout: 10s + retries: 3 \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..027eaf7 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,23 @@ +services: + backend: + container_name: prod_sage_api + build: + context: . + target: prod + env_file: .env.production + volumes: + - ./data:/app/data + - ./logs:/app/logs + ports: + - "8004:8004" + environment: + ENV: production + DEBUG: "false" + DATABASE_URL: "sqlite+aiosqlite:///./data/sage_prod.db" + restart: always + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8004/health"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 40s \ No newline at end of file diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml new file mode 100644 index 0000000..81f9215 --- /dev/null +++ b/docker-compose.staging.yml @@ -0,0 +1,22 @@ +services: + backend: + container_name: staging_sage_api + build: + context: . + target: staging + env_file: .env.staging + volumes: + - ./data:/app/data + - ./logs:/app/logs + ports: + - "8002:8002" + environment: + ENV: staging + DEBUG: "false" + DATABASE_URL: "sqlite+aiosqlite:///./data/sage_staging.db" + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8002/health"] + interval: 30s + timeout: 10s + retries: 3 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 2379fd6..9989985 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,11 +1,4 @@ services: - vps-sage-api: - build: . - container_name: vps-sage-api - env_file: .env - volumes: - - ./data:/app/data - - ./logs:/app/logs - ports: - - "8000:8000" - restart: unless-stopped \ No newline at end of file + backend: + build: + context: . \ No newline at end of file diff --git a/init_db.py b/init_db.py index f5f7e27..0534362 100644 --- a/init_db.py +++ b/init_db.py @@ -14,33 +14,14 @@ logger = logging.getLogger(__name__) async def main(): - print("\n" + "=" * 60) - print("Initialisation de la base de données délocalisée") - print("=" * 60 + "\n") - try: logger.info("Debut de l'initialisation") await init_db() logger.info("Initialisation terminee") + print("\nInitialisation terminee") print("\nBase de données créée avec succès !") - print("Fichier: sage_dataven.db") - print("\nTables créées:") - print(" |- email_logs (Journalisation emails)") - print(" |- signature_logs (Suivi signatures Universign)") - print(" |- workflow_logs (Transformations documents)") - print(" |- cache_metadata (Métadonnées cache)") - print(" |- audit_logs (Journal d'audit)") - - print("\nProchaines étapes:") - print(" 1. Configurer le fichier .env avec les credentials") - print(" 2. Lancer la gateway Windows sur la machine Sage") - print(" 3. Lancer l'API VPS: uvicorn api:app --host 0.0.0.0 --port 8000") - print(" 4. Ou avec Docker : docker-compose up -d") - print(" 5. Tester: http://IP_DU_VPS:8000/docs") - - print("\n" + "=" * 60 + "\n") return True except Exception as e: