Skip to content

Commit

Permalink
Merge pull request #137 from game-node-app/dev
Browse files Browse the repository at this point in the history
PSN integration work
  • Loading branch information
Lamarcke authored Jan 5, 2025
2 parents dd3d4f6 + a58bd4b commit f465060
Show file tree
Hide file tree
Showing 47 changed files with 623 additions and 410 deletions.
2 changes: 1 addition & 1 deletion docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ services:
redis:
image: redis:latest
hostname: server-redis
command: ["redis-server", "--appendonly", "no", "--maxmemory", "2048mb", "--maxmemory-policy", "allkeys-lru"]
command: ["redis-server", "--appendonly", "no", "--maxmemory", "4096mb", "--maxmemory-policy", "allkeys-lru"]
environment:
ALLOW_EMPTY_PASSWORD: 'yes'

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"dependencies": {
"@golevelup/nestjs-rabbitmq": "^5.6.1",
"@lamarcke/psn-api": "^1.0.1",
"@liaoliaots/nestjs-redis-health": "^9.0.4",
"@nestjs/axios": "^3.0.0",
"@nestjs/bullmq": "^10.2.2",
Expand Down Expand Up @@ -58,7 +59,6 @@
"mime-types": "^2.1.35",
"mysql2": "^3.11.4",
"nestjs-throttler-storage-redis": "^0.5.1",
"psn-api": "^2.10.1",
"redis": "^4.7.0",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
Expand Down
Binary file added public/icons/psn.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion server_swagger.json

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { FollowModule } from "./follow/follow.module";
import { IgdbSyncModule } from "./sync/igdb/igdb-sync.module";
import { NotificationsModule } from "./notifications/notifications.module";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { HltbSyncModule } from "./sync/hltb/hltb-sync.module";
import { SteamSyncModule } from "./sync/steam/steam-sync.module";
import { ConnectionsModule } from "./connections/connections.module";
import { CommentModule } from "./comment/comment.module";
Expand All @@ -31,7 +30,7 @@ import { ProfileMetricsModule } from "./profile/profile-metrics/profile-metrics.
import { RecommendationModule } from "./recommendation/recommendation.module";
import { PlaytimeModule } from "./playtime/playtime.module";
import { GameFilterModule } from "./game/game-filter/game-filter.module";
import { PsnModule } from "./sync/psn/psn.module";
import { PsnSyncModule } from "./sync/psn/psn-sync.module";

/**
* Should only be called after 'ConfigModule' is loaded (e.g. in useFactory)
Expand Down Expand Up @@ -97,6 +96,7 @@ function getRedisConfig() {
host: redisConfig.host,
port: redisConfig.port,
},
ignoreErrors: true,
},
};
},
Expand Down Expand Up @@ -158,7 +158,6 @@ function getRedisConfig() {
AchievementsModule,
FollowModule,
NotificationsModule,
HltbSyncModule,
SteamSyncModule,
ImporterWatchModule,
ConnectionsModule,
Expand All @@ -168,7 +167,7 @@ function getRedisConfig() {
RecommendationModule,
PlaytimeModule,
GameFilterModule,
PsnModule,
PsnSyncModule,
],
})
export class AppModule implements NestModule {
Expand Down
13 changes: 11 additions & 2 deletions src/connections/connections.constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
export enum EConnectionType {
Steam = "steam",
STEAM = "steam",
PSN = "psn",
}

export const IMPORTER_VIABLE_CONNECTIONS = [EConnectionType.Steam];
export const IMPORTER_VIABLE_CONNECTIONS = [
EConnectionType.STEAM,
EConnectionType.PSN,
];

export const IMPORTER_WATCH_VIABLE_CONNECTIONS = [
EConnectionType.STEAM,
EConnectionType.PSN,
];
7 changes: 6 additions & 1 deletion src/connections/connections.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ import { ConnectionsController } from "./connections.controller";
import { TypeOrmModule } from "@nestjs/typeorm";
import { UserConnection } from "./entity/user-connection.entity";
import { SteamSyncModule } from "../sync/steam/steam-sync.module";
import { PsnSyncModule } from "../sync/psn/psn-sync.module";

@Module({
imports: [TypeOrmModule.forFeature([UserConnection]), SteamSyncModule],
imports: [
TypeOrmModule.forFeature([UserConnection]),
SteamSyncModule,
PsnSyncModule,
],
providers: [ConnectionsService],
controllers: [ConnectionsController],
exports: [ConnectionsService],
Expand Down
44 changes: 37 additions & 7 deletions src/connections/connections.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,29 @@ import { In, Repository } from "typeorm";
import {
EConnectionType,
IMPORTER_VIABLE_CONNECTIONS,
IMPORTER_WATCH_VIABLE_CONNECTIONS,
} from "./connections.constants";
import { ConnectionCreateDto } from "./dto/connection-create.dto";
import { SteamSyncService } from "../sync/steam/steam-sync.service";
import { FindAvailableConnectionsResponseDto } from "./dto/find-available-connections-response.dto";
import { PsnSyncService } from "../sync/psn/psn-sync.service";
import { UserConnectionDto } from "./dto/user-connection.dto";

const toDto = (userConnection: UserConnection): UserConnectionDto => ({
...userConnection,
isImporterViable: IMPORTER_VIABLE_CONNECTIONS.includes(userConnection.type),
isImporterWatchViable: IMPORTER_WATCH_VIABLE_CONNECTIONS.includes(
userConnection.type,
),
});

@Injectable()
export class ConnectionsService {
constructor(
@InjectRepository(UserConnection)
private readonly userConnectionRepository: Repository<UserConnection>,
private readonly steamSyncService: SteamSyncService,
private readonly psnSyncService: PsnSyncService,
) {}

public findOneById(id: number) {
Expand All @@ -24,17 +36,21 @@ export class ConnectionsService {
});
}

public findOneByUserIdAndType(userId: string, type: EConnectionType) {
return this.userConnectionRepository.findOneBy({
public async findOneByUserIdAndType(userId: string, type: EConnectionType) {
const connection = await this.userConnectionRepository.findOneBy({
type,
profileUserId: userId,
});

if (!connection) return null;

return toDto(connection);
}

public async findOneByUserIdAndTypeOrFail(
userId: string,
type: EConnectionType,
): Promise<UserConnection> {
): Promise<UserConnectionDto> {
const entity = await this.findOneByUserIdAndType(userId, type);
if (!entity) {
throw new HttpException(
Expand All @@ -46,16 +62,20 @@ export class ConnectionsService {
return entity;
}

public async findAllByUserId(userId: string) {
return this.userConnectionRepository.findBy({
public async findAllByUserId(userId: string): Promise<UserConnectionDto[]> {
const connections = await this.userConnectionRepository.findBy({
profileUserId: userId,
});

return connections.map(toDto);
}

public async findAllByUserIdIn(userIds: string[]) {
return this.userConnectionRepository.findBy({
const connections = await this.userConnectionRepository.findBy({
profileUserId: In(userIds),
});

return connections.map(toDto);
}

public async findAvailableConnections(): Promise<
Expand All @@ -67,6 +87,8 @@ export class ConnectionsService {
type: type,
isImporterViable:
IMPORTER_VIABLE_CONNECTIONS.includes(type),
isImporterWatchViable:
IMPORTER_WATCH_VIABLE_CONNECTIONS.includes(type),
name: type.valueOf(),
iconName: type.valueOf(),
};
Expand All @@ -86,12 +108,20 @@ export class ConnectionsService {
let sourceUsername: string;

switch (type) {
case EConnectionType.Steam:
case EConnectionType.STEAM: {
const steamUserInfo =
await this.steamSyncService.resolveUserInfo(userIdentifier);
sourceUserId = steamUserInfo.userId;
sourceUsername = steamUserInfo.username;
break;
}
case EConnectionType.PSN: {
const psnUserInfo =
await this.psnSyncService.resolveUserInfo(userIdentifier);
sourceUserId = psnUserInfo.userId;
sourceUsername = psnUserInfo.username;
break;
}
default:
throw new HttpException(
"Invalid connection type",
Expand Down
4 changes: 4 additions & 0 deletions src/connections/dto/connection-user-resolve.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export class ConnectionUserResolveDto {
userId: string;
username: string;
}
10 changes: 10 additions & 0 deletions src/connections/dto/find-available-connections-response.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ import { EConnectionType } from "../connections.constants";
export class FindAvailableConnectionsResponseDto {
name: string;
type: EConnectionType;
/**
* If this connection can be used by the importer system to import games
* e.g.: Steam, PSN
*/
isImporterViable: boolean;
/**
* If this connection can be used by the importer watch system to periodically
* check for new importable games
* e.g.: Steam
*/
isImporterWatchViable: boolean;
iconName: string;
}
7 changes: 7 additions & 0 deletions src/connections/dto/user-connection.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { UserConnection } from "../entity/user-connection.entity";
import { OmitType } from "@nestjs/swagger";

