Websocket error that is only occuring on live server - node.js

Bizarre situation going on here with my websocket. It's giving me the error
Error during WebSocket handshake: Unexpected response code: 200
Which for the life of me I cannot figure out why.
I've set up a very similar server with the exact same code with the exact same servers & settings. The only difference is one server has a .com TLD while the other has a .sg TLD.
I've reduced it down to the simplest form, which is the below and the error is still happening. It's on the api side for sure and not the frontend as the frontend can connect to the .com TLD.
Below is all the code that I believe is related to the problem. If you think there might be other areas please ask and I will post other areas. It's hosted on AWS Elastic Beanstalk. I've also set the SSL cert to domain.com & *.domain.com
Does anybody know why this might be happening?
The bizarre thing to me is I literally set up a server with these exact settings and it's working perfectly fine.
server.js (start point in package.json)
'use strict';
(async function() {
// Server Setup
const WSServer = require('ws').Server;
const app = await require('./app.js');
const server = require('http').createServer(app);
const port = 3075;
// Create web socket server on top of a regular http server
const wss = new WSServer({
server: server
});
// Also mount the app here
// server.on('request', app);
let sendMessage = {
"connected":"connected to web socket",
}
wss.on('connection', function connection(ws) {
ws.send(JSON.stringify(sendMessage));
ws.on('message', async function incoming(message) {
let interval = setInterval(async () => {
console.log("ping");
ws.send(message);
}, 500);
});
ws.on('close', function close() {
console.log('/socket connection Closed');
});
});
server.listen(process.env.PORT || port, function() {
console.log(`AppName https/wss is listening on port ${process.env.PORT || port}`);
});
})();
app.js (removed much of the code that is irrelevant to this question)
module.exports = (async function() {
const {Config,Environments} = await require("./common/config");
const packageJson = require('./package.json');
// Handler
const AuthFunc = await require("./funcs/user/auth");
const BillingFunc = await require("./funcs/billing/billing");
const ObjectUtil = require("./utils/object");
const AccountStatusEnum = require("./enums/account-status").accountStatusEnum;
const sleep = require('util').promisify(setTimeout);
const {t} = require('./translations/i18n').i18n;
const {availableLanguages} = require('./translations/all');
// Simulate real API calls
const delayResponse = 250;// milliseconds
// ==============================================
// Base setup
// ==============================================
process.env.TZ = "Etc/GMT"
const express = require('express');
const app = express();
var expressWs = require('express-ws')(app);
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(require('express-useragent').express());
// Cors
app.use(async function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, auth-id, auth-token, x-csrf-token, _csrf");
res.header('Access-Control-Allow-Methods', 'PATCH, POST, GET, DELETE, OPTIONS');
next();
});
// ==============================================
// Auth
// ==============================================
const normRoute = (req, res, next) => {
req.body = (req.body != undefined && req.body != null) ? ObjectUtil.toCamelCaseKeys(req.body) : req.body;
response(req,res,next(req));
}
// Does not need to be logged in but passes user info if logged in
const passRoute = async (req, res, next) => {
authRoute(req, res, next, AccountStatusEnum.any, true);
}
const authRoute = async (req, res, next, minimumStatus, passRoute) => {
req.body = (req.body != undefined && req.body != null) ? ObjectUtil.toCamelCaseKeys(req.body) : req.body;
let authId = (req.headers['auth-id'] !== undefined) ? req.headers['auth-id'] :"";
let authToken = (req.headers['auth-token'] !== undefined) ? req.headers['auth-token'] : "";
let r = await AuthFunc.authUser(
req,
req.ip,
req.useragent,
authId,
authToken,
minimumStatus,
);
if(r.err.code){
if(r.err.authError && passRoute){
response(req,res,next(req,null));
return false;
}else{
response(req,res,r);
return false;
}
}
let user = r.res.user;
r = await BillingFunc.updateSubStatus(req,user);
if(r.err.code){ response(req,res,r); return false; }
if(r.res.userStatus !== undefined){
user.status = r.res.userStatus;
}
response(req,res,next(req,user));
}
// ===============================================================
// Routes
// ===============================================================
app.get('/', function(req, res) {
res.send(
'<html>'+
'<head></head>'+
'<body>'+
'API is running <br>'+
'App: '+Config.FrontEnd.AppName+'<br>'+
'Env: '+Config.Env+'<br>'+
'Version: '+packageJson.version+'<br>'+
'</body>'+
'</html>'
);
});
// ==============================================
// Response type
// ==============================================
const response = async (req,res,obj) => {
await obj;
Promise.resolve(obj).then(function(val) {
if(delayResponse >= 1 && (Config.Env === Environments.Local)){
setTimeout(function(){
resume(req,res,val);
}, delayResponse);
return true;
}
resume(req,res,val);
});
}
const resume = (req,res,obj) => {
obj = (obj === undefined) ? {} : obj;
var status = (obj.status !== undefined) ? obj.status : 200;
// Let status override settings
if(obj.status === undefined){
if((obj.err.code)){
status = 400;
}else if(obj.res === undefined || ObjectUtil.isEmpty(obj.res)){
// status = 204;
}
}
let json = {};
json.err = obj.err;
json.res = obj.res;
json = ObjectUtil.toCamelCaseKeys(json);
res.status(status).json(json);
}
// ==============================================
// Return the app
// ==============================================
return app;
})();
package.json
{
"name": "name-api",
"version": "1.17.0",
"description": "name-api",
"main": "server.js",
"dependencies": {
"axios": "^0.21.1",
"cookie-parser": "^1.4.4",
"express": "^4.17.1",
"express-session": "^1.17.1",
"express-useragent": "^1.0.13",
"express-ws": "^4.0.0",
"googleapis": "^50.0.0",
"mocha": "^8.0.1",
"mysql": "github:mysqljs/mysql",
"nodemailer": "^6.4.6",
"query-string": "^6.12.1",
"stripe": "^8.60.0",
"ws": "^7.4.3"
},
"devDependencies": {},
"scripts": {
"test": "mocha",
"start": "node server.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/..."
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/..."
},
"homepage": "https://github.com/..."
}

