Files
property-management-network/lib/plan-limits.ts
Leon SerfatyandClaude Opus 4.8 c9968531e4 Consolidate audit-fixes branch: webhooks, integrations, and deploy hardening
Batch commit of the pending working tree on security/audit-fixes-2026-07.
Major areas:
- Outbound webhooks / Zapier: schema + signed delivery with retries, public
  v1 API (REST-hook subscribe/unsubscribe), settings UI, cron drain.
- Deploy hardening: email via SMTP2GO (Resend fully removed), verified DB TLS
  (DATABASE_SSL=require + DATABASE_CA), storage fails loud in production when
  Spaces is unconfigured instead of silently using ephemeral disk.
- Integrations & features (concurrent work): accounting (QuickBooks/Xero),
  e-signature (DocuSign/Dropbox Sign), PayPal, geocoding/maps, onboarding,
  expanded legal pages.
- DB migrations 0006–0009.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 13:42:34 -04:00

42 lines
1.5 KiB
TypeScript

import { eq } from "drizzle-orm"
import { db } from "@/lib/db"
import { profiles } from "@/lib/db/schema"
import { PLAN_LIMITS } from "@/lib/stripe/plans"
import { getUserStorageBytes } from "@/lib/storage"
import type { Plan } from "@/types"
/**
* Server-side plan-limit enforcement helpers. Single source of truth is
* PLAN_LIMITS in lib/stripe/plans.ts — never hardcode limit numbers in routes.
*/
/** The user's current plan (defaults to "starter" if no profile row). */
export async function getUserPlan(userId: string): Promise<Plan> {
const profile = await db.query.profiles.findFirst({
where: eq(profiles.id, userId),
columns: { plan: true },
})
return (profile?.plan ?? "starter") as Plan
}
/**
* Returns an error message if storing `incomingBytes` more would exceed the
* user's plan storage cap, otherwise null. Reads actual usage from the storage
* backend so it stays accurate regardless of which tables reference the files.
*/
export async function checkStorageLimit(
userId: string,
incomingBytes: number
): Promise<string | null> {
const plan = await getUserPlan(userId)
const maxBytes = PLAN_LIMITS[plan].maxStorageMB * 1024 * 1024
if (!Number.isFinite(maxBytes)) return null // unlimited plan
const used = await getUserStorageBytes(userId)
if (used + incomingBytes > maxBytes) {
const limitMb = PLAN_LIMITS[plan].maxStorageMB
return `Storage limit reached (${limitMb} MB on your plan). Upgrade for more space.`
}
return null
}