Nom de l’auteur/autrice :yvonh

Comment partager votre site web en local sans utiliser Ngrok, ni localtunnel

Ngrok est (était) un service pratique pour partager votre site web en local à votre client, mais aujourd’hui il nécessite un compte. Il y a des alternatives comme localtunnel, ou serveo. Mais je vais vous ontrer qu’il est possible de faire sans. Pour ce faire il faut que vous disposiez d’un VPS.

Pour l’exemple je me base sur un Droplet DigitalOcean avec authentification par mot de passe.

Vérifiez juste que le firewall (iptable ou ufw) ne bloque par l’accès au port 22 et que le /etc/ssh/sshd_config contienne les lignes suivantes sur le VPS:

GatewayPorts yes
PermitRootLogin yes

Ensuite et c’est là où il faut comprendre qu’il faut faire la commande en local et non sur le VPS !! :

ssh -R 80:localhost:3000 root@159.223.3.3

La commande ci-dessus forwarde le port 80 du serveur VPS (identifié par son adresse IP) vers le localhost:3000.

Il va vous être demandé une authentification (mot de passe), une fois ceci fait, allez dans le navigateur et entrez l’adresse :

http://159.223.3.3

et vous verrez le contenu de votre localhost:3000 !

Utiliser netcat pour écouter des messages locaux et distants

netcat est un utilitaire réseau servant à écouter les ports. Par exemple l’exemple suivant va écouter sur le port 12345

# ouvrez un premier shell et faites la commande suivante:
nc -l -p 12345

# depuis le même hôte ouvrez un second shell et faites la commande suivante :
echo "Hello" | nc 127.0.0.1 12345

dans le premier shell vous allez voir le message "Hello" sortir

L’exercice serait plus intéressant si on envoyait un message depuis un ordinateur extérieur ayant une autre adresse IP:

# dans le premier shell de l'hôte avec l'adresse IP  145.231.65.88 (adresse donnée au hasard)
nc -l -p 12345

# dans le second ordinateur
echo "Hello" | nc 145.231.65.88 12345

# il ne se passe rien, parce que par défaut netcat n'écoute que sur l'interface 127.0.0.1, pour lui demander d'écouter sur toutes les interfaces :
nc -l -p 12345 -s 0.0.0.0 -k -v

cette fois ci ça marche.

Listening on 0.0.0.0 12345
Connection received on 5.48.206.214 63696
Hello

Rediriger ver un named pipe.

Ce que netcat va recevoir on le redirige vers un named pipe.Il faut d’abord créer un pipi appelé mypipe

# dans un premier shell
mkfifo mypipe

nc -l -p 12345 -s 0.0.0.0 -k -v > mypipe

# dans un second shell 
cat < mypipe

# depuis un ordinateur distant
echo '9' | nc 178.62.221.128 12345

# vous constaterez qu'il y a un petit délai d'une seconde pour voir afficher 
# mais le plus gênant c'est du côté de l'ordinateur distant, on ne nous rend pas la main, ceci est dû au fait que netcat  garde la connexion ouvert après l'envoi. Nous allons lui demander de fermer la connexion après envoi avec le paramètre -q à 1
$ echo '7' | nc -q 1 178.62.221.128 12345

la main nous est rendu après envoi.

Rediriger vers un fichier texte

au lieu de rediriger vers le named pipe on redirige vers un fichier 
$ nc -l -p 12345 -s 0.0.0.0 -k -v >> log.txt

Découverte des named pipe dans Linux.

Oubliez le web, revenez 30 ans en arrière, comment faisaient les process (programmes) pour communiquer?

Les named pipe sont une technologie qui le permettent, mais il y a aussi les sockets. Mais dans cet article on va parler uniquement des named pipes.

Mais d’abord c’est quoi les pipes?

Dans Linux un pipe est représenté par le caractère | (une barre verticale)

commande 1 | commande 2

Dans l’exemple ci-dessus, la sortie de la commande 1 est passée en entrée de la commande 2.

