From 08fcec36ba6dd2b49941c367d52c333c055e0eb7 Mon Sep 17 00:00:00 2001 From: Puyodead1 Date: Wed, 14 Jun 2023 11:37:17 -0400 Subject: [PATCH] update user modify for unique usernames --- assets/locales/en/common.json | 6 +- src/api/routes/users/@me/index.ts | 67 +++++++++++++++++-- src/schemas/uncategorised/UserModifySchema.ts | 22 ++---- src/util/config/types/UsersConfiguration.ts | 2 +- .../subconfigurations/limits/UserLimits.ts | 1 + src/util/entities/User.ts | 13 ++-- 6 files changed, 80 insertions(+), 31 deletions(-) diff --git a/assets/locales/en/common.json b/assets/locales/en/common.json index edcafa1da..dcb97ec0b 100644 --- a/assets/locales/en/common.json +++ b/assets/locales/en/common.json @@ -13,7 +13,11 @@ "BASE_TYPE_CONSTANT": "This field must be {{value}}", "EMAIL_TYPE_INVALID_EMAIL": "Not a well-formed email address", "DATE_TYPE_PARSE": "Could not parse {{date}}. Should be ISO8601", - "BASE_TYPE_BAD_LENGTH": "Must be between {{length}} in length" + "BASE_TYPE_BAD_LENGTH": "Must be between {{length}} in length", + "PASSWORD_DOES_NOT_MATCH": "Password does not match.", + "DISCRIMINATOR_UPDATE_BLOCKED": "This discriminator cannot be updated", + "USERNAME_ALREADY_TAKEN": "Username is unavailable. Try adding numbers, letters, underscores _ , or periods.", + "INVALID_DISCRIMINATOR": "This discriminator is already in use." }, "body": { "INVALID_BODY": "Invalid Body", diff --git a/src/api/routes/users/@me/index.ts b/src/api/routes/users/@me/index.ts index 796d56297..99ff2b24b 100644 --- a/src/api/routes/users/@me/index.ts +++ b/src/api/routes/users/@me/index.ts @@ -61,6 +61,8 @@ router.patch( }), async (req: Request, res: Response) => { const body = req.body as UserModifySchema; + const { uniqueUsernames } = Config.get().general; + const { minUsername, maxUsername } = Config.get().limits.user; const user = await User.findOneOrFail({ where: { id: req.user_id }, @@ -120,9 +122,53 @@ router.patch( newToken = (await generateToken(user.id)) as string; } - // TODO: uniqueUsernames: disallow if uniqueUsernames is enabled if (body.username) { - const check_username = body?.username?.replace(/\s/g, "").trim(); + // password is required to update username + if (!body.password) + throw FieldErrors({ + password: { + message: req.t("common:field.PASSWORD_DOES_NOT_MATCH"), + code: "PASSWORD_DOES_NOT_MATCH", + }, + }); + + // handle username changes (pomelo) + if (uniqueUsernames) { + body.username = body.username.toLowerCase(); + // validate username length + if ( + body.username.length < minUsername || + body.username.length > maxUsername + ) { + throw FieldErrors({ + username: { + code: "BASE_TYPE_BAD_LENGTH", + message: req.t( + "common:field.BASE_TYPE_BAD_LENGTH", + { length: `${minUsername} and ${maxUsername}` }, + ), + }, + }); + } + + // check if username is already taken (pomelo only) + const userCount = await User.count({ + where: { username: body.username }, + }); + if (userCount > 0) { + throw FieldErrors({ + username: { + code: "USERNAME_ALREADY_TAKEN", + message: req.t( + "common:field.USERNAME_ALREADY_TAKEN", + ), + }, + }); + } + } + + // handle username changes (old username system) + const check_username = body?.username?.replace(/\s/g, ""); if (!check_username) { throw FieldErrors({ username: { @@ -132,8 +178,7 @@ router.patch( }); } - const { maxUsername } = Config.get().limits.user; - if (check_username.length > maxUsername || check_username.length < 2) { + if (check_username.length > maxUsername) { throw FieldErrors({ username: { code: "BASE_TYPE_BAD_LENGTH", @@ -152,8 +197,18 @@ router.patch( } } - // TODO: uniqueUsernames: disallow if uniqueUsernames is enabled if (body.discriminator) { + if (uniqueUsernames) { + throw FieldErrors({ + username: { + code: "DISCRIMINATOR_UPDATE_BLOCKED", + message: req.t( + "common:field.DISCRIMINATOR_UPDATE_BLOCKED", + ), + }, + }); + } + if ( await User.findOne({ where: { @@ -165,7 +220,7 @@ router.patch( throw FieldErrors({ discriminator: { code: "INVALID_DISCRIMINATOR", - message: "This discriminator is already in use.", + message: req.t("common.field.INVALID_DISCRIMINATOR"), }, }); } diff --git a/src/schemas/uncategorised/UserModifySchema.ts b/src/schemas/uncategorised/UserModifySchema.ts index c7228e7b9..a135d8b1b 100644 --- a/src/schemas/uncategorised/UserModifySchema.ts +++ b/src/schemas/uncategorised/UserModifySchema.ts @@ -18,35 +18,25 @@ export interface UserModifySchema { /** - * @minLength 2 + * @minLength 1 + * @maxLength 100 */ username?: string; avatar?: string | null; + /** + * @maxLength 1024 + */ bio?: string; accent_color?: number; banner?: string | null; - /** - * @minLength 1 - * @maxLength 72 - */ password?: string; - /** - * @minLength 1 - * @maxLength 72 - */ new_password?: string; - /** - * @minLength 6 - * @maxLength 6 - */ code?: string; - /** - * @TJS-format email - */ email?: string; /** * @minLength 4 * @maxLength 4 */ discriminator?: string; + global_name?: string; } diff --git a/src/util/config/types/UsersConfiguration.ts b/src/util/config/types/UsersConfiguration.ts index 2ac058e92..6b61637e6 100755 --- a/src/util/config/types/UsersConfiguration.ts +++ b/src/util/config/types/UsersConfiguration.ts @@ -19,4 +19,4 @@ export class UserConfiguration { blockedContains: string[] = ["discord", "clyde", "spacebar", "steam", "community", "support", "ticket"]; blockedEquals: string[] = ["everyone", "here"]; -} +} \ No newline at end of file diff --git a/src/util/config/types/subconfigurations/limits/UserLimits.ts b/src/util/config/types/subconfigurations/limits/UserLimits.ts index cbe686d14..6fe4d8d81 100644 --- a/src/util/config/types/subconfigurations/limits/UserLimits.ts +++ b/src/util/config/types/subconfigurations/limits/UserLimits.ts @@ -18,6 +18,7 @@ export class UserLimits { maxGuilds: number = 1048576; + minUsername: number = 2; maxUsername: number = 32; maxFriends: number = 5000; maxBio: number = 190; diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts index e955bd004..1d232fb08 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts @@ -32,7 +32,6 @@ import { ChannelType, PrivateUserProjection, PublicUser, PublicUserProjection, U export enum PublicUserEnum { username, global_name, - legacy_username, discriminator, id, public_flags, @@ -195,7 +194,7 @@ export class User extends BaseClass { // TODO: I don't like this method? validate() { - if (this.discriminator) { + if (this.discriminator && this.discriminator !== "0") { const discrim = Number(this.discriminator); if (isNaN(discrim) || !(typeof discrim == "number") || !Number.isInteger(discrim) || discrim <= 0 || discrim >= 10000) throw FieldErrors({ @@ -273,9 +272,8 @@ export class User extends BaseClass { public get tag(): string { const { uniqueUsernames } = Config.get().general; - // if uniqueUsernames is enabled, global_name should be set return uniqueUsernames - ? (this.global_name as string) + ? this.username : `${this.username}#${this.discriminator}`; } @@ -325,7 +323,7 @@ export class User extends BaseClass { }); const user = User.create({ - username: username, + username: uniqueUsernames ? username.toLowerCase() : username, discriminator, id: id || Snowflake.generate(), email: email, @@ -334,8 +332,9 @@ export class User extends BaseClass { valid_tokens_since: new Date(), }, settings: settings, - - premium_since: Config.get().defaults.user.premium ? new Date() : undefined, + premium_since: Config.get().defaults.user.premium + ? new Date() + : undefined, rights: Config.get().register.defaultRights, premium: Config.get().defaults.user.premium ?? false, premium_type: Config.get().defaults.user.premiumType ?? 0,