laravel vuejs socket.io Access-Control-Allow-Origin - node.js

hello laravel i am trying to use socket.io with vuejs but i keep getting errors, i couldn't understand where i went wrong.
What I want to do is clearly when the form is submitted, it is listened to by the audience and instantly sees the post to the manager. Of course, this is not happening. I am using socket.io wrong. I couldn't understand.
laravel.local/:1 Access to XMLHttpRequest at 'http://localhost:3000/socket.io/?EIO=3&transport=polling&t=NNy_ruN' from origin 'http://laravel.local:8000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
server.js
var app = require('express')();
const server = require('http').createServer();
const io = require('socket.io')(server);
// http.listen(3000);
app.get('/', (req, res) => {
res.send('<h1>Hello world</h1>');
});
io.on('connection', socket => {
socket.on('new_appointment_create', function () {
console.log("okey");
io.emit('admin_appointment_list');
});
});
server.listen(3000, function () {
console.log('Listening on Port: 3000');
});
formpost.vue
import io from 'socket.io-client';
var socket = io('http://localhost:3000');
.then((response) => {
if (response.status) {
socket.emit('new_appointment_create');
this.completeForm = false;
}
})
adminlist.vue
import io from 'socket.io-client';
var socket = io('http://localh
created() {
this.getData();
socket.on('admin_appointment_list', () => {
console.log("list okey");
this.getData();
});
},
package.json
"devDependencies": {
"axios": "^0.19",
"bootstrap": "^4.0.0",
"cross-env": "^7.0",
"jquery": "^3.2",
"laravel-mix": "^5.0.1",
"lodash": "^4.17.19",
"popper.js": "^1.12",
"resolve-url-loader": "^3.1.2",
"sass": "^1.20.1",
"sass-loader": "^8.0.0",
"socket.io": "^3.0.3",
"socket.io-client": "^2.3.1",
"vue": "^2.5.17",
"vue-template-compiler": "^2.6.10"
},
"dependencies": {
"express": "^4.17.1",
"laravel-vue-pagination": "^2.3.1",
"v-mask": "^2.2.3",
"vue-resource": "^1.5.1"
}

The error you're receiving is due a feature from browsers called preflight that consists in the browser doing kind of "ping request" to the server that you want to access with an HTTP OPTIONS to get the response headers and check for the Access-Control-Allow-Origin header.
The idea is that your API should declare in this header which origins(domains) are allowed to consume your resources/APIs.
The "easiest" way to resolve this is to configure your API to respond with Access-Control-Allow-Origin: *.
But it's not the correct way, you shouldn't go to production * as configuration.
In my opinion the correct way to resolve this is use a http proxy while in dev, and in production you configure your Apache or NGINX or whatever http server you'll use to do so.
If you're using #vue/cli, it already has an option to configure a proxy server for development, take a look in the docs.
But fastforwarding the refactoring needed if you use the devProxy option, you will basically map an resource/context in the same server/port which is served you front-end to proxy requests/connections to your websocket.
Then you'll stop calling the http://localhost:3000 directly and will use the mapped resource/context.
For example:
In your vue.config.js
module.exports = {
devServer: {
proxy: {
'^/api': {
target: 'http://localhost:300',
ws: true
}
}
}
}
Then you will create the socket:
var socket = io(`${location.protocol}//${location.host}/api`);
Being honest with you, I've never used the devProxy with websockets, but the docs says it has support for it.

Related

Node.JS integration test error: listen EADDRINUSE: address already in use :::3000

I've used jest and supertest for integration tests. In afterEach section I closed server:
let server;
describe('/api/user', () => {
beforeEach(() => {
server = require('../../../app');
});
afterEach(async () => {
await server.close();
});
//some tests
});
But by running npm test I get this error:
listen EADDRINUSE: address already in use :::3200
When I using just one something.test.js file, everything is OK. The problem is when I add a new something.test.js. What's wrong?
Here is the package.json:
{
"name": "users",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "jest --watchAll"
},
"author": "Saeed Heidarbozorg",
"license": "ISC",
"dependencies": {
"config": "^3.3.4",
"express": "^4.17.1",
"express-async-errors": "^3.1.1",
"joi": "^17.4.0",
"morgan": "^1.10.0",
"pg": "^8.5.1",
"winston": "^3.3.3"
},
"devDependencies": {
"jest": "^26.6.3",
"supertest": "^6.1.3"
}
}
tl;dr in a test environment, you don't want to create an http server at all, just test your express app instance.
If you want to post your code from your app.js, I can probably give you a quicker fix.
In general this is the way I structure it to facilitate accomplishing the tl;dr:
app.js contains all the express-y stuff:
import express from 'express';
const app = express();
// ... do all your express-y stuff
export default app;
index.jsis the entry point to the app that starts the server. This file IS NOT NEEDED during testing:
import http from 'http';
import app from './app';
http.createServer(app).listen(...);
When testing, you don't need index.js at all, just import your express app from app.js and test it:
Some test file:
import app from './app';
import request from 'supertest';
describe('...',() => {
test('...', async () => {
expect((await request(app).get('/some/route')).status).toBe(200);
});
});

