Quellcode durchsuchen

Merge branch 'odiffey-owen-469-470' into polishing

Kristian Vos vor 3 Jahren
Ursprung
Commit
5abf963786

+ 3 - 2
backend/logic/actions/users.js

@@ -531,7 +531,7 @@ export default {
 				(hash, _id, next) => {
 					next(null, {
 						_id,
-						name: "",
+						name: username,
 						username,
 						email: {
 							address: email,
@@ -548,9 +548,10 @@ export default {
 				// generate the url for gravatar avatar
 				(user, next) => {
 					UtilsModule.runJob("CREATE_GRAVATAR", { email: user.email.address }, this).then(url => {
+						const avatarColors = ["blue", "orange", "green", "purple", "teal"];
 						user.avatar = {
 							type: "initials",
-							color: "blue",
+							color: avatarColors[Math.floor(Math.random() * avatarColors.length)],
 							url
 						};
 						next(null, user);

+ 15 - 2
backend/logic/db/index.js

@@ -22,7 +22,7 @@ const regex = {
 	az09_: /^[a-z0-9_]+$/,
 	emailSimple: /^[\x00-\x7F]+@[a-z0-9]+\.[a-z0-9]+(\.[a-z0-9]+)?$/,
 	ascii: /^[\x00-\x7F]+$/,
-	name: /^[\p{L} .'-]+$/u,
+	name: /^[\p{L}0-9 .'_-]+$/u,
 	custom: regex => new RegExp(`^[${regex}]+$`)
 };
 
@@ -126,7 +126,10 @@ class _DBModule extends CoreClass {
 					this.schemas.user
 						.path("username")
 						.validate(
-							username => isLength(username, 2, 32) && regex.custom("a-zA-Z0-9_-").test(username),
+							username =>
+								isLength(username, 2, 32) &&
+								regex.custom("a-zA-Z0-9_-").test(username) &&
+								username.replaceAll(/[_]/g, "").length > 0,
 							"Invalid username."
 						);
 
@@ -136,6 +139,16 @@ class _DBModule extends CoreClass {
 						return regex.emailSimple.test(email) && regex.ascii.test(email);
 					}, "Invalid email.");
 
+					this.schemas.user
+						.path("name")
+						.validate(
+							name =>
+								isLength(name, 1, 64) &&
+								regex.name.test(name) &&
+								name.replaceAll(/[ .'_-]/g, "").length > 0,
+							"Invalid name."
+						);
+
 					// Station
 					this.schemas.station
 						.path("name")

+ 1 - 1
backend/logic/db/schemas/user.js

@@ -36,7 +36,7 @@ export default {
 	likedSongsPlaylist: { type: mongoose.Schema.Types.ObjectId },
 	dislikedSongsPlaylist: { type: mongoose.Schema.Types.ObjectId },
 	favoriteStations: [{ type: String }],
-	name: { type: String, default: "" },
+	name: { type: String, required: true },
 	location: { type: String, default: "" },
 	bio: { type: String, default: "" },
 	createdAt: { type: Date, default: Date.now },

+ 1 - 2
backend/logic/migration/index.js

@@ -38,8 +38,7 @@ class _MigrationModule extends CoreClass {
 			mongoose
 				.connect(mongoUrl, {
 					useNewUrlParser: true,
-					useUnifiedTopology: true,
-					useCreateIndex: true
+					useUnifiedTopology: true
 				})
 				.then(async () => {
 					mongoose.connection.on("error", err => this.log("ERROR", err));

+ 46 - 0
backend/logic/migration/migrations/migration15.js

@@ -0,0 +1,46 @@
+import async from "async";
+
+/**
+ * Migration 15
+ *
+ * Migration for setting user name to username if not set
+ *
+ * @param {object} MigrationModule - the MigrationModule
+ * @returns {Promise} - returns promise
+ */
+export default async function migrate(MigrationModule) {
+	const userModel = await MigrationModule.runJob("GET_MODEL", { modelName: "user" }, this);
+
+	return new Promise((resolve, reject) => {
+		async.waterfall(
+			[
+				next => {
+					this.log("INFO", `Migration 15. Finding users with document version 3.`);
+					userModel.find({ documentVersion: 3, name: { $in: [null, ""] } }, (err, users) => {
+						if (err) next(err);
+						else {
+							async.eachLimit(
+								users.map(user => user._doc),
+								1,
+								(user, next) => {
+									userModel.updateOne({ _id: user._id }, { $set: { name: user.username } }, next);
+								},
+								err => {
+									this.log("INFO", `Migration 15. Users found: ${users.length}.`);
+									next(err);
+								}
+							);
+						}
+					});
+				}
+			],
+			err => {
+				if (err) {
+					reject(new Error(err));
+				} else {
+					resolve();
+				}
+			}
+		);
+	});
+}

+ 10 - 0
frontend/src/components/UserIdToUsername.vue

@@ -34,3 +34,13 @@ export default {
 	}
 };
 </script>
+
+<style lang="scss" scoped>
+a {
+	color: var(--primary-color);
+	&:hover,
+	&:focus {
+		filter: brightness(90%);
+	}
+}
+</style>

+ 4 - 0
frontend/src/components/modals/Register.vue

@@ -201,6 +201,10 @@ export default {
 				this.username.message =
 					"Invalid format. Allowed characters: a-z, A-Z, 0-9 and _.";
 				this.username.valid = false;
+			} else if (value.replaceAll(/[_]/g, "").length === 0) {
+				this.username.message =
+					"Invalid format. Allowed characters: a-z, A-Z, 0-9 and _, and there has to be at least one letter or number.";
+				this.username.valid = false;
 			} else {
 				this.username.message = "Everything looks great!";
 				this.username.valid = true;

+ 17 - 8
frontend/src/pages/Settings/Tabs/Account.vue

@@ -126,17 +126,21 @@ export default {
 		// prettier-ignore
 		// eslint-disable-next-line func-names
 		"modifiedUser.username": function (value) {
-		if (!validation.isLength(value, 2, 32)) {
-			this.validation.username.message =
-				"Username must have between 2 and 32 characters.";
-			this.validation.username.valid = false;
-		} else if (
-			!validation.regex.azAZ09_.test(value) &&
-			value !== this.originalUser.username // Sometimes a username pulled from GitHub won't succeed validation
-		) {
+			if (!validation.isLength(value, 2, 32)) {
+				this.validation.username.message =
+					"Username must have between 2 and 32 characters.";
+				this.validation.username.valid = false;
+			} else if (
+				!validation.regex.azAZ09_.test(value) &&
+				value !== this.originalUser.username // Sometimes a username pulled from GitHub won't succeed validation
+			) {
 				this.validation.username.message =
 					"Invalid format. Allowed characters: a-z, A-Z, 0-9 and _.";
 				this.validation.username.valid = false;
+			} else if (value.replaceAll(/[_]/g, "").length === 0) {
+				this.validation.username.message =
+					"Invalid format. Allowed characters: a-z, A-Z, 0-9 and _, and there has to be at least one letter or number.";
+				this.validation.username.valid = false;
 			} else {
 				this.validation.username.message = "Everything looks great!";
 				this.validation.username.valid = true;
@@ -246,6 +250,11 @@ export default {
 					"Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _."
 				);
 
+			if (username.replaceAll(/[_]/g, "").length === 0)
+				return new Toast(
+					"Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _, and there has to be at least one letter or number."
+				);
+
 			this.$refs.saveButton.saveStatus = "disabled";
 
 			return this.socket.dispatch(

+ 3 - 3
frontend/src/pages/Settings/Tabs/Profile.vue

@@ -148,11 +148,11 @@ export default {
 
 			if (!validation.regex.name.test(name))
 				return new Toast(
-					"Invalid name format. Only letters, spaces, apostrophes and hyphens are allowed."
+					"Invalid name format. Only letters, numbers, spaces, apostrophes, underscores and hyphens are allowed."
 				);
-			if (name.replaceAll(/[ .'-]/g, "").length === 0)
+			if (name.replaceAll(/[ .'_-]/g, "").length === 0)
 				return new Toast(
-					"Invalid name format. Only letters, spaces, apostrophes and hyphens are allowed, and there has to be at least one letter."
+					"Invalid name format. Only letters, numbers, spaces, apostrophes, underscores and hyphens are allowed, and there has to be at least one letter or number."
 				);
 
 			this.$refs.saveButton.status = "disabled";

+ 7 - 0
frontend/src/store/modules/user.js

@@ -62,6 +62,13 @@ const modules = {
 							)
 						);
 
+					if (username.replaceAll(/[_]/g, "").length === 0)
+						return reject(
+							new Error(
+								"Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _, and there has to be at least one letter or number."
+							)
+						);
+
 					if (!validation.isLength(password, 6, 200))
 						return reject(
 							new Error(

+ 1 - 1
frontend/src/validation.js

@@ -4,7 +4,7 @@ export default {
 		az09_: /^[a-z0-9_]+$/,
 		emailSimple: /^[\x00-\x7F]+@[a-z0-9]+\.[a-z0-9]+(\.[a-z0-9]+)?$/,
 		ascii: /^[\x00-\x7F]+$/,
-		name: /^[\p{L} .'-]+$/u,
+		name: /^[\p{L}0-9 .'_-]+$/u,
 		password:
 			/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~])[A-Za-z\d!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]/,
 		custom: regex => new RegExp(`^[${regex}]+$`)