Partner promo codes: reusable codes that add free ad credits, tracked per redemption

promos.js (promo_codes + promo_redemptions, JSON fallback): create/update codes with
credits, partner, optional cap and expiry, on/off. Redeemed once per account either
from a join link ?promo=CODE (cookie, applied at signup) or the Overview box
'Have a promo code?' (/api/my/promo/redeem). Admin > Traffic gets a Partner promo
codes card (create, toggle, uses, recent redemptions). Chatbot line added.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-12 10:53:17 -05:00
parent 9b60e6414e
commit fc55714757
8 changed files with 179 additions and 2 deletions
+8
View File
@@ -144,6 +144,14 @@ async function bootstrap() {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`);
await q(`CREATE TABLE IF NOT EXISTS nudges (email VARCHAR(190) PRIMARY KEY, rung INT NOT NULL, ts BIGINT NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`);
await q(`CREATE TABLE IF NOT EXISTS digests (email VARCHAR(190) PRIMARY KEY, ts BIGINT NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`);
await q(`CREATE TABLE IF NOT EXISTS promo_codes (
code VARCHAR(24) NOT NULL PRIMARY KEY, credits INT NOT NULL, partner VARCHAR(80) NULL, note VARCHAR(200) NULL,
max_uses INT NOT NULL DEFAULT 0, expires BIGINT NOT NULL DEFAULT 0, active TINYINT NOT NULL DEFAULT 1, created BIGINT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`); // partner promo codes -> free ad credits
await q(`CREATE TABLE IF NOT EXISTS promo_redemptions (
code VARCHAR(24) NOT NULL, email VARCHAR(190) NOT NULL, credits INT NOT NULL, via VARCHAR(12) NOT NULL, ts BIGINT NOT NULL,
PRIMARY KEY (code, email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`);
await q(`CREATE TABLE IF NOT EXISTS page_hits (
day CHAR(10) NOT NULL, host VARCHAR(80) NOT NULL, path VARCHAR(40) NOT NULL, n INT NOT NULL DEFAULT 0,
PRIMARY KEY (day, host, path)