From 07569328b14d346eb45bf865e9f610dc3f689ee6 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 +- assets/locales/ur/auth.json | 4 +- assets/openapi.json | Bin 575186 -> 575462 bytes assets/schemas.json | Bin 18404127 -> 18718290 bytes src/api/routes/users/@me/index.ts | 63 ++++++++++++++++-- .../subconfigurations/limits/UserLimits.ts | 1 + src/util/entities/User.ts | 9 +-- src/util/schemas/UserModifySchema.ts | 1 + 8 files changed, 71 insertions(+), 13 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/assets/locales/ur/auth.json b/assets/locales/ur/auth.json index 1dac24744..93631abcf 100644 --- a/assets/locales/ur/auth.json +++ b/assets/locales/ur/auth.json @@ -10,8 +10,8 @@ "EMAIL_INVALID": "Invalid Email", "EMAIL_ALREADY_REGISTERED": "Email is already registered", "DATE_OF_BIRTH_UNDERAGE": "You need to be {{years}} years or older", - "PASSWORD_REQUIREMENTS_MIN_LENGTH": "Must be at least {{min}} characters long.", - "CONSENT_REQUIRED": "You must agree to the Terms of Service and Privacy Policy.", + "PASSWORD_REQUIREMENTS_MIN_LENGTH": "Must be at least {{min}} characters long.", + "CONSENT_REQUIRED": "You must agree to the Terms of Service and Privacy Policy.", "USERNAME_TOO_MANY_USERS": "Too many users have this username, please try another" } } diff --git a/assets/openapi.json b/assets/openapi.json index 4c9006c2945bb1b09624b1b4a538719a14fc2e1a..7b2243e9cd815e798e1eab6fb6059be6ced1d651 100644 GIT binary patch delta 117 zcmccAtNg5AxuJz|3)50b=FGg%=>keDDwA{sr#B=ss!lIzVHBFY!JTCim&EoZl1#H0 zr)O?w7H7!?DRjKXXtTXYnyK#XBoXoH=YyCvCKsfuO+RppQGfabE*6eHDRbxVPI#G4(M{_q@hvvRzSzsqXD`0}e)^$rmDcCz%Sg Y8*?!OF$)m00x=s9vu`)%;@G?w0HzZb)&Kwi diff --git a/assets/schemas.json b/assets/schemas.json index 9a00e61326da82921a6b8ac93b51eeb018ceb3ce..0f2602f7161fbf7984b5e6565d580ff8c3689121 100644 GIT binary patch delta 8700 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 diff --git a/src/api/routes/users/@me/index.ts b/src/api/routes/users/@me/index.ts index fc44e17e9..f4578126c 100644 --- a/src/api/routes/users/@me/index.ts +++ b/src/api/routes/users/@me/index.ts @@ -70,6 +70,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 }, @@ -140,8 +142,52 @@ router.patch( newToken = (await generateToken(user.id)) as string; } - // TODO: uniqueUsernames: disallow if uniqueUsernames is enabled if (body.username) { + // 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({ @@ -152,7 +198,6 @@ router.patch( }); } - const { maxUsername } = Config.get().limits.user; if (check_username.length > maxUsername) { throw FieldErrors({ username: { @@ -163,8 +208,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: { @@ -176,7 +231,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/util/config/types/subconfigurations/limits/UserLimits.ts b/src/util/config/types/subconfigurations/limits/UserLimits.ts index 8f9b1a97f..77092344c 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; } diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts index acd2ea748..d1fbb5c2b 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts @@ -38,7 +38,6 @@ import { UserSettings } from "./UserSettings"; export enum PublicUserEnum { username, global_name, - legacy_username, discriminator, id, public_flags, @@ -241,7 +240,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) || @@ -334,9 +333,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}`; } @@ -387,7 +385,7 @@ export class User extends BaseClass { }); const user = User.create({ - username: username, + username: uniqueUsernames ? username.toLowerCase() : username, discriminator, id: id || Snowflake.generate(), email: email, @@ -397,7 +395,6 @@ export class User extends BaseClass { }, extended_settings: "{}", settings: settings, - premium_since: Config.get().defaults.user.premium ? new Date() : undefined, diff --git a/src/util/schemas/UserModifySchema.ts b/src/util/schemas/UserModifySchema.ts index e155b9af4..72d15dcb6 100644 --- a/src/util/schemas/UserModifySchema.ts +++ b/src/util/schemas/UserModifySchema.ts @@ -38,4 +38,5 @@ export interface UserModifySchema { * @maxLength 4 */ discriminator?: string; + global_name?: string; }