import { Request, Response, NextFunction } from 'express'; import { supabase } from '../lib/supabase'; export interface AuthenticatedRequest extends Request { user?: any; } export const requireAuth = async (req: AuthenticatedRequest, res: Response, next: NextFunction) => { try { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ error: 'Unauthorized: Missing or invalid token' }); } const token = authHeader.split(' ')[1]; const { data: { user }, error } = await supabase.auth.getUser(token); if (error || !user) { return res.status(401).json({ error: 'Unauthorized: Invalid token' }); } // P0-2: Lisans Kontrolü const { data: profile } = await supabase .from('profiles') .select('license_status, trial_end_date') .eq('id', user.id) .maybeSingle(); let userProfile = profile; if (!userProfile) { // Profil bulunamadıysa kullanıcıya otomatik deneme profili aç — süre // app_settings.trial_days'ten okunuyor (bkz. sql/19_configurable_trial_and_status_gate.sql), // handle_new_user() Postgres trigger'ıyla aynı kaynağı kullanıyor. const { data: settings } = await supabase.from('app_settings').select('trial_days').maybeSingle(); const trialDays = settings?.trial_days ?? 7; const trialEndDate = new Date(); trialEndDate.setDate(trialEndDate.getDate() + trialDays); const { data: newProfile } = await supabase .from('profiles') .insert([{ id: user.id, license_status: 'trial', trial_end_date: trialEndDate.toISOString() }]) .select() .maybeSingle(); userProfile = newProfile || { license_status: 'trial', trial_end_date: trialEndDate.toISOString() }; } if (!userProfile) { return res.status(403).json({ error: 'Forbidden: User profile not found' }); } // Deneme süresi dolmuş mu anlık kontrol et if (userProfile.license_status === 'trial' && userProfile.trial_end_date) { const trialEnd = new Date(userProfile.trial_end_date); if (trialEnd < new Date()) { await supabase .from('profiles') .update({ license_status: 'expired' }) .eq('id', user.id); return res.status(403).json({ error: 'Trial süresi dolmuştur. Lisansınızı aktive etmek için aktivasyon kodunuzu girin.', code: 'TRIAL_EXPIRED', }); } } if (userProfile.license_status !== 'active' && userProfile.license_status !== 'trial') { return res.status(403).json({ error: 'Forbidden: License is not active or in trial', code: 'LICENSE_INACTIVE' }); } req.user = user; // P0-3: Kaynak Sahipliği Doğrulama (Eğer istekte caseId veya case_id varsa) const caseId = req.params.caseId || req.body?.case_id; if (caseId && caseId !== 'general') { const { data: caseRecord } = await supabase .from('cases') .select('id, user_id') .eq('id', caseId) .maybeSingle(); if (caseRecord) { if (!caseRecord.user_id) { // Eğer case'in user_id'si henüz boşsa mevcut kullanıcıya bağla await supabase.from('cases').update({ user_id: user.id }).eq('id', caseId); } else if (caseRecord.user_id !== user.id) { return res.status(403).json({ error: 'Forbidden: You do not own this case' }); } } } next(); } catch (error) { console.error('Auth middleware error:', error); res.status(500).json({ error: 'Internal server error during authentication' }); } };