Socket.io: Overwritten generateId() gets executed, but the returned id seems to be ignored

I am trying to overwrite generateId. I used for that the example from the docs. But the behaviour is strange. generateId gets executed, but socket.id is like a regular id, instead of the desired result "custom:id:" + 1.
Code
const express = require('express');
const socketIO = require('socket.io');
const http = require('http');
const app = express();
app.set('port', 5000);
const server = http.Server(app);
const io = socketIO(server);
io.engine.generateId = (req) => {
console.log('This here gets printed')
return "custom:id:" + 1;
}
io.on('connection', socket => {
console.log(socket.id);
})
server.listen(5000, async () => {
console.log('Starting server on port 5000');
});
Expected Output: custom:id:1
Actual Output: MNWAC86CdPXB7thTAAAA
package.json Dependencies
"dependencies": {
"base64id": "^2.0.0",
"clean-webpack-plugin": "^3.0.0",
"cookie": "^0.4.1",
"css-loader": "^5.0.1",
"express": "^4.17.1",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^4.5.0",
"jquery": "^3.5.1",
"ngrok": "^3.4.0",
"sass": "^1.29.0",
"sass-loader": "^10.1.0",
"socket-io": "^1.0.0",
"socket.io": "^3.0.3",
"socket.io-client": "^3.0.3",
"style-loader": "^2.0.0",
"webpack": "^5.7.0",
"webpack-dev-middleware": "^4.0.2"
}
I was having this problem also. Apparently it is due to a change in socket.io version 4. The code below is from node_modules/socket.io/dist/socket.js. It appears the change is for security reasons.
if (client.conn.protocol === 3) {
// #ts-ignore
this.id = nsp.name !== "/" ? nsp.name + "#" + client.id : client.id;
}
else {
this.id = base64id_1.default.generateId(); // don't reuse the Engine.IO id because it's sensitive information
}
client.id will have the value you return in your generateId but it won't be used as we expected.
After looking through the source and debugging, it looks like the socket id can be set in an io.use (example below.)
var idIndex = 0
io.use((socket, next) => {
socket.id = `ID_${idIndex++}`;
next();
});

Webpack not detecing proxy for local development

I'm having trouble getting a little proxy working with my React app. I'm trying to use a little express server to keep some API keys secret so that my application can make use of a 3rd party API (the github API to be specific). I have a small express app running that I can query to get the API keys out of a .env file and attach those keys to my request to the 3rd party API.
Presently, I am able to start the front-end application and the express app simultaneously, and I can query the express app and get a response using my browser.
I'm trying to configure webpack to proxy my requests through this express app. In my webpack.config.js file I have:
//
devServer: {
port: 8080,
proxy: {
'/api/**': {
target: 'http://localhost:3000',
secure: false,
changeOrigin: true
}
}
}
//
Front-end application is running on port 8080, and my Express app is running on port 3000, both on the localhost.
In my React App, for trying to test whether this proxy is being detected/used, I have the following in a component:
//
handleSubmit(event) {
event.preventDefault();
fetch('/api/secret')
.then(res => {
console.log('RES: ', res)
res.json()
})
.then(data => {
console.log("Data: ", data)
return JSON.stringify(data)
})
this.props.onSubmit(this.state.username)
}
//
The backend code is super simple at the moment, but here it is:
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
require('dotenv').config();
// Initialize app
const app = express();
const port = 3000;
// Configure
app.use(bodyParser.json())
app.use(cors())
app.get('/secret', (req, res) => {
res.status(200)
res.send({ aSecret: process.env.<API_KEY> })
})
app.listen(port, () => console.log(`App is running on port ${port}`))
In my package.json I have the following (relevant script and dependencies):
...
...
"start": "concurrently --kill-others \"webpack-dev-server\" \"npm run server\"",
"server": "nodemon server/index.js"
},
"babel": {
"presets": [
"#babel/preset-env",
"#babel/preset-react"
]
},
"dependencies": {
"prop-types": "^15.7.2",
"react": "^16.13.0",
"react-dom": "^16.13.0",
"react-icons": "^3.9.0"
},
"devDependencies": {
"#babel/core": "^7.8.7",
"#babel/preset-env": "^7.8.7",
"#babel/preset-react": "^7.8.3",
"babel-loader": "^8.0.6",
"body-parser": "^1.19.0",
"concurrently": "^5.1.0",
"cors": "^2.8.5",
"css-loader": "^3.4.2",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"html-webpack-plugin": "^3.2.0",
"nodemon": "^2.0.2",
"style-loader": "^1.1.3",
"svg-inline-loader": "^0.8.2",
"webpack": "^4.42.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3"
},
"proxy": "http://localhost:3000"
}
As you can see, in the component I'm (attempting to) making a request to api/secret and hoping to get back in the response the API key that I have stored in my .env.
When I query this route in my browser using fetch('http://localhost:3000/secret') I am able to access the API key successfully, so I know that when I run the npm run start script that both the React application and the Express application are starting up simultaneously.
When I click the button in my React component that sends a request to /api/secret I get the following output in the browser console (in keeping with the console logs I have in the react component at the moment):
I'm just not sure at this point what I'm doing wrong with the proxy configuration in the devServer webpack configuration.
I can see that the hostname is being automatically prepended to the /api/secret in the fetch within the React component.
Stated Goal: Webpack successfully detects the proxy server I'm using to server requests to the 3rd party (GitHub) API.
I apologize if this question is a repeat, I've spent several hours researching and fiddling with this configuration and have been unsuccessful in finding out how to configure this. This is my first attempt at spinning up a little proxy server as well. Thanks in advance for any help!
You need to return res.json()
handleSubmit(event) {
event.preventDefault();
fetch('/api/secret')
.then(res => {
console.log('RES: ', res)
return res.json()
})
.then(data => {
console.log("Data: ", data)
return JSON.stringify(data)
})
this.props.onSubmit(this.state.username)
}

Proxy Routing on Angular App deployed on Heroku

I have an Angular app that is talking to a REST service.
When I run the Angular app local with the CLI, correctly proxies all /api requests to the REST service. When I try to build the app and run through a server.js (so that I can deploy the app to Heroku) I lose the proxy routing.
The REST service is deployed on Heroku and runs fine.
I run the Angular with:
ng serve
My proxy.conf.json
{
"/api": {
"target": "https://my-app.herokuapp.com",
"secure": true,
"changeOrigin": true
}
}
I created a server.js as described in this article so that I can deploy onto Heroku.
// server.js
const express = require('express');
const app = express();
const path = require('path');
// If an incoming request uses
// a protocol other than HTTPS,
// redirect that request to the
// same url but with HTTPS
const forceSSL = function () {
return function (req, res, next) {
if (req.headers['x-forwarded-proto'] !== 'https') {
return res.redirect(
['https://', req.get('Host'), req.url].join('')
);
}
next();
}
}
// Instruct the app
// to use the forceSSL
// middleware
app.use(forceSSL());
// Run the app by serving the static files
// in the dist directory
app.use(express.static(__dirname + '/dist'));
// For all GET requests, send back index.html
// so that PathLocationStrategy can be used
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname + '/dist/index.html'));
});
// Start the app by listening on the default
// Heroku port
app.listen(process.env.PORT || 4200);
I also set up a post install build in my package.json scripts:
{
"name": "catalog-manager-client",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "node server.js",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"postinstall": "ng build --aot"
},
"private": true,
"dependencies": {
"#angular/animations": "^6.0.3",
"#angular/cdk": "^6.2.1",
"#angular/cli": "~6.0.8",
"#angular/language-service": "^6.0.3",
"#angular/common": "^6.0.3",
"#angular/compiler": "^6.0.3",
"#angular/compiler-cli": "^6.0.3",
"#angular/core": "^6.0.3",
"#angular/flex-layout": "^6.0.0-beta.16",
"#angular/forms": "^6.0.5",
"#angular/http": "^6.0.3",
"#angular/material": "^6.2.1",
"#angular/platform-browser": "^6.0.3",
"#angular/platform-browser-dynamic": "^6.0.3",
"#angular/router": "^6.0.3",
"#swimlane/ngx-charts": "^8.0.2",
"#swimlane/ngx-datatable": "^13.0.1",
"core-js": "^2.5.4",
"express": "^4.16.4",
"hammerjs": "^2.0.8",
"jquery": "^3.3.1",
"moment": "^2.22.2",
"ngx-perfect-scrollbar": "^6.2.0",
"ngx-quill": "^3.2.0",
"rxjs": "^6.0.0",
"rxjs-compat": "^6.2.1",
"rxjs-tslint": "^0.1.4",
"zone.js": "^0.8.26"
},
"devDependencies": {
"#angular-devkit/build-angular": "~0.6.8",
"typescript": "~2.7.2",
"#types/jasmine": "~2.8.6",
"#types/jasminewd2": "~2.0.3",
"#types/node": "~8.9.4",
"codelyzer": "~4.2.1",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~1.7.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.0",
"karma-jasmine": "~1.1.1",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.3.0",
"ts-node": "~5.0.1",
"tslint": "~5.9.1"
},
"engines": {
"node": "9.11.2",
"npm": "6.5.0"
}
}
I am an Angular novice so I could be making a fundamental mistake, but how do I modify the server.js to use the proxy.conf.json settings?
The explanation falls into the yes, you're making a fundamental mistake category, but I've seen enough similar questions that I thought an explanation might just help the next dev.
The Angular CLI is running a full http server. The Angular UI is fully compiled and the CLI is serving it as static content from the /dist directory.
The proxy.conf.json settings are for the Server run by the Angular CLI, it has nothing to do with your compiled code.
When you move from a local environment to something like Heroku you need a server to take the place of the Angular CLI. This is where all the examples of node.js and express come in. The simple server.js file they walk you through is enough to set up a basic static content server. And that's fine, because your Angular code is static content!
But if you need routing to a dynamic backend server via a proxy.conf.json, well, your simple static server doesn't know anything about that.
In my case, my backend server runs on Koa, so I added static routing to the Angular code.
const router = require('koa-router')();
const body = require('koa-body')({ text: false });
const send = require('koa-send');
const fs = require('fs');
/**
* Code about server routes ommited
*/
async function main(ctx, next) {
//All dynamic routes start with "/api"
if (/\/api\//.test(ctx.path)) {
try {
await next();
}
catch (error) {
if (error instanceof ApplicationError) {
logger.error(error, { data: error.data, stack: error.stack });
ctx.status = error.code;
} else {
ctx.status = 500;
logger.error(error.message, { stack: error.stack });
}
}
return;
} else {
//Not a dynamic route, serve static content
if ((ctx.path != "/") && (fs.existsSync('dist' + ctx.path))) {
await send(ctx, 'dist' + ctx.path);
} else {
await send(ctx, 'dist/index.html');
}
}
}
module.exports = app => {
app.use(main);
app.use(router.routes());
};
NOTE - this isn't a performant solution for any kind of high workload, but if you've got a very small project that doesn't justify spending resources setting up something more scalable, this will work until you get bigger.
Any One looking for Implementation of angular application using proxy api on heroku you can use WebpackDev Server and http-proxy-middleware in server.js
npm install http-proxy-middleware
npm install webpack webpack-dev-server
webpack.config.js
const path = require('path');
module.exports = {
entry:'./src/index.js',//no implemenation needed by default webpack verification
mode: 'development',
devServer: {
historyApiFallback: true,// handle 404 cannot get error after refreshing url
https: true,//secure the server
compress: true,//invalid header multiple url proxy
client: {
webSocketURL: 'ws://0.0.0.0:8080/ws',// handle Invalid header error in heroku port 8080 maps in server.js
},
static: {
directory: path.join(__dirname, '/dist/<app-name>'),
},
proxy: {
/** Same as proxy.conf.json or proxy.conf.js */
' /api1/*': {
target: 'https://<other-heroku-deployed-url>',
changeOrigin:true,
secure:false,
pathRewrite: {
'^/api1':'https://<other-heroku-deployed-url>/api1' },
},
' /api2/*': {
target: 'https://<other-heroku-deployed-url>',
changeOrigin:true,
secure:false,
pathRewrite: {
'^/api2':'https://<other-heroku-deployed-url>/api2' },
}
},
},
};
server.js
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
const Webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
/** this is custom js to help proxy in server.js*/
const webpackConfig = require('./webpack.config.js');
const compiler = Webpack(webpackConfig);
const devServerOptions = { ...webpackConfig.devServer, open: true };
const server = new WebpackDevServer(devServerOptions, compiler);
const runServer = async () => {
console.log('Starting server...');
await server.start();
};
runServer();
/** If you have error creating proxy <app-url> to localhost
* Heroku internally redirect the Server port 8080 .
* For that reason we need to open listener port(I used 3000 here) redirect
through http-proxy-middleware*/
app.use("/*", createProxyMiddleware(
{ target: "https://localhost:8080",
ws: true ,
changeOrigin: true,
secure:false,
router: {
'dev.localhost:3000': 'https://localhost:8080',
},}))
app.listen(process.env.PORT || 3000)
npm start or node server.js

CORS/CORB issue with React/Node/Express and google OAuth

I have a react app that I'm trying to add a Node/Express/MySQL backend to with OAuth. My React app is hosted on localhost:3000 and the express server is on localhost:4000. I added "proxy":"http://localhost:4000" to the react app's package.json file to send requests to the server. The Authorized Javascript Origin for the OAuth is http://localhost:4000. The Authorized redirect URI is http://localhost:4000/auth/google/redirect.
These are the errors I get in the browser's console when I try to get to the route on the server:
One says No 'Access-Control-Allow-Origin' header is present on the requested resource.
The other says 'Cross-Origin Read Blocking (CORB) blocked cross-origin response....with MIME type text/html.'
I have no clue what I'm doing wrong and I've been stuck since yesterday.
Failed to load https://accounts.google.com/o/oauth2/v2/auth?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A4000%2Fauth%2Fgoogle%2Fredirect&scope=profile&client_id={clientiddeletedbyme}.apps.googleusercontent.com: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.
Cross-Origin Read Blocking (CORB) blocked cross-origin response https://accounts.google.com/o/oauth2/v2/auth?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A4000%2Fauth%2Fgoogle%2Fredirect&scope=profile&client_id={iddeletedbyme}apps.googleusercontent.com with MIME type text/html. See https://www.chromestatus.com/feature/5629709824032768 for more details.
Here is my code in the package.json file for my react app:
{
"name": "workout_tracker",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.18.0",
"firebase": "^5.3.0",
"jw-paginate": "^1.0.2",
"jw-react-pagination": "^1.0.7",
"normalize.css": "^8.0.0",
"random-id": "0.0.2",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-headroom": "^2.2.2",
"react-icons-kit": "^1.1.6",
"react-redux": "^5.0.7",
"react-router-dom": "^4.3.1",
"react-scripts-cssmodules": "^1.1.10",
"react-swipe-to-delete-component": "^0.3.4",
"react-swipeout": "^1.1.1",
"redux": "^4.0.0",
"redux-thunk": "^2.3.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"devDependencies": {
"redux-devtools-extension": "^2.13.5"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"proxy":"http://localhost:4000"
}
Here is the code in my react app that sends the request to the server:
express=()=>{
axiosInstance.get("/google").then(res=>{
console.log(res);
}).catch(err=>console.log(err));
}
Here is the code in the server
let express = require("express");
let cors= require("cors");
let mysql = require("mysql");
const util = require("util");
const passportSetup = require("./config/passport-setup");
const passport = require("passport");
let app = express();
let connection =mysql.createConnection({
host: "localhost",
user: "root",
password: "root",
database: "Workout_Tracker",
socketPath: '/Applications/MAMP/tmp/mysql/mysql.sock'
});
app.use(cors(
{origin:"http://localhost:3000",
credentials:true,
allowHeaders:"Content-Type"
}
));
app.options("/google", cors());
app.get("/google", cors(), passport.authenticate("google",{
scope:['profile']
}));
...omitted a bunch of SQL queries
app.listen(4000, () => console.log("Listening on port 4000!"));
Here is the sample code of a new middleware you need to install to express BEFORE you define any routes:
const cors = require('cors');
app.use('*', function(req, res, next) {
//replace localhost:8080 to the ip address:port of your server
res.header("Access-Control-Allow-Origin", "http://localhost:8080");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type');
res.header('Access-Control-Allow-Credentials', true);
next();
});
//enable pre-flight
app.options('*', cors());
But before copy and pasting, just so you know that you need to npm install cors --save before importing the cors. The above sample code simply means:
we allow a different ip address to access the server for all the routes you defined
Allow a 'X-Requested-With' and a 'Content-Type' parameters inside the header. You normally don't have to specifically define these but its good to have them.
Only with the allow credentials set to true your session/cookies are able to store during front-end refreshing the pages, which I think might be helpful for your future development.
pre-flight request will also be allowed, which many Http libraries will send by default.
for your front-end, if you are using axios, you do need: axios.create({
withCredentials: true
}); to say: both react and express are agree to use CORS. And likewise in the other http libraries.
Here is some documentation you can have a look at:
https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Instead of using AJAX to request the endpoint, I should have navigated there through the browser. I used an <a> tag with an href of "http://localhost:4000" and it worked as expected.
Here's my sample use of CORS with expressJs, this is needed to be done on backend or server-side. Server stops access of it's API from outside world not client-side.
// IP's allowed all access this server
let whitelist = ['http://localhost:3000', 'http://127.0.0.1:3000'];
let corsOptions = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
};
// Cross-Origin Resource Sharing
app.use(cors(corsOptions));

Resources