I am trying to get my code to handle a username already existing. I know that passport does that automatically with a console.log(err) however, I would like to flash the user a message if that happens, so I needed to add a bit more code to do it. It seems to work, however, I am getting a lot of console errors when I implement my code this way and I would like to avoid any errors in the future.
Here is my code:
router.post("/", isLoggedIn, isAdministrator, async function(req, res){
let users = [];
let newUser = new User({
username: req.body.user["username"],
first_name: req.body.user["first_name"],
middle_name: req.body.user["middle_name"],
last_name: req.body.user["last_name"],
email_address: req.body.user["email_address"],
phone_number: req.body.user["phone_number"],
street: req.body.user["street"],
city: req.body.user["city"],
state: req.body.user["state"],
zip: req.body.user["zip"],
user_permissions: req.body.user["user_permissions"],
});
try {
users = await User.find({});
}
catch (err) {console.log(err);}
users.forEach(function(user) {
if (user.username == newUser.username){
// flash username already exists
console.log("User Already Exists")
return res.redirect("/users/add");
}
});
// If username does not exist
try {
await User.register(newUser, req.body.user["password"]);
}
catch (err) {console.log(err);}
res.redirect("/users");
});
And here is what the console is logging
User Already Exists
ctor [UserExistsError]: A user with the given username is already registered
at ...\node_modules\passport-local-mongoose\index.js:237:17
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async ...\routes\users.js:93:3
(node:4780) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:518:11)
at ServerResponse.header (...\node_modules\express\lib\response.js:771:10)
at ServerResponse.location (...\node_modules\express\lib\response.js:888:15)
at ServerResponse.redirect (...\node_modules\express\lib\response.js:926:18)
at ...\routes\users.js:97:6
at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:4780) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:4780) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Like I said, the logic of this code still works fine, but I am concerned about the promise rejection part of the code. It seems that it is still trying to handle the last res.redirect("/users") in the last line, despite me already returning a res.redirect earlier. Is there a reason for this, and should I be concerned about it? Is there a better way of implementing a flash if the username already exists using passport, without my separate forEach loop? Thanks.
This not an answer to your problem but I feel obliged to mention this:
users.forEach(function(user) {
if (user.username == newUser.username){
// flash username already exists
console.log("User Already Exists")
return res.redirect("/users/add");
}
});
Instead of doing the above, why don't you just have the DB system do that for you instead of iterating through a list of users in memory?
Like so:
users = await User.find({ username: { $eq: newUser.username }});
Related
I have a problem with my POST. I'm trying to send a request with Postman Postman screen but I have an error in the terminal.
My code:
exports.createSauce = (req, res, next) => {
const sauce = new Sauce({
...req.body
})
sauce.save()
.then(res.status(201).json({ message : "registered object !" }))
.catch(error => res.status(400).json({ error }))
}
My error message :
(node:2808) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:481:11)
at ServerResponse.header (C:\Users\Admin\Desktop\Workspace\P6_saumureau_thibaud\node_modules\express\lib\response.js:771:10)
at ServerResponse.send (C:\Users\Admin\Desktop\Workspace\P6_saumureau_thibaud\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (C:\Users\Admin\Desktop\Workspace\P6_saumureau_thibaud\node_modules\express\lib\response.js:267:15)
at sauce.save.then.catch.error (C:\Users\Admin\Desktop\Workspace\P6_saumureau_thibaud\controllers\sauce.js:9:37)
at process._tickCallback (internal/process/next_tick.js:178:7)
(node:2808) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:2808) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Someone have a solution ?
Thanks in advance :)
The line .then(res.status(201).json({ message : "registered object !" })) calls res.status(201).json({ message : "registered object !" }) imediatly and uses the result of that call as parameter passed to then.
So your res.status(201).json({ message : "registered object !" }) will always be executed no matter if save was successful or not and will be called before the save happened.
According to the error message your save fails, so you first call res.status(201).json({ message : "registered object !" }) which sends header and body to the client, and then you call res.status(400).json({ error }) which trys to send headers again, but the request was already send.
You have to convert the part in the then to a callback using e.g. an arrow function: .then(() => res.status(201).json({ message : "registered object !" }))
I'm trying to compress and decompress the contents of a HTML file using zlib, to be able to send it over a post request to my database. I've got this code here, for example.
const { deflate, unzip } = require('zlib');
const input = '.................................';
deflate(input, (err, buffer) => {
if (err) {
console.error('An error occurred:', err);
process.exitCode = 1;
}
console.log(buffer.toString('base64'));
});
const buffer = Buffer.from('eJzT0yMAAGTvBe8=', 'base64');
unzip(buffer, (err, buffer) => {
if (err) {
console.error('An error occurred:', err);
process.exitCode = 1;
}
console.log(buffer.toString());
});
This is from NodeJS's official website, and it seems to work. It can compress and decompress the variable input. When I try to use this with my own variable, containing the HTML, deflating it worked; however, inflating it back into a string throws the error
(node:1353) UnhandledPromiseRejectionWarning: Error: invalid distance too far back
at Zlib.zlibOnError [as onerror] (zlib.js:182:17)
at processChunkSync (zlib.js:431:12)
at zlibBufferSync (zlib.js:168:12)
at Object.syncBufferWrapper [as unzipSync] (zlib.js:766:14)
at inflateString (/home/runner/woc-bot/functions.js:62:36)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:1353) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:1353) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
I think this means that the encoded string that my database returns is corrupted, so I tried to just send a simple encoded string to the database, fetch it back, and then inflate it again.
const { deflate} = require('zlib');
const input = 'Hellow worldw';
deflate(input, (err, buffer) => {
if (err) {
console.error('An error occurred:', err);
process.exitCode = 1;
}
console.log(buffer.toString('base64'));
});
I get eJzzSM3JyS9XKM8vykkpBwAjDAUr, so I send this to the database, and get it back. I unzip it, and it returns hellow worldw
I'm really not sure what's happening, I'm using Repl.it's database (plz dont judge me) and this has been killing me for the past two days.
I'm trying to implement a google sign-in for a server-side NodeJS application that needs googledrive access, and can't get the 'googleapis' google object to exchange the authorization code for an actual access token.
There's google documentation for what I want to do here, but step 7, which is where I'm stuck, is described in Java and Python, and so the NodeJS google object isn't present. I've also followed the nodeJS quickstart here, but many of the steps for authentication are different. Namely, I can't make a google.auth.OAuth2 object the same way because the credentials for my application differ in format from the example's (they don't have redirect uris)
This is where I've got it:
const fs = require('fs');
const { google } = require('googleapis');
formAuthClient('<PastedAuthCodeFromFrontEnd>');
async function formAuthClient(code) {
// reads credentials from file
const credentials = await readFile('./client_secret.json').then(JSON.parse);
// cannot read redirect_uris like in NodeJS quickstart because these do not exist.
// Also, no credentials.installed, it's credentials.web
const { client_secret, client_id } = credentials.web;
const oAuth2Client = new google.auth.OAuth2( // form authObject
client_id, client_secret
);
console.log(oAuth2Client);
const token = await oAuth2ClientGetToken(oAuth2Client, code).catch(console.err);
oAuth2Client.setCredentials(token);
return oAuth2Client;
}
// just an async wrapper for fs.readFile
function readFile(file) {
return new Promise((resolve, reject) => {
fs.readFile(file, (err, content) => {
if (err) reject(err);
resolve(content);
});
})
}
// just an async wrapper for oAuth2Client.getToken
function oAuth2ClientGetToken(oAuth2Client, code) {
return new Promise((resolve, reject) => {
oAuth2Client.getToken(code, (err, token) => { // errors out here
if (err) reject(err);
resolve(token);
});
});
}
Running this code first prints the oAuth2Client object, and then gives me the following error:
OAuth2Client {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
transporter: DefaultTransporter {},
credentials: {},
certificateCache: {},
certificateExpiry: null,
certificateCacheFormat: 'PEM',
refreshTokenPromises: Map {},
_clientId: '<probablySecretClientID>.apps.googleusercontent.com',
_clientSecret: '<defSecretClientSecret>',
redirectUri: undefined,
eagerRefreshThresholdMillis: 300000
}
(node:16186) UnhandledPromiseRejectionWarning: Error: invalid_grant
at Gaxios.request (/home/miko/node/clouds/api/node_modules/gaxios/build/src/gaxios.js:70:23)
at processTicksAndRejections (internal/process/task_queues.js:89:5)
at async OAuth2Client.getTokenAsync (/home/miko/node/clouds/api/node_modules/google-auth-library/build/src/auth/oauth2client.js:119:21)
(node:16186) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:16186) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code
Ideally, I'd want the top function to return a fully authenticated client object that I can later use to access google drive apis
I came up with a solution here https://github.com/googleapis/google-api-nodejs-client/issues/1951
For all thows that come after me - Resolution
Prerequisites
If you have the same setup as me where your performing the exchange for to token not in the frontend browser react code but in a back end setup. I did the following things which seems to resolve the issue.
1> Add the secondary hostname/port in the Google Cloud platform > APIs& Services > Credentials> (your oauth ClientID.) > Authorised JavaScript origins. with your web apps URI.
for example, if you have localhost:3000 serving your react code and localhost:3002. You need to ad both. This applies to your production setup as well.
2> The authorization code is one time use only. Once you use it, if your did not store the refresh token/access token. You need to ask the user to auth again. It gives the precise same message.
3> There seems to be a time out on how long you can hold the Authorization token. Google can answer this better than me, however I noticed I got the same error if I stored a working legitimate Authorization token for more than an hour and it gave me the same message as above.
My advise is send back better erroring than data: { error: 'invalid_grant', error_description: 'Bad Request' },
Since all the above scenarios seem to have caused the same error. It was hard for me to debug.
I played around with a package called instagram-profile-picture.
This is the code i used, straight from the npm website examples:
const ipp = require('instagram-profile-picture');
ipp('9gag').then(user => {
console.log(user);
// => https://scontent-sit4-1.cdninstagram.com/7...jpg
});
This used to work a couple days ago that i tested it.
Now, suddenly i get this error:
(node:1820) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'u
rl' of undefined
at got.then.res (C:\Users\User1\Desktop\testing npm\node_modules\insta
gram-profile-picture\index.js:15:49)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
(node:1820) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This
error originated either by throwing inside of an async function without a catch
block, or by rejecting a promise which was not handled with .catch(). (rejection
id: 1)
(node:1820) [DEP0018] DeprecationWarning: Unhandled promise rejections are depre
cated. In the future, promise rejections that are not handled will terminate the
Node.js process with a non-zero exit code.
It is very weird because i changed nothing in the code.
So i tried a new installation
npm init -> npm install instagram-profile-picture
then the same code i posted previously and i still get the same error straight from the npm example.
So, the problem is because you are not logged in. Basically instagram changed the query API where if you have active session(logged in), then it will show more information, if not then it will not.
So, for 9gag if logged in:(just pasting relevant part)
{
"user": {
"pk": 259220806,
"hd_profile_pic_url_info": {
"url": "https://instagram.fbom20-1.fna.fbcdn.net/vp/777f85cb149a23d10da15f4af19ef407/5DE89E04/t51.2885-19/18645376_238828349933616_4925847981183205376_a.jpg?_nc_ht=instagram.fbom20-1.fna.fbcdn.net",
"width": 512,
"height": 512
}
},
"status": "ok"
}
But if you are logged out:
{
"user": {
"username": "9gag",
"profile_pic_url": "https://instagram.fbom20-1.fna.fbcdn.net/vp/c91395418170cbb196a69ac9dea359a4/5DD372FE/t51.2885-19/s150x150/18645376_238828349933616_4925847981183205376_a.jpg?_nc_ht=instagram.fbom20-1.fna.fbcdn.net"
},
"status": "ok"
}
But the library needs user.hd_profile_pic_url_info.url which is undefined.
Is the library going to work with teaks?
No unfortunately the code is quite straightforward in that library just fetching urls, you need to find one where you are providing some kind of authentication.
You can check out node-instagram,
EDIT2: The endpoint called from the library is https://i.instagram.com/api/v1/users/${userid}/info/ maybe a library out there will support this api. Or you can manually authenticate yourself using the apis and hit this url yourself.
You don't need a whole npm module for this. The api is pretty simple.
const https = require('https');
function getUserDetails(username) {
return new Promise(done => {
var data = [];
https.get(`https://www.instagram.com/${username}/?__a=1`, resp => {
resp.on('data', chunk => data.push(chunk));
resp.on('end', () => {
var json = JSON.parse(data.join(''));
done(json.graphql.user);
});
});
});
}
getUserDetails('9gag').then(user=>{
var bio = user.biography;
var full_name = user.full_name;
var profile_pic = user.profile_pic_url;
console.log(bio);
console.log(full_name)
console.log(profile_pic);
});
I've been working on a new application that uses PostgreSQL and Knexjs as a query builder, but have been running into an issue I'm not sure how to handle.
I have a route handler that looks like so:
export const getSingleUser = async(req, res) => {
let respObj = {
status: 'fail',
message: 'User does not exist'
};
try {
const user = await knex('users').where('id', req.params.id).first();
if (!user) {
res.send(respObj);
}
respObj = {
status: 'success',
data: user
};
res.send(respObj);
} catch(e) {
res.send(respObj);
}
};
It works great, until I throw a non-existent user ID into the mix. I assumed the catch statement would handle the error if no user is found for the query, but that doesn't seem to work, it just spits out the respObj in the try block. So I added an if statement to check if the user object doesn't exist, and thats when I received the warning below:
(node:25711) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at validateHeader (_http_outgoing.js:503:11)
at ServerResponse.setHeader (_http_outgoing.js:510:3)
at ServerResponse.header (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/node_modules/express/lib/response.js:767:10)
at ServerResponse.send (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/node_modules/express/lib/response.js:170:12)
at ServerResponse.json (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/node_modules/express/lib/response.js:267:15)
at ServerResponse.send (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/node_modules/express/lib/response.js:158:21)
at _callee3$ (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/src/controllers/userController.js:45:7)
at tryCatch (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/node_modules/regenerator-runtime/runtime.js:65:40)
at Generator.invoke [as _invoke] (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/node_modules/regenerator-runtime/runtime.js:303:22)
at Generator.prototype.(anonymous function) [as next] (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/node_modules/regenerator-runtime/runtime.js:117:21)
at step (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/src/controllers/userController.js:14:191)
at /Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/src/controllers/userController.js:14:361
at <anonymous>
(node:25711) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:25711) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Can anyone provide more info on why this is happening, and whats the fix?
Work around
A response is sent twice in the try block if the user does not exist. This accounts for the error raised:
"Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client".
The unhandled promise rejection warning is being raised because if the res.send() call in the catch code block throws, the promise returned from calling getSingleUser gets rejected with the same error - implying there is no error handling in place for the returned promise (because isn't supposed to get rejected).
Directing the "user does not exist" case to the catch block, by throwing an error, could be a work around to avoid the issue in the first place. A cut-down example:
export const getSingleUser = async(req, res) => {
try {
const user = await knex('users').where('id', req.params.id).first();
if (!user) {
throw new Error(" user does not exist");
}
res.send( {
status: 'success',
data: user
});
} catch(e) {
// console.log(e); // debug if needed
res.send( {
status: 'fail',
message: 'User does not exist'
});
}
};