Related
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!!
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
});
Console Error message: v3:1 Uncaught (in promise) IntegrationError: Invalid value for stripe.confirmCardPayment intent secret: value should be a client secret of the form ${id}_secret_${secret}. You specified: .
You can see the error yourself here: https://stormy-forest-38471.herokuapp.com/
Instructions to get error: From the main page, click on MenĂº Diari -> Demanar -> Any of the first 3 buttons -> Select one radio button from each category and then click on Afegeix i paga -> Pagar -> 1st input: 'Random Name', 2nd input: 'Recollida a tenda - Cabrera de Mar', 4th input: '123'...
Then, according to Stripe documentation, you could fill the payment element with the following data in order to achieve a successfull test payment.
Visa Number: '4242 4242 4242 4242'
MM/AA: '04/24'
CVC: '123'
Once you press Pagar, you will get the IntegrationError I'm asking for in the console.
I am trying to deploy a React.js + Node.js + Express web using Heroku.
I have been following this guide, although I am not sure if that's what I need.
https://blog.heroku.com/deploying-react-with-zero-configuration
The web is for a restaurant. They want to sell different lunch plans. I am trying to implement the payment using Stripe. When I test the app in my localhost, everything works fine. However, when I try to deploy it, something breaks.
I guess something must be wrong with my server.js or package.json files... The main problem being that I am a complete noob at Back End...
Everything works as expected (I still have some console logs here and there for development), until I start making requests to the server.
This is my server.js
const { createServer } = require('https');
const express = require('express');
const compression = require('compression');
const morgan = require('morgan');
const path = require('path');
const normalizePort = (port) => parseInt(port, 10);
const PORT = normalizePort(process.env.PORT || 5000);
const app = express();
const dev = app.get('env') !== 'production';
if (!dev) {
app.disable('x-powered-by');
app.use(compression()); // "Will handle a few things for us... (?)"
app.use(morgan('common'));
app.use(express.static(path.join(__dirname, 'build')));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'build', 'index.html'));
});
}
const server = createServer(app);
const { resolve } = require('path');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
app.use(express.json());
const calculateOrderAmount = (items) => {
console.log(items);
let drinksTotal = 0;
const { water, cola, colaZero, beer, lemonFanta, orangeFanta } = items.drinks;
drinksTotal += water * 100;
drinksTotal += (cola + colaZero + lemonFanta + orangeFanta) * 130;
drinksTotal += beer * 150;
let foodTotal = 0;
foodTotal += items.primerSegonCount * 895;
foodTotal += items.dosPrimersCount * 795;
foodTotal += items.platPostresCount * 695;
grandTotal = parseInt(drinksTotal + foodTotal);
console.log(grandTotal);
return grandTotal;
};
app.post('/create-payment-intent', async(req, res) => {
const { items } = req.body;
console.log(items);
// Create a PaymentIntent with the order amount and currency
const paymentIntent = await stripe.paymentIntents.create({
amount: calculateOrderAmount(items),
currency: 'eur'
});
res.send({
clientSecret: '123_secret_123' //paymentIntent.client_secret
});
});
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
server.listen(PORT, (err) => {
if (err) throw err;
console.log('Server started on port ' + PORT);
});
This is my package.json
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"engine": {
"node": "12.16.3",
"npm": "6.14.5"
},
"dependencies": {
"#stripe/react-stripe-js": "^1.1.2",
"#stripe/stripe-js": "^1.7.0",
"#testing-library/jest-dom": "^4.2.4",
"#testing-library/react": "^9.5.0",
"#testing-library/user-event": "^7.2.1",
"axios": "^0.19.2",
"boostrap": "^2.0.0",
"bootstrap": "^4.5.0",
"compression": "^1.7.4",
"concurrently": "^5.2.0",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"emailjs-com": "^2.4.1",
"express": "^4.17.1",
"morgan": "^1.10.0",
"node-sass": "^4.14.1",
"nodemon": "^2.0.4",
"react": "^16.13.1",
"react-bootstrap": "^1.0.1",
"react-dom": "^16.13.1",
"react-redux": "^7.2.0",
"react-scripts": "3.4.1",
"react-scroll": "^1.7.16",
"react-stripe-checkout": "^2.6.3",
"reactstrap": "^8.5.1",
"stripe": "^8.67.0",
"uuid": "^8.2.0",
"uuidv4": "^6.1.1"
},
"scripts": {
"start": "react-scripts start",
"start-dev": "nodemon server.js",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"dev": "concurrently \"npm run-script start-dev\" \"npm run-script start\"",
"heroku-postbuild": "npm install && npm run build"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [">0.2%", "not dead", "not op_mini all"],
"development": ["last 1 chrome version", "last 1 firefox version", "last 1 safari version"]
},
"homepage": "."
}
This is the React.js file in charge of the payment
import React, { useState, useEffect } from 'react';
import { CardElement, useStripe, useElements } from '#stripe/react-stripe-js';
// Bootstrap
import { Container, Row, Col, Button, Spinner, Form } from 'react-bootstrap';
export default function CheckoutForm(props) {
const [ succeeded, setSucceeded ] = useState(false);
const [ error, setError ] = useState(null);
const [ processing, setProcessing ] = useState(false);
const [ disabled, setDisabled ] = useState(true);
const [ clientSecret, setClientSecret ] = useState('');
const stripe = useStripe();
const elements = useElements();
useEffect(() => {
console.log('inside use Effect');
// Create PaymentIntent as soon as the page loads
window
.fetch('/create-payment-intent', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
items: {
drinks: props.drinksOrdered,
primerSegonCount: props.primerSegonCount,
dosPrimersCount: props.dosPrimersCount,
platPostresCount: props.platPostresCount
}
})
})
.then((res) => {
console.log(res);
return res.json();
})
.then((data) => {
console.log(data.clientSecret);
console.log(data);
setClientSecret(data.clientSecret);
});
}, []);
const cardStyle = {
base: {
backgroundColor: 'white',
color: 'grey',
fontFamily: 'Montserrat, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#c6c6c6'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
},
complete: {
color: 'green',
iconColor: 'green'
}
};
const handleCardChange = async (event) => {
// Listen for changes in the CardElement
// and display any errors as the customer types their card details
setDisabled(event.empty);
setError(event.error ? event.error.message : '');
};
const handleSubmit = async (ev) => {
ev.preventDefault();
setProcessing(true);
const payload = await stripe.confirmCardPayment(clientSecret, {
payment_method: {
card: elements.getElement(CardElement),
billing_details: {
name: ev.target.name.value
}
}
});
// Handle Error
if (payload.error) {
setError(`Payment failed ${payload.error.message}`);
setProcessing(false);
} else if (payload.paymentIntent.status === 'succeeded') {
// If payment succeeded, send eMail with details
props.sendEmail();
setSucceeded(true);
setError(null);
setTimeout(() => {
window.location.replace('https://www.cateringroser.cat');
}, 10000);
}
};
Thank you so much in advance.
Gerard
Well, as I guessed, I had to figure this one out myself. :-)
If you go back to my original post, you will be able to see the code I had for my server.js, package.json and CheckoutForm.jsx files.
The CheckoutForm.jsx was ok. Below you can see the modified versions of the code for:
server.js
const express = require('express');
const path = require('path');
const app = express();
const { resolve } = require('path');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
app.use(express.static(path.join(__dirname, 'build')));
app.use(express.json());
// app.use(express.json());
const calculateOrderAmount = (items) => {
let drinksTotal = 0;
const { water, cola, colaZero, beer, lemonFanta, orangeFanta } = items.drinks;
drinksTotal += water * 100;
drinksTotal += (cola + colaZero + lemonFanta + orangeFanta) * 130;
drinksTotal += beer * 150;
let foodTotal = 0;
foodTotal += items.primerSegonCount * 895;
foodTotal += items.dosPrimersCount * 795;
foodTotal += items.platPostresCount * 695;
grandTotal = parseInt(drinksTotal + foodTotal);
console.log(grandTotal);
return grandTotal;
};
app.post('/create-payment-intent', async(req, res) => {
const { items } = req.body;
// Create a PaymentIntent with the order amount and currency
const paymentIntent = await stripe.paymentIntents.create({
amount: 100, //calculateOrderAmount(items),
currency: 'eur'
});
res.send({
clientSecret: paymentIntent.client_secret
});
});
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.listen(process.env.PORT || 8080);
package.json
{
"name": "my-app",
"version": "1.0.0",
"description": "take away restaurant app",
"private": true,
"engines": {
"node": "12.x"
},
"dependencies": {
"#stripe/react-stripe-js": "^1.1.2",
"#stripe/stripe-js": "^1.7.0",
"#testing-library/jest-dom": "^4.2.4",
"#testing-library/react": "^9.5.0",
"#testing-library/user-event": "^7.2.1",
"boostrap": "^2.0.0",
"bootstrap": "^4.5.0",
"concurrently": "^5.2.0",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"emailjs-com": "^2.4.1",
"express": "^4.17.1",
"node-sass": "^4.14.1",
"nodemon": "^2.0.4",
"react": "^16.13.1",
"react-bootstrap": "^1.0.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.1",
"react-scroll": "^1.7.16",
"react-stripe-checkout": "^2.6.3",
"reactstrap": "^8.5.1",
"stripe": "^8.67.0"
},
"scripts": {
"start": "node server.js",
"start-dev": "nodemon server.js",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"dev": "concurrently \"npm run-script start-dev\" \"npm run-script start\"",
"heroku-postbuild": "npm install && npm run build"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [">0.2%", "not dead", "not op_mini all"],
"development": ["last 1 chrome version", "last 1 firefox version", "last 1 safari version"]
}
}
I ended up using what I knew on server.js. I deleted everything I didn't understand and kept the original code.
One of the most important things was to understand what happens when you deploy an app using node/express (or at least I think I understand).
First, I thought I needed to deploy both the Front and Back End sepparately, and then link them somehow.
What (I think) I really needed to do was to serve the Front End files from the server. In my first attempts to deploy, I was really serving them from React directly, so all I was getting was an interactive painting, in which I could navigate, but hadn't the opportunity to communicate with the server (since it was not connected).
What was the solution?
Serve the /build folder and it's files as explained in React's documentation on deployment.
Remove the "proxy": "http://localhost:5000" from package.json. Also remove "homepage":"xxx" if you had so.
Add "start": "node server.js" to your package.json. You want to open your web through node.
I also added a Procfile as recommended in Heroku's best practices for deployment with Node.js. All I have in the file is the following code: web: node server.js.
It just makes sense now. I can accept payments and communicate with my server.
As a sidenote: I didn't delete any of the Stripe specific code in my server.js, since it might help other users using Stripe to handle payments.
use secret key instead of public key in index.js in functions folder.
I am trying to create a register form using React on the frontend, and Express with MongoDB atlas on the backend using typescript everywhere. When I click the submit button, nothing happens, but when I post the request using Postman application I get the following output after a long delay:
"Error occured while trying to proxy to: localhost:3000/api/register?email=test123#gmail.com&password=test123"
And I get an empty record in my database, something like that:
_id: Object("5d7679d8fa74d54ebb0534b9")
__v: 0
Also after long delay i see this error in terminal in vscode:
[HPM] Error occurred while trying to proxy request /api/register?email=test123#gmail.com&password=test123 from localhost:3000 to http://localhost:5000/ (ECONNRESET) (https://nodejs.org/api/errors.html#errors_common_system_errors)
I created React frontend using "create-react-app", and already did the following instructions in post: When specified, "proxy" in package.json must be a string .
The thing is that I also have a contact form that is working and a proxy is working there, and I get the email when clicking submit in a contact form.
I show my package.json in my main folder (which I use to run my frontend and backend concurrently) in which I have two folders: backend (in which I have dist folder to which I transpile my .ts files to .js), and frontend folder.
{
"name": "ecommerce-template-mern",
"version": "1.0.0",
"main": "main.js",
"license": "MIT",
"scripts": {
"build": "tsc -p backend/.",
"backend": "nodemon backend/dist/main.js",
"frontend": "cd frontend && npm run start",
"dev": "concurrently --kill-others-on-fail \"npm run backend\" \"npm run frontend\""
},
"devDependencies": {
"concurrently": "^4.1.2",
"nodemon": "^1.19.2",
"tsc-watch": "^3.0.0"
},
"dependencies": {
"body-parser": "^1.19.0",
"express": "^4.17.1",
"http-proxy-middleware": "^0.20.0",
"nodemailer": "^6.3.0",
"typescript": "^3.6.2"
}
}
And package.json in frontend folder in which I have a proxy.
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"proxy": "http://localhost:5000",
"dependencies": {
"#material-ui/core": "^4.4.0",
"#material-ui/icons": "^4.2.1",
"#types/jest": "24.0.18",
"#types/node": "^12.7.2",
"#types/react": "^16.9.2",
"#types/react-dom": "^16.9.0",
"axios": "^0.19.0",
"formik": "^1.5.8",
"http-proxy-middleware": "^0.20.0",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"react-router-dom": "^5.0.1",
"react-scripts": "3.1.1",
"styled-components": "^4.3.2",
"typescript": "3.5.3",
"yup": "^0.27.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"#types/enzyme": "^3.10.3",
"#types/react-router": "^5.0.3",
"#types/react-router-dom": "^4.3.5",
"#types/reactstrap": "^8.0.4",
"#types/styled-components": "^4.1.18",
"#types/yup": "^0.26.23",
"bootstrap": "^4.3.1",
"enzyme": "^3.10.0",
"nodemon": "^1.19.1",
"react-addons-test-utils": "^15.6.2",
"react-bootstrap": "^1.0.0-beta.12",
"reactstrap": "^8.0.1",
"ts-node": "^8.3.0",
"ts-node-dev": "^1.0.0-pre.42"
}
}
Below is my code that should put data in my MongoDB atlas:
import express, { Application, Request, Response } from "express";
import bodyParser from "body-parser";
import * as dotenv from "dotenv";
dotenv.config({ path: __dirname + "/../.env" });
const app: Application = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
const dbURI: any = process.env.MONGO_CONNECTION;
mongoose
.connect(dbURI, {
useNewUrlParser: true
})
.then(() => console.log("MongoDB Connected"))
.catch(err => console.log(err));
const Schema = mongoose.Schema;
const UserSchema = new Schema({ email: String, password: String });
interface IUser extends mongoose.Document {
email: string;
password: string;
}
const User = mongoose.model<IUser>("User", UserSchema);
app.post("/api/register", (req: Request, res: Response) => {
const newUser = new User({
email: req.body.email,
password: req.body.password
});
newUser.save().then(() => console.log("New user in database!"));
});
const PORT = 5000
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
});
Above code outputs "New user in database!", but as I wrote on top of post I get an error, and "empty" object in a database.
Also, another thing to note is that when I run this code below:
const newUser2 = new User({
email: "test#gmail.com",
password: "123"
});
newUser2.save().then(() => console.log("New user in database!"));
I get the desired data record in MongoDB.
I also attach my register form in React below (almost same contact form works when sending mail, so I don't think there is problem with it):
import axios from "axios";
import { Field, Form, FormikProps, withFormik } from "formik";
import React from "react";
import * as Yup from "yup";
interface FormValues {
email: string;
password: string;
}
interface MyFormProps {
initialEmail?: string;
initialPassword?: string;
}
class RegisterForm extends React.PureComponent<FormikProps<FormValues>> {
render() {
const { values, touched, errors, isSubmitting } = this.props;
return (
<Form>
<Field
type="email"
name="email"
placeholder="email"
value={values.email}
/>
{touched.email && errors.email && <div>{errors.email}</div>}
<Field
type="password"
name="password"
placeholder="password"
value={values.password}
/>
{touched.password && errors.password && <div>{errors.password}</div>}
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</Form>
);
}
}
const FormikRegisterForm = withFormik<MyFormProps, FormValues>({
mapPropsToValues: props => {
return {
email: props.initialEmail || "",
password: ""
};
},
validationSchema: Yup.object().shape({
login: Yup.string()
.email("email is not valid")
.required("email is required"),
password: Yup.string().required("password is required")
}),
handleSubmit(values) {
const { email, password } = values;
axios
.post("/api/register", {
email,
password
})
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
}
})(RegisterForm);
export default FormikRegisterForm;
What can I change to get the desired result it database after clicking submit button in register form? Thanks for help in advance.
This is Post type API.
You should send email & password inside the body.
So in code, you will access them by using req.body.email & req.body.password
localhost:3000/api/register?email=test123#gmail.com&password=test123"
If you sending like this then you should access email and password
req.query.email
req.query.password
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!