Skip to content

Commit 214b217

Browse files
committed
0.0.2
- 增加设置总流量功能,流量超出后自动禁用 - 优化部分 ui 细节 - 修复监听 ip 不为空导致无法启动 xray 的问题 - 修复二维码链接没有包含 address 的问题
1 parent f6eb413 commit 214b217

File tree

14 files changed

+149
-36
lines changed

14 files changed

+149
-36
lines changed

README.md

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,38 @@
11
# x-ui
2-
a web panel based on xray-core
2+
支持多协议多用户 xray 面板
3+
4+
# 功能介绍
5+
- 系统状态监控
6+
- 支持多用户多协议,网页可视化操作
7+
- 支持的协议:vmess、vless、trojan、shadowsocks、dokodemo-door、socks、http
8+
- 支持配置更多传输配置
9+
- 账号流量统计
10+
- 可自定义 xray 配置模板
11+
- 支持 https 访问面板(自备域名 + ssl 证书)
12+
- 更多高级配置项,详见面板
13+
14+
# 安装&升级
15+
## 测试版
16+
```
17+
bash <(curl -Ls https://raw.githubusercontent.com/sprov065/x-ui/master/install.sh) 0.0.1
18+
```
19+
20+
## 建议系统
21+
- CentOS 7+
22+
- Ubuntu 16+
23+
- Debian 8+
24+
25+
# 常见问题
26+
## 与 v2-ui 关系
27+
x-ui 相当于 v2-ui 的加强版,未来会加入更多功能,待 x-ui 功能稳定后,v2-ui 将不再提供更新
28+
29+
x-ui 可与 v2-ui 并存,数据不互通,不影响对方的运行
30+
31+
# Telegram
32+
群组:https://t.me/sprov_blog
33+
34+
频道:https://t.me/sprov_channel
35+
36+
## Stargazers over time
37+
38+
[![Stargazers over time](https://starchart.cc/sprov065/x-ui.svg)](https://starchart.cc/sprov065/x-ui)

a.s

Whitespace-only changes.

config/config.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
package config
22

33
import (
4+
_ "embed"
45
"fmt"
56
"os"
7+
"strings"
68
)
79

10+
//go:embed version
11+
var version string
12+
13+
//go:embed name
14+
var name string
15+
816
type LogLevel string
917

1018
const (
@@ -15,11 +23,11 @@ const (
1523
)
1624

1725
func GetVersion() string {
18-
return "0.0.1"
26+
return strings.TrimSpace(version)
1927
}
2028

2129
func GetName() string {
22-
return "x-ui"
30+
return strings.TrimSpace(name)
2331
}
2432

2533
func GetLogLevel() LogLevel {

config/name

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
x-ui

config/version

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.0.2

database/model/model.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package model
22

33
import (
4+
"fmt"
45
"x-ui/util/json_util"
56
"x-ui/xray"
67
)
@@ -25,8 +26,9 @@ type User struct {
2526
type Inbound struct {
2627
Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"`
2728
UserId int `json:"-"`
28-
Up int64 `json:"up"`
29-
Down int64 `json:"down"`
29+
Up int64 `json:"up" form:"up"`
30+
Down int64 `json:"down" form:"down"`
31+
Total int64 `json:"total" form:"total"`
3032
Remark string `json:"remark" form:"remark"`
3133
Enable bool `json:"enable" form:"enable"`
3234
ExpiryTime int64 `json:"expiryTime" form:"expiryTime"`
@@ -42,8 +44,12 @@ type Inbound struct {
4244
}
4345

4446
func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig {
47+
listen := i.Listen
48+
if listen != "" {
49+
listen = fmt.Sprintf("\"%v\"", listen)
50+
}
4551
return &xray.InboundConfig{
46-
Listen: json_util.RawMessage(i.Listen),
52+
Listen: json_util.RawMessage(listen),
4753
Port: i.Port,
4854
Protocol: string(i.Protocol),
4955
Settings: json_util.RawMessage(i.Settings),

main.go

+2-5
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ import (
1818
"x-ui/web/service"
1919
)
2020

21-
// this function call global.setWebServer
22-
func setWebServer(server global.WebServer)
23-
2421
func runWebServer() {
2522
log.Printf("%v %v", config.GetName(), config.GetVersion())
2623

@@ -45,7 +42,7 @@ func runWebServer() {
4542
var server *web.Server
4643

4744
server = web.NewServer()
48-
setWebServer(server)
45+
global.SetWebServer(server)
4946
err = server.Start()
5047
if err != nil {
5148
log.Println(err)
@@ -60,7 +57,7 @@ func runWebServer() {
6057
if sig == syscall.SIGHUP {
6158
server.Stop()
6259
server = web.NewServer()
63-
setWebServer(server)
60+
global.SetWebServer(server)
6461
err = server.Start()
6562
if err != nil {
6663
log.Println(err)

web/assets/js/model/models.js

+9
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class DBInbound {
2626
userId = 0;
2727
up = 0;
2828
down = 0;
29+
total = 0;
2930
remark = "";
3031
enable = true;
3132
expiryTime = 0;
@@ -45,6 +46,14 @@ class DBInbound {
4546
ObjectUtil.cloneProps(this, data);
4647
}
4748

49+
get totalGB() {
50+
return toFixed(this.total / ONE_GB, 2);
51+
}
52+
53+
set totalGB(gb) {
54+
this.total = toFixed(gb * ONE_GB, 0);
55+
}
56+
4857
toInbound() {
4958
let settings = {};
5059
if (!ObjectUtil.isEmpty(this.settings)) {

web/assets/js/util/utils.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ class ObjectUtil {
284284
return false;
285285
}
286286
}
287-
return true
287+
return true;
288288
}
289289

290290
}

web/global/global.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ type WebServer interface {
1313
GetCtx() context.Context
1414
}
1515

16-
//go:linkname setWebServer main.setWebServer
17-
func setWebServer(s WebServer) {
16+
func SetWebServer(s WebServer) {
1817
webServer = s
1918
}
2019

web/html/xui/form/inbound.html

+12
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@
2727
<a-form-item label="端口">
2828
<a-input type="number" v-model.number="inbound.port"></a-input>
2929
</a-form-item>
30+
<a-form-item>
31+
<span slot="label">
32+
总流量(GB)
33+
<a-tooltip>
34+
<template slot="title">
35+
0 表示不限制
36+
</template>
37+
<a-icon type="question-circle" theme="filled"></a-icon>
38+
</a-tooltip>
39+
</span>
40+
<a-input-number v-model="dbInbound.totalGB" :min="0"></a-input-number>
41+
</a-form-item>
3042
</a-form>
3143

3244
<!-- vmess settings -->

web/html/xui/inbounds.html

+39-16
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@
2727
<a-card hoverable style="margin-bottom: 20px;">
2828
<a-row>
2929
<a-col :xs="24" :sm="24" :lg="12">
30-
upload / download
30+
总上传 / 下载
3131
<a-tag color="green">[[ sizeFormat(total.up) ]] / [[ sizeFormat(total.down) ]]</a-tag>
3232
</a-col>
3333
<a-col :xs="24" :sm="24" :lg="12">
34-
total traffic
34+
总用量
3535
<a-tag color="green">[[ sizeFormat(total.up + total.down) ]]</a-tag>
3636
</a-col>
3737
<a-col :xs="24" :sm="24" :lg="12">
38-
number of accounts
38+
入站数量
3939
<a-tag color="green">[[ dbInbounds.length ]]</a-tag>
4040
</a-col>
4141
</a-row>
@@ -59,6 +59,8 @@
5959
<template slot="traffic" slot-scope="text, dbInbound">
6060
<a-tag color="blue">[[ sizeFormat(dbInbound.up) ]]</a-tag>
6161
<a-tag color="green">[[ sizeFormat(dbInbound.down) ]]</a-tag>
62+
<a-tag v-if="dbInbound.total > 0" color="cyan">[[ sizeFormat(dbInbound.total) ]]</a-tag>
63+
<a-tag v-else color="cyan">无限制</a-tag>
6264
</template>
6365
<template slot="settings" slot-scope="text, dbInbound">
6466
<a-button type="link">查看</a-button>
@@ -76,6 +78,7 @@
7678
<template slot="action" slot-scope="text, dbInbound">
7779
<a-button v-if="dbInbound.hasLink()" type="primary" icon="qrcode" @click="showQrcode(dbInbound)"></a-button>
7880
<a-button type="primary" icon="edit" @click="openEditInbound(dbInbound)"></a-button>
81+
<a-button icon="retweet" @click="resetTraffic(dbInbound)"></a-button>
7982
<a-button type="danger" icon="delete" @click="delInbound(dbInbound)"></a-button>
8083
</template>
8184
</a-table>
@@ -94,17 +97,17 @@
9497
dataIndex: "id",
9598
width: 60,
9699
}, {
97-
title: "protocol",
100+
title: "协议",
98101
align: 'center',
99102
width: 60,
100103
scopedSlots: { customRender: 'protocol' },
101104
}, {
102-
title: "port",
105+
title: "端口",
103106
align: 'center',
104107
dataIndex: "port",
105108
width: 60,
106109
}, {
107-
title: "traffic",
110+
title: "流量↑|↓",
108111
align: 'center',
109112
width: 60,
110113
scopedSlots: { customRender: 'traffic' },
@@ -119,7 +122,7 @@
119122
// width: 60,
120123
// scopedSlots: { customRender: 'streamSettings' },
121124
}, {
122-
title: "enable",
125+
title: "启用",
123126
align: 'center',
124127
width: 60,
125128
scopedSlots: { customRender: 'enable' },
@@ -172,8 +175,8 @@
172175
},
173176
openAddInbound() {
174177
inModal.show({
175-
title: 'add account',
176-
okText: 'add',
178+
title: '添加入站',
179+
okText: '添加',
177180
confirm: async (inbound, dbInbound) => {
178181
inModal.loading();
179182
await this.addInbound(inbound, dbInbound);
@@ -184,8 +187,8 @@
184187
openEditInbound(dbInbound) {
185188
const inbound = dbInbound.toInbound();
186189
inModal.show({
187-
title: 'update account',
188-
okText: 'update',
190+
title: '修改入站',
191+
okText: '修改',
189192
inbound: inbound,
190193
dbInbound: dbInbound,
191194
confirm: async (inbound, dbInbound) => {
@@ -197,6 +200,9 @@
197200
},
198201
async addInbound(inbound, dbInbound) {
199202
const data = {
203+
up: dbInbound.up,
204+
down: dbInbound.down,
205+
total: dbInbound.total,
200206
remark: dbInbound.remark,
201207
enable: dbInbound.enable,
202208

@@ -211,6 +217,9 @@
211217
},
212218
async updateInbound(inbound, dbInbound) {
213219
const data = {
220+
up: dbInbound.up,
221+
down: dbInbound.down,
222+
total: dbInbound.total,
214223
remark: dbInbound.remark,
215224
enable: dbInbound.enable,
216225

@@ -223,18 +232,32 @@
223232
};
224233
await this.submit(`/xui/inbound/update/${dbInbound.id}`, data, inModal);
225234
},
235+
resetTraffic(dbInbound) {
236+
this.$confirm({
237+
title: '重置流量',
238+
content: '确定要重置流量吗?',
239+
okText: '重置',
240+
cancelText: '取消',
241+
onOk: () => {
242+
const inbound = dbInbound.toInbound();
243+
dbInbound.up = 0;
244+
dbInbound.down = 0;
245+
this.updateInbound(inbound, dbInbound);
246+
},
247+
});
248+
},
226249
delInbound(dbInbound) {
227250
this.$confirm({
228-
title: 'delete account',
229-
content: 'Cannot be restored after deletion, confirm deletion?',
230-
okText: 'delete',
231-
cancelText: 'cancel',
251+
title: '删除入站',
252+
content: '确定要删除入站吗?',
253+
okText: '删除',
254+
cancelText: '取消',
232255
onOk: () => this.submit('/xui/inbound/del/' + dbInbound.id),
233256
});
234257
},
235258
showQrcode(dbInbound) {
236259
let address = location.hostname;
237-
if (!ObjectUtil.isEmpty(dbInbound.listen) || dbInbound.listen !== "0.0.0.0") {
260+
if (!ObjectUtil.isEmpty(dbInbound.listen) && dbInbound.listen !== "0.0.0.0") {
238261
address = dbInbound.listen;
239262
}
240263
const link = dbInbound.genLink(address);

web/service/inbound.go

+13
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) error {
5656
if err != nil {
5757
return err
5858
}
59+
oldInbound.Up = inbound.Up
60+
oldInbound.Down = inbound.Down
61+
oldInbound.Total = inbound.Total
5962
oldInbound.Remark = inbound.Remark
6063
oldInbound.Enable = inbound.Enable
6164
oldInbound.ExpiryTime = inbound.ExpiryTime
@@ -98,3 +101,13 @@ func (s *InboundService) AddTraffic(traffics []*xray.Traffic) (err error) {
98101
}
99102
return
100103
}
104+
105+
func (s *InboundService) DisableInvalidInbounds() (bool, error) {
106+
db := database.GetDB()
107+
result := db.Model(model.Inbound{}).
108+
Where("up + down >= total and total > 0 and enable = ?", true).
109+
Update("enable", false)
110+
err := result.Error
111+
count := result.RowsAffected
112+
return count > 0, err
113+
}

0 commit comments

Comments
 (0)