feat: create SystemPath class to prevent Hydra not opening when some user folders are not correctly set on windows

This commit is contained in:
Zamitto
2025-04-30 12:10:26 -03:00
parent 6068d70aeb
commit f52f0ca614
11 changed files with 79 additions and 23 deletions

View File

@@ -1,23 +1,24 @@
import { app } from "electron";
import path from "node:path";
import { SystemPath } from "./services/system-path";
export const LUDUSAVI_MANIFEST_URL = "https://cdn.losbroxas.org/manifest.yaml";
export const defaultDownloadsPath = app.getPath("downloads");
export const defaultDownloadsPath = SystemPath.getPath("downloads");
export const isStaging = import.meta.env.MAIN_VITE_API_URL.includes("staging");
export const levelDatabasePath = path.join(
app.getPath("userData"),
SystemPath.getPath("userData"),
`hydra-db${isStaging ? "-staging" : ""}`
);
export const commonRedistPath = path.join(
app.getPath("userData"),
SystemPath.getPath("userData"),
"CommonRedist"
);
export const logsPath = path.join(app.getPath("userData"), "logs");
export const logsPath = path.join(SystemPath.getPath("userData"), "logs");
export const seedsPath = app.isPackaged
? path.join(process.resourcesPath, "seeds")
@@ -27,7 +28,7 @@ export const achievementSoundPath = app.isPackaged
? path.join(process.resourcesPath, "achievement.wav")
: path.join(__dirname, "..", "..", "resources", "achievement.wav");
export const backupsPath = path.join(app.getPath("userData"), "Backups");
export const backupsPath = path.join(SystemPath.getPath("userData"), "Backups");
export const appVersion = app.getVersion() + (isStaging ? "-staging" : "");

View File

