'use strict'; /** * resolveTierFromRequest (Session 67) — best-effort tier for a PUBLIC endpoint. * * `requireAuth` is the wrong tool for a route that must keep serving anonymous * callers: it 401s. This resolves a tier when a bearer token happens to be * present and otherwise returns 'free', so the endpoint stays open while the * gated fields stay gated. * * FAILS CLOSED. Any error — bad token, Supabase unreachable, missing env — * returns 'free', which is the LEAST entitled tier. A resolution failure can * therefore only ever withhold the model price, never leak it. * * Deliberately dependency-light and injectable so route tests don't need a * Supabase client. */ async function resolveTierFromRequest(req, opts = {}) { try { const header = req && req.headers && req.headers.authorization; if (!header || !header.startsWith('Bearer ')) return 'free'; const token = header.slice(7).trim(); if (!token) return 'free'; const getClient = opts.getSupabaseServiceClient || require('../utils/supabase').getSupabaseServiceClient; const supabase = getClient(); if (!supabase) return 'free'; const { data, error } = await supabase.auth.getUser(token); if (error || !data || !data.user) return 'free'; const { data: profile } = await supabase .from('users') .select('tier') .eq('id', data.user.id) .single(); const tier = profile && profile.tier; return typeof tier === 'string' && tier ? tier : 'free'; } catch { return 'free'; // fail closed — never leak on an error path } } module.exports = { resolveTierFromRequest };