Spaces:
Build error
Build error
File size: 3,863 Bytes
3eebcd0 | 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 | // 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
}
}
|