API key working, CORS working, SocketIO working (but no JWT), Chat client v1, Session implemented (server side)

This commit is contained in:
Josako
2024-05-22 21:32:09 +02:00
parent 883988dbab
commit 364da812ba
21 changed files with 763 additions and 69 deletions

View File

@@ -1,10 +1,12 @@
import logging
import logging.config
from flask import Flask
from flask_socketio import emit
from redis import Redis
from common.extensions import db, socketio
from common.extensions import db, socketio, jwt, kms_client, cors, session
from config.logging_config import LOGGING
from eveai_chat.socket_handlers import chat_handler
from common.utils.cors_utils import create_cors_after_request
def create_app(config_file=None):
@@ -18,6 +20,15 @@ def create_app(config_file=None):
logging.config.dictConfig(LOGGING)
register_extensions(app)
# Register Blueprints
register_blueprints(app)
@app.route('/ping')
def ping():
return 'pong'
app.logger.info("EveAI Chat Server Started Successfully")
app.logger.info("-------------------------------------------------------------------------------------------------")
return app
@@ -29,5 +40,25 @@ def register_extensions(app):
async_mode=app.config.get('SOCKETIO_ASYNC_MODE'),
logger=app.config.get('SOCKETIO_LOGGER'),
engineio_logger=app.config.get('SOCKETIO_ENGINEIO_LOGGER'),
path='/socket.io'
)
jwt.init_app(app)
kms_client.init_app(app)
# Cors setup
cors.init_app(app, resources={r"/chat/*": {"origins": "*"}})
app.after_request(create_cors_after_request('/chat'))
# Session setup
# redis_config = app.config['SESSION_REDIS']
# redis_client = Redis(host=redis_config['host'],
# port=redis_config['port'],
# db=redis_config['db'],
# password=redis_config['password']
# )
session.init_app(app)
def register_blueprints(app):
from .views.chat_views import chat_bp
app.register_blueprint(chat_bp)

View File

@@ -0,0 +1,52 @@
from flask_jwt_extended import verify_jwt_in_request, get_jwt_identity, verify_jwt_in_request, decode_token
from flask_socketio import emit, disconnect
from flask import current_app, request
from common.extensions import socketio
@socketio.on('connect')
def handle_connect():
try:
# Extract token from the auth object
token = request.args.get('token')
if not token:
raise Exception("Missing Authorization Token")
current_app.logger.debug(f'SocketIO: Received token: {token}')
# Verify token
decoded_token = decode_token(token.split(" ")[1]) # Split to remove "Bearer " prefix
tenant_id = decoded_token["identity"]["tenant_id"]
current_app.logger.info(f'SocketIO: Tenant {decoded_token["identity"]["tenant_id"]} connected')
# communicate connection to client
emit('connect', {'status': 'Connected', 'tenant_id': tenant_id})
except Exception as e:
current_app.logger.error(f'SocketIO: Connection failed: {e}')
# communicate connection problem to client
emit('connect', {'status': 'Connection Failed'})
disconnect()
@socketio.on('disconnect')
def handle_disconnect():
current_app.logger.debug('SocketIO: Client disconnected')
@socketio.on('user_message')
def handle_message(data):
try:
current_app.logger.debug(f"SocketIO: Received message from tenant {data['tenantId']}: {data['message']}")
verify_jwt_in_request()
current_tenant = get_jwt_identity()
print(f'Tenant {current_tenant["tenant_id"]} sent a message: {data}')
# Store interaction in the database
response = {
'tenantId': data['tenantId'],
'message': 'This is a bot response. Actual implementation still required.',
'messageId': 'bot-message-id',
'algorithm': 'alg1'
}
current_app.logger.debug(f"SocketIO: Bot response: {response}")
emit('bot_response', response, broadcast=True)
except Exception as e:
current_app.logger.error(f'SocketIO: Message handling failed: {e}')
disconnect()

View File

