Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
authorMHSanaei <33454419+MHSanaei@users.noreply.github.com>2023-03-24 16:44:26 +0300
committerMHSanaei <33454419+MHSanaei@users.noreply.github.com>2023-03-24 16:44:26 +0300
commit9c0718bc44037d6415bcf3bcb300de62c67ad782 (patch)
treec5abba4c47baa60a3e5c1274b211e92bde7090ae /web
parent826c7264b5cbea4e4149645bef220fd3c6a010e7 (diff)
Revert "Add version and log"
This reverts commit 826c7264b5cbea4e4149645bef220fd3c6a010e7.
Diffstat (limited to 'web')
-rw-r--r--web/controller/server.go21
-rw-r--r--web/html/xui/index.html47
-rw-r--r--web/html/xui/setting.html4
-rw-r--r--web/service/inbound.go10
-rw-r--r--web/service/server.go47
-rw-r--r--web/service/tgbot.go60
6 files changed, 158 insertions, 31 deletions
diff --git a/web/controller/server.go b/web/controller/server.go
index 673a96d8..7b7239d5 100644
--- a/web/controller/server.go
+++ b/web/controller/server.go
@@ -1,10 +1,11 @@
package controller
import (
- "github.com/gin-gonic/gin"
"time"
"x-ui/web/global"
"x-ui/web/service"
+
+ "github.com/gin-gonic/gin"
)
type ServerController struct {
@@ -37,6 +38,7 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) {
g.POST("/stopXrayService", a.stopXrayService)
g.POST("/restartXrayService", a.restartXrayService)
g.POST("/installXray/:version", a.installXray)
+ g.POST("/logs", a.getLogs)
}
func (a *ServerController) refreshStatus() {
@@ -87,13 +89,13 @@ func (a *ServerController) installXray(c *gin.Context) {
}
func (a *ServerController) stopXrayService(c *gin.Context) {
- a.lastGetStatusTime = time.Now()
+ a.lastGetStatusTime = time.Now()
err := a.serverService.StopXrayService()
if err != nil {
jsonMsg(c, "", err)
return
}
- jsonMsg(c, "Xray stoped",err)
+ jsonMsg(c, "Xray stoped", err)
}
func (a *ServerController) restartXrayService(c *gin.Context) {
@@ -102,6 +104,15 @@ func (a *ServerController) restartXrayService(c *gin.Context) {
jsonMsg(c, "", err)
return
}
- jsonMsg(c, "Xray restarted",err)
+ jsonMsg(c, "Xray restarted", err)
+
+}
-} \ No newline at end of file
+func (a *ServerController) getLogs(c *gin.Context) {
+ logs, err := a.serverService.GetLogs()
+ if err != nil {
+ jsonMsg(c, I18n(c, "getLogs"), err)
+ return
+ }
+ jsonObj(c, logs, nil)
+}
diff --git a/web/html/xui/index.html b/web/html/xui/index.html
index b8a1e4b5..713d5b7e 100644
--- a/web/html/xui/index.html
+++ b/web/html/xui/index.html
@@ -84,16 +84,18 @@
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
- <a-tag color="green" @click="openSelectV2rayVersion">[[ status.xray.version ]]</a-tag>
- <a-tag color="blue" @click="stopXrayService">{{ i18n "pages.index.stopXray" }}</a-tag>
- <a-tag color="blue" @click="restartXrayService">{{ i18n "pages.index.restartXray" }}</a-tag>
- <a-tag color="blue" @click="openSelectV2rayVersion">{{ i18n "pages.index.xraySwitch" }}</a-tag>
+ <a-tag color="green" style="cursor: pointer;" @click="openSelectV2rayVersion">[[ status.xray.version ]]</a-tag>
+ <a-tag color="blue" style="cursor: pointer;" @click="stopXrayService">{{ i18n "pages.index.stopXray" }}</a-tag>
+ <a-tag color="blue" style="cursor: pointer;" @click="restartXrayService">{{ i18n "pages.index.restartXray" }}</a-tag>
+ <a-tag color="blue" style="cursor: pointer;" @click="openSelectV2rayVersion">{{ i18n "pages.index.xraySwitch" }}</a-tag>
</a-card>
</a-col>
<a-col :sm="24" :md="12">
<a-card hoverable :class="siderDrawer.isDarkTheme ? darkClass : ''">
+ x-ui: <a-tag color="green">{{ .cur_ver }}</a-tag>
+ <a-tag color="blue" style="cursor: pointer;" @click="openLogs">Logs</a-tag>
{{ i18n "pages.index.operationHours" }}:
- <a-tag color="#87d068">[[ formatSecond(status.uptime) ]]</a-tag>
+ <a-tag color="green">[[ formatSecond(status.uptime) ]]</a-tag>
<a-tooltip>
<template slot="title">
{{ i18n "pages.index.operationHoursDesc" }}
@@ -177,7 +179,7 @@
<a-modal id="version-modal" v-model="versionModal.visible" title='{{ i18n "pages.index.xraySwitch" }}'
:closable="true" @ok="() => versionModal.visible = false"
:class="siderDrawer.isDarkTheme ? darkClass : ''"
- ok-text='{{ i18n "confirm" }}' cancel-text='{{ i18n "cancel"}}'>
+ footer="">
<h2>{{ i18n "pages.index.xraySwitchClick"}}</h2>
<h2>{{ i18n "pages.index.xraySwitchClickDesk"}}</h2>
<template v-for="version, index in versionModal.versions">
@@ -187,6 +189,17 @@
</a-tag>
</template>
</a-modal>
+ <a-modal id="log-modal" v-model="logModal.visible" title="X-UI logs"
+ :closable="true" @ok="() => logModal.visible = false" @cancel="() => logModal.visible = false"
+ :class="siderDrawer.isDarkTheme ? darkClass : ''"
+ width="800px"
+ footer="">
+ <table style="margin: 0px; width: 100%; background-color: black; color: hsla(0,0%,100%,.65);">
+ <tr v-for="log , index in logModal.logs">
+ <td style="vertical-align: top;">[[ index ]]</td><td>[[ log ]]</td>
+ </tr>
+ </table>
+ </a-modal>
</a-layout>
{{template "js" .}}
<script>
@@ -280,6 +293,18 @@
},
};
+ const logModal = {
+ visible: false,
+ logs: '',
+ show(logs) {
+ this.visible = true;
+ this.logs = logs;
+ },
+ hide() {
+ this.visible = false;
+ },
+ };
+
const app = new Vue({
delimiters: ['[[', ']]'],
el: '#app',
@@ -287,6 +312,7 @@
siderDrawer,
status: new Status(),
versionModal,
+ logModal,
spinning: false,
loadingTip: '{{ i18n "loading"}}',
},
@@ -346,6 +372,15 @@
return;
}
},
+ async openLogs(){
+ this.loading(true);
+ const msg = await HttpUtil.post('server/logs');
+ this.loading(false);
+ if (!msg.success) {
+ return;
+ }
+ logModal.show(msg.obj);
+ }
},
async mounted() {
while (true) {
diff --git a/web/html/xui/setting.html b/web/html/xui/setting.html
index f8f873e6..057dae95 100644
--- a/web/html/xui/setting.html
+++ b/web/html/xui/setting.html
@@ -40,7 +40,7 @@
<a-list item-layout="horizontal" :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65);': 'background: white;'">
<setting-list-item type="text" title='{{ i18n "pages.setting.panelListeningIP"}}' desc='{{ i18n "pages.setting.panelListeningIPDesc"}}' v-model="allSetting.webListen"></setting-list-item>
- <setting-list-item type="text" title='{{ i18n "pages.setting.panelPort"}}' desc='{{ i18n "pages.setting.panelPortDesc"}}' v-model.number="allSetting.webPort"></setting-list-item>
+ <setting-list-item type="number" title='{{ i18n "pages.setting.panelPort"}}' desc='{{ i18n "pages.setting.panelPortDesc"}}' v-model.number="allSetting.webPort"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.setting.publicKeyPath"}}' desc='{{ i18n "pages.setting.publicKeyPathDesc"}}' v-model="allSetting.webCertFile"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.setting.privateKeyPath"}}' desc='{{ i18n "pages.setting.privateKeyPathDesc"}}' v-model="allSetting.webKeyFile"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.setting.panelUrlPath"}}' desc='{{ i18n "pages.setting.panelUrlPathDesc"}}' v-model="allSetting.webBasePath"></setting-list-item>
@@ -117,7 +117,7 @@
<a-list item-layout="horizontal" :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65);': 'background: white;'">
<setting-list-item type="switch" title='{{ i18n "pages.setting.telegramBotEnable" }}' desc='{{ i18n "pages.setting.telegramBotEnableDesc" }}' v-model="allSetting.tgBotEnable"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.setting.telegramToken"}}' desc='{{ i18n "pages.setting.telegramTokenDesc"}}' v-model="allSetting.tgBotToken"></setting-list-item>
- <setting-list-item type="number" title='{{ i18n "pages.setting.telegramChatId"}}' desc='{{ i18n "pages.setting.telegramChatIdDesc"}}' v-model="allSetting.tgBotChatId"></setting-list-item>
+ <setting-list-item type="text" title='{{ i18n "pages.setting.telegramChatId"}}' desc='{{ i18n "pages.setting.telegramChatIdDesc"}}' v-model="allSetting.tgBotChatId"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.setting.telegramNotifyTime"}}' desc='{{ i18n "pages.setting.telegramNotifyTimeDesc"}}' v-model="allSetting.tgRunTime"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.setting.tgNotifyBackup" }}' desc='{{ i18n "pages.setting.tgNotifyBackupDesc" }}' v-model="allSetting.tgBotBackup"></setting-list-item>
<setting-list-item type="number" title='{{ i18n "pages.setting.tgNotifyExpireTimeDiff" }}' desc='{{ i18n "pages.setting.tgNotifyExpireTimeDiffDesc" }}' v-model="allSetting.tgExpireDiff" :min="0"></setting-list-item>
diff --git a/web/service/inbound.go b/web/service/inbound.go
index b6f4f031..9814b549 100644
--- a/web/service/inbound.go
+++ b/web/service/inbound.go
@@ -634,3 +634,13 @@ func (s *InboundService) ClearClientIps(clientEmail string) error {
}
return nil
}
+
+func (s *InboundService) SearchInbounds(query string) ([]*model.Inbound, error) {
+ db := database.GetDB()
+ var inbounds []*model.Inbound
+ err := db.Model(model.Inbound{}).Preload("ClientStats").Where("remark like ?", "%"+query+"%").Find(&inbounds).Error
+ if err != nil && err != gorm.ErrRecordNotFound {
+ return nil, err
+ }
+ return inbounds, nil
+} \ No newline at end of file
diff --git a/web/service/server.go b/web/service/server.go
index 6737bef2..5e6065b5 100644
--- a/web/service/server.go
+++ b/web/service/server.go
@@ -9,7 +9,9 @@ import (
"io/fs"
"net/http"
"os"
+ "os/exec"
"runtime"
+ "strings"
"time"
"x-ui/logger"
"x-ui/util/sys"
@@ -200,24 +202,24 @@ func (s *ServerService) GetXrayVersions() ([]string, error) {
func (s *ServerService) StopXrayService() (string error) {
- err := s.xrayService.StopXray()
- if err != nil {
- logger.Error("stop xray failed:", err)
- return err
- }
+ err := s.xrayService.StopXray()
+ if err != nil {
+ logger.Error("stop xray failed:", err)
+ return err
+ }
return nil
}
func (s *ServerService) RestartXrayService() (string error) {
- s.xrayService.StopXray()
- defer func() {
- err := s.xrayService.RestartXray(true)
- if err != nil {
- logger.Error("start xray failed:", err)
+ s.xrayService.StopXray()
+ defer func() {
+ err := s.xrayService.RestartXray(true)
+ if err != nil {
+ logger.Error("start xray failed:", err)
}
- }()
+ }()
return nil
}
@@ -324,3 +326,26 @@ func (s *ServerService) UpdateXray(version string) error {
return nil
}
+
+func (s *ServerService) GetLogs() ([]string, error) {
+ // Define the journalctl command and its arguments
+ var cmdArgs []string
+ if runtime.GOOS == "linux" {
+ cmdArgs = []string{"journalctl", "-u", "x-ui", "--no-pager", "-n", "100"}
+ } else {
+ return []string{"Unsupported operating system"}, nil
+ }
+
+ // Run the command
+ cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
+ var out bytes.Buffer
+ cmd.Stdout = &out
+ err := cmd.Run()
+ if err != nil {
+ return nil, err
+ }
+
+ lines := strings.Split(out.String(), "\n")
+
+ return lines, nil
+}
diff --git a/web/service/tgbot.go b/web/service/tgbot.go
index e88edbdf..86097b0f 100644
--- a/web/service/tgbot.go
+++ b/web/service/tgbot.go
@@ -105,8 +105,6 @@ func (t *Tgbot) OnReceive() {
} else {
if update.Message.IsCommand() {
t.answerCommand(update.Message, chatId, isAdmin)
- } else {
- t.aswerChat(update.Message.Text, chatId, isAdmin)
}
}
}
@@ -137,16 +135,18 @@ func (t *Tgbot) answerCommand(message *tgbotapi.Message, chatId int64, isAdmin b
} else {
msg = "❗Please provide a text for search!"
}
+ case "inbound":
+ if isAdmin {
+ t.searchInbound(chatId, message.CommandArguments())
+ } else {
+ msg = "❗ Unknown command"
+ }
default:
msg = "❗ Unknown command"
}
t.SendAnswer(chatId, msg, isAdmin)
}
-func (t *Tgbot) aswerChat(message string, chatId int64, isAdmin bool) {
- t.SendAnswer(chatId, "❗ Unknown message", isAdmin)
-}
-
func (t *Tgbot) asnwerCallback(callbackQuery *tgbotapi.CallbackQuery, isAdmin bool) {
// Respond to the callback query, telling Telegram to show the user
// a message with the data received.
@@ -169,7 +169,7 @@ func (t *Tgbot) asnwerCallback(callbackQuery *tgbotapi.CallbackQuery, isAdmin bo
case "client_commands":
t.SendMsgToTgbot(callbackQuery.From.ID, "To search for statistics, just use folowing command:\r\n \r\n<code>/usage [UID|Passowrd]</code>\r\n \r\nUse UID for vmess and vless and Password for Trojan.")
case "commands":
- t.SendMsgToTgbot(callbackQuery.From.ID, "To search for a client email, just use folowing command:\r\n \r\n<code>/usage email</code>")
+ t.SendMsgToTgbot(callbackQuery.From.ID, "Search for a client email:\r\n<code>/usage email</code>\r\n \r\nSearch for inbounds (with client stats):\r\n<code>/inbound [remark]</code>")
}
}
@@ -276,6 +276,7 @@ func (t *Tgbot) getServerUsage() string {
name = ""
}
info = fmt.Sprintf("💻 Hostname: %s\r\n", name)
+ info += fmt.Sprintf("🚀X-UI Version: %s\r\n", config.GetVersion())
//get ip address
var ip string
var ipv6 string
@@ -427,6 +428,45 @@ func (t *Tgbot) searchClient(chatId int64, email string) {
}
}
+func (t *Tgbot) searchInbound(chatId int64, remark string) {
+ inbouds, err := t.inboundService.SearchInbounds(remark)
+ if err != nil {
+ logger.Warning(err)
+ msg := "❌ Something went wrong!"
+ t.SendMsgToTgbot(chatId, msg)
+ return
+ }
+ for _, inbound := range inbouds {
+ info := ""
+ info += fmt.Sprintf("📍Inbound:%s\r\nPort:%d\r\n", inbound.Remark, inbound.Port)
+ info += fmt.Sprintf("Traffic: %s (↑%s,↓%s)\r\n", common.FormatTraffic((inbound.Up + inbound.Down)), common.FormatTraffic(inbound.Up), common.FormatTraffic(inbound.Down))
+ if inbound.ExpiryTime == 0 {
+ info += "Expire date: ♾ Unlimited\r\n \r\n"
+ } else {
+ info += fmt.Sprintf("Expire date:%s\r\n \r\n", time.Unix((inbound.ExpiryTime/1000), 0).Format("2006-01-02 15:04:05"))
+ }
+ t.SendMsgToTgbot(chatId, info)
+ for _, traffic := range inbound.ClientStats {
+ expiryTime := ""
+ if traffic.ExpiryTime == 0 {
+ expiryTime = "♾Unlimited"
+ } else {
+ expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
+ }
+ total := ""
+ if traffic.Total == 0 {
+ total = "♾Unlimited"
+ } else {
+ total = common.FormatTraffic((traffic.Total))
+ }
+ output := fmt.Sprintf("💡 Active: %t\r\n📧 Email: %s\r\n🔼 Upload↑: %s\r\n🔽 Download↓: %s\r\n🔄 Total: %s / %s\r\n📅 Expire in: %s\r\n",
+ traffic.Enable, traffic.Email, common.FormatTraffic(traffic.Up), common.FormatTraffic(traffic.Down), common.FormatTraffic((traffic.Up + traffic.Down)),
+ total, expiryTime)
+ t.SendMsgToTgbot(chatId, output)
+ }
+ }
+}
+
func (t *Tgbot) searchForClient(chatId int64, query string) {
traffic, err := t.inboundService.SearchClientTraffic(query)
if err != nil {
@@ -547,4 +587,10 @@ func (t *Tgbot) sendBackup(chatId int64) {
if err != nil {
logger.Warning("Error in uploading backup: ", err)
}
+ file = tgbotapi.FilePath(xray.GetConfigPath())
+ msg = tgbotapi.NewDocument(chatId, file)
+ _, err = bot.Send(msg)
+ if err != nil {
+ logger.Warning("Error in uploading config.json: ", err)
+ }
}