mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-17 18:21:28 +10:00
updating dependencies, fixing server reloads
This commit is contained in:
@ -1,19 +1,7 @@
|
||||
{
|
||||
"root": true,
|
||||
"extends": "../.eslintrc.json",
|
||||
"plugins": ["unused-imports"],
|
||||
"ignorePatterns": ["dist"],
|
||||
"env": { "node": true },
|
||||
"rules": {
|
||||
// Unused Imports
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"unused-imports/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"vars": "all",
|
||||
"args": "none",
|
||||
"varsIgnorePattern": "^_",
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
]
|
||||
}
|
||||
"rules": {}
|
||||
}
|
||||
|
||||
1
server/.gitignore
vendored
1
server/.gitignore
vendored
@ -13,6 +13,7 @@ lerna-debug.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
.playwright
|
||||
|
||||
# Tests
|
||||
/coverage
|
||||
|
||||
@ -47,7 +47,4 @@ EXPOSE 3100
|
||||
|
||||
ENV PORT 3100
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=20s --retries=3 --start-period=15s \
|
||||
CMD curl -fSs localhost:3100/health || exit 1
|
||||
|
||||
CMD [ "pnpm", "run", "start" ]
|
||||
@ -8,68 +8,68 @@
|
||||
"start": "node dist/main"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.317.0",
|
||||
"@aws-sdk/client-s3": "^3.347.1",
|
||||
"@nestjs/axios": "^2.0.0",
|
||||
"@nestjs/cache-manager": "^1.0.0",
|
||||
"@nestjs/common": "^9.4.0",
|
||||
"@nestjs/config": "^2.3.1",
|
||||
"@nestjs/core": "^9.4.0",
|
||||
"@nestjs/common": "^9.4.2",
|
||||
"@nestjs/config": "^2.3.2",
|
||||
"@nestjs/core": "^9.4.2",
|
||||
"@nestjs/jwt": "^10.0.3",
|
||||
"@nestjs/mapped-types": "^1.2.2",
|
||||
"@nestjs/passport": "^9.0.3",
|
||||
"@nestjs/platform-express": "^9.4.0",
|
||||
"@nestjs/schedule": "^2.2.1",
|
||||
"@nestjs/platform-express": "^9.4.2",
|
||||
"@nestjs/schedule": "^2.2.2",
|
||||
"@nestjs/serve-static": "^3.0.1",
|
||||
"@nestjs/terminus": "^9.2.2",
|
||||
"@nestjs/typeorm": "^9.0.1",
|
||||
"@types/passport": "^1.0.12",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"cache-manager": "^5.2.0",
|
||||
"cache-manager": "^5.2.2",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"csvtojson": "^2.0.10",
|
||||
"dayjs": "^1.11.7",
|
||||
"google-auth-library": "^8.7.0",
|
||||
"joi": "^17.9.1",
|
||||
"dayjs": "^1.11.8",
|
||||
"google-auth-library": "^8.8.0",
|
||||
"joi": "^17.9.2",
|
||||
"lodash": "^4.17.21",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"nanoid": "3.3.4",
|
||||
"node-stream-zip": "^1.15.0",
|
||||
"nodemailer": "^6.9.1",
|
||||
"nodemailer": "^6.9.3",
|
||||
"passport": "^0.6.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pg": "^8.10.0",
|
||||
"playwright-chromium": "^1.32.3",
|
||||
"pg": "^8.11.0",
|
||||
"playwright-chromium": "^1.34.3",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^5.0.0",
|
||||
"rxjs": "^7.8.0",
|
||||
"typeorm": "0.3.15",
|
||||
"rxjs": "^7.8.1",
|
||||
"typeorm": "0.3.16",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^9.4.0",
|
||||
"@nestjs/schematics": "^9.1.0",
|
||||
"@nestjs/cli": "^9.5.0",
|
||||
"@nestjs/schematics": "^9.2.0",
|
||||
"@reactive-resume/schema": "workspace:*",
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/cookie-parser": "^1.4.3",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/lodash": "^4.14.194",
|
||||
"@types/lodash": "^4.14.195",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/node": "^18.15.12",
|
||||
"@types/nodemailer": "^6.4.7",
|
||||
"@types/node": "^20.2.5",
|
||||
"@types/nodemailer": "^6.4.8",
|
||||
"@types/passport-jwt": "^3.0.8",
|
||||
"@types/passport-local": "^1.0.35",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"prettier": "^2.8.7",
|
||||
"prettier": "^2.8.8",
|
||||
"rimraf": "^5.0.1",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-loader": "^9.4.2",
|
||||
"ts-loader": "^9.4.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.0.4",
|
||||
"webpack": "^5.80.0"
|
||||
"typescript": "^5.1.3",
|
||||
"webpack": "^5.86.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,9 +7,8 @@ import { join } from 'path';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { ConfigModule } from './config/config.module';
|
||||
import { DatabaseModule } from './database/database.module';
|
||||
import { HttpExceptionFilter } from './filters/http-exception.filter';
|
||||
import { AllExceptionsFilter } from './filters/all-exceptions.filter';
|
||||
import { FontsModule } from './fonts/fonts.module';
|
||||
import { HealthModule } from './health/health.module';
|
||||
import { IntegrationsModule } from './integrations/integrations.module';
|
||||
import { MailModule } from './mail/mail.module';
|
||||
import { PrinterModule } from './printer/printer.module';
|
||||
@ -33,7 +32,6 @@ import { UsersModule } from './users/users.module';
|
||||
FontsModule,
|
||||
IntegrationsModule,
|
||||
PrinterModule,
|
||||
HealthModule,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
@ -42,7 +40,7 @@ import { UsersModule } from './users/users.module';
|
||||
},
|
||||
{
|
||||
provide: APP_FILTER,
|
||||
useClass: HttpExceptionFilter,
|
||||
useClass: AllExceptionsFilter,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Body, Controller, Delete, Get, HttpCode, Patch, Post, UseGuards } from '@nestjs/common';
|
||||
import { BadRequestException, Body, Controller, Delete, Get, HttpCode, Patch, Post, UseGuards } from '@nestjs/common';
|
||||
|
||||
import { User } from '@/decorators/user.decorator';
|
||||
import { User as UserEntity } from '@/users/entities/user.entity';
|
||||
@ -23,10 +23,14 @@ export class AuthController {
|
||||
|
||||
@Post('google')
|
||||
async loginWithGoogle(@Body('credential') credential: string) {
|
||||
const user = await this.authService.authenticateWithGoogle(credential);
|
||||
const accessToken = this.authService.getAccessToken(user.id);
|
||||
try {
|
||||
const user = await this.authService.authenticateWithGoogle(credential);
|
||||
const accessToken = this.authService.getAccessToken(user.id);
|
||||
|
||||
return { user, accessToken };
|
||||
return { user, accessToken };
|
||||
} catch (error) {
|
||||
throw new BadRequestException('User with this email might already exist.');
|
||||
}
|
||||
}
|
||||
|
||||
@Post('register')
|
||||
|
||||
23
server/src/filters/all-exceptions.filter.ts
Normal file
23
server/src/filters/all-exceptions.filter.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus } from '@nestjs/common';
|
||||
import { HttpAdapterHost } from '@nestjs/core';
|
||||
|
||||
@Catch()
|
||||
export class AllExceptionsFilter implements ExceptionFilter {
|
||||
constructor(private readonly httpAdapterHost: HttpAdapterHost) {}
|
||||
|
||||
catch(exception: unknown, host: ArgumentsHost) {
|
||||
const { httpAdapter } = this.httpAdapterHost;
|
||||
|
||||
const ctx = host.switchToHttp();
|
||||
|
||||
const httpStatus = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
|
||||
const responseBody = {
|
||||
statusCode: httpStatus,
|
||||
timestamp: new Date().toISOString(),
|
||||
path: httpAdapter.getRequestUrl(ctx.getRequest()),
|
||||
};
|
||||
|
||||
httpAdapter.reply(ctx.getResponse(), responseBody, httpStatus);
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from '@nestjs/common';
|
||||
import type { Request, Response } from 'express';
|
||||
import { TypeORMError } from 'typeorm';
|
||||
|
||||
@Catch(HttpException)
|
||||
export class HttpExceptionFilter implements ExceptionFilter {
|
||||
catch(exception: HttpException, host: ArgumentsHost) {
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse<Response>();
|
||||
const request = ctx.getRequest<Request>();
|
||||
|
||||
const statusCode = exception.getStatus();
|
||||
const message = (exception.getResponse() as TypeORMError).message || exception.message;
|
||||
|
||||
response.status(statusCode).json({
|
||||
statusCode,
|
||||
message,
|
||||
timestamp: new Date().toISOString(),
|
||||
path: request.url,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import { CacheInterceptor, Controller, Get, UseGuards, UseInterceptors } from '@nestjs/common';
|
||||
import { CacheInterceptor } from '@nestjs/cache-manager';
|
||||
import { Controller, Get, UseGuards, UseInterceptors } from '@nestjs/common';
|
||||
|
||||
import { JwtAuthGuard } from '@/auth/guards/jwt.guard';
|
||||
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { HealthCheck, HealthCheckService, TypeOrmHealthIndicator } from '@nestjs/terminus';
|
||||
|
||||
@Controller('health')
|
||||
export class HealthController {
|
||||
constructor(private health: HealthCheckService, private db: TypeOrmHealthIndicator) {}
|
||||
|
||||
@Get()
|
||||
@HealthCheck()
|
||||
check() {
|
||||
return this.health.check([() => this.db.pingCheck('database')]);
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TerminusModule } from '@nestjs/terminus';
|
||||
|
||||
import { HealthController } from './health.controller';
|
||||
|
||||
@Module({
|
||||
imports: [HttpModule, TerminusModule],
|
||||
controllers: [HealthController],
|
||||
})
|
||||
export class HealthModule {}
|
||||
@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Param, Query } from '@nestjs/common';
|
||||
import { Controller, GatewayTimeoutException,Get, Param, Query } from '@nestjs/common';
|
||||
|
||||
import { PrinterService } from './printer.service';
|
||||
|
||||
@ -7,11 +7,15 @@ export class PrinterController {
|
||||
constructor(private readonly printerService: PrinterService) {}
|
||||
|
||||
@Get('/:username/:slug')
|
||||
printAsPdf(
|
||||
async printAsPdf(
|
||||
@Param('username') username: string,
|
||||
@Param('slug') slug: string,
|
||||
@Query('lastUpdated') lastUpdated: string
|
||||
): Promise<string> {
|
||||
return this.printerService.printAsPdf(username, slug, lastUpdated);
|
||||
try {
|
||||
return await this.printerService.printAsPdf(username, slug, lastUpdated);
|
||||
} catch (error) {
|
||||
throw new GatewayTimeoutException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,17 +5,57 @@ import { PageConfig } from '@reactive-resume/schema';
|
||||
import { access, mkdir, readdir, unlink, writeFile } from 'fs/promises';
|
||||
import { join } from 'path';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import { Browser, chromium } from 'playwright-chromium';
|
||||
import { BrowserContext, chromium } from 'playwright-chromium';
|
||||
|
||||
const minimal_chromium_args = [
|
||||
'--autoplay-policy=user-gesture-required',
|
||||
'--disable-background-networking',
|
||||
'--disable-background-timer-throttling',
|
||||
'--disable-backgrounding-occluded-windows',
|
||||
'--disable-breakpad',
|
||||
'--disable-client-side-phishing-detection',
|
||||
'--disable-component-update',
|
||||
'--disable-default-apps',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-domain-reliability',
|
||||
'--disable-extensions',
|
||||
'--disable-features=AudioServiceOutOfProcess',
|
||||
'--disable-hang-monitor',
|
||||
'--disable-ipc-flooding-protection',
|
||||
'--disable-notifications',
|
||||
'--disable-offer-store-unmasked-wallet-cards',
|
||||
'--disable-popup-blocking',
|
||||
'--disable-print-preview',
|
||||
'--disable-prompt-on-repost',
|
||||
'--disable-renderer-backgrounding',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-speech-api',
|
||||
'--disable-sync',
|
||||
'--hide-scrollbars',
|
||||
'--ignore-gpu-blacklist',
|
||||
'--metrics-recording-only',
|
||||
'--mute-audio',
|
||||
'--no-default-browser-check',
|
||||
'--no-first-run',
|
||||
'--no-pings',
|
||||
'--no-sandbox',
|
||||
'--no-zygote',
|
||||
'--password-store=basic',
|
||||
'--use-gl=swiftshader',
|
||||
'--use-mock-keychain',
|
||||
];
|
||||
|
||||
@Injectable()
|
||||
export class PrinterService implements OnModuleInit, OnModuleDestroy {
|
||||
private browser: Browser;
|
||||
private browser: BrowserContext;
|
||||
|
||||
constructor(private readonly schedulerRegistry: SchedulerRegistry, private readonly configService: ConfigService) {}
|
||||
|
||||
async onModuleInit() {
|
||||
this.browser = await chromium.launch({
|
||||
args: ['--disable-dev-shm-usage'],
|
||||
this.browser = await chromium.launchPersistentContext('.playwright', {
|
||||
headless: true,
|
||||
forcedColors: 'active',
|
||||
args: minimal_chromium_args,
|
||||
});
|
||||
}
|
||||
|
||||
@ -34,6 +74,7 @@ export class PrinterService implements OnModuleInit, OnModuleDestroy {
|
||||
await access(join(directory, filename));
|
||||
} catch {
|
||||
const activeSchedulerTimeouts = this.schedulerRegistry.getTimeouts();
|
||||
|
||||
await readdir(directory).then(async (files) => {
|
||||
await Promise.all(
|
||||
files.map(async (file) => {
|
||||
@ -53,9 +94,8 @@ export class PrinterService implements OnModuleInit, OnModuleDestroy {
|
||||
|
||||
const page = await this.browser.newPage();
|
||||
|
||||
await page.goto(`${url}/${username}/${slug}/printer?secretKey=${secretKey}`);
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForSelector('html.wf-active');
|
||||
await page.goto(`${url}/${username}/${slug}/printer?secretKey=${secretKey}`, { waitUntil: 'networkidle' });
|
||||
await page.waitForSelector('html.wf-active', { state: 'visible' });
|
||||
|
||||
const pageFormat: PageConfig['format'] = await page.$$eval(
|
||||
'[data-page]',
|
||||
|
||||
Reference in New Issue
Block a user