test: add test framework which can test http request and generate mock data

This commit is contained in:
moonrailgun 2024-03-24 16:32:40 +08:00
parent bd4e6b5b05
commit 90e8292b30
9 changed files with 327 additions and 142 deletions

View File

@ -404,6 +404,9 @@ importers:
'@types/request-ip':
specifier: ^0.0.38
version: 0.0.38
'@types/supertest':
specifier: ^6.0.2
version: 6.0.2
'@types/swagger-ui-express':
specifier: ^4.1.5
version: 4.1.5
@ -431,6 +434,9 @@ importers:
prisma-zod-generator:
specifier: 0.8.13
version: 0.8.13(prisma@5.4.2)
supertest:
specifier: ^6.3.4
version: 6.3.4
tailwindcss:
specifier: ^3.3.5
version: 3.3.5(ts-node@10.9.1)
@ -9533,6 +9539,10 @@ packages:
resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
dev: false
/@types/cookiejar@2.1.5:
resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==}
dev: true
/@types/cors@2.8.15:
resolution: {integrity: sha512-n91JxbNLD8eQIuXDIChAN1tCKNWCEgpceU9b7ZMbFA+P+Q4yIeh80jizFLEvolRPc1ES0VdwFlGv+kJTSirogw==}
dependencies:
@ -9757,6 +9767,10 @@ packages:
resolution: {integrity: sha512-HM5bwOaIQJIQbAYfax35HCKxx7a3KrK3nBtIqJgSOitivTD1y3oW9P3rxY9RkXYPUk7y/AjAohfHKmFpGE79zw==}
dev: true
/@types/methods@1.1.4:
resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==}
dev: true
/@types/mime@1.3.2:
resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==}
@ -9963,12 +9977,27 @@ packages:
dependencies:
'@types/node': 18.17.12
/@types/superagent@8.1.6:
resolution: {integrity: sha512-yzBOv+6meEHSzV2NThYYOA6RtqvPr3Hbob9ZLp3i07SH27CrYVfm8CrF7ydTmidtelsFiKx2I4gZAiAOamGgvQ==}
dependencies:
'@types/cookiejar': 2.1.5
'@types/methods': 1.1.4
'@types/node': 18.17.12
dev: true
/@types/supercluster@7.1.3:
resolution: {integrity: sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==}
dependencies:
'@types/geojson': 7946.0.13
dev: false
/@types/supertest@6.0.2:
resolution: {integrity: sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==}
dependencies:
'@types/methods': 1.1.4
'@types/superagent': 8.1.6
dev: true
/@types/swagger-ui-express@4.1.5:
resolution: {integrity: sha512-MRvm1OCzIR321glc/4tP34wRVmsupgLzs6XIq50CFp0CJUzxbpDsrhJxEBMQfoO46ixrlCiw3QXxEs5HHxYI8Q==}
dependencies:
@ -10696,7 +10725,7 @@ packages:
/array-buffer-byte-length@1.0.0:
resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
dependencies:
call-bind: 1.0.2
call-bind: 1.0.7
is-array-buffer: 3.0.2
/array-flatten@1.1.1:
@ -10732,7 +10761,7 @@ packages:
resolution: {integrity: sha512-nK1psgF2cXqP3wSyCSq0Hc7zwNq3sfljQqaG27r/7a7ooNUnn5nGq6yYWyks9jMO5EoFQ0ax80hSg6oXSRNXaw==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
call-bind: 1.0.7
define-properties: 1.2.0
es-abstract: 1.22.1
es-array-method-boxes-properly: 1.0.0
@ -10744,9 +10773,9 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
array-buffer-byte-length: 1.0.0
call-bind: 1.0.2
call-bind: 1.0.7
define-properties: 1.2.0
get-intrinsic: 1.2.1
get-intrinsic: 1.2.4
is-array-buffer: 3.0.2
is-shared-array-buffer: 1.0.2
@ -10756,7 +10785,6 @@ packages:
/asap@2.0.6:
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
dev: false
/assertion-error@1.1.0:
resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
@ -10806,7 +10834,6 @@ packages:
/asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: false
/at-least-node@1.0.0:
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
@ -11347,7 +11374,8 @@ packages:
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
dependencies:
function-bind: 1.1.2
get-intrinsic: 1.2.1
get-intrinsic: 1.2.4
dev: true
/call-bind@1.0.7:
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
@ -11857,7 +11885,6 @@ packages:
engines: {node: '>= 0.8'}
dependencies:
delayed-stream: 1.0.0
dev: false
/comma-separated-tokens@1.0.8:
resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==}
@ -11912,7 +11939,6 @@ packages:
/component-emitter@1.3.1:
resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==}
dev: false
/compose-middleware@5.0.1:
resolution: {integrity: sha512-Rcv19QgPOtYHu8wDJsu4ehSfkqSXjQLwKRXhIy9TFiIijSZz330ORyLCeirb4sPuBBbDNC5lUvQLuM72vWjKSQ==}
@ -12137,7 +12163,7 @@ packages:
handlebars: 4.7.8
json-stringify-safe: 5.0.1
meow: 12.1.1
semver: 7.5.4
semver: 7.6.0
split2: 4.2.0
dev: true
@ -12211,7 +12237,6 @@ packages:
/cookiejar@2.1.4:
resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==}
dev: false
/copy-anything@2.0.6:
resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==}
@ -12533,7 +12558,7 @@ packages:
postcss-modules-scope: 3.1.0(postcss@8.4.33)
postcss-modules-values: 4.0.0(postcss@8.4.33)
postcss-value-parser: 4.2.0
semver: 7.5.4
semver: 7.6.0
webpack: 5.89.0
dev: false
@ -13133,7 +13158,6 @@ packages:
/delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dev: false
/density-clustering@1.3.0:
resolution: {integrity: sha512-icpmBubVTwLnsaor9qH/4tG5+7+f61VcqMN3V3pm9sxxSCt2Jcs0zWOgwZW9ARJYaKD3FumIgHiMOcIMRRAzFQ==}
@ -13216,7 +13240,6 @@ packages:
dependencies:
asap: 2.0.6
wrappy: 1.0.2
dev: false
/didyoumean@1.2.2:
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
@ -13699,11 +13722,11 @@ packages:
array-buffer-byte-length: 1.0.0
arraybuffer.prototype.slice: 1.0.1
available-typed-arrays: 1.0.5
call-bind: 1.0.2
call-bind: 1.0.7
es-set-tostringtag: 2.0.1
es-to-primitive: 1.2.1
function.prototype.name: 1.1.6
get-intrinsic: 1.2.1
get-intrinsic: 1.2.4
get-symbol-description: 1.0.0
globalthis: 1.0.3
gopd: 1.0.1
@ -13753,8 +13776,8 @@ packages:
/es-get-iterator@1.1.3:
resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==}
dependencies:
call-bind: 1.0.2
get-intrinsic: 1.2.1
call-bind: 1.0.7
get-intrinsic: 1.2.4
has-symbols: 1.0.3
is-arguments: 1.1.1
is-map: 2.0.2
@ -13771,7 +13794,7 @@ packages:
resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==}
engines: {node: '>= 0.4'}
dependencies:
get-intrinsic: 1.2.1
get-intrinsic: 1.2.4
has: 1.0.3
has-tostringtag: 1.0.0
@ -14188,7 +14211,6 @@ packages:
/fast-safe-stringify@2.1.1:
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
dev: false
/fast-shallow-equal@1.0.0:
resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==}
@ -14471,7 +14493,7 @@ packages:
memfs: 3.5.3
minimatch: 3.1.2
schema-utils: 2.7.0
semver: 7.5.4
semver: 7.6.0
tapable: 1.1.3
typescript: 4.7.4
webpack: 5.89.0
@ -14502,7 +14524,7 @@ packages:
memfs: 3.5.3
minimatch: 3.1.2
schema-utils: 2.7.0
semver: 7.5.4
semver: 7.6.0
tapable: 1.1.3
typescript: 4.7.4
webpack: 5.90.3
@ -14520,7 +14542,6 @@ packages:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
dev: false
/format@0.2.2:
resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
@ -14541,7 +14562,6 @@ packages:
hexoid: 1.0.0
once: 1.4.0
qs: 6.11.2
dev: false
/forwarded@0.2.0:
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
@ -14653,7 +14673,7 @@ packages:
resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
call-bind: 1.0.7
define-properties: 1.2.0
es-abstract: 1.22.1
functions-have-names: 1.2.3
@ -14700,6 +14720,7 @@ packages:
has: 1.0.3
has-proto: 1.0.3
has-symbols: 1.0.3
dev: true
/get-intrinsic@1.2.4:
resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
@ -14746,8 +14767,8 @@ packages:
resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
get-intrinsic: 1.2.1
call-bind: 1.0.7
get-intrinsic: 1.2.4
/get-uri@6.0.2:
resolution: {integrity: sha512-5KLucCJobh8vBY1K07EFV4+cPZH3mrV9YeAruUseCQKHB58SGjjT2l9/eA9LD082IiuMjSlFJEcdJ27TXvbZNw==}
@ -14781,7 +14802,7 @@ packages:
hasBin: true
dependencies:
meow: 12.1.1
semver: 7.5.4
semver: 7.6.0
dev: true
/git-up@7.0.0:
@ -15354,7 +15375,6 @@ packages:
/hexoid@1.0.0:
resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==}
engines: {node: '>=8'}
dev: false
/history@4.10.1:
resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==}
@ -15862,7 +15882,7 @@ packages:
resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==}
engines: {node: '>= 0.4'}
dependencies:
get-intrinsic: 1.2.1
get-intrinsic: 1.2.4
has: 1.0.3
side-channel: 1.0.4
@ -15943,14 +15963,14 @@ packages:
resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
call-bind: 1.0.7
has-tostringtag: 1.0.0
/is-array-buffer@3.0.2:
resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
dependencies:
call-bind: 1.0.2
get-intrinsic: 1.2.1
call-bind: 1.0.7
get-intrinsic: 1.2.4
is-typed-array: 1.1.12
/is-arrayish@0.2.1:
@ -15975,7 +15995,7 @@ packages:
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
call-bind: 1.0.7
has-tostringtag: 1.0.0
/is-buffer@1.1.6:
@ -16193,7 +16213,7 @@ packages:
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
call-bind: 1.0.7
has-tostringtag: 1.0.0
/is-regexp@1.0.0:
@ -16218,7 +16238,7 @@ packages:
/is-shared-array-buffer@1.0.2:
resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
dependencies:
call-bind: 1.0.2
call-bind: 1.0.7
/is-ssh@1.4.0:
resolution: {integrity: sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==}
@ -16292,7 +16312,7 @@ packages:
/is-weakref@1.0.2:
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
dependencies:
call-bind: 1.0.2
call-bind: 1.0.7
/is-what@3.14.1:
resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==}
@ -17846,7 +17866,6 @@ packages:
resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==}
engines: {node: '>=4.0.0'}
hasBin: true
dev: false
/mime@3.0.0:
resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
@ -18186,7 +18205,7 @@ packages:
resolution: {integrity: sha512-JJ98b02z16ILv7859irtXn4oUaFWADtvkzy2c0IAatNVX2Mc9Yoh8z6hZInn3QwvMEYhHuQloYi+TTQy67SIdQ==}
engines: {node: '>=10'}
dependencies:
semver: 7.5.4
semver: 7.6.0
dev: false
/node-domexception@1.0.0:
@ -18321,7 +18340,7 @@ packages:
dependencies:
hosted-git-info: 7.0.1
is-core-module: 2.13.0
semver: 7.5.4
semver: 7.6.0
validate-npm-package-license: 3.0.4
dev: true
@ -18741,7 +18760,7 @@ packages:
got: 12.6.1
registry-auth-token: 5.0.2
registry-url: 6.0.1
semver: 7.5.4
semver: 7.6.0
dev: true
/param-case@3.0.4:
@ -19272,7 +19291,7 @@ packages:
dependencies:
lilconfig: 2.1.0
postcss: 8.4.33
ts-node: 10.9.1(@types/node@18.17.12)(typescript@5.2.2)
ts-node: 10.9.1(@types/node@18.17.12)(typescript@5.3.3)
yaml: 2.3.2
dev: true
@ -19286,7 +19305,7 @@ packages:
cosmiconfig: 8.3.6(typescript@4.7.4)
jiti: 1.21.0
postcss: 8.4.33
semver: 7.5.4
semver: 7.6.0
webpack: 5.89.0
transitivePeerDependencies:
- typescript
@ -19302,7 +19321,7 @@ packages:
cosmiconfig: 8.3.6(typescript@4.7.4)
jiti: 1.21.0
postcss: 8.4.35
semver: 7.5.4
semver: 7.6.0
webpack: 5.90.3
transitivePeerDependencies:
- typescript
@ -22282,7 +22301,7 @@ packages:
resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
call-bind: 1.0.7
define-properties: 1.2.0
set-function-name: 2.0.1
@ -22839,8 +22858,8 @@ packages:
resolution: {integrity: sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==}
engines: {node: '>=0.4'}
dependencies:
call-bind: 1.0.2
get-intrinsic: 1.2.1
call-bind: 1.0.7
get-intrinsic: 1.2.4
has-symbols: 1.0.3
isarray: 2.0.5
@ -22853,8 +22872,8 @@ packages:
/safe-regex-test@1.0.0:
resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==}
dependencies:
call-bind: 1.0.2
get-intrinsic: 1.2.1
call-bind: 1.0.7
get-intrinsic: 1.2.4
is-regex: 1.1.4
/safe-stable-stringify@2.4.3:
@ -22972,7 +22991,7 @@ packages:
resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==}
engines: {node: '>=12'}
dependencies:
semver: 7.5.4
semver: 7.6.0
dev: true
/semver@5.7.2:
@ -23010,7 +23029,6 @@ packages:
hasBin: true
dependencies:
lru-cache: 6.0.0
dev: true
/send@0.18.0:
resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
@ -23630,21 +23648,21 @@ packages:
resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
call-bind: 1.0.7
define-properties: 1.2.0
es-abstract: 1.22.1
/string.prototype.trimend@1.0.6:
resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==}
dependencies:
call-bind: 1.0.2
call-bind: 1.0.7
define-properties: 1.2.0
es-abstract: 1.22.1
/string.prototype.trimstart@1.0.6:
resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==}
dependencies:
call-bind: 1.0.2
call-bind: 1.0.7
define-properties: 1.2.0
es-abstract: 1.22.1
@ -23798,11 +23816,29 @@ packages:
mime: 2.6.0
qs: 6.11.2
readable-stream: 3.6.2
semver: 7.5.4
semver: 7.6.0
transitivePeerDependencies:
- supports-color
dev: false
/superagent@8.1.2:
resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==}
engines: {node: '>=6.4.0 <13 || >=14'}
dependencies:
component-emitter: 1.3.1
cookiejar: 2.1.4
debug: 4.3.4(supports-color@5.5.0)
fast-safe-stringify: 2.1.1
form-data: 4.0.0
formidable: 2.1.2
methods: 1.1.2
mime: 2.6.0
qs: 6.11.2
semver: 7.6.0
transitivePeerDependencies:
- supports-color
dev: true
/supercluster@7.1.5:
resolution: {integrity: sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==}
dependencies:
@ -23815,6 +23851,16 @@ packages:
kdbush: 4.0.2
dev: false
/supertest@6.3.4:
resolution: {integrity: sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==}
engines: {node: '>=6.4.0'}
dependencies:
methods: 1.1.2
superagent: 8.1.2
transitivePeerDependencies:
- supports-color
dev: true
/supports-color@2.0.0:
resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==}
engines: {node: '>=0.8.0'}
@ -24466,7 +24512,6 @@ packages:
typescript: 5.3.3
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
dev: false
/ts-pattern@4.3.0:
resolution: {integrity: sha512-pefrkcd4lmIVR0LA49Imjf9DYLK8vtWhqBPA3Ya1ir8xCW0O2yjL9dsCVvI7pCodLC5q7smNpEtDR2yVulQxOg==}
@ -24553,15 +24598,15 @@ packages:
resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
get-intrinsic: 1.2.1
call-bind: 1.0.7
get-intrinsic: 1.2.4
is-typed-array: 1.1.12
/typed-array-byte-length@1.0.0:
resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
call-bind: 1.0.7
for-each: 0.3.3
has-proto: 1.0.3
is-typed-array: 1.1.12
@ -24571,7 +24616,7 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
available-typed-arrays: 1.0.5
call-bind: 1.0.2
call-bind: 1.0.7
for-each: 0.3.3
has-proto: 1.0.3
is-typed-array: 1.1.12
@ -24579,7 +24624,7 @@ packages:
/typed-array-length@1.0.4:
resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==}
dependencies:
call-bind: 1.0.2
call-bind: 1.0.7
for-each: 0.3.3
is-typed-array: 1.1.12
@ -24657,7 +24702,7 @@ packages:
/unbox-primitive@1.0.2:
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
dependencies:
call-bind: 1.0.2
call-bind: 1.0.7
has-bigints: 1.0.2
has-symbols: 1.0.3
which-boxed-primitive: 1.0.2
@ -24957,7 +25002,7 @@ packages:
is-yarn-global: 0.3.0
latest-version: 5.1.0
pupa: 2.1.1
semver: 7.5.4
semver: 7.6.0
semver-diff: 3.1.1
xdg-basedir: 4.0.0
dev: false
@ -25172,7 +25217,7 @@ packages:
engines: {node: '>= 12'}
dependencies:
resolve-package-path: 4.0.3
semver: 7.5.4
semver: 7.6.0
dev: true
/validator@13.11.0:
@ -25836,7 +25881,7 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
available-typed-arrays: 1.0.5
call-bind: 1.0.2
call-bind: 1.0.7
for-each: 0.3.3
gopd: 1.0.1
has-tostringtag: 1.0.0

