Integrate PeerServer in NestJS application - node.js

I am trying to combine peer server with my nestjs application. Unfortunately it doesn't work as expected. I am creating a service containing the peer server instance and initialize it on application start. I also use this service to handle requests coming
in a specific controller. I did the configuration as follow:
main.ts
import { NestFactory } from '#nestjs/core';
import { NestExpressApplication } from '#nestjs/platform-express';
import { I18nMiddleware } from 'nestjs-i18n';
import { Logger, LoggerErrorInterceptor } from 'nestjs-pino';
import { AppModule } from './app.module';
import { PeerServerService } from './peer-server/peer-server.service';
import { PrismaService } from './prisma/prisma.service';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
bufferLogs: true,
});
app.useLogger(app.get(Logger));
app.useGlobalInterceptors(new LoggerErrorInterceptor());
app.use(I18nMiddleware);
const prismaService = app.get(PrismaService);
const peerServerService = app.get(PeerServerService);
prismaService.enableShutdownHooks(app);
peerServerService.enablePeerServer(app);
await app.listen(3000);
}
bootstrap();
peer-server.service.ts
import { Injectable } from '#nestjs/common';
import { NestExpressApplication } from '#nestjs/platform-express';
import { ExpressPeerServer, PeerServerEvents } from 'peer';
import { Express } from 'express';
#Injectable()
export class PeerServerService {
peerServer: Express & PeerServerEvents;
enablePeerServer(app: NestExpressApplication) {
this.peerServer = ExpressPeerServer(app.getHttpServer(), {
path: '/myapp',
});
console.log('peer server: ', this.peerServer);
this.peerServer.get('/test', (req, res) => {
res.send('hello');
});
}
}
peer-server.controller.ts
import { All, Controller, Next, Req, Res } from '#nestjs/common';
import { NextFunction, Request, Response } from 'express';
import { PeerServerService } from './peer-server.service';
#Controller('/peer-server')
export class PeerServerController {
constructor(private readonly peerServerService: PeerServerService) {}
#All('*')
server(
#Req() request: Request,
#Res() response: Response,
#Next() next: NextFunction,
) {
const entryPointPath = '/peer-server/';
request.url = request.url.replace(entryPointPath, '/');
console.log('in route peer: ', request.url);
this.peerServerService.peerServer(request, response, next);
}
}
I verified that the server is correctly forwarded to the peer service with this request
this.peerServer.get('/test', (req, res) => {
res.send('hello');
});
Sending a request to /peer-server/test works but /peer-server/myapp returns 404
Has anyone ever done that successfully ?

Related

How to use external (from another package) exception filter Nest.js?

I'm trying to create shared-module for microservices.
There are two packages:
#name/hub - ordinary HTTP-service
#name/lib - shared library
Library contains simple exception-filter module:
http.exception-filter.ts
import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from '#nestjs/common';
import { Request, Response } from 'express';
#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 status = exception.getStatus();
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message: exception.message,
});
}
}
exceptions-fitlers.module.ts
import { Module, Scope } from '#nestjs/common';
import { HttpExceptionFilter } from './http.exception-filter';
import { APP_FILTER } from '#nestjs/core';
#Module({
providers: [
{
provide: APP_FILTER,
scope: Scope.REQUEST,
useClass: HttpExceptionFilter,
},
],
})
export class ExceptionsFiltersModule {}
Service contains controller that uses this filter:
app.module.ts
import { Module } from '#nestjs/common';
import { ExceptionsFiltersModule } from '#name/nodejs-lib/dist';
#Module({
imports: [ExceptionsFiltersModule, ...],
})
export class AppModule {}
controller.ts
#Controller('app')
#UseFilters(new HttpExceptionFilter())
export class AppController{
#Post('/check')
#HttpCode(200)
async check(#Body() dto: A): Promise<B> {
throw new BadRequestException('Invalid data');
}
}
main.ts
import { NestFactory } from '#nestjs/core';
import { AppModule } from './modules/app.module';
import { ConfigService } from '#nestjs/config';
import { DocumentationBuilder, HttpExceptionFilter } from '#name/nodejs-lib/dist';
async function bootstrap() {
const app = await NestFactory.create(AppModule, { cors: true });
const config = app.get(ConfigService);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(config.get<number>('HTTP_PORT'), () => {
logger.log(`HTTP Server: http://${config.get('HTTP_HOST')}:${config.get('HTTP_PORT')}`);
});
}
bootstrap().then();
Then I trying trigger this filter, I receive generic response:
{
"statusCode": 400,
"message": "Invalid data",
"error": "Bad Request"
}
If someone has opinion, please let me know. Thanks

