mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-03-20 01:25:49 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04d85af40e | ||
|
|
fca882ee31 | ||
|
|
2832106bc6 | ||
|
|
bf24838939 | ||
|
|
16e3107d23 | ||
|
|
262e3c0985 | ||
|
|
2b460bac1a | ||
|
|
d1c4eb9b4c | ||
|
|
dfa3d39ab3 |
@@ -166,7 +166,7 @@ Reference syntax:
|
|||||||
| `POST` | `"/clientIps/:email"` | Client Ip address |
|
| `POST` | `"/clientIps/:email"` | Client Ip address |
|
||||||
| `POST` | `"/clearClientIps/:email"` | Clear Client Ip address |
|
| `POST` | `"/clearClientIps/:email"` | Clear Client Ip address |
|
||||||
| `POST` | `"/addClient/"` | Add Client to inbound |
|
| `POST` | `"/addClient/"` | Add Client to inbound |
|
||||||
| `POST` | `"/delClient/:email"` | Delete Client |
|
| `POST` | `"/:id/delClient/:clientId"` | Delete Client by UID/Password as clientId |
|
||||||
| `POST` | `"/updateClient/:index"` | Update Client |
|
| `POST` | `"/updateClient/:index"` | Update Client |
|
||||||
| `POST` | `"/:id/resetClientTraffic/:email"` | Reset Client's Traffic |
|
| `POST` | `"/:id/resetClientTraffic/:email"` | Reset Client's Traffic |
|
||||||
| `POST` | `"/resetAllTraffics"` | Reset traffics of all inbounds |
|
| `POST` | `"/resetAllTraffics"` | Reset traffics of all inbounds |
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
1.2.7
|
1.2.8
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ install_base() {
|
|||||||
|
|
||||||
#This function will be called when user installed x-ui out of sercurity
|
#This function will be called when user installed x-ui out of sercurity
|
||||||
config_after_install() {
|
config_after_install() {
|
||||||
|
/usr/local/x-ui/x-ui migrate
|
||||||
echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}"
|
echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}"
|
||||||
read -p "Do you want to continue with the modification [y/n]? ": config_confirm
|
read -p "Do you want to continue with the modification [y/n]? ": config_confirm
|
||||||
if [[ x"${config_confirm}" == x"y" || x"${config_confirm}" == x"Y" ]]; then
|
if [[ x"${config_confirm}" == x"y" || x"${config_confirm}" == x"Y" ]]; then
|
||||||
|
|||||||
16
main.go
16
main.go
@@ -204,6 +204,19 @@ func updateSetting(port int, username string, password string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func migrateDb() {
|
||||||
|
inboundService := service.InboundService{}
|
||||||
|
|
||||||
|
err := database.InitDB(config.GetDBPath())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println("Start migrating database...")
|
||||||
|
inboundService.MigrationRequirements()
|
||||||
|
inboundService.RemoveOrphanedTraffics()
|
||||||
|
fmt.Println("Migration done!")
|
||||||
|
}
|
||||||
|
|
||||||
func removeSecret() {
|
func removeSecret() {
|
||||||
err := database.InitDB(config.GetDBPath())
|
err := database.InitDB(config.GetDBPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -265,6 +278,7 @@ func main() {
|
|||||||
fmt.Println("Commands:")
|
fmt.Println("Commands:")
|
||||||
fmt.Println(" run run web panel")
|
fmt.Println(" run run web panel")
|
||||||
fmt.Println(" v2-ui migrate form v2-ui")
|
fmt.Println(" v2-ui migrate form v2-ui")
|
||||||
|
fmt.Println(" migrate migrate form other/old x-ui")
|
||||||
fmt.Println(" setting set settings")
|
fmt.Println(" setting set settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,6 +296,8 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
runWebServer()
|
runWebServer()
|
||||||
|
case "migrate":
|
||||||
|
migrateDb()
|
||||||
case "v2-ui":
|
case "v2-ui":
|
||||||
err := v2uiCmd.Parse(os.Args[2:])
|
err := v2uiCmd.Parse(os.Args[2:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1409,6 +1409,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
|
|
||||||
if (this.reality) {
|
if (this.reality) {
|
||||||
params.set("security", "reality");
|
params.set("security", "reality");
|
||||||
|
params.set("fp", this.stream.reality.settings.fingerprint);
|
||||||
params.set("pbk", this.stream.reality.settings.publicKey);
|
params.set("pbk", this.stream.reality.settings.publicKey);
|
||||||
if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
|
if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
|
||||||
params.set("sni", this.stream.reality.serverNames.split(",")[0]);
|
params.set("sni", this.stream.reality.serverNames.split(",")[0]);
|
||||||
@@ -1419,9 +1420,6 @@ class Inbound extends XrayCommonClass {
|
|||||||
if (this.stream.reality.shortIds.length > 0) {
|
if (this.stream.reality.shortIds.length > 0) {
|
||||||
params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
||||||
}
|
}
|
||||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.fingerprint)) {
|
|
||||||
params.set("fp", this.stream.reality.settings.fingerprint);
|
|
||||||
}
|
|
||||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) {
|
if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) {
|
||||||
address = this.stream.reality.settings.serverName;
|
address = this.stream.reality.settings.serverName;
|
||||||
}
|
}
|
||||||
@@ -1513,19 +1511,14 @@ class Inbound extends XrayCommonClass {
|
|||||||
|
|
||||||
if (this.reality) {
|
if (this.reality) {
|
||||||
params.set("security", "reality");
|
params.set("security", "reality");
|
||||||
|
params.set("fp", this.stream.reality.settings.fingerprint);
|
||||||
params.set("pbk", this.stream.reality.settings.publicKey);
|
params.set("pbk", this.stream.reality.settings.publicKey);
|
||||||
if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
|
if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
|
||||||
params.set("sni", this.stream.reality.serverNames.split(",")[0]);
|
params.set("sni", this.stream.reality.serverNames.split(",")[0]);
|
||||||
}
|
}
|
||||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) {
|
|
||||||
address = this.stream.reality.settings.serverName;
|
|
||||||
}
|
|
||||||
if (this.stream.reality.shortIds.length > 0) {
|
if (this.stream.reality.shortIds.length > 0) {
|
||||||
params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
||||||
}
|
}
|
||||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.fingerprint)) {
|
|
||||||
params.set("fp", this.stream.reality.settings.fingerprint);
|
|
||||||
}
|
|
||||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) {
|
if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) {
|
||||||
address = this.stream.reality.settings.serverName;
|
address = this.stream.reality.settings.serverName;
|
||||||
}
|
}
|
||||||
@@ -1537,7 +1530,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
if(this.stream.xtls.settings.allowInsecure){
|
if(this.stream.xtls.settings.allowInsecure){
|
||||||
params.set("allowInsecure", "1");
|
params.set("allowInsecure", "1");
|
||||||
}
|
}
|
||||||
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
|
if (!ObjectUtil.isEmpty(this.stream.xtls.server)) {
|
||||||
address = this.stream.xtls.server;
|
address = this.stream.xtls.server;
|
||||||
}
|
}
|
||||||
params.set("flow", this.settings.trojans[clientIndex].flow);
|
params.set("flow", this.settings.trojans[clientIndex].flow);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func (a *APIController) initRouter(g *gin.RouterGroup) {
|
|||||||
g.POST("/clientIps/:email", a.getClientIps)
|
g.POST("/clientIps/:email", a.getClientIps)
|
||||||
g.POST("/clearClientIps/:email", a.clearClientIps)
|
g.POST("/clearClientIps/:email", a.clearClientIps)
|
||||||
g.POST("/addClient/", a.addInboundClient)
|
g.POST("/addClient/", a.addInboundClient)
|
||||||
g.POST("/delClient/:email", a.delInboundClient)
|
g.POST("/:id/delClient/:clientId", a.delInboundClient)
|
||||||
g.POST("/updateClient/:index", a.updateInboundClient)
|
g.POST("/updateClient/:index", a.updateInboundClient)
|
||||||
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
|
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
|
||||||
g.POST("/resetAllTraffics", a.resetAllTraffics)
|
g.POST("/resetAllTraffics", a.resetAllTraffics)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
|
|||||||
g.POST("/clientIps/:email", a.getClientIps)
|
g.POST("/clientIps/:email", a.getClientIps)
|
||||||
g.POST("/clearClientIps/:email", a.clearClientIps)
|
g.POST("/clearClientIps/:email", a.clearClientIps)
|
||||||
g.POST("/addClient", a.addInboundClient)
|
g.POST("/addClient", a.addInboundClient)
|
||||||
g.POST("/delClient/:email", a.delInboundClient)
|
g.POST("/:id/delClient/:clientId", a.delInboundClient)
|
||||||
g.POST("/updateClient/:index", a.updateInboundClient)
|
g.POST("/updateClient/:index", a.updateInboundClient)
|
||||||
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
|
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
|
||||||
g.POST("/resetAllTraffics", a.resetAllTraffics)
|
g.POST("/resetAllTraffics", a.resetAllTraffics)
|
||||||
@@ -155,7 +155,7 @@ func (a *InboundController) clearClientIps(c *gin.Context) {
|
|||||||
|
|
||||||
err := a.inboundService.ClearClientIps(email)
|
err := a.inboundService.ClearClientIps(email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jsonMsg(c, "修改", err)
|
jsonMsg(c, "Revise", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
jsonMsg(c, "Log Cleared", nil)
|
jsonMsg(c, "Log Cleared", nil)
|
||||||
@@ -180,15 +180,14 @@ func (a *InboundController) addInboundClient(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *InboundController) delInboundClient(c *gin.Context) {
|
func (a *InboundController) delInboundClient(c *gin.Context) {
|
||||||
email := c.Param("email")
|
id, err := strconv.Atoi(c.Param("id"))
|
||||||
inbound := &model.Inbound{}
|
|
||||||
err := c.ShouldBind(inbound)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
|
jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
clientId := c.Param("clientId")
|
||||||
|
|
||||||
err = a.inboundService.DelInboundClient(inbound, email)
|
err = a.inboundService.DelInboundClient(id, clientId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jsonMsg(c, "something worng!", err)
|
jsonMsg(c, "something worng!", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -46,13 +46,13 @@
|
|||||||
<a-input type="number" v-model.number="clientsBulkModal.limitIp" min="0" style="width: 70px;" ></a-input>
|
<a-input type="number" v-model.number="clientsBulkModal.limitIp" min="0" style="width: 70px;" ></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-if="clientsBulkModal.inbound.xtls" label="Flow">
|
<a-form-item v-if="clientsBulkModal.inbound.xtls" label="Flow">
|
||||||
<a-select v-model="clientsBulkModal.flow" style="width: 150px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
<a-select v-model="clientsBulkModal.flow" style="width: 200px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
<a-select-option value="">{{ i18n "none" }}</a-select-option>
|
<a-select-option value="">{{ i18n "none" }}</a-select-option>
|
||||||
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-if="clientsBulkModal.inbound.canEnableTlsFlow()" label="Flow" layout="inline">
|
<a-form-item v-if="clientsBulkModal.inbound.canEnableTlsFlow()" label="Flow" layout="inline">
|
||||||
<a-select v-model="clientsBulkModal.flow" style="width: 150px">
|
<a-select v-model="clientsBulkModal.flow" style="width: 200px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
||||||
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
|
|||||||
@@ -69,13 +69,13 @@
|
|||||||
</a-form>
|
</a-form>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-if="inbound.xtls" label="Flow">
|
<a-form-item v-if="inbound.xtls" label="Flow">
|
||||||
<a-select v-model="client.flow" style="width: 150px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
<a-select v-model="client.flow" style="width: 200px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
<a-select-option value="">{{ i18n "none" }}</a-select-option>
|
<a-select-option value="">{{ i18n "none" }}</a-select-option>
|
||||||
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-else-if="inbound.canEnableTlsFlow()" label="Flow" layout="inline">
|
<a-form-item v-else-if="inbound.canEnableTlsFlow()" label="Flow" layout="inline">
|
||||||
<a-select v-model="client.flow" style="width: 150px">
|
<a-select v-model="client.flow" style="width: 200px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
||||||
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
|
|||||||
@@ -18,6 +18,12 @@
|
|||||||
</a-form>
|
</a-form>
|
||||||
<a-form-item label="Password">
|
<a-form-item label="Password">
|
||||||
<a-input v-model.trim="client.password" style="width: 150px;"></a-input>
|
<a-input v-model.trim="client.password" style="width: 150px;"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Subscription" v-if="client.email">
|
||||||
|
<a-input v-model.trim="client.subId"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Telegram Username" v-if="client.email">
|
||||||
|
<a-input v-model.trim="client.tgId"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<span slot="label">
|
<span slot="label">
|
||||||
|
|||||||
@@ -18,6 +18,12 @@
|
|||||||
</a-form>
|
</a-form>
|
||||||
<a-form-item label="ID">
|
<a-form-item label="ID">
|
||||||
<a-input v-model.trim="client.id" style="width: 300px;" ></a-input>
|
<a-input v-model.trim="client.id" style="width: 300px;" ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Subscription" v-if="client.email">
|
||||||
|
<a-input v-model.trim="client.subId"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Telegram Username" v-if="client.email">
|
||||||
|
<a-input v-model.trim="client.tgId"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<span slot="label">
|
<span slot="label">
|
||||||
@@ -32,13 +38,13 @@
|
|||||||
<a-input type="number" v-model.number="client.limitIp" min="0" style="width: 70px;" ></a-input>
|
<a-input type="number" v-model.number="client.limitIp" min="0" style="width: 70px;" ></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-if="inbound.xtls" label="Flow">
|
<a-form-item v-if="inbound.xtls" label="Flow">
|
||||||
<a-select v-model="inbound.settings.vlesses[index].flow" style="width: 150px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
<a-select v-model="inbound.settings.vlesses[index].flow" style="width: 200px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
||||||
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-else-if="inbound.canEnableTlsFlow()" label="Flow" layout="inline">
|
<a-form-item v-else-if="inbound.canEnableTlsFlow()" label="Flow" layout="inline">
|
||||||
<a-select v-model="inbound.settings.vlesses[index].flow" style="width: 150px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
<a-select v-model="inbound.settings.vlesses[index].flow" style="width: 200px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
||||||
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
|
|||||||
@@ -21,6 +21,12 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='{{ i18n "additional" }} ID'>
|
<a-form-item label='{{ i18n "additional" }} ID'>
|
||||||
<a-input type="number" v-model.number="client.alterId" style="width: 70px;"></a-input>
|
<a-input type="number" v-model.number="client.alterId" style="width: 70px;"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Subscription" v-if="client.email">
|
||||||
|
<a-input v-model.trim="client.subId"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Telegram Username" v-if="client.email">
|
||||||
|
<a-input v-model.trim="client.tgId"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<span slot="label">
|
<span slot="label">
|
||||||
|
|||||||
@@ -41,19 +41,19 @@
|
|||||||
<a-col :xs="24" :sm="24" :lg="12">
|
<a-col :xs="24" :sm="24" :lg="12">
|
||||||
{{ i18n "clients" }}:
|
{{ i18n "clients" }}:
|
||||||
<a-tag color="green">[[ total.clients ]]</a-tag>
|
<a-tag color="green">[[ total.clients ]]</a-tag>
|
||||||
<a-popover title="{{ i18n "disabled" }}" :overlay-class-name="siderDrawer.isDarkTheme ? 'ant-dark' : ''">
|
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="siderDrawer.isDarkTheme ? 'ant-dark' : ''">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p v-for="clientEmail in total.deactive">[[ clientEmail ]]</p>
|
<p v-for="clientEmail in total.deactive">[[ clientEmail ]]</p>
|
||||||
</template>
|
</template>
|
||||||
<a-tag v-if="total.deactive.length">[[ total.deactive.length ]]</a-tag>
|
<a-tag v-if="total.deactive.length">[[ total.deactive.length ]]</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-popover title="{{ i18n "depleted" }}" :overlay-class-name="siderDrawer.isDarkTheme ? 'ant-dark' : ''">
|
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="siderDrawer.isDarkTheme ? 'ant-dark' : ''">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p v-for="clientEmail in total.depleted">[[ clientEmail ]]</p>
|
<p v-for="clientEmail in total.depleted">[[ clientEmail ]]</p>
|
||||||
</template>
|
</template>
|
||||||
<a-tag color="red" v-if="total.depleted.length">[[ total.depleted.length ]]</a-tag>
|
<a-tag color="red" v-if="total.depleted.length">[[ total.depleted.length ]]</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-popover title="{{ i18n "depletingSoon" }}" :overlay-class-name="siderDrawer.isDarkTheme ? 'ant-dark' : ''">
|
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="siderDrawer.isDarkTheme ? 'ant-dark' : ''">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p v-for="clientEmail in total.expiring">[[ clientEmail ]]</p>
|
<p v-for="clientEmail in total.expiring">[[ clientEmail ]]</p>
|
||||||
</template>
|
</template>
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
<a-button type="primary" icon="export" @click="exportAllLinks">{{ i18n "pages.inbounds.export" }}</a-button>
|
<a-button type="primary" icon="export" @click="exportAllLinks">{{ i18n "pages.inbounds.export" }}</a-button>
|
||||||
<a-button type="primary" icon="reload" @click="resetAllTraffic">{{ i18n "pages.inbounds.resetAllTraffic" }}</a-button>
|
<a-button type="primary" icon="reload" @click="resetAllTraffic">{{ i18n "pages.inbounds.resetAllTraffic" }}</a-button>
|
||||||
</div>
|
</div>
|
||||||
<a-input v-model.lazy="searchKey" placeholder="{{ i18n "search" }}" autofocus style="max-width: 300px"></a-input>
|
<a-input v-model.lazy="searchKey" placeholder='{{ i18n "search" }}' autofocus style="max-width: 300px"></a-input>
|
||||||
<a-table :columns="columns" :row-key="dbInbound => dbInbound.id"
|
<a-table :columns="columns" :row-key="dbInbound => dbInbound.id"
|
||||||
:data-source="searchedInbounds"
|
:data-source="searchedInbounds"
|
||||||
:loading="spinning" :scroll="{ x: 1300 }"
|
:loading="spinning" :scroll="{ x: 1300 }"
|
||||||
@@ -426,7 +426,7 @@
|
|||||||
},
|
},
|
||||||
openCloneInbound(dbInbound) {
|
openCloneInbound(dbInbound) {
|
||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: '{{ i18n "pages.inbounds.cloneInbound"}} ' + dbInbound.remark,
|
title: '{{ i18n "pages.inbounds.cloneInbound"}}' + dbInbound.remark,
|
||||||
content: '{{ i18n "pages.inbounds.cloneInboundContent"}}',
|
content: '{{ i18n "pages.inbounds.cloneInboundContent"}}',
|
||||||
okText: '{{ i18n "pages.inbounds.cloneInboundOk"}}',
|
okText: '{{ i18n "pages.inbounds.cloneInboundOk"}}',
|
||||||
cancelText: '{{ i18n "cancel" }}',
|
cancelText: '{{ i18n "cancel" }}',
|
||||||
@@ -615,22 +615,14 @@
|
|||||||
},
|
},
|
||||||
delClient(dbInboundId,client) {
|
delClient(dbInboundId,client) {
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
newDbInbound = new DBInbound(dbInbound);
|
clientId = dbInbound.protocol == "trojan" ? client.password : client.id;
|
||||||
inbound = newDbInbound.toInbound();
|
|
||||||
clients = this.getClients(dbInbound.protocol, inbound.settings);
|
|
||||||
index = this.findIndexOfClient(clients, client);
|
|
||||||
clients.splice(index, 1);
|
|
||||||
const data = {
|
|
||||||
id: dbInboundId,
|
|
||||||
settings: inbound.settings.toString(),
|
|
||||||
};
|
|
||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: '{{ i18n "pages.inbounds.deleteInbound"}}',
|
title: '{{ i18n "pages.inbounds.deleteInbound"}}',
|
||||||
content: '{{ i18n "pages.inbounds.deleteInboundContent"}}',
|
content: '{{ i18n "pages.inbounds.deleteInboundContent"}}',
|
||||||
class: siderDrawer.isDarkTheme ? darkClass : '',
|
class: siderDrawer.isDarkTheme ? darkClass : '',
|
||||||
okText: '{{ i18n "delete"}}',
|
okText: '{{ i18n "delete"}}',
|
||||||
cancelText: '{{ i18n "cancel"}}',
|
cancelText: '{{ i18n "cancel"}}',
|
||||||
onOk: () => this.submit('/xui/inbound/delClient/' + client.email, data),
|
onOk: () => this.submit(`/xui/inbound/${dbInboundId}/delClient/${clientId}`),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getClients(protocol, clientSettings) {
|
getClients(protocol, clientSettings) {
|
||||||
|
|||||||
@@ -298,9 +298,9 @@
|
|||||||
this.spinning = spinning;
|
this.spinning = spinning;
|
||||||
},
|
},
|
||||||
async getAllSetting() {
|
async getAllSetting() {
|
||||||
this.loading(true,{});
|
this.loading(true);
|
||||||
const msg = await HttpUtil.post("/xui/setting/all");
|
const msg = await HttpUtil.post("/xui/setting/all");
|
||||||
this.loading(false,null);
|
this.loading(false);
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
this.oldAllSetting = new AllSetting(msg.obj);
|
this.oldAllSetting = new AllSetting(msg.obj);
|
||||||
this.allSetting = new AllSetting(msg.obj);
|
this.allSetting = new AllSetting(msg.obj);
|
||||||
@@ -309,17 +309,17 @@
|
|||||||
await this.getUserSecret();
|
await this.getUserSecret();
|
||||||
},
|
},
|
||||||
async updateAllSetting() {
|
async updateAllSetting() {
|
||||||
this.loading(true,{});
|
this.loading(true);
|
||||||
const msg = await HttpUtil.post("/xui/setting/update", this.allSetting);
|
const msg = await HttpUtil.post("/xui/setting/update", this.allSetting);
|
||||||
this.loading(false,null);
|
this.loading(false);
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
await this.getAllSetting();
|
await this.getAllSetting();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async updateUser() {
|
async updateUser() {
|
||||||
this.loading(true,{});
|
this.loading(true);
|
||||||
const msg = await HttpUtil.post("/xui/setting/updateUser", this.user);
|
const msg = await HttpUtil.post("/xui/setting/updateUser", this.user);
|
||||||
this.loading(false,null);
|
this.loading(false);
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
this.user = {};
|
this.user = {};
|
||||||
}
|
}
|
||||||
@@ -334,11 +334,11 @@
|
|||||||
onOk: () => resolve(),
|
onOk: () => resolve(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.loading(true,{});
|
this.loading(true);
|
||||||
const msg = await HttpUtil.post("/xui/setting/restartPanel");
|
const msg = await HttpUtil.post("/xui/setting/restartPanel");
|
||||||
this.loading(false,null);
|
this.loading(false);
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
this.loading(true,{});
|
this.loading(true);
|
||||||
await PromiseUtil.sleep(5000);
|
await PromiseUtil.sleep(5000);
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
@@ -351,16 +351,16 @@
|
|||||||
this.loading(false);
|
this.loading(false);
|
||||||
},
|
},
|
||||||
async updateSecret(){
|
async updateSecret(){
|
||||||
this.loading(true,{});
|
this.loading(true);
|
||||||
const msg = await HttpUtil.post("/xui/setting/updateUserSecret", this.user);
|
const msg = await HttpUtil.post("/xui/setting/updateUserSecret", this.user);
|
||||||
if (msg.success){
|
if (msg.success){
|
||||||
this.user = msg.obj;
|
this.user = msg.obj;
|
||||||
}
|
}
|
||||||
this.loading(false,null);
|
this.loading(false);
|
||||||
await this.updateAllSetting();
|
await this.updateAllSetting();
|
||||||
},
|
},
|
||||||
async getNewSecret(){
|
async getNewSecret(){
|
||||||
this.loading(true,{});
|
this.loading(true);
|
||||||
await PromiseUtil.sleep(1000);
|
await PromiseUtil.sleep(1000);
|
||||||
var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
|
var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
|
||||||
var string = '';
|
var string = '';
|
||||||
@@ -370,7 +370,7 @@
|
|||||||
}
|
}
|
||||||
this.user.loginSecret = string;
|
this.user.loginSecret = string;
|
||||||
document.getElementById('token').value =this.user.loginSecret;
|
document.getElementById('token').value =this.user.loginSecret;
|
||||||
this.loading(false,null);
|
this.loading(false);
|
||||||
},
|
},
|
||||||
async toggleToken(value){
|
async toggleToken(value){
|
||||||
if(value)
|
if(value)
|
||||||
@@ -379,9 +379,9 @@
|
|||||||
this.user.loginSecret = "";
|
this.user.loginSecret = "";
|
||||||
},
|
},
|
||||||
async resetXrayConfigToDefault() {
|
async resetXrayConfigToDefault() {
|
||||||
this.loading(true,{});
|
this.loading(true);
|
||||||
const msg = await HttpUtil.get("/xui/setting/getDefaultJsonConfig");
|
const msg = await HttpUtil.get("/xui/setting/getDefaultJsonConfig");
|
||||||
this.loading(false,null);
|
this.loading(false);
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
this.templateSettings = JSON.parse(JSON.stringify(msg.obj, null, 2));
|
this.templateSettings = JSON.parse(JSON.stringify(msg.obj, null, 2));
|
||||||
this.saveBtnDisable = true;
|
this.saveBtnDisable = true;
|
||||||
|
|||||||
@@ -314,28 +314,56 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) error {
|
|||||||
return db.Save(oldInbound).Error
|
return db.Save(oldInbound).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) DelInboundClient(inbound *model.Inbound, email string) error {
|
func (s *InboundService) DelInboundClient(inboundId int, clientId string) error {
|
||||||
db := database.GetDB()
|
oldInbound, err := s.GetInbound(inboundId)
|
||||||
err := s.DelClientStat(db, email)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("Delete stats Data Error")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
oldInbound, err := s.GetInbound(inbound.Id)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Load Old Data Error")
|
logger.Error("Load Old Data Error")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
var settings map[string]interface{}
|
||||||
|
err = json.Unmarshal([]byte(oldInbound.Settings), &settings)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
oldInbound.Settings = inbound.Settings
|
email := ""
|
||||||
|
client_key := "id"
|
||||||
|
if oldInbound.Protocol == "trojan" {
|
||||||
|
client_key = "password"
|
||||||
|
}
|
||||||
|
|
||||||
|
inerfaceClients := settings["clients"].([]interface{})
|
||||||
|
var newClients []interface{}
|
||||||
|
for _, client := range inerfaceClients {
|
||||||
|
c := client.(map[string]interface{})
|
||||||
|
c_id := c[client_key].(string)
|
||||||
|
if c_id == clientId {
|
||||||
|
email = c["email"].(string)
|
||||||
|
} else {
|
||||||
|
newClients = append(newClients, client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settings["clients"] = newClients
|
||||||
|
newSettings, err := json.MarshalIndent(settings, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
oldInbound.Settings = string(newSettings)
|
||||||
|
|
||||||
|
db := database.GetDB()
|
||||||
|
err = s.DelClientStat(db, email)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Delete stats Data Error")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
err = s.DelClientIPs(db, email)
|
err = s.DelClientIPs(db, email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Error in delete client IPs")
|
logger.Error("Error in delete client IPs")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.Save(oldInbound).Error
|
return db.Save(oldInbound).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,45 +446,35 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, index int) err
|
|||||||
return db.Save(oldInbound).Error
|
return db.Save(oldInbound).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) AddTraffic(traffics []*xray.Traffic) (err error) {
|
func (s *InboundService) AddTraffic(traffics []*xray.Traffic) error {
|
||||||
if len(traffics) == 0 {
|
if len(traffics) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
db := database.GetDB()
|
// Update traffics in a single transaction
|
||||||
db = db.Model(model.Inbound{})
|
err := database.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
tx := db.Begin()
|
for _, traffic := range traffics {
|
||||||
defer func() {
|
if traffic.IsInbound {
|
||||||
if err != nil {
|
update := tx.Model(&model.Inbound{}).Where("tag = ?", traffic.Tag).
|
||||||
tx.Rollback()
|
Updates(map[string]interface{}{
|
||||||
} else {
|
"up": gorm.Expr("up + ?", traffic.Up),
|
||||||
tx.Commit()
|
"down": gorm.Expr("down + ?", traffic.Down),
|
||||||
}
|
})
|
||||||
}()
|
if update.Error != nil {
|
||||||
for _, traffic := range traffics {
|
return update.Error
|
||||||
if traffic.IsInbound {
|
}
|
||||||
err = tx.Where("tag = ?", traffic.Tag).
|
|
||||||
UpdateColumns(map[string]interface{}{
|
|
||||||
"up": gorm.Expr("up + ?", traffic.Up),
|
|
||||||
"down": gorm.Expr("down + ?", traffic.Down)}).Error
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
return
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err error) {
|
func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err error) {
|
||||||
if len(traffics) == 0 {
|
if len(traffics) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
traffics, err = s.adjustTraffics(traffics)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
db = db.Model(xray.ClientTraffic{})
|
|
||||||
tx := db.Begin()
|
tx := db.Begin()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -467,7 +485,32 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = tx.Save(traffics).Error
|
emails := make([]string, 0, len(traffics))
|
||||||
|
for _, traffic := range traffics {
|
||||||
|
emails = append(emails, traffic.Email)
|
||||||
|
}
|
||||||
|
dbClientTraffics := make([]*xray.ClientTraffic, 0, len(traffics))
|
||||||
|
err = db.Model(xray.ClientTraffic{}).Where("email IN (?)", emails).Find(&dbClientTraffics).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dbClientTraffics, err = s.adjustTraffics(tx, dbClientTraffics)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for dbTraffic_index := range dbClientTraffics {
|
||||||
|
for traffic_index := range traffics {
|
||||||
|
if dbClientTraffics[dbTraffic_index].Email == traffics[traffic_index].Email {
|
||||||
|
dbClientTraffics[dbTraffic_index].Up += traffics[traffic_index].Up
|
||||||
|
dbClientTraffics[dbTraffic_index].Down += traffics[traffic_index].Down
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Save(dbClientTraffics).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning("AddClientTraffic update data ", err)
|
logger.Warning("AddClientTraffic update data ", err)
|
||||||
}
|
}
|
||||||
@@ -475,81 +518,56 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) adjustTraffics(traffics []*xray.ClientTraffic) (full_traffics []*xray.ClientTraffic, err error) {
|
func (s *InboundService) adjustTraffics(tx *gorm.DB, dbClientTraffics []*xray.ClientTraffic) ([]*xray.ClientTraffic, error) {
|
||||||
db := database.GetDB()
|
inboundIds := make([]int, 0, len(dbClientTraffics))
|
||||||
dbInbound := db.Model(model.Inbound{})
|
for _, dbClientTraffic := range dbClientTraffics {
|
||||||
txInbound := dbInbound.Begin()
|
if dbClientTraffic.ExpiryTime < 0 {
|
||||||
|
inboundIds = append(inboundIds, dbClientTraffic.InboundId)
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
txInbound.Rollback()
|
|
||||||
} else {
|
|
||||||
txInbound.Commit()
|
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
for _, traffic := range traffics {
|
|
||||||
inbound := &model.Inbound{}
|
|
||||||
client_traffic := &xray.ClientTraffic{}
|
|
||||||
err := db.Model(xray.ClientTraffic{}).Where("email = ?", traffic.Email).First(client_traffic).Error
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
logger.Warning(err, traffic.Email)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
client_traffic.Up += traffic.Up
|
|
||||||
client_traffic.Down += traffic.Down
|
|
||||||
|
|
||||||
err = txInbound.Where("id=?", client_traffic.InboundId).First(inbound).Error
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
logger.Warning(err, traffic.Email)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// get clients
|
|
||||||
clients, err := s.getClients(inbound)
|
|
||||||
needUpdate := false
|
|
||||||
if err == nil {
|
|
||||||
for client_index, client := range clients {
|
|
||||||
if traffic.Email == client.Email {
|
|
||||||
if client.ExpiryTime < 0 {
|
|
||||||
clients[client_index].ExpiryTime = (time.Now().Unix() * 1000) - client.ExpiryTime
|
|
||||||
needUpdate = true
|
|
||||||
}
|
|
||||||
client_traffic.ExpiryTime = client.ExpiryTime
|
|
||||||
client_traffic.Total = client.TotalGB
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if needUpdate {
|
|
||||||
settings := map[string]interface{}{}
|
|
||||||
json.Unmarshal([]byte(inbound.Settings), &settings)
|
|
||||||
|
|
||||||
// Convert clients to []interface to update clients in settings
|
|
||||||
var clientsInterface []interface{}
|
|
||||||
for _, c := range clients {
|
|
||||||
clientsInterface = append(clientsInterface, interface{}(c))
|
|
||||||
}
|
|
||||||
|
|
||||||
settings["clients"] = clientsInterface
|
|
||||||
modifiedSettings, err := json.MarshalIndent(settings, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = txInbound.Where("id=?", inbound.Id).Update("settings", string(modifiedSettings)).Error
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
full_traffics = append(full_traffics, client_traffic)
|
|
||||||
}
|
}
|
||||||
return full_traffics, nil
|
|
||||||
|
if len(inboundIds) > 0 {
|
||||||
|
var inbounds []*model.Inbound
|
||||||
|
err := tx.Model(model.Inbound{}).Where("id IN (?)", inboundIds).Find(&inbounds).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for inbound_index := range inbounds {
|
||||||
|
settings := map[string]interface{}{}
|
||||||
|
json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings)
|
||||||
|
clients, ok := settings["clients"].([]interface{})
|
||||||
|
if ok {
|
||||||
|
var newClients []interface{}
|
||||||
|
for client_index := range clients {
|
||||||
|
c := clients[client_index].(map[string]interface{})
|
||||||
|
for traffic_index := range dbClientTraffics {
|
||||||
|
if c["email"] == dbClientTraffics[traffic_index].Email {
|
||||||
|
oldExpiryTime := c["expiryTime"].(float64)
|
||||||
|
newExpiryTime := (time.Now().Unix() * 1000) - int64(oldExpiryTime)
|
||||||
|
c["expiryTime"] = newExpiryTime
|
||||||
|
dbClientTraffics[traffic_index].ExpiryTime = newExpiryTime
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newClients = append(newClients, interface{}(c))
|
||||||
|
}
|
||||||
|
settings["clients"] = newClients
|
||||||
|
modifiedSettings, err := json.MarshalIndent(settings, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
inbounds[inbound_index].Settings = string(modifiedSettings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = tx.Save(inbounds).Error
|
||||||
|
if err != nil {
|
||||||
|
logger.Warning("AddClientTraffic update inbounds ", err)
|
||||||
|
logger.Error(inbounds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbClientTraffics, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) DisableInvalidInbounds() (int64, error) {
|
func (s *InboundService) DisableInvalidInbounds() (int64, error) {
|
||||||
@@ -791,3 +809,43 @@ func (s *InboundService) SearchInbounds(query string) ([]*model.Inbound, error)
|
|||||||
}
|
}
|
||||||
return inbounds, nil
|
return inbounds, nil
|
||||||
}
|
}
|
||||||
|
func (s *InboundService) MigrationRequirements() {
|
||||||
|
db := database.GetDB()
|
||||||
|
var inbounds []*model.Inbound
|
||||||
|
err := db.Model(model.Inbound{}).Where("protocol IN (?)", []string{"vmess", "vless", "trojan"}).Find(&inbounds).Error
|
||||||
|
if err != nil && err != gorm.ErrRecordNotFound {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for inbound_index := range inbounds {
|
||||||
|
settings := map[string]interface{}{}
|
||||||
|
json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings)
|
||||||
|
clients, ok := settings["clients"].([]interface{})
|
||||||
|
if ok {
|
||||||
|
var newClients []interface{}
|
||||||
|
for client_index := range clients {
|
||||||
|
c := clients[client_index].(map[string]interface{})
|
||||||
|
|
||||||
|
// Add email='' if it is not exists
|
||||||
|
if _, ok := c["email"]; !ok {
|
||||||
|
c["email"] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove "flow": "xtls-rprx-direct"
|
||||||
|
if _, ok := c["flow"]; ok {
|
||||||
|
if c["flow"] == "xtls-rprx-direct" {
|
||||||
|
c["flow"] = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newClients = append(newClients, interface{}(c))
|
||||||
|
}
|
||||||
|
settings["clients"] = newClients
|
||||||
|
modifiedSettings, err := json.MarshalIndent(settings, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
inbounds[inbound_index].Settings = string(modifiedSettings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db.Save(inbounds)
|
||||||
|
}
|
||||||
|
|||||||
@@ -352,9 +352,6 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
|||||||
|
|
||||||
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
||||||
if xtlsSetting != nil {
|
if xtlsSetting != nil {
|
||||||
if sniValue, ok := searchKey(xtlsSettings, "serverName"); ok {
|
|
||||||
params["sni"], _ = sniValue.(string)
|
|
||||||
}
|
|
||||||
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
|
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
|
||||||
params["fp"], _ = fpValue.(string)
|
params["fp"], _ = fpValue.(string)
|
||||||
}
|
}
|
||||||
@@ -529,9 +526,6 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
|||||||
|
|
||||||
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
||||||
if xtlsSetting != nil {
|
if xtlsSetting != nil {
|
||||||
if sniValue, ok := searchKey(xtlsSettings, "serverName"); ok {
|
|
||||||
params["sni"], _ = sniValue.(string)
|
|
||||||
}
|
|
||||||
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
|
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
|
||||||
params["fp"], _ = fpValue.(string)
|
params["fp"], _ = fpValue.(string)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.inboundService.DisableInvalidClients()
|
s.inboundService.DisableInvalidClients()
|
||||||
s.inboundService.RemoveOrphanedTraffics()
|
|
||||||
|
|
||||||
inbounds, err := s.inboundService.GetAllInbounds()
|
inbounds, err := s.inboundService.GetAllInbounds()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -124,7 +123,7 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
settings["clients"] = final_clients
|
settings["clients"] = final_clients
|
||||||
modifiedSettings, err := json.Marshal(settings)
|
modifiedSettings, err := json.MarshalIndent(settings, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
61
x-ui.sh
61
x-ui.sh
@@ -56,14 +56,6 @@ elif [[ "${release}" == "debian" ]]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
arch3xui() {
|
|
||||||
case "$(uname -m)" in
|
|
||||||
x86_64 | x64 | amd64 ) echo 'amd64' ;;
|
|
||||||
armv8 | arm64 | aarch64 ) echo 'arm64' ;;
|
|
||||||
* ) echo -e "${red} Unsupported CPU architecture!${plain}" && exit 1 ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
confirm() {
|
confirm() {
|
||||||
if [[ $# > 1 ]]; then
|
if [[ $# > 1 ]]; then
|
||||||
echo && read -p "$1 [Default $2]: " temp
|
echo && read -p "$1 [Default $2]: " temp
|
||||||
@@ -106,49 +98,18 @@ install() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
read -rp "This function will update the X-UI panel to the latest version. Data will not be lost. Whether to continues? [Y/N]: " yn
|
confirm "This function will forcefully reinstall the latest version, and the data will not be lost. Do you want to continue?" "n"
|
||||||
if [[ $yn =~ "Y"|"y" ]]; then
|
if [[ $? != 0 ]]; then
|
||||||
systemctl stop x-ui
|
LOGE "Cancelled"
|
||||||
if [[ -e /usr/local/x-ui/ ]]; then
|
if [[ $# == 0 ]]; then
|
||||||
cd
|
before_show_menu
|
||||||
rm -rf /usr/local/x-ui/
|
|
||||||
fi
|
fi
|
||||||
|
return 0
|
||||||
last_version=$(curl -Ls "https://api.github.com/repos/MHSanaei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') || last_version=$(curl -sm8 https://raw.githubusercontent.com/MHSanaei/3x-ui/main/config/version)
|
fi
|
||||||
if [[ -z "$last_version" ]]; then
|
bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/main/install.sh)
|
||||||
echo -e "${red}Detecting the X-UI version failed, please make sure your server can connect to the GitHub API ${plain}"
|
if [[ $? == 0 ]]; then
|
||||||
exit 1
|
LOGI "Update is complete, Panel has automatically restarted "
|
||||||
fi
|
exit 0
|
||||||
|
|
||||||
echo -e "${yellow}The latest version of X-UI is: ${last_version}, starting update...${plain}"
|
|
||||||
wget -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch3xui).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${last_version}/x-ui-linux-$(arch3xui).tar.gz
|
|
||||||
if [[ $? -ne 0 ]]; then
|
|
||||||
echo -e "${red}Download the X-UI failure, please make sure your server can connect and download the files from github ${plain}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd /usr/local/
|
|
||||||
tar zxvf x-ui-linux-$(arch3xui).tar.gz
|
|
||||||
rm -f x-ui-linux-$(arch3xui).tar.gz
|
|
||||||
|
|
||||||
cd x-ui
|
|
||||||
chmod +x x-ui bin/xray-linux-$(arch3xui)
|
|
||||||
cp -f x-ui.service /etc/systemd/system/
|
|
||||||
|
|
||||||
wget -N --no-check-certificate https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh -O /usr/bin/x-ui
|
|
||||||
chmod +x /usr/local/x-ui/x-ui.sh
|
|
||||||
chmod +x /usr/bin/x-ui
|
|
||||||
|
|
||||||
systemctl daemon-reload
|
|
||||||
systemctl enable x-ui >/dev/null 2>&1
|
|
||||||
systemctl start x-ui
|
|
||||||
systemctl restart x-ui
|
|
||||||
|
|
||||||
echo -e "${green}The update is completed, and the X-UI panel has been automatically restarted ${plain}"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo -e "${red}The upgrade X-UI panel has been canceled! ${plain}"
|
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user