Using an async function for a route gives me a 404 - javascript

I'm running an express server trying to use Outlooks API with node,
The routes are now using async and I copied a snippet from the docs:
The handling of the route:
const handle = {};
handle['/mail'] = mail;
and the function is:
async function mail(response, request) {
let token;
try {
token = await getAccessToken(request, response);
} catch (error) {
response.writeHead(200, {
'Content-Type': 'text/html'
});
response.write('<p> No token found in cookie!</p>');
response.end();
return;
}
console.log('Token found in cookie: ', token);
const email = getValueFromCookie(
'node-tutorial-email',
request.headers.cookie
);
console.log('Email found in cookie: ', email);
response.writeHead(200, {
'Content-Type': 'text/html'
});
response.write('<div><h1>Your inbox</h1></div>');
// Create a Graph client
const client = microsoftGraph.Client.init({
authProvider: done => {
// Just return the token
done(null, token);
}
});
try {
// Get the 10 newest messages
const res = await client
.api('/me/mailfolders/inbox/messages')
.header('X-AnchorMailbox', email)
.top(10)
.select('subject,from,receivedDateTime,isRead')
.orderby('receivedDateTime DESC')
.get();
console.log(`getMessages returned ${res.value.length} messages.`);
response.write(
'<table><tr><th>From</th><th>Subject</th><th>Received</th></tr>'
);
res.value.forEach(message => {
console.log(' Subject: ' + message.subject);
const from = message.from ? message.from.emailAddress.name : 'NONE';
response.write(
`<tr><td>${from}` +
`</td><td>${message.isRead ? '' : '<b>'} ${message.subject} ${
message.isRead ? '' : '</b>'
}` +
`</td><td>${message.receivedDateTime.toString()}</td></tr>`
);
});
response.write('</table>');
} catch (err) {
console.log(`getMessages returned an error: ${err}`);
response.write(`<p>ERROR: ${err}</p>`);
}
response.end();
}
It should be working... I don't see any errors there. Why am I getting a 404?

Related

Promise rejected in POST login using fetch API in reactjs and Nodejs

I am working on a simple login using POST Fetch in Reactjs with NodeJs API. The code is working good and redirect the page when we login using correct username and password but the problem is when using fake username. I got the error in console.log with Promise : "Rejected". And I still can not figure it why
Here is the code in login.js
async SubmitLogin(event){
event.preventDefault();
//debugger;
console.log(this.state)
await fetch(`http://localhost:4000/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(this.state)
})
.then ((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
// then Read the response as json.
else {
let result = response.json();
console.log(result)
if(result === 'Invalid'){
console.log(response)
alert('Invalid User');
//this.props.history.push('/login');
}
else {
alert('Login Sucessfull');
this.props.history.push('/home');
}
}
})
.catch((err) => {
console.error();
})
}
in my server.js, I used express-session like this:
//sales login
app.post('/login', jsonParser, (req, res) => { //jsonParser,
let username = req.body.username;
let password = req.body.password;
console.log("req: ",req.body);
if (username && password) {
dbConn.query(`SELECT * FROM user_tbl WHERE username = ? AND password = ?`, [username, password], (err, results, fields) => {
if (results.length > 0) {
req.session.loggedin = true;
req.session.username = username;
res.redirect('/home');
console.log(results)
console.log("req: ", req.body);
} else {
res.send('Incorrect Username and/or Password!');
}
res.end();
});
} else {
res.send('Please enter Username and Password!');
res.end();
}
});
app.get('/home', (req, res) => {
if (req.session.loggedin) {
res.send('Welcome back, ' + req.session.username + '!');
} else {
res.send('Please login to view this page!');
}
res.end();
});
and this is the result I got in console:
hopefully my question is clear.
I think your response doesnt come json format.You cant parse string to json.
Your response should be like this res.send({success:false , message : "Incorrect Username and/or Password!"})
After many suggestions and anwers, finally I can figure out how to solved this problem. Here is the code in login.js
//submit function
async SubmitLogin(event){
event.preventDefault();
console.log(this.state)
await fetch(`http://localhost:4000/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(this.state)
})
.then ((response) => {
if(response.status === 401) {
throw new Error('Unauthorized');
}
//return response.json();
})
.then((result) => {
console.log(result);
this.props.history.push('/home');
alert('Login Sucessfull');
})
.catch((err) => {
console.log();
})
}
and in the backend, I didn't change anything.