export class UserConnectionDto extends OmitType(UserConnection, ["profile"]) {
isImporterViable: boolean;
isImporterWatchViable: boolean;
}
8 changes: 0 additions & 8 deletions src/connections/entity/user-connection.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,6 @@ export class UserConnection {
nullable: false,
})
sourceUsername: string;
/**
* If this connection can be used by the 'importer' system.
*/
@Column({
nullable: false,
default: false,
})
isImporterViable: boolean;
@Column({
nullable: false,
default: false,
Expand Down
79 changes: 79 additions & 0 deletions src/game/game-repository/game-repository-cache.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Inject, Injectable, Logger } from "@nestjs/common";
import { CACHE_MANAGER, Cache } from "@nestjs/cache-manager";
import { FindManyOptions, FindOneOptions, Repository } from "typeorm";
import { Game } from "./entities/game.entity";
import { InjectRepository } from "@nestjs/typeorm";
import { minutes } from "@nestjs/throttler";
import { TPaginationData } from "../../utils/pagination/pagination-response.dto";

/**
* Adds a cache layer for calls to game's entity repository <br>
* Necessary because TypeORM's cache tend to miss.
* @param options
* @param ttl
*/
@Injectable()
export class GameRepositoryCacheService {
private readonly logger = new Logger(GameRepositoryCacheService.name);

constructor(
@InjectRepository(Game)
private readonly gameRepository: Repository<Game>,
@Inject(CACHE_MANAGER) private readonly cacheManager: Cache,
) {}

async find(
options: Omit<FindManyOptions<Game>, "cache">,
ttl: number = minutes(5),
) {
const cacheKey = JSON.stringify(options);
const itemsInCache = await this.cacheManager.get<Game[]>(cacheKey);

if (itemsInCache) return itemsInCache;

const items = await this.gameRepository.find(options);

this.cacheManager.set(cacheKey, items, ttl).catch((err) => {
this.logger.error(err);
});

return items;
}

async findAndCount(
options: Omit<FindManyOptions<Game>, "cache">,
ttl: number = minutes(5),
) {
const cacheKey = JSON.stringify(options);
const itemsInCache =
await this.cacheManager.get<TPaginationData<Game>>(cacheKey);

if (itemsInCache) return itemsInCache;

const items = await this.gameRepository.findAndCount(options);

this.cacheManager.set(cacheKey, items, ttl).catch((err) => {
this.logger.error(err);
});

return items;
}

async findOne(
options: Omit<FindOneOptions<Game>, "cache">,
ttl: number = minutes(1),
) {
const cacheKey = JSON.stringify(options);
const itemInCache = await this.cacheManager.get<Game>(cacheKey);

if (itemInCache) return itemInCache;

const items = await this.gameRepository.findOne(options);

this.cacheManager.set(cacheKey, items, ttl).catch((err) => {
this.logger.error(err);
});

return items;
}
}
27 changes: 0 additions & 27 deletions src/game/game-repository/game-repository.controller.spec.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/game/game-repository/game-repository.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { GameCompanyLogo } from "./entities/game-company-logo.entity";
import { GamePlayerPerspective } from "./entities/game-player-perspective.entity";
import { GameRepositoryCreateService } from "./game-repository-create.service";
import { StatisticsQueueModule } from "../../statistics/statistics-queue/statistics-queue.module";
import { HltbSyncModule } from "../../sync/hltb/hltb-sync.module";
import { GameRepositoryCacheService } from './game-repository-cache.service';

/**
* This is a pretty big module, with lots of dependencies.
Expand Down Expand Up @@ -55,7 +55,7 @@ import { HltbSyncModule } from "../../sync/hltb/hltb-sync.module";
]),
forwardRef(() => StatisticsQueueModule),
],
providers: [GameRepositoryService, GameRepositoryCreateService],
providers: [GameRepositoryService, GameRepositoryCreateService, GameRepositoryCacheService],
exports: [GameRepositoryService, GameRepositoryCreateService],
controllers: [GameRepositoryController],
})
Expand Down
Loading

0 comments on commit f465060

Please sign in to comment.