first commit
This commit is contained in:
commit
d69d3b7033
21 changed files with 1142 additions and 0 deletions
181
src/database/redis.ts
Normal file
181
src/database/redis.ts
Normal file
|
@ -0,0 +1,181 @@
|
|||
import { redisConfig } from "@config/secrets";
|
||||
import { logger } from "@helpers/logger";
|
||||
import { createClient, type RedisClientType } from "redis";
|
||||
|
||||
class RedisJson {
|
||||
private static instance: RedisJson | null = null;
|
||||
private client: RedisClientType | null = null;
|
||||
|
||||
private constructor() {}
|
||||
|
||||
public static async initialize(): Promise<RedisJson> {
|
||||
if (!RedisJson.instance) {
|
||||
RedisJson.instance = new RedisJson();
|
||||
RedisJson.instance.client = createClient({
|
||||
socket: {
|
||||
host: redisConfig.host,
|
||||
port: redisConfig.port,
|
||||
},
|
||||
username: redisConfig.username || undefined,
|
||||
password: redisConfig.password || undefined,
|
||||
});
|
||||
|
||||
RedisJson.instance.client.on("error", (err: Error) => {
|
||||
logger.error("Redis connection error:");
|
||||
logger.error((err as Error) || "Unknown error");
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
RedisJson.instance.client.on("connect", () => {
|
||||
logger.info([
|
||||
"Connected to Redis on",
|
||||
`${redisConfig.host}:${redisConfig.port}`,
|
||||
]);
|
||||
});
|
||||
|
||||
await RedisJson.instance.client.connect();
|
||||
}
|
||||
|
||||
return RedisJson.instance;
|
||||
}
|
||||
|
||||
public static getInstance(): RedisJson {
|
||||
if (!RedisJson.instance || !RedisJson.instance.client) {
|
||||
throw new Error(
|
||||
"Redis instance not initialized. Call initialize() first.",
|
||||
);
|
||||
}
|
||||
return RedisJson.instance;
|
||||
}
|
||||
|
||||
public async disconnect(): Promise<void> {
|
||||
if (!this.client) {
|
||||
logger.error("Redis client is not initialized.");
|
||||
process.exit(1);
|
||||
}
|
||||
try {
|
||||
await this.client.disconnect();
|
||||
this.client = null;
|
||||
logger.info("Redis disconnected successfully.");
|
||||
} catch (error) {
|
||||
logger.error("Error disconnecting Redis client:");
|
||||
logger.error(error as Error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async get(
|
||||
type: "JSON" | "STRING",
|
||||
key: string,
|
||||
path?: string,
|
||||
): Promise<
|
||||
string | number | boolean | Record<string, unknown> | null | unknown
|
||||
> {
|
||||
if (!this.client) {
|
||||
logger.error("Redis client is not initialized.");
|
||||
throw new Error("Redis client is not initialized.");
|
||||
}
|
||||
try {
|
||||
if (type === "JSON") {
|
||||
const value: unknown = await this.client.json.get(key, {
|
||||
path,
|
||||
});
|
||||
|
||||
if (value instanceof Date) {
|
||||
return value.toISOString();
|
||||
}
|
||||
|
||||
return value;
|
||||
} else if (type === "STRING") {
|
||||
const value: string | null = await this.client.get(key);
|
||||
return value;
|
||||
} else {
|
||||
throw new Error(`Invalid type: ${type}`);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error getting value from Redis for key: ${key}`);
|
||||
logger.error(error as Error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async set(
|
||||
type: "JSON" | "STRING",
|
||||
key: string,
|
||||
value: unknown,
|
||||
path?: string,
|
||||
expiresInSeconds?: number,
|
||||
): Promise<void> {
|
||||
if (!this.client) {
|
||||
logger.error("Redis client is not initialized.");
|
||||
throw new Error("Redis client is not initialized.");
|
||||
}
|
||||
try {
|
||||
if (type === "JSON") {
|
||||
await this.client.json.set(key, path || "$", value as string);
|
||||
|
||||
if (expiresInSeconds) {
|
||||
await this.client.expire(key, expiresInSeconds);
|
||||
}
|
||||
} else if (type === "STRING") {
|
||||
if (expiresInSeconds) {
|
||||
await this.client.set(key, value as string, {
|
||||
EX: expiresInSeconds,
|
||||
});
|
||||
} else {
|
||||
await this.client.set(key, value as string);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Invalid type: ${type}`);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error setting value in Redis for key: ${key}`);
|
||||
logger.error(error as Error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async delete(type: "JSON" | "STRING", key: string): Promise<void> {
|
||||
if (!this.client) {
|
||||
logger.error("Redis client is not initialized.");
|
||||
throw new Error("Redis client is not initialized.");
|
||||
}
|
||||
try {
|
||||
if (type === "JSON") {
|
||||
await this.client.json.del(key);
|
||||
} else if (type === "STRING") {
|
||||
await this.client.del(key);
|
||||
} else {
|
||||
throw new Error(`Invalid type: ${type}`);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error deleting value from Redis for key: ${key}`);
|
||||
logger.error(error as Error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async expire(key: string, seconds: number): Promise<void> {
|
||||
if (!this.client) {
|
||||
logger.error("Redis client is not initialized.");
|
||||
throw new Error("Redis client is not initialized.");
|
||||
}
|
||||
try {
|
||||
await this.client.expire(key, seconds);
|
||||
} catch (error) {
|
||||
logger.error(`Error expiring key in Redis: ${key}`);
|
||||
logger.error(error as Error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const redis: {
|
||||
initialize: () => Promise<RedisJson>;
|
||||
getInstance: () => RedisJson;
|
||||
} = {
|
||||
initialize: RedisJson.initialize,
|
||||
getInstance: RedisJson.getInstance,
|
||||
};
|
||||
|
||||
export { RedisJson };
|
59
src/helpers/char.ts
Normal file
59
src/helpers/char.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { booruConfig } from "@config/booru";
|
||||
|
||||
function timestampToReadable(timestamp?: number): string {
|
||||
const date: Date =
|
||||
timestamp && !isNaN(timestamp) ? new Date(timestamp) : new Date();
|
||||
if (isNaN(date.getTime())) return "Invalid Date";
|
||||
return date.toISOString().replace("T", " ").replace("Z", "");
|
||||
}
|
||||
|
||||
function tagsToExpectedFormat(
|
||||
tags: string[] | string | Record<string, string[]>,
|
||||
minus: boolean = false,
|
||||
onlyMinus: boolean = false,
|
||||
): string {
|
||||
const delimiter: string = minus ? (onlyMinus ? "-" : "+-") : "+";
|
||||
|
||||
if (!tags) return "";
|
||||
|
||||
const processTag: (tag: string) => string | null = (tag: string) => {
|
||||
const trimmed: string | null = tag.trim();
|
||||
return trimmed ? trimmed : null;
|
||||
};
|
||||
|
||||
if (typeof tags === "string") {
|
||||
return tags
|
||||
.split(/\s+|,/)
|
||||
.map(processTag)
|
||||
.filter((tag: string | null): tag is string => Boolean(tag))
|
||||
.join(delimiter);
|
||||
}
|
||||
|
||||
if (Array.isArray(tags)) {
|
||||
return tags
|
||||
.map(processTag)
|
||||
.filter((tag: string | null): tag is string => Boolean(tag))
|
||||
.join(delimiter);
|
||||
}
|
||||
|
||||
const allTags: string[] = Object.values(tags).flat();
|
||||
return allTags
|
||||
.map(processTag)
|
||||
.filter((tag: string | null): tag is string => Boolean(tag))
|
||||
.join(delimiter);
|
||||
}
|
||||
|
||||
function determineBooru(
|
||||
booruName: string,
|
||||
): IBooruConfigMap[keyof IBooruConfigMap] | null {
|
||||
const booru: IBooruConfigMap[keyof IBooruConfigMap] | undefined =
|
||||
Object.values(booruConfig).find(
|
||||
(booru: IBooruConfigMap[keyof IBooruConfigMap]) =>
|
||||
booru.name === booruName ||
|
||||
booru.aliases.includes(booruName.toLowerCase()),
|
||||
);
|
||||
|
||||
return booru || null;
|
||||
}
|
||||
|
||||
export { determineBooru, tagsToExpectedFormat, timestampToReadable };
|
175
src/helpers/logger.ts
Normal file
175
src/helpers/logger.ts
Normal file
|
@ -0,0 +1,175 @@
|
|||
import { environment } from "@config/environment";
|
||||
import type { Stats } from "fs";
|
||||
import {
|
||||
createWriteStream,
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
statSync,
|
||||
WriteStream,
|
||||
} from "fs";
|
||||
import { EOL } from "os";
|
||||
import { basename, join } from "path";
|
||||
|
||||
import { timestampToReadable } from "./char";
|
||||
|
||||
class Logger {
|
||||
private static instance: Logger;
|
||||
private static log: string = join(__dirname, "../../logs");
|
||||
|
||||
public static getInstance(): Logger {
|
||||
if (!Logger.instance) {
|
||||
Logger.instance = new Logger();
|
||||
}
|
||||
|
||||
return Logger.instance;
|
||||
}
|
||||
|
||||
private writeToLog(logMessage: string): void {
|
||||
if (environment.development) return;
|
||||
|
||||
const date: Date = new Date();
|
||||
const logDir: string = Logger.log;
|
||||
const logFile: string = join(
|
||||
logDir,
|
||||
`${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}.log`,
|
||||
);
|
||||
|
||||
if (!existsSync(logDir)) {
|
||||
mkdirSync(logDir, { recursive: true });
|
||||
}
|
||||
|
||||
let addSeparator: boolean = false;
|
||||
|
||||
if (existsSync(logFile)) {
|
||||
const fileStats: Stats = statSync(logFile);
|
||||
if (fileStats.size > 0) {
|
||||
const lastModified: Date = new Date(fileStats.mtime);
|
||||
if (
|
||||
lastModified.getFullYear() === date.getFullYear() &&
|
||||
lastModified.getMonth() === date.getMonth() &&
|
||||
lastModified.getDate() === date.getDate() &&
|
||||
lastModified.getHours() !== date.getHours()
|
||||
) {
|
||||
addSeparator = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const stream: WriteStream = createWriteStream(logFile, { flags: "a" });
|
||||
|
||||
if (addSeparator) {
|
||||
stream.write(`${EOL}${date.toISOString()}${EOL}`);
|
||||
}
|
||||
|
||||
stream.write(`${logMessage}${EOL}`);
|
||||
stream.close();
|
||||
}
|
||||
|
||||
private extractFileName(stack: string): string {
|
||||
const stackLines: string[] = stack.split("\n");
|
||||
let callerFile: string = "";
|
||||
|
||||
for (let i: number = 2; i < stackLines.length; i++) {
|
||||
const line: string = stackLines[i].trim();
|
||||
if (line && !line.includes("Logger.") && line.includes("(")) {
|
||||
callerFile = line.split("(")[1]?.split(")")[0] || "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return basename(callerFile);
|
||||
}
|
||||
|
||||
private getCallerInfo(stack: unknown): {
|
||||
filename: string;
|
||||
timestamp: string;
|
||||
} {
|
||||
const filename: string =
|
||||
typeof stack === "string" ? this.extractFileName(stack) : "unknown";
|
||||
|
||||
const readableTimestamp: string = timestampToReadable();
|
||||
|
||||
return { filename, timestamp: readableTimestamp };
|
||||
}
|
||||
|
||||
public info(message: string | string[], breakLine: boolean = false): void {
|
||||
const stack: string = new Error().stack || "";
|
||||
const { filename, timestamp } = this.getCallerInfo(stack);
|
||||
|
||||
const joinedMessage: string = Array.isArray(message)
|
||||
? message.join(" ")
|
||||
: message;
|
||||
|
||||
const logMessageParts: ILogMessageParts = {
|
||||
readableTimestamp: { value: timestamp, color: "90" },
|
||||
level: { value: "[INFO]", color: "32" },
|
||||
filename: { value: `(${filename})`, color: "36" },
|
||||
message: { value: joinedMessage, color: "0" },
|
||||
};
|
||||
|
||||
this.writeToLog(`${timestamp} [INFO] (${filename}) ${joinedMessage}`);
|
||||
this.writeConsoleMessageColored(logMessageParts, breakLine);
|
||||
}
|
||||
|
||||
public warn(message: string | string[], breakLine: boolean = false): void {
|
||||
const stack: string = new Error().stack || "";
|
||||
const { filename, timestamp } = this.getCallerInfo(stack);
|
||||
|
||||
const joinedMessage: string = Array.isArray(message)
|
||||
? message.join(" ")
|
||||
: message;
|
||||
|
||||
const logMessageParts: ILogMessageParts = {
|
||||
readableTimestamp: { value: timestamp, color: "90" },
|
||||
level: { value: "[WARN]", color: "33" },
|
||||
filename: { value: `(${filename})`, color: "36" },
|
||||
message: { value: joinedMessage, color: "0" },
|
||||
};
|
||||
|
||||
this.writeToLog(`${timestamp} [WARN] (${filename}) ${joinedMessage}`);
|
||||
this.writeConsoleMessageColored(logMessageParts, breakLine);
|
||||
}
|
||||
|
||||
public error(
|
||||
message: string | string[] | Error | Error[],
|
||||
breakLine: boolean = false,
|
||||
): void {
|
||||
const stack: string = new Error().stack || "";
|
||||
const { filename, timestamp } = this.getCallerInfo(stack);
|
||||
|
||||
const messages: (string | Error)[] = Array.isArray(message)
|
||||
? message
|
||||
: [message];
|
||||
const joinedMessage: string = messages
|
||||
.map((msg: string | Error): string =>
|
||||
typeof msg === "string" ? msg : msg.message,
|
||||
)
|
||||
.join(" ");
|
||||
|
||||
const logMessageParts: ILogMessageParts = {
|
||||
readableTimestamp: { value: timestamp, color: "90" },
|
||||
level: { value: "[ERROR]", color: "31" },
|
||||
filename: { value: `(${filename})`, color: "36" },
|
||||
message: { value: joinedMessage, color: "0" },
|
||||
};
|
||||
|
||||
this.writeToLog(`${timestamp} [ERROR] (${filename}) ${joinedMessage}`);
|
||||
this.writeConsoleMessageColored(logMessageParts, breakLine);
|
||||
}
|
||||
|
||||
private writeConsoleMessageColored(
|
||||
logMessageParts: ILogMessageParts,
|
||||
breakLine: boolean = false,
|
||||
): void {
|
||||
const logMessage: string = Object.keys(logMessageParts)
|
||||
.map((key: string) => {
|
||||
const part: ILogMessagePart = logMessageParts[key];
|
||||
return `\x1b[${part.color}m${part.value}\x1b[0m`;
|
||||
})
|
||||
.join(" ");
|
||||
console.log(logMessage + (breakLine ? EOL : ""));
|
||||
}
|
||||
}
|
||||
|
||||
const logger: Logger = Logger.getInstance();
|
||||
export { logger };
|
19
src/index.ts
Normal file
19
src/index.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { redis } from "@database/redis";
|
||||
import { logger } from "@helpers/logger";
|
||||
|
||||
import { serverHandler } from "./server";
|
||||
|
||||
async function main(): Promise<void> {
|
||||
try {
|
||||
await redis.initialize();
|
||||
serverHandler.initialize();
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error: Error) => {
|
||||
logger.error("Error initializing the server:");
|
||||
logger.error(error as Error);
|
||||
process.exit(1);
|
||||
});
|
146
src/routes/[booru]/[id].ts
Normal file
146
src/routes/[booru]/[id].ts
Normal file
|
@ -0,0 +1,146 @@
|
|||
import { determineBooru } from "@helpers/char";
|
||||
import { fetch } from "bun";
|
||||
|
||||
import { redis } from "@/database/redis";
|
||||
|
||||
const routeDef: RouteDef = {
|
||||
method: "GET",
|
||||
accepts: "*/*",
|
||||
returns: "application/json",
|
||||
};
|
||||
|
||||
async function handler(
|
||||
request: Request,
|
||||
server: BunServer,
|
||||
requestBody: unknown,
|
||||
query: Query,
|
||||
params: Params,
|
||||
): Promise<Response> {
|
||||
const { booru, id } = params as { booru: string; id: string };
|
||||
|
||||
if (!booru || !id) {
|
||||
return Response.json(
|
||||
{
|
||||
success: false,
|
||||
code: 400,
|
||||
error: "Missing booru or id",
|
||||
},
|
||||
{
|
||||
status: 400,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const booruConfig: IBooruConfig | null = determineBooru(booru);
|
||||
|
||||
if (!booruConfig) {
|
||||
return Response.json(
|
||||
{
|
||||
success: false,
|
||||
code: 404,
|
||||
error: "Booru not found",
|
||||
},
|
||||
{
|
||||
status: 404,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (!booruConfig.enabled) {
|
||||
return Response.json(
|
||||
{
|
||||
success: false,
|
||||
code: 403,
|
||||
error: "Booru is disabled",
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const funcString: string | [string, string] = booruConfig.functions.id;
|
||||
let url: string = `https://${booruConfig.endpoint}/${booruConfig.functions.id}${id}`;
|
||||
|
||||
if (Array.isArray(funcString)) {
|
||||
const [start, end] = funcString;
|
||||
|
||||
url = `https://${booruConfig.endpoint}/${start}${id}${end}`;
|
||||
}
|
||||
|
||||
const cacheKey: string = `${booru}:${id}`;
|
||||
const cacheData: unknown = await redis.getInstance().get("JSON", cacheKey);
|
||||
|
||||
if (cacheData) {
|
||||
return Response.json(
|
||||
{
|
||||
success: true,
|
||||
code: 200,
|
||||
cache: true,
|
||||
data: cacheData,
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const response: Response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
return Response.json(
|
||||
{
|
||||
success: false,
|
||||
code: response.status || 500,
|
||||
error: response.statusText || "Could not reach booru",
|
||||
},
|
||||
{
|
||||
status: response.status || 500,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const data: unknown = await response.json();
|
||||
|
||||
if (!data) {
|
||||
return Response.json(
|
||||
{
|
||||
success: false,
|
||||
code: 404,
|
||||
error: "Post not found",
|
||||
},
|
||||
{
|
||||
status: 404,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// let keyString = Array.isArray(data) ? "posts" : "post";
|
||||
|
||||
return Response.json(
|
||||
{
|
||||
success: true,
|
||||
code: 200,
|
||||
cache: false,
|
||||
data,
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
},
|
||||
);
|
||||
} catch {
|
||||
return Response.json(
|
||||
{
|
||||
success: false,
|
||||
code: 500,
|
||||
error: "Internal Server Error",
|
||||
},
|
||||
{
|
||||
status: 500,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { handler, routeDef };
|
15
src/routes/index.ts
Normal file
15
src/routes/index.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
const routeDef: RouteDef = {
|
||||
method: "GET",
|
||||
accepts: "*/*",
|
||||
returns: "text/html",
|
||||
};
|
||||
|
||||
async function handler(): Promise<Response> {
|
||||
return new Response("Hello, World!", {
|
||||
headers: {
|
||||
"content-type": "text/html",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export { handler, routeDef };
|
160
src/server.ts
Normal file
160
src/server.ts
Normal file
|
@ -0,0 +1,160 @@
|
|||
import { environment } from "@config/environment";
|
||||
import { logger } from "@helpers/logger";
|
||||
import { FileSystemRouter, type MatchedRoute, type Serve } from "bun";
|
||||
|
||||
class ServerHandler {
|
||||
private router: FileSystemRouter;
|
||||
|
||||
constructor(
|
||||
private port: number,
|
||||
private host: string,
|
||||
) {
|
||||
this.router = new FileSystemRouter({
|
||||
style: "nextjs",
|
||||
dir: "./src/routes",
|
||||
origin: `http://${this.host}:${this.port}`,
|
||||
});
|
||||
}
|
||||
|
||||
public initialize(): void {
|
||||
const server: Serve = Bun.serve({
|
||||
port: this.port,
|
||||
hostname: this.host,
|
||||
fetch: this.handleRequest.bind(this),
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`Server running at http://${server.hostname}:${server.port}`,
|
||||
true,
|
||||
);
|
||||
|
||||
this.logRoutes();
|
||||
}
|
||||
|
||||
private logRoutes(): void {
|
||||
logger.info("Available routes:");
|
||||
|
||||
const sortedRoutes: [string, string][] = Object.entries(
|
||||
this.router.routes,
|
||||
).sort(([pathA]: [string, string], [pathB]: [string, string]) =>
|
||||
pathA.localeCompare(pathB),
|
||||
);
|
||||
|
||||
for (const [path, filePath] of sortedRoutes) {
|
||||
logger.info(`Route: ${path}, File: ${filePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleRequest(
|
||||
request: Request,
|
||||
server: BunServer,
|
||||
): Promise<Response> {
|
||||
const match: MatchedRoute | null = this.router.match(request);
|
||||
let requestBody: unknown = {};
|
||||
let response: Response;
|
||||
|
||||
if (match) {
|
||||
const { filePath, params, query } = match;
|
||||
|
||||
try {
|
||||
const routeModule: RouteModule = await import(filePath);
|
||||
const contentType: string | null =
|
||||
request.headers.get("Content-Type");
|
||||
const actualContentType: string | null = contentType
|
||||
? contentType.split(";")[0].trim()
|
||||
: null;
|
||||
|
||||
if (
|
||||
routeModule.routeDef.needsBody === "json" &&
|
||||
actualContentType === "application/json"
|
||||
) {
|
||||
try {
|
||||
requestBody = await request.json();
|
||||
} catch {
|
||||
requestBody = {};
|
||||
}
|
||||
} else if (
|
||||
routeModule.routeDef.needsBody === "multipart" &&
|
||||
actualContentType === "multipart/form-data"
|
||||
) {
|
||||
try {
|
||||
requestBody = await request.formData();
|
||||
} catch {
|
||||
requestBody = {};
|
||||
}
|
||||
}
|
||||
|
||||
if (routeModule.routeDef.method !== request.method) {
|
||||
response = Response.json(
|
||||
{
|
||||
success: false,
|
||||
code: 405,
|
||||
error: `Method ${request.method} Not Allowed`,
|
||||
},
|
||||
{ status: 405 },
|
||||
);
|
||||
} else {
|
||||
const expectedContentType: string | null =
|
||||
routeModule.routeDef.accepts;
|
||||
|
||||
const matchesAccepts: boolean =
|
||||
expectedContentType === "*/*" ||
|
||||
actualContentType === expectedContentType;
|
||||
|
||||
if (!matchesAccepts) {
|
||||
response = Response.json(
|
||||
{
|
||||
success: false,
|
||||
code: 406,
|
||||
error: `Content-Type ${contentType} Not Acceptable`,
|
||||
},
|
||||
{ status: 406 },
|
||||
);
|
||||
} else {
|
||||
response = await routeModule.handler(
|
||||
request,
|
||||
server,
|
||||
requestBody,
|
||||
query,
|
||||
params,
|
||||
);
|
||||
|
||||
response.headers.set(
|
||||
"Content-Type",
|
||||
routeModule.routeDef.returns,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
logger.error(`Error handling route ${request.url}:`);
|
||||
logger.error(error as Error);
|
||||
|
||||
response = Response.json(
|
||||
{
|
||||
success: false,
|
||||
code: 500,
|
||||
error: "Internal Server Error",
|
||||
},
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
} else {
|
||||
response = Response.json(
|
||||
{
|
||||
success: false,
|
||||
code: 404,
|
||||
error: "Not Found",
|
||||
},
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
const serverHandler: ServerHandler = new ServerHandler(
|
||||
environment.port,
|
||||
environment.host,
|
||||
);
|
||||
|
||||
export { serverHandler };
|
Loading…
Add table
Add a link
Reference in a new issue