Express.js server used as a Users API to Connect to a MongoDB, runs locally but not when deployed on Cyclic or Vercel

37 Views Asked by At

I am using ThunderClient in VS Code to test a POST request to register a new user to the database. I ran the server locally (http://localhost:8080/api/user/register) with success, passing the following JSON info:

{ "FIRST_NAME": "John", "LAST_NAME": "Doe", "USER_NAME": "rmon", "USER_PASS": "password123?", "USER_PASS2": "password123?", "EMAIL_ADDRESS": "[email protected]", "DATE_CREATED": "2022-01-01", "IS_ACTIVE": true, "TERMS": true }

When I deploy the same server to Cyclic and test with ThunderClient at the Cyclic link (ex. https://link.cyclic.app/api/user/register) I receive this response:

write EPROTO 22793600:error:100000f7:SSL routines:OPENSSL_internal:WRONG_VERSION_NUMBER:....\third_party\boringssl\src\ssl\tls_record.cc:231:

I read that there could be an issue with SSL certificate. I don't really understand what the problem is because I am new to web development, but I was told to change the URL to HTTP instead of HTTPS.

When I do that, and try the same POST requestagain, I receive this response:

Connection was refused by the server.

In my project I have a .env containing JWT_SECRET and MONGO_URL to my db, which I dont think I need to share to get help.

The other two files are server.js and user-service.js:

server.js:

// Import express and create an express object:
const express = require('express');
const app = express();
// Import CORS middleware to allow requests from different origins.
// This is particularly important for web applications that might 
// be served from a different domain than the API server.
const cors = require('cors');
// Import 'dotenv' module to load variables from a '.env' file
const dotenv = require('dotenv');
dotenv.config()
// Import user-service2 module
const userService = require("./user-service.js")

const jwt = require('jsonwebtoken');
const passport = require('passport');
const passportJWT = require('passport-jwt');


const HTTP_PORT = process.env.PORT || 8080;

// JSON Web Token Setup
let ExtractJwt = passportJWT.ExtractJwt;
let JwtStrategy = passportJWT.Strategy;

// Configure its options
let jwtOptions = {
  jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('jwt'),
  secretOrKey: process.env.JWT_SECRET,
};

let strategy = new JwtStrategy(jwtOptions, function (jwt_payload, next) {
  console.log('payload received', jwt_payload);

  if (jwt_payload) {
 
    next(null, {
      _id: jwt_payload._id,
      FIRST_NAME: jwt_payload.FITST_NAME,
      LAST_NAME: jwt_payload.LAST_NAME,
      USER_NAME: jwt_payload.USER_NAME,
      USER_PASS: jwt_payload.USER_PASS,
      USER_PASS2: jwt_payload.USER_PASS2,
      EMAIL_ADDRESS: jwt_payload.EMAIL_ADDRESS,
      DATE_CREATED: jwt_payload.DATE_CREATED,
      IS_ACTIVE: jwt_payload.IS_ACTIVE,
      TERMS: jwt_payload.TERMS,
    });
  } else {
    next(null, false);
  }
});


// Responsible for parsing incoming requests with JSON payloads, globally.
app.use(express.json());
// Responsible for allowing requests from different origins, globally.
app.use(cors());

passport.use(strategy);
app.use(passport.initialize());

app.post("/api/user/register", (req,res) => {
    userService.registerUser(req.body)
    .then((msg) => {
        res.json({ "message": msg });
    }).catch((msg) => {
        res.status(422).json({ "message": msg });
    });
});

app.post("/api/user/login", (req, res) => {
    userService.checkUser(req.body)
    .then((user) => {
        res.json({ "message": "login successful"});
    }).catch(msg => {
        res.status(422).json({ "message": msg });
    });
});

userService.connect()
.then(() => {
    app.listen(HTTP_PORT, () => {
        console.log("API listening on: " + HTTP_PORT)
    });
})
.catch((err) => {
    console.log(`unable to start the server: ${err}`);
    process.exit();
})

user-service.js:

const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

let mongoDBConnectionString = process.env.MONGO_URL;

let Schema = mongoose.Schema;

let userSchema = new Schema({
    FIRST_NAME: String,
    LAST_NAME: String,
    USER_NAME: {
        type: String,
        unique: true
    },
    USER_PASS: String,
    USER_PASS2: String,
    EMAIL_ADDRESS: String,
    DATE_CREATED: String,
    IS_ACTIVE: Boolean,
    TERMS: Boolean
});

let User;

module.exports.connect = () =>{
    return new Promise(function (resolve, reject){
        let db = mongoose.createConnection(mongoDBConnectionString);
        console.log("Connected to Chronicle DB")
        db.on('error', err => {
            reject(err);
        })

        db.once('open', () => {
            User = db.model("users", userSchema);
            resolve();
        });
    });
};

module.exports.registerUser = (userData) => {
    console.log("Registering user (backend)")
    return new Promise(function (resolve, reject) {
        const specialCharRegex = /[!@#$%^&*?]/;
        if (userData.USER_PASS != userData.USER_PASS2) {
            reject("Passwords do not match");
        } else if (userData.USER_PASS.length < 8 || userData.USER_PASS.length > 16) {
            reject("Password must be between 8 and 16 characters");
        } else if (!specialCharRegex.test(userData.USER_PASS)) {
            reject("Password must contain at least one special character (!@#$%^&*?)");
        } else {
            // Check if email already exists
            User.findOne({ EMAIL_ADDRESS: userData.EMAIL_ADDRESS })
                .then(user => {
                    if (user) {
                        reject("Email already exists");
                    } else {

                        // Hash password for encryption; salted 10 times
                        bcrypt.hash(userData.USER_PASS, 10).then(hash => {

                        userData.USER_PASS = hash;

                        let newUser = new User(userData);
                        newUser.save().then(() => {
                            resolve(`User ${userData.USER_NAME} successfully registered`);  
                            }).catch(err => {
                                if (err.code == 11000) {
                                    reject("User Name already taken");
                                } else {
                                    reject(`There was an error creating the user: ${err}`);
                                }
                            })
                        }).catch(err => reject(`Error: ${err}`));
                        
                    }
                })
                .catch(err => {
                    reject(`There was an error checking the email: ${err}`);
                });
        }
    });
};

module.exports.checkUser = function (userData) {
    return new Promise(function (resolve, reject) {

        User.findOne({ userName: userData.userName })
            .exec()
            .then(user => {
                bcrypt.compare(userData.password, user.password).then(res => {
                    if (res === true) {
                        resolve(user);
                    } else {
                        reject("Incorrect password for user " + userData.userName);
                    }
                });
            }).catch(err => {
                reject("Unable to find user " + userData.userName);
            });
    });
};

When I deploy on Cyclic, I checked the logs and it shows that the API is listening on port 3000, so I'm not receiving any errors running server.js file.

Anyone have any ideas or can point me in the right direction?

0

There are 0 best solutions below