Angular-Electronjs – Enregistrement du token grâce à l’API fs de Nodejs


Dans l’article « Angular-ElectronJS – Login API REST jwt », le token de l’utilisateur était enregistré dans son navigateur grâce au localStorage.

Or nous avons comme objectif de créer une application multiplateforme : logiciel de bureau et application web, nous allons ajuster notre code pour répondre à cet objectif.

Nous allons suivre quelques étapes :

  • Création d’un service storageService partagé qui enregistre les informations retournées par le serveur :
    • soit localStorage (dans le contexte webapplication)
    • soit dans un fichier json (dans le contexte electronjs)
  • Remplacement des appels de localStorage .

Service storageService

La création de ce service se fait grâce à la commande Angular suivante :

ng g service providers/storage 

Les fichiers suivants sont créés :

  • angular-electron/src/app/providers/storage.service.ts
  • angular-electron/src/app/providers/storage.service.spec.ts

Modifions angular-electron/src/app/providers/login.service.ts et importons le service ElectronService. ElectronService est un service pré-installé dans l’application. Il permet d’utiliser l’API de Electronjs dans une application Angular.

import { StorageService } from './storage.service';
import { ElectronService } from './electron.service';

Ajoutons quelques variables avant le constructeur

public storage: StorageService; 

Puis ajoutons dans le constructeur le service ElectronService

constructor(private http: HttpClient, private electron: ElectronService)  

Remplaçons toutes les directives localStorage par this.storage dans le fichier angular-electron/src/app/providers/login.service.ts

Ancien Code Nouveau code
this.currentUserSubject = new BehaviorSubject<User>(JSON.parse(localStorage.get('user'))); this.currentUserSubject = new BehaviorSubject<User>(JSON.parse(this.storage.get('user')));
localStorage.setItem('user', JSON.stringify(user)); this.storage.save('user', user);
localStorage.removeItem('user'); this.storage.remove('user');
return localStorage.getItem('user'); return this.storage.get('user');

Comme vu un peu plus haut, les informations retournées par le serveur (si l’utilisateur a bien été authentifié) seront enregistrées dans un fichier .json (dans le contexte electronjs). Nous allons enregistrer le « nom de ce fichier » dans le fichier de configuration spécifique à l’environnement utilisé. Rappelons que nous avons choisi les 3 environnements suivants :

  • LOCAL
  • DEV
  • PROD

L’environnement en LOCAL (angular-electron/src/environments/environment.ts) contient le code suivant

export const AppConfig = {
  production: false,
  environment: 'LOCAL',
  apiUrl: 'http://localhost:3008/api',
  configFile: 'token.json'
};

La ligne 5 de ce fichier contient le nom du fichier .json. Ce fichier se situe à l’emplacement suivant pour l’OS windows: C:Users~AppDataRoamingangular-electrontoken.json. L’API de ElectronJs permet de récupérer le chemin de ce fichier. Voici un exemple de cet appel :

this.electron.remote.app.getPath('userData') + '/' + AppConfig.configFile

Le fichier angular-electron/src/app/providers/storage.service.ts contient le code suivant

import { Injectable } from '@angular/core';
import {ElectronService} from "./electron.service";
import { AppConfig } from '../../environments/environment';

@Injectable({
  providedIn: 'root'
})
export class StorageService  {

  isElectron: boolean;
  confExists: boolean;
  electron: ElectronService;
  configFile: string;

  constructor(electron: ElectronService) {
    this.electron = electron;

    if (this.electron.isElectron()) {
      this.initElectron();
    }

  }

  save(key: string, content: string) {
    if (this.isElectron) {
      let data =  {};
      data[key] =  content;
      this.electron.fs.writeFileSync(this.configFile, JSON.stringify(data));
      return;
    }

    localStorage.setItem(key, JSON.stringify(content));
  }

  remove(key: string) {

    if (this.isElectron) {
      // Remove file
      this.electron.fs.unlinkSync(this.configFile);
      return;
    }

    localStorage.removeItem(key);
  }

  get(key: string) {

    if (this.isElectron && this.electron.fs.existsSync(this.configFile)) {
      let jsonContents = this.electron.fs.readFileSync(this.configFile, "utf8");
      jsonContents = JSON.parse(jsonContents); 
      return JSON.stringify(jsonContents[key]);
    } 
    return localStorage.getItem(key);
  }

  private initElectron() {
    this.isElectron = true;
    this.configFile = this.electron.remote.app.getPath('userData') + '/' + AppConfig.configFile; 
    this.confExists = this.electron.fs.existsSync(this.configFile);
  }
}

save

Si on regarde de près la methode save qui enregistre les informations retournées par le serveur.

save(key: string, content: string) {
    if (this.isElectron) {
      let data =  {};
      data[key] =  content;
      this.electron.fs.writeFileSync(this.configFile, JSON.stringify(data));
      return;
    }

    localStorage.setItem(key, JSON.stringify(content));
  }

La ligne 2-7 vérifie si nous sommes dans le contexte electronjs. Dans ce cas, les informations retournées par le serveur ( token y compris) seront enregistrées dans le fichier json C:Users~AppDataRoamingangular-electrontoken.json .Ceci est possible grâce à l’API de electronjs qui permet d’enregistrer un fichier sur la machine. Cet API utilise tout simplement l’API fs de nodejs.

this.electron.fs.writeFileSync(this.configFile, JSON.stringify(data));

La ligne 9 est utilisée dans le contexte web application

remove

La methode remove est légèrement différente selon le contexte :

  • electronjs: le fichier C:Users~AppDataRoamingangular-electrontoken.json sera supprimé.
  • webapplication: le localstorage contenant la clé user sera supprimé.

get

La méthode get est similaire dans les 2 contextes :

  • lire les informations contenus dans localstorage ou dans le fichier json

Et voila, nous avons atteint notre objectif. Notre application multiplateforme fonctionne correctement.

Capture webapplication

Capture electronjsapplication

Sources: https://github.com/rabehasy/angular-electron/tree/step3