尚未检测到更新。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.