Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bd8d5d6f24 | |||
| d39df72896 | |||
| cfa5f38177 | |||
| abcf0bca99 | |||
| 5de5d6c3d0 | |||
| 944f671d96 | |||
| 00ee69e541 | |||
| f5b609204e | |||
| edc1038623 | |||
| c687a42d7e | |||
| bf1759ceda | |||
| e404342e2c | |||
| 4a3917ea5d | |||
| 037dbbd7a0 | |||
| 541e54c9a4 | |||
| 2cc78da07f | |||
| 63dc779f68 | |||
| 9ba611649c | |||
| b0817e9c05 | |||
| 8a2407fbd3 | |||
| 204e0d30e1 | |||
| 19a6053098 | |||
| e91d8038ad | |||
| 840b7ba1b1 | |||
| 233c27be23 | |||
| c54d9c15bc | |||
| 30c9ed6aa7 | |||
| 1bdf4e9c4c | |||
| 2f0d7a8502 | |||
| 99b8cbc75f |
@@ -10,16 +10,21 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform: [amd64, arm64, arm]
|
platform:
|
||||||
|
- amd64
|
||||||
|
- arm64
|
||||||
|
- armv7
|
||||||
|
- 386
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4.1.1
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5.0.0
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
cache: false
|
||||||
|
go-version: '1.22'
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
@@ -27,13 +32,15 @@ jobs:
|
|||||||
node-version: '20'
|
node-version: '20'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
- name: Install dependencies for arm64 and arm
|
- name: Install dependencies
|
||||||
if: matrix.platform == 'arm64' || matrix.platform == 'arm'
|
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
|
if [ "${{ matrix.platform }}" == "arm64" ]; then
|
||||||
sudo apt install gcc-aarch64-linux-gnu
|
sudo apt install gcc-aarch64-linux-gnu
|
||||||
if [ "${{ matrix.platform }}" == "arm" ]; then
|
elif [ "${{ matrix.platform }}" == "armv7" ]; then
|
||||||
sudo apt install gcc-arm-linux-gnueabihf
|
sudo apt install gcc-arm-linux-gnueabihf
|
||||||
|
elif [ "${{ matrix.platform }}" == "386" ]; then
|
||||||
|
sudo apt install gcc-i686-linux-gnu
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build frontend
|
- name: Build frontend
|
||||||
@@ -44,26 +51,30 @@ jobs:
|
|||||||
cd ..
|
cd ..
|
||||||
mv frontend/dist backend/web/html
|
mv frontend/dist backend/web/html
|
||||||
|
|
||||||
- name: Set evironments
|
- name: Build s-ui & singbox
|
||||||
run: |
|
run: |
|
||||||
export CGO_ENABLED=1
|
export CGO_ENABLED=1
|
||||||
export GOOS=linux
|
export GOOS=linux
|
||||||
export GOARCH=${{ matrix.platform }}
|
export GOARCH=${{ matrix.platform }}
|
||||||
if [ "${{ matrix.platform }}" == "arm64" ]; then
|
if [ "${{ matrix.platform }}" == "arm64" ]; then
|
||||||
|
export GOARCH=arm64
|
||||||
export CC=aarch64-linux-gnu-gcc
|
export CC=aarch64-linux-gnu-gcc
|
||||||
elif [ "${{ matrix.platform }}" == "arm" ]; then
|
elif [ "${{ matrix.platform }}" == "armv7" ]; then
|
||||||
|
export GOARCH=arm
|
||||||
|
export GOARM=7
|
||||||
export CC=arm-linux-gnueabihf-gcc
|
export CC=arm-linux-gnueabihf-gcc
|
||||||
|
elif [ "${{ matrix.platform }}" == "386" ]; then
|
||||||
|
export GOARCH=386
|
||||||
|
export CC=i686-linux-gnu-gcc
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build sing-box
|
#### Build Sing-Box
|
||||||
run: |
|
git clone -b v1.8.7 https://github.com/SagerNet/sing-box
|
||||||
git clone -b v1.8.5 https://github.com/SagerNet/sing-box
|
|
||||||
cd sing-box
|
cd sing-box
|
||||||
go build -tags with_v2ray_api,with_clash_api,with_grpc,with_quic,with_ech -o sing-box ./cmd/sing-box
|
go build -tags with_v2ray_api,with_clash_api,with_grpc,with_quic,with_ech -o sing-box ./cmd/sing-box
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
- name: Build s-ui
|
### Build s-ui
|
||||||
run: |
|
|
||||||
cd backend
|
cd backend
|
||||||
go build -o ../sui main.go
|
go build -o ../sui main.go
|
||||||
cd ..
|
cd ..
|
||||||
@@ -80,7 +91,7 @@ jobs:
|
|||||||
run: tar -zcvf s-ui-linux-${{ matrix.platform }}.tar.gz s-ui
|
run: tar -zcvf s-ui-linux-${{ matrix.platform }}.tar.gz s-ui
|
||||||
|
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: svenstaro/upload-release-action@2.7.0
|
uses: svenstaro/upload-release-action@v2
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
tag: ${{ github.ref }}
|
tag: ${{ github.ref }}
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ WORKDIR /app
|
|||||||
COPY frontend/ ./
|
COPY frontend/ ./
|
||||||
RUN npm install && npm run build
|
RUN npm install && npm run build
|
||||||
|
|
||||||
FROM golang:1.21-alpine AS backend-builder
|
FROM golang:1.22-alpine AS backend-builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE"
|
ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE"
|
||||||
|
|||||||
@@ -26,6 +26,11 @@
|
|||||||
| Dark/Light Theme | :heavy_check_mark: |
|
| Dark/Light Theme | :heavy_check_mark: |
|
||||||
|
|
||||||
|
|
||||||
|
## Default Installation Informarion
|
||||||
|
- Panel Port: 2095
|
||||||
|
- Subscription Port: 2096
|
||||||
|
- User/Passowrd: admin
|
||||||
|
|
||||||
## Install & Upgrade to Latest Version
|
## Install & Upgrade to Latest Version
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -40,6 +45,19 @@ bash <(curl -Ls https://raw.githubusercontent.com/alireza0/s-ui/master/install.s
|
|||||||
bash <(curl -Ls https://raw.githubusercontent.com/alireza0/s-ui/master/install.sh) 0.0.1
|
bash <(curl -Ls https://raw.githubusercontent.com/alireza0/s-ui/master/install.sh) 0.0.1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Uninstall S-UI
|
||||||
|
|
||||||
|
```sh
|
||||||
|
systemctl disable sing-box --now
|
||||||
|
systemctl disable s-ui --now
|
||||||
|
|
||||||
|
rm -f /etc/systemd/system/s-ui.service
|
||||||
|
rm -f /etc/systemd/system/sing-box.service
|
||||||
|
systemctl daemon-reload
|
||||||
|
|
||||||
|
rm -fr /usr/local/s-ui
|
||||||
|
```
|
||||||
|
|
||||||
## Install using Docker
|
## Install using Docker
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|||||||
+26
-1
@@ -27,7 +27,8 @@ func NewAPIHandler(g *gin.RouterGroup) {
|
|||||||
|
|
||||||
func (a *APIHandler) initRouter(g *gin.RouterGroup) {
|
func (a *APIHandler) initRouter(g *gin.RouterGroup) {
|
||||||
g.Use(func(c *gin.Context) {
|
g.Use(func(c *gin.Context) {
|
||||||
if c.Request.URL.Path != "/api/login" && c.Request.URL.Path != "/api/logout" {
|
path := c.Request.URL.Path
|
||||||
|
if !strings.HasSuffix(path, "login") && !strings.HasSuffix(path, "logout") {
|
||||||
checkLogin(c)
|
checkLogin(c)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -62,9 +63,26 @@ func (a *APIHandler) postHandler(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = SetLoginUser(c, loginUser)
|
err = SetLoginUser(c, loginUser)
|
||||||
|
if err == nil {
|
||||||
logger.Info("user ", loginUser, " login success")
|
logger.Info("user ", loginUser, " login success")
|
||||||
|
} else {
|
||||||
|
logger.Warning("login failed: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
jsonMsg(c, "", nil)
|
jsonMsg(c, "", nil)
|
||||||
|
case "changePass":
|
||||||
|
id := c.Request.FormValue("id")
|
||||||
|
oldPass := c.Request.FormValue("oldPass")
|
||||||
|
newUsername := c.Request.FormValue("newUsername")
|
||||||
|
newPass := c.Request.FormValue("newPass")
|
||||||
|
err = a.UserService.ChangePass(id, oldPass, newUsername, newPass)
|
||||||
|
if err == nil {
|
||||||
|
logger.Info("change user credentials success")
|
||||||
|
jsonMsg(c, "save", nil)
|
||||||
|
} else {
|
||||||
|
logger.Warning("change user credentials failed:", err)
|
||||||
|
jsonMsg(c, "", err)
|
||||||
|
}
|
||||||
case "save":
|
case "save":
|
||||||
loginUser := GetLoginUser(c)
|
loginUser := GetLoginUser(c)
|
||||||
data := map[string]string{}
|
data := map[string]string{}
|
||||||
@@ -99,6 +117,13 @@ func (a *APIHandler) getHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
jsonObj(c, data, nil)
|
jsonObj(c, data, nil)
|
||||||
|
case "users":
|
||||||
|
users, err := a.UserService.GetUsers()
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, "", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jsonObj(c, *users, nil)
|
||||||
case "setting":
|
case "setting":
|
||||||
data, err := a.SettingService.GetAllSetting()
|
data, err := a.SettingService.GetAllSetting()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"s-ui/database/model"
|
"s-ui/database/model"
|
||||||
|
|
||||||
"github.com/gin-contrib/sessions"
|
sessions "github.com/Calidity/gin-sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func pureJsonMsg(c *gin.Context, success bool, msg string) {
|
|||||||
func checkLogin(c *gin.Context) {
|
func checkLogin(c *gin.Context) {
|
||||||
if !IsLogin(c) {
|
if !IsLogin(c) {
|
||||||
if c.GetHeader("X-Requested-With") == "XMLHttpRequest" {
|
if c.GetHeader("X-Requested-With") == "XMLHttpRequest" {
|
||||||
pureJsonMsg(c, false, "Not authorized")
|
pureJsonMsg(c, false, "Invalid login")
|
||||||
} else {
|
} else {
|
||||||
c.Redirect(http.StatusTemporaryRedirect, "/login")
|
c.Redirect(http.StatusTemporaryRedirect, "/login")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"s-ui/config"
|
||||||
|
"s-ui/database"
|
||||||
|
"s-ui/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resetAdmin() {
|
||||||
|
err := database.InitDB(config.GetDBPath())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userService := service.UserService{}
|
||||||
|
err = userService.UpdateFirstUser("admin", "admin")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("reset admin credentials failed:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("reset admin credentials success")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateAdmin(username string, password string) {
|
||||||
|
err := database.InitDB(config.GetDBPath())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if username != "" || password != "" {
|
||||||
|
userService := service.UserService{}
|
||||||
|
err := userService.UpdateFirstUser(username, password)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("reset admin credentials failed:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("reset admin credentials success")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func showAdmin() {
|
||||||
|
err := database.InitDB(config.GetDBPath())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userService := service.UserService{}
|
||||||
|
userModel, err := userService.GetFirstUser()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("get current user info failed,error info:", err)
|
||||||
|
}
|
||||||
|
username := userModel.Username
|
||||||
|
userpasswd := userModel.Password
|
||||||
|
if (username == "") || (userpasswd == "") {
|
||||||
|
fmt.Println("current username or password is empty")
|
||||||
|
}
|
||||||
|
fmt.Println("First admin credentials:")
|
||||||
|
fmt.Println("\tUsername:\t", username)
|
||||||
|
fmt.Println("\tPassword:\t", userpasswd)
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"s-ui/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseCmd() {
|
||||||
|
var showVersion bool
|
||||||
|
flag.BoolVar(&showVersion, "v", false, "show version")
|
||||||
|
|
||||||
|
adminCmd := flag.NewFlagSet("admin", flag.ExitOnError)
|
||||||
|
settingCmd := flag.NewFlagSet("setting", flag.ExitOnError)
|
||||||
|
|
||||||
|
var username string
|
||||||
|
var password string
|
||||||
|
var port int
|
||||||
|
var path string
|
||||||
|
var subPort int
|
||||||
|
var subPath string
|
||||||
|
var reset bool
|
||||||
|
var show bool
|
||||||
|
settingCmd.BoolVar(&reset, "reset", false, "reset all settings")
|
||||||
|
settingCmd.BoolVar(&show, "show", false, "show current settings")
|
||||||
|
settingCmd.IntVar(&port, "port", 0, "set panel port")
|
||||||
|
settingCmd.StringVar(&path, "path", "", "set panel path")
|
||||||
|
settingCmd.IntVar(&subPort, "subPort", 0, "set sub port")
|
||||||
|
settingCmd.StringVar(&subPath, "subPath", "", "set sub path")
|
||||||
|
|
||||||
|
adminCmd.BoolVar(&show, "show", false, "show first admin credentials")
|
||||||
|
adminCmd.BoolVar(&reset, "reset", false, "reset first admin credentials")
|
||||||
|
adminCmd.StringVar(&username, "username", "", "set login username")
|
||||||
|
adminCmd.StringVar(&password, "password", "", "set login password")
|
||||||
|
|
||||||
|
oldUsage := flag.Usage
|
||||||
|
flag.Usage = func() {
|
||||||
|
oldUsage()
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Commands:")
|
||||||
|
fmt.Println(" admin set/reset/show first admin credentials")
|
||||||
|
fmt.Println(" setting set/reset/show settings")
|
||||||
|
fmt.Println()
|
||||||
|
adminCmd.Usage()
|
||||||
|
fmt.Println()
|
||||||
|
settingCmd.Usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
if showVersion {
|
||||||
|
fmt.Println(config.GetVersion())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch os.Args[1] {
|
||||||
|
case "admin":
|
||||||
|
err := adminCmd.Parse(os.Args[2:])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case show:
|
||||||
|
showAdmin()
|
||||||
|
case reset:
|
||||||
|
resetAdmin()
|
||||||
|
default:
|
||||||
|
updateAdmin(username, password)
|
||||||
|
showAdmin()
|
||||||
|
}
|
||||||
|
|
||||||
|
case "setting":
|
||||||
|
err := settingCmd.Parse(os.Args[2:])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case show:
|
||||||
|
showSetting()
|
||||||
|
case reset:
|
||||||
|
resetSetting()
|
||||||
|
default:
|
||||||
|
updateSetting(port, path, subPort, subPath)
|
||||||
|
showSetting()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fmt.Println("Invalid subcommands")
|
||||||
|
flag.Usage()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"s-ui/config"
|
||||||
|
"s-ui/database"
|
||||||
|
"s-ui/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resetSetting() {
|
||||||
|
err := database.InitDB(config.GetDBPath())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
settingService := service.SettingService{}
|
||||||
|
err = settingService.ResetSettings()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("reset setting failed:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("reset setting success")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateSetting(port int, path string, subPort int, subPath string) {
|
||||||
|
err := database.InitDB(config.GetDBPath())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
settingService := service.SettingService{}
|
||||||
|
|
||||||
|
if port > 0 {
|
||||||
|
err := settingService.SetPort(port)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("set port failed:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("set port success")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if path != "" {
|
||||||
|
err := settingService.SetWebPath(path)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("set path failed:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("set path success")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if subPort > 0 {
|
||||||
|
err := settingService.SetSubPort(subPort)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("set sub port failed:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("set sub port success")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if subPath != "" {
|
||||||
|
err := settingService.SetSubPath(subPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("set sub path failed:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("set sub path success")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func showSetting() {
|
||||||
|
err := database.InitDB(config.GetDBPath())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
settingService := service.SettingService{}
|
||||||
|
allSetting, err := settingService.GetAllSetting()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("get current port failed,error info:", err)
|
||||||
|
}
|
||||||
|
fmt.Println("Current panel settings:")
|
||||||
|
fmt.Println("\tPanel port:\t", (*allSetting)["webPort"])
|
||||||
|
fmt.Println("\tPanel path:\t", (*allSetting)["webPath"])
|
||||||
|
if (*allSetting)["webListen"] != "" {
|
||||||
|
fmt.Println("\tPanel IP:\t", (*allSetting)["webListen"])
|
||||||
|
}
|
||||||
|
if (*allSetting)["webDomain"] != "" {
|
||||||
|
fmt.Println("\tPanel Domain:\t", (*allSetting)["webDomain"])
|
||||||
|
}
|
||||||
|
if (*allSetting)["webURI"] != "" {
|
||||||
|
fmt.Println("\tPanel URI:\t", (*allSetting)["webURI"])
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Current subscription settings:")
|
||||||
|
fmt.Println("\tSub port:\t", (*allSetting)["subPort"])
|
||||||
|
fmt.Println("\tSub path:\t", (*allSetting)["subPath"])
|
||||||
|
if (*allSetting)["subListen"] != "" {
|
||||||
|
fmt.Println("\tSub IP:\t", (*allSetting)["subListen"])
|
||||||
|
}
|
||||||
|
if (*allSetting)["subDomain"] != "" {
|
||||||
|
fmt.Println("\tSub Domain:\t", (*allSetting)["subDomain"])
|
||||||
|
}
|
||||||
|
if (*allSetting)["subURI"] != "" {
|
||||||
|
fmt.Println("\tSub URI:\t", (*allSetting)["subURI"])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,7 +59,7 @@ func GetBinFolderPath() string {
|
|||||||
func GetDBFolderPath() string {
|
func GetDBFolderPath() string {
|
||||||
dbFolderPath := os.Getenv("SUI_DB_FOLDER")
|
dbFolderPath := os.Getenv("SUI_DB_FOLDER")
|
||||||
if dbFolderPath == "" {
|
if dbFolderPath == "" {
|
||||||
dbFolderPath = "db"
|
dbFolderPath = "/usr/local/s-ui/db"
|
||||||
}
|
}
|
||||||
return dbFolderPath
|
return dbFolderPath
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
0.0.1
|
0.0.2
|
||||||
+17
-20
@@ -1,23 +1,23 @@
|
|||||||
module s-ui
|
module s-ui
|
||||||
|
|
||||||
go 1.21.5
|
go 1.22.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-contrib/gzip v0.0.6
|
github.com/gin-contrib/gzip v0.0.6
|
||||||
github.com/gin-contrib/sessions v0.0.5
|
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||||
|
github.com/v2fly/v2ray-core/v5 v5.13.0
|
||||||
gorm.io/driver/sqlite v1.5.5
|
gorm.io/driver/sqlite v1.5.5
|
||||||
gorm.io/gorm v1.25.7
|
gorm.io/gorm v1.25.7
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/adrg/xdg v0.4.0 // indirect
|
github.com/adrg/xdg v0.4.0 // indirect
|
||||||
github.com/bytedance/sonic v1.10.2 // indirect
|
github.com/bytedance/sonic v1.11.1 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
@@ -26,42 +26,39 @@ require (
|
|||||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
github.com/gorilla/sessions v1.2.2 // indirect
|
github.com/gorilla/sessions v1.2.2 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pires/go-proxyproto v0.7.0 // indirect
|
github.com/pires/go-proxyproto v0.7.0 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
github.com/tklauser/go-sysconf v0.3.13 // indirect
|
||||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
github.com/tklauser/numcpus v0.7.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
golang.org/x/arch v0.7.0 // indirect
|
golang.org/x/arch v0.7.0 // indirect
|
||||||
golang.org/x/crypto v0.18.0 // indirect
|
golang.org/x/crypto v0.20.0 // indirect
|
||||||
golang.org/x/mod v0.14.0 // indirect
|
golang.org/x/net v0.21.0 // indirect
|
||||||
golang.org/x/net v0.20.0 // indirect
|
golang.org/x/sys v0.17.0 // indirect
|
||||||
golang.org/x/sync v0.6.0 // indirect
|
|
||||||
golang.org/x/sys v0.16.0 // indirect
|
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
golang.org/x/tools v0.17.0 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 // indirect
|
|
||||||
google.golang.org/protobuf v1.32.0 // indirect
|
google.golang.org/protobuf v1.32.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Calidity/gin-sessions v1.3.1
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.17.0 // indirect
|
github.com/go-playground/validator/v10 v10.18.0 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
|
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/shirou/gopsutil/v3 v3.24.1
|
github.com/shirou/gopsutil/v3 v3.24.1
|
||||||
github.com/v2fly/v2ray-core/v5 v5.13.0
|
google.golang.org/grpc v1.62.0
|
||||||
google.golang.org/grpc v1.61.0
|
|
||||||
)
|
)
|
||||||
|
|||||||
+36
-27
@@ -1,3 +1,5 @@
|
|||||||
|
github.com/Calidity/gin-sessions v1.3.1 h1:nF3dCBWa7TZ4j26iYLwGRmzZy9YODhWoOS3fmi+snyE=
|
||||||
|
github.com/Calidity/gin-sessions v1.3.1/go.mod h1:I0+QE6qkO50TeN/n6If6novvxHk4Isvr23U8EdvPdns=
|
||||||
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
||||||
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
||||||
github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1 h1:+JkXLHME8vLJafGhOH4aoV2Iu8bR55nU6iKMVfYVLjY=
|
github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1 h1:+JkXLHME8vLJafGhOH4aoV2Iu8bR55nU6iKMVfYVLjY=
|
||||||
@@ -12,8 +14,8 @@ github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28
|
|||||||
github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE=
|
github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE=
|
||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
||||||
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
|
github.com/bytedance/sonic v1.11.1 h1:JC0+6c9FoWYYxakaoa+c5QTtJeiSZNeByOBhXtAFSn4=
|
||||||
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
github.com/bytedance/sonic v1.11.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
||||||
@@ -37,8 +39,6 @@ github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXb
|
|||||||
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
|
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
|
||||||
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
||||||
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
||||||
github.com/gin-contrib/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE=
|
|
||||||
github.com/gin-contrib/sessions v0.0.5/go.mod h1:vYAuaUPqie3WUSsft6HUlCjlwwoJQs97miaG2+7neKY=
|
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||||
@@ -48,8 +48,9 @@ github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
|
|||||||
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||||
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
|
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||||
|
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
@@ -60,8 +61,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl
|
|||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||||
github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74=
|
github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U=
|
||||||
github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
@@ -105,8 +106,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
|||||||
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
|
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
|
||||||
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/klauspost/reedsolomon v1.11.7 h1:9uaHU0slncktTEEg4+7Vl7q7XUNMBUOK4R9gnKhMjAU=
|
github.com/klauspost/reedsolomon v1.11.7 h1:9uaHU0slncktTEEg4+7Vl7q7XUNMBUOK4R9gnKhMjAU=
|
||||||
github.com/klauspost/reedsolomon v1.11.7/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A=
|
github.com/klauspost/reedsolomon v1.11.7/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A=
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
@@ -121,8 +122,9 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|||||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a h1:3Bm7EwfUQUvhNeKIkUct/gl9eod1TcXuj8stxvi/GoI=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
|
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
|
||||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
|
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
@@ -165,8 +167,9 @@ github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP
|
|||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
||||||
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||||
github.com/quic-go/quic-go v0.40.0 h1:GYd1iznlKm7dpHD7pOVpUvItgMPo/jrMgDWZhMCecqw=
|
github.com/quic-go/quic-go v0.40.0 h1:GYd1iznlKm7dpHD7pOVpUvItgMPo/jrMgDWZhMCecqw=
|
||||||
@@ -188,6 +191,7 @@ github.com/shirou/gopsutil/v3 v3.24.1 h1:R3t6ondCEvmARp3wxODhXMTLC/klMa87h2PHUw5
|
|||||||
github.com/shirou/gopsutil/v3 v3.24.1/go.mod h1:UU7a2MSBQa+kW1uuDq8DeEBS8kmrnQwsv2b5O513rwU=
|
github.com/shirou/gopsutil/v3 v3.24.1/go.mod h1:UU7a2MSBQa+kW1uuDq8DeEBS8kmrnQwsv2b5O513rwU=
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||||
|
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||||
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
@@ -200,10 +204,12 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
|||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
|
||||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
|
||||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||||
|
github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4=
|
||||||
|
github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||||
@@ -224,8 +230,9 @@ github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432 h1:I/ATawgO2Rer
|
|||||||
github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432/go.mod h1:QN7Go2ftTVfx0aCTh9RXHV8pkpi0FtmbwQw40dy61wQ=
|
github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432/go.mod h1:QN7Go2ftTVfx0aCTh9RXHV8pkpi0FtmbwQw40dy61wQ=
|
||||||
github.com/xtaci/smux v1.5.24 h1:77emW9dtnOxxOQ5ltR+8BbsX1kzcOxQ5gB+aaV9hXOY=
|
github.com/xtaci/smux v1.5.24 h1:77emW9dtnOxxOQ5ltR+8BbsX1kzcOxQ5gB+aaV9hXOY=
|
||||||
github.com/xtaci/smux v1.5.24/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
github.com/xtaci/smux v1.5.24/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
|
||||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
go.starlark.net v0.0.0-20230612165344-9532f5667272 h1:2/wtqS591wZyD2OsClsVBKRPEvBsQt/Js+fsCiYhwu8=
|
go.starlark.net v0.0.0-20230612165344-9532f5667272 h1:2/wtqS591wZyD2OsClsVBKRPEvBsQt/Js+fsCiYhwu8=
|
||||||
go.starlark.net v0.0.0-20230612165344-9532f5667272/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
|
go.starlark.net v0.0.0-20230612165344-9532f5667272/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
|
||||||
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
|
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
|
||||||
@@ -236,15 +243,15 @@ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUu
|
|||||||
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
||||||
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=
|
||||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
|
||||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
|
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
|
||||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -254,12 +261,14 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||||
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
@@ -268,13 +277,13 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|||||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
|
||||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 h1:FSL3lRCkhaPFxqi0s9o+V4UI2WTzAVOvkgbd4kVV4Wg=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014/go.mod h1:SaPjaZGWb0lPqs6Ittu0spdfrOArqji4ZdeP5IC/9N4=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
||||||
google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
|
google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
|
||||||
google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
|
google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
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.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
|||||||
+11
-1
@@ -5,10 +5,11 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"s-ui/app"
|
"s-ui/app"
|
||||||
|
"s-ui/cmd"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func runApp() {
|
||||||
app := app.NewApp()
|
app := app.NewApp()
|
||||||
|
|
||||||
err := app.Init()
|
err := app.Init()
|
||||||
@@ -36,3 +37,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) < 2 {
|
||||||
|
runApp()
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
cmd.ParseCmd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ var defaultValueMap = map[string]string{
|
|||||||
"webSecret": common.Random(32),
|
"webSecret": common.Random(32),
|
||||||
"webCertFile": "",
|
"webCertFile": "",
|
||||||
"webKeyFile": "",
|
"webKeyFile": "",
|
||||||
|
"webPath": "/app/",
|
||||||
|
"webURI": "",
|
||||||
"sessionMaxAge": "0",
|
"sessionMaxAge": "0",
|
||||||
"timeLocation": "Asia/Tehran",
|
"timeLocation": "Asia/Tehran",
|
||||||
"subListen": "",
|
"subListen": "",
|
||||||
@@ -67,6 +69,11 @@ func (s *SettingService) GetAllSetting() (*map[string]string, error) {
|
|||||||
return &allSetting, nil
|
return &allSetting, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) ResetSettings() error {
|
||||||
|
db := database.GetDB()
|
||||||
|
return db.Where("1 = 1").Delete(model.Setting{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SettingService) getSetting(key string) (*model.Setting, error) {
|
func (s *SettingService) getSetting(key string) (*model.Setting, error) {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
setting := &model.Setting{}
|
setting := &model.Setting{}
|
||||||
@@ -158,6 +165,30 @@ func (s *SettingService) GetKeyFile() (string, error) {
|
|||||||
return s.getString("webKeyFile")
|
return s.getString("webKeyFile")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetWebPath() (string, error) {
|
||||||
|
webPath, err := s.getString("webPath")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(webPath, "/") {
|
||||||
|
webPath = "/" + webPath
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(webPath, "/") {
|
||||||
|
webPath += "/"
|
||||||
|
}
|
||||||
|
return webPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) SetWebPath(webPath string) error {
|
||||||
|
if !strings.HasPrefix(webPath, "/") {
|
||||||
|
webPath = "/" + webPath
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(webPath, "/") {
|
||||||
|
webPath += "/"
|
||||||
|
}
|
||||||
|
return s.setString("webPath", webPath)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SettingService) GetSecret() ([]byte, error) {
|
func (s *SettingService) GetSecret() ([]byte, error) {
|
||||||
secret, err := s.getString("webSecret")
|
secret, err := s.getString("webSecret")
|
||||||
if secret == defaultValueMap["webSecret"] {
|
if secret == defaultValueMap["webSecret"] {
|
||||||
@@ -195,6 +226,10 @@ func (s *SettingService) GetSubPort() (int, error) {
|
|||||||
return s.getInt("subPort")
|
return s.getInt("subPort")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) SetSubPort(subPort int) error {
|
||||||
|
return s.setInt("subPort", subPort)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SettingService) GetSubPath() (string, error) {
|
func (s *SettingService) GetSubPath() (string, error) {
|
||||||
subPath, err := s.getString("subPath")
|
subPath, err := s.getString("subPath")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -209,6 +244,16 @@ func (s *SettingService) GetSubPath() (string, error) {
|
|||||||
return subPath, nil
|
return subPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) SetSubPath(subPath string) error {
|
||||||
|
if !strings.HasPrefix(subPath, "/") {
|
||||||
|
subPath = "/" + subPath
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(subPath, "/") {
|
||||||
|
subPath += "/"
|
||||||
|
}
|
||||||
|
return s.setString("subPath", subPath)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SettingService) GetSubDomain() (string, error) {
|
func (s *SettingService) GetSubDomain() (string, error) {
|
||||||
return s.getString("subDomain")
|
return s.getString("subDomain")
|
||||||
}
|
}
|
||||||
@@ -278,6 +323,17 @@ func (s *SettingService) Save(tx *gorm.DB, changes []model.Changes) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Correct Pathes start and ends with `/`
|
||||||
|
if key == "webPath" ||
|
||||||
|
key == "subPath" {
|
||||||
|
if !strings.HasPrefix(obj, "/") {
|
||||||
|
obj = "/" + obj
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(obj, "/") {
|
||||||
|
obj += "/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = tx.Model(model.Setting{}).Where("key = ?", key).Update("value", obj).Error
|
err = tx.Model(model.Setting{}).Where("key = ?", key).Update("value", obj).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
+57
-1
@@ -13,6 +13,40 @@ import (
|
|||||||
type UserService struct {
|
type UserService struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *UserService) GetFirstUser() (*model.User, error) {
|
||||||
|
db := database.GetDB()
|
||||||
|
|
||||||
|
user := &model.User{}
|
||||||
|
err := db.Model(model.User{}).
|
||||||
|
First(user).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UserService) UpdateFirstUser(username string, password string) error {
|
||||||
|
if username == "" {
|
||||||
|
return common.NewError("username can not be empty")
|
||||||
|
} else if password == "" {
|
||||||
|
return common.NewError("password can not be empty")
|
||||||
|
}
|
||||||
|
db := database.GetDB()
|
||||||
|
user := &model.User{}
|
||||||
|
err := db.Model(model.User{}).First(user).Error
|
||||||
|
if database.IsNotFound(err) {
|
||||||
|
user.Username = username
|
||||||
|
user.Password = password
|
||||||
|
return db.Model(model.User{}).Create(user).Error
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user.Username = username
|
||||||
|
user.Password = password
|
||||||
|
return db.Save(user).Error
|
||||||
|
}
|
||||||
|
|
||||||
func (s *UserService) Login(username string, password string, remoteIP string) (string, error) {
|
func (s *UserService) Login(username string, password string, remoteIP string) (string, error) {
|
||||||
user := s.CheckUser(username, password, remoteIP)
|
user := s.CheckUser(username, password, remoteIP)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
@@ -36,7 +70,7 @@ func (s *UserService) CheckUser(username string, password string, remoteIP strin
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
lastLoginTxt := time.Now().Format("2006-01-02 15:04:05") + "-" + remoteIP
|
lastLoginTxt := time.Now().Format("2006-01-02 15:04:05") + " " + remoteIP
|
||||||
err = db.Model(model.User{}).
|
err = db.Model(model.User{}).
|
||||||
Where("username = ?", username).
|
Where("username = ?", username).
|
||||||
Update("last_logins", &lastLoginTxt).Error
|
Update("last_logins", &lastLoginTxt).Error
|
||||||
@@ -45,3 +79,25 @@ func (s *UserService) CheckUser(username string, password string, remoteIP strin
|
|||||||
}
|
}
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *UserService) GetUsers() (*[]model.User, error) {
|
||||||
|
var users []model.User
|
||||||
|
db := database.GetDB()
|
||||||
|
err := db.Model(model.User{}).Select("id,username,last_logins").Scan(&users).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UserService) ChangePass(id string, oldPass string, newUser string, newPass string) error {
|
||||||
|
db := database.GetDB()
|
||||||
|
user := &model.User{}
|
||||||
|
err := db.Model(model.User{}).Where("id = ? AND password = ?", id, oldPass).First(user).Error
|
||||||
|
if err != nil || database.IsNotFound(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user.Username = newUser
|
||||||
|
user.Password = newPass
|
||||||
|
return db.Save(user).Error
|
||||||
|
}
|
||||||
|
|||||||
+31
-13
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"embed"
|
"embed"
|
||||||
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net"
|
"net"
|
||||||
@@ -17,9 +18,9 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
sessions "github.com/Calidity/gin-sessions"
|
||||||
|
"github.com/Calidity/gin-sessions/cookie"
|
||||||
"github.com/gin-contrib/gzip"
|
"github.com/gin-contrib/gzip"
|
||||||
"github.com/gin-contrib/sessions"
|
|
||||||
"github.com/gin-contrib/sessions/cookie"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,6 +54,19 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
|||||||
|
|
||||||
engine := gin.Default()
|
engine := gin.Default()
|
||||||
|
|
||||||
|
// Load the HTML template
|
||||||
|
t := template.New("").Funcs(engine.FuncMap)
|
||||||
|
template, err := t.ParseFS(content, "html/index.html")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
engine.SetHTMLTemplate(template)
|
||||||
|
|
||||||
|
base_url, err := s.settingService.GetWebPath()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
webDomain, err := s.settingService.GetWebDomain()
|
webDomain, err := s.settingService.GetWebDomain()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -68,10 +82,11 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
engine.Use(gzip.Gzip(gzip.DefaultCompression))
|
engine.Use(gzip.Gzip(gzip.DefaultCompression))
|
||||||
assetsBasePath := "/assets/"
|
assetsBasePath := base_url + "assets/"
|
||||||
|
|
||||||
store := cookie.NewStore(secret)
|
store := cookie.NewStore(secret)
|
||||||
engine.Use(sessions.Sessions("session", store))
|
engine.Use(sessions.Sessions("s-ui", store))
|
||||||
|
|
||||||
engine.Use(func(c *gin.Context) {
|
engine.Use(func(c *gin.Context) {
|
||||||
uri := c.Request.RequestURI
|
uri := c.Request.RequestURI
|
||||||
if strings.HasPrefix(uri, assetsBasePath) {
|
if strings.HasPrefix(uri, assetsBasePath) {
|
||||||
@@ -87,26 +102,29 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
|||||||
|
|
||||||
engine.StaticFS(assetsBasePath, http.FS(assetsFS))
|
engine.StaticFS(assetsBasePath, http.FS(assetsFS))
|
||||||
|
|
||||||
group_api := engine.Group("/api")
|
group_api := engine.Group(base_url + "api")
|
||||||
api.NewAPIHandler(group_api)
|
api.NewAPIHandler(group_api)
|
||||||
|
|
||||||
// Serve index.html as the entry point
|
// Serve index.html as the entry point
|
||||||
// Handle all other routes by serving index.html
|
// Handle all other routes by serving index.html
|
||||||
engine.NoRoute(func(c *gin.Context) {
|
engine.NoRoute(func(c *gin.Context) {
|
||||||
if c.Request.URL.Path != "/login" && !api.IsLogin(c) {
|
if c.Request.URL.Path == strings.TrimSuffix(base_url, "/") {
|
||||||
c.Redirect(http.StatusTemporaryRedirect, "/login")
|
c.Redirect(http.StatusTemporaryRedirect, base_url)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if c.Request.URL.Path == "/login" && api.IsLogin(c) {
|
if !strings.HasPrefix(c.Request.URL.Path, base_url) {
|
||||||
c.Redirect(http.StatusTemporaryRedirect, "/")
|
c.String(404, "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data, err := content.ReadFile("html/index.html")
|
if c.Request.URL.Path != base_url+"login" && !api.IsLogin(c) {
|
||||||
if err != nil {
|
c.Redirect(http.StatusTemporaryRedirect, base_url+"login")
|
||||||
c.String(http.StatusInternalServerError, "Internal Server Error")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Data(http.StatusOK, "text/html", data)
|
if c.Request.URL.Path == base_url+"login" && api.IsLogin(c) {
|
||||||
|
c.Redirect(http.StatusTemporaryRedirect, base_url)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.HTML(http.StatusOK, "index.html", gin.H{"BASE_URL": base_url})
|
||||||
})
|
})
|
||||||
|
|
||||||
return engine, nil
|
return engine, nil
|
||||||
|
|||||||
@@ -5,6 +5,12 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" href="assets/favicon.ico" />
|
<link rel="icon" href="assets/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<script>
|
||||||
|
window.BASE_URL = "{{ .BASE_URL }}"
|
||||||
|
|
||||||
|
// Dev Mode
|
||||||
|
if (window.BASE_URL.charAt(0) === '{') window.BASE_URL = "/app/"
|
||||||
|
</script>
|
||||||
<title>S-UI</title>
|
<title>S-UI</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|||||||
@@ -143,9 +143,9 @@ export default {
|
|||||||
usePath: 0,
|
usePath: 0,
|
||||||
defaults: defaultInTls,
|
defaults: defaultInTls,
|
||||||
alpn: [
|
alpn: [
|
||||||
{ title: "H3", value: 'HTTP/3' },
|
{ title: "H3", value: 'h3' },
|
||||||
{ title: "H2", value: 'HTTP/2' },
|
{ title: "H2", value: 'h2' },
|
||||||
{ title: "Http1.1", value: 'HTTP/1.1' },
|
{ title: "Http/1.1", value: 'http/1.1' },
|
||||||
],
|
],
|
||||||
tlsVersions: [ '1.0', '1.1', '1.2', '1.3' ],
|
tlsVersions: [ '1.0', '1.1', '1.2', '1.3' ],
|
||||||
cipher_suites: [
|
cipher_suites: [
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
<v-col cols="12" sm="6" md="3" v-for="i in reloadItems" :key="i">
|
<v-col cols="12" sm="6" md="3" v-for="i in reloadItems" :key="i">
|
||||||
<v-card class="rounded-lg" variant="outlined" height="200px"
|
<v-card class="rounded-lg" variant="outlined" height="200px"
|
||||||
:title="menuItems.flatMap(cat => cat.value).find(m => m.value == i)?.title">
|
:title="menuItems.flatMap(cat => cat.value).find(m => m.value == i)?.title">
|
||||||
<v-card-text style="padding: 0 16px;">
|
<v-card-text style="padding: 0 16px;" align="center" justify="center">
|
||||||
<Gauge :tilesData="tilesData" :type="i" v-if="i.charAt(0) == 'g'" />
|
<Gauge :tilesData="tilesData" :type="i" v-if="i.charAt(0) == 'g'" />
|
||||||
<History :tilesData="tilesData" :type="i" v-if="i.charAt(0) == 'h'" />
|
<History :tilesData="tilesData" :type="i" v-if="i.charAt(0) == 'h'" />
|
||||||
<template v-if="i == 'i-sys'">
|
<template v-if="i == 'i-sys'">
|
||||||
@@ -184,7 +184,7 @@ const reloadItems = computed({
|
|||||||
|
|
||||||
const reloadData = async () => {
|
const reloadData = async () => {
|
||||||
const request = [...new Set(reloadItems.value.map(r => r.split('-')[1]))]
|
const request = [...new Set(reloadItems.value.map(r => r.split('-')[1]))]
|
||||||
const data = await HttpUtils.get('/api/status',{ r: request.join(',')})
|
const data = await HttpUtils.get('api/status',{ r: request.join(',')})
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
tilesData.value = data.obj
|
tilesData.value = data.obj
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,15 @@
|
|||||||
v-model="transport.path">
|
v-model="transport.path">
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
<v-col cols="12" sm="6" md="4">
|
||||||
|
<v-text-field
|
||||||
|
:label="$t('transport.host')"
|
||||||
|
hide-details
|
||||||
|
v-model="host">
|
||||||
|
</v-text-field>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-row>
|
||||||
<v-col cols="12" sm="6" md="4">
|
<v-col cols="12" sm="6" md="4">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
label="Max Early Data"
|
label="Max Early Data"
|
||||||
@@ -42,10 +51,16 @@ export default {
|
|||||||
get() { return this.WS.max_early_data ? this.WS.max_early_data : '' },
|
get() { return this.WS.max_early_data ? this.WS.max_early_data : '' },
|
||||||
set(newValue:number) { this.$props.transport.max_early_data = newValue != 0 ? newValue : undefined }
|
set(newValue:number) { this.$props.transport.max_early_data = newValue != 0 ? newValue : undefined }
|
||||||
},
|
},
|
||||||
|
host: {
|
||||||
|
get() { return this.WS.headers?.Host ? this.WS.headers.Host : '' },
|
||||||
|
set(newValue:string) {
|
||||||
|
this.$props.transport.headers = newValue != "" ? { Host: newValue } : undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.WS.early_data_header_name = 'Sec-WebSocket-Protocol'
|
this.WS.early_data_header_name ??= 'Sec-WebSocket-Protocol'
|
||||||
this.WS.path = '/'
|
this.WS.path ??= '/'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-app-bar :elevation="5">
|
<v-app-bar :elevation="5">
|
||||||
<v-icon v-if="isMobile" icon="mdi-menu" @click="$emit('toggleDrawer')" />
|
<v-icon v-if="isMobile" icon="mdi-menu" @click="$emit('toggleDrawer')" />
|
||||||
|
<span v-else style="width: 24px"></span>
|
||||||
<v-app-bar-title :text="$t(<string>$router.currentRoute.value.name)" class="align-center text-center " />
|
<v-app-bar-title :text="$t(<string>$router.currentRoute.value.name)" class="align-center text-center " />
|
||||||
<v-btn prepend-icon="mdi-content-save" v-if="stateChange" :text="$t('actions.save')" @click="saveChanges"></v-btn>
|
<v-btn prepend-icon="mdi-content-save" v-if="stateChange" :text="$t('actions.save')" @click="saveChanges"></v-btn>
|
||||||
<v-icon icon="mdi-theme-light-dark" @click="toggleTheme()" style="margin: 0 10px;"></v-icon>
|
<v-icon icon="mdi-theme-light-dark" @click="toggleTheme()" style="margin: 0 10px;"></v-icon>
|
||||||
|
|||||||
@@ -25,3 +25,10 @@ const isMobile = computed( ():boolean =>{
|
|||||||
return smAndDown.value
|
return smAndDown.value
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.v-card-subtitle {
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 1px solid gray;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
<v-list-item prepend-icon="mdi-logout" :title="$t('menu.logout')" @click="logout"></v-list-item>
|
<v-list-item prepend-icon="mdi-logout" :title="$t('menu.logout')" @click="Logout"></v-list-item>
|
||||||
</template>
|
</template>
|
||||||
</v-navigation-drawer>
|
</v-navigation-drawer>
|
||||||
</template>
|
</template>
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
import HttpUtil from '@/plugins/httputil'
|
import { logout } from '@/plugins/httputil'
|
||||||
|
|
||||||
const props = defineProps(['isMobile','displayDrawer'])
|
const props = defineProps(['isMobile','displayDrawer'])
|
||||||
|
|
||||||
@@ -55,13 +55,11 @@ const menu = [
|
|||||||
{ title: 'pages.outbounds', icon: 'mdi-cloud-upload', path: '/outbounds' },
|
{ title: 'pages.outbounds', icon: 'mdi-cloud-upload', path: '/outbounds' },
|
||||||
{ title: 'pages.rules', icon: 'mdi-routes', path: '/rules' },
|
{ title: 'pages.rules', icon: 'mdi-routes', path: '/rules' },
|
||||||
{ title: 'pages.basics', icon: 'mdi-application-cog', path: '/basics' },
|
{ title: 'pages.basics', icon: 'mdi-application-cog', path: '/basics' },
|
||||||
|
{ title: 'pages.admins', icon: 'mdi-account-tie', path: '/admins' },
|
||||||
{ title: 'pages.settings', icon: 'mdi-cog', path: '/settings' },
|
{ title: 'pages.settings', icon: 'mdi-cog', path: '/settings' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const logout = async () => {
|
const Logout = async () => {
|
||||||
const response = await HttpUtil.get('/api/logout')
|
logout()
|
||||||
if(response.success){
|
|
||||||
router.push('/login')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
<template>
|
||||||
|
<v-dialog transition="dialog-bottom-transition" width="400">
|
||||||
|
<v-card class="rounded-lg">
|
||||||
|
<v-card-title>
|
||||||
|
{{ $t('admin.changeCred') + " " + user.username }}
|
||||||
|
</v-card-title>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-card-text>
|
||||||
|
<v-row>
|
||||||
|
<v-col>
|
||||||
|
<v-text-field v-model="newData.oldPass" :label="$t('admin.oldPass')" :rules="passwordRules" type="password" required></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-row>
|
||||||
|
<v-col>
|
||||||
|
<v-text-field v-model="newData.newUsername" :label="$t('admin.newUname')" :rules="usernameRules" required></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-row>
|
||||||
|
<v-col>
|
||||||
|
<v-text-field v-model="newData.newPass" :label="$t('admin.newPass')" :rules="passwordRules" type="password" required></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn
|
||||||
|
color="blue-darken-1"
|
||||||
|
variant="outlined"
|
||||||
|
@click="closeModal"
|
||||||
|
>
|
||||||
|
{{ $t('actions.close') }}
|
||||||
|
</v-btn>
|
||||||
|
<v-btn
|
||||||
|
color="blue-darken-1"
|
||||||
|
variant="tonal"
|
||||||
|
@click="saveChanges"
|
||||||
|
>
|
||||||
|
{{ $t('actions.save') }}
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { i18n } from '@/locales'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: ['visible', 'user'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
newData: {
|
||||||
|
id: 0,
|
||||||
|
oldPass: "",
|
||||||
|
newUsername: "",
|
||||||
|
newPass: ""
|
||||||
|
},
|
||||||
|
usernameRules: [
|
||||||
|
(value: string) => {
|
||||||
|
if (value?.length > 0) return true
|
||||||
|
return i18n.global.t('login.unRules')
|
||||||
|
},
|
||||||
|
],
|
||||||
|
passwordRules: [
|
||||||
|
(value: string) => {
|
||||||
|
if (value?.length > 0) return true
|
||||||
|
return i18n.global.t('login.pwRules')
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
resetData() {
|
||||||
|
this.newData.id = this.$props.user.id
|
||||||
|
this.newData.oldPass = ""
|
||||||
|
this.newData.newUsername = ""
|
||||||
|
this.newData.newPass = ""
|
||||||
|
},
|
||||||
|
closeModal() {
|
||||||
|
this.resetData() // reset
|
||||||
|
this.$emit('close')
|
||||||
|
},
|
||||||
|
saveChanges() {
|
||||||
|
if (this.newData.oldPass == '' || this.newData.newUsername == '' || this.newData.newPass == '') return
|
||||||
|
this.$emit('save', this.newData)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
visible(newValue) { if (newValue) {
|
||||||
|
this.resetData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -101,7 +101,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
async loadData(limit: number) {
|
async loadData(limit: number) {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
const data = await HttpUtils.get('/api/stats', { resource: this.resource, tag: this.tag, limit: limit })
|
const data = await HttpUtils.get('api/stats', { resource: this.resource, tag: this.tag, limit: limit })
|
||||||
if (data.success && data.obj) {
|
if (data.success && data.obj) {
|
||||||
const obj = <any[]>data.obj
|
const obj = <any[]>data.obj
|
||||||
const l = String(i18n.global.locale) == 'fa' ? "fa-IR" : "en-US"
|
const l = String(i18n.global.locale) == 'fa' ? "fa-IR" : "en-US"
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export default {
|
|||||||
network: "Network",
|
network: "Network",
|
||||||
copyToClipboard: "Copy to clipboard",
|
copyToClipboard: "Copy to clipboard",
|
||||||
noData: "No data!",
|
noData: "No data!",
|
||||||
|
invalidLogin: "Invalid Login!",
|
||||||
online: "Online",
|
online: "Online",
|
||||||
pages: {
|
pages: {
|
||||||
login: "Login",
|
login: "Login",
|
||||||
@@ -26,6 +27,7 @@ export default {
|
|||||||
clients: "Clients",
|
clients: "Clients",
|
||||||
rules: "Rules",
|
rules: "Rules",
|
||||||
basics: "Basics",
|
basics: "Basics",
|
||||||
|
admins: "Admins",
|
||||||
settings: "Settings",
|
settings: "Settings",
|
||||||
},
|
},
|
||||||
main: {
|
main: {
|
||||||
@@ -69,10 +71,12 @@ export default {
|
|||||||
del: "Delete",
|
del: "Delete",
|
||||||
save: "Save",
|
save: "Save",
|
||||||
update: "Update",
|
update: "Update",
|
||||||
|
submit: "Submit",
|
||||||
close: "Close",
|
close: "Close",
|
||||||
restartApp: "Restart App",
|
restartApp: "Restart App",
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
|
title: "Login",
|
||||||
username: "Username",
|
username: "Username",
|
||||||
unRules: "Username can not be empty",
|
unRules: "Username can not be empty",
|
||||||
password: "Password",
|
password: "Password",
|
||||||
@@ -81,14 +85,22 @@ export default {
|
|||||||
menu: {
|
menu: {
|
||||||
logout: "Logout",
|
logout: "Logout",
|
||||||
},
|
},
|
||||||
|
admin: {
|
||||||
|
changeCred: "Change credentials",
|
||||||
|
oldPass: "Current Password",
|
||||||
|
newUname: "New Username",
|
||||||
|
newPass: "New Password",
|
||||||
|
},
|
||||||
setting: {
|
setting: {
|
||||||
interface: "Interface",
|
interface: "Interface",
|
||||||
sub: "Subscription",
|
sub: "Subscription",
|
||||||
addr: "Address",
|
addr: "Address",
|
||||||
port: "Port",
|
port: "Port",
|
||||||
|
webPath: "Base URI",
|
||||||
domain: "Domain",
|
domain: "Domain",
|
||||||
sslKey: "SSL Key Path",
|
sslKey: "SSL Key Path",
|
||||||
sslCert: "SSL Certificate Path",
|
sslCert: "SSL Certificate Path",
|
||||||
|
webUri: "Panel URI",
|
||||||
sessionAge: "Session Maximum Age",
|
sessionAge: "Session Maximum Age",
|
||||||
timeLoc: "Timezone Location",
|
timeLoc: "Timezone Location",
|
||||||
subEncode: "Enable Encoding",
|
subEncode: "Enable Encoding",
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export default {
|
|||||||
network: "شبکه",
|
network: "شبکه",
|
||||||
copyToClipboard: "کپی در حافظه",
|
copyToClipboard: "کپی در حافظه",
|
||||||
noData: "بدون داده!",
|
noData: "بدون داده!",
|
||||||
|
invalidLogin: "ورود نامعتبر!",
|
||||||
online: "آنلاین",
|
online: "آنلاین",
|
||||||
pages: {
|
pages: {
|
||||||
login: "ورود",
|
login: "ورود",
|
||||||
@@ -26,6 +27,7 @@ export default {
|
|||||||
clients: "کاربران",
|
clients: "کاربران",
|
||||||
rules: "قوانین",
|
rules: "قوانین",
|
||||||
basics: "ترازها",
|
basics: "ترازها",
|
||||||
|
admins: "ادمینها",
|
||||||
settings: "پیکربندی",
|
settings: "پیکربندی",
|
||||||
},
|
},
|
||||||
main: {
|
main: {
|
||||||
@@ -69,10 +71,12 @@ export default {
|
|||||||
del: "حذف",
|
del: "حذف",
|
||||||
save: "ذخیره",
|
save: "ذخیره",
|
||||||
update: "بروزرسانی",
|
update: "بروزرسانی",
|
||||||
|
submit: "ارسال",
|
||||||
close: "بستن",
|
close: "بستن",
|
||||||
restartApp: "ریستارت پنل",
|
restartApp: "ریستارت پنل",
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
|
title: "ورود",
|
||||||
username: "نام کاربری",
|
username: "نام کاربری",
|
||||||
unRules: "نام کاربری نمیتواند خالی باشد",
|
unRules: "نام کاربری نمیتواند خالی باشد",
|
||||||
password: "کلمه عبور",
|
password: "کلمه عبور",
|
||||||
@@ -81,14 +85,22 @@ export default {
|
|||||||
menu: {
|
menu: {
|
||||||
logout: "خروج",
|
logout: "خروج",
|
||||||
},
|
},
|
||||||
|
admin: {
|
||||||
|
changeCred: "ویرایش دادهها",
|
||||||
|
oldPass: "رمز کنونی",
|
||||||
|
newUname: "نام کاربری جدید",
|
||||||
|
newPass: "رمز جدید",
|
||||||
|
},
|
||||||
setting: {
|
setting: {
|
||||||
interface: "نما",
|
interface: "نما",
|
||||||
sub: "سابسکریپشن",
|
sub: "سابسکریپشن",
|
||||||
addr: "آدرس",
|
addr: "آدرس",
|
||||||
port: "پورت",
|
port: "پورت",
|
||||||
|
webPath: "مسیر پایه",
|
||||||
domain: "دامنه",
|
domain: "دامنه",
|
||||||
sslKey: "مسیر فایل کلید",
|
sslKey: "مسیر فایل کلید",
|
||||||
sslCert: "مسیر فایل گواهی",
|
sslCert: "مسیر فایل گواهی",
|
||||||
|
webUri: "آدرس نهایی پنل",
|
||||||
sessionAge: "بیشینه زمان لاگین ماندن",
|
sessionAge: "بیشینه زمان لاگین ماندن",
|
||||||
timeLoc: "منطقه زمانی",
|
timeLoc: "منطقه زمانی",
|
||||||
subEncode: "رمزگذاری",
|
subEncode: "رمزگذاری",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
import en from './en'
|
import en from './en'
|
||||||
import fa from './fa'
|
import fa from './fa'
|
||||||
|
import zhcn from './zhcn'
|
||||||
|
|
||||||
|
|
||||||
export const i18n = createI18n({
|
export const i18n = createI18n({
|
||||||
@@ -10,10 +11,12 @@ export const i18n = createI18n({
|
|||||||
messages: {
|
messages: {
|
||||||
en,
|
en,
|
||||||
fa,
|
fa,
|
||||||
|
zhcn
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export const languages = [
|
export const languages = [
|
||||||
{ title: 'English', value: 'en' },
|
{ title: 'English', value: 'en' },
|
||||||
{ title: 'فارسی', value: 'fa' },
|
{ title: 'فارسی', value: 'fa' },
|
||||||
|
{ title: '简体中文', value: 'zhcn' },
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,172 @@
|
|||||||
|
export default {
|
||||||
|
message: "欢迎",
|
||||||
|
success: "成功",
|
||||||
|
failed: "失败",
|
||||||
|
enable: "启用",
|
||||||
|
disable: "禁用",
|
||||||
|
loading: "加载中...",
|
||||||
|
confirm: "是否确定?",
|
||||||
|
yes: "确认",
|
||||||
|
no: "取消",
|
||||||
|
unlimited: "无限",
|
||||||
|
remained: "剩余",
|
||||||
|
type: "类型",
|
||||||
|
submit: "提交",
|
||||||
|
reset: "重置",
|
||||||
|
now: "当前",
|
||||||
|
network: "网络",
|
||||||
|
copyToClipboard: "复制到剪贴板",
|
||||||
|
noData: "无数据!",
|
||||||
|
invalidLogin: "登录无效!",
|
||||||
|
online: "在线",
|
||||||
|
pages: {
|
||||||
|
login: "登录",
|
||||||
|
home: "主页",
|
||||||
|
inbounds: "入站管理",
|
||||||
|
outbounds: "出站管理",
|
||||||
|
clients: "用户管理",
|
||||||
|
rules: "路由列表",
|
||||||
|
basics: "基础信息",
|
||||||
|
admins: "管理员",
|
||||||
|
settings: "设置",
|
||||||
|
},
|
||||||
|
main: {
|
||||||
|
tiles: "信息卡",
|
||||||
|
gauges: "仪表板",
|
||||||
|
charts: "图表",
|
||||||
|
infos: "信息",
|
||||||
|
gauge: {
|
||||||
|
cpu: "CPU 仪表",
|
||||||
|
mem: "RAM 仪表",
|
||||||
|
},
|
||||||
|
chart: {
|
||||||
|
cpu: "CPU 监视器",
|
||||||
|
mem: "RAM 监视器",
|
||||||
|
net: "网络带宽",
|
||||||
|
pnet: "网络数据包",
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
sys: "系统信息",
|
||||||
|
sbd: "运行信息",
|
||||||
|
host: "主机",
|
||||||
|
cpu: "CPU",
|
||||||
|
core: "核心",
|
||||||
|
uptime: "运行时间",
|
||||||
|
threads: "线程",
|
||||||
|
memory: "内存",
|
||||||
|
running: "运行状态"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
objects: {
|
||||||
|
inbound: "入站",
|
||||||
|
client: "客户端",
|
||||||
|
outbound: "出站",
|
||||||
|
rule: "规则",
|
||||||
|
user: "用户",
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
action: "操作",
|
||||||
|
add: "添加",
|
||||||
|
edit: "编辑",
|
||||||
|
del: "删除",
|
||||||
|
save: "保存",
|
||||||
|
update: "更新",
|
||||||
|
submit: "提交",
|
||||||
|
close: "关闭",
|
||||||
|
restartApp: "重启面板",
|
||||||
|
},
|
||||||
|
login: {
|
||||||
|
title: "登录",
|
||||||
|
username: "用户名",
|
||||||
|
unRules: "用户名不能为空",
|
||||||
|
password: "密码",
|
||||||
|
pwRules: "密码不能为空",
|
||||||
|
},
|
||||||
|
menu: {
|
||||||
|
logout: "退出登录",
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
changeCred: "更改凭据",
|
||||||
|
oldPass: "当前密码",
|
||||||
|
newUname: "新用户名",
|
||||||
|
newPass: "新密码",
|
||||||
|
},
|
||||||
|
setting: {
|
||||||
|
interface: "界面",
|
||||||
|
sub: "订阅",
|
||||||
|
addr: "地址",
|
||||||
|
port: "端口",
|
||||||
|
webPath: "基本 URI",
|
||||||
|
domain: "域名",
|
||||||
|
sslKey: "SSL 密钥 (Key) 路径",
|
||||||
|
sslCert: "SSL 证书 (cert) 路径",
|
||||||
|
webUri: "面板 URI",
|
||||||
|
sessionAge: "会话最大连接数",
|
||||||
|
timeLoc: "时区",
|
||||||
|
subEncode: "启用编码",
|
||||||
|
subInfo: "启用用户信息",
|
||||||
|
path: "默认路径",
|
||||||
|
update: "自动更新时间",
|
||||||
|
subUri: "订阅 URL",
|
||||||
|
},
|
||||||
|
client: {
|
||||||
|
name: "名称",
|
||||||
|
inboundTags: "入站标签",
|
||||||
|
basics: "基础",
|
||||||
|
config: "配置",
|
||||||
|
links: "链接",
|
||||||
|
external: "外部链接",
|
||||||
|
sub: "外部订阅",
|
||||||
|
},
|
||||||
|
in: {
|
||||||
|
tag: "标签",
|
||||||
|
addr: "地址",
|
||||||
|
port: "端口",
|
||||||
|
sniffing: "嗅探",
|
||||||
|
tls: "TLS",
|
||||||
|
clients: "启用客户端",
|
||||||
|
multiplex: "多路复用",
|
||||||
|
transport: "传输",
|
||||||
|
},
|
||||||
|
transport: {
|
||||||
|
enable: "启用传输",
|
||||||
|
host: "主机",
|
||||||
|
hosts: "主机列表",
|
||||||
|
path: "路径",
|
||||||
|
},
|
||||||
|
tls : {
|
||||||
|
enable: "启用 TLS",
|
||||||
|
usePath: "使用外部路径",
|
||||||
|
useText: "使用文件内容",
|
||||||
|
certPath: "证书文件路径",
|
||||||
|
keyPath: "私钥文件路径",
|
||||||
|
cert: "证书文件内容",
|
||||||
|
key: "私钥文件内容",
|
||||||
|
},
|
||||||
|
stats: {
|
||||||
|
upload: "上传",
|
||||||
|
download: "下载",
|
||||||
|
volume: "流量",
|
||||||
|
usage: "已用",
|
||||||
|
enable: "启用统计",
|
||||||
|
graphTitle: "流量图表",
|
||||||
|
B: "B",
|
||||||
|
KB: "KB",
|
||||||
|
MB: "MB",
|
||||||
|
GB: "GB",
|
||||||
|
TB: "TB",
|
||||||
|
PB: "PB",
|
||||||
|
p: "p",
|
||||||
|
Kp: "Kp",
|
||||||
|
Mp: "Mp",
|
||||||
|
Gb: "Gb",
|
||||||
|
},
|
||||||
|
date: {
|
||||||
|
expiry: "到期",
|
||||||
|
expired: "已到期",
|
||||||
|
d: "天",
|
||||||
|
h: "时",
|
||||||
|
m: "分",
|
||||||
|
s: "秒",
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ import axios from 'axios'
|
|||||||
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
|
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
|
||||||
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
|
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
|
||||||
|
|
||||||
|
axios.defaults.baseURL = "./"
|
||||||
|
|
||||||
axios.interceptors.request.use(
|
axios.interceptors.request.use(
|
||||||
(config) => {
|
(config) => {
|
||||||
if (config.data instanceof FormData) {
|
if (config.data instanceof FormData) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import api from './api'
|
import api from './api'
|
||||||
import { i18n } from '@/locales'
|
import { i18n } from '@/locales'
|
||||||
|
import router from '@/router'
|
||||||
import Message from "@/store/modules/message"
|
import Message from "@/store/modules/message"
|
||||||
|
|
||||||
export interface Msg {
|
export interface Msg {
|
||||||
@@ -14,11 +15,23 @@ function _handleMsg(msg: any): void {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if(msg.msg){
|
if(msg.msg){
|
||||||
|
if (!msg.success && msg.msg == "Invalid login") {
|
||||||
|
sb.showMessage(i18n.global.t('invalidLogin'),'error', 5000)
|
||||||
|
logout()
|
||||||
|
return
|
||||||
|
}
|
||||||
const message = msg.success ? i18n.global.t('success') + ": " + i18n.global.t('actions.' + msg.msg) : i18n.global.t('failed') + ": " + msg.msg
|
const message = msg.success ? i18n.global.t('success') + ": " + i18n.global.t('actions.' + msg.msg) : i18n.global.t('failed') + ": " + msg.msg
|
||||||
sb.showMessage(message, msg.success ? 'success' : 'error', 5000)
|
sb.showMessage(message, msg.success ? 'success' : 'error', 5000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const logout = async () => {
|
||||||
|
const response = await HttpUtils.get('api/logout')
|
||||||
|
if(response.success){
|
||||||
|
router.push('/login')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function _respToMsg(resp: any): Msg {
|
function _respToMsg(resp: any): Msg {
|
||||||
const data = resp.data
|
const data = resp.data
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ export namespace LinkUtil {
|
|||||||
case TrspTypes.WebSocket:
|
case TrspTypes.WebSocket:
|
||||||
const tw = <WebSocket>t
|
const tw = <WebSocket>t
|
||||||
params.path = tw.path?? null
|
params.path = tw.path?? null
|
||||||
|
params.host = tw.headers?.Host?? null
|
||||||
break
|
break
|
||||||
case TrspTypes.gRPC:
|
case TrspTypes.gRPC:
|
||||||
const tg = <gRPC>t
|
const tg = <gRPC>t
|
||||||
|
|||||||
@@ -44,6 +44,11 @@ const routes = [
|
|||||||
name: 'pages.basics',
|
name: 'pages.basics',
|
||||||
component: () => import('@/views/Basics.vue'),
|
component: () => import('@/views/Basics.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/admins',
|
||||||
|
name: 'pages.admins',
|
||||||
|
component: () => import('@/views/Admins.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/settings',
|
path: '/settings',
|
||||||
name: 'pages.settings',
|
name: 'pages.settings',
|
||||||
@@ -54,7 +59,7 @@ const routes = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(process.env.BASE_URL),
|
history: createWebHistory((window as any).BASE_URL),
|
||||||
routes,
|
routes,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -64,7 +69,7 @@ let intervalId:any
|
|||||||
// Navigation guard to check authentication state
|
// Navigation guard to check authentication state
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
// Check the session cookie
|
// Check the session cookie
|
||||||
const sessionCookie = document.cookie.split(';').find(cookie => cookie.trim().startsWith('session='))
|
const sessionCookie = document.cookie.split(';').find(cookie => cookie.trim().startsWith('s-ui='))
|
||||||
const isAuthenticated = !!sessionCookie
|
const isAuthenticated = !!sessionCookie
|
||||||
|
|
||||||
// If the route requires authentication and the user is not authenticated, redirect to /login
|
// If the route requires authentication and the user is not authenticated, redirect to /login
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const Data = defineStore('Data', {
|
|||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
async loadData() {
|
async loadData() {
|
||||||
const msg = await HttpUtils.get('/api/load', this.lastLoad >0 ? {lu: this.lastLoad} : {} )
|
const msg = await HttpUtils.get('api/load', this.lastLoad >0 ? {lu: this.lastLoad} : {} )
|
||||||
if(msg.success) {
|
if(msg.success) {
|
||||||
this.lastLoad = Math.floor((new Date()).getTime()/1000)
|
this.lastLoad = Math.floor((new Date()).getTime()/1000)
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ const Data = defineStore('Data', {
|
|||||||
config: JSON.stringify(FindDiff.Config(this.config,this.oldData.config)),
|
config: JSON.stringify(FindDiff.Config(this.config,this.oldData.config)),
|
||||||
clients: JSON.stringify(FindDiff.Clients(this.clients,this.oldData.clients)),
|
clients: JSON.stringify(FindDiff.Clients(this.clients,this.oldData.clients)),
|
||||||
}
|
}
|
||||||
const msg = await HttpUtils.post('/api/save',diff)
|
const msg = await HttpUtils.post('api/save',diff)
|
||||||
if(msg.success) {
|
if(msg.success) {
|
||||||
this.loadData()
|
this.loadData()
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ const Data = defineStore('Data', {
|
|||||||
config: JSON.stringify([{key: "inbounds", action: "del", index: index, obj: null}]),
|
config: JSON.stringify([{key: "inbounds", action: "del", index: index, obj: null}]),
|
||||||
clients: JSON.stringify(FindDiff.Clients(this.clients,this.oldData.clients)),
|
clients: JSON.stringify(FindDiff.Clients(this.clients,this.oldData.clients)),
|
||||||
}
|
}
|
||||||
const msg = await HttpUtils.post('/api/save',diff)
|
const msg = await HttpUtils.post('api/save',diff)
|
||||||
if(msg.success) {
|
if(msg.success) {
|
||||||
this.loadData()
|
this.loadData()
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ const Data = defineStore('Data', {
|
|||||||
config: JSON.stringify(FindDiff.Config(this.config,this.oldData.config)),
|
config: JSON.stringify(FindDiff.Config(this.config,this.oldData.config)),
|
||||||
clients:JSON.stringify([{key: "clients", action: "del", index: id, obj: null}]),
|
clients:JSON.stringify([{key: "clients", action: "del", index: id, obj: null}]),
|
||||||
}
|
}
|
||||||
const msg = await HttpUtils.post('/api/save',diff)
|
const msg = await HttpUtils.post('api/save',diff)
|
||||||
if(msg.success) {
|
if(msg.success) {
|
||||||
this.loadData()
|
this.loadData()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export interface iTls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const defaultInTls: iTls = {
|
export const defaultInTls: iTls = {
|
||||||
alpn: ['HTTP/3', 'HTTP/2', 'HTTP/1.1'],
|
alpn: ['h3', 'h2', 'http/1.1'],
|
||||||
min_version: "1.2",
|
min_version: "1.2",
|
||||||
max_version: "1.3",
|
max_version: "1.3",
|
||||||
cipher_suites: [""],
|
cipher_suites: [""],
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ export interface HTTP extends TransportBasics {
|
|||||||
|
|
||||||
export interface WebSocket extends TransportBasics {
|
export interface WebSocket extends TransportBasics {
|
||||||
path: string
|
path: string
|
||||||
headers?: {}
|
headers?: {
|
||||||
|
Host: string
|
||||||
|
}
|
||||||
max_early_data?: number
|
max_early_data?: number
|
||||||
early_data_header_name?: string
|
early_data_header_name?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<template>
|
||||||
|
<AdminModal
|
||||||
|
v-model="editModal.visible"
|
||||||
|
:visible="editModal.visible"
|
||||||
|
:user="editModal.user"
|
||||||
|
@close="closeEditModal"
|
||||||
|
@save="saveEditModal"
|
||||||
|
/>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" sm="4" md="3" lg="2" v-for="(item, index) in <any[]>users" :key="item.id">
|
||||||
|
<v-card rounded="xl" elevation="5" min-width="200" :title="item.username">
|
||||||
|
<v-card-subtitle>
|
||||||
|
Last Login
|
||||||
|
</v-card-subtitle>
|
||||||
|
<v-card-text>
|
||||||
|
<v-row>
|
||||||
|
<v-col>Date</v-col>
|
||||||
|
<v-col dir="ltr">
|
||||||
|
{{ item.lastLogin.split(" ")[0]?? '-' }}
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-row>
|
||||||
|
<v-col>Time</v-col>
|
||||||
|
<v-col dir="ltr">
|
||||||
|
{{ item.lastLogin.split(" ")[1]?? '-' }}
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-row>
|
||||||
|
<v-col>IP</v-col>
|
||||||
|
<v-col dir="ltr">
|
||||||
|
{{ item.lastLogin.split(" ")[2]?? '-' }}
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-card-actions style="padding: 0;">
|
||||||
|
<v-btn icon="mdi-account-edit" @click="showEditModal(item)">
|
||||||
|
<v-icon />
|
||||||
|
<v-tooltip activator="parent" location="top" :text="$t('actions.edit')"></v-tooltip>
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import AdminModal from '@/layouts/modals/Admin.vue';
|
||||||
|
import HttpUtils from '@/plugins/httputil';
|
||||||
|
import { Ref, ref, inject, onMounted } from 'vue';
|
||||||
|
|
||||||
|
const loading:Ref = inject('loading')?? ref(false)
|
||||||
|
|
||||||
|
const users = ref([])
|
||||||
|
|
||||||
|
onMounted(async () => {loadData()})
|
||||||
|
|
||||||
|
const loadData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
const msg = await HttpUtils.get('api/users')
|
||||||
|
loading.value = false
|
||||||
|
if (msg.success) {
|
||||||
|
users.value = msg.obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const editModal = ref({
|
||||||
|
visible: false,
|
||||||
|
user: {},
|
||||||
|
})
|
||||||
|
|
||||||
|
const showEditModal = (user: any) => {
|
||||||
|
editModal.value.user = user
|
||||||
|
editModal.value.visible = true
|
||||||
|
}
|
||||||
|
const closeEditModal = () => {
|
||||||
|
editModal.value.visible = false
|
||||||
|
}
|
||||||
|
const saveEditModal = async (data:any) => {
|
||||||
|
loading.value=true
|
||||||
|
const response = await HttpUtils.post('api/changePass',data)
|
||||||
|
if(response.success){
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value=false
|
||||||
|
editModal.value.visible = false
|
||||||
|
}, 500)
|
||||||
|
} else {
|
||||||
|
loading.value=false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -337,10 +337,3 @@ const enableClashApi = computed({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
.v-card-subtitle {
|
|
||||||
text-align: center;
|
|
||||||
border-bottom: 1px solid gray;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
@close="closeStats"
|
@close="closeStats"
|
||||||
/>
|
/>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12">
|
<v-col cols="12" justify="center" align="center">
|
||||||
<v-btn color="primary" @click="showModal(-1)">{{ $t('actions.add') }}</v-btn>
|
<v-btn color="primary" @click="showModal(-1)">{{ $t('actions.add') }}</v-btn>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
{{ $t('remained') }}: {{ HumanReadable.sizeFormat(item.volume - (item.up + item.down)) }}
|
{{ $t('remained') }}: {{ HumanReadable.sizeFormat(item.volume - (item.up + item.down)) }}
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
{{ item.volume>0 ? HumanReadable.sizeFormat(item.up + item.down) : $t('unlimited') }}
|
{{ HumanReadable.sizeFormat(item.up + item.down) }}
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
<v-row>
|
<v-row>
|
||||||
@@ -221,7 +221,7 @@ const buildInboundsUsers = (inboundTags:string[]) => {
|
|||||||
// Remove flow in non tls VLESS
|
// Remove flow in non tls VLESS
|
||||||
if (newInbound.type == InTypes.VLESS) {
|
if (newInbound.type == InTypes.VLESS) {
|
||||||
const vlessInbound = <VLESS>newInbound
|
const vlessInbound = <VLESS>newInbound
|
||||||
if (!vlessInbound.tls?.enabled) delete(clientConfig["vless"].flow)
|
if (!vlessInbound.tls?.enabled || vlessInbound.transport?.type) delete(clientConfig["vless"].flow)
|
||||||
}
|
}
|
||||||
users.push(clientConfig[newInbound.type])
|
users.push(clientConfig[newInbound.type])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
@close="closeStats"
|
@close="closeStats"
|
||||||
/>
|
/>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12">
|
<v-col cols="12" justify="center" align="center">
|
||||||
<v-btn color="primary" @click="showModal(-1)">{{ $t('actions.add') }}</v-btn>
|
<v-btn color="primary" @click="showModal(-1)">{{ $t('actions.add') }}</v-btn>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
<v-tooltip activator="parent" dir="ltr" location="bottom" v-if="Object.hasOwn(item,'users')">
|
<v-tooltip activator="parent" dir="ltr" location="bottom" v-if="Object.hasOwn(item,'users')">
|
||||||
<span v-for="u in findInbounsUsers(item)">{{ u }}<br /></span>
|
<span v-for="u in findInbounsUsers(item)">{{ u }}<br /></span>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
{{ Object.hasOwn(item,'users') ? item.users.length : '-' }}
|
{{ Array.isArray(item.users) ? item.users.length : '-' }}
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
<v-row>
|
<v-row>
|
||||||
@@ -236,7 +236,7 @@ const buildInboundsUsers = (inbound:InboundWithUser):Inbound => {
|
|||||||
// Remove flow in non tls VLESS
|
// Remove flow in non tls VLESS
|
||||||
if (inbound.type == InTypes.VLESS) {
|
if (inbound.type == InTypes.VLESS) {
|
||||||
const vlessInbound = <VLESS>inbound
|
const vlessInbound = <VLESS>inbound
|
||||||
if (!vlessInbound.tls?.enabled) clientConfig["vless"].flow = undefined
|
if (!vlessInbound.tls?.enabled || vlessInbound.transport?.type) delete(clientConfig["vless"].flow)
|
||||||
}
|
}
|
||||||
users.push(clientConfig[inbound.type])
|
users.push(clientConfig[inbound.type])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
<v-row justify="center" align="center">
|
<v-row justify="center" align="center">
|
||||||
<v-col cols="12" sm="8" md="4">
|
<v-col cols="12" sm="8" md="4">
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-card-title class="headline">Login</v-card-title>
|
<v-card-title class="headline" v-text="$t('login.title')"></v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-form @submit.prevent="login" ref="form">
|
<v-form @submit.prevent="login" ref="form">
|
||||||
<v-text-field v-model="username" :label="$t('login.username')" :rules="usernameRules" required></v-text-field>
|
<v-text-field v-model="username" :label="$t('login.username')" :rules="usernameRules" required></v-text-field>
|
||||||
<v-text-field v-model="password" :label="$t('login.password')" :rules="passwordRules" type="password" required></v-text-field>
|
<v-text-field v-model="password" :label="$t('login.password')" :rules="passwordRules" type="password" required></v-text-field>
|
||||||
<v-btn :loading="loading" type="submit" color="primary" block class="mt-2">Login</v-btn>
|
<v-btn :loading="loading" type="submit" color="primary" block class="mt-2" v-text="$t('actions.submit')"></v-btn>
|
||||||
</v-form>
|
</v-form>
|
||||||
<v-select
|
<v-select
|
||||||
density="compact"
|
density="compact"
|
||||||
@@ -63,7 +63,7 @@ const router = useRouter()
|
|||||||
const login = async () => {
|
const login = async () => {
|
||||||
if (username.value == '' || password.value == '') return
|
if (username.value == '' || password.value == '') return
|
||||||
loading.value=true
|
loading.value=true
|
||||||
const response = await HttpUtil.post('/api/login',{user: username.value, pass: password.value})
|
const response = await HttpUtil.post('api/login',{user: username.value, pass: password.value})
|
||||||
if(response.success){
|
if(response.success){
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loading.value=false
|
loading.value=false
|
||||||
|
|||||||
@@ -32,6 +32,9 @@
|
|||||||
<v-col cols="12" sm="6" md="4">
|
<v-col cols="12" sm="6" md="4">
|
||||||
<v-text-field v-model="webPort" :label="$t('setting.port')" hide-details></v-text-field>
|
<v-text-field v-model="webPort" :label="$t('setting.port')" hide-details></v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
<v-col cols="12" sm="6" md="4">
|
||||||
|
<v-text-field v-model="settings.webPath" :label="$t('setting.webPath')" hide-details></v-text-field>
|
||||||
|
</v-col>
|
||||||
<v-col cols="12" sm="6" md="4">
|
<v-col cols="12" sm="6" md="4">
|
||||||
<v-text-field v-model="settings.webDomain" :label="$t('setting.domain')" hide-details></v-text-field>
|
<v-text-field v-model="settings.webDomain" :label="$t('setting.domain')" hide-details></v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
@@ -41,6 +44,9 @@
|
|||||||
<v-col cols="12" sm="6" md="4">
|
<v-col cols="12" sm="6" md="4">
|
||||||
<v-text-field v-model="settings.webCertFile" :label="$t('setting.sslCert')" hide-details></v-text-field>
|
<v-text-field v-model="settings.webCertFile" :label="$t('setting.sslCert')" hide-details></v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
<v-col cols="12" sm="6" md="4">
|
||||||
|
<v-text-field v-model="settings.webURI" :label="$t('setting.webUri')" hide-details></v-text-field>
|
||||||
|
</v-col>
|
||||||
<v-col cols="12" sm="6" md="4">
|
<v-col cols="12" sm="6" md="4">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
type="number"
|
type="number"
|
||||||
@@ -143,6 +149,8 @@ const settings = ref({
|
|||||||
webPort: "2095",
|
webPort: "2095",
|
||||||
webCertFile: "",
|
webCertFile: "",
|
||||||
webKeyFile: "",
|
webKeyFile: "",
|
||||||
|
webPath: "/app/",
|
||||||
|
webURI: "",
|
||||||
sessionMaxAge: "0",
|
sessionMaxAge: "0",
|
||||||
timeLocation: "Asia/Tehran",
|
timeLocation: "Asia/Tehran",
|
||||||
subListen: "",
|
subListen: "",
|
||||||
@@ -166,7 +174,7 @@ const changeLocale = (l: any) => {
|
|||||||
|
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const msg = await HttpUtils.get('/api/setting')
|
const msg = await HttpUtils.get('api/setting')
|
||||||
loading.value = false
|
loading.value = false
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
settings.value = msg.obj
|
settings.value = msg.obj
|
||||||
@@ -179,7 +187,7 @@ const saveChanges = async () => {
|
|||||||
const diff = {
|
const diff = {
|
||||||
settings: JSON.stringify(FindDiff.Settings(settings.value,oldSettings.value)),
|
settings: JSON.stringify(FindDiff.Settings(settings.value,oldSettings.value)),
|
||||||
}
|
}
|
||||||
const msg = await HttpUtils.post('/api/save', diff)
|
const msg = await HttpUtils.post('api/save', diff)
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
loadData()
|
loadData()
|
||||||
}
|
}
|
||||||
@@ -190,17 +198,20 @@ const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
|||||||
|
|
||||||
const restartApp = async () => {
|
const restartApp = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const msg = await HttpUtils.post('/api/restartApp',{})
|
const msg = await HttpUtils.post('api/restartApp',{})
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
|
let url = settings.value.webURI
|
||||||
|
if (url !== "") {
|
||||||
const isTLS = settings.value.webCertFile !== "" || settings.value.webKeyFile !== ""
|
const isTLS = settings.value.webCertFile !== "" || settings.value.webKeyFile !== ""
|
||||||
const url = buildURL(settings.value.webDomain,settings.value.webPort.toString(),isTLS)
|
url = buildURL(settings.value.webDomain,settings.value.webPort.toString(),isTLS, settings.value.webPath)
|
||||||
|
}
|
||||||
await sleep(3000)
|
await sleep(3000)
|
||||||
window.location.replace(url)
|
window.location.replace(url)
|
||||||
}
|
}
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildURL = (host: string, port: string, isTLS: boolean ) => {
|
const buildURL = (host: string, port: string, isTLS: boolean, path: string) => {
|
||||||
if (!host || host.length == 0) host = window.location.hostname
|
if (!host || host.length == 0) host = window.location.hostname
|
||||||
if (!port || port.length == 0) port = window.location.port
|
if (!port || port.length == 0) port = window.location.port
|
||||||
|
|
||||||
@@ -212,7 +223,7 @@ const buildURL = (host: string, port: string, isTLS: boolean ) => {
|
|||||||
port = `:${port}`
|
port = `:${port}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${protocol}//${host}${port}/settings`
|
return `${protocol}//${host}${port}${path}settings`
|
||||||
}
|
}
|
||||||
|
|
||||||
const subEncode = computed({
|
const subEncode = computed({
|
||||||
@@ -245,7 +256,6 @@ const subUpdates = computed({
|
|||||||
set: (v:number) => { settings.value.subUpdates = v.toString() }
|
set: (v:number) => { settings.value.subUpdates = v.toString() }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const stateChange = computed(() => {
|
const stateChange = computed(() => {
|
||||||
return !FindDiff.deepCompare(settings.value,oldSettings.value)
|
return !FindDiff.deepCompare(settings.value,oldSettings.value)
|
||||||
})
|
})
|
||||||
|
|||||||
+17
-3
@@ -5,8 +5,18 @@ import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'
|
|||||||
// Utilities
|
// Utilities
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import { fileURLToPath, URL } from 'node:url'
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
import { randomBytes } from 'crypto';
|
||||||
|
|
||||||
|
function getUniqueFileName(template) {
|
||||||
|
if (template.includes('.js') || template.includes('.css')) {
|
||||||
|
const hash = randomBytes(8).toString('hex');
|
||||||
|
return template.replace('[name]', hash);
|
||||||
|
}
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
base: '',
|
||||||
plugins: [
|
plugins: [
|
||||||
vue({
|
vue({
|
||||||
template: { transformAssetUrls },
|
template: { transformAssetUrls },
|
||||||
@@ -25,8 +35,12 @@ export default defineConfig({
|
|||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
inlineDynamicImports: true,
|
inlineDynamicImports: true,
|
||||||
entryFileNames: 'assets/[name].js',
|
entryFileNames: getUniqueFileName('assets/[name].js'),
|
||||||
assetFileNames: 'assets/[name].[ext]',
|
chunkFileNames: getUniqueFileName('assets/[name].js'),
|
||||||
|
assetFileNames: (assetInfo) => {
|
||||||
|
if (assetInfo.name == "index.css") return getUniqueFileName('assets/[name].css');
|
||||||
|
return 'assets/' + assetInfo.name;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -40,7 +54,7 @@ export default defineConfig({
|
|||||||
server: {
|
server: {
|
||||||
port: 3000,
|
port: 3000,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/app/api': {
|
||||||
target: 'http://localhost:2095',
|
target: 'http://localhost:2095',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
|
|||||||
+82
-31
@@ -23,25 +23,17 @@ else
|
|||||||
fi
|
fi
|
||||||
echo "The OS release is: $release"
|
echo "The OS release is: $release"
|
||||||
|
|
||||||
arch=$(arch)
|
arch() {
|
||||||
|
case "$(uname -m)" in
|
||||||
|
x86_64 | x64 | amd64) echo 'amd64' ;;
|
||||||
|
i*86 | x86) echo '386' ;;
|
||||||
|
armv8* | armv8 | arm64 | aarch64) echo 'arm64' ;;
|
||||||
|
armv7* | armv7 | arm) echo 'armv7' ;;
|
||||||
|
*) echo -e "${green}Unsupported CPU architecture! ${plain}" && rm -f install.sh && exit 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
if [[ $arch == "x86_64" || $arch == "x64" || $arch == "amd64" ]]; then
|
echo "arch: $(arch)"
|
||||||
arch="amd64"
|
|
||||||
elif [[ $arch == "aarch64" || $arch == "arm64" ]]; then
|
|
||||||
arch="arm64"
|
|
||||||
elif [[ $arch == "s390x" ]]; then
|
|
||||||
arch="s390x"
|
|
||||||
else
|
|
||||||
arch="amd64"
|
|
||||||
echo -e "${red} Failed to check system arch, will use default arch: ${arch}${plain}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "arch: ${arch}"
|
|
||||||
|
|
||||||
if [ $(getconf WORD_BIT) != '32' ] && [ $(getconf LONG_BIT) != '64' ]; then
|
|
||||||
echo "s-ui dosen't support 32-bit(x86) system, please use 64 bit operating system(x86_64) instead, if there is something wrong, please get in touch with me!"
|
|
||||||
exit -1
|
|
||||||
fi
|
|
||||||
|
|
||||||
os_version=""
|
os_version=""
|
||||||
os_version=$(grep -i version_id /etc/os-release | cut -d \" -f2 | cut -d . -f1)
|
os_version=$(grep -i version_id /etc/os-release | cut -d \" -f2 | cut -d . -f1)
|
||||||
@@ -70,10 +62,67 @@ fi
|
|||||||
|
|
||||||
|
|
||||||
install_base() {
|
install_base() {
|
||||||
if [[ "${release}" == "centos" ]] || [[ "${release}" == "fedora" ]] ; then
|
case "${release}" in
|
||||||
yum install wget curl tar unzip jq -y
|
centos)
|
||||||
|
yum -y update && yum install -y -q wget curl tar tzdata
|
||||||
|
;;
|
||||||
|
fedora)
|
||||||
|
dnf -y update && dnf install -y -q wget curl tar tzdata
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
apt-get update && apt install -y -q wget curl tar tzdata
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
config_after_install() {
|
||||||
|
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
|
||||||
|
if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then
|
||||||
|
read -p "Enter the ${yellow}panel port${plain} (leave blank for existing/default value):" config_port
|
||||||
|
read -p "Enter the ${yellow}panel path${plain} (leave blank for existing/default value):" config_path
|
||||||
|
|
||||||
|
# Sub configuration
|
||||||
|
read -p "Enter the ${yellow}subscription port${plain} (leave blank for existing/default value):" config_subPort
|
||||||
|
read -p "Enter the ${yellow}subscription path${plain} (leave blank for existing/default value):" config_subPath
|
||||||
|
|
||||||
|
# Set configs
|
||||||
|
echo -e "${yellow}Initializing, please wait...${plain}"
|
||||||
|
params=""
|
||||||
|
[ -z "$config_port" ] || params="$params -port $config_port"
|
||||||
|
[ -z "$config_path" ] || params="$params -path $config_path"
|
||||||
|
[ -z "$config_subPort" ] || params="$params -subPort $config_subPort"
|
||||||
|
[ -z "$config_subPath" ] || params="$params -subPath $config_subPath"
|
||||||
|
/usr/local/s-ui/sui setting ${params}
|
||||||
|
|
||||||
|
read -p "Do you want to change admin credentials [y/n]? ": admin_confirm
|
||||||
|
if [[ "${admin_confirm}" == "y" || "${admin_confirm}" == "Y" ]]; then
|
||||||
|
# First admin credentials
|
||||||
|
read -p "Please set up your username:" config_account
|
||||||
|
read -p "Please set up your password:" config_password
|
||||||
|
|
||||||
|
# Set credentials
|
||||||
|
echo -e "${yellow}Initializing, please wait...${plain}"
|
||||||
|
/usr/local/s-ui/sui admin -username ${config_account} -password ${config_password}
|
||||||
else
|
else
|
||||||
apt install wget curl tar unzip jq -y
|
echo -e "${yellow}Your current admin credentials: ${plain}"
|
||||||
|
/usr/local/s-ui/sui admin -show
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${red}cancel...${plain}"
|
||||||
|
if [[ ! -f "/usr/local/s-ui/db/s-ui.db" ]]; then
|
||||||
|
local usernameTemp=$(head -c 6 /dev/urandom | base64)
|
||||||
|
local passwordTemp=$(head -c 6 /dev/urandom | base64)
|
||||||
|
echo -e "this is a fresh installation,will generate random login info for security concerns:"
|
||||||
|
echo -e "###############################################"
|
||||||
|
echo -e "${green}username:${usernameTemp}${plain}"
|
||||||
|
echo -e "${green}password:${passwordTemp}${plain}"
|
||||||
|
echo -e "###############################################"
|
||||||
|
echo -e "${red}if you forgot your login info,you can type ${green}s-ui${red} for configuration menu${plain}"
|
||||||
|
/usr/local/s-ui/sui admin -username ${usernameTemp} -password ${passwordTemp}
|
||||||
|
else
|
||||||
|
echo -e "${red} this is your upgrade,will keep old settings,if you forgot your login info,you can type ${green}s-ui${red} for configuration menu${plain}"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,16 +136,16 @@ install_s-ui() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo -e "Got s-ui latest version: ${last_version}, beginning the installation..."
|
echo -e "Got s-ui latest version: ${last_version}, beginning the installation..."
|
||||||
wget -N --no-check-certificate -O /tmp/s-ui-linux-${arch}.tar.gz https://github.com/alireza0/s-ui/releases/download/${last_version}/s-ui-linux-${arch}.tar.gz
|
wget -N --no-check-certificate -O /tmp/s-ui-linux-$(arch).tar.gz https://github.com/alireza0/s-ui/releases/download/${last_version}/s-ui-linux-$(arch).tar.gz
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
echo -e "${red}Dowanloading s-ui failed, please be sure that your server can access Github ${plain}"
|
echo -e "${red}Dowanloading s-ui failed, please be sure that your server can access Github ${plain}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
last_version=$1
|
last_version=$1
|
||||||
url="https://github.com/alireza0/s-ui/releases/download/${last_version}/s-ui-linux-${arch}.tar.gz"
|
url="https://github.com/alireza0/s-ui/releases/download/${last_version}/s-ui-linux-$(arch).tar.gz"
|
||||||
echo -e "Begining to install s-ui v$1"
|
echo -e "Begining to install s-ui v$1"
|
||||||
wget -N --no-check-certificate -O /tmp/s-ui-linux-${arch}.tar.gz ${url}
|
wget -N --no-check-certificate -O /tmp/s-ui-linux-$(arch).tar.gz ${url}
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
echo -e "${red}dowanload s-ui v$1 failed,please check the verison exists${plain}"
|
echo -e "${red}dowanload s-ui v$1 failed,please check the verison exists${plain}"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -108,23 +157,25 @@ install_s-ui() {
|
|||||||
systemctl stop sing-box
|
systemctl stop sing-box
|
||||||
fi
|
fi
|
||||||
|
|
||||||
tar zxvf s-ui-linux-${arch}.tar.gz
|
tar zxvf s-ui-linux-$(arch).tar.gz
|
||||||
rm s-ui-linux-${arch}.tar.gz -f
|
rm s-ui-linux-$(arch).tar.gz -f
|
||||||
chmod +x s-ui/sui s-ui/bin/sing-box s-ui/bin/runSingbox.sh
|
|
||||||
|
wget --no-check-certificate -O /usr/bin/s-ui https://raw.githubusercontent.com/alireza0/s-ui/main/s-ui.sh
|
||||||
|
|
||||||
|
chmod +x s-ui/sui s-ui/bin/sing-box s-ui/bin/runSingbox.sh /usr/bin/s-ui
|
||||||
cp -rf s-ui /usr/local/
|
cp -rf s-ui /usr/local/
|
||||||
cp -f s-ui/*.service /etc/systemd/system/
|
cp -f s-ui/*.service /etc/systemd/system/
|
||||||
rm -rf s-ui
|
rm -rf s-ui
|
||||||
|
|
||||||
|
config_after_install
|
||||||
|
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable s-ui --now
|
systemctl enable s-ui --now
|
||||||
systemctl enable sing-box --now
|
systemctl enable sing-box --now
|
||||||
|
|
||||||
echo -e "${green}s-ui v${last_version}${plain} installation finished, it is up and running now..."
|
echo -e "${green}s-ui v${last_version}${plain} installation finished, it is up and running now..."
|
||||||
echo -e ""
|
echo -e ""
|
||||||
echo -e "s-ui usages: "
|
s-ui help
|
||||||
echo -e "----------------------------------------------"
|
|
||||||
echo -e "s-ui - Enter Run S-UI"
|
|
||||||
echo -e "----------------------------------------------"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
echo -e "${green}Excuting...${plain}"
|
echo -e "${green}Excuting...${plain}"
|
||||||
|
|||||||
@@ -0,0 +1,903 @@
|
|||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
red='\033[0;31m'
|
||||||
|
green='\033[0;32m'
|
||||||
|
yellow='\033[0;33m'
|
||||||
|
plain='\033[0m'
|
||||||
|
|
||||||
|
#Add some basic function here
|
||||||
|
function LOGD() {
|
||||||
|
echo -e "${yellow}[DEG] $* ${plain}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function LOGE() {
|
||||||
|
echo -e "${red}[ERR] $* ${plain}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function LOGI() {
|
||||||
|
echo -e "${green}[INF] $* ${plain}"
|
||||||
|
}
|
||||||
|
# check root
|
||||||
|
[[ $EUID -ne 0 ]] && LOGE "ERROR: You must be root to run this script! \n" && exit 1
|
||||||
|
|
||||||
|
# Check OS and set release variable
|
||||||
|
if [[ -f /etc/os-release ]]; then
|
||||||
|
source /etc/os-release
|
||||||
|
release=$ID
|
||||||
|
elif [[ -f /usr/lib/os-release ]]; then
|
||||||
|
source /usr/lib/os-release
|
||||||
|
release=$ID
|
||||||
|
else
|
||||||
|
echo "Failed to check the system OS, please contact the author!" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "The OS release is: $release"
|
||||||
|
|
||||||
|
|
||||||
|
os_version=""
|
||||||
|
os_version=$(grep -i version_id /etc/os-release | cut -d \" -f2 | cut -d . -f1)
|
||||||
|
|
||||||
|
if [[ "${release}" == "centos" ]]; then
|
||||||
|
if [[ ${os_version} -lt 8 ]]; then
|
||||||
|
echo -e "${red} Please use CentOS 8 or higher ${plain}\n" && exit 1
|
||||||
|
fi
|
||||||
|
elif [[ "${release}" == "ubuntu" ]]; then
|
||||||
|
if [[ ${os_version} -lt 20 ]]; then
|
||||||
|
echo -e "${red}please use Ubuntu 20 or higher version! ${plain}\n" && exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif [[ "${release}" == "fedora" ]]; then
|
||||||
|
if [[ ${os_version} -lt 36 ]]; then
|
||||||
|
echo -e "${red}please use Fedora 36 or higher version! ${plain}\n" && exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif [[ "${release}" == "debian" ]]; then
|
||||||
|
if [[ ${os_version} -lt 10 ]]; then
|
||||||
|
echo -e "${red} Please use Debian 10 or higher ${plain}\n" && exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
confirm() {
|
||||||
|
if [[ $# > 1 ]]; then
|
||||||
|
echo && read -p "$1 [Default$2]: " temp
|
||||||
|
if [[ x"${temp}" == x"" ]]; then
|
||||||
|
temp=$2
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
read -p "$1 [y/n]: " temp
|
||||||
|
fi
|
||||||
|
if [[ x"${temp}" == x"y" || x"${temp}" == x"Y" ]]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm_restart() {
|
||||||
|
confirm "Restart the ${1} service" "y"
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
restart
|
||||||
|
else
|
||||||
|
show_menu
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
before_show_menu() {
|
||||||
|
echo && echo -n -e "${yellow}Press enter to return to the main menu: ${plain}" && read temp
|
||||||
|
show_menu
|
||||||
|
}
|
||||||
|
|
||||||
|
install() {
|
||||||
|
bash <(curl -Ls https://raw.githubusercontent.com/alireza0/s-ui/main/install.sh)
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
if [[ $# == 0 ]]; then
|
||||||
|
start
|
||||||
|
else
|
||||||
|
start 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
confirm "This function will forcefully reinstall the latest version, and the data will not be lost. Do you want to continue?" "n"
|
||||||
|
if [[ $? != 0 ]]; then
|
||||||
|
LOGE "Cancelled"
|
||||||
|
if [[ $# == 0 ]]; then
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
bash <(curl -Ls https://raw.githubusercontent.com/alireza0/s-ui/main/install.sh)
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
LOGI "Update is complete, Panel has automatically restarted "
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
custom_version() {
|
||||||
|
echo "Enter the panel version (like 0.0.1):"
|
||||||
|
read panel_version
|
||||||
|
|
||||||
|
if [ -z "$panel_version" ]; then
|
||||||
|
echo "Panel version cannot be empty. Exiting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
download_link="https://raw.githubusercontent.com/alireza0/s-ui/master/install.sh"
|
||||||
|
|
||||||
|
# Use the entered panel version in the download link
|
||||||
|
install_command="bash <(curl -Ls $download_link) $panel_version"
|
||||||
|
|
||||||
|
echo "Downloading and installing panel version $panel_version..."
|
||||||
|
eval $install_command
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall() {
|
||||||
|
confirm "Are you sure you want to uninstall the panel? sing-box will also uninstalled!" "n"
|
||||||
|
if [[ $? != 0 ]]; then
|
||||||
|
if [[ $# == 0 ]]; then
|
||||||
|
show_menu
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
systemctl stop s-ui
|
||||||
|
systemctl disable s-ui
|
||||||
|
systemctl stop sing-box
|
||||||
|
systemctl disable sing-box
|
||||||
|
rm /etc/systemd/system/s-ui.service -f
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl reset-failed
|
||||||
|
rm /etc/s-ui/ -rf
|
||||||
|
rm /usr/local/s-ui/ -rf
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "Uninstalled Successfully, If you want to remove this script, then after exiting the script run ${green}rm /usr/local/s-ui -f${plain} to delete it."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [[ $# == 0 ]]; then
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_admin() {
|
||||||
|
echo "It is not recommended to set admin's credentials to default!"
|
||||||
|
confirm "Are you sure you want to reset admin's credentials to default ?" "n"
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
/usr/local/s-ui/sui admin -reset
|
||||||
|
fi
|
||||||
|
before_show_menu
|
||||||
|
}
|
||||||
|
|
||||||
|
set_admin() {
|
||||||
|
echo "It is not recommended to set admin's credentials to a complex text."
|
||||||
|
read -p "Please set up your username:" config_account
|
||||||
|
read -p "Please set up your password:" config_password
|
||||||
|
/usr/local/s-ui/sui admin -username ${config_account} -password ${config_password}
|
||||||
|
before_show_menu
|
||||||
|
}
|
||||||
|
|
||||||
|
view_admin() {
|
||||||
|
/usr/local/s-ui/sui admin -show
|
||||||
|
before_show_menu
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_setting() {
|
||||||
|
confirm "Are you sure you want to reset settings to default ?" "n"
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
/usr/local/s-ui/sui setting -reset
|
||||||
|
fi
|
||||||
|
before_show_menu
|
||||||
|
}
|
||||||
|
|
||||||
|
set_setting() {
|
||||||
|
echo -e "Enter the ${yellow}panel port${plain} (leave blank for existing/default value):"
|
||||||
|
read config_port
|
||||||
|
echo -e "Enter the ${yellow}panel path${plain} (leave blank for existing/default value):"
|
||||||
|
read config_path
|
||||||
|
|
||||||
|
# Sub configuration
|
||||||
|
echo -e "Enter the ${yellow}subscription port${plain} (leave blank for existing/default value):"
|
||||||
|
read config_subPort
|
||||||
|
echo -e "Enter the ${yellow}subscription path${plain} (leave blank for existing/default value):"
|
||||||
|
read config_subPath
|
||||||
|
|
||||||
|
# Set configs
|
||||||
|
echo -e "${yellow}Initializing, please wait...${plain}"
|
||||||
|
params=""
|
||||||
|
[ -z "$config_port" ] || params="$params -port $config_port"
|
||||||
|
[ -z "$config_path" ] || params="$params -path $config_path"
|
||||||
|
[ -z "$config_subPort" ] || params="$params -subPort $config_subPort"
|
||||||
|
[ -z "$config_subPath" ] || params="$params -subPath $config_subPath"
|
||||||
|
/usr/local/s-ui/sui setting ${params}
|
||||||
|
before_show_menu
|
||||||
|
}
|
||||||
|
|
||||||
|
view_setting() {
|
||||||
|
/usr/local/s-ui/sui setting -show
|
||||||
|
before_show_menu
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
check_status $1
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
LOGI -e "${1} is running, No need to start again, If you need to restart, please select restart"
|
||||||
|
else
|
||||||
|
systemctl start $1
|
||||||
|
sleep 2
|
||||||
|
check_status $1
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
LOGI "${1} Started Successfully"
|
||||||
|
else
|
||||||
|
LOGE "Failed to start ${1}, Probably because it takes longer than two seconds to start, Please check the log information later"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $# == 1 ]]; then
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
check_status $1
|
||||||
|
if [[ $? == 1 ]]; then
|
||||||
|
echo ""
|
||||||
|
LOGI "${1} stopped, No need to stop again!"
|
||||||
|
else
|
||||||
|
systemctl stop $1
|
||||||
|
sleep 2
|
||||||
|
check_status
|
||||||
|
if [[ $? == 1 ]]; then
|
||||||
|
LOGI "${1} stopped successfully"
|
||||||
|
else
|
||||||
|
LOGE "Falied to stop ${1}, Probably because the stop time exceeds two seconds, Please check the log information later"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $# == 1 ]]; then
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
restart() {
|
||||||
|
systemctl restart $1
|
||||||
|
sleep 2
|
||||||
|
check_status $1
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
LOGI "${1} Restarted successfully"
|
||||||
|
else
|
||||||
|
LOGE "Failed to restart ${1}, Probably because it takes longer than two seconds to start, Please check the log information later"
|
||||||
|
fi
|
||||||
|
if [[ $# == 1 ]]; then
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
status() {
|
||||||
|
systemctl status s-ui -l
|
||||||
|
systemctl status sing-box -l
|
||||||
|
if [[ $# == 0 ]]; then
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
enable() {
|
||||||
|
systemctl enable $1
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
LOGI "Set ${1} to boot automatically on startup successfully"
|
||||||
|
else
|
||||||
|
LOGE "Failed to set ${1} Autostart"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $# == 1 ]]; then
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
disable() {
|
||||||
|
systemctl disable $1
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
LOGI "Autostart ${1} Cancelled successfully"
|
||||||
|
else
|
||||||
|
LOGE "Failed to cancel ${1} autostart"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $# == 1 ]]; then
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
show_log() {
|
||||||
|
journalctl -u $1.service -e --no-pager -f
|
||||||
|
if [[ $# == 1 ]]; then
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
update_shell() {
|
||||||
|
wget -O /usr/bin/s-ui -N --no-check-certificate https://github.com/alireza0/s-ui/raw/main/s-ui.sh
|
||||||
|
if [[ $? != 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
LOGE "Failed to download script, Please check whether the machine can connect Github"
|
||||||
|
before_show_menu
|
||||||
|
else
|
||||||
|
chmod +x /usr/bin/s-ui
|
||||||
|
LOGI "Upgrade script succeeded, Please rerun the script" && exit 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 0: running, 1: not running, 2: not installed
|
||||||
|
check_status() {
|
||||||
|
if [[ ! -f "/etc/systemd/system/$1.service" ]]; then
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
temp=$(systemctl status "$1" | grep Active | awk '{print $3}' | cut -d "(" -f2 | cut -d ")" -f1)
|
||||||
|
if [[ x"${temp}" == x"running" ]]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_enabled() {
|
||||||
|
temp=$(systemctl is-enabled $1)
|
||||||
|
if [[ x"${temp}" == x"enabled" ]]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_uninstall() {
|
||||||
|
check_status s-ui
|
||||||
|
if [[ $? != 2 ]]; then
|
||||||
|
echo ""
|
||||||
|
LOGE "Panel is already installed, Please do not reinstall"
|
||||||
|
if [[ $# == 0 ]]; then
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_install() {
|
||||||
|
check_status s-ui
|
||||||
|
if [[ $? == 2 ]]; then
|
||||||
|
echo ""
|
||||||
|
LOGE "Please install the panel first"
|
||||||
|
if [[ $# == 0 ]]; then
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
show_status() {
|
||||||
|
check_status $1
|
||||||
|
case $? in
|
||||||
|
0)
|
||||||
|
echo -e "${1} state: ${green}Running${plain}"
|
||||||
|
show_enable_status $1
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
echo -e "${1} state: ${yellow}Not Running${plain}"
|
||||||
|
show_enable_status $1
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
echo -e "${1} state: ${red}Not Installed${plain}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
show_enable_status() {
|
||||||
|
check_enabled $1
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
echo -e "Start automatically: ${green}Yes${plain}"
|
||||||
|
else
|
||||||
|
echo -e "Start automatically: ${red}No${plain}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_s-ui_status() {
|
||||||
|
count=$(ps -ef | grep "sui" | grep -v "grep" | wc -l)
|
||||||
|
if [[ count -ne 0 ]]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
show_s-ui_status() {
|
||||||
|
check_s-ui_status
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
echo -e "s-ui state: ${green}Running${plain}"
|
||||||
|
else
|
||||||
|
echo -e "s-ui state: ${red}Not Running${plain}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bbr_menu() {
|
||||||
|
echo -e "${green}\t1.${plain} Enable BBR"
|
||||||
|
echo -e "${green}\t2.${plain} Disable BBR"
|
||||||
|
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||||
|
read -p "Choose an option: " choice
|
||||||
|
case "$choice" in
|
||||||
|
0)
|
||||||
|
show_menu
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
enable_bbr
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
disable_bbr
|
||||||
|
;;
|
||||||
|
*) echo "Invalid choice" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
disable_bbr() {
|
||||||
|
|
||||||
|
if ! grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf || ! grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then
|
||||||
|
echo -e "${yellow}BBR is not currently enabled.${plain}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Replace BBR with CUBIC configurations
|
||||||
|
sed -i 's/net.core.default_qdisc=fq/net.core.default_qdisc=pfifo_fast/' /etc/sysctl.conf
|
||||||
|
sed -i 's/net.ipv4.tcp_congestion_control=bbr/net.ipv4.tcp_congestion_control=cubic/' /etc/sysctl.conf
|
||||||
|
|
||||||
|
# Apply changes
|
||||||
|
sysctl -p
|
||||||
|
|
||||||
|
# Verify that BBR is replaced with CUBIC
|
||||||
|
if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "cubic" ]]; then
|
||||||
|
echo -e "${green}BBR has been replaced with CUBIC successfully.${plain}"
|
||||||
|
else
|
||||||
|
echo -e "${red}Failed to replace BBR with CUBIC. Please check your system configuration.${plain}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
enable_bbr() {
|
||||||
|
if grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf && grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then
|
||||||
|
echo -e "${green}BBR is already enabled!${plain}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check the OS and install necessary packages
|
||||||
|
case "${release}" in
|
||||||
|
ubuntu | debian)
|
||||||
|
apt-get update && apt-get install -yqq --no-install-recommends ca-certificates
|
||||||
|
;;
|
||||||
|
centos | almalinux | rocky)
|
||||||
|
yum -y update && yum -y install ca-certificates
|
||||||
|
;;
|
||||||
|
fedora)
|
||||||
|
dnf -y update && dnf -y install ca-certificates
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Enable BBR
|
||||||
|
echo "net.core.default_qdisc=fq" | tee -a /etc/sysctl.conf
|
||||||
|
echo "net.ipv4.tcp_congestion_control=bbr" | tee -a /etc/sysctl.conf
|
||||||
|
|
||||||
|
# Apply changes
|
||||||
|
sysctl -p
|
||||||
|
|
||||||
|
# Verify that BBR is enabled
|
||||||
|
if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "bbr" ]]; then
|
||||||
|
echo -e "${green}BBR has been enabled successfully.${plain}"
|
||||||
|
else
|
||||||
|
echo -e "${red}Failed to enable BBR. Please check your system configuration.${plain}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
install_acme() {
|
||||||
|
cd ~
|
||||||
|
LOGI "install acme..."
|
||||||
|
curl https://get.acme.sh | sh
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "install acme failed"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
LOGI "install acme succeed"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl_cert_issue_main() {
|
||||||
|
echo -e "${green}\t1.${plain} Get SSL"
|
||||||
|
echo -e "${green}\t2.${plain} Revoke"
|
||||||
|
echo -e "${green}\t3.${plain} Force Renew"
|
||||||
|
read -p "Choose an option: " choice
|
||||||
|
case "$choice" in
|
||||||
|
1) ssl_cert_issue ;;
|
||||||
|
2)
|
||||||
|
local domain=""
|
||||||
|
read -p "Please enter your domain name to revoke the certificate: " domain
|
||||||
|
~/.acme.sh/acme.sh --revoke -d ${domain}
|
||||||
|
LOGI "Certificate revoked"
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
local domain=""
|
||||||
|
read -p "Please enter your domain name to forcefully renew an SSL certificate: " domain
|
||||||
|
~/.acme.sh/acme.sh --renew -d ${domain} --force ;;
|
||||||
|
*) echo "Invalid choice" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl_cert_issue() {
|
||||||
|
# check for acme.sh first
|
||||||
|
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
|
||||||
|
echo "acme.sh could not be found. we will install it"
|
||||||
|
install_acme
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "install acme failed, please check logs"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# install socat second
|
||||||
|
case "${release}" in
|
||||||
|
ubuntu|debian)
|
||||||
|
apt update && apt install socat -y ;;
|
||||||
|
centos)
|
||||||
|
yum -y update && yum -y install socat ;;
|
||||||
|
fedora)
|
||||||
|
dnf -y update && dnf -y install socat ;;
|
||||||
|
*)
|
||||||
|
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
|
||||||
|
exit 1 ;;
|
||||||
|
esac
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "install socat failed, please check logs"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
LOGI "install socat succeed..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# get the domain here,and we need verify it
|
||||||
|
local domain=""
|
||||||
|
read -p "Please enter your domain name:" domain
|
||||||
|
LOGD "your domain is:${domain},check it..."
|
||||||
|
# here we need to judge whether there exists cert already
|
||||||
|
local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}')
|
||||||
|
|
||||||
|
if [ ${currentCert} == ${domain} ]; then
|
||||||
|
local certInfo=$(~/.acme.sh/acme.sh --list)
|
||||||
|
LOGE "system already has certs here,can not issue again,current certs details:"
|
||||||
|
LOGI "$certInfo"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
LOGI "your domain is ready for issuing cert now..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# create a directory for install cert
|
||||||
|
certPath="/root/cert/${domain}"
|
||||||
|
if [ ! -d "$certPath" ]; then
|
||||||
|
mkdir -p "$certPath"
|
||||||
|
else
|
||||||
|
rm -rf "$certPath"
|
||||||
|
mkdir -p "$certPath"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# get needed port here
|
||||||
|
local WebPort=80
|
||||||
|
read -p "please choose which port do you use,default will be 80 port:" WebPort
|
||||||
|
if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then
|
||||||
|
LOGE "your input ${WebPort} is invalid,will use default port"
|
||||||
|
fi
|
||||||
|
LOGI "will use port:${WebPort} to issue certs,please make sure this port is open..."
|
||||||
|
# NOTE:This should be handled by user
|
||||||
|
# open the port and kill the occupied progress
|
||||||
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
|
||||||
|
~/.acme.sh/acme.sh --issue -d ${domain} --standalone --httpport ${WebPort}
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "issue certs failed,please check logs"
|
||||||
|
rm -rf ~/.acme.sh/${domain}
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
LOGE "issue certs succeed,installing certs..."
|
||||||
|
fi
|
||||||
|
# install cert
|
||||||
|
~/.acme.sh/acme.sh --installcert -d ${domain} \
|
||||||
|
--key-file /root/cert/${domain}/privkey.pem \
|
||||||
|
--fullchain-file /root/cert/${domain}/fullchain.pem
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "install certs failed,exit"
|
||||||
|
rm -rf ~/.acme.sh/${domain}
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
LOGI "install certs succeed,enable auto renew..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
~/.acme.sh/acme.sh --upgrade --auto-upgrade
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "auto renew failed, certs details:"
|
||||||
|
ls -lah cert/*
|
||||||
|
chmod 755 $certPath/*
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
LOGI "auto renew succeed, certs details:"
|
||||||
|
ls -lah cert/*
|
||||||
|
chmod 755 $certPath/*
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl_cert_issue_CF() {
|
||||||
|
echo -E ""
|
||||||
|
LOGD "******Instructions for use******"
|
||||||
|
LOGI "This Acme script requires the following data:"
|
||||||
|
LOGI "1.Cloudflare Registered e-mail"
|
||||||
|
LOGI "2.Cloudflare Global API Key"
|
||||||
|
LOGI "3.The domain name that has been resolved dns to the current server by Cloudflare"
|
||||||
|
LOGI "4.The script applies for a certificate. The default installation path is /root/cert "
|
||||||
|
confirm "Confirmed?[y/n]" "y"
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
# check for acme.sh first
|
||||||
|
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
|
||||||
|
echo "acme.sh could not be found. we will install it"
|
||||||
|
install_acme
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "install acme failed, please check logs"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
CF_Domain=""
|
||||||
|
CF_GlobalKey=""
|
||||||
|
CF_AccountEmail=""
|
||||||
|
certPath=/root/cert
|
||||||
|
if [ ! -d "$certPath" ]; then
|
||||||
|
mkdir $certPath
|
||||||
|
else
|
||||||
|
rm -rf $certPath
|
||||||
|
mkdir $certPath
|
||||||
|
fi
|
||||||
|
LOGD "Please set a domain name:"
|
||||||
|
read -p "Input your domain here:" CF_Domain
|
||||||
|
LOGD "Your domain name is set to:${CF_Domain}"
|
||||||
|
LOGD "Please set the API key:"
|
||||||
|
read -p "Input your key here:" CF_GlobalKey
|
||||||
|
LOGD "Your API key is:${CF_GlobalKey}"
|
||||||
|
LOGD "Please set up registered email:"
|
||||||
|
read -p "Input your email here:" CF_AccountEmail
|
||||||
|
LOGD "Your registered email address is:${CF_AccountEmail}"
|
||||||
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "Default CA, Lets'Encrypt fail, script exiting..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
export CF_Key="${CF_GlobalKey}"
|
||||||
|
export CF_Email=${CF_AccountEmail}
|
||||||
|
~/.acme.sh/acme.sh --issue --dns dns_cf -d ${CF_Domain} -d *.${CF_Domain} --log
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "Certificate issuance failed, script exiting..."
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
LOGI "Certificate issued Successfully, Installing..."
|
||||||
|
fi
|
||||||
|
~/.acme.sh/acme.sh --installcert -d ${CF_Domain} -d *.${CF_Domain} --ca-file /root/cert/ca.cer \
|
||||||
|
--cert-file /root/cert/${CF_Domain}.cer --key-file /root/cert/${CF_Domain}.key \
|
||||||
|
--fullchain-file /root/cert/fullchain.cer
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "Certificate installation failed, script exiting..."
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
LOGI "Certificate installed Successfully,Turning on automatic updates..."
|
||||||
|
fi
|
||||||
|
~/.acme.sh/acme.sh --upgrade --auto-upgrade
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "Auto update setup Failed, script exiting..."
|
||||||
|
ls -lah cert
|
||||||
|
chmod 755 $certPath
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
LOGI "The certificate is installed and auto-renewal is turned on, Specific information is as follows"
|
||||||
|
ls -lah cert
|
||||||
|
chmod 755 $certPath
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
show_menu
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
show_usage() {
|
||||||
|
echo -e "S-UI Control Menu Usage"
|
||||||
|
echo -e "------------------------------------------"
|
||||||
|
echo -e "SUBCOMMANDS:"
|
||||||
|
echo -e "s-ui - Admin Management Script"
|
||||||
|
echo -e "s-ui start - Start s-ui and sing-box"
|
||||||
|
echo -e "s-ui stop - Stop s-ui and sing-box"
|
||||||
|
echo -e "s-ui restart - Restart s-ui and sing-box"
|
||||||
|
echo -e "s-ui status - Current Status of s-ui and sing-box"
|
||||||
|
echo -e "s-ui enable - Enable Autostart on OS Startup"
|
||||||
|
echo -e "s-ui disable - Disable Autostart on OS Startup"
|
||||||
|
echo -e "s-ui log - Check s-ui Logs"
|
||||||
|
echo -e "s-ui update - Update"
|
||||||
|
echo -e "s-ui install - Install"
|
||||||
|
echo -e "s-ui uninstall - Uninstall"
|
||||||
|
echo -e "s-ui help - Control Menu Usage"
|
||||||
|
echo -e "------------------------------------------"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_menu() {
|
||||||
|
echo -e "
|
||||||
|
${green}S-UI Admin Management Script ${plain}
|
||||||
|
————————————————————————————————
|
||||||
|
${green}0.${plain} Exit
|
||||||
|
————————————————————————————————
|
||||||
|
${green}1.${plain} Install
|
||||||
|
${green}2.${plain} Update
|
||||||
|
${green}3.${plain} Custom Version
|
||||||
|
${green}4.${plain} Uninstall
|
||||||
|
————————————————————————————————
|
||||||
|
${green}5.${plain} Reset admin credentials to default
|
||||||
|
${green}6.${plain} Set admin credentials
|
||||||
|
${green}7.${plain} View admin credentials
|
||||||
|
————————————————————————————————
|
||||||
|
${green}8.${plain} Reset Panel Settings
|
||||||
|
${green}9.${plain} Set Panel settings
|
||||||
|
${green}10.${plain} View Panel Settings
|
||||||
|
————————————————————————————————
|
||||||
|
${green}11.${plain} S-UI Start
|
||||||
|
${green}12.${plain} S-UI Stop
|
||||||
|
${green}13.${plain} S-UI Restart
|
||||||
|
${green}14.${plain} S-UI Check State
|
||||||
|
${green}15.${plain} S-UI Check Logs
|
||||||
|
${green}16.${plain} S-UI Enable Autostart
|
||||||
|
${green}17.${plain} S-UI Disable Autostart
|
||||||
|
————————————————————————————————
|
||||||
|
${green}18.${plain} Sing-Box Start
|
||||||
|
${green}19.${plain} Sing-Box Stop
|
||||||
|
${green}20.${plain} Sing-Box Restart
|
||||||
|
${green}21.${plain} Sing-Box Check State
|
||||||
|
${green}22.${plain} Sing-Box Check Logs
|
||||||
|
${green}23.${plain} Sing-Box Enable Autostart
|
||||||
|
${green}24.${plain} Sing-Box Disable Autostart
|
||||||
|
————————————————————————————————
|
||||||
|
${green}25.${plain} Enable or Disable BBR
|
||||||
|
${green}26.${plain} SSL Certificate Management
|
||||||
|
${green}27.${plain} Cloudflare SSL Certificate
|
||||||
|
————————————————————————————————
|
||||||
|
"
|
||||||
|
show_status s-ui
|
||||||
|
show_status sing-box
|
||||||
|
echo && read -p "Please enter your selection [0-27]: " num
|
||||||
|
|
||||||
|
case "${num}" in
|
||||||
|
0)
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
check_uninstall && install
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
check_install && update
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
check_install && custom_version
|
||||||
|
;;
|
||||||
|
4)
|
||||||
|
check_install && uninstall
|
||||||
|
;;
|
||||||
|
5)
|
||||||
|
check_install && reset_admin
|
||||||
|
;;
|
||||||
|
6)
|
||||||
|
check_install && set_admin
|
||||||
|
;;
|
||||||
|
7)
|
||||||
|
check_install && view_admin
|
||||||
|
;;
|
||||||
|
8)
|
||||||
|
check_install && reset_setting
|
||||||
|
;;
|
||||||
|
9)
|
||||||
|
check_install && set_setting
|
||||||
|
;;
|
||||||
|
10)
|
||||||
|
check_install && view_setting
|
||||||
|
;;
|
||||||
|
11)
|
||||||
|
check_install && start s-ui
|
||||||
|
;;
|
||||||
|
12)
|
||||||
|
check_install && stop s-ui
|
||||||
|
;;
|
||||||
|
13)
|
||||||
|
check_install && restart s-ui
|
||||||
|
;;
|
||||||
|
14)
|
||||||
|
check_install && status s-ui
|
||||||
|
;;
|
||||||
|
15)
|
||||||
|
check_install && show_log s-ui
|
||||||
|
;;
|
||||||
|
16)
|
||||||
|
check_install && enable s-ui
|
||||||
|
;;
|
||||||
|
17)
|
||||||
|
check_install && disable s-ui
|
||||||
|
;;
|
||||||
|
18)
|
||||||
|
check_install && start sing-box
|
||||||
|
;;
|
||||||
|
19)
|
||||||
|
check_install && stop sing-box
|
||||||
|
;;
|
||||||
|
20)
|
||||||
|
check_install && restart sing-box
|
||||||
|
;;
|
||||||
|
21)
|
||||||
|
check_install && status sing-box
|
||||||
|
;;
|
||||||
|
22)
|
||||||
|
check_install && show_log sing-box
|
||||||
|
;;
|
||||||
|
23)
|
||||||
|
check_install && enable sing-box
|
||||||
|
;;
|
||||||
|
24)
|
||||||
|
check_install && disable sing-box
|
||||||
|
;;
|
||||||
|
25)
|
||||||
|
bbr_menu
|
||||||
|
;;
|
||||||
|
26)
|
||||||
|
ssl_cert_issue_main
|
||||||
|
;;
|
||||||
|
27)
|
||||||
|
ssl_cert_issue_CF
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
LOGE "Please enter the correct number [0-27]"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $# > 0 ]]; then
|
||||||
|
case $1 in
|
||||||
|
"start")
|
||||||
|
check_install 0 && start s-ui 0 && start sing-box 0
|
||||||
|
;;
|
||||||
|
"stop")
|
||||||
|
check_install 0 && stop s-ui 0 && stop sing-box 0
|
||||||
|
;;
|
||||||
|
"restart")
|
||||||
|
check_install 0 && restart s-ui 0 && restart sing-box 0
|
||||||
|
;;
|
||||||
|
"status")
|
||||||
|
check_install 0 && status 0
|
||||||
|
;;
|
||||||
|
"enable")
|
||||||
|
check_install 0 && enable s-ui 0 && enable sing-box 0
|
||||||
|
;;
|
||||||
|
"disable")
|
||||||
|
check_install 0 && disable s-ui 0 && disable sing-box 0
|
||||||
|
;;
|
||||||
|
"log")
|
||||||
|
check_install 0 && show_log s-ui 0
|
||||||
|
;;
|
||||||
|
"update")
|
||||||
|
check_install 0 && update 0
|
||||||
|
;;
|
||||||
|
"install")
|
||||||
|
check_uninstall 0 && install 0
|
||||||
|
;;
|
||||||
|
"uninstall")
|
||||||
|
check_install 0 && uninstall 0
|
||||||
|
;;
|
||||||
|
*) show_usage ;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
show_menu
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user