Nom de l’auteur/autrice :yvonh

Créer une application pour envoyer des sms avec Flutter

Flutter est un système de développement hybride, qui permet en une seule base de code de développer our IOS et Android, mais aussi pour le Web. Le langage utilisé est Dart.

Installation de Flutter (pour Windows)

Allez sur le site officiel, pour la version Windows, et il y a aussi pour MacOS et Linux.

Installation du plugin Flutter pour VSCode.

Installez le plugin Flutter de l’éditeur DartCode,. En bas à droite dans la barre de status, vous verrez « No Device », cliquez dessus, et la liste déroulante suivante va s’afficher.

Installer le nécessaire pour produire une application Android

Traditionnellement, si on veut créer une application Android nativement, on utilise Java( ou Kotlin), et on a besoin d’Android Studio, l’EDI pour développer des application Android. Si nous utilisons VScode, on n’en est pas moins contraint d’avoir Android Studio, qui apporte les outils de build pour avoir notre application Android.

Pour installer Android Studio allez sur ce site, après téléchargement/

Quid d’IOS?

Pour builder une applicaiton IOS, il faut l’équivalent sur Mac XCode. MAis XCode ne tourne que sur Mac, donc sur Windows on n epourra pas builder pour Mac. Par contre, sur un Mac vous pouvez aussi installer Android Studio !

Commandes en ligne pour vous aider

flutter --version
flutter emulator
flutter doctor

Il me manque Android toolchain, je vais donc aller dans Android Studio l’installer. On va aller dans le SDK Manager pour ajouter les outils manquants.

Aller dans System Setting > Android SDK > SDK Tools > et cochez la case Android SDK Command-line-tools (latest) et Apply.

flutter doctor --android-licenses  comme indiqué pour accepter les license Android.

Création de l’application flutter

créez le répertoire
> mkdir sms
> cd sms
> flutter create sms

Creating project sms...
Resolving dependencies in smsapp... (1.4s)
Found a legacy Pub cache at C:\Users\admin\AppData\Roaming\Pub\Cache. Pub is using C:\Users\admin\AppData\Local\Pub\Cache.

Consider deleting the legacy cache.

See https://dart.dev/resources/dart-3-migration#other-tools-changes for details.
Got dependencies in sms.
Wrote 129 files.

All done!
You can find general documentation for Flutter at: https://docs.flutter.dev/
Detailed API documentation is available at: https://api.flutter.dev/
If you prefer video documentation, consider: https://www.youtube.com/c/flutterdev

In order to run your application, type:

  $ cd sms
  $ flutter run

Your application code is in sms\lib\main.dart.

Note : mettre à jour flutter

Si votre version de Flutter a pris la poussière, faites la commande suivante pour mettre à jour :

flutter upgrade

NodeJS Express et variables d’environnement tout ce qu’il faut savoir

Vous connaissez tous ce code si vous avez fait du Node-Express :

const port = process.env.PORT || 3000;
const folder = process.env.FOLDER || '';

Lorsque vous démarrez votre serveur NodeJS, il écoute le port 3000 grâce à ce bout de code

app.listen(port, () => {
    console.log(`Server running on http://localhost:${port}`);
});


la ligne de code process.env.PORT || 3000; veut dire qu’en environnement de développement l’url pour atteindre le serveur est http://localhost:3000. Quid de l’environnement de production? Et d’abord, comment sait on que c’est pour l’environnement de production? Tout simplement en développement la variable port vaut 3000 car la constante PORT n’est pas définie en dévelolppement (normalement). En vertue de l’opérateur || (OU), ce sera al valeur 3000 qui sera assignée à al variable port.

En général en programmation, les constante (valeurs qui ne bougent pas sont en majuscule, et les variables en minuscule.

Considérons les différents cas de figure où la variable port peut être définie

Environnement local

On décide d’assigner une valeur à port (chose qu’on ne fait pas normalement) mais c’est pour illustrer. La commande ci-dessous est valable pour la session.

#MACOS/Linux
PORT=3000 node server.js

ou 
export PORT=3000
node server.js


#windows CMD
set PORT=3000
node server.js

Utilisation de fichier d’environnement

Créer un fichier .env
#installer dotenv
npm install dotenv


require('dotenv').config();

const port = process.env.PORT || 3000;
console.log(`Server running on port ${port}`);

configuration niveau système

Dans les systèmes Linux, vous pouvez ajouter la variable d’environnement au fichier .bashrc ou au fichier etc/environment

Faire une archive tar sans préserver les chemins de fichiers

Syntaxe usuelle d la commande tar pour la création d’une archive

soit la structure de fichiers suivant :
./test/folder1/folder1a/fichier1.txt
./test/folder1/fichier1.txt
./test/folder2/fichier3.txt


Nous sommes dans le répertoire test, et nous créons une archive de façon classique sans compression:
> tar -cvf archive.tar *

folder1/
folder1/folder1a/
folder1/folder1a/fichier1.txt
folder1/fichier1.txt
folder2/
folder2/fichier3.txt

Pour voir le contenu sans le désarchiver

> tar -tf archive.tar

folder1/
folder1/folder1a/
folder1/folder1a/fichier1.txt
folder1/fichier1.txt
folder2/
folder2/fichier3.txt

Nous voyons que les fichier ainsi que les dossiers sont préservés, ainsi si vous décompressez l’archive dans un autres répertoire, la structure sera conservée.

Archiver avec tar sans garder la structure des fichiers

Parfois on a envie de rassembler tous les fichiers sans conserver l’arborescence, dans un unique répertoire. Voici comment faire :

On se place dans le répertoire à archiver.
> find . -type f | tar --transform='s|^\./||;s|/|_|g' -T - -cvf archive.tar

on pipe deux commandes
find .-type f va trouver tous les fichiers à partir de là où se trouve l'invite de commande

Le seconde commande va archiver avec une transformation, on renomme les fichiers qui sont à archiver pour les rendre uniques. Le format du nom du fichier est composé de mot du chemin vers le fichier dont les slash seront transformés en underscore.

> tar -tf archive.tar
folder1_folder1a_fichier1.txt
folder1_fichier1.txt
folder2_fichier3.txt

Le renommage est plus prudent car on peut avoir deux fichiers du même nom.

Chargez des images avec NodeJS

Vous savez sans doute charger des images en PHP, mais savez vous le faire en NodeJS? On petu être perdu dans on aborde une nouvelle technologie. Nous allons voir comment le faire avec NodeJs. Le principe est un peu le même, on envoit un binaire via hTTP depuis la pageHTML, et côté serveur on récupère le fichier, mais là vous devez faire un peu plus de choses.

Le code de la page HTML

On fera au plus simple, un formulaire, avec une chose d’importance, l’envoi doit se faire en multipart/form-data. Qu’est ce que c’est? Lorsque vous avez un formulaire simple qui envoit des caractères alphanumériques, vous ne spécifiez pas le type d’envoi (enctype)

<form action="/submit" method="POST">
  <input type="text" name="username" value="example_user">
  <input type="text" name="age" value="30">
  <button type="submit">Submit</button>
</form>

Le format urlencode

En fait il y a un enctype par défaut qui est application/x-www-form-urlencoded, cela encode les données des champs sous forme de paire clé-valeur comme dans une url, ce qui le fait ressembler à un querystring. Les caractères spéciaux sont remplacé, par exemple le caractère space est remplacé par %20, les successions de paries clé-valeur sont liées par le signe esperluette « & ». Le request body est :

id=3&nom=antoine

C’est le mode préféré quand on n’a pas de fichier à uploader.

Ensuite si on veut envoyer des fichier binaires, on choisit l’enctype multipart/form-data.

Le format multipart/form-data

Utilisez ce format pour l’nevoi de fichiers. Si vous faites un envoi en multipar, observez le header envoyé, il a la forme suivante :

POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

Boundary veut dire limite ou frontière en anglais. Vos données envoyée se font par troçon délimité par la frontière.

Le request body est le suivant

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="username"

example_user
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="example.jpg"
Content-Type: image/jpeg

<binary content of the file>
------WebKitFormBoundary7MA4YWxkTrZu0gW--

Les données envoyée sont délimitées par la chaine « boundary=–« . On utilise ce mode quand il y a u nmix de texte et de fichier binaire.

Le format text/plain

username=example_user
age=30

Ce format est plus rare, et vous n’avez pas besoin de l’utiliser. Il envoit les données sans encoder

Mise en oeuvre du projet

Il va falloir installer NodeJs, avec la librairie multer our récupérer les binaires. Le fichiet HTML

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <title>Document</title>
</head>

<body>
    <div class="container">
        <h1>File Upload</h1>
        <form id='form'>
            <div class="input-group">
                <label for='name'>Your name</label>
                <input name='name' id='name' placeholder="Enter your name" />
            </div>
            <div class="input-group">
                <label for='files'>Select files</label>
                <input id='files' type="file" multiple>
            </div>
            <button class="submit-btn" type='submit'>Upload</button>
        </form>
    </div>
    <script src='./script.js'></script>
</body>

</html>

Le fichier JS front end

const form = document.getElementById("form");

form.addEventListener("submit", submitForm);

function submitForm(e) {
    e.preventDefault();
    const name = document.getElementById("name");
    const files = document.getElementById("files");
    const formData = new FormData();
    formData.append("name", name.value);
    for (let i = 0; i < files.files.length; i++) {
        formData.append("files", files.files[i]);
    }
    fetch("http://localhost:3000/upload_files", {
        method: 'POST',
        body: formData
    })
        .then((res) => console.log(res))
        .catch((err) => console.log("Error occured", err));
}

Le fichier style

/* style.css */
body {
    background-color: rgb(6, 26, 27);
}

* {
    box-sizing: border-box;
}

.container {
    max-width: 500px;
    margin: 60px auto;
}

.container h1 {
    text-align: center;
    color: white;
}

form {
    background-color: white;
    padding: 30px;
}

form .input-group {
    margin-bottom: 15px;
}

form label {
    display: block;
    margin-bottom: 10px;
}

form input {
    padding: 12px 20px;
    width: 100%;
    border: 1px solid #ccc;
}

.submit-btn {
    width: 100%;
    border: none;
    background: rgb(37, 83, 3);
    font-size: 18px;
    color: white;
    border-radius: 3px;
    padding: 20px;
    text-align: center;
}

Le fichier server.js

//https://blog.logrocket.com/multer-nodejs-express-upload-file/

const express = require('express');
const app = express();
const multer = require('multer');
// configuration de multer
const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, 'uploads/')
    },
    filename: function (req, file, cb) {
        cb(null, file.originalname)
    }
})
const upload = multer({
    storage: storage
})


