Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
chore: Merge pull request #36 from alist-org/dev
Browse files Browse the repository at this point in the history
backup, restore and fix error open text file after close image viewer
  • Loading branch information
xhofe authored Mar 31, 2022
2 parents d7cc1aa + a68ac11 commit 3cb5caf
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 18 deletions.
4 changes: 4 additions & 0 deletions src/i18n/locales/jp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ const zh = {
"hide files, support RegExp, one per line":"ファイルを隠す、正規表現をサポートし、1行に1つ",
"Install": "インストール",
"Installing": "インストール中",
"Backup & Restore": "バックアップと復元",
"Backup": "バックアップ",
"Restore": "復元",
"Virtual path": "仮想パス",
}

export default zh
Expand Down
4 changes: 4 additions & 0 deletions src/i18n/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ const zh = {
"hide files, support RegExp, one per line":"隐藏文件,支持正则表达式,每行一个",
"Install": "安装",
"Installing": "安装中",
"Backup & Restore": "备份与还原",
"Backup": "备份",
"Restore": "还原",
"Virtual path": "虚拟路径",
}

export default zh
Expand Down
24 changes: 13 additions & 11 deletions src/pages/list/layout/files/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,19 @@ const Files = () => {
/>
)}
<Page />
<Viewer
visible={visible}
activeIndex={index}
onClose={() => {
setVisible(false);
}}
onChange={(_, index) => {
setIndex(index);
}}
images={images}
/>
{visible && (
<Viewer
visible={visible}
activeIndex={index}
onClose={() => {
setVisible(false);
}}
onChange={(_, index) => {
setIndex(index);
}}
images={images}
/>
)}
<ContextMenu />
</Box>
);
Expand Down
10 changes: 5 additions & 5 deletions src/pages/manage/accounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import admin from "../../utils/admin";
import { useTranslation } from "react-i18next";
import FormItem from "../../components/form-item";
import { copyToClip, readFromClip } from "../../utils/copy-clip";
interface Account {
export interface Account {
id: number;
name: string;
type: string;
Expand Down Expand Up @@ -98,11 +98,11 @@ interface PropItem {
}

function GetDefaultValue(
type: "string" | "bool" | "select" | "number",
type: "string" | "bool" | "select" | "number" | "text",
value?: string
) {
switch (type) {
case "string":
case "string" || "text":
if (value) {
return value;
}
Expand All @@ -128,7 +128,7 @@ function GetDefaultValue(
const CommonItems: PropItem[] = [
{
name: "name",
label: "name",
label: "Virtual path",
type: "string",
required: true,
},
Expand Down Expand Up @@ -235,7 +235,7 @@ const Accounts = () => {
<Table w="full">
<Thead>
<Tr>
<Th>{t("name")}</Th>
<Th>{t("Virtual path")}</Th>
<Th>{t("type")}</Th>
<Th>{t("root_folder")}</Th>
<Th>{t("index")}</Th>
Expand Down
163 changes: 163 additions & 0 deletions src/pages/manage/backup-restore.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { Box, Button, Input, Textarea, useToast } from "@chakra-ui/react";
import React from "react";
import { useTranslation } from "react-i18next";
import admin from "~/utils/admin";
import download from "~/utils/download-json";
import { Account } from "./accounts";
import { Meta } from "./metas";
import { SettingItem } from "./settings";

interface Data {
accounts: Account[];
settings: SettingItem[];
metas: Meta[];
}

const NO_BACKUP_SETTINGS = ["version", "password"];

const data: Data = {
accounts: [],
settings: [],
metas: [],
};

const BackupRestore = () => {
const { t } = useTranslation();
const [log, setLog] = React.useState("");
const addLog = (msg: string) => {
setLog((log) => `${log}\n${msg}`);
};
const toast = useToast();
const fail = (msg: string) => {
toast({
title: t(msg),
status: "error",
duration: 3000,
isClosable: true,
});
};
const backup = async () => {
setLog("get settings...");
const resp1 = await admin.get("settings");
const res1 = resp1.data;
if (res1.code !== 200) {
fail(res1.message);
return;
} else {
addLog("get settings success");
data.settings = res1.data;
data.settings = data.settings.filter(
(item) => !NO_BACKUP_SETTINGS.includes(item.key)
);
}
addLog("get accounts...");
const resp2 = await admin.get("accounts");
const res2 = resp2.data;
if (res2.code !== 200) {
fail(res2.message);
return;
} else {
addLog("get accounts success");
data.accounts = res2.data;
// data.accounts = data.accounts.map((account) => {
// account.id = 0;
// return account;
// });
}
addLog("get metas...");
const resp3 = await admin.get("metas");
const res3 = resp3.data;
if (res3.code !== 200) {
fail(res3.message);
return;
} else {
addLog("get metas success");
data.metas = res3.data;
}
addLog("download backup file...");
download(
`${
data.settings.find((item) => item.key === "title")?.value || "alist"
}.json`,
data
);
};
const restore = async () => {
setLog("choose data file...");
const fileInput = document.querySelector(
"#restore-file"
) as HTMLInputElement;
fileInput.click();
};
const onRestoreFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const files = e.target.files;
const file = files![0];
if (!files || !file) {
return;
}
const reader = new FileReader();
reader.onload = async (e) => {
const data: Data = JSON.parse(reader.result as string);
addLog("restore settings...");
const resp1 = await admin.post("settings", data.settings);
const res1 = resp1.data;
if (res1.code !== 200) {
fail(res1.message);
return;
}
addLog("restore settings success");
addLog("restore accounts...");
for (const account of data.accounts) {
const resp2 = await admin.post("account/create", { ...account, id: 0 });
const res2 = resp2.data;
if (res2.code !== 200) {
addLog(
`failed to restore account:[${account.name}],the reason is [${res2.message}]`
);
continue;
}
addLog(`restore account:[${account.name}] success`);
}
addLog("finish restore accounts");
addLog("restore metas...");
for (const meta of data.metas) {
const resp3 = await admin.post("meta/create", { ...meta, id: 0 });
const res3 = resp3.data;
if (res3.code !== 200) {
addLog(
`failed to restore meta:[${meta.path}],the reason is [${res3.message}]`
);
continue;
}
addLog(`restore meta:[${meta.path}] success`);
}
addLog("finish restore metas");
toast({
title: t("restore success"),
status: "success",
duration: 3000,
isClosable: true,
});
};
reader.readAsText(file);
};
return (
<Box>
<Button colorScheme="green" onClick={backup}>
{t("Backup")}
</Button>
<Button ml="2" onClick={restore}>
{t("Restore")}
</Button>
<Input
display="none"
type="file"
id="restore-file"
onChange={onRestoreFileChange}
/>
<Textarea readOnly rows={23} mt="2" value={log}></Textarea>
</Box>
);
};

export default BackupRestore;
7 changes: 7 additions & 0 deletions src/pages/manage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import admin, { changeToken } from "../../utils/admin";
import Overlay from "../../components/overlay";
import useTitle from "../../hooks/useTitle";
import { IconType } from "react-icons";
import {FaDatabase} from "react-icons/fa";

const Login = lazy(() => import("./login"));
const Settings = lazy(() => import("./settings"));
Expand Down Expand Up @@ -80,6 +81,12 @@ const NavItems: NavItem[] = [
icon: SiMetabase,
component: Metas,
},
{
name: "Backup & Restore",
to: "backup-restore",
icon: FaDatabase,
component: lazy(() => import("./backup-restore")),
}
];

const getAllNavItems = (items: NavItem[], acc: NavItem[] = []) => {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/manage/metas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { useTranslation } from "react-i18next";
import admin from "../../utils/admin";
import FormItem from "../../components/form-item";

interface Meta {
export interface Meta {
id: number;
path: string;
password: string;
Expand Down
2 changes: 1 addition & 1 deletion src/pages/manage/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { useTranslation } from "react-i18next";
import FormItem from "../../components/form-item";
import { useLocation } from "react-router-dom";

interface SettingItem {
export interface SettingItem {
key: string;
value: string;
description: string;
Expand Down
12 changes: 12 additions & 0 deletions src/utils/download-json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
function download(filename: string, data: any) {
const blob = new Blob([JSON.stringify(data,null,2)], {
type: "application/json",
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
export default download;

0 comments on commit 3cb5caf

Please sign in to comment.