Connect flash not displaying messages in Pug - express

I have an application with several routes, and I want to send flash messages of different kinds given the user interaction: either a success or failure message, or in some cases no message. Right now the messages are not displaying and I can't figure out how to get it to work. I'm using Node, Express and Pug.
I have a server.js file, routes.js file, message.pug file, and layout.pug file. Here are my files:
server.js
// init project
const express = require('express');
const app = express();
const bodyparser = require("body-parser");
const flash = require('connect-flash');
const passport = require("passport");
const session = require("express-session");
// http://expressjs.com/en/starter/static-files.html
app.use(express.static("public"));
app.set('view engine', 'pug');
// bodyparser middleware
app.use(bodyparser.urlencoded({ extended: false }));
app.use(bodyparser.json());
// express-session middleware
app.use(session({
secret: process.env.SECRET,
resave: true,
saveUninitialized: true
}));
// express-messages middleware
app.use(flash());
app.use((req, res, next) => {
res.locals.messages = require('express-messages')(req, res);
next();
});
app.use(express.json());
// import passport-config file
require("./passport-config")(passport);
// passport middleware
app.use(passport.initialize());
app.use(passport.session());
const routes = require('./routes.js');
routes(app);
// listen for requests :)
const listener = app.listen(process.env.PORT, () => {
console.log('Your app is listening on port ' + listener.address().port);
});
routes.js
app.get('/', (req, res) => {
req.flash("success", "your flash messages are working");
res.redirect("/admin");
});
app.get("/admin", (req, res) => {
res.render(process.cwd() + '/views/pug/admin');
});
message.pug
.messages
each type in Object.keys(messages)
each message in messages[type]
div(class="alert alert-" + type) #{ message }
// expected output
// div(class="alert alert-success") your flash messages are working
layout.pug
div.col-10.ml-sm-auto.px-4
!= messages('message', locals)

Since I had only two types of messages I was sending -- success or error -- I got rid of my messages.pug file and used the following code, which works.
server.js
// init project
const express = require('express');
const app = express();
const bodyparser = require("body-parser");
const flash = require('connect-flash');
const passport = require("passport");
const session = require("express-session");
// http://expressjs.com/en/starter/static-files.html
app.use(express.static("public"));
app.set('view engine', 'pug');
// bodyparser middleware
app.use(bodyparser.urlencoded({ extended: false }));
app.use(bodyparser.json());
// express-session middleware
app.use(session({
secret: process.env.SECRET,
resave: true,
saveUninitialized: true
}));
// express-messages middleware THIS IS WHAT I CHANGED
app.use(flash());
app.use((req, res, next) => {
res.locals.errors = req.flash("error");
res.locals.successes = req.flash("success");
next();
});
app.use(express.json());
// import passport-config file
require("./passport-config")(passport);
// passport middleware
app.use(passport.initialize());
app.use(passport.session());
const routes = require('./routes.js');
routes(app);
// listen for requests :)
const listener = app.listen(process.env.PORT, () => {
console.log('Your app is listening on port ' + listener.address().port);
});
routes.js (the same as before)
app.get('/', (req, res) => {
req.flash("success", "your flash messages are working");
res.redirect("/admin");
});
app.get("/admin", (req, res) => {
res.render(process.cwd() + '/views/pug/admin');
});
Then in layout, I use the following:
div.col-10.ml-sm-auto.px-4
if successes
for success in successes
div.alert.alert-success #{ success }
if errors
for error, i in errors
div.alert.alert-danger #{ error }
This works for displaying a message in a redirect.
Note: If you want to display a message directly (via res.render()), you have to pass it to the render method directly like this:
app.get('/admin', (req, res) => {
req.flash("success", "flash message for a render method");
res.render("/admin", { successes: req.flash("success") });
});

Related

authenticate tp express server tunneled through ngrok