75
src/server/app.ts Normal file
View File

@ -0,0 +1,75 @@
import express from 'express';
import 'express-async-errors';
import compression from 'compression';
import swaggerUI from 'swagger-ui-express';
import passport from 'passport';
import morgan from 'morgan';
import { websiteRouter } from './router/website';
import { workspaceRouter } from './router/workspace';
import { telemetryRouter } from './router/telemetry';
import {
trpcExpressMiddleware,
trpcOpenapiDocument,
trpcOpenapiHttpHandler,
} from './trpc';
import { env } from './utils/env';
import cors from 'cors';
import { serverStatusRouter } from './router/serverStatus';
import { logger } from './utils/logger';
import { monitorRouter } from './router/monitor';
import { healthRouter } from './router/health';
import path from 'path';
const app = express();
app.use(compression());
app.use(express.json());
app.use(passport.initialize());
app.use(morgan('tiny'));
app.use(cors());
app.use(express.static('public'));
// http://expressjs.com/en/advanced/best-practice-security.html#at-a-minimum-disable-x-powered-by-header
app.disable('x-powered-by');
app.use(
'/tracker.js',
express.static('./public/tracker.js', {
maxAge: '7d',
})
);
app.use('/health', healthRouter);
app.use('/api/website', websiteRouter);
app.use('/api/workspace', workspaceRouter);
app.use('/monitor', monitorRouter);
app.use('/telemetry', telemetryRouter);
app.use('/serverStatus', serverStatusRouter);
app.use('/trpc', trpcExpressMiddleware);
if (env.customTrackerScriptName) {
app.get(`/${env.customTrackerScriptName}`, (req, res) =>
res.sendFile(path.resolve(__dirname, './public/tracker.js'))
);
}
if (env.allowOpenapi) {
app.use('/open/_ui', swaggerUI.serve, swaggerUI.setup(trpcOpenapiDocument));
app.use('/open/_document', (req, res) => res.send(trpcOpenapiDocument));
app.use('/open', trpcOpenapiHttpHandler);
}
// fallback
app.use('/*', (req, res) => {
if (req.method === 'GET' && req.accepts('html')) {
res.sendFile(path.join(process.cwd(), 'public', 'index.html'));
}
});
app.use((err: any, req: any, res: any, next: any) => {
logger.error('[express]', err);
res.status(500).json({ message: err.message });
});
export { app };

View File

@ -1,35 +1,16 @@
import 'dotenv/config';
import './init';
import express from 'express';
import 'express-async-errors';
import compression from 'compression';
import swaggerUI from 'swagger-ui-express';
import passport from 'passport';
import morgan from 'morgan';
import { websiteRouter } from './router/website';
import { workspaceRouter } from './router/workspace';
import { telemetryRouter } from './router/telemetry';
import {
trpcExpressMiddleware,
trpcOpenapiDocument,
trpcOpenapiHttpHandler,
} from './trpc';
import { initUdpServer } from './udp/server';
import { createServer } from 'http';
import { initSocketio } from './ws';
import { monitorManager } from './model/monitor';
import { env } from './utils/env';
import cors from 'cors';
import { serverStatusRouter } from './router/serverStatus';
import { initCronjob } from './cronjob';
import { logger } from './utils/logger';
import { monitorRouter } from './router/monitor';
import { healthRouter } from './router/health';
import path from 'path';
import { app } from './app';
const port = env.port;
const app = express();
const httpServer = createServer(app);
initUdpServer(port);
@ -40,56 +21,6 @@ initCronjob();
monitorManager.startAll();
app.use(compression());
app.use(express.json());
app.use(passport.initialize());
app.use(morgan('tiny'));
app.use(cors());
app.use(express.static('public'));
// http://expressjs.com/en/advanced/best-practice-security.html#at-a-minimum-disable-x-powered-by-header
app.disable('x-powered-by');
app.use(
'/tracker.js',
express.static('./public/tracker.js', {
maxAge: '7d',
})
);
app.use('/health', healthRouter);
app.use('/api/website', websiteRouter);
app.use('/api/workspace', workspaceRouter);
app.use('/monitor', monitorRouter);
app.use('/telemetry', telemetryRouter);
app.use('/serverStatus', serverStatusRouter);
app.use('/trpc', trpcExpressMiddleware);
if (env.customTrackerScriptName) {
app.get(`/${env.customTrackerScriptName}`, (req, res) =>
res.sendFile(path.resolve(__dirname, './public/tracker.js'))
);
}
if (env.allowOpenapi) {
app.use('/open/_ui', swaggerUI.serve, swaggerUI.setup(trpcOpenapiDocument));
app.use('/open/_document', (req, res) => res.send(trpcOpenapiDocument));
app.use('/open', trpcOpenapiHttpHandler);
}
// fallback
app.use('/*', (req, res) => {
if (req.method === 'GET' && req.accepts('html')) {
res.sendFile(path.join(process.cwd(), 'public', 'index.html'));
}
});
app.use((err: any, req: any, res: any, next: any) => {
logger.error('[express]', err);
res.status(500).json({ message: err.message });
});
httpServer.listen(port, () => {
logger.info(`Server is listening on port ${port}...`);
if (env.allowOpenapi) {

View File

@ -12,7 +12,14 @@ import {
import { SESSION_COLUMNS } from '../utils/const';
export async function recordTelemetryEvent(req: Request) {
const { url = req.headers.referer, name, ...others } = req.query;
const {
url = req.headers.referer,
name,
title,
start,
fullNum,
...others
} = req.query;
if (!(url && typeof url === 'string')) {
return;
@ -272,8 +279,8 @@ export async function getTelemetrySessionMetrics(
where "TelemetryEvent"."telemetryId" = ${telemetryId}
and "TelemetryEvent"."createdAt"
between ${params.startDate}::timestamptz and ${
params.endDate
}::timestamptz
params.endDate
}::timestamptz
${filterQuery}
group by 1
${includeCountry ? Prisma.sql([', 3']) : Prisma.empty}
@ -301,8 +308,8 @@ export async function getTelemetryPageviewMetrics(
where "TelemetryEvent"."telemetryId" = ${telemetryId}
and "TelemetryEvent"."createdAt"
between ${params.startDate}::timestamptz and ${
params.endDate
}::timestamptz
params.endDate
}::timestamptz
${filterQuery}
group by 1
order by 2 desc

View File

@ -79,6 +79,7 @@
"@types/passport-jwt": "^3.0.9",
"@types/ping": "^0.4.2",
"@types/request-ip": "^0.0.38",
"@types/supertest": "^6.0.2",
"@types/swagger-ui-express": "^4.1.5",
"@types/tcp-ping": "^0.1.5",
"@types/uuid": "^9.0.7",
@ -88,6 +89,7 @@
"prisma": "5.4.2",
"prisma-json-types-generator": "3.0.3",
"prisma-zod-generator": "0.8.13",
"supertest": "^6.3.4",
"tailwindcss": "^3.3.5",
"vite": "^5.0.12",
"vitest": "^1.2.1"

View File

@ -0,0 +1,39 @@
import { describe, expect, test } from 'vitest';
import { createTestContext } from '../../tests/utils';
import { generateETag } from '../../utils/common';
describe('telemetry router', () => {
const { app, createTestUser, createTestTelemetry } = createTestContext();
describe('/badge', () => {
test('check header', async () => {
const { workspace } = await createTestUser();
const telemetry = await createTestTelemetry(workspace.id);
const { header, status } = await app.get(
`/telemetry/${workspace.id}/${telemetry.id}/badge.svg`
);
expect(status).toBe(200);
expect(header['content-type']).toBe('image/svg+xml; charset=utf-8');
expect(header['cache-control']).toBe(
'no-cache,max-age=0,no-store,s-maxage=0,proxy-revalidate'
);
expect(header['etag']).toBe('"f2a1eaa06e04bf61f14a783fdc8ac38c"');
expect(header['etag']).toBe(generateETag('visitor|0'));
});
test('check header change with count', async () => {
const { workspace } = await createTestUser();
const telemetry = await createTestTelemetry(workspace.id);
const { header, status } = await app.get(
`/telemetry/${workspace.id}/${telemetry.id}/badge.svg?url=https://www.google.com/`
);
expect(status).toBe(200);
expect(header['etag']).toBe('"988dd9b8dab74e167225028b4b19faf7"');
expect(header['etag']).toBe(generateETag('visitor|1'));
});
});
});

View File

@ -3,6 +3,7 @@ import { query, validate } from '../middleware/validate';
import { recordTelemetryEvent, sumTelemetryEvent } from '../model/telemetry';
import { generateETag, numify } from '../utils/common';
import { makeBadge } from 'badge-maker';
import { env } from '../utils/env';
export const telemetryRouter = Router();
@ -60,7 +61,11 @@ telemetryRouter.get(
query('url').optional().isURL()
),
async (req, res) => {
recordTelemetryEvent(req);
if (env.isTest) {
await recordTelemetryEvent(req);
} else {
recordTelemetryEvent(req);
}
res
.header('Content-Type', 'image/gif')
@ -87,7 +92,11 @@ telemetryRouter.get(
const start = req.query.start ? Number(req.query.start) : 0;
const fullNum = req.query.fullNum === 'true';
recordTelemetryEvent(req);
if (env.isTest) {
await recordTelemetryEvent(req);
} else {
recordTelemetryEvent(req);
}
const num = await sumTelemetryEvent(req);
const count = num + start;

76
src/server/tests/utils.ts Normal file
View File

@ -0,0 +1,76 @@
import supertest from 'supertest';
import { afterAll } from 'vitest';
import { app } from '../app';
import { prisma } from '../model/_client';
import { PrismaPromise } from '@prisma/client';
import { createUser } from '../model/user';
import { nanoid } from 'nanoid';
export function createTestContext() {
const testDataCallback: (() => PrismaPromise<any> | Promise<any>)[] = [];
afterAll(async () => {
for (const cb of testDataCallback.reverse()) {
await cb();
}
});
const createTestUser = async () => {
const data = await createUser(nanoid(), '');
testDataCallback.push(async () => {
await prisma.user.delete({
where: { id: data.id },
});
await prisma.workspace.delete({
where: { id: data.currentWorkspace.id },
});
});
return {
user: data,
workspace: data.currentWorkspace,
};
};
const createTestWorkspace = async () => {
const data = await prisma.workspace.create({
data: {
name: 'Test Workspace',
},
});
testDataCallback.push(() =>
prisma.workspace.delete({
where: { id: data.id },
})
);
return data;
};
const createTestTelemetry = async (workspaceId: string) => {
const data = await prisma.telemetry.create({
data: {
name: 'Test Telemetry',
workspaceId,
},
});
testDataCallback.push(() =>
prisma.telemetry.delete({
where: { id: data.id },
})
);
return data;
};
return {
app: supertest(app),
createTestUser,
createTestWorkspace,
createTestTelemetry,
};
}

View File

@ -1,5 +1,6 @@
export const env = {
isProd: process.env.NODE_ENV === 'production',
isTest: process.env.NODE_ENV === 'test',
port: Number(process.env.PORT || 12345),
allowRegister: checkEnvTrusty(process.env.ALLOW_REGISTER),
allowOpenapi: checkEnvTrusty(process.env.ALLOW_OPENAPI),