inseriti log per verifica login
This commit is contained in:
parent
71ed95251c
commit
2de9d601f5
8 changed files with 181 additions and 17 deletions
|
|
@ -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
|
||||
|
|
|
|||
22
src/app/(main)/articoli/page.tsx
Normal file
22
src/app/(main)/articoli/page.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
export default function ArticoliPage() {
|
||||
return (
|
||||
<div className="flex flex-1 flex-col bg-zinc-50 p-6 dark:bg-black">
|
||||
<div className="mx-auto w-full max-w-6xl">
|
||||
<header className="mb-8">
|
||||
<h1 className="text-3xl font-semibold text-zinc-950 dark:text-zinc-50">
|
||||
Articoli
|
||||
</h1>
|
||||
<p className="mt-2 text-zinc-600 dark:text-zinc-400">
|
||||
Catalogo ricambi e componenti
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div className="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<p className="text-zinc-600 dark:text-zinc-400">
|
||||
Contenuto della pagina articoli in arrivo...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
22
src/app/(main)/macchine/page.tsx
Normal file
22
src/app/(main)/macchine/page.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
export default function MachinePage() {
|
||||
return (
|
||||
<div className="flex flex-1 flex-col bg-zinc-50 p-6 dark:bg-black">
|
||||
<div className="mx-auto w-full max-w-6xl">
|
||||
<header className="mb-8">
|
||||
<h1 className="text-3xl font-semibold text-zinc-950 dark:text-zinc-50">
|
||||
Macchine
|
||||
</h1>
|
||||
<p className="mt-2 text-zinc-600 dark:text-zinc-400">
|
||||
Gestione del parco macchine e attrezzature
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div className="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<p className="text-zinc-600 dark:text-zinc-400">
|
||||
Contenuto della pagina macchine in arrivo...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
101
src/auth.ts
101
src/auth.ts
|
|
@ -8,12 +8,41 @@ 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<string, unknown>): void {
|
||||
console.log(`[auth] ${message}`, details ?? "");
|
||||
}
|
||||
|
||||
function authError(message: string, error: unknown, details?: Record<string, unknown>): 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<DbUser | null> {
|
||||
const [rows] = await db.execute<DbUserRow[]>(
|
||||
let rows: DbUserRow[];
|
||||
|
||||
try {
|
||||
[rows] = await db.execute<DbUserRow[]>(
|
||||
`SELECT id, utente, mail, password, ruolo, attivo, last_login
|
||||
FROM utenti
|
||||
WHERE attivo = 1
|
||||
|
|
@ -21,21 +50,50 @@ async function findActiveUser(identifier: string): Promise<DbUser | null> {
|
|||
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<void> {
|
||||
try {
|
||||
await db.execute<ResultSetHeader>(
|
||||
"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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue