mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-03-09 15:40:26 +00:00
feat: adding initial leveldb configuration
This commit is contained in:
parent
d4be5b8c66
commit
2c5fb8a037
18 changed files with 202 additions and 196 deletions
|
@ -7,13 +7,18 @@ export const defaultDownloadsPath = app.getPath("downloads");
|
|||
|
||||
export const isStaging = import.meta.env.MAIN_VITE_API_URL.includes("staging");
|
||||
|
||||
export const levelDatabasePath = path.join(
|
||||
app.getPath("userData"),
|
||||
`hydra-db${isStaging ? "-staging" : ""}`
|
||||
);
|
||||
|
||||
export const databaseDirectory = path.join(app.getPath("appData"), "hydra");
|
||||
export const databasePath = path.join(
|
||||
databaseDirectory,
|
||||
isStaging ? "hydra_test.db" : "hydra.db"
|
||||
);
|
||||
|
||||
export const logsPath = path.join(app.getPath("appData"), "hydra", "logs");
|
||||
export const logsPath = path.join(app.getPath("userData"), "hydra", "logs");
|
||||
|
||||
export const seedsPath = app.isPackaged
|
||||
? path.join(process.resourcesPath, "seeds")
|
||||
|
|
|
@ -4,9 +4,7 @@ import {
|
|||
Game,
|
||||
GameShopCache,
|
||||
UserPreferences,
|
||||
UserAuth,
|
||||
GameAchievement,
|
||||
UserSubscription,
|
||||
} from "@main/entity";
|
||||
|
||||
import { databasePath } from "./constants";
|
||||
|
@ -15,9 +13,7 @@ export const dataSource = new DataSource({
|
|||
type: "better-sqlite3",
|
||||
entities: [
|
||||
Game,
|
||||
UserAuth,
|
||||
UserPreferences,
|
||||
UserSubscription,
|
||||
GameShopCache,
|
||||
DownloadQueue,
|
||||
GameAchievement,
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
export * from "./game.entity";
|
||||
export * from "./user-auth.entity";
|
||||
export * from "./user-preferences.entity";
|
||||
export * from "./user-subscription.entity";
|
||||
export * from "./game-shop-cache.entity";
|
||||
export * from "./game.entity";
|
||||
export * from "./game-achievements.entity";
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
OneToOne,
|
||||
} from "typeorm";
|
||||
import { UserSubscription } from "./user-subscription.entity";
|
||||
|
||||
@Entity("user_auth")
|
||||
export class UserAuth {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column("text", { default: "" })
|
||||
userId: string;
|
||||
|
||||
@Column("text", { default: "" })
|
||||
displayName: string;
|
||||
|
||||
@Column("text", { nullable: true })
|
||||
profileImageUrl: string | null;
|
||||
|
||||
@Column("text", { nullable: true })
|
||||
backgroundImageUrl: string | null;
|
||||
|
||||
@Column("text", { default: "" })
|
||||
accessToken: string;
|
||||
|
||||
@Column("text", { default: "" })
|
||||
refreshToken: string;
|
||||
|
||||
@Column("int", { default: 0 })
|
||||
tokenExpirationTimestamp: number;
|
||||
|
||||
@OneToOne("UserSubscription", "user")
|
||||
subscription: UserSubscription | null;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
import type { SubscriptionStatus } from "@types";
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
OneToOne,
|
||||
JoinColumn,
|
||||
} from "typeorm";
|
||||
import { UserAuth } from "./user-auth.entity";
|
||||
|
||||
@Entity("user_subscription")
|
||||
export class UserSubscription {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column("text", { default: "" })
|
||||
subscriptionId: string;
|
||||
|
||||
@OneToOne("UserAuth", "subscription")
|
||||
@JoinColumn()
|
||||
user: UserAuth;
|
||||
|
||||
@Column("text", { default: "" })
|
||||
status: SubscriptionStatus;
|
||||
|
||||
@Column("text", { default: "" })
|
||||
planId: string;
|
||||
|
||||
@Column("text", { default: "" })
|
||||
planName: string;
|
||||
|
||||
@Column("datetime", { nullable: true })
|
||||
expiresAt: Date | null;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
}
|
|
@ -1,13 +1,20 @@
|
|||
import jwt from "jsonwebtoken";
|
||||
|
||||
import { userAuthRepository } from "@main/repository";
|
||||
import { registerEvent } from "../register-event";
|
||||
import { db } from "@main/level";
|
||||
import type { Auth } from "@types";
|
||||
import { levelKeys } from "@main/level/sublevels/keys";
|
||||
import { Crypto } from "@main/services";
|
||||
|
||||
const getSessionHash = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
const auth = await userAuthRepository.findOne({ where: { id: 1 } });
|
||||
const auth = await db.get<string, Auth>(levelKeys.auth, {
|
||||
valueEncoding: "json",
|
||||
});
|
||||
|
||||
if (!auth) return null;
|
||||
const payload = jwt.decode(auth.accessToken) as jwt.JwtPayload;
|
||||
const payload = jwt.decode(
|
||||
Crypto.decrypt(auth.accessToken)
|
||||
) as jwt.JwtPayload;
|
||||
|
||||
if (!payload) return null;
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { registerEvent } from "../register-event";
|
||||
import { DownloadManager, HydraApi, gamesPlaytime } from "@main/services";
|
||||
import { dataSource } from "@main/data-source";
|
||||
import { DownloadQueue, Game, UserAuth, UserSubscription } from "@main/entity";
|
||||
import { DownloadQueue, Game } from "@main/entity";
|
||||
import { PythonRPC } from "@main/services/python-rpc";
|
||||
import { db } from "@main/level";
|
||||
import { levelKeys } from "@main/level/sublevels/keys";
|
||||
|
||||
const signOut = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
const databaseOperations = dataSource
|
||||
|
@ -11,13 +13,16 @@ const signOut = async (_event: Electron.IpcMainInvokeEvent) => {
|
|||
|
||||
await transactionalEntityManager.getRepository(Game).delete({});
|
||||
|
||||
await transactionalEntityManager
|
||||
.getRepository(UserAuth)
|
||||
.delete({ id: 1 });
|
||||
|
||||
await transactionalEntityManager
|
||||
.getRepository(UserSubscription)
|
||||
.delete({ id: 1 });
|
||||
await db.batch([
|
||||
{
|
||||
type: "del",
|
||||
key: levelKeys.auth,
|
||||
},
|
||||
{
|
||||
type: "del",
|
||||
key: levelKeys.user,
|
||||
},
|
||||
]);
|
||||
})
|
||||
.then(() => {
|
||||
/* Removes all games being played */
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
import { shell } from "electron";
|
||||
import { registerEvent } from "../register-event";
|
||||
import { userAuthRepository } from "@main/repository";
|
||||
import { HydraApi } from "@main/services";
|
||||
import { Crypto, HydraApi } from "@main/services";
|
||||
import { db } from "@main/level";
|
||||
import type { Auth } from "@types";
|
||||
import { levelKeys } from "@main/level/sublevels/keys";
|
||||
|
||||
const openCheckout = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
const userAuth = await userAuthRepository.findOne({ where: { id: 1 } });
|
||||
const auth = await db.get<string, Auth>(levelKeys.auth, {
|
||||
valueEncoding: "json",
|
||||
});
|
||||
|
||||
if (!userAuth) {
|
||||
if (!auth) {
|
||||
return;
|
||||
}
|
||||
|
||||
const paymentToken = await HydraApi.post("/auth/payment", {
|
||||
refreshToken: userAuth.refreshToken,
|
||||
refreshToken: Crypto.decrypt(auth.refreshToken),
|
||||
}).then((response) => response.accessToken);
|
||||
|
||||
const params = new URLSearchParams({
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
import { userAuthRepository } from "@main/repository";
|
||||
import { db } from "@main/level";
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
import type { UserFriends } from "@types";
|
||||
import type { User, UserFriends } from "@types";
|
||||
import { levelKeys } from "@main/level/sublevels/keys";
|
||||
|
||||
export const getUserFriends = async (
|
||||
userId: string,
|
||||
take: number,
|
||||
skip: number
|
||||
): Promise<UserFriends> => {
|
||||
const loggedUser = await userAuthRepository.findOne({ where: { id: 1 } });
|
||||
const user = await db.get<string, User>(levelKeys.user, {
|
||||
valueEncoding: "json",
|
||||
});
|
||||
|
||||
if (loggedUser?.userId === userId) {
|
||||
if (user?.id === userId) {
|
||||
return HydraApi.get(`/profile/friends`, { take, skip });
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,7 @@ import {
|
|||
Game,
|
||||
GameShopCache,
|
||||
UserPreferences,
|
||||
UserAuth,
|
||||
GameAchievement,
|
||||
UserSubscription,
|
||||
} from "@main/entity";
|
||||
|
||||
export const gameRepository = dataSource.getRepository(Game);
|
||||
|
@ -18,10 +16,5 @@ export const gameShopCacheRepository = dataSource.getRepository(GameShopCache);
|
|||
|
||||
export const downloadQueueRepository = dataSource.getRepository(DownloadQueue);
|
||||
|
||||
export const userAuthRepository = dataSource.getRepository(UserAuth);
|
||||
|
||||
export const userSubscriptionRepository =
|
||||
dataSource.getRepository(UserSubscription);
|
||||
|
||||
export const gameAchievementRepository =
|
||||
dataSource.getRepository(GameAchievement);
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
import {
|
||||
userAuthRepository,
|
||||
userSubscriptionRepository,
|
||||
} from "@main/repository";
|
||||
import axios, { AxiosError, AxiosInstance } from "axios";
|
||||
import { WindowManager } from "./window-manager";
|
||||
import url from "url";
|
||||
|
@ -13,6 +9,10 @@ import { omit } from "lodash-es";
|
|||
import { appVersion } from "@main/constants";
|
||||
import { getUserData } from "./user/get-user-data";
|
||||
import { isFuture, isToday } from "date-fns";
|
||||
import { db } from "@main/level";
|
||||
import { levelKeys } from "@main/level/sublevels/keys";
|
||||
import type { Auth, User } from "@types";
|
||||
import { Crypto } from "./crypto";
|
||||
|
||||
interface HydraApiOptions {
|
||||
needsAuth?: boolean;
|
||||
|
@ -77,14 +77,14 @@ export class HydraApi {
|
|||
tokenExpirationTimestamp
|
||||
);
|
||||
|
||||
await userAuthRepository.upsert(
|
||||
db.put<string, Auth>(
|
||||
levelKeys.auth,
|
||||
{
|
||||
id: 1,
|
||||
accessToken,
|
||||
accessToken: Crypto.encrypt(accessToken),
|
||||
refreshToken: Crypto.encrypt(refreshToken),
|
||||
tokenExpirationTimestamp,
|
||||
refreshToken,
|
||||
},
|
||||
["id"]
|
||||
{ valueEncoding: "json" }
|
||||
);
|
||||
|
||||
await getUserData().then((userDetails) => {
|
||||
|
@ -186,17 +186,23 @@ export class HydraApi {
|
|||
);
|
||||
}
|
||||
|
||||
const userAuth = await userAuthRepository.findOne({
|
||||
where: { id: 1 },
|
||||
relations: { subscription: true },
|
||||
const result = await db.getMany<string>([levelKeys.auth, levelKeys.user], {
|
||||
valueEncoding: "json",
|
||||
});
|
||||
|
||||
const userAuth = result.at(0) as Auth | undefined;
|
||||
const user = result.at(1) as User | undefined;
|
||||
|
||||
this.userAuth = {
|
||||
authToken: userAuth?.accessToken ?? "",
|
||||
refreshToken: userAuth?.refreshToken ?? "",
|
||||
authToken: userAuth?.accessToken
|
||||
? Crypto.decrypt(userAuth.accessToken)
|
||||
: "",
|
||||
refreshToken: userAuth?.refreshToken
|
||||
? Crypto.decrypt(userAuth.refreshToken)
|
||||
: "",
|
||||
expirationTimestamp: userAuth?.tokenExpirationTimestamp ?? 0,
|
||||
subscription: userAuth?.subscription
|
||||
? { expiresAt: userAuth.subscription?.expiresAt }
|
||||
subscription: user?.subscription
|
||||
? { expiresAt: user.subscription?.expiresAt }
|
||||
: null,
|
||||
};
|
||||
|
||||
|
@ -239,14 +245,19 @@ export class HydraApi {
|
|||
this.userAuth.expirationTimestamp
|
||||
);
|
||||
|
||||
userAuthRepository.upsert(
|
||||
{
|
||||
id: 1,
|
||||
accessToken,
|
||||
tokenExpirationTimestamp,
|
||||
},
|
||||
["id"]
|
||||
);
|
||||
await db
|
||||
.get<string, Auth>(levelKeys.auth, { valueEncoding: "json" })
|
||||
.then((auth) => {
|
||||
return db.put<string, Auth>(
|
||||
levelKeys.auth,
|
||||
{
|
||||
...auth,
|
||||
accessToken: Crypto.encrypt(accessToken),
|
||||
tokenExpirationTimestamp,
|
||||
},
|
||||
{ valueEncoding: "json" }
|
||||
);
|
||||
});
|
||||
} catch (err) {
|
||||
this.handleUnauthorizedError(err);
|
||||
}
|
||||
|
@ -276,8 +287,16 @@ export class HydraApi {
|
|||
subscription: null,
|
||||
};
|
||||
|
||||
userAuthRepository.delete({ id: 1 });
|
||||
userSubscriptionRepository.delete({ id: 1 });
|
||||
db.batch([
|
||||
{
|
||||
type: "del",
|
||||
key: levelKeys.auth,
|
||||
},
|
||||
{
|
||||
type: "del",
|
||||
key: levelKeys.user,
|
||||
},
|
||||
]);
|
||||
|
||||
this.sendSignOutEvent();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export * from "./crypto";
|
||||
export * from "./logger";
|
||||
export * from "./steam";
|
||||
export * from "./steam-250";
|
||||
|
|
|
@ -1,43 +1,30 @@
|
|||
import type { ProfileVisibility, UserDetails } from "@types";
|
||||
import { User, type ProfileVisibility, type UserDetails } from "@types";
|
||||
import { HydraApi } from "../hydra-api";
|
||||
import {
|
||||
userAuthRepository,
|
||||
userSubscriptionRepository,
|
||||
} from "@main/repository";
|
||||
import { UserNotLoggedInError } from "@shared";
|
||||
import { logger } from "../logger";
|
||||
import { db } from "@main/level";
|
||||
import { levelKeys } from "@main/level/sublevels/keys";
|
||||
|
||||
export const getUserData = () => {
|
||||
export const getUserData = async () => {
|
||||
return HydraApi.get<UserDetails>(`/profile/me`)
|
||||
.then(async (me) => {
|
||||
userAuthRepository.upsert(
|
||||
{
|
||||
id: 1,
|
||||
displayName: me.displayName,
|
||||
profileImageUrl: me.profileImageUrl,
|
||||
backgroundImageUrl: me.backgroundImageUrl,
|
||||
userId: me.id,
|
||||
},
|
||||
["id"]
|
||||
db.get<string, User>(levelKeys.user, { valueEncoding: "json" }).then(
|
||||
(user) => {
|
||||
return db.put<string, User>(
|
||||
levelKeys.user,
|
||||
{
|
||||
...user,
|
||||
id: me.id,
|
||||
displayName: me.displayName,
|
||||
profileImageUrl: me.profileImageUrl,
|
||||
backgroundImageUrl: me.backgroundImageUrl,
|
||||
subscription: me.subscription,
|
||||
},
|
||||
{ valueEncoding: "json" }
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
if (me.subscription) {
|
||||
await userSubscriptionRepository.upsert(
|
||||
{
|
||||
id: 1,
|
||||
subscriptionId: me.subscription?.id || "",
|
||||
status: me.subscription?.status || "",
|
||||
planId: me.subscription?.plan.id || "",
|
||||
planName: me.subscription?.plan.name || "",
|
||||
expiresAt: me.subscription?.expiresAt || null,
|
||||
user: { id: 1 },
|
||||
},
|
||||
["id"]
|
||||
);
|
||||
} else {
|
||||
await userSubscriptionRepository.delete({ id: 1 });
|
||||
}
|
||||
|
||||
return me;
|
||||
})
|
||||
.catch(async (err) => {
|
||||
|
@ -46,15 +33,14 @@ export const getUserData = () => {
|
|||
return null;
|
||||
}
|
||||
logger.error("Failed to get logged user");
|
||||
const loggedUser = await userAuthRepository.findOne({
|
||||
where: { id: 1 },
|
||||
relations: { subscription: true },
|
||||
|
||||
const loggedUser = await db.get<string, User>(levelKeys.user, {
|
||||
valueEncoding: "json",
|
||||
});
|
||||
|
||||
if (loggedUser) {
|
||||
return {
|
||||
...loggedUser,
|
||||
id: loggedUser.userId,
|
||||
username: "",
|
||||
bio: "",
|
||||
email: null,
|
||||
|
@ -64,11 +50,11 @@ export const getUserData = () => {
|
|||
},
|
||||
subscription: loggedUser.subscription
|
||||
? {
|
||||
id: loggedUser.subscription.subscriptionId,
|
||||
id: loggedUser.subscription.id,
|
||||
status: loggedUser.subscription.status,
|
||||
plan: {
|
||||
id: loggedUser.subscription.planId,
|
||||
name: loggedUser.subscription.planName,
|
||||
id: loggedUser.subscription.plan.id,
|
||||
name: loggedUser.subscription.plan.name,
|
||||
},
|
||||
expiresAt: loggedUser.subscription.expiresAt,
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ export function Modal({
|
|||
)
|
||||
)
|
||||
return false;
|
||||
|
||||
const openModals = document.querySelectorAll("[role=dialog]");
|
||||
|
||||
return (
|
||||
|
|
|
@ -248,16 +248,6 @@ export interface UserProfileCurrentGame extends Omit<GameRunning, "objectId"> {
|
|||
|
||||
export type ProfileVisibility = "PUBLIC" | "PRIVATE" | "FRIENDS";
|
||||
|
||||
export type SubscriptionStatus = "active" | "pending" | "cancelled";
|
||||
|
||||
export interface Subscription {
|
||||
id: string;
|
||||
status: SubscriptionStatus;
|
||||
plan: { id: string; name: string };
|
||||
expiresAt: string | null;
|
||||
paymentMethod: "pix" | "paypal";
|
||||
}
|
||||
|
||||
export interface UserDetails {
|
||||
id: string;
|
||||
username: string;
|
||||
|
@ -421,3 +411,4 @@ export * from "./real-debrid.types";
|
|||
export * from "./ludusavi.types";
|
||||
export * from "./how-long-to-beat.types";
|
||||
export * from "./torbox.types";
|
||||
export * from "./level.types";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue