From 0b22a68cb807cbe48b4eec7c68ffdcfcc34fde69 Mon Sep 17 00:00:00 2001 From: Erwan Osouf Date: Fri, 29 Sep 2017 13:16:13 +0200 Subject: [PATCH 1/9] Add enpoint for import and upload page --- app/controllers/contacts.js | 10 ++++++++++ app/views/contactImport.hbs | 8 ++++++++ package.json | 3 ++- 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 app/views/contactImport.hbs diff --git a/app/controllers/contacts.js b/app/controllers/contacts.js index a08ab1f..cf32cab 100644 --- a/app/controllers/contacts.js +++ b/app/controllers/contacts.js @@ -11,6 +11,7 @@ module.exports = function (app) { router.get('/', function (req, res, next) { var query = {}; + // Search by all provided tags if (req.query.tagId) { query.tags = { $all : req.query.tagId }; } @@ -38,6 +39,15 @@ router.get('/edit/', function (req, res, next) { res.render('contactEdit', { contact : {} }); }); +router.get('/import/', function (req, res, next) { + res.render('contactImport', { contact : {} }); +}); + +router.post('/import/', function (req, res, next) { + console.log(req); + //res.render('contactImport', { contact : {} }); +}); + router.get('/edit/:contactId', function (req, res, next) { console.log('Editing contact'); var id = req.params.contactId; diff --git a/app/views/contactImport.hbs b/app/views/contactImport.hbs new file mode 100644 index 0000000..8177c44 --- /dev/null +++ b/app/views/contactImport.hbs @@ -0,0 +1,8 @@ +
+ + + +
diff --git a/package.json b/package.json index 636981e..bc40f9a 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "co": "^4.6.0", "compression": "^1.5.2", "cookie-parser": "^1.3.3", + "csv": "^1.1.1", "express": "^4.13.3", "express-handlebars": "^3.0.0", "glob": "^6.0.4", @@ -30,8 +31,8 @@ "debug": "^2.2.0", "gulp": "^3.9.0", "gulp-less": "^3.0.1", - "gulp-nodemon": "^2.0.2", "gulp-livereload": "^3.8.0", + "gulp-nodemon": "^2.0.2", "gulp-plumber": "^1.0.0", "mocha": "^3.0.2", "nyc": "^10.0.0", From a35078afbea0a1832fb219e838d700c0eb3cb4e7 Mon Sep 17 00:00:00 2001 From: Erwan Osouf Date: Sun, 1 Oct 2017 14:42:46 +0200 Subject: [PATCH 2/9] Add First draft of import and export scripts --- scripts/contacts-export.sh | 2 ++ scripts/contacts-import.sh | 6 ++++++ scripts/fields.txt | 8 ++++++++ scripts/post-process.js | 22 ++++++++++++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 scripts/contacts-export.sh create mode 100644 scripts/contacts-import.sh create mode 100644 scripts/fields.txt create mode 100644 scripts/post-process.js diff --git a/scripts/contacts-export.sh b/scripts/contacts-export.sh new file mode 100644 index 0000000..a7b3aa7 --- /dev/null +++ b/scripts/contacts-export.sh @@ -0,0 +1,2 @@ + +mongoexport -d $1 -c contacts --type csv --fieldFile fields.txt -o test.csv diff --git a/scripts/contacts-import.sh b/scripts/contacts-import.sh new file mode 100644 index 0000000..6d43dc3 --- /dev/null +++ b/scripts/contacts-import.sh @@ -0,0 +1,6 @@ + +# The file to import +mongoimport -d $1 -c contacts-import --type csv --fieldFile fields.txt --ignoreBlanks --file $2 + +# Run post-processing script on the mongo shell. +mongo localhost:27017/$1 post-process.js diff --git a/scripts/fields.txt b/scripts/fields.txt new file mode 100644 index 0000000..9c6f675 --- /dev/null +++ b/scripts/fields.txt @@ -0,0 +1,8 @@ +name.first +name.last +emails.0.value +phones.0.value +phones.1.value +organization +title + diff --git a/scripts/post-process.js b/scripts/post-process.js new file mode 100644 index 0000000..e94dc04 --- /dev/null +++ b/scripts/post-process.js @@ -0,0 +1,22 @@ +// Post processing script of Contacts import +var collection = db['contacts-import']; +collection.find({}).forEach(r => { + if (r.emails && r.emails['0']) { + r.emails = [r.emails[0]]; + } + if (r.phones) { + phones = []; + if (r.phones[0]) { + phones.push(r.phones[0]); + } + if (r.phones[1]) { + phones.push(r.phones[1]); + } + r.phones = phones; + } + r.meta = {}; + r.meta.creationDate = new Date(); + r.meta.modificationDate = new Date(); + collection.save(r); +}); + From 81d0310da67f6d7b2c5691c3e44a99468132bce8 Mon Sep 17 00:00:00 2001 From: Erwan Osouf Date: Sun, 1 Oct 2017 14:43:02 +0200 Subject: [PATCH 3/9] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 200de69..f3e066a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ node_modules/ -public/components +public/lib .nyc_output .sass-cache npm-debug.log From 7df0ddc0706cdc293055e8783296e801aa2f2b6f Mon Sep 17 00:00:00 2001 From: Erwan Osouf Date: Sun, 1 Oct 2017 17:19:31 +0200 Subject: [PATCH 4/9] Update script --- scripts/contacts-export.sh | 2 +- scripts/contacts-import.sh | 7 ++++--- scripts/post-process.js | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/contacts-export.sh b/scripts/contacts-export.sh index a7b3aa7..fdbb41e 100644 --- a/scripts/contacts-export.sh +++ b/scripts/contacts-export.sh @@ -1,2 +1,2 @@ - +#!/bin/sh mongoexport -d $1 -c contacts --type csv --fieldFile fields.txt -o test.csv diff --git a/scripts/contacts-import.sh b/scripts/contacts-import.sh index 6d43dc3..230728e 100644 --- a/scripts/contacts-import.sh +++ b/scripts/contacts-import.sh @@ -1,6 +1,7 @@ - -# The file to import -mongoimport -d $1 -c contacts-import --type csv --fieldFile fields.txt --ignoreBlanks --file $2 +#!/bin/sh +# $1 : database name +# $2 : input file to import +mongoimport -d $1 -c contacts-import --file $2 --type csv --fieldFile fields.txt --ignoreBlanks --drop # Run post-processing script on the mongo shell. mongo localhost:27017/$1 post-process.js diff --git a/scripts/post-process.js b/scripts/post-process.js index e94dc04..2bda942 100644 --- a/scripts/post-process.js +++ b/scripts/post-process.js @@ -1,6 +1,7 @@ // Post processing script of Contacts import var collection = db['contacts-import']; -collection.find({}).forEach(r => { +collection.find({}).forEach(r => { + // Make the e-mail an array instead of the imported object. if (r.emails && r.emails['0']) { r.emails = [r.emails[0]]; } @@ -19,4 +20,3 @@ collection.find({}).forEach(r => { r.meta.modificationDate = new Date(); collection.save(r); }); - From 2b785248b3c854950714ecabe6bf3d6b07886e18 Mon Sep 17 00:00:00 2001 From: Erwan Osouf Date: Sun, 1 Oct 2017 23:22:43 +0200 Subject: [PATCH 5/9] Fix #2 --- .gitignore | 4 ++ app/controllers/contacts.js | 9 --- app/controllers/import.js | 42 +++++++++++++ app/views/contactImport.hbs | 29 ++++++--- config/config.js | 30 +++++++--- config/db.js | 5 +- package.json | 1 + scripts/contacts-import.sh | 10 +++- scripts/fields.txt | 6 +- scripts/post-process.js | 29 +++++++-- scripts/test.sh | 4 ++ test/resources/test-import.csv | 1 + yarn.lock | 106 ++++++++++++++++++++++++++++----- 13 files changed, 226 insertions(+), 50 deletions(-) create mode 100644 app/controllers/import.js mode change 100644 => 100755 scripts/contacts-import.sh create mode 100755 scripts/test.sh create mode 100644 test/resources/test-import.csv diff --git a/.gitignore b/.gitignore index 5d4500a..cffed65 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ public/lib npm-debug.log public/css/style.css + +data/ +data/uploads/ +data/downloads/ diff --git a/app/controllers/contacts.js b/app/controllers/contacts.js index 3bc1baa..d26fb7a 100644 --- a/app/controllers/contacts.js +++ b/app/controllers/contacts.js @@ -60,15 +60,6 @@ router.get('/edit/', function (req, res, next) { res.render('contactEdit', { contact : {} }); }); -router.get('/import/', function (req, res, next) { - res.render('contactImport', { contact : {} }); -}); - -router.post('/import/', function (req, res, next) { - console.log(req); - //res.render('contactImport', { contact : {} }); -}); - router.get('/edit/:contactId', function (req, res, next) { console.log('Editing contact'); var id = req.params.contactId; diff --git a/app/controllers/import.js b/app/controllers/import.js new file mode 100644 index 0000000..8077afb --- /dev/null +++ b/app/controllers/import.js @@ -0,0 +1,42 @@ +var express = require('express') + , router = express.Router() + , upload = require('multer')({ dest: 'data/uploads/' }) + , conf = require('../../config/config') + , path = require('path') + , db = conf.db + , cwd = conf.root + , execFile = require('child_process').execFile + , scriptDir = path.join(cwd, 'scripts/') + , script = path.join(scriptDir, 'contacts-import.sh'); + +// Exports a function to bind Controller +module.exports = function (app) { + app.use('/import', router); +}; + +router.get('/', function (req, res, next) { + res.render('contactImport', { title : 'Import de contact' }); +}); + +router.post('/', upload.single('upload'), function (req, res, next) { + if (!req.file) { + res.render('contactImport', { title : 'Import de contact', message : 'Fichier manquant' }); + return; + } + var filename = path.join(cwd, req.file.path) + , options = { + cwd : scriptDir, + env : db + }; + execFile(script, [ filename ], options, (error, stdout, stderr) => { + console.log(stdout); + console.log(stderr); + + if (error) { + next(error); + return; + } + + res.redirect("/contacts"); + }); +}); diff --git a/app/views/contactImport.hbs b/app/views/contactImport.hbs index 8177c44..ffca5a0 100644 --- a/app/views/contactImport.hbs +++ b/app/views/contactImport.hbs @@ -1,8 +1,23 @@ -
- - - + + Uploader un fichier CSV, encodé en UTF-8, sans ligne d'en-tête, séparateur ",".
+ Les colonnes sont : +
    +
  • Prénom
  • +
  • Nom
  • +
  • E-mail
  • +
  • Téléphone 1
  • +
  • Téléphone 2
  • +
  • Organisation
  • +
  • Fonction
  • +
  • N° de Rue
  • +
  • Rue
  • +
  • Code Postal
  • +
  • Ville
  • +
  • Note
  • +
+
+{{#if message }}
{{message}}
{{/if}} + + +
diff --git a/config/config.js b/config/config.js index 6f632d8..9790976 100644 --- a/config/config.js +++ b/config/config.js @@ -10,8 +10,13 @@ var config = { }, port: process.env.PORT || 3000, db : { - url : 'mongodb://localhost:27017/contacts-manager-development', - poolSize : 10 + database : 'contacts-manager-development', + host : 'localhost', + port : '27017', + options : { + poolSize : 10 + } + } }, @@ -22,8 +27,12 @@ var config = { }, port: process.env.PORT || 3000, db : { - url : 'mongodb://localhost:27017/contacts-manager-test', - poolSize : 10 + database : 'contacts-manager-test', + host : 'localhost', + port : '27017', + options : { + poolSize : 10 + } } }, @@ -34,11 +43,16 @@ var config = { }, port: process.env.PORT || 3000, db : { - url : 'mongodb://localhost:27017/contacts-manager-production', - poolSize : 10, - config : { - autoIndex : false + database : 'contacts-manager-production', + host : 'localhost', + port : '27017', + options : { + poolSize : 10, + config : { + autoIndex : false + } } + } } }; diff --git a/config/db.js b/config/db.js index 80d0125..b3edcd9 100644 --- a/config/db.js +++ b/config/db.js @@ -5,11 +5,10 @@ var conf = require('./config').db, module.exports = function() { // Connection URL - var url = conf.url; - delete conf.url; + var url = 'mongodb://' + conf.host + ':' + conf.port + '/' + conf.database; // Set default Promise mongoose.Promise = global.Promise; - return mongoose.connect(url, conf); + return mongoose.connect(url, conf.options); }; diff --git a/package.json b/package.json index 4eae4bf..921756e 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "mongodb": "^2.2.25", "mongoose": "^4.9.1", "morgan": "^1.6.1", + "multer": "^1.3.0", "passport": "^0.4.0", "passport-http": "^0.3.0", "serve-favicon": "^2.3.0" diff --git a/scripts/contacts-import.sh b/scripts/contacts-import.sh old mode 100644 new mode 100755 index 230728e..75f0156 --- a/scripts/contacts-import.sh +++ b/scripts/contacts-import.sh @@ -1,7 +1,11 @@ #!/bin/sh -# $1 : database name # $2 : input file to import -mongoimport -d $1 -c contacts-import --file $2 --type csv --fieldFile fields.txt --ignoreBlanks --drop +mongoimport -d $database -c contacts-import --file $1 --type csv --fieldFile fields.txt --ignoreBlanks --drop +echo $? # Run post-processing script on the mongo shell. -mongo localhost:27017/$1 post-process.js +mongo $host:$port/$database post-process.js + +echo $? + +rm $1 diff --git a/scripts/fields.txt b/scripts/fields.txt index 9c6f675..d5449ba 100644 --- a/scripts/fields.txt +++ b/scripts/fields.txt @@ -5,4 +5,8 @@ phones.0.value phones.1.value organization title - +adresses.0.number +adresses.0.street +adresses.0.code +adresses.0.city +note diff --git a/scripts/post-process.js b/scripts/post-process.js index 2bda942..6b36b6e 100644 --- a/scripts/post-process.js +++ b/scripts/post-process.js @@ -1,9 +1,18 @@ // Post processing script of Contacts import -var collection = db['contacts-import']; -collection.find({}).forEach(r => { - // Make the e-mail an array instead of the imported object. +var source = db['contacts-import'] + , target = db.contacts + , imports = db.imports + , count = 0 + , currentImport = { meta : { date : new Date() }} + , importId; + +importId = imports.insertOne(currentImport).insertedId; +currentImport._id = importId; + +source.find({}).forEach(r => { + // Transforms imported objects into arrays. if (r.emails && r.emails['0']) { - r.emails = [r.emails[0]]; + r.emails = [r.emails['0']]; } if (r.phones) { phones = []; @@ -15,8 +24,18 @@ collection.find({}).forEach(r => { } r.phones = phones; } + if (r.adresses && r.adresses['0']) { + r.adresses = [r.adresses['0']]; + } + // Set meta fields. r.meta = {}; r.meta.creationDate = new Date(); r.meta.modificationDate = new Date(); - collection.save(r); + r.meta.importId = importId; + delete r._id; + target.save(r); + count++; }); + +currentImport.meta.count = count; +imports.save(currentImport); diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..6fa8c2f --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,4 @@ +pwd +echo $1 +echo $2 +cat $2 diff --git a/test/resources/test-import.csv b/test/resources/test-import.csv new file mode 100644 index 0000000..8512dc1 --- /dev/null +++ b/test/resources/test-import.csv @@ -0,0 +1 @@ +"Elliot","Alderson","elliot.alderson@fsociety.com","01.02.03.04.05","06.07.08.09.10","fsociety","Lead Hacker","123", "Washington Street", "10028", "New York", "A slight addiction to hardcore drugs" diff --git a/yarn.lock b/yarn.lock index 2b3ab3d..eba23bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -291,6 +291,10 @@ anymatch@^1.3.0: micromatch "^2.1.5" normalize-path "^2.0.0" +append-field@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/append-field/-/append-field-0.1.0.tgz#6ddc58fa083c7bc545d3c5995b2830cc2366d44a" + append-transform@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" @@ -659,6 +663,13 @@ builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" +busboy@^0.2.11: + version "0.2.14" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" + dependencies: + dicer "0.2.5" + readable-stream "1.1.x" + bytes@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.2.0.tgz#fd35464a403f6f9117c2de3609ecff9cae000588" @@ -884,6 +895,14 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" +concat-stream@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + configstore@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.1.tgz#094ee662ab83fad9917678de114faaea8fcdca90" @@ -988,6 +1007,29 @@ crypto-random-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" +csv-generate@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/csv-generate/-/csv-generate-1.0.0.tgz#bd52886859d0c925f3e51f60f3abed262fa15caf" + +csv-parse@^1.2.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-1.2.3.tgz#466206b51eaf77ccb50c3fadebd098cdeb2c69e7" + +csv-stringify@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-1.0.4.tgz#bc18bab9ad4cef3195fd257980b58b479c42d3e5" + dependencies: + lodash.get "^4.0.0" + +csv@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/csv/-/csv-1.1.1.tgz#d9952d59b1f964a7afbcdd804d6818a73199a477" + dependencies: + csv-generate "^1.0.0" + csv-parse "^1.2.0" + csv-stringify "^1.0.0" + stream-transform "^0.1.0" + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -1120,6 +1162,13 @@ detect-indent@^4.0.0: dependencies: repeating "^2.0.0" +dicer@0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" + dependencies: + readable-stream "1.1.x" + streamsearch "0.1.2" + diff@3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" @@ -2140,7 +2189,7 @@ inherits@1: version "1.0.2" resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -2678,6 +2727,10 @@ lodash.foreach@^4.1.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" +lodash.get@^4.0.0: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + lodash.includes@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" @@ -3072,6 +3125,19 @@ ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" +multer@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/multer/-/multer-1.3.0.tgz#092b2670f6846fa4914965efc8cf94c20fec6cd2" + dependencies: + append-field "^0.1.0" + busboy "^0.2.11" + concat-stream "^1.5.0" + mkdirp "^0.5.1" + object-assign "^3.0.0" + on-finished "^2.3.0" + type-is "^1.6.4" + xtend "^4.0.0" + multipipe@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" @@ -3279,7 +3345,7 @@ object.pick@^1.2.0, object.pick@^1.3.0: dependencies: isobject "^3.0.1" -on-finished@~2.3.0: +on-finished@^2.3.0, on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" dependencies: @@ -3603,6 +3669,15 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" +readable-stream@1.1.x, readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + readable-stream@2.2.7: version "2.2.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.7.tgz#07057acbe2467b22042d36f98c5ad507054e95b1" @@ -3624,7 +3699,7 @@ readable-stream@2.2.7: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5: +readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2: version "2.3.3" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" dependencies: @@ -3636,15 +3711,6 @@ readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable string_decoder "~1.0.3" util-deprecate "~1.0.1" -readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - readdirp@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" @@ -4107,6 +4173,14 @@ stream-consume@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.0.tgz#a41ead1a6d6081ceb79f65b061901b6d8f3d1d0f" +stream-transform@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/stream-transform/-/stream-transform-0.1.2.tgz#7d8e6b4e03ac4781778f8c79517501bfb0762a9f" + +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -4363,13 +4437,17 @@ type-detect@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2" -type-is@~1.6.10, type-is@~1.6.15: +type-is@^1.6.4, type-is@~1.6.10, type-is@~1.6.15: version "1.6.15" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" dependencies: media-typer "0.3.0" mime-types "~2.1.15" +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + typeof-article@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/typeof-article/-/typeof-article-0.1.1.tgz#9f07e733c3fbb646ffa9e61c08debacd460e06af" @@ -4637,7 +4715,7 @@ xdg-basedir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" -"xtend@>=4.0.0 <4.1.0-0", xtend@~4.0.1: +"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" From e2dc0c15d8c636e2bf9412667d13c1fb061d750c Mon Sep 17 00:00:00 2001 From: Erwan Osouf Date: Sun, 1 Oct 2017 23:22:43 +0200 Subject: [PATCH 6/9] Fix #2 --- scripts/fields.txt | 8 ++++---- scripts/post-process.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/fields.txt b/scripts/fields.txt index d5449ba..028420f 100644 --- a/scripts/fields.txt +++ b/scripts/fields.txt @@ -5,8 +5,8 @@ phones.0.value phones.1.value organization title -adresses.0.number -adresses.0.street -adresses.0.code -adresses.0.city +addresses.0.number +addresses.0.street +addresses.0.code +addresses.0.city note diff --git a/scripts/post-process.js b/scripts/post-process.js index 6b36b6e..3371a17 100644 --- a/scripts/post-process.js +++ b/scripts/post-process.js @@ -24,8 +24,8 @@ source.find({}).forEach(r => { } r.phones = phones; } - if (r.adresses && r.adresses['0']) { - r.adresses = [r.adresses['0']]; + if (r.addresses && r.addresses['0']) { + r.addresses = [r.addresses['0']]; } // Set meta fields. r.meta = {}; From 9959cc6aba18c8449cabe34b4b6748c0d05be665 Mon Sep 17 00:00:00 2001 From: Erwan Osouf Date: Sun, 1 Oct 2017 23:27:46 +0200 Subject: [PATCH 7/9] Update export --- scripts/contacts-export.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/contacts-export.sh b/scripts/contacts-export.sh index fdbb41e..504113b 100644 --- a/scripts/contacts-export.sh +++ b/scripts/contacts-export.sh @@ -1,2 +1,2 @@ #!/bin/sh -mongoexport -d $1 -c contacts --type csv --fieldFile fields.txt -o test.csv +mongoexport -d $database -c contacts --type csv --fieldFile fields.txt -o $1 From e919a027ab2d3c2d63400cdf7328a7b05127b0c0 Mon Sep 17 00:00:00 2001 From: Erwan Osouf Date: Tue, 3 Oct 2017 23:50:46 +0200 Subject: [PATCH 8/9] Add signup --- app/controllers/contacts.js | 3 +- app/controllers/home.js | 32 ++++++++++-- app/controllers/import.js | 3 +- app/controllers/tags.js | 3 +- app/controllers/users.js | 62 +++++++++++++++++++++++ app/models/User.js | 93 ++++++++++++++++++++++++++++++++++ app/views/{ => home}/index.hbs | 0 app/views/home/login.hbs | 15 ++++++ app/views/home/signup.hbs | 19 +++++++ app/views/users/userView.hbs | 22 ++++++++ config/auth.js | 67 +++++++++++++++++++++--- config/express.js | 24 ++++++--- package.json | 3 ++ yarn.lock | 38 ++++++++++++++ 14 files changed, 363 insertions(+), 21 deletions(-) create mode 100644 app/controllers/users.js create mode 100644 app/models/User.js rename app/views/{ => home}/index.hbs (100%) create mode 100644 app/views/home/login.hbs create mode 100644 app/views/home/signup.hbs create mode 100644 app/views/users/userView.hbs diff --git a/app/controllers/contacts.js b/app/controllers/contacts.js index d26fb7a..fe9351b 100644 --- a/app/controllers/contacts.js +++ b/app/controllers/contacts.js @@ -1,12 +1,13 @@ var express = require('express') , co = require('co') , router = express.Router() + , ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn , Contact = require('../models/Contact') , Tag = require('../models/Tag'); // Exports a function to bind Controller module.exports = function (app) { - app.use('/contacts', router); + app.use('/contacts', ensureLoggedIn('/login'), router); }; router.get('/', function (req, res, next) { diff --git a/app/controllers/home.js b/app/controllers/home.js index 87b339b..f3eccd2 100644 --- a/app/controllers/home.js +++ b/app/controllers/home.js @@ -1,12 +1,38 @@ var express = require('express'), - router = express.Router(); + router = express.Router(), + auth = require('../../config/auth'); module.exports = function (app) { app.use('/', router); }; router.get('/', function (req, res, next) { - res.render('index', { - title : 'Contact Manager' + res.render('home/index', { + title : 'Contacts Manager' }); }); + +router.route('/login') + .get(function (req, res, next) { + res.render('home/login', { + title : 'Contacts Manager - Login' + }); + }) + .post(auth.authenticate('local', { + successRedirect: '/contacts', + failureRedirect: '/login' + })); + +router.get('/logout', function (req, res, next) { + req.logout(); + res.render('home/index', { + title : 'Contacts Manager - Logout' + }); +}); + +router.route('/signup') + .get(function (req, res, next) { + res.render('home/signup', { + title : 'Contacts Manager - Sign Up' + }); + }) diff --git a/app/controllers/import.js b/app/controllers/import.js index 8077afb..3040c9f 100644 --- a/app/controllers/import.js +++ b/app/controllers/import.js @@ -1,5 +1,6 @@ var express = require('express') , router = express.Router() + , ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn , upload = require('multer')({ dest: 'data/uploads/' }) , conf = require('../../config/config') , path = require('path') @@ -11,7 +12,7 @@ var express = require('express') // Exports a function to bind Controller module.exports = function (app) { - app.use('/import', router); + app.use('/import', ensureLoggedIn('/login'), router); }; router.get('/', function (req, res, next) { diff --git a/app/controllers/tags.js b/app/controllers/tags.js index 03d50ef..1b86150 100644 --- a/app/controllers/tags.js +++ b/app/controllers/tags.js @@ -1,10 +1,11 @@ var express = require('express') , router = express.Router() + , ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn , Tag = require('../models/Tag'); // Exports a function to bind Controller module.exports = function (app) { - app.use('/tags', router); + app.use('/tags', ensureLoggedIn('/login'), router); }; router.get('/edit/', function (req, res, next) { diff --git a/app/controllers/users.js b/app/controllers/users.js new file mode 100644 index 0000000..068ce03 --- /dev/null +++ b/app/controllers/users.js @@ -0,0 +1,62 @@ +var express = require('express') + , co = require('co') + , ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn + , router = express.Router() + , User = require('../models/User') + , Tag = require('../models/Tag'); + +// Exports a function to bind Controller +module.exports = function (app) { + app.use('/users', router); +}; + +function userView(req, res, next, id) { + console.log('Displaying user ' + id); + + co(function* () { + var user, data = {}; + + user = yield user.findById(id).exec(); + + data.user = user; + return data; + }).then(data => { + data.title = 'Profil ' + data.user.username; + res.render('users/userView', data); + }).catch(err => { next(err); }); +} + +router.route('/') + .get(function (req, res, next) { + res.redirect('me'); + }) + .post(function (req, res, next) { + console.log('Submitting User '); + var user = new User(); + user.set({ + username : req.body.username, + email : req.body.email, + phone : req.body.phone, + organization : req.body.organization, + title : req.body.title, + meta : { + // Disable self registration of user for now. + disabled : new Date() + } + }); + user.save().then(model => { + res.redirect(model.id) + }) + .catch(err => { + next(err); + }); + }); + +router.get('/:userId', ensureLoggedIn('/login'), function (req, res, next) { + var id = req.params.userId; + userView(id); +}); + +router.get('/me', function (req, res, next) { + res.redirect(req.user._id); +}); diff --git a/app/models/User.js b/app/models/User.js new file mode 100644 index 0000000..19b9368 --- /dev/null +++ b/app/models/User.js @@ -0,0 +1,93 @@ +var mongoose = require('mongoose') + , Schema = mongoose.Schema + , Phone = require('./Phone') + , Email = require('./Email'); +// Base Schema +// TODO Factor with Contact as BaseEntity ? +// TODO Using ES6 Classes as definition ? +var schema = new Schema({ + username : {type : String, required : true, trim : true, unique : true }, +/* hash : {type : String, required : true, trim : true }, + name : { + last : { type: String , trim : true }, + first : {type : String, required : true, trim : true } + }, */ + organization : {type : String, trim : true }, + title : {type : String, trim : true }, + emails : [Email.schema], + phones : [Phone.schema], + meta : { + disabled : { type : Date } + } +}, { + collection : 'users', + timestamps: { + createdAt : 'meta.creationDate', + updatedAt : 'meta.modificationDate' + } +}); + +// Add Virtuals +schema.virtual('fullName'). + get(function () { + var name = this.name || {}, fullName = name.first; + if (name.last) { + fullName += ' ' + name.last; + } + if (name.prefix) { + fullName = name.prefix + fullName; + } + if (name.suffix) { + fullName += ', ' + name.suffix; + } + return fullName; + }). + set(function(v) { + // TODO Enhance for suffix. + this.name = this.name || {}; + var idx = v.indexOf(' '); + if (idx == -1) { + this.name.first = v; + } else { + this.name.first = v.substr(0, idx); + this.name.last = v.substr(idx + 1); + } +}); + +schema.virtual('email').get(function() { + var mails = this.emails; + if (mails && mails.length > 0) { + return mails[0].value; + } + return null; +}).set(function(v) { + var mails = this.emails; + if (!mails) { + this.emails = mails = []; + } + if (mails.length == 0){ + mails.push(new Email()); + } + mails[0].value = v; +}); + +schema.virtual('phone').get(function() { + var phones = this.phones; + if (phones && phones.length > 0) { + return phones[0].value; + } + return null; +}).set(function(v) { + var phones = this.phones; + if (!phones) { + phones = []; + } + if (phones.length == 0){ + phones.push(new Phone()); + } + phones[0].value = v; +}); + +var User = mongoose.model('User', schema); + +module.exports = User; diff --git a/app/views/index.hbs b/app/views/home/index.hbs similarity index 100% rename from app/views/index.hbs rename to app/views/home/index.hbs diff --git a/app/views/home/login.hbs b/app/views/home/login.hbs new file mode 100644 index 0000000..bb59cba --- /dev/null +++ b/app/views/home/login.hbs @@ -0,0 +1,15 @@ + +{{#if message }}
{{message}}
{{/if}} +
+
+ + +
+
+ + +
+
+ +
+
diff --git a/app/views/home/signup.hbs b/app/views/home/signup.hbs new file mode 100644 index 0000000..4c3d6c4 --- /dev/null +++ b/app/views/home/signup.hbs @@ -0,0 +1,19 @@ + +{{#if message }}
{{message}}
{{/if}} +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
diff --git a/app/views/users/userView.hbs b/app/views/users/userView.hbs new file mode 100644 index 0000000..1dba0a2 --- /dev/null +++ b/app/views/users/userView.hbs @@ -0,0 +1,22 @@ +{{#with user}} + + + + + + + + + + + + + + + + +
Username{{ username }}
Mail{{ email }}
Téléphone{{ phone }}
Organisation{{ organization }}
Fonction{{ title }}
+
+ +
+{{/with}} diff --git a/config/auth.js b/config/auth.js index f45fd21..ed9fac0 100644 --- a/config/auth.js +++ b/config/auth.js @@ -1,25 +1,76 @@ // Auth conf var passport = require('passport'), BasicStrategy = require('passport-http').BasicStrategy, - // Really not secure but hey, we'll implement something later. - credentials = { - username : 'bccontacts', - password : 't€stc0ntact$' - }, configure; + LocalStrategy = require('passport-local').Strategy, + User = require('../app/models/User'), + configureBasic, + configureLocal; /** * Configure the authentication mechanism and return the passport middleware used. */ -configure = function() { - var checker = function(username, password, done) { +configureBasic = function() { + var credentials, checker; + // Really not secure but hey, we'll implement something later. + credentials = { + username : 'bccontacts', + password : 't€stc0ntact$' + }, + checker = function(username, password, done) { console.log("Authenticating " + username + " " + password); if (credentials.username === username && credentials.password === password) { return done(null, credentials); } return done(null, false); }, basicAuth = new BasicStrategy(checker); + + passport.use(basicAuth); return passport; }; -module.exports = configure; +/** +* Configure the authentication mechanism and return the passport middleware used. +*/ +configureLocal = function() { + var checker = function(req, username, password, done) { + console.log("[LocalStrategy] Authenticating " + username + " " + password); + // check in mongo if a user with username exists or not + User.findOne({ 'username' : username }, + function(err, user) { + // In case of any error, return using the done method + if (err) + return done(err); + // Username does not exist, log error & redirect back + if (!user){ + console.log('User Not Found with username ' + username); + return done(null, false); + } + // User exists but wrong password, log the error + // TODO + + // User and password both match, return user from + // done method which will be treated like success + return done(null, user); + } + ); + }; + + passport.use(new LocalStrategy({ + passReqToCallback : true + }, checker)); + + passport.serializeUser(function(user, done) { + done(null, user._id); + }); + + passport.deserializeUser(function(id, done) { + User.findById(id, function(err, user) { + done(err, user); + }); + }); + + return passport; +}; + +module.exports = configureLocal(); diff --git a/config/express.js b/config/express.js index e20ac58..6f91431 100644 --- a/config/express.js +++ b/config/express.js @@ -1,20 +1,27 @@ var express = require('express'); var glob = require('glob'); -var favicon = require('serve-favicon'); +// var favicon = require('serve-favicon'); var logger = require('morgan'); + +// Parsers var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); + var compress = require('compression'); var methodOverride = require('method-override'); +// Handlebars var exphbs = require('express-handlebars'); var helpers = require('handlebars-helpers')(['collection', 'array']); +var session = require('express-session'); + // Auth conf -var auth = require('./auth')(); +var auth = require('./auth'); // Exports a configuration function. module.exports = function(app, config) { var env = process.env.NODE_ENV || 'development'; + var secret = 'secretkey'; app.locals.ENV = env; app.locals.ENV_DEVELOPMENT = env == 'development'; @@ -35,13 +42,19 @@ module.exports = function(app, config) { app.use(bodyParser.urlencoded({ extended: true })); - app.use(cookieParser()); + app.use(cookieParser(secret)); + app.use(session({ + secret: secret, + saveUninitialized : false, + resave : false + })); + app.use(compress()); app.use(express.static(config.root + '/public')); app.use(methodOverride()); app.use(auth.initialize()); - app.use(auth.authenticate('basic', { session: false })); + app.use(auth.session()); // Register all controllers var controllers = glob.sync(config.root + '/app/controllers/*.js'); @@ -75,8 +88,5 @@ module.exports = function(app, config) { }); }); } - - - return app; }; diff --git a/package.json b/package.json index 921756e..8e7a15a 100644 --- a/package.json +++ b/package.json @@ -14,10 +14,12 @@ "body-parser": "^1.13.3", "co": "^4.6.0", "compression": "^1.5.2", + "connect-ensure-login": "^0.1.1", "cookie-parser": "^1.3.3", "csv": "^1.1.1", "express": "^4.13.3", "express-handlebars": "^3.0.0", + "express-session": "^1.15.6", "glob": "^6.0.4", "handlebars": "^4.0.6", "handlebars-helpers": "^0.9.8", @@ -28,6 +30,7 @@ "multer": "^1.3.0", "passport": "^0.4.0", "passport-http": "^0.3.0", + "passport-local": "^1.0.0", "serve-favicon": "^2.3.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index eba23bd..438840b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -914,6 +914,10 @@ configstore@^3.0.0: write-file-atomic "^2.0.0" xdg-basedir "^3.0.0" +connect-ensure-login@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/connect-ensure-login/-/connect-ensure-login-0.1.1.tgz#174dcc51243b9eac23f8d98215aeb6694e2e8a12" + console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" @@ -961,6 +965,10 @@ core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" +crc@3.4.4: + version "3.4.4" + resolved "https://registry.yarnpkg.com/crc/-/crc-3.4.4.tgz#9da1e980e3bd44fc5c93bf5ab3da3378d85e466b" + create-error-class@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" @@ -1327,6 +1335,20 @@ express-handlebars@^3.0.0: object.assign "^4.0.3" promise "^7.0.0" +express-session@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.15.6.tgz#47b4160c88f42ab70fe8a508e31cbff76757ab0a" + dependencies: + cookie "0.3.1" + cookie-signature "1.0.6" + crc "3.4.4" + debug "2.6.9" + depd "~1.1.1" + on-headers "~1.0.1" + parseurl "~1.3.2" + uid-safe "~2.1.5" + utils-merge "1.0.1" + express@^4.13.3: version "4.16.1" resolved "https://registry.yarnpkg.com/express/-/express-4.16.1.tgz#6b33b560183c9b253b7b62144df33a4654ac9ed0" @@ -3461,6 +3483,12 @@ passport-http@^0.3.0: dependencies: passport-strategy "1.x.x" +passport-local@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/passport-local/-/passport-local-1.0.0.tgz#1fe63268c92e75606626437e3b906662c15ba6ee" + dependencies: + passport-strategy "1.x.x" + passport-strategy@1.x.x: version "1.0.0" resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" @@ -3617,6 +3645,10 @@ qs@~6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" +random-bytes@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" + randomatic@^1.1.3: version "1.1.7" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" @@ -4471,6 +4503,12 @@ uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" +uid-safe@~2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a" + dependencies: + random-bytes "~1.0.0" + unc-path-regex@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" From 5e772373cc9d62b64c2a3f8c2213b6d5c3eb48a9 Mon Sep 17 00:00:00 2001 From: Erwan Osouf Date: Fri, 6 Oct 2017 19:59:09 +0200 Subject: [PATCH 9/9] Phat commit, auth + signup --- app/controllers/contacts.js | 9 +-- app/controllers/home.js | 19 +++-- app/controllers/import.js | 2 +- app/controllers/tags.js | 8 +-- app/controllers/users.js | 45 ++++++++---- app/models/Profile.js | 20 ++++++ app/models/User.js | 34 +++++++-- app/views/{layouts => _layouts}/main.hbs | 7 ++ .../contactFilters.hbs | 0 app/views/_partials/message.hbs | 5 ++ app/views/{partials => _partials}/tag.hbs | 0 app/views/{partials => _partials}/tagEdit.hbs | 0 app/views/_partials/user.hbs | 10 +++ app/views/{partials => _partials}/welcome.hbs | 0 app/views/{ => contacts}/contactEdit.hbs | 0 app/views/{ => contacts}/contactList.hbs | 0 app/views/{ => contacts}/contactView.hbs | 0 app/views/home/login.hbs | 11 +-- app/views/home/signup.hbs | 19 ----- app/views/{ => import}/contactImport.hbs | 0 app/views/{ => tags}/tagEdit.hbs | 0 app/views/{ => tags}/tagList.hbs | 0 app/views/{ => tags}/tagView.hbs | 0 app/views/users/userEdit.hbs | 16 +++++ app/views/users/userView.hbs | 6 ++ config/auth.js | 70 +++---------------- config/express.js | 14 +++- package.json | 2 + public/css/style.less | 8 +++ scripts/activate-user.js | 1 + scripts/activate-user.sh | 7 ++ scripts/contacts-import.sh | 3 +- yarn.lock | 24 ++++++- 33 files changed, 217 insertions(+), 123 deletions(-) create mode 100644 app/models/Profile.js rename app/views/{layouts => _layouts}/main.hbs (88%) rename app/views/{partials => _partials}/contactFilters.hbs (100%) create mode 100644 app/views/_partials/message.hbs rename app/views/{partials => _partials}/tag.hbs (100%) rename app/views/{partials => _partials}/tagEdit.hbs (100%) create mode 100644 app/views/_partials/user.hbs rename app/views/{partials => _partials}/welcome.hbs (100%) rename app/views/{ => contacts}/contactEdit.hbs (100%) rename app/views/{ => contacts}/contactList.hbs (100%) rename app/views/{ => contacts}/contactView.hbs (100%) delete mode 100644 app/views/home/signup.hbs rename app/views/{ => import}/contactImport.hbs (100%) rename app/views/{ => tags}/tagEdit.hbs (100%) rename app/views/{ => tags}/tagList.hbs (100%) rename app/views/{ => tags}/tagView.hbs (100%) create mode 100644 app/views/users/userEdit.hbs create mode 100644 scripts/activate-user.js create mode 100644 scripts/activate-user.sh diff --git a/app/controllers/contacts.js b/app/controllers/contacts.js index fe9351b..d7134c8 100644 --- a/app/controllers/contacts.js +++ b/app/controllers/contacts.js @@ -53,12 +53,12 @@ router.get('/', function (req, res, next) { } data.size = size; data.title = 'Liste de Contact'; - res.render('contactList', data); + res.render('contacts/contactList', data); }).catch(err => { next(err); }); }); router.get('/edit/', function (req, res, next) { - res.render('contactEdit', { contact : {} }); + res.render('contacts/contactEdit', { contact : {} }); }); router.get('/edit/:contactId', function (req, res, next) { @@ -67,7 +67,7 @@ router.get('/edit/:contactId', function (req, res, next) { console.log('id :' + id); Contact.findById(id).populate('tags').exec(). then(data => { - res.render('contactEdit', { + res.render('contacts/contactEdit', { contact : data }); }). @@ -87,6 +87,7 @@ router.get('/:contactId', function (req, res, next) { }).exec(); contact.tags.forEach(tag => { ids.push(tag._id) }); + // Load other tags for edition tags = yield Tag.find({ _id : { $nin : ids }}).sort('name').exec(); data.contact = contact; @@ -94,7 +95,7 @@ router.get('/:contactId', function (req, res, next) { return data; }).then(data => { data.title = 'Fiche Contact ' + data.contact.fullName; - res.render('contactView', data); + res.render('contacts/contactView', data); }).catch(err => { next(err); }); }); diff --git a/app/controllers/home.js b/app/controllers/home.js index f3eccd2..4248ea1 100644 --- a/app/controllers/home.js +++ b/app/controllers/home.js @@ -14,13 +14,22 @@ router.get('/', function (req, res, next) { router.route('/login') .get(function (req, res, next) { - res.render('home/login', { + var data = { title : 'Contacts Manager - Login' - }); + }, flash = req.flash(); + console.log(flash); + if (flash && flash.error) { + data.message = { + level : 'error', + message : flash.error[0] + } + } + res.render('home/login', data); }) .post(auth.authenticate('local', { successRedirect: '/contacts', - failureRedirect: '/login' + failureRedirect: '/login', + failureFlash: true })); router.get('/logout', function (req, res, next) { @@ -32,7 +41,7 @@ router.get('/logout', function (req, res, next) { router.route('/signup') .get(function (req, res, next) { - res.render('home/signup', { + res.render('users/userEdit', { title : 'Contacts Manager - Sign Up' }); - }) + }); diff --git a/app/controllers/import.js b/app/controllers/import.js index 3040c9f..a430c34 100644 --- a/app/controllers/import.js +++ b/app/controllers/import.js @@ -16,7 +16,7 @@ module.exports = function (app) { }; router.get('/', function (req, res, next) { - res.render('contactImport', { title : 'Import de contact' }); + res.render('import/contactImport', { title : 'Import de contact' }); }); router.post('/', upload.single('upload'), function (req, res, next) { diff --git a/app/controllers/tags.js b/app/controllers/tags.js index 1b86150..b15f480 100644 --- a/app/controllers/tags.js +++ b/app/controllers/tags.js @@ -9,7 +9,7 @@ module.exports = function (app) { }; router.get('/edit/', function (req, res, next) { - res.render('tagEdit', { tag : {} }); + res.render('tags/tagEdit', { tag : {} }); }); router.get('/edit/:tagId', function (req, res, next) { @@ -18,7 +18,7 @@ router.get('/edit/:tagId', function (req, res, next) { console.log('id :' + id); Tag.findById(id).exec(). then(data => { - res.render('tagEdit', { + res.render('tags/tagEdit', { tag : data }); }). @@ -29,7 +29,7 @@ router.get('/', function (req, res, next) { console.log('Listing tags'); var tags = Tag.find({}).sort('name').exec(); tags.then(data => { - res.render('tagList', { + res.render('tags/tagList', { title : 'Liste d\'etiquettes', tags : data }); @@ -42,7 +42,7 @@ router.get('/:tagId', function (req, res, next) { console.log('id :' + id); Tag.findById(id).exec(). then(data => { - res.render('tagView', { + res.render('tags/tagView', { tag : data }); }). diff --git a/app/controllers/users.js b/app/controllers/users.js index 068ce03..e0e63bc 100644 --- a/app/controllers/users.js +++ b/app/controllers/users.js @@ -16,7 +16,7 @@ function userView(req, res, next, id) { co(function* () { var user, data = {}; - user = yield user.findById(id).exec(); + user = yield User.findById(id).exec(); data.user = user; return data; @@ -28,13 +28,19 @@ function userView(req, res, next, id) { router.route('/') .get(function (req, res, next) { - res.redirect('me'); + console.log(req.user); + console.log(req.session.user); + res.redirect('/users/me'); }) .post(function (req, res, next) { console.log('Submitting User '); var user = new User(); user.set({ username : req.body.username, + name : { + first : req.body.firstname, + last : req.body.lastname + }, email : req.body.email, phone : req.body.phone, organization : req.body.organization, @@ -44,19 +50,34 @@ router.route('/') disabled : new Date() } }); - user.save().then(model => { - res.redirect(model.id) - }) - .catch(err => { - next(err); + User.register(user, req.body.password, function(err) { + var data; + if (err) { + console.log(err); + data = req.body || {}; + data.message = { + level : 'error', + message : err.message + } + res.render('users/userEdit', data); + return; + } + + console.log('user registered!'); + + res.redirect('/'); }); }); -router.get('/:userId', ensureLoggedIn('/login'), function (req, res, next) { - var id = req.params.userId; - userView(id); +router.get('/me', function (req, res, next) { + var data = { + user : req.user, + title : 'Profil ' + req.user.username + }; + res.render('users/userView', data); }); -router.get('/me', function (req, res, next) { - res.redirect(req.user._id); +router.get('/:userId', ensureLoggedIn('/login'), function (req, res, next) { + var id = req.params.userId; + userView(req, res, next, id); }); diff --git a/app/models/Profile.js b/app/models/Profile.js new file mode 100644 index 0000000..09329e8 --- /dev/null +++ b/app/models/Profile.js @@ -0,0 +1,20 @@ +var mongoose = require('mongoose') + , Schema = mongoose.Schema + , Profile; + +// Base Schema +var schema = new Schema({ + name : { type: String, required : true, trim : true }, + roles : [{ type: String, trim : true }] +}, { + collection : 'profiles', + timestamps: { + createdAt : 'meta.creationDate', + updatedAt : 'meta.modificationDate' + } +}); + +Profile = mongoose.model('Profile', schema); +Profile.schema = schema; + +module.exports = Profile; diff --git a/app/models/User.js b/app/models/User.js index 19b9368..b66bb3a 100644 --- a/app/models/User.js +++ b/app/models/User.js @@ -1,21 +1,22 @@ var mongoose = require('mongoose') , Schema = mongoose.Schema , Phone = require('./Phone') - , Email = require('./Email'); + , Email = require('./Email') + , Profile = require('./Profile') + , passportLocalMongoose = require('passport-local-mongoose'); // Base Schema // TODO Factor with Contact as BaseEntity ? // TODO Using ES6 Classes as definition ? var schema = new Schema({ - username : {type : String, required : true, trim : true, unique : true }, -/* hash : {type : String, required : true, trim : true }, name : { last : { type: String , trim : true }, first : {type : String, required : true, trim : true } - }, */ + }, organization : {type : String, trim : true }, title : {type : String, trim : true }, emails : [Email.schema], phones : [Phone.schema], + profile : Profile.schema, meta : { disabled : { type : Date } } @@ -27,6 +28,31 @@ var schema = new Schema({ } }); +// +schema.plugin(passportLocalMongoose, { + saltField : 'meta.salt', + hashField : 'password', + attemptsField : 'meta.attempts', + lastLoginField : 'meta.lastLogin', + populateFields : 'profile', + usernameUnique : false, + findByUsername: function(model, queryParameters) { + // Add additional query parameter - AND condition - active: true + queryParameters["meta.disabled"] = null; + return model.findOne(queryParameters); + }, + errorMessages : { + MissingPasswordError : 'Mot de passe manquant', + AttemptTooSoonError : 'Trop d\'essais manqué, veuillez ré-essayer plus tard', + TooManyAttemptsError : 'Trop d\'essais manqué, veuillez ré-essayer plus tard', + NoSaltValueStoredError : 'Authentication not possible. No salt value stored', + IncorrectPasswordError : 'Nom d\'utilisateur ou mot de passe incorrect', + IncorrectUsernameError : 'Nom d\'utilisateur ou mot de passe incorrect', + MissingUsernameError : 'Nom d\'utilisateur manquant', + UserExistsError : 'Un utilisateur avec ce nom existe déja' + } +}); + // Add Virtuals schema.virtual('fullName'). get(function () { diff --git a/app/views/layouts/main.hbs b/app/views/_layouts/main.hbs similarity index 88% rename from app/views/layouts/main.hbs rename to app/views/_layouts/main.hbs index 62909b4..f7980e5 100644 --- a/app/views/layouts/main.hbs +++ b/app/views/_layouts/main.hbs @@ -30,10 +30,17 @@
  • Ajouter
  • +
  • + Importer +
  • + {{>user}}
    + {{#with message }} + {{>message}} + {{/with }} {{{body}}}