vanyle / PAPPS

The Papp's !

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

The PAPS app ! 🍳 đŸ„—

Ce repo contient le code pour le site du paps qui permet de partager des recettes de d'organiser sa liste de course.

Plus d'informations ici: https://docs.google.com/document/d/1QQNS7YFifi6eaitlNc8k2h3rlRvja7o_O6aRMfCU7z0/edit#

Lisez ce fichier avant de contribuer, il contient des bonnes pratiques Ă  suivre: CONTRIBUTING.md

Structure générale du projet

L'application est construire avec NodeJS et Express. Le back-end communique avec le front-end avec Ajax par le end-point /q

Elle utilise une base de donnĂ©es RethinkDB que l'application lance et gĂšre elle-mĂȘme. Le dossier vers la base de donnĂ©e se trouve dans le fichier de config. Il est possible de lancer plusieurs instances du serveur sur plusieurs ordinateurs diffĂ©rents et avec un peu de configuration (ajout de l'ip d'un des serveurs dans l'option --join host:portdans config.js), ces instances vont synchronisĂ©es leurs donnĂ©es ce qui permet de supporter des millions d'utilisateurs si besoin.

Elle se lance en faisant npm start ou node server.js

Installation

Node

Si Node n'est pas installé, on commence par l'installer avec ces commandes (Pour Ubuntu ou Debian)

sudo apt-get install nodejs
sudo apt-get install npm

Sur Windows, Node est disponible ici: https://nodejs.org/en/

Pour savoir si Node est installé, entrez la commande npm -v dans un terminal. Si elle affiche une version, c'est que Node marche.

PAPPS

On commence par télécharger le projet et installer les librairies requises par Node:

git clone https://github.com/vanyle/PAPPS/
cd PAPPS
npm install

Ensuite, il faut installer la base de données RethinkDB. Les instructions générales se trouvent ici: https://rethinkdb.com/docs/install/

Pour Debian

export CODENAME=`lsb_release -cs`
echo "deb https://download.rethinkdb.com/repository/debian-$CODENAME $CODENAME main" | sudo tee /etc/apt/sources.list.d/rethinkdb.list
wget -qO- https://download.rethinkdb.com/repository/raw/pubkey.gpg | sudo apt-key add -
sudo apt-get update
sudo apt-get install rethinkdb

Pour Ubuntu

source /etc/lsb-release && echo "deb https://download.rethinkdb.com/repository/ubuntu-$DISTRIB_CODENAME $DISTRIB_CODENAME main" | sudo tee /etc/apt/sources.list.d/rethinkdb.list
wget -qO- https://download.rethinkdb.com/repository/raw/pubkey.gpg | sudo apt-key add -
sudo apt-get update
sudo apt-get install rethinkdb

Pour Windows

Téléchargez RethinkDB depuis cette URL: https://download.rethinkdb.com/repository/raw/windows/rethinkdb-2.3.6.zip

DĂ©compressez le zip tĂ©lĂ©chargĂ© et mettez l'exĂ©cutable rethinkdb.exe dans votre PATH ou dans le mĂȘme dossier que database.js i.e. /back

Mettre des trucs dans la base de donnée

Par défaut, la base de donnée est vide, donc, si vous lancez le site avec node server.js, et que vous allez à l'url du site, vous ne verrez aucune recette et vous ne pourrez pas vous connecter. Pour générer des données "de test", mettez dans config.js l'option put_fake_data à true. Cela à aussi pour effet de supprimer tout le contenu déjà présent, donc faites attention à n'utiliser cette option que dans un contexte de test.

Lorsque l'application est lancée, vous aurez accÚs à une console dans laquelle vous pourrez entrer des commandes. Pour tout supprimer de la base de données et pouvoir commencer à ajouter des utilisateurs, faites:

erase_db_and_start_clean


VoilĂ  les autres commandes disponibles:

new_user <username> <password> [rights]

Crée un nouvel utilisateur appelé username avec comme mot de passe password. Par défaut, cette utilisateur aura uniquement le droit make_recipe. Sinon, mettez la liste des droits que vous voulez, séparée par des virgules.

Exemple: new_user tom azerty make_recipe,delete_recipe,delete_comment


list_users

Affiche la liste des utilisateurs du site, leur id, et si ils sont connectés.


logs <on/off>

Active ou désactive les logs. Permet d'afficher ce que les utilisateurs font dans la console.


Pour d'autres modifications plus poussĂ©es comme supprimer un utilisateur, il faut utiliser le site d'administration en supprimant de la config l'option --no-http-admin. Dans l'onglet DataExplorer, vous pouvez entrer des requĂȘtes pour lire et modifier la base de donnĂ©es.

Exemple de requĂȘte:

r.table('users').delete({id:"<id_de_utilisateur_a_supprimer>"});

Plus d'information sur les requĂȘtes possibles ici: https://rethinkdb.com/api/javascript/insert

Mettre Ă  jour le site

Faites la commande ci-dessous dans le dossier ou le serveur tourne pour mettre Ă  jour le code du site. Lancer la commande aprĂšs avoir arrĂȘter le serveur.

git pull

DĂ©ployer proprement le site

https://github.com/Unitech/pm2

npm install pm2 -g
pm2 start server.js

ProblĂšmes courants

Directory '../db/' is already in use, perhaps another instance of rethinkdb is using it.

La base de donnĂ©es s'est arrĂȘtĂ©e sans le site. C'est parfois le cas si le processus du site Ă  Ă©tĂ© arrĂȘtĂ© de maniĂšre trop violente. Par exemple avec un signal KILL ou lieu de TERMINATE ou INTERRUPT. (Il est recommandĂ© d'utiliser le signal INT pour l'arrĂȘt mĂȘme si TERMINATE convient). Essayez de ne jamais utiliser KILL pour terminer le processus ! Cela pourrait endommager la base de donnĂ©e !

Il faut retrouver le processus de la base de donnĂ©e et l'arrĂȘter manuellement.

ps aux | grep rethinkdb # Affiche la liste des processus utilisants la base de données

Plusieurs lignes font ĂȘtre affichĂ©s de cette forme:

root      1600  0.0  5.7 267136 117676 ?       Sl   00:01   0:25 rethinkdb -d ../db/ ........
root      1601  0.0  1.0  86372 21776 ?        S    00:01   0:00 rethinkdb -d ../db/ ........

Ensuite, pour chaque ligne contenant rethinkdb, faites

kill -2 <numéro du process à tuer>
# ici, par exemple, on ferait: kill -2 1600 et kill -2 1601
# si cette commande ne marche pas, remplaçez le -2 par un -15 ou un -9

Port 80 / 443 already in use

Un autre processus utilise les ports dont notre application a besoin. Dans 90% des cas, c'est cette saloperie de nginx. Pour s'en dĂ©barrasser, il faut arrĂȘter le processus nginx. Pour ça utiliser la commande kill avec un -9 pour envoyer un signal KILL. Aussi, faites un sudo apt-get remove nginx et aussi supprimez tout les fichiers de config de nginx, le contenu de var/www et tout. Il faut tout dĂ©truire.

En gĂ©nĂ©ral, utilisez la commande ci-dessous pour savoir quels applications utilisent des ports et quel est le processus associĂ© Ă  arrĂȘter:

netstat -ltnp | grep -w ':80' # Remplacez 80 par le numéro du port qui pose problÚme 

ReqlOpFailedError: Cannot perform read: primary replica for shard ["", +inf) not available in:

