mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-19 17:23:57 +00:00
feat: adding i18n for cloud sync
This commit is contained in:
14
src/main/events/cloud-save/check-game-cloud-sync-support.ts
Normal file
14
src/main/events/cloud-save/check-game-cloud-sync-support.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { GameShop } from "@types";
|
||||
import { Ludusavi } from "@main/services";
|
||||
|
||||
const checkGameCloudSyncSupport = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
objectId: string,
|
||||
shop: GameShop
|
||||
) => {
|
||||
const games = await Ludusavi.findGames(shop, objectId);
|
||||
return games.length === 1;
|
||||
};
|
||||
|
||||
registerEvent("checkGameCloudSyncSupport", checkGameCloudSyncSupport);
|
||||
9
src/main/events/cloud-save/delete-game-artifact.ts
Normal file
9
src/main/events/cloud-save/delete-game-artifact.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { HydraApi } from "@main/services";
|
||||
import { registerEvent } from "../register-event";
|
||||
|
||||
const deleteGameArtifact = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
gameArtifactId: string
|
||||
) => HydraApi.delete<{ ok: boolean }>(`/games/artifacts/${gameArtifactId}`);
|
||||
|
||||
registerEvent("deleteGameArtifact", deleteGameArtifact);
|
||||
56
src/main/events/cloud-save/download-game-artifact.ts
Normal file
56
src/main/events/cloud-save/download-game-artifact.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { HydraApi, logger, Ludusavi, WindowManager } from "@main/services";
|
||||
import fs from "node:fs";
|
||||
import AdmZip from "adm-zip";
|
||||
import { registerEvent } from "../register-event";
|
||||
import axios from "axios";
|
||||
import { app } from "electron";
|
||||
import path from "node:path";
|
||||
import { backupsPath } from "@main/constants";
|
||||
import type { GameShop } from "@types";
|
||||
|
||||
const downloadGameArtifact = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
objectId: string,
|
||||
shop: GameShop,
|
||||
gameArtifactId: string
|
||||
) => {
|
||||
const { downloadUrl, objectKey } = await HydraApi.post<{
|
||||
downloadUrl: string;
|
||||
objectKey: string;
|
||||
}>(`/games/artifacts/${gameArtifactId}/download`);
|
||||
|
||||
const response = await axios.get(downloadUrl, {
|
||||
responseType: "stream",
|
||||
});
|
||||
|
||||
const zipLocation = path.join(app.getPath("userData"), objectKey);
|
||||
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
|
||||
|
||||
const writer = fs.createWriteStream(zipLocation);
|
||||
|
||||
response.data.pipe(writer);
|
||||
|
||||
writer.on("error", (err) => {
|
||||
logger.error("Failed to write zip", err);
|
||||
throw err;
|
||||
});
|
||||
|
||||
writer.on("close", () => {
|
||||
const zip = new AdmZip(zipLocation);
|
||||
zip.extractAllToAsync(backupPath, true, true, (err) => {
|
||||
if (err) {
|
||||
logger.error("Failed to extract zip", err);
|
||||
throw err;
|
||||
}
|
||||
|
||||
Ludusavi.restoreBackup(backupPath).then(() => {
|
||||
WindowManager.mainWindow?.webContents.send(
|
||||
`on-download-complete-${objectId}-${shop}`,
|
||||
true
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("downloadGameArtifact", downloadGameArtifact);
|
||||
18
src/main/events/cloud-save/get-game-artifacts.ts
Normal file
18
src/main/events/cloud-save/get-game-artifacts.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { HydraApi } from "@main/services";
|
||||
import { registerEvent } from "../register-event";
|
||||
import type { GameArtifact, GameShop } from "@types";
|
||||
|
||||
const getGameArtifacts = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
objectId: string,
|
||||
shop: GameShop
|
||||
) => {
|
||||
const params = new URLSearchParams({
|
||||
objectId,
|
||||
shop,
|
||||
});
|
||||
|
||||
return HydraApi.get<GameArtifact[]>(`/games/artifacts?${params.toString()}`);
|
||||
};
|
||||
|
||||
registerEvent("getGameArtifacts", getGameArtifacts);
|
||||
17
src/main/events/cloud-save/get-game-backup-preview.ts
Normal file
17
src/main/events/cloud-save/get-game-backup-preview.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { GameShop } from "@types";
|
||||
import { Ludusavi } from "@main/services";
|
||||
import path from "node:path";
|
||||
import { backupsPath } from "@main/constants";
|
||||
|
||||
const getGameBackupPreview = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
objectId: string,
|
||||
shop: GameShop
|
||||
) => {
|
||||
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
|
||||
|
||||
return Ludusavi.getBackupPreview(shop, objectId, backupPath);
|
||||
};
|
||||
|
||||
registerEvent("getGameBackupPreview", getGameBackupPreview);
|
||||
101
src/main/events/cloud-save/upload-save-game.ts
Normal file
101
src/main/events/cloud-save/upload-save-game.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { HydraApi, logger, Ludusavi, WindowManager } from "@main/services";
|
||||
import { registerEvent } from "../register-event";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import archiver from "archiver";
|
||||
import crypto from "node:crypto";
|
||||
import { GameShop } from "@types";
|
||||
import axios from "axios";
|
||||
import os from "node:os";
|
||||
import { app } from "electron";
|
||||
import { backupsPath } from "@main/constants";
|
||||
|
||||
const compressBackupToArtifact = async (
|
||||
shop: GameShop,
|
||||
objectId: string,
|
||||
cb: (zipLocation: string) => void
|
||||
) => {
|
||||
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
|
||||
|
||||
await Ludusavi.backupGame(shop, objectId, backupPath);
|
||||
|
||||
const archive = archiver("zip", {
|
||||
zlib: { level: 9 },
|
||||
});
|
||||
|
||||
const zipLocation = path.join(
|
||||
app.getPath("userData"),
|
||||
`${crypto.randomUUID()}.zip`
|
||||
);
|
||||
|
||||
const output = fs.createWriteStream(zipLocation);
|
||||
|
||||
output.on("close", () => {
|
||||
cb(zipLocation);
|
||||
});
|
||||
|
||||
output.on("error", (err) => {
|
||||
logger.error("Failed to compress folder", err);
|
||||
throw err;
|
||||
});
|
||||
|
||||
archive.pipe(output);
|
||||
|
||||
archive.directory(backupPath, false);
|
||||
archive.finalize();
|
||||
};
|
||||
|
||||
const uploadSaveGame = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
objectId: string,
|
||||
shop: GameShop
|
||||
) => {
|
||||
compressBackupToArtifact(shop, objectId, (zipLocation) => {
|
||||
fs.stat(zipLocation, 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;
|
||||
}>("/games/artifacts", {
|
||||
artifactLengthInBytes: stat.size,
|
||||
shop,
|
||||
objectId,
|
||||
hostname: os.hostname(),
|
||||
});
|
||||
|
||||
fs.readFile(zipLocation, async (err, fileBuffer) => {
|
||||
if (err) {
|
||||
logger.error("Failed to read zip file", err);
|
||||
throw err;
|
||||
}
|
||||
|
||||
axios.put(uploadUrl, fileBuffer, {
|
||||
headers: {
|
||||
"Content-Type": "application/zip",
|
||||
},
|
||||
onUploadProgress: (progressEvent) => {
|
||||
if (progressEvent.progress === 1) {
|
||||
fs.rm(zipLocation, (err) => {
|
||||
if (err) {
|
||||
logger.error("Failed to remove zip file", err);
|
||||
throw err;
|
||||
}
|
||||
|
||||
WindowManager.mainWindow?.webContents.send(
|
||||
`on-upload-complete-${objectId}-${shop}`,
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("uploadSaveGame", uploadSaveGame);
|
||||
Reference in New Issue
Block a user