Skip to content

Planos por dentro (tiers)

O sistema de planos vive no painel (db.mjs) e é espelhado no site (para o site também saber esconder recursos). Aqui está como ele funciona por dentro.

TIER_RANK = { essencial: 1, profissional: 2, premium: 3 }

Cada rádio tem um tier no banco (radios.tier). Vazio = essencial (o piso). A comparação é por rank: a rádio tem o recurso se o rank do tier dela o rank exigido pelo recurso.

Cada recurso declara o tier mínimo que o libera (FEATURE_MIN_TIER):

Recurso Tier mínimo
programete essencial
studio (estúdio ao vivo) profissional
horaCertaCustom (hora certa com nome) profissional
sitePremium profissional
appPremium profissional
vinheta profissional
campanha profissional
programa profissional
youtubeAuto (vídeos do canal) premium

Existe uma janela promocional (PROMO_END) em que alguns recursos ficam soltos pra qualquer rádio, independente do tier:

PROMO_FEATURES = new Set(['studio', 'horaCertaCustom'])

Durante a promoção, só esses dois ficam livres pra todos. Os conteúdos de biblioteca (vinheta, campanha, programa) não entram na promoção — travam por tier o tempo todo. Quando a promoção vence, a trava dura volta a valer para todos.

A decisão fica em hasFeatureNow(tier, feature):

function hasFeatureNow(tier, feature, now) {
if (promoActive(now) && PROMO_FEATURES.has(feature)) return true;
return tierHasFeature(tier, feature);
}

Dá pra forçar liberar ou bloquear um recurso numa rádio específica, ignorando o plano, via a tabela station_feature_overrides:

  • mode = 'allow' → libera mesmo sem o plano;
  • mode = 'block' → tira mesmo tendo o plano.

O override vence o tier e a promoção. Útil pra cortesias pontuais ou pra desligar algo numa rádio específica.

  • Tier: POST /api/admin/tier { station_id, tier } → grava em radios.tier.
  • Override: POST /api/admin/feature-override { station_id, feature, mode }.

Ambos são endpoints de administração (PSYCO). Não há, hoje, uma tela pra o operador fazer isso sozinho — é o que a página operacional de planos chama de “falar com a PSYCO”.

O painel calcula um mapa { recurso: true/false } por rádio (effectiveFeatures) e o front só mostra ou esconde. Recurso bloqueado vira uma vitrine de upgrade. O site público recebe o tier no config e aplica a mesma lógica (via um espelho do FEATURE_MIN_TIER), pra não renderizar um recurso que a rádio não tem.