mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-27 04:41:03 +00:00
feat: adding automatic cloud sync
This commit is contained in:
@@ -1,44 +1,8 @@
|
||||
import { HydraApi, logger, Ludusavi, WindowManager } from "@main/services";
|
||||
import { CloudSync } from "@main/services";
|
||||
import { registerEvent } from "../register-event";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import * as tar from "tar";
|
||||
import crypto from "node:crypto";
|
||||
import type { GameShop } from "@types";
|
||||
import axios from "axios";
|
||||
import os from "node:os";
|
||||
import { backupsPath } from "@main/constants";
|
||||
import { app } from "electron";
|
||||
import { normalizePath } from "@main/helpers";
|
||||
import { gamesSublevel, levelKeys } from "@main/level";
|
||||
|
||||
const bundleBackup = async (
|
||||
shop: GameShop,
|
||||
objectId: string,
|
||||
winePrefix: string | null
|
||||
) => {
|
||||
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
|
||||
|
||||
// Remove existing backup
|
||||
if (fs.existsSync(backupPath)) {
|
||||
fs.rmSync(backupPath, { recursive: true });
|
||||
}
|
||||
|
||||
await Ludusavi.backupGame(shop, objectId, backupPath, winePrefix);
|
||||
|
||||
const tarLocation = path.join(backupsPath, `${crypto.randomUUID()}.tar`);
|
||||
|
||||
await tar.create(
|
||||
{
|
||||
gzip: false,
|
||||
file: tarLocation,
|
||||
cwd: backupPath,
|
||||
},
|
||||
["."]
|
||||
);
|
||||
|
||||
return tarLocation;
|
||||
};
|
||||
import { t } from "i18next";
|
||||
import { format } from "date-fns";
|
||||
|
||||
const uploadSaveGame = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
@@ -46,61 +10,15 @@ const uploadSaveGame = async (
|
||||
shop: GameShop,
|
||||
downloadOptionTitle: string | null
|
||||
) => {
|
||||
const game = await gamesSublevel.get(levelKeys.game(shop, objectId));
|
||||
|
||||
const bundleLocation = await bundleBackup(
|
||||
shop,
|
||||
return CloudSync.uploadSaveGame(
|
||||
objectId,
|
||||
game?.winePrefixPath ?? null
|
||||
shop,
|
||||
downloadOptionTitle,
|
||||
t("backup_from", {
|
||||
ns: "game_details",
|
||||
date: format(new Date(), "dd/MM/yyyy"),
|
||||
})
|
||||
);
|
||||
|
||||
fs.stat(bundleLocation, async (err, stat) => {
|
||||
if (err) {
|
||||
logger.error("Failed to get zip file stats", err);
|
||||
throw err;
|
||||
}
|
||||
|
||||
const { uploadUrl } = await HydraApi.post<{
|
||||
id: string;
|
||||
uploadUrl: string;
|
||||
}>("/profile/games/artifacts", {
|
||||
artifactLengthInBytes: stat.size,
|
||||
shop,
|
||||
objectId,
|
||||
hostname: os.hostname(),
|
||||
homeDir: normalizePath(app.getPath("home")),
|
||||
downloadOptionTitle,
|
||||
platform: os.platform(),
|
||||
});
|
||||
|
||||
fs.readFile(bundleLocation, async (err, fileBuffer) => {
|
||||
if (err) {
|
||||
logger.error("Failed to read zip file", err);
|
||||
throw err;
|
||||
}
|
||||
|
||||
await axios.put(uploadUrl, fileBuffer, {
|
||||
headers: {
|
||||
"Content-Type": "application/tar",
|
||||
},
|
||||
onUploadProgress: (progressEvent) => {
|
||||
logger.log(progressEvent);
|
||||
},
|
||||
});
|
||||
|
||||
WindowManager.mainWindow?.webContents.send(
|
||||
`on-upload-complete-${objectId}-${shop}`,
|
||||
true
|
||||
);
|
||||
|
||||
fs.rm(bundleLocation, (err) => {
|
||||
if (err) {
|
||||
logger.error("Failed to remove tar file", err);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("uploadSaveGame", uploadSaveGame);
|
||||
|
||||
@@ -88,6 +88,8 @@ import "./themes/get-active-custom-theme";
|
||||
import "./themes/close-editor-window";
|
||||
import "./themes/toggle-custom-theme";
|
||||
import "./misc/get-badges";
|
||||
import "./user/get-auth";
|
||||
import "./library/toggle-automatic-cloud-sync";
|
||||
import { isPortableVersion } from "@main/helpers";
|
||||
|
||||
ipcMain.handle("ping", () => "pong");
|
||||
|
||||
23
src/main/events/library/toggle-automatic-cloud-sync.ts
Normal file
23
src/main/events/library/toggle-automatic-cloud-sync.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { levelKeys, gamesSublevel } from "@main/level";
|
||||
import type { GameShop } from "@types";
|
||||
|
||||
const toggleAutomaticCloudSync = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
shop: GameShop,
|
||||
objectId: string,
|
||||
automaticCloudSync: boolean
|
||||
) => {
|
||||
const gameKey = levelKeys.game(shop, objectId);
|
||||
|
||||
const game = await gamesSublevel.get(gameKey);
|
||||
|
||||
if (!game) return;
|
||||
|
||||
await gamesSublevel.put(gameKey, {
|
||||
...game,
|
||||
automaticCloudSync,
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("toggleAutomaticCloudSync", toggleAutomaticCloudSync);
|
||||
11
src/main/events/user/get-auth.ts
Normal file
11
src/main/events/user/get-auth.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { Auth } from "@types";
|
||||
import { registerEvent } from "../register-event";
|
||||
|
||||
import { db, levelKeys } from "@main/level";
|
||||
|
||||
const getAuth = async (_event: Electron.IpcMainInvokeEvent) =>
|
||||
db.get<string, Auth>(levelKeys.auth, {
|
||||
valueEncoding: "json",
|
||||
});
|
||||
|
||||
registerEvent("getAuth", getAuth);
|
||||
Reference in New Issue
Block a user