Repository navigation
feat: admin proxy pool with per-proxy device capacity and automatic assignment - #376
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #366
Summary
Adds a proxy pool that an admin manages from the dashboard or the admin API. Each proxy has a
max_deviceslimit. When a session connects without its own proxy, wuzAPI assigns the least-loaded enabled pool proxy with free capacity, saves the choice on the user, and applies it through the existing per-session proxy path. The assignment is sticky: a number keeps the same IP on every reconnect.With an empty pool, behaviour is identical to today. Explicit per-user proxies (
proxyConfig,POST /session/proxy) keep working exactly as before and are never touched by the pool.Behaviour
POST /session/connectassigned / max_devices), saved on the user503 proxy pool exhausted(default), or connects without a proxy ifWUZAPI_PROXY_POOL_FALLBACK=directSELECT … FOR UPDATEon the pool rows on PostgreSQL), so concurrent connects cannot overfill a proxy.Admin API
GET/admin/proxy-poolassigned_count. Proxy passwords are masked.POST/admin/proxy-pool{label, proxy_url, max_devices, enabled}. Samehttp/socks5validation asSetProxy.PUT/admin/proxy-pool/{id}max_devicescannot go below the assigned count. Changingproxy_urlupdates the assigned users (used on their next connect).DELETE/admin/proxy-pool/{id}409while sessions are still assignedPOST/admin/users/{id}/proxy-pool/releaseGET /admin/usersandGET /session/statusnow also returnproxy_pool_id. Documented inAPI.mdandstatic/api/spec.yml.Configuration
WUZAPI_PROXY_POOL_FALLBACK(flag-proxypoolfallback):block(default) ordirect. It only applies when the pool has enabled proxies but all are full.directconnects without a proxy and saves nothing, so the next connect tries the pool again. An invalid value stops startup, likeWUZAPI_AUTO_PRESENCE. Added to the README,.env.sampleand both compose files.Database
Migration 14 (
add_proxy_pool), for both PostgreSQL and SQLite:proxy_pool (id, label, proxy_url UNIQUE, max_devices CHECK >= 1, enabled, created_at)users.proxy_pool_id→proxy_pool(id) ON DELETE RESTRICT, indexedTested upgrading an existing PostgreSQL database and on a fresh SQLite one.
Dashboard
assigned / maxbar, status, Edit / Disable / Delete (Delete is disabled while proxies are assigned).Testing
go test -race ./...,go vet,gofmtandgo mod tidyare clean.proxy_pool_test.go: empty and disabled-only pool keep current behaviour, least-loaded selection and capacity, stickiness, explicit proxy wins, 10 concurrent assignments against 3 slots never overfill, admin CRUD and validation,EditUserexplicit proxy leaves the pool, fallback parsing, andblockvsdirect.503), stickiness across reconnect, release and slot reuse, and all dashboard flows.Notes for review
/session/connect. Kept startup unchanged on purpose; happy to change it.POST /session/proxynow also rejects a proxy URL with no host (e.g.socks5://).?v=so browsers don't use old cached JS.