Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: changing permission verify strategy #1374

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
31feb4a
feat: changing permission verify strategy
thegrannychaseroperation Jan 2, 2025
1cfe3dc
Merge branch 'main' into feature/check-directory-permission
thegrannychaseroperation Jan 2, 2025
1f42ebd
fix: ensure ipcHandles are loaded before create window
zamitto Jan 2, 2025
71decd9
feat: show featurebase dropdown changelog on bottom panel version
zamitto Jan 3, 2025
99364df
Merge branch 'main' into feature/check-directory-permission
zamitto Jan 3, 2025
0adcc73
feat: add optional game params back
zamitto Jan 5, 2025
74d4975
feat: handle open new browser window on link
zamitto Jan 5, 2025
9bb89a1
feat: remove unnecessary userPreferences findOne
zamitto Jan 5, 2025
47f7731
feat: prefer to play achievement sound in browser window if available
zamitto Jan 5, 2025
715c4a6
feat: update user language
zamitto Jan 5, 2025
92e641e
feat: sonar suggestions
zamitto Jan 5, 2025
097aff1
feat: delete hydra.db if migrations are corrupted
zamitto Jan 5, 2025
f682c56
feat: use fs already imported
zamitto Jan 5, 2025
fb66557
feat: reduce python log level
zamitto Jan 5, 2025
40552cb
Merge branch 'main' into feature/check-directory-permission
zamitto Jan 5, 2025
540fd80
feat: add featureBaseJwt to type
zamitto Jan 5, 2025
5ac0c04
fix: error handling
zamitto Jan 6, 2025
1d8a3c4
feat: undo knex error handling
zamitto Jan 6, 2025
1b4f962
feat: updating title bar z-index
thegrannychaseroperation Jan 6, 2025
abf9d9b
Merge branch 'feature/check-directory-permission' of github.com:hydra…
thegrannychaseroperation Jan 6, 2025
3f8a440
feat: adjust python log file
zamitto Jan 7, 2025
33e1de7
feat: check for updates every 60 minutes
zamitto Jan 7, 2025
16a3b1e
Merge branch 'main' into feature/check-directory-permission
zamitto Jan 7, 2025
8ef2377
feat: i18n
zamitto Jan 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion python_rpc/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
from http_downloader import HttpDownloader
from profile_image_processor import ProfileImageProcessor
import libtorrent as lt
import logging

log = logging.getLogger('werkzeug')

log.setLevel(logging.ERROR)

app = Flask(__name__)

Expand Down Expand Up @@ -94,7 +99,7 @@ def seed_status():

@app.route("/healthcheck", methods=["GET"])
def healthcheck():
return "", 200
return "ok", 200

@app.route("/process-list", methods=["GET"])
def process_list():
Expand Down
20 changes: 13 additions & 7 deletions src/main/events/hardware/check-folder-write-permission.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import fs from "node:fs";
import path from "node:path";

import { registerEvent } from "../register-event";

const checkFolderWritePermission = async (
_event: Electron.IpcMainInvokeEvent,
path: string
) =>
new Promise((resolve) => {
fs.access(path, fs.constants.W_OK, (err) => {
resolve(!err);
});
});
testPath: string
) => {
const testFilePath = path.join(testPath, ".hydra-write-test");

try {
fs.writeFileSync(testFilePath, "");
fs.rmSync(testFilePath);
return true;
} catch (err) {
return false;
}
};

registerEvent("checkFolderWritePermission", checkFolderWritePermission);
7 changes: 7 additions & 0 deletions src/main/events/helpers/parse-launch-options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const parseLaunchOptions = (params: string | null): string[] => {
if (!params) {
return [];
}

return params.split(" ");
};
11 changes: 9 additions & 2 deletions src/main/events/library/open-game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,30 @@ import { gameRepository } from "@main/repository";

import { registerEvent } from "../register-event";
import { shell } from "electron";
import { spawn } from "child_process";
import { parseExecutablePath } from "../helpers/parse-executable-path";
import { parseLaunchOptions } from "../helpers/parse-launch-options";

