Compare commits

...
Author SHA1 Message Date
ssongliu 67ce559b12 fix: 概览页磁盘信息获取增加锁 (#3100) 2023-11-29 15:34:51 +08:00
ssongliu 71869b7264 fix: 概览页磁盘显示优化 (#3096) 2023-11-29 14:44:02 +08:00
ssongliu 03774bbba6 fix: 概览页磁盘信息获取增加超时时间 (#3074)
Refs #2825
2023-11-28 15:10:35 +08:00
igophper e07b6bc050 fix: 类名重命名 2023-11-28 15:10:26 +08:00
igophper b1935d8368 fix: 修复网站404重定向次数过多 (#3054) (#3056)
Refs #3054
2023-11-28 15:10:17 +08:00
江杭辉 f8e344db76 fix: 修复登录菜单消失 2023-11-23 22:20:06 +08:00
江杭辉 2c3a8dd6af fix: 修复登录菜单消失 2023-11-23 22:20:01 +08:00
江杭辉 86e5fc36f4 fix: 修复登录菜单消失 2023-11-23 22:19:56 +08:00
江杭辉 858cefc0c9 fix: mfa登录菜单消失 2023-11-23 22:19:53 +08:00
ssongliu 4535c0b90b fix: 解决镜像仓库编辑失败的问题 (#3029) 2023-11-23 22:19:43 +08:00
zhengkunwang 136489e346 fix: 解决网站某些设置会报服务器内部错误的问题 (#2973)
Refs https://github.com/1Panel-dev/1Panel/issues/2955
2023-11-23 22:18:20 +08:00
wanghe e2882fd1cc Update sonar-project.properties (#3004) 2023-11-23 22:17:15 +08:00
hzz dba97076df fix: 本地数据备份命令增加 --default-character-set 参数 与备份还原的编码保持一致 2023-11-23 22:17:02 +08:00
QuentinHsu 000bcd37c1 fix: FileRole 初始化 (#2956) 2023-11-23 22:16:53 +08:00
wanghe bf28223745 Update README.md (#2937) 2023-11-23 22:16:47 +08:00
maninhill 1fed5c4d15 Update README.md 2023-11-23 22:16:42 +08:00
13 changed files with 73 additions and 40 deletions
+1
View File
@@ -44,6 +44,7 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
- [在线文档](https://1panel.cn/docs/) - [在线文档](https://1panel.cn/docs/)
- [教学视频](https://space.bilibili.com/510493147/channel/collectiondetail?sid=1199760) - [教学视频](https://space.bilibili.com/510493147/channel/collectiondetail?sid=1199760)
- [社区论坛](https://bbs.fit2cloud.com/c/1p/7) - [社区论坛](https://bbs.fit2cloud.com/c/1p/7)
- [如何加入微信交流群?](https://bbs.fit2cloud.com/t/topic/2147)
## 安全说明 ## 安全说明
+48 -18
View File
@@ -3,7 +3,9 @@ package service
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"sort"
"strings" "strings"
"sync"
"time" "time"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -173,7 +175,7 @@ type diskInfo struct {
func loadDiskInfo() []dto.DiskInfo { func loadDiskInfo() []dto.DiskInfo {
var datas []dto.DiskInfo var datas []dto.DiskInfo
stdout, err := cmd.Exec("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev") stdout, err := cmd.ExecWithTimeOut("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev", 2*time.Second)
if err != nil { if err != nil {
return datas return datas
} }
@@ -207,24 +209,52 @@ func loadDiskInfo() []dto.DiskInfo {
mounts = append(mounts, diskInfo{Type: fields[1], Device: fields[0], Mount: fields[6]}) mounts = append(mounts, diskInfo{Type: fields[1], Device: fields[0], Mount: fields[6]})
} }
var (
wg sync.WaitGroup
mu sync.Mutex
)
wg.Add(len(mounts))
for i := 0; i < len(mounts); i++ { for i := 0; i < len(mounts); i++ {
state, err := disk.Usage(mounts[i].Mount) go func(timeoutCh <-chan time.Time, mount diskInfo) {
if err != nil { defer wg.Done()
continue
} var itemData dto.DiskInfo
var itemData dto.DiskInfo itemData.Path = mount.Mount
itemData.Path = mounts[i].Mount itemData.Type = mount.Type
itemData.Type = mounts[i].Type itemData.Device = mount.Device
itemData.Device = mounts[i].Device select {
itemData.Total = state.Total case <-timeoutCh:
itemData.Free = state.Free mu.Lock()
itemData.Used = state.Used datas = append(datas, itemData)
itemData.UsedPercent = state.UsedPercent mu.Unlock()
itemData.InodesTotal = state.InodesTotal global.LOG.Errorf("load disk info from %s failed, err: timeout", mount.Mount)
itemData.InodesUsed = state.InodesUsed default:
itemData.InodesFree = state.InodesFree state, err := disk.Usage(mount.Mount)
itemData.InodesUsedPercent = state.InodesUsedPercent if err != nil {
datas = append(datas, itemData) mu.Lock()
datas = append(datas, itemData)
mu.Unlock()
global.LOG.Errorf("load disk info from %s failed, err: %v", mount.Mount, err)
return
}
itemData.Total = state.Total
itemData.Free = state.Free
itemData.Used = state.Used
itemData.UsedPercent = state.UsedPercent
itemData.InodesTotal = state.InodesTotal
itemData.InodesUsed = state.InodesUsed
itemData.InodesFree = state.InodesFree
itemData.InodesUsedPercent = state.InodesUsedPercent
mu.Lock()
datas = append(datas, itemData)
mu.Unlock()
}
}(time.After(5*time.Second), mounts[i])
} }
wg.Wait()
sort.Slice(datas, func(i, j int) bool {
return datas[i].Path < datas[j].Path
})
return datas return datas
} }
+1 -1
View File
@@ -217,12 +217,12 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
case "create": case "create":
registries = common.RemoveRepeatElement(append(registries, newHost)) registries = common.RemoveRepeatElement(append(registries, newHost))
case "update": case "update":
registries = common.RemoveRepeatElement(append(registries, newHost))
for i, regi := range registries { for i, regi := range registries {
if regi == delHost { if regi == delHost {
registries = append(registries[:i], registries[i+1:]...) registries = append(registries[:i], registries[i+1:]...)
} }
} }
registries = common.RemoveRepeatElement(append(registries, newHost))
case "delete": case "delete":
for i, regi := range registries { for i, regi := range registries {
if regi == delHost { if regi == delHost {
+5 -2
View File
@@ -196,8 +196,11 @@ func opNginx(containerName, operate string) error {
if operate == constant.NginxCheck { if operate == constant.NginxCheck {
nginxCmd = fmt.Sprintf("docker exec -i %s %s", containerName, "nginx -t") nginxCmd = fmt.Sprintf("docker exec -i %s %s", containerName, "nginx -t")
} }
if out, err := cmd.ExecWithTimeOut(nginxCmd, 2*time.Second); err != nil { if out, err := cmd.ExecWithTimeOut(nginxCmd, 20*time.Second); err != nil {
return errors.New(out) if out != "" {
return errors.New(out)
}
return err
} }
return nil return nil
} }
+1 -8
View File
@@ -2093,15 +2093,8 @@ func (w WebsiteService) OperateRedirect(req request.NginxRedirectReq) (err error
}) })
} }
case "404": case "404":
if req.KeepPath && !req.RedirectRoot {
target = req.Target + "$request_uri"
}
if req.RedirectRoot { if req.RedirectRoot {
target = "/" target = "/"
} else {
if req.KeepPath {
target = req.Target + "$request_uri"
}
} }
block = &components.Block{ block = &components.Block{
Directives: []components.IDirective{ Directives: []components.IDirective{
@@ -2116,7 +2109,7 @@ func (w WebsiteService) OperateRedirect(req request.NginxRedirectReq) (err error
Directives: []components.IDirective{ Directives: []components.IDirective{
&components.Directive{ &components.Directive{
Name: "return", Name: "return",
Parameters: []string{"301", target}, Parameters: []string{req.Redirect, target},
}, },
}, },
}, },
+1 -1
View File
@@ -220,7 +220,7 @@ func (r *Local) Backup(info BackupInfo) error {
} }
outfile, _ := os.OpenFile(path.Join(info.TargetDir, info.FileName), os.O_RDWR|os.O_CREATE, 0755) outfile, _ := os.OpenFile(path.Join(info.TargetDir, info.FileName), os.O_RDWR|os.O_CREATE, 0755)
global.LOG.Infof("start to mysqldump | gzip > %s.gzip", info.TargetDir+"/"+info.FileName) global.LOG.Infof("start to mysqldump | gzip > %s.gzip", info.TargetDir+"/"+info.FileName)
cmd := exec.Command("docker", "exec", r.ContainerName, "mysqldump", "-uroot", "-p"+r.Password, info.Name) cmd := exec.Command("docker", "exec", r.ContainerName, "mysqldump", "-uroot", "-p"+r.Password, "--default-character-set="+info.Format, info.Name)
gzipCmd := exec.Command("gzip", "-cf") gzipCmd := exec.Command("gzip", "-cf")
gzipCmd.Stdin, _ = cmd.StdoutPipe() gzipCmd.Stdin, _ = cmd.StdoutPipe()
gzipCmd.Stdout = outfile gzipCmd.Stdout = outfile
@@ -6,7 +6,7 @@
<el-row> <el-row>
<el-col :span="22" :offset="1" v-loading="loading"> <el-col :span="22" :offset="1" v-loading="loading">
<FileRole :mode="mode" @get-mode="getMode"></FileRole> <FileRole :mode="mode" @get-mode="getMode" :key="open.toString()"></FileRole>
<el-form ref="fileForm" label-position="left" :model="addForm" label-width="100px" :rules="rules"> <el-form ref="fileForm" label-position="left" :model="addForm" label-width="100px" :rules="rules">
<el-form-item :label="$t('commons.table.user')" prop="user"> <el-form-item :label="$t('commons.table.user')" prop="user">
<el-input v-model.trim="addForm.user" /> <el-input v-model.trim="addForm.user" />
@@ -6,7 +6,7 @@
<el-row> <el-row>
<el-col :span="22" :offset="1"> <el-col :span="22" :offset="1">
<FileRole v-loading="loading" :mode="mode" @get-mode="getMode"></FileRole> <FileRole v-loading="loading" :mode="mode" @get-mode="getMode" :key="open.toString()"></FileRole>
<el-form-item v-if="form.isDir"> <el-form-item v-if="form.isDir">
<el-checkbox v-model="form.sub">{{ $t('file.containSub') }}</el-checkbox> <el-checkbox v-model="form.sub">{{ $t('file.containSub') }}</el-checkbox>
</el-form-item> </el-form-item>
@@ -44,7 +44,7 @@
<el-checkbox v-if="addForm.isDir" v-model="setRole" :label="$t('file.setRole')"></el-checkbox> <el-checkbox v-if="addForm.isDir" v-model="setRole" :label="$t('file.setRole')"></el-checkbox>
</el-form-item> </el-form-item>
</el-form> </el-form>
<FileRole v-if="setRole" :mode="'0755'" @get-mode="getMode"></FileRole> <FileRole v-if="setRole" :mode="'0755'" @get-mode="getMode" :key="open.toString()"></FileRole>
</el-col> </el-col>
</el-row> </el-row>
@@ -189,6 +189,7 @@ const loginRules = reactive({
password: computed(() => [{ required: true, message: i18n.global.t('commons.rule.password'), trigger: 'blur' }]), password: computed(() => [{ required: true, message: i18n.global.t('commons.rule.password'), trigger: 'blur' }]),
}); });
let isLoggingIn = false;
const mfaButtonFocused = ref(); const mfaButtonFocused = ref();
const mfaLoginForm = reactive({ const mfaLoginForm = reactive({
name: '', name: '',
@@ -223,7 +224,7 @@ function handleCommand(command: string) {
} }
const login = (formEl: FormInstance | undefined) => { const login = (formEl: FormInstance | undefined) => {
if (!formEl) return; if (!formEl || isLoggingIn) return;
formEl.validate(async (valid) => { formEl.validate(async (valid) => {
if (!valid) return; if (!valid) return;
let requestLoginForm = { let requestLoginForm = {
@@ -244,6 +245,7 @@ const login = (formEl: FormInstance | undefined) => {
return; return;
} }
try { try {
isLoggingIn = true;
loading.value = true; loading.value = true;
const res = await loginApi(requestLoginForm); const res = await loginApi(requestLoginForm);
if (res.code === 406) { if (res.code === 406) {
@@ -274,18 +276,22 @@ const login = (formEl: FormInstance | undefined) => {
} catch (error) { } catch (error) {
loginVerify(); loginVerify();
} finally { } finally {
isLoggingIn = false;
loading.value = false; loading.value = false;
} }
}); });
}; };
const mfaLogin = async (auto: boolean) => { const mfaLogin = async (auto: boolean) => {
if (isLoggingIn) return;
if ((!auto && mfaLoginForm.code) || (auto && mfaLoginForm.code.length === 6)) { if ((!auto && mfaLoginForm.code) || (auto && mfaLoginForm.code.length === 6)) {
isLoggingIn = true;
mfaLoginForm.name = loginForm.name; mfaLoginForm.name = loginForm.name;
mfaLoginForm.password = loginForm.password; mfaLoginForm.password = loginForm.password;
const res = await mfaLoginApi(mfaLoginForm); const res = await mfaLoginApi(mfaLoginForm);
if (res.code === 406) { if (res.code === 406) {
errMfaInfo.value = true; errMfaInfo.value = true;
isLoggingIn = false;
return; return;
} }
globalStore.setLogStatus(true); globalStore.setLogStatus(true);
+2 -2
View File
@@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<div class="login-backgroud" v-if="isSafety && !isErr && !isNotFound"> <div class="login-background" v-if="isSafety && !isErr && !isNotFound">
<div class="login-wrapper"> <div class="login-wrapper">
<div :class="screenWidth > 1110 ? 'left inline-block' : ''"> <div :class="screenWidth > 1110 ? 'left inline-block' : ''">
<div class="login-title"> <div class="login-title">
@@ -100,7 +100,7 @@ onMounted(() => {
align-items: center; align-items: center;
} }
.login-backgroud { .login-background {
height: 100vh; height: 100vh;
background: url(@/assets/images/1panel-login-bg.png) no-repeat, background: url(@/assets/images/1panel-login-bg.png) no-repeat,
radial-gradient(153.25% 257.2% at 118.99% 181.67%, rgba(50, 132, 255, 0.2) 0%, rgba(82, 120, 255, 0) 100%) radial-gradient(153.25% 257.2% at 118.99% 181.67%, rgba(50, 132, 255, 0.2) 0%, rgba(82, 120, 255, 0) 100%)
+2 -2
View File
@@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<div class="login-backgroud"> <div class="login-background">
<div class="login-wrapper"> <div class="login-wrapper">
<div :class="screenWidth > 1110 ? 'left inline-block' : ''"> <div :class="screenWidth > 1110 ? 'left inline-block' : ''">
<div class="login-title"> <div class="login-title">
@@ -54,7 +54,7 @@ onMounted(() => {
align-items: center; align-items: center;
} }
.login-backgroud { .login-background {
height: 100vh; height: 100vh;
background: url(@/assets/images/1panel-login-bg.png) no-repeat, background: url(@/assets/images/1panel-login-bg.png) no-repeat,
radial-gradient(153.25% 257.2% at 118.99% 181.67%, rgba(50, 132, 255, 0.2) 0%, rgba(82, 120, 255, 0) 100%) radial-gradient(153.25% 257.2% at 118.99% 181.67%, rgba(50, 132, 255, 0.2) 0%, rgba(82, 120, 255, 0) 100%)
+2 -2
View File
@@ -1,7 +1,7 @@
sonar.projectKey=1Panel-dev_1Panel sonar.projectKey=1Panel-dev_1Panel
sonar.organization=1panel-dev sonar.organization=1panel-dev
sonar.exclusions= frontend/src/lang/modules/* sonar.exclusions=frontend/src/lang/modules/*,backend/i18n/lang/*
# This is the name and version displayed in the SonarCloud UI. # This is the name and version displayed in the SonarCloud UI.
#sonar.projectName=1Panel #sonar.projectName=1Panel
@@ -12,4 +12,4 @@ sonar.exclusions= frontend/src/lang/modules/*
#sonar.sources=. #sonar.sources=.
# Encoding of the source code. Default is default system encoding # Encoding of the source code. Default is default system encoding
#sonar.sourceEncoding=UTF-8 #sonar.sourceEncoding=UTF-8