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