Skip to content

Commit

Permalink
Release 12.1.0 (#287)
Browse files Browse the repository at this point in the history
* Implement Feature/bookmarks #178 (#278)

* Feature/bookmarks (#279)

* implement bookmarks

* impl bookmarks not yet done

* further implementation of bookmarks

* implement migrations for bookmarks

* lint

* fix plural

* fix logs

* fix bookmarked_users not being loaded without filter

* format

* query result caching

* fix idempotency

* add db indexes

* fix utc dates

* fix pnpm

* load user relations to add bookmark

* super efficient relationship loader

* faster and more architecturally clean dupe detection

* inverse dupe detection

* performance test

* remove deprecated apis

* disable query result cache

* Revert "disable query result cache"

This reverts commit 4aed05a.

* Revert "performance test"

This reverts commit 014d256.

* refactor rawg recache

* refactor logging pt. 1

* refactor logging pt. 2

* refactor logging pt. 3

* No Stack needed if contained in err-obj

* update dependencies

* linting

* disable numseperator

* fix deleted games bookmark bug

* clean imports

* fix bookmarking bug

* implement stream-slicing using range-header on downloads

* accept ranges header

* syntax check

* fix stream slicer

* increment version

* fix range content length

* refactor

* refactor

* document header

* fix range header

* fix start end logic

* fix logic again

* fix this damn logic PLEASE

* log header

* add texts

* update deps

* fix linter

* fix config parser #286

* support tls Add encryption support for PostgreSQL Client #285

* fix namings

* support for tls db conns

* support for rawg-to-steam-redirect

* dont skip cache check without api-key when using rawg2steam

* fix logger

* Release 12.1.0
  • Loading branch information
Alfagun74 authored May 6, 2024
1 parent 5480a18 commit 7d8e13b
Show file tree
Hide file tree
Showing 107 changed files with 2,285 additions and 517 deletions.
7 changes: 6 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ module.exports = {
parserOptions: {
project: "tsconfig.json",
sourceType: "module",
ecmaVersion: "latest"
},
plugins: ["@typescript-eslint/eslint-plugin"],
plugins: ["@typescript-eslint/eslint-plugin", "simple-import-sort"],
extends: [
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
Expand All @@ -17,4 +18,8 @@ module.exports = {
jest: true,
},
ignorePatterns: [".eslintrc.js"],
rules: {
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error"
},
};
3 changes: 1 addition & 2 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"singleQuote": false,
"endOfLine": "auto",
"plugins": ["prettier-plugin-jsdoc"]
"endOfLine": "auto"
}
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# GameVault Backend Server Changelog

## 12.1.0

Recommended Gamevault App Version: `v1.10.0.0`

### Changes

- Support for Pausing and Resuming Downloads by implementing HTTP `Range` Header [#14](https://github.com/Phalcode/gamevault-backend/issues/14)
- Fixed a Bug where deleted bookmarked games would break bookmarking for users who had them bookmarked.
- Formatted imports in entire codebase and refactored project structure
- Made Rotating File logger handle TESTING_MOCK_FILES environment variable
- Fixed a bug where the Config parser would not accept 0 as a value. [#286](https://github.com/Phalcode/gamevault-backend/issues/286)
- Added support for TLS Encrypted Postgres connections [#285](https://github.com/Phalcode/gamevault-backend/issues/285)
- Support for [Rawg2Steam](https://github.com/Phalcode/rawg-to-steam-redirect) API (Removed Reliance on Rawg IDs and their API KEY, and implemented support for RawgToSteam Box Images)

### Thanks

- @wieluk
- @hostmatic

## 12.0.0

Recommended Gamevault App Version: `v1.9.2.0`
Expand Down
15 changes: 8 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gamevault-backend",
"version": "12.0.0",
"version": "12.1.0",
"description": "the self-hosted gaming platform for drm-free games",
"author": "Alkan Alper, Schäfer Philip GbR / Phalcode",
"private": true,
Expand Down Expand Up @@ -41,7 +41,7 @@
"async-g-i-s": "^1.5.2",
"axios": "^1.6.8",
"bcrypt": "^5.1.1",
"better-sqlite3": "^9.5.0",
"better-sqlite3": "^9.6.0",
"builder-pattern": "^2.2.0",
"chokidar": "^3.6.0",
"class-transformer": "^0.5.1",
Expand Down Expand Up @@ -90,18 +90,19 @@
"@types/mime": "^3.0.4",
"@types/morgan": "^1.9.9",
"@types/multer": "^1.4.11",
"@types/node": "^20.12.7",
"@types/node": "^20.12.8",
"@types/node-7z": "^2.1.8",
"@types/passport-http": "^0.3.11",
"@types/pg": "^8.11.5",
"@types/pg": "^8.11.6",
"@types/string-similarity": "^4.0.2",
"@types/unidecode": "^0.1.3",
"@typescript-eslint/eslint-plugin": "^7.7.1",
"@typescript-eslint/parser": "^7.7.1",
"eslint": "^8.57.0",
"@typescript-eslint/eslint-plugin": "^7.8.0",
"@typescript-eslint/parser": "^7.8.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-simple-import-sort": "^12.1.0",
"jest": "^29.7.0",
"prettier": "^3.2.5",
"prettier-plugin-jsdoc": "^1.3.0",
Expand Down
294 changes: 153 additions & 141 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

35 changes: 18 additions & 17 deletions src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import { PluginModule } from "./modules/plugin/plugin.module";
import { Module } from "@nestjs/common";
import { APP_INTERCEPTOR } from "@nestjs/core";
import { EventEmitterModule } from "@nestjs/event-emitter";
import { ScheduleModule } from "@nestjs/schedule";

import { DisableApiIfInterceptor } from "./interceptors/disable-api-if.interceptor";
import { AdminModule } from "./modules/admin/admin.module";
import { DatabaseModule } from "./modules/database/database.module";
import { FilesModule } from "./modules/files/files.module";
import { BoxartsModule } from "./modules/boxarts/boxarts.module";
import { DatabaseModule } from "./modules/database/database.module";
import { DevelopersModule } from "./modules/developers/developers.module";
import { FilesModule } from "./modules/files/files.module";
import { GamesModule } from "./modules/games/games.module";
import { GarbageCollectionModule } from "./modules/garbage-collection/garbage-collection.module";
import { GenresModule } from "./modules/genres/genres.module";
import { DefaultStrategy } from "./modules/guards/basic-auth.strategy";
import { HealthModule } from "./modules/health/health.module";
import { ImagesModule } from "./modules/images/images.module";
import { PluginModule } from "./modules/plugins/plugin.module";
import { ProgressModule } from "./modules/progresses/progress.module";
import { RawgModule } from "./modules/providers/rawg/rawg.module";
import { PublishersModule } from "./modules/publishers/publishers.module";
import { StoresModule } from "./modules/stores/stores.module";
import { UsersModule } from "./modules/users/users.module";
import { TagsModule } from "./modules/tags/tags.module";
import { ProgressModule } from "./modules/progress/progress.module";
import { ImagesModule } from "./modules/images/images.module";
import { HealthModule } from "./modules/health/health.module";
import { GenresModule } from "./modules/genres/genres.module";
import { GamesModule } from "./modules/games/games.module";
import { Module } from "@nestjs/common";
import { ScheduleModule } from "@nestjs/schedule";
import { RawgModule } from "./modules/providers/rawg/rawg.module";
import { DefaultStrategy } from "./modules/guards/basic-auth.strategy";
import { GarbageCollectionModule } from "./modules/garbage-collection/garbage-collection.module";
import { EventEmitterModule } from "@nestjs/event-emitter";
import { APP_INTERCEPTOR } from "@nestjs/core";
import { DisableApiIfInterceptor } from "./interceptors/disable-api-if.interceptor";
import { UsersModule } from "./modules/users/users.module";

@Module({
imports: [
Expand Down
51 changes: 40 additions & 11 deletions src/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import globals from "./globals";
import packageJson from "../package.json";
import globals from "./globals";

function parseBooleanEnvVariable(
environmentVariable: string,
Expand Down Expand Up @@ -38,6 +38,17 @@ function parseList(
: defaultList;
}

function parseNumber(
environmentVariable: string,
defaultValue?: number,
): number | undefined {
const number = Number(environmentVariable);
if (isNaN(number) || number < 0 || number > Number.MAX_SAFE_INTEGER) {
return defaultValue ?? undefined;
}
return number;
}

function parseNumberList(
environmentVariable: string,
defaultList: number[] = [],
Expand All @@ -63,7 +74,7 @@ function parseKibibytesToBytes(

const configuration = {
SERVER: {
PORT: Number(process.env.SERVER_PORT) || 8080,
PORT: parseNumber(process.env.SERVER_PORT, 8080),
VERSION: process.env.npm_package_version || packageJson.version,
DEMO_MODE_ENABLED: parseBooleanEnvVariable(
process.env.SERVER_DEMO_MODE_ENABLED,
Expand Down Expand Up @@ -106,17 +117,29 @@ const configuration = {
DB: {
SYSTEM: process.env.DB_SYSTEM || "POSTGRESQL",
HOST: process.env.DB_HOST || "localhost",
PORT: Number(process.env.DB_PORT) || 5432,
PORT: parseNumber(process.env.DB_PORT, 5432),
USERNAME: process.env.DB_USERNAME || "default",
PASSWORD: process.env.DB_PASSWORD || "default",
DATABASE: process.env.DB_DATABASE || "gamevault",
DEBUG: parseBooleanEnvVariable(process.env.DB_DEBUG),
SYNCHRONIZE: parseBooleanEnvVariable(process.env.DB_SYNCHRONIZE),
TLS: {
ENABLED: parseBooleanEnvVariable(process.env.DB_TLS_ENABLED),
REJECT_UNAUTHORIZED_ENABLED: parseBooleanEnvVariable(
process.env.DB_TLS_REJECT_UNAUTHORIZED_ENABLED,
),
KEY_PATH: parsePath(process.env.DB_TLS_KEY_PATH, ""),
CERTIFICATE_PATH: parsePath(process.env.DB_TLS_CERTIFICATE_PATH, ""),
CA_CERTIFICATE_PATH: parsePath(
process.env.DB_TLS_CA_CERTIFICATE_PATH,
"",
),
},
} as const,
RAWG_API: {
URL: process.env.RAWG_API_URL || "https://api.rawg.io/api",
KEY: process.env.RAWG_API_KEY || "",
CACHE_DAYS: Number(process.env.RAWG_API_CACHE_DAYS) || 30,
CACHE_DAYS: parseNumber(process.env.RAWG_API_CACHE_DAYS, 30),
INCLUDED_STORES: parseNumberList(
process.env.RAWG_API_INCLUDED_STORES,
globals.DEFAULT_INCLUDED_RAWG_STORES,
Expand All @@ -136,8 +159,10 @@ const configuration = {
),
} as const,
GAMES: {
INDEX_INTERVAL_IN_MINUTES:
Number(process.env.GAMES_INDEX_INTERVAL_IN_MINUTES) || 60,
INDEX_INTERVAL_IN_MINUTES: parseNumber(
process.env.GAMES_INDEX_INTERVAL_IN_MINUTES,
60,
),
SUPPORTED_FILE_FORMATS: parseList(
process.env.GAMES_SUPPORTED_FILE_FORMATS,
globals.SUPPORTED_FILE_FORMATS,
Expand All @@ -149,16 +174,20 @@ const configuration = {
} as const,
IMAGE: {
MAX_SIZE_IN_KB:
Number(process.env.IMAGE_MAX_SIZE_IN_KB) * 1000 || 10_000_000,
GOOGLE_API_RATE_LIMIT_COOLDOWN_IN_HOURS:
Number(process.env.IMAGE_GOOGLE_API_RATE_LIMIT_COOLDOWN_IN_HOURS) || 24,
parseNumber(process.env.IMAGE_MAX_SIZE_IN_KB, 1000) * 10_000,
GOOGLE_API_RATE_LIMIT_COOLDOWN_IN_HOURS: parseNumber(
process.env.IMAGE_GOOGLE_API_RATE_LIMIT_COOLDOWN_IN_HOURS,
24,
),
SUPPORTED_IMAGE_FORMATS: parseList(
process.env.GAMES_SUPPORTED_IMAGE_FORMATS,
globals.SUPPORTED_IMAGE_FORMATS,
),
GC_DISABLED: parseBooleanEnvVariable(process.env.IMAGE_GC_DISABLED, false),
GC_INTERVAL_IN_MINUTES:
Number(process.env.IMAGE_GC_INTERVAL_IN_MINUTES) || 60,
GC_INTERVAL_IN_MINUTES: parseNumber(
process.env.IMAGE_GC_INTERVAL_IN_MINUTES,
24,
),
} as const,
TESTING: {
AUTHENTICATION_DISABLED: parseBooleanEnvVariable(
Expand Down
1 change: 1 addition & 0 deletions src/decorators/conditional-registration.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { noop } from "rxjs";

import configuration from "../configuration";
import { Public } from "./public.decorator";

Expand Down
1 change: 1 addition & 0 deletions src/decorators/disable-api-if.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SetMetadata } from "@nestjs/common";

export const DISABLE_API_IF_KEY = "disableApiIf";
export const DisableApiIf = (disabled: boolean) =>
SetMetadata(DISABLE_API_IF_KEY, disabled);
1 change: 1 addition & 0 deletions src/decorators/minimum-role.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SetMetadata } from "@nestjs/common";

import { Role } from "../modules/users/models/role.enum";

export const MINIMUM_ROLE_KEY = "minimumRole";
Expand Down
1 change: 1 addition & 0 deletions src/decorators/public.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
import { SetMetadata } from "@nestjs/common";

export const Public = () => SetMetadata("public", true);
File renamed without changes.
5 changes: 2 additions & 3 deletions src/filters/http-exception.filter.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import {
ExceptionFilter,
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
ArgumentsHost,
Logger,
} from "@nestjs/common";

import { Request, Response } from "express";

@Catch()
Expand Down
28 changes: 28 additions & 0 deletions src/globals.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,34 @@
import { applyDecorators, Type } from "@nestjs/common";
import { ApiExtraModels, ApiOkResponse, getSchemaPath } from "@nestjs/swagger";

import { PaginatedEntity } from "./modules/database/models/paginated-entity.model";
import { RawgPlatform } from "./modules/providers/rawg/models/platforms";
import { RawgStore } from "./modules/providers/rawg/models/stores";

export const ApiOkResponsePaginated = <DataDto extends Type<unknown>>(
dataDto: DataDto,
) =>
applyDecorators(
ApiExtraModels(PaginatedEntity, dataDto),
ApiOkResponse({
schema: {
required: ["data", "meta", "links"],
allOf: [
{
properties: {
data: {
description: "paginated list of entities",
type: "array",
items: { $ref: getSchemaPath(dataDto) },
},
},
},
{ $ref: getSchemaPath(PaginatedEntity) },
],
},
}),
);

export default {
get SUPPORTED_FILE_FORMATS() {
return [...this.ARCHIVE_FORMATS, ...this.EXECUTABLE_FORMATS];
Expand Down
9 changes: 5 additions & 4 deletions src/interceptors/disable-api-if.interceptor.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
ExecutionContext,
Injectable,
MethodNotAllowedException,
NestInterceptor,
} from "@nestjs/common";
import { Reflector } from "@nestjs/core";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
import { Reflector } from "@nestjs/core";

import { DISABLE_API_IF_KEY } from "../decorators/disable-api-if.decorator";

@Injectable()
Expand Down
5 changes: 4 additions & 1 deletion src/logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ if (configuration.SERVER.LOG_LEVEL != "off") {
);
}

if (configuration.SERVER.LOG_FILES_ENABLED) {
if (
configuration.SERVER.LOG_FILES_ENABLED &&
!configuration.TESTING.MOCK_FILES
) {
transports.push(
new DailyRotateFile({
level: "debug",
Expand Down
11 changes: 7 additions & 4 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
/* eslint-disable */
import * as dotenv from "dotenv";
dotenv.config();
/* eslint-enable */

import { ValidationPipe } from "@nestjs/common";
import { NestFactory, Reflector } from "@nestjs/core";
import { NestExpressApplication } from "@nestjs/platform-express";
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
import compression from "compression";
//import { AsyncApiDocumentBuilder, AsyncApiModule } from "nestjs-asyncapi";
import cookieparser from "cookie-parser";
import compression from "compression";
import helmet from "helmet";
import morgan from "morgan";

import { AppModule } from "./app.module";
import configuration, { getCensoredConfiguration } from "./configuration";
import { LoggingExceptionFilter } from "./filters/http-exception.filter";
import { default as logger, default as winston, stream } from "./logging";
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
import { ApiVersionMiddleware } from "./middleware/remove-api-version.middleware";
import { AuthenticationGuard } from "./modules/guards/authentication.guard";
import { AuthorizationGuard } from "./modules/guards/authorization.guard";
import { LoggingExceptionFilter } from "./filters/http-exception.filter";
import { ApiVersionMiddleware } from "./middleware/remove-api-version.middleware";

async function bootstrap(): Promise<void> {
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
Expand Down
2 changes: 1 addition & 1 deletion src/middleware/remove-api-version.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Injectable, NestMiddleware } from "@nestjs/common";
import { Request, Response, NextFunction } from "express";
import { NextFunction, Request, Response } from "express";

@Injectable()
export class ApiVersionMiddleware implements NestMiddleware {
Expand Down
Loading

0 comments on commit 7d8e13b

Please sign in to comment.