diff --git a/docker-compose.yml b/docker-compose.yml
index c93c889..86c1766 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -7,6 +7,8 @@ services:
restart: always
env_file:
- .env
+ environment:
+ MYSQL_HOST: mysql
ports:
- "127.0.0.1:3001:3000"
depends_on:
@@ -23,6 +25,8 @@ services:
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
volumes:
- dbdata:/var/lib/mysql
+ ports:
+ - "127.0.0.1:3306:3306"
phpmyadmin:
image: phpmyadmin:latest
diff --git a/src/app/(main)/articoli/page.tsx b/src/app/(main)/articoli/page.tsx
new file mode 100644
index 0000000..3c28e03
--- /dev/null
+++ b/src/app/(main)/articoli/page.tsx
@@ -0,0 +1,22 @@
+export default function ArticoliPage() {
+ return (
+
+
+
+
+
+
+ Contenuto della pagina articoli in arrivo...
+
+
+
+
+ );
+}
diff --git a/src/app/(main)/articoli/pages.ts b/src/app/(main)/articoli/pages.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/src/app/(main)/macchine/page.tsx b/src/app/(main)/macchine/page.tsx
new file mode 100644
index 0000000..6bb336a
--- /dev/null
+++ b/src/app/(main)/macchine/page.tsx
@@ -0,0 +1,22 @@
+export default function MachinePage() {
+ return (
+
+
+
+
+
+
+ Contenuto della pagina macchine in arrivo...
+
+
+
+
+ );
+}
diff --git a/src/app/(main)/macchine/pages.ts b/src/app/(main)/macchine/pages.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
index eebf47a..d4c310d 100644
--- a/src/app/login/page.tsx
+++ b/src/app/login/page.tsx
@@ -12,17 +12,31 @@ type LoginPageProps = {
async function login(formData: FormData) {
"use server";
+ const identifier = String(formData.get("identifier") ?? "").trim();
+
+ console.log("[login-page] Submit login ricevuto", {
+ hasIdentifier: Boolean(identifier),
+ hasPassword: Boolean(formData.get("password")),
+ });
+
try {
await signIn("credentials", {
- identifier: formData.get("identifier"),
+ identifier,
password: formData.get("password"),
redirectTo: "/",
});
} catch (error) {
if (error instanceof AuthError) {
+ console.log("[login-page] AuthError durante login", {
+ type: error.type,
+ cause: error.cause,
+ });
redirect("/login?error=CredentialsSignin");
}
+ console.error("[login-page] Errore inatteso durante login", {
+ error: error instanceof Error ? error.message : String(error),
+ });
throw error;
}
}
diff --git a/src/auth.ts b/src/auth.ts
index 701ca2f..a0aa73b 100644
--- a/src/auth.ts
+++ b/src/auth.ts
@@ -8,34 +8,92 @@ import type { DbUser, UserRole } from "@/src/types/user";
type DbUserRow = DbUser & RowDataPacket;
+function maskIdentifier(identifier: string): string {
+ if (identifier.includes("@")) {
+ const [name = "", domain = ""] = identifier.split("@");
+ const visibleName = name.slice(0, 2);
+
+ return `${visibleName}${"*".repeat(Math.max(name.length - 2, 1))}@${domain}`;
+ }
+
+ if (identifier.length <= 2) {
+ return "*".repeat(identifier.length);
+ }
+
+ return `${identifier.slice(0, 2)}${"*".repeat(identifier.length - 2)}`;
+}
+
+function authLog(message: string, details?: Record): void {
+ console.log(`[auth] ${message}`, details ?? "");
+}
+
+function authError(message: string, error: unknown, details?: Record): void {
+ console.error(`[auth] ${message}`, {
+ ...details,
+ error: error instanceof Error ? error.message : String(error),
+ });
+}
+
function isUserRole(value: string): value is UserRole {
return ["admin", "manager", "user", "sviluppo"].includes(value);
}
async function findActiveUser(identifier: string): Promise {
- const [rows] = await db.execute(
- `SELECT id, utente, mail, password, ruolo, attivo, last_login
- FROM utenti
- WHERE attivo = 1
- AND (utente = :identifier OR mail = :identifier)
- LIMIT 1`,
- { identifier },
- );
+ let rows: DbUserRow[];
+
+ try {
+ [rows] = await db.execute(
+ `SELECT id, utente, mail, password, ruolo, attivo, last_login
+ FROM utenti
+ WHERE attivo = 1
+ AND (utente = :identifier OR mail = :identifier)
+ LIMIT 1`,
+ { identifier },
+ );
+ } catch (error) {
+ authError("Errore durante la query utente attivo", error, {
+ identifier: maskIdentifier(identifier),
+ });
+ throw error;
+ }
const user = rows[0];
- if (!user || !isUserRole(user.ruolo)) {
+ if (!user) {
+ authLog("Nessun utente attivo trovato", {
+ identifier: maskIdentifier(identifier),
+ });
return null;
}
+ if (!isUserRole(user.ruolo)) {
+ authLog("Utente trovato con ruolo non valido", {
+ userId: user.id,
+ ruolo: user.ruolo,
+ });
+ return null;
+ }
+
+ authLog("Utente attivo trovato", {
+ userId: user.id,
+ utente: user.utente,
+ ruolo: user.ruolo,
+ });
+
return user;
}
async function updateLastLogin(userId: number): Promise {
- await db.execute(
- "UPDATE utenti SET last_login = NOW() WHERE id = :userId",
- { userId },
- );
+ try {
+ await db.execute(
+ "UPDATE utenti SET last_login = NOW() WHERE id = :userId",
+ { userId },
+ );
+ authLog("last_login aggiornato", { userId });
+ } catch (error) {
+ authError("Errore durante aggiornamento last_login", error, { userId });
+ throw error;
+ }
}
export const { handlers, auth, signIn, signOut } = NextAuth({
@@ -53,27 +111,64 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
+ const startedAt = Date.now();
const identifier = String(credentials?.identifier ?? "").trim();
const password = String(credentials?.password ?? "");
+ const maskedIdentifier = maskIdentifier(identifier);
+
+ authLog("Tentativo login ricevuto", {
+ identifier: maskedIdentifier,
+ hasIdentifier: Boolean(identifier),
+ hasPassword: Boolean(password),
+ });
if (!identifier || !password) {
+ authLog("Login respinto: campi mancanti", {
+ identifier: maskedIdentifier,
+ hasIdentifier: Boolean(identifier),
+ hasPassword: Boolean(password),
+ });
return null;
}
const user = await findActiveUser(identifier);
if (!user) {
+ authLog("Login respinto: utente non trovato o non valido", {
+ identifier: maskedIdentifier,
+ durationMs: Date.now() - startedAt,
+ });
return null;
}
- const passwordMatches = await bcrypt.compare(password, user.password);
+ let passwordMatches = false;
+
+ try {
+ passwordMatches = await bcrypt.compare(password, user.password);
+ } catch (error) {
+ authError("Errore durante confronto password bcrypt", error, {
+ userId: user.id,
+ durationMs: Date.now() - startedAt,
+ });
+ throw error;
+ }
if (!passwordMatches) {
+ authLog("Login respinto: password non valida", {
+ userId: user.id,
+ durationMs: Date.now() - startedAt,
+ });
return null;
}
await updateLastLogin(user.id);
+ authLog("Login riuscito", {
+ userId: user.id,
+ ruolo: user.ruolo,
+ durationMs: Date.now() - startedAt,
+ });
+
return {
id: String(user.id),
name: user.utente,
diff --git a/src/lib/db/connection.ts b/src/lib/db/connection.ts
index 731981b..c1db94a 100644
--- a/src/lib/db/connection.ts
+++ b/src/lib/db/connection.ts
@@ -58,8 +58,15 @@ function getPool(): Pool {
}
export const db = new Proxy({} as Pool, {
- get(_target, property, receiver) {
- return Reflect.get(getPool(), property, receiver);
+ get(_target, property) {
+ const pool = getPool();
+ const value = Reflect.get(pool, property);
+
+ if (typeof value === "function") {
+ return value.bind(pool);
+ }
+
+ return value;
},
});