正在加载完整 README。 以下为已保存的摘录。
Application Android de suivi calorique par photo, pensée pour l'Afrique francophone (marché de départ : Bénin / Cotonou). On prend son plat en photo, on peut dire ce qu'il contient, l'IA identifie les éléments et les portions, et l'app calcule les calories avec une table de plats locaux (attiéké, pâte de maïs, sauce graine, amiwo, poisson braisé…), pas avec des chiffres inventés par le modèle.查看完整 README ↗
Application Android de suivi calorique par photo, pensée pour l'Afrique francophone (marché de départ : Bénin / Cotonou). On prend son plat en photo, on peut dire ce qu'il contient, l'IA identifie les éléments et les portions, et l'app calcule les calories avec une table de plats locaux (attiéké, pâte de maïs, sauce graine, amiwo, poisson braisé…), pas avec des chiffres inventés par le modèle.
gemini-flash-lite-latest par défaut, modèle léger de l'offre gratuite), appelé uniquement depuis une Edge FunctionMise en route, secrets et build de l'APK :
docs/SETUP.md(liste de contrôle de tout ce qui demande tes identifiants, puis pas à pas). Configuration de Supabase sans terminal, uniquement dans le navigateur :docs/SUPABASE_TABLEAU_DE_BORD.md.
docs/PAIEMENTS_MAKETOU.md).runtimeVersion en politique fingerprint) change, et les APK
antérieurs cessent proprement de recevoir ces mises à jour.Téléphone (APK) Supabase
┌───────────────────────────────┐ JWT ┌──────────────────────────────────────┐
│ Expo Router · écrans │ ───────▶ │ Edge Function analyze-meal │
│ SQLite : journal local + file │ │ ├ vérifie le jeton, consomme le quota│
│ Cache : table des plats │ │ ├ charge foods, appelle Gemini ─────┼─▶ Gemini
│ Calcul kcal/macros (foods) │ │ └ valide le JSON, journalise tokens │ (clé dans
│ Clé anon Supabase seulement │ ◀─────── │ Postgres + RLS : profils, repas, │ les secrets)
└───────────────────────────────┘ synchro │ éléments, corrections, quota… │
│ Edge Function delete-account │
└──────────────────────────────────────┘
analyze-meal avec le jeton de l'utilisateur.foods et appelle Gemini en décrivant
la forme JSON attendue ; food_key doit être un plat connu (ou autre), sinon la réponse est rejetée.GEMINI_FALLBACK_MODELS).foods (src/lib/nutrition.ts). Les valeurs de l'IA ne servent
que pour un élément autre, affiché « estimé ».src/lib/meals.ts) puis envoyés à Supabase au retour du réseau
(identifiants générés par l'app : aucune duplication si la synchro est rejouée). Les 35 derniers jours
sont rapatriés depuis le serveur (nouveau téléphone), sans écraser un repas local pas encore envoyé.| Où | Variable | Contenu |
|---|---|---|
App (.env en local, EAS en build) | EXPO_PUBLIC_SUPABASE_URL | URL du projet Supabase (publique) |
EXPO_PUBLIC_SUPABASE_ANON_KEY | clé anon / publishable (publique, sécurité assurée par la RLS) | |
Secrets Supabase (supabase/functions/.env) | AI_PROVIDER | gemini par défaut (facturation Google directe) ; gateway pour passer par une passerelle à crédit prépayé |
GEMINI_API_KEY | clé Gemini — jamais dans l'app | |
GEMINI_MODEL | défaut gemini-flash-lite-latest, modifiable sans republier l'app | |
GEMINI_FALLBACK_MODELS | défaut gemini-flash-lite-latest,gemini-flash-latest : secours si le modèle principal est saturé ou à court de quota ; none pour désactiver | |
GEMINI_STRUCTURED | false par défaut : requête simplifiée (forme JSON décrite dans le prompt) ; true impose schéma, température et réflexion | |
GEMINI_TEMPERATURE | avec GEMINI_STRUCTURED=true : défaut 0.3 ; default = valeur du modèle | |
GEMINI_THINKING_LEVEL | avec GEMINI_STRUCTURED=true : défaut low (coût et latence) | |
GATEWAY_API_KEY | avec AI_PROVIDER=gateway : clé de la passerelle — jamais dans l'app | |
GATEWAY_API_BASE | défaut https://api.rodiumai.io/v1 (RodiumAi, rechargeable par Mobile Money) ; toute passerelle compatible OpenAI convient | |
GATEWAY_MODEL | défaut google/gemini-2.5-flash-lite ; accepte aussi les modèles d'autres fournisseurs (anthropic/…, openai/…) sans changer le code | |
GATEWAY_FALLBACK_MODELS | modèles de secours séparés par des virgules ; défaut |
npm install
cp .env.example .env # remplir l'URL et la clé anon Supabase
npm start # serveur Expo (Expo Go ou build de développement)
| Commande | Rôle |
|---|---|
npm run typecheck | TypeScript strict |
npm run lint | ESLint (config Expo + React Compiler) |
npm test | tests : calories, portions, corrections, journées, SQL local, logique des Edge Functions |
npm run db:seed | valide supabase/seed/foods.json (cohérence kcal/macros) et régénère supabase/seed.sql |
npm run check:secrets | vérifie qu'aucune clé secrète n'est dans le bundle de l'app |
scripts/test-db.sh | migrations + seed + tests RLS sur un PostgreSQL vide (DATABASE_URL) |
scripts/test-analyze-meal.sh photo.jpg "indice" | appelle la fonction d'analyse déployée avec curl |
scripts/build-dashboard-files.sh | régénère supabase/dashboard/ (SQL unique + fonctions en un fichier, pour le tableau de bord) |
Edge Functions (Deno 2) : deno check, deno lint et deno test dans supabase/functions.
La CI GitHub (.github/workflows/ci.yml) lance tout cela à chaque push.
Le nom de l'app se change uniquement dans src/brand.json. L'identifiant Android com.calbasse.app
(app.config.ts) ne doit plus changer une fois l'app publiée.
npx eas-cli@latest login
npx eas-cli@latest init # puis coller le projectId dans EAS_PROJECT_ID (app.config.ts)
npx eas-cli@latest env:set --environment preview --visibility plaintext --name EXPO_PUBLIC_SUPABASE_URL --value https://<ref>.supabase.co
npx eas-cli@latest env:set --environment preview --visibility plaintext --name EXPO_PUBLIC_SUPABASE_ANON_KEY --value <clé anon>
npx eas-cli@latest build -p android --profile preview
Le lien de téléchargement de l'APK s'affiche à la fin du build et reste sur expo.dev (Projects > calbasse > Builds).
Détails, profil production, versions et recette : docs/SETUP.md, étapes 6 et 7.
Permissions Android demandées : caméra uniquement (plus le stockage, limité à Android 12 et moins, pour la galerie) ; micro, superposition et vibreur sont retirés du manifeste. Android 7.0 minimum.
src/
app/ routes Expo Router (un fichier = un écran)
(auth)/ accueil, connexion, inscription, code
(onboarding)/ objectif, infos corporelles, cible
(tabs)/ Journal, Scanner, Historique, Profil
result.tsx résultat d'un scan · item-editor · food-picker
meal/[id].tsx détail d'un repas · day/[day].tsx détail d'une journée
profile-edit.tsx modification du profil · link-account.tsx invité → compte
components/ composants d'interface (boutons, anneau, graphique…)
i18n/ textes (fr.ts) et fonction t() typée
lib/ Supabase, calculs purs testés, journal local, synchro, analyse
state/ session, brouillon d'onboarding, scan en cours
brand.json nom de l'app
supabase/
migrations/ schéma SQL versionné (RLS partout)
seed/foods.json table des plats (source) · seed.sql (généré)
functions/ analyze-meal, delete-account, logique partagée (_shared)
templates/ e-mails avec code de confirmation
tests/ tests RLS/quota + imitation Supabase pour PostgreSQL nu
site/ page de téléchargement et confidentialité, publiées sur Netlify
(branche main, dossier de base « site ») ; /apk redirige vers le dernier APK
docs/
SETUP.md mise en route, secrets, build APK, recette
SUPABASE_TABLEAU_DE_BORD.md configuration de Supabase depuis le navigateur
FOODS_TODO.md valeurs nutritionnelles à faire vérifier (FAO)
verified = false),
à contrôler avec la table FAO de l'Afrique de l'Ouest (docs/FOODS_TODO.md).src/i18n).Hors périmètre de ce MVP : paiement mobile money, coach IA, suivi d'entraînement, code-barres, iOS, web.
noneSTORE_PHOTOS | false par défaut ; true conserve les photos des utilisateurs consentants |
MAKETOU_API_KEY, MAKETOU_PRODUCT_MONTHLY, MAKETOU_PRODUCT_YEARLY, PREMIUM_PRICE_* | paiements Premium (docs/PAIEMENTS_MAKETOU.md) — jamais dans l'app |
| Fournies par Supabase | SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY | utilisées par les Edge Functions seulement |