Second exemple:
ps aux
la commande ci-dessus nous permet de lister tous les process en cours.
.......
apple            79180   0,0  0,2 67548168  32508   ??  S     9:40     0:00.29 /Applications/Go
apple            75919   0,0  0,0 34151264   1908 s005  S     9:25     0:00.08 -bash
root             75918   0,0  0,0 34151500   4856 s005  Ss    9:25     0:00.03 login -pf apple
apple            75772   0,0  0,0 33619312   2640 s004  Ss+   9:25     0:00.03 /usr/local/bin/b
.......

Il y en a des centaines. On va filtrer la ou les lignes qui contiennent le mot ‘bash’, (vous pouvez choisir le mot que vous voulez). Pour ce faire on va utiliser grep qui permet de chercher dans un fichier un mot ou une expression.

ps aux | grep bash


apple            75919   0,0  0,0 34151264   1908 s005  S     9:25     0:00.08 -bash
apple            75772   0,0  0,0 33619312   2640 s004  Ss+   9:25     0:00.03 /usr/local/bin/bash --init-file /Applications/Visual Studio Code.app/Contents/Resources/app/out/vs/workbench/contrib/terminal/common/scripts/shellIntegration-bash.sh
apple            88204   0,0  0,0 34122828    836 s007  S+   10:20     0:00.00 grep bash
apple            83845   0,0  0,0 34151264   1924 s007  S    10:00     0:00.04 -bash
apple            81464   0,0  0,0 34151264   1876 s006  S     9:50     0:00.03 -bash

la réunion de deux commandes avec pipe nous permet d’avoir un affichage plus restreint.

C’est quoi un named pipe?

un named pipe est un fichier qui permet de faire transiter des messages, dans la logique du premier entré premier sorti ( First In First Out en anglais, sous l’acronyme FIFO). Le fait que ce soit un fichier (un peu spécial) fait qu’il est persistent, jusqu’à ce qu’il soit effacé. Un named pipe permet de faire communiquer des process qui n’ont aucun lien entre eux ! Vous pouvez imaginer comme un téléphone pour les programmes.

Création d’un named pipe

la création se fait avec la commande mkfifo
$ mkfifo mypipe

ls -l
total 0
prw-r--r--  1 apple  staff  0 15 fév 21:42 mypipe

Regardez la sortie de la commande ls -l, la première lettre est « p » comme pipe.

Ecriture et lecture d’un named pipe

Dans ce terminal écrivez quelque chose et passez le au pipe :

echo 'Hello le named pipe!' > mypipe
après avoir écrit cette commande, le shell ne vous rend pas la main
ouvrez un second terminal
et tapiez la commande
$ cat < mypipe
Hello le named pipe!

Un exemple plus consistant

Ouvrez un premier terminal et mettez ce code shell :

$ while true;do
> echo "message at $(date)" > mypipe
> sleep 2
> done

Ce script va toutes les 2 secondes afficher le message avec la date et l’envoyer à mypipe. (Pour taper un script multiligne, utilisez la combinaison SHIFT + ENTREE pour sauter une ligne.)

Ouvrez un second terminal et tapez la commande pour lire la sortie de mypipe:

cat < mypipe
message at Sam 15 fév 2025 22:32:53 CET
$

Aussitôt le message lu le shell vous rend la main Essayez d’enchainer rapidement deux cat < mypipe, si rien n’est dispo à la sortie, le shell ne vous rend pas la main. Notez que si vous attendez un peu trop longtemps,les messages se perdent donc il faut lire de façon continue, ce que nous allons faire avec tail.

tail -f < mypipe

Faire une boucle pour écouter continuellement des message

#Le code ci-dessous va écouter en permanence des messages venant du pipe

while true; do cat < mypipe; done

# ouvrez un second terminal dans le même hôte et faites
$ echo "un message" > mypipe

#le code ci dessus va envoyer un message au pipe et rendre la main.

Ecrire sur un named pipe vers un hôte distant (A venir)

Avec Linux, il est très facile via SSH de traverser la frontière du cyberespace !Allez sur un serveur Linux er créez un named pipe.

