I'm trying to make the a multi-language website. I've decided to go with data objects, to fill in all the text necessary. The routes I'm using for the 2 languages are exactly the same with only a difference in the import. Is there any way to make this happen without code duplication?
The templating engine is Express Handlebars, so if there is another solution for making a website multi-language, I'm open to implementing it :)
Thanks in advance for taking the time to read this.
The code in my routes for Sl language:
const express = require("express"),
router = express.Router(),
data = require("../Views/res/lang/sl");
router.get("/", (req, res) => {
data.Home = setupDataObject(data.Home, req.originalUrl, "Home");
res.render("homepage", data.Home);
});
router.get("/About", (req, res) => {
data.About = setupDataObject(data.About, req.originalUrl, "About");
res.render("About", data.About);
});
function setupDataObject(data, url, active) {
data.current = url.substring(4);
data.active = { [active]: true };
return data;
}
module.exports = router;
And the English version
const express = require("express"),
router = express.Router(),
data = require("../Views/res/lang/en");
router.get("/", (req, res) => {
data.Home = setupDataObject(data.Home, req.originalUrl, "Home");
res.render("homepage", data.Home);
});
router.get("/About", (req, res) => {
data.About = setupDataObject(data.About, req.originalUrl, "About");
res.render("About", data.About);
});
function setupDataObject(data, url, active) {
data.current = url.substring(4);
data.active = { [active]: true };
return data;
}
module.exports = router;
You can handle the locale in using with the url params, no ?
const express = require("express"),
router = express.Router();
router.get("/:locale/", (req, res) => {
const data = require("../Views/res/lang/" + locale);
data.Home = setupDataObject(data.Home, req.originalUrl, "Home");
res.render("homepage", data.Home);
});
router.get("/:locale/About", (req, res) => {
const data = require("../Views/res/lang/" + locale);
data.About = setupDataObject(data.About, req.originalUrl, "About");
res.render("About", data.About);
});
function setupDataObject(data, url, active) {
data.current = url.substring(4);
data.active = { [active]: true };
return data;
}
module.exports = router;
Related
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('/');
}
I am trying to post to my cosmosDB using Angular. I can GET just fine, but POST returns with a 404 error in Postman. I am new to routes and APIs so I am a little lost on what is causing the issue.
Here is my index.js
const bodyParser = require('body-parser');
const path = require('path');
const routes = require('./routes');
const root = './';
const port = process.env.PORT || '3000';
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(root, 'dist/checkin')));
app.use('/api', routes);
app.get('*', (req, res) => {
res.sendFile('dist/checkin/index.html', {root});
});
app.listen(port, () => console.log(`API running on localhost:${port}`));
My routes.js
const contactService = require('./contact.service');
const router = express.Router();
router.get('/contacts', (req, res) => {
contactService.getContacts(req, res);
});
router.post('/contact/', (req, res) => {
contactService.postContact(req, res);
});
module.exports=router;
My contact.service.js which contains all of my operations (Just GET and POST right now)
const ReadPreference = require('mongodb').ReadPreference;
require('./mongo').connect();
function getContacts(req, res) {
const docquery = Contact.find({}).read(ReadPreference.NEAREST);
docquery
.exec()
.then(contacts => {
res.status(200).json(contacts);
})
.catch(error => {
res.status(500).send(error);
return;
});
}
function postContact(req, res) {
const originalContact = { uid: req.body.uid, name: req.body.name, description: req.body.description };
const contact = new Contact(originalContact);
contact.save(error => {
if (checkServerError(res, error)) return;
res.status(201).json(contact);
console.log('Contact created successfully!');
});
}
function checkServerError(res, error) {
if (error) {
res.status(500).send(error);
return error;
}
}
module.exports = {
getContacts,
postContact
};
Input is obtained through an HTML forum which is picked up and sent through
return this.http.post<Contact>(`${api}/contact/`, contact);
}
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 have followed the node-postgres.org instruction at https://node-postgres.com/guides/async-express to connect via async/await to my postgres table users.
Navigating to localhost:3000/users/1 will return the JSON string for user 1 in the browser. I have extended this a bit to return all users at localhost:3000/users. My routes/user.js script is:
const Router = require('express-promise-router')
const db = require('../db')
// create a new express-promise-router
// this has the same API as the normal express router except
// it allows you to use async functions as route handlers
const router = new Router()
// export our router to be mounted by the parent application
module.exports = router
router.get('/:id', async (req, res) => {
console.log('Where id = ');
const { id } = req.params
const { rows } = await db.query('SELECT * FROM users WHERE id = $1', [id])
res.send(rows[0])
})
router.get('/', async (req, res) => {
console.log('*');
const { rows } = await db.all('SELECT * FROM users')
res.send(rows)
})
the index for this route at routes/index.js is simply:
const users = require('./user')
module.exports = (app) => {
app.use('/users', users)
}
and the db.query() and db.all() functions that I am awaiting are in db/index.js:
const { Pool } = require('pg')
const pool = new Pool()
module.exports = {
query: (text, params) => pool.query(text, params),
all: (text) => pool.query(text)
}
The routes are required in my main app.js file:
// ./app.js
const express = require('express')
const mountRoutes = require('./routes')
const cons = require('consolidate')
const path = require('path')
const bodyParser = require('body-parser')
const app = express()
mountRoutes(app)
// Assign Dust Engine to .dust files
app.engine('dust', cons.dust);
// Set .dust as the default extension
app.set('view engine', 'dust');
app.set('views', __dirname + '/views');
// Set Public Folder
app.use(express.static(path.join(__dirname, 'public')));
//Body parser and Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
app.get('/', function(reg, res) {
console.log('Root');
res.render('index', {hallo:'test'})
});
//Server
app.listen(3000, function() {
console.log('Server Started on Port 3000');
});
So far this works beautifully! I get the JSON strings that I want and I can build upon this sort of API by extending my routes and queries.
Question:
How can I return my JSON object rows back to app.js to res.render() it there?
or
Can I do something like this anywhere in my app:
jsonVar = GetMyJson('/users/1');
console.log(jsonVar);
returns:
[
{
"id": 1,
"usr_name": "Michael"
},
{
"id": 2,
"usr_name": "Thomas"
},
{
"id": 3,
"usr_name": "Paul"
}
]
I could then pass whatever route and parameters I want into GetMyJson() and deal with the resulting JSON.
This may be a trivial question for javascript devs ...
Thanks!
EDIT 21/12/2017
I have created a frontend script called graphs.js that simply logs my result when i call the fuction api('/user/1').
var el = document.getElementById("clickMe");
if (el.addEventListener)
el.addEventListener("click", api, false);
else if (el.attachEvent)
el.attachEvent('onclick', api);
var api = function(what){
// Action
sendRequest(what, function(result){
if(result){
log(result);
}
})
}
var apiEndpoint = 'http://localhost:3000/'
function sendRequest(_path, cb) {
var oReq = new XMLHttpRequest();
oReq.open('GET', apiEndpoint+_path);
oReq.onreadystatechange = function() {
if (this.readyState === 4) {
cb(JSON.parse(this.response));
}
else{
cb(null);
}
}
oReq.send();
}
function log(msg){
console.log(msg);
}
BUT
Is that a proper way of doing it in javascript?
The way to go about it would be something like this:
router.get('/', async (req, res) => {
console.log('*');
const { rows } = await db.all('SELECT * FROM users')
res.render('user', rows)
});
Instead of res.send you do res.render
You have to get it from database. Making self call will not be an ideal solution. So you can do following
const db = require('../db') // fix the location of db.js
app.get('/', function(req, res) {
db.all('SELECT * FROM users').then(function({ rows }) {
res.render('index', rows)
})
})
Best would be to make an userRepository.js and add code related to db over there. Then use that repository at both places.
I'm trying to route different radio sessions/episodes from database queries with express.js. I'm able to grab the route param from the URL in my express route. But I'm unsure how to use the param to query the database for the right object.
I'm able to get this to work by hardcoding the param into my service as shown below with the 'title' variable in service.js. Currently, I only know how to pass variables from express to the view. Is there a way to pass the title param from my express route to the service.js file?
Routes.js
'use strict';
const express = require('express');
const Sessions = require('../models/sessions')
const routes = express.Router();
const apiRoutes = express.Router();
apiRoutes.get('/sessions', (req, res) => {
Sessions.find({}, (err, sessions) => {
if(err) {
return res.status(500).json({message: err.message});
}
res.json({sessions: sessions});
})
});
routes.get('/', (req, res, next) => {
return res.render('index', {title: 'Home'});
});
// I added this bit of code after realizing I needed to use mongoose to query the database. I'm still unsure how to pass the object to my javascript but I'm getting closer.
routes.get('/:title', function (req, res, next) {
const title = req.params.title
Sessions.find((err, sessions) => {
if (err) return console.error(err);
for (let i = 0; i < sessions.length; i++) {
if (title === sessions[i].title.toLowerCase()) {
res.render('session')
}
}
})
})
module.exports = [routes, apiRoutes];
index.js
'use strict';
const express = require('express');
const app = express();
const [routes, apiRoutes] = require('./src/routes');
require('./src/database');
require('./src/seed');
app.use('/', express.static('public'));
app.set('view engine', 'pug');
app.set('views', __dirname + '/views');
app.use('/api', apiRoutes);
app.use('/', routes);
app.listen(process.env.PORT || 8082);
service.js
const root = window.location.origin;
const title = 'session 1'
function callService(){
service(`${root}/api/sessions`)
.then(retrieveSession)
.catch(function(e) {
console.log(e)
});
}
function retrieveSession(data) {
for (let i = 0; i < data.sessions.length; i++) {
if(data.sessions[i].title.toLowerCase() === title) {
return data.sessions[i];
}
}
}
function service(url) {
return new Promise(function(res, rej) {
var httpRequest = new XMLHttpRequest();
httpRequest.open('GET', url);
httpRequest.onreadystatechange = handleResponse;
httpRequest.onerror = function(error) {
rej(error)
}
httpRequest.send();
function handleResponse() {
if (httpRequest.readyState === 4) {
if (httpRequest.status === 200) {
var data = JSON.parse(httpRequest.responseText);
res(data)
} else {
rej(this.statusText)
}
}
};
});
}
I was able to achieve the desired result by passing the data to the view via mongoose with the following snippet.
routes.get('/:title', function (req, res, next) { // Passes url param
const title = req.params.title //stores url param in varaible
Sessions.findOne({ 'title': title }, function (err, session) { // compares url param against my mongoose schema and queries the database for the desired object
if (err) return handleError(err); // checks for errors
res.render('session', {session: session}); // renders the page while passing the desired data.
})
})