feat: adding change hero

This commit is contained in:
Chubby Granny Chaser
2024-10-05 02:21:41 +01:00
parent 586df616e8
commit 035e424a76
48 changed files with 520 additions and 500 deletions

View File

@@ -4,6 +4,9 @@ import { registerEvent } from "../register-event";
const deleteGameArtifact = async (
_event: Electron.IpcMainInvokeEvent,
gameArtifactId: string
) => HydraApi.delete<{ ok: boolean }>(`/games/artifacts/${gameArtifactId}`);
) =>
HydraApi.delete<{ ok: boolean }>(
`/profile/games/artifacts/${gameArtifactId}`
);
registerEvent("deleteGameArtifact", deleteGameArtifact);

View File

@@ -1,6 +1,6 @@
import { HydraApi, logger, Ludusavi, WindowManager } from "@main/services";
import fs from "node:fs";
import AdmZip from "adm-zip";
import * as tar from "tar";
import { registerEvent } from "../register-event";
import axios from "axios";
import { app } from "electron";
@@ -8,16 +8,54 @@ import path from "node:path";
import { backupsPath } from "@main/constants";
import type { GameShop } from "@types";
import YAML from "yaml";
export interface LudusaviBackup {
files: {
[key: string]: {
hash: string;
size: number;
};
};
}
const replaceLudusaviBackupWithCurrentUser = (
mappingPath: string,
backupHomeDir: string
) => {
const data = fs.readFileSync(mappingPath, "utf8");
const manifest = YAML.parse(data);
const currentHomeDir = app.getPath("home");
const backups = manifest.backups.map((backup: LudusaviBackup) => {
const files = Object.entries(backup.files).reduce((prev, [key, value]) => {
return {
...prev,
[key.replace(backupHomeDir, currentHomeDir)]: value,
};
}, {});
return {
...backup,
files,
};
});
fs.writeFileSync(mappingPath, YAML.stringify({ ...manifest, backups }));
};
const downloadGameArtifact = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string,
shop: GameShop,
gameArtifactId: string
) => {
const { downloadUrl, objectKey } = await HydraApi.post<{
const { downloadUrl, objectKey, homeDir } = await HydraApi.post<{
downloadUrl: string;
objectKey: string;
}>(`/games/artifacts/${gameArtifactId}/download`);
homeDir: string;
}>(`/profile/games/artifacts/${gameArtifactId}/download`);
const zipLocation = path.join(app.getPath("userData"), objectKey);
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
@@ -42,20 +80,31 @@ const downloadGameArtifact = async (
});
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;
}
tar
.x({
file: zipLocation,
cwd: backupPath,
})
.then(async () => {
const [game] = await Ludusavi.findGames(shop, objectId);
if (!game) throw new Error("Game not found in Ludusavi manifest");
Ludusavi.restoreBackup(backupPath).then(() => {
WindowManager.mainWindow?.webContents.send(
`on-backup-download-complete-${objectId}-${shop}`,
true
const mappingPath = path.join(
backupsPath,
`${shop}-${objectId}`,
game,
"mapping.yaml"
);
replaceLudusaviBackupWithCurrentUser(mappingPath, homeDir);
Ludusavi.restoreBackup(backupPath).then(() => {
WindowManager.mainWindow?.webContents.send(
`on-backup-download-complete-${objectId}-${shop}`,
true
);
});
});
});
});
};

View File

@@ -12,7 +12,9 @@ const getGameArtifacts = async (
shop,
});
return HydraApi.get<GameArtifact[]>(`/games/artifacts?${params.toString()}`);
return HydraApi.get<GameArtifact[]>(
`/profile/games/artifacts?${params.toString()}`
);
};
registerEvent("getGameArtifacts", getGameArtifacts);

View File

@@ -2,47 +2,31 @@ 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 * as tar from "tar";
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";
import { app } from "electron";
const compressBackupToArtifact = async (
shop: GameShop,
objectId: string,
cb: (zipLocation: string) => void
) => {
const bundleBackup = async (shop: GameShop, objectId: string) => {
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
await Ludusavi.backupGame(shop, objectId, backupPath);
const archive = archiver("zip", {
zlib: { level: 9 },
});
const tarLocation = path.join(backupsPath, `${crypto.randomUUID()}.zip`);
const zipLocation = path.join(
app.getPath("userData"),
`${crypto.randomUUID()}.zip`
await tar.create(
{
gzip: false,
file: tarLocation,
cwd: backupPath,
},
["."]
);
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();
return tarLocation;
};
const uploadSaveGame = async (
@@ -50,49 +34,51 @@ const uploadSaveGame = async (
objectId: string,
shop: GameShop
) => {
compressBackupToArtifact(shop, objectId, (zipLocation) => {
fs.stat(zipLocation, async (err, stat) => {
const bundleLocation = await bundleBackup(shop, objectId);
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: app.getPath("home"),
platform: os.platform(),
});
fs.readFile(bundleLocation, async (err, fileBuffer) => {
if (err) {
logger.error("Failed to get zip file stats", err);
logger.error("Failed to read zip file", err);
throw err;
}
const { uploadUrl } = await HydraApi.post<{
id: string;
uploadUrl: string;
}>("/games/artifacts", {
artifactLengthInBytes: stat.size,
shop,
objectId,
hostname: os.hostname(),
await axios.put(uploadUrl, fileBuffer, {
headers: {
"Content-Type": "application/tar",
},
onUploadProgress: (progressEvent) => {
console.log(progressEvent);
},
});
fs.readFile(zipLocation, async (err, fileBuffer) => {
WindowManager.mainWindow?.webContents.send(
`on-upload-complete-${objectId}-${shop}`,
true
);
fs.rm(bundleLocation, (err) => {
if (err) {
logger.error("Failed to read zip file", err);
logger.error("Failed to remove tar file", err);
throw err;
}
await axios.put(uploadUrl, fileBuffer, {
headers: {
"Content-Type": "application/zip",
},
onUploadProgress: (progressEvent) => {
console.log(progressEvent);
},
});
WindowManager.mainWindow?.webContents.send(
`on-upload-complete-${objectId}-${shop}`,
true
);
fs.rm(zipLocation, (err) => {
if (err) {
logger.error("Failed to remove zip file", err);
throw err;
}
});
});
});
});