Répond à deux besoins : permettre à chaque foyer de choisir quelles
sources apparaissent dans ses onglets de recettes, et distinguer les
sources à API officielle des sources scrapées.
- RecipeSourceAdapter.official (booléen, sans défaut — chaque
adaptateur doit le déclarer explicitement) synchronisé sur
Source.official par syncRecipeSources.
- HouseSource : table de jointure opt-in (House <-> Source) — aucune
ligne = source masquée. Un foyer nouvellement créé ne voit aucune
source tant qu'il ne les active pas explicitement.
- GET /reference/sources (catalogue des sources implémentées, avec le
flag officiel).
- GET/PATCH /house/current/sources (lecture/remplacement complet des
sources activées par le foyer courant).
- recipe.service.ts : sourceVisibilityWhere() filtre désormais TOUS
les onglets (perso/foyer/publique/favoris) — une recette sans
source reste toujours visible ; une recette importée ne l'est que
si sa source est activée pour le foyer du viewer. Un viewer sans
foyer ne voit aucune recette sourcée.
Côté web :
- Nouvelle étape /onboarding/sources dans le wizard d'inscription,
atteinte uniquement si un foyer vient d'être créé/rejoint (sinon on
saute direct aux allergènes) ; s'auto-saute aussi si aucune source
n'est encore implémentée (catalogue vide aujourd'hui).
- Nouvelle section « Sources de recettes » dans /parametres/foyer
(masquée dans les mêmes conditions), avec sauvegarde à la volée
(même pattern que les autres préférences hot-saved).
- SourceSelect (features/house/), grille de cases à cocher avec badge
officiel/non-officielle, sur le même principe qu'AllergySelect.
172 tests backend passent (dont 25 nouveaux). Build et lint propres.
Vérifié manuellement en navigateur : le parcours d'onboarding saute
bien l'étape sources (catalogue vide) et affiche « 4 sur 4 » quand un
foyer a été créé ; la section paramètres reste invisible tant
qu'aucune source n'existe.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Répond au besoin identifié précédemment : la table `sources` devient
un vrai catalogue des sources implémentées, et une recette importée
pourra être reliée à l'item source dont elle provient.
- Source.key (unique) — même convention que Diet.key/Unit.key/
TechStep.key. Le catalogue est désormais synchronisé depuis le
registre d'adaptateurs (recipe-source-registry.ts) via
syncRecipeSources() (nouveau apps/api/src/db/recipe-source-sync.ts),
plutôt que maintenu à la main comme DIETS/UNITS — reste vide tant
qu'aucun adaptateur concret n'est enregistré.
- Recipe.externalId (nullable) — l'identifiant de la recette côté
source. Contrainte @@unique([sourceId, externalId]) : empêche
d'importer deux fois la même recette (les recettes manuelles, aux
deux colonnes nulles, ne sont jamais en conflit entre elles).
- findImportedExternalIds(prisma, sourceKey, externalIds) — le
pendant DB de markAlreadyImported (recipe-source-adapter.ts),
ferme la boucle commencée dans la PR précédente pour distinguer les
recettes déjà intégrées lors du browse.
- syncRecipeSources() appelé après seedReferenceData() dans
prisma/seed.ts et test-support/reset-db.ts.
Toujours pas de route HTTP ni de champ sourceId/externalId exposé
dans createRecipeSchema — la sauvegarde effective d'une recette
importée reste pour une PR ultérieure.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>