Files
Inv_web/Inventera.iss
2026-07-25 16:55:38 +03:00

523 lines
17 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#define MyAppName "Inventera"
#define MyAppVersion "0.1.0-beta"
#define MyAppPublisher "Inventera"
#define MyAppExeName "Inventera.Server.exe"
#define PostgreSqlMajor "18"
#define PostgreSqlInstaller "installer\prerequisites\postgesql.exe"
[Setup]
AppId={{78BAF0DB-D841-49A7-954C-16DCA688B136}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
DefaultDirName={autopf}\{#MyAppName}
DefaultGroupName={#MyAppName}
PrivilegesRequired=admin
OutputDir=dist
OutputBaseFilename=Inventera
SetupIconFile=picture\logo\main_logo.ico
UninstallDisplayIcon={app}\{#MyAppExeName}
Compression=lzma2/ultra64
SolidCompression=yes
WizardStyle=modern
ArchitecturesAllowed=x64compatible
ArchitecturesInstallIn64BitMode=x64compatible
CloseApplications=yes
RestartApplications=no
DisableProgramGroupPage=yes
UsePreviousAppDir=yes
[Languages]
Name: "russian"; MessagesFile: "compiler:Languages\Russian.isl"
[Files]
Source: "{#PostgreSqlInstaller}"; Flags: dontcopy noencryption nocompression
Source: "installer\initialize_inventera_database.ps1"; Flags: dontcopy noencryption
Source: "dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
[Icons]
Name: "{group}\Inventera"; Filename: "http://127.0.0.1:{code:GetApplicationPort}"; IconFilename: "{app}\{#MyAppExeName}"
Name: "{commondesktop}\Inventera"; Filename: "http://127.0.0.1:{code:GetApplicationPort}"; IconFilename: "{app}\{#MyAppExeName}"
Name: "{commonstartup}\Inventera Server"; Filename: "{app}\{#MyAppExeName}"; WorkingDir: "{app}"; IconFilename: "{app}\{#MyAppExeName}"
[Run]
Filename: "{sys}\netsh.exe"; Parameters: "advfirewall firewall delete rule name=""Inventera Web"""; Flags: runhidden waituntilterminated
Filename: "{sys}\netsh.exe"; Parameters: "advfirewall firewall add rule name=""Inventera Web"" dir=in action=allow protocol=TCP localport={code:GetApplicationPort}"; Flags: runhidden waituntilterminated
Filename: "{app}\{#MyAppExeName}"; WorkingDir: "{app}"; Flags: nowait runhidden; StatusMsg: "Запуск сервера Inventera..."
Filename: "http://127.0.0.1:{code:GetApplicationPort}"; Description: "Открыть Inventera"; Flags: shellexec nowait postinstall skipifsilent
[UninstallRun]
Filename: "{sys}\taskkill.exe"; Parameters: "/F /IM ""{#MyAppExeName}"""; Flags: runhidden waituntilterminated; RunOnceId: "StopInventera"
Filename: "{sys}\netsh.exe"; Parameters: "advfirewall firewall delete rule name=""Inventera Web"""; Flags: runhidden waituntilterminated; RunOnceId: "RemoveInventeraFirewallRule"
[Code]
var
ModePage: TInputOptionWizardPage;
PostgreSqlInstallPage: TInputOptionWizardPage;
AdminPage: TInputQueryWizardPage;
PortsPage: TInputQueryWizardPage;
DatabasePage: TInputQueryWizardPage;
ExistingPsqlPage: TInputFileWizardPage;
AutomaticAdminPassword: String;
function IsAutomaticMode: Boolean;
begin
Result := ModePage.SelectedValueIndex = 0;
end;
function ShouldInstallPostgreSql: Boolean;
begin
Result := IsAutomaticMode or (PostgreSqlInstallPage.SelectedValueIndex = 0);
end;
function GetPostgreSqlPort(Param: String): String;
begin
Result := Trim(PortsPage.Values[0]);
end;
function GetApplicationPort(Param: String): String;
begin
Result := Trim(PortsPage.Values[1]);
end;
function GetDatabaseName: String;
begin
if IsAutomaticMode then
Result := 'Inv_Web'
else
Result := Trim(DatabasePage.Values[0]);
end;
function GetDatabaseUser: String;
begin
if IsAutomaticMode then
Result := 'invwebuser'
else
Result := Trim(DatabasePage.Values[1]);
end;
function GetDatabasePassword: String;
begin
if IsAutomaticMode then
Result := 'invwebpassword'
else
Result := DatabasePage.Values[2];
end;
function GetAdminUser: String;
begin
if IsAutomaticMode then
Result := 'postgres'
else
Result := Trim(AdminPage.Values[0]);
end;
function GetAdminPassword: String;
begin
if IsAutomaticMode then
Result := AutomaticAdminPassword
else
Result := AdminPage.Values[1];
end;
function QuoteArgument(const Value: String): String;
begin
Result := '"' + Value + '"';
end;
function GenerateAutomaticPassword: String;
const
Characters = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789!@#_-';
var
I: Integer;
begin
Result := 'Iv!';
for I := 1 to 29 do
Result := Result + Characters[Random(Length(Characters)) + 1];
end;
function FindExistingPsql: String;
var
Candidate: String;
begin
Result := '';
Candidate := ExpandConstant('{autopf}\PostgreSQL\18\bin\psql.exe');
if FileExists(Candidate) then begin Result := Candidate; Exit; end;
Candidate := ExpandConstant('{autopf}\PostgreSQL\17\bin\psql.exe');
if FileExists(Candidate) then begin Result := Candidate; Exit; end;
Candidate := ExpandConstant('{autopf}\PostgreSQL\16\bin\psql.exe');
if FileExists(Candidate) then begin Result := Candidate; Exit; end;
Candidate := ExpandConstant('{autopf}\PostgreSQL\15\bin\psql.exe');
if FileExists(Candidate) then begin Result := Candidate; Exit; end;
Candidate := ExpandConstant('{autopf}\PostgreSQL\14\bin\psql.exe');
if FileExists(Candidate) then begin Result := Candidate; Exit; end;
Candidate := ExpandConstant('{autopf}\PostgreSQL\13\bin\psql.exe');
if FileExists(Candidate) then begin Result := Candidate; Exit; end;
end;
function GetPsqlPath: String;
begin
if ShouldInstallPostgreSql then
Result := ExpandConstant('{autopf}\PostgreSQL\{#PostgreSqlMajor}\bin\psql.exe')
else
Result := Trim(ExistingPsqlPage.Values[0]);
end;
function IsValidPort(const Value: String; var PortNumber: Integer): Boolean;
begin
Result := TryStrToInt(Trim(Value), PortNumber) and
(PortNumber >= 1) and (PortNumber <= 65535);
end;
function IsValidIdentifier(const Value: String): Boolean;
var
I: Integer;
AllowedFirst: String;
AllowedRest: String;
begin
Result := False;
if Value = '' then
Exit;
AllowedFirst := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_';
AllowedRest := AllowedFirst + '0123456789';
if Pos(Value[1], AllowedFirst) = 0 then
Exit;
for I := 2 to Length(Value) do
if Pos(Value[I], AllowedRest) = 0 then
Exit;
Result := True;
end;
function IsSafePassword(const Value: String): Boolean;
begin
Result := (Value <> '') and
(Pos('"', Value) = 0) and
(Pos(#13, Value) = 0) and
(Pos(#10, Value) = 0);
end;
function IsTcpPortAvailable(PortNumber: Integer): Boolean;
var
PowerShellPath: String;
Parameters: String;
ExitCode: Integer;
begin
PowerShellPath := ExpandConstant('{sys}\WindowsPowerShell\v1.0\powershell.exe');
Parameters :=
'-NoProfile -NonInteractive -ExecutionPolicy Bypass -Command ' +
'"$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, ' +
IntToStr(PortNumber) +
'); try { $listener.Start(); $listener.Stop(); exit 0 } catch { exit 1 }"';
Result := Exec(
PowerShellPath,
Parameters,
'',
SW_HIDE,
ewWaitUntilTerminated,
ExitCode
) and (ExitCode = 0);
end;
procedure InitializeWizard;
begin
AutomaticAdminPassword := GenerateAutomaticPassword;
ModePage := CreateInputOptionPage(
wpWelcome,
'Режим установки',
'Выберите способ настройки Inventera',
'Автоматический режим использует готовые параметры базы. Ручной режим позволяет изменить их.',
True,
False
);
ModePage.Add('Автоматический');
ModePage.Add('Ручной');
ModePage.SelectedValueIndex := 0;
PostgreSqlInstallPage := CreateInputOptionPage(
ModePage.ID,
'Установка PostgreSQL',
'Нужно ли установить PostgreSQL?',
'Выберите «Нет», только если PostgreSQL уже установлен и доступен на этом компьютере.',
True,
False
);
PostgreSqlInstallPage.Add('Да, установить PostgreSQL из комплекта Inventera');
PostgreSqlInstallPage.Add('Нет, использовать существующий PostgreSQL');
PostgreSqlInstallPage.SelectedValueIndex := 0;
AdminPage := CreateInputQueryPage(
PostgreSqlInstallPage.ID,
'Администратор PostgreSQL',
'Укажите учётную запись администратора',
'Эта учётная запись нужна только для создания базы и пользователя Inventera.'
);
AdminPage.Add('Логин администратора PostgreSQL:', False);
AdminPage.Add('Пароль администратора PostgreSQL:', True);
AdminPage.Values[0] := 'postgres';
PortsPage := CreateInputQueryPage(
AdminPage.ID,
'Сетевые порты',
'Укажите порты PostgreSQL и Inventera',
'PostgreSQL и веб-приложение используют разные порты. Установщик проверит, что нужные порты свободны.'
);
PortsPage.Add('Порт PostgreSQL:', False);
PortsPage.Add('Порт веб-приложения Inventera:', False);
PortsPage.Values[0] := '5432';
PortsPage.Values[1] := '5050';
DatabasePage := CreateInputQueryPage(
PortsPage.ID,
'База данных Inventera',
'Укажите имя базы и учётную запись приложения',
'Предлагаемые значения уже заполнены и могут быть изменены.'
);
DatabasePage.Add('Имя базы данных:', False);
DatabasePage.Add('Логин пользователя базы:', False);
DatabasePage.Add('Пароль пользователя базы:', True);
DatabasePage.Values[0] := 'Inv_Web';
DatabasePage.Values[1] := 'invwebuser';
DatabasePage.Values[2] := 'invwebpassword';
ExistingPsqlPage := CreateInputFilePage(
DatabasePage.ID,
'Расположение PostgreSQL',
'Укажите psql.exe существующей установки',
'Файл psql.exe обычно находится в папке C:\Program Files\PostgreSQL\<версия>\bin.'
);
ExistingPsqlPage.Add('Путь к psql.exe:', 'Исполняемые файлы|*.exe|Все файлы|*.*', '.exe');
ExistingPsqlPage.Values[0] := FindExistingPsql;
end;
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False;
if IsAutomaticMode and
((PageID = PostgreSqlInstallPage.ID) or
(PageID = AdminPage.ID) or
(PageID = DatabasePage.ID) or
(PageID = ExistingPsqlPage.ID)) then
begin
Result := True;
Exit;
end;
if (not IsAutomaticMode) and
(PageID = ExistingPsqlPage.ID) and
ShouldInstallPostgreSql then
begin
Result := True;
end;
end;
function NextButtonClick(CurPageID: Integer): Boolean;
var
PostgreSqlPort: Integer;
ApplicationPort: Integer;
begin
Result := False;
if CurPageID = AdminPage.ID then
begin
if not IsValidIdentifier(Trim(AdminPage.Values[0])) then
begin
MsgBox('Введите корректный логин администратора PostgreSQL.', mbError, MB_OK);
Exit;
end;
if not IsSafePassword(AdminPage.Values[1]) then
begin
MsgBox('Введите пароль администратора. Двойные кавычки и переносы строк недопустимы.', mbError, MB_OK);
Exit;
end;
end;
if CurPageID = PortsPage.ID then
begin
if not IsValidPort(PortsPage.Values[0], PostgreSqlPort) then
begin
MsgBox('Порт PostgreSQL должен быть числом от 1 до 65535.', mbError, MB_OK);
Exit;
end;
if not IsValidPort(PortsPage.Values[1], ApplicationPort) then
begin
MsgBox('Порт Inventera должен быть числом от 1 до 65535.', mbError, MB_OK);
Exit;
end;
if PostgreSqlPort = ApplicationPort then
begin
MsgBox('PostgreSQL и Inventera не могут использовать один и тот же порт.', mbError, MB_OK);
Exit;
end;
if ShouldInstallPostgreSql and (not IsTcpPortAvailable(PostgreSqlPort)) then
begin
MsgBox(
'Порт PostgreSQL ' + IntToStr(PostgreSqlPort) + ' уже занят. Укажите другой порт.',
mbError,
MB_OK
);
Exit;
end;
if not IsTcpPortAvailable(ApplicationPort) then
begin
MsgBox(
'Порт Inventera ' + IntToStr(ApplicationPort) + ' уже занят. Укажите другой порт.',
mbError,
MB_OK
);
Exit;
end;
end;
if CurPageID = DatabasePage.ID then
begin
if not IsValidIdentifier(Trim(DatabasePage.Values[0])) then
begin
MsgBox('Имя базы может содержать только латинские буквы, цифры и знак подчёркивания.', mbError, MB_OK);
Exit;
end;
if not IsValidIdentifier(Trim(DatabasePage.Values[1])) then
begin
MsgBox('Логин базы может содержать только латинские буквы, цифры и знак подчёркивания.', mbError, MB_OK);
Exit;
end;
if not IsSafePassword(DatabasePage.Values[2]) then
begin
MsgBox('Введите пароль базы. Двойные кавычки и переносы строк недопустимы.', mbError, MB_OK);
Exit;
end;
end;
if CurPageID = ExistingPsqlPage.ID then
begin
if not FileExists(Trim(ExistingPsqlPage.Values[0])) then
begin
MsgBox('Укажите существующий файл psql.exe.', mbError, MB_OK);
Exit;
end;
end;
Result := True;
end;
function PostgreSqlInstallParameters: String;
var
Prefix: String;
begin
Prefix := ExpandConstant('{autopf}\PostgreSQL\{#PostgreSqlMajor}');
Result :=
'--mode unattended --unattendedmodeui minimal ' +
'--enable-components server,commandlinetools ' +
'--prefix ' + QuoteArgument(Prefix) + ' ' +
'--datadir ' + QuoteArgument(Prefix + '\data') + ' ' +
'--serverport ' + GetPostgreSqlPort('') + ' ' +
'--servicename InventeraPostgreSQL{#PostgreSqlMajor} ' +
'--superaccount ' + QuoteArgument(GetAdminUser) + ' ' +
'--superpassword ' + QuoteArgument(GetAdminPassword) + ' ' +
'--servicepassword ' + QuoteArgument(GetAdminPassword) + ' ' +
'--create_shortcuts 0';
end;
function DatabaseInitializationParameters: String;
begin
Result :=
'-NoProfile -NonInteractive -ExecutionPolicy Bypass -File ' +
QuoteArgument(ExpandConstant('{tmp}\initialize_inventera_database.ps1')) + ' ' +
'-PsqlPath ' + QuoteArgument(GetPsqlPath) + ' ' +
'-AdminUser ' + QuoteArgument(GetAdminUser) + ' ' +
'-AdminPassword ' + QuoteArgument(GetAdminPassword) + ' ' +
'-PostgreSqlPort ' + GetPostgreSqlPort('') + ' ' +
'-DatabaseName ' + QuoteArgument(GetDatabaseName) + ' ' +
'-DatabaseUser ' + QuoteArgument(GetDatabaseUser) + ' ' +
'-DatabasePassword ' + QuoteArgument(GetDatabasePassword) + ' ' +
'-ApplicationPort ' + GetApplicationPort('') + ' ' +
'-ConfigPath ' + QuoteArgument(ExpandConstant('{commonappdata}\Inventera\config.json'));
end;
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
ExitCode: Integer;
IgnoreExitCode: Integer;
Success: Boolean;
PowerShellPath: String;
PostgreSqlPort: Integer;
ApplicationPort: Integer;
begin
Result := '';
NeedsRestart := False;
Exec(
ExpandConstant('{sys}\taskkill.exe'),
'/F /IM "{#MyAppExeName}"',
'',
SW_HIDE,
ewWaitUntilTerminated,
IgnoreExitCode
);
if not IsValidPort(GetPostgreSqlPort(''), PostgreSqlPort) or
not IsValidPort(GetApplicationPort(''), ApplicationPort) then
begin
Result := 'Указаны некорректные сетевые порты.';
Exit;
end;
if ShouldInstallPostgreSql and (not IsTcpPortAvailable(PostgreSqlPort)) then
begin
Result := 'Порт PostgreSQL ' + IntToStr(PostgreSqlPort) + ' уже занят.';
Exit;
end;
if not IsTcpPortAvailable(ApplicationPort) then
begin
Result := 'Порт Inventera ' + IntToStr(ApplicationPort) + ' уже занят.';
Exit;
end;
if ShouldInstallPostgreSql then
begin
WizardForm.PreparingLabel.Caption :=
'Установка PostgreSQL. Это может занять несколько минут, пожалуйста, подождите...';
ExtractTemporaryFile(ExtractFileName(ExpandConstant('{#PostgreSqlInstaller}')));
Success := Exec(
ExpandConstant('{tmp}\postgesql.exe'),
PostgreSqlInstallParameters,
'',
SW_SHOW,
ewWaitUntilTerminated,
ExitCode
);
if (not Success) or (ExitCode <> 0) then
begin
Result := 'Не удалось установить PostgreSQL. Код ошибки: ' + IntToStr(ExitCode);
Exit;
end;
end;
WizardForm.PreparingLabel.Caption :=
'Создание базы данных и сохранение настроек Inventera...';
ExtractTemporaryFile('initialize_inventera_database.ps1');
PowerShellPath := ExpandConstant('{sys}\WindowsPowerShell\v1.0\powershell.exe');
Success := Exec(
PowerShellPath,
DatabaseInitializationParameters,
'',
SW_HIDE,
ewWaitUntilTerminated,
ExitCode
);
if (not Success) or (ExitCode <> 0) then
begin
Result :=
'Не удалось создать базу данных или сохранить настройки Inventera. Код ошибки: ' +
IntToStr(ExitCode);
Exit;
end;
end;