- Getting containers ready for the cloud

This commit is contained in:
Josako
2024-08-06 14:56:12 +02:00
parent 64cf8df3a9
commit ab8359365d
20 changed files with 494 additions and 110 deletions

View File

@@ -10,7 +10,8 @@ from flask_jwt_extended import JWTManager
from flask_session import Session from flask_session import Session
from flask_wtf import CSRFProtect from flask_wtf import CSRFProtect
from .utils.key_encryption import JosKMSClient # from .utils.key_encryption import JosKMSClient
from .utils.simple_encryption import SimpleEncryption
from .utils.minio_utils import MinioClient from .utils.minio_utils import MinioClient
# Create extensions # Create extensions
@@ -26,5 +27,7 @@ socketio = SocketIO()
jwt = JWTManager() jwt = JWTManager()
session = Session() session = Session()
kms_client = JosKMSClient.from_service_account_json('config/gc_sa_eveai.json') # kms_client = JosKMSClient.from_service_account_json('config/gc_sa_eveai.json')
simple_encryption = SimpleEncryption()
minio_client = MinioClient() minio_client = MinioClient()

View File

@@ -0,0 +1,39 @@
from cryptography.fernet import Fernet
from flask import Flask
class SimpleEncryption:
def __init__(self, app: Flask = None):
self.app = app
self.fernet = None
if app is not None:
self.init_app(app)
def init_app(self, app: Flask):
self.app = app
encryption_key = app.config.get('API_ENCRYPTION_KEY')
if not encryption_key:
raise ValueError("ENCRYPTION_KEY is not set in the app configuration")
self.fernet = Fernet(encryption_key.encode())
# Optionally log the initialization (similar to your current setup)
app.logger.info('SimpleEncryption initialized')
def encrypt_api_key(self, api_key: str) -> str:
if not self.fernet:
raise RuntimeError("SimpleEncryption is not initialized. Call init_app first.")
return self.fernet.encrypt(api_key.encode()).decode()
def decrypt_api_key(self, encrypted_api_key: str) -> str:
if not self.fernet:
raise RuntimeError("SimpleEncryption is not initialized. Call init_app first.")
return self.fernet.decrypt(encrypted_api_key.encode()).decode()
@staticmethod
def generate_key() -> str:
"""Generate a new Fernet key."""
return Fernet.generate_key().decode()
# Usage:
# from common.utils.simple_encryption import simple_encryption
# simple_encryption.init_app(app)

View File

