Spaces:
Running
Running
File size: 6,416 Bytes
ea81969 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
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);
}
};
|