identify with track
This commit is contained in:
@@ -2,13 +2,13 @@ import type { GeoLocation } from '@/utils/parseIp';
|
|||||||
import { getClientIp, parseIp } from '@/utils/parseIp';
|
import { getClientIp, parseIp } from '@/utils/parseIp';
|
||||||
import { parseUserAgent } from '@/utils/parseUserAgent';
|
import { parseUserAgent } from '@/utils/parseUserAgent';
|
||||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||||
import { assocPath, pathOr, pick } from 'ramda';
|
import { assocPath, path, pathOr, pick } from 'ramda';
|
||||||
|
|
||||||
import { generateDeviceId } from '@openpanel/common';
|
import { generateDeviceId } from '@openpanel/common';
|
||||||
import {
|
import {
|
||||||
createProfileAlias,
|
createProfileAlias,
|
||||||
getProfileById,
|
getProfileById,
|
||||||
getProfileId,
|
getProfileIdCached,
|
||||||
getSalts,
|
getSalts,
|
||||||
upsertProfile,
|
upsertProfile,
|
||||||
} from '@openpanel/db';
|
} from '@openpanel/db';
|
||||||
@@ -42,32 +42,53 @@ export function getStringHeaders(headers: FastifyRequest['headers']) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getIdentity(body: TrackHandlerPayload): IdentifyPayload | undefined {
|
||||||
|
const identity = path<IdentifyPayload>(
|
||||||
|
['properties', '__identify'],
|
||||||
|
body.payload
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
identity ||
|
||||||
|
(body.payload.profileId
|
||||||
|
? {
|
||||||
|
profileId: body.payload.profileId,
|
||||||
|
}
|
||||||
|
: undefined)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export async function handler(
|
export async function handler(
|
||||||
request: FastifyRequest<{
|
request: FastifyRequest<{
|
||||||
Body: TrackHandlerPayload;
|
Body: TrackHandlerPayload;
|
||||||
}>,
|
}>,
|
||||||
reply: FastifyReply
|
reply: FastifyReply
|
||||||
) {
|
) {
|
||||||
const ip = getClientIp(request)!;
|
const ip =
|
||||||
|
path<string>(['properties', '__ip'], request.body.payload) ||
|
||||||
|
getClientIp(request)!;
|
||||||
const ua = request.headers['user-agent']!;
|
const ua = request.headers['user-agent']!;
|
||||||
const projectId = request.client?.projectId;
|
const projectId = request.client?.projectId;
|
||||||
const profileId =
|
|
||||||
projectId && request.body.payload.profileId
|
|
||||||
? await getProfileId({
|
|
||||||
projectId,
|
|
||||||
profileId: request.body.payload.profileId,
|
|
||||||
})
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
if (profileId) {
|
|
||||||
request.body.payload.profileId = profileId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!projectId) {
|
if (!projectId) {
|
||||||
reply.status(400).send('missing origin');
|
reply.status(400).send('missing origin');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const identity = getIdentity(request.body);
|
||||||
|
const profileId = identity?.profileId
|
||||||
|
? await getProfileIdCached({
|
||||||
|
projectId,
|
||||||
|
profileId: identity?.profileId,
|
||||||
|
})
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
// We might get a profileId from the alias table
|
||||||
|
// If we do, we should use that instead of the one from the payload
|
||||||
|
if (profileId) {
|
||||||
|
request.body.payload.profileId = profileId;
|
||||||
|
}
|
||||||
|
|
||||||
switch (request.body.type) {
|
switch (request.body.type) {
|
||||||
case 'track': {
|
case 'track': {
|
||||||
const [salts, geo] = await Promise.all([getSalts(), parseIp(ip)]);
|
const [salts, geo] = await Promise.all([getSalts(), parseIp(ip)]);
|
||||||
@@ -87,14 +108,32 @@ export async function handler(
|
|||||||
ua,
|
ua,
|
||||||
})
|
})
|
||||||
: '';
|
: '';
|
||||||
await track({
|
|
||||||
payload: request.body.payload,
|
const promises = [
|
||||||
currentDeviceId,
|
track({
|
||||||
previousDeviceId,
|
payload: request.body.payload,
|
||||||
projectId,
|
currentDeviceId,
|
||||||
geo,
|
previousDeviceId,
|
||||||
headers: getStringHeaders(request.headers),
|
projectId,
|
||||||
});
|
geo,
|
||||||
|
headers: getStringHeaders(request.headers),
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
// If we have more than one property in the identity object, we should identify the user
|
||||||
|
// Otherwise its only a profileId and we should not identify the user
|
||||||
|
if (identity && Object.keys(identity).length > 1) {
|
||||||
|
promises.push(
|
||||||
|
identify({
|
||||||
|
payload: identity,
|
||||||
|
projectId,
|
||||||
|
geo,
|
||||||
|
ua,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'identify': {
|
case 'identify': {
|
||||||
|
|||||||
@@ -21,6 +21,25 @@ Sets global properties that will be included with every subsequent event.
|
|||||||
|
|
||||||
Tracks a custom event with the given name and optional properties.
|
Tracks a custom event with the given name and optional properties.
|
||||||
|
|
||||||
|
**Tips**
|
||||||
|
|
||||||
|
You can identify the user directly with this method.
|
||||||
|
|
||||||
|
```js filename="Example shown in JavaScript"
|
||||||
|
track('your_event_name', {
|
||||||
|
foo: 'bar',
|
||||||
|
baz: 'qux',
|
||||||
|
// reserved property name
|
||||||
|
__identify: {
|
||||||
|
profileId: 'your_user_id', // required
|
||||||
|
email: 'your_user_email',
|
||||||
|
firstName: 'your_user_name',
|
||||||
|
lastName: 'your_user_name',
|
||||||
|
avatar: 'your_user_avatar',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### identify
|
### identify
|
||||||
|
|
||||||
Associates the current user with a unique identifier and optional traits.
|
Associates the current user with a unique identifier and optional traits.
|
||||||
|
|||||||
Reference in New Issue
Block a user