diff --git a/src/api/routes/auth/register.ts b/src/api/routes/auth/register.ts
index 79929f5e..1427b3c6 100644
--- a/src/api/routes/auth/register.ts
+++ b/src/api/routes/auth/register.ts
@@ -17,7 +17,7 @@
*/
import { route, verifyCaptcha } from "@spacebar/api";
-import { Config, FieldErrors, Invite, User, ValidRegistrationToken, generateToken, IpDataClient, AbuseIpDbClient, TimeSpan } from "@spacebar/util";
+import { Config, FieldErrors, Invite, User, ValidRegistrationToken, generateToken, IpDataClient, AbuseIpDbClient, TimeSpan, Stopwatch } from "@spacebar/util";
import bcrypt from "bcrypt";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
@@ -45,6 +45,13 @@ router.post(
},
}),
async (req: Request, res: Response) => {
+ const totalSw = Stopwatch.startNew();
+ const incSw = Stopwatch.startNew();
+ const logTrace = (...data: unknown[]) => {
+ if (process.env.LOG_VERBOSE_TRACES !== "true") return;
+ console.log("[Register]", ...data, `[${totalSw.elapsed().toString()} (+${incSw.getElapsedAndReset().totalMilliseconds}ms)]`);
+ };
+
const body = req.body as RegisterSchema;
const { register, security, limits } = Config.get();
const ip = req.ip!;
@@ -133,6 +140,8 @@ router.post(
}
}
+ logTrace("Basic checks");
+
//region IP checks
const cacheBlockedIp = (ip: string, reason: string) => {
recentlyBlockedIps[ip] = {
@@ -206,6 +215,7 @@ router.post(
}
}
//endregion
+ logTrace("IP checks");
// TODO: gift_code_sku_id?
// TODO: check password strength
@@ -242,6 +252,8 @@ router.post(
});
}
+ logTrace("Email checks");
+
if (register.dateOfBirth.required && !body.date_of_birth) {
throw FieldErrors({
date_of_birth: {
@@ -302,6 +314,7 @@ router.post(
},
});
}
+ logTrace("Password checks");
if (!regTokenUsed && !body.invite && (register.requireInvite || (register.guestsRequireInvite && !register.email))) {
// require invite to register -> e.g. for organizations to send invites to their employees
@@ -330,6 +343,7 @@ router.post(
},
});
}
+ logTrace("Absolute register rate checks");
const { maxUsername } = Config.get().limits.user;
if (body.username.length > maxUsername) {
@@ -342,13 +356,16 @@ router.post(
}
const user = await User.register({ ...body, req });
+ logTrace("Register user");
if (body.invite) {
// await to fail if the invite doesn't exist (necessary for requireInvite to work properly) (username only signups are possible)
await Invite.joinGuild(user.id, body.invite);
+ logTrace("Accept invite");
}
- return res.json({ token: await generateToken(user.id) });
+ res.json({ token: await generateToken(user.id) });
+ logTrace("Generate token");
},
);
diff --git a/src/util/entities/Member.ts b/src/util/entities/Member.ts
index 8a460afe..fba7c400 100644
--- a/src/util/entities/Member.ts
+++ b/src/util/entities/Member.ts
@@ -21,7 +21,7 @@ import { BeforeInsert, BeforeUpdate, Column, Entity, Index, JoinColumn, JoinTabl
import { Ban, Channel, PublicGuildRelations } from ".";
import { ReadyGuildDTO } from "../dtos";
import { GuildCreateEvent, GuildDeleteEvent, GuildMemberAddEvent, GuildMemberRemoveEvent, GuildMemberUpdateEvent, MessageCreateEvent } from "../interfaces";
-import { Config, emitEvent, DiscordApiErrors } from "../util";
+import { Config, emitEvent, DiscordApiErrors, Stopwatch } from "../util";
import { BaseClassWithoutId } from "./BaseClass";
import { Guild } from "./Guild";
import { Message } from "./Message";
@@ -305,16 +305,26 @@ export class Member extends BaseClassWithoutId {
}
static async addToGuild(user_id: string, guild_id: string) {
+ const totalSw = Stopwatch.startNew();
+ const incSw = Stopwatch.startNew();
+ const logTrace = (...data: unknown[]) => {
+ if (process.env.LOG_VERBOSE_TRACES !== "true") return;
+ console.log("[Member.addToGuild]", ...data, `[${totalSw.elapsed().toString()} (+${incSw.getElapsedAndReset().totalMilliseconds}ms)]`);
+ };
+
const user = await User.getPublicUser(user_id);
- const isBanned = await Ban.count({ where: { guild_id, user_id } });
- if (isBanned) {
- throw DiscordApiErrors.USER_BANNED;
- }
+ logTrace("Get user");
+
+ const isBanned = await Ban.findOne({ where: { guild_id, user_id }, select: { id: true } });
+ if (isBanned) throw DiscordApiErrors.USER_BANNED;
+ logTrace("Check ban");
+
const { maxGuilds } = Config.get().limits.user;
const guild_count = await Member.count({ where: { id: user_id } });
if (guild_count >= maxGuilds) {
throw new HTTPError(`You are at the ${maxGuilds} server limit.`, 403);
}
+ logTrace("Enforce max guilds");
const guild = await Guild.findOneOrFail({
where: {
@@ -323,12 +333,15 @@ export class Member extends BaseClassWithoutId {
relations: PublicGuildRelations,
relationLoadStrategy: "query",
});
+ logTrace("Find guild");
for await (const channel of guild.channels) {
channel.position = await Channel.calculatePosition(channel.id, guild_id);
}
+ logTrace("Reorder channels");
const memberCount = await Member.count({ where: { guild_id } });
+ logTrace("Get member count");
const memberPreview = (
await Member.find({
@@ -344,6 +357,7 @@ export class Member extends BaseClassWithoutId {
take: 10,
})
).map((member) => member.toPublicMember());
+ logTrace("Calculate member preview");
if (
await Member.count({
@@ -351,6 +365,7 @@ export class Member extends BaseClassWithoutId {
})
)
throw new HTTPError("You are already a member of this guild", 400);
+ logTrace("Check existing membership");
const member = {
id: user_id,
@@ -417,6 +432,7 @@ export class Member extends BaseClassWithoutId {
user_id,
} satisfies GuildCreateEvent),
]);
+ logTrace("Save member info");
if (guild.system_channel_id) {
const channel = await Channel.findOneOrFail({
@@ -452,6 +468,7 @@ export class Member extends BaseClassWithoutId {
} satisfies MessageCreateEvent),
channel.save(),
]);
+ logTrace("Send welcome message");
}
}
diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts
index 249d0952..a58b6579 100644
--- a/src/util/entities/User.ts
+++ b/src/util/entities/User.ts
@@ -18,7 +18,7 @@
import { Request } from "express";
import { Column, Entity, JoinColumn, OneToMany, OneToOne } from "typeorm";
-import { Channel, Config, Email, FieldErrors, Snowflake, trimSpecial } from "..";
+import { Channel, Config, Email, FieldErrors, Snowflake, Stopwatch, trimSpecial } from "..";
import { Random } from "../util";
import { BaseClass } from "./BaseClass";
import { ConnectedAccount } from "./ConnectedAccount";
@@ -296,6 +296,13 @@ export class User extends BaseClass {
req?: Request;
bot?: boolean;
}) {
+ const totalSw = Stopwatch.startNew();
+ const incSw = Stopwatch.startNew();
+ const logTrace = (...data: unknown[]) => {
+ if (process.env.LOG_VERBOSE_TRACES !== "true") return;
+ console.log("[User.register]", ...data, `[${totalSw.elapsed().toString()} (+${incSw.getElapsedAndReset().totalMilliseconds}ms)]`);
+ };
+
// trim special utf8 control characters -> Backspace, Newline, ...
username = trimSpecial(username);
@@ -309,6 +316,7 @@ export class User extends BaseClass {
},
});
}
+ logTrace("Generate discriminator");
// TODO: save date_of_birth
// apparently discord doesn't save the date of birth and just calculate if nsfw is allowed
@@ -340,20 +348,24 @@ export class User extends BaseClass {
});
user.validate();
+ logTrace("Generate/validate user");
+
await Promise.all([user.save(), settings.save()]);
+ logTrace("Save user");
// send verification email if users aren't verified by default and we have an email
if (!Config.get().defaults.user.verified && email) {
await Email.sendVerifyEmail(user, email).catch((e) => {
console.error(`Failed to send verification email to ${user.tag}: ${e}`);
});
+ logTrace("Send verify email");
}
- const { guild } = Config.get();
- if (guild.autoJoin.enabled && !(bot && !guild.autoJoin.bots))
- for (const guild of Config.get().guild.autoJoin.guilds || []) {
- await Member.addToGuild(user.id, guild).catch((e) => console.error("[Autojoin]", e));
- }
+ const { autoJoin } = Config.get().guild;
+ if (autoJoin.enabled && autoJoin.guilds.length > 0 && !(bot && !autoJoin.bots)) {
+ await Promise.all(autoJoin.guilds.map((guild) => Member.addToGuild(user.id, guild).catch((e) => console.error("[Autojoin]", e))));
+ logTrace("Autojoin", autoJoin.guilds.length, "guilds");
+ }
return user;
}
|