From 10723f41ab3413769645b4f77bbf9dc651a4e522 Mon Sep 17 00:00:00 2001 From: ziMu-66ccff <3553189793@qq.com> Date: Wed, 13 Dec 2023 16:47:55 +0800 Subject: [PATCH 1/2] fix performance data report --- tracker/src/core/core.ts | 19 ++++++++------ tracker/src/performance/getNT.ts | 20 +++++++-------- tracker/src/performance/getRF.ts | 34 -------------------------- tracker/src/performance/performance.ts | 12 +-------- 4 files changed, 22 insertions(+), 63 deletions(-) diff --git a/tracker/src/core/core.ts b/tracker/src/core/core.ts index b8b0330..29f1a06 100644 --- a/tracker/src/core/core.ts +++ b/tracker/src/core/core.ts @@ -84,14 +84,17 @@ export class Tracker { }); } - public report>(data: T) { - const params = Object.assign(data, { - appId: this.appId, - uid: this.uid, - extra: this.extra, - time: new Date().getTime(), - timeFormat: formatDate(new Date()), - }); + public report>(data: T, type?: string) { + const params = Object.assign( + { data, type: type ?? 'customize' }, + { + appId: this.appId, + uid: this.uid, + extra: this.extra, + time: new Date().getTime(), + timeFormat: formatDate(new Date()), + }, + ); const blob = new Blob([JSON.stringify(params)]); navigator.sendBeacon(this.options.requestUrl as string, blob); } diff --git a/tracker/src/performance/getNT.ts b/tracker/src/performance/getNT.ts index b9f340d..7fcd0e1 100644 --- a/tracker/src/performance/getNT.ts +++ b/tracker/src/performance/getNT.ts @@ -42,21 +42,16 @@ export function getNavigationTiming(): PerformanceNavigationTiming { end: responseEnd, value: responseEnd - responseStart, }, - DomParse: { - start: responseEnd, - end: domInteractive, - value: domInteractive - responseEnd, - }, - Res: { - start: domContentLoadedEventEnd, - end: loadEventStart, - value: loadEventStart - domContentLoadedEventEnd, - }, FP: { start: fetchStart, end: responseEnd, value: responseEnd - fetchStart, }, + DomParse: { + start: responseEnd, + end: domInteractive, + value: domInteractive - responseEnd, + }, TTI: { start: fetchStart, end: domInteractive, @@ -67,6 +62,11 @@ export function getNavigationTiming(): PerformanceNavigationTiming { end: domContentLoadedEventEnd, value: domContentLoadedEventEnd - fetchStart, }, + Res: { + start: responseEnd, + end: loadEventStart, + value: loadEventStart - responseEnd, + }, Load: { start: fetchStart, end: loadEventStart, diff --git a/tracker/src/performance/getRF.ts b/tracker/src/performance/getRF.ts index 40f281a..ef7a311 100644 --- a/tracker/src/performance/getRF.ts +++ b/tracker/src/performance/getRF.ts @@ -1,38 +1,4 @@ import { type ResourceFlowTiming } from '@/types/performance'; -// import { observe } from './observe'; - -// export function getResourceFlow(resourceFlow: ResourceFlowTiming[]) { -// const entryHandler = (entry: PerformanceResourceTiming) => { -// const { -// name, -// transferSize, -// initiatorType, -// startTime, -// responseEnd, -// domainLookupEnd, -// domainLookupStart, -// connectStart, -// connectEnd, -// secureConnectionStart, -// responseStart, -// requestStart, -// } = entry; - -// resourceFlow.push({ -// name, -// initiatorType, -// transferSize, -// start: startTime, -// end: responseEnd, -// DNS: domainLookupEnd - domainLookupStart, -// TCP: connectEnd - connectStart, -// SSL: connectEnd - secureConnectionStart, -// TTFB: responseStart - requestStart, -// Trans: responseEnd - requestStart, -// }); -// }; -// return observe('resource', entryHandler); -// } export function getResourceFlow(): ResourceFlowTiming[] { const resouceDatas = performance.getEntriesByType('resource'); diff --git a/tracker/src/performance/performance.ts b/tracker/src/performance/performance.ts index 18e1d73..23868c2 100644 --- a/tracker/src/performance/performance.ts +++ b/tracker/src/performance/performance.ts @@ -80,16 +80,6 @@ export class PerformanceTracker { } private initResourceFlow() { - // const resouceFlow: ResourceFlowTiming[] = []; - // const resouceObserver = getResourceFlow(resouceFlow); - - // const stopListeningAndInitRF = () => { - // if (resouceObserver) resouceObserver.disconnect(); - // this.data[metricsName.RF] = resouceFlow; - // }; - - // afterLoad(stopListeningAndInitRF); - const resouceFlow: ResourceFlowTiming[] = getResourceFlow(); this.data[metricsName.RF] = resouceFlow; } @@ -102,7 +92,7 @@ export class PerformanceTracker { private performanceDataReportHandler() { window.addEventListener('visibilitychange', () => { if (document.visibilityState === 'hidden') { - this.report(this.data); + this.report(this.data, 'performance'); } }); } -- Gitee From 60de4342f7f6fafb7a8ef29b781b664e5db549bd Mon Sep 17 00:00:00 2001 From: ziMu-66ccff <3553189793@qq.com> Date: Mon, 18 Dec 2023 16:32:49 +0800 Subject: [PATCH 2/2] add userAction track --- tracker-server/package.json | 3 + tracker-server/pnpm-lock.yaml | 185 +++++++++++++++- tracker-server/src/app.controller.ts | 14 +- tracker-server/src/app.module.ts | 20 +- tracker-server/src/app.service.ts | 91 +++++++- .../src/dto/create-trackedData.dto.ts | 27 +++ tracker-server/src/entities/custom.entity.ts | 24 +++ .../src/entities/hehaviorStack.entity.ts | 41 ++++ .../src/entities/performance.entity.ts | 118 ++++++++++ tracker-server/src/entities/pv.entity.ts | 67 ++++++ .../src/entities/userAction.entity.ts | 157 ++++++++++++++ tracker/index.html | 6 +- tracker/package.json | 6 +- tracker/pnpm-lock.yaml | 21 ++ tracker/src/const/dom.ts | 10 - tracker/src/const/index.ts | 1 - tracker/src/core/core.ts | 90 ++------ .../performance/{getCD.ts => getCacheData.ts} | 0 .../{getNT.ts => getNavigationTiming.ts} | 0 .../{getRF.ts => getResourceFlow.ts} | 0 tracker/src/performance/performance.ts | 70 +++--- tracker/src/types/core/core.ts | 20 +- tracker/src/types/index.ts | 2 + tracker/src/types/performance/performance.ts | 40 ++-- tracker/src/types/userAction/behaviorStack.ts | 13 ++ tracker/src/types/userAction/index.ts | 2 + tracker/src/types/userAction/userAction.ts | 74 +++++++ tracker/src/userAction/behaviorStack.ts | 35 +++ .../src/userAction/getOriginInformation.ts | 8 + tracker/src/userAction/getPageInformation.ts | 53 +++++ tracker/src/userAction/index.ts | 1 + tracker/src/userAction/proxyFetch.ts | 36 ++++ tracker/src/userAction/proxyXmlHttp.ts | 45 ++++ tracker/src/userAction/trackRouterChange.ts | 5 + tracker/src/userAction/userAction.ts | 204 ++++++++++++++++++ .../writePushStateAndReplaceState.ts | 14 ++ tracker/src/utils/index.ts | 3 +- tracker/src/utils/pv.ts | 9 - 38 files changed, 1348 insertions(+), 167 deletions(-) create mode 100644 tracker-server/src/dto/create-trackedData.dto.ts create mode 100644 tracker-server/src/entities/custom.entity.ts create mode 100644 tracker-server/src/entities/hehaviorStack.entity.ts create mode 100644 tracker-server/src/entities/performance.entity.ts create mode 100644 tracker-server/src/entities/pv.entity.ts create mode 100644 tracker-server/src/entities/userAction.entity.ts delete mode 100644 tracker/src/const/dom.ts delete mode 100644 tracker/src/const/index.ts rename tracker/src/performance/{getCD.ts => getCacheData.ts} (100%) rename tracker/src/performance/{getNT.ts => getNavigationTiming.ts} (100%) rename tracker/src/performance/{getRF.ts => getResourceFlow.ts} (100%) create mode 100644 tracker/src/types/userAction/behaviorStack.ts create mode 100644 tracker/src/types/userAction/index.ts create mode 100644 tracker/src/types/userAction/userAction.ts create mode 100644 tracker/src/userAction/behaviorStack.ts create mode 100644 tracker/src/userAction/getOriginInformation.ts create mode 100644 tracker/src/userAction/getPageInformation.ts create mode 100644 tracker/src/userAction/index.ts create mode 100644 tracker/src/userAction/proxyFetch.ts create mode 100644 tracker/src/userAction/proxyXmlHttp.ts create mode 100644 tracker/src/userAction/trackRouterChange.ts create mode 100644 tracker/src/userAction/userAction.ts create mode 100644 tracker/src/userAction/writePushStateAndReplaceState.ts delete mode 100644 tracker/src/utils/pv.ts diff --git a/tracker-server/package.json b/tracker-server/package.json index 8f0e40d..67f3856 100644 --- a/tracker-server/package.json +++ b/tracker-server/package.json @@ -22,9 +22,12 @@ "dependencies": { "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0", + "@nestjs/mapped-types": "*", + "@nestjs/mongoose": "^10.0.2", "@nestjs/platform-express": "^10.0.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "mongoose": "^8.0.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1" }, diff --git a/tracker-server/pnpm-lock.yaml b/tracker-server/pnpm-lock.yaml index 3922940..aacc6b3 100644 --- a/tracker-server/pnpm-lock.yaml +++ b/tracker-server/pnpm-lock.yaml @@ -11,6 +11,12 @@ dependencies: '@nestjs/core': specifier: ^10.0.0 version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.2.10)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/mapped-types': + specifier: '*' + version: 2.0.4(@nestjs/common@10.2.10)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13) + '@nestjs/mongoose': + specifier: ^10.0.2 + version: 10.0.2(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)(mongoose@8.0.3)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/platform-express': specifier: ^10.0.0 version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10) @@ -20,6 +26,9 @@ dependencies: class-validator: specifier: ^0.14.0 version: 0.14.0 + mongoose: + specifier: ^8.0.3 + version: 8.0.3 reflect-metadata: specifier: ^0.1.13 version: 0.1.13 @@ -843,6 +852,12 @@ packages: resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} engines: {node: '>=8'} + /@mongodb-js/saslprep@1.1.1: + resolution: {integrity: sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==} + dependencies: + sparse-bitfield: 3.0.3 + dev: false + /@nestjs/cli@10.2.1: resolution: {integrity: sha512-CAJAQwmxFZfB3RTvqz/eaXXWpyU+mZ4QSqfBYzjneTsPgF+uyOAW3yQpaLNn9Dfcv39R9UxSuAhayv6yuFd+Jg==} engines: {node: '>= 16.14'} @@ -937,6 +952,41 @@ packages: transitivePeerDependencies: - encoding + /@nestjs/mapped-types@2.0.4(@nestjs/common@10.2.10)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13): + resolution: {integrity: sha512-xl+gUSp0B+ln1VSNoUftlglk8dfpUes3DHGxKZ5knuBxS5g2H/8p9/DSBOYWUfO5f4u9s6ffBPZ71WO+tbe5SA==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + class-transformer: ^0.4.0 || ^0.5.0 + class-validator: ^0.13.0 || ^0.14.0 + reflect-metadata: ^0.1.12 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + dependencies: + '@nestjs/common': 10.2.10(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) + class-transformer: 0.5.1 + class-validator: 0.14.0 + reflect-metadata: 0.1.13 + dev: false + + /@nestjs/mongoose@10.0.2(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)(mongoose@8.0.3)(reflect-metadata@0.1.13)(rxjs@7.8.1): + resolution: {integrity: sha512-ITHh075DynjPIaKeJh6WkarS21WXYslu4nrLkNPbWaCP6JfxVAOftaA2X5tPSiiE/gNJWgs+QFWsfCFZUUenow==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 + mongoose: ^6.0.2 || ^7.0.0 || ^8.0.0 + reflect-metadata: ^0.1.12 + rxjs: ^7.0.0 + dependencies: + '@nestjs/common': 10.2.10(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.2.10)(reflect-metadata@0.1.13)(rxjs@7.8.1) + mongoose: 8.0.3 + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + dev: false + /@nestjs/platform-express@10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10): resolution: {integrity: sha512-U4KDgtMjH8TqEvt0RzC/POP8ABvL9bYoCScvlGtFSKgVGaMLBKkZ4+jHtbQx6qItYSlBBRUuz/dveMZCObfrkQ==} peerDependencies: @@ -1216,7 +1266,6 @@ packages: resolution: {integrity: sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==} dependencies: undici-types: 5.26.5 - dev: true /@types/qs@6.9.10: resolution: {integrity: sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==} @@ -1265,6 +1314,17 @@ packages: /@types/validator@13.11.7: resolution: {integrity: sha512-q0JomTsJ2I5Mv7dhHhQLGjMvX0JJm5dyZ1DXQySIUzU1UlwzB8bt+R6+LODUbz0UDIOvEzGc28tk27gBJw2N8Q==} + /@types/webidl-conversions@7.0.3: + resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} + dev: false + + /@types/whatwg-url@8.2.2: + resolution: {integrity: sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==} + dependencies: + '@types/node': 20.10.3 + '@types/webidl-conversions': 7.0.3 + dev: false + /@types/yargs-parser@21.0.3: resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} dev: true @@ -1872,6 +1932,11 @@ packages: node-int64: 0.4.0 dev: true + /bson@6.2.0: + resolution: {integrity: sha512-ID1cI+7bazPDyL9wYy9GaQ8gEEohWvcUl/Yf0dIdutJxnmInEEyCsb4awy/OiBfall7zBA179Pahi3vCdFze3Q==} + engines: {node: '>=16.20.1'} + dev: false + /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -2207,7 +2272,6 @@ packages: optional: true dependencies: ms: 2.1.2 - dev: true /dedent@1.5.1: resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} @@ -3784,6 +3848,11 @@ packages: graceful-fs: 4.2.11 dev: true + /kareem@2.5.1: + resolution: {integrity: sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==} + engines: {node: '>=12.0.0'} + dev: false + /keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} dependencies: @@ -3912,6 +3981,10 @@ packages: fs-monkey: 1.0.5 dev: true + /memory-pager@1.5.0: + resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + dev: false + /merge-descriptors@1.0.1: resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} @@ -4006,12 +4079,86 @@ packages: dependencies: minimist: 1.2.8 + /mongodb-connection-string-url@2.6.0: + resolution: {integrity: sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==} + dependencies: + '@types/whatwg-url': 8.2.2 + whatwg-url: 11.0.0 + dev: false + + /mongodb@6.2.0: + resolution: {integrity: sha512-d7OSuGjGWDZ5usZPqfvb36laQ9CPhnWkAGHT61x5P95p/8nMVeH8asloMwW6GcYFeB0Vj4CB/1wOTDG2RA9BFA==} + engines: {node: '>=16.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.188.0 + '@mongodb-js/zstd': ^1.1.0 + gcp-metadata: ^5.2.0 + kerberos: ^2.0.1 + mongodb-client-encryption: '>=6.0.0 <7' + snappy: ^7.2.2 + socks: ^2.7.1 + peerDependenciesMeta: + '@aws-sdk/credential-providers': + optional: true + '@mongodb-js/zstd': + optional: true + gcp-metadata: + optional: true + kerberos: + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + socks: + optional: true + dependencies: + '@mongodb-js/saslprep': 1.1.1 + bson: 6.2.0 + mongodb-connection-string-url: 2.6.0 + dev: false + + /mongoose@8.0.3: + resolution: {integrity: sha512-LJRT0yP4TW14HT4r2RkxqyvoTylMSzWpl5QOeVHTnRggCLQSpkoBdgbUtORFq/mSL2o9cLCPJz+6uzFj25qbHw==} + engines: {node: '>=16.20.1'} + dependencies: + bson: 6.2.0 + kareem: 2.5.1 + mongodb: 6.2.0 + mpath: 0.9.0 + mquery: 5.0.0 + ms: 2.1.3 + sift: 16.0.1 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + dev: false + + /mpath@0.9.0: + resolution: {integrity: sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==} + engines: {node: '>=4.0.0'} + dev: false + + /mquery@5.0.0: + resolution: {integrity: sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==} + engines: {node: '>=14.0.0'} + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -4353,7 +4500,6 @@ packages: /punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - dev: true /pure-rand@6.0.4: resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==} @@ -4651,6 +4797,10 @@ packages: get-intrinsic: 1.2.2 object-inspect: 1.13.1 + /sift@16.0.1: + resolution: {integrity: sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==} + dev: false + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true @@ -4693,6 +4843,12 @@ packages: engines: {node: '>= 8'} dev: true + /sparse-bitfield@3.0.3: + resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + dependencies: + memory-pager: 1.5.0 + dev: false + /sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: true @@ -4946,6 +5102,13 @@ packages: /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + /tr46@3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + dependencies: + punycode: 2.3.1 + dev: false + /tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -5114,7 +5277,6 @@ packages: /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - dev: true /universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} @@ -5198,6 +5360,11 @@ packages: /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + /webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: false + /webpack-node-externals@3.0.0: resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} engines: {node: '>=6'} @@ -5248,6 +5415,14 @@ packages: - uglify-js dev: true + /whatwg-url@11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + dev: false + /whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: diff --git a/tracker-server/src/app.controller.ts b/tracker-server/src/app.controller.ts index 43c5241..d31b7a9 100644 --- a/tracker-server/src/app.controller.ts +++ b/tracker-server/src/app.controller.ts @@ -1,18 +1,13 @@ -import { Body, Controller, Get, Post, Req, Res } from '@nestjs/common'; +import { Controller, Post, Req } from '@nestjs/common'; import { AppService } from './app.service'; -import { Request, Response } from 'express'; +import { Request } from 'express'; @Controller() export class AppController { constructor(private readonly appService: AppService) {} - @Get() - getHello(): string { - return this.appService.getHello(); - } - @Post() - tracker(@Req() request: Request, @Res() response: Response) { + trackedDataReport(@Req() request: Request) { const buffers = []; request.on('data', (chunk) => { buffers.push(chunk); @@ -20,8 +15,7 @@ export class AppController { request.on('end', () => { const bufferData = Buffer.concat(buffers); const data = JSON.parse(bufferData.toString()); - console.log(data); - response.status(200).send('success'); + this.appService.saveTrackedData(data); }); } } diff --git a/tracker-server/src/app.module.ts b/tracker-server/src/app.module.ts index 8662803..253a99d 100644 --- a/tracker-server/src/app.module.ts +++ b/tracker-server/src/app.module.ts @@ -1,9 +1,27 @@ import { Module } from '@nestjs/common'; +import { MongooseModule } from '@nestjs/mongoose'; import { AppController } from './app.controller'; import { AppService } from './app.service'; +import { Performance, PerformanceSchema } from './entities/performance.entity'; +import { UserAction, UserActionSchema } from './entities/userAction.entity'; +import { Custom, CustomSchema } from './entities/custom.entity'; +import { PV, PVSchema } from './entities/pv.entity'; +import { + BehaviorStack, + BehaviorStackSchema, +} from './entities/hehaviorStack.entity'; @Module({ - imports: [], + imports: [ + MongooseModule.forRoot('mongodb://127.0.0.1:27017/ziMuTracker'), + MongooseModule.forFeature([ + { name: Performance.name, schema: PerformanceSchema }, + { name: UserAction.name, schema: UserActionSchema }, + { name: PV.name, schema: PVSchema }, + { name: Custom.name, schema: CustomSchema }, + { name: BehaviorStack.name, schema: BehaviorStackSchema }, + ]), + ], controllers: [AppController], providers: [AppService], }) diff --git a/tracker-server/src/app.service.ts b/tracker-server/src/app.service.ts index 927d7cc..3b40403 100644 --- a/tracker-server/src/app.service.ts +++ b/tracker-server/src/app.service.ts @@ -1,8 +1,95 @@ import { Injectable } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { Performance } from './entities/performance.entity'; +import { Model } from 'mongoose'; +import { CreateTrackedDataDto } from './dto/create-trackedData.dto'; +import { UserAction } from './entities/userAction.entity'; +import { Custom } from './entities/custom.entity'; +import { PV } from './entities/pv.entity'; +import { BehaviorStack } from './entities/hehaviorStack.entity'; @Injectable() export class AppService { - getHello(): string { - return 'Hello World!'; + constructor( + @InjectModel(Performance.name) + private readonly PerformanceModel: Model, + @InjectModel(UserAction.name) + private readonly UserActionModel: Model, + @InjectModel(Custom.name) + private readonly CustomModel: Model, + @InjectModel(BehaviorStack.name) + private readonly BehaviorStack: Model, + @InjectModel(PV.name) + private readonly PVModel: Model, + ) {} + + async saveTrackedData(createTrackedDataDto: CreateTrackedDataDto) { + const { type, data, appId, uid, extra, time, timeFormat } = + createTrackedDataDto; + + if (type === 'performance') { + const performanceData = await this.PerformanceModel.create({ + ...data, + extra, + appId, + uid, + time, + timeFormat, + }); + if (performanceData) return console.log('performance data send success'); + else return console.log('performance data sendfail'); + } + + if (type === 'userAction') { + const userActionData = await this.UserActionModel.create({ + ...data, + extra, + appId, + uid, + time, + timeFormat, + }); + if (userActionData) return console.log('userAction data send success'); + else return console.log('userAction data sendfail'); + } + + if (type === 'custom') { + const customData = await this.CustomModel.create({ + data, + extra, + appId, + uid, + time, + timeFormat, + }); + if (customData) return console.log('custom data send success'); + else return console.log('custom data sendfail'); + } + + if (type === 'behaviorStack') { + const behaviorStack = await this.BehaviorStack.create({ + data, + extra, + appId, + uid, + time, + timeFormat, + }); + if (behaviorStack) return console.log('behaviorStack data send success'); + else return console.log('behaviorStack data sendfail'); + } + + if (type === 'PV') { + const PVData = await this.PVModel.create({ + ...data, + extra, + appId, + uid, + time, + timeFormat, + }); + if (PVData) return console.log('PV data send success'); + else return console.log('PV data sendfail'); + } } } diff --git a/tracker-server/src/dto/create-trackedData.dto.ts b/tracker-server/src/dto/create-trackedData.dto.ts new file mode 100644 index 0000000..d600258 --- /dev/null +++ b/tracker-server/src/dto/create-trackedData.dto.ts @@ -0,0 +1,27 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class CreateTrackedDataDto { + @IsString({ message: 'the type of message must be string' }) + @IsNotEmpty({ message: '类型不能为空奥' }) + type: string; + + @IsNotEmpty({ message: '数据不能为空奥' }) + data: Record; + + extra: Record; + + @IsString({ message: 'the type of appId must be string' }) + @IsNotEmpty({ message: 'appId不能为空奥' }) + appId: string; + + @IsString({ message: 'the type of uid must be string' }) + uid: string; + + @IsString({ message: 'the type of time must be number' }) + @IsNotEmpty({ message: 'time不能为空奥' }) + time: number; + + @IsString({ message: 'the type of timeFormat must be string' }) + @IsNotEmpty({ message: 'timeFormat不能为空奥' }) + timeFormat: string; +} diff --git a/tracker-server/src/entities/custom.entity.ts b/tracker-server/src/entities/custom.entity.ts new file mode 100644 index 0000000..e5ccf8a --- /dev/null +++ b/tracker-server/src/entities/custom.entity.ts @@ -0,0 +1,24 @@ +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; + +@Schema() +export class Custom { + @Prop({ type: Object, required: true }) + data: Record; + + @Prop({ type: Object }) + extra: Record; + + @Prop({ type: String, required: true }) + appId: string; + + @Prop(String) + uid: string; + + @Prop({ type: Number, required: true }) + time: number; + + @Prop({ type: String, required: true }) + timeFormat: string; +} + +export const CustomSchema = SchemaFactory.createForClass(Custom); diff --git a/tracker-server/src/entities/hehaviorStack.entity.ts b/tracker-server/src/entities/hehaviorStack.entity.ts new file mode 100644 index 0000000..0f42581 --- /dev/null +++ b/tracker-server/src/entities/hehaviorStack.entity.ts @@ -0,0 +1,41 @@ +import { Prop, Schema, SchemaFactory, raw } from '@nestjs/mongoose'; + +@Schema() +export class BehaviorStack { + @Prop({ + type: [ + raw({ + name: String, + page: String, + value: Object, + time: Number, + timeFormat: String, + }), + ], + required: true, + }) + data: Array<{ + name: string; + page: string; + value: Record; + time: number; + timeFormat: string; + }>; + + @Prop({ type: Object }) + extra: Record; + + @Prop({ type: String, required: true }) + appId: string; + + @Prop(String) + uid: string; + + @Prop({ type: Number, required: true }) + time: number; + + @Prop({ type: String, required: true }) + timeFormat: string; +} + +export const BehaviorStackSchema = SchemaFactory.createForClass(BehaviorStack); diff --git a/tracker-server/src/entities/performance.entity.ts b/tracker-server/src/entities/performance.entity.ts new file mode 100644 index 0000000..c753070 --- /dev/null +++ b/tracker-server/src/entities/performance.entity.ts @@ -0,0 +1,118 @@ +import { Prop, Schema, SchemaFactory, raw } from '@nestjs/mongoose'; + +@Schema() +export class Performance { + @Prop(raw({ name: String, value: Number, rating: String })) + FCP: { + name: string; + value: number; + rating: 'good' | 'needs-improvement' | 'poor'; + }; + + @Prop(raw({ name: String, value: Number, rating: String })) + LCP: { + name: string; + value: number; + rating: 'good' | 'needs-improvement' | 'poor'; + }; + + @Prop(raw({ name: String, value: Number, rating: String })) + FID: { + name: string; + value: number; + rating: 'good' | 'needs-improvement' | 'poor'; + }; + + @Prop(raw({ name: String, value: Number, rating: String })) + CLS: { + name: string; + value: number; + rating: 'good' | 'needs-improvement' | 'poor'; + }; + + @Prop( + raw({ + DNS: raw({ start: Number, end: Number, value: Number }), + SSL: raw({ start: Number, end: Number, value: Number }), + TCP: raw({ start: Number, end: Number, value: Number }), + TTFB: raw({ start: Number, end: Number, value: Number }), + Trans: raw({ start: Number, end: Number, value: Number }), + FP: raw({ start: Number, end: Number, value: Number }), + DomParse: raw({ start: Number, end: Number, value: Number }), + TTI: raw({ start: Number, end: Number, value: Number }), + DomReady: raw({ start: Number, end: Number, value: Number }), + Res: raw({ start: Number, end: Number, value: Number }), + Load: raw({ start: Number, end: Number, value: Number }), + }), + ) + navigationTiming: { + DNS: { start: number; end: number; value: number }; + SSL: { start: number; end: number; value: number }; + TCP: { start: number; end: number; value: number }; + TTFB: { start: number; end: number; value: number }; + Trans: { start: number; end: number; value: number }; + FP: { start: number; end: number; value: number }; + DomParse: { start: number; end: number; value: number }; + TTI: { start: number; end: number; value: number }; + DomReady: { start: number; end: number; value: number }; + Res: { start: number; end: number; value: number }; + Load: { start: number; end: number; value: number }; + }; + + @Prop([ + raw({ + name: String, + initiatorType: String, + transferSize: Number, + start: Number, + end: Number, + DNS: Number, + TCP: Number, + SSL: Number, + TTFB: Number, + Trans: Number, + }), + ]) + 'resource-flow': Array<{ + name: string; + initiatorType: string; + transferSize: number; + start: number; + end: number; + DNS: number; + TCP: number; + SSL: number; + TTFB: number; + Trans: number; + }>; + + @Prop( + raw({ + cacheHitQuantity: Number, + noCacheHitQuantity: Number, + cacheHitRate: String, + }), + ) + 'cache-data': { + cacheHitQuantity: number; + noCacheHitQuantity: number; + cacheHitRate: string; + }; + + @Prop({ type: Object }) + extra: Record; + + @Prop({ type: String, required: true }) + appId: string; + + @Prop(String) + uid: string; + + @Prop({ type: Number, required: true }) + time: number; + + @Prop({ type: String, required: true }) + timeFormat: string; +} + +export const PerformanceSchema = SchemaFactory.createForClass(Performance); diff --git a/tracker-server/src/entities/pv.entity.ts b/tracker-server/src/entities/pv.entity.ts new file mode 100644 index 0000000..f3fc500 --- /dev/null +++ b/tracker-server/src/entities/pv.entity.ts @@ -0,0 +1,67 @@ +import { Prop, Schema, SchemaFactory, raw } from '@nestjs/mongoose'; + +@Schema() +export class PV { + @Prop( + raw({ + href: String, + origin: String, + protocol: String, + host: String, + hostname: String, + port: String, + pathname: String, + search: String, + hash: String, + title: String, + language: String, + userAgent: Object, + winScreen: String, + docScreen: String, + }), + ) + pageInfo: { + href: string; + origin: string; + protocol: string; + host: string; + hostname: string; + port: string; + pathname: string; + search: string; + hash: string; + title: string; + language: string; + userAgent: Record; + winScreen: string; + docScreen: string; + }; + + @Prop([ + raw({ + referrer: String, + type: String, + }), + ]) + originInformation: { + referrer: string; + type: string; + }; + + @Prop({ type: Object }) + extra: Record; + + @Prop({ type: String, required: true }) + appId: string; + + @Prop(String) + uid: string; + + @Prop({ type: Number, required: true }) + time: number; + + @Prop({ type: String, required: true }) + timeFormat: string; +} + +export const PVSchema = SchemaFactory.createForClass(PV); diff --git a/tracker-server/src/entities/userAction.entity.ts b/tracker-server/src/entities/userAction.entity.ts new file mode 100644 index 0000000..b379a70 --- /dev/null +++ b/tracker-server/src/entities/userAction.entity.ts @@ -0,0 +1,157 @@ +import { Prop, Schema, SchemaFactory, raw } from '@nestjs/mongoose'; + +@Schema() +export class UserAction { + @Prop( + raw({ + href: String, + origin: String, + protocol: String, + host: String, + hostname: String, + port: String, + pathname: String, + search: String, + hash: String, + title: String, + language: String, + userAgent: Object, + winScreen: String, + docScreen: String, + }), + ) + 'page-information': { + href: string; + origin: string; + protocol: string; + host: string; + hostname: string; + port: string; + pathname: string; + search: string; + hash: string; + title: string; + language: string; + userAgent: Record; + winScreen: string; + docScreen: string; + }; + + @Prop([ + raw({ + referrer: String, + type: String, + }), + ]) + 'origin-information': { + referrer: string; + type: string; + }; + + @Prop([ + raw({ + jumpType: String, + pageInfo: raw({ + href: String, + origin: String, + protocol: String, + host: String, + hostname: String, + port: String, + pathname: String, + search: String, + hash: String, + title: String, + language: String, + userAgent: Object, + winScreen: String, + docScreen: String, + }), + time: Number, + timeFormat: String, + }), + ]) + 'router-change-record': Array<{ + jumpType: string; + pageInfo: { + href: string; + origin: string; + protocol: string; + host: string; + hostname: string; + port: string; + pathname: string; + search: string; + hash: string; + title: string; + language: string; + userAgent: Record; + winScreen: string; + docScreen: string; + }; + time: number; + timeFormat: string; + }>; + + @Prop({ type: Object }) + 'dom-behavior-record': Record< + string, + Array<{ + tagInfo: { + id: string; + classList: string[]; + tagName: string; + text: string | null; + }; + pageInfo: { + href: string; + origin: string; + protocol: string; + host: string; + hostname: string; + port: string; + pathname: string; + search: string; + hash: string; + title: string; + language: string; + userAgent: Record; + winScreen: string; + docScreen: string; + }; + time: number; + timeFormat: string; + }> + >; + + @Prop({ type: [Object] }) + 'http-record': Array<{ + method: string; + url: string | URL; + body: Document | XMLHttpRequestBodyInit | null | undefined | ReadableStream; + requestTime: number; + requestTimeFormat: string; + responseTime: number; + responseTimeFormat: string; + status: number; + statusText: string; + response?: any; + }>; + + @Prop({ type: Object }) + extra: Record; + + @Prop({ type: String, required: true }) + appId: string; + + @Prop(String) + uid: string; + + @Prop({ type: Number, required: true }) + time: number; + + @Prop({ type: String, required: true }) + timeFormat: string; +} + +export const UserActionSchema = SchemaFactory.createForClass(UserAction); diff --git a/tracker/index.html b/tracker/index.html index 0ebe9b1..9c82fb8 100644 --- a/tracker/index.html +++ b/tracker/index.html @@ -113,7 +113,7 @@