Commit 47f6036a authored by Xysto's avatar Xysto 💬
Browse files

init express

parent 56fa662a
# server
# ExpressJS Starter Kit
Serveur NodeJS principal
\ No newline at end of file
This project suggest a MVC (model - view - controller) architecture for an ExpressJS project.
It uses MongoDB to store data as well as Nunjucks for templating.
## Project structure
```
|-- app.js (main script to start the app)
|-- package.json (metadata and dependencies)
|-- gulpfile.js (specify files to watch)
|-- models
|-- db.js (to connect to database)
|-- users.js (define users contenttype)
|-- controllers
|-- index.js (for homepage)
|-- users.js (for users contenttype)
|-- views
|-- users (templates for users contenttype)
|-- partials (templates called with 'include')
|-- layout (templates called with 'extends')
|-- something.html (any template used by Nunjucks)
|-- ...
|-- public
|-- assets (any css, js or img that need to be publicly accessible)
```
## Installation
You must have nodejs and mongodb installed on your system, verify with :
`node --version && mongo --version`
Go into root folder and enter the following line in your terminal :
`npm install`
Install gulp (and associated components) :
`npm install --global gulp-cli gulp-rename gulp-clean-css gulp-minify`
## Launch project
You may start the project with
`node app`
or use Gulp automation so that any modifications to files will reload the app and minify assets :
`gulp`
Your project is accessible at [localhost:3000](http://localhost:3000).
## About design
The administrative panel template is from [Ad.min](https://github.com/Mikescops/ad.min) which is a minimal admin template.
## Various documentation
- ExpressJS : https://expressjs.com/en/starter/hello-world.html
- Mongoose (mongodb object modeling for nodejs) : https://mongoosejs.com/docs/
- Nunjucks (mozilla templating for nodejs) : https://mozilla.github.io/nunjucks/templating.html
- GulpJS (development automation) : https://gulpjs.com/
- FontAwesome (icons font) : https://fontawesome.com/icons
- Kacole2's skeleton (inspiration of this project) : https://git.io/fxQXK
## Contributing
Feel free to contribute to this project, fork and pull request your ideas.
Don't include work that is not open source or not from you.
## Authors
| [![twitter/mikescops](https://avatars0.githubusercontent.com/u/4266283?s=100&v=4)](http://twitter.com/mikescops "Follow @mikescops on Twitter") |
|---|
| [Corentin Mors](https://pixelswap.fr/) |
\ No newline at end of file
var express = require('express'),
app = express(),
port = process.env.PORT || 3000,
path = require('path'),
favicon = require('serve-favicon'),
logger = require('morgan'),
cookieParser = require('cookie-parser'),
bodyParser = require('body-parser');
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
// Load DB
var db = require('./models/db');
var personnages = require('./models/users');
// Load templating and statics
app.use(express.static(path.join(__dirname, 'public')));
var nunjucks = require('nunjucks');
nunjucks.configure('views', {
autoescape: true,
express: app
});
app.set('view engine', 'html');
// Call controllers
app.use(require('./controllers'))
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
err.message = 'The page ' + req.hostname + req.originalUrl + ' could not be found on this website.';
next(err);
});
app.listen(port, function() {
console.log('Listening on port ' + port)
})
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
title: 'Error ' + err.status,
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
title: 'Error ' + err.status,
message: err.message,
error: {}
});
});
module.exports = app;
\ No newline at end of file
var express = require('express'),
router = express.Router(),
mongoose = require('mongoose');
router.use('/users', require('./users'))
router.get('/', function(req, res) {
mongoose.model('Users').find({}, function (err, users) {
if (err) {
return console.error(err);
} else {
//respond to both HTML and JSON. JSON responses require 'Accept: application/json;' in the Request Header
res.format({
//HTML response will render the users/index.html
html: function(){
res.render('index', { users: users });
},
//JSON response will show all users in JSON format
json: function(){
res.json(users);
}
});
}
});
})
module.exports = router
\ No newline at end of file
var express = require('express'),
router = express.Router(),
mongoose = require('mongoose');
router.route('/')
//GET all users
.get(function(req, res, next) {
//retrieve all users from Monogo
mongoose.model('Users').find({}, function (err, users) {
if (err) {
return console.error(err);
} else {
//respond to both HTML and JSON. JSON responses require 'Accept: application/json;' in the Request Header
res.format({
//HTML response will render the users/index.html
html: function(){
res.render('./users/list', {
title: 'Users list',
users: users
});
},
//JSON response will show all users in JSON format
json: function(){
res.json(users);
}
});
}
});
})
//POST a new user
.post(function(req, res) {
// Get values from POST request. These can be done through forms or REST calls. These rely on the "name" attributes for forms
var name = req.body.name;
var email = req.body.email;
//call the create function for our database
mongoose.model('Users').create({
_id : new mongoose.Types.ObjectId(),
name : name,
email : email,
}, function (err, user) {
if (err) {
res.send("There was a problem adding the information to the database.");
} else {
//user has been created
console.log('POST creating new user: ' + user);
res.format({
//HTML response will set the location and redirect back to the home page. You could also create a 'success' page if that's your thing
html: function(){
// If it worked, set the header so the address bar doesn't still say /adduser
res.location("users");
// And forward to success page
res.redirect("/users");
},
//JSON response will show the newly created user
json: function(){
res.json(user);
}
});
}
})
});
router.route('/:id')
.get(function(req, res) {
mongoose.model('Users').findById(req.params.id, function (err, user) {
if (err) {
console.log('GET Error: There was a problem retrieving: ' + err);
} else {
console.log('GET Retrieving ID: ' + user._id);
res.format({
html: function(){
res.render('users/view', {
title: 'View of ' + user.name,
user : user
});
},
json: function(){
res.json(user);
}
});
}
});
});
router.route('/:id/edit')
//GET the individual user by Mongo ID
.get(function(req, res) {
//search for the user within Mongo
mongoose.model('Users').findById(req.params.id, function (err, user) {
if (err) {
console.log('GET Error: There was a problem retrieving: ' + err);
} else {
//Return the user
console.log('GET Retrieving ID: ' + user._id);
res.format({
//HTML response will render the 'edit.jade' template
html: function(){
res.render('users/edit', {
title: 'Edit user #' + user._id,
user : user
});
},
//JSON response will return the JSON output
json: function(){
res.json(user);
}
});
}
});
})
//POST to update a user by ID
.post(function(req, res) {
// Get our REST and form values.
var id = req.params.id;
var name = req.body.name;
var email = req.body.email;
console.log(req.body);
//find the document by ID
mongoose.model('Users').findByIdAndUpdate(id, {
name : name,
email : email,
}, function (err, user) {
if (err) {
res.send("There was a problem updating the information to the database: " + err);
}
else {
//HTML responds by going back to the page or you can be fancy and create a new view that shows a success page.
res.format({
html: function(){
res.redirect("/users/" + user._id);
},
//JSON responds showing the updated values
json: function(){
res.json(user);
}
});
}
})
});
router.route('/:id/delete')
//DELETE a Users by ID
.get(function (req, res){
//find user by ID
mongoose.model('Users').findById(req.params.id, function (err, user) {
if (err) {
return console.error(err);
} else {
//remove it from Mongo
user.remove(function (err, user) {
if (err) {
return console.error(err);
} else {
//Returning success messages saying it was deleted
console.log('DELETE removing ID: ' + user._id);
res.format({
//HTML returns us back to the main page, or you can create a success page
html: function(){
res.redirect("/users");
},
//JSON returns the item with the message that is has been deleted
json: function(){
res.json({message : 'deleted',
item : user
});
}
});
}
});
}
});
});
module.exports = router
\ No newline at end of file
var gulp = require('gulp'),
spawn = require('child_process').spawn,
minify = require('gulp-minify'),
cleanCSS = require('gulp-clean-css'),
rename = require('gulp-rename'),
node;
const build = ['minify-css', 'minify-js', 'server'],
run = ['server'],
minifyCSS = ['minify-css'],
minifyJS = ['minify-js'];
/**
* $ gulp server
* description: launch the server. If there's a server already running, kill it
*/
gulp.task('server', function() {
if (node) node.kill()
node = spawn('node', ['app.js'], {stdio: 'inherit'})
node.on('close', function (code) {
if (code === 8) {
gulp.log('Error detected, waiting for changes...');
}
});
})
/**
* $ gulp
* description: start the development environment
*/
gulp.task('default', build, function() {
gulp.watch(['./public/assets/css/*.css'], minifyCSS);
gulp.watch(['./public/assets/js/*.js'], minifyJS);
gulp.watch(['./app.js', './**/*.js', './**/*.html'], build);
});
/**
* $ gulp minify-js
* description: compress all js files and output them in the dist folder
*/
gulp.task('minify-js', function() {
gulp.src('public/assets/js/*.js')
.pipe(minify({
noSource : true,
ext:{
src:'-debug.js',
min:'.min.js'
},
ignoreFiles: ['.min.js']
}))
.pipe(gulp.dest('public/assets/dist'))
});
/**
* $ gulp minify-css
* description: compress all css files and output them in the dist folder
*/
gulp.task('minify-css', () => {
return gulp.src('public/assets/css/*.css')
.pipe(cleanCSS({compatibility: 'ie8'}))
.pipe(rename({
suffix: '.min'
}))
.pipe(gulp.dest('public/assets/dist'));
});
// clean up if an error goes unhandled.
process.on('exit', function() {
if (node) node.kill()
})
\ No newline at end of file
var mongoose = require('mongoose');
mongoose.set('useFindAndModify', false); // This prevent current mongoose depreciation warning
mongoose.connect('mongodb://localhost/starterkit', {useNewUrlParser: true });
\ No newline at end of file
var mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ObjectId = Schema.ObjectId;
const userSchema = new Schema({
_id: ObjectId,
name: String,
email: String
});
mongoose.model('Users', userSchema);
\ No newline at end of file
{
"name": "expressjs-starter-kit",
"version": "0.0.1",
"description": "A starter kit to kickstart your expressjs projects.",
"dependencies": {
"body-parser": "^1.10.2",
"cookie-parser": "~1.3.3",
"debug": "^4.1.0",
"express": "^4.16.4",
"mongoose": "^5.3.7",
"morgan": "^1.9.1",
"nunjucks": "^3.1.3",
"serve-favicon": "^2.5.0"
}
}
/** GLOBAL CSS */
body {
padding-top: 5rem;
}
/** INDEX CSS */
.contenttype-title{
display: flex;
}
.contenttype-title h3{
flex: 1 0;
}
.contenttype-all-button{
margin-top: 5px;
}
/** LIST CSS */
.list-group-item-flex{
display: flex;
}
.list-group-item-tools{
display: block;
width: 30px;
}
.list-group-item-image{
margin-right: 10px;
}
.list-group-item-content{
flex: 1 0;
}
/** EDIT CSS */
.content-input{
height: 500px;
}
\ No newline at end of file
body{padding-top:5rem}.contenttype-title{display:flex}.contenttype-title h3{flex:1 0}.contenttype-all-button{margin-top:5px}.list-group-item-flex{display:flex}.list-group-item-tools{display:block;width:30px}.list-group-item-image{margin-right:10px}.list-group-item-content{flex:1 0}.content-input{height:500px}
\ No newline at end of file
{% extends "layout/base.html" %}
{% block content %}
<main role="main" class="container">
<h2>{{ title }}</h2>
<p>{{ message }}</p>
</main><!-- /.container -->
{% endblock %}
\ No newline at end of file
{% extends "layout/base.html" %}
{% block content %}
<main role="main" class="container-fluid">
<div class="row">
<div class="col-sm">
<div class="contenttype-title">
<h3>Users</h3>
</div>
<div class="list-group">
{% for user in users %}
<a href="users/{{ user._id }}" class="list-group-item list-group-item-action">{{user.name}}</a>
{% endfor %}
</div>
<a href="/users" class="contenttype-all-button float-right">View all users <i class="fas fa-chevron-circle-right"></i></a>
</div>
<div class="col-sm">
<!-- Future contenttype -->
</div>
<div class="col-sm">
<!-- Future contenttype -->
</div>
</div>
</main><!-- /.container -->
{% endblock %}
\ No newline at end of file
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="ExpressJS + MongoDB + Nunjucks starter kit to kickstart your projects">
<meta name="author" content="">
<link rel="icon" href="favicon.ico">
<title>{% if title %}{{ title }} - StarterKit{% else %}Dashboard - StarterKit{% endif %}</title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<!-- Fontawesome core CSS -->
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous">
<!-- Custom styles for this template -->
<link href="/assets/dist/style.min.css" rel="stylesheet">
</head>
<body><nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
<a class="navbar-brand" href="/"><i class="fas fa-cubes"></i> StarterKit</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarMain" aria-controls="navbarMain" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarMain">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="/users"><i class="fas fa-user"></i> Users</a>
</li>
<li class="nav-item">
<a class="nav-link" target="_blank" href="https://pixelswap.fr/"><i class="far fa-question-circle"></i> About</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="http://example.com" id="dropdown-tools" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fas fa-toolbox"></i> Tools</a>
<div class="dropdown-menu" aria-labelledby="dropdown-tools">
<a class="dropdown-item" href="#">Edit content</a>