iatoolkit 0.55.2__py3-none-any.whl → 0.55.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of iatoolkit might be problematic. Click here for more details.

@@ -111,8 +111,8 @@ def register_views(injector, app):
111
111
  except FileNotFoundError:
112
112
  abort(404)
113
113
 
114
- # login testing (old home page)
115
- app.add_url_rule('/login_test/<company_short_name>/<external_user_id>',
114
+ # login testing
115
+ app.add_url_rule('/<company_short_name>/login_test',
116
116
  view_func=LoginSimulationView.as_view('login_test'))
117
117
 
118
118
  app.add_url_rule(
iatoolkit/iatoolkit.py CHANGED
@@ -19,7 +19,7 @@ from werkzeug.middleware.proxy_fix import ProxyFix
19
19
  from injector import Binder, singleton, Injector
20
20
  from importlib.metadata import version as _pkg_version, PackageNotFoundError
21
21
 
22
- IATOOLKIT_VERSION = "0.55.1"
22
+ IATOOLKIT_VERSION = "0.55.3"
23
23
 
24
24
  # global variable for the unique instance of IAToolkit
25
25
  _iatoolkit_instance: Optional['IAToolkit'] = None
@@ -21,16 +21,19 @@ class DatabaseManager:
21
21
  :param echo: Si True, habilita logs de SQL.
22
22
  """
23
23
  self.url = make_url(database_url)
24
- self._engine = create_engine(
25
- database_url,
26
- echo=False,
27
- pool_size=2, # per worker
28
- max_overflow=3,
29
- pool_timeout=30,
30
- pool_recycle=1800,
31
- pool_pre_ping=True,
32
- future=True,
33
- )
24
+ if database_url.startswith('sqlite'): # for tests
25
+ self._engine = create_engine(database_url, echo=False)
26
+ else:
27
+ self._engine = create_engine(
28
+ database_url,
29
+ echo=False,
30
+ pool_size=2, # per worker
31
+ max_overflow=3,
32
+ pool_timeout=30,
33
+ pool_recycle=1800,
34
+ pool_pre_ping=True,
35
+ future=True,
36
+ )
34
37
  self.SessionFactory = sessionmaker(bind=self._engine,
35
38
  autoflush=False,
36
39
  autocommit=False,
@@ -74,20 +74,20 @@ class ProfileService:
74
74
  except Exception as e:
75
75
  return {'success': False, "message": str(e)}
76
76
 
77
- def create_external_user_session(self, company: Company, external_user_id: str):
77
+ def create_external_user_session(self, company: Company, user_identifier: str):
78
78
  """
79
79
  Public method for views to create a web session for an external user.
80
80
  """
81
81
  # 1. Fetch the profile from the external system via Dispatcher.
82
82
  user_profile = self.dispatcher.get_user_info(
83
83
  company_name=company.short_name,
84
- user_identifier=external_user_id
84
+ user_identifier=user_identifier
85
85
  )
86
86
 
87
87
  # 2. Call the session creation helper with external_user_id as user_identifier
88
88
  self.create_web_session(
89
89
  company=company,
90
- user_identifier=external_user_id,
90
+ user_identifier=user_identifier,
91
91
  user_profile=user_profile)
92
92
 
93
93
  def create_web_session(self, company: Company, user_identifier: str, user_profile: dict):
@@ -114,10 +114,6 @@ class QueryService:
114
114
  self._has_valid_cached_context(company_short_name, user_identifier))
115
115
 
116
116
  if rebuild_is_needed:
117
- logging.info(
118
- f"Se necesita reconstrucción de contexto para {company_short_name}/{user_identifier}. Preparando...")
119
-
120
-
121
117
  # Guardar el contexto preparado y su versión para que `finalize_context_rebuild` los use.
122
118
  self.session_context.save_prepared_context(company_short_name, user_identifier, final_system_context,
123
119
  current_version)
@@ -0,0 +1,91 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Prueba de Login para {{ company_short_name }}{% endblock %}
4
+
5
+ {% block content %}
6
+
7
+ <div class="container-fluid">
8
+ <div class="row flex-fill mt-5 justify-content-center">
9
+ <!-- login desde sistema externo -->
10
+ <div class="col-12 col-lg-6">
11
+ <div class="border rounded p-4 p-md-5 shadow-sm bg-light">
12
+ {# El título ahora muestra dinámicamente el nombre de la empresa #}
13
+ <h3 class="text-muted fw-semibold text-start mb-3">
14
+ Login Externo para <span style="color:#0d6efd;">{{ company_short_name }}</span>
15
+ </h3>
16
+ <div class="text-center mb-4">
17
+ <p class="text-muted widget-intro-text">
18
+ Este formulario permite testear el acceso a IAToolkit desde un portal externo utilizando una api-key. El external user ID es el nombre del usuario ya autentificado en algún portal interno de la empresa.
19
+ </p>
20
+ </div>
21
+
22
+ {# El 'action' y 'method' son manejados por JS, pero el id es crucial #}
23
+ <form id="external-login">
24
+
25
+ <div class="mb-3">
26
+ <label for="external_user_id" class="form-label d-block">External user ID</label>
27
+ <input type="text" id="external_user_id" name="external_user_id" class="form-control" required>
28
+ </div>
29
+
30
+ <button type="submit" id="loginButton" class="btn btn-primary">
31
+ Iniciar Sesión
32
+ </button>
33
+ </form>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ {% endblock %}
39
+
40
+ {% block scripts %}
41
+ <script>
42
+ document.addEventListener('DOMContentLoaded', function() {
43
+ const companyShortName = "{{ company_short_name }}";
44
+ const apiKey = "{{ api_key }}";
45
+ const loginForm = document.getElementById('external-login');
46
+ const userIdInput = document.getElementById('external_user_id');
47
+
48
+ loginForm.addEventListener('submit', function(event) {
49
+ event.preventDefault();
50
+ const userIdentifier = userIdInput.value.trim();
51
+ if (!userIdentifier) return;
52
+
53
+ // 1. Reemplazamos la página actual con un mensaje de carga.
54
+ document.body.innerHTML = '<div style="display:flex; align-items:center; justify-content:center; height:100vh; font-family:sans-serif;">Iniciando sesión, por favor espera...</div>';
55
+
56
+ const apiUrl = `/${companyShortName}/external_login`;
57
+
58
+ fetch(apiUrl, {
59
+ method: 'POST',
60
+ headers: {
61
+ 'Content-Type': 'application/json',
62
+ 'Authorization': `Bearer ${apiKey}`
63
+ },
64
+ body: JSON.stringify({
65
+ // Usamos 'userIdentifier' como el valor para el campo 'external_user_id'
66
+ user_identifier: userIdentifier
67
+ })
68
+ })
69
+ .then(async response => {
70
+ if (response.ok) {
71
+ return response.text();
72
+ } else {
73
+ const errorText = await response.text();
74
+ throw new Error(errorText || `Error ${response.status}`);
75
+ }
76
+ })
77
+ .then(htmlContent => {
78
+ // 2. Reemplazamos todo el documento con el nuevo HTML recibido.
79
+ // Esto asegura que todos los scripts y estilos del nuevo HTML se carguen correctamente.
80
+ document.open();
81
+ document.write(htmlContent);
82
+ document.close();
83
+ })
84
+ .catch(error => {
85
+ // Si hay un error, lo mostramos en la página.
86
+ document.body.innerHTML = `<div style="padding:20px; font-family:monospace; color:red;"><h2>Error</h2><pre>${error.message}</pre></div>`;
87
+ });
88
+ });
89
+ });
90
+ </script>
91
+ {% endblock %}
@@ -43,16 +43,16 @@ class ExternalLoginView(BaseLoginView):
43
43
 
44
44
  def post(self, company_short_name: str):
45
45
  data = request.get_json()
46
- if not data or 'external_user_id' not in data:
47
- return jsonify({"error": "Falta external_user_id"}), 400
46
+ if not data or 'user_identifier' not in data:
47
+ return jsonify({"error": "Falta user_identifier"}), 400
48
48
 
49
49
  company = self.profile_service.get_company_by_short_name(company_short_name)
50
50
  if not company:
51
51
  return jsonify({"error": "Empresa no encontrada"}), 404
52
52
 
53
- external_user_id = data['external_user_id']
54
- if not external_user_id:
55
- return jsonify({"error": "missing external_user_id"}), 404
53
+ user_identifier = data.get('user_identifier')
54
+ if not user_identifier:
55
+ return jsonify({"error": "missing user_identifier"}), 404
56
56
 
57
57
  # 1. Authenticate the API call.
58
58
  iaut = self.iauthentication.verify()
@@ -60,11 +60,11 @@ class ExternalLoginView(BaseLoginView):
60
60
  return jsonify(iaut), 401
61
61
 
62
62
  # 2. Create the external user session.
63
- self.profile_service.create_external_user_session(company, external_user_id)
63
+ self.profile_service.create_external_user_session(company, user_identifier)
64
64
 
65
65
  # 3. Delegate the path decision to the centralized logic.
66
66
  try:
67
- return self._handle_login_path(company_short_name, external_user_id, company)
67
+ return self._handle_login_path(company_short_name, user_identifier, company)
68
68
  except Exception as e:
69
- logging.exception(f"Error processing external login path for {company_short_name}/{external_user_id}: {e}")
69
+ logging.exception(f"Error processing external login path for {company_short_name}/{user_identifier}: {e}")
70
70
  return jsonify({"error": f"Internal server error while starting chat. {str(e)}"}), 500
@@ -1,60 +1,27 @@
1
- import requests
2
- import json
3
- import os
1
+ # Copyright (c) 2024 Fernando Libedinsky
2
+ # Product: IAToolkit
3
+ #
4
+ # IAToolkit is open source software.
5
+
4
6
  from flask.views import MethodView
5
- from flask import Response, abort, request, make_response
7
+ from flask import render_template, request
8
+ from injector import inject
9
+ from iatoolkit.services.profile_service import ProfileService
10
+ import os
6
11
 
7
12
 
8
13
  class LoginSimulationView(MethodView):
9
- """
10
- Simula un portal externo que llama a la API de IAToolkit de servidor a servidor,
11
- replicando el flujo real de `dispatch_request`.
12
- Para usarlo, visita /login_test/<company_short_name>/<external_user_id>
13
- """
14
-
15
- def get(self, company_short_name: str, external_user_id: str):
16
- api_key = os.getenv("IATOOLKIT_API_KEY")
17
- base_url = request.host_url.rstrip('/')
18
-
19
- if not api_key:
20
- abort(500, "Error: IATOOLKIT_API_KEY no está configurada en el servidor de prueba.")
21
- if not external_user_id:
22
- abort(400, "Error: Debes proporcionar un external_user_id en la URL.")
23
-
24
- target_url = f"{base_url}/{company_short_name}/external_login"
25
-
26
- # --- INICIO DE LA CORRECCIÓN ---
27
- # Usamos el formato de header 'Authorization: Bearer' como solicitaste.
28
- headers = {
29
- 'Content-Type': 'application/json',
30
- 'Authorization': f'Bearer {api_key}'
31
- }
32
- # --- FIN DE LA CORRECCIÓN ---
33
-
34
- payload = {'external_user_id': external_user_id}
35
-
36
- try:
37
- # Llamada POST interna. 'stream=True' es importante para manejar la respuesta.
38
- internal_response = requests.post(target_url, headers=headers, data=json.dumps(payload), timeout=120,
39
- stream=True)
40
- internal_response.raise_for_status()
41
-
42
- # Creamos una nueva Response de Flask para el navegador del usuario.
43
- user_response = Response(
44
- internal_response.iter_content(chunk_size=1024),
45
- status=internal_response.status_code
46
- )
14
+ @inject
15
+ def __init__(self,
16
+ profile_service: ProfileService):
17
+ self.profile_service = profile_service
47
18
 
48
- # Copiamos TODAS las cabeceras de la respuesta interna a la respuesta final,
49
- # incluyendo 'Content-Type' y, crucialmente, 'Set-Cookie'.
50
- for key, value in internal_response.headers.items():
51
- if key.lower() not in ['content-encoding', 'content-length', 'transfer-encoding', 'connection']:
52
- user_response.headers[key] = value
19
+ def get(self, company_short_name: str = None):
53
20
 
54
- return user_response
21
+ # Esta API_KEY para el login
22
+ api_key_for_login = os.getenv("IATOOLKIT_API_KEY", "tu_api_key_por_defecto_o_error")
55
23
 
56
- except requests.exceptions.HTTPError as e:
57
- error_text = f"Error en la llamada interna a la API: {e.response.status_code}. Respuesta: {e.response.text}"
58
- return Response(error_text, status=e.response.status_code, mimetype='text/plain')
59
- except requests.exceptions.RequestException as e:
60
- return Response(f'Error de conexión con el servicio de IA: {str(e)}', status=502, mimetype='text/plain')
24
+ return render_template('login_simulation.html',
25
+ company_short_name=company_short_name,
26
+ api_key=api_key_for_login
27
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iatoolkit
3
- Version: 0.55.2
3
+ Version: 0.55.3
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT
@@ -2,10 +2,10 @@ iatoolkit/__init__.py,sha256=4PWjMJjktixtrxF6BY405qyA50Sv967kEP2x-oil6qk,1120
2
2
  iatoolkit/base_company.py,sha256=uFJmy77LPAceVqkTeuJqo15-auDiq4aTwvC_bbBD0mQ,4607
3
3
  iatoolkit/cli_commands.py,sha256=G5L9xQXZ0lVFXQWBaE_KEZHyfuiT6PL1nTQRoSdnBzc,2302
4
4
  iatoolkit/company_registry.py,sha256=tduqt3oV8iDX_IB1eA7KIgvIxE4edTcy-3qZIXh3Lzw,2549
5
- iatoolkit/iatoolkit.py,sha256=G_4plhotRBuf-u6OsFZhov_MoFbdvyd--u1vVc3hz18,17645
5
+ iatoolkit/iatoolkit.py,sha256=1Uo619dvzhZzMwgycUFLb0iH3eIJiBw-FuDZbQmMsGE,17645
6
6
  iatoolkit/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  iatoolkit/common/exceptions.py,sha256=EXx40n5htp7UiOM6P1xfJ9U6NMcADqm62dlFaKz7ICU,1154
8
- iatoolkit/common/routes.py,sha256=LniD_xsVZ6iHx1Z2_daOChhXZ9AQwsYFtqWSuFkQVyk,6117
8
+ iatoolkit/common/routes.py,sha256=EcfJXJ3e3sQX_jRiMRKambcYoAaLnnjMuKS9H7NqkDM,6082
9
9
  iatoolkit/common/session_manager.py,sha256=UeKfD15bcEA3P5e0WSURfotLqpsiIMp3AXxAMhtgHs0,471
10
10
  iatoolkit/common/util.py,sha256=w9dTd3csK0gKtFSp-a4t7XmCPZiYDhiON92uXRbTT8A,14624
11
11
  iatoolkit/infra/__init__.py,sha256=5JqK9sZ6jBuK83zDQokUhxQ0wuJJJ9DXB8pYCLkX7X4,102
@@ -26,7 +26,7 @@ iatoolkit/infra/connectors/google_drive_connector.py,sha256=WR1AlO5-Bl3W89opdja0
26
26
  iatoolkit/infra/connectors/local_file_connector.py,sha256=hrzIgpMJOTuwTqzlQeTIU_50ZbZ6yl8lcWPv6hMnoqI,1739
27
27
  iatoolkit/infra/connectors/s3_connector.py,sha256=Nj4_YaLobjfcnbZewJf21_K2EXohgcc3mJll1Pzn4zg,1123
28
28
  iatoolkit/repositories/__init__.py,sha256=5JqK9sZ6jBuK83zDQokUhxQ0wuJJJ9DXB8pYCLkX7X4,102
29
- iatoolkit/repositories/database_manager.py,sha256=nrBSAPuFkTYlvAht_3Kp2XeIDiqjq-mBTeqfCoMfn3A,3515
29
+ iatoolkit/repositories/database_manager.py,sha256=QgV8hNnVv9RmeOvUdomdj_mfk0bf3Rl8Ti41a-5zIAY,3700
30
30
  iatoolkit/repositories/document_repo.py,sha256=Y7bF1kZB1HWJsAGjWdF7P2aVYeTYNufq9ngQXp7mDkY,1124
31
31
  iatoolkit/repositories/llm_query_repo.py,sha256=YT_t7cYGQk8rwzH_17-28aTzO-e2jUfa2rvXy8tugvA,3612
32
32
  iatoolkit/repositories/models.py,sha256=3YbIJXNMZiTkMbPyiSOiyzqUKEQF0JIfN4VSWYzwr44,13146
@@ -46,9 +46,9 @@ iatoolkit/services/jwt_service.py,sha256=YoZ9h7_o9xBko-arNQv4MbcwnxoSWVNj4VbZmMo
46
46
  iatoolkit/services/load_documents_service.py,sha256=ZpB0BZ3qX1fGJGBtZtMLbFdWWx0hkPoeCS3OqJKwCTs,7291
47
47
  iatoolkit/services/mail_service.py,sha256=2h-fcF3swZDya_o7IpgXkmuj3iEVHVCiHi7oVxU99sQ,2182
48
48
  iatoolkit/services/onboarding_service.py,sha256=cMO2Ho1-G3wAeVNl-j25LwCMJjRwj3yKHpYKnZUFLDE,2001
49
- iatoolkit/services/profile_service.py,sha256=Y1EbTDNFDWrUQ4QqiaM2OfluZqiJfL9hhbeqJ7nsbtw,20235
49
+ iatoolkit/services/profile_service.py,sha256=w4ZhMuhZMH5_UHbDgvKeZHXNNecEIiExtuKI8fKZL-k,20232
50
50
  iatoolkit/services/prompt_manager_service.py,sha256=U-XmSpkeXvv1KRN4dytdMxSYBMRSB7y-UHcb18mk0nA,8342
51
- iatoolkit/services/query_service.py,sha256=sEmdBDLTpdZbNu43KAUO6vXqqWBBxTWKWlQm4oFGtSk,17796
51
+ iatoolkit/services/query_service.py,sha256=J42KzthYIIsUX8cUS2E7vcXA6IZOJ28Y8I637m-9za4,17649
52
52
  iatoolkit/services/search_service.py,sha256=i1xGWu7ORKIIDH0aAQBkF86dVVbLQ0Yrooz5TiZ6aGo,1823
53
53
  iatoolkit/services/sql_service.py,sha256=MIslAtpJWnTMgSD74nnqTvQj27p-lHiyRXc6OiA2C_c,2172
54
54
  iatoolkit/services/tasks_service.py,sha256=itREO5rDnUIgsqtyCOBKDtH30QL5v1egs4qPTiBK8xU,6865
@@ -81,7 +81,7 @@ iatoolkit/templates/error.html,sha256=c3dxieMygsvdjQMiQu_sn6kqqag9zFtVu-z5FunX6s
81
81
  iatoolkit/templates/forgot_password.html,sha256=NRZqbNHJXSLNArF_KLbzuem-U57v07awS0ikI_DJbfM,2360
82
82
  iatoolkit/templates/header.html,sha256=179agI7rnYwP_rvJNXIiVde5E8Ec5649_XKq6eew2Hk,1263
83
83
  iatoolkit/templates/index.html,sha256=Q2wBHDv3HTBt-jURVLggye7XOqoqDY8E0LnrmLdc3SQ,7910
84
- iatoolkit/templates/login_test.html,sha256=ApvW4l0UmcjJ9pBl3jm-0BojWwiFV6ZyCWz3caM1DN4,4722
84
+ iatoolkit/templates/login_simulation.html,sha256=DqzH0ygJzkGHjdQcoITdONe8GJmv4u_rgoAw1TizYeo,3513
85
85
  iatoolkit/templates/onboarding_shell.html,sha256=r1ivSR2ci8GrDSm1uaD-cf78rfO1bKT5gXa-v5aHLAk,4659
86
86
  iatoolkit/templates/signup.html,sha256=9ArDvcNQgHFR2dwxy-37AXzGUOeOsT7Nz5u0y6fAB3U,4385
87
87
  iatoolkit/templates/test.html,sha256=rwNtxC83tbCl5COZFXYvmRBxxmgFJtPNuVBd_nq9KWY,133
@@ -89,7 +89,7 @@ iatoolkit/views/__init__.py,sha256=5JqK9sZ6jBuK83zDQokUhxQ0wuJJJ9DXB8pYCLkX7X4,1
89
89
  iatoolkit/views/base_login_view.py,sha256=6IWJXEg_6K9cPYoKnG_-Kpt_hIdYnv5ZNC5anIrytXk,2819
90
90
  iatoolkit/views/change_password_view.py,sha256=tM0woZyKdhY4XYjS_YXg2sKq3RYkXGfcq_eVAKrNvNM,4498
91
91
  iatoolkit/views/chat_token_request_view.py,sha256=wf32_A2Sq8NHYWshCwL10Tovd1znLoD0jQjzutR3sVE,4408
92
- iatoolkit/views/external_login_view.py,sha256=qY5wxh98HyKI0Q1Phf6pkOzRGD7PvbH27GUZn0ccYu4,2889
92
+ iatoolkit/views/external_login_view.py,sha256=IC3X0IZYAfTjHfWSzCwrjqlnBin3Ky7hJZzj3AGk8ZI,2884
93
93
  iatoolkit/views/file_store_api_view.py,sha256=Uz9f6sey3_F5K8zuyQz6SwYRKAalCjD1ekf-Mkl_Kfo,2326
94
94
  iatoolkit/views/forgot_password_view.py,sha256=-qKJeeOBqJFdvDUk7rCNg1E1cDQnJQkozPpb0T0FgwA,3159
95
95
  iatoolkit/views/history_api_view.py,sha256=x-tZhB8UzqrD2n-WDIfmHK9iVhGZ9f0yncsGs9mxwt0,1953
@@ -97,7 +97,7 @@ iatoolkit/views/index_view.py,sha256=P5aVdEWxsYOZGbzcXd6WFE733qZ7YXIoeqriUMAM6V8
97
97
  iatoolkit/views/init_context_api_view.py,sha256=1j8NKfODfPrffbA5YO8TPMHh-ildlLNzReIxv_qO-W4,2586
98
98
  iatoolkit/views/llmquery_api_view.py,sha256=Rh-y-VENwwtNsDrYAD_SWKwjK16fW-pFRWlEvI-OYwY,2120
99
99
  iatoolkit/views/llmquery_web_view.py,sha256=WhjlA1mfsoL8hL9tlKQfjCUcaTzT43odlp_uQKmT314,1500
100
- iatoolkit/views/login_simulation_view.py,sha256=hujduAuq84YyHXfG1iVVbypkwYOreKr9FnQNvIXBDyQ,2675
100
+ iatoolkit/views/login_simulation_view.py,sha256=GFKtrHXzU_bPRgtPO1quoPknV_GeI-fUHq9YVPqNrJs,854
101
101
  iatoolkit/views/login_view.py,sha256=3mV-I1KWrVCi4GBmSWCPmIIKj0reZEBMmyTZdBKFCFM,4630
102
102
  iatoolkit/views/prompt_api_view.py,sha256=MP0r-MiswwKcbNc_5KY7aVbHkrR218I8XCiCX1D0yTA,1244
103
103
  iatoolkit/views/signup_view.py,sha256=BCjhM2lMiDPwYrlW_eEwPl-ZLupblbFfsonWtq0E4vU,3922
@@ -105,7 +105,7 @@ iatoolkit/views/tasks_review_view.py,sha256=keLsLCyOTTlcoIapnB_lbuSvLwrPVZVpBiFC
105
105
  iatoolkit/views/tasks_view.py,sha256=a3anTXrJTTvbQuc6PSpOzidLKQFL4hWa7PI2Cppcz8w,4110
106
106
  iatoolkit/views/user_feedback_api_view.py,sha256=59XB9uQLHI4Q6QA4_XhK787HzfXb-c6EY7k1Ccyr4hI,2424
107
107
  iatoolkit/views/verify_user_view.py,sha256=7XLSaxvs8LjBr3cYOUDa9B8DqW_50IGlq0IvmOQcD0Y,2340
108
- iatoolkit-0.55.2.dist-info/METADATA,sha256=hR0pzM6CSYaGhZCLbwA_G4A-_85DUV3ttYIojwpDkY4,9301
109
- iatoolkit-0.55.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
110
- iatoolkit-0.55.2.dist-info/top_level.txt,sha256=V_w4QvDx0b1RXiy8zTCrD1Bp7AZkFe3_O0-9fMiwogg,10
111
- iatoolkit-0.55.2.dist-info/RECORD,,
108
+ iatoolkit-0.55.3.dist-info/METADATA,sha256=JYdzDOAsjbP7j9lAd6wtdNf6Lj2LjJEBpbqIPpCLt6Y,9301
109
+ iatoolkit-0.55.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
110
+ iatoolkit-0.55.3.dist-info/top_level.txt,sha256=V_w4QvDx0b1RXiy8zTCrD1Bp7AZkFe3_O0-9fMiwogg,10
111
+ iatoolkit-0.55.3.dist-info/RECORD,,
@@ -1,118 +0,0 @@
1
- {% extends "base.html" %}
2
-
3
- {% block title %}Prueba de Login para {{ branding.name }}{% endblock %}
4
-
5
- {% block styles %}
6
- {# Este bloque asegura que los colores de la marca estén disponibles como variables CSS #}
7
- {% if branding and branding.css_variables %}
8
- <style>
9
- {{ branding.css_variables|safe }}
10
- /* Usa la variable de CSS para el color primario del botón */
11
- #initiateJwtChatButton {
12
- background-color: var(--brand-primary-color, #0d6efd);
13
- border-color: var(--brand-primary-color, #0d6efd);
14
- }
15
- </style>
16
- {% endif %}
17
- {% endblock %}
18
-
19
- {% block content %}
20
- <div class="container-fluid">
21
- <div class="row flex-fill mt-5 justify-content-center">
22
- <!-- login desde sistema externo -->
23
- <div class="col-12 col-lg-6">
24
- <div class="border rounded p-4 p-md-5 shadow-sm bg-light">
25
- {# El título ahora muestra dinámicamente el nombre de la empresa #}
26
- <h3 class="text-muted fw-semibold text-start mb-3">
27
- Login Externo para <span style="color: var(--brand-primary-color, #0d6efd);">{{ branding.name }}</span>
28
- </h3>
29
- <div class="text-center mb-4">
30
- <p class="text-muted widget-intro-text">
31
- Este formulario permite testear el acceso a IAToolkit desde un portal externo utilizando una api-key. El external user ID es el nombre del usuario ya autentificado en algún portal interno de la empresa.
32
- </p>
33
- </div>
34
-
35
- {# El 'action' y 'method' son manejados por JS, pero el id es crucial #}
36
- <form id="jwt-form">
37
-
38
- <div class="mb-3">
39
- <label for="external_user_id" class="form-label d-block">External user ID</label>
40
- <input type="text" id="external_user_id" name="external_user_id" class="form-control" required>
41
- </div>
42
-
43
- <button type="submit" id="initiateJwtChatButton" class="btn btn-primary">
44
- <span class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
45
- Iniciar Sesión
46
- </button>
47
- </form>
48
- </div>
49
- </div>
50
- </div>
51
- </div>
52
- {% endblock %}
53
-
54
- {% block scripts %}
55
- <script>
56
- document.addEventListener('DOMContentLoaded', function() {
57
- const companyShortName = "{{ company_short_name }}";
58
- const apiKey = "{{ api_key }}";
59
- const loginForm = document.getElementById('jwt-form');
60
- const userIdInput = document.getElementById('external_user_id');
61
- const submitButton = document.getElementById('initiateJwtChatButton');
62
- const spinner = submitButton.querySelector('.spinner-border');
63
-
64
- loginForm.addEventListener('submit', function(event) {
65
- event.preventDefault();
66
- const userIdentifier = userIdInput.value.trim();
67
- if (!userIdentifier) return;
68
-
69
- // Deshabilitar botón y mostrar spinner
70
- submitButton.disabled = true;
71
- spinner.classList.remove('d-none');
72
-
73
- const newWindow = window.open('', '_blank');
74
- const apiUrl = `/${companyShortName}/external_login`;
75
-
76
- fetch(apiUrl, {
77
- method: 'POST',
78
- headers: {
79
- 'Content-Type': 'application/json',
80
- 'Authorization': `Bearer ${apiKey}`
81
- },
82
- body: JSON.stringify({ external_user_id: userIdentifier })
83
- })
84
- .then(response => response.ok ? response.text() : response.text().then(text => { throw new Error(text) }))
85
- .then(html => {
86
- newWindow.document.documentElement.innerHTML = html;
87
- // Buscamos todos los scripts en el HTML inyectado y los re-ejecutamos.
88
- const scripts = newWindow.document.querySelectorAll('script');
89
- scripts.forEach(oldScript => {
90
- const newScript = newWindow.document.createElement('script');
91
-
92
- // Copiamos los atributos (como 'src' para archivos externos)
93
- Array.from(oldScript.attributes).forEach(attr => {
94
- newScript.setAttribute(attr.name, attr.value);
95
- });
96
-
97
- // Copiamos el contenido para scripts inline
98
- newScript.appendChild(newWindow.document.createTextNode(oldScript.innerHTML));
99
-
100
- // Reemplazamos el script viejo por el nuevo para que se ejecute.
101
- oldScript.parentNode.replaceChild(newScript, oldScript);
102
- });
103
-
104
- })
105
- .catch(error => {
106
- console.error("Falló la prueba de login externo:", error);
107
- const errorMessage = error.message || "Error desconocido.";
108
- newWindow.document.body.innerHTML = `<div style="padding: 20px; font-family: monospace;"><h2>Error</h2><pre>${errorMessage}</pre></div>`;
109
- })
110
- .finally(() => {
111
- // Volver a habilitar el botón y ocultar el spinner
112
- submitButton.disabled = false;
113
- spinner.classList.add('d-none');
114
- });
115
- });
116
- });
117
- </script>
118
- {% endblock %}