Compare commits

...
13 Commits
Author SHA1 Message Date
zhengkunwang223 cbf4f8ec36 feat: 修改应用同步方法 2023-08-31 16:21:42 +08:00
ssongliu 3e3b1f2c56 fix: 解决防火墙 ip 规则删除失败的问题 2023-08-31 15:44:11 +08:00
ssongliu e5a7f104f1 fix: 解决版本升级导致无法创建数据库的问题 (#2108) 2023-08-29 17:21:14 +08:00
Allyxandallyxmiko-server 17062da52e fix: 修复压缩包解压时可能的由于父目录不存在导致的解压失败的问题 (#2099)
Co-authored-by: allyxmiko-server <allyxmiko@gmail.com>
2023-08-29 15:50:47 +08:00
zhengkunwang 512c7daeee feat: 修改应用商店版本升级判断条件 (#2089) 2023-08-29 15:50:32 +08:00
zhengkunwang ff120c421e feat: 应用商店增加版本控制 (#2068) 2023-08-29 15:49:58 +08:00
zhengkunwang 4d5993c6ab fix: 解决打开进程守护页面报错的问题 (#2057)
Refs https://github.com/1Panel-dev/1Panel/issues/2051
2023-08-29 15:49:39 +08:00
ssongliu 627e1e3755 feat: 放开 1panel-network 按钮禁用 (#2052) 2023-08-29 15:49:33 +08:00
ssongliu 0eee2d9807 fix: 解决数据库用户名显示错误的问题 (#2028) 2023-08-22 16:33:26 +08:00
zhengkunwang f0e78c4b7c fix: 解决运行环境网站切换PHP版本导致已安装应用消失的问题 (#2026) 2023-08-22 15:42:22 +08:00
ssongliu 6da64f2c8f fix: 解决清空容器日志导致的 CPU 占用异常问题 (#2019)
Refs #1999
2023-08-22 15:42:19 +08:00
ssongliu d572c28d1c fix: 服务器地址支持设置为空 (#2016) 2023-08-22 15:42:13 +08:00
ssongliu 7fac0fcbe8 fix: 解决数据库用户名超长导致用户创建失败的问题 (#2011) 2023-08-22 15:42:09 +08:00
23 changed files with 156 additions and 70 deletions
+2 -1
View File
@@ -67,7 +67,8 @@ type LocalAppInstallDefine struct {
}
type ExtraProperties struct {
Tags []Tag `json:"tags"`
Tags []Tag `json:"tags"`
Version string `json:"version"`
}
type AppProperty struct {
+18 -2
View File
@@ -668,6 +668,21 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
res.CanUpdate = true
return res, err
}
if err = getAppFromRepo(fmt.Sprintf("%s/%s/1panel.json.zip", global.CONF.System.AppRepo, global.CONF.System.Mode)); err != nil {
return nil, err
}
listFile := path.Join(constant.ResourceDir, "1panel.json")
content, err := os.ReadFile(listFile)
if err != nil {
return nil, err
}
list := &dto.AppList{}
if err = json.Unmarshal(content, list); err != nil {
return nil, err
}
if list.Extra.Version != "" && setting.SystemVersion != list.Extra.Version && !common.CompareVersion(setting.SystemVersion, list.Extra.Version) {
return nil, buserr.New("ErrVersionTooLow")
}
return res, nil
}
@@ -766,7 +781,8 @@ func (a AppService) SyncAppListFromRemote() (err error) {
version := v.Name
detail := detailsMap[version]
dockerComposeUrl := fmt.Sprintf("%s/%s/%s/%s", baseRemoteUrl, app.Key, version, "docker-compose.yml")
remoteVersionUrl := fmt.Sprintf("%s/%s/%s", baseRemoteUrl, app.Key, version)
dockerComposeUrl := fmt.Sprintf("%s/%s", remoteVersionUrl, "docker-compose.yml")
composeRes, err := http.Get(dockerComposeUrl)
if err != nil {
return err
@@ -779,7 +795,7 @@ func (a AppService) SyncAppListFromRemote() (err error) {
paramByte, _ := json.Marshal(v.AppForm)
detail.Params = string(paramByte)
detail.DownloadUrl = v.DownloadUrl
detail.DownloadUrl = fmt.Sprintf("%s/%s", remoteVersionUrl, version+".tar.gz")
detail.DownloadCallBackUrl = v.DownloadCallBackUrl
detail.Update = true
detail.LastModified = v.LastModified
+4
View File
@@ -840,6 +840,10 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
}
versions = common.GetSortedVersions(versions)
if len(versions) == 0 {
if !updated {
installDTO.CanUpdate = false
res = append(res, installDTO)
}
continue
}
lastVersion := versions[0]
+11 -3
View File
@@ -531,11 +531,15 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
if err != nil {
return err
}
container, err := client.ContainerInspect(context.Background(), req.Name)
ctx := context.Background()
containerItem, err := client.ContainerInspect(ctx, req.Name)
if err != nil {
return err
}
file, err := os.OpenFile(container.LogPath, os.O_RDWR|os.O_CREATE, 0666)
if err := client.ContainerStop(ctx, containerItem.ID, container.StopOptions{}); err != nil {
return err
}
file, err := os.OpenFile(containerItem.LogPath, os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
return err
}
@@ -545,10 +549,14 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
}
_, _ = file.Seek(0, 0)
files, _ := filepath.Glob(fmt.Sprintf("%s.*", container.LogPath))
files, _ := filepath.Glob(fmt.Sprintf("%s.*", containerItem.LogPath))
for _, file := range files {
_ = os.Remove(file)
}
if err := client.ContainerStart(ctx, containerItem.ID, types.ContainerStartOptions{}); err != nil {
return err
}
return nil
}
+18 -18
View File
@@ -378,25 +378,25 @@ func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcess
}
func (h *HostToolService) LoadProcessStatus() ([]response.ProcessStatus, error) {
var datas []response.ProcessStatus
statuLines, err := cmd.Exec("supervisorctl status")
if err != nil {
return datas, fmt.Errorf("exec `supervisorctl status` failed, err: %v", statuLines)
var res []response.ProcessStatus
statusLines, _ := cmd.Exec("supervisorctl status")
if len(statusLines) == 0 {
return res, nil
}
lines := strings.Split(string(statuLines), "\n")
lines := strings.Split(statusLines, "\n")
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) > 1 {
datas = append(datas, response.ProcessStatus{Name: fields[0]})
res = append(res, response.ProcessStatus{Name: fields[0]})
}
}
var wg sync.WaitGroup
wg.Add(len(datas))
for i := 0; i < len(datas); i++ {
wg.Add(len(res))
for i := 0; i < len(res); i++ {
go func(index int) {
for t := 0; t < 3; t++ {
status, err := cmd.ExecWithTimeOut(fmt.Sprintf("supervisorctl status %s", datas[index].Name), 2*time.Second)
status, err := cmd.ExecWithTimeOut(fmt.Sprintf("supervisorctl status %s", res[index].Name), 2*time.Second)
if err != nil {
time.Sleep(2 * time.Second)
continue
@@ -406,25 +406,25 @@ func (h *HostToolService) LoadProcessStatus() ([]response.ProcessStatus, error)
time.Sleep(2 * time.Second)
continue
}
datas[index].Name = fields[0]
datas[index].Status = fields[1]
res[index].Name = fields[0]
res[index].Status = fields[1]
if fields[1] != "RUNNING" {
datas[index].Msg = strings.Join(fields[2:], " ")
res[index].Msg = strings.Join(fields[2:], " ")
break
}
datas[index].PID = strings.TrimSuffix(fields[3], ",")
datas[index].Uptime = fields[5]
res[index].PID = strings.TrimSuffix(fields[3], ",")
res[index].Uptime = fields[5]
break
}
if len(datas[index].Status) == 0 {
datas[index].Status = "FATAL"
datas[index].Msg = "Timeout for getting process status"
if len(res[index].Status) == 0 {
res[index].Status = "FATAL"
res[index].Msg = "Timeout for getting process status"
}
wg.Done()
}(i)
}
wg.Wait()
return datas, nil
return res, nil
}
func (h *HostToolService) GetSupervisorProcessConfig() ([]response.SupervisorProcessConfig, error) {
+1
View File
@@ -46,6 +46,7 @@ ErrContainerNotFound: '{{ .name }} container does not exist'
ErrContainerMsg: '{{ .name }} container is abnormal, please check the log on the container page for details'
ErrAppBackup: '{{ .name }} application backup failed err {{.err}}'
ErrImagePull: '{{ .name }} image pull failed err {{.err}}'
ErrVersionTooLow: 'The current 1Panel version is too low to update the app store, please upgrade the version'
#file
ErrFileCanNotRead: "File can not read"
+1
View File
@@ -46,6 +46,7 @@ ErrContainerNotFound: '{{ .name }} 容器不存在'
ErrContainerMsg: '{{ .name }} 容器異常,具體請在容器頁面查看日誌'
ErrAppBackup: '{{ .name }} 應用備份失敗 err {{.err}}'
ErrImagePull: '{{ .name }} 鏡像拉取失敗 err {{.err}}'
ErrVersionTooLow: '當前 1Panel 版本過低,無法更新應用商店,請升級版本之後操作'
#file
ErrFileCanNotRead: "此文件不支持預覽"
+1
View File
@@ -46,6 +46,7 @@ ErrContainerNotFound: '{{ .name }} 容器不存在'
ErrContainerMsg: '{{ .name }} 容器异常,具体请在容器页面查看日志'
ErrAppBackup: '{{ .name }} 应用备份失败 err {{.err}}'
ErrImagePull: '镜像拉取失败 {{.err}}'
ErrVersionTooLow: '当前 1Panel 版本过低,无法更新应用商店,请升级版本之后操作'
#file
ErrFileCanNotRead: "此文件不支持预览"
+7
View File
@@ -518,6 +518,13 @@ func (f FileOp) Decompress(srcFile string, dst string, cType CompressType) error
return err
}
return nil
} else {
parentDir := path.Dir(filePath)
if !f.Stat(parentDir) {
if err := f.Fs.MkdirAll(parentDir, info.Mode()); err != nil {
return err
}
}
}
fr, err := archFile.Open()
if err != nil {
-4
View File
@@ -8,7 +8,6 @@ import (
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
)
@@ -28,9 +27,6 @@ type MysqlClient interface {
func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {
if conn.From == "local" {
if cmd.CheckIllegal(conn.Address, conn.Username, conn.Password) {
return nil, buserr.New(constant.ErrCmdIllegal)
}
connArgs := []string{"exec", conn.Address, "mysql", "-u" + conn.Username, "-p" + conn.Password, "-e"}
return client.NewLocal(connArgs, conn.Address, conn.Password, conn.From), nil
}
+19
View File
@@ -1,5 +1,11 @@
package client
import (
"strings"
"github.com/1Panel-dev/1Panel/backend/utils/common"
)
type DBInfo struct {
From string `json:"from"`
Address string `json:"address"`
@@ -85,3 +91,16 @@ var formatMap = map[string]string{
"gbk": "gbk_chinese_ci",
"big5": "big5_chinese_ci",
}
func loadNameByDB(name, version string) string {
if strings.HasPrefix(version, "5.6") {
if len(name) <= 16 {
return name
}
return strings.TrimSuffix(name[:10], "_") + "_" + common.RandStr(5)
}
if len(name) <= 32 {
return name
}
return strings.TrimSuffix(name[:25], "_") + "_" + common.RandStr(5)
}
+6 -6
View File
@@ -38,7 +38,7 @@ func (r *Local) Create(info CreateInfo) error {
return err
}
if err := r.CreateUser(info); err != nil {
if err := r.CreateUser(info, true); err != nil {
_ = r.ExecSQL(fmt.Sprintf("drop database if exists `%s`", info.Name), info.Timeout)
return err
}
@@ -46,7 +46,7 @@ func (r *Local) Create(info CreateInfo) error {
return nil
}
func (r *Local) CreateUser(info CreateInfo) error {
func (r *Local) CreateUser(info CreateInfo, withDeleteDB bool) error {
var userlist []string
if strings.Contains(info.Permission, ",") {
ips := strings.Split(info.Permission, ",")
@@ -201,7 +201,7 @@ func (r *Local) ChangeAccess(info AccessChangeInfo) error {
Password: info.Password,
Permission: info.Permission,
Timeout: info.Timeout,
}); err != nil {
}, false); err != nil {
return err
}
if err := r.ExecSQL("flush privileges", 300); err != nil {
@@ -305,18 +305,18 @@ func (r *Local) SyncDB(version string) ([]SyncDBInfo, error) {
}
}
if len(dataItem.Username) == 0 {
dataItem.Username = loadNameByDB(parts[0], version)
if err := r.CreateUser(CreateInfo{
Name: parts[0],
Format: parts[1],
Version: version,
Username: parts[0],
Username: dataItem.Username,
Password: common.RandStr(16),
Permission: "%",
Timeout: 300,
}); err != nil {
}, false); err != nil {
global.LOG.Errorf("sync from remote server failed, err: create user failed %v", err)
}
dataItem.Username = parts[0]
dataItem.Permission = "%"
} else {
if isLocal {
+27 -23
View File
@@ -39,7 +39,7 @@ func (r *Remote) Create(info CreateInfo) error {
return err
}
if err := r.CreateUser(info); err != nil {
if err := r.CreateUser(info, true); err != nil {
_ = r.ExecSQL(fmt.Sprintf("drop database if exists `%s`", info.Name), info.Timeout)
return err
}
@@ -47,7 +47,7 @@ func (r *Remote) Create(info CreateInfo) error {
return nil
}
func (r *Remote) CreateUser(info CreateInfo) error {
func (r *Remote) CreateUser(info CreateInfo, withDeleteDB bool) error {
var userlist []string
if strings.Contains(info.Permission, ",") {
ips := strings.Split(info.Permission, ",")
@@ -62,15 +62,17 @@ func (r *Remote) CreateUser(info CreateInfo) error {
for _, user := range userlist {
if err := r.ExecSQL(fmt.Sprintf("create user %s identified by '%s';", user, info.Password), info.Timeout); err != nil {
_ = r.Delete(DeleteInfo{
Name: info.Name,
Version: info.Version,
Username: info.Username,
Permission: info.Permission,
ForceDelete: true,
Timeout: 300})
if strings.Contains(err.Error(), "ERROR 1396") {
return buserr.New(constant.ErrUserIsExist)
if withDeleteDB {
_ = r.Delete(DeleteInfo{
Name: info.Name,
Version: info.Version,
Username: info.Username,
Permission: info.Permission,
ForceDelete: true,
Timeout: 300})
if strings.Contains(err.Error(), "ERROR 1396") {
return buserr.New(constant.ErrUserIsExist)
}
}
return err
}
@@ -82,13 +84,15 @@ func (r *Remote) CreateUser(info CreateInfo) error {
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, info.Password)
}
if err := r.ExecSQL(grantStr, info.Timeout); err != nil {
_ = r.Delete(DeleteInfo{
Name: info.Name,
Version: info.Version,
Username: info.Username,
Permission: info.Permission,
ForceDelete: true,
Timeout: 300})
if withDeleteDB {
_ = r.Delete(DeleteInfo{
Name: info.Name,
Version: info.Version,
Username: info.Username,
Permission: info.Permission,
ForceDelete: true,
Timeout: 300})
}
return err
}
}
@@ -202,7 +206,7 @@ func (r *Remote) ChangeAccess(info AccessChangeInfo) error {
Password: info.Password,
Permission: info.Permission,
Timeout: info.Timeout,
}); err != nil {
}, false); err != nil {
return err
}
if err := r.ExecSQL("flush privileges", 300); err != nil {
@@ -312,18 +316,18 @@ func (r *Remote) SyncDB(version string) ([]SyncDBInfo, error) {
i++
}
if len(dataItem.Username) == 0 {
dataItem.Username = loadNameByDB(dbName, version)
if err := r.CreateUser(CreateInfo{
Name: dbName,
Format: charsetName,
Version: version,
Username: dbName,
Username: dataItem.Username,
Password: common.RandStr(16),
Permission: "%",
Timeout: 300,
}); err != nil {
global.LOG.Errorf("sync from remote server failed, err: create user failed %v", err)
}, false); err != nil {
return datas, fmt.Errorf("sync db from remote server failed, err: create user failed %v", err)
}
dataItem.Username = dbName
dataItem.Permission = "%"
} else {
if isLocal {
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html>
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/public/favicon.png" />
@@ -130,7 +130,7 @@ const acceptParams = (props: DialogProps): void => {
};
const onClean = async () => {
ElMessageBox.confirm(i18n.global.t('commons.msg.clean'), i18n.global.t('container.cleanLog'), {
ElMessageBox.confirm(i18n.global.t('container.cleanLogHelper'), i18n.global.t('container.cleanLog'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
+2
View File
@@ -507,6 +507,8 @@ const message = {
lastHour: 'Last Hour',
last10Min: 'Last 10 Minutes',
cleanLog: 'Clean log',
cleanLogHelper:
'Clearing logs requires restarting the container, and this operation cannot be rolled back. Do you want to continue?',
newName: 'New name',
source: 'Resource rate',
cpuShare: 'CPU Share',
+1
View File
@@ -496,6 +496,7 @@ const message = {
lastHour: '最近 1 小時',
last10Min: '最近 10 分鐘',
cleanLog: '清空日誌',
cleanLogHelper: '清空日誌需要重啟容器,該操作無法回滾,是否繼續?',
newName: '新名稱',
source: '資源使用率',
cpuShare: 'CPU 權重',
+1
View File
@@ -496,6 +496,7 @@ const message = {
lastHour: '最近 1 小时',
last10Min: '最近 10 分钟',
cleanLog: '清空日志',
cleanLogHelper: '清空日志需要重启容器,该操作无法回滚,是否继续?',
newName: '新名称',
source: '资源使用率',
cpuShare: 'CPU 权重',
@@ -161,7 +161,7 @@ const onDownload = async () => {
};
const onClean = async () => {
ElMessageBox.confirm(i18n.global.t('commons.msg.clean'), i18n.global.t('container.cleanLog'), {
ElMessageBox.confirm(i18n.global.t('container.cleanLogHelper'), i18n.global.t('container.cleanLog'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
@@ -51,7 +51,9 @@
</el-table-column>
<el-table-column width="90">
<template #default="{ row }">
<el-tag effect="dark" round v-if="row.isSystem">system</el-tag>
<el-tag effect="dark" round v-if="row.isSystem || row.name === '1panel-network'">
system
</el-tag>
</template>
</el-table-column>
<el-table-column
@@ -78,11 +80,12 @@
</el-table-column>
<el-table-column
prop="createdAt"
show-overflow-tooltip
min-width="90"
:label="$t('commons.table.date')"
:formatter="dateFormat"
/>
<fu-table-operations :buttons="buttons" :label="$t('commons.table.operate')" fix />
<fu-table-operations width="100" :buttons="buttons" :label="$t('commons.table.operate')" fix />
</ComplexTable>
</template>
</LayoutContent>
@@ -222,7 +225,7 @@ const onInspect = async (id: string) => {
};
function isSystem(val: string) {
return val === 'bridge' || val === '1panel-network' || val === 'none' || val === 'host';
return val === 'bridge' || val === 'none' || val === 'host';
}
const buttons = [
@@ -247,7 +247,7 @@ const onDelete = async (row: Host.RuleIP | null) => {
}
}
loading.value = true;
await batchOperateRule({ type: 'port', rules: rules })
await batchOperateRule({ type: 'address', rules: rules })
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
@@ -184,6 +184,7 @@ const setting = () => {
const getStatus = (status: any) => {
supervisorStatus.value = status;
search();
};
const showStopped = computed((): boolean => {
@@ -211,6 +212,9 @@ const openCreate = () => {
};
const search = async () => {
if (!supervisorStatus.value.isExist) {
return;
}
loading.value = true;
loadStatus();
try {
@@ -231,9 +235,7 @@ const loadStatus = async () => {
process.status.push(item);
}
}
if (process.status.length !== 0) {
process.hasLoad = true;
}
process.hasLoad = true;
}
})
.catch(() => {
@@ -4,10 +4,17 @@
<template #header>
<DrawerHeader :header="$t('setting.systemIP')" :back="handleClose" />
</template>
<el-form ref="formRef" label-position="top" :model="form" @submit.prevent v-loading="loading">
<el-form
ref="formRef"
label-position="top"
:model="form"
:rules="rules"
@submit.prevent
v-loading="loading"
>
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('setting.systemIP')" prop="systemIP" :rules="Rules.ipV4V6OrDomain">
<el-form-item :label="$t('setting.systemIP')" prop="systemIP">
<el-input clearable v-model="form.systemIP" />
<span class="input-help">{{ $t('commons.rule.hostHelper') }}</span>
</el-form-item>
@@ -31,8 +38,8 @@ import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { updateSetting } from '@/api/modules/setting';
import { FormInstance } from 'element-plus';
import { Rules } from '@/global/form-rules';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { checkDomain, checkIpV4V6 } from '@/utils/util';
const emit = defineEmits<{ (e: 'search'): void }>();
@@ -47,6 +54,18 @@ const form = reactive({
});
const formRef = ref<FormInstance>();
const rules = reactive({
systemIP: [{ validator: checkSystemIP, trigger: 'blur' }],
});
function checkSystemIP(rule: any, value: any, callback: any) {
if (form.systemIP !== '') {
if (checkIpV4V6(form.systemIP) && checkDomain(form.systemIP)) {
return callback(new Error(i18n.global.t('commons.rule.host')));
}
}
callback();
}
const acceptParams = (params: DialogProps): void => {
form.systemIP = params.systemIP;