Files
stats/apps/api/src/controllers/profile.controller.ts
Carl-Gerhard Lindesvärd da59622dce fix: overall perf improvements
* fix: ignore private ips

* fix: performance related fixes

* fix: simply event buffer

* fix: default to 1 events queue shard

* add: cleanup scripts

* fix: comments

* fix comments

* fix

* fix: groupmq

* wip

* fix: sync cachable

* remove cluster names and add it behind env flag (if someone want to scale)

* fix

* wip

* better logger

* remove reqid and user agent

* fix lock

* remove wait_for_async_insert
2025-11-15 22:13:59 +01:00

136 lines
3.2 KiB
TypeScript

import type { FastifyReply, FastifyRequest } from 'fastify';
import { assocPath, pathOr } from 'ramda';
import { parseUserAgent } from '@openpanel/common/server';
import { getProfileById, upsertProfile } from '@openpanel/db';
import { getGeoLocation } from '@openpanel/geo';
import type {
IncrementProfilePayload,
UpdateProfilePayload,
} from '@openpanel/sdk';
export async function updateProfile(
request: FastifyRequest<{
Body: UpdateProfilePayload;
}>,
reply: FastifyReply,
) {
const payload = request.body;
const projectId = request.client!.projectId;
if (!projectId) {
return reply.status(400).send('No projectId');
}
const ip = request.clientIp;
const ua = request.headers['user-agent']!;
const uaInfo = parseUserAgent(ua, payload.properties);
const geo = await getGeoLocation(ip);
await upsertProfile({
...payload,
id: payload.profileId,
isExternal: true,
projectId,
properties: {
...(payload.properties ?? {}),
country: geo.country,
city: geo.city,
region: geo.region,
longitude: geo.longitude,
latitude: geo.latitude,
os: uaInfo.os,
os_version: uaInfo.osVersion,
browser: uaInfo.browser,
browser_version: uaInfo.browserVersion,
device: uaInfo.device,
brand: uaInfo.brand,
model: uaInfo.model,
},
});
reply.status(202).send(payload.profileId);
}
export async function incrementProfileProperty(
request: FastifyRequest<{
Body: IncrementProfilePayload;
}>,
reply: FastifyReply,
) {
const { profileId, property, value } = request.body;
const projectId = request.client!.projectId;
if (!projectId) {
return reply.status(400).send('No projectId');
}
const profile = await getProfileById(profileId, projectId);
if (!profile) {
return reply.status(404).send('Not found');
}
const parsed = Number.parseInt(
pathOr<string>('0', property.split('.'), profile.properties),
10,
);
if (Number.isNaN(parsed)) {
return reply.status(400).send('Not number');
}
profile.properties = assocPath(
property.split('.'),
parsed + value,
profile.properties,
);
await upsertProfile({
id: profile.id,
projectId,
properties: profile.properties,
isExternal: true,
});
reply.status(202).send(profile.id);
}
export async function decrementProfileProperty(
request: FastifyRequest<{
Body: IncrementProfilePayload;
}>,
reply: FastifyReply,
) {
const { profileId, property, value } = request.body;
const projectId = request.client?.projectId;
if (!projectId) {
return reply.status(400).send('No projectId');
}
const profile = await getProfileById(profileId, projectId);
if (!profile) {
return reply.status(404).send('Not found');
}
const parsed = Number.parseInt(
pathOr<string>('0', property.split('.'), profile.properties),
10,
);
if (Number.isNaN(parsed)) {
return reply.status(400).send('Not number');
}
profile.properties = assocPath(
property.split('.'),
parsed - value,
profile.properties,
);
await upsertProfile({
id: profile.id,
projectId,
properties: profile.properties,
isExternal: true,
});
reply.status(202).send(profile.id);
}