mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-12 08:00:34 +00:00
Compare commits
31
Commits
dev
...
v1.10.19-lts
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
534bd98230 | ||
|
|
1045f2d475 | ||
|
|
c5d007e4dd | ||
|
|
da249a2cef | ||
|
|
f738cd42be | ||
|
|
d7d15479f2 | ||
|
|
d9de82dc04 | ||
|
|
2d0d3704ae | ||
|
|
a3bad3ccf9 | ||
|
|
41471f93f0 | ||
|
|
a817beb029 | ||
|
|
e6322ad76b | ||
|
|
df003c5fb7 | ||
|
|
10f8e8f3c4 | ||
|
|
a750e87940 | ||
|
|
c12f690753 | ||
|
|
0dd51d0c35 | ||
|
|
7f627c041c | ||
|
|
472ca884fd | ||
|
|
236a2dffea | ||
|
|
9be9f1974f | ||
|
|
7961bddf9e | ||
|
|
a05f5c71b9 | ||
|
|
139e77112f | ||
|
|
d4e21ae032 | ||
|
|
104af30cbf | ||
|
|
f68dd7f00b | ||
|
|
220a6fffa0 | ||
|
|
23c4365c0e | ||
|
|
301bc4f6b8 | ||
|
|
ab03b05843 |
@@ -116,7 +116,7 @@ type PortHelper struct {
|
|||||||
|
|
||||||
type ContainerOperation struct {
|
type ContainerOperation struct {
|
||||||
Names []string `json:"names" validate:"required"`
|
Names []string `json:"names" validate:"required"`
|
||||||
Operation string `json:"operation" validate:"required,oneof=start stop restart kill pause unpause remove"`
|
Operation string `json:"operation" validate:"required,oneof=up start stop restart kill pause unpause remove"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContainerRename struct {
|
type ContainerRename struct {
|
||||||
@@ -200,6 +200,7 @@ type ComposeInfo struct {
|
|||||||
Workdir string `json:"workdir"`
|
Workdir string `json:"workdir"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Containers []ComposeContainer `json:"containers"`
|
Containers []ComposeContainer `json:"containers"`
|
||||||
|
Env []string `json:"env"`
|
||||||
}
|
}
|
||||||
type ComposeContainer struct {
|
type ComposeContainer struct {
|
||||||
ContainerID string `json:"containerID"`
|
ContainerID string `json:"containerID"`
|
||||||
@@ -208,22 +209,24 @@ type ComposeContainer struct {
|
|||||||
State string `json:"state"`
|
State string `json:"state"`
|
||||||
}
|
}
|
||||||
type ComposeCreate struct {
|
type ComposeCreate struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
From string `json:"from" validate:"required,oneof=edit path template"`
|
From string `json:"from" validate:"required,oneof=edit path template"`
|
||||||
File string `json:"file"`
|
File string `json:"file"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Template uint `json:"template"`
|
Template uint `json:"template"`
|
||||||
|
Env []string `json:"env"`
|
||||||
}
|
}
|
||||||
type ComposeOperation struct {
|
type ComposeOperation struct {
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
Path string `json:"path" validate:"required"`
|
Path string `json:"path" validate:"required"`
|
||||||
Operation string `json:"operation" validate:"required,oneof=start stop down"`
|
Operation string `json:"operation" validate:"required,oneof=up start stop down delete"`
|
||||||
WithFile bool `json:"withFile"`
|
WithFile bool `json:"withFile"`
|
||||||
}
|
}
|
||||||
type ComposeUpdate struct {
|
type ComposeUpdate struct {
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
Path string `json:"path" validate:"required"`
|
Path string `json:"path" validate:"required"`
|
||||||
Content string `json:"content" validate:"required"`
|
Content string `json:"content" validate:"required"`
|
||||||
|
Env []string `json:"env"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContainerLog struct {
|
type ContainerLog struct {
|
||||||
|
|||||||
@@ -175,21 +175,22 @@ type WebsiteUpdateDirPermission struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WebsiteProxyConfig struct {
|
type WebsiteProxyConfig struct {
|
||||||
ID uint `json:"id" validate:"required"`
|
ID uint `json:"id" validate:"required"`
|
||||||
Operate string `json:"operate" validate:"required"`
|
Operate string `json:"operate" validate:"required"`
|
||||||
Enable bool `json:"enable" `
|
Enable bool `json:"enable" `
|
||||||
Cache bool `json:"cache" `
|
Cache bool `json:"cache" `
|
||||||
CacheTime int `json:"cacheTime" `
|
CacheTime int `json:"cacheTime" `
|
||||||
CacheUnit string `json:"cacheUnit"`
|
CacheUnit string `json:"cacheUnit"`
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
Modifier string `json:"modifier"`
|
Modifier string `json:"modifier"`
|
||||||
Match string `json:"match" validate:"required"`
|
Match string `json:"match" validate:"required"`
|
||||||
ProxyPass string `json:"proxyPass" validate:"required"`
|
ProxyPass string `json:"proxyPass" validate:"required"`
|
||||||
ProxyHost string `json:"proxyHost" validate:"required"`
|
ProxyHost string `json:"proxyHost" validate:"required"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
FilePath string `json:"filePath"`
|
FilePath string `json:"filePath"`
|
||||||
Replaces map[string]string `json:"replaces"`
|
Replaces map[string]string `json:"replaces"`
|
||||||
SNI bool `json:"sni"`
|
SNI bool `json:"sni"`
|
||||||
|
ProxySSLName string `json:"proxySSLName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebsiteProxyReq struct {
|
type WebsiteProxyReq struct {
|
||||||
|
|||||||
@@ -12,4 +12,5 @@ type Compose struct {
|
|||||||
BaseModel
|
BaseModel
|
||||||
|
|
||||||
Name string `gorm:"type:varchar(256)" json:"name"`
|
Name string `gorm:"type:varchar(256)" json:"name"`
|
||||||
|
Path string `gorm:"type:varchar(256)" json:"path"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type IComposeTemplateRepo interface {
|
|||||||
CreateRecord(compose *model.Compose) error
|
CreateRecord(compose *model.Compose) error
|
||||||
DeleteRecord(opts ...DBOption) error
|
DeleteRecord(opts ...DBOption) error
|
||||||
ListRecord() ([]model.Compose, error)
|
ListRecord() ([]model.Compose, error)
|
||||||
|
UpdateRecord(name string, vars map[string]interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIComposeTemplateRepo() IComposeTemplateRepo {
|
func NewIComposeTemplateRepo() IComposeTemplateRepo {
|
||||||
@@ -102,3 +103,7 @@ func (u *ComposeTemplateRepo) DeleteRecord(opts ...DBOption) error {
|
|||||||
}
|
}
|
||||||
return db.Delete(&model.Compose{}).Error
|
return db.Delete(&model.Compose{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *ComposeTemplateRepo) UpdateRecord(name string, vars map[string]interface{}) error {
|
||||||
|
return global.DB.Model(&model.Compose{}).Where("name = ?", name).Updates(vars).Error
|
||||||
|
}
|
||||||
|
|||||||
@@ -898,6 +898,10 @@ func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) s
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(containers) == 0 {
|
||||||
|
composeItem, _ := composeRepo.GetRecord(commonRepo.WithByName(req.Name))
|
||||||
|
filePath = composeItem.Path
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if _, err := os.Stat(filePath); err != nil {
|
if _, err := os.Stat(filePath); err != nil {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -31,6 +32,12 @@ const composeConfigLabel = "com.docker.compose.project.config_files"
|
|||||||
const composeWorkdirLabel = "com.docker.compose.project.working_dir"
|
const composeWorkdirLabel = "com.docker.compose.project.working_dir"
|
||||||
const composeCreatedBy = "createdBy"
|
const composeCreatedBy = "createdBy"
|
||||||
|
|
||||||
|
type DockerCompose struct {
|
||||||
|
Version string `yaml:"version"`
|
||||||
|
Services map[string]map[string]interface{} `yaml:"services"`
|
||||||
|
Networks map[string]interface{} `yaml:"networks"`
|
||||||
|
}
|
||||||
|
|
||||||
func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface{}, error) {
|
func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface{}, error) {
|
||||||
var (
|
var (
|
||||||
records []dto.ComposeInfo
|
records []dto.ComposeInfo
|
||||||
@@ -52,6 +59,20 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
|||||||
}
|
}
|
||||||
|
|
||||||
composeCreatedByLocal, _ := composeRepo.ListRecord()
|
composeCreatedByLocal, _ := composeRepo.ListRecord()
|
||||||
|
|
||||||
|
composeLocalMap := make(map[string]dto.ComposeInfo)
|
||||||
|
for _, localItem := range composeCreatedByLocal {
|
||||||
|
composeItemLocal := dto.ComposeInfo{
|
||||||
|
ContainerNumber: 0,
|
||||||
|
CreatedAt: localItem.CreatedAt.Format(constant.DateTimeLayout),
|
||||||
|
ConfigFile: localItem.Path,
|
||||||
|
Workdir: strings.TrimSuffix(localItem.Path, "/docker-compose.yml"),
|
||||||
|
}
|
||||||
|
composeItemLocal.CreatedBy = "1Panel"
|
||||||
|
composeItemLocal.Path = localItem.Path
|
||||||
|
composeLocalMap[localItem.Name] = composeItemLocal
|
||||||
|
}
|
||||||
|
|
||||||
composeMap := make(map[string]dto.ComposeInfo)
|
composeMap := make(map[string]dto.ComposeInfo)
|
||||||
for _, container := range list {
|
for _, container := range list {
|
||||||
if name, ok := container.Labels[composeProjectLabel]; ok {
|
if name, ok := container.Labels[composeProjectLabel]; ok {
|
||||||
@@ -95,12 +116,24 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, item := range composeCreatedByLocal {
|
|
||||||
if err := composeRepo.DeleteRecord(commonRepo.WithByID(item.ID)); err != nil {
|
mergedMap := make(map[string]dto.ComposeInfo)
|
||||||
global.LOG.Error(err)
|
for key, localItem := range composeLocalMap {
|
||||||
|
mergedMap[key] = localItem
|
||||||
|
}
|
||||||
|
for key, item := range composeMap {
|
||||||
|
if existingItem, exists := mergedMap[key]; exists {
|
||||||
|
if item.ContainerNumber > 0 {
|
||||||
|
if existingItem.ContainerNumber <= 0 {
|
||||||
|
mergedMap[key] = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mergedMap[key] = item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for key, value := range composeMap {
|
|
||||||
|
for key, value := range mergedMap {
|
||||||
value.Name = key
|
value.Name = key
|
||||||
records = append(records, value)
|
records = append(records, value)
|
||||||
}
|
}
|
||||||
@@ -127,7 +160,26 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
|||||||
}
|
}
|
||||||
BackDatas = records[start:end]
|
BackDatas = records[start:end]
|
||||||
}
|
}
|
||||||
return int64(total), BackDatas, nil
|
listItem := loadEnv(BackDatas)
|
||||||
|
return int64(total), listItem, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadEnv(list []dto.ComposeInfo) []dto.ComposeInfo {
|
||||||
|
for i := 0; i < len(list); i++ {
|
||||||
|
envFilePath := filepath.Join(path.Dir(list[i].Path), "1panel.env")
|
||||||
|
file, err := os.ReadFile(envFilePath)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lines := strings.Split(string(file), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
lineItem := strings.TrimSpace(line)
|
||||||
|
if len(lineItem) != 0 && !strings.HasPrefix(lineItem, "#") {
|
||||||
|
list[i].Env = append(list[i].Env, lineItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
|
func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
|
||||||
@@ -141,6 +193,9 @@ func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
|
|||||||
if err := u.loadPath(&req); err != nil {
|
if err := u.loadPath(&req); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
if err := newComposeEnv(req.Path, req.Env); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
cmd := exec.Command("docker-compose", "-f", req.Path, "config")
|
cmd := exec.Command("docker-compose", "-f", req.Path, "config")
|
||||||
stdout, err := cmd.CombinedOutput()
|
stdout, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -173,6 +228,9 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
if err := newComposeEnv(req.Path, req.Env); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
go func() {
|
go func() {
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
cmd := exec.Command("docker-compose", "-f", req.Path, "up", "-d")
|
cmd := exec.Command("docker-compose", "-f", req.Path, "up", "-d")
|
||||||
@@ -186,7 +244,7 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
global.LOG.Infof("docker-compose up %s successful!", req.Name)
|
global.LOG.Infof("docker-compose up %s successful!", req.Name)
|
||||||
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name})
|
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name, Path: req.Path})
|
||||||
_, _ = file.WriteString("docker-compose up successful!")
|
_, _ = file.WriteString("docker-compose up successful!")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -200,17 +258,26 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
|
|||||||
if _, err := os.Stat(req.Path); err != nil {
|
if _, err := os.Stat(req.Path); err != nil {
|
||||||
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
|
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
|
||||||
}
|
}
|
||||||
if stdout, err := compose.Operate(req.Path, req.Operation); err != nil {
|
if req.Operation == "delete" {
|
||||||
return errors.New(string(stdout))
|
if stdout, err := compose.Operate(req.Path, "down"); err != nil {
|
||||||
}
|
return errors.New(string(stdout))
|
||||||
global.LOG.Infof("docker-compose %s %s successful", req.Operation, req.Name)
|
}
|
||||||
if req.Operation == "down" {
|
|
||||||
_ = composeRepo.DeleteRecord(commonRepo.WithByName(req.Name))
|
|
||||||
if req.WithFile {
|
if req.WithFile {
|
||||||
_ = os.RemoveAll(path.Dir(req.Path))
|
_ = os.RemoveAll(path.Dir(req.Path))
|
||||||
}
|
}
|
||||||
|
_ = composeRepo.DeleteRecord(commonRepo.WithByName(req.Name))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
if req.Operation == "up" {
|
||||||
|
if stdout, err := compose.Up(req.Path); err != nil {
|
||||||
|
return errors.New(string(stdout))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if stdout, err := compose.Operate(req.Path, req.Operation); err != nil {
|
||||||
|
return errors.New(string(stdout))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
global.LOG.Infof("docker-compose %s %s successful", req.Operation, req.Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,6 +299,10 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
|
|||||||
write.Flush()
|
write.Flush()
|
||||||
|
|
||||||
global.LOG.Infof("docker-compose.yml %s has been replaced, now start to docker-compose restart", req.Path)
|
global.LOG.Infof("docker-compose.yml %s has been replaced, now start to docker-compose restart", req.Path)
|
||||||
|
if err := newComposeEnv(req.Path, req.Env); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if stdout, err := compose.Up(req.Path); err != nil {
|
if stdout, err := compose.Up(req.Path); err != nil {
|
||||||
if err := recreateCompose(string(oldFile), req.Path); err != nil {
|
if err := recreateCompose(string(oldFile), req.Path); err != nil {
|
||||||
return fmt.Errorf("update failed when handle compose up, err: %s, recreate failed: %v", string(stdout), err)
|
return fmt.Errorf("update failed when handle compose up, err: %s, recreate failed: %v", string(stdout), err)
|
||||||
@@ -280,3 +351,26 @@ func recreateCompose(content, path string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newComposeEnv(pathItem string, env []string) error {
|
||||||
|
if len(env) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
envFilePath := path.Join(path.Dir(pathItem), "1panel.env")
|
||||||
|
|
||||||
|
file, err := os.OpenFile(envFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("failed to create env file: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
for _, env := range env {
|
||||||
|
envItem := strings.TrimSpace(env)
|
||||||
|
if _, err := file.WriteString(fmt.Sprintf("%s\n", envItem)); err != nil {
|
||||||
|
global.LOG.Errorf("failed to write env to file: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
global.LOG.Infof("1panel.env file successfully created or updated with env variables in %s", envFilePath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,10 +3,6 @@ package service
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
|
||||||
"github.com/shirou/gopsutil/v3/load"
|
|
||||||
"github.com/shirou/gopsutil/v3/mem"
|
|
||||||
"github.com/shirou/gopsutil/v3/net"
|
|
||||||
network "net"
|
network "net"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -14,6 +10,11 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
|
"github.com/shirou/gopsutil/v3/load"
|
||||||
|
"github.com/shirou/gopsutil/v3/mem"
|
||||||
|
"github.com/shirou/gopsutil/v3/net"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||||
@@ -268,13 +269,13 @@ func loadDiskInfo() []dto.DiskInfo {
|
|||||||
if strings.HasPrefix(fields[6], "/snap") || len(strings.Split(fields[6], "/")) > 10 {
|
if strings.HasPrefix(fields[6], "/snap") || len(strings.Split(fields[6], "/")) > 10 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(fields[1]) == "tmpfs" {
|
if strings.TrimSpace(fields[1]) == "tmpfs" || strings.TrimSpace(fields[1]) == "overlay" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.Contains(fields[2], "K") {
|
if strings.Contains(fields[2], "K") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.Contains(fields[6], "docker") {
|
if strings.Contains(fields[6], "docker") || strings.Contains(fields[6], "podman") || strings.Contains(fields[6], "containerd") || strings.HasPrefix(fields[6], "/var/lib/containers") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
isExclude := false
|
isExclude := false
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ import (
|
|||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -336,8 +338,15 @@ func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {
|
|||||||
|
|
||||||
func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
|
func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
|
||||||
service := "docker"
|
service := "docker"
|
||||||
|
sudo := cmd.SudoHandleCmd()
|
||||||
if req.Operation == "stop" {
|
if req.Operation == "stop" {
|
||||||
service = "docker.socket"
|
isSocketActive, _ := systemctl.IsActive("docker.socket")
|
||||||
|
if isSocketActive {
|
||||||
|
std, err := cmd.Execf("%s systemctl stop docker.socket", sudo)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("handle systemctl stop docker.socket failed, err: %v", std)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
stdout, err := cmd.Execf("systemctl %s %s ", req.Operation, service)
|
stdout, err := cmd.Execf("systemctl %s %s ", req.Operation, service)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -124,6 +124,16 @@ func (u *SSHService) OperateSSH(operation string) error {
|
|||||||
if operation == "enable" || operation == "disable" {
|
if operation == "enable" || operation == "disable" {
|
||||||
serviceName += ".service"
|
serviceName += ".service"
|
||||||
}
|
}
|
||||||
|
if operation == "stop" {
|
||||||
|
isSocketActive, _ := systemctl.IsActive(serviceName + ".socket")
|
||||||
|
if isSocketActive {
|
||||||
|
std, err := cmd.Execf("%s systemctl stop %s", sudo, serviceName+".socket")
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("handle systemctl stop %s.socket failed, err: %v", serviceName, std)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
stdout, err := cmd.Execf("%s systemctl %s %s", sudo, operation, serviceName)
|
stdout, err := cmd.Execf("%s systemctl %s %s", sudo, operation, serviceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(stdout, "alias name or linked unit file") {
|
if strings.Contains(stdout, "alias name or linked unit file") {
|
||||||
|
|||||||
@@ -1667,6 +1667,9 @@ func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error)
|
|||||||
}
|
}
|
||||||
if req.SNI {
|
if req.SNI {
|
||||||
location.UpdateDirective("proxy_ssl_server_name", []string{"on"})
|
location.UpdateDirective("proxy_ssl_server_name", []string{"on"})
|
||||||
|
if req.ProxySSLName != "" {
|
||||||
|
location.UpdateDirective("proxy_ssl_name", []string{req.ProxySSLName})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
location.UpdateDirective("proxy_ssl_server_name", []string{"off"})
|
location.UpdateDirective("proxy_ssl_server_name", []string{"off"})
|
||||||
}
|
}
|
||||||
@@ -1749,6 +1752,9 @@ func (w WebsiteService) GetProxies(id uint) (res []request.WebsiteProxyConfig, e
|
|||||||
if directive.GetName() == "proxy_ssl_server_name" {
|
if directive.GetName() == "proxy_ssl_server_name" {
|
||||||
proxyConfig.SNI = directive.GetParameters()[0] == "on"
|
proxyConfig.SNI = directive.GetParameters()[0] == "on"
|
||||||
}
|
}
|
||||||
|
if directive.GetName() == "proxy_ssl_name" {
|
||||||
|
proxyConfig.ProxySSLName = directive.GetParameters()[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
res = append(res, proxyConfig)
|
res = append(res, proxyConfig)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,15 +217,14 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) (*model.Website
|
|||||||
if req.Domains != "" {
|
if req.Domains != "" {
|
||||||
domainArray := strings.Split(req.Domains, "\n")
|
domainArray := strings.Split(req.Domains, "\n")
|
||||||
for _, domain := range domainArray {
|
for _, domain := range domainArray {
|
||||||
if !common.IsValidDomain(domain) {
|
if ipAddress := net.ParseIP(domain); ipAddress == nil {
|
||||||
err = buserr.WithName("ErrDomainFormat", domain)
|
if !common.IsValidDomain(domain) {
|
||||||
return nil, err
|
err = buserr.WithName("ErrDomainFormat", domain)
|
||||||
} else {
|
return nil, err
|
||||||
if ipAddress := net.ParseIP(domain); ipAddress == nil {
|
|
||||||
domains = append(domains, domain)
|
|
||||||
} else {
|
|
||||||
ips = append(ips, ipAddress)
|
|
||||||
}
|
}
|
||||||
|
domains = append(domains, domain)
|
||||||
|
} else {
|
||||||
|
ips = append(ips, ipAddress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(domains) > 0 {
|
if len(domains) > 0 {
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ func Init() {
|
|||||||
migrations.AddClam,
|
migrations.AddClam,
|
||||||
migrations.AddClamStatus,
|
migrations.AddClamStatus,
|
||||||
migrations.AddAlertMenu,
|
migrations.AddAlertMenu,
|
||||||
|
migrations.AddComposeColumn,
|
||||||
})
|
})
|
||||||
if err := m.Migrate(); err != nil {
|
if err := m.Migrate(); err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
|
|||||||
@@ -314,3 +314,13 @@ var AddAlertMenu = &gormigrate.Migration{
|
|||||||
return tx.Model(&model.Setting{}).Where("key", "XpackHideMenu").Updates(map[string]interface{}{"value": string(data)}).Error
|
return tx.Model(&model.Setting{}).Where("key", "XpackHideMenu").Updates(map[string]interface{}{"value": string(data)}).Error
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var AddComposeColumn = &gormigrate.Migration{
|
||||||
|
ID: "20240906-add-compose-command",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
if err := tx.AutoMigrate(&model.Compose{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ var whiteUrlList = map[string]struct{}{
|
|||||||
"/api/v1/logs/operation": {},
|
"/api/v1/logs/operation": {},
|
||||||
"/api/v1/logs/login": {},
|
"/api/v1/logs/login": {},
|
||||||
"/api/v1/auth/logout": {},
|
"/api/v1/auth/logout": {},
|
||||||
|
"/api/v1/dashboard/current": {},
|
||||||
|
|
||||||
"/api/v1/apps/installed/loadport": {},
|
"/api/v1/apps/installed/loadport": {},
|
||||||
"/api/v1/apps/installed/check": {},
|
"/api/v1/apps/installed/check": {},
|
||||||
|
|||||||
@@ -27,12 +27,16 @@ func NewS3Client(vars map[string]interface{}) (*s3Client, error) {
|
|||||||
if len(scType) == 0 {
|
if len(scType) == 0 {
|
||||||
scType = "Standard"
|
scType = "Standard"
|
||||||
}
|
}
|
||||||
|
mode := loadParamFromVars("mode", vars)
|
||||||
|
if len(mode) == 0 {
|
||||||
|
mode = "virtual hosted"
|
||||||
|
}
|
||||||
sess, err := session.NewSession(&aws.Config{
|
sess, err := session.NewSession(&aws.Config{
|
||||||
Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""),
|
Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""),
|
||||||
Endpoint: aws.String(endpoint),
|
Endpoint: aws.String(endpoint),
|
||||||
Region: aws.String(region),
|
Region: aws.String(region),
|
||||||
DisableSSL: aws.Bool(true),
|
DisableSSL: aws.Bool(true),
|
||||||
S3ForcePathStyle: aws.Bool(false),
|
S3ForcePathStyle: aws.Bool(mode == "path"),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -29,10 +29,17 @@ type FirewallClient interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewFirewallClient() (FirewallClient, error) {
|
func NewFirewallClient() (FirewallClient, error) {
|
||||||
if _, err := os.Stat("/usr/sbin/firewalld"); err == nil {
|
_, firewalldErr := os.Stat("/usr/sbin/firewalld")
|
||||||
|
_, ufwErr := os.Stat("/usr/sbin/ufw")
|
||||||
|
|
||||||
|
if firewalldErr == nil && ufwErr == nil {
|
||||||
|
return nil, buserr.New("firewalld and ufw both found, only one firewall should be active")
|
||||||
|
}
|
||||||
|
|
||||||
|
if firewalldErr == nil {
|
||||||
return client.NewFirewalld()
|
return client.NewFirewalld()
|
||||||
}
|
}
|
||||||
if _, err := os.Stat("/usr/sbin/ufw"); err == nil {
|
if ufwErr == nil {
|
||||||
return client.NewUfw()
|
return client.NewUfw()
|
||||||
}
|
}
|
||||||
return nil, buserr.New(constant.ErrFirewall)
|
return nil, buserr.New(constant.ErrFirewall)
|
||||||
|
|||||||
@@ -234,11 +234,13 @@ func (l *Location) AddSubFilter(subFilters map[string]string) {
|
|||||||
}
|
}
|
||||||
l.UpdateDirective("proxy_set_header", []string{"Accept-Encoding", `""`})
|
l.UpdateDirective("proxy_set_header", []string{"Accept-Encoding", `""`})
|
||||||
l.UpdateDirective("sub_filter_once", []string{"off"})
|
l.UpdateDirective("sub_filter_once", []string{"off"})
|
||||||
|
l.UpdateDirective("sub_filter_types", []string{"*"})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Location) RemoveSubFilter() {
|
func (l *Location) RemoveSubFilter() {
|
||||||
l.RemoveDirective("sub_filter", []string{})
|
l.RemoveDirective("sub_filter", []string{})
|
||||||
l.RemoveDirective("proxy_set_header", []string{"Accept-Encoding", `""`})
|
l.RemoveDirective("proxy_set_header", []string{"Accept-Encoding", `""`})
|
||||||
l.RemoveDirective("sub_filter_once", []string{"off"})
|
l.RemoveDirective("sub_filter_once", []string{"off"})
|
||||||
|
l.RemoveDirective("sub_filter_types", []string{"*"})
|
||||||
l.Replaces = nil
|
l.Replaces = nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ location ^~ /test {
|
|||||||
add_header X-Cache $upstream_cache_status;
|
add_header X-Cache $upstream_cache_status;
|
||||||
add_header Cache-Control no-cache;
|
add_header Cache-Control no-cache;
|
||||||
proxy_ssl_server_name off;
|
proxy_ssl_server_name off;
|
||||||
|
proxy_ssl_name $proxy_host;
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 6.1 KiB |
@@ -41,7 +41,7 @@
|
|||||||
"highlight.js": "^11.9.0",
|
"highlight.js": "^11.9.0",
|
||||||
"js-base64": "^3.7.7",
|
"js-base64": "^3.7.7",
|
||||||
"md-editor-v3": "^2.11.3",
|
"md-editor-v3": "^2.11.3",
|
||||||
"monaco-editor": "^0.51.0",
|
"monaco-editor": "^0.50.0",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"pinia-plugin-persistedstate": "^1.6.1",
|
"pinia-plugin-persistedstate": "^1.6.1",
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 6.1 KiB |
@@ -241,6 +241,8 @@ export namespace Container {
|
|||||||
path: string;
|
path: string;
|
||||||
containers: Array<ComposeContainer>;
|
containers: Array<ComposeContainer>;
|
||||||
expand: boolean;
|
expand: boolean;
|
||||||
|
envStr: string;
|
||||||
|
env: Array<string>;
|
||||||
}
|
}
|
||||||
export interface ComposeContainer {
|
export interface ComposeContainer {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -254,6 +256,8 @@ export namespace Container {
|
|||||||
file: string;
|
file: string;
|
||||||
path: string;
|
path: string;
|
||||||
template: number;
|
template: number;
|
||||||
|
env: Array<string>;
|
||||||
|
envStr: string;
|
||||||
}
|
}
|
||||||
export interface ComposeOperation {
|
export interface ComposeOperation {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -265,6 +269,8 @@ export namespace Container {
|
|||||||
name: string;
|
name: string;
|
||||||
path: string;
|
path: string;
|
||||||
content: string;
|
content: string;
|
||||||
|
env: Array<string>;
|
||||||
|
createdBy: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TemplateCreate {
|
export interface TemplateCreate {
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ export namespace Setting {
|
|||||||
}
|
}
|
||||||
export interface LicenseStatus {
|
export interface LicenseStatus {
|
||||||
productPro: string;
|
productPro: string;
|
||||||
|
trial: boolean;
|
||||||
status: string;
|
status: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -373,6 +373,8 @@ export namespace Website {
|
|||||||
content?: string;
|
content?: string;
|
||||||
proxyAddress?: string;
|
proxyAddress?: string;
|
||||||
proxyProtocol?: string;
|
proxyProtocol?: string;
|
||||||
|
sni: boolean;
|
||||||
|
proxySSLName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProxReplace {
|
export interface ProxReplace {
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 6.1 KiB |
@@ -1,16 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<body>
|
<div>
|
||||||
<div>
|
<el-row type="flex" justify="center">
|
||||||
<el-row type="flex" justify="center">
|
<h1>{{ loadErrInfo() }}</h1>
|
||||||
<h1>{{ loadErrInfo() }}</h1>
|
</el-row>
|
||||||
</el-row>
|
</div>
|
||||||
</div>
|
<hr />
|
||||||
<hr />
|
<div>
|
||||||
<div>
|
<el-row type="flex" justify="center"><span>nginx</span></el-row>
|
||||||
<el-row type="flex" justify="center"><span>nginx</span></el-row>
|
</div>
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,9 @@ const showBack = computed(() => {
|
|||||||
.el-button + .el-button {
|
.el-button + .el-button {
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
|
.el-button-group .el-button {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-container_form {
|
.content-container_form {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-loading="isLoading">
|
<div v-loading="firstLoading">
|
||||||
<div v-if="defaultButton">
|
<div v-if="defaultButton">
|
||||||
<el-checkbox border v-model="tailLog" class="float-left" @change="changeTail(false)">
|
<el-checkbox border v-model="tailLog" class="float-left" @change="changeTail(false)">
|
||||||
{{ $t('commons.button.watch') }}
|
{{ $t('commons.button.watch') }}
|
||||||
@@ -78,6 +78,7 @@ const minPage = ref(1);
|
|||||||
const maxPage = ref(1);
|
const maxPage = ref(1);
|
||||||
const logs = ref([]);
|
const logs = ref([]);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
const firstLoading = ref(true);
|
||||||
|
|
||||||
const readReq = reactive({
|
const readReq = reactive({
|
||||||
id: 0,
|
id: 0,
|
||||||
@@ -129,6 +130,7 @@ const getContent = (pre: boolean) => {
|
|||||||
}
|
}
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
ReadByLine(readReq).then((res) => {
|
ReadByLine(readReq).then((res) => {
|
||||||
|
firstLoading.value = false;
|
||||||
if (!end.value && res.data.end) {
|
if (!end.value && res.data.end) {
|
||||||
lastLogs.value = [...logs.value];
|
lastLogs.value = [...logs.value];
|
||||||
}
|
}
|
||||||
@@ -193,7 +195,7 @@ const getContent = (pre: boolean) => {
|
|||||||
maxPage.value = res.data.total;
|
maxPage.value = res.data.total;
|
||||||
minPage.value = res.data.total;
|
minPage.value = res.data.total;
|
||||||
}
|
}
|
||||||
if (logs.value && logs.value.length > 3000) {
|
if (logs.value && logs.value.length > 1000) {
|
||||||
logs.value.splice(0, readReq.pageSize);
|
logs.value.splice(0, readReq.pageSize);
|
||||||
if (minPage.value > 1) {
|
if (minPage.value > 1) {
|
||||||
minPage.value--;
|
minPage.value--;
|
||||||
@@ -278,8 +280,6 @@ const init = () => {
|
|||||||
}
|
}
|
||||||
readReq.latest = true;
|
readReq.latest = true;
|
||||||
search();
|
search();
|
||||||
|
|
||||||
nextTick(() => {});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearLog = (): void => {
|
const clearLog = (): void => {
|
||||||
|
|||||||
@@ -211,8 +211,8 @@ const onRecover = async (row: File.File) => {
|
|||||||
currentRow.value = row;
|
currentRow.value = row;
|
||||||
if (type.value !== 'app' && type.value !== 'website') {
|
if (type.value !== 'app' && type.value !== 'website') {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(
|
||||||
i18n.global.t('commons.msg.backupHelper', [name.value + '( ' + detailName.value + ' )']),
|
i18n.global.t('commons.msg.recoverHelper', [row.name]),
|
||||||
i18n.global.t('commons.button.backup'),
|
i18n.global.t('commons.button.recover'),
|
||||||
{
|
{
|
||||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||||
|
|||||||
@@ -654,6 +654,8 @@ const message = {
|
|||||||
privileged: 'Privileged',
|
privileged: 'Privileged',
|
||||||
privilegedHelper:
|
privilegedHelper:
|
||||||
'Allows the container to perform certain privileged operations on the host, which may increase container risks. Use with caution!',
|
'Allows the container to perform certain privileged operations on the host, which may increase container risks. Use with caution!',
|
||||||
|
editComposeHelper:
|
||||||
|
'Note: The environment variables set will be written to the 1panel.env file by default.\nIf you want to use these parameters in the container, you also need to manually add an env_file reference in the compose file.',
|
||||||
|
|
||||||
upgradeHelper: 'Repository Name/Image Name: Image Version',
|
upgradeHelper: 'Repository Name/Image Name: Image Version',
|
||||||
upgradeWarning2:
|
upgradeWarning2:
|
||||||
@@ -768,8 +770,9 @@ const message = {
|
|||||||
composeHelper:
|
composeHelper:
|
||||||
'The composition created through 1Panel editor or template will be saved in the {0}/docker/compose directory.',
|
'The composition created through 1Panel editor or template will be saved in the {0}/docker/compose directory.',
|
||||||
deleteFile: 'Delete file',
|
deleteFile: 'Delete file',
|
||||||
|
allDelete: 'Permanently Delete',
|
||||||
deleteComposeHelper:
|
deleteComposeHelper:
|
||||||
'Delete all files in the {0} directory, including persistent files in this directory. Please proceed with caution!',
|
'Delete all files related to container compose, including configuration files and persistent files. Please proceed with caution!',
|
||||||
deleteCompose: '" Delete this composition.',
|
deleteCompose: '" Delete this composition.',
|
||||||
apps: 'Apps',
|
apps: 'Apps',
|
||||||
local: 'Local',
|
local: 'Local',
|
||||||
@@ -781,11 +784,16 @@ const message = {
|
|||||||
content: 'Content',
|
content: 'Content',
|
||||||
contentEmpty: 'Compose content cannot be empty, please enter and try again!',
|
contentEmpty: 'Compose content cannot be empty, please enter and try again!',
|
||||||
containerNumber: 'Container number',
|
containerNumber: 'Container number',
|
||||||
|
containerStatus: 'Container Status',
|
||||||
|
exited: 'Exited',
|
||||||
|
running: 'Running',
|
||||||
down: 'Down',
|
down: 'Down',
|
||||||
up: 'Up',
|
up: 'Up',
|
||||||
composeDetailHelper:
|
composeDetailHelper:
|
||||||
'The compose is created external to 1Panel. The start and stop operations are not supported.',
|
'The compose is created external to 1Panel. The start and stop operations are not supported.',
|
||||||
composeOperatorHelper: '{1} operation will be performed on {0}. Do you want to continue?',
|
composeOperatorHelper: '{1} operation will be performed on {0}. Do you want to continue?',
|
||||||
|
composeDownHelper:
|
||||||
|
'This will stop and remove all containers and networks under the {0} compose. Do you want to continue?',
|
||||||
|
|
||||||
setting: 'Setting',
|
setting: 'Setting',
|
||||||
operatorStatusHelper: 'This action will {0} Docker service, do you want to continue?',
|
operatorStatusHelper: 'This action will {0} Docker service, do you want to continue?',
|
||||||
@@ -1410,6 +1418,7 @@ const message = {
|
|||||||
LOCAL: 'Server Disks',
|
LOCAL: 'Server Disks',
|
||||||
OSS: 'Ali OSS',
|
OSS: 'Ali OSS',
|
||||||
S3: 'Amazon S3',
|
S3: 'Amazon S3',
|
||||||
|
mode: 'Mode',
|
||||||
MINIO: 'MINIO',
|
MINIO: 'MINIO',
|
||||||
SFTP: 'SFTP',
|
SFTP: 'SFTP',
|
||||||
WebDAV: 'WebDAV',
|
WebDAV: 'WebDAV',
|
||||||
|
|||||||
@@ -629,6 +629,8 @@ const message = {
|
|||||||
emptyUser: '為空時,將使用容器默認的用戶登錄',
|
emptyUser: '為空時,將使用容器默認的用戶登錄',
|
||||||
privileged: '特權模式',
|
privileged: '特權模式',
|
||||||
privilegedHelper: '允許容器在主機上執行某些特權操作,可能會增加容器風險,請謹慎開啟!',
|
privilegedHelper: '允許容器在主機上執行某些特權操作,可能會增加容器風險,請謹慎開啟!',
|
||||||
|
editComposeHelper:
|
||||||
|
'注意:設置的環境變數會默認寫入 1panel.env 文件。\n若需在容器中使用這些參數,還需在 compose 文件中手動添加 env_file 引用。',
|
||||||
|
|
||||||
upgradeHelper: '倉庫名稱/鏡像名稱:鏡像版本',
|
upgradeHelper: '倉庫名稱/鏡像名稱:鏡像版本',
|
||||||
upgradeWarning2: '升級操作需要重建容器,任何未持久化的數據將會丟失,是否繼續?',
|
upgradeWarning2: '升級操作需要重建容器,任何未持久化的數據將會丟失,是否繼續?',
|
||||||
@@ -739,7 +741,8 @@ const message = {
|
|||||||
composePathHelper: '配置文件保存路徑: {0}',
|
composePathHelper: '配置文件保存路徑: {0}',
|
||||||
composeHelper: '通過 1Panel 編輯或者模版創建的編排,將保存在 {0}/docker/compose 路徑下',
|
composeHelper: '通過 1Panel 編輯或者模版創建的編排,將保存在 {0}/docker/compose 路徑下',
|
||||||
deleteFile: '刪除文件',
|
deleteFile: '刪除文件',
|
||||||
deleteComposeHelper: '刪除 {0} 目录下所有文件,包括該文件下的持久化文件等,請謹慎操作!',
|
allDelete: '徹底刪除',
|
||||||
|
deleteComposeHelper: '刪除容器編排的所有文件,包括配置文件和持久化文件,請謹慎操作!',
|
||||||
deleteCompose: '" 刪除此編排',
|
deleteCompose: '" 刪除此編排',
|
||||||
apps: '應用商店',
|
apps: '應用商店',
|
||||||
local: '本地',
|
local: '本地',
|
||||||
@@ -751,10 +754,14 @@ const message = {
|
|||||||
content: '內容',
|
content: '內容',
|
||||||
contentEmpty: '編排內容不能為空,請輸入後重試!',
|
contentEmpty: '編排內容不能為空,請輸入後重試!',
|
||||||
containerNumber: '容器數量',
|
containerNumber: '容器數量',
|
||||||
|
containerStatus: '容器狀態',
|
||||||
|
exited: '已停止',
|
||||||
|
running: '運行中',
|
||||||
down: '刪除',
|
down: '刪除',
|
||||||
up: '啟動',
|
up: '啟動',
|
||||||
composeDetailHelper: '該 compose 為 1Panel 編排外部創建。暫不支持啟停操作。',
|
composeDetailHelper: '該 compose 為 1Panel 編排外部創建。暫不支持啟停操作。',
|
||||||
composeOperatorHelper: '將對 {0} 進行 {1} 操作,是否繼續?',
|
composeOperatorHelper: '將對 {0} 進行 {1} 操作,是否繼續?',
|
||||||
|
composeDownHelper: '將停止並刪除 {0} 編排下所有容器及網絡,是否繼續?',
|
||||||
|
|
||||||
setting: '配置',
|
setting: '配置',
|
||||||
operatorStatusHelper: '此操作將{0}Docker 服務,是否繼續?',
|
operatorStatusHelper: '此操作將{0}Docker 服務,是否繼續?',
|
||||||
@@ -1323,6 +1330,7 @@ const message = {
|
|||||||
LOCAL: '服務器磁盤',
|
LOCAL: '服務器磁盤',
|
||||||
OSS: '阿裏雲 OSS',
|
OSS: '阿裏雲 OSS',
|
||||||
S3: '亞馬遜 S3 雲存儲',
|
S3: '亞馬遜 S3 雲存儲',
|
||||||
|
mode: '模式',
|
||||||
MINIO: 'MINIO',
|
MINIO: 'MINIO',
|
||||||
SFTP: 'SFTP',
|
SFTP: 'SFTP',
|
||||||
WebDAV: 'WebDAV',
|
WebDAV: 'WebDAV',
|
||||||
|
|||||||
@@ -632,7 +632,8 @@ const message = {
|
|||||||
emptyUser: '为空时,将使用容器默认的用户登录',
|
emptyUser: '为空时,将使用容器默认的用户登录',
|
||||||
privileged: '特权模式',
|
privileged: '特权模式',
|
||||||
privilegedHelper: '允许容器在主机上执行某些特权操作,可能会增加容器风险,谨慎开启!',
|
privilegedHelper: '允许容器在主机上执行某些特权操作,可能会增加容器风险,谨慎开启!',
|
||||||
|
editComposeHelper:
|
||||||
|
'注意:设置的环境变量会默认写入 1panel.env 文件。\n如需在容器中使用这些参数,还需在 compose 文件中手动添加 env_file 引用。',
|
||||||
upgradeHelper: '仓库名称/镜像名称:镜像版本',
|
upgradeHelper: '仓库名称/镜像名称:镜像版本',
|
||||||
upgradeWarning2: '升级操作需要重建容器,任何未持久化的数据将会丢失,是否继续?',
|
upgradeWarning2: '升级操作需要重建容器,任何未持久化的数据将会丢失,是否继续?',
|
||||||
oldImage: '当前镜像',
|
oldImage: '当前镜像',
|
||||||
@@ -736,13 +737,13 @@ const message = {
|
|||||||
mirrorHelper:
|
mirrorHelper:
|
||||||
'当存在多个加速器时,需要换行显示,例: \nhttp://xxxxxx.m.daocloud.io \nhttps://xxxxxx.mirror.aliyuncs.com',
|
'当存在多个加速器时,需要换行显示,例: \nhttp://xxxxxx.m.daocloud.io \nhttps://xxxxxx.mirror.aliyuncs.com',
|
||||||
registrieHelper: '当存在多个私有仓库时,需要换行显示,例:\n172.16.10.111:8081 \n172.16.10.112:8081',
|
registrieHelper: '当存在多个私有仓库时,需要换行显示,例:\n172.16.10.111:8081 \n172.16.10.112:8081',
|
||||||
|
|
||||||
compose: '编排',
|
compose: '编排',
|
||||||
fromChangeHelper: '切换来源将清空当前已编辑内容,是否继续?',
|
fromChangeHelper: '切换来源将清空当前已编辑内容,是否继续?',
|
||||||
composePathHelper: '配置文件保存路径: {0}',
|
composePathHelper: '配置文件保存路径: {0}',
|
||||||
composeHelper: '通过 1Panel 编辑或者模版创建的编排,将保存在 {0}/docker/compose 路径下',
|
composeHelper: '通过 1Panel 编辑或者模版创建的编排,将保存在 {0}/docker/compose 路径下',
|
||||||
deleteFile: '删除文件',
|
deleteFile: '删除文件',
|
||||||
deleteComposeHelper: '删除 {0} 目录下所有文件,包括该文件下的持久化文件等,请谨慎操作!',
|
allDelete: '彻底删除',
|
||||||
|
deleteComposeHelper: '删除容器编排的所有文件,包括配置文件和持久化文件,请谨慎操作!',
|
||||||
deleteCompose: '" 删除此编排',
|
deleteCompose: '" 删除此编排',
|
||||||
apps: '应用商店',
|
apps: '应用商店',
|
||||||
local: '本地',
|
local: '本地',
|
||||||
@@ -754,10 +755,14 @@ const message = {
|
|||||||
content: '内容',
|
content: '内容',
|
||||||
contentEmpty: '编排内容不能为空,请输入后重试!',
|
contentEmpty: '编排内容不能为空,请输入后重试!',
|
||||||
containerNumber: '容器数量',
|
containerNumber: '容器数量',
|
||||||
|
containerStatus: '容器状态',
|
||||||
|
exited: '已停止',
|
||||||
|
running: '运行中',
|
||||||
down: '删除',
|
down: '删除',
|
||||||
up: '启动',
|
up: '启动',
|
||||||
composeDetailHelper: '该 compose 为 1Panel 编排外部创建。暂不支持启停操作。',
|
composeDetailHelper: '该 compose 为 1Panel 编排外部创建。暂不支持启停操作。',
|
||||||
composeOperatorHelper: '将对 {0} 进行 {1} 操作,是否继续?',
|
composeOperatorHelper: '将对 {0} 进行 {1} 操作,是否继续?',
|
||||||
|
composeDownHelper: '将停止并删除 {0} 编排下所有容器及网络,是否继续?',
|
||||||
|
|
||||||
setting: '配置',
|
setting: '配置',
|
||||||
operatorStatusHelper: '此操作将{0}Docker 服务,是否继续?',
|
operatorStatusHelper: '此操作将{0}Docker 服务,是否继续?',
|
||||||
@@ -1327,6 +1332,7 @@ const message = {
|
|||||||
LOCAL: '服务器磁盘',
|
LOCAL: '服务器磁盘',
|
||||||
OSS: '阿里云 OSS',
|
OSS: '阿里云 OSS',
|
||||||
S3: '亚马逊 S3 云存储',
|
S3: '亚马逊 S3 云存储',
|
||||||
|
mode: '模式',
|
||||||
MINIO: 'MINIO',
|
MINIO: 'MINIO',
|
||||||
SFTP: 'SFTP',
|
SFTP: 'SFTP',
|
||||||
WebDAV: 'WebDAV',
|
WebDAV: 'WebDAV',
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export interface GlobalState {
|
|||||||
defaultNetwork: string;
|
defaultNetwork: string;
|
||||||
|
|
||||||
isProductPro: boolean;
|
isProductPro: boolean;
|
||||||
|
isTrial: boolean;
|
||||||
productProExpires: number;
|
productProExpires: number;
|
||||||
|
|
||||||
errStatus: string;
|
errStatus: string;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ const GlobalStore = defineStore({
|
|||||||
defaultNetwork: 'all',
|
defaultNetwork: 'all',
|
||||||
|
|
||||||
isProductPro: false,
|
isProductPro: false,
|
||||||
|
isTrial: false,
|
||||||
productProExpires: 0,
|
productProExpires: 0,
|
||||||
|
|
||||||
errStatus: '',
|
errStatus: '',
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ export async function loadProductProFromDB() {
|
|||||||
res.data.status === 'Enable' || res.data.status === 'Lost01' || res.data.status === 'Lost02';
|
res.data.status === 'Enable' || res.data.status === 'Lost01' || res.data.status === 'Lost02';
|
||||||
if (globalStore.isProductPro) {
|
if (globalStore.isProductPro) {
|
||||||
globalStore.productProExpires = Number(res.data.productPro);
|
globalStore.productProExpires = Number(res.data.productPro);
|
||||||
|
globalStore.isTrial = res.data.trial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switchTheme();
|
switchTheme();
|
||||||
@@ -81,6 +82,7 @@ export async function getXpackSettingForTheme() {
|
|||||||
res.data.status === 'Enable' || res.data.status === 'Lost01' || res.data.status === 'Lost02';
|
res.data.status === 'Enable' || res.data.status === 'Lost01' || res.data.status === 'Lost02';
|
||||||
if (globalStore.isProductPro) {
|
if (globalStore.isProductPro) {
|
||||||
globalStore.productProExpires = Number(res.data.productPro);
|
globalStore.productProExpires = Number(res.data.productPro);
|
||||||
|
globalStore.isTrial = res.data.trial;
|
||||||
}
|
}
|
||||||
if (!globalStore.isProductPro) {
|
if (!globalStore.isProductPro) {
|
||||||
globalStore.isProductPro = false;
|
globalStore.isProductPro = false;
|
||||||
|
|||||||
@@ -66,7 +66,7 @@
|
|||||||
placeholder="#Define or paste the content of your docker-compose file here"
|
placeholder="#Define or paste the content of your docker-compose file here"
|
||||||
:indent-with-tab="true"
|
:indent-with-tab="true"
|
||||||
:tabSize="4"
|
:tabSize="4"
|
||||||
style="width: 100%; height: calc(100vh - 376px)"
|
style="width: 100%; height: calc(100vh - 400px)"
|
||||||
:lineWrapping="true"
|
:lineWrapping="true"
|
||||||
:matchBrackets="true"
|
:matchBrackets="true"
|
||||||
theme="cobalt"
|
theme="cobalt"
|
||||||
@@ -86,6 +86,26 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('container.env')" prop="envStr">
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
:placeholder="$t('container.tagHelper')"
|
||||||
|
:rows="3"
|
||||||
|
v-model="form.envStr"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<span class="input-help whitespace-break-spaces">{{ $t('container.editComposeHelper') }}</span>
|
||||||
|
<codemirror
|
||||||
|
v-model="form.envFileContent"
|
||||||
|
:autofocus="true"
|
||||||
|
:indent-with-tab="true"
|
||||||
|
:tabSize="4"
|
||||||
|
:lineWrapping="true"
|
||||||
|
:matchBrackets="true"
|
||||||
|
theme="cobalt"
|
||||||
|
:styleActiveLine="true"
|
||||||
|
:extensions="extensions"
|
||||||
|
></codemirror>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -143,6 +163,9 @@ const form = reactive({
|
|||||||
path: '',
|
path: '',
|
||||||
file: '',
|
file: '',
|
||||||
template: null as number,
|
template: null as number,
|
||||||
|
env: [],
|
||||||
|
envStr: '',
|
||||||
|
envFileContent: `env_file:\n - 1panel.env`,
|
||||||
});
|
});
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
name: [Rules.requiredInput, Rules.imageName],
|
name: [Rules.requiredInput, Rules.imageName],
|
||||||
@@ -163,6 +186,8 @@ const acceptParams = (): void => {
|
|||||||
form.path = '';
|
form.path = '';
|
||||||
form.file = '';
|
form.file = '';
|
||||||
form.template = null;
|
form.template = null;
|
||||||
|
form.envStr = '';
|
||||||
|
form.env = [];
|
||||||
loadTemplates();
|
loadTemplates();
|
||||||
loadPath();
|
loadPath();
|
||||||
isStartReading.value = false;
|
isStartReading.value = false;
|
||||||
@@ -242,6 +267,9 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
|||||||
MsgError(i18n.global.t('container.contentEmpty'));
|
MsgError(i18n.global.t('container.contentEmpty'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (form.envStr) {
|
||||||
|
form.env = form.envStr.split('\n');
|
||||||
|
}
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
await testCompose(form)
|
await testCompose(form)
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
<el-form ref="deleteForm" v-loading="loading">
|
<el-form ref="deleteForm" v-loading="loading">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-checkbox v-model="deleteFile" :label="$t('container.deleteFile')" />
|
<el-checkbox v-model="deleteFile" :label="$t('container.deleteFile')" />
|
||||||
<span class="input-help">
|
<span class="input-help whitespace-break-spaces">
|
||||||
{{ $t('container.deleteComposeHelper', [loadComposeDir()]) }}
|
{{ $t('container.deleteComposeHelper') }}
|
||||||
</span>
|
</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
@@ -64,21 +64,12 @@ const acceptParams = async (prop: DialogProps) => {
|
|||||||
dialogVisible.value = true;
|
dialogVisible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadComposeDir = () => {
|
|
||||||
const parts = composePath.value.split('/');
|
|
||||||
if (parts.length <= 2) {
|
|
||||||
return '/';
|
|
||||||
}
|
|
||||||
const parentDirectory = parts.slice(0, -1).join('/');
|
|
||||||
return parentDirectory;
|
|
||||||
};
|
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
let params = {
|
let params = {
|
||||||
name: composeName.value,
|
name: composeName.value,
|
||||||
path: composePath.value,
|
path: composePath.value,
|
||||||
operation: 'down',
|
operation: 'delete',
|
||||||
withFile: deleteFile.value,
|
withFile: deleteFile.value,
|
||||||
};
|
};
|
||||||
await composeOperator(params)
|
await composeOperator(params)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<el-tag effect="dark" type="success">{{ composeName }}</el-tag>
|
<el-tag effect="dark" type="success">{{ composeName }}</el-tag>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="createdBy === '1Panel'" style="margin-left: 50px">
|
<div v-if="createdBy === '1Panel'" style="margin-left: 50px">
|
||||||
<el-button link type="primary" @click="onComposeOperate('start')">
|
<el-button link type="primary" @click="onComposeOperate('up')">
|
||||||
{{ $t('container.start') }}
|
{{ $t('container.start') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
@@ -143,7 +143,6 @@ const dialogContainerLogRef = ref();
|
|||||||
|
|
||||||
const opRef = ref();
|
const opRef = ref();
|
||||||
|
|
||||||
const emit = defineEmits<{ (e: 'back'): void }>();
|
|
||||||
interface DialogProps {
|
interface DialogProps {
|
||||||
createdBy: string;
|
createdBy: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -260,15 +259,18 @@ const onOperate = async (op: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onComposeOperate = async (operation: string) => {
|
const onComposeOperate = async (operation: string) => {
|
||||||
ElMessageBox.confirm(
|
let mes =
|
||||||
i18n.global.t('container.composeOperatorHelper', [composeName.value, i18n.global.t('container.' + operation)]),
|
operation === 'down'
|
||||||
i18n.global.t('container.' + operation),
|
? i18n.global.t('container.composeDownHelper', [composeName.value])
|
||||||
{
|
: i18n.global.t('container.composeOperatorHelper', [
|
||||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
composeName.value,
|
||||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
i18n.global.t('container.' + operation),
|
||||||
type: 'info',
|
]);
|
||||||
},
|
ElMessageBox.confirm(mes, i18n.global.t('container.' + operation), {
|
||||||
).then(async () => {
|
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||||
|
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||||
|
type: 'info',
|
||||||
|
}).then(async () => {
|
||||||
let params = {
|
let params = {
|
||||||
name: composeName.value,
|
name: composeName.value,
|
||||||
path: composePath.value,
|
path: composePath.value,
|
||||||
@@ -280,11 +282,7 @@ const onComposeOperate = async (operation: string) => {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
if (operation === 'down') {
|
search();
|
||||||
emit('back');
|
|
||||||
} else {
|
|
||||||
search();
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|||||||
@@ -9,20 +9,56 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<DrawerHeader :header="$t('commons.button.edit')" :resource="name" :back="handleClose" />
|
<DrawerHeader :header="$t('commons.button.edit')" :resource="name" :back="handleClose" />
|
||||||
</template>
|
</template>
|
||||||
<div v-loading="loading">
|
<div v-loading="loading" style="padding-bottom: 20px">
|
||||||
<codemirror
|
<el-row type="flex" justify="center">
|
||||||
:autofocus="true"
|
<el-col :span="22">
|
||||||
placeholder="#Define or paste the content of your docker-compose file here"
|
<el-form ref="formRef" @submit.prevent label-position="top">
|
||||||
:indent-with-tab="true"
|
<el-form-item>
|
||||||
:tabSize="4"
|
<codemirror
|
||||||
style="width: 100%; height: calc(100vh - 175px)"
|
:autofocus="true"
|
||||||
:lineWrapping="true"
|
placeholder="#Define or paste the content of your docker-compose file here"
|
||||||
:matchBrackets="true"
|
:indent-with-tab="true"
|
||||||
theme="cobalt"
|
:tabSize="4"
|
||||||
:styleActiveLine="true"
|
:style="{ width: '100%', height: `calc(100vh - ${loadHeight()})` }"
|
||||||
:extensions="extensions"
|
:lineWrapping="true"
|
||||||
v-model="content"
|
:matchBrackets="true"
|
||||||
/>
|
theme="cobalt"
|
||||||
|
:styleActiveLine="true"
|
||||||
|
:extensions="extensions"
|
||||||
|
v-model="content"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<div v-if="createdBy === '1Panel'">
|
||||||
|
<el-form-item
|
||||||
|
:label="$t('container.env')"
|
||||||
|
prop="environmentStr"
|
||||||
|
v-if="createdBy === '1Panel'"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
:placeholder="$t('container.tagHelper')"
|
||||||
|
:rows="3"
|
||||||
|
v-model="environmentStr"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<span class="input-help whitespace-break-spaces">
|
||||||
|
{{ $t('container.editComposeHelper') }}
|
||||||
|
</span>
|
||||||
|
<codemirror
|
||||||
|
v-model="envFileContent"
|
||||||
|
:autofocus="true"
|
||||||
|
:indent-with-tab="true"
|
||||||
|
:tabSize="4"
|
||||||
|
:lineWrapping="true"
|
||||||
|
:matchBrackets="true"
|
||||||
|
theme="cobalt"
|
||||||
|
:styleActiveLine="true"
|
||||||
|
:extensions="extensions"
|
||||||
|
></codemirror>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
@@ -45,26 +81,38 @@ import { composeUpdate } from '@/api/modules/container';
|
|||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
import { MsgSuccess } from '@/utils/message';
|
||||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||||
|
import { ElForm } from 'element-plus';
|
||||||
|
|
||||||
|
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const composeVisible = ref(false);
|
const composeVisible = ref(false);
|
||||||
const extensions = [javascript(), oneDark];
|
const extensions = [javascript(), oneDark];
|
||||||
const path = ref();
|
const path = ref();
|
||||||
const content = ref();
|
const content = ref();
|
||||||
const name = ref();
|
const name = ref();
|
||||||
|
const environmentStr = ref();
|
||||||
|
const environmentEnv = ref();
|
||||||
|
const createdBy = ref();
|
||||||
|
const envFileContent = `env_file:\n - 1panel.env`;
|
||||||
|
|
||||||
const onSubmitEdit = async () => {
|
const onSubmitEdit = async () => {
|
||||||
const param = {
|
const param = {
|
||||||
name: name.value,
|
name: name.value,
|
||||||
path: path.value,
|
path: path.value,
|
||||||
content: content.value,
|
content: content.value,
|
||||||
|
env: environmentStr.value,
|
||||||
|
createdBy: createdBy.value,
|
||||||
};
|
};
|
||||||
|
if (environmentStr.value != undefined) {
|
||||||
|
param.env = environmentStr.value.split('\n');
|
||||||
|
}
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
await composeUpdate(param)
|
await composeUpdate(param)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
composeVisible.value = false;
|
composeVisible.value = false;
|
||||||
|
emit('search');
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
@@ -75,13 +123,22 @@ interface DialogProps {
|
|||||||
name: string;
|
name: string;
|
||||||
path: string;
|
path: string;
|
||||||
content: string;
|
content: string;
|
||||||
|
env: Array<string>;
|
||||||
|
envStr: string;
|
||||||
|
createdBy: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loadHeight = () => {
|
||||||
|
return createdBy.value === '1Panel' ? '300px' : '200px';
|
||||||
|
};
|
||||||
const acceptParams = (props: DialogProps): void => {
|
const acceptParams = (props: DialogProps): void => {
|
||||||
composeVisible.value = true;
|
composeVisible.value = true;
|
||||||
path.value = props.path;
|
path.value = props.path;
|
||||||
name.value = props.name;
|
name.value = props.name;
|
||||||
content.value = props.content;
|
content.value = props.content;
|
||||||
|
createdBy.value = props.createdBy;
|
||||||
|
environmentEnv.value = props.env || [];
|
||||||
|
environmentStr.value = environmentEnv.value.join('\n');
|
||||||
};
|
};
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
composeVisible.value = false;
|
composeVisible.value = false;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-loading="loading">
|
<div v-loading="loading">
|
||||||
<div v-show="isOnDetail">
|
<div v-show="isOnDetail">
|
||||||
<ComposeDetail @back="backList" ref="composeDetailRef" />
|
<ComposeDetail ref="composeDetailRef" />
|
||||||
</div>
|
</div>
|
||||||
<el-card v-if="dockerStatus != 'Running'" class="mask-prompt">
|
<el-card v-if="dockerStatus != 'Running'" class="mask-prompt">
|
||||||
<span>{{ $t('container.serviceUnavailable') }}</span>
|
<span>{{ $t('container.serviceUnavailable') }}</span>
|
||||||
@@ -64,12 +64,13 @@
|
|||||||
<span v-if="row.createdBy === '1Panel'">1Panel</span>
|
<span v-if="row.createdBy === '1Panel'">1Panel</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column :label="$t('container.containerStatus')" min-width="80" fix>
|
||||||
:label="$t('container.containerNumber')"
|
<template #default="scope">
|
||||||
prop="containerNumber"
|
<div>
|
||||||
min-width="80"
|
{{ getContainerStatus(scope.row.containers) }}
|
||||||
fix
|
</div>
|
||||||
/>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column :label="$t('commons.table.createdAt')" prop="createdAt" min-width="80" fix />
|
<el-table-column :label="$t('commons.table.createdAt')" prop="createdAt" min-width="80" fix />
|
||||||
<fu-table-operations
|
<fu-table-operations
|
||||||
width="200px"
|
width="200px"
|
||||||
@@ -82,7 +83,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</LayoutContent>
|
</LayoutContent>
|
||||||
|
|
||||||
<EditDialog ref="dialogEditRef" />
|
<EditDialog @search="search" ref="dialogEditRef" />
|
||||||
<CreateDialog @search="search" ref="dialogRef" />
|
<CreateDialog @search="search" ref="dialogRef" />
|
||||||
<DeleteDialog @search="search" ref="dialogDelRef" />
|
<DeleteDialog @search="search" ref="dialogDelRef" />
|
||||||
</div>
|
</div>
|
||||||
@@ -173,9 +174,16 @@ const loadDetail = async (row: Container.ComposeInfo) => {
|
|||||||
isOnDetail.value = true;
|
isOnDetail.value = true;
|
||||||
composeDetailRef.value!.acceptParams(params);
|
composeDetailRef.value!.acceptParams(params);
|
||||||
};
|
};
|
||||||
const backList = async () => {
|
const getContainerStatus = (containers) => {
|
||||||
isOnDetail.value = false;
|
const safeContainers = containers || [];
|
||||||
search();
|
const runningCount = safeContainers.filter((container) => container.state.toLowerCase() === 'running').length;
|
||||||
|
const totalCount = safeContainers.length;
|
||||||
|
const statusText = runningCount > 0 ? 'Running' : 'Exited';
|
||||||
|
if (statusText === 'Exited') {
|
||||||
|
return i18n.global.t('container.exited');
|
||||||
|
} else {
|
||||||
|
return i18n.global.t('container.running') + ` (${runningCount}/${totalCount})`;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const dialogRef = ref();
|
const dialogRef = ref();
|
||||||
@@ -199,6 +207,8 @@ const onEdit = async (row: Container.ComposeInfo) => {
|
|||||||
name: row.name,
|
name: row.name,
|
||||||
path: row.path,
|
path: row.path,
|
||||||
content: res.data,
|
content: res.data,
|
||||||
|
env: row.env,
|
||||||
|
createdBy: row.createdBy,
|
||||||
};
|
};
|
||||||
dialogEditRef.value!.acceptParams(params);
|
dialogEditRef.value!.acceptParams(params);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ const dialogUpgradeRef = ref();
|
|||||||
const dialogCommitRef = ref();
|
const dialogCommitRef = ref();
|
||||||
const dialogPortJumpRef = ref();
|
const dialogPortJumpRef = ref();
|
||||||
const opRef = ref();
|
const opRef = ref();
|
||||||
const includeAppStore = ref(true);
|
const includeAppStore = ref();
|
||||||
const columns = ref([]);
|
const columns = ref([]);
|
||||||
|
|
||||||
const dockerStatus = ref('Running');
|
const dockerStatus = ref('Running');
|
||||||
@@ -423,6 +423,7 @@ const dialogRenameRef = ref();
|
|||||||
const dialogPruneRef = ref();
|
const dialogPruneRef = ref();
|
||||||
|
|
||||||
const search = async (column?: any) => {
|
const search = async (column?: any) => {
|
||||||
|
localStorage.setItem('includeAppStore', includeAppStore.value ? 'true' : 'false');
|
||||||
let filterItem = props.filters ? props.filters : '';
|
let filterItem = props.filters ? props.filters : '';
|
||||||
paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;
|
paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;
|
||||||
paginationConfig.order = column?.order ? column.order : paginationConfig.order;
|
paginationConfig.order = column?.order ? column.order : paginationConfig.order;
|
||||||
@@ -767,6 +768,8 @@ const buttons = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
let includeItem = localStorage.getItem('includeAppStore');
|
||||||
|
includeAppStore.value = !includeItem || includeItem === 'true';
|
||||||
loadStatus();
|
loadStatus();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -7,9 +7,7 @@
|
|||||||
size="50%"
|
size="50%"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header">
|
<DrawerHeader :header="$t('commons.button.view')" :back="handleClose" />
|
||||||
<span>{{ $t('commons.button.view') }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
<codemirror
|
<codemirror
|
||||||
:autofocus="true"
|
:autofocus="true"
|
||||||
@@ -38,6 +36,7 @@ import { javascript } from '@codemirror/lang-javascript';
|
|||||||
import { oneDark } from '@codemirror/theme-one-dark';
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { Codemirror } from 'vue-codemirror';
|
import { Codemirror } from 'vue-codemirror';
|
||||||
|
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||||
const extensions = [javascript(), oneDark];
|
const extensions = [javascript(), oneDark];
|
||||||
|
|
||||||
const detailVisible = ref(false);
|
const detailVisible = ref(false);
|
||||||
@@ -51,6 +50,10 @@ const acceptParams = (params: DialogProps): void => {
|
|||||||
detailVisible.value = true;
|
detailVisible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
detailVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
acceptParams,
|
acceptParams,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
<el-table-column :label="$t('container.description')" prop="description" min-width="200" fix />
|
<el-table-column :label="$t('container.description')" prop="description" min-width="200" fix />
|
||||||
<el-table-column :label="$t('commons.table.createdAt')" min-width="80" fix>
|
<el-table-column :label="$t('commons.table.createdAt')" min-width="80" fix>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
{{ dateFormatSimple(row.createdAt) }}
|
{{ dateFormat(0, 0, row.createdAt) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<fu-table-operations :buttons="buttons" :label="$t('commons.table.operate')" />
|
<fu-table-operations :buttons="buttons" :label="$t('commons.table.operate')" />
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, onMounted, ref } from 'vue';
|
import { reactive, onMounted, ref } from 'vue';
|
||||||
import { dateFormatSimple } from '@/utils/util';
|
import { dateFormat } from '@/utils/util';
|
||||||
import { Container } from '@/api/interface/container';
|
import { Container } from '@/api/interface/container';
|
||||||
import DetailDialog from '@/views/container/template/detail/index.vue';
|
import DetailDialog from '@/views/container/template/detail/index.vue';
|
||||||
import OperatorDialog from '@/views/container/template/operator/index.vue';
|
import OperatorDialog from '@/views/container/template/operator/index.vue';
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ const acceptParams = (props: BatchRoleProps) => {
|
|||||||
addForm.paths.push(file.path);
|
addForm.paths.push(file.path);
|
||||||
});
|
});
|
||||||
addForm.mode = Number.parseInt(String(props.files[0].mode), 8);
|
addForm.mode = Number.parseInt(String(props.files[0].mode), 8);
|
||||||
addForm.group = props.files[0].group;
|
addForm.group = props.files[0].group || props.files[0].gid;
|
||||||
addForm.user = props.files[0].user;
|
addForm.user = props.files[0].user || props.files[0].uid;
|
||||||
addForm.sub = true;
|
addForm.sub = true;
|
||||||
|
|
||||||
mode.value = String(props.files[0].mode);
|
mode.value = String(props.files[0].mode);
|
||||||
|
|||||||
@@ -53,7 +53,9 @@
|
|||||||
<template #prompt>
|
<template #prompt>
|
||||||
<el-alert type="info" :closable="false">
|
<el-alert type="info" :closable="false">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span v-html="$t('file.fileHeper')"></span>
|
<span class="input-help whitespace-break-spaces">
|
||||||
|
{{ $t('file.fileHeper') }}
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-alert>
|
</el-alert>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -115,7 +115,9 @@ const acceptParams = (props: EditProps) => {
|
|||||||
isFullscreen.value = fileType.value === 'excel';
|
isFullscreen.value = fileType.value === 'excel';
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
fileUrl.value = `${import.meta.env.VITE_API_URL as string}/files/download?path=${encodeURIComponent(props.path)}`;
|
fileUrl.value = `${import.meta.env.VITE_API_URL as string}/files/download?path=${encodeURIComponent(
|
||||||
|
props.path,
|
||||||
|
)}×tamp=${new Date().getTime()}`;
|
||||||
open.value = true;
|
open.value = true;
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
<el-form-item label="Endpoint" prop="varsJson.endpointItem" :rules="Rules.requiredInput">
|
<el-form-item label="Endpoint" prop="varsJson.endpointItem" :rules="Rules.requiredInput">
|
||||||
<el-input v-model.trim="cosData.rowData!.varsJson['endpointItem']">
|
<el-input v-model.trim="cosData.rowData!.varsJson['endpointItem']">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<el-select v-model.trim="endpointProto" style="width: 100px">
|
<el-select v-model.trim="endpointProto" style="width: 120px">
|
||||||
<el-option label="http" value="http" />
|
<el-option label="http" value="http" />
|
||||||
<el-option label="https" value="https" />
|
<el-option label="https" value="https" />
|
||||||
</el-select>
|
</el-select>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
>
|
>
|
||||||
<el-input v-model="kodoData.rowData!.varsJson['domainItem']">
|
<el-input v-model="kodoData.rowData!.varsJson['domainItem']">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<el-select v-model.trim="domainProto" style="width: 100px">
|
<el-select v-model.trim="domainProto" style="width: 120px">
|
||||||
<el-option label="http" value="http" />
|
<el-option label="http" value="http" />
|
||||||
<el-option label="https" value="https" />
|
<el-option label="https" value="https" />
|
||||||
</el-select>
|
</el-select>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<el-form-item label="Endpoint" prop="varsJson.endpointItem" :rules="Rules.requiredInput">
|
<el-form-item label="Endpoint" prop="varsJson.endpointItem" :rules="Rules.requiredInput">
|
||||||
<el-input v-model="minioData.rowData!.varsJson['endpointItem']">
|
<el-input v-model="minioData.rowData!.varsJson['endpointItem']">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<el-select v-model.trim="endpointProto" style="width: 100px">
|
<el-select v-model.trim="endpointProto" style="width: 120px">
|
||||||
<el-option label="http" value="http" />
|
<el-option label="http" value="http" />
|
||||||
<el-option label="https" value="https" />
|
<el-option label="https" value="https" />
|
||||||
</el-select>
|
</el-select>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<el-form-item label="Endpoint" prop="varsJson.endpointItem" :rules="Rules.requiredInput">
|
<el-form-item label="Endpoint" prop="varsJson.endpointItem" :rules="Rules.requiredInput">
|
||||||
<el-input v-model="ossData.rowData!.varsJson['endpointItem']">
|
<el-input v-model="ossData.rowData!.varsJson['endpointItem']">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<el-select v-model.trim="endpointProto" style="width: 100px">
|
<el-select v-model.trim="endpointProto" style="width: 120px">
|
||||||
<el-option label="http" value="http" />
|
<el-option label="http" value="http" />
|
||||||
<el-option label="https" value="https" />
|
<el-option label="https" value="https" />
|
||||||
</el-select>
|
</el-select>
|
||||||
|
|||||||
@@ -16,6 +16,12 @@
|
|||||||
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
|
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
|
||||||
<el-tag>{{ $t('setting.' + s3Data.rowData!.type) }}</el-tag>
|
<el-tag>{{ $t('setting.' + s3Data.rowData!.type) }}</el-tag>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('setting.mode')" prop="varsJson.mode" :rules="Rules.requiredSelect">
|
||||||
|
<el-radio-group v-model="s3Data.rowData!.varsJson['mode']">
|
||||||
|
<el-radio value="virtual hosted">Virtual Hosted</el-radio>
|
||||||
|
<el-radio value="path">Path</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="Access Key ID" prop="accessKey" :rules="Rules.requiredInput">
|
<el-form-item label="Access Key ID" prop="accessKey" :rules="Rules.requiredInput">
|
||||||
<el-input v-model.trim="s3Data.rowData!.accessKey" />
|
<el-input v-model.trim="s3Data.rowData!.accessKey" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -28,7 +34,7 @@
|
|||||||
<el-form-item label="Endpoint" prop="varsJson.endpointItem" :rules="Rules.requiredInput">
|
<el-form-item label="Endpoint" prop="varsJson.endpointItem" :rules="Rules.requiredInput">
|
||||||
<el-input v-model="s3Data.rowData!.varsJson['endpointItem']">
|
<el-input v-model="s3Data.rowData!.varsJson['endpointItem']">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<el-select v-model.trim="endpointProto" style="width: 100px">
|
<el-select v-model="endpointProto" style="width: 120px">
|
||||||
<el-option label="http" value="http" />
|
<el-option label="http" value="http" />
|
||||||
<el-option label="https" value="https" />
|
<el-option label="https" value="https" />
|
||||||
</el-select>
|
</el-select>
|
||||||
@@ -117,9 +123,12 @@ const s3Data = ref<DialogProps>({
|
|||||||
const acceptParams = (params: DialogProps): void => {
|
const acceptParams = (params: DialogProps): void => {
|
||||||
buckets.value = [];
|
buckets.value = [];
|
||||||
s3Data.value = params;
|
s3Data.value = params;
|
||||||
if (params.title === 'create' || (params.title === 'edit' && !s3Data.value.rowData.varsJson['scType'])) {
|
if (!s3Data.value.rowData.varsJson['scType']) {
|
||||||
s3Data.value.rowData.varsJson['scType'] = 'STANDARD';
|
s3Data.value.rowData.varsJson['scType'] = 'STANDARD';
|
||||||
}
|
}
|
||||||
|
if (!s3Data.value.rowData.varsJson['mode']) {
|
||||||
|
s3Data.value.rowData.varsJson['mode'] = 'virtual hosted';
|
||||||
|
}
|
||||||
if (s3Data.value.title === 'edit') {
|
if (s3Data.value.title === 'edit') {
|
||||||
let httpItem = splitHttp(s3Data.value.rowData!.varsJson['endpoint']);
|
let httpItem = splitHttp(s3Data.value.rowData!.varsJson['endpoint']);
|
||||||
s3Data.value.rowData!.varsJson['endpointItem'] = httpItem.url;
|
s3Data.value.rowData!.varsJson['endpointItem'] = httpItem.url;
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ const search = async () => {
|
|||||||
hasLicense.value = true;
|
hasLicense.value = true;
|
||||||
if (globalStore.isProductPro) {
|
if (globalStore.isProductPro) {
|
||||||
globalStore.productProExpires = Number(res.data.productPro);
|
globalStore.productProExpires = Number(res.data.productPro);
|
||||||
|
globalStore.isTrial = res.data.trial;
|
||||||
}
|
}
|
||||||
license.licenseName = res.data.licenseName;
|
license.licenseName = res.data.licenseName;
|
||||||
license.message = res.data.message;
|
license.message = res.data.message;
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
<template #prompt>
|
<template #prompt>
|
||||||
<el-alert type="info" :closable="false">
|
<el-alert type="info" :closable="false">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span v-html="$t('runtime.statusHelper')"></span>
|
<span class="input-help whitespace-break-spaces">
|
||||||
|
{{ $t('runtime.statusHelper') }}
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-alert>
|
</el-alert>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
<template #prompt>
|
<template #prompt>
|
||||||
<el-alert type="info" :closable="false">
|
<el-alert type="info" :closable="false">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span v-html="$t('runtime.statusHelper')"></span>
|
<span class="input-help whitespace-break-spaces">
|
||||||
|
{{ $t('runtime.statusHelper') }}
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-alert>
|
</el-alert>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
<template #prompt>
|
<template #prompt>
|
||||||
<el-alert type="info" :closable="false">
|
<el-alert type="info" :closable="false">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span v-html="$t('runtime.statusHelper')"></span>
|
<span class="input-help whitespace-break-spaces">
|
||||||
|
{{ $t('runtime.statusHelper') }}
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-alert>
|
</el-alert>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -31,6 +31,9 @@
|
|||||||
<el-switch v-model="proxy.sni"></el-switch>
|
<el-switch v-model="proxy.sni"></el-switch>
|
||||||
<span class="input-help">{{ $t('website.sniHelper') }}</span>
|
<span class="input-help">{{ $t('website.sniHelper') }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="proxy_ssl_name" prop="proxySSLName" v-if="proxy.sni">
|
||||||
|
<el-input v-model.trim="proxy.proxySSLName"></el-input>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item :label="$t('website.cacheTime')" prop="cacheTime" v-if="proxy.cache">
|
<el-form-item :label="$t('website.cacheTime')" prop="cacheTime" v-if="proxy.cache">
|
||||||
<el-input v-model.number="proxy.cacheTime" maxlength="15">
|
<el-input v-model.number="proxy.cacheTime" maxlength="15">
|
||||||
<template #append>
|
<template #append>
|
||||||
@@ -47,7 +50,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-row :gutter="10">
|
<el-row :gutter="10">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item :label="$t('website.proxyPass')" prop="proxyPass">
|
<el-form-item :label="$t('website.proxyPass')" prop="proxyAddress">
|
||||||
<el-input
|
<el-input
|
||||||
v-model.trim="proxy.proxyAddress"
|
v-model.trim="proxy.proxyAddress"
|
||||||
:placeholder="$t('website.proxyHelper')"
|
:placeholder="$t('website.proxyHelper')"
|
||||||
@@ -139,6 +142,7 @@ const rules = ref({
|
|||||||
cacheTime: [Rules.requiredInput, checkNumberRange(1, 65535)],
|
cacheTime: [Rules.requiredInput, checkNumberRange(1, 65535)],
|
||||||
proxyPass: [Rules.requiredInput],
|
proxyPass: [Rules.requiredInput],
|
||||||
proxyHost: [Rules.requiredInput],
|
proxyHost: [Rules.requiredInput],
|
||||||
|
proxyAddress: [Rules.requiredInput],
|
||||||
});
|
});
|
||||||
const open = ref(false);
|
const open = ref(false);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
@@ -159,6 +163,8 @@ const initData = (): Website.ProxyConfig => ({
|
|||||||
replaces: {},
|
replaces: {},
|
||||||
proxyAddress: '',
|
proxyAddress: '',
|
||||||
proxyProtocol: 'http://',
|
proxyProtocol: 'http://',
|
||||||
|
sni: false,
|
||||||
|
proxySSLName: '$proxy_host',
|
||||||
});
|
});
|
||||||
let proxy = ref(initData());
|
let proxy = ref(initData());
|
||||||
const replaces = ref<any>([]);
|
const replaces = ref<any>([]);
|
||||||
|
|||||||
Reference in New Issue
Block a user