/opt/canhelp/apps/api/src/lib
Edit: /opt/canhelp/apps/api/src/lib/messaging-rules.ts (3123B)
import { eq } from 'drizzle-orm'
import { db } from '../db.js'
import { users, plans } from '@canhelp/db'
export interface MessagingCheckResult {
allowed: boolean
reason?: string
code?: string
senderTier?: string
recipientTier?: string
}
/**
* Check if sender can message recipient based on their plan tiers.
*
* Rules:
* - Free can only message users on paid plans (Pro+)
* - Pro can message everyone in their direction
* - Ultimate can message everyone
* - canContactAll overrides everything
*
* @param senderId - the user initiating the message
* @param recipientId - the target user
*/
export async function canSendMessage(
senderId: string,
recipientId: string,
): Promise
{
// Load both users with their plans in parallel
const [[sender], [recipient]] = await Promise.all([
db
.select({ id: users.id, planId: users.planId, role: users.role })
.from(users)
.where(eq(users.id, senderId)),
db
.select({ id: users.id, planId: users.planId, role: users.role })
.from(users)
.where(eq(users.id, recipientId)),
])
if (!sender || !recipient) {
return { allowed: false, reason: 'User not found', code: 'USER_NOT_FOUND' }
}
// Admins can message anyone
if (sender.role === 'admin') {
return { allowed: true }
}
// Load plans in parallel
const [senderPlan, recipientPlan] = await Promise.all([
sender.planId
? db.select().from(plans).where(eq(plans.id, sender.planId)).then((r) => r[0] ?? null)
: Promise.resolve(null),
recipient.planId
? db.select().from(plans).where(eq(plans.id, recipient.planId)).then((r) => r[0] ?? null)
: Promise.resolve(null),
])
const senderTier = senderPlan?.tier ?? 'free'
const recipientTier = recipientPlan?.tier ?? 'free'
// canContactAll = Ultimate, can message everyone
if (senderPlan?.canContactAll) {
return { allowed: true, senderTier, recipientTier }
}
// Check based on recipient's tier
if (recipientTier === 'free') {
if (senderPlan?.canContactFreePlan) {
return { allowed: true, senderTier, recipientTier }
}
return {
allowed: false,
reason: 'Your plan does not allow messaging users on the Free plan',
code: 'PLAN_CONTACT_RESTRICTED',
senderTier,
recipientTier,
}
}
if (recipientTier === 'pro') {
if (senderPlan?.canContactProPlan) {
return { allowed: true, senderTier, recipientTier }
}
return {
allowed: false,
reason: 'Your plan does not allow messaging users on the Pro plan',
code: 'PLAN_CONTACT_RESTRICTED',
senderTier,
recipientTier,
}
}
// recipientTier === 'ultimate' — Pro plans always have canContactProPlan=true,
// and Ultimate has canContactAll=true — so anyone with canContactProPlan can reach Ultimate
if (senderPlan?.canContactProPlan) {
return { allowed: true, senderTier, recipientTier }
}
return {
allowed: false,
reason: 'Your plan does not allow messaging this user',
code: 'PLAN_CONTACT_RESTRICTED',
senderTier,
recipientTier,
}
}