fix comments
This commit is contained in:
@@ -8,12 +8,19 @@ export async function duplicateHook(
|
|||||||
}>,
|
}>,
|
||||||
reply: FastifyReply,
|
reply: FastifyReply,
|
||||||
) {
|
) {
|
||||||
const isDuplicate = await isDuplicatedEvent({
|
const ip = req.clientIp;
|
||||||
ip: req.clientIp ?? '',
|
const origin = req.headers.origin;
|
||||||
origin: req.headers.origin ?? '',
|
const clientId = req.headers['openpanel-client-id'];
|
||||||
|
const shouldCheck = ip && origin && clientId;
|
||||||
|
|
||||||
|
const isDuplicate = shouldCheck
|
||||||
|
? await isDuplicatedEvent({
|
||||||
|
ip,
|
||||||
|
origin,
|
||||||
payload: req.body,
|
payload: req.body,
|
||||||
projectId: (req.headers['openpanel-client-id'] as string) || '',
|
projectId: clientId as string,
|
||||||
});
|
})
|
||||||
|
: false;
|
||||||
|
|
||||||
if (isDuplicate) {
|
if (isDuplicate) {
|
||||||
return reply.status(200).send('Duplicate event');
|
return reply.status(200).send('Duplicate event');
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ import { handler } from '@/controllers/track.controller';
|
|||||||
import type { FastifyPluginCallback } from 'fastify';
|
import type { FastifyPluginCallback } from 'fastify';
|
||||||
|
|
||||||
import { clientHook } from '@/hooks/client.hook';
|
import { clientHook } from '@/hooks/client.hook';
|
||||||
|
import { duplicateHook } from '@/hooks/duplicate.hook';
|
||||||
import { isBotHook } from '@/hooks/is-bot.hook';
|
import { isBotHook } from '@/hooks/is-bot.hook';
|
||||||
|
|
||||||
const trackRouter: FastifyPluginCallback = async (fastify) => {
|
const trackRouter: FastifyPluginCallback = async (fastify) => {
|
||||||
fastify.addHook('preHandler', clientHook);
|
|
||||||
fastify.addHook('preHandler', isBotHook);
|
fastify.addHook('preHandler', isBotHook);
|
||||||
|
fastify.addHook('preValidation', duplicateHook);
|
||||||
|
fastify.addHook('preHandler', clientHook);
|
||||||
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ export async function validateSdkRequest(
|
|||||||
|
|
||||||
if (client.secret && clientSecret) {
|
if (client.secret && clientSecret) {
|
||||||
const isVerified = await getCache(
|
const isVerified = await getCache(
|
||||||
`client:auth:${clientId}:${clientSecret.slice(0, 5)}`,
|
`client:auth:${clientId}:${Buffer.from(clientSecret).toString('base64')}`,
|
||||||
60 * 5,
|
60 * 5,
|
||||||
async () => await verifyPassword(clientSecret, client.secret!),
|
async () => await verifyPassword(clientSecret, client.secret!),
|
||||||
true,
|
true,
|
||||||
|
|||||||
@@ -104,8 +104,12 @@ async function cleanupOldEventBufferKeys(): Promise<CleanupStats> {
|
|||||||
const events = await redis.lrange(sessionKey, 0, -1);
|
const events = await redis.lrange(sessionKey, 0, -1);
|
||||||
|
|
||||||
if (events.length > 0) {
|
if (events.length > 0) {
|
||||||
// Move events to new queue
|
// Move events to new queue in safe batches to avoid exceeding V8 arg limits
|
||||||
await redis.rpush(newQueueKey, ...events);
|
const chunkSize = 1000;
|
||||||
|
for (let offset = 0; offset < events.length; offset = chunkSize) {
|
||||||
|
const chunk = events.slice(offset, offset + chunkSize);
|
||||||
|
await redis.rpush(newQueueKey, ...chunk);
|
||||||
|
}
|
||||||
// Update buffer counter
|
// Update buffer counter
|
||||||
await redis.incrby('event_buffer:total_count', events.length);
|
await redis.incrby('event_buffer:total_count', events.length);
|
||||||
totalEventsMigrated += events.length;
|
totalEventsMigrated += events.length;
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export async function bootCron() {
|
|||||||
// TODO: Switch to getJobSchedulers
|
// TODO: Switch to getJobSchedulers
|
||||||
const repeatableJobs = await cronQueue.getRepeatableJobs();
|
const repeatableJobs = await cronQueue.getRepeatableJobs();
|
||||||
for (const repeatableJob of repeatableJobs) {
|
for (const repeatableJob of repeatableJobs) {
|
||||||
cronQueue.removeRepeatableByKey(repeatableJob.key);
|
await cronQueue.removeRepeatableByKey(repeatableJob.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add repeatable jobs
|
// Add repeatable jobs
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { setTimeout as sleep } from 'node:timers/promises';
|
|||||||
import { Worker as GroupWorker } from 'groupmq';
|
import { Worker as GroupWorker } from 'groupmq';
|
||||||
|
|
||||||
import { cronJob } from './jobs/cron';
|
import { cronJob } from './jobs/cron';
|
||||||
import { incomingEventPure } from './jobs/events.incoming-event';
|
import { incomingEvent } from './jobs/events.incoming-event';
|
||||||
import { importJob } from './jobs/import';
|
import { importJob } from './jobs/import';
|
||||||
import { miscJob } from './jobs/misc';
|
import { miscJob } from './jobs/misc';
|
||||||
import { notificationJob } from './jobs/notification';
|
import { notificationJob } from './jobs/notification';
|
||||||
@@ -122,7 +122,7 @@ export async function bootWorkers() {
|
|||||||
process.env.EVENT_BLOCKING_TIMEOUT_SEC || '1',
|
process.env.EVENT_BLOCKING_TIMEOUT_SEC || '1',
|
||||||
),
|
),
|
||||||
handler: async (job) => {
|
handler: async (job) => {
|
||||||
return await incomingEventPure(job.data);
|
return await incomingEvent(job.data);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ export async function bootWorkers() {
|
|||||||
concurrency,
|
concurrency,
|
||||||
});
|
});
|
||||||
workers.push(importWorker);
|
workers.push(importWorker);
|
||||||
logger.info('Started worker for misc', { concurrency });
|
logger.info('Started worker for import', { concurrency });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (workers.length === 0) {
|
if (workers.length === 0) {
|
||||||
|
|||||||
@@ -45,18 +45,8 @@ async function createEventAndNotify(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function incomingEvent(
|
export async function incomingEvent(
|
||||||
job: Job<EventsQueuePayloadIncomingEvent>,
|
|
||||||
token?: string,
|
|
||||||
) {
|
|
||||||
return incomingEventPure(job.data.payload, job, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function incomingEventPure(
|
|
||||||
jobPayload: EventsQueuePayloadIncomingEvent['payload'],
|
jobPayload: EventsQueuePayloadIncomingEvent['payload'],
|
||||||
job?: Job<EventsQueuePayloadIncomingEvent>,
|
|
||||||
token?: string,
|
|
||||||
) {
|
) {
|
||||||
await getRedisCache().incr('queue:counter');
|
|
||||||
const {
|
const {
|
||||||
geo,
|
geo,
|
||||||
event: body,
|
event: body,
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { type IServiceEvent, createEvent } from '@openpanel/db';
|
import { type IServiceEvent, createEvent } from '@openpanel/db';
|
||||||
import { eventBuffer } from '@openpanel/db';
|
import { eventBuffer } from '@openpanel/db';
|
||||||
import { sessionsQueue } from '@openpanel/queue';
|
import {
|
||||||
|
type EventsQueuePayloadIncomingEvent,
|
||||||
|
sessionsQueue,
|
||||||
|
} from '@openpanel/queue';
|
||||||
import type { Job } from 'bullmq';
|
import type { Job } from 'bullmq';
|
||||||
import { type Mock, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { type Mock, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import { incomingEvent } from './events.incoming-event';
|
import { incomingEvent } from './events.incoming-event';
|
||||||
@@ -32,6 +35,28 @@ const geo = {
|
|||||||
latitude: 0,
|
latitude: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const uaInfo: EventsQueuePayloadIncomingEvent['payload']['uaInfo'] = {
|
||||||
|
isServer: false,
|
||||||
|
device: 'desktop',
|
||||||
|
os: 'Windows',
|
||||||
|
osVersion: '10',
|
||||||
|
browser: 'Chrome',
|
||||||
|
browserVersion: '91.0.4472.124',
|
||||||
|
brand: '',
|
||||||
|
model: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const uaInfoServer: EventsQueuePayloadIncomingEvent['payload']['uaInfo'] = {
|
||||||
|
isServer: true,
|
||||||
|
device: 'server',
|
||||||
|
os: '',
|
||||||
|
osVersion: '',
|
||||||
|
browser: '',
|
||||||
|
browserVersion: '',
|
||||||
|
brand: '',
|
||||||
|
model: '',
|
||||||
|
};
|
||||||
|
|
||||||
describe('incomingEvent', () => {
|
describe('incomingEvent', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
@@ -41,14 +66,15 @@ describe('incomingEvent', () => {
|
|||||||
const spySessionsQueueAdd = vi.spyOn(sessionsQueue, 'add');
|
const spySessionsQueueAdd = vi.spyOn(sessionsQueue, 'add');
|
||||||
const timestamp = new Date();
|
const timestamp = new Date();
|
||||||
// Mock job data
|
// Mock job data
|
||||||
const jobData = {
|
const jobData: EventsQueuePayloadIncomingEvent['payload'] = {
|
||||||
payload: {
|
|
||||||
geo,
|
geo,
|
||||||
event: {
|
event: {
|
||||||
name: 'test_event',
|
name: 'test_event',
|
||||||
timestamp: timestamp.toISOString(),
|
timestamp: timestamp.toISOString(),
|
||||||
|
isTimestampFromThePast: false,
|
||||||
properties: { __path: 'https://example.com/test' },
|
properties: { __path: 'https://example.com/test' },
|
||||||
},
|
},
|
||||||
|
uaInfo,
|
||||||
headers: {
|
headers: {
|
||||||
'request-id': '123',
|
'request-id': '123',
|
||||||
'user-agent':
|
'user-agent':
|
||||||
@@ -59,13 +85,10 @@ describe('incomingEvent', () => {
|
|||||||
projectId,
|
projectId,
|
||||||
currentDeviceId,
|
currentDeviceId,
|
||||||
previousDeviceId,
|
previousDeviceId,
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const job = { data: jobData } as Job;
|
|
||||||
|
|
||||||
// Execute the job
|
// Execute the job
|
||||||
await incomingEvent(job);
|
await incomingEvent(jobData);
|
||||||
|
|
||||||
const event = {
|
const event = {
|
||||||
name: 'test_event',
|
name: 'test_event',
|
||||||
@@ -78,8 +101,8 @@ describe('incomingEvent', () => {
|
|||||||
properties: {
|
properties: {
|
||||||
__hash: undefined,
|
__hash: undefined,
|
||||||
__query: undefined,
|
__query: undefined,
|
||||||
__user_agent: jobData.payload.headers['user-agent'],
|
__user_agent: jobData.headers['user-agent'],
|
||||||
__reqId: jobData.payload.headers['request-id'],
|
__reqId: jobData.headers['request-id'],
|
||||||
},
|
},
|
||||||
createdAt: timestamp,
|
createdAt: timestamp,
|
||||||
country: 'US',
|
country: 'US',
|
||||||
@@ -92,16 +115,16 @@ describe('incomingEvent', () => {
|
|||||||
browser: 'Chrome',
|
browser: 'Chrome',
|
||||||
browserVersion: '91.0.4472.124',
|
browserVersion: '91.0.4472.124',
|
||||||
device: 'desktop',
|
device: 'desktop',
|
||||||
brand: undefined,
|
brand: '',
|
||||||
model: undefined,
|
model: '',
|
||||||
duration: 0,
|
duration: 0,
|
||||||
path: '/test',
|
path: '/test',
|
||||||
origin: 'https://example.com',
|
origin: 'https://example.com',
|
||||||
referrer: '',
|
referrer: '',
|
||||||
referrerName: '',
|
referrerName: '',
|
||||||
referrerType: '',
|
referrerType: '',
|
||||||
sdkName: jobData.payload.headers['openpanel-sdk-name'],
|
sdkName: jobData.headers['openpanel-sdk-name'],
|
||||||
sdkVersion: jobData.payload.headers['openpanel-sdk-version'],
|
sdkVersion: jobData.headers['openpanel-sdk-version'],
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(spySessionsQueueAdd).toHaveBeenCalledWith(
|
expect(spySessionsQueueAdd).toHaveBeenCalledWith(
|
||||||
@@ -135,13 +158,13 @@ describe('incomingEvent', () => {
|
|||||||
|
|
||||||
const timestamp = new Date();
|
const timestamp = new Date();
|
||||||
// Mock job data
|
// Mock job data
|
||||||
const jobData = {
|
const jobData: EventsQueuePayloadIncomingEvent['payload'] = {
|
||||||
payload: {
|
|
||||||
geo,
|
geo,
|
||||||
event: {
|
event: {
|
||||||
name: 'test_event',
|
name: 'test_event',
|
||||||
timestamp: timestamp.toISOString(),
|
timestamp: timestamp.toISOString(),
|
||||||
properties: { __path: 'https://example.com/test' },
|
properties: { __path: 'https://example.com/test' },
|
||||||
|
isTimestampFromThePast: false,
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
'request-id': '123',
|
'request-id': '123',
|
||||||
@@ -150,14 +173,12 @@ describe('incomingEvent', () => {
|
|||||||
'openpanel-sdk-name': 'web',
|
'openpanel-sdk-name': 'web',
|
||||||
'openpanel-sdk-version': '1.0.0',
|
'openpanel-sdk-version': '1.0.0',
|
||||||
},
|
},
|
||||||
|
uaInfo,
|
||||||
projectId,
|
projectId,
|
||||||
currentDeviceId,
|
currentDeviceId,
|
||||||
previousDeviceId,
|
previousDeviceId,
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const job = { data: jobData } as Job;
|
|
||||||
|
|
||||||
const changeDelay = vi.fn();
|
const changeDelay = vi.fn();
|
||||||
const updateData = vi.fn();
|
const updateData = vi.fn();
|
||||||
spySessionsQueueGetJob.mockResolvedValueOnce({
|
spySessionsQueueGetJob.mockResolvedValueOnce({
|
||||||
@@ -175,7 +196,7 @@ describe('incomingEvent', () => {
|
|||||||
},
|
},
|
||||||
} as Partial<Job> as Job);
|
} as Partial<Job> as Job);
|
||||||
// Execute the job
|
// Execute the job
|
||||||
await incomingEvent(job);
|
await incomingEvent(jobData);
|
||||||
|
|
||||||
const event = {
|
const event = {
|
||||||
name: 'test_event',
|
name: 'test_event',
|
||||||
@@ -186,8 +207,8 @@ describe('incomingEvent', () => {
|
|||||||
properties: {
|
properties: {
|
||||||
__hash: undefined,
|
__hash: undefined,
|
||||||
__query: undefined,
|
__query: undefined,
|
||||||
__user_agent: jobData.payload.headers['user-agent'],
|
__user_agent: jobData.headers['user-agent'],
|
||||||
__reqId: jobData.payload.headers['request-id'],
|
__reqId: jobData.headers['request-id'],
|
||||||
},
|
},
|
||||||
createdAt: timestamp,
|
createdAt: timestamp,
|
||||||
country: 'US',
|
country: 'US',
|
||||||
@@ -200,16 +221,16 @@ describe('incomingEvent', () => {
|
|||||||
browser: 'Chrome',
|
browser: 'Chrome',
|
||||||
browserVersion: '91.0.4472.124',
|
browserVersion: '91.0.4472.124',
|
||||||
device: 'desktop',
|
device: 'desktop',
|
||||||
brand: undefined,
|
brand: '',
|
||||||
model: undefined,
|
model: '',
|
||||||
duration: 0,
|
duration: 0,
|
||||||
path: '/test',
|
path: '/test',
|
||||||
origin: 'https://example.com',
|
origin: 'https://example.com',
|
||||||
referrer: '',
|
referrer: '',
|
||||||
referrerName: '',
|
referrerName: '',
|
||||||
referrerType: '',
|
referrerType: '',
|
||||||
sdkName: jobData.payload.headers['openpanel-sdk-name'],
|
sdkName: jobData.headers['openpanel-sdk-name'],
|
||||||
sdkVersion: jobData.payload.headers['openpanel-sdk-version'],
|
sdkVersion: jobData.headers['openpanel-sdk-version'],
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(spySessionsQueueAdd).toHaveBeenCalledTimes(0);
|
expect(spySessionsQueueAdd).toHaveBeenCalledTimes(0);
|
||||||
@@ -220,14 +241,14 @@ describe('incomingEvent', () => {
|
|||||||
|
|
||||||
it('should handle server events (with existing screen view)', async () => {
|
it('should handle server events (with existing screen view)', async () => {
|
||||||
const timestamp = new Date();
|
const timestamp = new Date();
|
||||||
const jobData = {
|
const jobData: EventsQueuePayloadIncomingEvent['payload'] = {
|
||||||
payload: {
|
|
||||||
geo,
|
geo,
|
||||||
event: {
|
event: {
|
||||||
name: 'server_event',
|
name: 'server_event',
|
||||||
timestamp: timestamp.toISOString(),
|
timestamp: timestamp.toISOString(),
|
||||||
properties: { custom_property: 'test_value' },
|
properties: { custom_property: 'test_value' },
|
||||||
profileId: 'profile-123',
|
profileId: 'profile-123',
|
||||||
|
isTimestampFromThePast: false,
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
'user-agent': 'OpenPanel Server/1.0',
|
'user-agent': 'OpenPanel Server/1.0',
|
||||||
@@ -238,11 +259,9 @@ describe('incomingEvent', () => {
|
|||||||
projectId,
|
projectId,
|
||||||
currentDeviceId: '',
|
currentDeviceId: '',
|
||||||
previousDeviceId: '',
|
previousDeviceId: '',
|
||||||
},
|
uaInfo: uaInfoServer,
|
||||||
};
|
};
|
||||||
|
|
||||||
const job = { data: jobData } as Job;
|
|
||||||
|
|
||||||
const mockLastScreenView = {
|
const mockLastScreenView = {
|
||||||
deviceId: 'last-device-123',
|
deviceId: 'last-device-123',
|
||||||
sessionId: 'last-session-456',
|
sessionId: 'last-session-456',
|
||||||
@@ -268,7 +287,7 @@ describe('incomingEvent', () => {
|
|||||||
mockLastScreenView as IServiceEvent,
|
mockLastScreenView as IServiceEvent,
|
||||||
);
|
);
|
||||||
|
|
||||||
await incomingEvent(job);
|
await incomingEvent(jobData);
|
||||||
|
|
||||||
expect((createEvent as Mock).mock.calls[0]![0]).toStrictEqual({
|
expect((createEvent as Mock).mock.calls[0]![0]).toStrictEqual({
|
||||||
name: 'server_event',
|
name: 'server_event',
|
||||||
@@ -311,14 +330,14 @@ describe('incomingEvent', () => {
|
|||||||
|
|
||||||
it('should handle server events (without existing screen view)', async () => {
|
it('should handle server events (without existing screen view)', async () => {
|
||||||
const timestamp = new Date();
|
const timestamp = new Date();
|
||||||
const jobData = {
|
const jobData: EventsQueuePayloadIncomingEvent['payload'] = {
|
||||||
payload: {
|
|
||||||
geo,
|
geo,
|
||||||
event: {
|
event: {
|
||||||
name: 'server_event',
|
name: 'server_event',
|
||||||
timestamp: timestamp.toISOString(),
|
timestamp: timestamp.toISOString(),
|
||||||
properties: { custom_property: 'test_value' },
|
properties: { custom_property: 'test_value' },
|
||||||
profileId: 'profile-123',
|
profileId: 'profile-123',
|
||||||
|
isTimestampFromThePast: false,
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
'user-agent': 'OpenPanel Server/1.0',
|
'user-agent': 'OpenPanel Server/1.0',
|
||||||
@@ -329,15 +348,13 @@ describe('incomingEvent', () => {
|
|||||||
projectId,
|
projectId,
|
||||||
currentDeviceId: '',
|
currentDeviceId: '',
|
||||||
previousDeviceId: '',
|
previousDeviceId: '',
|
||||||
},
|
uaInfo: uaInfoServer,
|
||||||
};
|
};
|
||||||
|
|
||||||
const job = { data: jobData } as Job;
|
|
||||||
|
|
||||||
// Mock getLastScreenView to return null
|
// Mock getLastScreenView to return null
|
||||||
vi.mocked(eventBuffer.getLastScreenView).mockResolvedValueOnce(null);
|
vi.mocked(eventBuffer.getLastScreenView).mockResolvedValueOnce(null);
|
||||||
|
|
||||||
await incomingEvent(job);
|
await incomingEvent(jobData);
|
||||||
|
|
||||||
expect((createEvent as Mock).mock.calls[0]![0]).toStrictEqual({
|
expect((createEvent as Mock).mock.calls[0]![0]).toStrictEqual({
|
||||||
name: 'server_event',
|
name: 'server_event',
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
import type { Job } from 'bullmq';
|
|
||||||
|
|
||||||
import type {
|
|
||||||
EventsQueuePayload,
|
|
||||||
EventsQueuePayloadIncomingEvent,
|
|
||||||
} from '@openpanel/queue';
|
|
||||||
|
|
||||||
import { incomingEvent } from './events.incoming-event';
|
|
||||||
|
|
||||||
export async function eventsJob(job: Job<EventsQueuePayload>, token?: string) {
|
|
||||||
return await incomingEvent(
|
|
||||||
job as Job<EventsQueuePayloadIncomingEvent>,
|
|
||||||
token,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -90,7 +90,7 @@ const parse = (ua: string): UAParser.IResult => {
|
|||||||
...res,
|
...res,
|
||||||
os: {
|
os: {
|
||||||
...res.os,
|
...res.os,
|
||||||
version: osVersion[1]!.replace('_', '.'),
|
version: osVersion[1]!.replace(/_/g, '.'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
parseCache.set(ua, result);
|
parseCache.set(ua, result);
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { readFile } from 'node:fs/promises';
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { dirname } from 'node:path';
|
import { dirname } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = dirname(__filename);
|
|
||||||
import type { ReaderModel } from '@maxmind/geoip2-node';
|
import type { ReaderModel } from '@maxmind/geoip2-node';
|
||||||
import { Reader } from '@maxmind/geoip2-node';
|
import { Reader } from '@maxmind/geoip2-node';
|
||||||
import { LRUCache } from 'lru-cache';
|
import { LRUCache } from 'lru-cache';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
const filename = 'GeoLite2-City.mmdb';
|
const filename = 'GeoLite2-City.mmdb';
|
||||||
// From api or worker package
|
// From api or worker package
|
||||||
const dbPath = path.join(__dirname, `../../../packages/geo/${filename}`);
|
const dbPath = path.join(__dirname, `../../../packages/geo/${filename}`);
|
||||||
@@ -73,13 +73,15 @@ export async function getGeoLocation(ip?: string): Promise<GeoLocation> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await reader?.city(ip);
|
const response = await reader?.city(ip);
|
||||||
return {
|
const res = {
|
||||||
city: response?.city?.names.en,
|
city: response?.city?.names.en,
|
||||||
country: response?.country?.isoCode,
|
country: response?.country?.isoCode,
|
||||||
region: response?.subdivisions?.[0]?.names.en,
|
region: response?.subdivisions?.[0]?.names.en,
|
||||||
longitude: response?.location?.longitude,
|
longitude: response?.location?.longitude,
|
||||||
latitude: response?.location?.latitude,
|
latitude: response?.location?.latitude,
|
||||||
};
|
};
|
||||||
|
cache.set(ip, res);
|
||||||
|
return res;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return DEFAULT_GEO;
|
return DEFAULT_GEO;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user