How to add passport-saml in nestjs?

I am doing an authentication with saml in nestjs I am using the passport-saml package I am directing the page to the microsoft login correctly but in the callback route I do not get the profile data, what I want is to make the person authenticate with saml and that after that a token bearer is generated in the system, so far as I mentioned before I have not been able to obtain the data of the user who starts the session.
auth.module.ts
import { AuthController } from './auth.controller';
import { Saml2Strategy } from './strategies/saml.strategy';
import { Module } from '#nestjs/common';
import { AuthService } from './auth.service';
#Module({
controllers: [AuthController],
providers: [AuthService, Saml2Strategy],
})
export class AuthModule {}
controller the route is get('saml'), It is addressing correctly, but when returning to the callback url I cannot obtain the data of the person.
auth.controller.ts
import { Saml2Strategy } from './strategies/saml.strategy';
import {
Controller,
Get,
Post,
UseGuards,
Res,
Req,
Request,
Body,
} from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
import { ApiTags } from '#nestjs/swagger';
const passport = require('passport');
const SamlStrategy = require('passport-saml').Strategy;
import { ConfigSaml } from './../user/controllers/config';
const fs = require('fs');
#ApiTags('Auth')
#Controller('auth')
export class AuthController {
public config: ConfigSaml;
public userData: any;
constructor() {
this.config = new ConfigSaml();
}
#Get('saml')
#UseGuards(AuthGuard('saml'))
samlLogin() {
}
#Post('/callback')
async callback(#Request() req, #Body() body: any) {
if (req.isAuthenticated()) {
console.log('autenticado');
}
}
}
saml.strategy.ts
import { Injectable } from '#nestjs/common';
import { PassportStrategy } from '#nestjs/passport';
import { passport } from 'passport';
var SamlStrategy = require('passport-saml').Strategy;
const fs = require('fs');
#Injectable()
export class Saml2Strategy extends PassportStrategy(SamlStrategy, 'saml') {
constructor() {
super({
entryPoint: process.env.SAML_ENTRY_POINT,
issuer: process.env.SAML_ISSUER,
callbackUrl: process.env.SAML_CALLBACK_URL,
cert: fs.readFileSync(
process.cwd() +
'/src/modules/auth/strategies/' +
process.env.SAML_CERT ||
process.cwd() + '/src/modules/auth/strategies/certificate.pem',
'utf-8',
),
function(profile, done) {
console.log('profile in strategy', profile);
return done(null, {
id: profile.nameID,
email:
profile[
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'
],
displayName:
profile['http://schemas.microsoft.com/identity/claims/displayname'],
firstName:
profile[
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname'
],
lastName:
profile[
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname'
],
});
},
});
}
}
It seems like an old query...
and I guess over here you are trying to get the SAML response as a Profile object, which is provided by the passport-saml strategy
So to retrieve the SAML response as a Profile object you need to set the same guard in the callback API '#Post('/callback')' as well, which you have used in the '#Get('saml')' API which is
#UseGuards(AuthGuard('saml'))
So your updated code block will be:
import { Saml2Strategy } from './strategies/saml.strategy';
import {
Controller,
Get,
Post,
UseGuards,
Res,
Req,
Request,
Body,
} from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
import { ApiTags } from '#nestjs/swagger';
const passport = require('passport');
const SamlStrategy = require('passport-saml').Strategy;
import { ConfigSaml } from './../user/controllers/config';
const fs = require('fs');
#ApiTags('Auth')
#Controller('auth')
export class AuthController {
public config: ConfigSaml;
public userData: any;
constructor() {
this.config = new ConfigSaml();
}
#Get('saml')
#UseGuards(AuthGuard('saml'))
samlLogin() {
}
#Post('/callback')
#UseGuards(AuthGuard('saml'))
async callback(#Request() req, #Body() body: any) {
if (req.isAuthenticated()) {
console.log('autenticado');
}
}
}

