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 CHANGED
@@ -1,4 +1,4 @@
1
- var myVersion = "0.5.35", myProductName = "daveAppServer";
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 != 200) {
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
- if (err) {
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.35",
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 &quot;../appserver.js&quot; instead of &quot;lib/daveappserver.js&quot; which only exists on my development machine. "></outline>
50
54
  </outline>