diff --git a/cmd/xbctl/main.go b/cmd/xbctl/main.go index 4a05d66..53e6731 100644 --- a/cmd/xbctl/main.go +++ b/cmd/xbctl/main.go @@ -30,7 +30,7 @@ const ( serviceName = "xboard-node.service" serviceFilePath = "/etc/systemd/system/xboard-node.service" defaultInstallRoot = "/etc/xboard-node" - downloadBase = "https://github.com/cedar2025/xboard-node/releases" + downloadBase = "http://103.112.1.141:3000/will/xboard-node" ) var ( @@ -593,10 +593,8 @@ func ensureRoot(cmd string) error { } func resolveDownloadURL(artifact, version string) string { - if version == "latest" { - return downloadBase + "/latest/download/" + artifact - } - return downloadBase + "/download/" + version + "/" + artifact + // Gitea release download URL: {base}/releases/download/{tag}/{file} + return downloadBase + "/releases/download/" + version + "/" + artifact } func downloadFile(url, dest string) error { diff --git a/internal/kernel/singbox/conntracker.go b/internal/kernel/singbox/conntracker.go index 77d6852..c7f9e9d 100644 --- a/internal/kernel/singbox/conntracker.go +++ b/internal/kernel/singbox/conntracker.go @@ -791,14 +791,14 @@ func (c *trackedPacketConn) UnwrapPacketReader() (N.PacketReader, []N.CountFunc) if c.us == nil { return c.PacketConn, nil } - return c.PacketConn, []N.CountFunc{c.makeCountFunc(&c.us.download)} + return c.PacketConn, []N.CountFunc{c.makeCountFunc(&c.us.upload)} } func (c *trackedPacketConn) UnwrapPacketWriter() (N.PacketWriter, []N.CountFunc) { if c.us == nil { return c.PacketConn, nil } - return c.PacketConn, []N.CountFunc{c.makeCountFunc(&c.us.upload)} + return c.PacketConn, []N.CountFunc{c.makeCountFunc(&c.us.download)} } func (c *trackedPacketConn) Upstream() any { return c.PacketConn } diff --git a/internal/service/service.go b/internal/service/service.go index 890940d..266e73e 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -838,6 +838,18 @@ func (s *Service) applyUserDelta(ctx context.Context, action string, deltaUsers return } + // Force-close active connections for removed users before revoking + // credentials. Without this, existing long-lived connections + // (QUIC/TCP) persist until natural timeout — subscription resets + // and bans would not take immediate effect. + if s.kernel.Capabilities().ForceCloseUser { + for _, u := range deltaUsers { + if err := s.kernel.CloseUserConnections(ctx, u.UUID); err != nil { + nlog.Core().Debug(fmt.Sprintf("CloseUserConnections(%s): %v", u.UUID, err)) + } + } + } + prevUsers, prevHash := s.prepareUserState(filtered) removed, err := s.kernel.RemoveUsers(deltaUsers) if err != nil {