expressjs-stats 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of expressjs-stats might be problematic. Click here for more details.
- package/README.md +63 -0
- package/index.js +109 -0
- package/package.json +38 -0
- package/prisma/schema.prisma +18 -0
package/README.md
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# **Simple and easy statistics**
|
2
|
+
|
3
|
+
`expressjs-stats` is a small NPM package that allows you to create simple statistics for your ExpressJS REST API.
|
4
|
+
|
5
|
+
### **Installation**
|
6
|
+
|
7
|
+
```bash
|
8
|
+
npm install expressjs-stats
|
9
|
+
```
|
10
|
+
|
11
|
+
## **Middleware implementation**
|
12
|
+
|
13
|
+
The setup is super easy and done in under a minute. Just import `routesTracker` from `expressjs-stats`:
|
14
|
+
|
15
|
+
```js
|
16
|
+
const { routesTracker } = require('expressjs-stats')
|
17
|
+
```
|
18
|
+
|
19
|
+
After that add it to any route you want to get tracked.
|
20
|
+
|
21
|
+
```js
|
22
|
+
app.get('/', routesTracker, async (req, res) => {
|
23
|
+
return res.send('Awesome!')
|
24
|
+
})
|
25
|
+
```
|
26
|
+
|
27
|
+
And you are completely done. Congratulations!
|
28
|
+
|
29
|
+
## **Get statistics data**
|
30
|
+
|
31
|
+
We do not only want to track them but also access the data. Simply import `getStats` from `expressjs-stats`.
|
32
|
+
|
33
|
+
```js
|
34
|
+
const { routesTracker, getStats } = require('expressjs-stats')
|
35
|
+
```
|
36
|
+
|
37
|
+
Now await this function anywhere you want and get the data.
|
38
|
+
|
39
|
+
```js
|
40
|
+
app.get('/stats', routesTracker, async(req, res) => {
|
41
|
+
const stats = await getStats()
|
42
|
+
return res.send(stats)
|
43
|
+
})
|
44
|
+
```
|
45
|
+
|
46
|
+
There's also the `range` option. You can define the time period in which you want to get all the data. The default value is `alltime`.
|
47
|
+
|
48
|
+
**Examples:**
|
49
|
+
- `20min`
|
50
|
+
- `3d`
|
51
|
+
- `90d`
|
52
|
+
- `1y`
|
53
|
+
|
54
|
+
```js
|
55
|
+
app.get('/stats', routesTracker, async(req, res) => {
|
56
|
+
const stats = await getStats(range = '1h')
|
57
|
+
return res.send(stats)
|
58
|
+
})
|
59
|
+
```
|
60
|
+
|
61
|
+
**Example Response:**
|
62
|
+
```json
|
63
|
+
```
|
package/index.js
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
const { PrismaClient } = require('@prisma/client')
|
2
|
+
const prisma = new PrismaClient()
|
3
|
+
|
4
|
+
const DeviceDetector = require("device-detector-js");
|
5
|
+
const deviceDetector = new DeviceDetector();
|
6
|
+
|
7
|
+
const ms = require('ms')
|
8
|
+
|
9
|
+
async function routesTracker(req, res, next) {
|
10
|
+
const informations = deviceDetector.parse(req.headers['user-agent'])
|
11
|
+
|
12
|
+
var data = {
|
13
|
+
ip: null,
|
14
|
+
browser: null,
|
15
|
+
os: null,
|
16
|
+
device: null,
|
17
|
+
route: null,
|
18
|
+
timestamp: +new Date()
|
19
|
+
}
|
20
|
+
|
21
|
+
try { data.ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress } catch (error) {}
|
22
|
+
if(data.ip == '::1') data.ip = 'localhost'
|
23
|
+
try { data.browser = informations.client.type } catch (error) {}
|
24
|
+
try { data.os = informations.os.name } catch (error) {}
|
25
|
+
try { data.device = informations.device.type } catch (error) {}
|
26
|
+
try { data.route = req.path } catch (error) {}
|
27
|
+
|
28
|
+
await prisma.requests.create({
|
29
|
+
data: data,
|
30
|
+
})
|
31
|
+
|
32
|
+
next()
|
33
|
+
}
|
34
|
+
|
35
|
+
async function getStats( range = 'alltime') {
|
36
|
+
var time_period = 0
|
37
|
+
if(range !== 'alltime') time_period = ms(range)
|
38
|
+
if(!time_period) return 'Invalid range'
|
39
|
+
|
40
|
+
var timestamp_gte = 0
|
41
|
+
if(range !== 'alltime') timestamp_gte = +new Date() - time_period
|
42
|
+
|
43
|
+
const getData = await prisma.requests.findMany({
|
44
|
+
where: {
|
45
|
+
timestamp: {
|
46
|
+
gte: timestamp_gte
|
47
|
+
},
|
48
|
+
}
|
49
|
+
})
|
50
|
+
|
51
|
+
const ips = [...new Set(getData.map(data => data.ip))]
|
52
|
+
const devices = [...new Set(getData.map(data => data.device))]
|
53
|
+
const os = [...new Set(getData.map(data => data.os))]
|
54
|
+
const browsers = [...new Set(getData.map(data => data.browser))]
|
55
|
+
const routes = [...new Set(getData.map(data => data.route))]
|
56
|
+
|
57
|
+
const categories = ['ip', 'device', 'os', 'browser', 'route']
|
58
|
+
const values = [ips, devices, os, browsers, routes]
|
59
|
+
|
60
|
+
var dataArray = {}
|
61
|
+
|
62
|
+
const totalAmount = await prisma.requests.count({ where: { timestamp: { gte: timestamp_gte } } })
|
63
|
+
|
64
|
+
dataArray['time_range'] = range
|
65
|
+
dataArray['total_requests'] = totalAmount
|
66
|
+
dataArray['unique_users'] = ips.length
|
67
|
+
|
68
|
+
for (let index = 0; index < categories.length; index++) {
|
69
|
+
var catgAmount = 0
|
70
|
+
|
71
|
+
if(categories[index] === 'device') catgAmount = await prisma.requests.count({ where: { timestamp: { gte: timestamp_gte }, NOT: { device: null } } })
|
72
|
+
if(categories[index] === 'os') catgAmount = await prisma.requests.count({ where: { timestamp: { gte: timestamp_gte }, NOT: { os: null } } })
|
73
|
+
if(categories[index] === 'browser') catgAmount = await prisma.requests.count({ where: { timestamp: { gte: timestamp_gte }, NOT: { browser: null } } })
|
74
|
+
if(categories[index] === 'route') catgAmount = await prisma.requests.count({ where: { timestamp: { gte: timestamp_gte } } })
|
75
|
+
|
76
|
+
var categoryDataArray = []
|
77
|
+
|
78
|
+
if(categories[index] !== 'ip') {
|
79
|
+
for (let i = 0; i < values[index].length; i++) {
|
80
|
+
const element = values[index][i]
|
81
|
+
|
82
|
+
if(element) {
|
83
|
+
var amount = 0
|
84
|
+
|
85
|
+
if(categories[index] === 'device') amount = await prisma.requests.count({ where: { timestamp: { gte: timestamp_gte }, NOT: { device: null }, device: element } })
|
86
|
+
if(categories[index] === 'os') amount = await prisma.requests.count({ where: { timestamp: { gte: timestamp_gte }, NOT: { os: null }, os: element } })
|
87
|
+
if(categories[index] === 'browser') amount = await prisma.requests.count({ where: { timestamp: { gte: timestamp_gte }, NOT: { browser: null }, browser: element } })
|
88
|
+
if(categories[index] === 'route') amount = await prisma.requests.count({ where: { timestamp: { gte: timestamp_gte }, route: element } })
|
89
|
+
|
90
|
+
const percentage = (amount / catgAmount * 100).toFixed(2)
|
91
|
+
|
92
|
+
categoryDataArray.push({
|
93
|
+
name: element,
|
94
|
+
requests: amount,
|
95
|
+
percentage: percentage + '%'
|
96
|
+
})
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
categoryDataArray.sort((a, b) => parseFloat(b.requests) - parseFloat(a.requests))
|
101
|
+
|
102
|
+
dataArray[categories[index]] = categoryDataArray
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
return dataArray
|
107
|
+
}
|
108
|
+
|
109
|
+
module.exports = { routesTracker, getStats }
|
package/package.json
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
{
|
2
|
+
"name": "expressjs-stats",
|
3
|
+
"version": "1.0.7",
|
4
|
+
"description": "Simple and easy statistics for your expressjs REST API",
|
5
|
+
"main": "index.js",
|
6
|
+
"scripts": {
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
8
|
+
"postinstall": "prisma generate && prisma migrate dev --name init"
|
9
|
+
},
|
10
|
+
"repository": {
|
11
|
+
"type": "git",
|
12
|
+
"url": "git+https://github.com/JannisMilz/expressjs-stats.git"
|
13
|
+
},
|
14
|
+
"keywords": [
|
15
|
+
"expressjs",
|
16
|
+
"statistics",
|
17
|
+
"middleware"
|
18
|
+
],
|
19
|
+
"author": "AquaDev",
|
20
|
+
"license": "ISC",
|
21
|
+
"bugs": {
|
22
|
+
"url": "https://github.com/JannisMilz/expressjs-stats/issues"
|
23
|
+
},
|
24
|
+
"homepage": "https://github.com/JannisMilz/expressjs-stats#readme",
|
25
|
+
"dependencies": {
|
26
|
+
"@prisma/client": "^3.12.0",
|
27
|
+
"prisma": "^3.12.0",
|
28
|
+
"device-detector-js": "^3.0.3",
|
29
|
+
"ms": "^2.1.3"
|
30
|
+
},
|
31
|
+
"devDependencies": {
|
32
|
+
"@types/node": "^17.0.27",
|
33
|
+
"express": "^4.18.0",
|
34
|
+
"sqlite": "^4.1.1",
|
35
|
+
"ts-node": "^10.7.0",
|
36
|
+
"typescript": "^4.6.3"
|
37
|
+
}
|
38
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
generator client {
|
2
|
+
provider = "prisma-client-js"
|
3
|
+
}
|
4
|
+
|
5
|
+
datasource db {
|
6
|
+
provider = "sqlite"
|
7
|
+
url = "file:./requests.db"
|
8
|
+
}
|
9
|
+
|
10
|
+
model requests {
|
11
|
+
id Int @id @default(autoincrement())
|
12
|
+
ip String?
|
13
|
+
device String?
|
14
|
+
os String?
|
15
|
+
browser String?
|
16
|
+
route String
|
17
|
+
timestamp Decimal
|
18
|
+
}
|