Prendre en main UFW Uncomplicated Firewall sous Linux Ubuntu

Vous avez sans doute entendu parler de iptables, ce firewall très connu sous Linux, mais peut être u peu difficile à configurer car très technique et verbeux. Il existe un autre équivalent beaucoup plus friendly : UFW. UFW est un e interface d’accès à iptables, pour faciliter l’usage de ce dernier.

Un pare feu a pour rôle de bloquer le traffic réseau sortant ou entrant, sur le protocole que vous voulez (par exemple SSH, HTTP etc). Vous pouvez blocker des ports, des adresses IP et des intervalle d’adresses IP.

Installation de UFW et activation

Si ce n’est déjà installé vous pouvez faire la commande suivante:

$ sudo apt update
$ sudo apt upgrade
# vérification préalable de l'existence de UFW
$ which ufw


# le cas échéant
$ sudo apt-get install ufw

# après installation on véirifie l'état d'UFW
$ sudo ufw status verbose

-----------------------------
Output
Status: inactive

# on active UFW
$ sudo ufw enable

#pour désactiver
$ sudo ufw disable

La commande status est importante pour avoir l’état des lieux, je vous recommande de souvent la faire pour diagnostiquer des erreur.

On eptu utiliser une autre command pour avoir le status

systemctl status ufw
● ufw.service - Uncomplicated firewall
     Loaded: loaded (/lib/systemd/system/ufw.service; enabled; vendor preset: enabled)
     Active: active (exited) since Sun 2025-02-16 09:12:11 CET; 30min ago
       Docs: man:ufw(8)
   Main PID: 129 (code=exited, status=0/SUCCESS)

Feb 16 09:12:11 PC-YVON systemd[1]: Starting Uncomplicated firewall...
Feb 16 09:12:11 PC-YVON systemd[1]: Finished Uncomplicated firewall.

Autoriser un port sur un protocol : port 80 sur TCP

# va autoriser les connexion HTTP (web)
$ sudo ufw allow 80/tcp

Rule added
Rule added (v6)

