feat(deploy): installeur Inno Setup pour déploiement professionnel
- Lea.iss : script Inno Setup 6 (enrollment 2 pages, licence, machine_id) - build_installer.sh : staging + ISCC (compatible Wine sur Linux) - uninstall_lea.ps1 : kill PID + cleanup + notif serveur - configure_embed.ps1 : Python 3.12 embedded optionnel - config_template.txt : modèle pour installation silencieuse - LICENSE.txt : CGU AI Act Art. 50 - README.md : doc build, signing, déploiement silencieux Paramètres d'installation silencieuse : Lea-Setup-v1.0.0.exe /VERYSILENT /CONFIG=enroll.txt /LOG=install.log Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
554
deploy/installer/Lea.iss
Normal file
554
deploy/installer/Lea.iss
Normal file
@@ -0,0 +1,554 @@
|
||||
; ============================================================
|
||||
; Lea.iss — Script Inno Setup pour l'installeur Lea
|
||||
; ------------------------------------------------------------
|
||||
; Compile avec Inno Setup 6.2+ (ISCC.exe Lea.iss)
|
||||
;
|
||||
; Ce script produit Lea-Setup-v{VERSION}.exe dans ..\releases\
|
||||
;
|
||||
; Fonctions principales :
|
||||
; - Page de bienvenue + licence (CGU)
|
||||
; - Page custom d'enrollment (nom, email, ID AIVANOV, URL, token)
|
||||
; - Generation d'un machine_id unique par poste
|
||||
; - Generation automatique de config.txt
|
||||
; - Installation silencieuse de Python 3.12 embedded (optionnelle)
|
||||
; - Raccourci demarrage automatique (checkbox)
|
||||
; - Installation silencieuse : /VERYSILENT /CONFIG=path\to\config.txt
|
||||
; - Desinstallation propre (kill process, cleanup, export logs)
|
||||
;
|
||||
; Pre-requis staging :
|
||||
; Le dossier ..\build\installer_staging\ doit contenir :
|
||||
; - Le package Lea complet (agent_v1/, lea_ui/, run_agent_v1.py, Lea.bat, ...)
|
||||
; - Optionnel : python-3.12-embed\ (runtime Python embedded pre-configure)
|
||||
; build_installer.sh s'occupe de preparer ce staging.
|
||||
; ============================================================
|
||||
|
||||
#define MyAppName "Lea"
|
||||
#define MyAppVersion "1.0.0"
|
||||
#define MyAppPublisher "AIVANOV"
|
||||
#define MyAppURL "https://lea.labs.laurinebazin.design"
|
||||
#define MyAppExeName "Lea.bat"
|
||||
#define MyAppDescription "Lea - Assistante IA pour l'automatisation"
|
||||
|
||||
; Chemin du staging (peut etre surcharge via ISCC /DSourceDir=...)
|
||||
#ifndef SourceDir
|
||||
#define SourceDir "..\build\installer_staging"
|
||||
#endif
|
||||
|
||||
; Chemin de sortie des installeurs
|
||||
#ifndef OutputDir
|
||||
#define OutputDir "..\releases"
|
||||
#endif
|
||||
|
||||
; Activer le bundle Python embedded si present dans le staging
|
||||
#define PythonEmbedDir "python-3.12-embed"
|
||||
|
||||
[Setup]
|
||||
AppId={{B3F9A1E2-5C4D-4E7F-9A1B-2C3D4E5F6789}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
AppVerName={#MyAppName} {#MyAppVersion}
|
||||
AppPublisher={#MyAppPublisher}
|
||||
AppPublisherURL={#MyAppURL}
|
||||
AppSupportURL={#MyAppURL}
|
||||
AppUpdatesURL={#MyAppURL}
|
||||
DefaultDirName={autopf}\{#MyAppName}
|
||||
DefaultGroupName={#MyAppName}
|
||||
DisableProgramGroupPage=yes
|
||||
OutputDir={#OutputDir}
|
||||
OutputBaseFilename=Lea-Setup-v{#MyAppVersion}
|
||||
; Compression correcte (pas trop aggressive pour que l'install reste rapide)
|
||||
Compression=lzma2
|
||||
SolidCompression=yes
|
||||
; Support HiDPI
|
||||
WizardStyle=modern
|
||||
; Langue FR par defaut
|
||||
ShowLanguageDialog=no
|
||||
; Autorise l'install en mode user si pas admin (bascule sur LOCALAPPDATA)
|
||||
PrivilegesRequired=lowest
|
||||
PrivilegesRequiredOverridesAllowed=dialog
|
||||
; Icone de l'installeur (decommenter si disponible)
|
||||
; SetupIconFile=lea.ico
|
||||
; Uninstall
|
||||
UninstallDisplayName={#MyAppName} {#MyAppVersion}
|
||||
; UninstallDisplayIcon={app}\lea.ico ; decommenter quand l'icone sera fournie
|
||||
; Architecture : 64-bit uniquement (Windows 10+ / 11)
|
||||
ArchitecturesAllowed=x64compatible
|
||||
ArchitecturesInstallIn64BitMode=x64compatible
|
||||
; Version minimale Windows : 10
|
||||
MinVersion=10.0
|
||||
; Informations legales
|
||||
VersionInfoVersion={#MyAppVersion}
|
||||
VersionInfoCompany={#MyAppPublisher}
|
||||
VersionInfoDescription={#MyAppDescription}
|
||||
VersionInfoCopyright=Copyright (C) 2026 {#MyAppPublisher}
|
||||
; Licence CGU affichee avant le choix du repertoire
|
||||
LicenseFile=LICENSE.txt
|
||||
|
||||
[Languages]
|
||||
Name: "french"; MessagesFile: "compiler:Languages\French.isl"
|
||||
|
||||
[Files]
|
||||
; Package complet (code Python + .bat + requirements)
|
||||
; Note : install.bat EST copie (execute par [Run] pour creer le venv Python)
|
||||
; Note : config.txt n'est PAS copie depuis le staging (il est genere par [Code])
|
||||
Source: "{#SourceDir}\*"; \
|
||||
DestDir: "{app}"; \
|
||||
Flags: ignoreversion recursesubdirs createallsubdirs; \
|
||||
Excludes: "{#PythonEmbedDir}\*,config.txt,*.log,sessions\*,__pycache__\*"
|
||||
|
||||
; Python 3.12 embedded (optionnel, copie conditionnelle via check)
|
||||
Source: "{#SourceDir}\{#PythonEmbedDir}\*"; \
|
||||
DestDir: "{app}\python-embed"; \
|
||||
Flags: ignoreversion recursesubdirs createallsubdirs skipifsourcedoesntexist; \
|
||||
Components: pythonembed
|
||||
|
||||
; Script de desinstallation custom (kill + export logs)
|
||||
Source: "uninstall_lea.ps1"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
; Script de configuration du runtime Python embedded (optionnel)
|
||||
Source: "configure_embed.ps1"; DestDir: "{app}"; Flags: ignoreversion; Components: pythonembed
|
||||
|
||||
; Licence CGU (affichee dans la page licence ET conservee dans {app})
|
||||
Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion isreadme
|
||||
|
||||
; Template de config pour installation silencieuse (reference)
|
||||
Source: "config_template.txt"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
[Components]
|
||||
Name: "core"; Description: "Lea (obligatoire)"; Types: full compact custom; Flags: fixed
|
||||
Name: "pythonembed"; Description: "Python 3.12 embedded (recommande si Python non installe sur le poste)"; Types: full
|
||||
Name: "autostart"; Description: "Demarrer Lea automatiquement au demarrage de Windows"; Types: full
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "Creer un raccourci sur le bureau"; GroupDescription: "Raccourcis :"; Flags: unchecked
|
||||
Name: "startmenuicon"; Description: "Creer un raccourci dans le menu Demarrer"; GroupDescription: "Raccourcis :"
|
||||
|
||||
[Icons]
|
||||
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; WorkingDir: "{app}"; Tasks: startmenuicon
|
||||
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; WorkingDir: "{app}"; Tasks: desktopicon
|
||||
; Raccourci autostart (shell:startup) — cree si composant autostart selectionne
|
||||
Name: "{userstartup}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; \
|
||||
WorkingDir: "{app}"; Components: autostart
|
||||
|
||||
[Run]
|
||||
; Apres copie : executer install.bat pour creer le venv et installer les dependances Python
|
||||
; Skip si bundle embedded (dans ce cas, on utilise python-embed directement)
|
||||
Filename: "{app}\install.bat"; \
|
||||
WorkingDir: "{app}"; \
|
||||
StatusMsg: "Installation des composants Python (1-2 minutes)..."; \
|
||||
Flags: runhidden waituntilterminated; \
|
||||
Components: not pythonembed
|
||||
|
||||
; Configuration Python embedded : creer un Lea.bat qui pointe sur python-embed
|
||||
Filename: "{cmd}"; \
|
||||
Parameters: "/c copy /y ""{app}\Lea.bat"" ""{app}\Lea.bat.bak"" && powershell -NoProfile -ExecutionPolicy Bypass -File ""{app}\configure_embed.ps1"""; \
|
||||
WorkingDir: "{app}"; \
|
||||
StatusMsg: "Configuration du runtime Python embedded..."; \
|
||||
Flags: runhidden waituntilterminated skipifsilent; \
|
||||
Components: pythonembed
|
||||
|
||||
; Lancer Lea a la fin de l'installation (optionnel)
|
||||
Filename: "{app}\{#MyAppExeName}"; \
|
||||
Description: "Lancer {#MyAppName} maintenant"; \
|
||||
Flags: postinstall skipifsilent nowait shellexec
|
||||
|
||||
[UninstallRun]
|
||||
; Tuer le process via PID du lock avant suppression des fichiers
|
||||
Filename: "powershell.exe"; \
|
||||
Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{app}\uninstall_lea.ps1"" -AppDir ""{app}"""; \
|
||||
RunOnceId: "KillLeaProcess"; \
|
||||
Flags: runhidden waituntilterminated
|
||||
|
||||
[UninstallDelete]
|
||||
Type: filesandordirs; Name: "{app}\.venv"
|
||||
Type: filesandordirs; Name: "{app}\__pycache__"
|
||||
Type: filesandordirs; Name: "{app}\agent_v1\__pycache__"
|
||||
Type: filesandordirs; Name: "{app}\agent_v1\sessions"
|
||||
Type: filesandordirs; Name: "{app}\agent_v1\logs"
|
||||
Type: files; Name: "{app}\lea_agent.lock"
|
||||
Type: files; Name: "{app}\config.txt"
|
||||
Type: files; Name: "{app}\machine_id.txt"
|
||||
|
||||
; ============================================================
|
||||
; Code Pascal : pages custom + generation config.txt + helpers
|
||||
; ============================================================
|
||||
[Code]
|
||||
const
|
||||
SERVER_URL_DEFAULT = 'https://lea.labs.laurinebazin.design/api/v1';
|
||||
SERVER_HOST_DEFAULT = 'lea.labs.laurinebazin.design';
|
||||
DEFAULT_TOKEN = '86031addb338e449fccdb1a983f61807aec15d42d482b9c7748ad607dc23caab';
|
||||
|
||||
var
|
||||
EnrollmentPage: TInputQueryWizardPage;
|
||||
TokenPage: TInputQueryWizardPage;
|
||||
MachineIdValue: string;
|
||||
ConfigFilePath: string;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Helper : ajoute des guillemets autour d'une chaine
|
||||
// --------------------------------------------------------------------
|
||||
function AddQuotes(const S: string): string;
|
||||
begin
|
||||
Result := '"' + S + '"';
|
||||
end;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Wrapper CreateGUIDString (via PowerShell, fallback par defaut)
|
||||
// --------------------------------------------------------------------
|
||||
function CreateGUIDString(var Guid: string): Boolean;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
TmpFile: string;
|
||||
Lines: TArrayOfString;
|
||||
begin
|
||||
Result := False;
|
||||
TmpFile := ExpandConstant('{tmp}\guid.txt');
|
||||
// powershell : genere un GUID
|
||||
if Exec('powershell.exe',
|
||||
'-NoProfile -Command "[guid]::NewGuid().ToString() | Out-File -Encoding ASCII ' + AddQuotes(TmpFile) + '"',
|
||||
'', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
|
||||
begin
|
||||
if LoadStringsFromFile(TmpFile, Lines) and (GetArrayLength(Lines) > 0) then
|
||||
begin
|
||||
Guid := Trim(Lines[0]);
|
||||
Result := Length(Guid) > 0;
|
||||
end;
|
||||
DeleteFile(TmpFile);
|
||||
end;
|
||||
end;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Recupere le hostname de la machine
|
||||
// --------------------------------------------------------------------
|
||||
function GetComputerNameString(): string;
|
||||
var
|
||||
Buffer: string;
|
||||
begin
|
||||
Buffer := ExpandConstant('{computername}');
|
||||
if Length(Buffer) = 0 then
|
||||
Buffer := 'unknown-host';
|
||||
Result := Buffer;
|
||||
end;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Genere un identifiant machine unique : UUID4 + hostname hashe
|
||||
// --------------------------------------------------------------------
|
||||
function GenerateMachineId(): string;
|
||||
var
|
||||
Guid: string;
|
||||
Hostname: string;
|
||||
I: Integer;
|
||||
Hash: Cardinal;
|
||||
begin
|
||||
// Essaye d'utiliser le GUID genere par Windows (via PowerShell)
|
||||
Guid := '';
|
||||
if CreateGUIDString(Guid) then
|
||||
Result := LowerCase(StringChange(StringChange(StringChange(Guid, '{', ''), '}', ''), '-', ''))
|
||||
else
|
||||
Result := IntToStr(GetTickCount);
|
||||
|
||||
// Ajoute un hash du hostname pour stabilite
|
||||
Hostname := GetComputerNameString();
|
||||
Hash := 0;
|
||||
for I := 1 to Length(Hostname) do
|
||||
Hash := (Hash * 31 + Ord(Hostname[I])) and $FFFFFFFF;
|
||||
|
||||
Result := Copy(Result, 1, 16) + '-' + Format('%08x', [Hash]);
|
||||
end;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Charge une configuration depuis /CONFIG=path (installation silencieuse)
|
||||
// Format du fichier : NOM=valeur, une ligne par parametre
|
||||
// Cles attendues : USER_NAME, USER_EMAIL, USER_ID, SERVER_URL, API_TOKEN
|
||||
// --------------------------------------------------------------------
|
||||
procedure LoadConfigFromCommandLine(); forward;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Initialisation : cree les pages custom d'enrollment
|
||||
// --------------------------------------------------------------------
|
||||
procedure InitializeWizard();
|
||||
begin
|
||||
// Page 1 : informations collaborateur
|
||||
EnrollmentPage := CreateInputQueryPage(wpSelectTasks,
|
||||
'Identification du collaborateur',
|
||||
'Veuillez renseigner vos informations pour l''enrollment',
|
||||
'Ces informations sont envoyees au serveur Lea pour identifier votre poste. ' +
|
||||
'Elles sont stockees de maniere securisee et ne sont jamais partagees avec des tiers.');
|
||||
|
||||
EnrollmentPage.Add('Nom et prenom :', False);
|
||||
EnrollmentPage.Add('Email professionnel :', False);
|
||||
EnrollmentPage.Add('ID interne AIVANOV (optionnel) :', False);
|
||||
|
||||
EnrollmentPage.Values[0] := '';
|
||||
EnrollmentPage.Values[1] := '';
|
||||
EnrollmentPage.Values[2] := '';
|
||||
|
||||
// Page 2 : configuration serveur (URL + token)
|
||||
TokenPage := CreateInputQueryPage(EnrollmentPage.ID,
|
||||
'Connexion au serveur Lea',
|
||||
'Configuration de la connexion au serveur central',
|
||||
'L''URL du serveur est pre-remplie par defaut. Le token d''authentification ' +
|
||||
'vous est fourni par votre administrateur AIVANOV. Laissez la valeur par defaut ' +
|
||||
'si vous ne savez pas quoi mettre.');
|
||||
|
||||
TokenPage.Add('URL du serveur (avec /api/v1) :', False);
|
||||
TokenPage.Add('Token d''authentification :', False);
|
||||
|
||||
TokenPage.Values[0] := SERVER_URL_DEFAULT;
|
||||
TokenPage.Values[1] := DEFAULT_TOKEN;
|
||||
|
||||
// Si un fichier /CONFIG= est passe en ligne de commande, pre-remplir
|
||||
LoadConfigFromCommandLine();
|
||||
end;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Implementation de LoadConfigFromCommandLine (declare en forward ci-dessus)
|
||||
// --------------------------------------------------------------------
|
||||
procedure LoadConfigFromCommandLine();
|
||||
var
|
||||
ConfigParam: string;
|
||||
Lines: TArrayOfString;
|
||||
I: Integer;
|
||||
Line, Key, Value: string;
|
||||
EqPos: Integer;
|
||||
begin
|
||||
ConfigParam := ExpandConstant('{param:CONFIG}');
|
||||
if Length(ConfigParam) = 0 then Exit;
|
||||
if not FileExists(ConfigParam) then Exit;
|
||||
|
||||
if not LoadStringsFromFile(ConfigParam, Lines) then Exit;
|
||||
|
||||
for I := 0 to GetArrayLength(Lines) - 1 do
|
||||
begin
|
||||
Line := Trim(Lines[I]);
|
||||
if (Length(Line) = 0) or (Line[1] = '#') then Continue;
|
||||
|
||||
EqPos := Pos('=', Line);
|
||||
if EqPos = 0 then Continue;
|
||||
|
||||
Key := Trim(Copy(Line, 1, EqPos - 1));
|
||||
Value := Trim(Copy(Line, EqPos + 1, Length(Line)));
|
||||
|
||||
if Key = 'USER_NAME' then EnrollmentPage.Values[0] := Value
|
||||
else if Key = 'USER_EMAIL' then EnrollmentPage.Values[1] := Value
|
||||
else if Key = 'USER_ID' then EnrollmentPage.Values[2] := Value
|
||||
else if Key = 'SERVER_URL' then TokenPage.Values[0] := Value
|
||||
else if Key = 'API_TOKEN' then TokenPage.Values[1] := Value;
|
||||
end;
|
||||
end;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Validation des pages custom (Nom/Email obligatoires, token non vide)
|
||||
// --------------------------------------------------------------------
|
||||
function NextButtonClick(CurPageID: Integer): Boolean;
|
||||
var
|
||||
Email: string;
|
||||
begin
|
||||
Result := True;
|
||||
|
||||
if CurPageID = EnrollmentPage.ID then
|
||||
begin
|
||||
if Length(Trim(EnrollmentPage.Values[0])) = 0 then
|
||||
begin
|
||||
MsgBox('Le nom est obligatoire.', mbError, MB_OK);
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
Email := Trim(EnrollmentPage.Values[1]);
|
||||
if (Length(Email) = 0) or (Pos('@', Email) = 0) then
|
||||
begin
|
||||
MsgBox('Un email valide est obligatoire.', mbError, MB_OK);
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
if CurPageID = TokenPage.ID then
|
||||
begin
|
||||
if Length(Trim(TokenPage.Values[0])) = 0 then
|
||||
begin
|
||||
MsgBox('L''URL du serveur est obligatoire.', mbError, MB_OK);
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
if Length(Trim(TokenPage.Values[1])) < 16 then
|
||||
begin
|
||||
if MsgBox('Le token parait court (< 16 caracteres). Continuer quand meme ?',
|
||||
mbConfirmation, MB_YESNO) = IDNO then
|
||||
begin
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Ecrit config.txt genere dans le dossier d'installation
|
||||
// --------------------------------------------------------------------
|
||||
procedure WriteGeneratedConfig();
|
||||
var
|
||||
Config: string;
|
||||
ServerUrl, ServerHost, Token: string;
|
||||
UserName, UserEmail, UserId: string;
|
||||
SlashPos: Integer;
|
||||
begin
|
||||
ConfigFilePath := ExpandConstant('{app}\config.txt');
|
||||
|
||||
ServerUrl := Trim(TokenPage.Values[0]);
|
||||
Token := Trim(TokenPage.Values[1]);
|
||||
UserName := Trim(EnrollmentPage.Values[0]);
|
||||
UserEmail := Trim(EnrollmentPage.Values[1]);
|
||||
UserId := Trim(EnrollmentPage.Values[2]);
|
||||
|
||||
// Derive ServerHost depuis ServerUrl : https://host/api/v1 -> host
|
||||
ServerHost := ServerUrl;
|
||||
ServerHost := StringChange(ServerHost, 'https://', '');
|
||||
ServerHost := StringChange(ServerHost, 'http://', '');
|
||||
SlashPos := Pos('/', ServerHost);
|
||||
if SlashPos > 0 then
|
||||
ServerHost := Copy(ServerHost, 1, SlashPos - 1);
|
||||
|
||||
Config :=
|
||||
'# ============================================================' + #13#10 +
|
||||
'# Configuration Lea (genere par l''installeur)' + #13#10 +
|
||||
'# ============================================================' + #13#10 +
|
||||
'# Genere le ' + GetDateTimeString('yyyy-mm-dd hh:nn:ss', '-', ':') + #13#10 +
|
||||
'# Installe par : ' + UserName + ' <' + UserEmail + '>' + #13#10 +
|
||||
'# ID interne : ' + UserId + #13#10 +
|
||||
'# Machine ID : ' + MachineIdValue + #13#10 +
|
||||
'# ============================================================' + #13#10 +
|
||||
'' + #13#10 +
|
||||
'# Adresse du serveur Lea (URL complete avec /api/v1)' + #13#10 +
|
||||
'RPA_SERVER_URL=' + ServerUrl + #13#10 +
|
||||
'' + #13#10 +
|
||||
'# Cle d''authentification (fournie par l''administrateur)' + #13#10 +
|
||||
'RPA_API_TOKEN=' + Token + #13#10 +
|
||||
'' + #13#10 +
|
||||
'# Nom du serveur (sans https://, sans /api/v1)' + #13#10 +
|
||||
'RPA_SERVER_HOST=' + ServerHost + #13#10 +
|
||||
'' + #13#10 +
|
||||
'# Identifiant unique de cette machine (genere a l''install)' + #13#10 +
|
||||
'RPA_MACHINE_ID=' + MachineIdValue + #13#10 +
|
||||
'' + #13#10 +
|
||||
'# Informations collaborateur (utilisees pour l''audit cote serveur)' + #13#10 +
|
||||
'RPA_USER_NAME=' + UserName + #13#10 +
|
||||
'RPA_USER_EMAIL=' + UserEmail + #13#10;
|
||||
|
||||
if Length(UserId) > 0 then
|
||||
Config := Config + 'RPA_USER_ID=' + UserId + #13#10;
|
||||
|
||||
Config := Config + '' + #13#10 +
|
||||
'# ============================================================' + #13#10 +
|
||||
'# Parametres avances (ne pas modifier sauf indication)' + #13#10 +
|
||||
'# ============================================================' + #13#10 +
|
||||
'' + #13#10 +
|
||||
'# Flouter les zones de texte dans les captures (securite donnees)' + #13#10 +
|
||||
'RPA_BLUR_SENSITIVE=true' + #13#10 +
|
||||
'' + #13#10 +
|
||||
'# Duree de conservation des logs en jours (minimum 180 pour conformite)' + #13#10 +
|
||||
'RPA_LOG_RETENTION_DAYS=180' + #13#10;
|
||||
|
||||
if not SaveStringToFile(ConfigFilePath, Config, False) then
|
||||
MsgBox('Echec de l''ecriture de config.txt dans ' + ConfigFilePath, mbError, MB_OK);
|
||||
end;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Ecrit le machine_id.txt (identifiant du poste)
|
||||
// --------------------------------------------------------------------
|
||||
procedure WriteMachineId();
|
||||
var
|
||||
MachineIdFile: string;
|
||||
begin
|
||||
MachineIdFile := ExpandConstant('{app}\machine_id.txt');
|
||||
if not SaveStringToFile(MachineIdFile, MachineIdValue, False) then
|
||||
MsgBox('Echec de l''ecriture de machine_id.txt', mbError, MB_OK);
|
||||
end;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Notifie le serveur de l'enrollment (best-effort, non bloquant)
|
||||
// POST vers {SERVER_URL}/agents/enroll avec les infos collaborateur
|
||||
// --------------------------------------------------------------------
|
||||
procedure NotifyServerEnrollment();
|
||||
var
|
||||
ResultCode: Integer;
|
||||
PsScript: string;
|
||||
PsFile: string;
|
||||
ServerUrl, Token: string;
|
||||
begin
|
||||
ServerUrl := Trim(TokenPage.Values[0]);
|
||||
Token := Trim(TokenPage.Values[1]);
|
||||
|
||||
PsFile := ExpandConstant('{tmp}\enroll.ps1');
|
||||
PsScript :=
|
||||
'$ErrorActionPreference = ''SilentlyContinue''' + #13#10 +
|
||||
'$body = @{' + #13#10 +
|
||||
' machine_id = ''' + MachineIdValue + '''' + #13#10 +
|
||||
' hostname = $env:COMPUTERNAME' + #13#10 +
|
||||
' user_name = ''' + EnrollmentPage.Values[0] + '''' + #13#10 +
|
||||
' user_email = ''' + EnrollmentPage.Values[1] + '''' + #13#10 +
|
||||
' user_id = ''' + EnrollmentPage.Values[2] + '''' + #13#10 +
|
||||
' agent_version = ''' + '{#MyAppVersion}' + '''' + #13#10 +
|
||||
'} | ConvertTo-Json' + #13#10 +
|
||||
'try {' + #13#10 +
|
||||
' Invoke-RestMethod -Uri ''' + ServerUrl + '/agents/enroll'' ' +
|
||||
'-Method POST -Body $body -ContentType ''application/json'' ' +
|
||||
'-Headers @{ Authorization = ''Bearer ' + Token + ''' } -TimeoutSec 10 | Out-Null' + #13#10 +
|
||||
'} catch { exit 0 }' + #13#10;
|
||||
|
||||
SaveStringToFile(PsFile, PsScript, False);
|
||||
Exec('powershell.exe',
|
||||
'-NoProfile -ExecutionPolicy Bypass -File ' + AddQuotes(PsFile),
|
||||
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
DeleteFile(PsFile);
|
||||
end;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Hook : actions apres copie des fichiers (ssPostInstall)
|
||||
// --------------------------------------------------------------------
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
begin
|
||||
if CurStep = ssInstall then
|
||||
begin
|
||||
// Genere le machine_id AVANT la copie des fichiers
|
||||
MachineIdValue := GenerateMachineId();
|
||||
end;
|
||||
|
||||
if CurStep = ssPostInstall then
|
||||
begin
|
||||
// Ecrit config.txt et machine_id.txt
|
||||
WriteGeneratedConfig();
|
||||
WriteMachineId();
|
||||
// Notifie le serveur (best-effort)
|
||||
NotifyServerEnrollment();
|
||||
end;
|
||||
end;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Desinstallation : proposer d'exporter les logs avant suppression
|
||||
// --------------------------------------------------------------------
|
||||
function InitializeUninstall(): Boolean;
|
||||
var
|
||||
LogDir, ExportDir: string;
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
Result := True;
|
||||
LogDir := ExpandConstant('{app}\agent_v1\logs');
|
||||
|
||||
if DirExists(LogDir) then
|
||||
begin
|
||||
if MsgBox('Voulez-vous exporter les logs de Lea avant la desinstallation ?' + #13#10 +
|
||||
'(les logs seront copies dans votre dossier Documents)',
|
||||
mbConfirmation, MB_YESNO) = IDYES then
|
||||
begin
|
||||
ExportDir := ExpandConstant('{userdocs}\Lea_logs_export');
|
||||
ForceDirectories(ExportDir);
|
||||
Exec('powershell.exe',
|
||||
'-NoProfile -Command "Copy-Item -Path ' + AddQuotes(LogDir + '\*') +
|
||||
' -Destination ' + AddQuotes(ExportDir) + ' -Recurse -Force"',
|
||||
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
MsgBox('Logs exportes dans : ' + ExportDir, mbInformation, MB_OK);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
Reference in New Issue
Block a user