mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 11:05:54 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			459 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			459 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| var	async = require('async'),
 | |
| 	nconf = require('nconf'),
 | |
| 	user = require('../user'),
 | |
| 	groups = require('../groups'),
 | |
| 	topics = require('../topics'),
 | |
| 	posts = require('../posts'),
 | |
| 	notifications = require('../notifications'),
 | |
| 	messaging = require('../messaging'),
 | |
| 	plugins = require('../plugins'),
 | |
| 	utils = require('../../public/src/utils'),
 | |
| 	websockets = require('./index'),
 | |
| 	meta = require('../meta'),
 | |
| 	events = require('../events'),
 | |
| 	emailer = require('../emailer'),
 | |
| 	db = require('../database'),
 | |
| 	SocketUser = {};
 | |
| 
 | |
| SocketUser.exists = function(socket, data, callback) {
 | |
| 	if (data && data.username) {
 | |
| 		meta.userOrGroupExists(utils.slugify(data.username), callback);
 | |
| 	}
 | |
| };
 | |
| 
 | |
| SocketUser.deleteAccount = function(socket, data, callback) {
 | |
| 	if (!socket.uid) {
 | |
| 		return;
 | |
| 	}
 | |
| 	user.isAdministrator(socket.uid, function(err, isAdmin) {
 | |
| 		if (err || isAdmin) {
 | |
| 			return callback(err || new Error('[[error:cant-delete-admin]]'));
 | |
| 		}
 | |
| 
 | |
| 		socket.broadcast.emit('event:user_status_change', {uid: socket.uid, status: 'offline'});
 | |
| 		user.deleteAccount(socket.uid, function(err) {
 | |
| 			if (err) {
 | |
| 				return callback(err);
 | |
| 			}
 | |
| 			websockets.in('uid_' + socket.uid).emit('event:logout');
 | |
| 			callback();
 | |
| 		});
 | |
| 	});
 | |
| };
 | |
| 
 | |
| SocketUser.emailExists = function(socket, data, callback) {
 | |
| 	if (data && data.email) {
 | |
| 		user.email.exists(data.email, callback);
 | |
| 	}
 | |
| };
 | |
| 
 | |
