从现有站点恢复重命名为从指定站点恢复
This commit is contained in:
parent
da3f814451
commit
057d8164f5
@ -45,7 +45,7 @@ function getSiteActionHandler(action) {
|
|||||||
'使用文件恢复': defineAsyncComponent(() =>
|
'使用文件恢复': defineAsyncComponent(() =>
|
||||||
import('./SiteDatabaseRestoreDialog.vue')
|
import('./SiteDatabaseRestoreDialog.vue')
|
||||||
),
|
),
|
||||||
'从现有站点恢复': defineAsyncComponent(() =>
|
'从指定站点恢复': defineAsyncComponent(() =>
|
||||||
import('./site/SiteDatabaseRestoreFromURLDialog.vue')
|
import('./site/SiteDatabaseRestoreFromURLDialog.vue')
|
||||||
),
|
),
|
||||||
'管理数据库用户': defineAsyncComponent(() =>
|
'管理数据库用户': defineAsyncComponent(() =>
|
||||||
|
|||||||
@ -1,156 +1,188 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog
|
<Dialog
|
||||||
:options="{
|
:options="{
|
||||||
title: '从现有站点恢复'
|
title: '从指定站点恢复'
|
||||||
}"
|
}"
|
||||||
v-model="showRestoreDialog"
|
v-model="showRestoreDialog"
|
||||||
>
|
>
|
||||||
<template #body-content>
|
<template #body-content>
|
||||||
<div
|
<div
|
||||||
class="mb-6 flex items-center rounded border border-gray-200 bg-gray-100 p-4 text-sm text-gray-600"
|
class="mb-6 flex items-center rounded border border-gray-200 bg-gray-100 p-4 text-sm text-gray-600"
|
||||||
>
|
>
|
||||||
<i-lucide-alert-triangle class="mr-4 inline-block h-6 w-6" />
|
<i-lucide-alert-triangle class="mr-4 inline-block h-6 w-6" />
|
||||||
<div>
|
<div>
|
||||||
此操作将用备份中的<b>数据</b>和<b>应用</b>替换您站点中的当前内容
|
此操作将用备份中的<b>数据</b>和<b>应用</b>替换您站点中的当前内容
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<FormControl label="站点URL" v-model="siteURL" />
|
<FormControl label="站点URL" v-model="siteURL" />
|
||||||
<FormControl label="邮箱" v-model="email" />
|
<FormControl label="邮箱" v-model="email" />
|
||||||
<FormControl label="密码" type="password" v-model="password" />
|
<FormControl label="密码" type="password" v-model="password" />
|
||||||
<div class="flex text-base" v-if="$resources.getBackupLinks.data">
|
<div class="flex text-base" v-if="$resources.getBackupLinks.data">
|
||||||
<GreenCheckIcon class="mr-2 w-4" />
|
<GreenCheckIcon class="mr-2 w-4" />
|
||||||
找到来自 {{ fetchedBackupFileTimestamp }} 的最新备份
|
找到来自 {{ fetchedBackupFileTimestamp }} 的最新备份
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
v-else
|
v-else
|
||||||
@click="$resources.getBackupLinks.submit()"
|
@click="$resources.getBackupLinks.submit()"
|
||||||
:loading="$resources.getBackupLinks.loading"
|
:loading="$resources.getBackupLinks.loading"
|
||||||
>
|
>
|
||||||
获取备份
|
获取备份
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<FormControl
|
<FormControl
|
||||||
label="跳过失败的补丁(如果有任何补丁失败)"
|
label="跳过失败的补丁(如果有任何补丁失败)"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
v-model="skipFailingPatches"
|
v-model="skipFailingPatches"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
class="mt-2"
|
class="mt-2"
|
||||||
:message="
|
:message="
|
||||||
$resources.restoreBackup.error || $resources.getBackupLinks.error
|
$resources.restoreBackup.error || $resources.getBackupLinks.error
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<Button
|
<Button
|
||||||
class="w-full"
|
class="w-full"
|
||||||
label="恢复"
|
label="恢复"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
theme="red"
|
theme="red"
|
||||||
:loading="$resources.restoreBackup.loading"
|
:loading="$resources.restoreBackup.loading"
|
||||||
:disabled="!$resources.getBackupLinks.data"
|
:disabled="!$resources.getBackupLinks.data"
|
||||||
@click="$resources.restoreBackup.submit"
|
@click="$resources.restoreBackup.submit"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { date } from '../../utils/format';
|
import { date } from '../../utils/format';
|
||||||
import { DashboardError } from '../../utils/error';
|
import { DashboardError } from '../../utils/error';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SiteDatabaseRestoreDialog',
|
name: 'SiteDatabaseRestoreDialog',
|
||||||
props: {
|
props: {
|
||||||
site: {
|
site: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
siteURL: '',
|
siteURL: '',
|
||||||
email: '',
|
email: '',
|
||||||
password: '',
|
password: '',
|
||||||
selectedFiles: {
|
selectedFiles: {
|
||||||
database: null,
|
database: null,
|
||||||
public: null,
|
public: null,
|
||||||
private: null
|
private: null
|
||||||
},
|
},
|
||||||
showRestoreDialog: true,
|
showRestoreDialog: true,
|
||||||
skipFailingPatches: false
|
skipFailingPatches: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
resources: {
|
resources: {
|
||||||
getBackupLinks() {
|
getBackupLinks() {
|
||||||
return {
|
return {
|
||||||
url: 'jcloud.api.site.get_backup_links',
|
url: 'jcloud.api.site.get_backup_links',
|
||||||
params: {
|
params: {
|
||||||
url: this.siteURL,
|
url: this.siteURL,
|
||||||
email: this.email,
|
email: this.email,
|
||||||
password: this.password
|
password: this.password
|
||||||
},
|
},
|
||||||
validate() {
|
validate() {
|
||||||
if (!this.siteURL) {
|
if (!this.siteURL) {
|
||||||
throw new DashboardError('站点URL是必填项');
|
throw new DashboardError('站点URL是必填项');
|
||||||
}
|
}
|
||||||
if (!this.email) {
|
if (!this.email) {
|
||||||
throw new DashboardError('邮箱是必填项');
|
throw new DashboardError('邮箱是必填项');
|
||||||
}
|
}
|
||||||
if (!this.password) {
|
if (!this.password) {
|
||||||
throw new DashboardError('密码是必填项');
|
throw new DashboardError('密码是必填项');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSuccess(remoteFiles) {
|
onSuccess(remoteFiles) {
|
||||||
for (let file of remoteFiles) {
|
for (let file of remoteFiles) {
|
||||||
this.selectedFiles[file.type] = file.remote_file;
|
this.selectedFiles[file.type] = file.remote_file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
restoreBackup() {
|
restoreBackup() {
|
||||||
return {
|
return {
|
||||||
url: 'jcloud.api.site.restore',
|
url: 'jcloud.api.site.restore',
|
||||||
params: {
|
params: {
|
||||||
name: this.site,
|
name: this.site,
|
||||||
files: this.selectedFiles,
|
files: this.selectedFiles,
|
||||||
skip_failing_patches: this.skipFailingPatches
|
skip_failing_patches: this.skipFailingPatches
|
||||||
},
|
},
|
||||||
validate() {
|
validate() {
|
||||||
if (!this.selectedFiles.database) {
|
if (!this.selectedFiles.database) {
|
||||||
throw new DashboardError(
|
throw new DashboardError(
|
||||||
'从站点获取备份时出错'
|
'从站点获取备份时出错'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
this.siteURL = '';
|
this.siteURL = '';
|
||||||
this.email = '';
|
this.email = '';
|
||||||
this.password = '';
|
this.password = '';
|
||||||
this.showRestoreDialog = false;
|
this.showRestoreDialog = false;
|
||||||
|
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'Site Jobs',
|
name: 'Site Jobs',
|
||||||
params: { name: this.site }
|
params: { name: this.site }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
fetchedBackupFileTimestamp() {
|
fetchedBackupFileTimestamp() {
|
||||||
if (!this.$resources.getBackupLinks.data) return '';
|
if (!this.$resources.getBackupLinks.data) return '';
|
||||||
|
|
||||||
let backup = this.$resources.getBackupLinks.data[0];
|
let backup = this.$resources.getBackupLinks.data[0];
|
||||||
let timestamp_string = backup.file_name
|
if (!backup || !backup.file_name) return '';
|
||||||
.split('-')[0]
|
|
||||||
.split('_')
|
let timestamp_string = '';
|
||||||
.join('T');
|
|
||||||
|
// 尝试匹配 YYYYMMDD_HHMMSS 格式(实际格式)
|
||||||
return date(timestamp_string);
|
let timestampMatch = backup.file_name.match(/(\d{8}_\d{6})/);
|
||||||
}
|
if (timestampMatch) {
|
||||||
}
|
let match = timestampMatch[1];
|
||||||
};
|
let year = match.substring(0, 4);
|
||||||
|
let month = match.substring(4, 6);
|
||||||
|
let day = match.substring(6, 8);
|
||||||
|
let hour = match.substring(9, 11);
|
||||||
|
let minute = match.substring(11, 13);
|
||||||
|
let second = match.substring(13, 15);
|
||||||
|
timestamp_string = `${year}-${month}-${day}T${hour}:${minute}:${second}`;
|
||||||
|
} else {
|
||||||
|
// 尝试匹配 YYYY-MM-DD_HH-MM-SS 格式(备用格式)
|
||||||
|
let oldFormatMatch = backup.file_name.match(/(\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2})/);
|
||||||
|
if (oldFormatMatch) {
|
||||||
|
timestamp_string = oldFormatMatch[1].replace('_', 'T').replace(/-/g, '-');
|
||||||
|
} else {
|
||||||
|
// 尝试匹配 YYYY-MM-DD 格式
|
||||||
|
let dateMatch = backup.file_name.match(/(\d{4}-\d{2}-\d{2})/);
|
||||||
|
if (dateMatch) {
|
||||||
|
timestamp_string = dateMatch[1] + 'T00:00:00';
|
||||||
|
} else {
|
||||||
|
// 如果都没有匹配到,返回文件名本身
|
||||||
|
return backup.file_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return date(timestamp_string);
|
||||||
|
} catch (e) {
|
||||||
|
// 如果日期解析失败,返回文件名
|
||||||
|
return backup.file_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -2783,8 +2783,8 @@ class Site(Page, TagHelpers):
|
|||||||
"group": "危险操作",
|
"group": "危险操作",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"action": "从现有站点恢复",
|
"action": "从指定站点恢复",
|
||||||
"description": "从另一个站点恢复数据库、公共和私有文件",
|
"description": "从指定站点的备份文件恢复数据库、公共和私有文件",
|
||||||
"button_label": "恢复",
|
"button_label": "恢复",
|
||||||
"pg_method": "restore_site_from_files",
|
"pg_method": "restore_site_from_files",
|
||||||
"group": "危险操作",
|
"group": "危险操作",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user