mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-03-21 10:05:49 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5487dc41cc | ||
|
|
5a908b9f58 | ||
|
|
61288db11e | ||
|
|
317f7fe9da | ||
|
|
7b5dd2d0ee | ||
|
|
b1302c70fb | ||
|
|
addedb1adf | ||
|
|
62bb42cfab | ||
|
|
f4be9f234a | ||
|
|
947129a62a | ||
|
|
66f0a13145 | ||
|
|
9626379731 | ||
|
|
c2c61cdd5b | ||
|
|
b5ae580d12 |
@@ -24,10 +24,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
|
|||||||
|
|
||||||
## Install custom version
|
## Install custom version
|
||||||
|
|
||||||
To install your desired version you can add the version to the end of install command. Example for ver `v1.4.5`:
|
To install your desired version you can add the version to the end of install command. Example for ver `v1.4.6`:
|
||||||
|
|
||||||
```
|
```
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v1.4.5
|
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v1.4.6
|
||||||
```
|
```
|
||||||
|
|
||||||
# SSL
|
# SSL
|
||||||
@@ -187,7 +187,7 @@ Reference syntax:
|
|||||||
## API routes
|
## API routes
|
||||||
|
|
||||||
- `/login` with `PUSH` user data: `{username: '', password: ''}` for login
|
- `/login` with `PUSH` user data: `{username: '', password: ''}` for login
|
||||||
- `/xui/API/inbounds` base for following actions:
|
- `/panel/api/inbounds` base for following actions:
|
||||||
|
|
||||||
| Method | Path | Action |
|
| Method | Path | Action |
|
||||||
| :----: | ---------------------------------- | ------------------------------------------- |
|
| :----: | ---------------------------------- | ------------------------------------------- |
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
1.4.5
|
1.4.6
|
||||||
@@ -28,6 +28,12 @@ body {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.ant-layout-sider {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.ant-card {
|
.ant-card {
|
||||||
border-radius: 1.5rem;
|
border-radius: 1.5rem;
|
||||||
}
|
}
|
||||||
@@ -269,12 +275,15 @@ body {
|
|||||||
|
|
||||||
.ant-card-dark .ant-table-tbody>tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)>td,
|
.ant-card-dark .ant-table-tbody>tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)>td,
|
||||||
.ant-card-dark .ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled),
|
.ant-card-dark .ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled),
|
||||||
.ant-card-dark .ant-calendar-date:hover,
|
.ant-card-dark .ant-select-dropdown-menu-item-active {
|
||||||
.ant-card-dark .ant-select-dropdown-menu-item-active,
|
|
||||||
.ant-card-dark li.ant-calendar-time-picker-select-option-selected {
|
|
||||||
background-color: #11314d;
|
background-color: #11314d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-card-dark .ant-calendar-date:hover,
|
||||||
|
.ant-card-dark li.ant-calendar-time-picker-select-option-selected {
|
||||||
|
background-color: rgb(4, 119, 90);
|
||||||
|
}
|
||||||
|
|
||||||
.ant-card-dark tbody .ant-table-expanded-row,
|
.ant-card-dark tbody .ant-table-expanded-row,
|
||||||
.ant-card-dark .ant-calendar-time-picker-inner {
|
.ant-card-dark .ant-calendar-time-picker-inner {
|
||||||
color: hsla(0,0%,100%,.65);
|
color: hsla(0,0%,100%,.65);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const supportLangs = [
|
|||||||
icon: '🇺🇸',
|
icon: '🇺🇸',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Farsi',
|
name: 'فارسی',
|
||||||
value: 'fa_IR',
|
value: 'fa_IR',
|
||||||
icon: '🇮🇷',
|
icon: '🇮🇷',
|
||||||
},
|
},
|
||||||
@@ -15,7 +15,7 @@ const supportLangs = [
|
|||||||
icon: '🇨🇳',
|
icon: '🇨🇳',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Russian',
|
name: 'Русский',
|
||||||
value: 'ru_RU',
|
value: 'ru_RU',
|
||||||
icon: '🇷🇺',
|
icon: '🇷🇺',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ func NewAPIController(g *gin.RouterGroup) *APIController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIController) initRouter(g *gin.RouterGroup) {
|
func (a *APIController) initRouter(g *gin.RouterGroup) {
|
||||||
g = g.Group("/xui/API/inbounds")
|
g = g.Group("/panel/api/inbounds")
|
||||||
g.Use(a.checkLogin)
|
g.Use(a.checkLogin)
|
||||||
|
|
||||||
g.GET("/list", a.getAllInbounds)
|
g.GET("/list", a.getAllInbounds)
|
||||||
@@ -35,21 +35,27 @@ func (a *APIController) initRouter(g *gin.RouterGroup) {
|
|||||||
|
|
||||||
a.inboundController = NewInboundController(g)
|
a.inboundController = NewInboundController(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIController) getAllInbounds(c *gin.Context) {
|
func (a *APIController) getAllInbounds(c *gin.Context) {
|
||||||
a.inboundController.getInbounds(c)
|
a.inboundController.getInbounds(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIController) getSingleInbound(c *gin.Context) {
|
func (a *APIController) getSingleInbound(c *gin.Context) {
|
||||||
a.inboundController.getInbound(c)
|
a.inboundController.getInbound(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIController) getClientTraffics(c *gin.Context) {
|
func (a *APIController) getClientTraffics(c *gin.Context) {
|
||||||
a.inboundController.getClientTraffics(c)
|
a.inboundController.getClientTraffics(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIController) addInbound(c *gin.Context) {
|
func (a *APIController) addInbound(c *gin.Context) {
|
||||||
a.inboundController.addInbound(c)
|
a.inboundController.addInbound(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIController) delInbound(c *gin.Context) {
|
func (a *APIController) delInbound(c *gin.Context) {
|
||||||
a.inboundController.delInbound(c)
|
a.inboundController.delInbound(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIController) updateInbound(c *gin.Context) {
|
func (a *APIController) updateInbound(c *gin.Context) {
|
||||||
a.inboundController.updateInbound(c)
|
a.inboundController.updateInbound(c)
|
||||||
}
|
}
|
||||||
@@ -61,24 +67,31 @@ func (a *APIController) getClientIps(c *gin.Context) {
|
|||||||
func (a *APIController) clearClientIps(c *gin.Context) {
|
func (a *APIController) clearClientIps(c *gin.Context) {
|
||||||
a.inboundController.clearClientIps(c)
|
a.inboundController.clearClientIps(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIController) addInboundClient(c *gin.Context) {
|
func (a *APIController) addInboundClient(c *gin.Context) {
|
||||||
a.inboundController.addInboundClient(c)
|
a.inboundController.addInboundClient(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIController) delInboundClient(c *gin.Context) {
|
func (a *APIController) delInboundClient(c *gin.Context) {
|
||||||
a.inboundController.delInboundClient(c)
|
a.inboundController.delInboundClient(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIController) updateInboundClient(c *gin.Context) {
|
func (a *APIController) updateInboundClient(c *gin.Context) {
|
||||||
a.inboundController.updateInboundClient(c)
|
a.inboundController.updateInboundClient(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIController) resetClientTraffic(c *gin.Context) {
|
func (a *APIController) resetClientTraffic(c *gin.Context) {
|
||||||
a.inboundController.resetClientTraffic(c)
|
a.inboundController.resetClientTraffic(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIController) resetAllTraffics(c *gin.Context) {
|
func (a *APIController) resetAllTraffics(c *gin.Context) {
|
||||||
a.inboundController.resetAllTraffics(c)
|
a.inboundController.resetAllTraffics(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIController) resetAllClientTraffics(c *gin.Context) {
|
func (a *APIController) resetAllClientTraffics(c *gin.Context) {
|
||||||
a.inboundController.resetAllClientTraffics(c)
|
a.inboundController.resetAllClientTraffics(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIController) delDepletedClients(c *gin.Context) {
|
func (a *APIController) delDepletedClients(c *gin.Context) {
|
||||||
a.inboundController.delDepletedClients(c)
|
a.inboundController.delDepletedClients(c)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -317,6 +317,7 @@
|
|||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
el: '#app',
|
el: '#app',
|
||||||
data: {
|
data: {
|
||||||
|
siderDrawer,
|
||||||
themeSwitcher,
|
themeSwitcher,
|
||||||
spinning: false,
|
spinning: false,
|
||||||
inbounds: [],
|
inbounds: [],
|
||||||
|
|||||||
@@ -388,6 +388,7 @@
|
|||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
el: '#app',
|
el: '#app',
|
||||||
data: {
|
data: {
|
||||||
|
siderDrawer,
|
||||||
themeSwitcher,
|
themeSwitcher,
|
||||||
status: new Status(),
|
status: new Status(),
|
||||||
versionModal,
|
versionModal,
|
||||||
|
|||||||
@@ -241,6 +241,7 @@
|
|||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
el: '#app',
|
el: '#app',
|
||||||
data: {
|
data: {
|
||||||
|
siderDrawer,
|
||||||
themeSwitcher,
|
themeSwitcher,
|
||||||
spinning: false,
|
spinning: false,
|
||||||
oldAllSetting: new AllSetting(),
|
oldAllSetting: new AllSetting(),
|
||||||
@@ -357,7 +358,7 @@
|
|||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
this.loading(true);
|
this.loading(true);
|
||||||
await PromiseUtil.sleep(5000);
|
await PromiseUtil.sleep(5000);
|
||||||
location.reload();
|
window.location.replace(this.allSetting.webBasePath + "panel/settings");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getUserSecret() {
|
async getUserSecret() {
|
||||||
|
|||||||
25
web/web.go
25
web/web.go
@@ -147,6 +147,28 @@ func (s *Server) getHtmlTemplate(funcMap template.FuncMap) (*template.Template,
|
|||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func redirectMiddleware(basePath string) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// Redirect from old '/xui' path to '/panel'
|
||||||
|
path := c.Request.URL.Path
|
||||||
|
redirects := map[string]string{
|
||||||
|
"panel/API": "panel/api",
|
||||||
|
"xui/API": "panel/api",
|
||||||
|
"xui": "panel",
|
||||||
|
}
|
||||||
|
for from, to := range redirects {
|
||||||
|
from, to = basePath+from, basePath+to
|
||||||
|
if strings.HasPrefix(path, from) {
|
||||||
|
newPath := to + path[len(from):]
|
||||||
|
c.Redirect(http.StatusMovedPermanently, newPath)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) initRouter() (*gin.Engine, error) {
|
func (s *Server) initRouter() (*gin.Engine, error) {
|
||||||
if config.IsDebug() {
|
if config.IsDebug() {
|
||||||
gin.SetMode(gin.DebugMode)
|
gin.SetMode(gin.DebugMode)
|
||||||
@@ -203,6 +225,9 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
|||||||
engine.StaticFS(basePath+"assets", http.FS(&wrapAssetsFS{FS: assetsFS}))
|
engine.StaticFS(basePath+"assets", http.FS(&wrapAssetsFS{FS: assetsFS}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply the redirect middleware (`/xui` to `/panel`)
|
||||||
|
engine.Use(redirectMiddleware(basePath))
|
||||||
|
|
||||||
g := engine.Group(basePath)
|
g := engine.Group(basePath)
|
||||||
|
|
||||||
s.index = controller.NewIndexController(g)
|
s.index = controller.NewIndexController(g)
|
||||||
|
|||||||
Reference in New Issue
Block a user