Axios multiple request on interceptor

I'm using the library axios in my react app.
I'm having a problem with the interceptor.
My question is let say I have three requests happening concurrently and I don't have the token, the interceptor calling the getUserRandomToken three time, I want the interceptor will wait until I'm getting the token from the first request and then continue to the others.
P.S. the token he is with an expiration date so I also checking for it and if the expiration date is not valid I need to create a new token.
This is the interceptor:
axios.interceptors.request.use(
config => {
/*I'm getting the token from the local storage
If there is any add it to the header for each request*/
if (tokenExist()) {
config.headers.common["token"] = "...";
return config;
}
/*If there is no token i need to generate it
every time create a random token, this is a axios get request*/
getUserRandomToken()
.then(res => {
/*add the token to the header*/
config.headers.common["token"] = res;
return config;
})
.catch(err => {
console.log(err);
});
},
function(error) {
// Do something with request error
return Promise.reject(error);
}
);
How about singleton object that will handle the token generations? something similar to this:
const tokenGenerator ={
getTokenPromise: null,
token: null,
getToken(){
if (!this.getTokenPromise){
this.getTokenPromise = new Promise(resolve=>{
/*supposed to be a http request*/
if (!this.token){
setTimeout(()=>{
this.token = 'generated';
resolve(this.token);
},0)
}else{
resolve(this.token);
}
})
}
return this.getTokenPromise;
}
you can reference this same object from the interceptors.
see example: JS FIddle
reference: reference
You can return a Promise from interceptor callback to "wait" until promise fullfiles (this will fit your case). Check out this example:
function axiosCall () {
return new Promise((resolve, reject) => {
Axios.post(URL, {apiKey}).then((response) => {
resolve(response.data.message);
}).catch((error) => {
reject(error);
});
});
}
instance.interceptors.request.use((config) => {
return axiosCall().then((tokenResponse) => {
setWebCreds(tokenResponse);
config.headers.Authorization = `Bearer ${tokenResponse}`;
return Promise.resolve(config)
}).catch(error => {
// decide what to do if you can't get your token
})
}, (error) => {
return Promise.reject(error);
});
More details here: https://github.com/axios/axios/issues/754
Following code doing certain tasks:
Update Token on 401
Make a queue of failed requests while the token is refreshing.
Restore the original request after token refreshing.
Once the peculiar request is given 200, remove it from the queue.
Config.js
import axios from 'axios';
import { AsyncStorage } from 'react-native';
import { stateFunctions } from '../../src/sharedcomponent/static';
const APIKit = axios.create({
baseURL: '',
timeout: 10000,
withCredentials: true,
});
const requestArray = [];
// Interceptor for Request
export const setClientToken = token => {
APIKit.interceptors.request.use(
async config => {
console.log('Interceptor calling');
let userToken = await AsyncStorage.getItem('userToken');
userToken = JSON.parse(userToken);
config.headers = {
'Authorization': `Bearer ${userToken}`,
'Accept': 'application/json',
"Content-Type": "application/json",
"Cache-Control": "no-cache",
}
// console.log('caling ' , config)
return config;
},
error => {
Promise.reject(error)
});
};
// Interceptor for Response
APIKit.interceptors.response.use(
function (response) {
if (requestArray.length != 0) {
requestArray.forEach(function (x, i) {
if (response.config.url == x.url) {
requestArray.splice(i, 1);
}
});
}
return response;
},
function (error) {
const originalRequest = error.config;
requestArray.push(originalRequest);
let reqData = "username=" + number + "&password=" + pin + "&grant_type=password" + "&AppType=2" + "&FcmToken=null";
// console.log('error ' , error);
if (error.message === "Request failed with status code 401" || error.statuscode === 401) {
if (!originalRequest._retry) {
originalRequest._retry = true;
return axios({
method: 'post',
url: '/api/login',
data: reqData,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Cache-Control": "no-cache",
}
})
.then(res => {
let response = res.data;
console.log('successfull Login', response)
if (res.data.StatusCode == 200) {
AsyncStorage.setItem('userToken', JSON.stringify(response.access_token));
stateFunctions.UserId = response.UserId;
stateFunctions.CustomerContactID = response.CustomerContactID;
let obj = {
access_token: response.access_token,
token_type: response.token_type,
expires_in: response.expires_in,
UserId: response.UserId,
CustomerContactID: response.CustomerContactID,
Mobile: response.Mobile,
StatusCode: response.StatusCode
}
AsyncStorage.setItem('logindetail', JSON.stringify(obj));
if (requestArray.length != 0) {
requestArray.forEach(x => {
try {
console.log(x, "request Url");
x.headers.Authorization = `Bearer ${response.access_token}`;
x.headers["Content-Type"] = "application/x-www-form-urlencoded";
APIKit.defaults.headers.common["Authorization"] = `Bearer${response.access_token}`;
APIKit(x)
} catch (e) {
console.log(e)
}
});
}
return APIKit(originalRequest);
}
})
.catch(err => {
console.log(err);
});
}
}
return Promise.reject(error);
}
);
export default APIKit;
Home.js
gettingToken = async () => {
let userToken = await AsyncStorage.getItem('userToken');
userToken = JSON.parse(userToken);
await setClientToken(userToken);
}

