Retrieving data from server using Node/Express/Ajax/EJS - node.js

I'm creating a simple node.js application with the following aims:
Logs the user in using passport-steam, this is done to have a way of automatically collecting the user's steamID and other useful information like their profile pic etc.
Use this steamID to make two more API calls to retrieve their achievements for a certain game, and the game schema which has the images for these achievements.
I can successfully log the user in and use EJS to display some of their data by simply using
res.render('index', { user: req.user});
but I'm stuck on how to achieve the other two API calls. Here are my endpoints for the achievements and data/images:
app.get("/achievements", function (req, res) {
axios.get('https://api.steampowered.com/ISteamUserStats/GetPlayerAchievements/v0001/?appid=250900&key='KEY'&steamid='+'ID')
.then(result => {
res.send(result.data['playerstats']['achievements']);
})
.catch(error => {
console.log(error);
});
});
app.get("/data", function (req, res) {
console.log("testing");
axios.get('http://api.steampowered.com/ISteamUserStats/GetSchemaForGame/v0002/?key='KEY'&appid=250900&l=english&format=json')
.then(result => {
res.send(result.data);
})
.catch(error => {
console.log(error);
});
});
And here's my relevant index.ejs:
<form id="testform">
<input id="steamid" type="text"/>
<button type="submit">Submit</button>
</form>
<% if (!user) { %>
<h2>Welcome! Please log in.</h2>
<p>Sign in</p>
<% } else { %>
<h2>Hello, <%= user.displayName %>. - <a href='logout'>Logout</a></h2>
<p><img src='<%= user.photos[2].value %>' alt='Your Avatar Image' /></p>
<% } %>
</body>
I've attempted/considered a few methods. I thought I could, like req.user, add more properties to the request object. Another consideration was using sessions to store the information. My final idea was simply create two more separate get requests for the information. But ideally, I want all this to happen on 'login' button press, so when the user is redirected back they have all this information displayed immediately.

Related

How do I make logged in webpage (dashboard) visible only to one who logs in?

Currently, my code works with discord oauth2 to access user's information.
After getting it is pushed to my website /dashboard
But the problem is it won't just happen to user.
The user's information is visible to everyone if they go directly to /dashboard.
Currently using NodeJS for all purposes.
Part of code associated:
app.get('/login',async(req,res)=>{
const code=req.query.code;
const params = new URLSearchParams();
let user;
params.append('client_id', "redacted");
params.append('client_secret', "redacted");
params.append('grant_type', 'authorization_code');
params.append('code', code);
params.append('redirect_uri', "redacted");
try{
const response=await axios.post('https://discord.com/api/oauth2/token',params)
const { access_token,token_type}=response.data;
const userDataResponse=await axios.get('https://discord.com/api/users/#me',{
headers:{
authorization: `${token_type} ${access_token}`
}
})
console.log('Data: ',userDataResponse.data)
module.exports = user={
username:userDataResponse.data.username,
email:userDataResponse.data.email,
avatar:`https://cdn.discordapp.com/avatars/${userDataResponse.data.id}/${userDataResponse.data.avatar}`
}
res.send(`
// Redirect html to dashboard here
`)
}catch(error){
console.log('Error',error)
return res.send('Some error occurred! ')
}
app.get('/dashboard',(req,res)=>{
res.send(`
<div class="container p-5 text-center">
<h3 style="color: azure">Login Success</h3>
<img src="${user.avatar}"/>
<h3 style="color: azure">Welcome ${user.username}</h3>
<span style="color: azure">Email: ${user.email}</span>
`)
}
)
})
Tried giving IsAuthenticated didn't work
Also tried Passport but I could not get it to work
Expected:-
User Logs in and can see the dashboard but the user's dashboard should not be visible to everyone.

Post current React state to express server

