update visitId hash and expiration logic
This commit is contained in:
parent
d3ca856521
commit
0aaf2c0b3b
|
@ -1,9 +1,17 @@
|
||||||
-- AlterTable
|
-- AlterTable
|
||||||
ALTER TABLE "website_event" ADD COLUMN "visit_id" UUID NULL;
|
ALTER TABLE "website_event" ADD COLUMN "visit_id" UUID NULL;
|
||||||
|
|
||||||
UPDATE "website_event"
|
UPDATE "website_event" we
|
||||||
SET visit_id = uuid_in(overlay(overlay(md5(CONCAT(session_id::text, to_char(date_trunc('hour', created_at), 'YYYY-MM-DD HH24:00:00'))) placing '4' from 13) placing '8' from 17)::cstring)
|
SET visit_id = a.uuid
|
||||||
WHERE visit_id IS NULL;
|
FROM (SELECT DISTINCT
|
||||||
|
s.session_id,
|
||||||
|
s.visit_time,
|
||||||
|
gen_random_uuid() uuid
|
||||||
|
FROM (SELECT DISTINCT session_id,
|
||||||
|
date_trunc('hour', created_at) visit_time
|
||||||
|
FROM "website_event") s) a
|
||||||
|
WHERE we.session_id = a.session_id
|
||||||
|
and date_trunc('hour', we.created_at) = a.visit_time;
|
||||||
|
|
||||||
ALTER TABLE "website_event" ALTER COLUMN "visit_id" SET NOT NULL;
|
ALTER TABLE "website_event" ALTER COLUMN "visit_id" SET NOT NULL;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { startOfMonth } from 'date-fns';
|
import { startOfHour, startOfMonth } from 'date-fns';
|
||||||
import { hash } from 'next-basics';
|
import { hash } from 'next-basics';
|
||||||
import { v4, v5, validate } from 'uuid';
|
import { v4, v5, validate } from 'uuid';
|
||||||
|
|
||||||
|
@ -12,6 +12,12 @@ export function salt() {
|
||||||
return hash(secret(), ROTATING_SALT);
|
return hash(secret(), ROTATING_SALT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function sessionSalt() {
|
||||||
|
const ROTATING_SALT = hash(startOfHour(new Date()).toUTCString());
|
||||||
|
|
||||||
|
return hash(secret(), ROTATING_SALT);
|
||||||
|
}
|
||||||
|
|
||||||
export function uuid(...args: any) {
|
export function uuid(...args: any) {
|
||||||
if (!args.length) return v4();
|
if (!args.length) return v4();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { isUuid, secret, uuid } from 'lib/crypto';
|
import { isUuid, secret, sessionSalt, uuid } from 'lib/crypto';
|
||||||
import { getClientInfo } from 'lib/detect';
|
import { getClientInfo } from 'lib/detect';
|
||||||
import { parseToken } from 'next-basics';
|
import { parseToken } from 'next-basics';
|
||||||
import { NextApiRequestCollect } from 'pages/api/send';
|
import { NextApiRequestCollect } from 'pages/api/send';
|
||||||
|
@ -10,6 +10,7 @@ import { loadSession, loadWebsite } from './load';
|
||||||
export async function findSession(req: NextApiRequestCollect): Promise<{
|
export async function findSession(req: NextApiRequestCollect): Promise<{
|
||||||
id: any;
|
id: any;
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
|
visitId: string;
|
||||||
hostname: string;
|
hostname: string;
|
||||||
browser: string;
|
browser: string;
|
||||||
os: any;
|
os: any;
|
||||||
|
@ -67,12 +68,14 @@ export async function findSession(req: NextApiRequestCollect): Promise<{
|
||||||
await getClientInfo(req, payload);
|
await getClientInfo(req, payload);
|
||||||
|
|
||||||
const sessionId = uuid(websiteId, hostname, ip, userAgent);
|
const sessionId = uuid(websiteId, hostname, ip, userAgent);
|
||||||
|
const visitId = uuid(sessionId, sessionSalt());
|
||||||
|
|
||||||
// Clickhouse does not require session lookup
|
// Clickhouse does not require session lookup
|
||||||
if (clickhouse.enabled) {
|
if (clickhouse.enabled) {
|
||||||
return {
|
return {
|
||||||
id: sessionId,
|
id: sessionId,
|
||||||
websiteId,
|
websiteId,
|
||||||
|
visitId,
|
||||||
hostname,
|
hostname,
|
||||||
browser,
|
browser,
|
||||||
os: os as any,
|
os: os as any,
|
||||||
|
@ -114,7 +117,7 @@ export async function findSession(req: NextApiRequestCollect): Promise<{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...session, ownerId: website.userId };
|
return { ...session, ownerId: website.userId, visitId: visitId };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkUserBlock(userId: string) {
|
async function checkUserBlock(userId: string) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import ipaddr from 'ipaddr.js';
|
import ipaddr from 'ipaddr.js';
|
||||||
import { isbot } from 'isbot';
|
import { isbot } from 'isbot';
|
||||||
import { COLLECTION_TYPE, HOSTNAME_REGEX, IP_REGEX } from 'lib/constants';
|
import { COLLECTION_TYPE, HOSTNAME_REGEX, IP_REGEX } from 'lib/constants';
|
||||||
import { secret } from 'lib/crypto';
|
import { secret, sessionSalt, uuid } from 'lib/crypto';
|
||||||
import { getIpAddress } from 'lib/detect';
|
import { getIpAddress } from 'lib/detect';
|
||||||
import { useCors, useSession, useValidate } from 'lib/middleware';
|
import { useCors, useSession, useValidate } from 'lib/middleware';
|
||||||
import { CollectionType, YupRequest } from 'lib/types';
|
import { CollectionType, YupRequest } from 'lib/types';
|
||||||
|
@ -31,6 +31,7 @@ export interface NextApiRequestCollect extends NextApiRequest {
|
||||||
session: {
|
session: {
|
||||||
id: string;
|
id: string;
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
|
visitId: string;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
hostname: string;
|
hostname: string;
|
||||||
browser: string;
|
browser: string;
|
||||||
|
@ -93,6 +94,14 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
|
||||||
|
|
||||||
const session = req.session;
|
const session = req.session;
|
||||||
|
|
||||||
|
// expire visitId after 30 minutes
|
||||||
|
session.visitId =
|
||||||
|
!!session.iat && Math.floor(new Date().getTime() / 1000) - session.iat > 1800
|
||||||
|
? uuid(session.id, sessionSalt())
|
||||||
|
: session.visitId;
|
||||||
|
|
||||||
|
session.iat = Math.floor(new Date().getTime() / 1000);
|
||||||
|
|
||||||
if (type === COLLECTION_TYPE.event) {
|
if (type === COLLECTION_TYPE.event) {
|
||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
let [urlPath, urlQuery] = url?.split('?') || [];
|
let [urlPath, urlQuery] = url?.split('?') || [];
|
||||||
|
@ -125,6 +134,7 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
|
||||||
eventData,
|
eventData,
|
||||||
...session,
|
...session,
|
||||||
sessionId: session.id,
|
sessionId: session.id,
|
||||||
|
visitId: session.visitId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,9 @@ import { uuid } from 'lib/crypto';
|
||||||
import { saveEventData } from 'queries/analytics/eventData/saveEventData';
|
import { saveEventData } from 'queries/analytics/eventData/saveEventData';
|
||||||
|
|
||||||
export async function saveEvent(args: {
|
export async function saveEvent(args: {
|
||||||
sessionId: string;
|
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
|
sessionId: string;
|
||||||
|
visitId: string;
|
||||||
urlPath: string;
|
urlPath: string;
|
||||||
urlQuery?: string;
|
urlQuery?: string;
|
||||||
referrerPath?: string;
|
referrerPath?: string;
|
||||||
|
@ -34,8 +35,9 @@ export async function saveEvent(args: {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function relationalQuery(data: {
|
async function relationalQuery(data: {
|
||||||
sessionId: string;
|
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
|
sessionId: string;
|
||||||
|
visitId: string;
|
||||||
urlPath: string;
|
urlPath: string;
|
||||||
urlQuery?: string;
|
urlQuery?: string;
|
||||||
referrerPath?: string;
|
referrerPath?: string;
|
||||||
|
@ -48,6 +50,7 @@ async function relationalQuery(data: {
|
||||||
const {
|
const {
|
||||||
websiteId,
|
websiteId,
|
||||||
sessionId,
|
sessionId,
|
||||||
|
visitId,
|
||||||
urlPath,
|
urlPath,
|
||||||
urlQuery,
|
urlQuery,
|
||||||
referrerPath,
|
referrerPath,
|
||||||
|
@ -64,6 +67,7 @@ async function relationalQuery(data: {
|
||||||
id: websiteEventId,
|
id: websiteEventId,
|
||||||
websiteId,
|
websiteId,
|
||||||
sessionId,
|
sessionId,
|
||||||
|
visitId,
|
||||||
urlPath: urlPath?.substring(0, URL_LENGTH),
|
urlPath: urlPath?.substring(0, URL_LENGTH),
|
||||||
urlQuery: urlQuery?.substring(0, URL_LENGTH),
|
urlQuery: urlQuery?.substring(0, URL_LENGTH),
|
||||||
referrerPath: referrerPath?.substring(0, URL_LENGTH),
|
referrerPath: referrerPath?.substring(0, URL_LENGTH),
|
||||||
|
@ -90,8 +94,9 @@ async function relationalQuery(data: {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clickhouseQuery(data: {
|
async function clickhouseQuery(data: {
|
||||||
sessionId: string;
|
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
|
sessionId: string;
|
||||||
|
visitId: string;
|
||||||
urlPath: string;
|
urlPath: string;
|
||||||
urlQuery?: string;
|
urlQuery?: string;
|
||||||
referrerPath?: string;
|
referrerPath?: string;
|
||||||
|
@ -114,6 +119,7 @@ async function clickhouseQuery(data: {
|
||||||
const {
|
const {
|
||||||
websiteId,
|
websiteId,
|
||||||
sessionId,
|
sessionId,
|
||||||
|
visitId,
|
||||||
urlPath,
|
urlPath,
|
||||||
urlQuery,
|
urlQuery,
|
||||||
referrerPath,
|
referrerPath,
|
||||||
|
@ -136,6 +142,7 @@ async function clickhouseQuery(data: {
|
||||||
...args,
|
...args,
|
||||||
website_id: websiteId,
|
website_id: websiteId,
|
||||||
session_id: sessionId,
|
session_id: sessionId,
|
||||||
|
visit_id: visitId,
|
||||||
event_id: uuid(),
|
event_id: uuid(),
|
||||||
country: country,
|
country: country,
|
||||||
subdivision1:
|
subdivision1:
|
||||||
|
|
Loading…
Reference in New Issue