Send a POST request via Axios to a Firebase Cloud Function

I try to send a simple request to a Firebase function, but I get the same error every time... Apparently, the Firebase function does not receive the data I want to transmit from the Axios request.
This is the Firebase function :
[...] // Some imports
exports.completeProfile = functions.https.onRequest((req, res) => {
// Debug
console.log(req);
console.log(req.body);
console.log(req.method);
console.log("Test: " + userId + ", " + profilePicture + ", " + username);
// We recover the data
const userId = req.body.userId; // return "undefined"
const profilePicture = req.body.profilePicture; // return "undefined"
const username = req.body.username; // return "undefined"
// we're checking to see if they've been transferred
if (!userId || !profilePicture || !username) {
// At least one of the 3 required data is not completed
console.error("Error level 1: missing data");
return res.status(400).send("Error: missing data");
}
[...] // (We have all the data, we continue the function)
});
And here is my Axios request :
axios
.post(
'<FIREBASE CLOUD FUNCTION URL>',
{
userId: '12345667',
profilePicture: 'https://profilepicture.com/url',
username: 'test',
}
)
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
});
When I run the Axios query, I always come across the "Network Error" error. Here is what console.log(error); gives :
And here are the server logs:
How to solve the problem? Thanks for your help.
change your firebase code to this
var cors = require("cors");
completeProfileFn = (req, res) => {
// Debug
console.log(req);
console.log(req.body);
console.log(req.method);
console.log("Test: " + userId + ", " + profilePicture + ", " + username);
// We recover the data
const userId = req.body.userId; // return "undefined"
const profilePicture = req.body.profilePicture; // return "undefined"
const username = req.body.username; // return "undefined"
// we're checking to see if they've been transferred
if (!userId || !profilePicture || !username) {
// At least one of the 3 required data is not completed
console.error("Error level 1: missing data");
return res.status(400).send("Error: missing data");
}
// (We have all the data, we continue the function)
};
// CORS and Cloud Functions export logic
exports.completeProfile = functions.https.onRequest((req, res) => {
var corsFn = cors();
corsFn(req, res, function() {
completeProfileFn(req, res);
});
});
It is a CORS issue.

Cloud Functions for Firebase running into infinite loop

