From 4ebf0ba3aeca4d6493c35febdfbca613a96322c2 Mon Sep 17 00:00:00 2001 From: DJP Date: Fri, 27 Mar 2026 10:42:26 -0400 Subject: [PATCH] Update README with email notifications, history, and full setup guide Documents all new features: email approve/reject flow, credit requests, history tracking, low balance view. Includes complete env var reference, email security notes, and troubleshooting section. Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 131 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 91 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index ee198e7..d488649 100644 --- a/README.md +++ b/README.md @@ -7,20 +7,30 @@ Admin dashboard for managing LibreChat user token balances. View, search, top up - **Dashboard** — overview stats (total users, average balance, zero-balance users) - **All Balances** — paginated table sorted by credits, with per-user Top Up / Set actions - **Find User** — search by name or email (type "dave" to find daveporter@...) with instant results +- **Low Balance** — filter users below 1M/2M/3M/4M/5M token thresholds +- **Credit Requests** — users submit requests via a public form; admins approve/reject from the dashboard +- **Email Notifications** — automatic email to admins when a request is submitted, with one-click approve buttons +- **Email Approval** — approve (5M/10M/20M/custom) or reject directly from the email without logging in +- **History** — full audit trail of all balance changes, searchable by user email - **Bulk Operations** — add tokens to all users or set everyone to a specific amount -- **Preset Amounts** — quick buttons for 100K, 500K, 1M, 2M, 5M tokens +- **Preset Amounts** — quick buttons for common token amounts - **CSV Export** — download balance data from the All Balances view -- **API Key Auth** — simple key-based authentication +- **API Key Auth** — simple key-based authentication for the admin dashboard ## Install on Dev Server -### 1. Copy files to the server +### 1. Clone the repo ```bash -scp -r /path/to/Balance-Manager root@optical-librechat-dev:/opt/Balance-Manager +cd /opt +git clone https://x-token-auth:YOUR_TOKEN@bitbucket.org/zlalani/librechat-balances.git Balance-Manager ``` -Or clone/pull from your repo if you've pushed it. +Or with SSH: + +```bash +GIT_SSH_COMMAND="ssh -i ~/.ssh/djp1971" git clone git@bitbucket.org:zlalani/librechat-balances.git Balance-Manager +``` ### 2. Create the .env file @@ -36,10 +46,36 @@ Set your values: MONGO_URI=mongodb://mongodb:27017/LibreChat PORT=3002 API_KEY=your-secure-key-here + +# Webhook security +WEBHOOK_SECRET=your-random-secret-here +WEBHOOK_BASE_URL=https://chat-dev.oliver.solutions/balance-manager +WEBHOOK_TTL_HOURS=72 + +# SMTP (Mailgun or any SMTP server) +SMTP_HOST=smtp.mailgun.org +SMTP_PORT=587 +SMTP_USER=your-smtp-user +SMTP_PASS=your-smtp-password +SMTP_FROM=noreply@yourdomain.com + +# Notification recipients (comma-separated for multiple) +NOTIFY_TO=admin@yourdomain.com,another@yourdomain.com ``` -- `MONGO_URI` — points to the LibreChat MongoDB container (uses Docker network name `mongodb`) -- `API_KEY` — whatever secret key you want; you'll enter this in the browser on first load +| Variable | Description | +|---|---| +| `MONGO_URI` | LibreChat MongoDB connection (uses Docker network name `mongodb`) | +| `API_KEY` | Secret key for the admin dashboard — entered in browser on first load | +| `WEBHOOK_SECRET` | Random string for signing email approve/reject links. Generate with `openssl rand -hex 32` | +| `WEBHOOK_BASE_URL` | Public URL of the app (e.g. `https://chat-dev.oliver.solutions/balance-manager`) | +| `WEBHOOK_TTL_HOURS` | How long email approve links stay valid (default 72 hours) | +| `SMTP_HOST` | SMTP server hostname | +| `SMTP_PORT` | SMTP port (587 for TLS) | +| `SMTP_USER` | SMTP username | +| `SMTP_PASS` | SMTP password | +| `SMTP_FROM` | Sender email address | +| `NOTIFY_TO` | Comma-separated list of admin emails to receive request notifications | ### 3. Build and start @@ -61,21 +97,9 @@ Connected to MongoDB Balance Manager running on http://localhost:3002 ``` -### 5. Access the dashboard +### 5. NGINX setup (recommended) -The app runs on port **3002** (localhost only by default). - -**Option A — Direct access (if port is open):** - -``` -http://your-server-ip:3002 -``` - -To expose externally, change `127.0.0.1:3002:3002` to `0.0.0.0:3002:3002` in `docker-compose.yml`. - -**Option B — Add to NGINX (recommended):** - -Add this to your NGINX config (`/opt/LibreChat/client/nginx.conf`) inside the `server` block: +Add this to your NGINX config (`/opt/LibreChat/client/nginx.conf`) inside the `server` block, **before** the `location /` block: ```nginx location /balance-manager/ { @@ -95,28 +119,40 @@ docker restart LibreChat-NGINX Access at: `https://chat-dev.oliver.solutions/balance-manager/` -**Note:** If using the NGINX route, the balance-manager container must be on the same Docker network. This is already configured in `docker-compose.yml` via the `librechat_default` external network. +The balance-manager container must be on the same Docker network as NGINX. This is already configured in `docker-compose.yml` via the `librechat_default` external network. -### 6. Add to LibreChat's docker-compose.override.yml (optional) +## How It Works -If you'd prefer to manage it alongside LibreChat instead of as a separate compose project, add this service to `/opt/LibreChat/docker-compose.override.yml`: +### Credit Request Flow -```yaml - balance-manager: - build: /opt/Balance-Manager - container_name: librechat-balance-manager - ports: - - "127.0.0.1:3002:3002" - env_file: - - /opt/Balance-Manager/.env - restart: unless-stopped -``` +1. User visits `/balance-manager/request` and submits their email + OMG job number +2. An email is sent to all `NOTIFY_TO` recipients with approve buttons (5M, 10M, 20M, Custom, Reject) +3. Admin clicks a button in the email — a confirmation page opens in the browser +4. Admin clicks "Confirm" on the page — the request is approved and tokens are added +5. Alternatively, admins can approve/reject from the dashboard under the "Requests" tab + +### Email Security + +- Each button link is signed with HMAC-SHA256 using `WEBHOOK_SECRET` +- Links are scoped to a specific request ID + amount and expire after `WEBHOOK_TTL_HOURS` +- Clicking a link shows a confirmation page first (GET) — the actual approval only happens on form submit (POST) +- This prevents email security scanners (Microsoft Defender Safe Links, etc.) from auto-approving requests +- Links cannot be reused — once a request is processed, subsequent clicks show "Already Processed" + +### History / Audit Trail + +All balance changes are logged to `data/history.json`: +- Manual top-ups and balance sets from the admin dashboard +- Request approvals (from email or dashboard) +- Bulk operations + +Search by email in the "History" tab to see all changes for a user with totals. ## Updating ```bash cd /opt/Balance-Manager -docker compose down +git pull https://x-token-auth:YOUR_TOKEN@bitbucket.org/zlalani/librechat-balances.git main docker compose up -d --build ``` @@ -124,13 +160,11 @@ docker compose up -d --build **Container won't connect to MongoDB:** -Check that the container is on the `librechat_default` network: - ```bash docker network inspect librechat_default | grep balance ``` -If not listed, make sure the network section in `docker-compose.yml` has: +If not listed, make sure `docker-compose.yml` has: ```yaml networks: @@ -144,10 +178,27 @@ Change the port in both `.env` (`PORT=3003`) and `docker-compose.yml` (`127.0.0. **Reset API key in browser:** -Open browser console and run: - ```js localStorage.removeItem('bm_api_key') ``` Then refresh the page. + +**Emails not sending:** + +Check the container logs for SMTP errors: + +```bash +docker logs librechat-balance-manager +``` + +Common issues: +- Missing or incorrect SMTP credentials in `.env` +- Mailgun sandbox domains require recipients to be added as authorized +- `SMTP_FROM` must match a verified domain in Mailgun + +**Email approve links not working:** + +- Check `WEBHOOK_BASE_URL` matches your public URL exactly +- Ensure NGINX is proxying `/balance-manager/` traffic correctly +- Links expire after `WEBHOOK_TTL_HOURS` — check if the link has expired