diff --git a/website/src/controllers/api-v2/challenges.js b/website/src/controllers/api-v2/challenges.js index f20bf96ad8..605e3424cf 100644 --- a/website/src/controllers/api-v2/challenges.js +++ b/website/src/controllers/api-v2/challenges.js @@ -10,6 +10,7 @@ import { import { model as Group, basicFields as basicGroupFields, + TAVERN_ID, } from '../../models/group'; import { model as Challenge, @@ -152,7 +153,7 @@ api.create = async function(req, res, next){ return res.status(401).json({err:"Only the group leader can create challenges"}); } - if (groupId === 'habitrpg' && prize < 1) { + if (group._id === TAVERN_ID && prize < 1) { return res.status(401).json({err: 'Prize must be at least 1 Gem for public challenges.'}) } diff --git a/website/src/controllers/api-v2/groups.js b/website/src/controllers/api-v2/groups.js index fded19274e..f864828217 100644 --- a/website/src/controllers/api-v2/groups.js +++ b/website/src/controllers/api-v2/groups.js @@ -19,6 +19,7 @@ import { } from './../../models/user'; import { model as Group, + TAVERN_ID, } from './../../models/group'; import { model as Challenge, @@ -127,7 +128,7 @@ api.list = function(req, res, next) { // unecessary given our ui-router setup tavern: function(cb) { if (!~type.indexOf('tavern')) return cb(null, {}); - Group.findById('habitrpg').select(groupFields).exec(function(err, tavern){ + Group.findById(TAVERN_ID).select(groupFields).exec(function(err, tavern){ if (err) return cb(err); tavern.getTransformedData({cb: function (err, transformedTavern) { if (err) return cb(err); @@ -169,6 +170,8 @@ api.get = function(req, res, next) { if (isUserGuild) { q = Group.findOne({type: 'guild', _id: gid}); + } else if (gid === 'habitrpg') { + q = Group.findOne({_id: TAVERN_ID}); } else { q = Group.findOne({type: 'guild', privacy: 'public', _id: gid}); } @@ -284,6 +287,7 @@ api.update = function(req, res, next) { api.attachGroup = function(req, res, next) { var user = res.locals.user; var gid = req.params.gid === 'party' ? user.party._id : req.params.gid; + if (gid === 'habitrpg') gid = TAVERN_ID; let q = Group.findOne({_id: gid}) @@ -313,6 +317,8 @@ api.getChat = function(req, res, next) { } else { if (isUserGuild) { q = Group.findOne({type: 'guild', _id: gid}); + } else if (gid === 'habitrpg') { + q = Group.findOne({_id: TAVERN_ID}); } else { q = Group.findOne({type: 'guild', privacy: 'public', _id: gid}); } @@ -423,7 +429,7 @@ api.flagChatMessage = function(req, res, next){ {name: "GROUP_NAME", content: group.name}, {name: "GROUP_TYPE", content: group.type}, {name: "GROUP_ID", content: group._id}, - {name: "GROUP_URL", content: group._id == 'habitrpg' ? '/#/options/groups/tavern' : (group.type === 'guild' ? ('/#/options/groups/guilds/' + group._id) : 'party')}, + {name: "GROUP_URL", content: group._id == TAVERN_ID ? '/#/options/groups/tavern' : (group.type === 'guild' ? ('/#/options/groups/guilds/' + group._id) : 'party')}, ]); return res.sendStatus(204); diff --git a/website/src/controllers/api-v3/challenges.js b/website/src/controllers/api-v3/challenges.js index ecaad87eb1..b3dd588118 100644 --- a/website/src/controllers/api-v3/challenges.js +++ b/website/src/controllers/api-v3/challenges.js @@ -5,6 +5,7 @@ import { model as Challenge } from '../../models/challenge'; import { model as Group, basicFields as basicGroupFields, + TAVERN_ID, } from '../../models/group'; import { model as User, @@ -54,7 +55,7 @@ api.createChallenge = { throw new NotAuthorized(res.t('onlyGroupLeaderChal')); } - if (groupId === 'habitrpg' && prize < 1) { + if (group._id === TAVERN_ID && prize < 1) { throw new NotAuthorized(res.t('pubChalsMinPrize')); } @@ -454,7 +455,7 @@ export async function _closeChal (challenge, broken = {}) { await Challenge.remove({_id: challenge._id}).exec(); // Refund the leader if the challenge is closed and the group not the tavern - if (challenge.group !== 'habitrpg' && brokenReason === 'CHALLENGE_DELETED') { + if (challenge.group !== TAVERN_ID && brokenReason === 'CHALLENGE_DELETED') { await User.update({_id: challenge.leader}, {$inc: {balance: challenge.prize / 4}}).exec(); } diff --git a/website/src/controllers/api-v3/chat.js b/website/src/controllers/api-v3/chat.js index 1fd1ebe1bd..43e4ba69e9 100644 --- a/website/src/controllers/api-v3/chat.js +++ b/website/src/controllers/api-v3/chat.js @@ -1,6 +1,9 @@ import { authWithHeaders } from '../../middlewares/api-v3/auth'; import cron from '../../middlewares/api-v3/cron'; -import { model as Group } from '../../models/group'; +import { + model as Group, + TAVERN_ID, +} from '../../models/group'; import { model as User } from '../../models/user'; import { NotFound, @@ -23,7 +26,7 @@ let api = {}; * @apiName GetChat * @apiGroup Chat * - * @apiParam {string} groupId The group _id (or 'party') + * @apiParam {string} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) * * @apiSuccess {Array} chat An array of chat messages */ @@ -219,7 +222,7 @@ api.flagChat = { } let groupUrl; - if (group._id === 'habitrpg') { + if (group._id === TAVERN_ID) { groupUrl = '/#/options/groups/tavern'; } else if (group.type === 'guild') { groupUrl = `/#/options/groups/guilds/{$group._id}`; @@ -336,7 +339,7 @@ api.seenChat = { * @apiName DeleteChat * @apiGroup Chat * - * @apiParam {string} groupId The group _id (or 'party') + * @apiParam {string} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) * @apiParam {string} chatId The chat _id * * @apiSuccess {Array} The update chat array diff --git a/website/src/controllers/api-v3/groups.js b/website/src/controllers/api-v3/groups.js index 81a634a725..333e38a4a5 100644 --- a/website/src/controllers/api-v3/groups.js +++ b/website/src/controllers/api-v3/groups.js @@ -118,7 +118,7 @@ api.getGroups = { * @apiName GetGroup * @apiGroup Group * - * @apiParam {string} groupId The group _id (or 'party') + * @apiParam {string} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) * * @apiSuccess {Object} group The group object */ @@ -152,7 +152,7 @@ api.getGroup = { * @apiName UpdateGroup * @apiGroup Group * - * @apiParam {string} groupId The group _id (or 'party') + * @apiParam {string} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) * * @apiSuccess {Object} group The updated group object */ @@ -334,7 +334,7 @@ api.rejectGroupInvite = { * @apiName LeaveGroup * @apiGroup Group * - * @apiParam {string} groupId The group _id (or 'party') + * @apiParam {string} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) * @apiParam {string="remove-all","keep-all"} keep Wheter to keep or not challenges' tasks, as an optional query string * * @apiSuccess {Object} empty An empty object @@ -390,7 +390,7 @@ function _sendMessageToRemoved (group, removedUser, message) { * @apiName RemoveGroupMember * @apiGroup Group * - * @apiParam {string} groupId The group _id (or 'party') + * @apiParam {string} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) * @apiParam {UUID} memberId The _id of the member to remove * @apiParam {string} message The message to send to the removed members, as a query string // TODO in req.body? * @@ -591,7 +591,7 @@ async function _inviteByEmail (invite, group, inviter, req, res) { * @apiName InviteToGroup * @apiGroup Group * - * @apiParam {string} groupId The group _id (or 'party') + * @apiParam {string} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) * * @apiParam {array} emails An array of emails addresses to invite (optional) (inside body) * @apiParam {array} uuids An array of uuids to invite (optional) (inside body) diff --git a/website/src/libs/api-v2/firebase.js b/website/src/libs/api-v2/firebase.js index 84a90b22d4..c0b69d9eb9 100644 --- a/website/src/libs/api-v2/firebase.js +++ b/website/src/libs/api-v2/firebase.js @@ -6,6 +6,8 @@ var firebaseConfig = nconf.get('FIREBASE'); var firebaseRef; var isFirebaseEnabled = (nconf.get('NODE_ENV') === 'production') && (firebaseConfig.ENABLED === 'true'); +import { TAVERN_ID } from '../../models/group'; + // Setup if(isFirebaseEnabled){ firebaseRef = new Firebase('https://' + firebaseConfig.APP + '.firebaseio.com'); @@ -24,7 +26,7 @@ api.updateGroupData = function(group){ // TODO is throw ok? we don't have callbacks if(!group) throw new Error('group is required.'); // Return in case of tavern (comparison working because we use string for _id) - if(group._id === 'habitrpg') return; + if(group._id === TAVERN_ID) return; firebaseRef.child('rooms/' + group._id) .set({ @@ -35,7 +37,7 @@ api.updateGroupData = function(group){ api.addUserToGroup = function(groupId, userId){ if(!isFirebaseEnabled) return; if(!userId || !groupId) throw new Error('groupId, userId are required.'); - if(groupId === 'habitrpg') return; + if(groupId === TAVERN_ID) return; firebaseRef.child('members/' + groupId + '/' + userId) .set(true); @@ -47,7 +49,7 @@ api.addUserToGroup = function(groupId, userId){ api.removeUserFromGroup = function(groupId, userId){ if(!isFirebaseEnabled) return; if(!userId || !groupId) throw new Error('groupId, userId are required.'); - if(groupId === 'habitrpg') return; + if(groupId === TAVERN_ID) return; firebaseRef.child('members/' + groupId + '/' + userId) .remove(); @@ -59,7 +61,7 @@ api.removeUserFromGroup = function(groupId, userId){ api.deleteGroup = function(groupId){ if(!isFirebaseEnabled) return; if(!groupId) throw new Error('groupId is required.'); - if(groupId === 'habitrpg') return; + if(groupId === TAVERN_ID) return; firebaseRef.child('rooms/' + groupId) .remove(); @@ -78,4 +80,4 @@ api.deleteUser = function(userId){ firebaseRef.child('users/' + userId) .remove(); -}; \ No newline at end of file +}; diff --git a/website/src/libs/api-v3/firebase.js b/website/src/libs/api-v3/firebase.js index 92d6c28fc1..6cf3bb1ce7 100644 --- a/website/src/libs/api-v3/firebase.js +++ b/website/src/libs/api-v3/firebase.js @@ -1,5 +1,7 @@ import Firebase from 'firebase'; import nconf from 'nconf'; +import { TAVERN_ID } from '../../models/group'; + const FIREBASE_CONFIG = nconf.get('FIREBASE'); const FIREBASE_ENABLED = FIREBASE_CONFIG.ENABLED === 'true'; @@ -20,7 +22,7 @@ export function updateGroupData (group) { // TODO is throw ok? we don't have callbacks if (!group) throw new Error('group obj is required.'); // Return in case of tavern (comparison working because we use string for _id) - if (group._id === 'habitrpg') return; + if (group._id === TAVERN_ID) return; firebaseRef.child(`rooms/${group._id}`) .set({ @@ -31,7 +33,7 @@ export function updateGroupData (group) { export function addUserToGroup (groupId, userId) { if (!FIREBASE_ENABLED) return; if (!userId || !groupId) throw new Error('groupId, userId are required.'); - if (groupId === 'habitrpg') return; + if (groupId === TAVERN_ID) return; firebaseRef.child(`members/${groupId}/${userId}`).set(true); firebaseRef.child(`users/${userId}/rooms/${groupId}`).set(true); @@ -40,7 +42,7 @@ export function addUserToGroup (groupId, userId) { export function removeUserFromGroup (groupId, userId) { if (!FIREBASE_ENABLED) return; if (!userId || !groupId) throw new Error('groupId, userId are required.'); - if (groupId === 'habitrpg') return; + if (groupId === TAVERN_ID) return; firebaseRef.child(`members/${groupId}/${userId}`).remove(); firebaseRef.child(`users/${userId}/rooms/${groupId}`).remove(); @@ -49,7 +51,7 @@ export function removeUserFromGroup (groupId, userId) { export function deleteGroup (groupId) { if (!FIREBASE_ENABLED) return; if (!groupId) throw new Error('groupId is required.'); - if (groupId === 'habitrpg') return; + if (groupId === TAVERN_ID) return; firebaseRef.child(`members/${groupId}`).remove(); // FIXME not really necessary as long as we only store room data, @@ -64,4 +66,4 @@ export function deleteUser (userId) { if (!userId) throw new Error('userId is required.'); firebaseRef.child(`users/${userId}`).remove(); -} \ No newline at end of file +} diff --git a/website/src/models/group.js b/website/src/models/group.js index d28e14b542..d85bc8c13e 100644 --- a/website/src/models/group.js +++ b/website/src/models/group.js @@ -17,8 +17,10 @@ import nconf from 'nconf'; import sendPushNotification from '../libs/api-v3/pushNotifications'; const questScrolls = shared.content.quests; +const Schema = mongoose.Schema; -let Schema = mongoose.Schema; +export const INVITES_LIMIT = 100; +export const TAVERN_ID = '00000000-0000-4000-A000-000000000000'; // NOTE once Firebase is enabled any change to groups' members in MongoDB will have to be run through the API // changes made directly to the db will cause Firebase to get out of sync @@ -126,15 +128,18 @@ schema.statics.getGroup = async function getGroup (options = {}) { let isUserParty = groupId === 'party' || user.party._id === groupId; let isUserGuild = user.guilds.indexOf(groupId) !== -1; + let isTavern = ['habitrpg', TAVERN_ID].indexOf(groupId) !== -1; // When requireMembership is true check that user is member even in public guild - if (requireMembership && !isUserParty && !isUserGuild) { + if (requireMembership && !isUserParty && !isUserGuild && !isTavern) { return null; } // When optionalMembership is true it's not required for the user to be a member of the group if (isUserParty) { query = {type: 'party', _id: user.party._id}; + } else if (isTavern) { + query = {_id: TAVERN_ID}; } else if (optionalMembership === true) { query = {_id: groupId}; } else if (isUserGuild) { @@ -180,7 +185,7 @@ schema.statics.getGroups = async function getGroups (options = {}) { break; case 'tavern': if (types.indexOf('publicGuilds') === -1) { - queries.push(this.getGroup({user, groupId: 'habitrpg', fields: groupFields})); + queries.push(this.getGroup({user, groupId: TAVERN_ID, fields: groupFields})); } break; } @@ -230,7 +235,7 @@ schema.methods.removeGroupInvitations = async function removeGroupInvitations () // Return true if user is a member of the group schema.methods.isMember = function isGroupMember (user) { - if (this._id === 'habitrpg') { + if (this._id === TAVERN_ID) { return true; // everyone is considered part of the tavern } else if (this.type === 'party') { return user.party._id === this._id ? true : false; @@ -263,7 +268,7 @@ export function chatDefaults (msg, user) { return message; } -const NO_CHAT_NOTIFICATIONS = ['habitrpg']; +const NO_CHAT_NOTIFICATIONS = [TAVERN_ID]; schema.methods.sendChat = function sendChat (message, user) { this.chat.unshift(chatDefaults(message, user)); this.chat.splice(200); @@ -421,7 +426,7 @@ schema.methods.finishQuest = function finishQuest (quest) { updates.$inc['stats.exp'] = Number(quest.drop.exp); updates.$inc._v = 1; - if (this._id === 'habitrpg') { + if (this._id === TAVERN_ID) { updates.$set['party.quest.completed'] = questK; // Just show the notif } else { updates.$set['party.quest'] = _cleanQuestProgress({completed: questK}); // clear quest progress @@ -450,7 +455,7 @@ schema.methods.finishQuest = function finishQuest (quest) { } }); - let q = this._id === 'habitrpg' ? {} : {_id: {$in: _.keys(this.quest.members)}}; + let q = this._id === TAVERN_ID ? {} : {_id: {$in: _.keys(this.quest.members)}}; this.quest = {}; this.markModified('quest'); return User.update(q, updates, {multi: true}).exec(); @@ -539,10 +544,10 @@ schema.statics.bossQuest = async function bossQuest (user, progress) { return group.save(); }; -// to set a boss: `db.groups.update({_id:'habitrpg'},{$set:{quest:{key:'dilatory',active:true,progress:{hp:1000,rage:1500}}}})` +// to set a boss: `db.groups.update({_id:TAVERN_ID},{$set:{quest:{key:'dilatory',active:true,progress:{hp:1000,rage:1500}}}})` // we export an empty object that is then populated with the query-returned data export let tavernQuest = {}; -let tavernQ = {_id: 'habitrpg', 'quest.key': {$ne: null}}; +let tavernQ = {_id: TAVERN_ID, 'quest.key': {$ne: null}}; // we use process.nextTick because at this point the model is not yet available process.nextTick(() => { @@ -723,23 +728,20 @@ schema.methods.getTransformedData = function getTransformedData (options) { }; // END API v2 compatibility methods -export const INVITES_LIMIT = 100; export let model = mongoose.model('Group', schema); // initialize tavern if !exists (fresh installs) // do not run when testing as it's handled by the tests and can easily cause a race condition if (!nconf.get('IS_TEST')) { - model.count({_id: 'habitrpg'}, (err, ct) => { + model.count({_id: TAVERN_ID}, (err, ct) => { if (err) throw err; if (ct > 0) return; new model({ // eslint-disable-line babel/new-cap - _id: 'habitrpg', - leader: '9', // TODO change this user id + _id: TAVERN_ID, + leader: '7bde7864-ebc5-4ee2-a4b7-1070d464cdb0', // Siena Leslie name: 'HabitRPG', type: 'guild', privacy: 'public', - }).save({ - validateBeforeSave: false, // _id = 'habitrpg' would not be valid otherwise - }); + }).save(); }); } diff --git a/website/src/models/user.js b/website/src/models/user.js index 0086167f2e..fcf0078cf4 100644 --- a/website/src/models/user.js +++ b/website/src/models/user.js @@ -7,9 +7,11 @@ import * as Tasks from './task'; import Q from 'q'; import { schema as TagSchema } from './tag'; import baseModel from '../libs/api-v3/baseModel'; -import { chatDefaults } from './group'; +import { + chatDefaults, + TAVERN_ID, +} from './group'; import { defaults } from 'lodash'; -// import {model as Challenge} from './challenge'; let Schema = mongoose.Schema; @@ -706,7 +708,7 @@ schema.methods.isSubscribed = function isSubscribed () { schema.methods.getGroups = function getUserGroups () { let userGroups = this.guilds.slice(0); // clone user.guilds so we don't modify the original if (this.party._id) userGroups.push(this.party._id); - userGroups.push('habitrpg'); // tavern + userGroups.push(TAVERN_ID); return userGroups; };