10 Commits

Author SHA1 Message Date
ordinarthur
b0e9425ed5 feat(stripe): on-demand sync fallback when webhooks are missed
Problem: if stripe listen is not running (dev) or the webhook secret is
misconfigured, a successful checkout leaves the user stuck on the free
plan in the DB even though Stripe knows they're subscribed.

Solution: 3 recovery mechanisms.

1. Backend: POST /stripe/sync (auth required)
   Fetches the current user's subscriptions from Stripe by customer ID,
   picks the most recent active/trialing/past_due one, and applies it to
   the User row via the same applySubscriptionToUser helper used by the
   webhook. If no active sub exists, downgrades to free. Returns the
   current plan state.

2. Frontend: CheckoutSuccess now calls /stripe/sync first (instant,
   reliable) before falling back to polling /stripe/subscription. This
   fixes the 'just paid but still free' bug even with no webhook setup.

3. Frontend: 'Rafraîchir' button on the Profile free-plan upgrade banner
   (ghost style with RefreshCw spinning icon). Tooltip hints at its
   purpose. Users who paid but see the free state can click it to
   self-heal in one click.

4. Backend script: scripts/sync-subscription.ts
   - npm run stripe:sync -- user@example.com  (sync one user by email)
   - npm run stripe:sync -- --all             (sync every user with a
                                                stripeId, useful after
                                                a prod webhook outage)
   Colored output with ✓ / ✗ / ↷ status per user.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:07:02 +02:00
ordinarthur
64d2bf4506 feat(stripe): idempotent setup script that provisions products + prices
New script backend/scripts/setup-stripe.ts that:
- Reads STRIPE_SECRET_KEY from .env
- Detects test vs live mode and warns + 5s delay for live
- For each plan (Essentiel 3EUR/mo, Premium 5EUR/mo):
  - Looks up existing price by lookup_key (freedge_essential_monthly,
    freedge_premium_monthly) — idempotent, safe to re-run
  - If missing, creates the product then the recurring price with the
    lookup_key and nickname for clarity
- Prints the resulting price IDs with their env var names
- With --write-env flag, automatically upserts the values into
  backend/.env preserving other lines
- Points to Customer Portal settings and stripe listen command as
  next steps

npm scripts added:
- npm run stripe:setup        # dry run, just print IDs
- npm run stripe:setup:write  # update .env automatically
- npm run stripe:listen       # shortcut for stripe CLI webhook forward

Updated README to show the script as the recommended path for step 1,
keeping the manual dashboard instructions as a fallback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:58:26 +02:00
ordinarthur
64df5db077 fix(ai): robust image pipeline + local MinIO stack + TS errors fix
Image generation:
- Automatic model fallback: try gpt-image-1 first, fall back to
  dall-e-3 if it fails (e.g. org not verified on OpenAI)
- Local filesystem fallback: if MinIO upload fails, write the image
  to backend/uploads/recipes/ and return a URL served by fastify-static
- Unified handling of base64 vs URL responses from the Images API
- DALL-E quality mapped automatically (low/medium/high -> standard)

Local MinIO stack:
- docker-compose.yml at repo root with minio + minio-init service
  that auto-creates the bucket and makes it publicly readable
- Default credentials: freedge / freedge123 (configurable)
- Console at :9001, API at :9000
- .env.example now points to the local stack by default

Static file serving:
- Register @fastify/static to serve ./uploads at /uploads/*
- Enables local fallback to return usable URLs to the frontend
- New PUBLIC_BASE_URL env var to build absolute URLs

TypeScript errors (21 -> 0):
- JWT typing via '@fastify/jwt' module augmentation (FastifyJWT
  interface with payload + user) fixes all request.user.id errors
- Stripe constructor now passes required StripeConfig
- fastify.createCustomer guards checked on the helper itself for
  proper TS narrowing (not on fastify.stripe)
- Remove 'done' arg from async onClose hook
- MinIO transport.agent + listFiles return type cast ignored

README:
- Add 'Stockage des fichiers' section explaining the two modes
- Updated setup instructions to start with docker compose up

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:30:47 +02:00
ordinarthur
fc3dfe83c9 refactor(backend): migrate from JavaScript to TypeScript
- tsconfig.json strict mode (noImplicitAny, strictNullChecks, noUnused*)
- Replace nodemon with tsx (watch + run TS directly)
- Build script (tsc -> dist/) and typecheck script
- Fastify decorator types in types/fastify.d.ts (prisma, openai,
  stripe, googleClient, auth helpers, ai helpers, request.user)
- Typed route handlers with generic Body/Params
- Strict null checks on Prisma results and env vars
- Stripe plugin now optional (no-op if STRIPE_SECRET_KEY missing)
- Delete dead utils/errors.js (empty) and utils/resend.js
  (contained a hardcoded Resend API key, unused)
- Add @types/bcrypt, @types/nodemailer, typescript-eslint
- ESLint upgraded to typescript-eslint flat config
- deploy.sh: run prisma generate, migrate deploy, backend build

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 22:36:21 +02:00
ordinarthur
0134390f5e refactor: cleanup, security hardening & frontend dedup
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>
2026-04-07 21:00:54 +02:00
Arthur Barre
8b6d76fdea add minio storage 2025-03-15 13:11:24 +01:00
Arthur Barre
b311e76bf3 add reset password from mail 2025-03-13 23:03:29 +01:00
Arthur Barre
7c2374f0b3 change mail + change password ok ! 2025-03-13 22:31:10 +01:00
Arthur Barre
191b8a4da2 add google sso 2025-03-11 13:05:13 +01:00
Arthur Barre
958a778f85 init a fonctional prototype 2025-03-10 00:24:26 +01:00