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