Compare commits

...
42 Commits
Author SHA1 Message Date
QuentinHsu c510a028db fix: 解决重复访问容器内部时,终端显示宽度异常的问题 (#3996) 2024-02-27 14:54:57 +08:00
ssongliu 121a1ff5da fix: 修改快照 json 文件生成逻辑 (#3993) 2024-02-27 03:58:12 +00:00
ssongliu 1ecc53dad3 feat: 面板帮助文档跳转到具体目录 (#3992) 2024-02-27 03:30:13 +00:00
zhengkunwang 279b3942b4 fix: 解决应用同步异常的问题 (#3991) 2024-02-27 03:28:12 +00:00
ssongliu a0af04bbf7 fix: 调整快照回滚逻辑 (#3989) 2024-02-27 02:20:12 +00:00
ssongliu d5d1125660 fix: 解决快照恢复过程中生成回滚文件不完整的问题 (#3987) 2024-02-26 22:45:52 +08:00
wanghe-fit2cloud 08801ebfcd chore: Update README.md 2024-02-26 18:05:40 +08:00
zhengkunwang 262cbb9405 fix: 解决删除 openresty 之后没有同步删除 php 运行容器的问题 (#3984) 2024-02-26 10:04:12 +00:00
zhengkunwang 96233ee9b2 fix: 解决拖动上传文件夹错误的问题 (#3983) 2024-02-26 10:02:11 +00:00
ssongliu 49dcb094dc fix: 解决容器镜像清理的显示问题 (#3980) 2024-02-26 06:14:10 +00:00
zhengkunwang f0d5aca657 fix: 解决应用重建失败的问题 (#3967) 2024-02-23 10:38:40 +00:00
zhengkunwang 58f58f0fac fix: 解决清空文件后保存失败的问题 (#3965) 2024-02-23 10:36:40 +00:00
QuentinHsu 3bd6f46fe2 perf: 应用商店的应用介绍样式 (#3961) 2024-02-22 23:03:56 +08:00
zhengkunwang e427ccf70c feat: 修改应用重建逻辑 (#3953) 2024-02-22 09:12:08 +00:00
ssongliu 1b47e8dd63 fix: 解决 Pr 导致的代码冲突 (#3949) 2024-02-22 07:34:07 +00:00
ssongliu b668f55222 fix: 移除项目未使用代码 (#3948) 2024-02-22 07:32:07 +00:00
wanghe-fit2cloud 9a127d9afe chore: 升级 Node.js 和 Golang 版本 2024-02-22 15:23:20 +08:00
ssongliu 63484601b5 fix: 忽略 WebDAV 获取端口信息错误日志 (#3946) 2024-02-22 05:42:06 +00:00
ssongliu ef5e42cf0b fix: 解决远程数据库添加验证失败的问题 (#3942) 2024-02-21 11:08:29 +00:00
ssongliu 50a83e7e11 fix: Cookie 启用 httponly (#3941) 2024-02-21 11:06:28 +00:00
ssongliu 2475c82a63 fix: 修改 Mysql 远程数据库添加校验 (#3939)
Refs #3936
2024-02-21 09:00:30 +00:00
ssongliu 61ff55e157 pref: 计划任务数据库备份增加实例标签 (#3938)
Refs #3937
2024-02-21 08:58:28 +00:00
ssongliu dbc7799e89 fix: 解决代码冲突 (#3930) 2024-02-20 12:30:08 +00:00
wangdan-fit2cloud 6275e06806 feat: 样式优化 (#3928) 2024-02-20 18:20:45 +08:00
ssongliu e4f9ec32cd fix: 快照时排除备份目录下的快照文件 (#3926)
Refs #3923
2024-02-20 08:36:08 +00:00
ssongliu 319c21fc36 style: 修改备份账号提示信息 (#3925) 2024-02-20 08:18:08 +00:00
ssongliu e346442d36 fix: 解决备份账号异常导致备份记录无法删除的问题 (#3922)
Refs #3919
2024-02-20 07:02:07 +00:00
ALMAS 1781362342 pref: 优化应用安装表单选择器组件支持多选 (#3918) 2024-02-20 14:58:58 +08:00
wanghe-fit2cloud 3035bb2bf6 chore: Fix typos 2024-02-19 18:02:09 +08:00
maninhill b896a5be3b Update README.md 2024-02-19 17:20:56 +08:00
maninhill d51abd529a Update README.md 2024-02-19 17:19:57 +08:00
zhengkunwang 550eed7962 feat: 升级依赖 (#3907) 2024-02-19 05:55:27 +00:00
mobeicanyue 51d09b7164 Fix typos (#3897)
* Fix typos in backend folder

* Fix typos in frontend

* Fix typos in frontend

* Fix Percent typo
2024-02-17 22:42:09 +08:00
moonrailgun 046d8f526b fix: fix typo for i18n-en (#3887) 2024-02-15 23:12:23 +08:00
zhengkunwang 5bf121b687 feat: 限制文件最大上传个数 (#3867) 2024-02-09 01:52:09 +00:00
zhengkunwang e6a43d22c8 feat: 优化应用状态同步 (#3866) 2024-02-09 01:50:11 +00:00
zhengkunwang 3fd7aa17a8 fix: 解决拖拽上传文件夹文件上传不全的问题 (#3865)
Refs https://github.com/1Panel-dev/1Panel/issues/3848
2024-02-08 08:00:09 +00:00
ssongliu 6a903b7fdf fix: 解决容器监控单位不正确的问题 (#3857)
Refs #3853
2024-02-07 14:30:09 +00:00
ssongliu 958f9a1aad fix: 修改容器镜像仓库正则表达式 (#3856)
Refs #3851
2024-02-07 14:28:08 +00:00
zhengkunwang e48830cd99 feat: 升级 Go 版本到 1.21 (#3847) 2024-02-06 08:36:13 +00:00
ssongliu ece2cb433d style: 系统快照恢复样式调整 (#3845) 2024-02-06 06:20:14 +00:00
zhengkunwang fb06e29aa3 feat: Nginx 配置支持 Lua 模块 (#3844) 2024-02-06 06:02:12 +00:00
127 changed files with 1773 additions and 1717 deletions
+2 -2
View File
@@ -15,7 +15,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '18.14'
node-version: '20.2'
- name: Build Web
run: |
cd frontend && npm install && npm run build:pro
@@ -24,7 +24,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.20.x'
go-version: '1.21.x'
- name: Build Release
uses: goreleaser/goreleaser-action@v4
with:
+2
View File
@@ -39,6 +39,8 @@ frontend/auto-imports.d.ts
frontend/components.d.ts
frontend/src/xpack
backend/xpack
backend/router/entry_xpack.go
backend/server/init_xpack.go
.history/
dist/
+6 -6
View File
@@ -11,13 +11,13 @@
------------------------------
1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。1Panel 的功能和优势包括:
1Panel 是新一代的 Linux 服务器运维管理面板。
- **快速建站**:深度集成 Wordpress 和 [Halo](https://github.com/halo-dev/halo/),域名绑定、SSL 证书配置等一键搞定;
- **高效管理**:通过 Web 端轻松管理 Linux 服务器,包括主机监控、文件管理、数据库管理、容器管理等;
- **安全可靠**:基于容器来管理和部署应用,最小漏洞暴露面,提供防火墙和日志审计等功能;
- **一键备份**:支持一键备份和恢复,备份数据到各类云端存储,永不丢失;
- **应用商店**:精选各类高质量开源工具和应用软件,助力您轻松安装和升级。
- **高效管理**:用户可以通过 Web 图形界面轻松管理 Linux 服务器,实现主机监控、文件管理、数据库管理、容器管理等功能;
- **快速建站**:深度集成开源建站软件 WordPress 和 [Halo](https://github.com/halo-dev/halo/),域名绑定、SSL 证书配置等操作一键搞定;
- **应用商店**:精选上架各类高质量的开源工具和应用软件,协助用户轻松安装并升级;
- **安全可靠**:基于容器管理并部署应用,实现最小的漏洞暴露面,同时提供防火墙和日志审计等功能;
- **一键备份**:支持一键备份和恢复,用户可以将数据备份到各类云端存储介质,永不丢失。
## UI 展示
-18
View File
@@ -164,24 +164,6 @@ func (b *BaseApi) SearchPostgresql(c *gin.Context) {
})
}
// @Tags Database Postgresql
// @Summary List postgresql database names
// @Description 获取 postgresql 数据库列表
// @Accept json
// @Param request body dto.PageInfo true "request"
// @Success 200 {array} dto.PostgresqlOption
// @Security ApiKeyAuth
// @Router /databases/pg/options [get]
func (b *BaseApi) ListPostgresqlDBName(c *gin.Context) {
list, err := postgresqlService.ListDBOption()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Database Postgresql
// @Summary Load postgresql database from remote
// @Description 从服务器获取
+1 -1
View File
@@ -64,7 +64,7 @@ type FileDeCompress struct {
type FileEdit struct {
Path string `json:"path" validate:"required"`
Content string `json:"content" validate:"required"`
Content string `json:"content"`
}
type FileRename struct {
+80 -82
View File
@@ -12,8 +12,6 @@ import (
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"gopkg.in/yaml.v3"
@@ -152,7 +150,7 @@ func (a *AppInstallService) CheckExist(req request.AppInstalledInfo) (*response.
if reflect.DeepEqual(appInstall, model.AppInstall{}) {
return res, nil
}
if err = syncByID(appInstall.ID); err != nil {
if err = syncAppInstallStatus(&appInstall); err != nil {
return nil, err
}
@@ -242,26 +240,26 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
if err != nil {
return handleErr(install, err, out)
}
return syncByID(install.ID)
return syncAppInstallStatus(&install)
case constant.Stop:
out, err := compose.Stop(dockerComposePath)
if err != nil {
return handleErr(install, err, out)
}
return syncByID(install.ID)
return syncAppInstallStatus(&install)
case constant.Restart:
out, err := compose.Restart(dockerComposePath)
if err != nil {
return handleErr(install, err, out)
}
return syncByID(install.ID)
return syncAppInstallStatus(&install)
case constant.Delete:
if err := deleteAppInstall(install, req.DeleteBackup, req.ForceDelete, req.DeleteDB); err != nil && !req.ForceDelete {
return err
}
return nil
case constant.Sync:
return syncByID(install.ID)
return syncAppInstallStatus(&install)
case constant.Upgrade:
return upgradeInstall(install.ID, req.DetailId, req.Backup, req.PullImage)
case constant.Reload:
@@ -413,7 +411,7 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
return err
}
for _, i := range allList {
if i.Status == constant.Installing || i.Status == constant.Upgrading {
if i.Status == constant.Installing || i.Status == constant.Upgrading || i.Status == constant.Rebuilding {
if systemInit {
i.Status = constant.Error
i.Message = "1Panel restart causes the task to terminate"
@@ -422,7 +420,7 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
continue
}
if !systemInit {
if err := syncByID(i.ID); err != nil {
if err = syncAppInstallStatus(&i); err != nil {
global.LOG.Errorf("sync install app[%s] error,mgs: %s", i.Name, err.Error())
}
}
@@ -641,10 +639,11 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
for _, form := range appForm.FormFields {
if v, ok := envs[form.EnvKey]; ok {
appParam := response.AppParam{
Edit: false,
Key: form.EnvKey,
Rule: form.Rule,
Type: form.Type,
Edit: false,
Key: form.EnvKey,
Rule: form.Rule,
Type: form.Type,
Multiple: form.Multiple,
}
if form.Edit {
appParam.Edit = true
@@ -656,10 +655,20 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(v.(string)))
appParam.ShowValue = appInstall.Name
} else if form.Type == "select" {
for _, fv := range form.Values {
if fv.Value == v {
appParam.ShowValue = fv.Label
break
if form.Multiple {
if v == "" {
appParam.Value = []string{}
} else {
if str, ok := v.(string); ok {
appParam.Value = strings.Split(str, ",")
}
}
} else {
for _, fv := range form.Values {
if fv.Value == v {
appParam.ShowValue = fv.Label
break
}
}
}
appParam.Values = form.Values
@@ -667,14 +676,15 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
params = append(params, appParam)
} else {
params = append(params, response.AppParam{
Edit: form.Edit,
Key: form.EnvKey,
Rule: form.Rule,
Type: form.Type,
LabelZh: form.LabelZh,
LabelEn: form.LabelEn,
Value: form.Default,
Values: form.Values,
Edit: form.Edit,
Key: form.EnvKey,
Rule: form.Rule,
Type: form.Type,
LabelZh: form.LabelZh,
LabelEn: form.LabelEn,
Value: form.Default,
Values: form.Values,
Multiple: form.Multiple,
})
}
}
@@ -690,15 +700,11 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
return &res, nil
}
func syncByID(installID uint) error {
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(installID))
if err != nil {
return err
}
if appInstall.Status == constant.Installing {
func syncAppInstallStatus(appInstall *model.AppInstall) error {
if appInstall.Status == constant.Installing || appInstall.Status == constant.Rebuilding || appInstall.Status == constant.Upgrading {
return nil
}
containerNames, err := getContainerNames(appInstall)
containerNames, err := getContainerNames(*appInstall)
if err != nil {
return err
}
@@ -710,72 +716,64 @@ func syncByID(installID uint) error {
if err != nil {
return err
}
var (
errorContainers []string
notFoundContainers []string
runningContainers []string
exitedContainers []string
runningCount int
exitedCount int
pausedCount int
exitedContainerNames []string
total = len(containerNames)
count = len(containers)
)
for _, n := range containers {
switch n.State {
foundNames := make(map[string]bool)
for _, con := range containers {
foundNames[con.Names[0]] = true
switch con.State {
case "exited":
exitedContainers = append(exitedContainers, n.Names[0])
exitedContainerNames = append(exitedContainerNames, strings.TrimPrefix(con.Names[0], "/"))
exitedCount++
case "running":
runningContainers = append(runningContainers, n.Names[0])
default:
errorContainers = append(errorContainers, n.Names[0])
runningCount++
case "paused":
pausedCount++
}
}
var notFoundNames []string
for _, name := range containerNames {
exist := false
for _, container := range containers {
if common.ExistWithStrArray(name, container.Names) {
exist = true
break
}
}
if !exist {
notFoundContainers = append(notFoundContainers, name)
if !foundNames["/"+name] {
notFoundNames = append(notFoundNames, name)
}
}
containerCount := len(containers)
errCount := len(errorContainers)
notFoundCount := len(notFoundContainers)
existedCount := len(exitedContainers)
normalCount := len(containerNames)
runningCount := len(runningContainers)
if containerCount == 0 {
switch {
case count == 0:
appInstall.Status = constant.Error
appInstall.Message = i18n.GetMsgWithMap("ErrContainerNotFound", map[string]interface{}{"name": strings.Join(containerNames, ",")})
return appInstallRepo.Save(context.Background(), &appInstall)
}
if errCount == 0 && existedCount == 0 && notFoundCount == 0 {
appInstall.Status = constant.Running
return appInstallRepo.Save(context.Background(), &appInstall)
}
if existedCount == normalCount {
appInstall.Message = buserr.WithName("ErrContainerNotFound", strings.Join(containerNames, ",")).Error()
case exitedCount == total:
appInstall.Status = constant.Stopped
return appInstallRepo.Save(context.Background(), &appInstall)
}
if errCount == normalCount {
appInstall.Status = constant.Error
}
if runningCount < normalCount {
case runningCount == total:
appInstall.Status = constant.Running
case pausedCount == total:
appInstall.Status = constant.Paused
default:
var msg string
if exitedCount > 0 {
msg = buserr.WithName("ErrContainerMsg", strings.Join(exitedContainerNames, ",")).Error()
}
if len(notFoundNames) > 0 {
msg += buserr.WithName("ErrContainerNotFound", strings.Join(notFoundNames, ",")).Error()
}
if msg == "" {
msg = buserr.New("ErrAppWarn").Error()
}
appInstall.Message = msg
appInstall.Status = constant.UnHealthy
}
_ = appInstallRepo.Save(context.Background(), appInstall)
var errMsg string
if errCount > 0 {
errMsg += i18n.GetMsgWithMap("ErrContainerMsg", map[string]interface{}{"name": strings.Join(errorContainers, ",")})
}
if notFoundCount > 0 {
errMsg += i18n.GetMsgWithMap("ErrContainerNotFound", map[string]interface{}{"name": strings.Join(notFoundContainers, ",")})
}
appInstall.Message = errMsg
return appInstallRepo.Save(context.Background(), &appInstall)
return nil
}
func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value interface{}) error {
+15 -4
View File
@@ -354,8 +354,8 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
websiteApp, _ := appRepo.GetFirst(commonRepo.WithByID(websiteAppInstall.AppId))
if websiteApp.Type == constant.RuntimePHP {
go func() {
_, _ = compose.Down(install.GetComposePath())
_ = op.DeleteDir(install.GetPath())
_, _ = compose.Down(websiteAppInstall.GetComposePath())
_ = op.DeleteDir(websiteAppInstall.GetPath())
}()
_ = appInstallRepo.Delete(ctx, websiteAppInstall)
}
@@ -687,6 +687,12 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
envParams[k] = strconv.Itoa(int(t))
case int:
envParams[k] = strconv.Itoa(t)
case []interface{}:
strArray := make([]string, len(t))
for i := range t {
strArray[i] = strings.ToLower(fmt.Sprintf("%v", t[i]))
}
envParams[k] = strings.Join(strArray, ",")
default:
envParams[k] = t.(string)
}
@@ -897,11 +903,12 @@ func rebuildApp(appInstall model.AppInstall) error {
_ = handleErr(appInstall, err, out)
return
}
out, err = compose.Up(dockerComposePath)
out, err = compose.Up(appInstall.GetComposePath())
if err != nil {
_ = handleErr(appInstall, err, out)
return
}
appInstall.Status = constant.Running
_ = appInstallRepo.Save(context.Background(), &appInstall)
}()
@@ -1040,8 +1047,8 @@ func handleErr(install model.AppInstall, err error, out string) error {
if out != "" {
install.Message = out
reErr = errors.New(out)
install.Status = constant.Error
}
install.Status = constant.Error
_ = appInstallRepo.Save(context.Background(), &install)
return reErr
}
@@ -1052,6 +1059,10 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
if updated && (installed.App.Type == "php" || installed.Status == constant.Installing || (installed.App.Key == constant.AppMysql && installed.Version == "5.6.51")) {
continue
}
if err := syncAppInstallStatus(&installed); err != nil {
global.LOG.Error("sync app install status error : ", err)
}
installDTO := response.AppInstalledDTO{
AppInstall: installed,
Path: installed.GetPath(),
+2 -2
View File
@@ -133,7 +133,7 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (
sessionUser, err := global.SESSION.Get(sID)
if err != nil {
sID = uuid.New().String()
c.SetCookie(constant.SessionName, sID, 0, "", "", httpsSetting.Value == "enable", false)
c.SetCookie(constant.SessionName, sID, 0, "", "", httpsSetting.Value == "enable", true)
err := global.SESSION.Set(sID, sessionUser, lifeTime)
if err != nil {
return nil, err
@@ -154,7 +154,7 @@ func (u *AuthService) LogOut(c *gin.Context) error {
}
sID, _ := c.Cookie(constant.SessionName)
if sID != "" {
c.SetCookie(constant.SessionName, sID, -1, "", "", httpsSetting.Value == "enable", false)
c.SetCookie(constant.SessionName, sID, -1, "", "", httpsSetting.Value == "enable", true)
err := global.SESSION.Delete(sID)
if err != nil {
return err
+4 -2
View File
@@ -268,11 +268,13 @@ func (u *BackupService) BatchDeleteRecord(ids []uint) error {
for _, record := range records {
backupAccount, err := backupRepo.Get(commonRepo.WithByType(record.Source))
if err != nil {
return err
global.LOG.Errorf("load backup account %s info from db failed, err: %v", record.Source, err)
continue
}
client, err := u.NewClient(&backupAccount)
if err != nil {
return err
global.LOG.Errorf("new client for backup account %s failed, err: %v", record.Source, err)
continue
}
if _, err = client.Delete(path.Join(record.FileDir, record.FileName)); err != nil {
global.LOG.Errorf("remove file %s from %s failed, err: %v", path.Join(record.FileDir, record.FileName), record.Source, err)
+2 -2
View File
@@ -434,7 +434,7 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
data.AutoRemove = oldContainer.HostConfig.AutoRemove
data.Privileged = oldContainer.HostConfig.Privileged
data.PublishAllPorts = oldContainer.HostConfig.PublishAllPorts
data.RestartPolicy = oldContainer.HostConfig.RestartPolicy.Name
data.RestartPolicy = string(oldContainer.HostConfig.RestartPolicy.Name)
if oldContainer.HostConfig.NanoCPUs != 0 {
data.NanoCPUs = float64(oldContainer.HostConfig.NanoCPUs) / 1000000000
}
@@ -985,7 +985,7 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
hostConf.AutoRemove = req.AutoRemove
hostConf.CPUShares = req.CPUShares
hostConf.PublishAllPorts = req.PublishAllPorts
hostConf.RestartPolicy = container.RestartPolicy{Name: req.RestartPolicy}
hostConf.RestartPolicy = container.RestartPolicy{Name: container.RestartPolicyMode(req.RestartPolicy)}
if req.RestartPolicy == "on-failure" {
hostConf.RestartPolicy.MaximumRetryCount = 5
}
+1 -1
View File
@@ -181,7 +181,7 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
} else {
daemonMap["iptables"] = false
}
case "Dirver":
case "Driver":
if opts, ok := daemonMap["exec-opts"]; ok {
if optsValue, isArray := opts.([]interface{}); isArray {
for i := 0; i < len(optsValue); i++ {
+19 -6
View File
@@ -37,7 +37,10 @@ func getNginxFull(website *model.Website) (dto.NginxFull, error) {
if err != nil {
return nginxFull, err
}
config := parser.NewStringParser(string(content)).Parse()
config, err := parser.NewStringParser(string(content)).Parse()
if err != nil {
return dto.NginxFull{}, err
}
config.FilePath = nginxConfig.FilePath
nginxConfig.OldContent = string(content)
nginxConfig.Config = config
@@ -54,7 +57,10 @@ func getNginxFull(website *model.Website) (dto.NginxFull, error) {
if err != nil {
return nginxFull, err
}
siteConfig := parser.NewStringParser(string(siteNginxContent)).Parse()
siteConfig, err := parser.NewStringParser(string(siteNginxContent)).Parse()
if err != nil {
return dto.NginxFull{}, err
}
siteConfig.FilePath = siteConfigPath
siteNginxConfig.Config = siteConfig
siteNginxConfig.OldContent = string(siteNginxContent)
@@ -156,15 +162,22 @@ func deleteNginxConfig(scope string, params []dto.NginxParam, website *model.Web
}
func getNginxParamsFromStaticFile(scope dto.NginxKey, newParams []dto.NginxParam) []dto.NginxParam {
newConfig := &components.Config{}
var (
newConfig = &components.Config{}
err error
)
updateScope := "in"
switch scope {
case dto.SSL:
newConfig = parser.NewStringParser(string(nginx_conf.SSL)).Parse()
newConfig, err = parser.NewStringParser(string(nginx_conf.SSL)).Parse()
case dto.CACHE:
newConfig = parser.NewStringParser(string(nginx_conf.Cache)).Parse()
newConfig, err = parser.NewStringParser(string(nginx_conf.Cache)).Parse()
case dto.ProxyCache:
newConfig = parser.NewStringParser(string(nginx_conf.ProxyCache)).Parse()
newConfig, err = parser.NewStringParser(string(nginx_conf.ProxyCache)).Parse()
}
if err != nil {
return nil
}
for _, dir := range newConfig.GetDirectives() {
addParam := dto.NginxParam{
+2 -2
View File
@@ -199,7 +199,7 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
_ = os.Remove(path.Join(secretDir, "server.crt"))
_ = os.Remove(path.Join(secretDir, "server.key"))
sID, _ := c.Cookie(constant.SessionName)
c.SetCookie(constant.SessionName, sID, 0, "", "", false, false)
c.SetCookie(constant.SessionName, sID, 0, "", "", false, true)
go func() {
_, err := cmd.Exec("systemctl restart 1panel.service")
@@ -294,7 +294,7 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
}
sID, _ := c.Cookie(constant.SessionName)
c.SetCookie(constant.SessionName, sID, 0, "", "", true, false)
c.SetCookie(constant.SessionName, sID, 0, "", "", true, true)
go func() {
time.Sleep(1 * time.Second)
_, err := cmd.Exec("systemctl restart 1panel.service")
+29 -6
View File
@@ -234,7 +234,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
}
isReTry = false
}
_ = u.saveJson(snapJson, rootDir)
_ = u.saveJson(snapJson, u.OriginalPath)
_, _ = cmd.Exec("systemctl stop docker")
if !isReTry || snap.InterruptStep == "DockerDir" {
@@ -245,6 +245,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
isReTry = false
}
} else {
_ = u.saveJson(snapJson, u.OriginalPath)
if !isReTry || snap.InterruptStep == "DockerDir" {
if err := u.handleDockerDatasWithSave(fileOp, operation, rootDir, ""); err != nil {
updateRecoverStatus(snap.ID, "DockerDir", constant.StatusFailed, err.Error())
@@ -337,13 +338,14 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error {
defer func() {
global.Cron.Start()
}()
snapJson, err := u.readFromJson(fmt.Sprintf("%s/snapshot.json", rootDir))
if err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, fmt.Sprintf("decompress file failed, err: %v", err))
u.OriginalPath = fmt.Sprintf("%s/1panel_original/original_%s", global.CONF.System.BaseDir, snap.Name)
if _, err := os.Stat(u.OriginalPath); err != nil && os.IsNotExist(err) {
return
}
u.OriginalPath = fmt.Sprintf("%s/1panel_original/original_%s", snapJson.OldBaseDir, snap.Name)
if _, err := os.Stat(u.OriginalPath); err != nil && os.IsNotExist(err) {
snapJson, err := u.readFromJson(fmt.Sprintf("%s/snapshot.json", u.OriginalPath))
if err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, fmt.Sprintf("read snapjson file failed, err: %v", err))
return
}
@@ -625,20 +627,41 @@ func (u *SnapshotService) handleDockerDatas(fileOp files.FileOp, operation strin
func (u *SnapshotService) handleDockerDatasWithSave(fileOp files.FileOp, operation, source, target string) error {
switch operation {
case "snapshot":
var wg sync.WaitGroup
wg.Add(1)
var status model.SnapshotStatus
go snapAppData(snapHelper{Wg: &wg, Status: &status}, u.OriginalPath)
wg.Wait()
if status.AppData != constant.StatusDone {
return errors.New(status.AppData)
}
case "recover":
if err := u.handleDockerDatasWithSave(fileOp, "snapshot", "", u.OriginalPath); err != nil {
return fmt.Errorf("backup docker data failed, err: %v", err)
}
if _, err := os.Stat(path.Join(source, "docker/docker_image.tar")); err != nil {
global.LOG.Debug("no such docker images in snapshot")
return nil
}
std, err := cmd.Execf("docker load < %s", path.Join(source, "docker/docker_image.tar"))
if err != nil {
return errors.New(std)
}
case "re-recover":
if _, err := os.Stat(path.Join(source, "docker/docker_image.tar")); err != nil {
global.LOG.Debug("no such docker images in snapshot")
return nil
}
std, err := cmd.Execf("docker load < %s", path.Join(source, "docker/docker_image.tar"))
if err != nil {
return errors.New(std)
}
case "rollback":
if _, err := os.Stat(path.Join(source, "docker/docker_image.tar")); err != nil {
global.LOG.Debug("no such docker images in snapshot")
return nil
}
std, err := cmd.Execf("docker load < %s", path.Join(source, "docker_image.tar"))
if err != nil {
return errors.New(std)
+13 -11
View File
@@ -97,21 +97,23 @@ func snapAppData(snap snapHelper, targetDir string) {
}
}
}
for _, rumtime := range runtimes {
for _, runtime := range runtimes {
for _, existImage := range existImages {
if rumtime.Image == existImage && !duplicateMap[rumtime.Image] {
imageSaveList = append(imageSaveList, rumtime.Image)
duplicateMap[rumtime.Image] = true
if runtime.Image == existImage && !duplicateMap[runtime.Image] {
imageSaveList = append(imageSaveList, runtime.Image)
duplicateMap[runtime.Image] = true
}
}
}
global.LOG.Debugf("docker save %s | gzip -c > %s", strings.Join(imageSaveList, " "), path.Join(targetDir, "docker_image.tar"))
std, err := cmd.Execf("docker save %s | gzip -c > %s", strings.Join(imageSaveList, " "), path.Join(targetDir, "docker_image.tar"))
if err != nil {
snap.Status.AppData = err.Error()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"app_data": std})
return
if len(imageSaveList) != 0 {
global.LOG.Debugf("docker save %s | gzip -c > %s", strings.Join(imageSaveList, " "), path.Join(targetDir, "docker_image.tar"))
std, err := cmd.Execf("docker save %s | gzip -c > %s", strings.Join(imageSaveList, " "), path.Join(targetDir, "docker_image.tar"))
if err != nil {
snap.Status.AppData = err.Error()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"app_data": std})
return
}
}
snap.Status.AppData = constant.StatusDone
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"app_data": constant.StatusDone})
@@ -121,7 +123,7 @@ func snapBackup(snap snapHelper, localDir, targetDir string) {
defer snap.Wg.Done()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"backup_data": constant.Running})
status := constant.StatusDone
if err := handleSnapTar(localDir, targetDir, "1panel_backup.tar.gz", "./system;"); err != nil {
if err := handleSnapTar(localDir, targetDir, "1panel_backup.tar.gz", "./system;./system_snapshot;"); err != nil {
status = err.Error()
}
snap.Status.BackupData = status
+2 -2
View File
@@ -261,8 +261,8 @@ func loadArch() (string, error) {
if std == "armv7l\n" {
return "armv7", nil
}
return "", fmt.Errorf("unsupport such arch: arm-%s", std)
return "", fmt.Errorf("unsupported such arch: arm-%s", std)
default:
return "", fmt.Errorf("unsupport such arch: %s", runtime.GOARCH)
return "", fmt.Errorf("unsupported such arch: %s", runtime.GOARCH)
}
}
+26 -13
View File
@@ -820,30 +820,27 @@ func (w WebsiteService) PreInstallCheck(req request.WebsiteInstallCheckReq) ([]r
} else {
checkIds = append(req.InstallIds, appInstall.ID)
}
for _, id := range checkIds {
if err := syncByID(id); err != nil {
return nil, err
}
}
if len(checkIds) > 0 {
installList, _ := appInstallRepo.ListBy(commonRepo.WithIdsIn(checkIds))
for _, install := range installList {
if err = syncAppInstallStatus(&install); err != nil {
return nil, err
}
res = append(res, response.WebsitePreInstallCheck{
Name: install.Name,
Status: install.Status,
Version: install.Version,
AppName: install.App.Name,
})
if install.Status != constant.StatusRunning {
if install.Status != constant.Running {
showErr = true
}
}
}
if showErr {
return res, nil
} else {
return nil, nil
}
return nil, nil
}
func (w WebsiteService) GetWafConfig(req request.WebsiteWafReq) (response.WebsiteWafConfig, error) {
@@ -1511,13 +1508,19 @@ func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error)
switch req.Operate {
case "create":
config = parser.NewStringParser(string(nginx_conf.Proxy)).Parse()
config, err = parser.NewStringParser(string(nginx_conf.Proxy)).Parse()
if err != nil {
return
}
case "edit":
par, err = parser.NewParser(includePath)
if err != nil {
return
}
config = par.Parse()
config, err = par.Parse()
if err != nil {
return
}
oldContent, err = fileOp.GetContent(includePath)
if err != nil {
return
@@ -1533,6 +1536,7 @@ func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error)
_ = fileOp.Rename(backPath, includePath)
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
}
config.FilePath = includePath
directives := config.Directives
location, ok := directives[0].(*components.Location)
@@ -1610,7 +1614,10 @@ func (w WebsiteService) GetProxies(id uint) (res []request.WebsiteProxyConfig, e
return
}
proxyConfig.Content = string(content)
config = parser.NewStringParser(string(content)).Parse()
config, err = parser.NewStringParser(string(content)).Parse()
if err != nil {
return nil, err
}
directives := config.GetDirectives()
location, ok := directives[0].(*components.Location)
@@ -2057,7 +2064,10 @@ func (w WebsiteService) OperateRedirect(req request.NginxRedirectReq) (err error
if err != nil {
return
}
config = oldPar.Parse()
config, err = oldPar.Parse()
if err != nil {
return
}
oldContent, err = fileOp.GetContent(includePath)
if err != nil {
return
@@ -2198,7 +2208,10 @@ func (w WebsiteService) GetRedirect(id uint) (res []response.NginxRedirectConfig
return
}
redirectConfig.Content = string(content)
config = parser.NewStringParser(string(content)).Parse()
config, err = parser.NewStringParser(string(content)).Parse()
if err != nil {
return
}
dirs := config.GetDirectives()
if len(dirs) > 0 {
+12 -3
View File
@@ -132,7 +132,10 @@ func createProxyFile(website *model.Website, runtime *model.Runtime) error {
return err
}
}
config := parser.NewStringParser(string(nginx_conf.Proxy)).Parse()
config, err := parser.NewStringParser(string(nginx_conf.Proxy)).Parse()
if err != nil {
return err
}
config.FilePath = filePath
directives := config.Directives
location, ok := directives[0].(*components.Location)
@@ -208,7 +211,10 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
nginxFileName := website.Alias + ".conf"
configPath := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, "conf", "conf.d", nginxFileName)
nginxContent := string(nginx_conf.WebsiteDefault)
config := parser.NewStringParser(nginxContent).Parse()
config, err := parser.NewStringParser(nginxContent).Parse()
if err != nil {
return err
}
servers := config.FindServers()
if len(servers) == 0 {
return errors.New("nginx config is not valid")
@@ -872,7 +878,10 @@ func ChangeHSTSConfig(enable bool, nginxInstall model.AppInstall, website model.
if err != nil {
return err
}
config := par.Parse()
config, err := par.Parse()
if err != nil {
return err
}
config.FilePath = path
directives := config.Directives
location, ok := directives[0].(*components.Location)
+1 -1
View File
@@ -12,7 +12,7 @@ const (
Rebuilding = "Rebuilding"
Syncing = "Syncing"
SyncSuccess = "SyncSuccess"
SyncErr = "SyncErr"
Paused = "Paused"
ContainerPrefix = "1Panel-"
+3
View File
@@ -5,6 +5,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/init/cache/badger_db"
"github.com/1Panel-dev/1Panel/backend/init/session/psession"
"github.com/go-playground/validator/v10"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/robfig/cron/v3"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
@@ -23,4 +24,6 @@ var (
Cron *cron.Cron
MonitorCronID cron.EntryID
OneDriveCronID cron.EntryID
I18n *i18n.Localizer
)
+25 -27
View File
@@ -2,9 +2,9 @@ package i18n
import (
"embed"
"github.com/1Panel-dev/1Panel/backend/global"
"strings"
ginI18n "github.com/gin-contrib/i18n"
"github.com/gin-gonic/gin"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
@@ -12,13 +12,13 @@ import (
)
func GetMsgWithMap(key string, maps map[string]interface{}) string {
content := ""
var content string
if maps == nil {
content = ginI18n.MustGetMessage(&i18n.LocalizeConfig{
content, _ = global.I18n.Localize(&i18n.LocalizeConfig{
MessageID: key,
})
} else {
content = ginI18n.MustGetMessage(&i18n.LocalizeConfig{
content, _ = global.I18n.Localize(&i18n.LocalizeConfig{
MessageID: key,
TemplateData: maps,
})
@@ -32,13 +32,13 @@ func GetMsgWithMap(key string, maps map[string]interface{}) string {
}
func GetErrMsg(key string, maps map[string]interface{}) string {
content := ""
var content string
if maps == nil {
content = ginI18n.MustGetMessage(&i18n.LocalizeConfig{
content, _ = global.I18n.Localize(&i18n.LocalizeConfig{
MessageID: key,
})
} else {
content = ginI18n.MustGetMessage(&i18n.LocalizeConfig{
content, _ = global.I18n.Localize(&i18n.LocalizeConfig{
MessageID: key,
TemplateData: maps,
})
@@ -47,7 +47,7 @@ func GetErrMsg(key string, maps map[string]interface{}) string {
}
func GetMsgByKey(key string) string {
content := ginI18n.MustGetMessage(&i18n.LocalizeConfig{
content, _ := global.I18n.Localize(&i18n.LocalizeConfig{
MessageID: key,
})
return content
@@ -55,24 +55,22 @@ func GetMsgByKey(key string) string {
//go:embed lang/*
var fs embed.FS
var bundle *i18n.Bundle
func GinI18nLocalize() gin.HandlerFunc {
return ginI18n.Localize(
ginI18n.WithBundle(&ginI18n.BundleCfg{
RootPath: "./lang",
AcceptLanguage: []language.Tag{language.Chinese, language.English, language.TraditionalChinese},
DefaultLanguage: language.Chinese,
FormatBundleFile: "yaml",
UnmarshalFunc: yaml.Unmarshal,
Loader: &ginI18n.EmbedLoader{FS: fs},
}),
ginI18n.WithGetLngHandle(
func(context *gin.Context, defaultLng string) string {
lng := context.GetHeader("Accept-Language")
if lng == "" {
return defaultLng
}
return lng
},
))
func UseI18n() gin.HandlerFunc {
return func(context *gin.Context) {
lang := context.GetHeader("Accept-Language")
if lang == "" {
lang = "zh"
}
global.I18n = i18n.NewLocalizer(bundle, lang)
}
}
func Init() {
bundle = i18n.NewBundle(language.Chinese)
bundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal)
_, _ = bundle.LoadMessageFileFS(fs, "lang/zh.yaml")
_, _ = bundle.LoadMessageFileFS(fs, "lang/en.yaml")
_, _ = bundle.LoadMessageFileFS(fs, "lang/zh-Hant.yaml")
}
+1
View File
@@ -59,6 +59,7 @@ ErrVersionTooLow: 'The current 1Panel version is too low to update the app store
ErrAppNameExist: 'App name is already exist'
AppStoreIsSyncing: 'The App Store is syncing, please try again later'
ErrGetCompose: "Failed to obtain docker-compose.yml file! {{ .detail }}"
ErrAppWarn: "Abnormal status, please check the log"
#file
ErrFileCanNotRead: "File can not read"
+1
View File
@@ -59,6 +59,7 @@ ErrVersionTooLow: '當前 1Panel 版本過低,無法更新應用商店,請
ErrAppNameExist: '應用名稱已存在'
AppStoreIsSyncing: '應用程式商店正在同步中,請稍後再試'
ErrGetCompose: "docker-compose.yml 檔案取得失敗!{{ .detail }}"
ErrAppWarn: "狀態異常,請查看日誌"
#file
ErrFileCanNotRead: "此文件不支持預覽"
+1
View File
@@ -59,6 +59,7 @@ ErrVersionTooLow: '当前 1Panel 版本过低,无法更新应用商店,请
ErrAppNameExist: '应用名称已存在'
AppStoreIsSyncing: '应用商店正在同步中,请稍后再试'
ErrGetCompose: "docker-compose.yml 文件获取失败!{{ .detail }}"
ErrAppWarn: "状态异常,请查看日志"
#file
ErrFileCanNotRead: "此文件不支持预览"
+1 -1
View File
@@ -23,7 +23,7 @@ func syncApp() {
func syncInstalledApp() {
if err := service.NewIAppInstalledService().SyncAll(true); err != nil {
global.LOG.Errorf("sync instaled app error: %s", err.Error())
global.LOG.Errorf("sync installed app error: %s", err.Error())
}
}
-13
View File
@@ -5,7 +5,6 @@ import (
"time"
"github.com/dgraph-io/badger/v4"
"github.com/pkg/errors"
)
type Cache struct {
@@ -18,18 +17,6 @@ func NewCacheDB(db *badger.DB) *Cache {
}
}
func (c *Cache) SetNX(key string, value interface{}) error {
err := c.db.Update(func(txn *badger.Txn) error {
_, err := txn.Get([]byte(key))
if errors.Is(err, badger.ErrKeyNotFound) {
v := []byte(fmt.Sprintf("%v", value))
return txn.Set([]byte(key), v)
}
return err
})
return err
}
func (c *Cache) Set(key string, value interface{}) error {
err := c.db.Update(func(txn *badger.Txn) error {
v := []byte(fmt.Sprintf("%v", value))
+2 -2
View File
@@ -17,13 +17,13 @@ import (
func Init() {
if _, err := os.Stat(global.CONF.System.DbPath); err != nil {
if err := os.MkdirAll(global.CONF.System.DbPath, os.ModePerm); err != nil {
panic(fmt.Errorf("init db dir falied, err: %v", err))
panic(fmt.Errorf("init db dir failed, err: %v", err))
}
}
fullPath := global.CONF.System.DbPath + "/" + global.CONF.System.DbFile
if _, err := os.Stat(fullPath); err != nil {
if _, err := os.Create(fullPath); err != nil {
panic(fmt.Errorf("init db file falied, err: %v", err))
panic(fmt.Errorf("init db file failed, err: %v", err))
}
}
+2 -2
View File
@@ -56,8 +56,8 @@ type MineFormatter struct{}
func (s *MineFormatter) Format(entry *logrus.Entry) ([]byte, error) {
detailInfo := ""
if entry.Caller != nil {
funcion := strings.ReplaceAll(entry.Caller.Function, "github.com/1Panel-dev/1Panel/backend/", "")
detailInfo = fmt.Sprintf("(%s: %d)", funcion, entry.Caller.Line)
function := strings.ReplaceAll(entry.Caller.Function, "github.com/1Panel-dev/1Panel/backend/", "")
detailInfo = fmt.Sprintf("(%s: %d)", function, entry.Caller.Line)
}
if len(entry.Data) == 0 {
msg := fmt.Sprintf("[%s] [%s] %s %s \n", time.Now().Format(TimeFormat), strings.ToUpper(entry.Level.String()), entry.Message, detailInfo)
+3 -3
View File
@@ -330,7 +330,7 @@ var UpdateCronjobSpec = &gormigrate.Migration{
Source: snap.From,
BackupType: snap.From,
BaseModel: model.BaseModel{
CreatedAt: job.CreatedAt,
CreatedAt: record.CreatedAt,
},
}
_ = tx.Create(&item).Error
@@ -348,7 +348,7 @@ var UpdateCronjobSpec = &gormigrate.Migration{
Source: mapAccount[uint(job.TargetDirID)].Type,
BackupType: mapAccount[uint(job.TargetDirID)].Type,
BaseModel: model.BaseModel{
CreatedAt: job.CreatedAt,
CreatedAt: record.CreatedAt,
},
}
_ = tx.Create(&item).Error
@@ -364,7 +364,7 @@ var UpdateCronjobSpec = &gormigrate.Migration{
FileName: path.Base(record.File),
BackupType: mapAccount[uint(job.TargetDirID)].Type,
BaseModel: model.BaseModel{
CreatedAt: job.CreatedAt,
CreatedAt: record.CreatedAt,
},
}
if record.FromLocal {
+1 -6
View File
@@ -9,11 +9,9 @@ import (
"github.com/1Panel-dev/1Panel/cmd/server/docs"
"github.com/1Panel-dev/1Panel/cmd/server/web"
"github.com/gin-contrib/gzip"
ginI18n "github.com/gin-contrib/i18n"
"github.com/gin-gonic/gin"
swaggerfiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"html/template"
"net/http"
)
@@ -53,10 +51,7 @@ func Routers() *gin.Engine {
c.Writer.Flush()
})
Router.Use(i18n.GinI18nLocalize())
Router.SetFuncMap(template.FuncMap{
"Localize": ginI18n.GetMessage,
})
Router.Use(i18n.UseI18n())
swaggerRouter := Router.Group("1panel")
docs.SwaggerInfo.BasePath = "/api/v1"
+2
View File
@@ -4,6 +4,7 @@ import (
"crypto/tls"
"encoding/gob"
"fmt"
"github.com/1Panel-dev/1Panel/backend/i18n"
"net"
"net/http"
"os"
@@ -31,6 +32,7 @@ import (
func Start() {
viper.Init()
i18n.Init()
log.Init()
db.Init()
migration.Init()
+1 -1
View File
@@ -8,7 +8,7 @@ import (
func loadParamFromVars(key string, vars map[string]interface{}) string {
if _, ok := vars[key]; !ok {
if key != "bucket" {
if key != "bucket" && key != "port" {
global.LOG.Errorf("load param %s from vars failed, err: not exist!", key)
}
return ""
@@ -8,6 +8,7 @@ import (
"path"
"time"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/pkg/sftp"
"golang.org/x/crypto/ssh"
)
@@ -21,6 +22,9 @@ type sftpClient struct {
func NewSftpClient(vars map[string]interface{}) (*sftpClient, error) {
address := loadParamFromVars("address", vars)
port := loadParamFromVars("port", vars)
if len(port) == 0 {
global.LOG.Errorf("load param port from vars failed, err: not exist!")
}
password := loadParamFromVars("password", vars)
username := loadParamFromVars("username", vars)
bucket := loadParamFromVars("bucket", vars)
+2 -10
View File
@@ -3,7 +3,6 @@ package common
import (
"crypto/rand"
"fmt"
"golang.org/x/net/idna"
"io"
mathRand "math/rand"
"net"
@@ -15,6 +14,8 @@ import (
"time"
"unicode"
"golang.org/x/net/idna"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
)
@@ -143,15 +144,6 @@ func ScanPortWithProto(port int, proto string) bool {
return ScanPort(port)
}
func ExistWithStrArray(str string, arr []string) bool {
for _, a := range arr {
if strings.Contains(a, str) {
return true
}
}
return false
}
func IsNum(s string) bool {
_, err := strconv.ParseFloat(s, 64)
return err == nil
+29 -37
View File
@@ -2,14 +2,19 @@ package docker
import (
"context"
"github.com/compose-spec/compose-go/loader"
"github.com/compose-spec/compose-go/types"
"github.com/docker/compose/v2/pkg/api"
"github.com/joho/godotenv"
"path"
"regexp"
"strings"
"time"
"github.com/compose-spec/compose-go/v2/loader"
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/flags"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/compose"
"github.com/docker/docker/client"
"github.com/joho/godotenv"
)
type ComposeService struct {
@@ -17,8 +22,7 @@ type ComposeService struct {
project *types.Project
}
func (s *ComposeService) SetProject(project *types.Project) {
s.project = project
func UpComposeProject(project *types.Project) error {
for i, s := range project.Services {
s.CustomLabels = map[string]string{
api.ProjectLabel: project.Name,
@@ -30,47 +34,35 @@ func (s *ComposeService) SetProject(project *types.Project) {
}
project.Services[i] = s
}
}
func (s *ComposeService) ComposeUp() error {
return s.Up(context.Background(), s.project, api.UpOptions{
apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return err
}
var ops []command.CLIOption
ops = append(ops, command.WithAPIClient(apiClient), command.WithDefaultContextStoreConfig())
cli, err := command.NewDockerCli(ops...)
if err != nil {
return err
}
cliOp := flags.NewClientOptions()
if err = cli.Initialize(cliOp); err != nil {
return err
}
service := compose.NewComposeService(cli)
composeService := ComposeService{Service: service}
return composeService.Up(context.Background(), project, api.UpOptions{
Create: api.CreateOptions{
Timeout: getComposeTimeout(),
},
Start: api.StartOptions{
WaitTimeout: *getComposeTimeout(),
Wait: true,
},
})
}
func (s *ComposeService) ComposeDown() error {
return s.Down(context.Background(), s.project.Name, api.DownOptions{})
}
func (s *ComposeService) ComposeStart() error {
return s.Start(context.Background(), s.project.Name, api.StartOptions{})
}
func (s *ComposeService) ComposeRestart() error {
return s.Restart(context.Background(), s.project.Name, api.RestartOptions{})
}
func (s *ComposeService) ComposeStop() error {
return s.Stop(context.Background(), s.project.Name, api.StopOptions{})
}
func (s *ComposeService) ComposeCreate() error {
return s.Create(context.Background(), s.project, api.CreateOptions{})
}
func (s *ComposeService) ComposeBuild() error {
return s.Build(context.Background(), s.project, api.BuildOptions{})
}
func (s *ComposeService) ComposePull() error {
return s.Pull(context.Background(), s.project, api.PullOptions{})
}
func GetComposeProject(projectName, workDir string, yml []byte, env []byte, skipNormalization bool) (*types.Project, error) {
var configFiles []types.ConfigFile
configFiles = append(configFiles, types.ConfigFile{
+9 -14
View File
@@ -3,6 +3,8 @@ package docker
import (
"context"
"github.com/docker/docker/api/types/container"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
@@ -44,24 +46,17 @@ func NewDockerClient() (*client.Client, error) {
return cli, nil
}
func (c Client) ListAllContainers() ([]types.Container, error) {
var options types.ContainerListOptions
containers, err := c.cli.ContainerList(context.Background(), options)
if err != nil {
return nil, err
}
return containers, nil
}
func (c Client) ListContainersByName(names []string) ([]types.Container, error) {
var (
options types.ContainerListOptions
res []types.Container
options container.ListOptions
namesMap = make(map[string]bool)
res []types.Container
)
options.All = true
if len(names) > 0 {
var array []filters.KeyValuePair
for _, n := range names {
namesMap["/"+n] = true
array = append(array, filters.Arg("name", n))
}
options.Filters = filters.NewArgs(array...)
@@ -70,9 +65,9 @@ func (c Client) ListContainersByName(names []string) ([]types.Container, error)
if err != nil {
return nil, err
}
for _, container := range containers {
if container.Names[0] == "/"+names[0] {
res = append(res, container)
for _, con := range containers {
if _, ok := namesMap[con.Names[0]]; ok {
res = append(res, con)
}
}
return res, nil
-8
View File
@@ -4,10 +4,8 @@ import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"fmt"
"io"
@@ -61,12 +59,6 @@ func StringDecrypt(text string) (string, error) {
return "", err
}
func Md5(str string) string {
h := md5.New()
h.Write([]byte(str))
return hex.EncodeToString(h.Sum(nil))
}
func padding(plaintext []byte, blockSize int) []byte {
padding := blockSize - len(plaintext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
+6 -51
View File
@@ -7,11 +7,6 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
http2 "github.com/1Panel-dev/1Panel/backend/utils/http"
cZip "github.com/klauspost/compress/zip"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
"io"
"io/fs"
"net/http"
@@ -23,6 +18,12 @@ import (
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
http2 "github.com/1Panel-dev/1Panel/backend/utils/http"
cZip "github.com/klauspost/compress/zip"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/mholt/archiver/v4"
"github.com/pkg/errors"
@@ -85,10 +86,6 @@ func (f FileOp) DeleteFile(dst string) error {
return f.Fs.Remove(dst)
}
func (f FileOp) Delete(dst string) error {
return os.RemoveAll(dst)
}
func (f FileOp) CleanDir(dst string) error {
return cmd.ExecCmd(fmt.Sprintf("rm -rf %s/*", dst))
}
@@ -129,14 +126,6 @@ func (f FileOp) SaveFile(dst string, content string, mode fs.FileMode) error {
return nil
}
func (f FileOp) Chmod(dst string, mode fs.FileMode) error {
return f.Fs.Chmod(dst, mode)
}
func (f FileOp) Chown(dst string, uid int, gid int) error {
return f.Fs.Chown(dst, uid, gid)
}
func (f FileOp) ChownR(dst string, uid string, gid string, sub bool) error {
cmdStr := fmt.Sprintf(`chown %s:%s "%s"`, uid, gid, dst)
if sub {
@@ -563,40 +552,6 @@ func (f FileOp) Decompress(srcFile string, dst string, cType CompressType) error
return nil
}
func (f FileOp) Backup(srcFile string) (string, error) {
backupPath := srcFile + "_bak"
info, _ := f.Fs.Stat(backupPath)
if info != nil {
if info.IsDir() {
_ = f.DeleteDir(backupPath)
} else {
_ = f.DeleteFile(backupPath)
}
}
if err := f.Rename(srcFile, backupPath); err != nil {
return backupPath, err
}
return backupPath, nil
}
func (f FileOp) CopyAndBackup(src string) (string, error) {
backupPath := src + "_bak"
info, _ := f.Fs.Stat(backupPath)
if info != nil {
if info.IsDir() {
_ = f.DeleteDir(backupPath)
} else {
_ = f.DeleteFile(backupPath)
}
}
_ = f.CreateDir(backupPath, 0755)
if err := f.Copy(src, backupPath); err != nil {
return backupPath, err
}
return backupPath, nil
}
func ZipFile(files []archiver.File, dst afero.File) error {
zw := zip.NewWriter(dst)
defer zw.Close()
+2 -2
View File
@@ -141,7 +141,7 @@ func (f *Ufw) Port(port FireInfo, operation string) error {
case "drop":
port.Strategy = "deny"
default:
return fmt.Errorf("unsupport strategy %s", port.Strategy)
return fmt.Errorf("unsupported strategy %s", port.Strategy)
}
if cmd.CheckIllegal(port.Protocol, port.Port) {
return buserr.New(constant.ErrCmdIllegal)
@@ -168,7 +168,7 @@ func (f *Ufw) RichRules(rule FireInfo, operation string) error {
case "drop":
rule.Strategy = "deny"
default:
return fmt.Errorf("unsupport strategy %s", rule.Strategy)
return fmt.Errorf("unsupported strategy %s", rule.Strategy)
}
if cmd.CheckIllegal(operation, rule.Protocol, rule.Address, rule.Port) {
-17
View File
@@ -17,20 +17,3 @@ func GetIniValue(filePath, Group, Key string) (string, error) {
}
return startKey.Value(), nil
}
func SetIniValue(filePath, Group, Key, value string) error {
cfg, err := ini.Load(filePath)
if err != nil {
return err
}
service, err := cfg.GetSection(Group)
if err != nil {
return err
}
targetKey := service.Key(Key)
if err != nil {
return err
}
targetKey.SetValue(value)
return cfg.SaveTo(filePath)
}
+2
View File
@@ -9,6 +9,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
)
@@ -50,6 +51,7 @@ func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(conn.Timeout)*time.Second)
defer cancel()
if err := db.PingContext(ctx); err != nil {
global.LOG.Errorf("test mysql conn failed, err: %v", err)
return nil, err
}
if ctx.Err() == context.DeadlineExceeded {
+15 -29
View File
@@ -5,6 +5,7 @@ import (
"crypto/x509"
"errors"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/go-sql-driver/mysql"
)
@@ -101,45 +102,30 @@ var formatMap = map[string]string{
"big5": "big5_chinese_ci",
}
func VerifyPeerCertFunc(pool *x509.CertPool) func([][]byte, [][]*x509.Certificate) error {
return func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
if len(rawCerts) == 0 {
return errors.New("no certificates available to verify")
}
cert, err := x509.ParseCertificate(rawCerts[0])
if err != nil {
return err
}
opts := x509.VerifyOptions{Roots: pool}
if _, err = cert.Verify(opts); err != nil {
return err
}
return nil
}
}
func ConnWithSSL(ssl, skipVerify bool, clientKey, clientCert, rootCert string) (string, error) {
if !ssl {
return "", nil
}
pool := x509.NewCertPool()
tlsConfig := &tls.Config{
InsecureSkipVerify: skipVerify,
}
if len(rootCert) != 0 {
pool := x509.NewCertPool()
if ok := pool.AppendCertsFromPEM([]byte(rootCert)); !ok {
global.LOG.Error("append certs from pem failed")
return "", errors.New("unable to append root cert to pool")
}
tlsConfig.RootCAs = pool
}
cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
if err != nil {
return "", err
if len(clientCert) != 0 && len(clientKey) != 0 {
cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
if err != nil {
return "", err
}
tlsConfig.Certificates = []tls.Certificate{cert}
}
if err := mysql.RegisterTLSConfig("cloudsql", &tls.Config{
RootCAs: pool,
Certificates: []tls.Certificate{cert},
InsecureSkipVerify: skipVerify,
VerifyPeerCertificate: VerifyPeerCertFunc(pool),
}); err != nil {
if err := mysql.RegisterTLSConfig("cloudsql", tlsConfig); err != nil {
global.LOG.Errorf("register tls config failed, err: %v", err)
return "", err
}
return "&tls=cloudsql", nil
+1 -13
View File
@@ -38,18 +38,6 @@ func WithData() DumpOption {
}
}
func WithTables(tables ...string) DumpOption {
return func(option *dumpOption) {
option.tables = tables
}
}
func WithAllTable() DumpOption {
return func(option *dumpOption) {
option.isAllTable = true
}
}
func WithWriter(writer io.Writer) DumpOption {
return func(option *dumpOption) {
option.writer = writer
@@ -152,7 +140,7 @@ func Dump(dns string, opts ...DumpOption) error {
var columns []string
columns, err = lineRows.Columns()
if err != nil {
global.LOG.Errorf("get columes falied, err: %v", err)
global.LOG.Errorf("get columes failed, err: %v", err)
return err
}
columnTypes, err := lineRows.ColumnTypes()
-12
View File
@@ -19,24 +19,12 @@ type sourceOption struct {
}
type SourceOption func(*sourceOption)
func WithDryRun() SourceOption {
return func(o *sourceOption) {
o.dryRun = true
}
}
func WithMergeInsert(size int) SourceOption {
return func(o *sourceOption) {
o.mergeInsert = size
}
}
func WithDebug() SourceOption {
return func(o *sourceOption) {
o.debug = true
}
}
type dbWrapper struct {
DB *sql.DB
debug bool
+9 -3
View File
@@ -1,9 +1,11 @@
package components
type Block struct {
Line int
Comment string
Directives []IDirective
Line int
Comment string
Directives []IDirective
IsLuaBlock bool
LiteralCode string
}
func (b *Block) GetDirectives() []IDirective {
@@ -18,6 +20,10 @@ func (b *Block) GetLine() int {
return b.Line
}
func (b *Block) GetCodeBlock() string {
return b.LiteralCode
}
func (b *Block) FindDirectives(directiveName string) []IDirective {
directives := make([]IDirective, 0)
for _, directive := range b.GetDirectives() {
-13
View File
@@ -5,19 +5,6 @@ type Config struct {
FilePath string
}
func (c *Config) FindDirectives(directiveName string) []IDirective {
return c.Block.FindDirectives(directiveName)
}
func (c *Config) FindUpstreams() []*Upstream {
var upstreams []*Upstream
directives := c.Block.FindDirectives("upstream")
for _, directive := range directives {
upstreams = append(upstreams, directive.(*Upstream))
}
return upstreams
}
func (c *Config) FindServers() []*Server {
var servers []*Server
directives := c.Block.FindDirectives("server")
+4
View File
@@ -11,6 +11,10 @@ type Http struct {
Line int
}
func (h *Http) GetCodeBlock() string {
return ""
}
func (h *Http) GetComment() string {
return h.Comment
}
@@ -22,6 +22,10 @@ type Location struct {
Replaces map[string]string
}
func (l *Location) GetCodeBlock() string {
return ""
}
func NewLocation(directive IDirective) *Location {
location := &Location{
Modifier: "",
+120
View File
@@ -0,0 +1,120 @@
package components
import (
"fmt"
)
type LuaBlock struct {
Directives []IDirective
Name string
Comment string
LuaCode string
Line int
}
func NewLuaBlock(directive IDirective) (*LuaBlock, error) {
if block := directive.GetBlock(); block != nil {
lb := &LuaBlock{
Directives: []IDirective{},
Name: directive.GetName(),
LuaCode: block.GetCodeBlock(),
}
lb.Directives = append(lb.Directives, block.GetDirectives()...)
return lb, nil
}
return nil, fmt.Errorf("%s must have a block", directive.GetName())
}
func (lb *LuaBlock) GetName() string {
return lb.Name
}
func (lb *LuaBlock) GetParameters() []string {
return []string{}
}
func (lb *LuaBlock) GetDirectives() []IDirective {
directives := make([]IDirective, 0)
directives = append(directives, lb.Directives...)
return directives
}
func (lb *LuaBlock) FindDirectives(directiveName string) []IDirective {
directives := make([]IDirective, 0)
for _, directive := range lb.GetDirectives() {
if directive.GetName() == directiveName {
directives = append(directives, directive)
}
if directive.GetBlock() != nil {
directives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)
}
}
return directives
}
func (lb *LuaBlock) GetCodeBlock() string {
return lb.LuaCode
}
func (lb *LuaBlock) GetBlock() IBlock {
return lb
}
func (lb *LuaBlock) GetComment() string {
return lb.Comment
}
func (lb *LuaBlock) RemoveDirective(key string, params []string) {
directives := lb.Directives
var newDirectives []IDirective
for _, dir := range directives {
if dir.GetName() == key {
if len(params) > 0 {
oldParams := dir.GetParameters()
if oldParams[0] == params[0] {
continue
}
} else {
continue
}
}
newDirectives = append(newDirectives, dir)
}
lb.Directives = newDirectives
}
func (lb *LuaBlock) UpdateDirective(key string, params []string) {
if key == "" || len(params) == 0 {
return
}
directives := lb.Directives
index := -1
for i, dir := range directives {
if dir.GetName() == key {
if IsRepeatKey(key) {
oldParams := dir.GetParameters()
if !(len(oldParams) > 0 && oldParams[0] == params[0]) {
continue
}
}
index = i
break
}
}
newDirective := &Directive{
Name: key,
Parameters: params,
}
if index > -1 {
directives[index] = newDirective
} else {
directives = append(directives, newDirective)
}
lb.Directives = directives
}
func (lb *LuaBlock) GetLine() int {
return lb.Line
}
+4
View File
@@ -11,6 +11,10 @@ type Server struct {
Line int
}
func (s *Server) GetCodeBlock() string {
return ""
}
func NewServer(directive IDirective) (*Server, error) {
server := &Server{}
if block := directive.GetBlock(); block != nil {
@@ -7,6 +7,7 @@ type IBlock interface {
UpdateDirective(name string, params []string)
GetComment() string
GetLine() int
GetCodeBlock() string
}
type IDirective interface {
@@ -12,6 +12,10 @@ type Upstream struct {
Line int
}
func (us *Upstream) GetCodeBlock() string {
return ""
}
func (us *Upstream) GetName() string {
return "upstream"
}
+15
View File
@@ -65,6 +65,21 @@ func DumpDirective(d components.IDirective, style *Style) string {
func DumpBlock(b components.IBlock, style *Style, startLine int) string {
var buf bytes.Buffer
if b.GetCodeBlock() != "" {
luaLines := strings.Split(b.GetCodeBlock(), "\n")
for i, line := range luaLines {
if strings.Replace(line, " ", "", -1) == "" {
continue
}
buf.WriteString(line)
if i != len(luaLines)-1 {
buf.WriteString("\n")
}
}
return buf.String()
}
line := startLine
if b.GetLine() > startLine {
for i := 0; i < b.GetLine()-startLine; i++ {
-14
View File
@@ -1,14 +0,0 @@
package nginx
import (
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser"
)
func GetConfig(path string) (*components.Config, error) {
p, err := parser.NewParser(path)
if err != nil {
return nil, err
}
return p.Parse(), nil
}
-21
View File
@@ -1,21 +0,0 @@
package nginx
import (
"fmt"
"testing"
)
func TestNginx(t *testing.T) {
//config, err := GetConfig("/opt/1Panel/data/apps/nginx/nginx-1/conf/conf.d/word-1.conf")
config, err := GetConfig("/opt/1Panel/data/apps/nginx/nginx-new/conf/conf.d/1panel.cloud.conf")
if err != nil {
panic(err)
}
//server := config.FindServers()[0]
//fmt.Println(server)
//serverD := config.FindServers()[0]
//serverD.AddListen("8989", false)
fmt.Println(DumpConfig(config, IndentedStyle))
}
+1 -24
View File
@@ -1,9 +1,5 @@
package flag
import (
"fmt"
)
type Type int
const (
@@ -18,6 +14,7 @@ const (
Comment
Illegal
Regex
LuaCode
)
var (
@@ -46,33 +43,13 @@ type Flag struct {
Column int
}
func (t Flag) String() string {
return fmt.Sprintf("{Type:%s,Literal:\"%s\",Line:%d,Column:%d}", t.Type, t.Literal, t.Line, t.Column)
}
func (t Flag) Lit(literal string) Flag {
t.Literal = literal
return t
}
func (t Flag) EqualTo(t2 Flag) bool {
return t.Type == t2.Type && t.Literal == t2.Literal
}
type Flags []Flag
func (fs Flags) EqualTo(flags Flags) bool {
if len(fs) != len(flags) {
return false
}
for i, t := range fs {
if !t.EqualTo(flags[i]) {
return false
}
}
return true
}
func (t Flag) Is(typ Type) bool {
return t.Type == typ
}
+80 -34
View File
@@ -3,17 +3,18 @@ package parser
import (
"bufio"
"bytes"
"fmt"
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser/flag"
"io"
"strings"
)
type lexer struct {
reader *bufio.Reader
file string
line int
column int
Latest flag.Flag
reader *bufio.Reader
file string
line int
column int
inLuaBlock bool
Latest flag.Flag
}
func lex(content string) *lexer {
@@ -45,6 +46,11 @@ func (s *lexer) scan() flag.Flag {
//}
func (s *lexer) getNextFlag() flag.Flag {
if s.inLuaBlock {
s.inLuaBlock = false
flag := s.scanLuaCode()
return flag
}
retoFlag:
ch := s.peek()
switch {
@@ -56,6 +62,9 @@ retoFlag:
case ch == ';':
return s.NewToken(flag.Semicolon).Lit(string(s.read()))
case ch == '{':
if isLuaBlock(s.Latest) {
s.inLuaBlock = true
}
return s.NewToken(flag.BlockStart).Lit(string(s.read()))
case ch == '}':
return s.NewToken(flag.BlockEnd).Lit(string(s.read()))
@@ -70,6 +79,35 @@ retoFlag:
}
}
func (s *lexer) scanLuaCode() flag.Flag {
ret := s.NewToken(flag.LuaCode)
stack := make([]rune, 0, 50)
code := strings.Builder{}
for {
ch := s.read()
if ch == rune(flag.EOF) {
panic("unexpected end of file while scanning a string, maybe an unclosed lua code?")
}
if ch == '#' {
code.WriteRune(ch)
code.WriteString(s.readUntil(isEndOfLine))
continue
} else if ch == '}' {
if len(stack) == 0 {
_ = s.reader.UnreadRune()
return ret.Lit(strings.TrimRight(strings.Trim(code.String(), "\n"), "\n "))
}
if stack[len(stack)-1] == '{' {
stack = stack[0 : len(stack)-1]
}
} else if ch == '{' {
stack = append(stack, ch)
}
code.WriteRune(ch)
}
}
func (s *lexer) peek() rune {
r, _, _ := s.reader.ReadRune()
_ = s.reader.UnreadRune()
@@ -128,7 +166,7 @@ func (s *lexer) scanComment() flag.Flag {
func (s *lexer) scanQuotedString(delimiter rune) flag.Flag {
var buf bytes.Buffer
tok := s.NewToken(flag.QuotedString)
buf.WriteRune(s.read()) //consume delimiter
_, _ = buf.WriteRune(s.read())
for {
ch := s.read()
@@ -136,29 +174,13 @@ func (s *lexer) scanQuotedString(delimiter rune) flag.Flag {
panic("unexpected end of file while scanning a string, maybe an unclosed quote?")
}
if ch == '\\' {
if needsEscape(s.peek(), delimiter) {
nextch := s.read()
switch nextch {
case 'n':
fmt.Println("n")
buf.WriteRune('\n')
case 'r':
fmt.Println("r")
buf.WriteRune('\r')
case 't':
fmt.Println("t")
buf.WriteRune('\t')
case '\\':
buf.WriteRune('\\')
default:
buf.WriteRune('\\')
buf.WriteRune(nextch)
}
continue
}
if ch == '\\' && (s.peek() == delimiter) {
buf.WriteRune(ch)
buf.WriteRune(s.read())
continue
}
buf.WriteRune(ch)
_, _ = buf.WriteRune(ch)
if ch == delimiter {
break
}
@@ -168,7 +190,31 @@ func (s *lexer) scanQuotedString(delimiter rune) flag.Flag {
}
func (s *lexer) scanKeyword() flag.Flag {
return s.NewToken(flag.Keyword).Lit(s.readUntil(isKeywordTerminator))
var buf bytes.Buffer
tok := s.NewToken(flag.Keyword)
prev := s.read()
buf.WriteRune(prev)
for {
ch := s.peek()
if isSpace(ch) || isEOF(ch) || ch == ';' {
break
}
if ch == '{' {
if prev == '$' {
buf.WriteString(s.readUntil(func(r rune) bool {
return r == '}'
}))
buf.WriteRune(s.read()) //consume latest '}'
} else {
break
}
}
buf.WriteRune(s.read())
}
return tok.Lit(buf.String())
}
func (s *lexer) scanVariable() flag.Flag {
@@ -198,10 +244,6 @@ func isKeywordTerminator(ch rune) bool {
return isSpace(ch) || isEndOfLine(ch) || ch == '{' || ch == ';'
}
func needsEscape(ch, delimiter rune) bool {
return ch == delimiter || ch == 'n' || ch == 't' || ch == '\\' || ch == 'r'
}
func isSpace(ch rune) bool {
return ch == ' ' || ch == '\t' || isEndOfLine(ch)
}
@@ -213,3 +255,7 @@ func isEOF(ch rune) bool {
func isEndOfLine(ch rune) bool {
return ch == '\r' || ch == '\n'
}
func isLuaBlock(t flag.Flag) bool {
return t.Type == flag.Keyword && strings.HasSuffix(t.Literal, "_by_lua_block")
}
+58 -24
View File
@@ -2,17 +2,19 @@ package parser
import (
"bufio"
"errors"
"fmt"
components "github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser/flag"
"os"
"strings"
)
type Parser struct {
lexer *lexer
currentToken flag.Flag
followingToken flag.Flag
blockWrappers map[string]func(*components.Directive) components.IDirective
blockWrappers map[string]func(*components.Directive) (components.IDirective, error)
directiveWrappers map[string]func(*components.Directive) components.IDirective
}
@@ -39,18 +41,21 @@ func NewParserFromLexer(lexer *lexer) *Parser {
parser.nextToken()
parser.nextToken()
parser.blockWrappers = map[string]func(*components.Directive) components.IDirective{
"http": func(directive *components.Directive) components.IDirective {
return parser.wrapHttp(directive)
parser.blockWrappers = map[string]func(*components.Directive) (components.IDirective, error){
"http": func(directive *components.Directive) (components.IDirective, error) {
return parser.wrapHttp(directive), nil
},
"server": func(directive *components.Directive) components.IDirective {
return parser.wrapServer(directive)
"server": func(directive *components.Directive) (components.IDirective, error) {
return parser.wrapServer(directive), nil
},
"location": func(directive *components.Directive) components.IDirective {
return parser.wrapLocation(directive)
"location": func(directive *components.Directive) (components.IDirective, error) {
return parser.wrapLocation(directive), nil
},
"upstream": func(directive *components.Directive) components.IDirective {
return parser.wrapUpstream(directive)
"upstream": func(directive *components.Directive) (components.IDirective, error) {
return parser.wrapUpstream(directive), nil
},
"_by_lua_block": func(directive *components.Directive) (components.IDirective, error) {
return parser.wrapLuaBlock(directive)
},
}
@@ -76,14 +81,19 @@ func (p *Parser) followingTokenIs(t flag.Type) bool {
return p.followingToken.Type == t
}
func (p *Parser) Parse() *components.Config {
return &components.Config{
FilePath: p.lexer.file,
Block: p.parseBlock(),
func (p *Parser) Parse() (*components.Config, error) {
parsedBlock, err := p.parseBlock(false)
if err != nil {
return nil, err
}
c := &components.Config{
FilePath: p.lexer.file,
Block: parsedBlock,
}
return c, err
}
func (p *Parser) parseBlock() *components.Block {
func (p *Parser) parseBlock(inBlock bool) (*components.Block, error) {
context := &components.Block{
Comment: "",
Directives: make([]components.IDirective, 0),
@@ -93,10 +103,22 @@ func (p *Parser) parseBlock() *components.Block {
parsingloop:
for {
switch {
case p.curTokenIs(flag.EOF) || p.curTokenIs(flag.BlockEnd):
case p.curTokenIs(flag.EOF):
if inBlock {
return nil, errors.New("unexpected eof in block")
}
break parsingloop
case p.curTokenIs(flag.BlockEnd):
break parsingloop
case p.curTokenIs(flag.LuaCode):
context.IsLuaBlock = true
context.LiteralCode = p.currentToken.Literal
case p.curTokenIs(flag.Keyword):
context.Directives = append(context.Directives, p.parseStatement())
s, err := p.parseStatement()
if err != nil {
return nil, err
}
context.Directives = append(context.Directives, s)
case p.curTokenIs(flag.Comment):
context.Directives = append(context.Directives, &components.Comment{
Detail: p.currentToken.Literal,
@@ -106,10 +128,10 @@ parsingloop:
p.nextToken()
}
return context
return context, nil
}
func (p *Parser) parseStatement() components.IDirective {
func (p *Parser) parseStatement() (components.IDirective, error) {
d := &components.Directive{
Name: p.currentToken.Literal,
Line: p.currentToken.Line,
@@ -121,30 +143,38 @@ func (p *Parser) parseStatement() components.IDirective {
if p.curTokenIs(flag.Semicolon) {
if dw, ok := p.directiveWrappers[d.Name]; ok {
return dw(d)
return dw(d), nil
}
if p.followingTokenIs(flag.Comment) && p.currentToken.Line == p.followingToken.Line {
d.Comment = p.followingToken.Literal
p.nextToken()
}
return d
return d, nil
}
if p.curTokenIs(flag.BlockStart) {
inLineComment := ""
if p.followingTokenIs(flag.Comment) && p.currentToken.Line == p.followingToken.Line {
inLineComment = p.followingToken.Literal
p.nextToken()
p.nextToken()
}
block := p.parseBlock()
block, err := p.parseBlock(false)
if err != nil {
return nil, err
}
block.Comment = inLineComment
d.Block = block
if strings.HasSuffix(d.Name, "_by_lua_block") {
return p.blockWrappers["_by_lua_block"](d)
}
if bw, ok := p.blockWrappers[d.Name]; ok {
return bw(d)
}
return d
return d, nil
}
panic(fmt.Errorf("unexpected token %s (%s) on line %d, column %d", p.currentToken.Type.String(), p.currentToken.Literal, p.currentToken.Line, p.currentToken.Column))
@@ -169,6 +199,10 @@ func (p *Parser) wrapHttp(directive *components.Directive) *components.Http {
return h
}
func (p *Parser) wrapLuaBlock(directive *components.Directive) (*components.LuaBlock, error) {
return components.NewLuaBlock(directive)
}
func (p *Parser) parseUpstreamServer(directive *components.Directive) *components.UpstreamServer {
return components.NewUpstreamServer(directive)
}
+2 -18
View File
@@ -3,6 +3,8 @@ package ssl
import (
"crypto"
"encoding/json"
"time"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
@@ -21,7 +23,6 @@ import (
"github.com/go-acme/lego/v4/providers/http/webroot"
"github.com/go-acme/lego/v4/registration"
"github.com/pkg/errors"
"time"
)
type AcmeUser struct {
@@ -204,23 +205,6 @@ func (c *AcmeClient) ObtainSSL(domains []string, privateKey crypto.PrivateKey) (
return *certificates, nil
}
func (c *AcmeClient) RenewSSL(certUrl string) (certificate.Resource, error) {
certificates, err := c.Client.Certificate.Get(certUrl, true)
if err != nil {
return certificate.Resource{}, err
}
certificates, err = c.Client.Certificate.RenewWithOptions(*certificates, &certificate.RenewOptions{
Bundle: true,
PreferredChain: "",
MustStaple: true,
})
if err != nil {
return certificate.Resource{}, err
}
return *certificates, nil
}
type Resolve struct {
Key string
Value string
+5 -16
View File
@@ -3,15 +3,16 @@ package websocket
import (
"encoding/json"
"fmt"
"sort"
"strings"
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/net"
"github.com/shirou/gopsutil/v3/process"
"sort"
"strings"
"sync"
"time"
)
type WsInput struct {
@@ -86,18 +87,6 @@ type processConnect struct {
type ProcessConnects []processConnect
func (p ProcessConnects) Len() int {
return len(p)
}
func (p ProcessConnects) Less(i, j int) bool {
return p[i].PID < p[j].PID
}
func (p ProcessConnects) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
type sshSession struct {
Username string `json:"username"`
PID int32 `json:"PID"`
+2 -61
View File
@@ -1,5 +1,5 @@
// Package docs GENERATED BY SWAG; DO NOT EDIT
// This file was generated by swaggo/swag
// Code generated by swaggo/swag. DO NOT EDIT.
package docs
import "github.com/swaggo/swag"
@@ -4954,45 +4954,6 @@ const docTemplate = `{
}
}
},
"/databases/pg/options": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取 postgresql 数据库列表",
"consumes": [
"application/json"
],
"tags": [
"Database Postgresql"
],
"summary": "List postgresql database names",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.PageInfo"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.PostgresqlOption"
}
}
}
}
}
},
"/databases/pg/password": {
"post": {
"security": [
@@ -17203,26 +17164,6 @@ const docTemplate = `{
}
}
},
"dto.PostgresqlOption": {
"type": "object",
"properties": {
"database": {
"type": "string"
},
"from": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"dto.RecordSearch": {
"type": "object",
"required": [
-59
View File
@@ -4947,45 +4947,6 @@
}
}
},
"/databases/pg/options": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取 postgresql 数据库列表",
"consumes": [
"application/json"
],
"tags": [
"Database Postgresql"
],
"summary": "List postgresql database names",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.PageInfo"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.PostgresqlOption"
}
}
}
}
}
},
"/databases/pg/password": {
"post": {
"security": [
@@ -17196,26 +17157,6 @@
}
}
},
"dto.PostgresqlOption": {
"type": "object",
"properties": {
"database": {
"type": "string"
},
"from": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"dto.RecordSearch": {
"type": "object",
"required": [
-37
View File
@@ -2119,19 +2119,6 @@ definitions:
- from
- type
type: object
dto.PostgresqlOption:
properties:
database:
type: string
from:
type: string
id:
type: integer
name:
type: string
type:
type: string
type: object
dto.RecordSearch:
properties:
detailName:
@@ -8140,30 +8127,6 @@ paths:
[description]
formatZH: postgresql 数据库 [name] 描述信息修改 [description]
paramKeys: []
/databases/pg/options:
get:
consumes:
- application/json
description: 获取 postgresql 数据库列表
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.PageInfo'
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/dto.PostgresqlOption'
type: array
security:
- ApiKeyAuth: []
summary: List postgresql database names
tags:
- Database Postgresql
/databases/pg/password:
post:
consumes:
+5 -5
View File
@@ -12,11 +12,11 @@
1Panel is a modern and Open-Source linux server operation and management panel, the functions and advantages of 1Panel include:
- **Quick website building**: Deeply integrated with Wordpress and [Halo](https://github.com/halo-dev/halo/), with one-click solutions for domain name binding, SSL certificate configuration, and more;
- **Efficient management**: Easily manage Linux servers through the web interface, including application management, host monitoring, file management, database management, container management, and more;
- **Secure and reliable**: Minimal vulnerability exposure, with firewall and security audit functions provided;
- **One-click backup**: Support for one-click backup and restore, with backup data stored in the cloud and never lost;
- **App Store**: Curate a variety of high-quality open-source tools and application software, enabling you to easily install and upgrade them.
- **Efficient Management**: Users can easily manage Linux servers through a web graphical interface, achieving functions such as host monitoring, file management, database management, and container management.
- **Rapid Website Building**: Deep integration of open-source website building software WordPress and [Halo](https://github.com/halo-dev/halo/), with operations such as domain binding and SSL certificate configuration completed with one click.
- **Application Store**: Curates a variety of high-quality open-source tools and application software, assisting users in easy installation and upgrades.
- **Security and Reliability**: Based on container management and application deployment, minimizing vulnerability exposure, while providing features such as firewall and log auditing.
- **One-Click Backup**: Supports one-click backup and restoration, allowing users to backup data to various cloud storage media, ensuring data is never lost.
## UI Display
+5 -5
View File
@@ -12,11 +12,11 @@
1Panel はモダンでオープンソースの Linux サーバー操作と管理パネルです:
- **素早いウェブサイト構築**: Wordpress および [Halo](https://github.com/halo-dev/halo/) と深く統合されており、ドメイン名のバインディング、SSL 証明書の設定など、ワンクリックで解決できます;
- **効率的なマネジメント**: アプリケーション管理、ホスト監視、ファイル管理、データベース管理、コンテナ管理など、web インターフェイスを通じて Linux サーバーを簡単に管理できます;
- **安全性と信頼性**: ファイアウォールとセキュリティ監査機能により、脆弱性の露出を最小限に抑えます;
- **ワンクリックバックアップ**: ワンクリックでのバックアップと復元をサポートし、バックアップデータはクラウドに保存され、失われることはありません;
- **アプリストア**: さまざまな種類の高品質なオープンソースツールとアプリケーションソフトウェアを厳選し、インストールとアップグレードを容易にサポートします。
- **効率的管理**:ユーザーは、Web グラフィカルインターフェースを使用して、Linux サーバーを簡単に管理し、ホストの監視、ファイル管理、データベース管理、コンテナ管理などの機能を実現できます。
- **迅速なウェブサイト構築**:オープンソースのウェブサイト構築ソフトウェアであるWordPressと[Halo](https://github.com/halo-dev/halo/)を深く統合し、ドメインのバインディング、SSL 証明書の設定などをワンクリックで完了します。
- **アプリストア**:さまざまな高品質のオープンソースツールやアプリケーションソフトウェアを厳選して提供し、ユーザーが簡単にインストールおよびアップグレードできるよう支援します。
- **安全性と信頼性**:コンテナの管理とアプリケーションのデプロイに基づいて、最小限の脆弱性露出面を実現し、同時にファイアウォールやログ監査などの機能を提供します。
- **ワンクリックバックアップ**:ワンクリックでバックアップと復元をサポートし、ユーザーはデータをさまざまなクラウドストレージメディアにバックアップし、永久に失うことはありません。
## UI ディスプレイ
+5 -5
View File
@@ -13,11 +13,11 @@
1Panel 是一個現代化、開源的 Linux 服務器運維管理面板。1Panel 的功能和優勢包括:
- **快速建站**:深度集成 Wordpress 和 [Halo](https://github.com/halo-dev/halo/),域名綁定、SSL 證書配置等一鍵搞定;
- **高效管理**:通過 Web 端輕鬆管理 Linux 服務器,包括主機監控、文件管理、數據庫管理、容器管理等;
- **安全可靠**:基於容器來管理和部署應用,最小漏洞暴露面,提供防火牆和日誌審計等功能;
- **一鍵備份**:支持一鍵備份和恢復,備份數據到各類雲端存儲,永不丟失;
- **應用商店**:精選各類高質量開源工具和應用軟件,助力您輕鬆安裝和升級。
- **高效管理**:使用者可以透過網頁圖形介面輕鬆管理 Linux 伺服器,實現主機監控、檔案管理、資料庫管理、容器管理等功能;
- **快速建站**:深度整合開源建站軟體 WordPress 和 [Halo](https://github.com/halo-dev/halo/),域名綁定、SSL 憑證配置等操作一鍵搞定;
- **應用商店**:精選上架各類高品質的開源工具和應用軟體,協助使用者輕鬆安裝並升級;
- **安全可靠**:基於容器管理並部署應用,實現最小的漏洞暴露面,同時提供防火牆和日誌審計等功能;
- **一鍵備份**:支援一鍵備份和恢復,使用者可以將資料備份到各類雲端儲存媒介,永不遺失。
## UI 展示
+2 -2
View File
@@ -27,8 +27,8 @@ class RequestHttp {
...config.headers,
};
if (config.url === '/auth/login' || config.url === '/auth/mfalogin') {
let entrace = Base64.encode(globalStore.entrance);
config.headers.EntranceCode = entrace;
let entrance = Base64.encode(globalStore.entrance);
config.headers.EntranceCode = entrance;
}
return {
...config,
+1 -1
View File
@@ -245,7 +245,7 @@ export namespace Container {
path: string;
template: number;
}
export interface ComposeOpration {
export interface ComposeOperation {
name: string;
operation: string;
path: string;
+1 -1
View File
@@ -156,7 +156,7 @@ export const upCompose = (params: Container.ComposeCreate) => {
export const testCompose = (params: Container.ComposeCreate) => {
return http.post<boolean>(`/containers/compose/test`, params);
};
export const composeOperator = (params: Container.ComposeOpration) => {
export const composeOperator = (params: Container.ComposeOperation) => {
return http.post(`/containers/compose/operate`, params);
};
export const composeUpdate = (params: Container.ComposeUpdate) => {
+6 -6
View File
@@ -66,7 +66,7 @@
<span class="warn" v-if="key === 'openresty' && (httpPort != 80 || httpsPort != 443)">
<el-alert class="helper" type="error" :closable="false">
{{ $t('website.openrestryHelper', [httpPort, httpsPort]) }}
{{ $t('website.openrestyHelper', [httpPort, httpsPort]) }}
</el-alert>
</span>
</div>
@@ -76,15 +76,15 @@
<LayoutContent :title="getTitle(key)" :divider="true">
<template #main>
<div class="app-warn">
<div>
<div class="flx-center">
<span>{{ $t('app.checkInstalledWarn', [data.app]) }}</span>
<span @click="goRouter(key)">
<span @click="goRouter(key)" class="flx-align-center">
<el-icon class="ml-2"><Position /></el-icon>
{{ $t('database.goInstall') }}
</span>
<div>
<img src="@/assets/images/no_app.svg" />
</div>
</div>
<div>
<img src="@/assets/images/no_app.svg" />
</div>
</div>
</template>
+1 -1
View File
@@ -164,7 +164,7 @@ const getContent = () => {
}
data.value = res.data;
if (res.data.content != '') {
if (stopSignals.some((singal) => res.data.content.endsWith(singal))) {
if (stopSignals.some((signal) => res.data.content.endsWith(signal))) {
onCloseLog();
}
if (end.value) {
+7 -5
View File
@@ -1,9 +1,11 @@
<template>
<el-tag :type="getType(status)" round effect="light">
{{ $t('commons.status.' + status) }}
<el-icon v-if="loadingIcon(status)" class="is-loading">
<Loading />
</el-icon>
<span class="flx-align-center">
{{ $t('commons.status.' + status) }}
<el-icon v-if="loadingIcon(status)" class="is-loading">
<Loading />
</el-icon>
</span>
</el-tag>
</template>
@@ -13,7 +15,7 @@ import { onMounted, ref } from 'vue';
const props = defineProps({
status: {
type: String,
default: 'runnning',
default: 'running',
},
});
let status = ref('running');
@@ -36,9 +36,9 @@
</template>
<div class="panel-MdEditor">
<el-alert :closable="false">
<span class="line-heigth">{{ $t('setting.versionHelper') }}</span>
<li class="line-heigth">{{ $t('setting.versionHelper1') }}</li>
<li class="line-heigth">{{ $t('setting.versionHelper2') }}</li>
<span class="line-height">{{ $t('setting.versionHelper') }}</span>
<li class="line-height">{{ $t('setting.versionHelper1') }}</li>
<li class="line-height">{{ $t('setting.versionHelper2') }}</li>
</el-alert>
<div class="default-theme" style="margin-left: 20px">
<h2 class="inline-block">{{ $t('app.version') }}</h2>
@@ -161,7 +161,7 @@ onMounted(() => {
text-decoration: none;
letter-spacing: 0.5px;
}
.line-heigth {
.line-height {
line-height: 25px;
}
.panel-MdEditor {
@@ -5,7 +5,7 @@
import { onMounted, nextTick, watch, onBeforeUnmount } from 'vue';
import * as echarts from 'echarts';
import { GlobalStore } from '@/store';
import { computeSizeFromKBs, computeSizeFromMB } from '@/utils/util';
import { computeSizeFromKBs, computeSizeFromKB, computeSizeFromMB } from '@/utils/util';
const globalStore = GlobalStore();
const props = defineProps({
id: {
@@ -141,6 +141,11 @@ function initChart() {
res += item.marker + ' ' + item.seriesName + ':' + computeSizeFromKBs(item.data) + '<br/>';
}
break;
case 'KB':
for (const item of datas) {
res += item.marker + ' ' + item.seriesName + ':' + computeSizeFromKB(item.data) + '<br/>';
}
break;
case 'MB':
for (const item of datas) {
res += item.marker + ' ' + item.seriesName + ':' + computeSizeFromMB(item.data) + '<br/>';
+1 -1
View File
@@ -7,7 +7,7 @@ export enum CompressType {
Xz = 'xz',
}
export enum CompressExtention {
export enum CompressExtension {
zip = '.zip',
gz = '.gz',
bz2 = '.tar.bz2',
+8 -11
View File
@@ -188,7 +188,7 @@ const message = {
nginxDoc: 'Only supports English case, numbers, and .',
appName: 'Support English, numbers, - and _, length 2-30, and cannot start and end with -_',
containerName: 'Supports letters, numbers, -, _ and .; cannot start with - _ or .; length: 2-128',
mirror: 'Support image accelerator addresses that start with http(s)://, English uppercase and lowercase letters, numbers, periods, and hyphens, and there should be no empty lines.',
mirror: 'The mirror acceleration address should start with http(s)://, support English letters (both uppercase and lowercase), numbers, . / and -, and should not contain blank lines.',
disableFunction: 'Only support letters ,underscores,and,',
leechExts: 'Only support letters, numbers and,',
paramSimple: 'Support lowercase letters and numbers, length 1-128',
@@ -821,8 +821,7 @@ const message = {
default_download_path: 'Default Download Link',
saveLocal: 'Retain local backups (the same as the number of cloud storage copies)',
url: 'URL Address',
target: 'Target',
targetHelper: 'Third-party backup accounts are maintained in panel settings.',
targetHelper: 'Backup accounts are maintained in panel settings.',
retainCopies: 'Retain copies',
retainCopiesHelper: 'Number of copies to retain for execution records and logs',
retainCopiesHelper1: 'Number of copies to retain for backup files',
@@ -1069,7 +1068,6 @@ const message = {
hardLink: 'Hard link',
linkPath: 'Link Path',
selectFile: 'Select file',
downloadSuccess: 'Download succeeded',
downloadUrl: 'Download URL',
downloadStart: 'Download start!',
moveSuccess: 'Move success',
@@ -1084,7 +1082,7 @@ const message = {
infoDetail: 'File Properties',
list: 'File List',
sub: 'Subdirectory',
downlodSuccess: 'Download Success',
downloadSuccess: 'Download Success',
theme: 'Theme',
language: 'Language',
eol: 'End Of Line',
@@ -1099,7 +1097,7 @@ const message = {
uploadFailed: '[{0}] File Upload file',
fileUploadStart: 'Uploading [{0}]....',
currentSelect: 'Current Select: ',
unsupportType: 'Unsupported file type',
unsupportedType: 'Unsupported file type',
deleteHelper:
'Are you sure you want to delete the following files? By default, it will enter the recycle bin after deletion',
fileHeper: 'Note: 1. Search results do not support sorting. 2. Folders cannot be sorted by size.',
@@ -1127,6 +1125,7 @@ const message = {
ignoreCertificate: 'Ignore Certificate',
ignoreCertificateHelper:
'Ignoring untrusted certificates during downloads may lead to data leakage or tampering. Please use this option with caution, only when trusting the download source.',
uploadOverLimit: 'The number of files exceeds 1000! Please compress and upload',
},
ssh: {
autoStart: 'Auto Start',
@@ -1244,7 +1243,6 @@ const message = {
createBackupAccount: 'Add {0}',
noTypeForCreate: 'No backup type is currently created',
LOCAL: 'Server disks',
currentPath: 'Current path',
OSS: 'Ali OSS',
S3: 'Amazon S3',
MINIO: 'MINIO',
@@ -1402,7 +1400,6 @@ const message = {
backupData: 'Backup local backup directory for 1Panel',
compress: 'Create snapshot file',
upload: 'Upload snapshot file',
thirdPartySupport: 'Only third-party accounts are supported',
recoverDetail: 'Recover detail',
createSnapshot: 'Create snapshot',
importSnapshot: 'Sync snapshot',
@@ -1552,7 +1549,7 @@ const message = {
less1Minute: 'Less than 1 minute',
appOfficeWebsite: 'Office website',
github: 'Github',
document: 'Cocument',
document: 'Document',
updatePrompt: 'The current application is the latest version',
installPrompt: 'No apps installed yet',
updateHelper: 'Updating parameters may cause the application to fail to start, please operate with caution',
@@ -1837,7 +1834,7 @@ const message = {
changeVersion: 'Switch version',
retainConfig: 'Whether to keep php-fpm.conf and php.ini files',
runDirHelper2: 'Please ensure that the secondary running directory is under the index directory',
openrestryHelper:
openrestyHelper:
'OpenResty default HTTP port: {0} HTTPS port: {1}, which may affect website domain name access and HTTPS forced redirect',
primaryDomainHelper: 'Support domain name: port',
acmeAccountType: 'Account Type',
@@ -2032,7 +2029,7 @@ const message = {
versionHelper: 'PHP version, e.g. v8.0',
buildHelper:
'The more extensions you select, the more CPU will be occupied during the image making process, so avoid selecting all extensions,If there is no extension you want, you can manually enter it and select it',
openrestryWarn: 'PHP needs to be upgraded to OpenResty to version 1.21.4.1 or later to use',
openrestyWarn: 'PHP needs to be upgraded to OpenResty to version 1.21.4.1 or later to use',
toupgrade: 'To Upgrade',
edit: 'Edit runtime',
extendHelper:
+6 -9
View File
@@ -186,7 +186,7 @@ const message = {
nginxDoc: '僅支持英文大小寫,數字,和.',
appName: '支持英文、數字、-和_,長度2-30,並且不能以-_開頭和結尾',
containerName: '支持字母、數字、_-和.,不能以-_或.開頭,長度2-128',
mirror: '支持以 http(s):// 開頭,英文大小寫,數字,. 和 - 的鏡像加速地址,且不能有空行',
mirror: '支持以 http(s):// 開頭,英文大小寫,數字,. / 和 - 的鏡像加速地址,且不能有空行',
disableFunction: '僅支持字母、下劃線和,',
leechExts: '僅支持字母數字和,',
paramSimple: '支持小寫字母和數字,長度 1-128',
@@ -781,8 +781,7 @@ const message = {
default_download_path: '默認下載地址',
saveLocal: '同時保留本地備份(和雲存儲保留份數一致)',
url: 'URL 地址',
target: '備份到',
targetHelper: '第三方備份賬號在面板設置中維護',
targetHelper: '備份賬號可在面板設置中維護',
retainCopies: '保留份數',
retainCopiesHelper: '執行記錄及日誌保留份数',
retainCopiesHelper1: '備份文件保留份数',
@@ -1032,7 +1031,6 @@ const message = {
root: '根目錄',
list: '文件列表',
sub: '子目錄',
downlodSuccess: '下載完成',
theme: '主題',
language: '語言',
eol: '行尾符',
@@ -1046,7 +1044,7 @@ const message = {
uploadFailed: '【{0}】 文件上傳失敗',
fileUploadStart: '正在上傳【{0}】....',
currentSelect: '當前選中: ',
unsupportType: '不支持的文件類型',
unsupportedType: '不支持的文件類型',
deleteHelper: '確定刪除所選檔案? 預設刪除之後將進入回收站?',
fileHeper: '注意:1. 搜尋結果不支援排序功能 2. 資料夾無法依大小排序。',
forceDeleteHelper: '永久刪除檔案(不進入回收站,直接刪除)',
@@ -1071,6 +1069,7 @@ const message = {
ignoreCertificate: '忽略不可信證書',
ignoreCertificateHelper:
'下載時忽略不可信證書可能導致數據洩露或篡改。請謹慎使用此選項,僅在信任下載源的情況下啟用',
uploadOverLimit: '文件數量超過 1000! 請壓縮後上傳',
},
ssh: {
autoStart: '開機自啟',
@@ -1167,7 +1166,6 @@ const message = {
createBackupAccount: '添加 {0}',
noTypeForCreate: '當前無可創建備份類型',
LOCAL: '服務器磁盤',
currentPath: '當前路徑',
OSS: '阿裏雲 OSS',
S3: '亞馬遜 S3 雲存儲',
MINIO: 'MINIO',
@@ -1243,7 +1241,6 @@ const message = {
backupData: '備份 1Panel 本地備份目錄',
compress: '製作快照文件',
upload: '上傳快照文件',
thirdPartySupport: '僅支持第三方賬號',
recoverDetail: '恢復詳情',
createSnapshot: '創建快照',
importSnapshot: '同步快照',
@@ -1723,7 +1720,7 @@ const message = {
changeVersion: '切換版本',
retainConfig: '是否保留 php-fpm.conf 和 php.ini 文件',
runDirHelper2: '請確保二級運行目錄位於 index 目錄下',
openrestryHelper: 'OpenResty 默認 HTTP 端口:{0} HTTPS 端口:{1},可能影響網站域名訪問和 HTTPS 強制跳轉',
openrestyHelper: 'OpenResty 默認 HTTP 端口:{0} HTTPS 端口:{1},可能影響網站域名訪問和 HTTPS 強制跳轉',
primaryDomainHelper: '支援網域:port',
acmeAccountType: '賬號類型',
keyType: '密鑰演算法',
@@ -1900,7 +1897,7 @@ const message = {
version: '版本',
versionHelper: 'PHP的版本,例 v8.0',
buildHelper: '選擇的擴展越多,製作鏡像過程中占用 CPU 越多,請盡量避免選擇全部擴展',
openrestryWarn: 'PHP 需要升級 OpenResty 至 1.21.4.1 版本以上才能使用',
openrestyWarn: 'PHP 需要升級 OpenResty 至 1.21.4.1 版本以上才能使用',
toupgrade: '去升級',
edit: '編輯運行環境',
extendHelper: '列表中不存在的擴展,可以手動輸入之後選擇,例:輸入 sockets ,然後在下拉列表中選擇第一個',
+6 -9
View File
@@ -186,7 +186,7 @@ const message = {
nginxDoc: '仅支持英文大小写,数字,和.',
appName: '支持英文、数字、-和_,长度2-30,并且不能以-_开头和结尾',
containerName: '支持字母、数字、_-和.,不能以-_或.开头,长度2-128',
mirror: '支持以 http(s):// 开头,英文大小写,数字,. 和 - 的镜像加速地址,且不能有空行',
mirror: '支持以 http(s):// 开头,英文大小写,数字,. / 和 - 的镜像加速地址,且不能有空行',
disableFunction: '仅支持字母、下划线和,',
leechExts: '仅支持字母数字和,',
paramSimple: '支持小写字母和数字,长度1-128',
@@ -782,8 +782,7 @@ const message = {
default_download_path: '默认下载地址',
saveLocal: '同时保留本地备份(和云存储保留份数一致)',
url: 'URL 地址',
target: '备份到',
targetHelper: '第三方备份账号在面板设置中维护',
targetHelper: '备份账号可在面板设置中维护',
retainCopies: '保留份数',
retainCopiesHelper: '执行记录及日志保留份数',
retainCopiesHelper1: '备份文件保留份数',
@@ -1033,7 +1032,6 @@ const message = {
root: '根目录',
list: '文件列表',
sub: '子目录',
downlodSuccess: '下载完成',
theme: '主题',
language: '语言',
eol: '行尾符',
@@ -1047,7 +1045,7 @@ const message = {
uploadFailed: '【{0}】 文件上传失败',
fileUploadStart: '正在上传【{0}】....',
currentSelect: '当前选中: ',
unsupportType: '不支持的文件类型',
unsupportedType: '不支持的文件类型',
deleteHelper: '确定删除所选文件? 默认删除之后将进入回收站',
fileHeper: '注意:1. 搜索结果不支持排序功能 2. 文件夹无法按大小排序。',
forceDeleteHelper: '永久删除文件(不进入回收站,直接删除)',
@@ -1072,6 +1070,7 @@ const message = {
ignoreCertificate: '忽略不可信证书',
ignoreCertificateHelper:
'下载时忽略不可信证书可能导致数据泄露或篡改。请谨慎使用此选项,仅在信任下载源的情况下启用',
uploadOverLimit: '文件数量超过 1000!请压缩后上传',
},
ssh: {
autoStart: '开机自启',
@@ -1168,7 +1167,6 @@ const message = {
createBackupAccount: '添加 {0}',
noTypeForCreate: '当前无可创建备份类型',
LOCAL: '服务器磁盘',
currentPath: '当前路径',
OSS: '阿里云 OSS',
S3: '亚马逊 S3 云存储',
MINIO: 'MINIO',
@@ -1244,7 +1242,6 @@ const message = {
backupData: '备份 1Panel 本地备份目录',
compress: '制作快照文件',
upload: '上传快照文件',
thirdPartySupport: '仅支持第三方账号',
recoverDetail: '恢复详情',
createSnapshot: '创建快照',
importSnapshot: '同步快照',
@@ -1723,7 +1720,7 @@ const message = {
changeVersion: '切换版本',
retainConfig: '是否保留 php-fpm.conf 和 php.ini 文件',
runDirHelper2: '请确保二级运行目录位于 index 目录下',
openrestryHelper: 'OpenResty 默认 HTTP 端口:{0} HTTPS 端口 :{1},可能影响网站域名访问和 HTTPS 强制跳转',
openrestyHelper: 'OpenResty 默认 HTTP 端口:{0} HTTPS 端口 :{1},可能影响网站域名访问和 HTTPS 强制跳转',
primaryDomainHelper: '支持域名:端口',
acmeAccountType: '账号类型',
keyType: '密钥算法',
@@ -1900,7 +1897,7 @@ const message = {
version: '版本',
versionHelper: 'PHP的版本,例 v8.0',
buildHelper: '选择的扩展越多,制作镜像过程中占用 CPU 越多,请尽量避免选择全部扩展',
openrestryWarn: 'PHP 需要升级 OpenResty 至 1.21.4.1 版本以上才能使用',
openrestyWarn: 'PHP 需要升级 OpenResty 至 1.21.4.1 版本以上才能使用',
toupgrade: '去升级',
edit: '编辑运行环境',
extendHelper: '列表中不存在的扩展,可以手动输入之后选择,例:输入 sockets ,然后在下拉列表中选择第一个,',
+3 -3
View File
@@ -1,5 +1,5 @@
<template>
<div :class="classObj" class="app-wrapper" v-loading="loading" :element-loading-text="loadinText" fullscreen>
<div :class="classObj" class="app-wrapper" v-loading="loading" :element-loading-text="loadingText" fullscreen>
<div v-if="classObj.mobile && classObj.openSidebar" class="drawer-bg" @click="handleClickOutside" />
<div class="app-sidebar" v-if="!globalStore.isFullScreen">
<Sidebar />
@@ -31,7 +31,7 @@ const globalStore = GlobalStore();
const i18n = useI18n();
const loading = ref(false);
const loadinText = ref();
const loadingText = ref();
const themeConfig = computed(() => globalStore.themeConfig);
const { switchDark } = useTheme();
@@ -84,7 +84,7 @@ const updateDarkMode = async (event: MediaQueryListEvent) => {
const loadStatus = async () => {
loading.value = globalStore.isLoading;
loadinText.value = globalStore.loadingText;
loadingText.value = globalStore.loadingText;
if (loading.value) {
timer = setInterval(async () => {
await getSystemAvailable()
+5 -1
View File
@@ -409,4 +409,8 @@ html {
.p-ml-5 {
margin-left: 5px !important;
}
}
.p-mb-5 {
margin-bottom: 5px !important;
}
+11 -3
View File
@@ -166,6 +166,14 @@ export function computeSizeFromMB(size: number): string {
return (size / Math.pow(num, 3)).toFixed(2) + ' TB';
}
export function computeSizeFromKB(size: number): string {
const num = 1024.0;
if (size < num) return size + ' KB';
if (size < Math.pow(num, 2)) return (size / num).toFixed(2) + ' MB';
if (size < Math.pow(num, 3)) return (size / Math.pow(num, 2)).toFixed(2) + ' GB';
return (size / Math.pow(num, 3)).toFixed(2) + ' TB';
}
export function computeSizeFromKBs(size: number): string {
const num = 1024.0;
if (size < num) return size + ' KB/s';
@@ -193,9 +201,9 @@ let icons = new Map([
['.pdf', 'p-file-pdf'],
]);
export function getIcon(extention: string): string {
if (icons.get(extention) != undefined) {
const icon = icons.get(extention);
export function getIcon(extension: string): string {
if (icons.get(extension) != undefined) {
const icon = icons.get(extension);
return String(icon);
} else {
return 'p-file-normal';
+5 -7
View File
@@ -361,15 +361,13 @@ onMounted(() => {
.app-desc {
margin-top: 8px;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
height: 43px;
.desc {
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
font-size: 14px;
color: var(--el-text-color-regular);
}
@@ -42,7 +42,7 @@
{{ $t('app.toInstall') }}
</el-link>
</span>
<el-select v-model="form[p.envKey]" v-if="p.type == 'select'">
<el-select v-model="form[p.envKey]" v-if="p.type == 'select'" :multiple="p.multiple">
<el-option
v-for="service in p.values"
:key="service.label"
@@ -30,7 +30,11 @@
v-model.number="paramModel.params[p.key]"
:disabled="!p.edit"
></el-input>
<el-select v-model="paramModel.params[p.key]" v-else-if="p.type == 'select'">
<el-select
v-model="paramModel.params[p.key]"
v-else-if="p.type == 'select'"
:multiple="p.multiple"
>
<el-option
v-for="value in p.values"
:key="value.label"
@@ -201,6 +205,7 @@ const get = async () => {
type: d.type,
values: d.values,
showValue: d.showValue,
multiple: d.multiple,
});
rules.params[d.key] = [Rules.requiredInput];
if (d.rule) {
@@ -17,7 +17,7 @@
</div>
</el-col>
<el-col :span="6">
<el-button type="primary" link @click="cancelIngore(app.detailID)">
<el-button type="primary" link @click="cancelIgnore(app.detailID)">
{{ $t('app.cancelIgnore') }}
</el-button>
</el-col>
@@ -61,7 +61,7 @@ const getApps = async () => {
} catch (error) {}
};
const cancelIngore = async (id: number) => {
const cancelIgnore = async (id: number) => {
loading.value = true;
await IgnoreUpgrade({ detailID: id, operate: 'cancel' })
.then(() => {
@@ -72,18 +72,21 @@
<el-button @click="sync" type="primary" plain v-if="mode === 'installed' && data != null">
{{ $t('app.sync') }}
</el-button>
<el-button @click="openIngore" type="primary" plain v-if="mode === 'upgrade'">
<el-button @click="openIgnore" type="primary" plain v-if="mode === 'upgrade'">
{{ $t('app.showIgnore') }}
</el-button>
</template>
<template #main>
<el-alert type="info" :closable="false" v-if="mode === 'installed'">
<template #default>
{{ $t('app.installHelper') }}
<el-link class="ml-5" icon="Position" @click="quickJump()" type="primary">
{{ $t('firewall.quickJump') }}
</el-link>
<template #title>
<span class="flx-align-center">
{{ $t('app.installHelper') }}
<el-link class="ml-5" icon="Position" @click="quickJump()" type="primary">
{{ $t('firewall.quickJump') }}
</el-link>
 
</span>
</template>
</el-alert>
<el-alert type="info" :title="$t('app.upgradeHelper')" :closable="false" v-if="mode === 'upgrade'" />
@@ -222,25 +225,32 @@
</el-button>
</div>
<div class="d-description">
<el-tag class="middle-center">
<el-button class="tagMargin" plain size="small">
{{ $t('app.version') }}:{{ installed.version }}
</el-tag>
<el-tag
class="middle-center"
</el-button>
<el-button
v-if="installed.httpPort > 0"
@click="goDashboard(installed.httpPort, 'http')"
class="tagMargin"
icon="Position"
plain
size="small"
>
<el-icon class="middle-center"><Position /></el-icon>
{{ $t('app.busPort') }}:{{ installed.httpPort }}
</el-tag>
<el-tag
class="middle-center"
</el-button>
<el-button
v-if="installed.httpsPort > 0"
@click="goDashboard(installed.httpsPort, 'https')"
class="tagMargin"
icon="Position"
plain
size="small"
>
<el-icon class="middle-center"><Position /></el-icon>
{{ $t('app.busPort') }}:{{ installed.httpsPort }}
</el-tag>
</el-button>
<div class="description">
<span>
{{ $t('app.alreadyRun') }}: {{ getAge(installed.createdAt) }}
@@ -451,7 +461,7 @@ const openOperate = (row: any, op: string) => {
}
};
const openIngore = () => {
const openIgnore = () => {
ignoreRef.value.acceptParams();
};
@@ -462,6 +472,9 @@ const operate = async () => {
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
search();
setTimeout(() => {
search();
}, 1500);
})
.catch(() => {
search();
@@ -603,7 +616,7 @@ onMounted(() => {
search();
timer = setInterval(() => {
search();
}, 10000 * 6);
}, 1000 * 60);
});
onUnmounted(() => {
@@ -1,7 +1,7 @@
<template>
<div v-loading="loading">
<div v-show="isOnDetail">
<ComposeDetial @back="backList" ref="composeDetailRef" />
<ComposeDetail @back="backList" ref="composeDetailRef" />
</div>
<el-card v-if="dockerStatus != 'Running'" class="mask-prompt">
<span>{{ $t('container.serviceUnavailable') }}</span>
@@ -12,8 +12,8 @@
<LayoutContent v-if="!isOnDetail" :title="$t('container.compose')" :class="{ mask: dockerStatus != 'Running' }">
<template #prompt>
<el-alert type="info" :closable="false">
<template #default>
<span>
<template #title>
<span class="flx-align-center">
<span>{{ $t('container.composeHelper', [baseDir]) }}</span>
<el-button type="primary" link @click="toFolder">
<el-icon>
@@ -97,7 +97,7 @@ import { reactive, onMounted, ref } from 'vue';
import EditDialog from '@/views/container/compose/edit/index.vue';
import CreateDialog from '@/views/container/compose/create/index.vue';
import DeleteDialog from '@/views/container/compose/delete/index.vue';
import ComposeDetial from '@/views/container/compose/detail/index.vue';
import ComposeDetail from '@/views/container/compose/detail/index.vue';
import { loadContainerLog, loadDockerStatus, searchCompose } from '@/api/modules/container';
import i18n from '@/lang';
import { Container } from '@/api/interface/container';
@@ -93,7 +93,7 @@
:row-style="{ height: '65px' }"
style="width: 100%"
:columns="columns"
localKey="contanerColumn"
localKey="containerColumn"
>
<el-table-column type="selection" />
<el-table-column
@@ -215,7 +215,7 @@ const loadData = async () => {
data: netRxDatas.value,
},
],
formatStr: 'KB/s',
formatStr: 'KB',
};
};
const handleClose = async () => {
@@ -44,13 +44,17 @@
{{ $t('commons.button.conn') }}
</el-button>
<el-button v-else @click="onClose()">{{ $t('commons.button.disconn') }}</el-button>
<Terminal style="height: calc(100vh - 302px)" ref="terminalRef"></Terminal>
<Terminal
style="height: calc(100vh - 302px); margin-top: 18px"
ref="terminalRef"
v-if="terminalOpen"
></Terminal>
</el-form>
</el-drawer>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { reactive, ref, nextTick } from 'vue';
import { ElForm, FormInstance } from 'element-plus';
import { Rules } from '@/global/form-rules';
import Terminal from '@/components/terminal/index.vue';
@@ -91,6 +95,7 @@ const initTerm = (formEl: FormInstance | undefined) => {
formEl.validate(async (valid) => {
if (!valid) return;
terminalOpen.value = true;
await nextTick();
terminalRef.value!.acceptParams({
endpoint: '/api/v1/containers/exec',
args: `containerid=${form.containerID}&user=${form.user}&command=${form.command}`,
@@ -1,6 +1,6 @@
<template>
<el-drawer
v-model="drawerVisiable"
v-model="drawerVisible"
:destroy-on-close="true"
@close="handleClose"
:close-on-click-modal="false"
@@ -58,7 +58,7 @@
ref="logRef"
:config="logConfig"
:default-button="false"
v-if="logVisiable"
v-if="logVisible"
:style="'height: calc(100vh - 370px);min-height: 200px'"
/>
</el-col>
@@ -66,7 +66,7 @@
<template #footer>
<span class="dialog-footer">
<el-button @click="drawerVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
<el-button :disabled="buttonDisabled" type="primary" @click="onSubmit(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
@@ -87,10 +87,10 @@ import { ElForm, ElMessage } from 'element-plus';
import { imageBuild } from '@/api/modules/container';
import DrawerHeader from '@/components/drawer-header/index.vue';
const logVisiable = ref<boolean>(false);
const logVisible = ref<boolean>(false);
const extensions = [javascript(), oneDark];
const buttonDisabled = ref(false);
const drawerVisiable = ref(false);
const drawerVisible = ref(false);
const logRef = ref();
const logConfig = reactive({
@@ -111,8 +111,8 @@ const rules = reactive({
dockerfile: [Rules.requiredInput],
});
const acceptParams = async () => {
logVisiable.value = false;
drawerVisiable.value = true;
logVisible.value = false;
drawerVisible.value = true;
form.from = 'path';
form.dockerfile = '';
form.tagStr = '';
@@ -122,7 +122,7 @@ const acceptParams = async () => {
const emit = defineEmits<{ (e: 'search'): void }>();
const handleClose = () => {
drawerVisiable.value = false;
drawerVisible.value = false;
emit('search');
};
@@ -145,9 +145,9 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
};
const loadLogs = () => {
logVisiable.value = false;
logVisible.value = false;
nextTick(() => {
logVisiable.value = true;
logVisible.value = true;
nextTick(() => {
logRef.value.changeTail(true);
});
@@ -18,10 +18,10 @@
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabeld="loading" @click="loadVisible = false">
<el-button :disabled="loading" @click="loadVisible = false">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabeld="loading" type="primary" @click="onSubmit(formRef)">
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
{{ $t('commons.button.import') }}
</el-button>
</span>
@@ -22,14 +22,17 @@
</span>
<div v-if="!withTagAll">
<ul v-for="(item, index) in unTagList" :key="index">
<li>
{{ item.tags[0] || '< none >' }}
<li v-if="item.tags && item.tags[0]">
{{ item.tags[0] }}
</li>
<li v-else>
{{ item.id.replaceAll('sha256:', '').substring(0, 12) }}
</li>
</ul>
</div>
<div v-else>
<ul v-for="(item, index) in unUsedList" :key="index">
<li v-if="item.tags">{{ item.tags.join(', ') }}</li>
<li v-if="item.tags && item.tags[0]">{{ item.tags.join(', ') }}</li>
<li v-else>{{ item.id.replaceAll('sha256:', '').substring(0, 12) }}</li>
</ul>
</div>
@@ -38,7 +38,7 @@
ref="logRef"
:config="logConfig"
:default-button="false"
v-if="logVisiable"
v-if="logVisible"
:style="'height: calc(100vh - 370px);min-height: 200px'"
v-model:loading="loading"
/>
@@ -76,7 +76,7 @@ const form = reactive({
name: '',
});
const logVisiable = ref(false);
const logVisible = ref(false);
const loading = ref(false);
const logRef = ref();
@@ -95,7 +95,7 @@ const dialogData = ref<DialogProps>({
});
const acceptParams = async (params: DialogProps): Promise<void> => {
logVisiable.value = false;
logVisible.value = false;
loading.value = false;
drawerVisible.value = true;
form.tags = params.tags;
@@ -114,7 +114,7 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
formEl.validate(async (valid) => {
if (!valid) return;
const res = await imagePush(form);
logVisiable.value = true;
logVisible.value = true;
logConfig.name = res.data;
loadLogs();
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
@@ -122,9 +122,9 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
};
const loadLogs = () => {
logVisiable.value = false;
logVisible.value = false;
nextTick(() => {
logVisiable.value = true;
logVisible.value = true;
nextTick(() => {
logRef.value.changeTail(true);
});
@@ -35,10 +35,10 @@
<template #footer>
<span class="dialog-footer">
<el-button :disabeld="loading" @click="drawerVisible = false">
<el-button :disabled="loading" @click="drawerVisible = false">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabeld="loading" type="primary" @click="onSubmit(formRef)">
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
{{ $t('container.export') }}
</el-button>
</span>
@@ -43,10 +43,10 @@
<template #footer>
<span class="dialog-footer">
<el-button :disabeld="loading" @click="drawerVisible = false">
<el-button :disabled="loading" @click="drawerVisible = false">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabeld="loading" type="primary" @click="onSubmit(formRef)">
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
{{ $t('commons.button.save') }}
</el-button>
</span>
@@ -66,7 +66,7 @@
</template>
</el-input>
<span class="input-help">{{ $t('container.mirrorsHelper') }}</span>
<span class="input-help">
<span class="input-help flx-align-center" style="display: flex">
{{ $t('container.mirrorsHelper2') }}
<el-link
style="font-size: 12px; margin-left: 5px"
@@ -414,7 +414,7 @@ const handleCgroup = async () => {
confirmDialogRefCgroup.value!.acceptParams(params);
};
const onSubmitSaveCgroup = () => {
save('Dirver', form.cgroupDriver);
save('Driver', form.cgroupDriver);
};
const save = async (key: string, value: string) => {
@@ -67,7 +67,7 @@ const rules = reactive({
function checkMirrors(rule: any, value: any, callback: any) {
if (form.mirrors !== '') {
const reg = /^https?:\/\/[a-zA-Z0-9.-]+$/;
const reg = /^https?:\/\/[a-zA-Z0-9./-]+$/;
let mirrors = form.mirrors.split('\n');
for (const item of mirrors) {
if (item === '') {

Some files were not shown because too many files have changed in this diff Show More