- eveai_app adapted to handle removal of complex rewrite rules in nginx.conf, which cannot be achieved in Ingress

This commit is contained in:
Josako
2025-09-06 16:53:51 +02:00
parent b812aedb81
commit 5c20e6c1f9
28 changed files with 341 additions and 336 deletions

View File

@@ -1,27 +1,61 @@
from flask import request, current_app, url_for from flask import request, url_for
from urllib.parse import urlsplit, urlunsplit from urllib.parse import urlsplit, urlunsplit
import re
VISIBLE_PREFIXES = ('/admin', '/api', '/chat-client')
def _derive_visible_prefix():
# 1) Edge-provided header (beste en meest expliciete bron)
xfp = request.headers.get('X-Forwarded-Prefix')
if xfp and any(xfp.startswith(p) for p in VISIBLE_PREFIXES):
return xfp.rstrip('/')
# 2) Referer fallback: haal het top-level segment uit de Referer path
ref = request.headers.get('Referer') or ''
try:
ref_path = urlsplit(ref).path or ''
m = re.match(r'^/(admin|api|chat-client)(?:\b|/)', ref_path)
if m:
return f"/{m.group(1)}"
except Exception:
pass
# 3) Geen prefix bekend
return ''
def prefixed_url_for(endpoint, **values): def prefixed_url_for(endpoint, **values):
prefix = request.headers.get('X-Forwarded-Prefix', '') """
scheme = request.headers.get('X-Forwarded-Proto', request.scheme) Gedrag:
host = request.headers.get('Host', request.host) - Default (_external=False, for_redirect=False): retourneer relatief pad (zonder leading '/')
voor templates/JS. De dynamische <base> zorgt voor correcte resolutie onder het zichtbare prefix.
- _external=True: bouw absolute URL (schema/host). Als X-Forwarded-Prefix aanwezig is,
prefixeer de path daarmee (handig voor e-mails/deeplinks).
- for_redirect=True: geef root-absoluut pad inclusief zichtbaar top-prefix, geschikt
voor HTTP Location headers. Backwards compat: _as_location=True wordt behandeld als for_redirect.
"""
external = values.pop('_external', False) external = values.pop('_external', False)
generated_url = url_for(endpoint, **values) # Backwards compatibility met oudere paramnaam
if values.pop('_as_location', False):
values['for_redirect'] = True
for_redirect = values.pop('for_redirect', False)
generated_url = url_for(endpoint, **values) # bv. "/user/tenant_overview"
path, query, fragment = urlsplit(generated_url)[2:5]
if external: if external:
path, query, fragment = urlsplit(generated_url)[2:5] scheme = request.headers.get('X-Forwarded-Proto', request.scheme)
# Check if the prefix is already present in the path host = request.headers.get('Host', request.host)
if prefix and not path.startswith(prefix): xfp = request.headers.get('X-Forwarded-Prefix', '') or ''
new_path = prefix + path new_path = (xfp.rstrip('/') + path) if (xfp and not path.startswith(xfp)) else path
else:
new_path = path
return urlunsplit((scheme, host, new_path, query, fragment)) return urlunsplit((scheme, host, new_path, query, fragment))
else:
# Check if the prefix is already present in the generated URL
if prefix and not generated_url.startswith(prefix):
return prefix + generated_url
else:
return generated_url
if for_redirect:
visible_prefix = _derive_visible_prefix()
if visible_prefix and not path.startswith(visible_prefix):
return f"{visible_prefix}{path}"
# root-absoluut pad, zonder prefix als onbekend
return path
# Default: relatief pad
return path[1:] if path.startswith('/') else path

View File

@@ -353,9 +353,9 @@ class StagingConfig(Config):
EXPLAIN_TEMPLATE_LOADING = False EXPLAIN_TEMPLATE_LOADING = False
# Define the nginx prefix used for the specific apps # Define the nginx prefix used for the specific apps
EVEAI_APP_LOCATION_PREFIX = '/admin' EVEAI_APP_LOCATION_PREFIX = ''
EVEAI_CHAT_LOCATION_PREFIX = '/chat' EVEAI_CHAT_LOCATION_PREFIX = ''
CHAT_CLIENT_PREFIX = 'chat-client/chat/' CHAT_CLIENT_PREFIX = ''
# Define the static path # Define the static path
STATIC_URL = 'https://evie-staging-static.askeveai.com' STATIC_URL = 'https://evie-staging-static.askeveai.com'
@@ -386,8 +386,8 @@ class ProdConfig(Config):
WTF_CSRF_SSL_STRICT = True # Set to True if using HTTPS WTF_CSRF_SSL_STRICT = True # Set to True if using HTTPS
# Define the nginx prefix used for the specific apps # Define the nginx prefix used for the specific apps
EVEAI_APP_LOCATION_PREFIX = '/admin' EVEAI_APP_LOCATION_PREFIX = ''
EVEAI_CHAT_LOCATION_PREFIX = '/chat' EVEAI_CHAT_LOCATION_PREFIX = ''
# Define the static path # Define the static path
STATIC_URL = 'https://evie-staging-static.askeveai.com' STATIC_URL = 'https://evie-staging-static.askeveai.com'

View File

@@ -119,20 +119,14 @@ def create_app(config_file=None):
from flask_login import current_user from flask_login import current_user
import datetime import datetime
# app.logger.debug(f"Before request - URL: {request.url}") app.logger.debug(f"Before request - URL: {request.url}")
# app.logger.debug(f"Before request - Session permanent: {session.permanent}") app.logger.debug(f"Before request - Session permanent: {session.permanent}")
# Log session expiry tijd als deze bestaat # Log session expiry tijd als deze bestaat
if current_user.is_authenticated: if current_user.is_authenticated:
# Controleer of sessie permanent is (nodig voor PERMANENT_SESSION_LIFETIME) # Controleer of sessie permanent is (nodig voor PERMANENT_SESSION_LIFETIME)
if not session.permanent: if not session.permanent:
session.permanent = True session.permanent = True
# app.logger.debug("Session marked as permanent (enables 60min timeout)")
# Log wanneer sessie zou verlopen
# if '_permanent' in session:
# expires_at = datetime.datetime.now() + app.permanent_session_lifetime
# app.logger.debug(f"Session will expire at: {expires_at} (60 min from now)")
@app.route('/debug/session') @app.route('/debug/session')
def debug_session(): def debug_session():
@@ -185,21 +179,20 @@ def register_extensions(app):
def register_blueprints(app): def register_blueprints(app):
prefix = app.config.get('EVEAI_APP_LOCATION_PREFIX', '/admin')
from .views.user_views import user_bp from .views.user_views import user_bp
app.register_blueprint(user_bp, url_prefix=prefix) app.register_blueprint(user_bp)
from .views.basic_views import basic_bp from .views.basic_views import basic_bp
app.register_blueprint(basic_bp, url_prefix=prefix) app.register_blueprint(basic_bp)
from .views.document_views import document_bp from .views.document_views import document_bp
app.register_blueprint(document_bp, url_prefix=prefix) app.register_blueprint(document_bp)
from .views.security_views import security_bp from .views.security_views import security_bp
app.register_blueprint(security_bp, url_prefix=prefix) app.register_blueprint(security_bp)
from .views.interaction_views import interaction_bp from .views.interaction_views import interaction_bp
app.register_blueprint(interaction_bp, url_prefix=prefix) app.register_blueprint(interaction_bp)
from .views.entitlements_views import entitlements_bp from .views.entitlements_views import entitlements_bp
app.register_blueprint(entitlements_bp, url_prefix=prefix) app.register_blueprint(entitlements_bp)
from .views.partner_views import partner_bp from .views.partner_views import partner_bp
app.register_blueprint(partner_bp, url_prefix=prefix) app.register_blueprint(partner_bp)
from .views.healthz_views import healthz_bp, init_healtz from .views.healthz_views import healthz_bp, init_healtz
app.register_blueprint(healthz_bp) app.register_blueprint(healthz_bp)
init_healtz(app) init_healtz(app)

View File

@@ -8,7 +8,7 @@
{% block content %} {% block content %}
<div class="container"> <div class="container">
<form method="POST" action="{{ url_for('document_bp.handle_library_selection') }}"> <form method="POST">
<div class="form-group mt-3"> <div class="form-group mt-3">
<h2>Create Default RAG Library</h2> <h2>Create Default RAG Library</h2>
<p>This function will create a default library setup for RAG purposes. More specifically, it will create:</p> <p>This function will create a default library setup for RAG purposes. More specifically, it will create:</p>
@@ -35,7 +35,6 @@
Please use it with caution! Please use it with caution!
</p> </p>
<button type="submit" name="action" value="refresh_all_documents" class="btn btn-danger">Refresh All Documents (expensive)</button> <button type="submit" name="action" value="refresh_all_documents" class="btn btn-danger">Refresh All Documents (expensive)</button>
</p>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -7,7 +7,7 @@
{% block content_description %}View License Usage{% endblock %} {% block content_description %}View License Usage{% endblock %}
{% block content %} {% block content %}
<form action="{{ url_for('entitlements_bp.handle_usage_selection') }}" method="POST"> <form method="POST">
{{ render_selectable_table(headers=["Usage ID", "Start Date", "End Date", "Storage (MiB)", "Embedding (MiB)", "Interaction (tokens)"], rows=rows, selectable=False, id="usagesTable") }} {{ render_selectable_table(headers=["Usage ID", "Start Date", "End Date", "Storage (MiB)", "Embedding (MiB)", "Interaction (tokens)"], rows=rows, selectable=False, id="usagesTable") }}
<!-- <div class="form-group mt-3">--> <!-- <div class="form-group mt-3">-->
<!-- <button type="submit" name="action" value="edit_user" class="btn btn-primary">Edit Selected User</button>--> <!-- <button type="submit" name="action" value="edit_user" class="btn btn-primary">Edit Selected User</button>-->

View File

@@ -14,5 +14,6 @@
<!-- Gebundelde CSS (bevat nu al je CSS) --> <!-- Gebundelde CSS (bevat nu al je CSS) -->
<link href="{{url_for('static', filename='dist/main.css')}}" rel="stylesheet" /> <link href="{{url_for('static', filename='dist/main.css')}}" rel="stylesheet" />
<base href="/admin/">
</head> </head>

View File

@@ -9,7 +9,7 @@
{% block content %} {% block content %}
<div class="container"> <div class="container">
<form method="POST" action="{{ url_for(action_url) }}"> <form method="POST">
{{ render_selectable_table(headers=["ID", "Name", "Type"], rows=rows, selectable=True, id=table_id) }} {{ render_selectable_table(headers=["ID", "Name", "Type"], rows=rows, selectable=True, id=table_id) }}
<div class="form-group mt-3"> <div class="form-group mt-3">
<button type="submit" name="action" value="edit_{{component_type|lower}}" class="btn btn-primary">Edit {{ component_type }}</button> <button type="submit" name="action" value="edit_{{component_type|lower}}" class="btn btn-primary">Edit {{ component_type }}</button>

View File

