Compare commits

...

19 Commits
0.4.2 ... 0.4.3

Author SHA1 Message Date
Alireza Ahmadi
8f0a701d9e v0.4.3 2023-03-26 13:38:32 +02:00
Alireza Ahmadi
921a72b5cf v0.4.3 2023-03-24 12:52:00 +01:00
Alireza Ahmadi
9e902fc82f Add version and log 2023-03-24 12:24:12 +01:00
Alireza Ahmadi
6ed8ff8e05 [infoModal] better display 2023-03-24 12:23:23 +01:00
Alireza Ahmadi
f333ad23d9 Merge pull request #92 from alireza0/dependabot/go_modules/google.golang.org/grpc-1.54.0
Bump google.golang.org/grpc from 1.53.0 to 1.54.0
2023-03-24 12:06:58 +01:00
Alireza Ahmadi
3f49aa5541 Merge pull request #89 from alireza0/TLS-enhancements
TLS-enhancements
2023-03-24 12:06:33 +01:00
Alireza Ahmadi
0f487dc10f [tgbot] fix admins input 2023-03-24 08:43:09 +01:00
dependabot[bot]
f3c0edac07 Bump google.golang.org/grpc from 1.53.0 to 1.54.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.53.0 to 1.54.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.53.0...v1.54.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-23 22:07:30 +00:00
Alireza Ahmadi
394c857bb6 [tgbot] fix exhausted report 2023-03-23 22:55:23 +01:00
Alireza Ahmadi
6080709fc7 Enable fallback in xtls as well 2023-03-23 20:13:21 +01:00
Alireza Ahmadi
17c47f0711 Remove ugly-bugy qrCode footer 2023-03-23 18:29:12 +01:00
Alireza Ahmadi
dac234357a Fix input-group dark theme 2023-03-23 12:53:36 +01:00
Alireza Ahmadi
2d15073a90 Fix vmess link 2023-03-23 11:16:48 +01:00
Alireza Ahmadi
1b2e165a65 Fix TLS and ALPN 2023-03-23 10:03:09 +01:00
Alireza Ahmadi
e1f8e8efd2 Add allowInsecure option 2023-03-23 10:02:18 +01:00
Alireza Ahmadi
738c7064a9 Default TLS version change to 1.0-1.2 2023-03-23 10:01:28 +01:00
Alireza Ahmadi
c24483ad44 [tgbot] fix client search #85 2023-03-23 09:59:34 +01:00
Alireza Ahmadi
046f071378 [bug] Fix Transport column in search table 2023-03-21 15:03:26 +01:00
Alireza Ahmadi
d40a16e29d Update README 2023-03-20 13:50:39 +01:00
20 changed files with 185 additions and 84 deletions

View File

@@ -40,12 +40,18 @@ xray panel supporting multi-protocol, **Multi-lang (English,Farsi,Chinese)**
![inbounds](./media/inbounds.png)
![Dark inbounds](./media/inbounds-dark.png)
# Install & Upgrade
# Install & Upgrade to latest version
```
bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh)
```
## Install custom version
To install your desired version you can add the version to the end of install command. Example for ver `0.4.0`:
```
bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh) 0.4.0
```
## Manual install & upgrade
1. First download the latest compressed package from https://github.com/alireza0/x-ui/releases , generally choose Architecture `amd64`

View File

@@ -1 +1 @@
0.4.2
0.4.3

2
go.mod
View File

@@ -15,7 +15,7 @@ require (
github.com/xtls/xray-core v1.8.0
go.uber.org/atomic v1.10.0
golang.org/x/text v0.8.0
google.golang.org/grpc v1.53.0
google.golang.org/grpc v1.54.0
gorm.io/driver/sqlite v1.4.4
gorm.io/gorm v1.24.6
)

4
go.sum
View File

@@ -229,8 +229,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA=
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0=

View File

