import { isProKey } from "../services/keys.js"; import logger from "../services/logger.js"; import pool from "../services/db.js"; const FREE_TIER_LIMIT = 100; // In-memory cache, periodically synced to PostgreSQL let usage = new Map(); function getMonthKey() { const d = new Date(); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`; } export async function loadUsageData() { try { const result = await pool.query("SELECT key, count, month_key FROM usage"); usage = new Map(); for (const row of result.rows) { usage.set(row.key, { count: row.count, monthKey: row.month_key }); } logger.info(`Loaded usage data for ${usage.size} keys from PostgreSQL`); } catch (error) { logger.info("No existing usage data found, starting fresh"); usage = new Map(); } } async function saveUsageEntry(key, record) { try { await pool.query(`INSERT INTO usage (key, count, month_key) VALUES ($1, $2, $3) ON CONFLICT (key) DO UPDATE SET count = $2, month_key = $3`, [key, record.count, record.monthKey]); } catch (error) { logger.error({ err: error }, "Failed to save usage data"); } } export function usageMiddleware(req, res, next) { const keyInfo = req.apiKeyInfo; const key = keyInfo?.key || "unknown"; const monthKey = getMonthKey(); if (isProKey(key)) { trackUsage(key, monthKey); next(); return; } const record = usage.get(key); if (record && record.monthKey === monthKey && record.count >= FREE_TIER_LIMIT) { res.status(429).json({ error: "Free tier limit reached", limit: FREE_TIER_LIMIT, used: record.count, upgrade: "Upgrade to Pro for unlimited conversions: https://docfast.dev/pricing", }); return; } trackUsage(key, monthKey); next(); } function trackUsage(key, monthKey) { const record = usage.get(key); if (!record || record.monthKey !== monthKey) { const newRecord = { count: 1, monthKey }; usage.set(key, newRecord); saveUsageEntry(key, newRecord).catch((err) => logger.error({ err }, "Failed to save usage entry")); } else { record.count++; saveUsageEntry(key, record).catch((err) => logger.error({ err }, "Failed to save usage entry")); } } export function getUsageStats(apiKey) { const stats = {}; if (apiKey) { const record = usage.get(apiKey); if (record) { const masked = apiKey.slice(0, 8) + "..."; stats[masked] = { count: record.count, month: record.monthKey }; } } return stats; }