Profil bulunamadığında otomatik açılan trial süresi Postgres trigger'ıyla (handle_new_user, 3 gün) tutarsızdı. İkisi de artık aynı app_settings tablosundan okuyor (bkz. laawos/sql/19_configurable_trial_and_status_gate.sql). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
99 lines
3.6 KiB
TypeScript
99 lines
3.6 KiB
TypeScript
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' });
|
||
}
|
||
};
|
||
|