/** express app */ const express = require("express"); /** Manages oauth 2.0 w/ fitbit */ const passport = require('passport'); /** Used to make API calls */ const unirest = require('unirest'); /** express app */ const app = express(); /** pull config file */ const utils = require("./utils.js"); const config = utils.getFileAsJSON("config.json"); app.use(passport.initialize()); app.use(passport.session({ resave: false, saveUninitialized: true })); var FitbitStrategy = require( 'passport-fitbit-oauth2' ).FitbitOAuth2Strategy; var accessTokenTemp = null; passport.use(new FitbitStrategy({ clientID: config.clientID, clientSecret: config.clientSecret, callbackURL: config.callbackURL }, function(accessToken, refreshToken, profile, done) { console.log(accessToken); accessTokenTemp = accessToken; done(null, { accessToken: accessToken, refreshToken: refreshToken, profile: profile }); } )); passport.serializeUser(function(user, done) { done(null, user); }); passport.deserializeUser(function(obj, done) { done(null, obj); }); passport.authenticate('fitbit', { scope: ['activity','heartrate','location','profile'] }); app.get('/auth/fitbit', passport.authenticate('fitbit', { scope: ['activity','heartrate','location','profile'] } )); app.get( '/auth/fitbit/callback', passport.authenticate( 'fitbit', { successRedirect: '/', failureRedirect: '/error' })); app.get('/error', (request, result) => { result.write("Error authenticating with Fitbit API"); result.end(); }); const queryAPI = function(result, path) { return new Promise((resolve, reject)=> { if(accessTokenTemp == null) { result.redirect('/auth/fitbit'); resolve(false); } unirest.get(path) .headers({'Accept': 'application/json', 'Content-Type': 'application/json', Authorization: "Bearer " + accessTokenTemp}) .end(function (response) { if(response.hasOwnProperty("success") && response.success == false) { result.redirect('/auth/fitbit'); resolve(false); } resolve(response.body); }); }); }; app.get('/steps', (request, result)=> { queryAPI(result, 'https://api.fitbit.com/1/user/-/activities/tracker/steps/date/today/1m.json').then((data)=> { if(data != false) { result.writeHead(200, {'Content-Type': 'text/html'}); result.write(JSON.stringify(data)); result.end(); } else { console.log("Validating with API"); } }); }); //2021-06-01T00:00:00 app.get('/list-activities/:startTime', (request, result)=> { var startTime = request.params.startTime; fetchActivities(result, startTime).then((data)=> { result.writeHead(200, {'Content-Type': 'text/json'}); result.write(JSON.stringify(data)); result.end(); }).catch((error)=> { console.log(error); result.writeHead(500, {'Content-Type': 'text/json'}); result.end(); }); }); function fetchActivities(result, startTime) { return new Promise((resolve, reject)=> { queryAPI(result, 'https://api.fitbit.com/1/user/-/activities/list.json?beforeDate=' + startTime + '&sort=desc&offset=0&limit=100').then((data)=> { if(data != false) { resolve(data.activities); } reject("Error with API, are you authenticated") }); }); } //2021-06-01T00:00:00 //2020-04-06T17:16:19.000-04:00 function fetchAllRuns(result, startTime) { var runs = []; console.log(startTime); return new Promise((resolve, reject)=> { fetchActivities(result, startTime).then((events)=> { if(events.length < 10) { resolve(runs); } else { for(var i = 0; i < events.length; i++) { if(events[i].logType === "mobile_run") { console.log(events[i]); runs.push(events[i]); } } var newStart = events[events.length -1].startTime.slice(0, -10); fetchAllRuns(result, newStart).then((run_rec)=> { resolve(runs.concat(run_rec)); }).catch((err)=> { reject(err); }); } }).catch((error)=> { reject(error); }); }); } //2021-06-01T00:00:00 app.get('/all-logged-runs', (request, result)=> { var startTime = '2030-06-01T00:00:00'; fetchAllRuns(result, startTime).then((data)=> { result.writeHead(200, {'Content-Type': 'text/json'}); result.write(JSON.stringify(data)); result.end(); }).catch((error)=> { console.log(error); result.writeHead(500, {'Content-Type': 'text/json'}); result.end(); }); }); app.get('/activities', (request, result)=> { queryAPI(result, 'https://api.fitbit.com/1/user/-/activities/recent.json').then((data)=> { if(data != false) { result.writeHead(200, {'Content-Type': 'text/html'}); result.write(JSON.stringify(data)); result.end(); } else { console.log("Validating with API"); } }); }); function fetchTCX(result, tcxID) { console.log("Fetching tcx event: " + tcxID); return new Promise((resolve, reject)=> { queryAPI(result, 'https://api.fitbit.com/1/user/-/activities/' + tcxID + ".tcx").then((data)=> { if(data != false) { resolve(data); } reject("Error with API, are you authenticated") }); }); } function saveTCX(result, tcxID) { return new Promise((resolve, reject)=> { fetchTCX(result, tcxID).then((tcx)=> { utils.saveFile(tcxID + ".tcx", tcx); resolve(); }).catch((err)=> { reject(err); }) }); } app.get('/save-all-tcx', (request, result)=> { var tcxID = request.params.id; var startTime = '2030-06-01T00:00:00'; fetchAllRuns(result, startTime).then((data)=> { var promises = []; for(var i =0; i < data.length; i++) { promises.push(saveTCX(result, data[i].logId)); } Promise.all(promises).then(function(content) { result.write("All events saved"); result.end(); }).catch(function(err) { console.log(err); throw err; }); }).catch((error)=> { console.log(error); result.writeHead(500, {'Content-Type': 'text/json'}); result.end(); }); }); app.get('/tcx/:id', (request, result)=> { var tcxID = request.params.id; fetchTCX(result, tcxID).then((data)=> { result.writeHead(200, {'Content-Type': 'text/xml'}); result.write(data); result.end(); }).catch((error)=> { console.log(error); result.writeHead(500, {'Content-Type': 'text/json'}); result.end(); }); }); app.get('/', (request, result) => { result.write(utils.getFile("index.html")); result.end(); }); app.listen(config.port, () => console.log(`App listening on port ${config.port}!`) ); app.use(express.static('public'));