尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
Carnet de recettes construit avec Next.js et prêt à être déployé sur Vercel. Le test test:emulator utilise uniquement le projet de démonstration local demo-nico-firestore. Il vérifie les règles de sécurité (accès publics, écritures administrateur, refus, validation, limite d’envoi) puis exécute le vrai code de lib/ contre l’émulateur. Les mêmes commandes tournent dans GitHub Actions (.github/workflows/ci.yml). Pour faire tourner le site contre l’émulateur, définissez FIRESTORE_EMULATOR_HOST=127.0.0.1:8085 et NEXT_PUBLIC_FIREBASE_PROJECT_ID=demo-nico-firestore.檢視完整 README ↗
Carnet de recettes construit avec Next.js et prêt à être déployé sur Vercel.
npm install
npm run dev
npm run lint
npm run typecheck
npm test # tests unitaires
npm run test:emulator # règles Firestore et accès Firestore, dans l’émulateur local (Java requis)
Le test test:emulator utilise uniquement le projet de démonstration local demo-nico-firestore. Il vérifie les règles de sécurité (accès publics, écritures administrateur, refus, validation, limite d’envoi) puis exécute le vrai code de lib/ contre l’émulateur. Les mêmes commandes tournent dans GitHub Actions (.github/workflows/ci.yml).
Pour faire tourner le site contre l’émulateur, définissez FIRESTORE_EMULATOR_HOST=127.0.0.1:8085 et NEXT_PUBLIC_FIREBASE_PROJECT_ID=demo-nico-firestore.
/ : le carnet, rendu côté serveur avec les recettes déjà dans le HTML. Sans filtre, il présente six recettes par moment (petit-déjeuner, midi, soir, enfants, desserts…) ; les filtres sont dans l’adresse (/?moment=desserts&temps=30&q=citron), si bien que le bouton retour les restaure et qu’une vue filtrée se partage par un lien. Les moments et le filtre de temps sont définis dans lib/recipe-browse.ts./recettes/<id> : une page par recette, avec ses métadonnées de partage (Open Graph) et ses données structurées Recipe (durée totale, date de publication, fil d’Ariane)./categories/<moment> : une page par moment, qui liste toutes ses recettes. Son titre et son texte de présentation sont dans app/categories/[id]/page.tsx ; le pied de page y renvoie depuis tout le site./sitemap.xml et /robots.txt sont générés automatiquement. L’adresse publique vient de NEXT_PUBLIC_SITE_URL, ou à défaut du domaine de production Vercel.Les favoris sont conservés dans le navigateur du visiteur. Dès qu’une recette est en favori, le bouton « Liste de courses » réunit les ingrédients des recettes favorites en additionnant les quantités d’un même ingrédient (100 g + 150 g + 50 g de farine deviennent 300 g de farine). On coche ce qu’on a déjà, et « Copier la liste » copie ce qui reste à acheter.
Les ingrédients étant du texte libre, l’addition repose sur leur lecture (lib/shopping-list.ts) : une quantité, une unité éventuelle, puis le nom. Les unités d’une même famille sont converties (g et kg, cl et l, cuillères à café et à soupe) ; les autres restent côte à côte (« 250 g + 3 pots de farine »). Les lignes sans quantité (« Sel, poivre ») sont listées à part.
En dessous de 760 px de large, le site change de forme plutôt que de se tasser : navigation dans une barre en bas de l’écran, un carrousel par moment sur l’accueil, résultats en lignes compactes, filtres dans un volet, recette en deux onglets, liste de courses en plein écran. Le bouton « Cuisiner pas à pas » ouvre le mode cuisine (components/cooking-mode.tsx) : une étape par écran, les ingrédients de l’étape, un minuteur quand elle demande d’attendre, et l’écran maintenu allumé là où le navigateur le permet.
Le site s’installe sur l’écran d’accueil (app/manifest.ts). Le service worker public/sw.js garde lisibles sans réseau les pages déjà visitées et les recettes mises en favori ; en ligne, le réseau passe toujours en premier, donc rien d’affiché n’est périmé.
Chaque recette peut avoir une photo public/photos/<identifiant>.jpg (900 × 600, paysage). Une recette sans photo affiche son émoji. Après avoir ajouté, remplacé ou retiré une photo, lancez npm run photos : la commande crée la copie allégée servie aux téléphones (public/photos/sm/, 480 px) et met à jour la liste lib/recipe-photos.ts. Commitez le tout.
Les recettes sont stockées dans Cloud Firestore Standard, collection recipes, base (default) du projet lesrecettesdenico-51466, région Paris — europe-west9. Le projet reste sur le forfait Spark, sans facturation activée.
Les neuf recettes Notion ont été importées avec leurs identifiants d’origine. Le site lit exclusivement Firestore : le fichier lib/notion-recipes.ts est conservé comme archive d’import, sans servir de données de secours à l’application. Aucune variable DATABASE_URL n’est nécessaire.
Les lectures passent par un cache serveur de 60 secondes, partagé par les pages et l’API, et invalidé après chaque écriture. La recherche et les favoris ne produisent pas de lectures Firestore supplémentaires. Le quota gratuit Standard inclut 1 Gio de stockage, 50 000 lectures et 20 000 écritures par jour ; sur Spark, un dépassement limite le service sans activer une facturation. Voir https://firebase.google.com/docs/firestore/quotas.
Les règles firestore.rules autorisent la lecture publique des recettes, et leur création, modification et suppression uniquement avec le compte Google vérifié [email protected]. Les textes sont bornés et la date de création est immuable.
Les propositions de la communauté (recipeSubmissions) sont limitées à 5 par compte Google et par 24 heures. La limite est appliquée par les règles elles-mêmes, grâce au compteur submissionLimits/<uid> écrit dans la même opération : elle vaut donc aussi pour une écriture directe dans Firestore. Une proposition validée devient une recette et une proposition refusée est supprimée ; dans les deux cas elle disparaît de recipeSubmissions.
Après toute modification de firestore.rules, déployez les règles :
npx -y firebase-tools@latest deploy --only firestore --project lesrecettesdenico-51466
La lecture du carnet est publique. Le serveur Vercel vérifie chaque jeton Firebase à partir des certificats publics Google, puis le transmet à Firestore, qui applique ses règles. Aucun compte de service ni clé privée n’est nécessaire.
L’administrateur est défini à un seul endroit : la fonction isAdmin() de firestore.rules. Pour le changer, modifiez cette adresse et redéployez les règles. L’ancienne variable ADMIN_EMAILS n’est plus lue et peut être retirée de Vercel.
Depuis le site, l’administrateur peut ajouter, modifier et supprimer une recette, cocher « Le choix de Nico » et valider ou refuser les propositions. Chaque recette peut porter une étiquette libre (par exemple deNico), affichée sur sa carte et proposée comme filtre dans le carnet.
localhost et votre domaine Vercel dans Authorized domains.NEXT_PUBLIC_FIREBASE_* sont celles de l’application Web Firebase.NEXT_PUBLIC_FIREBASE_API_KEY=
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=
NEXT_PUBLIC_FIREBASE_PROJECT_ID=
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
NEXT_PUBLIC_FIREBASE_APP_ID=
# Facultatif : active Firebase Analytics (avec consentement).
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=
# Facultatif : adresse publique du site, si elle diffère du domaine de production Vercel.
NEXT_PUBLIC_SITE_URL=
Chaque page reçoit une Content-Security-Policy avec un nonce propre à la requête (proxy.ts) : seuls les scripts du site, et ceux qu’ils chargent eux-mêmes (connexion Google, Analytics), peuvent s’exécuter. Les autres en-têtes de sécurité sont dans next.config.ts.
Connectez le dépôt GitHub à Vercel. La configuration utilise automatiquement Next.js et la commande npm run build.