fix(outbound): store subscription refreshes without trusting the row read

The refresh rewrote the whole row it read before a download whose length
the provider controls, so an admin's delete, disable or edit made in the
meantime was undone; Update had the mirror problem. Both now write only
their own columns, and a refresh of a row that was deleted or re-pointed
stores nothing.

Around the same path:
- the preview fetches without creating (and leaking) a scratch row, and
  Create keeps enabled=false instead of falling back to the column default;
- fetch errors name the host but not the subscription URL, which is a
  credential and ends up in the log and in lastError;
- each fetch closes its transport's idle connections;
- one document loads at most 2000 links, and the cut is reported;
- wireguard outbounds from a subscription always run on the userspace TUN;
- editing a subscription schedules an Xray reload like delete and move do.
This commit is contained in:
MHSanaei
2026-10-31 08:31:03 +00:00
parent 2d72d9c715
commit f1444bec3f
5 changed files with 353 additions and 51 deletions
+7
View File
@@ -32,6 +32,10 @@ type ParseResult struct {
Identity string
}
// MaxSubscriptionOutbounds caps the outbounds one document yields: each is held, validated
// and merged into the core's config, and the byte cap alone allows hundreds of thousands.
const MaxSubscriptionOutbounds = 2000
// ParseSubscriptionBody accepts the raw body returned by a subscription URL.
// It handles the common case where the body is a base64-encoded blob of
// newline-separated links, and also tolerates an already-decoded text body.
@@ -54,6 +58,9 @@ func ParseSubscriptionBody(body []byte) ([]Outbound, []string, error) {
seen := map[string]int{}
for _, ln := range lines {
if len(outbounds) >= MaxSubscriptionOutbounds {
break
}
ln = strings.TrimSpace(ln)
if ln == "" || strings.HasPrefix(ln, "#") {
continue