const path = require('path');
app.use(express.static(path.join(__dirname, './')))


app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// app.post('/upload_files', (req, res) => {
//     console.log(req.body);
// })
app.get('/', (req, res) => {
    res.sendFile(__dirname + '/index.html');
})

app.post('/upload_files', upload.array("files"), (req, res) => {
    console.log(req.files);
    console.log(req.body);
    res.json({ message: "Files uploaded successfully" });
})

app.listen(3000, () => {
    console.log("Server is running on port 3000");
})

J’ai fourni les scripts, je commenterai le fichier server.js puisque tout se joue dans ce fichier.

  • on utilise multer pour prendre en charge l’upload de fichier,
  • la variable storage paramètre le fonctionnement du multer, répertoire de sauvegarde des fichiers, nommage des fichiers
  • Pour servir index.html, on utiliser express.static, notez l’argument de path.join qui spécifie le répertoire du fichier index.html. express.static permet de servir les fichiers statiques (css, texte,html,javascript), il faut le dire explicitement.
  • app.use(express.urlencoded({ extended: true })); est un middleware, qui va attacher à req.body (le corps de la requête) la parsing des éléments envoyé par le formulaire avec un enctype=application/x-www-form-urlencoded. Concernant extended:true, c’est juste un choix de librairie de parsing, si à true, va utiliser la librairie qs qui supporte la récursivité des objets, alors que extende:false va utiliser la librairie querystring, qui ne supporte par la récursivité.
app.use(express.urlencoded({ extended: true}));
{
  "username": "JohnDoe",
  "details": {
    "age": "30"
  }
}

app.use(express.urlencoded({ extended: false }));
{
  "username": "JohnDoe",
  "details[age]": "30"
}

Facilitons nous la vie avec nodemon

J’utilise systématiquement nodemon pour redémarrer le serveur nodeJS quand il y a une modification de fichier, c’est très pratique pour développer, on n’ pas besoin de redémarrer manuellmenet à chaque fois en ligne de commande : node server.js, à la place on fait nodemon server.js.

La librairie multer

Il y a beaucoup plus d’option sur la librairie multer que je n’ai montré dans ce post, je vous renvois vers la documentation officielle de multer. Multer est un middleware d’express, qui permet de sauvegarder les fichiers binaires envoyés. Autres propriétés intéressant : mimetype, size, originalName, fileFilter pour filtrer les types de fichier.

Crypter et décrypter un message avec un clé SSH en ligne de commande shell

Nous allons voir comment crypter en ligne de commande un message et ensuite le décrypter. Il existe plusieur contexte pour faire cette opération, comme avec un langage de programmation, mais cela reviens à utiliser un programme comme OpenSSL.

Pour faire ce tuto, je vous invite à regarder le tuto sur la création d’une clé SSH.

Commande pour crypter sous bash

echo "Ton message secret" | openssl rsautl -encrypt -pubin -inkey id_rsa.pub > encrypted.txt

Il se peut que vous ayez une erreur du type : unable to load Private Key

Ceci vient du fait que vous avez une clé du type open SSH. Il faudrait une clé de type PEM. Voici la commande pour convertir en clé PEM. Mais avant veuillez faire un cat sur votre clé privée

cat id_rsa.pub
Si vous voyez un texte commençant par 
-----BEGIN RSA PRIVATE KEY-----
ce n'est pas bon il faut qu'il commence par 
-----BEGIN PUBLIC KEY-----

Essayez avec cette comamnde pour convertir votre clé

ssh-keygen -f id_rsa.pub -e -m pem > public.pem

et regardez le début de votre clé privée, si ça commence toujours par -----BEGIN RSA PRIVATE KEY-----
ce n'est toujours pas bon (vous pouvez vous enrendre compte en essaynt de crypter.

Il vous faut faire la commande suivante :
ssh-keygen -f id_rsa.pub -e -m PKCS8 > public.pem


Grâce à l'ajout de PKCS8, en vérifiant le contenu de la clé qui doti avoir le bon en-tête

Maintenant vous pouvez tenter de crypte avec la ligne suivante :
echo "your secret message" | openssl rsautl -encrypt -pubin -inkey public.pem > encrypted.txt

Commande pour décrypter

Essayez d’afficher encrypted.txt, ce sera du charabia. Maintenant il faut faire l’opération inverse

openssl rsautl -decrypt -inkey id_rsa -in encrypted.txt
Le message en clair va s'afficher

Récapitulatif

  • Créer la paire de clés SSH, on se sert de la publique id_rsa.pub
  • convertir la clé SSH publique au format PEM avec l’argument PKCS8
  • utiliser la commande pour chiffrer
  • utiliser la command pour déchiffrer

Où est la clé privée?

Vous avez remarqué que nous n’avons pas parlé de clé privée du tout, on en avait pas besoin. En fait l’usage de la clé privée sert dans un processus de signature, pas dans le contexte de chiffrement de message.

Deux Scénarios dans RSA

1. Confidentialité des données (Chiffrement standard)

  • Chiffrer avec la clé publique : Tout le monde peut chiffrer des données en utilisant la clé publique.
  • Déchiffrer avec la clé privée : Seul le propriétaire de la clé privée peut déchiffrer les données.
    C’est le cas d’utilisation typique pour envoyer des messages en toute sécurité.

2. Signatures numériques (Authentification et intégrité)

  • Signer avec la clé privée : La clé privée génère une signature pour un message.
  • Vérifier avec la clé publique : Toute personne disposant de la clé publique peut vérifier que la signature a été générée par la clé privée correspondante.
    Ce cas d’utilisation garantit l’authenticité des données et qu’elles n’ont pas été altérées.

Débugger une application NodeJs avec VSCode

Débug d’une application NodeJS exécutée dans une fenêtre VSCode

Aller dans Debug (icône avec un insecte), cliquer sur le triangle vert, puis sélectionner NodeJS. Mettre un point d’arrêt (Breakpoint). Une barre d’outil apparaît permettant de naviguer dans l’exécution pas à pas. C’est le mode de débug le plus simple (en dehor de console.log bien sûr).

Débug d’une application NodeJS initié depuis le navigateur en s’attachant au process

Il s’agit de s’attacher à un process avec VSCode. Clonez le projet :

https://github.com/gtsopour/nodejs-shopping-cart

Il vous faut faire un fichier de configuration launch.json:





Ensuite placez un breakpoint, et recharchez la page correspondante.

Débug en lançant le process

Lors qu clic sur le triangle vert, assurez vous d’être sur Launch Program.

Debug en remote (depuis la production par exemple)

Débugger quand c’est Typescript

https://code.visualstudio.com/docs/typescript/typescript-debugging

Utiliser le rollback dans MySQL au cas ou une requête échoue partiellement

Pourquoi effectuer un rollback en cas d’erreur SQL ?

Récemment j’ai eu une gêne lors de mon développement : j’avais une requête qui devait insérer dans deux tables des informations, mais la seconde requêtes échouait souvent car le code n’était pas bon. Il fallait à chaque fois que j’efface les données insérées dans la première table.

C’est alors qu’est venue l’idée de faire du transactionnel. C’est quoi une requête transactionnelle? C’est une requête que l’on peut inverser si quelque chose ne se passe pas comme prévu, cela s’appelle un ROLLBACK.

Par défaut dans MySQL, les requêtes ne sont pas transactionnelle, on ne peut pas revenir en arrière, car l’autocommit est activé. Un COMMIT c’est quoi? c’est le fait de valider une requête SQL.

Donc pour faire une requête transactionnelle, il faut désactiver l’AUTOCOMMIT le temps de la requête.

Les raisons de faire des requêtes transactionnelles

Lorsque l’on manipule une base de données relationnelle, les transactions sont essentielles pour garantir l’intégrité des données. Une transaction regroupe plusieurs opérations SQL qui doivent être exécutées comme une unité indivisible : soit toutes les opérations réussissent, soit aucune n’est appliquée. Si une erreur survient en cours d’exécution, effectuer un rollback (retour à l’état précédent) est crucial.

Prenons comme exemple la fonction importUsers présentée ci-dessus. Elle illustre un scénario classique où plusieurs opérations SQL sont imbriquées :

  1. Insertion de nouveaux utilisateurs dans la table utilisateurs : Cette première opération insère les données personnelles des utilisateurs (prénom, nom, email, etc.).
  2. Association des utilisateurs à leurs groupes dans la table utilisateur_groupe : Une fois les utilisateurs créés, ils sont liés à des groupes prédéfinis via une deuxième opération SQL.

Une transaction SQL encadre ces deux étapes afin qu’aucune donnée incohérente ne soit enregistrée si un problème survient. Le code pour le farie est le suivant :

