113 lines
3.0 KiB
TypeScript
113 lines
3.0 KiB
TypeScript
import "dotenv/config";
|
|
import { drizzle } from "drizzle-orm/libsql";
|
|
import { eq } from "drizzle-orm";
|
|
import { createClient } from "@libsql/client";
|
|
import { SignJWT } from "jose";
|
|
|
|
import { Hono } from "@hono/hono";
|
|
import { commonResponse } from "./utils/response.ts";
|
|
import { hashPassword, verifyPassword } from "./utils/crypto.ts";
|
|
import { metasTable } from "./db/schema.ts";
|
|
|
|
const client = createClient({
|
|
url: `file:${process.env.DATABASE_PATH!}`,
|
|
});
|
|
const db = drizzle(client);
|
|
|
|
const JWT_SECRET = crypto.getRandomValues(new Uint8Array(32));
|
|
|
|
async function initDefaultUser() {
|
|
const row = await db
|
|
.select()
|
|
.from(metasTable)
|
|
.where(eq(metasTable.key, "userinfo_username"))
|
|
.get();
|
|
if (row) return;
|
|
|
|
const usernameHash = await hashPassword("ah");
|
|
const passwordHash = await hashPassword("123456");
|
|
|
|
await db
|
|
.insert(metasTable)
|
|
.values([
|
|
{ key: "userinfo_username", value: usernameHash },
|
|
{ key: "userinfo_password", value: passwordHash },
|
|
])
|
|
.run();
|
|
console.log("Default user created (username: ah, password: 123456)");
|
|
}
|
|
|
|
await initDefaultUser();
|
|
|
|
const app = new Hono();
|
|
|
|
app.get("/", (c) => {
|
|
return commonResponse(c, true, 200, { "hint": "Hello! But nothing here." });
|
|
});
|
|
app.get("/ping", (c) => {
|
|
return commonResponse(c, true, 200, {});
|
|
});
|
|
|
|
app.post("/authenticate", async (c) => {
|
|
const body = await c.req.json<{ username?: string; password?: string }>();
|
|
if (!body.username || !body.password) {
|
|
return commonResponse(
|
|
c,
|
|
false,
|
|
400,
|
|
{},
|
|
"Missing username or password",
|
|
);
|
|
}
|
|
|
|
const storedUsername = await db
|
|
.select()
|
|
.from(metasTable)
|
|
.where(eq(metasTable.key, "userinfo_username"))
|
|
.get();
|
|
const storedPassword = await db
|
|
.select()
|
|
.from(metasTable)
|
|
.where(eq(metasTable.key, "userinfo_password"))
|
|
.get();
|
|
|
|
if (!storedUsername?.value || !storedPassword?.value) {
|
|
return commonResponse(c, false, 500, {}, "User not configured");
|
|
}
|
|
|
|
const usernameValid = await verifyPassword(
|
|
body.username,
|
|
storedUsername.value,
|
|
);
|
|
if (!usernameValid) {
|
|
return commonResponse(c, false, 401, {}, "Invalid credentials");
|
|
}
|
|
|
|
const passwordValid = await verifyPassword(
|
|
body.password,
|
|
storedPassword.value,
|
|
);
|
|
if (!passwordValid) {
|
|
return commonResponse(c, false, 401, {}, "Invalid credentials");
|
|
}
|
|
|
|
const jwt = await new SignJWT({ username: body.username })
|
|
.setProtectedHeader({ alg: "HS256" })
|
|
.setIssuedAt()
|
|
.setExpirationTime("24h")
|
|
.sign(JWT_SECRET);
|
|
|
|
return commonResponse(c, true, 200, { token: jwt });
|
|
});
|
|
|
|
Deno.serve(app.fetch);
|
|
|
|
const handleExit = () => {
|
|
console.log("Closing database connection...");
|
|
db.$client.close();
|
|
console.log("Disconnected.");
|
|
process.exit(0);
|
|
};
|
|
process.on("SIGINT", handleExit);
|
|
process.on("SIGTERM", handleExit);
|