login working
This commit is contained in:
parent
a0885013bb
commit
c2f36e9723
8 changed files with 278 additions and 51 deletions
15
README.md
15
README.md
|
|
@ -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
BIN
bun.lockb
Binary file not shown.
13
package.json
13
package.json
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
155
src/index.ts
155
src/index.ts
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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
40
src/pages/login.html
Normal 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
36
src/pages/register.html
Normal 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>
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue