server side asset rendering example, totp challenge start

This commit is contained in:
Evert Prants 2023-08-14 21:56:11 +03:00
parent ae51916e06
commit 048775899b
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
21 changed files with 767 additions and 89 deletions

View File

@ -3,19 +3,35 @@ import { AuthService } from './services/auth.service';
import { MessagePattern } from '@nestjs/microservices';
import { LoginRequest } from './interfaces/auth.interface';
import { UserInfo } from '@freeblox/shared';
import { AuthChallenge } from './enums/challenge.enum';
@Controller()
export class AuthController {
constructor(private readonly authService: AuthService) {}
@MessagePattern('auth.login')
login({ body }: { body: LoginRequest }) {
return this.authService.login(body);
login({ body, ip }: { body: LoginRequest; ip: string }) {
return this.authService.login(body, ip);
}
@MessagePattern('auth.loginByRefreshToken')
loginByRefreshToken({ token }: { token: string }) {
return this.authService.loginByRefreshToken(token);
loginByRefreshToken({ token, ip }: { token: string; ip: string }) {
return this.authService.loginByRefreshToken(token, ip);
}
@MessagePattern('auth.loginByChallenge')
loginByChallenge({
challenge,
secret,
body,
ip,
}: {
challenge: AuthChallenge;
secret: string;
body: Record<string, string>;
ip: string;
}) {
return this.authService.loginByChallenge(challenge, secret, body, ip);
}
@MessagePattern('auth.verify')

View File

@ -2,7 +2,13 @@ import { Module, OnModuleInit } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { AuthService } from './services/auth.service';
import { ClientsModule } from '@nestjs/microservices';
import { makeKnex, makeTypeOrm, natsClient } from '@freeblox/shared';
import {
getCacheManager,
getTypeOrm,
makeKnex,
makeTypeOrm,
natsClient,
} from '@freeblox/shared';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import knex from 'knex';
@ -33,14 +39,8 @@ const entities = [
ignoreEnvFile: process.env.NODE_ENV === 'development',
load: [makeKnex('auth', __dirname), makeTypeOrm('auth'), security],
}),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
...config.get('typeorm'),
entities,
}),
}),
getTypeOrm(entities),
getCacheManager(),
TypeOrmModule.forFeature(entities),
ClientsModule.register([natsClient('auth')]),
],

View File

@ -0,0 +1,3 @@
export enum AuthChallenge {
OTP = 'OTP',
}

View File

