Stop execution after writing to text file with fs.writeFile - javascript

I have the following Node.JS (ran with Express) code :
let app = express();
app.use(cors());
app.get('/callback', function (req, res) {
// your application requests refresh and access tokens
// after checking the state parameter
var code = req.query.code || null;
var authOptions = {
url: 'https://accounts.spotify.com/api/token',
form: {
code: code,
redirect_uri: redirectUri,
grant_type: 'authorization_code'
},
headers: {
'Authorization': 'Basic ' + (new Buffer(clientId + ':' + clientSecret).toString('base64'))
},
json: true
};
request.post(authOptions, function (error, response, body) {
if (!error && response.statusCode === 200) {
var access_token = body.access_token,
refresh_token = body.refresh_token;
fs.writeFile('test.txt', 'HELLO', function (err) {
if (err) return console.log(err);
console.log('Hello World > helloworld.txt');
});
}
}
)
});
console.log('Listening on 8888');
app.listen(8888);
The route is used as a callback for a request to the Spotify Web API, thus I can get an access token.
Spotify then redirects to the callback function above, you can see it in the URI by looking at "redirect_uri".
If you need more information about the Spotify Authorization Flow, see here.
Here's the URI I'm using to authenticate my app to Spotify.
https://accounts.spotify.com/authorize?client_id=CLIENT_ID&response_type=code&redirect_uri=http://localhost:8888/callback&scope=user-read-private%20user-read-email%20playlist-modify-public&state=PexBrjEzISHepTp7&show_dialog=false
CLIENT_ID is replaced by my real CLIENT_ID in the request I make
My problem is located to the file writing part :
fs.writeFile('test.txt', 'HELLO', function (err) {
if (err) return console.log(err);
console.log('Hello World > helloworld.txt');
});
When the callback route is called by Spotify, I have the string "HELLO" wrote in my text file, so the file writing is functional.
But even if it has finished writing the string, the Chrome page is still running and "pending" on the server. It runs for a few minutes and then crash by saying that the page didn't sent any data. Why ?
I've looked at this page talking about the methods of writing to text files, using writeFile and writeFileAsync, but using both of them didn't solved my problem.
EDIT: I don't really want to stop the Express process! I just want to be able to process another request :)
Any idea ? Thanks in advance :)

You aren't returning anything from your route, try adding res.send({})

In your get route you are not sending response, you must send response irrespective of writing a file was successful or not.
Add below code post writing to file (as well as in if error case)
res.send({YOUR_CHOICE_RESPONSE_DATA})

Related

GET Request repeatedly failed on the front end but not on backend