@@ -10,7 +10,7 @@ basedir = path.abspath(path.dirname(__file__))
class Config(object): class Config(object):
DEBUG = False DEBUG = False
DEVELOPMENT = False DEVELOPMENT = False
SECRET_KEY = '97867c1491bea5ee6a8e8436eb11bf2ba6a69ff53ab1b17ecba450d0f2e572e1' SECRET_KEY = environ.get('SECRET_KEY')
SESSION_COOKIE_SECURE = False SESSION_COOKIE_SECURE = False
SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_HTTPONLY = True
@@ -29,7 +29,7 @@ class Config(object):
# SECURITY_POST_RESET_VIEW = '/admin/login' # SECURITY_POST_RESET_VIEW = '/admin/login'
# SECURITY_POST_CHANGE_VIEW = '/admin/login' # SECURITY_POST_CHANGE_VIEW = '/admin/login'
# SECURITY_BLUEPRINT_NAME = 'security_bp' # SECURITY_BLUEPRINT_NAME = 'security_bp'
SECURITY_PASSWORD_SALT = '228614859439123264035565568761433607235' SECURITY_PASSWORD_SALT = environ.get('SECURITY_PASSWORD_SALT')
REMEMBER_COOKIE_SAMESITE = 'strict' REMEMBER_COOKIE_SAMESITE = 'strict'
SESSION_COOKIE_SAMESITE = 'strict' SESSION_COOKIE_SAMESITE = 'strict'
SECURITY_CONFIRMABLE = True SECURITY_CONFIRMABLE = True
@@ -45,13 +45,6 @@ class Config(object):
SECURITY_CSRF_HEADER = 'X-XSRF-TOKEN' SECURITY_CSRF_HEADER = 'X-XSRF-TOKEN'
WTF_CSRF_CHECK_DEFAULT = False WTF_CSRF_CHECK_DEFAULT = False
# flask-mailman settings
MAIL_SERVER = 'mail.flow-it.net'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USE_SSL = False
MAIL_DEFAULT_SENDER = ('eveAI Admin', 'eveai_admin@flow-it.net')
# file upload settings # file upload settings
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 MAX_CONTENT_LENGTH = 16 * 1024 * 1024
UPLOAD_EXTENSIONS = ['.txt', '.pdf', '.png', '.jpg', '.jpeg', '.gif'] UPLOAD_EXTENSIONS = ['.txt', '.pdf', '.png', '.jpg', '.jpeg', '.gif']
@@ -75,6 +68,18 @@ class Config(object):
'anthropic.claude-3-5-sonnet': 8000 'anthropic.claude-3-5-sonnet': 8000
} }
# OpenAI API Keys
OPENAI_API_KEY = environ.get('OPENAI_API_KEY')
# Groq API Keys
GROQ_API_KEY = environ.get('GROQ_API_KEY')
# Anthropic API Keys
ANTHROPIC_API_KEY = environ.get('ANTHROPIC_API_KEY')
# Portkey API Keys
PORTKEY_API_KEY = environ.get('PORTKEY_API_KEY')
# Celery settings # Celery settings
CELERY_TASK_SERIALIZER = 'json' CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json'
@@ -93,6 +98,12 @@ class Config(object):
PERMANENT_SESSION_LIFETIME = timedelta(minutes=60) PERMANENT_SESSION_LIFETIME = timedelta(minutes=60)
SESSION_REFRESH_EACH_REQUEST = True SESSION_REFRESH_EACH_REQUEST = True
# JWT settings
JWT_SECRET_KEY = environ.get('JWT_SECRET_KEY')
# API Encryption
API_ENCRYPTION_KEY = environ.get('API_ENCRYPTION_KEY')
# Fallback Algorithms # Fallback Algorithms
FALLBACK_ALGORITHMS = [ FALLBACK_ALGORITHMS = [
"RAG_TENANT", "RAG_TENANT",
@@ -115,25 +126,31 @@ class DevConfig(Config):
DEBUG = True DEBUG = True
FLASK_DEBUG = True FLASK_DEBUG = True
PYCHARM_DEBUG = False PYCHARM_DEBUG = False
EXPLAIN_TEMPLATE_LOADING = False
# Database Settings
DB_HOST = environ.get('DB_HOST', 'localhost') DB_HOST = environ.get('DB_HOST', 'localhost')
DB_USER = environ.get('DB_USER', 'luke') DB_USER = environ.get('DB_USER', 'luke')
DB_PASS = environ.get('DB_PASS', 'Skywalker!') DB_PASS = environ.get('DB_PASS', 'Skywalker!')
DB_NAME = environ.get('DB_NAME', 'eveai') DB_NAME = environ.get('DB_NAME', 'eveai')
SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASS}@{DB_HOST}:5432/{DB_NAME}' SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASS}@{DB_HOST}:5432/{DB_NAME}'
SQLALCHEMY_BINDS = {'public': SQLALCHEMY_DATABASE_URI} SQLALCHEMY_BINDS = {'public': SQLALCHEMY_DATABASE_URI}
EXPLAIN_TEMPLATE_LOADING = False
# flask-mailman settings
MAIL_SERVER = 'mail.flow-it.net'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USE_SSL = False
MAIL_DEFAULT_SENDER = ('eveAI Admin', 'eveai_admin@flow-it.net')
MAIL_USERNAME = environ.get('MAIL_USERNAME')
MAIL_PASSWORD = environ.get('MAIL_PASSWORD')
# 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 = '/admin'
EVEAI_CHAT_LOCATION_PREFIX = '/chat' EVEAI_CHAT_LOCATION_PREFIX = '/chat'
# flask-mailman settings
MAIL_USERNAME = 'eveai_super@flow-it.net'
MAIL_PASSWORD = '$6xsWGbNtx$CFMQZqc*'
# file upload settings # file upload settings
UPLOAD_FOLDER = '/app/tenant_files' # UPLOAD_FOLDER = '/app/tenant_files'
# Celery settings # Celery settings
# eveai_app Redis Settings # eveai_app Redis Settings
@@ -143,22 +160,10 @@ class DevConfig(Config):
CELERY_BROKER_URL_CHAT = 'redis://redis:6379/3' CELERY_BROKER_URL_CHAT = 'redis://redis:6379/3'
CELERY_RESULT_BACKEND_CHAT = 'redis://redis:6379/3' CELERY_RESULT_BACKEND_CHAT = 'redis://redis:6379/3'
# OpenAI API Keys
OPENAI_API_KEY = 'sk-proj-8R0jWzwjL7PeoPyMhJTZT3BlbkFJLb6HfRB2Hr9cEVFWEhU7'
# Groq API Keys
GROQ_API_KEY = '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'
# Anthropic API Keys
ANTHROPIC_API_KEY = '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'
# Portkey API Keys
PORTKEY_API_KEY = 'T2Dt4QTpgCvWxa1OftYCJtj7NcDZ'
# Unstructured settings # Unstructured settings
UNSTRUCTURED_API_KEY = 'pDgCrXumYhM3CNvjvwV8msMldXC3uw' # UNSTRUCTURED_API_KEY = 'pDgCrXumYhM3CNvjvwV8msMldXC3uw'
UNSTRUCTURED_BASE_URL = 'https://flowitbv-16c4us0m.api.unstructuredapp.io' # UNSTRUCTURED_BASE_URL = 'https://flowitbv-16c4us0m.api.unstructuredapp.io'
UNSTRUCTURED_FULL_URL = 'https://flowitbv-16c4us0m.api.unstructuredapp.io/general/v0/general' # UNSTRUCTURED_FULL_URL = 'https://flowitbv-16c4us0m.api.unstructuredapp.io/general/v0/general'
# SocketIO settings # SocketIO settings
SOCKETIO_MESSAGE_QUEUE = 'redis://redis:6379/1' SOCKETIO_MESSAGE_QUEUE = 'redis://redis:6379/1'
@@ -176,9 +181,6 @@ class DevConfig(Config):
GC_KEY_RING = 'eveai-chat' GC_KEY_RING = 'eveai-chat'
GC_CRYPTO_KEY = 'envelope-encryption-key' GC_CRYPTO_KEY = 'envelope-encryption-key'
# JWT settings
JWT_SECRET_KEY = 'bsdMkmQ8ObfMD52yAFg4trrvjgjMhuIqg2fjDpD/JqvgY0ccCcmlsEnVFmR79WPiLKEA3i8a5zmejwLZKl4v9Q=='
# Session settings # Session settings
SESSION_REDIS = redis.from_url('redis://redis:6379/2') SESSION_REDIS = redis.from_url('redis://redis:6379/2')
@@ -194,19 +196,28 @@ class DevConfig(Config):
class ProdConfig(Config): class ProdConfig(Config):
DEVELOPMENT = False DEVELOPMENT = False
DEBUG = False DEBUG = False
DEVELOPMENT = True DEBUG = False
DEBUG = True FLASK_DEBUG = False
FLASK_DEBUG = True
PYCHARM_DEBUG = False PYCHARM_DEBUG = False
DB_HOST = environ.get('DB_HOST', 'bswnz4.stackhero-network.com') EXPLAIN_TEMPLATE_LOADING = False
DB_USER = environ.get('DB_USER', 'luke_skywalker')
DB_PASS = environ.get('DB_PASS', '2MK&1rHmWEydE2rFuJLq*ls%tdkPAk2')
DB_NAME = environ.get('DB_NAME', 'eveai')
DB_PORT = environ.get('DB_PORT', '5945')
# Database Settings
DB_HOST = environ.get('DB_HOST')
DB_USER = environ.get('DB_USER')
DB_PASS = environ.get('DB_PASS')
DB_NAME = environ.get('DB_NAME')
DB_PORT = environ.get('DB_PORT')
SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}' SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}'
SQLALCHEMY_BINDS = {'public': SQLALCHEMY_DATABASE_URI} SQLALCHEMY_BINDS = {'public': SQLALCHEMY_DATABASE_URI}
EXPLAIN_TEMPLATE_LOADING = False
# flask-mailman settings
MAIL_SERVER = 'mail.askeveai.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USE_SSL = False
MAIL_DEFAULT_SENDER = ('Evie Admin', 'evie_admin@askeveai.com')
MAIL_USERNAME = environ.get('MAIL_USERNAME')
MAIL_PASSWORD = environ.get('MAIL_PASSWORD')
# 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 = '/admin'
@@ -217,12 +228,13 @@ class ProdConfig(Config):
MAIL_PASSWORD = '$6xsWGbNtx$CFMQZqc*' MAIL_PASSWORD = '$6xsWGbNtx$CFMQZqc*'
# file upload settings # file upload settings
UPLOAD_FOLDER = '/app/tenant_files' # UPLOAD_FOLDER = '/app/tenant_files'
REDIS_USER = 'admin' # Redis Settings
REDIS_PASS = 'b32vtDtLriSY1fL2zGrZg8IZKI0g9ucsLtVNanRFAras6oZ51wjVNB1Y05uG7uEw' REDIS_USER = environ.get('REDIS_USER')
REDIS_URL = '8bciqc.stackhero-network.com' REDIS_PASS = environ.get('REDIS_PASS')
REDIS_PORT = '9961' REDIS_URL = environ.get('REDIS_URL')
REDIS_PORT = environ.get('REDIS_PORT', '6379')
REDIS_BASE_URI = f'redis://{REDIS_USER}:{REDIS_PASS}@{REDIS_URL}:{REDIS_PORT}' REDIS_BASE_URI = f'redis://{REDIS_USER}:{REDIS_PASS}@{REDIS_URL}:{REDIS_PORT}'
# Celery settings # Celery settings
@@ -236,23 +248,6 @@ class ProdConfig(Config):
# Session settings # Session settings
SESSION_REDIS = redis.from_url(f'{REDIS_BASE_URI}/2') SESSION_REDIS = redis.from_url(f'{REDIS_BASE_URI}/2')
# OpenAI API Keys
OPENAI_API_KEY = 'sk-proj-8R0jWzwjL7PeoPyMhJTZT3BlbkFJLb6HfRB2Hr9cEVFWEhU7'
# Groq API Keys
GROQ_API_KEY = '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'
# Anthropic API Keys
ANTHROPIC_API_KEY = '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'
# Portkey API Keys
PORTKEY_API_KEY = 'T2Dt4QTpgCvWxa1OftYCJtj7NcDZ'
# Unstructured settings
UNSTRUCTURED_API_KEY = 'pDgCrXumYhM3CNvjvwV8msMldXC3uw'
UNSTRUCTURED_BASE_URL = 'https://flowitbv-16c4us0m.api.unstructuredapp.io'
UNSTRUCTURED_FULL_URL = 'https://flowitbv-16c4us0m.api.unstructuredapp.io/general/v0/general'
# SocketIO settings # SocketIO settings
SOCKETIO_MESSAGE_QUEUE = f'{REDIS_BASE_URI}/1' SOCKETIO_MESSAGE_QUEUE = f'{REDIS_BASE_URI}/1'
SOCKETIO_CORS_ALLOWED_ORIGINS = '*' SOCKETIO_CORS_ALLOWED_ORIGINS = '*'
@@ -269,15 +264,14 @@ class ProdConfig(Config):
GC_KEY_RING = 'eveai-chat' GC_KEY_RING = 'eveai-chat'
GC_CRYPTO_KEY = 'envelope-encryption-key' GC_CRYPTO_KEY = 'envelope-encryption-key'
# JWT settings
JWT_SECRET_KEY = 'bsdMkmQ8ObfMD52yAFg4trrvjgjMhuIqg2fjDpD/JqvgY0ccCcmlsEnVFmR79WPiLKEA3i8a5zmejwLZKl4v9Q=='
# PATH settings # PATH settings
ffmpeg_path = '/usr/bin/ffmpeg' ffmpeg_path = '/usr/bin/ffmpeg'
config = { def get_config(config_name='dev'):
'dev': DevConfig(), configs = {
'prod': ProdConfig(), 'dev': DevConfig,
'default': DevConfig(), 'prod': ProdConfig,
} 'default': DevConfig,
}
return configs.get(config_name)

