diff --git a/README.md b/README.md index 09d152f..240d308 100644 --- a/README.md +++ b/README.md @@ -17,25 +17,27 @@ The repository structure is as follows: ├── .devcontainer │ ├── devcontainer.json │ └── Dockerfile +├── .gitignore ├── LICENSE.md ├── README.md ├── resources │ ├── api -│ │ ├── pipelines -│ │ │ └── production -│ │ │ ├── flair-anonymizer -│ │ │ │ └── pipeline.json -│ │ │ └── full-paragraph -│ │ │ └── pipeline.json -│ │ └── static -│ │ └── logo256-text.ico +│ │ └── pipelines +│ │ └── production +│ │ ├── flair-anonymizer +│ │ │ └── pipeline.json +│ │ └── full-paragraph +│ │ └── pipeline.json │ ├── aymurai--py3-none-any.whl │ ├── banner.bmp │ ├── environment.yml +│ ├── favicon.ico │ ├── header.bmp │ ├── install.bat -│ ├── license.txt │ ├── run_server.bat +│ ├── terms_and_conditions_es.txt +│ ├── terms_and_conditions.txt +│ ├── tray_runner.py │ └── uninstall.bat └── scripts ├── headers @@ -89,7 +91,7 @@ If you are working in the development container, you can compile the installer u ```bash # Compile the installer -wine ~/.wine/drive_c/Program\ Files/NSIS/makensis.exe -- scripts/installer.nsi +wine ~/.wine/drive_c/Program\ Files/NSIS/makensis.exe /INPUTCHARSET UTF8 scripts/installer.nsi ``` The installer executable will be created in the `build` directory. diff --git a/resources/api/static/logo256-text.ico b/resources/api/static/logo256-text.ico deleted file mode 100644 index 9ff327f..0000000 Binary files a/resources/api/static/logo256-text.ico and /dev/null differ diff --git a/resources/aymurai-1.1.10-py3-none-any.whl b/resources/aymurai-1.1.12-py3-none-any.whl similarity index 63% rename from resources/aymurai-1.1.10-py3-none-any.whl rename to resources/aymurai-1.1.12-py3-none-any.whl index 04d4acb..1888300 100644 Binary files a/resources/aymurai-1.1.10-py3-none-any.whl and b/resources/aymurai-1.1.12-py3-none-any.whl differ diff --git a/resources/banner.bmp b/resources/banner.bmp index e48a232..25ad7e9 100644 Binary files a/resources/banner.bmp and b/resources/banner.bmp differ diff --git a/resources/environment.yml b/resources/environment.yml index 0a3c4c3..629f9dc 100644 --- a/resources/environment.yml +++ b/resources/environment.yml @@ -2,10 +2,12 @@ name: aymurai-backend channels: - conda-forge - defaults -dependencies: - - python=3.10 - - pip=23.2.1 - - openjdk==22.0.1 - - git==2.46.2 - - pip: - - ./aymurai-1.1.10-py3-none-any.whl +dependencies: + - python=3.10 + - pip=23.2.1 + - openjdk==22.0.1 + - git==2.46.2 + - pip: + - ./aymurai-1.1.12-py3-none-any.whl + - pillow==10.4.0 + - pystray==0.19.5 diff --git a/resources/favicon.ico b/resources/favicon.ico new file mode 100644 index 0000000..7e58946 Binary files /dev/null and b/resources/favicon.ico differ diff --git a/resources/header.bmp b/resources/header.bmp index 2ad4981..87f5e61 100644 Binary files a/resources/header.bmp and b/resources/header.bmp differ diff --git a/resources/install.bat b/resources/install.bat index 02525d8..6ac133c 100644 --- a/resources/install.bat +++ b/resources/install.bat @@ -7,6 +7,13 @@ if not defined CONDA_DIR ( set "CONDA_DIR=%USERPROFILE%\miniconda3" ) +REM Define the writable application data directory under the current user profile +if not defined LOCALAPPDATA ( + echo Error: LOCALAPPDATA environment variable is not defined. + exit /b 1 +) +set "AYMURAI_DATA_DIR=%LOCALAPPDATA%\AymurAI" + REM Check if Miniconda is already installed if exist "%CONDA_DIR%" ( echo Miniconda is already installed. @@ -66,32 +73,6 @@ if not exist "%SCRIPT_DIR%full_env" ( ) ) - REM Workaround to fix python-magic issue - REM https://github.com/ahupp/python-magic/issues/248 - call pip uninstall -y python-magic - if errorlevel 1 ( - echo Conda environment creation failed. - exit /b 1 - ) - - call pip install python-magic==0.4.27 - if errorlevel 1 ( - echo Conda environment creation failed. - exit /b 1 - ) - call pip install python-magic-bin==0.4.14 - if errorlevel 1 ( - echo Conda environment creation failed. - exit /b 1 - ) - - REM Check if libmagic path is already in the PATH environment variable - powershell -Command "if (-not $env:PATH.Contains('magic\libmagic')) { Write-Host 'libmagic path not found in PATH, adding it...'; $sitePackages = (Get-ChildItem -Path (Get-Command python).Source).Directory.Parent.FullName + '\aymurai-backend\Lib\site-packages'; $libmagicPath = Join-Path -Path $sitePackages -ChildPath 'magic\libmagic'; [System.Environment]::SetEnvironmentVariable('PATH', $env:PATH + ';' + $libmagicPath, [System.EnvironmentVariableTarget]::User); Write-Host 'libmagic path added to PATH.' } else { Write-Host 'libmagic path already exists in PATH.' }" - if errorlevel 1 ( - echo Conda environment creation failed. - exit /b 1 - ) - REM Instal forked version of textract to fix this issue REM https://github.com/deanmalmgren/textract/issues/313 call pip uninstall -y textract @@ -128,7 +109,7 @@ if not exist "%SCRIPT_DIR%api\resources\api" ( mkdir "%SCRIPT_DIR%api\resources\api" ) -REM Move pipelines and static directories to the api directory +REM Move pipelines directory to the api directory if not exist "%SCRIPT_DIR%api\resources\pipelines" ( move "%SCRIPT_DIR%pipelines" "%SCRIPT_DIR%api\resources\pipelines" ) else ( @@ -136,13 +117,6 @@ if not exist "%SCRIPT_DIR%api\resources\pipelines" ( rmdir /s /q "%SCRIPT_DIR%pipelines" ) ) -if not exist "%SCRIPT_DIR%api\resources\api\static" ( - move "%SCRIPT_DIR%static" "%SCRIPT_DIR%api\resources\api\static" -) else ( - if exist "%SCRIPT_DIR%static" ( - rmdir /s /q "%SCRIPT_DIR%static" - ) -) REM Download the api module from the repository if not exist "%SCRIPT_DIR%api\__init__.py" ( @@ -152,14 +126,37 @@ if not exist "%SCRIPT_DIR%api\main.py" ( curl --ssl-no-revoke -o "%SCRIPT_DIR%api\main.py" https://raw.githubusercontent.com/AymurAI/backend/refs/heads/dev/aymurai/api/main.py ) -REM Define the cache directories -set "AYMURAI_CACHE_BASEPATH=%SCRIPT_DIR%cache\aymurai" -set "DISKCACHE_ROOT=%SCRIPT_DIR%cache\diskcache" -set "FLAIR_CACHE_ROOT=%SCRIPT_DIR%models\flair" -set "TFHUB_CACHE_DIR=%SCRIPT_DIR%models\tfhub" +REM Prepare writable cache/model/data directories under LOCALAPPDATA +set "AYMURAI_CACHE_BASEPATH=%AYMURAI_DATA_DIR%\cache\aymurai" +set "DISKCACHE_ROOT=%AYMURAI_DATA_DIR%\cache\diskcache" +set "FLAIR_CACHE_ROOT=%AYMURAI_DATA_DIR%\models\flair" +set "TFHUB_CACHE_DIR=%AYMURAI_DATA_DIR%\models\tfhub" +set "AYMURAI_SQLITE_DIR=%AYMURAI_DATA_DIR%\data\sqlite" +set "AYMURAI_DATABASE_FILE=%AYMURAI_SQLITE_DIR%\database.db" +set "AYMURAI_LOG_DIR=%AYMURAI_DATA_DIR%\logs" +set "AYMURAI_LOG_FILE=%AYMURAI_LOG_DIR%\backend.log" + +for %%D in ( + "%AYMURAI_DATA_DIR%" + "%AYMURAI_DATA_DIR%\cache" + "%AYMURAI_CACHE_BASEPATH%" + "%DISKCACHE_ROOT%" + "%AYMURAI_DATA_DIR%\models" + "%FLAIR_CACHE_ROOT%" + "%TFHUB_CACHE_DIR%" + "%AYMURAI_DATA_DIR%\data" + "%AYMURAI_SQLITE_DIR%" + "%AYMURAI_LOG_DIR%" +) do ( + if not exist "%%~D" mkdir "%%~D" +) REM Define the RESOURCES_BASEPATH environment variable set "RESOURCES_BASEPATH=%SCRIPT_DIR%api\resources" +set "AYMURAI_TRAY_ICON=%SCRIPT_DIR%resources\app\build\app\favicon.ico" + +REM Point SQLAlchemy to the writable SQLite database location (convert backslashes to forward slashes) +set "SQLALCHEMY_DATABASE_URI=sqlite:///%AYMURAI_DATABASE_FILE:\=/%%" REM Run the api main.py file to download the models call python "%SCRIPT_DIR%api\main.py" diff --git a/resources/license.txt b/resources/license.txt deleted file mode 100644 index 019cfc0..0000000 --- a/resources/license.txt +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) 2025 AymurAI - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/resources/run_server.bat b/resources/run_server.bat index ef9f5a1..daec027 100644 --- a/resources/run_server.bat +++ b/resources/run_server.bat @@ -2,14 +2,6 @@ REM Define the directory where the script is located set "SCRIPT_DIR=%~dp0" -REM Check for Administrator privileges -net session >nul 2>&1 -if %errorLevel% neq 0 ( - echo Requesting administrative privileges... - powershell -Command "Start-Process -Verb runAs -WorkingDirectory '%SCRIPT_DIR%' -FilePath '%~f0'" - exit /b -) - REM Force working directory to script location cd /d "%SCRIPT_DIR%" @@ -28,14 +20,41 @@ call "%CONDA_DIR%\Scripts\activate.bat" %ENV_NAME% REM Set PYTHONUTF8=1 to enable UTF-8 encoding set "PYTHONUTF8=1" -REM Define the environment variables -set "AYMURAI_CACHE_BASEPATH=%SCRIPT_DIR%cache\aymurai" -set "DISKCACHE_ROOT=%SCRIPT_DIR%cache\diskcache" -set "FLAIR_CACHE_ROOT=%SCRIPT_DIR%models\flair" -set "TFHUB_CACHE_DIR=%SCRIPT_DIR%models\tfhub" -set "SQLALCHEMY_DATABASE_URI=sqlite:///%SCRIPT_DIR%api\resources\cache\sqlite\database.db" +REM Ensure LOCALAPPDATA is defined and establish a writable data root +if not defined LOCALAPPDATA ( + echo Error: LOCALAPPDATA environment variable is not defined. + exit /b 1 +) +set "AYMURAI_DATA_DIR=%LOCALAPPDATA%\AymurAI" +set "AYMURAI_CACHE_BASEPATH=%AYMURAI_DATA_DIR%\cache\aymurai" +set "DISKCACHE_ROOT=%AYMURAI_DATA_DIR%\cache\diskcache" +set "FLAIR_CACHE_ROOT=%AYMURAI_DATA_DIR%\models\flair" +set "TFHUB_CACHE_DIR=%AYMURAI_DATA_DIR%\models\tfhub" +set "AYMURAI_SQLITE_DIR=%AYMURAI_DATA_DIR%\data\sqlite" +set "AYMURAI_DATABASE_FILE=%AYMURAI_SQLITE_DIR%\database.db" +set "AYMURAI_LOG_DIR=%AYMURAI_DATA_DIR%\logs" +set "AYMURAI_LOG_FILE=%AYMURAI_LOG_DIR%\backend.log" +set "AYMURAI_TRAY_ICON=%SCRIPT_DIR%\resources\app\build\app\favicon.ico" + +for %%D in ( + "%AYMURAI_DATA_DIR%" + "%AYMURAI_DATA_DIR%\cache" + "%AYMURAI_CACHE_BASEPATH%" + "%DISKCACHE_ROOT%" + "%AYMURAI_DATA_DIR%\models" + "%FLAIR_CACHE_ROOT%" + "%TFHUB_CACHE_DIR%" + "%AYMURAI_DATA_DIR%\data" + "%AYMURAI_SQLITE_DIR%" + "%AYMURAI_LOG_DIR%" +) do ( + if not exist "%%~D" mkdir "%%~D" +) + +REM Define the environment variables consumed by the backend set "RESOURCES_BASEPATH=%SCRIPT_DIR%api\resources" -set "LIBREOFFICE_BIN=C:\\Program Files\\LibreOffice\\program\\soffice.exe" +set "SQLALCHEMY_DATABASE_URI=sqlite:///%AYMURAI_DATABASE_FILE:\=/%%" +set "LIBREOFFICE_BIN=C:\Program Files\LibreOffice\program\soffice.exe" -REM Run the application -call python -m uvicorn --app-dir=api main:api --reload --host=0.0.0.0 --port=8899 +REM Run the tray launcher that manages the backend process +call python "%SCRIPT_DIR%tray_runner.py" diff --git a/resources/terms_and_conditions.txt b/resources/terms_and_conditions.txt new file mode 100644 index 0000000..6f4e7c8 --- /dev/null +++ b/resources/terms_and_conditions.txt @@ -0,0 +1,29 @@ +TERMS AND CONDITIONS OF USE OF THE AYMURAI TOOL + +1. INFORMATION ABOUT THE OWNER OF THE TOOL: The AymurAI tool (hereinafter, the "Tool") and all rights thereto are the property of DataGnero - Observatorio de Datos con Perspectiva de Gnero A.C, a non-profit organization, CUIT: 30-71739406-9, registered under the laws of the Argentine Republic (hereinafter "DataGnero"). + +2. ACCEPTANCE OF THE TERMS OF USE OF THE TOOL: DataGnero grants you a free, limited, non-exclusive, non-transferable, and temporary license to use the Tool, subject to your strict compliance with these terms and conditions. By accessing and using the Tool, you (hereinafter, the "User/s") acknowledge that you have read, understood, and expressly accept these Terms and Conditions of Use of the Tool (hereinafter, the "Terms"). By using the Tool, the User assumes sole responsibility for its management and utility in full accordance with these Terms. IF THE USER DOES NOT AGREE WITH THESE TERMS, PLEASE REFRAIN FROM USING THE TOOL. Likewise, total or partial non-compliance with these Terms by a User will result in the immediate revocation of the license to use the Tool for that User, who must immediately cease its use. + +3. DESCRIPTION OF THE TOOL: To use the Tool, each User must download and install it on their systems, devices, and/or servers. The Tool is a software program designed for members of the judiciary who, by uploading judicial rulings and/or resolutions to the Tool, can anonymize personal data in such documents and generate structured databases based on common parameters for use in analysis, statistics, management, and public policy. The Tool operates offline, meaning Users can download and use it without needing to be connected to the Internet. The Tool also does not allow different Users to connect with each other, so the data and/or documents uploaded to the Tool by one User are not accessible by other Users (nor by DataGnero). + +4. PURPOSE. OBJECTIVES. GRATUITY: The license to use the Tool is granted free of charge, without requiring any payment or consideration - direct or indirect - for downloading and using the Tool. Likewise, and as an essential condition of these Terms, Users may not require any payment or consideration from third parties for the use of the Tool. The purpose of the Tool is to assist Users in the administration of justice, so that through its use, Users can improve transparency and the administration of justice, with the aim of achieving the common good of society, in an altruistic, selfless, beneficial, and non-profit manner. Therefore, only public sector entities and/or individuals and/or non-profit entities pursuing the aforementioned purposes are permitted to download and use the Tool. + +5. PROHIBITED USES: It is expressly prohibited to download and/or use the Tool in any way for purposes that do not align with the altruistic, social, beneficial, and non-profit purpose for which the Tool was developed, for example: (i) activities aimed at pursuing personal interests; (ii) activities contrary to the pursuit of the common good of society and the transparency of justice; (iii) activities for profit, commercial, transactional, etc.; (iv) acts that violate these Terms and/or applicable law and/or cause or may reasonably cause harm to third parties. This list is not exhaustive and may be modified by DataGnero as deemed appropriate. + +6. PERSONAL DATA. ANONYMIZATION. NO PROCESSING BY DataGnero: The Tool is provided to Users "free" of data, with Users, under their sole responsibility and as "Data Controllers," being those who upload, store, process, and/or handle data - personal and/or non-personal - using the Tool. DataGnero is not responsible for, nor a processor, assignor, or assignee of such data. DataGnero - neither directly nor through third parties - does not access, monitor, store, assign, download, host, process, transfer, handle, or intervene in the processing of personal data and/or other information entered by the User into the Tool or the results generated by the Tool, regardless of whether such information is anonymized (or not) at the time of using the Tool. The download of the Tool cannot be understood as an activity of personal data processing by DataGnero. + +7. NO WARRANTY: Due to the still experimental nature of the Tool, its intrinsic characteristics, and the gratuitousness of this license, DataGnero provides no warranty regarding the Tool or the consequences of its use, so Users accept the installation and use of the Tool at their own and exclusive risk, releasing DataGnero from all liability in this regard. In particular, and without limitation, DataGnero: (i) DOES NOT guarantee that the Tool adequately fulfills any functionality, not even those described in these terms; (ii) DOES NOT guarantee the suitability or adaptability of the Tool for a specific computer system or for any particular purpose; (iii) DOES NOT guarantee that the Tool will fulfill any objective or that it complies with the legislation applicable to the User; (iv) DOES NOT guarantee that the "results" provided by the Tool are accurate, precise, or harmless, nor is it responsible for the consequences arising from the use of such results by Users; (v) DOES NOT guarantee the continuous and uninterrupted availability of the Tool, nor is it responsible for any damages or losses that may result from such interruptions or alterations; and (vi) DOES NOT guarantee the security or inviolability of the Tool. The Tool and its functionalities are provided as is, without any express or implied warranties. + +8. USER RESPONSIBILITY: The User is absolutely and exclusively responsible for any operation performed with the Tool. The User acknowledges and accepts that access to and use of the Tool (as well as the uploading of data and information) is carried out under their sole responsibility and risk, holding DataGnero harmless as indicated in Clause 9 of these Terms. Likewise, the User declares, states, and guarantees that they do not and will not violate applicable law, including, by way of example and without limitation: (i) personal data protection laws; (ii) confidentiality laws; (iii) industrial property and trademark laws; (iv) any other legislation determined by DataGnero. + +9. INDEMNITY: The User agrees to defend, indemnify, and hold harmless DataGnero, its affiliates, and their respective directors, officers, employees, and agents from damages, judicial and/or extrajudicial claims, and expenses arising from the use of the Tool by said User, including attorneys' fees, resulting from the use of this Tool, as well as from damages, claims, and expenses arising as a consequence of a violation by the User of these Terms. + +10. TRADEMARKS. INTELLECTUAL PROPERTY. LIMITED LICENSE. PROHIBITION OF DERIVATIVE WORKS: Notwithstanding the gratuitous nature of the license granted by these Terms, the User acknowledges and accepts that all intellectual and/or industrial property rights over the Tool and/or associated with the Tool are the exclusive property of DataGnero. Except for the license to use the Tool in accordance with these Terms, this does not grant Users any intellectual property rights, license, trademark, patent, trade secret, or any other rights over the Tool, nor over any improvements and/or derivative works of the Tool. Users are expressly prohibited from: (i) making modifications, derivative works, reverse engineering, and/or disassembling the Tool; (ii) applying for themselves and/or third parties the registration of the Tool and/or trademarks and/or rights associated with it as their own; and/or claiming any rights of any kind over the Tool, its derivatives, or its components; (iii) commercializing the Tool and/or granting sublicenses - even free of charge - and/or any other permission to use the Tool to third parties; (iv) performing any act that violates these Terms and/or applicable law and/or harms and/or infringes the intellectual and/or industrial property rights of DataGnero over the Tool. + +11. LIMITATION OF LIABILITY: Under no circumstances shall DataGnero and/or any other party involved in the creation, development, and dissemination of this Tool be liable for direct, indirect, special, incidental, or consequential damages, including, without limitation, damages for loss of data, loss of profits, security breaches, damages from erroneous or inaccurate results, and/or other direct or indirect consequences arising from the Tool, its use and/or failures, and/or the inability to use the Tool, even if DataGnero or an authorized representative of DataGnero has been advised of the possibility of such damages. + +12. INDEPENDENCE OF THE PARTIES: Both DataGnero and the Users acknowledge and accept that they act at all times independently and autonomously. Acceptance of these Terms and/or use of the Tool shall not, under any circumstances, be interpreted as the creation of a partnership, employment relationship, agency, mandate, representation, association, franchise, concession, or any relationship of subordination or dependency between DataGnero and the Users. + +13. APPLICABLE LAW AND JURISDICTION: These Terms are governed by Argentine law. Any dispute arising from their application, interpretation, execution, or validity shall be resolved by the competent ordinary national civil courts located in the Autonomous City of Buenos Aires. + +These Terms were last updated in September 2025. diff --git a/resources/terms_and_conditions_es.txt b/resources/terms_and_conditions_es.txt new file mode 100644 index 0000000..f28bf35 --- /dev/null +++ b/resources/terms_and_conditions_es.txt @@ -0,0 +1,29 @@ +TRMINOS Y CONDICIONES DE USO DE LA HERRAMIENTA AYMURAI. + +1. INFORMACIN DEL TITULAR DE LA HERRAMIENTA: La herramienta AymurAI (en adelante, la "Herramienta") y todos los derechos sobre la misma son de propiedad de DataGnero - Observatorio de Datos con Perspectiva de Gnero A.C, organizacin sin fines de lucro, CUIT: 30-71739406-9, inscripta bajo las leyes de la Repblica Argentina (en adelante "DataGnero"). + +2. ACEPTACIN DE LOS TRMINOS DE USO DE LA HERRAMIENTA: DataGnero otorga a Usted una licencia gratuita, limitada, no exclusiva, intransferible y temporal de uso de la Herramienta, sujeta al estricto cumplimiento de estos trminos y condiciones por parte de Usted. Al acceder y utilizar la Herramienta, Ud. (en adelante, el/los "Usuario/s") reconoce haber ledo, comprendido y acepta expresamente estos Trminos y Condiciones de Uso de la Herramienta (en adelante, los "Trminos"). Al utilizar la Herramienta, el Usuario asume la responsabilidad en forma exclusiva del manejo y utilidad de sta en un todo de acuerdo con estos Trminos. SI EL USUARIO NO EST DE ACUERDO CON ESTOS TRMINOS, POR FAVOR DEBE ABSTENERSE DE USAR LA HERRAMIENTA. Asimismo, el incumplimiento total o parcial de estos Trminos por parte de un Usuario implicarn la revocacin inmediata de la licencia de uso de la Herramienta para dicho Usuario, debiendo el Usuario cesar inmediatamente en el uso de la misma. + +3. DESCRIPCIN DE LA HERRAMIENTA: Para utilizar la Herramienta, cada Usuario debe descargar e instalar la misma en sus sistemas, equipos y/o servidores. La Herramienta es un programa de software pensado para miembros del poder judicial que, a partir de sentencias judiciales y/o resoluciones judiciales cargadas en la Herramienta misma por sus Usuarios, anonimiza datos personales de tales documentos, y genera bases de datos estructuradas a partir de parmetros comunes en las mismas, para su uso en anlisis, estadsticas, gestin y polticas pblicas. La Herramienta opera offline, es decir, los Usuarios la pueden descargar y utilizarla sin necesidad de estar conectados a Internet. La Herramienta tampoco permite a los distintos Usuarios de la misma conectarse entre s, con lo cual los datos y/o documentos cargados en la Herramienta por un Usuario no son accesibles por otros Usuarios (ni tampoco por DataGnero). + +4. FINALIDAD. PROPSITOS. GRATUIDAD: La licencia de uso de la Herramienta es otorgada en forma gratuita, sin requerir pago ni contraprestacin de ningn tipo -ni directa ni indirecta- por la descarga y uso de la Herramienta. Del mismo modo, y como condicin ineludible de los presente Trminos, los Usuarios no podrn requerir pago ni contraprestacin alguna a terceros para el uso de la Herramienta. El propsito de la Herramienta es colaborar con los Usuarios en la administracin de justicia, de modo tal que mediante el uso de la misma los Usuarios puedan mejorar la transparencia y la administracin de justicia, con el objetivo de lograr el bien comn de la sociedad, en forma altruista, desinteresada, benfica y sin fines de lucro. Por ende, slo tienen permitido descargar y usar la Herramienta entidades del sector pblico y/o personas fsicas y/o entidades particulares sin fines de lucro, que persigan los propsitos antes referidos. + +5. USOS PROHIBIDOS: Queda expresamente prohibido descargar y/o utilizar de cualquier modo la Herramienta para fines que no se ajusten con el propsito altruista, social, benfico y sin fines de lucro por el cual se desarroll la Herramienta, por ejemplo: (i) actividades que tengan como fin la persecucin de un inters personal; (ii) actividades que sean contrarias a la bsqueda del bien comn de la sociedad y la transparencia de la justicia; (iii) actividades con fines de lucro, comerciales, transaccionales, etc.; (iv) actos que infrinjan estos Trminos y/o la legislacin aplicable y/o causen y/o puedan razonablemente causar daos a terceros. Esta enumeracin no es taxativa, pudiendo DataGnero modificarla cuando as lo considere oportuno. + +6. DATOS PERSONALES. ANONIMIZACIN. NO TRATAMIENTO POR DATA GNERO: La Herramienta es provista a los Usuarios libre de datos, siendo los Usuarios, bajo su exclusiva responsabilidad y en carcter de Responsables de tratamiento quienes carguen, almacenen, procesen y/o traten datos -personales y/o no personales- mediante la Herramienta. DataGnero no es responsable ni encargado de tratamiento ni cedente ni cesionario de tales datos. DataGnero -ni por s ni a travs de terceros- no accede, monitorea, almacena, cede, descarga, hostea, procesa, transfiere, trata, ni interviene en el tratamiento de los datos personales y/u otra informacin volcada por el Usuario en la Herramienta ni los resultados que arroje la Herramienta, independientemente de si dicha informacin se encuentra anonimizada (o no) al momento de utilizar la Herramienta, no realizando DataGnero ninguna de las acciones previamente detalladas. La descarga de la Herramienta no podr ser entendida como una actividad de procesamiento de datos personales por parte de DataGnero. + +7. INEXISTENCIA DE GARANTA: Debido al carcter an experimental de la Herramienta, las caractersticas intrnsecas de la misma y la gratuidad de la presente licencia de uso de la misma, DataGnero no otorga garanta alguna respecto de la misma ni de las consecuencias del uso de la misma, por lo que los Usuarios aceptan la instalacin y uso de la Herramienta bajo su propio y exclusivo riesgo, liberando a DataGnero de toda responsabilidad al respecto. En particular, y sin que implique limitacin alguna, DataGnero: (i) NO garantiza que la Herramienta cumpla adecuadamente funcionalidad alguna, ni siquiera las descriptas en estos trminos; (ii) NO garantiza la adecuacin ni adaptabilidad de la Herramienta para un sistema informtico especfico ni para un fin o propsito en particular; (iii) NO garantiza que la Herramienta cumplir con objetivo alguno ni que la misma cumple con la legislacin aplicable al Usuario; (iv) NO garantiza que los resultados provistos por la herramienta sean exactos, precisos ni inocuos, ni se responsabiliza por las consecuencias derivadas del uso de tales resultados por los Usuarios; (v) NO garantiza la disponibilidad continua e ininterrumpida de la Herramienta, ni se responsabiliza por eventuales daos o perjuicios que pudieran derivarse de tales interrupciones o alteraciones; y (vi) NO garantiza la seguridad ni inviolabilidad informtica de la Herramienta. La Herramienta y sus funcionalidades son provistas en su estado actual sin garantas de ningn tipo ya sean expresas o implcitas. + +8. RESPONSABILIDAD DEL USUARIO. Es responsabilidad absoluta y exclusiva del Usuario cualquier operacin efectuada con la Herramienta. El Usuario reconoce y acepta que el acceso y uso de la Herramienta (as como la carga de datos e informacin) se realiza bajo su exclusiva responsabilidad y riesgo, manteniendo indemne a DataGnero de acuerdo con lo indicado en la Clusula 9 de estos Trminos. Asimismo, el Usuario declara, manifiesta y garantiza que no infringe ni infringir la legislacin aplicable, incluyendo, a modo de ejemplo, y sin que se entienda como limitativo: (i) legislacin de proteccin de datos personales; (ii) legislacin de confidencialidad; (iii) legislacin de propiedad industrial y marcas; (iv) cualquier otra legislacin que determine DataGnero. + +9. INDEMNIDAD: El Usuario acuerda defender, indemnizar y mantener indemne a DataGnero, sus afiliadas y sus respectivos directores, funcionarios, empleados y agentes contra daos, reclamos judiciales y/o extrajudiciales y gastos derivados del uso de la Herramienta por dicho Usuario, incluyendo los honorarios de abogados, que surjan del uso de esta Herramienta, como asimismo contra los daos, reclamos y gastos que surjan como consecuencia de una violacin por parte del Usuario de los presentes Trminos. + +10. MARCAS. PROPIEDAD INTELECTUAL. LICENCIA LIMITADA. PROHIBICIN DERECHO DE OBRAS DERIVADAS: Sin perjuicio del carcter gratuito de la licencia otorgada mediante estos Trminos, el Usuario reconoce y acepta que todos los derechos de propiedad intelectual y/o industrial sobre la Herramienta y/o asociados a la Herramienta son de propiedad exclusiva y excluyente de DataGnero. Salvo por la licencia de uso de la Herramienta de acuerdo con estos Trminos, la presente no confiere a los Usuarios derecho alguno de propiedad intelectual, licencia, marca, patente, secreto comercial ni de ningn otro tipo sobre la misma, ni tampoco sobre eventuales mejoras y/u obras derivadas de la Herramienta. Queda expresamente prohibido a los Usuarios: (i) realizar modificaciones, obras derivadas, ingeniera inversa y/o desensamblar la Herramienta; (ii) solicitar para s y/o para terceros el registro de la Herramienta y/o de las marcas y/o derechos asociados a la misma como de su propiedad; y/o reclamar derechos de cualquier tipo sobre la Herramienta, sus derivados o sus componentes; (iii) comercializar la Herramienta y/u otorgar sublicencias -incluso gratuitas- y/o cualquier otro permiso de uso de la Herramienta a terceros; (iv) realizar cualquier acto que infrinja estos Trminos y/o la legislacin aplicable y/o perjudique y/o infrinja los derechos de propiedad intelectual y/o industrial de DataGnero sobre la Herramienta. + +11. LIMITACIN DE RESPONSABILIDAD: En ninguna circunstancia DataGnero y/o cualquier otra parte envuelta en la creacin, desarrollo y divulgacin de esta Herramienta ser responsable por los daos directos, indirectos, especiales, incidentales o emergentes, incluyendo sin limitacin, a ttulo enunciativo, daos por prdida de datos, por lucro cesante, por violaciones de seguridad informtica, daos por resultados errneos, inexactos, y/u otras consecuencias directas ni indirectas que emerjan de la Herramienta, del uso y/o fallas de la Herramienta y/o de la incapacidad para utilizar la Herramienta, aun cuando DataGnero o un representante autorizado de DataGnero hubieran advertido sobre los posibles daos. + +12. INDEPENDENCIA DE LAS PARTES: Tanto DataGnero como los Usuarios reconocen y aceptan que actan en todo momento de manera independiente y autnoma. La aceptacin de los presentes Trminos y/o utilizacin de la Herramienta no deber interpretarse, en ningn caso, como la creacin de una sociedad, relacin laboral, agencia, mandato, representacin, asociacin, franquicia, concesin, ni vnculo de subordinacin o dependencia alguna entre DataGnero y los Usuarios. + +13. LEGISLACIN APLICABLE Y JURISDICCIN: Estos Trminos se rigen por la ley argentina. Toda controversia derivada de su aplicacin, interpretacin, ejecucin o validez ser resuelta por los tribunales nacionales civiles ordinarios competentes, con asiento en la Ciudad Autnoma de Buenos Aires. + +Estos Trminos fueron actualizados por ltima vez en septiembre 2025. diff --git a/resources/tray_runner.py b/resources/tray_runner.py new file mode 100644 index 0000000..9fc7f08 --- /dev/null +++ b/resources/tray_runner.py @@ -0,0 +1,511 @@ +""" +System tray launcher for the AymurAI backend API. + +This script starts the Uvicorn server, writes logs to the per-user data +directory, and exposes basic controls via a Windows notification-area icon. +""" + +from __future__ import annotations + +import contextlib +import os +import signal +import subprocess +import sys +import threading +import time +from functools import partial +from pathlib import Path +from typing import TextIO + +import pystray +from PIL import Image +from pystray import Menu, MenuItem + +SCRIPT_DIR = Path(__file__).resolve().parent +DATA_DIR = Path( + os.environ.get("AYMURAI_DATA_DIR", Path.home() / "AppData" / "Local" / "AymurAI") +) +LOG_DIR = Path(os.environ.get("AYMURAI_LOG_DIR", DATA_DIR / "logs")) +LOG_FILE = Path(os.environ.get("AYMURAI_LOG_FILE", LOG_DIR / "backend.log")) +ICON_PATH = Path( + os.environ.get( + "AYMURAI_TRAY_ICON", + SCRIPT_DIR / "resources" / "app" / "build" / "app" / "favicon.ico", + ) +) + +DEFAULT_LANGUAGE = "es" +REGISTRY_PATH = r"Software\AymurAI\Installer" +REGISTRY_VALUE = "Language" + +LANGUAGE_ALIASES = { + "en": "en", + "english": "en", + "1033": "en", + "es": "es", + "spa": "es", + "spanish": "es", + "1034": "es", + "3082": "es", +} + +LANGUAGE_STRINGS = { + "tray_title_template": { + "en": "AymurAI Backend ({status})", + "es": "Backend de AymurAI ({status})", + }, + "status_starting": {"en": "Starting", "es": "Iniciando"}, + "status_running": {"en": "Running", "es": "En ejecución"}, + "status_stopped": {"en": "Stopped", "es": "Detenido"}, + "notify_title": {"en": "AymurAI Backend", "es": "Backend de AymurAI"}, + "notify_restarted": { + "en": "AymurAI backend restarted", + "es": "El backend de AymurAI se reinicio", + }, + "notify_unexpected_stop": { + "en": "AymurAI backend stopped. Use the tray icon to restart it.", + "es": "El backend de AymurAI se detuvo. Usa el icono de la bandeja para reiniciarlo.", + }, + "menu_open_log": {"en": "Open log", "es": "Abrir log"}, + "menu_open_logs_folder": { + "en": "Open log folder", + "es": "Abrir carpeta de logs", + }, + "menu_restart_backend": { + "en": "Start / Restart backend", + "es": "Iniciar / reiniciar backend", + }, + "menu_stop_backend": {"en": "Stop backend", "es": "Detener backend"}, + "menu_quit": {"en": "Quit", "es": "Salir"}, +} + +BACKEND_COMMAND = [ + sys.executable, + "-m", + "uvicorn", + "--app-dir=api", + "main:api", + "--host=0.0.0.0", + "--port=8899", +] + +CREATE_NO_WINDOW = getattr(subprocess, "CREATE_NO_WINDOW", 0x08000000) +CREATE_NEW_PROCESS_GROUP = getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0x00000200) + + +def _normalise_language(value: str | int | None) -> str: + """ + Map registry or environment values to supported language codes. + + Args: + value (str | int | None): The raw language value from environment or registry. + + Returns: + str: The normalized language code ('en' or 'es'), or default if unrecognized. + """ + if value is None: + return DEFAULT_LANGUAGE + + if isinstance(value, int): + return LANGUAGE_ALIASES.get(str(value), DEFAULT_LANGUAGE) + + token = value.strip().lower() + return LANGUAGE_ALIASES.get(token, DEFAULT_LANGUAGE) + + +def detect_language() -> str: + """Determine the UI language based on environment or registry settings.""" + + env_lang = os.environ.get("AYMURAI_LANG") + if env_lang: + return _normalise_language(env_lang) + + if os.name == "nt": + try: + import winreg # type: ignore + + with winreg.OpenKey(winreg.HKEY_CURRENT_USER, REGISTRY_PATH) as key: + raw_value, _ = winreg.QueryValueEx(key, REGISTRY_VALUE) + except FileNotFoundError: + return DEFAULT_LANGUAGE + except OSError: + return DEFAULT_LANGUAGE + else: + return _normalise_language(raw_value) + + return DEFAULT_LANGUAGE + + +class Localizer: + """Provide language-specific strings with graceful fallback.""" + + def __init__(self, language: str) -> None: + self.language = language if language in {"en", "es"} else DEFAULT_LANGUAGE + + def get(self, key: str, **kwargs: object) -> str: + """ + Retrieve a localized string by key, formatting with kwargs if provided. + + Args: + key (str): The key identifying the string to retrieve. + **kwargs (object): Optional keyword arguments for string formatting. + + Returns: + str: The localized and formatted string. + """ + options = LANGUAGE_STRINGS.get(key, {}) + text = ( + options.get(self.language) + or options.get(DEFAULT_LANGUAGE) + or options.get("en") + or key + ) + if kwargs: + with contextlib.suppress(Exception): + return text.format(**kwargs) + return text + + +class TrayApp: + """Manage the backend process and accompanying system tray icon.""" + + def __init__(self, localizer: Localizer) -> None: + """Initialize the TrayApp, setting up process management, logging, and tray icon image.""" + self.localize = localizer + self.process: subprocess.Popen[str] | None = None + self.log_handle: TextIO | None = None + self.lock = threading.Lock() + self.stop_event = threading.Event() + self.icon: pystray.Icon | None = None + self.image = self._load_icon_image() + self.monitor_thread = threading.Thread( + target=self._monitor_backend, daemon=True + ) + + def _load_icon_image(self) -> Image.Image: + """Load the best available icon for the tray from the specified or default path.""" + candidate = ( + ICON_PATH + if ICON_PATH.exists() + else SCRIPT_DIR + / "resources" + / "app" + / "build" + / "app" + / "favicon.ico" + ) + with Image.open(candidate) as source: + return source.copy() + + def _ensure_log_dir(self) -> None: + """Ensure the log directory exists.""" + LOG_DIR.mkdir(parents=True, exist_ok=True) + + def _open_log(self) -> None: + """Open the log file for appending log messages.""" + self._ensure_log_dir() + self.log_handle = LOG_FILE.open("a", encoding="utf-8", buffering=1) + + def _close_log(self) -> None: + """Close the log file handle if open.""" + if self.log_handle is not None: + with contextlib.suppress(Exception): + self.log_handle.flush() + self.log_handle.close() + self.log_handle = None + + def _write_log(self, message: str) -> None: + """ + Write a timestamped message to the log file. + + Args: + message (str): The message to log. + """ + timestamp = time.strftime("%Y-%m-%d %H:%M:%S") + line = f"[{timestamp}] {message}\n" + if self.log_handle is not None: + with contextlib.suppress(Exception): + self.log_handle.write(line) + return + + self._ensure_log_dir() + with contextlib.suppress(Exception): + with LOG_FILE.open("a", encoding="utf-8") as handle: + handle.write(line) + + def start_backend(self) -> None: + """Start the backend Uvicorn server as a subprocess and begin monitoring.""" + with self.lock: + if self.process is not None and self.process.poll() is None: + return + + self._open_log() + self._write_log("Starting Uvicorn backend") + + creationflags = 0 + if os.name == "nt": + creationflags = CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP + self.process = subprocess.Popen( + BACKEND_COMMAND, + cwd=str(SCRIPT_DIR), + stdout=self.log_handle, + stderr=subprocess.STDOUT, + creationflags=creationflags, + ) + + if not self.monitor_thread.is_alive(): + self.monitor_thread.start() + + self._update_icon_title("running") + + def stop_backend(self, reason: str = "Stopped by user") -> None: + """ + Stop the backend process gracefully and update status/logs. + + Args: + reason (str): The reason for stopping the backend, for logging purposes. + Default is "Stopped by user". + """ + with self.lock: + proc = self.process + if proc is None: + return + + if proc.poll() is None: + _graceful_stop(proc) + + exit_code = proc.poll() + self._write_log(f"Backend stopped ({reason}, code={exit_code})") + self.process = None + self._close_log() + + self._update_icon_title("stopped") + + def restart_backend(self) -> None: + """Restart the backend process and notify the user.""" + self.stop_backend("Restart requested") + time.sleep(0.5) + self.start_backend() + self._notify(self.localize.get("notify_restarted")) + + def open_log_file(self) -> None: + """Open the backend log file in the system's file explorer.""" + self._ensure_log_dir() + LOG_FILE.touch(exist_ok=True) + _open_in_explorer(LOG_FILE) + + def open_logs_folder(self) -> None: + """Open the logs folder in the system's file explorer.""" + self._ensure_log_dir() + _open_in_explorer(LOG_DIR) + + def exit_application(self) -> None: + """Signal the application to exit and stop the backend.""" + self.stop_event.set() + self.stop_backend("Tray icon closed") + + def _monitor_backend(self) -> None: + """Monitor the backend process and handle unexpected exits.""" + while not self.stop_event.is_set(): + time.sleep(2) + + with self.lock: + proc = self.process + + if proc is None: + continue + + exit_code = proc.poll() + if exit_code is None: + continue + + self._write_log(f"Backend exited unexpectedly (code={exit_code})") + with self.lock: + self.process = None + self._close_log() + + self._update_icon_title("stopped") + self._notify(self.localize.get("notify_unexpected_stop")) + + def _update_icon_title(self, status_key: str) -> None: + """ + Update the tray icon's title to reflect backend status. + + Args: + status_key (str): The status key to localize. + """ + if self.icon is not None: + title = self._format_title(status_key) + self.icon.title = title + + def _format_title(self, status_key: str) -> str: + """ + Format the tray icon title based on the current status. + + Args: + status_key (str): The status key to localize. + + Returns: + str: The formatted title string. + """ + status_text = self.localize.get(f"status_{status_key}") + template = self.localize.get("tray_title_template") + return template.format(status=status_text) + + def _notify(self, message: str) -> None: + """ + Show a notification message via the tray icon. + + Args: + message (str): The message to display in the notification. + """ + if self.icon is not None: + with contextlib.suppress(Exception): + self.icon.notify(message, self.localize.get("notify_title")) + + +def _open_in_explorer(path: Path) -> None: + """ + Open the given file or directory in the system's file explorer. + + Args: + path (Path): The file or directory to open. + """ + try: + if sys.platform.startswith("win"): + os.startfile(str(path)) # type: ignore[attr-defined] + elif sys.platform == "darwin": + subprocess.Popen(["open", str(path)]) + else: + subprocess.Popen(["xdg-open", str(path)]) + except Exception: + pass + + +def _wait_for_process(proc: subprocess.Popen[str], timeout: float) -> bool: + """ + Wait for a process to exit within a timeout. + + Args: + proc (subprocess.Popen[str]): The process to wait for. + timeout (float): The maximum time to wait in seconds. + + Returns: + bool: True if the process exited, False if timeout expired. + """ + try: + proc.wait(timeout=timeout) + return True + except subprocess.TimeoutExpired: + return False + + +def _graceful_stop(proc: subprocess.Popen[str]) -> None: + """ + Attempt to gracefully stop a process, escalating to force if needed. + + Args: + proc (subprocess.Popen[str]): The process to stop. + """ + if proc.poll() is not None: + _wait_for_process(proc, 5) + return + + if os.name == "nt": + proc.terminate() + if not _wait_for_process(proc, 5): + subprocess.run( + ["taskkill", "/F", "/T", "/PID", str(proc.pid)], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + check=False, + ) + _wait_for_process(proc, 5) + return + + proc.terminate() + if not _wait_for_process(proc, 10): + proc.kill() + _wait_for_process(proc, 5) + + +def handle_signal( + signum: int, _frame: object, app: TrayApp, icon: pystray.Icon +) -> None: + """ + Handle received signal by exiting application and stopping icon. + + Args: + signum (int): The signal number received. + _frame (object): The current stack frame (unused). + app (TrayApp): The tray application instance to shut down. + icon (pystray.Icon): The tray icon to stop. + """ + del signum + app.exit_application() + icon.stop() + + +def _setup_signal_handlers(app: TrayApp, icon: pystray.Icon) -> None: + """ + Set up signal handlers for graceful shutdown on SIGINT/SIGTERM. + + Args: + app (TrayApp): The tray application instance to shut down. + icon (pystray.Icon): The tray icon to stop. + """ + handler = partial(handle_signal, app=app, icon=icon) + for sig in (signal.SIGINT, signal.SIGTERM): + with contextlib.suppress(Exception): + signal.signal(sig, handler) + + +def main() -> None: + """Main entry point for the tray runner application.""" + language = detect_language() + localizer = Localizer(language) + app = TrayApp(localizer) + + menu = Menu( + MenuItem( + localizer.get("menu_open_log"), + lambda _icon, _item: app.open_log_file(), + ), + MenuItem( + localizer.get("menu_open_logs_folder"), + lambda _icon, _item: app.open_logs_folder(), + ), + MenuItem( + localizer.get("menu_restart_backend"), + lambda _icon, _item: app.restart_backend(), + ), + MenuItem( + localizer.get("menu_stop_backend"), + lambda _icon, _item: app.stop_backend(), + ), + Menu.SEPARATOR, + MenuItem(localizer.get("menu_quit"), lambda icon, _item: _quit(icon, app)), + ) + + initial_title = app._format_title("starting") + icon = pystray.Icon("aymurai-backend", app.image, initial_title, menu) + app.icon = icon + _setup_signal_handlers(app, icon) + + app.start_backend() + + try: + icon.run() + finally: + app.exit_application() + + +def _quit(icon: pystray.Icon, app: TrayApp) -> None: + """Quit handler for the tray menu: exit app and stop icon.""" + app.exit_application() + icon.stop() + + +if __name__ == "__main__": + main() diff --git a/scripts/headers/install_backend.nsh b/scripts/headers/install_backend.nsh index 112f62c..c1dbbfc 100644 --- a/scripts/headers/install_backend.nsh +++ b/scripts/headers/install_backend.nsh @@ -8,18 +8,29 @@ SetOutPath $INSTDIR ; Copy installation files - File "${SOURCE_DIR}\aymurai-1.1.10-py3-none-any.whl" + File "${SOURCE_DIR}\aymurai-1.1.12-py3-none-any.whl" File "${SOURCE_DIR}\environment.yml" File "${SOURCE_DIR}\install.bat" File "${SOURCE_DIR}\run_server.bat" + File "${SOURCE_DIR}\tray_runner.py" File /r "${SOURCE_DIR}\api\*.*" + + ; Create frontend resources directory structure for the favicon + CreateDirectory "$INSTDIR\resources\app\build\app" + + ; Copy favicon directly to the frontend resources directory + SetOutPath "$INSTDIR\resources\app\build\app" + File "${SOURCE_DIR}\favicon.ico" + + ; Return to the installation directory + SetOutPath $INSTDIR ; Run the installation batch file and capture the return code - DetailPrint "Installing backend dependencies..." + DetailPrint "$(STR_DETAIL_BACKEND_INSTALL)" nsExec::Exec '"$INSTDIR\install.bat" > "$INSTDIR\install.log" 2>&1' Pop $1 ; return code ${If} $1 != 0 - MessageBox MB_ICONSTOP "Backend installation failed. Please check the installation log at $INSTDIR\install.log for details." + MessageBox MB_ICONSTOP "$(STR_DETAIL_BACKEND_FAIL)" Abort ${EndIf} @@ -28,18 +39,18 @@ nsExec::ExecToLog 'powershell -NoProfile -ExecutionPolicy Bypass -Command "Set-WinSystemLocale -SystemLocale \"en-US\""' nsExec::ExecToLog 'powershell -NoProfile -ExecutionPolicy Bypass -Command "Set-WinUILanguageOverride -Language \"es-AR\""' nsExec::ExecToLog 'powershell -NoProfile -ExecutionPolicy Bypass -Command "Set-WinUserLanguageList \"es-AR\" -Force"' - nsExec::ExecToLog 'powershell -NoProfile -ExecutionPolicy Bypass -Command "$env:LANG = \"en-US.UTF-8\"; $env:LC_ALL = \"en-US.UTF-8\""' + nsExec::ExecToLog 'powershell -NoProfile -ExecutionPolicy Bypass -Command "$$env:LANG = \"en-US.UTF-8\"; $$env:LC_ALL = \"en-US.UTF-8\""' nsExec::ExecToLog 'powershell -NoProfile -ExecutionPolicy Bypass -Command "Get-WinUserLanguageList"' nsExec::ExecToLog 'powershell -NoProfile -ExecutionPolicy Bypass -Command "Get-Culture"' ; Remove installation files - DetailPrint "Removing installation files..." - Delete "$INSTDIR\aymurai-1.1.10-py3-none-any.whl" + DetailPrint "$(STR_DETAIL_BACKEND_REMOVE)" + Delete "$INSTDIR\aymurai-1.1.12-py3-none-any.whl" Delete "$INSTDIR\install.bat" ; Write installation path to registry WriteRegStr HKLM "Software\${APP_NAME}" "Install_Dir" "$INSTDIR" - DetailPrint "Backend installation successful." + DetailPrint "$(STR_DETAIL_BACKEND_SUCCESS)" Delete "$INSTDIR\install.log" !macroend diff --git a/scripts/headers/install_frontend.nsh b/scripts/headers/install_frontend.nsh index 967d48c..de72e99 100644 --- a/scripts/headers/install_frontend.nsh +++ b/scripts/headers/install_frontend.nsh @@ -1,13 +1,13 @@ ; Frontend Installation Header -!define FRONTEND_URL "https://github.com/AymurAI/desktop-app/releases/download/1.20.2/AymurAI-win32-x64-1-20-2.zip" +!define FRONTEND_URL "https://github.com/AymurAI/desktop-app/releases/download/v1.23.2/AymurAI-win32-1-23-2.zip" !macro InstallFrontend ; Check if frontend is already installed IfFileExists "$INSTDIR\${APP_NAME}.exe" frontend_skip_download frontend_download frontend_skip_download: - DetailPrint "Frontend is already installed." + DetailPrint "$(STR_DETAIL_FRONTEND_SKIP)" Goto finish frontend_download: @@ -15,11 +15,11 @@ SetOutPath $INSTDIR ; Download ZIP from GitHub - DetailPrint "Downloading frontend..." + DetailPrint "$(STR_DETAIL_FRONTEND_DOWNLOAD)" nsExec::ExecToLog 'powershell -Command "Invoke-WebRequest -Uri ${FRONTEND_URL} -OutFile \"$INSTDIR\${APP_NAME}.zip\""' ; Unzip downloaded file - DetailPrint "Extracting frontend..." + DetailPrint "$(STR_DETAIL_FRONTEND_EXTRACT)" nsExec::ExecToLog 'powershell -Command "Expand-Archive -Path \"$INSTDIR\${APP_NAME}.zip\" -DestinationPath \"$INSTDIR\" -Force"' ; Remove ZIP file after extraction @@ -52,12 +52,12 @@ IfFileExists "$INSTDIR\${APP_NAME}.exe" frontend_success frontend_fail frontend_fail: - MessageBox MB_OK|MB_ICONEXCLAMATION "Frontend installation failed. Please try again." + MessageBox MB_OK|MB_ICONEXCLAMATION "$(STR_DETAIL_FRONTEND_FAIL)" Abort - + frontend_success: - DetailPrint "Frontend installation successful." - + DetailPrint "$(STR_DETAIL_FRONTEND_SUCCESS)" + finish: ; No further actions needed here !macroend diff --git a/scripts/headers/uninstall.nsh b/scripts/headers/uninstall.nsh index 3c05b43..5477ec7 100644 --- a/scripts/headers/uninstall.nsh +++ b/scripts/headers/uninstall.nsh @@ -20,6 +20,19 @@ ; Remove registry keys DeleteRegKey HKLM "Software\${APP_NAME}" DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" + + ; Remove per-user data directory if requested + ${If} $UNINSTALL_APPDATA = 1 + StrCpy $0 "$LOCALAPPDATA\${APP_NAME}" + StrCpy $1 0 + IfFileExists "$0\\*.*" 0 +2 + StrCpy $1 1 + IfFileExists "$0\\" 0 +2 + StrCpy $1 1 + StrCmp $1 1 0 +3 + DetailPrint "Removing user data directory..." + RMDir /r /REBOOTOK "$0" + ${EndIf} ; Remove installation directory and all its contents, including subdirectories RMDir /r /REBOOTOK "$INSTDIR" diff --git a/scripts/installer.nsi b/scripts/installer.nsi index bcd405b..fb5f0d8 100644 --- a/scripts/installer.nsi +++ b/scripts/installer.nsi @@ -1,197 +1,354 @@ -; Main Installer Script -Unicode true -ManifestDPIAware true - -!define APP_NAME "AymurAI" -!define APP_VERSION "1.1" -!define OUTPUT_DIR "..\build" - -; Installer Information -Name "${APP_NAME} v${APP_VERSION}" -OutFile "${OUTPUT_DIR}\${APP_NAME}-Installer.exe" -BrandingText "DataGenero - Collective AI" - -; Request application privileges -RequestExecutionLevel admin - -; The default installation directory -InstallDir $PROGRAMFILES64\${APP_NAME} - -; Registry key to store the installation directory -InstallDirRegKey HKLM "Software\${APP_NAME}" "Install_Dir" - -; Include necessary headers -!include "FileFunc.nsh" -!include "LogicLib.nsh" - -; Include custom headers -!include "headers\install_backend.nsh" -!include "headers\install_frontend.nsh" -!include "headers\uninstall.nsh" - -; Modern interface settings -!include "MUI2.nsh" - -!define MUI_ICON "..\resources\api\static\logo256-text.ico" -!define MUI_HEADERIMAGE_BITMAP "..\resources\header.bmp" -!define MUI_WELCOMEFINISHPAGE_BITMAP "..\resources\banner.bmp" - -!define MUI_WELCOMEPAGE_TEXT "This setup will guide you through the installation of ${APP_NAME} on your system.$\r$\n$\r$\nIt is recommended that you close all other applications before starting.$\r$\n$\r$\nMake sure you have LibreOffice and Miniconda installed on your system, as they are required for ${APP_NAME} to function properly.$\r$\n$\r$\nClick Next to continue." - -!define MUI_HEADERIMAGE -!define MUI_PAGE_HEADER_TEXT "License Information" -!define MUI_PAGE_HEADER_SUBTEXT "Please review the license terms before installing ${APP_NAME}." -!define MUI_LICENSEPAGE_TEXT_TOP "Press Page Down or scroll to see the rest of the license." -!define MUI_LICENSEPAGE_TEXT_BOTTOM " " -!define MUI_LICENSEPAGE_BUTTON "&Next >" - -!define MUI_ABORTWARNING -!define MUI_FINISHPAGE_TITLE "Setup Complete" -!define MUI_FINISHPAGE_RUN -!define MUI_FINISHPAGE_RUN_FUNCTION LaunchAymurAI -!define MUI_FINISHPAGE_SHOWREADME "https://github.com/AymurAI/desktop-app/releases" -!define MUI_FINISHPAGE_SHOWREADME_TEXT "View Release Notes" -!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED -!define MUI_FINISHPAGE_LINK "Learn more about AymurAI" -!define MUI_FINISHPAGE_LINK_LOCATION "https://www.aymurai.info/" -!define MUI_FINISHPAGE_LINK_COLOR 3F479D - -Function LaunchAymurAI - ; Check if the frontend executable exists before launching - IfFileExists "$INSTDIR\${APP_NAME}.exe" 0 +3 - Exec "$INSTDIR\${APP_NAME}.exe" - Return - MessageBox MB_ICONEXCLAMATION|MB_OK "AymurAI desktop application is not installed or missing.$\r$\nPlease verify that you selected the frontend component during installation." -FunctionEnd - -!define UNINST_MINICONDA_VAR "UNINSTALL_MINICONDA" - -Var UNINSTALL_MINICONDA -Var MinicondaCheckbox - -;-------------------------------- - -; Modern UI pages -!insertmacro MUI_PAGE_WELCOME -!insertmacro MUI_PAGE_LICENSE "..\resources\license.txt" -!insertmacro MUI_PAGE_COMPONENTS -!insertmacro MUI_PAGE_DIRECTORY -!insertmacro MUI_PAGE_INSTFILES -!insertmacro MUI_PAGE_FINISH - -; Uninstall pages -UninstPage custom un.MinicondaPageCreate un.MinicondaPageLeave -!insertmacro MUI_UNPAGE_CONFIRM -!insertmacro MUI_UNPAGE_INSTFILES - -!insertmacro MUI_LANGUAGE "English" - -;-------------------------------- - -Function CheckPrerequisites - ; Check Miniconda in USERPROFILE only (standard location) - ReadEnvStr $R0 "USERPROFILE" - StrCpy $R1 "$R0\miniconda3" - - ; Check for Miniconda (conda.exe) - IfFileExists "$R1\Scripts\conda.exe" CheckLibreOffice - ; If we get here, Miniconda is not installed - MessageBox MB_ICONSTOP|MB_OK "Miniconda is required but was not found in:$\r$\n$R1.$\r$\nPlease install Miniconda before continuing." - Abort - - CheckLibreOffice: - ; Check LibreOffice installation by registry key - ClearErrors - ReadRegStr $R5 HKLM "SOFTWARE\LibreOffice\LibreOffice" "Path" - IfErrors 0 LibreOK - ; Check for LibreOffice executable as a fallback - IfFileExists "$PROGRAMFILES\LibreOffice\program\soffice.exe" LibreOK 0 - IfFileExists "$PROGRAMFILES64\LibreOffice\program\soffice.exe" LibreOK 0 - MessageBox MB_ICONSTOP|MB_OK "LibreOffice is required but was not found in the standard installation paths.$\r$\nPlease install LibreOffice before continuing." - Abort - - LibreOK: - Return -FunctionEnd - -Function .onInit - Call CheckPrerequisites -FunctionEnd - -;-------------------------------- - -Section "Backend" SecBackend - ; Execute custom .nsh headers for backend setup - !insertmacro InstallBackend - AddSize 2254864 ; Add size for backend files - - ; Create uninstaller file - WriteUninstaller "$INSTDIR\Uninstall.exe" - AddSize 64 ; Add size for uninstaller file - - ; Add registry entries for Add/Remove Programs - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "DisplayName" "${APP_NAME}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "UninstallString" '"$INSTDIR\Uninstall.exe"' - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "DisplayIcon" "$INSTDIR\${APP_NAME}.exe,0" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "Publisher" "DataGenero - Collective AI" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "DisplayVersion" "${APP_VERSION}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "URLInfoAbout" "https://www.aymurai.info/" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "InstallLocation" "$INSTDIR" - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "NoModify" 1 - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "NoRepair" 1 -SectionEnd - -; Define a subsection for frontend-related components -SubSection "Frontend" SubSecFrontend - Section "Desktop app" SecDesktopApp - ; Execute custom .nsh headers for frontend setup - !insertmacro InstallFrontend - AddSize 232260 ; Add size for frontend files - SectionEnd - - Section "Shortcuts" SecShortcuts - ; Create a shortcut on the desktop to the client executable - CreateShortCut "$DESKTOP\${APP_NAME}.lnk" "$INSTDIR\${APP_NAME}.exe" - - ; Create a shortcut in the Start Menu Programs folder - CreateShortCut "$SMPROGRAMS\${APP_NAME}.lnk" "$INSTDIR\${APP_NAME}.exe" - SectionEnd -SubSectionEnd - -!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN - ; Insert descriptions for each section - !insertmacro MUI_DESCRIPTION_TEXT ${SecBackend} "Installs the AymurAI backend services." - !insertmacro MUI_DESCRIPTION_TEXT ${SecDesktopApp} "Installs the AymurAI desktop application." - !insertmacro MUI_DESCRIPTION_TEXT ${SecShortcuts} "Creates desktop shortcuts for easy access." -!insertmacro MUI_FUNCTION_DESCRIPTION_END - -Function .onSelChange - ; Prevent shortcuts selection without desktop app - ${IfNot} ${SectionIsSelected} ${SecDesktopApp} - !insertmacro UnselectSection ${SecShortcuts} - ${EndIf} -FunctionEnd - -;-------------------------------- - -Section "Uninstall" - ; Pass the Miniconda removal choice to the macro - !insertmacro Uninstall -SectionEnd - -Function un.MinicondaPageCreate - ; Custom Uninstall Miniconda Checkbox Page - !insertmacro MUI_HEADER_TEXT "Uninstall Options" "Select additional components to remove" - nsDialogs::Create 1018 - Pop $0 - - ${NSD_CreateCheckbox} 0u 20u 100% 12u "Remove Miniconda (not recommended)" - Pop $MinicondaCheckbox - ${NSD_SetState} $MinicondaCheckbox ${BST_UNCHECKED} - ${NSD_CreateLabel} 0u 36u 100% 24u "Warning: Only check this if you are sure no other Conda environments are needed. This will remove Miniconda and all its environments." - nsDialogs::Show -FunctionEnd - -Function un.MinicondaPageLeave - ${NSD_GetState} $MinicondaCheckbox $UNINSTALL_MINICONDA -FunctionEnd +; Main Installer Script +Unicode true +ManifestDPIAware true + +!define APP_NAME "AymurAI" +!define APP_VERSION "1.1" +!define OUTPUT_DIR "..\build" + +; Installer Information +Name "${APP_NAME} v${APP_VERSION}" +OutFile "${OUTPUT_DIR}\${APP_NAME}-Installer.exe" +BrandingText "DataGénero - Collective AI" + +; Language storage and defaults +!define MUI_LANGDLL_REGISTRY_ROOT HKCU +!define MUI_LANGDLL_REGISTRY_KEY "Software\${APP_NAME}\Installer" +!define MUI_LANGDLL_REGISTRY_VALUENAME "Language" +!define MUI_LANGDLL_ALWAYSSHOW + +; Request application privileges +RequestExecutionLevel admin + +; The default installation directory +InstallDir $PROGRAMFILES64\${APP_NAME} + +; Registry key to store the installation directory +InstallDirRegKey HKLM "Software\${APP_NAME}" "Install_Dir" + +; Include necessary headers +!include "FileFunc.nsh" +!include "LogicLib.nsh" + +; Include custom headers +!include "headers\install_backend.nsh" +!include "headers\install_frontend.nsh" +!include "headers\uninstall.nsh" + +; Modern interface settings +!include "MUI2.nsh" + +; Language settings +!define MUI_LANGDLL_DEFAULT_LANG ${LANG_SPANISH} +!define MUI_LANGDLL_WINDOWTITLE "${APP_NAME} v${APP_VERSION}" +!define MUI_LANGDLL_INFO "Selecciona idioma / Select language" + +!insertmacro MUI_RESERVEFILE_LANGDLL + +;-------------------------------- +; UI assets and text definitions + +!define MUI_ICON "..\resources\favicon.ico" +!define MUI_HEADERIMAGE_BITMAP "..\resources\header.bmp" +!define MUI_WELCOMEFINISHPAGE_BITMAP "..\resources\banner.bmp" + +!define MUI_WELCOMEPAGE_TEXT "$(STR_WELCOME_TEXT)" + +!define MUI_HEADERIMAGE +!define MUI_PAGE_HEADER_TEXT "$(STR_TERMS_HEADER)" +!define MUI_PAGE_HEADER_SUBTEXT "$(STR_TERMS_SUBHEADER)" +!define MUI_LICENSEPAGE_TEXT_TOP "$(STR_LICENSE_TOP)" +!define MUI_LICENSEPAGE_TEXT_BOTTOM " " + +!define MUI_ABORTWARNING +!define MUI_FINISHPAGE_TITLE "$(STR_FINISH_TITLE)" +!define MUI_FINISHPAGE_RUN +!define MUI_FINISHPAGE_RUN_FUNCTION LaunchAymurAI +!define MUI_FINISHPAGE_RUN_TEXT "$(STR_FINISH_RUN_TEXT)" +!define MUI_FINISHPAGE_SHOWREADME "https://github.com/AymurAI/desktop-app/releases" +!define MUI_FINISHPAGE_SHOWREADME_TEXT "$(STR_FINISH_RELEASE_NOTES)" +!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED +!define MUI_FINISHPAGE_LINK "$(STR_FINISH_LINK_TEXT)" +!define MUI_FINISHPAGE_LINK_LOCATION "https://www.aymurai.info/" +!define MUI_FINISHPAGE_LINK_COLOR 3F479D + +Function LaunchAymurAI + ; Check if the frontend executable exists before launching + IfFileExists "$INSTDIR\${APP_NAME}.exe" 0 +3 + Exec "$INSTDIR\${APP_NAME}.exe" + Return + MessageBox MB_ICONEXCLAMATION|MB_OK "$(STR_LAUNCH_MISSING)" +FunctionEnd + +!define UNINST_MINICONDA_VAR "UNINSTALL_MINICONDA" + +Var UNINSTALL_MINICONDA +Var MinicondaCheckbox +Var UNINSTALL_APPDATA +Var RemoveDataCheckbox + +;-------------------------------- + +; Modern UI pages +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_LICENSE "$(STR_LICENSE_FILE)" +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH + +; Uninstall pages +UninstPage custom un.RemoveDataPageCreate un.RemoveDataPageLeave +UninstPage custom un.MinicondaPageCreate un.MinicondaPageLeave +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +;-------------------------------- +; Language strings + +!insertmacro MUI_LANGUAGE "English" +!insertmacro MUI_LANGUAGE "Spanish" + +LangString STR_WELCOME_TEXT ${LANG_SPANISH} "Este asistente te guiará en la instalación de ${APP_NAME} en tu equipo.$\r$\n$\r$\nSe recomienda cerrar las demás aplicaciones antes de comenzar.$\r$\n$\r$\nAsegúrate de tener LibreOffice y Miniconda instalados, ya que son necesarios para que ${APP_NAME} funcione correctamente.$\r$\n$\r$\nHaz clic en Siguiente para continuar." +LangString STR_WELCOME_TEXT ${LANG_ENGLISH} "This setup will guide you through the installation of ${APP_NAME} on your system.$\r$\n$\r$\nIt is recommended that you close all other applications before starting.$\r$\n$\r$\nMake sure you have LibreOffice and Miniconda installed on your system, as they are required for ${APP_NAME} to function properly.$\r$\n$\r$\nClick Next to continue." + +LangString STR_DETAIL_BACKEND_INSTALL ${LANG_SPANISH} "Instalando dependencias del backend..." +LangString STR_DETAIL_BACKEND_INSTALL ${LANG_ENGLISH} "Installing backend dependencies..." + +LangString STR_DETAIL_BACKEND_REMOVE ${LANG_SPANISH} "Eliminando archivos temporales..." +LangString STR_DETAIL_BACKEND_REMOVE ${LANG_ENGLISH} "Removing installation files..." + +LangString STR_DETAIL_BACKEND_SUCCESS ${LANG_SPANISH} "Instalación del backend completada." +LangString STR_DETAIL_BACKEND_SUCCESS ${LANG_ENGLISH} "Backend installation successful." + +LangString STR_DETAIL_BACKEND_FAIL ${LANG_SPANISH} "La instalación del backend falló. Revisá el archivo install.log para más detalles." +LangString STR_DETAIL_BACKEND_FAIL ${LANG_ENGLISH} "Backend installation failed. Please check install.log for details." + +LangString STR_DETAIL_FRONTEND_SKIP ${LANG_SPANISH} "El frontend ya está instalado." +LangString STR_DETAIL_FRONTEND_SKIP ${LANG_ENGLISH} "Frontend is already installed." + +LangString STR_DETAIL_FRONTEND_DOWNLOAD ${LANG_SPANISH} "Descargando el frontend..." +LangString STR_DETAIL_FRONTEND_DOWNLOAD ${LANG_ENGLISH} "Downloading frontend..." + +LangString STR_DETAIL_FRONTEND_EXTRACT ${LANG_SPANISH} "Extrayendo el frontend..." +LangString STR_DETAIL_FRONTEND_EXTRACT ${LANG_ENGLISH} "Extracting frontend..." + +LangString STR_DETAIL_FRONTEND_SUCCESS ${LANG_SPANISH} "Instalación del frontend completada." +LangString STR_DETAIL_FRONTEND_SUCCESS ${LANG_ENGLISH} "Frontend installation successful." + +LangString STR_DETAIL_FRONTEND_FAIL ${LANG_SPANISH} "La instalación del frontend falló. Intentá nuevamente." +LangString STR_DETAIL_FRONTEND_FAIL ${LANG_ENGLISH} "Frontend installation failed. Please try again." + +LangString STR_TERMS_HEADER ${LANG_SPANISH} "Términos y condiciones" +LangString STR_TERMS_HEADER ${LANG_ENGLISH} "Terms and Conditions" + +LangString STR_TERMS_SUBHEADER ${LANG_SPANISH} "Revisa los términos y condiciones antes de instalar ${APP_NAME}." +LangString STR_TERMS_SUBHEADER ${LANG_ENGLISH} "Please review the terms and conditions before installing ${APP_NAME}." + +LangString STR_LICENSE_TOP ${LANG_SPANISH} "Presiona Av Pág o desplázate para ver el resto de los términos y condiciones." +LangString STR_LICENSE_TOP ${LANG_ENGLISH} "Press Page Down or scroll to see the rest of the terms and conditions." + +LangString STR_FINISH_TITLE ${LANG_SPANISH} "Instalación completada" +LangString STR_FINISH_TITLE ${LANG_ENGLISH} "Setup Complete" + +LangString STR_FINISH_RUN_TEXT ${LANG_SPANISH} "Iniciar ${APP_NAME}" +LangString STR_FINISH_RUN_TEXT ${LANG_ENGLISH} "Launch ${APP_NAME}" + +LangString STR_FINISH_RELEASE_NOTES ${LANG_SPANISH} "Ver notas de la versión" +LangString STR_FINISH_RELEASE_NOTES ${LANG_ENGLISH} "View Release Notes" + +LangString STR_FINISH_LINK_TEXT ${LANG_SPANISH} "Más información sobre AymurAI" +LangString STR_FINISH_LINK_TEXT ${LANG_ENGLISH} "Learn more about AymurAI" + +LangString STR_LAUNCH_MISSING ${LANG_SPANISH} "La aplicación de escritorio de AymurAI no está instalada o falta.$\r$\nVerifica que hayas seleccionado el componente de interfaz durante la instalación." +LangString STR_LAUNCH_MISSING ${LANG_ENGLISH} "AymurAI desktop application is not installed or missing.$\r$\nPlease verify that you selected the frontend component during installation." + +LangString STR_MINICONDA_MISSING ${LANG_SPANISH} "Se requiere Miniconda pero no se encontró en:$\r$\n$R1.$\r$\nInstala Miniconda antes de continuar." +LangString STR_MINICONDA_MISSING ${LANG_ENGLISH} "Miniconda is required but was not found in:$\r$\n$R1.$\r$\nPlease install Miniconda before continuing." + +LangString STR_LIBREOFFICE_MISSING ${LANG_SPANISH} "Se requiere LibreOffice pero no se encontró en las rutas de instalación habituales.$\r$\nInstala LibreOffice antes de continuar." +LangString STR_LIBREOFFICE_MISSING ${LANG_ENGLISH} "LibreOffice is required but was not found in the standard installation paths.$\r$\nPlease install LibreOffice before continuing." + +LangString STR_SECTION_BACKEND ${LANG_SPANISH} "Servicios backend" +LangString STR_SECTION_BACKEND ${LANG_ENGLISH} "Backend" + +LangString STR_SECTION_FRONTEND ${LANG_SPANISH} "Interfaz gráfica" +LangString STR_SECTION_FRONTEND ${LANG_ENGLISH} "Frontend" + +LangString STR_SECTION_DESKTOP ${LANG_SPANISH} "Aplicación de escritorio" +LangString STR_SECTION_DESKTOP ${LANG_ENGLISH} "Desktop app" + +LangString STR_SECTION_SHORTCUTS ${LANG_SPANISH} "Accesos directos" +LangString STR_SECTION_SHORTCUTS ${LANG_ENGLISH} "Shortcuts" + +LangString STR_DESC_BACKEND ${LANG_SPANISH} "Instala los servicios backend de AymurAI." +LangString STR_DESC_BACKEND ${LANG_ENGLISH} "Installs the AymurAI backend services." + +LangString STR_DESC_DESKTOP ${LANG_SPANISH} "Instala la aplicación de escritorio de AymurAI." +LangString STR_DESC_DESKTOP ${LANG_ENGLISH} "Installs the AymurAI desktop application." + +LangString STR_DESC_SHORTCUTS ${LANG_SPANISH} "Crea accesos directos para un acceso rápido." +LangString STR_DESC_SHORTCUTS ${LANG_ENGLISH} "Creates desktop shortcuts for easy access." + +LangString STR_UNINSTALL_HEADER ${LANG_SPANISH} "Opciones de desinstalación" +LangString STR_UNINSTALL_HEADER ${LANG_ENGLISH} "Uninstall Options" + +LangString STR_UNINSTALL_SUBHEADER ${LANG_SPANISH} "Selecciona componentes adicionales para eliminar" +LangString STR_UNINSTALL_SUBHEADER ${LANG_ENGLISH} "Select additional components to remove" + +LangString STR_UNINSTALL_MINICONDA_HEADER ${LANG_SPANISH} "Eliminar Miniconda" +LangString STR_UNINSTALL_MINICONDA_HEADER ${LANG_ENGLISH} "Remove Miniconda" + +LangString STR_UNINSTALL_MINICONDA_SUBHEADER ${LANG_SPANISH} "Decide si querés desinstalar Miniconda" +LangString STR_UNINSTALL_MINICONDA_SUBHEADER ${LANG_ENGLISH} "Choose whether to uninstall Miniconda" + +LangString STR_UNINSTALL_DATA_HEADER ${LANG_SPANISH} "Eliminar datos locales" +LangString STR_UNINSTALL_DATA_HEADER ${LANG_ENGLISH} "Remove local data" + +LangString STR_UNINSTALL_DATA_SUBHEADER ${LANG_SPANISH} "Selecciona si querés borrar caché, modelos y base de datos" +LangString STR_UNINSTALL_DATA_SUBHEADER ${LANG_ENGLISH} "Choose whether to remove cache, models, and database" + +LangString STR_UNINSTALL_REMOVE_MINICONDA ${LANG_SPANISH} "Eliminar Miniconda (no recomendado)" +LangString STR_UNINSTALL_REMOVE_MINICONDA ${LANG_ENGLISH} "Remove Miniconda (not recommended)" + +LangString STR_UNINSTALL_REMOVE_MINICONDA_DESC ${LANG_SPANISH} "Advertencia: Márcalo solo si estás seguro de que no necesitas otros entornos de Conda. Esto eliminará Miniconda y todos sus entornos." +LangString STR_UNINSTALL_REMOVE_MINICONDA_DESC ${LANG_ENGLISH} "Warning: Only check this if you are sure no other Conda environments are needed. This will remove Miniconda and all its environments." + +LangString STR_UNINSTALL_REMOVE_DATA ${LANG_SPANISH} "Eliminar datos de AymurAI (caché, modelos, base de datos)" +LangString STR_UNINSTALL_REMOVE_DATA ${LANG_ENGLISH} "Remove AymurAI data (cache, models, database)" + +LangString STR_UNINSTALL_REMOVE_DATA_DESC ${LANG_SPANISH} "Elimina los archivos en $LOCALAPPDATA\AymurAI para este usuario." +LangString STR_UNINSTALL_REMOVE_DATA_DESC ${LANG_ENGLISH} "Deletes files under $LOCALAPPDATA\AymurAI for this user." + +LicenseLangString STR_LICENSE_FILE ${LANG_ENGLISH} "..\resources\terms_and_conditions.txt" +LicenseLangString STR_LICENSE_FILE ${LANG_SPANISH} "..\resources\terms_and_conditions_es.txt" + +;-------------------------------- + +; Functions + +Function CheckPrerequisites + ; Check Miniconda in USERPROFILE only (standard location) + ReadEnvStr $R0 "USERPROFILE" + StrCpy $R1 "$R0\miniconda3" + + ; Check for Miniconda (conda.exe) + IfFileExists "$R1\Scripts\conda.exe" CheckLibreOffice + ; If we get here, Miniconda is not installed + MessageBox MB_ICONSTOP|MB_OK "$(STR_MINICONDA_MISSING)" + Abort + + CheckLibreOffice: + ; Check LibreOffice installation by registry key + ClearErrors + ReadRegStr $R5 HKLM "SOFTWARE\LibreOffice\LibreOffice" "Path" + IfErrors 0 LibreOK + ; Check for LibreOffice executable as a fallback + IfFileExists "$PROGRAMFILES\LibreOffice\program\soffice.exe" LibreOK 0 + IfFileExists "$PROGRAMFILES64\LibreOffice\program\soffice.exe" LibreOK 0 + MessageBox MB_ICONSTOP|MB_OK "$(STR_LIBREOFFICE_MISSING)" + Abort + + LibreOK: + Return +FunctionEnd + +Function .onInit + !insertmacro MUI_LANGDLL_DISPLAY + Call CheckPrerequisites +FunctionEnd + +;-------------------------------- + +Section "$(STR_SECTION_BACKEND)" SecBackend + ; Execute custom .nsh headers for backend setup + !insertmacro InstallBackend + AddSize 2254864 ; Add size for backend files + + ; Create uninstaller file + WriteUninstaller "$INSTDIR\Uninstall.exe" + AddSize 64 ; Add size for uninstaller file + + ; Add registry entries for Add/Remove Programs + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "DisplayName" "${APP_NAME}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "UninstallString" '"$INSTDIR\Uninstall.exe"' + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "DisplayIcon" "$INSTDIR\${APP_NAME}.exe,0" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "Publisher" "DataGenero - Collective AI" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "DisplayVersion" "${APP_VERSION}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "URLInfoAbout" "https://www.aymurai.info/" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "InstallLocation" "$INSTDIR" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "NoRepair" 1 +SectionEnd + +; Define a subsection for frontend-related components +SubSection "$(STR_SECTION_FRONTEND)" SubSecFrontend + Section "$(STR_SECTION_DESKTOP)" SecDesktopApp + ; Execute custom .nsh headers for frontend setup + !insertmacro InstallFrontend + AddSize 232260 ; Add size for frontend files + SectionEnd + + Section "$(STR_SECTION_SHORTCUTS)" SecShortcuts + ; Create a shortcut on the desktop to the client executable + CreateShortCut "$DESKTOP\${APP_NAME}.lnk" "$INSTDIR\${APP_NAME}.exe" + + ; Create a shortcut in the Start Menu Programs folder + CreateShortCut "$SMPROGRAMS\${APP_NAME}.lnk" "$INSTDIR\${APP_NAME}.exe" + SectionEnd +SubSectionEnd + +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + ; Insert descriptions for each section + !insertmacro MUI_DESCRIPTION_TEXT ${SecBackend} "$(STR_DESC_BACKEND)" + !insertmacro MUI_DESCRIPTION_TEXT ${SecDesktopApp} "$(STR_DESC_DESKTOP)" + !insertmacro MUI_DESCRIPTION_TEXT ${SecShortcuts} "$(STR_DESC_SHORTCUTS)" +!insertmacro MUI_FUNCTION_DESCRIPTION_END + +Function .onSelChange + ; Prevent shortcuts selection without desktop app + ${IfNot} ${SectionIsSelected} ${SecDesktopApp} + !insertmacro UnselectSection ${SecShortcuts} + ${EndIf} +FunctionEnd + +;-------------------------------- + +Section "Uninstall" + ; Pass the Miniconda removal choice to the macro + !insertmacro Uninstall +SectionEnd + +Function un.MinicondaPageCreate + ; Custom page for Miniconda removal + !insertmacro MUI_HEADER_TEXT "$(STR_UNINSTALL_MINICONDA_HEADER)" "$(STR_UNINSTALL_MINICONDA_SUBHEADER)" + nsDialogs::Create 1018 + Pop $0 + + ${NSD_CreateCheckbox} 0u 28u 100% 12u "$(STR_UNINSTALL_REMOVE_MINICONDA)" + Pop $MinicondaCheckbox + ${NSD_SetState} $MinicondaCheckbox ${BST_UNCHECKED} + ${NSD_CreateLabel} 0u 44u 100% 24u "$(STR_UNINSTALL_REMOVE_MINICONDA_DESC)" + nsDialogs::Show +FunctionEnd + +Function un.MinicondaPageLeave + ${NSD_GetState} $MinicondaCheckbox $UNINSTALL_MINICONDA +FunctionEnd + +Function un.RemoveDataPageCreate + ; Custom page for data/cache removal + !insertmacro MUI_HEADER_TEXT "$(STR_UNINSTALL_DATA_HEADER)" "$(STR_UNINSTALL_DATA_SUBHEADER)" + nsDialogs::Create 1018 + Pop $0 + + ${NSD_CreateCheckbox} 0u 28u 100% 12u "$(STR_UNINSTALL_REMOVE_DATA)" + Pop $RemoveDataCheckbox + ${NSD_SetState} $RemoveDataCheckbox ${BST_CHECKED} + ${NSD_CreateLabel} 0u 44u 100% 24u "$(STR_UNINSTALL_REMOVE_DATA_DESC)" + nsDialogs::Show +FunctionEnd + +Function un.RemoveDataPageLeave + ${NSD_GetState} $RemoveDataCheckbox $UNINSTALL_APPDATA +FunctionEnd