I'm working on a personal project that will allow users to find new books based on their preferences for the genre. The database I'm using is MongoDB. However, while I'm able to get all the data on the backend using Postman, I can't get it properly displayed on the frontend. At the moment, I'm just trying to get the data sent to the front end and at least console.log'd but it isn't making it that far.
Here is the code in the routes file.
router.get('/books/:genre', bookBuilder.get_some_books)
Here's the code on the backend that the routes file is pointing to and is working:
exports.get_some_books = async function (req, res) {
let { genre } = req.params;
try {
let books = await Book.find({"genre": genre});
if (books) {
res.json(books)
} else {
res.status(404).send({error: 'Not Found'});
}
} catch (err) {
res.status(500).send({error: err.message});
}
}
Here's my code on the frontend that is not working.
async getEverything() {
try {
let pbBooks = await axios.get(`/books/`, {
method: 'GET',
headers: {'Content-Type': 'application/json'},
params: {
genre: 'PB'
}
})
if (pbBooks) {
console.log(pbBooks)
} else {
this.$router.push('/Error');
}
} catch (err) {
console.log(`Network error: ${err.message}`)
}
}
My code stack is Vue.js, Express.js, Node.js and Axios. On the frontend, I've tried making the inner code of axios.get() into '/books/PB' and then tried getEverything(genre) along with /books/${genre} but neither seems to be working.
The error I am getting is a 404 Request Failed error that is from the catch block in the getEverything() function. I'm not sure why the frontend is unable to get the data when the backend works just fine. Is there anything I'm doing wrong?
404 is the HTTP status code for Not found, which implies there is no route setup on localhost for /books. Actually, /books would route to your app, not the backend (unless you have a proxy setup on your app server that redirects to the backend).
If a proxy were involved, it's likely misconfigured. Otherwise, the target URL in the Axios request should be <backend_url>/books (e.g., http://localhost:9999/books with the back running locally on port 9999, and the app on some other port).
Change
let pbBooks = await axios.get(`/books/`, {
...
to
let genre = "PB"
let pbBooks = await axios.get(`/books/${genre}`, {
method: 'GET',
headers: {'Content-Type': 'application/json'}
})
reason is the params part of the config object is converted to query strings (/books?genre=PB) instead of /books/PB, which is what the backend is expecting
More: https://masteringjs.io/tutorials/axios/get-query-params

make http get call after sigin in with jasmine and request

I'm adding tests to an application that already (partially) exists. It was written using angular and php/MariaDB in the backend. I'm now working on the http calls to the server. I plan to use jasmine with request.
I was able to make some simple tests, and can login. But I cannot test the pages that require to be logged in. I cannot find a way to add the token to the calls.
If I understand things correctly, on the received message from the sig in I should get a token that I should then use in the following calls. Who wrote the app followed the instructions given by the angular documentation which handles everything, so we are learning toguether how things really work under the hood.
Going through the received answer on the login, the only thing that looks like a token is a cookie set in the header, whose name is 'PHPSESSID'. I read and parse that cookie to get the token and make the next call like this:
request.get(
{
url: 'http://xxx.xxx.com/php/authentication/session.php',
'auth': {
'bearer': mytoken
}
}, function(err, res) {
console.log(res['body']);
done();
})
the response is what I should get if the user is NOT logged in.
Using Postman, everything works. Aparently it saves the token and uses it for the next call. That is, f I make the sign in and then make the get call to session.php I get the correct answer. I just cannot figure out what exact call postman makes or how to use the token in the next call using jasmine and request.
Since the token was passed as a cookie on the response call, what I was supposed to do was set the same cookie in the next call. Here is the code of the whole test just in case somebody needs a hand. First I sign in, then I made a call to an address that should return my email if indeed I'm logged in.
var request = require('request');
describe("login test", function() {
it("should log in", (done) => {
var user = {'email':'test#xxx.de', 'password':'blablabla'};
request.post(
{
url: 'http://xxx/test/public/php/authentication/login.php',
body: JSON.stringify(user)
},
(err, res) => {
var result = JSON.parse(res['body']);
var cookieString = res['headers']['set-cookie'];
expect(result['success']).toBe(true);
request.get(
{
url: 'http://xxx/test/public/php/authentication/session.php',
headers: {
'Cookie': cookieString
}
}, function(err, res) {
var result = JSON.parse(res['body']);
expect(result.user.email).toBe(user.email);
done();
})
});
});
});

Using Node request - How to authenticate on challenge based web page?

Exploring the 'request' package to perform some automated tasks at the office.
The requirement is to automatically log into a server (but there are many many), navigate to a web page and then collect information then create a report with it.
Thing is, these servers first ask for a user account, then they generate a challenge number that one would copy and paste into another server to be resolved as the challenge response. This response is pasted back into the web page I want the script to log in.
Trying to figure out how to go about doing this with 'request'. Following code only gets as far as looping through the page where to enter the user name:
"use strict"
var request = require('request');
var getJar = request.jar();
var opts = {
method: 'GET',
uri: 'https://serverIP/path/to/post/action',
agentOptions: {
rejectUnauthorized: false
},
jar: getJar,
followAllRedirects: true
};
var postJar = request.jar();
var postOpts = {
method: 'POST',
uri: 'https://serverIP/path/to/post/action',
form: {
userName: 'init'
},
agentOptions: {
rejectUnauthorized: false
},
postJar: postJar,
followAllRedirects: true,
headers: {
'Referer': 'value I see in dev console'
}
};
request(opts, function (err, res, body) {
console.log('GET error is: ' + JSON.stringify(err));
console.log('GET response is: ' + res);
console.log('GET body is: ' + body);
var cookie_string = getJar.getCookieString(opts.uri);
var cookies = getJar.getCookies(opts.uri);
console.log(JSON.stringify(cookies) + '\n' + cookie_string);
postOpts.postJar.setCookie(cookie_string, postOpts.uri);
request(postOpts, function (err, res, body) {
console.log('POST error is: ' + JSON.stringify(err));
console.log('POST response is: ' + res);
console.log('POST body is: ' + body);
});
});
The form parameters in the snippet are things I see in Firefox's developer console under Network > POST request > Params when I submit the user name through the browser.
The code is only taking me back to the page where the user name is requested. It kind of gets stuck there. I know this by logging the value of the POST body to console, it spits out the HTML code asking for that value, instead of the HTML where you see the challenge string and the input field where to paste the challenge response.
What may I do differently?
I used 'request-debug' package for further insight. Turned out, the first GET request happened successfully, from which I take the cookie=sessionId value, and inject it into the POST message jar as shown in my initial snippet.
With the debug on, I was able to detect a redirection to a URL saying 'cookiesRequired' at the end, request was following this redirection and generating a second GET to it, where I was shown HTML code with the message 'Invalid session you must be logged in'.
It then hit me that, the cookie-session system was not working properly. I saw from the second GET that it inserted a header with the session id taken from the cookie. So I injected this header into postJar like this:
var cookie_string = getJar.getCookieString(opts.uri);
console.log('ATTENTION--- The cookie from getJar is:\t' + cookie_string);
postOpts.headers.cookie = cookie_string;
This line doesnt even affect the behavior so far:
postOpts.jar.setCookie(cookie_string, '/');
After inserting the cookie header I am now getting prompted to resolve the next phase of the authentication.

Google Token Authentication with node.js and express.js

I have a node.js/express webservice that I would like to use to verify Google token coming from a mobile app.
From this tutorial (https://developers.google.com/identity/sign-in/android/backend-auth) I learned that I have to make such a call:
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=XYZ123
So I came up with this in my code:
var request = require('request');
module.exports = function(app) {
app.get('/authenticate', function(req, res) {
request('https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=XYZ123', function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body)
}
})
});
};
Then, based on the response from google api, I would like to return true or false.
But it doesn't work. When I put "https://www.google.com" in the request, body is printed in the console, but the request is still being executed for some time.
What am I missing here? Is is the correct approach to the problem I described or should I do it in a completely different way?
You need to send a response to your incoming HTTP request.
Call res.send(...).
For more information, see the Express documentation.

