mirror of
https://github.com/spacebarchat/server.git
synced 2026-04-13 00:55:40 +00:00
Implement Pomelo Registration
This commit is contained in:
@@ -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.
@@ -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;
|
||||
|
||||
33
src/api/routes/unique-username/username-attempt-unauthed.ts
Normal file
33
src/api/routes/unique-username/username-attempt-unauthed.ts
Normal 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;
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
3
src/util/schemas/UsernameAttemptUnauthedSchema.ts
Normal file
3
src/util/schemas/UsernameAttemptUnauthedSchema.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface UsernameAttemptUnauthedSchema {
|
||||
username: string;
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
3
src/util/schemas/responses/UsernameAttemptResponse.ts
Normal file
3
src/util/schemas/responses/UsernameAttemptResponse.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface UsernameAttemptResponse {
|
||||
taken: boolean;
|
||||
}
|
||||
Reference in New Issue
Block a user