function importUsers($pdo, $users)
{
    $insertedIds = [];

    $pdo->beginTransaction();

Les risques sans rollback

Si le rollback n’est pas correctement implémenté, plusieurs problèmes peuvent apparaître :

  • Incohérence des données : Supposons que la première opération (insertion dans utilisateurs) réussisse, mais que la seconde (liaison avec utilisateur_groupe) échoue. Les utilisateurs seront présents dans la base, mais sans lien avec leurs groupes, ce qui pourrait perturber les fonctionnalités de l’application.
  • Perte de confiance dans le système : Des erreurs de ce type peuvent miner la crédibilité du système aux yeux des utilisateurs ou de l’équipe technique.
  • Problèmes de maintenance : Identifier et corriger ces incohérences peut être complexe et coûteux en temps.

Le rôle de try-catch et du rollback

La fonction importUsers utilise un bloc try-catch pour lever une exception en cas de problème. En cas d’exception, le rollback annule toutes les opérations de la transaction et remet la base à son état initial. Pratique non? plus besoin d’effacer à la main les données !

try {
        // table utilisateur
        foreach ($users as $user) {
            //separate the items of csv
            $user = explode(',', $user);
            $sql = "INSERT INTO utilisateurs (firstname, lastname, email, password, role,groupe) 
        VALUES (:nom, :prenom, :email, :mdp,:role,:groupe)";
            $params = [
                'nom' => $user[0],
                'prenom' => $user[1],
                'email' => $user[2],
                'mdp' => 'mdp', // generate password
                'role' => STUDENT,
                'groupe' => $user[4],
            ];
            $stmt = $pdo->prepare($sql);
            $stmt->execute($params);
            $tmpArray = [
                'id' => $pdo->lastInsertId(),
                'groupe' => $user[4]
            ];
            $insertedIds[] = $tmpArray;
        }

Rappelons les étapes principale :

  • Début de la transaction : La méthode $pdo->beginTransaction() initialise une transaction.
  • Exécution des opérations : Les opérations SQL sont effectuées dans le cadre de cette transaction dans le try
  • Gestion des erreurs : Si une exception est levée, le rollback est exécuté via $pdo->rollBack().
  • Validation des modifications : Si toutes les opérations réussissent, la transaction est validée avec $pdo->commit().

Pas la peine d’utiliser un if car le catch fait office de if.

        // les groupes sont créés séparément
        // raccorder les utilisateur à table utilisateur groupe
        foreach ($insertedIds as $user) {
            $sql = "INSERT INTO utilisateur_groupe (utilisateur_id, groupe_id) VALUES (:utilisateur_id, :groupe_id)";
            $params = [
                'utilisateur_id' => $user['id'],
                'groupe_id' => $user['groupe'],
            ];
            $stmt = $pdo->prepare($sql);
            $stmt->execute($params);
        }
    } catch (Exception $e) {
        $pdo->rollBack();
        echo 'error : ' . $e . PHP_EOL;
        return false;
    }
    $pdo->commit();
    return true;
}

Regardez comment le COMMIT est placé en dehors du bloc catch, alors que le ROLLBACK est dans le bloc catch.

Avantages d’utiliser un rollback

  1. Intégrité des données : Les transactions assurent qu’aucune opération partiellement réussie n’impacte la base de données.
  2. Fiabilité : En annulant toutes les modifications en cas d’erreur, le système devient plus robuste.
  3. Facilité de maintenance :Au prix d’un complexité accrue, les développeurs sont moins embêtés.

Conclusion

L’utilisation des transactions SQL et du rollback est une pratique essentielle pour garantir la stabilité des applications manipulant des bases de données. La fonction ci dessus en est un excellent exemple. En regroupant les opérations critiques dans une transaction et en prévoyant un rollback en cas d’erreur, on s’assure que les données restent cohérentes et fiables, même si à la base c’est utilisé en production, je m’en sers surtout ici pour le développement ! Car heureusement en production il est rare que cela se produise.

Utiliser PHPMailer pour envoyer des emails avec un SMTP

Introduction à PHPMailer en PHP : Un Guide pour Envoyer des Emails en Toute Simplicité

Dans le développement web, l’envoi d’emails est une fonctionnalité souvent indispensable : confirmations d’inscription, réinitialisation de mot de passe, notifications, etc. PHP possède sa propre fonction native mail(), mais celle-ci reste assez limitée et complexe à configurer pour des envois avancés, en particulier avec des serveurs SMTP sécurisés. mail() est à proscrire aujourd’hui.

C’est là que PHPMailer entre en jeu. PHPMailer est une bibliothèque PHP populaire qui simplifie l’envoi d’emails et permet de se connecter facilement à des serveurs SMTP. Elle offre des fonctionnalités supplémentaires telles que l’envoi sécurisé via SSL/TLS, la gestion des pièces jointes, le formatage HTML des emails, et bien plus encore. Dans ce guide, nous vous montrerons comment intégrer PHPMailer dans vos projets PHP et vous guiderons pas à pas pour envoyer des emails en utilisant cette bibliothèque.

Pourquoi utiliser PHPMailer ?

PHPMailer présente plusieurs avantages par rapport à la fonction mail() :

  1. Simplicité d’utilisation : PHPMailer simplifie la configuration et l’envoi d’emails.
  2. Support SMTP avec Authentification : PHPMailer supporte les protocoles SMTP avec authentification, ce qui est plus sûr et essentiel pour éviter que les emails soient marqués comme spam.
  3. Support SSL et TLS : PHPMailer prend en charge les protocoles de sécurité SSL et TLS.
  4. Support HTML et Pièces Jointes : Ajoutez facilement des pièces jointes et formatez le contenu en HTML pour des emails professionnels.

C’est simple cela a toujours marché avec moi en SMTP, et quand il y a un problème d’envois, vous pouvez activer le mode DEBUG pour avoir des messages clairs.

Étape 1 : Installer PHPMailer avec Composer

La méthode la plus simple pour installer PHPMailer est d’utiliser Composer. Si vous n’avez pas encore installé Composer, vous pouvez le faire en suivant les instructions officielles sur le site de Composer.

Une fois Composer installé, ouvrez un terminal ou une invite de commande, placez-vous dans le répertoire de votre projet et exécutez la commande suivante :

composer require phpmailer/phpmailer

Composer téléchargera PHPMailer et l’ajoutera aux dépendances de votre projet.

Étape 2 : Envoyer un Email avec PHPMailer

Créons un script PHP simple pour envoyer un email en utilisant PHPMailer.

Configuration de base de PHPMailer

  1. Créez un fichier email.php dans votre projet.
  2. Ajoutez le code suivant pour configurer et envoyer un email :
<?php
// Charger automatiquement les dépendances de Composer
require 'vendor/autoload.php'; // ne pas oublier cette ligne très importante !

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

// Initialiser PHPMailer
$mail = new PHPMailer(true);

try {
    // Paramètres du serveur SMTP
    $mail->isSMTP();                                        // Utiliser SMTP
    $mail->Host = 'smtp.example.com';                       // Remplacez par le serveur SMTP
    $mail->SMTPAuth = true;                                 // Activer l'authentification SMTP
    $mail->Username = 'votre-email@example.com';            // Votre adresse email
    $mail->Password = 'votre-mot-de-passe';                 // Votre mot de passe
    $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;     // Activer TLS (ou 'ssl' pour SSL)
    $mail->Port = 587;                                      // Port SMTP, 587 pour TLS ou 465 pour SSL

    // Expéditeur et destinataire
    $mail->setFrom('votre-email@example.com', 'Nom de l\'Expéditeur');
    $mail->addAddress('destinataire@example.com', 'Nom du Destinataire');

    // Contenu de l'email
    $mail->isHTML(true);                                    // Activer HTML
    $mail->Subject = 'Sujet de l\'email';
    $mail->Body    = '<h1>Bonjour!</h1><p>Ceci est un email envoyé depuis PHPMailer.</p>';
    $mail->AltBody = 'Ceci est un email envoyé depuis PHPMailer.'; // Version texte brut

    // Envoyer l'email
    $mail->send();
    echo 'Email envoyé avec succès';
} catch (Exception $e) {
    echo "L'email n'a pas pu être envoyé. Erreur : {$mail->ErrorInfo}";
}
?>

Délivrabilité des emails

Il existe des services qui offrent des SMTP gratuits, mais la délivrabilité est aléatoire. Par exemple le SMTP de Mailjet n’envoit pas toujours des email, alors qu’un SMTP comme Gmail est beaucoup plus fiables. Mais vous ne pouvez pas modifier l’email de l’expéditeur, alors que Mailjet le permet.

Comment exposer votre site local au réseau local avec Laragon ou Apache

Quand vous développez un site en responsive, vous aurez envie de le consulter sur votre terminal mobile (smartphone). Sous certaines conditions, vous n’aurez pas besoin de le déployer sur Github pages ou sur la préproduction pour pouvoir le voir. En fait si vous avez le wifi et un réseau local (une box internet) c’est possible.

Quelle est l’adresse de mon site sur mon réseau local?

Sur l’ordinateur de développement, l’hôte local a pour adresse IP 127.0.0.1, depuis le mobile en accédant à cette adresse, vous allez sur l’hôte local de votre smartphone.

Vous accéderez à une adresse du type 192.168.0.150, qui est l’adresse que pourrait avoir votre ordinateur dans le réseau local.

Comment faire en sorte que mon ordinateur soit visible dans le réseau local?

C’est où les choses intéressantes commencent. Pour exposer votre ordinateur, il faut binder à l’adresse 0.0.0.0, dans dans le fichier httpd.conf, il y a une ligne à modifier.

Mais si vous n’avez pas envie de modifier cette ligne, il y a un autre moyen, c’est d’utiliser ViteJS. En effet en lançant la commande :

npm run dev -- --host

Vous aurez un affichage du style :

  VITE v4.5.5  ready in 802 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: http://192.168.1.151:5173/  <<< adresse à entrer dans votre smartphone
  ➜  Network: http://172.19.16.1:5173/
  ➜  Network: http://172.18.96.1:5173/
  ➜  press h to show help

Mais voici les options que vous pouvez avoir, si vous avez Laragon par exemple, et que vous voulez exposer manuellement sans l’aide de viteJS votre machine au réseau local.

Pour exposer votre site local au réseau local (LAN) avec Laragon ou Apache, le processus est similaire à celui d’autres serveurs locaux comme Node.js. Cependant, il y a quelques ajustements spécifiques à faire selon que vous utilisez le serveur intégré de Laragon ou une installation Apache autonome.

1. Exposer Laragon au LAN

Laragon est un environnement de développement populaire sous Windows, incluant Apache, MySQL, PHP, etc. Voici comment vous pouvez exposer un site Laragon à votre réseau local.

Étape 1 : Modifier la configuration Apache de Laragon

Par défaut, Laragon est configuré pour n’écouter que sur localhost. Vous devez modifier la configuration d’Apache pour qu’il écoute sur toutes les interfaces (0.0.0.0), permettant ainsi l’accès depuis le réseau local.

  • Ouvrez le fichier de configuration Apache dans Laragon :
    • Allez dans Menu > Apache > httpd.conf.
    • Recherchez la ligne suivante (généralement définie sur localhost) :apacheCopy codeListen 127.0.0.1:80
  • Changez 127.0.0.1 en 0.0.0.0 pour qu’Apache écoute sur toutes les interfaces :apacheCopy codeListen 0.0.0.0:80

Étape 2 : Permettre aux hôtes virtuels de fonctionner sur le LAN

Si vous utilisez des hôtes virtuels avec Laragon (par exemple, monsite.test), vous devez également modifier la configuration des hôtes virtuels :

  • Ouvrez votre fichier de configuration des hôtes virtuels (Menu > Apache > sites-enabled > 00-default.conf ou httpd-vhosts.conf).
  • Trouvez la directive VirtualHost, qui ressemble à ceci :apacheCopy code<VirtualHost 127.0.0.1:80>
  • Changez 127.0.0.1 en 0.0.0.0 :apacheCopy code<VirtualHost 0.0.0.0:80>

Étape 3 : Trouver votre adresse IP locale

Vous devez maintenant trouver l’adresse IP locale de votre machine. Sous Windows, exécutez la commande ipconfig. Sous Linux ou Mac, utilisez ifconfig ou ip a.

Une fois que vous avez votre adresse IP (par exemple, 192.168.1.100), les autres appareils du réseau local peuvent accéder à votre site hébergé sur Laragon en allant à l’adresse suivante :

arduinoCopy codehttp://192.168.1.100

Si vous utilisez un hôte virtuel comme monsite.test, vous y accéderez avec cette adresse :

arduinoCopy codehttp://192.168.1.100

Étape 4 : Configurer le pare-feu (si nécessaire)

Assurez-vous que votre pare-feu ne bloque pas l’accès au port (généralement 80 pour HTTP) :

  • Sous Windows :
    • Allez dans Panneau de configuration > Système et sécurité > Pare-feu Windows Defender > Autoriser une application ou une fonctionnalité via le pare-feu Windows Defender.
    • Ajoutez une règle pour Apache HTTP Server ou autorisez les connexions entrantes sur le port 80.

Étape 5 : Accéder depuis des appareils sur le LAN

Depuis n’importe quel appareil sur le même réseau local, accédez à votre site Laragon local en saisissant l’adresse IP de votre machine dans le navigateur, comme décrit ci-dessus.

2. Exposer un Apache autonome au LAN

Si vous utilisez une installation Apache autonome, le processus est similaire, mais implique la modification directe des fichiers de configuration Apache.

Étape 1 : Modifier la configuration Apache

  1. Ouvrez votre fichier de configuration Apache. Il est généralement situé à :
    • Sous Windows (XAMPP/WAMP) : C:\xampp\apache\conf\httpd.conf ou C:\wamp\bin\apache\apache2.x.x\conf\httpd.conf
    • Sous Linux/Mac : /etc/httpd/httpd.conf ou /etc/apache2/apache2.conf
  2. Recherchez la directive Listen, qui ressemble à ceci :apacheCopy codeListen 127.0.0.1:80
  3. Modifiez-la pour écouter sur toutes les interfaces (0.0.0.0) :apacheCopy codeListen 0.0.0.0:80

Étape 2 : Mettre à jour les hôtes virtuels (si applicable)

Si vous utilisez des hôtes virtuels, ouvrez votre fichier d’hôtes virtuels (par exemple, httpd-vhosts.conf) et changez l’hôte virtuel de 127.0.0.1 à 0.0.0.0 :

apacheCopy code<VirtualHost 0.0.0.0:80>
  DocumentRoot "C:/xampp/htdocs/monsite"
  ServerName monsite.local
</VirtualHost>

Cela garantit que les hôtes virtuels sont accessibles sur le réseau local.

Étape 3 : Configuration du pare-feu

Assurez-vous que votre pare-feu permet les connexions entrantes sur le port 80 :

  • Sous Linux (avec ufw) :bashCopy codesudo ufw allow 80/tcp
  • Sous Windows : Suivez les étapes mentionnées plus haut pour autoriser le trafic via le port 80.

Étape 4 : Accéder depuis des appareils sur le LAN

Une fois votre serveur Apache configuré pour écouter sur 0.0.0.0, vous pouvez y accéder depuis d’autres appareils sur le LAN en utilisant l’adresse IP locale de votre machine :

arduinoCopy codehttp://192.168.1.x

Si vous avez configuré un hôte virtuel, cela fonctionnera de manière similaire.


Résumé

  • Pour Laragon : Modifiez Listen 127.0.0.1:80 en Listen 0.0.0.0:80 dans le fichier httpd.conf ainsi que dans les fichiers des hôtes virtuels si nécessaire.
  • Pour Apache autonome : Suivez un processus similaire en modifiant la configuration pour qu’Apache écoute sur 0.0.0.0 et mettez à jour les hôtes virtuels.
  • Après avoir configuré votre serveur, accédez au site via l’adresse IP locale de votre machine (192.168.x.x) depuis d’autres appareils sur le réseau local.

De cette manière, vous pouvez exposer votre environnement de développement local, que ce soit avec Laragon ou Apache, à votre réseau local (LAN) !

Comment connaitre l’IP exposé sous Windows?

Dans le terminal DOS, faites la commande ipconfig, et vous aurez une sortie de ce genre:

C:\Users\admin>ipconfig

Configuration IP de Windows


Carte Ethernet Ethernet 2 :

   Suffixe DNS propre à la connexion. . . : lan
   Adresse IPv4. . . . . . . . . . . . . .: 192.168.1.151
   Masque de sous-réseau. . . . . . . . . : 255.255.255.0
   Passerelle par défaut. . . . . . . . . : 192.168.1.254

Carte inconnue Connexion au réseau local :

   Statut du média. . . . . . . . . . . . : Média déconnecté
   Suffixe DNS propre à la connexion. . . :

Carte Ethernet vEthernet (Default Switch) :

   Suffixe DNS propre à la connexion. . . :
   Adresse IPv6 de liaison locale. . . . .: fe80::e9be:7f6:cc88:dfa2%34
   Adresse IPv4. . . . . . . . . . . . . .: 172.19.16.1
   Masque de sous-réseau. . . . . . . . . : 255.255.240.0
   Passerelle par défaut. . . . . . . . . :

Carte Ethernet vEthernet (WSL) :

   Suffixe DNS propre à la connexion. . . :
   Adresse IPv6 de liaison locale. . . . .: fe80::aa45:c533:e7ff:dde4%49
   Adresse IPv4. . . . . . . . . . . . . .: 172.18.96.1
   Masque de sous-réseau. . . . . . . . . : 255.255.240.0
   Passerelle par défaut. . . . . . . . . :

C:\Users\admin>

Installer une application NodeJs sur O2Switch

Mettre en place le projet NodeJS dans l’insterface O2Switch (CPanel)

Aller depuis l apage d’accueil de Cpanel, vers la section « Logiciels », choisir Setup Node.js App

Dans la page de configuration de votre application NodeJS, cliquer sur « Create Application »

Remplir le champs version de NodeJS, le mode de déploiement (Productionà, le chemin absolu vers la racine, le startup file corresond au fichier d’entrée de votre application, celui auquel vous faite node nom_fichier.js.

Une fois la configuration validée, vous verrez cet écran.

Comment organiser votre projet Node/Expresse/React Typescript?

La difficulté est que dans mon cas j’avais deux répertoires, frontend et backend. Imaginons que notre nom de domaine soit cloneflickr.jedeploiemonappli.com, la page d’accueil doit correspondre à la route ‘/’ dans Express. Cependant on a aussi une application ReactJS

fff

Connectez vous à Mysql pour un projet NodeJS Typescript

Si vous avez lu le premier article concernant le bootstrapping d’un projet Node Express Typescript, je n’ai pas encore parlé de la connection de la dernière pièce qui est la base de données. Dans cet article je vais parler de la connexion à une base MySQL.

Installation des composants mysql2

npm install mysql2

npm install typeorm reflect-metadata

Ici on installe un ORM, mais on ne va pas forcément l’utiliser.

Connexion à la base de données

Démarrer un projet ReactJS typescript avec ViteJS

Installation de viteJS avec npm

D’abord installons ViteJS

npm create vite@4.1.0

// alternative pour installer la dernière version

npm create vite@latest

// répondez aux questions
? Project name: » nom_projet

>   Vanilla
    Vue
    React
    Preact
    Lit
    Svelte
    Others


// on sélectionne React puis on a l'écran suivant
>   JavaScript
    TypeScript
    JavaScript + SWC
    TypeScript + SWC

Sélectionnons Typescript simple


√ Project name: ... cloneflicker
√ Select a framework: » React
√ Select a variant: » TypeScript

Scaffolding project in E:\projet\frontend\cloneflicker...

Done. Now run:

  cd cloneflicker
  npm install    
  npm run dev 

A la fin de la configuration de votre projet, un petit rappel de ses caractéristiques et une suggestion de commandes.

Au lancement on a l’écran suivant :

  VITE v4.5.5  ready in 13692 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h to show help

Vou spouvez CTRL + Click sur le lien pour lancer le site

Une des choses que vous allez remarquer le plus est la vitesse de lancement. C’est plus rapide qu’avec Webpack, la raison est que Vite optimise le build et ne construit pas tout, comme Webpack.

Structure du projet React Typescript

Le dossier public contient la page index.html qui va servir d’hôte à toute votre application React. Tandis que le dossier src contient tous les fichier de votre application React.

Le fichier tsconfig.json sert à configurer Typescript, le fichier vite.config.ts sert à introduire Vite,et n’oublions pas le fichier package.json.

Création de notre premier composant React en typescript !

Créons un fichier Message.tsx

function Message() {
    return <h1>Hello World</h1>;
}

export default Message;

Maintenant incorporons le dans App.tsx, effaçons le contenu par défaut de App.tsx pour simplifier notre construction.

import Message from './Message';

function App() {
  return <div>
    <Message />
  </div>
}

export default App

Au fur et à mesure que vous mettez à jour vos fichier, regardez la console, il y a le message

22:53:30 [vite] hmr update /src/App.tsx (x2)

Votre page s’est rafraichie.

Installation de Tailwind CSS

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

ça va générer deux fichiers de configuration tailwind.config.js et postcss.config.js. Remarquez que tailwindcss est une devDependency !

/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

Rajouter dans index.css ces lignes :

@tailwind base;
@tailwind components;
@tailwind utilities;

Modifier la class pour h1

function Message() {
    const name = 'Yvon';
    return <h1 className="text-4xl font-bold underline">Hello {name}</h1>;
}

export default Message;

Lancer l’application accessible via votre mobile

> npm run dev -- --host



  VITE v4.5.5  ready in 830 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: http://172.22.80.1:5173/
  ➜  Network: http://172.18.96.1:5173/
  ➜  Network: http://192.168.1.151:5173/
  ➜  press h to show help

Pour pouvoir utilise vite --host, il faut l’installer globalement.

Changer le répertoire de build pour vite

Par défaut vite build dans le répertoire dist. Ce répertoire sert pour la production, en phase de développement, il n’est pas généré. Pour avoir le build dans le répertoire build, il faut modifier vite.config.js

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  build: {
    outDir: 'build', // Spécifie ici le répertoire de sortie
  },
})

L’attribut build n’apparait dans le fichier originel.

Requêter Sqlite en ligne de commande

Pour utiliser Sqlite avec une base de données, vous avez 2 possibilités :

Vous êtes déjà dans Sqlite, dans ce cas ouvrez le fichier de base de donnée, par exemple le fichier s’appelle bdd.db, pou ouvrir un fichier il faut indiquer le chemin complet. Dans l’exemple ci-dessous, on a déjà ouvert Sqlite dans le répertoire du fichier bdd.db.

sqlite> .open bdd.db

Vous n’êtes pas encore dans Sqlite:

sqlite bdd.db

Montrer les commandes

sqlite> .help
.archive ...             Manage SQL archives
.auth ON|OFF             Show authorizer callbacks
.backup ?DB? FILE        Backup DB (default "main") to FILE
.bail on|off             Stop after hitting an error.  Default OFF
.binary on|off           Turn binary output on or off.  Default OFF
.cd DIRECTORY            Change the working directory to DIRECTORY
.changes on|off          Show number of rows changed by SQL
.check GLOB              Fail if output since .testcase does not match
.clone NEWDB             Clone data into NEWDB from the existing database
.databases               List names and files of attached databases
.dbconfig ?op? ?val?     List or change sqlite3_db_config() options
.dbinfo ?DB?             Show status information about the database
.dump ?TABLE? ...        Render all database content as SQL
.echo on|off             Turn command echo on or off
.eqp on|off|full         Enable or disable automatic EXPLAIN QUERY PLAN
.excel                   Display the output of next command in a spreadsheet
.exit ?CODE?             Exit this program with return-code CODE
.expert                  EXPERIMENTAL. Suggest indexes for specified queries
.fullschema ?--indent?   Show schema and the content of sqlite_stat tables
.headers on|off          Turn display of headers on or off
.help ?-all? ?PATTERN?   Show help text for PATTERN
.import FILE TABLE       Import data from FILE into TABLE
.imposter INDEX TABLE    Create imposter table TABLE on index INDEX
.indexes ?TABLE?         Show names of indexes
.limit ?LIMIT? ?VAL?     Display or change the value of an SQLITE_LIMIT
.lint OPTIONS            Report potential schema issues.
.load FILE ?ENTRY?       Load an extension library
.log FILE|off            Turn logging on or off.  FILE can be stderr/stdout
.mode MODE ?TABLE?       Set output mode
.nullvalue STRING        Use STRING in place of NULL values
.once (-e|-x|FILE)       Output for the next SQL command only to FILE
.open ?OPTIONS? ?FILE?   Close existing database and reopen FILE
.output ?FILE?           Send output to FILE or stdout if FILE is omitted
.print STRING...         Print literal STRING
.prompt MAIN CONTINUE    Replace the standard prompts
.quit                    Exit this program
.read FILE               Read input from FILE
.restore ?DB? FILE       Restore content of DB (default "main") from FILE
.save FILE               Write in-memory database into FILE
.scanstats on|off        Turn sqlite3_stmt_scanstatus() metrics on or off
.schema ?PATTERN?        Show the CREATE statements matching PATTERN
.selftest ?OPTIONS?      Run tests defined in the SELFTEST table
.separator COL ?ROW?     Change the column and row separators
.sha3sum ...             Compute a SHA3 hash of database content
.shell CMD ARGS...       Run CMD ARGS... in a system shell
.show                    Show the current values for various settings
.stats ?on|off?          Show stats or turn stats on or off
.system CMD ARGS...      Run CMD ARGS... in a system shell
.tables ?TABLE?          List names of tables matching LIKE pattern TABLE
.testcase NAME           Begin redirecting output to 'testcase-out.txt'
.timeout MS              Try opening locked tables for MS milliseconds
.timer on|off            Turn SQL timer on or off
.trace FILE|off          Output each SQL statement as it is run
.vfsinfo ?AUX?           Information about the top-level VFS
.vfslist                 List all available VFSes
.vfsname ?AUX?           Print the name of the VFS stack
.width NUM1 NUM2 ...     Set column widths for "column" mode

Toutes les commandes commencent par un point.

sqlite>.databases
main: /home/scxxccc70/easyupload.jedeploiemonappli.com/bdd.db

sqlite>.tables


sqlite>.schemapiece_jointe
CREATE TABLE IF NOT EXISTS "piece_jointe" (   
        "id"    INTEGER NOT NULL UNIQUE,      
        "email_emmeteur"        TEXT NOT NULL,
        "email_destinataire"    TEXT NOT NULL,
        "date_creation" INTEGER NOT NULL,     
        "chemin"        TEXT NOT NULL,        
        PRIMARY KEY("id" AUTOINCREMENT)       
);

Je me suis intéressé à ça pour comparer les schémas des tables en local et en production avant de déployer EasyUpload, un clone de WeTransfer développé à 95% par mes étudiants.

Démarrer une application Node/Express Typescript

Bootstrapping du projet Node Express

Avec NodeJS, il existe plusieurs façon de faire une application web

#créer le répertoire de projet
mkdir nodeexpressts
# initaliser le projet en répondant oui à tout
npm init -y