I have a custom logic to verify the users.
I have written a Cloud Function for Firebase and to verify the custom tokens.
The problem is the cloud function is not getting terminated and is being run into infinite loop, till Firebase kills the function
The cloud function runs into infinite in both matching and non-matching scenario.
Below is the code:
/* CLOUD FUNCTION */
exports.verifyToken = functions.https.onRequest((req, res) => {
var corsFn = cors();
corsFn(req, res, function () {
verifyTheUserToken(req, res);
});
});
function verifyTheUserToken(req, res) {
if (!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) {
console.error('No Firebase ID token was passed as a Bearer token in the Authorization header.');
res.status(403).send('Unauthorized');
}
const firebaseToken = req.headers.authorization.split('Bearer ')[1];
const userId = req.body.uid;
const receievedToken = req.body.token;
return admin.auth().verifyIdToken(firebaseToken).then(decodedFirebaseToken => {
console.log('ID Token correctly decoded', decodedFirebaseToken);
console.log('req', req.body);
return 'sucess';
}).then(function (receivedValues) {
return admin.database().ref().child('userTokens').child(userId).child('token').once('value');
}).then(function (snapshot) {
if (!snapshot.val()) {
return Promise.reject('token is not set ');
}
if (snapshot.val() != receievedToken) {
return Promise.reject('token doesnt match');
}
return 'verified';
}).then(function (success) {
return admin.database().ref().child('users').child(userId).child('isVerified').set(true);
}).then(function (success) {
console.log('The user is verified');
return;
}).catch(function (error) {
console.log('Error', error);
return;
});
}
Client side I am doing a HTTP request to call the firebase cloud function.
/* CLIENT SIDE */
var currentUser = firebase.auth().currentUser.uid;
var firebaseUserToken = firebase.auth().currentUser.getToken();
firebase.auth().currentUser.getToken(/* forceRefresh */ true).then(function (firebaseUserToken) {
fetch('https://us-central1-MYAPP.cloudfunctions.net/verifyToken', {
'method': 'POST',
'headers': {
'Authorization': 'Bearer ' + firebaseUserToken,
'Content-Type': 'application/json'
},
'body': JSON.stringify({
'uid': currentUser,
'token': 1234,
})
}).then(function (response) {
console.log('successful response');
}).catch(function (error) {
console.error('Error in fetch', error);
});
}).catch(function (error) {
console.error('Error in getting firebase token', error);
});
I am unable to figure out the reason for the infinite loop.
I would really appreciate any help on this.
Thanks!
I had missed res.send() for the success case.
As per documentation:
Always end an HTTP function with send(), redirect(), or end(). Otherwise, your function might to continue to run and be forcibly terminated by the system.
https://firebase.google.com/docs/functions/http-events

JS convert string to rfc822

I am trying the gmail apis. I've done the auth. Now I want to create a draft. But I am getting this error
{ error:
I20161220-15:53:43.486(4)? { errors: [Object],
I20161220-15:53:43.487(4)? code: 400,
I20161220-15:53:43.488(4)? message: 'Media type \'application/octet-stream\' is not supported. Valid media types: [message/rfc822]' } } }
Gmail api require base64 string with rfc822 standard. I am not sure of any good way to convert a string to rfc822. How do I do that?
I am using meteor for my app and here is my code.
import { Meteor } from 'meteor/meteor'
import { HTTP } from 'meteor/http'
Meteor.startup(() => {
// Meteor.call('createDraft')
Meteor.methods({
'createDraft': function () {
console.log(this.userId)
const user = Meteor.users.findOne(this.userId)
const email = user.services.google.email
console.log(email)
const token = user.services.google.accessToken
const dataObject = {
message: {
raw: CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse('dddd'))
},
headers: {
Authorization: `Bearer ${token}`
}
}
HTTP.post(`https://www.googleapis.com/upload/gmail/v1/users/${email}/drafts`, dataObject, (error, result) => {
if (error) {
console.log('err', error)
}
if (result) {
console.log('res', result)
}
})
}
})
})
Base64 encode the message and replace all + with -, replace all / with _, and remove the trailing = to make it URL-safe:
const rawMessage = btoa(
"From: sender#gmail.com\r\n" +
"To: receiver#gmail.com\r\n" +
"Subject: Subject Text\r\n\r\n" +
"The message text goes here"
).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
const dataObject = {
message: {
raw: rawMessage
},
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
};
I just needed to send content type as message/rfc822. Here is the working code. Note that the raw message has something wrong in ts because the draft that is created has empty content. But the draft itself is created successfully.
import { Meteor } from 'meteor/meteor'
import { HTTP } from 'meteor/http'
Meteor.startup(() => {
// Meteor.call('createDraft')
Meteor.methods({
'createDraft': function () {
console.log(this.userId)
// CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse('dddd'))
const user = Meteor.users.findOne(this.userId)
const email = user.services.google.email
console.log(email)
const token = user.services.google.accessToken
const rawMessage = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(
'From: sender#gmail.com\r\n' +
'To: receiver#gmail.com\r\n' +
'Subject: Subject Text\r\n\r\n' +
'The message text goes here'
)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
const dataObject = {
message: {
raw: rawMessage
},
headers: {
'Content-Type': 'message/rfc822',
Authorization: `Bearer ${token}`
}
}
HTTP.post(`https://www.googleapis.com/upload/gmail/v1/users/${email}/drafts`, dataObject, (error, result) => {
if (error) {
console.log('err', error)
}
if (result) {
console.log('res', result)
}
})
}
})
})

Categories

Resources