Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
31 changes: 31 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed
.env

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# https://docs.npmjs.com/cli/shrinkwrap#caveats
node_modules

# Debug log from npm
npm-debug.log
14 changes: 14 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM node:11.2.0-alpine

RUN apk add --no-cache curl-dev libzip-dev autoconf build-base gmp-dev coreutils python

RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY . /usr/src/app
RUN npm i

EXPOSE 3000

ENV DB_HOST database

CMD ["npm", "start"]
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: npm start
69 changes: 68 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,68 @@
# gettingMean-2
# gettingMean-2
# Getting MEAN Second Edition application code

This is the code for the sample 'Loc8r' application that is built through the course of the book Getting MEAN Second Edition.

Getting MEAN Second Edition is published by Manning, and teaches readers how to develop web applications end-to-end using the MEAN stack with Node 11 and Angular 7. It is currently in early access through the MEAP program, with new chapters being released regularly.

> Note: if you have the First Edition of the book using Node 4 and Angular 1 you need the [First Edition code](https://github.com/simonholmes/getting-MEAN/) instead.

## The application at various stages

There will be named branches for the various states of the code throughout the book:

* `master` **Chapter 3 start**: A blank Express 4.16.3 project
* `chapter-03` **Chapter 3 end**: Creating and setting up a MEAN project
* `chapter-04-views` **Chapter 4 mid-point**: The data is hard coded into views
* `chapter-04` **Chapter 4 end**: Building a static site with Node.js and Express
* `chapter-05` **Chapter 5**: Building a data model with MongoDB and Mongoose
* `chapter-06` **Chapter 6**: Writing a REST API: Exposing your MongoDB database to the application
* `chapter-07` **Chapter 7**: Consuming a REST API: Using an API from inside Express
* `chapter-08` **Chapter 8**: Creating an Angular application with TypeScript
* `chapter-09` **Chapter 9**: Building a Single Page Application with Angular
* `chapter-10` **Chapter 10**: Building a Single Page Application with Angular: The next level
* `chapter-11` **Chapter 11**: Authenticating users, managing sessions and securing APIs
* `chapter-12` **Chapter 12**: Using an authentication API in Angular applications

## Get the code to run on your machine

Pre-requisites:

* Git installed
* A command line interface capable of running Git commands
* Node v11 installed

To get the code for a specific branch:

`$ git clone -b branch-name https://github.com/cliveharber/gettingMean-2.git`

Then change into the folder the git clone command will create:

`$ cd getting-MEAN-2`

And finally install the dependencies:

`npm install`

## Getting the code via Docker

Pre-requisites:

* Docker

To get the code for a specific branch:

`$ git clone -b branch-name https://github.com/cliveharber/gettingMean-2.git`

Then change into the folder the git clone command will create:

`$ cd getting-MEAN-2`

And finally run the docker containers

`make build`

To remove the containers when complete

`make destroy`

44 changes: 44 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const favicon = require('serve-favicon');
const bodyParser = require('body-parser');
require('./app_api/models/db');

const indexRouter = require('./app_server/routes/index');
const apiRouter = require('./app_api/routes/index');

const app = express();

// view engine setup
app.set('views', path.join(__dirname, 'app_server', 'views'));
app.set('view engine', 'pug');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/api', apiRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});

// error handler
app.use(function(err, req, res) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};

// render the error page
res.status(err.status || 500);
res.render('error');
});

module.exports = app;
200 changes: 200 additions & 0 deletions app_api/controllers/locations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
const mongoose = require('mongoose');
const Loc = mongoose.model('Location');

const locationsListByDistance = async (req, res) => {
const lng = parseFloat(req.query.lng);
const lat = parseFloat(req.query.lat);
const near = {
type: "Point",
coordinates: [lng, lat]
};
const geoOptions = {
distanceField: "distance.calculated",
key: 'coords',
spherical: true,
maxDistance: 20000,
limit: 10
};
if (!lng || !lat) {
return res
.status(404)
.json({ "message": "lng and lat query parameters are required" });
}

try {
const results = await Loc.aggregate([
{
$geoNear: {
near,
...geoOptions
}
}
]);
const locations = results.map(result => {
return {
_id: result._id,
name: result.name,
address: result.address,
rating: result.rating,
facilities: result.facilities,
distance: `${result.distance.calculated.toFixed()}m`
}
});
res
.status(200)
.json(locations);
} catch (err) {
res
.status(404)
.json(err);
}
};

const locationsCreate = (req, res) => {
Loc.create({
name: req.body.name,
address: req.body.address,
facilities: req.body.facilities.split(","),
coords: {
type: "Point",
coordinates: [
parseFloat(req.body.lng),
parseFloat(req.body.lat)
]
},
openingTimes: [
{
days: req.body.days1,
opening: req.body.opening1,
closing: req.body.closing1,
closed: req.body.closed1
},
{
days: req.body.days2,
opening: req.body.opening2,
closing: req.body.closing2,
closed: req.body.closed2
}
]
},
(err, location) => {
if (err) {
res
.status(400)
.json(err);
} else {
res
.status(201)
.json(location);
}
});
};

const locationsReadOne = (req, res) => {
Loc
.findById(req.params.locationid)
.exec((err, location) => {
if (!location) {
return res
.status(404)
.json({"message": "location not found"});
} else if (err) {
return res
.status(404)
.json(err);
} else {
return res
.status(200)
.json(location);
}
});
};

const locationsUpdateOne = (req, res) => {
if (!req.params.locationid) {
return res
.status(404)
.json({
"message": "Not found, locationid is required"
});
}
Loc
.findById(req.params.locationid)
.select('-reviews -rating')
.exec((err, location) => {
if (!location) {
return res
.status(404)
.json({
"message": "locationid not found"
});
} else if (err) {
return res
.status(400)
.json(err);
}
location.name = req.body.name;
location.address = req.body.address;
location.facilities = req.body.facilities.split(',');
location.coords = [
parseFloat(req.body.lng),
parseFloat(req.body.lat)
];
location.openingTimes = [{
days: req.body.days1,
opening: req.body.opening1,
closing: req.body.closing1,
closed: req.body.closed1,
}, {
days: req.body.days2,
opening: req.body.opening2,
closing: req.body.closing2,
closed: req.body.closed2,
}];
location.save((err, loc) => {
if (err) {
res
.status(404)
.json(err);
} else {
res
.status(200)
.json(loc);
}
});
}
);
};

const locationsDeleteOne = (req, res) => {
const {locationid} = req.params;
if (locationid) {
Loc
.findByIdAndRemove(locationid)
.exec((err, location) => {
if (err) {
return res
.status(404)
.json(err);
}
res
.status(204)
.json(null);
}
);
} else {
res
.status(404)
.json({
"message": "No Location"
});
}
};

module.exports = {
locationsListByDistance,
locationsCreate,
locationsReadOne,
locationsUpdateOne,
locationsDeleteOne
};
Loading