master
Dave Winer 7 years ago
parent 7c670e4b33
commit 3d6e454951

@ -266,6 +266,12 @@ There will always be more work to do here. ;-)
### Updates
#### v0.7.8 10/3/17 by DW
New features supports logging over WebSockets. To enable, in config.json set <i>flWebsocketEnabled</i> true, and assign a port to the WS server in <i>websocketPort.</i> On every hit, PagePark will send back a JSON structure containing information about the request.
Note: There's a new dependency, <a href="https://github.com/websockets/ws">nodejs-websocket</a>, so when installing this update you have to do an `npm install` before running pagepark.js.
#### v0.7.7 9/26/17 by DW
Added two config.json options and changed the name of another.

@ -2,7 +2,7 @@
"name": "PagePark",
"description": "A simple Node.js folder-based HTTP server that serves static and dynamic pages for domains.",
"author": "Dave Winer <dave@smallpicture.com>",
"version": "0.7.7",
"version": "0.7.8",
"scripts": {
"start": "node pagepark.js"
},
@ -11,6 +11,7 @@
"mime": "*",
"opmltojs": "*",
"daveutils": "*",
"nodejs-websocket": "*",
"marked": "*"
},
"license": "MIT",

@ -1,4 +1,4 @@
var myVersion = "0.7.7", myProductName = "PagePark";
var myVersion = "0.7.8", myProductName = "PagePark";
/* The MIT License (MIT)
Copyright (c) 2014-2017 Dave Winer
@ -33,9 +33,11 @@ var dns = require ("dns");
var mime = require ("mime"); //1/8/15 by DW
var utils = require ("daveutils"); //6/7/17 by DW
var opmlToJs = require ("opmltojs"); //6/16/17 by DW
const websocket = require ("nodejs-websocket"); //9/29/17 by DW
var pageparkPrefs = {
myPort: 1339, //1/8/15 by DW -- was 80, see note in readme.md
flWebsocketEnabled: false, websocketPort: 1340, //9/29/17 by DW
indexFilename: "index",
flProcessScriptFiles: true, extScriptFiles: "js", //5/5/15 by DW
flProcessMarkdownFiles: true, extMarkdownFiles: "md", //5/5/15 by DW
@ -50,7 +52,7 @@ var pageparkPrefs = {
var pageparkStats = {
ctStarts: 0,
whenLastStart: new Date (0),
ctHits: 0, ctHitsToday: 0,
ctHits: 0, ctHitsToday: 0, ctHitsSinceStart: 0,
whenLastHit: new Date (0),
hitsByDomain: {}
};
@ -64,6 +66,47 @@ var opmlTemplatePath = "prefs/opmlTemplate.txt";
var folderPathFromEnv = process.env.pageparkFolderPath; //1/3/15 by DW
var flEveryMinuteScheduled = false; //7/17/17 by DW
//websockets -- 9/29/17 by DW
var theWsServer = undefined;
function notifySocketSubscribers (verb, jstruct) {
if (theWsServer !== undefined) {
var ctUpdates = 0, now = new Date (), jsontext = "";
if (jstruct !== undefined) { //10/7/16 by DW
jsontext = utils.jsonStringify (jstruct);
}
for (var i = 0; i < theWsServer.connections.length; i++) {
var conn = theWsServer.connections [i];
if (conn.pageParkData !== undefined) { //it's one of ours
try {
conn.sendText (verb + "\r" + jsontext);
conn.pageParkData.whenLastUpdate = now;
conn.pageParkData.ctUpdates++;
ctUpdates++;
}
catch (err) {
console.log ("notifySocketSubscribers: socket #" + i + ": error updating");
}
}
}
}
}
function webSocketStartup () {
if (pageparkPrefs.flWebsocketEnabled) {
try {
theWsServer = websocket.createServer (function (conn) {
conn.pageParkData = {
whenLastUpdate: new Date (0),
ctUpdates: 0
};
});
theWsServer.listen (pageparkPrefs.websocketPort);
}
catch (err) {
console.log ("webSocketStartup: err.message == " + err.message);
}
}
}
function httpExt2MIME (ext) { //12/24/14 by DW
mime.default_type = "text/plain";
return (mime.getType (ext));
@ -167,6 +210,8 @@ function everySecond () {
}
}
function handleHttpRequest (httpRequest, httpResponse) {
var logInfo; //9/30/17 by DW
function hasAcceptHeader (theHeader) {
if (httpRequest.headers.accept === undefined) {
return (false);
@ -233,16 +278,37 @@ function handleHttpRequest (httpRequest, httpResponse) {
}
});
}
function httpRespond (code, type, val, headers) {
if (headers === undefined) {
headers = new Object ();
}
headers ["Content-Type"] = type;
httpResponse.writeHead (code, headers);
val = val.toString ();
httpResponse.end (val);
logInfo.ctSecs = utils.secondsSince (logInfo.when);
logInfo.size = val.length;
logInfo.code = code;
logInfo.type = type;
logInfo.serverStats = {
pageParkVersion: myVersion,
whenStart: pageparkStats.whenLastStart,
ctHits: pageparkStats.ctHits,
ctHitsToday: pageparkStats.ctHitsToday,
ctHitsSinceStart: pageparkStats.ctHitsSinceStart
};
notifySocketSubscribers ("log", logInfo);
}
function return404 () {
getTemplate (pageparkPrefs.error404File, pageparkPrefs.urlDefaultErrorPage, function (htmtext) {
httpResponse.writeHead (404, {"Content-Type": "text/html"});
httpResponse.end (htmtext);
httpRespond (404, "text/html", htmtext);
});
}
function returnRedirect (urlRedirectTo, flPermanent) { //7/30/15 by DW
var code = (flPermanent) ? 301 : 302;
httpResponse.writeHead (code, {"Location": urlRedirectTo, "Content-Type": "text/plain"});
httpResponse.end ("Redirect to " + urlRedirectTo + ".");
httpRespond (code, "text/plain", "Redirect to " + urlRedirectTo + ".", {"Location": urlRedirectTo})
}
function findSpecificFile (folder, specificFname, callback) {
specificFname = specificFname.toLowerCase (); //7/16/15 by DW
@ -338,8 +404,7 @@ function handleHttpRequest (httpRequest, httpResponse) {
}
else {
processResponse (f, data, config, function (code, type, text) {
httpResponse.writeHead (code, {"Content-Type": type});
httpResponse.end (text);
httpRespond (code, type, text);
});
}
});
@ -369,8 +434,7 @@ function handleHttpRequest (httpRequest, httpResponse) {
function handleError (err) {
if (err) {
console.log ("delegateRequest: error == " + err.message);
httpResponse.writeHead (500, {"Content-Type": "text/plain"});
httpResponse.end (err.message);
httpRespond (500, "text/plain", err.message);
}
}
var req = httpRequest.pipe (request (theRequest));
@ -391,6 +455,7 @@ function handleHttpRequest (httpRequest, httpResponse) {
try {
var parsedUrl = urlpack.parse (httpRequest.url, true), host, lowerhost, port, referrer;
var lowerpath = parsedUrl.pathname.toLowerCase (), now = new Date ();
var remoteAddress = httpRequest.connection.remoteAddress;
//set host, port
host = httpRequest.headers.host;
if (utils.stringContains (host, ":")) {
@ -407,6 +472,22 @@ function handleHttpRequest (httpRequest, httpResponse) {
referrer = "";
}
//clean up remoteAddress -- 9/29/17 by DW
if (utils.beginsWith (remoteAddress, "::ffff:")) {
remoteAddress = utils.stringDelete (remoteAddress, 1, 7);
}
//set up logInfo -- 9/30/17 by DW
logInfo = {
when: now,
method: httpRequest.method,
host: host,
port: port,
path: parsedUrl.pathname,
lowerpath: lowerpath,
referrer: referrer,
params: parsedUrl.query,
remoteAddress: remoteAddress
};
//stats
//hits by domain
if (pageparkStats.hitsByDomain [lowerhost] == undefined) {
@ -421,12 +502,13 @@ function handleHttpRequest (httpRequest, httpResponse) {
}
pageparkStats.ctHits++;
pageparkStats.ctHitsToday++;
pageparkStats.ctHitsSinceStart++; //9/30/17 by DW
pageparkStats.whenLastHit = now;
flStatsDirty = true;
//log the request
dns.reverse (httpRequest.connection.remoteAddress, function (err, domains) {
var client = httpRequest.connection.remoteAddress;
dns.reverse (remoteAddress, function (err, domains) {
var client = remoteAddress;
if (!err) {
if (domains.length > 0) {
client = domains [0];
@ -436,6 +518,7 @@ function handleHttpRequest (httpRequest, httpResponse) {
client = "";
}
console.log (now.toLocaleTimeString () + " " + httpRequest.method + " " + host + ":" + port + " " + lowerpath + " " + referrer + " " + client);
logInfo.client = client;
});
//handle the request
findMappedDomain (host, function (thePort) {
@ -458,19 +541,16 @@ function handleHttpRequest (httpRequest, httpResponse) {
if (config.jsSiteRedirect != undefined) { //7/7/15 by DW
try {
var urlRedirect = eval (config.jsSiteRedirect.toString ());
httpResponse.writeHead (302, {"Location": urlRedirect.toString (), "Content-Type": "text/plain"});
httpResponse.end ("Temporary redirect to " + urlRedirect + ".");
returnRedirect (urlRedirect.toString (), false); //9/30/17 by DW
}
catch (err) {
httpResponse.writeHead (500, {"Content-Type": "text/plain"});
httpResponse.end ("Error running " + config.jsSiteRedirect + ": \"" + err.message + "\"");
httpRespond (500, "text/plain", "Error running " + config.jsSiteRedirect + ": \"" + err.message + "\"");
}
return;
}
if (config.urlSiteRedirect != undefined) {
var urlRedirect = config.urlSiteRedirect + parsedUrl.pathname;
httpResponse.writeHead (302, {"Location": urlRedirect, "Content-Type": "text/plain"});
httpResponse.end ("Temporary redirect to " + urlRedirect + ".");
returnRedirect (urlRedirect.toString (), false); //9/30/17 by DW
return;
}
if (config.urlSiteContents != undefined) { //4/26/15 by DW -- v0.55
@ -482,12 +562,10 @@ function handleHttpRequest (httpRequest, httpResponse) {
var s3url = "http:/" + config.fargoS3Path + firstPartOfHost + parsedUrl.pathname; //xxx
request (s3url, function (error, response, body) {
if (error) {
httpResponse.writeHead (500, {"Content-Type": "text/plain"});
httpResponse.end ("Error accessing S3 data: " + error.message);
httpRespond (500, "text/plain", "Error accessing S3 data: " + error.message);
}
else {
httpResponse.writeHead (response.statusCode, {"Content-Type": response.headers ["content-type"]});
httpResponse.end (body);
httpRespond (response.statusCode, response.headers ["content-type"], body);
}
});
return;
@ -496,19 +574,16 @@ function handleHttpRequest (httpRequest, httpResponse) {
var s3url = "http:/" + config.s3Path + parsedUrl.pathname;
request (s3url, function (error, response, body) {
if (error) {
httpResponse.writeHead (500, {"Content-Type": "text/plain"});
httpResponse.end ("Error accessing S3 data: " + error.message);
httpRespond (500, "text/plain", "Error accessing S3 data: " + error.message);
}
else {
if (response.statusCode == 200) {
processResponse (parsedUrl.pathname, body, config, function (code, type, text) {
httpResponse.writeHead (code, {"Content-Type": type});
httpResponse.end (text);
httpRespond (code, type, text);
});
}
else {
httpResponse.writeHead (response.statusCode, {"Content-Type": response.headers ["content-type"]});
httpResponse.end (body);
httpRespond (response.statusCode, response.headers ["content-type"], body);
}
}
});
@ -525,20 +600,17 @@ function handleHttpRequest (httpRequest, httpResponse) {
if (err) {
switch (lowerpath) {
case "/version":
httpResponse.writeHead (200, {"Content-Type": "text/plain"});
httpResponse.end (myVersion);
httpRespond (200, "text/plain", myVersion);
break;
case "/now":
httpResponse.writeHead (200, {"Content-Type": "text/plain"});
httpResponse.end (now.toString ());
httpRespond (200, "text/plain", now.toString ());
break;
case "/status":
var status = {
prefs: pageparkPrefs,
status: pageparkStats
}
httpResponse.writeHead (200, {"Content-Type": "text/plain"});
httpResponse.end (utils.jsonStringify (status));
httpRespond (200, "text/plain", utils.jsonStringify (status));
break;
default:
if (!serveRedirect (lowerpath, config)) { //12/8/15 by DW -- it wasn't a redirect
@ -569,16 +641,14 @@ function handleHttpRequest (httpRequest, httpResponse) {
});
}
else {
httpResponse.writeHead (500, {"Content-Type": "text/plain"});
httpResponse.end ("The file name contains illegal characters.");
httpRespond (500, "text/plain", "The file name contains illegal characters.");
}
});
}
});
}
catch (err) {
httpResponse.writeHead (500, {"Content-Type": "text/plain"});
httpResponse.end (err.message);
httpRespond (500, "text/plain", err.message);
}
}
function writeStats (fname, stats, callback) {
@ -635,7 +705,6 @@ function readStats (fname, stats, callback) {
});
});
}
function getTopLevelPrefs (callback) { //6/7/17 by DW -- first look for config.json, then prefs/prefs.json
const newFnameConfig = "config.json", oldFnameConfig = "prefs/prefs.json";
fs.exists (newFnameConfig, function (flExists) {
@ -657,7 +726,6 @@ function getTopLevelPrefs (callback) { //6/7/17 by DW -- first look for config.j
}
});
}
function startup () {
getTopLevelPrefs (function () {
console.log ("\n" + myProductName + " v" + myVersion + " running on port " + pageparkPrefs.myPort + ".\n");
@ -667,8 +735,10 @@ function startup () {
var now = new Date ();
pageparkStats.ctStarts++;
pageparkStats.whenLastStart = now;
pageparkStats.ctHitsSinceStart = 0; //9/30/17 by DW
flStatsDirty = true;
http.createServer (handleHttpRequest).listen (pageparkPrefs.myPort);
webSocketStartup (); //9/29/17 by DW
setInterval (everySecond, 1000);
});
});

Loading…
Cancel
Save