122 lines
3.1 KiB
TypeScript
122 lines
3.1 KiB
TypeScript
import HttpRouter from "@lib/router.ts";
|
|
import { Eta } from "@eta-dev/eta";
|
|
import { serveFile } from "jsr:@std/http/file-server";
|
|
import rateLimitMiddleware from "@src/middleware/rateLimiter.ts";
|
|
import authMiddleware from "@src/middleware/auth.ts";
|
|
import loggerMiddleware from "@src/middleware/logger.ts";
|
|
import { SchemaValidationError, z } from "@shared/utils/validator.ts";
|
|
import { Api } from "@src/lib/apiValidator.ts";
|
|
import { loginApi } from "./api.ts";
|
|
import { err, getMessageFromError, ok } from "@shared/utils/result.ts";
|
|
import admin from "@src/lib/admin.ts";
|
|
import {
|
|
FailedToParseRequestAsJSON,
|
|
QueryExecutionError,
|
|
} from "@src/lib/errors.ts";
|
|
import { Context } from "@src/lib/context.ts";
|
|
|
|
const AUTH_COOKIE_NAME = "token";
|
|
|
|
const router = new HttpRouter();
|
|
|
|
const views = Deno.cwd() + "/views/";
|
|
const eta = new Eta({ views });
|
|
|
|
router.use(loggerMiddleware);
|
|
router.use(rateLimitMiddleware);
|
|
router.use(authMiddleware);
|
|
|
|
const cache: Map<string, Response> = new Map();
|
|
|
|
router.get("/public/*", async (c) => {
|
|
const filePath = "." + c.path;
|
|
|
|
//const cached = cache.get(filePath);
|
|
//
|
|
//if (cached) {
|
|
// return cached.clone();
|
|
//}
|
|
|
|
const res = await serveFile(c.req, filePath);
|
|
|
|
//cache.set(filePath, res.clone());
|
|
|
|
return res;
|
|
});
|
|
|
|
router
|
|
.get(["", "/index.html"], (c) => {
|
|
return c.html(eta.render("./index.html", {}));
|
|
})
|
|
.get(["/login", "/login.html"], (c) => {
|
|
return c.html(eta.render("./login.html", {}));
|
|
});
|
|
|
|
const schema = {
|
|
req: z.obj({
|
|
password: z.string().max(1024),
|
|
}),
|
|
res: z.result(z.void(), z.string()),
|
|
};
|
|
|
|
router.api(loginApi, async (c) => {
|
|
const r = await c
|
|
.parseBody()
|
|
.andThenAsync(
|
|
({ password }) => admin.verifyPassword(password),
|
|
);
|
|
|
|
if (r.isErr()) {
|
|
if (r.error.type === "AdminPasswordNotSetError") {
|
|
return c.json(
|
|
err({
|
|
type: r.error.type,
|
|
msg: r.error.message,
|
|
}),
|
|
);
|
|
}
|
|
return handleCommonErrors(c, r.error);
|
|
}
|
|
|
|
return admin.sessions.create()
|
|
.map(({ value, expires }) => {
|
|
c.cookies.set({
|
|
name: AUTH_COOKIE_NAME,
|
|
value,
|
|
expires,
|
|
});
|
|
return ok();
|
|
}).match(
|
|
() => c.json(ok()),
|
|
(e) => handleCommonErrors(c, e),
|
|
);
|
|
});
|
|
|
|
function handleCommonErrors(
|
|
c: Context<any, any, any>,
|
|
error:
|
|
| QueryExecutionError
|
|
| FailedToParseRequestAsJSON
|
|
| SchemaValidationError,
|
|
): Response {
|
|
switch (error.type) {
|
|
case "QueryExecutionError":
|
|
return c.json(
|
|
err(new QueryExecutionError("Server failed to execute query")),
|
|
{ status: 500 },
|
|
);
|
|
case "FailedToParseRequestAsJSON":
|
|
case "SchemaValiationError":
|
|
return c.json(
|
|
err(error),
|
|
{ status: 400 },
|
|
);
|
|
}
|
|
}
|
|
|
|
export default {
|
|
async fetch(req, connInfo) {
|
|
return await router.handleRequest(req, connInfo);
|
|
},
|
|
} satisfies Deno.ServeDefaultExport;
|