Source: app.js

/**
 * APP module
 * @module APP
 */

/**
 * @constant express
 * @requires express
*/
const express = require('express');

/**
 * @constant app
 * @requires app
*/
const app = express();

/**
 * @constant config
 * @requires config
*/
const config = require('./config');

/**
 * @constant cors
 * @requires cors
*/
const cors = require('cors');

/**
 * @constant bodyParser
 * @requires bodyParser
*/
var bodyParser = require('body-parser')

/**
 * @constant fs
 * @requires fs
*/
const fs = require('fs');

/**
 * @constant openMeteoService
 * @requires openMeteoService
*/
const openMeteoService = require('./Services/openMeteoService');

/**
 * @constant twitterService
 * @requires twitterService
*/
const twitterService = require('./Services/twitterService');

/**
 * @constant TwitchService
 * @requires TwitchService
*/
const TwitchService = require('./Services/TwitchService');

/**
 * @constant firebaseFunctions
 * @requires firebaseFunctions
*/
const firebaseFunctions = require('./firebaseFunctions');

/**
 * @constant ISSStationService
 * @requires ISSStationService
*/
const ISSStationService = require('./Services/ISSStationService');

/**
 * @constant areasFunctions
 * @requires areasFunctions
*/
const areasFunctions = require('./Services/areasFunctions');

/**
 * @constant port
 * @requires config.port
*/
const port = config.port;

/**
 * @constant nodeCron
 * @requires node-cron
*/
const nodeCron = require("node-cron")

/**
 * @constant spotifyServices
 * @requires spotifyServices
*/
const spotifyService = require('./Services/spotifyServices')

/**
 * @constant passport
 * @requires passport
*/
var passport = require('passport');

/**
 * @constant session
 * @requires session
*/
const session = require('express-session')

/**
 * @constant session
 * @requires (passport-oauth').OAuth2Strategy
*/
var OAuth2Strategy = require('passport-oauth').OAuth2Strategy;

/**
 * @constant twitchConfig
 * @requires './twitchConfig.js'
 */
const twitchConfig = require('./twitchConfig.js')

/**
 * @constant TWITCH_CLIENT_ID
 * @requires TWITCH_CLIENT_ID
*/
const TWITCH_CLIENT_ID = twitchConfig.twitchCliendID;

/**
 * @constant TWITCH_SECRET
 * @requires TWITCH_SECRET
*/
const TWITCH_SECRET    = twitchConfig.twitchSecret;

/**
 * @constant SESSION_SECRET
 * @requires SESSION_SECRET
*/
const SESSION_SECRET   = '<YOUR CLIENT SECRET HERE>';

/**
 * @constant CALLBACK_URL
 * @requires CALLBACK_URL
*/
const CALLBACK_URL     = 'http://localhost:8080/auth/twitch/callback';

/**
 * @constant firebase_admin
 * @requires firebase-admin
 */
const firebase_admin = require('firebase-admin');

/**
 * @constant serviceAccount
 * @requires serviceAccountKey.json
 */
var serviceAccount = require("./serviceAccountKey.json");

/**
 * @constant firebaseConfig
 * @requires firebaseConfig
 */
const firebaseConfig = require('./firebaseConfig')

/**
 * Initialisation of firebase-admin
 */
firebase_admin.initializeApp({
    credential: firebase_admin.credential.cert(serviceAccount),
    databaseURL: firebaseConfig.databaseURL
});
  

const {getAuth} = require("firebase-admin/auth");

/**
 * @constant stravaApi
 * @requires strava-v3
 */
var stravaApi = require('strava-v3');

/**
 * @constant ClientAndToken
 * @requires strava-oauth2
 */
const { Client, Token } = require('strava-oauth2');

/**
  * @constant stravaToken
  * @requires stravaToken
  */
var stravaToken = {
    uid: "any",
    access_token: "any"
};

/**
 * @constant stravaClient
 * @requires stravaClient
 */
var stravaClient = '';

/**
 * @constant client
 * @requires client
 */
var client = '';

/**
 * session & passport required for twitch service
*/
app.use(session({secret: SESSION_SECRET, resave: false, saveUninitialized: false}));
app.use(passport.initialize());
app.use(passport.session());

/**
 * @class class which contains the access token & refresh token, and the user's uid.
 */
class TwitchToken {
    constructor(accessToken, refreshToken, uid) {
        this.accessToken = accessToken;
        this.refreshToken = refreshToken;
        this.uid = uid;
    }
}