@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html>
<head>
<title>Chat Client</title>
<script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
</head>
<body>
<h1>Chat Client</h1>
<button onclick="registerClient()">Register Client</button>
<div>
<input type="text" id="message" placeholder="Enter your message">
<button onclick="sendMessage()">Send Message</button>
</div>
<div id="messages"></div>
<script>
let socket;
async function registerClient() {
const tenantId = '1';
const apiKey = 'EveAI-CHAT-8553-7987-2800-9115-6454';
const response = await fetch('http://127.0.0.1:5001/chat/register_client', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ tenant_id: tenantId, api_key: apiKey })
});
if (response.ok) {
const data = await response.json();
const token = data.token;
socket = io('http://127.0.0.1:5001/chat', {
auth: {
token: `Bearer ${token}`
}
});
socket.on('connect', () => {
console.log('Connected to server');
});
socket.on('response', (msg) => {
const messagesDiv = document.getElementById('messages');
const messageElement = document.createElement('div');
messageElement.innerText = `Response: ${msg.data}`;
messagesDiv.appendChild(messageElement);
});
} else {
console.error('Registration failed');
}
}
function sendMessage() {
const message = document.getElementById('message').value;
if (socket) {
socket.emit('message', { content: message });
} else {
console.error('Socket not connected');
}
}
</script>
</body>
</html>

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chat Client</title>
<link rel="stylesheet" href="css/eveai-chat-style.css">
<script src="js/eveai-sdk.js" defer></script>
<script src="js/eveai-chat-widget.js" defer></script>
</head>
<body>
<div id="chat-container"></div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const eveAI = new EveAI('tenant-id', 'EVEAI-CHAT-xxxx-xxxx-xxxx-xxxx-xxxx');
eveAI.initializeChat('chat-container');
});
</script>
</body>
</html>

View File

@@ -1,34 +1,75 @@
# from . import user_bp
import uuid
from datetime import datetime as dt, timezone as tz
from flask import request, redirect, url_for, flash, render_template, Blueprint, session, current_app
from flask import request, redirect, url_for, render_template, Blueprint, session, current_app, jsonify
from flask_security import hash_password, roles_required, roles_accepted
from sqlalchemy.exc import SQLAlchemyError
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
from flask_socketio import emit, join_room, leave_room
import ast
from common.models.user import User, Tenant
from common.models.interaction import ChatSession, Interaction, InteractionEmbedding
from common.models.document import Embedding
from common.extensions import db, socketio
from common.extensions import db, socketio, kms_client
from common.utils.database import Database
chat_bp = Blueprint('chat_bp', __name__, url_prefix='/chat')
@chat_bp.route('/', methods=['GET', 'POST'])
def chat():
return render_template('chat.html')
@chat_bp.route('/register_client', methods=['POST'])
def register_client():
tenant_id = request.json.get('tenant_id')
api_key = request.json.get('api_key')
# Validate tenant_id and api_key here (e.g., check against the database)
if validate_tenant(tenant_id, api_key):
access_token = create_access_token(identity={'tenant_id': tenant_id, 'api_key': api_key})
return jsonify({'token': access_token}), 200
else:
return jsonify({'message': 'Invalid credentials'}), 401
@chat.record_once
def on_register(state):
# TODO: write initialisation code when the blueprint is registered (only once)
# socketio.init_app(state.app)
pass
@socketio.on('connect', namespace='/chat')
@jwt_required()
def handle_connect():
current_tenant = get_jwt_identity()
print(f'Tenant {current_tenant["tenant_id"]} connected')
@socketio.on('message', namespace='/chat')
def handle_message(message):
# TODO: write message handling code to actually realise chat
# print('Received message:', message)
# socketio.emit('response', {'data': message}, namespace='/chat')
pass
@jwt_required()
def handle_message(data):
current_tenant = get_jwt_identity()
print(f'Tenant {current_tenant["tenant_id"]} sent a message: {data}')
# Store interaction in the database
emit('response', {'data': 'Message received'}, broadcast=True)
def validate_tenant(tenant_id, api_key):
tenant = Tenant.query.get_or_404(tenant_id)
encrypted_api_key = ast.literal_eval(tenant.encrypted_chat_api_key)
decrypted_api_key = kms_client.decrypt_api_key(encrypted_api_key)
return decrypted_api_key == api_key
# @chat_bp.route('/', methods=['GET', 'POST'])
# def chat():
# return render_template('chat.html')
#
#
# @chat.record_once
# def on_register(state):
# # TODO: write initialisation code when the blueprint is registered (only once)
# # socketio.init_app(state.app)
# pass
#
#
# @socketio.on('message', namespace='/chat')
# def handle_message(message):
# # TODO: write message handling code to actually realise chat
# # print('Received message:', message)
# # socketio.emit('response', {'data': message}, namespace='/chat')
# pass