diff --git a/website/client/src/components/admin-panel/user-support/index.vue b/website/client/src/components/admin-panel/user-support/index.vue index 3002ca8e4e..eeae634998 100644 --- a/website/client/src/components/admin-panel/user-support/index.vue +++ b/website/client/src/components/admin-panel/user-support/index.vue @@ -67,6 +67,11 @@ :reset-counter="resetCounter" /> + + +
+
+

+ User History +

+
+
+
+
+
+ + + +
+
+ +
+
+ + + + + + + + + + + +
+ {{ $t('timestamp') }} + Client + Received +
+ {{ entry.timestamp | timeAgo }} + {{ entry.client }}{{ entry.reward }}
+
+
+ + + + + + + + + + + + + +
+ {{ $t('timestamp') }} + ClientQuest KeyResponse
+ {{ entry.timestamp | timeAgo }} + {{ entry.client }}{{ entry.quest }}{{ entry.response }}
+
+
+ + + + + + + + + +
+ {{ $t('timestamp') }} + Client
+ {{ entry.timestamp | timeAgo }} + {{ entry.client }}
+
+
+
+
+
+ + + + + diff --git a/website/client/src/store/actions/adminPanel.js b/website/client/src/store/actions/adminPanel.js index 43e1805429..5084295db1 100644 --- a/website/client/src/store/actions/adminPanel.js +++ b/website/client/src/store/actions/adminPanel.js @@ -5,3 +5,9 @@ export async function searchUsers (store, payload) { const response = await axios.get(url); return response.data.data; } + +export async function getUserHistory (store, payload) { + const url = `/api/v4/admin/user/${payload.userIdentifier}/history`; + const response = await axios.get(url); + return response.data.data; +} diff --git a/website/server/controllers/api-v3/quests.js b/website/server/controllers/api-v3/quests.js index a738df8238..4e81f744b2 100644 --- a/website/server/controllers/api-v3/quests.js +++ b/website/server/controllers/api-v3/quests.js @@ -229,7 +229,7 @@ api.acceptQuest = { headers: req.headers, }); - await UserHistory.beginUserHistoryUpdate(user._id) + await UserHistory.beginUserHistoryUpdate(user._id, req.headers['x-client']) .withQuestInviteResponse(group.quest.key, 'accept') .commit(); }, @@ -294,7 +294,7 @@ api.rejectQuest = { headers: req.headers, }); - await UserHistory.beginUserHistoryUpdate(user._id) + await UserHistory.beginUserHistoryUpdate(user._id, req.headers['x-client']) .withQuestInviteResponse(group.quest.key, 'reject') .commit(); }, diff --git a/website/server/controllers/api-v3/user.js b/website/server/controllers/api-v3/user.js index c4706b1979..fc3c79620e 100644 --- a/website/server/controllers/api-v3/user.js +++ b/website/server/controllers/api-v3/user.js @@ -504,7 +504,7 @@ api.buy = { await user.save(); if (type === 'armoire') { - await UserHistory.beginUserHistoryUpdate(user._id) + await UserHistory.beginUserHistoryUpdate(user._id, req.headers['x-client']) .withArmoire(buyRes[0].armoire.dropKey || 'experience') .commit(); } @@ -601,7 +601,7 @@ api.buyArmoire = { } const buyArmoireResponse = await common.ops.buy(user, req, res.analytics); await user.save(); - await UserHistory.beginUserHistoryUpdate(user._id) + await UserHistory.beginUserHistoryUpdate(user._id, req.headers['x-client']) .withArmoire(buyArmoireResponse[1].data.armoire.dropKey) .commit(); res.respond(200, ...buyArmoireResponse); diff --git a/website/server/controllers/api-v4/admin.js b/website/server/controllers/api-v4/admin.js index 01b8e4b4f6..bf1decf363 100644 --- a/website/server/controllers/api-v4/admin.js +++ b/website/server/controllers/api-v4/admin.js @@ -2,6 +2,10 @@ import validator from 'validator'; import { authWithHeaders } from '../../middlewares/auth'; import { ensurePermission } from '../../middlewares/ensureAccessRight'; import { model as User } from '../../models/user'; +import { model as UserHistory } from '../../models/userHistory'; +import { + NotFound, +} from '../../libs/errors'; const api = {}; @@ -21,7 +25,7 @@ const api = {}; * @apiUse NoUser * @apiUse NotAdmin */ -api.getHero = { +api.searchHero = { method: 'GET', url: '/admin/search/:userIdentifier', middlewares: [authWithHeaders(), ensurePermission('userSupport')], @@ -73,4 +77,43 @@ api.getHero = { }, }; +/** + * @api {get} /api/v4/admin/user/:userId/history Get the history of a user + * @apiParam (Path) {String} userIdentifier The username or email of the user + * @apiName GetUserHistory + * @apiGroup Admin + * @apiPermission Admin + * + * @apiDescription Returns the history of a user + * + * @apiSuccess {Object} data The User history + * + * @apiUse NoAuthHeaders + * @apiUse NoAccount + * @apiUse NoUser + * @apiUse NotAdmin + */ +api.getUserHistory = { + method: 'GET', + url: '/admin/user/:userId/history', + middlewares: [authWithHeaders(), ensurePermission('userSupport')], + async handler (req, res) { + req.checkParams('userId', res.t('heroIdRequired')).notEmpty().isUUID(); + + const validationErrors = req.validationErrors(); + if (validationErrors) throw validationErrors; + + const { userId } = req.params; + + const history = await UserHistory + .findOne({ userId }) + .lean() + .exec(); + + if (!history) throw new NotFound(res.t('userWithIDNotFound', { userId })); + + res.respond(200, history); + }, +}; + export default api; diff --git a/website/server/libs/cron.js b/website/server/libs/cron.js index bf746e37a3..e57f4bc49f 100644 --- a/website/server/libs/cron.js +++ b/website/server/libs/cron.js @@ -524,7 +524,7 @@ export async function cron (options = {}) { user.flags.cronCount += 1; trackCronAnalytics(analytics, user, _progress, options); - await UserHistory.beginUserHistoryUpdate(user._id) + await UserHistory.beginUserHistoryUpdate(user._id, options.headers['x-client']) .withCron() .commit(); diff --git a/website/server/models/userHistory.js b/website/server/models/userHistory.js index 53a51a9279..f91719200c 100644 --- a/website/server/models/userHistory.js +++ b/website/server/models/userHistory.js @@ -17,6 +17,7 @@ export const schema = new Schema({ { _id: false, timestamp: { $type: Date, required: true }, + client: { $type: String, required: false }, reward: { $type: String, required: true }, }, ], @@ -24,6 +25,7 @@ export const schema = new Schema({ { _id: false, timestamp: { $type: Date, required: true }, + client: { $type: String, required: false }, quest: { $type: String, required: true }, response: { $type: String, required: true }, }, @@ -32,6 +34,7 @@ export const schema = new Schema({ { _id: false, timestamp: { $type: Date, required: true }, + client: { $type: String, required: false }, }, ], }, { @@ -81,10 +84,11 @@ const commitUserHistoryUpdate = function commitUserHistoryUpdate (update) { ).exec(); }; -model.beginUserHistoryUpdate = function beginUserHistoryUpdate (userID) { +model.beginUserHistoryUpdate = function beginUserHistoryUpdate (userID, client=null) { return { userId: userID, data: { + client, armoire: [], questInviteResponses: [], cron: [], @@ -92,6 +96,7 @@ model.beginUserHistoryUpdate = function beginUserHistoryUpdate (userID) { withArmoire: function withArmoire (reward) { this.data.armoire.push({ timestamp: new Date(), + client: this.data.client, reward, }); return this; @@ -99,6 +104,7 @@ model.beginUserHistoryUpdate = function beginUserHistoryUpdate (userID) { withQuestInviteResponse: function withQuestInviteResponse (quest, response) { this.data.questInviteResponses.push({ timestamp: new Date(), + client: this.data.client, quest, response, }); @@ -107,6 +113,7 @@ model.beginUserHistoryUpdate = function beginUserHistoryUpdate (userID) { withCron: function withCron () { this.data.cron.push({ timestamp: new Date(), + client: this.data.client, }); return this; },