from quart import Quart, Response, request, send_from_directory, websocket from quart_auth import ( QuartAuth, current_user, AuthUser, login_user, logout_user ) import json import asyncio import nest_asyncio def start_server(app): server = Quart(__name__, static_folder='../frontend/build/web') server.secret_key = 'horde-secret' auth_manager = QuartAuth(server, mode="brearer") @server.after_request def after_request(response): response.headers.add('Access-Control-Allow-Origin', '*') response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization') response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS') return response @server.route('/') async def index(): return await send_from_directory(server.static_folder, 'index.html') @server.route('/') async def serve_static(path): return await send_from_directory(server.static_folder, path) @server.route('/user/login', methods=['POST']) async def login(): data = await request.get_json() if "username" not in data or "password" not in data: await websocket.send(json.dumps({'error': 'invalid arguments'})) return user_id = app.verify_user_credentials(data['username'], data['password']) if user_id == None: return jsonify({'error': "bad credentials"}), 401 token = auth_manager.dump_token(user_id) return {"token": token} @server.route('/user/signup', methods=['POST']) async def signup(): data = await request.get_json() user_id = None username = data['username'] email = data['email'] password = data['password'] user_id = app.create_new_user(username, email, password) if user_id is None: return {"error": "existing credentials"} app.generate_default_user_data(user_id) # token = auth_manager.dump_token(user_id) return {"userId": user_id} @server.websocket('/ws') async def ws(): print("incoming ws") # Authenticate before dedicating any resources auth_req = json.loads(await websocket.receive()) if 'token' in auth_req: try: user_id = auth_manager.load_token(auth_req['token']) except Exception as err: print("Token authentication failed: ", err) await websocket.send(json.dumps({'error': 'token authentication failed'})) return else: if "username" not in auth_req or "password" not in auth_req: print("Invalid auth request:", auth_req) await websocket.send(json.dumps({'error': 'invalid auth'})) return user_id = app.verify_user_credentials(auth_req['username'], auth_req['password']) if user_id is None: await websocket.send(json.dumps({'error': 'invalid credentials'})) return # TODO: print username instead, same in the finally block print("User logged in:", user_id) await websocket.send(json.dumps({'message': 'authentication successful'})) send_queue = asyncio.Queue() async def sender_task(): nonlocal send_queue while True: msg = await send_queue.get() await websocket.send(json.dumps(msg)) async def send(msg): await send_queue.put(msg) try: if 'remotename' in auth_req: app.add_remote_connection(user_id, authreq['remotename'], authreq['tools'], send_queue) else: app.add_user_connection(user_id, send_queue) asyncio.create_task(sender_task()) while True: req = json.loads(await websocket.receive()) if not 'action' in req: await send({'error': 'no action'}) continue action = req['action'] body = req.get('body', {}) nonce = req.get('nonce', None) if action == "ping": await send({'message': "pong"}) elif action == "create-agent": name = body['name'] agent_id = app.create_agent( user_id=user_id, name=name, agentname=body.get('agentname'), description=body.get('description'), picture=body.get('picture'), kind=body['kind'], data=body.get('data') ) await send({'message': "agent created", 'agentId': agent_id, 'nonce': nonce}) elif action == "update-agent": agent_id = body['agentId'] app.update_agent( agent_id=agent_id, name=body.get('name'), agentname=body.get('agentname'), description=body.get('description'), picture=body.get('picture'), data=body.get('data', {}) ) await send({'message': "agent updated", 'agentId': agent_id, 'nonce': nonce}) elif action == "get-agent-config-fields": await send({'fields': app.get_agent_config_fields(body['agentId']), 'nonce': nonce}) elif action == "get-agent-kinds": await send({'message': "agents retrieved", 'kinds': app.get_agent_kinds(), 'nonce': nonce}) elif action == "get-agent-kind-info": await send({'message': "agent kind retrieved", 'info': app.get_agent_kind_info(body['kind']), 'nonce': nonce}) elif action == "get-agents": await send({'message': "agents retrieved", 'agents': app.get_agents(user_id), 'nonce': nonce}) elif action == "create-chat": chat_id = app.create_chat( user_id=user_id, name=body['name'], description=body.get('description'), picture=body.get('picture'), members=body.get('members', []), ) await send({'message': "chat created", 'chatId': chat_id, 'nonce': nonce}) elif action == "get-chats": chats_data = app.get_chats(user_id) await send({'message': "chats retrieved", 'chats': chats_data, 'nonce': nonce}) elif action == "delete-chat": chat_id = app.delete_chat( chat_id=body['chatId'] ) await send({'message': "chat deleted", 'chatId': chat_id, 'nonce': nonce}) elif action == "get-chat-messages": messages_data = app.get_chat_messages( body['chatId'], earliest_index=body.get('earliestIndex', 0) ) await send({'message': "chat messages retrieved", 'messages': messages_data, 'nonce': nonce}) elif action == "send-chat-message": message = app.send_chat_message( chat_id=body['chatId'], thread_id=body.get('threadId'), content=body['content'], kind=body.get('kind'), subkind=body.get('subkind'), format=body.get('format'), ) await send({'message': "message sent", 'messageId': message['id'], 'nonce': nonce}) elif action == "update-chat-message": message = app.update_chat_message( message_id=body['messageId'], content=body['content'], kind=body.get('kind'), subkind=body.get('subkind'), format=body.get('format'), ) await send({'message': "message sent", 'messageId': body['messageId'], 'nonce': nonce}) elif action == "create-chat-thread": thread_id = app.create_chat_thread( chat_id=body['chatId'], name=body.get('name'), ) await send({'message': "chat thread created", 'threadId': thread_id, 'nonce': nonce}) elif action == "send-agent-prompt": response = await app.send_agent_prompt(body['agentId'], body['content']) await send({'message': "propmt sent", 'response': response, 'nonce': nonce}) elif action == "call-agent-action": result = await app.call_agent_action(body['agentId'], body['agentAction'], body['arguments']) await send({'message': "agent action executed", 'result': result, 'nonce': nonce}) else: await send({'error': 'unknown action: ' + action, 'nonce': nonce}) finally: print("User logged out:", user_id) app.remove_user_connection(user_id, send_queue) server.run(debug=True)