diff --git a/src/main/services/achievements/merge-achievements.ts b/src/main/services/achievements/merge-achievements.ts index ae2ba3ed..c97e8f56 100644 --- a/src/main/services/achievements/merge-achievements.ts +++ b/src/main/services/achievements/merge-achievements.ts @@ -115,10 +115,14 @@ export const mergeAchievements = async ( const mergedLocalAchievements = unlockedAchievements.concat(newAchievements); if (game?.remoteId) { - return HydraApi.put("/profile/games/achievements", { - id: game.remoteId, - achievements: mergedLocalAchievements, - }) + return HydraApi.put( + "/profile/games/achievements", + { + id: game.remoteId, + achievements: mergedLocalAchievements, + }, + { needsCloud: true } + ) .then((response) => { return saveAchievementsOnLocal( response.objectId, diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts index d62bafcb..c8f3795e 100644 --- a/src/main/services/hydra-api.ts +++ b/src/main/services/hydra-api.ts @@ -1,16 +1,23 @@ -import { userAuthRepository } from "@main/repository"; +import { + userAuthRepository, + userSubscriptionRepository, +} from "@main/repository"; import axios, { AxiosError, AxiosInstance } from "axios"; import { WindowManager } from "./window-manager"; import url from "url"; import { uploadGamesBatch } from "./library-sync"; import { clearGamesRemoteIds } from "./library-sync/clear-games-remote-id"; import { logger } from "./logger"; -import { UserNotLoggedInError } from "@shared"; +import { + UserNotLoggedInError, + UserWithoutCloudSubscriptionError, +} from "@shared"; import { omit } from "lodash-es"; import { appVersion } from "@main/constants"; interface HydraApiOptions { - needsAuth: boolean; + needsAuth?: boolean; + needsCloud?: boolean; } export class HydraApi { @@ -31,6 +38,19 @@ export class HydraApi { return this.userAuth.authToken !== ""; } + private static async hasCloudSubscription() { + // TODO change this later, this is just a quick test + return userSubscriptionRepository + .findOne({ where: { id: 1 } }) + .then((userSubscription) => { + if (userSubscription?.status !== "active") return false; + return ( + !userSubscription.expiresAt || + userSubscription!.expiresAt > new Date() + ); + }); + } + static async handleExternalAuth(uri: string) { const { payload } = url.parse(uri, true).query; @@ -234,15 +254,28 @@ export class HydraApi { throw err; }; + private static async validateOptions(options?: HydraApiOptions) { + const needsAuth = options?.needsAuth == undefined || options.needsAuth; + const needsCloud = options?.needsCloud === true; + + if (needsAuth) { + if (!this.isLoggedIn()) throw new UserNotLoggedInError(); + await this.revalidateAccessTokenIfExpired(); + } + + if (needsCloud) { + if (!(await this.hasCloudSubscription())) { + throw new UserWithoutCloudSubscriptionError(); + } + } + } + static async get( url: string, params?: any, options?: HydraApiOptions ) { - if (!options || options.needsAuth) { - if (!this.isLoggedIn()) throw new UserNotLoggedInError(); - await this.revalidateAccessTokenIfExpired(); - } + await this.validateOptions(options); return this.instance .get(url, { params, ...this.getAxiosConfig() }) @@ -255,10 +288,7 @@ export class HydraApi { data?: any, options?: HydraApiOptions ) { - if (!options || options.needsAuth) { - if (!this.isLoggedIn()) throw new UserNotLoggedInError(); - await this.revalidateAccessTokenIfExpired(); - } + await this.validateOptions(options); return this.instance .post(url, data, this.getAxiosConfig()) @@ -271,10 +301,7 @@ export class HydraApi { data?: any, options?: HydraApiOptions ) { - if (!options || options.needsAuth) { - if (!this.isLoggedIn()) throw new UserNotLoggedInError(); - await this.revalidateAccessTokenIfExpired(); - } + await this.validateOptions(options); return this.instance .put(url, data, this.getAxiosConfig()) @@ -287,10 +314,7 @@ export class HydraApi { data?: any, options?: HydraApiOptions ) { - if (!options || options.needsAuth) { - if (!this.isLoggedIn()) throw new UserNotLoggedInError(); - await this.revalidateAccessTokenIfExpired(); - } + await this.validateOptions(options); return this.instance .patch(url, data, this.getAxiosConfig()) @@ -299,10 +323,7 @@ export class HydraApi { } static async delete(url: string, options?: HydraApiOptions) { - if (!options || options.needsAuth) { - if (!this.isLoggedIn()) throw new UserNotLoggedInError(); - await this.revalidateAccessTokenIfExpired(); - } + await this.validateOptions(options); return this.instance .delete(url, this.getAxiosConfig()) diff --git a/src/shared/index.ts b/src/shared/index.ts index 5d216183..c2a98c8a 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -10,6 +10,13 @@ export class UserNotLoggedInError extends Error { } } +export class UserWithoutCloudSubscriptionError extends Error { + constructor() { + super("user does not have hydra cloud subscription"); + this.name = "UserWithoutCloudSubscriptionError"; + } +} + const FORMAT = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; export const formatBytes = (bytes: number): string => {