Problem solved.
It was a load balancer issue. Apparently this doesn't work well with Classic Load Balancer. I believe it's due to the way it's requested.
Changing over to a Application Load balancer fixed the issue for me.

Related

jest test is not able to see my express server and return app is undefined

I am trying to test different endpoints with a express server.
here is my app.js
const express = require('express');
const app = express();
const cors = require('cors');
const port = 5000;
const dogFunctions = require('./Models/Dog')
const { Pool, Client } = require('pg')
app.use(cors());
app.use(express.json());
const dogRoutes = require('./routes/dogs');
app.use('/dogs', dogRoutes);
app.get('/', (req, res) => res.send('Hello, world!'))
module.exports = app;
this is my config file for testing
const { Pool } = require('pg');
const fs = require('fs');
const request = require('supertest');
const apiServer = require('../../app');
const reset = fs.readFileSync(__dirname + '/reset.sql').toString();
// enable resetting of db between tests
const resetTestDB = () => {
return new Promise (async (res, rej) => {
try {
const db = new Pool();
await db.query(reset)
res('Test DB reset')
} catch (err) {
rej('Could not reset TestDB')
}
})
}
// make these things available to test suites
global.request = request
global.app = apiServer
global.resetTestDB = resetTestDB
here is one of the test files of mine:
describe('dogs endpoints', () => {
let api;
beforeEach(async () => {
await resetTestDB()
})
beforeAll(async () => {
api = app.listen(5000, () => console.log('Test server running on port 5000'))
});
})
here is what my package.json looks like
"scripts": {
"test": "jest --watchAll",
},
"jest": {
"testEnvironment": "node",
"coveragePathIgnorePatterns": [
"/node_modules/"
]
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.2",
"pg": "^8.8.0"
},
"devDependencies": {
"jest": "^29.3.0",
"supertest": "^6.3.1"
}
}
What have i done wrong here?
I think its something simple.. but cant figure it out.
I am using global of jest to attach my express to be able available as app in my test files as a global variable.
I have also set my test env to be node to be able to test for node applications.
Is there anything i missed?

