lib/user.js
var models = require('../models')
, Paths = models.paths
, Users = models.users
, File = models.file
, Movies = models.movies
, Albums = models.albums
, Others = models.others
, _ = require('underscore')
, bcrypt = require('bcrypt-nodejs')
, isObjectId = require('./helpers').isObjectId
, async = require('async')
var debug = require('debug')('ezseed:database:user')
var db = {
files: require('./files'),
paths: require('./paths')
}
var user = {
exists: function(username, done) {
Users.count({username: username}, function(err, count) {
if(count)
done(true)
else
done(false)
})
},
/**
* Get paths by user
* @param {ObjectId|String} value
* @param {Object} options
* @param {Function} done
*/
paths: function(value, options, done) {
done = typeof options == 'function' ? options : done
options = typeof options == 'function' ? {} : options
var populate_options = {
path: 'paths'
}
if(options.default && isObjectId(options.default))
populate_options.match = { _id: options.default }
if(options.paths) {
debug('only choosen paths')
if(typeof options.paths == 'string') {
options.paths = [options.paths]
}
populate_options.match = { _id : { $in: options.paths } }
}
if(isObjectId(value)) {
Users
.findById(value, '-hash')
.populate(populate_options)
.lean(true).exec(done)
} else {
Users
.findOne({username:value}, '-hash')
.populate(populate_options)
.lean(true).exec(done)
}
},
reset: function(uid, options, done) {
if(typeof options == 'function') {
done = options
options = {}
}
this.paths(uid, options, function(err, docs) {
if(err) return done(err)
async.each(docs.paths, function(item, cb) {
return db.paths.reset(item._id, cb)
}, function(err) {
//returns paths that have been reset
done(err, docs)
})
})
},
/**
* [files description]
* @param {[type]} uid [description]
* @param Object options {default: _id, limit, sort etc.}
* @param {Function} done [description]
* @return {[type]} [description]
*/
files: function (uid, params, done) {
this.paths(uid, params, function(err, docs) {
if(err) {
return done(err)
} else if(!docs) {
return done(new Erro('No docs'))
}
var last_update = params.last_update ? params.last_update : 0
var match = typeof params.match == 'object' ? params.match : {}
match.dateAdded = {"$gt":last_update}
var options = {}
options.limit = params.limit ? params.limit : 0
options.skip = params.skip ? params.skip : 0
options.sort = params.sort ? params.sort : {dateAdded: 1}
if(match.movieType == null)
delete match.movieType
//get a search string
if(match.search) {
//require helper that will pre-work the string (S01E02 => season/episode)
var search = require('./search')(match.search)
delete match.search //we don't want this anymore
for(var i in search) {
for(var j in match) {
if(j !== 'movieType') {
search[i][j] = match[j]
} else if(i == 'movies') {
search[i][j] = match[j]
}
}
}
}
debug('Files query', match, options)
var population = {
movies: {
path: 'paths.movies',
model: Movies,
match: search ? search.movies : match,
options: options
},
albums: {
path: 'paths.albums',
model: models.albums,
match: search ? search.albums : match,
options: options
},
others: {
path: 'paths.others',
model: models.others,
match: search ? search.others : match,
options: options
}
}
var populate_array = []
if(options.type) {
populate_array.push(population[options.type])
} else {
populate_array = [population.movies, population.albums, population.others]
}
Paths.populate(docs,
populate_array,
function(err, docs) {
async.parallel([
function(next) {
Movies.populate(
docs,
[
{
path: 'paths.movies.videos',
model: File
},
{
path: 'paths.movies.infos',
model: models.scrapper
}
],
next
)
},
function(next) {
Albums.populate(docs, {path: 'paths.albums.songs', model: File}, next)
},
function(next) {
Others.populate(docs, {path: 'paths.others.files', model: File}, next)
}
], function(err, results) {
docs.paths = _.extend(docs.paths, {
movies: results[0],
albums: results[1],
others: results[2]
})
done(null, docs)
})
})
})
},
/**
* add user to dabase
* @param {Object} u username, password, client, role
* @callback {Function} done fn(err, user)
*/
create: function(u, done) {
var password = u.password, username = u.username
//Generates the hash
bcrypt.hash(password, null, null, function(err, hash) {
//We save only the hash
// var user = new Users ({
// username: username,
// role: 'admin',
// client: client,
// hash: hash
// })
//We are doing this test because a random user could do `ezseed useradd` to update it
Users.findOne({username: username}, function (err, doc){
if(doc) {
doc.role = u.role ? u.role: 'user'
doc.hash = hash
doc.client = u.client ? u.client: 'aucun'
} else {
doc = new Users ({
username: username,
role: u.role ? u.role: 'user',
hash: hash,
client: u.client ? u.client: 'none',
port: u.port ? u.port : 0,
})
}
doc.save(function(err) {
if(err) {
//Checking for the username validation - see models/index.js
//Le nom d'utilisateur ne peut contenir que des caractères alphanumériques et des tirets
if(err.name == 'ValidationError')
done("username is not valid", null)
else
done(err, null)
} else
done(null, doc)
})
})
})
},
login: function(user, done) {
if(!user.password || !user.username) {
return done('Missing password or username')
}
debug(user.username + ' is logging in')
this.get(user.username, function(err, dbuser) {
if(!dbuser) {
return done('User not found')
}
debug(dbuser.username + ' has been found')
bcrypt.compare(user.password, dbuser.hash, function(err, result) {
if(result === true) {
return done(null, dbuser.session)
} else {
return done('Password does not match')
}
})
})
},
delete: function(username, done) {
Users.findOne({username: username}, function(err, user) {
if(user) {
//this deletes paths if they are not watched by any other user
db.paths.remove(user.paths, {not: user._id}, function(err, paths) {
if(err)
return done(err)
Users.findByIdAndRemove(user._id, function(err) {
done(err, paths)
})
})
} else {
done(new Error('user ' + username + ' does not exists'))
}
})
},
/**
* update user
* @param {mixed} value ObjectId or username
* @param {Object} update Update {key:value}
* @callback {Function} cb
*/
update: function(value, update, done) {
var hash_password = function(update, cb) {
if(update.password !== undefined) {
bcrypt.hash(update.password, null, null, function(err, hash) {
update.hash = hash
delete update.password
cb(update)
})
} else {
cb(update)
}
}
hash_password(update, function(update) {
if(isObjectId(value)) {
Users.findByIdAndUpdate(value, update, null, done)
} else {
Users.findOneAndUpdate({username:value}, update, null, done)
}
})
},
update_path: function(path, options, done) {
if(!options)
throw new Error('No username or id')
if(!path._id || !isObjectId(path._id)) {
return done(new Error('Path must have an ObjectId'))
}
var update = { $addToSet: {paths: path._id} }
if(options.default === true)
update.default_path = path._id
this.update(options._id || options.username, update, function(err) {
if(err)
return done(err)
done(null, path)
})
},
remove_path: function(id_path, user, done) {
if(!isObjectId(id_path)) {
return done(new Error('Path must be an ObjectId'))
}
this.update(user, {$pull : {paths : id_path}}, done)
},
get: function(value, done) {
if(isObjectId(value))
Users.findById(value, done)
else
Users.findOne({username: value}, done)
},
//to be remove
// byUsername: function(username, done) {
// Users.findOne({username: username}, done)
// },
// byId: function(uid, done) {
// Users.findById(uid, done)
// },
// //todo remove this update update is there for that
// setClient: function(username, client, done) {
// Users.findOneAndUpdate({username: username}, {client: client}, done)
// },
// setSpaceLeft: function(id, left, done) {
// Users.findByIdAndUpdate(id, {spaceLeft: left}, done)
// }
// //Reset user database is a mess
// reset: function(uid, done) {
// var cleanPath = function(err, results) {
// if(err)
// return cb(err)
// db.paths.byUser(uid, function(err, map) {
// async.each(
// map.docs.paths,
// function(path,callback){
// //Deleting each file id, previously saved inside the path object
// Paths.findByIdAndUpdate(path._id, {others: [], movies: [], albums: []}, function(err) {
// callback(err)
// })
// },
// function(err){
// done(err)
// }
// )
// })
// }
// db.files.byUser(uid, 0, {}, function(err, docs) {
// async.each(docs.paths, function(path, next) {
// async.parallel({
// albums: function(callback){
// async.each(path.albums,
// function(album, cb) {
// db.files.albums.delete(album._id, cb)
// },
// function(err){
// callback(err)
// }
// )
// },
// movies: function(callback){
// async.each(path.movies,
// function(movie, cb) {
// db.files.movies.delete(movie._id, cb)
// },
// function(err){
// callback(err)
// }
// )
// },
// others: function(callback) {
// async.each(path.others,
// function(other, cb) {
// db.files.others.delete(other._id, cb)
// },
// function(err){
// callback(err)
// }
// )
// }
// },
// cleanPath)
// })
// })
// }
}
module.exports = user