Status: active
Logging: on (low)
Default: allow (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
80/tcp                     ALLOW IN    Anywhere
80/tcp (v6)                ALLOW IN    Anywhere (v6)

Nettoyer toutes les règles:

ufw reset

# après redémarrer
ufw enable

Liste des commandes de UFW:

Usage: ufw COMMAND

Commands:
 enable                          enables the firewall
 disable                         disables the firewall
 default ARG                     set default policy
 logging LEVEL                   set logging to LEVEL
 allow ARGS                      add allow rule
 deny ARGS                       add deny rule
 reject ARGS                     add reject rule
 limit ARGS                      add limit rule
 delete RULE|NUM                 delete RULE
 insert NUM RULE                 insert RULE at NUM
 prepend RULE                    prepend RULE
 route RULE                      add route RULE
 route delete RULE|NUM           delete route RULE
 route insert NUM RULE           insert route RULE at NUM
 reload                          reload firewall
 reset                           reset firewall
 status                          show firewall status
 status numbered                 show firewall status as numbered list of RULES
 status verbose                  show verbose firewall status
 show ARG                        show firewall report
 version                         display version information

Application profile commands:
 app list                        list application profiles
 app info PROFILE                show information on PROFILE
 app update PROFILE              update PROFILE
 app default ARG                 set default application policy

https://www.vps-mart.com/blog/how-to-configure-firewall-with-ufw-on-ubuntu

// Principales commandes de ufw
===============================
sudo ufw enable	
sudo ufw disable	
sudo ufw status	
sudo ufw status numbered	
sudo ufw allow 22	
sudo ufw allow http	
sudo ufw allow 8080/tcp	
sudo ufw deny from 192.168.1.100	"192,168,1,255 adresse de broadcast
decouverte de réseau
imprimante qui annonce sa disponibilité"
sudo ufw deny 23	
sudo ufw status numbered	
sudo ufw delete NUMERO	
sudo ufw reset	
sudo ufw allow from 192.168.1.0/24	"notation CIDR sert à représenter un bloc d'adresses IP, IP est codé sur 32 bits, ici 24 premiers bits sont fixes
Quelle plage d’adresses couvre ce bloc ? 192.168.1.1 à 192.168.1.254
/24 correspond à 255.255.255.0 (masque de sous réseau)"
sudo ufw deny from 10.0.0.0/8	
sudo ufw limit ssh	

Configurer le firewall Linux iptables

iptables est un firewall de Linux. La configuration est vaste car en matière de réseau il y a différents types de protocole. Par exemple si vous pingez une adresse IP c’est le protocole ICMP qui est en jeu. nftables est censé remplacer iptables.

Installation de iptables

apt install iptables

// connaitre la version de iptables
iptables --version

Concepts clé d’iptables

  • Tables, les 4 tables filter, nat, mangle,raw
  • Chains INPUT OUTPUT, FORWARD
  • Policies comportement par défaut d’une chaine (ACCEPT or DROP)
  • Rules règles appliquées au réseau

Lister et voir les règles

Pour voir les règles tapez la commande suivante :

iptables -L



Pour lister les règles avec leur numéro :
iptables -L --line-numbers

La politique par défaut c’est quoi?

Imaginez une clause switch case, le cas par défaut est celui qui n’est couvert par aucune règle.

Comment définir une politique par défaut?

avec le flag -P

iptables -P <chaine> <cible>

iptables -P INPUT DROP

iptables -P OUTPUT ACCEPT

iptables -P FORWARD ACCEPT

Le flag -P ne crée pas de nouvelle règle.

Exemple de politique par défaut

iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

Exemple de règles

Ajouter une règle d’interdiction pour les ping (protocole ICMP)

#Tapez la commande suivante pour ajouter une règle à iptables
iptables -I INPUT -p ICMP --icmp-type 8 -j DROP

Bloquer les demandes SSH

iptables -A INPUT -i eth0 -p tcp --dport 22 -j DROP

Supprimer un règle

//Pour supprimer les règles :
supprime la règle 1
iptables -D INPUT 1

Création de règles

Autoriser SSH

sudo iptables -A INPUT -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
sudo iptables -A OUTPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT

Autoriser HTTP et HTTPS

sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT

Autoriser le ping (ICMP)

iptables -A INPUT -p icmp -j ACCEPT

Autoriser le traffic local (loopback)

iptables -A INPUT -i lo -j ACCEPT

Refuser une IP spécifique

iptables -A INPUT -s 192.168.1.100 -j DROP

Limitation brute force

sudo iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH
sudo iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP

Exemple de configuration typique d’un serveur

# Vider les règles existantes
iptables -F
iptables -X

# Politique par défaut
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Autoriser loopback
iptables -A INPUT -i lo -j ACCEPT

# Autoriser les connexions établies
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Autoriser SSH
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Autoriser HTTP / HTTPS
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Autoriser ping
iptables -A INPUT -p icmp -j ACCEPT

// on sauvegarde les règles
iptables-save > /etc/iptables/rules.v4

C’est quoi le loopback?

C’est l’interface réseau de votre ordinateur, appelé lo (lettre l et lettre o)

Configurer fail2ban

Qu’est ce que c’est fail2ban?

Fail2ban, c’est un outil de sécurité pour les serveurs, principalement utilisé sous Linux. Son rôle principal est de protéger le serveur contre les attaques par force brute (par exemple, quand un attaquant essaie de deviner un mot de passe en tentant plein de fois de se connecter).

Voici ce que fait fail2ban concrètement :

  • Il surveille les fichiers logs des services (comme SSH, FTP, Apache, etc.) pour détecter des tentatives de connexion échouées répétées.
  • Dès qu’il remarque qu’une même adresse IP tente plusieurs fois sans succès (configurable), il va bloquer cette IP temporairement via le pare-feu (iptables, nftables, etc.).
  • Le blocage est temporaire (par exemple 10 minutes, 1 heure), mais si l’IP continue à poser problème, fail2ban peut prolonger ce blocage.
  • Cela limite efficacement les risques d’intrusion par brute force sans que tu aies à intervenir manuellement.

Ce que n’est pas fail2ban

Fail2ban ne va pas vous bloquer tout seul, il a besoin d’un firewall comme iptables pour le faire ou ufw. Fail2ban met juste en place des IP à bloquer.

Installer fail2ban

Si vous êtes sous Debian faites la commande suivante :

sudo apt install fail2ban

#apt est une nouvelle version de la commande apt-get, designé pour être plus ergonomique

Ensuite dupliquez les fichiers fail2ban.conf et jail.conf e, fail2ban.local et jail.local, c’est une habitude à prendre pour en cas de mise à jour de fail2ban, vous perdiez votre configuration.

Configurer fail2ban pour protéger ssh

Nous allons modifier avec vim ou nano le fichier jail.local. Comme nous voulons protéger dans cet article seulement sshd, nous allons trouver la section [sshd] et nous assurer de trouver les lignes suivantes

[sshd]

maxretry=2
findtime=600   #10 minutes soit 600 secondes
bantime=600  # temps d ebanissement

bantime est le temps de banissement, findtime est le temps durant lequel les tentatives infructueuses de login se déroulent, c’est la fenêtre d’observation.

Expérimentation:

Connectez vous à un compte et faites exprès d’échouer 2 sessions de connexion de suite.

Ensuite tentez de vous connecter à nouveau.

Quelques commandes relatives à fail2ban

connaitre l’état des lieux de fail2ban

# connaitre l'état des lieux des bans pour un programme comme sshd
fail2ban-client status sshd

#Redémarrer fail2ban après une modification de fichier d configuration

sudo systemctl restart fail2ban

Le fichier de log de fail2ban se trouve dans /var/log/fail2ban.log

en conjonction de fail2ban, il est intéressant de monitorer les logs de connexion du fichier auth.log. Si ce fichier n’est pas présent c’est qu’il faut installer via la command suivante :

sudo apt-get install rsyslog

Débuter avec Docker avec un déploiement simple

Pour tout ce qui suit, il faut avoir sous Windows ou MacOS démarré Docker Desktop.

Déploiement d’un site pur front end mono container

C’est le cas le plus simple.

Pour rappeler une image Docker est construite en copiant les fichiers d’un projet vers une image qui sera « buildée ». Cette image sera runnée pour devenir un container.

Déploiement d’une application PHP/MySQL multicontainer

Une application PHP/MySQL est fait d’un script PHP et d’une base de données. On ne peut pas mettre dans une seule image, donc il nous faudra deux images, les runner sous forme de deux containers. Il faut que la bse de données soit disponible avant que le script PHP ne soit exécuté.

Le script PHP va faire une requête en base de donnée, donc il faut que les deux containers puissent communiquer entre eux.

Enfin on doit pouvoir visiter l’application PHP depui snotre ordinateur hôte.

FROM php:7.3-apache

#Install git and MySQL extensions for PHP

RUN apt-get update && apt-get install -y git
RUN docker-php-ext-install pdo pdo_mysql mysqli
RUN a2enmod rewrite

COPY src /var/www/html/
EXPOSE 80/tcp
EXPOSE 443/tcp

On va builder une image pour le projet PHP, pour l’image de MySQL on va prendre l’image officielle de MySQL.

Et pour mettre en musique les deux images, on a le fichier docker-compose.yml

version: '3.8'

volumes:
  mysql_data:
    # Persistent storage for MySQL data

services:
  mysql:
    image: mysql:8.0
    container_name: mysql8
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: app_db # Create a default database
      MYSQL_USER: app_user # Create a non-root user
      MYSQL_PASSWORD: app_password # Password for the non-root user
    ports:
      - "3306:3306" # Optional: Expose MySQL port to host
    volumes:
      - mysql_data:/var/lib/mysql # Persist database files
    restart: always

  php:
    container_name: php_app
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:80" # Map port 80 in the container to port 8080 on the host
    depends_on:
      - mysql # Ensure MySQL starts before PHP
    environment:
      MYSQL_DB_HOST: mysql # MySQL service hostname
      MYSQL_DB_PORT: 3306
      MYSQL_DB_USER: app_user
      MYSQL_DB_PASS: app_password
      MYSQL_DB_NAME: app_db

Ne soyez pas effrayés par la syntaxe, il y a un formalisme qui reste le même pour ce genre de fichier.

Ici on a deux images, l’image de Mysql est configurée avec le mot de passe root, nom de la bdd, du user et le mot de passe du user non root. On mappe le port 3306 de l’hôte (à gauche des deux points) à celui du container (à droite).

Pour le container de l’application PHP, on définit des variables d’environnement qui sont bien sûr un peu identique aux pramétrage de du container MySQL, mais on n’est pas obligé de les avoir et on peut coder en dur dans le script les mots de passe et autre nom de base de donnée. On mappe le port 8080 de l’hôte au port 80 du container. Pour lancer l amachinerie faites :

docker-compose up

docker-compose docn pour arrêter

# et si vous faites des modifications il faut rebuilder l'image

Vous allez peut être rencontrer un soucis pour la connexion à la base de données, MySQL8, qui utiliser un hashage nouveau, il vous faudra rentrer dans le docker de MySQL et taper la commande suivante:

docker exec -it mysql8 mysql -uroot -psecret

//mysql native password use sha1, newer method use sha2 but not supported by PDO, so roll back to native password
//et une fois dans le prompt de MySQL:
//exécutez la requêtes SLQ suivante :
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'secret';
FLUSH PRIVILEGES;

Cette commande a pour but d’utiliser l’ancien algorithme de hashage de mot de passe.

Le script PHP

C’est un simple script qui fait une connexion à al base de donnée et va dumper l’objet PDO

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

<head>
    <title>Show databases in MySQL server</title>
    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
</head>

<body>
    <div class="container">
        <h1>Show databases in MySQL server</h1>
        <?php
        $dsn = 'mysql:host=mysql;dbname=app_db';
        $user = "root";
        $pass = "secret";
        // Création de l'objet de connexion qui va nous permettre de faire des requêtes SQL
        $pdo = new \PDO($dsn, $user, $pass);
        $result = $pdo->exec("SET CHARACTER SET utf8");
        var_dump($pdo);
   
        ?>
    </div>
</body>

</html>

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.

Installation de NodeJS

voici le contenu de package.json
{
  "name": "nodeupload",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.21.2",
    "multer": "^1.4.5-lts.1",
    "nodemon": "^3.1.9"
  }
}

