mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-03-09 15:40:26 +00:00
Merge pull request #596 from hydralauncher/feature/integrate-hydra-api
feat: create HydraApi
This commit is contained in:
commit
8fad9b05e6
7 changed files with 122 additions and 4 deletions
|
@ -1,4 +1,3 @@
|
||||||
MAIN_VITE_STEAMGRIDDB_API_KEY=YOUR_API_KEY
|
MAIN_VITE_STEAMGRIDDB_API_KEY=YOUR_API_KEY
|
||||||
MAIN_VITE_ONLINEFIX_USERNAME=YOUR_USERNAME
|
MAIN_VITE_API_URL=API_URL
|
||||||
MAIN_VITE_ONLINEFIX_PASSWORD=YOUR_PASSWORD
|
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import type { BetterSqlite3ConnectionOptions } from "typeorm/driver/better-sqlit
|
||||||
|
|
||||||
import { databasePath } from "./constants";
|
import { databasePath } from "./constants";
|
||||||
import migrations from "./migrations";
|
import migrations from "./migrations";
|
||||||
|
import { UserAuth } from "./entity/user-auth";
|
||||||
|
|
||||||
export const createDataSource = (
|
export const createDataSource = (
|
||||||
options: Partial<BetterSqlite3ConnectionOptions>
|
options: Partial<BetterSqlite3ConnectionOptions>
|
||||||
|
@ -24,6 +25,7 @@ export const createDataSource = (
|
||||||
GameShopCache,
|
GameShopCache,
|
||||||
DownloadSource,
|
DownloadSource,
|
||||||
DownloadQueue,
|
DownloadQueue,
|
||||||
|
UserAuth,
|
||||||
],
|
],
|
||||||
synchronize: true,
|
synchronize: true,
|
||||||
database: databasePath,
|
database: databasePath,
|
||||||
|
|
28
src/main/entity/user-auth.ts
Normal file
28
src/main/entity/user-auth.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from "typeorm";
|
||||||
|
|
||||||
|
@Entity("user_auth")
|
||||||
|
export class UserAuth {
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column("text", { default: "" })
|
||||||
|
accessToken: string;
|
||||||
|
|
||||||
|
@Column("text", { default: "" })
|
||||||
|
refreshToken: string;
|
||||||
|
|
||||||
|
@Column("int", { default: 0 })
|
||||||
|
tokenExpirationTimestamp: number;
|
||||||
|
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import { RealDebridClient } from "./services/real-debrid";
|
||||||
import { fetchDownloadSourcesAndUpdate } from "./helpers";
|
import { fetchDownloadSourcesAndUpdate } from "./helpers";
|
||||||
import { publishNewRepacksNotifications } from "./services/notifications";
|
import { publishNewRepacksNotifications } from "./services/notifications";
|
||||||
import { MoreThan } from "typeorm";
|
import { MoreThan } from "typeorm";
|
||||||
|
import { HydraApi } from "./services/hydra-api";
|
||||||
|
|
||||||
startMainLoop();
|
startMainLoop();
|
||||||
|
|
||||||
|
@ -20,6 +21,8 @@ const loadState = async (userPreferences: UserPreferences | null) => {
|
||||||
if (userPreferences?.realDebridApiToken)
|
if (userPreferences?.realDebridApiToken)
|
||||||
RealDebridClient.authorize(userPreferences?.realDebridApiToken);
|
RealDebridClient.authorize(userPreferences?.realDebridApiToken);
|
||||||
|
|
||||||
|
HydraApi.setupApi();
|
||||||
|
|
||||||
const [nextQueueItem] = await downloadQueueRepository.find({
|
const [nextQueueItem] = await downloadQueueRepository.find({
|
||||||
order: {
|
order: {
|
||||||
id: "DESC",
|
id: "DESC",
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
Repack,
|
Repack,
|
||||||
UserPreferences,
|
UserPreferences,
|
||||||
} from "@main/entity";
|
} from "@main/entity";
|
||||||
|
import { UserAuth } from "./entity/user-auth";
|
||||||
|
|
||||||
export const gameRepository = dataSource.getRepository(Game);
|
export const gameRepository = dataSource.getRepository(Game);
|
||||||
|
|
||||||
|
@ -21,3 +22,5 @@ export const downloadSourceRepository =
|
||||||
dataSource.getRepository(DownloadSource);
|
dataSource.getRepository(DownloadSource);
|
||||||
|
|
||||||
export const downloadQueueRepository = dataSource.getRepository(DownloadQueue);
|
export const downloadQueueRepository = dataSource.getRepository(DownloadQueue);
|
||||||
|
|
||||||
|
export const userAuthRepository = dataSource.getRepository(UserAuth);
|
||||||
|
|
84
src/main/services/hydra-api.ts
Normal file
84
src/main/services/hydra-api.ts
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
import { userAuthRepository } from "@main/repository";
|
||||||
|
import axios, { AxiosInstance } from "axios";
|
||||||
|
|
||||||
|
export class HydraApi {
|
||||||
|
private static instance: AxiosInstance;
|
||||||
|
|
||||||
|
private static readonly EXPIRATION_OFFSET_IN_MS = 1000 * 60 * 5;
|
||||||
|
|
||||||
|
private static userAuth = {
|
||||||
|
authToken: "",
|
||||||
|
refreshToken: "",
|
||||||
|
expirationTimestamp: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static async setupApi() {
|
||||||
|
this.instance = axios.create({
|
||||||
|
baseURL: import.meta.env.MAIN_VITE_API_URL,
|
||||||
|
});
|
||||||
|
|
||||||
|
const userAuth = await userAuthRepository.findOne({
|
||||||
|
where: { id: 1 },
|
||||||
|
});
|
||||||
|
|
||||||
|
this.userAuth = {
|
||||||
|
authToken: userAuth?.accessToken ?? "",
|
||||||
|
refreshToken: userAuth?.refreshToken ?? "",
|
||||||
|
expirationTimestamp: userAuth?.tokenExpirationTimestamp ?? 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async revalidateAccessTokenIfExpired() {
|
||||||
|
const now = new Date();
|
||||||
|
if (this.userAuth.expirationTimestamp < now.getTime()) {
|
||||||
|
const response = await this.instance.post(`/auth/refresh`, {
|
||||||
|
refreshToken: this.userAuth.refreshToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { accessToken, expiresIn } = response.data;
|
||||||
|
|
||||||
|
const tokenExpirationTimestamp =
|
||||||
|
now.getTime() + expiresIn - this.EXPIRATION_OFFSET_IN_MS;
|
||||||
|
|
||||||
|
this.userAuth.authToken = accessToken;
|
||||||
|
this.userAuth.expirationTimestamp = tokenExpirationTimestamp;
|
||||||
|
|
||||||
|
userAuthRepository.upsert(
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
accessToken,
|
||||||
|
tokenExpirationTimestamp,
|
||||||
|
},
|
||||||
|
["id"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getAxiosConfig() {
|
||||||
|
return {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${this.userAuth.authToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static async get(url: string) {
|
||||||
|
await this.revalidateAccessTokenIfExpired();
|
||||||
|
return this.instance.get(url, this.getAxiosConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
static async post(url: string, data?: any) {
|
||||||
|
await this.revalidateAccessTokenIfExpired();
|
||||||
|
return this.instance.post(url, data, this.getAxiosConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
static async put(url, data?: any) {
|
||||||
|
await this.revalidateAccessTokenIfExpired();
|
||||||
|
return this.instance.put(url, data, this.getAxiosConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
static async patch(url, data?: any) {
|
||||||
|
await this.revalidateAccessTokenIfExpired();
|
||||||
|
return this.instance.patch(url, data, this.getAxiosConfig());
|
||||||
|
}
|
||||||
|
}
|
3
src/main/vite-env.d.ts
vendored
3
src/main/vite-env.d.ts
vendored
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
readonly MAIN_VITE_STEAMGRIDDB_API_KEY: string;
|
readonly MAIN_VITE_STEAMGRIDDB_API_KEY: string;
|
||||||
readonly MAIN_VITE_ONLINEFIX_USERNAME: string;
|
readonly MAIN_VITE_API_URL: string;
|
||||||
readonly MAIN_VITE_ONLINEFIX_PASSWORD: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportMeta {
|
interface ImportMeta {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue