Add customer login activity logging

- Add CustomerLoginLog model to track customer login attempts
- Record successful and failed customer logins
- Capture email, IP address, and user agent for each attempt
- Create /admin/customer-login-logs page to view activity
- Show login stats (successes, failures, active customers)
- Display browser/device and IP for each login attempt
- Add "Customer login activity" link to admin menu
- Monitor customer access patterns and detect suspicious activity
- Last 30 days active customer count

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Andymick
2026-07-16 21:43:09 +01:00
co-authored by Claude Haiku 4.5
parent ea6f9d2e25
commit e69759081c
4 changed files with 168 additions and 1 deletions
+15
View File
@@ -62,8 +62,14 @@ export async function loginCustomer(formData: FormData) {
const next = safeNext(formData.get('next'));
const ip = getClientIp(headers());
const userAgent = headers().get('user-agent') ?? undefined;
const { allowed } = await checkRateLimit(`login:customer:${ip}`, 5, 15 * 60 * 1000);
if (!allowed) {
// Log rate limited attempt
await prisma.customerLoginLog.create({
data: { success: false, email, ipAddress: ip, userAgent },
});
redirect(`/account/login?error=rate_limited&next=${encodeURIComponent(next)}`);
}
@@ -71,9 +77,18 @@ export async function loginCustomer(formData: FormData) {
const valid = customer ? await verifyPassword(password, customer.passwordHash) : false;
if (!customer || !valid) {
// Log failed login attempt
await prisma.customerLoginLog.create({
data: { success: false, email, ipAddress: ip, userAgent },
});
redirect(`/account/login?error=invalid&next=${encodeURIComponent(next)}`);
}
// Log successful login
await prisma.customerLoginLog.create({
data: { success: true, customerId: customer.id, email, ipAddress: ip, userAgent },
});
await linkGuestOrders(customer.id, customer.email);
await createSession(customer.id);
redirect(next);