Compare commits

...
Author SHA1 Message Date
zhengkunwang 1c826525e3 style: 优化中国地图显示 (#4681) 2024-04-29 14:52:56 +08:00
zhengkunwang 3f0fd48cf3 fix: 修复 WAF 地图英文模式下的显示错误 (#4704) 2024-04-29 14:27:46 +08:00
zhengkunwang 211dbfdead feat: 伪静态增加提示 (#4678) 2024-04-29 14:27:39 +08:00
zhengkunwang aafec142cc fix: 解决低版本 OpenResty 创建网站报错的问题 (#4661) 2024-04-29 14:27:25 +08:00
ssongliu 384ff57df7 style: 优化网站列表描述信息显示 (#4726) 2024-04-29 12:21:53 +08:00
zhengkunwang 01ddd203ea fix: 解决反向代理匹配规则为空之后无法删除的问题 (#4777)
Refs https://github.com/1Panel-dev/1Panel/issues/4771
2024-04-29 12:18:11 +08:00
zhengkunwang 29aed616ac fix: 解决第二次上传同名文件不显示的问题 (#4737) 2024-04-29 12:18:01 +08:00
ssongliu 83f0df9d84 style: 优化概览页 CPU 显示 (#4721) 2024-04-29 12:16:15 +08:00
ssongliu 7aa16c8142 fix: Docker 配置自动过滤空行 (#4707)
Refs #4701
2024-04-29 12:16:10 +08:00
ssongliu 3bd505954f fix: 解决计划任务本地保存份数错误的问题 (#4692)
Refs #4671
2024-04-29 12:15:53 +08:00
John Bro 275c0522a3 fix: 快速命令管理页面中增加关键词筛选 (#4679)
Refs #3525
2024-04-29 12:15:25 +08:00
John Bro 85dec6815b fix: 创建网站可以使用相同域名问题修复 (#4669)
Refs #4624
2024-04-29 12:15:19 +08:00
ssongliu 5e4e9f762e fix: 解决概览页折线图选择失败的问题 (#4656)
Refs #4647
2024-04-29 12:15:11 +08:00
John Bro 7ae3722997 fix: SSH 连接列表显示优化 (#4650)
Refs #4546
2024-04-29 12:15:00 +08:00
31 changed files with 215 additions and 1746 deletions
+4 -4
View File
@@ -61,7 +61,7 @@ func (b *BaseApi) LoadDaemonJson(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /containers/daemonjson/update [post] // @Router /containers/daemonjson/update [post]
// @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json 配置 [key]=>[value]","formatEN":"Updated the docker daemon.json configuration [key]=>[value]"} // @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新配置 [key]","formatEN":"Updated configuration [key]"}
func (b *BaseApi) UpdateDaemonJson(c *gin.Context) { func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
var req dto.SettingUpdate var req dto.SettingUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil { if err := helper.CheckBindAndValidate(&req, c); err != nil {
@@ -84,7 +84,7 @@ func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /containers/logoption/update [post] // @Router /containers/logoption/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json 日志配置","formatEN":"Updated the docker daemon.json log option"} // @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新日志配置","formatEN":"Updated the log option"}
func (b *BaseApi) UpdateLogOption(c *gin.Context) { func (b *BaseApi) UpdateLogOption(c *gin.Context) {
var req dto.LogOption var req dto.LogOption
if err := helper.CheckBind(&req, c); err != nil { if err := helper.CheckBind(&req, c); err != nil {
@@ -107,7 +107,7 @@ func (b *BaseApi) UpdateLogOption(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /containers/ipv6option/update [post] // @Router /containers/ipv6option/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json ipv6 配置","formatEN":"Updated the docker daemon.json ipv6 option"} // @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 ipv6 配置","formatEN":"Updated the ipv6 option"}
func (b *BaseApi) UpdateIpv6Option(c *gin.Context) { func (b *BaseApi) UpdateIpv6Option(c *gin.Context) {
var req dto.Ipv6Option var req dto.Ipv6Option
if err := helper.CheckBind(&req, c); err != nil { if err := helper.CheckBind(&req, c); err != nil {
@@ -130,7 +130,7 @@ func (b *BaseApi) UpdateIpv6Option(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /containers/daemonjson/update/byfile [post] // @Router /containers/daemonjson/update/byfile [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json 配置","formatEN":"Updated the docker daemon.json configuration"} // @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新配置文件","formatEN":"Updated configuration file"}
func (b *BaseApi) UpdateDaemonJsonByFile(c *gin.Context) { func (b *BaseApi) UpdateDaemonJsonByFile(c *gin.Context) {
var req dto.DaemonJsonUpdateByFile var req dto.DaemonJsonUpdateByFile
if err := helper.CheckBindAndValidate(&req, c); err != nil { if err := helper.CheckBindAndValidate(&req, c); err != nil {
+1
View File
@@ -6,6 +6,7 @@ type SearchCommandWithPage struct {
Order string `json:"order"` Order string `json:"order"`
GroupID uint `json:"groupID"` GroupID uint `json:"groupID"`
Info string `json:"info"` Info string `json:"info"`
Name string `json:"name"`
} }
type CommandOperate struct { type CommandOperate struct {
+1 -1
View File
@@ -178,7 +178,7 @@ type WebsiteProxyConfig struct {
CacheTime int `json:"cacheTime" ` CacheTime int `json:"cacheTime" `
CacheUnit string `json:"cacheUnit"` CacheUnit string `json:"cacheUnit"`
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
Modifier string `json:"modifier" validate:"required"` Modifier string `json:"modifier"`
Match string `json:"match" validate:"required"` Match string `json:"match" validate:"required"`
ProxyPass string `json:"proxyPass" validate:"required"` ProxyPass string `json:"proxyPass" validate:"required"`
ProxyHost string `json:"proxyHost" validate:"required"` ProxyHost string `json:"proxyHost" validate:"required"`
+10
View File
@@ -16,6 +16,7 @@ type ICommandRepo interface {
Update(id uint, vars map[string]interface{}) error Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error Delete(opts ...DBOption) error
Get(opts ...DBOption) (model.Command, error) Get(opts ...DBOption) (model.Command, error)
WithLikeName(name string) DBOption
} }
func NewICommandRepo() ICommandRepo { func NewICommandRepo() ICommandRepo {
@@ -79,3 +80,12 @@ func (u *CommandRepo) Delete(opts ...DBOption) error {
} }
return db.Delete(&model.Command{}).Error return db.Delete(&model.Command{}).Error
} }
func (a CommandRepo) WithLikeName(name string) DBOption {
return func(g *gorm.DB) *gorm.DB {
if len(name) == 0 {
return g
}
return g.Where("name like ? or command like ?", "%"+name+"%", "%"+name+"%")
}
}
+1 -1
View File
@@ -65,7 +65,7 @@ func (u *CommandService) SearchForTree() ([]dto.CommandTree, error) {
} }
func (u *CommandService) SearchWithPage(search dto.SearchCommandWithPage) (int64, interface{}, error) { func (u *CommandService) SearchWithPage(search dto.SearchCommandWithPage) (int64, interface{}, error) {
total, commands, err := commandRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info), commonRepo.WithByGroupID(search.GroupID), commonRepo.WithOrderRuleBy(search.OrderBy, search.Order)) total, commands, err := commandRepo.Page(search.Page, search.PageSize, commandRepo.WithLikeName(search.Name), commonRepo.WithLikeName(search.Info), commonRepo.WithByGroupID(search.GroupID), commonRepo.WithOrderRuleBy(search.OrderBy, search.Order))
if err != nil { if err != nil {
return 0, nil, err return 0, nil, err
} }
-16
View File
@@ -2,7 +2,6 @@ package service
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"os" "os"
"path" "path"
@@ -276,21 +275,6 @@ func loadClientMap(backupAccounts string) (map[string]cronjobUploadHelper, error
if account.BackupPath != "/" { if account.BackupPath != "/" {
pathItem = strings.TrimPrefix(account.BackupPath, "/") pathItem = strings.TrimPrefix(account.BackupPath, "/")
} }
if target == constant.Local {
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(account.Vars), &varMap); err != nil {
return nil, err
}
if _, ok := varMap["dir"]; !ok {
return nil, errors.New("load local backup dir failed")
}
baseDir, ok := varMap["dir"].(string)
if ok {
pathItem = baseDir
} else {
return nil, fmt.Errorf("error type dir: %T", varMap["dir"])
}
}
clients[target] = cronjobUploadHelper{ clients[target] = cronjobUploadHelper{
client: client, client: client,
backupPath: pathItem, backupPath: pathItem,
+7
View File
@@ -288,6 +288,10 @@ func createWafConfig(website *model.Website, domains []model.WebsiteDomain) erro
if err != nil { if err != nil {
return err return err
} }
if !common.CompareVersion(nginxInstall.Version, "1.21.4.3-2-0") {
return nil
}
wafDataPath := path.Join(nginxInstall.GetPath(), "1pwaf", "data") wafDataPath := path.Join(nginxInstall.GetPath(), "1pwaf", "data")
fileOp := files.NewFileOp() fileOp := files.NewFileOp()
if !fileOp.Stat(wafDataPath) { if !fileOp.Stat(wafDataPath) {
@@ -419,6 +423,9 @@ func delWafConfig(website model.Website, force bool) error {
if err != nil { if err != nil {
return err return err
} }
if !common.CompareVersion(nginxInstall.Version, "1.21.4.3-2-0") {
return nil
}
wafDataPath := path.Join(nginxInstall.GetPath(), "1pwaf", "data") wafDataPath := path.Join(nginxInstall.GetPath(), "1pwaf", "data")
fileOp := files.NewFileOp() fileOp := files.NewFileOp()
if !fileOp.Stat(wafDataPath) { if !fileOp.Stat(wafDataPath) {
+1 -1
View File
@@ -54,7 +54,7 @@ func (c localClient) Upload(src, target string) (bool, error) {
} }
} }
if err := common.CopyFile(src, target); err != nil { if err := common.CopyFile(src, targetFilePath); err != nil {
return false, fmt.Errorf("cp file failed, err: %v", err) return false, fmt.Errorf("cp file failed, err: %v", err)
} }
return true, nil return true, nil
+1 -2
View File
@@ -314,6 +314,7 @@ func (f FileOp) DownloadFile(url, dst string) error {
if err != nil { if err != nil {
return err return err
} }
defer resp.Body.Close()
out, err := os.Create(dst) out, err := os.Create(dst)
if err != nil { if err != nil {
@@ -324,8 +325,6 @@ func (f FileOp) DownloadFile(url, dst string) error {
if _, err = io.Copy(out, resp.Body); err != nil { if _, err = io.Copy(out, resp.Body); err != nil {
return fmt.Errorf("save download file [%s] error, err %s", dst, err.Error()) return fmt.Errorf("save download file [%s] error, err %s", dst, err.Error())
} }
out.Close()
resp.Body.Close()
return nil return nil
} }
+3 -5
View File
@@ -3,7 +3,6 @@ package files
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"github.com/spf13/afero"
"io" "io"
"net/http" "net/http"
"os" "os"
@@ -12,6 +11,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"github.com/spf13/afero"
) )
func IsSymlink(mode os.FileMode) bool { func IsSymlink(mode os.FileMode) bool {
@@ -144,8 +145,5 @@ func GetParentMode(path string) (os.FileMode, error) {
} }
func IsInvalidChar(name string) bool { func IsInvalidChar(name string) bool {
if strings.Contains(name, "&") { return strings.Contains(name, "&")
return true
}
return false
} }
+8 -8
View File
@@ -1444,8 +1444,8 @@ const docTemplate = `{
"key", "key",
"value" "value"
], ],
"formatEN": "Updated the docker daemon.json configuration [key]=\u003e[value]", "formatEN": "Updated configuration [key]",
"formatZH": "更新 docker daemon.json 配置 [key]=\u003e[value]", "formatZH": "更新配置 [key]",
"paramKeys": [] "paramKeys": []
} }
} }
@@ -1484,8 +1484,8 @@ const docTemplate = `{
"x-panel-log": { "x-panel-log": {
"BeforeFunctions": [], "BeforeFunctions": [],
"bodyKeys": [], "bodyKeys": [],
"formatEN": "Updated the docker daemon.json configuration", "formatEN": "Updated configuration file",
"formatZH": "更新 docker daemon.json 配置", "formatZH": "更新配置文件",
"paramKeys": [] "paramKeys": []
} }
} }
@@ -2094,8 +2094,8 @@ const docTemplate = `{
"x-panel-log": { "x-panel-log": {
"BeforeFunctions": [], "BeforeFunctions": [],
"bodyKeys": [], "bodyKeys": [],
"formatEN": "Updated the docker daemon.json ipv6 option", "formatEN": "Updated the ipv6 option",
"formatZH": "更新 docker daemon.json ipv6 配置", "formatZH": "更新 ipv6 配置",
"paramKeys": [] "paramKeys": []
} }
} }
@@ -2233,8 +2233,8 @@ const docTemplate = `{
"x-panel-log": { "x-panel-log": {
"BeforeFunctions": [], "BeforeFunctions": [],
"bodyKeys": [], "bodyKeys": [],
"formatEN": "Updated the docker daemon.json log option", "formatEN": "Updated the log option",
"formatZH": "更新 docker daemon.json 日志配置", "formatZH": "更新日志配置",
"paramKeys": [] "paramKeys": []
} }
} }
+8 -8
View File
@@ -1437,8 +1437,8 @@
"key", "key",
"value" "value"
], ],
"formatEN": "Updated the docker daemon.json configuration [key]=\u003e[value]", "formatEN": "Updated configuration [key]",
"formatZH": "更新 docker daemon.json 配置 [key]=\u003e[value]", "formatZH": "更新配置 [key]",
"paramKeys": [] "paramKeys": []
} }
} }
@@ -1477,8 +1477,8 @@
"x-panel-log": { "x-panel-log": {
"BeforeFunctions": [], "BeforeFunctions": [],
"bodyKeys": [], "bodyKeys": [],
"formatEN": "Updated the docker daemon.json configuration", "formatEN": "Updated configuration file",
"formatZH": "更新 docker daemon.json 配置", "formatZH": "更新配置文件",
"paramKeys": [] "paramKeys": []
} }
} }
@@ -2087,8 +2087,8 @@
"x-panel-log": { "x-panel-log": {
"BeforeFunctions": [], "BeforeFunctions": [],
"bodyKeys": [], "bodyKeys": [],
"formatEN": "Updated the docker daemon.json ipv6 option", "formatEN": "Updated the ipv6 option",
"formatZH": "更新 docker daemon.json ipv6 配置", "formatZH": "更新 ipv6 配置",
"paramKeys": [] "paramKeys": []
} }
} }
@@ -2226,8 +2226,8 @@
"x-panel-log": { "x-panel-log": {
"BeforeFunctions": [], "BeforeFunctions": [],
"bodyKeys": [], "bodyKeys": [],
"formatEN": "Updated the docker daemon.json log option", "formatEN": "Updated the log option",
"formatZH": "更新 docker daemon.json 日志配置", "formatZH": "更新日志配置",
"paramKeys": [] "paramKeys": []
} }
} }
+8 -8
View File
@@ -5938,8 +5938,8 @@ paths:
bodyKeys: bodyKeys:
- key - key
- value - value
formatEN: Updated the docker daemon.json configuration [key]=>[value] formatEN: Updated configuration [key]
formatZH: 更新 docker daemon.json 配置 [key]=>[value] formatZH: 更新配置 [key]
paramKeys: [] paramKeys: []
/containers/daemonjson/update/byfile: /containers/daemonjson/update/byfile:
post: post:
@@ -5964,8 +5964,8 @@ paths:
x-panel-log: x-panel-log:
BeforeFunctions: [] BeforeFunctions: []
bodyKeys: [] bodyKeys: []
formatEN: Updated the docker daemon.json configuration formatEN: Updated configuration file
formatZH: 更新 docker daemon.json 配置 formatZH: 更新配置文件
paramKeys: [] paramKeys: []
/containers/docker/operate: /containers/docker/operate:
post: post:
@@ -6353,8 +6353,8 @@ paths:
x-panel-log: x-panel-log:
BeforeFunctions: [] BeforeFunctions: []
bodyKeys: [] bodyKeys: []
formatEN: Updated the docker daemon.json ipv6 option formatEN: Updated the ipv6 option
formatZH: 更新 docker daemon.json ipv6 配置 formatZH: 更新 ipv6 配置
paramKeys: [] paramKeys: []
/containers/limit: /containers/limit:
get: get:
@@ -6438,8 +6438,8 @@ paths:
x-panel-log: x-panel-log:
BeforeFunctions: [] BeforeFunctions: []
bodyKeys: [] bodyKeys: []
formatEN: Updated the docker daemon.json log option formatEN: Updated the log option
formatZH: 更新 docker daemon.json 日志配置 formatZH: 更新日志配置
paramKeys: [] paramKeys: []
/containers/network: /containers/network:
get: get:
+1
View File
@@ -24,6 +24,7 @@ export interface SearchWithPage {
pageSize: number; pageSize: number;
orderBy?: string; orderBy?: string;
order?: string; order?: string;
name?: string;
} }
export interface CommonModel { export interface CommonModel {
id: number; id: number;
+1
View File
@@ -139,6 +139,7 @@ export namespace Setting {
upload: string; upload: string;
} }
export interface UpgradeInfo { export interface UpgradeInfo {
testVersion: string;
newVersion: string; newVersion: string;
latestVersion: string; latestVersion: string;
releaseNote: string; releaseNote: string;
File diff suppressed because it is too large Load Diff
@@ -83,7 +83,8 @@ const seriesStyle = [
function initChart() { function initChart() {
let itemChart = echarts?.getInstanceByDom(document.getElementById(props.id) as HTMLElement); let itemChart = echarts?.getInstanceByDom(document.getElementById(props.id) as HTMLElement);
// 如果不存在,就进行初始化 const optionItem = itemChart?.getOption();
const itemSelect = optionItem?.legend;
if (itemChart == null) { if (itemChart == null) {
itemChart = echarts.init(document.getElementById(props.id) as HTMLElement); itemChart = echarts.init(document.getElementById(props.id) as HTMLElement);
} }
@@ -109,10 +110,9 @@ function initChart() {
yAxis.push({ yAxis.push({
splitLine: { splitLine: {
show: true, show: true,
//分隔辅助线
lineStyle: { lineStyle: {
type: 'dashed', //线的类型 虚线0 type: 'dashed',
opacity: theme === 'dark' ? 0.1 : 1, //透明度 opacity: theme === 'dark' ? 0.1 : 1,
}, },
}, },
...item, ...item,
@@ -120,7 +120,6 @@ function initChart() {
}); });
} }
// 把配置和数据放这里
const option = { const option = {
title: [ title: [
{ {
@@ -168,7 +167,7 @@ function initChart() {
}, },
}, },
grid: props.option.grid || { left: '7%', right: '7%', bottom: '20%' }, grid: props.option.grid || { left: '7%', right: '7%', bottom: '20%' },
legend: { legend: itemSelect || {
right: 10, right: 10,
itemWidth: 8, itemWidth: 8,
textStyle: { textStyle: {
+5
View File
@@ -58,6 +58,8 @@ const message = {
fullscreen: 'Fullscreen', fullscreen: 'Fullscreen',
quitFullscreen: 'Quit Fullscreen', quitFullscreen: 'Quit Fullscreen',
update: 'Edit', update: 'Edit',
showAll: 'Show All',
hideSome: 'Hide Some',
}, },
search: { search: {
timeStart: 'Time start', timeStart: 'Time start',
@@ -1934,6 +1936,9 @@ const message = {
acmeAccountType: 'Account Type', acmeAccountType: 'Account Type',
keyType: 'Key algorithm', keyType: 'Key algorithm',
tencentCloud: 'Tencent Cloud', tencentCloud: 'Tencent Cloud',
containWarn: 'The domain name contains the main domain, please re-enter',
rewriteHelper2:
'Applications such as WordPress installed from the app store have pseudo-static configuration by default. Repeated configuration may result in errors',
}, },
php: { php: {
short_open_tag: 'Short tag support', short_open_tag: 'Short tag support',
+4
View File
@@ -58,6 +58,8 @@ const message = {
fullscreen: '全屏', fullscreen: '全屏',
quitFullscreen: '退出全屏', quitFullscreen: '退出全屏',
update: '編輯', update: '編輯',
showAll: '顯示所有',
hideSome: '隱藏部分',
}, },
search: { search: {
timeStart: '開始時間', timeStart: '開始時間',
@@ -1810,6 +1812,8 @@ const message = {
acmeAccountType: '賬號類型', acmeAccountType: '賬號類型',
keyType: '密鑰演算法', keyType: '密鑰演算法',
tencentCloud: '騰訊雲', tencentCloud: '騰訊雲',
containWarn: '其他域名中包含主域名,请重新輸入',
rewriteHelper2: '從應用程式商店安裝的 WordPress 等應用,預設已經配置好偽靜態,重複配置可能會報錯',
}, },
php: { php: {
short_open_tag: '短標簽支持', short_open_tag: '短標簽支持',
+4
View File
@@ -58,6 +58,8 @@ const message = {
fullscreen: '全屏', fullscreen: '全屏',
quitFullscreen: '退出全屏', quitFullscreen: '退出全屏',
update: '编辑', update: '编辑',
showAll: '显示所有',
hideSome: '隐藏部分',
}, },
search: { search: {
timeStart: '开始时间', timeStart: '开始时间',
@@ -1810,6 +1812,8 @@ const message = {
acmeAccountType: '账号类型', acmeAccountType: '账号类型',
keyType: '密钥算法', keyType: '密钥算法',
tencentCloud: '腾讯云', tencentCloud: '腾讯云',
containWarn: '其他域名中包含主域名,请重新输入',
rewriteHelper2: '从应用商店安装的 WordPress 等应用,默认已经配置好伪静态,重复配置可能会报错',
}, },
php: { php: {
short_open_tag: '短标签支持', short_open_tag: '短标签支持',
+11
View File
@@ -518,3 +518,14 @@ export function getAction(action: string) {
export function getLanguage() { export function getLanguage() {
return localStorage.getItem('lang') || 'zh'; return localStorage.getItem('lang') || 'zh';
} }
export function emptyLineFilter(str: string, spilt: string) {
let list = str.split(spilt);
let results = [];
for (let i = 0; i < list.length; i++) {
if (list[i].trim() !== '') {
results.push(list[i]);
}
}
return results.join(spilt);
}
@@ -46,6 +46,7 @@ import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { updateDaemonJson } from '@/api/modules/container'; import { updateDaemonJson } from '@/api/modules/container';
import DrawerHeader from '@/components/drawer-header/index.vue'; import DrawerHeader from '@/components/drawer-header/index.vue';
import { FormInstance } from 'element-plus'; import { FormInstance } from 'element-plus';
import { emptyLineFilter } from '@/utils/util';
const emit = defineEmits<{ (e: 'search'): void }>(); const emit = defineEmits<{ (e: 'search'): void }>();
@@ -101,7 +102,7 @@ const onSave = async (formEl: FormInstance | undefined) => {
const onSubmit = async () => { const onSubmit = async () => {
loading.value = true; loading.value = true;
await updateDaemonJson('Mirrors', form.mirrors.replaceAll('\n', ',')) await updateDaemonJson('Mirrors', emptyLineFilter(form.mirrors, '\n').replaceAll('\n', ','))
.then(() => { .then(() => {
loading.value = false; loading.value = false;
emit('search'); emit('search');
@@ -46,6 +46,7 @@ import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { updateDaemonJson } from '@/api/modules/container'; import { updateDaemonJson } from '@/api/modules/container';
import DrawerHeader from '@/components/drawer-header/index.vue'; import DrawerHeader from '@/components/drawer-header/index.vue';
import { FormInstance } from 'element-plus'; import { FormInstance } from 'element-plus';
import { emptyLineFilter } from '@/utils/util';
const emit = defineEmits<{ (e: 'search'): void }>(); const emit = defineEmits<{ (e: 'search'): void }>();
@@ -97,7 +98,7 @@ const onSave = async () => {
const onSubmit = async () => { const onSubmit = async () => {
loading.value = true; loading.value = true;
await updateDaemonJson('Registries', form.registries.replaceAll('\n', ',')) await updateDaemonJson('Registries', emptyLineFilter(form.registries, '\n').replaceAll('\n', ','))
.then(() => { .then(() => {
loading.value = false; loading.value = false;
handleClose(); handleClose();
+42 -15
View File
@@ -1,12 +1,7 @@
<template> <template>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center"> <el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center">
<el-popover <el-popover placement="bottom" :width="loadWidth()" trigger="hover" v-if="chartsOption['cpu']">
placement="bottom"
:width="currentInfo.cpuPercent.length < 36 ? 300 : (currentInfo.cpuPercent.length / 12) * 150"
trigger="hover"
v-if="chartsOption['cpu']"
>
<div> <div>
<el-tooltip <el-tooltip
effect="dark" effect="dark"
@@ -14,7 +9,7 @@
v-if="baseInfo.cpuModelName.length > 40" v-if="baseInfo.cpuModelName.length > 40"
placement="top" placement="top"
> >
<el-tag> <el-tag class="cpuModeTag">
{{ baseInfo.cpuModelName.substring(0, 40) + '...' }} {{ baseInfo.cpuModelName.substring(0, 40) + '...' }}
</el-tag> </el-tag>
</el-tooltip> </el-tooltip>
@@ -22,14 +17,24 @@
{{ baseInfo.cpuModelName }} {{ baseInfo.cpuModelName }}
</el-tag> </el-tag>
</div> </div>
<el-tag class="tagClass"> <el-tag class="cpuDetailTag">{{ $t('home.core') }} *{{ baseInfo.cpuCores }}</el-tag>
{{ $t('home.core') }} *{{ baseInfo.cpuCores }} {{ $t('home.logicCore') }} *{{ <el-tag class="cpuDetailTag">{{ $t('home.logicCore') }} *{{ baseInfo.cpuLogicalCores }}</el-tag>
baseInfo.cpuLogicalCores
}}
</el-tag>
<br /> <br />
<div v-for="(item, index) of currentInfo.cpuPercent" :key="index"> <div v-for="(item, index) of currentInfo.cpuPercent" :key="index">
<el-tag class="tagCPUClass">CPU-{{ index }}: {{ formatNumber(item) }}%</el-tag> <el-tag v-if="cpuShowAll || (!cpuShowAll && index < 32)" class="tagCPUClass">
CPU-{{ index }}: {{ formatNumber(item) }}%
</el-tag>
</div>
<div v-if="currentInfo.cpuPercent.length > 32" class="mt-1 float-right">
<el-button v-if="!cpuShowAll" @click="cpuShowAll = true" link type="primary" size="small">
{{ $t('commons.button.showAll') }}
<el-icon><DArrowRight /></el-icon>
</el-button>
<el-button v-if="cpuShowAll" @click="cpuShowAll = false" link type="primary" size="small">
{{ $t('commons.button.hideSome') }}
<el-icon><DArrowLeft /></el-icon>
</el-button>
</div> </div>
<template #reference> <template #reference>
<v-charts <v-charts
@@ -302,6 +307,8 @@ const currentInfo = ref<Dashboard.CurrentInfo>({
shotTime: new Date(), shotTime: new Date(),
}); });
const cpuShowAll = ref();
const chartsOption = ref({ cpu: null, memory: null, load: null }); const chartsOption = ref({ cpu: null, memory: null, load: null });
const acceptParams = (current: Dashboard.CurrentInfo, base: Dashboard.BaseInfo, isInit: boolean): void => { const acceptParams = (current: Dashboard.CurrentInfo, base: Dashboard.BaseInfo, isInit: boolean): void => {
@@ -359,6 +366,14 @@ const goGPU = () => {
router.push({ name: 'GPU' }); router.push({ name: 'GPU' });
}; };
const loadWidth = () => {
if (!cpuShowAll.value || currentInfo.value.cpuPercent.length < 32) {
return 310;
}
let line = Math.floor(currentInfo.value.cpuPercent.length / 16);
return line * 141 + 28;
};
function formatNumber(val: number) { function formatNumber(val: number) {
return Number(val.toFixed(2)); return Number(val.toFixed(2));
} }
@@ -369,6 +384,18 @@ defineExpose({
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.cpuModeTag {
justify-content: flex-start !important;
text-align: left !important;
width: 280px;
}
.cpuDetailTag {
justify-content: flex-start !important;
text-align: left !important;
width: 140px;
margin-top: 3px;
margin-left: 1px;
}
.tagClass { .tagClass {
margin-top: 3px; margin-top: 3px;
} }
@@ -378,8 +405,8 @@ defineExpose({
text-align: left !important; text-align: left !important;
float: left; float: left;
margin-top: 3px; margin-top: 3px;
margin-left: 4px; margin-left: 1px;
width: 100px; width: 140px;
} }
.buttonClass { .buttonClass {
@@ -111,27 +111,24 @@ let uploadHelper = ref('');
const em = defineEmits(['close']); const em = defineEmits(['close']);
const handleClose = () => { const handleClose = () => {
open.value = false; open.value = false;
uploadRef.value!.clearFiles(); clearFiles();
em('close', false); em('close', false);
}; };
const state = reactive({ const state = reactive({
uploadEle: null, uploadEle: null,
}); });
const uploaderFiles = ref<UploadFiles>([]); const uploaderFiles = ref<UploadFiles>([]);
const isUploadFolder = ref(false);
const hoverIndex = ref(null); const hoverIndex = ref(null);
const uploadType = ref('file');
const tmpFiles = ref<UploadFiles>([]); const tmpFiles = ref<UploadFiles>([]);
const breakFlag = ref(false); const breakFlag = ref(false);
const upload = (command: string) => { const upload = (command: string) => {
uploadType.value = command;
if (command == 'dir') { if (command == 'dir') {
state.uploadEle.webkitdirectory = true; state.uploadEle.webkitdirectory = true;
} else { } else {
state.uploadEle.webkitdirectory = false; state.uploadEle.webkitdirectory = false;
} }
isUploadFolder.value = true; uploadRef.value.$el.querySelector('input').value = '';
uploadRef.value.$el.querySelector('input').click(); uploadRef.value.$el.querySelector('input').click();
}; };
@@ -253,10 +250,11 @@ const fileOnChange = (_uploadFile: UploadFile, uploadFiles: UploadFiles) => {
const clearFiles = () => { const clearFiles = () => {
uploadRef.value!.clearFiles(); uploadRef.value!.clearFiles();
uploaderFiles.value = [];
}; };
const handleExceed: UploadProps['onExceed'] = () => { const handleExceed: UploadProps['onExceed'] = () => {
uploadRef.value!.clearFiles(); clearFiles();
MsgWarning(i18n.global.t('file.uploadOverLimit')); MsgWarning(i18n.global.t('file.uploadOverLimit'));
}; };
@@ -339,8 +337,7 @@ const submit = async () => {
loading.value = false; loading.value = false;
uploadHelper.value = ''; uploadHelper.value = '';
if (success == files.length) { if (success == files.length) {
uploadRef.value!.clearFiles(); clearFiles();
uploaderFiles.value = [];
MsgSuccess(i18n.global.t('file.uploadSuccess')); MsgSuccess(i18n.global.t('file.uploadSuccess'));
} }
} }
@@ -16,13 +16,20 @@
</el-button> </el-button>
</template> </template>
<template #search> <template #search>
<el-select v-model="group" @change="search()" clearable class="p-w-200"> <el-row :gutter="5">
<template #prefix>{{ $t('terminal.group') }}</template> <el-col :xs="24" :sm="20" :md="20" :lg="20" :xl="20">
<el-option :label="$t('commons.table.all')" value=""></el-option> <el-select v-model="group" @change="search()" clearable class="p-w-200">
<div v-for="item in groupList" :key="item.name"> <template #prefix>{{ $t('terminal.group') }}</template>
<el-option :value="item.id" :label="item.name" /> <el-option :label="$t('commons.table.all')" value=""></el-option>
</div> <div v-for="item in groupList" :key="item.name">
</el-select> <el-option :value="item.id" :label="item.name" />
</div>
</el-select>
</el-col>
<el-col :xs="24" :sm="4" :md="4" :lg="4" :xl="4">
<TableSearch @search="search()" v-model:searchName="commandReq.name" />
</el-col>
</el-row>
</template> </template>
<template #main> <template #main>
<ComplexTable <ComplexTable
@@ -160,6 +167,10 @@ let commandInfo = reactive<Command.CommandOperate>({
command: '', command: '',
}); });
const commandReq = reactive({
name: '',
});
const cmdVisible = ref<boolean>(false); const cmdVisible = ref<boolean>(false);
const loadGroups = async () => { const loadGroups = async () => {
@@ -274,6 +285,7 @@ const search = async (column?: any) => {
info: info.value, info: info.value,
orderBy: paginationConfig.orderBy, orderBy: paginationConfig.orderBy,
order: paginationConfig.order, order: paginationConfig.order,
name: commandReq.name,
}; };
loading.value = true; loading.value = true;
await getCommandPage(params) await getCommandPage(params)
@@ -105,7 +105,7 @@
<span v-if="node.label.length <= 25"> <span v-if="node.label.length <= 25">
<a @click="onClickConn(node, data)">{{ node.label }}</a> <a @click="onClickConn(node, data)">{{ node.label }}</a>
</span> </span>
<el-tooltip v-else :content="node.label" placement="top-start"> <el-tooltip v-else :content="node.label" placement="right">
<span> <span>
<a @click="onClickConn(node, data)"> <a @click="onClickConn(node, data)">
{{ node.label.substring(0, 22) }}... {{ node.label.substring(0, 22) }}...
@@ -126,7 +126,6 @@ const proxyForm = ref<FormInstance>();
const rules = ref({ const rules = ref({
name: [Rules.requiredInput, Rules.appName], name: [Rules.requiredInput, Rules.appName],
match: [Rules.requiredInput], match: [Rules.requiredInput],
modifier: [Rules.requiredInput],
cacheTime: [Rules.requiredInput, checkNumberRange(1, 65535)], cacheTime: [Rules.requiredInput, checkNumberRange(1, 65535)],
proxyPass: [Rules.requiredInput], proxyPass: [Rules.requiredInput],
proxyHost: [Rules.requiredInput], proxyHost: [Rules.requiredInput],
@@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<el-form-item :label="$t('website.rewriteMode')"> <el-form-item :label="$t('website.rewriteMode')">
<el-select v-model="req.name" filterable @change="getRewriteConfig(req.name)"> <el-select v-model="req.name" filterable @change="getRewriteConfig(req.name)" class="p-w-200">
<el-option :label="$t('website.current')" :value="'current'"></el-option> <el-option :label="$t('website.current')" :value="'current'"></el-option>
<el-option <el-option
v-for="(rewrite, index) in Rewrites" v-for="(rewrite, index) in Rewrites"
@@ -11,6 +11,7 @@
></el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-text type="warning">{{ $t('website.rewriteHelper2') }}</el-text>
<Codemirror <Codemirror
ref="codeRef" ref="codeRef"
v-loading="loading" v-loading="loading"
@@ -26,7 +27,7 @@
:extensions="extensions" :extensions="extensions"
v-model="content" v-model="content"
/> />
<div style="margin-top: 10px"> <div class="mt-2">
<el-form-item> <el-form-item>
<el-alert :title="$t('website.rewriteHelper')" type="info" :closable="false" /> <el-alert :title="$t('website.rewriteHelper')" type="info" :closable="false" />
</el-form-item> </el-form-item>
@@ -349,7 +349,7 @@ import { ElForm, FormInstance } from 'element-plus';
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import Params from '@/views/app-store/detail/params/index.vue'; import Params from '@/views/app-store/detail/params/index.vue';
import Check from '../check/index.vue'; import Check from '../check/index.vue';
import { MsgSuccess } from '@/utils/message'; import { MsgError, MsgSuccess } from '@/utils/message';
import { GetGroupList } from '@/api/modules/group'; import { GetGroupList } from '@/api/modules/group';
import { Group } from '@/api/interface/group'; import { Group } from '@/api/interface/group';
import { SearchRuntimes } from '@/api/modules/runtime'; import { SearchRuntimes } from '@/api/modules/runtime';
@@ -582,6 +582,16 @@ const changeAppType = (type: string) => {
} }
}; };
function isSubsetOfStrArray(primaryDomain: string, otherDomains: string): boolean {
const arr: string[] = otherDomains.split('\n');
for (const item of arr) {
if (primaryDomain === item) {
return false;
}
}
return true;
}
const submit = async (formEl: FormInstance | undefined) => { const submit = async (formEl: FormInstance | undefined) => {
if (!formEl) return; if (!formEl) return;
await formEl.validate((valid) => { await formEl.validate((valid) => {
@@ -589,6 +599,12 @@ const submit = async (formEl: FormInstance | undefined) => {
return; return;
} }
loading.value = true; loading.value = true;
const flag = isSubsetOfStrArray(website.value.primaryDomain, website.value.otherDomains);
if (!flag) {
MsgError(i18n.global.t('website.containWarn'));
loading.value = false;
return false;
}
PreCheck({}) PreCheck({})
.then((res) => { .then((res) => {
if (res.data) { if (res.data) {
+6 -6
View File
@@ -111,11 +111,12 @@
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('website.remark')" prop="remark" min-width="120px"> <el-table-column
<template #default="{ row }"> :label="$t('website.remark')"
<MsgInfo :info="row.remark" /> prop="remark"
</template> show-overflow-tooltip
</el-table-column> min-width="120px"
></el-table-column>
<el-table-column <el-table-column
:label="$t('commons.table.protocol')" :label="$t('commons.table.protocol')"
prop="protocol" prop="protocol"
@@ -194,7 +195,6 @@ import { dateFormatSimple } from '@/utils/util';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { VideoPlay, VideoPause } from '@element-plus/icons-vue'; import { VideoPlay, VideoPause } from '@element-plus/icons-vue';
import MsgInfo from '@/components/msg-info/index.vue';
import { GetGroupList } from '@/api/modules/group'; import { GetGroupList } from '@/api/modules/group';
import { Group } from '@/api/interface/group'; import { Group } from '@/api/interface/group';
import { GlobalStore } from '@/store'; import { GlobalStore } from '@/store';