mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 14:57:16 +08:00
feat: website backup and restore
This commit is contained in:
@@ -28,6 +28,7 @@ func NewWebsiteController() *WebsiteController {
|
||||
}
|
||||
}
|
||||
|
||||
// List 网站列表
|
||||
func (c *WebsiteController) List(ctx http.Context) {
|
||||
limit := ctx.Request().QueryInt("limit")
|
||||
page := ctx.Request().QueryInt("page")
|
||||
@@ -45,6 +46,7 @@ func (c *WebsiteController) List(ctx http.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// Add 添加网站
|
||||
func (c *WebsiteController) Add(ctx http.Context) {
|
||||
if !Check(ctx, "openresty") {
|
||||
return
|
||||
@@ -88,6 +90,7 @@ func (c *WebsiteController) Add(ctx http.Context) {
|
||||
Success(ctx, newSite)
|
||||
}
|
||||
|
||||
// Delete 删除网站
|
||||
func (c *WebsiteController) Delete(ctx http.Context) {
|
||||
if !Check(ctx, "openresty") {
|
||||
return
|
||||
@@ -103,6 +106,7 @@ func (c *WebsiteController) Delete(ctx http.Context) {
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
// GetDefaultConfig 获取默认配置
|
||||
func (c *WebsiteController) GetDefaultConfig(ctx http.Context) {
|
||||
if !Check(ctx, "openresty") {
|
||||
return
|
||||
@@ -116,6 +120,7 @@ func (c *WebsiteController) GetDefaultConfig(ctx http.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// SaveDefaultConfig 保存默认配置
|
||||
func (c *WebsiteController) SaveDefaultConfig(ctx http.Context) {
|
||||
if !Check(ctx, "openresty") {
|
||||
return
|
||||
@@ -138,6 +143,7 @@ func (c *WebsiteController) SaveDefaultConfig(ctx http.Context) {
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
// GetConfig 获取配置
|
||||
func (c *WebsiteController) GetConfig(ctx http.Context) {
|
||||
if !Check(ctx, "openresty") {
|
||||
return
|
||||
@@ -158,6 +164,7 @@ func (c *WebsiteController) GetConfig(ctx http.Context) {
|
||||
Success(ctx, config)
|
||||
}
|
||||
|
||||
// SaveConfig 保存配置
|
||||
func (c *WebsiteController) SaveConfig(ctx http.Context) {
|
||||
if !Check(ctx, "openresty") {
|
||||
return
|
||||
@@ -393,6 +400,7 @@ func (c *WebsiteController) SaveConfig(ctx http.Context) {
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
// ClearLog 清空日志
|
||||
func (c *WebsiteController) ClearLog(ctx http.Context) {
|
||||
if !Check(ctx, "openresty") {
|
||||
return
|
||||
@@ -416,6 +424,7 @@ func (c *WebsiteController) ClearLog(ctx http.Context) {
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
// UpdateRemark 更新备注
|
||||
func (c *WebsiteController) UpdateRemark(ctx http.Context) {
|
||||
id := ctx.Request().InputInt("id")
|
||||
if id == 0 {
|
||||
@@ -442,6 +451,7 @@ func (c *WebsiteController) UpdateRemark(ctx http.Context) {
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
// BackupList 备份列表
|
||||
func (c *WebsiteController) BackupList(ctx http.Context) {
|
||||
backupList, err := c.backup.WebsiteList()
|
||||
if err != nil {
|
||||
@@ -453,6 +463,7 @@ func (c *WebsiteController) BackupList(ctx http.Context) {
|
||||
Success(ctx, backupList)
|
||||
}
|
||||
|
||||
// CreateBackup 创建备份
|
||||
func (c *WebsiteController) CreateBackup(ctx http.Context) {
|
||||
id := ctx.Request().InputInt("id")
|
||||
if id == 0 {
|
||||
@@ -478,6 +489,82 @@ func (c *WebsiteController) CreateBackup(ctx http.Context) {
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
// UploadBackup 上传备份
|
||||
func (c *WebsiteController) UploadBackup(ctx http.Context) {
|
||||
file, err := ctx.Request().File("file")
|
||||
if err != nil {
|
||||
Error(ctx, http.StatusBadRequest, "上传文件失败")
|
||||
return
|
||||
}
|
||||
|
||||
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/website"
|
||||
if !tools.Exists(backupPath) {
|
||||
tools.Mkdir(backupPath, 0644)
|
||||
}
|
||||
|
||||
name := file.GetClientOriginalName()
|
||||
_, err = file.StoreAs(backupPath, name)
|
||||
if err != nil {
|
||||
Error(ctx, http.StatusBadRequest, "上传文件失败")
|
||||
return
|
||||
}
|
||||
|
||||
Success(ctx, "上传文件成功")
|
||||
}
|
||||
|
||||
// RestoreBackup 还原备份
|
||||
func (c *WebsiteController) RestoreBackup(ctx http.Context) {
|
||||
id := ctx.Request().InputInt("id")
|
||||
if id == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "参数错误")
|
||||
return
|
||||
}
|
||||
fileName := ctx.Request().Input("name")
|
||||
if len(fileName) == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
website := models.Website{}
|
||||
err := facades.Orm().Query().Where("id", id).Get(&website)
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 获取网站信息失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "获取网站信息失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = c.backup.WebsiteRestore(website, fileName)
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][WebsiteController] 还原网站失败 ", err)
|
||||
Error(ctx, http.StatusInternalServerError, "还原网站失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
// DeleteBackup 删除备份
|
||||
func (c *WebsiteController) DeleteBackup(ctx http.Context) {
|
||||
fileName := ctx.Request().Input("name")
|
||||
if len(fileName) == 0 {
|
||||
Error(ctx, http.StatusBadRequest, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/website"
|
||||
if !tools.Exists(backupPath) {
|
||||
tools.Mkdir(backupPath, 0644)
|
||||
}
|
||||
|
||||
if !tools.RemoveFile(backupPath + "/" + fileName) {
|
||||
Error(ctx, http.StatusInternalServerError, "删除备份失败")
|
||||
return
|
||||
}
|
||||
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
// ResetConfig 重置配置
|
||||
func (c *WebsiteController) ResetConfig(ctx http.Context) {
|
||||
if !Check(ctx, "openresty") {
|
||||
return
|
||||
@@ -569,6 +656,7 @@ server
|
||||
Success(ctx, nil)
|
||||
}
|
||||
|
||||
// Status 网站状态
|
||||
func (c *WebsiteController) Status(ctx http.Context) {
|
||||
if !Check(ctx, "openresty") {
|
||||
return
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
type Backup interface {
|
||||
WebsiteList() ([]BackupFile, error)
|
||||
WebSiteBackup(website models.Website) error
|
||||
WebsiteRestore(website models.Website, backupFile string) error
|
||||
}
|
||||
|
||||
type BackupFile struct {
|
||||
@@ -38,6 +39,9 @@ func (s *BackupImpl) WebsiteList() ([]BackupFile, error) {
|
||||
}
|
||||
|
||||
path += "/website"
|
||||
if !tools.Exists(path) {
|
||||
tools.Mkdir(path, 0644)
|
||||
}
|
||||
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
@@ -69,8 +73,32 @@ func (s *BackupImpl) WebSiteBackup(website models.Website) error {
|
||||
tools.Mkdir(backupPath, 0644)
|
||||
}
|
||||
|
||||
backupFile := backupPath + "/" + website.Name + carbon.Now().ToShortDateTimeString() + ".zip"
|
||||
tools.ExecShell("cd " + website.Path + " && zip -r " + backupFile + " .")
|
||||
backupFile := backupPath + "/" + website.Name + "_" + carbon.Now().ToShortDateTimeString() + ".zip"
|
||||
tools.ExecShell(`cd '` + website.Path + `' && zip -r '` + backupFile + `' .`)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *BackupImpl) WebsiteRestore(website models.Website, backupFile string) error {
|
||||
backupPath := s.setting.Get(models.SettingKeyBackupPath)
|
||||
if len(backupPath) == 0 {
|
||||
return errors.New("未正确配置备份路径")
|
||||
}
|
||||
|
||||
backupPath += "/website"
|
||||
if !tools.Exists(backupPath) {
|
||||
tools.Mkdir(backupPath, 0644)
|
||||
}
|
||||
|
||||
backupFile = backupPath + "/" + backupFile
|
||||
if !tools.Exists(backupFile) {
|
||||
return errors.New("备份文件不存在")
|
||||
}
|
||||
|
||||
tools.ExecShell(`rm -rf '` + website.Path + `/*'`)
|
||||
tools.ExecShell(`unzip -o '` + backupFile + `' -d '` + website.Path + `' 2>&1`)
|
||||
tools.Chmod(website.Path, 0755)
|
||||
tools.Chown(website.Path, "www", "www")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -348,7 +348,7 @@ func (r *WebsiteImpl) GetConfig(id int) (WebsiteSetting, error) {
|
||||
}
|
||||
|
||||
setting.Rewrite = tools.ReadFile("/www/server/vhost/rewrite/" + website.Name + ".conf")
|
||||
setting.Log = tools.ExecShell("tail -n 100 /www/wwwlogs/" + website.Name + ".log")
|
||||
setting.Log = tools.ExecShell(`tail -n 100 '/www/wwwlogs/` + website.Name + `.log'`)
|
||||
|
||||
return setting, nil
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<!--
|
||||
Name: 网站 - 备份
|
||||
Author: 耗子
|
||||
Date: 2023-07-21
|
||||
Date: 2023-07-24
|
||||
-->
|
||||
<h1>这里正在装修,下个版本再来看看吧!</h1>
|
||||
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs12 layui-col-sm12 layui-col-md12">
|
||||
@@ -26,8 +25,7 @@ Date: 2023-07-21
|
||||
<script>
|
||||
layui.data.sendParams = function (params) {
|
||||
layui.use(['admin', 'form', 'laydate', 'code'], function () {
|
||||
var $ = layui.$
|
||||
, admin = layui.admin
|
||||
var admin = layui.admin
|
||||
, layer = layui.layer
|
||||
, table = layui.table
|
||||
, upload = layui.upload;
|
||||
@@ -39,7 +37,7 @@ Date: 2023-07-21
|
||||
, toolbar: '#website-backup-bar'
|
||||
, title: '备份列表'
|
||||
, cols: [[
|
||||
{field: 'backup', title: '备份名称', width: 500}
|
||||
{field: 'name', title: '备份名称', width: 500}
|
||||
, {field: 'size', title: '文件大小'}
|
||||
, {field: 'right', title: '操作', width: 150, toolbar: '#website-backup-control'}
|
||||
]]
|
||||
@@ -56,6 +54,7 @@ Date: 2023-07-21
|
||||
index = layer.msg('正在上传备份文件,可能需要较长时间,请勿操作...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
}
|
||||
, done: function (res) {
|
||||
@@ -63,9 +62,6 @@ Date: 2023-07-21
|
||||
layer.msg('上传成功!', {icon: 1});
|
||||
table.reload('website-backup-list');
|
||||
}
|
||||
, error: function (res) {
|
||||
layer.msg('上传失败:' + res.msg, {icon: 2});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -75,26 +71,23 @@ Date: 2023-07-21
|
||||
index = layer.msg('正在备份网站,请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
admin.req({
|
||||
url: '/api/panel/website/createBackup'
|
||||
, type: 'post'
|
||||
, data: {
|
||||
name: params.data.name
|
||||
id: params.data.id
|
||||
}
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:网站失败,接口返回' + result);
|
||||
layer.alert('备份失败!');
|
||||
return false;
|
||||
}
|
||||
table.reload('website-backup-list');
|
||||
layer.msg('备份成功!', {icon: 1});
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -102,52 +95,46 @@ Date: 2023-07-21
|
||||
table.on('tool(website-backup-list)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'del') {
|
||||
layer.confirm('确定要删除网站备份 <b style="color: red;">' + data.backup + '</b> 吗?', function (index) {
|
||||
layer.confirm('确定要删除网站备份 <b style="color: red;">' + data.name + '</b> 吗?', function (index) {
|
||||
index = layer.msg('正在删除网站备份,请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/panel/website/deleteBackup"
|
||||
, method: 'post'
|
||||
, type: 'post'
|
||||
, data: data
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:网站备份删除失败,接口返回' + result);
|
||||
layer.msg('网站备份删除失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
obj.del();
|
||||
layer.alert('网站备份' + data.backup + '删除成功!');
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
layer.alert('网站备份' + data.name + '删除成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (obj.event === 'restore') {
|
||||
layer.confirm('高风险操作,确定要恢复网站备份 <b style="color: red;">' + data.backup + '</b> 吗?', function (index) {
|
||||
layer.confirm('高风险操作,确定要恢复网站备份 <b style="color: red;">' + data.name + '</b> 吗?', function (index) {
|
||||
index = layer.msg('正在恢复网站备份,可能需要较长时间,请勿操作...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
, shade: 0.3
|
||||
});
|
||||
data.name = params.data.name;
|
||||
data.id = params.data.id;
|
||||
admin.req({
|
||||
url: "/api/panel/website/restoreBackup"
|
||||
, method: 'post'
|
||||
, type: 'post'
|
||||
, data: data
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:网站备份恢复失败,接口返回' + result);
|
||||
layer.msg('网站备份恢复失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
layer.alert('网站备份' + data.backup + '恢复成功!');
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
layer.alert('网站备份' + data.name + '恢复成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!--
|
||||
Name: 网站 - 编辑
|
||||
Author: 耗子
|
||||
Date: 2023-07-21
|
||||
Date: 2023-07-24
|
||||
-->
|
||||
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
|
||||
<div class="layui-tab" lay-filter="website-edit-tab">
|
||||
@@ -321,7 +321,7 @@ Date: 2023-07-21
|
||||
url: '/api/panel/website/config'
|
||||
, type: 'post'
|
||||
, data: {
|
||||
id: params.id,
|
||||
id: params.data.id,
|
||||
domains: domains.join('\n'),
|
||||
ports: ports.join('\n'),
|
||||
ssl: $('input[name="ssl"]').prop('checked'),
|
||||
@@ -365,7 +365,7 @@ Date: 2023-07-21
|
||||
admin.req({
|
||||
url: '/api/panel/website/resetConfig'
|
||||
, type: 'post'
|
||||
, data: {id: params.id}
|
||||
, data: {id: params.data.id}
|
||||
, success: function (res) {
|
||||
layer.close(index)
|
||||
if (res.code === 0) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!--
|
||||
Name: 网站 - 列表
|
||||
Author: 耗子
|
||||
Date: 2023-07-71
|
||||
Date: 2023-07-74
|
||||
-->
|
||||
<title>网站</title>
|
||||
<div class="layui-fluid">
|
||||
@@ -167,7 +167,6 @@ Date: 2023-07-71
|
||||
, id: 'LAY-popup-website-edit'
|
||||
, success: function (layero, index) {
|
||||
view(this.id).render('website/edit', {
|
||||
id : data.id,
|
||||
php: php,
|
||||
mysql: mysql,
|
||||
postgresql: postgresql,
|
||||
|
||||
@@ -48,6 +48,9 @@ func Web() {
|
||||
r.Post("updateRemark", websiteController.UpdateRemark)
|
||||
r.Get("backupList", websiteController.BackupList)
|
||||
r.Post("createBackup", websiteController.CreateBackup)
|
||||
r.Post("uploadBackup", websiteController.UploadBackup)
|
||||
r.Post("restoreBackup", websiteController.RestoreBackup)
|
||||
r.Post("deleteBackup", websiteController.DeleteBackup)
|
||||
r.Post("resetConfig", websiteController.ResetConfig)
|
||||
r.Post("status", websiteController.Status)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user