/**
 * @class class containing the access token and refresh token, with the user uid for spotify api
 */
class SpotifyToken {
    constructor(accessToken, refreshToken, uid) {
        this.accessToken = accessToken;
        this.refreshToken = refreshToken;
        this.uid = uid;
    }
}
/**
* @var SpotifyTokens
* @requires SpotifyToken()
*/
var SpotifyTokens = new SpotifyToken("any", "any", 'none')

/**
 * @var twhtokens
 * @requires TwitchToken()
*/
var twhtokens = new TwitchToken("any", "any", 'none')

/**
 * passport required for twitch service
*/
passport.use('twitch', new OAuth2Strategy({
    authorizationURL: 'https://id.twitch.tv/oauth2/authorize',
    tokenURL: 'https://id.twitch.tv/oauth2/token',
    clientID: TWITCH_CLIENT_ID,
    clientSecret: TWITCH_SECRET,
    callbackURL: CALLBACK_URL,
    state: true
  },
  function(accessToken, refreshToken, profile, done) {
    profile.accessToken = accessToken;
    profile.refreshToken = refreshToken;

    done(null, profile);
  }
));


/**
 * initialisation of cors in app.
*/
app.use(cors());
/**
 * initialisation of express.urlencoded() in app.
*/
app.use(express.urlencoded())

/**
 * @constant googleService
 * @requires googleService
*/
const googleService = require('./Services/googleService');

/**
 * parse application/x-www-form-urlencoded
*/
app.use(bodyParser.urlencoded({ extended: false }))

/**
 * parse application/json
*/
app.use(bodyParser.json())
/**
 * Set spaces for json object
 */
app.set('json spaces', 2)

/**
 * @method get
 * @function '/' Server HomePage redirect to about.json
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.get('/', (req, res) => {
    res.redirect("/about.json")
})

/**
 * @method get
 * @function '/testConnexion' Server HomePage
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.get("/testConnexion", (req, res) => {
    res.send("Connexion established").status(200);
})

/**
 * @function nodeCron.schedule nodeCron.schedule("*10 * * * * *")
*/
nodeCron.schedule("*/10 * * * * *", () => {
    try {
        firebaseFunctions.getAllUsersFromFireBase().then(data => {

            for (const uid in data) {
                try {
                    areasFunctions.areaLoop(uid);
                } catch (err) {
                    console.log(err);
                }
            }
        })
    } catch (err) {
        console.log(err)
    }
})

/**
 * @method post
 * @function '/register' Server register page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.post('/register', (req, res) => {
    firebaseFunctions.register(req, res);
})

/**
 * @method post
 * @function '/login' Server login page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.post('/login', (req, res) => {
    firebaseFunctions.login(req, res);
})

/**
 * @method post
 * @function '/resetPassword' Server reset password page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.post('/resetPassword', (req, res) => {
    firebaseFunctions.resetPassword(req, res)
})

/**
 * @method get
 * @function '/about.json' Server about page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.get('/about.json', (req, res) => {

    var obj = new Array();
    const dir_path = 'Services/description'

    var services_file = fs.readdirSync(dir_path, function(err, items) {
        if (err) {
            console.log(err);
            return;
        }
    })

    for (var i = 0; i < services_file.length; i++) {
        if (services_file[i] == "template.json") {
            continue;
        }
        const data = fs.readFileSync(dir_path + '/' + services_file[i], 'utf8', function(err, data) {
            if (err) {
                console.log(err);
                return;
            }
        })
        obj[i] = JSON.parse(data, null, 2);
    }

    const d = new Date()
    const about = {}
    about.client = {"host": req.ip}
    about.server = {
        current_time : d.toString(),
        services : obj,
    }
    console.log(about)
    res.header('Content-Type', 'application/json')
    res.type('json').send(JSON.stringify(about, null, 2) + '\n');
})

/**
 * call the RegistedRequiredOpenMeteo & RegistedRequiredIss to register OpenMétéo & Iss data for the user.
 * @method post
 * @function '/register/position' Server register position page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.post('/register/position', (req, res) => {
    const { latitude, longitude, uid } = req.body;
    var position = {
        latitude: latitude,
        longitude: longitude
    }
    openMeteoService.RegistedRequiredOpenMeteo(uid, position)
    ISSStationService.RegistedRequiredIss(uid, position)
})

/**
 * call the RegistedRequiredGoogle to register google data for the user.
 * @method post
 * @function '/register/google' Server register google page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.post('/register/google', (req, res) => {
    const { uid } = req.body;
    googleService.RegistedRequiredGoogle(uid, res)
})

/**
 * Set twitch user data to passport and setUserData to DB.
 * @method get
 * @function '/twitch/auth/' Server twitch authentification page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.get('/twitch/auth/', function (req, res) {
    if(req.session && req.session.passport && req.session.passport.user) {
      twhtokens.accessToken = req.session.passport.user.accessToken
      twhtokens.refreshToken = req.session.passport.user.refreshToken
      TwitchService.setUserData(twhtokens)
      res.send("SUCCESS ! You can go back to the AREA Application.\n The access token will only last one hour.")
    } else {
        res.send("Authentification has failed. Please try again.")
    }
});

/**
 * Get the user id and set in the class twhtokens.
 * @method post
 * @function '/twitch/post/' Server twitch post page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.post('/twitch/post', (req, res) => {
    twhtokens.uid = req.body.uid
    res.json({body: "OK"}).status(200);
})

/**
 * call serializeUser from the passport.
*/
passport.serializeUser(function(user, done) {
    done(null, user);
});

