summary refs log tree commit diff
path: root/src/api/routes/users
diff options
context:
space:
mode:
Diffstat (limited to 'src/api/routes/users')
-rw-r--r--src/api/routes/users/#id/delete.ts13
-rw-r--r--src/api/routes/users/#id/index.ts22
-rw-r--r--src/api/routes/users/#id/profile.ts54
-rw-r--r--src/api/routes/users/#id/relationships.ts16
-rw-r--r--src/api/routes/users/@me/channels.ts51
-rw-r--r--src/api/routes/users/@me/connections/#connection_name/#connection_id/index.ts2
-rw-r--r--src/api/routes/users/@me/delete.ts69
-rw-r--r--src/api/routes/users/@me/disable.ts61
-rw-r--r--src/api/routes/users/@me/guilds.ts133
-rw-r--r--src/api/routes/users/@me/guilds/#guild_id/settings.ts40
-rw-r--r--src/api/routes/users/@me/index.ts59
-rw-r--r--src/api/routes/users/@me/mfa/codes-verification.ts21
-rw-r--r--src/api/routes/users/@me/mfa/codes.ts22
-rw-r--r--src/api/routes/users/@me/mfa/totp/disable.ts22
-rw-r--r--src/api/routes/users/@me/mfa/totp/enable.ts23
-rw-r--r--src/api/routes/users/@me/mfa/webauthn/credentials/#key_id/index.ts34
-rw-r--r--src/api/routes/users/@me/mfa/webauthn/credentials/index.ts12
-rw-r--r--src/api/routes/users/@me/notes.ts134
-rw-r--r--src/api/routes/users/@me/relationships.ts195
-rw-r--r--src/api/routes/users/@me/settings.ts46
20 files changed, 661 insertions, 368 deletions
diff --git a/src/api/routes/users/#id/delete.ts b/src/api/routes/users/#id/delete.ts
index e36a35e6..5b1a682c 100644
--- a/src/api/routes/users/#id/delete.ts
+++ b/src/api/routes/users/#id/delete.ts
@@ -30,7 +30,18 @@ const router = Router();
 
 router.post(
 	"/",
-	route({ right: "MANAGE_USERS" }),
+	route({
+		right: "MANAGE_USERS",
+		responses: {
+			204: {},
+			403: {
+				body: "APIErrorResponse",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		await User.findOneOrFail({
 			where: { id: req.params.id },
diff --git a/src/api/routes/users/#id/index.ts b/src/api/routes/users/#id/index.ts
index 0c7cfe37..1bd413d3 100644
--- a/src/api/routes/users/#id/index.ts
+++ b/src/api/routes/users/#id/index.ts
@@ -16,16 +16,26 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Router, Request, Response } from "express";
-import { User } from "@spacebar/util";
 import { route } from "@spacebar/api";
+import { User } from "@spacebar/util";
+import { Request, Response, Router } from "express";
 
 const router: Router = Router();
 
-router.get("/", route({}), async (req: Request, res: Response) => {
-	const { id } = req.params;
+router.get(
+	"/",
+	route({
+		responses: {
+			200: {
+				body: "APIPublicUser",
+			},
+		},
+	}),
+	async (req: Request, res: Response) => {
+		const { id } = req.params;
 
-	res.json(await User.getPublicUser(id));
-});
+		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
index 2836c563..eecec0f3 100644
--- a/src/api/routes/users/#id/profile.ts
+++ b/src/api/routes/users/#id/profile.ts
@@ -16,23 +16,23 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Router, Request, Response } from "express";
+import { route } from "@spacebar/api";
 import {
-	User,
 	Member,
-	UserProfileModifySchema,
-	handleFile,
 	PrivateUserProjection,
-	emitEvent,
+	User,
+	UserProfileModifySchema,
 	UserUpdateEvent,
+	emitEvent,
+	handleFile,
 } from "@spacebar/util";
-import { route } from "@spacebar/api";
+import { Request, Response, Router } from "express";
 
 const router: Router = Router();
 
 router.get(
 	"/",
-	route({ test: { response: { body: "UserProfileResponse" } } }),
+	route({ responses: { 200: { body: "UserProfileResponse" } } }),
 	async (req: Request, res: Response) => {
 		if (req.params.id === "@me") req.params.id = req.user_id;
 
@@ -84,18 +84,6 @@ router.get(
 
 		// 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,
@@ -104,28 +92,6 @@ router.get(
 			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,
@@ -139,11 +105,11 @@ router.get(
 			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,
+			user: user.toPublicUser(),
 			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: guild_member?.toPublicMember(),
 			guild_member_profile: guild_id && guildMemberProfile,
 		});
 	},
@@ -151,7 +117,7 @@ router.get(
 
 router.patch(
 	"/",
-	route({ body: "UserProfileModifySchema" }),
+	route({ requestBody: "UserProfileModifySchema" }),
 	async (req: Request, res: Response) => {
 		const body = req.body as UserProfileModifySchema;
 
diff --git a/src/api/routes/users/#id/relationships.ts b/src/api/routes/users/#id/relationships.ts
index dfe52a5e..3737ca00 100644
--- a/src/api/routes/users/#id/relationships.ts
+++ b/src/api/routes/users/#id/relationships.ts
@@ -16,17 +16,25 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Router, Request, Response } from "express";
-import { User } from "@spacebar/util";
 import { route } from "@spacebar/api";
+import { User, UserRelationsResponse } from "@spacebar/util";
+import { Request, Response, Router } from "express";
 
 const router: Router = Router();
 
 router.get(
 	"/",
-	route({ test: { response: { body: "UserRelationsResponse" } } }),
+	route({
+		responses: {
+			200: { body: "UserRelationsResponse" },
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
-		const mutual_relations: object[] = [];
+		const mutual_relations: UserRelationsResponse = [];
+
 		const requested_relations = await User.findOneOrFail({
 			where: { id: req.params.id },
 			relations: ["relationships"],
diff --git a/src/api/routes/users/@me/channels.ts b/src/api/routes/users/@me/channels.ts
index 04db4fe9..8a8fadd9 100644
--- a/src/api/routes/users/@me/channels.ts
+++ b/src/api/routes/users/@me/channels.ts
@@ -16,32 +16,51 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Request, Response, Router } from "express";
+import { route } from "@spacebar/api";
 import {
-	Recipient,
-	DmChannelDTO,
 	Channel,
 	DmChannelCreateSchema,
+	DmChannelDTO,
+	Recipient,
 } from "@spacebar/util";
-import { route } from "@spacebar/api";
+import { Request, Response, Router } from "express";
 
 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.get(
+	"/",
+	route({
+		responses: {
+			200: {
+				body: "APIDMChannelArray",
+			},
+		},
+	}),
+	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" }),
+	route({
+		requestBody: "DmChannelCreateSchema",
+		responses: {
+			200: {
+				body: "DmChannelDTO",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		const body = req.body as DmChannelCreateSchema;
 		res.json(
diff --git a/src/api/routes/users/@me/connections/#connection_name/#connection_id/index.ts b/src/api/routes/users/@me/connections/#connection_name/#connection_id/index.ts
index 3a4e5e0a..351ec99a 100644
--- a/src/api/routes/users/@me/connections/#connection_name/#connection_id/index.ts
+++ b/src/api/routes/users/@me/connections/#connection_name/#connection_id/index.ts
@@ -29,7 +29,7 @@ const router = Router();
 // TODO: connection update schema
 router.patch(
 	"/",
-	route({ body: "ConnectionUpdateSchema" }),
+	route({ requestBody: "ConnectionUpdateSchema" }),
 	async (req: Request, res: Response) => {
 		const { connection_name, connection_id } = req.params;
 		const body = req.body as ConnectionUpdateSchema;
diff --git a/src/api/routes/users/@me/delete.ts b/src/api/routes/users/@me/delete.ts
index dce737fc..e36a1e92 100644
--- a/src/api/routes/users/@me/delete.ts
+++ b/src/api/routes/users/@me/delete.ts
@@ -16,41 +16,58 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Router, Request, Response } from "express";
-import { Member, User } from "@spacebar/util";
 import { route } from "@spacebar/api";
+import { Member, User } from "@spacebar/util";
 import bcrypt from "bcrypt";
+import { Request, Response, Router } from "express";
 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"));
+router.post(
+	"/",
+	route({
+		responses: {
+			204: {},
+			401: {
+				body: "APIErrorResponse",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
+	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
+		// TODO: decrement guild member count
 
-	if (correctpass) {
-		await Promise.all([
-			User.delete({ id: req.user_id }),
-			Member.delete({ id: req.user_id }),
-		]);
+		if (correctpass) {
+			await Promise.all([
+				User.delete({ id: req.user_id }),
+				Member.delete({ id: req.user_id }),
+			]);
 
-		res.sendStatus(204);
-	} else {
-		res.sendStatus(401);
-	}
-});
+			res.sendStatus(204);
+		} else {
+			res.sendStatus(401);
+		}
+	},
+);
 
 export default router;
diff --git a/src/api/routes/users/@me/disable.ts b/src/api/routes/users/@me/disable.ts
index d123a6a1..b4d03e62 100644
--- a/src/api/routes/users/@me/disable.ts
+++ b/src/api/routes/users/@me/disable.ts
@@ -16,35 +16,52 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { User } from "@spacebar/util";
-import { Router, Response, Request } from "express";
 import { route } from "@spacebar/api";
+import { User } from "@spacebar/util";
 import bcrypt from "bcrypt";
+import { Request, Response, Router } from "express";
 
 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;
+router.post(
+	"/",
+	route({
+		responses: {
+			204: {},
+			400: {
+				body: "APIErrorResponse",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
+	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 (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 });
+		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,
-		});
-	}
-});
+			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/guilds.ts b/src/api/routes/users/@me/guilds.ts
index b16b909d..0bce432b 100644
--- a/src/api/routes/users/@me/guilds.ts
+++ b/src/api/routes/users/@me/guilds.ts
@@ -16,79 +16,106 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Router, Request, Response } from "express";
+import { route } from "@spacebar/api";
 import {
+	Config,
 	Guild,
-	Member,
-	User,
 	GuildDeleteEvent,
 	GuildMemberRemoveEvent,
+	Member,
+	User,
 	emitEvent,
-	Config,
 } from "@spacebar/util";
+import { Request, Response, Router } from "express";
 import { HTTPError } from "lambert-server";
-import { route } from "@spacebar/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 },
-	});
+router.get(
+	"/",
+	route({
+		responses: {
+			200: {
+				body: "APIGuildArray",
+			},
+		},
+	}),
+	async (req: Request, res: Response) => {
+		const members = await Member.find({
+			relations: ["guild"],
+			where: { id: req.user_id },
+		});
 
-	let guild = members.map((x) => x.guild);
+		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
-	}
+		if ("with_counts" in req.query && req.query.with_counts == "true") {
+			guild = []; // TODO: Load guilds with user role permissions number
+		}
 
-	res.json(guild);
-});
+		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"],
-	});
+router.delete(
+	"/:guild_id",
+	route({
+		responses: {
+			204: {},
+			400: {
+				body: "APIErrorResponse",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
+	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);
-	}
+		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),
-	]);
+		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);
+		const user = await User.getPublicUser(req.user_id);
 
-	await emitEvent({
-		event: "GUILD_MEMBER_REMOVE",
-		data: {
+		await emitEvent({
+			event: "GUILD_MEMBER_REMOVE",
+			data: {
+				guild_id: guild_id,
+				user: user,
+			},
 			guild_id: guild_id,
-			user: user,
-		},
-		guild_id: guild_id,
-	} as GuildMemberRemoveEvent);
+		} as GuildMemberRemoveEvent);
 
-	return res.sendStatus(204);
-});
+		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
index 7e9f2a08..ac6586ce 100644
--- a/src/api/routes/users/@me/guilds/#guild_id/settings.ts
+++ b/src/api/routes/users/@me/guilds/#guild_id/settings.ts
@@ -16,29 +16,49 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Router, Response, Request } from "express";
+import { route } from "@spacebar/api";
 import {
 	Channel,
 	Member,
 	OrmUtils,
 	UserGuildSettingsSchema,
 } from "@spacebar/util";
-import { route } from "@spacebar/api";
+import { Request, Response, Router } from "express";
 
 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.get(
+	"/",
+	route({
+		responses: {
+			200: {},
+			404: {},
+		},
+	}),
+	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" }),
+	route({
+		requestBody: "UserGuildSettingsSchema",
+		responses: {
+			200: {},
+			400: {
+				body: "APIErrorResponse",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		const body = req.body as UserGuildSettingsSchema;
 
diff --git a/src/api/routes/users/@me/index.ts b/src/api/routes/users/@me/index.ts
index b3eeb964..8fe86265 100644
--- a/src/api/routes/users/@me/index.ts
+++ b/src/api/routes/users/@me/index.ts
@@ -16,36 +16,59 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Router, Request, Response } from "express";
+import { route } from "@spacebar/api";
 import {
-	User,
-	PrivateUserProjection,
-	emitEvent,
-	UserUpdateEvent,
-	handleFile,
-	FieldErrors,
 	adjustEmail,
 	Config,
-	UserModifySchema,
+	emitEvent,
+	FieldErrors,
 	generateToken,
+	handleFile,
+	PrivateUserProjection,
+	User,
+	UserModifySchema,
+	UserUpdateEvent,
 } from "@spacebar/util";
-import { route } from "@spacebar/api";
 import bcrypt from "bcrypt";
+import { Request, Response, Router } from "express";
 
 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.get(
+	"/",
+	route({
+		responses: {
+			200: {
+				body: "APIPrivateUser",
+			},
+		},
+	}),
+	async (req: Request, res: Response) => {
+		res.json(
+			await User.findOne({
+				select: PrivateUserProjection,
+				where: { id: req.user_id },
+			}),
+		);
+	},
+);
 
 router.patch(
 	"/",
-	route({ body: "UserModifySchema" }),
+	route({
+		requestBody: "UserModifySchema",
+		responses: {
+			200: {
+				body: "UserUpdateResponse",
+			},
+			400: {
+				body: "APIErrorResponse",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		const body = req.body as UserModifySchema;
 
diff --git a/src/api/routes/users/@me/mfa/codes-verification.ts b/src/api/routes/users/@me/mfa/codes-verification.ts
index 69d45e91..f71704a9 100644
--- a/src/api/routes/users/@me/mfa/codes-verification.ts
+++ b/src/api/routes/users/@me/mfa/codes-verification.ts
@@ -16,21 +16,34 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Router, Request, Response } from "express";
 import { route } from "@spacebar/api";
 import {
 	BackupCode,
-	generateMfaBackupCodes,
-	User,
 	CodesVerificationSchema,
 	DiscordApiErrors,
+	User,
+	generateMfaBackupCodes,
 } from "@spacebar/util";
+import { Request, Response, Router } from "express";
 
 const router = Router();
 
 router.post(
 	"/",
-	route({ body: "CodesVerificationSchema" }),
+	route({
+		requestBody: "CodesVerificationSchema",
+		responses: {
+			200: {
+				body: "APIBackupCodeArray",
+			},
+			400: {
+				body: "APIErrorResponse",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		// const { key, nonce, regenerate } = req.body as CodesVerificationSchema;
 		const { regenerate } = req.body as CodesVerificationSchema;
diff --git a/src/api/routes/users/@me/mfa/codes.ts b/src/api/routes/users/@me/mfa/codes.ts
index 4ddbf78e..f9cfc4c4 100644
--- a/src/api/routes/users/@me/mfa/codes.ts
+++ b/src/api/routes/users/@me/mfa/codes.ts
@@ -16,16 +16,16 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Router, Request, Response } from "express";
 import { route } from "@spacebar/api";
 import {
 	BackupCode,
 	FieldErrors,
 	generateMfaBackupCodes,
-	User,
 	MfaCodesSchema,
+	User,
 } from "@spacebar/util";
 import bcrypt from "bcrypt";
+import { Request, Response, Router } from "express";
 
 const router = Router();
 
@@ -33,7 +33,23 @@ const router = Router();
 
 router.post(
 	"/",
-	route({ body: "MfaCodesSchema" }),
+	route({
+		requestBody: "MfaCodesSchema",
+		deprecated: true,
+		description:
+			"This route is replaced with users/@me/mfa/codes-verification in newer clients",
+		responses: {
+			200: {
+				body: "APIBackupCodeArray",
+			},
+			400: {
+				body: "APIErrorResponse",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		const { password, regenerate } = req.body as MfaCodesSchema;
 
diff --git a/src/api/routes/users/@me/mfa/totp/disable.ts b/src/api/routes/users/@me/mfa/totp/disable.ts
index 9f406423..362152d7 100644
--- a/src/api/routes/users/@me/mfa/totp/disable.ts
+++ b/src/api/routes/users/@me/mfa/totp/disable.ts
@@ -16,22 +16,32 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Router, Request, Response } from "express";
 import { route } from "@spacebar/api";
-import { verifyToken } from "node-2fa";
-import { HTTPError } from "lambert-server";
 import {
-	User,
-	generateToken,
 	BackupCode,
 	TotpDisableSchema,
+	User,
+	generateToken,
 } from "@spacebar/util";
+import { Request, Response, Router } from "express";
+import { HTTPError } from "lambert-server";
+import { verifyToken } from "node-2fa";
 
 const router = Router();
 
 router.post(
 	"/",
-	route({ body: "TotpDisableSchema" }),
+	route({
+		requestBody: "TotpDisableSchema",
+		responses: {
+			200: {
+				body: "TokenOnlyResponse",
+			},
+			400: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		const body = req.body as TotpDisableSchema;
 
diff --git a/src/api/routes/users/@me/mfa/totp/enable.ts b/src/api/routes/users/@me/mfa/totp/enable.ts
index 4d6b2763..19836e4d 100644
--- a/src/api/routes/users/@me/mfa/totp/enable.ts
+++ b/src/api/routes/users/@me/mfa/totp/enable.ts
@@ -16,15 +16,15 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Router, Request, Response } from "express";
+import { route } from "@spacebar/api";
 import {
+	TotpEnableSchema,
 	User,
-	generateToken,
 	generateMfaBackupCodes,
-	TotpEnableSchema,
+	generateToken,
 } from "@spacebar/util";
-import { route } from "@spacebar/api";
 import bcrypt from "bcrypt";
+import { Request, Response, Router } from "express";
 import { HTTPError } from "lambert-server";
 import { verifyToken } from "node-2fa";
 
@@ -32,7 +32,20 @@ const router = Router();
 
 router.post(
 	"/",
-	route({ body: "TotpEnableSchema" }),
+	route({
+		requestBody: "TotpEnableSchema",
+		responses: {
+			200: {
+				body: "TokenWithBackupCodesResponse",
+			},
+			400: {
+				body: "APIErrorResponse",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		const body = req.body as TotpEnableSchema;
 
diff --git a/src/api/routes/users/@me/mfa/webauthn/credentials/#key_id/index.ts b/src/api/routes/users/@me/mfa/webauthn/credentials/#key_id/index.ts
index 04aca7e4..9cf42def 100644
--- a/src/api/routes/users/@me/mfa/webauthn/credentials/#key_id/index.ts
+++ b/src/api/routes/users/@me/mfa/webauthn/credentials/#key_id/index.ts
@@ -21,21 +21,31 @@ import { SecurityKey, User } from "@spacebar/util";
 import { Request, Response, Router } from "express";
 const router = Router();
 
-router.delete("/", route({}), async (req: Request, res: Response) => {
-	const { key_id } = req.params;
+router.delete(
+	"/",
+	route({
+		responses: {
+			204: {},
+		},
+	}),
+	async (req: Request, res: Response) => {
+		const { key_id } = req.params;
 
-	await SecurityKey.delete({
-		id: key_id,
-		user_id: req.user_id,
-	});
+		await SecurityKey.delete({
+			id: key_id,
+			user_id: req.user_id,
+		});
 
-	const keys = await SecurityKey.count({ where: { user_id: req.user_id } });
+		const keys = await SecurityKey.count({
+			where: { user_id: req.user_id },
+		});
 
-	// disable webauthn if there are no keys left
-	if (keys === 0)
-		await User.update({ id: req.user_id }, { webauthn_enabled: false });
+		// disable webauthn if there are no keys left
+		if (keys === 0)
+			await User.update({ id: req.user_id }, { webauthn_enabled: false });
 
-	res.sendStatus(204);
-});
+		res.sendStatus(204);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/users/@me/mfa/webauthn/credentials/index.ts b/src/api/routes/users/@me/mfa/webauthn/credentials/index.ts
index 29dbb7cf..f383ffb7 100644
--- a/src/api/routes/users/@me/mfa/webauthn/credentials/index.ts
+++ b/src/api/routes/users/@me/mfa/webauthn/credentials/index.ts
@@ -73,7 +73,17 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 
 router.post(
 	"/",
-	route({ body: "WebAuthnPostSchema" }),
+	route({
+		requestBody: "WebAuthnPostSchema",
+		responses: {
+			200: {
+				body: "WebAuthnCreateResponse",
+			},
+			400: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		if (!WebAuthn.fido2) {
 			// TODO: I did this for typescript and I can't use !
diff --git a/src/api/routes/users/@me/notes.ts b/src/api/routes/users/@me/notes.ts
index d05c799c..248e61f9 100644
--- a/src/api/routes/users/@me/notes.ts
+++ b/src/api/routes/users/@me/notes.ts
@@ -16,71 +16,99 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Request, Response, Router } from "express";
 import { route } from "@spacebar/api";
-import { User, Note, emitEvent, Snowflake } from "@spacebar/util";
+import { Note, Snowflake, User, emitEvent } from "@spacebar/util";
+import { Request, Response, Router } from "express";
 
 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 },
+router.get(
+	"/:id",
+	route({
+		responses: {
+			200: {
+				body: "UserNoteResponse",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
 		},
-	});
+	}),
+	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,
-	});
-});
+		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;
+router.put(
+	"/:id",
+	route({
+		requestBody: "UserNoteUpdateSchema",
+		responses: {
+			204: {},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
+	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 },
-			);
+		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 {
-			Note.insert({
-				id: Snowflake.generate(),
-				owner,
-				target,
-				content: note,
+			await Note.delete({
+				owner: { id: owner.id },
+				target: { id: target.id },
 			});
 		}
-	} 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,
-	});
+		await emitEvent({
+			event: "USER_NOTE_UPDATE",
+			data: {
+				note: note,
+				id: target.id,
+			},
+			user_id: owner.id,
+		});
 
-	return res.status(204);
-});
+		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
index e9ea47e6..bce0a654 100644
--- a/src/api/routes/users/@me/relationships.ts
+++ b/src/api/routes/users/@me/relationships.ts
@@ -16,20 +16,20 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
+import { route } from "@spacebar/api";
 import {
-	RelationshipAddEvent,
-	User,
+	Config,
+	DiscordApiErrors,
 	PublicUserProjection,
-	RelationshipType,
+	Relationship,
+	RelationshipAddEvent,
 	RelationshipRemoveEvent,
+	RelationshipType,
+	User,
 	emitEvent,
-	Relationship,
-	Config,
 } from "@spacebar/util";
-import { Router, Response, Request } from "express";
+import { Request, Response, Router } from "express";
 import { HTTPError } from "lambert-server";
-import { DiscordApiErrors } from "@spacebar/util";
-import { route } from "@spacebar/api";
 
 const router = Router();
 
@@ -38,29 +38,53 @@ const userProjection: (keyof User)[] = [
 	...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"],
-	});
+router.get(
+	"/",
+	route({
+		responses: {
+			200: {
+				body: "UserRelationshipsResponse",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
+	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(),
-		};
-	});
+		//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);
-});
+		return res.json(related_users);
+	},
+);
 
 router.put(
 	"/:id",
-	route({ body: "RelationshipPutSchema" }),
+	route({
+		requestBody: "RelationshipPutSchema",
+		responses: {
+			204: {},
+			400: {
+				body: "APIErrorResponse",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		return await updateRelationship(
 			req,
@@ -77,7 +101,18 @@ router.put(
 
 router.post(
 	"/",
-	route({ body: "RelationshipPostSchema" }),
+	route({
+		requestBody: "RelationshipPostSchema",
+		responses: {
+			204: {},
+			400: {
+				body: "APIErrorResponse",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		return await updateRelationship(
 			req,
@@ -98,64 +133,78 @@ router.post(
 	},
 );
 
-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");
+router.delete(
+	"/:id",
+	route({
+		responses: {
+			204: {},
+			400: {
+				body: "APIErrorResponse",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
+	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 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,
-	);
+		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
+		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",
-				user_id: req.user_id,
 				data: relationship.toPublicRelationship(),
+				user_id: req.user_id,
 			} 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);
-});
+		return res.sendStatus(204);
+	},
+);
 
 export default router;
 
diff --git a/src/api/routes/users/@me/settings.ts b/src/api/routes/users/@me/settings.ts
index 62cfe904..d22d6de1 100644
--- a/src/api/routes/users/@me/settings.ts
+++ b/src/api/routes/users/@me/settings.ts
@@ -16,23 +16,49 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Router, Response, Request } from "express";
-import { User, UserSettingsSchema } from "@spacebar/util";
 import { route } from "@spacebar/api";
+import { User, UserSettingsSchema } from "@spacebar/util";
+import { Request, Response, Router } from "express";
 
 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.get(
+	"/",
+	route({
+		responses: {
+			200: {
+				body: "UserSettings",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
+	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" }),
+	route({
+		requestBody: "UserSettingsSchema",
+		responses: {
+			200: {
+				body: "UserSettings",
+			},
+			400: {
+				body: "APIErrorResponse",
+			},
+			404: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
 	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