Compare commits

...

34 Commits

Author SHA1 Message Date
Alireza Ahmadi 13117843ec v1.3.0-rc.1 2025-07-26 20:18:06 +02:00
Alireza Ahmadi 60b0b3c878 fix dockerhub push 2025-07-26 09:59:26 +02:00
Alireza Ahmadi 825a8d9fd9 fix install script on oracle linux #680 2025-07-26 09:58:19 +02:00
Alireza Ahmadi 58105be433 v1.3.0-rc.0 2025-07-24 20:50:32 +02:00
Alireza Ahmadi 98db6d2445 sing-box v1.12.0-rc.3 2025-07-24 20:46:16 +02:00
Alireza Ahmadi cd3d4e6451 fix add bulk client 2025-07-24 15:11:50 +02:00
Alireza Ahmadi 1e3d1b9ed3 faster docker build 2025-07-20 15:36:53 +02:00
Alireza Ahmadi a794cace54 go 1.24.5 2025-07-19 22:01:26 +02:00
Alireza Ahmadi 6520a8dc9c v1.3.0-beta.5 2025-07-18 21:35:11 +02:00
Alireza Ahmadi 8ccd60cb74 Merge pull request #667 from Shellgate/sing-box-v.1.12
افزودن گواهی خود امضا
2025-07-18 21:25:06 +02:00
Alireza Ahmadi c9d89540d3 sing-box v1.12.0-beta.34 2025-07-18 19:47:45 +02:00
Alireza Ahmadi c2d33d2a1e revert back to normal restart inbounds 2025-07-18 19:47:29 +02:00
Alireza Ahmadi fe4fa9b9e6 fix client links #670 #671 2025-07-18 19:45:55 +02:00
Shellgate 1d23f5a1df Update s-ui.sh 2025-07-13 20:19:09 +03:30
Alireza Ahmadi 349d490a65 v1.3.0-beta.4 2025-07-13 12:32:02 +02:00
Alireza Ahmadi 11326d7cc1 update dependencies 2025-07-13 12:31:10 +02:00
Alireza Ahmadi d2827d013b improve client's inbound changes 2025-07-13 12:29:21 +02:00
Alireza Ahmadi f239574e41 v1.3.0-beta.3 2025-07-08 00:17:24 +02:00
Alireza Ahmadi bc05aed51f update frontend 2025-07-08 00:15:27 +02:00
Alireza Ahmadi ff791d0a27 update packages 2025-07-08 00:15:12 +02:00
Alireza Ahmadi 319e3b1eba use musl gcc for docker #651
Co-authored-by: @elseif
2025-07-08 00:13:14 +02:00
Alireza Ahmadi 12a24ec617 sing-box v1.12.0-beta.24 2025-06-13 01:30:38 +02:00
Alireza Ahmadi 92c742987e fix old link removal on inbound tag change #633 2025-06-13 00:57:45 +02:00
Alireza Ahmadi 4dabe656c9 disk and swap info #341 2025-06-11 03:26:48 +02:00
Alireza Ahmadi 03fff53260 UI screenshots #82 #366 2025-06-11 01:53:40 +02:00
Alireza Ahmadi f65cb2ca06 fix http-opts path in clash sub 2025-06-07 01:44:31 +02:00
Alireza Ahmadi 36938aee41 add migration for anytls config of clients 2025-06-07 01:32:58 +02:00
Alireza Ahmadi d82af6f9bd v1.3.0-beta.2 2025-06-07 00:06:48 +02:00
Alireza Ahmadi 6b785c3404 v1.3.0-beta.1 2025-06-06 02:27:46 +02:00
Alireza Ahmadi df1a271efa migration to 1.3 with singbox 1.12 2025-06-06 02:23:00 +02:00
Alireza Ahmadi bd9bd8590c singbox v1.12.0-beta.21 2025-06-05 22:33:34 +02:00
Alireza Ahmadi d186875ab7 clash - stash subscription #373 2025-06-01 23:50:45 +02:00
Alireza Ahmadi 3f7657c080 adjust subJson 2025-05-31 20:34:40 +02:00
Alireza Ahmadi a5f4c46066 support anytls link #611 2025-05-30 23:05:55 +02:00
30 changed files with 1205 additions and 368 deletions
+40 -8
View File
@@ -6,15 +6,39 @@ on:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
build: frontend-build:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
with: with:
submodules: recursive submodules: recursive
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies and build frontend
run: |
cd frontend
npm install
npm run build
- name: Upload frontend build artifact
uses: actions/upload-artifact@v4
with:
name: frontend-dist
path: frontend/dist/
build:
needs: frontend-build
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v4.2.2
- name: Download frontend build artifact
uses: actions/download-artifact@v4
with:
name: frontend-dist
path: frontend_dist
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v5
@@ -26,31 +50,39 @@ jobs:
type=ref,event=branch type=ref,event=branch
type=ref,event=tag type=ref,event=tag
type=pep440,pattern={{version}} type=pep440,pattern={{version}}
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
with:
install: true
buildkitd-flags: --debug
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKER_HUB_USERNAME }} username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }} password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Login to GHCR - name: Login to GHCR
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push - name: Build and push
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
context: . context: .
file: Dockerfile.frontend-artifact
push: true push: true
platforms: linux/amd64, linux/arm64/v8, linux/arm/v7, linux/arm/v6, linux/386 platforms: linux/amd64, linux/386, linux/arm64/v8, linux/arm/v7, linux/arm/v6
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache,mode=max
+18 -3
View File
@@ -6,13 +6,28 @@ RUN npm install && npm run build
FROM golang:1.24-alpine AS backend-builder FROM golang:1.24-alpine AS backend-builder
WORKDIR /app WORKDIR /app
ARG TARGETARCH ARG TARGETARCH
ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE"
ENV CGO_ENABLED=1 ENV CGO_ENABLED=1
ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE"
ENV GOARCH=$TARGETARCH ENV GOARCH=$TARGETARCH
RUN apk update && apk --no-cache --update add build-base gcc wget unzip
RUN apk update && apk add --no-cache \
gcc \
musl-dev \
libc-dev \
make \
git \
wget \
unzip \
bash
ENV CC=gcc
COPY . . COPY . .
COPY --from=front-builder /app/dist/ /app/web/html/ COPY --from=front-builder /app/dist/ /app/web/html/
RUN go build -ldflags="-w -s" -tags "with_quic,with_grpc,with_utls,with_acme,with_gvisor" -o sui main.go
RUN go build -ldflags="-w -s" \
-tags "with_quic,with_grpc,with_utls,with_acme,with_gvisor" \
-o sui main.go
FROM --platform=$TARGETPLATFORM alpine FROM --platform=$TARGETPLATFORM alpine
LABEL org.opencontainers.image.authors="alireza7@gmail.com" LABEL org.opencontainers.image.authors="alireza7@gmail.com"
+36
View File
@@ -0,0 +1,36 @@
FROM golang:1.24-alpine AS backend-builder
WORKDIR /app
ARG TARGETARCH
ENV CGO_ENABLED=1
ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE"
ENV GOARCH=$TARGETARCH
RUN apk update && apk add --no-cache \
gcc \
musl-dev \
libc-dev \
make \
git \
wget \
unzip \
bash
ENV CC=gcc
COPY . .
# Copy pre-built frontend files from a known location (provided by workflow artifact)
COPY frontend_dist/ /app/web/html/
RUN go build -ldflags="-w -s" \
-tags "with_quic,with_grpc,with_utls,with_acme,with_gvisor" \
-o sui main.go
FROM --platform=$TARGETPLATFORM alpine
LABEL org.opencontainers.image.authors="alireza7@gmail.com"
ENV TZ=Asia/Tehran
WORKDIR /app
RUN apk add --no-cache --update ca-certificates tzdata
COPY --from=backend-builder /app/sui /app/
COPY entrypoint.sh /app/
VOLUME [ "s-ui" ]
ENTRYPOINT [ "./entrypoint.sh" ]
+6
View File
@@ -27,6 +27,12 @@
| Dark/Light Theme | :heavy_check_mark: | | Dark/Light Theme | :heavy_check_mark: |
| API Interface | :heavy_check_mark: | | API Interface | :heavy_check_mark: |
## Screenshots
!["Main"](https://github.com/alireza0/s-ui-frontend/raw/main/media/main.png)
[Other UI Screenshots](https://github.com/alireza0/s-ui-frontend/blob/main/screenshots.md)
## API Documentation ## API Documentation
[API-Documentation Wiki](https://github.com/alireza0/s-ui/wiki/API-Documentation) [API-Documentation Wiki](https://github.com/alireza0/s-ui/wiki/API-Documentation)
+154
View File
@@ -0,0 +1,154 @@
package migration
import (
"encoding/json"
"net/url"
"s-ui/database/model"
"strconv"
"strings"
"gorm.io/gorm"
)
func migrate_dns(db *gorm.DB) error {
var configStr string
err := db.Model(model.Setting{}).Select("value").Where("key = ?", "config").First(&configStr).Error
if err != nil {
return err
}
if configStr == "" {
return nil
}
var config map[string]interface{}
err = json.Unmarshal([]byte(configStr), &config)
if err != nil {
return err
}
if dnsConfig, ok := config["dns"].(map[string]interface{}); ok {
if dnsServers, ok := dnsConfig["servers"].([]interface{}); ok {
for index, dnsServer := range dnsServers {
if dnsServer, ok := dnsServer.(map[string]interface{}); ok {
if addr, ok := dnsServer["address"].(string); ok && addr != "" {
switch addr {
case "local":
delete(dnsServer, "address")
dnsServer["type"] = "local"
case "fakeip":
delete(dnsServer, "address")
dnsServer["type"] = "fakeip"
default:
addrParsed, err := url.Parse(addr)
if err != nil {
continue
}
switch addrParsed.Scheme {
case "":
dnsServer["type"] = "udp"
dnsServer["server"] = addr
case "udp", "tcp", "tls", "quic", "https", "h3":
dnsServer["type"] = addrParsed.Scheme
dnsServer["server"] = addrParsed.Host
case "dhcp":
dnsServer["type"] = addrParsed.Scheme
if addrParsed.Host != "auto" && addrParsed.Host != "" {
dnsServer["interface"] = addrParsed.Host
}
case "rcode":
dnsServer["type"] = "predefined"
dnsServer["responses"] = []map[string]string{
{
"rcode": strings.ToUpper(addrParsed.Host),
},
}
}
delete(dnsServer, "address")
if addrParsed.Port() != "" {
port, err := strconv.Atoi(addrParsed.Port())
if err == nil {
dnsServer["server_port"] = port
}
}
if address_resolver, ok := dnsServer["address_resolver"].(string); ok && address_resolver != "" {
delete(dnsServer, "address_resolver")
dnsServer["domain_resolver"] = address_resolver
}
delete(dnsServer, "strategy")
}
dnsServers[index] = dnsServer
}
}
}
dnsConfig["servers"] = dnsServers
}
config["dns"] = dnsConfig
} else {
return nil
}
// save changes
configs, err := json.MarshalIndent(config, "", " ")
if err != nil {
return err
}
return db.Model(model.Setting{}).Where("key = ?", "config").Update("value", string(configs)).Error
}
func remove_outbound_strategy(db *gorm.DB) error {
var outbounds []model.Outbound
err := db.Find(&outbounds).Where("json_extract(options, '$.domain_strategy') IS NOT NULL").Error
if err != nil {
return err
}
for _, outbound := range outbounds {
var restFields map[string]json.RawMessage
if err := json.Unmarshal(outbound.Options, &restFields); err != nil {
return err
}
delete(restFields, "domain_strategy")
outbound.Options, _ = json.MarshalIndent(restFields, "", " ")
db.Save(&outbound)
}
return nil
}
func anytls_user_config(db *gorm.DB) error {
var clients []model.Client
err := db.Model(model.Client{}).Find(&clients).Error
if err != nil {
return err
}
for index, client := range clients {
var configs map[string]json.RawMessage
if err := json.Unmarshal(client.Config, &configs); err != nil {
return err
}
if configs["anytls"] != nil {
continue
}
configs["anytls"] = configs["trojan"]
configJson, err := json.MarshalIndent(configs, "", " ")
if err != nil {
return err
}
clients[index].Config = configJson
db.Save(&clients[index])
}
return nil
}
func to1_3(db *gorm.DB) error {
err := anytls_user_config(db)
if err != nil {
return err
}
err = migrate_dns(db)
if err != nil {
return err
}
err = remove_outbound_strategy(db)
if err != nil {
return err
}
return nil
}
+11 -1
View File
@@ -56,10 +56,20 @@ func MigrateDb() {
log.Fatal("Migration to 1.2 failed: ", err) log.Fatal("Migration to 1.2 failed: ", err)
return return
} }
dbVersion = "1.2"
}
// Before 1.3
if dbVersion[0:3] == "1.2" {
err = to1_3(tx)
if err != nil {
log.Fatal("Migration to 1.3 failed: ", err)
return
}
} }
// Set version // Set version
err = tx.Raw("UPDATE settings SET value = ? WHERE key = ?", currentVersion, "version").Error err = tx.Exec("UPDATE settings SET value = ? WHERE key = ?", currentVersion, "version").Error
if err != nil { if err != nil {
log.Fatal("Update version failed: ", err) log.Fatal("Update version failed: ", err)
return return
+1 -1
View File
@@ -1 +1 @@
1.3.0-beta.0 1.3.0-rc.1
+1 -1
View File
@@ -491,7 +491,7 @@ func (s *Box) Close() error {
close(s.done) close(s.done)
} }
err := sbCommon.Close( err := sbCommon.Close(
s.endpoint, s.inbound, s.outbound, s.router, s.connection, s.dnsRouter, s.dnsTransport, s.network, s.service, s.service, s.endpoint, s.inbound, s.outbound, s.router, s.connection, s.dnsRouter, s.dnsTransport, s.network,
) )
for _, lifecycleService := range s.internalService { for _, lifecycleService := range s.internalService {
err1 := lifecycleService.Close() err1 := lifecycleService.Close()
+9 -1
View File
@@ -1,12 +1,14 @@
package cronjob package cronjob
import ( import (
"s-ui/database"
"s-ui/logger" "s-ui/logger"
"s-ui/service" "s-ui/service"
) )
type DepleteJob struct { type DepleteJob struct {
service.ClientService service.ClientService
service.InboundService
} }
func NewDepleteJob() *DepleteJob { func NewDepleteJob() *DepleteJob {
@@ -14,9 +16,15 @@ func NewDepleteJob() *DepleteJob {
} }
func (s *DepleteJob) Run() { func (s *DepleteJob) Run() {
err := s.ClientService.DepleteClients() inboundIds, err := s.ClientService.DepleteClients()
if err != nil { if err != nil {
logger.Warning("Disable depleted users failed: ", err) logger.Warning("Disable depleted users failed: ", err)
return return
} }
if len(inboundIds) > 0 {
err := s.InboundService.RestartInbounds(database.GetDB(), inboundIds)
if err != nil {
logger.Error("unable to restart inbounds: ", err)
}
}
} }
+43 -44
View File
@@ -1,6 +1,6 @@
module s-ui module s-ui
go 1.24.3 go 1.24.5
require ( require (
github.com/gin-contrib/gzip v1.2.3 github.com/gin-contrib/gzip v1.2.3
@@ -8,12 +8,13 @@ require (
github.com/gin-gonic/gin v1.10.1 github.com/gin-gonic/gin v1.10.1
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b github.com/sagernet/sing v0.7.0-beta.1.0.20250722151551-64142925accb
github.com/sagernet/sing-box v1.12.0-beta.19 github.com/sagernet/sing-box v1.12.0-rc.3
github.com/sagernet/sing-dns v0.4.5 github.com/sagernet/sing-dns v0.4.6
github.com/shirou/gopsutil/v4 v4.25.4 github.com/shirou/gopsutil/v4 v4.25.6
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
gorm.io/driver/sqlite v1.5.7 gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/sqlite v1.6.0
gorm.io/gorm v1.30.0 gorm.io/gorm v1.30.0
) )
@@ -25,30 +26,29 @@ require (
github.com/andybalholm/brotli v1.1.0 // indirect github.com/andybalholm/brotli v1.1.0 // indirect
github.com/anytls/sing-anytls v0.0.8 // indirect github.com/anytls/sing-anytls v0.0.8 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect
github.com/bytedance/sonic v1.13.2 // indirect github.com/bytedance/sonic v1.13.3 // indirect
github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/caddyserver/certmagic v0.23.0 // indirect github.com/caddyserver/certmagic v0.23.0 // indirect
github.com/caddyserver/zerossl v0.1.3 // indirect github.com/caddyserver/zerossl v0.1.3 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect github.com/cloudwego/base64x v0.1.5 // indirect
github.com/coder/websocket v1.8.12 // indirect github.com/coder/websocket v1.8.13 // indirect
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect
github.com/cretz/bine v0.2.0 // indirect github.com/cretz/bine v0.2.0 // indirect
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect
github.com/ebitengine/purego v0.8.2 // indirect github.com/ebitengine/purego v0.8.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/gaissmai/bart v0.11.1 // indirect github.com/gaissmai/bart v0.11.1 // indirect
github.com/gin-contrib/sse v1.0.0 // indirect github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-chi/chi/v5 v5.2.1 // indirect github.com/go-chi/chi/v5 v5.2.2 // indirect
github.com/go-chi/render v1.0.3 // indirect github.com/go-chi/render v1.0.3 // indirect
github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 // indirect github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.26.0 // indirect github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect github.com/gobwas/pool v0.2.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-json v0.10.5 // indirect
@@ -75,27 +75,27 @@ require (
github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/libdns/alidns v1.0.4-libdns.v1.beta1 // indirect github.com/libdns/alidns v1.0.5-libdns.v1.beta1 // indirect
github.com/libdns/cloudflare v0.2.2-0.20250430151523-b46a2b0885f6 // indirect github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6 // indirect
github.com/libdns/libdns v1.0.0-beta.1 // indirect github.com/libdns/libdns v1.1.0 // indirect
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/mattn/go-sqlite3 v1.14.28 // indirect
github.com/mdlayher/genetlink v1.3.2 // indirect github.com/mdlayher/genetlink v1.3.2 // indirect
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect
github.com/mdlayher/sdnotify v1.0.0 // indirect github.com/mdlayher/sdnotify v1.0.0 // indirect
github.com/mdlayher/socket v0.5.1 // indirect github.com/mdlayher/socket v0.5.1 // indirect
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 // indirect github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 // indirect
github.com/metacubex/utls v1.7.0-alpha.3 // indirect github.com/metacubex/utls v1.8.0 // indirect
github.com/mholt/acmez/v3 v3.1.2 // indirect github.com/mholt/acmez/v3 v3.1.2 // indirect
github.com/miekg/dns v1.1.66 // indirect github.com/miekg/dns v1.1.67 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus-community/pro-bing v0.4.0 // indirect github.com/prometheus-community/pro-bing v0.4.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect
github.com/safchain/ethtool v0.3.0 // indirect github.com/safchain/ethtool v0.3.0 // indirect
@@ -107,12 +107,12 @@ require (
github.com/sagernet/nftables v0.3.0-beta.4 // indirect github.com/sagernet/nftables v0.3.0-beta.4 // indirect
github.com/sagernet/quic-go v0.52.0-beta.1 // indirect github.com/sagernet/quic-go v0.52.0-beta.1 // indirect
github.com/sagernet/sing-mux v0.3.2 // indirect github.com/sagernet/sing-mux v0.3.2 // indirect
github.com/sagernet/sing-quic v0.5.0-beta.1 // indirect github.com/sagernet/sing-quic v0.5.0-beta.3 // indirect
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect github.com/sagernet/sing-shadowsocks v0.2.8 // indirect
github.com/sagernet/sing-shadowsocks2 v0.2.1 // indirect github.com/sagernet/sing-shadowsocks2 v0.2.1 // indirect
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 // indirect github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 // indirect
github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210 // indirect github.com/sagernet/sing-tun v0.6.10-0.20250721014417-ebbe32588cfb // indirect
github.com/sagernet/sing-vmess v0.2.2-0.20250503051933-9b4cf17393f8 // indirect github.com/sagernet/sing-vmess v0.2.4 // indirect
github.com/sagernet/smux v1.5.34-mod.2 // indirect github.com/sagernet/smux v1.5.34-mod.2 // indirect
github.com/sagernet/tailscale v1.80.3-mod.5 // indirect github.com/sagernet/tailscale v1.80.3-mod.5 // indirect
github.com/sagernet/wireguard-go v0.0.1-beta.7 // indirect github.com/sagernet/wireguard-go v0.0.1-beta.7 // indirect
@@ -125,11 +125,11 @@ require (
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect github.com/tklauser/numcpus v0.10.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.3.0 // indirect
github.com/vishvananda/netns v0.0.5 // indirect github.com/vishvananda/netns v0.0.5 // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect
@@ -139,22 +139,21 @@ require (
go.uber.org/zap/exp v0.3.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/arch v0.16.0 // indirect golang.org/x/arch v0.18.0 // indirect
golang.org/x/crypto v0.38.0 // indirect golang.org/x/crypto v0.40.0 // indirect
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
golang.org/x/mod v0.24.0 // indirect golang.org/x/mod v0.26.0 // indirect
golang.org/x/net v0.40.0 // indirect golang.org/x/net v0.42.0 // indirect
golang.org/x/sync v0.14.0 // indirect golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.33.0 // indirect golang.org/x/sys v0.34.0 // indirect
golang.org/x/term v0.32.0 // indirect golang.org/x/term v0.33.0 // indirect
golang.org/x/text v0.25.0 // indirect golang.org/x/text v0.27.0 // indirect
golang.org/x/time v0.9.0 // indirect golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.33.0 // indirect golang.org/x/tools v0.34.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
google.golang.org/grpc v1.72.0 // indirect google.golang.org/grpc v1.73.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.4.1 // indirect
lukechampine.com/blake3 v1.3.0 // indirect
) )
+92 -97
View File
@@ -12,24 +12,22 @@ github.com/anytls/sing-anytls v0.0.8 h1:1u/fnH1HoeeMV5mX7/eUOjLBvPdkd1UJRmXiRi6V
github.com/anytls/sing-anytls v0.0.8/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= github.com/anytls/sing-anytls v0.0.8/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU= github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU=
github.com/caddyserver/certmagic v0.23.0/go.mod h1:9mEZIWqqWoI+Gf+4Trh04MOVPD0tGSxtqsxg87hAIH4= github.com/caddyserver/certmagic v0.23.0/go.mod h1:9mEZIWqqWoI+Gf+4Trh04MOVPD0tGSxtqsxg87hAIH4=
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk= github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk=
github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE=
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0= github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0=
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo= github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
@@ -42,28 +40,28 @@ github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbww
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ= github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ=
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q= github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q=
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A= github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A=
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc= github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc=
github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg= github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg=
github.com/gin-contrib/gzip v1.2.3 h1:dAhT722RuEG330ce2agAs75z7yB+NKvX/ZM1r8w0u2U= github.com/gin-contrib/gzip v1.2.3 h1:dAhT722RuEG330ce2agAs75z7yB+NKvX/ZM1r8w0u2U=
github.com/gin-contrib/gzip v1.2.3/go.mod h1:ad72i4Bzmaypk8M762gNXa2wkxxjbz0icRNnuLJ9a/c= github.com/gin-contrib/gzip v1.2.3/go.mod h1:ad72i4Bzmaypk8M762gNXa2wkxxjbz0icRNnuLJ9a/c=
github.com/gin-contrib/sessions v1.0.4 h1:ha6CNdpYiTOK/hTp05miJLbpTSNfOnFg5Jm2kbcqy8U= github.com/gin-contrib/sessions v1.0.4 h1:ha6CNdpYiTOK/hTp05miJLbpTSNfOnFg5Jm2kbcqy8U=
github.com/gin-contrib/sessions v1.0.4/go.mod h1:ccmkrb2z6iU2osiAHZG3x3J4suJK+OU27oqzlWOqQgs= github.com/gin-contrib/sessions v1.0.4/go.mod h1:ccmkrb2z6iU2osiAHZG3x3J4suJK+OU27oqzlWOqQgs=
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I= github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I=
github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo= github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 h1:KbX3Z3CgiYlbaavUq3Cj9/MjpO+88S7/AGXzynVDv84= github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 h1:KbX3Z3CgiYlbaavUq3Cj9/MjpO+88S7/AGXzynVDv84=
@@ -81,8 +79,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
@@ -99,7 +97,6 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -143,20 +140,21 @@ github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk= github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/libdns/alidns v1.0.4-libdns.v1.beta1 h1:ods22gD4PcT0g4qRX77ucykjz7Rppnkz3vQoxDbbKTM= github.com/libdns/alidns v1.0.5-libdns.v1.beta1 h1:txHK7UxDed3WFBDjrTZPuMn8X+WmhjBTTAMW5xdy5pQ=
github.com/libdns/alidns v1.0.4-libdns.v1.beta1/go.mod h1:ystHmPwcGoWjPrGpensQSMY9VoCx4cpR2hXNlwk9H/g= github.com/libdns/alidns v1.0.5-libdns.v1.beta1/go.mod h1:ystHmPwcGoWjPrGpensQSMY9VoCx4cpR2hXNlwk9H/g=
github.com/libdns/cloudflare v0.2.2-0.20250430151523-b46a2b0885f6 h1:0dlpPjNr8TaYZbkpwCiee4udBNrYrWG8EZPYEbjHEn8= github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6 h1:3MGrVWs2COjMkQR17oUw1zMIPbm2YAzxDC3oGVZvQs8=
github.com/libdns/cloudflare v0.2.2-0.20250430151523-b46a2b0885f6/go.mod h1:Aq4IXdjalB6mD0ELvKqJiIGim8zSC6mlIshRPMOAb5w= github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6/go.mod h1:w9uTmRCDlAoafAsTPnn2nJ0XHK/eaUMh86DUk8BWi60=
github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ=
github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/libdns/libdns v1.1.0 h1:9ze/tWvt7Df6sbhOJRB8jT33GHEHpEQXdtkE3hPthbU=
github.com/libdns/libdns v1.1.0/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 h1:A1Cq6Ysb0GM0tpKMbdCXCIfBclan4oHk1Jb+Hrejirg= github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 h1:A1Cq6Ysb0GM0tpKMbdCXCIfBclan4oHk1Jb+Hrejirg=
@@ -167,12 +165,12 @@ github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY= github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY=
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw= github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
github.com/metacubex/utls v1.7.0-alpha.3 h1:cp1cEMUnoifiWrGHRzo+nCwPRveN9yPD8QaRFmfcYxA= github.com/metacubex/utls v1.8.0 h1:mSYi6FMnmc5riARl5UZDmWVy710z+P5b7xuGW0lV9ac=
github.com/metacubex/utls v1.7.0-alpha.3/go.mod h1:oknYT0qTOwE4hjPmZOEpzVdefnW7bAdGLvZcqmk4TLU= github.com/metacubex/utls v1.8.0/go.mod h1:FdjYzVfCtgtna19hX0ER1Xsa5uJInwdQ4IcaaI98lEQ=
github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc= github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0=
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= github.com/miekg/dns v1.1.67/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -184,15 +182,15 @@ github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyfyrrjEaAchdy3R4= github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyfyrrjEaAchdy3R4=
github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4= github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
@@ -216,26 +214,26 @@ github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/l
github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs= github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs=
github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4= github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b h1:ZjTCYPb5f7aHdf1UpUvE22dVmf7BL8eQ/zLZhjgh7Wo= github.com/sagernet/sing v0.7.0-beta.1.0.20250722151551-64142925accb h1:9DU5JA9Cow/bUfdP1v1pYMbAkFiW17UbI4b/iEPjVnc=
github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing v0.7.0-beta.1.0.20250722151551-64142925accb/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-box v1.12.0-beta.19 h1:UrEuYcewe9C68aGuQyE+dDRjtv+uZXXh5DK9hkQ0UTE= github.com/sagernet/sing-box v1.12.0-rc.3 h1:2II6wtPSAZZtE7+1EvdoEo1M0+De8483sqwMd8evaos=
github.com/sagernet/sing-box v1.12.0-beta.19/go.mod h1:zhoIuo39/5gsmJPIMK5P9Z0/SiRmFJsGtfl+8j+Cdcw= github.com/sagernet/sing-box v1.12.0-rc.3/go.mod h1:9HHuLZi2GS3xaDT9oh9hpXsaoEB71HN8YM03lctYB10=
github.com/sagernet/sing-dns v0.4.5 h1:D9REN14qx2FTrZRBrtFLL99f2CuFzQ9S7mIf8uV5hZI= github.com/sagernet/sing-dns v0.4.6 h1:mjZC0o6d5sQ1sraoOBbK3G3apCbuL8wWYwu2RNu5rbM=
github.com/sagernet/sing-dns v0.4.5/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8= github.com/sagernet/sing-dns v0.4.6/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8=
github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE= github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA= github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
github.com/sagernet/sing-quic v0.5.0-beta.1 h1:nC0i/s8LhlZB8ev6laZCXF/uiwAE4kRdT4PcDdE4rI4= github.com/sagernet/sing-quic v0.5.0-beta.3 h1:X/acRNsqQNfDlmwE7SorHfaZiny5e67hqIzM/592ric=
github.com/sagernet/sing-quic v0.5.0-beta.1/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0= github.com/sagernet/sing-quic v0.5.0-beta.3/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0=
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8= github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE= github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo= github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w= github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA= github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210 h1:6H4BZaTqKI3YcDMyTV3E576LuJM4S4wY99xoq2T1ECw= github.com/sagernet/sing-tun v0.6.10-0.20250721014417-ebbe32588cfb h1:cvHEzjk3sVy80UA9PFKX15MzSP0g1uKwUspOm2ds3no=
github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= github.com/sagernet/sing-tun v0.6.10-0.20250721014417-ebbe32588cfb/go.mod h1:AHJuRrLbNRJuivuFZ2VhXwDj4ViYp14szG5EkkKAqRQ=
github.com/sagernet/sing-vmess v0.2.2-0.20250503051933-9b4cf17393f8 h1:zW+zAOCxUIqBCgnZiPovt1uQ3S+zBS+w0NGp+1zITGA= github.com/sagernet/sing-vmess v0.2.4 h1:wSg/SdxThELAvoRIN2yCZgu5xsmP1FWPBrP2ab2wq3A=
github.com/sagernet/sing-vmess v0.2.2-0.20250503051933-9b4cf17393f8/go.mod h1:IL8Rr+EGwuqijszZkNrEFTQDKhilEpkqFqOlvdpS6/w= github.com/sagernet/sing-vmess v0.2.4/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs=
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4= github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc= github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
github.com/sagernet/tailscale v1.80.3-mod.5 h1:7V7z+p2C//TGtff20pPnDCt3qP6uFyY62peJoKF9z/A= github.com/sagernet/tailscale v1.80.3-mod.5 h1:7V7z+p2C//TGtff20pPnDCt3qP6uFyY62peJoKF9z/A=
@@ -244,8 +242,8 @@ github.com/sagernet/wireguard-go v0.0.1-beta.7 h1:ltgBwYHfr+9Wz1eG59NiWnHrYEkDKH
github.com/sagernet/wireguard-go v0.0.1-beta.7/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo= github.com/sagernet/wireguard-go v0.0.1-beta.7/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
github.com/shirou/gopsutil/v4 v4.25.4 h1:cdtFO363VEOOFrUCjZRh4XVJkb548lyF0q0uTeMqYPw= github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
github.com/shirou/gopsutil/v4 v4.25.4/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA= github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -277,16 +275,16 @@ github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 h1:U
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ= github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ=
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA= github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk= github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM= github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA= github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
@@ -302,16 +300,16 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -324,24 +322,24 @@ go4.org/mem v0.0.0-20240501181205-ae6ca9944745 h1:Tl++JLUCe4sxGu8cTpDzRLd3tN7US4
go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U= golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE= golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -351,34 +349,31 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 h1:3GDAcqdIg1ozBNLgPy4SLT84nfcBjr6rhGtXYtrkWLU= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 h1:3GDAcqdIg1ozBNLgPy4SLT84nfcBjr6rhGtXYtrkWLU=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ=
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM= google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
@@ -386,12 +381,12 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I= gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs= gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
+1 -1
View File
@@ -75,7 +75,7 @@ elif [[ "${release}" == "rocky" ]]; then
if [[ ${os_version} -lt 9 ]]; then if [[ ${os_version} -lt 9 ]]; then
echo -e "${red} Please use Rocky Linux 9 or higher ${plain}\n" && exit 1 echo -e "${red} Please use Rocky Linux 9 or higher ${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "oracle" ]]; then elif [[ "${release}" == "ol" ]]; then
if [[ ${os_version} -lt 8 ]]; then if [[ ${os_version} -lt 8 ]]; then
echo -e "${red} Please use Oracle Linux 8 or higher ${plain}\n" && exit 1 echo -e "${red} Please use Oracle Linux 8 or higher ${plain}\n" && exit 1
fi fi
+63 -37
View File
@@ -1,4 +1,3 @@
#!/bin/bash #!/bin/bash
red='\033[0;31m' red='\033[0;31m'
@@ -6,7 +5,6 @@ green='\033[0;32m'
yellow='\033[0;33m' yellow='\033[0;33m'
plain='\033[0m' plain='\033[0m'
#Add some basic function here
function LOGD() { function LOGD() {
echo -e "${yellow}[DEG] $* ${plain}" echo -e "${yellow}[DEG] $* ${plain}"
} }
@@ -18,10 +16,9 @@ function LOGE() {
function LOGI() { function LOGI() {
echo -e "${green}[INF] $* ${plain}" echo -e "${green}[INF] $* ${plain}"
} }
# check root
[[ $EUID -ne 0 ]] && LOGE "ERROR: You must be root to run this script! \n" && exit 1 [[ $EUID -ne 0 ]] && LOGE "ERROR: You must be root to run this script! \n" && exit 1
# Check OS and set release variable
if [[ -f /etc/os-release ]]; then if [[ -f /etc/os-release ]]; then
source /etc/os-release source /etc/os-release
release=$ID release=$ID
@@ -35,7 +32,6 @@ fi
echo "The OS release is: $release" echo "The OS release is: $release"
os_version="" os_version=""
os_version=$(grep -i version_id /etc/os-release | cut -d \" -f2 | cut -d . -f1) os_version=$(grep -i version_id /etc/os-release | cut -d \" -f2 | cut -d . -f1)
@@ -54,8 +50,8 @@ elif [[ "${release}" == "centos" ]]; then
echo -e "${red} Please use CentOS 8 or higher ${plain}\n" && exit 1 echo -e "${red} Please use CentOS 8 or higher ${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "ubuntu" ]]; then elif [[ "${release}" == "ubuntu" ]]; then
if [[ ${os_version} -lt 20 ]]; then if [[ ${os_version} -lt 22 ]]; then
echo -e "${red} Please use Ubuntu 20 or higher version!${plain}\n" && exit 1 echo -e "${red} Please use Ubuntu 22 or higher version!${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "fedora" ]]; then elif [[ "${release}" == "fedora" ]]; then
if [[ ${os_version} -lt 36 ]]; then if [[ ${os_version} -lt 36 ]]; then
@@ -80,7 +76,7 @@ elif [[ "${release}" == "oracle" ]]; then
else else
echo -e "${red}Your operating system is not supported by this script.${plain}\n" echo -e "${red}Your operating system is not supported by this script.${plain}\n"
echo "Please ensure you are using one of the following supported operating systems:" echo "Please ensure you are using one of the following supported operating systems:"
echo "- Ubuntu 20.04+" echo "- Ubuntu 22.04+"
echo "- Debian 11+" echo "- Debian 11+"
echo "- CentOS 8+" echo "- CentOS 8+"
echo "- Fedora 36+" echo "- Fedora 36+"
@@ -93,7 +89,6 @@ else
echo "- Oracle Linux 8+" echo "- Oracle Linux 8+"
echo "- OpenSUSE Tumbleweed" echo "- OpenSUSE Tumbleweed"
exit 1 exit 1
fi fi
confirm() { confirm() {
@@ -164,7 +159,6 @@ custom_version() {
download_link="https://raw.githubusercontent.com/alireza0/s-ui/master/install.sh" download_link="https://raw.githubusercontent.com/alireza0/s-ui/master/install.sh"
# Use the entered panel version in the download link
install_command="bash <(curl -Ls $download_link) $panel_version" install_command="bash <(curl -Ls $download_link) $panel_version"
echo "Downloading and installing panel version $panel_version..." echo "Downloading and installing panel version $panel_version..."
@@ -232,13 +226,11 @@ set_setting() {
echo -e "Enter the ${yellow}panel path${plain} (leave blank for existing/default value):" echo -e "Enter the ${yellow}panel path${plain} (leave blank for existing/default value):"
read config_path read config_path
# Sub configuration
echo -e "Enter the ${yellow}subscription port${plain} (leave blank for existing/default value):" echo -e "Enter the ${yellow}subscription port${plain} (leave blank for existing/default value):"
read config_subPort read config_subPort
echo -e "Enter the ${yellow}subscription path${plain} (leave blank for existing/default value):" echo -e "Enter the ${yellow}subscription path${plain} (leave blank for existing/default value):"
read config_subPath read config_subPath
# Set configs
echo -e "${yellow}Initializing, please wait...${plain}" echo -e "${yellow}Initializing, please wait...${plain}"
params="" params=""
[ -z "$config_port" ] || params="$params -port $config_port" [ -z "$config_port" ] || params="$params -port $config_port"
@@ -373,7 +365,6 @@ update_shell() {
fi fi
} }
# 0: running, 1: not running, 2: not installed
check_status() { check_status() {
if [[ ! -f "/etc/systemd/system/$1.service" ]]; then if [[ ! -f "/etc/systemd/system/$1.service" ]]; then
return 2 return 2
@@ -487,20 +478,13 @@ bbr_menu() {
} }
disable_bbr() { disable_bbr() {
if ! grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf || ! grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then if ! grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf || ! grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then
echo -e "${yellow}BBR is not currently enabled.${plain}" echo -e "${yellow}BBR is not currently enabled.${plain}"
exit 0 exit 0
fi fi
# Replace BBR with CUBIC configurations
sed -i 's/net.core.default_qdisc=fq/net.core.default_qdisc=pfifo_fast/' /etc/sysctl.conf sed -i 's/net.core.default_qdisc=fq/net.core.default_qdisc=pfifo_fast/' /etc/sysctl.conf
sed -i 's/net.ipv4.tcp_congestion_control=bbr/net.ipv4.tcp_congestion_control=cubic/' /etc/sysctl.conf sed -i 's/net.ipv4.tcp_congestion_control=bbr/net.ipv4.tcp_congestion_control=cubic/' /etc/sysctl.conf
# Apply changes
sysctl -p sysctl -p
# Verify that BBR is replaced with CUBIC
if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "cubic" ]]; then if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "cubic" ]]; then
echo -e "${green}BBR has been replaced with CUBIC successfully.${plain}" echo -e "${green}BBR has been replaced with CUBIC successfully.${plain}"
else else
@@ -513,8 +497,6 @@ enable_bbr() {
echo -e "${green}BBR is already enabled!${plain}" echo -e "${green}BBR is already enabled!${plain}"
exit 0 exit 0
fi fi
# Check the OS and install necessary packages
case "${release}" in case "${release}" in
ubuntu | debian | armbian) ubuntu | debian | armbian)
apt-get update && apt-get install -yqq --no-install-recommends ca-certificates apt-get update && apt-get install -yqq --no-install-recommends ca-certificates
@@ -533,15 +515,9 @@ enable_bbr() {
exit 1 exit 1
;; ;;
esac esac
# Enable BBR
echo "net.core.default_qdisc=fq" | tee -a /etc/sysctl.conf echo "net.core.default_qdisc=fq" | tee -a /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" | tee -a /etc/sysctl.conf echo "net.ipv4.tcp_congestion_control=bbr" | tee -a /etc/sysctl.conf
# Apply changes
sysctl -p sysctl -p
# Verify that BBR is enabled
if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "bbr" ]]; then if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "bbr" ]]; then
echo -e "${green}BBR has been enabled successfully.${plain}" echo -e "${green}BBR has been enabled successfully.${plain}"
else else
@@ -566,6 +542,7 @@ ssl_cert_issue_main() {
echo -e "${green}\t1.${plain} Get SSL" echo -e "${green}\t1.${plain} Get SSL"
echo -e "${green}\t2.${plain} Revoke" echo -e "${green}\t2.${plain} Revoke"
echo -e "${green}\t3.${plain} Force Renew" echo -e "${green}\t3.${plain} Force Renew"
echo -e "${green}\t4.${plain} Self-signed Certificate"
read -p "Choose an option: " choice read -p "Choose an option: " choice
case "$choice" in case "$choice" in
1) ssl_cert_issue ;; 1) ssl_cert_issue ;;
@@ -579,12 +556,14 @@ ssl_cert_issue_main() {
local domain="" local domain=""
read -p "Please enter your domain name to forcefully renew an SSL certificate: " domain read -p "Please enter your domain name to forcefully renew an SSL certificate: " domain
~/.acme.sh/acme.sh --renew -d ${domain} --force ;; ~/.acme.sh/acme.sh --renew -d ${domain} --force ;;
4)
generate_self_signed_cert
;;
*) echo "Invalid choice" ;; *) echo "Invalid choice" ;;
esac esac
} }
ssl_cert_issue() { ssl_cert_issue() {
# check for acme.sh first
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
echo "acme.sh could not be found. we will install it" echo "acme.sh could not be found. we will install it"
install_acme install_acme
@@ -593,7 +572,6 @@ ssl_cert_issue() {
exit 1 exit 1
fi fi
fi fi
# install socat second
case "${release}" in case "${release}" in
ubuntu | debian | armbian) ubuntu | debian | armbian)
apt update && apt install socat -y apt update && apt install socat -y
@@ -619,11 +597,9 @@ ssl_cert_issue() {
LOGI "install socat succeed..." LOGI "install socat succeed..."
fi fi
# get the domain here,and we need verify it
local domain="" local domain=""
read -p "Please enter your domain name:" domain read -p "Please enter your domain name:" domain
LOGD "your domain is:${domain},check it..." LOGD "your domain is:${domain},check it..."
# here we need to judge whether there exists cert already
local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}') local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}')
if [ ${currentCert} == ${domain} ]; then if [ ${currentCert} == ${domain} ]; then
@@ -635,7 +611,6 @@ ssl_cert_issue() {
LOGI "your domain is ready for issuing cert now..." LOGI "your domain is ready for issuing cert now..."
fi fi
# create a directory for install cert
certPath="/root/cert/${domain}" certPath="/root/cert/${domain}"
if [ ! -d "$certPath" ]; then if [ ! -d "$certPath" ]; then
mkdir -p "$certPath" mkdir -p "$certPath"
@@ -644,15 +619,12 @@ ssl_cert_issue() {
mkdir -p "$certPath" mkdir -p "$certPath"
fi fi
# get needed port here
local WebPort=80 local WebPort=80
read -p "please choose which port do you use,default will be 80 port:" WebPort read -p "please choose which port do you use,default will be 80 port:" WebPort
if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then
LOGE "your input ${WebPort} is invalid,will use default port" LOGE "your input ${WebPort} is invalid,will use default port"
fi fi
LOGI "will use port:${WebPort} to issue certs,please make sure this port is open..." LOGI "will use port:${WebPort} to issue certs,please make sure this port is open..."
# NOTE:This should be handled by user
# open the port and kill the occupied progress
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
~/.acme.sh/acme.sh --issue -d ${domain} --standalone --httpport ${WebPort} ~/.acme.sh/acme.sh --issue -d ${domain} --standalone --httpport ${WebPort}
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
@@ -662,7 +634,6 @@ ssl_cert_issue() {
else else
LOGE "issue certs succeed,installing certs..." LOGE "issue certs succeed,installing certs..."
fi fi
# install cert
~/.acme.sh/acme.sh --installcert -d ${domain} \ ~/.acme.sh/acme.sh --installcert -d ${domain} \
--key-file /root/cert/${domain}/privkey.pem \ --key-file /root/cert/${domain}/privkey.pem \
--fullchain-file /root/cert/${domain}/fullchain.pem --fullchain-file /root/cert/${domain}/fullchain.pem
@@ -804,6 +775,61 @@ ssl_cert_issue_CF() {
esac esac
} }
generate_self_signed_cert() {
cert_dir="/etc/sing-box"
mkdir -p "$cert_dir"
LOGI "Choose certificate type:"
echo -e "${green}\t1.${plain} Ed25519 (*recommended*)"
echo -e "${green}\t2.${plain} RSA 2048"
echo -e "${green}\t3.${plain} RSA 4096"
echo -e "${green}\t4.${plain} ECDSA prime256v1"
echo -e "${green}\t5.${plain} ECDSA secp384r1"
read -p "Enter your choice [1-5, default 1]: " cert_type
cert_type=${cert_type:-1}
case "$cert_type" in
1)
algo="ed25519"
key_opt="-newkey ed25519"
;;
2)
algo="rsa"
key_opt="-newkey rsa:2048"
;;
3)
algo="rsa"
key_opt="-newkey rsa:4096"
;;
4)
algo="ecdsa"
key_opt="-newkey ec -pkeyopt ec_paramgen_curve:prime256v1"
;;
5)
algo="ecdsa"
key_opt="-newkey ec -pkeyopt ec_paramgen_curve:secp384r1"
;;
*)
algo="ed25519"
key_opt="-newkey ed25519"
;;
esac
LOGI "Generating self-signed certificate ($algo)..."
sudo openssl req -x509 -nodes -days 3650 $key_opt \
-keyout "${cert_dir}/self.key" \
-out "${cert_dir}/self.crt" \
-subj "/CN=myserver"
if [[ $? -eq 0 ]]; then
sudo chmod 600 "${cert_dir}/self."*
LOGI "Self-signed certificate generated successfully!"
LOGI "Certificate path: ${cert_dir}/self.crt"
LOGI "Key path: ${cert_dir}/self.key"
else
LOGE "Failed to generate self-signed certificate."
fi
before_show_menu
}
show_usage() { show_usage() {
echo -e "S-UI Control Menu Usage" echo -e "S-UI Control Menu Usage"
echo -e "------------------------------------------" echo -e "------------------------------------------"
+60 -42
View File
@@ -1,6 +1,7 @@
package service package service
import ( import (
"bytes"
"encoding/json" "encoding/json"
"s-ui/database" "s-ui/database"
"s-ui/database/model" "s-ui/database/model"
@@ -13,9 +14,7 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
type ClientService struct { type ClientService struct{}
InboundService
}
func (s *ClientService) Get(id string) (*[]model.Client, error) { func (s *ClientService) Get(id string) (*[]model.Client, error) {
if id == "" { if id == "" {
@@ -56,13 +55,21 @@ func (s *ClientService) Save(tx *gorm.DB, act string, data json.RawMessage, host
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.updateLinksWithFixedInbounds(tx, []*model.Client{&client}, hostname)
if err != nil {
return nil, err
}
if act == "edit" {
// Find changed inbounds
inboundIds, err = s.findInboundsChanges(tx, client)
if err != nil {
return nil, err
}
} else {
err = json.Unmarshal(client.Inbounds, &inboundIds) err = json.Unmarshal(client.Inbounds, &inboundIds)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.updateLinksWithFixedInbounds(tx, []*model.Client{&client}, inboundIds, hostname)
if err != nil {
return nil, err
} }
err = tx.Save(&client).Error err = tx.Save(&client).Error
if err != nil { if err != nil {
@@ -78,7 +85,7 @@ func (s *ClientService) Save(tx *gorm.DB, act string, data json.RawMessage, host
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.updateLinksWithFixedInbounds(tx, clients, inboundIds, hostname) err = s.updateLinksWithFixedInbounds(tx, clients, hostname)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -112,13 +119,19 @@ func (s *ClientService) Save(tx *gorm.DB, act string, data json.RawMessage, host
return inboundIds, nil return inboundIds, nil
} }
func (s *ClientService) updateLinksWithFixedInbounds(tx *gorm.DB, clients []*model.Client, inbounIds []uint, hostname string) error { func (s *ClientService) updateLinksWithFixedInbounds(tx *gorm.DB, clients []*model.Client, hostname string) error {
var err error var err error
var inbounds []model.Inbound var inbounds []model.Inbound
var inboundIds []uint
err = json.Unmarshal(clients[0].Inbounds, &inboundIds)
if err != nil {
return err
}
// Zero inbounds means removing local links only // Zero inbounds means removing local links only
if len(inbounIds) > 0 { if len(inboundIds) > 0 {
err = tx.Model(model.Inbound{}).Preload("Tls").Where("id in ? and type in ?", inbounIds, util.InboundTypeWithLink).Find(&inbounds).Error err = tx.Model(model.Inbound{}).Preload("Tls").Where("id in ? and type in ?", inboundIds, util.InboundTypeWithLink).Find(&inbounds).Error
if err != nil { if err != nil {
return err return err
} }
@@ -142,7 +155,7 @@ func (s *ClientService) updateLinksWithFixedInbounds(tx *gorm.DB, clients []*mod
} }
} }
// Add no local links // Add non local links
for _, clientLink := range clientLinks { for _, clientLink := range clientLinks {
if clientLink["type"] != "local" { if clientLink["type"] != "local" {
newClientLinks = append(newClientLinks, clientLink) newClientLinks = append(newClientLinks, clientLink)
@@ -248,13 +261,9 @@ func (s *ClientService) UpdateClientsOnInboundDelete(tx *gorm.DB, id uint, tag s
return nil return nil
} }
func (s *ClientService) UpdateLinksByInboundChange(tx *gorm.DB, inbounIds []uint, hostname string) error { func (s *ClientService) UpdateLinksByInboundChange(tx *gorm.DB, inbounds *[]model.Inbound, hostname string, oldTag string) error {
var inbounds []model.Inbound var err error
err := tx.Model(model.Inbound{}).Preload("Tls").Where("id in ? and type in ?", inbounIds, util.InboundTypeWithLink).Find(&inbounds).Error for _, inbound := range *inbounds {
if err != nil && database.IsNotFound(err) {
return err
}
for _, inbound := range inbounds {
var clients []model.Client var clients []model.Client
err = tx.Table("clients"). err = tx.Table("clients").
Where("EXISTS (SELECT 1 FROM json_each(clients.inbounds) WHERE json_each.value = ?)", inbound.Id). Where("EXISTS (SELECT 1 FROM json_each(clients.inbounds) WHERE json_each.value = ?)", inbound.Id).
@@ -274,7 +283,7 @@ func (s *ClientService) UpdateLinksByInboundChange(tx *gorm.DB, inbounIds []uint
}) })
} }
for _, clientLink := range clientLinks { for _, clientLink := range clientLinks {
if clientLink["remark"] != inbound.Tag { if clientLink["remark"] != inbound.Tag && clientLink["remark"] != oldTag {
newClientLinks = append(newClientLinks, clientLink) newClientLinks = append(newClientLinks, clientLink)
} }
} }
@@ -292,7 +301,7 @@ func (s *ClientService) UpdateLinksByInboundChange(tx *gorm.DB, inbounIds []uint
return nil return nil
} }
func (s *ClientService) DepleteClients() error { func (s *ClientService) DepleteClients() ([]uint, error) {
var err error var err error
var clients []model.Client var clients []model.Client
var changes []model.Changes var changes []model.Changes
@@ -306,12 +315,6 @@ func (s *ClientService) DepleteClients() error {
defer func() { defer func() {
if err == nil { if err == nil {
tx.Commit() tx.Commit()
if len(inboundIds) > 0 && corePtr.IsRunning() {
err1 := s.InboundService.RestartInbounds(db, inboundIds)
if err1 != nil {
logger.Error("unable to restart inbounds: ", err1)
}
}
} else { } else {
tx.Rollback() tx.Rollback()
} }
@@ -319,7 +322,7 @@ func (s *ClientService) DepleteClients() error {
err = tx.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", now).Scan(&clients).Error err = tx.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", now).Scan(&clients).Error
if err != nil { if err != nil {
return err return nil, err
} }
dt := time.Now().Unix() dt := time.Now().Unix()
@@ -328,7 +331,8 @@ func (s *ClientService) DepleteClients() error {
users = append(users, client.Name) users = append(users, client.Name)
var userInbounds []uint var userInbounds []uint
json.Unmarshal(client.Inbounds, &userInbounds) json.Unmarshal(client.Inbounds, &userInbounds)
inboundIds = s.uniqueAppendInboundIds(inboundIds, userInbounds) // Find changed inbounds
inboundIds = common.UnionUintArray(inboundIds, userInbounds)
changes = append(changes, model.Changes{ changes = append(changes, model.Changes{
DateTime: dt, DateTime: dt,
Actor: "DepleteJob", Actor: "DepleteJob",
@@ -342,30 +346,44 @@ func (s *ClientService) DepleteClients() error {
if len(changes) > 0 { if len(changes) > 0 {
err = tx.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", now).Update("enable", false).Error err = tx.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", now).Update("enable", false).Error
if err != nil { if err != nil {
return err return nil, err
} }
err = tx.Model(model.Changes{}).Create(&changes).Error err = tx.Model(model.Changes{}).Create(&changes).Error
if err != nil { if err != nil {
return err return nil, err
} }
LastUpdate = dt LastUpdate = dt
} }
return nil return inboundIds, nil
} }
// avoid duplicate inboundIds func (s *ClientService) findInboundsChanges(tx *gorm.DB, client model.Client) ([]uint, error) {
func (s *ClientService) uniqueAppendInboundIds(a []uint, b []uint) []uint { var err error
m := make(map[uint]bool) var oldClient model.Client
for _, v := range a { var oldInboundIds, newInboundIds []uint
m[v] = true err = tx.Model(model.Client{}).Where("id = ?", client.Id).First(&oldClient).Error
if err != nil {
return nil, err
} }
for _, v := range b { err = json.Unmarshal(oldClient.Inbounds, &oldInboundIds)
m[v] = true if err != nil {
return nil, err
} }
var res []uint err = json.Unmarshal(client.Inbounds, &newInboundIds)
for k := range m { if err != nil {
res = append(res, k) return nil, err
} }
return res
// Check client.Config changes
if !bytes.Equal(oldClient.Config, client.Config) ||
oldClient.Name != client.Name ||
oldClient.Enable != client.Enable {
return common.UnionUintArray(oldInboundIds, newInboundIds), nil
}
// Check client.Inbounds changes
diffInbounds := common.DiffUintArray(oldInboundIds, newInboundIds)
return diffInbounds, nil
} }
+12 -60
View File
@@ -124,9 +124,6 @@ func (s *ConfigService) StopCore() error {
func (s *ConfigService) Save(obj string, act string, data json.RawMessage, initUsers string, loginUser string, hostname string) ([]string, error) { func (s *ConfigService) Save(obj string, act string, data json.RawMessage, initUsers string, loginUser string, hostname string) ([]string, error) {
var err error var err error
var inboundIds []uint
var serviceIds []uint
var inboundId uint
var objs []string = []string{obj} var objs []string = []string{obj}
db := database.GetDB() db := database.GetDB()
@@ -134,18 +131,6 @@ func (s *ConfigService) Save(obj string, act string, data json.RawMessage, initU
defer func() { defer func() {
if err == nil { if err == nil {
tx.Commit() tx.Commit()
if len(inboundIds) > 0 && corePtr.IsRunning() {
err1 := s.InboundService.RestartInbounds(db, inboundIds)
if err1 != nil {
logger.Error("unable to restart inbounds: ", err1)
}
}
if len(serviceIds) > 0 && corePtr.IsRunning() {
err1 := s.ServicesService.RestartServices(db, serviceIds)
if err1 != nil {
logger.Error("unable to restart services: ", err1)
}
}
// Try to start core if it is not running // Try to start core if it is not running
if !corePtr.IsRunning() { if !corePtr.IsRunning() {
s.StartCore("") s.StartCore("")
@@ -157,12 +142,20 @@ func (s *ConfigService) Save(obj string, act string, data json.RawMessage, initU
switch obj { switch obj {
case "clients": case "clients":
inboundIds, err = s.ClientService.Save(tx, act, data, hostname) inboundIds, err := s.ClientService.Save(tx, act, data, hostname)
if err == nil && len(inboundIds) > 0 {
objs = append(objs, "inbounds") objs = append(objs, "inbounds")
err = s.InboundService.RestartInbounds(tx, inboundIds)
if err != nil {
return nil, common.NewErrorf("failed to update users for inbounds: %v", err)
}
}
case "tls": case "tls":
serviceIds, inboundIds, err = s.TlsService.Save(tx, act, data) err = s.TlsService.Save(tx, act, data, hostname)
objs = append(objs, "clients", "inbounds")
case "inbounds": case "inbounds":
inboundId, err = s.InboundService.Save(tx, act, data, initUsers, hostname) err = s.InboundService.Save(tx, act, data, initUsers, hostname)
objs = append(objs, "clients")
case "outbounds": case "outbounds":
err = s.OutboundService.Save(tx, act, data) err = s.OutboundService.Save(tx, act, data)
case "services": case "services":
@@ -195,49 +188,8 @@ func (s *ConfigService) Save(obj string, act string, data json.RawMessage, initU
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Commit changes so far
tx.Commit()
LastUpdate = time.Now().Unix() LastUpdate = time.Now().Unix()
tx = db.Begin()
// Update side changes
// Update client links
if obj == "tls" && len(inboundIds) > 0 {
err = s.ClientService.UpdateLinksByInboundChange(tx, inboundIds, hostname)
if err != nil {
return nil, err
}
objs = append(objs, "clients")
}
if obj == "inbounds" {
switch act {
case "new":
err = s.ClientService.UpdateClientsOnInboundAdd(tx, initUsers, inboundId, hostname)
case "edit":
err = s.ClientService.UpdateLinksByInboundChange(tx, []uint{inboundId}, hostname)
case "del":
var tag string
err = json.Unmarshal(data, &tag)
if err != nil {
return nil, err
}
err = s.ClientService.UpdateClientsOnInboundDelete(tx, inboundId, tag)
}
if err != nil {
return nil, err
}
objs = append(objs, "clients")
}
// Update out_json of inbounds when tls is changed
if obj == "tls" && len(inboundIds) > 0 {
err = s.InboundService.UpdateOutJsons(tx, inboundIds, hostname)
if err != nil {
return nil, common.NewError("unable to update out_json of inbounds: ", err.Error())
}
objs = append(objs, "inbounds")
}
return objs, nil return objs, nil
} }
+42 -23
View File
@@ -13,7 +13,9 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
type InboundService struct{} type InboundService struct {
ClientService
}
func (s *InboundService) Get(ids string) (*[]map[string]interface{}, error) { func (s *InboundService) Get(ids string) (*[]map[string]interface{}, error) {
if ids == "" { if ids == "" {
@@ -97,40 +99,41 @@ func (s *InboundService) FromIds(ids []uint) ([]*model.Inbound, error) {
return inbounds, nil return inbounds, nil
} }
func (s *InboundService) Save(tx *gorm.DB, act string, data json.RawMessage, initUserIds string, hostname string) (uint, error) { func (s *InboundService) Save(tx *gorm.DB, act string, data json.RawMessage, initUserIds string, hostname string) error {
var err error var err error
var id uint
switch act { switch act {
case "new", "edit": case "new", "edit":
var inbound model.Inbound var inbound model.Inbound
err = inbound.UnmarshalJSON(data) err = inbound.UnmarshalJSON(data)
if err != nil { if err != nil {
return 0, err return err
} }
if inbound.TlsId > 0 { if inbound.TlsId > 0 {
err = tx.Model(model.Tls{}).Where("id = ?", inbound.TlsId).Find(&inbound.Tls).Error err = tx.Model(model.Tls{}).Where("id = ?", inbound.TlsId).Find(&inbound.Tls).Error
if err != nil { if err != nil {
return 0, err return err
}
}
var oldTag string
if act == "edit" {
err = tx.Model(model.Inbound{}).Select("tag").Where("id = ?", inbound.Id).Find(&oldTag).Error
if err != nil {
return err
} }
} }
if corePtr.IsRunning() { if corePtr.IsRunning() {
if act == "edit" { if act == "edit" {
var oldTag string
err = tx.Model(model.Inbound{}).Select("tag").Where("id = ?", inbound.Id).Find(&oldTag).Error
if err != nil {
return 0, err
}
err = corePtr.RemoveInbound(oldTag) err = corePtr.RemoveInbound(oldTag)
if err != nil && err != os.ErrInvalid { if err != nil && err != os.ErrInvalid {
return 0, err return err
} }
} }
inboundConfig, err := inbound.MarshalJSON() inboundConfig, err := inbound.MarshalJSON()
if err != nil { if err != nil {
return 0, err return err
} }
if act == "edit" { if act == "edit" {
@@ -139,49 +142,62 @@ func (s *InboundService) Save(tx *gorm.DB, act string, data json.RawMessage, ini
inboundConfig, err = s.initUsers(tx, inboundConfig, initUserIds, inbound.Type) inboundConfig, err = s.initUsers(tx, inboundConfig, initUserIds, inbound.Type)
} }
if err != nil { if err != nil {
return 0, err return err
} }
err = corePtr.AddInbound(inboundConfig) err = corePtr.AddInbound(inboundConfig)
if err != nil { if err != nil {
return 0, err return err
} }
} }
err = util.FillOutJson(&inbound, hostname) err = util.FillOutJson(&inbound, hostname)
if err != nil { if err != nil {
return 0, err return err
} }
err = tx.Save(&inbound).Error err = tx.Save(&inbound).Error
if err != nil { if err != nil {
return 0, err return err
}
switch act {
case "new":
err = s.ClientService.UpdateClientsOnInboundAdd(tx, initUserIds, inbound.Id, hostname)
case "edit":
err = s.ClientService.UpdateLinksByInboundChange(tx, &[]model.Inbound{inbound}, hostname, oldTag)
}
if err != nil {
return err
} }
id = inbound.Id
case "del": case "del":
var tag string var tag string
err = json.Unmarshal(data, &tag) err = json.Unmarshal(data, &tag)
if err != nil { if err != nil {
return 0, err return err
} }
if corePtr.IsRunning() { if corePtr.IsRunning() {
err = corePtr.RemoveInbound(tag) err = corePtr.RemoveInbound(tag)
if err != nil && err != os.ErrInvalid { if err != nil && err != os.ErrInvalid {
return 0, err return err
} }
} }
var id uint
err = tx.Model(model.Inbound{}).Select("id").Where("tag = ?", tag).Scan(&id).Error err = tx.Model(model.Inbound{}).Select("id").Where("tag = ?", tag).Scan(&id).Error
if err != nil { if err != nil {
return 0, err return err
}
err = s.ClientService.UpdateClientsOnInboundDelete(tx, id, tag)
if err != nil {
return err
} }
err = tx.Where("tag = ?", tag).Delete(model.Inbound{}).Error err = tx.Where("tag = ?", tag).Delete(model.Inbound{}).Error
if err != nil { if err != nil {
return 0, err return err
} }
default: default:
return 0, common.NewErrorf("unknown action: %s", act) return common.NewErrorf("unknown action: %s", act)
} }
return id, nil return nil
} }
func (s *InboundService) UpdateOutJsons(tx *gorm.DB, inboundIds []uint, hostname string) error { func (s *InboundService) UpdateOutJsons(tx *gorm.DB, inboundIds []uint, hostname string) error {
@@ -312,6 +328,9 @@ func (s *InboundService) initUsers(db *gorm.DB, inboundJson []byte, clientIds st
} }
func (s *InboundService) RestartInbounds(tx *gorm.DB, ids []uint) error { func (s *InboundService) RestartInbounds(tx *gorm.DB, ids []uint) error {
if !corePtr.IsRunning() {
return nil
}
var inbounds []*model.Inbound var inbounds []*model.Inbound
err := tx.Model(model.Inbound{}).Preload("Tls").Where("id in ?", ids).Find(&inbounds).Error err := tx.Model(model.Inbound{}).Preload("Tls").Where("id in ?", ids).Find(&inbounds).Error
if err != nil { if err != nil {
+50
View File
@@ -12,6 +12,7 @@ import (
"github.com/sagernet/sing-box/common/tls" "github.com/sagernet/sing-box/common/tls"
"github.com/shirou/gopsutil/v4/cpu" "github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/disk"
"github.com/shirou/gopsutil/v4/host" "github.com/shirou/gopsutil/v4/host"
"github.com/shirou/gopsutil/v4/mem" "github.com/shirou/gopsutil/v4/mem"
"github.com/shirou/gopsutil/v4/net" "github.com/shirou/gopsutil/v4/net"
@@ -29,6 +30,12 @@ func (s *ServerService) GetStatus(request string) *map[string]interface{} {
status["cpu"] = s.GetCpuPercent() status["cpu"] = s.GetCpuPercent()
case "mem": case "mem":
status["mem"] = s.GetMemInfo() status["mem"] = s.GetMemInfo()
case "dsk":
status["dsk"] = s.GetDiskInfo()
case "dio":
status["dio"] = s.GetDiskIO()
case "swp":
status["swp"] = s.GetSwapInfo()
case "net": case "net":
status["net"] = s.GetNetInfo() status["net"] = s.GetNetInfo()
case "sys": case "sys":
@@ -73,6 +80,49 @@ func (s *ServerService) GetMemInfo() map[string]interface{} {
return info return info
} }
func (s *ServerService) GetDiskInfo() map[string]interface{} {
info := make(map[string]interface{}, 0)
diskInfo, err := disk.Usage("/")
if err != nil {
logger.Warning("get disk usage failed:", err)
} else {
info["current"] = diskInfo.Used
info["total"] = diskInfo.Total
}
return info
}
func (s *ServerService) GetDiskIO() map[string]interface{} {
info := make(map[string]interface{}, 0)
ioStats, err := disk.IOCounters()
if err != nil {
logger.Warning("get disk io counters failed:", err)
} else if len(ioStats) > 0 {
infoR, infoW := uint64(0), uint64(0)
for _, ioStat := range ioStats {
infoR += ioStat.ReadBytes
infoW += ioStat.WriteBytes
}
info["read"] = infoR
info["write"] = infoW
} else {
logger.Warning("can not find disk io counters")
}
return info
}
func (s *ServerService) GetSwapInfo() map[string]interface{} {
info := make(map[string]interface{}, 0)
swapInfo, err := mem.SwapMemory()
if err != nil {
logger.Warning("get swap memory failed:", err)
} else {
info["current"] = swapInfo.Used
info["total"] = swapInfo.Total
}
return info
}
func (s *ServerService) GetNetInfo() map[string]interface{} { func (s *ServerService) GetNetInfo() map[string]interface{} {
info := make(map[string]interface{}, 0) info := make(map[string]interface{}, 0)
ioStats, err := net.IOCounters(false) ioStats, err := net.IOCounters(false)
+3
View File
@@ -126,6 +126,9 @@ func (s *ServicesService) Save(tx *gorm.DB, act string, data json.RawMessage) er
} }
func (s *ServicesService) RestartServices(tx *gorm.DB, ids []uint) error { func (s *ServicesService) RestartServices(tx *gorm.DB, ids []uint) error {
if !corePtr.IsRunning() {
return nil
}
var services []*model.Service var services []*model.Service
err := tx.Model(model.Service{}).Preload("Tls").Where("id in ?", ids).Find(&services).Error err := tx.Model(model.Service{}).Preload("Tls").Where("id in ?", ids).Find(&services).Error
if err != nil { if err != nil {
+5
View File
@@ -56,6 +56,7 @@ var defaultValueMap = map[string]string{
"subShowInfo": "false", "subShowInfo": "false",
"subURI": "", "subURI": "",
"subJsonExt": "", "subJsonExt": "",
"subClashExt": "",
"config": defaultConfig, "config": defaultConfig,
"version": config.GetVersion(), "version": config.GetVersion(),
} }
@@ -392,6 +393,10 @@ func (s *SettingService) GetSubJsonExt() (string, error) {
return s.getString("subJsonExt") return s.getString("subJsonExt")
} }
func (s *SettingService) GetSubClashExt() (string, error) {
return s.getString("subClashExt")
}
func (s *SettingService) fileExists(path string) error { func (s *SettingService) fileExists(path string) error {
_, err := os.Stat(path) _, err := os.Stat(path)
return err return err
+43 -14
View File
@@ -11,6 +11,7 @@ import (
type TlsService struct { type TlsService struct {
InboundService InboundService
ServicesService
} }
func (s *TlsService) GetAll() ([]model.Tls, error) { func (s *TlsService) GetAll() ([]model.Tls, error) {
@@ -24,52 +25,80 @@ func (s *TlsService) GetAll() ([]model.Tls, error) {
return tlsConfig, nil return tlsConfig, nil
} }
func (s *TlsService) Save(tx *gorm.DB, action string, data json.RawMessage) ([]uint, []uint, error) { func (s *TlsService) Save(tx *gorm.DB, action string, data json.RawMessage, hostname string) error {
var err error var err error
var inboundIds []uint
var serviceIds []uint
switch action { switch action {
case "new", "edit": case "new", "edit":
var tls model.Tls var tls model.Tls
err = json.Unmarshal(data, &tls) err = json.Unmarshal(data, &tls)
if err != nil { if err != nil {
return nil, nil, err return err
} }
err = tx.Save(&tls).Error err = tx.Save(&tls).Error
if err != nil { if err != nil {
return nil, nil, err return err
} }
err = tx.Model(model.Inbound{}).Select("id").Where("tls_id = ?", tls.Id).Scan(&inboundIds).Error if action == "edit" {
var inbounds []model.Inbound
err = tx.Model(model.Inbound{}).Preload("Tls").Where("tls_id = ?", tls.Id).Find(&inbounds).Error
if err != nil { if err != nil {
return nil, nil, err return err
} }
if len(inbounds) > 0 {
err = s.ClientService.UpdateLinksByInboundChange(tx, &inbounds, hostname, "")
if err != nil {
return err
}
var inboundIds []uint
for _, inbound := range inbounds {
inboundIds = append(inboundIds, inbound.Id)
}
err = s.InboundService.UpdateOutJsons(tx, inboundIds, hostname)
if err != nil {
return common.NewError("unable to update out_json of inbounds: ", err.Error())
}
err = s.InboundService.RestartInbounds(tx, inboundIds)
if err != nil {
return err
}
}
var serviceIds []uint
err = tx.Model(model.Service{}).Where("tls_id = ?", tls.Id).Scan(&serviceIds).Error err = tx.Model(model.Service{}).Where("tls_id = ?", tls.Id).Scan(&serviceIds).Error
return serviceIds, inboundIds, nil if err != nil {
return err
}
if len(serviceIds) > 0 {
err = s.ServicesService.RestartServices(tx, serviceIds)
if err != nil {
return err
}
}
}
case "del": case "del":
var id uint var id uint
err = json.Unmarshal(data, &id) err = json.Unmarshal(data, &id)
if err != nil { if err != nil {
return nil, nil, err return err
} }
var inboundCount int64 var inboundCount int64
err = tx.Model(model.Inbound{}).Where("tls_id = ?", id).Count(&inboundCount).Error err = tx.Model(model.Inbound{}).Where("tls_id = ?", id).Count(&inboundCount).Error
if err != nil { if err != nil {
return nil, nil, err return err
} }
var serviceCount int64 var serviceCount int64
err = tx.Model(model.Service{}).Where("tls_id = ?", id).Count(&serviceCount).Error err = tx.Model(model.Service{}).Where("tls_id = ?", id).Count(&serviceCount).Error
if err != nil { if err != nil {
return nil, nil, err return err
} }
if inboundCount > 0 || serviceCount > 0 { if inboundCount > 0 || serviceCount > 0 {
return nil, nil, common.NewError("tls in use") return common.NewError("tls in use")
} }
err = tx.Where("id = ?", id).Delete(model.Tls{}).Error err = tx.Where("id = ?", id).Delete(model.Tls{}).Error
if err != nil { if err != nil {
return nil, nil, err return err
} }
} }
return nil, nil, nil return nil
} }
+332
View File
@@ -0,0 +1,332 @@
package sub
import (
"s-ui/logger"
"s-ui/service"
"s-ui/util"
"strings"
"gopkg.in/yaml.v3"
)
type ClashService struct {
service.SettingService
JsonService
LinkService
}
const basicClashConfig = `mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
external-controller: 127.0.0.1:9090
tun:
enable: true
stack: system
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
dns:
enable: true
ipv6: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
default-nameserver:
- 8.8.8.8
- 1.1.1.1
nameserver:
- https://doh.pub/dns-query
- https://1.0.0.1/dns-query
fallback:
- tcp://9.9.9.9:53
fake-ip-filter:
- "*.lan"
- localhost
- "*.local"
rules:
- GEOIP,Private,DIRECT
- MATCH,Proxy
`
const ProxyGroups = `- name: Proxy
type: select
proxies: []
- name: Auto
type: url-test
proxies: []
url: http://www.gstatic.com/generate_204
interval: 300
tolerance: 50
`
func (s *ClashService) GetClash(subId string) (*string, []string, error) {
client, inDatas, err := s.getData(subId)
if err != nil {
return nil, nil, err
}
outbounds, outTags, err := s.getOutbounds(client.Config, inDatas)
if err != nil {
return nil, nil, err
}
links := s.LinkService.GetLinks(&client.Links, "external", "")
for index, link := range links {
json, tag, err := util.GetOutbound(link, index)
if err == nil && len(tag) > 0 {
*outbounds = append(*outbounds, *json)
*outTags = append(*outTags, tag)
}
}
othersStr, err := s.getClashConfig()
if err != nil || len(othersStr) == 0 {
othersStr = basicClashConfig
}
result, err := s.ConvertToClashMeta(outbounds)
resultStr := othersStr + "\n" + string(result)
updateInterval, _ := s.SettingService.GetSubUpdates()
headers := util.GetHeaders(client, updateInterval)
return &resultStr, headers, nil
}
func (s *ClashService) getClashConfig() (string, error) {
subClashExt, err := s.SettingService.GetSubClashExt()
if err != nil {
return "", err
}
return subClashExt, nil
}
func (s *ClashService) ConvertToClashMeta(outbounds *[]map[string]interface{}) ([]byte, error) {
var proxies []interface{}
proxyTags := make([]string, 0)
for _, obMap := range *outbounds {
t, _ := obMap["type"].(string)
if t == "selector" || t == "urltest" || t == "direct" {
continue
}
proxy := make(map[string]interface{})
proxy["name"] = obMap["tag"]
proxy["type"] = t
proxy["server"] = obMap["server"]
proxy["port"] = obMap["server_port"]
switch t {
case "vmess", "vless", "tuic":
proxy["uuid"] = obMap["uuid"]
if t == "vmess" {
proxy["alterId"] = obMap["alter_id"]
proxy["cipher"] = "auto"
}
if t == "vless" {
if flow, ok := obMap["flow"].(string); ok {
proxy["flow"] = flow
}
}
case "trojan":
proxy["password"] = obMap["password"]
case "socks", "http":
if t == "socks" {
proxy["type"] = "socks5"
}
proxy["username"] = obMap["username"]
proxy["password"] = obMap["password"]
case "hysteria", "hysteria2":
if _, ok := obMap["up_mbps"].(float64); ok {
proxy["up"] = obMap["up_mbps"]
} else {
proxy["up"] = 1000
}
if _, ok := obMap["down_mbps"].(float64); ok {
proxy["down"] = obMap["down_mbps"]
} else {
proxy["down"] = 1000
}
if t == "hysteria" {
proxy["auth-str"] = obMap["auth_str"]
if obfs, ok := obMap["obfs"].(string); ok {
proxy["obfs"] = obfs
}
} else {
proxy["password"] = obMap["password"]
if obfs, ok := obMap["obfs"].(map[string]interface{}); ok {
proxy["obfs"] = obfs["type"]
proxy["obfs-password"] = obfs["password"]
}
if ports, ok := obMap["server_ports"].([]string); ok {
proxy["ports"] = strings.ReplaceAll(strings.Join(ports, ","), ":", "-")
}
}
case "anytls":
proxy["password"] = obMap["password"]
if tls, ok := obMap["tls"].(map[string]interface{}); ok {
proxy["sni"] = tls["server_name"]
proxy["skip-cert-verify"] = tls["insecure"]
}
default:
continue
}
// TLS params
tls, isTls := obMap["tls"].(map[string]interface{})
if isTls {
tlsEnabled, ok := tls["enabled"].(bool)
if ok && !tlsEnabled {
isTls = false
}
}
if isTls {
// ignore ech outbounds
if _, ok := tls["ech"].(interface{}); ok {
continue
}
proxy["tls"] = tls["enabled"]
// ALPN if exists
if alpn, ok := tls["alpn"].([]interface{}); ok {
proxy["alpn"] = alpn
}
// Add reality if exists
if reality, ok := tls["reality"].(map[string]interface{}); ok && reality["enabled"].(bool) {
reality_opts := make(map[string]interface{})
if pbk, ok := reality["public_key"].(string); ok {
reality_opts["public-key"] = pbk
}
if sid, ok := reality["short_id"].(string); ok {
reality_opts["short-id"] = sid
}
proxy["reality-opts"] = reality_opts
}
if utls, ok := tls["utls"].(map[string]interface{}); ok {
if enabled, ok := utls["enabled"].(bool); ok && enabled {
if fp, ok := utls["fingerprint"].(string); ok {
proxy["client-fingerprint"] = fp
}
}
}
if sni, ok := tls["server_name"].(string); ok {
if t == "http" {
proxy["sni"] = sni
} else {
proxy["servername"] = sni
}
}
if insecure, ok := tls["insecure"].(bool); ok && insecure {
proxy["skip-cert-verify"] = insecure
}
}
// Transport if exist
if transport, ok := obMap["transport"].(map[string]interface{}); ok {
tt, _ := transport["type"].(string)
switch tt {
case "http":
httpOpts := make(map[string]interface{})
if path, ok := transport["path"].([]interface{}); ok {
httpOpts["path"] = path[0]
} else if path, ok := transport["path"].(string); ok {
httpOpts["path"] = path
}
if host, ok := transport["host"].([]interface{}); ok {
httpOpts["host"] = host[0]
}
if isTls {
proxy["network"] = "h2"
proxy["h2-opts"] = httpOpts
} else {
proxy["network"] = "http"
proxy["http-opts"] = map[string]interface{}{"path": []interface{}{httpOpts["path"]}, "host": httpOpts["host"]}
}
case "ws", "httpupgrade":
proxy["network"] = "ws"
wsOpts := make(map[string]interface{})
if path, ok := transport["path"].(string); ok {
wsOpts["path"] = path
}
if headers, ok := transport["headers"].([]interface{}); ok {
wsOpts["headers"] = headers
}
if ed, ok := transport["early_data_header_name"].(string); ok {
wsOpts["early-data-header-name"] = ed
}
if tt == "httpupgrade" {
wsOpts["v2ray-http-upgrade"] = true
}
proxy["ws-opts"] = wsOpts
case "grpc":
proxy["network"] = "grpc"
grpcOpts := make(map[string]interface{})
if service_name, ok := transport["service_name"].(string); ok {
grpcOpts["grpc-service-name"] = service_name
}
proxy["grpc-opts"] = grpcOpts
}
}
// Multiplex
if mux, ok := obMap["multiplex"].(map[string]interface{}); ok {
if enabled, ok := mux["enabled"].(bool); ok && enabled {
smux := make(map[string]interface{})
smux["enabled"] = true
if protocol, ok := mux["protocol"].(string); ok {
smux["protocol"] = protocol
}
if _, ok := mux["max_connections"].(float64); ok {
smux["max-connections"] = mux["max_connections"]
}
if _, ok := mux["min_streams"].(float64); ok {
smux["min-streams"] = mux["min_streams"]
}
if _, ok := mux["max_streams"].(float64); ok {
smux["max-streams"] = mux["max_streams"]
}
if _, ok := mux["padding"].(bool); ok {
smux["padding"] = mux["padding"]
}
if brutal, ok := mux["brutal"].(map[string]interface{}); ok {
if enabled, ok := brutal["enabled"].(bool); ok && enabled {
brutalOpts := make(map[string]interface{})
brutalOpts["enabled"] = true
if _, ok := brutal["up_mbps"].(float64); ok {
brutalOpts["up"] = brutal["up_mbps"]
}
if _, ok := brutal["down_mbps"].(float64); ok {
brutalOpts["down"] = brutal["down_mbps"]
}
smux["brutal-opts"] = brutalOpts
}
}
proxy["smux"] = smux
}
}
proxies = append(proxies, proxy)
proxyTags = append(proxyTags, obMap["tag"].(string))
}
var proxyGroups []map[string]interface{}
err := yaml.Unmarshal([]byte(ProxyGroups), &proxyGroups)
if err != nil {
logger.Error(err.Error())
}
proxyGroups[1]["proxies"] = proxyTags
proxyGroups[0]["proxies"] = append([]string{proxyGroups[1]["name"].(string)}, proxyTags...)
output := map[string]interface{}{
"proxies": proxies,
"proxy-groups": proxyGroups,
}
return yaml.Marshal(output)
}
+18 -8
View File
@@ -46,17 +46,17 @@ type JsonService struct {
LinkService LinkService
} }
func (j *JsonService) GetJson(subId string, format string) (*string, error) { func (j *JsonService) GetJson(subId string, format string) (*string, []string, error) {
var jsonConfig map[string]interface{} var jsonConfig map[string]interface{}
client, inDatas, err := j.getData(subId) client, inDatas, err := j.getData(subId)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
outbounds, outTags, err := j.getOutbounds(client.Config, inDatas) outbounds, outTags, err := j.getOutbounds(client.Config, inDatas)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
links := j.LinkService.GetLinks(&client.Links, "external", "") links := j.LinkService.GetLinks(&client.Links, "external", "")
@@ -72,7 +72,7 @@ func (j *JsonService) GetJson(subId string, format string) (*string, error) {
err = json.Unmarshal([]byte(defaultJson), &jsonConfig) err = json.Unmarshal([]byte(defaultJson), &jsonConfig)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
jsonConfig["outbounds"] = outbounds jsonConfig["outbounds"] = outbounds
@@ -82,7 +82,11 @@ func (j *JsonService) GetJson(subId string, format string) (*string, error) {
result, _ := json.MarshalIndent(jsonConfig, "", " ") result, _ := json.MarshalIndent(jsonConfig, "", " ")
resultStr := string(result) resultStr := string(result)
return &resultStr, nil
updateInterval, _ := j.SettingService.GetSubUpdates()
headers := util.GetHeaders(client, updateInterval)
return &resultStr, headers, nil
} }
func (j *JsonService) getData(subId string) (*model.Client, []*model.Inbound, error) { func (j *JsonService) getData(subId string) (*model.Client, []*model.Inbound, error) {
@@ -211,7 +215,7 @@ func (j *JsonService) addDefaultOutbounds(outbounds *[]map[string]interface{}, o
} }
func (j *JsonService) addOthers(jsonConfig *map[string]interface{}) error { func (j *JsonService) addOthers(jsonConfig *map[string]interface{}) error {
rules := []interface{}{ rules_start := []interface{}{
map[string]interface{}{ map[string]interface{}{
"action": "sniff", "action": "sniff",
}, },
@@ -220,6 +224,8 @@ func (j *JsonService) addOthers(jsonConfig *map[string]interface{}) error {
"action": "route", "action": "route",
"outbound": "direct", "outbound": "direct",
}, },
}
rules_end := []interface{}{
map[string]interface{}{ map[string]interface{}{
"clash_mode": "Global", "clash_mode": "Global",
"action": "route", "action": "route",
@@ -229,7 +235,7 @@ func (j *JsonService) addOthers(jsonConfig *map[string]interface{}) error {
route := map[string]interface{}{ route := map[string]interface{}{
"auto_detect_interface": true, "auto_detect_interface": true,
"final": "proxy", "final": "proxy",
"rules": rules, "rules": rules_start,
} }
othersStr, err := j.SettingService.GetSubJsonExt() othersStr, err := j.SettingService.GetSubJsonExt()
@@ -261,7 +267,11 @@ func (j *JsonService) addOthers(jsonConfig *map[string]interface{}) error {
route["rule_set"] = othersJson["rule_set"] route["rule_set"] = othersJson["rule_set"]
} }
if settingRules, ok := othersJson["rules"].([]interface{}); ok { if settingRules, ok := othersJson["rules"].([]interface{}); ok {
route["rules"] = append(rules, settingRules...) rules := append(rules_start, settingRules...)
route["rules"] = append(rules, rules_end...)
}
if defaultDomainResolver, ok := othersJson["default_domain_resolver"].(string); ok {
route["default_domain_resolver"] = defaultDomainResolver
} }
(*jsonConfig)["route"] = route (*jsonConfig)["route"] = route
+15 -8
View File
@@ -11,6 +11,7 @@ type SubHandler struct {
service.SettingService service.SettingService
SubService SubService
JsonService JsonService
ClashService
} }
func NewSubHandler(g *gin.RouterGroup) { func NewSubHandler(g *gin.RouterGroup) {
@@ -23,23 +24,31 @@ func (s *SubHandler) initRouter(g *gin.RouterGroup) {
} }
func (s *SubHandler) subs(c *gin.Context) { func (s *SubHandler) subs(c *gin.Context) {
var headers []string
var result *string
var err error
subId := c.Param("subid") subId := c.Param("subid")
format, isFormat := c.GetQuery("format") format, isFormat := c.GetQuery("format")
if isFormat { if isFormat {
result, err := s.JsonService.GetJson(subId, format) switch format {
case "json":
result, headers, err = s.JsonService.GetJson(subId, format)
case "clash":
result, headers, err = s.ClashService.GetClash(subId)
}
if err != nil || result == nil { if err != nil || result == nil {
logger.Error(err) logger.Error(err)
c.String(400, "Error!") c.String(400, "Error!")
} else { return
c.String(200, *result)
} }
} else { } else {
result, headers, err := s.SubService.GetSubs(subId) result, headers, err = s.SubService.GetSubs(subId)
if err != nil || result == nil { if err != nil || result == nil {
logger.Error(err) logger.Error(err)
c.String(400, "Error!") c.String(400, "Error!")
} else { return
}
}
// Add headers // Add headers
c.Writer.Header().Set("Subscription-Userinfo", headers[0]) c.Writer.Header().Set("Subscription-Userinfo", headers[0])
c.Writer.Header().Set("Profile-Update-Interval", headers[1]) c.Writer.Header().Set("Profile-Update-Interval", headers[1])
@@ -47,5 +56,3 @@ func (s *SubHandler) subs(c *gin.Context) {
c.String(200, *result) c.String(200, *result)
} }
}
}
+2 -4
View File
@@ -6,6 +6,7 @@ import (
"s-ui/database" "s-ui/database"
"s-ui/database/model" "s-ui/database/model"
"s-ui/service" "s-ui/service"
"s-ui/util"
"strings" "strings"
"time" "time"
) )
@@ -34,11 +35,8 @@ func (s *SubService) GetSubs(subId string) (*string, []string, error) {
linksArray := s.LinkService.GetLinks(&client.Links, "all", clientInfo) linksArray := s.LinkService.GetLinks(&client.Links, "all", clientInfo)
result := strings.Join(linksArray, "\n") result := strings.Join(linksArray, "\n")
var headers []string
updateInterval, _ := s.SettingService.GetSubUpdates() updateInterval, _ := s.SettingService.GetSubUpdates()
headers = append(headers, fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", client.Up, client.Down, client.Volume, client.Expiry)) headers := util.GetHeaders(client, updateInterval)
headers = append(headers, fmt.Sprintf("%d", updateInterval))
headers = append(headers, subId)
subEncode, _ := s.SettingService.GetSubEncode() subEncode, _ := s.SettingService.GetSubEncode()
if subEncode { if subEncode {
+44
View File
@@ -0,0 +1,44 @@
package common
func UnionUintArray(a []uint, b []uint) []uint {
m := make(map[uint]bool)
for _, v := range a {
m[v] = true
}
for _, v := range b {
m[v] = true
}
var res []uint
for k := range m {
res = append(res, k)
}
return res
}
// Find different elements in two slices
// Returns elements in 'a' that are not in 'b' and elements in 'b' that are not in 'a'
func DiffUintArray(a []uint, b []uint) []uint {
different := []uint{}
set := make(map[uint]bool)
for _, item := range a {
set[item] = true
}
for _, item := range b {
if !set[item] {
different = append(different, item)
}
}
set = make(map[uint]bool)
for _, item := range b {
set[item] = true
}
for _, item := range a {
if !set[item] {
different = append(different, item)
}
}
return different
}
+37 -1
View File
@@ -10,7 +10,7 @@ import (
"strings" "strings"
) )
var InboundTypeWithLink = []string{"shadowsocks", "naive", "hysteria", "hysteria2", "tuic", "vless", "trojan", "vmess"} var InboundTypeWithLink = []string{"shadowsocks", "naive", "hysteria", "hysteria2", "anytls", "tuic", "vless", "trojan", "vmess"}
func LinkGenerator(clientConfig json.RawMessage, i *model.Inbound, hostname string) []string { func LinkGenerator(clientConfig json.RawMessage, i *model.Inbound, hostname string) []string {
inbound, err := i.MarshalFull() inbound, err := i.MarshalFull()
@@ -73,6 +73,8 @@ func LinkGenerator(clientConfig json.RawMessage, i *model.Inbound, hostname stri
return tuicLink(userConfig["tuic"], *inbound, Addrs) return tuicLink(userConfig["tuic"], *inbound, Addrs)
case "vless": case "vless":
return vlessLink(userConfig["vless"], *inbound, Addrs) return vlessLink(userConfig["vless"], *inbound, Addrs)
case "anytls":
return anytlsLink(userConfig["anytls"], Addrs)
case "trojan": case "trojan":
return trojanLink(userConfig["trojan"], *inbound, Addrs) return trojanLink(userConfig["trojan"], *inbound, Addrs)
case "vmess": case "vmess":
@@ -280,6 +282,40 @@ func hysteria2Link(
return links return links
} }
func anytlsLink(
userConfig map[string]interface{},
addrs []map[string]interface{}) []string {
password, _ := userConfig["password"].(string)
baseUri := fmt.Sprintf("%s%s@", "anytls://", password)
var links []string
for _, addr := range addrs {
params := map[string]string{}
if tls, ok := addr["tls"].(map[string]interface{}); ok {
if sni, ok := tls["server_name"].(string); ok {
params["sni"] = sni
}
if alpn, ok := tls["alpn"].([]interface{}); ok {
alpnList := make([]string, len(alpn))
for i, v := range alpn {
alpnList[i] = v.(string)
}
params["alpn"] = strings.Join(alpnList, ",")
}
if insecure, ok := tls["insecure"].(bool); ok && insecure {
params["insecure"] = "1"
}
}
port, _ := addr["server_port"].(float64)
uri := fmt.Sprintf("%s%s:%d", baseUri, addr["server"].(string), uint(port))
links = append(links, addParams(uri, params, addr["remark"].(string)))
}
return links
}
func tuicLink( func tuicLink(
userConfig map[string]interface{}, userConfig map[string]interface{},
inbound map[string]interface{}, inbound map[string]interface{},
+38
View File
@@ -24,6 +24,8 @@ func GetOutbound(uri string, i int) (*map[string]interface{}, string, error) {
return hy(u, i) return hy(u, i)
case "hy2", "hysteria2": case "hy2", "hysteria2":
return hy2(u, i) return hy2(u, i)
case "anytls":
return anytls(u, i)
case "tuic": case "tuic":
return tuic(u, i) return tuic(u, i)
case "ss", "shadowsocks": case "ss", "shadowsocks":
@@ -293,6 +295,42 @@ func hy2(u *url.URL, i int) (*map[string]interface{}, string, error) {
return &hy2, tag, nil return &hy2, tag, nil
} }
func anytls(u *url.URL, i int) (*map[string]interface{}, string, error) {
query, _ := url.ParseQuery(u.RawQuery)
host, portStr, _ := net.SplitHostPort(u.Host)
port := 443
if len(portStr) > 0 {
port, _ = strconv.Atoi(portStr)
}
tls := map[string]interface{}{
"enabled": true,
"server_name": query.Get("sni"),
}
alpn := query.Get("alpn")
insecure := query.Get("insecure")
if len(alpn) > 0 {
tls["alpn"] = strings.Split(alpn, ",")
}
if insecure == "1" || insecure == "true" {
tls["insecure"] = true
}
tag := u.Fragment
if i > 0 {
tag = fmt.Sprintf("%d.%s", i, u.Fragment)
}
anytls := map[string]interface{}{
"type": "anytls",
"tag": tag,
"server": host,
"server_port": port,
"password": u.User.Username(),
"tls": tls,
}
return &anytls, tag, nil
}
func tuic(u *url.URL, i int) (*map[string]interface{}, string, error) { func tuic(u *url.URL, i int) (*map[string]interface{}, string, error) {
query, _ := url.ParseQuery(u.RawQuery) query, _ := url.ParseQuery(u.RawQuery)
host, portStr, _ := net.SplitHostPort(u.Host) host, portStr, _ := net.SplitHostPort(u.Host)
+1
View File
@@ -209,6 +209,7 @@ func trojanOut(out *map[string]interface{}, inbound map[string]interface{}) {
} }
func vmessOut(out *map[string]interface{}, inbound map[string]interface{}) { func vmessOut(out *map[string]interface{}, inbound map[string]interface{}) {
(*out)["alter_id"] = 0
delete(*out, "transport") delete(*out, "transport")
if transport, ok := inbound["transport"]; ok { if transport, ok := inbound["transport"]; ok {
(*out)["transport"] = transport (*out)["transport"] = transport
+14
View File
@@ -0,0 +1,14 @@
package util
import (
"fmt"
"s-ui/database/model"
)
func GetHeaders(client *model.Client, updateInterval int) []string {
var headers []string
headers = append(headers, fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", client.Up, client.Down, client.Volume, client.Expiry))
headers = append(headers, fmt.Sprintf("%d", updateInterval))
headers = append(headers, client.Name)
return headers
}