daveappserver 0.5.36 → 0.5.40
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 +285 -7
- 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.40", 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
|
}
|
|
@@ -93,6 +96,9 @@ function httpReadUrl (url, callback) {
|
|
|
93
96
|
}
|
|
94
97
|
function httpFullRequest (jsontext, callback) { //11/5/21 by DW
|
|
95
98
|
var theRequest;
|
|
99
|
+
function isErrorStatusCode (theCode) { //11/8/21 by DW
|
|
100
|
+
return ((theCode < 200) || (theCode > 299));
|
|
101
|
+
}
|
|
96
102
|
try {
|
|
97
103
|
theRequest = JSON.parse (jsontext);
|
|
98
104
|
}
|
|
@@ -105,10 +111,11 @@ function httpFullRequest (jsontext, callback) { //11/5/21 by DW
|
|
|
105
111
|
callback (err);
|
|
106
112
|
}
|
|
107
113
|
else {
|
|
108
|
-
if (response.statusCode
|
|
114
|
+
if (isErrorStatusCode (response.statusCode)) { //11/8/21 by DW
|
|
109
115
|
const errstruct = {
|
|
110
116
|
message: "Can't read the URL, \"" + theRequest.url + "\" because we received a status code of " + response.statusCode + ".",
|
|
111
|
-
statusCode: response.statusCode
|
|
117
|
+
statusCode: response.statusCode,
|
|
118
|
+
data //11/8/21 by DW
|
|
112
119
|
};
|
|
113
120
|
callback (errstruct);
|
|
114
121
|
}
|
|
@@ -434,6 +441,12 @@ function cleanFileStats (stats) { //4/19/21 by DW
|
|
|
434
441
|
}
|
|
435
442
|
});
|
|
436
443
|
}
|
|
444
|
+
|
|
445
|
+
function getPublicFileUrl (screenname, relpath) { //12/4/21 by DW
|
|
446
|
+
var urlpublic = config.urlServerForClient + screenname + "/" + relpath;
|
|
447
|
+
return (urlpublic);
|
|
448
|
+
}
|
|
449
|
+
|
|
437
450
|
function makeFilePublic (screenname, relpath, callback) { //2/20/21 by DW
|
|
438
451
|
console.log ("makeFilePublic: relpath == " + relpath);
|
|
439
452
|
getFile (screenname, relpath, false, function (err, data) {
|
|
@@ -470,7 +483,21 @@ function cleanFileStats (stats) { //4/19/21 by DW
|
|
|
470
483
|
}
|
|
471
484
|
else {
|
|
472
485
|
folderToJson.getObject (config.publicFilesPath + screenname + "/", function (err, publicSubs) {
|
|
473
|
-
|
|
486
|
+
function legitError (err) {
|
|
487
|
+
if (err) {
|
|
488
|
+
if (err.code == "ENOENT") {
|
|
489
|
+
publicSubs = new Object ();
|
|
490
|
+
return (false);
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
return (true);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
return (false);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (legitError (err)) {
|
|
474
501
|
callback (err);
|
|
475
502
|
}
|
|
476
503
|
else {
|
|
@@ -639,6 +666,216 @@ function cleanFileStats (stats) { //4/19/21 by DW
|
|
|
639
666
|
});
|
|
640
667
|
});
|
|
641
668
|
}
|
|
669
|
+
//github -- 11/8/21 by DW
|
|
670
|
+
function handleGithubOauthCallback (theCode, callback) { //11/8/21 by DW
|
|
671
|
+
var params = {
|
|
672
|
+
client_id: config.githubClientId,
|
|
673
|
+
client_secret: config.githubClientSecret,
|
|
674
|
+
code: theCode
|
|
675
|
+
};
|
|
676
|
+
var apiUrl = "https://github.com/login/oauth/access_token?" + utils.buildParamList (params);
|
|
677
|
+
var githubRequest = {
|
|
678
|
+
method: "POST",
|
|
679
|
+
url: apiUrl
|
|
680
|
+
};
|
|
681
|
+
console.log ("handleGithubOauthCallback: githubRequest === " + utils.jsonStringify (githubRequest));
|
|
682
|
+
request (githubRequest, function (err, response, body) {
|
|
683
|
+
if (err) {
|
|
684
|
+
console.log ("handleGithubOauthCallback: err.message == " + err.message);
|
|
685
|
+
callback (err);
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
var postbody = qs.parse (body);
|
|
689
|
+
var urlRedirect = "/?githubaccesstoken=" + postbody.access_token;
|
|
690
|
+
console.log ("handleGithubOauthCallback: urlRedirect = " + urlRedirect);
|
|
691
|
+
callback (undefined, urlRedirect);
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
function downloadFromGithub (username, repository, path, accessToken, callback) { //calls back with the JSON structure GitHub returns
|
|
696
|
+
if (!utils.beginsWith (path, "/")) {
|
|
697
|
+
path = "/" + path;
|
|
698
|
+
}
|
|
699
|
+
var url = "https://api.github.com/repos/" + username + "/" + repository + "/contents" + path;
|
|
700
|
+
var theRequest = {
|
|
701
|
+
url: url,
|
|
702
|
+
jar: true, //"remember cookies for future use"
|
|
703
|
+
maxRedirects: 5,
|
|
704
|
+
headers: {
|
|
705
|
+
"User-Agent": config.userAgent,
|
|
706
|
+
"Authorization": "token " + accessToken
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
request (theRequest, function (err, response, jsontext) {
|
|
710
|
+
if (err) {
|
|
711
|
+
callback (err);
|
|
712
|
+
}
|
|
713
|
+
else {
|
|
714
|
+
if (response.statusCode == 404) {
|
|
715
|
+
callback ({message: "The file \"" + path + "\" was not found."});
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
if (response.headers ["x-ratelimit-remaining"] == 0) {
|
|
719
|
+
var theLimit = response.headers ["x-ratelimit-limit"];
|
|
720
|
+
callback ({"message": "GitHub reported a rate limit error. You are limited to " + theLimit + " calls per hour."});
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
try {
|
|
724
|
+
var jstruct = JSON.parse (jsontext);
|
|
725
|
+
callback (undefined, jstruct);
|
|
726
|
+
}
|
|
727
|
+
catch (err) {
|
|
728
|
+
callback (err);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
function uploadToGithub (jsontext, data, callback) {
|
|
736
|
+
var options;
|
|
737
|
+
try {
|
|
738
|
+
options = JSON.parse (jsontext);
|
|
739
|
+
}
|
|
740
|
+
catch (err) {
|
|
741
|
+
callback (err);
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
options.data = data;
|
|
745
|
+
if (options.userAgent === undefined) {
|
|
746
|
+
options.userAgent = config.userAgent;
|
|
747
|
+
}
|
|
748
|
+
if (options.type === undefined) {
|
|
749
|
+
options.type = utils.httpExt2MIME (options.path);
|
|
750
|
+
}
|
|
751
|
+
if (options.message === undefined) {
|
|
752
|
+
options.message = utils.getRandomSnarkySlogan ();
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
var bodyStruct = {
|
|
756
|
+
message: options.message,
|
|
757
|
+
committer: options.committer,
|
|
758
|
+
content: Buffer.from (options.data).toString ("base64")
|
|
759
|
+
};
|
|
760
|
+
downloadFromGithub (options.username, options.repository, options.path, options.accessToken, function (err, jstruct) {
|
|
761
|
+
if (jstruct !== undefined) {
|
|
762
|
+
bodyStruct.sha = jstruct.sha;
|
|
763
|
+
}
|
|
764
|
+
var url = "https://api.github.com/repos/" + options.username + "/" + options.repository + "/contents/" + options.path;
|
|
765
|
+
var theRequest = {
|
|
766
|
+
method: "PUT",
|
|
767
|
+
url,
|
|
768
|
+
body: JSON.stringify (bodyStruct),
|
|
769
|
+
headers: {
|
|
770
|
+
"User-Agent": options.userAgent,
|
|
771
|
+
"Authorization": "token " + options.accessToken,
|
|
772
|
+
"Content-Type": options.type
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
request (theRequest, function (err, response, body) {
|
|
776
|
+
if (err) {
|
|
777
|
+
callback (err);
|
|
778
|
+
}
|
|
779
|
+
else {
|
|
780
|
+
var rateLimitMessage;
|
|
781
|
+
if (response.headers ["x-ratelimit-remaining"] == 0) {
|
|
782
|
+
var theLimit = response.headers ["x-ratelimit-limit"];
|
|
783
|
+
rateLimitMessage = "GitHub reported a rate limit error. You are limited to " + theLimit + " calls per hour.";
|
|
784
|
+
}
|
|
785
|
+
var returnedStruct = JSON.parse (body);
|
|
786
|
+
returnedStruct.statusCode = response.statusCode;
|
|
787
|
+
returnedStruct.rateLimitMessage = rateLimitMessage;
|
|
788
|
+
callback (undefined, returnedStruct);
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
function getGithubDirectory (username, repository, path, accessToken, callback) {
|
|
794
|
+
function loadDirectory (theArray, parentpath, callback) {
|
|
795
|
+
function nextFile (ix) {
|
|
796
|
+
if (ix < theArray.length) {
|
|
797
|
+
var item = theArray [ix];
|
|
798
|
+
if (item.type == "dir") {
|
|
799
|
+
getGithubDirectory (username, repository, item.path, accessToken, function (err, jstruct) {
|
|
800
|
+
if (jstruct !== undefined) { //no error
|
|
801
|
+
item.subs = jstruct;
|
|
802
|
+
}
|
|
803
|
+
nextFile (ix + 1);
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
nextFile (ix + 1);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
else {
|
|
811
|
+
callback ();
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
nextFile (0);
|
|
815
|
+
}
|
|
816
|
+
if (utils.beginsWith (path, "/")) {
|
|
817
|
+
path = utils.stringDelete (path, 1, 1);
|
|
818
|
+
}
|
|
819
|
+
var theRequest = {
|
|
820
|
+
method: "GET",
|
|
821
|
+
url: "https://api.github.com/repos/" + username + "/" + repository + "/contents/" + path,
|
|
822
|
+
headers: {
|
|
823
|
+
"User-Agent": config.userAgent,
|
|
824
|
+
"Authorization": "token " + accessToken,
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
request (theRequest, function (err, response, body) {
|
|
828
|
+
if (err) {
|
|
829
|
+
callback (err);
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
try {
|
|
833
|
+
var jstruct = JSON.parse (body);
|
|
834
|
+
if (Array.isArray (jstruct)) { //it's a directory
|
|
835
|
+
loadDirectory (jstruct, path, function () {
|
|
836
|
+
callback (undefined, jstruct);
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
else {
|
|
840
|
+
callback (undefined, jstruct);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
catch (err) {
|
|
844
|
+
if (callback !== undefined) {
|
|
845
|
+
callback (err);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
function getGithubUserInfo (username, accessToken, callback) {
|
|
852
|
+
var url = "https://api.github.com/user";
|
|
853
|
+
if (username !== undefined) {
|
|
854
|
+
url += "s/" + username
|
|
855
|
+
}
|
|
856
|
+
var theRequest = {
|
|
857
|
+
method: "GET",
|
|
858
|
+
url,
|
|
859
|
+
headers: {
|
|
860
|
+
"User-Agent": config.userAgent,
|
|
861
|
+
"Authorization": "token " + accessToken
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
request (theRequest, function (err, response, body) {
|
|
865
|
+
if (err) {
|
|
866
|
+
callback (err);
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
try {
|
|
870
|
+
var jstruct = JSON.parse (body);
|
|
871
|
+
callback (undefined, jstruct);
|
|
872
|
+
}
|
|
873
|
+
catch (err) {
|
|
874
|
+
callback (err);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
});
|
|
878
|
+
}
|
|
642
879
|
|
|
643
880
|
function startup (options, callback) {
|
|
644
881
|
function readConfig (f, theConfig, flReportError, callback) {
|
|
@@ -720,6 +957,16 @@ function startup (options, callback) {
|
|
|
720
957
|
returnData (jstruct);
|
|
721
958
|
}
|
|
722
959
|
}
|
|
960
|
+
function httpReturnRedirect (url, code) { //9/30/20 by DW
|
|
961
|
+
var headers = {
|
|
962
|
+
location: url
|
|
963
|
+
};
|
|
964
|
+
if (code === undefined) {
|
|
965
|
+
code = 302;
|
|
966
|
+
}
|
|
967
|
+
theRequest.httpReturn (code, "text/plain", code + " REDIRECT", headers);
|
|
968
|
+
}
|
|
969
|
+
|
|
723
970
|
function httpReturnObject (err, jstruct) {
|
|
724
971
|
if (err) {
|
|
725
972
|
returnError (err);
|
|
@@ -749,7 +996,8 @@ function startup (options, callback) {
|
|
|
749
996
|
urlWebsocketServerForClient: config.urlWebsocketServerForClient,
|
|
750
997
|
flEnableLogin: config.flEnableLogin,
|
|
751
998
|
prefsPath: config.prefsPath,
|
|
752
|
-
docsPath: config.docsPath
|
|
999
|
+
docsPath: config.docsPath,
|
|
1000
|
+
idGitHubClient: config.githubClientId //11/9/21 by DW
|
|
753
1001
|
};
|
|
754
1002
|
if (theRequest.addToPagetable !== undefined) { //3/9/21 by DW
|
|
755
1003
|
for (var x in theRequest.addToPagetable) {
|
|
@@ -790,6 +1038,11 @@ function startup (options, callback) {
|
|
|
790
1038
|
returnError (err);
|
|
791
1039
|
}
|
|
792
1040
|
else { //quirk in API, it wants a string, not a JSON struct
|
|
1041
|
+
if (!flprivate) {
|
|
1042
|
+
if (config.publicFileSaved !== undefined) {
|
|
1043
|
+
config.publicFileSaved (token, secret, getPublicFileUrl (screenname, params.relpath));
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
793
1046
|
returnPlainText (utils.jsonStringify (data));
|
|
794
1047
|
}
|
|
795
1048
|
});
|
|
@@ -800,6 +1053,9 @@ function startup (options, callback) {
|
|
|
800
1053
|
writeWholeFile (screenname, params.relpath, theRequest.postBody.toString (), httpReturn);
|
|
801
1054
|
});
|
|
802
1055
|
return (true);
|
|
1056
|
+
case "/uploadtogithub": //11/9/21 by DW
|
|
1057
|
+
uploadToGithub (params.options, theRequest.postBody, httpReturn);
|
|
1058
|
+
return (true);
|
|
803
1059
|
}
|
|
804
1060
|
break;
|
|
805
1061
|
case "get":
|
|
@@ -903,6 +1159,28 @@ function startup (options, callback) {
|
|
|
903
1159
|
getFileInfo (screenname, params.relpath, httpReturn);
|
|
904
1160
|
});
|
|
905
1161
|
return (true);
|
|
1162
|
+
case "/githuboauthcallback": //11/8/21 by DW
|
|
1163
|
+
handleGithubOauthCallback (params.code, function (err, urlRedirect) {
|
|
1164
|
+
if (err) {
|
|
1165
|
+
returnError (err);
|
|
1166
|
+
}
|
|
1167
|
+
else {
|
|
1168
|
+
httpReturnRedirect (urlRedirect);
|
|
1169
|
+
}
|
|
1170
|
+
});
|
|
1171
|
+
return (true);
|
|
1172
|
+
case "/downloadfromgithub": //11/8/21 by DW
|
|
1173
|
+
downloadFromGithub (params.username, params.repository, params.path, params.accessToken, httpReturn);
|
|
1174
|
+
return (true);
|
|
1175
|
+
case "/githubgetdirectory": //11/10/21 by DW
|
|
1176
|
+
getGithubDirectory (params.username, params.repository, params.path, params.accessToken, httpReturn);
|
|
1177
|
+
return (true);
|
|
1178
|
+
case "/githubgetuserinfo": //11/10/21 by DW
|
|
1179
|
+
callWithScreenname (function (screenname) {
|
|
1180
|
+
getGithubUserInfo (params.username, params.accessToken, httpReturn);
|
|
1181
|
+
});
|
|
1182
|
+
return (true);
|
|
1183
|
+
|
|
906
1184
|
}
|
|
907
1185
|
break;
|
|
908
1186
|
}
|
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.40",
|
|
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>
|