Spaces:
Running
Running
| 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)), | |
| }; | |
| } | |