81
docker/build_tag_and_push.sh Executable file
View File

@@ -0,0 +1,81 @@
#!/bin/bash
# Source the environment setup script
if [ -f "./docker_env_switch.sh" ]; then
source ./docker_env_switch.sh dev
else
echo "Error: set_environment.sh not found in the current directory."
exit 1
fi
# Docker Hub username
USERNAME="josakola"
# List of all your services
ALL_SERVICES=("eveai_app" "eveai_workers" "eveai_chat" "eveai_chat_workers" "nginx")
# Function to build, tag and push an image
build_tag_and_push() {
local service=$1
echo "Building, tagging, and pushing ${service}..."
# Extract dockerfile and context paths from Docker Compose configuration
local dockerfile_path
local context_path
dockerfile_path=$(docker compose -f "$COMPOSE_FILE" config | grep "${service}" -A 10 | grep "dockerfile" | awk '{print $2}')
context_path=$(docker compose -f "$COMPOSE_FILE" config | grep "${service}" -A 10 | grep "context" | awk '{print $2}')
# Verify the paths
echo "Dockerfile path: ${dockerfile_path}"
echo "Context path: ${context_path}"
# Use docker buildx to build the image
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t "${USERNAME}/${service}:latest" \
--push \
--file "${dockerfile_path}" \
"${context_path}"
}
# Function to check if a service is in the ALL_SERVICES array
service_exists() {
local service=$1
for s in "${ALL_SERVICES[@]}"; do
if [[ "$s" == "$service" ]]; then
return 0
fi
done
return 1
}
# Ensure we're using buildx
docker buildx create --use
# If COMPOSE_FILE is not set, use a default value
if [ -z "$COMPOSE_FILE" ]; then
COMPOSE_FILE="compose_dev.yaml"
echo "COMPOSE_FILE not set, using default: $COMPOSE_FILE"
else
echo "Using COMPOSE_FILE: $COMPOSE_FILE"
fi
# If no arguments are provided, process all services
if [ $# -eq 0 ]; then
echo "No specific services provided. Processing all services..."
for service in "${ALL_SERVICES[@]}"; do
build_tag_and_push "$service"
done
else
# Process only the specified services
for service in "$@"; do
if service_exists "$service"; then
build_tag_and_push "$service"
else
echo "Warning: ${service} is not a recognized service name. Skipping."
fi
done
fi
echo "Done!"

View File

@@ -11,17 +11,33 @@
x-common-variables: &common-variables x-common-variables: &common-variables
DB_HOST: db DB_HOST: db
DB_USER: luke DB_USER: luke
DB_PASS: Skywalker! DB_PASS: 'Skywalker!'
DB_NAME: eveai DB_NAME: eveai
DB_PORT: '5432'
FLASK_ENV: development FLASK_ENV: development
FLASK_DEBUG: 1 FLASK_DEBUG: true
SECRET_KEY: '97867c1491bea5ee6a8e8436eb11bf2ba6a69ff53ab1b17ecba450d0f2e572e1'
SECURITY_PASSWORD_SALT: '228614859439123264035565568761433607235'
MAIL_USERNAME: eveai_super@flow-it.net
MAIL_PASSWORD: '$6xsWGbNtx$CFMQZqc*'
OPENAI_API_KEY: 'sk-proj-8R0jWzwjL7PeoPyMhJTZT3BlbkFJLb6HfRB2Hr9cEVFWEhU7'
GROQ_API_KEY: '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'
ANTHROPIC_API_KEY: 'sk-ant-api03-c2TmkzbReeGhXBO5JxNH6BJNylRDonc9GmZd0eRbrvyekec2'
PORTKEY_API_KEY: 'T2Dt4QTpgCvWxa1OftYCJtj7NcDZ'
JWT_SECRET_KEY: 'bsdMkmQ8ObfMD52yAFg4trrvjgjMhuIqg2fjDpD/JqvgY0ccCcmlsEnVFmR79WPiLKEA3i8a5zmejwLZKl4v9Q=='
API_ENCRYPTION_KEY: 'xfF5369IsredSrlrYZqkM9ZNrfUASYYS6TCcAR9UKj4='
MINIO_ENDPOINT: minio:9000 MINIO_ENDPOINT: minio:9000
MINIO_ACCESS_KEY: minioadmin MINIO_ACCESS_KEY: minioadmin
MINIO_SECRET_KEY: minioadmin MINIO_SECRET_KEY: minioadmin
services: services:
nginx: nginx:
image: nginx:latest build:
context: ../nginx
dockerfile: Dockerfile
platforms:
- linux/amd64
- linux/arm64
ports: ports:
- 80:80 - 80:80
- 8080:8080 - 8080:8080
@@ -39,6 +55,9 @@ services:
build: build:
context: .. context: ..
dockerfile: ./docker/eveai_app/Dockerfile dockerfile: ./docker/eveai_app/Dockerfile
platforms:
- linux/amd64
- linux/arm64
ports: ports:
- 5001:5001 - 5001:5001
environment: environment:
@@ -69,6 +88,9 @@ services:
build: build:
context: .. context: ..
dockerfile: ./docker/eveai_workers/Dockerfile dockerfile: ./docker/eveai_workers/Dockerfile
platforms:
- linux/amd64
- linux/arm64
# ports: # ports:
# - 5001:5001 # - 5001:5001
environment: environment:
@@ -98,6 +120,9 @@ services:
build: build:
context: .. context: ..
dockerfile: ./docker/eveai_chat/Dockerfile dockerfile: ./docker/eveai_chat/Dockerfile
platforms:
- linux/amd64
- linux/arm64
ports: ports:
- 5002:5002 - 5002:5002
environment: environment:
@@ -125,6 +150,9 @@ services:
build: build:
context: .. context: ..
dockerfile: ./docker/eveai_chat_workers/Dockerfile dockerfile: ./docker/eveai_chat_workers/Dockerfile
platforms:
- linux/amd64
- linux/arm64
# ports: # ports:
# - 5001:5001 # - 5001:5001
environment: environment:

View File

@@ -0,0 +1,123 @@
# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Docker Compose reference guide at
# https://docs.docker.com/go/compose-spec-reference/
# Here the instructions define your application as a service called "server".
# This service is built from the Dockerfile in the current directory.
# You can add other services your application may depend on here, such as a
# database or a cache. For examples, see the Awesome Compose repository:
# https://github.com/docker/awesome-compose
x-common-variables: &common-variables
DB_HOST: bswnz4.stackhero-network.com
DB_USER: luke_skywalker
DB_PASS: 2MK&1rHmWEydE2rFuJLq*ls%tdkPAk2
DB_NAME: eveai
DB_PORT: '5945'
FLASK_ENV: production
FLASK_DEBUG: false
SECRET_KEY: '38wg8e1lvhlvcu0apr95n8o07axf244lzaa7b7djh7itrf8jnyyh1lkuco529w'
SECURITY_PASSWORD_SALT: '166448071751628781809462050022558634074'
MAIL_USERNAME: 'evie_admin@askeveai.com'
MAIL_PASSWORD: 's5D%R#y^v!s&6Z^i0k&'
REDIS_USER: admin
REDIS_PASS: 'b32vtDtLriSY1fL2zGrZg8IZKI0g9ucsLtVNanRFAras6oZ51wjVNB1Y05uG7uEw'
REDIS_URL: 8bciqc.stackhero-network.com
REDIS_PORT: '9961'
OPENAI_API_KEY: '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'
GROQ_API_KEY: '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'
ANTHROPIC_API_KEY: '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'
PORTKEY_API_KEY: '3C+zAGR8pCalevBXFVc0l8R2MPYc'
JWT_SECRET_KEY: '0d99e810e686ea567ef305d8e9b06195c4db482952e19276590a726cde60a408'
API_ENCRYPTION_KEY: 'Ly5XYWwEKiasfAwEqdEMdwR-k0vhrq6QPYd4whEROB0='
services:
nginx:
image: nginx:latest
ports:
- 80:80
- 8080:8080
volumes:
- ../nginx:/etc/nginx
- ../nginx/sites-enabled:/etc/nginx/sites-enabled
- ../nginx/static:/etc/nginx/static
- ../nginx/public:/etc/nginx/public
- ./logs/nginx:/var/log/nginx
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`evie.askeveai.com`)"
- "traefik.http.services.nginx.loadbalancer.server.port=80"
depends_on:
- eveai_app
- eveai_chat
eveai_app:
image: josakola/eveai_app:latest
ports:
- 5001:5001
environment:
<<: *common-variables
volumes:
- ./logs:/app/logs
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5001/health"]
interval: 10s
timeout: 5s
retries: 5
command: ["sh", "-c", "scripts/start_eveai_app.sh"]
eveai_workers:
image: josakola/eveai_workers:latest
# ports:
# - 5001:5001
environment:
<<: *common-variables
volumes:
- ./logs:/app/logs
# healthcheck:
# test: [ "CMD", "curl", "-f", "http://localhost:5001/health" ]
# interval: 10s
# timeout: 5s
# retries: 5
command: [ "sh", "-c", "scripts/start_eveai_workers.sh" ]
eveai_chat:
image: josakola/eveai_chat:latest
ports:
- 5002:5002
environment:
<<: *common-variables
volumes:
- ./logs:/app/logs
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:5002/health" ] # Adjust based on your health endpoint
interval: 10s
timeout: 5s
retries: 5
command: ["sh", "-c", "scripts/start_eveai_chat.sh"]
eveai_chat_workers:
image: josakola/eveai_chat_workers:latest
# ports:
# - 5001:5001
environment:
<<: *common-variables
volumes:
- ./logs:/app/logs
# healthcheck:
# test: [ "CMD", "curl", "-f", "http://localhost:5001/health" ]
# interval: 10s
# timeout: 5s
# retries: 5
command: [ "sh", "-c", "scripts/start_eveai_chat_workers.sh" ]
#volumes:
# minio_data:
# db-data:
# redis-data:
# tenant-files:
#secrets:
# db-password:
# file: ./db/password.txt

