// Auth — JWT + bcrypt pra Shivao SaaS multi-tenant. import bcrypt from 'bcryptjs'; import jwt from 'jsonwebtoken'; const JWT_SECRET = process.env.JWT_SECRET || (process.env.BOAT_TOKEN + '-jwt-derive-key'); const JWT_ACCESS_TTL = '7d'; // access token expira em 7 dias (menos fricção pra usuário móvel) const JWT_REFRESH_TTL = '90d'; // refresh em 90 dias if (!process.env.JWT_SECRET && !process.env.BOAT_TOKEN) { console.warn('[auth] AVISO: JWT_SECRET não configurado. Use env JWT_SECRET em produção.'); } export async function hashPassword(plain) { if (!plain || plain.length < 8) throw new Error('Senha precisa ter no mínimo 8 caracteres'); return bcrypt.hash(plain, 10); } export async function verifyPassword(plain, hash) { if (!plain || !hash) return false; try { return await bcrypt.compare(plain, hash); } catch { return false; } } export function signAccessToken(user) { return jwt.sign({ uid: user.id, email: user.email, type: 'access' }, JWT_SECRET, { expiresIn: JWT_ACCESS_TTL }); } export function signRefreshToken(user) { return jwt.sign({ uid: user.id, type: 'refresh' }, JWT_SECRET, { expiresIn: JWT_REFRESH_TTL }); } export function verifyToken(token) { try { return jwt.verify(token, JWT_SECRET); } catch { return null; } } // Plans → features matrix // free: âncora local + diário básico (até 10 viagens) // pro: tudo do free + sync nuvem ilimitada + GPS tracking + mídia + geofencing // captain: tudo do pro + Windy premium + multi-barco + relatórios PDF + audit log export const PLANS = { free: { name: 'Free (Âncora)', price_brl: 0, features: ['anchor_local', 'diary_limited_10', 'export_gpx_basic'] }, pro: { name: 'Pro', price_brl_monthly: 19, price_brl_yearly: 149, features: ['anchor_local', 'anchor_remote', 'diary_unlimited', 'cloud_sync', 'gps_tracking', 'media_cloud', 'geofencing', 'webhooks', 'export_all'] }, captain: { name: 'Captain', price_brl_monthly: 39, price_brl_yearly: 299, features: ['anchor_local', 'anchor_remote', 'diary_unlimited', 'cloud_sync', 'gps_tracking', 'media_cloud', 'geofencing', 'webhooks', 'export_all', 'windy_premium', 'multi_boat', 'pdf_reports', 'audit_log'] } }; export function planFeatures(plan) { return (PLANS[plan] || PLANS.free).features; } export function planHasFeature(plan, feature) { return planFeatures(plan).includes(feature); }