sdk: add express sdk

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-03-31 15:19:03 +02:00
parent fdc035d321
commit edcc08e1df
6 changed files with 154 additions and 17 deletions

View File

@@ -52,6 +52,8 @@ function createContextLogger(request: FastifyRequest) {
};
}
const GLOBAL_PROPERTIES = ['__path', '__referrer'];
export async function postEvent(
request: FastifyRequest<{
Body: PostEventPayload;
@@ -84,7 +86,7 @@ export async function postEvent(
const origin = request.headers.origin!;
const ua = request.headers['user-agent']!;
const uaInfo = parseUserAgent(ua);
const salts = await getSalts();
const [geo, salts] = await Promise.all([parseIp(ip), getSalts()]);
const currentDeviceId = generateDeviceId({
salt: salts.current,
origin,
@@ -114,18 +116,11 @@ export async function postEvent(
sessionId: event?.sessionId || '',
profileId,
projectId,
properties: Object.assign(
{},
omit(['__path', '__referrer'], properties),
{
hash,
query,
}
),
properties: Object.assign({}, omit(GLOBAL_PROPERTIES, properties)),
createdAt,
country: event?.country ?? '',
city: event?.city ?? '',
region: event?.region ?? '',
country: event?.country || geo.country || '',
city: event?.city || geo.city || '',
region: event?.region || geo.region || '',
continent: event?.continent ?? '',
os: event?.os ?? '',
osVersion: event?.osVersion ?? '',
@@ -164,11 +159,10 @@ export async function postEvent(
return reply.status(200).send('');
}
const [geo, sessionEndJobCurrentDeviceId, sessionEndJobPreviousDeviceId] =
const [sessionEndJobCurrentDeviceId, sessionEndJobPreviousDeviceId] =
await withTiming(
'Get geo and jobs from queue',
Promise.all([
parseIp(ip),
findJobByPrefix(
eventsQueue,
`sessionEnd:${projectId}:${currentDeviceId}:`
@@ -225,9 +219,9 @@ export async function postEvent(
profileId,
projectId,
sessionId: createSessionStart ? uuid() : sessionStartEvent?.sessionId ?? '',
properties: Object.assign({}, omit(['__path', '__referrer'], properties), {
hash,
query,
properties: Object.assign({}, omit(GLOBAL_PROPERTIES, properties), {
__hash: hash,
__query: query,
}),
createdAt,
country: geo.country,

View File

@@ -0,0 +1,50 @@
import type { NextFunction, Request, Response } from 'express';
import { getClientIp } from 'request-ip';
import type { OpenpanelSdkOptions } from '@openpanel/sdk';
import { OpenpanelSdk } from '@openpanel/sdk';
export * from '@openpanel/sdk';
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Express {
export interface Request {
op: OpenpanelSdk;
}
}
}
export type OpenpanelOptions = OpenpanelSdkOptions & {
trackRequest?: (url: string) => boolean;
getProfileId?: (req: Request) => string;
};
export default function createMiddleware(options: OpenpanelOptions) {
return function middleware(req: Request, res: Response, next: NextFunction) {
const sdk = new OpenpanelSdk(options);
const ip = getClientIp(req);
if (ip) {
sdk.api.headers['x-forwarded-for'] = ip;
}
if (options.getProfileId) {
const profileId = options.getProfileId(req);
if (profileId) {
sdk.setProfileId(profileId);
}
}
if (options.trackRequest?.(req.url)) {
sdk.event('request', {
url: req.url,
method: req.method,
query: req.query,
});
}
req.op = sdk;
return next();
};
}

View File

@@ -0,0 +1,36 @@
{
"name": "@openpanel/express",
"version": "0.0.1",
"module": "index.ts",
"scripts": {
"build": "rm -rf dist && tsup",
"lint": "eslint .",
"format": "prettier --check \"**/*.{mjs,ts,md,json}\"",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@openpanel/sdk": "workspace:*",
"request-ip": "^3.3.0"
},
"peerDependencies": {
"express": "^3.0.0 || ^4.0.0"
},
"devDependencies": {
"@openpanel/eslint-config": "workspace:*",
"@openpanel/prettier-config": "workspace:*",
"@openpanel/tsconfig": "workspace:*",
"@types/express": "^4.17.21",
"@types/request-ip": "^0.0.41",
"eslint": "^8.48.0",
"prettier": "^3.0.3",
"tsup": "^7.2.0",
"typescript": "^5.2.2"
},
"eslintConfig": {
"root": true,
"extends": [
"@openpanel/eslint-config/base"
]
},
"prettier": "@openpanel/prettier-config"
}

View File

@@ -0,0 +1,8 @@
{
"extends": "@openpanel/tsconfig/base.json",
"compilerOptions": {
"incremental": false,
"outDir": "dist"
},
"exclude": ["dist"]
}

View File

@@ -0,0 +1,9 @@
import { defineConfig } from 'tsup';
import config from '@openpanel/tsconfig/tsup.config.json' assert { type: 'json' };
export default defineConfig({
...(config as any),
entry: ['index.ts', 'cdn.ts'],
format: ['cjs', 'esm', 'iife'],
});

40
pnpm-lock.yaml generated
View File

@@ -891,6 +891,46 @@ importers:
specifier: ^5.2.2
version: 5.3.3
packages/sdks/express:
dependencies:
'@openpanel/sdk':
specifier: workspace:*
version: link:../sdk
express:
specifier: ^3.0.0 || ^4.0.0
version: 4.18.2
request-ip:
specifier: ^3.3.0
version: 3.3.0
devDependencies:
'@openpanel/eslint-config':
specifier: workspace:*
version: link:../../../tooling/eslint
'@openpanel/prettier-config':
specifier: workspace:*
version: link:../../../tooling/prettier
'@openpanel/tsconfig':
specifier: workspace:*
version: link:../../../tooling/typescript
'@types/express':
specifier: ^4.17.21
version: 4.17.21
'@types/request-ip':
specifier: ^0.0.41
version: 0.0.41
eslint:
specifier: ^8.48.0
version: 8.56.0
prettier:
specifier: ^3.0.3
version: 3.2.5
tsup:
specifier: ^7.2.0
version: 7.3.0(typescript@5.3.3)
typescript:
specifier: ^5.2.2
version: 5.3.3
packages/sdks/nextjs:
dependencies:
'@openpanel/web':