Files
Dramlog-Prod/src/services/admin-credit-service.ts

242 lines
8.1 KiB
TypeScript

'use server';
import { createClient } from '@/lib/supabase/server';
import { checkIsAdmin } from './track-api-usage';
import { addCredits, getUserCredits } from './credit-service';
import { AdminCreditUpdateSchema, AdminSettingsSchema } from '@/types/whisky';
interface UserWithCredits {
id: string;
email: string;
username: string;
balance: number;
total_purchased: number;
total_used: number;
daily_limit: number | null;
google_search_cost: number;
gemini_ai_cost: number;
last_active?: string;
}
/**
* Get all users with their credit information (admin only)
*/
export async function getAllUsersWithCredits(): Promise<UserWithCredits[]> {
try {
const supabase = await createClient();
// Check if current user is admin
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
console.log('[getAllUsersWithCredits] No user found');
return [];
}
const isAdmin = await checkIsAdmin(user.id);
if (!isAdmin) {
console.log('[getAllUsersWithCredits] User is not admin');
return [];
}
console.log('[getAllUsersWithCredits] Fetching profiles...');
// Get all users with their profiles
const { data: profiles, error: profilesError } = await supabase
.from('profiles')
.select('id, username');
if (profilesError) {
console.error('Error fetching profiles:', profilesError);
return [];
}
console.log('[getAllUsersWithCredits] Found profiles:', profiles?.length);
// Get all user credits
const { data: credits, error: creditsError } = await supabase
.from('user_credits')
.select('*');
if (creditsError) {
console.error('Error fetching credits:', creditsError);
}
console.log('[getAllUsersWithCredits] Found credits:', credits?.length);
// Combine data - we'll use profile id as email fallback
const usersWithCredits: UserWithCredits[] = profiles?.map(profile => {
const userCredits = credits?.find(c => c.user_id === profile.id);
return {
id: profile.id,
email: profile.id.substring(0, 8) + '...', // Show partial ID as placeholder
username: profile.username || 'Unknown',
balance: userCredits?.balance || 0,
total_purchased: userCredits?.total_purchased || 0,
total_used: userCredits?.total_used || 0,
daily_limit: userCredits?.daily_limit || null,
google_search_cost: userCredits?.google_search_cost || 1,
gemini_ai_cost: userCredits?.gemini_ai_cost || 1,
last_active: undefined
};
}) || [];
console.log('[getAllUsersWithCredits] Returning users:', usersWithCredits.length);
return usersWithCredits;
} catch (err) {
console.error('Error in getAllUsersWithCredits:', err);
return [];
}
}
/**
* Update user's credit balance (admin only)
*/
export async function updateUserCredits(
userId: string,
newBalance: number,
reason: string
): Promise<{ success: boolean; error?: string }> {
try {
const validated = AdminCreditUpdateSchema.parse({ userId, newBalance, reason });
const supabase = await createClient();
// Check if current user is admin
const { data: { user } } = await supabase.auth.getUser();
if (!user) return { success: false, error: 'Not authenticated' };
const isAdmin = await checkIsAdmin(user.id);
if (!isAdmin) return { success: false, error: 'Not authorized' };
// Get current credits
const currentCredits = await getUserCredits(validated.userId);
if (!currentCredits) {
return { success: false, error: 'User credits not found' };
}
const difference = validated.newBalance - currentCredits.balance;
// Use addCredits which handles the transaction logging
const result = await addCredits(validated.userId, difference, validated.reason, user.id);
return result;
} catch (err) {
console.error('Error in updateUserCredits:', err);
return { success: false, error: err instanceof Error ? err.message : 'Failed to update credits' };
}
}
/**
* Set user's daily limit (admin only)
*/
export async function setUserDailyLimit(
userId: string,
dailyLimit: number | null
): Promise<{ success: boolean; error?: string }> {
try {
const validated = AdminSettingsSchema.parse({ userId, dailyLimit });
const supabase = await createClient();
// Check if current user is admin
const { data: { user } } = await supabase.auth.getUser();
if (!user) return { success: false, error: 'Not authenticated' };
const isAdmin = await checkIsAdmin(user.id);
if (!isAdmin) return { success: false, error: 'Not authorized' };
const { error } = await supabase
.from('user_credits')
.update({ daily_limit: validated.dailyLimit })
.eq('user_id', validated.userId);
if (error) {
console.error('Error setting daily limit:', error);
return { success: false, error: 'Failed to set daily limit' };
}
return { success: true };
} catch (err) {
console.error('Error in setUserDailyLimit:', err);
return { success: false, error: err instanceof Error ? err.message : 'Failed to set daily limit' };
}
}
/**
* Set user's API costs (admin only)
*/
export async function setUserApiCosts(
userId: string,
googleSearchCost: number,
geminiAiCost: number
): Promise<{ success: boolean; error?: string }> {
try {
const validated = AdminSettingsSchema.parse({ userId, googleSearchCost, geminiAiCost });
const supabase = await createClient();
// Check if current user is admin
const { data: { user } } = await supabase.auth.getUser();
if (!user) return { success: false, error: 'Not authenticated' };
const isAdmin = await checkIsAdmin(user.id);
if (!isAdmin) return { success: false, error: 'Not authorized' };
const { error } = await supabase
.from('user_credits')
.update({
google_search_cost: validated.googleSearchCost,
gemini_ai_cost: validated.geminiAiCost
})
.eq('user_id', validated.userId);
if (error) {
console.error('Error setting API costs:', error);
return { success: false, error: 'Failed to set API costs' };
}
return { success: true };
} catch (err) {
console.error('Error in setUserApiCosts:', err);
return { success: false, error: err instanceof Error ? err.message : 'Failed to set API costs' };
}
}
/**
* Bulk add credits to multiple users (admin only)
*/
export async function bulkAddCredits(
userIds: string[],
amount: number,
reason: string
): Promise<{ success: boolean; processed: number; failed: number; error?: string }> {
try {
const supabase = await createClient();
// Check if current user is admin
const { data: { user } } = await supabase.auth.getUser();
if (!user) return { success: false, processed: 0, failed: 0, error: 'Not authenticated' };
const isAdmin = await checkIsAdmin(user.id);
if (!isAdmin) return { success: false, processed: 0, failed: 0, error: 'Not authorized' };
let processed = 0;
let failed = 0;
for (const userId of userIds) {
const result = await addCredits(userId, amount, reason, user.id);
if (result.success) {
processed++;
} else {
failed++;
}
}
return {
success: true,
processed,
failed
};
} catch (err) {
console.error('Error in bulkAddCredits:', err);
return { success: false, processed: 0, failed: 0, error: 'Failed to bulk add credits' };
}
}