| import fs from 'fs/promises'; |
| import path from 'path'; |
|
|
| const DATA_DIR = '/data'; |
| const STORE_FILE = path.join(DATA_DIR, 'web-search-usage.json'); |
| const APP_TIMEZONE = process.env.APP_TIMEZONE || 'America/New_York'; |
|
|
| let state = { days: {} }; |
| let loaded = false; |
| let saveChain = Promise.resolve(); |
|
|
| function todayKey() { |
| return new Intl.DateTimeFormat('en-CA', { |
| timeZone: APP_TIMEZONE, |
| year: 'numeric', |
| month: '2-digit', |
| day: '2-digit', |
| }).format(new Date()); |
| } |
|
|
| function pruneDays() { |
| const keepKey = todayKey(); |
| state.days = Object.fromEntries( |
| Object.entries(state.days || {}).filter(([key]) => key === keepKey) |
| ); |
| } |
|
|
| async function ensureLoaded() { |
| if (loaded) return; |
| loaded = true; |
| try { |
| await fs.mkdir(DATA_DIR, { recursive: true }); |
| const raw = await fs.readFile(STORE_FILE, 'utf8'); |
| const parsed = JSON.parse(raw); |
| if (parsed && typeof parsed === 'object') state = parsed; |
| } catch {} |
| pruneDays(); |
| } |
|
|
| function saveState() { |
| saveChain = saveChain.then(async () => { |
| pruneDays(); |
| await fs.mkdir(DATA_DIR, { recursive: true }); |
| await fs.writeFile(STORE_FILE, JSON.stringify(state, null, 2), 'utf8'); |
| }).catch((err) => { |
| console.error('Failed to persist web search usage:', err); |
| }); |
| return saveChain; |
| } |
|
|
| function getCounterRecord(key) { |
| const day = todayKey(); |
| if (!state.days[day]) state.days[day] = {}; |
| if (!state.days[day][key]) state.days[day][key] = 0; |
| return { |
| day, |
| used: state.days[day][key], |
| set(nextValue) { |
| state.days[day][key] = nextValue; |
| }, |
| }; |
| } |
|
|
| export async function getWebSearchUsage(key, limit = 15) { |
| await ensureLoaded(); |
| const record = getCounterRecord(key); |
| return { |
| limit, |
| used: record.used, |
| remaining: Math.max(0, limit - record.used), |
| window: record.day, |
| period: 'daily', |
| }; |
| } |
|
|
| export async function consumeWebSearchUsage(key, limit = 15) { |
| await ensureLoaded(); |
| const record = getCounterRecord(key); |
| if (record.used >= limit) { |
| return { |
| allowed: false, |
| ...(await getWebSearchUsage(key, limit)), |
| }; |
| } |
| record.set(record.used + 1); |
| await saveState(); |
| return { |
| allowed: true, |
| ...(await getWebSearchUsage(key, limit)), |
| }; |
| } |
|
|