1

I am working on an application that needs to listen to a WebSocket and make changes to a remote SQL Server database for each user. Each user has their own WebSocket and database. The application previously worked locally on a computer and only needed to connect to one database and one WebSocket.

I have added authentication, but I have no idea how to connect to the WebSocket and database for each user when the application starts. Additionally, the WebSocket and database connection must remain active even when the user is not logged in (the frontend only serves to input data for connecting to the user's WebSocket and database). Do I need to create an instance for each user's WebSocket?

Below is the code that worked locally for one user - app.js runs dbConn.js, which connects to the database and runs wsConn.js, which connects to the WebSocket. I'm quite confused about how this should work for multiple users and I would really appreciate some guidance on how to solve this problem.

// App.js
const path = require("path");

const dbConnect = require("./dbConn");

const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const session = require("express-session");
const MongoDBStore = require("connect-mongodb-session")(session);
const csrf = require("csurf");
const flash = require("connect-flash");
const User = require("./models/user");
const app = express();

const MONGODB_URI = "mongodb://127.0.0.1/main_db";

const store = new MongoDBStore({
  uri: MONGODB_URI,
  collection: "sessions",
});

const adminRoutes = require("./routes/admin");
const authRoutes = require("./routes/auth");

const csrfProtection = csrf();

app.set("view engine", "ejs");
app.set("views", "views");

app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, "public")));
app.use(
  session({
    secret: "secret_long_string",
    resave: false,
    saveUninitialized: false,
    store: store,
  })
);

app.use(csrfProtection);
app.use(flash());

app.use((req, res, next) => {
  if (!req.session.user) {
    return next();
  }
  User.findById(req.session.user._id)
    .then((user) => {
      req.user = user;
      next();
    })
    .catch((err) => console.log(err));
});

app.use((req, res, next) => {
  res.locals.csrfToken = req.csrfToken();
  res.locals.isAuthenticated = req.session.isLoggedIn;
  next();
});

app.use(adminRoutes);
app.use(authRoutes);
app.use("/", (req, res, next) => {
  res.redirect("/home");
});

mongoose
  .connect(MONGODB_URI)
  .then((result) => {
    app.listen(3000);
  })
  .catch((err) => {
    console.log(err);
  });

try {
  dbConnect.dbConnect();
} catch (e) {
  console.log(e);
}
// dbConn.js

const sql = require("mssql");
const nconf = require("nconf");
const wsConn = require("./wsConn");
nconf.use("file", { file: "config.json" });
nconf.load();

module.exports = {
  dbConnect: function () {
    // config for your database
    const config = {
      user: nconf.get("db:user"),
      password: nconf.get("db:password"),
      server: nconf.get("db:server"),
      database: nconf.get("db:database"),
      trustServerCertificate: true,
    };

    sql.connect(config, function (err) {
      if (err) {
        console.log(err);
      } else {
        console.log("db connected");

        try {
          wsConn.connect();
        } catch (e) {
          console.log(e);
        }
      }
    });
  },
};
// wsConn.js

const WebSocket = require("ws");
const fs = require("fs");
const nconf = require("nconf");
const custom = require("./util/custom");

module.exports = {
  connect: function () {
    let readData = fs.readFileSync("./config.json"),
      myObj;

    nconf.use("file", { file: "./config.json" });
    nconf.load();
    const ws = new WebSocket(nconf.get("wssUrl"), {
      reconnection: true,
      reconnectionDelay: 500,
      reconnectionAttempts: 10,
      headers: {
        "tenant-id": nconf.get("tenantId"),
        "tenant-secret": nconf.get("tenantSecret"),
      },
    });

    ws.on("open", function open() {
      console.log("WebSocket connection established", new Date().toISOString());
      nconf.set("wss:isConnected", true);
    });

    ws.on("message", function message(data) {
      console.log(
        "received: %s",
        data,
        typeof data,
        JSON.parse(data),
        new Date().toISOString()
      );
      const parsedData = JSON.parse(data);
      if (parsedData.action === "NewMessage") {
        custom.syncMessages(parsedData.messageId);
      } else if (parsedData.action === "SyncHouses") {
        custom.syncHouses();
      } else if (parsedData.action === "SyncMap") {
        custom.syncMap();
      } 
    });

    ws.on("error", function message(data) {
      console.log("WebSocket error:", data.message, new Date().toISOString());
      nconf.set("wss:isConnected", false);
    });

    ws.on("close", function message(data) {
      nconf.set("wss:isConnected", false);
      console.log(
        "WebSocket connection closed",
        data,
        new Date().toISOString()
      );
      setTimeout(() => {
        console.log("WebSocket reconnect");
        module.exports.connect();
      }, 200);
    });

    ws.on("pong", function () {
      // As required by the spec, when the ping is received, the recipient must send back a pong.
      //console.log("connection is alive");
    });

    ws.on("disconnect", function () {
      console.log("disconnected");
      nconf.set("wss:isConnected", false);
    });

    const ping = function () {
      ws.ping("");
      console.log("ping");
    };

    setInterval(ping, 30000);
  },
};
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
freya
  • 11
  • 2
  • 1
    You definitely don't need a separate instance for each user. If you haven't figured out database [connection pooling](https://www.npmjs.com/package/mssql#connection-pools) it is worth your trouble to do so. Long-lived persistent connections to a database don't make sense but connection pools do. – O. Jones Apr 07 '23 at 09:28
  • A single nodejs instance can handle hundreds of concurrent websocket connections. A [listener](https://www.npmjs.com/package/ws#simple-server) to handle incoming connection events from clients is your starting point. A more complete description of your websocket approach will help us give you better answers. – O. Jones Apr 07 '23 at 09:34
  • Thanks, I accidentally added dbConn twice, I edited post now and added wsConn.js – freya Apr 07 '23 at 09:45

0 Answers0