diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 7dcc8c1..f9a3247 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -106,7 +106,6 @@ model WebsiteEvent { id String @id() @default(uuid()) @db.Uuid websiteId String @db.Uuid sessionId String @db.Uuid - createdAt DateTime? @default(now()) @db.Timestamptz(6) urlPath String @db.VarChar(500) urlQuery String? @db.VarChar(500) referrerPath String? @db.VarChar(500) @@ -115,6 +114,7 @@ model WebsiteEvent { pageTitle String? @db.VarChar(500) eventType Int @default(1) @db.Integer eventName String? @db.VarChar(50) + createdAt DateTime? @default(now()) @db.Timestamptz(6) eventData WebsiteEventData[] session WebsiteSession @relation(fields: [sessionId], references: [id]) @@ -173,6 +173,7 @@ model WebsiteSessionData { model TelemetrySession { id String @id @unique @db.Uuid + workspaceId String @db.Uuid hostname String? @db.VarChar(100) browser String? @db.VarChar(20) os String? @db.VarChar(20) @@ -185,17 +186,22 @@ model TelemetrySession { telemetryEvent TelemetryEvent[] @@index([createdAt]) + @@index([workspaceId, createdAt]) } model TelemetryEvent { - id String @id() @default(uuid()) @db.Uuid - sessionId String @db.Uuid - createdAt DateTime? @default(now()) @db.Timestamptz(6) - urlOrigin String @db.VarChar(500) - urlPath String @db.VarChar(500) + id String @id() @default(uuid()) @db.Uuid + sessionId String @db.Uuid + workspaceId String @db.Uuid + eventName String? @db.VarChar(100) + urlOrigin String @db.VarChar(500) + urlPath String @db.VarChar(500) + createdAt DateTime? @default(now()) @db.Timestamptz(6) session TelemetrySession @relation(fields: [sessionId], references: [id]) @@index([createdAt]) @@index([sessionId]) + @@index([workspaceId]) + @@index([workspaceId, createdAt]) } diff --git a/src/server/model/telemetry.ts b/src/server/model/telemetry.ts index a5b9926..72e4a97 100644 --- a/src/server/model/telemetry.ts +++ b/src/server/model/telemetry.ts @@ -9,17 +9,25 @@ export async function recordTelemetryEvent(req: Request) { if (!(url && typeof url === 'string')) { return; } + const eventName = req.query.name ? String(req.query.name) : undefined; const session = await findSession(req, url); if (!session) { return; } + const workspaceId = req.params.workspaceId; + if (!workspaceId) { + return; + } + const { origin, pathname } = new URL(url); await prisma.telemetryEvent.create({ data: { sessionId: session.id, + workspaceId, + eventName, urlOrigin: origin, urlPath: pathname, }, @@ -32,10 +40,19 @@ export async function sumTelemetryEvent(req: Request): Promise { return 0; } + const eventName = req.query.name ? String(req.query.name) : undefined; + + const workspaceId = req.params.workspaceId; + if (!workspaceId) { + return 0; + } + const { origin, pathname } = new URL(url); const number = await prisma.telemetryEvent.count({ where: { + workspaceId, + eventName, urlOrigin: origin, urlPath: pathname, }, @@ -46,6 +63,10 @@ export async function sumTelemetryEvent(req: Request): Promise { async function findSession(req: Request, url: string) { const { hostname } = new URL(url); + const workspaceId = req.params.workspaceId; + if (!workspaceId) { + throw new Error('Not found workspaceId'); + } const { userAgent, @@ -58,7 +79,7 @@ async function findSession(req: Request, url: string) { city, } = await getRequestInfo(req); - const sessionId = hashUuid(hostname, ip, userAgent!); + const sessionId = hashUuid(workspaceId, hostname, ip, userAgent!); let session = await loadSession(sessionId); if (!session) { @@ -66,6 +87,7 @@ async function findSession(req: Request, url: string) { session = await prisma.telemetrySession.create({ data: { id: sessionId, + workspaceId, hostname, browser, os, diff --git a/src/server/model/workspace.ts b/src/server/model/workspace.ts index c21c73b..469322c 100644 --- a/src/server/model/workspace.ts +++ b/src/server/model/workspace.ts @@ -1,7 +1,6 @@ import { prisma } from './_client'; import { QueryFilters, parseFilters, getDateQuery } from '../utils/prisma'; import { DEFAULT_RESET_DATE, EVENT_TYPE } from '../utils/const'; -import { Prisma } from '@prisma/client'; export async function getWorkspaceUser(workspaceId: string, userId: string) { const info = await prisma.workspacesOnUsers.findFirst({ @@ -27,6 +26,14 @@ export async function checkIsWorkspaceUser( } } +export async function getWorkspace(workspaceId: string) { + return prisma.workspace.findUnique({ + where: { + id: workspaceId, + }, + }); +} + export async function getWorkspaceWebsites(workspaceId: string) { const workspace = await prisma.workspace.findUnique({ where: { diff --git a/src/server/router/telemetry.ts b/src/server/router/telemetry.ts index 85c3c01..8c9be5f 100644 --- a/src/server/router/telemetry.ts +++ b/src/server/router/telemetry.ts @@ -1,42 +1,57 @@ import { Router } from 'express'; +import { query, validate } from '../middleware/validate'; import { recordTelemetryEvent, sumTelemetryEvent } from '../model/telemetry'; import { numify } from '../utils/common'; const openBadge = require('openbadge'); export const telemetryRouter = Router(); -telemetryRouter.get('/blank.gif', async (req, res) => { - const buffer = Buffer.from( - 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7', - 'base64' - ); - - recordTelemetryEvent(req); - - res.header('Content-Type', 'image/gif').send(buffer); -}); - -telemetryRouter.get('/badge.svg', async (req, res) => { - const title = req.query.title || 'visitor'; - const start = req.query.start ? Number(req.query.start) : 0; - - recordTelemetryEvent(req); - const num = await sumTelemetryEvent(req); - - const svg = await new Promise((resolve, reject) => { - openBadge( - { - text: [title, numify(num + start)], - }, - (err: any, badgeSvg: string) => { - if (err) { - reject(err); - } else { - resolve(badgeSvg); - } - } +telemetryRouter.get( + '/:workspaceId/blank.gif', + validate( + query('name').optional().isString(), + query('url').optional().isURL() + ), + async (req, res) => { + const buffer = Buffer.from( + 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7', + 'base64' ); - }); - res.header('Content-Type', 'image/svg+xml').send(svg); -}); + recordTelemetryEvent(req); + + res.header('Content-Type', 'image/gif').send(buffer); + } +); + +telemetryRouter.get( + '/:workspaceId/badge.svg', + validate( + query('name').optional().isString(), + query('url').optional().isURL() + ), + async (req, res) => { + const title = req.query.title || 'visitor'; + const start = req.query.start ? Number(req.query.start) : 0; + + recordTelemetryEvent(req); + const num = await sumTelemetryEvent(req); + + const svg = await new Promise((resolve, reject) => { + openBadge( + { + text: [title, numify(num + start)], + }, + (err: any, badgeSvg: string) => { + if (err) { + reject(err); + } else { + resolve(badgeSvg); + } + } + ); + }); + + res.header('Content-Type', 'image/svg+xml').send(svg); + } +);