| SocketUser.emailConfirm = function(socket, data, callback) {
 | |
| 	if (socket.uid && parseInt(meta.config.requireEmailConfirmation, 10) === 1) {
 | |
| 		user.getUserField(socket.uid, 'email', function(err, email) {
 | |
| 			if (err) {
 | |
| 				return callback(err);
 | |
| 			}
 | |
| 
 | |
| 			if (!email) {
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			user.email.sendValidationEmail(socket.uid, email, callback);
 | |
| 		});
 | |
| 	}
 | |
| };
 | |
| 
 | |
| SocketUser.search = function(socket, data, callback) {
 | |
| 	if (!data) {
 | |
| 		return callback(new Error('[[error:invalid-data]]'));
 | |
| 	}
 | |
| 	if (!socket.uid) {
 | |
| 		return callback(new Error('[[error:not-logged-in]]'));
 | |
| 	}
 | |
| 	user.search({
 | |
| 		query: data.query,
 | |
| 		page: data.page,
 | |
| 		searchBy: data.searchBy,
 | |
| 		sortBy: data.sortBy,
 | |
| 		filterBy: data.filterBy,
 | |
| 		uid: socket.uid
 | |
| 	}, callback);
 | |
| };
 | |
| 
 | |
| // Password Reset
 | |
| SocketUser.reset = {};
 | |
| 
 | |
| SocketUser.reset.send = function(socket, email, callback) {
 | |
| 	if (email) {
 | |
| 		user.reset.send(email, callback);
 | |
| 	}
 | |
| };
 | |
| 
 | |
| SocketUser.reset.commit = function(socket, data, callback) {
 | |
| 	if (!data || !data.code || !data.password) {
 | |
| 		return callback(new Error('[[error:invalid-data]]'));
 | |
| 	}
 | |
| 
 | |
| 	async.parallel({
 | |
| 		uid: async.apply(db.getObjectField, 'reset:uid', data.code),
 | |
| 		reset: async.apply(user.reset.commit, data.code, data.password)
 | |
| 	}, function(err, results) {
 | |
| 		if (err) {
 | |
| 			return callback(err);
 | |
| 		}
 | |
| 
 | |
| 		var uid = results.uid,
 | |
| 			now = new Date(),
 | |
| 			parsedDate = now.getFullYear() + '/' + (now.getMonth()+1) + '/' + now.getDate();
 | |
| 
 | |
| 		user.getUserField(uid, 'username', function(err, username) {
 | |
| 			emailer.send('reset_notify', uid, {
 | |
| 				username: username,
 | |
| 				date: parsedDate,
 | |
| 				site_title: meta.config.title || 'NodeBB',
 | |
| 				subject: '[[email:reset.notify.subject]]'
 | |
| 			});
 | |
| 		});
 | |
| 
 | |
| 		events.log({
 | |
| 			type: 'password-reset',
 | |
| 			uid: uid,
 | |
| 			ip: socket.ip
 | |
| 		});
 | |
| 		callback();
 | |
| 	});
 | |
| };
 | |
| 
 | |
| SocketUser.checkStatus = function(socket, uid, callback) {
 | |
| 	if (!socket.uid) {
 | |
| 		return callback('[[error:invalid-uid]]');
 | |
| 	}
 | |
| 	var online = websockets.isUserOnline(uid);
 | |
| 	if (!online) {
 | |
| 		return callback(null, 'offline');
 | |
| 	}
 | |
| 	user.getUserField(uid, 'status', function(err, status) {
 | |
| 		if (err) {
 | |
| 			return callback(err);
 | |
| 		}
 | |
| 		status = status || 'online';
 | |
| 		callback(null, status);
 | |
| 	});
 | |
| };
 | |
| 
 | |
| SocketUser.changePassword = function(socket, data, callback) {
 | |
| 	if (!data || !data.uid || data.newPassword.length < meta.config.minimumPasswordLength) {
 | |
| 		return callback(new Error('[[error:invalid-data]]'));
 | |
| 	}
 | |
| 	if (!socket.uid) {
 | |
| 		return callback('[[error:invalid-uid]]');
 | |
| 	}
 | |
| 
 | |
| 	user.changePassword(socket.uid, data, function(err) {
 | |
| 		if (err) {
 | |
| 			return callback(err);
 | |
| 		}
 | |
| 
 | |
| 		events.log({
 | |
| 			type: 'password-change',
 | |
| 			uid: socket.uid,
 | |
| 			targetUid: data.uid,
 | |
| 			ip: socket.ip
 | |
| 		});
 | |
| 		callback();
 | |
| 	});
 | |
| };
 | |
| 
 | |
| SocketUser.updateProfile = function(socket, data, callback) {
 | |
| 	function update(oldUserData) {
 | |
| 		function done(err, userData) {
 | |
| 			if (err) {
 | |
| 				return callback(err);
 | |
| 			}
 | |
| 
 | |
| 			if (userData.email !== oldUserData.email) {
 | |
| 				events.log({
 | |
| 					type: 'email-change',
 | |
| 					uid: socket.uid,
 | |
| 					targetUid: data.uid,
 | |
| 					ip: socket.ip,
 | |
| 					oldEmail: oldUserData.email,
 | |
| 					newEmail: userData.email
 | |
| 				});
 | |
| 			}
 | |
| 
 | |
| 			if (userData.username !== oldUserData.username) {
 | |
| 				events.log({
 | |
| 					type: 'username-change',
 | |
| 					uid: socket.uid,
 | |
| 					targetUid: data.uid,
 | |
| 					ip: socket.ip,
 | |
| 					oldUsername: oldUserData.username,
 | |
| 					newUsername: userData.username
 | |
| 				});
 | |
| 			}
 | |
| 			callback(null, userData);
 | |
| 		}
 | |
| 
 | |
| 		if (socket.uid === parseInt(data.uid, 10)) {
 | |
| 			return user.updateProfile(socket.uid, data, done);
 | |
| 		}
 | |
| 
 | |
| 		user.isAdministrator(socket.uid, function(err, isAdmin) {
 | |
| 			if (err || !isAdmin) {
 | |
| 				return callback(err || new Error('[[error:no-privileges]]'));
 | |
| 			}
 | |
| 
 | |
| 			user.updateProfile(data.uid, data, done);
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	if (!socket.uid) {
 | |
| 		return callback('[[error:invalid-uid]]');
 | |
| 	}
 | |
| 
 | |
| 	if (!data || !data.uid) {
 | |
| 		return callback(new Error('[[error:invalid-data]]'));
 | |
| 	}
 | |
| 
 | |
| 	user.getUserFields(data.uid, ['email', 'username'], function(err, oldUserData) {
 | |
| 		if (err) {
 | |
| 			return callback(err);
 | |
| 		}
 | |
| 
 | |
| 		update(oldUserData, callback);
 | |
| 	});
 | |
| };
 | |
| 
 | |
| SocketUser.changePicture = function(socket, data, callback) {
 | |
| 	if (!socket.uid) {
 | |
| 		return callback('[[error:invalid-uid]]');
 | |
| 	}
 | |
| 
 | |
| 	if (!data) {
 | |
| 		return callback(new Error('[[error:invalid-data]]'));
 | |
| 	}
 | |
| 
 | |
| 	var type = data.type;
 | |
| 
 | |
| 	function changePicture(uid, callback) {
 | |
| 		user.getUserField(uid, type, function(err, picture) {
 | |
| 			if (err) {
 | |
| 				return callback(err);
 | |
| 			}
 | |
| 
 | |
| 			user.setUserField(uid, 'picture', picture, callback);
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	if (type === 'gravatar') {
 | |
| 		type = 'gravatarpicture';
 | |
| 	} else if (type === 'uploaded') {
 | |
| 		type = 'uploadedpicture';
 | |
| 	} else {
 | |
| 		return callback(new Error('[[error:invalid-image-type, ' + ['gravatar', 'uploadedpicture'].join(', ') + ']]'));
 | |
| 	}
 | |
| 
 | |
| 	if (socket.uid === parseInt(data.uid, 10)) {
 | |
| 		return changePicture(socket.uid, callback);
 | |
| 	}
 | |
| 
 | |
| 	user.isAdministrator(socket.uid, function(err, isAdmin) {
 | |
| 		if (err || !isAdmin) {
 | |
| 			return callback(err || new Error('[[error:no-privileges]]'));
 | |
| 		}
 | |
| 
 | |
| 		changePicture(data.uid, callback);
 | |
| 	});
 | |
| };
 | |
| 
 | |
| SocketUser.uploadProfileImageFromUrl = function(socket, data, callback) {
 | |
| 	function upload() {
 | |
| 		user.uploadFromUrl(data.uid, data.url, function(err, uploadedImage) {
 | |
| 			callback(err, uploadedImage ? uploadedImage.url : null);
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	if (!socket.uid || !data.url || !data.uid) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (parseInt(socket.uid, 10) === parseInt(data.uid, 10)) {
 | |
| 		return upload();
 | |
| 	}
 | |
| 
 | |
| 	user.isAdministrator(socket.uid, function(err, isAdmin) {
 | |
| 		if (err || !isAdmin) {
 | |
| 			return callback(err || new Error('[[error:not-allowed]]'));
 | |
| 		}
 | |
| 
 | |
| 		upload();
 | |
| 	});
 | |
| };
 | |
| 
 | |
| SocketUser.follow = function(socket, data, callback) {
 | |
| 	if (!socket.uid || !data) {
 | |
| 		return;
 | |
| 	}
 | |
| 	var userData;
 | |
| 	async.waterfall([
 | |
| 		function(next) {
 | |
| 			toggleFollow('follow', socket.uid, data.uid, next);
 | |
| 		},
 | |
| 		function(next) {
 | |
| 			user.getUserFields(socket.uid, ['username', 'userslug'], next);
 | |
| 		},
 | |
| 		function(_userData, next) {
 | |
| 			userData = _userData;
 | |
| 			notifications.create({
 | |
| 				bodyShort: '[[notifications:user_started_following_you, ' + userData.username + ']]',
 | |
| 				nid: 'follow:' + data.uid + ':uid:' + socket.uid,
 | |
| 				from: socket.uid
 | |
| 			}, next);
 | |
| 		},
 | |
| 		function(notification, next) {
 | |
| 			notification.user = userData;
 | |
| 			notifications.push(notification, [data.uid], next);
 | |
| 		}
 | |
| 	], callback);
 | |
| };
 | |
| 
 | |
| SocketUser.unfollow = function(socket, data, callback) {
 | |
| 	if (socket.uid && data) {
 | |
| 		toggleFollow('unfollow', socket.uid, data.uid, callback);
 | |
| 	}
 | |
| };
 | |
| 
 | |
| function toggleFollow(method, uid, theiruid, callback) {
 | |
| 	user[method](uid, theiruid, function(err) {
 | |
| 		if (err) {
 | |
| 			return callback(err);
 | |
| 		}
 | |
| 
 | |
| 		plugins.fireHook('action:user.' + method, {
 | |
| 			fromUid: uid,
 | |
| 			toUid: theiruid
 | |
| 		});
 | |
| 		callback();
 | |
| 	});
 | |
| }
 | |
| 
 | |
| SocketUser.saveSettings = function(socket, data, callback) {
 | |
| 	if (!socket.uid || !data) {
 | |
| 		return callback(new Error('[[error:invalid-data]]'));
 | |
| 	}
 | |
| 
 | |
| 	if (socket.uid === parseInt(data.uid, 10)) {
 | |
| 		return user.saveSettings(socket.uid, data.settings, callback);
 | |
| 	}
 | |
| 
 | |
| 	user.isAdministrator(socket.uid, function(err, isAdmin) {
 | |
| 		if (err) {
 | |
| 			return callback(err);
 | |
| 		}
 | |
| 
 | |
| 		if (!isAdmin) {
 | |
| 			return callback(new Error('[[error:no-privileges]]'));
 | |
| 		}
 | |
| 
 | |
| 		user.saveSettings(data.uid, data.settings, callback);
 | |
| 	});
 | |
| };
 | |
| 
 | |
| SocketUser.setTopicSort = function(socket, sort, callback) {
 | |
| 	if (socket.uid) {
 | |
| 		user.setSetting(socket.uid, 'topicPostSort', sort, callback);
 | |
| 	}
 | |
| };
 | |
| 
 | |
| SocketUser.setCategorySort = function(socket, sort, callback) {
 | |
| 	if (socket.uid) {
 | |
| 		user.setSetting(socket.uid, 'categoryTopicSort', sort, callback);
 | |
| 	}
 | |
| };
 | |
| 
 | |
| SocketUser.getOnlineAnonCount = function(socket, data, callback) {
 | |
| 	callback(null, module.parent.exports.getOnlineAnonCount());
 | |
| };
 | |
| 
 | |
| SocketUser.getUnreadCount = function(socket, data, callback) {
 | |
| 	if (!socket.uid) {
 | |
| 		return callback(null, 0);
 | |
| 	}
 | |
| 	topics.getTotalUnread(socket.uid, callback);
 | |
| };
 | |
| 
 | |
| SocketUser.getUnreadChatCount = function(socket, data, callback) {
 | |
| 	if (!socket.uid) {
 | |
| 		return callback(null, 0);
 | |
| 	}
 | |
| 	messaging.getUnreadCount(socket.uid, callback);
 | |
| };
 | |
| 
 | |
| SocketUser.loadMore = function(socket, data, callback) {
 | |
| 	if (!data || !data.set || parseInt(data.after, 10) < 0) {
 | |
| 		return callback(new Error('[[error:invalid-data]]'));
 | |
| 	}
 | |
| 
 | |
| 	if (!socket.uid && !!parseInt(meta.config.privateUserInfo, 10)) {
 | |
| 		return callback(new Error('[[error:no-privileges]]'));
 | |
| 	}
 | |
| 
 | |
| 	var start = parseInt(data.after, 10),
 | |
| 		stop = start + 19;
 | |
| 
 | |
| 	async.parallel({
 | |
| 		isAdmin: function(next) {
 | |
| 			user.isAdministrator(socket.uid, next);
 | |
| 		},
 | |
| 		users: function(next) {
 | |
| 			user.getUsersFromSet(data.set, socket.uid, start, stop, next);
 | |
| 		}
 | |
| 	}, function(err, results) {
 | |
| 		if (err) {
 | |
| 			return callback(err);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		if (!results.isAdmin && data.set === 'users:online') {
 | |
| 			results.users = results.users.filter(function(user) {
 | |
| 				return user.status !== 'offline';
 | |
| 			});
 | |
| 		}
 | |
| 		var result = {
 | |
| 			users: results.users,
 | |
| 			nextStart: stop + 1,
 | |
| 		};
 | |
| 		result['route_' + data.set] = true;
 | |
| 		callback(null, result);
 | |
| 	});
 | |
| };
 | |
| 
 | |
| SocketUser.setStatus = function(socket, status, callback) {
 | |
| 	if (!socket.uid) {
 | |
| 		return callback(new Error('[[error:invalid-uid]]'));
 | |
| 	}
 | |
| 
 | |
| 	var allowedStatus = ['online', 'offline', 'dnd', 'away'];
 | |
| 	if (allowedStatus.indexOf(status) === -1) {
 | |
| 		return callback(new Error('[[error:invalid-user-status]]'));
 | |
| 	}
 | |
| 	user.setUserField(socket.uid, 'status', status, function(err) {
 | |
| 		if (err) {
 | |
| 			return callback(err);
 | |
| 		}
 | |
| 		var data = {
 | |
| 			uid: socket.uid,
 | |
| 			status: status
 | |
| 		};
 | |
| 		websockets.server.sockets.emit('event:user_status_change', data);
 | |
| 		callback(null, data);
 | |
| 	});
 | |
| };
 | |
| 
 | |
| /* Exports */
 | |
| 
 | |
| module.exports = SocketUser;
 |