mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-13 09:28:14 +08:00
@@ -4,6 +4,10 @@
|
|||||||
* @since Beta v0.6.0
|
* @since Beta v0.6.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { path } from "@tauri-apps/api";
|
||||||
|
import { exists, mkdir, readTextFile, writeTextFile } from "@tauri-apps/plugin-fs";
|
||||||
|
|
||||||
|
import TGLogger from "../../../utils/TGLogger.js";
|
||||||
import { timestampToDate } from "../../../utils/toolFunc.js";
|
import { timestampToDate } from "../../../utils/toolFunc.js";
|
||||||
import TGSqlite from "../index.js";
|
import TGSqlite from "../index.js";
|
||||||
|
|
||||||
@@ -120,6 +124,51 @@ async function saveAccount(data: TGApp.App.Account.User): Promise<void> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 备份用户数据
|
||||||
|
* @since Beta v0.6.0
|
||||||
|
* @param {string} dir - 备份目录
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async function backUpAccount(dir: string): Promise<void> {
|
||||||
|
if (!(await exists(dir))) {
|
||||||
|
await TGLogger.Warn("不存在指定的账户备份目录,即将创建");
|
||||||
|
await mkdir(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
const accounts = await getAllAccount();
|
||||||
|
await writeTextFile(`${dir}${path.sep()}accounts.json`, JSON.stringify(accounts, null, 2));
|
||||||
|
await TGLogger.Info("账户数据备份完成");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 恢复用户数据
|
||||||
|
* @since Beta v0.6.0
|
||||||
|
* @param {string} dir
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
async function restoreAccount(dir: string): Promise<boolean> {
|
||||||
|
if (!(await exists(dir))) {
|
||||||
|
await TGLogger.Warn("不存在指定的账户备份目录");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const filePath = `${dir}${path.sep()}accounts.json`;
|
||||||
|
if (!(await exists(filePath))) {
|
||||||
|
await TGLogger.Warn("不存在指定的账户备份文件");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const data = await readTextFile(filePath);
|
||||||
|
const accounts: TGApp.App.Account.User[] = JSON.parse(data);
|
||||||
|
for (const account of accounts) {
|
||||||
|
await saveAccount(account);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
await TGLogger.Error(`[UserAccount][restoreAccount] ${e}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 复制cookie
|
* @description 复制cookie
|
||||||
* @since Beta v0.6.0
|
* @since Beta v0.6.0
|
||||||
@@ -236,6 +285,8 @@ const TSUserAccount = {
|
|||||||
saveAccount,
|
saveAccount,
|
||||||
copy: copyCookie,
|
copy: copyCookie,
|
||||||
deleteAccount,
|
deleteAccount,
|
||||||
|
backup: backUpAccount,
|
||||||
|
restore: restoreAccount,
|
||||||
},
|
},
|
||||||
game: {
|
game: {
|
||||||
getAccount: getGameAccount,
|
getAccount: getGameAccount,
|
||||||
|
|||||||
@@ -306,22 +306,23 @@ async function backupUiaf(dir: string, uid?: number): Promise<void> {
|
|||||||
async function restoreUiaf(dir: string): Promise<boolean> {
|
async function restoreUiaf(dir: string): Promise<boolean> {
|
||||||
if (!(await exists(dir))) return false;
|
if (!(await exists(dir))) return false;
|
||||||
const filesRead = await readDir(dir);
|
const filesRead = await readDir(dir);
|
||||||
const files = filesRead.filter((item) => item.name.includes("UIAF_") && item.isFile);
|
// 校验 UIAF_xxx.json 文件
|
||||||
|
const fileRegex = /^UIAF_\d+\.json$/;
|
||||||
|
const files = filesRead.filter((item) => item.isFile && fileRegex.test(item.name));
|
||||||
|
if (files.length === 0) return false;
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
try {
|
try {
|
||||||
// todo 完善正则判断
|
const uid = parseInt(file.name.replace("UIAF_", "").replace(".json", ""));
|
||||||
const reg = /UIAF_(\d+).json/;
|
|
||||||
if (!file.name.match(reg)) return false;
|
|
||||||
const uid: number = Number(file.name.match(reg)![0]);
|
|
||||||
const filePath = `${dir}${path.sep()}${file.name}`;
|
const filePath = `${dir}${path.sep()}${file.name}`;
|
||||||
const data: TGApp.Plugins.UIAF.Achievement[] = JSON.parse(await readTextFile(filePath));
|
const data: TGApp.Plugins.UIAF.Achievement[] = JSON.parse(await readTextFile(filePath));
|
||||||
await TSUserAchi.mergeUiaf(data, uid);
|
await TSUserAchi.mergeUiaf(data, uid);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await TGLogger.Error(`[UIAF][RESTORE] 恢复成就数据${file.name} `);
|
await TGLogger.Error(`[UIAF][RESTORE] 恢复成就数据${file.name} `);
|
||||||
await TGLogger.Error(`${e}`);
|
await TGLogger.Error(`${e}`);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,7 +4,12 @@
|
|||||||
* @since Beta v0.6.0
|
* @since Beta v0.6.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { path } from "@tauri-apps/api";
|
||||||
|
import { exists, mkdir, readDir } from "@tauri-apps/plugin-fs";
|
||||||
|
|
||||||
import { AppCharacterData, AppWeaponData } from "../../../data/index.js";
|
import { AppCharacterData, AppWeaponData } from "../../../data/index.js";
|
||||||
|
import TGLogger from "../../../utils/TGLogger.js";
|
||||||
|
import { exportUigfData, readUigfData, verifyUigfData } from "../../../utils/UIGF.js";
|
||||||
import TGSqlite from "../index.js";
|
import TGSqlite from "../index.js";
|
||||||
|
|
||||||
type gachaItemTypeRes =
|
type gachaItemTypeRes =
|
||||||
@@ -177,6 +182,62 @@ async function mergeUIGF4(data: TGApp.Plugins.UIGF.GachaHk4e): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 备份祈愿数据
|
||||||
|
* @since Beta v0.6.0
|
||||||
|
* @param {string} dir - 备份目录
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async function backUpUigf(dir: string): Promise<void> {
|
||||||
|
if (!(await exists(dir))) {
|
||||||
|
await TGLogger.Warn("不存在指定的祈愿备份目录,即将创建");
|
||||||
|
await mkdir(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
const uidList = await getUidList();
|
||||||
|
for (const uid of uidList) {
|
||||||
|
const dataGacha = await getGachaRecords(uid);
|
||||||
|
const savePath = `${dir}${path.sep()}UIGF_${uid}.json`;
|
||||||
|
await exportUigfData(uid, dataGacha, savePath);
|
||||||
|
}
|
||||||
|
await TGLogger.Info("祈愿数据备份完成");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 恢复祈愿数据
|
||||||
|
* @since Beta v0.6.0
|
||||||
|
* @param {string} dir - 备份目录
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
async function restoreUigf(dir: string): Promise<boolean> {
|
||||||
|
if (!(await exists(dir))) {
|
||||||
|
await TGLogger.Warn("不存在指定的祈愿备份目录");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const filesRead = await readDir(dir);
|
||||||
|
// 校验 UIGF_xxx.json 文件
|
||||||
|
const fileRegex = /^UIGF_\d+\.json$/;
|
||||||
|
const files = filesRead.filter((item) => item.isFile && fileRegex.test(item.name));
|
||||||
|
if (files.length === 0) return false;
|
||||||
|
try {
|
||||||
|
for (const file of files) {
|
||||||
|
const filePath = `${dir}${path.sep()}${file.name}`;
|
||||||
|
const check = await verifyUigfData(filePath);
|
||||||
|
if (!check) {
|
||||||
|
await TGLogger.Warn(`UIGF数据校验失败${filePath}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const data = await readUigfData(filePath);
|
||||||
|
const uid = data.info.uid;
|
||||||
|
await mergeUIGF(uid, data.list);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
await TGLogger.Error(`恢复祈愿数据失败${dir}`);
|
||||||
|
await TGLogger.Error(typeof e === "string" ? e : JSON.stringify(e));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const TSUserGacha = {
|
const TSUserGacha = {
|
||||||
getUidList,
|
getUidList,
|
||||||
getGachaCheck,
|
getGachaCheck,
|
||||||
@@ -185,6 +246,8 @@ const TSUserGacha = {
|
|||||||
deleteGachaRecords,
|
deleteGachaRecords,
|
||||||
mergeUIGF,
|
mergeUIGF,
|
||||||
mergeUIGF4,
|
mergeUIGF4,
|
||||||
|
backUpUigf,
|
||||||
|
restoreUigf,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TSUserGacha;
|
export default TSUserGacha;
|
||||||
|
|||||||
@@ -3,18 +3,15 @@
|
|||||||
* @description 用户数据的备份、恢复、迁移
|
* @description 用户数据的备份、恢复、迁移
|
||||||
* @since Beta v0.6.0
|
* @since Beta v0.6.0
|
||||||
*/
|
*/
|
||||||
|
import { exists, mkdir } from "@tauri-apps/plugin-fs";
|
||||||
import { path } from "@tauri-apps/api";
|
|
||||||
import { exists, mkdir, writeTextFile, readDir, readTextFile } from "@tauri-apps/plugin-fs";
|
|
||||||
|
|
||||||
import showSnackbar from "../components/func/snackbar.js";
|
import showSnackbar from "../components/func/snackbar.js";
|
||||||
import TGSqlite from "../plugins/Sqlite/index.js";
|
|
||||||
import TSUserAbyss from "../plugins/Sqlite/modules/userAbyss.js";
|
import TSUserAbyss from "../plugins/Sqlite/modules/userAbyss.js";
|
||||||
|
import TSUserAccount from "../plugins/Sqlite/modules/userAccount.js";
|
||||||
import TSUserAchi from "../plugins/Sqlite/modules/userAchi.js";
|
import TSUserAchi from "../plugins/Sqlite/modules/userAchi.js";
|
||||||
import TSUserGacha from "../plugins/Sqlite/modules/userGacha.js";
|
import TSUserGacha from "../plugins/Sqlite/modules/userGacha.js";
|
||||||
|
|
||||||
import TGLogger from "./TGLogger.js";
|
import TGLogger from "./TGLogger.js";
|
||||||
import { exportUigfData, readUigfData, verifyUigfData } from "./UIGF.js";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 备份用户数据
|
* @description 备份用户数据
|
||||||
@@ -24,23 +21,13 @@ import { exportUigfData, readUigfData, verifyUigfData } from "./UIGF.js";
|
|||||||
*/
|
*/
|
||||||
export async function backUpUserData(dir: string): Promise<void> {
|
export async function backUpUserData(dir: string): Promise<void> {
|
||||||
if (!(await exists(dir))) {
|
if (!(await exists(dir))) {
|
||||||
console.log("备份目录不存在,创建备份目录");
|
await TGLogger.Warn("备份数据不存在,即将创建");
|
||||||
await mkdir(dir, { recursive: true });
|
await mkdir(dir, { recursive: true });
|
||||||
}
|
}
|
||||||
// 备份成就
|
|
||||||
await TSUserAchi.backupUiaf(dir);
|
await TSUserAchi.backupUiaf(dir);
|
||||||
// 备份 ck
|
await TSUserAccount.account.backup(dir);
|
||||||
const dataCK = await TGSqlite.getCookie();
|
|
||||||
await writeTextFile(`${dir}${path.sep()}cookie.json`, JSON.stringify(dataCK));
|
|
||||||
// 备份深渊数据
|
|
||||||
await TSUserAbyss.backupAbyss(dir);
|
await TSUserAbyss.backupAbyss(dir);
|
||||||
// 备份祈愿数据
|
await TSUserGacha.backUpUigf(dir);
|
||||||
const uidList = await TSUserGacha.getUidList();
|
|
||||||
for (const uid of uidList) {
|
|
||||||
const dataGacha = await TSUserGacha.getGachaRecords(uid);
|
|
||||||
const savePath = `${dir}${path.sep()}UIGF_${uid}.json`;
|
|
||||||
await exportUigfData(uid, dataGacha, savePath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,56 +42,25 @@ export async function restoreUserData(dir: string): Promise<void> {
|
|||||||
showSnackbar({ text: "备份目录不存在", color: "error" });
|
showSnackbar({ text: "备份目录不存在", color: "error" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const filesRead = await readDir(dir);
|
|
||||||
const files = filesRead.filter((item) => item.isFile && item.name.endsWith(".json"));
|
|
||||||
await TGLogger.Info(`[DataBS][restoreUserData] files: ${JSON.stringify(files)}`);
|
|
||||||
const restoreAchi = await TSUserAchi.restoreUiaf(dir);
|
const restoreAchi = await TSUserAchi.restoreUiaf(dir);
|
||||||
if (!restoreAchi) {
|
if (!restoreAchi) {
|
||||||
showSnackbar({ text: `成就数据恢复失败`, color: "error" });
|
showSnackbar({ text: `成就数据恢复失败`, color: "error" });
|
||||||
errNum++;
|
errNum++;
|
||||||
}
|
}
|
||||||
// 恢复 ck
|
const restoreAccount = await TSUserAccount.account.restore(dir);
|
||||||
const ckFind = files.find((item) => item.name === "cookie.json");
|
if (!restoreAccount) {
|
||||||
if (ckFind) {
|
showSnackbar({ text: "Cookie 数据恢复失败", color: "error" });
|
||||||
try {
|
errNum++;
|
||||||
const dataCK = await readTextFile(ckFind.name);
|
|
||||||
await TGSqlite.saveAppData("cookie", JSON.stringify(JSON.parse(dataCK)));
|
|
||||||
await TGLogger.Info(`[DataBS][restoreUserData] Cookie 数据恢复成功`);
|
|
||||||
} catch (e) {
|
|
||||||
showSnackbar({ text: `Cookie 数据恢复失败 ${e}`, color: "error" });
|
|
||||||
await TGLogger.Error(`[DataBS][restoreUserData] Cookie 数据恢复失败 ${e}`);
|
|
||||||
errNum++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showSnackbar({ text: "Cookie 数据恢复失败,备份文件不存在", color: "warn" });
|
|
||||||
await TGLogger.Warn(`[DataBS][restoreUserData] 未检测到 Cookie 数据备份文件`);
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
||||||
}
|
}
|
||||||
// 恢复深渊数据
|
|
||||||
const restoreAbyss = await TSUserAbyss.restoreAbyss(dir);
|
const restoreAbyss = await TSUserAbyss.restoreAbyss(dir);
|
||||||
if (!restoreAbyss) {
|
if (!restoreAbyss) {
|
||||||
showSnackbar({ text: "深渊数据恢复失败", color: "error" });
|
showSnackbar({ text: "深渊数据恢复失败", color: "error" });
|
||||||
errNum++;
|
errNum++;
|
||||||
}
|
}
|
||||||
// 恢复祈愿数据
|
const restoreGacha = await TSUserGacha.restoreUigf(dir);
|
||||||
const reg = /UIGF_(\d+).json/;
|
if (!restoreGacha) {
|
||||||
const dataGachaList = files.filter((item) => reg.test(item.name));
|
showSnackbar({ text: "祈愿数据恢复失败", color: "error" });
|
||||||
for (const item of dataGachaList) {
|
errNum++;
|
||||||
const check = await verifyUigfData(item.name);
|
|
||||||
if (!check) {
|
|
||||||
errNum++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const data = await readUigfData(item.name);
|
|
||||||
const uid = data.info.uid;
|
|
||||||
try {
|
|
||||||
await TSUserGacha.mergeUIGF(uid, data.list);
|
|
||||||
await TGLogger.Info(`[DataBS][restoreUserData] UID: ${uid} 祈愿数据恢复成功`);
|
|
||||||
} catch (e) {
|
|
||||||
showSnackbar({ text: `UID: ${uid} 祈愿数据恢复失败`, color: "error" });
|
|
||||||
await TGLogger.Error(`[DataBS][restoreUserData] UID: ${uid} 祈愿数据恢复失败 ${e}`);
|
|
||||||
errNum++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (errNum === 0) {
|
if (errNum === 0) {
|
||||||
showSnackbar({ text: "数据恢复成功", color: "success" });
|
showSnackbar({ text: "数据恢复成功", color: "success" });
|
||||||
|
|||||||
Reference in New Issue
Block a user