Skip to content

Commit 89677c4

Browse files
committed
0.2.0
- 优化 ui 界面 - 优化网站加载速度 - 新增从 v2-ui 迁移账号数据的功能
1 parent d67dff5 commit 89677c4

15 files changed

+391
-18
lines changed

config/version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.0
1+
0.2.0

install.sh

+1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ install_x-ui() {
137137
echo -e "x-ui enable - 设置 x-ui 开机自启"
138138
echo -e "x-ui disable - 取消 x-ui 开机自启"
139139
echo -e "x-ui log - 查看 x-ui 日志"
140+
echo -e "x-ui v2-ui - 迁移本机器的 v2-ui 账号数据至 x-ui"
140141
echo -e "x-ui update - 更新 x-ui 面板"
141142
echo -e "x-ui install - 安装 x-ui 面板"
142143
echo -e "x-ui uninstall - 卸载 x-ui 面板"

main.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,12 @@ func runWebServer() {
5050
}
5151

5252
sigCh := make(chan os.Signal, 1)
53-
signal.Notify(sigCh, syscall.SIGHUP)
53+
signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGKILL)
5454
for {
5555
sig := <-sigCh
5656

57-
if sig == syscall.SIGHUP {
57+
switch sig {
58+
case syscall.SIGHUP:
5859
err := server.Stop()
5960
if err != nil {
6061
logger.Warning("stop server err:", err)
@@ -66,8 +67,9 @@ func runWebServer() {
6667
log.Println(err)
6768
return
6869
}
69-
} else {
70-
continue
70+
default:
71+
server.Stop()
72+
return
7173
}
7274
}
7375
}
@@ -173,7 +175,7 @@ func main() {
173175
}
174176
err = v2ui.MigrateFromV2UI(dbPath)
175177
if err != nil {
176-
logger.Error("migrate from v2-ui failed:", err)
178+
fmt.Println("migrate from v2-ui failed:", err)
177179
}
178180
case "setting":
179181
err := settingCmd.Parse(os.Args[2:])

v2ui/db.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package v2ui
2+
3+
import (
4+
"gorm.io/driver/sqlite"
5+
"gorm.io/gorm"
6+
"gorm.io/gorm/logger"
7+
)
8+
9+
var v2db *gorm.DB
10+
11+
func initDB(dbPath string) error {
12+
c := &gorm.Config{
13+
Logger: logger.Discard,
14+
}
15+
var err error
16+
v2db, err = gorm.Open(sqlite.Open(dbPath), c)
17+
if err != nil {
18+
return err
19+
}
20+
21+
return nil
22+
}
23+
24+
func getV2Inbounds() ([]*V2Inbound, error) {
25+
inbounds := make([]*V2Inbound, 0)
26+
err := v2db.Model(V2Inbound{}).Find(&inbounds).Error
27+
return inbounds, err
28+
}

v2ui/models.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package v2ui
2+
3+
import "x-ui/database/model"
4+
5+
type V2Inbound struct {
6+
Id int `gorm:"primaryKey;autoIncrement"`
7+
Port int `gorm:"unique"`
8+
Listen string
9+
Protocol string
10+
Settings string
11+
StreamSettings string
12+
Tag string `gorm:"unique"`
13+
Sniffing string
14+
Remark string
15+
Up int64
16+
Down int64
17+
Enable bool
18+
}
19+
20+
func (i *V2Inbound) TableName() string {
21+
return "inbound"
22+
}
23+
24+
func (i *V2Inbound) ToInbound(userId int) *model.Inbound {
25+
return &model.Inbound{
26+
UserId: userId,
27+
Up: i.Up,
28+
Down: i.Down,
29+
Total: 0,
30+
Remark: i.Remark,
31+
Enable: i.Enable,
32+
ExpiryTime: 0,
33+
Listen: i.Listen,
34+
Port: i.Port,
35+
Protocol: model.Protocol(i.Protocol),
36+
Settings: i.Settings,
37+
StreamSettings: i.StreamSettings,
38+
Tag: i.Tag,
39+
Sniffing: i.Sniffing,
40+
}
41+
}

v2ui/v2ui.go

+46-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,51 @@
11
package v2ui
22

3-
import "errors"
3+
import (
4+
"fmt"
5+
"x-ui/config"
6+
"x-ui/database"
7+
"x-ui/database/model"
8+
"x-ui/util/common"
9+
"x-ui/web/service"
10+
)
411

512
func MigrateFromV2UI(dbPath string) error {
6-
return errors.New("not support right now")
13+
err := initDB(dbPath)
14+
if err != nil {
15+
return common.NewError("init v2-ui database failed:", err)
16+
}
17+
err = database.InitDB(config.GetDBPath())
18+
if err != nil {
19+
return common.NewError("init x-ui database failed:", err)
20+
}
21+
22+
v2Inbounds, err := getV2Inbounds()
23+
if err != nil {
24+
return common.NewError("get v2-ui inbounds failed:", err)
25+
}
26+
if len(v2Inbounds) == 0 {
27+
fmt.Println("migrate v2-ui inbounds success: 0")
28+
return nil
29+
}
30+
31+
userService := service.UserService{}
32+
user, err := userService.GetFirstUser()
33+
if err != nil {
34+
return common.NewError("get x-ui user failed:", err)
35+
}
36+
37+
inbounds := make([]*model.Inbound, 0)
38+
for _, v2inbound := range v2Inbounds {
39+
inbounds = append(inbounds, v2inbound.ToInbound(user.Id))
40+
}
41+
42+
inboundService := service.InboundService{}
43+
err = inboundService.AddInbounds(inbounds)
44+
if err != nil {
45+
return common.NewError("add x-ui inbounds failed:", err)
46+
}
47+
48+
fmt.Println("migrate v2-ui inbounds success:", len(inbounds))
49+
50+
return nil
751
}

web/assets/[email protected]/antd.min.js

-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/assets/js/model/xray.js

+88
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,15 @@ TcpStreamSettings.TcpRequest = class extends XrayCommonClass {
168168
this.headers.push({ name: name, value: value });
169169
}
170170

171+
getHeader(name) {
172+
for (const header of this.headers) {
173+
if (header.name.toLowerCase() === name.toLowerCase()) {
174+
return header.value;
175+
}
176+
}
177+
return null;
178+
}
179+
171180
removeHeader(index) {
172181
this.headers.splice(index, 1);
173182
}
@@ -294,6 +303,15 @@ class WsStreamSettings extends XrayCommonClass {
294303
this.headers.push({ name: name, value: value });
295304
}
296305

306+
getHeader(name) {
307+
for (const header of this.headers) {
308+
if (header.name.toLowerCase() === name.toLowerCase()) {
309+
return header.value;
310+
}
311+
}
312+
return null;
313+
}
314+
297315
removeHeader(index) {
298316
this.headers.splice(index, 1);
299317
}
@@ -643,6 +661,30 @@ class Inbound extends XrayCommonClass {
643661
this.stream.network = network;
644662
}
645663

664+
get isTcp() {
665+
return this.network === "tcp";
666+
}
667+
668+
get isWs() {
669+
return this.network === "ws";
670+
}
671+
672+
get isKcp() {
673+
return this.network === "kcp";
674+
}
675+
676+
get isQuic() {
677+
return this.network === "quic"
678+
}
679+
680+
get isGrpc() {
681+
return this.network === "grpc";
682+
}
683+
684+
get isH2() {
685+
return this.network === "http";
686+
}
687+
646688
// VMess & VLess
647689
get uuid() {
648690
switch (this.protocol) {
@@ -718,6 +760,52 @@ class Inbound extends XrayCommonClass {
718760
return "";
719761
}
720762

763+
get host() {
764+
if (this.isTcp) {
765+
return this.stream.tcp.request.getHeader("Host");
766+
} else if (this.isWs) {
767+
return this.stream.ws.getHeader("Host");
768+
} else if (this.isH2) {
769+
return this.stream.http.host[0];
770+
}
771+
return null;
772+
}
773+
774+
get path() {
775+
if (this.isTcp) {
776+
return this.stream.tcp.request.path[0];
777+
} else if (this.isWs) {
778+
return this.stream.ws.path;
779+
} else if (this.isH2) {
780+
return this.stream.http.path[0];
781+
}
782+
return null;
783+
}
784+
785+
get quicSecurity() {
786+
return this.stream.quic.security;
787+
}
788+
789+
get quicKey() {
790+
return this.stream.quic.key;
791+
}
792+
793+
get quicType() {
794+
return this.stream.quic.type;
795+
}
796+
797+
get kcpType() {
798+
return this.stream.kcp.type;
799+
}
800+
801+
get kcpSeed() {
802+
return this.stream.kcp.seed;
803+
}
804+
805+
get serviceName() {
806+
return this.stream.grpc.serviceName;
807+
}
808+
721809
canEnableTls() {
722810
switch (this.protocol) {
723811
case Protocols.VMESS:

web/html/xui/component/inbound_info.html

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,28 @@
11
{{define "inboundInfoStream"}}
22
<p>传输: <a-tag color="green">[[ inbound.network ]]</a-tag></p>
33

4-
<!-- TODO -->
4+
<template v-if="inbound.isTcp || inbound.isWs || inbound.isH2">
5+
<p v-if="inbound.host">host: <a-tag color="green">[[ inbound.host ]]</a-tag></p>
6+
<p v-else>host: <a-tag color="orange"></a-tag></p>
7+
8+
<p v-if="inbound.path">path: <a-tag color="green">[[ inbound.path ]]</a-tag></p>
9+
<p v-else>path: <a-tag color="orange"></a-tag></p>
10+
</template>
11+
12+
<template v-if="inbound.isQuic">
13+
<p>quic 加密: <a-tag color="green">[[ inbound.quicSecurity ]]</a-tag></p>
14+
<p>quic 密码: <a-tag color="green">[[ inbound.quicKey ]]</a-tag></p>
15+
<p>quic 伪装: <a-tag color="green">[[ inbound.quicType ]]</a-tag></p>
16+
</template>
17+
18+
<template v-if="inbound.isKcp">
19+
<p>kcp 加密: <a-tag color="green">[[ inbound.kcpType ]]</a-tag></p>
20+
<p>kcp 密码: <a-tag color="green">[[ inbound.kcpSeed ]]</a-tag></p>
21+
</template>
22+
23+
<template v-if="inbound.isGrpc">
24+
<p>grpc serviceName: <a-tag color="green">[[ inbound.serviceName ]]</a-tag></p>
25+
</template>
526

627
<template v-if="inbound.tls || inbound.xtls">
728
<p v-if="inbound.tls">tls: <a-tag color="green">开启</a-tag></p>
@@ -11,10 +32,10 @@
1132
<p>tls: <a-tag color="red">关闭</a-tag></p>
1233
</template>
1334
<p v-if="inbound.tls">
14-
tls域名: <a-tag color="green">[[ inbound.serverName ? inbound.serverName : "无" ]]</a-tag>
35+
tls域名: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : "无" ]]</a-tag>
1536
</p>
1637
<p v-if="inbound.xtls">
17-
xtls域名: <a-tag color="green">[[ inbound.serverName ? inbound.serverName : "无" ]]</a-tag>
38+
xtls域名: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : "无" ]]</a-tag>
1839
</p>
1940
{{end}}
2041

web/html/xui/inbound_info_modal.html

+23-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{{template "component/inboundInfo"}}
33
<a-modal id="inbound-info-modal" v-model="infoModal.visible" title="详细信息" @ok="infoModal.ok"
44
:closable="true" :mask-closable="true"
5-
ok-text="复制链接" cancel-text='{{ i18n "close" }}'>
5+
ok-text="复制链接" cancel-text='{{ i18n "close" }}' :ok-button-props="infoModal.okBtnPros">
66
<inbound-info :db-inbound="dbInbound" :inbound="inbound"></inbound-info>
77
</a-modal>
88
<script>
@@ -11,20 +11,39 @@
1111
visible: false,
1212
inbound: new Inbound(),
1313
dbInbound: new DBInbound(),
14-
ok() {
15-
14+
clipboard: null,
15+
okBtnPros: {
16+
attrs: {
17+
id: "inbound-info-modal-ok-btn",
18+
style: "",
19+
},
1620
},
1721
show(dbInbound) {
1822
this.inbound = dbInbound.toInbound();
1923
this.dbInbound = new DBInbound(dbInbound);
2024
this.visible = true;
25+
26+
if (dbInbound.hasLink()) {
27+
this.okBtnPros.attrs.style = "";
28+
} else {
29+
this.okBtnPros.attrs.style = "display: none";
30+
}
31+
32+
if (this.clipboard == null) {
33+
infoModalApp.$nextTick(() => {
34+
this.clipboard = new ClipboardJS(`#${this.okBtnPros.attrs.id}`, {
35+
text: () => this.dbInbound.genLink(),
36+
});
37+
this.clipboard.on('success', () => app.$message.success('复制成功'));
38+
});
39+
}
2140
},
2241
close() {
2342
infoModal.visible = false;
2443
},
2544
};
2645

27-
new Vue({
46+
const infoModalApp = new Vue({
2847
delimiters: ['[[', ']]'],
2948
el: '#inbound-info-modal',
3049
data: {

0 commit comments

Comments
 (0)