feat: 面板关于信息实现

This commit is contained in:
ssongliu 2022-09-19 19:42:06 +08:00 committed by ssongliu
parent f99a2ae656
commit 99edb9c7ad
32 changed files with 513 additions and 110 deletions

1
.gitignore vendored
View File

@ -15,3 +15,4 @@
# Dependency directories (remove the comment below to include it)
# vendor/
/pkg/
backend/__debug_bin

Binary file not shown.

View File

@ -98,7 +98,7 @@ func (b *BaseApi) SafeEntrance(c *gin.Context) {
}
codeWithMD5 := encrypt.Md5(code)
cookieValue, _ := encrypt.StringEncrypt(codeWithMD5)
c.SetCookie(codeWithMD5, cookieValue, 86400, "", "", false, false)
c.SetCookie(codeWithMD5, cookieValue, 604800, "", "", false, false)
helper.SuccessWithData(c, nil)
}

View File

@ -88,25 +88,12 @@ func (b *BaseApi) UpdateBackup(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) PageBackup(c *gin.Context) {
var req dto.PageInfo
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
total, list, err := backupService.Page(req)
func (b *BaseApi) ListBackup(c *gin.Context) {
data, err := backupService.List()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Items: list,
Total: total,
})
helper.SuccessWithData(c, data)
}

View File

@ -3,7 +3,6 @@ package dto
import "time"
type BackupOperate struct {
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
Bucket string `json:"bucket"`
Credential string `json:"credential"`
@ -13,7 +12,6 @@ type BackupOperate struct {
type BackupInfo struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
Name string `json:"name"`
Type string `json:"type"`
Bucket string `json:"bucket"`
Vars string `json:"vars"`

View File

@ -2,10 +2,8 @@ package model
type BackupAccount struct {
BaseModel
Name string `gorm:"type:varchar(64);not null" json:"name"`
Type string `gorm:"type:varchar(64)" json:"type"`
Type string `gorm:"type:varchar(64);unique;not null" json:"type"`
Bucket string `gorm:"type:varchar(256)" json:"bucket"`
Credential string `gorm:"type:varchar(256)" json:"credential"`
Vars string `gorm:"type:longText" json:"vars"`
Status string `gorm:"type:varchar(64)" json:"status"`
}

View File

@ -8,7 +8,8 @@ import (
type BackupRepo struct{}
type IBackupRepo interface {
Page(page, size int, opts ...DBOption) (int64, []model.BackupAccount, error)
Get(opts ...DBOption) (model.BackupAccount, error)
List(opts ...DBOption) ([]model.BackupAccount, error)
Create(backup *model.BackupAccount) error
Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error
@ -28,7 +29,7 @@ func (u *BackupRepo) Get(opts ...DBOption) (model.BackupAccount, error) {
return backup, err
}
func (u *BackupRepo) Page(page, size int, opts ...DBOption) (int64, []model.BackupAccount, error) {
func (u *BackupRepo) List(opts ...DBOption) ([]model.BackupAccount, error) {
var ops []model.BackupAccount
db := global.DB.Model(&model.BackupAccount{})
for _, opt := range opts {
@ -36,8 +37,8 @@ func (u *BackupRepo) Page(page, size int, opts ...DBOption) (int64, []model.Back
}
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error
return count, ops, err
err := db.Find(&ops).Error
return ops, err
}
func (u *BackupRepo) Create(backup *model.BackupAccount) error {

View File

@ -26,6 +26,12 @@ func (c *CommonRepo) WithByName(name string) DBOption {
}
}
func (c *CommonRepo) WithByType(name string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("type = ?", name)
}
}
func (c *CommonRepo) WithLikeName(name string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("name like ?", "%"+name+"%")

View File

@ -102,7 +102,7 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (
sessionUser, err := global.SESSION.Get(sID)
if err != nil {
sID = uuid.NewV4().String()
c.SetCookie(constant.SessionName, sID, lifeTime, "", "", false, false)
c.SetCookie(constant.SessionName, sID, 604800, "", "", false, false)
err := global.SESSION.Set(sID, sessionUser, lifeTime)
if err != nil {
return nil, err

View File

@ -13,7 +13,7 @@ import (
type BackupService struct{}
type IBackupService interface {
Page(search dto.PageInfo) (int64, interface{}, error)
List() ([]dto.BackupInfo, error)
Create(backupDto dto.BackupOperate) error
GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error)
Update(id uint, upMap map[string]interface{}) error
@ -24,21 +24,21 @@ func NewIBackupService() IBackupService {
return &BackupService{}
}
func (u *BackupService) Page(search dto.PageInfo) (int64, interface{}, error) {
total, ops, err := backupRepo.Page(search.Page, search.PageSize, commonRepo.WithOrderBy("created_at desc"))
func (u *BackupService) List() ([]dto.BackupInfo, error) {
ops, err := backupRepo.List(commonRepo.WithOrderBy("created_at desc"))
var dtobas []dto.BackupInfo
for _, group := range ops {
var item dto.BackupInfo
if err := copier.Copy(&item, &group); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
dtobas = append(dtobas, item)
}
return total, dtobas, err
return dtobas, err
}
func (u *BackupService) Create(backupDto dto.BackupOperate) error {
backup, _ := backupRepo.Get(commonRepo.WithByName(backupDto.Name))
backup, _ := backupRepo.Get(commonRepo.WithByType(backupDto.Type))
if backup.ID != 0 {
return constant.ErrRecordExist
}

View File

@ -50,17 +50,6 @@ func (u *SettingService) Update(c *gin.Context, key, value string) error {
if err := settingRepo.Update(key, value); err != nil {
return err
}
switch key {
case "UserName":
sID, _ := c.Cookie(constant.SessionName)
if sID != "" {
c.SetCookie(constant.SessionName, sID, -1, "", "", false, false)
err := global.SESSION.Delete(sID)
if err != nil {
return err
}
}
}
return settingRepo.Update(key, value)
}

View File

@ -81,7 +81,7 @@ var AddTableSetting = &gormigrate.Migration{
if err := tx.Create(&model.Setting{Key: "ServerPort", Value: "4004"}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "SecurityEntrance", Value: "89dc6ae8"}).Error; err != nil {
if err := tx.Create(&model.Setting{Key: "SecurityEntrance", Value: "onepanel"}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "PasswordTimeOut", Value: time.Now().AddDate(0, 0, 10).Format("2016.01.02 15:04:05")}).Error; err != nil {
@ -127,10 +127,8 @@ var AddTableBackupAccount = &gormigrate.Migration{
return err
}
item := &model.BackupAccount{
Name: "Default Local",
Type: "LOCAL",
Status: "VALID",
Vars: "{\"dir\":\"/opt/1Panel/backup\"}",
Type: "LOCAL",
Vars: "{\"dir\":\"/opt/1Panel/backup\"}",
}
if err := tx.Create(item).Error; err != nil {
return err

View File

@ -1,7 +1,10 @@
package middleware
import (
"strconv"
"github.com/1Panel-dev/1Panel/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/app/repo"
"github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/global"
"github.com/gin-gonic/gin"
@ -17,10 +20,18 @@ func SessionAuth() gin.HandlerFunc {
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrTypeNotLogin, nil)
return
}
if _, err := global.SESSION.Get(sId); err != nil {
psession, err := global.SESSION.Get(sId)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrTypeNotLogin, nil)
return
}
settingRepo := repo.NewISettingRepo()
setting, err := settingRepo.Get(settingRepo.WithByKey("SessionTimeout"))
if err != nil {
global.LOG.Errorf("create operation record failed, err: %v", err)
}
lifeTime, _ := strconv.Atoi(setting.Value)
_ = global.SESSION.Set(sId, psession, lifeTime)
c.Next()
}
}

View File

@ -14,7 +14,7 @@ func (s *BackupRouter) InitBackupRouter(Router *gin.RouterGroup) {
withRecordRouter := Router.Group("backups").Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.OperationRecord())
baseApi := v1.ApiGroupApp.BaseApi
{
baRouter.POST("/search", baseApi.PageBackup)
baRouter.GET("/search", baseApi.ListBackup)
baRouter.POST("/buckets", baseApi.ListBuckets)
withRecordRouter.POST("", baseApi.CreateBackup)
withRecordRouter.POST("/del", baseApi.DeleteBackup)

View File

@ -1,7 +1,6 @@
export namespace Backup {
export interface BackupInfo {
id: number;
name: string;
type: string;
bucket: string;
vars: string;
@ -9,7 +8,6 @@ export namespace Backup {
}
export interface BackupOperate {
id: number;
name: string;
type: string;
bucket: string;
credential: string;

View File

@ -1,9 +1,8 @@
import http from '@/api';
import { Backup } from '../interface/backup';
import { ResPage, ReqPage } from '../interface';
export const getBackupList = (params: ReqPage) => {
return http.post<ResPage<Backup.BackupInfo>>(`/backups/search`, params);
export const getBackupList = () => {
return http.get<Array<Backup.BackupInfo>>(`/backups/search`);
};
export const addBackup = (params: Backup.BackupOperate) => {

View File

@ -1,9 +1,9 @@
@font-face {
font-family: "panel"; /* Project id 3575356 */
src: url('iconfont.woff2?t=1662692062751') format('woff2'),
url('iconfont.woff?t=1662692062751') format('woff'),
url('iconfont.ttf?t=1662692062751') format('truetype'),
url('iconfont.svg?t=1662692062751#panel') format('svg');
src: url('iconfont.woff2?t=1663584463212') format('woff2'),
url('iconfont.woff?t=1663584463212') format('woff'),
url('iconfont.ttf?t=1663584463212') format('truetype'),
url('iconfont.svg?t=1663584463212#panel') format('svg');
}
.panel {
@ -14,6 +14,38 @@
-moz-osx-font-smoothing: grayscale;
}
.p-taolun:before {
content: "\e602";
}
.p-StarStar:before {
content: "\e635";
}
.p-bug:before {
content: "\e616";
}
.p-SFTP:before {
content: "\e647";
}
.p-huaban88:before {
content: "\e67c";
}
.p-oss:before {
content: "\e607";
}
.p-s3:before {
content: "\e8e4";
}
.p-minio:before {
content: "\e63c";
}
.p-logout:before {
content: "\e8fe";
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,247 @@
{
"id": "3575356",
"name": "panel",
"font_family": "panel",
"css_prefix_text": "p-",
"description": "",
"glyphs": [
{
"icon_id": "1760690",
"name": "讨论",
"font_class": "taolun",
"unicode": "e602",
"unicode_decimal": 58882
},
{
"icon_id": "5192988",
"name": "Star Star",
"font_class": "StarStar",
"unicode": "e635",
"unicode_decimal": 58933
},
{
"icon_id": "6642940",
"name": "bug",
"font_class": "bug",
"unicode": "e616",
"unicode_decimal": 58902
},
{
"icon_id": "13532955",
"name": "SFTP",
"font_class": "SFTP",
"unicode": "e647",
"unicode_decimal": 58951
},
{
"icon_id": "15337722",
"name": "Logo GitHub",
"font_class": "huaban88",
"unicode": "e67c",
"unicode_decimal": 59004
},
{
"icon_id": "16268521",
"name": "oss",
"font_class": "oss",
"unicode": "e607",
"unicode_decimal": 58887
},
{
"icon_id": "17895439",
"name": "Amazon S3上传",
"font_class": "s3",
"unicode": "e8e4",
"unicode_decimal": 59620
},
{
"icon_id": "20290513",
"name": "minio",
"font_class": "minio",
"unicode": "e63c",
"unicode_decimal": 58940
},
{
"icon_id": "924436",
"name": "logout",
"font_class": "logout",
"unicode": "e8fe",
"unicode_decimal": 59646
},
{
"icon_id": "837256",
"name": "terminal",
"font_class": "terminal2",
"unicode": "e82a",
"unicode_decimal": 59434
},
{
"icon_id": "8358944",
"name": "英文5",
"font_class": "yingwen",
"unicode": "e6c3",
"unicode_decimal": 59075
},
{
"icon_id": "8358949",
"name": "中文5",
"font_class": "zhongwen",
"unicode": "e6c8",
"unicode_decimal": 59080
},
{
"icon_id": "11487994",
"name": "calendar",
"font_class": "plan",
"unicode": "e746",
"unicode_decimal": 59206
},
{
"icon_id": "11488064",
"name": "integral",
"font_class": "database",
"unicode": "e754",
"unicode_decimal": 59220
},
{
"icon_id": "11488108",
"name": "rejected-order",
"font_class": "rejected-order",
"unicode": "e75e",
"unicode_decimal": 59230
},
{
"icon_id": "11488148",
"name": "tool",
"font_class": "toolbox",
"unicode": "e769",
"unicode_decimal": 59241
},
{
"icon_id": "4765743",
"name": "earth",
"font_class": "website",
"unicode": "e781",
"unicode_decimal": 59265
},
{
"icon_id": "4765891",
"name": "setting",
"font_class": "config",
"unicode": "e78e",
"unicode_decimal": 59278
},
{
"icon_id": "4765962",
"name": "app store",
"font_class": "appstore1",
"unicode": "e792",
"unicode_decimal": 59282
},
{
"icon_id": "4765971",
"name": "detail",
"font_class": "log",
"unicode": "e793",
"unicode_decimal": 59283
},
{
"icon_id": "4766440",
"name": "sever",
"font_class": "host",
"unicode": "e7b1",
"unicode_decimal": 59313
},
{
"icon_id": "4766685",
"name": "home",
"font_class": "home",
"unicode": "e7c6",
"unicode_decimal": 59334
},
{
"icon_id": "19688849",
"name": "应用商店",
"font_class": "appstore",
"unicode": "eb65",
"unicode_decimal": 60261
},
{
"icon_id": "3876424",
"name": "docker",
"font_class": "docker",
"unicode": "e659",
"unicode_decimal": 58969
},
{
"icon_id": "15838431",
"name": "arrow-right",
"font_class": "arrow-right",
"unicode": "e665",
"unicode_decimal": 58981
},
{
"icon_id": "6172786",
"name": "terminal",
"font_class": "terminal",
"unicode": "e864",
"unicode_decimal": 59492
},
{
"icon_id": "14772948",
"name": "terminal",
"font_class": "terminal1",
"unicode": "e663",
"unicode_decimal": 58979
},
{
"icon_id": "7533292",
"name": "中英文",
"font_class": "language",
"unicode": "e605",
"unicode_decimal": 58885
},
{
"icon_id": "22551111",
"name": "主题",
"font_class": "theme",
"unicode": "e638",
"unicode_decimal": 58936
},
{
"icon_id": "22735864",
"name": "文件类型-文件夹",
"font_class": "file-folder",
"unicode": "66",
"unicode_decimal": 102
},
{
"icon_id": "22761833",
"name": "文件类型-未知文件",
"font_class": "file-unknown",
"unicode": "233",
"unicode_decimal": 563
},
{
"icon_id": "22761837",
"name": "文件类型-Txt",
"font_class": "file-txt",
"unicode": "74",
"unicode_decimal": 116
},
{
"icon_id": "19671156",
"name": "txt-1",
"font_class": "file-normal",
"unicode": "e7ac",
"unicode_decimal": 59308
},
{
"icon_id": "22761832",
"name": "文件类型-压缩包",
"font_class": "file-zip",
"unicode": "e606",
"unicode_decimal": 58886
}
]
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -262,6 +262,7 @@ export default {
downloading: 'Downloading...',
},
setting: {
all: 'All',
panel: 'Panel',
emailHelper: 'For password retrieval',
title: 'Panel alias',
@ -282,6 +283,8 @@ export default {
backup: 'Backup',
serverDisk: 'Server disks',
OSS: 'Ali OSS',
S3: 'Amazon S3',
backupAccount: 'Backup account',
loadBucket: 'Get bucket',
accountName: 'Account name',
@ -298,11 +301,11 @@ export default {
'The recommended port range is 8888 to 65535. Note: If the server has a security group, permit the new port from the security group in advance',
safeEntrance: 'Security entrance',
safeEntranceHelper:
'Panel management portal. You can log in to the panel only through a specified security portal, for example, / 89DC6AE8',
passwordTimeout: 'Password expiration Time',
'Panel management portal. You can log in to the panel only through a specified security portal, for example: onepanel',
passwordTimeout: 'Expiration Time',
timeoutHelper:
'[ {0} days ] The panel password is about to expire. After the expiration, you need to reset the password',
complexity: 'Password complexity verification',
complexity: 'Complexity verification',
complexityHelper:
'The password must contain at least eight characters and contain at least three uppercase letters, lowercase letters, digits, and special characters',
mfa: 'MFA',
@ -324,5 +327,12 @@ export default {
emailAddr: 'Service address',
emailSMTP: 'SMTP code',
secret: 'Secret',
about: 'About',
project: 'Project Address',
issue: 'Feedback',
chat: 'Community Discussion',
star: 'Star',
description: 'A modern Linux panel tool',
},
};

View File

@ -259,6 +259,7 @@ export default {
downloading: '正在下载...',
},
setting: {
all: '全部',
panel: '面板',
emailHelper: '用于密码找回',
title: '面板别名',
@ -278,6 +279,8 @@ export default {
backup: '备份',
serverDisk: '服务器磁盘',
OSS: '阿里云 OSS',
S3: '亚马逊 S3 云存储',
backupAccount: '备份账号',
loadBucket: '获取桶',
accountName: '账户名称',
@ -292,7 +295,7 @@ export default {
panelPort: '面板端口',
portHelper: '建议端口范围8888 - 65535注意有安全组的服务器请提前在安全组放行新端口',
safeEntrance: '安全入口',
safeEntranceHelper: '面板管理入口设置后只能通过指定安全入口登录面板,: 89dc6ae8',
safeEntranceHelper: '面板管理入口设置后只能通过指定安全入口登录面板: onepanel',
passwordTimeout: '密码过期时间',
timeoutHelper: ' {0} 天后 面板密码即将过期过期后需要重新设置密码',
complexity: '密码复杂度验证',
@ -318,5 +321,10 @@ export default {
secret: '密钥',
about: '关于',
project: '项目地址',
issue: '问题反馈',
chat: '参与讨论',
star: '点亮 Star',
description: '一个现代化的 Linux 面板工具',
},
};

View File

@ -410,23 +410,23 @@ onBeforeMount(() => {
</script>
<style lang="scss" scoped>
.terminal-tabs {
:deep(.el-tabs__header) {
:deep .el-tabs__header {
padding: 0;
position: relative;
margin: 0 0 3px 0;
}
:deep(.el-tabs__nav) {
::deep .el-tabs__nav {
white-space: nowrap;
position: relative;
transition: transform var(--el-transition-duration);
float: left;
z-index: calc(var(--el-index-normal) + 1);
}
:deep(.el-tabs__item) {
:deep .el-tabs__item {
color: #575758;
padding: 0 0px;
}
:deep(.el-tabs__item.is-active) {
:deep .el-tabs__item.is-active {
color: #ebeef5;
background-color: #575758;
}
@ -441,7 +441,7 @@ onBeforeMount(() => {
.fullScreen {
position: absolute;
right: 50px;
top: 6px;
top: 86px;
font-weight: 600;
font-size: 14px;
}

View File

@ -2,18 +2,27 @@
<div>
<el-card class="topCard">
<el-radio-group v-model="activeNames">
<el-radio-button class="topButton" size="large" label="all">全部</el-radio-button>
<el-radio-button class="topButton" size="large" label="panel">面板</el-radio-button>
<el-radio-button class="topButton" size="large" label="safe">安全</el-radio-button>
<el-radio-button class="topButton" size="large" label="backup">备份</el-radio-button>
<el-radio-button class="topButton" size="large" label="monitor">监控</el-radio-button>
<el-radio-button class="topButton" size="large" label="about">关于</el-radio-button>
<el-radio-button class="topButton" size="large" label="all">{{ $t('setting.all') }}</el-radio-button>
<el-radio-button class="topButton" size="large" label="panel">
{{ $t('setting.panel') }}
</el-radio-button>
<el-radio-button class="topButton" size="large" label="safe">{{ $t('setting.safe') }}</el-radio-button>
<el-radio-button class="topButton" size="large" label="backup">
{{ $t('setting.backup') }}
</el-radio-button>
<el-radio-button class="topButton" size="large" label="monitor">
{{ $t('menu.monitor') }}
</el-radio-button>
<el-radio-button class="topButton" size="large" label="about">
{{ $t('setting.about') }}
</el-radio-button>
</el-radio-group>
</el-card>
<Panel v-if="activeNames === 'all' || activeNames === 'panel'" :settingInfo="form" @on-save="SaveSetting" />
<Safe v-if="activeNames === 'all' || activeNames === 'safe'" :settingInfo="form" @on-save="SaveSetting" />
<Backup v-if="activeNames === 'all' || activeNames === 'backup'" :settingInfo="form" @on-save="SaveSetting" />
<Monitor v-if="activeNames === 'all' || activeNames === 'monitor'" :settingInfo="form" @on-save="SaveSetting" />
<About v-if="activeNames === 'all' || activeNames === 'about'" />
</div>
</template>
@ -25,6 +34,7 @@ import Panel from '@/views/setting/tabs/panel.vue';
import Safe from '@/views/setting/tabs/safe.vue';
import Backup from '@/views/setting/tabs/backup.vue';
import Monitor from '@/views/setting/tabs/monitor.vue';
import About from '@/views/setting/tabs/about.vue';
import { GlobalStore } from '@/store';
import { useTheme } from '@/hooks/use-theme';
import { useI18n } from 'vue-i18n';

View File

@ -0,0 +1,50 @@
<template>
<el-card style="margin-top: 20px">
<template #header>
<div class="card-header">
<span>{{ $t('setting.about') }}</span>
</div>
</template>
<div style="text-align: center">
<div style="justify-self: center">
<img style="width: 80px" src="@/assets/images/ko_image.png" />
</div>
<h1>1Panel</h1>
<h3>{{ $t('setting.description') }}</h3>
<h3>v0.0.1</h3>
<div style="margin-top: 10px">
<svg-icon style="font-size: 9px" iconName="p-huaban88"></svg-icon>
<el-link @click="toGithub">
<span>{{ $t('setting.project') }}</span>
</el-link>
<svg-icon style="font-size: 9px; margin-left: 15px" iconName="p-bug"></svg-icon>
<el-link @click="toIssue">
<span>{{ $t('setting.issue') }}</span>
</el-link>
<svg-icon style="font-size: 9px; margin-left: 15px" iconName="p-taolun"></svg-icon>
<el-link @click="toTalk">
<span>{{ $t('setting.chat') }}</span>
</el-link>
<svg-icon style="font-size: 9px; margin-left: 15px" iconName="p-StarStar"></svg-icon>
<el-link @click="toGithubStar">
<span>{{ $t('setting.star') }}</span>
</el-link>
</div>
</div>
</el-card>
</template>
<script lang="ts" setup>
const toGithub = () => {
window.open('https://github.com/1Panel-dev/1Panel', '_blank');
};
const toIssue = () => {
window.open('https://github.com/1Panel-dev/1Panel/issues', '_blank');
};
const toTalk = () => {
window.open('https://github.com/1Panel-dev/1Panel', '_blank');
};
const toGithubStar = () => {
window.open('https://github.com/1Panel-dev/1Panel', '_blank');
};
</script>

View File

@ -13,8 +13,9 @@
<el-card class="el-card">
<template #header>
<div class="card-header">
<svg-icon style="font-size: 7px" :iconName="loadIconName(item.type)"></svg-icon>
<span style="font-size: 16px; font-weight: 500">
[{{ item.type === 'LOCAL' ? $t('setting.serverDisk') : item.type }}] {{ item.name }}
{{ loadBackupName(item.type) }}
</span>
<div style="float: right">
<el-button @click="onEdit(item)">{{ $t('commons.button.edit') }}</el-button>
@ -25,7 +26,7 @@
</div>
</template>
<el-form label-position="left" label-width="130px">
<el-form-item v-if="item.type === 'LOCAL'" label="Dir">
<el-form-item v-if="item.type === 'LOCAL'" label="Directory">
{{ item.varsJson['dir'] }}
</el-form-item>
<el-form-item v-if="hasBucket(item.type)" label="Access Key ID">
@ -59,9 +60,6 @@
<el-dialog @close="search" v-model="backupVisiable" :title="$t('setting.backupAccount')" width="30%">
<el-form ref="formRef" label-position="left" :model="form" label-width="160px">
<el-form-item :label="$t('commons.table.name')" prop="name" :rules="Rules.name">
<el-input v-model="form.name" :disabled="operation === 'edit'" />
</el-form-item>
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
<el-select style="width: 100%" v-model="form.type" :disabled="operation === 'edit'">
<el-option
@ -74,7 +72,7 @@
</el-form-item>
<el-form-item
v-if="form.type === 'LOCAL'"
label="Dir"
label="Directory"
prop="varsJson['dir']"
:rules="Rules.requiredInput"
>
@ -94,7 +92,7 @@
</el-form-item>
<el-form-item
v-if="hasBucket(form.type)"
label="Secret Access Key"
label="Access Key Secret"
prop="credential"
:rules="Rules.requiredInput"
>
@ -182,19 +180,8 @@ const selects = ref<any>([]);
const backupVisiable = ref<boolean>(false);
const operation = ref<string>('create');
const paginationConfig = reactive({
currentPage: 1,
pageSize: 5,
total: 0,
});
const backSearch = reactive({
page: 1,
pageSize: 5,
});
const form = reactive({
id: 0,
name: '',
type: 'LOCAL',
bucket: '',
credential: '',
@ -203,31 +190,22 @@ const form = reactive({
});
type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>();
const typeOptions = ref([
{ label: i18n.global.t('setting.serverDisk'), value: 'LOCAL' },
{ label: 'OSS', value: 'OSS' },
{ label: 'S3', value: 'S3' },
{ label: 'SFTP', value: 'SFTP' },
{ label: 'MINIO', value: 'MINIO' },
]);
const typeOptions = ref();
const buckets = ref();
const search = async () => {
backSearch.page = paginationConfig.currentPage;
backSearch.pageSize = paginationConfig.pageSize;
const res = await getBackupList(backSearch);
data.value = res.data.items;
const res = await getBackupList();
data.value = res.data;
for (const bac of data.value) {
bac.varsJson = JSON.parse(bac.vars);
}
paginationConfig.total = res.data.total;
};
const onCreate = () => {
loadOption();
operation.value = 'create';
form.id = 0;
form.name = '';
form.type = 'LOCAL';
form.type = typeOptions.value[0].value;
form.bucket = '';
form.credential = '';
form.vars = '';
@ -250,9 +228,15 @@ const onBatchDelete = async (row: Backup.BackupInfo | null) => {
};
const onEdit = (row: Backup.BackupInfo) => {
typeOptions.value = [
{ label: i18n.global.t('setting.serverDisk'), value: 'LOCAL' },
{ label: i18n.global.t('setting.OSS'), value: 'OSS' },
{ label: i18n.global.t('setting.S3'), value: 'S3' },
{ label: 'SFTP', value: 'SFTP' },
{ label: 'MinIO', value: 'MINIO' },
];
restForm();
form.id = row.id;
form.name = row.name;
form.type = row.type;
form.bucket = row.bucket;
form.varsJson = JSON.parse(row.vars);
@ -296,6 +280,57 @@ const loadDir = async (path: string) => {
console.log(path);
form.varsJson['dir'] = path;
};
const loadOption = () => {
let options = [
{ label: i18n.global.t('setting.serverDisk'), value: 'LOCAL' },
{ label: i18n.global.t('setting.OSS'), value: 'OSS' },
{ label: i18n.global.t('setting.S3'), value: 'S3' },
{ label: 'SFTP', value: 'SFTP' },
{ label: 'MinIO', value: 'MINIO' },
];
for (const item of data.value) {
for (let i = 0; i < options.length; i++) {
if (item.type === options[i].value) {
options.splice(i, 1);
}
}
}
typeOptions.value = options;
};
const loadIconName = (type: string) => {
switch (type) {
case 'OSS':
return 'p-oss';
break;
case 'S3':
return 'p-s3';
break;
case 'SFTP':
return 'p-SFTP';
break;
case 'MINIO':
return 'p-minio';
break;
case 'LOCAL':
return 'p-file-folder';
break;
}
};
const loadBackupName = (type: string) => {
switch (type) {
case 'OSS':
return i18n.global.t('setting.OSS');
break;
case 'S3':
return i18n.global.t('setting.S3');
break;
case 'LOCAL':
return i18n.global.t('setting.serverDisk');
break;
default:
return type;
}
};
onMounted(() => {
search();

View File

@ -21,6 +21,7 @@
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('auth.password')" :rules="Rules.requiredInput" prop="settingInfo.password">
<el-input type="password" clearable disabled v-model="form.settingInfo.password">
<template #append>
@ -30,6 +31,7 @@
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('auth.email')" :rules="Rules.email" prop="settingInfo.email">
<el-input clearable v-model="form.settingInfo.email">
<template #append>
@ -45,6 +47,7 @@
<span class="input-help">{{ $t('setting.emailHelper') }}</span>
</div>
</el-form-item>
<el-form-item
:label="$t('setting.title')"
:rules="Rules.requiredInput"
@ -61,6 +64,7 @@
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('setting.theme')" :rules="Rules.requiredSelect" prop="settingInfo.theme">
<el-radio-group
@change="onSave(panelFormRef, 'Theme', form.settingInfo.theme)"
@ -76,12 +80,14 @@
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item
:label="$t('setting.language')"
:rules="Rules.requiredSelect"
prop="settingInfo.language"
>
<el-radio-group
style="width: 100%"
@change="onSave(panelFormRef, 'Language', form.settingInfo.language)"
v-model="form.settingInfo.language"
>
@ -94,6 +100,7 @@
</span>
</div>
</el-form-item>
<el-form-item
:label="$t('setting.sessionTimeout')"
:rules="Rules.number"
@ -115,6 +122,7 @@
</span>
</div>
</el-form-item>
<el-form-item :label="$t('setting.syncTime')">
<el-input disabled v-model="form.settingInfo.localTime">
<template #append>

View File

@ -1,7 +1,7 @@
<template>
<div>
<el-form :model="form" ref="panelFormRef" label-position="left" label-width="160px">
<el-card style="margin-top: 10px">
<el-card style="margin-top: 20px">
<template #header>
<div class="card-header">
<span>{{ $t('setting.safe') }}</span>
@ -86,6 +86,7 @@
:rules="Rules.requiredSelect"
>
<el-radio-group
style="width: 100%"
@change="
onSave(
panelFormRef,