@ -1,4 +1,4 @@
import { Injectable } from '@nestjs/common';
import { Inject, Injectable } from '@nestjs/common';
import { LoginRequest } from '../interfaces/auth.interface';
import { JWTService } from './jwt.service';
import { ILike, Repository } from 'typeorm';
@ -11,12 +11,15 @@ import { BanService } from './ban.service';
import {
BadRequestRpcException,
ForbiddenRpcException,
PreconditionFailedRpcException,
UserInfo,
generateString,
} from '@freeblox/shared';
import { RoleService } from './role.service';
import { ConfigService } from '@nestjs/config';
import { RefreshService } from './refresh.service';
import { AuthChallenge } from '../enums/challenge.enum';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
@Injectable()
export class AuthService {
@ -29,6 +32,7 @@ export class AuthService {
@InjectRepository(UserEntity)
private readonly userRepository: Repository<UserEntity>,
private readonly config: ConfigService,
@Inject(CACHE_MANAGER) private cache: Cache,
) {}
/**
@ -36,7 +40,7 @@ export class AuthService {
* @param body Username/email and password
* @returns JWT token
*/
async login(body: LoginRequest) {
async login(body: LoginRequest, ip: string) {
if (!body.email || !body.password) {
throw new BadRequestRpcException('Invalid username or password');
}
@ -68,20 +72,9 @@ export class AuthService {
}
// Check TOTP
const userOTPToken = await this.otpService.getUserTOTP(userEntity);
const userOTPToken = await this.otpService.userHasTOTP(userEntity);
if (userOTPToken) {
if (!body.totpToken) {
throw new PreconditionFailedRpcException('TOTP Token required');
}
const validate = this.otpService.validateTOTP(
userOTPToken.token,
body.totpToken,
);
if (!validate) {
throw new ForbiddenRpcException('Invalid TOTP Token');
}
return this.createOTPChallenge(userEntity);
}
// Issue access token
@ -100,13 +93,104 @@ export class AuthService {
);
return {
token: issuedToken,
refresh: refreshToken,
expires_in: exp,
challenge: null,
challengeSecret: null,
authResult: {
token: issuedToken,
refresh: refreshToken,
expires_in: exp,
},
};
}
async loginByRefreshToken(token: string) {
/**
* Login by challenge.
* @param challenge Challenge type
* @param token Challenge secret
* @param response Challenge response from user
* @returns Authentication result or another challenge
*/
async loginByChallenge(
challenge: AuthChallenge,
token: string,
response: Record<string, string>,
ip: string,
) {
const decrypted = await this.jwtService.decrypt(token);
const tokenKey = `chlg:${challenge}:${decrypted.token}`;
const challengeSubject = await this.cache.get<string>(tokenKey);
// Invalid token
if (!challengeSubject) {
throw new ForbiddenRpcException('Invalid challenge response');
}
// Wrong subject, somehow..
if (challengeSubject !== decrypted.sub) {
await this.cache.del(tokenKey);
throw new ForbiddenRpcException('Invalid challenge response');
}
const loginUser = await this.userRepository.findOneByOrFail({
id: challengeSubject,
});
// TOTP verification login
if (challenge === AuthChallenge.OTP) {
// If no code is provided, assume request is incorrect
// and delete the challenge from cache.
if (!response.code) {
await this.cache.del(tokenKey);
throw new ForbiddenRpcException('Invalid challenge response');
}
const userOTPToken = await this.otpService.getUserTOTP(loginUser);
// User does not actually have TOTP?
if (!userOTPToken) {
await this.cache.del(tokenKey);
throw new ForbiddenRpcException('Invalid challenge response');
}
const validate = this.otpService.validateTOTP(
userOTPToken.token,
response.code,
);
// Here we do not delete from the cache, let the user retry.
if (!validate) {
throw new ForbiddenRpcException('Invalid challenge response');
}
} else {
await this.cache.del(tokenKey);
throw new ForbiddenRpcException('Invalid challenge');
}
await this.cache.del(tokenKey);
// Issue access and refresh token
const exp = this.config.get('security.jwtTokenExpiry');
const issuedToken = await this.issueAccessToken(loginUser);
const refreshToken = await this.refreshService.issueRefreshToken(loginUser);
// Set login time to now
await this.userRepository.update(
{ id: loginUser.id },
{ loginAt: new Date() },
);
return {
challenge: null,
challengeSecret: null,
authResult: {
token: issuedToken,
refresh: refreshToken,
expires_in: exp,
},
};
}
async loginByRefreshToken(token: string, ip: string) {
const refreshToken = await this.refreshService.useRefreshToken(token);
const userEntity = refreshToken.user;
@ -121,9 +205,13 @@ export class AuthService {
);
return {
token: issuedToken,
refresh: refreshToken.token,
expires_in: exp,
challenge: null,
challengeSecret: null,
authResult: {
token: issuedToken,
refresh: refreshToken.token,
expires_in: exp,
},
};
}
@ -193,21 +281,51 @@ export class AuthService {
(privileges, ban) => [...privileges, ...(ban.privileges || [])],
[],
);
const privileges = await this.roleService.getUserPrivileges(
const userPrivileges = await this.roleService.getUserPrivileges(
user,
bannedPrivileges,
);
// If we only have privilege bans, only restrict those privileges.
// If we have a system ban, only return the web privilege.
const privileges =
!banned || bans.every((ban) => ban.privilegeBan)
? userPrivileges
: ['web'];
// Issue new token
return this.jwtService.sign({
sub: user.id,
username: user.username,
display_name: user.displayName,
language: user.language,
banned: banned,
privileges: banned
? []
: privileges.map((privilege) => privilege.privilege),
banned,
privileges,
});
}
/**
* Create OTP challenge for user's login.
* @private
* @param userEntity User
* @returns Challenge response
*/
async createOTPChallenge(userEntity: UserEntity) {
// Create challenge for auth.
const challengeToken = generateString(256);
const tokenKey = `chlg:${AuthChallenge.OTP}:${challengeToken}`;
const encryptedToken = await this.jwtService.encrypt({
token: challengeToken,
sub: userEntity.id,
});
// Challenge is only valid for 5 minutes.
await this.cache.set(tokenKey, userEntity.id, 300000);
return {
challenge: AuthChallenge.OTP,
challengeSecret: encryptedToken,
authResult: null,
};
}
}

View File

@ -7,10 +7,15 @@ import { AuthModule } from './services/auth/auth.module';
import { CatalogModule } from './services/catalog/catalog.module';
import { UserMiddleware } from './middleware/user.middleware';
import { AssetsModule } from './services/assets/assets.module';
import { ThrottlerModule } from '@nestjs/throttler';
@Module({
imports: [
ClientsModule.register([natsClient('auth')]),
ThrottlerModule.forRoot({
ttl: 60,
limit: 10,
}),
AuthModule,
CatalogModule,
AssetsModule,

View File

@ -4,6 +4,7 @@ import {
Controller,
Get,
Inject,
Ip,
Post,
UseGuards,
UseInterceptors,
@ -21,8 +22,10 @@ import { UserInfo } from '@freeblox/shared';
import { lastValueFrom } from 'rxjs';
import { AuthGuard } from '../../guards/auth.guard';
import { UserDto } from './dtos/user.dto';
import { LoginResponseDto } from './dtos/login-response.dto';
import { ChallengeResponseDto } from './dtos/challenge-response.dto';
import { LoginByRefreshTokenDto } from './dtos/login-refresh-token.dto';
import { ChallengeRequestDto } from './dtos/challenge-request.dto';
import { Throttle } from '@nestjs/throttler';
@Controller({
version: '1',
@ -35,17 +38,35 @@ export class AuthController {
constructor(@Inject('auth') private auth: ClientProxy) {}
@Post('login')
@Throttle(3, 60)
@ApiOperation({ summary: 'Login by username or email and password' })
@ApiOkResponse({ type: LoginResponseDto })
async login(@Body() body: LoginDto) {
return this.auth.send('auth.login', { body });
@ApiOkResponse({ type: ChallengeResponseDto })
async login(@Body() body: LoginDto, @Ip() ip: string) {
return this.auth.send('auth.login', { body, ip });
}
@Post('challenge')
@Throttle(3, 60)
@ApiOperation({ summary: 'Login by challenge' })
@ApiOkResponse({ type: ChallengeResponseDto })
async challenge(@Body() body: ChallengeRequestDto, @Ip() ip: string) {
return this.auth.send('auth.loginByChallenge', {
challenge: body.challenge,
secret: body.secret,
body: body.body,
ip,
});
}
@Post('refresh')
@Throttle(3, 60)
@ApiOperation({ summary: 'Login by refresh token' })
@ApiOkResponse({ type: LoginResponseDto })
async refresh(@Body() body: LoginByRefreshTokenDto) {
return this.auth.send('auth.loginByRefreshToken', { token: body.token });
@ApiOkResponse({ type: ChallengeResponseDto })
async refresh(@Body() body: LoginByRefreshTokenDto, @Ip() ip: string) {
return this.auth.send('auth.loginByRefreshToken', {
token: body.token,
ip,
});
}
@Get('me')

View File

@ -0,0 +1,13 @@
import { ApiProperty } from '@nestjs/swagger';
import { AuthChallenge } from 'apps/auth/src/enums/challenge.enum';
export class ChallengeRequestDto {
@ApiProperty({ type: String, enum: AuthChallenge })
challenge: AuthChallenge;
@ApiProperty()
secret: string;
@ApiProperty()
body: Record<string, string>;
}

View File

@ -0,0 +1,24 @@
import { ApiProperty } from '@nestjs/swagger';
import { AuthChallenge } from '../../../../../auth/src/enums/challenge.enum';
export class AuthResultResponse {
@ApiProperty()
token: string;
@ApiProperty()
refresh: string;
@ApiProperty()
expires_in: number;
}
export class ChallengeResponseDto {
@ApiProperty({ type: String, enum: AuthChallenge, nullable: true })
challenge: AuthChallenge;
@ApiProperty({ nullable: true })
challengeSecret: string;
@ApiProperty({ type: AuthResultResponse, nullable: true })
authResult: AuthResultResponse;
}

View File

@ -1,12 +0,0 @@
import { ApiProperty } from '@nestjs/swagger';
export class LoginResponseDto {
@ApiProperty()
token: string;
@ApiProperty()
refresh: string;
@ApiProperty()
expires_in: number;
}

View File

@ -24,6 +24,7 @@ import { lastValueFrom } from 'rxjs';
import { CategoryResponseDto } from './dtos/category-response.dto';
import { ContentAssetDto } from './dtos/content-asset.dto';
import { RequirePrivileges } from '../../decorators/require-privileges.decorator';
import { Throttle } from '@nestjs/throttler';
@Controller({
version: '1',
@ -42,6 +43,7 @@ export class CatalogController {
}
@Post('content')
@Throttle(3, 60)
@ApiOperation({ summary: 'Create new content' })
@ApiOkResponse({ type: ContentResponseDto })
@RequirePrivileges('create:*', 'contentedit')
@ -83,6 +85,7 @@ export class CatalogController {
}
@Patch('content/:id')
@Throttle(3, 60)
@ApiOperation({ summary: 'Update content details' })
@ApiOkResponse({ type: ContentResponseDto })
@RequirePrivileges('create:*', 'contentedit')
@ -95,6 +98,7 @@ export class CatalogController {
}
@Post('content/:id/revision')
@Throttle(3, 60)
@ApiOperation({
summary: 'Create a new revision (upload new content for item)',
})
@ -115,6 +119,7 @@ export class CatalogController {
}
@Patch('content/:id/publish')
@Throttle(3, 60)
@ApiOkResponse({ type: ContentResponseDto })
@ApiOperation({
summary: 'Publish content',
@ -129,4 +134,9 @@ export class CatalogController {
user,
});
}
@Post('test')
async test() {
return this.catalog.send('render.assetid', {});
}
}

View File

@ -1,7 +1,13 @@
import { Controller } from '@nestjs/common';
import { RenderService } from './services/render.service';
import { MessagePattern } from '@nestjs/microservices';
@Controller()
export class RenderController {
constructor(private readonly renderService: RenderService) {}
@MessagePattern('render.assetid')
async renderAsset({ id }: { id: string }) {
return this.renderService.render();
}
}

View File

@ -0,0 +1,48 @@
import {
Engine,
EngineEvents,
EnvironmentComponent,
EventEmitter,
LevelComponent,
ViewportComponent,
WebGLRenderer,
instanceCharacterObject,
} from '@freeblox/engine';
import { Vector2, Vector3 } from 'three';
export class BarebonesRenderer extends Engine {
public events = new EventEmitter<EngineEvents>();
override mount(element: HTMLElement): void {
this.element = element;
this.render = new WebGLRenderer(element, new Vector2(800, 800), {
alpha: true,
});
this.render.renderer.autoClear = false;
this.use(ViewportComponent);
this.use(EnvironmentComponent);
this.use(LevelComponent);
this.getComponent(ViewportComponent).setSize(800, 800);
}
}
(async function () {
const engine = new BarebonesRenderer();
const div = document.createElement('div');
document.body.appendChild(div);
document.body.style.margin = '0';
document.body.style.padding = '0';
engine.mount(div);
const level = engine.getComponent(LevelComponent);
const viewport = engine.getComponent(ViewportComponent);
const char = await instanceCharacterObject('test');
level.world.add(char);
viewport.camera.position.set(2, 6, 5);
viewport.camera.lookAt(new Vector3(0, 3.5, 0));
engine.render.renderer.setClearColor(0x000000, 0);
engine.render.render();
engine.stop();
})();

View File

@ -1,4 +1,53 @@
import { Injectable } from '@nestjs/common';
import { Injectable, Logger } from '@nestjs/common';
import { readFileSync } from 'fs';
import { join } from 'path';
import puppeteer from 'puppeteer';
@Injectable()
export class RenderService {}
export class RenderService {
private logger = new Logger(RenderService.name);
async render() {
const file = readFileSync(join(__dirname, 'renderer.js'), 'utf-8');
const browser = await puppeteer.launch({
headless: true,
args: [
'--use-gl=swiftshader',
'--no-sandbox',
'--enable-surface-synchronization',
],
});
const page = await browser.newPage();
await page.setViewport({ width: 800, height: 800 });
page
.on('console', (message) =>
this.logger.log(`${message.type().toUpperCase()} ${message.text()}`),
)
.on('pageerror', ({ message }) => this.logger.log(message))
.on('response', (response) =>
this.logger.log(`${response.status()} ${response.url()}`),
)
.on('requestfailed', (request) =>
this.logger.log(`${request.failure().errorText} ${request.url()}`),
);
await page.setContent(
`<html><body><script type="application/javascript">${file}</script></body></html>`,
{
waitUntil: ['load', 'networkidle0'],
},
);
const picture = await page.screenshot({
encoding: 'base64',
omitBackground: true,
});
await browser.close();
return picture;
}
}

View File

@ -21,6 +21,13 @@ services:
- ./docker/postgres:/docker-entrypoint-initdb.d
networks:
- fblx
redis:
container_name: fblx-redis
image: redis:7-alpine
volumes:
- fblx-redis:/data
networks:
- fblx
auth:
container_name: fblx-auth
build:
@ -28,6 +35,10 @@ services:
dockerfile: Dockerfile.dev
args:
- SERVICE=auth
depends_on:
- nats
- postgres
- redis
networks:
- fblx
environment:
@ -49,6 +60,10 @@ services:
dockerfile: Dockerfile.dev
args:
- SERVICE=catalog
depends_on:
- nats
- postgres
- redis
networks:
- fblx
environment:
@ -66,6 +81,10 @@ services:
dockerfile: Dockerfile.dev
args:
- SERVICE=game
depends_on:
- nats
- postgres
- redis
networks:
- fblx
environment:
@ -83,6 +102,10 @@ services:
dockerfile: Dockerfile.dev
args:
- SERVICE=player
depends_on:
- nats
- postgres
- redis
networks:
- fblx
environment:
@ -100,6 +123,10 @@ services:
dockerfile: Dockerfile.dev
args:
- SERVICE=server
depends_on:
- nats
- postgres
- redis
networks:
- fblx
environment:
@ -117,6 +144,10 @@ services:
dockerfile: Dockerfile.dev
args:
- SERVICE=session
depends_on:
- nats
- postgres
- redis
networks:
- fblx
environment:
@ -134,6 +165,10 @@ services:
dockerfile: Dockerfile.dev
args:
- SERVICE=bank
depends_on:
- nats
- postgres
- redis
networks:
- fblx
environment:
@ -151,6 +186,10 @@ services:
dockerfile: Dockerfile.dev
args:
- SERVICE=assets
depends_on:
- nats
- postgres
- redis
networks:
- fblx
environment:
@ -171,6 +210,9 @@ services:
build:
context: .
dockerfile: Dockerfile.render.dev
depends_on:
- nats
- redis
networks:
- fblx
environment:
@ -185,6 +227,8 @@ services:
dockerfile: Dockerfile.dev
args:
- SERVICE=freeblox-web-service
depends_on:
- nats
networks:
- fblx
ports:
@ -224,3 +268,4 @@ networks:
fblx:
volumes:
fblx-pgadmin:
fblx-redis:

14
libs/shared/src/cache/cache-manager.ts vendored Normal file
View File

@ -0,0 +1,14 @@
import { CacheModule } from '@nestjs/cache-manager';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { redisConfig } from './redis.provider';
import * as redisStore from 'cache-manager-redis-store';
export const getCacheManager = () =>
CacheModule.registerAsync({
imports: [ConfigModule, ConfigModule.forFeature(redisConfig)],
useFactory: (config) => ({
store: redisStore,
...config.get('redis'),
}),
inject: [ConfigService],
});

View File

@ -0,0 +1,6 @@
import { registerAs } from '@nestjs/config';
export const redisConfig = registerAs('redis', () => ({
host: String(process.env.REDIS_HOST || 'redis'),
port: Number(process.env.REDIS_PORT) || 6379,
}));

View File

@ -1,5 +1,5 @@
import { registerAs } from '@nestjs/config';
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import { ConfigModule, ConfigService, registerAs } from '@nestjs/config';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
export const makeTypeOrm = (database: string) =>
registerAs(
@ -14,3 +14,13 @@ export const makeTypeOrm = (database: string) =>
database,
} as TypeOrmModuleOptions),
);
export const getTypeOrm = (entities?: any[]) =>
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
...config.get('typeorm'),
entities,
}),
});

View File

@ -12,3 +12,5 @@ export * from './types/userinfo';
export * from './types/page-query.interface';
export * from './exception/rpc.exception';
export * from './filters/rpc-exception.filter';
export * from './cache/redis.provider';
export * from './cache/cache-manager';

View File

@ -28,16 +28,22 @@
"@aws-sdk/s3-request-presigner": "^3.375.0",
"@aws-sdk/url-parser": "^3.374.0",
"@aws-sdk/util-format-url": "^3.370.0",
"@freeblox/engine": "^0.0.4",
"@nestjs/axios": "^3.0.0",
"@nestjs/cache-manager": "^2.1.0",
"@nestjs/common": "^10.0.3",
"@nestjs/config": "^3.0.0",
"@nestjs/core": "^10.0.3",
"@nestjs/microservices": "^10.0.3",
"@nestjs/platform-express": "^10.0.3",
"@nestjs/schedule": "^3.0.1",
"@nestjs/swagger": "^7.0.11",
"@nestjs/throttler": "^4.2.1",
"@nestjs/typeorm": "^10.0.0",
"axios": "^1.4.0",
"bcrypt": "^5.1.0",
"cache-manager": "^5.2.3",
"cache-manager-redis-store": "^3.0.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"jose": "^4.14.4",
@ -53,13 +59,15 @@
"rxjs": "^7.8.1",
"three": "^0.154.0",
"typeorm": "^0.3.17",
"uuid": "^9.0.0"
"uuid": "^9.0.0",
"webpack-merge": "^5.9.0"
},
"devDependencies": {
"@nestjs/cli": "^10.0.5",
"@nestjs/schematics": "^10.0.1",
"@nestjs/testing": "^10.0.3",
"@types/bcrypt": "^5.0.0",
"@types/cron": "^2.0.1",
"@types/express": "^4.17.17",
"@types/jest": "29.5.2",
"@types/multer": "^1.4.7",
@ -106,4 +114,4 @@
"^@freeblox/shared(|/.*)$": "<rootDir>/libs/shared/src/$1"
}
}
}
}

272
pnpm-lock.yaml generated
View File

@ -26,9 +26,15 @@ dependencies:
'@aws-sdk/util-format-url':
specifier: ^3.370.0
version: 3.370.0
'@freeblox/engine':
specifier: ^0.0.4
version: 0.0.4
'@nestjs/axios':
specifier: ^3.0.0
version: 3.0.0(@nestjs/common@10.0.3)(axios@1.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/cache-manager':
specifier: ^2.1.0
version: 2.1.0(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(cache-manager@5.2.3)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/common':
specifier: ^10.0.3
version: 10.0.3(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1)
@ -40,13 +46,19 @@ dependencies:
version: 10.0.3(@nestjs/common@10.0.3)(@nestjs/microservices@10.0.3)(@nestjs/platform-express@10.0.3)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/microservices':
specifier: ^10.0.3
version: 10.0.3(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1)
version: 10.0.3(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(cache-manager@5.2.3)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/platform-express':
specifier: ^10.0.3
version: 10.0.3(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)
'@nestjs/schedule':
specifier: ^3.0.1
version: 3.0.1(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(reflect-metadata@0.1.13)
'@nestjs/swagger':
specifier: ^7.0.11
version: 7.0.11(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)
'@nestjs/throttler':
specifier: ^4.2.1
version: 4.2.1(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(reflect-metadata@0.1.13)
'@nestjs/typeorm':
specifier: ^10.0.0
version: 10.0.0(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(reflect-metadata@0.1.13)(rxjs@7.8.1)(typeorm@0.3.17)
@ -56,6 +68,12 @@ dependencies:
bcrypt:
specifier: ^5.1.0
version: 5.1.0
cache-manager:
specifier: ^5.2.3
version: 5.2.3
cache-manager-redis-store:
specifier: ^3.0.1
version: 3.0.1
class-transformer:
specifier: ^0.5.1
version: 0.5.1
@ -104,6 +122,9 @@ dependencies:
uuid:
specifier: ^9.0.0
version: 9.0.0
webpack-merge:
specifier: ^5.9.0
version: 5.9.0
devDependencies:
'@nestjs/cli':
@ -118,6 +139,9 @@ devDependencies:
'@types/bcrypt':
specifier: ^5.0.0
version: 5.0.0
'@types/cron':
specifier: ^2.0.1
version: 2.0.1
'@types/express':
specifier: ^4.17.17
version: 4.17.17
@ -1300,6 +1324,10 @@ packages:
dependencies:
'@jridgewell/trace-mapping': 0.3.9
/@dimforge/rapier3d@0.11.2:
resolution: {integrity: sha512-B+AKkPmtJxED3goMTGU8v0ju8hUAUQGLgghzCos4G4OeN9X+mJ5lfN2xtNA0n8tJRJk2YfsMk9BOj/6AN89Acg==}
dev: false
/@eslint-community/eslint-utils@4.4.0(eslint@8.43.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -1337,6 +1365,17 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/@freeblox/engine@0.0.4:
resolution: {integrity: sha512-faxtovSxW6hHNgpOdPeRTYuZk01LiHt8hZA8Vp6cBkdbBMP8jmXBXX/ADFTLv/v9GBXK2DBLbmeZhZ0R46imwg==, tarball: https://git.icynet.eu/api/packages/freeblox/npm/%40freeblox%2Fengine/-/0.0.4/engine-0.0.4.tgz}
dependencies:
'@dimforge/rapier3d': 0.11.2
buffer: 6.0.3
reflect-metadata: 0.1.13
smart-buffer: 4.2.0
three: 0.153.0
uuid: 9.0.0
dev: false
/@humanwhocodes/config-array@0.11.10:
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
engines: {node: '>=10.10.0'}
@ -1683,6 +1722,22 @@ packages:
rxjs: 7.8.1
dev: false
/@nestjs/cache-manager@2.1.0(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(cache-manager@5.2.3)(reflect-metadata@0.1.13)(rxjs@7.8.1):
resolution: {integrity: sha512-9kep3a8Mq5cMuXN/anGhSYc0P48CRBXk5wyJJRBFxhNkCH8AIzZF4CASGVDIEMmm3OjVcEUHojjyJwCODS17Qw==}
peerDependencies:
'@nestjs/common': ^9.0.0 || ^10.0.0
'@nestjs/core': ^9.0.0 || ^10.0.0
cache-manager: <=5
reflect-metadata: ^0.1.12
rxjs: ^7.0.0
dependencies:
'@nestjs/common': 10.0.3(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/core': 10.0.3(@nestjs/common@10.0.3)(@nestjs/microservices@10.0.3)(@nestjs/platform-express@10.0.3)(reflect-metadata@0.1.13)(rxjs@7.8.1)
cache-manager: 5.2.3
reflect-metadata: 0.1.13
rxjs: 7.8.1
dev: false
/@nestjs/cli@10.0.5:
resolution: {integrity: sha512-Btc1lzAkm4j+af9YA0Kv+6N2x7vDneEhsozzEp87kHZrONYNS8zg/SBFTRx5b/e6g0MJshv6vP56PVrdNZy3kA==}
engines: {node: '>= 16'}
@ -1778,7 +1833,7 @@ packages:
optional: true
dependencies:
'@nestjs/common': 10.0.3(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/microservices': 10.0.3(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/microservices': 10.0.3(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(cache-manager@5.2.3)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/platform-express': 10.0.3(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)
'@nuxtjs/opencollective': 0.3.2
fast-safe-stringify: 2.1.1
@ -1810,7 +1865,7 @@ packages:
reflect-metadata: 0.1.13
dev: false
/@nestjs/microservices@10.0.3(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1):
/@nestjs/microservices@10.0.3(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(cache-manager@5.2.3)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1):
resolution: {integrity: sha512-ySAdASLA2FLD0ScEmAHObNsg/IdnG4pYXWxQKLO3co3CAAwHCjF0D4kI2SeegBx89KU0D1wPLvLYzRI0pXh4jQ==}
peerDependencies:
'@grpc/grpc-js': '*'
@ -1848,6 +1903,7 @@ packages:
dependencies:
'@nestjs/common': 10.0.3(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/core': 10.0.3(@nestjs/common@10.0.3)(@nestjs/microservices@10.0.3)(@nestjs/platform-express@10.0.3)(reflect-metadata@0.1.13)(rxjs@7.8.1)
cache-manager: 5.2.3
iterare: 1.2.1
nats: 2.15.1
reflect-metadata: 0.1.13
@ -1870,6 +1926,20 @@ packages:
transitivePeerDependencies:
- supports-color
/@nestjs/schedule@3.0.1(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(reflect-metadata@0.1.13):
resolution: {integrity: sha512-4CAFu4rE/QPYnz/icRg3GiuLmY1bXopG8bWTJ9d7bXzaHBaPKIjGvZ20wsK8P+MncrVCkmK0iYhQrNj0cwX9+A==}
peerDependencies:
'@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0
'@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0
reflect-metadata: ^0.1.12
dependencies:
'@nestjs/common': 10.0.3(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/core': 10.0.3(@nestjs/common@10.0.3)(@nestjs/microservices@10.0.3)(@nestjs/platform-express@10.0.3)(reflect-metadata@0.1.13)(rxjs@7.8.1)
cron: 2.3.1
reflect-metadata: 0.1.13
uuid: 9.0.0
dev: false
/@nestjs/schematics@10.0.1(chokidar@3.5.3)(typescript@5.1.3):
resolution: {integrity: sha512-buxpYtSwOmWyf0nUJWJCkCkYITwbOfIEKHTnGS7sDbcfaajrOFXb5pPAGD2E1CUb3C1+NkQIURPKzs0IouZTQg==}
peerDependencies:
@ -1929,11 +1999,24 @@ packages:
dependencies:
'@nestjs/common': 10.0.3(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/core': 10.0.3(@nestjs/common@10.0.3)(@nestjs/microservices@10.0.3)(@nestjs/platform-express@10.0.3)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/microservices': 10.0.3(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/microservices': 10.0.3(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(cache-manager@5.2.3)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/platform-express': 10.0.3(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)
tslib: 2.5.3
dev: true
/@nestjs/throttler@4.2.1(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(reflect-metadata@0.1.13):
resolution: {integrity: sha512-wVPMuIyr0KdrK1RVVQceWVNesogCm9IgYC1V5EkaTZ+usIE4qxEyzdwU5IqQLgOO/Loiq98MLwReDxazX7i9Uw==}
peerDependencies:
'@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0
'@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0
reflect-metadata: ^0.1.13
dependencies:
'@nestjs/common': 10.0.3(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/core': 10.0.3(@nestjs/common@10.0.3)(@nestjs/microservices@10.0.3)(@nestjs/platform-express@10.0.3)(reflect-metadata@0.1.13)(rxjs@7.8.1)
md5: 2.3.0
reflect-metadata: 0.1.13
dev: false
/@nestjs/typeorm@10.0.0(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(reflect-metadata@0.1.13)(rxjs@7.8.1)(typeorm@0.3.17):
resolution: {integrity: sha512-WQU4HCDTz4UavsFzvGUKDHqi0MO5K47yFoPXdmh+Z/hCNO7SHCMmV9jLiLukM8n5nKUqJ3jDqiljkWBcZPdCtA==}
peerDependencies:
@ -2044,6 +2127,55 @@ packages:
- supports-color
dev: false
/@redis/bloom@1.2.0(@redis/client@1.5.8):
resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.8
dev: false
/@redis/client@1.5.8:
resolution: {integrity: sha512-xzElwHIO6rBAqzPeVnCzgvrnBEcFL1P0w8P65VNLRkdVW8rOE58f52hdj0BDgmsdOm4f1EoXPZtH4Fh7M/qUpw==}
engines: {node: '>=14'}
dependencies:
cluster-key-slot: 1.1.2
generic-pool: 3.9.0
yallist: 4.0.0
dev: false
/@redis/graph@1.1.0(@redis/client@1.5.8):
resolution: {integrity: sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.8
dev: false
/@redis/json@1.0.4(@redis/client@1.5.8):
resolution: {integrity: sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.8
dev: false
/@redis/search@1.1.3(@redis/client@1.5.8):
resolution: {integrity: sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.8
dev: false
/@redis/time-series@1.0.4(@redis/client@1.5.8):
resolution: {integrity: sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.8
dev: false
/@sinclair/typebox@0.25.24:
resolution: {integrity: sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==}
dev: true
@ -2554,6 +2686,13 @@ packages:
resolution: {integrity: sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==}
dev: true
/@types/cron@2.0.1:
resolution: {integrity: sha512-WHa/1rtNtD2Q/H0+YTTZoty+/5rcE66iAFX2IY+JuUoOACsevYyFkSYu/2vdw+G5LrmO7Lxowrqm0av4k3qWNQ==}
dependencies:
'@types/luxon': 3.3.1
'@types/node': 20.3.2
dev: true
/@types/eslint-scope@3.7.4:
resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==}
dependencies:
@ -2627,6 +2766,10 @@ packages:
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
dev: true
/@types/luxon@3.3.1:
resolution: {integrity: sha512-XOS5nBcgEeP2PpcqJHjCWhUCAzGfXIU8ILOSLpx2FhxqMW9KdxgCGXNOEKGVBfveKtIpztHzKK5vSRVLyW/NqA==}
dev: true
/@types/mime@1.3.2:
resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==}
dev: true
@ -3427,6 +3570,19 @@ packages:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'}
/cache-manager-redis-store@3.0.1:
resolution: {integrity: sha512-o560kw+dFqusC9lQJhcm6L2F2fMKobJ5af+FoR2PdnMVdpQ3f3Bz6qzvObTGyvoazQJxjQNWgMQeChP4vRTuXQ==}
engines: {node: '>= 16.18.0'}
dependencies:
redis: 4.6.7
dev: false
/cache-manager@5.2.3:
resolution: {integrity: sha512-9OErI8fksFkxAMJ8Mco0aiZSdphyd90HcKiOMJQncSlU1yq/9lHHxrT8PDayxrmr9IIIZPOAEfXuGSD7g29uog==}
dependencies:
lodash.clonedeep: 4.5.0
lru-cache: 9.1.2
/call-bind@1.0.2:
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
dependencies:
@ -3475,6 +3631,10 @@ packages:
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
dev: true
/charenc@0.0.2:
resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
dev: false
/chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
engines: {node: '>= 8.10.0'}
@ -3583,11 +3743,25 @@ packages:
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
/clone-deep@4.0.1:
resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==}
engines: {node: '>=6'}
dependencies:
is-plain-object: 2.0.4
kind-of: 6.0.3
shallow-clone: 3.0.1
dev: false
/clone@1.0.4:
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
engines: {node: '>=0.8'}
dev: true
/cluster-key-slot@1.1.2:
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
engines: {node: '>=0.10.0'}
dev: false
/co@4.6.0:
resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
@ -3740,6 +3914,12 @@ packages:
/create-require@1.1.1:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
/cron@2.3.1:
resolution: {integrity: sha512-1eRRlIT0UfIqauwbG9pkg3J6CX9A6My2ytJWqAXoK0T9oJnUZTzGBNPxao0zjodIbPgf8UQWjE62BMb9eVllSQ==}
dependencies:
luxon: 3.3.0
dev: false
/cross-fetch@4.0.0:
resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==}
dependencies:
@ -3756,6 +3936,10 @@ packages:
shebang-command: 2.0.0
which: 2.0.2
/crypt@0.0.2:
resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==}
dev: false
/data-uri-to-buffer@5.0.1:
resolution: {integrity: sha512-a9l6T1qqDogvvnw0nKlfZzqsyikEBZBClF39V3TFoKhDtGBqHu2HkuomJc02j5zft8zrUaXEuoicLeW54RkzPg==}
engines: {node: '>= 14'}
@ -4484,6 +4668,11 @@ packages:
wide-align: 1.1.5
dev: false
/generic-pool@3.9.0:
resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==}
engines: {node: '>= 4'}
dev: false
/gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
@ -4839,6 +5028,10 @@ packages:
binary-extensions: 2.2.0
dev: true
/is-buffer@1.1.6:
resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
dev: false
/is-core-module@2.12.1:
resolution: {integrity: sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==}
dependencies:
@ -4880,6 +5073,13 @@ packages:
engines: {node: '>=8'}
dev: true
/is-plain-object@2.0.4:
resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
engines: {node: '>=0.10.0'}
dependencies:
isobject: 3.0.1
dev: false
/is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
@ -4896,6 +5096,11 @@ packages:
/isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
/isobject@3.0.1:
resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
engines: {node: '>=0.10.0'}
dev: false
/istanbul-lib-coverage@3.2.0:
resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==}
engines: {node: '>=8'}
@ -5464,6 +5669,11 @@ packages:
safe-buffer: 5.2.1
dev: false
/kind-of@6.0.3:
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
engines: {node: '>=0.10.0'}
dev: false
/kleur@3.0.3:
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
engines: {node: '>=6'}
@ -5558,6 +5768,9 @@ packages:
p-locate: 5.0.0
dev: true
/lodash.clonedeep@4.5.0:
resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==}
/lodash.memoize@4.1.2:
resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==}
dev: true
@ -5598,6 +5811,15 @@ packages:
engines: {node: '>=12'}
dev: false
/lru-cache@9.1.2:
resolution: {integrity: sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==}
engines: {node: 14 || >=16.14}
/luxon@3.3.0:
resolution: {integrity: sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==}
engines: {node: '>=12'}
dev: false
/macos-release@2.5.1:
resolution: {integrity: sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==}
engines: {node: '>=6'}
@ -5625,6 +5847,14 @@ packages:
tmpl: 1.0.5
dev: true
/md5@2.3.0:
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
dependencies:
charenc: 0.0.2
crypt: 0.0.2
is-buffer: 1.1.6
dev: false
/media-typer@0.3.0:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'}
@ -6464,6 +6694,17 @@ packages:
resolve: 1.22.2
dev: false
/redis@4.6.7:
resolution: {integrity: sha512-KrkuNJNpCwRm5vFJh0tteMxW8SaUzkm5fBH7eL5hd/D0fAkzvapxbfGPP/r+4JAXdQuX7nebsBkBqA2RHB7Usw==}
dependencies:
'@redis/bloom': 1.2.0(@redis/client@1.5.8)
'@redis/client': 1.5.8
'@redis/graph': 1.1.0(@redis/client@1.5.8)
'@redis/json': 1.0.4(@redis/client@1.5.8)
'@redis/search': 1.1.3(@redis/client@1.5.8)
'@redis/time-series': 1.0.4(@redis/client@1.5.8)
dev: false
/reflect-metadata@0.1.13:
resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==}
@ -6645,6 +6886,13 @@ packages:
safe-buffer: 5.2.1
dev: false
/shallow-clone@3.0.1:
resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==}
engines: {node: '>=8'}
dependencies:
kind-of: 6.0.3
dev: false
/shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@ -7024,6 +7272,10 @@ packages:
engines: {node: '>=0.2.6'}
dev: false
/three@0.153.0:
resolution: {integrity: sha512-OCP2/uQR6GcDpSLnJt/3a4mdS0kNWcbfUXIwLoEMgLzEUIVIYsSDwskpmOii/AkDM+BBwrl6+CKgrjX9+E2aWg==}
dev: false
/three@0.154.0:
resolution: {integrity: sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug==}
dev: false
@ -7416,6 +7668,14 @@ packages:
/webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
/webpack-merge@5.9.0:
resolution: {integrity: sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==}
engines: {node: '>=10.0.0'}
dependencies:
clone-deep: 4.0.1
wildcard: 2.0.1
dev: false
/webpack-node-externals@3.0.0:
resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==}
engines: {node: '>=6'}
@ -7525,6 +7785,10 @@ packages:
string-width: 4.2.3
dev: false
/wildcard@2.0.1:
resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==}
dev: false
/windows-release@4.0.0:
resolution: {integrity: sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==}
engines: {node: '>=10'}

View File

@ -12,23 +12,51 @@ const writeGlobEntry = (pattern, to) => {
);
};
module.exports = function (config) {
const appName = process.argv[process.argv.length - 1];
config.entry = {
main: path.join(__dirname, 'apps', appName, 'src', 'main.ts'),
...writeGlobEntry(
path.resolve('apps', appName, 'src', 'database', 'migrations', '*.ts'),
'database/migrations',
),
...writeGlobEntry(
path.resolve('apps', appName, 'src', 'database', 'seeds', '*.ts'),
'database/seeds',
),
const appName = process.argv[process.argv.length - 1];
const configs = [
function (config) {
config.entry = {
main: path.join(__dirname, 'apps', appName, 'src', 'main.ts'),
...writeGlobEntry(
path.resolve('apps', appName, 'src', 'database', 'migrations', '*.ts'),
'database/migrations',
),
...writeGlobEntry(
path.resolve('apps', appName, 'src', 'database', 'seeds', '*.ts'),
'database/seeds',
),
};
config.output = {
path: `${__dirname}/dist/apps/${appName}`,
filename: '[name].js',
libraryTarget: 'commonjs',
};
return config;
},
];
if (appName === 'render') {
const config2 = {
entry: {
renderer:
appName === 'render'
? path.resolve('apps', appName, 'src', 'renderer', 'index.ts')
: undefined,
},
target: 'web',
externalsPresets: {},
externals: {
'@dimforge/rapier3d': 'RAPIER',
},
output: {
path: `${__dirname}/dist/apps/${appName}`,
filename: '[name].js',
libraryTarget: '',
},
};
config.output = {
path: `${__dirname}/dist/apps/${appName}`,
filename: '[name].js',
libraryTarget: 'commonjs',
};
return config;
};
configs.push(config2);
}
module.exports = configs;