From 66df10d6b02cb1bed437665bc293dbcd5b9c73ff Mon Sep 17 00:00:00 2001 From: TheArcaneBrony Date: Sat, 14 Jan 2023 13:08:48 +0100 Subject: Move endpoints to respective versions, split out non implemented routes Signed-off-by: TheArcaneBrony --- src/api/routes/users/#id/delete.ts | 39 ---- src/api/routes/users/#id/index.ts | 13 -- src/api/routes/users/#id/profile.ts | 182 --------------- src/api/routes/users/#id/relationships.ts | 54 ----- .../@me/activities/statistics/applications.ts | 11 - src/api/routes/users/@me/affinities/guilds.ts | 11 - src/api/routes/users/@me/affinities/users.ts | 11 - .../users/@me/applications/#app_id/entitlements.ts | 11 - src/api/routes/users/@me/billing/country-code.ts | 11 - .../routes/users/@me/billing/payment-sources.ts | 11 - src/api/routes/users/@me/billing/subscriptions.ts | 11 - src/api/routes/users/@me/channels.ts | 39 ---- src/api/routes/users/@me/connections.ts | 11 - src/api/routes/users/@me/delete.ts | 38 --- src/api/routes/users/@me/devices.ts | 11 - src/api/routes/users/@me/disable.ts | 32 --- src/api/routes/users/@me/email-settings.ts | 20 -- src/api/routes/users/@me/entitlements.ts | 11 - src/api/routes/users/@me/guilds.ts | 76 ------ .../routes/users/@me/guilds/#guild_id/settings.ts | 44 ---- .../users/@me/guilds/premium/subscription-slots.ts | 11 - src/api/routes/users/@me/index.ts | 156 ------------- src/api/routes/users/@me/library.ts | 11 - src/api/routes/users/@me/mfa/codes-verification.ts | 49 ---- src/api/routes/users/@me/mfa/codes.ts | 62 ----- src/api/routes/users/@me/mfa/totp/disable.ts | 56 ----- src/api/routes/users/@me/mfa/totp/enable.ts | 59 ----- src/api/routes/users/@me/notes.ts | 68 ------ src/api/routes/users/@me/relationships.ts | 259 --------------------- src/api/routes/users/@me/settings.ts | 35 --- 30 files changed, 1413 deletions(-) delete mode 100644 src/api/routes/users/#id/delete.ts delete mode 100644 src/api/routes/users/#id/index.ts delete mode 100644 src/api/routes/users/#id/profile.ts delete mode 100644 src/api/routes/users/#id/relationships.ts delete mode 100644 src/api/routes/users/@me/activities/statistics/applications.ts delete mode 100644 src/api/routes/users/@me/affinities/guilds.ts delete mode 100644 src/api/routes/users/@me/affinities/users.ts delete mode 100644 src/api/routes/users/@me/applications/#app_id/entitlements.ts delete mode 100644 src/api/routes/users/@me/billing/country-code.ts delete mode 100644 src/api/routes/users/@me/billing/payment-sources.ts delete mode 100644 src/api/routes/users/@me/billing/subscriptions.ts delete mode 100644 src/api/routes/users/@me/channels.ts delete mode 100644 src/api/routes/users/@me/connections.ts delete mode 100644 src/api/routes/users/@me/delete.ts delete mode 100644 src/api/routes/users/@me/devices.ts delete mode 100644 src/api/routes/users/@me/disable.ts delete mode 100644 src/api/routes/users/@me/email-settings.ts delete mode 100644 src/api/routes/users/@me/entitlements.ts delete mode 100644 src/api/routes/users/@me/guilds.ts delete mode 100644 src/api/routes/users/@me/guilds/#guild_id/settings.ts delete mode 100644 src/api/routes/users/@me/guilds/premium/subscription-slots.ts delete mode 100644 src/api/routes/users/@me/index.ts delete mode 100644 src/api/routes/users/@me/library.ts delete mode 100644 src/api/routes/users/@me/mfa/codes-verification.ts delete mode 100644 src/api/routes/users/@me/mfa/codes.ts delete mode 100644 src/api/routes/users/@me/mfa/totp/disable.ts delete mode 100644 src/api/routes/users/@me/mfa/totp/enable.ts delete mode 100644 src/api/routes/users/@me/notes.ts delete mode 100644 src/api/routes/users/@me/relationships.ts delete mode 100644 src/api/routes/users/@me/settings.ts (limited to 'src/api/routes/users') diff --git a/src/api/routes/users/#id/delete.ts b/src/api/routes/users/#id/delete.ts deleted file mode 100644 index 2c08635b..00000000 --- a/src/api/routes/users/#id/delete.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { route } from "@fosscord/api"; -import { - emitEvent, - Member, - PrivateUserProjection, - User, - UserDeleteEvent, - UserDeleteSchema, -} from "@fosscord/util"; -import { Request, Response, Router } from "express"; - -const router = Router(); - -router.post( - "/", - route({ right: "MANAGE_USERS" }), - async (req: Request, res: Response) => { - - let user = await User.findOneOrFail({ - where: { id: req.params.id }, - select: [...PrivateUserProjection, "data"], - }); - await Promise.all([ - Member.delete({ id: req.params.id }), - User.delete({ id: req.params.id }), - ]); - - // TODO: respect intents as USER_DELETE has potential to cause privacy issues - await emitEvent({ - event: "USER_DELETE", - user_id: req.user_id, - data: { user_id: req.params.id }, - } as UserDeleteEvent); - - res.sendStatus(204); - }, -); - -export default router; diff --git a/src/api/routes/users/#id/index.ts b/src/api/routes/users/#id/index.ts deleted file mode 100644 index bdb1060f..00000000 --- a/src/api/routes/users/#id/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Router, Request, Response } from "express"; -import { User } from "@fosscord/util"; -import { route } from "@fosscord/api"; - -const router: Router = Router(); - -router.get("/", route({}), async (req: Request, res: Response) => { - const { id } = req.params; - - res.json(await User.getPublicUser(id)); -}); - -export default router; diff --git a/src/api/routes/users/#id/profile.ts b/src/api/routes/users/#id/profile.ts deleted file mode 100644 index 5c649056..00000000 --- a/src/api/routes/users/#id/profile.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { Router, Request, Response } from "express"; -import { - PublicConnectedAccount, - PublicUser, - User, - UserPublic, - Member, - Guild, - UserProfileModifySchema, - handleFile, - PrivateUserProjection, - emitEvent, - UserUpdateEvent, -} from "@fosscord/util"; -import { route } from "@fosscord/api"; - -const router: Router = Router(); - -export interface UserProfileResponse { - user: UserPublic; - connected_accounts: PublicConnectedAccount; - premium_guild_since?: Date; - premium_since?: Date; -} - -router.get( - "/", - route({ test: { response: { body: "UserProfileResponse" } } }), - async (req: Request, res: Response) => { - if (req.params.id === "@me") req.params.id = req.user_id; - - const { guild_id, with_mutual_guilds } = req.query; - - const user = await User.getPublicUser(req.params.id, { - relations: ["connected_accounts"], - }); - - var mutual_guilds: object[] = []; - var premium_guild_since; - - if (with_mutual_guilds == "true") { - const requested_member = await Member.find({ - where: { id: req.params.id }, - }); - const self_member = await Member.find({ - where: { id: req.user_id }, - }); - - for (const rmem of requested_member) { - if (rmem.premium_since) { - if (premium_guild_since) { - if (premium_guild_since > rmem.premium_since) { - premium_guild_since = rmem.premium_since; - } - } else { - premium_guild_since = rmem.premium_since; - } - } - for (const smem of self_member) { - if (smem.guild_id === rmem.guild_id) { - mutual_guilds.push({ - id: rmem.guild_id, - nick: rmem.nick, - }); - } - } - } - } - - const guild_member = - guild_id && typeof guild_id == "string" - ? await Member.findOneOrFail({ - where: { id: req.params.id, guild_id: guild_id }, - relations: ["roles"], - }) - : undefined; - - // TODO: make proper DTO's in util? - - const userDto = { - username: user.username, - discriminator: user.discriminator, - id: user.id, - public_flags: user.public_flags, - avatar: user.avatar, - accent_color: user.accent_color, - banner: user.banner, - bio: req.user_bot ? null : user.bio, - bot: user.bot, - }; - - const userProfile = { - bio: req.user_bot ? null : user.bio, - accent_color: user.accent_color, - banner: user.banner, - pronouns: user.pronouns, - theme_colors: user.theme_colors, - }; - - const guildMemberDto = guild_member - ? { - avatar: guild_member.avatar, - banner: guild_member.banner, - bio: req.user_bot ? null : guild_member.bio, - communication_disabled_until: - guild_member.communication_disabled_until, - deaf: guild_member.deaf, - flags: user.flags, - is_pending: guild_member.pending, - pending: guild_member.pending, // why is this here twice, discord? - joined_at: guild_member.joined_at, - mute: guild_member.mute, - nick: guild_member.nick, - premium_since: guild_member.premium_since, - roles: guild_member.roles - .map((x) => x.id) - .filter((id) => id != guild_id), - user: userDto, - } - : undefined; - - const guildMemberProfile = { - accent_color: null, - banner: guild_member?.banner || null, - bio: guild_member?.bio || "", - guild_id, - }; - res.json({ - connected_accounts: user.connected_accounts, - premium_guild_since: premium_guild_since, // TODO - premium_since: user.premium_since, // TODO - mutual_guilds: mutual_guilds, // TODO {id: "", nick: null} when ?with_mutual_guilds=true - user: userDto, - premium_type: user.premium_type, - profile_themes_experiment_bucket: 4, // TODO: This doesn't make it available, for some reason? - user_profile: userProfile, - guild_member: guild_id && guildMemberDto, - guild_member_profile: guild_id && guildMemberProfile, - }); - }, -); - -router.patch( - "/", - route({ body: "UserProfileModifySchema" }), - async (req: Request, res: Response) => { - const body = req.body as UserProfileModifySchema; - - if (body.banner) - body.banner = await handleFile( - `/banners/${req.user_id}`, - body.banner as string, - ); - let user = await User.findOneOrFail({ - where: { id: req.user_id }, - select: [...PrivateUserProjection, "data"], - }); - - user.assign(body); - await user.save(); - - // @ts-ignore - delete user.data; - - // TODO: send update member list event in gateway - await emitEvent({ - event: "USER_UPDATE", - user_id: req.user_id, - data: user, - } as UserUpdateEvent); - - res.json({ - accent_color: user.accent_color, - bio: user.bio, - banner: user.banner, - theme_colors: user.theme_colors, - pronouns: user.pronouns, - }); - }, -); - -export default router; diff --git a/src/api/routes/users/#id/relationships.ts b/src/api/routes/users/#id/relationships.ts deleted file mode 100644 index c6480567..00000000 --- a/src/api/routes/users/#id/relationships.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Router, Request, Response } from "express"; -import { User } from "@fosscord/util"; -import { route } from "@fosscord/api"; - -const router: Router = Router(); - -export interface UserRelationsResponse { - object: { - id?: string; - username?: string; - avatar?: string; - discriminator?: string; - public_flags?: number; - }; -} - -router.get( - "/", - route({ test: { response: { body: "UserRelationsResponse" } } }), - async (req: Request, res: Response) => { - var mutual_relations: object[] = []; - const requested_relations = await User.findOneOrFail({ - where: { id: req.params.id }, - relations: ["relationships"], - }); - const self_relations = await User.findOneOrFail({ - where: { id: req.user_id }, - relations: ["relationships"], - }); - - for (const rmem of requested_relations.relationships) { - for (const smem of self_relations.relationships) - if ( - rmem.to_id === smem.to_id && - rmem.type === 1 && - rmem.to_id !== req.user_id - ) { - var relation_user = await User.getPublicUser(rmem.to_id); - - mutual_relations.push({ - id: relation_user.id, - username: relation_user.username, - avatar: relation_user.avatar, - discriminator: relation_user.discriminator, - public_flags: relation_user.public_flags, - }); - } - } - - res.json(mutual_relations); - }, -); - -export default router; diff --git a/src/api/routes/users/@me/activities/statistics/applications.ts b/src/api/routes/users/@me/activities/statistics/applications.ts deleted file mode 100644 index 014df8af..00000000 --- a/src/api/routes/users/@me/activities/statistics/applications.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Router, Response, Request } from "express"; -import { route } from "@fosscord/api"; - -const router = Router(); - -router.get("/", route({}), (req: Request, res: Response) => { - // TODO: - res.json([]).status(200); -}); - -export default router; diff --git a/src/api/routes/users/@me/affinities/guilds.ts b/src/api/routes/users/@me/affinities/guilds.ts deleted file mode 100644 index 8d744744..00000000 --- a/src/api/routes/users/@me/affinities/guilds.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Router, Response, Request } from "express"; -import { route } from "@fosscord/api"; - -const router = Router(); - -router.get("/", route({}), (req: Request, res: Response) => { - // TODO: - res.status(200).send({ guild_affinities: [] }); -}); - -export default router; diff --git a/src/api/routes/users/@me/affinities/users.ts b/src/api/routes/users/@me/affinities/users.ts deleted file mode 100644 index 6d4e4991..00000000 --- a/src/api/routes/users/@me/affinities/users.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Router, Response, Request } from "express"; -import { route } from "@fosscord/api"; - -const router = Router(); - -router.get("/", route({}), (req: Request, res: Response) => { - // TODO: - res.status(200).send({ user_affinities: [], inverse_user_affinities: [] }); -}); - -export default router; diff --git a/src/api/routes/users/@me/applications/#app_id/entitlements.ts b/src/api/routes/users/@me/applications/#app_id/entitlements.ts deleted file mode 100644 index 411e95bf..00000000 --- a/src/api/routes/users/@me/applications/#app_id/entitlements.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Request, Response, Router } from "express"; -import { route } from "@fosscord/api"; - -const router: Router = Router(); - -router.get("/", route({}), async (req: Request, res: Response) => { - //TODO - res.json([]).status(200); -}); - -export default router; diff --git a/src/api/routes/users/@me/billing/country-code.ts b/src/api/routes/users/@me/billing/country-code.ts deleted file mode 100644 index 33d40796..00000000 --- a/src/api/routes/users/@me/billing/country-code.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Request, Response, Router } from "express"; -import { route } from "@fosscord/api"; - -const router: Router = Router(); - -router.get("/", route({}), async (req: Request, res: Response) => { - //TODO - res.json({ country_code: "US" }).status(200); -}); - -export default router; diff --git a/src/api/routes/users/@me/billing/payment-sources.ts b/src/api/routes/users/@me/billing/payment-sources.ts deleted file mode 100644 index 014df8af..00000000 --- a/src/api/routes/users/@me/billing/payment-sources.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Router, Response, Request } from "express"; -import { route } from "@fosscord/api"; - -const router = Router(); - -router.get("/", route({}), (req: Request, res: Response) => { - // TODO: - res.json([]).status(200); -}); - -export default router; diff --git a/src/api/routes/users/@me/billing/subscriptions.ts b/src/api/routes/users/@me/billing/subscriptions.ts deleted file mode 100644 index 411e95bf..00000000 --- a/src/api/routes/users/@me/billing/subscriptions.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Request, Response, Router } from "express"; -import { route } from "@fosscord/api"; - -const router: Router = Router(); - -router.get("/", route({}), async (req: Request, res: Response) => { - //TODO - res.json([]).status(200); -}); - -export default router; diff --git a/src/api/routes/users/@me/channels.ts b/src/api/routes/users/@me/channels.ts deleted file mode 100644 index 237be102..00000000 --- a/src/api/routes/users/@me/channels.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Request, Response, Router } from "express"; -import { - Recipient, - DmChannelDTO, - Channel, - DmChannelCreateSchema, -} from "@fosscord/util"; -import { route } from "@fosscord/api"; - -const router: Router = Router(); - -router.get("/", route({}), async (req: Request, res: Response) => { - const recipients = await Recipient.find({ - where: { user_id: req.user_id, closed: false }, - relations: ["channel", "channel.recipients"], - }); - res.json( - await Promise.all( - recipients.map((r) => DmChannelDTO.from(r.channel, [req.user_id])), - ), - ); -}); - -router.post( - "/", - route({ body: "DmChannelCreateSchema" }), - async (req: Request, res: Response) => { - const body = req.body as DmChannelCreateSchema; - res.json( - await Channel.createDMChannel( - body.recipients, - req.user_id, - body.name, - ), - ); - }, -); - -export default router; diff --git a/src/api/routes/users/@me/connections.ts b/src/api/routes/users/@me/connections.ts deleted file mode 100644 index 411e95bf..00000000 --- a/src/api/routes/users/@me/connections.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Request, Response, Router } from "express"; -import { route } from "@fosscord/api"; - -const router: Router = Router(); - -router.get("/", route({}), async (req: Request, res: Response) => { - //TODO - res.json([]).status(200); -}); - -export default router; diff --git a/src/api/routes/users/@me/delete.ts b/src/api/routes/users/@me/delete.ts deleted file mode 100644 index a9f8167c..00000000 --- a/src/api/routes/users/@me/delete.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Router, Request, Response } from "express"; -import { Guild, Member, User } from "@fosscord/util"; -import { route } from "@fosscord/api"; -import bcrypt from "bcrypt"; -import { HTTPError } from "lambert-server"; - -const router = Router(); - -router.post("/", route({}), async (req: Request, res: Response) => { - const user = await User.findOneOrFail({ - where: { id: req.user_id }, - select: ["data"], - }); //User object - let correctpass = true; - - if (user.data.hash) { - // guest accounts can delete accounts without password - correctpass = await bcrypt.compare(req.body.password, user.data.hash); - if (!correctpass) { - throw new HTTPError(req.t("auth:login.INVALID_PASSWORD")); - } - } - - // TODO: decrement guild member count - - if (correctpass) { - await Promise.all([ - User.delete({ id: req.user_id }), - Member.delete({ id: req.user_id }), - ]); - - res.sendStatus(204); - } else { - res.sendStatus(401); - } -}); - -export default router; diff --git a/src/api/routes/users/@me/devices.ts b/src/api/routes/users/@me/devices.ts deleted file mode 100644 index 8556a3ad..00000000 --- a/src/api/routes/users/@me/devices.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Router, Response, Request } from "express"; -import { route } from "@fosscord/api"; - -const router = Router(); - -router.post("/", route({}), (req: Request, res: Response) => { - // TODO: - res.sendStatus(204); -}); - -export default router; diff --git a/src/api/routes/users/@me/disable.ts b/src/api/routes/users/@me/disable.ts deleted file mode 100644 index 313a888f..00000000 --- a/src/api/routes/users/@me/disable.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { User } from "@fosscord/util"; -import { Router, Response, Request } from "express"; -import { route } from "@fosscord/api"; -import bcrypt from "bcrypt"; - -const router = Router(); - -router.post("/", route({}), async (req: Request, res: Response) => { - const user = await User.findOneOrFail({ - where: { id: req.user_id }, - select: ["data"], - }); //User object - let correctpass = true; - - if (user.data.hash) { - // guest accounts can delete accounts without password - correctpass = await bcrypt.compare(req.body.password, user.data.hash); //Not sure if user typed right password :/ - } - - if (correctpass) { - await User.update({ id: req.user_id }, { disabled: true }); - - res.sendStatus(204); - } else { - res.status(400).json({ - message: "Password does not match", - code: 50018, - }); - } -}); - -export default router; diff --git a/src/api/routes/users/@me/email-settings.ts b/src/api/routes/users/@me/email-settings.ts deleted file mode 100644 index a2834b89..00000000 --- a/src/api/routes/users/@me/email-settings.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Router, Response, Request } from "express"; -import { route } from "@fosscord/api"; - -const router = Router(); - -router.get("/", route({}), (req: Request, res: Response) => { - // TODO: - res.json({ - categories: { - social: true, - communication: true, - tips: false, - updates_and_announcements: false, - recommendations_and_events: false, - }, - initialized: false, - }).status(200); -}); - -export default router; diff --git a/src/api/routes/users/@me/entitlements.ts b/src/api/routes/users/@me/entitlements.ts deleted file mode 100644 index 341e2b4c..00000000 --- a/src/api/routes/users/@me/entitlements.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Router, Response, Request } from "express"; -import { route } from "@fosscord/api"; - -const router = Router(); - -router.get("/gifts", route({}), (req: Request, res: Response) => { - // TODO: - res.json([]).status(200); -}); - -export default router; diff --git a/src/api/routes/users/@me/guilds.ts b/src/api/routes/users/@me/guilds.ts deleted file mode 100644 index e12bf258..00000000 --- a/src/api/routes/users/@me/guilds.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { Router, Request, Response } from "express"; -import { - Guild, - Member, - User, - GuildDeleteEvent, - GuildMemberRemoveEvent, - emitEvent, - Config, -} from "@fosscord/util"; -import { HTTPError } from "lambert-server"; -import { route } from "@fosscord/api"; - -const router: Router = Router(); - -router.get("/", route({}), async (req: Request, res: Response) => { - const members = await Member.find({ - relations: ["guild"], - where: { id: req.user_id }, - }); - - let guild = members.map((x) => x.guild); - - if ("with_counts" in req.query && req.query.with_counts == "true") { - guild = []; // TODO: Load guilds with user role permissions number - } - - res.json(guild); -}); - -// user send to leave a certain guild -router.delete("/:guild_id", route({}), async (req: Request, res: Response) => { - const { autoJoin } = Config.get().guild; - const { guild_id } = req.params; - const guild = await Guild.findOneOrFail({ - where: { id: guild_id }, - select: ["owner_id"], - }); - - if (!guild) throw new HTTPError("Guild doesn't exist", 404); - if (guild.owner_id === req.user_id) - throw new HTTPError("You can't leave your own guild", 400); - if ( - autoJoin.enabled && - autoJoin.guilds.includes(guild_id) && - !autoJoin.canLeave - ) { - throw new HTTPError("You can't leave instance auto join guilds", 400); - } - - await Promise.all([ - Member.delete({ id: req.user_id, guild_id: guild_id }), - emitEvent({ - event: "GUILD_DELETE", - data: { - id: guild_id, - }, - user_id: req.user_id, - } as GuildDeleteEvent), - ]); - - const user = await User.getPublicUser(req.user_id); - - await emitEvent({ - event: "GUILD_MEMBER_REMOVE", - data: { - guild_id: guild_id, - user: user, - }, - guild_id: guild_id, - } as GuildMemberRemoveEvent); - - return res.sendStatus(204); -}); - -export default router; diff --git a/src/api/routes/users/@me/guilds/#guild_id/settings.ts b/src/api/routes/users/@me/guilds/#guild_id/settings.ts deleted file mode 100644 index 436261d4..00000000 --- a/src/api/routes/users/@me/guilds/#guild_id/settings.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Router, Response, Request } from "express"; -import { - Channel, - Member, - OrmUtils, - UserGuildSettingsSchema, -} from "@fosscord/util"; -import { route } from "@fosscord/api"; - -const router = Router(); - -// GET doesn't exist on discord.com -router.get("/", route({}), async (req: Request, res: Response) => { - const user = await Member.findOneOrFail({ - where: { id: req.user_id, guild_id: req.params.guild_id }, - select: ["settings"], - }); - return res.json(user.settings); -}); - -router.patch( - "/", - route({ body: "UserGuildSettingsSchema" }), - async (req: Request, res: Response) => { - const body = req.body as UserGuildSettingsSchema; - - if (body.channel_overrides) { - for (var channel in body.channel_overrides) { - Channel.findOneOrFail({ where: { id: channel } }); - } - } - - const user = await Member.findOneOrFail({ - where: { id: req.user_id, guild_id: req.params.guild_id }, - select: ["settings"], - }); - OrmUtils.mergeDeep(user.settings || {}, body); - Member.update({ id: req.user_id, guild_id: req.params.guild_id }, user); - - res.json(user.settings); - }, -); - -export default router; diff --git a/src/api/routes/users/@me/guilds/premium/subscription-slots.ts b/src/api/routes/users/@me/guilds/premium/subscription-slots.ts deleted file mode 100644 index 014df8af..00000000 --- a/src/api/routes/users/@me/guilds/premium/subscription-slots.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Router, Response, Request } from "express"; -import { route } from "@fosscord/api"; - -const router = Router(); - -router.get("/", route({}), (req: Request, res: Response) => { - // TODO: - res.json([]).status(200); -}); - -export default router; diff --git a/src/api/routes/users/@me/index.ts b/src/api/routes/users/@me/index.ts deleted file mode 100644 index 37356d9d..00000000 --- a/src/api/routes/users/@me/index.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { Router, Request, Response } from "express"; -import { - User, - PrivateUserProjection, - emitEvent, - UserUpdateEvent, - handleFile, - FieldErrors, - adjustEmail, - Config, - UserModifySchema, - generateToken, -} from "@fosscord/util"; -import { route } from "@fosscord/api"; -import bcrypt from "bcrypt"; - -const router: Router = Router(); - -router.get("/", route({}), async (req: Request, res: Response) => { - res.json( - await User.findOne({ - select: PrivateUserProjection, - where: { id: req.user_id }, - }), - ); -}); - -router.patch( - "/", - route({ body: "UserModifySchema" }), - async (req: Request, res: Response) => { - const body = req.body as UserModifySchema; - - const user = await User.findOneOrFail({ - where: { id: req.user_id }, - select: [...PrivateUserProjection, "data"], - }); - - // Populated on password change - var newToken: string | undefined; - - if (body.avatar) - body.avatar = await handleFile( - `/avatars/${req.user_id}`, - body.avatar as string, - ); - if (body.banner) - body.banner = await handleFile( - `/banners/${req.user_id}`, - body.banner as string, - ); - - if (body.password) { - if (user.data?.hash) { - const same_password = await bcrypt.compare( - body.password, - user.data.hash || "", - ); - if (!same_password) { - throw FieldErrors({ - password: { - message: req.t("auth:login.INVALID_PASSWORD"), - code: "INVALID_PASSWORD", - }, - }); - } - } else { - user.data.hash = await bcrypt.hash(body.password, 12); - } - } - - if (body.email) { - body.email = adjustEmail(body.email); - if (!body.email && Config.get().register.email.required) - throw FieldErrors({ - email: { - message: req.t("auth:register.EMAIL_INVALID"), - code: "EMAIL_INVALID", - }, - }); - if (!body.password) - throw FieldErrors({ - password: { - message: req.t("auth:register.INVALID_PASSWORD"), - code: "INVALID_PASSWORD", - }, - }); - } - - if (body.new_password) { - if (!body.password && !user.email) { - throw FieldErrors({ - password: { - code: "BASE_TYPE_REQUIRED", - message: req.t("common:field.BASE_TYPE_REQUIRED"), - }, - }); - } - user.data.hash = await bcrypt.hash(body.new_password, 12); - user.data.valid_tokens_since = new Date(); - newToken = (await generateToken(user.id)) as string; - } - - if (body.username) { - var check_username = body?.username?.replace(/\s/g, ""); - if (!check_username) { - throw FieldErrors({ - username: { - code: "BASE_TYPE_REQUIRED", - message: req.t("common:field.BASE_TYPE_REQUIRED"), - }, - }); - } - } - - if (body.discriminator) { - if ( - await User.findOne({ - where: { - discriminator: body.discriminator, - username: body.username || user.username, - }, - }) - ) { - throw FieldErrors({ - discriminator: { - code: "INVALID_DISCRIMINATOR", - message: "This discriminator is already in use.", - }, - }); - } - } - - user.assign(body); - user.validate(); - await user.save(); - - // @ts-ignore - delete user.data; - - // TODO: send update member list event in gateway - await emitEvent({ - event: "USER_UPDATE", - user_id: req.user_id, - data: user, - } as UserUpdateEvent); - - res.json({ - ...user, - newToken, - }); - }, -); - -export default router; -// {"message": "Invalid two-factor code", "code": 60008} diff --git a/src/api/routes/users/@me/library.ts b/src/api/routes/users/@me/library.ts deleted file mode 100644 index 7ac13bae..00000000 --- a/src/api/routes/users/@me/library.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Router, Response, Request } from "express"; -import { route } from "@fosscord/api"; - -const router = Router(); - -router.get("/", route({}), (req: Request, res: Response) => { - // TODO: - res.status(200).send([]); -}); - -export default router; diff --git a/src/api/routes/users/@me/mfa/codes-verification.ts b/src/api/routes/users/@me/mfa/codes-verification.ts deleted file mode 100644 index 3411605b..00000000 --- a/src/api/routes/users/@me/mfa/codes-verification.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Router, Request, Response } from "express"; -import { route } from "@fosscord/api"; -import { - BackupCode, - generateMfaBackupCodes, - User, - CodesVerificationSchema, -} from "@fosscord/util"; - -const router = Router(); - -router.post( - "/", - route({ body: "CodesVerificationSchema" }), - async (req: Request, res: Response) => { - const { key, nonce, regenerate } = req.body as CodesVerificationSchema; - - // TODO: We don't have email/etc etc, so can't send a verification code. - // Once that's done, this route can verify `key` - - const user = await User.findOneOrFail({ where: { id: req.user_id } }); - - var codes: BackupCode[]; - if (regenerate) { - await BackupCode.update( - { user: { id: req.user_id } }, - { expired: true }, - ); - - codes = generateMfaBackupCodes(req.user_id); - await Promise.all(codes.map((x) => x.save())); - } else { - codes = await BackupCode.find({ - where: { - user: { - id: req.user_id, - }, - expired: false, - }, - }); - } - - return res.json({ - backup_codes: codes.map((x) => ({ ...x, expired: undefined })), - }); - }, -); - -export default router; diff --git a/src/api/routes/users/@me/mfa/codes.ts b/src/api/routes/users/@me/mfa/codes.ts deleted file mode 100644 index 33053028..00000000 --- a/src/api/routes/users/@me/mfa/codes.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Router, Request, Response } from "express"; -import { route } from "@fosscord/api"; -import { - BackupCode, - FieldErrors, - generateMfaBackupCodes, - User, - MfaCodesSchema, -} from "@fosscord/util"; -import bcrypt from "bcrypt"; - -const router = Router(); - -// TODO: This route is replaced with users/@me/mfa/codes-verification in newer clients - -router.post( - "/", - route({ body: "MfaCodesSchema" }), - async (req: Request, res: Response) => { - const { password, regenerate } = req.body as MfaCodesSchema; - - const user = await User.findOneOrFail({ - where: { id: req.user_id }, - select: ["data"], - }); - - if (!(await bcrypt.compare(password, user.data.hash || ""))) { - throw FieldErrors({ - password: { - message: req.t("auth:login.INVALID_PASSWORD"), - code: "INVALID_PASSWORD", - }, - }); - } - - var codes: BackupCode[]; - if (regenerate) { - await BackupCode.update( - { user: { id: req.user_id } }, - { expired: true }, - ); - - codes = generateMfaBackupCodes(req.user_id); - await Promise.all(codes.map((x) => x.save())); - } else { - codes = await BackupCode.find({ - where: { - user: { - id: req.user_id, - }, - expired: false, - }, - }); - } - - return res.json({ - backup_codes: codes.map((x) => ({ ...x, expired: undefined })), - }); - }, -); - -export default router; diff --git a/src/api/routes/users/@me/mfa/totp/disable.ts b/src/api/routes/users/@me/mfa/totp/disable.ts deleted file mode 100644 index 7916e598..00000000 --- a/src/api/routes/users/@me/mfa/totp/disable.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Router, Request, Response } from "express"; -import { route } from "@fosscord/api"; -import { verifyToken } from "node-2fa"; -import { HTTPError } from "lambert-server"; -import { - User, - generateToken, - BackupCode, - TotpDisableSchema, -} from "@fosscord/util"; - -const router = Router(); - -router.post( - "/", - route({ body: "TotpDisableSchema" }), - async (req: Request, res: Response) => { - const body = req.body as TotpDisableSchema; - - const user = await User.findOneOrFail({ - where: { id: req.user_id }, - select: ["totp_secret"], - }); - - const backup = await BackupCode.findOne({ where: { code: body.code } }); - if (!backup) { - const ret = verifyToken(user.totp_secret!, body.code); - if (!ret || ret.delta != 0) - throw new HTTPError( - req.t("auth:login.INVALID_TOTP_CODE"), - 60008, - ); - } - - await User.update( - { id: req.user_id }, - { - mfa_enabled: false, - totp_secret: "", - }, - ); - - await BackupCode.update( - { user: { id: req.user_id } }, - { - expired: true, - }, - ); - - return res.json({ - token: await generateToken(user.id), - }); - }, -); - -export default router; diff --git a/src/api/routes/users/@me/mfa/totp/enable.ts b/src/api/routes/users/@me/mfa/totp/enable.ts deleted file mode 100644 index 2c7044da..00000000 --- a/src/api/routes/users/@me/mfa/totp/enable.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Router, Request, Response } from "express"; -import { - User, - generateToken, - generateMfaBackupCodes, - TotpEnableSchema, -} from "@fosscord/util"; -import { route } from "@fosscord/api"; -import bcrypt from "bcrypt"; -import { HTTPError } from "lambert-server"; -import { verifyToken } from "node-2fa"; - -const router = Router(); - -router.post( - "/", - route({ body: "TotpEnableSchema" }), - async (req: Request, res: Response) => { - const body = req.body as TotpEnableSchema; - - const user = await User.findOneOrFail({ - where: { id: req.user_id }, - select: ["data", "email"], - }); - - // TODO: Are guests allowed to enable 2fa? - if (user.data.hash) { - if (!(await bcrypt.compare(body.password, user.data.hash))) { - throw new HTTPError(req.t("auth:login.INVALID_PASSWORD")); - } - } - - if (!body.secret) - throw new HTTPError(req.t("auth:login.INVALID_TOTP_SECRET"), 60005); - - if (!body.code) - throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); - - if (verifyToken(body.secret, body.code)?.delta != 0) - throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); - - let backup_codes = generateMfaBackupCodes(req.user_id); - await Promise.all(backup_codes.map((x) => x.save())); - await User.update( - { id: req.user_id }, - { mfa_enabled: true, totp_secret: body.secret }, - ); - - res.send({ - token: await generateToken(user.id), - backup_codes: backup_codes.map((x) => ({ - ...x, - expired: undefined, - })), - }); - }, -); - -export default router; diff --git a/src/api/routes/users/@me/notes.ts b/src/api/routes/users/@me/notes.ts deleted file mode 100644 index e54eb897..00000000 --- a/src/api/routes/users/@me/notes.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Request, Response, Router } from "express"; -import { route } from "@fosscord/api"; -import { User, Note, emitEvent, Snowflake } from "@fosscord/util"; - -const router: Router = Router(); - -router.get("/:id", route({}), async (req: Request, res: Response) => { - const { id } = req.params; - - const note = await Note.findOneOrFail({ - where: { - owner: { id: req.user_id }, - target: { id: id }, - }, - }); - - return res.json({ - note: note?.content, - note_user_id: id, - user_id: req.user_id, - }); -}); - -router.put("/:id", route({}), async (req: Request, res: Response) => { - const { id } = req.params; - const owner = await User.findOneOrFail({ where: { id: req.user_id } }); - const target = await User.findOneOrFail({ where: { id: id } }); //if noted user does not exist throw - const { note } = req.body; - - if (note && note.length) { - // upsert a note - if ( - await Note.findOne({ - where: { owner: { id: owner.id }, target: { id: target.id } }, - }) - ) { - Note.update( - { owner: { id: owner.id }, target: { id: target.id } }, - { owner, target, content: note }, - ); - } else { - Note.insert({ - id: Snowflake.generate(), - owner, - target, - content: note, - }); - } - } else { - await Note.delete({ - owner: { id: owner.id }, - target: { id: target.id }, - }); - } - - await emitEvent({ - event: "USER_NOTE_UPDATE", - data: { - note: note, - id: target.id, - }, - user_id: owner.id, - }); - - return res.status(204); -}); - -export default router; diff --git a/src/api/routes/users/@me/relationships.ts b/src/api/routes/users/@me/relationships.ts deleted file mode 100644 index 3eec704b..00000000 --- a/src/api/routes/users/@me/relationships.ts +++ /dev/null @@ -1,259 +0,0 @@ -import { - RelationshipAddEvent, - User, - PublicUserProjection, - RelationshipType, - RelationshipRemoveEvent, - emitEvent, - Relationship, - Config, -} from "@fosscord/util"; -import { Router, Response, Request } from "express"; -import { HTTPError } from "lambert-server"; -import { DiscordApiErrors } from "@fosscord/util"; -import { route } from "@fosscord/api"; - -const router = Router(); - -const userProjection: (keyof User)[] = [ - "relationships", - ...PublicUserProjection, -]; - -router.get("/", route({}), async (req: Request, res: Response) => { - const user = await User.findOneOrFail({ - where: { id: req.user_id }, - relations: ["relationships", "relationships.to"], - select: ["id", "relationships"], - }); - - //TODO DTO - const related_users = user.relationships.map((r) => { - return { - id: r.to.id, - type: r.type, - nickname: null, - user: r.to.toPublicUser(), - }; - }); - - return res.json(related_users); -}); - -router.put( - "/:id", - route({ body: "RelationshipPutSchema" }), - async (req: Request, res: Response) => { - return await updateRelationship( - req, - res, - await User.findOneOrFail({ - where: { id: req.params.id }, - relations: ["relationships", "relationships.to"], - select: userProjection, - }), - req.body.type ?? RelationshipType.friends, - ); - }, -); - -router.post( - "/", - route({ body: "RelationshipPostSchema" }), - async (req: Request, res: Response) => { - return await updateRelationship( - req, - res, - await User.findOneOrFail({ - relations: ["relationships", "relationships.to"], - select: userProjection, - where: { - discriminator: String(req.body.discriminator).padStart( - 4, - "0", - ), //Discord send the discriminator as integer, we need to add leading zeroes - username: req.body.username, - }, - }), - req.body.type, - ); - }, -); - -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"); - - 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"], - }); - - 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?.type === RelationshipType.blocked) { - // unblock user - - await Promise.all([ - Relationship.delete({ id: relationship.id }), - emitEvent({ - event: "RELATIONSHIP_REMOVE", - user_id: req.user_id, - data: relationship.toPublicRelationship(), - } as RelationshipRemoveEvent), - ]); - return res.sendStatus(204); - } - if (friendRequest && friendRequest.type !== RelationshipType.blocked) { - await Promise.all([ - Relationship.delete({ id: friendRequest.id }), - await emitEvent({ - event: "RELATIONSHIP_REMOVE", - data: friendRequest.toPublicRelationship(), - user_id: id, - } as RelationshipRemoveEvent), - ]); - } - - await Promise.all([ - Relationship.delete({ id: relationship.id }), - emitEvent({ - event: "RELATIONSHIP_REMOVE", - data: relationship.toPublicRelationship(), - user_id: req.user_id, - } as RelationshipRemoveEvent), - ]); - - return res.sendStatus(204); -}); - -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"); - - const user = await User.findOneOrFail({ - where: { id: req.user_id }, - relations: ["relationships", "relationships.to"], - select: userProjection, - }); - - var relationship = user.relationships.find((x) => x.to_id === id); - const friendRequest = friend.relationships.find( - (x) => x.to_id === req.user_id, - ); - - // 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"); - relationship.type = RelationshipType.blocked; - await relationship.save(); - } else { - relationship = await Relationship.create({ - to_id: id, - type: RelationshipType.blocked, - from_id: req.user_id, - }).save(); - } - - if (friendRequest && friendRequest.type !== RelationshipType.blocked) { - await Promise.all([ - Relationship.delete({ id: friendRequest.id }), - emitEvent({ - event: "RELATIONSHIP_REMOVE", - data: friendRequest.toPublicRelationship(), - user_id: id, - } as RelationshipRemoveEvent), - ]); - } - - await emitEvent({ - event: "RELATIONSHIP_ADD", - data: relationship.toPublicRelationship(), - user_id: req.user_id, - } as RelationshipAddEvent); - - return res.sendStatus(204); - } - - const { maxFriends } = Config.get().limits.user; - if (user.relationships.length >= maxFriends) - throw DiscordApiErrors.MAXIMUM_FRIENDS.withParams(maxFriends); - - var incoming_relationship = Relationship.create({ - nickname: undefined, - type: RelationshipType.incoming, - to: user, - from: friend, - }); - var outgoing_relationship = Relationship.create({ - nickname: undefined, - type: RelationshipType.outgoing, - to: friend, - from: user, - }); - - 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"); - // accept friend request - incoming_relationship = friendRequest; - 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"); - outgoing_relationship = relationship; - outgoing_relationship.type = RelationshipType.friends; - } - - await Promise.all([ - incoming_relationship.save(), - outgoing_relationship.save(), - emitEvent({ - event: "RELATIONSHIP_ADD", - data: outgoing_relationship.toPublicRelationship(), - user_id: req.user_id, - } as RelationshipAddEvent), - emitEvent({ - event: "RELATIONSHIP_ADD", - data: { - ...incoming_relationship.toPublicRelationship(), - should_notify: true, - }, - user_id: id, - } as RelationshipAddEvent), - ]); - - return res.sendStatus(204); -} diff --git a/src/api/routes/users/@me/settings.ts b/src/api/routes/users/@me/settings.ts deleted file mode 100644 index cce366ac..00000000 --- a/src/api/routes/users/@me/settings.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Router, Response, Request } from "express"; -import { OrmUtils, User, UserSettingsSchema } from "@fosscord/util"; -import { route } from "@fosscord/api"; - -const router = Router(); - -router.get("/", route({}), async (req: Request, res: Response) => { - const user = await User.findOneOrFail({ - where: { id: req.user_id }, - relations: ["settings"], - }); - return res.json(user.settings); -}); - -router.patch( - "/", - route({ body: "UserSettingsSchema" }), - async (req: Request, res: Response) => { - const body = req.body as UserSettingsSchema; - if (body.locale === "en") body.locale = "en-US"; // fix discord client crash on unkown locale - - const user = await User.findOneOrFail({ - where: { id: req.user_id, bot: false }, - relations: ["settings"], - }); - - user.settings.assign(body); - - user.settings.save(); - - res.json(user.settings); - }, -); - -export default router; -- cgit 1.5.1