sofia-cloud / src /lib /credits.ts
Gmagl
fix: correcciones cr铆ticas y refactorizaci贸n de componentes
3eebcd0
Raw
History Blame Contribute Delete
3.86 kB
// Sistema de cr茅ditos por tier - PERMANENTES (se resetean solo si suben de nivel)
// Los l铆mites no se resetean mensualmente, son parte de lo que pagan
// Se resetean SOLO si el cliente actualiza su suscripci贸n a un tier superior
export const TIER_LIMITS = {
free: {
influencers_per_month: 2,
stories_per_month: 3,
images_per_month: 5,
videos_per_month: 1,
content_per_month: 10,
},
basic: {
influencers_per_month: 10,
stories_per_month: 15,
images_per_month: 30,
videos_per_month: 5,
content_per_month: 50,
},
premium: {
influencers_per_month: 50,
stories_per_month: 60,
images_per_month: 150,
videos_per_month: 20,
content_per_month: 300,
},
pro: {
influencers_per_month: 500,
stories_per_month: 999,
images_per_month: 999,
videos_per_month: 999,
content_per_month: 999,
},
};
export type Tier = keyof typeof TIER_LIMITS;
export type ResourceType = keyof (typeof TIER_LIMITS)["free"];
// Ranking de tiers para comparaciones
export const TIER_RANK: Record<Tier, number> = {
free: 0,
basic: 1,
premium: 2,
pro: 3,
};
// Comprueba si el usuario con `userTier` puede suscribirse a una suscripci贸n de `subTier`.
// Regla: el usuario solo puede crear suscripciones con un tier <= su propio tier.
export function isSubscriptionTierAllowed(userTier: Tier | string, subTier: Tier | string) {
const u = (userTier as Tier) in TIER_RANK ? (userTier as Tier) : (userTier as Tier);
const s = (subTier as Tier) in TIER_RANK ? (subTier as Tier) : (subTier as Tier);
const ur = TIER_RANK[u as Tier] ?? 0;
const sr = TIER_RANK[s as Tier] ?? 0;
return ur >= sr;
}
// Validar si usuario puede hacer la acci贸n
// Los l铆mites son PERMANENTES para el tier contratado
// Se resetean solo si el usuario sube a un tier superior o solicita un reset manual
export async function validateUserCredit(
db: any,
userId: string,
resourceType: ResourceType,
tier: Tier
): Promise<{ allowed: boolean; reason?: string; remaining?: number }> {
const limit = TIER_LIMITS[tier][resourceType];
// Contar TODOS los recursos creados (sin l铆mite de fecha)
// Son permanentes para este tier de suscripci贸n
let used = 0;
try {
if (resourceType === "influencers_per_month") {
used = await db.aIInfluencer.count({
where: {
// Sin filtro de fecha - son permanentes
},
});
} else if (resourceType === "stories_per_month") {
used = await db.story.count({
where: {
// Sin filtro de fecha - son permanentes
},
});
} else if (resourceType === "images_per_month") {
used = await db.content.count({
where: {
type: "image",
// Sin filtro de fecha - son permanentes
},
});
} else if (resourceType === "videos_per_month") {
used = await db.content.count({
where: {
type: "video",
// Sin filtro de fecha - son permanentes
},
});
} else if (resourceType === "content_per_month") {
used = await db.content.count({
where: {
// Sin filtro de fecha - son permanentes
},
});
}
} catch {
// Si hay error, permitir (m谩s seguro que bloquear)
used = 0;
}
const remaining = Math.max(0, limit - used);
return {
allowed: remaining > 0,
reason:
remaining === 0
? `L铆mite de ${resourceType} alcanzado (${limit}). Necesitas subir a un tier superior.`
: undefined,
remaining,
};
}
// Log de uso (opcional, para futuro tracking)
export async function logResourceUsage(
db: any,
userId: string,
resourceType: ResourceType,
resourceId: string
) {
try {
// Implementar cuando sea necesario
// await db.resourceUsage.create({...})
} catch {
// Ignorar errores de logging
}
}