//importing express into the project
const express = require('express');
//importing body parser into the file, allows express to automatically decode request and response bodies
const bodyParser = require('body-parser');
//router is used to make modular server architectures
const router = require('./server/router');
// importing path into the project, allows express to use nested file paths
const path = require('path');
// importing the controller that has all of the route callbacks
const controller = require('./server/controller');
// instantiating an express application
const app = express();
// the application will serve up files to a certain environment (test, development, staging) and default to localhost:3000
app.set('port', process.env.PORT || 3000);
// creating an internal name for the project and attaching it the locals object
app.locals.title = 'Jet Fuel';
// express is serving up static assets and declaring a path to the public folder in the root directory
app.use(express.static(path.resolve(__dirname, './public')));
// body parser will return json data when interpreting request and response bodies
app.use(bodyParser.json());
// the midddleware will parse the body into key value pairs, extended true means those values can be any datatype
app.use(bodyParser.urlencoded({ extended: true }));
// relative urls starting with /api will be passed to the router
app.use('/api', router);
// on homepage app will serve the html file
app.get('/', (req, res) => {
res.status(200).sendFile(path.join(__dirname, 'public/index.html'))
})
// on realtive url path app will call the redirect function from controller and redirect to long url
app.get('/*/:charHash', controller.redirectUrl)
// app is listening for events on previously sti[ulated port and alerting in the console what port it is listening to.
app.listen(app.get('port'), () => {
console.log(`${app.locals.title} is running on http://localhost:${app.get('port')}.`);
});
// exporting the app for testing suite
module.exports = app;
#router.js
// importing express to use router
const express = require('express');
//importing router and linking it to express
const router = express.Router();
// importing controller to use callbacks
const controller = require('./controller');
// router listening for GET requests on /api/v1/folders
router.get('/v1/folders', controller.getFolders);
// router listening for POST requests on /api/v1/folders
router.post('/v1/folders', controller.newFolder);
// router listening for GET requests on /api//v1/folders/:id/links
router.get('/v1/folders/:id/links', controller.getFolderLinks);
// router listening for POST requests on /api//v1/links
router.post('/v1/links', controller.createNewLink);
// exporting router to link in app
module.exports = router;
// setting environment variable for knex database, defaulting to development
const environment = process.env.NODE_ENV || 'development';
// establishing stipulated environment settings to use with database
const configuration = require('../knexfile')[environment];
// importing the database with its settings fro the particular environment
const database = require('knex')(configuration);
// importing node package to make short hashes of strings for shortened urls
const shortHash = require('short-hash');
// creating a function to get folders from database
const getFolders = (request, response) => {
//selecting all the folders from the folders table in DB
database('folders').select('*')
// promise is returned and folders are sent to client with a 200 OK status
.then(folders => {
response.status(200).json(folders);
})
//any errors result in a 500 internal server error and the error is sent to client
.catch(error => {
response.status(500).json({ error });
})
}
// function expression to create new folder in the database
const newFolder = (request, response) => {
// request body is assigned to newFolder variable
const newFolder = request.body;
// keys in requst body are checked for required data and rejected if not present with a 422 unprocessable entity
for (let requiredParameter of ['name']) {
if (!newFolder[requiredParameter]) {
return response.status(422).json({
error: `Missing required parameter ${requiredParameter}`
});
}
}
// folders table is selected in DB and request body is inserted in DB. name and id are returned.
database('folders').insert(newFolder, ['name', 'id'])
// if successful promise is returned, 201 created status code is sent along with the name and id to client
.then(folder => {
console.log(folder)
response.status(201).json({
name: folder[0].name,
id: folder[0].id })
})
// errors at any point give internal server error to client
.catch(error => {
response.status(500).json({ error })
});
}
// function expression to get links associated with a folder
const getFolderLinks = (request, response) => {
// links table is selected and narrowed down to links associated with the requested folder
database('links').where('folder_id', request.params.id).select()
// if success, status 200 OK is sent to client along with the links that were requested
.then(links => {
response.status(200).json(links);
})
// if problem with request, internal server error 500 is sent to client
.catch(error => {
response.status(500).json({ error })
})
}
// function expression to create new link in DB
const createNewLink = (req, res) => {
// short URL is made using shorthash, interpolated into a quazi url
const shortURL = `http://jt.fl/${shortHash(req.body.long_URL)}`
// short url is injected intp request body
const formattedData = Object.assign(req.body, {
short_URL: shortURL
});
// request is checked for required parameters
for (let requiredParams of ['long_URL', 'short_URL', 'folder_id', 'description']) {
// if data is missing, return status 422 unprocessable entity to client with message
if (!formattedData[requiredParams]) {
return res.status(422).json({
error: `Missing required parameter ${requiredParams}.`
})
}
}
// links table selected in DB and request body is inserted. The entire request body is returned
database('links').insert(formattedData, '*')
//success sends status 201 created to client
.then(link => {
res.status(201).json(link[0])
})
// failure sends internal server error 500 to client
.catch(error => res.status(500).json({ error }))
}
//function expression to redirect client to long url
const redirectUrl = (request, response) => {
//links table selected in DB and matched to data that have the same short URL
database('links').where({
short_URL: `http://jt.fl/${request.params.charHash}`
})
// long url is taken from matched data and returned
.select('long_URL')
// if successful, client is redirected to url
.then(data => {
response.redirect(`${data[0].long_URL}`);
})
//failure and 404 not found is sent to client
.catch(error => {
response.status(404).json({ error });
});
}
module.exports = {
getFolders: getFolders,
newFolder: newFolder,
getFolderLinks: getFolderLinks,
createNewLink: createNewLink,
redirectUrl: redirectUrl
};