basic register

This commit is contained in:
Matteo Rosati
2026-01-26 00:36:05 +01:00
parent 54141eb91a
commit 4b93f0c7f7
12 changed files with 217 additions and 44 deletions

View File

@@ -3,17 +3,24 @@
<html>
<body>
<h1>LOGIN</h1>
<a href="/">Game</a>
<div id="login">
<p>
<a href="/">Game</a>
</p>
<form id="login">
<label for="email">Email</label>
<input type="email" name="email" id="email" /><br />
<input required type="email" name="email" id="email" />
(required)<br />
<label for="password">Password</label>
<input type="password" name="password" id="password" /><br />
<button id="login-button">Login</button>
</div>
<div>
<input required type="password" name="password" id="password" />
(required)<br />
<button type="submit" id="login-button">Login</button>
</form>
<p>
<a href="/register">Register?</a>
</div>
</p>
<script type="module" src="/src/login.ts"></script>
</body>

View File

@@ -2,7 +2,8 @@
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"email" TEXT NOT NULL,
"name" TEXT,
"password" TEXT NOT NULL,
"first_login" BOOLEAN NOT NULL,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);

View File

@@ -8,8 +8,8 @@ datasource db {
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
password String
id Int @id @default(autoincrement())
email String @unique
password String
first_login Boolean
}

View File

@@ -3,18 +3,31 @@
<html>
<body>
<h1>REGISTER</h1>
<a href="/">Game</a>
<div id="login">
<label for="email">Email</label>
<input type="email" name="user" id="email" /><br />
<label for="password">Password</label>
<input type="password" name="password" id="password" /><br />
<button id="login-button">Register</button>
</div>
<div>
<a href="/login">Login?</a>
</div>
<script type="module" src="/src/login.ts"></script>
<p>
<a href="/">Game</a>
</p>
<form id="register">
<label for="email">Email</label>
<input required type="email" name="user" id="email" />
(required)<br />
<label for="password">Password</label>
<input
required
type="password"
name="password"
id="password"
pattern=".{8,}"
/>
(required, min 8 chars)<br />
<button type="submit" id="login-button">Register</button>
</form>
<p>
<a href="/login">Login?</a>
</p>
<script type="module" src="/src/register.ts"></script>
</body>
</html>

108
server.ts
View File

