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 ( +
+
+
+

+ Articoli +

+

+ Catalogo ricambi e componenti +

+
+ +
+

+ 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 ( +
+
+
+

+ Macchine +

+

+ Gestione del parco macchine e attrezzature +

+
+ +
+

+ 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; }, });