57
docker/docker_env_switch.sh Executable file
View File

@@ -0,0 +1,57 @@
#!/bin/bash
# Function to display usage information
usage() {
echo "Usage: source $0 [dev|prod]"
echo " dev : Switch to development environment"
echo " prod : Switch to production environment"
}
# Check if the script is sourced
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
echo "Error: This script must be sourced, not executed directly."
echo "Please run: source $0 [dev|prod]"
exit 1
fi
# Check if an argument is provided
if [ $# -eq 0 ]; then
usage
return 1
fi
# Set variables based on the environment
case $1 in
dev)
DOCKER_CONTEXT="default"
COMPOSE_FILE="compose_dev.yaml"
;;
prod)
DOCKER_CONTEXT="mxz536.stackhero-network.com"
COMPOSE_FILE="compose_stackhero.yaml"
;;
*)
echo "Invalid environment. Use 'dev' or 'prod'."
usage
return 1
;;
esac
# Switch Docker context
echo "Switching to Docker context: $DOCKER_CONTEXT"
docker context use $DOCKER_CONTEXT
# Set the COMPOSE_FILE environment variable
export COMPOSE_FILE=$COMPOSE_FILE
echo "Set COMPOSE_FILE to $COMPOSE_FILE"
# Define aliases for common Docker commands
alias docker-compose="docker compose -f $COMPOSE_FILE"
alias dc="docker compose -f $COMPOSE_FILE"
alias dcup="docker compose -f $COMPOSE_FILE up -d"
alias dcdown="docker compose -f $COMPOSE_FILE down"
alias dcps="docker compose -f $COMPOSE_FILE ps"
alias dclogs="docker compose -f $COMPOSE_FILE logs"
echo "Docker environment switched to $1"
echo "You can now use 'docker-compose', 'dc', 'dcup', 'dcdown', 'dcps', and 'dclogs' commands"

