Commit Graph
28 Commits
Author SHA1 Message Date
aaaamoooo 5733928198 docs: report addendum for items 10-13
CI / test (push) Canceled after 0s
CI / anytls-smoke (push) Canceled after 0s
2026-10-21 11:18:00 +08:00
aaaamoooo 18bbd388ea feat(web): topbar/nav cleanup batch (items 10-13)
Item 10: remove the disabled decorative topbar search input (each page
has its own working search); CSS rule and media-query reference removed.
Item 11: menu/title renames — 服务器 -> 节点管理, 客户与订阅 -> 用户订阅,
转发与节点 -> 入站&出站, 设置与运维 -> 设置 (nav, TITLES/breadcrumb and
cross-references synced); the chains page add button is now 添加入站 and
its count reads N 个入站; the 安装与命令 page is merged into 设置
(commandsHTML block between the settings grid and the jobs table) and
the page itself is removed from nav, PAGES, TITLES, load() and the view
map — an old #commands hash now lands on the SPA's standard overview
fallback.
Item 12: remove the system page's 最近活动 panel (rendering and its
audit label map plus orphaned CSS); backend audit records untouched.
Item 13: the settings jobs table gets the w-jobs treatment — ID, status
and time cells never wrap, compact row padding, and the error column is
capped at 22% width so an empty error column no longer balloons.

Also fixes a class collision from the item-8 batch: the exit-mode
segmented control is renamed .seg -> .segctl so it no longer draws a
border box around the system page info-bar segments.
Golden re-captured: the only diff is the served index HTML inside
errors.badSubscription, audited character by character as exactly these
renames and removals.
2026-10-21 11:18:00 +08:00
aaaamoooo 25cb58245c docs: round-2 (v2) nine-item change report 2026-10-21 11:18:00 +08:00
aaaamoooo 0037116edc feat: manual landing endpoints for forward nodes (item 8)
A forward node's exit target is now a choice: a managed panel server
(existing behavior) or a manual landing endpoint — address + port +
protocol (VLESS / SS2022 / SOCKS5) + operator-supplied credentials,
including third-party nodes the panel does not run. The panel only
builds the entry-side sing-box outbound and route rule; there is no
exit resource, no exit job, and the node is exit-ready by definition.

- model: Node.ExitMode/ExitAddress; manual exits force fixed port mode
  and skip the exit-server port table entirely
- validation: address/port required; per-protocol credential presence;
  SS2022 passwords must be base64 key material of the method's key
  length (one PSK segment or serverKey:userKey) — the exact form
  sing-box accepts, rejected up front instead of failing at deploy
- orchestration: resource building, deploy validation and rebuilds all
  branch on manualExit; switching managed->manual queues removal of the
  old exit resource, manual->managed mints a fresh exit resource id
  (deploy validation previously dereferenced the nil exit server —
  regression test locks the fix)
- UI: exit target segmented control, link paste box that parses
  vless:// / ss:// / socks5:// links into the form, per-protocol
  credential fields (blank keeps the stored value on edit)
- golden: new manualCreate/manualAssign/manualDeploy/manualJobs/
  manualNodes sections — exactly one entry-side job whose outbound
  targets the manual address; all prior sections unchanged
- the generated combined sing-box config passes the real sing-box
  1.13.21 check
2026-10-21 11:18:00 +08:00
aaaamoooo d316d0f8bb feat: customer-level credentials shared across all nodes (item 2)
One credential set per customer (UUID + 32-byte root relay key + shortId)
lives on the customer record; node bindings no longer store credentials.
SS2022 user keys derive deterministically from the root key by method
length (16/24/32-byte prefix), so a single password serves every protocol,
matching 3x-ui's per-client credential model. Reality public keys stay a
node property and are shown per binding in the credentials pane.

- store: load-time convergence — first binding's set wins; a converged
  password that is not a 32-byte root key is regenerated (uuid/shortId
  preserved); bindings are stripped; idempotent, covered by tests
