├── .gitignore
├── LICENSE
├── README.md
├── api
├── lib
│ ├── assignUser.js
│ └── isMobile.js
├── package-lock.json
├── package.json
└── server.js
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
├── manifest.json
└── robots.txt
└── src
├── App.css
├── App.js
├── component
├── About.js
├── ActiveVisitors.js
├── AvgVisitDuration.js
├── Devices.js
├── HoursOfActivityPerDay.js
├── NewVsReturn.js
├── Referrer.js
├── TopPages.js
└── VisitsByCountry.js
├── config.json
├── index.css
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | api/node_modules
3 | /node_modules
4 | /.pnp
5 | /.pnp.js
6 |
7 | # testing
8 | /coverage
9 |
10 | # production
11 | /build
12 |
13 | # misc
14 | /.DS_Store
15 | /.env.local
16 | /.env.development.local
17 | /.env.test.local
18 | /.env.production.local
19 |
20 | /npm-debug.log*
21 | /yarn-debug.log*
22 | /yarn-error.log*
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Daniel31X13
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Open Analytics
4 |
5 |  
6 |
7 | **Self-hosted, accurate analytics tool for your website in a clean minimalist interface.**
8 |
9 | [Demo](https://open-analytics-demo.herokuapp.com/) (w/ fake data)
10 |
11 |
12 |
13 | ---
14 |
15 | This project is a derivation of [open-analytics-core](https://github.com/Daniel31x13/open-analytics-core).
16 | So like its predecessor it logs the following to a MongoDB database: Device Ip, User-agent, IsMobile, Date, Active Time (Seconds), Visited url, Location, Referrer...
17 |
18 | ### And uses those data to visualize:
19 | - Average time on each page
20 | - Current active users
21 | - Most used device (Mobile/Desktop)
22 | - Visits by each country
23 | - Websites most users are comming from
24 | - Top visited links
25 | - New vs returning users
26 | - Sum of all users activity per day
27 |
28 | ---
29 | ### Setup
30 |
31 | #### First Step:
32 | Clone this repository.
33 |
34 | #### Second Step:
35 | Add this code inside the `body` tag of whatever page you want to include in the analytics:
36 |
37 | ```javascript
38 | // If you need to include in multiple pages, add the latter script tag to a seperate .js file to avoid unnecessary lines of code.
39 |
40 |
52 | ```
53 | #### Third Step:
54 | Edit the [`./src/config.json`](src/config.json) file accordingly:
55 | ```javascript
56 | {
57 | "api": { // Edit here accordingly
58 | "port": 8070,
59 | "uri": "mongodb://localhost:27017",
60 | "databaseName": "DatabaseName",
61 | "collectionName": "CollectionName",
62 | "address": "http://192.168.1.7" // DONT include the "/" at the end
63 | },
64 | "tls_support": { // Leave this as is if you dont need HTTPS support
65 | "enabled": false,
66 | "key": "", // key.pem file path (relative to "./api/server.js")
67 | "cert": "" // cert.pem file path (relative to "./api/server.js")
68 | }
69 | }
70 | ```
71 |
72 | #### Fourth Step:
73 | Run `(cd api && node server.js) & npm start` in the main folder, the dashboard will be available in [http://localhost:3000](http://localhost:3000/).
74 |
--------------------------------------------------------------------------------
/api/lib/assignUser.js:
--------------------------------------------------------------------------------
1 | const isMobile = require("./isMobile");
2 |
3 | function assignUser(clientIp, country, clientHeader, clientURL, clientReferrer, time, activeTime, isNewUser) {
4 | return { // Store client info in an object
5 | "Ip": clientIp,
6 | "Location": country,
7 | "User-Agent": clientHeader,
8 | "IsMobile": isMobile(clientHeader),
9 | "Url": clientURL,
10 | "Referrer": clientReferrer,
11 | "Date": time,
12 | "ActiveTimeInSecond": activeTime,
13 | "isNewUser": isNewUser
14 | }
15 | }
16 |
17 | module.exports = assignUser;
18 |
--------------------------------------------------------------------------------
/api/lib/isMobile.js:
--------------------------------------------------------------------------------
1 | function isMobile(clientHeader) { // Checks if client is mobile
2 | const toMatch = [
3 | /Android/i,
4 | /webOS/i,
5 | /iPhone/i,
6 | /iPad/i,
7 | /iPod/i,
8 | /BlackBerry/i,
9 | /Windows Phone/i
10 | ];
11 |
12 | return toMatch.some((toMatchItem) => {
13 | return clientHeader.match(toMatchItem);
14 | });
15 | }
16 |
17 | module.exports = isMobile;
--------------------------------------------------------------------------------
/api/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "open-analytics-core",
3 | "version": "1.1.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "open-analytics-core",
9 | "version": "1.1.0",
10 | "license": "MIT",
11 | "dependencies": {
12 | "cors": "^2.8.5",
13 | "express": "^4.17.1",
14 | "fast-geoip": "^1.1.54",
15 | "mongodb": "^4.3.0",
16 | "socket.io": "^4.4.0"
17 | }
18 | },
19 | "node_modules/@socket.io/base64-arraybuffer": {
20 | "version": "1.0.2",
21 | "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
22 | "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==",
23 | "engines": {
24 | "node": ">= 0.6.0"
25 | }
26 | },
27 | "node_modules/@types/component-emitter": {
28 | "version": "1.2.11",
29 | "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz",
30 | "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ=="
31 | },
32 | "node_modules/@types/cookie": {
33 | "version": "0.4.1",
34 | "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
35 | "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
36 | },
37 | "node_modules/@types/cors": {
38 | "version": "2.8.12",
39 | "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
40 | "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw=="
41 | },
42 | "node_modules/@types/node": {
43 | "version": "17.0.9",
44 | "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.9.tgz",
45 | "integrity": "sha512-5dNBXu/FOER+EXnyah7rn8xlNrfMOQb/qXnw4NQgLkCygKBKhdmF/CA5oXVOKZLBEahw8s2WP9LxIcN/oDDRgQ=="
46 | },
47 | "node_modules/@types/webidl-conversions": {
48 | "version": "6.1.1",
49 | "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz",
50 | "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q=="
51 | },
52 | "node_modules/@types/whatwg-url": {
53 | "version": "8.2.1",
54 | "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.1.tgz",
55 | "integrity": "sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ==",
56 | "dependencies": {
57 | "@types/node": "*",
58 | "@types/webidl-conversions": "*"
59 | }
60 | },
61 | "node_modules/accepts": {
62 | "version": "1.3.7",
63 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
64 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
65 | "dependencies": {
66 | "mime-types": "~2.1.24",
67 | "negotiator": "0.6.2"
68 | },
69 | "engines": {
70 | "node": ">= 0.6"
71 | }
72 | },
73 | "node_modules/array-flatten": {
74 | "version": "1.1.1",
75 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
76 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
77 | },
78 | "node_modules/base64-js": {
79 | "version": "1.5.1",
80 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
81 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
82 | "funding": [
83 | {
84 | "type": "github",
85 | "url": "https://github.com/sponsors/feross"
86 | },
87 | {
88 | "type": "patreon",
89 | "url": "https://www.patreon.com/feross"
90 | },
91 | {
92 | "type": "consulting",
93 | "url": "https://feross.org/support"
94 | }
95 | ]
96 | },
97 | "node_modules/base64id": {
98 | "version": "2.0.0",
99 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
100 | "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
101 | "engines": {
102 | "node": "^4.5.0 || >= 5.9"
103 | }
104 | },
105 | "node_modules/body-parser": {
106 | "version": "1.19.0",
107 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
108 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
109 | "dependencies": {
110 | "bytes": "3.1.0",
111 | "content-type": "~1.0.4",
112 | "debug": "2.6.9",
113 | "depd": "~1.1.2",
114 | "http-errors": "1.7.2",
115 | "iconv-lite": "0.4.24",
116 | "on-finished": "~2.3.0",
117 | "qs": "6.7.0",
118 | "raw-body": "2.4.0",
119 | "type-is": "~1.6.17"
120 | },
121 | "engines": {
122 | "node": ">= 0.8"
123 | }
124 | },
125 | "node_modules/body-parser/node_modules/debug": {
126 | "version": "2.6.9",
127 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
128 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
129 | "dependencies": {
130 | "ms": "2.0.0"
131 | }
132 | },
133 | "node_modules/body-parser/node_modules/ms": {
134 | "version": "2.0.0",
135 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
136 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
137 | },
138 | "node_modules/bson": {
139 | "version": "4.6.1",
140 | "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.1.tgz",
141 | "integrity": "sha512-I1LQ7Hz5zgwR4QquilLNZwbhPw0Apx7i7X9kGMBTsqPdml/03Q9NBtD9nt/19ahjlphktQImrnderxqpzeVDjw==",
142 | "dependencies": {
143 | "buffer": "^5.6.0"
144 | },
145 | "engines": {
146 | "node": ">=6.9.0"
147 | }
148 | },
149 | "node_modules/buffer": {
150 | "version": "5.7.1",
151 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
152 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
153 | "funding": [
154 | {
155 | "type": "github",
156 | "url": "https://github.com/sponsors/feross"
157 | },
158 | {
159 | "type": "patreon",
160 | "url": "https://www.patreon.com/feross"
161 | },
162 | {
163 | "type": "consulting",
164 | "url": "https://feross.org/support"
165 | }
166 | ],
167 | "dependencies": {
168 | "base64-js": "^1.3.1",
169 | "ieee754": "^1.1.13"
170 | }
171 | },
172 | "node_modules/bytes": {
173 | "version": "3.1.0",
174 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
175 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
176 | "engines": {
177 | "node": ">= 0.8"
178 | }
179 | },
180 | "node_modules/component-emitter": {
181 | "version": "1.3.0",
182 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
183 | "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
184 | },
185 | "node_modules/content-disposition": {
186 | "version": "0.5.3",
187 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
188 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
189 | "dependencies": {
190 | "safe-buffer": "5.1.2"
191 | },
192 | "engines": {
193 | "node": ">= 0.6"
194 | }
195 | },
196 | "node_modules/content-type": {
197 | "version": "1.0.4",
198 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
199 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
200 | "engines": {
201 | "node": ">= 0.6"
202 | }
203 | },
204 | "node_modules/cookie": {
205 | "version": "0.4.1",
206 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
207 | "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
208 | "engines": {
209 | "node": ">= 0.6"
210 | }
211 | },
212 | "node_modules/cookie-signature": {
213 | "version": "1.0.6",
214 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
215 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
216 | },
217 | "node_modules/cors": {
218 | "version": "2.8.5",
219 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
220 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
221 | "dependencies": {
222 | "object-assign": "^4",
223 | "vary": "^1"
224 | },
225 | "engines": {
226 | "node": ">= 0.10"
227 | }
228 | },
229 | "node_modules/denque": {
230 | "version": "2.0.1",
231 | "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz",
232 | "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==",
233 | "engines": {
234 | "node": ">=0.10"
235 | }
236 | },
237 | "node_modules/depd": {
238 | "version": "1.1.2",
239 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
240 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
241 | "engines": {
242 | "node": ">= 0.6"
243 | }
244 | },
245 | "node_modules/destroy": {
246 | "version": "1.0.4",
247 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
248 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
249 | },
250 | "node_modules/ee-first": {
251 | "version": "1.1.1",
252 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
253 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
254 | },
255 | "node_modules/encodeurl": {
256 | "version": "1.0.2",
257 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
258 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
259 | "engines": {
260 | "node": ">= 0.8"
261 | }
262 | },
263 | "node_modules/engine.io": {
264 | "version": "6.1.1",
265 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.1.tgz",
266 | "integrity": "sha512-AyMc20q8JUUdvKd46+thc9o7yCZ6iC6MoBCChG5Z1XmFMpp+2+y/oKvwpZTUJB0KCjxScw1dV9c2h5pjiYBLuQ==",
267 | "dependencies": {
268 | "@types/cookie": "^0.4.1",
269 | "@types/cors": "^2.8.12",
270 | "@types/node": ">=10.0.0",
271 | "accepts": "~1.3.4",
272 | "base64id": "2.0.0",
273 | "cookie": "~0.4.1",
274 | "cors": "~2.8.5",
275 | "debug": "~4.3.1",
276 | "engine.io-parser": "~5.0.0",
277 | "ws": "~8.2.3"
278 | },
279 | "engines": {
280 | "node": ">=10.0.0"
281 | }
282 | },
283 | "node_modules/engine.io-parser": {
284 | "version": "5.0.3",
285 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
286 | "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
287 | "dependencies": {
288 | "@socket.io/base64-arraybuffer": "~1.0.2"
289 | },
290 | "engines": {
291 | "node": ">=10.0.0"
292 | }
293 | },
294 | "node_modules/engine.io/node_modules/debug": {
295 | "version": "4.3.3",
296 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
297 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
298 | "dependencies": {
299 | "ms": "2.1.2"
300 | },
301 | "engines": {
302 | "node": ">=6.0"
303 | },
304 | "peerDependenciesMeta": {
305 | "supports-color": {
306 | "optional": true
307 | }
308 | }
309 | },
310 | "node_modules/engine.io/node_modules/ms": {
311 | "version": "2.1.2",
312 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
313 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
314 | },
315 | "node_modules/escape-html": {
316 | "version": "1.0.3",
317 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
318 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
319 | },
320 | "node_modules/etag": {
321 | "version": "1.8.1",
322 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
323 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
324 | "engines": {
325 | "node": ">= 0.6"
326 | }
327 | },
328 | "node_modules/express": {
329 | "version": "4.17.1",
330 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
331 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
332 | "dependencies": {
333 | "accepts": "~1.3.7",
334 | "array-flatten": "1.1.1",
335 | "body-parser": "1.19.0",
336 | "content-disposition": "0.5.3",
337 | "content-type": "~1.0.4",
338 | "cookie": "0.4.0",
339 | "cookie-signature": "1.0.6",
340 | "debug": "2.6.9",
341 | "depd": "~1.1.2",
342 | "encodeurl": "~1.0.2",
343 | "escape-html": "~1.0.3",
344 | "etag": "~1.8.1",
345 | "finalhandler": "~1.1.2",
346 | "fresh": "0.5.2",
347 | "merge-descriptors": "1.0.1",
348 | "methods": "~1.1.2",
349 | "on-finished": "~2.3.0",
350 | "parseurl": "~1.3.3",
351 | "path-to-regexp": "0.1.7",
352 | "proxy-addr": "~2.0.5",
353 | "qs": "6.7.0",
354 | "range-parser": "~1.2.1",
355 | "safe-buffer": "5.1.2",
356 | "send": "0.17.1",
357 | "serve-static": "1.14.1",
358 | "setprototypeof": "1.1.1",
359 | "statuses": "~1.5.0",
360 | "type-is": "~1.6.18",
361 | "utils-merge": "1.0.1",
362 | "vary": "~1.1.2"
363 | },
364 | "engines": {
365 | "node": ">= 0.10.0"
366 | }
367 | },
368 | "node_modules/express/node_modules/cookie": {
369 | "version": "0.4.0",
370 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
371 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
372 | "engines": {
373 | "node": ">= 0.6"
374 | }
375 | },
376 | "node_modules/express/node_modules/debug": {
377 | "version": "2.6.9",
378 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
379 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
380 | "dependencies": {
381 | "ms": "2.0.0"
382 | }
383 | },
384 | "node_modules/express/node_modules/ms": {
385 | "version": "2.0.0",
386 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
387 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
388 | },
389 | "node_modules/fast-geoip": {
390 | "version": "1.1.54",
391 | "resolved": "https://registry.npmjs.org/fast-geoip/-/fast-geoip-1.1.54.tgz",
392 | "integrity": "sha512-X3tpXwYWh+aPn69mHBnWleOBNTSuN6eutF8olXCEBekdYj5ERFnNDPGhiqQbjnJT+fVLx3vWnvanX2EpLcakCQ=="
393 | },
394 | "node_modules/finalhandler": {
395 | "version": "1.1.2",
396 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
397 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
398 | "dependencies": {
399 | "debug": "2.6.9",
400 | "encodeurl": "~1.0.2",
401 | "escape-html": "~1.0.3",
402 | "on-finished": "~2.3.0",
403 | "parseurl": "~1.3.3",
404 | "statuses": "~1.5.0",
405 | "unpipe": "~1.0.0"
406 | },
407 | "engines": {
408 | "node": ">= 0.8"
409 | }
410 | },
411 | "node_modules/finalhandler/node_modules/debug": {
412 | "version": "2.6.9",
413 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
414 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
415 | "dependencies": {
416 | "ms": "2.0.0"
417 | }
418 | },
419 | "node_modules/finalhandler/node_modules/ms": {
420 | "version": "2.0.0",
421 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
422 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
423 | },
424 | "node_modules/forwarded": {
425 | "version": "0.2.0",
426 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
427 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
428 | "engines": {
429 | "node": ">= 0.6"
430 | }
431 | },
432 | "node_modules/fresh": {
433 | "version": "0.5.2",
434 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
435 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
436 | "engines": {
437 | "node": ">= 0.6"
438 | }
439 | },
440 | "node_modules/http-errors": {
441 | "version": "1.7.2",
442 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
443 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
444 | "dependencies": {
445 | "depd": "~1.1.2",
446 | "inherits": "2.0.3",
447 | "setprototypeof": "1.1.1",
448 | "statuses": ">= 1.5.0 < 2",
449 | "toidentifier": "1.0.0"
450 | },
451 | "engines": {
452 | "node": ">= 0.6"
453 | }
454 | },
455 | "node_modules/iconv-lite": {
456 | "version": "0.4.24",
457 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
458 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
459 | "dependencies": {
460 | "safer-buffer": ">= 2.1.2 < 3"
461 | },
462 | "engines": {
463 | "node": ">=0.10.0"
464 | }
465 | },
466 | "node_modules/ieee754": {
467 | "version": "1.2.1",
468 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
469 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
470 | "funding": [
471 | {
472 | "type": "github",
473 | "url": "https://github.com/sponsors/feross"
474 | },
475 | {
476 | "type": "patreon",
477 | "url": "https://www.patreon.com/feross"
478 | },
479 | {
480 | "type": "consulting",
481 | "url": "https://feross.org/support"
482 | }
483 | ]
484 | },
485 | "node_modules/inherits": {
486 | "version": "2.0.3",
487 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
488 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
489 | },
490 | "node_modules/ip": {
491 | "version": "1.1.5",
492 | "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
493 | "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
494 | },
495 | "node_modules/ipaddr.js": {
496 | "version": "1.9.1",
497 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
498 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
499 | "engines": {
500 | "node": ">= 0.10"
501 | }
502 | },
503 | "node_modules/media-typer": {
504 | "version": "0.3.0",
505 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
506 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
507 | "engines": {
508 | "node": ">= 0.6"
509 | }
510 | },
511 | "node_modules/memory-pager": {
512 | "version": "1.5.0",
513 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
514 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
515 | "optional": true
516 | },
517 | "node_modules/merge-descriptors": {
518 | "version": "1.0.1",
519 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
520 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
521 | },
522 | "node_modules/methods": {
523 | "version": "1.1.2",
524 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
525 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
526 | "engines": {
527 | "node": ">= 0.6"
528 | }
529 | },
530 | "node_modules/mime": {
531 | "version": "1.6.0",
532 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
533 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
534 | "bin": {
535 | "mime": "cli.js"
536 | },
537 | "engines": {
538 | "node": ">=4"
539 | }
540 | },
541 | "node_modules/mime-db": {
542 | "version": "1.51.0",
543 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
544 | "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
545 | "engines": {
546 | "node": ">= 0.6"
547 | }
548 | },
549 | "node_modules/mime-types": {
550 | "version": "2.1.34",
551 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
552 | "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
553 | "dependencies": {
554 | "mime-db": "1.51.0"
555 | },
556 | "engines": {
557 | "node": ">= 0.6"
558 | }
559 | },
560 | "node_modules/mongodb": {
561 | "version": "4.3.0",
562 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.3.0.tgz",
563 | "integrity": "sha512-ovq9ZD9wEvab+LsaQOiwtne1Sy2egaHW8K/H5M18Tv+V5PgTRi+qdmxDGlbm94TSL3h56m6amstptu115Nzgow==",
564 | "dependencies": {
565 | "bson": "^4.6.1",
566 | "denque": "^2.0.1",
567 | "mongodb-connection-string-url": "^2.3.2",
568 | "socks": "^2.6.1"
569 | },
570 | "engines": {
571 | "node": ">=12.9.0"
572 | },
573 | "optionalDependencies": {
574 | "saslprep": "^1.0.3"
575 | }
576 | },
577 | "node_modules/mongodb-connection-string-url": {
578 | "version": "2.4.1",
579 | "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.4.1.tgz",
580 | "integrity": "sha512-d5Kd2bVsKcSA7YI/yo57fSTtMwRQdFkvc5IZwod1RRxJtECeWPPSo7zqcUGJELifRA//Igs4spVtYAmvFCatug==",
581 | "dependencies": {
582 | "@types/whatwg-url": "^8.2.1",
583 | "whatwg-url": "^11.0.0"
584 | }
585 | },
586 | "node_modules/negotiator": {
587 | "version": "0.6.2",
588 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
589 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
590 | "engines": {
591 | "node": ">= 0.6"
592 | }
593 | },
594 | "node_modules/object-assign": {
595 | "version": "4.1.1",
596 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
597 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
598 | "engines": {
599 | "node": ">=0.10.0"
600 | }
601 | },
602 | "node_modules/on-finished": {
603 | "version": "2.3.0",
604 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
605 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
606 | "dependencies": {
607 | "ee-first": "1.1.1"
608 | },
609 | "engines": {
610 | "node": ">= 0.8"
611 | }
612 | },
613 | "node_modules/parseurl": {
614 | "version": "1.3.3",
615 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
616 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
617 | "engines": {
618 | "node": ">= 0.8"
619 | }
620 | },
621 | "node_modules/path-to-regexp": {
622 | "version": "0.1.7",
623 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
624 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
625 | },
626 | "node_modules/proxy-addr": {
627 | "version": "2.0.7",
628 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
629 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
630 | "dependencies": {
631 | "forwarded": "0.2.0",
632 | "ipaddr.js": "1.9.1"
633 | },
634 | "engines": {
635 | "node": ">= 0.10"
636 | }
637 | },
638 | "node_modules/punycode": {
639 | "version": "2.1.1",
640 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
641 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
642 | "engines": {
643 | "node": ">=6"
644 | }
645 | },
646 | "node_modules/qs": {
647 | "version": "6.7.0",
648 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
649 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
650 | "engines": {
651 | "node": ">=0.6"
652 | }
653 | },
654 | "node_modules/range-parser": {
655 | "version": "1.2.1",
656 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
657 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
658 | "engines": {
659 | "node": ">= 0.6"
660 | }
661 | },
662 | "node_modules/raw-body": {
663 | "version": "2.4.0",
664 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
665 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
666 | "dependencies": {
667 | "bytes": "3.1.0",
668 | "http-errors": "1.7.2",
669 | "iconv-lite": "0.4.24",
670 | "unpipe": "1.0.0"
671 | },
672 | "engines": {
673 | "node": ">= 0.8"
674 | }
675 | },
676 | "node_modules/safe-buffer": {
677 | "version": "5.1.2",
678 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
679 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
680 | },
681 | "node_modules/safer-buffer": {
682 | "version": "2.1.2",
683 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
684 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
685 | },
686 | "node_modules/saslprep": {
687 | "version": "1.0.3",
688 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
689 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
690 | "optional": true,
691 | "dependencies": {
692 | "sparse-bitfield": "^3.0.3"
693 | },
694 | "engines": {
695 | "node": ">=6"
696 | }
697 | },
698 | "node_modules/send": {
699 | "version": "0.17.1",
700 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
701 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
702 | "dependencies": {
703 | "debug": "2.6.9",
704 | "depd": "~1.1.2",
705 | "destroy": "~1.0.4",
706 | "encodeurl": "~1.0.2",
707 | "escape-html": "~1.0.3",
708 | "etag": "~1.8.1",
709 | "fresh": "0.5.2",
710 | "http-errors": "~1.7.2",
711 | "mime": "1.6.0",
712 | "ms": "2.1.1",
713 | "on-finished": "~2.3.0",
714 | "range-parser": "~1.2.1",
715 | "statuses": "~1.5.0"
716 | },
717 | "engines": {
718 | "node": ">= 0.8.0"
719 | }
720 | },
721 | "node_modules/send/node_modules/debug": {
722 | "version": "2.6.9",
723 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
724 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
725 | "dependencies": {
726 | "ms": "2.0.0"
727 | }
728 | },
729 | "node_modules/send/node_modules/debug/node_modules/ms": {
730 | "version": "2.0.0",
731 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
732 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
733 | },
734 | "node_modules/send/node_modules/ms": {
735 | "version": "2.1.1",
736 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
737 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
738 | },
739 | "node_modules/serve-static": {
740 | "version": "1.14.1",
741 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
742 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
743 | "dependencies": {
744 | "encodeurl": "~1.0.2",
745 | "escape-html": "~1.0.3",
746 | "parseurl": "~1.3.3",
747 | "send": "0.17.1"
748 | },
749 | "engines": {
750 | "node": ">= 0.8.0"
751 | }
752 | },
753 | "node_modules/setprototypeof": {
754 | "version": "1.1.1",
755 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
756 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
757 | },
758 | "node_modules/smart-buffer": {
759 | "version": "4.2.0",
760 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
761 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
762 | "engines": {
763 | "node": ">= 6.0.0",
764 | "npm": ">= 3.0.0"
765 | }
766 | },
767 | "node_modules/socket.io": {
768 | "version": "4.4.1",
769 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz",
770 | "integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==",
771 | "dependencies": {
772 | "accepts": "~1.3.4",
773 | "base64id": "~2.0.0",
774 | "debug": "~4.3.2",
775 | "engine.io": "~6.1.0",
776 | "socket.io-adapter": "~2.3.3",
777 | "socket.io-parser": "~4.0.4"
778 | },
779 | "engines": {
780 | "node": ">=10.0.0"
781 | }
782 | },
783 | "node_modules/socket.io-adapter": {
784 | "version": "2.3.3",
785 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz",
786 | "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ=="
787 | },
788 | "node_modules/socket.io-parser": {
789 | "version": "4.0.4",
790 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz",
791 | "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==",
792 | "dependencies": {
793 | "@types/component-emitter": "^1.2.10",
794 | "component-emitter": "~1.3.0",
795 | "debug": "~4.3.1"
796 | },
797 | "engines": {
798 | "node": ">=10.0.0"
799 | }
800 | },
801 | "node_modules/socket.io-parser/node_modules/debug": {
802 | "version": "4.3.3",
803 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
804 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
805 | "dependencies": {
806 | "ms": "2.1.2"
807 | },
808 | "engines": {
809 | "node": ">=6.0"
810 | },
811 | "peerDependenciesMeta": {
812 | "supports-color": {
813 | "optional": true
814 | }
815 | }
816 | },
817 | "node_modules/socket.io-parser/node_modules/ms": {
818 | "version": "2.1.2",
819 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
820 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
821 | },
822 | "node_modules/socket.io/node_modules/debug": {
823 | "version": "4.3.3",
824 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
825 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
826 | "dependencies": {
827 | "ms": "2.1.2"
828 | },
829 | "engines": {
830 | "node": ">=6.0"
831 | },
832 | "peerDependenciesMeta": {
833 | "supports-color": {
834 | "optional": true
835 | }
836 | }
837 | },
838 | "node_modules/socket.io/node_modules/ms": {
839 | "version": "2.1.2",
840 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
841 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
842 | },
843 | "node_modules/socks": {
844 | "version": "2.6.1",
845 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz",
846 | "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==",
847 | "dependencies": {
848 | "ip": "^1.1.5",
849 | "smart-buffer": "^4.1.0"
850 | },
851 | "engines": {
852 | "node": ">= 10.13.0",
853 | "npm": ">= 3.0.0"
854 | }
855 | },
856 | "node_modules/sparse-bitfield": {
857 | "version": "3.0.3",
858 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
859 | "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
860 | "optional": true,
861 | "dependencies": {
862 | "memory-pager": "^1.0.2"
863 | }
864 | },
865 | "node_modules/statuses": {
866 | "version": "1.5.0",
867 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
868 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
869 | "engines": {
870 | "node": ">= 0.6"
871 | }
872 | },
873 | "node_modules/toidentifier": {
874 | "version": "1.0.0",
875 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
876 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
877 | "engines": {
878 | "node": ">=0.6"
879 | }
880 | },
881 | "node_modules/tr46": {
882 | "version": "3.0.0",
883 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
884 | "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
885 | "dependencies": {
886 | "punycode": "^2.1.1"
887 | },
888 | "engines": {
889 | "node": ">=12"
890 | }
891 | },
892 | "node_modules/type-is": {
893 | "version": "1.6.18",
894 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
895 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
896 | "dependencies": {
897 | "media-typer": "0.3.0",
898 | "mime-types": "~2.1.24"
899 | },
900 | "engines": {
901 | "node": ">= 0.6"
902 | }
903 | },
904 | "node_modules/unpipe": {
905 | "version": "1.0.0",
906 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
907 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
908 | "engines": {
909 | "node": ">= 0.8"
910 | }
911 | },
912 | "node_modules/utils-merge": {
913 | "version": "1.0.1",
914 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
915 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
916 | "engines": {
917 | "node": ">= 0.4.0"
918 | }
919 | },
920 | "node_modules/vary": {
921 | "version": "1.1.2",
922 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
923 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
924 | "engines": {
925 | "node": ">= 0.8"
926 | }
927 | },
928 | "node_modules/webidl-conversions": {
929 | "version": "7.0.0",
930 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
931 | "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
932 | "engines": {
933 | "node": ">=12"
934 | }
935 | },
936 | "node_modules/whatwg-url": {
937 | "version": "11.0.0",
938 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
939 | "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
940 | "dependencies": {
941 | "tr46": "^3.0.0",
942 | "webidl-conversions": "^7.0.0"
943 | },
944 | "engines": {
945 | "node": ">=12"
946 | }
947 | },
948 | "node_modules/ws": {
949 | "version": "8.2.3",
950 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
951 | "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
952 | "engines": {
953 | "node": ">=10.0.0"
954 | },
955 | "peerDependencies": {
956 | "bufferutil": "^4.0.1",
957 | "utf-8-validate": "^5.0.2"
958 | },
959 | "peerDependenciesMeta": {
960 | "bufferutil": {
961 | "optional": true
962 | },
963 | "utf-8-validate": {
964 | "optional": true
965 | }
966 | }
967 | }
968 | },
969 | "dependencies": {
970 | "@socket.io/base64-arraybuffer": {
971 | "version": "1.0.2",
972 | "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
973 | "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ=="
974 | },
975 | "@types/component-emitter": {
976 | "version": "1.2.11",
977 | "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz",
978 | "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ=="
979 | },
980 | "@types/cookie": {
981 | "version": "0.4.1",
982 | "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
983 | "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
984 | },
985 | "@types/cors": {
986 | "version": "2.8.12",
987 | "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
988 | "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw=="
989 | },
990 | "@types/node": {
991 | "version": "17.0.9",
992 | "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.9.tgz",
993 | "integrity": "sha512-5dNBXu/FOER+EXnyah7rn8xlNrfMOQb/qXnw4NQgLkCygKBKhdmF/CA5oXVOKZLBEahw8s2WP9LxIcN/oDDRgQ=="
994 | },
995 | "@types/webidl-conversions": {
996 | "version": "6.1.1",
997 | "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz",
998 | "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q=="
999 | },
1000 | "@types/whatwg-url": {
1001 | "version": "8.2.1",
1002 | "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.1.tgz",
1003 | "integrity": "sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ==",
1004 | "requires": {
1005 | "@types/node": "*",
1006 | "@types/webidl-conversions": "*"
1007 | }
1008 | },
1009 | "accepts": {
1010 | "version": "1.3.7",
1011 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
1012 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
1013 | "requires": {
1014 | "mime-types": "~2.1.24",
1015 | "negotiator": "0.6.2"
1016 | }
1017 | },
1018 | "array-flatten": {
1019 | "version": "1.1.1",
1020 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
1021 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
1022 | },
1023 | "base64-js": {
1024 | "version": "1.5.1",
1025 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
1026 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
1027 | },
1028 | "base64id": {
1029 | "version": "2.0.0",
1030 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
1031 | "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
1032 | },
1033 | "body-parser": {
1034 | "version": "1.19.0",
1035 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
1036 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
1037 | "requires": {
1038 | "bytes": "3.1.0",
1039 | "content-type": "~1.0.4",
1040 | "debug": "2.6.9",
1041 | "depd": "~1.1.2",
1042 | "http-errors": "1.7.2",
1043 | "iconv-lite": "0.4.24",
1044 | "on-finished": "~2.3.0",
1045 | "qs": "6.7.0",
1046 | "raw-body": "2.4.0",
1047 | "type-is": "~1.6.17"
1048 | },
1049 | "dependencies": {
1050 | "debug": {
1051 | "version": "2.6.9",
1052 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
1053 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
1054 | "requires": {
1055 | "ms": "2.0.0"
1056 | }
1057 | },
1058 | "ms": {
1059 | "version": "2.0.0",
1060 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1061 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
1062 | }
1063 | }
1064 | },
1065 | "bson": {
1066 | "version": "4.6.1",
1067 | "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.1.tgz",
1068 | "integrity": "sha512-I1LQ7Hz5zgwR4QquilLNZwbhPw0Apx7i7X9kGMBTsqPdml/03Q9NBtD9nt/19ahjlphktQImrnderxqpzeVDjw==",
1069 | "requires": {
1070 | "buffer": "^5.6.0"
1071 | }
1072 | },
1073 | "buffer": {
1074 | "version": "5.7.1",
1075 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
1076 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
1077 | "requires": {
1078 | "base64-js": "^1.3.1",
1079 | "ieee754": "^1.1.13"
1080 | }
1081 | },
1082 | "bytes": {
1083 | "version": "3.1.0",
1084 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
1085 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
1086 | },
1087 | "component-emitter": {
1088 | "version": "1.3.0",
1089 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
1090 | "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
1091 | },
1092 | "content-disposition": {
1093 | "version": "0.5.3",
1094 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
1095 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
1096 | "requires": {
1097 | "safe-buffer": "5.1.2"
1098 | }
1099 | },
1100 | "content-type": {
1101 | "version": "1.0.4",
1102 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
1103 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
1104 | },
1105 | "cookie": {
1106 | "version": "0.4.1",
1107 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
1108 | "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
1109 | },
1110 | "cookie-signature": {
1111 | "version": "1.0.6",
1112 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
1113 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
1114 | },
1115 | "cors": {
1116 | "version": "2.8.5",
1117 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
1118 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
1119 | "requires": {
1120 | "object-assign": "^4",
1121 | "vary": "^1"
1122 | }
1123 | },
1124 | "denque": {
1125 | "version": "2.0.1",
1126 | "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz",
1127 | "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ=="
1128 | },
1129 | "depd": {
1130 | "version": "1.1.2",
1131 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
1132 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
1133 | },
1134 | "destroy": {
1135 | "version": "1.0.4",
1136 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
1137 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
1138 | },
1139 | "ee-first": {
1140 | "version": "1.1.1",
1141 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
1142 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
1143 | },
1144 | "encodeurl": {
1145 | "version": "1.0.2",
1146 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
1147 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
1148 | },
1149 | "engine.io": {
1150 | "version": "6.1.1",
1151 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.1.tgz",
1152 | "integrity": "sha512-AyMc20q8JUUdvKd46+thc9o7yCZ6iC6MoBCChG5Z1XmFMpp+2+y/oKvwpZTUJB0KCjxScw1dV9c2h5pjiYBLuQ==",
1153 | "requires": {
1154 | "@types/cookie": "^0.4.1",
1155 | "@types/cors": "^2.8.12",
1156 | "@types/node": ">=10.0.0",
1157 | "accepts": "~1.3.4",
1158 | "base64id": "2.0.0",
1159 | "cookie": "~0.4.1",
1160 | "cors": "~2.8.5",
1161 | "debug": "~4.3.1",
1162 | "engine.io-parser": "~5.0.0",
1163 | "ws": "~8.2.3"
1164 | },
1165 | "dependencies": {
1166 | "debug": {
1167 | "version": "4.3.3",
1168 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
1169 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
1170 | "requires": {
1171 | "ms": "2.1.2"
1172 | }
1173 | },
1174 | "ms": {
1175 | "version": "2.1.2",
1176 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1177 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
1178 | }
1179 | }
1180 | },
1181 | "engine.io-parser": {
1182 | "version": "5.0.3",
1183 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
1184 | "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
1185 | "requires": {
1186 | "@socket.io/base64-arraybuffer": "~1.0.2"
1187 | }
1188 | },
1189 | "escape-html": {
1190 | "version": "1.0.3",
1191 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
1192 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
1193 | },
1194 | "etag": {
1195 | "version": "1.8.1",
1196 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
1197 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
1198 | },
1199 | "express": {
1200 | "version": "4.17.1",
1201 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
1202 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
1203 | "requires": {
1204 | "accepts": "~1.3.7",
1205 | "array-flatten": "1.1.1",
1206 | "body-parser": "1.19.0",
1207 | "content-disposition": "0.5.3",
1208 | "content-type": "~1.0.4",
1209 | "cookie": "0.4.0",
1210 | "cookie-signature": "1.0.6",
1211 | "debug": "2.6.9",
1212 | "depd": "~1.1.2",
1213 | "encodeurl": "~1.0.2",
1214 | "escape-html": "~1.0.3",
1215 | "etag": "~1.8.1",
1216 | "finalhandler": "~1.1.2",
1217 | "fresh": "0.5.2",
1218 | "merge-descriptors": "1.0.1",
1219 | "methods": "~1.1.2",
1220 | "on-finished": "~2.3.0",
1221 | "parseurl": "~1.3.3",
1222 | "path-to-regexp": "0.1.7",
1223 | "proxy-addr": "~2.0.5",
1224 | "qs": "6.7.0",
1225 | "range-parser": "~1.2.1",
1226 | "safe-buffer": "5.1.2",
1227 | "send": "0.17.1",
1228 | "serve-static": "1.14.1",
1229 | "setprototypeof": "1.1.1",
1230 | "statuses": "~1.5.0",
1231 | "type-is": "~1.6.18",
1232 | "utils-merge": "1.0.1",
1233 | "vary": "~1.1.2"
1234 | },
1235 | "dependencies": {
1236 | "cookie": {
1237 | "version": "0.4.0",
1238 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
1239 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
1240 | },
1241 | "debug": {
1242 | "version": "2.6.9",
1243 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
1244 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
1245 | "requires": {
1246 | "ms": "2.0.0"
1247 | }
1248 | },
1249 | "ms": {
1250 | "version": "2.0.0",
1251 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1252 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
1253 | }
1254 | }
1255 | },
1256 | "fast-geoip": {
1257 | "version": "1.1.54",
1258 | "resolved": "https://registry.npmjs.org/fast-geoip/-/fast-geoip-1.1.54.tgz",
1259 | "integrity": "sha512-X3tpXwYWh+aPn69mHBnWleOBNTSuN6eutF8olXCEBekdYj5ERFnNDPGhiqQbjnJT+fVLx3vWnvanX2EpLcakCQ=="
1260 | },
1261 | "finalhandler": {
1262 | "version": "1.1.2",
1263 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
1264 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
1265 | "requires": {
1266 | "debug": "2.6.9",
1267 | "encodeurl": "~1.0.2",
1268 | "escape-html": "~1.0.3",
1269 | "on-finished": "~2.3.0",
1270 | "parseurl": "~1.3.3",
1271 | "statuses": "~1.5.0",
1272 | "unpipe": "~1.0.0"
1273 | },
1274 | "dependencies": {
1275 | "debug": {
1276 | "version": "2.6.9",
1277 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
1278 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
1279 | "requires": {
1280 | "ms": "2.0.0"
1281 | }
1282 | },
1283 | "ms": {
1284 | "version": "2.0.0",
1285 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1286 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
1287 | }
1288 | }
1289 | },
1290 | "forwarded": {
1291 | "version": "0.2.0",
1292 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
1293 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
1294 | },
1295 | "fresh": {
1296 | "version": "0.5.2",
1297 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
1298 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
1299 | },
1300 | "http-errors": {
1301 | "version": "1.7.2",
1302 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
1303 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
1304 | "requires": {
1305 | "depd": "~1.1.2",
1306 | "inherits": "2.0.3",
1307 | "setprototypeof": "1.1.1",
1308 | "statuses": ">= 1.5.0 < 2",
1309 | "toidentifier": "1.0.0"
1310 | }
1311 | },
1312 | "iconv-lite": {
1313 | "version": "0.4.24",
1314 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
1315 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
1316 | "requires": {
1317 | "safer-buffer": ">= 2.1.2 < 3"
1318 | }
1319 | },
1320 | "ieee754": {
1321 | "version": "1.2.1",
1322 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
1323 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
1324 | },
1325 | "inherits": {
1326 | "version": "2.0.3",
1327 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
1328 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
1329 | },
1330 | "ip": {
1331 | "version": "1.1.5",
1332 | "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
1333 | "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
1334 | },
1335 | "ipaddr.js": {
1336 | "version": "1.9.1",
1337 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
1338 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
1339 | },
1340 | "media-typer": {
1341 | "version": "0.3.0",
1342 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
1343 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
1344 | },
1345 | "memory-pager": {
1346 | "version": "1.5.0",
1347 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
1348 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
1349 | "optional": true
1350 | },
1351 | "merge-descriptors": {
1352 | "version": "1.0.1",
1353 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
1354 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
1355 | },
1356 | "methods": {
1357 | "version": "1.1.2",
1358 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
1359 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
1360 | },
1361 | "mime": {
1362 | "version": "1.6.0",
1363 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
1364 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
1365 | },
1366 | "mime-db": {
1367 | "version": "1.51.0",
1368 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
1369 | "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="
1370 | },
1371 | "mime-types": {
1372 | "version": "2.1.34",
1373 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
1374 | "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
1375 | "requires": {
1376 | "mime-db": "1.51.0"
1377 | }
1378 | },
1379 | "mongodb": {
1380 | "version": "4.3.0",
1381 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.3.0.tgz",
1382 | "integrity": "sha512-ovq9ZD9wEvab+LsaQOiwtne1Sy2egaHW8K/H5M18Tv+V5PgTRi+qdmxDGlbm94TSL3h56m6amstptu115Nzgow==",
1383 | "requires": {
1384 | "bson": "^4.6.1",
1385 | "denque": "^2.0.1",
1386 | "mongodb-connection-string-url": "^2.3.2",
1387 | "saslprep": "^1.0.3",
1388 | "socks": "^2.6.1"
1389 | }
1390 | },
1391 | "mongodb-connection-string-url": {
1392 | "version": "2.4.1",
1393 | "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.4.1.tgz",
1394 | "integrity": "sha512-d5Kd2bVsKcSA7YI/yo57fSTtMwRQdFkvc5IZwod1RRxJtECeWPPSo7zqcUGJELifRA//Igs4spVtYAmvFCatug==",
1395 | "requires": {
1396 | "@types/whatwg-url": "^8.2.1",
1397 | "whatwg-url": "^11.0.0"
1398 | }
1399 | },
1400 | "negotiator": {
1401 | "version": "0.6.2",
1402 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
1403 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
1404 | },
1405 | "object-assign": {
1406 | "version": "4.1.1",
1407 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1408 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
1409 | },
1410 | "on-finished": {
1411 | "version": "2.3.0",
1412 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
1413 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
1414 | "requires": {
1415 | "ee-first": "1.1.1"
1416 | }
1417 | },
1418 | "parseurl": {
1419 | "version": "1.3.3",
1420 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1421 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
1422 | },
1423 | "path-to-regexp": {
1424 | "version": "0.1.7",
1425 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
1426 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
1427 | },
1428 | "proxy-addr": {
1429 | "version": "2.0.7",
1430 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
1431 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
1432 | "requires": {
1433 | "forwarded": "0.2.0",
1434 | "ipaddr.js": "1.9.1"
1435 | }
1436 | },
1437 | "punycode": {
1438 | "version": "2.1.1",
1439 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
1440 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
1441 | },
1442 | "qs": {
1443 | "version": "6.7.0",
1444 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
1445 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
1446 | },
1447 | "range-parser": {
1448 | "version": "1.2.1",
1449 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1450 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
1451 | },
1452 | "raw-body": {
1453 | "version": "2.4.0",
1454 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
1455 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
1456 | "requires": {
1457 | "bytes": "3.1.0",
1458 | "http-errors": "1.7.2",
1459 | "iconv-lite": "0.4.24",
1460 | "unpipe": "1.0.0"
1461 | }
1462 | },
1463 | "safe-buffer": {
1464 | "version": "5.1.2",
1465 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
1466 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
1467 | },
1468 | "safer-buffer": {
1469 | "version": "2.1.2",
1470 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1471 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
1472 | },
1473 | "saslprep": {
1474 | "version": "1.0.3",
1475 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
1476 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
1477 | "optional": true,
1478 | "requires": {
1479 | "sparse-bitfield": "^3.0.3"
1480 | }
1481 | },
1482 | "send": {
1483 | "version": "0.17.1",
1484 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
1485 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
1486 | "requires": {
1487 | "debug": "2.6.9",
1488 | "depd": "~1.1.2",
1489 | "destroy": "~1.0.4",
1490 | "encodeurl": "~1.0.2",
1491 | "escape-html": "~1.0.3",
1492 | "etag": "~1.8.1",
1493 | "fresh": "0.5.2",
1494 | "http-errors": "~1.7.2",
1495 | "mime": "1.6.0",
1496 | "ms": "2.1.1",
1497 | "on-finished": "~2.3.0",
1498 | "range-parser": "~1.2.1",
1499 | "statuses": "~1.5.0"
1500 | },
1501 | "dependencies": {
1502 | "debug": {
1503 | "version": "2.6.9",
1504 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
1505 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
1506 | "requires": {
1507 | "ms": "2.0.0"
1508 | },
1509 | "dependencies": {
1510 | "ms": {
1511 | "version": "2.0.0",
1512 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1513 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
1514 | }
1515 | }
1516 | },
1517 | "ms": {
1518 | "version": "2.1.1",
1519 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
1520 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
1521 | }
1522 | }
1523 | },
1524 | "serve-static": {
1525 | "version": "1.14.1",
1526 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
1527 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
1528 | "requires": {
1529 | "encodeurl": "~1.0.2",
1530 | "escape-html": "~1.0.3",
1531 | "parseurl": "~1.3.3",
1532 | "send": "0.17.1"
1533 | }
1534 | },
1535 | "setprototypeof": {
1536 | "version": "1.1.1",
1537 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
1538 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
1539 | },
1540 | "smart-buffer": {
1541 | "version": "4.2.0",
1542 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
1543 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="
1544 | },
1545 | "socket.io": {
1546 | "version": "4.4.1",
1547 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz",
1548 | "integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==",
1549 | "requires": {
1550 | "accepts": "~1.3.4",
1551 | "base64id": "~2.0.0",
1552 | "debug": "~4.3.2",
1553 | "engine.io": "~6.1.0",
1554 | "socket.io-adapter": "~2.3.3",
1555 | "socket.io-parser": "~4.0.4"
1556 | },
1557 | "dependencies": {
1558 | "debug": {
1559 | "version": "4.3.3",
1560 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
1561 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
1562 | "requires": {
1563 | "ms": "2.1.2"
1564 | }
1565 | },
1566 | "ms": {
1567 | "version": "2.1.2",
1568 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1569 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
1570 | }
1571 | }
1572 | },
1573 | "socket.io-adapter": {
1574 | "version": "2.3.3",
1575 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz",
1576 | "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ=="
1577 | },
1578 | "socket.io-parser": {
1579 | "version": "4.0.4",
1580 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz",
1581 | "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==",
1582 | "requires": {
1583 | "@types/component-emitter": "^1.2.10",
1584 | "component-emitter": "~1.3.0",
1585 | "debug": "~4.3.1"
1586 | },
1587 | "dependencies": {
1588 | "debug": {
1589 | "version": "4.3.3",
1590 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
1591 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
1592 | "requires": {
1593 | "ms": "2.1.2"
1594 | }
1595 | },
1596 | "ms": {
1597 | "version": "2.1.2",
1598 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1599 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
1600 | }
1601 | }
1602 | },
1603 | "socks": {
1604 | "version": "2.6.1",
1605 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz",
1606 | "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==",
1607 | "requires": {
1608 | "ip": "^1.1.5",
1609 | "smart-buffer": "^4.1.0"
1610 | }
1611 | },
1612 | "sparse-bitfield": {
1613 | "version": "3.0.3",
1614 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
1615 | "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
1616 | "optional": true,
1617 | "requires": {
1618 | "memory-pager": "^1.0.2"
1619 | }
1620 | },
1621 | "statuses": {
1622 | "version": "1.5.0",
1623 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
1624 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
1625 | },
1626 | "toidentifier": {
1627 | "version": "1.0.0",
1628 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
1629 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
1630 | },
1631 | "tr46": {
1632 | "version": "3.0.0",
1633 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
1634 | "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
1635 | "requires": {
1636 | "punycode": "^2.1.1"
1637 | }
1638 | },
1639 | "type-is": {
1640 | "version": "1.6.18",
1641 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1642 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1643 | "requires": {
1644 | "media-typer": "0.3.0",
1645 | "mime-types": "~2.1.24"
1646 | }
1647 | },
1648 | "unpipe": {
1649 | "version": "1.0.0",
1650 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1651 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
1652 | },
1653 | "utils-merge": {
1654 | "version": "1.0.1",
1655 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1656 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
1657 | },
1658 | "vary": {
1659 | "version": "1.1.2",
1660 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1661 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
1662 | },
1663 | "webidl-conversions": {
1664 | "version": "7.0.0",
1665 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
1666 | "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
1667 | },
1668 | "whatwg-url": {
1669 | "version": "11.0.0",
1670 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
1671 | "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
1672 | "requires": {
1673 | "tr46": "^3.0.0",
1674 | "webidl-conversions": "^7.0.0"
1675 | }
1676 | },
1677 | "ws": {
1678 | "version": "8.2.3",
1679 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
1680 | "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
1681 | "requires": {}
1682 | }
1683 | }
1684 | }
1685 |
--------------------------------------------------------------------------------
/api/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "open-analytics-core",
3 | "version": "1.1.0",
4 | "description": "Open analytics backend.",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node index"
8 | },
9 | "author": "Daniel",
10 | "license": "MIT",
11 | "dependencies": {
12 | "cors": "^2.8.5",
13 | "express": "^4.17.1",
14 | "fast-geoip": "^1.1.54",
15 | "mongodb": "^4.3.0",
16 | "socket.io": "^4.4.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/api/server.js:
--------------------------------------------------------------------------------
1 | const assignUser = require("./lib/assignUser");
2 | const geoip = require("fast-geoip");
3 | const express = require("express");
4 | const { MongoClient } = require("mongodb");
5 | const app = express();
6 | const config = require("../src/config.json");
7 | const fs = require('fs');
8 |
9 | tls_key = () => {
10 | try {
11 | return fs.readFileSync(config.tls_support.key);
12 | } catch (err) {
13 | return "";
14 | console.log(err);
15 | }
16 | }
17 |
18 | tls_cert = () => {
19 | try {
20 | return fs.readFileSync(config.tls_support.cert);
21 | } catch (err) {
22 | return "";
23 | console.log(err);
24 | }
25 | }
26 |
27 | const option = {
28 | key: tls_key,
29 | cert: tls_cert,
30 | cors: {
31 | origin: '*',
32 | }
33 | }
34 |
35 | const http = require(config.tls_support.enabled ? "https" : "http").createServer(option, app);
36 | const io = require("socket.io")(http, {
37 | cors: {
38 | origin: "*",
39 | },
40 | });
41 |
42 | const cors = require("cors");
43 |
44 | const PORT = config.api.port;
45 | let count = 0;
46 | let totalClients = [];
47 |
48 | // Connect to MongoDB
49 | const uri = config.api.uri;
50 | const MClient = new MongoClient(uri);
51 | MClient.connect().catch(console.error);
52 |
53 | let results;
54 | app.use(async (req, res, next) => {
55 | let lastMonth = new Date(
56 | new Date().getFullYear(),
57 | new Date().getMonth() - 1,
58 | new Date().getDate()
59 | );
60 | results = await MClient.db(config.api.databaseName)
61 | .collection(config.api.collectionName)
62 | .find({Date: {$gt: lastMonth}})
63 | .toArray();
64 | next();
65 | });
66 |
67 | app.get("/api", cors(), (req, res) => {
68 | res.send(results);
69 | });
70 |
71 | app.get("/api/active", cors(), (req, res) => {
72 | res.send(count.toString());
73 | });
74 |
75 | // On connection
76 | io.on("connection", async (socket) => {
77 | let clientIp = socket.request.connection.remoteAddress;
78 | let clientHeader = socket.request.headers["user-agent"];
79 | let geo = await geoip.lookup(clientIp.slice(7)); // Check location by ip (Does not work for local ip addresses)
80 | let clientURL;
81 | let clientReferrer;
82 | let isNewUser;
83 |
84 | // Add user to array
85 | totalClients.push(clientIp);
86 |
87 | // The number of unique clients from the array
88 | count = totalClients.filter(function (item, pos) {
89 | return totalClients.indexOf(item) == pos;
90 | }).length;
91 |
92 | const connectDate = new Date();
93 |
94 | console.clear(); // Clear previous log
95 |
96 | io.emit("socketClientID", socket.client.id);
97 | socket.on("clientMessage", (data) => { // Get data from client
98 | clientURL = data.url;
99 | clientReferrer = data.referrer;
100 | isNewUser = data.isFirstVisit;
101 | isNewUser = data.isFirstVisit;
102 | });
103 |
104 | // On disconnection
105 | socket.on("disconnect", () => {
106 | const disconnectDate = new Date();
107 | let activeTime = Math.ceil(
108 | (disconnectDate - connectDate) / 1000
109 | ).toString();
110 |
111 | // Remove user from array & update the number of unique clients from the array
112 | totalClients.splice(totalClients.indexOf(clientIp), 1);
113 | count = totalClients.filter(function (item, pos) {
114 | return totalClients.indexOf(item) == pos;
115 | }).length;
116 |
117 | let user = assignUser(
118 | clientIp,
119 | geo.country,
120 | clientHeader,
121 | clientURL,
122 | clientReferrer,
123 | connectDate,
124 | activeTime,
125 | isNewUser
126 | );
127 |
128 | // Add to DB
129 | MClient.db(config.api.databaseName).collection(config.api.collectionName).insertOne(user);
130 | });
131 | });
132 |
133 | http.listen(PORT, () => {
134 | console.clear();
135 | console.log("ALL SET!");
136 | });
137 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pen-analytics",
3 | "description": "Self-hosted, accurate analytics tool for your website in a clean minimalist interface.",
4 | "version": "0.1.0",
5 | "private": true,
6 | "dependencies": {
7 | "@testing-library/jest-dom": "^5.16.1",
8 | "@testing-library/react": "^12.1.2",
9 | "@testing-library/user-event": "^13.5.0",
10 | "chart.js": "^3.7.0",
11 | "react": "^17.0.2",
12 | "react-chartjs-2": "^4.0.1",
13 | "react-dom": "^17.0.2",
14 | "react-scripts": "5.0.0"
15 | },
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build",
19 | "test": "react-scripts test",
20 | "eject": "react-scripts eject"
21 | },
22 | "eslintConfig": {
23 | "extends": [
24 | "react-app",
25 | "react-app/jest"
26 | ]
27 | },
28 | "browserslist": {
29 | "production": [
30 | ">0.2%",
31 | "not dead",
32 | "not op_mini all"
33 | ],
34 | "development": [
35 | "last 1 chrome version",
36 | "last 1 firefox version",
37 | "last 1 safari version"
38 | ]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daniel31x13/open-analytics/200808ca75f2fa68e49a8253aa418d0aea959e92/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 | React App
13 |
14 |
15 | You need to enable JavaScript to run this app.
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "theme_color": "#000000",
7 | "background_color": "#ffffff"
8 | }
9 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | @media (min-width: 650px) {
2 | .group {
3 | border-radius: 10px;
4 | display: flex;
5 | justify-content: space-evenly;
6 | }
7 |
8 | .group > div {
9 | width: 100%;
10 | }
11 | }
12 |
13 | .container {
14 | display: flex;
15 | flex-direction: column;
16 | padding: 10px;
17 | text-align: center;
18 | max-width: 1200px;
19 | margin-right: auto;
20 | margin-left: auto;
21 | }
22 |
23 | .container > div {
24 | box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
25 | margin-bottom: 10px;
26 | padding: 10px;
27 | }
28 |
29 | .About {
30 | border-radius: 10px;
31 | text-align: center;
32 | }
33 |
34 | .rainbowEffect {
35 | font-size: 1.5em;
36 | font-weight: 700;
37 | text-decoration: underline;
38 | font-family: monospace;
39 | }
40 |
41 | .rainbowEffect:hover {
42 | font-size: 1.5em;
43 | font-weight: 700;
44 | text-decoration: none;
45 | font-family: monospace;
46 | animation: colorRotate 5s linear 0s infinite;
47 | }
48 |
49 | @keyframes colorRotate {
50 | from {
51 | color: #6666ff;
52 | }
53 | 10% {
54 | color: #0099ff;
55 | }
56 | 50% {
57 | color: #00ff00;
58 | }
59 | 75% {
60 | color: #ff3399;
61 | }
62 | 100% {
63 | color: #6666ff;
64 | }
65 | }
66 |
67 | .About h1 {
68 | font-weight: 300;
69 | color: #444444;
70 | background: #FFFFFF;
71 | text-shadow: 1px 0px 1px #CCCCCC, 0px 1px 1px #EEEEEE, 2px 1px 1px #CCCCCC, 1px 2px 1px #EEEEEE, 3px 2px 1px #CCCCCC, 2px 3px 1px #EEEEEE, 4px 3px 1px #CCCCCC, 3px 4px 1px #EEEEEE, 5px 4px 1px #CCCCCC, 4px 5px 1px #EEEEEE, 6px 5px 1px #CCCCCC, 5px 6px 1px #EEEEEE, 7px 6px 1px #CCCCCC;
72 | color: #444444;
73 | background: #FFFFFF;
74 | }
75 |
76 | .About h5 {
77 | color: gray;
78 | }
79 |
80 | .ActiveVisitors {
81 | border-radius: 10px;
82 | text-align: center;
83 | }
84 |
85 | .AvgVisitDuration {
86 | border-radius: 10px;
87 | text-align: center;
88 | }
89 |
90 | .VisitsByCountry {
91 | border-radius: 10px;
92 | text-align: center;
93 | height: 250px;
94 | }
95 |
96 | .Devices {
97 | border-radius: 10px;
98 | text-align: center;
99 | height: 250px;
100 | }
101 |
102 | .TopPages {
103 | height: 300px;
104 | border-radius: 10px;
105 | text-align: center;
106 | }
107 |
108 | .HoursOfActivityPerDay {
109 | height: 300px;
110 | border-radius: 10px;
111 | text-align: center;
112 | }
113 |
114 | .NewVsReturn {
115 | height: 300px;
116 | border-radius: 10px;
117 | text-align: center;
118 | }
119 |
120 | .Referrer {
121 | height: 300px;
122 | border-radius: 10px;
123 | text-align: center;
124 | }
125 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import './App.css';
3 | import ActiveVisitors from './component/ActiveVisitors';
4 | import AvgVisitDuration from './component/AvgVisitDuration';
5 | import Devices from './component/Devices';
6 | import NewVsReturn from './component/NewVsReturn';
7 | import TopPages from './component/TopPages';
8 | import VisitsByCountry from './component/VisitsByCountry';
9 | import HoursOfActivityPerDay from './component/HoursOfActivityPerDay';
10 | import About from './component/About';
11 | import Referrer from './component/Referrer';
12 | import config from "./config.json";
13 |
14 | function App() {
15 | const [stats, setStats] = useState([]);
16 | const [active, setActive] = useState("0");
17 |
18 | useEffect(() => {
19 | async function fetchStats() {
20 | const res = await fetch(config.api.address + ":" + config.api.port + '/api');
21 | const data = await res.json();
22 | data.sort((b, a) => new Date(b.Date) - new Date(a.Date)) // Sort by date
23 | setStats(data);
24 | }
25 | fetchStats();
26 | async function fetchActiveUsers() {
27 | const res = await fetch(config.api.address + ":" + config.api.port + "/api/active");
28 | const data = await res.text();
29 | setActive(parseInt(data));
30 | }
31 | fetchActiveUsers();
32 | }, []);
33 |
34 | return (
35 |
36 |
37 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | );
53 | }
54 |
55 | export default App;
56 |
--------------------------------------------------------------------------------
/src/component/About.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const About = () => {
4 | return (
5 |
Open Analytics
6 |
A somewhat "Minimalist" SEO analytic for your website.
7 |
Check it out on my GitHub!
8 |
);
9 | };
10 |
11 | export default About;
12 |
--------------------------------------------------------------------------------
/src/component/ActiveVisitors.js:
--------------------------------------------------------------------------------
1 | const ActiveVisitors = ({active}) => {
2 | return (
3 |
4 | Right now:
5 |
{active.toString()}
6 | Active Users
7 |
8 | );
9 | };
10 |
11 | export default ActiveVisitors;
12 |
--------------------------------------------------------------------------------
/src/component/AvgVisitDuration.js:
--------------------------------------------------------------------------------
1 | const AvgVisitDuration = ({ time }) => {
2 | function getAvg() {
3 | let Times = time.map((entity) => {return parseInt(entity.ActiveTimeInSecond)});
4 | Times = Times.filter((entity) => {return !Number.isNaN(entity)});
5 |
6 | const average = Times.reduce((a, b) => a + b, 0) / Times.length
7 |
8 | if(!isNaN(average)) {
9 | return (Math.round(average * 100) / 100).toString();
10 | }
11 |
12 | else {
13 | return "0";
14 | }
15 | }
16 |
17 | return Average time on each page:
{getAvg()} Seconds;
18 | };
19 |
20 | export default AvgVisitDuration;
21 |
--------------------------------------------------------------------------------
/src/component/Devices.js:
--------------------------------------------------------------------------------
1 | import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
2 | import { Pie } from 'react-chartjs-2';
3 |
4 | const Devices = ({devices}) => {
5 | function getDevicesKeys() {
6 | const Devices = devices.map((entity) => {return entity.IsMobile});
7 |
8 | let deviceStats = Devices.reduce((a, b) => a.set(b, (a.get(b) || 0) + 1), new Map());
9 | let devicesKeys = [...deviceStats.keys()].map(entity => entity = entity.toString());
10 |
11 | return devicesKeys;
12 | }
13 |
14 | function getDevicesValue() {
15 | const Devices = devices.map((entity) => {return entity.IsMobile});
16 |
17 | let deviceStats = Devices.reduce((a, b) => a.set(b, (a.get(b) || 0) + 1), new Map());
18 | let devicesValues = [...deviceStats.values()];
19 |
20 | return devicesValues;
21 | }
22 |
23 | const data = {
24 | labels: getDevicesKeys(),
25 | datasets: [
26 | {
27 | label: 'Devices',
28 | data: getDevicesValue(),
29 | backgroundColor: [
30 | 'rgba(255, 99, 132, 0.2)',
31 | 'rgba(54, 162, 235, 0.2)',
32 | ],
33 | borderWidth: 2,
34 | borderColor: 'rgba(100, 0, 100, 1)',
35 | borderRadius: 5
36 | },
37 | ],
38 | };
39 |
40 | return ( );
49 | };
50 |
51 | export default Devices;
52 |
--------------------------------------------------------------------------------
/src/component/HoursOfActivityPerDay.js:
--------------------------------------------------------------------------------
1 | import {
2 | Chart as ChartJS,
3 | CategoryScale,
4 | LinearScale,
5 | BarElement,
6 | Title,
7 | Tooltip,
8 | Legend,
9 | } from 'chart.js';
10 | import { Bar } from 'react-chartjs-2';
11 |
12 | const HoursOfActivityPerDay = ({ activity }) => {
13 | let rawData = activity.map((entity) => { // Parse the information so that only the Date (as the key) and the time (as the value) is extracted
14 | const date = new Date(entity.Date).toISOString().substring(0, 10)
15 | const num = parseInt(entity.ActiveTimeInSecond);
16 | const obj = { [date]: num }
17 | return obj;
18 | });
19 |
20 | let Data = {};
21 |
22 | for(let i = 0; i < rawData.length; ++i){ // If the keys were the same then add the values together
23 | for(let obj in rawData[i]){
24 | Data[obj] = Data[obj] ? Data[obj] + rawData[i][obj] : rawData[i][obj];
25 | }
26 | }
27 |
28 | const date = Object.keys(Data);
29 | const Sec = Object.values(Data).map((entity) => {
30 | return (Math.round(entity / 60 * 100) / 100)
31 | });
32 |
33 | const labels = date;
34 |
35 | const data = {
36 | labels,
37 | datasets: [
38 | {
39 | label: 'Minutes',
40 | data: Sec,
41 | backgroundColor: 'rgba(0, 116, 204, 0.2)',
42 | borderColor: 'rgba(0, 116, 204, 1)',
43 | borderWidth: 2,
44 | maxBarThickness: 50
45 | }
46 | ],
47 | };
48 |
49 | return (
50 |
55 |
);
56 | };
57 |
58 | export default HoursOfActivityPerDay;
59 |
--------------------------------------------------------------------------------
/src/component/NewVsReturn.js:
--------------------------------------------------------------------------------
1 | import 'chart.js/auto';
2 | import { Line } from "react-chartjs-2";
3 |
4 | const NewVsReturn = ({users}) => {
5 | let rawData = users.map((entity) => { // Parse the information so that only the Date (as the key) and the ip (as the value) is extracted
6 | const date = new Date(entity.Date).toISOString().substring(0, 10)
7 | const isNewUser = entity.isNewUser;
8 | const obj = { x: date, y: +isNewUser }
9 | return obj;
10 | });
11 |
12 | let Dates = rawData.map((entity) => {
13 | const obj = entity.x;
14 | return obj;
15 | }).filter(entity => {return entity});
16 |
17 | let uniqueDates = [...new Set(Dates)].sort((a, b) => {
18 | return new Date(a) - new Date(b);
19 | });
20 |
21 | function newUsers() {
22 | let rawUsers = rawData.filter((entity) => {
23 | return Boolean(entity.y)
24 | });
25 |
26 | for(const i in rawUsers) {
27 | let counts = 0;
28 | rawUsers.forEach((x) => {if (x.x === rawUsers[i].x) counts++});
29 | rawUsers[i].y = counts;
30 | }
31 |
32 |
33 | return rawUsers.filter((value, index, self) => index === self.findIndex((t) => t.x === value.x));
34 | }
35 |
36 | function returningUsers() {
37 | let rawUsers = rawData.filter((entity) => {
38 | return !Boolean(entity.y)
39 | }).map((entity) => {
40 | return { x: entity.x , y: 1 }
41 | });
42 |
43 | for(const i in rawUsers) {
44 | let counts = 0;
45 | rawUsers.forEach((x) => {if (x.x === rawUsers[i].x) counts++});
46 | rawUsers[i].y = counts;
47 | }
48 |
49 | return rawUsers.filter((value, index, self) => index === self.findIndex((t) => t.x === value.x));
50 | }
51 |
52 | const data = {
53 | labels: uniqueDates,
54 | datasets: [
55 | {
56 | label: "New Users",
57 | data: newUsers(),
58 | fill: true,
59 | backgroundColor: "rgba(75,192,192,0.2)",
60 | borderColor: "rgba(75,192,192,1)",
61 | borderWidth: 2,
62 | tension: 0.3
63 | },
64 | {
65 | label: "Returning Users",
66 | data: returningUsers(),
67 | fill: true,
68 | backgroundColor: "rgba(0, 116, 204, 0.2)",
69 | borderColor: "rgba(0, 116, 204, 1)",
70 | borderWidth: 2,
71 | tension: 0.3
72 | }
73 | ]
74 | };
75 |
76 | return (
77 |
81 |
);
82 | };
83 |
84 | export default NewVsReturn;
85 |
--------------------------------------------------------------------------------
/src/component/Referrer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | BarElement,
7 | Title,
8 | Tooltip,
9 | Legend,
10 | } from 'chart.js';
11 | import { Bar } from 'react-chartjs-2';
12 |
13 | const Referrer = ({ links }) => {
14 | function getTopLinksKeys() {
15 | const Links = links.map((entity) => {return entity.Referrer});
16 |
17 | let stats = Links.reduce((a, b) => a.set(b, (a.get(b) || 0) + 1), new Map());
18 | // let topKeys = [...stats.keys()];
19 | // let topValues = [...stats.values()];
20 | let topEntries = [...stats.entries()];
21 |
22 | topEntries.sort((function(index){
23 | return function(a, b){
24 | return (a[index] === b[index] ? 0 : (b[index] < a[index] ? -1 : 1));
25 | };
26 | })(1));
27 |
28 | let sortedTop = topEntries.map((i) => { return i[0] });
29 | sortedTop = sortedTop.map((i) => {
30 | if(i == "") {
31 | return 'Browser';
32 | }
33 | else {
34 | return i;
35 | }
36 | });
37 | return sortedTop;
38 | }
39 |
40 | function getTopLinksValues() {
41 | const Links = links.map((entity) => {return entity.Referrer});
42 |
43 | let stats = Links.reduce((a, b) => a.set(b, (a.get(b) || 0) + 1), new Map());
44 | // let topKeys = [...stats.keys()];
45 | // let topValues = [...stats.values()];
46 | let topEntries = [...stats.entries()];
47 |
48 | topEntries.sort((function(index){
49 | return function(a, b){
50 | return (a[index] === b[index] ? 0 : (b[index] < a[index] ? -1 : 1));
51 | };
52 | })(1));
53 |
54 | const sortedTop = topEntries.map((i) => { return i[1] });
55 |
56 | return sortedTop;
57 | }
58 |
59 | const labels = getTopLinksKeys().slice(0, 5);
60 |
61 | const data = {
62 | labels,
63 | datasets: [
64 | {
65 | label: 'Referrer',
66 | data: getTopLinksValues().slice(0, 5),
67 | backgroundColor: 'rgba(0, 116, 204, 0.2)',
68 | borderColor: 'rgba(0, 116, 204, 1)',
69 | borderWidth: 2,
70 | maxBarThickness: 50
71 | }
72 | ],
73 | };
74 |
75 | return (
76 |
81 |
);
82 | };
83 |
84 | export default Referrer;
85 |
--------------------------------------------------------------------------------
/src/component/TopPages.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | BarElement,
7 | Title,
8 | Tooltip,
9 | Legend,
10 | } from 'chart.js';
11 | import { Bar } from 'react-chartjs-2';
12 |
13 | const TopPages = ({pages}) => {
14 | function getTopPagesKeys() {
15 | const Pages = pages.map((entity) => {return entity.Url});
16 |
17 | let stats = Pages.reduce((a, b) => a.set(b, (a.get(b) || 0) + 1), new Map());
18 | // let topKeys = [...stats.keys()];
19 | // let topValues = [...stats.values()];
20 | let topEntries = [...stats.entries()];
21 |
22 | topEntries.sort((function(index){
23 | return function(a, b){
24 | return (a[index] === b[index] ? 0 : (b[index] < a[index] ? -1 : 1));
25 | };
26 | })(1));
27 |
28 | const sortedTop = topEntries.map((i) => { return i[0] });
29 |
30 | return sortedTop;
31 | }
32 |
33 | function getTopPagesValues() {
34 | const Pages = pages.map((entity) => {return entity.Url});
35 |
36 | let stats = Pages.reduce((a, b) => a.set(b, (a.get(b) || 0) + 1), new Map());
37 | // let topKeys = [...stats.keys()];
38 | // let topValues = [...stats.values()];
39 | let topEntries = [...stats.entries()];
40 |
41 | topEntries.sort((function(index){
42 | return function(a, b){
43 | return (a[index] === b[index] ? 0 : (b[index] < a[index] ? -1 : 1));
44 | };
45 | })(1));
46 |
47 | const sortedTop = topEntries.map((i) => { return i[1] });
48 |
49 | return sortedTop;
50 | }
51 |
52 |
53 |
54 | const labels = getTopPagesKeys().slice(0, 5);
55 |
56 | const data = {
57 | labels,
58 | datasets: [
59 | {
60 | label: 'Links',
61 | data: getTopPagesValues().slice(0, 5),
62 | backgroundColor: 'rgba(0, 116, 204, 0.2)',
63 | borderColor: 'rgba(0, 116, 204, 1)',
64 | borderWidth: 2,
65 | maxBarThickness: 50
66 | }
67 | ],
68 | };
69 |
70 | return (
71 |
76 |
);
77 | };
78 |
79 | export default TopPages;
80 |
--------------------------------------------------------------------------------
/src/component/VisitsByCountry.js:
--------------------------------------------------------------------------------
1 | import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
2 | import { Doughnut } from 'react-chartjs-2';
3 |
4 | const VisitsByCountry = ({location}) => {
5 | function getTopCountriesKeys() {
6 | const Location = location.map((entity) => {return entity.Location});
7 |
8 | let stats = Location.reduce((a, b) => a.set(b, (a.get(b) || 0) + 1), new Map());
9 | // let topKeys = [...stats.keys()];
10 | // let topValues = [...stats.values()];
11 | let topEntries = [...stats.entries()];
12 |
13 | topEntries.sort((function(index){
14 | return function(a, b){
15 | return (a[index] === b[index] ? 0 : (b[index] < a[index] ? -1 : 1));
16 | };
17 | })(1));
18 |
19 | const sortedTop = topEntries.map((i) => { return i[0] });
20 |
21 | return sortedTop;
22 | }
23 |
24 | function getTopCountriesValues() {
25 | const Location = location.map((entity) => {return entity.Location});
26 |
27 | let stats = Location.reduce((a, b) => a.set(b, (a.get(b) || 0) + 1), new Map());
28 | // let topKeys = [...stats.keys()];
29 | // let topValues = [...stats.values()];
30 | let topEntries = [...stats.entries()];
31 |
32 | topEntries.sort((function(index){
33 | return function(a, b){
34 | return (a[index] === b[index] ? 0 : (b[index] < a[index] ? -1 : 1));
35 | };
36 | })(1));
37 |
38 | const sortedTop = topEntries.map((i) => { return i[1] });
39 |
40 | return sortedTop;
41 | }
42 |
43 | const data = {
44 | labels: getTopCountriesKeys().slice(0, 5),
45 | datasets: [
46 | {
47 | label: 'Visits by country',
48 | data: getTopCountriesValues().slice(0, 5),
49 | backgroundColor: [
50 | 'rgba(255, 99, 132, 0.2)',
51 | 'rgba(54, 162, 235, 0.2)',
52 | 'rgba(255, 206, 86, 0.2)',
53 | 'rgba(75, 192, 192, 0.2)',
54 | 'rgba(153, 102, 255, 0.2)'
55 | ],
56 | borderWidth: 2,
57 | borderColor: 'rgba(100, 0, 100, 1)',
58 | borderRadius: 5
59 | },
60 | ],
61 | };
62 |
63 | return (
64 |
71 |
);
72 | };
73 |
74 | export default VisitsByCountry;
75 |
--------------------------------------------------------------------------------
/src/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "api": {
3 | "port": 8070,
4 | "uri": "mongodb://localhost:27017",
5 | "databaseName": "DatabaseName",
6 | "collectionName": "CollectionName",
7 | "address": "http://192.168.1.7"
8 | },
9 | "tls_support": {
10 | "enabled": false,
11 | "key": "",
12 | "cert": ""
13 | }
14 | }
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 |
6 | ReactDOM.render(
7 | ,
8 | document.getElementById('root')
9 | );
10 |
--------------------------------------------------------------------------------