npm init -y    // dit oui à tout
{
  "name": "nodeexpressts",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
  // Ensuite créer un fichier index.js pour commencer
touch index.js
ouvrir VSCode dans ce répertoire projet
code .

On va installer Express

npm install express

On va code le fichier index.js

//index.js
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;

app.get('/', (req, res) => {
    res.send('Hello World!');
});

app.listen(port, () => {
    console.log(`Example app listening on port ${port}!`);
});

Passons à Typescript !

Installation de Typescript

npm install typescript

Ensuite renommons le fichier index.js en index.ts !

En survolant les variable maintenant vous allez voir que pas mal de variable sont de type any, ça prouve qu’on est passé nen mode Typescript.

Installaton des fichier de type pour nodeJS et Express

npm install @types/express @types/node

modification des façons d’importer

Dans Typescript on n’utilise pas require mais import

import express, { Express, Request, Response } from 'express';
const app: Express = express();
const port = process.env.PORT || 3000;

app.get('/', (req: Request, res: Response) => {
    res.send('Hello World! in Typescript Now'); // modifié
});

app.listen(port, () => {
    console.log(`Example app listening on port ${port}!`);
});

A ce stade en survolant la varaible express, on voit que c’est typé.

Illustration, vous ne pouvez pas mettre n’importe quoi avec la notation pointée, si l’objet ne possède pas la méthode, vous aurez une erreur.

app.toto  << erreur souligné en rouge dans VSCode

Pour aller plus loin dans le typage et aider Typescript à savoir de quel type est une varaible on postfix avec les deux point

app.get('/', (req : Request, res : Response) => {
    res.send('Hello World!');
});

démarrage du serveur sour Typescript

Nous devons transpiler le Typescript en Javascript avant de démarrer le serveur. Invoquons tsc

npx tsc --init    // npx si on n'a pas tsc en global

Created a new tsconfig.json with:                                                                                       
                                                                                                                     TS 
  target: es2016
  module: commonjs
  strict: true
  esModuleInterop: true
  skipLibCheck: true
  forceConsistentCasingInFileNames: true


You can learn more at https://aka.ms/tsconfig

Un fichier tsconfig.json est apparu, pour configurer la manière dont Typescript sera transpilé par exemple. Nous devons indiqué où se trouvent les fichier Js de sortie, ouvrons ce fichier

"outDir": "./dist"

Pour transpiler taper simplement npx tsc ou tsc (si on a installé en global, vérifiez le)

tsc
un répertoire dist est apparu, regarez le contenu ce sont des fichiers JS.
Maintenant pour démarrer nodeJS

node dist/index.js

Faisons comme les pro, commetn avoir npm build par exemple

Ouvron spackage.json et configurons l’objet scripts:

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "tsc",
    "start": "node dist/index.js"
  },

Modifions le script

app.get('/', (req: Request, res: Response) => {
    res.send('Hello World! in Typescript Now WITH NPM RUN');
});

Et en ligne de commande faisons

npm run build
> nodeexpressts@1.0.0 build
> tsc
//puis
npm run start

Installons Nodemon ! (en devDependencies)

npm install -D nodemon

Maintenant ça va redémarrer à chaque fois qu’on va modifier un fichier.

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "tsc -w & nodemon dist/index.js"
  },

npm run dev

Le & simple va exécuter en background

Il se peut que ça ne marche pas, en effet le & tout seul n’est pas une commande Windows mais Linux. Remplacer par :

"dev": "tsc -w && nodemon dist/index.js

il est probable que ça ne marche pas non plus, car tsc -w (w comme watch) peut ne pas rendre la main à nodemon. Dans ce cas il faut installer concurrently

npm install concurrently --save-dev
    "dev": "concurrently \"tsc -w\" \"nodemon dist/index.js\""

C’est un peu lourd comme syntaxe mais ça devrait résoudre vos problèmes.

Nettoyer le dossier dist avec rimraf

Si on veut s’assurer que le dossier soit entièrement nettoyer, on va installer rimraf (comme rm -rf !)

npm i -D rimraf


puis on modifie package.json

    "build": "rimraf dist &&tsc",

Mettez un fichier dans dist puis exécutez npm run build, le fichier va disparaître. Modifions le script pour ajouter un prestart et preserve, (et renommer dev en serve), qui vont simplement nettoyer le répertoire dist.

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "rimraf dist &&tsc",
    "prestart": "npm run build",
    "start": "node dist/index.js",
    "preserve": "npm run build",
    "serve": "concurrently \"tsc -w\" \"nodemon dist/index.js\""
  },

En lançant npm run serve, c’est preserve qui est lancé en premier.

Ce tuto est inspiré de cette vidéo Youtube

Comment utiliser le SMTP de GMAIL

Pourquoi utiliser le SMTP de Gmail?

Pour quel type d’envoi Gmail est-il adapté?

Comment s’authentifier avec l’email de Gmail

La manipulation aujourd’hui (elle a évolué au cours du temps) est la suivante, il faut avoir une clé d’accès qui remplace le mot de passe de votre compte Gmail.

Créer une clé d’accès

Allez dans les paramètre de votre compte Google

Puis allez dans la sécurité

Pour plus d’information voici le lien vers l’aide de google pour générer le mot de passe d’application

La page de création de mot de passe d’application est sur cette page, personnellement en naviguant dans le rofil de google, je n’ai jamais réussi à retrouver cette page, aussi ce lien est précieux ! Si vous y allez et que vous avez cette page d’erreur :

C’est que vous n’avez pas activé le 2FA. Faites le

Puis revenez sur cette page, vous devriez avoir cet écran

Voilà ! Personnellement j’utilise cette méthode pour me générer de quoi utiliser dans les applications web le SMTP de Gmail, qui a une délivrabilité tès fiable en transactionnel.

Faire des requêtes réseau dans le terminal

Je me suis intéressé récemment à faire des requêtes de al façon la plus basique possible. Loin des librairies qui vous masquent ce qui se passe sous le capot, on va voir les technique dans le shell qui permettent de faire des requêtes réseau, je suis sûr que je n’aurais pas la technique la plus bas niveau mais on va s’en rapprocher.

CURL

Ce programme est mondialement connu, il est présent partout, dans toutes les plateformes. Il est présent dans tous les langages. Par exemple la librairie curl en PHP permet de faire tout type de requête réseau. Voyons comment en shell nous pouvons faire une requête basique

curl https://yvonh.com/ -o yvonh.html
cat yvonh.html  //lire le fichier généré qui est vide

L’argument -o va rediriger l’output dans un fichier texte avec extension HTML.

Dans ce cas précis vous n’allez rien voir, car il y a une redirection vers www.yvonh.com, or par défaut Curl ne suit pas la redirection. Il va retourner un résultat vide. La requête suivante va instruire Curl de suivre les redirections.

curl -L https://yvonh.com -o yvonh.html

On en va pas voir en détail toutes les commandes de Curl, on va voir les autres méthode de requêtage réseau

wget

wget est une commande très souvent utilisée

wget -qO- https://www.example.com

openssl

openssl s_client -connect www.example.com:80

Il y a aussi d’autres commandes mais qui ne sont pas toutes présentes dans Linux

nc, /dec/tcp, telnet, /dev/udp

git versioning

Comment cloner sans mot de passe un Repo Github

Support for password authentication was removed on August 13, 2021

Depuis cette date, il est impossible de cloner un repository privé, car pour cloner un repository privé il faut s’authentifier. Pour remplacer le mot de passe, Github utilise le PAT : Personal Access Token.

Comment créer le PAT avec Github

Pour créer le PAT, allez dans Settings sur la page d’accueil de votre Github, puis sur la barre latérale gauche, cliquer sur Developer Settings, puis sur Personal Access tokens, puis dans la liste choisissez Tokens (Classic)

Cliquez sur Generate new token dans la liste déroulante et choisissez classic.

Pour les opération usuelles, cliquez la checkbox repo, et choisissez une durée de validité, 30 jours par défaut. Une fois généré, copiez la chaine PAT en lieu sûr; car vous ne le verrez plus dans votre Github. (Evitez de le mettre dans votre repository !!!)

Comment utiliser le PAT pour cloner?

Le PAT remplace en lieu et place votre mot de passe, il est plus long et plus secret, et a une durée de vie déterminée, contrairement à votre mot de passe, il est donc plus sûr.

Donc suite à la commande git clone https://…

Username for 'https://github.com': votre_nick
Password for 'https://votre_nick@github.com

Au moment d’entrer le password, vous collez simplement votre PAT (Personal Access Token)

Ou alors si vous renseignez votre PAT ça va plus vite:

git clone https://<PAT>@github.com/<nick>/<repo>.git

Le PAT vous permet aussi d’utiliser le webservice de Github.

Se connecter à un droplet DigitalOcean avec Putty

Vous devez avoir fait la création d’une paire de clé privée-public. Ensuite lors de la création de votre droplet, vous aurez uploadé la clé publique sur digitalOcean.

Nous allons faire la connexion avec Putty

D’abord localisez l’adresse IP de votre Droplet, ouvrez Putty et mettez l’adresse IP. Puis allez dans Auth, puis ajoutez votre clé privée au format .ppk Le soucis c’est qu’avec ssh-keygen, vous ne générez pas de .ppk, il vous faudra l’utilitaire Puttygen.

Création de la clé privée au format .ppk

Dans Puttygen, chargez votre clé privée et sauvez la.

Importez la clé

Sélectionnez votre clé privée (la clé sans extension)

Chargez la clé

Vous pouvez sauver votre clé sans passphrase (c’est plus simple)

Retour dans Putty

Ajout de l’utilisateur, aller dans Connection > data

Sauvegarder les données de la session pour ne pas avoir à les retaper

Dans le cas où cela ne marcherait pas essayez de télécharger la dernière version de Putty, j’étais pour ma part en 0.72 et en mettant à jour vers la 0.81 ça marche.

DigitalOcean recouvrez l’accès à votre console de droplet

Si vous vouslez accéder à votre console et que l’écran tourne en boucle, c’est que sans doute vous avez touché au FireWall. Vous avez touché au FireWall à l’occasion de l’installation de Nginx.

Pour recouvrer l’accès à votre console, il faut utiliser la Recovery Console, pour y accéder, aller dans Access dans la page principale du droplet.

Après le démarrage de la Recovery Console, il vous sera demandé de rentrer le mot de passe du user root, le problème est que vous n’avez pas de mot de passe root si vous avez choisi d’accéder pas clé SSH.

Il vous faudra donc resetter votre mot de passe root, en choisissant l’option juste après la Recovery Console, Reset Root Password.

Vous voila prêt à changer la configuration du Firewall une fois loggé

ufw allow ssh

Installer Nginx sur Ubuntu sur DigitalOcean

Une fois que vous avez créé votre droplet, il est conseillé de créer un utilisateur sudoable, sinon faites les opérations suivantes avec l’utilisateur root.