ajax get to 3rd party server within express middleware results in 500 error

I have a requirement to access multiple devices over IP from an HTML5 web app.
My approach to working around the cross-domain impossibility of doing this all on the client side has been to "cook" requests from the client inside of express' middleware. A route receives a get or post from the client, and then performs a get or post to the 3rd party device identified by the payload from the client.
I'm using the code to get info from a device. It works just fine when I run it directly from file inside of a client I made for testing purposes. Running directly from file avoids the CORS difficulty because the client is also the server I guess.
When I run the same code from within an express route, I get a 500 error.
Am I trying to do something impossible? I'm only about a week into node, express etc so hopefully it's something dumb and easy to solve. I'm taking fact that I haven't been able to find any other questions quite like this as an indication that there's a proper way to achieve what I need.
// post to find a camera
router.post('/find', function(req, res) {
var url = 'http://' + req.body.addr + '/cgi-bin/aw_cam?cmd=QID&res=1';
console.log(url);
$.ajax({
type: 'GET',
url: url,
dataType: 'html',
success: function (result) {
console.log('success: ' + result);
res.send(result);
},
error: function (xhr, textStatus, err) {
console.log('error: ' + textStatus);
}
});
});
Here's what logged to the server console:
http://192.168.0.10/cgi-bin/aw_cam?cmd=QID&res=1
POST /cameras/find 500 126.593 ms - 1656
Thanks in advance!
ok I found how to do this. The trick is to use Node's built-in http messaging capabilities. I found a good article on how to do this here
The code below does exactly what I wanted from within my custom route middleware. I guess I just learned that I can only use AJAX how I wanted on the client side.
This lets me abstract the hairier details of the device control protocol into the server, leaving my client apps to use JSON/AJAX model to interact with them. Success!
var http = require('http');
// post to find a camera
router.post('/find', function(req, res) {
var url = 'http://' + req.body.addr + '/cgi-bin/aw_cam?cmd=QID&res=1';
console.log(url);
http.get(url, (response) => {
console.log(`Got response: ${response.statusCode}`);
var body = '';
response.on ('data', function(d) {
body += d;
});
response.on ('end', function () {
console.log('received: ' + body);
var reply = {};
if (body.indexOf('OID:') == 0) {
reply.msg = body.slice(4);
reply.ok = true;
} else {
reply.msg = body;
reply.ok = false;
}
res.send(reply);
});
// consume response body
response.resume();
}).on('error', (e) => {
console.log(`Got error: ${e.message}`);
});
});

Categories

Resources