0.0.2
- 增加设置总流量功能,流量超出后自动禁用 - 优化部分 ui 细节 - 修复监听 ip 不为空导致无法启动 xray 的问题 - 修复二维码链接没有包含 address 的问题
This commit is contained in:
38
README.md
38
README.md
@@ -1,2 +1,38 @@
|
|||||||
# x-ui
|
# x-ui
|
||||||
a web panel based on xray-core
|
支持多协议多用户 xray 面板
|
||||||
|
|
||||||
|
# 功能介绍
|
||||||
|
- 系统状态监控
|
||||||
|
- 支持多用户多协议,网页可视化操作
|
||||||
|
- 支持的协议:vmess、vless、trojan、shadowsocks、dokodemo-door、socks、http
|
||||||
|
- 支持配置更多传输配置
|
||||||
|
- 账号流量统计
|
||||||
|
- 可自定义 xray 配置模板
|
||||||
|
- 支持 https 访问面板(自备域名 + ssl 证书)
|
||||||
|
- 更多高级配置项,详见面板
|
||||||
|
|
||||||
|
# 安装&升级
|
||||||
|
## 测试版
|
||||||
|
```
|
||||||
|
bash <(curl -Ls https://raw.githubusercontent.com/sprov065/x-ui/master/install.sh) 0.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## 建议系统
|
||||||
|
- CentOS 7+
|
||||||
|
- Ubuntu 16+
|
||||||
|
- Debian 8+
|
||||||
|
|
||||||
|
# 常见问题
|
||||||
|
## 与 v2-ui 关系
|
||||||
|
x-ui 相当于 v2-ui 的加强版,未来会加入更多功能,待 x-ui 功能稳定后,v2-ui 将不再提供更新
|
||||||
|
|
||||||
|
x-ui 可与 v2-ui 并存,数据不互通,不影响对方的运行
|
||||||
|
|
||||||
|
# Telegram
|
||||||
|
群组:https://t.me/sprov_blog
|
||||||
|
|
||||||
|
频道:https://t.me/sprov_channel
|
||||||
|
|
||||||
|
## Stargazers over time
|
||||||
|
|
||||||
|
[](https://starchart.cc/sprov065/x-ui)
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed version
|
||||||
|
var version string
|
||||||
|
|
||||||
|
//go:embed name
|
||||||
|
var name string
|
||||||
|
|
||||||
type LogLevel string
|
type LogLevel string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -15,11 +23,11 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func GetVersion() string {
|
func GetVersion() string {
|
||||||
return "0.0.1"
|
return strings.TrimSpace(version)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetName() string {
|
func GetName() string {
|
||||||
return "x-ui"
|
return strings.TrimSpace(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetLogLevel() LogLevel {
|
func GetLogLevel() LogLevel {
|
||||||
|
|||||||
1
config/name
Normal file
1
config/name
Normal file
@@ -0,0 +1 @@
|
|||||||
|
x-ui
|
||||||
1
config/version
Normal file
1
config/version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
0.0.2
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"x-ui/util/json_util"
|
"x-ui/util/json_util"
|
||||||
"x-ui/xray"
|
"x-ui/xray"
|
||||||
)
|
)
|
||||||
@@ -25,8 +26,9 @@ type User struct {
|
|||||||
type Inbound struct {
|
type Inbound struct {
|
||||||
Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"`
|
Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"`
|
||||||
UserId int `json:"-"`
|
UserId int `json:"-"`
|
||||||
Up int64 `json:"up"`
|
Up int64 `json:"up" form:"up"`
|
||||||
Down int64 `json:"down"`
|
Down int64 `json:"down" form:"down"`
|
||||||
|
Total int64 `json:"total" form:"total"`
|
||||||
Remark string `json:"remark" form:"remark"`
|
Remark string `json:"remark" form:"remark"`
|
||||||
Enable bool `json:"enable" form:"enable"`
|
Enable bool `json:"enable" form:"enable"`
|
||||||
ExpiryTime int64 `json:"expiryTime" form:"expiryTime"`
|
ExpiryTime int64 `json:"expiryTime" form:"expiryTime"`
|
||||||
@@ -42,8 +44,12 @@ type Inbound struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig {
|
func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig {
|
||||||
|
listen := i.Listen
|
||||||
|
if listen != "" {
|
||||||
|
listen = fmt.Sprintf("\"%v\"", listen)
|
||||||
|
}
|
||||||
return &xray.InboundConfig{
|
return &xray.InboundConfig{
|
||||||
Listen: json_util.RawMessage(i.Listen),
|
Listen: json_util.RawMessage(listen),
|
||||||
Port: i.Port,
|
Port: i.Port,
|
||||||
Protocol: string(i.Protocol),
|
Protocol: string(i.Protocol),
|
||||||
Settings: json_util.RawMessage(i.Settings),
|
Settings: json_util.RawMessage(i.Settings),
|
||||||
|
|||||||
7
main.go
7
main.go
@@ -18,9 +18,6 @@ import (
|
|||||||
"x-ui/web/service"
|
"x-ui/web/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
// this function call global.setWebServer
|
|
||||||
func setWebServer(server global.WebServer)
|
|
||||||
|
|
||||||
func runWebServer() {
|
func runWebServer() {
|
||||||
log.Printf("%v %v", config.GetName(), config.GetVersion())
|
log.Printf("%v %v", config.GetName(), config.GetVersion())
|
||||||
|
|
||||||
@@ -45,7 +42,7 @@ func runWebServer() {
|
|||||||
var server *web.Server
|
var server *web.Server
|
||||||
|
|
||||||
server = web.NewServer()
|
server = web.NewServer()
|
||||||
setWebServer(server)
|
global.SetWebServer(server)
|
||||||
err = server.Start()
|
err = server.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
@@ -60,7 +57,7 @@ func runWebServer() {
|
|||||||
if sig == syscall.SIGHUP {
|
if sig == syscall.SIGHUP {
|
||||||
server.Stop()
|
server.Stop()
|
||||||
server = web.NewServer()
|
server = web.NewServer()
|
||||||
setWebServer(server)
|
global.SetWebServer(server)
|
||||||
err = server.Start()
|
err = server.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class DBInbound {
|
|||||||
userId = 0;
|
userId = 0;
|
||||||
up = 0;
|
up = 0;
|
||||||
down = 0;
|
down = 0;
|
||||||
|
total = 0;
|
||||||
remark = "";
|
remark = "";
|
||||||
enable = true;
|
enable = true;
|
||||||
expiryTime = 0;
|
expiryTime = 0;
|
||||||
@@ -45,6 +46,14 @@ class DBInbound {
|
|||||||
ObjectUtil.cloneProps(this, data);
|
ObjectUtil.cloneProps(this, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get totalGB() {
|
||||||
|
return toFixed(this.total / ONE_GB, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
set totalGB(gb) {
|
||||||
|
this.total = toFixed(gb * ONE_GB, 0);
|
||||||
|
}
|
||||||
|
|
||||||
toInbound() {
|
toInbound() {
|
||||||
let settings = {};
|
let settings = {};
|
||||||
if (!ObjectUtil.isEmpty(this.settings)) {
|
if (!ObjectUtil.isEmpty(this.settings)) {
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ class ObjectUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ type WebServer interface {
|
|||||||
GetCtx() context.Context
|
GetCtx() context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname setWebServer main.setWebServer
|
func SetWebServer(s WebServer) {
|
||||||
func setWebServer(s WebServer) {
|
|
||||||
webServer = s
|
webServer = s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,18 @@
|
|||||||
<a-form-item label="端口">
|
<a-form-item label="端口">
|
||||||
<a-input type="number" v-model.number="inbound.port"></a-input>
|
<a-input type="number" v-model.number="inbound.port"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
总流量(GB)
|
||||||
|
<a-tooltip>
|
||||||
|
<template slot="title">
|
||||||
|
0 表示不限制
|
||||||
|
</template>
|
||||||
|
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-input-number v-model="dbInbound.totalGB" :min="0"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
<!-- vmess settings -->
|
<!-- vmess settings -->
|
||||||
|
|||||||
@@ -27,15 +27,15 @@
|
|||||||
<a-card hoverable style="margin-bottom: 20px;">
|
<a-card hoverable style="margin-bottom: 20px;">
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :xs="24" :sm="24" :lg="12">
|
<a-col :xs="24" :sm="24" :lg="12">
|
||||||
upload / download:
|
总上传 / 下载:
|
||||||
<a-tag color="green">[[ sizeFormat(total.up) ]] / [[ sizeFormat(total.down) ]]</a-tag>
|
<a-tag color="green">[[ sizeFormat(total.up) ]] / [[ sizeFormat(total.down) ]]</a-tag>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :xs="24" :sm="24" :lg="12">
|
<a-col :xs="24" :sm="24" :lg="12">
|
||||||
total traffic:
|
总用量:
|
||||||
<a-tag color="green">[[ sizeFormat(total.up + total.down) ]]</a-tag>
|
<a-tag color="green">[[ sizeFormat(total.up + total.down) ]]</a-tag>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :xs="24" :sm="24" :lg="12">
|
<a-col :xs="24" :sm="24" :lg="12">
|
||||||
number of accounts:
|
入站数量:
|
||||||
<a-tag color="green">[[ dbInbounds.length ]]</a-tag>
|
<a-tag color="green">[[ dbInbounds.length ]]</a-tag>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
@@ -59,6 +59,8 @@
|
|||||||
<template slot="traffic" slot-scope="text, dbInbound">
|
<template slot="traffic" slot-scope="text, dbInbound">
|
||||||
<a-tag color="blue">[[ sizeFormat(dbInbound.up) ]]</a-tag>
|
<a-tag color="blue">[[ sizeFormat(dbInbound.up) ]]</a-tag>
|
||||||
<a-tag color="green">[[ sizeFormat(dbInbound.down) ]]</a-tag>
|
<a-tag color="green">[[ sizeFormat(dbInbound.down) ]]</a-tag>
|
||||||
|
<a-tag v-if="dbInbound.total > 0" color="cyan">[[ sizeFormat(dbInbound.total) ]]</a-tag>
|
||||||
|
<a-tag v-else color="cyan">无限制</a-tag>
|
||||||
</template>
|
</template>
|
||||||
<template slot="settings" slot-scope="text, dbInbound">
|
<template slot="settings" slot-scope="text, dbInbound">
|
||||||
<a-button type="link">查看</a-button>
|
<a-button type="link">查看</a-button>
|
||||||
@@ -76,6 +78,7 @@
|
|||||||
<template slot="action" slot-scope="text, dbInbound">
|
<template slot="action" slot-scope="text, dbInbound">
|
||||||
<a-button v-if="dbInbound.hasLink()" type="primary" icon="qrcode" @click="showQrcode(dbInbound)"></a-button>
|
<a-button v-if="dbInbound.hasLink()" type="primary" icon="qrcode" @click="showQrcode(dbInbound)"></a-button>
|
||||||
<a-button type="primary" icon="edit" @click="openEditInbound(dbInbound)"></a-button>
|
<a-button type="primary" icon="edit" @click="openEditInbound(dbInbound)"></a-button>
|
||||||
|
<a-button icon="retweet" @click="resetTraffic(dbInbound)"></a-button>
|
||||||
<a-button type="danger" icon="delete" @click="delInbound(dbInbound)"></a-button>
|
<a-button type="danger" icon="delete" @click="delInbound(dbInbound)"></a-button>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
@@ -94,17 +97,17 @@
|
|||||||
dataIndex: "id",
|
dataIndex: "id",
|
||||||
width: 60,
|
width: 60,
|
||||||
}, {
|
}, {
|
||||||
title: "protocol",
|
title: "协议",
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 60,
|
width: 60,
|
||||||
scopedSlots: { customRender: 'protocol' },
|
scopedSlots: { customRender: 'protocol' },
|
||||||
}, {
|
}, {
|
||||||
title: "port",
|
title: "端口",
|
||||||
align: 'center',
|
align: 'center',
|
||||||
dataIndex: "port",
|
dataIndex: "port",
|
||||||
width: 60,
|
width: 60,
|
||||||
}, {
|
}, {
|
||||||
title: "traffic",
|
title: "流量↑|↓",
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 60,
|
width: 60,
|
||||||
scopedSlots: { customRender: 'traffic' },
|
scopedSlots: { customRender: 'traffic' },
|
||||||
@@ -119,7 +122,7 @@
|
|||||||
// width: 60,
|
// width: 60,
|
||||||
// scopedSlots: { customRender: 'streamSettings' },
|
// scopedSlots: { customRender: 'streamSettings' },
|
||||||
}, {
|
}, {
|
||||||
title: "enable",
|
title: "启用",
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 60,
|
width: 60,
|
||||||
scopedSlots: { customRender: 'enable' },
|
scopedSlots: { customRender: 'enable' },
|
||||||
@@ -172,8 +175,8 @@
|
|||||||
},
|
},
|
||||||
openAddInbound() {
|
openAddInbound() {
|
||||||
inModal.show({
|
inModal.show({
|
||||||
title: 'add account',
|
title: '添加入站',
|
||||||
okText: 'add',
|
okText: '添加',
|
||||||
confirm: async (inbound, dbInbound) => {
|
confirm: async (inbound, dbInbound) => {
|
||||||
inModal.loading();
|
inModal.loading();
|
||||||
await this.addInbound(inbound, dbInbound);
|
await this.addInbound(inbound, dbInbound);
|
||||||
@@ -184,8 +187,8 @@
|
|||||||
openEditInbound(dbInbound) {
|
openEditInbound(dbInbound) {
|
||||||
const inbound = dbInbound.toInbound();
|
const inbound = dbInbound.toInbound();
|
||||||
inModal.show({
|
inModal.show({
|
||||||
title: 'update account',
|
title: '修改入站',
|
||||||
okText: 'update',
|
okText: '修改',
|
||||||
inbound: inbound,
|
inbound: inbound,
|
||||||
dbInbound: dbInbound,
|
dbInbound: dbInbound,
|
||||||
confirm: async (inbound, dbInbound) => {
|
confirm: async (inbound, dbInbound) => {
|
||||||
@@ -197,6 +200,9 @@
|
|||||||
},
|
},
|
||||||
async addInbound(inbound, dbInbound) {
|
async addInbound(inbound, dbInbound) {
|
||||||
const data = {
|
const data = {
|
||||||
|
up: dbInbound.up,
|
||||||
|
down: dbInbound.down,
|
||||||
|
total: dbInbound.total,
|
||||||
remark: dbInbound.remark,
|
remark: dbInbound.remark,
|
||||||
enable: dbInbound.enable,
|
enable: dbInbound.enable,
|
||||||
|
|
||||||
@@ -211,6 +217,9 @@
|
|||||||
},
|
},
|
||||||
async updateInbound(inbound, dbInbound) {
|
async updateInbound(inbound, dbInbound) {
|
||||||
const data = {
|
const data = {
|
||||||
|
up: dbInbound.up,
|
||||||
|
down: dbInbound.down,
|
||||||
|
total: dbInbound.total,
|
||||||
remark: dbInbound.remark,
|
remark: dbInbound.remark,
|
||||||
enable: dbInbound.enable,
|
enable: dbInbound.enable,
|
||||||
|
|
||||||
@@ -223,18 +232,32 @@
|
|||||||
};
|
};
|
||||||
await this.submit(`/xui/inbound/update/${dbInbound.id}`, data, inModal);
|
await this.submit(`/xui/inbound/update/${dbInbound.id}`, data, inModal);
|
||||||
},
|
},
|
||||||
|
resetTraffic(dbInbound) {
|
||||||
|
this.$confirm({
|
||||||
|
title: '重置流量',
|
||||||
|
content: '确定要重置流量吗?',
|
||||||
|
okText: '重置',
|
||||||
|
cancelText: '取消',
|
||||||
|
onOk: () => {
|
||||||
|
const inbound = dbInbound.toInbound();
|
||||||
|
dbInbound.up = 0;
|
||||||
|
dbInbound.down = 0;
|
||||||
|
this.updateInbound(inbound, dbInbound);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
delInbound(dbInbound) {
|
delInbound(dbInbound) {
|
||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: 'delete account',
|
title: '删除入站',
|
||||||
content: 'Cannot be restored after deletion, confirm deletion?',
|
content: '确定要删除入站吗?',
|
||||||
okText: 'delete',
|
okText: '删除',
|
||||||
cancelText: 'cancel',
|
cancelText: '取消',
|
||||||
onOk: () => this.submit('/xui/inbound/del/' + dbInbound.id),
|
onOk: () => this.submit('/xui/inbound/del/' + dbInbound.id),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
showQrcode(dbInbound) {
|
showQrcode(dbInbound) {
|
||||||
let address = location.hostname;
|
let address = location.hostname;
|
||||||
if (!ObjectUtil.isEmpty(dbInbound.listen) || dbInbound.listen !== "0.0.0.0") {
|
if (!ObjectUtil.isEmpty(dbInbound.listen) && dbInbound.listen !== "0.0.0.0") {
|
||||||
address = dbInbound.listen;
|
address = dbInbound.listen;
|
||||||
}
|
}
|
||||||
const link = dbInbound.genLink(address);
|
const link = dbInbound.genLink(address);
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
oldInbound.Up = inbound.Up
|
||||||
|
oldInbound.Down = inbound.Down
|
||||||
|
oldInbound.Total = inbound.Total
|
||||||
oldInbound.Remark = inbound.Remark
|
oldInbound.Remark = inbound.Remark
|
||||||
oldInbound.Enable = inbound.Enable
|
oldInbound.Enable = inbound.Enable
|
||||||
oldInbound.ExpiryTime = inbound.ExpiryTime
|
oldInbound.ExpiryTime = inbound.ExpiryTime
|
||||||
@@ -98,3 +101,13 @@ func (s *InboundService) AddTraffic(traffics []*xray.Traffic) (err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *InboundService) DisableInvalidInbounds() (bool, error) {
|
||||||
|
db := database.GetDB()
|
||||||
|
result := db.Model(model.Inbound{}).
|
||||||
|
Where("up + down >= total and total > 0 and enable = ?", true).
|
||||||
|
Update("enable", false)
|
||||||
|
err := result.Error
|
||||||
|
count := result.RowsAffected
|
||||||
|
return count > 0, err
|
||||||
|
}
|
||||||
|
|||||||
20
web/web.go
20
web/web.go
@@ -21,7 +21,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"x-ui/config"
|
"x-ui/config"
|
||||||
"x-ui/logger"
|
"x-ui/logger"
|
||||||
"x-ui/util"
|
|
||||||
"x-ui/util/common"
|
"x-ui/util/common"
|
||||||
"x-ui/web/controller"
|
"x-ui/web/controller"
|
||||||
"x-ui/web/service"
|
"x-ui/web/service"
|
||||||
@@ -244,6 +243,7 @@ func (s *Server) startTask() {
|
|||||||
logger.Warning("start xray failed:", err)
|
logger.Warning("start xray failed:", err)
|
||||||
}
|
}
|
||||||
var checkTime = 0
|
var checkTime = 0
|
||||||
|
// 每 30 秒检查一次 xray 是否在运行
|
||||||
s.cron.AddFunc("@every 30s", func() {
|
s.cron.AddFunc("@every 30s", func() {
|
||||||
if s.xrayService.IsXrayRunning() {
|
if s.xrayService.IsXrayRunning() {
|
||||||
checkTime = 0
|
checkTime = 0
|
||||||
@@ -255,9 +255,10 @@ func (s *Server) startTask() {
|
|||||||
}
|
}
|
||||||
s.xrayService.SetIsNeedRestart(true)
|
s.xrayService.SetIsNeedRestart(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(time.Second * 5)
|
time.Sleep(time.Second * 5)
|
||||||
// 与重启 xray 的时间错开
|
// 每 10 秒统计一次流量,首次启动延迟 5 秒,与重启 xray 的时间错开
|
||||||
s.cron.AddFunc("@every 10s", func() {
|
s.cron.AddFunc("@every 10s", func() {
|
||||||
if !s.xrayService.IsXrayRunning() {
|
if !s.xrayService.IsXrayRunning() {
|
||||||
return
|
return
|
||||||
@@ -273,6 +274,16 @@ func (s *Server) startTask() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// 每分钟检查一次 inbound 流量超出情况
|
||||||
|
s.cron.AddFunc("@every 1m", func() {
|
||||||
|
needRestart, err := s.inboundService.DisableInvalidInbounds()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warning("disable invalid inbounds err:", err)
|
||||||
|
} else if needRestart {
|
||||||
|
s.xrayService.SetIsNeedRestart(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Start() (err error) {
|
func (s *Server) Start() (err error) {
|
||||||
@@ -343,11 +354,8 @@ func (s *Server) Start() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Stop() error {
|
func (s *Server) Stop() error {
|
||||||
if util.IsDone(s.ctx) {
|
|
||||||
// 防止 gc 后调用第二次 Stop
|
|
||||||
s.xrayService.StopXray()
|
|
||||||
}
|
|
||||||
s.cancel()
|
s.cancel()
|
||||||
|
s.xrayService.StopXray()
|
||||||
if s.cron != nil {
|
if s.cron != nil {
|
||||||
s.cron.Stop()
|
s.cron.Stop()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user