mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 07:07:35 +01:00
@@ -89,12 +89,6 @@
|
||||
"USERNAME": "admin",
|
||||
"PASSWORD": "password"
|
||||
},
|
||||
"PUSHER": {
|
||||
"ENABLED": "false",
|
||||
"APP_ID": "appId",
|
||||
"KEY": "key",
|
||||
"SECRET": "secret"
|
||||
},
|
||||
"SLACK": {
|
||||
"FLAGGING_URL": "https://hooks.slack.com/services/id/id/id",
|
||||
"FLAGGING_FOOTER_LINK": "https://habitrpg.github.io/flag-o-rama/",
|
||||
|
||||
97
package-lock.json
generated
97
package-lock.json
generated
@@ -22174,103 +22174,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"pusher": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/pusher/-/pusher-1.5.1.tgz",
|
||||
"integrity": "sha1-gYbPFuWxJLUdpsgwAaTDairUK0Q=",
|
||||
"requires": {
|
||||
"request": "2.74.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
|
||||
"integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
|
||||
"requires": {
|
||||
"lodash": "^4.17.10"
|
||||
}
|
||||
},
|
||||
"bl": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz",
|
||||
"integrity": "sha1-/cqHGplxOqANGeO7ukHER4emU5g=",
|
||||
"requires": {
|
||||
"readable-stream": "~2.0.5"
|
||||
}
|
||||
},
|
||||
"form-data": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.1.tgz",
|
||||
"integrity": "sha1-rjFduaSQf6BlUCMEpm13M0de43w=",
|
||||
"requires": {
|
||||
"async": "^2.0.1",
|
||||
"combined-stream": "^1.0.5",
|
||||
"mime-types": "^2.1.11"
|
||||
}
|
||||
},
|
||||
"node-uuid": {
|
||||
"version": "1.4.8",
|
||||
"resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
|
||||
"integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc="
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
|
||||
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz",
|
||||
"integrity": "sha1-HPyyXBCpsrSDBT/zn138kjOQjP4="
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
|
||||
"integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.1",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~1.0.6",
|
||||
"string_decoder": "~0.10.x",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"version": "2.74.0",
|
||||
"resolved": "http://registry.npmjs.org/request/-/request-2.74.0.tgz",
|
||||
"integrity": "sha1-dpPKdou7DqXIzgjAhKRe+gW4kqs=",
|
||||
"requires": {
|
||||
"aws-sign2": "~0.6.0",
|
||||
"aws4": "^1.2.1",
|
||||
"bl": "~1.1.2",
|
||||
"caseless": "~0.11.0",
|
||||
"combined-stream": "~1.0.5",
|
||||
"extend": "~3.0.0",
|
||||
"forever-agent": "~0.6.1",
|
||||
"form-data": "~1.0.0-rc4",
|
||||
"har-validator": "~2.0.6",
|
||||
"hawk": "~3.1.3",
|
||||
"http-signature": "~1.1.0",
|
||||
"is-typedarray": "~1.0.0",
|
||||
"isstream": "~0.1.2",
|
||||
"json-stringify-safe": "~5.0.1",
|
||||
"mime-types": "~2.1.7",
|
||||
"node-uuid": "~1.4.7",
|
||||
"oauth-sign": "~0.8.1",
|
||||
"qs": "~6.2.0",
|
||||
"stringstream": "~0.0.4",
|
||||
"tough-cookie": "~2.3.0",
|
||||
"tunnel-agent": "~0.4.1"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
|
||||
}
|
||||
}
|
||||
},
|
||||
"q": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
|
||||
|
||||
@@ -79,7 +79,6 @@
|
||||
"postcss-easy-import": "^3.0.0",
|
||||
"ps-tree": "^1.0.0",
|
||||
"pug": "^2.0.3",
|
||||
"pusher": "^1.3.0",
|
||||
"rimraf": "^2.4.3",
|
||||
"sass-loader": "^7.0.0",
|
||||
"shelljs": "^0.8.2",
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
import {
|
||||
generateUser,
|
||||
requester,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
describe('POST /user/auth/pusher', () => {
|
||||
let user;
|
||||
let endpoint = '/user/auth/pusher';
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('requires authentication', async () => {
|
||||
let api = requester();
|
||||
|
||||
await expect(api.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingAuthHeaders'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if req.body.socket_id is missing', async () => {
|
||||
await expect(user.post(endpoint, {
|
||||
channel_name: '123',
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if req.body.channel_name is missing', async () => {
|
||||
await expect(user.post(endpoint, {
|
||||
socket_id: '123',
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if req.body.channel_name is badly formatted', async () => {
|
||||
await expect(user.post(endpoint, {
|
||||
channel_name: '123',
|
||||
socket_id: '123',
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'Invalid Pusher channel type.',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if an invalid channel type is passed', async () => {
|
||||
await expect(user.post(endpoint, {
|
||||
channel_name: 'invalid-group-123',
|
||||
socket_id: '123',
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'Invalid Pusher channel type.',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if an invalid resource type is passed', async () => {
|
||||
await expect(user.post(endpoint, {
|
||||
channel_name: 'presence-user-123',
|
||||
socket_id: '123',
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'Invalid Pusher resource type.',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if an invalid resource id is passed', async () => {
|
||||
await expect(user.post(endpoint, {
|
||||
channel_name: 'presence-group-123',
|
||||
socket_id: '123',
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'Invalid Pusher resource id, must be a UUID.',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error if the passed resource id doesn\'t match the user\'s party', async () => {
|
||||
await expect(user.post(endpoint, {
|
||||
channel_name: `presence-group-${generateUUID()}`,
|
||||
socket_id: '123',
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: 'Resource id must be the user\'s party.',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -46,8 +46,6 @@ export async function postChat (store, payload) {
|
||||
message: payload.message,
|
||||
});
|
||||
|
||||
// @TODO: pusherSocketId: $rootScope.pusherSocketId, // to make sure the send doesn't get notified of it's own message
|
||||
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import validator from 'validator';
|
||||
import moment from 'moment';
|
||||
import nconf from 'nconf';
|
||||
|
||||
import {
|
||||
authWithHeaders,
|
||||
} from '../../middlewares/auth';
|
||||
@@ -10,11 +9,9 @@ import common from '../../../common';
|
||||
import {
|
||||
NotAuthorized,
|
||||
BadRequest,
|
||||
NotFound,
|
||||
} from '../../libs/errors';
|
||||
import * as passwordUtils from '../../libs/password';
|
||||
import { send as sendEmail } from '../../libs/email';
|
||||
import pusher from '../../libs/pusher';
|
||||
import { validatePasswordResetCodeAndFindUser, convertToBcrypt} from '../../libs/password';
|
||||
import { encrypt } from '../../libs/encryption';
|
||||
import {
|
||||
@@ -141,75 +138,6 @@ api.loginSocial = {
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* @apiIgnore Private route
|
||||
* @api {post} /api/v3/user/auth/pusher Pusher.com authentication
|
||||
* @apiDescription Authentication for Pusher.com private and presence channels
|
||||
* @apiName UserAuthPusher
|
||||
* @apiGroup User
|
||||
*
|
||||
* @apiParam (Body) {String} socket_id A unique identifier for the specific client connection to Pusher
|
||||
* @apiParam (Body) {String} channel_name The name of the channel being subscribed to
|
||||
*
|
||||
* @apiSuccess {String} auth The authentication token
|
||||
*/
|
||||
api.pusherAuth = {
|
||||
method: 'POST',
|
||||
middlewares: [authWithHeaders()],
|
||||
url: '/user/auth/pusher',
|
||||
async handler (req, res) {
|
||||
let user = res.locals.user;
|
||||
|
||||
req.checkBody('socket_id').notEmpty();
|
||||
req.checkBody('channel_name').notEmpty();
|
||||
|
||||
let validationErrors = req.validationErrors();
|
||||
if (validationErrors) throw validationErrors;
|
||||
|
||||
let socketId = req.body.socket_id;
|
||||
let channelName = req.body.channel_name;
|
||||
|
||||
// Channel names are in the form of {presence|private}-{group|...}-{resourceId}
|
||||
let [channelType, resourceType, ...resourceId] = channelName.split('-');
|
||||
|
||||
if (['presence'].indexOf(channelType) === -1) { // presence is used only for parties, private for guilds
|
||||
throw new BadRequest('Invalid Pusher channel type.');
|
||||
}
|
||||
|
||||
if (resourceType !== 'group') { // only groups are supported
|
||||
throw new BadRequest('Invalid Pusher resource type.');
|
||||
}
|
||||
|
||||
resourceId = resourceId.join('-'); // the split at the beginning had split resourceId too
|
||||
if (!validator.isUUID(String(resourceId))) {
|
||||
throw new BadRequest('Invalid Pusher resource id, must be a UUID.');
|
||||
}
|
||||
|
||||
// Only the user's party is supported for now
|
||||
if (user.party._id !== resourceId) {
|
||||
throw new NotFound('Resource id must be the user\'s party.');
|
||||
}
|
||||
|
||||
let authResult;
|
||||
|
||||
// Max 100 members for presence channel - parties only
|
||||
if (channelType === 'presence') {
|
||||
let presenceData = {
|
||||
user_id: user._id, // eslint-disable-line camelcase
|
||||
// Max 1KB
|
||||
user_info: {}, // eslint-disable-line camelcase
|
||||
};
|
||||
|
||||
authResult = pusher.authenticate(socketId, channelName, presenceData);
|
||||
} else {
|
||||
authResult = pusher.authenticate(socketId, channelName);
|
||||
}
|
||||
|
||||
// Not using res.respond because Pusher requires a different response format
|
||||
res.status(200).json(authResult);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {put} /api/v3/user/auth/update-username Update username
|
||||
* @apiDescription Update the username of a local user
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
import { removeFromArray } from '../../libs/collectionManipulators';
|
||||
import { getUserInfo, getGroupUrl, sendTxn } from '../../libs/email';
|
||||
import slack from '../../libs/slack';
|
||||
import pusher from '../../libs/pusher';
|
||||
import { getAuthorEmailFromMessage } from '../../libs/chat';
|
||||
import { chatReporterFactory } from '../../libs/chatReporting/chatReporterFactory';
|
||||
import nconf from 'nconf';
|
||||
@@ -186,13 +185,6 @@ api.postChat = {
|
||||
|
||||
await Promise.all(toSave);
|
||||
|
||||
// @TODO: rethink if we want real-time
|
||||
if (group.privacy === 'private' && group.type === 'party') {
|
||||
// req.body.pusherSocketId is sent from official clients to identify the sender user's real time socket
|
||||
// see https://pusher.com/docs/server_api_guide/server_excluding_recipients
|
||||
pusher.trigger(`presence-group-${group._id}`, 'new-chat', newChatMessage, req.body.pusherSocketId);
|
||||
}
|
||||
|
||||
if (chatUpdated) {
|
||||
res.respond(200, {chat: chatRes.chat});
|
||||
} else {
|
||||
|
||||
@@ -19,7 +19,6 @@ import { removeFromArray } from '../../libs/collectionManipulators';
|
||||
import { sendTxn as sendTxnEmail } from '../../libs/email';
|
||||
import { encrypt } from '../../libs/encryption';
|
||||
import { sendNotification as sendPushNotification } from '../../libs/pushNotifications';
|
||||
import pusher from '../../libs/pusher';
|
||||
import common from '../../../common';
|
||||
import payments from '../../libs/payments/payments';
|
||||
import stripePayments from '../../libs/payments/stripe';
|
||||
@@ -882,12 +881,6 @@ api.removeGroupMember = {
|
||||
removeFromArray(member.guilds, group._id);
|
||||
}
|
||||
if (isInGroup === 'party') {
|
||||
// Tell the realtime clients that a user is being removed
|
||||
// If the user that is being removed is still connected, they'll get disconnected automatically
|
||||
pusher.trigger(`presence-group-${group._id}`, 'user-removed', {
|
||||
userId: user._id,
|
||||
});
|
||||
|
||||
member.party._id = undefined; // TODO remove quest information too? Use group.leave()?
|
||||
}
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import Pusher from 'pusher';
|
||||
import nconf from 'nconf';
|
||||
import { InternalServerError } from './errors';
|
||||
|
||||
const IS_PUSHER_ENABLED = nconf.get('PUSHER:ENABLED') === 'true';
|
||||
|
||||
let pusherInstance;
|
||||
|
||||
if (IS_PUSHER_ENABLED) {
|
||||
pusherInstance = new Pusher({
|
||||
appId: nconf.get('PUSHER:APP_ID'),
|
||||
key: nconf.get('PUSHER:KEY'),
|
||||
secret: nconf.get('PUSHER:SECRET'),
|
||||
encrypted: true,
|
||||
});
|
||||
}
|
||||
|
||||
let api = {
|
||||
// https://github.com/pusher/pusher-http-node#publishing-events
|
||||
trigger (channel, event, data, socketId = null) {
|
||||
if (!IS_PUSHER_ENABLED) return Promise.resolve(null);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
pusherInstance.trigger(channel, event, data, socketId, (err, req, res) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve([req, res]);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// https://github.com/pusher/pusher-http-node#authenticating-private-channels
|
||||
authenticate (...args) {
|
||||
if (!IS_PUSHER_ENABLED) throw new InternalServerError('Pusher is not enabled.');
|
||||
|
||||
return pusherInstance.authenticate(...args);
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = api;
|
||||
@@ -29,7 +29,6 @@ import baseModel from '../libs/baseModel';
|
||||
import { sendTxn as sendTxnEmail } from '../libs/email';
|
||||
import nconf from 'nconf';
|
||||
import { sendNotification as sendPushNotification } from '../libs/pushNotifications';
|
||||
import pusher from '../libs/pusher';
|
||||
import {
|
||||
syncableAttrs,
|
||||
} from '../libs/taskManager';
|
||||
@@ -522,12 +521,6 @@ schema.methods.sendChat = function sendChat (message, user, metaData) {
|
||||
User.update(query, lastSeenUpdateAddNew, {multi: true}).exec();
|
||||
});
|
||||
|
||||
// If the message being sent is a system message (not gone through the api.postChat controller)
|
||||
// then notify Pusher about it (only parties for now)
|
||||
if (newMessage.uuid === 'system' && this.privacy === 'private' && this.type === 'party') {
|
||||
pusher.trigger(`presence-group-${this._id}`, 'new-chat', newMessage);
|
||||
}
|
||||
|
||||
return newChatMessage;
|
||||
};
|
||||
|
||||
@@ -1160,11 +1153,6 @@ schema.methods.leave = async function leaveGroup (user, keep = 'keep-all', keepC
|
||||
promises.push(User.update({_id: user._id}, {$pull: {guilds: group._id}}).exec());
|
||||
} else {
|
||||
promises.push(User.update({_id: user._id}, {$set: {party: {}}}).exec());
|
||||
// Tell the realtime clients that a user has left
|
||||
// If the user that left is still connected, they'll get disconnected
|
||||
pusher.trigger(`presence-group-${group._id}`, 'user-left', {
|
||||
userId: user._id,
|
||||
});
|
||||
|
||||
update.$unset = {[`quest.members.${user._id}`]: 1};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user