← back to Dw Signup Fulfillment

lib/auto-approve-ledger.js

42 lines

'use strict';
// Daily auto-approve ledger — the volume backstop for guarded auto-approve.
// TRADE_AUTO_APPROVE instantly grants a `trade` account (trade pricing + free memo
// samples) to ANY valid submission, and the only other volume control is the 5/hr/IP
// rate limit — so a sprayer using unique emails from rotating IPs is otherwise
// unbounded. Persists a per-UTC-day {count} to data/auto-approve-ledger.json so a
// restart or a pm2 reload cannot reset the cap mid-day, and so there is an auditable
// record of how many trade accounts were auto-granted each day.
//
// Modeled deliberately on lib/mint-ledger.js (the gift-card money backstop) — same
// shape, same UTC-day keying, same restart-durability. Do not "simplify" this to an
// in-memory counter: that resets on every deploy.
const fs = require('fs');
const path = require('path');

// Path is overridable ONLY so tests can point at a throwaway file — production never
// sets this. A test that wrote to the real ledger would corrupt the live day's count
// (and an earlier test in this repo did exactly that class of damage, TK-11377).
const P = process.env.AUTO_APPROVE_LEDGER_PATH
  || path.join(__dirname, '..', 'data', 'auto-approve-ledger.json');

function today() { return new Date().toISOString().slice(0, 10); } // UTC YYYY-MM-DD
function read() { try { return JSON.parse(fs.readFileSync(P, 'utf8')); } catch { return {}; } }
function write(o) { fs.mkdirSync(path.dirname(P), { recursive: true }); fs.writeFileSync(P, JSON.stringify(o, null, 2)); }

function todayCount() { const d = read()[today()]; return d ? d.count : 0; }

// Record one auto-approve; returns the updated day record {count}.
// Counted at DISPATCH, not on success: an approve() that later fails has still consumed
// a slot. That errs toward under-approving, which is the safe direction for a control
// whose whole job is to bound how many trade accounts a bad actor can mint.
function record() {
  const o = read();
  const d = today();
  o[d] = o[d] || { count: 0 };
  o[d].count += 1;
  write(o);
  return o[d];
}

module.exports = { today, todayCount, record, PATH: P };