Personal intranet/start page where I can view the weather, links, fitbit data, and the news.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

323 lines
7.6 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. /** express app */
  2. const express = require("express");
  3. /** Manages oauth 2.0 w/ fitbit */
  4. const passport = require('passport');
  5. /** Used to make API calls */
  6. const unirest = require('unirest');
  7. /** express app */
  8. const app = express();
  9. /** pull config file */
  10. const utils = require("./utils.js");
  11. const config = utils.getFileAsJSON("config.json");
  12. app.use(passport.initialize());
  13. app.use(passport.session({
  14. resave: false,
  15. saveUninitialized: true
  16. }));
  17. var FitbitStrategy = require( 'passport-fitbit-oauth2' ).FitbitOAuth2Strategy;
  18. var accessTokenTemp = null;
  19. passport.use(new FitbitStrategy({
  20. clientID: config.clientID,
  21. clientSecret: config.clientSecret,
  22. callbackURL: config.callbackURL
  23. },
  24. function(accessToken, refreshToken, profile, done)
  25. {
  26. console.log(accessToken);
  27. accessTokenTemp = accessToken;
  28. done(null, {
  29. accessToken: accessToken,
  30. refreshToken: refreshToken,
  31. profile: profile
  32. });
  33. }
  34. ));
  35. passport.serializeUser(function(user, done) {
  36. done(null, user);
  37. });
  38. passport.deserializeUser(function(obj, done) {
  39. done(null, obj);
  40. });
  41. passport.authenticate('fitbit', { scope: ['activity','heartrate','location','profile'] });
  42. app.get('/auth/fitbit',
  43. passport.authenticate('fitbit', { scope: ['activity','heartrate','location','profile'] }
  44. ));
  45. app.get( '/auth/fitbit/callback', passport.authenticate( 'fitbit', {
  46. successRedirect: '/',
  47. failureRedirect: '/error'
  48. }));
  49. app.get('/error', (request, result) =>
  50. {
  51. result.write("Error authenticating with Fitbit API");
  52. result.end();
  53. });
  54. const queryAPI = function(result, path)
  55. {
  56. return new Promise((resolve, reject)=>
  57. {
  58. if(accessTokenTemp == null)
  59. {
  60. result.redirect('/auth/fitbit');
  61. resolve(false);
  62. }
  63. unirest.get(path)
  64. .headers({'Accept': 'application/json', 'Content-Type': 'application/json', Authorization: "Bearer " + accessTokenTemp})
  65. .end(function (response)
  66. {
  67. if(response.hasOwnProperty("success") && response.success == false)
  68. {
  69. result.redirect('/auth/fitbit');
  70. resolve(false);
  71. }
  72. resolve(response.body);
  73. });
  74. });
  75. };
  76. app.get('/steps', (request, result)=>
  77. {
  78. queryAPI(result, 'https://api.fitbit.com/1/user/-/activities/tracker/steps/date/today/1m.json').then((data)=>
  79. {
  80. if(data != false)
  81. {
  82. result.writeHead(200, {'Content-Type': 'text/html'});
  83. result.write(JSON.stringify(data));
  84. result.end();
  85. }
  86. else
  87. {
  88. console.log("Validating with API");
  89. }
  90. });
  91. });
  92. //2021-06-01T00:00:00
  93. app.get('/list-activities/:startTime', (request, result)=>
  94. {
  95. var startTime = request.params.startTime;
  96. fetchActivities(result, startTime).then((data)=>
  97. {
  98. result.writeHead(200, {'Content-Type': 'text/json'});
  99. result.write(JSON.stringify(data));
  100. result.end();
  101. }).catch((error)=>
  102. {
  103. console.log(error);
  104. result.writeHead(500, {'Content-Type': 'text/json'});
  105. result.end();
  106. });
  107. });
  108. function fetchActivities(result, startTime)
  109. {
  110. return new Promise((resolve, reject)=>
  111. {
  112. queryAPI(result, 'https://api.fitbit.com/1/user/-/activities/list.json?beforeDate=' + startTime + '&sort=desc&offset=0&limit=100').then((data)=>
  113. {
  114. if(data != false)
  115. {
  116. resolve(data.activities);
  117. }
  118. reject("Error with API, are you authenticated")
  119. });
  120. });
  121. }
  122. //2021-06-01T00:00:00
  123. //2020-04-06T17:16:19.000-04:00
  124. function fetchAllRuns(result, startTime)
  125. {
  126. var runs = [];
  127. console.log(startTime);
  128. return new Promise((resolve, reject)=>
  129. {
  130. fetchActivities(result, startTime).then((events)=>
  131. {
  132. if(events.length < 10)
  133. {
  134. resolve(runs);
  135. }
  136. else
  137. {
  138. for(var i = 0; i < events.length; i++)
  139. {
  140. if(events[i].logType === "mobile_run")
  141. {
  142. console.log(events[i]);
  143. runs.push(events[i]);
  144. }
  145. }
  146. var newStart = events[events.length -1].startTime.slice(0, -10);
  147. fetchAllRuns(result, newStart).then((run_rec)=>
  148. {
  149. resolve(runs.concat(run_rec));
  150. }).catch((err)=>
  151. {
  152. reject(err);
  153. });
  154. }
  155. }).catch((error)=>
  156. {
  157. reject(error);
  158. });
  159. });
  160. }
  161. //2021-06-01T00:00:00
  162. app.get('/all-logged-runs', (request, result)=>
  163. {
  164. var startTime = '2030-06-01T00:00:00';
  165. fetchAllRuns(result, startTime).then((data)=>
  166. {
  167. result.writeHead(200, {'Content-Type': 'text/json'});
  168. result.write(JSON.stringify(data));
  169. result.end();
  170. }).catch((error)=>
  171. {
  172. console.log(error);
  173. result.writeHead(500, {'Content-Type': 'text/json'});
  174. result.end();
  175. });
  176. });
  177. app.get('/activities', (request, result)=>
  178. {
  179. queryAPI(result, 'https://api.fitbit.com/1/user/-/activities/recent.json').then((data)=>
  180. {
  181. if(data != false)
  182. {
  183. result.writeHead(200, {'Content-Type': 'text/html'});
  184. result.write(JSON.stringify(data));
  185. result.end();
  186. }
  187. else
  188. {
  189. console.log("Validating with API");
  190. }
  191. });
  192. });
  193. function fetchTCX(result, tcxID)
  194. {
  195. console.log("Fetching tcx event: " + tcxID);
  196. return new Promise((resolve, reject)=>
  197. {
  198. queryAPI(result, 'https://api.fitbit.com/1/user/-/activities/' + tcxID + ".tcx").then((data)=>
  199. {
  200. if(data != false)
  201. {
  202. resolve(data);
  203. }
  204. reject("Error with API, are you authenticated")
  205. });
  206. });
  207. }
  208. function saveTCX(result, tcxID)
  209. {
  210. return new Promise((resolve, reject)=>
  211. {
  212. fetchTCX(result, tcxID).then((tcx)=>
  213. {
  214. utils.saveFile(tcxID + ".tcx", tcx);
  215. resolve();
  216. }).catch((err)=>
  217. {
  218. reject(err);
  219. })
  220. });
  221. }
  222. app.get('/save-all-tcx', (request, result)=>
  223. {
  224. var tcxID = request.params.id;
  225. var startTime = '2030-06-01T00:00:00';
  226. fetchAllRuns(result, startTime).then((data)=>
  227. {
  228. var promises = [];
  229. for(var i =0; i < data.length; i++)
  230. {
  231. promises.push(saveTCX(result, data[i].logId));
  232. }
  233. Promise.all(promises).then(function(content)
  234. {
  235. result.write("All events saved");
  236. result.end();
  237. }).catch(function(err)
  238. {
  239. console.log(err);
  240. throw err;
  241. });
  242. }).catch((error)=>
  243. {
  244. console.log(error);
  245. result.writeHead(500, {'Content-Type': 'text/json'});
  246. result.end();
  247. });
  248. });
  249. app.get('/tcx/:id', (request, result)=>
  250. {
  251. var tcxID = request.params.id;
  252. fetchTCX(result, tcxID).then((data)=>
  253. {
  254. result.writeHead(200, {'Content-Type': 'text/xml'});
  255. result.write(data);
  256. result.end();
  257. }).catch((error)=>
  258. {
  259. console.log(error);
  260. result.writeHead(500, {'Content-Type': 'text/json'});
  261. result.end();
  262. });
  263. });
  264. app.get('/', (request, result) =>
  265. {
  266. result.write(utils.getFile("index.html"));
  267. result.end();
  268. });
  269. app.listen(config.port, () =>
  270. console.log(`App listening on port ${config.port}!`)
  271. );
  272. app.use(express.static('public'));