ls-deno/routes/login.tsx

99 lines
2.8 KiB
TypeScript

import { HandlerContext, Handlers, PageProps } from "$fresh/server.ts";
import { compare } from "https://deno.land/x/bcrypt@v0.4.1/mod.ts";
import { createToken, getUser } from "@/db/mod.ts";
import * as base64 from "$std/encoding/base64.ts";
import { setCookie } from "$std/http/cookie.ts";
type UserID = string;
interface LoginError {
message: string;
}
async function invalidLogin<LoginError>(context: HandlerContext<LoginError>) {
return await context.render({ message: "Invalid login" } as LoginError);
}
export const handler: Handlers<UserID | LoginError | null> = {
async POST(request: Request, context) {
const formData = (await request.formData());
const username = formData.get("username");
const password = formData.get("password");
if (!username) {
return await context.render({ message: "no username provided" });
}
if (!password) {
return await context.render({ message: "no password provided" });
}
const user = await getUser({ username: username.toString() });
if (!user) {
return await invalidLogin(context);
}
if (!await compare(password.toString(), user.passwordDigest)) {
return await invalidLogin(context);
}
const token = await createToken({
userId: user.id,
data: { type: "login" },
});
if (!token || !token.bytes) throw "failed to create token";
const cookie = base64.encode(token.bytes);
const newUrl = new URL(request.url);
newUrl.pathname = "/dashboard";
const response = await context.render(user.id);
const headers = new Headers(response.headers);
setCookie(headers, { name: "lsauth", value: cookie });
headers.set("location", newUrl.toString());
const actualResponse = new Response(response.body, {
...response,
headers: headers,
status: 302,
});
// response.headers.set("location", newUrl.toString());
// response.status = 302;
return actualResponse;
},
};
export default function Login({ data }: PageProps) {
if (typeof data == "string") {
return LoginSuccessful(data);
} else {
return LoginForm(data);
}
}
function LoginSuccessful(_userId: UserID) {
return (
<p>
You are now logged in. Let's go to your{" "}
<a href="/dashboard">dashboard</a>!
</p>
);
}
function LoginForm(props?: LoginError | null) {
return (
<form class="flex flex-col max-w-lg" method="post">
{props != null &&
(
<p class="text-red-500">
<strong>Error</strong>: {props.message}
</p>
)}
<h1 class="text-4xl mb-4 outline-white">
Log in to your account
</h1>
<label for="username">Username</label>
<input type="text" name="username" />
<label for="password">Password</label>
<input type="password" name="password" />
<input class="mt-2" type="submit" value="Login" />
</form>
);
}