1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 08:19:15 +08:00

fix: 计划任务数据库选项增加前缀 (#1820)

This commit is contained in:
ssongliu 2023-08-03 16:19:30 +08:00 committed by GitHub
parent df770460d6
commit a031d3ba41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 114 additions and 40 deletions

View File

@ -216,11 +216,11 @@ func (b *BaseApi) SearchMysql(c *gin.Context) {
// @Description 获取 mysql 数据库列表 // @Description 获取 mysql 数据库列表
// @Accept json // @Accept json
// @Param request body dto.PageInfo true "request" // @Param request body dto.PageInfo true "request"
// @Success 200 {array} string // @Success 200 {array} dto.MysqlOption
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /databases/options [get] // @Router /databases/options [get]
func (b *BaseApi) ListDBName(c *gin.Context) { func (b *BaseApi) ListDBName(c *gin.Context) {
list, err := mysqlService.ListDBName() list, err := mysqlService.ListDBOption()
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return

View File

@ -24,6 +24,12 @@ type MysqlDBInfo struct {
Description string `json:"description"` Description string `json:"description"`
} }
type MysqlOption struct {
ID uint `json:"id"`
Name string `json:"name"`
From string `json:"from"`
}
type MysqlDBCreate struct { type MysqlDBCreate struct {
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
From string `json:"from" validate:"required"` From string `json:"from" validate:"required"`

View File

@ -110,7 +110,7 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
if err != nil { if err != nil {
return err return err
} }
if err := handleMysqlBackup(db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil { if err := handleMysqlBackup(db.MysqlName, db.Name, tmpDir, fmt.Sprintf("%s.sql.gz", install.Name)); err != nil {
return err return err
} }
} }

View File

@ -26,7 +26,7 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
targetDir := path.Join(localDir, fmt.Sprintf("database/mysql/%s/%s", req.Name, req.DetailName)) targetDir := path.Join(localDir, fmt.Sprintf("database/mysql/%s/%s", req.Name, req.DetailName))
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow) fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow)
if err := handleMysqlBackup(req.DetailName, targetDir, fileName); err != nil { if err := handleMysqlBackup(req.Name, req.DetailName, targetDir, fileName); err != nil {
return err return err
} }
@ -97,8 +97,8 @@ func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
return nil return nil
} }
func handleMysqlBackup(dbName, targetDir, fileName string) error { func handleMysqlBackup(name, dbName, targetDir, fileName string) error {
dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(dbName)) dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(dbName), mysqlRepo.WithByMysqlName(name))
if err != nil { if err != nil {
return err return err
} }
@ -127,7 +127,7 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
if !fileOp.Stat(req.File) { if !fileOp.Stat(req.File) {
return errors.New(fmt.Sprintf("%s file is not exist", req.File)) return errors.New(fmt.Sprintf("%s file is not exist", req.File))
} }
dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(req.DetailName)) dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(req.DetailName), mysqlRepo.WithByMysqlName(req.Name))
if err != nil { if err != nil {
return err return err
} }

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"os" "os"
"path" "path"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -254,15 +255,18 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, backup model.Back
return paths, err return paths, err
} }
var dblist []string var dbs []model.DatabaseMysql
if cronjob.DBName == "all" { if cronjob.DBName == "all" {
mysqlService := NewIMysqlService() dbs, err = mysqlRepo.List()
dblist, err = mysqlService.ListDBName()
if err != nil { if err != nil {
return paths, err return paths, err
} }
} else { } else {
dblist = append(dblist, cronjob.DBName) itemID, _ := (strconv.Atoi(cronjob.DBName))
dbs, err = mysqlRepo.List(commonRepo.WithByID(uint(itemID)))
if err != nil {
return paths, err
}
} }
var client cloud_storage.CloudStorageClient var client cloud_storage.CloudStorageClient
@ -273,25 +277,21 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, backup model.Back
} }
} }
for _, dbName := range dblist { for _, dbInfo := range dbs {
var record model.BackupRecord var record model.BackupRecord
record.Type = "mysql" record.Type = "mysql"
record.Source = "LOCAL" record.Source = "LOCAL"
record.BackupType = backup.Type record.BackupType = backup.Type
dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(dbName))
if err != nil {
return paths, err
}
record.Name = dbInfo.MysqlName record.Name = dbInfo.MysqlName
backupDir := path.Join(localDir, fmt.Sprintf("database/mysql/%s/%s", record.Name, dbName)) backupDir := path.Join(localDir, fmt.Sprintf("database/mysql/%s/%s", record.Name, dbInfo.Name))
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbName, startTime.Format("20060102150405")) record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format("20060102150405"))
if err = handleMysqlBackup(dbName, backupDir, record.FileName); err != nil { if err = handleMysqlBackup(dbInfo.MysqlName, dbInfo.Name, backupDir, record.FileName); err != nil {
return paths, err return paths, err
} }
record.DetailName = dbName record.DetailName = dbInfo.Name
record.FileDir = backupDir record.FileDir = backupDir
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/") itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
if !cronjob.KeepLocal && backup.Type != "LOCAL" { if !cronjob.KeepLocal && backup.Type != "LOCAL" {

View File

@ -32,7 +32,7 @@ type MysqlService struct{}
type IMysqlService interface { type IMysqlService interface {
SearchWithPage(search dto.MysqlDBSearch) (int64, interface{}, error) SearchWithPage(search dto.MysqlDBSearch) (int64, interface{}, error)
ListDBName() ([]string, error) ListDBOption() ([]dto.MysqlOption, error)
Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error) Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error)
LoadFromRemote(from string) error LoadFromRemote(from string) error
ChangeAccess(info dto.ChangeDBInfo) error ChangeAccess(info dto.ChangeDBInfo) error
@ -71,13 +71,17 @@ func (u *MysqlService) SearchWithPage(search dto.MysqlDBSearch) (int64, interfac
return total, dtoMysqls, err return total, dtoMysqls, err
} }
func (u *MysqlService) ListDBName() ([]string, error) { func (u *MysqlService) ListDBOption() ([]dto.MysqlOption, error) {
mysqls, err := mysqlRepo.List() mysqls, err := mysqlRepo.List()
var dbNames []string var dbs []dto.MysqlOption
for _, mysql := range mysqls { for _, mysql := range mysqls {
dbNames = append(dbNames, mysql.Name) var item dto.MysqlOption
if err := copier.Copy(&item, &mysql); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
dbs = append(dbs, item)
} }
return dbNames, err return dbs, err
} }
func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error) { func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error) {
@ -85,6 +89,11 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
return nil, buserr.New(constant.ErrCmdIllegal) return nil, buserr.New(constant.ErrCmdIllegal)
} }
mysql, _ := mysqlRepo.Get(commonRepo.WithByName(req.Name), remoteDBRepo.WithByFrom(req.From))
if mysql.ID != 0 {
return nil, constant.ErrRecordExist
}
var createItem model.DatabaseMysql var createItem model.DatabaseMysql
if err := copier.Copy(&createItem, &req); err != nil { if err := copier.Copy(&createItem, &req); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())

View File

@ -60,6 +60,9 @@ func (r *Local) CreateUser(info CreateInfo) error {
for _, user := range userlist { for _, user := range userlist {
if err := r.ExecSQL(fmt.Sprintf("create user %s identified by '%s';", user, info.Password), info.Timeout); err != nil { if err := r.ExecSQL(fmt.Sprintf("create user %s identified by '%s';", user, info.Password), info.Timeout); err != nil {
if strings.Contains(err.Error(), "ERROR 1396") {
return buserr.New(constant.ErrUserIsExist)
}
_ = r.Delete(DeleteInfo{ _ = r.Delete(DeleteInfo{
Name: info.Name, Name: info.Name,
Version: info.Version, Version: info.Version,
@ -67,9 +70,6 @@ func (r *Local) CreateUser(info CreateInfo) error {
Permission: info.Permission, Permission: info.Permission,
ForceDelete: true, ForceDelete: true,
Timeout: 300}) Timeout: 300})
if strings.Contains(err.Error(), "ERROR 1396") {
return buserr.New(constant.ErrUserIsExist)
}
return err return err
} }
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to %s", info.Name, user) grantStr := fmt.Sprintf("grant all privileges on `%s`.* to %s", info.Name, user)

View File

@ -242,6 +242,10 @@ func (r *Remote) Recover(info RecoverInfo) error {
if err != nil { if err != nil {
return fmt.Errorf("gunzip file %s failed, stdout: %v, err: %v", info.SourceFile, string(stdout), err) return fmt.Errorf("gunzip file %s failed, stdout: %v, err: %v", info.SourceFile, string(stdout), err)
} }
defer func() {
gzipCmd := exec.Command("gzip", fileName)
_, _ = gzipCmd.CombinedOutput()
}()
} }
dns := fmt.Sprintf("%s:%s@tcp(%s:%v)/%s?charset=%s&parseTime=true&loc=Asia%sShanghai", r.User, r.Password, r.Address, r.Port, info.Name, info.Format, "%2F") dns := fmt.Sprintf("%s:%s@tcp(%s:%v)/%s?charset=%s&parseTime=true&loc=Asia%sShanghai", r.User, r.Password, r.Address, r.Port, info.Name, info.Format, "%2F")
f, err := os.Open(fileName) f, err := os.Open(fileName)

View File

@ -1,5 +1,5 @@
// Package docs GENERATED BY SWAG; DO NOT EDIT // Code generated by swaggo/swag. DO NOT EDIT.
// This file was generated by swaggo/swag
package docs package docs
import "github.com/swaggo/swag" import "github.com/swaggo/swag"
@ -4056,7 +4056,7 @@ const docTemplate = `{
"schema": { "schema": {
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "$ref": "#/definitions/dto.MysqlOption"
} }
} }
} }
@ -13147,6 +13147,20 @@ const docTemplate = `{
} }
} }
}, },
"dto.MysqlOption": {
"type": "object",
"properties": {
"from": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"dto.MysqlStatus": { "dto.MysqlStatus": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -4049,7 +4049,7 @@
"schema": { "schema": {
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "$ref": "#/definitions/dto.MysqlOption"
} }
} }
} }
@ -13140,6 +13140,20 @@
} }
} }
}, },
"dto.MysqlOption": {
"type": "object",
"properties": {
"from": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"dto.MysqlStatus": { "dto.MysqlStatus": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -1149,6 +1149,15 @@ definitions:
- page - page
- pageSize - pageSize
type: object type: object
dto.MysqlOption:
properties:
from:
type: string
id:
type: integer
name:
type: string
type: object
dto.MysqlStatus: dto.MysqlStatus:
properties: properties:
Aborted_clients: Aborted_clients:
@ -6414,7 +6423,7 @@ paths:
description: OK description: OK
schema: schema:
items: items:
type: string $ref: '#/definitions/dto.MysqlOption'
type: array type: array
security: security:
- ApiKeyAuth: [] - ApiKeyAuth: []

View File

@ -111,6 +111,11 @@ export namespace Database {
File: string; File: string;
Position: number; Position: number;
} }
export interface MysqlOption {
id: number;
name: string;
from: string;
}
export interface ChangeInfo { export interface ChangeInfo {
id: number; id: number;
value: string; value: string;

View File

@ -59,8 +59,8 @@ export const loadMysqlStatus = () => {
export const loadRemoteAccess = () => { export const loadRemoteAccess = () => {
return http.get<boolean>(`/databases/remote`); return http.get<boolean>(`/databases/remote`);
}; };
export const loadDBNames = () => { export const loadDBOptions = () => {
return http.get<Array<string>>(`/databases/options`); return http.get<Array<Database.MysqlOption>>(`/databases/options`);
}; };
// redis // redis

View File

@ -122,7 +122,18 @@
<el-form-item :label="$t('cronjob.database')" prop="dbName"> <el-form-item :label="$t('cronjob.database')" prop="dbName">
<el-select class="selectClass" clearable v-model="dialogData.rowData!.dbName"> <el-select class="selectClass" clearable v-model="dialogData.rowData!.dbName">
<el-option :label="$t('commons.table.all')" value="all" /> <el-option :label="$t('commons.table.all')" value="all" />
<el-option v-for="item in mysqlInfo.dbNames" :key="item" :label="item" :value="item" /> <div v-for="item in mysqlInfo.dbs" :key="item.id">
<el-option
v-if="item.from === 'local'"
:label="$t('database.localDB') + ' [' + item.name + ']'"
:value="item.id + ''"
/>
<el-option
v-else
:label="item.from + ' [' + item.name + ']'"
:value="item.id + ''"
/>
</div>
</el-select> </el-select>
</el-form-item> </el-form-item>
</div> </div>
@ -219,12 +230,13 @@ import i18n from '@/lang';
import { ElForm } from 'element-plus'; import { ElForm } from 'element-plus';
import { Cronjob } from '@/api/interface/cronjob'; import { Cronjob } from '@/api/interface/cronjob';
import { addCronjob, editCronjob } from '@/api/modules/cronjob'; import { addCronjob, editCronjob } from '@/api/modules/cronjob';
import { loadDBNames } from '@/api/modules/database'; import { loadDBOptions } from '@/api/modules/database';
import { GetWebsiteOptions } from '@/api/modules/website'; import { GetWebsiteOptions } from '@/api/modules/website';
import DrawerHeader from '@/components/drawer-header/index.vue'; import DrawerHeader from '@/components/drawer-header/index.vue';
import { MsgError, MsgSuccess } from '@/utils/message'; import { MsgError, MsgSuccess } from '@/utils/message';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { listContainer } from '@/api/modules/container'; import { listContainer } from '@/api/modules/container';
import { Database } from '@/api/interface/database';
const router = useRouter(); const router = useRouter();
interface DialogProps { interface DialogProps {
@ -275,7 +287,7 @@ const mysqlInfo = reactive({
isExist: false, isExist: false,
name: '', name: '',
version: '', version: '',
dbNames: [] as Array<string>, dbs: [] as Array<Database.MysqlOption>,
}); });
const varifySpec = (rule: any, value: any, callback: any) => { const varifySpec = (rule: any, value: any, callback: any) => {
@ -458,8 +470,8 @@ const loadContainers = async () => {
}; };
const checkMysqlInstalled = async () => { const checkMysqlInstalled = async () => {
const data = await loadDBNames(); const data = await loadDBOptions();
mysqlInfo.dbNames = data.data || []; mysqlInfo.dbs = data.data || [];
}; };
function isBackup() { function isBackup() {

View File

@ -190,6 +190,7 @@ const onSave = async (formEl: FormInstance | undefined) => {
const onSubmitAccess = async () => { const onSubmitAccess = async () => {
let param = { let param = {
id: 0, id: 0,
from: form.from,
value: form.privilege ? '%' : 'localhost', value: form.privilege ? '%' : 'localhost',
}; };
loading.value = true; loading.value = true;