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;
|