Compare commits

..
28 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
ssongliu aa1f2d0f01 fix: 优化镜像清理弹框内容显示 (#2931)
Refs #2928
2023-11-13 08:56:08 +00:00
zhengkunwang 80c68b8e49 fix: 解决部分文件不能上传的问题 (#2933) 2023-11-13 08:52:08 +00:00
ssongliu fb556d6149 feat: 概览页重启按钮优化 (#2926)
Refs #2921
2023-11-13 07:02:08 +00:00
zhengkunwang 78576a3724 fix: 解决文件上传多选可能导致的文件列表丢失的问题 (#2925) 2023-11-13 07:00:08 +00:00
zhengkunwang e09cefd0f7 fix: logr 依赖回退版本 (#2920) 2023-11-13 11:30:59 +08:00
zhengkunwang ed3262a07c fix: 解决 Openresty 性能调整保存报错的问题 (#2919) 2023-11-13 03:14:08 +00:00
dependabot[bot] b904650682 build(deps): bump go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc (#2916)
Bumps [go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc](https://github.com/open-telemetry/opentelemetry-go-contrib) from 0.44.0 to 0.46.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.44.0...zpages/v0.46.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-13 09:18:46 +08:00
QuentinHsu abb3073251 perf: home page style (#2911) 2023-11-13 09:14:58 +08:00
dependabot[bot] d8d46aa27c build(deps): bump axios from 0.27.2 to 1.6.0 in /frontend
Bumps [axios](https://github.com/axios/axios) from 0.27.2 to 1.6.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.27.2...v1.6.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-12 02:20:36 +08:00
QuentinHsu 0737a75bd3 perf: 弹窗操作的 successMsg 参数 2023-11-12 02:19:23 +08:00
zhengkunwang 7667399c5e fix: 解决网站密码访问启动报错的问题 (#2891)
Refs #2894
2023-11-10 15:08:09 +00:00
QuentinHsu 0a003d3750 perf: 弹窗操作的 successMsg 参数 (#2892) 2023-11-10 23:07:16 +08:00
33 changed files with 233 additions and 137 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://space.bilibili.com/510493147/channel/collectiondetail?sid=1199760)
- [社区论坛](https://bbs.fit2cloud.com/c/1p/7)
- [如何加入微信交流群?](https://bbs.fit2cloud.com/t/topic/2147)
## 安全说明
+3 -3
View File
@@ -15,7 +15,7 @@ type NginxScopeReq struct {
type NginxConfigUpdate struct {
Scope dto.NginxKey `json:"scope"`
Operate string `json:"operate" validate:"required,oneof=add update delete"`
WebsiteID uint `json:"websiteId" validate:"required"`
WebsiteID uint `json:"websiteId"`
Params interface{} `json:"params"`
}
@@ -39,8 +39,8 @@ type NginxProxyUpdate struct {
type NginxAuthUpdate struct {
WebsiteID uint `json:"websiteID" validate:"required"`
Operate string `json:"operate" validate:"required"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Username string `json:"username"`
Password string `json:"password"`
Remark string `json:"remark"`
}
+49 -19
View File
@@ -3,7 +3,9 @@ package service
import (
"encoding/json"
"fmt"
"sort"
"strings"
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -31,7 +33,7 @@ func NewIDashboardService() IDashboardService {
}
func (u *DashboardService) Restart(operation string) error {
if operation != "panel" && operation != "system" {
if operation != "1panel" && operation != "system" {
return fmt.Errorf("handle restart operation %s failed, err: nonsupport such operation", operation)
}
itemCmd := fmt.Sprintf("%s 1pctl restart", cmd.SudoHandleCmd())
@@ -173,7 +175,7 @@ type diskInfo struct {
func loadDiskInfo() []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 {
return datas
}
@@ -207,24 +209,52 @@ func loadDiskInfo() []dto.DiskInfo {
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++ {
state, err := disk.Usage(mounts[i].Mount)
if err != nil {
continue
}
var itemData dto.DiskInfo
itemData.Path = mounts[i].Mount
itemData.Type = mounts[i].Type
itemData.Device = mounts[i].Device
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
datas = append(datas, itemData)
go func(timeoutCh <-chan time.Time, mount diskInfo) {
defer wg.Done()
var itemData dto.DiskInfo
itemData.Path = mount.Mount
itemData.Type = mount.Type
itemData.Device = mount.Device
select {
case <-timeoutCh:
mu.Lock()
datas = append(datas, itemData)
mu.Unlock()
global.LOG.Errorf("load disk info from %s failed, err: timeout", mount.Mount)
default:
state, err := disk.Usage(mount.Mount)
if err != nil {
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
}
+1 -1
View File
@@ -217,12 +217,12 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
case "create":
registries = common.RemoveRepeatElement(append(registries, newHost))
case "update":
registries = common.RemoveRepeatElement(append(registries, newHost))
for i, regi := range registries {
if regi == delHost {
registries = append(registries[:i], registries[i+1:]...)
}
}
registries = common.RemoveRepeatElement(append(registries, newHost))
case "delete":
for i, regi := range registries {
if regi == delHost {
+5 -2
View File
@@ -196,8 +196,11 @@ func opNginx(containerName, operate string) error {
if operate == constant.NginxCheck {
nginxCmd = fmt.Sprintf("docker exec -i %s %s", containerName, "nginx -t")
}
if out, err := cmd.ExecWithTimeOut(nginxCmd, 2*time.Second); err != nil {
return errors.New(out)
if out, err := cmd.ExecWithTimeOut(nginxCmd, 20*time.Second); err != nil {
if out != "" {
return errors.New(out)
}
return err
}
return nil
}
+1 -8
View File
@@ -2093,15 +2093,8 @@ func (w WebsiteService) OperateRedirect(req request.NginxRedirectReq) (err error
})
}
case "404":
if req.KeepPath && !req.RedirectRoot {
target = req.Target + "$request_uri"
}
if req.RedirectRoot {
target = "/"
} else {
if req.KeepPath {
target = req.Target + "$request_uri"
}
}
block = &components.Block{
Directives: []components.IDirective{
@@ -2116,7 +2109,7 @@ func (w WebsiteService) OperateRedirect(req request.NginxRedirectReq) (err error
Directives: []components.IDirective{
&components.Directive{
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)
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.Stdin, _ = cmd.StdoutPipe()
gzipCmd.Stdout = outfile
+12 -6
View File
@@ -14,7 +14,7 @@
"@codemirror/theme-one-dark": "^6.1.0",
"@element-plus/icons-vue": "^1.1.4",
"@vueuse/core": "^8.0.1",
"axios": "^0.27.2",
"axios": "^1.6.0",
"echarts": "^5.3.0",
"echarts-liquidfill": "^3.1.0",
"element-plus": "^2.3.4",
@@ -2736,12 +2736,13 @@
}
},
"node_modules/axios": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
"dependencies": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/babel-walk": {
@@ -8109,6 +8110,11 @@
"asap": "~2.0.3"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/pug": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/pug/-/pug-3.0.2.tgz",
+1 -1
View File
@@ -26,7 +26,7 @@
"@codemirror/theme-one-dark": "^6.1.0",
"@element-plus/icons-vue": "^1.1.4",
"@vueuse/core": "^8.0.1",
"axios": "^0.27.2",
"axios": "^1.6.0",
"echarts": "^5.3.0",
"echarts-liquidfill": "^3.1.0",
"element-plus": "^2.3.4",
+1
View File
@@ -119,6 +119,7 @@ export namespace Container {
name: string;
tags: Array<string>;
size: string;
isUsed: boolean;
}
export interface ImageBuild {
from: string;
@@ -6,8 +6,8 @@
</div>
</template>
<div>
<span style="font-size: 12px">{{ operationInfo }}</span>
<div style="margin-top: 10px">
<span v-if="operationInfo" style="font-size: 12px">{{ operationInfo }}</span>
<div :style="{ 'margin-top': operationInfo ? '10px' : '0px' }">
<span style="font-size: 12px">{{ $t('commons.msg.operateConfirm') }}</span>
<span style="font-size: 12px; color: red; font-weight: 500">'{{ submitInputInfo }}'</span>
</div>
+2
View File
@@ -4,6 +4,7 @@ import RouterButton from './router-button/index.vue';
import ComplexTable from './complex-table/index.vue';
import ErrPrompt from './error-prompt/index.vue';
import OpDialog from './del-dialog/index.vue';
import Tooltip from '@/components/tooltip/index.vue';
export default {
install(app: App) {
app.component(LayoutContent.name, LayoutContent);
@@ -11,5 +12,6 @@ export default {
app.component(ComplexTable.name, ComplexTable);
app.component(ErrPrompt.name, ErrPrompt);
app.component(OpDialog.name, OpDialog);
app.component(Tooltip.name, Tooltip);
},
};
@@ -69,6 +69,11 @@ onMounted(() => {
<style lang="scss">
.router_card {
--el-card-padding: 0;
.el-card__body {
display: flex;
justify-content: space-between;
align-items: center;
}
}
.router_card_button {
@@ -15,6 +15,8 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
defineOptions({ name: 'Tooltip' });
const showTooltip = ref();
const tooltipBox = ref();
const tooltipItem = ref();
+3 -4
View File
@@ -286,10 +286,7 @@ const message = {
home: {
restart_1panel: 'Restart Panel',
restart_system: 'Restart Server',
panel: '1Panel',
system: '1Panel Server',
restartHelper: 'About to restart {0}, do you want to continue?',
operationSuccess: 'Operation successful! Restarting, please wait...',
operationSuccess: 'Operation succeeded, rebooting, please refresh the browser manually later!',
overview: 'Overview',
entranceHelper:
'Enabling a secure entrance can help improve system security. If necessary, go to the Control Panel settings, select Security, and enable the secure entrance.',
@@ -522,8 +519,10 @@ const message = {
containerPruneHelper3: 'This operation cannot be rolled back. Do you want to continue?',
imagePrune: 'Image prune',
imagePruneSome: 'Clean unlabeled',
imagePruneSomeEmpty: 'No image with the "none" tag is to be cleared',
imagePruneSomeHelper: 'Clean the images with the tag "none" that are not used by any containers.',
imagePruneAll: 'Clean unused',
imagePruneAllEmpty: 'No unused images to be cleared',
imagePruneAllHelper: 'Clean the images that are not used by any containers.',
networkPrune: 'Network prune',
networkPruneHelper: 'Remove all unused networks. Do you want to continue?',
+5 -6
View File
@@ -284,10 +284,7 @@ const message = {
home: {
restart_1panel: '重啟面板',
restart_system: '重啟服務器',
panel: '1Panel 面板',
system: '1Panel 服務器',
restartHelper: '即將重新啟動 {0},是否繼續?',
operationSuccess: '操作成功!正在重啟,請稍候...',
operationSuccess: '操作成功,正在重啟,請稍後手動刷新瀏覽器!',
overview: '概覽',
entranceHelper: '設置安全入口有利於提高系統的安全性,如有需要,前往 面板設置-安全 中,啟用安全入口',
appInstalled: '已安裝應用',
@@ -510,9 +507,11 @@ const message = {
containerPruneHelper3: '該操作無法回滾,是否繼續?',
imagePrune: '清理鏡像',
imagePruneSome: '未標簽鏡像',
imagePruneSomeHelper: '清理下列標簽為 none 且未被任何容器使用的鏡像。',
imagePruneSomeEmpty: '暫無待清理的未使用 none 標簽鏡像',
imagePruneSomeHelper: '清理下列標簽為 none 且未被任何容器使用的鏡像',
imagePruneAll: '未使用鏡像',
imagePruneAllHelper: '清理下列未被任何容器使用的鏡像。',
imagePruneAllEmpty: '暫無待清理的未使用鏡像',
imagePruneAllHelper: '清理下列未被任何容器使用的鏡像',
networkPrune: '清理網絡',
networkPruneHelper: '清理網絡 將刪除所有未被使用的網絡,該操作無法回滾,是否繼續?',
volumePrune: '清理存儲卷',
+5 -6
View File
@@ -284,10 +284,7 @@ const message = {
home: {
restart_1panel: '重启面板',
restart_system: '重启服务器',
panel: '1Panel 面板',
system: '1Panel 服务器',
restartHelper: '即将重新启动 {0},是否继续?',
operationSuccess: '操作成功!正在重启,请稍候...',
operationSuccess: '操作成功,正在重启,请稍后手动刷新浏览器!',
overview: '概览',
entranceHelper: '设置安全入口有利于提高系统的安全性,如有需要,前往 面板设置-安全 中,启用安全入口',
appInstalled: '已安装应用',
@@ -511,9 +508,11 @@ const message = {
containerPruneHelper3: '该操作无法回滚,是否继续?',
imagePrune: '清理镜像',
imagePruneSome: '未标签镜像',
imagePruneSomeHelper: '清理下列标签为 none 且未被任何容器使用的镜像。',
imagePruneSomeEmpty: '暂无待清理的未使用 none 标签镜像',
imagePruneSomeHelper: '清理下列标签为 none 且未被任何容器使用的镜像',
imagePruneAll: '未使用镜像',
imagePruneAllHelper: '清理下列未被任何容器使用的镜像。',
imagePruneAllEmpty: '暂无待清理的未使用镜像',
imagePruneAllHelper: '清理下列未被任何容器使用的镜像',
networkPrune: '清理网络',
networkPruneHelper: '清理网络 将删除所有未被使用的网络,该操作无法回滚,是否继续?',
volumePrune: '清理存储卷',
+1 -3
View File
@@ -379,9 +379,7 @@ html {
}
.router-button {
margin-top: 12px;
float: right;
margin-right: 50px;
margin-right: 20px;
}
.text-parent {
@@ -249,6 +249,7 @@ const onOperate = async (op: string) => {
msg: msg,
api: containerOperator,
params: { names: names, operation: op },
successMsg: `${i18n.global.t('container.' + op)}${i18n.global.t('commons.status.success')}`,
});
};
@@ -567,12 +567,14 @@ const onOperate = async (op: string, row: Container.ContainerInfo | null) => {
msg = i18n.global.t('container.operatorAppHelper', [i18n.global.t('container.' + op)]);
}
}
const successMsg = `${i18n.global.t('container.' + op)}${i18n.global.t('commons.status.success')}`;
opRef.value.acceptParams({
title: i18n.global.t('container.' + op),
names: names,
msg: msg,
api: containerOperator,
params: { names: names, operation: op },
successMsg,
});
};
@@ -12,19 +12,25 @@
<el-radio :label="true">{{ $t('container.imagePruneAll') }}</el-radio>
</el-radio-group>
</el-form-item>
<span>
{{ withTagAll ? $t('container.imagePruneAllHelper') : $t('container.imagePruneSomeHelper') }}
<span v-if="withTagAll">
{{ unUsedList.length !== 0 ? $t('container.imagePruneAllHelper') : $t('container.imagePruneAllEmpty') }}
</span>
<span v-else>
{{
unTagList.length !== 0 ? $t('container.imagePruneSomeHelper') : $t('container.imagePruneSomeEmpty')
}}
</span>
<div v-if="!withTagAll">
<ul v-for="(item, index) in imageList" :key="index">
<li v-if="item.tags && item.tags.length === 1 && item.tags[0].indexOf('<none>') !== -1">
<ul v-for="(item, index) in unTagList" :key="index">
<li>
{{ item.tags[0] }}
</li>
</ul>
</div>
<div v-else>
<ul v-for="(item, index) in imageList" :key="index">
<li v-if="item.tags && !item.isUsed">{{ item.tags.join(', ') }}</li>
<ul v-for="(item, index) in unUsedList" :key="index">
<li v-if="item.tags">{{ item.tags.join(', ') }}</li>
<li v-else>{{ item.id.replaceAll('sha256:', '').substring(0, 12) }}</li>
</ul>
</div>
</el-form>
@@ -33,7 +39,7 @@
<el-button @click="dialogVisible = false">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button type="primary" @click="onClean" :disabled="loading">
<el-button type="primary" :disabled="buttonDisable() || loading" @click="onClean">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
@@ -52,19 +58,34 @@ import { ref } from 'vue';
const dialogVisible = ref(false);
const withTagAll = ref(false);
const loading = ref();
const imageList = ref();
const unTagList = ref();
const unUsedList = ref();
interface DialogProps {
list: Array<Container.ImageInfo>;
}
const acceptParams = (params: DialogProps): void => {
imageList.value = params.list;
let list = params.list || [];
unTagList.value = [];
unUsedList.value = [];
for (const item of list) {
if (item.tags && item.tags.length === 1 && item.tags[0].indexOf('<none>') !== -1 && !item.isUsed) {
unTagList.value.push(item);
}
if (!item.isUsed) {
unUsedList.value.push(item);
}
}
dialogVisible.value = true;
withTagAll.value = false;
};
const emit = defineEmits<{ (e: 'search'): void }>();
const buttonDisable = () => {
return withTagAll.value ? unUsedList.value.length === 0 : unTagList.value.length === 0;
};
const onClean = async () => {
loading.value = true;
let params = {
+20 -16
View File
@@ -10,11 +10,11 @@
>
<template #route-button>
<div class="router-button">
<el-button link type="primary" @click="restart('panel')">
<el-button link type="primary" @click="onRestart('1panel')">
{{ $t('home.restart_1panel') }}
</el-button>
<el-divider direction="vertical" />
<el-button link type="primary" @click="restart('system')">
<el-button link type="primary" @click="onRestart('system')">
{{ $t('home.restart_system') }}
</el-button>
</div>
@@ -230,10 +230,13 @@
</CardWithHeader>
</el-col>
</el-row>
<ConfirmDialog ref="confirmDialogRef" @confirm="onSave"></ConfirmDialog>
</div>
</template>
<script lang="ts" setup>
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { onMounted, onBeforeUnmount, ref, reactive } from 'vue';
import Status from '@/views/home/status/index.vue';
import App from '@/views/home/app/index.vue';
@@ -252,6 +255,8 @@ const router = useRouter();
const globalStore = GlobalStore();
const statusRef = ref();
const restartType = ref();
const confirmDialogRef = ref();
const appRef = ref();
const isSafety = ref();
@@ -540,20 +545,19 @@ const loadSafeStatus = async () => {
isSafety.value = res.data.securityEntrance;
};
const restart = async (type: string) => {
ElMessageBox.confirm(
i18n.global.t('home.restartHelper', [i18n.global.t('home.' + type)]),
i18n.global.t('commons.msg.operate'),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
},
).then(async () => {
globalStore.isOnRestart = true;
MsgSuccess(i18n.global.t('home.operationSuccess'));
await systemRestart(type);
});
const onRestart = (type: string) => {
restartType.value = type;
let params = {
header: i18n.global.t('home.restart_' + type),
operationInfo: '',
submitInputInfo: i18n.global.t('database.restartNow'),
};
confirmDialogRef.value!.acceptParams(params);
};
const onSave = async () => {
globalStore.isOnRestart = true;
MsgSuccess(i18n.global.t('home.operationSuccess'));
await systemRestart(restartType.value);
};
const onFocus = () => {
@@ -6,7 +6,7 @@
<el-row>
<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-item :label="$t('commons.table.user')" prop="user">
<el-input v-model.trim="addForm.user" />
@@ -6,7 +6,7 @@
<el-row>
<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-checkbox v-model="form.sub">{{ $t('file.containSub') }}</el-checkbox>
</el-form-item>
@@ -44,7 +44,7 @@
<el-checkbox v-if="addForm.isDir" v-model="setRole" :label="$t('file.setRole')"></el-checkbox>
</el-form-item>
</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-row>
@@ -1,25 +1,36 @@
<template>
<el-dialog v-model="open" :title="$t('app.delete')" width="30%" :close-on-click-modal="false">
<el-row>
<el-col :span="20" :offset="2">
<el-alert class="mt-2" :show-icon="true" type="warning" :closable="false">
<div style="line-height: 20px; word-wrap: break-word">
<span>{{ $t('file.deleteHelper') }}</span>
<div>
<el-row>
<el-col :span="20" :offset="2">
<el-alert class="mt-2" :show-icon="true" type="warning" :closable="false">
<div class="delete-warn">
<span>{{ $t('file.deleteHelper') }}</span>
</div>
</el-alert>
<div class="mt-4">
<el-checkbox v-model="forceDelete">{{ $t('file.forceDeleteHelper') }}</el-checkbox>
</div>
</el-alert>
<div class="mt-4">
<el-checkbox v-model="forceDelete">{{ $t('file.forceDeleteHelper') }}</el-checkbox>
</div>
<div class="flx-align-center mb-1" v-for="(row, index) in files" :key="index">
<div>
<svg-icon v-if="row.isDir" className="table-icon mr-1 " iconName="p-file-folder"></svg-icon>
<svg-icon v-else className="table-icon mr-1" :iconName="getIconName(row.extension)"></svg-icon>
<div class="file-list">
<div class="flx-align-center mb-1" v-for="(row, index) in files" :key="index">
<div>
<svg-icon
v-if="row.isDir"
className="table-icon mr-1 "
iconName="p-file-folder"
></svg-icon>
<svg-icon
v-else
className="table-icon mr-1"
:iconName="getIconName(row.extension)"
></svg-icon>
</div>
<span class="sle">{{ row.name }}</span>
</div>
</div>
<span class="sle">{{ row.name }}</span>
</div>
</el-col>
</el-row>
</el-col>
</el-row>
</div>
<template #footer>
<span class="dialog-footer">
@@ -85,4 +96,14 @@ defineExpose({
max-height: 400px;
overflow: auto;
}
.file-list {
height: 400px;
overflow-y: auto;
}
.delete-warn {
line-height: 20px;
word-wrap: break-word;
}
</style>
@@ -127,15 +127,18 @@ const removeFile = (index: number) => {
};
const fileOnChange = (_uploadFile: UploadFile, uploadFiles: UploadFiles) => {
const reader = new FileReader();
reader.readAsDataURL(_uploadFile.raw);
reader.onload = async () => {
if (_uploadFile.size == 64 || _uploadFile.size == 0) {
uploaderFiles.value = uploadFiles;
};
reader.onerror = () => {
uploaderFiles.value = uploaderFiles.value.filter((file) => file.uid !== _uploadFile.uid);
MsgError(i18n.global.t('file.typeErrOrEmpty', [_uploadFile.name]));
};
const reader = new FileReader();
reader.readAsDataURL(_uploadFile.raw);
reader.onload = async () => {};
reader.onerror = () => {
uploaderFiles.value = uploaderFiles.value.filter((file) => file.uid !== _uploadFile.uid);
MsgError(i18n.global.t('file.typeErrOrEmpty', [_uploadFile.name]));
};
} else {
uploaderFiles.value = uploadFiles;
}
};
const clearFiles = () => {
@@ -163,7 +166,7 @@ const submit = async () => {
const fileSize = file.size;
uploadHelper.value = i18n.global.t('file.fileUploadStart', [file.name]);
if (fileSize <= 1024 * 1024 * 50) {
if (fileSize <= 1024 * 1024 * 10) {
const formData = new FormData();
formData.append('file', file.raw);
if (file.raw.webkitRelativePath != '') {
@@ -175,7 +178,7 @@ const submit = async () => {
success++;
uploaderFiles.value[i].status = 'success';
} else {
const CHUNK_SIZE = 1024 * 1024 * 20; // 10MB
const CHUNK_SIZE = 1024 * 1024 * 10;
const chunkCount = Math.ceil(fileSize / CHUNK_SIZE);
let uploadedChunkCount = 0;
for (let c = 0; c < chunkCount; c++) {
@@ -189,6 +189,7 @@ const loginRules = reactive({
password: computed(() => [{ required: true, message: i18n.global.t('commons.rule.password'), trigger: 'blur' }]),
});
let isLoggingIn = false;
const mfaButtonFocused = ref();
const mfaLoginForm = reactive({
name: '',
@@ -223,7 +224,7 @@ function handleCommand(command: string) {
}
const login = (formEl: FormInstance | undefined) => {
if (!formEl) return;
if (!formEl || isLoggingIn) return;
formEl.validate(async (valid) => {
if (!valid) return;
let requestLoginForm = {
@@ -244,6 +245,7 @@ const login = (formEl: FormInstance | undefined) => {
return;
}
try {
isLoggingIn = true;
loading.value = true;
const res = await loginApi(requestLoginForm);
if (res.code === 406) {
@@ -274,18 +276,22 @@ const login = (formEl: FormInstance | undefined) => {
} catch (error) {
loginVerify();
} finally {
isLoggingIn = false;
loading.value = false;
}
});
};
const mfaLogin = async (auto: boolean) => {
if (isLoggingIn) return;
if ((!auto && mfaLoginForm.code) || (auto && mfaLoginForm.code.length === 6)) {
isLoggingIn = true;
mfaLoginForm.name = loginForm.name;
mfaLoginForm.password = loginForm.password;
const res = await mfaLoginApi(mfaLoginForm);
if (res.code === 406) {
errMfaInfo.value = true;
isLoggingIn = false;
return;
}
globalStore.setLogStatus(true);
+2 -2
View File
@@ -1,6 +1,6 @@
<template>
<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="screenWidth > 1110 ? 'left inline-block' : ''">
<div class="login-title">
@@ -100,7 +100,7 @@ onMounted(() => {
align-items: center;
}
.login-backgroud {
.login-background {
height: 100vh;
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%)
+2 -2
View File
@@ -1,6 +1,6 @@
<template>
<div>
<div class="login-backgroud">
<div class="login-background">
<div class="login-wrapper">
<div :class="screenWidth > 1110 ? 'left inline-block' : ''">
<div class="login-title">
@@ -54,7 +54,7 @@ onMounted(() => {
align-items: center;
}
.login-backgroud {
.login-background {
height: 100vh;
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%)
+4 -4
View File
@@ -204,14 +204,14 @@ require (
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.44.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.44.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 // indirect
go.opentelemetry.io/otel v1.18.0 // indirect
go.opentelemetry.io/otel v1.19.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 // indirect
go.opentelemetry.io/otel/metric v1.18.0 // indirect
go.opentelemetry.io/otel/metric v1.19.0 // indirect
go.opentelemetry.io/otel/sdk v1.18.0 // indirect
go.opentelemetry.io/otel/trace v1.18.0 // indirect
go.opentelemetry.io/otel/trace v1.19.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
golang.org/x/arch v0.3.0 // indirect
+8 -8
View File
@@ -810,22 +810,22 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.44.0 h1:b8xjZxHbLrXAum4SxJd1Rlm7Y/fKaB+6ACI7/e5EfSA=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.44.0/go.mod h1:1ei0a32xOGkFoySu7y1DAHfcuIhC0pNZpvY2huXuMy4=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 h1:RsQi0qJ2imFfCvZabqzM9cNXBG8k6gXMv1A0cXRmH6A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0/go.mod h1:vsh3ySueQCiKPxFLvjWC4Z135gIa34TQ/NSqkDTZYUM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.44.0 h1:ewRgsETI7b5nPCK3FqKdY9mFR/9ZwtexwC26//Srjn0=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.44.0/go.mod h1:+BrAX3hlRmkYIKl2e/eSRaKLkClDTY19gzegkQ+KeEQ=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 h1:KfYpVmrjI7JuToy5k8XV3nkapjWx48k4E4JOtVstzQI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0/go.mod h1:SeQhzAEccGVZVEy7aH87Nh0km+utSpo1pTv6eMMop48=
go.opentelemetry.io/otel v1.18.0 h1:TgVozPGZ01nHyDZxK5WGPFB9QexeTMXEH7+tIClWfzs=
go.opentelemetry.io/otel v1.18.0/go.mod h1:9lWqYO0Db579XzVuCKFNPDl4s73Voa+zEck3wHaAYQI=
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 h1:IAtl+7gua134xcV3NieDhJHjjOVeJhXAnYf/0hswjUY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0/go.mod h1:w+pXobnBzh95MNIkeIuAKcHe/Uu/CX2PKIvBP6ipKRA=
go.opentelemetry.io/otel/metric v1.18.0 h1:JwVzw94UYmbx3ej++CwLUQZxEODDj/pOuTCvzhtRrSQ=
go.opentelemetry.io/otel/metric v1.18.0/go.mod h1:nNSpsVDjWGfb7chbRLUNW+PBNdcSTHD4Uu5pfFMOI0k=
go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
go.opentelemetry.io/otel/sdk v1.18.0 h1:e3bAB0wB3MljH38sHzpV/qWrOTCFrdZF2ct9F8rBkcY=
go.opentelemetry.io/otel/sdk v1.18.0/go.mod h1:1RCygWV7plY2KmdskZEDDBs4tJeHG92MdHZIluiYs/M=
go.opentelemetry.io/otel/trace v1.18.0 h1:NY+czwbHbmndxojTEKiSMHkG2ClNH2PwmcHrdo0JY10=
go.opentelemetry.io/otel/trace v1.18.0/go.mod h1:T2+SGJGuYZY3bjj5rgh/hN7KIrlpWC5nS8Mjvzckz+0=
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+2 -2
View File
@@ -1,7 +1,7 @@
sonar.projectKey=1Panel-dev_1Panel
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.
#sonar.projectName=1Panel
@@ -12,4 +12,4 @@ sonar.exclusions= frontend/src/lang/modules/*
#sonar.sources=.
# Encoding of the source code. Default is default system encoding
#sonar.sourceEncoding=UTF-8
#sonar.sourceEncoding=UTF-8