diff --git a/README.md b/README.md deleted file mode 100644 index 688c87e..0000000 --- a/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Elysia with Bun runtime - -## Getting Started -To get started with this template, simply paste this command into your terminal: -```bash -bun create elysia ./elysia-example -``` - -## Development -To start the development server run: -```bash -bun run dev -``` - -Open http://localhost:3000/ with your browser to see the result. \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index 501b456..268fe52 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index f573fa1..76c60fc 100644 --- a/package.json +++ b/package.json @@ -6,16 +6,19 @@ "dev": "bun run --hot src/index.ts" }, "dependencies": { + "@elysiajs/cookie": "^0.8.0", "@elysiajs/html": "^1.0.2", + "@elysiajs/jwt": "^1.0.2", "@elysiajs/static": "^1.0.2", - "elysia": "latest", - "nanoid": "^5.0.7" - }, - "devDependencies": { - "bun-types": "latest" + "elysia": "latest" }, "module": "src/index.js", "bun-create": { "start": "bun run src/index.ts" + }, + "devDependencies": { + "@types/bun": "^1.1.2", + "@types/node": "^20.12.12", + "bun-types": "latest" } } diff --git a/src/index.ts b/src/index.ts index ca7096d..81ca231 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,45 @@ -import { Elysia } from "elysia"; +import { Elysia, t } from "elysia"; import { staticPlugin } from "@elysiajs/static"; import { html } from "@elysiajs/html"; import { Database } from "bun:sqlite"; +import cookie from "@elysiajs/cookie"; +import { unlink } from "node:fs/promises"; +import { randomUUID } from "node:crypto"; +import { jwt } from "@elysiajs/jwt"; +// import { Lucia } from "lucia"; +// import { BunSQLiteAdapter } from "@lucia-auth/adapter-sqlite"; const db = new Database("./mydb.sqlite"); -const baseDir = import.meta.dir; const uploadsDir = "./uploads/"; +// init db +db.exec(` +CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + email TEXT NOT NULL, + password TEXT NOT NULL +); +`); + +const basicAuthModel = new Elysia().model({ + basicAuthModel: t.Object({ + email: t.String(), + password: t.String(), + }), +}); + const app = new Elysia() + .use(cookie()) + .use( + jwt({ + name: "jwt", + schema: t.Object({ + id: t.String(), + }), + secret: "secret", + exp: "7d", + }), + ) .use(html()) .use( staticPlugin({ @@ -15,14 +47,126 @@ const app = new Elysia() prefix: "/", }), ) - .get("/", () => Bun.file("src/pages/index.html")) + .get("/register", async () => { + return Bun.file("src/pages/register.html"); + }) + .post( + "/register", + async function handler({ body, set, jwt, cookie: { auth } }) { + const existingUser = await db + .query("SELECT * FROM users WHERE email = ?") + .get(body.email); + if (existingUser) { + set.status = 400; + return { + message: "Email already in use.", + }; + } + const savedPassword = await Bun.password.hash(body.password); + + db.run( + "INSERT INTO users (email, password) VALUES (?, ?)", + body.email, + savedPassword, + ); + + const user = await db + .query("SELECT * FROM users WHERE email = ?") + .get(body.email); + + const accessToken = await jwt.sign({ + id: String(user.id), + }); + + // set cookie + auth.set({ + value: accessToken, + httpOnly: true, + secure: true, + maxAge: 60 * 60 * 24 * 7, + sameSite: "strict", + }); + + // redirect to home + set.status = 302; + set.headers = { + Location: "/", + }; + }, + ) + .get("/login", async () => { + return Bun.file("src/pages/login.html"); + }) + .post("/login", async function handler({ body, set, jwt, cookie: { auth } }) { + const existingUser = await db + .query("SELECT * FROM users WHERE email = ?") + .get(body.email); + + if (!existingUser) { + set.status = 403; + return { + message: "Invalid credentials.", + }; + } + + const validPassword = await Bun.password.verify( + body.password, + existingUser.password, + ); + + if (!validPassword) { + set.status = 403; + return { + message: "Invalid credentials.", + }; + } + + const accessToken = await jwt.sign({ + id: String(existingUser.id), + }); + + // set cookie + // set cookie + auth.set({ + value: accessToken, + httpOnly: true, + secure: true, + maxAge: 60 * 60 * 24 * 7, + sameSite: "strict", + }); + + // redirect to home + set.status = 302; + set.headers = { + Location: "/", + }; + }) + .post("/logout", async ({ set, cookie: { auth } }) => { + auth.remove(); + set.status = 302; + set.headers = { + Location: "/login", + }; + }) + .get("/", async ({ jwt, set, cookie: { auth } }) => { + // validate jwt + const user = await jwt.verify(auth.value); + if (!user) { + // redirect to login + set.status = 302; + set.headers = { + Location: "/login", + }; + return; + } + return Bun.file("src/pages/index.html"); + }) .post("/upload", async (ctx) => { console.log(ctx.body); if (ctx.body?.file) { await Bun.write(`${uploadsDir}${ctx.body.file.name}`, ctx.body.file); } else if (ctx.body?.files) { if (Array.isArray(ctx.body.files)) { - console.log("Found array of files"); for (const file of ctx.body.files) { console.log(file); await Bun.write(`${uploadsDir}${file.name}`, file); @@ -32,6 +176,9 @@ const app = new Elysia() } } }) + .post("/delete/:file", async (ctx) => { + await unlink(`${uploadsDir}${ctx.params.file}`); + }) .listen(3000); console.log( diff --git a/src/pages/index.html b/src/pages/index.html index de49eb3..d00bac6 100644 --- a/src/pages/index.html +++ b/src/pages/index.html @@ -7,14 +7,14 @@