503 Service Unavailable after deploy NextJS app

so, after trying a few times, it is clear that I am doing something wrong but I do not realize what. My steps are:
rm -rf .next/ && npm run build
upload .next, package.json,next.config.js and server.js to cPanel file manager
create node JS app from cPanel with 14.18.3 version. (on my local node -v show me 14.18.1, but on cPanel I don't have this version)
run npm install with success
then i'm geting 503 service unavailable.
server.js
// server.js
const { createServer } = require('http');
const { parse } = require('url');
const next = require('next');
const dev = process.env.NODE_ENV !== 'production';
const hostname = 'localhost';
const port = process.env.PORT || 3000;
// when using middleware `hostname` and `port` must be provided below
const app = next({ dev, hostname, port });
const handle = app.getRequestHandler();
app.prepare().then(() => {
createServer(async (req, res) => {
try {
// Be sure to pass `true` as the second argument to `url.parse`.
// This tells it to parse the query portion of the URL.
const parsedUrl = parse(req.url, true);
const { pathname, query } = parsedUrl;
if (pathname === '/a') {
await app.render(req, res, '/a', query);
} else if (pathname === '/b') {
await app.render(req, res, '/b', query);
} else {
await handle(req, res, parsedUrl);
}
} catch (err) {
console.error('Error occurred handling', req.url, err);
res.statusCode = 500;
res.end('internal server error');
}
}).listen(port, (err) => {
if (err) throw err;
console.log(`> Ready on http://${hostname}:${port}`);
});
});
package.json scripts
"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "NODE_ENV=production node server.js"
},
next.config.json
/** #type {import('next').NextConfig} */
const nextConfig = {
basePath: '/apps/nextjs-cpanel',
trailingSlash: true,
reactStrictMode: true,
sassOptions: {
additionalData: `#import "./styles/variables.scss"; #import "./styles/mixins.scss";`,
}
};
module.exports = nextConfig;
on cPanel application root is apps/nextjs-cpanel, application url is my-domain/apps/nextjs-cpanel and application startup file server.js

MongooseError: Operation `urls.find()` buffering timed out after 10000ms

I am trying to make a post request that saves find data in MongoDB or create one if it does not exist then I got the error like this
MongooseError: Operation `urls.find()` buffering timed out after 10000ms
my main.js
require("dotenv").config();
const express = require("express");
const cors = require("cors");
const app = express();
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
// Basic Configuration
const port = process.env.PORT || 3000;
app.use(cors());
app.use("/public", express.static(`${process.cwd()}/public`));
app.get("/", function (req, res) {
res.sendFile(process.cwd() + "/views/index.html");
});
// Your first API endpoint
app.get("/api/hello", function (req, res) {
res.json({ greeting: "hello API" });
});
app.listen(port, function () {
console.log(`Listening on port ${port}`);
});
mongoose.connect(
process.env.MONGODB_CONNECT,
{
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
},
() => {
console.log("Connected to mongoDB");
}
);
let urlSchema = new mongoose.Schema({
original: { type: String, required: true },
short: Number,
});
let Url = mongoose.model("Url", urlSchema);
let responseObject = {};
app.post(
"/api/shorturl/new",
bodyParser.urlencoded({ extended: false }),
(request, response) => {
let inputUrl = request.body["url"];
responseObject["original_url"] = inputUrl;
let inputShort = 1;
Url.findOne({})
.sort({ short: "desc" })
.exec((error, result) => {
// THE ERROR APPEARS
console.log(error);
if (!error && result != undefined) {
inputShort = result.short + 1;
}
if (!error) {
Url.findOneAndUpdate(
{ original: inputUrl },
{ original: inputUrl, short: inputShort },
{ new: true, upsert: true },
(error, savedUrl) => {
if (!error) {
responseObject["short_url"] = savedUrl.short;
response.json(responseObject);
}
}
);
}
});
}
);
my dependencies
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"mongoose": "^5.11.10"
I already try some solutions in similar question like:
delete node_module file and reinstalled mongoose
change the version of nodejs in MongoDB cluster to
2.2.12 or later
change my IP to google public DNS 8888 8844
any help would be much appreciated
I did try to set up a new environment and using the same MongoDB database, it turns out my database get problem may be the connection,
I tried to create a new project and setup access from everywhere and chose the server near to my place, it works fine now.
The problem
As stated in this answer the problem is very likely to be that YOU DO NOT HAVE your localhost database running.
How to fix
Start both mongod and mongo processes.