@@ -1,31 +1,115 @@
import { Hono } from "hono";
import { cors } from "hono/cors";
import { upgradeWebSocket, websocket } from "hono/bun";
import { DatabaseService } from "@/services/database-service";
import { RegisterRequest, RegisterResponse } from "@/types/types";
import { MESSAGES } from "@/auth/messages";
import { validateEmail } from "@/utilities/email";
import { Prisma } from "@/orm/generated/prisma/client";
import { hashPassword } from "@/utilities/password";
const app = new Hono();
app.use(
"/api/v1/*",
cors({
origin: "*",
allowHeaders: ["X-Custom-Header", "Upgrade-Insecure-Requests"],
allowMethods: ["POST", "GET", "OPTIONS"],
exposeHeaders: ["Content-Length", "X-Kuma-Revision"],
maxAge: 600,
credentials: true,
}),
);
app.get("/", async (c) => {
const database = new DatabaseService();
await database.getClient().user.create({
data: {
email: "rosati5.matteo@gmail.com",
name: "Matteo",
},
});
const users = await database.getClient().user.findMany();
return c.json({
message: "ok",
users: users,
});
});
app.post("/api/v1/register", async (c) => {
let body: RegisterRequest | undefined;
let errors = false;
let messages: Array<string> = [];
// Get the request body and handle malformed payload
try {
body = (await c.req.json()) as RegisterRequest;
} catch (error) {
console.error(`Received invalid payload: ${error}`);
return c.json({
status: "error",
messages: [MESSAGES.INVALID_REQUEST],
} as RegisterResponse);
}
// //////////////////
// Request validation
if (!body.email) {
errors = true;
messages.push(MESSAGES.MISSING_EMAIL);
}
if (!validateEmail(body.email)) {
errors = true;
messages.push(MESSAGES.INVALID_EMAIL);
}
if (!body.password) {
errors = true;
messages.push(MESSAGES.MISSING_PASSWORD);
}
// End: Request validation
// ///////////////////////
if (errors) {
return c.json({
status: "error",
messages: messages,
} as RegisterResponse);
}
// Database
const database = new DatabaseService();
try {
// Sala la password
body.password = await hashPassword(body.password);
await database.getClient().user.create({
data: {
...body,
first_login: true,
},
});
} catch (e) {
if (
e instanceof Prisma.PrismaClientKnownRequestError &&
e.code === "P2002"
) {
return c.json({
status: "error",
messages: [MESSAGES.USER_ALREADY_EXISTS],
} as RegisterResponse);
}
return c.json({
status: "error",
messages: [MESSAGES.UNKNOWN_DATABASE_ERROR],
} as RegisterResponse);
}
return c.json({
status: "success",
} as RegisterResponse);
});
app.get(
"/ws",
upgradeWebSocket((c) => {
return {
onOpen(event, ws) {
onOpen(e, ws) {
console.log("Server: Connection opened");
ws.send("Hello!");
},

11
src/auth/messages.ts Normal file
View File

@@ -0,0 +1,11 @@
export const MESSAGES = {
MISSING_EMAIL: "The e-mail address is missing",
INVALID_EMAIL: "The e-mail address is invalid",
MISSING_PASSWORD: "The password is missing",
WRONG_PASSWORD: "Wrong password",
USER_NOT_FOUND: "User not found",
USER_CREATED: "The user has been created",
USER_ALREADY_EXISTS: "This user already exists",
INVALID_REQUEST: "Invalid request",
UNKNOWN_DATABASE_ERROR: "Il database ha dato un errore sconosciuto",
};

View File

@@ -1,14 +1,19 @@
// STYLES
// import "modern-normalize/modern-normalize.css";
// import "@fontsource-variable/jetbrains-mono";
import "modern-normalize/modern-normalize.css";
import "@fontsource-variable/jetbrains-mono";
import "@/main.css";
// import "@maptiler/sdk/dist/maptiler-sdk.css";
// LIBRARIES
// import $ from "jquery";
import $ from "jquery";
// import { FilterSpecification, Map, config } from "@maptiler/sdk";
// import { createIcons, Locate, LocateFixed } from "lucide";
// import area from "@turf/area";
// import { polygon } from "@turf/helpers";
console.log("login");
$("#login").on("submit", (e) => {
e.preventDefault();
const email = $("#email").val();
const password = $("#password").val();
console.log(email, password);
});

25
src/register.ts Normal file
View File

@@ -0,0 +1,25 @@
// STYLES
import "modern-normalize/modern-normalize.css";
import "@fontsource-variable/jetbrains-mono";
import "@/main.css";
// LIBRARIES
import $ from "jquery";
const API_SERVER = import.meta.env.VITE_API_SERVER!;
$("#register").on("submit", async (e) => {
e.preventDefault();
const email = $("#email");
const password = $("#password");
const response = await fetch(`${API_SERVER}/api/v1/register`, {
method: "POST",
body: JSON.stringify({
email: email.val(),
password: password.val(),
}),
});
console.log(await response.json());
});

View File

@@ -1,5 +1,5 @@
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "@/orm/generated/prisma";
import { PrismaClient } from "@/orm/generated/prisma/client";
export class DatabaseService {
private prisma: PrismaClient;

View File

@@ -92,3 +92,13 @@ export interface SelectedFeature {
id: number;
area: number;
}
export interface RegisterRequest {
email: string;
password: string;
}
export interface RegisterResponse {
status: "success" | "error";
messages: Array<string> | undefined;
}

4
src/utilities/email.ts Normal file
View File

@@ -0,0 +1,4 @@
export const validateEmail = (email: string): boolean => {
const emailRegex: RegExp = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
return emailRegex.test(email);
};

13
src/utilities/password.ts Normal file
View File

@@ -0,0 +1,13 @@
export const hashPassword = async (password: string): Promise<string> => {
const PASSWORD_SALT = Bun.env.PASSWORD_SALT!;
const saltedPassword = PASSWORD_SALT + password;
const encoder = new TextEncoder();
const data = encoder.encode(saltedPassword);
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
return hashHex;
};