I have an express server serving react pages. I am trying to show the work to a client via a ngrok tunnel. But whenever I try to log in at the ngrok URL for my server it fails to connect back to my localhost where the express routes for auth are.
I feel like it's a simple error I'm making. The browser is trying to make a request to the express server at localhost:5000, but there is nothing at localhost on my client's network.
This is my app.js
const createError = require('http-errors');
const express = require('express');
const debug = require('debug')('app');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
require('dotenv').config()
const ensureToken = require('./middleware/ensureToken');
const path = require('path');
const app = express();
const { init } = require('./utils/cron/createCron');
const cors = require('cors')
// Routers
const authRouter = require('./routes/auth')
const botRouter = require('./routes/bot')
const proxyRouter = require('./routes/proxy');
const winstonLogger = require('./utils/log/logger');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
const whiteList = ['http://localhost:3000/', 'http://localhost:3000']
const corsOptions = {
origin: (origin, callback)=>{
if (whiteList.indexOf(origin) !== -1){
callback(null, true)
}else{
callback(new Error('Not Allowed by CORS'))
}
optionsSuccessStatus:200
}
}
// app.use(cors(corsOptions))
app.use(cors({ credentials: true, origin: true }))
app.use(express.static(path.join(__dirname, 'build')));
// Base Routes
app.use('/api/auth', authRouter);
app.use(ensureToken) //Add this before routes that need to be protected ny valid token
app.use('/api/bot', botRouter);
app.use('/api/proxy', proxyRouter);
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "../client/build/index.html"));
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
app.listen(process.env.PORT, ()=>{
winstonLogger.info(`Listening on ${process.env.PORT}`)
debug(`Listening on ${process.env.PORT}`);
console.log(`Listening on ${process.env.PORT}`);
init()
})
module.exports = app;
Any help is appreciated!!

Redirect Express default route to a new route

I have FeathersJS app (which is on top of Express) and I am using handlebars as templating engine.
I want to redirect the '/' of express to other URL but is keeps on loading the index.html in /public folder. My goal is to redirect it to a handlebars pages so that I can use the layout and don't repeat myself.
const path = require('path');
const favicon = require('serve-favicon');
const compress = require('compression');
const cors = require('cors');
const helmet = require('helmet');
const logger = require('winston');
const feathers = require('#feathersjs/feathers');
const configuration = require('#feathersjs/configuration');
const express = require('#feathersjs/express');
const exphbs = require('express-handlebars');
const socketio = require('#feathersjs/socketio');
const middleware = require('./middleware');
const services = require('./services');
const appHooks = require('./app.hooks');
const channels = require('./channels');
const app = express(feathers());
app.set('views', path.join(app.get('views')));
app.engine('.hbs', exphbs({
extname: '.hbs',
defaultLayout: 'main'
}));
app.set('view engine', '.hbs');
// Load app configuration
app.configure(configuration());
// Enable CORS, security, compression, favicon and body parsing
app.use(cors());
app.use(helmet());
app.use(compress());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(favicon(path.join(app.get('public'), 'favicon.ico')));
app.use(express.static(app.get('public')));
// Host the public folder
app.get('/', function(req, res){
console.log('test');
res.redirect('/home');
});
app.get('/home', function(req, res, next){
res.render('home');
});
app.get('/test', function(req, res, next){
res.redirect('/');
});
app.get('/admin', function(req, res, next) {
res.render('admin',{});
});
app.get('/services', function(req, res, next) {
res.render('services',{});
});
// Set up Plugins and providers
app.configure(express.rest());
app.configure(socketio());
// Configure other middleware (see `middleware/index.js`)
app.configure(middleware);
// Set up our services (see `services/index.js`)
app.configure(services);
// Set up event channels (see channels.js)
app.configure(channels);
// Configure a middleware for 404s and the error handler
app.use(express.notFound());
app.use(express.errorHandler({ logger }));
app.hooks(appHooks);
module.exports = app;
It is not going to '/home' that I indicated. Am I missing something????
Thank you!
It's because you are using the express.static middleware before your / handler, since there is an index.html in the public folder, when you hit / it defaults to the index file, you can switch the order like this :
app.use(express.urlencoded({ extended: true }));
app.use(favicon(path.join(app.get('public'), 'favicon.ico')));
app.get('/', function(req, res){
console.log('test');
res.redirect('/home');
});
app.use(express.static(app.get('public')));
// Host the public folder
app.get('/home', function(req, res, next){
res.render('home');
});
app.get('/test', function(req, res, next){
res.redirect('/');
});

