You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
online-hygiene/index.js

155 lines
5.4 KiB

// for testing without killing current version
// PORT=7789 node .
// if validating tests can replace via e.g
// killall onhygi ; nohup node . &
// to add on tridactyl :
// autocmd DocStart .* js fetch('http://localhost:7788/check?url='+window.location.href).then(r => r.json()).then( r => { if(!r.passed) { if (r.redirect) { window.location.href = r.redirect } else { window.location.href = 'https://ggsc.berkeley.edu/'} } } )
const http = require("http");
const fs = require("fs");
const path = require("path");
const express = require("express"); // could be good to replace with code, no dep
const port = process.env.PORT || 7788;
const configFilePath = path.resolve(__dirname, "config.json")
const usageFilePath = path.resolve(__dirname, "usage.json")
process.title = 'onhygi'
// didn't work on its own with ^C nor killall
process.on('beforeExit', _ => { console.log('trying to save before exit') });
// process.on('exit', _ => { console.log('trying to save on exit') });
// catching signals and do something before exit
['SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT',
'SIGBUS', 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2', 'SIGTERM'
].forEach(function (sig) {
process.on(sig, function () {
terminator(sig);
console.log('signal: ' + sig);
});
});
function terminator(sig) {
if (typeof sig === "string") {
console.log('Received %s - terminating server app ...', sig);
console.log('trying to save on exit')
if (quota.length) fs.writeFileSync(usageFilePath, JSON.stringify( usage ))
// to avoiding busting usage quota
process.exit(1);
}
console.log('Node server stopped.');
}
// from https://stackoverflow.com/a/40574758/1442164
var usage = {}
if (fs.existsSync( usageFilePath ) ){
console.log('found usage file')
usage = JSON.parse( fs.readFileSync(usageFilePath).toString() )
}
console.log('past usage', usage)
// does not apply in a P2P fashion, must rely on local configuration here config.json
let quota = [
// unlimited {id:"ownhome", pattern:/.*fabien\.benetou\.fr.*/, perHour:10, perDay:200},
{id:"twitter",pattern:/.*twitter\.com.*/,perHour:1,perDay:12},
{id:"proton",pattern:/.*proton\.me.*/,perHour:6,perDay:6*12},
{id:"yt",pattern:/https:\/\/www\.youtube\.com.*/,perHour:2,perDay:12}, // allow for redirection
{id:"yts",pattern:/https:\/\/youtube\.com.*/,perHour:2,perDay:12}, // allow for redirection, consider merged IDs
{id:"linkedin",pattern:/.*linkedin\.com.*/,perHour:1,perDay:12},
{id:"reddit",pattern:/.*reddit\.com.*/,perHour:3,perDay:12,redirect:'https://lemmy.world'}, // redirection then enough to read private messages
// reconsider the tridactyl check for permanently open pages, e.g TabEnter rather than DocStart
{id:"element",pattern:/.*element\.io.*/,perHour:1,perDay:12},
{id:"discord",pattern:/.*discord\.com.*/,perHour:1,perDay:12},
]
// could also check on time, e.g not before 7am nor after 10pm as done via tridactyl now
// bypass for e.g presentations
//quota = []
if (fs.existsSync( configFilePath ) ){
const configurationFromFile = JSON.parse( fs.readFileSync( configFilePath ).toString() )
console.log('found quota file')
quota = configurationFromFile
}
console.log('quota', quota)
quota.map( rule => { if (typeof usage[rule.id] == 'undefined') usage[rule.id] = [] } )
// Setup and configure Express http server.
const app = express();
// CORS
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.get('/', (req, res) => {
res.json( quota )
})
app.get('/currentusage', (req, res) => {
res.json(
quota.map( rule => {
let leftOnRule = { id: rule.id }
let now = Date.now()/1000
leftOnRule['perHour'] = rule.perHour - usage[rule.id].filter( t => t>now-60*60).length
leftOnRule['perDay'] = rule.perDay - usage[rule.id].filter( t => t>now-60*60*24).length
return leftOnRule
})
)
})
app.get('/check', (req, res) => {
console.log('starting check')
if (!req.query.url) {
res.sendStatus(400);
console.log('no url found')
return;
}
let matched = false
quota.map( rule => {
if ( req.query.url.match( rule.pattern ) ){
matched = true
console.log('match on', rule.id)
let now = Date.now()/1000
usage[rule.id].push( now )
if ( usage[rule.id].filter( t => t>now-60*60).length > rule.perHour ){
if (rule.redirect)
res.json( { passed: false, msg: 'over quota per day', redirect:rule.redirect } )
else
res.json( { passed: false, msg: 'over quota per day' } )
return
}
if ( usage[rule.id].filter( t => t>now-60*60*24).length > rule.perDay ){
if (rule.redirect)
res.json( { passed: false, msg: 'over quota per day', redirect:rule.redirect } )
else
res.json( { passed: false, msg: 'over quota per day' } )
return
}
res.json( { passed: true, msg: 'within quota' } )
return // returns from map, not get
}
})
if (!matched) {
res.json( { passed: true, msg: 'no quota' } )
console.log('no match')
}
// otherwise assume response already sent
})
/*
const privateKey = fs.readFileSync("naf-key.pem", "utf8");
const certificate = fs.readFileSync("naf.pem", "utf8");
const credentials = { key: privateKey, cert: certificate };
const webServer = https.createServer(credentials, app);
*/
// Start Express http server
const webServer = http.createServer(app);
// Listen on port
webServer.listen(port, () => {
console.log("listening on http://localhost:" + port);
});