Newbie is Here!
I am developing media server for sfu.
I want to make sfu media server, so i think it needs RTCPeerconnection like client side.
myProblem is that 'wrtc.RTCPeerConnection.createOffer()' is not work in nestjs
I want to use webrtc api in nestjs like
import * as webrtc from 'wrtc'
const Peer = new webrtc.RTCPeerConnection();
Peer.createOffer();
I want to use methods (createOffer, createAnswer ...etc) and events (onicecandidate, ontrack ...etc)
but it is not working.
It is result of wrtc.RTCPeerConnection().createOffer() in my project
It make "offer" of empty object like this
Promise { <pending> }
in console.log()
there is my code of nestjs
sfu.service.ts
import { Injectable } from '#nestjs/common';
import { Server, Socket } from 'socket.io';
import * as DefaultRTCPeerConnection from 'wrtc';
const {
RTCPeerConnection,
} = DefaultRTCPeerConnection;
#Injectable()
export class SfuService {
private peers: Map<string, RTCPeerConnection>;
private id: string;
constructor() {
this.peers = new Map();
}
getter() {
return this.peers.get(this.id);
}
async createPeerConnection(connectionId) {
const peerConnection = new RTCPeerConnection();
this.peers.set(connectionId, peerConnection);
this.id = connectionId;
// peerConnection.ontrack = (stream) => this.onTrackHandler(stream);
return peerConnection;
}
async createOffer(id, option) {
try {
// Todo option setting
const offer = await this.peers.get(id).createOffer(option);
return offer;
} catch (e) {
console.log(e);
}
}
async createAnswer(id, option) {
try {
// Todo option setting
const answer = await this.peers.get(id).createAnswer(option);
return answer;
} catch (e) {
console.log(e);
}
}
async setLocalDescription(description) {
try {
await this.peers.get(this.id).setLocalDescription(description);
} catch (e) {
console.log(e);
}
}
async setRemoteDescription(description) {
try {
await this.peers.get(this.id).setRemoteDescription(description);
} catch (e) {
console.log(e);
}
}
}
and then my gateway code
sfu.gateway.ts
import { UsePipes, ValidationPipe } from '#nestjs/common';
import {
SubscribeMessage,
WebSocketGateway,
OnGatewayInit,
OnGatewayConnection,
OnGatewayDisconnect,
} from '#nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { OfferDto } from 'src/wss/dto';
import { offerValidationPipe } from 'src/wss/pipes';
import { SfuService } from './sfu.service';
import * as DefaultRTCPeerConnection from 'wrtc';
const { RTCPeerConnection } = DefaultRTCPeerConnection;
#WebSocketGateway({
namespace: 'sfu',
cors: {
origin: [
'http://localhost:4000',
'http://localhost:3300',
'http://localhost:5000',
'http://localhost:3000',
],
},
})
export class SfuGateway
implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
constructor(private service: SfuService) {}
server: Server;
afterInit(_server: Server) {
this.server = _server;
}
handleConnection(socket: Socket, ...args: any[]) {
console.log(socket.id, 'Connected!');
this.service.Test();
}
handleDisconnect(socket: Socket) {
console.log(socket.id, 'DisConnected!');
}
#UsePipes(new ValidationPipe())
#SubscribeMessage('join_room')
async JoinRoom(socket: Socket, roomName: string) {
socket.join(roomName);
socket.emit('welcome', socket.id);
return {};
}
#UsePipes(new offerValidationPipe())
#SubscribeMessage('offer')
async Offer(socket: Socket, data: OfferDto) {
this.service.createPeerConnection(socket.id);
const connection = this.service.getter();
connection.onicecandidate = (e) => {
socket.emit('ice', e.candidate, socket.id);
};
const t = new RTCPeerConnection();
t.setRemoteDescription(data.offer);
console.log('offer Set!', data.offer);
await this.service.setRemoteDescription(data.offer);
console.log('Offer Set Complete\n');
const temp = await this.service.createAnswer(socket.id, {
offerToReceiveAudio: false,
offerToReceiveVideo: false,
});
console.log('Answer ppppppppppp : ', temp);
this.service.setLocalDescription(temp);
const offer = this.service.createOffer(socket.id, {
offerToReceiveAudio: false,
offerToReceiveVideo: false,
});
console.log(offer);
socket.emit('test', offer);
socket.to(data.roomName).emit('offer', data.offer, socket.id);
}
}
my package.json
{
"name": "socketserver",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"#nestjs/common": "^8.0.0",
"#nestjs/core": "^8.0.0",
"#nestjs/platform-express": "^8.0.0",
"#nestjs/platform-socket.io": "^9.0.3",
"#nestjs/websockets": "^9.0.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"nestjs-socket-handlers-with-ack": "^2.0.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.5.6",
"rxjs-compat": "^6.6.7",
"socket.io-redis": "^6.1.1",
"wrtc": "^0.4.7"
},
"devDependencies": {
"#nestjs/cli": "^8.0.0",
"#nestjs/schematics": "^8.0.0",
"#nestjs/testing": "^8.0.0",
"#types/express": "^4.17.13",
"#types/jest": "27.5.0",
"#types/node": "^16.0.0",
"#types/socket.io": "^3.0.2",
"#types/supertest": "^2.0.11",
"#types/ws": "^8.5.3",
"#typescript-eslint/eslint-plugin": "^5.0.0",
"#typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "28.0.3",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "28.0.1",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.0.0",
"typescript": "^4.3.5"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
Please Help me!!
Related
I am facing this issue on a particular event page that has a route event/[id]. Apparently, my teammate who developed the code is not facing any problems. However, I face this issue whenever I reload the page. If I go to the page through normal flow i.e. url/eventList -> click on event card -> url/event/[id] then everything works fine. But as soon as I reload the page, I face hydration errors.
npm version: 8.19.3
node version: 16.17.0
Errors:
1. Text content does not match server-rendered HTML.
2. Error: Hydration failed because the initial UI does not match what was rendered on the server.
3. Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
/events/[id] page;:
import Event from '../../../Components/Event/Event';
import { useRouter } from 'next/router';
import classes from './event.module.css';
const EventDetails = (props) => {
const router = useRouter();
const {
query: { tid },
} = router;
return (
<section style={{ minHeight: '91vh' }} className={classes.background}>
<Event eventDetails={props.eventDetails} teamId={tid} />
</section>
);
};
export async function getStaticPaths() {
const events = await fetch(
process.env.NEXT_PUBLIC_BACKEND_API + 'events/'
).then((res) => res.json());
return {
fallback: false,
paths: events.map((event) => {
const eventId = event.id;
return {
params: {
id: eventId,
},
};
}),
};
}
export async function getStaticProps(context) {
const eventId = context.params.id;
const res = await fetch(
process.env.NEXT_PUBLIC_BACKEND_API + 'events/' + eventId
);
if (!res || res.status != 200) {
return {
notFound: true,
};
}
const eventDetails = await res.json();
return {
props: {
eventDetails: eventDetails,
},
revalidate: 100,
};
}
export default EventDetails;
package.json :-
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "npm run prettify && next dev",
"build": "npm run prettify && next build",
"start": "next start",
"lint": "npm run prettify && next lint",
"prettify": "prettier --write \"**/*.{js, jsx}\""
},
"dependencies": {
"#emotion/react": "11.10.4",
"#emotion/server": "11.10.0",
"#emotion/styled": "11.10.4",
"#mui/icons-material": "5.10.6",
"#mui/material": "5.10.6",
"#mui/x-date-pickers": "^5.0.3",
"cryptr": "^6.0.3",
"dayjs": "^1.11.5",
"intl": "^1.2.5",
"mui-file-dropzone": "^4.0.2",
"next": "12.2.5",
"next-auth": "^4.12.0",
"normalize.css": "^8.0.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-share": "^4.4.1",
"react-toastify": "^9.0.8",
"swr": "^1.3.0",
"universal-cookie": "^4.0.4"
},
"devDependencies": {
"eslint": "8.23.1",
"eslint-config-next": "12.3.1",
"eslint-config-prettier": "8.5.0",
"prettier": "2.7.1"
}
}
I have tried modifying getStaticPaths and getStaticProps but this issue still persists.
import 'zone.js/dist/zone-node';
import 'localstorage-polyfill';
global['localStorage'] = localStorage
import {APP_BASE_HREF} from '#angular/common';
import {ngExpressEngine} from '#nguniversal/express-engine';
import * as express from 'express';
import {existsSync, readFileSync} from 'fs';
import {join} from 'path';
import * as cookieparser from 'cookie-parser';
import { REQUEST, RESPONSE } from '#nguniversal/express-engine/tokens';
// ssr DOM
const domino = require('domino');
const fs = require('fs');
const path = require('path');
// Use the browser index.html as template for the mock window
const template = fs.readFileSync(path.join(process.cwd(), '.', 'dist/functions/browser', 'index.html')).toString();
console.log(template)
const win = domino.createWindow(template);
global['window'] = win;
global['document'] = win.document;
global['self'] = win;
global['IDBIndex'] = win.IDBIndex;
global['navigator'] = win.navigator;
import {AppServerModule} from './src/main.server';
import { environment } from 'src/environments/environment';
import { globalAgent } from 'http';
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const websiteFileLocation = environment.production ? "browser" : "dist/functions/browser"
const distFolder = join(process.cwd(), websiteFileLocation);
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
server.use(cookieparser());
// Our Universal express-engine (found # https://github.com/angular/universal/tree/main/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, res, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }, {provide: REQUEST, useValue: req},
{provide: RESPONSE, useValue: res}] });
});
return server;
}
function run(): void {
const port = process.env['PORT'] || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
run();
export * from './src/main.server';
that is my server.ts
import { NgModule } from '#angular/core';
import { ServerModule } from '#angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { ServerCookiesModule } from 'ngx-universal-cookies/server';
import { BrowserModule } from '#angular/platform-browser';
import { CookieBackendModule } from 'ngx-cookie-backend';
#NgModule({
imports: [
AppModule,
ServerModule,
CookieBackendModule.withOptions()
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
this is app.server.module.ts
I try to run npm run dev:ssr and it fails to start and get the error
C:\Users\Fujitsu\terabulk\terabulk-front-ssr\dist\functions\server\main.js:92809
throw new Error("NotYetImplemented");
^
Error: NotYetImplemented
at Document.exports.nyi (C:\Users\Fujitsu\terabulk\terabulk-front-ssr\dist\functions\server\main.js:92809:9)
at CookiesStorage.getAllItems (C:\Users\Fujitsu\terabulk\terabulk-front-ssr\dist\functions\server\main.js:127014:46)
at new CookiesStorage (C:\Users\Fujitsu\terabulk\terabulk-front-ssr\dist\functions\server\main.js:126929:10)
at Module.89257 (C:\Users\Fujitsu\terabulk\terabulk-front-ssr\dist\functions\server\main.js:127102:24)
at __webpack_require__ (C:\Users\Fujitsu\terabulk\terabulk-front-ssr\dist\functions\server\main.js:365916:42)
at Object.28011 (C:\Users\Fujitsu\terabulk\terabulk-front-ssr\dist\functions\server\main.js:799:21)
at __webpack_require__ (C:\Users\Fujitsu\terabulk\terabulk-front-ssr\dist\functions\server\main.js:365916:42)
at Object.34440 (C:\Users\Fujitsu\terabulk\terabulk-front-ssr\dist\functions\server\main.js:631:12)
at __webpack_require__ (C:\Users\Fujitsu\terabulk\terabulk-front-ssr\dist\functions\server\main.js:365916:42)
at Object.90158 (C:\Users\Fujitsu\terabulk\terabulk-front-ssr\dist\functions\server\main.js:94:39)
A server error has occurred.
node exited with 1 code.
connect ECONNREFUSED 127.0.0.1:58554
angular.json
{
"$schema": "./node_modules/#angular/cli/lib/config/schema.json",
"cli": {
"analytics": "8be174a3-843d-4fe9-8af7-242a16b597a4"
},
"version": 1,
"newProjectRoot": "projects",
"projects": {
"paddybaba2": {
"projectType": "application",
"schematics": {
"#schematics/angular:application": {
"strict": true
}
},
"root": "",
"sourceRoot": "src",
"prefix": "terabulk",
"i18n": {
"sourceLocale": {
"code": "en"
},
"locales": {
"zh": "translate/messages5.cn.xlf",
"ru": "translate/messages.ru.xlf",
"de": "translate/messages.de.xlf",
"ga": "translate/messages.ga.xlf",
"es": "translate/messages.es.xlf"
}
},
"architect": {
"build": {
"builder": "#angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/functions/browser",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"node_modules/animate.css/animate.min.css",
"src/styles.scss",
"./node_modules/ngx-lightbox/lightbox.css",
"node_modules/#videogular/ngx-videogular/fonts/videogular.css",
"src/style-variables.scss"
],
"scripts": [],
"localize": false
},
"configurations": {
"en": {
"localize": [
"en"
]
},
"cn": {
"localize": [
"zh"
]
},
"ru": {
"localize": [
"ru"
]
},
"de": {
"localize": [
"de"
]
},
"ga": {
"localize": [
"ga"
]
},
"es": {
"localize": [
"es"
]
},
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "5000kb",
"maximumError": "10mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "16kb",
"maximumError": "400kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "#angular-devkit/build-angular:dev-server",
"configurations": {
"en": {
"browserTarget": "paddybaba2:build:en"
},
"cn": {
"browserTarget": "paddybaba2:build:cn"
},
"ru": {
"browserTarget": "paddybaba2:build:ru"
},
"de": {
"browserTarget": "paddybaba2:build:de"
},
"es": {
"browserTarget": "paddybaba2:build:es"
},
"ga": {
"browserTarget": "paddybaba2:build:ga"
},
"production": {
"browserTarget": "paddybaba2:build:production"
},
"development": {
"browserTarget": "paddybaba2:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "#angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "paddybaba2:build"
}
},
"test": {
"builder": "#angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
},
"deploy": {
"builder": "#angular/fire:deploy",
"options": {
"prerender": false,
"ssr": false,
"browserTarget": "paddybaba2:build:production",
"firebaseProject": "terabulk2",
"firebaseHostingSite": "terabulk2"
}
},
"server": {
"builder": "#angular-devkit/build-angular:server",
"options": {
"outputPath": "dist/functions/server",
"main": "server.ts",
"tsConfig": "tsconfig.server.json"
},
"configurations": {
"en": {},
"cn": {},
"ru": {},
"de": {},
"ga": {},
"es": {},
"production": {
"outputHashing": "media",
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
},
"development": {
"optimization": false,
"sourceMap": true,
"extractLicenses": false
}
},
"defaultConfiguration": "production"
},
"serve-ssr": {
"builder": "#nguniversal/builders:ssr-dev-server",
"configurations": {
"development": {
"browserTarget": "paddybaba2:build:development",
"serverTarget": "paddybaba2:server:development"
},
"production": {
"browserTarget": "paddybaba2:build:production",
"serverTarget": "paddybaba2:server:production"
}
},
"defaultConfiguration": "development"
},
"prerender": {
"builder": "#nguniversal/builders:prerender",
"options": {
"routes": [
"/"
]
},
"configurations": {
"production": {
"browserTarget": "paddybaba2:build:production",
"serverTarget": "paddybaba2:server:production"
},
"development": {
"browserTarget": "paddybaba2:build:development",
"serverTarget": "paddybaba2:server:development"
}
},
"defaultConfiguration": "production"
}
}
}
},
"schematics": {
"#schematics/angular:component": {
"style": "scss"
}
}
}
package.json
{
"name": "paddybaba2",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"build-all": "ng build --configuration en --deleteOutputPath=false && ng build --configuration cn --deleteOutputPath=false && ng build --configuration ru --deleteOutputPath=false && echo internationalization build all languages finished. && ws",
"deploy": "ng build --localize=true && firebase deploy",
"push": "git add . && git commit -m %npm_config_msg% && git push -u origin main",
"deploy-blog": "ng build --localize=true && cd .. && cd terabulk-blog && ng build --output-path C:\\Users\\Fujitsu\\terabulk\\terabulk-front\\public\\blog --base-href /blog/ && cd .. && cd terabulk-front && firebase deploy",
"dev:ssr": "ng run paddybaba2:serve-ssr",
"serve:ssr": "node dist/paddybaba2/server/main.js",
"build:ssr": "ng build && ng run paddybaba2:server",
"prerender": "ng run paddybaba2:prerender"
},
"private": true,
"dependencies": {
"#angular/animations": "^14.1.3",
"#angular/cdk": "^14.1.2",
"#angular/common": "^14.1.3",
"#angular/compiler": "^14.1.3",
"#angular/core": "^14.1.3",
"#angular/fire": "^7.4.1",
"#angular/forms": "^14.1.3",
"#angular/platform-browser": "^14.1.3",
"#angular/platform-browser-dynamic": "^14.1.3",
"#angular/platform-server": "^14.1.3",
"#angular/router": "^14.1.3",
"#bugsplat/ngx-animated-counter": "^2.0.0",
"#ng-select/ng-select": "^9.0.2",
"#nguniversal/express-engine": "^14.1.0",
"#nguniversal/module-map-ngfactory-loader": "^8.2.6",
"#stomp/stompjs": "^6.1.2",
"#syncfusion/ej2-notifications": "^20.1.47",
"#types/socket.io": "^3.0.2",
"#videogular/ngx-videogular": "^6.0.0",
"angular-cc-library": "^3.0.1",
"angular-file-saver": "^1.1.3",
"angular-progress-bar": "^1.0.11",
"angular-responsive-carousel": "^2.1.2",
"angular-shorturl": "^0.1.7",
"angular2-toaster": "^11.0.1",
"animate.css": "^4.1.1",
"canvas": "^2.9.3",
"cookie-parser": "^1.4.6",
"domino": "^2.1.6",
"express": "^4.15.2",
"fabric": "^5.2.1",
"file-saver": "^2.0.5",
"jsdom-canvas-2": "^11.6.3-alpha.0",
"localstorage-polyfill": "^1.0.1",
"lodash.get": "^4.4.2",
"lodash.isequal": "^4.5.0",
"lodash.merge": "^4.6.2",
"lodash.set": "^4.3.2",
"net": "^1.0.2",
"ng-animate": "^1.0.0",
"ng-pattern-restrict": "^0.2.3",
"ng-popups": "^9.0.0",
"ng-tiny-url": "^1.0.2",
"ngx-clipboard": "^15.1.0",
"ngx-cookie": "^6.0.1",
"ngx-cookie-backend": "^6.0.1",
"ngx-editor": "^11.1.0",
"ngx-filesaver": "^13.0.0",
"ngx-lightbox": "^2.4.1",
"ngx-spinner": "^12.0.0",
"ngx-store": "^3.0.1",
"ngx-universal-cookies": "^8.0.1",
"rxjs": "~6.6.0",
"rxjs-compat": "^6.6.7",
"sockjs-client": "^1.5.2",
"stompjs": "^2.3.3",
"three.js": "^0.77.0",
"ts-debug": "^1.3.0",
"tslib": "^2.3.0",
"zone.js": "~0.11.4"
},
"devDependencies": {
"#angular-devkit/build-angular": "^14.1.3",
"#angular/cli": "^14.1.3",
"#angular/compiler-cli": "^14.1.3",
"#angular/localize": "^14.1.3",
"#nguniversal/builders": "^14.1.0",
"#types/cookie-parser": "^1.4.3",
"#types/core-js": "^2.5.5",
"#types/express": "^4.17.0",
"#types/jasmine": "~3.8.0",
"#types/node": "^12.11.1",
"#types/sockjs-client": "^1.5.1",
"#types/stompjs": "^2.3.5",
"jasmine-core": "~3.8.0",
"jsdom": "20.0.0",
"jsdom-global": "3.0.2",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "~1.7.0",
"typescript": "~4.6.4"
}
}
it is a large project can't fit everything in here but i can add your requested code if needs be. I believe the problem lies with domino not being able to work with cookies. I need to do this so i can deploy to firebase with ssr.
As you can see I am using ngx-store, this provides decorators that can automatically store variables to localStorage. I don't think it has universal support. Do you know of anyway to polyfill or shim it?
full error:
ERROR [ExceptionHandler] Nest can't resolve dependencies of the CommitteesController (?).
Please make sure that the argument CommitteesService at index [0] is available in the CommitteesModule context.
Potential solutions:
- If CommitteesService is a provider, is it part of the current CommitteesModule?
- If CommitteesService is exported from a separate #Module, is that module imported within CommitteesModule?
#Module({
imports: [ /* the Module containing CommitteesService */ ]
})
at Injector.lookupComponentInParentModules (C:\Sites\AM-API-MDD\node_modules\#nestjs\core\injector\injector.js:193:19)
at async Injector.resolveComponentInstance (C:\Sites\AM-API-MDD\node_modules\#nestjs\core\injector\injector.js:149:33)
This was working with NestJS 7.6, but I upgraded the version to 8.0.1 and started getting this error.
There are several other modules that don't get an error, however, this is the first module in the list. The processor may not get to the other modules after returning this error.
committees.services.ts
import { Injectable } from '#nestjs/common';
import { PrismaService } from '../database';
import { defaultIncludeQuery, PER_PAGE_COUNT } from './constant';
import { Prisma } from '#prisma/client';
#Injectable()
export class CommitteesService {
constructor(private readonly prisma: PrismaService) {}
committees() {
return Promise.all([
this.prisma.committee.findMany({
include: {
PersonCommittee: {
include: {
Person: {
select: {
PKPersonID: true,
LastName: true,
FirstName: true,
MiddleName: true,
PreferredFirstName: true,
DisplayName: true,
EmploymentStatus: true
}
}
}
}
},
where: {
Active: true,
OR: [
{ PersonCommittee: { some: { Person: { EmploymentStatus: 'A' } } } },
{ PersonCommittee: { some: { Person: { EmploymentStatus: 'B' } } } },
{ PersonCommittee: { some: { Person: { EmploymentStatus: 'C' } } } },
],
}
})
])
}
committees.controller.ts
import {
Body,Controller,DefaultValuePipe,Delete,Get,Param,ParseIntPipe,Post,Put,Query,
} from '#nestjs/common';
import { CommitteesService } from './committees.service';
import { RESOURCE_BASE_ROUTE } from '../constant';
import { PER_PAGE_COUNT } from './constant';
import { Prisma } from '#prisma/client';
const Route = RESOURCE_BASE_ROUTE.committees;
#Controller()
export class CommitteesController {
constructor(private readonly committeesService: CommitteesService) {}
#Get(`${Route}`)
all() {
return this.committeesService.committees();
}
#Get(`${Route}/:id`)
byId(#Param('id', ParseIntPipe) id: number) {
if ( id == -1 ) {
return this.committeesService.committees()
}
return this.committeesService.committee({
CommitteeID: id,
});
}
committees.modules.ts
import { Module } from '#nestjs/common';
import { CommitteesController } from './committees.controller';
import { CommitteesService } from './Committees.service';
#Module({
imports: [],
controllers: [CommitteesController],
providers: [CommitteesService],
exports: [ CommitteesService],
})
export class CommitteesModule {}
package.json
{
"name": "am-api-mdd",
"version": "2.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"#angular/router": "^6.1.10",
"#nestjs/common": "^8.0.10",
"#nestjs/core": "^8.0.6",
"#nestjs/microservices": "^7.6.18",
"#nestjs/platform-express": "^7.6.18",
"node-windows": "^1.0.0-beta.5",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^6.6.7"
},
"devDependencies": {
"#nestjs/cli": "^8.1.2",
"#nestjs/schematics": "^7.3.1",
"#nestjs/testing": "^7.6.18",
"#prisma/client": "^3.1.1",
"#types/express": "^4.17.13",
"#types/jest": "^26.0.24",
"#types/node": "^14.17.7",
"#types/supertest": "^2.0.11",
"#typescript-eslint/eslint-plugin": "^4.29.0",
"#typescript-eslint/parser": "^4.29.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "7.2.0",
"eslint-plugin-prettier": "^3.4.0",
"jest": "^26.6.3",
"prettier": "^2.3.2",
"prisma": "^3.1.1",
"supertest": "^6.1.4",
"ts-jest": "^26.5.6",
"ts-loader": "^8.3.0",
"ts-node": "^9.0.0",
"tsconfig-paths": "^3.10.1",
"typescript": "^4.3.5"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
I am able to run my project locally without any errors but on heroku it's stuck on initial login/signup page. I assume that's because it's not interacting with the backend and only displaying frontend.
This is what my project structure looks like,
Project Structure
Content of package.json of my root folder which is react frontend
frontend/package.json
{
"name": "cook-it",
"version": "0.1.0",
"private": true,
"dependencies": {
"#apollo/react-hooks": "4.0.0",
"#material-ui/core": "4.11.3",
"#material-ui/lab": "4.0.0-alpha.57",
"#testing-library/jest-dom": "5.11.9",
"#testing-library/react": "11.2.5",
"#testing-library/user-event": "12.8.0",
"add": "2.0.6",
"antd": "4.15.0",
"apollo-cache-inmemory": "1.6.6",
"apollo-client": "2.6.10",
"apollo-link-context": "1.0.20",
"apollo-link-http": "1.5.17",
"firebase": "8.3.1",
"graphql": "15.5.0",
"graphql-tag": "2.11.0",
"jwt-decode": "3.1.2",
"react": "17.0.1",
"react-bootstrap": "1.5.1",
"react-dom": "17.0.1",
"react-router-dom": "5.2.0",
"react-scripts": "4.0.3",
"semantic-ui-css": "2.4.1",
"semantic-ui-react": "2.0.3",
"web-vitals": "1.1.0",
"yarn": "^1.22.10"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
frontend/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App';
import 'antd/dist/antd.css';
import { ApolloProvider } from '#apollo/react-hooks';
import ApolloClient from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { createHttpLink } from 'apollo-link-http';
import { setContext } from 'apollo-link-context';
import { AuthProvider } from './context/auth';
import 'semantic-ui-css/semantic.min.css'
const httpLink = createHttpLink({
uri: 'http://localhost:5000/'
});
const authLink = setContext(() => {
const token = localStorage.getItem('jwtToken');
return {
headers: {
Authorization: token ? `Bearer ${token}` : ''
}
};
});
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache()
});
ReactDOM.render(
<ApolloProvider client={client}>
<AuthProvider>
<App />
</AuthProvider>
</ApolloProvider>,
document.getElementById('root')
);
frontend/backend/package.json
{
"name": "merng",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"serve": "nodemon index",
"start": "node index"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"apollo-server": "^2.24.0",
"bcryptjs": "^2.4.3",
"csvtojson": "^2.0.10",
"graphql": "^14.3.1",
"jsonwebtoken": "^8.5.1",
"mongoose": "^5.12.7",
"react-router-dom": "^5.0.1",
"semantic-ui-css": "^2.4.1",
"semantic-ui-react": "^0.87.2"
},
"devDependencies": {
"nodemon": "^1.19.1"
}
}
frontend/backend/index.js
const { ApolloServer } = require('apollo-server');
const mongoose = require('mongoose');
const typeDefs = require('./graphql/typeDefs');
const resolvers = require('./graphql/resolvers');
const { MONGODB } = require('./config.js');
const ports = process.env.PORT || 5000
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({ req })
});
mongoose
.connect(MONGODB, { useNewUrlParser: true,useUnifiedTopology: true })
.then(() => {
console.log('MongoDB Connected');
return server.listen({ port: ports });
})
.then((res) => {
console.log(`Server running at ${res.url}`);
});
You deployed your backend to heroku
But did not update backend url in your frontend app
const httpLink = createHttpLink({
uri: 'http://localhost:5000/' // <-- here
});
I am learning test, for now I using jest and supertest to test koa2 server, and I test features is login, but I always receive false when test login feature, I find the require returns an empty object in my controllers/users.js after long time debug, I don't know why and very confuse about that, because everything is OK when I really running the koa2 server, help, thanks.
Here is my code
user.spec.js
import server from "../../app.js"
import request from "supertest"
afterEach(() => {
server.close()
})
test("Failed to login if typing wrong name and password", async () => {
const response = await request(server)
.post("/auth/user")
.send({
name: "wrong",
password: "wrong"
})
expect(response.body.success).toBe(false)
})
test("Successed to login if typing right name and password", async () => {
const response = await request(server)
.post("/auth/user")
.send({
name: "rignt",
password: "right"
})
expect(response.body.success).toBe(true) // always received false
})
app.js
const Koa = require('koa')
const app = new Koa()
const router = require('koa-router')()
const bodyParser = require('koa-bodyparser')
const logger = require('koa-logger')
// const port = 4155
const port = 6155
const auth = require('./server/routes/auth')
const api = require('./server/routes/api')
const users = require('./server/models/users')
const path = require('path')
const serve = require('koa-static')
const mount = require('koa-mount')
const historyApiFallback = require('koa2-history-api-fallback')
// handle database
const objection = require('objection')
const Model = objection.Model
const Knex = require('knex')
const knexConfig = require('./server/config/knexfile')
// Initialize knex
const knex = Knex(knexConfig)
// Bind all Models to a knex instance
Model.knex(knex)
app.use(bodyParser())
app.use(logger())
app.use(async (ctx, next) => {
let start = new Date()
await next()
let ms = new Date() - start
console.log('%s %s - %s', ctx.method, ctx.url, ms)
})
app.on('error', (err, ctx) => {
console.log('server error', err)
})
// api for operate
router.use('/api', api.routes())
// auth for login
router.use('/auth', async (ctx, next) => {
let urlReg = /auth\/user$/
if(urlReg.test(ctx.url)) {
await next()
} else {
const { token } = ctx.request.body
if(global.userTokenObj[token] === undefined) {
const rs = await users.findToken(token)
if(rs.length > 0) {
global.userTokenObj[token] = 1
await next()
} else {
ctx.body = {
code: 1001,
msg: "no login"
}
}
} else {
await next()
}
}
})
router.use('/auth', auth.routes())
app.use(router.routes())
// avoid refesh 404
app.use(historyApiFallback())
// koa static
app.use(mount('/static', serve(path.resolve('static'))))
app.use(serve(path.resolve('dist')))
module.exports = app.listen(port, () => {
console.log(`Koa is listening in ${port}`)
})
// module.exports = app
routes/auth.js
const {
getUserInfo,
postUserAuth,
} = require('../controllers/users')
const {
postFileNode,
postReadFile,
postWriteFile,
postBuildCode,
postDownloadProject
} = require('../controllers/editCode')
const router = require('koa-router')()
router.get('/user/:id', getUserInfo)
router.post('/user', postUserAuth)
router.post('/fileNode', postFileNode)
router.post('/readFile', postReadFile)
router.post('/writeFile', postWriteFile)
router.post('/build', postBuildCode)
router.post('/download', postDownloadProject)
module.exports = router
controllers/users.js
const users = require('../models/users') **// return empty object**
const jwt = require('jsonwebtoken') **// return empty object**
global.userTokenObj = {};
const getUserInfo = async ctx => {
try {
const id = ctx.params.id
const result = await users.getUserById(id)
ctx.body = result
} catch (error) {
console.log(error)
ctx.body = `error is ${error} when get user info`
}
}
const postUserAuth = async ctx => {
try {
const data = ctx.request.body // post data from request.body
const userInfo = await users.getUserByName(data.name)
if (userInfo != null) {
if (userInfo.password != data.password) {
ctx.body = {
success: false,
code: 2,
info: 'wrong password'
}
} else {
const userToken = {
name: userInfo.username,
id: userInfo.id
}
const secret = 'autoactivity'
const token = jwt.sign(userToken, secret)
global.userTokenObj[token] = userInfo.id
const rs = await users.setTokenById(userInfo.id, token)
ctx.body = {
success: true,
code: 0,
info: 'OK',
token
}
}
} else {
ctx.body = {
success: false,
code: 1,
info: 'no users'
}
}
} catch (error) {
console.log(error)
ctx.body = `error is ${error} when post user auth`
}
}
module.exports = {
getUserInfo,
postUserAuth,
}
models/users.js
const Model = require('objection').Model
class Users extends Model {
// Table name is the only required property.
static get tableName () {
return 'users'
}
}
const getUserById = async id => {
try {
const userInfo = await Users.query().findById(id)
return userInfo
} catch (error) {
console.log(error)
return error
}
}
const getUserByName = async name => {
try {
const userInfo = await Users
.query()
.findOne({
username: name
})
return userInfo
} catch (error) {
console.log(error)
return error
}
}
const setTokenById = async (id, token) => {
try {
const result = Users
.query()
.update({token})
.where('id', id)
return result
} catch(error) {
return error
}
}
const findToken = async (token) => {
try {
const result = Users
.query()
.select()
.where('token', token)
return result
} catch(error) {
return error
}
}
module.exports = {
getUserById,
getUserByName,
setTokenById,
findToken
}
package.json
{
"name": "auto-activity",
"version": "1.0.0",
"description": "A Vue.js project",
"author": "shangwenlong652 <dxcqcv#gmail.com>",
"private": true,
"scripts": {
"update": "gulp",
"debugnode": "nodemon --inspect-brk app.js",
"start": "nodemon app.js",
"devserver": "node app.js",
"dev": "node build/dev-server.js",
"server": "pm2 start app.js --name 'auto-activity'",
"server:test": "pm2 start app.js --name 'test-auto-activity'",
"showpm2": "pm2 show auto-activity",
"showpm2:test": "pm2 show test-auto-activity",
"savepm2": "pm2 save",
"logspm2": "pm2 logs",
"restartpm2": "pm2 restart auto-activity",
"restartpm2:test": "pm2 restart test-auto-activity",
"listpm2": "pm2 ls",
"stoppm2": "pm2 stop auto-activity",
"stoppm2:test": "pm2 stop test-auto-activity",
"delpm2": "pm2 delete auto-activity",
"delpm2:test": "pm2 delete test-auto-activity",
"server:dev": "nodemon app.js",
"newMigrate": "knex migrate:make add_name_date_type_to_projects --knexfile=./server/config/knexfile.js",
"migrate": "knex --knexfile=./server/config/knexfile.js migrate:latest",
"rollback": "knex --knexfile=./server/config/knexfile.js migrate:rollback",
"build": "node build/build.js",
"test": "jest --forceExit --runInBand"
},
"jest": {
"verbose": true,
"testURL": "http://localhost/",
"moduleFileExtensions": [
"js"
],
"transform": {
".*\\.(vue)$": "<rootDir>/node_modules/vue-jest",
"^.+\\.js$": "<rootDir>/node_modules/babel-jest"
},
"setupTestFrameworkScriptFile": "mock-local-storage",
"coverageDirectory": "coverage",
"collectCoverage": true,
"coverageReporters": [
"lcov",
"text"
],
"moduleNameMapper": {
"#/(.*)$": "<rootDir>/src/$1"
},
"snapshotSerializers": [
"<rootDir>/node_modules/jest-serializer-vue"
]
},
"dependencies": {
"archiver": "2.1.0",
"await-busboy": "^1.0.1",
"axios": "^0.16.2",
"graceful-fs": "^4.1.11",
"jsonwebtoken": "~8.3.0",
"knex": "~0.15.2",
"koa": "^2.3.0",
"koa-bodyparser": "^4.2.0",
"koa-logger": "^3.0.1",
"koa-mount": "~3.0.0",
"koa-router": "^7.2.1",
"koa-static": "~4.0.1",
"koa2-history-api-fallback": "0.0.5",
"lodash": "^4.17.4",
"mariasql": "^0.2.6",
"mysql": "^2.13.0",
"objection": "^0.8.5",
"recursive-copy": "^2.0.6",
"replacestream": "^4.0.3",
"rimraf": "^2.6.2",
"vue": "2.4.2",
"vue-codemirror": "^4.0.5",
"vue-color": "^2.4.0",
"vue-router": "^2.6.0",
"vue-tree-halower": "^1.5.4",
"vuetify": "^0.15.2",
"vuex": "^2.3.1"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "~6.26.3",
"babel-eslint": "^7.1.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-jest": "~23.4.2",
"babel-loader": "^7.1.1",
"babel-plugin-array-includes": "^2.0.3",
"babel-plugin-istanbul": "^4.1.1",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-object-assign": "^6.22.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-plugin-transform-vue-jsx": "^3.7.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-2": "^6.22.0",
"babel-register": "^6.22.0",
"babel-runtime": "^6.26.0",
"chai": "^3.5.0",
"chalk": "^2.3.0",
"connect-history-api-fallback": "^1.3.0",
"copy-webpack-plugin": "^4.0.1",
"cross-env": "~5.2.0",
"cross-spawn": "^5.0.1",
"css-loader": "^0.28.0",
"cssnano": "^3.10.0",
"del": "^3.0.0",
"dotenv": "~6.0.0",
"eslint": "~5.2.0",
"eslint-config-standard": "^6.2.1",
"eslint-friendly-formatter": "^3.0.0",
"eslint-loader": "^1.7.1",
"eslint-plugin-html": "^3.0.0",
"eslint-plugin-promise": "^3.4.0",
"eslint-plugin-standard": "^2.0.1",
"eventsource-polyfill": "^0.9.6",
"express": "~4.16.3",
"extract-text-webpack-plugin": "^2.0.0",
"file-loader": "^0.11.1",
"friendly-errors-webpack-plugin": "^1.1.3",
"gulp": "~4.0.0",
"gulp-decompress": "^2.0.1",
"html-webpack-plugin": "^2.28.0",
"http-proxy-middleware": "~0.18.0",
"inject-loader": "^3.0.0",
"jest": "~23.4.2",
"jest-serializer-vue": "~2.0.2",
"lolex": "^1.5.2",
"mock-local-storage": "^1.0.5",
"opn": "^5.1.0",
"optimize-css-assets-webpack-plugin": "^2.0.0",
"ora": "^1.2.0",
"pm2": "~3.0.3",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"sinon": "^2.1.0",
"sinon-chai": "^2.8.0",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.1",
"supertest": "~3.1.0",
"url-loader": "~1.0.1",
"vue-jest": "~2.6.0",
"vue-loader": "^12.1.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "2.4.2",
"vue-test-utils": "~1.0.0-beta.11",
"webpack": "^2.6.1",
"webpack-bundle-analyzer": "~2.13.1",
"webpack-dev-middleware": "^1.10.0",
"webpack-hot-middleware": "^2.18.0",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 4.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
.babelrc
{
"presets": [
["env", {
"modules": false,
"targets": {
"chrome": 42,
"firefox": 38,
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-runtime", "transform-vue-jsx"],
"env": {
"test": {
"presets": [["env", { "targets": { "node": "current" }} ]] ,
"plugins": [
"istanbul",
"transform-object-assign",
"array-includes",
"transform-runtime"
]
}
}
}
and the project full code on bitbutck
You're trying to import server from app.js at the very first line of your test code!
import server from "../../app.js"
But in the app.js you're not exporting server constant. You should export server after you passing the app instance to http.createServer() method.
const server = http.createServer(app);
module.exports = server;
Then it should work!