2023-09-11 05:54:14 +02:00
|
|
|
import { getConfig } from "@config";
|
2023-10-02 02:07:29 +02:00
|
|
|
import { jsonResponse } from "@response";
|
2023-10-16 05:51:29 +02:00
|
|
|
import { MatchedRoute } from "bun";
|
2023-09-15 05:21:38 +02:00
|
|
|
import { appendFile } from "fs/promises";
|
2023-10-16 05:51:29 +02:00
|
|
|
import { matches } from "ip-matching";
|
2023-09-12 22:48:10 +02:00
|
|
|
import "reflect-metadata";
|
2023-09-14 04:25:45 +02:00
|
|
|
import { AppDataSource } from "~database/datasource";
|
2023-10-16 05:51:29 +02:00
|
|
|
import { User } from "~database/entities/User";
|
|
|
|
|
import { APIRouteMeta } from "~types/api";
|
2023-09-11 05:54:14 +02:00
|
|
|
|
2023-09-11 05:31:08 +02:00
|
|
|
const router = new Bun.FileSystemRouter({
|
|
|
|
|
style: "nextjs",
|
|
|
|
|
dir: process.cwd() + "/server/api",
|
2023-09-13 02:29:13 +02:00
|
|
|
});
|
2023-09-11 05:31:08 +02:00
|
|
|
|
2023-09-15 07:08:59 +02:00
|
|
|
console.log("[+] Starting Lysand...");
|
2023-09-11 05:46:20 +02:00
|
|
|
|
2023-09-11 05:54:14 +02:00
|
|
|
const config = getConfig();
|
2023-09-15 05:21:38 +02:00
|
|
|
const requests_log = Bun.file(process.cwd() + "/logs/requests.log");
|
|
|
|
|
|
|
|
|
|
if (!(await requests_log.exists())) {
|
|
|
|
|
console.log("[+] requests.log does not exist, creating it...");
|
|
|
|
|
await Bun.write(process.cwd() + "/logs/requests.log", "");
|
|
|
|
|
}
|
2023-09-11 05:54:14 +02:00
|
|
|
|
2023-09-14 04:25:45 +02:00
|
|
|
if (!AppDataSource.isInitialized) await AppDataSource.initialize();
|
|
|
|
|
|
2023-09-11 05:31:08 +02:00
|
|
|
Bun.serve({
|
2023-09-22 04:15:12 +02:00
|
|
|
port: config.http.bind_port,
|
|
|
|
|
hostname: config.http.bind || "0.0.0.0", // defaults to "0.0.0.0"
|
2023-09-11 05:31:08 +02:00
|
|
|
async fetch(req) {
|
2023-10-16 05:51:29 +02:00
|
|
|
/* Check for banned IPs */
|
|
|
|
|
const request_ip = this.requestIP(req)?.address ?? "";
|
2023-09-15 05:21:38 +02:00
|
|
|
|
2023-10-16 05:51:29 +02:00
|
|
|
for (const ip of config.http.banned_ips) {
|
|
|
|
|
try {
|
|
|
|
|
if (matches(ip, request_ip)) {
|
|
|
|
|
return new Response(undefined, {
|
|
|
|
|
status: 403,
|
|
|
|
|
statusText: "Forbidden",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error(`[-] Error while parsing banned IP "${ip}" `);
|
|
|
|
|
throw e;
|
2023-09-15 05:21:38 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-16 05:51:29 +02:00
|
|
|
await logRequest(req);
|
|
|
|
|
|
2023-10-02 02:07:29 +02:00
|
|
|
if (req.method === "OPTIONS") {
|
|
|
|
|
return jsonResponse({});
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-11 05:31:08 +02:00
|
|
|
const matchedRoute = router.match(req);
|
|
|
|
|
|
|
|
|
|
if (matchedRoute) {
|
2023-10-16 05:51:29 +02:00
|
|
|
const file: {
|
|
|
|
|
meta: APIRouteMeta;
|
|
|
|
|
default: (
|
|
|
|
|
req: Request,
|
|
|
|
|
matchedRoute: MatchedRoute
|
|
|
|
|
) => Response | Promise<Response>;
|
|
|
|
|
} = await import(matchedRoute.filePath);
|
|
|
|
|
|
|
|
|
|
const meta = file.meta;
|
|
|
|
|
|
|
|
|
|
// Check for allowed requests
|
|
|
|
|
if (!meta.allowedMethods.includes(req.method as any)) {
|
|
|
|
|
return new Response(undefined, {
|
|
|
|
|
status: 405,
|
|
|
|
|
statusText: `Method not allowed: allowed methods are: ${meta.allowedMethods.join(
|
|
|
|
|
", "
|
|
|
|
|
)}`,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Check for ratelimits
|
|
|
|
|
|
|
|
|
|
// Check for authentication if required
|
|
|
|
|
if (meta.auth.required) {
|
|
|
|
|
const { user } = await User.getFromRequest(req);
|
|
|
|
|
|
|
|
|
|
if (!user) {
|
|
|
|
|
return new Response(undefined, {
|
|
|
|
|
status: 401,
|
|
|
|
|
statusText: "Unauthorized",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} else if (
|
|
|
|
|
(meta.auth.requiredOnMethods ?? []).includes(req.method as any)
|
|
|
|
|
) {
|
|
|
|
|
const { user } = await User.getFromRequest(req);
|
|
|
|
|
|
|
|
|
|
if (!user) {
|
|
|
|
|
return new Response(undefined, {
|
|
|
|
|
status: 401,
|
|
|
|
|
statusText: "Unauthorized",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return file.default(req, matchedRoute);
|
2023-09-11 05:31:08 +02:00
|
|
|
} else {
|
2023-09-11 05:46:20 +02:00
|
|
|
return new Response(undefined, {
|
2023-09-11 05:31:08 +02:00
|
|
|
status: 404,
|
|
|
|
|
statusText: "Route not found",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
});
|
2023-09-11 05:46:20 +02:00
|
|
|
|
2023-10-16 05:51:29 +02:00
|
|
|
const logRequest = async (req: Request) => {
|
|
|
|
|
if (config.logging.log_requests_verbose) {
|
|
|
|
|
await appendFile(
|
|
|
|
|
`${process.cwd()}/logs/requests.log`,
|
|
|
|
|
`[${new Date().toISOString()}] ${req.method} ${
|
|
|
|
|
req.url
|
|
|
|
|
}\n\tHeaders:\n`
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Add headers
|
|
|
|
|
|
|
|
|
|
const headers = req.headers.entries();
|
|
|
|
|
|
|
|
|
|
for (const [key, value] of headers) {
|
|
|
|
|
await appendFile(
|
|
|
|
|
`${process.cwd()}/logs/requests.log`,
|
|
|
|
|
`\t\t${key}: ${value}\n`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const body = await req.clone().text();
|
|
|
|
|
|
|
|
|
|
await appendFile(
|
|
|
|
|
`${process.cwd()}/logs/requests.log`,
|
|
|
|
|
`\tBody:\n\t${body}\n`
|
|
|
|
|
);
|
|
|
|
|
} else if (config.logging.log_requests) {
|
|
|
|
|
await appendFile(
|
|
|
|
|
process.cwd() + "/logs/requests.log",
|
|
|
|
|
`[${new Date().toISOString()}] ${req.method} ${req.url}\n`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-09-15 07:08:59 +02:00
|
|
|
console.log("[+] Lysand started!");
|