[](https://gitlab.binets.fr/br/sigma-backend/commits/master) Sigma - serveur backend === Ce dépôt contient le _backend_ de Sigma, le successeur de Frankiz, un site étudiant permettant de gérer les groupes et les étudiants du plateau de Saclay. À terme, ce projet doit tourner sur un serveur du BR et servir d'API à un _frontend_ React *au code séparé et documenté séparément* toute les données nécessaires à son bon fonctionnement (authentification, appartenance à un groupe, droits de visibilité...). Le dépôt pour le serveur front se trouve ici : <https://gitlab.binets.fr/br/sigma-frontend> (on l'appellera indifféremment serveur front, front ou frontend...) **Le but des lignes qui suivent est de permettre au lecteur de rapidement mettre en place et lancer un sigma en local et se familiariser avec son administration.** Comment obtenir la documentation détaillée du projet est expliqué à la fin de ce document. Pour avoir un guide détaillé de l'installation de l'environnement de dev (i.e accès en écriture aux dépôts, installation de Node.js, npm, VSCode et les extensions utilisées), voir [ce carnet](https://carnets.binets.fr/yhvva08BQiWBZ0r0rS3RMw#). Pour obtenir une copie du projet, cloner le dépôt par : ```bash git clone git@gitlab.binets.fr:br/sigma-backend.git # ou git clone https://gitlab.binets.fr/br/sigma-backend.git ``` ## Installer les dépendances *npm* On utilise un serveur node.js avec [express.js](https://expressjs.com/). [^server] Utiliser Node.js permet d'utiliser facilement [*npm*](https://www.npmjs.com/) (Node Package Manager). Une "dépendance" est un package utilisé dans le projet. [^server]: il est configuré dans [`app.ts`](./src/app.ts) puis lancé sur le port 3000 dans [`index.ts`](../src/index.ts). On trouve la liste des dépendances dans [`package.json`](../package.json). Express est un exemple de dépendance normale, utilisée en production ; nodemon et ESLint (voir infra) sont des dépendances dev (`devDependencies`), utilisées seulement en mode développement. Les dépendances s'installent avec `npm install`. Cette commande a deux comportements possibles selon la valeur de la variable `NODE_ENV` (vérifier avec la commande `echo "$NODE_ENV"`): * si `NODE_ENV` n'est pas configuré : on installe tout * si `NODE_ENV` == `development` : on installe tout * si `NODE_ENV` == `production` : on n'installe pas les dépendances développeur Pour installer les dépendances spécifiées dans `package.json` il faut donc lancer : ```bash npm install ``` Les dépendances principales utilisées sont - *knex.js*, qui permet de construire des requêtes SQL facilement, - *GraphQL*, qui fournit une couche d'abstraction pour l'échange de données frontend-backend, - *ldap.js*, qui permet d'interroger un serveur LDAP, - *webpack*, qui compile et optimise tout le code source javascript en un `bundle.js`, - *ESLint* et *TSLint*, pour le développement, outils de vérification syntaxique. Pour pouvoir initialiser la BDD PostgreSQL avec les migrations du repo, il faut installer la dépendance *knex.js* globalement : ```bash sudo npm install -g knex ``` Et une dépendance non-npm, *PostgreSQL* (linux debian est supposé) : ```bash sudo apt install postgresql ``` ## Configuration L'API est conçue pour fonctionner dans plusieurs environnements. On peut donc la configurer via des fichiers ou des variables d'environnement. En deux mots : * [`jsdoc_config.json`](../jsdoc_config.json), [`Dockerfile`](../Dockerfile), [`.dockerignore`](../.dockerignore), [`.gitignore`](../.gitignore), [`.eslintignore`](../.eslintignore), [`webpack.config.js`](../webpack.config.js) : transparents * [`ldap_config.json`](../ldap_config.json) : noms champs du LDAP * [`ldap_credentials.json`](../ldap_credentials.json) : paramètres de connexion secrets au LDAP * [`.estlintrc.json`](../.eslintrc.json) : ESLINT ou à quel point cancériser le développeur * [`.gitattributes`](../.gitattributes) : terminaison de fichiers * [`.gitlab-ci.yml`](../.gitlab-ci.yml) : pipeline gitlab * [`package.json`](../package.json) et [`package-lock.json`](../package-lock.json) : gestion des dépendances usuel * [`tsconfig.json`](../tsconfig.json) : configure la compilation de fichiers Typescript en Javascript * [`tslint.json`](../tslint.json) : configure tslint, utilisé plutôt que tsc dans le projet final * [`.env`](../.env) : définit les variables d'environnement et ports utilisés... Certains de ces fichiers de configurations ont une version "distribution" en "_dist" qui permet de les partager (le reste du temps ils sont dans le .gitignore), quitte à les renommer et à les modifier en local. ### Configuration LDAP L'API de Sigma nécessite de se connecter au LDAP Frankiz, à la fois pour obtenir des données et pour l'authentification des utilisateurs. Cela est fait à l'aide de la librairie [ldapjs](http://ldapjs.org) pour faire les requêtes au LDAP et [passportJS](http://www.passportjs.org/) pour l'authentification. * La configuration LDAP de base se situe dans [ldap_config.json](../ldap_config.json). * Les identifiants utilisés pour authentifier le serveur au LDAP sont dans [ldap_credentials.json](../ldap_credentials.json). Ils prennent la forme suivante: ```json { "dn": "uid=<username>,ou=eleves,dc=frankiz,dc=net", "passwd": "<password>" } ``` On peut s'inspirer de [ldap_credentials_dist.json](../ldap_credentials_dist.json). * Elle est importée dans l'application depuis [src/ldap/internal/config.ts](../src/ldap/internal/config.ts). * Si la variable d'environnement `LDAP_URI` est définie, l'adresse où trouver le LDAP est remplacée. Le LDAP de Frankiz est sous OpenLDAP, qui a l'avantage d'être largement utilisée, documentée sur Internet, compatible avec des lecteurs génériques comme [JXplorer](http://jxplorer.org/) et gérant ses propres logs (voir [ce blog](https://www.vincentliefooghe.net/content/openldap-gestion-des-logs)). **Exemple** Si on développe en dehors du plâtal et qu'on ouvre un proxy SSH avec _port forwarding_ du LDAP (<ldap.eleves.polytechnique.fr:389>) vers <localhost:8389>, on s'y connecte en définissant : `LDAP_URI=ldap://localhost:8389`, soit en faisant `export LDAP_URI=...`, soit en écrivant un fichier `.env`. Le fichier `config.ts` s'occupe du reste. ### Variables d'environnement | **Variable** | **Description** | **Défaut** | | ------ | ------ | ----- | | NODE_ENV | Type de l'environnement pour node : `development` ou `production` | `development` | | TARGET_ENV | Config choisie pour la BDD et le LDAP : `development`, `staging` ou `production` | [.env](../.env) | | HOST | Addresse sur laquelle le serveur écoute des requêtes. | [index.ts](../src/index.ts) | | PORT | Port sur lequel le serveur écoute | [.env](../.env) | | LDAP_URI | URI vers le serveur LDAP. | [ldap_config.json](../ldap_config.json) | | DB_HOST | Addresse de la base de données. | [knexfile.js](../db/knexfile.js) | | DB_USER | Utilisateur de la BDD | [knexfile.js](../db/knexfile.js) | | DB_PASSWD | Mot de passe de la BDD | [knexfile.js](../db/knexfile.js) | | DB_DATABASE | Base sur laquelle se connecter | [knexfile.js](../db/knexfile.js) | Certaines variables doivent etre définies dans un fichier `.env`. On peut se contenter de recopier [.env_dist](../.env_dist) avec `cp .env_dist .env`. Par ailleurs, on peut définir ces variables d'environnement, **dans l'ordre décroissant de priorité :** * dans sa session de terminal (équivalent à `docker run -e KEY=value`) : ```bash export KEY=value ``` * au moment de lancer l'application (cela écrase la valeur dans la session, mais seulement pour cette commande) : ```bash KEY=value npm start ``` * dans un fichier [`.env`](https://www.npmjs.com/package/dotenv) (plus faible niveau, n'écrase jamais une valeur déja existante) : ```dotenv KEY1=value1 KEY2=value2 ... ``` ## Setup la BDD PostgreSQL La BDD PostgreSQL est utilisée pour stocker permissions, écoles des utilisateurs, annonces et événements. Créer un rôle PostgreSQL "sigma" : ```bash sudo -u postgres -s createuser sigma --login --createdb --pwprompt # penser à répercuter le mot de passe choisi dans `.env` ``` Créer une base de données PostgreSQL "sigma_dev" : ```bash createdb sigma_dev -U sigma -W ``` - Si vous n'arrivez pas à vous connecter (`createdb: could not connect to database template1: FATAL: Peer authentication failed for user`) : mettre à jour votre fichier `pg_hba.conf`. - `sudo nano /etc/postgresql/<version>/main/pg_hba.conf` (en tant qu'utilisateur normal) - remplacer tous les `peer` par `md5` - redémarrer le serveur postgres : `sudo /etc/init.d/postgresql restart` - Si vous souhaitez utiliser d'autres noms que "sigma" et "sigma_dev" : ça ne pose pas de problème, il vous faudra simplement modifier [.env](../.env). - Sortir de l'utilisateur `postgres` avec CTRL + d Exécuter les *migrations* et les *seeds* de knex (dans le dossier `db`) : ```bash # construire le schéma de la BDD, i.e. définir les tables et leur structure. knex migrate:latest # insérer des données de test dans la BDD knex seed:run ``` Voilà, vous avez une base de données à jour ! ## Démarrer le serveur Dire à webpack de build le projet (build le bundle `../build/bundle.js`) : ```bash npm run dev # en mode developpement # ou npm run build # en mode production ``` Lancer un serveur express/node : ```bash npm run start # ou le raccourci: npm start ``` Comme indiqué dans src/index.js, ceci lance un serveur servant l'application express sur le port 3000. ## Alternative : déployer dans un conteneur Docker L'image Docker est définie dans [`Dockerfile`](../Dockerfile). Il s'agit d'une distro Alpine avec Node.js et libstdc++. Lors du _build_ les dépendances _runtime_ dont dépend le `bundle.js` sont installées. Compiler l'image : ```bash docker build -t sigma-api . ``` Faire tourner le conteneur : ```bash docker run sigma-api ``` Idem mais avec un LDAP custom : ```bash docker run -e LDAP_URI=ldap://172.17.0.1:8389 sigma-api ``` ## Mode développement / staging / production Deux variables d'environnement gerent le mode de déploiement de l'application : `NODE_ENV` et `TARGET_ENV`. - `NODE_ENV` détermine dans quel mode sont configurés node et tous ses modules : en particulier, des que `NODE_ENV === production`, de nombreux modules mettent en place des optimisations et les `devDependencies` ne sont plus installés... Par convention, `NODE_ENV` ne doit prendre que les valeurs `development` ou `production`. - `TARGET_ENV` est une variable custom qui détermine dans notre code quelles configurations doivent etre chargées, en particulier en ce qui concerne le LDAP et la BDD. `TARGET_ENV` peut prendre les trois valeurs `development`, `staging` et `production`. Voici plus en détail les 3 contextes différents de déploiement : - mode "développement" (quand on compile et fait tourner le serveur en local) : - installer toutes les dépendances avec `npm i && npm i -g knex` - configurer le LDAP : `ldap_credentials.json` - configurer l'environnement : `.env` avec `TARGET_ENV=development` - setup la BDD `sigma_dev` et la populer avec `knex migrate:latest && knex seed:run` - compiler le serveur : `npm run watch` - démarrer Nodemon : `npm run start` - mode "staging" (quand on lance l'application en mode dev sur un serveur pour des tests) : - installer toutes les dépendances avec `npm i && npm i -g knex` (on a besoin de tout pour pouvoir compiler) - configurer le LDAP : `ldap_credentials.json` - configurer l'environnement : `.env` avec `TARGET_ENV=staging` - setup la BDD `sigma_staging` et la populer avec `knex migrate:latest --env staging` - compiler le serveur en mode production : `npm run build` - démarrer le serveur : `npm run start_prod` - mode "production" (quand on lance l'application précompilée en `bundle.js` en prod) : - **définir NODE_ENV** : `export NODE_ENV=production` dans la session terminal - installer seul les dépendances prod : `npm i && npm i -g knex` (les devDependencies ne sont plus installées) - configurer le LDAP : `ldap_credentials.json` - configurer l'environnement : `.env` avec `TARGET_ENV=production` - setup la BDD `sigma_prod` et la populer avec `knex migrate:latest` - lancer le serveur précompilé : `npm run start_prod` ## Scripts Les scripts sont des instructions en ligne de commande que l'on peut faire tourner avec la commande `npm run`. Ce sont des raccourcis pour gagner du temps sur des opérations un peu longues. Ils sont définis dans [`package.json`](../package.json). Les plus importants sont détaillées ci-dessous : - `npm run build` : transpiler avec Webpack, en mode production - `npm run dev` : idem, mais en mode développement - `npm run watch` : idem, mais retranspile automatiquement dès que le code est modifié. - `npm run start` : lancer un serveur Node avec nodemon - `npm run start_prod` : lancer le serveur avec Node - `npm run doc` : générer la doc JSDoc - `npm run lint` : discontinué - `npm run eslint` : vérifier la syntaxe de tous les fichiers .js du dossier src/ - `npm run tslint` : vérifier la syntaxe de tous les fichiers .ts du dossier src/ - `npm run tsfix` : vérifie et corrige - `npm run tsc` : compile le code TypeScript - `npm run test` : démarre les tests unitaires `npm run start` démarre en fait le serveur buildé [`build/bundle.js`](../build/bundle.js) avec [nodemon](https://nodemon.io/), un outil de dév qui le redémarre automatiquement après toute modification *du bundle*. Donc, lancer `npm run watch` dans un terminal et `npm run start` dans un autre permet de rebuilder **et** relancer automatiquement le serveur, après toute modification *du code source*. ## Panneau d'administration Il est accessible par navigateur au path [/adminview/admin](localhost:3000/adminview/admin) ; n'importe quel path devrait rediriger dessus. L'accès y est protégé par une page d'authentification, les identifiants à utiliser sont ceux de Frankiz. Le hruid (i.e. prenom.nom) de l'utilisateur doit de plus être sur une whitelist des hruid autorisés. Pour l'instant cette whitelist est hardcodée dans le code source. ### Accès direct à la BDD Le panneau d'administration sert (ou plutôt, servira à terme) à accéder directement à la BDD propre de sigma, grâce à une API REST. Autrement dit : - on accède à la table `table_name` par une requête GET à [adminview/db/table_name](localhost:3000/adminview/db/table_name), - et aux colonnes `columns` de cette table par une requête GET à [/adminview/db/table_name?columns=columns](localhost:3000//adminview/db/table_name?columns=columns). ### GraphQL Voyager L'application Voyager, accessible à [/adminview/voyager](localhost:3000/adminview/voyager), permet de visualiser le « graphe » sous-jacent à la structure de l'API. ### GraphQL Playground == Attention, comme tout GraphQL Playground est géré directement par le package apollo-server-express, les requêtes dans le Playground **ne sont pas** soumises au mêmes règles de permission que dans adminview. (D'ailleurs, `/graphql` n'est même pas un sous-path de `/adminview`.) == Accéder via un navigateur à `/graphql` renvoie l'application GraphQL Playground. Il s'agit du même `/graphql` que l'_endpoint_ de l'API, mais le serveur est configuré de sorte à renvoyer Playground lorsqu'il détecte un accès via navigateur. Les requêtes dans le Playground sont cependant soumises au mêmes permissions que dans l'API GraphQL [^doute]. GraphQL Playground est désactivé en production. [^doute]: euuuuh à vérifier... ## Tests Sigma possède une suite de tests unitaires, déstinés à tester les resolvers graphql. Pour executer les tests, il suffit d'utiliser la commande `npm test`. Les tests effectués sont tous les fichiers en `*.test.ts`, actuellement [resolvers.test.ts](../test/resolvers/resolvers.test.ts). Les différentes requetes testées sont stockées sous forme de liste dans différents fichiers dans [data/](../test/resolvers/data). Chaque élément contient une requête graphql, et les données qu'elle doit renvoyer. Quand les seed sont modifiées, il faudra modifier les resultats attendus également. Les tests peuvent être créés ou mis à jour en entrant la requête dans graphiql, et en copiant le resultat. ## Documentation La documentation détaillée du projet se trouve [ici](index.html) (fichier `./doc/index.html`). Elle doit être préalablement compilée avec [JSDoc](http://usejsdoc.org/index.html), selon le fichier de config idoine (fichier `./jsdoc_config.json` à la racine du projet). Le script pour faire tourner JSDoc et (ré)générer la documentation est : `npm run doc`. Les fichiers compilés se situent dans `./doc/` avec leurs fichiers image. Par nature de l'outil JSDoc il est facile de documenter en détail des fonctions .js mais plus compliqué de documenter un fichier. A chaque execution JSDoc rajoute les commentaires placés dans chacun des fichiers dans la doc de façon structurée. Les notes en Markdown placés dans notes/ sont également rajoutées en tant que tutoriels (voir {@tutorial CONTRIBUTING}).