La base de donnĂ©e n'est pas encore prĂȘte mais vous avez essayer d'Ă©crire ou de lire dedans. Cette erreur n'est pas grave, rĂ©essayer ce que vous aviez fait en attendant 1 seconde et ça devrait marcher. Si aprĂšs 5 secondes d'attente, l'erreur est encore lĂ , ce n'est pas normal (ce n'est jamais arrivĂ©). Je conseillerais de relancer le serveur.

Faire marcher le HTTPS

Par défaut, le site utilise HTTP. Si vous le lancez, vous verrez aussi une erreur comme quoi le HTTPS ne marche pas du type: "Unable to start HTTPS Server. Did you put the HTTPS secrets inside ./secret/ ?"

Pour faire marcher le HTTPS, il faut faire exactement ça, mettre les clefs HTTPS dans le dossier secret, qui devra alors ressembler à ça:

contenu_du_dossier_secret

Vous pouvez alors lancer le serveur et le https marchera. Pour obtenir les clefs HTTPS, suivez le tutoriel de Viarezo avec Let's Encrypt et certbot. Celui-ci stocke les clefs dans /etc/letsencrypt/live/ habituellement.

Alternativement, vous pouvez changer la valeur de https_secret dans la config pour mettre le chemin vers vos clefs (/etc/letsencrypt/live/nom_du_site en général si vous utilisez certbot)

Structure du front-end

Le front-end se trouve dans le dossier /client. Tout fichier se trouvant dedans est fourni de maniĂšre statique au client et est accessible Ă  l'adresse correspondant au nom du fichier (par exemple client/style.css est disponible Ă  l'adresse www.nom_du_site.com/style.css)

Je recommande de séparer le front en plusieurs fichiers .html avec chaque fichier correspondant à un onglet du site. De plus, je recommande les bibliothÚques suivantes:

  • https://fontawesome.com/ pour les icones (version 5)
  • https://fullcalendar.io/ pour le calendrier
  • https://editorjs.io/ pour l'Ă©diteur de recette (je sais pas si ça sera nĂ©cessaire mais si les gens veulent mettre des titres dans leurs recettes et du gras, ça peut ĂȘtre sympa)
  • Eviter JQuery si possible sauf si vous estimez que c'est absolument nĂ©cessaire.

Bien sûr, vous pouvez utiliser les bibliothÚques que vous voulez, ce sont des suggestions. Sinon, prenez bien les versions minified des bibliothÚques quand vous les intégrez.

Structure du back-end

Le back-end communique avec le front avec l'adresse /q. Les arguments sont fournis au front avec des paramÚtres GET. Le back-end utilise une base de donnée RethinkDB qui se base sur une structure JSON pour stocker les données. Plus d'info ici: https://rethinkdb.com/

Le schéma de données est décrit dans ./doc/fake_data.js qui permet aussi de peupler la base de données avec des données fictives pour tester l'interface. (changer la configuration pour activer les données fictives.)

Comptes et authentification

Le site supporte 2 types d'authentification:

OAuth: Utilise le compte Viarezo. Permet de voir les recettes, commenter et voter sur les recettes.

login & password: Utilise un mot de passe et un nom d'utilisateur. Il faut un accÚs ssh à la machine pour créer ce type de compte. Ce type de compte permet de commenter, voir les recettes, créer des recettes et modérer les commentaires.

Listes des end-points (susceptible de changer.)

Note: Les endpoints en GET ne modifient pas la base de donnĂ©e et peuvent ĂȘtre appelĂ©s de maniĂšre rĂ©pĂ©tĂ©e (sauf log et unlog). Ce n'est pas le cas des endpoints en POST qui eux modifient la base de donnĂ©e.


GET /q?type=recipes&tag=tag1|tag2|tag3&s=query

Renvoie la liste de toutes les recettes publiques du site au format JSON contenant les tags fournis et contenant dans leur description ou leur titre query. Si tag n'est pas pas renseignĂ©, la requĂȘte renverra toutes les recettes disponibles par ordre de rating (jusqu'Ă  un maximum de 100 recettes). MĂȘme chose si s n'est pas renseignĂ©. Note: s est une expression rĂ©guliĂšre.

Format de la réponse:

[{
	"id":"id",
	"title":"title",
	"description":"description",
     "image_id":"imageid"
	"rating":4, // 0 - 5
	"tags":["tag1","tag2"],
},{...}, ...]

GET /q?type=image&id=id

Permet de récupérer une image d'aprÚs son identifiant. Renvoie l'image de maniÚre brut. Affichable avec <img src="/q?type=image&id=id" alt=""/>


GET /q?type=recipe&id=id

Renvoie des informations détaillés sur une recette spécifique. Renvoie une erreur si la recette n'existe pas.

Format de la réponse:

{
	"id":"id"
	"title":"title",
	"description":"description",
	"rating":4, // 0 - 5
	"image_id":"id" // might not exist or be null
	"tags":["tag1","tag2"],
	"steps":["step1","step2",...],
	"ingredients":["ingredient1","ingredient2",...],
    "creator_id":"id of creator",
	"creation_time":"2020-11-01T01:45:01.758Z" // something in this format, can be parsed with new Date(format)
	"comments":[
		{
			"name":"name of the commenter",
			"creation_time":"2020-11-01T23:51:15.760Z",
			"user_id":"id of the commenter",
			"content":"content of the comment"
		},
		...
	]
}

GET /q?type=unlog

Supprime les cookies de l'utilisateur et le déconnecte du site


GET /q?type=log&name=<username>&pass=<password>

Connecte l'utilisateur au site si name et pass sont correct. Modifie ses cookies pour que les requĂȘtes suivantes se fassent en Ă©tant connectĂ©. name et pass sont le nom d'utilisateur et le mot de passe sans encryption.

{"co":"OK"} // authentification success
{"co":"NOOK"} // authentification failed

GET /q?type=oauth (implémentation non terminée)

Redirige l'utilisateur de maniĂšre Ă  la connectĂ© par OAuth. Cette URL retourne un code 302. Il ne faut pas charger cette page par AJAX mais rediriger l'utilisateur vers elle pour que l'authentification puisse avoir lieu (avec window.location ou un lien par exemple). En cas de succĂšs, les cookies de l'utilisateurs seront modifiĂ©s et les requĂȘtes telles que /q?type=uinfo fonctionneront.


GET /q?type=uinfo

Retourne des informations relatives à l'utilisateur si celui-ci est connecté. Retourne une erreur si l'utilisateur n'est pas connecté.

Format de la réponse: {"name":"name","rights":["admin","make_recipe"],"shopping_lists":[{...},...]}


GET /q?type=uname&id=<id>

Retourne le nom de l'utilisateur dont l'id est <id> . Retourne une erreur si celui-ci n'existe pas.

Format de la réponse: {"name":"name"}


POST /q?type=make_recipe

Permet de crĂ©er une nouvelle recette. Le body de la requĂȘte POST doit utiliser le format JSON (Content-Type:application/json)

Le format du body est le suivant:

{
	"title":"title", // max length = 100 characters All UTF8 is allowed (including emojis)
	"description":"description", // max length = 600 characters. All UTF8 is allowed (including emojis)
	"image":"<img data>", // filecontent of an image, as base64 encoding.
     "tags":["tag1","tag2"] // 6 tags max, max length of a tag = 50 characters. Must only contain lowercase letters (special characters like Ă©,ĆŒ or ę are allowed) and spaces,
	"ingredients":["ingredient1","ingredient2",...], // must not be empty, max length = 100, max length of an ingredient: 200 characters. Only numbers, lowercase and uppercase letters and spaces are allowed and currency signs (€ or „)
	"steps":["Mix stuff","Heat it",...], // must not be empty, max length = 100, max length of a step: 1000 characters. Everything is allowed. Some custom formatting is supported (*bold*, ~strike~)
}

Tous les champs sont obligatoires sauf image. Les contraintes sont vĂ©rifiĂ©s cĂŽtĂ© serveur. En cas de non-respect des contraintes ,le serveur renverra une erreur et la recette ne sera pas crĂ©Ă©e. Je conseille que les tags puissent ĂȘtre choisis depuis un menu dĂ©roulant pour forcer l'utilisateur Ă  classer son plat dans la catĂ©gorie "plat principal" ou "dessert", etc... pour faciliter la recherche. Si l'utilisateur n'est pas connectĂ© ou ne possĂšde pas le droit "new_recipe", la recette ne sera pas crĂ©Ă©e.

image doit ĂȘtre encodĂ©e avec une base64. Pour plus d'information, se rĂ©fĂ©rer au post suivant: https://stackoverflow.com/questions/34485420/how-do-you-put-an-image-file-in-a-json-object. La solution avec le canvas permet de crop l'image si celle-ci est trop grosse pour ĂȘtre gentil avec la back-end.

En cas de succĂšs, la rĂ©ponse sera: {"id":"id"} oĂč id est l'identifiant de la recette crĂ©Ă©e.


POST /q?type=delete_recipe&id=id

Supprime la recette dont l'id est id si les droits de l'utilisateur effectuant la recette sont suffisant.

Retourne: {msg:OK} en cas de succĂšs


POST /q?type=make_comment&id=id

Crée un commentaire sur le recette dont l'id est id. Le format du body est le suivant:

{
	"content":"The content of the comment." // max length = 1000 characters
}

En cas de succĂšs, la rĂ©ponse sera {"id":"id"} oĂč id est l'identifiant du commentaire crĂ©Ă©


POST /q?type=delete_comment&id=id&cid=id

Supprime le commentaire avec l'id cid sur la recette id (si les droits de l'utilisateur effectuant la recette sont suffisant)

Retourne: {msg:OK} en cas de succĂšs


POST /q?type=rate&id=id&rate=rate

Permet à un utilisateur de noter une recette. id est l'id de la recette à noter. rate est un nombre entre 0 et 5 inclus correspondant à la note donnée par l'utilisateur. Si l'utilisateur a déjà noté la recette, cette url modifiera la note précédente.

Retourne: {msg:OK} en cas de succĂšs


POST /q?type=add_shop_list&item=item

Ajoute item à la liste de courses de l'utilisateur. item est une chaine de caractÚre quelconque de longueur inférieure à 500 caractÚres. La liste de course d'un utilisateur ne peut contenir que 300 éléments au maximum.

Rappel: /q?type=uinfo récupÚre les listes de courses de l'utilisateur.


POST /q?type=remove_shop_list&id=id

Retire le id-iÚme élément de la liste de course de l'utilisateur. id est un entier positif.


Les url prĂ©sentĂ©es sont dĂ©finitives, les champs ne le sont pas: Un champ utilisateur et image seront probablement ajoutĂ©s pour les requĂȘtes relatives aux recettes.


N'importe quel endpoint est susceptible de générer une erreur lorsqu'il est appelé. Dans ce cas, il retournera un objet de la forme {error:"Description of the error"}. La description de l'erreur ne dévoile aucune information confidentielle sur la structure de la backend. Des exemples d'erreurs sont: database not ready. Please wait a bit. ou type option not recognized


Liste des droits des utilisateurs

  • Par dĂ©faut, un utilisateur non connectĂ© peut voir toutes les recettes et les commentaires.
  • Un utilisateur connectĂ© peut commenter les recettes, les notĂ©s et supprimer les commentaire qu'il a crĂ©Ă© et supprimer les recettes qu'il a crĂ©e.
  • make_recipe donne le droit de crĂ©Ă© des recettes
  • delete_recipe donne le droit de supprimer n'importe quelle recette du site
  • delete_comment donne le droit de supprimer n'importe quel commentaire du site

Sécurité

Les mots de passe sont stockĂ©s hachĂ©s 30 fois avec du sel de qualitĂ© alĂ©atoire cryptographique avec du sha256, selon un algorithme Ă©vitant le parcours de cycle large. Les requĂȘtes sont effectuĂ©s depuis du JS avec du NoSQL ce qui limite les possibilitĂ©s d'injection. Le type de toute les variables provenant de l'utilisateur est vĂ©rifiĂ© pour voir si c'est bien "string". Le contenu stockĂ© dans la base de donnĂ©e peut contenir des caractĂšres du type > ou <. C'est au front-end de retirer ces caractĂšres et d'appliquer des traitements de style. L'assainissement des donnĂ©es s'effectue cĂŽtĂ© client Ă  la lecture, en particulier l'Ă©vasion des tags HTML.

Attention, lors du dĂ©ploiement, le port permettant d'accĂ©der Ă  l'interface HTTP d'administration de la base de donnĂ©es ne doit pas ĂȘtre accessible. De mĂȘme pour le port permettant de connecter d'autres serveurs pour la rĂ©alisation de clusters. Il faut donc bien indiquer les options -no-http-admin et mettre db_port et db_port+1Ă  des ports inaccessibles depuis l'extĂ©rieur. (27017 et 27018 par dĂ©faut)

About

The Papp's !


Languages

Language:JavaScript 72.9%Language:CSS 13.6%Language:HTML 13.5%