transcriptmaster / backend /services /creditService.js
bigbossmonster's picture
Upload 24 files
ea81969 verified
import { db } from '@/backend/services/firebase';
export const CreditService = {
async checkEligibility(sessionId, guestId, toolKey, isOwnKey, creditCostOverride = null) {
if (!sessionId) throw new Error("Authentication failed.");
const now = new Date();
const today = now.toLocaleDateString('en-CA');
const usageKey = isOwnKey ? `own_${toolKey}` : `app_${toolKey}`;
// 1. Fetch System Tier Settings
const sysSnapshot = await db.ref('system/settings/tiers').once('value');
const tiers = sysSnapshot.val() || {};
const toolMapper = {
'count_transcript': 'transcript',
'count_translate': 'translate',
'count_srt_translate': 'srt_translator',
'count_tts': 'tts',
'count_subtitle': 'subtitle_gen',
'count_creator_text': 'content_creator',
'count_creator_image': 'ai_image',
'downloader': 'downloader',
'book_recap': 'book_recap'
};
const tierToolKey = toolMapper[toolKey] || toolKey;
let userKey, userData, userClass, storagePath;
if (sessionId === 'free_access') {
userClass = 'FREE';
if (!guestId || guestId === 'anonymous') {
// Return generic guest data for free mode
userData = { Usage: {}, Credits: 0, Class: 'FREE', Last_Usage_Date: today };
storagePath = `guests/temp_guest`;
} else {
storagePath = `guests/${guestId}/${today}`;
const usageSnapshot = await db.ref(storagePath).once('value');
const currentUsage = usageSnapshot.val() || {};
userData = { Usage: currentUsage, Credits: 0, Class: 'FREE', Last_Usage_Date: today };
}
} else {
// Find user by temporary session ID
const snapshot = await db.ref('users').orderByChild('Active_Session_ID').equalTo(sessionId).once('value');
if (!snapshot.exists()) throw new Error("Invalid Session. Please login again.");
userKey = Object.keys(snapshot.val())[0];
userData = snapshot.val()[userKey];
if (userData.Status !== 'ACTIVE') throw new Error("Account suspended.");
userClass = userData.Class === 'MEMBER+' ? 'MEMBER_PLUS' : (userData.Class === 'BASIC' ? 'BASIC' : 'MEMBER');
storagePath = `users/${userKey}`;
}
const tierConfig = tiers[userClass] || { apiAccess: { app: true, own: true } };
// Check if user is trying to use their own API but it's disabled for their class
if (isOwnKey && tierConfig.apiAccess?.own === false) throw new Error("OWN_API_RESTRICTED");
if (!isOwnKey && tierConfig.apiAccess?.app === false) throw new Error("APP_API_RESTRICTED");
// Check Specific Tool Toggle (Mode Specific)
const toolToggleMap = isOwnKey ? (tierConfig.ownTools || {}) : (tierConfig.tools || {});
if (toolToggleMap[tierToolKey] === false) throw new Error("TOOL_DISABLED");
const usage = (userData.Last_Usage_Date === today) ? (userData.Usage || {}) : {};
// 1. Declare the limit variable based on the tier settings
let limit = isOwnKey
? (tierConfig.ownLimits?.[tierToolKey] ?? -1)
: (tierConfig.limits?.[tierToolKey] ?? -1);
// 2. Override: If using Own API Key, force the limit to -1 (Unlimited)
if (isOwnKey) {
limit = -1;
}
// 3. Perform the check only if a limit exists
if (limit !== -1) {
const currentCount = parseInt(usage[usageKey] || 0);
if (currentCount >= limit) {
throw new Error("DAILY_LIMIT_REACHED");
}
}
if (isOwnKey) {
limit = -1; // -1 means "No Limit" or "Unlimited" in your logic (LOL kyaw Gyi)
}
// Check Daily Frequency Limit (Mode Specific)
// let limit = isOwnKey ? (tierConfig.ownLimits?.[tierToolKey] ?? -1) : (tierConfig.limits?.[tierToolKey] ?? -1);
if (limit !== -1) {
const currentCount = parseInt(usage[usageKey] || 0);
if (currentCount >= limit) throw new Error("DAILY_LIMIT_REACHED");
}
let cost = 0;
if (userClass !== 'FREE' && !isOwnKey) {
const costMap = {
'count_transcript': 4,
'count_translate': 4,
'count_srt_translate': 7,
'count_tts': 3,
'count_subtitle': 2,
'count_creator_text': 3,
'count_creator_image': 5,
'downloader': 2,
'book_recap': 8
};
cost = (creditCostOverride !== null) ? creditCostOverride : (costMap[toolKey] || 1);
if (cost > 0 && (userData.Credits || 0) < cost) {
throw new Error(`INSUFFICIENT_BALANCE: Needs ${cost} credits.`);
}
}
return { userKey, storagePath, userData, usageKey, cost, today, isGuest: (userClass === 'FREE'), isOwnKey };
},
async commitDeduction(eligibilityData) {
if (!eligibilityData) return;
const { storagePath, userData, usageKey, cost, today, isGuest, isOwnKey } = eligibilityData;
// Own API mode records usage but never deducts credits
const finalCost = isOwnKey ? 0 : cost;
const updates = {};
if (isGuest) {
if (storagePath === 'guests/temp_guest') return;
const currentUsage = (userData.Usage?.[usageKey]) || 0;
updates[`${storagePath}/${usageKey}`] = currentUsage + 1;
await db.ref().update(updates);
return;
}
if (finalCost > 0) {
const newBalance = (userData.Credits || 0) - finalCost;
updates[`${storagePath}/Credits`] = Math.max(0, newBalance);
}
if (userData.Last_Usage_Date !== today) {
updates[`${storagePath}/Last_Usage_Date`] = today;
updates[`${storagePath}/Usage`] = { [usageKey]: 1 };
} else {
const currentUsage = (userData.Usage?.[usageKey]) || 0;
updates[`${storagePath}/Usage/${usageKey}`] = currentUsage + 1;
}
await db.ref().update(updates);
}
};