basic auth
parent
0b1c246fe2
commit
32f03a452b
@ -1,6 +1,9 @@
|
||||
import Elysia from "elysia";
|
||||
import { todosController } from "./todos";
|
||||
import { authController } from "./auth";
|
||||
|
||||
export const api = new Elysia({
|
||||
prefix: "/api",
|
||||
}).use(todosController);
|
||||
})
|
||||
.use(todosController)
|
||||
.use(authController);
|
||||
|
@ -1,5 +1,75 @@
|
||||
import { Elysia } from "elysia";
|
||||
import { Elysia, t } from "elysia";
|
||||
import { ctx } from "../context";
|
||||
import { set } from "zod";
|
||||
|
||||
class DuplicateEmailError extends Error {
|
||||
constructor() {
|
||||
super("Duplicate email");
|
||||
}
|
||||
}
|
||||
|
||||
export const authController = new Elysia({
|
||||
prefix: "/auth",
|
||||
}).post("/signup", "signup");
|
||||
})
|
||||
.use(ctx)
|
||||
.post(
|
||||
"/signup",
|
||||
async ({ body: { email, password }, auth, set }) => {
|
||||
const user = await auth
|
||||
.createUser({
|
||||
key: {
|
||||
providerId: "email", // auth method
|
||||
providerUserId: email.toLowerCase(), // unique id when using "email" auth method
|
||||
password, // hashed by Lucia
|
||||
},
|
||||
attributes: {
|
||||
email,
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.code === "SQLITE_CONSTRAINT") {
|
||||
throw new DuplicateEmailError();
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
const session = await auth.createSession({
|
||||
userId: user.userId,
|
||||
attributes: {},
|
||||
});
|
||||
const sessionCookie = auth.createSessionCookie(session);
|
||||
|
||||
set.headers["Set-Cookie"] = sessionCookie.serialize();
|
||||
set.redirect = "/profile";
|
||||
},
|
||||
{
|
||||
body: t.Object({
|
||||
email: t.String({
|
||||
minLength: 5,
|
||||
maxLength: 30,
|
||||
}),
|
||||
password: t.String({
|
||||
minLength: 6,
|
||||
maxLength: 255,
|
||||
}),
|
||||
}),
|
||||
error({ code, error, set }) {
|
||||
if (code === "VALIDATION") {
|
||||
console.log("sign up validation error");
|
||||
console.log(error);
|
||||
set.status = 400;
|
||||
return "Invalid email or password";
|
||||
} else if (error instanceof DuplicateEmailError) {
|
||||
console.log("sign up duplicate email error");
|
||||
console.log(error);
|
||||
set.status = 400;
|
||||
return "Email already exists";
|
||||
} else {
|
||||
console.log("sign up error");
|
||||
console.log(error);
|
||||
set.status = 500;
|
||||
return "Internal server error";
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
|
@ -0,0 +1,5 @@
|
||||
import Elysia from "elysia";
|
||||
import { signup } from "./signup";
|
||||
import { profile } from "./profile";
|
||||
|
||||
export const authGroup = new Elysia().use(signup).use(profile);
|
@ -0,0 +1,16 @@
|
||||
import Elysia from "elysia";
|
||||
import { BaseHtml } from "../../components/base";
|
||||
import { ctx } from "../../context";
|
||||
|
||||
export const profile = new Elysia()
|
||||
.use(ctx)
|
||||
.get("/profile", async ({ auth, html, request }) => {
|
||||
const authRequest = auth.handleRequest(request);
|
||||
|
||||
const session = await authRequest.validate();
|
||||
if (session) {
|
||||
return html(<div>Hello {session.user.email}</div>);
|
||||
} else {
|
||||
return html(<div>Not logged in</div>);
|
||||
}
|
||||
});
|
@ -0,0 +1,54 @@
|
||||
import Elysia from "elysia";
|
||||
import { BaseHtml } from "../../components/base";
|
||||
import { ctx } from "../../context";
|
||||
|
||||
export const signup = new Elysia().use(ctx).get("/signup", ({ html }) =>
|
||||
html(
|
||||
<BaseHtml>
|
||||
<div class="flex w-full h-screen bg-gray-200 justify-center items-center">
|
||||
<form
|
||||
hx-post="/api/auth/signup"
|
||||
hx-swap="afterend"
|
||||
class="bg-white p-8 rounded-lg shadow-md w-96"
|
||||
>
|
||||
<div class="mb-4">
|
||||
<label
|
||||
for="email"
|
||||
class="block text-sm font-medium text-gray-600 mb-2"
|
||||
>
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="email"
|
||||
id="email"
|
||||
placeholder="Enter your email"
|
||||
class="w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label
|
||||
for="password"
|
||||
class="block text-sm font-medium text-gray-600 mb-2"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
id="password"
|
||||
placeholder="Enter your password"
|
||||
class="w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full bg-indigo-600 text-white p-2 rounded-md hover:bg-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:ring-opacity-50"
|
||||
>
|
||||
Sign Up
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</BaseHtml>
|
||||
)
|
||||
);
|
@ -1,4 +1,5 @@
|
||||
import Elysia from "elysia";
|
||||
import { index } from "./index";
|
||||
import { authGroup } from "./(auth)/*";
|
||||
|
||||
export const pages = new Elysia().use(index);
|
||||
export const pages = new Elysia().use(index).use(authGroup);
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue