Created
April 5, 2025 22:23
-
-
Save andysylvester/fff60fb57417edf46f52014fcfb044db to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const myVersion = "0.4.0", myProductName = "clouddemo"; | |
const fs = require ("fs"); | |
const utils = require ("daveutils"); | |
const qs = require ("querystring"); | |
const request = require ("request"); | |
const davehttp = require ("davehttp"); | |
const xml2js = require ("xml2js"); | |
const reallysimple = require ("reallysimple"); | |
// https://scott2.andysylvester.com/feed/ the feeds I used for my test | |
// https://rsscloud4.wordpress.com/feed/ | |
var config = { | |
port: process.env.PORT || 2005, // was 1422, had tried 8080 | |
flPostEnabled: true, | |
flLogToConsole: true, //davehttp logs each request to the console | |
flTraceOnError: false, //davehttp does not try to catch the error | |
defaultFeedUrl: "https://scott2.andysylvester.com/feed/", //4 | |
thisServer: { //how the cloud server should call us back -- //1 | |
domain: "john.mystatustool.com", | |
port: 80, | |
feedUpdatedCallback: "/feedupdated" | |
} | |
}; | |
var whenLastRequest = new Date (0); | |
var stats = { | |
events: new Array () //2 | |
} | |
var flStatsChanged = false; | |
const fnameStats = "stats.json"; | |
function readStats (callback) { | |
fs.readFile (fnameStats, function (err, jsontext) { | |
try { | |
stats = JSON.parse (jsontext); | |
callback (undefined); | |
} | |
catch (err) { | |
callback (err); | |
} | |
}); | |
} | |
function statsChanged () { | |
flStatsChanged = true; | |
} | |
function logEvent (infoAboutEvent) { | |
if (infoAboutEvent.whenstart !== undefined) { | |
infoAboutEvent.ctSecs = utils.secondsSince (infoAboutEvent.whenstart); | |
delete infoAboutEvent.whenstart; | |
} | |
infoAboutEvent.when = new Date ().toLocaleString (); | |
stats.events.push (infoAboutEvent); //insert at beginning | |
statsChanged (); | |
} | |
function requestWithRedirect (theRequest, callback) { //12/11/22 by DW | |
var myRequest = new Object (); | |
for (var x in theRequest) { | |
myRequest [x] = theRequest [x]; | |
} | |
myRequest.followAllRedirects = false; //we're doing this ourselves | |
myRequest.maxRedirects = (myRequest.maxRedirects === undefined) ? 0 : myRequest.maxRedirects; | |
request (myRequest, function (err, response, body) { | |
const code = response.statusCode; | |
if ((code == 301) || (code == 302)) { //redirect | |
if (myRequest.maxRedirects == 0) { | |
callback (err, response, body); | |
} | |
else { | |
myRequest.maxRedirects--; | |
myRequest.url = response.headers.location; | |
requestWithRedirect (myRequest, callback); | |
} | |
} | |
else { | |
callback (err, response, body); | |
} | |
}); | |
} | |
function getUrlCloudServer (theCloudElement) { | |
var url = undefined; | |
if ((theCloudElement !== undefined) && (theCloudElement.type == "rsscloud")) { | |
url = "http://" + theCloudElement.domain + ":" + theCloudElement.port + theCloudElement.path; | |
} | |
return (url); | |
} | |
function pleaseNotify (urlCloudServer, feedUrl, thisServer, callback) { //3 | |
function buildParamList (paramtable) { //12/10/22 by DW | |
if (paramtable === undefined) { | |
return (""); | |
} | |
else { | |
var s = ""; | |
for (var x in paramtable) { | |
if (paramtable [x] !== undefined) { //8/4/21 by DW | |
if (s.length > 0) { | |
s += "&"; | |
} | |
s += x + "=" + encodeURIComponent (paramtable [x]); | |
} | |
} | |
return (s); | |
} | |
} | |
const theRequest = { | |
url: urlCloudServer, | |
method: "POST", | |
followAllRedirects: true, | |
maxRedirects: 5, | |
headers: { | |
"Content-Type": "application/x-www-form-urlencoded" | |
}, | |
body: buildParamList ({ | |
domain: thisServer.domain, | |
port: thisServer.port, | |
path: thisServer.feedUpdatedCallback, | |
url1: feedUrl, | |
protocol: "http-post" | |
}) | |
}; | |
requestWithRedirect (theRequest, function (err, response, body) { | |
if (err) { | |
callback (err); | |
} | |
else { | |
callback (undefined, body); | |
} | |
}); | |
} | |
function requestNotification (feedUrl, callback) { | |
const whenstart = new Date (); | |
function getResponseFromXml (xmltext, callback) { | |
var options = { | |
explicitArray: false | |
}; | |
xml2js.parseString (xmltext, options, function (err, jstruct) { | |
if (err) { | |
callback (err); | |
} | |
else { | |
if (jstruct == null) { //12/27/21 by DW | |
let err = {message: "Internal error: xml2js.parseString returned null."}; | |
callback (err); | |
} | |
else { | |
callback (undefined, jstruct); | |
} | |
} | |
}); | |
} | |
reallysimple.readFeed (feedUrl, function (err, theFeed) { | |
if (err) { | |
console.log (err.message); | |
if (callback !== undefined) { | |
callback (err); | |
} | |
} | |
else { | |
var urlCloudServer = getUrlCloudServer (theFeed.cloud); | |
pleaseNotify (urlCloudServer, feedUrl, config.thisServer, function (err, xmltext) { | |
if (err) { | |
console.log ("requestNotification: err.message == " + err.message + ", urlCloudServer == " + urlCloudServer + ", feedUrl == " + feedUrl); | |
logEvent ({ | |
type: "requestNotification", | |
error: err.message, | |
urlCloudServer, | |
feedUrl, | |
whenstart | |
}); | |
if (callback !== undefined) { | |
callback (err); | |
} | |
} | |
else { | |
getResponseFromXml (xmltext, function (err, jstruct) { | |
var theEvent = { | |
type: "requestNotification", | |
urlCloudServer, | |
response: jstruct.notifyResult ["$"], | |
feedUrl, | |
whenstart | |
} | |
logEvent (theEvent); | |
}); | |
if (callback !== undefined) { | |
callback (undefined, theFeed); | |
} | |
} | |
}); | |
} | |
}); | |
} | |
function handlePing (feedUrl, callback) { //5 | |
//12/17/22; 11:14:18 AM by DW | |
//this is where you'd put code that reads the feed, looks for new or updated items | |
//it's the punchline, why we did all this stuff in rssCloud, to get you this bit of info | |
//much faster. | |
callback (undefined, {status: "Got the update. Thanks! :-)"}) | |
} | |
function handleHttpRequest (theRequest) { | |
var now = new Date (); | |
const params = theRequest.params; | |
function returnPlainText (theString) { | |
if (theString === undefined) { | |
theString = ""; | |
} | |
theRequest.httpReturn (200, "text/plain", theString); | |
} | |
function returnNotFound () { | |
theRequest.httpReturn (404, "text/plain", "Not found."); | |
} | |
function returnError (jstruct) { | |
theRequest.httpReturn (500, "application/json", utils.jsonStringify (jstruct)); | |
} | |
switch (theRequest.method) { | |
case "POST": | |
switch (theRequest.lowerpath) { | |
case config.thisServer.feedUpdatedCallback: //6 | |
var jstruct = qs.parse (theRequest.postBody); | |
handlePing (jstruct.url, function (err, pingResponse) { //read the feed, add new stuff to database, etc. | |
returnPlainText (pingResponse.status); | |
logEvent ({ | |
method: "POST", | |
path: config.thisServer.feedUpdatedCallback, | |
params: jstruct, | |
myResponse: pingResponse | |
}); | |
}); | |
break; | |
default: | |
returnNotFound () | |
break; | |
} | |
break; | |
case "GET": | |
switch (theRequest.lowerpath) { | |
case "/now": | |
returnPlainText (new Date ()); | |
return (true); | |
case config.thisServer.feedUpdatedCallback: //7 | |
handlePing (params.url, function (err, pingResponse) { //read the feed, add new stuff to database, etc. | |
logEvent ({ | |
method: "GET", | |
path: config.thisServer.feedUpdatedCallback, | |
params, | |
myResponse: params.challenge | |
}); | |
returnPlainText (params.challenge); | |
}); | |
break; | |
default: | |
returnNotFound (); | |
break; | |
} | |
break; | |
} | |
} | |
function everySecond () { | |
if (utils.secondsSince (whenLastRequest) > 3600) { //request notification once an hour | |
requestNotification (config.defaultFeedUrl); | |
whenLastRequest = new Date (); | |
} | |
if (flStatsChanged) { | |
flStatsChanged = false; | |
fs.writeFile (fnameStats, utils.jsonStringify (stats), function (err) { | |
if (err) { | |
console.log ("everySecond: err.message == " + err.message); | |
} | |
}); | |
} | |
} | |
readStats (function (err) { | |
davehttp.start (config, handleHttpRequest); | |
setInterval (everySecond, 1000); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment