Feat/um 305 unique session ch (#2065)

* Add session_data / session redis to CH.

* Add mysql migration.
This commit is contained in:
Brian Cao 2023-05-31 21:46:49 -07:00 committed by GitHub
parent 1038a54fe4
commit b484286523
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 405 additions and 300 deletions

View file

@ -7,6 +7,9 @@ import { getJsonBody, getIpAddress } from 'lib/detect';
import { secret } from 'lib/crypto';
import { NextApiRequest, NextApiResponse } from 'next';
import { Resolver } from 'dns/promises';
import { CollectionType } from 'lib/types';
import { COLLECTION_TYPE } from 'lib/constants';
import { saveSessionData } from 'queries/analytics/session/saveSessionData';
export interface CollectRequestBody {
payload: {
@ -20,7 +23,7 @@ export interface CollectRequestBody {
website: string;
name: string;
};
type: string;
type: CollectionType;
}
export interface NextApiRequestCollect extends NextApiRequest {
@ -52,17 +55,81 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
const { type, payload } = getJsonBody<CollectRequestBody>(req);
if (type !== 'event') {
validateBody(res, { type, payload });
if (await hasBlockedIp(req)) {
return forbidden(res);
}
const { url, referrer, name: eventName, data: dynamicData, title: pageTitle } = payload;
await useSession(req, res);
const session = req.session;
if (type === COLLECTION_TYPE.event) {
// eslint-disable-next-line prefer-const
let [urlPath, urlQuery] = url?.split('?') || [];
let [referrerPath, referrerQuery] = referrer?.split('?') || [];
let referrerDomain;
if (!urlPath) {
urlPath = '/';
}
if (referrerPath?.startsWith('http')) {
const refUrl = new URL(referrer);
referrerPath = refUrl.pathname;
referrerQuery = refUrl.search.substring(1);
referrerDomain = refUrl.hostname.replace(/www\./, '');
}
if (process.env.REMOVE_TRAILING_SLASH) {
urlPath = urlPath.replace(/.+\/$/, '');
}
await saveEvent({
urlPath,
urlQuery,
referrerPath,
referrerQuery,
referrerDomain,
pageTitle,
eventName,
eventData: dynamicData,
...session,
sessionId: session.id,
});
}
if (type === COLLECTION_TYPE.identify) {
if (!dynamicData) {
return badRequest(res, 'Data required.');
}
await saveSessionData({ ...session, sessionData: dynamicData, sessionId: session.id });
}
const token = createToken(session, secret());
return send(res, token);
};
function validateBody(res: NextApiResponse, { type, payload }: CollectRequestBody) {
const { data } = payload;
// Validate type
if (type !== COLLECTION_TYPE.event && type !== COLLECTION_TYPE.identify) {
return badRequest(res, 'Wrong payload type.');
}
const { url, referrer, name: eventName, data: eventData, title: pageTitle } = payload;
// Validate eventData is JSON
if (eventData && !(typeof eventData === 'object' && !Array.isArray(eventData))) {
if (data && !(typeof data === 'object' && !Array.isArray(data))) {
return badRequest(res, 'Invalid event data.');
}
}
async function hasBlockedIp(req: NextApiRequestCollect) {
const ignoreIps = process.env.IGNORE_IP;
const ignoreHostnames = process.env.IGNORE_HOSTNAME;
@ -100,49 +167,6 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
return false;
});
if (blocked) {
return forbidden(res);
}
return blocked;
}
await useSession(req, res);
const session = req.session;
// eslint-disable-next-line prefer-const
let [urlPath, urlQuery] = url?.split('?') || [];
let [referrerPath, referrerQuery] = referrer?.split('?') || [];
let referrerDomain;
if (!urlPath) {
urlPath = '/';
}
if (referrerPath?.startsWith('http')) {
const refUrl = new URL(referrer);
referrerPath = refUrl.pathname;
referrerQuery = refUrl.search.substring(1);
referrerDomain = refUrl.hostname.replace(/www\./, '');
}
if (process.env.REMOVE_TRAILING_SLASH) {
urlPath = urlPath.replace(/.+\/$/, '');
}
await saveEvent({
urlPath,
urlQuery,
referrerPath,
referrerQuery,
referrerDomain,
pageTitle,
eventName,
eventData,
...session,
sessionId: session.id,
});
const token = createToken(session, secret());
return send(res, token);
};
}

View file

@ -1,4 +1,4 @@
import { NextApiRequestQueryBody, Roles, User } from 'lib/types';
import { NextApiRequestQueryBody, Role, User } from 'lib/types';
import { canDeleteUser, canUpdateUser, canViewUser } from 'lib/auth';
import { useAuth } from 'lib/middleware';
import { NextApiResponse } from 'next';
@ -12,7 +12,7 @@ export interface UserRequestQuery {
export interface UserRequestBody {
username: string;
password: string;
role: Roles;
role: Role;
}
export default async (

View file

@ -2,7 +2,7 @@ import { canCreateUser, canViewUsers } from 'lib/auth';
import { ROLES } from 'lib/constants';
import { uuid } from 'lib/crypto';
import { useAuth } from 'lib/middleware';
import { NextApiRequestQueryBody, Roles, User } from 'lib/types';
import { NextApiRequestQueryBody, Role, User } from 'lib/types';
import { NextApiResponse } from 'next';
import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics';
import { createUser, getUser, getUsers } from 'queries';
@ -11,7 +11,7 @@ export interface UsersRequestBody {
username: string;
password: string;
id: string;
role?: Roles;
role?: Role;
}
export default async (