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