daveappserver 0.5.37 → 0.5.41
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/appserver.js +292 -5
- package/package.json +2 -1
- package/readme.md +6 -0
- package/readme.opml +4 -0
package/appserver.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
var myVersion = "0.5.
|
|
1
|
+
var myVersion = "0.5.41", myProductName = "daveAppServer";
|
|
2
2
|
|
|
3
3
|
exports.start = startup;
|
|
4
4
|
exports.notifySocketSubscribers = notifySocketSubscribers;
|
|
5
5
|
exports.saveStats = saveStats;
|
|
6
6
|
exports.getStats = getStats; //6/28/21 by DW
|
|
7
7
|
exports.getConfig = getConfig;
|
|
8
|
+
exports.publishFile = publishFile; //12/13/21 by DW
|
|
8
9
|
|
|
9
10
|
const fs = require ("fs");
|
|
10
11
|
var dns = require ("dns");
|
|
@@ -17,6 +18,7 @@ const davetwitter = require ("davetwitter");
|
|
|
17
18
|
const filesystem = require ("davefilesystem");
|
|
18
19
|
const folderToJson = require ("foldertojson");
|
|
19
20
|
const zip = require ("davezip");
|
|
21
|
+
const qs = require ("querystring");
|
|
20
22
|
|
|
21
23
|
const whenStart = new Date ();
|
|
22
24
|
|
|
@@ -42,7 +44,9 @@ var config = {
|
|
|
42
44
|
privateFilesPath: "privateFiles/users/",
|
|
43
45
|
publicFilesPath: "publicFiles/users/",
|
|
44
46
|
|
|
45
|
-
defaultContentType: "text/plain" //8/3/21 by DW
|
|
47
|
+
defaultContentType: "text/plain", //8/3/21 by DW
|
|
48
|
+
|
|
49
|
+
userAgent: myProductName + " v" + myVersion //11/8/21 by DW
|
|
46
50
|
};
|
|
47
51
|
const fnameConfig = "config.json";
|
|
48
52
|
|
|
@@ -56,7 +60,6 @@ var stats = {
|
|
|
56
60
|
const fnameStats = "stats.json";
|
|
57
61
|
|
|
58
62
|
|
|
59
|
-
|
|
60
63
|
function statsChanged () {
|
|
61
64
|
flStatsChanged = true;
|
|
62
65
|
}
|
|
@@ -279,6 +282,18 @@ function cleanFileStats (stats) { //4/19/21 by DW
|
|
|
279
282
|
});
|
|
280
283
|
}
|
|
281
284
|
|
|
285
|
+
function kissOtherLogonsGoodnight (screenname, theNewConnection) { //12/14/21 by DW
|
|
286
|
+
theWsServer.connections.forEach (function (conn, ix) {
|
|
287
|
+
if (conn.appData !== undefined) { //it's one of ours
|
|
288
|
+
if (conn != theNewConnection) { //it's not the new one
|
|
289
|
+
if (conn.appData.screenname == screenname) {
|
|
290
|
+
conn.sendText ("goodnight");
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
282
297
|
conn.on ("text", function (s) {
|
|
283
298
|
var words = s.split (" ");
|
|
284
299
|
if (words.length > 1) { //new protocol as of 11/29/15 by DW
|
|
@@ -297,6 +312,7 @@ function cleanFileStats (stats) { //4/19/21 by DW
|
|
|
297
312
|
conn.appData.urlToWatch = "";
|
|
298
313
|
davetwitter.getScreenName (token, secret, function (screenname) {
|
|
299
314
|
conn.appData.screenname = screenname;
|
|
315
|
+
kissOtherLogonsGoodnight (screenname, conn); //12/14/21 by DW
|
|
300
316
|
logToConsole (conn, conn.appData.lastVerb, conn.appData.screenname);
|
|
301
317
|
});
|
|
302
318
|
break;
|
|
@@ -438,6 +454,12 @@ function cleanFileStats (stats) { //4/19/21 by DW
|
|
|
438
454
|
}
|
|
439
455
|
});
|
|
440
456
|
}
|
|
457
|
+
|
|
458
|
+
function getPublicFileUrl (screenname, relpath) { //12/4/21 by DW
|
|
459
|
+
var urlpublic = config.urlServerForClient + screenname + "/" + relpath;
|
|
460
|
+
return (urlpublic);
|
|
461
|
+
}
|
|
462
|
+
|
|
441
463
|
function makeFilePublic (screenname, relpath, callback) { //2/20/21 by DW
|
|
442
464
|
console.log ("makeFilePublic: relpath == " + relpath);
|
|
443
465
|
getFile (screenname, relpath, false, function (err, data) {
|
|
@@ -474,7 +496,21 @@ function cleanFileStats (stats) { //4/19/21 by DW
|
|
|
474
496
|
}
|
|
475
497
|
else {
|
|
476
498
|
folderToJson.getObject (config.publicFilesPath + screenname + "/", function (err, publicSubs) {
|
|
477
|
-
|
|
499
|
+
function legitError (err) {
|
|
500
|
+
if (err) {
|
|
501
|
+
if (err.code == "ENOENT") {
|
|
502
|
+
publicSubs = new Object ();
|
|
503
|
+
return (false);
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
return (true);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
return (false);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
if (legitError (err)) {
|
|
478
514
|
callback (err);
|
|
479
515
|
}
|
|
480
516
|
else {
|
|
@@ -643,6 +679,216 @@ function cleanFileStats (stats) { //4/19/21 by DW
|
|
|
643
679
|
});
|
|
644
680
|
});
|
|
645
681
|
}
|
|
682
|
+
//github -- 11/8/21 by DW
|
|
683
|
+
function handleGithubOauthCallback (theCode, callback) { //11/8/21 by DW
|
|
684
|
+
var params = {
|
|
685
|
+
client_id: config.githubClientId,
|
|
686
|
+
client_secret: config.githubClientSecret,
|
|
687
|
+
code: theCode
|
|
688
|
+
};
|
|
689
|
+
var apiUrl = "https://github.com/login/oauth/access_token?" + utils.buildParamList (params);
|
|
690
|
+
var githubRequest = {
|
|
691
|
+
method: "POST",
|
|
692
|
+
url: apiUrl
|
|
693
|
+
};
|
|
694
|
+
console.log ("handleGithubOauthCallback: githubRequest === " + utils.jsonStringify (githubRequest));
|
|
695
|
+
request (githubRequest, function (err, response, body) {
|
|
696
|
+
if (err) {
|
|
697
|
+
console.log ("handleGithubOauthCallback: err.message == " + err.message);
|
|
698
|
+
callback (err);
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
var postbody = qs.parse (body);
|
|
702
|
+
var urlRedirect = "/?githubaccesstoken=" + postbody.access_token;
|
|
703
|
+
console.log ("handleGithubOauthCallback: urlRedirect = " + urlRedirect);
|
|
704
|
+
callback (undefined, urlRedirect);
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
function downloadFromGithub (username, repository, path, accessToken, callback) { //calls back with the JSON structure GitHub returns
|
|
709
|
+
if (!utils.beginsWith (path, "/")) {
|
|
710
|
+
path = "/" + path;
|
|
711
|
+
}
|
|
712
|
+
var url = "https://api.github.com/repos/" + username + "/" + repository + "/contents" + path;
|
|
713
|
+
var theRequest = {
|
|
714
|
+
url: url,
|
|
715
|
+
jar: true, //"remember cookies for future use"
|
|
716
|
+
maxRedirects: 5,
|
|
717
|
+
headers: {
|
|
718
|
+
"User-Agent": config.userAgent,
|
|
719
|
+
"Authorization": "token " + accessToken
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
request (theRequest, function (err, response, jsontext) {
|
|
723
|
+
if (err) {
|
|
724
|
+
callback (err);
|
|
725
|
+
}
|
|
726
|
+
else {
|
|
727
|
+
if (response.statusCode == 404) {
|
|
728
|
+
callback ({message: "The file \"" + path + "\" was not found."});
|
|
729
|
+
}
|
|
730
|
+
else {
|
|
731
|
+
if (response.headers ["x-ratelimit-remaining"] == 0) {
|
|
732
|
+
var theLimit = response.headers ["x-ratelimit-limit"];
|
|
733
|
+
callback ({"message": "GitHub reported a rate limit error. You are limited to " + theLimit + " calls per hour."});
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
try {
|
|
737
|
+
var jstruct = JSON.parse (jsontext);
|
|
738
|
+
callback (undefined, jstruct);
|
|
739
|
+
}
|
|
740
|
+
catch (err) {
|
|
741
|
+
callback (err);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
function uploadToGithub (jsontext, data, callback) {
|
|
749
|
+
var options;
|
|
750
|
+
try {
|
|
751
|
+
options = JSON.parse (jsontext);
|
|
752
|
+
}
|
|
753
|
+
catch (err) {
|
|
754
|
+
callback (err);
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
options.data = data;
|
|
758
|
+
if (options.userAgent === undefined) {
|
|
759
|
+
options.userAgent = config.userAgent;
|
|
760
|
+
}
|
|
761
|
+
if (options.type === undefined) {
|
|
762
|
+
options.type = utils.httpExt2MIME (options.path);
|
|
763
|
+
}
|
|
764
|
+
if (options.message === undefined) {
|
|
765
|
+
options.message = utils.getRandomSnarkySlogan ();
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
var bodyStruct = {
|
|
769
|
+
message: options.message,
|
|
770
|
+
committer: options.committer,
|
|
771
|
+
content: Buffer.from (options.data).toString ("base64")
|
|
772
|
+
};
|
|
773
|
+
downloadFromGithub (options.username, options.repository, options.path, options.accessToken, function (err, jstruct) {
|
|
774
|
+
if (jstruct !== undefined) {
|
|
775
|
+
bodyStruct.sha = jstruct.sha;
|
|
776
|
+
}
|
|
777
|
+
var url = "https://api.github.com/repos/" + options.username + "/" + options.repository + "/contents/" + options.path;
|
|
778
|
+
var theRequest = {
|
|
779
|
+
method: "PUT",
|
|
780
|
+
url,
|
|
781
|
+
body: JSON.stringify (bodyStruct),
|
|
782
|
+
headers: {
|
|
783
|
+
"User-Agent": options.userAgent,
|
|
784
|
+
"Authorization": "token " + options.accessToken,
|
|
785
|
+
"Content-Type": options.type
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
request (theRequest, function (err, response, body) {
|
|
789
|
+
if (err) {
|
|
790
|
+
callback (err);
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
var rateLimitMessage;
|
|
794
|
+
if (response.headers ["x-ratelimit-remaining"] == 0) {
|
|
795
|
+
var theLimit = response.headers ["x-ratelimit-limit"];
|
|
796
|
+
rateLimitMessage = "GitHub reported a rate limit error. You are limited to " + theLimit + " calls per hour.";
|
|
797
|
+
}
|
|
798
|
+
var returnedStruct = JSON.parse (body);
|
|
799
|
+
returnedStruct.statusCode = response.statusCode;
|
|
800
|
+
returnedStruct.rateLimitMessage = rateLimitMessage;
|
|
801
|
+
callback (undefined, returnedStruct);
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
function getGithubDirectory (username, repository, path, accessToken, callback) {
|
|
807
|
+
function loadDirectory (theArray, parentpath, callback) {
|
|
808
|
+
function nextFile (ix) {
|
|
809
|
+
if (ix < theArray.length) {
|
|
810
|
+
var item = theArray [ix];
|
|
811
|
+
if (item.type == "dir") {
|
|
812
|
+
getGithubDirectory (username, repository, item.path, accessToken, function (err, jstruct) {
|
|
813
|
+
if (jstruct !== undefined) { //no error
|
|
814
|
+
item.subs = jstruct;
|
|
815
|
+
}
|
|
816
|
+
nextFile (ix + 1);
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
else {
|
|
820
|
+
nextFile (ix + 1);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
else {
|
|
824
|
+
callback ();
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
nextFile (0);
|
|
828
|
+
}
|
|
829
|
+
if (utils.beginsWith (path, "/")) {
|
|
830
|
+
path = utils.stringDelete (path, 1, 1);
|
|
831
|
+
}
|
|
832
|
+
var theRequest = {
|
|
833
|
+
method: "GET",
|
|
834
|
+
url: "https://api.github.com/repos/" + username + "/" + repository + "/contents/" + path,
|
|
835
|
+
headers: {
|
|
836
|
+
"User-Agent": config.userAgent,
|
|
837
|
+
"Authorization": "token " + accessToken,
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
request (theRequest, function (err, response, body) {
|
|
841
|
+
if (err) {
|
|
842
|
+
callback (err);
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
try {
|
|
846
|
+
var jstruct = JSON.parse (body);
|
|
847
|
+
if (Array.isArray (jstruct)) { //it's a directory
|
|
848
|
+
loadDirectory (jstruct, path, function () {
|
|
849
|
+
callback (undefined, jstruct);
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
callback (undefined, jstruct);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
catch (err) {
|
|
857
|
+
if (callback !== undefined) {
|
|
858
|
+
callback (err);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
function getGithubUserInfo (username, accessToken, callback) {
|
|
865
|
+
var url = "https://api.github.com/user";
|
|
866
|
+
if (username !== undefined) {
|
|
867
|
+
url += "s/" + username
|
|
868
|
+
}
|
|
869
|
+
var theRequest = {
|
|
870
|
+
method: "GET",
|
|
871
|
+
url,
|
|
872
|
+
headers: {
|
|
873
|
+
"User-Agent": config.userAgent,
|
|
874
|
+
"Authorization": "token " + accessToken
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
request (theRequest, function (err, response, body) {
|
|
878
|
+
if (err) {
|
|
879
|
+
callback (err);
|
|
880
|
+
}
|
|
881
|
+
else {
|
|
882
|
+
try {
|
|
883
|
+
var jstruct = JSON.parse (body);
|
|
884
|
+
callback (undefined, jstruct);
|
|
885
|
+
}
|
|
886
|
+
catch (err) {
|
|
887
|
+
callback (err);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
}
|
|
646
892
|
|
|
647
893
|
function startup (options, callback) {
|
|
648
894
|
function readConfig (f, theConfig, flReportError, callback) {
|
|
@@ -724,6 +970,16 @@ function startup (options, callback) {
|
|
|
724
970
|
returnData (jstruct);
|
|
725
971
|
}
|
|
726
972
|
}
|
|
973
|
+
function httpReturnRedirect (url, code) { //9/30/20 by DW
|
|
974
|
+
var headers = {
|
|
975
|
+
location: url
|
|
976
|
+
};
|
|
977
|
+
if (code === undefined) {
|
|
978
|
+
code = 302;
|
|
979
|
+
}
|
|
980
|
+
theRequest.httpReturn (code, "text/plain", code + " REDIRECT", headers);
|
|
981
|
+
}
|
|
982
|
+
|
|
727
983
|
function httpReturnObject (err, jstruct) {
|
|
728
984
|
if (err) {
|
|
729
985
|
returnError (err);
|
|
@@ -753,7 +1009,8 @@ function startup (options, callback) {
|
|
|
753
1009
|
urlWebsocketServerForClient: config.urlWebsocketServerForClient,
|
|
754
1010
|
flEnableLogin: config.flEnableLogin,
|
|
755
1011
|
prefsPath: config.prefsPath,
|
|
756
|
-
docsPath: config.docsPath
|
|
1012
|
+
docsPath: config.docsPath,
|
|
1013
|
+
idGitHubClient: config.githubClientId //11/9/21 by DW
|
|
757
1014
|
};
|
|
758
1015
|
if (theRequest.addToPagetable !== undefined) { //3/9/21 by DW
|
|
759
1016
|
for (var x in theRequest.addToPagetable) {
|
|
@@ -794,6 +1051,11 @@ function startup (options, callback) {
|
|
|
794
1051
|
returnError (err);
|
|
795
1052
|
}
|
|
796
1053
|
else { //quirk in API, it wants a string, not a JSON struct
|
|
1054
|
+
if (!flprivate) {
|
|
1055
|
+
if (config.publicFileSaved !== undefined) {
|
|
1056
|
+
config.publicFileSaved (token, secret, getPublicFileUrl (screenname, params.relpath));
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
797
1059
|
returnPlainText (utils.jsonStringify (data));
|
|
798
1060
|
}
|
|
799
1061
|
});
|
|
@@ -804,6 +1066,9 @@ function startup (options, callback) {
|
|
|
804
1066
|
writeWholeFile (screenname, params.relpath, theRequest.postBody.toString (), httpReturn);
|
|
805
1067
|
});
|
|
806
1068
|
return (true);
|
|
1069
|
+
case "/uploadtogithub": //11/9/21 by DW
|
|
1070
|
+
uploadToGithub (params.options, theRequest.postBody, httpReturn);
|
|
1071
|
+
return (true);
|
|
807
1072
|
}
|
|
808
1073
|
break;
|
|
809
1074
|
case "get":
|
|
@@ -907,6 +1172,28 @@ function startup (options, callback) {
|
|
|
907
1172
|
getFileInfo (screenname, params.relpath, httpReturn);
|
|
908
1173
|
});
|
|
909
1174
|
return (true);
|
|
1175
|
+
case "/githuboauthcallback": //11/8/21 by DW
|
|
1176
|
+
handleGithubOauthCallback (params.code, function (err, urlRedirect) {
|
|
1177
|
+
if (err) {
|
|
1178
|
+
returnError (err);
|
|
1179
|
+
}
|
|
1180
|
+
else {
|
|
1181
|
+
httpReturnRedirect (urlRedirect);
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
return (true);
|
|
1185
|
+
case "/downloadfromgithub": //11/8/21 by DW
|
|
1186
|
+
downloadFromGithub (params.username, params.repository, params.path, params.accessToken, httpReturn);
|
|
1187
|
+
return (true);
|
|
1188
|
+
case "/githubgetdirectory": //11/10/21 by DW
|
|
1189
|
+
getGithubDirectory (params.username, params.repository, params.path, params.accessToken, httpReturn);
|
|
1190
|
+
return (true);
|
|
1191
|
+
case "/githubgetuserinfo": //11/10/21 by DW
|
|
1192
|
+
callWithScreenname (function (screenname) {
|
|
1193
|
+
getGithubUserInfo (params.username, params.accessToken, httpReturn);
|
|
1194
|
+
});
|
|
1195
|
+
return (true);
|
|
1196
|
+
|
|
910
1197
|
}
|
|
911
1198
|
break;
|
|
912
1199
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "daveappserver",
|
|
3
3
|
"description": "Factored code that was appearing in all my servers.",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.41",
|
|
5
5
|
"main": "appserver.js",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type" : "git",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"appserver.js"
|
|
13
13
|
],
|
|
14
14
|
"dependencies" : {
|
|
15
|
+
"querystring": "*",
|
|
15
16
|
"request": "*",
|
|
16
17
|
"nodejs-websocket": "*",
|
|
17
18
|
"daveutils": "*",
|
package/readme.md
CHANGED
|
@@ -52,6 +52,12 @@ The second section configures the HTTP server and the connection to Twitter for
|
|
|
52
52
|
|
|
53
53
|
### Updates
|
|
54
54
|
|
|
55
|
+
#### v0.5.32 -- 12/4/21 by DW
|
|
56
|
+
|
|
57
|
+
Added a new callback, publicFileSaved, which is called when the user updates a public file.
|
|
58
|
+
|
|
59
|
+
Fixed a bug where a user couldn't create a new file if their public files folder was empty.
|
|
60
|
+
|
|
55
61
|
#### v0.5.32 -- 9/26/21 by DW
|
|
56
62
|
|
|
57
63
|
Fixed the example app to require "../appserver.js" instead of "lib/daveappserver.js" which only exists on my development machine.
|
package/readme.opml
CHANGED
|
@@ -45,6 +45,10 @@
|
|
|
45
45
|
</outline>
|
|
46
46
|
</outline>
|
|
47
47
|
<outline created="Wed, 15 Sep 2021 14:19:02 GMT" text="### Updates">
|
|
48
|
+
<outline created="Sat, 04 Dec 2021 21:29:57 GMT" text="#### v0.5.32 -- 12/4/21 by DW">
|
|
49
|
+
<outline created="Sat, 04 Dec 2021 21:30:33 GMT" text="Added a new callback, publicFileSaved, which is called when the user updates a public file. "></outline>
|
|
50
|
+
<outline created="Sat, 04 Dec 2021 21:29:58 GMT" text="Fixed a bug where a user couldn't create a new file if their public files folder was empty. "></outline>
|
|
51
|
+
</outline>
|
|
48
52
|
<outline created="Wed, 15 Sep 2021 14:19:10 GMT" text="#### v0.5.32 -- 9/26/21 by DW">
|
|
49
53
|
<outline created="Wed, 15 Sep 2021 14:19:12 GMT" text="Fixed the example app to require "../appserver.js" instead of "lib/daveappserver.js" which only exists on my development machine. "></outline>
|
|
50
54
|
</outline>
|