Compare commits

..

25 Commits

Author SHA1 Message Date
Alireza Ahmadi 9d1ad833f9 v1.3.6 2025-09-13 13:20:41 +02:00
Alireza Ahmadi e6f7354ce7 sing-box v1.12.8 2025-09-13 13:19:58 +02:00
Alireza Ahmadi a2c3033f5a fix hy speed on links #801 2025-09-13 13:15:09 +02:00
Alireza Ahmadi 03cda07c9d fix queryEscape parts in links #806 2025-09-13 12:52:33 +02:00
Alireza Ahmadi fb999b4ee8 v1.3.5 2025-09-12 00:42:39 +02:00
Alireza Ahmadi e3ebfcf721 bump packages 2025-09-12 00:39:58 +02:00
Alireza Ahmadi 33071deb53 Merge pull request #782 from alireza0/dependabot/github_actions/actions/setup-go-6
Bump actions/setup-go from 5 to 6
2025-09-12 00:37:45 +02:00
Alireza Ahmadi 9b3b8d4540 Merge pull request #783 from alireza0/dependabot/github_actions/actions/setup-node-5
Bump actions/setup-node from 4 to 5
2025-09-12 00:37:32 +02:00
Alireza Ahmadi 98bf124078 sing-box v1.12.5 2025-09-12 00:24:13 +02:00
Alireza Ahmadi abc73a6525 fix external link on tls change 2025-09-12 00:19:51 +02:00
Alireza Ahmadi 2276175354 reality support links #794 2025-09-12 00:19:30 +02:00
Alireza Ahmadi 7f24735677 fix hysteria link generator #801 2025-09-11 23:23:58 +02:00
Alireza Ahmadi 4d1544864d go 1.25.1 2025-09-11 21:10:56 +02:00
Alireza Ahmadi 4aadee7ca0 fix ipv6 hostname #789 2025-09-09 22:30:07 +02:00
Alireza Ahmadi 6aba1354d5 fix client api error handling #781 2025-09-04 19:56:56 +02:00
dependabot[bot] 63b229143d Bump actions/setup-node from 4 to 5
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 5.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-04 16:10:29 +00:00
dependabot[bot] f861950c50 Bump actions/setup-go from 5 to 6
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 6.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-04 16:10:26 +00:00
Alireza Ahmadi 97d1694bfa fix numbered external links #774 2025-09-04 00:47:58 +02:00
Alireza Ahmadi 2d28d9b409 v1.3.4 2025-09-01 01:06:51 +02:00
Alireza Ahmadi 560c41acbe disable traffic charts if set to zero 2025-08-30 02:11:43 +02:00
Alireza Ahmadi e3c33bf649 sing-box v1.12.4 2025-08-30 01:20:38 +02:00
Alireza Ahmadi 216db63551 fix reality link to clash sub #766 2025-08-29 23:28:00 +02:00
Alireza Ahmadi 05d8a6bf85 Merge pull request #757 from Peter1303/main
update: docker run command
2025-08-26 20:22:45 +02:00
PeterPan 1e6c2b9598 update: docker run command 2025-08-25 11:35:00 +08:00
Alireza Ahmadi f006323f54 remove OS version dependency 2025-08-21 13:39:33 +02:00
21 changed files with 163 additions and 273 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
with:
submodules: recursive
- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: 20
- name: Install dependencies and build frontend
+2 -2
View File
@@ -37,13 +37,13 @@ jobs:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
cache: false
go-version-file: go.mod
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
+4 -4
View File
@@ -26,13 +26,13 @@ jobs:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
cache: false
go-version-file: go.mod
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
@@ -110,13 +110,13 @@ jobs:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
cache: false
go-version-file: go.mod
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
+1 -16
View File
@@ -134,7 +134,7 @@ docker compose up -d
mkdir s-ui && cd s-ui
docker run -itd \
-p 2095:2095 -p 2096:2096 -p 443:443 -p 80:80 \
-v $PWD/db/:/usr/local/s-ui/db/ \
-v $PWD/db/:/app/db/ \
-v $PWD/cert/:/root/cert/ \
--name s-ui --restart=unless-stopped \
alireza7/s-ui:latest
@@ -217,21 +217,6 @@ To run backend (from root folder of repository):
- HTTPS for secure access to the web panel and subscription service (self-provided domain + SSL certificate)
- Dark/Light theme
## Recommended OS
- Ubuntu 22.04+
- Debian 12+
- CentOS 9+
- Fedora 36+
- Arch Linux
- Parch Linux
- Manjaro
- Armbian
- AlmaLinux 9.5+
- Rocky Linux 9.5+
- Oracle Linux 8+
- OpenSUSE Tubleweed
## Environment Variables
<details>
+6 -2
View File
@@ -7,7 +7,6 @@ import (
"s-ui/service"
"s-ui/util"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
@@ -86,7 +85,11 @@ func (a *ApiService) getData(c *gin.Context) (interface{}, error) {
if err != nil {
return "", err
}
subURI, err := a.SettingService.GetFinalSubURI(strings.Split(c.Request.Host, ":")[0])
subURI, err := a.SettingService.GetFinalSubURI(getHostname(c))
if err != nil {
return "", err
}
trafficAge, err := a.SettingService.GetTrafficAge()
if err != nil {
return "", err
}
@@ -98,6 +101,7 @@ func (a *ApiService) getData(c *gin.Context) (interface{}, error) {
data["endpoints"] = endpoints
data["services"] = services
data["subURI"] = subURI
data["enableTraffic"] = trafficAge > 0
data["onlines"] = onlines
} else {
data["onlines"] = onlines
+4 -1
View File
@@ -29,8 +29,11 @@ func getRemoteIp(c *gin.Context) string {
func getHostname(c *gin.Context) string {
host := c.Request.Host
if colonIndex := strings.LastIndex(host, ":"); colonIndex != -1 {
if strings.Contains(host, ":") {
host, _, _ = net.SplitHostPort(c.Request.Host)
if strings.Contains(host, ":") {
host = "[" + host + "]"
}
}
return host
}
+1 -1
View File
@@ -1 +1 @@
1.3.3
1.3.6
+4 -2
View File
@@ -20,11 +20,13 @@ func (c *CronJob) Start(loc *time.Location, trafficAge int) error {
go func() {
// Start stats job
c.cron.AddJob("@every 10s", NewStatsJob())
c.cron.AddJob("@every 10s", NewStatsJob(trafficAge > 0))
// Start expiry job
c.cron.AddJob("@every 1m", NewDepleteJob())
// Start deleting old stats
c.cron.AddJob("@daily", NewDelStatsJob(trafficAge))
if trafficAge > 0 {
c.cron.AddJob("@daily", NewDelStatsJob(trafficAge))
}
// Start core if it is not running
c.cron.AddJob("@every 5s", NewCheckCoreJob())
}()
+6 -3
View File
@@ -7,14 +7,17 @@ import (
type StatsJob struct {
service.StatsService
enableTraffic bool
}
func NewStatsJob() *StatsJob {
return &StatsJob{}
func NewStatsJob(saveTraffic bool) *StatsJob {
return &StatsJob{
enableTraffic: saveTraffic,
}
}
func (s *StatsJob) Run() {
err := s.StatsService.SaveStats()
err := s.StatsService.SaveStats(s.enableTraffic)
if err != nil {
logger.Warning("Get stats failed: ", err)
return
+9 -9
View File
@@ -1,6 +1,6 @@
module s-ui
go 1.25.0
go 1.25.1
require (
github.com/gin-contrib/gzip v1.2.3
@@ -9,14 +9,14 @@ require (
github.com/gofrs/uuid/v5 v5.3.2
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/robfig/cron/v3 v3.0.1
github.com/sagernet/sing v0.7.6-0.20250815070458-d33ece7a184f
github.com/sagernet/sing-box v1.12.3
github.com/sagernet/sing v0.7.10
github.com/sagernet/sing-box v1.12.8
github.com/sagernet/sing-dns v0.4.6
github.com/shirou/gopsutil/v4 v4.25.7
github.com/shirou/gopsutil/v4 v4.25.8
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/sqlite v1.6.0
gorm.io/gorm v1.30.1
gorm.io/gorm v1.31.0
)
require (
@@ -107,14 +107,14 @@ require (
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
github.com/sagernet/quic-go v0.52.0-beta.1 // indirect
github.com/sagernet/sing-mux v0.3.3 // indirect
github.com/sagernet/sing-quic v0.5.0 // indirect
github.com/sagernet/sing-quic v0.5.2-0.20250909083218-00a55617c0fb // indirect
github.com/sagernet/sing-shadowsocks v0.2.8 // 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-tun v0.7.0-beta.1 // indirect
github.com/sagernet/sing-tun v0.7.2 // indirect
github.com/sagernet/sing-vmess v0.2.7 // 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-sing-box-1.12-mod.1 // indirect
github.com/sagernet/wireguard-go v0.0.1-beta.7 // indirect
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect
@@ -139,7 +139,7 @@ require (
go.uber.org/zap/exp v0.3.0 // indirect
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/arch v0.19.0 // indirect
golang.org/x/arch v0.20.0 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
golang.org/x/mod v0.27.0 // indirect
+18 -18
View File
@@ -214,36 +214,36 @@ 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/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing v0.7.6-0.20250815070458-d33ece7a184f h1:HIBo8l+tsS3wLwuI1E56uRTQw46QytXSUpZTP3vwG/U=
github.com/sagernet/sing v0.7.6-0.20250815070458-d33ece7a184f/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-box v1.12.3 h1:EWtKXSJ0L8OVHS4Swl8HsakMOwwiEC68AU+pEkcUlQw=
github.com/sagernet/sing-box v1.12.3/go.mod h1:spyNZpDcPQIl5VUL/6rnDF7x/SFtQ0u0dybT5hIuU/c=
github.com/sagernet/sing v0.7.10 h1:2yPhZFx+EkyHPH8hXNezgyRSHyGY12CboId7CtwLROw=
github.com/sagernet/sing v0.7.10/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-box v1.12.8 h1:XnDRmD5tT5PsBPvMQ6zmLtbAKD3/l/6mHUfCJYa+L2g=
github.com/sagernet/sing-box v1.12.8/go.mod h1:HRB+cgvwOMnoNmVhJm2DoFQPbJvDSRDaLonaMlT4tjo=
github.com/sagernet/sing-dns v0.4.6 h1:mjZC0o6d5sQ1sraoOBbK3G3apCbuL8wWYwu2RNu5rbM=
github.com/sagernet/sing-dns v0.4.6/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8=
github.com/sagernet/sing-mux v0.3.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw=
github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
github.com/sagernet/sing-quic v0.5.0 h1:jNLIyVk24lFPvu8A4x+ZNEnZdI+Tg1rp7eCJ6v0Csak=
github.com/sagernet/sing-quic v0.5.0/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0=
github.com/sagernet/sing-quic v0.5.2-0.20250909083218-00a55617c0fb h1:5Wx3XeTiKrrrcrAky7Hc1bO3CGxrvho2Vu5b/adlEIM=
github.com/sagernet/sing-quic v0.5.2-0.20250909083218-00a55617c0fb/go.mod h1:evP1e++ZG8TJHVV5HudXV4vWeYzGfCdF4HwSJZcdqkI=
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
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/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/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
github.com/sagernet/sing-tun v0.7.0-beta.1 h1:mBIFXYAnGO5ey/HcCYanqnBx61E7yF8zTFGRZonGYmY=
github.com/sagernet/sing-tun v0.7.0-beta.1/go.mod h1:AHJuRrLbNRJuivuFZ2VhXwDj4ViYp14szG5EkkKAqRQ=
github.com/sagernet/sing-tun v0.7.2 h1:uJkAZM0KBqIYzrq077QGqdvj/+4i/pMOx6Pnx0jYqAs=
github.com/sagernet/sing-tun v0.7.2/go.mod h1:pUEjh9YHQ2gJT6Lk0TYDklh3WJy7lz+848vleGM3JPM=
github.com/sagernet/sing-vmess v0.2.7 h1:2ee+9kO0xW5P4mfe6TYVWf9VtY8k1JhNysBqsiYj0sk=
github.com/sagernet/sing-vmess v0.2.7/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/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/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI=
github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.1 h1:gMC0q+0VvZBotZMZ9G0R8ZMEIT/Q6KnXbw0/OgMjmdk=
github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.1/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI=
github.com/sagernet/wireguard-go v0.0.1-beta.7 h1:ltgBwYHfr+9Wz1eG59NiWnHrYEkDKHG7otNZvu85DXI=
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/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
github.com/shirou/gopsutil/v4 v4.25.7 h1:bNb2JuqKuAu3tRlPv5piSmBZyMfecwQ+t/ILq+1JqVM=
github.com/shirou/gopsutil/v4 v4.25.7/go.mod h1:XV/egmwJtd3ZQjBpJVY5kndsiOO4IRqy9TQnmm6VP7U=
github.com/shirou/gopsutil/v4 v4.25.8 h1:NnAsw9lN7587WHxjJA9ryDnqhJpFH6A+wagYWTOH970=
github.com/shirou/gopsutil/v4 v4.25.8/go.mod h1:q9QdMmfAOVIw7a+eF86P7ISEU6ka+NLgkUxlopV4RwI=
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.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -255,8 +255,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ=
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4=
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
@@ -322,8 +322,8 @@ 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/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/arch v0.19.0 h1:LmbDQUodHThXE+htjrnmVD73M//D9GTH6wFZjyDkjyU=
golang.org/x/arch v0.19.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
golang.org/x/arch v0.20.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.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
@@ -383,8 +383,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
gorm.io/gorm v1.30.1 h1:lSHg33jJTBxs2mgJRfRZeLDG+WZaHYCk3Wtfl6Ngzo4=
gorm.io/gorm v1.30.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
gorm.io/gorm v1.31.0 h1:0VlycGreVhK7RF/Bwt51Fk8v0xLiiiFdbGDPIZQ7mJY=
gorm.io/gorm v1.31.0/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=
lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
-60
View File
@@ -38,66 +38,6 @@ arch() {
echo "arch: $(arch)"
os_version=""
os_version=$(grep -i version_id /etc/os-release | cut -d \" -f2 | cut -d . -f1)
if [[ "${release}" == "arch" ]]; then
echo "Your OS is Arch Linux"
elif [[ "${release}" == "parch" ]]; then
echo "Your OS is Parch linux"
elif [[ "${release}" == "manjaro" ]]; then
echo "Your OS is Manjaro"
elif [[ "${release}" == "armbian" ]]; then
echo "Your OS is Armbian"
elif [[ "${release}" == "opensuse-tumbleweed" ]]; then
echo "Your OS is OpenSUSE Tumbleweed"
elif [[ "${release}" == "centos" ]]; then
if [[ ${os_version} -lt 9 ]]; then
echo -e "${red} Please use CentOS 9 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "ubuntu" ]]; then
if [[ ${os_version} -lt 22 ]]; then
echo -e "${red} Please use Ubuntu 22 or higher version!${plain}\n" && exit 1
fi
elif [[ "${release}" == "fedora" ]]; then
if [[ ${os_version} -lt 36 ]]; then
echo -e "${red} Please use Fedora 36 or higher version!${plain}\n" && exit 1
fi
elif [[ "${release}" == "debian" ]]; then
if [[ ${os_version} -lt 12 ]]; then
echo -e "${red} Please use Debian 12 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "almalinux" ]]; then
if [[ ${os_version} -lt 95 ]]; then
echo -e "${red} Please use AlmaLinux 9.5 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "rocky" ]]; then
if [[ ${os_version} -lt 95 ]]; then
echo -e "${red} Please use Rocky Linux 9.5 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "ol" ]]; then
if [[ ${os_version} -lt 8 ]]; then
echo -e "${red} Please use Oracle Linux 8 or higher ${plain}\n" && exit 1
fi
else
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 "- Ubuntu 22.04+"
echo "- Debian 12+"
echo "- CentOS 9+"
echo "- Fedora 36+"
echo "- Arch Linux"
echo "- Parch Linux"
echo "- Manjaro"
echo "- Armbian"
echo "- AlmaLinux 9.5+"
echo "- Rocky Linux 9.5+"
echo "- Oracle Linux 8+"
echo "- OpenSUSE Tumbleweed"
exit 1
fi
install_base() {
case "${release}" in
centos | almalinux | rocky | oracle)
+1 -1
View File
@@ -283,7 +283,7 @@ func (s *ClientService) UpdateLinksByInboundChange(tx *gorm.DB, inbounds *[]mode
})
}
for _, clientLink := range clientLinks {
if clientLink["remark"] != inbound.Tag && clientLink["remark"] != oldTag {
if clientLink["type"] != "local" || (clientLink["remark"] != inbound.Tag && clientLink["remark"] != oldTag) {
newClientLinks = append(newClientLinks, clientLink)
}
}
+2 -1
View File
@@ -142,7 +142,8 @@ func (s *ConfigService) Save(obj string, act string, data json.RawMessage, initU
switch obj {
case "clients":
inboundIds, err := s.ClientService.Save(tx, act, data, hostname)
var inboundIds []uint
inboundIds, err = s.ClientService.Save(tx, act, data, hostname)
if err == nil && len(inboundIds) > 0 {
objs = append(objs, "inbounds")
err = s.InboundService.RestartInbounds(tx, inboundIds)
+7
View File
@@ -391,6 +391,13 @@ func (s *SettingService) Save(tx *gorm.DB, data json.RawMessage) error {
}
}
// Delete all stats if it is set to 0
if key == "trafficAge" && obj == "0" {
err = tx.Where("id > 0").Delete(model.Stats{}).Error
if err != nil {
return err
}
}
err = tx.Model(model.Setting{}).Where("key = ?", key).Update("value", obj).Error
if err != nil {
return err
+5 -3
View File
@@ -19,7 +19,7 @@ var onlineResources = &onlines{}
type StatsService struct {
}
func (s *StatsService) SaveStats() error {
func (s *StatsService) SaveStats(enableTraffic bool) error {
if !corePtr.IsRunning() {
return nil
}
@@ -70,8 +70,10 @@ func (s *StatsService) SaveStats() error {
}
}
err = tx.Create(&stats).Error
return err
if !enableTraffic {
return nil
}
return tx.Create(&stats).Error
}
func (s *StatsService) GetStats(resource string, tag string, limit int) ([]model.Stats, error) {
+5 -1
View File
@@ -73,8 +73,12 @@ func (s *ClashService) GetClash(subId string) (*string, []string, error) {
}
links := s.LinkService.GetLinks(&client.Links, "external", "")
tagNumEnable := 0
if len(links) > 1 {
tagNumEnable = 1
}
for index, link := range links {
json, tag, err := util.GetOutbound(link, index)
json, tag, err := util.GetOutbound(link, (index+1)*tagNumEnable)
if err == nil && len(tag) > 0 {
*outbounds = append(*outbounds, *json)
*outTags = append(*outTags, tag)
+5 -1
View File
@@ -61,8 +61,12 @@ func (j *JsonService) GetJson(subId string, format string) (*string, []string, e
}
links := j.LinkService.GetLinks(&client.Links, "external", "")
tagNumEnable := 0
if len(links) > 1 {
tagNumEnable = 1
}
for index, link := range links {
json, tag, err := util.GetOutbound(link, index)
json, tag, err := util.GetOutbound(link, (index+1)*tagNumEnable)
if err == nil && len(tag) > 0 {
*outbounds = append(*outbounds, *json)
*outTags = append(*outTags, tag)
+77 -142
View File
@@ -67,7 +67,9 @@ func LinkGenerator(clientConfig json.RawMessage, i *model.Inbound, hostname stri
return httpLink(userConfig["http"], *inbound, Addrs)
case "mixed":
return append(
socksLink(userConfig["socks"], *inbound, Addrs), httpLink(userConfig["http"], *inbound, Addrs)...)
socksLink(userConfig["socks"], *inbound, Addrs),
httpLink(userConfig["http"], *inbound, Addrs)...,
)
case "shadowsocks":
return shadowsocksLink(userConfig, *inbound, Addrs)
case "naive":
@@ -157,7 +159,7 @@ func shadowsocksLink(
var links []string
for _, addr := range addrs {
port, _ := addr["server_port"].(float64)
links = append(links, fmt.Sprintf("%s@%s:%d#%s", uriBase, addr["server"].(string), uint(port), addr["remark"].(string)))
links = append(links, fmt.Sprintf("%s@%s:%.0f#%s", uriBase, addr["server"].(string), port, addr["remark"].(string)))
}
return links
}
@@ -198,7 +200,7 @@ func naiveLink(
}
port, _ := addr["server_port"].(float64)
uri := baseUri + toBase64([]byte(fmt.Sprintf("%s:%s@%s:%d", username, password, addr["server"].(string), uint(port))))
uri := baseUri + toBase64([]byte(fmt.Sprintf("%s:%s@%s:%.0f", username, password, addr["server"].(string), port)))
links = append(links, addParams(uri, params, addr["remark"].(string)))
}
return links
@@ -214,29 +216,17 @@ func hysteriaLink(
for _, addr := range addrs {
params := map[string]string{}
if upmbps, ok := inbound["up_mbps"].(string); ok {
params["up_mbps"] = upmbps
if upmbps, ok := inbound["up_mbps"].(float64); ok {
params["downmbps"] = fmt.Sprintf("%.0f", upmbps)
}
if downmbps, ok := inbound["down_mbps"].(string); ok {
params["down_mbps"] = downmbps
if downmbps, ok := inbound["down_mbps"].(float64); ok {
params["upmbps"] = fmt.Sprintf("%.0f", downmbps)
}
if auth, ok := userConfig["auth_str"].(string); ok {
params["auth"] = auth
}
if tls, ok := addr["tls"].(map[string]interface{}); ok {
if sni, ok := tls["server_name"].(string); ok {
params["peer"] = 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"
}
getTlsParams(&params, tls, "insecure")
}
if obfs, ok := inbound["obfs"].(string); ok {
params["obfs"] = obfs
@@ -246,9 +236,18 @@ func hysteriaLink(
} else {
params["fastopen"] = "0"
}
var outJson map[string]interface{}
json.Unmarshal(inbound["out_json"].(json.RawMessage), &outJson)
if mport, ok := outJson["server_ports"].([]interface{}); ok {
mportList := make([]string, len(mport))
for i, v := range mport {
mportList[i] = v.(string)
}
params["mport"] = strings.Join(mportList, ",")
}
port, _ := addr["server_port"].(float64)
uri := fmt.Sprintf("%s%s:%d", baseUri, addr["server"].(string), uint(port))
uri := fmt.Sprintf("%s%s:%.0f", baseUri, addr["server"].(string), port)
links = append(links, addParams(uri, params, addr["remark"].(string)))
}
@@ -266,26 +265,14 @@ func hysteria2Link(
for _, addr := range addrs {
params := map[string]string{}
if upmbps, ok := inbound["up_mbps"].(string); ok {
params["up_mbps"] = upmbps
if upmbps, ok := inbound["up_mbps"].(float64); ok {
params["downmbps"] = fmt.Sprintf("%.0f", upmbps)
}
if downmbps, ok := inbound["down_mbps"].(string); ok {
params["down_mbps"] = downmbps
if downmbps, ok := inbound["down_mbps"].(float64); ok {
params["upmbps"] = fmt.Sprintf("%.0f", downmbps)
}
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"
}
getTlsParams(&params, tls, "insecure")
}
if obfs, ok := inbound["obfs"].(map[string]interface{}); ok {
if obfsType, ok := obfs["type"].(string); ok {
@@ -300,9 +287,18 @@ func hysteria2Link(
} else {
params["fastopen"] = "0"
}
var outJson map[string]interface{}
json.Unmarshal(inbound["out_json"].(json.RawMessage), &outJson)
if mport, ok := outJson["server_ports"].([]interface{}); ok {
mportList := make([]string, len(mport))
for i, v := range mport {
mportList[i] = v.(string)
}
params["mport"] = strings.Join(mportList, ",")
}
port, _ := addr["server_port"].(float64)
uri := fmt.Sprintf("%s%s:%d", baseUri, addr["server"].(string), uint(port))
uri := fmt.Sprintf("%s%s:%.0f", baseUri, addr["server"].(string), port)
links = append(links, addParams(uri, params, addr["remark"].(string)))
}
@@ -320,23 +316,11 @@ func anytlsLink(
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"
}
getTlsParams(&params, tls, "insecure")
}
port, _ := addr["server_port"].(float64)
uri := fmt.Sprintf("%s%s:%d", baseUri, addr["server"].(string), uint(port))
uri := fmt.Sprintf("%s%s:%.0f", baseUri, addr["server"].(string), port)
links = append(links, addParams(uri, params, addr["remark"].(string)))
}
@@ -356,29 +340,14 @@ func tuicLink(
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"
}
if disableSni, ok := tls["disable_sni"].(bool); ok && disableSni {
params["disable_sni"] = "1"
}
getTlsParams(&params, tls, "insecure")
}
if congestionControl, ok := inbound["congestion_control"].(string); ok {
params["congestion_control"] = congestionControl
}
port, _ := addr["server_port"].(float64)
uri := fmt.Sprintf("%s%s:%d", baseUri, addr["server"].(string), uint(port))
uri := fmt.Sprintf("%s%s:%.0f", baseUri, addr["server"].(string), port)
links = append(links, addParams(uri, params, addr["remark"].(string)))
}
@@ -397,39 +366,13 @@ func vlessLink(
for _, addr := range addrs {
params := baseParams
if tls, ok := addr["tls"].(map[string]interface{}); ok && tls["enabled"].(bool) {
if reality, ok := tls["reality"].(map[string]interface{}); ok && reality["enabled"].(bool) {
params["security"] = "reality"
if pbk, ok := reality["public_key"].(string); ok {
params["pbk"] = pbk
}
if sid, ok := reality["short_id"].(string); ok {
params["sid"] = sid
}
} else {
params["security"] = "tls"
if insecure, ok := tls["insecure"].(bool); ok && insecure {
params["allowInsecure"] = "1"
}
}
getTlsParams(&params, tls, "allowInsecure")
if flow, ok := userConfig["flow"].(string); ok {
params["flow"] = flow
}
if utls, ok := tls["utls"].(map[string]interface{}); ok {
params["fp"], _ = utls["fingerprint"].(string)
}
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, ",")
}
}
port, _ := addr["server_port"].(float64)
uri := fmt.Sprintf("vless://%s@%s:%d", uuid, addr["server"].(string), uint(port))
uri := fmt.Sprintf("vless://%s@%s:%.0f", uuid, addr["server"].(string), port)
uri = addParams(uri, params, addr["remark"].(string))
links = append(links, uri)
}
@@ -448,36 +391,10 @@ func trojanLink(
for _, addr := range addrs {
params := baseParams
if tls, ok := addr["tls"].(map[string]interface{}); ok && tls["enabled"].(bool) {
if reality, ok := tls["reality"].(map[string]interface{}); ok && reality["enabled"].(bool) {
params["security"] = "reality"
if pbk, ok := reality["public_key"].(string); ok {
params["pbk"] = pbk
}
if sid, ok := reality["short_id"].(string); ok {
params["sid"] = sid
}
} else {
params["security"] = "tls"
if insecure, ok := tls["insecure"].(bool); ok && insecure {
params["allowInsecure"] = "1"
}
}
if utls, ok := tls["utls"].(map[string]interface{}); ok {
params["fp"], _ = utls["fingerprint"].(string)
}
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, ",")
}
getTlsParams(&params, tls, "allowInsecure")
}
port, _ := addr["server_port"].(float64)
uri := fmt.Sprintf("trojan://%s@%s:%d", password, addr["server"].(string), uint(port))
uri := fmt.Sprintf("trojan://%s@%s:%.0f", password, addr["server"].(string), port)
uri = addParams(uri, params, addr["remark"].(string))
links = append(links, uri)
}
@@ -556,11 +473,16 @@ func toBase64(d []byte) string {
func addParams(uri string, params map[string]string, remark string) string {
URL, _ := url.Parse(uri)
q := URL.Query()
var q []string
for k, v := range params {
q.Add(k, v)
switch k {
case "mport", "alpn":
q = append(q, fmt.Sprintf("%s=%s", k, v))
default:
q = append(q, fmt.Sprintf("%s=%s", k, url.QueryEscape(v)))
}
}
URL.RawQuery = q.Encode()
URL.RawQuery = strings.Join(q, "&")
URL.Fragment = remark
return URL.String()
}
@@ -610,22 +532,35 @@ func getTransportParams(t interface{}) map[string]string {
return params
}
func getTlsParams(t interface{}) map[string]string {
params := map[string]string{}
if tls, hasTls := t.(map[string]interface{}); hasTls {
if sni, ok := tls["server_name"].(string); ok {
params["sni"] = sni
func getTlsParams(params *map[string]string, tls map[string]interface{}, insecureKey string) {
if reality, ok := tls["reality"].(map[string]interface{}); ok && reality["enabled"].(bool) {
(*params)["security"] = "reality"
if pbk, ok := reality["public_key"].(string); ok {
(*params)["pbk"] = pbk
}
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 sid, ok := reality["short_id"].(string); ok {
(*params)["sid"] = sid
}
} else {
(*params)["security"] = "tls"
if insecure, ok := tls["insecure"].(bool); ok && insecure {
params["insecure"] = "1"
(*params)[insecureKey] = "1"
}
if disableSni, ok := tls["disable_sni"].(bool); ok && disableSni {
(*params)["disable_sni"] = "1"
}
}
return params
if utls, ok := tls["utls"].(map[string]interface{}); ok {
(*params)["fp"], _ = utls["fingerprint"].(string)
}
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, ",")
}
}
+4 -4
View File
@@ -435,7 +435,7 @@ func ss(u *url.URL, i int) (*map[string]interface{}, string, error) {
return &ss, tag, nil
}
func getTransport(tp_type string, q *url.Values) *map[string]interface{} {
func getTransport(tp_type string, q *url.Values) map[string]interface{} {
transport := map[string]interface{}{}
tp_host := q.Get("host")
tp_path := q.Get("path")
@@ -472,10 +472,10 @@ func getTransport(tp_type string, q *url.Values) *map[string]interface{} {
transport["path"] = tp_path
transport["host"] = tp_host
}
return &transport
return transport
}
func getTls(security string, q *url.Values) *map[string]interface{} {
func getTls(security string, q *url.Values) map[string]interface{} {
tls := map[string]interface{}{}
tls_fp := q.Get("fp")
tls_sni := q.Get("sni")
@@ -516,5 +516,5 @@ func getTls(security string, q *url.Values) *map[string]interface{} {
},
}
}
return &tls
return tls
}