/**
 * call deserializeUser from the passport.
*/
passport.deserializeUser(function(user, done) {
    done(null, user);
});

/**
 * Get the callback from twitch API and redirect to /twitch/auth/
 * @method get
 * @function '/auth/twitch/callback'' Server twitch callback page
*/
app.get('/auth/twitch/callback', passport.authenticate('twitch', { successRedirect: '/twitch/auth/', failureRedirect: '/twitch/auth' }));

/**
 * return twitch information to the front.
 * @method get
 * @function '/twitch/get' Server twitch get page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.get('/twitch/get', (req, res) => {
    firebaseFunctions.getDataFromFireBaseServer('Twitch').then(serverData => {
        res.json(serverData).status(200);
    })
})

/**
 * return twitter information to the front.
 * @method get
 * @function '/twitter/get' Server twitter get page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.get('/twitter/get', (req, res) => {
    firebaseFunctions.getDataFromFireBaseServer('twitter').then(serverData => {
        res.json(serverData).status(200);
    })
})

/**
 * Twitter login page use loginTwitter
 * @method post
 * @function '/twitter/login' Server twitter login page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.post('/twitter/login', (req, res) => {
    twitterService.loginTwitter(req, res, req.body.params)
})

/**
 * Twitter sign page use signTwitter
 * @method get
 * @function '/twitter/sign' Server twitter sign page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.get('/twitter/sign', (req, res) => {
    twitterService.signTwitter(req, res)
})

/**
 * listening on port
 * @method listen
*/
app.listen(port, () => {
    console.log(`AREA app server listening on port ${port}!`)
})

/**
 * register areas page use areaRegister
 * @method post
 * @function '/register/areas' Server register areas page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.post('/register/areas', (req, res) => {
    const { action, reaction, uid, id } = req.body;
    areasFunctions.areaRegister(uid, action, reaction, id)
    res.send('Area registered')
})

/**
 * remove areas page use removeDataFromFireBase
 * @method post
 * @function '/register/areas' Server remove areas page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.post('/remove/area', (req, res) => {
    const { uid, id } = req.body;
    firebaseFunctions.removeDataFromFireBase(`USERS/${uid}/AREAS/${id}`)
    .then(() => {
        res.json({body: "Success"}).status(200);
    })
    .catch(error => {
        console.log(error)
        res.json(error).status(400);
    })
})

/**
 * Send areas for 1 uid to the front.
 * @method get
 * @function '/getAreas/:uid' Server getAreas page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.get('/getAreas/:uid', (req, res) => {
    const uid  = req.params.uid;
    firebaseFunctions.getDataFromFireBase(uid, 'AREAS')
        .then(data => {
            res.json({areas: data}).status(200);
        })
        .catch(error => {
            console.log(error);
            res.json(error).status(400);
        })
})

/**
  * Constant which store the default redirect_uri and scope for strava
  */
const configStrava = {
    client_id: 0,
    client_secret: "",
    redirect_uri: 'http://localhost:8080/strava/callback',
    scope: 'read,activity:read_all'
};

/**
 * Send url for strava authentification
 * @method get
 * @function '/strava/auth/:uid' Server strava page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
 */
