Преглед на файлове

feat(Settings): added option for user to completely delete account

Signed-off-by: Jonathan <theflametrooper@gmail.com>
Jonathan преди 4 години
родител
ревизия
d3125b4e5b
променени са 3 файла, в които са добавени 131 реда и са изтрити 60 реда
  1. 62 29
      backend/logic/actions/users.js
  2. 59 0
      frontend/src/pages/Settings/tabs/Account.vue
  3. 10 31
      frontend/src/pages/Settings/tabs/Security.vue

+ 62 - 29
backend/logic/actions/users.js

@@ -196,6 +196,63 @@ export default {
 		);
 	}),
 
+	/**
+	 * Removes all data held on a user, including their ability to login
+	 *
+	 * @param {object} session - the session object automatically added by socket.io
+	 * @param {Function} cb - gets called with the result
+	 */
+	remove: isLoginRequired(async function remove(session, cb) {
+		const userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" }, this);
+		const stationModel = await DBModule.runJob("GET_MODEL", { modelName: "station" }, this);
+		const playlistModel = await DBModule.runJob("GET_MODEL", { modelName: "playlist" }, this);
+		const activityModel = await DBModule.runJob("GET_MODEL", { modelName: "activity" }, this);
+
+		async.waterfall(
+			[
+				next => {
+					activityModel.deleteMany({ userId: session.userId }, next);
+				},
+
+				(res, next) => {
+					stationModel.deleteMany({ owner: session.userId }, next);
+				},
+
+				(res, next) => {
+					playlistModel.deleteMany({ createdBy: session.userId }, next);
+				},
+
+				(res, next) => {
+					userModel.deleteMany({ _id: session.userId }, next);
+				}
+			],
+			async err => {
+				console.log(err);
+
+				if (err && err !== true) {
+					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
+					this.log(
+						"ERROR",
+						"USER_REMOVE",
+						`Removing data and account for user "${session.userId}" failed. "${err}"`
+					);
+					return cb({ status: "failure", message: err });
+				}
+
+				this.log(
+					"SUCCESS",
+					"USER_REMOVE",
+					`Successfully removed data and account for user "${session.userId}"`
+				);
+
+				return cb({
+					status: "success",
+					message: "Successfully removed data and account."
+				});
+			}
+		);
+	}),
+
 	/**
 	 * Logs user in
 	 *
@@ -207,13 +264,7 @@ export default {
 	async login(session, identifier, password, cb) {
 		identifier = identifier.toLowerCase();
 		const userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" }, this);
-		const sessionSchema = await CacheModule.runJob(
-			"GET_SCHEMA",
-			{
-				schemaName: "session"
-			},
-			this
-		);
+		const sessionSchema = await CacheModule.runJob("GET_SCHEMA", { schemaName: "session" }, this);
 
 		async.waterfall(
 			[
@@ -519,17 +570,8 @@ export default {
 		async.waterfall(
 			[
 				next => {
-					CacheModule.runJob(
-						"HGET",
-						{
-							table: "sessions",
-							key: session.sessionId
-						},
-						this
-					)
-						.then(session => {
-							next(null, session);
-						})
+					CacheModule.runJob("HGET", { table: "sessions", key: session.sessionId }, this)
+						.then(session => next(null, session))
 						.catch(next);
 				},
 
@@ -539,17 +581,8 @@ export default {
 				},
 
 				(session, next) => {
-					CacheModule.runJob(
-						"HDEL",
-						{
-							table: "sessions",
-							key: session.sessionId
-						},
-						this
-					)
-						.then(() => {
-							next();
-						})
+					CacheModule.runJob("HDEL", { table: "sessions", key: session.sessionId }, this)
+						.then(() => next())
 						.catch(next);
 				}
 			],

+ 59 - 0
frontend/src/pages/Settings/tabs/Account.vue

@@ -61,6 +61,46 @@
 				v-html="saveButtonMessage"
 			/>
 		</transition>
+
+		<!-- <div class="section-margin-bottom" />
+
+		<h4 class="section-title">Export my data</h4>
+
+		<p class="section-description">
+			Download a copy of all data we store on you in JSON format.
+		</p>
+
+		<hr class="section-horizontal-rule" /> -->
+
+		<div class="section-margin-bottom" />
+
+		<h4 class="section-title">Remove any data we hold on you</h4>
+
+		<p class="section-description">
+			Permanently remove your account and/or data we store on you.
+		</p>
+
+		<hr class="section-horizontal-rule" />
+
+		<div>
+			<a
+				class="button is-warning"
+				href="#"
+				@click.prevent="removeActivities()"
+			>
+				<i class="material-icons icon-with-button">clear</i>
+				Clear my activities
+			</a>
+
+			<a
+				class="button is-danger"
+				href="#"
+				@click.prevent="removeAccount()"
+			>
+				<i class="material-icons icon-with-button">delete</i>
+				Remove my account
+			</a>
+		</div>
 	</div>
 </template>
 
@@ -250,6 +290,25 @@ export default {
 				}
 			);
 		},
+		removeAccount() {
+			return this.socket.emit("users.remove", res => {
+				if (res.status === "success") {
+					return this.socket.emit("users.logout", () => {
+						return lofig.get("cookie").then(cookie => {
+							document.cookie = `${cookie.SIDname}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
+							return window.location.reload();
+						});
+					});
+				}
+
+				return new Toast({ content: res.message, timeout: 8000 });
+			});
+		},
+		removeActivities() {
+			this.socket.emit("activities.removeAllForUser", res => {
+				new Toast({ content: res.message, timeout: 4000 });
+			});
+		},
 		...mapActions("settings", ["updateOriginalUser"])
 	}
 };

+ 10 - 31
frontend/src/pages/Settings/tabs/Security.vue

@@ -99,22 +99,24 @@
 
 			<hr class="section-horizontal-rule" />
 
-			<div id="remove-login-method-buttons">
+			<div>
 				<a
 					v-if="isPasswordLinked"
 					class="button is-danger"
 					href="#"
 					@click.prevent="unlinkPassword()"
-					><i class="material-icons icon-with-button">close</i>Remove
-					password
+				>
+					<i class="material-icons icon-with-button">close</i>
+					Remove password
 				</a>
 
 				<a
 					class="button is-danger"
 					href="#"
 					@click.prevent="unlinkGitHub()"
-					><i class="material-icons icon-with-button">link_off</i
-					>Remove GitHub from account
+				>
+					<i class="material-icons icon-with-button">link_off</i>
+					Remove GitHub from account
 				</a>
 			</div>
 
@@ -133,27 +135,9 @@
 				class="button is-warning"
 				href="#"
 				@click.prevent="removeSessions()"
-				><i class="material-icons icon-with-button">exit_to_app</i>Log
-				out everywhere
-			</a>
-
-			<div class="section-margin-bottom" />
-		</div>
-
-		<div>
-			<h4 class="section-title">Clear my activities</h4>
-			<p class="section-description">
-				Permanently remove all my logged activity on Musare to date.
-			</p>
-
-			<hr class="section-horizontal-rule" />
-
-			<a
-				class="button is-warning"
-				href="#"
-				@click.prevent="deleteActivities()"
-				><i class="material-icons icon-with-button">delete</i>Clear my
-				activities
+			>
+				<i class="material-icons icon-with-button">exit_to_app</i>
+				Log out everywhere
 			</a>
 		</div>
 	</div>
@@ -269,11 +253,6 @@ export default {
 			this.socket.emit(`users.removeSessions`, this.userId, res => {
 				new Toast({ content: res.message, timeout: 4000 });
 			});
-		},
-		deleteActivities() {
-			this.socket.emit(`activities.removeAllForUser`, res => {
-				new Toast({ content: res.message, timeout: 4000 });
-			});
 		}
 	}
 };