diff --git a/server/index.mjs b/server/index.mjs index 75b5ae8..9dd0b40 100644 --- a/server/index.mjs +++ b/server/index.mjs @@ -578,6 +578,9 @@ r.get('/oauth/google/yt-channels', authMiddlewareCookieAware, oauthLimiter, asyn const out = { error: error?.message || 'yt_channels_failed' }; if (error?.detail) out.detail = error.detail; if (error?.hint) out.hint = error.hint; + try { + console.warn(`[oauth] yt-channels échec user=${req.user?.id} code=${out.error} detail=${String(error?.detail || '').slice(0, 300)}`); + } catch {} return res.status(error?.status || 502).json(out); } }); diff --git a/server/oauth.mjs b/server/oauth.mjs index bef093d..388e461 100644 --- a/server/oauth.mjs +++ b/server/oauth.mjs @@ -642,13 +642,29 @@ async function getAuthedInnertube(conn, { pageId } = {}) { return innertube; } -/** Détail d'erreur YouTube exposable (clé API masquée, 300 car. max, sans token). */ +/** Détail d'erreur YouTube exposable (clé/token masqués, 500 car. max). */ function libErrorDetail(e) { try { - return String(e?.message || e || '') + // youtubei.js met le corps de réponse dans e.info (JSON d'erreur Google). + let raw = ''; + if (e?.info && typeof e.info === 'object') { + try { raw = JSON.stringify(e.info); } catch { raw = String(e.info); } + } else if (typeof e?.info === 'string') { + raw = e.info; + } + const combined = [raw, String(e?.message || '')].filter(Boolean).join(' | '); + // Extrait le motif Google s'il existe : {"error":{"errors":[{"reason":"X"}]}}. + let reason = ''; + try { + const parsed = JSON.parse(raw); + const first = parsed?.error?.errors?.[0]; + if (first?.reason) reason = `reason=${first.reason}; msg=${String(first.message || parsed?.error?.message || '').slice(0, 200)}`; + } catch {} + const out = (reason || combined) .replace(/key=[^&\s]*/gi, 'key=[redacted]') .replace(/Bearer\s+[A-Za-z0-9._~-]+/gi, 'Bearer [redacted]') - .slice(0, 300) || undefined; + .slice(0, 500); + return out || undefined; } catch { return undefined; } }