UpstreamOps is a centralized monitoring and operations dashboard for NewAPI and Sub2API upstream sites. It helps manage upstream accounts, balances, spending, model or group rates, rate changes, upstream API keys, recharge and redeem workflows, subscriptions, announcements, and notification alerts.
UpstreamOps is not a model proxy or request forwarding gateway. It is an operations console for maintaining multiple upstream admin panels from one place.
This project is based on worryzyy/upstream-hub. Thanks to @worryzyy for the original open-source work.
Click to expand
![]() |
Thanks to 触摸云 for sponsoring this project. 触摸云 provides overseas cloud computing services, including Hong Kong cloud servers, US high-defense servers, physical servers, protection services, acceleration CDN, and self-developed CDN systems. UpstreamOps users can use this link. |
When you maintain multiple NewAPI or Sub2API upstream accounts, balance, spending, rates, announcements, API keys, subscriptions, and recharge entry points are usually scattered across different admin panels. Manually logging in one by one is repetitive and can easily miss low balances, rate changes, login failures, expiring subscriptions, or upstream announcements.
UpstreamOps focuses on these problems:
- Centralized status view: balances, spending, rates, announcements, subscriptions, and abnormal states across multiple upstreams.
- Less manual checking: scheduled balance, spending, rate, and subscription usage synchronization.
- Faster risk detection: low balances, rate changes, login failures, monitor failures, low subscription quota, and expiring subscriptions can be pushed through notifications.
- Historical tracking: rate changes, balance snapshots, notification logs, and upstream announcements are stored locally.
- Easier operations: API key management, recharge, redeem, subscription purchase, and renewal are available from one entry point.
- Complex network support: global proxy support with per-upstream, per-notification-channel, and per-captcha-provider proxy switches.
- Supports NewAPI and Sub2API upstreams.
- Supports username/password credentials and token/cookie credentials.
- Enables or disables monitoring per channel.
- Supports custom channel sort order; higher values are displayed and monitored first.
- Configures low-balance alert thresholds.
- Tests login and manually syncs balances and rates.
- Supports extra login form parameters for modified NewAPI or Sub2API login endpoints.
- Supports Cloudflare Turnstile solving for upstream login flows.
- Opens upstream site URLs directly from channel cards.
- Supports clearing saved login information from channel cards.
- Deleting a channel cleans related snapshots, rates, announcements, notification cooldowns, and notification logs.
- Shows total balance, today spending, total spending, lowest-balance channel, and abnormal channel count.
- Periodically collects balance and spending data.
- Displays balance history trends.
- Pushes notifications when balance falls below the configured threshold.
- Supports cooldown for repeated low-balance alerts.
- Supports recharge multiplier conversion for balance, spending, and redeem values, using either the upstream multiplier or a manual divide/multiply mode.
- Syncs upstream model or group rates.
- Stores current rate snapshots.
- Records rate change history.
- Supports paginated rate change history and channel filters.
- Sends rate change notifications.
- Merges multiple rate changes from the same scan into one notification.
- Merges added and removed groups in the same scan into one structure-change notification.
- Filters small rate changes by minimum percentage.
- Supports notification subscriptions filtered by upstream channel and rate group.
- Provides a full channel group overview with search and sorting by channel or rate.
For Sub2API upstream channels, UpstreamOps provides subscription lifecycle management and usage monitoring:
- Queries upstream subscription plans and payment methods.
- Purchases or renews subscriptions.
- Supports QR code, redirect URL, and form-submit payment launch modes.
- Queries daily, weekly, and monthly quota limits, used amount, remaining amount, and remaining percentage.
- Shows subscription expiration time, remaining days, and status.
- Sends low remaining-quota alerts for daily, weekly, and monthly windows.
- Sends expiring-subscription alerts.
- Supports cooldown for repeated subscription alerts.
- Provides summary cards and detail dialogs in the frontend.
- Supports CapSolver, 2Captcha, AntiCaptcha, and YesCaptcha.
- Queries captcha provider account balances.
- Refreshes one provider balance manually.
- Refreshes all provider balances in batch.
- Shows balance value, balance unit, refresh time, and error message.
- Supports HTTP, HTTPS, and SOCKS5 proxies.
- Supports proxy username and password.
- Allows upstream channels, notification channels, and captcha providers to opt in separately.
- Allows version checks to use the proxy separately.
- Configures upstream request timeout and
User-Agent. - Provides proxy connectivity testing in the system settings page.
- Syncs NewAPI announcements from
/api/statusand/api/notice. - Syncs Sub2API user-visible announcements from
/api/v1/announcements. - Announcement sync runs with rate sync and does not require a separate cron task.
- The first sync only creates a baseline and does not push historical announcements.
- New announcements are stored locally and pushed through notification channels.
- Shows recent announcements on the dashboard.
- Supports paginated announcement queries and detail views.
- Renders announcement details as Markdown.
- Cleans up related announcements when an upstream channel is deleted.
- Supports retention-based announcement cleanup.
- Supports channel-level
ignore_announcements.
Supported notification channels:
- Telegram
- Webhook
- WeCom
- DingTalk
- Feishu
- ServerChan3
Notification channels support subscription filters:
- Empty or
[]: receive all events. mode=all: receive all events from selected upstreams.mode=groups: receive only selected rate groups for rate-related events. Announcement, balance, login failure, and monitor failure events are still filtered by upstream channel.
From each channel card, you can manage upstream API keys:
- List API keys.
- Search by name or key.
- Filter by status.
- Create API keys.
- Edit name, group, status, quota, expiration time, IP allowlist or blocklist, model restrictions, and related fields.
- Delete API keys.
- Reveal and copy full keys.
Available fields depend on the upstream type and its API capability.
From each channel card, you can handle upstream recharge and redeem workflows:
- Query upstream recharge configuration.
- Supports upstream-provided payment methods such as Alipay and WeChat Pay.
- Supports QR code, redirect URL, and form-submit payment launch modes.
- Prefers QR code on desktop and redirect on mobile.
- Redeems redeem codes online.
- Shows returned balance, concurrency, group subscription, validity period, and related results.
- Sub2API channels additionally support subscription purchase and renewal.
The system settings page manages:
- Admin login authentication.
- Admin username and password.
- Token signing secret.
- Balance sync cron.
- Rate sync cron.
- Scheduler concurrency.
- Monitor log, balance snapshot, notification log, and announcement retention.
- Rate change notification merge policy.
- Minimum rate change percentage for notifications.
- Low-balance alert cooldown.
- Daily, weekly, and monthly subscription remaining percentage thresholds.
- Subscription expiration threshold.
- Subscription alert cooldown.
- Maximum notification retry attempts.
- Global proxy configuration.
- Proxy connectivity test.
- Version check result notification.
- Upstream request timeout and
User-Agent. - Notification channels.
- Captcha providers.
Saving writes the configuration file. Applying settings hot-reloads authentication, scheduler, notification policy, proxy, and upstream HTTP settings. Notification channels and captcha providers take effect immediately after database writes.
SQLite is the default deployment mode.
cp .env.example .envEdit .env and set at least:
APP_SECRET=replace-with-a-random-string-at-least-32-bytesAPP_SECRET is used to encrypt sensitive fields with AES-GCM, including upstream passwords, tokens, cookies, notification channel secrets, and captcha provider API keys. If you change it later, existing encrypted data cannot be decrypted.
Compose defaults to admin login enabled. Before the first start, set a strong admin password:
ADMIN_USERNAME=admin
ADMIN_PASSWORD=replace-with-a-strong-passwordFor trusted internal-only deployments, disable it explicitly:
AUTH_ENABLED=falseDocker pulls ghcr.io/ifty-r/upstream-ops:${IMAGE_TAG:-latest} by default. Configuration and data are stored in the host data/ directory.
Start:
docker compose up -dDefault URL:
http://localhost:8080
Default database file inside the container:
/app/data/upstream-ops.db
The host file is data/upstream-ops.db. Runtime system settings are persisted to data/config.yaml.
The default image tag comes from .env:
IMAGE_TAG=latestFor production, pin a specific version:
IMAGE_TAG=v0.0.6Use the MySQL compose file together with the base compose file:
docker compose -f docker-compose.yml -f docker-compose.mysql.yml up -dRequired .env values:
APP_SECRET=replace-with-a-random-string-at-least-32-bytes
MYSQL_DATABASE=upstreamops
MYSQL_USER=upstreamops
MYSQL_PASSWORD=replace-with-database-password
MYSQL_ROOT_PASSWORD=replace-with-root-password
MYSQL_PORT=33069HTTP_PORT=8080
IMAGE_TAG=latest
SERVER_MODE=release
LOG_LEVEL=infoHTTP_PORT: host port.IMAGE_TAG: Docker image tag.SERVER_MODE: Gin mode, usuallyrelease.LOG_LEVEL: log level.
SQLite:
DATABASE_DRIVER=sqlite
DATABASE_PATH=/app/data/upstream-ops.dbMySQL:
DATABASE_DRIVER=mysql
DATABASE_HOST=mysql
DATABASE_PORT=3306
DATABASE_USER=upstreamops
DATABASE_PASSWORD=change-me
DATABASE_NAME=upstreamopsAPP_SECRET=please-change-me-to-a-long-random-secret-32bytes-min
AUTH_ENABLED=true
ADMIN_USERNAME=admin
ADMIN_PASSWORD=
AUTH_TOKEN_SECRET=APP_SECRET: required master secret.AUTH_ENABLED: admin login switch. Compose defaults totrue; setfalseexplicitly only for trusted internal deployments.ADMIN_USERNAME: admin username.ADMIN_PASSWORD: admin password. Required wheneverAUTH_ENABLED=true.AUTH_TOKEN_SECRET: token signing secret. Falls back toAPP_SECRETwhen empty.
Use the project scripts to start, stop, and restart both the backend and frontend:
node .\scripts\dev-start.mjsnode .\scripts\dev-stop.mjsnode .\scripts\dev-restart.mjsDefaults: data/upstream-ops.db for SQLite, http://127.0.0.1:8418 for the backend, http://127.0.0.1:3010 for the frontend, and tmp/dev/ for PID state and logs.
See scripts/README.md for options.
Backend:
go run ./cmd/serverDefault backend port:
8418
Frontend:
cd frontend
pnpm install
pnpm devDefault frontend development URL:
http://127.0.0.1:3010
Checks:
go test ./...cd frontend
pnpm buildSystem settings can configure global proxy and upstream request settings. Proxy is disabled by default, protocol defaults to http, upstream timeout defaults to 30 seconds, and User-Agent is left empty so each connector can choose an appropriate default.
Configuration fields:
proxy:
enabled: false
versionCheckEnabled: false
protocol: http
host: 127.0.0.1
port: 7890
username: ""
password: ""
upstream:
timeoutSeconds: 30
userAgent: ""
shopRequestIntervalMilliseconds: 1500
shopInfoTTLHours: 24proxy.enabled: enables global proxy.proxy.versionCheckEnabled: routes version checks through proxy.proxy.protocol:http,https, orsocks5.proxy.host/proxy.port: proxy host and port.proxy.username/proxy.password: optional proxy authentication.upstream.timeoutSeconds: upstream request timeout.upstream.userAgent: custom upstream requestUser-Agent; leave empty to use the connector default.upstream.shopRequestIntervalMilliseconds: minimum delay between requests to the same shop origin; defaults to 1500 ms.upstream.shopInfoTTLHours: shop metadata cache lifetime; inventory sync skips the shop info endpoint while the cache is valid.- When
proxy.enabled=false, per-channelproxy_enabledsettings do not take effect.
Proxy test endpoint:
POST /api/settings/proxy/test
Upstream channels can enable proxy_enabled individually. Upstream login, balance sync, rate sync, announcement sync, API key management, recharge, redeem, and subscription APIs use proxy only when both global proxy and channel proxy are enabled.
NewAPI supports two credential modes.
Username/password mode:
- Provide upstream site URL, username, and password.
- If the login endpoint requires extra fields, provide a JSON object in extra form parameters.
- If Turnstile is enabled, configure a captcha provider first, then enable Turnstile in the channel.
Token/cookie mode:
{
"cookie": "session=xxx; other=yyy",
"user_id": "123"
}NewAPI token mode also supports the system access token (user.access_token, the 32 character token generated from the personal settings page). Use access_token instead of cookie. Cookie and access token are mutually exclusive, but user_id is always required:
{
"access_token": "your-system-access-token",
"user_id": "123"
}When editing a NewAPI token/cookie channel, the form shows the saved user_id for reuse, while the saved cookie or access token remains hidden.
Sub2API supports username/password mode and token mode.
Token mode credentials:
{
"access_token": "your-access-token",
"refresh_token": "your-refresh-token"
}refresh_token is optional but recommended. When present, Sub2API sessions and token-mode credentials can be refreshed automatically after access-token expiration. Without refresh_token, paste updated credentials when the token expires.
The channel card menu provides a clear-login action:
- Password mode: clears only cached login sessions.
- Token mode: clears cached sessions and the saved token/cookie credential JSON.
Notification secrets, webhooks, and SMTP passwords are encrypted at rest. Add or edit a notification channel with the JSON configuration matching its type.
Notification channels can enable proxy_enabled individually. Telegram, Webhook, WeCom, DingTalk, Feishu, and ServerChan3 requests use proxy only when both global proxy and notification-channel proxy are enabled.
{
"bot_token": "1234567890:AAEh...",
"chat_id": "-1001234567890"
}{
"url": "https://example.com/hook",
"method": "POST",
"headers": {
"Authorization": "Bearer xxx"
}
}Webhook body example:
{
"event": "announcement",
"subject": "[UpstreamOps] xxx",
"body": "notification body",
"extra": {}
}{
"host": "smtp.example.com",
"port": 465,
"use_tls": true,
"username": "alert@example.com",
"password": "smtp-password-or-app-password",
"from": "alert@example.com",
"to": ["ops@example.com"]
}{
"webhook_url": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxx"
}{
"webhook_url": "https://oapi.dingtalk.com/robot/send?access_token=xxx",
"secret": "SEC..."
}{
"webhook_url": "https://open.feishu.cn/open-apis/bot/v2/hook/xxxx",
"secret": "..."
}{
"uid": "your UID",
"sendkey": "sctp_xxx"
}Messages are sent through https://{uid}.push.ft07.com/send/{sendkey}.send.
Notification channels can limit which upstreams, events, or rate groups they receive. Empty value, empty string, null, or [] means all upstreams and all events.
[
{ "channel_ids": [1, 2], "mode": "all" },
{ "channel_ids": [3], "mode": "groups", "groups": ["default", "pro"], "events": ["rate_changed"] },
{ "channel_ids": [4], "mode": "all", "events": ["announcement", "monitor_failed"] }
]channel_ids: upstream channel ID list. Historicalchannel_idsingle-value rules are still accepted.events: event type list. Empty means all events for that upstream.mode=all: receive all rate groups.mode=groups: receive only selected groups for rate-related events.
balance_low: balance below threshold.rate_changed: rate changed.rate_structure_changed: group structure changed.rate_added: group added. Kept for historical compatibility.rate_removed: group removed. Kept for historical compatibility.announcement: new upstream announcement.login_failed: login failed.captcha_failed: captcha solving failed.monitor_failed: balance, spending, or rate collection failed.subscription_daily_remaining_low: daily subscription remaining quota below threshold.subscription_weekly_remaining_low: weekly subscription remaining quota below threshold.subscription_monthly_remaining_low: monthly subscription remaining quota below threshold.subscription_expiring: subscription is about to expire.
Announcement list:
GET /api/announcements?page=1&page_size=20
Notification logs:
GET /api/notifications/logs?page=1&page_size=20
Notification log rows include the upstream channel ID when the event is tied to a specific upstream channel.
Rate change logs:
GET /api/rate-changes?page=1&page_size=20
GET /api/rate-changes?channel_id=1&page=1&page_size=20
Channels:
GET /api/channels?page=1&page_size=20
GET /api/channels?page=1&page_size=-1
POST /api/channels/:id/clear-login-info
Recharge:
GET /api/channels/:id/recharge-info
POST /api/channels/:id/recharge
Redeem:
POST /api/channels/:id/redeem
Subscription:
GET /api/channels/:id/subscription-info
POST /api/channels/:id/subscription
GET /api/channels/:id/subscription-usage
Captcha providers:
GET /api/captcha-configs
POST /api/captcha-configs
PUT /api/captcha-configs/:id
POST /api/captcha-configs/:id/refresh-balance
DELETE /api/captcha-configs/:id
SSE progress endpoints:
POST /api/channels/:id/test-login
POST /api/channels/:id/sync
POST /api/channels/sync-all
The system settings page supports runtime hot reload without restarting the service.
Hot-reloadable modules:
appauthschedulernotificationsretentionproxyupstream
Database connection, HTTP port, and log level still require restart.
Default schedules:
- Balance sync: every 15 minutes.
- Rate sync: every 30 minutes.
- Subscription usage check: runs with balance sync.
- Captcha balance refresh: scheduled and manual refresh are supported.
- History cleanup: daily.
Default retention:
- Monitor logs: 30 days.
- Balance snapshots: 90 days.
- Notification logs: 90 days.
- Upstream announcements: controlled by announcement retention days.
0disables cleanup. - High-frequency shop changes (
stock_changed,monitor_failed): 15 days. - Other shop changes: 90 days.
- Shop monitor logs and completed sync jobs: 30 days.
- Current shop goods snapshots are not cleaned by retention.
- The settings page can run shop history cleanup immediately with the current form values and reports deleted rows per category.
- Rate change logs are not cleaned by default.
The following sensitive fields are encrypted with APP_SECRET:
- Upstream account passwords.
- NewAPI cookies.
- Sub2API access tokens.
- Login session cookies and tokens.
- Notification channel secrets.
- SMTP passwords.
- Captcha provider API keys.
Important:
APP_SECRETmust remain stable.- Changing
APP_SECRETmakes existing encrypted data undecryptable. - Back up
.envor configuration files together with the database.
Check whether the backend service is running and whether reverse proxy routes /api/* correctly.
Frontend development URL:
http://127.0.0.1:3010
Backend URL:
http://127.0.0.1:8418
Check the site URL, username, password, Turnstile requirement, captcha provider configuration, and whether token or cookie credentials have expired.
Check whether the first announcement baseline sync has completed, rate sync runs successfully, notification channels are enabled, subscription rules include the upstream, and failed notification logs exist.
Check the minimum change percentage, notification subscription groups, and rate change history.
Added and removed groups are merged into a rate_structure_changed notification for the same scan. If a notification channel uses mode=groups, the added/removed list is filtered by subscribed groups before generating the notification.
The first rate sync only creates a baseline and does not push all existing groups as newly added groups.
Check the low-balance alert cooldown in system settings. Cooldown state is stored in the database and survives restarts.
MIT







