Booking a meeting room has never been easier.
To install packages, please run the following command.
npm install
The database installation is performed using api/scripts/start.sh
script which is called automatically.
The database is fed using data files foundable in api/data
folder.
Please note in development mode, all API security guards are disabled.
npm run dev
Please note in production mode, all API security guards are enabled.
npm run prod
ESLint configuration extends airbnb-base. However, some rules have been modified.
npm run lint
Due to short time, only very few tests have been written. Those tests can be run using following command.
npm test
To get an HTML coverage report, following command can be used.
npm run test:coverage
To install packages, please run the following command.
npm install
To run development server, please run the following command.
npm run serve
To run development server, you will need serve
package. To install it, globally please run the following command.
npm i -g serve
Once serve
installed, you are able to run the production server using the following command.
npm run build && serve -s dist
ESLint configuration extends airbnb-base. However, some rules have been modified.
npm run lint
REST API has been developped using NodeJS and Express. It communicates with a Mongo database.
Postman documentation of the API can be found here (includes examples).
A Mongo database has been used. This database includes 4 models which can be found in api/src/models
. Each model contains static methods to verify data. Those methods are called by routes following needs.
Database calls are performed using helpers (api/src/helpers/mongo
). Those helpers are not dependant of the model so it adds a level of abstraction.
It is used in authentication system by client server.
Users model is refered in reservations model.
On database initialization, model is fed based on api/data/users.json
(password is: awesome).
It lists all equipments.
Equipments model is refered in rooms model.
On database initialization, model is fed based on api/data/rooms.json
.
It lists rooms.
Rooms model is refered in reservations model.
On database initialization, model is fed based on api/data/rooms.json
.
It lists all reservations and is the most trickiest model since it manipulates dates. Consequently, API routes referring to this model contain more logic than others.
The current API has 2 safety guards: cors and auth0.
The first level of securing corresponds to cors configuration which can be found in api/src/config/constants/cors.js
.
In production
mode, a white list is set. Only authorized URLs can hit the API.
In development
mode, this whitelist is by-passed.
The second level of securing corresponds to auth0 and scope.
When a user logs in, the API returns two JSON Web Token with expiration date, one is used to access the database (access-token) while the other one is consummed to refresh those tokens (refresh-token). Both token verification and creation are performed using helpers which can be found in api/src/helpers/jsonwebtoken
.
Moreover, all routes do not have the same authentication level. For example, to log in, no access token is required but to fetch rooms an access token is required (and verified by API). A third scope level is ADMIN
. If a route has ADMIN
level, it cannot be accessed in production
.
Both access-token and scope are verified using a middleware.
Resources:
- scope definition:
api/src/config/constants/scope.js
- jwt configuration:
api/src/config/constants/jwt.js
- jwt helpers:
api/src/helpers/jsonwebtoken
- middleware:
api/src/middlware/auth.js
Whatever the request, the response format is the same.
The API returns different HTTP status code depending on the following cases.
Everything went well.
User has tried to reach endpoint with an expired token.
User has tried to reach endpoint without correct level of authentication.
Route cannot be found.
Input data is missing or not well formatted.
Something went wrong on back-end side.
In case of HTML status code 200, the API always return data formatted as follow.
{
success: true | false,
payload: {
...
},
}
API
|- CONFIG : .env files
|- DATA : database feed
|- DATABASE : database itself (automatically generated)
|- SCRIPTS : scripts used on npm start
|- SRC
|- CONFIG : API configuration (including router)
|- HELPERS : mongo and jwt helpers
|- MIDDLEWARE : authentication middleware
|- MODELS : mongo models
|- ROUTES : API routes
|- AUTH : /auth [getRefresh]
|- EQUIPMENTS : /equipments [delete, get, post, put]
|- RESERVATIONS: /reservations [delete, get, post, put]
|- ROOMS : /rooms [delete, get, getSlots, post, put]
|- USERS : /users [get, getId, getLogin, post]
|- UTILS : some helpers
Client has been developped using VueJS.
In order to ease development, some libraries have been installed.
To ease date manipulation, momentjs
library has been used. Its documentation is here.
- moment
- moment-range
- moment-timezone
axios
library has been used to send request to API. It has been configured to refresh automatically request. Its document is here.
font-awesome
has been used for icons since it provides a wide range of easy-to-use icons.
vuematerial
UI library has been considered in first place. However, its still in construction and a lot of features was missing (documentation here). Consequently, I changed my mind and decided to use a - quite awesome - chinese library: muse-ui
(documentation in english here).
The application securing is based on 2 principles:
- navigation safeguard
- axios configuration
The application's router has a beforeEach
rule (can be found in client/src/components/routes/index.js
).
Before entering a page, user will go through the navigation guard which will check if the page requires an authentication and if user is authenticated.
Consequently, a user cannot enter login
page if he is already authenticated as he cannot enter dashboard
page if he is not authenticated.
axios
has been configured (client/src/services/axios
) with an interceptor on request and another one on response.
axios
will edit each outgoing request and set headers with a bearer token
based on the one stored in local storage
.
axios
will intercept each response incoming looking for errors. For each response error, it will look at http status code to assess if it is due to access-token
. If it is the case, it will ask the API to refresh the access-token
using the refresh-token
. If it is failed, user is redirected to login
page and his storage is cleared.
The application contains 4 routes (404 are redirected to /
).
Before accessing the booking system, each user has to be authenticated. Once authenticated, he will receive tokens used to fetch, modify, post data to the API.
User authentication is persistent (client/src/stores/modules/user/actions.js [ACTION_AUTOLOGIN]
).
User data is managed using global state (Vuex
).
If API is correctly setup, you can log in using dlaurent
or admin
as username
and awesome
as password.
Dashboard
page displays different statistics based on user passed reservations. Once logged in, user is redirected here.
Booking
page is the reservation module. User has to filter rooms by selecting at least a datetime and a duration. He can check dynamically available rooms.
Reservations
page permits the user to consult and delete his reservations.
Initially, project structure was based on components
vs views
model. However, I prefer a project structure based on features / routes so I changed project structure during the development.
CLIENT
|- PUBLIC : HTML and static assets such as favicon
|- SRC
|- ASSETS : images
|- COMPONENTS : all .vue files
|- NAVIGATION : header, footer
|- ROUTES : router + a folder by route
|- UI : components for design
|- EVENT_BUSES : communication between components that are not directly connected
|- MIXINS : just a mixin to modify page title, registered globally
|- SERVICES : contains axios configuration
|- STORE : user global state
|- UTILS