Compare commits

...
Author SHA1 Message Date
ssongliu 79eb0288b4 fix: Fix the problem that the terminal exit fails (#8189) 2025-03-19 03:43:21 +00:00
蘭 99775e1eb4 fix: Fix some issues (#8178) 2025-03-18 17:16:23 +08:00
zhengkunwang db848b41d2 feat: update buildx and containerd version (#8183) 2025-03-18 17:13:36 +08:00
david082321 b5b11dff86 Update tw.ts (#8161) 2025-03-17 10:58:19 +08:00
zhengkunwang 9cf4bfc606 feat: update golang.org/x/net version to v0.37.0 (#8146) 2025-03-13 18:30:25 +08:00
ThinkStu 221162d44f Update zh.ts (#8136)
correct typos
2025-03-12 22:43:20 +08:00
蘭 617970a1f8 fix: license setting data (#8132) 2025-03-12 16:25:32 +08:00
ssongliu 5610c73d67 fix: Fix the problem that services exit unexpectedly when the contain… (#8127) 2025-03-12 03:19:03 +00:00
ssongliu 51fe02661f fix: Fix the problem of incorrect calculation of container memory res… (#8125)
Refs #8033
2025-03-12 02:13:03 +00:00
lm379andlm379 8f491b2933 feat: allow tencent cloud cos dual stack domain (#8123)
Co-authored-by: lm379 <lm379@qq.com>
2025-03-11 23:58:23 +08:00
王贺 b7e16bd6d5 Update .goreleaser.yaml (#8118) 2025-03-11 13:50:48 +08:00
ssongliu 38ca2ddd01 fix(container): Fix cpu surge when the container terminal executes exit (#8115) 2025-03-10 23:47:50 +08:00
ssongliu 402b41100b fix(container): Fix cpu surge when the container terminal executes exit (#8096)
Refs #7574
2025-03-07 11:06:59 +00:00
ssongliu 766beae4e1 fix(container): Fix the problem that a container cannot create a read… (#8095)
Refs #8074
2025-03-07 07:44:59 +00:00
maninhill 05f72e9dcf chore: Update README.md 2025-03-06 11:40:56 +08:00
igophper 23553c68ae fix(fail2ban): fix the issue of set fail2ban log path err (#8076)
Refs #8075
2025-03-06 09:44:48 +08:00
zhengkunwang 4dd1eb7a1e feat: Modify Swagger route response for unauthenticated access (#8057) 2025-03-04 05:48:57 +00:00
蘭 db307732c6 feat: Adjust license (#8056) 2025-03-03 14:32:56 +00:00
igophperandssongliu 33d4ade1d4 refactor(container): optimize memory calculation and concurrent data handling (#8038)
* refactor(container): optimize memory calculation and concurrent data handling

Refs #8033

* Update container.go

---------

Co-authored-by: ssongliu <73214554+ssongliu@users.noreply.github.com>
2025-03-03 17:38:32 +08:00
igophper 186075ff9a style: optimize code editor file path style (#8039)
Refs #8015
2025-03-03 10:23:24 +08:00
zhengkunwang 34df745d1c style: Change wget page style (#8016) 2025-02-26 10:16:54 +00:00
zhengkunwang ec363ac157 feat: update default app.yaml (#8011) 2025-02-26 15:35:54 +08:00
蘭 193b0f3cd4 fix: Update swagger docs (#8002)
Refs #8001
2025-02-26 10:55:40 +08:00
zhengkunwang 67c1bf2924 feat: Add certificate validation when creating AI Proxy (#7994) 2025-02-25 15:21:16 +08:00
ssongliu b1373ff77e feat: Support gpu of schema v11 (#7988) 2025-02-25 14:16:34 +08:00
dependabot[bot] 55d0e27212 chore(deps): bump github.com/go-jose/go-jose/v4 from 4.0.4 to 4.0.5
Bumps [github.com/go-jose/go-jose/v4](https://github.com/go-jose/go-jose) from 4.0.4 to 4.0.5.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Changelog](https://github.com/go-jose/go-jose/blob/main/CHANGELOG.md)
- [Commits](https://github.com/go-jose/go-jose/compare/v4.0.4...v4.0.5)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-25 09:34:14 +08:00
zhengkunwang 1c052b7c13 fix: Fix the issue with AI proxy certificate res error (#7983) 2025-02-24 22:24:51 +08:00
蘭 6c8d48c2dc feat: API interface IP whitelist supports IPv6 (#7981)
Refs #7836
2025-02-24 10:05:13 +00:00
ssongliu b61dae638b fix(forward): Fix the problem of duplicate creation of port forwardin… (#7975)
Refs #7969
2025-02-24 09:21:14 +00:00
zhengkunwang 872a93aa07 fix: Fix the issue with the homepage route refresh (#7974) 2025-02-24 15:00:20 +08:00
igophper b1f337020c fix(container/log): enable close-on-click-modal and wrap log update in nextTick (#7960)
Refs #7547
2025-02-24 13:34:51 +08:00
maninhill 56858dd74c chore: Update README.md 2025-02-24 10:54:05 +08:00
Johannes Plunien 358415f737 feat: Support freemyip.com DNS provider (#7958) 2025-02-24 10:21:37 +08:00
david082321 5e73260103 feat: Update zh-Hant translation (#7966) 2025-02-22 23:16:38 +08:00
zhengkunwang e169f46784 fix: Fixed the issue of no echo after binding the SSL to the AI. (#7953) 2025-02-21 09:29:51 +00:00
ssongliu 39416abd47 fix: Fix the issue of inconsistent deletion status for the ollama model (#7950)
Refs #7948
2025-02-21 08:51:51 +00:00
wanghe-fit2cloud 640a0ca815 chore: Optimize some command outputs 2025-02-21 16:45:56 +08:00
ssongliu 8e54cd5162 style: Adjust the width display of the model list (#7946) 2025-02-21 07:54:29 +00:00
ssongliu 0f0099accb fix: Fix the issue of loading errors for editing container port data (#7945)
Refs #7938
2025-02-21 04:01:49 +00:00
81 changed files with 652 additions and 565 deletions
+1 -1
View File
@@ -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:
+4 -2
View File
@@ -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
+1 -1
View File
@@ -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
+4
View File
@@ -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
View File
@@ -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 {
+6 -5
View File
@@ -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"`
}
+2 -1
View File
@@ -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"`
+1
View File
@@ -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 {
+20
View File
@@ -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 {
+5 -2
View File
@@ -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
+5 -2
View File
@@ -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
+25 -12
View File
@@ -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]
+4
View File
@@ -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
}
}
+1
View File
@@ -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"`
+1
View File
@@ -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.'
-63
View File
@@ -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
View File
@@ -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コードをスキャンしてください。'
+2
View File
@@ -287,6 +287,8 @@ SystemMode: "모드: "
#ai-tool
ErrOpenrestyInstall: '먼저 Openresty를 설치하세요'
ErrSSL: "인증서 내용이 비어 있습니다. 인증서를 확인하세요!"
#mobile app
ErrVerifyToken: '토큰 검증 오류가 발생했습니다. 재설정 후 다시 스캔해 주세요.'
+2
View File
@@ -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.'
+1
View File
@@ -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.'
+2 -1
View File
@@ -286,7 +286,8 @@ SystemVersion: "версия: "
SystemMode: "режим: "
#ai-tool
"ErrOpenrestyInstall": "Пожалуйста, установите Openresty сначала"
ErrOpenrestyInstall: "Пожалуйста, установите Openresty сначала"
ErrSSL: "Содержимое сертификата пустое, пожалуйста, проверьте сертификат!"
#mobile app
ErrVerifyToken: 'шибка проверки токена, пожалуйста, сбросьте и отсканируйте снова.'
+28 -27
View File
@@ -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
View File
@@ -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: '令牌验证错误,请重置后再次扫码'
+15
View File
@@ -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
+1
View File
@@ -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
},
}
+21 -2
View File
@@ -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) {
+2
View File
@@ -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
}
+20 -9
View File
@@ -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))
}
-1
View File
@@ -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)
+1 -1
View File
@@ -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)
-1
View File
@@ -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)
+1 -1
View File
@@ -47,9 +47,9 @@ func Start() {
session.Init()
gin.SetMode(gin.DebugMode)
cron.Run()
hook.Init()
InitOthers()
business.Init()
hook.Init()
rootRouter := router.Routers()
+9 -6
View File
@@ -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
}
@@ -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,4 +1,4 @@
package schema_v12
package schema
type smi struct {
AttachedGpus string `xml:"attached_gpus"`
+13 -2
View File
@@ -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{
-55
View File
@@ -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)
+10 -1
View File
@@ -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
+13 -2
View File
@@ -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)
}
}()
+8 -2
View File
@@ -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"
+8 -2
View File
@@ -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"
+14 -2
View File
@@ -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
+1
View File
@@ -104,6 +104,7 @@ export namespace AI {
export interface BindDomainRes {
domain: string;
sslID: number;
acmeAccountID: number;
allowIPs: string[];
websiteID?: number;
connUrl: string;
+1
View File
@@ -62,6 +62,7 @@ export namespace Setting {
apiKey: string;
ipWhiteList: string;
apiKeyValidityTime: number;
licenseVerify: string;
}
export interface SettingUpdate {
key: string;
+1 -1
View File
@@ -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,
});
});
};
};
+6 -4
View File
@@ -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 = () => {
+4
View File
@@ -200,6 +200,10 @@ export const DNSTypes = [
label: 'GoDaddy',
value: 'Godaddy',
},
{
label: 'FreeMyIP',
value: 'FreeMyIP',
},
{
label: i18n.global.t('website.rainyun'),
value: 'RainYun',
-2
View File
@@ -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',
+1 -3
View File
@@ -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: '消去',
-2
View File
@@ -123,8 +123,6 @@ const message = {
},
msg: {
noneData: '데이터가 없습니다',
disConn:
'종료 명령어인 {0} 등을 사용하지 않고 직접 연결 끊기 버튼을 클릭하여 터미널 연결을 종료해 주십시오.',
delete: `이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?`,
clean: `이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?`,
deleteTitle: '삭제',
-2
View File
@@ -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',
-2
View File
@@ -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',
+1 -4
View File
@@ -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: 'Удалить',
+21 -22
View File
@@ -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 {
+1 -2
View File
@@ -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 之间',
+1
View File
@@ -37,6 +37,7 @@ export interface GlobalState {
isIntl: boolean;
isTrial: boolean;
productProExpires: number;
licenseVerify: string;
errStatus: string;
}
+1
View File
@@ -40,6 +40,7 @@ const GlobalStore = defineStore({
isIntl: false,
isTrial: false,
productProExpires: 0,
licenseVerify: '',
errStatus: '',
}),
+1 -1
View File
@@ -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;
}
+1 -1
View File
@@ -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) => {
+2 -2
View File
@@ -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>
+2 -4
View File
@@ -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) {}
+4 -4
View File
@@ -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: '',
});
+4 -4
View File
@@ -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);
+1 -1
View File
@@ -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;
}
});
};
+18 -9
View File
@@ -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
+17 -11
View File
@@ -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=