Express: unable to access route from browser due to accept:application/javascript header missing

I'm new to express. I have a Vue application running on express. I have some API routes that I'm able to access using axios through the browser. To access those routes using postman I have to have the header:
accept: application/javascript
for it to return the result of the actual API. If I don't use this header, I get the generated index.html from webpack. I need to reuse one of these routes to return excel/pdf, based on a parameter and have it accessible via a link on the page.
Here's my server.js - based on https://github.com/southerncross/vue-express-dev-boilerplate
import express from 'express'
import path from 'path'
import favicon from 'serve-favicon'
import logger from 'morgan'
import cookieParser from 'cookie-parser'
import bodyParser from 'body-parser'
import webpack from 'webpack'
const argon2 = require('argon2');
const passport = require('passport')
const LocalStrategy = require ('passport-local')
const session = require('express-session')
import history from 'connect-history-api-fallback'
// Formal(Prod) environment, the following two modules do not need to be introduced
import webpackDevMiddleware from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware'
import config from '../../build/webpack.dev.conf'
const app = express()
app.set('trust proxy', true)
app.set("view engine", "pug")
app.set('views', path.join(__dirname, 'views'))
app.use ('/', require('./routes'))
app.use(session({
secret: process.env.SESSION_SECRET || 'secretsauce',
resave: false,
saveUninitialized: true
}))
app.use(history())
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')))
app.use(logger('dev'))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: false
}))
app.use(cookieParser())
app.use(express.static(path.join(__dirname, 'public')))
const compiler = webpack(config)
app.use(webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath,
stats: {
colors: true
}
}))
app.use(webpackHotMiddleware(compiler))
////////// PASSPORT ///////////////////////
app.use (passport.initialize ());
app.use (passport.session ());
async function authenticateUser (username, password) {
//...
}
passport.use (
new LocalStrategy (async (username, password, done) => {
const user = await authenticateUser (username, password)
if (!user) {
return done (null, false, {
message: 'Username and password combination is wrong',
});
}
delete user.password;
return done (null, user)
})
);
// Serialize user in session
passport.serializeUser ((user, done) => {
done (null, user);
});
passport.deserializeUser (function(user, done) {
if(user === undefined || !user || Object.keys(user).length === 0)
return done(null, false)
else
done (null, user);
});
//////////// passport end ///////////////
app.set("view engine", "pug")
app.use(express.static(path.join(__dirname, 'views')))
app.get('/', function (req, res) {
res.sendFile('./views/index.html')
})
app.get('/success', function (req, res) {
res.render('./views/success')
})
app.use ('/api', require('./api'))
// catch 404 and forward to error handler
app.use(function (req, res, next) {
var err = new Error('Not Found')
err.status = 404
next(err)
})
app.use(function (err, req, res) {
res.status(err.status || 500)
res.send(err.message)
})
let server = app.listen(80)
export default app
And here's a bit of api.js
const {Router} = require ('express')
const router = Router()
router.get('/whome', function(req, res){
logger.info('whome', req.user)
return res.json(req.user)
})
router.get ('/hello', auth.isAuthenticated, async (req, res) => {
res.json ({text:'hello'})
})
module.exports = router
I can call http://localhost/api/hello from postman with the accept:application/javascript header and I get:
{
"text": "hello"
}
as expected. But if I call the same URL from the browser (and it's not sending that header), I get the created bundle index.html. How can I access these routes from the browser?
You have two options.
First one, try to add this in your server:
app.options('*', cors())
before to: app.set("view engine", "pug")
If that doesnt work, try to install this addon in your Google Chrome browser to test.
Allow-Control-Allow-Origin: *
And enable it. (The icon should be green instead of red).
Why this happens?
The request that's being made is called a preflight request.
Preflight requests are made by the browser, as CORS is a browser security restriction only - This is why it works in Postman, which is, of course, not a browser.
Reference: Preflight request

passportjs TypeError: User.authenticate is not a function

I'm trying to build a small nodejs, express, mongodb app with authentication using passport-local and passport-local-mongoose, however for some reason when entering the middleware for passport in the main app.js file I get this error "TypeError: User.authenticate is not a function"
I tried the authentication by itself in a test project and it worked just fine, so maybe it's something to do with the order of my code but I can't get to what's causing this issue. I've tried several refactors but to no avail.
Below is the code for the app.js and the User Schema , but Here's a link to the project files if needed.
app.js
const express = require('express'),
app = express(),
path = require('path'),
exphbs = require('express-handlebars'),
methodOverride = require('method-override'),
flash = require('connect-flash'),
session = require('express-session'),
bodyParser = require('body-parser'),
mongoose = require('mongoose'),
LocalStrategy = require("passport-local"),
passport = require("passport"),
mongodb = require("mongodb"),
User = require("./models/User");
// Load routes
const ideas = require('./routes/ideas');
const users = require('./routes/users');
// Connect to mongoose
mongoose.connect('mongodb://localhost/diaries');
mongoose.Promise = Promise;
// Handlebars Middleware
app.engine('handlebars', exphbs({
defaultLayout: 'main'
}));
app.set('view engine', 'handlebars');
// Body parser middleware
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Static folder
app.use(express.static(path.join(__dirname, 'public')));
// Method override middleware
app.use(methodOverride('_method'));
// Express session midleware
//PASSPORT CONFIGURATION
app.use(require("express-session")({
secret: "secret",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
app.use(flash());
// Global variables
app.use(function(req, res, next) {
res.locals.success_msg = req.flash('success_msg');
res.locals.error_msg = req.flash('error_msg');
res.locals.error = req.flash('error');
next();
});
// Index Route
app.get('/', (req, res) => {
const title = 'Welcome';
res.render('index', {
title: title
});
});
// About Route
app.get('/about', (req, res) => {
res.render('about');
});
// Use routes
app.use('/ideas', ideas);
app.use('/users', users);
const port = process.env.PORT;
app.listen(port, () => {
console.log(`Server started on port ${port}`);
});
User Schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const passportLocalMongoose = require("passport-local-mongoose");
// Create Schema
const UserSchema = new Schema({
name:{
type: String,
required: true
},
email:{
type: String,
required: true
},
password:{
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
UserSchema.plugin(passportLocalMongoose, {usernameField: "email"});
mongoose.model('users', UserSchema);
try importing LocalStrategy from require('passport-local').Strategy
like
const LocalStrategy = require('passport-local').Strategy

stormpath error If you do not specify a \'requestAuthenticator\' field, y

I'm getting the error specified above when trying to build a express-stormpath app. I'll list the applicable code:
Error: If you do not specify a 'requestAuthenticator' field, you must specify an ApiKey.
at Object.getAuthenticator (d:\dev\git-repos\bps\VolumeGridDataEntry\node_modules\express- stormpath\node_modules\stormpath\lib\authc\index.js:24:11)
at new RequestExecutor (d:\dev\git-repos\bps\VolumeGridDataEntry\node_modules\express-stormpath\node_modules\stormpath\lib\ds\RequestExecutor.js:37:37)
at new DataStore (d:\dev\git-repos\bps\VolumeGridDataEntry\node_modules\express-stormpath\node_modules\stormpath\lib\ds\DataStore.js:46:52)
at new Client (d:\dev\git-repos\bps\VolumeGridDataEntry\node_modules\express-stormpath\node_modules\stormpath\lib\Client.js:8:21)
at d:\dev\git-repos\bps\VolumeGridDataEntry\node_modules\express-stormpath\lib\stormpath.js:60:36
at d:\dev\git-repos\bps\VolumeGridDataEntry\node_modules\express-stormpath\node_modules\stormpath\lib\authc\ApiKeyLoader.js:14:14
at d:\dev\git-repos\bps\VolumeGridDataEntry\node_modules\express-stormpath\node_modules\stormpath\node_modules\properties-parser\index.js:348:20
at fs.js:208:20
at Object.oncomplete (fs.js:108:15)
[Updated] server.js
var express = require('express'),
session = require('express-session'),
crypto = require('crypto'),
formidable = require('formidable'),
path = require('path'),
favicon = require('serve-favicon'),
logger = require('morgan'),
cookieParser = require('cookie-parser'),
bodyParser = require('body-parser'),
index = require('./routes/index'),
users = require('./routes/users'),
stormpath = require('express-stormpath'),
config = require('./config/credentials.js'),
app = express();
function hashPwd(pwd) {
return crypto.createHash('sha256').update(pwd).digest('base64').toString();
}
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(stormpath.init(app, {
apiKeyFile: config.stormpathapi.apiKeyFile,
application: config.stormpathapi.application,
secretKey: config.stormpathapi.secretKey,
sessionDuration: 1000 * 60 * 30
}));
// domains for better error handling
app.use(function(req, res, next){
// create a domain for this request
var domain = require('domain').create();
// handle errors on this domain
domain.on('error', function(err){
console.error('DOMAIN ERROR CAUGHT\n', err.stack);
try {
// failsafe shutdown in 5 seconds
setTimeout(function(){
console.error('Failsafe shutdown.');
process.exit(1);
}, 5000);
// stop taking new requests
server.close();
try {
// attempt to use Express error route
next(err);
} catch(error){
// if Express error route failed, try
// plain Node response
console.error('Express error mechanism failed.\n', error.stack);
res.statusCode = 500;
res.setHeader('content-type', 'text/plain');
res.end('Server error.');
}
} catch(error){
console.error('Unable to send 500 response.\n', error.stack);
}
});
// add the request and response objects to the domain
domain.add(req);
domain.add(res);
// execute the rest of the request chain in the domain
domain.run(next);
});
// cross-site request forgery protection
app.use(require('csurf')());
app.use(function(req, res, next){
res.locals._csrfToken = req.csrfToken();
next();
});
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'client')));
//name: cookie_name,
//store: sessionStore, // connect-mongo session store
app.use(session({
secret: config.cookieSecret,
resave: false,
saveUninitialized: true
}));
app.use('/', index);
app.use('/users', users);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
module.exports = app;
route index.js
var express = require('express'),
stormpath = require('express-stormpath'),
router = express.Router();
router.get('/', stormpath.groupsRequired(['dataentry']), function(req, res) {
res.render('index', { title: 'Volume Grid Data Entry' });
});
module.exports = router;
Initially the route signature was this but that didn't work either. I will need the ability in the future to base page security on groups...so, I'm not sure if I should use loginRequired or groupsRequired or both :-/
router.get('/', stormpath.loginRequired, function(req, res) {
Thanks!
That code looks correct -- but where is your app.use(require('./index')); code? That will be necessary after the app.use(stormpath.init(...)) stuff above =)
NOTE: I'm the author of the express-stormpath library.
EDIT: Here's a full example:
var express = require('express'),
stormpath = require('express-stormpath'),
router = express.Router();
var app = express();
router.get('/', stormpath.groupsRequired(['dataentry']), function(req, res) {
res.render('index', { title: 'Volume Grid Data Entry' });
});
app.use('/', router);
app.listen(3000);