diff options
Diffstat (limited to 'src/api/routes')
-rw-r--r-- | src/api/routes/auth/generate-registration-tokens.ts | 27 | ||||
-rw-r--r-- | src/api/routes/auth/location-metadata.ts | 3 | ||||
-rw-r--r-- | src/api/routes/auth/login.ts | 4 | ||||
-rw-r--r-- | src/api/routes/auth/logout.ts | 16 | ||||
-rw-r--r-- | src/api/routes/auth/register.ts | 137 | ||||
-rw-r--r-- | src/api/routes/channels/#channel_id/invites.ts | 4 | ||||
-rw-r--r-- | src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts | 6 | ||||
-rw-r--r-- | src/api/routes/channels/#channel_id/messages/index.ts | 46 | ||||
-rw-r--r-- | src/api/routes/channels/#channel_id/permissions.ts | 8 | ||||
-rw-r--r-- | src/api/routes/channels/#channel_id/webhooks.ts | 2 | ||||
-rw-r--r-- | src/api/routes/guilds/#guild_id/bans.ts | 3 | ||||
-rw-r--r-- | src/api/routes/guilds/#guild_id/regions.ts | 4 | ||||
-rw-r--r-- | src/api/routes/guilds/#guild_id/stickers.ts | 2 | ||||
-rw-r--r-- | src/api/routes/guilds/#guild_id/templates.ts | 4 | ||||
-rw-r--r-- | src/api/routes/guilds/#guild_id/widget.json.ts | 4 | ||||
-rw-r--r-- | src/api/routes/users/@me/relationships.ts | 19 | ||||
-rw-r--r-- | src/api/routes/voice/regions.ts | 3 |
17 files changed, 215 insertions, 77 deletions
diff --git a/src/api/routes/auth/generate-registration-tokens.ts b/src/api/routes/auth/generate-registration-tokens.ts new file mode 100644 index 00000000..6f7f8630 --- /dev/null +++ b/src/api/routes/auth/generate-registration-tokens.ts @@ -0,0 +1,27 @@ +import { route } from "@fosscord/api"; +import { Config, random, ValidRegistrationToken } from "@fosscord/util"; +import { Request, Response, Router } from "express"; + +const router: Router = Router(); +export default router; + +router.get("/", route({ right: "OPERATOR" }), async (req: Request, res: Response) => { + let count = (req.query.count as unknown as number) ?? 1; + let tokens: string[] = []; + let dbtokens: ValidRegistrationToken[] = []; + for (let i = 0; i < count; i++) { + let token = random((req.query.length as unknown as number) ?? 255); + let vrt = new ValidRegistrationToken(); + vrt.token = token; + dbtokens.push(vrt); + if (req.query.include_url == "true") token = `${Config.get().general.publicUrl}/register?token=${token}`; + tokens.push(token); + } + await ValidRegistrationToken.save(dbtokens, { chunk: 1000, reload: false, transaction: false }); + + if (req.query.plain == "true") { + if (count == 1) res.send(tokens[0]); + else res.send(tokens.join("\n")); + } else if (count == 1) res.json({ token: tokens[0] }); + else res.json({ tokens }); +}); diff --git a/src/api/routes/auth/location-metadata.ts b/src/api/routes/auth/location-metadata.ts index b8caf579..5ccd7e85 100644 --- a/src/api/routes/auth/location-metadata.ts +++ b/src/api/routes/auth/location-metadata.ts @@ -1,4 +1,5 @@ -import { getIpAdress, IPAnalysis, route } from "@fosscord/api"; +import { route } from "@fosscord/api"; +import { getIpAdress, IPAnalysis } from "@fosscord/util"; import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/auth/login.ts b/src/api/routes/auth/login.ts index 045b86eb..4c882c14 100644 --- a/src/api/routes/auth/login.ts +++ b/src/api/routes/auth/login.ts @@ -1,5 +1,5 @@ -import { getIpAdress, route, verifyCaptcha } from "@fosscord/api"; -import { adjustEmail, Config, FieldErrors, generateToken, LoginSchema, User } from "@fosscord/util"; +import { route } from "@fosscord/api"; +import { adjustEmail, Config, FieldErrors, generateToken, getIpAdress, LoginSchema, User, verifyCaptcha } from "@fosscord/util"; import crypto from "crypto"; import { Request, Response, Router } from "express"; diff --git a/src/api/routes/auth/logout.ts b/src/api/routes/auth/logout.ts new file mode 100644 index 00000000..7e36ae9a --- /dev/null +++ b/src/api/routes/auth/logout.ts @@ -0,0 +1,16 @@ +import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; + +const router: Router = Router(); +export default router; + +router.post("/", route({}), async (req: Request, res: Response) => { + if (req.body.provider != null || req.body.voip_provider != null) { + console.log(`[LOGOUT]: provider or voip provider not null!`, req.body); + } else { + delete req.body.provider; + delete req.body.voip_provider; + if (Object.keys(req.body).length != 0) console.log(`[LOGOUT]: Extra fields sent in logout!`, req.body); + } + res.status(204).send(); +}); diff --git a/src/api/routes/auth/register.ts b/src/api/routes/auth/register.ts index 5cc28f7a..50f89522 100644 --- a/src/api/routes/auth/register.ts +++ b/src/api/routes/auth/register.ts @@ -1,6 +1,22 @@ -import { getIpAdress, IPAnalysis, isProxy, route, verifyCaptcha } from "@fosscord/api"; -import { adjustEmail, Config, FieldErrors, generateToken, HTTPError, Invite, RegisterSchema, User } from "@fosscord/util"; +import { route } from "@fosscord/api"; +import { + adjustEmail, + Config, + FieldErrors, + generateToken, + getIpAdress, + HTTPError, + Invite, + IPAnalysis, + isProxy, + RegisterSchema, + User, + ValidRegistrationToken, + verifyCaptcha +} from "@fosscord/util"; import { Request, Response, Router } from "express"; +import { red, yellow } from "picocolors"; +import { LessThan, MoreThan } from "typeorm"; let bcrypt: any; try { @@ -14,17 +30,28 @@ const router: Router = Router(); router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Response) => { const body = req.body as RegisterSchema; - const { register, security } = Config.get(); + const { register, security, limits } = Config.get(); const ip = getIpAdress(req); // email will be slightly modified version of the user supplied email -> e.g. protection against GMail Trick let email = adjustEmail(body.email); - // check if registration is allowed - if (!register.allowNewRegistration) { - throw FieldErrors({ - email: { code: "REGISTRATION_DISABLED", message: req.t("auth:register.REGISTRATION_DISABLED") } - }); + //check if referrer starts with any valid registration token + //!! bypasses captcha and registration disabling !!// + let validToken = false; + if (req.get("Referrer") && req.get("Referrer")?.includes("token=")) { + let token = req.get("Referrer")?.split("token=")[1].split("&")[0]; + if (token) { + await ValidRegistrationToken.delete({ expires_at: LessThan(new Date()) }); + let registrationToken = await ValidRegistrationToken.findOne({ where: { token: token, expires_at: MoreThan(new Date()) } }); + if (registrationToken) { + console.log(yellow(`[REGISTER] Registration token ${token} used for registration!`)); + await ValidRegistrationToken.delete(token); + validToken = true; + } else { + console.log(yellow(`[REGISTER] Invalid registration token ${token} used for registration by ${ip}!`)); + } + } } // check if the user agreed to the Terms of Service @@ -34,22 +61,7 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re }); } - if (register.disabled) { - throw FieldErrors({ - email: { - code: "DISABLED", - message: "registration is disabled on this instance" - } - }); - } - - if (!register.allowGuests) { - throw FieldErrors({ - email: { code: "GUESTS_DISABLED", message: req.t("auth:register.GUESTS_DISABLED") } - }); - } - - if (register.requireCaptcha && security.captcha.enabled) { + if (register.requireCaptcha && security.captcha.enabled && !validToken) { const { sitekey, service } = security.captcha; if (!body.captcha_key) { return res?.status(400).json({ @@ -69,24 +81,24 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re } } - if (!register.allowMultipleAccounts) { - // TODO: check if fingerprint was eligible generated - const exists = await User.findOne({ where: { fingerprints: body.fingerprint }, select: ["id"] }); - - if (exists) { - throw FieldErrors({ - email: { - code: "EMAIL_ALREADY_REGISTERED", - message: req.t("auth:register.EMAIL_ALREADY_REGISTERED") - } - }); - } + // check if registration is allowed + if (!register.allowNewRegistration && !validToken) { + throw FieldErrors({ + email: { code: "REGISTRATION_DISABLED", message: req.t("auth:register.REGISTRATION_DISABLED") } + }); } if (register.blockProxies) { - if (isProxy(await IPAnalysis(ip))) { - console.log(`proxy ${ip} blocked from registration`); - throw new HTTPError("Your IP is blocked from registration"); + let data; + try { + data = await IPAnalysis(ip); + } catch (e: any) { + console.warn(red(`[REGISTER]: Failed to analyze IP ${ip}: failed to contact api.ipdata.co!`), e.message); + } + + if (data && isProxy(data)) { + console.log(yellow(`[REGISTER] Proxy ${ip} blocked from registration!`)); + throw new HTTPError(req.t("auth:register.IP_BLOCKED")); } } @@ -94,15 +106,10 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re // TODO: check password strength if (email) { - // replace all dots and chars after +, if its a gmail.com email - if (!email) { - throw FieldErrors({ email: { code: "INVALID_EMAIL", message: req?.t("auth:register.INVALID_EMAIL") } }); - } - // check if there is already an account with this email const exists = await User.findOne({ where: { email: email } }); - if (exists) { + if (exists && !register.disabled) { throw FieldErrors({ email: { code: "EMAIL_ALREADY_REGISTERED", @@ -153,6 +160,46 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re }); } + if ( + !validToken && + limits.absoluteRate.register.enabled && + (await await User.count({ where: { created_at: MoreThan(new Date(Date.now() - limits.absoluteRate.register.window)) } })) >= + limits.absoluteRate.register.limit + ) { + console.log( + yellow( + `[REGISTER] Global register rate limit exceeded for ${getIpAdress(req)}: ${ + process.env.LOG_SENSITIVE ? req.body.email : "<email redacted>" + }, ${req.body.username}, ${req.body.invite ?? "No invite given"}` + ) + ); + let oldest = await User.findOne({ + where: { created_at: MoreThan(new Date(Date.now() - limits.absoluteRate.register.window)) }, + order: { created_at: "ASC" } + }); + if (!oldest) { + console.warn( + red( + `[REGISTER/WARN] Global rate limits exceeded, but no oldest user found. This should not happen. Did you misconfigure the limits?` + ) + ); + } else { + let retryAfterSec = Math.ceil( + (oldest!.created_at.getTime() - new Date(Date.now() - limits.absoluteRate.register.window).getTime()) / 1000 + ); + return res + .status(429) + .set("X-RateLimit-Limit", `${limits.absoluteRate.register.limit}`) + .set("X-RateLimit-Remaining", "0") + .set("X-RateLimit-Reset", `${(oldest!.created_at.getTime() + limits.absoluteRate.register.window) / 1000}`) + .set("X-RateLimit-Reset-After", `${retryAfterSec}`) + .set("X-RateLimit-Global", `true`) + .set("Retry-After", `${retryAfterSec}`) + .set("X-RateLimit-Bucket", `register`) + .send({ message: req.t("auth:register.TOO_MANY_REGISTRATIONS"), retry_after: retryAfterSec, global: true }); + } + } + const user = await User.register({ ...body, req }); if (body.invite) { diff --git a/src/api/routes/channels/#channel_id/invites.ts b/src/api/routes/channels/#channel_id/invites.ts index 3a1d2666..5fb8f5df 100644 --- a/src/api/routes/channels/#channel_id/invites.ts +++ b/src/api/routes/channels/#channel_id/invites.ts @@ -15,7 +15,7 @@ router.post( isTextChannel(channel.type); if (!channel.guild_id) { - throw new HTTPError("This channel doesn't exist", 404); + throw new HTTPError(req.t("common:notfound.CHANNEL"), 404); } const { guild_id } = channel; @@ -46,7 +46,7 @@ router.get("/", route({ permission: "MANAGE_CHANNELS" }), async (req: Request, r const channel = await Channel.findOneOrFail({ where: { id: channel_id } }); if (!channel.guild_id) { - throw new HTTPError("This channel doesn't exist", 404); + throw new HTTPError(req.t("common:notfound.CHANNEL"), 404); } const { guild_id } = channel; diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts b/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts index 44de5c45..f13f5f94 100644 --- a/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts +++ b/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts @@ -63,7 +63,7 @@ router.delete("/:emoji", route({ permission: "MANAGE_MESSAGES" }), async (req: R const message = await Message.findOneOrFail({ where: { id: message_id, channel_id } }); const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name); - if (!already_added) throw new HTTPError("Reaction not found", 404); + if (!already_added) throw new HTTPError(req.t("common:notfound.REACTION"), 404); message.reactions.remove(already_added); await Promise.all([ @@ -89,7 +89,7 @@ router.get("/:emoji", route({ permission: "VIEW_CHANNEL" }), async (req: Request const message = await Message.findOneOrFail({ where: { id: message_id, channel_id } }); const reaction = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name); - if (!reaction) throw new HTTPError("Reaction not found", 404); + if (!reaction) throw new HTTPError(req.t("common:notfound.REACTION"), 404); const users = await User.find({ where: { @@ -163,7 +163,7 @@ router.delete("/:emoji/:user_id", route({}), async (req: Request, res: Response) } const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name); - if (!already_added || !already_added.user_ids.includes(user_id)) throw new HTTPError("Reaction not found", 404); + if (!already_added || !already_added.user_ids.includes(user_id)) throw new HTTPError(req.t("common:notfound.REACTION"), 404); already_added.count--; diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts index 5fdcb6f9..87c689ec 100644 --- a/src/api/routes/channels/#channel_id/messages/index.ts +++ b/src/api/routes/channels/#channel_id/messages/index.ts @@ -6,17 +6,21 @@ import { Config, DmChannelDTO, emitEvent, + getIpAdress, getPermission, + getRights, HTTPError, Member, Message, MessageCreateEvent, MessageCreateSchema, + Rights, Snowflake, uploadFile } from "@fosscord/util"; import { Request, Response, Router } from "express"; import multer from "multer"; +import { red, yellow } from "picocolors"; import { FindManyOptions, LessThan, MoreThan } from "typeorm"; import { URL } from "url"; @@ -53,7 +57,7 @@ export function isTextChannel(type: ChannelType): boolean { router.get("/", async (req: Request, res: Response) => { const channel_id = req.params.channel_id; const channel = await Channel.findOneOrFail({ where: { id: channel_id } }); - if (!channel) throw new HTTPError("Channel not found", 404); + if (!channel) throw new HTTPError(req.t("common:notfound.CHANNEL"), 404); isTextChannel(channel.type); const around = req.query.around ? `${req.query.around}` : undefined; @@ -159,6 +163,46 @@ router.post( if (!channel.isWritable()) { throw new HTTPError(`Cannot send messages to channel of type ${channel.type}`, 400); } + var limits = Config.get().limits; + + if ( + !(await getRights(req.user_id)).has(Rights.FLAGS.BYPASS_RATE_LIMITS) && + limits.absoluteRate.sendMessage.enabled && + (await Message.count({ + where: { channel_id, timestamp: MoreThan(new Date(Date.now() - limits.absoluteRate.sendMessage.window)) } + })) >= limits.absoluteRate.sendMessage.limit + ) { + console.log( + yellow( + `[MESSAGE] Global register rate limit exceeded for ${getIpAdress(req)}: ${channel_id}, ${req.user_id}, ${body.content}` + ) + ); + let oldest = await Message.findOne({ + where: { channel_id, timestamp: MoreThan(new Date(Date.now() - limits.absoluteRate.sendMessage.window)) }, + order: { timestamp: "ASC" } + }); + if (!oldest) { + console.warn( + red( + `[MESSAGE/WARN] Global rate limits exceeded, but no oldest message found. This should not happen. Did you misconfigure the limits?` + ) + ); + } else { + let retryAfterSec = Math.ceil( + (oldest!.timestamp.getTime() - new Date(Date.now() - limits.absoluteRate.sendMessage.window).getTime()) / 1000 + ); + return res + .status(429) + .set("X-RateLimit-Limit", `${limits.absoluteRate.sendMessage.limit}`) + .set("X-RateLimit-Remaining", "0") + .set("X-RateLimit-Reset", `${(oldest!.timestamp.getTime() + limits.absoluteRate.sendMessage.window) / 1000}`) + .set("X-RateLimit-Reset-After", `${retryAfterSec}`) + .set("X-RateLimit-Global", `false`) + .set("Retry-After", `${retryAfterSec}`) + .set("X-RateLimit-Bucket", `chnl_${channel_id}`) + .send({ message: req.t("common:toomany.MESSAGE"), retry_after: retryAfterSec, global: false }); + } + } const files = (req.files as Express.Multer.File[]) ?? []; for (let currFile of files) { diff --git a/src/api/routes/channels/#channel_id/permissions.ts b/src/api/routes/channels/#channel_id/permissions.ts index bd462ea6..6b888c80 100644 --- a/src/api/routes/channels/#channel_id/permissions.ts +++ b/src/api/routes/channels/#channel_id/permissions.ts @@ -21,12 +21,12 @@ router.put( const body = req.body as ChannelPermissionOverwriteSchema; let channel = await Channel.findOneOrFail({ where: { id: channel_id } }); - if (!channel.guild_id) throw new HTTPError("Channel not found", 404); + if (!channel.guild_id) throw new HTTPError(req.t("common:notfound.CHANNEL"), 404); if (body.type === 0) { - if (!(await Role.count({ where: { id: overwrite_id } }))) throw new HTTPError("role not found", 404); + if (!(await Role.count({ where: { id: overwrite_id } }))) throw new HTTPError(req.t("common:notfound.ROLE"), 404); } else if (body.type === 1) { - if (!(await Member.count({ where: { id: overwrite_id } }))) throw new HTTPError("user not found", 404); + if (!(await Member.count({ where: { id: overwrite_id } }))) throw new HTTPError(req.t("common:notfound.USER"), 404); } else throw new HTTPError("type not supported", 501); // @ts-ignore @@ -60,7 +60,7 @@ router.delete("/:overwrite_id", route({ permission: "MANAGE_ROLES" }), async (re const { channel_id, overwrite_id } = req.params; const channel = await Channel.findOneOrFail({ where: { id: channel_id } }); - if (!channel.guild_id) throw new HTTPError("Channel not found", 404); + if (!channel.guild_id) throw new HTTPError(req.t("common:notfound.CHANNEL"), 404); channel.permission_overwrites = channel.permission_overwrites!.filter((x) => x.id === overwrite_id); diff --git a/src/api/routes/channels/#channel_id/webhooks.ts b/src/api/routes/channels/#channel_id/webhooks.ts index 38dcb869..14e14c8b 100644 --- a/src/api/routes/channels/#channel_id/webhooks.ts +++ b/src/api/routes/channels/#channel_id/webhooks.ts @@ -23,7 +23,7 @@ router.post("/", route({ body: "WebhookCreateSchema", permission: "MANAGE_WEBHOO let { avatar, name } = req.body as { name: string; avatar?: string }; name = trimSpecial(name); - if (name === "clyde") throw new HTTPError("Invalid name", 400); + if (name.toLowerCase() === "clyde") throw new HTTPError("Invalid name", 400); // TODO: save webhook in database and send response res.json(new Webhook()); diff --git a/src/api/routes/guilds/#guild_id/bans.ts b/src/api/routes/guilds/#guild_id/bans.ts index 4600b4cb..4963d36a 100644 --- a/src/api/routes/guilds/#guild_id/bans.ts +++ b/src/api/routes/guilds/#guild_id/bans.ts @@ -1,10 +1,11 @@ -import { getIpAdress, route } from "@fosscord/api"; +import { route } from "@fosscord/api"; import { Ban, BanModeratorSchema, BanRegistrySchema, DiscordApiErrors, emitEvent, + getIpAdress, GuildBanAddEvent, GuildBanRemoveEvent, HTTPError, diff --git a/src/api/routes/guilds/#guild_id/regions.ts b/src/api/routes/guilds/#guild_id/regions.ts index aa57ec65..4a5f5eca 100644 --- a/src/api/routes/guilds/#guild_id/regions.ts +++ b/src/api/routes/guilds/#guild_id/regions.ts @@ -1,5 +1,5 @@ -import { getIpAdress, getVoiceRegions, route } from "@fosscord/api"; -import { Guild } from "@fosscord/util"; +import { getVoiceRegions, route } from "@fosscord/api"; +import { getIpAdress, Guild } from "@fosscord/util"; import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/guilds/#guild_id/stickers.ts b/src/api/routes/guilds/#guild_id/stickers.ts index 15741780..06dade89 100644 --- a/src/api/routes/guilds/#guild_id/stickers.ts +++ b/src/api/routes/guilds/#guild_id/stickers.ts @@ -37,7 +37,7 @@ router.post( bodyParser, route({ permission: "MANAGE_EMOJIS_AND_STICKERS", body: "ModifyGuildStickerSchema" }), async (req: Request, res: Response) => { - if (!req.file) throw new HTTPError("missing file"); + if (!req.file) throw new HTTPError(req.t("common:body.MISSING_FILE")); const { guild_id } = req.params; const body = req.body as ModifyGuildStickerSchema; diff --git a/src/api/routes/guilds/#guild_id/templates.ts b/src/api/routes/guilds/#guild_id/templates.ts index 448ee033..af116760 100644 --- a/src/api/routes/guilds/#guild_id/templates.ts +++ b/src/api/routes/guilds/#guild_id/templates.ts @@ -1,5 +1,5 @@ -import { generateCode, route } from "@fosscord/api"; -import { Guild, HTTPError, OrmUtils, Template } from "@fosscord/util"; +import { route } from "@fosscord/api"; +import { generateCode, Guild, HTTPError, OrmUtils, Template } from "@fosscord/util"; import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/guilds/#guild_id/widget.json.ts b/src/api/routes/guilds/#guild_id/widget.json.ts index 368fe46e..66cc456f 100644 --- a/src/api/routes/guilds/#guild_id/widget.json.ts +++ b/src/api/routes/guilds/#guild_id/widget.json.ts @@ -1,5 +1,5 @@ -import { random, route } from "@fosscord/api"; -import { Channel, Guild, HTTPError, Invite, Member, OrmUtils, Permissions } from "@fosscord/util"; +import { route } from "@fosscord/api"; +import { Channel, Guild, HTTPError, Invite, Member, OrmUtils, Permissions, random } from "@fosscord/util"; import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/users/@me/relationships.ts b/src/api/routes/users/@me/relationships.ts index 8267c142..6383f3f3 100644 --- a/src/api/routes/users/@me/relationships.ts +++ b/src/api/routes/users/@me/relationships.ts @@ -69,7 +69,7 @@ router.post("/", route({ body: "RelationshipPostSchema" }), async (req: Request, router.delete("/:id", route({}), async (req: Request, res: Response) => { const { id } = req.params; - if (id === req.user_id) throw new HTTPError("You can't remove yourself as a friend"); + if (id === req.user_id) throw new HTTPError(req.t("common:relationship.REMOVE_SELF")); const user = await User.findOneOrFail({ where: { id: req.user_id }, select: userProjection, relations: ["relationships"] }); const friend = await User.findOneOrFail({ where: { id: id }, select: userProjection, relations: ["relationships"] }); @@ -77,7 +77,7 @@ router.delete("/:id", route({}), async (req: Request, res: Response) => { const relationship = user.relationships.find((x) => x.to_id === id); const friendRequest = friend.relationships.find((x) => x.to_id === req.user_id); - if (!relationship) throw new HTTPError("You are not friends with the user", 404); + if (!relationship) throw new HTTPError(req.t("common:relationship.NOT_FRIENDS")); if (relationship?.type === RelationshipType.blocked) { // unblock user @@ -118,7 +118,7 @@ export default router; async function updateRelationship(req: Request, res: Response, friend: User, type: RelationshipType) { const id = friend.id; - if (id === req.user_id) throw new HTTPError("You can't add yourself as a friend"); + if (id === req.user_id) throw new HTTPError(req.t("common:relationship.ADD_SELF")); const user = await User.findOneOrFail({ where: { id: req.user_id }, @@ -132,7 +132,7 @@ async function updateRelationship(req: Request, res: Response, friend: User, typ // TODO: you can add infinitely many blocked users (should this be prevented?) if (type === RelationshipType.blocked) { if (relationship) { - if (relationship.type === RelationshipType.blocked) throw new HTTPError("You already blocked the user"); + if (relationship.type === RelationshipType.blocked) throw new HTTPError(req.t("common:relationship.ALREADY_BLOCKED")); relationship.type = RelationshipType.blocked; await relationship.save(); } else { @@ -178,17 +178,18 @@ async function updateRelationship(req: Request, res: Response, friend: User, typ }); if (friendRequest) { - if (friendRequest.type === RelationshipType.blocked) throw new HTTPError("The user blocked you"); - if (friendRequest.type === RelationshipType.friends) throw new HTTPError("You are already friends with the user"); + //TODO: shouldn't this be failed silently? + if (friendRequest.type === RelationshipType.blocked) throw new HTTPError(req.t("common:relationship.BLOCKED")); + if (friendRequest.type === RelationshipType.friends) throw new HTTPError(req.t("common:relationship.ALREADY_FRIENDS")); // accept friend request incoming_relationship = friendRequest as any; //TODO: checkme, any cast incoming_relationship.type = RelationshipType.friends; } if (relationship) { - if (relationship.type === RelationshipType.outgoing) throw new HTTPError("You already sent a friend request"); - if (relationship.type === RelationshipType.blocked) throw new HTTPError("Unblock the user before sending a friend request"); - if (relationship.type === RelationshipType.friends) throw new HTTPError("You are already friends with the user"); + if (relationship.type === RelationshipType.outgoing) throw new HTTPError(req.t("common:relationship.ALREADY_SENT")); + if (relationship.type === RelationshipType.blocked) throw new HTTPError(req.t("common:relationship.UNBLOCK")); + if (relationship.type === RelationshipType.friends) throw new HTTPError(req.t("common:relationship.ALREADY_FRIENDS")); outgoing_relationship = relationship as any; //TODO: checkme, any cast outgoing_relationship.type = RelationshipType.friends; } diff --git a/src/api/routes/voice/regions.ts b/src/api/routes/voice/regions.ts index eacdcf11..1b5541e7 100644 --- a/src/api/routes/voice/regions.ts +++ b/src/api/routes/voice/regions.ts @@ -1,4 +1,5 @@ -import { getIpAdress, getVoiceRegions, route } from "@fosscord/api"; +import { getVoiceRegions, route } from "@fosscord/api"; +import { getIpAdress } from "@fosscord/util"; import { Request, Response, Router } from "express"; const router: Router = Router(); |