mirror of
https://github.com/spacebarchat/server.git
synced 2026-08-21 16:29:55 +00:00
Trace registration
This commit is contained in:
@@ -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");
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user