Backend: - Remove malicious crypto dep; use node:crypto - Add helmet + rate-limit (100 req/min) - CORS whitelist via CORS_ORIGINS env - Validate required env vars on boot (fail fast) - Health endpoint + clean shutdown (SIGINT/SIGTERM) - Multipart limits (15MB / 1 file) - Fix findUnique composite where bug (use findFirst) - Wrap JSON.parse(generatedRecipe) in try/catch - Isolate DALL-E best-effort; ENABLE_IMAGE_GENERATION toggle - Lazy MinIO client, safe TLS handling - Uniform fastify.hashPassword/comparePassword - Proper audio cleanup on delete - ESLint flat config, Prettier, .env.example, .editorconfig Frontend: - Delete 10 orphan/duplicate components - Remove orphan pages/recipe/, data/recipes.ts, root src/ - Fix /reset-password route order (was unreachable) - Remove unused ky dep Docs: - README rewritten to match real routes and env vars Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
118 lines
4.2 KiB
Markdown
118 lines
4.2 KiB
Markdown
# Freedge
|
|
|
|
Freedge génère des recettes personnalisées à partir des ingrédients dictés à l'oral. Le son est transcrit (Whisper), une recette est générée (GPT-4o-mini) et une image optionnelle est produite (DALL-E 3).
|
|
|
|
## Stack
|
|
|
|
- **Frontend** : React 19 + Vite + TailwindCSS + ShadCN/UI + React Router
|
|
- **Backend** : Fastify 4 + Prisma 5 + SQLite
|
|
- **IA** : OpenAI (Whisper, GPT-4o-mini, DALL-E 3)
|
|
- **Stockage** : MinIO (S3-compatible) avec fallback local
|
|
- **Paiement** : Stripe (client créé à l'inscription — intégration abonnement à finaliser)
|
|
- **Auth** : JWT + Google OAuth
|
|
|
|
## Structure
|
|
|
|
```
|
|
freedge/
|
|
├── backend/
|
|
│ ├── prisma/ # Schéma + migrations SQLite
|
|
│ └── src/
|
|
│ ├── plugins/ # auth, ai, stripe, google-auth
|
|
│ ├── routes/ # auth, recipes, users
|
|
│ ├── utils/ # env, storage (MinIO), email, resend
|
|
│ └── server.js
|
|
└── frontend/
|
|
└── src/
|
|
├── api/ # Clients HTTP (auth, user, recipe)
|
|
├── components/ # UI shadcn + composants métier
|
|
├── hooks/ # useAuth, useMobile, useAudioRecorder
|
|
├── layouts/ # MainLayout
|
|
└── pages/ # Home, Auth/*, Recipes/*, Profile, ResetPassword
|
|
```
|
|
|
|
## Prérequis
|
|
|
|
- Node.js ≥ 18
|
|
- pnpm (recommandé) ou npm
|
|
- Une clé API OpenAI
|
|
- (Optionnel) Un serveur MinIO pour le stockage images/audio
|
|
- (Optionnel) Un compte Resend pour les emails
|
|
|
|
## Démarrage
|
|
|
|
```bash
|
|
# Installation
|
|
cd backend && npm install
|
|
cd ../frontend && pnpm install
|
|
|
|
# Variables d'environnement backend (.env dans backend/)
|
|
cp .env.example .env # puis éditer
|
|
# Requis : DATABASE_URL, JWT_SECRET, OPENAI_API_KEY
|
|
|
|
# Base de données
|
|
cd backend
|
|
npx prisma migrate dev
|
|
npx prisma generate
|
|
|
|
# Lancement
|
|
npm run dev # backend sur :3000
|
|
cd ../frontend && pnpm dev # frontend sur :5173
|
|
```
|
|
|
|
## Variables d'environnement backend
|
|
|
|
| Variable | Requis | Description |
|
|
|---|---|---|
|
|
| `DATABASE_URL` | ✅ | URL Prisma (ex: `file:./prisma/dev.db`) |
|
|
| `JWT_SECRET` | ✅ | Clé JWT (≥ 32 caractères recommandé) |
|
|
| `OPENAI_API_KEY` | ✅ | Clé API OpenAI |
|
|
| `PORT` | ❌ | Port du serveur (défaut 3000) |
|
|
| `CORS_ORIGINS` | ❌ | Origines autorisées, séparées par virgule |
|
|
| `FRONTEND_URL` | ❌ | URL frontend pour les emails de reset |
|
|
| `ENABLE_IMAGE_GENERATION` | ❌ | `false` pour désactiver DALL-E |
|
|
| `OPENAI_TEXT_MODEL` | ❌ | Défaut `gpt-4o-mini` |
|
|
| `STRIPE_SECRET_KEY` | ❌ | Clé Stripe (pour créer les customers) |
|
|
| `MINIO_ENDPOINT` / `MINIO_PORT` / `MINIO_USE_SSL` / `MINIO_ACCESS_KEY` / `MINIO_SECRET_KEY` / `MINIO_BUCKET` | ❌ | Config MinIO ; fallback local sinon |
|
|
| `MINIO_ALLOW_SELF_SIGNED` | ❌ | `true` pour autoriser TLS auto-signé (DEV uniquement) |
|
|
| `RESEND_API_KEY` | ❌ | Clé Resend pour les emails |
|
|
|
|
## Routes API (préfixe `/`)
|
|
|
|
### Auth (`/auth`)
|
|
- `POST /auth/register` — Inscription email + mot de passe
|
|
- `POST /auth/login` — Connexion
|
|
- `POST /auth/google-auth` — Connexion/inscription via Google OAuth
|
|
|
|
### Utilisateurs (`/users`)
|
|
- `GET /users/profile` — Profil courant (🔒)
|
|
- `PUT /users/profile` — Mise à jour du nom (🔒)
|
|
- `PUT /users/change-password` — Changement de mot de passe (🔒)
|
|
- `PUT /users/change-email` — Changement d'email (🔒)
|
|
- `POST /users/forgot-password` — Demande de réinitialisation
|
|
- `POST /users/reset-password` — Réinitialisation avec token
|
|
- `DELETE /users/account` — Suppression du compte (🔒)
|
|
|
|
### Recettes (`/recipes`) — toutes 🔒
|
|
- `POST /recipes/create` — Upload audio + transcription + génération
|
|
- `GET /recipes/list` — Liste les recettes de l'utilisateur
|
|
- `GET /recipes/:id` — Détail d'une recette
|
|
- `DELETE /recipes/:id` — Supprime une recette
|
|
|
|
### Divers
|
|
- `GET /health` — Healthcheck
|
|
|
|
🔒 = nécessite un JWT `Authorization: Bearer <token>`
|
|
|
|
## Sécurité
|
|
|
|
- Helmet + rate-limit (100 req/min) activés
|
|
- CORS whitelisté via `CORS_ORIGINS`
|
|
- JWT signé, expiration 7 jours
|
|
- Bcrypt (10 rounds) pour les mots de passe
|
|
- Validation des variables d'environnement au démarrage
|
|
|
|
## Licence
|
|
|
|
MIT
|