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 4c9006c29..7b2243e9c 100644 Binary files a/assets/openapi.json and b/assets/openapi.json differ diff --git a/assets/schemas.json b/assets/schemas.json index 9a00e6132..0f2602f71 100644 Binary files a/assets/schemas.json and b/assets/schemas.json differ 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; }