Et le contenu du 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");
})

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(on dit aussi chiffre ou chiffrage) en ligne de commande un message et ensuite le décrypter. Il existe plusieurs contextes 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:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Launch Program",
            "skipFiles": [
                "<node_internals>/**"
            ],
            "program": "${workspaceFolder}\\src\\server.ts",
            "outFiles": [
                "${workspaceFolder}/**/*.js"
            ]
        }
    ]
}

Ensuite placez un breakpoint, et recharchez la page correspondante.

la clé « request » a pour valeur « launch« , le debugger est lancé quand vous exécutez le programme. Par contre si vous avez déjà démarré avec npm run dev, et que vous voulez débugger, il faudra rattacher le debugger au process, on associera la valeur « attach« 

Dans ce cas le launch.json ressemblera à ça (pas de clé program !)

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "attach",
      "name": "Attach to Node",
      "port": 9229,
      "restart": true,
      "protocol": "inspector",
      "skipFiles": [
        "<node_internals>/**",
        "${workspaceFolder}/node_modules/**/*.js"
      ]
    }
  ]
}

Dans le package.json, pour lancer le serveur on avait ça:

    "serve": "concurrently \"tsc -w\" \"nodemon dist/server.js\""

Il faut ajouter --inspect à nodemon !

    "serve": "concurrently \"tsc -w\" \"nodemon --inspect dist/server.js\""

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 :

