mirror of
https://github.com/umami-software/umami.git
synced 2026-02-07 14:17:13 +01:00
feat: implement identity stitching for session linking (#3820)
Adds automatic session linking/identity stitching to link anonymous browsing sessions with authenticated user sessions. ## Changes ### Database Schema - Add `identity_link` table (PostgreSQL + ClickHouse) to store mappings between visitor IDs and authenticated user IDs - Add `visitor_id` field to `Session` model - Add `visitor_id` column to ClickHouse `website_event` table ### Client Tracker - Generate and persist `visitor_id` in localStorage - Include `vid` in all tracking payloads - Support opt-out via `data-identity-stitching="false"` attribute ### API - Accept `vid` parameter in `/api/send` endpoint - Auto-create identity links when `identify()` is called with both visitor_id and distinct_id - Store visitor_id in sessions and events ### Query Updates - Update `getWebsiteStats` to deduplicate visitors by resolved identity - Visitors who browse anonymously then log in are now counted as one user ## Usage When a user logs in, call `umami.identify(userId)`. If identity stitching is enabled (default), the tracker automatically links the anonymous visitor_id to the authenticated userId. Stats queries then resolve linked identities to accurately count unique visitors. Resolves #3820
This commit is contained in:
parent
9a269ab811
commit
a902a87c08
11 changed files with 245 additions and 33 deletions
|
|
@ -43,6 +43,7 @@ model Session {
|
|||
region String? @db.VarChar(20)
|
||||
city String? @db.VarChar(50)
|
||||
distinctId String? @map("distinct_id") @db.VarChar(50)
|
||||
visitorId String? @map("visitor_id") @db.VarChar(50)
|
||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||
|
||||
websiteEvents WebsiteEvent[]
|
||||
|
|
@ -60,6 +61,7 @@ model Session {
|
|||
@@index([websiteId, createdAt, country])
|
||||
@@index([websiteId, createdAt, region])
|
||||
@@index([websiteId, createdAt, city])
|
||||
@@index([websiteId, visitorId])
|
||||
@@map("session")
|
||||
}
|
||||
|
||||
|
|
@ -76,14 +78,15 @@ model Website {
|
|||
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6)
|
||||
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6)
|
||||
|
||||
user User? @relation("user", fields: [userId], references: [id])
|
||||
createUser User? @relation("createUser", fields: [createdBy], references: [id])
|
||||
team Team? @relation(fields: [teamId], references: [id])
|
||||
eventData EventData[]
|
||||
reports Report[]
|
||||
revenue Revenue[]
|
||||
segments Segment[]
|
||||
sessionData SessionData[]
|
||||
user User? @relation("user", fields: [userId], references: [id])
|
||||
createUser User? @relation("createUser", fields: [createdBy], references: [id])
|
||||
team Team? @relation(fields: [teamId], references: [id])
|
||||
eventData EventData[]
|
||||
reports Report[]
|
||||
revenue Revenue[]
|
||||
segments Segment[]
|
||||
sessionData SessionData[]
|
||||
identityLinks IdentityLink[]
|
||||
|
||||
@@index([userId])
|
||||
@@index([teamId])
|
||||
|
|
@ -316,3 +319,18 @@ model Pixel {
|
|||
@@index([createdAt])
|
||||
@@map("pixel")
|
||||
}
|
||||
|
||||
model IdentityLink {
|
||||
id String @id @unique @map("identity_link_id") @db.Uuid
|
||||
websiteId String @map("website_id") @db.Uuid
|
||||
visitorId String @map("visitor_id") @db.VarChar(50)
|
||||
distinctId String @map("distinct_id") @db.VarChar(50)
|
||||
linkedAt DateTime @default(now()) @map("linked_at") @db.Timestamptz(6)
|
||||
|
||||
website Website @relation(fields: [websiteId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([websiteId, visitorId, distinctId])
|
||||
@@index([websiteId, distinctId])
|
||||
@@index([websiteId, visitorId])
|
||||
@@map("identity_link")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue