koa, passport-http strategy: basic authentication not working - passport.js

I can't figure out how to make koa work with basic authentication.
I have no problem with local strategy. So I thought I could implement basic auth easily.
My home page is a form which post to /login route.
This is auth.js:
'use strict';
let passport = require('koa-passport'),
co = require('co'),
monk = require('monk'),
wrap = require('co-monk'),
db = monk('localhost/test'),
users = wrap(db.get('users'));
function *getUser(username, password) {
let user = yield users.findOne({username: username, password: password});
return user;
};
passport.serializeUser(function(user, done) {
done(null, user._id)
})
passport.deserializeUser(function(id, done) {
done(null, id)
})
let BasicStrategy = require('passport-http').BasicStrategy
passport.use(new BasicStrategy(function(username, password, done) {
co(function *(next) {
try {
return yield getUser(username, password);
} catch (ex) {
console.log('error: ', ex);
return null;
}
}).then(function(user) {
done(null, user);
});
}));
This is server.js:
/* jshint node: true */
'use strict';
let koa = require('koa');
let app = koa();
// sessions
let session = require('koa-generic-session');
app.keys = ['your-session-secret'];
app.use(session());
// body parser
let bodyParser = require('koa-bodyparser');
app.use(bodyParser());
// authentication
require('./auth');
let passport = require('koa-passport');
app.use(passport.initialize());
app.use(passport.session());
// append view renderer
let views = require('koa-render');
app.use(views('./views', {
map: { html: 'handlebars' },
cache: false
}))
// public routes
let router = require('koa-router');
let pub = new router();
pub.get('/', function*() {
this.body = yield this.render('login');
})
pub.post('/login',
passport.authenticate('basic', {
session: false
})
)
pub.get('/logout', function*(next) {
this.logout();
this.redirect('/');
})
app.use(pub.routes());
app.use(pub.allowedMethods());
app.use(function*(next) {
if (this.isAuthenticated()) {
yield next
} else {
this.redirect('/')
}
})
var secured = new router()
secured.get('/app', function*(next) {
this.body = yield this.render('app');
})
app.use(secured.routes());
app.use(secured.allowedMethods());
app.listen(3000);
Using node 4.2.3 and these are the packages version I'm using:
"dependencies": {
"co": "^4.6.0",
"co-monk": "^1.0.0",
"handlebars": "^4.0.5",
"koa": "^1.1.2",
"koa-bodyparser": "^2.0.1",
"koa-generic-session": "^1.10.1",
"koa-passport": "^1.2.0",
"koa-render": "^0.2.1",
"koa-router": "^5.3.0",
"monk": "^1.0.1",
"passport-http": "^0.3.0"
}
After I compile the form and post my valid login form I recieve:
Request URL:http://localhost:3000/app
Request Method:GET
Status Code:302 Found
Remote Address:[::1]:300
This is just a sample I know password should be hashed, but I was just trying to get it working. Any ideas will be greatly appreciated.
Thanks in advance

I found a solution. It was even fairly simple. Posting it here for everyone else who might be interested.
Just comment:
// app.use(function*(next) {
// if (this.isAuthenticated()) {
// yield next
// } else {
// this.redirect('/')
// }
// });
and change the secured route to:
secured.get('/app', passport.authenticate('basic', {
session: false}), function*(next) {
this.body = yield this.render('app');
});
This way the auth middleware gets invoked whenever you try to access the protected route.

Related

How to solve the problem of getting req.cookies being undefined in production environment?