Modifier la class pour h1

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

export default Message;
@tailwind base;
@tailwind components;
@tailwind utilities;

Installation de Shadcn UI

Si vous préférez ne pas installer Tailwindcss, voici une alternative qui commence à bine être populaire au moment où j’écris ces lignes, c’est ShadCN. Selon la documentation d’installation de Shadcn, il faut installer quand même TailwindCSS, car le styling repose sur TailwindCSS

// installation de TailwindCSS
npm install tailwindcss @tailwindcss/vite


// vous serez amené à sélectionner un style
npm install @shadcn/ui


// pour ajouter un bouton, ceci va créer un fichier component.json, un répertoire
npx shadcn add button

// extrait du fichier component.json

....
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  },
...


// vous allez sans doute avoir une erreur avec Tailwindcss je ne sais pourquoi mais il doit réutiliser certaines choses, installez ce qui manque

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

Lors de l’installation de TailwindCSS, j’ai eu un soucis avec une version de Vite un peu ancienne, il fallait au moins la version de vite à 5.2 pour utiliser Shadcn. Plus d’information concernant component.json

npm install vite@^5.2.0

// mettre  à jour la peer dependency de react puisque le projet React a été démarré avec ViteJS

npm install @vitejs/plugin-react@latest

// si tout s'est bien passé refaites la commande
npx shadcn add button

