Files
Design-Buddy/src/lib/credit-service.ts
Rosario Moscato 57361a3e34 feat: implement Design Buddy AI interior design application
- Replace boilerplate with complete Design Buddy application
- Add AI-powered room design generation using Google Gemini SDK
- Implement user authentication with Google OAuth via Better Auth
- Create credit system with 30 free credits for new users
- Build image upload interface with drag-and-drop functionality
- Add room type and design style selection (Living Room, Kitchen, etc.)
- Implement AI generation with geographical restriction handling
- Add credit refund system for API failures
- Create responsive landing page with feature sections
- Replace all branding and navigation with Design Buddy theme
- Add complete user dashboard with real-time credit balance
- Implement download functionality for generated designs

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-16 16:17:50 +02:00

169 lines
4.2 KiB
TypeScript

import { db } from './db';
import { user, creditUsage } from './schema';
import { eq } from 'drizzle-orm';
import { v4 as uuidv4 } from 'uuid';
export interface CreditUsageResult {
success: boolean;
newBalance?: number;
error?: string;
}
export class CreditService {
/**
* Get user's current credit balance
*/
static async getUserCredits(userId: string): Promise<number> {
const result = await db.select({ credits: user.credits })
.from(user)
.where(eq(user.id, userId))
.limit(1);
return result[0]?.credits || 0;
}
/**
* Use credits for a user
*/
static async useCredits(
userId: string,
amount: number,
description: string
): Promise<CreditUsageResult> {
try {
// Start a transaction to ensure consistency
const result = await db.transaction(async (tx) => {
// Get current balance
const currentUser = await tx.select({ credits: user.credits })
.from(user)
.where(eq(user.id, userId))
.limit(1);
if (!currentUser[0]) {
throw new Error('User not found');
}
const currentBalance = currentUser[0].credits;
// Check if user has enough credits
if (currentBalance < amount) {
throw new Error('Insufficient credits');
}
// Update user balance
await tx.update(user)
.set({
credits: currentBalance - amount,
updatedAt: new Date()
})
.where(eq(user.id, userId));
// Record credit usage
await tx.insert(creditUsage).values({
id: uuidv4(),
userId,
creditsUsed: amount,
description,
createdAt: new Date(),
});
return { success: true, newBalance: currentBalance - amount };
});
return result;
} catch (error) {
console.error('Credit usage error:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to use credits'
};
}
}
/**
* Add credits to a user's account
*/
static async addCredits(
userId: string,
amount: number,
description: string
): Promise<CreditUsageResult> {
try {
const result = await db.transaction(async (tx) => {
// Get current balance
const currentUser = await tx.select({ credits: user.credits })
.from(user)
.where(eq(user.id, userId))
.limit(1);
if (!currentUser[0]) {
throw new Error('User not found');
}
const currentBalance = currentUser[0].credits;
// Update user balance
await tx.update(user)
.set({
credits: currentBalance + amount,
updatedAt: new Date()
})
.where(eq(user.id, userId));
// Record negative credit usage (addition)
if (amount > 0) {
await tx.insert(creditUsage).values({
id: uuidv4(),
userId,
creditsUsed: -amount, // Negative to indicate addition
description,
createdAt: new Date(),
});
}
return { success: true, newBalance: currentBalance + amount };
});
return result;
} catch (error) {
console.error('Credit addition error:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to add credits'
};
}
}
/**
* Get credit usage history for a user
*/
static async getCreditHistory(userId: string, limit: number = 50) {
return await db.select({
id: creditUsage.id,
creditsUsed: creditUsage.creditsUsed,
description: creditUsage.description,
createdAt: creditUsage.createdAt,
})
.from(creditUsage)
.where(eq(creditUsage.userId, userId))
.orderBy(creditUsage.createdAt)
.limit(limit);
}
/**
* Initialize user with 30 free credits (for new users)
*/
static async initializeUserCredits(userId: string): Promise<void> {
try {
await db.update(user)
.set({
credits: 30,
updatedAt: new Date()
})
.where(eq(user.id, userId));
} catch (error) {
console.error('Error initializing user credits:', error);
throw error;
}
}
}