227 lines
9.3 KiB
Python
227 lines
9.3 KiB
Python
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('/<path:path>')
|
|
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) |