mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 14:57:16 +08:00
fix: remove database app manage page
This commit is contained in:
@@ -40,7 +40,7 @@ func (r *cronRepo) Count() (int64, error) {
|
||||
func (r *cronRepo) List(page, limit uint) ([]*biz.Cron, int64, error) {
|
||||
var cron []*biz.Cron
|
||||
var total int64
|
||||
err := app.Orm.Model(&biz.Cert{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&cron).Error
|
||||
err := app.Orm.Model(&biz.Cron{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&cron).Error
|
||||
return cron, total, err
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/cast"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
|
||||
@@ -22,43 +22,5 @@ export default {
|
||||
rootPassword: (): Promise<AxiosResponse<any>> => request.get('/apps/mysql/rootPassword'),
|
||||
// 修改 root 密码
|
||||
setRootPassword: (password: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/apps/mysql/rootPassword', { password }),
|
||||
// 数据库列表
|
||||
databases: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get('/apps/mysql/databases', { params: { page, limit } }),
|
||||
// 创建数据库
|
||||
addDatabase: (database: any): Promise<AxiosResponse<any>> =>
|
||||
request.post('/apps/mysql/databases', database),
|
||||
// 删除数据库
|
||||
deleteDatabase: (database: string): Promise<AxiosResponse<any>> =>
|
||||
request.delete('/apps/mysql/databases', { params: { database } }),
|
||||
// 备份列表
|
||||
backups: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get('/apps/mysql/backups', { params: { page, limit } }),
|
||||
// 创建备份
|
||||
createBackup: (database: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/apps/mysql/backups', { database }),
|
||||
// 上传备份
|
||||
uploadBackup: (backup: any): Promise<AxiosResponse<any>> =>
|
||||
request.put('/apps/mysql/backups', backup),
|
||||
// 删除备份
|
||||
deleteBackup: (name: string): Promise<AxiosResponse<any>> =>
|
||||
request.delete('/apps/mysql/backups', { params: { name } }),
|
||||
// 还原备份
|
||||
restoreBackup: (backup: string, database: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/apps/mysql/backups/restore', { backup, database }),
|
||||
// 用户列表
|
||||
users: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get('/apps/mysql/users', { params: { page, limit } }),
|
||||
// 创建用户
|
||||
addUser: (user: any): Promise<AxiosResponse<any>> => request.post('/apps/mysql/users', user),
|
||||
// 删除用户
|
||||
deleteUser: (user: string): Promise<AxiosResponse<any>> =>
|
||||
request.delete('/apps/mysql/users', { params: { user } }),
|
||||
// 设置用户密码
|
||||
setUserPassword: (user: string, password: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/apps/mysql/users/password', { user, password }),
|
||||
// 设置用户权限
|
||||
setUserPrivileges: (user: string, database: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/apps/mysql/users/privileges', { user, database })
|
||||
request.post('/apps/mysql/rootPassword', { password })
|
||||
}
|
||||
|
||||
@@ -18,40 +18,5 @@ export default {
|
||||
// 获取日志
|
||||
log: (): Promise<AxiosResponse<any>> => request.get('/apps/postgresql/log'),
|
||||
// 清空错误日志
|
||||
clearLog: (): Promise<AxiosResponse<any>> => request.post('/apps/postgresql/clearLog'),
|
||||
// 数据库列表
|
||||
databases: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get('/apps/postgresql/databases', { params: { page, limit } }),
|
||||
// 创建数据库
|
||||
addDatabase: (database: any): Promise<AxiosResponse<any>> =>
|
||||
request.post('/apps/postgresql/databases', database),
|
||||
// 删除数据库
|
||||
deleteDatabase: (database: string): Promise<AxiosResponse<any>> =>
|
||||
request.delete('/apps/postgresql/databases', { params: { database } }),
|
||||
// 备份列表
|
||||
backups: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get('/apps/postgresql/backups', { params: { page, limit } }),
|
||||
// 创建备份
|
||||
createBackup: (database: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/apps/postgresql/backups', { database }),
|
||||
// 上传备份
|
||||
uploadBackup: (backup: any): Promise<AxiosResponse<any>> =>
|
||||
request.put('/apps/postgresql/backups', backup),
|
||||
// 删除备份
|
||||
deleteBackup: (name: string): Promise<AxiosResponse<any>> =>
|
||||
request.delete('/apps/postgresql/backups', { params: { name } }),
|
||||
// 还原备份
|
||||
restoreBackup: (backup: string, database: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/apps/postgresql/backups/restore', { backup, database }),
|
||||
// 角色列表
|
||||
roles: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get('/apps/postgresql/roles', { params: { page, limit } }),
|
||||
// 创建角色
|
||||
addRole: (user: any): Promise<AxiosResponse<any>> => request.post('/apps/postgresql/roles', user),
|
||||
// 删除角色
|
||||
deleteRole: (user: string): Promise<AxiosResponse<any>> =>
|
||||
request.delete('/apps/postgresql/roles', { params: { user } }),
|
||||
// 设置角色密码
|
||||
setRolePassword: (user: string, password: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/apps/postgresql/roles/password', { user, password })
|
||||
clearLog: (): Promise<AxiosResponse<any>> => request.post('/apps/postgresql/clearLog')
|
||||
}
|
||||
|
||||
@@ -5,19 +5,19 @@ import { request } from '@/utils'
|
||||
export default {
|
||||
// 获取任务列表
|
||||
list: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get('/cron/list', { params: { page, limit } }),
|
||||
request.get('/cron', { params: { page, limit } }),
|
||||
// 获取任务脚本
|
||||
script: (id: number): Promise<AxiosResponse<any>> => request.get('/cron/' + id),
|
||||
// 添加任务
|
||||
add: (task: any): Promise<AxiosResponse<any>> => request.post('/cron/add', task),
|
||||
get: (id: number): Promise<AxiosResponse<any>> => request.get('/cron/' + id),
|
||||
// 创建任务
|
||||
create: (task: any): Promise<AxiosResponse<any>> => request.post('/cron', task),
|
||||
// 修改任务
|
||||
update: (id: number, name: string, time: string, script: string): Promise<AxiosResponse<any>> =>
|
||||
request.put('/cron/' + id, { name, time, script }),
|
||||
// 删除任务
|
||||
delete: (id: number): Promise<AxiosResponse<any>> => request.delete('/cron/' + id),
|
||||
// 获取任务日志
|
||||
log: (id: number): Promise<AxiosResponse<any>> => request.get('/cron/log/' + id),
|
||||
log: (id: number): Promise<AxiosResponse<any>> => request.get('/cron/' + id + '/log'),
|
||||
// 修改任务状态
|
||||
status: (id: number, status: boolean): Promise<AxiosResponse<any>> =>
|
||||
request.post('/cron/status', { id, status })
|
||||
request.post('/cron/' + id + '/status', { status })
|
||||
}
|
||||
|
||||
@@ -348,8 +348,8 @@
|
||||
"actions": "操作"
|
||||
},
|
||||
"create": {
|
||||
"trigger": "新建网站",
|
||||
"title": "新建网站",
|
||||
"trigger": "创建网站",
|
||||
"title": "创建网站",
|
||||
"fields": {
|
||||
"name": {
|
||||
"label": "网站名",
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import Editor from '@guolao/vue-monaco-editor'
|
||||
import type { MessageReactive, UploadFileInfo } from 'naive-ui'
|
||||
import { NButton, NDataTable, NInput, NPopconfirm } from 'naive-ui'
|
||||
|
||||
import mysql from '@/api/apps/mysql'
|
||||
import systemctl from '@/api/panel/systemctl'
|
||||
import { generateRandomString, renderIcon } from '@/utils'
|
||||
import type { Backup, Database, User } from '@/views/apps/mysql/types'
|
||||
|
||||
let messageReactive: MessageReactive | null = null
|
||||
|
||||
const currentTab = ref('status')
|
||||
const currentDatabase = ref('')
|
||||
const status = ref(false)
|
||||
const isEnabled = ref(false)
|
||||
const config = ref('')
|
||||
@@ -26,339 +20,18 @@ const statusStr = computed(() => {
|
||||
return status.value ? '正常运行中' : '已停止运行'
|
||||
})
|
||||
|
||||
const addDatabaseModel = ref({
|
||||
database: '',
|
||||
user: '',
|
||||
password: generateRandomString(16)
|
||||
})
|
||||
const addUserModel = ref({
|
||||
database: '',
|
||||
user: '',
|
||||
password: generateRandomString(16)
|
||||
})
|
||||
const changePasswordModel = ref({
|
||||
user: '',
|
||||
password: generateRandomString(16)
|
||||
})
|
||||
const changePrivilegesModel = ref({
|
||||
user: '',
|
||||
database: ''
|
||||
})
|
||||
|
||||
const addDatabaseModal = ref(false)
|
||||
const addUserModal = ref(false)
|
||||
const changePasswordModal = ref(false)
|
||||
const changePrivilegesModal = ref(false)
|
||||
const backupModal = ref(false)
|
||||
|
||||
const databaseColumns: any = [
|
||||
{ title: '库名', key: 'name', fixed: 'left', resizable: true, ellipsis: { tooltip: true } },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 240,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
hideInExcel: true,
|
||||
render(row: any) {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'warning',
|
||||
secondary: true,
|
||||
onClick: () => {
|
||||
currentDatabase.value = row.name
|
||||
backupModal.value = true
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => '备份',
|
||||
icon: renderIcon('material-symbols:save-outline', { size: 14 })
|
||||
}
|
||||
),
|
||||
h(
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: () => handleDeleteDatabase(row.name)
|
||||
},
|
||||
{
|
||||
default: () => {
|
||||
return '确定删除数据库吗?'
|
||||
},
|
||||
trigger: () => {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => '删除',
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const userColumns: any = [
|
||||
{ title: '用户名', key: 'user', fixed: 'left', resizable: true, ellipsis: { tooltip: true } },
|
||||
{ title: '主机', key: 'host', resizable: true, ellipsis: { tooltip: true } },
|
||||
{ title: '权限', key: 'grants', width: 350, resizable: true, ellipsis: { tooltip: true } },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 300,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
hideInExcel: true,
|
||||
render(row: any) {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'warning',
|
||||
secondary: true,
|
||||
onClick: () => showChangePasswordModal(row.user)
|
||||
},
|
||||
{
|
||||
default: () => '改密',
|
||||
icon: renderIcon('majesticons:key-line', { size: 14 })
|
||||
}
|
||||
),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'warning',
|
||||
secondary: true,
|
||||
style: 'margin-left: 15px;',
|
||||
onClick: () => showChangePrivilegesModal(row.user)
|
||||
},
|
||||
{
|
||||
default: () => '权限',
|
||||
icon: renderIcon('majesticons:lock-line', { size: 14 })
|
||||
}
|
||||
),
|
||||
h(
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: () => handleDeleteUser(row.user)
|
||||
},
|
||||
{
|
||||
default: () => {
|
||||
return '确定删除用户吗?'
|
||||
},
|
||||
trigger: () => {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => '删除',
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const loadColumns: any = [
|
||||
{ title: '属性', key: 'name', fixed: 'left', resizable: true, ellipsis: { tooltip: true } },
|
||||
{ title: '当前值', key: 'value', width: 200, ellipsis: { tooltip: true } }
|
||||
]
|
||||
|
||||
const backupColumns: any = [
|
||||
{ title: '文件名', key: 'name', fixed: 'left', resizable: true, ellipsis: { tooltip: true } },
|
||||
{ title: '大小', key: 'size', width: 200, ellipsis: { tooltip: true } },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
hideInExcel: true,
|
||||
render(row: any) {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'warning',
|
||||
secondary: true,
|
||||
onClick: () => handleRestoreBackup(row)
|
||||
},
|
||||
{
|
||||
default: () => '恢复',
|
||||
icon: renderIcon('material-symbols:settings-backup-restore-rounded', { size: 14 })
|
||||
}
|
||||
),
|
||||
h(
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: () => handleDeleteBackup(row.name)
|
||||
},
|
||||
{
|
||||
default: () => {
|
||||
return '确定删除备份吗?'
|
||||
},
|
||||
trigger: () => {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => '删除',
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const databases = ref<Database[]>([] as Database[])
|
||||
const users = ref<User[]>([] as User[])
|
||||
const backup = ref<Backup[]>([])
|
||||
const load = ref<any[]>([])
|
||||
|
||||
const databasePagination = reactive({
|
||||
page: 1,
|
||||
pageCount: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
showQuickJumper: true,
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 20, 50, 100]
|
||||
})
|
||||
|
||||
const userPagination = reactive({
|
||||
page: 1,
|
||||
pageCount: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
showQuickJumper: true,
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 20, 50, 100]
|
||||
})
|
||||
|
||||
const backupPagination = reactive({
|
||||
page: 1,
|
||||
pageCount: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
showQuickJumper: true,
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 20, 50, 100]
|
||||
})
|
||||
|
||||
const getLoad = async () => {
|
||||
const { data } = await mysql.load()
|
||||
return data
|
||||
}
|
||||
|
||||
const getDatabaseList = async (page: number, limit: number) => {
|
||||
const { data } = await mysql.databases(page, limit)
|
||||
return data
|
||||
}
|
||||
|
||||
const getUserList = async (page: number, limit: number) => {
|
||||
const { data } = await mysql.users(page, limit)
|
||||
return data
|
||||
}
|
||||
|
||||
const getBackupList = async (page: number, limit: number) => {
|
||||
const { data } = await mysql.backups(page, limit)
|
||||
return data
|
||||
}
|
||||
|
||||
const onDatabasePageChange = (page: number) => {
|
||||
databasePagination.page = page
|
||||
getDatabaseList(page, databasePagination.pageSize).then((res) => {
|
||||
databases.value = res.items
|
||||
databasePagination.itemCount = res.total
|
||||
databasePagination.pageCount = res.total / databasePagination.pageSize + 1
|
||||
})
|
||||
}
|
||||
|
||||
const onUserPageChange = (page: number) => {
|
||||
userPagination.page = page
|
||||
getUserList(page, userPagination.pageSize).then((res) => {
|
||||
users.value = res.items
|
||||
userPagination.itemCount = res.total
|
||||
userPagination.pageCount = res.total / userPagination.pageSize + 1
|
||||
})
|
||||
}
|
||||
|
||||
const onBackupPageChange = (page: number) => {
|
||||
backupPagination.page = page
|
||||
getBackupList(page, backupPagination.pageSize).then((res) => {
|
||||
backup.value = res.items
|
||||
backupPagination.itemCount = res.total
|
||||
backupPagination.pageCount = res.total / backupPagination.pageSize + 1
|
||||
})
|
||||
}
|
||||
|
||||
const onDatabasePageSizeChange = (pageSize: number) => {
|
||||
databasePagination.pageSize = pageSize
|
||||
onDatabasePageChange(1)
|
||||
}
|
||||
|
||||
const onUserPageSizeChange = (pageSize: number) => {
|
||||
userPagination.pageSize = pageSize
|
||||
onUserPageChange(1)
|
||||
}
|
||||
|
||||
const onBackupPageSizeChange = (pageSize: number) => {
|
||||
backupPagination.pageSize = pageSize
|
||||
onBackupPageChange(1)
|
||||
}
|
||||
|
||||
const handleDeleteDatabase = async (name: string) => {
|
||||
mysql.deleteDatabase(name).then(() => {
|
||||
window.$message.success('删除成功')
|
||||
onDatabasePageChange(databasePagination.page)
|
||||
})
|
||||
}
|
||||
|
||||
const handleDeleteUser = async (user: string) => {
|
||||
mysql.deleteUser(user).then(() => {
|
||||
window.$message.success('删除成功')
|
||||
onUserPageChange(userPagination.page)
|
||||
})
|
||||
}
|
||||
|
||||
const showChangePasswordModal = (user: string) => {
|
||||
changePasswordModel.value.user = user
|
||||
changePasswordModal.value = true
|
||||
}
|
||||
|
||||
const showChangePrivilegesModal = (user: string) => {
|
||||
changePrivilegesModel.value.user = user
|
||||
changePrivilegesModal.value = true
|
||||
}
|
||||
|
||||
const getIsEnabled = async () => {
|
||||
await systemctl.isEnabled('mysqld').then((res: any) => {
|
||||
isEnabled.value = res.data
|
||||
@@ -453,113 +126,10 @@ const handleSetRootPassword = async () => {
|
||||
window.$message.success('修改成功')
|
||||
}
|
||||
|
||||
const handleAddDatabase = async () => {
|
||||
mysql.addDatabase(addDatabaseModel.value).then(() => {
|
||||
window.$message.success('添加成功')
|
||||
addDatabaseModal.value = false
|
||||
addDatabaseModel.value = {
|
||||
database: '',
|
||||
user: '',
|
||||
password: generateRandomString(16)
|
||||
}
|
||||
onDatabasePageChange(databasePagination.page)
|
||||
onUserPageChange(userPagination.page)
|
||||
})
|
||||
}
|
||||
|
||||
const handleAddUser = async () => {
|
||||
mysql.addUser(addUserModel.value).then(() => {
|
||||
window.$message.success('添加成功')
|
||||
addUserModal.value = false
|
||||
addDatabaseModel.value = {
|
||||
user: '',
|
||||
password: generateRandomString(16),
|
||||
database: ''
|
||||
}
|
||||
onUserPageChange(userPagination.page)
|
||||
})
|
||||
}
|
||||
|
||||
const handleChangePassword = async () => {
|
||||
mysql
|
||||
.setUserPassword(changePasswordModel.value.user, changePasswordModel.value.password)
|
||||
.then(() => {
|
||||
window.$message.success('修改成功')
|
||||
changePasswordModal.value = false
|
||||
changePasswordModel.value = {
|
||||
user: '',
|
||||
password: generateRandomString(16)
|
||||
}
|
||||
onUserPageChange(userPagination.page)
|
||||
})
|
||||
}
|
||||
|
||||
const handleChangePrivileges = async () => {
|
||||
mysql
|
||||
.setUserPrivileges(changePrivilegesModel.value.user, changePrivilegesModel.value.database)
|
||||
.then(() => {
|
||||
window.$message.success('修改成功')
|
||||
changePrivilegesModal.value = false
|
||||
changePrivilegesModel.value = {
|
||||
user: '',
|
||||
database: ''
|
||||
}
|
||||
onUserPageChange(userPagination.page)
|
||||
})
|
||||
}
|
||||
|
||||
const handleUploadBackup = async (files: UploadFileInfo[]) => {
|
||||
messageReactive = window.$message.loading('上传中...', {
|
||||
duration: 0
|
||||
})
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i]
|
||||
const formData = new FormData()
|
||||
formData.append('file', file.file as Blob, file.name)
|
||||
await mysql.uploadBackup(formData).then(() => {
|
||||
messageReactive?.destroy()
|
||||
window.$message.success('上传成功')
|
||||
onBackupPageChange(backupPagination.page)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreateBackup = async () => {
|
||||
messageReactive = window.$message.loading('创建中...', {
|
||||
duration: 0
|
||||
})
|
||||
await mysql.createBackup(currentDatabase.value).then(() => {
|
||||
messageReactive?.destroy()
|
||||
window.$message.success('创建成功')
|
||||
onBackupPageChange(backupPagination.page)
|
||||
})
|
||||
}
|
||||
|
||||
const handleRestoreBackup = async (row: any) => {
|
||||
messageReactive = window.$message.loading('恢复中...', {
|
||||
duration: 0
|
||||
})
|
||||
await mysql.restoreBackup(row.name, currentDatabase.value).then(() => {
|
||||
messageReactive?.destroy()
|
||||
window.$message.success('恢复成功')
|
||||
onBackupPageChange(backupPagination.page)
|
||||
})
|
||||
}
|
||||
|
||||
const handleDeleteBackup = async (name: string) => {
|
||||
await mysql.deleteBackup(name).then(() => {
|
||||
window.$message.success('删除成功')
|
||||
onBackupPageChange(backupPagination.page)
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getStatus()
|
||||
getIsEnabled()
|
||||
getRootPassword()
|
||||
onDatabasePageChange(databasePagination.page)
|
||||
onUserPageChange(userPagination.page)
|
||||
onBackupPageChange(backupPagination.page)
|
||||
getLoad().then((res) => {
|
||||
load.value = res
|
||||
})
|
||||
@@ -578,16 +148,6 @@ onMounted(() => {
|
||||
<template>
|
||||
<common-page show-footer>
|
||||
<template #action>
|
||||
<n-space v-if="currentTab == 'manage'">
|
||||
<n-button class="ml-16" type="info" @click="addUserModal = true">
|
||||
<TheIcon :size="18" class="mr-5" icon="material-symbols:add" />
|
||||
新建用户
|
||||
</n-button>
|
||||
<n-button class="ml-16" type="primary" @click="addDatabaseModal = true">
|
||||
<TheIcon :size="18" class="mr-5" icon="material-symbols:add" />
|
||||
新建数据库
|
||||
</n-button>
|
||||
</n-space>
|
||||
<n-button
|
||||
v-if="currentTab == 'config'"
|
||||
class="ml-16"
|
||||
@@ -671,34 +231,6 @@ onMounted(() => {
|
||||
</n-card>
|
||||
</n-space>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="manage" tab="管理">
|
||||
<n-space vertical>
|
||||
<n-card title="数据库" :segmented="true" rounded-10>
|
||||
<n-data-table
|
||||
striped
|
||||
remote
|
||||
:loading="false"
|
||||
:columns="databaseColumns"
|
||||
:data="databases"
|
||||
:row-key="(row: any) => row.name"
|
||||
@update:page="onDatabasePageChange"
|
||||
@update:page-size="onDatabasePageSizeChange"
|
||||
/>
|
||||
</n-card>
|
||||
<n-card title="用户" :segmented="true" rounded-10>
|
||||
<n-data-table
|
||||
striped
|
||||
remote
|
||||
:loading="false"
|
||||
:columns="userColumns"
|
||||
:data="users"
|
||||
:row-key="(row: any) => row.user"
|
||||
@update:page="onUserPageChange"
|
||||
@update:page-size="onUserPageSizeChange"
|
||||
/>
|
||||
</n-card>
|
||||
</n-space>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="config" tab="修改配置">
|
||||
<n-space vertical>
|
||||
<n-alert type="warning">
|
||||
@@ -753,159 +285,4 @@ onMounted(() => {
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</common-page>
|
||||
<n-modal v-model:show="addDatabaseModal" title="新建数据库">
|
||||
<n-card
|
||||
closable
|
||||
@close="() => (addDatabaseModal = false)"
|
||||
title="新建数据库"
|
||||
style="width: 60vw"
|
||||
>
|
||||
<n-form :model="addDatabaseModel">
|
||||
<n-form-item path="database" label="数据库名">
|
||||
<n-input
|
||||
v-model:value="addDatabaseModel.database"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="输入数据库名"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="user" label="用户名">
|
||||
<n-input
|
||||
v-model:value="addDatabaseModel.user"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="输入用户名"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="password" label="密码">
|
||||
<n-input
|
||||
v-model:value="addDatabaseModel.password"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="建议使用生成器生成随机密码"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-row :gutter="[0, 24]">
|
||||
<n-col :span="24">
|
||||
<n-button type="info" block @click="handleAddDatabase">提交</n-button>
|
||||
</n-col>
|
||||
</n-row>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<n-modal v-model:show="addUserModal" title="新建用户">
|
||||
<n-card closable @close="() => (addUserModal = false)" title="新建用户" style="width: 60vw">
|
||||
<n-form :model="addUserModel">
|
||||
<n-form-item path="user" label="用户名">
|
||||
<n-input
|
||||
v-model:value="addUserModel.user"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="输入用户名"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="password" label="密码">
|
||||
<n-input
|
||||
v-model:value="addUserModel.password"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="建议使用生成器生成随机密码"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="database" label="数据库名">
|
||||
<n-input
|
||||
v-model:value="addUserModel.database"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="输入授权给该用户的数据库名"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-row :gutter="[0, 24]">
|
||||
<n-col :span="24">
|
||||
<n-button type="info" block @click="handleAddUser">提交</n-button>
|
||||
</n-col>
|
||||
</n-row>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<n-modal v-model:show="backupModal">
|
||||
<n-card
|
||||
closable
|
||||
@close="() => (backupModal = false)"
|
||||
:title="'备份管理 - ' + currentDatabase"
|
||||
style="width: 60vw"
|
||||
>
|
||||
<n-space vertical>
|
||||
<n-space>
|
||||
<n-button type="primary" @click="handleCreateBackup">创建备份</n-button>
|
||||
<n-upload
|
||||
accept=".sql,.zip,tar.gz,.tar,.rar,.bz2"
|
||||
:default-upload="false"
|
||||
:show-file-list="false"
|
||||
@update:file-list="handleUploadBackup"
|
||||
>
|
||||
<n-button>上传备份</n-button>
|
||||
</n-upload>
|
||||
</n-space>
|
||||
<n-data-table
|
||||
striped
|
||||
remote
|
||||
:loading="false"
|
||||
:columns="backupColumns"
|
||||
:data="backup"
|
||||
:row-key="(row: any) => row.name"
|
||||
@update:page="onBackupPageChange"
|
||||
@update:page-size="onBackupPageSizeChange"
|
||||
/>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<n-modal v-model:show="changePasswordModal">
|
||||
<n-card
|
||||
closable
|
||||
@close="() => (changePasswordModal = false)"
|
||||
title="修改密码"
|
||||
style="width: 60vw"
|
||||
>
|
||||
<n-form :model="changePasswordModel">
|
||||
<n-form-item path="password" label="密码">
|
||||
<n-input
|
||||
v-model:value="changePasswordModel.password"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="建议使用生成器生成随机密码"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-row :gutter="[0, 24]">
|
||||
<n-col :span="24">
|
||||
<n-button type="info" block @click="handleChangePassword">提交</n-button>
|
||||
</n-col>
|
||||
</n-row>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<n-modal v-model:show="changePrivilegesModal">
|
||||
<n-card
|
||||
closable
|
||||
@close="() => (changePrivilegesModal = false)"
|
||||
title="修改权限"
|
||||
style="width: 60vw"
|
||||
>
|
||||
<n-form :model="changePrivilegesModel">
|
||||
<n-form-item path="database" label="数据库名">
|
||||
<n-input
|
||||
v-model:value="changePrivilegesModel.database"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="输入授权给该用户的数据库名"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-row :gutter="[0, 24]">
|
||||
<n-col :span="24">
|
||||
<n-button type="info" block @click="handleChangePrivileges">提交</n-button>
|
||||
</n-col>
|
||||
</n-row>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
@@ -3,17 +3,17 @@ import type { RouteType } from '~/types/router'
|
||||
const Layout = () => import('@/layout/IndexView.vue')
|
||||
|
||||
export default {
|
||||
name: 'mysql57',
|
||||
path: '/apps/mysql57',
|
||||
name: 'mysql',
|
||||
path: '/apps/mysql',
|
||||
component: Layout,
|
||||
isHidden: true,
|
||||
children: [
|
||||
{
|
||||
name: 'apps-mysql57-index',
|
||||
name: 'apps-mysql-index',
|
||||
path: '',
|
||||
component: () => import('../mysql/IndexView.vue'),
|
||||
component: () => import('./IndexView.vue'),
|
||||
meta: {
|
||||
title: 'MySQL 5.7',
|
||||
title: 'Percona(MySQL)',
|
||||
icon: 'mdi:database',
|
||||
role: ['admin'],
|
||||
requireAuth: true
|
||||
@@ -1,14 +0,0 @@
|
||||
export interface Database {
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface User {
|
||||
user: string
|
||||
host: string
|
||||
grants: string
|
||||
}
|
||||
|
||||
export interface Backup {
|
||||
name: string
|
||||
size: string
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import type { RouteType } from '~/types/router'
|
||||
|
||||
const Layout = () => import('@/layout/IndexView.vue')
|
||||
|
||||
export default {
|
||||
name: 'mysql84',
|
||||
path: '/apps/mysql84',
|
||||
component: Layout,
|
||||
isHidden: true,
|
||||
children: [
|
||||
{
|
||||
name: 'apps-mysql84-index',
|
||||
path: '',
|
||||
component: () => import('../mysql/IndexView.vue'),
|
||||
meta: {
|
||||
title: 'MySQL 8.4',
|
||||
icon: 'mdi:database',
|
||||
role: ['admin'],
|
||||
requireAuth: true
|
||||
}
|
||||
}
|
||||
]
|
||||
} as RouteType
|
||||
@@ -1,17 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import Editor from '@guolao/vue-monaco-editor'
|
||||
import type { MessageReactive, UploadFileInfo } from 'naive-ui'
|
||||
import { NButton, NDataTable, NFlex, NInput, NPopconfirm, NTag } from 'naive-ui'
|
||||
import { NButton, NDataTable, NPopconfirm } from 'naive-ui'
|
||||
|
||||
import postgresql from '@/api/apps/postgresql'
|
||||
import systemctl from '@/api/panel/systemctl'
|
||||
import { generateRandomString, renderIcon } from '@/utils'
|
||||
import type { Backup, Database, Role } from '@/views/apps/postgresql/types'
|
||||
|
||||
let messageReactive: MessageReactive | null = null
|
||||
|
||||
const currentTab = ref('status')
|
||||
const currentDatabase = ref('')
|
||||
const status = ref(false)
|
||||
const isEnabled = ref(false)
|
||||
const config = ref('')
|
||||
@@ -25,332 +19,18 @@ const statusStr = computed(() => {
|
||||
return status.value ? '正常运行中' : '已停止运行'
|
||||
})
|
||||
|
||||
const addDatabaseModel = ref({
|
||||
database: '',
|
||||
user: '',
|
||||
password: generateRandomString(16)
|
||||
})
|
||||
const addRoleModel = ref({
|
||||
database: '',
|
||||
user: '',
|
||||
password: generateRandomString(16)
|
||||
})
|
||||
const changePasswordModel = ref({
|
||||
user: '',
|
||||
password: generateRandomString(16)
|
||||
})
|
||||
|
||||
const addDatabaseModal = ref(false)
|
||||
const addRoleModal = ref(false)
|
||||
const changePasswordModal = ref(false)
|
||||
const backupModal = ref(false)
|
||||
|
||||
const databaseColumns: any = [
|
||||
{ title: '库名', key: 'name', fixed: 'left', resizable: true, ellipsis: { tooltip: true } },
|
||||
{ title: '拥有者', key: 'owner', resizable: true, ellipsis: { tooltip: true } },
|
||||
{ title: '编码', key: 'encoding', resizable: true, ellipsis: { tooltip: true } },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 240,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
hideInExcel: true,
|
||||
render(row: any) {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'warning',
|
||||
secondary: true,
|
||||
onClick: () => {
|
||||
currentDatabase.value = row.name
|
||||
backupModal.value = true
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => '备份',
|
||||
icon: renderIcon('material-symbols:save-outline', { size: 14 })
|
||||
}
|
||||
),
|
||||
h(
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: () => handleDeleteDatabase(row.name)
|
||||
},
|
||||
{
|
||||
default: () => {
|
||||
return '确定删除数据库吗?'
|
||||
},
|
||||
trigger: () => {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => '删除',
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const roleColumns: any = [
|
||||
{ title: '角色名', key: 'role', fixed: 'left', resizable: true, ellipsis: { tooltip: true } },
|
||||
{
|
||||
title: '权限',
|
||||
key: 'attributes',
|
||||
resizable: true,
|
||||
ellipsis: { tooltip: true },
|
||||
render(row: any) {
|
||||
return h(NFlex, null, {
|
||||
default: () =>
|
||||
row.attributes.map((perm: any) =>
|
||||
h(NTag, null, {
|
||||
default: () => perm
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 300,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
hideInExcel: true,
|
||||
render(row: any) {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'warning',
|
||||
secondary: true,
|
||||
onClick: () => showChangePasswordModal(row.user)
|
||||
},
|
||||
{
|
||||
default: () => '改密',
|
||||
icon: renderIcon('majesticons:key-line', { size: 14 })
|
||||
}
|
||||
),
|
||||
h(
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: () => handleDeleteRole(row.user)
|
||||
},
|
||||
{
|
||||
default: () => {
|
||||
return '确定删除角色吗?'
|
||||
},
|
||||
trigger: () => {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => '删除',
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const loadColumns: any = [
|
||||
{ title: '属性', key: 'name', fixed: 'left', resizable: true, ellipsis: { tooltip: true } },
|
||||
{ title: '当前值', key: 'value', width: 200, ellipsis: { tooltip: true } }
|
||||
]
|
||||
|
||||
const backupColumns: any = [
|
||||
{ title: '文件名', key: 'name', fixed: 'left', resizable: true, ellipsis: { tooltip: true } },
|
||||
{ title: '大小', key: 'size', width: 200, ellipsis: { tooltip: true } },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
hideInExcel: true,
|
||||
render(row: any) {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'warning',
|
||||
secondary: true,
|
||||
onClick: () => handleRestoreBackup(row)
|
||||
},
|
||||
{
|
||||
default: () => '恢复',
|
||||
icon: renderIcon('material-symbols:settings-backup-restore-rounded', { size: 14 })
|
||||
}
|
||||
),
|
||||
h(
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: () => handleDeleteBackup(row.name)
|
||||
},
|
||||
{
|
||||
default: () => {
|
||||
return '确定删除备份吗?'
|
||||
},
|
||||
trigger: () => {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => '删除',
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const databases = ref<Database[]>([] as Database[])
|
||||
const roles = ref<Role[]>([] as Role[])
|
||||
const backup = ref<Backup[]>([])
|
||||
const load = ref<any[]>([])
|
||||
|
||||
const databasePagination = reactive({
|
||||
page: 1,
|
||||
pageCount: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
showQuickJumper: true,
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 20, 50, 100]
|
||||
})
|
||||
|
||||
const rolePagination = reactive({
|
||||
page: 1,
|
||||
pageCount: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
showQuickJumper: true,
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 20, 50, 100]
|
||||
})
|
||||
|
||||
const backupPagination = reactive({
|
||||
page: 1,
|
||||
pageCount: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
showQuickJumper: true,
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 20, 50, 100]
|
||||
})
|
||||
|
||||
const getLoad = async () => {
|
||||
const { data } = await postgresql.load()
|
||||
return data
|
||||
}
|
||||
|
||||
const getDatabaseList = async (page: number, limit: number) => {
|
||||
const { data } = await postgresql.databases(page, limit)
|
||||
return data
|
||||
}
|
||||
|
||||
const getRoleList = async (page: number, limit: number) => {
|
||||
const { data } = await postgresql.roles(page, limit)
|
||||
return data
|
||||
}
|
||||
|
||||
const getBackupList = async (page: number, limit: number) => {
|
||||
const { data } = await postgresql.backups(page, limit)
|
||||
return data
|
||||
}
|
||||
|
||||
const onDatabasePageChange = (page: number) => {
|
||||
databasePagination.page = page
|
||||
getDatabaseList(page, databasePagination.pageSize).then((res) => {
|
||||
databases.value = res.items
|
||||
databasePagination.itemCount = res.total
|
||||
databasePagination.pageCount = res.total / databasePagination.pageSize + 1
|
||||
})
|
||||
}
|
||||
|
||||
const onRolePageChange = (page: number) => {
|
||||
rolePagination.page = page
|
||||
getRoleList(page, rolePagination.pageSize).then((res) => {
|
||||
roles.value = res.items
|
||||
rolePagination.itemCount = res.total
|
||||
rolePagination.pageCount = res.total / rolePagination.pageSize + 1
|
||||
})
|
||||
}
|
||||
|
||||
const onBackupPageChange = (page: number) => {
|
||||
backupPagination.page = page
|
||||
getBackupList(page, backupPagination.pageSize).then((res) => {
|
||||
backup.value = res.items
|
||||
backupPagination.itemCount = res.total
|
||||
backupPagination.pageCount = res.total / backupPagination.pageSize + 1
|
||||
})
|
||||
}
|
||||
|
||||
const onDatabasePageSizeChange = (pageSize: number) => {
|
||||
databasePagination.pageSize = pageSize
|
||||
onDatabasePageChange(1)
|
||||
}
|
||||
|
||||
const onRolePageSizeChange = (pageSize: number) => {
|
||||
rolePagination.pageSize = pageSize
|
||||
onRolePageChange(1)
|
||||
}
|
||||
|
||||
const onBackupPageSizeChange = (pageSize: number) => {
|
||||
backupPagination.pageSize = pageSize
|
||||
onBackupPageChange(1)
|
||||
}
|
||||
|
||||
const handleDeleteDatabase = async (name: string) => {
|
||||
postgresql.deleteDatabase(name).then(() => {
|
||||
window.$message.success('删除成功')
|
||||
onDatabasePageChange(databasePagination.page)
|
||||
getUserConfig()
|
||||
})
|
||||
}
|
||||
|
||||
const handleDeleteRole = async (user: string) => {
|
||||
postgresql.deleteRole(user).then(() => {
|
||||
window.$message.success('删除成功')
|
||||
onRolePageChange(rolePagination.page)
|
||||
})
|
||||
}
|
||||
|
||||
const showChangePasswordModal = (user: string) => {
|
||||
changePasswordModel.value.user = user
|
||||
changePasswordModal.value = true
|
||||
}
|
||||
|
||||
const getIsEnabled = async () => {
|
||||
await systemctl.isEnabled('postgresql').then((res: any) => {
|
||||
isEnabled.value = res.data
|
||||
@@ -433,99 +113,9 @@ const handleReload = async () => {
|
||||
await getStatus()
|
||||
}
|
||||
|
||||
const handleAddDatabase = async () => {
|
||||
postgresql.addDatabase(addDatabaseModel.value).then(() => {
|
||||
window.$message.success('添加成功')
|
||||
addDatabaseModal.value = false
|
||||
addDatabaseModel.value = {
|
||||
database: '',
|
||||
user: '',
|
||||
password: generateRandomString(16)
|
||||
}
|
||||
onDatabasePageChange(databasePagination.page)
|
||||
onRolePageChange(rolePagination.page)
|
||||
getUserConfig()
|
||||
})
|
||||
}
|
||||
|
||||
const handleAddRole = async () => {
|
||||
postgresql.addRole(addRoleModel.value).then(() => {
|
||||
window.$message.success('添加成功')
|
||||
addRoleModal.value = false
|
||||
addDatabaseModel.value = {
|
||||
user: '',
|
||||
password: generateRandomString(16),
|
||||
database: ''
|
||||
}
|
||||
onRolePageChange(rolePagination.page)
|
||||
})
|
||||
}
|
||||
|
||||
const handleChangePassword = async () => {
|
||||
postgresql
|
||||
.setRolePassword(changePasswordModel.value.user, changePasswordModel.value.password)
|
||||
.then(() => {
|
||||
window.$message.success('修改成功')
|
||||
changePasswordModal.value = false
|
||||
changePasswordModel.value = {
|
||||
user: '',
|
||||
password: generateRandomString(16)
|
||||
}
|
||||
onRolePageChange(rolePagination.page)
|
||||
})
|
||||
}
|
||||
|
||||
const handleUploadBackup = async (files: UploadFileInfo[]) => {
|
||||
messageReactive = window.$message.loading('上传中...', {
|
||||
duration: 0
|
||||
})
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i]
|
||||
const formData = new FormData()
|
||||
formData.append('file', file.file as Blob, file.name)
|
||||
await postgresql.uploadBackup(formData).then(() => {
|
||||
messageReactive?.destroy()
|
||||
window.$message.success('上传成功')
|
||||
onBackupPageChange(backupPagination.page)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreateBackup = async () => {
|
||||
messageReactive = window.$message.loading('创建中...', {
|
||||
duration: 0
|
||||
})
|
||||
await postgresql.createBackup(currentDatabase.value).then(() => {
|
||||
messageReactive?.destroy()
|
||||
window.$message.success('创建成功')
|
||||
onBackupPageChange(backupPagination.page)
|
||||
})
|
||||
}
|
||||
|
||||
const handleRestoreBackup = async (row: any) => {
|
||||
messageReactive = window.$message.loading('恢复中...', {
|
||||
duration: 0
|
||||
})
|
||||
await postgresql.restoreBackup(row.name, currentDatabase.value).then(() => {
|
||||
messageReactive?.destroy()
|
||||
window.$message.success('恢复成功')
|
||||
onBackupPageChange(backupPagination.page)
|
||||
})
|
||||
}
|
||||
|
||||
const handleDeleteBackup = async (name: string) => {
|
||||
await postgresql.deleteBackup(name).then(() => {
|
||||
window.$message.success('删除成功')
|
||||
onBackupPageChange(backupPagination.page)
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getStatus()
|
||||
getIsEnabled()
|
||||
onDatabasePageChange(databasePagination.page)
|
||||
onRolePageChange(rolePagination.page)
|
||||
onBackupPageChange(backupPagination.page)
|
||||
getLoad().then((res) => {
|
||||
load.value = res
|
||||
})
|
||||
@@ -540,16 +130,6 @@ onMounted(() => {
|
||||
<template>
|
||||
<common-page show-footer>
|
||||
<template #action>
|
||||
<n-space v-if="currentTab == 'manage'">
|
||||
<n-button class="ml-16" type="info" @click="addRoleModal = true">
|
||||
<TheIcon :size="18" class="mr-5" icon="material-symbols:add" />
|
||||
新建角色
|
||||
</n-button>
|
||||
<n-button class="ml-16" type="primary" @click="addDatabaseModal = true">
|
||||
<TheIcon :size="18" class="mr-5" icon="material-symbols:add" />
|
||||
新建数据库
|
||||
</n-button>
|
||||
</n-space>
|
||||
<n-button
|
||||
v-if="currentTab == 'config'"
|
||||
class="ml-16"
|
||||
@@ -622,34 +202,6 @@ onMounted(() => {
|
||||
</n-card>
|
||||
</n-space>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="manage" tab="管理">
|
||||
<n-space vertical>
|
||||
<n-card title="数据库" :segmented="true" rounded-10>
|
||||
<n-data-table
|
||||
striped
|
||||
remote
|
||||
:loading="false"
|
||||
:columns="databaseColumns"
|
||||
:data="databases"
|
||||
:row-key="(row: any) => row.name"
|
||||
@update:page="onDatabasePageChange"
|
||||
@update:page-size="onDatabasePageSizeChange"
|
||||
/>
|
||||
</n-card>
|
||||
<n-card title="角色" :segmented="true" rounded-10>
|
||||
<n-data-table
|
||||
striped
|
||||
remote
|
||||
:loading="false"
|
||||
:columns="roleColumns"
|
||||
:data="roles"
|
||||
:row-key="(row: any) => row.user"
|
||||
@update:page="onRolePageChange"
|
||||
@update:page-size="onRolePageSizeChange"
|
||||
/>
|
||||
</n-card>
|
||||
</n-space>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="config" tab="主配置">
|
||||
<n-space vertical>
|
||||
<n-alert type="warning">
|
||||
@@ -708,135 +260,4 @@ onMounted(() => {
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</common-page>
|
||||
<n-modal v-model:show="addDatabaseModal" title="新建数据库">
|
||||
<n-card
|
||||
closable
|
||||
@close="() => (addDatabaseModal = false)"
|
||||
title="新建数据库"
|
||||
style="width: 60vw"
|
||||
>
|
||||
<n-form :model="addDatabaseModel">
|
||||
<n-form-item path="database" label="数据库名">
|
||||
<n-input
|
||||
v-model:value="addDatabaseModel.database"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="输入数据库名"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="user" label="角色名">
|
||||
<n-input
|
||||
v-model:value="addDatabaseModel.user"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="输入角色名"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="password" label="密码">
|
||||
<n-input
|
||||
v-model:value="addDatabaseModel.password"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="建议使用生成器生成随机密码"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-row :gutter="[0, 24]">
|
||||
<n-col :span="24">
|
||||
<n-button type="info" block @click="handleAddDatabase">提交</n-button>
|
||||
</n-col>
|
||||
</n-row>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<n-modal v-model:show="addRoleModal" title="新建角色">
|
||||
<n-card closable @close="() => (addRoleModal = false)" title="新建角色" style="width: 60vw">
|
||||
<n-form :model="addRoleModel">
|
||||
<n-form-item path="user" label="角色名">
|
||||
<n-input
|
||||
v-model:value="addRoleModel.user"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="输入角色名"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="password" label="密码">
|
||||
<n-input
|
||||
v-model:value="addRoleModel.password"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="建议使用生成器生成随机密码"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="database" label="数据库名">
|
||||
<n-input
|
||||
v-model:value="addRoleModel.database"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="输入授权给该角色的数据库名"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-row :gutter="[0, 24]">
|
||||
<n-col :span="24">
|
||||
<n-button type="info" block @click="handleAddRole">提交</n-button>
|
||||
</n-col>
|
||||
</n-row>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<n-modal v-model:show="backupModal">
|
||||
<n-card
|
||||
closable
|
||||
@close="() => (backupModal = false)"
|
||||
:title="'备份管理 - ' + currentDatabase"
|
||||
style="width: 60vw"
|
||||
>
|
||||
<n-space vertical>
|
||||
<n-space>
|
||||
<n-button type="primary" @click="handleCreateBackup">创建备份</n-button>
|
||||
<n-upload
|
||||
accept=".sql,.zip,tar.gz,.tar,.rar,.bz2"
|
||||
:default-upload="false"
|
||||
:show-file-list="false"
|
||||
@update:file-list="handleUploadBackup"
|
||||
>
|
||||
<n-button>上传备份</n-button>
|
||||
</n-upload>
|
||||
</n-space>
|
||||
<n-data-table
|
||||
striped
|
||||
remote
|
||||
:loading="false"
|
||||
:columns="backupColumns"
|
||||
:data="backup"
|
||||
:row-key="(row: any) => row.name"
|
||||
@update:page="onBackupPageChange"
|
||||
@update:page-size="onBackupPageSizeChange"
|
||||
/>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<n-modal v-model:show="changePasswordModal">
|
||||
<n-card
|
||||
closable
|
||||
@close="() => (changePasswordModal = false)"
|
||||
title="修改密码"
|
||||
style="width: 60vw"
|
||||
>
|
||||
<n-form :model="changePasswordModel">
|
||||
<n-form-item path="password" label="密码">
|
||||
<n-input
|
||||
v-model:value="changePasswordModel.password"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="建议使用生成器生成随机密码"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-row :gutter="[0, 24]">
|
||||
<n-col :span="24">
|
||||
<n-button type="info" block @click="handleChangePassword">提交</n-button>
|
||||
</n-col>
|
||||
</n-row>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
@@ -3,17 +3,17 @@ import type { RouteType } from '~/types/router'
|
||||
const Layout = () => import('@/layout/IndexView.vue')
|
||||
|
||||
export default {
|
||||
name: 'mysql80',
|
||||
path: '/apps/mysql80',
|
||||
name: 'postgresql',
|
||||
path: '/apps/postgresql',
|
||||
component: Layout,
|
||||
isHidden: true,
|
||||
children: [
|
||||
{
|
||||
name: 'apps-mysql80-index',
|
||||
name: 'apps-postgresql-index',
|
||||
path: '',
|
||||
component: () => import('../mysql/IndexView.vue'),
|
||||
component: () => import('./IndexView.vue'),
|
||||
meta: {
|
||||
title: 'MySQL 8.0',
|
||||
title: 'PostgreSQL',
|
||||
icon: 'mdi:database',
|
||||
role: ['admin'],
|
||||
requireAuth: true
|
||||
@@ -1,13 +0,0 @@
|
||||
export interface Database {
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface Role {
|
||||
role: string
|
||||
attributes: string[]
|
||||
}
|
||||
|
||||
export interface Backup {
|
||||
name: string
|
||||
size: string
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import type { RouteType } from '~/types/router'
|
||||
|
||||
const Layout = () => import('@/layout/IndexView.vue')
|
||||
|
||||
export default {
|
||||
name: 'postgresql15',
|
||||
path: '/apps/postgresql15',
|
||||
component: Layout,
|
||||
isHidden: true,
|
||||
children: [
|
||||
{
|
||||
name: 'apps-postgresql15-index',
|
||||
path: '',
|
||||
component: () => import('../postgresql/IndexView.vue'),
|
||||
meta: {
|
||||
title: 'PostgreSQL 15',
|
||||
icon: 'mdi:database',
|
||||
role: ['admin'],
|
||||
requireAuth: true
|
||||
}
|
||||
}
|
||||
]
|
||||
} as RouteType
|
||||
@@ -1,23 +0,0 @@
|
||||
import type { RouteType } from '~/types/router'
|
||||
|
||||
const Layout = () => import('@/layout/IndexView.vue')
|
||||
|
||||
export default {
|
||||
name: 'postgresql16',
|
||||
path: '/apps/postgresql16',
|
||||
component: Layout,
|
||||
isHidden: true,
|
||||
children: [
|
||||
{
|
||||
name: 'apps-postgresql16-index',
|
||||
path: '',
|
||||
component: () => import('../postgresql/IndexView.vue'),
|
||||
meta: {
|
||||
title: 'PostgreSQL 16',
|
||||
icon: 'mdi:database',
|
||||
role: ['admin'],
|
||||
requireAuth: true
|
||||
}
|
||||
}
|
||||
]
|
||||
} as RouteType
|
||||
@@ -3,9 +3,10 @@ import Editor from '@guolao/vue-monaco-editor'
|
||||
import { NButton, NDataTable, NInput, NPopconfirm, NSwitch } from 'naive-ui'
|
||||
|
||||
import cron from '@/api/panel/cron'
|
||||
import file from '@/api/panel/file'
|
||||
import info from '@/api/panel/info'
|
||||
import website from '@/api/panel/website'
|
||||
import { renderIcon } from '@/utils'
|
||||
import { formatDateTime, renderIcon } from '@/utils'
|
||||
import type { CronTask } from '@/views/cron/types'
|
||||
|
||||
const addModel = ref({
|
||||
@@ -82,9 +83,19 @@ const columns: any = [
|
||||
key: 'created_at',
|
||||
width: 200,
|
||||
resizable: true,
|
||||
ellipsis: { tooltip: true }
|
||||
ellipsis: { tooltip: true },
|
||||
render(row: any): string {
|
||||
return formatDateTime(row.created_at)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '最后更新时间',
|
||||
key: 'updated_at',
|
||||
ellipsis: { tooltip: true },
|
||||
render(row: any): string {
|
||||
return formatDateTime(row.updated_at)
|
||||
}
|
||||
},
|
||||
{ title: '最后更新时间', key: 'updated_at', ellipsis: { tooltip: true } },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
@@ -219,20 +230,22 @@ const handleShowLog = async (row: any) => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleAdd = async () => {
|
||||
await cron.add(addModel.value).then(() => {
|
||||
window.$message.success('添加成功')
|
||||
const handleCreate = async () => {
|
||||
await cron.create(addModel.value).then(() => {
|
||||
window.$message.success('创建成功')
|
||||
})
|
||||
onPageChange(pagination.page)
|
||||
}
|
||||
|
||||
const handleEdit = async (row: any) => {
|
||||
cron.script(row.id).then((res) => {
|
||||
editTask.value.id = row.id
|
||||
editTask.value.name = row.name
|
||||
editTask.value.time = row.time
|
||||
editTask.value.script = res.data
|
||||
editTaskModal.value = true
|
||||
await cron.get(row.id).then(async (res) => {
|
||||
await file.content(res.data.shell).then((res) => {
|
||||
editTask.value.id = row.id
|
||||
editTask.value.name = row.name
|
||||
editTask.value.time = row.time
|
||||
editTask.value.script = res.data
|
||||
editTaskModal.value = true
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -270,7 +283,7 @@ onMounted(() => {
|
||||
<template>
|
||||
<common-page show-footer>
|
||||
<n-space vertical>
|
||||
<n-card flex-1 rounded-10 title="添加计划任务">
|
||||
<n-card flex-1 rounded-10 title="创建计划任务">
|
||||
<n-space vertical>
|
||||
<n-alert type="info">
|
||||
面板的计划任务均基于脚本运行,若任务类型满足不了需求,可自行修改对应的脚本。
|
||||
@@ -347,7 +360,7 @@ onMounted(() => {
|
||||
<n-input-number v-model:value="addModel.save" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-button type="primary" @click="handleAdd">添加</n-button>
|
||||
<n-button type="primary" @click="handleCreate">创建</n-button>
|
||||
</n-space>
|
||||
</n-card>
|
||||
<n-card flex-1 rounded-10 title="计划任务列表">
|
||||
|
||||
@@ -114,7 +114,7 @@ const bulkDelete = () => {
|
||||
]"
|
||||
@update:value="showNew"
|
||||
>
|
||||
<n-button type="primary"> 新建 </n-button>
|
||||
<n-button type="primary"> 创建 </n-button>
|
||||
</n-popselect>
|
||||
<n-button @click="upload = true"> 上传 </n-button>
|
||||
<n-button style="display: none"> 远程下载 </n-button>
|
||||
@@ -141,7 +141,7 @@ const bulkDelete = () => {
|
||||
<n-modal
|
||||
v-model:show="newModal"
|
||||
preset="card"
|
||||
title="新建"
|
||||
title="创建"
|
||||
style="width: 60vw"
|
||||
size="huge"
|
||||
:bordered="false"
|
||||
|
||||
@@ -4,7 +4,7 @@ import { NButton, NDataTable, NPopconfirm } from 'naive-ui'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import task from '@/api/panel/task'
|
||||
import { renderIcon } from '@/utils'
|
||||
import { formatDateTime, renderIcon } from '@/utils'
|
||||
import type { Task } from '@/views/task/types'
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -40,14 +40,20 @@ const columns: any = [
|
||||
{
|
||||
title: t('taskIndex.columns.createdAt'),
|
||||
key: 'created_at',
|
||||
width: 160,
|
||||
ellipsis: { tooltip: true }
|
||||
width: 200,
|
||||
ellipsis: { tooltip: true },
|
||||
render(row: any): string {
|
||||
return formatDateTime(row.created_at)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: t('taskIndex.columns.updatedAt'),
|
||||
key: 'updated_at',
|
||||
width: 160,
|
||||
ellipsis: { tooltip: true }
|
||||
width: 200,
|
||||
ellipsis: { tooltip: true },
|
||||
render(row: any): string {
|
||||
return formatDateTime(row.updated_at)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: t('taskIndex.columns.actions'),
|
||||
|
||||
Reference in New Issue
Block a user