I have tried a few different ways of doing this but no matter what I've tried I get the same result.
Once I start the server, the first time I post, it doesn't work. because the state hasn't updated (I think)? All of the following post requests afterwards work fine, along with the correct state. So I'm not so sure it's state. Here's my code
Register.js
export default class Register extends React.Component{
constructor(props){
super(props);
this.state = {
username: '',
password: ''
}
}
onChange = (e) => this.setState({[e.target.name]: e.target.value});
register = async () => {
await axios.post(config.API_URI + 'register', {
username: this.state.username,
password: this.state.password
});
}
render(){
return(
<div>
<form id='register-form' className='border'>
<div id='form-title'>Create account</div>
<div className='mb-3'>
<label className='form-label' htmlFor='username-label' name='username-label'>Username</label>
<input type='text' className='form-control' id='usernameInput' name='username' onChange={this.onChange}></input>
</div>
<div className='mb-3'>
<label className='form-label' htmlFor='password-label' name='password-label'>Password</label>
<input type='password' className='form-control' id='password-input' name='password' onChange={this.onChange}></input>
</div>
{/*}
<div className='mb-3'>
<label className='form-label' htmlFor='password-label-confirm' name='password-label-confirm'>Confirm Password</label>
<input type='password' className='form-control' id='password-input-confirm' name='passwordConfirm'></input>
</div>
{*/}
<div id='buttons'>
<button className='btn btn-primary' id='register-btn' onClick={this.register} disabled={this.state.username === '' & this.state.username === ''}>Register</button>
<Link className='btn btn-primary' to='/login' id='login-btn'>Login</Link>
</div>
</form>
</div>
)
}
}
server.js post request (currently just attempting to console.log my request instead of adding to my database). I have gotten it to save to the database just fine following the second post attempt.
...
app.post("/register", (req, res)=>{
try{
if(req.body){
console.log(req.body);
/*
User.find({username: req.body.username}, (err, data)=>{
if(data.length == 0){
let user = new User({
username: req.body.username,
password: req.body.password
});
user.save((err,data)=>{
if(err){
res.status(400).json({
errorMessage: err,
status: false
});
} else {
res.status(200).json({
status: true,
title: 'Registered Successfully'
});
}
});
}
});
*/
}
} catch(e){
res.status(400).json({
errorMessage: 'Something went wrong!',
status: false
})
}
});
I'm a beginner making my first full-stack application
Good night! I cannot comment because I'm too new to Stack Overflow, so I'll say the following as an answer:
I ran your code on my machine and it worked fine but the reload. When I clicked on the "Register" button, I read the current state value by adding console.log(this.state) just before await axios.post(...).
That worked since the first click. However, the page reloads each submition. You missed the event.preventDefault(). This function prevents the reload when submitting a form by clicking on a button inside it. This is the code:
register = async (event) => {
event.preventDefault();
await axios.post('http://localhost:3333/register', {
username: this.state.username,
password: this.state.password
});
}
Then, I ran a Node API with your code. It also console.log(req.body) worked perfectly fine as well.
Therefore, I don't have any other ideas of what could be causing the issue on your machine besides the missing event.preventDefault().
P.S.:
Some features that helps while debugging JavaScript:
Use the command debugger; on your Front-End code. For example, inside the register method. This allows you to stop the execution of the code and inspect all the variables available and their current values. This may help you.
While debugging don't forget to put a lot of console.log on your code in specific parts that could be related to your bug. This helps you understand what's going on.

Cannot successfully delete document in mongoose DB with DELETE or POST Request

I am trying do delete employees in my mongoose database through my admin.ejs page.
So far I've tried to do with with a normal POST request and with DELETE using method-override but neither work.
// Attempt Without method-override
//Schema//
const empSchema = {
name: String,
number: String
};
const Employee = mongoose.model("Employee", empSchema);
//admin.ejs//
<form action="/deleteEmp" method="POST" >
<div>
<label>Name</label>
<input type="text" name="name">
</div>
<button class="btn btn-primary" type="submit">Delete Employee</button>
</form>
//app.js//
app.post("/deleteEmp", function(req, res) {
Employee.findOneAndDelete({name: req.params.name}, function(err, result) {
if(!err) {
console.log('User Deleted');
res.redirect("admin");
} else {
console.log(err);
}
});
});
//Attempt with method-override//
//admin.ejs//
<form method="POST" action="/deleteEmp?_method=DELETE">
<div>
<label>ID</label>
<input type="text" name="id">
</div>
<button class="btn btn-primary" type="submit">Delete Employee</button>
</form>
//app.js//
app.delete("/deleteEmp", function(req, res) {
Employee.findOneAndDelete({name: req.params.name}, function(err, result)
{
if(!err) {
console.log('User Deleted');
res.redirect("admin");
} else {
console.log(err);
}
});
});
In my attempt without method override it will go through if it is set to POST method instead of DELETE but the employee is not deleted. If the method is set to DELETE it returns a cannot GET /deleteEMP
I didn't have any success with method-override, so I wonder if I am missing something?
Although you can perform a delete operation through a POST request, it is recommended to use the proper http VERB (GET, POST, PUT, DELETE ..) for each case. So in your case i would use an AJAX delete request to delete the document.
By the way, the reason that your first option (action="/deleteEmp" method="POST") does not work is a mistake in your controller. Use name: req.body.name instead of name: req.params.name. The data you send is in the body of your request object.
"cannot GET /deleteEMP" indicates that u sent GET request. Try to send DELETE request from Postman or XMLHttpRequest(Ajax). HTML forms only support GET and POST as HTTP request methods.