Nous allons mettre à jour vos paquets et installer Nginx

sudo apt update
sudo apt install nginx

Si vous êtes sous root pas la peine de mettre sudo. Sudo permet d’élever les privilèges d’un utilisateur non root.

Paramétrage du firewall ufw (uncomplicated Fire Wall)

sudo ufw app list   // liste les applications connue par ufw

Available applications:
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
  OpenSSH

Si on n’a pas encore configuré un SSL pour le serveur, on va utiliser le profil Nginx HTTP.

>ufw allow 'Nginx HTTP'

Afficher le status du firewall

>ufw status

Status: inactive    // si vous avez ça c'est que ufw n'est pas activé

faite la commande suivante:
>ufw enable

>ufw status

Status: active

To                         Action      From
--                         ------      ----
Nginx HTTP                 ALLOW       Anywhere                  
Nginx HTTP (v6)            ALLOW       Anywhere (v6)      

Vérifier que le serveur web tourne

A la fin de l’installation, Unbuntu démarre le serveur Nginx. Nous allons vérifier avec la commande

systemctl status nginx

● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Sat 2024-07-27 21:06:47 UTC; 16min ago
       Docs: man:nginx(8)
    Process: 2697 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUC>
    Process: 2699 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 2701 (nginx)
      Tasks: 2 (limit: 509)
     Memory: 2.2M (peak: 2.3M)
        CPU: 16ms
     CGroup: /system.slice/nginx.service
             ├─2701 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             └─2702 "nginx: worker process"

Jul 27 21:06:47 ubuntu-s-1vcpu-512mb-10gb-ams3-01 systemd[1]: Starting nginx.service - A high performance web se>
Jul 27 21:06:47 ubuntu-s-1vcpu-512mb-10gb-ams3-01 systemd[1]: Started nginx.service - A high performance web ser>
lines 1-16/16 (END)

troisième ligne indique active running.

Important ! autorisez le SSH, sinon vous risquez de ne plus avoir accès à la console, si c’est le cas voici une solution

ufw allow ssh

Nous allons visiter dans le navigateur une page pour vérifier que Nginx fonctionne. Faite la commande pour connaitre l’IP qui doit vous afficher une page:

ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'

165.232.90.185
10.18.0.5
fe80::28e7:4eff:fee5:b7fe

//commande alternative
curl -4 icanhazip.com

Je passe par l’IP V4, et j’obtiens bien une page web, la page d’accueil de Nginx.

f

Mémento sur les github actions

Concept essentiels de github actions

  • Workflow : un process fait d’un ou plusieur jobs. Le workflow est un fichier YAML
  • job : un ensemble de steps exécuté sur un même runner, tournent en parallèle en général et spécifie l’OS
  • step : une tâche dans un job, peut être une action ou une commande shell., share filesystem
  • runner : le serveur qui exécute le workflow

Qu’est ce qu’un action?

Une action est un ensemble de code au sein d’un workflow. En savoir plus sur les actions (EN)

Dans le contexte de GitHub Actions, une action est une application personnalisée pour la plateforme GitHub Actions qui automatise une tâche spécifique ou un ensemble de tâches au sein d’un workflow. Les actions sont les éléments de base des workflows et peuvent être réutilisées dans plusieurs workflows et dépôts.

Points Clés sur les Actions

  1. Unités de Code Réutilisables : Les actions sont conçues pour être réutilisables. Vous pouvez les partager entre différents dépôts et avec la communauté GitHub.
  2. Préconstruites ou Personnalisées : Vous pouvez utiliser des actions préconstruites disponibles sur le GitHub Marketplace ou créer vos propres actions personnalisées adaptées à vos besoins spécifiques.
  3. Encapsulent des Tâches : Les actions encapsulent des tâches telles que le checkout du code, la configuration des environnements, l’exécution de tests et le déploiement des applications.
  4. Configurables : Les actions acceptent souvent des entrées et produisent des sorties, ce qui permet de les personnaliser et de les composer ensemble pour créer des workflows complexes.

Types d’Actions

  1. Actions JavaScript : Actions construites en utilisant Node.js qui peuvent exécuter du code JavaScript.
  2. Actions de Conteneur Docker : Actions qui s’exécutent dans un conteneur Docker, ce qui vous permet de les empaqueter avec toutes leurs dépendances.
  3. Actions Composites : Actions qui combinent plusieurs étapes en une seule action, définie en utilisant une série de commandes shell et d’autres actions.

Qu’est ce que actions/checkout@v3?

C’est une action prédéfinie de Github, qui clone un repository dans l’environnement du workflow.

Glossaire Github action:

logs

uses : est utlisé pour sélectionner une action déjà définie dans un docker, dans un repository.

name: le nom du workflow

on : écouteur d’événement

artifact

runs-on: spécifie le type de machine (ou « runner ») sur laquelle un job doit s’exécuter. Un runner est un serveur qui exécute les workflows définis dans vos fichiers YAML. Un runner peut être auto-hébergé.

Types de Runners Disponibles

GitHub Actions offre plusieurs types de runners hébergés (préconfigurés par GitHub) que vous pouvez utiliser :

  • ubuntu-latest : Une machine virtuelle exécutant la dernière version stable d’Ubuntu.
  • ubuntu-20.04 : Une machine virtuelle exécutant Ubuntu 20.04.
  • ubuntu-22.04 : Une machine virtuelle exécutant Ubuntu 22.04.
  • windows-latest : Une machine virtuelle exécutant la dernière version stable de Windows.
  • windows-2019 : Une machine virtuelle exécutant Windows Server 2019.
  • macos-latest : Une machine virtuelle exécutant la dernière version stable de macOS.
  • macos-11 : Une machine virtuelle exécutant macOS 11 (Big Sur).

steps:

Liens externes:

https://www.digitalocean.com/community/tech-talks/deploying-to-digitalocean-with-github-actions

Rsync pour synchroniser des répertoire avec une machine distante

Rsync permet de synchroniser des dossiers, où qu’ils soient pourvu que deux machine aient accès via des clés SSH.

Par exemple soient les machines Msource et Mtarget. Msource doit générer une clé SSH et mettre la clé publique dans Mtarget. Ainsi Msource pourra se connecter sans mot de passe à Mtarget.

Bien que su rLinux Rsync soit disponible par défaut, sur Window c’est assez compliqué de l’installer.

Nous partirons du postulat qu’on est sous Linux et que Rsync est présent.

Synchronisation d’un fichier avec Rsync

rsync -a monfichier.txt login_mtarget@mtarget.com:/home/public_html

Synchronisation d’un dossier avec Rsync

rsync -a repertoire1 login_mtarget@mtarget.com:/home/public_html

Tous les fichier du repertoire1 seront copié dans le répertoire /home/public_html de la machine cible Mtarget.

Uploader en ajax un fichier avec barre de progression

Vous savez sans doute uploader via un formulaire une image vers un serveur, et aussi via ajax (c’est un peu plus compliqué). Mais il est possible d’uploader en Ajax progressivement par morceau (chunk en anglais).

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>BLOB upload</title>
</head>

<body>
    <form action="#">
        <input type="file" value="" id="file" name="file"><br>
        <input type="submit" id="uploadBtn" value="UPLOAD">
    </form>
</body>
<script>
...
</script>
</html>

Grâce à l’attribut name on peut faire référence au fichier. Dans la balise script on a le code suivant :

<script>
    const uploadBtn = document.querySelector('#uploadBtn')
    const url = 'http://localhost.test/blobupload.php'

    uploadBtn.addEventListener('click', function (e) {
        e.preventDefault()
        const file = document.querySelector('#file').files[0]
        uploadBlob(file)

    })


</script>

La fonction uploadBlob() va prendre le fichier qui est passé en argument, puis le décomposer dans une boucle en plusieurs chunk de taille fixe. Notre boucle se termine quand on a atteint la fin du fichier.

<script>
    const uploadBtn = document.querySelector('#uploadBtn')
    const url = 'http://localhost.test/blobupload.php'

    uploadBtn.addEventListener('click', function (e) {
        e.preventDefault()
        const file = document.querySelector('#file').files[0]
        uploadBlob(file)

    })
  
  function uploadBlob(file) {
        
        const chunkSize = 100_000;
      
        let start = 0

        for (let start = 0; start < file.size; start += chunkSize) {

            const chunk = file.slice(start, start + chunkSize);
            const fd = new FormData();
            fd.set('file', chunk,);


            fetch(url, {
                method: "POST",
                body: chunk
            })
                .then(response => alert('Blob Uploaded'))
                .catch(err => alert(err));
        }
    }
</script>

Note boucle for va saucissonner le fichier en portions égales (à l’exception du dernier tronçon) à chaque itération. Ensuite ce morceau sera uploadé.

Côté backend pour la réception des fichiers en PHP

<?php
//  blobupload.php
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
$result = file_get_contents('php://input');
$written = file_put_contents('cpu.png', $result, FILE_APPEND);
echo $written;

C’est plutôt simple côté backend, le code est synchrone. Il retourne la quantité de bytes écrits ( 1 byte = 1 octet)

Essai d’upload par paquet en javascript

Nous allons prendre un fichier qui fait plus de 100000 octets pour déclencher la découpe. Pour l’exemple j’ai une image de 811 Ko nommée cpu4.png. Cela fait combien de paquets (chunks) à envoyer? Simple division 811000 / 100000 = 9. Le dernier paquet fait moins de 100000 octets ou bytes.

Image que nous allons envoyer.

On ouvre les devtools, et effectivement j’ai 9 envois.

Si vous essyez d’ouvrir l’image cependant, vous ne pourrez pas la visualiser. En fait l’image uploadée est différente de l’image choisie, vous n’avez qu’à comparer leurs checksum sur un outil comme celui-ci.