14
docker/stackhero_link.sh Executable file
View File

@@ -0,0 +1,14 @@
# HOST is your Stackhero for Docker instance domain name (mxz536.stackhero-network.com).
# SERVICE_ID is your Stackhero service ID.
# CERTIFICATES_PASSWORD is the password defined in your Stackhero for Docker configuration.
(export HOST="mxz536.stackhero-network.com"
export SERVICE_ID="svc-rlaeva"
export CERTIFICATES_PASSWORD="OonfQaQerGLLsWPKmnudyghFilIcPJRW"
cd /tmp/ \
&& curl -o certificates.tar https://docker:$CERTIFICATES_PASSWORD@$HOST/stackhero/docker/certificates.tar \
&& tar -xf certificates.tar \
&& (docker context rm -f $HOST 2> /dev/null || true) \
&& docker context create $HOST \
--description "$SERVICE_ID ($HOST)" \
--docker "host=tcp://$HOST:2376,ca=ca.pem,cert=cert.pem,key=key.pem")

View File

@@ -6,8 +6,8 @@ from flask_security.signals import user_authenticated
from werkzeug.middleware.proxy_fix import ProxyFix from werkzeug.middleware.proxy_fix import ProxyFix
import logging.config import logging.config
from common.extensions import (db, migrate, bootstrap, security, mail, login_manager, cors, kms_client, csrf, session, from common.extensions import (db, migrate, bootstrap, security, mail, login_manager, cors, csrf, session,
minio_client) minio_client, simple_encryption)
from common.models.user import User, Role, Tenant, TenantDomain from common.models.user import User, Role, Tenant, TenantDomain
import common.models.interaction import common.models.interaction
from config.logging_config import LOGGING from config.logging_config import LOGGING
@@ -15,6 +15,7 @@ from common.utils.security import set_tenant_session_data
from .errors import register_error_handlers from .errors import register_error_handlers
from common.utils.celery_utils import make_celery, init_celery from common.utils.celery_utils import make_celery, init_celery
from common.utils.template_filters import register_filters from common.utils.template_filters import register_filters
from config.config import get_config
def create_app(config_file=None): def create_app(config_file=None):
@@ -23,10 +24,16 @@ def create_app(config_file=None):
# Ensure all necessary headers are handled # Ensure all necessary headers are handled
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1) app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1)
if config_file is None: environment = os.getenv('FLASK_ENV', 'development')
app.config.from_object('config.config.DevConfig') print(environment)
else:
app.config.from_object(config_file) match environment:
case 'development':
app.config.from_object(get_config('dev'))
case 'production':
app.config.from_object(get_config('prod'))
case _:
app.config.from_object(get_config('dev'))
app.config['SESSION_KEY_PREFIX'] = 'eveai_app_' app.config['SESSION_KEY_PREFIX'] = 'eveai_app_'
@@ -39,14 +46,13 @@ def create_app(config_file=None):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logger.info("eveai_app starting up") logger.info("eveai_app starting up")
logger.debug("start config")
logger.debug(app.config)
# Register extensions # Register extensions
register_extensions(app) register_extensions(app)
# Check GCloud availability
kms_client.check_kms_access_and_latency()
app.celery = make_celery(app.name, app.config) app.celery = make_celery(app.name, app.config)
init_celery(app.celery, app) init_celery(app.celery, app)
@@ -101,7 +107,8 @@ def register_extensions(app):
csrf.init_app(app) csrf.init_app(app)
login_manager.init_app(app) login_manager.init_app(app)
cors.init_app(app) cors.init_app(app)
kms_client.init_app(app) # kms_client.init_app(app)
simple_encryption.init_app(app)
session.init_app(app) session.init_app(app)
minio_client.init_app(app) minio_client.init_app(app)

