feat(api): production-ready P0/P1 — secrets hors navigateur, clés d'API, métriques
CI / build-and-test (push) Successful in 14m57s
CI / build-and-test (push) Successful in 14m57s
P0 - Aucun secret servi au navigateur : /assets/config.local.js est généré par le serveur AVANT les montages statiques (il l'emporte sur le fichier local) et ne contient plus YOUTUBE_API_KEY(S) ; le fichier local n'est plus copié dans l'image Docker ni embarqué dans dist (angular ignore config.local.js) ; youtube-api.service n'appelle plus googleapis directement (fetchYouTube = /api/yt -> /proxy/api/yt), gardes « pas de clé => écran vide », rotation et carte de bans côté client supprimées ; readiness YouTube via /healthz (youtube.keys.count) ; messages d'erreur orientés configuration serveur. - CORS : origines pilotées par API_ALLOWED_ORIGINS (CSV), méthode PATCH ajoutée (requis par /user/preferences), header X-API-Key accepté. - Clés d'API longue durée : table api_keys (empreinte SHA-256 + préfixe affichable), routes GET/POST /api/keys et DELETE /api/keys/:id, jeton ntk_… affiché une seule fois, last_used_at à chaque usage ; X-API-Key accepté par authMiddleware ET authMiddlewareCookieAware. P1 - X-Request-Id renvoyé sur chaque réponse + journal JSON structuré en prod (ts, reqId, method, route, status, ms). - GET /metrics : exposition Prometheus sans dépendance (http_requests_total par route/code, somme+nombre de durées, uptime/mémoire ; METRICS_TOKEN verrouille l'accès si défini). - Rate-limit sur /api/details (DETAILS_RATE_LIMIT, 60/min, réponse JSON) — /transcript avait déjà le sien. - Version unique package.json : menu du compte, info.version de l'OpenAPI (+ schéma apiKeyAuth et chemins /keys / /metrics documentés). Tests : api_coverage +7 cas « production-ready » (sentinel de fuite de clé, X-Request-Id, /metrics, CORS PATCH, contrat/version, cycle complet des clés d'API), suggest, transcript, flags, filters, kind — verts. Build OK. Vérifs instance locale : config servie sans secret, /api/yt 200 avec la clé serveur, /metrics alimenté, menu 1.0.59 et 40 cartes rendues sans clé côté client.
This commit is contained in:
@@ -161,7 +161,7 @@ Application : [http://localhost:8080](http://localhost:8080) (mappage `8080:4000
|
||||
|
||||
```bash
|
||||
npm install
|
||||
cp assets/config.local.example.js assets/config.local.js # (optionnel)
|
||||
cp assets/config.local.example.js assets/config.local.js # (optionnel — valeurs SANS SECRET uniquement)
|
||||
npm run dev # front + api dev proxy
|
||||
# API seule :
|
||||
npm run api
|
||||
@@ -227,7 +227,8 @@ Ajoutez au besoin `log-driver`, `log-opts`, `default-address-pools`, etc.
|
||||
* `PORT` (4000), `NODE_ENV`, `JWT_SECRET`
|
||||
* `ACCESS_TTL_MIN`, `REFRESH_TTL_DAYS`, `REMEMBER_TTL_DAYS`
|
||||
* `YT_CACHE_TTL_MS`
|
||||
* **Clés API** : `GEMINI_API_KEY`, `YOUTUBE_API_KEY` ou `YOUTUBE_API_KEYS` (CSV), `VIMEO_ACCESS_TOKEN`, `TWITCH_CLIENT_ID`, `TWITCH_CLIENT_SECRET`
|
||||
* **Clés API** (toutes **côté serveur**, aucune n'est envoyée au navigateur) : `GEMINI_API_KEY`, `YOUTUBE_API_KEY` ou `YOUTUBE_API_KEYS` (CSV), `VIMEO_ACCESS_TOKEN`, `RUMBLE_API_KEY`, `TWITCH_CLIENT_ID`, `TWITCH_CLIENT_SECRET`
|
||||
* **API ouverte aux clients** : `API_ALLOWED_ORIGINS` (CSV des origines autorisées en CORS, défaut `localhost:4200,4000,3000` — y ajouter l'URL publique de l'app pour des clients tiers), `METRICS_TOKEN` (si défini, `/metrics` exige `Authorization: Bearer …`), `DETAILS_RATE_LIMIT` (appels `/api/details` par minute, défaut 60)
|
||||
* `NEWTUBE_DB_FILE` (chemin SQLite alternatif)
|
||||
|
||||
> Voir `docker-compose/.env.example` pour un point de départ.
|
||||
@@ -272,14 +273,15 @@ Légende : **✅ livré** (câblé de bout en bout) · **🟡 partiellement livr
|
||||
* ✅ **OAuth** (Google/Twitch) pour import favoris/abonnements — `/library/import` : abonnements + likes YouTube (lecture seule), follows Twitch → abonnements, preview + import, `GET /api/oauth/status|connections|preview`, `POST /api/oauth/:provider/import`
|
||||
* ✅ **Live sur PeerTube & Rumble** — thème « En direct » + onglet Live de la chaîne : PeerTube via l'API native `?isLive=true` (recherche sepiasearch + vidéos de chaîne, items `type:'live'` + spectateurs), Rumble via la page SSR `rumble.com/browse/live` (route `/api/rumble/live`, items typés live ; onglet chaîne filtré par `channelId` — plafond : page 1 bornée à 50 directs). Odysee : **N/A** (aucune API publique de lives, probe 2026-10). Tests : `npm run test:live`
|
||||
* ✅ **Import/Export playlists (JSON)** — `GET /playlists/export` (dump complet + items, format `newtube-playlists-v1`), `POST /playlists/import` (création des listes manquantes, dédoublonnage par titre au réimport, compteurs détaillés ; bornes 200 listes / 5 000 vidéos par liste) ; boutons **[Exporter] [Importer]** sur `/#/library/playlists`.
|
||||
* ✅ **Page Administration** (`/#/admin`, entrée « Administration » de la section Informations dans la barre latérale) — `/healthz` + `/api/providers/metrics` rendus lisibles : statut API, mode YouTube, version et santé yt-dlp, clés actives / bannies (+ suffixes), anti-ban (cookies / PO token / proxy), cache de recherche par provider, compteurs 1 h par provider, quota YouTube du jour. Au passage : `healthz` servi aussi sous `/proxy/api/healthz` (le chemin que le front utilise en prod). *Journaux (logs) et exposition Prometheus restent à faire — voir Observabilité.*
|
||||
* ✅ **Page Administration** (`/#/admin`, entrée « Administration » de la section Informations dans la barre latérale) — `/healthz` + `/api/providers/metrics` rendus lisibles : statut API, mode YouTube, version et santé yt-dlp, clés actives / bannies (+ suffixes), anti-ban (cookies / PO token / proxy), cache de recherche par provider, compteurs 1 h par provider, quota YouTube du jour. Au passage : `healthz` servi aussi sous `/proxy/api/healthz` (le chemin que le front utilise en prod). *Journaux et exposition Prometheus livrés — voir Observabilité.*
|
||||
* ✅ **Cache TTL par provider** — `server/env-ttl.mjs` (`<BASE>_<PROVIDER>` → `<BASE>` → défaut, valeurs invalides ignorées, jamais de TTL nul) branché sur les caches **details** (`DETAILS_CACHE_TTL_MS_<P>`) et **transcripts** (`TRANSCRIPT_CACHE_TTL_<P>`) en plus de la recherche (`SEARCH_CACHE_TTL_MS_<P>`). Le cache **suggest** reste global par conception : sa clé agrégée couvre plusieurs providers d'un coup, un TTL par provider demanderait de scinder le cache. Tests : `npm run test:cache`
|
||||
* ✅ **Menu du compte refondu** (bouton avatar, haut à droite du header) — panneau sombre à lignes « icône + titre gras + sous-titre gris » : **Thème** (thème réellement appliqué en sous-titre, pastilles dépliées au clic), **Administration** (`/#/admin`), **API** (`/proxy/api/openapi.json` dans un nouvel onglet), **Préférences** (`/#/account/preferences`), **Guide d'utilisation** (`/#/info/utilisation`), **À propos** (version, tagline, lien code source), **Sessions** (`/#/account/sessions`), **Déconnexion** ; pied de page **Version** = version officielle de l'application (`src/app/version.ts`, alignée sur le tag semver publié par `docker/deploy-img.sh`). Clés i18n déclarées des deux côtés (FR + EN)
|
||||
* ✅ **Observabilité** — `GET /healthz` (mode `YT_SEARCH_MODE`, binaire yt-dlp, cache, quota du jour, clés masquées — testé par `test:api`), `GET /api/providers/metrics` et la **page Admin** qui les rend, **journal JSON structuré en prod** (une ligne par requête : `ts`, `reqId`, `method`, `route`, `status`, `ms`, adossé au header `X-Request-Id` renvoyé sur chaque réponse) et **`GET /metrics`** — exposition Prometheus sans dépendance (`http_requests_total` par route et code, somme/nombre de durées, démarrage + mémoire du processus ; `METRICS_TOKEN` verrouille l'accès si défini)
|
||||
* ✅ **API production-ready (P0 + P1)** — **plus aucun secret servi au navigateur** : `/assets/config.local.js` est généré par le serveur **avant** les montages statiques (il l'emporte donc sur le fichier local) et ne contient plus `YOUTUBE_API_KEY(S)`, le fichier local n'est plus copié dans l'image Docker ni embarqué dans `dist`, et les appels YouTube passent par `/api/yt` uniquement (clé, rotation, quota et cache côté serveur — suppression des appels directs à googleapis et des gardes « pas de clé ⇒ écran vide ») ; **CORS piloté par `API_ALLOWED_ORIGINS`** (CSV) + méthode `PATCH` (requis par `/user/preferences`) ; **clés d'API longue durée** : `GET/POST /api/keys` + `DELETE /api/keys/:id`, jeton `ntk_…` affiché une seule fois, stocké en SHA-256 avec préfixe affichable, header `X-API-Key` accepté par les **deux** middlewares d'auth (toutes les routes protégées deviennent scriptables), `last_used_at` renseigné à chaque usage ; **`X-Request-Id`** en réponse sur chaque requête ; **`/api/details` rate-limité** (`DETAILS_RATE_LIMIT`, 60/min, réponse JSON — `/api/transcript` avait déjà le sien) ; **version unique `package.json`** (menu du compte + `info.version` de l'OpenAPI). Tests : `npm run test:api`
|
||||
|
||||
### 🟡 Partiellement livré
|
||||
|
||||
* 🟡 **Tags** — ✅ tables `tags` / `video_tags` (stockage des listes « Aimés » et « À regarder plus tard », recherche serveur dans ces listes) ; ⏳ tags **libres** créés par l'utilisateur sur n'importe quelle vidéo + recherche de vidéos par tag
|
||||
* 🟡 **Observabilité** — ✅ `GET /healthz` (mode `YT_SEARCH_MODE`, binaire yt-dlp, cache, quota du jour, clés masquées — testé par `test:api`), `GET /api/providers/metrics` et la **page Admin** qui les rend (voir ✅) ; ⏳ journaux (aucun endpoint de logs aujourd'hui) + exposition métriques au format Prometheus
|
||||
* 🟡 **Qualité vidéo** — ✅ sélecteur de qualité sur `/#/watch` (formats yt-dlp, tri décroissant `1080p → 144p`, reprise de la lecture sur le flux choisi) ; ⏳ mode **« auto » intelligent** (choix automatique du flux selon la bande passante)
|
||||
* 🟡 **Traduction UI (i18n)** — ✅ socle FR/EN (`src/services/i18n.service.ts`, chaque clé déclarée des deux côtés) couvrant barre de recherche, barre de filtres, pages thèmes ; ⏳ couverture **élargie** aux écrans encore en texte FR hardcodé (`/watch`, `/shorts`, bibliothèque)
|
||||
|
||||
@@ -290,7 +292,6 @@ Légende : **✅ livré** (câblé de bout en bout) · **🟡 partiellement livr
|
||||
* ⏳ Mode **« TV »**
|
||||
* ⏳ **Theming avancé** (polices, densité, accents)
|
||||
* ⏳ **Playlists natives Dailymotion & PeerTube** (les 3 autres fournisseurs sont en **N/A** — voir la matrice : API Twitch supprimée, Odysee/Rumble sans API d'écriture) : inscription d'app OAuth Dailymotion, JWT par instance PeerTube
|
||||
* ⏳ **Logs & Prometheus** (volet restant de l'Observabilité — la page Admin est livrée)
|
||||
* ⏳ **Nouveaux fournisseurs** — plan détaillé ci-dessous
|
||||
|
||||
### 🆕 Nouveaux fournisseurs — plan d'implémentation
|
||||
|
||||
Reference in New Issue
Block a user