diff --git a/api/src/middlewares/BodyParser.ts b/api/src/middlewares/BodyParser.ts
index b0ff699d..4cb376bc 100644
--- a/api/src/middlewares/BodyParser.ts
+++ b/api/src/middlewares/BodyParser.ts
@@ -6,6 +6,8 @@ export function BodyParser(opts?: OptionsJson) {
const jsonParser = bodyParser.json(opts);
return (req: Request, res: Response, next: NextFunction) => {
+ if (!req.headers["content-type"]) req.headers["content-type"] = "application/json";
+
jsonParser(req, res, (err) => {
if (err) {
// TODO: different errors for body parser (request size limit, wrong body type, invalid body, ...)
diff --git a/api/src/middlewares/ErrorHandler.ts b/api/src/middlewares/ErrorHandler.ts
index d288f3fb..96e703ce 100644
--- a/api/src/middlewares/ErrorHandler.ts
+++ b/api/src/middlewares/ErrorHandler.ts
@@ -3,6 +3,7 @@ import { HTTPError } from "lambert-server";
import { EntityNotFoundError } from "typeorm";
import { FieldError } from "@fosscord/api";
import { ApiError } from "@fosscord/util";
+const EntityNotFoundErrorRegex = /"(\w+)"/;
export function ErrorHandler(error: Error, req: Request, res: Response, next: NextFunction) {
if (!error) return next();
@@ -18,9 +19,9 @@ export function ErrorHandler(error: Error, req: Request, res: Response, next: Ne
code = error.code;
message = error.message;
httpcode = error.httpStatus;
- } else if (error instanceof EntityNotFoundError) {
- message = `${(error as any).stringifyTarget || "Item"} could not be found`;
- code = 404;
+ } else if (error.name === "EntityNotFoundError") {
+ message = `${error.message.match(EntityNotFoundErrorRegex)?.[1] || "Item"} could not be found`;
+ code = httpcode = 404;
} else if (error instanceof FieldError) {
code = Number(error.code);
message = error.message;
diff --git a/api/src/middlewares/RateLimit.ts b/api/src/middlewares/RateLimit.ts
index d1fd072f..1a38cfcf 100644
--- a/api/src/middlewares/RateLimit.ts
+++ b/api/src/middlewares/RateLimit.ts
@@ -107,7 +107,8 @@ export default function rateLimit(opts: {
}
export async function initRateLimits(app: Router) {
- const { routes, global, ip, error } = Config.get().limits.rate;
+ const { routes, global, ip, error, disabled } = Config.get().limits.rate;
+ if (disabled) return;
await listenEvent(EventRateLimit, (event) => {
Cache.set(event.channel_id as string, event.data);
event.acknowledge?.();
diff --git a/api/src/routes/applications/detectable.ts b/api/src/routes/applications/detectable.ts
index e4fbe1e4..411e95bf 100644
--- a/api/src/routes/applications/detectable.ts
+++ b/api/src/routes/applications/detectable.ts
@@ -1,10 +1,11 @@
import { Request, Response, Router } from "express";
+import { route } from "@fosscord/api";
const router: Router = Router();
-router.get("/", async (req: Request, res: Response) => {
+router.get("/", route({}), async (req: Request, res: Response) => {
//TODO
res.json([]).status(200);
});
-export default router;
\ No newline at end of file
+export default router;
diff --git a/api/src/routes/auth/login.ts b/api/src/routes/auth/login.ts
index f672658a..ff04f8aa 100644
--- a/api/src/routes/auth/login.ts
+++ b/api/src/routes/auth/login.ts
@@ -1,9 +1,7 @@
import { Request, Response, Router } from "express";
import { FieldErrors, route } from "@fosscord/api";
import bcrypt from "bcrypt";
-import jwt from "jsonwebtoken";
-import { Config, User } from "@fosscord/util";
-import { adjustEmail } from "./register";
+import { Config, User, generateToken, adjustEmail } from "@fosscord/util";
const router: Router = Router();
export default router;
@@ -68,25 +66,6 @@ router.post("/", route({ body: "LoginSchema" }), async (req: Request, res: Respo
res.json({ token, settings: user.settings });
});
-export async function generateToken(id: string) {
- const iat = Math.floor(Date.now() / 1000);
- const algorithm = "HS256";
-
- return new Promise((res, rej) => {
- jwt.sign(
- { id: id, iat },
- Config.get().security.jwtSecret,
- {
- algorithm
- },
- (err, token) => {
- if (err) return rej(err);
- return res(token);
- }
- );
- });
-}
-
/**
* POST /auth/login
* @argument { login: "email@gmail.com", password: "cleartextpassword", undelete: false, captcha_key: null, login_source: null, gift_code_sku_id: null, }
diff --git a/api/src/routes/auth/register.ts b/api/src/routes/auth/register.ts
index 4d3f2860..9c058399 100644
--- a/api/src/routes/auth/register.ts
+++ b/api/src/routes/auth/register.ts
@@ -1,10 +1,8 @@
import { Request, Response, Router } from "express";
-import { trimSpecial, User, Snowflake, Config, defaultSettings, Member, Invite } from "@fosscord/util";
+import { trimSpecial, User, Snowflake, Config, defaultSettings, generateToken, Invite, adjustEmail } from "@fosscord/util";
import bcrypt from "bcrypt";
-import { EMAIL_REGEX, FieldErrors, route } from "@fosscord/api";
+import { FieldErrors, route, getIpAdress, IPAnalysis, isProxy } from "@fosscord/api";
import "missing-native-js-functions";
-import { generateToken } from "./login";
-import { getIpAdress, IPAnalysis, isProxy } from "@fosscord/api";
import { HTTPError } from "lambert-server";
const router: Router = Router();
@@ -228,24 +226,6 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
return res.json({ token: await generateToken(user.id) });
});
-export function adjustEmail(email: string): string | undefined {
- if (!email) return email;
- // body parser already checked if it is a valid email
- const parts = <RegExpMatchArray>email.match(EMAIL_REGEX);
- // @ts-ignore
- if (!parts || parts.length < 5) return undefined;
- const domain = parts[5];
- const user = parts[1];
-
- // TODO: check accounts with uncommon email domains
- if (domain === "gmail.com" || domain === "googlemail.com") {
- // replace .dots and +alternatives -> Gmail Dot Trick https://support.google.com/mail/answer/7436150 and https://generator.email/blog/gmail-generator
- return user.replace(/[.]|(\+.*)/g, "") + "@gmail.com";
- }
-
- return email;
-}
-
export default router;
/**
diff --git a/api/src/routes/channels/#channel_id/recipients.ts b/api/src/routes/channels/#channel_id/recipients.ts
index c7beeee8..83b62049 100644
--- a/api/src/routes/channels/#channel_id/recipients.ts
+++ b/api/src/routes/channels/#channel_id/recipients.ts
@@ -1,9 +1,10 @@
import { Request, Response, Router } from "express";
import { Channel, ChannelRecipientAddEvent, ChannelType, DiscordApiErrors, DmChannelDTO, emitEvent, PublicUserProjection, Recipient, User } from "@fosscord/util";
+import { route } from "@fosscord/api"
const router: Router = Router();
-router.put("/:user_id", async (req: Request, res: Response) => {
+router.put("/:user_id", route({}), async (req: Request, res: Response) => {
const { channel_id, user_id } = req.params;
const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients"] });
@@ -39,7 +40,7 @@ router.put("/:user_id", async (req: Request, res: Response) => {
}
});
-router.delete("/:user_id", async (req: Request, res: Response) => {
+router.delete("/:user_id", route({}), async (req: Request, res: Response) => {
const { channel_id, user_id } = req.params;
const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients"] });
if (!(channel.type === ChannelType.GROUP_DM && (channel.owner_id === req.user_id || user_id === req.user_id)))
diff --git a/api/src/routes/guilds/#guild_id/channels.ts b/api/src/routes/guilds/#guild_id/channels.ts
index 13c6b515..a36e5448 100644
--- a/api/src/routes/guilds/#guild_id/channels.ts
+++ b/api/src/routes/guilds/#guild_id/channels.ts
@@ -5,7 +5,7 @@ import { route } from "@fosscord/api";
import { ChannelModifySchema } from "../../channels/#channel_id";
const router = Router();
-router.get("/", async (req: Request, res: Response) => {
+router.get("/", route({}), async (req: Request, res: Response) => {
const { guild_id } = req.params;
const channels = await Channel.find({ guild_id });
diff --git a/api/src/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts b/api/src/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts
index ae10be82..8f5ca7ba 100644
--- a/api/src/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts
+++ b/api/src/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts
@@ -4,14 +4,14 @@ import { Request, Response, Router } from "express";
const router = Router();
-router.delete("/:member_id/roles/:role_id", route({ permission: "MANAGE_ROLES" }), async (req: Request, res: Response) => {
+router.delete("/", route({ permission: "MANAGE_ROLES" }), async (req: Request, res: Response) => {
const { guild_id, role_id, member_id } = req.params;
await Member.removeRole(member_id, guild_id, role_id);
res.sendStatus(204);
});
-router.put("/:member_id/roles/:role_id", route({ permission: "MANAGE_ROLES" }), async (req: Request, res: Response) => {
+router.put("/", route({ permission: "MANAGE_ROLES" }), async (req: Request, res: Response) => {
const { guild_id, role_id, member_id } = req.params;
await Member.addRole(member_id, guild_id, role_id);
diff --git a/api/src/routes/guilds/#guild_id/roles.ts b/api/src/routes/guilds/#guild_id/roles.ts
index bac63bd4..d1d60906 100644
--- a/api/src/routes/guilds/#guild_id/roles.ts
+++ b/api/src/routes/guilds/#guild_id/roles.ts
@@ -29,7 +29,7 @@ export type RolePositionUpdateSchema = {
position: number;
}[];
-router.get("/", async (req: Request, res: Response) => {
+router.get("/", route({}), async (req: Request, res: Response) => {
const guild_id = req.params.guild_id;
await Member.IsInGuildOrFail(req.user_id, guild_id);
diff --git a/api/src/routes/outbound-promotions.ts b/api/src/routes/outbound-promotions.ts
index e4fbe1e4..411e95bf 100644
--- a/api/src/routes/outbound-promotions.ts
+++ b/api/src/routes/outbound-promotions.ts
@@ -1,10 +1,11 @@
import { Request, Response, Router } from "express";
+import { route } from "@fosscord/api";
const router: Router = Router();
-router.get("/", async (req: Request, res: Response) => {
+router.get("/", route({}), async (req: Request, res: Response) => {
//TODO
res.json([]).status(200);
});
-export default router;
\ No newline at end of file
+export default router;
diff --git a/api/src/routes/sticker-packs/#id/index.ts b/api/src/routes/sticker-packs/#id/index.ts
index 2344a48f..7f723e97 100644
--- a/api/src/routes/sticker-packs/#id/index.ts
+++ b/api/src/routes/sticker-packs/#id/index.ts
@@ -1,8 +1,9 @@
import { Request, Response, Router } from "express";
+import { route } from "@fosscord/api";
const router: Router = Router();
-router.get("/", async (req: Request, res: Response) => {
+router.get("/", route({}), async (req: Request, res: Response) => {
//TODO
res.json({
id: "",
@@ -15,4 +16,4 @@ router.get("/", async (req: Request, res: Response) => {
}).status(200);
});
-export default router;
\ No newline at end of file
+export default router;
diff --git a/api/src/routes/sticker-packs/index.ts b/api/src/routes/sticker-packs/index.ts
index 6c4e46d8..d671c161 100644
--- a/api/src/routes/sticker-packs/index.ts
+++ b/api/src/routes/sticker-packs/index.ts
@@ -1,10 +1,11 @@
import { Request, Response, Router } from "express";
+import { route } from "@fosscord/api";
const router: Router = Router();
-router.get("/", async (req: Request, res: Response) => {
+router.get("/", route({}), async (req: Request, res: Response) => {
//TODO
res.json({ sticker_packs: [] }).status(200);
});
-export default router;
\ No newline at end of file
+export default router;
diff --git a/api/src/routes/store/applications.ts b/api/src/routes/store/applications.ts
index 69cd716d..352c1752 100644
--- a/api/src/routes/store/applications.ts
+++ b/api/src/routes/store/applications.ts
@@ -1,11 +1,12 @@
import { Request, Response, Router } from "express";
+import { route } from "@fosscord/api";
const router: Router = Router();
-router.get("/applications/:id", async (req: Request, res: Response) => {
- //TODO
- const { id } = req.params;
+router.get("/applications/:id", route({}), async (req: Request, res: Response) => {
+ //TODO
+ const { id } = req.params;
res.json([]).status(200);
});
-export default router;
\ No newline at end of file
+export default router;
diff --git a/api/src/routes/store/skus.ts b/api/src/routes/store/skus.ts
index 5c37850d..7d0e12eb 100644
--- a/api/src/routes/store/skus.ts
+++ b/api/src/routes/store/skus.ts
@@ -1,11 +1,12 @@
import { Request, Response, Router } from "express";
+import { route } from "@fosscord/api";
const router: Router = Router();
-router.get("/skus/:id", async (req: Request, res: Response) => {
+router.get("/skus/:id", route({}), async (req: Request, res: Response) => {
//TODO
- const { id } = req.params;
+ const { id } = req.params;
res.json([]).status(200);
});
-export default router;
\ No newline at end of file
+export default router;
diff --git a/api/src/routes/users/#id/profile.ts b/api/src/routes/users/#id/profile.ts
index 06d5c38c..15457547 100644
--- a/api/src/routes/users/#id/profile.ts
+++ b/api/src/routes/users/#id/profile.ts
@@ -11,7 +11,7 @@ export interface UserProfileResponse {
premium_since?: Date;
}
-router.get("/", route({ response: { body: "UserProfileResponse" } }), async (req: Request, res: Response) => {
+router.get("/", route({ test: { response: { body: "UserProfileResponse" } } }), async (req: Request, res: Response) => {
if (req.params.id === "@me") req.params.id = req.user_id;
const user = await User.getPublicUser(req.params.id, { relations: ["connected_accounts"] });
diff --git a/api/src/routes/users/@me/applications/#app_id/entitlements.ts b/api/src/routes/users/@me/applications/#app_id/entitlements.ts
index e4fbe1e4..411e95bf 100644
--- a/api/src/routes/users/@me/applications/#app_id/entitlements.ts
+++ b/api/src/routes/users/@me/applications/#app_id/entitlements.ts
@@ -1,10 +1,11 @@
import { Request, Response, Router } from "express";
+import { route } from "@fosscord/api";
const router: Router = Router();
-router.get("/", async (req: Request, res: Response) => {
+router.get("/", route({}), async (req: Request, res: Response) => {
//TODO
res.json([]).status(200);
});
-export default router;
\ No newline at end of file
+export default router;
diff --git a/api/src/routes/users/@me/billing/country-code.ts b/api/src/routes/users/@me/billing/country-code.ts
index ac3653a2..33d40796 100644
--- a/api/src/routes/users/@me/billing/country-code.ts
+++ b/api/src/routes/users/@me/billing/country-code.ts
@@ -1,10 +1,11 @@
import { Request, Response, Router } from "express";
+import { route } from "@fosscord/api";
const router: Router = Router();
-router.get("/", async (req: Request, res: Response) => {
+router.get("/", route({}), async (req: Request, res: Response) => {
//TODO
- res.json({ "country_code": "US" }).status(200);
+ res.json({ country_code: "US" }).status(200);
});
-export default router;
\ No newline at end of file
+export default router;
diff --git a/api/src/routes/users/@me/billing/subscriptions.ts b/api/src/routes/users/@me/billing/subscriptions.ts
index e4fbe1e4..411e95bf 100644
--- a/api/src/routes/users/@me/billing/subscriptions.ts
+++ b/api/src/routes/users/@me/billing/subscriptions.ts
@@ -1,10 +1,11 @@
import { Request, Response, Router } from "express";
+import { route } from "@fosscord/api";
const router: Router = Router();
-router.get("/", async (req: Request, res: Response) => {
+router.get("/", route({}), async (req: Request, res: Response) => {
//TODO
res.json([]).status(200);
});
-export default router;
\ No newline at end of file
+export default router;
diff --git a/api/src/routes/users/@me/index.ts b/api/src/routes/users/@me/index.ts
index 835aab4d..f6bb04d7 100644
--- a/api/src/routes/users/@me/index.ts
+++ b/api/src/routes/users/@me/index.ts
@@ -22,7 +22,7 @@ export interface UserModifySchema {
code?: string;
}
-router.get("/", async (req: Request, res: Response) => {
+router.get("/", route({}), async (req: Request, res: Response) => {
res.json(await User.findOne({ select: PrivateUserProjection, where: { id: req.user_id } }));
});
diff --git a/api/src/util/String.ts b/api/src/util/String.ts
index 2fe32d2c..67d87e37 100644
--- a/api/src/util/String.ts
+++ b/api/src/util/String.ts
@@ -1,8 +1,6 @@
import { Request } from "express";
import { ntob } from "./Base64";
import { FieldErrors } from "./FieldError";
-export const EMAIL_REGEX =
- /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
export function checkLength(str: string, min: number, max: number, key: string, req: Request) {
if (str.length < min || str.length > max) {
diff --git a/api/src/util/route.ts b/api/src/util/route.ts
index b7e6296b..e7c7ed1c 100644
--- a/api/src/util/route.ts
+++ b/api/src/util/route.ts
@@ -1,4 +1,4 @@
-import { DiscordApiErrors, Event, EventData, getPermission, PermissionResolvable, Permissions } from "@fosscord/util";
+import { DiscordApiErrors, EVENT, Event, EventData, getPermission, PermissionResolvable, Permissions } from "@fosscord/util";
import { NextFunction, Request, Response } from "express";
import fs from "fs";
import path from "path";
@@ -29,17 +29,16 @@ declare global {
}
}
-export type RouteSchema = string; // typescript interface name
-export type RouteResponse = { status?: number; body?: RouteSchema; headers?: Record<string, string> };
+export type RouteResponse = { status?: number; body?: `${string}Response`; headers?: Record<string, string> };
export interface RouteOptions {
permission?: PermissionResolvable;
- body?: RouteSchema;
- response?: RouteResponse;
- example?: {
+ body?: `${string}Schema`; // typescript interface name
+ test?: {
+ response?: RouteResponse;
body?: any;
path?: string;
- event?: EventData;
+ event?: EVENT | EVENT[];
headers?: Record<string, string>;
};
}
|