Implement Pomelo Registration

This commit is contained in:
Puyodead1
2023-09-08 21:33:04 -04:00
parent 07569328b1
commit 4d027a424e
11 changed files with 130 additions and 7 deletions

View File

@@ -15,7 +15,8 @@
"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",
"GUESTS_DISABLED": "Guest users are disabled",
"TOO_MANY_REGISTRATIONS": "Too many registrations, please try again later"
"TOO_MANY_REGISTRATIONS": "Too many registrations, please try again later",
"UNIQUE_USERNAMES_DISABLED": "The Unique Usernames feature is disabled on this instance"
},
"password_reset": {
"EMAIL_DOES_NOT_EXIST": "Email does not exist.",

Binary file not shown.

Binary file not shown.

View File

@@ -50,9 +50,18 @@ router.post(
}),
async (req: Request, res: Response) => {
const body = req.body as RegisterSchema;
const { register, security, limits } = Config.get();
const { register, security, limits, general } = Config.get();
const ip = getIpAdress(req);
if (!general.uniqueUsernames && body.unique_username_registration) {
throw FieldErrors({
unique_username_registration: {
code: "UNIQUE_USERNAMES_DISABLED",
message: req.t("auth:register.UNIQUE_USERNAMES_DISABLED"),
},
});
}
// Reg tokens
// They're a one time use token that bypasses registration limits ( rates, disabled reg, etc )
let regTokenUsed = false;

View File

@@ -0,0 +1,33 @@
import { route } from "@spacebar/api";
import { Config, User, UsernameAttemptUnauthedSchema } from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
const router = Router();
router.post(
"/",
route({
requestBody: "UsernameAttemptUnauthedSchema",
responses: {
200: { body: "UsernameAttemptResponse" },
400: { body: "APIErrorResponse" },
},
description: "Check if a username is available",
}),
async (req: Request, res: Response) => {
const body = req.body as UsernameAttemptUnauthedSchema;
const { uniqueUsernames } = Config.get().general;
if (!uniqueUsernames) {
throw new HTTPError(
"Unique Usernames feature is not enabled on this instance.",
400,
);
}
res.json({
taken: !User.isUsernameAvailable(body.username),
});
},
);
export default router;

View File

@@ -0,0 +1,37 @@
import { route } from "@spacebar/api";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { Config } from "../../../util";
const router = Router();
router.get(
"/",
route({
query: {
global_name: {
type: "string",
required: false,
},
},
responses: {
400: { body: "APIErrorResponse" },
},
}),
async (req: Request, res: Response) => {
const globalName = req.query.globalName as string | undefined;
const { uniqueUsernames } = Config.get().general;
if (!uniqueUsernames) {
throw new HTTPError(
"Unique Usernames feature is not enabled on this instance.",
400,
);
}
// return a random suggestion
if (!globalName) return res.json({ username: "" });
// return a suggestion based on the globalName
return res.json({ username: globalName });
},
);
export default router;

View File

@@ -172,10 +172,7 @@ router.patch(
}
// check if username is already taken (pomelo only)
const userCount = await User.count({
where: { username: body.username },
});
if (userCount > 0) {
if (!User.isUsernameAvailable(body.username))
throw FieldErrors({
username: {
code: "USERNAME_ALREADY_TAKEN",
@@ -184,7 +181,6 @@ router.patch(
),
},
});
}
}
// handle username changes (old username system)

View File

@@ -344,6 +344,7 @@ export class User extends BaseClass {
password,
id,
req,
global_name,
}: {
username: string;
password?: string;
@@ -351,8 +352,10 @@ export class User extends BaseClass {
date_of_birth?: Date; // "2000-04-03"
id?: string;
req?: Request;
global_name?: string;
}) {
const { uniqueUsernames } = Config.get().general;
const { minUsername, maxUsername } = Config.get().limits.user;
// trim special uf8 control characters -> Backspace, Newline, ...
username = trimSpecial(username);
@@ -374,6 +377,34 @@ export class User extends BaseClass {
}
}
if (uniqueUsernames) {
// check if there is already an account with this username
if (!User.isUsernameAvailable(username))
throw FieldErrors({
username: {
code: "USERNAME_ALREADY_TAKEN",
message:
req?.t("common:field.USERNAME_ALREADY_TAKEN") || "",
},
});
// validate username length
if (
username.length < minUsername ||
username.length > maxUsername
) {
throw FieldErrors({
username: {
code: "BASE_TYPE_BAD_LENGTH",
message:
req?.t("common:field.BASE_TYPE_BAD_LENGTH", {
length: `${minUsername} and ${maxUsername}`,
}) || "",
},
});
}
}
// TODO: save date_of_birth
// appearently discord doesn't save the date of birth and just calculate if nsfw is allowed
// if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false
@@ -386,6 +417,7 @@ export class User extends BaseClass {
const user = User.create({
username: uniqueUsernames ? username.toLowerCase() : username,
global_name: uniqueUsernames ? global_name : undefined,
discriminator,
id: id || Snowflake.generate(),
email: email,
@@ -429,6 +461,14 @@ export class User extends BaseClass {
return user;
}
static async isUsernameAvailable(username: string) {
const user = await User.findOne({
where: { username },
select: ["id"],
});
return !user;
}
}
export const CUSTOM_USER_FLAG_OFFSET = BigInt(1) << BigInt(32);

View File

@@ -0,0 +1,3 @@
export interface UsernameAttemptUnauthedSchema {
username: string;
}

View File

@@ -72,6 +72,7 @@ export * from "./UserModifySchema";
export * from "./UserNoteUpdateSchema";
export * from "./UserProfileModifySchema";
export * from "./UserSettingsSchema";
export * from "./UsernameAttemptUnauthedSchema";
export * from "./Validator";
export * from "./VanityUrlSchema";
export * from "./VoiceIdentifySchema";

View File

@@ -0,0 +1,3 @@
export interface UsernameAttemptResponse {
taken: boolean;
}