Metered commercial HTTPS edge (portal, admin, Stripe)
  • TypeScript 71.1%
  • JavaScript 20.5%
  • HTML 8.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
George Lambert 2f524deea0 feat(auth): password change + reset with emailed 6-digit codes for portal and admin
Customer portal:
- POST /portal/api/password/change (session): currentPassword -> emailed code;
  then {code,newPassword,totpCode?}. TOTP required when enabled. Aliases
  /password/change/start|confirm. Revokes all sessions, returns a new token,
  sends a 'password was changed' notice. Security tab UI replaces the
  'handled by support' card. Capabilities: passwordChange, loginIdentifier=email.

Admin console:
- admin_users gains email (unique, case-insensitive), totp_secret, totp_enabled
  (additive migration). ADMIN_USER_EMAILS backfill env (never overwrites).
- Login by email or legacy username; optional TOTP enforced at login.
- POST /admin/api/password/forgot|reset (no enumeration, emailed code, TOTP if on).
- /admin/api/account: password change (current pw -> code -> new pw), verified
  email change (code to new address), TOTP setup/enable/disable, revoke-others.
- POST /users takes {email,password}; email becomes the username.
  PUT /users/:username/email for other admins. Deactivate revokes sessions.
- UI: Forgot password on sign-in, TOTP prompt, Users -> My account, Set email.

Email-as-username: admin customer create validates/lower-cases email (portal
sign-in name, dup -> 409) and the UI asks for it; portal labels updated.

Shared: src/email-codes.ts, src/admin-sessions.ts, src/admin-account.ts.
Docs: docs/PASSWORD-2FA.md. Tests: tests/password-change-2fa.test.ts.
2026-10-05 14:00:47 -04:00
admin feat(auth): password change + reset with emailed 6-digit codes for portal and admin 2026-10-05 14:00:47 -04:00
client sync: import live /opt/verae/zappier-edge state (2026-10-05) into git 2026-10-05 13:50:26 -04:00
docs feat(auth): password change + reset with emailed 6-digit codes for portal and admin 2026-10-05 14:00:47 -04:00
portal feat(auth): password change + reset with emailed 6-digit codes for portal and admin 2026-10-05 14:00:47 -04:00
src feat(auth): password change + reset with emailed 6-digit codes for portal and admin 2026-10-05 14:00:47 -04:00
tests feat(auth): password change + reset with emailed 6-digit codes for portal and admin 2026-10-05 14:00:47 -04:00
zapier-app Initial import of zappier-edge from zapier monorepo 2026-09-11 19:53:10 -04:00
.DS_Store Initial import of zappier-edge from zapier monorepo 2026-09-11 19:53:10 -04:00
.env.example Initial import of zappier-edge from zapier monorepo 2026-09-11 19:53:10 -04:00
.gitignore Initial import of zappier-edge from zapier monorepo 2026-09-11 19:53:10 -04:00
Dockerfile Initial import of zappier-edge from zapier monorepo 2026-09-11 19:53:10 -04:00
jest.config.js Initial import of zappier-edge from zapier monorepo 2026-09-11 19:53:10 -04:00
NATS.md Initial import of zappier-edge from zapier monorepo 2026-09-11 19:53:10 -04:00
openapi.yaml sync: import live /opt/verae/zappier-edge state (2026-10-05) into git 2026-10-05 13:50:26 -04:00
package-lock.json sync: import live /opt/verae/zappier-edge state (2026-10-05) into git 2026-10-05 13:50:26 -04:00
package.json sync: import live /opt/verae/zappier-edge state (2026-10-05) into git 2026-10-05 13:50:26 -04:00
README.md sync: import live /opt/verae/zappier-edge state (2026-10-05) into git 2026-10-05 13:50:26 -04:00
SUMMARY.md Initial import of zappier-edge from zapier monorepo 2026-09-11 19:53:10 -04:00
tsconfig.json Initial import of zappier-edge from zapier monorepo 2026-09-11 19:53:10 -04:00

Zappier

Metered API platform: per-endpoint pricing, customer types with multipliers and monthly credits, a usage ledger, Stripe metered billing, purchase-order invoicing, a company admin console, a self-service customer portal, and a Zapier integration.