const openGame = async (
_event: Electron.IpcMainInvokeEvent,
gameId: number,
executablePath: string,
launchOptions: string | null
) => {
// TODO: revisit this for launchOptions
const parsedPath = parseExecutablePath(executablePath);
const parsedParams = parseLaunchOptions(launchOptions);

await gameRepository.update(
{ id: gameId },
{ executablePath: parsedPath, launchOptions }
);

shell.openPath(parsedPath);
if (parsedParams.length === 0) {
shell.openPath(parsedPath);
return;
}

spawn(parsedPath, parsedParams, { shell: false, detached: true });
};

registerEvent("openGame", openGame);
2 changes: 1 addition & 1 deletion src/main/events/profile/update-profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { omit } from "lodash-es";
import axios from "axios";
import { fileTypeFromFile } from "file-type";

const patchUserProfile = async (updateProfile: UpdateProfileRequest) => {
export const patchUserProfile = async (updateProfile: UpdateProfileRequest) => {
return HydraApi.patch<UserProfile>("/profile", updateProfile);
};

Expand Down
2 changes: 2 additions & 0 deletions src/main/events/user-preferences/update-user-preferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { registerEvent } from "../register-event";

import type { UserPreferences } from "@types";
import i18next from "i18next";
import { patchUserProfile } from "../profile/update-profile";

const updateUserPreferences = async (
_event: Electron.IpcMainInvokeEvent,
preferences: Partial<UserPreferences>
) => {
if (preferences.language) {
i18next.changeLanguage(preferences.language);
patchUserProfile({ language: preferences.language }).catch(() => {});
}

return userPreferencesRepository.upsert(
Expand Down
46 changes: 27 additions & 19 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import { logger, WindowManager } from "@main/services";
import { dataSource } from "@main/data-source";
import resources from "@locales";
import { userPreferencesRepository } from "@main/repository";
import { knexClient, migrationConfig } from "./knex-client";
import { databaseDirectory } from "./constants";
import { getKnexClient, migrationConfig } from "./knex-client";
import { databaseDirectory, databasePath } from "./constants";
import { PythonRPC } from "./services/python-rpc";
import { Aria2 } from "./services/aria2";
import { loadState } from "./main";

const { autoUpdater } = updater;

Expand Down Expand Up @@ -55,14 +56,25 @@ const runMigrations = async () => {
fs.mkdirSync(databaseDirectory, { recursive: true });
}

await knexClient.migrate.list(migrationConfig).then((result) => {
logger.log(
"Migrations to run:",
result[1].map((migration) => migration.name)
);
});

await knexClient.migrate.latest(migrationConfig);
for (let trial = 0; trial < 2; trial++) {
const knexClient = getKnexClient();

try {
await knexClient.migrate.list(migrationConfig).then((result) => {
logger.log(
"Migrations to run:",
result[1].map((migration) => migration.name)
);
});

await knexClient.migrate.latest(migrationConfig);
return;
} catch (err) {
logger.log("Migrations failed to run, deleting db and trying again", err);
await knexClient.destroy();
fs.rmSync(databasePath);
}
}
};

// This method will be called when Electron has finished
Expand All @@ -76,22 +88,18 @@ app.whenReady().then(async () => {
return net.fetch(url.pathToFileURL(decodeURI(filePath)).toString());
});

await runMigrations()
.then(() => {
logger.log("Migrations executed successfully");
})
.catch((err) => {
logger.log("Migrations failed to run:", err);
});
await runMigrations().then(() => {
logger.log("Migrations executed successfully");
});

await dataSource.initialize();

await import("./main");

const userPreferences = await userPreferencesRepository.findOne({
where: { id: 1 },
});

await loadState(userPreferences);

if (userPreferences?.language) {
i18n.changeLanguage(userPreferences.language);
}
Expand Down
15 changes: 8 additions & 7 deletions src/main/knex-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,14 @@ class MigrationSource implements Knex.MigrationSource<HydraMigration> {
}
}

export const knexClient = knex({
debug: !app.isPackaged,
client: "better-sqlite3",
connection: {
filename: databasePath,
},
});
export const getKnexClient = () =>
knex({
debug: !app.isPackaged,
client: "better-sqlite3",
connection: {
filename: databasePath,
},
});

export const migrationConfig: Knex.MigratorConfig = {
migrationSource: new MigrationSource(),
Expand Down
18 changes: 3 additions & 15 deletions src/main/main.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { DownloadManager, Ludusavi, startMainLoop } from "./services";
import {
downloadQueueRepository,
gameRepository,
userPreferencesRepository,
} from "./repository";
import { downloadQueueRepository, gameRepository } from "./repository";
import { UserPreferences } from "./entity";
import { RealDebridClient } from "./services/download/real-debrid";
import { HydraApi } from "./services/hydra-api";
Expand All @@ -12,8 +8,8 @@ import { Aria2 } from "./services/aria2";
import { Downloader } from "@shared";
import { IsNull, Not } from "typeorm";

const loadState = async (userPreferences: UserPreferences | null) => {
import("./events");
export const loadState = async (userPreferences: UserPreferences | null) => {
await import("./events");

Aria2.spawn();

Expand Down Expand Up @@ -49,11 +45,3 @@ const loadState = async (userPreferences: UserPreferences | null) => {

startMainLoop();
};

userPreferencesRepository
.findOne({
where: { id: 1 },
})
.then((userPreferences) => {
loadState(userPreferences);
});
22 changes: 11 additions & 11 deletions src/main/services/achievements/achievement-watcher-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,20 +144,20 @@ const processAchievementFileDiff = async (
export class AchievementWatcherManager {
private static hasFinishedMergingWithRemote = false;

public static watchAchievements = () => {
public static watchAchievements() {
if (!this.hasFinishedMergingWithRemote) return;

if (process.platform === "win32") {
return watchAchievementsWindows();
}

return watchAchievementsWithWine();
};
}

private static preProcessGameAchievementFiles = (
private static preProcessGameAchievementFiles(
game: Game,
gameAchievementFiles: AchievementFile[]
) => {
) {
const unlockedAchievements: UnlockedAchievement[] = [];
for (const achievementFile of gameAchievementFiles) {
const parsedAchievements = parseAchievementFile(
Expand Down Expand Up @@ -185,9 +185,9 @@ export class AchievementWatcherManager {
}

return mergeAchievements(game, unlockedAchievements, false);
};
}

private static preSearchAchievementsWindows = async () => {
private static async preSearchAchievementsWindows() {
const games = await gameRepository.find({
where: {
isDeleted: false,
Expand All @@ -213,9 +213,9 @@ export class AchievementWatcherManager {
return this.preProcessGameAchievementFiles(game, gameAchievementFiles);
})
);
};
}

private static preSearchAchievementsWithWine = async () => {
private static async preSearchAchievementsWithWine() {
const games = await gameRepository.find({
where: {
isDeleted: false,
Expand All @@ -233,9 +233,9 @@ export class AchievementWatcherManager {
return this.preProcessGameAchievementFiles(game, gameAchievementFiles);
})
);
};
}

public static preSearchAchievements = async () => {
public static async preSearchAchievements() {
try {
const newAchievementsCount =
process.platform === "win32"
Expand All @@ -261,5 +261,5 @@ export class AchievementWatcherManager {
}

this.hasFinishedMergingWithRemote = true;
};
}
}
4 changes: 2 additions & 2 deletions src/main/services/achievements/get-game-achievement-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const getGameAchievementData = async (
shop: GameShop,
cachedAchievements: GameAchievement | null
) => {
if (cachedAchievements && cachedAchievements.achievements) {
if (cachedAchievements?.achievements) {
return JSON.parse(cachedAchievements.achievements) as AchievementData[];
}

Expand Down Expand Up @@ -42,7 +42,7 @@ export const getGameAchievementData = async (
if (err instanceof UserNotLoggedInError) {
throw err;
}
logger.error("Failed to get game achievements", err);
logger.error("Failed to get game achievements for", objectId, err);
return gameAchievementRepository
.findOne({
where: { objectId, shop },
Expand Down
4 changes: 2 additions & 2 deletions src/main/services/achievements/merge-achievements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const mergeAchievements = async (
).filter((achievement) => achievement.name) as UnlockedAchievement[];

const newAchievementsMap = new Map(
achievements.reverse().map((achievement) => {
achievements.toReversed().map((achievement) => {
return [achievement.name.toUpperCase(), achievement];
})
);
Expand Down Expand Up @@ -92,7 +92,7 @@ export const mergeAchievements = async (
userPreferences?.achievementNotificationsEnabled
) {
const achievementsInfo = newAchievements
.sort((a, b) => {
.toSorted((a, b) => {
return a.unlockTime - b.unlockTime;
})
.map((achievement) => {
Expand Down
Loading
Loading