CI / build-and-test (push) Successful in 14m43s
7.3: capturedAt/source au registre + 6 adaptateurs + module provenance.ts + ?debug=1 (search-transport.mjs). 7.4: ProviderHealthService + badge source degradee. 7.6: squelettes par provider + snapshots progressifs + transport NDJSON /api/search. 8.1: ProviderAdapter unifie (search enveloppe + channelContent/channelMeta/capabilities) via getProviderAdapter + test de contrat offline.
98 lines
3.6 KiB
JavaScript
98 lines
3.6 KiB
JavaScript
/**
|
|
* Phase 8.3 — Feature flags par provider (`FF_<PROVIDER>`).
|
|
*
|
|
* Motivation : certain upstreams sont fragiles hors de notre contrôle. Rumble
|
|
* est derrière Cloudflare et peut casser du jour au lendemain ; Odysee dépend
|
|
* d'un backend NA. Désactiver un provider ne doit pas exiger un redéploiement
|
|
* ni un revert de code.
|
|
*
|
|
* Trois états, pas deux — c'est la distinction qui compte en exploitation :
|
|
* - unset : le provider est actif (comportement historique) ;
|
|
* - truthy : actif ;
|
|
* - falsy : DÉSACTIVÉ, exclu du fan-out et signalé `errors.<id> = disabled_by_ff`.
|
|
*
|
|
* Un flag falsy **signale** au lieu de disparaître en silence : une colonne vide
|
|
* sans explication ressemble à « aucune correspondance », ce qui envoie l'utilisateur
|
|
* (et le support) chercher au mauvais endroit.
|
|
*
|
|
* Convention de nommage : `FF_YT`, `FF_DM`, `FF_TW`, `FF_PT`, `FF_OD`, `FF_RU`
|
|
* (ids courts, cf. `provider-ids.ts`).
|
|
*/
|
|
|
|
/** Valeurs explicitement fausses. Tout le reste est vrai (unset inclus). */
|
|
const FALSY = new Set(['0', 'false', 'off', 'no', 'disabled']);
|
|
|
|
/**
|
|
* Lit un flag depuis l'environnement, sans cache : le lire à chaque appel permet
|
|
* de changer une variable sans redémarrer (utile en local, et ça évite un cache
|
|
* qui masquerait un changement en CI).
|
|
*
|
|
* @param {string} key
|
|
* @returns {{ set: boolean, enabled: boolean, raw: string|null }}
|
|
*/
|
|
function readFlag(key) {
|
|
const raw = process.env[key];
|
|
if (raw === undefined || raw === null || String(raw).trim() === '') {
|
|
// Unset = actif, et on le dit explicitement pour ne pas confondre
|
|
// « pas configuré » et « désactivé » dans les logs.
|
|
return { set: false, enabled: true, raw: null };
|
|
}
|
|
return { set: true, enabled: !FALSY.has(String(raw).trim().toLowerCase()), raw: String(raw) };
|
|
}
|
|
|
|
/**
|
|
* État d'un provider au regard des feature flags.
|
|
* @param {string} providerId id court ('yt', 'ru', …)
|
|
*/
|
|
export function providerFlag(providerId) {
|
|
const id = String(providerId || '').trim().toLowerCase();
|
|
const key = `FF_${id.toUpperCase()}`;
|
|
const { set, enabled, raw } = readFlag(key);
|
|
return { provider: id, flag: key, set, enabled, raw };
|
|
}
|
|
|
|
/** `true` si le provider est désactivé par un feature flag. */
|
|
export function isProviderDisabled(providerId) {
|
|
return !providerFlag(providerId).enabled;
|
|
}
|
|
|
|
/**
|
|
* Filtre une liste de providers, en retirant les désactivés.
|
|
*
|
|
* @template {string} T
|
|
* @param {T[]} providerIds
|
|
* @returns {{ enabled: T[], disabled: Array<{provider: string, flag: string}> }}
|
|
*/
|
|
export function partitionEnabledProviders(providerIds) {
|
|
const list = Array.isArray(providerIds) ? providerIds : [];
|
|
const enabled = [];
|
|
const disabled = [];
|
|
for (const id of list) {
|
|
const flag = providerFlag(id);
|
|
if (flag.enabled) enabled.push(id);
|
|
else disabled.push({ provider: flag.provider, flag: flag.flag });
|
|
}
|
|
return { enabled, disabled };
|
|
}
|
|
|
|
/**
|
|
* Applique les flags à une liste déjà validée, et renvoie les `errors` à fusionner
|
|
* dans la réponse de fan-out. Utilisé par `/api/search` ET `/api/suggest` pour
|
|
* qu'un provider désactivé se comporte pareil partout.
|
|
*
|
|
* @template {string} T
|
|
* @param {T[]} providerIds
|
|
* @returns {{ providerIds: T[], errors: Record<string, {message: string, code: string}> }}
|
|
*/
|
|
export function applyProviderFlags(providerIds) {
|
|
const { enabled, disabled } = partitionEnabledProviders(providerIds);
|
|
const errors = {};
|
|
for (const d of disabled) {
|
|
errors[d.provider] = {
|
|
message: `Provider désactivé par le feature flag ${d.flag}`,
|
|
code: 'disabled_by_ff',
|
|
};
|
|
}
|
|
return { providerIds: enabled, errors };
|
|
}
|