Vous aurez potentiellement un message d’erreur du type

No import alias found in your tsconfig.json file.
Visit https://ui.shadcn.com/docs/installation/vite to learn how to set an import alias.

Dans ce cas c’est le fichier tsconfig.json qu’il faut modifier, il faut dire à Typescript où sont les composants Shadcn.

// dans le section compilerOptions ajoutez ça : 

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

// le "." indique que le point de départ de résolution de chemin commence par là où le fichier tsconfig.json se situe (qui est la base du projet)

//en faisant ça vous pourrez avoir de beaux chemin d'import

import { MyComponent } from '@/components/MyComponent'

// au lieu de 

import { MyComponent } from '../../../components/MyComponent'

En refaisant la commande npx shadcn add button vous aurez un dossier component dans /src, et vous trouverez votre composant

Import du bouton généré

Vous ne pouvez sans doute pas encore importer correctement le bouton

// App.tsx
import { useState } from 'react'
import './App.css'
import { Button } from '@/components/ui/button'

function App() {
  const [count, setCount] = useState(0)

  return (
    <div className="App">
      <h1>Synapse Front</h1>
      <Button>Click me</Button>
    </div>
  )
}

export default App

// configuration complémentaire
// vite.config.ts

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

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
})



// tsconfig.json  cette ligne permet le mapping des types Typescript pour node et vite
...
  "compilerOptions": {
    "types": [
      "vite/client",
      "node"
    ],

...

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

Prérequis : projet PHP

si vous êtes en PHP, il est probable que l’extension sqlite soit déjà présent, sinon vérifiez les avec le phpinfo().

Pour créer la base de donnée, il vous suffit de faire le script suivant

<?php
try {
    // Chemin du fichier de la base de données SQLite
    $databasePath = 'ma_base.sqlite';

    // Connexion à la base SQLite via PDO
    $pdo = new PDO("sqlite:" . $databasePath);

    // Activer les exceptions en cas d'erreur
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    echo "Connexion réussie !";
} catch (PDOException $e) {
    echo "Erreur : " . $e->getMessage();
}
?>

Si le fichier ma_base.sqlite n’existe pas, SQLite le créera automatiquement.

Requêtage en ligne de commande

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

1/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, pour 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

2/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)

Si vous avez l’erreur suivante:

error TS18003: No inputs were found in config file 'E:/<chemin>/tsconfig.json'. Specified 'include' paths were '["**/*"]' and 'exclude' paths were '["E:/<chemin>/multiservice/dist"]'.

C’est que vous n’avez pas de fichier .ts (typescript) dans le projet, il faut renommer les fichier js en ts.

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, comment 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. Start est pour la production, serve est pour le développement.

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 sur cette page d’aide

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.

Retour en haut