@harshankur/viewcounter 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +115 -0
- package/LICENSE +21 -0
- package/README.md +797 -0
- package/allowed.sample.json +24 -0
- package/config/index.js +362 -0
- package/constants.js +229 -0
- package/db/DatabaseManager.js +704 -0
- package/db/schema.sql +71 -0
- package/dbInfo.sample.json +8 -0
- package/index.js +184 -0
- package/middleware/auth.js +194 -0
- package/middleware/security.js +109 -0
- package/middleware/validation.js +212 -0
- package/package.json +81 -0
- package/routes/analytics.js +456 -0
- package/scripts/setup.js +191 -0
- package/utils/appIdUtils.js +38 -0
- package/utils/errorUtils.js +157 -0
- package/utils/ipUtils.js +63 -0
- package/utils/logger.js +138 -0
- package/utils/privacyUtils.js +107 -0
- package/utils/referrerParser.js +137 -0
- package/utils/secretStore.js +57 -0
- package/utils/stringUtils.js +36 -0
- package/utils/userAgentParser.js +68 -0
package/.env.example
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# ViewCounter configuration
|
|
2
|
+
#
|
|
3
|
+
# Copy to `.env` and fill in. Every value here can also come from dbInfo.json /
|
|
4
|
+
# allowed.json, which take precedence — see config/index.js for the resolution
|
|
5
|
+
# order (config file > environment variable > built-in default).
|
|
6
|
+
#
|
|
7
|
+
# Anything marked Required is checked at startup in production: the server
|
|
8
|
+
# refuses to boot rather than run on a guessable default.
|
|
9
|
+
|
|
10
|
+
# ---------------------------------------------------------------------------
|
|
11
|
+
# Required in production
|
|
12
|
+
# ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
# Database credentials. The server will not start in production while these are
|
|
15
|
+
# still root with an empty password.
|
|
16
|
+
DB_USER=viewcounter
|
|
17
|
+
DB_PASSWORD=
|
|
18
|
+
|
|
19
|
+
# Database to write into. Created automatically when DB_MODE=create.
|
|
20
|
+
DB_NAME=viewcounterdb
|
|
21
|
+
|
|
22
|
+
# Apps allowed to record views, comma-separated. This is an authorization
|
|
23
|
+
# boundary, not a convenience list: an appId absent from it is rejected, and it
|
|
24
|
+
# also determines which tables get created in `create` mode. The placeholder
|
|
25
|
+
# `example_app` is refused in production.
|
|
26
|
+
ALLOWED_APP_IDS=blog,portfolio
|
|
27
|
+
|
|
28
|
+
# Browser origins permitted to call the write endpoints, comma-separated.
|
|
29
|
+
# Required in production: without it, CORS blocks every browser caller. Use the
|
|
30
|
+
# exact scheme+host+port, no trailing slash.
|
|
31
|
+
CORS_ORIGINS=https://example.com,https://www.example.com
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
# Recommended
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
# Credentials for the analytics READ endpoints (/stats, /views, /trends, ...),
|
|
38
|
+
# comma-separated, sent by clients as the `x-api-key` header. Keys shorter than
|
|
39
|
+
# 32 characters are ignored. List several so one consumer's key can be revoked
|
|
40
|
+
# without rotating everyone else's. With none set, the read API returns 503 —
|
|
41
|
+
# it fails closed rather than serving your analytics to anyone who asks.
|
|
42
|
+
# Generate one with:
|
|
43
|
+
# node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
|
44
|
+
READ_API_KEYS=
|
|
45
|
+
|
|
46
|
+
# Credential for the ADMIN API (POST /apps, which provisions a new app and
|
|
47
|
+
# creates its table). A separate tier from READ_API_KEYS on purpose: leaking a
|
|
48
|
+
# tenant's read key must never confer the ability to create apps, and revoking
|
|
49
|
+
# one tier must not force rotating the other. Leave unset to disable
|
|
50
|
+
# provisioning over HTTP entirely.
|
|
51
|
+
ADMIN_API_KEYS=
|
|
52
|
+
|
|
53
|
+
# Number of reverse proxies in front of this service, or a comma-separated list
|
|
54
|
+
# of trusted proxy CIDRs. Leave unset if nothing proxies it.
|
|
55
|
+
#
|
|
56
|
+
# Do NOT set this to `true`. Trusting every hop means any caller can send their
|
|
57
|
+
# own X-Forwarded-For and be believed, which forges geolocation, inflates
|
|
58
|
+
# unique-visitor counts, and lets an attacker rotate the rate-limiter key to
|
|
59
|
+
# bypass it entirely. `true` and `*` are downgraded to a single hop with a
|
|
60
|
+
# warning. Your proxy must set X-Forwarded-For; X-Real-IP alone is not read.
|
|
61
|
+
#TRUST_PROXY=1
|
|
62
|
+
|
|
63
|
+
# Where the visitor-hash secret is persisted. Generated with a CSPRNG on first
|
|
64
|
+
# run at mode 0600. This secret is the only reason a stored visitor hash cannot
|
|
65
|
+
# be brute-forced back to the IP that produced it, so back it up with the same
|
|
66
|
+
# care as the database password — and note that replacing it makes existing
|
|
67
|
+
# hashes unlinkable from new ones (which is deliberate, not a bug).
|
|
68
|
+
#VISITOR_SECRET_PATH=/var/lib/viewcounter/.visitor-secret
|
|
69
|
+
|
|
70
|
+
# Supply the secret directly instead of persisting a file. Useful for
|
|
71
|
+
# containers with a read-only filesystem or an external secrets manager.
|
|
72
|
+
#VISITOR_SECRET=
|
|
73
|
+
|
|
74
|
+
# ---------------------------------------------------------------------------
|
|
75
|
+
# Optional
|
|
76
|
+
# ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
# `connect` uses an existing database; `create` creates the database and one
|
|
79
|
+
# table per allowed appId on startup. Default: connect.
|
|
80
|
+
DB_MODE=connect
|
|
81
|
+
|
|
82
|
+
DB_HOST=127.0.0.1
|
|
83
|
+
DB_PORT=3306
|
|
84
|
+
|
|
85
|
+
# Port the HTTP server listens on. Default: 3030.
|
|
86
|
+
PORT=3030
|
|
87
|
+
|
|
88
|
+
# Defaults to `production`. Set to `development` for local work. This does not
|
|
89
|
+
# change what is returned to callers — errors never include internal detail in
|
|
90
|
+
# any environment — but it does relax the startup checks above.
|
|
91
|
+
#NODE_ENV=production
|
|
92
|
+
|
|
93
|
+
# Operational log verbosity: debug | info | warn | error | silent.
|
|
94
|
+
# The audit trail for state-mutating actions is emitted regardless.
|
|
95
|
+
LOG_LEVEL=info
|
|
96
|
+
|
|
97
|
+
# Rate limiting, applied per client IP across all endpoints. This is the
|
|
98
|
+
# single-abuser backstop.
|
|
99
|
+
RATE_LIMIT_WINDOW_MS=60000
|
|
100
|
+
RATE_LIMIT_MAX=100
|
|
101
|
+
|
|
102
|
+
# Per-app ceiling on the write endpoints, within the same window. Stops one
|
|
103
|
+
# tenant consuming the budget every other tenant on the instance depends on.
|
|
104
|
+
# Keyed on appId only, so it cannot be bypassed by rotating IP addresses.
|
|
105
|
+
# Set 0 to disable (sensible for a single-tenant deployment).
|
|
106
|
+
APP_RATE_LIMIT_MAX=1000
|
|
107
|
+
|
|
108
|
+
# How long a visitor counts as "the same visitor" for deduplication, in hours.
|
|
109
|
+
# Also the rotation period for the visitor hash: after this window the same
|
|
110
|
+
# person hashes differently, so their visits cannot be linked across windows.
|
|
111
|
+
# Set 0 to disable deduplication (the hash still rotates hourly).
|
|
112
|
+
UNIQUE_VISITOR_WINDOW_HOURS=24
|
|
113
|
+
|
|
114
|
+
# Device size buckets accepted on /registerView, comma-separated.
|
|
115
|
+
#ALLOWED_DEVICE_SIZES=small,medium,large
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Harsh Ankur
|
|
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.
|