I've made some vimeo api calls to get videos, but when I do a get request on the server it responds with the html on that path instead of the data from the server. I'm also using angular-client-side-auth (https://github.com/fnakstad/angular-client-side-auth). I'm new to this, so I'm struggling to understand why this happens.
server.js
app.get('/api/mostviewed', function (req, res) {
MostViewed.find({}, { _id: 0, iframe: 1 }, function (err, docs) {
res.json(docs);
});
});
inside client-side-auth's routes.js there's this, which causes it all(This file is on the server-side):
{
path: '/*',
httpMethod: 'GET',
middleware: [function(req, res) {
var role = userRoles.public, username = '';
if(req.user) {
role = req.user.role;
username = req.user.username;
}
res.cookie('user', JSON.stringify({
'username': username,
'role': role
}));
res.render('index');
}]
}
How can I solve this? I want to maintain the path: '/*', or change it while keeping the function similar, so I can get my data from the server. Or is there a different way to solve this?
EDIT:
Solution
{
path: '/api/mostviewed',
httpMethod: 'GET',
middleware: [Video.getmostviewed]
},
inside Video.js I made this:
getmostviewed: function(req,res){
MostViewed.find({}, { _id: 0, iframe: 1 }, function (err, docs) {
res.json(docs);
});
}
In your case, client could not find logic corresponding to /api/mostviewed, thus reached /* and displayed html instead of json.
Possible solution
Add following similar logic before /*
{
path: '/api/*',
httpMethod: 'GET',
middleware: [function(req, res) { // sample middleware logic
var role = userRoles.public, username = '';
if(req.user) {
role = req.user.role;
username = req.user.username;
}
res.cookie('user', JSON.stringify({
'username': username,
'role': role
}));
}]
},
Related
I've been trying to make an express middleware that sends an email using Nodemailer after the previous middleware finishes. I've come up with a few different designs, but ultimately each different version has it's drawback.
Ultimately, I would like the middleware to have a response from the previous middleware. If it is a success, then send a success email, otherwise, send an error email.
I came up with a dual design where one variation pushes to an error middleware, and a success leads to the next middleware. This contains some slight issues of sending multiple headers, specifically on an the second middleware erroring. I could say, if the mail errors out, do nothing. But that doesn't seem right. If anyone has any suggestions on a good design, that would be great.
From what you described, I would suggest not to create different middleware for that, but to just create one generic email function that would handle different type of messages. Then, just use that function in the first middleware and pass different parameters based on use case (success/error).
email-controller.js
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
host: process.env.EMAIL_HOST,
port: process.env.EMAIL_PORT,
secure: true,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASSWORD,
},
});
exports.send_email_message = (send_to, subject, message) => {
return new Promise((resolve, reject) => {
const email_message = {
from: { name: process.env.EMAIL_FRIENDLY_NAME },
to: send_to,
subject: subject,
text: message
};
transporter.sendMail(email_message).then(() => {
resolve(true);
}).catch((error) => {
reject(false);
});
})
}
custom-router.js
const { send_email_message } = require('./email-controller');
const express = require('express');
const router = express.Router();
router.get('/custom-middleware', async (req, res, next) => {
try {
// You can calculate "success" variable based on your custom logic
if(success){
await send_email_message('example#gmail.com', 'Success', 'This is body of success message.');
return res.status(200).json({ success: true });
} else {
await send_email_message('example#gmail.com', 'Error', 'This is body of error message.');
return res.status(400).json({ success: false });
}
} catch(error) {
return res.status(400).json({ success: false });
}
});
module.exports = router;
I am making a web application that allows Fortnite players to find other players to play with. Users should be able to register, login, post and comment. I have designed the frontend portion of the user login and registration features as well as the backend of the user registration but one of my requirements is that:
Before registration, the server should check whether the username provided is a real Fortnite username using the FortniteTracker API which provides user profiles using their very simple API.
Example Call: GET https://api.fortnitetracker.com/v1/profile/{platform}/{epic-nickname}
How do I verify the username exists before allowing the user to create the account?
I have tried creating a separate endpoint for the API call from the server side but I didn't know how to implement it into my /register endpoint
script.js
function registerRequest(username,password) {
$.ajax({
url: "http://localhost:8080/register",
type: 'POST',
data: JSON.stringify({username,password}),
contentType: "application/json",
error : function(err) {
console.log('Error here!', err)
},
success: function(data) {
console.log('Success!')
// What do I put here?
}
});
}
function handleRegisterSubmit(event) {
event.preventDefault();
const username = $(event.currentTarget).find('.username-register').val()
const password = $(event.currentTarget).find('.password-register').val()
const passwordConfirm = $(event.currentTarget).find('.password-confirm').val()
if (password === passwordConfirm) {
registerRequest(username,password)
}
else {
console.error("Passwords did not match")
}
}
$(function onLoad() {
displayRegisterPage()
$(`.js-content-section`).on('submit', '.js-register-form', handleRegisterSubmit)
}
})
server.js
app.post('/register', jsonParser, (req, res) => {
const requiredFields = ['username', 'password']
for (let i = 0; i < requiredFields.length; i++) {
const field = requiredFields[i]
if (!(field in req.body)) {
const message = `Missing \`${field}\` in request body`
console.error(message)
return res.status(400).send(message)
}
}
let username = req.body.username;
let password = req.body.password;
User.findOne({username})
.then(user => {
if (user) {
const message = `username is already taken`
console.error(message)
return res.status(400).send(message)
}
else {
User.create({username, password})
.then(user => {
const userRes = {
id: user._id,
username: user.username
}
res.status(201).json(userRes)
}
)
}
})
.catch(err => {
console.error(err)
res.status(500).json({ error: 'something went horribly wrong'})
})
})
app.get('/login', (req, res) => {
const usernameReq = User.findById(req.body.username);
if (usernameReq) {
console.log(usernameReq)
res.status(201).json(usernameReq)
}
})
schema.js
const UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true,
required: true,
trim: true
},
password: {
type: String,
required: true,
}
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
I expect that if I register with "ninja" as a username I should be able to register since that is a valid Fortnite username. The actual output currently allows users to register with any username that isnt already taken in the database.
You would need packages like axios, request, request-promise (Promise supported version of request) etc to make the external api call. You can try implementing within the register like.
const rp = require('request-promise');
app.post('/register', jsonParser, async (req, res) => {
...
let username = req.body.username;
let password = req.body.password;
const options = {
method : 'GET',
uri: 'https://api.fortnitetracker.com/v1/profile/{platform}/{epic-nickname}',
resolveWithFullResponse: true
}
const data = await rp(options)
// check if response code is 200 and check for the expected body
...
// continue rest of the code
}
Or have another middleware to call the external endpoint and do the checks like:
async function checkUser (req, res, next) {
const options = {
method : 'GET',
uri: 'https://api.fortnitetracker.com/v1/profile/{platform}/{epic-nickname}',
resolveWithFullResponse: true
}
const data = await rp(options)
// check if response code is 200 and check for the expected body
if (checks ok)
// if all check ok go to next middleware
next()
else
// if checks did not succeed
// you could pass error to error handler like next(new Error("Hey you do not exist"))
// or render something here
}
Then mount it like:
app.post('/register', jsonParser, checkUser, (req, res) {
...
You can do it simply by sending the username to the API https://api.fortnitetracker.com/v1/profile/{platform}/{epic-nickname}
It will give you a response mentioning about the user exists or not. Based on the response you can make another AJAX request to register the user only if the user does not exist.
I use a Promise request to resolve, reject when someone enters their username. It is only called onClick. in your request you will be able to determine if the call was successfull or not with the username.
I have an overloaded fetch function on an Angular service 'PostSvc'
if a user is provided as an argument the idea is to only return that users posts. Otherwise fetch all posts from the database....
The user is an 'object' passed from the post.ctrl.js with the following keys
{
username: $scope.currentUser,
body: $scope.postBody,
user_id: $scope.currentUser._id
}
this is giving me the _id field mongodb/mongoose generate as expected
console.log($scope.currentUser._id)
Here's the Mongoose Post model:
var db = require('../db')
var Post = db.model('Post', {
username : { type: String, required: true },
body : { type: String, required: true },
user_id: { type: String, required: true },
date : { type: String, required: true, default: Date.now },
})
module.exports = Post
Here's a snippet from the server.js file assigning the router:
app.use("/api/posts", require("./controllers/api/posts"))
Here's the Express './controllers/api/post.js' Router:
var Post = require('../../models/post')
var router = require('express').Router()
var websockets = require('../../websockets')
// this route works just fine
// returns All posts from the db
//
router.get("/", function(req, res, next) {
// find all posts from db
//
Post.find()
.sort("-date")
.exec(function(err, posts) {
if (err) { return next(err) }
res.json(posts)
next()
})
})
the probelem....
router.get("/:user_id", function(req, res, next) {
var query = {}
// these messages aren't being logged to console
// so there's no way the route is being used
console.log("from the get 'api/posts' method" )
console.dir(req.params.user_id)
if (req.params.user_id) {
query = { user_id: req.params.user_id } // sent from posts.svc.js
}
// this query is not executing properly
// I have no access to the `req.params.user_id`
//
Post.find({ user_id: req.params.user_id })
.sort("-date")
.exec(function(err, posts) {
if (err) { return next(err) }
res.json(posts)
next()
})
})
router.post("/", function(req, res, next) {
var post = new Post({ body: req.body.body })
if (!req.auth) { return res.sendStatus(401) }
post.username = req.auth.username
post.user_id = req.body.user_id
post.save(function(err, post) {
if (err) { return next(err) }
websockets.broadcast("new_post", post)
res.status(201).json(post)
})
})
module.exports = router
And last but not least here is the Angular Service that sends the initial 'GET' request to the 'server.js' -> './controllers/api/posts.js' and awaits the response:
angular.module("app").service('PostsSvc', function($http) {
this.fetch = function(user) {
var credentials = {}
if (user) {
credentials = user
// checkpoint
//
console.dir("from the fetch function " + credentials._id)
// only return authencated users posts
return $http.get("/api/posts", {
params: {
user_id: credentials._id // to the posts.js router
}
})
// return all posts otherwise
} else { return $http.get("/api/posts") }
}
this.create = function(post) {
return $http.post("/api/posts", post)
}
})
I have being in this same trouble with the router.get in express. The body is empty. I finally turned to use instead a router.post() which worked perfectly for me, as long as you also call the api with a post request whit angular.
I have a route as below which serves the static pages:
{
method: 'GET',
path: '/webapp/{param*}',
config: {
handler: {
directory :{
path : Path.join(__dirname, '../../webapp/'),
index: true
}
}
}
}
So, I want to check if the user is logged in or not before it takes user to that url "/webapp/#blabla".
User Can only access that url if user is logged in.
I tried to add pre option with a function in the above route but no luck.
{
method: 'GET',
path: '/webapp/{param*}',
pre:[{method:checkUrl, assign:'m1'}],
config: {
handler: {
directory :{
path : Path.join(__dirname, '../../webapp/'),
index: true
}
}
}
}
And the checkUrl function is as:
var checkUrl = function(request, reply) {
if (!request.auth.isAuthenticated) {
// redirect to login page
reply.redirect('/login');
}
return true;
}
Why is that i cannot get redirected to login page?
It depends slightly on which auth scheme you're using but the same principle applies. Here's an example using hapi-auth-basic (adapted from the example in the README):
var Bcrypt = require('bcrypt');
var Hapi = require('hapi');
var Path = require('path');
var Inert = require('inert');
var server = new Hapi.Server();
server.connection({ port: 4000});
var users = {
john: {
username: 'john',
password: '$2a$10$iqJSHD.BGr0E2IxQwYgJmeP3NvhPrXAeLSaGCj6IR/XU5QtjVu5Tm', // 'secret'
name: 'John Doe',
id: '2133d32a'
}
};
var validate = function (request, username, password, callback) {
var user = users[username];
if (!user) {
return callback(null, false);
}
Bcrypt.compare(password, user.password, function (err, isValid) {
callback(err, isValid, { id: user.id, name: user.name });
});
};
server.register([
require('inert'),
require('hapi-auth-basic')
], function (err) {
server.auth.strategy('simple', 'basic', { validateFunc: validate });
server.route({
method: 'GET',
path: '/webapp/{param*}',
config: {
auth: 'simple', // THIS IS THE IMPORTANT BIT
handler: {
directory :{
path : Path.join(__dirname, 'files'),
index: true
}
}
}
});
server.start(function (err) {
if (err) {
throw err;
}
console.log('Server started!');
})
});
The important point is just to add an auth property to the route config with the strategy name. It's the same as you would do for any routes. Have a read of this tutorial, it might clear it up for you.
Are you able to adapt that to your needs?
I am using satellizer for authentication in a MEAN app. After auth is complete i want to get the profile picture of the user. The following is my code.
Angular Controller
(function(){
'use strict';
angular
.module('nayakans07')
.controller('RegisterController', registerController);
function registerController ($scope, $auth) {
$scope.authenticate = function (provider) {
$auth.authenticate(provider)
.then(function (res) {
console.log(res);
}, authErrorHandler);
};
function authErrorHandler (err) {
console.log(err);
}
}
})();
Node.js Route Handler
(function () {
'use strict';
var request = require('request');
var qs = require('querystring');
var tokenHelper = require('../helpers/tokenHelper.js');
var config = require('../config/configuration.js');
var Member = require('../models/memberModel.js');
module.exports = function (req, res) {
var accessTokenUrl = 'https://graph.facebook.com/oauth/access_token';
var grapApiProfileUrl = 'https://graph.facebook.com/me';
var graphApiProfileImageUrl = 'https://graph.facebook.com/me/picture?width=250&json=true';
var params = {
client_id: req.body.clientId,
redirect_uri: req.body.redirectUri,
code: req.body.code,
client_secret: config.FACEBOOK_SECRET
};
request.get({
url: accessTokenUrl,
qs: params
}, function (err, response, accessToken) {
accessToken = qs.parse(accessToken);
request.get({
url: grapApiProfileUrl,
qs: accessToken,
json: true
}, function (err, response, profile) {
console.log(profile);
Member.findOne({
facebookId: profile.id
}, function (err, existingMember) {
if (existingMember) {
return res.status(200).send({
user: existingMember,
token: tokenHelper.createToken(existingMember, req, res)
});
} else {
request.get({
url: graphApiProfileImageUrl,
qs: accessToken,
}, function (err, response, profileImage) {
console.log(profileImage);
var fbData = {
facebookId: profile.id,
fullName: profile.name,
email: profile.email,
profileImage: profileImage
};
return res.status(200).send(fbData);
});
}
});
});
});
};
})();
After i get the access token from facebook i call the https://graph.facebook.com/me/picture?width=250&json=true endpoint with the access token to get the profile pic link. In the Facebook's API Explorer calling the same endpoint with the token i get the profile image url but when i call it from node.js endpoint handler i get some binary like data. part of the response is here.
����JFIF���Photoshop 3.08BIM�gtc2XjTON-90Jqu9JFj83(bFBMD01000ac0030000b909000069110000fe120000dc14000069190000c02400002326000029280000872a0000fd3e0000��ICC_PROF
How can i get the URL for the profile image instead of this response.
Not sure if this is still the case on the version of the API that you're using, but it used to cause a 301 redirect to the image if not passed the correct parameter (despite what their API Explorer suggests...) You can check your browser's network monitoring to verify this.
There are various sources for this, including the Official Facebook Graph-API documentation, which states:
By default this edge will return a 302 redirect to the picture image. To get access to the data about the picture, please include redirect=false in your query.
(Source: https://developers.facebook.com/docs/graph-api/reference/user/picture/#Reading )
Along with some (older, but similar) SO answers: https://stackoverflow.com/a/7881978/624590
You have a couple options. One is to add the redirect=false query param (per the Facebook documentation, above).
Another option is explicitly asking for the URL by adding an additional query param, &fields=url (the answer I linked to above suggests &fields=picture, but as far as I can tell, that's out of date). If you need the height and width in the response, just add those to the query: &fields=url,height,width. In my experience with the Facebook API, explicitly asking for fields tends to be a more reliable way to go.