diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f7c4ce5bc56f5b194d657b5e2c42e442b5f14d1b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +yarn.lock +.vscode +config.json \ No newline at end of file diff --git a/README.md b/README.md index dad5fca12728ecc3dfdd944028561b65e3981619..b5fe36890ac97d64fc55681dcc380131404275e3 100644 --- a/README.md +++ b/README.md @@ -1 +1,25 @@ -# RoleGame \ No newline at end of file +# RoleGame + +## Dependencies + +### Used +* `express`: Fast, unopinionated, minimalist web framework +* `bcrypt`: A bcrypt library for NodeJS. +* `express-session`: Simple session middleware for Express +* `pug`: A clean, whitespace-sensitive template language for writing HTML +* `mongoose`: Mongoose MongoDB ODM +* `body-parser`: Node.js body parsing middleware +* `morgan`: HTTP request logger middleware for node.js + +### To use +* `joi`: Object schema description language and validator for JavaScript objects. + +## Release + +### Next: v0.1.0 +* [ ] Authentication and session +* [ ] Validate mongoose models +* [ ] Make mongoose unique index work + +### Next: v0.2.0 +* [ ] Message handler diff --git a/config.json.template b/config.json.template new file mode 100644 index 0000000000000000000000000000000000000000..9b502de2b8b1236ffbde948bbf3e4e8450386fd5 --- /dev/null +++ b/config.json.template @@ -0,0 +1,5 @@ +{ + "port": Number, + "secret": String, + "cryptRounds": Number +} \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000000000000000000000000000000000000..d307bc5d409eec01a6c3049a08337f6eff3090c6 --- /dev/null +++ b/index.js @@ -0,0 +1,82 @@ +const express = require('express'); +const session = require('express-session'); +const bodyParser = require('body-parser'); +const User = require('./models/user'); +const mongoose = require('mongoose'); +const bcrypt = require('bcrypt'); +const morgan = require('morgan'); + +const config = require('./config.json'); + +// Utils +const render = (req, res, view, options) => res.render(view, { + ...options, + user: req.session.user, + nextUrl: req.url, +}); + +// Configuration +const app = express(); +app.set('view engine', 'pug'); +app.use(morgan('tiny')); +app.use(session({ + secret: config.secret, + resave: false, + saveUninitialized: false +})); + +// Middlewares +app.use(bodyParser.urlencoded({ + extended: false, +})); +app.use((req, res, next) => { + if (req.session.user || ['/', '/signup', '/login'].includes(req.url)) { + next(); + } else { + return res.redirect('/signup'); + } +}); + +app.get('/', (req, res) => { + return render(req, res, 'home'); +}); +app.get('/signup', (req, res) => { + return render(req, res, 'signup'); +}); +app.post('/signup', (req, res) => { + const passwordHash = bcrypt.hashSync(req.body.password, config.cryptRounds); + User.create(req.body, (err, user) => { + err ? console.error(err) : null; + user.passwordHash = passwordHash; + user.save(); + req.session.user = user; + return res.redirect('/'); + }); +}); +app.post('/login', (req, res) => { + User.findOne({ username: req.body.username }, (err, user) => { + err ? console.error(err) : null; + if (bcrypt.compareSync(req.body.password, user.passwordHash)) { + req.session.user = user; + return res.redirect(req.query.nextUrl || '/'); + } else { + console.error("Bad authentication"); + return res.redirect('/signup'); + } + }); +}); +app.post('/logout', (req, res) => { + req.session.destroy(); + return res.redirect('/'); +}) + +mongoose.connect('mongodb://localhost/rolegame', err => { + if (err) { + console.error('ERROR Unable to connect to Mongo database') + } else { + console.log('Server connected to Mongo database'); + } + app.listen(config.port, () => { + console.log(`Server listening on http://localhost:${config.port}`); + }); +}); diff --git a/models/character.js b/models/character.js new file mode 100644 index 0000000000000000000000000000000000000000..95853d2b3d31cb6259b7101a57e0af77485c918a --- /dev/null +++ b/models/character.js @@ -0,0 +1,8 @@ +const mongoose = require('mongoose'); + +const Character = new mongoose.Schema({ + name: String, + level: Number, +}); + +module.exports = mongoose.model('Character', Character); \ No newline at end of file diff --git a/models/user.js b/models/user.js new file mode 100644 index 0000000000000000000000000000000000000000..fdb9dc2587a5453ea56b85e9a19e301f93f3c045 --- /dev/null +++ b/models/user.js @@ -0,0 +1,25 @@ +const mongoose = require('mongoose'); + +const User = new mongoose.Schema({ + firstName: { + type: String, + required: true, + }, + lastName: { + type: String, + }, + username: { + unique: true, + type: String, + lowercase: true + }, + email: { + type: String, + required: true, + }, + passwordHash: { + type: String, + }, +}); + +module.exports = mongoose.model('User', User); \ No newline at end of file diff --git a/models/weapon.js b/models/weapon.js new file mode 100644 index 0000000000000000000000000000000000000000..1c74f2e8e7c46689a1d9ce16181ad26a970a2e96 --- /dev/null +++ b/models/weapon.js @@ -0,0 +1,8 @@ +const mongoose = require('mongoose'); + +const Weapon = new mongoose.Schema({ + name: String, + required_level: Number +}); + +module.exports = mongoose.model('Weapon', Weapon); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000000000000000000000000000000000000..e3b0f5265bcfe6b94893fabefb7e5623309b5744 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "scripts": { + "dev": "nodemon index.js" + }, + "dependencies": { + "bcrypt": "^3.0.4", + "body-parser": "^1.18.3", + "eslint": "^5.13.0", + "express": "^4.16.4", + "express-session": "^1.15.6", + "mongoose": "^5.4.11", + "morgan": "^1.9.1", + "nodemon": "^1.18.10", + "pug": "^2.0.3" + } +} diff --git a/views/base.pug b/views/base.pug new file mode 100644 index 0000000000000000000000000000000000000000..38855d3c86dc03a07458dd4ec5681663723f9595 --- /dev/null +++ b/views/base.pug @@ -0,0 +1,36 @@ +<!DOCTYPE html> +html(lang="en") + head + meta(charset="UTF-8") + meta(name="viewport", content="width=device-width, initial-scale=1.0") + meta(http-equiv="X-UA-Compatible", content="ie=edge") + link(rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css" integrity="sha256-9mbkOfVho3ZPXfM7W8sV2SndrGDuh7wuyLjtsWeTI1Q=" crossorigin="anonymous") + title RoleGame + body + block navbar + if !user + form.ui.form(action="/login", method="post") + .ui.pointing.menu + .right.menu + .item + .ui.input.transparent + input#username(type="text" name="username" placeholder="username") + .item + .ui.transparent.input + input#password(type="password" name="password" placeholder="password") + .item + .ui.transparent.input + input.ui.button(type="submit" value="Se connecter") + else + form.ui.form(action="/logout", method="post") + .ui.pointing.menu + .right.menu + .item #{user.firstName} #{user.lastName} + .item + button.ui.icon.button.basic(type="submit") + i.power.off.icon + .ui.container + block main + block script + script(src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha256-3edrmyuQ0w65f8gfBsqowzjJe2iM6n0nKciPUp8y+7E=" crossorigin="anonymous") + script(src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js" integrity="sha256-t8GepnyPmw9t+foMh3mKNvcorqNHamSKtKRxxpUEgFI=" crossorigin="anonymous") \ No newline at end of file diff --git a/views/home.pug b/views/home.pug new file mode 100644 index 0000000000000000000000000000000000000000..c227ddd8f33b019c79860ce241c0298e9b527bbe --- /dev/null +++ b/views/home.pug @@ -0,0 +1,4 @@ +extends base.pug + +block main + h1.ui.header Bienvenue sur RoleGame diff --git a/views/signup.pug b/views/signup.pug new file mode 100644 index 0000000000000000000000000000000000000000..afcd9076e903308f8f223e5feeec21b0c4b8e80c --- /dev/null +++ b/views/signup.pug @@ -0,0 +1,22 @@ +extends base.pug + +block main + form.ui.form(action="/signup" method="post") + .two.fields + .field + label(for="firstName") Prénom + input#firstName(type="text", name="firstName") + .field + label(for="lastName") Nom de famille + input#lastName(type="text", name="lastName") + .field + label(for="email") Email + input#email(type="email", name="email") + .field + label(for="username") Nom d'utilisateur + input#username(type="text", name="username") + .field + label(for="password") Mot de passe + input#password(type="password", name="password") + .field + input.ui.button(type="submit", value="S'inscrire") \ No newline at end of file