I am making a Portfolio application with nodeJS and express. I want to implement an admin panel which I can create,delete, update and edit my skills,experience,about etc, but I don't know how can I keep those admin routes secret and what kind of authentication to make.If we can do by putting Basic authentication on post,patch,delete route then how will we implement basic authentication on routes.
index.js
const express = require('express');
const app = express();
var cors = require('cors');
require('./db/mongoose')
const menuRouter = require('./routers/menu')
const skillRouter = require('./routers/skill')
const aboutRouter = require('./routers/About')
const experienceRouter = require('./routers/Experience')
const resumerouter = require('./routers/Resume')
const userRouter = require('./routers/user')
const port = process.env.PORT || 4000;
app.use(express.json());
app.use(cors());
app.use(menuRouter);
app.use(skillRouter);
app.use(aboutRouter);
app.use(experienceRouter);
app.use(resumerouter);
app.use(userRouter)
app.listen(port, () => {
console.log("Server is runing on port" + port)
});
skill.js
const express = require('express');
const Skill = require('../model/skill');
const router = new express.Router();
router.post('/skill', async (req, res) => {
const skill = new Skill(req.body);
try {
await skill.save();
res.status(201).send(skill);
} catch (e) {
console.log(e);
res.status(400).send(e);
}
})
router.get('/skill', async (rq, res) => {
try {
const skill = await Skill.find({});
res.status(201).send(skill);
} catch (e) {
res.status(400).send(e);
}
})
module.exports = router;
As specified in the comments, I would refactor your code a bit, seems messy and you're kind'a repeating yourself every line you import a route, so, you should do it better as well...
have an index.js file in your /routers folder with the content of the demo repo I've made for other StackOverflow question
then, to separate things, I would do something like:
const routes = require('./routes')
...
const protectRouteWithApiKey = (req, res, next) => {
const auth = req.headers['x-apikey']
if (auth && auth === '<YOUR API KEY>') return next()
return next(new Error('403 | Authorization is missing or value is wrong'))
}
...
app.use('/api', protectRouteWithApiKey, routes) // point to your routes and protect access
app.use('/', defaultEngine) // your engine to render html
you would then have a protected route in /api/* and normal routes for everything else
A middleware where you detect if the logged user is the admin?
In this sample checking by the email, and you can save the adminemail as a global variable
ensureAdmin: function(req, res, next) {
if (req.isAuthenticated()) {
if (req.user.email === adminemail) {
return next();
} else {
return res.redirect('/adminsecretroute');
}
}
res.redirect('/');
}
Related
I am trying to make a site where the code checks what subdomain the user is accessing from. I can get the host by running this:
app.get('/', (req, res) => {
const host = req.get('host');
const origin = req.get('origin');
res.send(host)
})
And i get the result: localhost:5000.
But i want to make it so that if the user is accessing from domain1.com it uses one router, and if it is from domain2.com it uses another. here is the code im trying to use:
server.js
const express = require('express')
const app = express()
const router = express.Router();
app.set('view engine', 'ejs')
app.get('/', (req, res) => {
const host = req.get('host');
const origin = req.get('origin');
if(host === "domain1.com:5000"){
const test = require('./routes/test.js')
app.use('/', test)
}else if(host === "domain2.com:5000"){
res.send("Domain2")
}else{
res.send(host)
}
})
app.listen(5000)
/routes/test.js
const express = require('express')
const router = express.Router()
router.get('/', async (req, res) => {
res.send("it Works")
})
module.exports = router
When i try accessing the domain1 it does not work, and i don't get any errors in console
I am trying to create a simple CRUD app. I was trying to set up the first backend route but I am stuck and can't figure out what is wrong with my code or what I am missing.
If I try to test the route with Insomnia, it doesn't return any error, but it targets the basic "/" route and returns the console.log('OK') instead of creating an item in MongoDB. Here is my code:
// app.js file
require("./db");
const express = require("express");
const app = express();
require("./config")(app);
const backoffice = require('./routes/backoffice');
app.use('/api/backoffice', backoffice);
app.use('/', (req, res) => {
res.send('OK')
});
module.exports = app;
// route file backoffice.js
const router = require("express").Router();
const Item = require("../models/Item");
router.post('/backoffice', (req, res, next) => {
console.log(req.body);
const {title, description} = req.body;
Item.create({
title,
description
})
.then(response => {
console.log(response)
res.status(200).json({message: 'New item added succesfully'})
})
.catch(err => res.json(err))
})
module.exports = router;
I've deployed my app on Heroku and after some tweaking, everything works except when I try to retrieve data from the Mongo database. The console error I get is: Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0.
I have the feeling that it won't go into the get request while that should be the case. (Because it's not logging anything in the console)
Am I missing something in the way routes are handled in production?
Everything in development is working.
I'm very confused at this point, hope someone can help me
Server.js:
const bodyParser = require('body-parser')
const path = require('path');
const express = require('express');
const morgan = require('morgan');
const MongoClient = require('mongodb').MongoClient;
const cors = require('cors')
const compression = require('compression');
const helmet = require('helmet')
const app = express();
const port = process.env.PORT || 5000;
app.use(helmet())
app.use(compression());
if (process.env.NODE_ENV === 'production') {
const publicPath = path.join(__dirname, 'client/build');
const apiPath = path.join(__dirname, 'api');
app.use(express.static(publicPath));
app.use('/overview', express.static(apiPath));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/client/build/index.html'));
})
}
app.use(cors())
app.use(morgan('tiny'));
app.use(bodyParser.json())
const apiRouter = require('./api/api');
app.use('/overview', apiRouter);
// connect to the db and start the express server
let db;
const url = process.env.MONGODB_URI
MongoClient.connect(url, {useUnifiedTopology: true,useNewUrlParser: true,}, (err, client) => {
if(err) {
return console.log(err);
}
console.log('mongo connected')
db = client.db('kvdlaanmeldingen');
// start the express web server listening on port 5000
app.listen(port, () => console.log(`Listening on port ${port}`));
});
apiRouter, api.js in api/api.js:
const express = require('express');
const apiRouter = express.Router()
const MongoClient = require('mongodb').MongoClient;
const mongodb = require('mongodb');
const url = process.env.MONGODB_URI
console.log('api.js is activated') //this is logged to console, so file can be read.
let db;
MongoClient.connect(url, {useUnifiedTopology: true,useNewUrlParser: true,}, (err, client) => {
db = client.db('kvdlaanmeldingen');
});
let aanmeldingen = [];
// this is where I believe it gets stuck
apiRouter.get('/', (req, res) => {
console.log(db)
db.collection('kvdlaanmeldingen').countDocuments({}, function(err, result) {
console.log(result)
if (err) return console.log(err);
res.send(JSON.stringify(result));
})
});
module.exports = apiRouter;
The get request should be done as soon as this React component is rendered:
import React from 'react';
import './Aanmeldingen.css';
import { Link, Route } from "react-router-dom";
import XPress from './utils/Xpress.js';
import TaakComponent from './TaakComponent';
import { snakeCase } from "snake-case";
class Aanmeldingen extends React.Component {
constructor (props) {
super(props);
this.state = {
dataLoaded: 0,
taken: [// an array of different names that will be loaded as headers],
taakKlik: false,
taakData: null,
taakNaam: null,
}
}
componentDidMount(){
XPress.getTaken().then(data => {
console.log(data)
if (data) {
this.setState({
taakData: data,
dataLoaded: 1,
});
}
});
}
{...}
render(){
return (
<div className="Aanmeldingenpage">
<div className="statistics" onClick={this.aanmeldingen}>
<p className="statistics" id="counterAanmeldingen">{this.state.dataLoaded ? `Aantal aanmeldingen: ${this.state.taakData}` : 'Data aan het laden..'}</p>
</div>
</div>
);
}
}
and Xpress.getTaken is looking like this:
const XPress = {};
const baseUrl = window.location.origin;
XPress.getTaken = () => {
const url = `${baseUrl}/overview`;
return fetch(url, {method: 'GET'}).then(response => {
if (!response.ok) {
return new Promise(resolve => resolve([]));
}
return response.json().then(jsonResponse => {
return jsonResponse
}
)
})
}
The error you posted is often seen when parsing JSON fails. I guess this happens when fetch fails to parse the result in the frontend at this line: return response.json().then(jsonResponse => {.
Instead of returning valid JSON, the backend returns a file that starts with "<" (the unexpected token). Your backend responds with an HTML page instead of JSON.
Issue comes from here most likely:
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/client/build/index.html'));
})
This basically says that all GET requests should serve index.html. That's why the request doesn't go to apiRouter.get('/'), it stops at the first match, which is the code above. It works on localhost because this code path is inside a conditional that checks NODE_ENV for production.
Not sure why you have it in there, but removing it would solve the issue.
Please try adding the heroku postbuild script to your json file in the root directory as same as the existence of the server.js file, that might help, using in react we must add heroku postbiuld so that the build is saved in the server, and that might not produce an issue,
I have node-express app where I have bunch of Routes for login, logout and signup and one Route for checking authorised Route which can be accessed only through providing authToken. I moved the Routes to separate Route file and I got the above error.
This is my Users Routes File:
const express = require('express');
const authenticate = require('./../middleware/authenticate');
const router = express.Router();
const {User} = require('./../models/user');
router.post('/',(req, res) => {
var body = _.pick(req.body,['email','password']);
var user = new User(body);
user.save().then(() => {
return user.generateAuthToken()
}).then((token) => {
res.header('x-auth', token).send(user);
}).catch((e) => {
res.status(400).send(e);
});
});
router.post('/login',(req, res) => {
var body = _.pick(req.body, ['email', 'password']);
User.findByCredentials(body.email, body.password).then((user) => {
return user.generateAuthToken().then((token) => {
res.header('x-auth', token).send(user);
});
}).catch((e) => {
res.status(400).send(e);
});
});
router.delete('/logout',authenticate, (req, res) => {
req.user.removeToken(req.token).then(() => {
res.status(200).send();
},(e) => {
res.status(400).send(e);
}) ;
});
router.get('/me',authenticate, (req,res) => {
res.send(req.user);
});
module.exports = router;
Following is my main server.js file:
const express = require('express');
const _ = require('lodash');
var app = express();
const usersRoutes = require('./routes/users');
app.use(express.json());
app.use('/users', usersRoutes);
var {mongoose} = require('./db/mongoose');
var {User} = require('./models/user');
var {authenticate} = require('./middleware/authenticate');
const port = process.env.PORT || 3000 ;
app.listen(port, () => console.log(`Listening on ${port}...`))
I have a model/Schema(mongoose) file for User so If You feel you need that I am ready to edit my question. Thanks.
The problem is that router.delete is expecting a function on the middleware parameter (like you did in your server.js file with app.use(express.json())) so it can be used like a callback which gets called whenever a request reach your route.
Try changing authenticate to authenticate().
It seems like in your users routes file you are importing the entire module who contains the authenticate function, so when try to access it like a function you'll get an error. You need to import it like you did in your server.js file.
Change the line const authenticate = require('./../middleware/authenticate'); for const {authenticate} = require('./../middleware/authenticate');.
I'm using Express in my Cloud Function with multiple routes, and I'm trying to add in a function that when triggered, will pull the users Gravitar and add it to their user object.
The problem I'm having is that I cannot get the onCreate function to fire, and I suspect it's to do with how I'm attempting to export it.
My index.js looks like the following:
const admin = require("firebase-admin");
const functions = require("firebase-functions");
const usersApi = require("./api/users")
const paymentsApi = require("./api/payments")
const express = require('express');
const cors = require('cors');
const app = express();
const checkHeader = async(req, res, next) => {
if(req.headers.authorization) {
admin.auth().verifyIdToken(req.headers.authorization)
.then(token => {
req.uid = token.uid;
req.email = token.email;
req.stripeID = token.stripeID || null;
return next();
})
.catch(e => {
return next(e.errorInfo.code)
})
} else {
return next('No token found');
}
}
app.use(cors({origin: true}));
app.use(express.urlencoded({extended: true}));
app.use(express.json());
app.use(checkHeader);
app.disable("x-powered-by");
app.use("/users", usersApi)
app.use("/payments", paymentsApi)
const setupUser = functions.auth.user().onCreate((user) => {
console.log("onCreate")
console.log(user)
})
module.exports = {
api: functions.https.onRequest(app),
setupUser
}
The api endpoint works fine, however I can't get the onCreate function to trigger when a user is created.