Files
Dramlog-Prod/src/services/track-api-usage.ts

222 lines
7.1 KiB
TypeScript

'use server';
import { createClient } from '@/lib/supabase/server';
interface TrackApiUsageParams {
userId: string;
apiType: 'google_search' | 'gemini_ai';
endpoint?: string;
success: boolean;
errorMessage?: string;
}
interface ApiStats {
totalCalls: number;
successfulCalls: number;
failedCalls: number;
todayCalls: number;
googleSearchCalls: number;
geminiAiCalls: number;
}
interface DailyLimitCheck {
allowed: boolean;
remaining: number;
limit: number;
}
const GOOGLE_SEARCH_DAILY_LIMIT = 80;
/**
* Track an API usage event
*/
export async function trackApiUsage(params: TrackApiUsageParams): Promise<{ success: boolean; error?: string }> {
try {
const supabase = await createClient();
// Security check: Ensure user is only tracking their own usage
const { data: { user } } = await supabase.auth.getUser();
if (!user || user.id !== params.userId) {
console.error('Unauthorized API tracking attempt');
return { success: false, error: 'Nicht autorisiert' };
}
const { error } = await supabase
.from('api_usage')
.insert({
user_id: params.userId,
api_type: params.apiType,
endpoint: params.endpoint,
success: params.success,
error_message: params.errorMessage,
});
if (error) {
console.error('Failed to track API usage:', error);
return { success: false, error: error.message };
}
return { success: true };
} catch (err) {
console.error('Error tracking API usage:', err);
return { success: false, error: 'Failed to track API usage' };
}
}
/**
* Check if daily limit has been reached for a specific API type
*/
export async function checkDailyLimit(apiType: 'google_search' | 'gemini_ai'): Promise<DailyLimitCheck> {
try {
const supabase = await createClient();
// Only enforce limit for Google Search
if (apiType !== 'google_search') {
return { allowed: true, remaining: 999999, limit: 999999 };
}
// Get today's date in Europe/Berlin timezone
const today = new Date();
const berlinDate = new Date(today.toLocaleString('en-US', { timeZone: 'Europe/Berlin' }));
const startOfDay = new Date(berlinDate);
startOfDay.setHours(0, 0, 0, 0);
const endOfDay = new Date(berlinDate);
endOfDay.setHours(23, 59, 59, 999);
const { count, error } = await supabase
.from('api_usage')
.select('*', { count: 'exact', head: true })
.eq('api_type', apiType)
.gte('created_at', startOfDay.toISOString())
.lte('created_at', endOfDay.toISOString());
if (error) {
console.error('Error checking daily limit:', error);
// Allow on error to avoid blocking users
return { allowed: true, remaining: GOOGLE_SEARCH_DAILY_LIMIT, limit: GOOGLE_SEARCH_DAILY_LIMIT };
}
const currentCount = count || 0;
const remaining = Math.max(0, GOOGLE_SEARCH_DAILY_LIMIT - currentCount);
const allowed = currentCount < GOOGLE_SEARCH_DAILY_LIMIT;
return { allowed, remaining, limit: GOOGLE_SEARCH_DAILY_LIMIT };
} catch (err) {
console.error('Error in checkDailyLimit:', err);
// Allow on error
return { allowed: true, remaining: GOOGLE_SEARCH_DAILY_LIMIT, limit: GOOGLE_SEARCH_DAILY_LIMIT };
}
}
/**
* Get API usage statistics for a specific user
*/
export async function getUserApiStats(userId: string): Promise<ApiStats | null> {
try {
const supabase = await createClient();
const { data, error } = await supabase
.from('api_usage')
.select('*')
.eq('user_id', userId);
if (error) {
console.error('Error fetching user API stats:', error);
return null;
}
const today = new Date();
const berlinDate = new Date(today.toLocaleString('en-US', { timeZone: 'Europe/Berlin' }));
const startOfDay = new Date(berlinDate);
startOfDay.setHours(0, 0, 0, 0);
const todayData = data.filter(item => new Date(item.created_at) >= startOfDay);
return {
totalCalls: data.length,
successfulCalls: data.filter(item => item.success).length,
failedCalls: data.filter(item => !item.success).length,
todayCalls: todayData.length,
googleSearchCalls: data.filter(item => item.api_type === 'google_search').length,
geminiAiCalls: data.filter(item => item.api_type === 'gemini_ai').length,
};
} catch (err) {
console.error('Error in getUserApiStats:', err);
return null;
}
}
/**
* Get global API usage statistics (admin only)
*/
export async function getGlobalApiStats(): Promise<ApiStats | null> {
try {
const supabase = await createClient();
// Check if user is admin
const { data: { user } } = await supabase.auth.getUser();
if (!user) return null;
const isAdmin = await checkIsAdmin(user.id);
if (!isAdmin) return null;
const { data, error } = await supabase
.from('api_usage')
.select('*');
if (error) {
console.error('Error fetching global API stats:', error);
return null;
}
const today = new Date();
const berlinDate = new Date(today.toLocaleString('en-US', { timeZone: 'Europe/Berlin' }));
const startOfDay = new Date(berlinDate);
startOfDay.setHours(0, 0, 0, 0);
const todayData = data.filter(item => new Date(item.created_at) >= startOfDay);
return {
totalCalls: data.length,
successfulCalls: data.filter(item => item.success).length,
failedCalls: data.filter(item => !item.success).length,
todayCalls: todayData.length,
googleSearchCalls: data.filter(item => item.api_type === 'google_search').length,
geminiAiCalls: data.filter(item => item.api_type === 'gemini_ai').length,
};
} catch (err) {
console.error('Error in getGlobalApiStats:', err);
return null;
}
}
/**
* Check if a user is an admin
*/
export async function checkIsAdmin(userId: string): Promise<boolean> {
try {
console.log('[checkIsAdmin] Checking admin status for user:', userId);
const supabase = await createClient();
const { data, error } = await supabase
.from('admin_users')
.select('role')
.eq('user_id', userId)
.single();
console.log('[checkIsAdmin] Query result - data:', data, 'error:', error);
if (error) {
console.log('[checkIsAdmin] Error occurred:', error.message);
return false;
}
const isAdmin = !!data;
console.log('[checkIsAdmin] Final result:', isAdmin);
return isAdmin;
} catch (err) {
console.error('[checkIsAdmin] Exception:', err);
return false;
}
}