implement ratelimiting
This commit is contained in:
parent
da9157fa20
commit
bb195d160e
|
@ -1,5 +1,7 @@
|
|||
# view count
|
||||
.count.json
|
||||
# rate limited ips
|
||||
.ratelimit.json
|
||||
|
||||
# ---> Node
|
||||
# Logs
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"port": 8001,
|
||||
"path": "/viewcounter",
|
||||
"site": "zvava.org",
|
||||
"db": ".count.json",
|
||||
"countDB": ".count.json",
|
||||
"ratelimitDB": ".ratelimit.json",
|
||||
"saveTimeout": 5000
|
||||
}
|
||||
|
|
92
index.js
92
index.js
|
@ -7,53 +7,52 @@ let config = readJson("conf.json", {
|
|||
port: 8001,
|
||||
path: "/viewcounter",
|
||||
site: "zvava.org",
|
||||
db: ".count.json",
|
||||
countDB: ".count.json",
|
||||
ratelimitDB: ".ratelimit.json",
|
||||
saveTimeout: 5000,
|
||||
})
|
||||
// read existing view count
|
||||
let count = readJson(config.db)
|
||||
// read databases
|
||||
let count = readJson(config.countDB)
|
||||
let ratelimit = readJson(config.ratelimitDB)
|
||||
// store the save timeout
|
||||
let saveTimeout
|
||||
|
||||
let server = new http.Server((request, response) => {
|
||||
try {
|
||||
if (request.url != config.path)
|
||||
return close(response, 404)
|
||||
if (request.url != config.path)
|
||||
return close(response, 404)
|
||||
|
||||
switch (request.method) {
|
||||
case "POST":
|
||||
const chunks = []
|
||||
request.on("data", chunk => chunks.push(chunk))
|
||||
request.on("end", async () => {
|
||||
let page = Buffer.concat(chunks).toString()
|
||||
switch (request.method) {
|
||||
case "POST":
|
||||
const chunks = []
|
||||
request.on("data", chunk => chunks.push(chunk))
|
||||
request.on("end", async () => {
|
||||
let page = Buffer.concat(chunks).toString()
|
||||
|
||||
// check if page is a valid url
|
||||
if (/\/[a-zA-Z0-9_-]+.html/.test(page) && await validateUrl(page)) {
|
||||
count[page] = count[page] ? count[page] + 1 : 1
|
||||
saveCount()
|
||||
close(response, 200, "counted page view", { views: count[page] })
|
||||
} else {
|
||||
close(response, 300, "invalid url provided")
|
||||
}
|
||||
})
|
||||
break
|
||||
// check if page is a valid url
|
||||
if (/\/[a-zA-Z0-9_-]+.html/.test(page) && await validateUrl(page)) {
|
||||
let message = incrementCount(request.socket.remoteAddress, page)
|
||||
? "counted page view" : "you have seen this page today"
|
||||
close(response, 200, { message, views: count[page] })
|
||||
} else {
|
||||
close(response, 300, "invalid url provided")
|
||||
}
|
||||
})
|
||||
break
|
||||
|
||||
case "GET": close(response, 200, count); break
|
||||
case "GET": close(response, 200, count); break
|
||||
|
||||
default: close(response, "invalid method " + request.method)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
default: close(response, "invalid method " + request.method)
|
||||
}
|
||||
})
|
||||
|
||||
server.listen(config.port)
|
||||
|
||||
function saveCount() {
|
||||
// if a timeout
|
||||
saveTimeout && clearTimeout(saveTimeout)
|
||||
clearTimeout(saveTimeout)
|
||||
saveTimeout = setTimeout(() => {
|
||||
fs.writeFile(config.db, JSON.stringify(count), (err) =>
|
||||
fs.writeFile(config.countDB, JSON.stringify(count), (err) =>
|
||||
err && console.error(err))
|
||||
fs.writeFile(config.ratelimitDB, JSON.stringify(ratelimit), (err) =>
|
||||
err && console.error(err))
|
||||
}, config.saveTimeout)
|
||||
}
|
||||
|
@ -66,10 +65,10 @@ function readJson(file, defaultJson = {}) {
|
|||
}
|
||||
|
||||
// helper function to close an incoming http request
|
||||
function close(res, code = 300, message = "", extraData = {}) {
|
||||
function close(res, code = 300, message = "") {
|
||||
let data = typeof message == "string"
|
||||
? { code, ...extraData, message: message || code.toString() }
|
||||
: { code, ...extraData, ...message }
|
||||
? { code, message: message || code.toString() }
|
||||
: { code, ...message }
|
||||
res.setHeader("Content-type", "application/json")
|
||||
res.writeHead(code)
|
||||
res.end(JSON.stringify(data))
|
||||
|
@ -98,3 +97,30 @@ function validateUrl(url) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
function incrementCount(ip, page) {
|
||||
if (!ip) return false // in case client disconnected
|
||||
|
||||
// calculate date string
|
||||
let d = new Date(); d = d.getMonth() + "-" + d.getDate()
|
||||
// will increment count at end
|
||||
let i = false
|
||||
|
||||
// create ratelimit profile for the ip if it doesn't exist already
|
||||
if (!ratelimit[ip]) ratelimit[ip] = {}
|
||||
|
||||
i = !ratelimit[ip][page]
|
||||
// if last visited date doesn't exist
|
||||
? true
|
||||
// else, check if last visited date was today
|
||||
: ratelimit[ip][page] != d
|
||||
|
||||
if (i) {
|
||||
// update last visited day
|
||||
ratelimit[ip][page] = d
|
||||
// increment page count if it exists, otherwise set page count to 1
|
||||
count[page] = count[page] ? count[page] + 1 : 1
|
||||
saveCount()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue