feat: removing crypto from level

This commit is contained in:
Chubby Granny Chaser 2025-02-15 19:28:38 +00:00
parent 47e6d88dd9
commit 0f0e27e2e5
No known key found for this signature in database
12 changed files with 88 additions and 134 deletions

View file

@ -3,7 +3,6 @@ import jwt from "jsonwebtoken";
import { registerEvent } from "../register-event";
import { db, levelKeys } from "@main/level";
import type { Auth } from "@types";
import { Crypto } from "@main/services";
const getSessionHash = async (_event: Electron.IpcMainInvokeEvent) => {
const auth = await db.get<string, Auth>(levelKeys.auth, {
@ -11,9 +10,7 @@ const getSessionHash = async (_event: Electron.IpcMainInvokeEvent) => {
});
if (!auth) return null;
const payload = jwt.decode(
Crypto.decrypt(auth.accessToken)
) as jwt.JwtPayload;
const payload = jwt.decode(auth.accessToken) as jwt.JwtPayload;
if (!payload) return null;

View file

@ -1,6 +1,6 @@
import { shell } from "electron";
import { registerEvent } from "../register-event";
import { Crypto, HydraApi } from "@main/services";
import { HydraApi } from "@main/services";
import { db, levelKeys } from "@main/level";
import type { Auth } from "@types";
@ -14,7 +14,7 @@ const openCheckout = async (_event: Electron.IpcMainInvokeEvent) => {
}
const paymentToken = await HydraApi.post("/auth/payment", {
refreshToken: Crypto.decrypt(auth.refreshToken),
refreshToken: auth.refreshToken,
}).then((response) => response.accessToken);
const params = new URLSearchParams({

View file

@ -1,21 +1,10 @@
import { registerEvent } from "../register-event";
import { db, levelKeys } from "@main/level";
import { Crypto } from "@main/services";
import type { UserPreferences } from "@types";
const getUserPreferences = async () =>
db
.get<string, UserPreferences | null>(levelKeys.userPreferences, {
valueEncoding: "json",
})
.then((userPreferences) => {
if (userPreferences?.realDebridApiToken) {
userPreferences.realDebridApiToken = Crypto.decrypt(
userPreferences.realDebridApiToken
);
}
return userPreferences;
});
db.get<string, UserPreferences | null>(levelKeys.userPreferences, {
valueEncoding: "json",
});
registerEvent("getUserPreferences", getUserPreferences);

View file

@ -1,10 +1,4 @@
import {
Crypto,
DownloadManager,
logger,
Ludusavi,
startMainLoop,
} from "./services";
import { DownloadManager, logger, Ludusavi, startMainLoop } from "./services";
import { RealDebridClient } from "./services/download/real-debrid";
import { HydraApi } from "./services/hydra-api";
import { uploadGamesBatch } from "./services/library-sync";
@ -27,9 +21,7 @@ const loadState = async (userPreferences: UserPreferences | null) => {
Aria2.spawn();
if (userPreferences?.realDebridApiToken) {
RealDebridClient.authorize(
Crypto.decrypt(userPreferences.realDebridApiToken)
);
RealDebridClient.authorize(userPreferences.realDebridApiToken);
}
Ludusavi.addManifestToLudusaviConfig();
@ -106,9 +98,7 @@ const migrateFromSqlite = async () => {
await db.put(levelKeys.userPreferences, {
...rest,
realDebridApiToken: realDebridApiToken
? Crypto.encrypt(realDebridApiToken)
: null,
realDebridApiToken,
preferQuitInsteadOfHiding: rest.preferQuitInsteadOfHiding === 1,
runAtStartup: rest.runAtStartup === 1,
startMinimized: rest.startMinimized === 1,
@ -171,8 +161,8 @@ const migrateFromSqlite = async () => {
await db.put<string, Auth>(
levelKeys.auth,
{
accessToken: Crypto.encrypt(users[0].accessToken),
refreshToken: Crypto.encrypt(users[0].refreshToken),
accessToken: users[0].accessToken,
refreshToken: users[0].refreshToken,
tokenExpirationTimestamp: users[0].tokenExpirationTimestamp,
},
{

View file

@ -1,28 +0,0 @@
import { safeStorage } from "electron";
import { logger } from "./logger";
export class Crypto {
public static encrypt(str: string) {
if (safeStorage.isEncryptionAvailable()) {
return safeStorage.encryptString(str).toString("base64");
} else {
logger.warn(
"Encrypt method returned raw string because encryption is not available"
);
return str;
}
}
public static decrypt(b64: string) {
if (safeStorage.isEncryptionAvailable()) {
return safeStorage.decryptString(Buffer.from(b64, "base64"));
} else {
logger.warn(
"Decrypt method returned raw string because encryption is not available"
);
return b64;
}
}
}

View file

@ -12,7 +12,6 @@ import { isFuture, isToday } from "date-fns";
import { db } from "@main/level";
import { levelKeys } from "@main/level/sublevels";
import type { Auth, User } from "@types";
import { Crypto } from "./crypto";
interface HydraApiOptions {
needsAuth?: boolean;
@ -32,7 +31,9 @@ export class HydraApi {
private static readonly EXPIRATION_OFFSET_IN_MS = 1000 * 60 * 5; // 5 minutes
private static readonly ADD_LOG_INTERCEPTOR = true;
private static secondsToMilliseconds = (seconds: number) => seconds * 1000;
private static secondsToMilliseconds(seconds: number) {
return seconds * 1000;
}
private static userAuth: HydraApiUserAuth = {
authToken: "",
@ -80,8 +81,8 @@ export class HydraApi {
db.put<string, Auth>(
levelKeys.auth,
{
accessToken: Crypto.encrypt(accessToken),
refreshToken: Crypto.encrypt(refreshToken),
accessToken,
refreshToken,
tokenExpirationTimestamp,
},
{ valueEncoding: "json" }
@ -194,12 +195,8 @@ export class HydraApi {
const user = result.at(1) as User | undefined;
this.userAuth = {
authToken: userAuth?.accessToken
? Crypto.decrypt(userAuth.accessToken)
: "",
refreshToken: userAuth?.refreshToken
? Crypto.decrypt(userAuth.refreshToken)
: "",
authToken: userAuth?.accessToken ?? "",
refreshToken: userAuth?.refreshToken ?? "",
expirationTimestamp: userAuth?.tokenExpirationTimestamp ?? 0,
subscription: user?.subscription
? { expiresAt: user.subscription?.expiresAt }
@ -248,7 +245,7 @@ export class HydraApi {
levelKeys.auth,
{
...auth,
accessToken: Crypto.encrypt(accessToken),
accessToken,
tokenExpirationTimestamp,
},
{ valueEncoding: "json" }

View file

@ -234,7 +234,7 @@ export class WindowManager {
if (is.dev && process.env["ELECTRON_RENDERER_URL"]) {
editorWindow.loadURL(
`${process.env["ELECTRON_RENDERER_URL"]}#/editor?themeId=${themeId}`
`${process.env["ELECTRON_RENDERER_URL"]}#/theme-editor?themeId=${themeId}`
);
} else {
editorWindow.loadFile(path.join(__dirname, "../renderer/index.html"), {

View file

@ -30,7 +30,6 @@ import { HydraCloudModal } from "./pages/shared-modals/hydra-cloud/hydra-cloud-m
import { injectCustomCss } from "./helpers";
import "./app.scss";
import { Theme } from "@types";
export interface AppProps {
children: React.ReactNode;
@ -214,22 +213,22 @@ export function App() {
const id = crypto.randomUUID();
const channel = new BroadcastChannel(`download_sources:sync:${id}`);
channel.onmessage = (event: MessageEvent<number>) => {
channel.onmessage = async (event: MessageEvent<number>) => {
const newRepacksCount = event.data;
window.electron.publishNewRepacksNotification(newRepacksCount);
updateRepacks();
downloadSourcesTable.toArray().then((downloadSources) => {
downloadSources
.filter((source) => !source.fingerprint)
.forEach((downloadSource) => {
window.electron
.putDownloadSource(downloadSource.objectIds)
.then(({ fingerprint }) => {
downloadSourcesTable.update(downloadSource.id, { fingerprint });
});
});
});
const downloadSources = await downloadSourcesTable.toArray();
downloadSources
.filter((source) => !source.fingerprint)
.forEach(async (downloadSource) => {
const { fingerprint } = await window.electron.putDownloadSource(
downloadSource.objectIds
);
downloadSourcesTable.update(downloadSource.id, { fingerprint });
});
};
downloadSourcesWorker.postMessage(["SYNC_DOWNLOAD_SOURCES", id]);
@ -237,9 +236,9 @@ export function App() {
useEffect(() => {
const loadAndApplyTheme = async () => {
const activeTheme: Theme = await window.electron.getActiveCustomTheme();
const activeTheme = await window.electron.getActiveCustomTheme();
if (activeTheme.code) {
if (activeTheme?.code) {
injectCustomCss(activeTheme.code);
}
};

View file

@ -33,7 +33,9 @@ const Profile = React.lazy(() => import("./pages/profile/profile"));
const Achievements = React.lazy(
() => import("./pages/achievements/achievements")
);
const Editor = React.lazy(() => import("./pages/editor/editor"));
const ThemeEditor = React.lazy(
() => import("./pages/theme-editor/theme-editor")
);
import * as Sentry from "@sentry/react";
@ -107,8 +109,8 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
</Route>
<Route
path="/editor"
element={<SuspenseWrapper Component={Editor} />}
path="/theme-editor"
element={<SuspenseWrapper Component={ThemeEditor} />}
/>
</Routes>
</HashRouter>

View file

@ -12,7 +12,7 @@ export function DeleteGameModal({
onClose,
visible,
deleteGame,
}: DeleteGameModalProps) {
}: Readonly<DeleteGameModalProps>) {
const { t } = useTranslation("downloads");
const handleDeleteGame = () => {

View file

@ -1,22 +1,25 @@
@use "../../scss/globals.scss" as globals;
@use "../../scss/globals.scss";
.editor {
.theme-editor {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
&__header {
height: 35px;
display: flex;
align-items: center;
padding: 10px;
padding: calc(globals.$spacing-unit * 2);
background-color: globals.$dark-background-color;
font-size: 8px;
z-index: 50;
-webkit-app-region: drag;
gap: 8px;
&--darwin {
padding-top: calc(globals.$spacing-unit * 6);
}
h1 {
margin: 0;
line-height: 1;

View file

@ -1,5 +1,5 @@
import { useEffect, useState } from "react";
import "./editor.scss";
import { useCallback, useEffect, useState } from "react";
import "./theme-editor.scss";
import Editor from "@monaco-editor/react";
import { Theme } from "@types";
import { useSearchParams } from "react-router-dom";
@ -10,8 +10,9 @@ import {
ProjectRoadmapIcon,
} from "@primer/octicons-react";
import { useTranslation } from "react-i18next";
import cn from "classnames";
const EditorPage = () => {
export default function ThemeEditor() {
const [searchParams] = useSearchParams();
const [theme, setTheme] = useState<Theme | null>(null);
const [code, setCode] = useState("");
@ -37,29 +38,7 @@ const EditorPage = () => {
}
}, [themeId]);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if ((event.ctrlKey || event.metaKey) && event.key === "s") {
event.preventDefault();
handleSave();
}
};
window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [code, theme]);
const handleEditorChange = (value: string | undefined) => {
if (value !== undefined) {
setCode(value);
setHasUnsavedChanges(true);
}
};
const handleSave = async () => {
const handleSave = useCallback(async () => {
if (theme) {
const updatedTheme = {
...theme,
@ -74,13 +53,41 @@ const EditorPage = () => {
window.electron.injectCSS(code);
}
}
}, [code, theme]);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if ((event.ctrlKey || event.metaKey) && event.key === "s") {
event.preventDefault();
handleSave();
}
};
window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [code, handleSave, theme]);
const handleEditorChange = (value: string | undefined) => {
if (value !== undefined) {
setCode(value);
setHasUnsavedChanges(true);
}
};
return (
<div className="editor">
<div className="editor__header">
<div className="theme-editor">
<div
className={cn("theme-editor__header", {
"theme-editor__header--darwin": window.electron.platform === "darwin",
})}
>
<h1>{theme?.name}</h1>
{hasUnsavedChanges && <div className="editor__header__status"></div>}
{hasUnsavedChanges && (
<div className="theme-editor__header__status"></div>
)}
</div>
{activeTab === "code" && (
@ -100,15 +107,15 @@ const EditorPage = () => {
)}
{activeTab === "info" && (
<div className="editor__info">
<div className="theme-editor__info">
entao mano eu ate fiz isso aqui mas tava feio dms ai deu vergonha e
removi kkkk
</div>
)}
<div className="editor__footer">
<div className="editor__footer-actions">
<div className="editor__footer-actions__tabs">
<div className="theme-editor__footer">
<div className="theme-editor__footer-actions">
<div className="theme-editor__footer-actions__tabs">
<Button
onClick={() => handleTabChange("code")}
theme="dark"
@ -135,6 +142,4 @@ const EditorPage = () => {
</div>
</div>
);
};
export default EditorPage;
}