//back end sign in controller
const signinController= async (req,res)=>
{
const{email,password}=req.body
try {
const user= await User.findOne({email:email})
if(!user)
{
res.status(400).json({errorMessage:"This email is not registered, please signup"})
}
else
{
let status= await bcrypt.compare(password,user.password)
if(!status)
{
res.status(400).json({errorMessage:"Incorrect password"})
}
else
{
const payload={
user:{
_id:user._id
}
}
jwt.sign(payload,jwtSecret,{expiresIn:jwtExpire},(err,token)=>
{
const{_id,username,email,role}=user
if(err)
{
console.log("jwterror : ",err)
}
else
{
res.status(200).json({token,user:{_id,username,email,role}})
}
})
}
}
} catch (error) {
if(error)
{
console.log(error)
res.status(400).json({errorMessage:"server error"})
}
}
}
// front end part
const handleSubmit=(e)=>
{
e.preventDefault()
if(isEmpty(email.trim())||isEmpty(password.trim()))
{
setFormData({...formData,errorMsg:"Fields are empty"})
}
else if(!isEmail(email))
{
setFormData({...formData,errorMsg:"Invalid email"})
}
else
{
setFormData({...formData,loading:true})
signinAuth({email,password}).then((res)=>
{
setFormData({...formData,loading:false,errorMsg:false})
setAuthentication(res.data.token,res.data.user)
if(isAuthenticated() && isAuthenticated().role===1)
{
history.push("/admin/dashboard")
}
else
{
history.push("/user/dashboard")
}
}).catch((err)=>
{
setFormData({...formData,loading:false,errorMsg:err.response.data.errorMessage})
})
}
}
export const signinAuth= async(data)=>
{
const config={
headers:{"Content-Type":"application/json"}
}
let response= await axios.post(`${api}/api/auth/signin`,data,config)
return response
}
export const setAuthentication=(token,user)=>
{
setCookies("token",token)
setLocalStorage("user",user)
}
export const setCookies=(key,value)=>
{
cookies.set(key,value,{expires:1})
}
I am unable to get req.cookies to verify JWT in production environment, but locally the code is working and I am able to get the req.cookies.
//jwt verification middleware
const jwt = require("jsonwebtoken")
const {jwtSecret}= require("../Config/Keys")
exports.jwtAuthenticator=(req,res,next)=>
{
const token = req.cookies.token
if(!token)
{
res.status(400).json({"errorMessage" :"authorisation denied because no token"})
}
try {
const decoded=jwt.verify(token,jwtSecret)
req.user=decoded.user
next()
} catch (error) {
console.log("jwt error: ",error)
res.status(401).json({"errorMessage" :"authorisation denied"})
}
// app.js
const express = require("express");
const mongoose=require("./database/db")
const cors= require("cors")
const authRouter= require("./Routes/Auth")
const categoryRoute= require("./Routes/Category")
const productRoute= require("./Routes/Product")
const cartRoute= require("./Routes/Cart")
const orderRoute=require("./Routes/Order")
const cookieParser = require("cookie-parser");
const morgan = require("morgan");
const dotenv= require("dotenv")
dotenv.config();
const app= express();
const PORT=process.env.PORT||5000
app.use(express.json())
app.use(express.urlencoded({ extended: true }));
const corsOptions ={
origin:'http://localhost:3000',
credentials:true //access-control-allow-credentials:true
}
app.use(cors(corsOptions))
app.use(morgan("dev"))
app.use(cookieParser())
app.use("/api/auth",authRouter)
app.use("/api/category",categoryRoute)
app.use("/api/product",productRoute)
app.use("/api/cart",cartRoute)
app.use("/api/order",orderRoute)
mongoose.connectDB()
app.get("/",(req,res)=>
{
res.send("server")
})
app.listen(PORT,()=>
{
console.log(`server connected to ${PORT}`)
})
I am unable to get req.cookies to verify JWT in production environment(render platform). However, I am able to fetch cookies locally.
I have tried these JWT middleware,
App.js code locally and it is working. However, after deploying, I couldn't able to get req.cookies.
Update2: give a try with this setting on the cookie if you want to set the cookie on the frontend, (i am not familiar though on the front)
export const setAuthentication=(token,user)=> {
setCookies(token)
setLocalStorage("user",user)
}
export const setCookies=(value)=> {
cookies.set("token", value, {
sameSite: "none" //none for cross domain cookie, If omitted, it's set to 'no_restriction'.
secure: true, //required for sameSite=none attribute, set to false by default in local dev
expires: 1,
})
}
Maybe have a look on the cookies.set() documentation https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/cookies/set
By default httpOnly is indeed false, that's why you can set cookie on the frontend.
2nd Solution: in case you just can't make it work, another common approch would be storing the jwt token in the localstorage just like how you do with the user, and retrieve it when you need by attaching it on your header, but again in my opinion setting this in localstorage or even manipulating cookie on the front is not the best practice, i believe setting all this stuff on the backend with httpOnly and secure cookie flags, or storing jwt in memory for direct use (some limitation) is still the best way to go.
export const setAuthentication=(token,user)=> {
setLocalStorage("token", token) // you have to make a function to store the token like how you do on the user.
setLocalStorage("user",user)
}
// addtoCard endpoint
const addToCart=async (productId)=> {
let user= localStorage.getItem("user")
user= JSON.parse(user)
let userId=user._id
let data={
userId,
productId
}
const token = localStorage.getItem("token");
const config={
headers:{
"Content-Type":"application/json",
Authorization: `Bearer ${token}`
},
"withCredentials": true
}
await axios.post(`${api}/api/cart/addtocart`,data,config).then((res)=> {
console.log(res.data.message)
})
}
than you just need to change the condition checking on the header instead of cookie and change the process in the middleware jwtAuthenticator:
exports.jwtAuthenticator=(req,res,next)=> {
const authHeader = req.headers['authorization'];
console.log(authHeader)
if(!authHeader) {
return res.status(400).json({"errorMessage" :"authorisation denied because no token"})
}
try {
const token = authHeader.split('')[1];
const decoded = jwt.verify(token, jwtSecret)
if(decoded) {
req.user = decoded.user
next();
}
} catch (error) {
console.log("jwt error: ",error)
res.status(403).json({"errorMessage": "forbidden token"})
}
};
//in signin i didn't need to use the jwt middleware. during signin I am only setting the generated token in cookie. I am using JWT middleware for verification is in route that used to add products to cart. and codes are mentioned below
//signin route
const express= require("express")
const {signupValidator,validatorResult}= require("../Middlewares/Validator")
const {signupController,signinController} =require("../Controllers/Auth")
const router= express.Router()
router.post("/signup",signupValidator,validatorResult,signupController)
router.post("/signin",signinController)
module.exports=router
//add to cart route
const express= require("express");
const { jwtAuthenticator } = require("../Middlewares/Authenticator");
const {AddToCart,GetCart,IncQuantity,DecQuantity,RemoveProduct,GetExactCart,RemoveCart} = require("../Controllers/Cart")
const router= express.Router()
router.post("/addtocart",jwtAuthenticator,AddToCart)
router.get("/getcart/:userId",GetCart)
router.get("/getexactcart/:userId",GetExactCart)
router.patch("/incquantity",jwtAuthenticator,IncQuantity)
router.patch("/decquantity",jwtAuthenticator,DecQuantity)
router.patch("/removeProduct",jwtAuthenticator,RemoveProduct)
router.patch("/removeCart/:userId",RemoveCart)
module.exports= router;
//front end part of calling this addtocart api
const addToCart=async (productId)=>
{
let user= localStorage.getItem("user")
user= JSON.parse(user)
let userId=user._id
let data={
userId,
productId
}
const config={
headers:{"Content-Type":"application/json"},
"withCredentials":true
}
await axios.post(`${api}/api/cart/addtocart`,data,config).then((res)=>
{
console.log(res.data.message)
})
}

Installation Link Breaks in Production - Shopify App

EDIT: I believe the issue is that app.use(serveStatic(PROD_INDEX_PATH)); prevents the app.use("/*", middleware from running. I'm still working on a solution and any help would be appreciated!
My Shopify app breaks after deploying to Heroku but ONLY when clicking the provided "install" link.
For example, when I click "Install App", this is the URL that is generated: https://shopify-app.herokuapp.com/?hmac=d61597ca3ea6ca74b8bd6ea8f8bcc812b4382fad6f15434c0158bc4c3ade519a&host=dXBsaWZ0ZWQtY29tbWVyY2UtZGV2Lm15c2hvcGlmeS5jb20vYWRtaW4&shop=dev.myshopify.com&timestamp=1657326911
Which skips that auth process and takes me to "This page does not exist."
So the flow is: "Click Install App" => "This page does not exist"
However, if I manually click the auth link: https://shopify-app.herokuapp.com/api/auth?shop=dev.myshopify.com, the app successfully completes the auth process and works without any issues.
I'm using the standard server code scaffolded from the Shopify CLI.
Commenting out this section in production allows the app to partially function:
if (isProd) {
const compression = await import("compression").then(
({ default: fn }) => fn
);
const serveStatic = await import("serve-static").then(
({ default: fn }) => fn
);
app.use(compression());
app.use(serveStatic(PROD_INDEX_PATH));
}
Once the code is commented out, the generated install link is of the format: https://shopify-app.herokuapp.com/api/auth?shop=dev.myshopify.com. However, functionality throughout the app breaks upon clicking the install button.
The new flow is: "Click Install App" => "Begin Auth flow" => "Accept requested scopes" => "App breaks"
So essentially, the code snippet if(isProd) breaks the installation link while removing it breaks the rest of the app's functionality.
More specifically, the app.use(serveStatic(PROD_INDEX_PATH)); snippet.
Really scratching my head on this one. Any ideas what's going on?
This is my server:
const USE_ONLINE_TOKENS = true;
const TOP_LEVEL_OAUTH_COOKIE = "shopify_top_level_oauth";
// #ts-ignore
const PORT = parseInt(process.env.BACKEND_PORT || process.env.PORT, 10);
const isTest = process.env.NODE_ENV === "test" || !!process.env.VITE_TEST_BUILD;
const versionFilePath = "./version.txt";
let templateVersion = "unknown";
if (fs.existsSync(versionFilePath)) {
templateVersion = fs.readFileSync(versionFilePath, "utf8").trim();
}
// TODO: There should be provided by env vars
const DEV_INDEX_PATH = `${process.cwd()}/frontend/`;
const PROD_INDEX_PATH = `${process.cwd()}/frontend/dist/`;
const DB_PATH = `${process.cwd()}/database.sqlite`;
Shopify.Context.initialize({
// #ts-ignore
API_KEY: process.env.SHOPIFY_API_KEY,
// #ts-ignore
API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
// #ts-ignore
SCOPES: process.env.SCOPES.split(","),
// #ts-ignore
HOST_NAME: process.env.HOST.replace(/https?:\/\//, ""),
// #ts-ignore
HOST_SCHEME: process.env.HOST.split("://")[0],
API_VERSION: ApiVersion.April22,
IS_EMBEDDED_APP: true,
// This should be replaced with your preferred storage strategy
// SESSION_STORAGE: new Shopify.Session.SQLiteSessionStorage(DB_PATH),
SESSION_STORAGE: new Shopify.Session.CustomSessionStorage(
storeCallback,
// #ts-ignore
loadCallback,
deleteCallback
),
USER_AGENT_PREFIX: `Node App Template/${templateVersion}`,
});
const ACTIVE_SHOPIFY_SHOPS = {};
Shopify.Webhooks.Registry.addHandler("APP_UNINSTALLED", {
path: "/api/webhooks",
webhookHandler: async (topic, shop, body) => {
// #ts-ignore
delete ACTIVE_SHOPIFY_SHOPS[shop],
//Delete unsubscribed shop and clean undefined entries
console.log("APP UNINSTALLED");
await pool.query(
`DELETE FROM shop WHERE shop_url=$1 OR shop_url='undefined' OR shop_url='' OR shop_url IS NULL`,
[shop]
);
},
});
setupGDPRWebHooks("/api/webhooks");
// export for test use only
export async function createServer(
root = process.cwd(),
isProd = process.env.NODE_ENV === "production",
billingSettings = BILLING_SETTINGS
) {
const app = express();
app.set("top-level-oauth-cookie", TOP_LEVEL_OAUTH_COOKIE);
app.set("active-shopify-shops", ACTIVE_SHOPIFY_SHOPS);
app.set("use-online-tokens", USE_ONLINE_TOKENS);
app.use(cookieParser(Shopify.Context.API_SECRET_KEY));
applyAuthMiddleware(app, {
billing: billingSettings,
});
app.post("/api/webhooks", async (req, res) => {
try {
await Shopify.Webhooks.Registry.process(req, res);
console.log(`Webhook processed, returned status code 200`);
} catch (error) {
console.log(`Failed to process webhook: ${error}`);
if (!res.headersSent) {
res.status(500).send(error.message);
}
}
});
app.use(bodyParser.json());
// All endpoints after this point will require an active session
app.use(
"/api/*",
verifyRequest(app, {
// #ts-ignore
billing: billingSettings,
})
);
//app.use("/api/test", test);
app.use("/api/sort-options", sortOptions);
app.use("/api/sort-logic", sortLogic);
app.get("/api/products-count", async (req, res) => {
const session = await Shopify.Utils.loadCurrentSession(req, res, true);
const { Product } = await import(
`#shopify/shopify-api/dist/rest-resources/${Shopify.Context.API_VERSION}/index.js`
);
const countData = await Product.count({ session });
res.status(200).send(countData);
});
app.post("/api/graphql", async (req, res) => {
try {
const response = await Shopify.Utils.graphqlProxy(req, res);
res.status(200).send(response.body);
} catch (error) {
res.status(500).send(error.message);
}
});
app.use(express.json());
app.use((req, res, next) => {
const shop = req.query.shop;
if (Shopify.Context.IS_EMBEDDED_APP && shop) {
res.setHeader(
"Content-Security-Policy",
`frame-ancestors https://${shop} https://admin.shopify.com;`
);
} else {
res.setHeader("Content-Security-Policy", `frame-ancestors 'none';`);
}
next();
});
if (isProd) {
const compression = await import("compression").then(
({ default: fn }) => fn
);
const serveStatic = await import("serve-static").then(
({ default: fn }) => fn
);
app.use(compression());
app.use(serveStatic(PROD_INDEX_PATH));
console.log(`Serving static files from ${PROD_INDEX_PATH}`);
}
app.use("/*", async (req, res, next) => {
const shop = req.query.shop;
console.log("THIS IS THE CATCHALL ROUTE")
// //CHECK TO MAKE SURE SCOPE EXISTS AND ISN'T UNDEFINED, OFFLINE SHOPS?
const shopValue = await pool.query(`SELECT * FROM shop WHERE shop_url=$1`, [
shop,
]);
if (shopValue?.rows[0]?.scope) {
ACTIVE_SHOPIFY_SHOPS[shop] = shopValue.rows[0].scope;
} else {
ACTIVE_SHOPIFY_SHOPS[shop] = undefined;
}
// Detect whether we need to reinstall the app, any request from Shopify will
// include a shop in the query parameters.
// #ts-ignore
if (app.get("active-shopify-shops")[shop] === undefined) {
res.redirect(`/api/auth?shop=${shop}`);
} else {
// res.set('X-Shopify-App-Nothing-To-See-Here', '1');
const fs = await import("fs");
console.log(`Serving static files from ${DEV_INDEX_PATH}`);
const fallbackFile = join(
isProd ? PROD_INDEX_PATH : DEV_INDEX_PATH,
"index.html"
);
res
.status(200)
.set("Content-Type", "text/html")
.send(fs.readFileSync(fallbackFile));
}
});
return { app };
}
The solution is changing: app.use(serveStatic(PROD_INDEX_PATH)); to app.use(serveStatic(PROD_INDEX_PATH, { index: false }));
It was a bug in the CLI which was resolved here

Bottender - Enabling persistent_menu and get_started button on facebook

I am using NodeJS & Bottender to run my facebook messenger webhook.
So I want to have a get started button and a persistent menu. I checked & patterned my config on the examples provided on the repo like this one but I can't get it to be present on messenger yet.
here's my config.js
require('dotenv').config();
const {
FB_ACCESS_TOKEN,
FB_VERIFY_TOKEN,
APP_ID,
APP_SECRET,
} = process.env;
const { bots } = require('./global-session');
const profile = {
get_started: {
payload: '/start',
},
persistent_menu: [
{
locale: 'default',
composer_input_disabled: false,
call_to_actions: [
{
type: 'postback',
title: 'Menu',
payload: '/menu',
},
],
},
],
};
/* istanbul ignore next */
module.exports = {
messenger: {
bot: {
accessToken: FB_ACCESS_TOKEN,
appId: APP_ID,
appSecret: APP_SECRET,
mapPageToAccessToken: bots.getTokenByPageById,
profile,
},
server: {
verifyToken: FB_VERIFY_TOKEN,
path: '/messenger',
profile,
},
},
};
and here's how I use it
const { MessengerBot } = require('bottender');
const express = require('express');
const bodyParser = require('body-parser');
const { registerRoutes } = require('bottender/express');
const handler = require('./handlers');
const logger = require('./utils/logger');
const { APP_PORT, NODE_ENV } = process.env;
const server = express();
/* istanbul ignore next */
const verify = (req, res, buf) => {
req.rawBody = buf.toString();
};
server.use(bodyParser.json({ verify }));
server.use(require('morgan')('short', { stream: logger.logStream }));
const { messenger } = require('./config');
const bots = {
messenger: new MessengerBot(messenger.bot).onEvent(handler.messenger.execute),
// Define other platform bots here!
};
const initialize = async () => {
try {
registerRoutes(server, bots.messenger, messenger.server);
// Start server
server.listen(APP_PORT, () => logger.info(`ENV[${NODE_ENV}] - server is listening on port ${APP_PORT}...`));
} catch (e) {
logger.error('unable to start server!');
logger.error(e);
throw Error();
}
};
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'test') {
initialize();
}
module.exports = initialize;
Background on my facebook app
- It is already published to public and approved(at least for pages_messaging)
Anything I missed to do? Any help would be appreciated!
We leave messenger profile control to developers, because we can't assume when developers want to set, update or delete bots' messenger profile.
For example, you can have a script like this:
const { MessengerClient } = require('messaging-api-messenger');
const config = require('./bottender.config');
const client = MessengerClient.connect();
const tokens = [ /* */ ]; // get tokens from somewhere
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
client.setMessengerProfile(config.messenger.profile, {
access_token: token, // important! pass token to client api
});
}
Or you can call setMessengerProfile when you register your token to your database:
async function registerPage(page, token) {
// ....put page and token to database
await client.setMessengerProfile(config.messenger.profile, {
access_token: token, // important! pass token to client api
});
}

ExpressJS + Express-mailer No default engine

I've developed my web app using ReactJS and I'm using as a server ExpressJS. Everithing was working fine until I implemented a /contactme endpoint which just must work as restful POST to send an email. I mean, my aim is just publishing an endpoint to send mails.
I'm using express-mailer for the job, but when I try to send an email using send method I'm getting this error:
message:"No default engine was specified and no extension was provided."
stack:"Error: No default engine was specified and no extension was provided.\n at new View (c:\React\web.facundolarocca.com\node_modules\express\lib\view.js:62:11)\n at EventEmitter.render (c:\React\web.facundolarocca.com\node_modules\express\lib\application.js:569:12)\n at sendMail (c:\React\web.facundolarocca.com\node_modules\express-mailer\lib\express-mailer.js:55:5)\n at Object.exports.extend.createSend [as send] (c:\React\web.facundolarocca.com\node_modules\express-mailer\lib\express-mailer.js:98:7)\n at c:\React\web.facundolarocca.com\server.js:28:16\n at Layer.handle [as handle_request] (c:\React\web.facundolarocca.com\node_modules\express\lib\router\layer.js:95:5)\n at next (c:\React\web.facundolarocca.com\node_modules\express\lib\router\route.js:131:13)\n at Route.dispatch (c:\React\web.facundolarocca.com\node_modules\express\lib\router\route.js:112:3)\n at Layer.handle [as handle_request] (c:\React\web.facundolarocca.com\node_modules\express\lib\router\layer.js:95:5)\n at c:\React\web....
I understand that ExpressJS needs some view engine to return html files, but in my case I'm going to respond a simpli JSON.
Below is my code:
var path = require('path');
var express = require('express');
var app = express();
var mailer = require('express-mailer');
var PORT = process.env.PORT || 3000;
app.use(express.static(path.join(__dirname, '/build')));
mailer.extend(app, {
from: 'no-reply#gmail.com',
host: 'smtp.gmail.com',
secureConnection: true,
port: 465,
transportMethod: 'SMTP',
auth: {
user: '*********#gmail.com',
pass: '**********'
}
});
app.get('/', function (req, res) {
//this is working fine
res.sendFile(__dirname + '/build/index.html')
});
app.get('/contactme', function (req, res) {
try {
app.mailer.send('test', {
to: 'myemaila#myemail.com.ar',
subject: 'some subject'
}, function (err) {
if (err) {
console.log(err);
res.status(400).json('There was an error sending the email');
} else {
res.status(200).json('Email Sent');
}
});
} catch (error) {
//Error is raised here
res.status(500).json(error);
}
});
app.listen(PORT, function (error) {
if (error) {
console.error(error);
} else {
console.info("==> 🌎 Listening on port %s. Visit http://localhost:%s/ in your browser.", PORT, PORT);
}
});
So I have two problems, on one hand is how to avoid this error , but in the other hand is why do I need an egine view .
I just expect, from the client side, to read the status code to show some message. I thought I could be able to implement in the same way as I was using restify.
My solution was to move to nodemailer. Here is the whole code:
package.json
{
"dependencies": {
"express": "^4.14.0",
"nodemailer": "^2.7.0",
"nodemailer-smtp-transport": "^2.7.2"
}
}
Code
server.post('/contactme', (req, res) => {
try{
let mailer = require('nodemailer')
let smtpTransport = require('nodemailer-smtp-transport')
let options = {
service: config.mailService.service,
secure: true,
auth: {
user: config.mailService.user,
pass: config.mailService.password
}
}
let transport = mailer.createTransport(smtpTransport(options))
let mail = {
from: 'from',
to: 'to',
subject: 'subject',
html: 'body'
}
transport.sendMail(mail, (error, response) => {
transport.close()
if (error) {
res.json(400, {error});
} else {
res.json(200, {response: 'Mail sent.'});
}
})
} catch(error) {
res.json(400, {error});
}
})

Create sequelize transaction inside Express router

I'm trying to make a rest request using sequelize transaction, unfortunately it doesn't works:
undefined is not a function
This means that sequelize.transaction is undefined, sequelize is imported but not instantiated for using inside my route:
router.post('/', secret.ensureAuthorized, function(req, res) {
var sequelize = models.sequelize;
var newpost;
sequelize.transaction({autocommit: false}, function (t) {
return models.post.build().updateAttributes({
title: req.body.title,
shortdescription: req.body.description.substring(0,255),
description: req.body.description,
titleImage: req.body.titleImage,
link: req.body.link,
userid: req.body.userid
}, {transaction: t}).then(function(post){
newpost = post;
// create categories
var tags = req.body.categories;
models.hashtag.bulkCreate(tags, {transaction: t}).then(function(){
newpost.setTags(tags, {transaction: t});
});
});
}).then(function (result) {
// Transaction has been committed
// result is whatever the result of the promise chain returned to the transaction callback is
if (newpost) {
res.json(newpost);
}
console.log(result);
}).catch(function (e) {
// Transaction has been rolled back
// err is whatever rejected the promise chain returned to the transaction callback is
throw e;
});
});
My models works without any issues and the express routes are working too but not with transaction.
My package json
{
"name": "myapp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"body-parser": "~1.12.4",
"cookie-parser": "~1.3.5",
"debug": "~2.2.0",
"express": "~4.12.4",
"jade": "~1.9.2",
"morgan": "~1.5.3",
"serve-favicon": "~2.2.1",
"jsonwebtoken": "^5.0.2",
"pg": "^4.4.0",
"pg-hstore": "^2.3.2",
"crypto-js": "^3.1.5",
"sequelize": "^3.2.0"
}
}
My index.js is working fine too, but don't know how to pass the same instance of sequelize here for express routes:
var Sequelize = require('sequelize');
var config = require('../config'); // we use node-config to handle environments
var fs = require("fs");
var path = require("path");
var models = require('../models');
// initialize database connection
var sequelize = new Sequelize(
config.database.name,
config.database.username,
config.database.password, {
dialect: 'postgres',
host: config.database.host,
port: config.database.port,
autoIncrement: true,
omitNull: true,
freezeTableName: true,
pool: {
max: 15,
min: 0,
idle: 10000
},
});
var db = {};
fs
.readdirSync(__dirname)
.filter(function(file) {
return (file.indexOf(".") !== 0) && (file !== "index.js");
})
.forEach(function(file) {
var model = sequelize["import"](path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(function(modelName) {
if ("associate" in db[modelName]) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
sequelize.sync({
force: true
}).then(function(){
// load batch
if (process.env.BATCH) {
console.log("loading batch");
var batch = require("../config/batch");
batch.loadPosts();
}
});
module.exports = db;
best regards.
UPDATE
I changed the code as above explained.
Now my error is:
Unhandled rejection Error: commit has been called on this transaction(c457e532-b
164-43dc-9b0e-432be031fe36), you can no longer use it
I'm using Postgres database.
It seems to me that your database initialization code may be stored in a file called sequelize.js, which you are trying to import in your route handler.
However, you're importing the global sequelize module, and not your local one. You need to use a relative path to do that:
router.post('/', function(req, res) {
var sequelize = require('./sequelize');
sequelize.transaction(function(t){
console.log('transaction openned'+t);
});
});
(that assumes that your sequelize.js file is located in the same directory as your route handler file is; if not, you should change the ./ part)
This is my final solution, I didn't know about returning those promises, now I can do that like this:
var sequelize = models.sequelize;
var newpost;
// create categories
var tags = req.body.categories;
sequelize.transaction({autocommit: false}, function (t) {
return models.post.build().updateAttributes({
title: req.body.title,
shortdescription: req.body.description.substring(0,255),
description: req.body.description,
titleImage: req.body.titleImage,
link: req.body.link,
userid: req.body.userid
}, {transaction: t}).then(function(post){
newpost = post;
for (var i = 0; i < tags.length;i++) {
// ({where: {username: 'sdepold'}, defaults: {job: 'Technical Lead JavaScript'}})
return models.hashtag.findOrCreate(
{where: {description: tags[i].description},
defaults: {description: tags[i].description},
transaction: t}).spread(function(tag, created) {
return newpost.addTag(tag, {transaction: t});
});
}
});
}).then(function (result) {
// Transaction has been committed
// result is whatever the result of the promise chain returned to the transaction callback is
if (newpost) {
res.json(newpost);
}
console.log(result);
}).catch(function (e) {
// Transaction has been rolled back
// err is whatever rejected the promise chain returned to the transaction callback is
res.status(500).send({
type: false,
data: e
});
throw e;
});

Resources