mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 13:59:16 +01:00
refactor: 🚚 Organize code into sub-packages, instead of a single large package
This commit is contained in:
parent
79742f47dc
commit
a6d3ebbeef
366 changed files with 942 additions and 833 deletions
16
packages/api/middlewares/agent-bans.ts
Normal file
16
packages/api/middlewares/agent-bans.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { ApiError } from "@versia/kit";
|
||||
import { config } from "@versia-server/config";
|
||||
import { createMiddleware } from "hono/factory";
|
||||
|
||||
export const agentBans = createMiddleware(async (context, next) => {
|
||||
// Check for banned user agents (regex)
|
||||
const ua = context.req.header("user-agent") ?? "";
|
||||
|
||||
for (const agent of config.http.banned_user_agents) {
|
||||
if (new RegExp(agent).test(ua)) {
|
||||
throw new ApiError(403, "Forbidden");
|
||||
}
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
20
packages/api/middlewares/boundary-check.ts
Normal file
20
packages/api/middlewares/boundary-check.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { ApiError } from "@versia/kit";
|
||||
import { createMiddleware } from "hono/factory";
|
||||
|
||||
export const boundaryCheck = createMiddleware(async (context, next) => {
|
||||
// Checks that FormData boundary is present
|
||||
const contentType = context.req.header("content-type");
|
||||
|
||||
if (
|
||||
contentType?.includes("multipart/form-data") &&
|
||||
!contentType.includes("boundary")
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
"Missing FormData boundary",
|
||||
"You are sending a request with a multipart/form-data content type but without a boundary. Please include a boundary in the Content-Type header. For more information, visit https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data",
|
||||
);
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
40
packages/api/middlewares/ip-bans.ts
Normal file
40
packages/api/middlewares/ip-bans.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { getLogger } from "@logtape/logtape";
|
||||
import { ApiError } from "@versia/kit";
|
||||
import { config } from "@versia-server/config";
|
||||
import type { SocketAddress } from "bun";
|
||||
import { createMiddleware } from "hono/factory";
|
||||
import { matches } from "ip-matching";
|
||||
import { sentry } from "@/sentry";
|
||||
|
||||
export const ipBans = createMiddleware(async (context, next) => {
|
||||
// Check for banned IPs
|
||||
|
||||
const requestIp = context.env?.ip as SocketAddress | undefined | null;
|
||||
|
||||
if (!requestIp?.address) {
|
||||
await next();
|
||||
return;
|
||||
}
|
||||
|
||||
for (const ip of config.http.banned_ips) {
|
||||
try {
|
||||
if (matches(ip, requestIp?.address)) {
|
||||
throw new ApiError(403, "Forbidden");
|
||||
}
|
||||
} catch (e) {
|
||||
const logger = getLogger("server");
|
||||
|
||||
logger.error`Error while parsing banned IP "${ip}" `;
|
||||
logger.error`${e}`;
|
||||
sentry?.captureException(e);
|
||||
|
||||
return context.json(
|
||||
{ error: `A server error occured: ${(e as Error).message}` },
|
||||
500,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await next();
|
||||
return;
|
||||
});
|
||||
37
packages/api/middlewares/logger.ts
Normal file
37
packages/api/middlewares/logger.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { getLogger } from "@logtape/logtape";
|
||||
import { config } from "@versia-server/config";
|
||||
import { SHA256 } from "bun";
|
||||
import chalk from "chalk";
|
||||
import { createMiddleware } from "hono/factory";
|
||||
|
||||
export const logger = createMiddleware(async (context, next) => {
|
||||
if (config.logging.types.requests) {
|
||||
const serverLogger = getLogger("server");
|
||||
const body = await context.req.raw.clone().text();
|
||||
|
||||
const urlAndMethod = `${chalk.green(context.req.method)} ${chalk.blue(context.req.url)}`;
|
||||
|
||||
const hash = `${chalk.bold("Hash")}: ${chalk.yellow(
|
||||
new SHA256().update(body).digest("hex"),
|
||||
)}`;
|
||||
|
||||
const headers = `${chalk.bold("Headers")}:\n${Array.from(
|
||||
context.req.raw.headers.entries(),
|
||||
)
|
||||
.map(
|
||||
([key, value]) =>
|
||||
` - ${chalk.cyan(key)}: ${chalk.white(value)}`,
|
||||
)
|
||||
.join("\n")}`;
|
||||
|
||||
const bodyLog = `${chalk.bold("Body")}: ${chalk.gray(body)}`;
|
||||
|
||||
if (config.logging.types.requests_content) {
|
||||
serverLogger.debug`${urlAndMethod}\n${hash}\n${headers}\n${bodyLog}`;
|
||||
} else {
|
||||
serverLogger.debug`${urlAndMethod}`;
|
||||
}
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
40
packages/api/middlewares/rate-limit.ts
Normal file
40
packages/api/middlewares/rate-limit.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import type { ApiError } from "@versia/kit";
|
||||
import { env } from "bun";
|
||||
import type { MiddlewareHandler } from "hono";
|
||||
import { rateLimiter } from "hono-rate-limiter";
|
||||
import type { z } from "zod";
|
||||
import type { HonoEnv } from "~/types/api";
|
||||
|
||||
// Not exported by hono-rate-limiter
|
||||
// So we define it ourselves
|
||||
type RateLimitEnv = HonoEnv & {
|
||||
Variables: {
|
||||
rateLimit: {
|
||||
limit: number;
|
||||
remaining: number;
|
||||
resetTime: Date;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export const rateLimit = (
|
||||
limit: number,
|
||||
windowMs = 60 * 1000,
|
||||
): MiddlewareHandler<RateLimitEnv> =>
|
||||
env.DISABLE_RATE_LIMIT === "true"
|
||||
? (_, next): Promise<void> => next()
|
||||
: rateLimiter<RateLimitEnv>({
|
||||
keyGenerator: (c): string => c.req.path,
|
||||
message: (c): z.infer<typeof ApiError.zodSchema> => ({
|
||||
error: "Too many requests, please try again later.",
|
||||
details: {
|
||||
limit: c.get("rateLimit").limit,
|
||||
remaining: c.get("rateLimit").remaining,
|
||||
reset: c.get("rateLimit").resetTime.toISOString(),
|
||||
resetInMs:
|
||||
c.get("rateLimit").resetTime.getTime() - Date.now(),
|
||||
},
|
||||
}),
|
||||
windowMs,
|
||||
limit,
|
||||
});
|
||||
19
packages/api/middlewares/url-check.ts
Normal file
19
packages/api/middlewares/url-check.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { config } from "@versia-server/config";
|
||||
import { createMiddleware } from "hono/factory";
|
||||
|
||||
export const urlCheck = createMiddleware(async (context, next) => {
|
||||
// Check that request URL matches base_url
|
||||
const baseUrl = config.http.base_url;
|
||||
|
||||
if (new URL(context.req.url).origin !== baseUrl.origin) {
|
||||
return context.json(
|
||||
{
|
||||
error: `Request URL ${context.req.url} does not match base URL ${baseUrl.origin}`,
|
||||
},
|
||||
400,
|
||||
);
|
||||
}
|
||||
|
||||
await next();
|
||||
return;
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue