Image Docker, CI et docs pour le serveur Rust

Image en trois étapes (frontend Vite, binaire Rust, Debian slim avec
FFmpeg), healthcheck /healthz, dépendances npm propres à l'ancien
serveur retirées, CI Rust (fmt, clippy, tests) et documentation de la
mise à jour au prochain démarrage de la stack.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013qMexTfVRaegbPAgsagCAJ
This commit is contained in:
Claude committed 2026-10-12 17:20:38 +00:00
1 parent f9828d4887
commit 25703abb7d
14 files changed
+272 -8270

No files matched your search

+5
View File
@@ -38,3 +38,8 @@ logs
# Uploads (seront montés comme volume) # Uploads (seront montés comme volume)
uploads/* uploads/*
# Compilation Rust
server/target
**/target
.claude
+20 -4
View File
@@ -6,8 +6,8 @@ on:
pull_request: pull_request:
jobs: jobs:
app: frontend:
name: Application (TypeScript) name: Frontend (TypeScript)
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -15,13 +15,29 @@ jobs:
with: with:
node-version: 20 node-version: 20
cache: npm cache: npm
# Les dépendances natives (canvas, sharp) et le navigateur Remotion ne
# servent pas aux vérifications : on saute leurs scripts d'installation.
- run: npm ci --ignore-scripts - run: npm ci --ignore-scripts
- run: npm run check - run: npm run check
- run: npm test - run: npm test
- run: npm run build - run: npm run build
server:
name: Serveur (Rust)
runs-on: ubuntu-latest
defaults:
run:
working-directory: server
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
workspaces: server
- run: cargo fmt --check
- run: cargo clippy --locked --all-targets -- -D warnings
- run: cargo test --locked
ffmpeg-service: ffmpeg-service:
name: Service vidéo (Python) name: Service vidéo (Python)
runs-on: ubuntu-latest runs-on: ubuntu-latest
+1
View File
@@ -35,3 +35,4 @@ npm-debug.log*
Thumbs.db Thumbs.db
# Rust # Rust
server/target server/target
.claude/worktrees
+30 -14
View File
@@ -4,7 +4,8 @@
L'application utilise Docker Compose pour orchestrer : L'application utilise Docker Compose pour orchestrer :
- **PostgreSQL 16** : Base de données sur réseau interne - **PostgreSQL 16** : Base de données sur réseau interne
- **Social Flow App** : Application Node.js exposée sur le port **4523** - **Social Flow App** : serveur Rust (API + frontend compilé), port interne **5555**
- **ffmpeg-api** / **remotion-renderer** : services de traitement vidéo des Reels
## 🚀 Démarrage rapide ## 🚀 Démarrage rapide
@@ -203,7 +204,8 @@ La stack se construit depuis les sources : `app` et `ffmpeg-api` ont une section
## 📊 Healthchecks ## 📊 Healthchecks
- **PostgreSQL** : Vérifie que la base est prête avant de démarrer l'app - **PostgreSQL** : Vérifie que la base est prête avant de démarrer l'app
- **Migrations** : Exécutées automatiquement au démarrage (`drizzle-kit push --force`) - **Migrations** : exécutées automatiquement par le serveur à chaque démarrage (idempotentes)
- **Application** : `GET /healthz` répond `ok` quand le serveur et la base sont joignables (healthcheck de l'image)
## 🐛 Dépannage ## 🐛 Dépannage
@@ -232,23 +234,37 @@ Vérifiez que `DATABASE_URL` dans `.env` correspond aux variables `PGUSER`, `PGP
## 📦 Volumes Docker ## 📦 Volumes Docker
- `postgres_data` : Données persistantes de PostgreSQL - `postgres_data` : données persistantes de PostgreSQL
- Mappages locaux : - `encryption_key` : clé de chiffrement des jetons (`/app/.encryption-key-data`)
- `./attached_assets` → `/app/attached_assets` (médias uploadés) - `audio_uploads`, `media_uploads`, `logos_uploads`, `stories_uploads` : fichiers téléversés (`/app/uploads/...`)
- `./migrations` → `/app/migrations` (migrations DB)
## 🔄 Mise à jour de l'application ## 🔄 Mise à jour de l'application
L'image `socialflow-app` est construite à partir des sources
(`pull_policy: build`) : le prochain démarrage de la stack après un `git pull`
applique la nouvelle version.
```bash ```bash
# 1. Pull les dernières modifications # 1. Récupérer les dernières modifications
git pull git pull
# 2. Reconstruire l'image # 2. Reconstruire et redémarrer (Portainer : « Update the stack »)
docker-compose build docker compose up -d --build
# 3. Redémarrer # 3. Vérifier les logs
docker-compose up -d docker compose logs -f app
# 4. Vérifier les logs
docker-compose logs -f app
``` ```
Au démarrage, le serveur complète le schéma de la base (tables, colonnes,
index) : aucune commande de migration n'est nécessaire. Les données, les
fichiers téléversés, la clé de chiffrement et les sessions ouvertes sont
conservés.
### Passage au serveur Rust
Depuis la version 2, le serveur est écrit en Rust (dossier `server/`) et
remplace l'ancien serveur Node/Express. La bascule ne demande rien de plus
qu'une reconstruction : mêmes variables d'environnement, même base, mêmes
volumes, et les utilisateurs déjà connectés le restent (cookie `connect.sid`
et table `session` repris tels quels). Le rendu Remotion des Reels tourne
désormais dans son propre conteneur (`remotion-renderer`).
+60 -76
View File
@@ -1,84 +1,68 @@
# Debian slim — glibc requis par le Chrome headless shell de Remotion (posix_fallocate64, etc.) # SocialFlow — image de l'application : serveur Rust + frontend React compilé.
FROM node:20-slim #
# Trois étapes : le frontend est compilé par Vite, le serveur par Cargo, et
# l'image finale ne contient que le binaire, les fichiers statiques et FFmpeg
# (vignettes vidéo, durée des pistes audio). Pas de Node à l'exécution.
# ---------------------------------------------------------------------------
# 1. Frontend (Vite)
# ---------------------------------------------------------------------------
FROM node:20-slim AS frontend
WORKDIR /app
COPY package.json package-lock.json ./
# Aucune dépendance native n'est utile au build du frontend
RUN npm ci --ignore-scripts --no-audit --no-fund
COPY client ./client
COPY shared ./shared
COPY attached_assets ./attached_assets
COPY vite.config.ts tsconfig.json tailwind.config.ts postcss.config.js components.json ./
RUN npx vite build
# ---------------------------------------------------------------------------
# 2. Serveur (Rust)
# ---------------------------------------------------------------------------
FROM rust:1-slim-bookworm AS backend
WORKDIR /app/server
# Dépendances compilées dans une couche à part : elles ne sont recompilées que
# si Cargo.toml / Cargo.lock changent.
COPY server/Cargo.toml server/Cargo.lock ./
RUN mkdir src && echo 'fn main() {}' > src/main.rs \
&& cargo build --release --locked \
&& rm -rf src target/release/socialflow target/release/deps/socialflow-* target/release/.fingerprint/socialflow-*
COPY server/src ./src
RUN cargo build --release --locked
# ---------------------------------------------------------------------------
# 3. Image d'exécution
# ---------------------------------------------------------------------------
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
ffmpeg \
fonts-dejavu-core \
fontconfig \
curl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app WORKDIR /app
# Augmenter la mémoire AVANT npm install + build (évite OOM sur les grosses dépendances) COPY --from=backend /app/server/target/release/socialflow /usr/local/bin/socialflow
ENV NODE_OPTIONS="--max-old-space-size=4096" COPY --from=frontend /app/dist/public ./dist/public
COPY public-root ./public-root
COPY server/fonts ./server/fonts
# Dépendances système : build tools, canvas/sharp, fonts, ffmpeg + dépendances Chrome RUN mkdir -p uploads/media uploads/logos uploads/stories uploads/audio uploads/temp
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 make g++ \
libcairo2-dev libjpeg-dev libpango1.0-dev libgif-dev libpixman-1-dev \
libvips-dev pkg-config \
fonts-dejavu-core fontconfig \
curl tar bzip2 \
ffmpeg \
libcap2-bin \
ca-certificates \
# Dépendances Chrome headless
libasound2 \
libatk-bridge2.0-0 \
libatk1.0-0 \
libcups2 \
libdbus-1-3 \
libdrm2 \
libgbm1 \
libglib2.0-0 \
libgtk-3-0 \
libnspr4 \
libnss3 \
libpango-1.0-0 \
libx11-6 \
libx11-xcb1 \
libxcb1 \
libxcomposite1 \
libxdamage1 \
libxext6 \
libxfixes3 \
libxkbcommon0 \
libxrandr2 \
libxshmfence1 \
libxtst6 \
&& rm -rf /var/lib/apt/lists/*
# Copier les fichiers de configuration
COPY package*.json ./
# Installer les dépendances
RUN npm install --legacy-peer-deps --no-audit --no-fund
# Copier tout le code source
COPY . .
# Télécharger et installer la police DejaVu Sans
RUN mkdir -p server/fonts && \
cd server/fonts && \
curl -L -f --retry 3 --retry-delay 2 -o dejavu.tar.bz2 "https://sourceforge.net/projects/dejavu/files/dejavu/2.37/dejavu-fonts-ttf-2.37.tar.bz2/download" && \
tar -xjf dejavu.tar.bz2 && \
cp dejavu-fonts-ttf-2.37/ttf/DejaVuSans.ttf . && \
rm -rf dejavu.tar.bz2 dejavu-fonts-ttf-2.37 && \
echo "✓ Police DejaVu Sans installée"
# Build du frontend et backend
RUN npm run build
# Nettoyer les fichiers inutiles
RUN rm -rf client/node_modules \
client/.vite \
node_modules/.cache \
/tmp/remotion-*
# Pré-bundler la composition Remotion
RUN node scripts/prebundle-remotion.mjs || true
# Télécharger le Chrome headless shell de Remotion (binaire glibc natif sur Debian)
RUN npx remotion browser ensure && echo "✓ Remotion browser prêt"
# Exposer le port
EXPOSE 5555
ENV NODE_ENV=production ENV NODE_ENV=production
ENV PORT=5555 ENV PORT=5555
EXPOSE 5555
CMD ["npm", "run", "start"] HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD curl -fsS "http://localhost:${PORT}/healthz" > /dev/null || exit 1
CMD ["socialflow"]
+45 -34
View File
@@ -100,9 +100,9 @@ Architecture Production-Ready avec sécurité renforcée.
| Fonctionnalité | Détail | | Fonctionnalité | Détail |
| ------------------------------- | ---------------------------------------------------- | | ------------------------------- | ---------------------------------------------------- |
| **Authentification** | Passport.js + bcrypt, sessions PostgreSQL | | **Authentification** | bcrypt, sessions PostgreSQL (cookie signé) |
| **Rate Limiting** | 100 req/15min global, 5 tentatives login/15min | | **Rate Limiting** | 1000 req/15min global, 30 tentatives login/15min |
| **Headers de sécurité** | Helmet (CSP, HSTS, XSS protection) | | **Headers de sécurité** | CSP, HSTS, X-Frame-Options, nosniff… |
| **Chiffrement des tokens** | Tokens Facebook/Instagram chiffrés (AES-256) | | **Chiffrement des tokens** | Tokens Facebook/Instagram chiffrés (AES-256) |
| **Gestion multi-utilisateurs** | Rôles Admin / User, permissions par page | | **Gestion multi-utilisateurs** | Rôles Admin / User, permissions par page |
| **Console SQL** | Requêtes directes en base (admin uniquement) | | **Console SQL** | Requêtes directes en base (admin uniquement) |
@@ -117,43 +117,53 @@ Architecture Production-Ready avec sécurité renforcée.
socialflow/ socialflow/
├── client/ # Frontend React (Vite + TailwindCSS) ├── client/ # Frontend React (Vite + TailwindCSS)
│ └── src/ │ └── src/
│ ├── pages/ # 17 pages desktop + 13 pages mobile │ ├── pages/ # Pages desktop + mobile
│ ├── components/ # 47 composants UI (Radix/shadcn) │ ├── components/ # Composants UI (Radix/shadcn)
│ └── hooks/ # Hooks React personnalisés │ └── remotion/ # Compositions vidéo des Reels (aperçu + rendu)
├── server/ # Backend Express.js (TypeScript) ├── server/ # Backend Rust (axum + sqlx)
│ ├── routes.ts # 45+ endpoints REST API │ ├── Cargo.toml
│ ├── routes/ # Routes modulaires (analytics, reels) │ └── src/
│ ├── services/ # 12 services métier │ ├── main.rs # Démarrage : migrations, routes, tâches de fond
│ │ ├── analytics.ts # Sync Facebook Graph API │ ├── migrate.rs # Schéma PostgreSQL, mis à jour au démarrage
│ │ ├── facebook.ts # Publication Facebook/Instagram │ ├── auth.rs # Sessions (cookie connect.sid) et rôles
│ │ ├── openrouter.ts # Génération texte IA │ ├── db/ # Accès aux données
│ │ ├── cloudinary.ts # Upload cloud médias │ ├── routes/ # API REST (/api, /api/v1 externe, pages légales)
│ │ ├── ffmpeg.ts # Encodage vidéo (microservice) │ └── services/ # Facebook, TikTok, OpenRouter, Reels, scheduler, crons
│ │ ├── freesound.ts # Musique libre de droits ├── shared/ # Types et logique partagés par le frontend
│ │ ├── jamendo.ts # Musique libre de droits ├── remotion-service/ # Rendu des Reels avec Remotion (Node + Chrome)
│ │ ├── imageProcessor.ts # Traitement images (Sharp)
│ │ ├── scheduler.ts # Publications programmées
│ │ └── token_manager.ts # Gestion tokens OAuth
│ └── cron.ts # Tâches planifiées
├── shared/ # Schéma DB partagé (Drizzle ORM)
├── ffmpeg-service/ # Microservice FFmpeg (Python/FastAPI) ├── ffmpeg-service/ # Microservice FFmpeg (Python/FastAPI)
├── docker-compose.yml # Stack Docker (3 services) ├── qwen-tts/ # Voix Qwen3-TTS (optionnel)
└── Dockerfile # Image production Node.js ├── docker-compose.yml # Stack Docker
└── Dockerfile # Image de l'application (Rust + frontend compilé)
``` ```
### Stack Technique ### Stack Technique
| Couche | Technologies | | Couche | Technologies |
| ---------- | --------------------------------------------------------------------------- | | ---------- | --------------------------------------------------------------------------- |
| **Frontend** | React 18, Vite, TailwindCSS, Radix UI, Recharts, Framer Motion, Wouter | | **Frontend** | React 18, Vite, TailwindCSS, Radix UI, Recharts, Wouter |
| **Backend** | Express.js, TypeScript, Passport.js, Helmet, node-cron | | **Backend** | Rust : axum, tokio, sqlx, reqwest |
| **BDD** | PostgreSQL 16, Drizzle ORM, connect-pg-simple (sessions) | | **BDD** | PostgreSQL 16 (schéma créé et migré automatiquement au démarrage) |
| **IA** | OpenRouter (multi-modèle : GPT-4, Claude, Gemini, etc.) | | **IA** | OpenRouter (multi-modèle : GPT-4, Claude, Gemini, etc.) |
| **Médias** | Cloudinary (upload), Sharp (images), FFmpeg microservice (vidéo) | | **Médias** | Stockage local, FFmpeg (vignettes, microservice vidéo), Remotion (Reels) |
| **Musique** | FreeSound API, Jamendo API | | **Réseaux** | Facebook Graph API (Pages, Instagram, Insights), TikTok Content Posting |
| **Réseaux** | Facebook Graph API v19 (Pages, Instagram, Insights) |
| **Infra** | Docker Compose, Nginx reverse proxy | | **Infra** | Docker Compose, Nginx reverse proxy |
### Mise à jour
L'image de l'application est construite à partir des sources (`pull_policy: build`).
Après un `git pull`, le prochain démarrage de la stack reconstruit l'image et
applique la nouvelle version :
```bash
git pull
docker compose up -d --build
```
Au démarrage, le serveur met le schéma de la base à jour tout seul (aucune
commande de migration à lancer). Les données, les médias, la clé de chiffrement
et les sessions ouvertes sont conservés.
--- ---
## 🚀 Installation Rapide ## 🚀 Installation Rapide
@@ -161,7 +171,7 @@ socialflow/
### Prérequis ### Prérequis
- [Docker](https://www.docker.com/) & Docker Compose - [Docker](https://www.docker.com/) & Docker Compose
- [Node.js](https://nodejs.org/) v18+ (pour le développement local) - Pour le développement local : [Rust](https://rustup.rs/) (stable), [Node.js](https://nodejs.org/) 20+ et PostgreSQL
### 1. Récupérer l'application ### 1. Récupérer l'application
@@ -199,11 +209,12 @@ FACEBOOK_APP_SECRET=...
```bash ```bash
# Production (Docker) # Production (Docker)
docker-compose up -d docker compose up -d --build
# Développement local # Développement local (deux terminaux)
npm install npm install
npm run dev DATABASE_URL=postgresql://... npm run dev:server # API Rust sur le port 5000
npm run dev # Frontend Vite, relaie /api vers le port 5000
``` ```
🚀 Rendez-vous sur **`http://localhost:5555`** pour commencer ! 🚀 Rendez-vous sur **`http://localhost:5555`** pour commencer !
-1
View File
@@ -69,7 +69,6 @@ services:
- media_uploads:/app/uploads/media - media_uploads:/app/uploads/media
- logos_uploads:/app/uploads/logos - logos_uploads:/app/uploads/logos
- stories_uploads:/app/uploads/stories - stories_uploads:/app/uploads/stories
command: npm run start
# Service FFmpeg dédié # Service FFmpeg dédié
ffmpeg-api: ffmpeg-api:
+89 -8090
View File
File diff suppressed because it is too large. Load diff
+4 -48
View File
@@ -4,22 +4,19 @@
"type": "module", "type": "module",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"dev": "NODE_ENV=development tsx server/index.ts", "dev": "vite",
"build": "vite build && esbuild server/index.ts --platform=node --packages=external --bundle --format=esm --outdir=dist", "dev:server": "cargo run --manifest-path server/Cargo.toml",
"start": "NODE_ENV=production node dist/index.js", "build": "vite build",
"start": "cargo run --release --manifest-path server/Cargo.toml",
"check": "tsc", "check": "tsc",
"db:push": "drizzle-kit push",
"test": "vitest run" "test": "vitest run"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.1016.0",
"@dnd-kit/core": "^6.3.1", "@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0", "@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2", "@dnd-kit/utilities": "^3.2.2",
"@fontsource/montserrat": "^5.3.0", "@fontsource/montserrat": "^5.3.0",
"@hookform/resolvers": "^3.10.0", "@hookform/resolvers": "^3.10.0",
"@jridgewell/trace-mapping": "^0.3.25",
"@neondatabase/serverless": "^0.10.4",
"@radix-ui/react-accordion": "^1.2.4", "@radix-ui/react-accordion": "^1.2.4",
"@radix-ui/react-alert-dialog": "^1.1.7", "@radix-ui/react-alert-dialog": "^1.1.7",
"@radix-ui/react-aspect-ratio": "^1.1.3", "@radix-ui/react-aspect-ratio": "^1.1.3",
@@ -47,46 +44,20 @@
"@radix-ui/react-toggle": "^1.1.3", "@radix-ui/react-toggle": "^1.1.3",
"@radix-ui/react-toggle-group": "^1.1.3", "@radix-ui/react-toggle-group": "^1.1.3",
"@radix-ui/react-tooltip": "^1.2.0", "@radix-ui/react-tooltip": "^1.2.0",
"@remotion/bundler": "^4.0.438",
"@remotion/cli": "^4.0.438",
"@remotion/player": "^4.0.438", "@remotion/player": "^4.0.438",
"@remotion/renderer": "^4.0.438",
"@tanstack/react-query": "^5.60.5", "@tanstack/react-query": "^5.60.5",
"@twemoji/api": "^17.0.2",
"@types/bcrypt": "^6.0.0",
"@types/multer": "^2.0.0",
"@types/node-cron": "^3.0.11",
"@types/node-fetch": "^2.6.13",
"@types/pg": "^8.15.5",
"bcrypt": "^6.0.0",
"canvas": "^2.11.2",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"cloudinary": "^2.7.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.1.1", "cmdk": "^1.1.1",
"connect-pg-simple": "^10.0.0",
"date-fns": "^3.6.0", "date-fns": "^3.6.0",
"drizzle-orm": "^0.39.1", "drizzle-orm": "^0.39.1",
"drizzle-zod": "^0.7.0", "drizzle-zod": "^0.7.0",
"embla-carousel-react": "^8.6.0", "embla-carousel-react": "^8.6.0",
"emoji-regex": "^10.6.0",
"express": "^4.21.2",
"express-rate-limit": "^7.5.1",
"express-session": "^1.18.1",
"framer-motion": "^11.13.1", "framer-motion": "^11.13.1",
"helmet": "^8.1.0",
"html-to-image": "^1.11.13", "html-to-image": "^1.11.13",
"html2canvas": "^1.4.1",
"input-otp": "^1.4.2", "input-otp": "^1.4.2",
"lucide-react": "^0.453.0", "lucide-react": "^0.453.0",
"memorystore": "^1.6.7",
"multer": "^2.0.2",
"music-metadata": "^11.12.3",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"node-cron": "^4.2.1",
"passport": "^0.7.0",
"passport-local": "^1.0.0",
"pg": "^8.16.3",
"react": "^18.3.1", "react": "^18.3.1",
"react-day-picker": "^8.10.1", "react-day-picker": "^8.10.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
@@ -97,15 +68,12 @@
"react-webcam": "^7.2.0", "react-webcam": "^7.2.0",
"recharts": "^2.15.2", "recharts": "^2.15.2",
"remotion": "^4.0.438", "remotion": "^4.0.438",
"sharp": "^0.34.4",
"tailwind-merge": "^2.6.0", "tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"tw-animate-css": "^1.2.5", "tw-animate-css": "^1.2.5",
"twemoji": "^14.0.2", "twemoji": "^14.0.2",
"twemoji-parser": "^14.0.0",
"vaul": "^1.1.2", "vaul": "^1.1.2",
"wouter": "^3.3.5", "wouter": "^3.3.5",
"ws": "^8.18.0",
"zod": "^3.24.2", "zod": "^3.24.2",
"zod-validation-error": "^3.4.0" "zod-validation-error": "^3.4.0"
}, },
@@ -115,27 +83,15 @@
"@replit/vite-plugin-runtime-error-modal": "^0.0.3", "@replit/vite-plugin-runtime-error-modal": "^0.0.3",
"@tailwindcss/typography": "^0.5.15", "@tailwindcss/typography": "^0.5.15",
"@tailwindcss/vite": "^4.1.3", "@tailwindcss/vite": "^4.1.3",
"@types/connect-pg-simple": "^7.0.3",
"@types/express": "4.17.21",
"@types/express-session": "^1.18.0",
"@types/node": "20.16.11", "@types/node": "20.16.11",
"@types/passport": "^1.0.16",
"@types/passport-local": "^1.0.38",
"@types/react": "^18.3.11", "@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^18.3.1",
"@types/ws": "^8.5.13",
"@vitejs/plugin-react": "^4.7.0", "@vitejs/plugin-react": "^4.7.0",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"drizzle-kit": "^0.31.4",
"esbuild": "^0.25.0",
"postcss": "^8.4.47", "postcss": "^8.4.47",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
"tsx": "^4.20.5",
"typescript": "5.6.3", "typescript": "5.6.3",
"vite": "^5.4.20", "vite": "^5.4.20",
"vitest": "^3.2.7" "vitest": "^3.2.7"
},
"optionalDependencies": {
"bufferutil": "^4.0.8"
} }
} }
+1 -1
View File
@@ -32,7 +32,7 @@ Preferred communication style: Simple, everyday language.
The frontend is built with React and TypeScript, leveraging Vite for bundling and Wouter for routing. State management is primarily handled by TanStack Query. UI components are constructed using Radix UI primitives and shadcn/ui, styled with Tailwind CSS, emphasizing a component-based, responsive, mobile-first design with real-time data synchronization. Dark mode is the default theme. The frontend is built with React and TypeScript, leveraging Vite for bundling and Wouter for routing. State management is primarily handled by TanStack Query. UI components are constructed using Radix UI primitives and shadcn/ui, styled with Tailwind CSS, emphasizing a component-based, responsive, mobile-first design with real-time data synchronization. Dark mode is the default theme.
### Backend Architecture ### Backend Architecture
The backend is developed in Node.js with Express.js, entirely in TypeScript. It adheres to a RESTful API design with dedicated endpoints for authentication, user management, AI content generation, media handling, social page management, and post scheduling. Multer is utilized for file uploads, integrated with Cloudinary. Node-cron manages background processes for scheduled post publication. The architecture incorporates a service layer, storage abstraction, and a middleware-based request processing pipeline. Session management is handled by `express-session` with `connect-pg-simple`. The backend is written in Rust (axum, tokio, sqlx) in the `server/` crate. It exposes the same RESTful API as the former Node/Express server (authentication, user management, AI content generation, media handling, social page management, post scheduling, external API). Uploads are stored locally under `uploads/`. Background tasks (scheduled publications, token checks, analytics, media purge, Reel render queue) run as tokio tasks. Sessions are stored in the PostgreSQL `session` table with a signed `connect.sid` cookie, compatible with the former server. The database schema is created and migrated automatically at startup (`server/src/migrate.rs`). Remotion Reel rendering runs in a separate Node sidecar (`remotion-service/`).
### Database Architecture ### Database Architecture
PostgreSQL serves as the database, interfaced using Drizzle ORM for type-safe operations and Neon serverless driver for WebSocket support. The schema includes tables for `users`, `cloudinary_config`, `social_pages`, `media`, `posts`, `post_media`, `scheduled_posts`, and `ai_generations`. Drizzle Kit is used for schema migrations. PostgreSQL serves as the database, interfaced using Drizzle ORM for type-safe operations and Neon serverless driver for WebSocket support. The schema includes tables for `users`, `cloudinary_config`, `social_pages`, `media`, `posts`, `post_media`, `scheduled_posts`, and `ai_generations`. Drizzle Kit is used for schema migrations.
+9
View File
@@ -24,6 +24,7 @@ use crate::state::AppState;
pub fn router(state: AppState) -> Router { pub fn router(state: AppState) -> Router {
let api = Router::new() let api = Router::new()
.route("/healthz", axum::routing::get(healthz))
.merge(auth::router()) .merge(auth::router())
.merge(core::router()) .merge(core::router())
.nest("/api/analytics", analytics::router()) .nest("/api/analytics", analytics::router())
@@ -56,6 +57,14 @@ pub fn router(state: AppState) -> Router {
.with_state(state) .with_state(state)
} }
/// Sonde de santé (Docker) : le serveur répond et la base est joignable.
async fn healthz(State(state): State<AppState>) -> Response {
match sqlx::query_scalar::<_, i32>("SELECT 1").fetch_one(&state.db.pool).await {
Ok(_) => (StatusCode::OK, "ok").into_response(),
Err(_) => (StatusCode::SERVICE_UNAVAILABLE, "database unavailable").into_response(),
}
}
/// Route inconnue : JSON pour `/api`, sinon fichiers publics puis l'application React. /// Route inconnue : JSON pour `/api`, sinon fichiers publics puis l'application React.
async fn fallback(State(state): State<AppState>, headers: HeaderMap, request: Request) -> Response { async fn fallback(State(state): State<AppState>, headers: HeaderMap, request: Request) -> Response {
let path = request.uri().path().to_string(); let path = request.uri().path().to_string();
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"include": ["client/src/**/*", "shared/**/*", "server/**/*"], "include": ["client/src/**/*", "shared/**/*"],
"exclude": ["node_modules", "build", "dist", "**/*.test.ts"], "exclude": ["node_modules", "build", "dist", "**/*.test.ts"],
"compilerOptions": { "compilerOptions": {
"incremental": true, "incremental": true,
+6
View File
@@ -32,6 +32,12 @@ export default defineConfig({
emptyOutDir: true, emptyOutDir: true,
}, },
server: { server: {
// En développement, l'API et les fichiers sont servis par le serveur Rust
// (`npm run dev:server`, port 5000 par défaut).
proxy: {
"/api": process.env.API_URL ?? "http://localhost:5000",
"/uploads": process.env.API_URL ?? "http://localhost:5000",
},
fs: { fs: {
strict: true, strict: true,
deny: ["**/.*"], deny: ["**/.*"],
+1 -1
View File
@@ -9,7 +9,7 @@ export default defineConfig({
}, },
}, },
test: { test: {
include: ["shared/**/*.test.ts", "server/**/*.test.ts"], include: ["shared/**/*.test.ts"],
environment: "node", environment: "node",
}, },
}); });