@@ -188,7 +188,8 @@
.ant-card-dark .ant-collapse-content,
.ant-card-dark .ant-calendar,
.ant-card-dark .ant-table-placeholder {
.ant-card-dark .ant-table-placeholder,
.ant-card-dark .ant-input-group-addon {
color: hsla(0,0%,100%,.65);
background-color: #262f3d;
}
@@ -208,7 +209,8 @@
.ant-card-dark .ant-calendar-year-select,
.ant-card-dark .ant-calendar-date,
.ant-card-dark .ant-collapse>.ant-collapse-item>.ant-collapse-header,
.ant-card-dark .ant-empty-normal {
.ant-card-dark .ant-empty-normal,
.ant-card-dark .ant-checkbox+span {
color: hsla(0,0%,100%,.65);
}
@@ -308,7 +310,8 @@
border-color: #593815;
}
.ant-card-dark .ant-table-row-expand-icon {
.ant-card-dark .ant-table-row-expand-icon,
.ant-card-dark .ant-checkbox-inner {
background: none;
}
@@ -316,12 +319,17 @@
background-color: #0c61b0;
}
.ant-card-dark .ant-btn {
.ant-card-dark .ant-btn,
.ant-card-dark .ant-radio-button-wrapper {
color: hsla(0,0%,100%,.65);
background: none;
border: 1px solid hsla(0,0%,100%,.65);
}
.ant-card-dark .ant-radio-button-wrapper:hover {
color: #177ddc;
}
.ant-card-dark .ant-btn-primary {
color: hsla(0,0%,100%,.65);
background-color: #073763;

View File

@@ -95,7 +95,6 @@ const UTLS_FINGERPRINT = {
const ALPN_OPTION = {
H2: "h2",
HTTP1: "http/1.1",
BOTH: "h2,http/1.1",
};
Object.freeze(Protocols);
@@ -474,8 +473,8 @@ class GrpcStreamSettings extends XrayCommonClass {
class TlsStreamSettings extends XrayCommonClass {
constructor(serverName='',
minVersion = TLS_VERSION_OPTION.TLS12,
maxVersion = TLS_VERSION_OPTION.TLS13,
minVersion = TLS_VERSION_OPTION.TLS10,
maxVersion = TLS_VERSION_OPTION.TLS12,
cipherSuites = '',
certificates=[new TlsStreamSettings.Cert()],
alpn=[''],
@@ -575,9 +574,9 @@ TlsStreamSettings.Cert = class extends XrayCommonClass {
};
TlsStreamSettings.Settings = class extends XrayCommonClass {
constructor(insecure = false, fingerprint = '', serverName = '') {
constructor(allowInsecure = false, fingerprint = '', serverName = '') {
super();
this.inSecure = insecure;
this.allowInsecure = allowInsecure;
this.fingerprint = fingerprint;
this.serverName = serverName;
}
@@ -590,7 +589,7 @@ TlsStreamSettings.Settings = class extends XrayCommonClass {
}
toJson() {
return {
allowInsecure: this.inSecure,
allowInsecure: this.allowInsecure,
fingerprint: this.fingerprint,
serverName: this.serverName,
};
@@ -1081,6 +1080,10 @@ class Inbound extends XrayCommonClass {
host: host,
path: path,
tls: this.stream.security,
sni: this.stream.tls.settings[0]['serverName'],
fp: this.stream.tls.settings[0]['fingerprint'],
alpn: this.stream.tls.alpn.join(','),
allowInsecure: this.stream.tls.settings[0].allowInsecure,
};
return 'vmess://' + base64(JSON.stringify(obj, null, 2));
}
@@ -1092,7 +1095,6 @@ class Inbound extends XrayCommonClass {
const type = this.stream.network;
const params = new Map();
params.set("type", this.stream.network);
params.set("security", this.stream.security);
switch (type) {
case "tcp":
const tcp = this.stream.tcp;
@@ -1139,8 +1141,12 @@ class Inbound extends XrayCommonClass {
}
if (this.tls) {
params.set("security", "tls");
params.set("fp" , this.stream.tls.settings[0]['fingerprint']);
params.set("alpn", this.stream.tls.alpn[0]);
params.set("alpn", this.stream.tls.alpn);
if(this.stream.tls.settings[0].allowInsecure){
params.set("allowInsecure", "1");
}
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
address = this.stream.tls.server;
}
@@ -1153,6 +1159,11 @@ class Inbound extends XrayCommonClass {
}
if (this.xtls) {
params.set("security", "xtls");
params.set("alpn", this.stream.tls.alpn);
if(this.stream.tls.settings[0].allowInsecure){
params.set("allowInsecure", "1");
}
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
address = this.stream.tls.server;
}
@@ -1188,7 +1199,6 @@ class Inbound extends XrayCommonClass {
const type = this.stream.network;
const params = new Map();
params.set("type", this.stream.network);
params.set("security", this.stream.security);
switch (type) {
case "tcp":
const tcp = this.stream.tcp;
@@ -1235,8 +1245,12 @@ class Inbound extends XrayCommonClass {
}
if (this.tls) {
params.set("security", "tls");
params.set("fp" , this.stream.tls.settings[0]['fingerprint']);
params.set("alpn", this.stream.tls.alpn[0]);
params.set("alpn", this.stream.tls.alpn);
if(this.stream.tls.settings[0].allowInsecure){
params.set("allowInsecure", "1");
}
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
address = this.stream.tls.server;
}
@@ -1246,6 +1260,11 @@ class Inbound extends XrayCommonClass {
}
if (this.xtls) {
params.set("security", "xtls");
params.set("alpn", this.stream.tls.alpn);
if(this.stream.tls.settings[0].allowInsecure){
params.set("allowInsecure", "1");
}
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
address = this.stream.tls.server;
}

View File

@@ -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)
}
}
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)
}

View File

@@ -1,9 +1,10 @@
{{define "qrcodeModal"}}
<a-modal id="qrcode-modal" v-model="qrModal.visible" :title="qrModal.title"
:closable="true" width="300px" :ok-text="qrModal.okText"
:closable="true"
:class="siderDrawer.isDarkTheme ? darkClass : ''"
cancel-text='{{ i18n "close" }}' :ok-button-props="{attrs:{id:'qr-modal-ok-btn'}}">
<a-tag color="green" style="margin-bottom: 10px;display: block;text-align: center;" >{{ i18n "pages.inbounds.clickOnQRcode" }}</a-tag>
:footer="null"
width="300px">
<a-tag color="green" style="margin-bottom: 10px;display: block;text-align: center;" >{{ i18n "pages.inbounds.clickOnQRcode" }}</a-tag>
<canvas @click="copyToClipboard()" id="qrCode" style="width: 100%; height: 100%;"></canvas>
</a-modal>
@@ -14,17 +15,15 @@
content: '',
inbound: new Inbound(),
dbInbound: new DBInbound(),
okText: '',
copyText: '',
qrcode: null,
clipboard: null,
visible: false,
show: function (title='', content='', dbInbound=new DBInbound(),okText='{{ i18n "copy" }}', copyText='') {
show: function (title='', content='', dbInbound=new DBInbound(), copyText='') {
this.title = title;
this.content = content;
this.dbInbound = dbInbound;
this.inbound = dbInbound.toInbound();
this.okText = okText;
if (ObjectUtil.isEmpty(copyText)) {
this.copyText = content;
} else {
@@ -32,13 +31,6 @@
}
this.visible = true;
qrModalApp.$nextTick(() => {
this.clipboard = new ClipboardJS('#qr-modal-ok-btn', {
text: () => this.copyText,
});
this.clipboard.on('success', () => {
app.$message.success('{{ i18n "copied" }}')
this.clipboard.destroy();
});
if (this.qrcode === null) {
this.qrcode = new QRious({
element: document.querySelector('#qrCode'),

View File

@@ -65,7 +65,7 @@
</table>
</a-collapse-panel>
</a-collapse>
<template v-if="inbound.isTcp && inbound.tls">
<template v-if="inbound.isTcp && (inbound.tls || inbound.xtls)">
<a-form layout="inline">
<a-form-item label="Fallbacks">
<a-row>

View File

@@ -71,7 +71,7 @@
</table>
</a-collapse-panel>
</a-collapse>
<template v-if="inbound.isTcp && inbound.tls">
<template v-if="inbound.isTcp && (inbound.tls || inbound.xtls)">
<a-form layout="inline">
<a-form-item label="Fallbacks">
<a-row>

View File

@@ -40,11 +40,13 @@
<a-form-item label='{{ i18n "domainName" }}'>
<a-input v-model.trim="inbound.stream.tls.server"></a-input>
</a-form-item>
<a-form-item label="Alpn" v-if="inbound.tls">
<a-select v-model="inbound.stream.tls.alpn[0]" style="width:200px">
<a-select-option value=''>auto</a-select-option>
<a-select-option v-for="key in ALPN_OPTION" :value="key">[[ key ]]</a-select-option>
</a-select>
<a-form-item label="Alpn">
<a-checkbox-group v-model="inbound.stream.tls.alpn" style="width:200px">
<a-checkbox v-for="key in ALPN_OPTION" :value="key">[[ key ]]</a-checkbox>
</a-checkbox-group>
</a-form-item>
<a-form-item label="Allow insecure">
<a-switch v-model="inbound.stream.tls.settings[0].allowInsecure"></a-switch>
</a-form-item>
<a-form-item label='{{ i18n "certificate" }}'>
<a-radio-group v-model="inbound.stream.tls.certs[0].useFile" button-style="solid">

View File

@@ -59,12 +59,10 @@
</table>
<template v-if="infoModal.clientSettings">
<a-divider>{{ i18n "pages.inbounds.client" }}</a-divider>
<table style="margin-bottom: 10px; width: 100%;">
<tr>
<th v-for="col in Object.keys(infoModal.clientSettings).slice(0, 3)">[[ col ]]</th>
</tr>
<tr>
<td v-for="col in Object.values(infoModal.clientSettings).slice(0, 3)"><a-tag color="green">[[ col ]]</a-tag></td>
<table style="margin-bottom: 10px;">
<tr v-for="col,index in Object.keys(infoModal.clientSettings).slice(0, 3)">
<td>[[ col ]]</td>
<td><a-tag color="green">[[ infoModal.clientSettings[col] ]]</a-tag></td>
</table>
<table style="margin-bottom: 10px; width: 100%;">
<tr><th>{{ i18n "usage" }}</th><th>{{ i18n "pages.inbounds.totalFlow" }}</th><th>{{ i18n "pages.inbounds.expireDate" }}</th><th>{{ i18n "enable" }}</th></tr>

View File

@@ -114,11 +114,11 @@
</template>
<a-tag v-else color="green">{{ i18n "unlimited" }}</a-tag>
</template>
<template slot="stream" slot-scope="text, dbInbound, index">
<template slot="stream" slot-scope="text, dbInbound">
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
<a-tag color="green">[[ inbounds[index].stream.network ]]</a-tag>
<a-tag v-if="inbounds[index].stream.isTls" color="blue">tls</a-tag>
<a-tag v-if="inbounds[index].stream.isXTls" color="blue">xtls</a-tag>
<a-tag color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag>
<a-tag v-if="dbInbound.toInbound().stream.isTls" color="blue">tls</a-tag>
<a-tag v-if="dbInbound.toInbound().stream.isXTls" color="blue">xtls</a-tag>
</template>
<template v-else>{{ i18n "none" }}</template>
</template>

View File

@@ -92,6 +92,8 @@
</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="green">[[ formatSecond(status.uptime) ]]</a-tag>
<a-tooltip>
@@ -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) {

View File

@@ -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="text" title='{{ i18n "pages.setting.telegramChatId"}}' desc='{{ i18n "pages.setting.telegramChatIdDesc"}}' v-model.number="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>

View File

@@ -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
}

View File

@@ -126,10 +126,14 @@ func (t *Tgbot) answerCommand(message *tgbotapi.Message, chatId int64, isAdmin b
case "status":
msg = "bot is ok ✅"
case "usage":
if isAdmin {
t.searchClient(chatId, message.CommandArguments())
if len(message.CommandArguments()) > 1 {
if isAdmin {
t.searchClient(chatId, message.CommandArguments())
} else {
t.searchForClient(chatId, message.CommandArguments())
}
} else {
t.searchForClient(chatId, message.CommandArguments())
msg = "❗Please provide a text for search!"
}
case "inbound":
if isAdmin {
@@ -272,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
@@ -508,7 +513,7 @@ func (t *Tgbot) getExhausted() string {
}
ExpireThreshold, err := t.settingService.GetTgExpireDiff()
if err == nil && ExpireThreshold > 0 {
exDiff = int64(ExpireThreshold) * 84600
exDiff = int64(ExpireThreshold) * 84600000
}
inbounds, err := t.inboundService.GetAllInbounds()
if err != nil {
@@ -516,14 +521,14 @@ func (t *Tgbot) getExhausted() string {
}
for _, inbound := range inbounds {
if inbound.Enable {
if (inbound.ExpiryTime > 0 && (now-inbound.ExpiryTime < exDiff)) ||
if (inbound.ExpiryTime > 0 && (inbound.ExpiryTime-now < exDiff)) ||
(inbound.Total > 0 && (inbound.Total-inbound.Up+inbound.Down < trDiff)) {
exhaustedInbounds = append(exhaustedInbounds, *inbound)
}
if len(inbound.ClientStats) > 0 {
for _, client := range inbound.ClientStats {
if client.Enable {
if (client.ExpiryTime > 0 && (now-client.ExpiryTime < exDiff)) ||
if (client.ExpiryTime > 0 && (client.ExpiryTime-now < exDiff)) ||
(client.Total > 0 && (client.Total-client.Up+client.Down < trDiff)) {
exhaustedClients = append(exhaustedClients, client)
}
@@ -537,7 +542,7 @@ func (t *Tgbot) getExhausted() string {
}
}
output += fmt.Sprintf("Exhausted Inbounds count:\r\n🛑 Disabled: %d\r\n🔜 Exhaust soon: %d\r\n \r\n", len(disabledInbounds), len(exhaustedInbounds))
if len(disabledInbounds)+len(exhaustedInbounds) > 0 {
if len(exhaustedInbounds) > 0 {
output += "Exhausted Inbounds:\r\n"
for _, inbound := range exhaustedInbounds {
output += fmt.Sprintf("📍Inbound:%s\r\nPort:%d\r\nTraffic: %s (↑%s,↓%s)\r\n", inbound.Remark, inbound.Port, common.FormatTraffic((inbound.Up + inbound.Down)), common.FormatTraffic(inbound.Up), common.FormatTraffic(inbound.Down))
@@ -549,7 +554,7 @@ func (t *Tgbot) getExhausted() string {
}
}
output += fmt.Sprintf("Exhausted Clients count:\r\n🛑 Disabled: %d\r\n🔜 Exhaust soon: %d\r\n \r\n", len(disabledClients), len(exhaustedClients))
if len(disabledClients)+len(exhaustedClients) > 0 {
if len(exhaustedClients) > 0 {
output += "Exhausted Clients:\r\n"
for _, traffic := range exhaustedClients {
expiryTime := ""
@@ -564,7 +569,7 @@ func (t *Tgbot) getExhausted() string {
} 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",
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 date: %s\r\n \r\n",
traffic.Enable, traffic.Email, common.FormatTraffic(traffic.Up), common.FormatTraffic(traffic.Down), common.FormatTraffic((traffic.Up + traffic.Down)),
total, expiryTime)
}

View File

@@ -69,8 +69,8 @@
"memory" = "Memory"
"hard" = "Hard disk"
"xrayStatus" = "xray Status"
"stopXray" = "Stop xray"
"restartXray" = "Restart xray"
"stopXray" = "Stop"
"restartXray" = "Restart"
"xraySwitch" = "Switch Version"
"xraySwitchClick" = "Click on the version you want to switch"
"xraySwitchClickDesk" = "Please choose carefully, older versions may have incompatible configurations"

View File

@@ -69,13 +69,13 @@
"memory" = "حافظه رم"
"hard" = "حافظه دیسک"
"xrayStatus" = "وضعیت Xray"
"stopXray" = "توقف xray"
"restartXray" = "شروع مجدد xray"
"stopXray" = "توقف"
"restartXray" = "شروع مجدد"
"xraySwitch" = "تغییر ورژن"
"xraySwitchClick" = "ورژن مورد نظر را انتخاب کنید"
"xraySwitchClickDesk" = "لطفا با دقت انتخاب کنید ، در صورت انتخاب اشتباه امکان قطعی سیستم وجود دارد ."
"operationHours" = "ساعت فعال"
"operationHoursDesc" = "ساعت فعال بعد از شروع سیستم"
"operationHours" = "مدت فعالیت"
"operationHoursDesc" = "مدت فعالیت سیستم بعد از روشن شدن"
"systemLoad" = "بار روی سیستم"
"connectionCount" = "تعداد کانکشن ها"
"connectionCountDesc" = "تعداد کانکشن ها برای کل شبکه"

View File

@@ -69,8 +69,8 @@
"memory" = "内存"
"hard" = "硬盘"
"xrayStatus" = "xray 状态"
"stopXray" = "停止 Xray"
"restartXray" = "重启 Xray"
"stopXray" = "停止"
"restartXray" = "重启"
"xraySwitch" = "切换版本"
"xraySwitchClick" = "点击你想切换的版本"
"xraySwitchClickDesk" = "请谨慎选择,旧版本可能配置不兼容"