- Added country, city, countryCode, latitude, and longitude fields to the Session model. - Updated geoip service to fetch and store geolocation data based on user IP. - Created a new admin endpoint to retrieve geolocation statistics, including user counts by country and city. - Enhanced admin page to display geolocation statistics with expandable city details. - Introduced caching for geolocation data to optimize performance.
69 lines
1.6 KiB
TypeScript
69 lines
1.6 KiB
TypeScript
import { auth } from '@/lib/auth';
|
|
import { fetchGeoInfo } from '@/lib/geoip';
|
|
import { db } from '@/lib/prisma';
|
|
import type { AppEnv } from '@/types/hono';
|
|
import { createMiddleware } from 'hono/factory';
|
|
|
|
type SessionWithGeo = {
|
|
id: string;
|
|
ipAddress: string | null;
|
|
userAgent: string | null;
|
|
country: string | null;
|
|
city: string | null;
|
|
countryCode: string | null;
|
|
latitude: number | null;
|
|
longitude: number | null;
|
|
};
|
|
|
|
export const requireAuth = createMiddleware<AppEnv>(async (c, next) => {
|
|
const session = await auth.api.getSession({
|
|
headers: c.req.raw.headers,
|
|
});
|
|
|
|
if (!session) {
|
|
return c.json({ message: 'Unauthorized' }, 401);
|
|
}
|
|
|
|
const user = session.user as { banned?: boolean; banReason?: string | null };
|
|
|
|
if (user.banned) {
|
|
return c.json(
|
|
{
|
|
message: 'Tu cuenta ha sido bloqueada.',
|
|
...(user.banReason ? { reason: user.banReason } : {}),
|
|
},
|
|
403
|
|
);
|
|
}
|
|
|
|
const s = session.session as unknown as SessionWithGeo;
|
|
|
|
c.set('user', session.user);
|
|
c.set('session', session.session);
|
|
|
|
if (s.ipAddress && !s.country) {
|
|
resolveSessionGeo(s.id, s.ipAddress);
|
|
}
|
|
|
|
await next();
|
|
});
|
|
|
|
async function resolveSessionGeo(sessionId: string, ipAddress: string) {
|
|
try {
|
|
const geo = await fetchGeoInfo(ipAddress);
|
|
if (!geo) return;
|
|
await db.session.update({
|
|
where: { id: sessionId },
|
|
data: {
|
|
country: geo.country,
|
|
city: geo.city,
|
|
countryCode: geo.countryCode,
|
|
latitude: geo.lat,
|
|
longitude: geo.lon,
|
|
},
|
|
});
|
|
} catch {
|
|
// Silently fail — geo enrichment is best-effort
|
|
}
|
|
}
|