@@ -11,7 +11,7 @@
<div class="row"> <div class="row">
<!-- Main Specialist Editor --> <!-- Main Specialist Editor -->
<div class="col-12" id="mainEditorSection"> <div class="col-12" id="mainEditorSection">
<form method="post" id="specialistForm" action="{{ url_for('interaction_bp.edit_specialist', specialist_id=specialist_id) }}"> <form method="post" id="specialistForm">
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
{% set disabled_fields = ['type', 'type_version'] %} {% set disabled_fields = ['type', 'type_version'] %}
{% set exclude_fields = [] %} {% set exclude_fields = [] %}
@@ -348,7 +348,7 @@ document.addEventListener('DOMContentLoaded', function() {
editorTabLink.classList.contains('component-task') ? 'task' : 'tool'; editorTabLink.classList.contains('component-task') ? 'task' : 'tool';
// Submit the data // Submit the data
fetch(`/admin/interaction/${componentType}/${componentId}/save`, { fetch(`interaction/${componentType}/${componentId}/save`, {
method: 'POST', method: 'POST',
body: formData, body: formData,
headers: { headers: {

View File

@@ -9,7 +9,7 @@
{% block content %} {% block content %}
<div class="container"> <div class="container">
<form method="POST" action="{{ url_for('interaction_bp.handle_chat_session_selection') }}" id="chatSessionsForm"> <form method="POST" id="chatSessionsForm">
{{ render_selectable_table(headers=["ID", "Question At", "Detailed Question At", "Answer At", "Processing Error"], rows=rows, selectable=False, id="interactionsTable") }} {{ render_selectable_table(headers=["ID", "Question At", "Detailed Question At", "Answer At", "Processing Error"], rows=rows, selectable=False, id="interactionsTable") }}
{# <div class="form-group mt-3 d-flex justify-content-between">#} {# <div class="form-group mt-3 d-flex justify-content-between">#}
{# <div>#} {# <div>#}

View File

@@ -6,7 +6,7 @@
{% block content %} {% block content %}
<div class="container"> <div class="container">
<form method="POST" action="{{ form_action }}" id="{{ table_id }}-form"> <form method="POST" action="{{ form_action.lstrip('/') }}" id="{{ table_id }}-form">
{% include 'eveai_list_view.html' %} {% include 'eveai_list_view.html' %}
</form> </form>
</div> </div>

View File

@@ -53,7 +53,7 @@
<a class="navbar-brand font-weight-bolder ms-sm-3 d-block d-md-none" href=" https://www.flow-it.net " rel="tooltip" title="Realised by Josako & Kobe" data-placement="bottom" target="_blank"> <a class="navbar-brand font-weight-bolder ms-sm-3 d-block d-md-none" href=" https://www.flow-it.net " rel="tooltip" title="Realised by Josako & Kobe" data-placement="bottom" target="_blank">
EveAI EveAI
</a> </a>
<a href="/session_defaults" class="btn btn-sm bg-gradient-primary mb-0 ms-auto d-lg-none d-block"> <a href="session_defaults" class="btn btn-sm bg-gradient-primary mb-0 ms-auto d-lg-none d-block">
{% if 'tenant' in session %} {% if 'tenant' in session %}
TENANT: {{ session['tenant'].get('name', 'None') }} TENANT: {{ session['tenant'].get('name', 'None') }}
{% endif %} {% endif %}
@@ -69,24 +69,24 @@
<ul class="navbar-nav navbar-nav-hover mx-auto"> <ul class="navbar-nav navbar-nav-hover mx-auto">
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
{{ dropdown('Tenants', 'source_environment', [ {{ dropdown('Tenants', 'source_environment', [
{'name': 'Tenants', 'url': '/user/tenants', 'roles': ['Super User', 'Partner Admin']}, {'name': 'Tenants', 'url': 'user/tenants', 'roles': ['Super User', 'Partner Admin']},
{'name': 'Tenant Overview', 'url': '/user/tenant_overview', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Tenant Overview', 'url': 'user/tenant_overview', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Edit Tenant', 'url': '/user/tenant/' ~ session['tenant'].get('id'), 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Edit Tenant', 'url': 'user/tenant/' ~ session['tenant'].get('id'), 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Tenant Partner Services', 'url': '/user/tenant_partner_services', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Tenant Partner Services', 'url': 'user/tenant_partner_services', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Tenant Makes', 'url': '/user/tenant_makes', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Tenant Makes', 'url': 'user/tenant_makes', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Tenant Projects', 'url': '/user/tenant_projects', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Tenant Projects', 'url': 'user/tenant_projects', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Users', 'url': '/user/view_users', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Users', 'url': 'user/view_users', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
]) }} ]) }}
{% endif %} {% endif %}
{% if current_user.is_authenticated and current_user.has_roles('Super User') %} {% if current_user.is_authenticated and current_user.has_roles('Super User') %}
{% set partner_menu_items = [ {% set partner_menu_items = [
{'name': 'Partners', 'url': '/partner/partners', 'roles': ['Super User']}, {'name': 'Partners', 'url': 'partner/partners', 'roles': ['Super User']},
{'name': 'Partner Services', 'url': '/partner/partner_services', 'roles': ['Super User']} {'name': 'Partner Services', 'url': 'partner/partner_services', 'roles': ['Super User']}
] %} ] %}
{% if 'partner' in session and session['partner'] %} {% if 'partner' in session and session['partner'] %}
{% set partner_menu_items = partner_menu_items + [ {% set partner_menu_items = partner_menu_items + [
{'name': 'Edit Partner', 'url': '/partner/partner/' ~ session['partner'].get('id'), 'roles': ['Super User']} {'name': 'Edit Partner', 'url': 'partner/partner/' ~ session['partner'].get('id'), 'roles': ['Super User']}
] %} ] %}
{% endif %} {% endif %}
@@ -94,42 +94,42 @@
{% endif %} {% endif %}
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
{{ dropdown('Documents', 'note_stack', [ {{ dropdown('Documents', 'note_stack', [
{'name': 'Catalogs', 'url': '/document/catalogs', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Catalogs', 'url': 'document/catalogs', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Processors', 'url': '/document/processors', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Processors', 'url': 'document/processors', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Retrievers', 'url': '/document/retrievers', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Retrievers', 'url': 'document/retrievers', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Add Document', 'url': '/document/add_document', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Add Document', 'url': 'document/add_document', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Add URL', 'url': '/document/add_url', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Add URL', 'url': 'document/add_url', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Documents', 'url': '/document/documents', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Documents', 'url': 'document/documents', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Document Processing', 'url': '/document/documents_processing', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Document Processing', 'url': 'document/documents_processing', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Library Operations', 'url': '/document/library_operations', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Library Operations', 'url': 'document/library_operations', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
]) }} ]) }}
{% endif %} {% endif %}
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
{{ dropdown('Interactions', 'hub', [ {{ dropdown('Interactions', 'hub', [
{'name': 'Specialists', 'url': '/interaction/specialists', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Specialists', 'url': 'interaction/specialists', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Specialist Magic Links', 'url': '/interaction/specialist_magic_links', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Specialist Magic Links', 'url': 'interaction/specialist_magic_links', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Assets', 'url': '/interaction/assets', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Assets', 'url': 'interaction/assets', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Data Capsules', 'url': '/interaction//eveai_data_capsules', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Data Capsules', 'url': 'interaction/eveai_data_capsules', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Chat Sessions', 'url': '/interaction/chat_sessions', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Chat Sessions', 'url': 'interaction/chat_sessions', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
]) }} ]) }}
{% endif %} {% endif %}
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
{{ dropdown('Entitlements', 'settings', [ {{ dropdown('Entitlements', 'settings', [
{'name': 'License Tiers', 'url': '/entitlements/license_tiers', 'roles': ['Super User', 'Partner Admin']}, {'name': 'License Tiers', 'url': 'entitlements/license_tiers', 'roles': ['Super User', 'Partner Admin']},
{'name': 'Trigger Actions', 'url': '/administration/trigger_actions', 'roles': ['Super User']}, {'name': 'Trigger Actions', 'url': 'administration/trigger_actions', 'roles': ['Super User']},
{'name': 'Licenses', 'url': '/entitlements/licenses', 'roles': ['Super User', 'Tenant Admin', 'Partner Admin']}, {'name': 'Licenses', 'url': 'entitlements/licenses', 'roles': ['Super User', 'Tenant Admin', 'Partner Admin']},
{'name': 'Active Usage', 'url': '/entitlements/active_usage', 'roles': ['Super User', 'Tenant Admin', 'Partner Admin']}, {'name': 'Active Usage', 'url': 'entitlements/active_usage', 'roles': ['Super User', 'Tenant Admin', 'Partner Admin']},
]) }} ]) }}
{% endif %} {% endif %}
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
{{ dropdown(current_user.user_name, 'person', [ {{ dropdown(current_user.user_name, 'person', [
{'name': 'Session Defaults', 'url': '/session_defaults', 'roles': ['Super User', 'Tenant Admin']}, {'name': 'Session Defaults', 'url': 'session_defaults', 'roles': ['Super User', 'Tenant Admin']},
{'name': 'Release Notes', 'url': '/release_notes', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Release Notes', 'url': 'release_notes', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Logout', 'url': '/logout'} {'name': 'Logout', 'url': 'logout'}
]) }} ]) }}
{% else %} {% else %}
{{ dropdown('Account', 'person', [ {{ dropdown('Account', 'person', [
{'name': 'Login', 'url': '/login'} {'name': 'Login', 'url': 'login'}
]) }} ]) }}
{% endif %} {% endif %}
</ul> </ul>

View File

@@ -6,7 +6,7 @@
{% block content %} {% block content %}
<!-- Trigger action Form --> <!-- Trigger action Form -->
<form method="POST" action="{{ url_for('partner_bp.handle_trigger_action') }}"> <form method="POST">
<div class="form-group mt-3"> <div class="form-group mt-3">
<button type="submit" name="action" value="update_usages" class="btn btn-secondary">Update Usages</button> <button type="submit" name="action" value="update_usages" class="btn btn-secondary">Update Usages</button>
</div> </div>

View File

@@ -7,7 +7,7 @@
{% block content %} {% block content %}
{% include "security/_messages.html" %} {% include "security/_messages.html" %}
<form action="{{ url_for('security_bp.forgot_password') }}" method="post" novalidate> <form method="post" novalidate>
{{ forgot_password_form.hidden_tag() }} {{ forgot_password_form.hidden_tag() }}
<p> <p>

View File

@@ -4,7 +4,7 @@
{% block content_title %}Sign In{% endblock %} {% block content_title %}Sign In{% endblock %}
{% block content_description %}Enter your email and password to Sign In{% endblock %} {% block content_description %}Enter your email and password to Sign In{% endblock %}
{% block content %} {% block content %}
<form action="{{ url_for('security_bp.login') }}" method="post" novalidate> <form method="post" novalidate>
{{ login_user_form.hidden_tag() }} {{ login_user_form.hidden_tag() }}
<p> <p>
{{ login_user_form.email.label }}<br> {{ login_user_form.email.label }}<br>

View File

@@ -28,7 +28,7 @@
const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
// Send timezone to the server via a POST request // Send timezone to the server via a POST request
fetch('/set_user_timezone', { fetch('set_user_timezone', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'

View File

@@ -32,7 +32,7 @@
const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
// Send timezone to the server via a POST request // Send timezone to the server via a POST request
fetch('/set_user_timezone', { fetch('set_user_timezone', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'

View File

@@ -7,7 +7,7 @@
{% block content_description %}Select the domain you'd like to action upon{% endblock %} {% block content_description %}Select the domain you'd like to action upon{% endblock %}
{% block content %} {% block content %}
<form action="{{ url_for('user_bp.handle_tenant_domain_action') }}" method="POST" id="tenantDomainsForm"> <form method="POST" id="tenantDomainsForm">
{{ render_selectable_table(headers=["ID", "Domain Name", "Valid To"], rows=rows, selectable=True, id="tenantDomainsTable") }} {{ render_selectable_table(headers=["ID", "Domain Name", "Valid To"], rows=rows, selectable=True, id="tenantDomainsTable") }}
<div class="form-group mt-3 d-flex justify-content-between"> <div class="form-group mt-3 d-flex justify-content-between">
<div> <div>

View File

@@ -70,7 +70,7 @@ def session_defaults():
session.pop('catalog_name', None) session.pop('catalog_name', None)
flash('Session defaults updated successfully', 'success') flash('Session defaults updated successfully', 'success')
return redirect(prefixed_url_for('basic_bp.index')) return redirect(prefixed_url_for('basic_bp.index', for_redirect=True))
return render_template('basic/session_defaults.html', form=form) return render_template('basic/session_defaults.html', form=form)
@@ -80,7 +80,7 @@ def session_defaults():
except Exception as e: except Exception as e:
current_app.logger.error(f"Error in session_defaults: {str(e)}") current_app.logger.error(f"Error in session_defaults: {str(e)}")
flash('Error accessing catalog data. Please ensure your session is valid.', 'danger') flash('Error accessing catalog data. Please ensure your session is valid.', 'danger')
return redirect(prefixed_url_for('security_bp.login')) return redirect(prefixed_url_for('security_bp.login', for_redirect=True))
@basic_bp.route('/set_user_timezone', methods=['POST']) @basic_bp.route('/set_user_timezone', methods=['POST'])
@@ -129,7 +129,7 @@ def view_content(content_type):
if not content_data: if not content_data:
flash(f'Content van type {content_type} werd niet gevonden.', 'danger') flash(f'Content van type {content_type} werd niet gevonden.', 'danger')
return redirect(prefixed_url_for('basic_bp.index')) return redirect(prefixed_url_for('basic_bp.index', for_redirect=True))
# Titels en beschrijvingen per contenttype # Titels en beschrijvingen per contenttype
titles = { titles = {
@@ -156,10 +156,10 @@ def view_content(content_type):
except Exception as e: except Exception as e:
current_app.logger.error(f"Error displaying content {content_type}: {str(e)}") current_app.logger.error(f"Error displaying content {content_type}: {str(e)}")
flash(f'Error displaying content: {str(e)}', 'danger') flash(f'Error displaying content: {str(e)}', 'danger')
return redirect(prefixed_url_for('basic_bp.index')) return redirect(prefixed_url_for('basic_bp.index', for_redirect=True))
@basic_bp.route('/release_notes', methods=['GET']) @basic_bp.route('/release_notes', methods=['GET'])
@roles_accepted('Super User', 'Partner Admin', 'Tenant Admin') @roles_accepted('Super User', 'Partner Admin', 'Tenant Admin')
def release_notes(): def release_notes():
"""Doorverwijzen naar de nieuwe content view voor changelog""" """Doorverwijzen naar de nieuwe content view voor changelog"""
return redirect(prefixed_url_for('basic_bp.view_content', content_type='changelog')) return redirect(prefixed_url_for('basic_bp.view_content', content_type='changelog', for_redirect=True))

View File

@@ -74,7 +74,7 @@ def catalog():
flash('Catalog successfully added!', 'success') flash('Catalog successfully added!', 'success')
current_app.logger.info(f'Catalog {new_catalog.name} successfully added for tenant {tenant_id}!') current_app.logger.info(f'Catalog {new_catalog.name} successfully added for tenant {tenant_id}!')
# Enable step 2 of creation of catalog - add configuration of the catalog (dependent on type) # Enable step 2 of creation of catalog - add configuration of the catalog (dependent on type)
return redirect(prefixed_url_for('document_bp.catalog', catalog_id=new_catalog.id)) return redirect(prefixed_url_for('document_bp.catalog', catalog_id=new_catalog.id, for_redirect=True))
except SQLAlchemyError as e: except SQLAlchemyError as e:
db.session.rollback() db.session.rollback()
flash(f'Failed to add catalog. Error: {e}', 'danger') flash(f'Failed to add catalog. Error: {e}', 'danger')
@@ -97,7 +97,7 @@ def catalogs():
def handle_catalog_selection(): def handle_catalog_selection():
action = request.form['action'] action = request.form['action']
if action == 'create_catalog': if action == 'create_catalog':
return redirect(prefixed_url_for('document_bp.catalog')) return redirect(prefixed_url_for('document_bp.catalog', for_redirect=True))
catalog_identification = request.form.get('selected_row') catalog_identification = request.form.get('selected_row')
catalog_id = ast.literal_eval(catalog_identification).get('value') catalog_id = ast.literal_eval(catalog_identification).get('value')
catalog = Catalog.query.get_or_404(catalog_id) catalog = Catalog.query.get_or_404(catalog_id)
@@ -108,9 +108,9 @@ def handle_catalog_selection():
session['catalog_name'] = catalog.name session['catalog_name'] = catalog.name
session['catalog'] = catalog.to_dict() session['catalog'] = catalog.to_dict()
elif action == 'edit_catalog': elif action == 'edit_catalog':
return redirect(prefixed_url_for('document_bp.edit_catalog', catalog_id=catalog_id)) return redirect(prefixed_url_for('document_bp.edit_catalog', catalog_id=catalog_id, for_redirect=True))
return redirect(prefixed_url_for('document_bp.catalogs')) return redirect(prefixed_url_for('document_bp.catalogs', for_redirect=True))
@document_bp.route('/catalog/<int:catalog_id>', methods=['GET', 'POST']) @document_bp.route('/catalog/<int:catalog_id>', methods=['GET', 'POST'])
@@ -136,7 +136,7 @@ def edit_catalog(catalog_id):
flash(f'Failed to update catalog. Error: {e}', 'danger') flash(f'Failed to update catalog. Error: {e}', 'danger')
current_app.logger.error(f'Failed to update catalog {catalog_id} for tenant {tenant_id}. Error: {str(e)}') current_app.logger.error(f'Failed to update catalog {catalog_id} for tenant {tenant_id}. Error: {str(e)}')
return redirect(prefixed_url_for('document_bp.catalogs')) return redirect(prefixed_url_for('document_bp.catalogs', for_redirect=True))
else: else:
form_validation_failed(request, form) form_validation_failed(request, form)
@@ -166,7 +166,7 @@ def processor():
flash('Processor successfully added!', 'success') flash('Processor successfully added!', 'success')
current_app.logger.info(f'Processor {new_processor.name} successfully added for tenant {tenant_id}!') current_app.logger.info(f'Processor {new_processor.name} successfully added for tenant {tenant_id}!')
# Enable step 2 of creation of retriever - add configuration of the retriever (dependent on type) # Enable step 2 of creation of retriever - add configuration of the retriever (dependent on type)
return redirect(prefixed_url_for('document_bp.edit_processor', processor_id=new_processor.id)) return redirect(prefixed_url_for('document_bp.edit_processor', processor_id=new_processor.id, for_redirect=True))
except SQLAlchemyError as e: except SQLAlchemyError as e:
db.session.rollback() db.session.rollback()
flash(f'Failed to add processor. Error: {e}', 'danger') flash(f'Failed to add processor. Error: {e}', 'danger')
@@ -215,7 +215,7 @@ def edit_processor(processor_id):
current_app.logger.error(f'Failed to update processor {processor_id}. Error: {str(e)}') current_app.logger.error(f'Failed to update processor {processor_id}. Error: {str(e)}')
return render_template('document/edit_processor.html', form=form, processor_id=processor_id) return render_template('document/edit_processor.html', form=form, processor_id=processor_id)
return redirect(prefixed_url_for('document_bp.processors')) return redirect(prefixed_url_for('document_bp.processors', for_redirect=True))
else: else:
form_validation_failed(request, form) form_validation_failed(request, form)
@@ -228,7 +228,7 @@ def processors():
catalog_id = session.get('catalog_id', None) catalog_id = session.get('catalog_id', None)
if not catalog_id: if not catalog_id:
flash('You need to set a Session Catalog before adding Documents or URLs', 'warning') flash('You need to set a Session Catalog before adding Documents or URLs', 'warning')
return redirect(prefixed_url_for('document_bp.catalogs')) return redirect(prefixed_url_for('document_bp.catalogs', for_redirect=True))
# Get configuration and render the list view # Get configuration and render the list view
config = get_processors_list_view(catalog_id) config = get_processors_list_view(catalog_id)
@@ -240,14 +240,14 @@ def processors():
def handle_processor_selection(): def handle_processor_selection():
action = request.form['action'] action = request.form['action']
if action == 'create_processor': if action == 'create_processor':
return redirect(prefixed_url_for('document_bp.processor')) return redirect(prefixed_url_for('document_bp.processor', for_redirect=True))
processor_identification = request.form.get('selected_row') processor_identification = request.form.get('selected_row')
processor_id = ast.literal_eval(processor_identification).get('value') processor_id = ast.literal_eval(processor_identification).get('value')
if action == 'edit_processor': if action == 'edit_processor':
return redirect(prefixed_url_for('document_bp.edit_processor', processor_id=processor_id)) return redirect(prefixed_url_for('document_bp.edit_processor', processor_id=processor_id, for_redirect=True))
return redirect(prefixed_url_for('document_bp.processors')) return redirect(prefixed_url_for('document_bp.processors', for_redirect=True))
# Retriever Management ---------------------------------------------------------------------------- # Retriever Management ----------------------------------------------------------------------------
@@ -272,7 +272,7 @@ def retriever():
flash('Retriever successfully added!', 'success') flash('Retriever successfully added!', 'success')
current_app.logger.info(f'Catalog {new_retriever.name} successfully added for tenant {tenant_id}!') current_app.logger.info(f'Catalog {new_retriever.name} successfully added for tenant {tenant_id}!')
# Enable step 2 of creation of retriever - add configuration of the retriever (dependent on type) # Enable step 2 of creation of retriever - add configuration of the retriever (dependent on type)
return redirect(prefixed_url_for('document_bp.edit_retriever', retriever_id=new_retriever.id)) return redirect(prefixed_url_for('document_bp.edit_retriever', retriever_id=new_retriever.id, for_redirect=True))
except SQLAlchemyError as e: except SQLAlchemyError as e:
db.session.rollback() db.session.rollback()
flash(f'Failed to add retriever. Error: {e}', 'danger') flash(f'Failed to add retriever. Error: {e}', 'danger')
@@ -315,7 +315,7 @@ def edit_retriever(retriever_id):
current_app.logger.error(f'Failed to update retriever {retriever_id}. Error: {str(e)}') current_app.logger.error(f'Failed to update retriever {retriever_id}. Error: {str(e)}')
return render_template('document/edit_retriever.html', form=form, retriever_id=retriever_id) return render_template('document/edit_retriever.html', form=form, retriever_id=retriever_id)
return redirect(prefixed_url_for('document_bp.retrievers')) return redirect(prefixed_url_for('document_bp.retrievers', for_redirect=True))
else: else:
form_validation_failed(request, form) form_validation_failed(request, form)
@@ -328,7 +328,7 @@ def retrievers():
catalog_id = session.get('catalog_id', None) catalog_id = session.get('catalog_id', None)
if not catalog_id: if not catalog_id:
flash('You need to set a Session Catalog before adding Documents or URLs', 'warning') flash('You need to set a Session Catalog before adding Documents or URLs', 'warning')
return redirect(prefixed_url_for('document_bp.catalogs')) return redirect(prefixed_url_for('document_bp.catalogs', for_redirect=True))
# Get configuration and render the list view # Get configuration and render the list view
config = get_retrievers_list_view(catalog_id) config = get_retrievers_list_view(catalog_id)
@@ -340,14 +340,14 @@ def retrievers():
def handle_retriever_selection(): def handle_retriever_selection():
action = request.form['action'] action = request.form['action']
if action == 'create_retriever': if action == 'create_retriever':
return redirect(prefixed_url_for('document_bp.retriever')) return redirect(prefixed_url_for('document_bp.retriever', for_redirect=True))
retriever_identification = request.form.get('selected_row') retriever_identification = request.form.get('selected_row')
retriever_id = ast.literal_eval(retriever_identification).get('value') retriever_id = ast.literal_eval(retriever_identification).get('value')
if action == 'edit_retriever': if action == 'edit_retriever':
return redirect(prefixed_url_for('document_bp.edit_retriever', retriever_id=retriever_id)) return redirect(prefixed_url_for('document_bp.edit_retriever', retriever_id=retriever_id, for_redirect=True))
return redirect(prefixed_url_for('document_bp.retrievers')) return redirect(prefixed_url_for('document_bp.retrievers', for_redirect=True))
# Document Management ----------------------------------------------------------------------------- # Document Management -----------------------------------------------------------------------------
@@ -358,7 +358,7 @@ def add_document():
catalog_id = session.get('catalog_id', None) catalog_id = session.get('catalog_id', None)
if catalog_id is None: if catalog_id is None:
flash('You need to set a Session Catalog before adding Documents or URLs', 'warning') flash('You need to set a Session Catalog before adding Documents or URLs', 'warning')
return redirect(prefixed_url_for('document_bp.catalogs')) return redirect(prefixed_url_for('document_bp.catalogs', for_redirect=True))
catalog = Catalog.query.get_or_404(catalog_id) catalog = Catalog.query.get_or_404(catalog_id)
if catalog.configuration and len(catalog.configuration) > 0: if catalog.configuration and len(catalog.configuration) > 0:
@@ -392,7 +392,7 @@ def add_document():
flash(f'Processing on document {new_doc.name}, version {new_doc_vers.id} started. Task ID: {task_id}.', flash(f'Processing on document {new_doc.name}, version {new_doc_vers.id} started. Task ID: {task_id}.',
'success') 'success')
return redirect(prefixed_url_for('document_bp.documents_processing')) return redirect(prefixed_url_for('document_bp.documents_processing', for_redirect=True))
except EveAIException as e: except EveAIException as e:
flash(str(e), 'danger') flash(str(e), 'danger')
@@ -411,7 +411,7 @@ def add_url():
catalog_id = session.get('catalog_id', None) catalog_id = session.get('catalog_id', None)
if catalog_id is None: if catalog_id is None:
flash('You need to set a Session Catalog before adding Documents or URLs', 'warning') flash('You need to set a Session Catalog before adding Documents or URLs', 'warning')
return redirect(prefixed_url_for('document_bp.catalogs')) return redirect(prefixed_url_for('document_bp.catalogs', for_redirect=True))
catalog = Catalog.query.get_or_404(catalog_id) catalog = Catalog.query.get_or_404(catalog_id)
if catalog.configuration and len(catalog.configuration) > 0: if catalog.configuration and len(catalog.configuration) > 0:
@@ -451,7 +451,7 @@ def add_url():
flash(f'Processing on document {new_doc.name}, version {new_doc_vers.id} started. Task ID: {task_id}.', flash(f'Processing on document {new_doc.name}, version {new_doc_vers.id} started. Task ID: {task_id}.',
'success') 'success')
return redirect(prefixed_url_for('document_bp.documents_processing')) return redirect(prefixed_url_for('document_bp.documents_processing', for_redirect=True))
except EveAIException as e: except EveAIException as e:
current_app.logger.error(f"Error adding document: {str(e)}") current_app.logger.error(f"Error adding document: {str(e)}")
@@ -472,7 +472,7 @@ def documents():
catalog_id = session.get('catalog_id', None) catalog_id = session.get('catalog_id', None)
if not catalog_id: if not catalog_id:
flash('You need to set a Session Catalog before adding Documents or URLs', 'warning') flash('You need to set a Session Catalog before adding Documents or URLs', 'warning')
return redirect(prefixed_url_for('document_bp.catalogs')) return redirect(prefixed_url_for('document_bp.catalogs', for_redirect=True))
config = get_documents_list_view(catalog_id) config = get_documents_list_view(catalog_id)
return render_list_view('list_view.html', **config) return render_list_view('list_view.html', **config)
@@ -484,7 +484,7 @@ def documents_processing():
catalog_id = session.get('catalog_id', None) catalog_id = session.get('catalog_id', None)
if not catalog_id: if not catalog_id:
flash('You need to set a Session Catalog before adding Documents or URLs', 'warning') flash('You need to set a Session Catalog before adding Documents or URLs', 'warning')
return redirect(prefixed_url_for('document_bp.catalogs')) return redirect(prefixed_url_for('document_bp.catalogs', for_redirect=True))
config = get_documents_processing_list_view(catalog_id) config = get_documents_processing_list_view(catalog_id)
return render_list_view('list_view.html', **config) return render_list_view('list_view.html', **config)
@@ -502,16 +502,16 @@ def handle_document_selection():
doc_id = ast.literal_eval(document_identification).get('value') doc_id = ast.literal_eval(document_identification).get('value')
except (ValueError, AttributeError): except (ValueError, AttributeError):
flash('Invalid document selection.', 'danger') flash('Invalid document selection.', 'danger')
return redirect(prefixed_url_for('document_bp.documents')) return redirect(prefixed_url_for('document_bp.documents', for_redirect=True))
action = request.form['action'] action = request.form['action']
match action: match action:
case 'edit_document': case 'edit_document':
return redirect(prefixed_url_for('document_bp.edit_document', document_id=doc_id)) return redirect(prefixed_url_for('document_bp.edit_document', document_id=doc_id, for_redirect=True))
case 'refresh': case 'refresh':
refresh_document_view(doc_id) refresh_document_view(doc_id)
return redirect(prefixed_url_for('document_bp.documents', document_id=doc_id)) return redirect(prefixed_url_for('document_bp.documents', document_id=doc_id, for_redirect=True))
case 're_process': case 're_process':
document = Document.query.get_or_404(doc_id) document = Document.query.get_or_404(doc_id)
doc_vers_id = document.latest_version.id doc_vers_id = document.latest_version.id
@@ -520,14 +520,14 @@ def handle_document_selection():
document = Document.query.get_or_404(doc_id) document = Document.query.get_or_404(doc_id)
doc_vers_id = document.latest_version.id doc_vers_id = document.latest_version.id
return redirect(prefixed_url_for('document_bp.view_document_version_markdown', return redirect(prefixed_url_for('document_bp.view_document_version_markdown',
document_version_id=doc_vers_id)) document_version_id=doc_vers_id, for_redirect=True))
case 'edit_document_version': case 'edit_document_version':
document = Document.query.get_or_404(doc_id) document = Document.query.get_or_404(doc_id)
doc_vers_id = document.latest_version.id doc_vers_id = document.latest_version.id
return redirect(prefixed_url_for('document_bp.edit_document_version', document_version_id=doc_vers_id)) return redirect(prefixed_url_for('document_bp.edit_document_version', document_version_id=doc_vers_id, for_redirect=True))
# Add more conditions for other actions # Add more conditions for other actions
return redirect(prefixed_url_for('document_bp.documents')) return redirect(prefixed_url_for('document_bp.documents', for_redirect=True))
@document_bp.route('/edit_document/<int:document_id>', methods=['GET', 'POST']) @document_bp.route('/edit_document/<int:document_id>', methods=['GET', 'POST'])
@@ -562,7 +562,7 @@ def edit_document(document_id):
) )
if updated_doc: if updated_doc:
flash(f'Document {updated_doc.id} updated successfully', 'success') flash(f'Document {updated_doc.id} updated successfully', 'success')
return redirect(prefixed_url_for('document_bp.documents')) return redirect(prefixed_url_for('document_bp.documents', for_redirect=True))
else: else:
flash(f'Error updating document: {error}', 'danger') flash(f'Error updating document: {error}', 'danger')
else: else:
@@ -600,7 +600,7 @@ def edit_document_version(document_version_id):
) )
if updated_version: if updated_version:
flash(f'Document Version {updated_version.id} updated successfully', 'success') flash(f'Document Version {updated_version.id} updated successfully', 'success')
return redirect(prefixed_url_for('document_bp.documents', document_id=updated_version.doc_id)) return redirect(prefixed_url_for('document_bp.documents', document_id=updated_version.doc_id, for_redirect=True))
else: else:
flash(f'Error updating document version: {error}', 'danger') flash(f'Error updating document version: {error}', 'danger')
else: else:
@@ -644,7 +644,7 @@ def view_document_version_markdown(document_version_id):
except Exception as e: except Exception as e:
current_app.logger.error(f"Error retrieving markdown for document version {document_version_id}: {str(e)}") current_app.logger.error(f"Error retrieving markdown for document version {document_version_id}: {str(e)}")
flash(f"Error retrieving processed document: {str(e)}", "danger") flash(f"Error retrieving processed document: {str(e)}", "danger")
return redirect(prefixed_url_for('document_bp.document_versions')) return redirect(prefixed_url_for('document_bp.document_versions', for_redirect=True))
# Utilities --------------------------------------------------------------------------------------- # Utilities ---------------------------------------------------------------------------------------
@@ -667,7 +667,7 @@ def handle_library_selection():
case 'refresh_all_documents': case 'refresh_all_documents':
refresh_all_documents() refresh_all_documents()
return redirect(prefixed_url_for('document_bp.library_operations')) return redirect(prefixed_url_for('document_bp.library_operations', for_redirect=True))
def create_default_rag_library(): def create_default_rag_library():
@@ -675,7 +675,7 @@ def create_default_rag_library():
catalogs = Catalog.query.all() catalogs = Catalog.query.all()
if catalogs: if catalogs:
flash("Default RAG Library can only be created if no catalogs are defined!", 'danger') flash("Default RAG Library can only be created if no catalogs are defined!", 'danger')
return redirect(prefixed_url_for('document_bp.library_operations')) return redirect(prefixed_url_for('document_bp.library_operations', for_redirect=True))
timestamp = dt.now(tz=tz.utc) timestamp = dt.now(tz=tz.utc)
try: try:
@@ -749,7 +749,7 @@ def create_default_rag_library():
current_app.logger.error(f'Failed to create Default RAG Library' current_app.logger.error(f'Failed to create Default RAG Library'
f'for tenant {session['tenant']['id']}. Error: {str(e)}') f'for tenant {session['tenant']['id']}. Error: {str(e)}')
return redirect(prefixed_url_for('document_bp.library_operations')) return redirect(prefixed_url_for('document_bp.library_operations', for_redirect=True))
def refresh_all_documents(): def refresh_all_documents():
@@ -763,7 +763,7 @@ def refresh_document_view(document_id):
flash(f'Document refreshed. New version: {new_version.id}. Task ID: {result}', 'success') flash(f'Document refreshed. New version: {new_version.id}. Task ID: {result}', 'success')
else: else:
flash(f'Error refreshing document: {result}', 'danger') flash(f'Error refreshing document: {result}', 'danger')
return redirect(prefixed_url_for('document_bp.documents')) return redirect(prefixed_url_for('document_bp.documents', for_redirect=True))
def re_embed_latest_versions(): def re_embed_latest_versions():
@@ -785,7 +785,7 @@ def process_version(version_id):
flash(f'Processing for document version {version_id} retriggered successfully...', 'success') flash(f'Processing for document version {version_id} retriggered successfully...', 'success')
return redirect(prefixed_url_for('document_bp.documents')) return redirect(prefixed_url_for('document_bp.documents', for_redirect=True))
def set_logging_information(obj, timestamp): def set_logging_information(obj, timestamp):

View File

@@ -47,7 +47,7 @@ def license_tier():
current_app.logger.info(f"Successfully created license tier {new_license_tier.id}") current_app.logger.info(f"Successfully created license tier {new_license_tier.id}")
flash(f"Successfully created tenant license tier {new_license_tier.id}", 'success') flash(f"Successfully created tenant license tier {new_license_tier.id}", 'success')
return redirect(prefixed_url_for('entitlements_bp.license_tiers')) return redirect(prefixed_url_for('entitlements_bp.license_tiers', for_redirect=True))
else: else:
form_validation_failed(request, form) form_validation_failed(request, form)
@@ -71,7 +71,7 @@ def license_tiers():
def handle_license_tier_selection(): def handle_license_tier_selection():
action = request.form['action'] action = request.form['action']
if action == 'create_license_tier': if action == 'create_license_tier':
return redirect(prefixed_url_for('entitlements_bp.license_tier')) return redirect(prefixed_url_for('entitlements_bp.license_tier', for_redirect=True))
license_tier_identification = request.form['selected_row'] license_tier_identification = request.form['selected_row']
license_tier_id = ast.literal_eval(license_tier_identification).get('value') license_tier_id = ast.literal_eval(license_tier_identification).get('value')
@@ -79,15 +79,15 @@ def handle_license_tier_selection():
match action: match action:
case 'edit_license_tier': case 'edit_license_tier':
return redirect(prefixed_url_for('entitlements_bp.edit_license_tier', return redirect(prefixed_url_for('entitlements_bp.edit_license_tier',
license_tier_id=license_tier_id)) license_tier_id=license_tier_id, for_redirect=True))
case 'create_license_for_tenant': case 'create_license_for_tenant':
return redirect(prefixed_url_for('entitlements_bp.create_license', return redirect(prefixed_url_for('entitlements_bp.create_license',
license_tier_id=license_tier_id)) license_tier_id=license_tier_id, for_redirect=True))
case 'associate_license_tier_to_partner': case 'associate_license_tier_to_partner':
LicenseTierServices.associate_license_tier_with_partner(license_tier_id) LicenseTierServices.associate_license_tier_with_partner(license_tier_id)
# Add more conditions for other actions # Add more conditions for other actions
return redirect(prefixed_url_for('entitlements_bp.license_tiers')) return redirect(prefixed_url_for('entitlements_bp.license_tiers', for_redirect=True))
@entitlements_bp.route('/license_tier/<int:license_tier_id>', methods=['GET', 'POST']) @entitlements_bp.route('/license_tier/<int:license_tier_id>', methods=['GET', 'POST'])
@@ -113,7 +113,7 @@ def edit_license_tier(license_tier_id):
flash('License Tier updated successfully.', 'success') flash('License Tier updated successfully.', 'success')
return redirect( return redirect(
prefixed_url_for('entitlements_bp.edit_license_tier', license_tier_id=license_tier_id)) prefixed_url_for('entitlements_bp.edit_license_tier', license_tier_id=license_tier_id, for_redirect=True))
else: else:
form_validation_failed(request, form) form_validation_failed(request, form)
@@ -152,7 +152,7 @@ def create_license(license_tier_id):
current_app.logger.error(f'Invalid currency {currency} for tenant {tenant_id} while creating license.') current_app.logger.error(f'Invalid currency {currency} for tenant {tenant_id} while creating license.')
flash(f"Invalid currency {currency} for tenant {tenant_id} while creating license. " flash(f"Invalid currency {currency} for tenant {tenant_id} while creating license. "
f"Check tenant's currency and try again.", 'danger') f"Check tenant's currency and try again.", 'danger')
return redirect(prefixed_url_for('user_bp.edit_tenant', tenant_id=tenant_id)) return redirect(prefixed_url_for('user_bp.edit_tenant', tenant_id=tenant_id, for_redirect=True))
# General data # General data
form.currency.data = currency form.currency.data = currency
form.max_storage_mb.data = license_tier.max_storage_mb form.max_storage_mb.data = license_tier.max_storage_mb
@@ -181,7 +181,7 @@ def create_license(license_tier_id):
db.session.add(new_license) db.session.add(new_license)
db.session.commit() db.session.commit()
flash('License created successfully', 'success') flash('License created successfully', 'success')
return redirect(prefixed_url_for('entitlements_bp.edit_license', license_id=new_license.id)) return redirect(prefixed_url_for('entitlements_bp.edit_license', license_id=new_license.id, for_redirect=True))
except Exception as e: except Exception as e:
db.session.rollback() db.session.rollback()
flash(f'Error creating license: {str(e)}', 'error') flash(f'Error creating license: {str(e)}', 'error')
@@ -223,7 +223,7 @@ def edit_license(license_id):
flash('License updated successfully.', 'success') flash('License updated successfully.', 'success')
return redirect( return redirect(
prefixed_url_for('entitlements_bp.edit_license', license_id=license_id)) prefixed_url_for('entitlements_bp.edit_license', license_id=license_id, for_redirect=True))
else: else:
form_validation_failed(request, form) form_validation_failed(request, form)
@@ -253,11 +253,11 @@ def handle_license_selection():
match action: match action:
case 'edit_license': case 'edit_license':
return redirect(prefixed_url_for('entitlements_bp.edit_license', license_id=license_id)) return redirect(prefixed_url_for('entitlements_bp.edit_license', license_id=license_id, for_redirect=True))
case 'view_periods': case 'view_periods':
return redirect(prefixed_url_for('entitlements_bp.license_periods', license_id=license_id)) return redirect(prefixed_url_for('entitlements_bp.license_periods', license_id=license_id, for_redirect=True))
case _: case _:
return redirect(prefixed_url_for('entitlements_bp.licenses')) return redirect(prefixed_url_for('entitlements_bp.licenses', for_redirect=True))
@entitlements_bp.route('/license/<int:license_id>/periods') @entitlements_bp.route('/license/<int:license_id>/periods')
@@ -270,7 +270,7 @@ def license_periods(license_id):
tenant_id = session.get('tenant').get('id') tenant_id = session.get('tenant').get('id')
if license.tenant_id != tenant_id: if license.tenant_id != tenant_id:
flash('Access denied to this license', 'danger') flash('Access denied to this license', 'danger')
return redirect(prefixed_url_for('entitlements_bp.licenses')) return redirect(prefixed_url_for('entitlements_bp.licenses', for_redirect=True))
config = get_license_periods_list_view(license_id) config = get_license_periods_list_view(license_id)
@@ -315,7 +315,7 @@ def edit_license_period(period_id):
flash(f'Error updating status: {str(e)}', 'danger') flash(f'Error updating status: {str(e)}', 'danger')
current_app.logger.error(f"Error updating period {period_id}: {str(e)}") current_app.logger.error(f"Error updating period {period_id}: {str(e)}")
return redirect(prefixed_url_for('entitlements_bp.license_periods', license_id=period.license_id)) return redirect(prefixed_url_for('entitlements_bp.license_periods', license_id=period.license_id, for_redirect=True))
return render_template('entitlements/edit_license_period.html', form=form) return render_template('entitlements/edit_license_period.html', form=form)
@@ -328,7 +328,7 @@ def handle_license_period_selection(license_id):
# For actions that don't require a selection # For actions that don't require a selection
if 'selected_row' not in request.form: if 'selected_row' not in request.form:
return redirect(prefixed_url_for('entitlements_bp.license_periods', license_id=license_id)) return redirect(prefixed_url_for('entitlements_bp.license_periods', license_id=license_id, for_redirect=True))
period_identification = request.form['selected_row'] period_identification = request.form['selected_row']
period_id = ast.literal_eval(period_identification).get('value') period_id = ast.literal_eval(period_identification).get('value')
@@ -337,19 +337,19 @@ def handle_license_period_selection(license_id):
case 'view_period_details': case 'view_period_details':
# TODO: Implement period details view if needed # TODO: Implement period details view if needed
flash('Period details view not yet implemented', 'info') flash('Period details view not yet implemented', 'info')
return redirect(prefixed_url_for('entitlements_bp.license_periods', license_id=license_id)) return redirect(prefixed_url_for('entitlements_bp.license_periods', license_id=license_id, for_redirect=True))
case 'edit_license_period': case 'edit_license_period':
# Display a form to choose the new status # Display a form to choose the new status
return redirect(prefixed_url_for('entitlements_bp.edit_license_period', period_id=period_id)) return redirect(prefixed_url_for('entitlements_bp.edit_license_period', period_id=period_id, for_redirect=True))
case _: case _:
return redirect(prefixed_url_for('entitlements_bp.license_periods', license_id=license_id)) return redirect(prefixed_url_for('entitlements_bp.license_periods', license_id=license_id, for_redirect=True))
@entitlements_bp.route('/view_licenses') @entitlements_bp.route('/view_licenses')
@roles_accepted('Super User', 'Partner Admin', 'Tenant Admin') @roles_accepted('Super User', 'Partner Admin', 'Tenant Admin')
def view_licenses_redirect(): def view_licenses_redirect():
# Redirect to the new licenses route # Redirect to the new licenses route
return redirect(prefixed_url_for('entitlements_bp.licenses')) return redirect(prefixed_url_for('entitlements_bp.licenses', for_redirect=True))
@entitlements_bp.route('/active_usage') @entitlements_bp.route('/active_usage')
@@ -359,7 +359,7 @@ def active_license_usage():
tenant_id = session.get('tenant', {}).get('id') tenant_id = session.get('tenant', {}).get('id')
if not tenant_id: if not tenant_id:
flash('No active or pending license period found for this tenant', 'warning') flash('No active or pending license period found for this tenant', 'warning')
return redirect(prefixed_url_for('user_bp.tenants')) return redirect(prefixed_url_for('user_bp.tenants', for_redirect=True))
active_period = LicensePeriod.query \ active_period = LicensePeriod.query \
.join(License) \ .join(License) \

View File

@@ -77,12 +77,12 @@ def handle_chat_session_selection():
match action: match action:
case 'view_chat_session': case 'view_chat_session':
return redirect(prefixed_url_for('interaction_bp.view_chat_session', chat_session_id=cs_id)) return redirect(prefixed_url_for('interaction_bp.view_chat_session', chat_session_id=cs_id, for_redirect=True))
case 'chat_session_interactions': case 'chat_session_interactions':
return redirect(prefixed_url_for('interaction_bp.session_interactions', chat_session_id=cs_id)) return redirect(prefixed_url_for('interaction_bp.session_interactions', chat_session_id=cs_id, for_redirect=True))
# Add more conditions for other actions # Add more conditions for other actions
return redirect(prefixed_url_for('interaction_bp.chat_sessions')) return redirect(prefixed_url_for('interaction_bp.chat_sessions', for_redirect=True))
@interaction_bp.route('/view_chat_session/<int:chat_session_id>', methods=['GET']) @interaction_bp.route('/view_chat_session/<int:chat_session_id>', methods=['GET'])
@@ -184,7 +184,7 @@ def specialist():
# Initialize the newly create specialist # Initialize the newly create specialist
SpecialistServices.initialize_specialist(new_specialist.id, new_specialist.type, new_specialist.type_version) SpecialistServices.initialize_specialist(new_specialist.id, new_specialist.type, new_specialist.type_version)
return redirect(prefixed_url_for('interaction_bp.edit_specialist', specialist_id=new_specialist.id)) return redirect(prefixed_url_for('interaction_bp.edit_specialist', specialist_id=new_specialist.id, for_redirect=True))
except Exception as e: except Exception as e:
db.session.rollback() db.session.rollback()
@@ -257,7 +257,7 @@ def edit_specialist(specialist_id):
db.session.commit() db.session.commit()
flash('Specialist updated successfully!', 'success') flash('Specialist updated successfully!', 'success')
current_app.logger.info(f'Specialist {specialist.id} updated successfully') current_app.logger.info(f'Specialist {specialist.id} updated successfully')
return redirect(prefixed_url_for('interaction_bp.specialists')) return redirect(prefixed_url_for('interaction_bp.specialists', for_redirect=True))
except SQLAlchemyError as e: except SQLAlchemyError as e:
db.session.rollback() db.session.rollback()
flash(f'Failed to update specialist. Error: {str(e)}', 'danger') flash(f'Failed to update specialist. Error: {str(e)}', 'danger')
@@ -297,17 +297,17 @@ def specialists():
def handle_specialist_selection(): def handle_specialist_selection():
action = request.form.get('action') action = request.form.get('action')
if action == 'create_specialist': if action == 'create_specialist':
return redirect(prefixed_url_for('interaction_bp.specialist')) return redirect(prefixed_url_for('interaction_bp.specialist', for_redirect=True))
specialist_identification = request.form.get('selected_row') specialist_identification = request.form.get('selected_row')
specialist_id = ast.literal_eval(specialist_identification).get('value') specialist_id = ast.literal_eval(specialist_identification).get('value')
if action == "edit_specialist": if action == "edit_specialist":
return redirect(prefixed_url_for('interaction_bp.edit_specialist', specialist_id=specialist_id)) return redirect(prefixed_url_for('interaction_bp.edit_specialist', specialist_id=specialist_id, for_redirect=True))
elif action == "execute_specialist": elif action == "execute_specialist":
return redirect(prefixed_url_for('interaction_bp.execute_specialist', specialist_id=specialist_id)) return redirect(prefixed_url_for('interaction_bp.execute_specialist', specialist_id=specialist_id, for_redirect=True))
return redirect(prefixed_url_for('interaction_bp.specialists')) return redirect(prefixed_url_for('interaction_bp.specialists', for_redirect=True))
# Routes for Agent management --------------------------------------------------------------------- # Routes for Agent management ---------------------------------------------------------------------
@@ -442,9 +442,9 @@ def handle_agent_selection():
action = request.form.get('action') action = request.form.get('action')
if action == "edit_agent": if action == "edit_agent":
return redirect(prefixed_url_for('interaction_bp.edit_agent', agent_id=agent_id)) return redirect(prefixed_url_for('interaction_bp.edit_agent', agent_id=agent_id, for_redirect=True))
return redirect(prefixed_url_for('interaction_bp.edit_specialist')) return redirect(prefixed_url_for('interaction_bp.edit_specialist', for_redirect=True))
@interaction_bp.route('/handle_task_selection', methods=['POST']) @interaction_bp.route('/handle_task_selection', methods=['POST'])
@@ -455,9 +455,9 @@ def handle_task_selection():
action = request.form.get('action') action = request.form.get('action')
if action == "edit_task": if action == "edit_task":
return redirect(prefixed_url_for('interaction_bp.edit_task', task_id=task_id)) return redirect(prefixed_url_for('interaction_bp.edit_task', task_id=task_id, for_redirect=True))
return redirect(prefixed_url_for('interaction_bp.edit_specialist')) return redirect(prefixed_url_for('interaction_bp.edit_specialist', for_redirect=True))
@interaction_bp.route('/handle_tool_selection', methods=['POST']) @interaction_bp.route('/handle_tool_selection', methods=['POST'])
@@ -468,9 +468,9 @@ def handle_tool_selection():
action = request.form.get('action') action = request.form.get('action')
if action == "edit_tool": if action == "edit_tool":
return redirect(prefixed_url_for('interaction_bp.edit_tool', tool_id=tool_id)) return redirect(prefixed_url_for('interaction_bp.edit_tool', tool_id=tool_id, for_redirect=True))
return redirect(prefixed_url_for('interaction_bp.edit_specialist')) return redirect(prefixed_url_for('interaction_bp.edit_specialist', for_redirect=True))
# Specialist Execution ---------------------------------------------------------------------------- # Specialist Execution ----------------------------------------------------------------------------
@@ -481,7 +481,7 @@ def execute_specialist(specialist_id):
if specialist_config.get('chat', True): if specialist_config.get('chat', True):
flash("Only specialists that don't require interactions can be executed this way!", 'error') flash("Only specialists that don't require interactions can be executed this way!", 'error')
return redirect(prefixed_url_for('interaction_bp.specialists')) return redirect(prefixed_url_for('interaction_bp.specialists', for_redirect=True))
form = ExecuteSpecialistForm(request.form, obj=specialist) form = ExecuteSpecialistForm(request.form, obj=specialist)
if 'arguments' in specialist_config: if 'arguments' in specialist_config:
@@ -498,7 +498,7 @@ def execute_specialist(specialist_id):
session_id=session_id, session_id=session_id,
user_timezone=session.get('tenant').get('timezone') user_timezone=session.get('tenant').get('timezone')
) )
return redirect(prefixed_url_for('interaction_bp.session_interactions_by_session_id', session_id=session_id)) return redirect(prefixed_url_for('interaction_bp.session_interactions_by_session_id', session_id=session_id, for_redirect=True))
return render_template('interaction/execute_specialist.html', form=form) return render_template('interaction/execute_specialist.html', form=form)
@@ -564,7 +564,7 @@ def session_interactions_by_session_id(session_id):
waiting='true')) waiting='true'))
# If we've already shown a waiting message and still don't have a session, go back to the specialists page # If we've already shown a waiting message and still don't have a session, go back to the specialists page
return redirect(prefixed_url_for('interaction_bp.specialists')) return redirect(prefixed_url_for('interaction_bp.specialists', for_redirect=True))
@interaction_bp.route('/session_interactions/<chat_session_id>', methods=['GET']) @interaction_bp.route('/session_interactions/<chat_session_id>', methods=['GET'])
@@ -619,7 +619,7 @@ def specialist_magic_link():
f'tenant {tenant_id}!') f'tenant {tenant_id}!')
return redirect(prefixed_url_for('interaction_bp.edit_specialist_magic_link', return redirect(prefixed_url_for('interaction_bp.edit_specialist_magic_link',
specialist_magic_link_id=new_specialist_magic_link.id)) specialist_magic_link_id=new_specialist_magic_link.id, for_redirect=True))
except Exception as e: except Exception as e:
db.session.rollback() db.session.rollback()
@@ -666,7 +666,7 @@ def edit_specialist_magic_link(specialist_magic_link_id):
db.session.commit() db.session.commit()
flash('Specialist Magic Link updated successfully!', 'success') flash('Specialist Magic Link updated successfully!', 'success')
current_app.logger.info(f'Specialist Magic Link {specialist_ml.id} updated successfully') current_app.logger.info(f'Specialist Magic Link {specialist_ml.id} updated successfully')
return redirect(prefixed_url_for('interaction_bp.specialist_magic_links')) return redirect(prefixed_url_for('interaction_bp.specialist_magic_links', for_redirect=True))
except SQLAlchemyError as e: except SQLAlchemyError as e:
db.session.rollback() db.session.rollback()
flash(f'Failed to update specialist Magic Link. Error: {str(e)}', 'danger') flash(f'Failed to update specialist Magic Link. Error: {str(e)}', 'danger')
@@ -745,19 +745,19 @@ def specialist_magic_links():
def handle_specialist_magic_link_selection(): def handle_specialist_magic_link_selection():
action = request.form.get('action') action = request.form.get('action')
if action == 'create_specialist_magic_link': if action == 'create_specialist_magic_link':
return redirect(prefixed_url_for('interaction_bp.specialist_magic_link')) return redirect(prefixed_url_for('interaction_bp.specialist_magic_link', for_redirect=True))
specialist_ml_identification = request.form.get('selected_row') specialist_ml_identification = request.form.get('selected_row')
specialist_ml_id = ast.literal_eval(specialist_ml_identification).get('value') specialist_ml_id = ast.literal_eval(specialist_ml_identification).get('value')
if action == "edit_specialist_magic_link": if action == "edit_specialist_magic_link":
return redirect(prefixed_url_for('interaction_bp.edit_specialist_magic_link', return redirect(prefixed_url_for('interaction_bp.edit_specialist_magic_link',
specialist_magic_link_id=specialist_ml_id)) specialist_magic_link_id=specialist_ml_id, for_redirect=True))
if action == "view_specialist_magic_link_urls": if action == "view_specialist_magic_link_urls":
return redirect(prefixed_url_for('interaction_bp.view_specialist_magic_link_urls', return redirect(prefixed_url_for('interaction_bp.view_specialist_magic_link_urls',
specialist_magic_link_id=specialist_ml_id)) specialist_magic_link_id=specialist_ml_id, for_redirect=True))
return redirect(prefixed_url_for('interaction_bp.specialists')) return redirect(prefixed_url_for('interaction_bp.specialists', for_redirect=True))
# Routes for Asset Management --------------------------------------------------------------------- # Routes for Asset Management ---------------------------------------------------------------------
@@ -788,13 +788,13 @@ def handle_data_capsule_selection():
match action: match action:
case 'view_data_capsule': case 'view_data_capsule':
# For now, we'll just redirect to view_data_capsule # For now, we'll just redirect to view_data_capsule
return redirect(prefixed_url_for('interaction_bp.view_data_capsule', data_capsule_id=capsule_id)) return redirect(prefixed_url_for('interaction_bp.view_data_capsule', data_capsule_id=capsule_id, for_redirect=True))
case 'view_chat_session': case 'view_chat_session':
# Redirect to the chat session # Redirect to the chat session
return redirect(prefixed_url_for('interaction_bp.view_chat_session', chat_session_id=data_capsule.chat_session_id)) return redirect(prefixed_url_for('interaction_bp.view_chat_session', chat_session_id=data_capsule.chat_session_id, for_redirect=True))
# Default redirect back to the data capsules list # Default redirect back to the data capsules list
return redirect(prefixed_url_for('interaction_bp.eveai_data_capsules')) return redirect(prefixed_url_for('interaction_bp.eveai_data_capsules', for_redirect=True))
@interaction_bp.route('/handle_asset_selection', methods=['POST']) @interaction_bp.route('/handle_asset_selection', methods=['POST'])
@@ -804,9 +804,9 @@ def handle_asset_selection():
asset_id = ast.literal_eval(asset_identification).get('value') asset_id = ast.literal_eval(asset_identification).get('value')
if action == 'edit_asset': if action == 'edit_asset':
return redirect(prefixed_url_for('interaction_bp.edit_asset', asset_id=asset_id)) return redirect(prefixed_url_for('interaction_bp.edit_asset', asset_id=asset_id, for_redirect=True))
return redirect(prefixed_url_for('interaction_bp.assets')) return redirect(prefixed_url_for('interaction_bp.assets', for_redirect=True))
@interaction_bp.route('/view_data_capsule/<int:data_capsule_id>', methods=['GET']) @interaction_bp.route('/view_data_capsule/<int:data_capsule_id>', methods=['GET'])
@@ -830,14 +830,14 @@ def edit_asset(asset_id):
if not tenant_id: if not tenant_id:
flash('Geen tenant geselecteerd', 'error') flash('Geen tenant geselecteerd', 'error')
return redirect(url_for('interaction_bp.assets')) return redirect(url_for('interaction_bp.assets', for_redirect=True))
# Controleer of het bestandstype wordt ondersteund # Controleer of het bestandstype wordt ondersteund
if asset.file_type != 'json': if asset.file_type != 'json':
flash( flash(
f'Bestandstype "{asset.file_type}" wordt momenteel niet ondersteund voor bewerking. Alleen JSON-bestanden kunnen worden bewerkt.', f'Bestandstype "{asset.file_type}" wordt momenteel niet ondersteund voor bewerking. Alleen JSON-bestanden kunnen worden bewerkt.',
'warning') 'warning')
return redirect(url_for('interaction_bp.assets')) return redirect(url_for('interaction_bp.assets', for_redirect=True))
if request.method == 'GET': if request.method == 'GET':
try: try:
@@ -861,11 +861,11 @@ def edit_asset(asset_id):
except json.JSONDecodeError: except json.JSONDecodeError:
flash('Fout bij het laden van het JSON-bestand: ongeldig JSON-formaat', 'error') flash('Fout bij het laden van het JSON-bestand: ongeldig JSON-formaat', 'error')
return redirect(prefixed_url_for('interaction_bp.assets')) return redirect(prefixed_url_for('interaction_bp.assets', for_redirect=True))
except Exception as e: except Exception as e:
current_app.logger.error(f"Error loading asset {asset_id}: {str(e)}") current_app.logger.error(f"Error loading asset {asset_id}: {str(e)}")
flash(f'Fout bij het laden van het asset: {str(e)}', 'error') flash(f'Fout bij het laden van het asset: {str(e)}', 'error')
return redirect(prefixed_url_for('interaction_bp.assets')) return redirect(prefixed_url_for('interaction_bp.assets', for_redirect=True))
elif request.method == 'POST': elif request.method == 'POST':
try: try:
@@ -877,14 +877,14 @@ def edit_asset(asset_id):
if not json_data: if not json_data:
current_app.logger.error(f"No JSON data received for asset {asset_id}") current_app.logger.error(f"No JSON data received for asset {asset_id}")
flash('Geen JSON data ontvangen', 'error') flash('Geen JSON data ontvangen', 'error')
return redirect(prefixed_url_for('interaction_bp.edit_asset', asset_id=asset_id)) return redirect(prefixed_url_for('interaction_bp.edit_asset', asset_id=asset_id, for_redirect=True))
# Valideer JSON formaat # Valideer JSON formaat
try: try:
parsed_json = json.loads(json_data) parsed_json = json.loads(json_data)
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
flash(f'Ongeldig JSON-formaat: {str(e)}', 'error') flash(f'Ongeldig JSON-formaat: {str(e)}', 'error')
return redirect(prefixed_url_for('interaction_bp.edit_asset', asset_id=asset_id)) return redirect(prefixed_url_for('interaction_bp.edit_asset', asset_id=asset_id, for_redirect=True))
# Upload de bijgewerkte JSON naar MinIO # Upload de bijgewerkte JSON naar MinIO
bucket_name, object_name, file_size = minio_client.upload_asset_file( bucket_name, object_name, file_size = minio_client.upload_asset_file(
@@ -904,10 +904,10 @@ def edit_asset(asset_id):
db.session.commit() db.session.commit()
flash('Asset succesvol bijgewerkt', 'success') flash('Asset succesvol bijgewerkt', 'success')
return redirect(prefixed_url_for('interaction_bp.assets')) return redirect(prefixed_url_for('interaction_bp.assets', for_redirect=True))
except Exception as e: except Exception as e:
db.session.rollback() db.session.rollback()
current_app.logger.error(f"Error saving asset {asset_id}: {str(e)}") current_app.logger.error(f"Error saving asset {asset_id}: {str(e)}")
flash(f'Fout bij het opslaan van het asset: {str(e)}', 'error') flash(f'Fout bij het opslaan van het asset: {str(e)}', 'error')
return redirect(prefixed_url_for('interaction_bp.edit_asset', asset_id=asset_id)) return redirect(prefixed_url_for('interaction_bp.edit_asset', asset_id=asset_id, for_redirect=True))

View File

@@ -44,7 +44,7 @@ def handle_trigger_action():
current_app.logger.error(f"Failed to trigger usage update task: {str(e)}") current_app.logger.error(f"Failed to trigger usage update task: {str(e)}")
flash(f'Failed to trigger usage update: {str(e)}', 'danger') flash(f'Failed to trigger usage update: {str(e)}', 'danger')
return redirect(prefixed_url_for('partner_bp.trigger_actions')) return redirect(prefixed_url_for('partner_bp.trigger_actions', for_redirect=True))
# Partner Management ------------------------------------------------------------------------------ # Partner Management ------------------------------------------------------------------------------
@@ -67,7 +67,7 @@ def edit_partner(partner_id):
refresh_session_partner(partner.id) refresh_session_partner(partner.id)
return redirect( return redirect(
prefixed_url_for('partner_bp.edit_partner', prefixed_url_for('partner_bp.edit_partner',
partner_id=partner.id)) # Assuming there's a user profile view to redirect to partner_id=partner.id, for_redirect=True)) # Assuming there's a user profile view to redirect to
else: else:
form_validation_failed(request, form) form_validation_failed(request, form)
@@ -88,11 +88,11 @@ def handle_partner_selection():
if action == 'create_partner': if action == 'create_partner':
try: try:
partner_id = register_partner_from_tenant(session['tenant']['id']) partner_id = register_partner_from_tenant(session['tenant']['id'])
return redirect(prefixed_url_for('partner_bp.edit_partner', partner_id=partner_id, )) return redirect(prefixed_url_for('partner_bp.edit_partner', partner_id=partner_id, for_redirect=True ))
except EveAIException as e: except EveAIException as e:
current_app.logger.error(f'Error registering partner for tenant {session['tenant']['id']}: {str(e)}') current_app.logger.error(f'Error registering partner for tenant {session['tenant']['id']}: {str(e)}')
flash('Error Registering Partner for Selected Tenant', 'danger') flash('Error Registering Partner for Selected Tenant', 'danger')
return redirect(prefixed_url_for('partner_bp.partners')) return redirect(prefixed_url_for('partner_bp.partners', for_redirect=True))
partner_identification = request.form.get('selected_row') partner_identification = request.form.get('selected_row')
partner_id = ast.literal_eval(partner_identification).get('value') partner_id = ast.literal_eval(partner_identification).get('value')
partner = Partner.query.get_or_404(partner_id) partner = Partner.query.get_or_404(partner_id)
@@ -101,9 +101,9 @@ def handle_partner_selection():
current_app.logger.info(f"Setting session partner: {partner.id}") current_app.logger.info(f"Setting session partner: {partner.id}")
session['partner'] = partner.to_dict() session['partner'] = partner.to_dict()
elif action == 'edit_partner': elif action == 'edit_partner':
return redirect(prefixed_url_for('partner_bp.edit_partner', partner_id=partner_id)) return redirect(prefixed_url_for('partner_bp.edit_partner', partner_id=partner_id, for_redirect=True))
return redirect(prefixed_url_for('partner_bp.partners')) return redirect(prefixed_url_for('partner_bp.partners', for_redirect=True))
# Partner Servide Management ---------------------------------------------------------------------- # Partner Servide Management ----------------------------------------------------------------------
@@ -116,7 +116,7 @@ def partner_service():
partner = session.get('partner', None) partner = session.get('partner', None)
if not partner: if not partner:
flash('No partner has been selected. Set partner before adding services.', 'warning') flash('No partner has been selected. Set partner before adding services.', 'warning')
return redirect(prefixed_url_for('partner_bp.partners')) return redirect(prefixed_url_for('partner_bp.partners', for_redirect=True))
partner_id = partner['id'] partner_id = partner['id']
new_partner_service = PartnerService() new_partner_service = PartnerService()
form.populate_obj(new_partner_service) form.populate_obj(new_partner_service)
@@ -130,7 +130,7 @@ def partner_service():
current_app.logger.info(f"Partner Service {new_partner_service.name} added successfully for {partner_id}") current_app.logger.info(f"Partner Service {new_partner_service.name} added successfully for {partner_id}")
# Step 2 of the creation process (depending on type) # Step 2 of the creation process (depending on type)
return redirect(prefixed_url_for('partner_bp.partner_service', return redirect(prefixed_url_for('partner_bp.partner_service',
partner_service_id=new_partner_service.id)) partner_service_id=new_partner_service.id, for_redirect=True))
except SQLAlchemyError as e: except SQLAlchemyError as e:
db.session.rollback() db.session.rollback()
flash(f'Failed to add Partner Service: {str(e)}', 'danger') flash(f'Failed to add Partner Service: {str(e)}', 'danger')
@@ -172,7 +172,7 @@ def edit_partner_service(partner_service_id):
return render_template('partner/edit_partner_service.html', form=form, return render_template('partner/edit_partner_service.html', form=form,
partner_service_id=partner_service_id) partner_service_id=partner_service_id)
return redirect(prefixed_url_for('partner_bp.partner_services')) return redirect(prefixed_url_for('partner_bp.partner_services', for_redirect=True))
else: else:
form_validation_failed(request, form) form_validation_failed(request, form)
@@ -186,7 +186,7 @@ def partner_services():
partner = session.get('partner', None) partner = session.get('partner', None)
if not partner: if not partner:
flash('No partner has been selected. Set partner before adding services.', 'warning') flash('No partner has been selected. Set partner before adding services.', 'warning')
return redirect(prefixed_url_for('partner_bp.partners')) return redirect(prefixed_url_for('partner_bp.partners', for_redirect=True))
partner_id = session['partner']['id'] partner_id = session['partner']['id']
config = get_partner_services_list_view(partner_id) config = get_partner_services_list_view(partner_id)
@@ -198,20 +198,20 @@ def partner_services():
def handle_partner_service_selection(): def handle_partner_service_selection():
action = request.form['action'] action = request.form['action']
if action == 'create_partner_service': if action == 'create_partner_service':
return redirect(prefixed_url_for('partner_bp.partner_service')) return redirect(prefixed_url_for('partner_bp.partner_service', for_redirect=True))
partner_service_identification = request.form.get('selected_row') partner_service_identification = request.form.get('selected_row')
partner_service_id = ast.literal_eval(partner_service_identification).get('value') partner_service_id = ast.literal_eval(partner_service_identification).get('value')
if action == 'edit_partner_service': if action == 'edit_partner_service':
return redirect(prefixed_url_for('partner_bp.edit_partner_service', return redirect(prefixed_url_for('partner_bp.edit_partner_service',
partner_service_id=partner_service_id)) partner_service_id=partner_service_id, for_redirect=True))
elif action == 'add_partner_service_for_tenant': elif action == 'add_partner_service_for_tenant':
add_partner_service_for_tenant(partner_service_id) add_partner_service_for_tenant(partner_service_id)
return redirect(prefixed_url_for('partner_bp.edit_partner_service', return redirect(prefixed_url_for('partner_bp.edit_partner_service',
partner_service_id=partner_service_id)) partner_service_id=partner_service_id, for_redirect=True))
return redirect(prefixed_url_for('partner_bp.partner_services')) return redirect(prefixed_url_for('partner_bp.partner_services', for_redirect=True))
# Utility Functions # Utility Functions
@@ -248,7 +248,7 @@ def add_partner_service_for_tenant(partner_service_id):
tenant = session.get('tenant', None) tenant = session.get('tenant', None)
if not tenant: if not tenant:
flash('No tenant has been selected. Set tenant before adding services.', 'warning') flash('No tenant has been selected. Set tenant before adding services.', 'warning')
return redirect(prefixed_url_for('user_bp.tenants')) return redirect(prefixed_url_for('user_bp.tenants', for_redirect=True))
tenant_id = tenant['id'] tenant_id = tenant['id']
@@ -257,7 +257,7 @@ def add_partner_service_for_tenant(partner_service_id):
partner_service = PartnerService.query.get(partner_service_id) partner_service = PartnerService.query.get(partner_service_id)
if not partner_service: if not partner_service:
flash(f'Partner service with ID {partner_service_id} not found.', 'danger') flash(f'Partner service with ID {partner_service_id} not found.', 'danger')
return redirect(prefixed_url_for('partner_bp.partner_services')) return redirect(prefixed_url_for('partner_bp.partner_services', for_redirect=True))
# Check if the association already exists # Check if the association already exists
existing = PartnerTenant.query.filter_by( existing = PartnerTenant.query.filter_by(
@@ -267,7 +267,7 @@ def add_partner_service_for_tenant(partner_service_id):
if existing: if existing:
flash(f'This tenant already has access to this partner service.', 'warning') flash(f'This tenant already has access to this partner service.', 'warning')
return redirect(prefixed_url_for('partner_bp.partner_services')) return redirect(prefixed_url_for('partner_bp.partner_services', for_redirect=True))
# Create new association # Create new association
new_partner_tenant = PartnerTenant( new_partner_tenant = PartnerTenant(
@@ -289,13 +289,13 @@ def add_partner_service_for_tenant(partner_service_id):
partner_name = partner_tenant.name if partner_tenant else 'Unknown Partner' partner_name = partner_tenant.name if partner_tenant else 'Unknown Partner'
flash(f'Successfully added {partner_service.type} service from {partner_name} to this tenant.', 'success') flash(f'Successfully added {partner_service.type} service from {partner_name} to this tenant.', 'success')
return redirect(prefixed_url_for('partner_bp.partner_services')) return redirect(prefixed_url_for('partner_bp.partner_services', for_redirect=True))
except SQLAlchemyError as e: except SQLAlchemyError as e:
db.session.rollback() db.session.rollback()
current_app.logger.error(f"Database error adding partner service: {str(e)}") current_app.logger.error(f"Database error adding partner service: {str(e)}")
flash(f'Error adding partner service: {str(e)}', 'danger') flash(f'Error adding partner service: {str(e)}', 'danger')
return redirect(prefixed_url_for('partner_bp.partner_services')) return redirect(prefixed_url_for('partner_bp.partner_services', for_redirect=True))
def refresh_session_partner(partner_id): def refresh_session_partner(partner_id):

View File

@@ -34,7 +34,7 @@ def log_after_request(response):
@security_bp.route('/login', methods=['GET', 'POST']) @security_bp.route('/login', methods=['GET', 'POST'])
def login(): def login():
if current_user.is_authenticated: if current_user.is_authenticated:
return redirect(prefixed_url_for('basic_bp.index')) return redirect(prefixed_url_for('basic_bp.index', for_redirect=True))
form = LoginForm() form = LoginForm()
@@ -55,9 +55,9 @@ def login():
current_app.logger.info(f'Login successful! Current User is {current_user.email}') current_app.logger.info(f'Login successful! Current User is {current_user.email}')
db.session.commit() db.session.commit()
if current_user.has_roles('Super User'): if current_user.has_roles('Super User'):
return redirect(prefixed_url_for('user_bp.tenants')) return redirect(prefixed_url_for('user_bp.tenants', for_redirect=True))
else: else:
return redirect(prefixed_url_for('user_bp.tenant_overview')) return redirect(prefixed_url_for('user_bp.tenant_overview', for_redirect=True))
else: else:
flash('Invalid username or password', 'danger') flash('Invalid username or password', 'danger')
current_app.logger.error(f'Invalid username or password for given email: {user.email}') current_app.logger.error(f'Invalid username or password for given email: {user.email}')
@@ -68,7 +68,7 @@ def login():
except CSRFError: except CSRFError:
current_app.logger.warning('CSRF token mismatch during login attempt') current_app.logger.warning('CSRF token mismatch during login attempt')
flash('Your session has expired. Please try logging in again.', 'danger') flash('Your session has expired. Please try logging in again.', 'danger')
return redirect(prefixed_url_for('security_bp.login')) return redirect(prefixed_url_for('security_bp.login', for_redirect=True))
if request.method == 'GET': if request.method == 'GET':
csrf_token = generate_csrf() csrf_token = generate_csrf()
@@ -80,7 +80,7 @@ def login():
@login_required @login_required
def logout(): def logout():
logout_user() logout_user()
return redirect(prefixed_url_for('basic_bp.index')) return redirect(prefixed_url_for('basic_bp.index', for_redirect=True))
@security_bp.route('/confirm_email/<token>', methods=['GET', 'POST']) @security_bp.route('/confirm_email/<token>', methods=['GET', 'POST'])
@@ -89,12 +89,12 @@ def confirm_email(token):
email = confirm_token(token) email = confirm_token(token)
except Exception as e: except Exception as e:
flash('The confirmation link is invalid or has expired.', 'danger') flash('The confirmation link is invalid or has expired.', 'danger')
return redirect(prefixed_url_for('basic_bp.confirm_email_fail')) return redirect(prefixed_url_for('basic_bp.confirm_email_fail', for_redirect=True))
user = User.query.filter_by(email=email).first_or_404() user = User.query.filter_by(email=email).first_or_404()
if user.active: if user.active:
flash('Account already confirmed. Please login.', 'success') flash('Account already confirmed. Please login.', 'success')
return redirect(prefixed_url_for('security_bp.login')) return redirect(prefixed_url_for('security_bp.login', for_redirect=True))
else: else:
user.active = True user.active = True
user.updated_at = dt.now(tz.utc) user.updated_at = dt.now(tz.utc)
@@ -105,10 +105,10 @@ def confirm_email(token):
db.session.commit() db.session.commit()
except SQLAlchemyError as e: except SQLAlchemyError as e:
db.session.rollback() db.session.rollback()
return redirect(prefixed_url_for('basic_bp.confirm_email_fail')) return redirect(prefixed_url_for('basic_bp.confirm_email_fail', for_redirect=True))
send_reset_email(user) send_reset_email(user)
return redirect(prefixed_url_for('basic_bp.confirm_email_ok')) return redirect(prefixed_url_for('basic_bp.confirm_email_ok', for_redirect=True))
@security_bp.route('/forgot_password', methods=['GET', 'POST']) @security_bp.route('/forgot_password', methods=['GET', 'POST'])
@@ -119,7 +119,7 @@ def forgot_password():
if user: if user:
send_reset_email(user) send_reset_email(user)
flash('An email with instructions to reset your password has been sent.', 'info') flash('An email with instructions to reset your password has been sent.', 'info')
return redirect(prefixed_url_for('security_bp.login')) return redirect(prefixed_url_for('security_bp.login', for_redirect=True))
return render_template('security/forgot_password.html', form=form) return render_template('security/forgot_password.html', form=form)
@@ -130,7 +130,7 @@ def reset_password(token):
except Exception as e: except Exception as e:
flash('The reset link is invalid or has expired.', 'danger') flash('The reset link is invalid or has expired.', 'danger')
current_app.logger.error(f'Invalid reset link detected: {token} - error: {e}') current_app.logger.error(f'Invalid reset link detected: {token} - error: {e}')
return redirect(prefixed_url_for('security_bp.reset_password_request')) return redirect(prefixed_url_for('security_bp.reset_password_request', for_redirect=True))
user = User.query.filter_by(email=email).first_or_404() user = User.query.filter_by(email=email).first_or_404()
form = ResetPasswordForm() form = ResetPasswordForm()
@@ -139,7 +139,7 @@ def reset_password(token):
user.updated_at = dt.now(tz.utc) user.updated_at = dt.now(tz.utc)
db.session.commit() db.session.commit()
flash('Your password has been updated.', 'success') flash('Your password has been updated.', 'success')
return redirect(prefixed_url_for('security_bp.login')) return redirect(prefixed_url_for('security_bp.login', for_redirect=True))
return render_template('security/reset_password.html', reset_password_form=form) return render_template('security/reset_password.html', reset_password_form=form)

View File

@@ -48,7 +48,7 @@ def tenant():
if not UserServices.can_user_create_tenant(): if not UserServices.can_user_create_tenant():
current_app.logger.error(f'User {current_user.email} cannot create tenant') current_app.logger.error(f'User {current_user.email} cannot create tenant')
flash(f"You don't have the appropriate permissions to create a tenant", 'danger') flash(f"You don't have the appropriate permissions to create a tenant", 'danger')
return redirect(prefixed_url_for('user_bp.tenants')) return redirect(prefixed_url_for('user_bp.tenants', for_redirect=True))
form = TenantForm() form = TenantForm()
if request.method == 'GET': if request.method == 'GET':
code = f"TENANT-{str(uuid.uuid4())}" code = f"TENANT-{str(uuid.uuid4())}"
@@ -107,7 +107,7 @@ def tenant():
current_app.logger.info(f"Creating MinIO bucket for tenant {new_tenant.id}") current_app.logger.info(f"Creating MinIO bucket for tenant {new_tenant.id}")
minio_client.create_tenant_bucket(new_tenant.id) minio_client.create_tenant_bucket(new_tenant.id)
return redirect(prefixed_url_for('user_bp.tenants')) return redirect(prefixed_url_for('user_bp.tenants', for_redirect=True))
else: else:
form_validation_failed(request, form) form_validation_failed(request, form)
@@ -155,14 +155,14 @@ def tenants():
def handle_tenant_selection(): def handle_tenant_selection():
action = request.form['action'] action = request.form['action']
if action == 'create_tenant': if action == 'create_tenant':
return redirect(prefixed_url_for('user_bp.tenant')) return redirect(prefixed_url_for('user_bp.tenant', for_redirect=True))
tenant_identification = request.form['selected_row'] tenant_identification = request.form['selected_row']
tenant_id = ast.literal_eval(tenant_identification).get('value') tenant_id = ast.literal_eval(tenant_identification).get('value')
if not UserServices.can_user_edit_tenant(tenant_id): if not UserServices.can_user_edit_tenant(tenant_id):
current_app.logger.info(f"User not authenticated to edit tenant {tenant_id}.") current_app.logger.info(f"User not authenticated to edit tenant {tenant_id}.")
flash(f"You are not authenticated to manage tenant {tenant_id}", 'danger') flash(f"You are not authenticated to manage tenant {tenant_id}", 'danger')
return redirect(prefixed_url_for('user_bp.tenants')) return redirect(prefixed_url_for('user_bp.tenants', for_redirect=True))
the_tenant = Tenant.query.get(tenant_id) the_tenant = Tenant.query.get(tenant_id)
# set tenant information in the session # set tenant information in the session
@@ -173,12 +173,12 @@ def handle_tenant_selection():
match action: match action:
case 'edit_tenant': case 'edit_tenant':
return redirect(prefixed_url_for('user_bp.edit_tenant', tenant_id=tenant_id)) return redirect(prefixed_url_for('user_bp.edit_tenant', tenant_id=tenant_id, for_redirect=True))
case 'select_tenant': case 'select_tenant':
return redirect(prefixed_url_for('user_bp.tenant_overview')) return redirect(prefixed_url_for('user_bp.tenant_overview', for_redirect=True))
# Add more conditions for other actions # Add more conditions for other actions
return redirect(prefixed_url_for('tenants')) return redirect(prefixed_url_for('tenants', for_redirect=True))
@user_bp.route('/tenant_overview', methods=['GET']) @user_bp.route('/tenant_overview', methods=['GET'])
@@ -244,7 +244,7 @@ def user():
flash('User added successfully, but failed to send confirmation email. ' flash('User added successfully, but failed to send confirmation email. '
'Please contact the administrator.', 'warning') 'Please contact the administrator.', 'warning')
return redirect(prefixed_url_for('user_bp.view_users')) return redirect(prefixed_url_for('user_bp.view_users', for_redirect=True))
except Exception as e: except Exception as e:
current_app.logger.error(f'Failed to add user with name {new_user.user_name}. Error: {str(e)}') current_app.logger.error(f'Failed to add user with name {new_user.user_name}. Error: {str(e)}')
db.session.rollback() db.session.rollback()
@@ -286,13 +286,13 @@ def edit_user(user_id):
flash('Trying to assign unauthorized roles', 'danger') flash('Trying to assign unauthorized roles', 'danger')
current_app.logger.error(f"Trying to assign unauthorized roles by user {user_id}," current_app.logger.error(f"Trying to assign unauthorized roles by user {user_id},"
f"tenant {session['tenant']['id']}") f"tenant {session['tenant']['id']}")
return redirect(prefixed_url_for('user_bp.edit_user', user_id=user_id)) return redirect(prefixed_url_for('user_bp.edit_user', user_id=user_id, for_redirect=True))
db.session.commit() db.session.commit()
flash('User updated successfully.', 'success') flash('User updated successfully.', 'success')
return redirect( return redirect(
prefixed_url_for('user_bp.edit_user', prefixed_url_for('user_bp.edit_user',
user_id=user.id)) # Assuming there's a user profile view to redirect to user_id=user.id, for_redirect=True)) # Assuming there's a user profile view to redirect to
else: else:
form_validation_failed(request, form) form_validation_failed(request, form)
@@ -314,14 +314,14 @@ def view_users():
def handle_user_action(): def handle_user_action():
action = request.form['action'] action = request.form['action']
if action == 'create_user': if action == 'create_user':
return redirect(prefixed_url_for('user_bp.user')) return redirect(prefixed_url_for('user_bp.user', for_redirect=True))
user_identification = request.form['selected_row'] user_identification = request.form['selected_row']
user_id = ast.literal_eval(user_identification).get('value') user_id = ast.literal_eval(user_identification).get('value')
user = User.query.get_or_404(user_id) user = User.query.get_or_404(user_id)
if action == 'edit_user': if action == 'edit_user':
return redirect(prefixed_url_for('user_bp.edit_user', user_id=user_id)) return redirect(prefixed_url_for('user_bp.edit_user', user_id=user_id, for_redirect=True))
elif action == 'resend_confirmation_email': elif action == 'resend_confirmation_email':
send_confirmation_email(user) send_confirmation_email(user)
flash(f'Confirmation email sent to {user.email}.', 'success') flash(f'Confirmation email sent to {user.email}.', 'success')
@@ -332,7 +332,7 @@ def handle_user_action():
reset_uniquifier(user) reset_uniquifier(user)
flash(f'Uniquifier reset for {user.user_name}.', 'success') flash(f'Uniquifier reset for {user.user_name}.', 'success')
return redirect(prefixed_url_for('user_bp.view_users')) return redirect(prefixed_url_for('user_bp.view_users', for_redirect=True))
# Tenant Domain Management (Probably obsolete )------------------------------------------------------------------------ # Tenant Domain Management (Probably obsolete )------------------------------------------------------------------------
@@ -349,15 +349,15 @@ def tenant_domains():
def handle_tenant_domain_action(): def handle_tenant_domain_action():
action = request.form['action'] action = request.form['action']
if action == 'create_tenant_domain': if action == 'create_tenant_domain':
return redirect(prefixed_url_for('user_bp.tenant_domain')) return redirect(prefixed_url_for('user_bp.tenant_domain', for_redirect=True))
tenant_domain_identification = request.form['selected_row'] tenant_domain_identification = request.form['selected_row']
tenant_domain_id = ast.literal_eval(tenant_domain_identification).get('value') tenant_domain_id = ast.literal_eval(tenant_domain_identification).get('value')
if action == 'edit_tenant_domain': if action == 'edit_tenant_domain':
return redirect(prefixed_url_for('user_bp.edit_tenant_domain', tenant_domain_id=tenant_domain_id)) return redirect(prefixed_url_for('user_bp.edit_tenant_domain', tenant_domain_id=tenant_domain_id, for_redirect=True))
# Add more conditions for other actions # Add more conditions for other actions
return redirect(prefixed_url_for('tenant_domains')) return redirect(prefixed_url_for('tenant_domains', for_redirect=True))
@user_bp.route('/tenant_domain', methods=['GET', 'POST']) @user_bp.route('/tenant_domain', methods=['GET', 'POST'])
@@ -410,7 +410,7 @@ def edit_tenant_domain(tenant_domain_id):
f'Error: {str(e)}') f'Error: {str(e)}')
return redirect( return redirect(
prefixed_url_for('user_bp.tenant_domains', prefixed_url_for('user_bp.tenant_domains',
tenant_id=session['tenant']['id'])) # Assuming there's a user profile view to redirect to tenant_id=session['tenant']['id'], for_redirect=True)) # Assuming there's a user profile view to redirect to
else: else:
form_validation_failed(request, form) form_validation_failed(request, form)
@@ -461,7 +461,7 @@ def tenant_project():
current_app.logger.info(f'Tenant Project {new_tenant_project.name} added for tenant ' current_app.logger.info(f'Tenant Project {new_tenant_project.name} added for tenant '
f'{session['tenant']['id']}.') f'{session['tenant']['id']}.')
return redirect(prefixed_url_for('user_bp.tenant_projects')) return redirect(prefixed_url_for('user_bp.tenant_projects', for_redirect=True))
except SQLAlchemyError as e: except SQLAlchemyError as e:
db.session.rollback() db.session.rollback()
flash(f'Failed to create Tenant Project. Error: {str(e)}', 'danger') flash(f'Failed to create Tenant Project. Error: {str(e)}', 'danger')
@@ -484,13 +484,13 @@ def tenant_projects():
def handle_tenant_project_selection(): def handle_tenant_project_selection():
action = request.form.get('action') action = request.form.get('action')
if action == 'create_tenant_project': if action == 'create_tenant_project':
return redirect(prefixed_url_for('user_bp.tenant_project')) return redirect(prefixed_url_for('user_bp.tenant_project', for_redirect=True))
tenant_project_identification = request.form.get('selected_row') tenant_project_identification = request.form.get('selected_row')
tenant_project_id = ast.literal_eval(tenant_project_identification).get('value') tenant_project_id = ast.literal_eval(tenant_project_identification).get('value')
tenant_project = TenantProject.query.get_or_404(tenant_project_id) tenant_project = TenantProject.query.get_or_404(tenant_project_id)
if action == 'edit_tenant_project': if action == 'edit_tenant_project':
return redirect(prefixed_url_for('user_bp.edit_tenant_project', tenant_project_id=tenant_project_id)) return redirect(prefixed_url_for('user_bp.edit_tenant_project', tenant_project_id=tenant_project_id, for_redirect=True))
elif action == 'invalidate_tenant_project': elif action == 'invalidate_tenant_project':
tenant_project.active = False tenant_project.active = False
try: try:
@@ -505,9 +505,9 @@ def handle_tenant_project_selection():
current_app.logger.error(f"Failed to invalidate Tenant Project for tenant {session['tenant']['id']}. " current_app.logger.error(f"Failed to invalidate Tenant Project for tenant {session['tenant']['id']}. "
f"Error: {str(e)}") f"Error: {str(e)}")
elif action == 'delete_tenant_project': elif action == 'delete_tenant_project':
return redirect(prefixed_url_for('user_bp.delete_tenant_project', tenant_project_id=tenant_project_id)) return redirect(prefixed_url_for('user_bp.delete_tenant_project', tenant_project_id=tenant_project_id, for_redirect=True))
return redirect(prefixed_url_for('user_bp.tenant_projects')) return redirect(prefixed_url_for('user_bp.tenant_projects', for_redirect=True))
@user_bp.route('/tenant_project/<int:tenant_project_id>', methods=['GET', 'POST']) @user_bp.route('/tenant_project/<int:tenant_project_id>', methods=['GET', 'POST'])
@@ -527,7 +527,7 @@ def edit_tenant_project(tenant_project_id):
db.session.commit() db.session.commit()
flash('Tenant Project updated successfully.', 'success') flash('Tenant Project updated successfully.', 'success')
current_app.logger.info(f'Tenant Project {tenant_project.name} updated for tenant {tenant_id}.') current_app.logger.info(f'Tenant Project {tenant_project.name} updated for tenant {tenant_id}.')
return redirect(prefixed_url_for('user_bp.tenant_projects')) return redirect(prefixed_url_for('user_bp.tenant_projects', for_redirect=True))
except SQLAlchemyError as e: except SQLAlchemyError as e:
db.session.rollback() db.session.rollback()
flash(f'Failed to update Tenant Project. Error: {str(e)}', 'danger') flash(f'Failed to update Tenant Project. Error: {str(e)}', 'danger')
@@ -545,7 +545,7 @@ def delete_tenant_project(tenant_project_id):
# Ensure project belongs to current tenant # Ensure project belongs to current tenant
if tenant_project.tenant_id != tenant_id: if tenant_project.tenant_id != tenant_id:
flash('You do not have permission to delete this project.', 'danger') flash('You do not have permission to delete this project.', 'danger')
return redirect(prefixed_url_for('user_bp.tenant_projects')) return redirect(prefixed_url_for('user_bp.tenant_projects', for_redirect=True))
if request.method == 'GET': if request.method == 'GET':
return render_template('user/confirm_delete_tenant_project.html', return render_template('user/confirm_delete_tenant_project.html',
@@ -562,7 +562,7 @@ def delete_tenant_project(tenant_project_id):
flash(f'Failed to delete Tenant Project. Error: {str(e)}', 'danger') flash(f'Failed to delete Tenant Project. Error: {str(e)}', 'danger')
current_app.logger.error(f'Failed to delete Tenant Project {tenant_project_id}. Error: {str(e)}') current_app.logger.error(f'Failed to delete Tenant Project {tenant_project_id}. Error: {str(e)}')
return redirect(prefixed_url_for('user_bp.tenant_projects')) return redirect(prefixed_url_for('user_bp.tenant_projects', for_redirect=True))
# Tenant Make Management -------------------------------------------------------------------------- # Tenant Make Management --------------------------------------------------------------------------
@@ -589,7 +589,7 @@ def tenant_make():
current_app.logger.info(f'Tenant Make {new_tenant_make.name}, id {new_tenant_make.id} successfully added ' current_app.logger.info(f'Tenant Make {new_tenant_make.name}, id {new_tenant_make.id} successfully added '
f'for tenant {tenant_id}!') f'for tenant {tenant_id}!')
# Enable step 2 of creation of retriever - add configuration of the retriever (dependent on type) # Enable step 2 of creation of retriever - add configuration of the retriever (dependent on type)
return redirect(prefixed_url_for('user_bp.edit_tenant_make', tenant_make_id=new_tenant_make.id)) return redirect(prefixed_url_for('user_bp.edit_tenant_make', tenant_make_id=new_tenant_make.id, for_redirect=True))
except SQLAlchemyError as e: except SQLAlchemyError as e:
db.session.rollback() db.session.rollback()
flash(f'Failed to add Tenant Make. Error: {e}', 'danger') flash(f'Failed to add Tenant Make. Error: {e}', 'danger')
@@ -647,7 +647,7 @@ def edit_tenant_make(tenant_make_id):
current_app.logger.error(f'Failed to update tenant make {tenant_make_id}. Error: {str(e)}') current_app.logger.error(f'Failed to update tenant make {tenant_make_id}. Error: {str(e)}')
return render_template('user/edit_tenant_make.html', form=form, tenant_make_id=tenant_make_id) return render_template('user/edit_tenant_make.html', form=form, tenant_make_id=tenant_make_id)
return redirect(prefixed_url_for('user_bp.tenant_makes')) return redirect(prefixed_url_for('user_bp.tenant_makes', for_redirect=True))
else: else:
form_validation_failed(request, form) form_validation_failed(request, form)
@@ -659,12 +659,12 @@ def edit_tenant_make(tenant_make_id):
def handle_tenant_make_selection(): def handle_tenant_make_selection():
action = request.form['action'] action = request.form['action']
if action == 'create_tenant_make': if action == 'create_tenant_make':
return redirect(prefixed_url_for('user_bp.tenant_make')) return redirect(prefixed_url_for('user_bp.tenant_make', for_redirect=True))
tenant_make_identification = request.form.get('selected_row') tenant_make_identification = request.form.get('selected_row')
tenant_make_id = ast.literal_eval(tenant_make_identification).get('value') tenant_make_id = ast.literal_eval(tenant_make_identification).get('value')
if action == 'edit_tenant_make': if action == 'edit_tenant_make':
return redirect(prefixed_url_for('user_bp.edit_tenant_make', tenant_make_id=tenant_make_id)) return redirect(prefixed_url_for('user_bp.edit_tenant_make', tenant_make_id=tenant_make_id, for_redirect=True))
elif action == 'set_as_default': elif action == 'set_as_default':
# Set this make as the default for the tenant # Set this make as the default for the tenant
tenant_id = session['tenant']['id'] tenant_id = session['tenant']['id']
@@ -682,7 +682,7 @@ def handle_tenant_make_selection():
current_app.logger.error(f'Failed to update default tenant make. Error: {str(e)}') current_app.logger.error(f'Failed to update default tenant make. Error: {str(e)}')
# Altijd teruggaan naar de tenant_makes pagina # Altijd teruggaan naar de tenant_makes pagina
return redirect(prefixed_url_for('user_bp.tenant_makes')) return redirect(prefixed_url_for('user_bp.tenant_makes', for_redirect=True))
@user_bp.route('/tenant_partner_services', methods=['GET', 'POST']) @user_bp.route('/tenant_partner_services', methods=['GET', 'POST'])

View File

@@ -57,9 +57,6 @@ http {
index index.html index.htm; index index.html index.htm;
} }
location /reset {
rewrite ^/reset(.*)$ /admin/reset$1 permanent;
}
location /static/ { location /static/ {
alias /etc/nginx/static/; alias /etc/nginx/static/;
@@ -100,17 +97,10 @@ http {
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Prefix /chat-client;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; proxy_set_header Connection "upgrade";
proxy_buffering off; proxy_buffering off;
# Add CORS headers
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
} }
location /admin/ { location /admin/ {
@@ -122,7 +112,6 @@ http {
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Prefix /admin; # Required for Flask views (used in nginx_utils
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; proxy_set_header Connection "upgrade";
@@ -140,60 +129,14 @@ http {
proxy_send_timeout 60s; proxy_send_timeout 60s;
proxy_read_timeout 60s; proxy_read_timeout 60s;
send_timeout 60s; send_timeout 60s;
# Subfilter to hide admin prefix from app
sub_filter_once off;
sub_filter_types text/html text/css application/javascript;
# General HTML sub_filters
sub_filter 'href="/static/' 'href="/static/';
sub_filter 'src="/static/' 'src="/static/';
sub_filter 'url("/static/' 'url("/static/';
sub_filter 'href="/' 'href="/admin/'; # Rewrites for other content
sub_filter 'src="/' 'src="/admin/';
sub_filter 'action="/' 'action="/admin/';
sub_filter 'url("/' 'url("/admin/';
# Sub_filters for JavaScript URLs
sub_filter 'url: "/' 'url: "/admin/';
sub_filter 'url: \"/' 'url: "/admin/';
sub_filter 'url("/' 'url("/admin/';
sub_filter 'url(\\"/' 'url("/admin/';
# Sub_filters for AJAX requests
sub_filter 'url: \'/user/' 'url: \'/admin/user/';
sub_filter 'url: "/user/' 'url: "/admin/user/';
sub_filter 'url: "/api/' 'url: "/admin/api/';
sub_filter 'url: \'/api/' 'url: \'/admin/api/';
} }
location /api/ { location /api/ {
# Handle preflight requests
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $http_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
# Mirror the Origin header if it's allowed by the application
# The application will handle the actual origin validation
add_header 'Access-Control-Allow-Origin' $http_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
proxy_pass http://eveai_api:8080/; proxy_pass http://eveai_api:8080/;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Prefix /api;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; proxy_set_header Connection "upgrade";

View File

@@ -0,0 +1,23 @@
Routing alignment notes (staging/prod)
Summary
- Root (/) issues a 301 redirect to /admin/ via server-snippet on the apps ingress.
- Prefixes /admin, /api, /chat-client are stripped at the edge and forwarded to their backends on /. The applications do not need to be prefix-aware.
- /verify remains available (Prefix) without any rewrite in a separate Ingress.
- No CORS annotations at ingress. Static assets are served by Bunny CDN; API CORS is not handled here.
- /flower is intentionally NOT exposed on k8s.
Files
- ingress-https.yaml: NGINX Ingress (apps) with regex paths and rewrite-target to strip prefixes; includes server-snippet to 301 redirect root to /admin/.
- ingress-verify.yaml: Separate Ingress for /verify without regex/rewrite.
Paths behavior
- / -> 301 /admin/
- /admin/<...> -> eveai-app-service:80, backend receives /<...>
- /api/<...> -> eveai-api-service:80, backend receives /<...>
- /chat-client/<...> -> eveai-chat-client-service:80, backend receives /<...>
- /verify[/**] -> verify-service:80, path preserved.
Notes
- The rewrite-target is global per Ingress. To avoid affecting /verify, we split it into its own Ingress. Keep this structure when adding/removing services.
- If you need temporary legacy redirects (e.g., /client -> /chat-client), add an additional Ingress with nginx.ingress.kubernetes.io/permanent-redirect and regex matching, or handle it at the app/CDN layer.

View File

@@ -14,6 +14,12 @@ metadata:
nginx.ingress.kubernetes.io/proxy-body-size: "10m" nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300" nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
cert-manager.io/cluster-issuer: letsencrypt-staging cert-manager.io/cluster-issuer: letsencrypt-staging
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: "/$2"
nginx.ingress.kubernetes.io/server-snippet: |
location = / {
return 301 /admin/;
}
spec: spec:
ingressClassName: nginx ingressClassName: nginx
tls: tls:
@@ -24,54 +30,28 @@ spec:
- host: evie-staging.askeveai.com - host: evie-staging.askeveai.com
http: http:
paths: paths:
# Verification service paths # Application services (strip prefix)
- path: /verify - path: /admin(/|$)(.*)
pathType: Prefix pathType: ImplementationSpecific
backend:
service:
name: verify-service
port:
number: 80
# Application services
- path: /admin
pathType: Prefix
backend: backend:
service: service:
name: eveai-app-service name: eveai-app-service
port: port:
number: 80 number: 80
- path: /api - path: /api(/|$)(.*)
pathType: Prefix pathType: ImplementationSpecific
backend: backend:
service: service:
name: eveai-api-service name: eveai-api-service
port: port:
number: 80 number: 80
- path: /client - path: /chat-client(/|$)(.*)
pathType: Prefix pathType: ImplementationSpecific
backend: backend:
service: service:
name: eveai-chat-client-service name: eveai-chat-client-service
port: port:
number: 80 number: 80
# Monitoring (when deployed)
# - path: /monitoring
# pathType: Prefix
# backend:
# service:
# name: monitoring-grafana
# port:
# number: 80
# Default: root path to verification service
- path: /
pathType: Prefix
backend:
service:
name: verify-service
port:
number: 80

View File

@@ -0,0 +1,32 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: eveai-staging-ingress-verify
namespace: eveai-staging
labels:
app: eveai
environment: staging
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
cert-manager.io/cluster-issuer: letsencrypt-staging
spec:
ingressClassName: nginx
tls:
- hosts:
- evie-staging.askeveai.com
secretName: evie-staging-tls
rules:
- host: evie-staging.askeveai.com
http:
paths:
- path: /verify
pathType: Prefix
backend:
service:
name: verify-service
port:
number: 80