feat: add geolocation fields to session and implement geo stats endpoint

- 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.
This commit is contained in:
Jose Selesan
2026-06-10 10:18:31 -03:00
parent 00f0cf511f
commit dc8a10a612
15 changed files with 389 additions and 30 deletions

View File

@@ -1,7 +1,20 @@
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,
@@ -23,8 +36,33 @@ export const requireAuth = createMiddleware<AppEnv>(async (c, next) => {
);
}
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
}
}