Can't figure out why Heroku website is outputting `cannot GET /` message

so, it just says 'cannot GET /' everytime i try to view my app
here is the code
server.js
//Import requiered packages
const express = require('express');
const {Client} = require('pg');
//Create the conection to the postgres server
const client = new Client({
connectionString: process.env.DATABASE_URL
});
client.connect();
//Create the express app
const bodyParser = require('body-parser');
const app = express();
// parse application/json
app.use(bodyParser.json());
//Handle a post request at /query
app.post('/query', (req, res) => {
res.setHeader('Content-Type', 'application/json');
console.log("Receiving request");
if(req.body.query) {
console.log(req.body.query);
client.query(req.body.query, (err, r) => {
if (err) throw err;
rows = [];
for(let row of r.rows){
rows.push(row);
}
response = JSON.stringify(rows);
console.log(response);
res.end(response);
});
}
});
const port = process.env.PORT || 8080
//Start listening
const server = app.listen(port, function () {
console.log("App listening at ${host}")
});
package.json
{
"name": "haha",
"description": "joseph = obesity > lawa",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "4.x.x",
"pg": "7.x.x",
"body-parser": "1.18.x"
}
}
I literally have no clue what's wrong and theres almost no erroirs in the CLI.
The errors in the CLI are basically irrelevant to this, as even if tehre are no errors im still presented with the 'cannot GET /' message
please help me :(

Node/Express flash message via res.locals on Heroku

I'm using making error messages available on res.locals and displaying them as needed with a redirect. I am not completely sure how this works, but it does. At least when I'm running the server locally.
Deployed to Heroku on the other hand, it doesn't. There's nothing. I make mistakes on the page and my sexy flash messages are glimmering with their absence.
I'm using sqlite3 locally and storing sessions with connect-session-sequelize.
I'm using postgres on Heroku.
server.js
/* jshint node: true */
'use strict';
var express = require('express');
var session = require('express-session');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var _ = require('underscore');
var db = require('./db.js');
var SequelizeStore = require('connect-session-sequelize')(session.Store);
var flash = require('express-flash');
var app = express();
var PORT = process.env.PORT || 3000;
app.use(cookieParser());
// Track logins with express session
app.use(session({
secret: 'Magic mike',
resave: true,
saveUninitialized: false,
store: new SequelizeStore({
db: db.sequelize
})
}));
app.use(flash());
// Make userId available in templates
app.use(function(req, res, next){
res.locals.currentUser = req.session.userId;
res.locals.sessionFlash = req.session.sessionFlash;
delete req.session.sessionFlash;
next();
});
// To parse incoming req
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
// Serve static files
app.use(express.static(__dirname + '/public'));
// View engine setup
app.set('view_engine', 'pug');
app.set('views', __dirname + '/views');
var routes = require('./routes/index.js');
app.use('/', routes);
....
db.js
var Sequelize = require('sequelize');
var env = process.env.NODE_ENV || 'development';
var sequelize;
if (env === 'production') {
sequelize = new Sequelize(process.env.DATABASE_URL, {
dialect: 'postgres'
});
} else {
sequelize = new Sequelize(undefined, undefined, undefined, {
'dialect': 'sqlite',
'storage': __dirname + '/data/shoplist.sqlite'
});
}
var db = {};
db.item = sequelize.import(__dirname + '/models/item.js');
db.user = sequelize.import(__dirname + '/models/user.js');
// db.shoplist = sequelize.import(__dirname + '/models/shoplist.js');
db.sequelize = sequelize;
db.Sequelize = Sequelize;
// db.item.belongsTo(db.shoplist);
// db.shoplist.hasMany(db.item);
db.item.belongsTo(db.user);
db.user.hasMany(db.item);
// db.user.belongsTo(db.shoplist);
// db.shoplist.hasMany(db.user);
module.exports = db;
....
routes/index.js
var express = require('express');
var router = express.Router();
var _ = require('underscore');
var mid = require('../middleware/');
var db = require('../db.js');
var flash = require('express-flash');
router.all('/login-error', function(req, res) {
res.redirect(301, '/login');
});
router.all('/register-error', function(req, res) {
res.redirect(301, '/register');
});
// GET /homepage
router.get('/', function(req, res, next) {
req.flash('info', 'Velkommen!');
return res.render('index.pug', {
title: 'Welcome!',
url: req.originalUrl
});
});
....
// POST /login
router.post('/login', function(req, res, next) {
var body = _.pick(req.body, 'name', 'password');
if (req.body.name && req.body.password) {
db.user.authenticate(body).then(function(user) {
req.session.userId = user.id;
// To make sure the session is stored in db befor redirect
req.session.save(function() {
return res.redirect('makeitem');
});
}).catch(function(e) {
var err = new Error("Wrong username or password.");
err.status = 400;
err.message = "Wrong username or password.";
req.session.sessionFlash = {
type: 'error',
message: err.message
};
res.redirect('/login-error');
});
} else {
var err = new Error('All fields required.');
err.status = 400;
err.message = 'All fields required.';
req.session.sessionFlash = {
type: 'error',
message: err.message
};
res.redirect('/login-error');
}
});
...
// POST /register
router.post('/register', function(req, res, next) {
var body = _.pick(req.body, 'name', 'email', 'password');
// Check if all fields are filled out
if (req.body.name &&
req.body.email &&
req.body.password) {
// // Check that passwords are the same
if (req.body.password !== req.body.comf_password) {
var err = new Error('Passwords do not match.');
err.status = 400;
err.message = 'Passwords do not match.';
req.session.sessionFlash = {
type: 'error',
message: err.message
};
res.redirect('/register-error');
} else {
db.user.create(body).then(function(user) {
//res.json(user.toPublicJSON());
res.redirect('/login');
}, function(e) {
var err = new Error('Email or Shopping List Name in use.');
err.status = 400;
err.message = 'There is already a Shopping List with this name or email';
req.session.sessionFlash = {
type: 'error-name',
message: err.message
};
res.redirect('/register-error');
});
}
} else {
var err = new Error('All fields required.');
err.status = 400;
err.message = 'All fields required.';
req.session.sessionFlash = {
type: 'error',
message: err.message
};
res.redirect('/register-error');
}
});
...
I'm using pug, so this is the way I display messages:
if sessionFlash && sessionFlash.message
.sessionflash
p.bg-warning #{ sessionFlash.message }
These are my dependencies:
"dependencies": {
"bcrypt": "^0.8.7",
"body-parser": "^1.15.2",
"connect-session-sequelize": "^3.1.0",
"cookie-parser": "^1.4.3",
"crypto-js": "^3.1.6",
"express": "^4.14.0",
"express-flash": "0.0.2",
"express-session": "^1.14.1",
"jsonwebtoken": "^7.1.9",
"pg": "^4.5.6",
"pg-hstore": "^2.3.2",
"pug": "^2.0.0-beta6",
"sequelize": "^3.5.1",
"sqlite3": "^3.1.4",
"underscore": "^1.8.3"
Why is this happening? And how do I display flash messages on Heroku?
Facing the same issue, I realized that it had to do with Cloudflare settings. Forwarding permissions were causing an issue that erased local variables any time I loaded a page. When I simply used the app deploy link with heroku and I stopped having an issue. If you're reading this in the future, use your command line to test the website in production with "NODE_ENV=production npm start" and web with "heroku local web", if both of those are working fine, the problem may be your dns manager.

Resources