This is the billing and user platform (originally the standalone zappier git tree at ~/zappier). Forgejo name: zappier-edge. It owns signup, TOTP, API keys, rate card, Stripe meter, PO invoices, admin users, and the customer portal. Verae middleware does not replace this.

Forgejo: https://git.georgelambert.org/marchon/zappier-edge
Catalog README: https://zapier.georgelambert.org/packages/zappier/README.pdf
Bring the whole system online: https://zapier.georgelambert.org/packages/verae-ops/GETTING-STARTED.pdf

Documentation (public PDFs)

Surfaces

Surface URL Audience
Public API /v1/* API customers (x-api-key)
Interactive API docs /docs Integrating developers
Admin console /admin Company ops & accounting
Customer portal /portal End-user customers (signup, 2FA, billing)
Zapier app zapier-app/ No-code users via Zapier

Pricing model

openapi.yaml defines the API surface; each operationId is a rate-card key. Endpoints carry list prices (seed: src/pricing.ts → DEFAULT_RATE_CARD). Customer types are tier configs (DEFAULT_TIERS) with a multiplier, a monthlyCreditCents quota, and an optional defaultRule for endpoints not on the card. Individual customers can carry a multiplierOverride. Billed price = round(list price × multiplier); usage up to the monthly credit is free. Pricing is editable at runtime in the admin console.

Seed rate card (list prices, cents per call)

Endpoint Model List price
status free 0
storage-list free 0
transform fixed 4
storage variable 10 + 1 per KB metadata + 50 per MB attached

Seed customer types

Tier Multiplier Monthly credit Default rule (unlisted endpoints)
free 1.0 100 cents none — call rejected with 403
pro 0.5 1000 cents fixed 8 list → 4 billed
business 0.25 10000 cents fixed 8 list → 2 billed

Adding a new API call = add it to openapi.yaml, then price it in the admin UI. Adding a customer type = create it in the admin UI. Variable pricing = base per call + metadata size (rounded up to KB) + attachment size (rounded up to MB), then the multiplier.

Quickstart

npm install
npm run dev

The server starts on port 3000. API docs at http://localhost:3000/docs, admin console at /admin, customer portal at /portal.

Deploying

npm ci && npm run build
node dist/index.js        # runs from ANY working directory

All runtime paths (SQLite default, .env, OpenAPI spec, static assets) resolve from the installation root, so the compiled server works under systemd, Docker, or cron regardless of cwd. PORT and ZAPPIER_DB remain environment-overridable.

Environment variables

Variable Default Purpose
PORT 3000 HTTP port the server listens on
ZAPPIER_DB <root>/zappier.db SQLite database file path
ADMIN_KEY admin-dev-key Admin UI / admin API key — set a real secret in production
ADMIN_USER admin Admin UI primary login username
DEMO_ADMIN_USER demo Admin UI demo login username
DEMO_ADMIN_PASSWORD $$$Adm1n### Demo login password — override in production
STRIPE_SECRET_KEY (none) Stripe secret key — billing job and portal reloads

Billing

Usage is reported to Stripe by a job (loads STRIPE_SECRET_KEY from .env):

npx ts-node src/jobs/report-usage.ts

The job sums each customer's usage since the first of the current month (UTC), applies the tier's monthly credit, and reports only the delta above what was already reported — re-runs are safe. Idempotency comes from three layers: a billing_reports ledger (cumulative cents per customer per month), an atomic job_locks run guard (1 h TTL), and a deterministic Stripe event identifier (customer:period:billable) that dedupes crash retries. It requires a Stripe meter named zappier.api_cents with Sum aggregation over the value field.

A Kimi cron job ("Zappier billing · report usage to Stripe") runs it daily at 06:17 America/New_York with a completion notification.

Purchase-order customers are invoiced manually from the admin console (Invoices tab); prepaid balances from the customer portal are drawn down automatically at invoice issue. See docs/ACCOUNTING.md.

Zapier app

The companion Zapier integration lives in zapier-app/:

cd zapier-app
npm install
npm test

To deploy it, create a Zapier developer account, run zapier login, then zapier push from the zapier-app/ directory.

Testing

npm test                    # root API/service suite (jest, 165 tests)
cd zapier-app && npm test   # Zapier integration suite (mocha, 4 tests)