- deploy: the orchestrator stamps the customer's set onto a build copy;
  subscriptions resolve the same set; the derivation applies in both the
  inbound builder and the URI builder (verified live: Alice's N1 link is
  byte-identical to the pre-change seed expectation, her N2 user key is
  exactly the root key's 16-byte prefix)
- API: GET /api/customers/:id/credentials returns {credentials, bindings};
  regenerate is now POST /api/customers/:id/credentials/regenerate and
  rotates the whole set, rebuilding every deployed node of the customer
- UI: credentials pane shows the single shared set plus per-binding
  node/protocol/Reality-public-key rows
- golden subscriptions fixture regenerated under one shared set (RF and
  SS entries byte-identical to before; five entries audited by hand)
- golden baseline re-captured for the new shapes; difftest normalizer now
  treats a base64 root key's prefix-truncation encodings and the Surge
  '='-mangled variants as the same secret
2026-10-21 11:18:00 +08:00
aaaamoooo b49c134513 feat(agent+panel): sing-box core version probe and display (round-2 #4)
agent-go: engineReport gains singBoxVersion. The probe runs
`sing-box version` once, parses the version token from its first line
('unknown' from unstamped builds reports empty), and caches it keyed by
the binary's mtime+size — re-probed only when an engine install/update
replaces the file. Steady-state heartbeats pay one stat, no subprocess.
Agent version stays 0.6.8.

panel-go: EngineState.singBoxVersion persisted from heartbeats (old
agents without the field keep showing '-'), servers table core-version
column now renders it. Golden gains heartbeatWithEngine +
serversAfterEngineHeartbeat sections locking the full chain, plus a
listen-address (<hostip>) normalization for the sandbox bind address.
singboxversion_test.go covers parsing, cache hits, and re-probe on
binary replacement.
2026-10-21 11:18:00 +08:00
aaaamoooo 35c9bd2cc0 feat(panel): customers stats strip with 3x-ui depletion semantics (round-2 #1)
GET /api/customers gains a summary object (total/online/depleted/
depletingSoon/disabled/enabled) computed server-side; the customers page
renders it as a six-cell stats strip above the toolbar.

Predicates aligned verbatim with 3x-ui client_paging.go:
- depleted = (limit>0 && used>=limit) || (expiry set && expiry<=now),
  counted regardless of status;
- depletingSoon = active && !depleted && (expiry within expireDiff days
  || remaining traffic < trafficDiff GB), thresholds from new settings
  expireDiff/trafficDiff with 3x-ui's factory default 0 (nobody counts
  until configured);
- enabled = status active; disabled = manual suspensions only.
Online reuses the existing 3-minute presence projection.

Golden diff confined to the additive summary keys; summary_test.go
covers every bucket with thresholds off (soon=0) and on (soon=2).
2026-10-21 11:18:00 +08:00
aaaamoooo 1b32b57377 feat(web): UI batch — content width, latency removal, icon tooltips, avatar removal (round-2 #3/#5/#6/#9)
#3 .content: drop max-width:1460px centering, padding 24px full width
   (3x-ui content-area parity), 8px under the 760px breakpoint.
#5 servers page: remove latency column + average-latency stat cell
   (stats grid 4->3, table 15->14 cols incl. detail colspan/empty row).
#6 icon buttons: native title replaced by data-tip with a 3x-ui style
   bubble (black/white, rounded, arrow, body-level fixed layer so table
   scroll containers cannot clip it); customer QR tip renamed to 二维码;
   covers customers/servers/nodes rows, credentials pane, links pane,
   address eye toggle, drag grip, filter chip clear, logout, modal close.
#9 remove decorative topbar avatar span#avatar, its showApp assignment
   and the dead .avatar CSS rule. Refresh button untouched.

Golden re-captured: sole diff was errors.badSubscription body (the SPA
index.html served there) reflecting the avatar/data-tip changes.
2026-10-21 11:18:00 +08:00
aaaamoooo 3de4c4945f fix(panel-go): random ports roll once — sticky across edits (round-2 #7)
NormalizeNode re-selected ports whenever the request merely CONTAINED
port/serverId keys; the browser form always submits them (port=null in
random mode), so every save re-rolled both entry and exit ports. Now an
assigned port only moves on an explicit addressing change: mode switch to
random, a different fixed port value, or a server move where the port is
taken (fixed mode surfaces the 409). random->fixed pins the current port.

Regression: portlock_test.go (UI-shape payload saved twice, rename-only,
mode switches, fixed change/conflict, server move free/taken).
Golden re-captured: diff confined to nodePatchUiStyle/nodePatchJobs/
nodesAfterPatch/backup port leaves returning to the seed ports.
2026-10-21 11:17:59 +08:00
aaaamoooo 85763ebdb7 test(panel-go): freeze Go golden behavior baseline (Node retired, difftest superseded)
Single-source Go from this round on: golden.mjs replays a fixed API script
against a Go panel seeded from the difftest seed DB and diffs every response
(learning-normalized) against golden.json. 25 sections: lists, credentials,
all subscription formats, deploy job payloads, usage/presence, UI-style node
PATCH, /api/system shape, backup, error cases.
2026-10-21 11:17:59 +08:00
aaaamoooo 612035ac4e docs: UI reskin report
CI / test (push) Canceled after 0s
CI / anytls-smoke (push) Canceled after 0s
2026-10-21 03:16:31 +08:00
aaaamoooo 0a2889b4e4 feat(web): 3x-ui style reskin — amo branding, light/dark themes, four pages + tabbed customer form 2026-10-21 03:16:30 +08:00
aaaamoooo d287bb9be9 feat(panel): reskin backend — node sortIndex, presence/speed, /api/system, credentials, subscription aggregation (Node+Go parity) 2026-10-21 03:16:30 +08:00
aaaamoooo c6f6b9dbea feat(panel-go): monthly traffic reset for customers
- trafficResetDay/Period/At on the shared customer record (omitempty,
  inert for the Node panel, which preserves unknown fields)
- housekeeping applies due resets before quota evaluation: period-key
  idempotency, short-month clamping (31st fires Feb 28), missed-period
  catch-up after panel downtime, traffic suspensions auto-resume
- first sight of a schedule only arms the period marker, never zeroes;
  API create/PATCH accept trafficResetDay (0=off) and re-arm on change
- UI: reset-day input + last-reset display in the customer form
- tests: calendar math, apply pass, API lifecycle, persistence;
  full Go/Node suites green, difftest formal 50/50
2026-10-21 03:16:30 +08:00
aaaamoooo f4ef3d96a6 chore: migrate self-distribution links from GitHub to self-hosted Gitea
CI / test (push) Canceled after 0s
CI / anytls-smoke (push) Canceled after 0s
GitHub accounts suspended; all first-party distribution now served from
git.881218.xyz/rogue/NexusGate (Gitea):
- raw:  /rogue/NexusGate/raw/branch/<branch>/<path>
- release assets and source archives under the same repo path
- scripts gain a GIT_BASE variable built from NG_REPO (default
  rogue/NexusGate); NG_REPO/NG_BRANCH/--repo/--branch semantics unchanged
- panel enrollment/uninstall commands in public/app.js point at Gitea raw
- install.sh/nexusgate.sh/agent-singbox.sh stats source previously
  defaulted to the upstream repo (a2899882); defaults now this repo
Upstream dependency references (sagernet/sing-box Go module) untouched.
2026-10-20 11:05:55 +00:00
aaaamoooo 3951134940 feat: Go port of the panel in panel-go/ — drop-in replacement for server.js
CI / test (push) Canceled after 0s
CI / anytls-smoke (push) Canceled after 0s
Pure-stdlib Go implementation of the whole control plane: same
nexusgate.json schema (unknown fields preserved verbatim, explicit
null/false tri-states modeled), same HTTP API and error strings,
embedded frontend (NG_PUBLIC_DIR override), self-implemented scrypt
auth, X25519 Reality keys, panel-hosted keyStore and heartbeat
reconciliation included. Version stays 0.6.8.

Verified: Go tests 33/33 (scrypt + Reality vectors generated from
the Node implementation, subscription goldens byte-identical),
Node suite 52/52 untouched, differential E2E against the Node
panel 50/50 including two-way DB swap. See panel-go/PARITY.md
and panel-go/INSTALL-GO.md.
2026-10-20 08:21:54 +00:00
aaaamoooo abb1a4344b feat: heartbeat reconciliation — panel re-dispatches resources a node lost
Agents (Go + Node) now report the resource IDs they actually hold in
each heartbeat. When an expected resource is missing locally (e.g. the
node's /etc/nexusgate state was wiped and only the agent restarted, so
no re-enrollment happens), the panel re-queues applies for just the
affected nodes via reconcileFromHeartbeat: gated on no queued/running
jobs for the server (in-flight jobs are never failed by this path) and
throttled to one trigger per server per 120s, audited as
system:heartbeat. reconcileServer gains optional onlyNodeIds /
failInFlight parameters; the enrollment call is unchanged. Heartbeats
without a resources array (old agents) behave exactly as before.
2026-10-20 07:46:33 +00:00
aaaamoooo f2b7d8e181 feat(agent-go): consume panel-hosted Reality keys (persist keyMaterial, backfill unused-provided keys) 2026-10-20 07:20:59 +00:00
aaaamoooo 483b891306 feat: panel-hosted Reality keys (keyStore, payload keyMaterial, backfill, cleanup) 2026-10-20 07:20:42 +00:00
aaaamoooo 1b77c610f4 fix: keep current page on refresh via hash routing (restore page from location.hash, back/forward supported) 2026-10-20 15:09:44 +08:00
aaaamoooo 495b949f18 feat: ng-agent update migrates Node agents to Go by default (NG_AGENT_IMPL=node opts out) 2026-10-20 15:05:07 +08:00
aaaamoooo 9c3789a1a4 chore(uninstall): also remove nexusgate-agent-go test unit; docs(README): Go agent memory/install notes 2026-10-20 06:56:17 +00:00
aaaamoooo d29b4f7ef1 feat(update): detect installed impl; Go path downloads release asset and replaces binary, flush-usage via binary; Node path unchanged except unit ExecStart rewrite; default REPO now aaaamoooo/NexusGate 2026-10-20 06:56:07 +00:00
aaaamoooo 545c0668ba feat(install): default to Go agent binary from release agent-v0.6.8, drop Node.js dependency; NG_AGENT_IMPL=node fallback via run.sh; shell-only enroll JSON; units ExecStart the Go binary 2026-10-20 06:55:54 +00:00
aaaamoooo ddfc27efba feat: Go agent source (stdlib-only rewrite of agent.js) + parity/install docs; binaries ship as release assets 2026-10-20 06:55:29 +00:00
aaaamoooo c6615f0ab5 fix: systemd agent unit add --max-semi-space-size=4 (match run.sh); README memory numbers to measured values 2026-10-20 06:25:37 +00:00
aaaamoooo ae9e607527 fix: agent run.sh add node memory limits (max-old-space 64) 2026-10-20 13:41:43 +08:00
aaaamoooo 5f272ed356 fix: create /usr/local/sbin before fetching agent scripts (Alpine) 2026-10-20 13:35:33 +08:00