Browse Source

feat(AccountRemoval): admin tab now shows unresolved data requests

Signed-off-by: Jonathan <theflametrooper@gmail.com>
Jonathan 3 years ago
parent
commit
df24f8f606

+ 102 - 0
backend/logic/actions/dataRequests.js

@@ -0,0 +1,102 @@
+import async from "async";
+
+import { isAdminRequired } from "./hooks";
+
+import moduleManager from "../../index";
+
+const DBModule = moduleManager.modules.db;
+const UtilsModule = moduleManager.modules.utils;
+const WSModule = moduleManager.modules.ws;
+const CacheModule = moduleManager.modules.cache;
+
+CacheModule.runJob("SUB", {
+	channel: "dataRequest.resolve",
+	cb: dataRequestId => {
+		WSModule.runJob("EMIT_TO_ROOM", {
+			room: "admin.users",
+			args: ["event:admin.dataRequests.resolved", { data: { dataRequestId } }]
+		});
+	}
+});
+
+export default {
+	/**
+	 * Gets all unresolved data requests
+	 *
+	 * @param {object} session - the session object automatically added by the websocket
+	 * @param {Function} cb - gets called with the result
+	 */
+	index: isAdminRequired(async function index(session, cb) {
+		const dataRequestModel = await DBModule.runJob("GET_MODEL", { modelName: "dataRequest" }, this);
+
+		async.waterfall(
+			[
+				next => {
+					dataRequestModel.find({ resolved: false }).sort({ createdAt: "desc" }).exec(next);
+				}
+			],
+			async (err, requests) => {
+				if (err) {
+					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
+					this.log("ERROR", "DATA_REQUESTS_INDEX", `Indexing data requests failed. "${err}"`);
+					return cb({ status: "error", message: err });
+				}
+
+				this.log("SUCCESS", "DATA_REQUESTS_INDEX", `Indexing data requests successful.`, false);
+
+				return cb({ status: "success", data: { requests } });
+			}
+		);
+	}),
+
+	/**
+	 * Resolves a data request
+	 *
+	 * @param {object} session - the session object automatically added by the websocket
+	 * @param {object} dataRequestId - the id of the data request to resolve
+	 * @param {Function} cb - gets called with the result
+	 */
+	resolve: isAdminRequired(async function update(session, dataRequestId, cb) {
+		const dataRequestModel = await DBModule.runJob("GET_MODEL", { modelName: "dataRequest" }, this);
+
+		async.waterfall(
+			[
+				next => {
+					if (!dataRequestId || typeof dataRequestId !== "string")
+						return next("Please provide a data request id.");
+					return next();
+				},
+
+				next => {
+					dataRequestModel.updateOne({ _id: dataRequestId }, { resolved: true }, { upsert: true }, err =>
+						next(err)
+					);
+				}
+			],
+			async err => {
+				if (err) {
+					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
+					this.log(
+						"ERROR",
+						"DATA_REQUESTS_RESOLVE",
+						`Resolving data request ${dataRequestId} failed for user "${session.userId}". "${err}"`
+					);
+					return cb({ status: "error", message: err });
+				}
+
+				CacheModule.runJob("PUB", { channel: "dataRequest.resolve", value: dataRequestId });
+
+				this.log(
+					"SUCCESS",
+					"DATA_REQUESTS_RESOLVE",
+					`Resolving data request "${dataRequestId}" successful for user ${session.userId}".`
+				);
+
+				return cb({
+					status: "success",
+					message: "Successfully resolved data request."
+				});
+			}
+		);
+	})
+};

+ 2 - 0
backend/logic/actions/index.js

@@ -3,6 +3,7 @@ import songs from "./songs";
 import stations from "./stations";
 import playlists from "./playlists";
 import users from "./users";
+import dataRequests from "./dataRequests";
 import activities from "./activities";
 import reports from "./reports";
 import news from "./news";
@@ -15,6 +16,7 @@ export default {
 	stations,
 	playlists,
 	users,
+	dataRequests,
 	activities,
 	reports,
 	news,

+ 10 - 1
backend/logic/actions/users.js

@@ -277,7 +277,16 @@ export default {
 					dataRequestModel.create({ userId: session.userId, type: "remove" }, next);
 				},
 
-				(request, next) => userModel.find({ role: "admin" }, next),
+				(request, next) => {
+					WSModule.runJob("EMIT_TO_ROOM", {
+						room: "admin.users",
+						args: ["event:admin.dataRequests.created", { data: { request } }]
+					});
+
+					return next();
+				},
+
+				next => userModel.find({ role: "admin" }, next),
 
 				// send email to all admins of a data removal request
 				(users, next) => {

+ 57 - 0
frontend/src/pages/Admin/tabs/Users.vue

@@ -2,6 +2,40 @@
 	<div>
 		<metadata title="Admin | Users" />
 		<div class="container">
+			<h2 v-if="dataRequests.length > 0">Data Requests</h2>
+
+			<table class="table is-striped" v-if="dataRequests.length > 0">
+				<thead>
+					<tr>
+						<td>User ID</td>
+						<td>Request Type</td>
+						<td>Options</td>
+					</tr>
+				</thead>
+				<tbody>
+					<tr v-for="(request, index) in dataRequests" :key="index">
+						<td>{{ request.userId }}</td>
+						<td>
+							{{
+								request.type === "remove"
+									? "Remove Account"
+									: request.type
+							}}
+						</td>
+						<td>
+							<button
+								class="button is-primary"
+								@click="resolveDataRequest(request._id)"
+							>
+								Resolve
+							</button>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+
+			<h2>Users</h2>
+
 			<table class="table is-striped">
 				<thead>
 					<tr>
@@ -67,6 +101,7 @@
 
 <script>
 import { mapState, mapActions, mapGetters } from "vuex";
+import Toast from "toasters";
 
 import ProfilePicture from "@/components/ProfilePicture.vue";
 import ws from "@/ws";
@@ -79,6 +114,7 @@ export default {
 	data() {
 		return {
 			editingUserId: "",
+			dataRequests: [],
 			users: []
 		};
 	},
@@ -111,7 +147,28 @@ export default {
 					}
 				}
 			});
+
+			this.socket.dispatch("dataRequests.index", res => {
+				if (res.status === "success")
+					this.dataRequests = res.data.requests;
+			});
+
 			this.socket.dispatch("apis.joinAdminRoom", "users", () => {});
+
+			this.socket.on("event:admin.dataRequests.created", res =>
+				this.dataRequests.push(res.data.request)
+			);
+
+			this.socket.on("event:admin.dataRequests.resolved", res => {
+				this.dataRequests = this.dataRequests.filter(
+					request => request._id !== res.data.dataRequestId
+				);
+			});
+		},
+		resolveDataRequest(id) {
+			this.socket.dispatch("dataRequests.resolve", id, res => {
+				if (res.status === "success") new Toast(res.message);
+			});
 		},
 		...mapActions("modalVisibility", ["openModal"])
 	}