codecombat/server/clans/clan_handler.coffee
Rob fcf0bc85fe Don't allow loading users sessions for non premium dashboards.
Hoist memberLimit variable
Don't sort member's on the server side, let the client handle it
Use mongo limits instead of breaking server side.
2015-11-04 17:01:05 -08:00

172 lines
8.3 KiB
CoffeeScript

async = require 'async'
mongoose = require 'mongoose'
Handler = require '../commons/Handler'
AnalyticsLogEvent = require '../analytics/AnalyticsLogEvent'
Clan = require './Clan'
EarnedAchievement = require '../achievements/EarnedAchievement'
EarnedAchievementHandler = require '../achievements/earned_achievement_handler'
LevelSession = require '../levels/sessions/LevelSession'
LevelSessionHandler = require '../levels/sessions/level_session_handler'
User = require '../users/User'
UserHandler = require '../users/user_handler'
memberLimit = 200
ClanHandler = class ClanHandler extends Handler
modelClass: Clan
jsonSchema: require '../../app/schemas/models/clan.schema'
allowedMethods: ['GET', 'POST', 'PUT', 'DELETE']
hasAccess: (req) ->
return true if req.method is 'GET'
return false if req.method is 'POST' and req.body?.type is 'private' and not req.user?.isPremium()
req.method in @allowedMethods or req.user?.isAdmin()
hasAccessToDocument: (req, document, method=null) ->
return false unless document?
return true if req.user?.isAdmin()
return true if (method or req.method).toLowerCase() is 'get'
return true if document.get('ownerID')?.equals req.user?._id
false
makeNewInstance: (req) ->
instance = super(req)
instance.set 'ownerID', req.user._id
instance.set 'members', [req.user._id]
instance.set 'dashboardType', 'premium' if req.body?.type is 'private'
instance
delete: (req, res, clanID) ->
@getDocumentForIdOrSlug clanID, (err, clan) =>
return @sendDatabaseError res, err if err
return @sendNotFoundError res unless clan
return @sendForbiddenError res unless @hasAccessToDocument(req, clan)
memberIDs = clan.get('members')
Clan.remove {_id: clan.get('_id')}, (err) =>
return @sendDatabaseError res, err if err
User.update {_id: {$in: memberIDs}}, {$pull: {clans: clan.get('_id')}}, {multi: true}, (err) =>
return @sendDatabaseError(res, err) if err
@sendNoContent(res)
AnalyticsLogEvent.logEvent req.user, 'Clan deleted', clanID: clanID, type: clan.get('type')
getByRelationship: (req, res, args...) ->
return @joinClan(req, res, args[0]) if args[1] is 'join'
return @leaveClan(req, res, args[0]) if args[1] is 'leave'
return @getMemberAchievements(req, res, args[0]) if args[1] is 'member_achievements'
return @getMembers(req, res, args[0]) if args[1] is 'members'
return @getMemberSessions(req, res, args[0]) if args[1] is 'member_sessions'
return @getPublicClans(req, res) if args[1] is 'public'
return @removeMember(req, res, args[0], args[2]) if args.length is 3 and args[1] is 'remove'
super(arguments...)
joinClan: (req, res, clanID) ->
return @sendForbiddenError(res) unless req.user? and not req.user.isAnonymous()
try
clanID = mongoose.Types.ObjectId(clanID)
catch err
return @sendNotFoundError(res, err)
Clan.findById clanID, (err, clan) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) unless clan
return @sendDatabaseError(res, err) unless clanType = clan.get('type')
return @sendForbiddenError(res) unless clanType is 'public' or req.user.isPremium()
Clan.update {_id: clanID}, {$addToSet: {members: req.user._id}}, (err) =>
return @sendDatabaseError(res, err) if err
User.update {_id: req.user._id}, {$addToSet: {clans: clanID}}, (err) =>
return @sendDatabaseError(res, err) if err
@sendSuccess(res)
AnalyticsLogEvent.logEvent req.user, 'Clan joined', clanID: clanID, type: clanType
leaveClan: (req, res, clanID) ->
return @sendForbiddenError(res) unless req.user? and not req.user.isAnonymous()
try
clanID = mongoose.Types.ObjectId(clanID)
catch err
return @sendNotFoundError(res, err)
Clan.findById clanID, (err, clan) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) unless clan
return @sendForbiddenError(res) if clan.get('ownerID')?.equals req.user._id
Clan.update {_id: clanID}, {$pull: {members: req.user._id}}, (err) =>
return @sendDatabaseError(res, err) if err
User.update {_id: req.user._id}, {$pull: {clans: clanID}}, (err) =>
return @sendDatabaseError(res, err) if err
@sendSuccess(res)
AnalyticsLogEvent.logEvent req.user, 'Clan left', clanID: clanID, type: clan.get('type')
getMemberAchievements: (req, res, clanID) ->
# TODO: add tests
Clan.findById clanID, (err, clan) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) unless clan
memberIDs = _.map clan.get('members') ? [], (memberID) -> memberID.toHexString?() or memberID
User.find {_id: {$in: memberIDs}}, 'nameLower', {limit: memberLimit}, (err, users) =>
return @sendDatabaseError(res, err) if err
memberIDs = []
for user in users
memberIDs.push user.id
EarnedAchievement.find {user: {$in: memberIDs}}, 'achievementName user', (err, documents) =>
return @sendDatabaseError(res, err) if err?
cleandocs = (EarnedAchievementHandler.formatEntity(req, doc) for doc in documents)
@sendSuccess(res, cleandocs)
getMembers: (req, res, clanID) ->
# TODO: add tests
Clan.findById clanID, (err, clan) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) unless clan
memberIDs = _.map clan.get('members') ? [], (memberID) -> memberID.toHexString?() or memberID
User.find {_id: {$in: memberIDs}}, 'name nameLower points heroConfig.thangType', {}, (err, users) =>
return @sendDatabaseError(res, err) if err
cleandocs = (UserHandler.formatEntity(req, doc) for doc in users)
@sendSuccess(res, cleandocs)
getMemberSessions: (req, res, clanID) ->
# TODO: add tests
# TODO: restrict information returned based on clan type
Clan.findById clanID, (err, clan) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) unless clan
return @sendForbiddenError(res) unless clan.get('dashboardType') is 'premium'
memberIDs = _.map clan.get('members') ? [], (memberID) -> memberID.toHexString?() or memberID
User.find {_id: {$in: memberIDs}}, 'name', {limit: memberLimit}, (err, users) =>
return @sendDatabaseError(res, err) if err
memberIDs = []
for user in users
memberIDs.push user.id
break unless memberIDs.length < memberLimit
LevelSession.find {creator: {$in: memberIDs}}, 'changed codeLanguage creator creatorName levelID levelName playtime state submittedCodeLanguage', (err, documents) =>
return @sendDatabaseError(res, err) if err?
cleandocs = (LevelSessionHandler.formatEntity(req, doc) for doc in documents)
@sendSuccess(res, cleandocs)
getPublicClans: (req, res) ->
# Return 100 public clans, sorted by member count, created date
query = [{ $match : {type : 'public'} }]
query.push {$project : {_id: 1, name: 1, slug: 1, type: 1, description: 1, memberCount: {$size: "$members"}, ownerID: 1}}
query.push {$sort: { memberCount: -1, _id: -1 }}
query.push {$limit: 100}
Clan.aggregate(query).exec (err, documents) =>
return @sendDatabaseError(res, err) if err
@sendSuccess(res, documents)
removeMember: (req, res, clanID, memberID) ->
return @sendForbiddenError(res) unless req.user? and not req.user.isAnonymous()
try
clanID = mongoose.Types.ObjectId(clanID)
memberID = mongoose.Types.ObjectId(memberID)
catch err
return @sendNotFoundError(res, err)
Clan.findById clanID, (err, clan) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) unless clan
return @sendForbiddenError res unless @hasAccessToDocument(req, clan)
return @sendForbiddenError(res) if clan.get('ownerID').equals memberID
Clan.update {_id: clanID}, {$pull: {members: memberID}}, (err) =>
return @sendDatabaseError(res, err) if err
User.update {_id: memberID}, {$pull: {clans: clanID}}, (err) =>
return @sendDatabaseError(res, err) if err
@sendSuccess(res)
AnalyticsLogEvent.logEvent req.user, 'Clan member removed', clanID: clanID, type: clan.get('type'), memberID: memberID
module.exports = new ClanHandler()