login working

This commit is contained in:
C4illin 2024-05-18 00:16:08 +02:00
parent a0885013bb
commit c2f36e9723
8 changed files with 278 additions and 51 deletions

View file

@ -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.

BIN
bun.lockb

Binary file not shown.

View file

@ -6,16 +6,19 @@
"dev": "bun run --hot src/index.ts" "dev": "bun run --hot src/index.ts"
}, },
"dependencies": { "dependencies": {
"@elysiajs/cookie": "^0.8.0",
"@elysiajs/html": "^1.0.2", "@elysiajs/html": "^1.0.2",
"@elysiajs/jwt": "^1.0.2",
"@elysiajs/static": "^1.0.2", "@elysiajs/static": "^1.0.2",
"elysia": "latest", "elysia": "latest"
"nanoid": "^5.0.7"
},
"devDependencies": {
"bun-types": "latest"
}, },
"module": "src/index.js", "module": "src/index.js",
"bun-create": { "bun-create": {
"start": "bun run src/index.ts" "start": "bun run src/index.ts"
},
"devDependencies": {
"@types/bun": "^1.1.2",
"@types/node": "^20.12.12",
"bun-types": "latest"
} }
} }

View file

@ -1,13 +1,45 @@
import { Elysia } from "elysia"; import { Elysia, t } from "elysia";
import { staticPlugin } from "@elysiajs/static"; import { staticPlugin } from "@elysiajs/static";
import { html } from "@elysiajs/html"; import { html } from "@elysiajs/html";
import { Database } from "bun:sqlite"; 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 db = new Database("./mydb.sqlite");
const baseDir = import.meta.dir;
const uploadsDir = "./uploads/"; 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() const app = new Elysia()
.use(cookie())
.use(
jwt({
name: "jwt",
schema: t.Object({
id: t.String(),
}),
secret: "secret",
exp: "7d",
}),
)
.use(html()) .use(html())
.use( .use(
staticPlugin({ staticPlugin({
@ -15,14 +47,126 @@ const app = new Elysia()
prefix: "/", 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) => { .post("/upload", async (ctx) => {
console.log(ctx.body); console.log(ctx.body);
if (ctx.body?.file) { if (ctx.body?.file) {
await Bun.write(`${uploadsDir}${ctx.body.file.name}`, ctx.body.file); await Bun.write(`${uploadsDir}${ctx.body.file.name}`, ctx.body.file);
} else if (ctx.body?.files) { } else if (ctx.body?.files) {
if (Array.isArray(ctx.body.files)) { if (Array.isArray(ctx.body.files)) {
console.log("Found array of files");
for (const file of ctx.body.files) { for (const file of ctx.body.files) {
console.log(file); console.log(file);
await Bun.write(`${uploadsDir}${file.name}`, 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); .listen(3000);
console.log( console.log(

View file

@ -7,14 +7,14 @@
<title>ConvertX</title> <title>ConvertX</title>
<link rel="stylesheet" href="pico.lime.min.css"> <link rel="stylesheet" href="pico.lime.min.css">
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
<script src="index.js" defer></script> <script src="script.js" defer></script>
</head> </head>
<body> <body>
<header class="container-fluid"> <header class="container-fluid">
<nav> <nav>
<ul> <ul>
<li><strong>ConvertX</strong></li> <li><a href="/">ConvertX</a></strong></li>
</ul> </ul>
<ul> <ul>
<li><a href="#">About</a></li> <li><a href="#">About</a></li>
@ -27,30 +27,29 @@
<main class="container-fluid"> <main class="container-fluid">
<!-- File upload --> <!-- File upload -->
<form method="post" action="upload" enctype="multipart/form-data">
<div>
<article>
<table id="file-list">
</table>
<input type="file" name="file" multiple />
</article>
<!-- <div class="icon">></div> --> <article>
<article> <table id="file-list">
<select name="to" aria-label="Convert to" required> </table>
<option selected disabled value="">Convert to</option> <input type="file" name="file" multiple />
<option>JPG</option>
<option>PNG</option> </article>
<option>SVG</option> <!-- <div class="icon">></div> -->
<option>PDF</option> <form method="post"></form>
<option>DOCX</option> <article>
</select> <select name="to" aria-label="Convert to" required>
</article> <option selected disabled value="">Convert to</option>
</div> <option>JPG</option>
<div class="center"> <option>PNG</option>
<input type="submit" value="Convert"> <option>SVG</option>
<!-- <button type="submit">Convert</button> --> <option>PDF</option>
</div> <option>DOCX</option>
</select>
</article>
<input type="submit" value="Convert">
<!-- <button type="submit">Convert</button> -->
<!-- </div> -->
</form> </form>
</main> </main>
<footer></footer> <footer></footer>

40
src/pages/login.html Normal file
View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ConvertX | Login</title>
<link rel="stylesheet" href="pico.lime.min.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="container-fluid">
<nav>
<ul>
<li><a href="/">ConvertX</a></strong></li>
</ul>
<ul>
<li><a href="#">About</a></li>
<li><a href="#">Services</a></li>
<li><button class="secondary">Products</button></li>
</ul>
</nav>
</header>
<main class="container-fluid">
<form method="post">
<input type="email" name="email" placeholder="Email" required>
<input type="password" name="password" placeholder="Password" required>
<div role="group">
<a href="/register" role="button" class="secondary">Register an account</a>
<input type="submit" value="Login">
</div>
</form>
</main>
</body>
</html>

36
src/pages/register.html Normal file
View file

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ConvertX | Register</title>
<link rel="stylesheet" href="pico.lime.min.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="container-fluid">
<nav>
<ul>
<li><a href="/">ConvertX</a></strong></li>
</ul>
<ul>
<li><a href="#">About</a></li>
<li><a href="#">Services</a></li>
<li><button class="secondary">Products</button></li>
</ul>
</nav>
</header>
<main class="container-fluid">
<form method="post">
<input type="email" name="email" placeholder="Email" required>
<input type="password" name="password" placeholder="Password" required>
<input type="submit" value="Register">
</form>
</main>
</body>
</html>

View file

@ -1,7 +1,7 @@
// Select the file input element // Select the file input element
const fileInput = document.querySelector('input[type="file"]'); const fileInput = document.querySelector('input[type="file"]');
let filesToUpload = []; const filesToUpload = [];
// Add a 'change' event listener to the file input element // Add a 'change' event listener to the file input element
fileInput.addEventListener("change", (e) => { fileInput.addEventListener("change", (e) => {
@ -19,7 +19,7 @@ fileInput.addEventListener("change", (e) => {
row.innerHTML = ` row.innerHTML = `
<td>${files[i].name}</td> <td>${files[i].name}</td>
<td>${(files[i].size / 1024 / 1024).toFixed(2)} MB</td> <td>${(files[i].size / 1024 / 1024).toFixed(2)} MB</td>
<td><button class="secondary">x</button></td> <td><button class="secondary" onclick="deleteRow(this)">x</button></td>
`; `;
// Append the row to the file-list table // Append the row to the file-list table
@ -29,6 +29,23 @@ fileInput.addEventListener("change", (e) => {
uploadFiles(files); uploadFiles(files);
}); });
// Add a onclick for the delete button
const deleteRow = (target) => {
const fileName = target.parentElement.parentElement.children[0].textContent;
const row = target.parentElement.parentElement;
row.remove();
fetch("/delete", {
method: "POST",
body: JSON.stringify({ fileName }),
})
.then((res) => res.json())
.then((data) => {
console.log(data);
})
.catch((err) => console.log(err));
};
const uploadFiles = (files) => { const uploadFiles = (files) => {
const formData = new FormData(); const formData = new FormData();