"then" is not exuceted after a Promise<void> when writing a file from a POST Request

I have currently an express server. I am trying to make a POST request without success.
Here is my ccontroller :
import { BAD_REQUEST } from '#app/constant';
import { SaveDrawService } from '#app/services/save-draw.service';
import { TYPES } from '#app/types';
import { Image } from '#common/communication/Image';
import { NextFunction, Request, Response, Router } from 'express';
import { inject, injectable } from 'inversify';
import 'reflect-metadata';
#injectable()
export class SaveDrawController {
router: Router;
constructor(#inject(TYPES.SaveDrawService) private saveDrawService: SaveDrawService) {
this.configureRouter();
}
private configureRouter(): void {
this.router = Router();
this.router.post('/write', (req: Request, res: Response, next: NextFunction) => {
if (!req.body) return res.sendStatus(BAD_REQUEST);
this.saveDrawService.writeData(req.body as Image);
return res.sendStatus(this.saveDrawService.code);
});
this.router.get('/read', (req: Request, res: Response, next: NextFunction) => {
return res.send(this.saveDrawService.readImageData());
});
}
}
Image here is a interface that i want to POST with these parameters:
export interface Image {
title: string;
tags: string[];
data: string; // base64 image from HTML canvas
}
Here is my service where I try to write the file :
import { ERROR, OK } from '#app/constant';
import { Image } from '#common/communication/Image';
import { readFile, writeFile } from 'fs';
import { injectable } from 'inversify';
import 'reflect-metadata';
import * as util from 'util';
#injectable()
export class SaveDrawService {
code: number;
constructor() {}
async writeData(image: Image): Promise<void> {
const base64Data = image.data.replace('data:image/png;base64,', '');
const write = util.promisify(writeFile);
return await write('test.png', base64Data, 'base64')
.then(() => {
this.code = OK; // 200
})
.catch((error: Error) => {
console.error(error);
this.code = ERROR; // 500
});
}
async readImageData(): Promise<string> {
const read = util.promisify(readFile);
return await read('test.png', { encoding: 'base64' });
}
extractFormat(base64Data: string) {}
}
The problem is that the "then" in write is not executed after the write and the "this.code" is therefore never updated and makes the request crash. I just started and I really don't know what can be causing this.
Here is my request I make to test the code:
On my server the POST is received and my server log this :
POST /api/draw/write 500 20.825 ms - 92
UPDATE: both my GET and POST return a error, but they are writing and reading the file on the server (I verify by making a POST and after a GET with logs to see if they are the same)
I think this is what you should change. Don't use async/await with then/catch, these are two different notation to wait for asynchronous code and get data.
async writeData(image: Image): Promise<void> {
const base64Data = image.data.replace('data:image/png;base64,', '');
const write = util.promisify(writeFile);
const resp = await write('test.png', base64Data, 'base64');
if (resp.ok) // whatever your condition
{
this.code = OK;
} else {
console.error(resp.error); // show error here
this.code = ERROR;
}
}
Check here for more details.

How to Handle BodyParser Errors (and in General Middleware errors) in NestJS

I'm tring to handle bodyparser errors with NestJS but I can't figure out how
this is what I have done so far;
main.ts
const expressServer = express.default();
const createFunction = async (expressInstance): Promise<void> => {
const app = await NestFactory.create(AppModule, new ExpressAdapter(expressInstance), {
cors: true,
bodyParser: true,
});
app.useGlobalFilters(new AllExceptionsFilter());
app.use(helmet());
await app.init();
};
createFunction(expressServer)
.then((v) => console.log('Nest ok'))
.catch((err) => console.error('Nest ko', err));
export const api = functions.region('europe-west3').https.onRequest(expressServer);
I tried to catch the error after bodyparser.
I then tried to use Filters
app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { APP_FILTER } from '#nestjs/core';
import { AllExceptionsFilter } from './catch-all.filter';
#Module({
imports: [],
controllers: [AppController],
providers: [
AppService,
{
provide: APP_FILTER,
useClass: AllExceptionsFilter,
},
],
})
export class AppModule {}
and
catch-all.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '#nestjs/common';
#Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
But if I try to send a req via postman with a malformed JSON the server crashes
any idea of how should I do it?
Since this is error occurs outside of the NestJS context (it uses the body-parser library), you must handle it with an express middleware.
Try to implement one, which can detect, and handle these errors. For example:
app.use((err, req, res, next) => {
if (err instanceof SyntaxError &&
err.status >= 400 && err.status < 500 &&
err.message.indexOf('JSON') !== -1) {
res.status(400).send('send your own response here')
}
// ...
})

How to pass form data from angular to nodejs

I am new to Angular5. I need to pass user details from angular to nodejs.
app.component.ts:
import { Component } from '#angular/core';
import { FormBuilder, FormGroup, Validators, FormControl, FormArray } from
'#angular/forms';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(private http:Http) { }
onSubmit(registerForm) {
console.log(registerForm.value);
let url = 'http://localhost:8080/signup';
this.http.post(url, {registerForm(registerForm)}).subscribe(res =>
console.log(res.json()));
}
}
Now I need to pass those data to nodejs routes to proceed further.
Node js routing file:
module.exports = function(app, passport) {
app.post('/signup', passport.authenticate('local-signup', {
successRedirect : '/',
failureRedirect : '/',
failureFlash : true
}));
};
Now am getting the following error: Uncaught Error: Can't resolve all parameters for AppComponent: (?).
Call Your function from the component.html file it will trigger the function which will be in your component.ts file.
From this function call service which contains the function which will be requesting your node API
addData() {
this.adminService.addCountry(this.form.value).subscribe(
res => {
var response = res.json();
this.flashMessagesService.show(response.message, {
cssClass: "alert-success",
timeout: 2000
});
},
error => {
if (error.status == 401) {
localStorage.removeItem("currentUser");
this.router.navigate(["/"]);
} else {
this.flashMessagesService.show(error.json().error, {
cssClass: "alert-danger",
timeout: 2000
});
}
}
);
}
Create admin service to call your HTTP URL which is running on node
Service
addCountry(formData) {
console.log(formData);
var authToken = this.getAuthToken();
if (authToken != "") {
var headers = this.getHeaders();
headers.append("Authorization", authToken);
return this.http
.post(
`http://localhost:3000/addData`,
this.formData(formData),
{ headers: headers }
)
.map((response: Response) => {
return response;
});
}
}
You can use service in angular to send data to nodeJs. Please refer the tutorials of Angular from Codecraft. Please have a look at https://codecraft.tv/courses/angular/http/core-http-api/
For now you need to send some registration form data. So
1. import http module to AppModule
2. Refer to the documentation above
3. You can pass data to nodejs using a POST method of http
I think you should look on Observable.
https://angular.io/guide/observables
On logic you should create server with Observable request to your NodeJs (express) app. Then you can add to your component function with subscribe.
Some code:
Create authentication service
ng generate service authentication
Create user service for store user data (or you can only store it in other components)
ng generate service user
On authentication.service.ts create authenticate method
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import { UserService } from '../user/user.service';
import { Router } from '#angular/router';`
#Injectable()
export class AuthenticationService {
token: string;
constructor(private router: Router, private httpClient: HttpClient,
public userService: UserService) {
const currentUser = JSON.parse(localStorage.getItem('currentUser'));
this.token = currentUser && currentUser.token;
}
getToken(email: string, password: string): Observable<User> {
return this.httpClient.post<User>(apiRoutes.authentication,
{userEmail: email, userPassword: password});
}
authenticate(email: string, password: string) {
this.getToken(email, password).subscribe(response => {
if (response.userToken.length > 0) {
this.userService.user.userEmail = response.userEmail;
this.userService.user.userToken = response.userToken;
this.userService.user._id = response._id;
this.userService.user.isUserAuthenticated = true;
localStorage.setItem('currentUser', JSON.stringify({token: response.userToken}));
this.router.navigate(['/']);
// TODO: Need some error logic
} else {
return false;
}
});
}
Now you can add to your form in template
<form (ngSubmit)="this.authenticationService.authenticate(userEmail, password)">
...
</form>

Resources