Mais comment est ce possible? Regardez l’image ci-dessus, le fichier php renvoit la taille écrite en guise de réponse, et l’avant dernier paquet montre 29669 octets, alors que ce devait être le dernier paquet qui doit montrer ce nombre ! Bref les paquets sont envoyés dans le désordre !! Nous allons corriger cela simplement avec async/await, qui va rendre un peu de synchronisme dans la boucle for.

Async/await à la rescousse.

Nous allons modifier le code de la façon suivante : uploadBlob sera async et on « awaitera » fetch.

    async function uploadBlob(file) {
        //reader = new FileReader() // le file reader va lire à partir d'une position donnée
        const chunkSize = 100_000;
        // const filename = file.name;
        let start = 0
        let i = 0

        for (let start = 0; start < file.size; start += chunkSize) {
            console.log("morceau ", i, "uploadé")
            i++
            let end = start + chunkSize
            const chunk = file.slice(start, end);
            const fd = new FormData();
            fd.set('file', chunk,);


            await fetch(url, {
                method: "POST",
                body: chunk
            })
                .then(response => response.text())

        }
    }

Maintenant le dernier paquet est bien le plus petit. Tous les paquets sont envoyés dans l’ordre. Nous pouvons vérifier que l’image s’ouvre bien. Vérifiez les checksum de ces deux fichiers, ils sont pareil.

Il est à noter que dans le cas des envois en désordre, la taille du fichier reçu est variable !

Utiliser le webservice de Github pour extraire des informations

Récemment, pour un projet open source, j’ai voulu afficher un classement des contributeur en fonction du nombre de commits. J’ai tout de suite eu l’idée de passer par le webservice de Github (alors que je n’en ai jamais entendu parler).

Il est normal que Github ait un webservice, cela permet de faire des nitégration un peut partout (plugin git par exemple).

Comment mener sa réflexion du webservice à l’affichage des badges pour récompenser les contributeurs?

L’idée de base est de proposer un classement des contributeurs en fonction du nombre de pull requests. Pour avoir les pull request, il faut avoir les commits sur la branch principale (dans notre cas develop).

Il a fallut d’abord trouver un moyen via la documentation de lister les commits d’une branche. Deplus j’ai pris le parti d’utiliser un token pour avoir moins de restriction sur les appels.

Exemple d’appel sans avoir besoin de clé:

https://api.github.com/user/repos

Appel avec un clé

Il faut d’abord générer une clé, plus exactement un PAT (personal Access Token). Pour ce faire aller à Settings, puis dans la barre de gauche aller dans Developper…

Générer le PAT, sélectionner les droits de ce PAT, je n’ai besoin qu’en lecture, donc cocher comme dans l’image. Je vous propose dans un premier temps d’expérimenter avec Postman, pour ceux qui ne le connaissent pas encore, Postman permet de faire des requêtes HTTP sans navigateur, pratique pour tester des webservices.

Vous aurez les repository du propriétaqire de votre token. Maintenant listons les commits d’une branches, il a fallu explorer la documentation, ne la baclez pas, la documentation est riche.

// lister les branches
https://api.github.com/repos/refschool/Clone-Weetransfert/branches

// lister les commits de la branche main
https://api.github.com/repos/refschool/Clone-Weetransfert/branches/main

//  lister les commits de la branche par défaut qui est main
https://api.github.com/repos/refschool/Clone-Weetransfert/commits

//  lister les commits de la branche develop avec 100 résultats par page.
https://api.github.com/repos/refschool/Clone-Weetransfert/commits?per_page=100&sha=develop

Il faut essayer de se familiariser avec la pagination le jour où l’on a vraiment beaucoup de commit.

Lien vers la documentation:

https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28

Mais ne peut on pas lister les pull request?

A l’évidence si ! voici la documentation :

https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests

vous devez être le propriétaire du repo pour avoir les pull requests du projet (normal)

Installer docker sur DigitalOcean Droplet avec Docker

Dans cet article on va voir comment déployer des container docker d’une application multicontainer dans un VPS à l’aide de Docker-compose. Sur DigitalOcean, la manipulation est plus facile que sur Azure ou AWS (le plus complexe)

Création du Droplet

Un droplet sur DigitalOcean (un prestataire Cloud abordable et pas trop compliqué à utiliser),

1/Créer un compte sur DigitalOcean

2/créer un droplet l’équivalent d’un VPS, choisir Ubuntu comme OS avec Docker préinstallé en vous aidant du Marketplace. Vous créerez par la même occasion un projet qui apparaitra dans la sidebar.

Ensuite vous choisissez le Datacenter le plus proche de chez vous, en France l’on choisira Amsterdam par exemple.

Dans la section « Choose an image« , cliquez sur l’onglet Marketplace, puis sur l’image Docker on Ubuntu, ici l’image représente une machine virtuelle bien sûr et pas une image Docker.

Ensuite choisissez les options les moins chères, dans Droplet Type, choisissez « Basic« , dans CPU options, choisissez « Regular« , et la formule au mois la moins chère.

Dans « Choose Authentication Method« , choisissez Password pour faire simple, et SSH Key pour faire pratique (pas de mémorisation de mot de passe et login nécessaire). Allez sur ce post pour générer et mettre votre clé sur le service. Enfin cliquez sur « Create Droplet ». Au bout d’un certain temps votre droplet sera créée avec Docker installé, cependant Docker-Compose ne sera pas encore installé. Il vous faudra faire via la console.

Si vous voulez installer à la main Docker, les instruction sont sur cette page :

https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-22-04

Cloner votre projet depuis Github

Pour vous connecter en console dans votre Droplet dans la sidebar « Projects« , cliquez sur votre projet

Installer docker-compose

Une fois dedans cliquez sur « Console« , cela ouvrira un shell. Installez docker-compose

///sudo apt-get install docker-compose-plugin

ou tapez docker-compose, une erreur avec un message sur comment installer docker-compose s'affichera.
apt install docker-compose

Vous êtes en connecté en root, allez à /home et cloner le projet. Le projet consiste en un front end en ReactJS, un backend en Node/Express et de MongoDB. Ce dernier est directement tiré du hub de Docker. Alors que le front et le back vont être transformé en image via leurs Dockerfiles respectifs.

Clonage du projet depuis Github

/home# git clone git@github.com:refschool/vidly.git

On clone soit en SSH car Github ne supporte plus l’authentification par login et mot de passe. ou alors il faut utiliser un PAT personal access token. L’ennui avec la clé SSH est qu’il faut en créer une sur le droplet et la mettre sur Github, cela fait beaucoup de manipulations. Le mieux est de créer le PAT sur Github. Il s’utilise comme un mot de passe.

git clone https://<PAT>@github.com/refschool/vidly.git

Copie du fichier docker-compose-prod.yml depuis votre poste vers votre droplet

A ce stade il n’y a pas grand d’installé ni de configuré pour votre droplet (pas de FTP etc). Le moyen le plus simple d’envoyer un fichier vers le droplet est d’utiliser l’utilitaire rsync pour envoyer ce fichier. Mais le problème est que ce n’est pas simple d’avoir rsync sur Windows (pas un problème sur Linux). On va utiliser scp depuis le terminal DOS.

scp -i C:\Users\admin\.ssh\id_rsa E:\mosh\vidly\docker-compose-prod.yml root@188.166.87.69:/home/

Update :

Si le fichier docker-compose-prod.yml est dans le git, pas besoin de faire cette manipulation finalement.

Démarrage du docker-compose avec le fichier de prod

Dans la racine du projet où il y a le fichier docker-compose-prod.yml

docker-compose -f docker-compose-prod.yml up -d

Cette unique ligne va créer ou importer les images, démarrer les containers et in fine votre application.

Accès à l’application sur DigitalOcean

Si tout s’est bien passé, vous allez pouvoir accéder à votre application sur le droplet à l’adresse IP que vous pouvez retrouver dans votre interface chez DigitalOcean, sur le port 3000.

Les problèmes rencontrés et leurs solutions

An accédant via l’adresse IP l’application, on rencontre une première erreur :

Problème de CORS

La preière fois que vous accédez à votre application, vous allez rencontrer ce message « Could not fetch movie »

Ceci est vraisemblablement dû à un problème de CORS (cross origin), si on ouvre les devtools, onglet Réseau, en inspectant la requête, vous aurez les indices. La requête est faite à http://localhost:3001/api alors que notre serveur possède une adresse IP (voir image)

En effet en phase de développement on a http://localhost:3001/api (voir le fichier src/services/api.js) , si la variable d’environnement REACT_APP_API_URL est settée, elle surchargera localhost.

const baseUrl = process.env.REACT_APP_API_URL || "http://localhost:3001/api";

A la racine du projet, il y a un fichier docker-compose-prod.yml. Dans le Dockerfile du front il vous faudra remplacer dans le fichier Dockerfile après COPY

#Dockerfile
...
COPY . .
ENV REACT_APP_API_URL=http://adresse-ip-du-droplet:3001/api
...

Problème de droits de fichier

Vérifiez aussi que dans le dossier backend, les fichiers docker-entrypoint.sh et wait-for soient exécutables en faisant la commande chmod +x <fichier>.

Une fois que la commande docker-compose est faite, vous avez la main dans le shell, Inspectez les containers

docker ps

//Voir les logs d'un container
docker logs <id-container>

//Par exemple si le container du backend ne fonctionne pas correctement, lisez les logs.

//Pour arrêter
docker-compose down


//Pour relancer avec un build d'images

docker-compose -f docker-compose-prod.yml up -d --build
Retour en haut