iatoolkit 0.18.1__py3-none-any.whl → 0.20.0__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.
- iatoolkit/common/routes.py +18 -7
- iatoolkit/iatoolkit.py +4 -1
- iatoolkit/services/branding_service.py +10 -0
- iatoolkit/static/js/chat_history.js +5 -7
- iatoolkit/static/js/chat_main.js +1 -1
- iatoolkit/static/styles/chat_iatoolkit.css +5 -0
- iatoolkit/static/styles/landing_page.css +156 -0
- iatoolkit/templates/_branding_styles.html +53 -0
- iatoolkit/templates/_login_widget.html +42 -0
- iatoolkit/templates/_navbar.html +9 -0
- iatoolkit/templates/base.html +2 -0
- iatoolkit/templates/change_password.html +56 -37
- iatoolkit/templates/chat.html +3 -2
- iatoolkit/templates/forgot_password.html +39 -24
- iatoolkit/templates/index.html +126 -0
- iatoolkit/templates/onboarding_shell.html +1 -1
- iatoolkit/templates/signup.html +62 -66
- iatoolkit/views/change_password_view.py +17 -8
- iatoolkit/views/forgot_password_view.py +15 -9
- iatoolkit/views/index_view.py +43 -0
- iatoolkit/views/init_context_view.py +35 -0
- iatoolkit/views/login_view.py +1 -1
- iatoolkit/views/signup_view.py +30 -25
- iatoolkit/views/verify_user_view.py +6 -6
- {iatoolkit-0.18.1.dist-info → iatoolkit-0.20.0.dist-info}/METADATA +1 -1
- {iatoolkit-0.18.1.dist-info → iatoolkit-0.20.0.dist-info}/RECORD +28 -21
- {iatoolkit-0.18.1.dist-info → iatoolkit-0.20.0.dist-info}/WHEEL +0 -0
- {iatoolkit-0.18.1.dist-info → iatoolkit-0.20.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block title %}IAToolkit - Acelerador de IA para tu Empresa{% endblock %}
|
|
4
|
+
|
|
5
|
+
{% block content %}
|
|
6
|
+
|
|
7
|
+
<!-- Incluimos los estilos de branding reutilizables -->
|
|
8
|
+
{% include '_branding_styles.html' %}
|
|
9
|
+
|
|
10
|
+
<!-- Enlazamos la hoja de estilos de la landing page -->
|
|
11
|
+
<link rel="stylesheet" href="{{ iatoolkit_base_url }}/static/styles/landing_page.css">
|
|
12
|
+
|
|
13
|
+
<!-- 1. Barra de Navegación (sin cambios) -->
|
|
14
|
+
<nav class="navbar landing-navbar">
|
|
15
|
+
<div class="container-fluid">
|
|
16
|
+
<span class="navbar-brand">IAToolkit</span>
|
|
17
|
+
</div>
|
|
18
|
+
</nav>
|
|
19
|
+
|
|
20
|
+
<!-- 2. Sección Principal (sin cambios) -->
|
|
21
|
+
<section class="hero-section">
|
|
22
|
+
<div class="container">
|
|
23
|
+
<div class="row align-items-center">
|
|
24
|
+
<div class="col-lg-7">
|
|
25
|
+
<div class="value-proposition">
|
|
26
|
+
<h1 class="hero-title">El <span class="hero-accelerator">Acelerador</span> de IA para tu Empresa</h1>
|
|
27
|
+
<p class="hero-subtitle">IAToolkit es la plataforma de código abierto que conecta de forma segura tus
|
|
28
|
+
datos internos —bases de datos, documentos y sistemas— para crear un asistente de IA
|
|
29
|
+
que entiende tu negocio desde el primer día.</p>
|
|
30
|
+
<p class="hero-subtitle">
|
|
31
|
+
Es un producto para ingenieros de software que quieren construir aplicaciones de IA
|
|
32
|
+
a gran velocidad. Proveemos la infraestructura fundamental y extensible construida en Python
|
|
33
|
+
para que tú te puedas enfocar en conectar tus datos y crear la lógica de negocio.</p>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="col-lg-5">
|
|
37
|
+
{% include '_login_widget.html' %}
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</section>
|
|
42
|
+
|
|
43
|
+
<!-- 3. Sección de Características con 6 recuadros -->
|
|
44
|
+
<section class="features-section">
|
|
45
|
+
<div class="container">
|
|
46
|
+
<!-- Primera Fila de Características -->
|
|
47
|
+
<div class="row g-4">
|
|
48
|
+
<!-- Feature 1: GitHub / Open Source -->
|
|
49
|
+
<div class="col-lg-4 d-flex align-items-stretch">
|
|
50
|
+
<div class="opensource-box">
|
|
51
|
+
<div>
|
|
52
|
+
<div class="opensource-icon"><i class="bi bi-github"></i></div>
|
|
53
|
+
<div class="opensource-content">
|
|
54
|
+
<h3>100% Open Source</h3>
|
|
55
|
+
<p>Construido en Python, IAToolkit te da control total. Audita el código, adáptalo y contribuye a una comunidad en crecimiento.</p>
|
|
56
|
+
<a href="https://github.com/tu-usuario/iatoolkit" target="_blank" class="btn btn-light"><i class="bi bi-github me-2"></i>Ir a GitHub</a>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<!-- Feature 4: Conectividad -->
|
|
64
|
+
<div class="col-lg-4 d-flex align-items-stretch">
|
|
65
|
+
<div class="feature-item">
|
|
66
|
+
<div class="feature-icon"><i class="bi bi-hdd-stack"></i></div>
|
|
67
|
+
<h3>Conecta tus Datos</h3>
|
|
68
|
+
<p>Integra tus bases de datos SQL, documentos (PDFs, TXT) y otros sistemas para que el asistente tenga un contexto real de tu negocio.</p>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<!-- Feature 2: Multi-LLM -->
|
|
73
|
+
<div class="col-lg-4 d-flex align-items-stretch">
|
|
74
|
+
<div class="feature-item">
|
|
75
|
+
<div class="feature-icon"><i class="bi bi-gem"></i></div>
|
|
76
|
+
<h3>Multi-LLM</h3>
|
|
77
|
+
<p>No te ates a un solo proveedor. IAToolkit está diseñado para funcionar con diferentes modelos, incluyendo Gemini y OpenAI (GPT).</p>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<!-- Segunda Fila de Características -->
|
|
84
|
+
<div class="row g-4 mt-4">
|
|
85
|
+
<div class="col-lg-4 d-flex align-items-stretch">
|
|
86
|
+
<div class="feature-item">
|
|
87
|
+
<div class="feature-icon"><i class="bi bi-magic"></i></div>
|
|
88
|
+
<h3>Prompt Manager</h3>
|
|
89
|
+
<p>Crea, gestiona y comparte una librería de prompts personalizados para tu empresa, optimizando las tareas repetitivas.</p>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
<!-- Feature 6: Personalización -->
|
|
93
|
+
<div class="col-lg-4 d-flex align-items-stretch">
|
|
94
|
+
<div class="feature-item">
|
|
95
|
+
<div class="feature-icon"><i class="bi bi-palette"></i></div>
|
|
96
|
+
<h3>100% Personalizable</h3>
|
|
97
|
+
<p>Desde la apariencia visual hasta las capacidades y herramientas del asistente,
|
|
98
|
+
adapta cada aspecto para que se alinee con la identidad de tu marca.</p>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<!-- Feature 5: Seguridad -->
|
|
103
|
+
<div class="col-lg-4 d-flex align-items-stretch">
|
|
104
|
+
<div class="feature-item">
|
|
105
|
+
<div class="feature-icon"><i class="bi bi-shield-lock"></i></div>
|
|
106
|
+
<h3>Privacidad Primero</h3>
|
|
107
|
+
<p>Despliega IAToolkit en tus propios servidores. Tus datos y consultas nunca son compartidos con terceros, garantizando la máxima confidencialidad.</p>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</section>
|
|
114
|
+
|
|
115
|
+
<!-- 4. Footer (sin cambios) -->
|
|
116
|
+
<footer class="landing-footer">
|
|
117
|
+
<div class="container">
|
|
118
|
+
© IAToolkit - Proyecto Open Source
|
|
119
|
+
</div>
|
|
120
|
+
</footer>
|
|
121
|
+
|
|
122
|
+
{% endblock %}
|
|
123
|
+
|
|
124
|
+
{% block scripts %}
|
|
125
|
+
<!-- No se necesita JavaScript aquí para la funcionalidad de la página -->
|
|
126
|
+
{% endblock %}
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
<!-- MEJORADO: Texto de estado ahora junto al spinner -->
|
|
96
96
|
<div id="loading-status">
|
|
97
97
|
<div class="spinner"></div>
|
|
98
|
-
<p>Inicializando el contexto de
|
|
98
|
+
<p>Inicializando el contexto de {{ branding.name }} para la IA...</p>
|
|
99
99
|
</div>
|
|
100
100
|
|
|
101
101
|
</div>
|
iatoolkit/templates/signup.html
CHANGED
|
@@ -1,78 +1,74 @@
|
|
|
1
1
|
{% extends "base.html" %}
|
|
2
2
|
|
|
3
|
-
{% block title %}Registro de Usuario{% endblock %}
|
|
3
|
+
{% block title %}Registro de Usuario - {{ company.name }}{% endblock %}
|
|
4
4
|
|
|
5
5
|
{% block content %}
|
|
6
|
+
<!-- 1. Incluimos los estilos de branding reutilizables -->
|
|
7
|
+
{% include '_branding_styles.html' %}
|
|
6
8
|
|
|
9
|
+
<!-- Enlazamos la hoja de estilos de la landing page para reutilizar estilos -->
|
|
10
|
+
<link rel="stylesheet" href="{{ iatoolkit_base_url }}/static/styles/landing_page.css">
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
<h4 class="text-muted fw-semibold mb-2 text-start">Crea tu cuenta</h4>
|
|
11
|
-
<p class="text-muted mb-3 text-start" style="text-align: justify;">
|
|
12
|
-
Regístrate para acceder a todas las funcionalidades de nuestra plataforma.
|
|
13
|
-
</p>
|
|
14
|
-
<form action="{{ url_for('signup', company_short_name=company_short_name) }}" method="post" >
|
|
15
|
-
<div class="mb-3 ">
|
|
16
|
-
<label for="email" class="form-label text-secondary">Correo Electrónico</label>
|
|
17
|
-
<input type="email" autocomplete="off" id="email" name="email"
|
|
18
|
-
class="form-control" required
|
|
19
|
-
value="{{ form_data.email if form_data else '' }}">
|
|
20
|
-
</div>
|
|
12
|
+
<!-- 2. Incluimos la barra de navegación reutilizable -->
|
|
13
|
+
{% include '_navbar.html' %}
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<div class="
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<div class="row">
|
|
38
|
-
<!-- En escritorio, los inputs están en la misma fila -->
|
|
39
|
-
<div class="col-md-6 mb-3">
|
|
40
|
-
<label for="first_name" class="form-label text-secondary">Nombre</label>
|
|
41
|
-
<input type="text" id="first_name" name="first_name"
|
|
42
|
-
class="form-control" required
|
|
43
|
-
value="{{ form_data.first_name if form_data else '' }}">
|
|
44
|
-
</div>
|
|
45
|
-
<div class="col-md-6 mb-3">
|
|
46
|
-
<label for="last_name" class="form-label text-secondary">Apellido</label>
|
|
47
|
-
<input type="text" id="last_name" name="last_name"
|
|
48
|
-
class="form-control" required
|
|
49
|
-
value="{{ form_data.last_name if form_data else '' }}">
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
15
|
+
<!-- 3. Sección contenedora para centrar el contenido -->
|
|
16
|
+
<section class="hero-section">
|
|
17
|
+
<div class="container">
|
|
18
|
+
<div class="row justify-content-center">
|
|
19
|
+
<!-- Se ha reducido el ancho de la columna a lg-6 y md-8 -->
|
|
20
|
+
<div class="col-lg-6 col-md-8">
|
|
21
|
+
<div class="border rounded p-4 p-md-5 shadow-sm bg-light">
|
|
22
|
+
<h4 class="form-title fw-bold mb-3 text-center">Crea tu cuenta en {{ company.name }}</h4>
|
|
23
|
+
<form action="{{ url_for('signup', company_short_name=company_short_name) }}" method="post">
|
|
24
|
+
<div class="mb-3">
|
|
25
|
+
<label for="email" class="form-label text-secondary">Correo Electrónico</label>
|
|
26
|
+
<input type="email" autocomplete="off" id="email" name="email"
|
|
27
|
+
class="form-control" required
|
|
28
|
+
value="{{ form_data.email if form_data else '' }}">
|
|
29
|
+
</div>
|
|
52
30
|
|
|
53
|
-
|
|
31
|
+
<div class="row">
|
|
32
|
+
<div class="col-md-6 mb-3">
|
|
33
|
+
<label for="first_name" class="form-label text-secondary">Nombre</label>
|
|
34
|
+
<input type="text" id="first_name" name="first_name"
|
|
35
|
+
class="form-control" required
|
|
36
|
+
value="{{ form_data.first_name if form_data else '' }}">
|
|
37
|
+
</div>
|
|
38
|
+
<div class="col-md-6 mb-3">
|
|
39
|
+
<label for="last_name" class="form-label text-secondary">Apellido</label>
|
|
40
|
+
<input type="text" id="last_name" name="last_name"
|
|
41
|
+
class="form-control" required
|
|
42
|
+
value="{{ form_data.last_name if form_data else '' }}">
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
54
45
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
46
|
+
<div class="mb-3">
|
|
47
|
+
<label for="password" class="form-label text-secondary">Contraseña</label>
|
|
48
|
+
<input type="password" id="password" name="password" class="form-control" required>
|
|
49
|
+
<!-- Bloque de ayuda para la contraseña mejorado -->
|
|
50
|
+
<div class="d-flex align-items-start text-muted mt-2" style="font-size: 0.8rem;">
|
|
51
|
+
<i class="bi bi-info-circle me-2" style="font-size: 0.9rem; line-height: 1.4;"></i>
|
|
52
|
+
<span>Debe contener al menos 8 caracteres, mayúscula, minúscula, número y un carácter especial.</span>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
61
55
|
|
|
56
|
+
<div class="mb-3">
|
|
57
|
+
<label for="confirm_password" class="form-label text-secondary">Confirmar Contraseña</label>
|
|
58
|
+
<input type="password" id="confirm_password" name="confirm_password" class="form-control" required>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<!-- Botón actualizado con la clase de branding -->
|
|
62
|
+
<button type="submit" class="btn btn-branded-primary w-100 fw-bold py-2 mt-3">Registrarse</button>
|
|
63
|
+
</form>
|
|
64
|
+
<!-- Nota de privacidad -->
|
|
65
|
+
<p class="text-muted small mb-0 text-center mt-4">
|
|
66
|
+
🔒 Valoramos tu privacidad. Tus datos se usarán exclusivamente para el funcionamiento de la plataforma.
|
|
67
|
+
</p>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
62
70
|
</div>
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
</div>
|
|
67
|
-
<button type="submit" class="btn btn-primary w-100">Registrarse</button>
|
|
68
|
-
</form>
|
|
69
|
-
<!-- Nota sobre privacidad -->
|
|
70
|
-
<p class="text-muted small mb-0 text-start mt-3" style="text-align: justify;">
|
|
71
|
-
🔒 Valoramos tu privacidad. Tus datos serán utilizados exclusivamente para brindarte una mejor experiencia
|
|
72
|
-
y no serán compartidos con terceros sin tu consentimiento. Para más información, consulta nuestra
|
|
73
|
-
<a href="#" class="text-decoration-none fw-semibold">Política de Privacidad</a>.
|
|
74
|
-
</p>
|
|
75
|
-
</p>
|
|
76
|
-
</div>
|
|
77
|
-
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</section>
|
|
73
|
+
|
|
78
74
|
{% endblock %}
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
6
|
from flask.views import MethodView
|
|
7
|
-
from flask import render_template, request
|
|
7
|
+
from flask import render_template, request, url_for, session, redirect
|
|
8
8
|
from iatoolkit.services.profile_service import ProfileService
|
|
9
|
+
from iatoolkit.services.branding_service import BrandingService
|
|
9
10
|
from itsdangerous import URLSafeTimedSerializer, SignatureExpired
|
|
10
11
|
from flask_bcrypt import Bcrypt
|
|
11
12
|
from injector import inject
|
|
@@ -14,8 +15,11 @@ import os
|
|
|
14
15
|
|
|
15
16
|
class ChangePasswordView(MethodView):
|
|
16
17
|
@inject
|
|
17
|
-
def __init__(self,
|
|
18
|
+
def __init__(self,
|
|
19
|
+
profile_service: ProfileService,
|
|
20
|
+
branding_service: BrandingService):
|
|
18
21
|
self.profile_service = profile_service
|
|
22
|
+
self.branding_service = branding_service # 3. Guardar la instancia
|
|
19
23
|
|
|
20
24
|
self.serializer = URLSafeTimedSerializer(os.getenv("PASS_RESET_KEY"))
|
|
21
25
|
self.bcrypt = Bcrypt()
|
|
@@ -26,16 +30,20 @@ class ChangePasswordView(MethodView):
|
|
|
26
30
|
if not company:
|
|
27
31
|
return render_template('error.html', message=f"Empresa no encontrada: {company_short_name}"), 404
|
|
28
32
|
|
|
33
|
+
branding_data = self.branding_service.get_company_branding(company)
|
|
34
|
+
|
|
29
35
|
try:
|
|
30
36
|
# Decodificar el token
|
|
31
37
|
email = self.serializer.loads(token, salt='password-reset', max_age=3600)
|
|
32
38
|
except SignatureExpired as e:
|
|
33
39
|
return render_template('forgot_password.html',
|
|
40
|
+
branding=branding_data,
|
|
34
41
|
alert_message="El enlace de cambio de contraseña ha expirado. Por favor, solicita uno nuevo.")
|
|
35
42
|
|
|
36
43
|
return render_template('change_password.html',
|
|
37
44
|
company_short_name=company_short_name,
|
|
38
45
|
company=company,
|
|
46
|
+
branding=branding_data,
|
|
39
47
|
token=token, email=email)
|
|
40
48
|
|
|
41
49
|
def post(self, company_short_name: str, token: str):
|
|
@@ -44,6 +52,7 @@ class ChangePasswordView(MethodView):
|
|
|
44
52
|
if not company:
|
|
45
53
|
return render_template('error.html', message=f"Empresa no encontrada: {company_short_name}"), 404
|
|
46
54
|
|
|
55
|
+
branding_data = self.branding_service.get_company_branding(company)
|
|
47
56
|
try:
|
|
48
57
|
# Decodificar el token
|
|
49
58
|
email = self.serializer.loads(token, salt='password-reset', max_age=3600)
|
|
@@ -51,6 +60,7 @@ class ChangePasswordView(MethodView):
|
|
|
51
60
|
return render_template('forgot_password.html',
|
|
52
61
|
company_short_name=company_short_name,
|
|
53
62
|
company=company,
|
|
63
|
+
branding=branding_data,
|
|
54
64
|
alert_message="El enlace de cambio de contraseña ha expirado. Por favor, solicita uno nuevo.")
|
|
55
65
|
|
|
56
66
|
try:
|
|
@@ -72,17 +82,16 @@ class ChangePasswordView(MethodView):
|
|
|
72
82
|
token=token,
|
|
73
83
|
company_short_name=company_short_name,
|
|
74
84
|
company=company,
|
|
85
|
+
branding=branding_data,
|
|
75
86
|
form_data={"temp_code": temp_code,
|
|
76
87
|
"new_password": new_password,
|
|
77
88
|
"confirm_password": confirm_password},
|
|
78
89
|
alert_message=response["error"]), 400
|
|
79
90
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
alert_icon='success',
|
|
85
|
-
alert_message="Tu contraseña ha sido restablecida exitosamente. Ahora puedes iniciar sesión.")
|
|
91
|
+
# Éxito: Guardar mensaje en sesión y redirigir
|
|
92
|
+
session['alert_message'] = "Tu contraseña ha sido restablecida exitosamente. Ahora puedes iniciar sesión."
|
|
93
|
+
session['alert_icon'] = 'success'
|
|
94
|
+
return redirect(url_for('index', company_short_name=company_short_name))
|
|
86
95
|
|
|
87
96
|
except Exception as e:
|
|
88
97
|
return render_template("error.html",
|
|
@@ -4,16 +4,19 @@
|
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
6
|
from flask.views import MethodView
|
|
7
|
-
from flask import render_template, request, url_for
|
|
7
|
+
from flask import render_template, request, url_for, redirect, session
|
|
8
8
|
from injector import inject
|
|
9
9
|
from iatoolkit.services.profile_service import ProfileService
|
|
10
|
+
from iatoolkit.services.branding_service import BrandingService
|
|
10
11
|
from itsdangerous import URLSafeTimedSerializer
|
|
11
12
|
import os
|
|
12
13
|
|
|
13
14
|
class ForgotPasswordView(MethodView):
|
|
14
15
|
@inject
|
|
15
|
-
def __init__(self, profile_service: ProfileService
|
|
16
|
+
def __init__(self, profile_service: ProfileService,
|
|
17
|
+
branding_service: BrandingService):
|
|
16
18
|
self.profile_service = profile_service
|
|
19
|
+
self.branding_service = branding_service # 3. Guardar la instancia
|
|
17
20
|
self.serializer = URLSafeTimedSerializer(os.getenv("PASS_RESET_KEY"))
|
|
18
21
|
|
|
19
22
|
def get(self, company_short_name: str):
|
|
@@ -22,9 +25,11 @@ class ForgotPasswordView(MethodView):
|
|
|
22
25
|
if not company:
|
|
23
26
|
return render_template('error.html', message="Empresa no encontrada"), 404
|
|
24
27
|
|
|
28
|
+
branding_data = self.branding_service.get_company_branding(company)
|
|
25
29
|
return render_template('forgot_password.html',
|
|
26
30
|
company=company,
|
|
27
|
-
company_short_name=company_short_name
|
|
31
|
+
company_short_name=company_short_name,
|
|
32
|
+
branding=branding_data
|
|
28
33
|
)
|
|
29
34
|
|
|
30
35
|
def post(self, company_short_name: str):
|
|
@@ -43,19 +48,20 @@ class ForgotPasswordView(MethodView):
|
|
|
43
48
|
|
|
44
49
|
response = self.profile_service.forgot_password(email=email, reset_url=reset_url)
|
|
45
50
|
if "error" in response:
|
|
51
|
+
branding_data = self.branding_service.get_company_branding(company)
|
|
46
52
|
return render_template(
|
|
47
53
|
'forgot_password.html',
|
|
48
54
|
company=company,
|
|
49
55
|
company_short_name=company_short_name,
|
|
50
|
-
|
|
56
|
+
branding=branding_data,
|
|
57
|
+
form_data={"email": email},
|
|
51
58
|
alert_message=response["error"]), 400
|
|
52
59
|
|
|
60
|
+
# Guardamos el mensaje y el icono en la sesión manualmente
|
|
61
|
+
session['alert_message'] = "Si tu correo está registrado, recibirás un enlace para restablecer tu contraseña."
|
|
62
|
+
session['alert_icon'] = "success"
|
|
63
|
+
return redirect(url_for('index', company_short_name=company_short_name))
|
|
53
64
|
|
|
54
|
-
return render_template('login.html',
|
|
55
|
-
company=company,
|
|
56
|
-
company_short_name=company_short_name,
|
|
57
|
-
alert_icon='success',
|
|
58
|
-
alert_message="Hemos enviado un enlace a tu correo para restablecer la contraseña.")
|
|
59
65
|
except Exception as e:
|
|
60
66
|
return render_template("error.html",
|
|
61
67
|
company=company,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# iatoolkit/views/index_view.py
|
|
2
|
+
|
|
3
|
+
from flask import render_template, abort, session
|
|
4
|
+
from flask.views import MethodView
|
|
5
|
+
from injector import inject
|
|
6
|
+
from iatoolkit.services.profile_service import ProfileService
|
|
7
|
+
from iatoolkit.services.branding_service import BrandingService
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class IndexView(MethodView):
|
|
11
|
+
"""
|
|
12
|
+
Handles the rendering of the company-specific landing page.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@inject
|
|
16
|
+
def __init__(self,
|
|
17
|
+
profile_service: ProfileService,
|
|
18
|
+
branding_service: BrandingService):
|
|
19
|
+
self.profile_service = profile_service
|
|
20
|
+
self.branding_service = branding_service
|
|
21
|
+
|
|
22
|
+
def get(self, company_short_name: str):
|
|
23
|
+
# La vista ahora recibe el company_short_name desde la URL
|
|
24
|
+
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
25
|
+
|
|
26
|
+
if not company:
|
|
27
|
+
abort(404, description=f"La empresa '{company_short_name}' no fue encontrada.")
|
|
28
|
+
|
|
29
|
+
# Obtenemos los datos de branding para la plantilla
|
|
30
|
+
branding_data = self.branding_service.get_company_branding(company)
|
|
31
|
+
|
|
32
|
+
alert_message = session.pop('alert_message', None)
|
|
33
|
+
alert_icon = session.pop('alert_icon', 'error')
|
|
34
|
+
|
|
35
|
+
# 2. Pasamos las variables a la plantilla. Si no hay mensaje, serán None.
|
|
36
|
+
return render_template(
|
|
37
|
+
'index.html',
|
|
38
|
+
company=company,
|
|
39
|
+
company_short_name=company_short_name,
|
|
40
|
+
branding=branding_data,
|
|
41
|
+
alert_message=alert_message,
|
|
42
|
+
alert_icon=alert_icon
|
|
43
|
+
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from flask.views import MethodView
|
|
2
|
+
from injector import inject
|
|
3
|
+
from iatoolkit.common.auth import IAuthentication
|
|
4
|
+
from iatoolkit.services.query_service import QueryService
|
|
5
|
+
from flask import jsonify
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
class InitContextView(MethodView):
|
|
9
|
+
|
|
10
|
+
@inject
|
|
11
|
+
def __init__(self,
|
|
12
|
+
iauthentication: IAuthentication,
|
|
13
|
+
query_service: QueryService
|
|
14
|
+
):
|
|
15
|
+
self.iauthentication = iauthentication
|
|
16
|
+
self.query_service = query_service
|
|
17
|
+
|
|
18
|
+
def get(self, company_short_name: str, external_user_id: str):
|
|
19
|
+
# 1. get access credentials
|
|
20
|
+
iaut = self.iauthentication.verify(company_short_name, external_user_id)
|
|
21
|
+
if not iaut.get("success"):
|
|
22
|
+
return jsonify(iaut), 401
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
# initialize the context
|
|
26
|
+
self.query_service.llm_init_context(
|
|
27
|
+
company_short_name=company_short_name,
|
|
28
|
+
external_user_id=external_user_id
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
return {'status': 'OK'}, 200
|
|
32
|
+
except Exception as e:
|
|
33
|
+
logging.exception(
|
|
34
|
+
f"Error inesperado al inicializar el contexto durante el login para company {company_short_name}: {e}")
|
|
35
|
+
return jsonify({"error_message": str(e)}), 500
|
iatoolkit/views/login_view.py
CHANGED
|
@@ -55,7 +55,7 @@ class InitiateLoginView(MethodView):
|
|
|
55
55
|
"email": email,
|
|
56
56
|
"password": password,
|
|
57
57
|
},
|
|
58
|
-
alert_message=response["
|
|
58
|
+
alert_message=response["message"]), 400
|
|
59
59
|
|
|
60
60
|
# 2. Get branding and onboarding data for the shell page
|
|
61
61
|
branding_data = self.branding_service.get_company_branding(company)
|
iatoolkit/views/signup_view.py
CHANGED
|
@@ -4,18 +4,20 @@
|
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
6
|
from flask.views import MethodView
|
|
7
|
-
from flask import render_template
|
|
7
|
+
from flask import render_template, request, url_for, session, redirect
|
|
8
8
|
from iatoolkit.services.profile_service import ProfileService
|
|
9
|
+
from iatoolkit.services.branding_service import BrandingService # 1. Importar BrandingService
|
|
9
10
|
from injector import inject
|
|
10
11
|
from itsdangerous import URLSafeTimedSerializer
|
|
11
|
-
from flask import url_for, request
|
|
12
12
|
import os
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class SignupView(MethodView):
|
|
16
16
|
@inject
|
|
17
|
-
def __init__(self, profile_service: ProfileService
|
|
17
|
+
def __init__(self, profile_service: ProfileService,
|
|
18
|
+
branding_service: BrandingService):
|
|
18
19
|
self.profile_service = profile_service
|
|
20
|
+
self.branding_service = branding_service # 3. Guardar la instancia
|
|
19
21
|
self.serializer = URLSafeTimedSerializer(os.getenv("USER_VERIF_KEY"))
|
|
20
22
|
|
|
21
23
|
|
|
@@ -25,12 +27,13 @@ class SignupView(MethodView):
|
|
|
25
27
|
if not company:
|
|
26
28
|
return render_template('error.html', message="Empresa no encontrada"), 404
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
# Obtener los datos de branding
|
|
31
|
+
branding_data = self.branding_service.get_company_branding(company)
|
|
32
|
+
|
|
30
33
|
return render_template('signup.html',
|
|
31
34
|
company=company,
|
|
32
35
|
company_short_name=company_short_name,
|
|
33
|
-
|
|
36
|
+
branding=branding_data)
|
|
34
37
|
|
|
35
38
|
def post(self, company_short_name: str):
|
|
36
39
|
# get company info
|
|
@@ -59,26 +62,28 @@ class SignupView(MethodView):
|
|
|
59
62
|
verification_url=verification_url)
|
|
60
63
|
|
|
61
64
|
if "error" in response:
|
|
65
|
+
branding_data = self.branding_service.get_company_branding(company)
|
|
62
66
|
return render_template(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
67
|
+
'signup.html',
|
|
68
|
+
company=company,
|
|
69
|
+
company_short_name=company_short_name,
|
|
70
|
+
branding=branding_data,
|
|
71
|
+
form_data={
|
|
72
|
+
"first_name": first_name,
|
|
73
|
+
"last_name": last_name,
|
|
74
|
+
"email": email,
|
|
75
|
+
"password": password,
|
|
76
|
+
"confirm_password": confirm_password
|
|
77
|
+
},
|
|
78
|
+
alert_message=response["error"]), 400
|
|
79
|
+
|
|
80
|
+
# Guardamos el mensaje de éxito en la sesión
|
|
81
|
+
session['alert_message'] = response["message"]
|
|
82
|
+
session['alert_icon'] = 'success'
|
|
83
|
+
|
|
84
|
+
# Redirigimos al usuario a la página de login
|
|
85
|
+
return redirect(url_for('index', company_short_name=company_short_name))
|
|
86
|
+
|
|
82
87
|
except Exception as e:
|
|
83
88
|
return render_template("error.html",
|
|
84
89
|
company=company,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
6
|
from flask.views import MethodView
|
|
7
|
-
from flask import render_template
|
|
7
|
+
from flask import render_template, url_for, redirect, session
|
|
8
8
|
from iatoolkit.services.profile_service import ProfileService
|
|
9
9
|
from itsdangerous import URLSafeTimedSerializer, SignatureExpired
|
|
10
10
|
from injector import inject
|
|
@@ -43,11 +43,11 @@ class VerifyAccountView(MethodView):
|
|
|
43
43
|
token=token,
|
|
44
44
|
alert_message=response["error"]), 400
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
# Guardamos el mensaje y el icono en la sesión manualmente
|
|
47
|
+
session['alert_message'] = response['message']
|
|
48
|
+
session['alert_icon'] = "success"
|
|
49
|
+
return redirect(url_for('index', company_short_name=company_short_name))
|
|
50
|
+
|
|
51
51
|
except Exception as e:
|
|
52
52
|
return render_template("error.html",
|
|
53
53
|
company=company,
|