# LibreChat Balance Manager Admin dashboard for managing LibreChat user token balances. View, search, top up, and bulk-manage user credits. ## Features - **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 common token amounts - **CSV Export** — download balance data from the All Balances view - **API Key Auth** — simple key-based authentication for the admin dashboard ## Install on Dev Server ### 1. Clone the repo ```bash cd /opt git clone https://x-token-auth:YOUR_TOKEN@bitbucket.org/zlalani/librechat-balances.git Balance-Manager ``` 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 ```bash cd /opt/Balance-Manager cp .env.example .env nano .env ``` 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 ``` | 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 ```bash cd /opt/Balance-Manager docker compose up -d --build ``` ### 4. Verify it's running ```bash docker logs librechat-balance-manager ``` You should see: ``` Connected to MongoDB Balance Manager running on http://localhost:3002 ``` ### 5. NGINX setup (recommended) Add this to your NGINX config (`/opt/LibreChat/client/nginx.conf`) inside the `server` block, **before** the `location /` block: ```nginx location /balance-manager/ { proxy_pass http://balance-manager:3002/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } ``` Then restart NGINX: ```bash docker restart LibreChat-NGINX ``` Access at: `https://chat-dev.oliver.solutions/balance-manager/` 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. ## How It Works ### Credit Request Flow 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 git pull https://x-token-auth:YOUR_TOKEN@bitbucket.org/zlalani/librechat-balances.git main docker compose up -d --build ``` ## Troubleshooting **Container won't connect to MongoDB:** ```bash docker network inspect librechat_default | grep balance ``` If not listed, make sure `docker-compose.yml` has: ```yaml networks: librechat_default: external: true ``` **Port conflict:** Change the port in both `.env` (`PORT=3003`) and `docker-compose.yml` (`127.0.0.1:3003:3003`). **Reset API key in browser:** ```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