@@ -4,13 +4,13 @@ import * as tar from "tar";
import { registerEvent } from "../register-event";
import axios from "axios";
import os from "node:os";
import { app } from "electron";
import path from "node:path";
import { backupsPath } from "@main/constants";
import type { GameShop } from "@types";
import YAML from "yaml";
import { normalizePath } from "@main/helpers";
import { SystemPath } from "@main/services/system-path";
export interface LudusaviBackup {
files: {
@@ -35,7 +35,7 @@ const replaceLudusaviBackupWithCurrentUser = (
drives: Record<string, string>;
};
const currentHomeDir = normalizePath(app.getPath("home"));
const currentHomeDir = normalizePath(SystemPath.getPath("home"));
/* Renaming logic */
if (os.platform() === "win32") {
@@ -84,7 +84,7 @@ const downloadGameArtifact = async (
homeDir: string;
}>(`/profile/games/artifacts/${gameArtifactId}/download`);
const zipLocation = path.join(app.getPath("userData"), objectKey);
const zipLocation = path.join(SystemPath.getPath("userData"), objectKey);
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
if (fs.existsSync(backupPath)) {

View File

@@ -5,6 +5,7 @@ import { app } from "electron";
import { removeSymbolsFromName } from "@shared";
import { GameShop } from "@types";
import { gamesSublevel, levelKeys } from "@main/level";
import { SystemPath } from "@main/services/system-path";
const createGameShortcut = async (
_event: Electron.IpcMainInvokeEvent,
@@ -24,7 +25,7 @@ const createGameShortcut = async (
const options = {
filePath,
name: removeSymbolsFromName(game.title),
outputPath: app.getPath("desktop"),
outputPath: SystemPath.getPath("desktop"),
};
return createDesktopShortcut({

View File

@@ -4,9 +4,10 @@ import { app } from "electron";
import path from "path";
import fs from "node:fs";
import { logger } from "@main/services";
import { SystemPath } from "@main/services/system-path";
const windowsStartupPath = path.join(
app.getPath("appData"),
SystemPath.getPath("appData"),
"Microsoft",
"Windows",
"Start Menu",

View File

@@ -9,8 +9,11 @@ import { levelKeys, db } from "./level";
import type { UserPreferences } from "@types";
import { TorBoxClient } from "./services/download/torbox";
import { CommonRedistManager } from "./services/common-redist-manager";
import { SystemPath } from "./services/system-path";
export const loadState = async () => {
SystemPath.checkIfPathsAreAvailable();
const userPreferences = await db.get<string, UserPreferences | null>(
levelKeys.userPreferences,
{

View File

@@ -1,26 +1,26 @@
import path from "node:path";
import fs from "node:fs";
import { app } from "electron";
import type { Game, AchievementFile } from "@types";
import { Cracker } from "@shared";
import { achievementsLogger } from "../logger";
import { SystemPath } from "../system-path";
const getAppDataPath = () => {
if (process.platform === "win32") {
return app.getPath("appData");
return SystemPath.getPath("appData");
}
const user = app.getPath("home").split("/").pop();
const user = SystemPath.getPath("home").split("/").pop();
return path.join("drive_c", "users", user || "", "AppData", "Roaming");
};
const getDocumentsPath = () => {
if (process.platform === "win32") {
return app.getPath("documents");
return SystemPath.getPath("documents");
}
const user = app.getPath("home").split("/").pop();
const user = SystemPath.getPath("home").split("/").pop();
return path.join("drive_c", "users", user || "", "Documents");
};
@@ -38,7 +38,7 @@ const getLocalAppDataPath = () => {
return path.join(appData, "..", "Local");
}
const user = app.getPath("home").split("/").pop();
const user = SystemPath.getPath("home").split("/").pop();
return path.join("drive_c", "users", user || "", "AppData", "Local");
};

View File

@@ -1,5 +1,4 @@
import { levelKeys, gamesSublevel, db } from "@main/level";
import { app } from "electron";
import path from "node:path";
import * as tar from "tar";
import crypto from "node:crypto";
@@ -15,6 +14,7 @@ import axios from "axios";
import { Ludusavi } from "./ludusavi";
import { formatDate, SubscriptionRequiredError } from "@shared";
import i18next, { t } from "i18next";
import { SystemPath } from "./system-path";
export class CloudSync {
public static getBackupLabel(automatic: boolean) {
@@ -102,7 +102,7 @@ export class CloudSync {
shop,
objectId,
hostname: os.hostname(),
homeDir: normalizePath(app.getPath("home")),
homeDir: normalizePath(SystemPath.getPath("home")),
downloadOptionTitle,
platform: os.platform(),
label,

View File

@@ -4,8 +4,8 @@ import fs from "node:fs";
import cp from "node:child_process";
import path from "node:path";
import { logger } from "./logger";
import { app } from "electron";
import { WindowManager } from "./window-manager";
import { SystemPath } from "./system-path";
export class CommonRedistManager {
private static readonly redistributables = [
@@ -18,7 +18,7 @@ export class CommonRedistManager {
];
private static readonly installationTimeout = 1000 * 60 * 5; // 5 minutes
private static readonly installationLog = path.join(
app.getPath("temp"),
SystemPath.getPath("temp"),
"common_redist_install.log"
);

View File

@@ -8,9 +8,13 @@ import YAML from "yaml";
import ludusaviWorkerPath from "../workers/ludusavi.worker?modulePath";
import { LUDUSAVI_MANIFEST_URL } from "@main/constants";
import { SystemPath } from "./system-path";
export class Ludusavi {
private static ludusaviPath = path.join(app.getPath("appData"), "ludusavi");
private static ludusaviPath = path.join(
SystemPath.getPath("appData"),
"ludusavi"
);
private static ludusaviConfigPath = path.join(
this.ludusaviPath,
"config.yaml"

View File

@@ -1,4 +1,4 @@
import { Notification, app } from "electron";
import { Notification } from "electron";
import { t } from "i18next";
import trayIcon from "@resources/tray-icon.png?asset";
import fs from "node:fs";
@@ -13,13 +13,14 @@ import { WindowManager } from "../window-manager";
import type { Game, UserPreferences } from "@types";
import { db, levelKeys } from "@main/level";
import { restartAndInstallUpdate } from "@main/events/autoupdater/restart-and-install-update";
import { SystemPath } from "../system-path";
async function downloadImage(url: string | null) {
if (!url) return undefined;
if (!url.startsWith("http")) return undefined;
const fileName = url.split("/").pop()!;
const outputPath = path.join(app.getPath("temp"), fileName);
const outputPath = path.join(SystemPath.getPath("temp"), fileName);
const writer = fs.createWriteStream(outputPath);
const response = await axios.get(url, {

View File

@@ -0,0 +1,45 @@
import { app, dialog } from "electron";
import { logger } from "./logger";
export class SystemPath {
static readonly paths = {
userData: "userData",
downloads: "downloads",
documents: "documents",
desktop: "desktop",
home: "home",
appData: "appData",
temp: "temp",
};
static checkIfPathsAreAvailable() {
const paths = Object.keys(SystemPath.paths) as Array<
keyof typeof SystemPath.paths
>;
paths.forEach((pathName) => {
try {
app.getPath(pathName);
} catch (error) {
logger.error(`Error getting path ${pathName}`);
if (error instanceof Error) {
logger.error(error.message, error.stack);
}
dialog.showErrorBox(
`Hydra was not able to find path for '${pathName}' system folder`,
`Some functionalities may not work as expected.\nPlease check your system settings.`
);
}
});
}
static getPath(pathName: keyof typeof SystemPath.paths): string {
try {
return app.getPath(pathName);
} catch (error) {
logger.error(`Error getting path: ${error}`);
return "";
}
}
}