mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-08-23 03:16:52 +00:00
added xray access log viewer
This commit is contained in:
parent
3c1634ca7c
commit
8409fdd24f
3 changed files with 141 additions and 1 deletions
|
@ -46,6 +46,7 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) {
|
|||
g.POST("/installXray/:version", a.installXray)
|
||||
g.POST("/updateGeofile/:fileName", a.updateGeofile)
|
||||
g.POST("/logs/:count", a.getLogs)
|
||||
g.POST("/xraylogs/:count", a.getXrayLogs)
|
||||
g.POST("/getConfigJson", a.getConfigJson)
|
||||
g.GET("/getDb", a.getDb)
|
||||
g.POST("/importDB", a.importDB)
|
||||
|
@ -133,6 +134,12 @@ func (a *ServerController) getLogs(c *gin.Context) {
|
|||
jsonObj(c, logs, nil)
|
||||
}
|
||||
|
||||
func (a *ServerController) getXrayLogs(c *gin.Context) {
|
||||
count := c.Param("count")
|
||||
logs := a.serverService.GetXrayLogs(count)
|
||||
jsonObj(c, logs, nil)
|
||||
}
|
||||
|
||||
func (a *ServerController) getConfigJson(c *gin.Context) {
|
||||
configJson, err := a.serverService.GetConfigJson()
|
||||
if err != nil {
|
||||
|
|
|
@ -167,7 +167,10 @@
|
|||
<span>{{ i18n "pages.index.xrayErrorPopoverTitle" }}</span>
|
||||
</a-col>
|
||||
<a-col>
|
||||
<a-icon type="bars" :style="{ cursor: 'pointer', float: 'right' }" @click="openLogs()"></a-tag>
|
||||
<a-icon type="bars" :style="{ cursor: 'pointer', float: 'right' }" @click="openLogs()"></a-icon>
|
||||
</a-col>
|
||||
<a-col>
|
||||
<a-icon type="bars" :style="{ cursor: 'pointer', float: 'right' }" @click="openXrayLogs()"></a-icon>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</span>
|
||||
|
@ -179,6 +182,10 @@
|
|||
</template>
|
||||
</template>
|
||||
<template #actions>
|
||||
<a-space direction="horizontal" @click="openXrayLogs()" :style="{ justifyContent: 'center' }">
|
||||
<a-icon type="bars"></a-icon>
|
||||
<span v-if="!isMobile">{{ i18n "pages.index.logs" }}</span>
|
||||
</a-space>
|
||||
<a-space direction="horizontal" @click="stopXrayService" :style="{ justifyContent: 'center' }">
|
||||
<a-icon type="poweroff"></a-icon>
|
||||
<span v-if="!isMobile">{{ i18n "pages.index.stopXray" }}</span>
|
||||
|
@ -422,6 +429,38 @@
|
|||
</a-form>
|
||||
<div class="ant-input" :style="{ height: 'auto', maxHeight: '500px', overflow: 'auto', marginTop: '0.5rem' }" v-html="logModal.formattedLogs"></div>
|
||||
</a-modal>
|
||||
<a-modal id="xraylog-modal" v-model="xraylogModal.visible"
|
||||
:closable="true" @cancel="() => xraylogModal.visible = false"
|
||||
:class="themeSwitcher.currentTheme"
|
||||
width="1200px" footer="">
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.logs" }}
|
||||
<a-icon :spin="xraylogModal.loading"
|
||||
type="sync"
|
||||
:style="{ verticalAlign: 'middle', marginLeft: '10px' }"
|
||||
:disabled="xraylogModal.loading"
|
||||
@click="openXrayLogs()">
|
||||
</a-icon>
|
||||
</template>
|
||||
<a-form layout="inline">
|
||||
<a-form-item :style="{ marginRight: '0.5rem' }">
|
||||
<a-input-group compact>
|
||||
<a-select size="small" v-model="xraylogModal.rows" :style="{ width: '70px' }"
|
||||
@change="openXrayLogs()" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="10">10</a-select-option>
|
||||
<a-select-option value="20">20</a-select-option>
|
||||
<a-select-option value="50">50</a-select-option>
|
||||
<a-select-option value="100">100</a-select-option>
|
||||
<a-select-option value="500">500</a-select-option>
|
||||
</a-select>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
<a-form-item :style="{ float: 'right' }">
|
||||
<a-button type="primary" icon="download" @click="FileManager.downloadTextFile(xraylogModal.logs?.join('\n'), 'x-ui.log')"></a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<div class="ant-input" :style="{ height: 'auto', maxHeight: '500px', overflow: 'auto', marginTop: '0.5rem' }" v-html="xraylogModal.formattedLogs"></div>
|
||||
</a-modal>
|
||||
<a-modal id="backup-modal"
|
||||
v-model="backupModal.visible"
|
||||
title='{{ i18n "pages.index.backupTitle" }}'
|
||||
|
@ -606,6 +645,57 @@
|
|||
},
|
||||
};
|
||||
|
||||
const xraylogModal = {
|
||||
visible: false,
|
||||
logs: [],
|
||||
rows: 20,
|
||||
loading: false,
|
||||
show(logs) {
|
||||
this.visible = true;
|
||||
this.logs = logs;
|
||||
this.formattedLogs = this.logs?.length > 0 ? this.formatLogs(this.logs) : "No Record...";
|
||||
},
|
||||
formatLogs(logs) {
|
||||
let formattedLogs = '';
|
||||
|
||||
logs.forEach((log, index) => {
|
||||
if(index > 0) formattedLogs += '<br>';
|
||||
|
||||
const parts = log.split(' ');
|
||||
|
||||
if(parts.length === 9) {
|
||||
const dateTime = `<b>${parts[0]} ${parts[1]}</b>`;
|
||||
const from = `<b>${parts[3]}</b>`;
|
||||
const to = `<b>${parts[5].replace(/^\/+/, "")}</b>`;
|
||||
|
||||
let outboundColor = '';
|
||||
if (parts[8].startsWith('blocked')) {
|
||||
outboundColor = ' style="color: #e04141;"';
|
||||
}
|
||||
else if (!parts[8].startsWith('direct')) {
|
||||
outboundColor = ' style="color: #3c89e8;"';
|
||||
}
|
||||
|
||||
formattedLogs += `<span${outboundColor}>
|
||||
${dateTime}
|
||||
${parts[2]}
|
||||
${from}
|
||||
${parts[4]}
|
||||
${to}
|
||||
${parts.slice(6).join(' ')}
|
||||
</span>`;
|
||||
} else {
|
||||
formattedLogs += `<span>${parts.join(' ')}</span>`;
|
||||
}
|
||||
});
|
||||
|
||||
return formattedLogs;
|
||||
},
|
||||
hide() {
|
||||
this.visible = false;
|
||||
},
|
||||
};
|
||||
|
||||
const backupModal = {
|
||||
visible: false,
|
||||
show() {
|
||||
|
@ -629,6 +719,7 @@
|
|||
status: new Status(),
|
||||
versionModal,
|
||||
logModal,
|
||||
xraylogModal,
|
||||
backupModal,
|
||||
loadingTip: '{{ i18n "loading"}}',
|
||||
showAlert: false,
|
||||
|
@ -721,6 +812,16 @@
|
|||
await PromiseUtil.sleep(500);
|
||||
logModal.loading = false;
|
||||
},
|
||||
async openXrayLogs(){
|
||||
xraylogModal.loading = true;
|
||||
const msg = await HttpUtil.post('server/xraylogs/'+xraylogModal.rows);
|
||||
if (!msg.success) {
|
||||
return;
|
||||
}
|
||||
xraylogModal.show(msg.obj);
|
||||
await PromiseUtil.sleep(500);
|
||||
xraylogModal.loading = false;
|
||||
},
|
||||
async openConfig() {
|
||||
this.loading(true);
|
||||
const msg = await HttpUtil.post('server/getConfigJson');
|
||||
|
|
|
@ -2,6 +2,7 @@ package service
|
|||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
@ -481,6 +482,37 @@ func (s *ServerService) GetLogs(count string, level string, syslog string) []str
|
|||
return lines
|
||||
}
|
||||
|
||||
func (s *ServerService) GetXrayLogs(count string) []string {
|
||||
c, _ := strconv.Atoi(count)
|
||||
var lines []string
|
||||
|
||||
pathToAccessLog, err := xray.GetAccessLogPath()
|
||||
if err != nil {
|
||||
return lines
|
||||
}
|
||||
|
||||
file, err := os.Open(pathToAccessLog)
|
||||
if err != nil {
|
||||
return lines
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.TrimSpace(line) == "" || strings.Contains(line, "api -> api") {
|
||||
continue
|
||||
}
|
||||
lines = append(lines, line)
|
||||
}
|
||||
|
||||
if len(lines) > c {
|
||||
lines = lines[len(lines)-c:]
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func (s *ServerService) GetConfigJson() (any, error) {
|
||||
config, err := s.xrayService.GetXrayConfig()
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in a new issue