basic register
This commit is contained in:
21
login.html
21
login.html
@@ -3,17 +3,24 @@
|
|||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
<h1>LOGIN</h1>
|
<h1>LOGIN</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
<a href="/">Game</a>
|
<a href="/">Game</a>
|
||||||
<div id="login">
|
</p>
|
||||||
|
|
||||||
|
<form id="login">
|
||||||
<label for="email">Email</label>
|
<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>
|
<label for="password">Password</label>
|
||||||
<input type="password" name="password" id="password" /><br />
|
<input required type="password" name="password" id="password" />
|
||||||
<button id="login-button">Login</button>
|
(required)<br />
|
||||||
</div>
|
<button type="submit" id="login-button">Login</button>
|
||||||
<div>
|
</form>
|
||||||
|
|
||||||
|
<p>
|
||||||
<a href="/register">Register?</a>
|
<a href="/register">Register?</a>
|
||||||
</div>
|
</p>
|
||||||
|
|
||||||
<script type="module" src="/src/login.ts"></script>
|
<script type="module" src="/src/login.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
CREATE TABLE "User" (
|
CREATE TABLE "User" (
|
||||||
"id" SERIAL NOT NULL,
|
"id" SERIAL NOT NULL,
|
||||||
"email" TEXT NOT NULL,
|
"email" TEXT NOT NULL,
|
||||||
"name" TEXT,
|
"password" TEXT NOT NULL,
|
||||||
|
"first_login" BOOLEAN NOT NULL,
|
||||||
|
|
||||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||||
);
|
);
|
||||||
@@ -10,6 +10,6 @@ datasource db {
|
|||||||
model User {
|
model User {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
email String @unique
|
email String @unique
|
||||||
name String?
|
|
||||||
password String
|
password String
|
||||||
|
first_login Boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,31 @@
|
|||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
<h1>REGISTER</h1>
|
<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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
108
server.ts
108
server.ts
@@ -1,31 +1,115 @@
|
|||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
|
import { cors } from "hono/cors";
|
||||||
import { upgradeWebSocket, websocket } from "hono/bun";
|
import { upgradeWebSocket, websocket } from "hono/bun";
|
||||||
import { DatabaseService } from "@/services/database-service";
|
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();
|
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) => {
|
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({
|
return c.json({
|
||||||
message: "ok",
|
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(
|
app.get(
|
||||||
"/ws",
|
"/ws",
|
||||||
upgradeWebSocket((c) => {
|
upgradeWebSocket((c) => {
|
||||||
return {
|
return {
|
||||||
onOpen(event, ws) {
|
onOpen(e, ws) {
|
||||||
console.log("Server: Connection opened");
|
console.log("Server: Connection opened");
|
||||||
ws.send("Hello!");
|
ws.send("Hello!");
|
||||||
},
|
},
|
||||||
|
|||||||
11
src/auth/messages.ts
Normal file
11
src/auth/messages.ts
Normal 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",
|
||||||
|
};
|
||||||
17
src/login.ts
17
src/login.ts
@@ -1,14 +1,19 @@
|
|||||||
// STYLES
|
// STYLES
|
||||||
// import "modern-normalize/modern-normalize.css";
|
import "modern-normalize/modern-normalize.css";
|
||||||
// import "@fontsource-variable/jetbrains-mono";
|
import "@fontsource-variable/jetbrains-mono";
|
||||||
import "@/main.css";
|
import "@/main.css";
|
||||||
// import "@maptiler/sdk/dist/maptiler-sdk.css";
|
|
||||||
|
|
||||||
// LIBRARIES
|
// LIBRARIES
|
||||||
// import $ from "jquery";
|
import $ from "jquery";
|
||||||
// import { FilterSpecification, Map, config } from "@maptiler/sdk";
|
// import { FilterSpecification, Map, config } from "@maptiler/sdk";
|
||||||
// import { createIcons, Locate, LocateFixed } from "lucide";
|
// import { createIcons, Locate, LocateFixed } from "lucide";
|
||||||
// import area from "@turf/area";
|
|
||||||
// import { polygon } from "@turf/helpers";
|
|
||||||
|
|
||||||
console.log("login");
|
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
25
src/register.ts
Normal 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());
|
||||||
|
});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { PrismaPg } from "@prisma/adapter-pg";
|
import { PrismaPg } from "@prisma/adapter-pg";
|
||||||
import { PrismaClient } from "@/orm/generated/prisma";
|
import { PrismaClient } from "@/orm/generated/prisma/client";
|
||||||
|
|
||||||
export class DatabaseService {
|
export class DatabaseService {
|
||||||
private prisma: PrismaClient;
|
private prisma: PrismaClient;
|
||||||
|
|||||||
@@ -92,3 +92,13 @@ export interface SelectedFeature {
|
|||||||
id: number;
|
id: number;
|
||||||
area: 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
4
src/utilities/email.ts
Normal 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
13
src/utilities/password.ts
Normal 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;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user