diff --git a/common/services/tenant_services.py b/common/services/tenant_services.py index 5920d2a..da5a3d7 100644 --- a/common/services/tenant_services.py +++ b/common/services/tenant_services.py @@ -36,7 +36,6 @@ class TenantServices: tenant_partner = PartnerTenant( partner_service_id=management_service['id'], tenant_id=tenant_id, - relationship_type='MANAGED', ) set_logging_information(tenant_partner, dt.now(tz.utc)) diff --git a/docker/build_and_push_eveai.sh b/docker/build_and_push_eveai.sh index 1ae329b..b67f238 100755 --- a/docker/build_and_push_eveai.sh +++ b/docker/build_and_push_eveai.sh @@ -141,7 +141,7 @@ if [ $# -eq 0 ]; then SERVICES=() while IFS= read -r line; do SERVICES+=("$line") - done < <(yq e '.services | keys | .[]' compose_dev.yaml | grep -E '^(nginx|eveai_|flower)') + done < <(yq e '.services | keys | .[]' compose_dev.yaml | grep -E '^(nginx|eveai_|flower|prometheus|grafana)') else SERVICES=("$@") fi @@ -161,14 +161,14 @@ for SERVICE in "${SERVICES[@]}"; do if [[ "$SERVICE" == "nginx" ]]; then ./copy_specialist_svgs.sh ../config ../nginx/static/assets fi - if [[ "$SERVICE" == "nginx" || "$SERVICE" == eveai_* || "$SERVICE" == "flower" ]]; then + if [[ "$SERVICE" == "nginx" || "$SERVICE" == eveai_* || "$SERVICE" == "flower" || "$SERVICE" == "prometheus" || "$SERVICE" == "grafana" ]]; then if process_service "$SERVICE"; then echo "Successfully processed $SERVICE" else echo "Failed to process $SERVICE" fi else - echo "Skipping $SERVICE as it's not nginx or doesn't start with eveai_" + echo "Skipping $SERVICE as it's not nginx, flower, prometheus, grafana or doesn't start with eveai_" fi done diff --git a/docker/compose_dev.yaml b/docker/compose_dev.yaml index 41c011e..39356a1 100644 --- a/docker/compose_dev.yaml +++ b/docker/compose_dev.yaml @@ -375,6 +375,9 @@ services: prometheus: image: prom/prometheus:latest + build: + context: ./prometheus + dockerfile: Dockerfile container_name: prometheus ports: - "9090:9090" @@ -401,6 +404,9 @@ services: grafana: image: grafana/grafana:latest + build: + context: ./grafana + dockerfile: Dockerfile container_name: grafana ports: - "3000:3000" diff --git a/docker/compose_test.yaml b/docker/compose_test.yaml index d1a0df4..764b93b 100644 --- a/docker/compose_test.yaml +++ b/docker/compose_test.yaml @@ -9,7 +9,7 @@ # https://github.com/docker/awesome-compose x-common-variables: &common-variables - DB_HOST: db + DB_HOST: minty.ask-eve-ai-local.com DB_USER: luke DB_PASS: 'Skywalker!' DB_NAME: eveai @@ -53,6 +53,7 @@ services: - eveai_api networks: - eveai-network + restart: "no" eveai_app: image: josakola/eveai_app:${EVEAI_VERSION:-latest} @@ -67,8 +68,6 @@ services: - eveai_logs:/app/logs - crewai_storage:/app/crewai_storage depends_on: - db: - condition: service_healthy redis: condition: service_healthy minio: @@ -81,6 +80,7 @@ services: start_period: 30s networks: - eveai-network + restart: "no" eveai_workers: image: josakola/eveai_workers:${EVEAI_VERSION:-latest} @@ -93,14 +93,13 @@ services: - eveai_logs:/app/logs - crewai_storage:/app/crewai_storage depends_on: - db: - condition: service_healthy redis: condition: service_healthy minio: condition: service_healthy networks: - eveai-network + restart: "no" eveai_chat_workers: image: josakola/eveai_chat_workers:${EVEAI_VERSION:-latest} @@ -113,12 +112,11 @@ services: - eveai_logs:/app/logs - crewai_storage:/app/crewai_storage depends_on: - db: - condition: service_healthy redis: condition: service_healthy networks: - eveai-network + restart: "no" eveai_api: image: josakola/eveai_api:${EVEAI_VERSION:-latest} @@ -133,8 +131,6 @@ services: - eveai_logs:/app/logs - crewai_storage:/app/crewai_storage depends_on: - db: - condition: service_healthy redis: condition: service_healthy minio: @@ -147,6 +143,7 @@ services: start_period: 30s networks: - eveai-network + restart: "no" eveai_beat: image: josakola/eveai_beat:${EVEAI_VERSION:-latest} @@ -161,6 +158,7 @@ services: condition: service_healthy networks: - eveai-network + restart: "no" eveai_entitlements: image: josakola/eveai_entitlements:${EVEAI_VERSION:-latest} @@ -173,39 +171,17 @@ services: - eveai_logs:/app/logs - crewai_storage:/app/crewai_storage depends_on: - db: - condition: service_healthy redis: condition: service_healthy minio: condition: service_healthy networks: - eveai-network - - db: - hostname: db - image: ankane/pgvector - ports: - - 5432:5432 - restart: always - environment: - - POSTGRES_DB=eveai - - POSTGRES_USER=luke - - POSTGRES_PASSWORD=Skywalker! - volumes: - - pgdata:/var/lib/postgresql/data - - ./docker/db/init.sql:/docker-entrypoint-initdb.d/init.sql - healthcheck: - test: [ "CMD-SHELL", "pg_isready -d eveai -U luke" ] - interval: 10s - timeout: 5s - retries: 5 - networks: - - eveai-network + restart: "no" redis: image: redis:7.2.5 - restart: always + restart: no ports: - "6379:6379" volumes: @@ -228,6 +204,7 @@ services: - redis networks: - eveai-network + restart: "no" minio: image: minio/minio @@ -251,14 +228,14 @@ services: start_period: 30s networks: - eveai-network + restart: "no" prometheus: - image: prom/prometheus:latest + image: josakola/prometheus:${EVEAI_VERSION:-latest} container_name: prometheus ports: - "9090:9090" volumes: - - ./docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml - prometheusdata:/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' @@ -266,7 +243,7 @@ services: - '--web.console.libraries=/etc/prometheus/console_libraries' - '--web.console.templates=/etc/prometheus/consoles' - '--web.enable-lifecycle' - restart: unless-stopped + restart: no networks: - eveai-network @@ -279,18 +256,17 @@ services: - eveai-network grafana: - image: grafana/grafana:latest + image: josakola/grafana:${EVEAI_VERSION:-latest} container_name: grafana ports: - "3000:3000" volumes: - - ./docker/grafana/provisioning:/etc/grafana/provisioning - grafanadata:/var/lib/grafana environment: - GF_SECURITY_ADMIN_USER=admin - GF_SECURITY_ADMIN_PASSWORD=admin - GF_USERS_ALLOW_SIGN_UP=false - restart: unless-stopped + restart: no depends_on: - prometheus networks: diff --git a/docker/grafana/Dockerfile b/docker/grafana/Dockerfile new file mode 100644 index 0000000..186ff0c --- /dev/null +++ b/docker/grafana/Dockerfile @@ -0,0 +1,13 @@ +FROM grafana/grafana:latest + +# Copy dashboard and datasource provisioning configurations +COPY provisioning/dashboards /etc/grafana/provisioning/dashboards +COPY provisioning/datasources /etc/grafana/provisioning/datasources + +# Set environment variables +ENV GF_SECURITY_ADMIN_USER=admin +ENV GF_SECURITY_ADMIN_PASSWORD=admin +ENV GF_USERS_ALLOW_SIGN_UP=false + +# Expose the Grafana web interface +EXPOSE 3000 \ No newline at end of file diff --git a/docker/prometheus/Dockerfile b/docker/prometheus/Dockerfile new file mode 100644 index 0000000..999e35e --- /dev/null +++ b/docker/prometheus/Dockerfile @@ -0,0 +1,13 @@ +FROM prom/prometheus:latest + +COPY prometheus.yml /etc/prometheus/prometheus.yml + +# Expose the Prometheus UI and metrics endpoint +EXPOSE 9090 + +# Run Prometheus with the config file +CMD ["--config.file=/etc/prometheus/prometheus.yml", \ + "--storage.tsdb.path=/prometheus", \ + "--web.console.libraries=/etc/prometheus/console_libraries", \ + "--web.console.templates=/etc/prometheus/consoles", \ + "--web.enable-lifecycle"] \ No newline at end of file diff --git a/eveai_app/templates/navbar.html b/eveai_app/templates/navbar.html index 894af6d..6393024 100644 --- a/eveai_app/templates/navbar.html +++ b/eveai_app/templates/navbar.html @@ -78,11 +78,18 @@ ]) }} {% endif %} {% if current_user.is_authenticated %} - {{ dropdown('Partners', 'partner_exchange', [ + {% set partner_menu_items = [ {'name': 'Partners', 'url': '/partner/partners', 'roles': ['Super User']}, - {'name': 'Partner Services', 'url': '/partner/partner_services', 'roles': ['Super User']}, - {'name': 'Edit Partner', 'url': '/partner/partner/' ~ session['partner'].get('id'), 'roles': ['Super User', 'Partner Admin']}, - ]) }} + {'name': 'Partner Services', 'url': '/partner/partner_services', 'roles': ['Super User']} + ] %} + + {% if 'partner' in session and session['partner'] %} + {% set partner_menu_items = partner_menu_items + [ + {'name': 'Edit Partner', 'url': '/partner/partner/' ~ session['partner'].get('id'), 'roles': ['Super User', 'Partner Admin']} + ] %} + {% endif %} + + {{ dropdown('Partners', 'partner_exchange', partner_menu_items) }} {% endif %} {% if current_user.is_authenticated %} {{ dropdown('Documents', 'note_stack', [ diff --git a/eveai_app/views/user_views.py b/eveai_app/views/user_views.py index 211aade..244232d 100644 --- a/eveai_app/views/user_views.py +++ b/eveai_app/views/user_views.py @@ -3,7 +3,7 @@ from datetime import datetime as dt, timezone as tz from flask import request, redirect, flash, render_template, Blueprint, session, current_app from flask_mailman import EmailMessage from flask_security import roles_accepted, current_user -from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.exc import SQLAlchemyError, IntegrityError import ast from common.models.user import User, Tenant, Role, TenantDomain, TenantProject, PartnerTenant @@ -38,8 +38,8 @@ def log_after_request(response): @user_bp.route('/tenant', methods=['GET', 'POST']) @roles_accepted('Super User', 'Partner Admin') def tenant(): - if not current_user.has_roles('Partner Admin') and UserServices.can_user_create_tenant(): - current_app.logger.error(f'User {current_user.email} cannot create tenant in the current user') + if not UserServices.can_user_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') return redirect(prefixed_url_for('user_bp.select_tenant')) form = TenantForm() @@ -68,6 +68,17 @@ def tenant(): # Super User chose to associate with partner TenantServices.associate_tenant_with_partner(new_tenant.id) + except IntegrityError as e: + db.session.rollback() + # Check for the specific error about duplicate tenant name + if "tenant_name_key" in str(e) or "duplicate key value" in str(e): + flash(f"A tenant with the name '{form.name.data}' already exists. Please choose a different name.", + 'danger') + else: + current_app.logger.error(f'Failed to add tenant to database. Error: {str(e)}') + flash(f'Failed to add tenant to database. Error: {str(e)}', 'danger') + return render_template('user/tenant.html', form=form) + except SQLAlchemyError as e: current_app.logger.error(f'Failed to add tenant to database. Error: {str(e)}') flash(f'Failed to add tenant to database. Error: {str(e)}', 'danger') @@ -227,8 +238,12 @@ def select_tenant(): # Start with a base query query = Tenant.query + current_app.logger.debug("We proberen het scherm op te bouwen") + current_app.logger.debug(f"Session: {session}") + # Apply different filters based on user role if current_user.has_roles('Partner Admin') and 'partner' in session: + current_app.logger.debug("We zitten in partner mode") # Get the partner's management service management_service = next((service for service in session['partner']['services'] if service.get('type') == 'MANAGEMENT_SERVICE'), None) @@ -251,6 +266,7 @@ def select_tenant(): # Filter query to only show allowed tenants query = query.filter(Tenant.id.in_(allowed_tenant_ids)) + current_app.logger.debug("We zitten na partner service selectie") # Apply form filters (for both Super User and Partner Admin) if filter_form.validate_on_submit(): if filter_form.types.data: diff --git a/migrations/public/versions/9ed466e9756b_remove_relation_type_from_partnertenant.py b/migrations/public/versions/9ed466e9756b_remove_relation_type_from_partnertenant.py new file mode 100644 index 0000000..1a8e25c --- /dev/null +++ b/migrations/public/versions/9ed466e9756b_remove_relation_type_from_partnertenant.py @@ -0,0 +1,31 @@ +"""Remove relation_type from PartnerTenant + +Revision ID: 9ed466e9756b +Revises: 43a9f29fb214 +Create Date: 2025-05-07 14:11:25.132834 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '9ed466e9756b' +down_revision = '43a9f29fb214' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('partner_tenant', schema=None) as batch_op: + batch_op.drop_column('relationship_type') + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('partner_tenant', schema=None) as batch_op: + batch_op.add_column(sa.Column('relationship_type', sa.VARCHAR(length=20), autoincrement=False, nullable=False)) + # ### end Alembic commands ### diff --git a/requirements.txt b/requirements.txt index e1b5524..7fca92b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,32 +15,32 @@ Flask-Login~=0.6.3 flask-mailman~=1.1.1 Flask-Migrate~=4.1.0 Flask-Principal~=0.4.0 -Flask-Security-Too~=5.6.1 +Flask-Security-Too~=5.6.2 Flask-Session~=0.8.0 Flask-SQLAlchemy~=3.1.1 Flask-WTF~=1.2.1 -gevent~=24.11.1 +gevent~=25.4.2 gevent-websocket~=0.10.1 -greenlet~=3.0.3 -gunicorn~=22.0.0 +greenlet~=3.2.1 +gunicorn~=23.0.0 Jinja2~=3.1.6 kombu~=5.3.7 -langchain~=0.3.23 -langchain-anthropic~=0.3.11 -langchain-community~=0.3.21 -langchain-core~=0.3.52 +langchain~=0.3.25 +langchain-anthropic~=0.3.12 +langchain-community~=0.3.23 +langchain-core~=0.3.58 langchain-mistralai~=0.2.10 -langchain-openai~=0.3.13 +langchain-openai~=0.3.16 langchain-postgres~=0.0.14 -langchain-text-splitters~=0.3.0 +langchain-text-splitters~=0.3.8 langcodes~=3.4.0 langdetect~=1.0.9 langsmith~=0.1.81 -openai~=1.62.0 +openai~=1.77.0 pg8000~=1.31.2 pgvector~=0.2.5 pycryptodome~=3.20.0 -pydantic~=2.9.1 +pydantic>=2.10.3,<3 PyJWT~=2.10.1 python-dateutil~=2.9.0.post0 python-engineio~=4.9.1 @@ -57,7 +57,6 @@ urllib3~=2.2.2 WTForms~=3.2.1 wtforms-html5~=0.6.1 zxcvbn~=4.4.28 -groq~=0.9.0 pydub~=0.25.1 argparse~=1.4.0 minio~=7.2.7 @@ -72,7 +71,7 @@ PyPDF2~=3.0.1 flask-restx~=1.3.0 flask-healthz~=1.0.1 langsmith~=0.1.121 -anthropic~=0.34.2 +anthropic~=0.50.0 prometheus-client~=0.21.1 prometheus-flask-exporter~=0.23.2 flower~=2.0.1 @@ -84,7 +83,7 @@ typing_extensions~=4.12.2 babel~=2.16.0 dogpile.cache~=1.3.3 python-docx~=1.1.2 -crewai~=0.114.0 +crewai~=0.118.0 sseclient~=0.0.27 termcolor~=2.5.0 mistral-common~=1.5.3