View File

@@ -1,5 +1,5 @@
<header class="header-2"> <header class="header-2">
<div class="page-header min-vh-25" style="background-image: url({{url_for('static', filename='/assets/img/EveAI_bg2.jpg')}})" loading="lazy"> <div class="page-header min-vh-25" style="background-image: url({{url_for('static', filename='/assets/img/EveAI_bg.jpg')}})" loading="lazy">
<span class="mask bg-gradient-primary opacity-4"></span> <span class="mask bg-gradient-primary opacity-4"></span>
<div class="container"> <div class="container">
<div class="row"> <div class="row">

View File

@@ -8,7 +8,7 @@ from sqlalchemy.exc import SQLAlchemyError
import ast import ast
from common.models.user import User, Tenant, Role, TenantDomain from common.models.user import User, Tenant, Role, TenantDomain
from common.extensions import db, kms_client, security, minio_client from common.extensions import db, security, minio_client, simple_encryption
from common.utils.security_utils import send_confirmation_email, send_reset_email from common.utils.security_utils import send_confirmation_email, send_reset_email
from .user_forms import TenantForm, CreateUserForm, EditUserForm, TenantDomainForm from .user_forms import TenantForm, CreateUserForm, EditUserForm, TenantDomainForm
from common.utils.database import Database from common.utils.database import Database
@@ -427,7 +427,7 @@ def generate_chat_api_key():
tenant = Tenant.query.get_or_404(session['tenant']['id']) tenant = Tenant.query.get_or_404(session['tenant']['id'])
new_api_key = generate_api_key(prefix="EveAI-CHAT") new_api_key = generate_api_key(prefix="EveAI-CHAT")
tenant.encrypted_chat_api_key = kms_client.encrypt_api_key(new_api_key) tenant.encrypted_chat_api_key = simple_encryption.encrypt_api_key(new_api_key)
update_logging_information(tenant, dt.now(tz.utc)) update_logging_information(tenant, dt.now(tz.utc))
try: try:

