Home > Enterprise >  express JS - connect-flash doesnt work fine when i use express-mysql-session
express JS - connect-flash doesnt work fine when i use express-mysql-session

Time:11-24

i have a strange problem. i detected where the problem is, but i dont know how to solve it. i use cookie-parser, express-mysql-session, express-session, connect-flash, passport and etc in my project. to store sessions in mySQL database, i use express-mysql-session module. but this module makes a problem for connect-flash. this is where i config first middlewares:

const setFirstMiddleWares = (server: Express) => {
  if (devMode) server.use(morgan("dev"));
  
  server.use(bodyParser.urlencoded({ extended: false }));
  server.use(cookieParser());
  server.use(
    expressSession({
      secret: "secret",
      saveUninitialized: false,
      resave: false,
      store: MySQLSessionStore, //the problem is exactly this line
    })
  );
  server.use(flash());
  server.use(passport.initialize());
  server.use(passport.session());
  server.use(setRenderConfig);
};

the problem that express-mysql-session make for connect-flash is that when i set a message with connect-flash in middlewares and then i redirect user to for example login page, at the first time no message will be displayed in view. but when i refresh the login page, the message will be displayed! but when i remove the store property of express-session config object, everything will be fine and the flash message will be displayed in login view immediately after user redirected to the login page and the login page doesnt need to be refreshed to display the error or success message! it is really strange behavior for flash-connect. you can see the important parts of my codes: DB.ts:

mport mysql from "mysql2/promise";
import MySQLStoreSessionsStore from "express-mysql-session";
import * as expressSession from "express-session";

const dbConfig = {
  host: process.env.DBHOST,
  user: process.env.DBUSER,
  password: process.env.DBPASSWORD,
  port: Number(process.env.DBPORT),
  database: process.env.DATABASENAME,
};

const connection = mysql.createPool(dbConfig);
const MySQLSessionStoreClass = MySQLStoreSessionsStore(expressSession);
const MySQLSessionStore = new MySQLSessionStoreClass({}, connection);

export { connection as mysql, MySQLSessionStore };

Users.ts:

import { NextFunction, Request, Response } from "express";
import passport from "passport";
import { signUpUser } from "../models/Users";
const signUpUserController = async (
  req: Request,
  res: Response,
  next: NextFunction
) => {
  try {
    await signUpUser(req.body);
    req.flash("sign-in-successes", [
      "your account has been created successfully! please login to your account.",
    ]);
    res.redirect("/login");
  } catch (errors) {
    if (Array.isArray(errors)) {
      req.flash("sign-up-errors", <string[]>errors);
      res.redirect("/sign-up");
    } else next({ error: errors, code: "500" });
  }
};

const signInUserController = (
  req: Request,
  res: Response,
  next: NextFunction
) => {
  passport.authenticate("local", {
    failureRedirect: "/login",
    failureFlash: true,
  })(req, res, next);
};

const remmemberUserController = (req: Request, res: Response) => {
  console.log("line 5:", req.body);
  if (req.body["remmember-user"]) {
    req.session.cookie.originalMaxAge = 253402300000000;
  } else {
    req.session.cookie.expires = undefined;
  }

  res.redirect("/account/dashboard");
};

const logOutController = (req: Request, res: Response, next: NextFunction) => {
  req.logOut({ keepSessionInfo: false }, (err) => {
    console.log(err);

    req.flash("sign-in-successes", ["logged out successfuly!"]);
    res.redirect("/login");
  });
};

export {
  signUpUserController,
  signInUserController,
  logOutController,
  remmemberUserController,
};

passport.ts:

import passport from "passport";
import localStrategy from "passport-local";
import bcrypt from "bcrypt";
import { getUsersInfos } from "../models/Users";

passport.use(
  new localStrategy.Strategy(
    {
      usernameField: "user-email-signin",
      passwordField: "user-password-signin",
    },
    async (email, password, done) => {
      try {
        const user: any = await getUsersInfos(email);

        if (Array.isArray(user)) {
          const length: 2 | number = user.length;
          if (length === 0) {
            return done(null, false, {
              message: "user with your entered email not found!",
            });
          }

          const isMatch = await bcrypt.compare(password, user[0].password);

          if (isMatch) {
            return done(null, user);
          } else {
            return done(null, false, {
              message: "email or password are incorrect!",
            });
          }
        } else {
          return done(null, false, {
            message: "something went wrong. please try again!",
          });
        }
      } catch (error) {
        console.log(error);
      }
    }
  )
);

passport.serializeUser((user, done) => {
  done(null, user);
});

passport.deserializeUser(async (user, done) => {
  if (Array.isArray(user)) {
    done(null, user[0].id);
  }
});

LoginController.ts:

import { Request } from "express";
import { NewExpressResponse } from "../types/Types";

const loginController = (req: Request, res: NewExpressResponse) => {
  res.render("login", {
    title: `login`,
    scripts: [...res.locals.scripts, "/js/LoginScripts.js"],
    successes: req.flash("sign-in-successes"),
    error: req.flash("error"),
  });
};

export { loginController };

i searched a lot for this problem in google, but the only thing that i found is that i should use req.session.save(() => { res.redirect("/login");}) in my middlewares to display the flash message in the view immediately after user redirected to the route. but there are some problems with this way:

  1. i should use this code for every redirection if i want to set and use the flash message at the redirect route and i actually dont like this.
  2. i cant do something like this in signInUserController because passport set the errors in flash by itself.

so do you have any idea to fix this strange behavior of connect-flash when im using express-mysql-session? thanks for help :)

CodePudding user response:

I found that this is a problem(actually not a problem, a feature that can be added) in connect-flash. It doesnt save the session by itself. Because of it, i created the async version of this package with more features. You can use promise base or callback base functions to save and get your flash messages. So you can use async-connect-flash instead of using connect-flash. I hope you like it :)

  • Related