Skip to content
Snippets Groups Projects
Commit 423ce958 authored by nicolas.paschoud's avatar nicolas.paschoud
Browse files

Half of the doc

parent 89825da1
No related branches found
No related tags found
No related merge requests found
# Hyperdrive
## Une introduction
Dans le cadre du cours de Développement Web Avancé, nous avons du réaliser un projet web. Durant ce projet, nous avons du créer une API REST afin d'offrir un accès aux données. Ce projet a été réalisé en Javascript en utilisant Node JS.
## L'application
Le projet que nous avons choisi de réaliser est un drive. Pour des besoins personnels, nous avons jugé ce projet pertinent et l'avons donc réalisé. Le but est donc de permettre à un utilisateur de s'enregistrer et de stocker des fichiers sur la plateforme. Il a aussi la possibilité de les organiser en créant des dossiers. Il a aussi la possibilité de les partager avec d'autre utilisateurs du service. Nous avons créer une interface simple découpée en quatre partie :
- Une zone de menu pour se connecter, se déconnecter et créer des dossiers / importer des fichiers.
- La zone de contenu du drive ou sont affichés tous les dossiers et fichiers de l'utilisateur.
- Une zone d'information sur le fichier (nom, son propriétaire, la date d'upload, l'adresse d'upload)
- Une zone de contenu affichants tous les fichiers que les autres utilisateurs partagent avec lui.
L'utilisateur a la possibilité de s'inscrire ainsi que de se connecter au service. Lors de sa connexion, il reçoit un token lui permettant d'accéder à l'API. Lors de sa déconnexion, le token est détruit. Pour créer un dossier, il lui suffit de se mettre à un endroit spécifique et de cliquer sur le bouton "New File" après avoir renseigné le nom du dossier à créer.
Pour permettre la géolocalisation des fichiers, nous avons utilisé 2 API publiques qui sont :
- https://ip-api.com/ Cette API nous permet de récupérer les informations de localisation d'une adresse IP. Nous pouvons grâce à celle-ci, récupérer les latitude et longitude que nous stockons dans la base de données.
- http://bot.whatismyipaddress.com Cette API permet de récupérer l'adresse ip de l'ordinateur courant. Nous l'utilisons exclusivement pour la raisons suivantes. Nous travaillons en local, et n'avons pas déployé de serveur public. Nous ne pouvons donc pas récupérer l'adresse du client qui fait une requête sur l'API car il s'agit d'une adresse privée. Il est cependant facile de modifier cette partie du code pour utiliser l'adresse du client au cas ou nous voudrions rendre notre API public.
## Installation
Avant de faire fonctionner le site, assurez vous d'avoir la plateforme Node JS d'installée. Installer ensuite les packages nécessaire au fonctionnement du site en vous rendant à la racine du projet et en executant la commande suivante : *npm install*
Pour démarrer le site il faut réaliser ces 2 étapes :
- Se rendre dans le dossier *db* se trouvant dans la hiérarchie du projet et taper la commande *docker-compose up -d*. Assurez-vous que le port 3306 est libre pour permettre au service MySQL de démarrer correctement.
- Se rendre dans le projet ou se trouve le fichier *hyperdrive-rest*.js et taper la commande *node hyperdrive-rest.js*. après vous être assurer que le port 8080 n'est pas utilisé par un autre service. Le site est accessible à l'adress de votre ordinateur sur le port 8080.
## Répartition
Fleury Noé :
- Upload / Download des fichiers
- Localisation de ceux-ci à l'upload
- JWT
- Partage des fichiers
Paschoud Nicolas :
- Base de donnée
- Requêtes SQL (sql-request.js)
- Affichage de l'arborescence dans le drive
- Affichage des informations du fichier
## Architecture
Notre architecture se présente sous cette forme.
![hyperdrive](/Users/klaus/Documents/Web/Back/2019_tp2/documentation/hyperdrive.png)
- Le bloc Client fais des appels à l'API hyperdrive qui lui réponds avec les données que le client souhaite. Le client les traites et les affiches à sa guise.
- L'API Hyperdrive réponds à des requêtes des clients. Il ne fait appel qu'en cas de besoin aux API *whatismyipaddress* et *ip-api*.
- L'API *ip-api* est appelée par hyperdrive seulement lorsque le client upload un fichier. Cet appel permet de retourner l'addresse de la personne en fonction de son IP. Les latitudes et longitudes sont stockée par l'hyperdrive dans le bloc SQL
- Le bloc whatismyipaddress n'est util que dans le cadre du développement. Cette API nous sert à récupérer notre IP pour ensuite faire un appel à *ip-api*.
## Technologies
Pour la réalisation de ce projet, nous avons utilisé la plateform Node JS pour la réalisation de notre API REST. Nous avons aussi réaliser une base de données relationnelle MySQL qui se trouve dans un container. Tout notre projet a été réaliser en Javascript en utilisant les modules Node JS suivant :
- Express
- MySQL
- Crypto-js
- http
\- Une représentation graphique de l’architecture
\- Les informations sur les technologies utilisées
\- Documentation des routes de l’API (adresse, méthode, paramètre, retours, etc.) - Toute informations utile à la compréhension de votre projet et au travail réalisé
## Routes
"use strict";
const CryptoJS = require("crypto-js");
const http = require("http");
const express = require('express');
const app = express();
const port = 8080;
const sql = require('./sql-request');
///// JWT part /////
var valid_tokens = [];
String.prototype.hashCode = function() {
​ var hash = 0, i, chr;
​ if (this.length === 0) return hash;
​ for (i = 0; i < this.length; i++) {
​ chr = this.charCodeAt(i);
​ hash = ((hash << 5) - hash) + chr;
​ hash |= 0; // Convert to 32bit integer
​ }
​ return hash;
};
function tob64(elem){
​ return Buffer.from(elem.toString()).toString('base64').slice(0, -2);
}
function Header(typ, alg){
​ this.typ = typ;
​ this.alg = alg;
​ this.toString = function(){ return "{" + this.typ + "," + this.alg + "}"};
}
function Payload(user, pass_enc){
​ this.user = user;
​ this.pass_enc = pass_enc;
​ this.toString = function(){ return "{" + this.user + "," + this.pass_enc + "}"};
}
function Signature(token){
​ const secret = "our super hyperdrive secret" + new Date();
​ return CryptoJS.HmacSHA512(token, secret);
}
function JWT(pl_user, pl_pass){
​ this.header = new Header("jwt", "HS512");
​ this.payload = new Payload(pl_user, pl_pass.hashCode());
​ let token = tob64(this.header) + "." + tob64(this.payload);
​ let signature = new Signature("token");
​ this.signedToken = token + "." + signature;
}
function add_token(token, user) {
​ for (let i = 0; i < valid_tokens.length; i++) {
​ if(Object.keys(valid_tokens[i])[0] == token){
​ valid_tokens.splice(i, 1);
​ console.log("Unable to add token. (token already present)");
​ break;
​ }
​ }
​ let obj = {}; obj[token.toString()] = user;
​ valid_tokens.push(obj);
​ console.log(valid_tokens)
​ console.log("Successfully added token.");
}
function remove_token(token) {
​ for (let i = 0; i < valid_tokens.length; i++) {
​ if(Object.keys(valid_tokens[i])[0] == token){
​ valid_tokens.splice(i, 1);
​ console.log("Successfully removed token from valid_tokens.");
​ return true;
​ }
​ }
​ console.log("Unable to remove token from valid_tokens. (Token not present)");
}
// verify the token
// return : user if exist
function verify_token(token) {
​ for (let i = 0; i < valid_tokens.length; i++) {
​ if(Object.keys(valid_tokens[i])[0] == token)
​ return valid_tokens[i][token];
​ }
​ return false;
}
///// End JWT part /////
app.get('/', (req, res) => {
​ res.sendFile(__dirname + '/front/index.html');
});
/* Login
\* param : pseudo
\* param : password
*/
// resCode : [ 0: User now logged in, 1: False password, 2: Invalid username, 3: Empty user or pass ]
app.get('/login', (req, res) => {
​ const user = req.query['user'];
​ const pass = req.query['pass'];
​ sql.userExist(user, pass, (element) => {
​ if (element)
​ check_login(user, pass, element, res);
​ });
});
function check_login(user, pass, userObject, res) {
​ if (!user || !pass) {
​ res.send({
​ "route": "/login",
​ "resCode": 4,
​ "comment": "Please enter a username and a password."
​ })
​ }
​ else{
​ if (userObject){
​ if (userObject.passwd == pass.hashCode()) {
​ let jwt = new JWT(user, pass);
​ res.send({
​ "route": "/login",
​ "resCode": 0,
​ "signedToken": jwt.signedToken,
​ "comment": `Password for user '${ user }' true.`
​ })
​ add_token(jwt.signedToken, user);
​ }
​ else {
​ res.send({
​ "route": "/login",
​ "resCode": 1,
​ "comment": `Password for user '${ user }' false.`
​ })
​ }
​ }
​ else {
​ res.send({
​ "route": "/login",
​ "resCode": 2,
​ "comment": `Username '${ user }' don't exist.`
​ })
​ }
​ }
}
// resCode : [ 0: Token is valid, 1: Token is not valid, 3: Empty token ]
app.get('/testmytoken', (req, res) => {
​ let token = req.query['token'];
​ if (!token) {
​ res.send({
​ "resCode": 3,
​ "comment": "Please enter a token."
​ })
​ }
​ if (verify_token(token)){
​ res.send({
​ "resCode": 0,
​ "comment": "Your token is valid."
​ })
​ }
​ else {
​ res.send({
​ "resCode": 1,
​ "comment": "Your token is not valid."
​ })
​ }
})
// resCode : [ 0: Logout ok, 1: Already logged out, 3: Empty token ]
app.get('/logout/', (req, res) => {
​ let token = req.query['token'];
​ if (!token) {
​ res.send({
​ "resCode": 3,
​ "comment": "Please enter a token."
​ })
​ }
​ else {
​ if (verify_token(token)){
​ remove_token(token);
​ res.send({
​ "resCode": 0,
​ "comment": "Your are now logged out."
​ })
​ }
​ else {
​ res.send({
​ "resCode": 1,
​ "comment": "Your are already logged out."
​ })
​ }
​ }
})
/**
\* Request register
\* param : pseudo
\* param : password
*/
app.get('/register/', (req, res) => {
​ sql.addUser(req.query['login'], req.query['pass']);
​ res.send(`Request for a register (${req.query['login']}, ${req.query['pass']})`);
})
/**
\* Request to share a file with a user
\* param : file_id
\* param : user1
\* param : user2
\* user1 share a file_id with user2
*/
app.get('/share/:file_id/:to_user', (req, res) => {
​ let token = req.query["token"]
​ let user = verify_token(token);
​ console.log("user : " + user)
​ if (req.params['to_user'] && req.params['file_id']){
​ let to_user = req.params['to_user'];
​ let file_id = req.params['file_id'];
​ sql.addSharing(user, to_user, file_id).then(function (r) {
​ res.send(r);
​ })
​ }
​ else{
​ res.send("Unable to share. Please provide a user to share with and a file_id.");
​ }
})
/**
\* Request to upload a file
*/
app.get('/upload/', (req, res) => {
​ let d = new Date();
​ let date_upload = d.getTime();
​ // "abcd", "un", "/a", 46.2054, 6.1459, date_upload
​ http.get('http://bot.whatismyipaddress.com', (res) => {
​ res.setEncoding('utf8');
​ res.on('data', function(chunk){
​ console.log(chunk);
​ let ip = chunk;
​ http.get('http://ip-api.com/json/' + ip, (resp) => {
​ let data = '';
​ resp.on('data', (chunk) => {
​ data += chunk;
​ });
​ resp.on('end', () => {
​ console.log(JSON.parse(data));
​ });
​ });
​ });
​ });
​ res.send(`Request for an upload (${req.query['file']})`)
});
app.get('/download/:file_id', (req, res) => {
​ let token = req.query["token"]
​ let file_id = req.params["file_id"]
​ let user = verify_token(token);
​ if (user){
​ sql.verifyFileID(user, file_id, (filename) => {
​ console.log(filename)
​ if (filename){
​ res.download("files/" + file_id, filename);
​ }
​ else{
​ res.send("Can't download (not your file).")
​ }
​ })
​ }
​ else{
​ res.send("Can't download (please be connected).")
​ }
})
/**
\* This function return the content of a new path
\* param
*/
app.get('/change-path*', (req, res) => {
​ let path = req.params['0'].split("/");
​ path.pop();
​ path = path.join("/");
​ let tok = req.params['0'].split("/").pop();
​ let name = verify_token(tok);
​ if (name) {
​ let content = sql.changeDirectory(name, path,
​ (content) => {
​ res.send(content);
​ });
​ }
})
app.get('/create-path*', (req, res) =>
### /get-info
La route get-info nous permet de récupérer les informations d'un fichier
Paramètres :
- file_id : Id du fichier dont on souhaite avoir les informations
- token : Il s'agit du token d'authentification
### /show-shared-file
\- Une conclusion
\ No newline at end of file
documentation/hyperdrive.png

38.2 KiB

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment