/opt/canhelp/apps/api/src/lib
NameSizeModeActions
activity.ts13240644editdlrm
apple-client-secret.ts40730644editdlrm
balance.ts35060644editdlrm
daily-limits.ts42820644editdlrm
email.ts527080644editdlrm
fomo.ts113660644editdlrm
google-oauth.ts105260644editdlrm
helper-capability.ts42070644editdlrm
messaging-rules.ts31230644editdlrm
nameUtils.ts7090644editdlrm
notif.ts189290644editdlrm
plan-history.ts10200644editdlrm
push.ts63860644editdlrm
referral.ts61100644editdlrm
telegram.ts62070644editdlrm
turnstile.ts7410644editdlrm
Edit: /opt/canhelp/apps/api/src/lib/daily-limits.ts (4282B)
import { eq, and, sql } from 'drizzle-orm' import { db } from '../db.js' import { dailyUsage, users, plans } from '@canhelp/db' import type { PlanRow } from '../middleware/plan.js' /** Get today's date in YYYY-MM-DD (Europe/Athens timezone) */ function todayDate(): string { return new Date().toLocaleDateString('sv-SE', { timeZone: 'Europe/Athens' }) } /** Get or create today's usage record for a user */ async function getOrCreateUsage(userId: string) { const date = todayDate() const [existing] = await db .select() .from(dailyUsage) .where(and(eq(dailyUsage.userId, userId), eq(dailyUsage.date, date))) if (existing) return existing const [created] = await db .insert(dailyUsage) .values({ userId, date, messagesSent: 0, ordersMade: 0 }) .onConflictDoNothing() .returning() // Race condition: another request created it first if (!created) { const [retry] = await db .select() .from(dailyUsage) .where(and(eq(dailyUsage.userId, userId), eq(dailyUsage.date, date))) return retry! } return created } /** Fetch the user's plan from DB */ async function getUserPlan(userId: string): Promise { const [user] = await db .select({ planId: users.planId }) .from(users) .where(eq(users.id, userId)) if (!user?.planId) return null const [plan] = await db.select().from(plans).where(eq(plans.id, user.planId)) return plan ?? null } export interface LimitCheckResult { allowed: boolean current: number limit: number | null // null = unlimited remaining: number | null // null = unlimited } /** * Check if user can send a message today. * Returns { allowed, current, limit, remaining } */ export async function checkMessageLimit(userId: string, plan?: PlanRow | null): Promise { const userPlan = plan ?? (await getUserPlan(userId)) const limit = userPlan?.maxMessagesPerDay ?? null if (limit === null) { return { allowed: true, current: 0, limit: null, remaining: null } } const usage = await getOrCreateUsage(userId) const current = usage.messagesSent const remaining = Math.max(0, limit - current) return { allowed: current < limit, current, limit, remaining } } /** * Check if user can create an order/offer today. * Returns { allowed, current, limit, remaining } */ export async function checkOrderLimit(userId: string, plan?: PlanRow | null): Promise { const userPlan = plan ?? (await getUserPlan(userId)) const limit = userPlan?.maxOrdersPerDay ?? null if (limit === null) { return { allowed: true, current: 0, limit: null, remaining: null } } const usage = await getOrCreateUsage(userId) const current = usage.ordersMade const remaining = Math.max(0, limit - current) return { allowed: current < limit, current, limit, remaining } } /** Increment message counter for today */ export async function incrementMessages(userId: string): Promise { const date = todayDate() // Ensure record exists await getOrCreateUsage(userId) // Atomically increment await db .update(dailyUsage) .set({ messagesSent: sql`${dailyUsage.messagesSent} + 1` }) .where(and(eq(dailyUsage.userId, userId), eq(dailyUsage.date, date))) } /** Increment order counter for today */ export async function incrementOrders(userId: string): Promise { const date = todayDate() // Ensure record exists await getOrCreateUsage(userId) // Atomically increment await db .update(dailyUsage) .set({ ordersMade: sql`${dailyUsage.ordersMade} + 1` }) .where(and(eq(dailyUsage.userId, userId), eq(dailyUsage.date, date))) } /** * Get daily usage summary for current user (for UI display). */ export async function getDailyUsageSummary(userId: string, plan?: PlanRow | null) { const userPlan = plan ?? (await getUserPlan(userId)) const usage = await getOrCreateUsage(userId) const msgLimit = userPlan?.maxMessagesPerDay ?? null const orderLimit = userPlan?.maxOrdersPerDay ?? null return { messagesLeft: msgLimit !== null ? Math.max(0, msgLimit - usage.messagesSent) : null, ordersLeft: orderLimit !== null ? Math.max(0, orderLimit - usage.ordersMade) : null, messagesSent: usage.messagesSent, ordersMade: usage.ordersMade, } }