mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
The network operator (carrier) used to resolve only from the optional
offline GeoLite2-ASN database. On a host without it the carrier stayed
empty even though the location resolved fine online.
Mine the operator from the same online provider response the location
lookup already fetches — ip-api's top-level `isp`/`org`/`as`, ipwho.is's
nested `connection.{isp,org,asn}` — so the carrier is populated out of the
box. The offline ASN database stays authoritative when configured.
A single `lookupIpGeo` now returns location + ASN + carrier in one pass;
the audit write and the backfill both go through it. The backfill also
revisits rows that already had a location but no carrier, so the column
fills in across existing history, not just for new sign-ins.
In the admin sign-in overview the carrier moves out of a chip stacked
under the login method into its own column, next to the location.
56 lines
1.7 KiB
TypeScript
56 lines
1.7 KiB
TypeScript
import { prisma } from "@/lib/db";
|
|
import { lookupIpGeo } from "@/lib/geo";
|
|
|
|
export async function auditLog(
|
|
action: string,
|
|
opts: {
|
|
userId?: string | null;
|
|
details?: Record<string, unknown>;
|
|
ipAddress?: string | null;
|
|
} = {},
|
|
) {
|
|
const entry = await prisma.auditLog.create({
|
|
data: {
|
|
action,
|
|
userId: opts.userId ?? null,
|
|
details: opts.details ? JSON.stringify(opts.details) : null,
|
|
ipAddress: opts.ipAddress ?? null,
|
|
},
|
|
});
|
|
|
|
// Fire-and-forget: resolve IP location + carrier and update the entry
|
|
// with whichever fields the resolver could fill. v1.25.8 — `lookupIpGeo`
|
|
// unifies the online location + carrier lookup with the offline ASN MMDB
|
|
// (carrier now resolves from the online provider when the optional offline
|
|
// DB is absent). The whole resolve still races a 3 s timeout so a hung
|
|
// provider can't stall the audit write.
|
|
if (opts.ipAddress && action.startsWith("auth.")) {
|
|
const ipAddress = opts.ipAddress;
|
|
const timeout = new Promise<null>((resolve) =>
|
|
setTimeout(() => resolve(null), 3000),
|
|
);
|
|
|
|
Promise.race([lookupIpGeo(ipAddress), timeout])
|
|
.then((result) => {
|
|
if (!result) return;
|
|
const { location, asn, carrier } = result;
|
|
const data: {
|
|
location?: string;
|
|
asn?: number;
|
|
carrier?: string | null;
|
|
} = {};
|
|
if (location) data.location = location;
|
|
if (typeof asn === "number") data.asn = asn;
|
|
if (carrier) data.carrier = carrier;
|
|
if (Object.keys(data).length === 0) return;
|
|
return prisma.auditLog.update({
|
|
where: { id: entry.id },
|
|
data,
|
|
});
|
|
})
|
|
.catch(() => {
|
|
// Silently ignore geo lookup failures
|
|
});
|
|
}
|
|
}
|