Add optional site archive backups
This commit is contained in:
+7
-9
@@ -980,12 +980,19 @@ body:not([data-admin-mode="knowledge"]) #knowledge-form {
|
||||
display: flex;
|
||||
min-height: 2.72rem;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.project-archive-button {
|
||||
min-width: 13rem;
|
||||
}
|
||||
|
||||
.project-archive-backup-toggle {
|
||||
min-height: 2.72rem;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.secret-input-wrap {
|
||||
position: relative;
|
||||
display: grid;
|
||||
@@ -1848,15 +1855,6 @@ select {
|
||||
.publish-progress-card {
|
||||
display: grid;
|
||||
gap: 0.9rem;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.publish-progress-card[data-state="completed"] {
|
||||
border-color: rgba(210, 255, 220, 0.32);
|
||||
}
|
||||
|
||||
.publish-progress-card[data-state="failed"] {
|
||||
border-color: rgba(255, 120, 120, 0.42);
|
||||
}
|
||||
|
||||
.publish-progress-head {
|
||||
|
||||
+21
-6
@@ -45,6 +45,7 @@ const state = {
|
||||
page: null,
|
||||
projectConfig: null,
|
||||
projectConfigDirty: false,
|
||||
keepSiteArchiveBackup: true,
|
||||
publishJob: null,
|
||||
publishResumeAttempted: false,
|
||||
knowledge: null,
|
||||
@@ -2206,14 +2207,14 @@ function createProjectSettingsCard({ kicker, title, note = "", fields = [], runt
|
||||
return card;
|
||||
}
|
||||
|
||||
async function uploadSiteArchive(file) {
|
||||
async function uploadSiteArchive(file, keepBackup = true) {
|
||||
if (!file) return;
|
||||
const formData = new FormData();
|
||||
formData.append("file", file, file.name);
|
||||
|
||||
const payload = await new Promise((resolve, reject) => {
|
||||
const request = new XMLHttpRequest();
|
||||
request.open("POST", "/api/project/upload-site");
|
||||
request.open("POST", `/api/project/upload-site?backup=${keepBackup ? "1" : "0"}`);
|
||||
request.responseType = "json";
|
||||
request.upload.addEventListener("progress", (event) => {
|
||||
if (!event.lengthComputable) {
|
||||
@@ -2253,7 +2254,10 @@ async function uploadSiteArchive(file) {
|
||||
|
||||
state.projectConfig = await loadProjectConfig();
|
||||
renderProjectSettings();
|
||||
setStatus(`Архив сайта загружен: ${Number(payload.files || 0).toLocaleString("ru-RU")} файлов, workspace ${payload.size?.label || payload.label || "обновлен"}.`);
|
||||
const backupStatus = payload.backup?.saved
|
||||
? ` Бэкап сохранён: ${payload.backup.fileName} (${payload.backup.label}).`
|
||||
: " Бэкап не создавался.";
|
||||
setStatus(`Архив сайта загружен: ${Number(payload.files || 0).toLocaleString("ru-RU")} файлов, workspace ${payload.size?.label || payload.label || "обновлен"}.${backupStatus}`);
|
||||
}
|
||||
|
||||
function renderProjectArchiveUpload() {
|
||||
@@ -2263,6 +2267,9 @@ function renderProjectArchiveUpload() {
|
||||
const body = document.createElement("div");
|
||||
const button = document.createElement("button");
|
||||
const input = document.createElement("input");
|
||||
const backupToggle = document.createElement("label");
|
||||
const backupCheckbox = document.createElement("input");
|
||||
const backupLabel = document.createElement("span");
|
||||
const note = document.createElement("small");
|
||||
|
||||
field.className = "field-control project-archive-field";
|
||||
@@ -2276,17 +2283,25 @@ function renderProjectArchiveUpload() {
|
||||
input.type = "file";
|
||||
input.accept = ".zip,application/zip";
|
||||
input.className = "hidden";
|
||||
backupToggle.className = "toggle-row project-archive-backup-toggle";
|
||||
backupCheckbox.type = "checkbox";
|
||||
backupCheckbox.checked = state.keepSiteArchiveBackup;
|
||||
backupLabel.textContent = "Сохранить бэкап на сервере";
|
||||
backupToggle.append(backupCheckbox, backupLabel);
|
||||
note.className = "field-hint";
|
||||
note.textContent = "ZIP должен содержать content/pages/home.json, content/knowledge.json, templates и tools рендера.";
|
||||
note.textContent = "При включённом бэкапе исходный ZIP сохраняется отдельной версией в .cms-backups. ZIP должен содержать content/pages/home.json, content/knowledge.json, templates и tools рендера.";
|
||||
|
||||
button.addEventListener("click", () => input.click());
|
||||
backupCheckbox.addEventListener("change", () => {
|
||||
state.keepSiteArchiveBackup = backupCheckbox.checked;
|
||||
});
|
||||
input.addEventListener("change", () => {
|
||||
const file = input.files?.[0];
|
||||
input.value = "";
|
||||
uploadSiteArchive(file).catch((error) => setStatus(error.message));
|
||||
uploadSiteArchive(file, backupCheckbox.checked).catch((error) => setStatus(error.message));
|
||||
});
|
||||
|
||||
body.append(button, input);
|
||||
body.append(button, input, backupToggle);
|
||||
field.append(title, badge, body, note);
|
||||
return field;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user