mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 16:00:29 +00:00
Compare commits
39
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79eb0288b4 | ||
|
|
99775e1eb4 | ||
|
|
db848b41d2 | ||
|
|
b5b11dff86 | ||
|
|
9cf4bfc606 | ||
|
|
221162d44f | ||
|
|
617970a1f8 | ||
|
|
5610c73d67 | ||
|
|
51fe02661f | ||
|
|
8f491b2933 | ||
|
|
b7e16bd6d5 | ||
|
|
38ca2ddd01 | ||
|
|
402b41100b | ||
|
|
766beae4e1 | ||
|
|
05f72e9dcf | ||
|
|
23553c68ae | ||
|
|
4dd1eb7a1e | ||
|
|
db307732c6 | ||
|
|
33d4ade1d4 | ||
|
|
186075ff9a | ||
|
|
34df745d1c | ||
|
|
ec363ac157 | ||
|
|
193b0f3cd4 | ||
|
|
67c1bf2924 | ||
|
|
b1373ff77e | ||
|
|
55d0e27212 | ||
|
|
1c052b7c13 | ||
|
|
6c8d48c2dc | ||
|
|
b61dae638b | ||
|
|
872a93aa07 | ||
|
|
b1f337020c | ||
|
|
56858dd74c | ||
|
|
358415f737 | ||
|
|
5e73260103 | ||
|
|
e169f46784 | ||
|
|
39416abd47 | ||
|
|
640a0ca815 | ||
|
|
8e54cd5162 | ||
|
|
0f0099accb |
+1
-1
@@ -33,7 +33,7 @@ builds:
|
||||
- s390x
|
||||
|
||||
archives:
|
||||
- format: tar.gz
|
||||
- formats: [ 'tar.gz' ]
|
||||
name_template: "1panel-v{{ .Version }}-{{ .Os }}-{{ .Arch }}{{- if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
wrap_in_directory: true
|
||||
files:
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
1Panel is an open-source, modern web-based control panel for Linux server management.
|
||||
|
||||
- **Efficient Management**: Through a user-friendly web graphical interface, 1Panel enables users to effortlessly manage their Linux servers. Key features include host monitoring, file management, database administration, and container management.
|
||||
- **Efficient Management**: Through a user-friendly web graphical interface, 1Panel enables users to effortlessly manage their Linux servers. Key features include host monitoring, file management, database administration, container management, LLMs management.
|
||||
- **Rapid Website Deployment**: With deep integration of the popular open-source website building software WordPress, 1Panel streamlines the process of domain binding and SSL certificate configuration, all achievable with just one click.
|
||||
- **Application Store**: 1Panel curates a wide range of high-quality open-source tools and applications, facilitating easy installation and updates for its users.
|
||||
- **Security and Reliability**: By leveraging containerization and secure application deployment practices, 1Panel minimizes vulnerability exposure. It further enhances security through integrated firewall management and log auditing capabilities.
|
||||
@@ -61,7 +61,9 @@ Please refer to our [documentation](https://docs.1panel.pro/quick_start/) for mo
|
||||
|
||||
## Pro Edition
|
||||
|
||||
Compared to the OSS Edition, 1Panel Pro Edition provides users with a wealth of enhanced features and technical support services. Enhanced features include WAF enhancement, website tamper protection, website monitoring, GPU monitoring, custom logo and theme color, etc. [Click to view the detailed introduction of the Pro Edition](https://1panel.pro/pricing).
|
||||
Compared to the OSS Edition, 1Panel Pro Edition provides users with a wealth of enhanced features and technical support services. Enhanced features include WAF enhancement, Website monitoring, Mobile APP, custom logo and theme, etc.
|
||||
|
||||
[Click to see Pro Edition details](https://1panel.pro/pricing).
|
||||
|
||||
## Security Information
|
||||
|
||||
|
||||
@@ -391,7 +391,7 @@ func (b *BaseApi) ContainerOperation(c *gin.Context) {
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Container stats
|
||||
// @Param id path integer true "container id"
|
||||
// @Param id path string true "container id"
|
||||
// @Success 200 {object} dto.ContainerStats
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
|
||||
@@ -66,6 +66,10 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if req.Key == "SecurityEntrance" {
|
||||
entranceValue := base64.StdEncoding.EncodeToString([]byte(req.Value))
|
||||
c.SetCookie("SecurityEntrance", entranceValue, 0, "", "", false, true)
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
|
||||
+70
-160
@@ -10,7 +10,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/service"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/copier"
|
||||
@@ -69,12 +68,11 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
|
||||
|
||||
<-quitChan
|
||||
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
dt := time.Now().Add(time.Second)
|
||||
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
|
||||
}
|
||||
|
||||
func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
@@ -96,193 +94,105 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||
return
|
||||
}
|
||||
source := c.Query("source")
|
||||
var containerID string
|
||||
var initCmd string
|
||||
switch source {
|
||||
case "redis":
|
||||
containerID, initCmd, err = loadRedisInitCmd(c)
|
||||
case "ollama":
|
||||
containerID, initCmd, err = loadOllamaInitCmd(c)
|
||||
case "container":
|
||||
containerID, initCmd, err = loadContainerInitCmd(c)
|
||||
default:
|
||||
if wshandleError(wsConn, fmt.Errorf("not support such source %s", source)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
pidMap := loadMapFromDockerTop(containerID)
|
||||
slave, err := terminal.NewCommand("clear && " + initCmd)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(containerID, strings.ReplaceAll(initCmd, fmt.Sprintf("docker exec -it %s ", containerID), ""), pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
|
||||
quitChan := make(chan bool, 3)
|
||||
tty.Start(quitChan)
|
||||
go slave.Wait(quitChan)
|
||||
|
||||
<-quitChan
|
||||
|
||||
global.LOG.Info("websocket finished")
|
||||
dt := time.Now().Add(time.Second)
|
||||
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
|
||||
|
||||
}
|
||||
|
||||
func loadRedisInitCmd(c *gin.Context) (string, string, error) {
|
||||
name := c.Query("name")
|
||||
from := c.Query("from")
|
||||
commands := []string{"redis-cli"}
|
||||
commands := "redis-cli"
|
||||
database, err := databaseService.Get(name)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "no such database in db")) {
|
||||
return
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("no such database in db, err: %v", err)
|
||||
}
|
||||
if from == "local" {
|
||||
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: "redis"})
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "no such database in db")) {
|
||||
return
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("no such app in db, err: %v", err)
|
||||
}
|
||||
name = redisInfo.ContainerName
|
||||
if len(database.Password) != 0 {
|
||||
commands = []string{"redis-cli", "-a", database.Password, "--no-auth-warning"}
|
||||
commands = "redis-cli -a " + database.Password + " --no-auth-warning"
|
||||
}
|
||||
} else {
|
||||
itemPort := fmt.Sprintf("%v", database.Port)
|
||||
commands = []string{"redis-cli", "-h", database.Address, "-p", itemPort}
|
||||
commands = fmt.Sprintf("redis-cli -h %s -p %v", database.Address, database.Port)
|
||||
if len(database.Password) != 0 {
|
||||
commands = []string{"redis-cli", "-h", database.Address, "-p", itemPort, "-a", database.Password, "--no-auth-warning"}
|
||||
commands = fmt.Sprintf("redis-cli -h %s -p %v -a %s --no-auth-warning", database.Address, database.Port, database.Password)
|
||||
}
|
||||
name = "1Panel-redis-cli-tools"
|
||||
}
|
||||
|
||||
pidMap := loadMapFromDockerTop(name)
|
||||
itemCmds := append([]string{"exec", "-it", name}, commands...)
|
||||
slave, err := terminal.NewCommand(itemCmds)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(name, strings.Join(commands, " "), pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
|
||||
quitChan := make(chan bool, 3)
|
||||
tty.Start(quitChan)
|
||||
go slave.Wait(quitChan)
|
||||
|
||||
<-quitChan
|
||||
|
||||
global.LOG.Info("websocket finished")
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
return name, fmt.Sprintf("docker exec -it %s %s", name, commands), nil
|
||||
}
|
||||
|
||||
func (b *BaseApi) OllamaWsSsh(c *gin.Context) {
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
if global.CONF.System.IsDemo {
|
||||
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
||||
return
|
||||
}
|
||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||
return
|
||||
}
|
||||
func loadOllamaInitCmd(c *gin.Context) (string, string, error) {
|
||||
name := c.Query("name")
|
||||
if cmd.CheckIllegal(name) {
|
||||
if wshandleError(wsConn, errors.New(" The command contains illegal characters.")) {
|
||||
return
|
||||
}
|
||||
return "", "", fmt.Errorf("ollama model %s contains illegal characters", name)
|
||||
}
|
||||
container, err := service.LoadContainerName()
|
||||
if wshandleError(wsConn, errors.WithMessage(err, " load container name for ollama failed")) {
|
||||
return
|
||||
}
|
||||
commands := []string{"ollama", "run", name}
|
||||
|
||||
pidMap := loadMapFromDockerTop(container)
|
||||
fmt.Println("pidMap")
|
||||
for k, v := range pidMap {
|
||||
fmt.Println(k, v)
|
||||
}
|
||||
itemCmds := append([]string{"exec", "-it", container}, commands...)
|
||||
slave, err := terminal.NewCommand(itemCmds)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(container, strings.Join(commands, " "), pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
|
||||
quitChan := make(chan bool, 3)
|
||||
tty.Start(quitChan)
|
||||
go slave.Wait(quitChan)
|
||||
|
||||
<-quitChan
|
||||
|
||||
global.LOG.Info("websocket finished")
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
ollamaInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: "", Type: "ollama"})
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("no such app in db, err: %v", err)
|
||||
}
|
||||
containerName := ollamaInfo.ContainerName
|
||||
return containerName, fmt.Sprintf("docker exec -it %s ollama run %s", containerName, name), nil
|
||||
}
|
||||
|
||||
func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
if global.CONF.System.IsDemo {
|
||||
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func loadContainerInitCmd(c *gin.Context) (string, string, error) {
|
||||
containerID := c.Query("containerid")
|
||||
command := c.Query("command")
|
||||
user := c.Query("user")
|
||||
if len(command) == 0 || len(containerID) == 0 {
|
||||
if wshandleError(wsConn, errors.New("error param of command or containerID")) {
|
||||
return
|
||||
}
|
||||
}
|
||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
||||
return
|
||||
}
|
||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||
return
|
||||
}
|
||||
|
||||
cmds := []string{"exec", containerID, command}
|
||||
if len(user) != 0 {
|
||||
cmds = []string{"exec", "-u", user, containerID, command}
|
||||
}
|
||||
if cmd.CheckIllegal(user, containerID, command) {
|
||||
if wshandleError(wsConn, errors.New(" The command contains illegal characters.")) {
|
||||
return
|
||||
}
|
||||
return "", "", fmt.Errorf("the command contains illegal characters. command: %s, user: %s, containerID: %s", command, user, containerID)
|
||||
}
|
||||
stdout, err := cmd.ExecWithCheck("docker", cmds...)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, stdout)) {
|
||||
return
|
||||
if len(command) == 0 || len(containerID) == 0 {
|
||||
return "", "", fmt.Errorf("error param of command: %s or containerID: %s", command, containerID)
|
||||
}
|
||||
|
||||
commands := []string{"exec", "-it", containerID, command}
|
||||
command = fmt.Sprintf("docker exec -it %s %s", containerID, command)
|
||||
if len(user) != 0 {
|
||||
commands = []string{"exec", "-it", "-u", user, containerID, command}
|
||||
}
|
||||
pidMap := loadMapFromDockerTop(containerID)
|
||||
slave, err := terminal.NewCommand(commands)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer killBash(containerID, command, pidMap)
|
||||
defer slave.Close()
|
||||
|
||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, true)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
command = fmt.Sprintf("docker exec -it -u %s %s %s", user, containerID, command)
|
||||
}
|
||||
|
||||
quitChan := make(chan bool, 3)
|
||||
tty.Start(quitChan)
|
||||
go slave.Wait(quitChan)
|
||||
|
||||
<-quitChan
|
||||
|
||||
global.LOG.Info("websocket finished")
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
return containerID, command, nil
|
||||
}
|
||||
|
||||
func wshandleError(ws *websocket.Conn, err error) bool {
|
||||
|
||||
@@ -36,9 +36,10 @@ type OllamaBindDomainReq struct {
|
||||
}
|
||||
|
||||
type OllamaBindDomainRes struct {
|
||||
Domain string `json:"domain"`
|
||||
SSLID uint `json:"sslID"`
|
||||
AllowIPs []string `json:"allowIPs"`
|
||||
WebsiteID uint `json:"websiteID"`
|
||||
ConnUrl string `json:"connUrl"`
|
||||
Domain string `json:"domain"`
|
||||
SSLID uint `json:"sslID"`
|
||||
AcmeAccountID uint `json:"acmeAccountID"`
|
||||
AllowIPs []string `json:"allowIPs"`
|
||||
WebsiteID uint `json:"websiteID"`
|
||||
ConnUrl string `json:"connUrl"`
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ type PortRuleOperate struct {
|
||||
}
|
||||
|
||||
type ForwardRuleOperate struct {
|
||||
Rules []struct {
|
||||
ForceDelete bool `json:"forceDelete"`
|
||||
Rules []struct {
|
||||
Operation string `json:"operation" validate:"required,oneof=add remove"`
|
||||
Num string `json:"num"`
|
||||
Protocol string `json:"protocol" validate:"required,oneof=tcp udp tcp/udp"`
|
||||
|
||||
@@ -71,6 +71,7 @@ type SettingInfo struct {
|
||||
ApiKey string `json:"apiKey"`
|
||||
IpWhiteList string `json:"ipWhiteList"`
|
||||
ApiKeyValidityTime string `json:"apiKeyValidityTime"`
|
||||
LicenseVerify string `json:"licenseVerify"`
|
||||
}
|
||||
|
||||
type SettingUpdate struct {
|
||||
|
||||
@@ -245,6 +245,15 @@ func (u *AIToolService) BindDomain(req dto.OllamaBindDomain) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.SSLID > 0 {
|
||||
ssl, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ssl.Pem == "" {
|
||||
return buserr.New("ErrSSL")
|
||||
}
|
||||
}
|
||||
createWebsiteReq := request.WebsiteCreate{
|
||||
PrimaryDomain: req.Domain,
|
||||
Alias: strings.ToLower(req.Domain),
|
||||
@@ -297,6 +306,8 @@ func (u *AIToolService) GetBindDomain(req dto.OllamaBindDomainReq) (*dto.OllamaB
|
||||
res.Domain = website.PrimaryDomain
|
||||
if website.WebsiteSSLID > 0 {
|
||||
res.SSLID = website.WebsiteSSLID
|
||||
ssl, _ := websiteSSLRepo.GetFirst(commonRepo.WithByID(website.WebsiteSSLID))
|
||||
res.AcmeAccountID = ssl.AcmeAccountID
|
||||
}
|
||||
res.ConnUrl = fmt.Sprintf("%s://%s", strings.ToLower(website.Protocol), website.PrimaryDomain)
|
||||
res.AllowIPs = GetAllowIps(website)
|
||||
@@ -318,6 +329,15 @@ func (u *AIToolService) UpdateBindDomain(req dto.OllamaBindDomain) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.SSLID > 0 {
|
||||
ssl, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ssl.Pem == "" {
|
||||
return buserr.New("ErrSSL")
|
||||
}
|
||||
}
|
||||
websiteService := NewIWebsiteService()
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
|
||||
@@ -2,13 +2,14 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
@@ -169,8 +170,9 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
global.LOG.Errorf("rollback mysql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
|
||||
} else {
|
||||
global.LOG.Infof("rollback mysql db %s from %s successful", req.DetailName, rollbackFile)
|
||||
}
|
||||
global.LOG.Infof("rollback mysql db %s from %s successful", req.DetailName, rollbackFile)
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
} else {
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
@@ -186,6 +188,7 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
global.LOG.Errorf("recover mysql db %s from %s failed, err: %v", req.DetailName, req.File, err)
|
||||
return err
|
||||
}
|
||||
isOk = true
|
||||
|
||||
@@ -2,13 +2,14 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
pgclient "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
|
||||
@@ -157,8 +158,9 @@ func handlePostgresqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
global.LOG.Errorf("rollback postgresql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
|
||||
} else {
|
||||
global.LOG.Infof("rollback postgresql db %s from %s successful", req.DetailName, rollbackFile)
|
||||
}
|
||||
global.LOG.Infof("rollback postgresql db %s from %s successful", req.DetailName, rollbackFile)
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
} else {
|
||||
_ = os.RemoveAll(rollbackFile)
|
||||
@@ -171,6 +173,7 @@ func handlePostgresqlRecover(req dto.CommonRecover, isRollback bool) error {
|
||||
Username: dbInfo.Username,
|
||||
Timeout: 300,
|
||||
}); err != nil {
|
||||
global.LOG.Errorf("recover postgresql db %s from %s failed, err: %v", req.DetailName, req.File, err)
|
||||
return err
|
||||
}
|
||||
isOk = true
|
||||
|
||||
@@ -255,14 +255,14 @@ func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var datas []dto.ContainerListStats
|
||||
datas := make([]dto.ContainerListStats, len(list))
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(list))
|
||||
for i := 0; i < len(list); i++ {
|
||||
go func(item types.Container) {
|
||||
datas = append(datas, loadCpuAndMem(client, item.ID))
|
||||
go func(index int, item types.Container) {
|
||||
datas[index] = loadCpuAndMem(client, item.ID)
|
||||
wg.Done()
|
||||
}(list[i])
|
||||
}(i, list[i])
|
||||
}
|
||||
wg.Wait()
|
||||
return datas, nil
|
||||
@@ -937,15 +937,27 @@ func calculateCPUPercentUnix(stats *types.StatsJSON) float64 {
|
||||
}
|
||||
return cpuPercent
|
||||
}
|
||||
|
||||
func calculateMemPercentUnix(memStats types.MemoryStats) float64 {
|
||||
memPercent := 0.0
|
||||
memUsage := float64(memStats.Usage)
|
||||
memLimit := float64(memStats.Limit)
|
||||
memUsage := calculateMemUsageUnixNoCache(memStats)
|
||||
memLimit := memStats.Limit
|
||||
if memUsage > 0.0 && memLimit > 0.0 {
|
||||
memPercent = (memUsage / memLimit) * 100.0
|
||||
memPercent = (float64(memUsage) / float64(memLimit)) * 100.0
|
||||
}
|
||||
return memPercent
|
||||
}
|
||||
|
||||
func calculateMemUsageUnixNoCache(mem types.MemoryStats) uint64 {
|
||||
if v, isCgroup1 := mem.Stats["total_inactive_file"]; isCgroup1 && v < mem.Usage {
|
||||
return mem.Usage - v
|
||||
}
|
||||
if v := mem.Stats["inactive_file"]; v < mem.Usage {
|
||||
return mem.Usage - v
|
||||
}
|
||||
return mem.Usage
|
||||
}
|
||||
|
||||
func calculateBlockIO(blkio types.BlkioStats) (blkRead float64, blkWrite float64) {
|
||||
for _, bioEntry := range blkio.IoServiceBytesRecursive {
|
||||
switch strings.ToLower(bioEntry.Op) {
|
||||
@@ -1063,7 +1075,7 @@ func loadCpuAndMem(client *client.Client, container string) dto.ContainerListSta
|
||||
data.PercpuUsage = len(stats.CPUStats.CPUUsage.PercpuUsage)
|
||||
|
||||
data.MemoryCache = stats.MemoryStats.Stats["cache"]
|
||||
data.MemoryUsage = stats.MemoryStats.Usage
|
||||
data.MemoryUsage = calculateMemUsageUnixNoCache(stats.MemoryStats)
|
||||
data.MemoryLimit = stats.MemoryStats.Limit
|
||||
|
||||
data.MemoryPercent = calculateMemPercentUnix(stats.MemoryStats)
|
||||
@@ -1183,9 +1195,10 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
|
||||
for _, volume := range req.Volumes {
|
||||
if volume.Type == "volume" {
|
||||
hostConf.Mounts = append(hostConf.Mounts, mount.Mount{
|
||||
Type: mount.Type(volume.Type),
|
||||
Source: volume.SourceDir,
|
||||
Target: volume.ContainerDir,
|
||||
Type: mount.Type(volume.Type),
|
||||
Source: volume.SourceDir,
|
||||
Target: volume.ContainerDir,
|
||||
ReadOnly: volume.Mode == "ro",
|
||||
})
|
||||
config.Volumes[volume.ContainerDir] = struct{}{}
|
||||
} else {
|
||||
@@ -1262,12 +1275,12 @@ func loadContainerPortForInfo(itemPorts []types.Port) []dto.PortHelper {
|
||||
var exposedPorts []dto.PortHelper
|
||||
samePortMap := make(map[string]dto.PortHelper)
|
||||
ports := transPortToStr(itemPorts)
|
||||
var itemPort dto.PortHelper
|
||||
for _, item := range ports {
|
||||
itemStr := strings.Split(item, "->")
|
||||
if len(itemStr) < 2 {
|
||||
continue
|
||||
}
|
||||
var itemPort dto.PortHelper
|
||||
lastIndex := strings.LastIndex(itemStr[0], ":")
|
||||
if lastIndex == -1 {
|
||||
itemPort.HostPort = itemStr[0]
|
||||
|
||||
@@ -381,6 +381,10 @@ func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
|
||||
TargetIP: r.TargetIP,
|
||||
TargetPort: r.TargetPort,
|
||||
}, r.Operation); err != nil {
|
||||
if req.ForceDelete {
|
||||
global.LOG.Error(err)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ type System struct {
|
||||
Language string `mapstructure:"language"`
|
||||
IsDemo bool `mapstructure:"is_demo"`
|
||||
IsIntl bool `mapstructure:"is_intl"`
|
||||
LicenseVerify string `mapstructure:"license_verify"`
|
||||
AppRepo string `mapstructure:"app_repo"`
|
||||
ChangeUserInfo string `mapstructure:"change_user_info"`
|
||||
OneDriveID string `mapstructure:"one_drive_id"`
|
||||
|
||||
@@ -287,6 +287,7 @@ SystemMode: "mode: "
|
||||
|
||||
#ai-tool
|
||||
ErrOpenrestyInstall: 'Please install Openresty first'
|
||||
ErrSSL: "Certificate content is empty, please check the certificate!"
|
||||
|
||||
#mobile app
|
||||
ErrVerifyToken: 'Token verification error, please reset and scan again.'
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
#cmd
|
||||
AppVersion: "Lotfan noskhe-ye barname ra vared konid"
|
||||
AppCommands: "Dasturat-e marbut be barname"
|
||||
AppInit: "Barname ra shoru' konid"
|
||||
AppKeyVal: "Kelid-e barname (faghat az zabane englisi poshtibani mikonad)"
|
||||
AppCreateFileErr: "Khalgh-e file {{ .name }} namovaffagh bud {{ .err }}"
|
||||
AppCreateDirErr: "Khalgh-e folder {{ .name }} namovaffagh bud {{ .err }}"
|
||||
AppMissKey: "Kelid-e barname mojood nist, az -k estefade konid"
|
||||
AppMissVersion: "Noskhe-ye barname mojood nist, az -v estefade konid"
|
||||
AppVersionExist: "Noskhe ghablan mojood ast!"
|
||||
AppCreateSuccessful: "Khalgh ba movaffaghiat anjam shod!"
|
||||
AppWriteErr: "Neveshtan file {{ .name }} namovaffagh bud {{ .err }}"
|
||||
SudoHelper: "Lotfan in eskript ra be onvane karbare root ya ba mojavez sudo ejra konid"
|
||||
ListenIPCommands: "IP goosh dadan ra taghir dahid"
|
||||
ListenIPv4: "Goosh dadan bar IPv4"
|
||||
ListenIPv6: "Goosh dadan bar IPv6"
|
||||
ListenChangeSuccessful: "Taghir movaffagh! Hala bar {{ .value }} goosh midahad"
|
||||
ResetCommands: "E'ade setadad etela'at-e system"
|
||||
ResetMFA: "Fa'al-sazi do marhale'i 1Panel ra laghv konid"
|
||||
ResetHttps: "Voroud HTTPS baraye 1Panel ra laghv konid"
|
||||
ResetEntrance: "Voroud amin 1Panel ra laghv konid"
|
||||
ResetIPs: "Mahdudiyat IP-haye mojaz 1Panel ra laghv konid"
|
||||
ResetDomain: "Sazgari domain 1Panel ra laghv konid"
|
||||
RestoreCommands: "Bargasht be khadamate 1Panel va data"
|
||||
RestoreNoSuchFile: "Hich file mojood baraye bargasht nist"
|
||||
RestoreStep1: "(1/5) Shoru' bargasht be khadamate 1Panel va data az directory {{ .name }} ..."
|
||||
RestoreStep2: "(2/5) Bargasht be binary 1Panel ba movaffaghiat anjam shod"
|
||||
RestoreStep3: "(3/5) Bargasht be script 1Panel ba movaffaghiat anjam shod"
|
||||
RestoreStep4: "(4/5) Bargasht be khadamat 1Panel ba movaffaghiat anjam shod"
|
||||
RestoreStep5: "(5/5) Bargasht be data 1Panel ba movaffaghiat anjam shod"
|
||||
RestoreSuccessful: "Bargasht ba movaffaghiat anjam shod! Khadamat dar hale restart ast, lotfan montazer bashid..."
|
||||
UpdateCommands: "Be'rooz resani etela'at-e panel"
|
||||
UpdateUser: "Be'rooz resani karbare panel"
|
||||
UpdatePassword: "Be'rooz resani ramz-e panel"
|
||||
UpdatePort: "Be'rooz resani port-e panel"
|
||||
UpdateUserNull: "Khatta: karbare panel khalist!"
|
||||
UpdateUserBlank: "Khatta: karbare panel shamel faseleh ast!"
|
||||
UpdateUserFormat: "Khatta: format-e karbare panel na dorost ast! Faghat az englisi, chine'i, adad va _, ba tool-e 3-30 poshtibani mikonad"
|
||||
UpdateUserErr: "Khatta: be'rooz resani karbare panel namovaffagh bud, {{ .err }}"
|
||||
UpdateSuccessful: "Be'rooz resani ba movaffaghiat anjam shod!"
|
||||
UpdateUserResult: "Karbare panel: {{ .name }}"
|
||||
UpdatePasswordRead: "Khatta: khatayi dar khandan etela'at-e ramz-e panel, {{ .err }}"
|
||||
UpdatePasswordNull: "Khatta: ramz-e panel khalist!"
|
||||
UpdateUPasswordBlank: "Khatta: ramz-e panel shamel faseleh ast!"
|
||||
UpdatePasswordFormat: "Khatta: ramz-e panel faghat az harf, adad, karakter-haye vizhe !@#$%*_,.? ba tool-e 8-30 poshtibani mikonad!"
|
||||
UpdatePasswordLen: "Khatta: lotfan ramzi bishtar az 6 karakter vared konid!"
|
||||
UpdatePasswordRe: "Teyid-e ramz:"
|
||||
UpdatePasswordErr: "Khatta: be'rooz resani ramz-e panel namovaffagh bud, {{ .err }}"
|
||||
UpdatePasswordSame: "Khatta: do ramz ba ham yeksan nistand, lotfan barrasi konid va dobare talash konid!"
|
||||
UpdatePasswordResult: "Ramz-e panel: {{ .name }}"
|
||||
UpdatePortFormat: "Khatta: shomareye port bayad bein 1 ta 65535 bashad!"
|
||||
UpdatePortUsed: "Khatta: shomareye port ghablan estefade shode ast, lotfan barrasi konid va dobare talash konid!"
|
||||
UpdatePortErr: "Khatta: be'rooz resani port-e panel namovaffagh bud, {{ .err }}"
|
||||
UpdatePortResult: "Port-e panel: {{ .name }}"
|
||||
UpdatePortFirewallAdd: "Ezafe kardane ghanoun-e port firewall namovaffagh bud, {{ .err }}, lotfan port {{ .name }} ra be dast az qavaneen firewall ezafe konid."
|
||||
UpdatePortFirewallDel: "Khatta: hazf-e port firewall namovaffagh bud, {{ .err }}"
|
||||
UpdatePortFirewallReload: "Reload firewall namovaffagh bud, {{ .err }}, lotfan firewall ra be dast reload konid."
|
||||
UserInfo: "Daryaft etela'at-e panel"
|
||||
UserInfoAddr: "Address-e panel: "
|
||||
UserInfoPassHelp: "Nokte: baraye taghiri ramz, mitavanid dastur zir ra anjam dahid: "
|
||||
DBConnErr: "Khatta: etesal be database namovaffagh bud, {{ .err }}"
|
||||
SystemVersion: "noskhe: "
|
||||
SystemMode: "halat: "
|
||||
+41
-39
@@ -1,17 +1,17 @@
|
||||
ErrInvalidParams: "リクエストパラメーターエラー:{{.Detail}}"
|
||||
ErrTokenParse: "トークン生成エラー:{{.Detail}}"
|
||||
ErrInvalidParams: "リクエストパラメーターエラー: {{.Detail}}"
|
||||
ErrTokenParse: "トークン生成エラー: {{.Detail}}"
|
||||
ErrInitialPassword: "初期パスワードエラー"
|
||||
ErrInternalServer: "サービス内部エラー:{{.Detail}}"
|
||||
ErrInternalServer: "サービス内部エラー: {{.Detail}}"
|
||||
ErrRecordExist: "レコードはすでに存在します"
|
||||
ErrRecordNotFound: "記録が見つかりません"
|
||||
ErrStructTransform: "タイプ変換障害:{{.Detail}}"
|
||||
ErrNotLogin: "ユーザーはログインしません:{{.Detail}}"
|
||||
ErrPasswordExpired: "現在のパスワードが期限切れになっています:{{.Detail}}"
|
||||
ErrNotSupportType: "システムは現在のタイプをサポートしていません:{{.Detail}}"
|
||||
ErrApiConfigStatusInvalid: "APIインターフェイスアクセス禁止:{{.Detail}}"
|
||||
ErrApiConfigKeyInvalid: "APIインターフェイスキーエラー:{{.Detail}}"
|
||||
ErrApiConfigIPInvalid: "APIインターフェイスIPはホワイトリストにありません:{{.Detail}}"
|
||||
ErrApiConfigDisable: "このインターフェイスは、APIインターフェイスコールの使用を禁止しています:{{.Detail}}"
|
||||
ErrStructTransform: "タイプ変換障害: {{.Detail}}"
|
||||
ErrNotLogin: "ユーザーはログインしません: {{.Detail}}"
|
||||
ErrPasswordExpired: "現在のパスワードが期限切れになっています: {{.Detail}}"
|
||||
ErrNotSupportType: "システムは現在のタイプをサポートしていません: {{.Detail}}"
|
||||
ErrApiConfigStatusInvalid: "APIインターフェイスアクセス禁止: {{.Detail}}"
|
||||
ErrApiConfigKeyInvalid: "APIインターフェイスキーエラー: {{.Detail}}"
|
||||
ErrApiConfigIPInvalid: "APIインターフェイスIPはホワイトリストにありません: {{.Detail}}"
|
||||
ErrApiConfigDisable: "このインターフェイスは、APIインターフェイスコールの使用を禁止しています: {{.Detail}}"
|
||||
|
||||
#common
|
||||
ErrNameIsExist: "名前はすでに存在しています"
|
||||
@@ -92,10 +92,10 @@ ErrDomainIsExist: "ドメインはすでに存在しています"
|
||||
ErrAliasIsExist: "エイリアスはすでに存在しています"
|
||||
ErrAppDelete: '他のウェブサイトはこのアプリを使用します'
|
||||
ErrGroupIsUsed: "グループは使用されており、削除することはできません"
|
||||
ErrBackupMatch: "バックアップファイルは、ウェブサイトの現在の部分的なデータと一致しません:{{.Detail}}"
|
||||
ErrBackupMatch: "バックアップファイルは、ウェブサイトの現在の部分的なデータと一致しません: {{.Detail}}"
|
||||
ErrBackupExist: 'バックアップファイルは、存在しない元のデータの一部に対応しています。{{.Detail}}'
|
||||
ErrPHPResource: 'ローカルランタイムはスイッチングをサポートしません!'
|
||||
ErrPathPermission: 'インデックスディレクトリで1000:1000以外のアクセス許可を持つフォルダーが検出されたため、Webサイトにアクセスするとアクセスが拒否された場合があります。上の[保存]ボタンをクリックしてください'
|
||||
ErrPathPermission: 'インデックスディレクトリで1000: 1000以外のアクセス許可を持つフォルダーが検出されたため、Webサイトにアクセスするとアクセスが拒否された場合があります。上の[保存]ボタンをクリックしてください'
|
||||
ErrDomainIsUsed: "ドメインはすでにWebサイトで使用されています{{.Name}}"
|
||||
ErrDomainFormat: "{{.name}}ドメイン形式エラー"
|
||||
ErrDefaultAlias: "デフォルトは予約済みのコード名です。別のコード名を使用してください"
|
||||
@@ -124,7 +124,7 @@ ErrDeleteCAWithSSL: "現在の組織の下に発行された証明書があり
|
||||
ErrDeleteWithPanelSSL: "パネルSSL構成はこの証明書を使用しており、削除できません"
|
||||
ErrDefaultCA: "デフォルトの証明書当局は削除できません"
|
||||
ApplyWebSiteSSLLog: "{{.name}} Webサイト証明書の更新を開始します"
|
||||
ErrUpdateWebsiteSSL: "{{.name}}ウェブサイトは証明書の更新に失敗しました:{{.err}}"
|
||||
ErrUpdateWebsiteSSL: "{{.name}}ウェブサイトは証明書の更新に失敗しました: {{.err}}"
|
||||
ApplyWebSiteSSLSuccess: "ウェブサイトの証明書を正常に更新します"
|
||||
ErrExecShell: "スクリプトの実行に失敗した{{.err}}"
|
||||
ExecShellStart: "スクリプトの実行を開始します"
|
||||
@@ -146,7 +146,7 @@ ErrTypeOfRedis: "回復ファイルのタイプは、現在の永続性モード
|
||||
ErrInUsed: "{{.Detail}}が使用されており、削除できません"
|
||||
ErrObjectInUsed: "このオブジェクトは使用されており、削除することはできません"
|
||||
ErrPortRules: "ポートの数が一致しません。再入力してください!"
|
||||
ErrPgImagePull: "画像プルタイムアウト。画像の加速を構成するか、Postgres:16.0-Alpine画像を手動で引いて、再試行してください"
|
||||
ErrPgImagePull: "画像プルタイムアウト。画像の加速を構成するか、Postgres: 16.0-Alpine画像を手動で引いて、再試行してください"
|
||||
|
||||
#runtime
|
||||
ErrDirNotFound: "ビルドフォルダーは存在しません!ファイルの整合性を確認してください!"
|
||||
@@ -253,37 +253,39 @@ UpdateCommands: "パネル情報を更新します"
|
||||
UpdateUser: "パネルユーザーを更新します"
|
||||
UpdatePassword: "パスワードを更新します"
|
||||
UpdatePort: "パネルポートを更新します"
|
||||
UpdateUserNull: "エラー:パネルユーザーは空です!"
|
||||
UpdateUserBlank: "エラー:パネルユーザーにはスペースが含まれています!"
|
||||
UpdateUserFormat: "エラー:無効なパネルユーザー形式!英語、中国語、数字、_、長さ3〜30のみをサポートしています"
|
||||
UpdateUserErr: "エラー:パネルユーザーの更新に失敗しました{{.err}}"
|
||||
UpdateUserNull: "エラー: パネルユーザーは空です!"
|
||||
UpdateUserBlank: "エラー: パネルユーザーにはスペースが含まれています!"
|
||||
UpdateUserFormat: "エラー: 無効なパネルユーザー形式!英語、中国語、数字、_、長さ3〜30のみをサポートしています"
|
||||
UpdateUserErr: "エラー: パネルユーザーの更新に失敗しました{{.err}}"
|
||||
UpdateSuccessful: "更新成功!"
|
||||
UpdateUserResult: "パネルユーザー:{{.name}}"
|
||||
UpdatePasswordRead: "エラー:パネルパスワード情報の読み取りに失敗しました、{{.err}}"
|
||||
UpdatePasswordNull: "エラー:パネルパスワードは空です!"
|
||||
UpdateUPasswordBlank: "エラー:パスワードにはスペースが含まれています!"
|
||||
UpdatePasswordFormat: "エラー:パネルパスワードは、文字、数字、特殊文字のみをサポートします!@#$%*_、。、、長さ8-30!"
|
||||
UpdatePasswordLen: "エラー:6文字より長くパスワードを入力してください!"
|
||||
UpdatePasswordRe: "パスワードを認証する:"
|
||||
UpdatePasswordErr: "エラー:{{.err}}のパスワードの更新に失敗しました"
|
||||
UpdatePasswordSame: "エラー:2つのパスワードが一致しません。チェックして再試行してください!"
|
||||
UpdatePasswordResult: "パネルパスワード:{{.name}}"
|
||||
UpdatePortFormat: "エラー:入力ポート番号は1〜65535でなければなりません!"
|
||||
UpdatePortUsed: "エラー:ポート番号はすでに使用されています。チェックして再試行してください!"
|
||||
UpdatePortErr: "エラー:パネルポートの更新に失敗しました{{.err}}"
|
||||
UpdatePortResult: "パネルポート:{{.name}}"
|
||||
UpdateUserResult: "パネルユーザー: {{.name}}"
|
||||
UpdatePasswordRead: "エラー: パネルパスワード情報の読み取りに失敗しました、{{.err}}"
|
||||
UpdatePasswordNull: "エラー: パネルパスワードは空です!"
|
||||
UpdateUPasswordBlank: "エラー: パスワードにはスペースが含まれています!"
|
||||
UpdatePasswordFormat: "エラー: パネルパスワードは、文字、数字、特殊文字のみをサポートします!@#$%*_、。、、長さ8-30!"
|
||||
UpdatePasswordLen: "エラー: 6文字より長くパスワードを入力してください!"
|
||||
UpdatePasswordRe: "パスワードを認証する: "
|
||||
UpdatePasswordErr: "エラー: {{.err}}のパスワードの更新に失敗しました"
|
||||
UpdatePasswordSame: "エラー: 2つのパスワードが一致しません。チェックして再試行してください!"
|
||||
UpdatePasswordResult: "パネルパスワード: {{.name}}"
|
||||
UpdatePortFormat: "エラー: 入力ポート番号は1〜65535でなければなりません!"
|
||||
UpdatePortUsed: "エラー: ポート番号はすでに使用されています。チェックして再試行してください!"
|
||||
UpdatePortErr: "エラー: パネルポートの更新に失敗しました{{.err}}"
|
||||
UpdatePortResult: "パネルポート: {{.name}}"
|
||||
UpdatePortFirewallAdd: "ファイアウォールポートルールの追加に失敗した{{.err}}、ファイアウォールルールに{{.name}}ポートを手動で追加してください。"
|
||||
UpdatePortFirewallDel: "エラー:ファイアウォールポートの削除に失敗しました、{{.err}}"
|
||||
UpdatePortFirewallDel: "エラー: ファイアウォールポートの削除に失敗しました、{{.err}}"
|
||||
UpdatePortFirewallReload: "ファイアウォールのリロードに失敗しました{{.err}}、ファイアウォールを手動でリロードしてください。"
|
||||
UserInfo: "パネル情報を取得します"
|
||||
UserInfoAddr: "パネルアドレス:"
|
||||
UserInfoPassHelp: "ヒント:パスワードを変更するには、コマンドを実行できます。"
|
||||
DBConnErr: "エラー:データベース接続の初期化に失敗しました、{{.err}}"
|
||||
SystemVersion: "バージョン:"
|
||||
SystemMode: "モード:"
|
||||
UserInfoAddr: "パネルアドレス: "
|
||||
UserInfoPassHelp: "ヒント: パスワードを変更するには、コマンドを実行できます。"
|
||||
DBConnErr: "エラー: データベース接続の初期化に失敗しました、{{.err}}"
|
||||
SystemVersion: "バージョン: "
|
||||
SystemMode: "モード: "
|
||||
|
||||
#ai-tool
|
||||
ErrOpenrestyInstall: 'まず Openresty をインストールしてください'
|
||||
ErrSSL: "証明書の内容が空です。証明書を確認してください!"
|
||||
|
||||
|
||||
#mobile app
|
||||
ErrVerifyToken: 'トークンの検証エラーです。リセット後、再度QRコードをスキャンしてください。'
|
||||
|
||||
@@ -287,6 +287,8 @@ SystemMode: "모드: "
|
||||
|
||||
#ai-tool
|
||||
ErrOpenrestyInstall: '먼저 Openresty를 설치하세요'
|
||||
ErrSSL: "인증서 내용이 비어 있습니다. 인증서를 확인하세요!"
|
||||
|
||||
|
||||
#mobile app
|
||||
ErrVerifyToken: '토큰 검증 오류가 발생했습니다. 재설정 후 다시 스캔해 주세요.'
|
||||
|
||||
@@ -286,6 +286,8 @@ SystemMode: "Mod: "
|
||||
|
||||
#ai-tool
|
||||
ErrOpenrestyInstall: 'Sila pasang Openresty terlebih dahulu'
|
||||
ErrSSL: "Kandungan sijil kosong, sila periksa sijil!"
|
||||
|
||||
|
||||
#mobile app
|
||||
ErrVerifyToken: 'Ralat pengesahan token, sila tetapkan semula dan imbas semula.'
|
||||
|
||||
@@ -284,6 +284,7 @@ SystemMode: "modo: "
|
||||
|
||||
#ai-tool
|
||||
ErrOpenrestyInstall: 'Por favor, instale o Openresty primeiro'
|
||||
ErrSSL: "O conteúdo do certificado está vazio, por favor, verifique o certificado!"
|
||||
|
||||
#mobile app
|
||||
ErrVerifyToken: 'Erro de verificação do token, por favor, reinicie e escaneie novamente.'
|
||||
|
||||
@@ -286,7 +286,8 @@ SystemVersion: "версия: "
|
||||
SystemMode: "режим: "
|
||||
|
||||
#ai-tool
|
||||
"ErrOpenrestyInstall": "Пожалуйста, установите Openresty сначала"
|
||||
ErrOpenrestyInstall: "Пожалуйста, установите Openresty сначала"
|
||||
ErrSSL: "Содержимое сертификата пустое, пожалуйста, проверьте сертификат!"
|
||||
|
||||
#mobile app
|
||||
ErrVerifyToken: 'шибка проверки токена, пожалуйста, сбросьте и отсканируйте снова.'
|
||||
|
||||
@@ -8,7 +8,7 @@ ErrStructTransform: "類型轉換失敗: {{ .detail }}"
|
||||
ErrNotLogin: "用戶未登入: {{ .detail }}"
|
||||
ErrPasswordExpired: "目前密碼已過期: {{ .detail }}"
|
||||
ErrNotSupportType: "系統暫不支援目前類型: {{ .detail }}"
|
||||
ErrApiConfigStatusInvalid: "API 介面禁止訪問: {{ .detail }}"
|
||||
ErrApiConfigStatusInvalid: "API 介面禁止瀏覽: {{ .detail }}"
|
||||
ErrApiConfigKeyInvalid: "API 介面金鑰錯誤: {{ .detail }}"
|
||||
ErrApiConfigIPInvalid: "呼叫 API 介面 IP 不在白名單: {{ .detail }}"
|
||||
ErrApiConfigDisable: "此介面禁止使用 API 介面呼叫: {{ .detail }}"
|
||||
@@ -243,7 +243,7 @@ ResetMFA: "取消 1Panel 兩步驗證"
|
||||
ResetHttps: "取消 1Panel https 方式登入"
|
||||
ResetEntrance: "取消 1Panel 安全入口"
|
||||
ResetIPs: "取消 1Panel 授權 IP 限制"
|
||||
ResetDomain: "取消 1Panel 訪問域名綁定"
|
||||
ResetDomain: "取消 1Panel 瀏覽域名綁定"
|
||||
RestoreCommands: "轉返 1Panel 服務及資料"
|
||||
RestoreNoSuchFile: "暫無可轉返文件"
|
||||
RestoreStep1: "(1/5)開始從 {{ .name }} 目錄轉返 1Panel 服務及資料... "
|
||||
@@ -256,37 +256,38 @@ UpdateCommands: "修改面板資訊"
|
||||
UpdateUser: "修改面板用戶"
|
||||
UpdatePassword: "修改面板密碼"
|
||||
UpdatePort: "修改面板埠"
|
||||
UpdateUserNull: "錯誤:輸入面板用戶為空!"
|
||||
UpdateUserBlank: "錯誤:輸入面板用戶中包含空格字元!"
|
||||
UpdateUserFormat: "錯誤:輸入面板用戶錯誤!僅支援英文、中文、數字和_,長度3-30"
|
||||
UpdateUserErr: "錯誤:面板用戶修改失敗,{{ .err }}"
|
||||
UpdateUserNull: "錯誤: 輸入面板用戶為空!"
|
||||
UpdateUserBlank: "錯誤: 輸入面板用戶中包含空格字元!"
|
||||
UpdateUserFormat: "錯誤: 輸入面板用戶錯誤!僅支援英文、中文、數字和_,長度3-30"
|
||||
UpdateUserErr: "錯誤: 面板用戶修改失敗,{{ .err }}"
|
||||
UpdateSuccessful: "修改成功!"
|
||||
UpdateUserResult: "面板用戶:{{ .name }}"
|
||||
UpdatePasswordRead: "錯誤:面板密碼資訊讀取錯誤,{{ .err }}"
|
||||
UpdatePasswordNull: "錯誤:輸入面板密碼為空!"
|
||||
UpdateUPasswordBlank: "錯誤:輸入面板密碼中包含空格字元!"
|
||||
UpdatePasswordFormat: "錯誤:面板密碼僅支援字母、數字、特殊字元(!@#$%*_,.?),長度 8-30 位!"
|
||||
UpdatePasswordLen: "錯誤:請輸入 6 位以上密碼!"
|
||||
UpdatePasswordRe: "確認密碼:"
|
||||
UpdatePasswordErr: "錯誤:面板密碼修改失敗,{{ .err }}"
|
||||
UpdatePasswordSame: "錯誤:兩次密碼不匹配,請檢查後重試!"
|
||||
UpdatePasswordResult: "面板密碼:{{ .name }}"
|
||||
UpdatePortFormat: "錯誤:輸入的埠號必須在 1 到 65535 之間!"
|
||||
UpdatePortUsed: "錯誤:該埠號正被佔用,請檢查後重試!"
|
||||
UpdatePortErr: "錯誤:面板埠修改失敗,{{ .err }}"
|
||||
UpdatePortResult: "面板埠:{{ .name }}"
|
||||
UpdateUserResult: "面板用戶: {{ .name }}"
|
||||
UpdatePasswordRead: "錯誤: 面板密碼資訊讀取錯誤,{{ .err }}"
|
||||
UpdatePasswordNull: "錯誤: 輸入面板密碼為空!"
|
||||
UpdateUPasswordBlank: "錯誤: 輸入面板密碼中包含空格字元!"
|
||||
UpdatePasswordFormat: "錯誤: 面板密碼僅支援字母、數字、特殊字元(!@#$%*_,.?),長度 8-30 位!"
|
||||
UpdatePasswordLen: "錯誤: 請輸入 6 位以上密碼!"
|
||||
UpdatePasswordRe: "確認密碼: "
|
||||
UpdatePasswordErr: "錯誤: 面板密碼修改失敗,{{ .err }}"
|
||||
UpdatePasswordSame: "錯誤: 兩次密碼不匹配,請檢查後重試!"
|
||||
UpdatePasswordResult: "面板密碼: {{ .name }}"
|
||||
UpdatePortFormat: "錯誤: 輸入的埠號必須在 1 到 65535 之間!"
|
||||
UpdatePortUsed: "錯誤: 該埠號正被佔用,請檢查後重試!"
|
||||
UpdatePortErr: "錯誤: 面板埠修改失敗,{{ .err }}"
|
||||
UpdatePortResult: "面板埠: {{ .name }}"
|
||||
UpdatePortFirewallAdd: "新增防火牆埠規則失敗,{{ .err }},請您手動將 {{ .name }} 埠新增至防火牆規則中。"
|
||||
UpdatePortFirewallDel: "錯誤:防火牆埠刪除失敗,{{ .err }}"
|
||||
UpdatePortFirewallDel: "錯誤: 防火牆埠刪除失敗,{{ .err }}"
|
||||
UpdatePortFirewallReload: "防火牆重載失敗,{{ .err }},請您手動重載防火牆。"
|
||||
UserInfo: "取得面板資訊"
|
||||
UserInfoAddr: "面板地址:"
|
||||
UserInfoPassHelp: "提示:修改密碼可執行指令:"
|
||||
DBConnErr: "錯誤:初始化資料庫連線失敗,{{ .err }}"
|
||||
SystemVersion: "版本:"
|
||||
SystemMode: "模式:"
|
||||
UserInfoAddr: "面板地址: "
|
||||
UserInfoPassHelp: "提示: 修改密碼可執行指令: "
|
||||
DBConnErr: "錯誤: 初始化資料庫連線失敗,{{ .err }}"
|
||||
SystemVersion: "版本: "
|
||||
SystemMode: "模式: "
|
||||
|
||||
#ai-tool
|
||||
"ErrOpenrestyInstall": "請先安裝 Openresty"
|
||||
ErrOpenrestyInstall: "請先安裝 Openresty"
|
||||
ErrSSL: "證書內容為空,請檢查證書!"
|
||||
|
||||
#mobile app
|
||||
ErrVerifyToken: '令牌驗證錯誤,請重置後再次掃碼'
|
||||
|
||||
+25
-24
@@ -256,37 +256,38 @@ UpdateCommands: "修改面板信息"
|
||||
UpdateUser: "修改面板用户"
|
||||
UpdatePassword: "修改面板密码"
|
||||
UpdatePort: "修改面板端口"
|
||||
UpdateUserNull: "错误:输入面板用户为空!"
|
||||
UpdateUserBlank: "错误:输入面板用户中包含空格字符!"
|
||||
UpdateUserFormat: "错误:输入面板用户错误!仅支持英文、中文、数字和_,长度3-30"
|
||||
UpdateUserErr: "错误:面板用户修改失败,{{ .err }}"
|
||||
UpdateUserNull: "错误: 输入面板用户为空!"
|
||||
UpdateUserBlank: "错误: 输入面板用户中包含空格字符!"
|
||||
UpdateUserFormat: "错误: 输入面板用户错误!仅支持英文、中文、数字和_,长度3-30"
|
||||
UpdateUserErr: "错误: 面板用户修改失败,{{ .err }}"
|
||||
UpdateSuccessful: "修改成功!"
|
||||
UpdateUserResult: "面板用户:{{ .name }}"
|
||||
UpdatePasswordRead: "错误:面板密码信息读取错误,{{ .err }}"
|
||||
UpdatePasswordNull: "错误:输入面板密码为空!"
|
||||
UpdateUPasswordBlank: "错误:输入面板密码中包含空格字符!"
|
||||
UpdatePasswordFormat: "错误:面板密码仅支持字母、数字、特殊字符(!@#$%*_,.?),长度 8-30 位!"
|
||||
UpdatePasswordLen: "错误:请输入 6 位以上密码!"
|
||||
UpdatePasswordRe: "确认密码:"
|
||||
UpdatePasswordErr: "错误:面板密码修改失败,{{ .err }}"
|
||||
UpdatePasswordSame: "错误:两次密码不匹配,请检查后重试!"
|
||||
UpdatePasswordResult: "面板密码:{{ .name }}"
|
||||
UpdatePortFormat: "错误:输入的端口号必须在 1 到 65535 之间!"
|
||||
UpdatePortUsed: "错误:该端口号正被占用,请检查后重试!"
|
||||
UpdatePortErr: "错误:面板端口修改失败,{{ .err }}"
|
||||
UpdatePortResult: "面板端口:{{ .name }}"
|
||||
UpdateUserResult: "面板用户: {{ .name }}"
|
||||
UpdatePasswordRead: "错误: 面板密码信息读取错误,{{ .err }}"
|
||||
UpdatePasswordNull: "错误: 输入面板密码为空!"
|
||||
UpdateUPasswordBlank: "错误: 输入面板密码中包含空格字符!"
|
||||
UpdatePasswordFormat: "错误: 面板密码仅支持字母、数字、特殊字符(!@#$%*_,.?),长度 8-30 位!"
|
||||
UpdatePasswordLen: "错误: 请输入 6 位以上密码!"
|
||||
UpdatePasswordRe: "确认密码: "
|
||||
UpdatePasswordErr: "错误: 面板密码修改失败,{{ .err }}"
|
||||
UpdatePasswordSame: "错误: 两次密码不匹配,请检查后重试!"
|
||||
UpdatePasswordResult: "面板密码: {{ .name }}"
|
||||
UpdatePortFormat: "错误: 输入的端口号必须在 1 到 65535 之间!"
|
||||
UpdatePortUsed: "错误: 该端口号正被占用,请检查后重试!"
|
||||
UpdatePortErr: "错误: 面板端口修改失败,{{ .err }}"
|
||||
UpdatePortResult: "面板端口: {{ .name }}"
|
||||
UpdatePortFirewallAdd: "添加防火墙端口规则失败,{{ .err }},请您手动将 {{ .name }} 端口添加至防火墙规则中。"
|
||||
UpdatePortFirewallDel: "错误:防火墙端口删除失败,{{ .err }}"
|
||||
UpdatePortFirewallDel: "错误: 防火墙端口删除失败,{{ .err }}"
|
||||
UpdatePortFirewallReload: "防火墙重载失败,{{ .err }},请您手动重载防火墙。"
|
||||
UserInfo: "获取面板信息"
|
||||
UserInfoAddr: "面板地址:"
|
||||
UserInfoPassHelp: "提示:修改密码可执行命令:"
|
||||
DBConnErr: "错误:初始化数据库连接失败,{{ .err }}"
|
||||
SystemVersion: "版本:"
|
||||
SystemMode: "模式:"
|
||||
UserInfoAddr: "面板地址: "
|
||||
UserInfoPassHelp: "提示: 修改密码可执行命令: "
|
||||
DBConnErr: "错误: 初始化数据库连接失败,{{ .err }}"
|
||||
SystemVersion: "版本: "
|
||||
SystemMode: "模式: "
|
||||
|
||||
#ai-tool
|
||||
ErrOpenrestyInstall: '请先安装 Openresty'
|
||||
ErrSSL: "证书内容为空,请检查证书!"
|
||||
|
||||
#mobile app
|
||||
ErrVerifyToken: '令牌验证错误,请重置后再次扫码'
|
||||
|
||||
@@ -84,6 +84,15 @@ func Init() {
|
||||
global.CONF.System.ApiKeyValidityTime = apiKeyValidityTimeSetting.Value
|
||||
}
|
||||
|
||||
if global.CONF.System.LicenseVerify == "" {
|
||||
licenseVerify, err := settingRepo.Get(settingRepo.WithByKey("LicenseVerify"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load service license verify from setting failed, err: %v", err)
|
||||
}
|
||||
global.CONF.System.LicenseVerify = licenseVerify.Value
|
||||
}
|
||||
handleLicenseVerify(global.CONF.System.LicenseVerify, settingRepo)
|
||||
|
||||
handleUserInfo(global.CONF.System.ChangeUserInfo, settingRepo)
|
||||
|
||||
handleCronjobStatus()
|
||||
@@ -207,6 +216,12 @@ func loadLocalDir() {
|
||||
global.LOG.Errorf("error type dir: %T", varMap["dir"])
|
||||
}
|
||||
|
||||
func handleLicenseVerify(licenseVerify string, settingRepo repo.ISettingRepo) {
|
||||
if err := settingRepo.Update("LicenseVerify", licenseVerify); err != nil {
|
||||
global.LOG.Fatalf("init license verify before start failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func handleUserInfo(tags string, settingRepo repo.ISettingRepo) {
|
||||
if len(tags) == 0 {
|
||||
return
|
||||
|
||||
@@ -105,6 +105,7 @@ func Init() {
|
||||
migrations.AddOllamaModel,
|
||||
migrations.AddAppMenu,
|
||||
migrations.AddAppPanelName,
|
||||
migrations.AddLicenseVerify,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
||||
@@ -448,3 +448,14 @@ var AddAppPanelName = &gormigrate.Migration{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddLicenseVerify = &gormigrate.Migration{
|
||||
ID: "20250226-add-license-verify",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
|
||||
if err := tx.Create(&model.Setting{Key: "LicenseVerify", Value: "LX"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -109,6 +109,15 @@ func handleNoRoute(c *gin.Context) {
|
||||
c.Data(statusCode, "text/html; charset=utf-8", data)
|
||||
}
|
||||
|
||||
func checkSession(c *gin.Context) bool {
|
||||
sId, err := c.Cookie(constant.SessionName)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_, err = global.SESSION.Get(sId)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func setWebStatic(rootRouter *gin.RouterGroup) {
|
||||
rootRouter.StaticFS("/public", http.FS(web.Favicon))
|
||||
rootRouter.StaticFS("/favicon.ico", http.FS(web.Favicon))
|
||||
@@ -136,10 +145,15 @@ func setWebStatic(rootRouter *gin.RouterGroup) {
|
||||
})
|
||||
}
|
||||
rootRouter.GET("/", func(c *gin.Context) {
|
||||
if !checkEntrance(c) {
|
||||
if !checkEntrance(c) && !checkSession(c) {
|
||||
handleNoRoute(c)
|
||||
return
|
||||
}
|
||||
entrance = authService.GetSecurityEntrance()
|
||||
if entrance != "" {
|
||||
entranceValue := base64.StdEncoding.EncodeToString([]byte(entrance))
|
||||
c.SetCookie("SecurityEntrance", entranceValue, 0, "", "", false, true)
|
||||
}
|
||||
staticServer := http.FileServer(http.FS(web.IndexHtml))
|
||||
staticServer.ServeHTTP(c.Writer, c.Request)
|
||||
})
|
||||
@@ -174,7 +188,12 @@ func Routers() *gin.Engine {
|
||||
|
||||
swaggerRouter := Router.Group("1panel")
|
||||
docs.SwaggerInfo.BasePath = "/api/v1"
|
||||
swaggerRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
|
||||
swaggerRouter.GET("/swagger/*any", func(c *gin.Context) {
|
||||
if !checkSession(c) {
|
||||
handleNoRoute(c)
|
||||
return
|
||||
}
|
||||
}, ginSwagger.WrapHandler(swaggerfiles.Handler))
|
||||
PublicGroup := Router.Group("")
|
||||
{
|
||||
PublicGroup.GET("/health", func(c *gin.Context) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package viper
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
@@ -103,6 +104,7 @@ func Init() {
|
||||
global.CONF.System.Entrance = entrance
|
||||
global.CONF.System.Language = language
|
||||
global.CONF.System.ChangeUserInfo = loadChangeInfo()
|
||||
global.CONF.System.LicenseVerify = os.Getenv("LXWARE_LICENSE_VERIFY")
|
||||
global.Viper = v
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -102,27 +102,38 @@ func isValid1PanelToken(panelToken string, panelTimestamp string) bool {
|
||||
|
||||
func isIPInWhiteList(clientIP string) bool {
|
||||
ipWhiteString := global.CONF.System.IpWhiteList
|
||||
ipWhiteList := strings.Split(ipWhiteString, "\n")
|
||||
if len(ipWhiteString) == 0 {
|
||||
global.LOG.Error("IP whitelist is empty")
|
||||
return false
|
||||
}
|
||||
ipWhiteList, ipErr := common.HandleIPList(ipWhiteString)
|
||||
if ipErr != nil {
|
||||
global.LOG.Errorf("Failed to handle IP list: %v", ipErr)
|
||||
return false
|
||||
}
|
||||
clientParsedIP := net.ParseIP(clientIP)
|
||||
if clientParsedIP == nil {
|
||||
return false
|
||||
}
|
||||
iPv4 := clientParsedIP.To4()
|
||||
iPv6 := clientParsedIP.To16()
|
||||
for _, cidr := range ipWhiteList {
|
||||
if cidr == "0.0.0.0" {
|
||||
if (iPv4 != nil && (cidr == "0.0.0.0" || cidr == "0.0.0.0/0" || iPv4.String() == cidr)) || (iPv6 != nil && (cidr == "::/0" || iPv6.String() == cidr)) {
|
||||
return true
|
||||
}
|
||||
_, ipNet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
if cidr == clientIP {
|
||||
return true
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ipNet.Contains(net.ParseIP(clientIP)) {
|
||||
if (iPv4 != nil && ipNet.Contains(iPv4)) || (iPv6 != nil && ipNet.Contains(iPv6)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GenerateMD5(input string) string {
|
||||
func GenerateMD5(param string) string {
|
||||
hash := md5.New()
|
||||
hash.Write([]byte(input))
|
||||
hash.Write([]byte(param))
|
||||
return hex.EncodeToString(hash.Sum(nil))
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
aiToolsRouter.GET("/ollama/exec", baseApi.OllamaWsSsh)
|
||||
aiToolsRouter.POST("/ollama/close", baseApi.CloseOllamaModel)
|
||||
aiToolsRouter.POST("/ollama/model", baseApi.CreateOllamaModel)
|
||||
aiToolsRouter.POST("/ollama/model/recreate", baseApi.RecreateOllamaModel)
|
||||
|
||||
@@ -15,7 +15,7 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
Use(middleware.PasswordExpired())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
baRouter.GET("/exec", baseApi.ContainerWsSsh)
|
||||
baRouter.GET("/exec", baseApi.ContainerWsSSH)
|
||||
baRouter.GET("/stats/:id", baseApi.ContainerStats)
|
||||
|
||||
baRouter.POST("", baseApi.ContainerCreate)
|
||||
|
||||
@@ -38,7 +38,6 @@ func (s *DatabaseRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
cmdRouter.POST("/redis/persistence/conf", baseApi.LoadPersistenceConf)
|
||||
cmdRouter.POST("/redis/status", baseApi.LoadRedisStatus)
|
||||
cmdRouter.POST("/redis/conf", baseApi.LoadRedisConf)
|
||||
cmdRouter.GET("/redis/exec", baseApi.RedisWsSsh)
|
||||
cmdRouter.GET("/redis/check", baseApi.CheckHasCli)
|
||||
cmdRouter.POST("/redis/install/cli", baseApi.InstallCli)
|
||||
cmdRouter.POST("/redis/password", baseApi.ChangeRedisPassword)
|
||||
|
||||
@@ -47,9 +47,9 @@ func Start() {
|
||||
session.Init()
|
||||
gin.SetMode(gin.DebugMode)
|
||||
cron.Run()
|
||||
hook.Init()
|
||||
InitOthers()
|
||||
business.Init()
|
||||
hook.Init()
|
||||
|
||||
rootRouter := router.Routers()
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/gpu/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/gpu/schema_v12"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/gpu/schema"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
)
|
||||
|
||||
@@ -28,7 +28,7 @@ func (n NvidiaSMI) LoadGpuInfo() (*common.GpuInfo, error) {
|
||||
return nil, fmt.Errorf("calling nvidia-smi failed, err: %v", std)
|
||||
}
|
||||
data := []byte(std)
|
||||
schema := "v11"
|
||||
version := "v11"
|
||||
|
||||
buf := bytes.NewBuffer(data)
|
||||
decoder := xml.NewDecoder(buf)
|
||||
@@ -51,15 +51,18 @@ func (n NvidiaSMI) LoadGpuInfo() (*common.GpuInfo, error) {
|
||||
parts := strings.Split(directive, " ")
|
||||
s := strings.Trim(parts[len(parts)-1], "\" ")
|
||||
if strings.HasPrefix(s, "nvsmi_device_") && strings.HasSuffix(s, ".dtd") {
|
||||
schema = strings.TrimSuffix(strings.TrimPrefix(s, "nvsmi_device_"), ".dtd")
|
||||
version = strings.TrimSuffix(strings.TrimPrefix(s, "nvsmi_device_"), ".dtd")
|
||||
} else {
|
||||
global.LOG.Debugf("Cannot find schema version in %q", directive)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if schema != "v12" {
|
||||
return &common.GpuInfo{}, nil
|
||||
if version == "v12" || version == "v11" {
|
||||
return schema.Parse(data, version)
|
||||
} else {
|
||||
global.LOG.Errorf("don't support such schema version %s", version)
|
||||
}
|
||||
return schema_v12.Parse(data)
|
||||
|
||||
return &common.GpuInfo{}, nil
|
||||
}
|
||||
|
||||
+9
-4
@@ -1,4 +1,4 @@
|
||||
package schema_v12
|
||||
package schema
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/gpu/common"
|
||||
)
|
||||
|
||||
func Parse(buf []byte) (*common.GpuInfo, error) {
|
||||
func Parse(buf []byte, version string) (*common.GpuInfo, error) {
|
||||
var (
|
||||
s smi
|
||||
info common.GpuInfo
|
||||
@@ -36,8 +36,13 @@ func Parse(buf []byte) (*common.GpuInfo, error) {
|
||||
|
||||
gpuItem.Temperature = s.Gpu[i].Temperature.GpuTemp
|
||||
gpuItem.PerformanceState = s.Gpu[i].PerformanceState
|
||||
gpuItem.PowerDraw = s.Gpu[i].GpuPowerReadings.PowerDraw
|
||||
gpuItem.MaxPowerLimit = s.Gpu[i].GpuPowerReadings.MaxPowerLimit
|
||||
if version == "v12" {
|
||||
gpuItem.PowerDraw = s.Gpu[i].GpuPowerReadings.PowerDraw
|
||||
gpuItem.MaxPowerLimit = s.Gpu[i].GpuPowerReadings.MaxPowerLimit
|
||||
} else {
|
||||
gpuItem.PowerDraw = s.Gpu[i].PowerReadings.PowerDraw
|
||||
gpuItem.MaxPowerLimit = s.Gpu[i].PowerReadings.MaxPowerLimit
|
||||
}
|
||||
gpuItem.MemUsed = s.Gpu[i].FbMemoryUsage.Used
|
||||
gpuItem.MemTotal = s.Gpu[i].FbMemoryUsage.Total
|
||||
gpuItem.GPUUtil = s.Gpu[i].Utilization.GpuUtil
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package schema_v12
|
||||
package schema
|
||||
|
||||
type smi struct {
|
||||
AttachedGpus string `xml:"attached_gpus"`
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
cosSDK "github.com/tencentyun/cos-go-sdk-v5"
|
||||
)
|
||||
@@ -18,6 +19,7 @@ type cosClient struct {
|
||||
|
||||
func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||
region := loadParamFromVars("region", vars)
|
||||
endpoint := loadParamFromVars("endpoint", vars)
|
||||
accessKey := loadParamFromVars("accessKey", vars)
|
||||
secretKey := loadParamFromVars("secretKey", vars)
|
||||
bucket := loadParamFromVars("bucket", vars)
|
||||
@@ -26,7 +28,16 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||
scType = "Standard"
|
||||
}
|
||||
|
||||
u, _ := url.Parse(fmt.Sprintf("https://cos.%s.myqcloud.com", region))
|
||||
// 允许使用对象存储双栈域名
|
||||
endpointType := "cos"
|
||||
if len(endpoint) != 0 {
|
||||
re := regexp.MustCompile(`.*cos-dualstack\..*`)
|
||||
if re.MatchString(endpoint) {
|
||||
endpointType = "cos-dualstack"
|
||||
}
|
||||
}
|
||||
|
||||
u, _ := url.Parse(fmt.Sprintf("https://%s.%s.myqcloud.com", endpointType, region))
|
||||
b := &cosSDK.BaseURL{BucketURL: u}
|
||||
client := cosSDK.NewClient(b, &http.Client{
|
||||
Transport: &cosSDK.AuthorizationTransport{
|
||||
@@ -36,7 +47,7 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||
})
|
||||
|
||||
if len(bucket) != 0 {
|
||||
u2, _ := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", bucket, region))
|
||||
u2, _ := url.Parse(fmt.Sprintf("https://%s.%s.%s.myqcloud.com", bucket, endpointType, region))
|
||||
b2 := &cosSDK.BaseURL{BucketURL: u2}
|
||||
clientWithBucket := cosSDK.NewClient(b2, &http.Client{
|
||||
Transport: &cosSDK.AuthorizationTransport{
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
@@ -82,58 +79,6 @@ func (c *ConnInfo) Close() {
|
||||
_ = c.Client.Close()
|
||||
}
|
||||
|
||||
type SshConn struct {
|
||||
StdinPipe io.WriteCloser
|
||||
ComboOutput *wsBufferWriter
|
||||
Session *gossh.Session
|
||||
}
|
||||
|
||||
func (c *ConnInfo) NewSshConn(cols, rows int) (*SshConn, error) {
|
||||
sshSession, err := c.Client.NewSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stdinP, err := sshSession.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
comboWriter := new(wsBufferWriter)
|
||||
sshSession.Stdout = comboWriter
|
||||
sshSession.Stderr = comboWriter
|
||||
|
||||
modes := gossh.TerminalModes{
|
||||
gossh.ECHO: 1,
|
||||
gossh.TTY_OP_ISPEED: 14400,
|
||||
gossh.TTY_OP_OSPEED: 14400,
|
||||
}
|
||||
if err := sshSession.RequestPty("xterm", rows, cols, modes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := sshSession.Shell(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SshConn{StdinPipe: stdinP, ComboOutput: comboWriter, Session: sshSession}, nil
|
||||
}
|
||||
|
||||
func (s *SshConn) Close() {
|
||||
if s.Session != nil {
|
||||
s.Session.Close()
|
||||
}
|
||||
}
|
||||
|
||||
type wsBufferWriter struct {
|
||||
buffer bytes.Buffer
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (w *wsBufferWriter) Write(p []byte) (int, error) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return w.buffer.Write(p)
|
||||
}
|
||||
|
||||
func makePrivateKeySigner(privateKey []byte, passPhrase []byte) (gossh.Signer, error) {
|
||||
if len(passPhrase) != 0 {
|
||||
return gossh.ParsePrivateKeyWithPassphrase(privateKey, passPhrase)
|
||||
|
||||
@@ -3,11 +3,13 @@ package ssl
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/json"
|
||||
"github.com/go-acme/lego/v4/providers/dns/rainyun"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/freemyip"
|
||||
"github.com/go-acme/lego/v4/providers/dns/rainyun"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/go-acme/lego/v4/acme"
|
||||
"github.com/go-acme/lego/v4/acme/api"
|
||||
@@ -73,6 +75,7 @@ const (
|
||||
Volcengine DnsType = "Volcengine"
|
||||
CloudFlare DnsType = "CloudFlare"
|
||||
CloudDns DnsType = "CloudDns"
|
||||
FreeMyIP DnsType = "FreeMyIP"
|
||||
NameSilo DnsType = "NameSilo"
|
||||
NameCheap DnsType = "NameCheap"
|
||||
NameCom DnsType = "NameCom"
|
||||
@@ -155,6 +158,12 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string, websiteSSL model.Web
|
||||
clouddnsConfig.PollingInterval = pollingInterval
|
||||
clouddnsConfig.TTL = ttl
|
||||
p, err = clouddns.NewDNSProviderConfig(clouddnsConfig)
|
||||
case FreeMyIP:
|
||||
freeMyIpConfig := freemyip.NewDefaultConfig()
|
||||
freeMyIpConfig.Token = param.Token
|
||||
freeMyIpConfig.PropagationTimeout = propagationTimeout
|
||||
freeMyIpConfig.PollingInterval = pollingInterval
|
||||
p, err = freemyip.NewDNSProviderConfig(freeMyIpConfig)
|
||||
case NameCheap:
|
||||
namecheapConfig := namecheap.NewDefaultConfig()
|
||||
namecheapConfig.APIKey = param.APIkey
|
||||
|
||||
@@ -25,14 +25,24 @@ type LocalCommand struct {
|
||||
pty *os.File
|
||||
}
|
||||
|
||||
func NewCommand(commands []string) (*LocalCommand, error) {
|
||||
cmd := exec.Command("docker", commands...)
|
||||
func NewCommand(initCmd string) (*LocalCommand, error) {
|
||||
cmd := exec.Command("bash")
|
||||
if term := os.Getenv("TERM"); term != "" {
|
||||
cmd.Env = append(os.Environ(), "TERM="+term)
|
||||
} else {
|
||||
cmd.Env = append(os.Environ(), "TERM=xterm")
|
||||
}
|
||||
|
||||
pty, err := pty.Start(cmd)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to start command")
|
||||
}
|
||||
|
||||
if len(initCmd) != 0 {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
_, _ = pty.Write([]byte(initCmd + "\n"))
|
||||
}
|
||||
|
||||
lcmd := &LocalCommand{
|
||||
closeSignal: DefaultCloseSignal,
|
||||
closeTimeout: DefaultCloseTimeout,
|
||||
@@ -90,4 +100,5 @@ func (lcmd *LocalCommand) Wait(quitChan chan bool) {
|
||||
global.LOG.Errorf("ssh session wait failed, err: %v", err)
|
||||
setQuit(quitChan)
|
||||
}
|
||||
setQuit(quitChan)
|
||||
}
|
||||
|
||||
@@ -53,6 +53,11 @@ func (sws *LocalWsSession) handleSlaveEvent(exitCh chan bool) {
|
||||
}
|
||||
|
||||
func (sws *LocalWsSession) masterWrite(data []byte) error {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
global.LOG.Errorf("A panic occurred during write ws message to master, error message: %v", r)
|
||||
}
|
||||
}()
|
||||
sws.writeMutex.Lock()
|
||||
defer sws.writeMutex.Unlock()
|
||||
wsData, err := json.Marshal(WsMsg{
|
||||
@@ -72,6 +77,7 @@ func (sws *LocalWsSession) masterWrite(data []byte) error {
|
||||
func (sws *LocalWsSession) receiveWsMsg(exitCh chan bool) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
setQuit(exitCh)
|
||||
global.LOG.Errorf("A panic occurred during receive ws message, error message: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -3483,7 +3483,7 @@ const docTemplate = `{
|
||||
"summary": "Container stats",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"description": "container id",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
@@ -19382,6 +19382,9 @@ const docTemplate = `{
|
||||
"dto.ForwardRuleOperate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"forceDelete": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"rules": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -20708,6 +20711,9 @@ const docTemplate = `{
|
||||
"dto.OllamaBindDomainRes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"acmeAccountID": {
|
||||
"type": "integer"
|
||||
},
|
||||
"allowIPs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -26721,7 +26727,7 @@ const docTemplate = `{
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"ApiKeyAuth": {
|
||||
"description": "- ` + "`" + `1Panel-Token` + "`" + ` is the key for the panel API Key.",
|
||||
"description": "Custom Token Format, Format: md5('1panel' + API-Key + UnixTimestamp).\n` + "`" + `` + "`" + `` + "`" + `\neg:\ncurl -X GET \"http://localhost:4004/api/v1/dashboard/current\" \\\n-H \"1Panel-Token: \u003c1panel_token\u003e\" \\\n-H \"1Panel-Timestamp: \u003ccurrent_unix_timestamp\u003e\"\n` + "`" + `` + "`" + `` + "`" + `\n- ` + "`" + `1Panel-Token` + "`" + ` is the key for the panel API Key.",
|
||||
"type": "apiKey",
|
||||
"name": "1Panel-Token",
|
||||
"in": "header"
|
||||
|
||||
@@ -3480,7 +3480,7 @@
|
||||
"summary": "Container stats",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"type": "string",
|
||||
"description": "container id",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
@@ -19379,6 +19379,9 @@
|
||||
"dto.ForwardRuleOperate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"forceDelete": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"rules": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -20705,6 +20708,9 @@
|
||||
"dto.OllamaBindDomainRes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"acmeAccountID": {
|
||||
"type": "integer"
|
||||
},
|
||||
"allowIPs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -26718,7 +26724,7 @@
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"ApiKeyAuth": {
|
||||
"description": "- `1Panel-Token` is the key for the panel API Key.",
|
||||
"description": "Custom Token Format, Format: md5('1panel' + API-Key + UnixTimestamp).\n```\neg:\ncurl -X GET \"http://localhost:4004/api/v1/dashboard/current\" \\\n-H \"1Panel-Token: \u003c1panel_token\u003e\" \\\n-H \"1Panel-Timestamp: \u003ccurrent_unix_timestamp\u003e\"\n```\n- `1Panel-Token` is the key for the panel API Key.",
|
||||
"type": "apiKey",
|
||||
"name": "1Panel-Token",
|
||||
"in": "header"
|
||||
|
||||
@@ -1578,6 +1578,8 @@ definitions:
|
||||
type: object
|
||||
dto.ForwardRuleOperate:
|
||||
properties:
|
||||
forceDelete:
|
||||
type: boolean
|
||||
rules:
|
||||
items:
|
||||
properties:
|
||||
@@ -2480,6 +2482,8 @@ definitions:
|
||||
type: object
|
||||
dto.OllamaBindDomainRes:
|
||||
properties:
|
||||
acmeAccountID:
|
||||
type: integer
|
||||
allowIPs:
|
||||
items:
|
||||
type: string
|
||||
@@ -8585,7 +8589,7 @@ paths:
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
@@ -16762,7 +16766,15 @@ schemes:
|
||||
- https
|
||||
securityDefinitions:
|
||||
ApiKeyAuth:
|
||||
description: '- `1Panel-Token` is the key for the panel API Key.'
|
||||
description: |-
|
||||
Custom Token Format, Format: md5('1panel' + API-Key + UnixTimestamp).
|
||||
```
|
||||
eg:
|
||||
curl -X GET "http://localhost:4004/api/v1/dashboard/current" \
|
||||
-H "1Panel-Token: <1panel_token>" \
|
||||
-H "1Panel-Timestamp: <current_unix_timestamp>"
|
||||
```
|
||||
- `1Panel-Token` is the key for the panel API Key.
|
||||
in: header
|
||||
name: 1Panel-Token
|
||||
type: apiKey
|
||||
|
||||
@@ -104,6 +104,7 @@ export namespace AI {
|
||||
export interface BindDomainRes {
|
||||
domain: string;
|
||||
sslID: number;
|
||||
acmeAccountID: number;
|
||||
allowIPs: string[];
|
||||
websiteID?: number;
|
||||
connUrl: string;
|
||||
|
||||
@@ -62,6 +62,7 @@ export namespace Setting {
|
||||
apiKey: string;
|
||||
ipWhiteList: string;
|
||||
apiKeyValidityTime: number;
|
||||
licenseVerify: string;
|
||||
}
|
||||
export interface SettingUpdate {
|
||||
key: string;
|
||||
|
||||
@@ -98,7 +98,7 @@ export const operateFire = (operation: string) => {
|
||||
export const operatePortRule = (params: Host.RulePort) => {
|
||||
return http.post<Host.RulePort>(`/hosts/firewall/port`, params, TimeoutEnum.T_40S);
|
||||
};
|
||||
export const operateForwardRule = (params: { rules: Host.RuleForward[] }) => {
|
||||
export const operateForwardRule = (params: { rules: Host.RuleForward[]; forceDelete: boolean }) => {
|
||||
return http.post<Host.RulePort>(`/hosts/firewall/forward`, params, TimeoutEnum.T_40S);
|
||||
};
|
||||
export const operateIPRule = (params: Host.RuleIP) => {
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<script lang="ts" setup>
|
||||
import i18n from '@/lang';
|
||||
import { dateFormatForName } from '@/utils/util';
|
||||
import { computed, onBeforeUnmount, reactive, ref, shallowRef, watch } from 'vue';
|
||||
import { computed, nextTick, onBeforeUnmount, reactive, ref, shallowRef, watch } from 'vue';
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
@@ -145,10 +145,12 @@ const searchLogs = async () => {
|
||||
);
|
||||
terminalSocket.value.onmessage = (event) => {
|
||||
logInfo.value += event.data;
|
||||
const state = view.value.state;
|
||||
view.value.dispatch({
|
||||
selection: { anchor: state.doc.length, head: state.doc.length },
|
||||
scrollIntoView: true,
|
||||
nextTick(() => {
|
||||
const state = view.value.state;
|
||||
view.value.dispatch({
|
||||
selection: { anchor: state.doc.length, head: state.doc.length },
|
||||
scrollIntoView: true,
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
import { cleanContainerLog } from '@/api/modules/container';
|
||||
import i18n from '@/lang';
|
||||
import { dateFormatForName, downloadWithContent } from '@/utils/util';
|
||||
import { onBeforeUnmount, reactive, ref, shallowRef } from 'vue';
|
||||
import { nextTick, onBeforeUnmount, reactive, ref, shallowRef } from 'vue';
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
@@ -113,10 +113,12 @@ const searchLogs = async () => {
|
||||
);
|
||||
terminalSocket.value.onmessage = (event) => {
|
||||
logInfo.value += event.data;
|
||||
const state = view.value.state;
|
||||
view.value.dispatch({
|
||||
selection: { anchor: state.doc.length, head: state.doc.length },
|
||||
scrollIntoView: true,
|
||||
nextTick(() => {
|
||||
const state = view.value.state;
|
||||
view.value.dispatch({
|
||||
selection: { anchor: state.doc.length, head: state.doc.length },
|
||||
scrollIntoView: true,
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@@ -85,7 +85,9 @@ function onClose(isKeepShow: boolean = false) {
|
||||
term.value.dispose();
|
||||
} catch {}
|
||||
}
|
||||
terminalElement.value.innerHTML = '';
|
||||
if (terminalElement.value) {
|
||||
terminalElement.value.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
// terminal 相关代码 start
|
||||
@@ -202,10 +204,10 @@ const errorRealTerminal = (ex: any) => {
|
||||
|
||||
const closeRealTerminal = (ev: CloseEvent) => {
|
||||
if (heartbeatTimer.value) {
|
||||
clearInterval(heartbeatTimer.value);
|
||||
clearInterval(Number(heartbeatTimer.value));
|
||||
}
|
||||
term.value.write('The connection has been disconnected.');
|
||||
term.value.write(ev.reason);
|
||||
term.value?.write('The connection has been disconnected.');
|
||||
term.value?.write(ev.reason);
|
||||
};
|
||||
|
||||
const isWsOpen = () => {
|
||||
|
||||
@@ -200,6 +200,10 @@ export const DNSTypes = [
|
||||
label: 'GoDaddy',
|
||||
value: 'Godaddy',
|
||||
},
|
||||
{
|
||||
label: 'FreeMyIP',
|
||||
value: 'FreeMyIP',
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('website.rainyun'),
|
||||
value: 'RainYun',
|
||||
|
||||
@@ -123,8 +123,6 @@ const message = {
|
||||
},
|
||||
msg: {
|
||||
noneData: 'No data available',
|
||||
disConn:
|
||||
'Please click the disconnect button directly to terminate the terminal connection, avoiding the use of exit commands like {0}.',
|
||||
delete: `This operation delete can't be undone. Do you want to continue?`,
|
||||
clean: `This operation clean can't be undone. Do you want to continue?`,
|
||||
deleteTitle: 'Delete',
|
||||
|
||||
@@ -74,7 +74,7 @@ const message = {
|
||||
createNewFile: '新しいファイルを作成します',
|
||||
helpDoc: '文書をヘルプします',
|
||||
unbind: 'バインド',
|
||||
cover: '上書き',
|
||||
cover: 'に覆いを',
|
||||
skip: 'スキップ',
|
||||
},
|
||||
search: {
|
||||
@@ -122,8 +122,6 @@ const message = {
|
||||
},
|
||||
msg: {
|
||||
noneData: '利用可能なデータはありません',
|
||||
disConn:
|
||||
'端末接続を切断するには、{0} のような終了コマンドを使用せずに、直接切断ボタンをクリックしてください',
|
||||
delete: `この操作削除は元に戻すことはできません。続けたいですか?`,
|
||||
clean: `この操作は取り消すことはできません。続けたいですか?`,
|
||||
deleteTitle: '消去',
|
||||
|
||||
@@ -123,8 +123,6 @@ const message = {
|
||||
},
|
||||
msg: {
|
||||
noneData: '데이터가 없습니다',
|
||||
disConn:
|
||||
'종료 명령어인 {0} 등을 사용하지 않고 직접 연결 끊기 버튼을 클릭하여 터미널 연결을 종료해 주십시오.',
|
||||
delete: `이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?`,
|
||||
clean: `이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?`,
|
||||
deleteTitle: '삭제',
|
||||
|
||||
@@ -123,8 +123,6 @@ const message = {
|
||||
},
|
||||
msg: {
|
||||
noneData: 'Tiada data tersedia',
|
||||
disConn:
|
||||
'Sila klik butang putus sambungan secara langsung untuk menamatkan sambungan terminal, mengelakkan penggunaan arahan keluar seperti {0}.',
|
||||
delete: 'Operasi ini tidak boleh diundur. Adakah anda mahu meneruskan?',
|
||||
clean: 'Operasi ini tidak boleh diundur. Adakah anda mahu meneruskan?',
|
||||
deleteTitle: 'Padam',
|
||||
|
||||
@@ -123,8 +123,6 @@ const message = {
|
||||
},
|
||||
msg: {
|
||||
noneData: 'Nenhum dado disponível',
|
||||
disConn:
|
||||
'Por favor, clique diretamente no botão de desconexão para encerrar a conexão do terminal, evitando o uso de comandos de saída como {0}.',
|
||||
delete: 'Esta operação de exclusão não pode ser desfeita. Deseja continuar?',
|
||||
clean: 'Esta operação de limpeza não pode ser desfeita. Deseja continuar?',
|
||||
deleteTitle: 'Excluir',
|
||||
|
||||
@@ -113,8 +113,7 @@ const message = {
|
||||
refreshRate: 'Частота обновления',
|
||||
refreshRateUnit: 'Без обновления | {n} секунда/раз | {n} секунд/раз',
|
||||
selectColumn: 'Выбрать столбец',
|
||||
cover: 'Заменить',
|
||||
skip: 'Пропустить',
|
||||
serialNumber: 'Серийный номер',
|
||||
},
|
||||
loadingText: {
|
||||
Upgrading: 'Обновление системы, пожалуйста, подождите...',
|
||||
@@ -124,8 +123,6 @@ const message = {
|
||||
},
|
||||
msg: {
|
||||
noneData: 'Нет данных',
|
||||
disConn:
|
||||
'Пожалуйста, нажмите кнопку отключения, чтобы разорвать соединение с терминалом, избегая использования команд выхода, таких как {0}.',
|
||||
delete: 'Эта операция удаления не может быть отменена. Хотите продолжить?',
|
||||
clean: 'Эта операция очистки не может быть отменена. Хотите продолжить?',
|
||||
deleteTitle: 'Удалить',
|
||||
|
||||
@@ -14,7 +14,7 @@ const message = {
|
||||
fit2cloud: '飛致雲',
|
||||
lingxia: '凌霞',
|
||||
button: {
|
||||
run: '運行',
|
||||
run: '執行',
|
||||
create: '建立',
|
||||
add: '新增',
|
||||
save: '儲存',
|
||||
@@ -123,7 +123,6 @@ const message = {
|
||||
},
|
||||
msg: {
|
||||
noneData: '暫無資料',
|
||||
disConn: '請直接點擊斷開按鈕斷開終端連接,避免使用 {0} 等退出命令。',
|
||||
delete: '刪除 操作不可復原,是否繼續?',
|
||||
clean: '清空 操作不可復原,是否繼續?',
|
||||
deleteTitle: '刪除',
|
||||
@@ -375,7 +374,7 @@ const message = {
|
||||
|
||||
core: '物理核心',
|
||||
logicCore: '邏輯核心',
|
||||
loadAverage: '最近 {0} 分鐘平均負載',
|
||||
loadAverage: '最近 1 分鐘平均負載 | 最近 {n} 分鐘平均負載',
|
||||
load: '負載',
|
||||
mount: '掛載點',
|
||||
fileSystem: '檔案系統',
|
||||
@@ -579,18 +578,18 @@ const message = {
|
||||
ai_tools: {
|
||||
model: {
|
||||
model: '模型',
|
||||
create: '添加模型',
|
||||
create: '新增模型',
|
||||
create_helper: '拉取 "{0}"',
|
||||
ollama_doc: '您可以訪問 Ollama 官方網站,搜索並查找更多模型。',
|
||||
container_conn_helper: '容器間訪問或連接使用此地址',
|
||||
ollama_doc: '您可以瀏覽 Ollama 官方網站,搜尋並查找更多模型。',
|
||||
container_conn_helper: '容器間瀏覽或連接使用此地址',
|
||||
ollama_sync: '同步 Ollama 模型發現下列模型不存在,是否刪除?',
|
||||
from_remote: '該模型並非通過 1Panel 下載,無相關拉取日誌。',
|
||||
from_remote: '該模型並非透過 1Panel 下載,無相關拉取日誌。',
|
||||
no_logs: '該模型的拉取日誌已被刪除,無法查看相關日誌。',
|
||||
},
|
||||
gpu: {
|
||||
gpu: 'GPU 监控',
|
||||
gpu: 'GPU 監控',
|
||||
base: '基礎資訊',
|
||||
gpuHelper: '目前系統未檢測到 NVIDIA-SMI或者XPU-SMI 指令,請檢查後重試!',
|
||||
gpuHelper: '目前系統未檢測到 NVIDIA-SMI 或者 XPU-SMI 指令,請檢查後重試!',
|
||||
driverVersion: '驅動版本',
|
||||
cudaVersion: 'CUDA 版本',
|
||||
process: '行程資訊',
|
||||
@@ -599,10 +598,10 @@ const message = {
|
||||
typeC: '計算',
|
||||
typeCG: '計算+圖形',
|
||||
processName: '行程名稱',
|
||||
processMemoryUsage: '顯存使用',
|
||||
processMemoryUsage: '記憶體使用',
|
||||
temperatureHelper: 'GPU 溫度過高會導致 GPU 頻率下降',
|
||||
performanceStateHelper: '從 P0 (最大性能) 到 P12 (最小性能)',
|
||||
busID: '總線地址',
|
||||
busID: '匯流排地址',
|
||||
persistenceMode: '持續模式',
|
||||
enabled: '開啟',
|
||||
disabled: '關閉',
|
||||
@@ -610,15 +609,15 @@ const message = {
|
||||
displayActive: '顯卡初始化',
|
||||
displayActiveT: '是',
|
||||
displayActiveF: '否',
|
||||
ecc: '是否開啟錯誤檢查和紀正技術',
|
||||
ecc: '是否開啟錯誤檢查和糾正技術',
|
||||
computeMode: '計算模式',
|
||||
default: '預設',
|
||||
exclusiveProcess: '行程排他',
|
||||
exclusiveThread: '線程排他',
|
||||
exclusiveThread: '執行緒排他',
|
||||
prohibited: '禁止',
|
||||
defaultHelper: '預設: 行程可以並發執行',
|
||||
exclusiveProcessHelper: '行程排他: 只有一個 CUDA 上下文可以使用 GPU, 但可以由多個線程共享',
|
||||
exclusiveThreadHelper: '線程排他: 只有一個線程在 CUDA 上下文中可以使用 GPU',
|
||||
exclusiveProcessHelper: '行程排他: 只有一個 CUDA 上下文可以使用 GPU, 但可以由多個執行緒共享',
|
||||
exclusiveThreadHelper: '執行緒排他: 只有一個執行緒在 CUDA 上下文中可以使用 GPU',
|
||||
prohibitedHelper: '禁止: 不允許行程同時執行',
|
||||
migModeHelper: '用於建立 MIG 實例,在用戶層實現 GPU 的物理隔離。',
|
||||
migModeNA: '不支援',
|
||||
@@ -1414,7 +1413,7 @@ const message = {
|
||||
proxyDockerHelper: '將代理伺服器配寘同步至 Docker,支援離線伺服器拉取鏡像等操作',
|
||||
apiInterface: 'API 介面',
|
||||
apiInterfaceClose: '關閉後將不能使用 API 介面進行瀏覽,APP功能將被限制,是否繼續?',
|
||||
apiInterfaceHelper: '支持面板 API 接口訪問功能(面板 APP 需要開啟該功能)',
|
||||
apiInterfaceHelper: '支持面板 API 介面瀏覽功能(面板 APP 需要開啟該功能)',
|
||||
apiInterfaceAlert1: '請不要在生產環境開啟,這可能新增伺服器安全風險',
|
||||
apiInterfaceAlert2: '請不要使用協力廠商應用呼叫面板 API,以防止潛在的安全威脅',
|
||||
apiInterfaceAlert3: 'API 介面檔案',
|
||||
@@ -1692,7 +1691,7 @@ const message = {
|
||||
setting: '升級專業版可以自訂面板 Logo、歡迎簡介等資訊。',
|
||||
monitor: '升級專業版可以查看網站的即時狀態、訪客趨勢、訪客來源、請求日誌等資訊。 ',
|
||||
alert: '升級專業版可透過簡訊接收告警資訊,並查看告警日誌,全面掌控各類關鍵事件,確保系統執行無憂。',
|
||||
app: '升級專業版可以通過手機 APP 管理伺服器並查看監控等功能。',
|
||||
app: '升級專業版可以透過手機 APP 管理伺服器並查看監控等功能。',
|
||||
},
|
||||
clean: {
|
||||
scan: '開始掃描',
|
||||
@@ -2112,7 +2111,7 @@ const message = {
|
||||
sni: '回源 SNI',
|
||||
sniHelper: '反代後端為 https 的時候可能需要設定回源 SNI,具體需要看 CDN 服務商文件',
|
||||
huaweicloud: '華為雲',
|
||||
rainyun: '瑞雲',
|
||||
rainyun: '雨雲',
|
||||
},
|
||||
php: {
|
||||
short_open_tag: '短標籤支援',
|
||||
@@ -2427,12 +2426,12 @@ const message = {
|
||||
aitool: {
|
||||
proxy: 'AI 代理增強',
|
||||
proxyHelper1: '綁定域名並啟用 HTTPS,提高傳輸安全性',
|
||||
proxyHelper2: '限制 IP 訪問,防止在公共網絡上暴露',
|
||||
proxyHelper3: '啟用流式傳輸',
|
||||
proxyHelper2: '限制 IP 瀏覽,防止在網路上暴露',
|
||||
proxyHelper3: '啟用即時串流',
|
||||
proxyHelper4: '創建後,您可以在網站列表中查看並管理',
|
||||
proxyHelper5: '啟用後,您可以在應用商店 - 已安裝 - Ollama - 參數中取消端口外部訪問以提高安全性。',
|
||||
proxyHelper5: '啟用後,您可以在應用商店 - 已安裝 - Ollama - 參數中取消埠外部瀏覽以提高安全性',
|
||||
proxyHelper6: '如需關閉代理配置,可以在網站列表中刪除',
|
||||
whiteListHelper: '限制僅白名單中的 IP 可訪問',
|
||||
whiteListHelper: '限制僅白名單中的 IP 可瀏覽',
|
||||
},
|
||||
};
|
||||
export default {
|
||||
|
||||
@@ -121,7 +121,6 @@ const message = {
|
||||
Rollbacking: '快照回滚中,请稍候...',
|
||||
},
|
||||
msg: {
|
||||
disConn: '请直接点击断开按钮断开终端连接,避免使用 {0} 等退出命令',
|
||||
noneData: '暂无数据',
|
||||
delete: '删除 操作不可回滚,是否继续?',
|
||||
clean: '清空 操作不可回滚,是否继续?',
|
||||
@@ -564,7 +563,7 @@ const message = {
|
||||
restartNowHelper: '修改配置后需要重启生效。',
|
||||
|
||||
persistence: '持久化',
|
||||
rdbHelper1: '秒內,插入',
|
||||
rdbHelper1: '秒内,插入',
|
||||
rdbHelper2: '条数据',
|
||||
rdbHelper3: '符合任意一个条件将会触发RDB持久化',
|
||||
rdbInfo: '请确认规则列表中值在 1-100000 之间',
|
||||
|
||||
@@ -37,6 +37,7 @@ export interface GlobalState {
|
||||
isIntl: boolean;
|
||||
isTrial: boolean;
|
||||
productProExpires: number;
|
||||
licenseVerify: string;
|
||||
|
||||
errStatus: string;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ const GlobalStore = defineStore({
|
||||
isIntl: false,
|
||||
isTrial: false,
|
||||
productProExpires: 0,
|
||||
licenseVerify: '',
|
||||
|
||||
errStatus: '',
|
||||
}),
|
||||
|
||||
@@ -358,7 +358,7 @@ export function checkCidrV6(value: string): boolean {
|
||||
if (checkIpV6(value.split('/')[0])) {
|
||||
return true;
|
||||
}
|
||||
const reg = /^(?:[1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])$/;
|
||||
const reg = /^(?:[0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])$/;
|
||||
if (!reg.test(value.split('/')[1])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -329,7 +329,7 @@ const loadEcc = (val: string) => {
|
||||
if (val === 'Enabled') {
|
||||
return i18n.global.t('ai_tools.gpu.enabled');
|
||||
}
|
||||
return val;
|
||||
return val || 0;
|
||||
};
|
||||
|
||||
const loadProcessType = (val: string) => {
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<template #title>
|
||||
<span class="flx-align-center">
|
||||
{{ $t('ai_tools.model.ollama_doc') }}
|
||||
<el-link class="ml-5" icon="Position" @click="goSearch()" type="primary">
|
||||
<el-button link class="ml-5" icon="Position" @click="goSearch()" type="primary">
|
||||
{{ $t('firewall.quickJump') }}
|
||||
</el-link>
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-alert>
|
||||
|
||||
@@ -202,9 +202,6 @@ const listSSL = () => {
|
||||
const listAcmeAccount = () => {
|
||||
SearchAcmeAccount({ page: 1, pageSize: 100 }).then((res) => {
|
||||
acmeAccounts.value = res.data.items || [];
|
||||
if (acmeAccounts.value.length > 0) {
|
||||
req.value.acmeAccountID = acmeAccounts.value[0].id;
|
||||
}
|
||||
listSSL();
|
||||
});
|
||||
};
|
||||
@@ -238,7 +235,8 @@ const search = async (appInstallID: number) => {
|
||||
if (res.data.sslID > 0) {
|
||||
req.value.enableSSL = true;
|
||||
req.value.sslID = res.data.sslID;
|
||||
listSSL();
|
||||
req.value.acmeAccountID = res.data.acmeAccountID;
|
||||
listAcmeAccount();
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
@@ -114,14 +114,14 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
min-width="100"
|
||||
:label="$t('commons.table.createdAt')"
|
||||
min-width="80"
|
||||
:label="$t('commons.table.date')"
|
||||
prop="createdAt"
|
||||
:formatter="dateFormat"
|
||||
/>
|
||||
<fu-table-operations
|
||||
:ellipsis="mobile ? 0 : 10"
|
||||
:min-width="mobile ? 'auto' : 100"
|
||||
:min-width="mobile ? 'auto' : 200"
|
||||
:buttons="buttons"
|
||||
:label="$t('commons.table.operate')"
|
||||
fixed="right"
|
||||
@@ -430,7 +430,7 @@ const buttons = [
|
||||
onDelete(row);
|
||||
},
|
||||
disabled: (row: any) => {
|
||||
return row.status !== 'Success' && row.status !== 'Deleted';
|
||||
return row.status === 'Waiting';
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -16,12 +16,7 @@
|
||||
</template>
|
||||
</DrawerHeader>
|
||||
</template>
|
||||
<el-alert type="error" :closable="false">
|
||||
<template #title>
|
||||
<span>{{ $t('commons.msg.disConn', ['/bye exit']) }}</span>
|
||||
</template>
|
||||
</el-alert>
|
||||
<Terminal class="mt-2" style="height: calc(100vh - 225px)" ref="terminalRef"></Terminal>
|
||||
<Terminal class="mt-2" style="height: calc(100vh - 175px)" ref="terminalRef"></Terminal>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
@@ -67,8 +62,8 @@ const loadTooltip = () => {
|
||||
const initTerm = () => {
|
||||
nextTick(() => {
|
||||
terminalRef.value.acceptParams({
|
||||
endpoint: '/api/v1/ai/ollama/exec',
|
||||
args: `name=${itemName.value}`,
|
||||
endpoint: '/api/v1/containers/exec',
|
||||
args: `source=ollama&name=${itemName.value}`,
|
||||
error: '',
|
||||
initCmd: '',
|
||||
});
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
import { cleanContainerLog, DownloadFile } from '@/api/modules/container';
|
||||
import i18n from '@/lang';
|
||||
import { dateFormatForName } from '@/utils/util';
|
||||
import { computed, onBeforeUnmount, reactive, ref, shallowRef, watch } from 'vue';
|
||||
import { computed, nextTick, onBeforeUnmount, reactive, ref, shallowRef, watch } from 'vue';
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
@@ -152,10 +152,12 @@ const searchLogs = async () => {
|
||||
);
|
||||
terminalSocket.value.onmessage = (event) => {
|
||||
logInfo.value += event.data.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '');
|
||||
const state = view.value.state;
|
||||
view.value.dispatch({
|
||||
selection: { anchor: state.doc.length, head: state.doc.length },
|
||||
scrollIntoView: true,
|
||||
nextTick(() => {
|
||||
const state = view.value.state;
|
||||
view.value.dispatch({
|
||||
selection: { anchor: state.doc.length, head: state.doc.length },
|
||||
scrollIntoView: true,
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@@ -10,12 +10,7 @@
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('container.containerTerminal')" :resource="title" :back="handleClose" />
|
||||
</template>
|
||||
<el-alert type="error" :closable="false">
|
||||
<template #title>
|
||||
<span>{{ $t('commons.msg.disConn', ['exit']) }}</span>
|
||||
</template>
|
||||
</el-alert>
|
||||
<el-form ref="formRef" class="mt-2" :model="form" label-position="top">
|
||||
<el-form ref="formRef" :model="form" label-position="top">
|
||||
<el-form-item :label="$t('commons.table.user')" prop="user">
|
||||
<el-input placeholder="root" clearable v-model="form.user" />
|
||||
</el-form-item>
|
||||
@@ -51,7 +46,7 @@
|
||||
</el-button>
|
||||
<el-button v-else @click="onClose()">{{ $t('commons.button.disconnect') }}</el-button>
|
||||
<Terminal
|
||||
style="height: calc(100vh - 355px); margin-top: 18px"
|
||||
style="height: calc(100vh - 312px); margin-top: 18px"
|
||||
ref="terminalRef"
|
||||
v-if="terminalOpen"
|
||||
></Terminal>
|
||||
@@ -104,7 +99,7 @@ const initTerm = (formEl: FormInstance | undefined) => {
|
||||
await nextTick();
|
||||
terminalRef.value!.acceptParams({
|
||||
endpoint: '/api/v1/containers/exec',
|
||||
args: `containerid=${form.containerID}&user=${form.user}&command=${form.command}`,
|
||||
args: `source=container&containerid=${form.containerID}&user=${form.user}&command=${form.command}`,
|
||||
error: '',
|
||||
initCmd: '',
|
||||
});
|
||||
|
||||
@@ -309,8 +309,8 @@ const initTerminal = async () => {
|
||||
terminalShow.value = true;
|
||||
redisStatus.value = 'Running';
|
||||
terminalRef.value.acceptParams({
|
||||
endpoint: '/api/v1/databases/redis/exec',
|
||||
args: `name=${currentDBName.value}&from=${currentDB.value.from}`,
|
||||
endpoint: '/api/v1/containers/exec',
|
||||
args: `source=redis&name=${currentDBName.value}&from=${currentDB.value.from}`,
|
||||
error: '',
|
||||
initCmd: '',
|
||||
});
|
||||
@@ -327,8 +327,8 @@ const initTerminal = async () => {
|
||||
if (res.data.status === 'Running') {
|
||||
terminalShow.value = true;
|
||||
terminalRef.value.acceptParams({
|
||||
endpoint: '/api/v1/databases/redis/exec',
|
||||
args: `name=${currentDBName.value}&from=${currentDB.value.from}`,
|
||||
endpoint: '/api/v1/containers/exec',
|
||||
args: `source=redis&name=${currentDBName.value}&from=${currentDB.value.from}`,
|
||||
error: '',
|
||||
initCmd: '',
|
||||
});
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
>
|
||||
<template #header>
|
||||
<div ref="dialogHeader" class="flex items-center justify-between">
|
||||
<span>{{ $t('commons.button.edit') + ' - ' + form.path }}</span>
|
||||
<el-space alignment="center" :size="1" class="dialog-header-icon">
|
||||
<div class="truncate flex-1 mr-2">
|
||||
<span>{{ $t('commons.button.edit') + ' - ' + form.path }}</span>
|
||||
</div>
|
||||
<el-space alignment="center" :size="1" class="dialog-header-icon flex-shrink-0">
|
||||
<el-tooltip :content="loadTooltip()" placement="top">
|
||||
<el-button
|
||||
@click="toggleFullscreen"
|
||||
|
||||
@@ -6,15 +6,47 @@
|
||||
:before-close="handleClose"
|
||||
:title="$t('file.downloadProcess')"
|
||||
>
|
||||
<div v-for="(value, index) in res" :key="index">
|
||||
<span>{{ value['percent'] === 100 ? $t('file.downloadSuccess') : $t('file.downloading') }}</span>
|
||||
<MsgInfo :info="value['name']" width="250" />
|
||||
<el-progress v-if="value['total'] == 0" :percentage="100" :indeterminate="true" :duration="1" />
|
||||
<el-progress v-else :text-inside="true" :stroke-width="15" :percentage="value['percent']"></el-progress>
|
||||
<span>
|
||||
{{ getFileSize(value['written']) }}/
|
||||
<span v-if="value['total'] > 0">{{ getFileSize(value['total']) }}</span>
|
||||
</span>
|
||||
<div class="space-y-4 p-4">
|
||||
<div
|
||||
v-for="(value, index) in res"
|
||||
:key="index"
|
||||
class="bg-white rounded-lg p-4 shadow-sm border border-gray-100 transition-all duration-200 hover:shadow-md"
|
||||
:class="{ completed: value.percent === 100 }"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex-1">
|
||||
<MsgInfo :info="value.name" class="text-gray-700" />
|
||||
<div class="text-gray-500">
|
||||
{{ value.percent === 100 ? $t('file.downloadSuccess') : $t('file.downloading') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-end text-gray-500 mb-1">
|
||||
<span>{{ getFileSize(value.written) }}</span>
|
||||
<span v-if="value.total > 0" class="text-gray-400">/{{ getFileSize(value.total) }}</span>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<el-progress
|
||||
v-if="value.total === 0 && value.percent != 100"
|
||||
:percentage="100"
|
||||
:indeterminate="true"
|
||||
:duration="1"
|
||||
class="progress-bar"
|
||||
:stroke-width="8"
|
||||
:show-text="false"
|
||||
/>
|
||||
<el-progress
|
||||
v-else
|
||||
:percentage="value.percent"
|
||||
:stroke-width="8"
|
||||
class="progress-bar"
|
||||
:status="value.percent === 100 ? 'success' : ''"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
@@ -108,3 +140,30 @@ const onOpen = () => {
|
||||
getKeys();
|
||||
};
|
||||
</script>
|
||||
<style type="scss" scoped>
|
||||
.download-item.completed {
|
||||
@apply bg-green-50/50;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
:deep(.el-progress-bar__outer) {
|
||||
@apply rounded-full bg-gray-100;
|
||||
}
|
||||
|
||||
:deep(.el-progress-bar__inner) {
|
||||
@apply rounded-full transition-all duration-300;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(-10%);
|
||||
animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(0);
|
||||
animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -77,7 +77,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<OpDialog ref="opRef" @search="search" />
|
||||
<OpDialog ref="opRef" @search="search" @submit="onSubmitDelete()">
|
||||
<template #content>
|
||||
<el-form class="mt-4 mb-1" ref="deleteForm" label-position="left">
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="forceDelete" :label="$t('website.forceDelete')" />
|
||||
<span class="input-help">
|
||||
{{ $t('website.forceDeleteHelper') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
</OpDialog>
|
||||
<OperateDialog @search="search" ref="dialogRef" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -91,6 +102,7 @@ import { operateForwardRule, searchFireRule } from '@/api/modules/host';
|
||||
import { Host } from '@/api/interface/host';
|
||||
import i18n from '@/lang';
|
||||
import { GlobalStore } from '@/store';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
@@ -107,6 +119,8 @@ const fireName = ref();
|
||||
const fireStatusRef = ref();
|
||||
|
||||
const opRef = ref();
|
||||
const forceDelete = ref(false);
|
||||
const operateRules = ref();
|
||||
|
||||
const data = ref();
|
||||
const paginationConfig = reactive({
|
||||
@@ -180,6 +194,7 @@ const onDelete = async (row: Host.RuleForward | null) => {
|
||||
});
|
||||
}
|
||||
}
|
||||
operateRules.value = rules;
|
||||
opRef.value.acceptParams({
|
||||
title: i18n.global.t('commons.button.delete'),
|
||||
names: names,
|
||||
@@ -187,11 +202,24 @@ const onDelete = async (row: Host.RuleForward | null) => {
|
||||
i18n.global.t('firewall.forwardRule'),
|
||||
i18n.global.t('commons.button.delete'),
|
||||
]),
|
||||
api: operateForwardRule,
|
||||
params: { rules: rules },
|
||||
api: null,
|
||||
params: null,
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmitDelete = async () => {
|
||||
loading.value = true;
|
||||
await operateForwardRule({ rules: operateRules.value, forceDelete: forceDelete.value })
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
|
||||
search();
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('commons.button.edit'),
|
||||
@@ -208,6 +236,7 @@ const buttons = [
|
||||
];
|
||||
|
||||
onMounted(() => {
|
||||
forceDelete.value = false;
|
||||
if (fireName.value !== '-') {
|
||||
loading.value = true;
|
||||
fireStatusRef.value.acceptParams();
|
||||
|
||||
@@ -412,6 +412,7 @@ const loadDataFromDB = async () => {
|
||||
i18n.locale.value = res.data.language;
|
||||
i18n.warnHtmlMessage = false;
|
||||
globalStore.entrance = res.data.securityEntrance;
|
||||
globalStore.licenseVerify = res.data.licenseVerify;
|
||||
globalStore.setDefaultNetwork(res.data.defaultNetwork);
|
||||
globalStore.setOpenMenuTabs(res.data.menuTabs === 'enable');
|
||||
globalStore.updateLanguage(res.data.language);
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
<el-col :xs="24" :sm="24" :md="9" :lg="9" :xl="9">
|
||||
<CardWithHeader :header="$t('license.quickUpdate')" height="160px">
|
||||
<template #body>
|
||||
<div class="h-app-card">
|
||||
<div class="h-app-card" v-if="globalStore.licenseVerify != 'TC' || !hasLicense">
|
||||
<el-row>
|
||||
<el-col :span="15">
|
||||
<div class="h-app-content">{{ $t('license.importLicense') }}</div>
|
||||
|
||||
@@ -98,7 +98,7 @@ import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { ElMessageBox, FormInstance } from 'element-plus';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { checkCidr, checkIp } from '@/utils/util';
|
||||
import { checkCidr, checkCidrV6, checkIpV4V6 } from '@/utils/util';
|
||||
import { GlobalStore } from '@/store';
|
||||
|
||||
const globalStore = GlobalStore();
|
||||
@@ -138,10 +138,16 @@ function checkIPs(rule: any, value: any, callback: any) {
|
||||
continue;
|
||||
}
|
||||
if (item.indexOf('/') !== -1) {
|
||||
if (checkCidr(item)) {
|
||||
return callback(new Error(i18n.global.t('firewall.addressFormatError')));
|
||||
if (item.indexOf(':') !== -1) {
|
||||
if (checkCidrV6(item)) {
|
||||
return callback(new Error(i18n.global.t('firewall.addressFormatError')));
|
||||
}
|
||||
} else {
|
||||
if (checkCidr(item)) {
|
||||
return callback(new Error(i18n.global.t('firewall.addressFormatError')));
|
||||
}
|
||||
}
|
||||
} else if (checkIp(item)) {
|
||||
} else if (checkIpV4V6(item)) {
|
||||
return callback(new Error(i18n.global.t('firewall.addressFormatError')));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ const onSave = async (formEl: FormInstance | undefined) => {
|
||||
type: 'info',
|
||||
},
|
||||
).then(async () => {
|
||||
await updateFail2ban({ key: 'logPath', value: form.logPath })
|
||||
await updateFail2ban({ key: 'logpath', value: form.logPath })
|
||||
.then(async () => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
loading.value = false;
|
||||
|
||||
@@ -68,7 +68,11 @@
|
||||
<el-input v-model.trim="account.authorization['token']"></el-input>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div v-if="account.type === 'FreeMyIP'">
|
||||
<el-form-item label="Token" prop="authorization.token">
|
||||
<el-input v-model.trim="account.authorization['token']"></el-input>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div v-if="account.type === 'CloudFlare'">
|
||||
<el-form-item label="EMAIL" prop="authorization.email">
|
||||
<el-input v-model.trim="account.authorization['email']"></el-input>
|
||||
|
||||
@@ -507,7 +507,7 @@ const searchAppInstalled = () => {
|
||||
GetAppInstalled({ type: 'website', unused: true, all: true, page: 1, pageSize: 100 }).then((res) => {
|
||||
appInstalls.value = res.data.items;
|
||||
if (res.data.items && res.data.items.length > 0) {
|
||||
website.value.appInstallId = res.data[0].id;
|
||||
website.value.appInstallId = res.data.items[0].id;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ require (
|
||||
github.com/gin-contrib/gzip v0.0.6
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/glebarez/sqlite v1.10.0
|
||||
github.com/go-acme/lego/v4 v4.21.0
|
||||
github.com/go-acme/lego/v4 v4.22.2
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.1
|
||||
github.com/go-playground/validator/v10 v10.18.0
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
@@ -50,13 +50,15 @@ require (
|
||||
github.com/swaggo/files v1.0.1
|
||||
github.com/swaggo/gin-swagger v1.6.0
|
||||
github.com/swaggo/swag v1.16.3
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cloudapp v1.0.1105
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1105
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.46
|
||||
github.com/xlzd/gotp v0.1.0
|
||||
golang.org/x/crypto v0.31.0
|
||||
golang.org/x/net v0.33.0
|
||||
golang.org/x/crypto v0.32.0
|
||||
golang.org/x/net v0.37.0
|
||||
golang.org/x/oauth2 v0.24.0
|
||||
golang.org/x/sys v0.28.0
|
||||
golang.org/x/term v0.27.0
|
||||
golang.org/x/sys v0.29.0
|
||||
golang.org/x/term v0.28.0
|
||||
golang.org/x/text v0.21.0
|
||||
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
@@ -66,6 +68,7 @@ require (
|
||||
|
||||
require (
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
@@ -96,20 +99,23 @@ require (
|
||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.112.0 // indirect
|
||||
github.com/containerd/cgroups v1.1.0 // indirect
|
||||
github.com/containerd/console v1.0.4 // indirect
|
||||
github.com/containerd/containerd v1.7.13 // indirect
|
||||
github.com/containerd/containerd v1.7.27 // indirect
|
||||
github.com/containerd/continuity v0.4.3 // indirect
|
||||
github.com/containerd/fifo v1.1.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/ttrpc v1.2.3 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.1.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/distribution/reference v0.5.0 // indirect
|
||||
github.com/docker/buildx v0.13.1 // indirect
|
||||
github.com/docker/buildx v0.21.3 // indirect
|
||||
github.com/docker/cli v26.0.0+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.1 // indirect
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
@@ -119,7 +125,7 @@ require (
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
@@ -183,6 +189,7 @@ require (
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/sys/mountinfo v0.7.1 // indirect
|
||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.0 // indirect
|
||||
github.com/moby/sys/user v0.1.0 // indirect
|
||||
@@ -194,9 +201,12 @@ require (
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/nrdcg/dnspod-go v0.4.0 // indirect
|
||||
github.com/nrdcg/freemyip v0.3.0 // indirect
|
||||
github.com/nrdcg/namesilo v0.2.1 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.1.0 // indirect
|
||||
github.com/opencontainers/selinux v1.11.0 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||
@@ -218,7 +228,6 @@ require (
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1065 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1065 // indirect
|
||||
github.com/therootcompany/xz v1.0.1 // indirect
|
||||
github.com/theupdateframework/notary v0.7.0 // indirect
|
||||
|
||||
@@ -318,15 +318,15 @@ github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec
|
||||
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
|
||||
github.com/glebarez/sqlite v1.10.0 h1:u4gt8y7OND/cCei/NMHmfbLxF6xP2wgKcT/BJf2pYkc=
|
||||
github.com/glebarez/sqlite v1.10.0/go.mod h1:IJ+lfSOmiekhQsFTJRx/lHtGYmCdtAiTaf5wI9u5uHA=
|
||||
github.com/go-acme/lego/v4 v4.21.0 h1:arEW+8o5p7VI8Bk1kr/PDlgD1DrxtTH1gJ4b7mehL8o=
|
||||
github.com/go-acme/lego/v4 v4.21.0/go.mod h1:HrSWzm3Ckj45Ie3i+p1zKVobbQoMOaGu9m4up0dUeDI=
|
||||
github.com/go-acme/lego/v4 v4.22.2 h1:ck+HllWrV/rZGeYohsKQ5iKNnU/WAZxwOdiu6cxky+0=
|
||||
github.com/go-acme/lego/v4 v4.22.2/go.mod h1:E2FndyI3Ekv0usNJt46mFb9LVpV/XBYT+4E3tz02Tzo=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.1 h1:eGS0WTFRV30r103lU8JNXY27KbviRnqqIDobW3EV3iY=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.1/go.mod h1:L7nJ620PFDKei9QOhJzqA8kRCk+E3UbV2f5gv+1ndLc=
|
||||
github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
|
||||
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
|
||||
@@ -756,6 +756,8 @@ github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7g
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nrdcg/dnspod-go v0.4.0 h1:c/jn1mLZNKF3/osJ6mz3QPxTudvPArXTjpkmYj0uK6U=
|
||||
github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ=
|
||||
github.com/nrdcg/freemyip v0.3.0 h1:0D2rXgvLwe2RRaVIjyUcQ4S26+cIS2iFwnhzDsEuuwc=
|
||||
github.com/nrdcg/freemyip v0.3.0/go.mod h1:c1PscDvA0ukBF0dwelU/IwOakNKnVxetpAQ863RMJoM=
|
||||
github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg=
|
||||
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
|
||||
@@ -958,9 +960,12 @@ github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+z
|
||||
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
|
||||
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
|
||||
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cloudapp v1.0.1105 h1:ikgsQkFcKDzOJFbxIcSCdu7oj8GQBfwJohBpYHWeSco=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cloudapp v1.0.1105/go.mod h1:/GMcTQRH1+iDTX8RSl+G79doAWcabhpt+xmv3V9P3p0=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1065 h1:krcqtAmexnHHBm/4ge4tr2b1cn/a7JGBESVGoZYXQAE=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1065/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1105 h1:lCs0dmezU6/8JcfNwEaam1Pm1RS/5MhXvNhf/X2y65s=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1105/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1065 h1:aEFtLD1ceyeljQXB1S2BjN0zjTkf0X3XmpuxFIiC29w=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1065/go.mod h1:HWvwy09hFSMXrj9SMvVRWV4U7rZO3l+WuogyNuxiT3M=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
|
||||
@@ -1104,8 +1109,8 @@ golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -1238,6 +1243,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -1250,16 +1256,16 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
||||
Reference in New Issue
Block a user