summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api/routes/auth/forgot.ts13
-rw-r--r--src/api/routes/auth/generate-registration-tokens.ts7
-rw-r--r--src/api/routes/auth/location-metadata.ts35
-rw-r--r--src/api/routes/auth/login.ts12
-rw-r--r--src/api/routes/auth/logout.ts33
-rw-r--r--src/api/routes/auth/mfa/totp.ts18
-rw-r--r--src/api/routes/auth/mfa/webauthn.ts8
-rw-r--r--src/api/routes/auth/register.ts8
-rw-r--r--src/api/routes/auth/reset.ts13
-rw-r--r--src/api/routes/auth/verify/index.ts13
-rw-r--r--src/api/routes/auth/verify/resend.ts9
-rw-r--r--src/api/routes/auth/verify/view-backup-codes-challenge.ts12
-rw-r--r--src/api/util/handlers/route.ts17
-rw-r--r--src/util/schemas/index.ts1
-rw-r--r--src/util/schemas/responses/APIErrorOrCaptchaResponse.ts6
-rw-r--r--src/util/schemas/responses/APIErrorResponse.ts12
-rw-r--r--src/util/schemas/responses/BackupCodesChallengeResponse.ts4
-rw-r--r--src/util/schemas/responses/CaptchaRequiredResponse.ts5
-rw-r--r--src/util/schemas/responses/GenerateRegistrationTokensResponse.ts3
-rw-r--r--src/util/schemas/responses/LocationMetadataResponse.ts5
-rw-r--r--src/util/schemas/responses/TokenResponse.ts6
-rw-r--r--src/util/schemas/responses/index.ts7
22 files changed, 201 insertions, 46 deletions
diff --git a/src/api/routes/auth/forgot.ts b/src/api/routes/auth/forgot.ts
index e240dff2..7e1ba65a 100644
--- a/src/api/routes/auth/forgot.ts
+++ b/src/api/routes/auth/forgot.ts
@@ -30,7 +30,18 @@ const router = Router();
 
 router.post(
 	"/",
-	route({ body: "ForgotPasswordSchema" }),
+	route({
+		body: "ForgotPasswordSchema",
+		responses: {
+			204: {},
+			400: {
+				body: "APIErrorResponse",
+			},
+			500: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		const { login, captcha_key } = req.body as ForgotPasswordSchema;
 
diff --git a/src/api/routes/auth/generate-registration-tokens.ts b/src/api/routes/auth/generate-registration-tokens.ts
index 723875f8..48fe6421 100644
--- a/src/api/routes/auth/generate-registration-tokens.ts
+++ b/src/api/routes/auth/generate-registration-tokens.ts
@@ -16,7 +16,7 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { route, random } from "@spacebar/api";
+import { random, route } from "@spacebar/api";
 import { Config, ValidRegistrationToken } from "@spacebar/util";
 import { Request, Response, Router } from "express";
 
@@ -25,7 +25,10 @@ export default router;
 
 router.get(
 	"/",
-	route({ right: "OPERATOR" }),
+	route({
+		right: "OPERATOR",
+		responses: { 200: { body: "GenerateRegistrationTokensResponse" } },
+	}),
 	async (req: Request, res: Response) => {
 		const count = req.query.count ? parseInt(req.query.count as string) : 1;
 		const length = req.query.length
diff --git a/src/api/routes/auth/location-metadata.ts b/src/api/routes/auth/location-metadata.ts
index 52a45c67..28293e59 100644
--- a/src/api/routes/auth/location-metadata.ts
+++ b/src/api/routes/auth/location-metadata.ts
@@ -16,20 +16,29 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Router, Request, Response } from "express";
-import { route } from "@spacebar/api";
-import { getIpAdress, IPAnalysis } from "@spacebar/api";
+import { IPAnalysis, getIpAdress, route } from "@spacebar/api";
+import { Request, Response, Router } from "express";
 const router = Router();
 
-router.get("/", route({}), async (req: Request, res: Response) => {
-	//TODO
-	//Note: It's most likely related to legal. At the moment Discord hasn't finished this too
-	const country_code = (await IPAnalysis(getIpAdress(req))).country_code;
-	res.json({
-		consent_required: false,
-		country_code: country_code,
-		promotional_email_opt_in: { required: true, pre_checked: false },
-	});
-});
+router.get(
+	"/",
+	route({
+		responses: {
+			200: {
+				body: "LocationMetadataResponse",
+			},
+		},
+	}),
+	async (req: Request, res: Response) => {
+		//TODO
+		//Note: It's most likely related to legal. At the moment Discord hasn't finished this too
+		const country_code = (await IPAnalysis(getIpAdress(req))).country_code;
+		res.json({
+			consent_required: false,
+			country_code: country_code,
+			promotional_email_opt_in: { required: true, pre_checked: false },
+		});
+	},
+);
 
 export default router;
diff --git a/src/api/routes/auth/login.ts b/src/api/routes/auth/login.ts
index fe0b4f99..729bd1cb 100644
--- a/src/api/routes/auth/login.ts
+++ b/src/api/routes/auth/login.ts
@@ -36,7 +36,17 @@ export default router;
 
 router.post(
 	"/",
-	route({ body: "LoginSchema" }),
+	route({
+		body: "LoginSchema",
+		responses: {
+			200: {
+				body: "TokenResponse",
+			},
+			400: {
+				body: "APIErrorOrCaptchaResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		const { login, password, captcha_key, undelete } =
 			req.body as LoginSchema;
diff --git a/src/api/routes/auth/logout.ts b/src/api/routes/auth/logout.ts
index 51909afa..94a3e474 100644
--- a/src/api/routes/auth/logout.ts
+++ b/src/api/routes/auth/logout.ts
@@ -22,14 +22,25 @@ import { Request, Response, Router } from "express";
 const router: Router = Router();
 export default router;
 
-router.post("/", route({}), async (req: Request, res: Response) => {
-	if (req.body.provider != null || req.body.voip_provider != null) {
-		console.log(`[LOGOUT]: provider or voip provider not null!`, req.body);
-	} else {
-		delete req.body.provider;
-		delete req.body.voip_provider;
-		if (Object.keys(req.body).length != 0)
-			console.log(`[LOGOUT]: Extra fields sent in logout!`, req.body);
-	}
-	res.status(204).send();
-});
+router.post(
+	"/",
+	route({
+		responses: {
+			204: {},
+		},
+	}),
+	async (req: Request, res: Response) => {
+		if (req.body.provider != null || req.body.voip_provider != null) {
+			console.log(
+				`[LOGOUT]: provider or voip provider not null!`,
+				req.body,
+			);
+		} else {
+			delete req.body.provider;
+			delete req.body.voip_provider;
+			if (Object.keys(req.body).length != 0)
+				console.log(`[LOGOUT]: Extra fields sent in logout!`, req.body);
+		}
+		res.status(204).send();
+	},
+);
diff --git a/src/api/routes/auth/mfa/totp.ts b/src/api/routes/auth/mfa/totp.ts
index 2396443d..0bfc2c52 100644
--- a/src/api/routes/auth/mfa/totp.ts
+++ b/src/api/routes/auth/mfa/totp.ts
@@ -16,16 +16,26 @@
 	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, generateToken, User, TotpSchema } from "@spacebar/util";
-import { verifyToken } from "node-2fa";
+import { BackupCode, TotpSchema, 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: "TotpSchema" }),
+	route({
+		body: "TotpSchema",
+		responses: {
+			200: {
+				body: "TokenResponse",
+			},
+			400: {
+				body: "APIErrorResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		// const { code, ticket, gift_code_sku_id, login_source } =
 		const { code, ticket } = req.body as TotpSchema;
diff --git a/src/api/routes/auth/mfa/webauthn.ts b/src/api/routes/auth/mfa/webauthn.ts
index 1b387411..e7278047 100644
--- a/src/api/routes/auth/mfa/webauthn.ts
+++ b/src/api/routes/auth/mfa/webauthn.ts
@@ -41,7 +41,13 @@ function toArrayBuffer(buf: Buffer) {
 
 router.post(
 	"/",
-	route({ body: "WebAuthnTotpSchema" }),
+	route({
+		body: "WebAuthnTotpSchema",
+		responses: {
+			200: { body: "TokenResponse" },
+			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/auth/register.ts b/src/api/routes/auth/register.ts
index 430c9532..70acedcd 100644
--- a/src/api/routes/auth/register.ts
+++ b/src/api/routes/auth/register.ts
@@ -42,7 +42,13 @@ const router: Router = Router();
 
 router.post(
 	"/",
-	route({ body: "RegisterSchema" }),
+	route({
+		body: "RegisterSchema",
+		responses: {
+			200: { body: "TokenResponse" },
+			400: { body: "APIErrorOrCaptchaResponse" },
+		},
+	}),
 	async (req: Request, res: Response) => {
 		const body = req.body as RegisterSchema;
 		const { register, security, limits } = Config.get();
diff --git a/src/api/routes/auth/reset.ts b/src/api/routes/auth/reset.ts
index 852a43c7..9c17d2b0 100644
--- a/src/api/routes/auth/reset.ts
+++ b/src/api/routes/auth/reset.ts
@@ -31,9 +31,20 @@ import { Request, Response, Router } from "express";
 
 const router = Router();
 
+// TODO: the response interface also returns settings, but this route doesn't actually return that.
 router.post(
 	"/",
-	route({ body: "PasswordResetSchema" }),
+	route({
+		body: "PasswordResetSchema",
+		responses: {
+			200: {
+				body: "TokenResponse",
+			},
+			400: {
+				body: "APIErrorOrCaptchaResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		const { password, token } = req.body as PasswordResetSchema;
 
diff --git a/src/api/routes/auth/verify/index.ts b/src/api/routes/auth/verify/index.ts
index c1afcde9..da92f256 100644
--- a/src/api/routes/auth/verify/index.ts
+++ b/src/api/routes/auth/verify/index.ts
@@ -37,9 +37,20 @@ async function getToken(user: User) {
 	return { token };
 }
 
+// TODO: the response interface also returns settings, but this route doesn't actually return that.
 router.post(
 	"/",
-	route({ body: "VerifyEmailSchema" }),
+	route({
+		body: "VerifyEmailSchema",
+		responses: {
+			200: {
+				body: "TokenResponse",
+			},
+			400: {
+				body: "APIErrorOrCaptchaResponse",
+			},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		const { captcha_key, token } = req.body;
 
diff --git a/src/api/routes/auth/verify/resend.ts b/src/api/routes/auth/verify/resend.ts
index f2727abd..215308ec 100644
--- a/src/api/routes/auth/verify/resend.ts
+++ b/src/api/routes/auth/verify/resend.ts
@@ -24,7 +24,14 @@ const router = Router();
 
 router.post(
 	"/",
-	route({ right: "RESEND_VERIFICATION_EMAIL" }),
+	route({
+		right: "RESEND_VERIFICATION_EMAIL",
+		responses: {
+			204: {},
+			400: {},
+			500: {},
+		},
+	}),
 	async (req: Request, res: Response) => {
 		const user = await User.findOneOrFail({
 			where: { id: req.user_id },
diff --git a/src/api/routes/auth/verify/view-backup-codes-challenge.ts b/src/api/routes/auth/verify/view-backup-codes-challenge.ts
index b12719ff..63158d9d 100644
--- a/src/api/routes/auth/verify/view-backup-codes-challenge.ts
+++ b/src/api/routes/auth/verify/view-backup-codes-challenge.ts
@@ -16,15 +16,21 @@
 	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-import { Router, Request, Response } from "express";
 import { route } from "@spacebar/api";
-import { FieldErrors, User, BackupCodesChallengeSchema } from "@spacebar/util";
+import { BackupCodesChallengeSchema, FieldErrors, User } from "@spacebar/util";
 import bcrypt from "bcrypt";
+import { Request, Response, Router } from "express";
 const router = Router();
 
 router.post(
 	"/",
-	route({ body: "BackupCodesChallengeSchema" }),
+	route({
+		body: "BackupCodesChallengeSchema",
+		responses: {
+			200: { body: "BackupCodesChallengeResponse" },
+			400: { body: "APIErrorResponse" },
+		},
+	}),
 	async (req: Request, res: Response) => {
 		const { password } = req.body as BackupCodesChallengeSchema;
 
diff --git a/src/api/util/handlers/route.ts b/src/api/util/handlers/route.ts
index 604df4e9..66bd2890 100644
--- a/src/api/util/handlers/route.ts
+++ b/src/api/util/handlers/route.ts
@@ -17,21 +17,21 @@
 */
 
 import {
-	ajv,
 	DiscordApiErrors,
 	EVENT,
 	FieldErrors,
-	SpacebarApiErrors,
-	getPermission,
-	getRights,
-	normalizeBody,
 	PermissionResolvable,
 	Permissions,
 	RightResolvable,
 	Rights,
+	SpacebarApiErrors,
+	ajv,
+	getPermission,
+	getRights,
+	normalizeBody,
 } from "@spacebar/util";
-import { NextFunction, Request, Response } from "express";
 import { AnyValidateFunction } from "ajv/dist/core";
+import { NextFunction, Request, Response } from "express";
 
 declare global {
 	// TODO: fix this
@@ -53,6 +53,11 @@ export interface RouteOptions {
 	permission?: PermissionResolvable;
 	right?: RightResolvable;
 	body?: `${string}Schema`; // typescript interface name
+	responses?: {
+		[status: number]: {
+			body?: `${string}Response`;
+		};
+	};
 	test?: {
 		response?: RouteResponse;
 		body?: unknown;
diff --git a/src/util/schemas/index.ts b/src/util/schemas/index.ts
index 2d254752..22927146 100644
--- a/src/util/schemas/index.ts
+++ b/src/util/schemas/index.ts
@@ -58,6 +58,7 @@ export * from "./PurgeSchema";
 export * from "./RegisterSchema";
 export * from "./RelationshipPostSchema";
 export * from "./RelationshipPutSchema";
+export * from "./responses";
 export * from "./RoleModifySchema";
 export * from "./RolePositionUpdateSchema";
 export * from "./SelectProtocolSchema";
diff --git a/src/util/schemas/responses/APIErrorOrCaptchaResponse.ts b/src/util/schemas/responses/APIErrorOrCaptchaResponse.ts
new file mode 100644
index 00000000..c9a0e5be
--- /dev/null
+++ b/src/util/schemas/responses/APIErrorOrCaptchaResponse.ts
@@ -0,0 +1,6 @@
+import { APIErrorResponse } from "./APIErrorResponse";
+import { CaptchaRequiredResponse } from "./CaptchaRequiredResponse";
+
+export type APIErrorOrCaptchaResponse =
+	| CaptchaRequiredResponse
+	| APIErrorResponse;
diff --git a/src/util/schemas/responses/APIErrorResponse.ts b/src/util/schemas/responses/APIErrorResponse.ts
new file mode 100644
index 00000000..25bb9504
--- /dev/null
+++ b/src/util/schemas/responses/APIErrorResponse.ts
@@ -0,0 +1,12 @@
+export interface APIErrorResponse {
+	code: number;
+	message: string;
+	errors: {
+		[key: string]: {
+			_errors: {
+				message: string;
+				code: string;
+			}[];
+		};
+	};
+}
diff --git a/src/util/schemas/responses/BackupCodesChallengeResponse.ts b/src/util/schemas/responses/BackupCodesChallengeResponse.ts
new file mode 100644
index 00000000..5473ad1f
--- /dev/null
+++ b/src/util/schemas/responses/BackupCodesChallengeResponse.ts
@@ -0,0 +1,4 @@
+export interface BackupCodesChallengeResponse {
+	nonce: string;
+	regenerate_nonce: string;
+}
diff --git a/src/util/schemas/responses/CaptchaRequiredResponse.ts b/src/util/schemas/responses/CaptchaRequiredResponse.ts
new file mode 100644
index 00000000..9f7f02ff
--- /dev/null
+++ b/src/util/schemas/responses/CaptchaRequiredResponse.ts
@@ -0,0 +1,5 @@
+export interface CaptchaRequiredResponse {
+	captcha_key: string;
+	captcha_sitekey: string;
+	captcha_service: string;
+}
diff --git a/src/util/schemas/responses/GenerateRegistrationTokensResponse.ts b/src/util/schemas/responses/GenerateRegistrationTokensResponse.ts
new file mode 100644
index 00000000..8816eabf
--- /dev/null
+++ b/src/util/schemas/responses/GenerateRegistrationTokensResponse.ts
@@ -0,0 +1,3 @@
+export interface GenerateRegistrationTokensResponse {
+	tokens: string[];
+}
diff --git a/src/util/schemas/responses/LocationMetadataResponse.ts b/src/util/schemas/responses/LocationMetadataResponse.ts
new file mode 100644
index 00000000..55337557
--- /dev/null
+++ b/src/util/schemas/responses/LocationMetadataResponse.ts
@@ -0,0 +1,5 @@
+export interface LocationMetadataResponse {
+	consent_required: boolean;
+	country_code: string;
+	promotional_email_opt_in: { required: true; pre_checked: false };
+}
diff --git a/src/util/schemas/responses/TokenResponse.ts b/src/util/schemas/responses/TokenResponse.ts
new file mode 100644
index 00000000..c811632f
--- /dev/null
+++ b/src/util/schemas/responses/TokenResponse.ts
@@ -0,0 +1,6 @@
+import { UserSettings } from "../../entities";
+
+export interface TokenResponse {
+	token: string;
+	settings: UserSettings;
+}
diff --git a/src/util/schemas/responses/index.ts b/src/util/schemas/responses/index.ts
new file mode 100644
index 00000000..ed91b866
--- /dev/null
+++ b/src/util/schemas/responses/index.ts
@@ -0,0 +1,7 @@
+export * from "./APIErrorOrCaptchaResponse";
+export * from "./APIErrorResponse";
+export * from "./BackupCodesChallengeResponse";
+export * from "./CaptchaRequiredResponse";
+export * from "./GenerateRegistrationTokensResponse";
+export * from "./LocationMetadataResponse";
+export * from "./TokenResponse";