View File

@@ -1,22 +1,28 @@
import logging import logging
import logging.config import logging.config
from flask import Flask, jsonify from flask import Flask, jsonify
from redis import Redis import os
from common.extensions import db, socketio, jwt, kms_client, cors, session from common.extensions import db, socketio, jwt, cors, session, simple_encryption
from config.logging_config import LOGGING from config.logging_config import LOGGING
from eveai_chat.socket_handlers import chat_handler from eveai_chat.socket_handlers import chat_handler
from common.utils.cors_utils import create_cors_after_request from common.utils.cors_utils import create_cors_after_request
from common.utils.celery_utils import make_celery, init_celery from common.utils.celery_utils import make_celery, init_celery
from config.config import get_config
def create_app(config_file=None): def create_app(config_file=None):
app = Flask(__name__) app = Flask(__name__)
if config_file is None: environment = os.getenv('FLASK_ENV', 'development')
app.config.from_object('config.config.DevConfig')
else: match environment:
app.config.from_object(config_file) case 'development':
app.config.from_object(get_config('dev'))
case 'production':
app.config.from_object(get_config('prod'))
case _:
app.config.from_object(get_config('dev'))
app.config['SESSION_KEY_PREFIX'] = 'eveai_chat_' app.config['SESSION_KEY_PREFIX'] = 'eveai_chat_'
@@ -55,7 +61,8 @@ def register_extensions(app):
ping_interval=app.config.get('SOCKETIO_PING_INTERVAL'), ping_interval=app.config.get('SOCKETIO_PING_INTERVAL'),
) )
jwt.init_app(app) jwt.init_app(app)
kms_client.init_app(app) # kms_client.init_app(app)
simple_encryption.init_app(app)
# Cors setup # Cors setup
cors.init_app(app, resources={r"/chat/*": {"origins": "*"}}) cors.init_app(app, resources={r"/chat/*": {"origins": "*"}})

View File

@@ -6,7 +6,7 @@ from flask import current_app, request, session
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from datetime import datetime, timedelta from datetime import datetime, timedelta
from common.extensions import socketio, kms_client, db from common.extensions import socketio, db, simple_encryption
from common.models.user import Tenant from common.models.user import Tenant
from common.models.interaction import Interaction from common.models.interaction import Interaction
from common.utils.celery_utils import current_celery from common.utils.celery_utils import current_celery
@@ -156,7 +156,7 @@ def handle_feedback(data):
def validate_api_key(tenant_id, api_key): def validate_api_key(tenant_id, api_key):
tenant = Tenant.query.get_or_404(tenant_id) tenant = Tenant.query.get_or_404(tenant_id)
decrypted_api_key = kms_client.decrypt_api_key(tenant.encrypted_chat_api_key) decrypted_api_key = simple_encryption.decrypt_api_key(tenant.encrypted_chat_api_key)
return decrypted_api_key == api_key return decrypted_api_key == api_key

View File

@@ -1,19 +1,26 @@
import logging import logging
import logging.config import logging.config
from flask import Flask from flask import Flask
import os
from common.utils.celery_utils import make_celery, init_celery from common.utils.celery_utils import make_celery, init_celery
from common.extensions import db from common.extensions import db
from config.logging_config import LOGGING from config.logging_config import LOGGING
from config.config import get_config
def create_app(config_file=None): def create_app(config_file=None):
app = Flask(__name__) app = Flask(__name__)
if config_file is None: environment = os.getenv('FLASK_ENV', 'development')
app.config.from_object('config.config.DevConfig')
else: match environment:
app.config.from_object(config_file) case 'development':
app.config.from_object(get_config('dev'))
case 'production':
app.config.from_object(get_config('prod'))
case _:
app.config.from_object(get_config('dev'))
logging.config.dictConfig(LOGGING) logging.config.dictConfig(LOGGING)

View File

@@ -1,19 +1,26 @@
import logging import logging
import logging.config import logging.config
from flask import Flask from flask import Flask
import os
from common.utils.celery_utils import make_celery, init_celery from common.utils.celery_utils import make_celery, init_celery
from common.extensions import db, minio_client from common.extensions import db, minio_client
from config.logging_config import LOGGING from config.logging_config import LOGGING
from config.config import get_config
def create_app(config_file=None): def create_app(config_file=None):
app = Flask(__name__) app = Flask(__name__)
if config_file is None: environment = os.getenv('FLASK_ENV', 'development')
app.config.from_object('config.config.DevConfig')
else: match environment:
app.config.from_object(config_file) case 'development':
app.config.from_object(get_config('dev'))
case 'production':
app.config.from_object(get_config('prod'))
case _:
app.config.from_object(get_config('dev'))
logging.config.dictConfig(LOGGING) logging.config.dictConfig(LOGGING)
app.embed_tuning_logger = logging.getLogger('embed_tuning') app.embed_tuning_logger = logging.getLogger('embed_tuning')

View File

@@ -17,7 +17,7 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const eveAI = new EveAI( const eveAI = new EveAI(
'6', '6',
'EveAI-CHAT-3622-2083-4559-6024-8786', 'EveAI-CHAT-6530-7449-1171-8988-8156',
'http://macstudio.ask-eve-ai-local.com', 'http://macstudio.ask-eve-ai-local.com',
'en' 'en'
); );

View File

@@ -1,3 +1,13 @@
/*Overriding Colors*/
:root {
--bs-primary: #76599a;
--bs-secondary: #b14f9d;
--bs-success: #f8e1a9;
--bs-info: #423372;
--bs-warning: #eb7f31;
--bs-danger: #9c2d66;
}
.form-control { .form-control {
border: 1px solid #d2d6da; border: 1px solid #d2d6da;
padding: 0.625rem 0.75rem; padding: 0.625rem 0.75rem;

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

View File

@@ -79,4 +79,5 @@ portkey_ai~=1.7.0
minio~=7.2.7 minio~=7.2.7
Werkzeug~=3.0.3 Werkzeug~=3.0.3
itsdangerous~=2.2.0 itsdangerous~=2.2.0
cryptography~=43.0.0

View File

@@ -0,0 +1,6 @@
from common.utils.simple_encryption import SimpleEncryption
if __name__ == "__main__":
key = SimpleEncryption.generate_key()
print(f"Generated encryption key: {key}")
print("Set this as your ENCRYPTION_KEY environment variable or in your config.py file.")