Passing user object to view results in "user is not defined" with Sails

I've been trying to learn node lately using the Express and SailsJS frameworks and with both i've struggled with passing data to the EJS view.
I'm following the correct convention of passing data to the view using {data: 'This is the data'} however I'm constantly having issues with this data persisting.
Sails app with passport for authentication. I can both signup users and login however using conditionals in the view and passing data is always a problem.
AuthContoller.js
const passport = require('passport');
module.exports = {
_config: {
actions: false,
shortcuts: false,
rest: false
},
login: function(req, res) {
passport.authenticate('local', function(err, user, info) {
if ((err) || (!user)) {
return res.send({
message: info.message,
user: user
});
}
req.logIn(user, function(err, user) {
if (err) res.send(err);
return res.view('homepage', {
message: info.message,
user: user
});
});
})(req, res);
},
logout: function(req, res) {
req.logout();
res.redirect('/');
}
};
Then the part of the view i'm trying to show a logout button when a user is logged in and hide it when logged out. But I constantly get a user is not defined error for the view.
homepage.ejs
<ul class="nav navbar-nav navbar-right">
<li>Signup</li>
<% if (user) { %>
<li><%= user.email %></li>
<li>Logout</li>
<% } else { %>
<li>Login</li>
<% } %>
</ul>
I don't know if i'm supposed to save the user in the session, or if it's something to do with locals or the view syntax but i've tried multiple solutions with no luck. Had the same issues with Express too.
I've tried using if (locals.user) in the view and that stops the errors but it doesn't change the view.
TL;DR: I basically want to create a user object once a user is logged in that can display details in the view, accessible to multiple pages. And know what to do when a user who hasn't signed up visits the webpage and no user object has been created yet.

NodeJS not able to get token value from req.params.token

app.post('/reset/:token', function(req, res) {
async.waterfall([
function(done) {
User.findOne({ 'local.resetPasswordToken' : req.params.token, 'local.resetPasswordExpires' : { $gt: Date.now() } }, function(err, user) {
if (!user) {
req.flash('resetMessage', req.params.token);
return res.redirect('back');
}
], function(err) {
res.redirect('/');
});
});
app.get('/reset/:token', function(req, res) {
User.findOne({ 'local.resetPasswordToken': req.params.token, 'local.resetPasswordExpires' : { $gt: Date.now() } }, function(err, user) {
if (!user) {
req.flash('forgotMessage', req.params.token );
return res.redirect('/forgot');
}
res.render('reset.ejs', { user: req.user, message: req.flash('resetMessage') });
});
});
<!--Reset.ejs page ResetPassword FORM -->
<form action="/reset/:token" method="post">
<div class="form-group">
<label>New Password</label>
<input type="text" class="form-control" name="newpassword">
</div>
<div class="form-group">
<label>Confirm Password</label>
<input type="text" class="form-control" name="confirmpassword">
</div>
<button type="submit" class="btn btn-warning btn-lg">Reset</button>
</form>
I able to get the token with req.params.token for the "post" after clicking
http://localhost:8080/reset/fed831abf73150c96f6a3e392b5cbdcaccdeb9bd
Later when I submit through the reset.ejs for the "get" I couldn't retrieved any token value with req.params.token.
Any solution to it?
I imagine that the original code for this might have come from http://sahatyalkabov.com/how-to-implement-password-reset-in-nodejs/. In this tut the jade templating engine is used and if you look at the reset.jade you will see that it starts with
form(method='POST')
but no action is defined. I don't really know jade but in your example you are using ejs and in your code you are setting the action to
form action="/reset/:token" method="post"
and as everybody has pointed out the route that you post to is exactly /reset/:token. So req.params will be :token and the reset will fail. What you need to do is post the url exactly as it appears in the get request. If you read
Is it a good practice to use an empty URL for a HTML form's action attribute? (action="")
you can see that you can amend your reset.ejs page code to read
form action="" method="post"
Now the post should have an action equal to the get url with the token in place and the reset should occur.
you need another form with method='get' and action='reset/' + tokenvar. Also your async waterfall does not call done() so will not call the redirect if the user exists

Resources