app.get('/strava/auth/:uid', async (req, res) => {
    const stravaClientData = await firebaseFunctions.getDataFromFireBaseServer('Strava');

    stravaToken.uid = req.params.uid;
    configStrava.client_id = stravaClientData.client_id;
    configStrava.client_secret = stravaClientData.client_secret;
    client = new Client(configStrava);
    var url = client.getAuthorizationUri();
    url += ',activity:read';
    res.json(url);
});

/**
 * Strava page use strava
 * @method get
 * @function '/strava' Server strava page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
 */
app.get('/strava', async (req, res) => {
    const stravaClientData = await firebaseFunctions.getDataFromFireBaseServer('Strava');
    res.json(stravaClientData);
});

/**
 * Strava callback route for strava authentification
 * @method get
 * @function '/strava/callback' Server strava authentification callback page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
 */
app.get('/strava/callback', async (req, res) => {
    const stravaClientData = await firebaseFunctions.getDataFromFireBaseServer('Strava');

    await fetch('https://www.strava.com/oauth/token?client_id=' + stravaClientData.client_id + '&client_secret=' + stravaClientData.client_secret + '&code=' + req.query.code + '&grant_type=authorization_code', {
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        }
    })
    .then((response) => {
        response.json().then(async (data) => {
            stravaClient = new stravaApi.client(data.access_token);
            console.log(data.athlete.id);
            const stats = await stravaClient.athletes.stats({id: data.athlete.id});
            const activities = await stravaClient.athlete.listActivities({id: data.athlete.id});
            var lastActivity = {};
            try {
                lastActivity =  {
                    id: activities[0].id,
                    kudos: activities[0].kudos_count,
                    comment: activities[0].comment_count
                }
            } catch (error) {
                lastActivity =  {
                    id: 0,
                    kudos: 0,
                    comment: 0
                }
            }
            data = {
                lastActivity: lastActivity,
                stats: {
                    bike: {
                        stat: stats.all_ride_totals.distance,
                        triggered: true
                    },
                    run: {
                        stat: stats.all_run_totals.distance,
                        triggered: true
                    },
                    swim: {
                        stat: stats.all_swim_totals.distance,
                        triggered: true
                    }
                },
                access_token: data.access_token,
                athleteId: data.athlete.id
            };
            if (stravaToken.uid !== "any") {
                await firebaseFunctions.setDataInDb('USERS/' + stravaToken.uid + '/StravaService', data);
                console.log(stravaToken.uid);
            }
            res.send('SUCCESS You can now go back to the app');
        });
    });
});


/**
 * Send position for 1 uid to the front.
 * @method get
 * @function '/getPosition/:uid' Server getPosition page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.get('/getPosition/:uid', (req, res) => {
    const uid  = req.params.uid;
    firebaseFunctions.getDataFromFireBase(uid, 'IssStation')
        .then(data => {
            res.json({latitude: data.latitude, longitude: data.longitude}).status(200);
        })
        .catch(error => {
            console.log(error);
            res.json(error).status(400);
        })

})

/// SPOTIFY SERVICES

/**
 * Login Page
 * @method get
 * @function '/spotify' Server spotify page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.get('/spotify', (req, res) => {
    firebaseFunctions.getDataFromFireBaseServer('Spotify').then(serverData => {
        res.json(serverData.clientID).status(200);
    })
})

/**
 * Get the user id and set in the class SpotifyTokens.
 * @method post
 * @function '/twitch/post/' Server Spotify post page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.post('/spotify/post', (req, res) => {
    SpotifyTokens.uid = req.body.uid
    res.json({body: "OK"}).status(200);
})

/**
 * Spotify callback page use callBack function.
 * @method get
 * @function '/spotify/callback' Server spotify callback page
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
*/
app.get('/spotify/callback', (req, res) => {
    firebaseFunctions.getDataFromFireBaseServer('Spotify').then(serverData => {
        spotifyService.callBack(req, res, serverData, SpotifyTokens)
    })
})

app.post('/register/facebook', (req, res) => {
    const {uid, email} = req.body;
    firebaseFunctions.setInfoInDb(uid, email);
    res.json("ok");
});

app.get('/check/auth/:uid', (req, res) => {

    getAuth().getUser(req.params.uid).then((userRecord) => {
        res.json({uid: userRecord.uid}).status(200);
    })
    .catch((error) => {
        console.log('Error fetching user data:', error);
        res.json(error).status(400);
    });
});