diff --git a/application/api/answer/routes.py b/application/api/answer/routes.py index ebe1e971..ce06fda5 100644 --- a/application/api/answer/routes.py +++ b/application/api/answer/routes.py @@ -398,7 +398,7 @@ class Stream(Resource): decoded_token = request.decoded_token if not decoded_token: - return bad_request(401, "Unauthorized") + return make_response({"error": "Unauthorized"}, 401) logger.info( f"/stream - request_data: {data}, source: {source}", @@ -545,7 +545,7 @@ class Answer(Resource): decoded_token = request.decoded_token if not decoded_token: - return bad_request(401, "Unauthorized") + return make_response({"error": "Unauthorized"}, 401) prompt = get_prompt(prompt_id) @@ -723,6 +723,9 @@ class Search(Resource): user_api_key = None decoded_token = request.decoded_token + if not decoded_token: + return make_response({"error": "Unauthorized"}, 401) + logger.info( f"/api/answer - request_data: {data}, source: {source}", extra={"data": json.dumps({"request_data": data, "source": source})}, diff --git a/application/api/user/routes.py b/application/api/user/routes.py index 9d1bfadc..87f23cdc 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -67,6 +67,21 @@ def generate_date_range(start_date, end_date): } +def get_vector_store(source_id): + """ + Get the Vector Store + Args: + source_id (str): source id of the document + """ + + store = VectorCreator.create_vectorstore( + settings.VECTOR_STORE, + source_id=source_id, + embeddings_key=os.getenv("EMBEDDINGS_KEY"), + ) + return store + + @user_ns.route("/api/delete_conversation") class DeleteConversation(Resource): @api.doc( @@ -74,6 +89,9 @@ class DeleteConversation(Resource): params={"id": "The ID of the conversation to delete"}, ) def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) conversation_id = request.args.get("id") if not conversation_id: return make_response( @@ -81,7 +99,9 @@ class DeleteConversation(Resource): ) try: - conversations_collection.delete_one({"_id": ObjectId(conversation_id)}) + conversations_collection.delete_one( + {"_id": ObjectId(conversation_id), "user": decoded_token["sub"]} + ) except Exception as err: current_app.logger.error(f"Error deleting conversation: {err}") return make_response(jsonify({"success": False}), 400) @@ -94,7 +114,10 @@ class DeleteAllConversations(Resource): description="Deletes all conversations for a specific user", ) def get(self): - user_id = "local" + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user_id = decoded_token.get("sub") try: conversations_collection.delete_many({"user": user_id}) except Exception as err: @@ -176,6 +199,9 @@ class UpdateConversationName(Resource): description="Updates the name of a conversation", ) def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) data = request.get_json() required_fields = ["id", "name"] missing_fields = check_required_fields(data, required_fields) @@ -184,7 +210,8 @@ class UpdateConversationName(Resource): try: conversations_collection.update_one( - {"_id": ObjectId(data["id"])}, {"$set": {"name": data["name"]}} + {"_id": ObjectId(data["id"]), "user": decoded_token.get("sub")}, + {"$set": {"name": data["name"]}}, ) except Exception as err: current_app.logger.error(f"Error updating conversation name: {err}") @@ -219,6 +246,9 @@ class SubmitFeedback(Resource): description="Submit feedback for a conversation", ) def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) data = request.get_json() required_fields = ["feedback", "conversation_id", "question_index"] missing_fields = check_required_fields(data, required_fields) @@ -231,6 +261,7 @@ class SubmitFeedback(Resource): conversations_collection.update_one( { "_id": ObjectId(data["conversation_id"]), + "user": decoded_token.get("sub"), f"queries.{data['question_index']}": {"$exists": True}, }, { @@ -245,6 +276,7 @@ class SubmitFeedback(Resource): conversations_collection.update_one( { "_id": ObjectId(data["conversation_id"]), + "user": decoded_token.get("sub"), f"queries.{data['question_index']}": {"$exists": True}, }, { @@ -297,13 +329,18 @@ class DeleteOldIndexes(Resource): params={"source_id": "The source ID to delete"}, ) def get(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) source_id = request.args.get("source_id") if not source_id: return make_response( jsonify({"success": False, "message": "Missing required fields"}), 400 ) - doc = sources_collection.find_one({"_id": ObjectId(source_id), "user": "local"}) + doc = sources_collection.find_one( + {"_id": ObjectId(source_id), "user": decoded_token.get("sub")} + ) if not doc: return make_response(jsonify({"status": "not found"}), 404) try: @@ -341,6 +378,9 @@ class UploadFile(Resource): description="Uploads a file to be vectorized and indexed", ) def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) data = request.form files = request.files.getlist("file") required_fields = ["user", "name"] @@ -356,7 +396,7 @@ class UploadFile(Resource): 400, ) - user = secure_filename(request.form["user"]) + user = secure_filename(decoded_token.get("sub")) job_name = secure_filename(request.form["name"]) try: save_dir = os.path.join(current_dir, settings.UPLOAD_FOLDER, user, job_name) @@ -456,6 +496,9 @@ class UploadRemote(Resource): description="Uploads remote source for vectorization", ) def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) data = request.form required_fields = ["user", "source", "name", "data"] missing_fields = check_required_fields(data, required_fields) @@ -476,7 +519,7 @@ class UploadRemote(Resource): task = ingest_remote.delay( source_data=source_data, job_name=data["name"], - user=data["user"], + user=decoded_token.get("sub"), loader=data["source"], ) except Exception as err: @@ -532,7 +575,10 @@ class RedirectToSources(Resource): class PaginatedSources(Resource): @api.doc(description="Get document with pagination, sorting and filtering") def get(self): - user = "local" + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") sort_field = request.args.get("sort", "date") # Default to 'date' sort_order = request.args.get("order", "desc") # Default to 'desc' page = int(request.args.get("page", 1)) # Default to 1 @@ -597,7 +643,10 @@ class PaginatedSources(Resource): class CombinedJson(Resource): @api.doc(description="Provide JSON file with combined available indexes") def get(self): - user = "local" + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = [ { "name": "Default", @@ -698,13 +747,16 @@ class CreatePrompt(Resource): @api.expect(create_prompt_model) @api.doc(description="Create a new prompt") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) data = request.get_json() required_fields = ["content", "name"] missing_fields = check_required_fields(data, required_fields) if missing_fields: return missing_fields - user = "local" + user = decoded_token.get("sub") try: resp = prompts_collection.insert_one( @@ -726,7 +778,10 @@ class CreatePrompt(Resource): class GetPrompts(Resource): @api.doc(description="Get all prompts for the user") def get(self): - user = "local" + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") try: prompts = prompts_collection.find({"user": user}) list_prompts = [ @@ -754,6 +809,10 @@ class GetPrompts(Resource): class GetSinglePrompt(Resource): @api.doc(params={"id": "ID of the prompt"}, description="Get a single prompt by ID") def get(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") prompt_id = request.args.get("id") if not prompt_id: return make_response( @@ -784,7 +843,9 @@ class GetSinglePrompt(Resource): chat_reduce_strict = f.read() return make_response(jsonify({"content": chat_reduce_strict}), 200) - prompt = prompts_collection.find_one({"_id": ObjectId(prompt_id)}) + prompt = prompts_collection.find_one( + {"_id": ObjectId(prompt_id), "user": user} + ) except Exception as err: current_app.logger.error(f"Error retrieving prompt: {err}") return make_response(jsonify({"success": False}), 400) @@ -802,6 +863,10 @@ class DeletePrompt(Resource): @api.expect(delete_prompt_model) @api.doc(description="Delete a prompt by ID") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() required_fields = ["id"] missing_fields = check_required_fields(data, required_fields) @@ -809,7 +874,7 @@ class DeletePrompt(Resource): return missing_fields try: - prompts_collection.delete_one({"_id": ObjectId(data["id"])}) + prompts_collection.delete_one({"_id": ObjectId(data["id"]), "user": user}) except Exception as err: current_app.logger.error(f"Error deleting prompt: {err}") return make_response(jsonify({"success": False}), 400) @@ -833,6 +898,10 @@ class UpdatePrompt(Resource): @api.expect(update_prompt_model) @api.doc(description="Update an existing prompt") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() required_fields = ["id", "name", "content"] missing_fields = check_required_fields(data, required_fields) @@ -841,7 +910,7 @@ class UpdatePrompt(Resource): try: prompts_collection.update_one( - {"_id": ObjectId(data["id"])}, + {"_id": ObjectId(data["id"]), "user": user}, {"$set": {"name": data["name"], "content": data["content"]}}, ) except Exception as err: @@ -855,7 +924,10 @@ class UpdatePrompt(Resource): class GetApiKeys(Resource): @api.doc(description="Retrieve API keys for the user") def get(self): - user = "local" + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") try: keys = api_key_collection.find({"user": user}) list_keys = [] @@ -902,13 +974,16 @@ class CreateApiKey(Resource): @api.expect(create_api_key_model) @api.doc(description="Create a new API key") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() required_fields = ["name", "prompt_id", "chunks"] missing_fields = check_required_fields(data, required_fields) if missing_fields: return missing_fields - user = "local" try: key = str(uuid.uuid4()) new_api_key = { @@ -942,6 +1017,10 @@ class DeleteApiKey(Resource): @api.expect(delete_api_key_model) @api.doc(description="Delete an API key by ID") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() required_fields = ["id"] missing_fields = check_required_fields(data, required_fields) @@ -949,7 +1028,9 @@ class DeleteApiKey(Resource): return missing_fields try: - result = api_key_collection.delete_one({"_id": ObjectId(data["id"])}) + result = api_key_collection.delete_one( + {"_id": ObjectId(data["id"]), "user": user} + ) if result.deleted_count == 0: return {"success": False, "message": "API Key not found"}, 404 except Exception as err: @@ -976,6 +1057,10 @@ class ShareConversation(Resource): @api.expect(share_conversation_model) @api.doc(description="Share a conversation") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() required_fields = ["conversation_id"] missing_fields = check_required_fields(data, required_fields) @@ -987,8 +1072,6 @@ class ShareConversation(Resource): return make_response( jsonify({"success": False, "message": "isPromptable is required"}), 400 ) - - user = data.get("user", "local") conversation_id = data["conversation_id"] try: @@ -1238,13 +1321,19 @@ class GetMessageAnalytics(Resource): @api.expect(get_message_analytics_model) @api.doc(description="Get message analytics based on filter option") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() api_key_id = data.get("api_key_id") filter_option = data.get("filter_option", "last_30_days") try: api_key = ( - api_key_collection.find_one({"_id": ObjectId(api_key_id)})["key"] + api_key_collection.find_one( + {"_id": ObjectId(api_key_id), "user": user} + )["key"] if api_key_id else None ) @@ -1280,8 +1369,13 @@ class GetMessageAnalytics(Resource): try: pipeline = [ - # Initial match for API key if provided - {"$match": {"api_key": api_key if api_key else {"$exists": False}}}, + # Initial match for user and API key if provided + { + "$match": { + "user": user, + "api_key": api_key if api_key else {"$exists": False}, + } + }, {"$unwind": "$queries"}, # Match queries within the time range { @@ -1352,13 +1446,19 @@ class GetTokenAnalytics(Resource): @api.expect(get_token_analytics_model) @api.doc(description="Get token analytics data") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() api_key_id = data.get("api_key_id") filter_option = data.get("filter_option", "last_30_days") try: api_key = ( - api_key_collection.find_one({"_id": ObjectId(api_key_id)})["key"] + api_key_collection.find_one( + {"_id": ObjectId(api_key_id), "user": user} + )["key"] if api_key_id else None ) @@ -1440,6 +1540,7 @@ class GetTokenAnalytics(Resource): try: match_stage = { "$match": { + "user_id": user, "timestamp": {"$gte": start_date, "$lte": end_date}, } } @@ -1506,13 +1607,19 @@ class GetFeedbackAnalytics(Resource): @api.expect(get_feedback_analytics_model) @api.doc(description="Get feedback analytics data") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() api_key_id = data.get("api_key_id") filter_option = data.get("filter_option", "last_30_days") try: api_key = ( - api_key_collection.find_one({"_id": ObjectId(api_key_id)})["key"] + api_key_collection.find_one( + {"_id": ObjectId(api_key_id), "user": user} + )["key"] if api_key_id else None ) @@ -1666,6 +1773,10 @@ class GetUserLogs(Resource): @api.expect(get_user_logs_model) @api.doc(description="Get user logs with pagination") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() page = int(data.get("page", 1)) api_key_id = data.get("api_key_id") @@ -1682,7 +1793,7 @@ class GetUserLogs(Resource): current_app.logger.error(f"Error getting API key: {err}") return make_response(jsonify({"success": False}), 400) - query = {} + query = {"user": user} if api_key: query = {"api_key": api_key} @@ -1740,6 +1851,10 @@ class ManageSync(Resource): @api.expect(manage_sync_model) @api.doc(description="Manage sync frequency for sources") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() required_fields = ["source_id", "sync_frequency"] missing_fields = check_required_fields(data, required_fields) @@ -1759,7 +1874,7 @@ class ManageSync(Resource): sources_collection.update_one( { "_id": ObjectId(source_id), - "user": "local", + "user": user, }, update_data, ) @@ -1836,7 +1951,10 @@ class GetTools(Resource): @api.doc(description="Get tools created by a user") def get(self): try: - user = "local" + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") tools = user_tools_collection.find({"user": user}) user_tools = [] for tool in tools: @@ -1879,6 +1997,10 @@ class CreateTool(Resource): ) @api.doc(description="Create a new tool") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() required_fields = [ "name", @@ -1892,7 +2014,6 @@ class CreateTool(Resource): if missing_fields: return missing_fields - user = "local" transformed_actions = [] for action in data["actions"]: action["active"] = True @@ -1943,6 +2064,10 @@ class UpdateTool(Resource): ) @api.doc(description="Update a tool by ID") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() required_fields = ["id"] missing_fields = check_required_fields(data, required_fields) @@ -1978,7 +2103,7 @@ class UpdateTool(Resource): update_data["status"] = data["status"] user_tools_collection.update_one( - {"_id": ObjectId(data["id"]), "user": "local"}, + {"_id": ObjectId(data["id"]), "user": user}, {"$set": update_data}, ) except Exception as err: @@ -2003,6 +2128,10 @@ class UpdateToolConfig(Resource): ) @api.doc(description="Update the configuration of a tool") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() required_fields = ["id", "config"] missing_fields = check_required_fields(data, required_fields) @@ -2011,7 +2140,7 @@ class UpdateToolConfig(Resource): try: user_tools_collection.update_one( - {"_id": ObjectId(data["id"])}, + {"_id": ObjectId(data["id"]), "user": user}, {"$set": {"config": data["config"]}}, ) except Exception as err: @@ -2038,6 +2167,10 @@ class UpdateToolActions(Resource): ) @api.doc(description="Update the actions of a tool") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() required_fields = ["id", "actions"] missing_fields = check_required_fields(data, required_fields) @@ -2046,7 +2179,7 @@ class UpdateToolActions(Resource): try: user_tools_collection.update_one( - {"_id": ObjectId(data["id"])}, + {"_id": ObjectId(data["id"]), "user": user}, {"$set": {"actions": data["actions"]}}, ) except Exception as err: @@ -2071,6 +2204,10 @@ class UpdateToolStatus(Resource): ) @api.doc(description="Update the status of a tool") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() required_fields = ["id", "status"] missing_fields = check_required_fields(data, required_fields) @@ -2079,7 +2216,7 @@ class UpdateToolStatus(Resource): try: user_tools_collection.update_one( - {"_id": ObjectId(data["id"])}, + {"_id": ObjectId(data["id"]), "user": user}, {"$set": {"status": data["status"]}}, ) except Exception as err: @@ -2099,6 +2236,10 @@ class DeleteTool(Resource): ) @api.doc(description="Delete a tool by ID") def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() required_fields = ["id"] missing_fields = check_required_fields(data, required_fields) @@ -2106,7 +2247,9 @@ class DeleteTool(Resource): return missing_fields try: - result = user_tools_collection.delete_one({"_id": ObjectId(data["id"])}) + result = user_tools_collection.delete_one( + {"_id": ObjectId(data["id"]), "user": user} + ) if result.deleted_count == 0: return {"success": False, "message": "Tool not found"}, 404 except Exception as err: @@ -2116,21 +2259,6 @@ class DeleteTool(Resource): return {"success": True}, 200 -def get_vector_store(source_id): - """ - Get the Vector Store - Args: - source_id (str): source id of the document - """ - - store = VectorCreator.create_vectorstore( - settings.VECTOR_STORE, - source_id=source_id, - embeddings_key=os.getenv("EMBEDDINGS_KEY"), - ) - return store - - @user_ns.route("/api/get_chunks") class GetChunks(Resource): @api.doc( @@ -2138,6 +2266,10 @@ class GetChunks(Resource): params={"id": "The document ID"}, ) def get(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") doc_id = request.args.get("id") page = int(request.args.get("page", 1)) per_page = int(request.args.get("per_page", 10)) @@ -2145,6 +2277,12 @@ class GetChunks(Resource): if not ObjectId.is_valid(doc_id): return make_response(jsonify({"error": "Invalid doc_id"}), 400) + doc = sources_collection.find_one({"_id": ObjectId(doc_id), "user": user}) + if not doc: + return make_response( + jsonify({"error": "Document not found or access denied"}), 404 + ) + try: store = get_vector_store(doc_id) chunks = store.get_chunks() @@ -2189,6 +2327,10 @@ class AddChunk(Resource): description="Adds a new chunk to the document", ) def post(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() required_fields = ["id", "text"] missing_fields = check_required_fields(data, required_fields) @@ -2202,6 +2344,12 @@ class AddChunk(Resource): if not ObjectId.is_valid(doc_id): return make_response(jsonify({"error": "Invalid doc_id"}), 400) + doc = sources_collection.find_one({"_id": ObjectId(doc_id), "user": user}) + if not doc: + return make_response( + jsonify({"error": "Document not found or access denied"}), 404 + ) + try: store = get_vector_store(doc_id) chunk_id = store.add_chunk(text, metadata) @@ -2221,12 +2369,22 @@ class DeleteChunk(Resource): params={"id": "The document ID", "chunk_id": "The ID of the chunk to delete"}, ) def delete(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") doc_id = request.args.get("id") chunk_id = request.args.get("chunk_id") if not ObjectId.is_valid(doc_id): return make_response(jsonify({"error": "Invalid doc_id"}), 400) + doc = sources_collection.find_one({"_id": ObjectId(doc_id), "user": user}) + if not doc: + return make_response( + jsonify({"error": "Document not found or access denied"}), 404 + ) + try: store = get_vector_store(doc_id) deleted = store.delete_chunk(chunk_id) @@ -2268,6 +2426,10 @@ class UpdateChunk(Resource): description="Updates an existing chunk in the document.", ) def put(self): + decoded_token = request.decoded_token + if not decoded_token: + return make_response(jsonify({"success": False}), 401) + user = decoded_token.get("sub") data = request.get_json() required_fields = ["id", "chunk_id"] missing_fields = check_required_fields(data, required_fields) @@ -2282,6 +2444,12 @@ class UpdateChunk(Resource): if not ObjectId.is_valid(doc_id): return make_response(jsonify({"error": "Invalid doc_id"}), 400) + doc = sources_collection.find_one({"_id": ObjectId(doc_id), "user": user}) + if not doc: + return make_response( + jsonify({"error": "Document not found or access denied"}), 404 + ) + try: store = get_vector_store(doc_id) chunks = store.get_chunks() diff --git a/application/core/settings.py b/application/core/settings.py index 2a78b3a0..74bffe53 100644 --- a/application/core/settings.py +++ b/application/core/settings.py @@ -10,7 +10,7 @@ current_dir = os.path.dirname( class Settings(BaseSettings): - AUTH_TYPE: Optional[str] = "simple_jwt" + AUTH_TYPE: Optional[str] = None LLM_NAME: str = "docsgpt" MODEL_NAME: Optional[str] = ( None # if LLM_NAME is openai, MODEL_NAME can be gpt-4 or gpt-3.5-turbo diff --git a/frontend/src/conversation/SharedConversation.tsx b/frontend/src/conversation/SharedConversation.tsx index b231ed1e..c44d6598 100644 --- a/frontend/src/conversation/SharedConversation.tsx +++ b/frontend/src/conversation/SharedConversation.tsx @@ -1,37 +1,35 @@ -import { Query } from './conversationModels'; import { Fragment, useEffect, useRef, useState } from 'react'; +import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; +import { useDispatch, useSelector } from 'react-redux'; import { useNavigate, useParams } from 'react-router-dom'; import conversationService from '../api/services/conversationService'; -import ConversationBubble from './ConversationBubble'; import Send from '../assets/send.svg'; import Spinner from '../assets/spinner.svg'; +import { selectToken } from '../preferences/preferenceSlice'; +import { AppDispatch } from '../store'; +import ConversationBubble from './ConversationBubble'; +import { Query } from './conversationModels'; import { - selectClientAPIKey, - setClientApiKey, - updateQuery, addQuery, fetchSharedAnswer, - selectStatus, -} from './sharedConversationSlice'; -import { setIdentifier, setFetchedData } from './sharedConversationSlice'; - -import { useDispatch } from 'react-redux'; -import { AppDispatch } from '../store'; - -import { + selectClientAPIKey, selectDate, - selectTitle, selectQueries, + selectStatus, + selectTitle, + setClientApiKey, + setFetchedData, + setIdentifier, + updateQuery, } from './sharedConversationSlice'; -import { useSelector } from 'react-redux'; -import { Helmet } from 'react-helmet'; export const SharedConversation = () => { const navigate = useNavigate(); const { identifier } = useParams(); //identifier is a uuid, not conversationId + const token = useSelector(selectToken); const queries = useSelector(selectQueries); const title = useSelector(selectTitle); const date = useSelector(selectDate); @@ -85,7 +83,7 @@ export const SharedConversation = () => { const fetchQueries = () => { identifier && conversationService - .getSharedConversation(identifier || '') + .getSharedConversation(identifier || '', token) .then((res) => { if (res.status === 404 || res.status === 400) navigate('/pagenotfound'); diff --git a/frontend/src/hooks/useDefaultDocument.ts b/frontend/src/hooks/useDefaultDocument.ts index 7f4b9812..a2642dc5 100644 --- a/frontend/src/hooks/useDefaultDocument.ts +++ b/frontend/src/hooks/useDefaultDocument.ts @@ -1,20 +1,22 @@ import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { getDocs } from '../preferences/preferenceApi'; import { Doc } from '../models/misc'; +import { getDocs } from '../preferences/preferenceApi'; import { selectSelectedDocs, + selectToken, setSelectedDocs, setSourceDocs, } from '../preferences/preferenceSlice'; export default function useDefaultDocument() { const dispatch = useDispatch(); + const token = useSelector(selectToken); const selectedDoc = useSelector(selectSelectedDocs); const fetchDocs = () => { - getDocs().then((data) => { + getDocs(token).then((data) => { dispatch(setSourceDocs(data)); if (!selectedDoc) Array.isArray(data) && diff --git a/frontend/src/modals/AddToolModal.tsx b/frontend/src/modals/AddToolModal.tsx index 07360eb7..94aebe1e 100644 --- a/frontend/src/modals/AddToolModal.tsx +++ b/frontend/src/modals/AddToolModal.tsx @@ -1,12 +1,14 @@ import React, { useRef } from 'react'; import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; import userService from '../api/services/userService'; +import Spinner from '../components/Spinner'; import { useOutsideAlerter } from '../hooks'; import { ActiveState } from '../models/misc'; +import { selectToken } from '../preferences/preferenceSlice'; import ConfigToolModal from './ConfigToolModal'; import { AvailableToolType } from './types'; -import Spinner from '../components/Spinner'; import WrapperComponent from './WrapperModal'; export default function AddToolModal({ @@ -23,6 +25,7 @@ export default function AddToolModal({ onToolAdded: (toolId: string) => void; }) { const { t } = useTranslation(); + const token = useSelector(selectToken); const modalRef = useRef(null); const [availableTools, setAvailableTools] = React.useState< AvailableToolType[] @@ -42,7 +45,7 @@ export default function AddToolModal({ const getAvailableTools = () => { setLoading(true); userService - .getAvailableTools() + .getAvailableTools(token) .then((res) => { return res.json(); }) @@ -55,14 +58,17 @@ export default function AddToolModal({ const handleAddTool = (tool: AvailableToolType) => { if (Object.keys(tool.configRequirements).length === 0) { userService - .createTool({ - name: tool.name, - displayName: tool.displayName, - description: tool.description, - config: {}, - actions: tool.actions, - status: true, - }) + .createTool( + { + name: tool.name, + displayName: tool.displayName, + description: tool.description, + config: {}, + actions: tool.actions, + status: true, + }, + token, + ) .then((res) => { if (res.status === 200) { return res.json(); diff --git a/frontend/src/modals/ConfigToolModal.tsx b/frontend/src/modals/ConfigToolModal.tsx index 04553677..a8a80aaf 100644 --- a/frontend/src/modals/ConfigToolModal.tsx +++ b/frontend/src/modals/ConfigToolModal.tsx @@ -1,11 +1,13 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; -import WrapperModal from './WrapperModal'; +import userService from '../api/services/userService'; import Input from '../components/Input'; import { ActiveState } from '../models/misc'; +import { selectToken } from '../preferences/preferenceSlice'; import { AvailableToolType } from './types'; -import userService from '../api/services/userService'; +import WrapperModal from './WrapperModal'; interface ConfigToolModalProps { modalState: ActiveState; @@ -21,18 +23,22 @@ export default function ConfigToolModal({ getUserTools, }: ConfigToolModalProps) { const { t } = useTranslation(); + const token = useSelector(selectToken); const [authKey, setAuthKey] = React.useState(''); const handleAddTool = (tool: AvailableToolType) => { userService - .createTool({ - name: tool.name, - displayName: tool.displayName, - description: tool.description, - config: { token: authKey }, - actions: tool.actions, - status: true, - }) + .createTool( + { + name: tool.name, + displayName: tool.displayName, + description: tool.description, + config: { token: authKey }, + actions: tool.actions, + status: true, + }, + token, + ) .then(() => { setModalState('INACTIVE'); getUserTools(); diff --git a/frontend/src/modals/CreateAPIKeyModal.tsx b/frontend/src/modals/CreateAPIKeyModal.tsx index 128bdff6..3d18f512 100644 --- a/frontend/src/modals/CreateAPIKeyModal.tsx +++ b/frontend/src/modals/CreateAPIKeyModal.tsx @@ -6,7 +6,7 @@ import userService from '../api/services/userService'; import Dropdown from '../components/Dropdown'; import Input from '../components/Input'; import { CreateAPIKeyModalProps, Doc } from '../models/misc'; -import { selectSourceDocs } from '../preferences/preferenceSlice'; +import { selectSourceDocs, selectToken } from '../preferences/preferenceSlice'; import WrapperModal from './WrapperModal'; const embeddingsName = @@ -18,6 +18,7 @@ export default function CreateAPIKeyModal({ createAPIKey, }: CreateAPIKeyModalProps) { const { t } = useTranslation(); + const token = useSelector(selectToken); const docs = useSelector(selectSourceDocs); const [APIKeyName, setAPIKeyName] = React.useState(''); @@ -60,7 +61,7 @@ export default function CreateAPIKeyModal({ React.useEffect(() => { const handleFetchPrompts = async () => { try { - const response = await userService.getPrompts(); + const response = await userService.getPrompts(token); if (!response.ok) { throw new Error('Failed to fetch prompts'); } diff --git a/frontend/src/modals/ShareConversationModal.tsx b/frontend/src/modals/ShareConversationModal.tsx index 44156761..69f7ea95 100644 --- a/frontend/src/modals/ShareConversationModal.tsx +++ b/frontend/src/modals/ShareConversationModal.tsx @@ -1,15 +1,20 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; -import { - selectSourceDocs, - selectSelectedDocs, - selectChunks, - selectPrompt, -} from '../preferences/preferenceSlice'; + +import conversationService from '../api/services/conversationService'; +import Spinner from '../assets/spinner.svg'; import Dropdown from '../components/Dropdown'; import { Doc } from '../models/misc'; -import Spinner from '../assets/spinner.svg'; +import { + selectChunks, + selectPrompt, + selectSelectedDocs, + selectSourceDocs, + selectToken, +} from '../preferences/preferenceSlice'; +import WrapperModal from './WrapperModal'; + const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; const embeddingsName = import.meta.env.VITE_EMBEDDINGS_NAME || @@ -17,9 +22,6 @@ const embeddingsName = type StatusType = 'loading' | 'idle' | 'fetched' | 'failed'; -import conversationService from '../api/services/conversationService'; -import WrapperModal from './WrapperModal'; - export const ShareConversationModal = ({ close, conversationId, @@ -28,6 +30,7 @@ export const ShareConversationModal = ({ conversationId: string; }) => { const { t } = useTranslation(); + const token = useSelector(selectToken); const domain = window.location.origin; @@ -85,7 +88,7 @@ export const ShareConversationModal = ({ sourcePath && (payload.source = sourcePath.value); } conversationService - .shareConversation(isPromptable, payload) + .shareConversation(isPromptable, payload, token) .then((res) => { return res.json(); }) diff --git a/frontend/src/settings/APIKeys.tsx b/frontend/src/settings/APIKeys.tsx index 6d76d7e8..5fcd1192 100644 --- a/frontend/src/settings/APIKeys.tsx +++ b/frontend/src/settings/APIKeys.tsx @@ -1,17 +1,20 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; import userService from '../api/services/userService'; import Trash from '../assets/trash.svg'; -import CreateAPIKeyModal from '../modals/CreateAPIKeyModal'; -import SaveAPIKeyModal from '../modals/SaveAPIKeyModal'; -import ConfirmationModal from '../modals/ConfirmationModal'; -import { APIKeyData } from './types'; import SkeletonLoader from '../components/SkeletonLoader'; import { useLoaderState } from '../hooks'; +import ConfirmationModal from '../modals/ConfirmationModal'; +import CreateAPIKeyModal from '../modals/CreateAPIKeyModal'; +import SaveAPIKeyModal from '../modals/SaveAPIKeyModal'; +import { selectToken } from '../preferences/preferenceSlice'; +import { APIKeyData } from './types'; export default function APIKeys() { const { t } = useTranslation(); + const token = useSelector(selectToken); const [isCreateModalOpen, setCreateModal] = useState(false); const [isSaveKeyModalOpen, setSaveKeyModal] = useState(false); const [newKey, setNewKey] = useState(''); @@ -25,7 +28,7 @@ export default function APIKeys() { const handleFetchKeys = async () => { setLoading(true); try { - const response = await userService.getAPIKeys(); + const response = await userService.getAPIKeys(token); if (!response.ok) { throw new Error('Failed to fetch API Keys'); } @@ -41,7 +44,7 @@ export default function APIKeys() { const handleDeleteKey = (id: string) => { setLoading(true); userService - .deleteAPIKey({ id }) + .deleteAPIKey({ id }, token) .then((response) => { if (!response.ok) { throw new Error('Failed to delete API Key'); @@ -71,7 +74,7 @@ export default function APIKeys() { }) => { setLoading(true); userService - .createAPIKey(payload) + .createAPIKey(payload, token) .then((response) => { if (!response.ok) { throw new Error('Failed to create API Key'); diff --git a/frontend/src/settings/Analytics.tsx b/frontend/src/settings/Analytics.tsx index 53538833..9963c627 100644 --- a/frontend/src/settings/Analytics.tsx +++ b/frontend/src/settings/Analytics.tsx @@ -1,5 +1,3 @@ -import React, { useState, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; import { BarElement, CategoryScale, @@ -9,18 +7,21 @@ import { Title, Tooltip, } from 'chart.js'; +import React, { useEffect, useState } from 'react'; import { Bar } from 'react-chartjs-2'; +import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; import userService from '../api/services/userService'; import Dropdown from '../components/Dropdown'; +import SkeletonLoader from '../components/SkeletonLoader'; +import { useLoaderState } from '../hooks'; +import { selectToken } from '../preferences/preferenceSlice'; import { htmlLegendPlugin } from '../utils/chartUtils'; import { formatDate } from '../utils/dateTimeUtils'; import { APIKeyData } from './types'; -import { useLoaderState } from '../hooks'; import type { ChartData } from 'chart.js'; -import SkeletonLoader from '../components/SkeletonLoader'; - ChartJS.register( CategoryScale, LinearScale, @@ -32,6 +33,7 @@ ChartJS.register( export default function Analytics() { const { t } = useTranslation(); + const token = useSelector(selectToken); const filterOptions = [ { label: t('settings.analytics.filterOptions.hour'), value: 'last_hour' }, @@ -97,7 +99,7 @@ export default function Analytics() { const fetchChatbots = async () => { setLoadingChatbots(true); try { - const response = await userService.getAPIKeys(); + const response = await userService.getAPIKeys(token); if (!response.ok) { throw new Error('Failed to fetch Chatbots'); } @@ -113,10 +115,13 @@ export default function Analytics() { const fetchMessagesData = async (chatbot_id?: string, filter?: string) => { setLoadingMessages(true); try { - const response = await userService.getMessageAnalytics({ - api_key_id: chatbot_id, - filter_option: filter, - }); + const response = await userService.getMessageAnalytics( + { + api_key_id: chatbot_id, + filter_option: filter, + }, + token, + ); if (!response.ok) { throw new Error('Failed to fetch analytics data'); } @@ -132,10 +137,13 @@ export default function Analytics() { const fetchTokenData = async (chatbot_id?: string, filter?: string) => { setLoadingTokens(true); try { - const response = await userService.getTokenAnalytics({ - api_key_id: chatbot_id, - filter_option: filter, - }); + const response = await userService.getTokenAnalytics( + { + api_key_id: chatbot_id, + filter_option: filter, + }, + token, + ); if (!response.ok) { throw new Error('Failed to fetch analytics data'); } @@ -151,10 +159,13 @@ export default function Analytics() { const fetchFeedbackData = async (chatbot_id?: string, filter?: string) => { setLoadingFeedback(true); try { - const response = await userService.getFeedbackAnalytics({ - api_key_id: chatbot_id, - filter_option: filter, - }); + const response = await userService.getFeedbackAnalytics( + { + api_key_id: chatbot_id, + filter_option: filter, + }, + token, + ); if (!response.ok) { throw new Error('Failed to fetch analytics data'); } diff --git a/frontend/src/settings/Documents.tsx b/frontend/src/settings/Documents.tsx index 1203758d..3a5e077e 100644 --- a/frontend/src/settings/Documents.tsx +++ b/frontend/src/settings/Documents.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import userService from '../api/services/userService'; import ArrowLeft from '../assets/arrow-left.svg'; @@ -21,6 +21,7 @@ import ConfirmationModal from '../modals/ConfirmationModal'; import { ActiveState, Doc, DocumentsProps } from '../models/misc'; import { getDocs, getDocsWithPagination } from '../preferences/preferenceApi'; import { + selectToken, setPaginatedDocuments, setSourceDocs, } from '../preferences/preferenceSlice'; @@ -50,6 +51,7 @@ export default function Documents({ }: DocumentsProps) { const { t } = useTranslation(); const dispatch = useDispatch(); + const token = useSelector(selectToken); const [searchTerm, setSearchTerm] = useState(''); const [modalState, setModalState] = useState('INACTIVE'); @@ -103,6 +105,7 @@ export default function Documents({ page, rowsPerPg, searchTerm, + token, ) .then((data) => { dispatch(setPaginatedDocuments(data ? data.docs : [])); @@ -119,9 +122,9 @@ export default function Documents({ const handleManageSync = (doc: Doc, sync_frequency: string) => { setLoading(true); userService - .manageSync({ source_id: doc.id, sync_frequency }) + .manageSync({ source_id: doc.id, sync_frequency }, token) .then(() => { - return getDocs(); + return getDocs(token); }) .then((data) => { dispatch(setSourceDocs(data)); @@ -130,6 +133,8 @@ export default function Documents({ sortOrder, currentPage, rowsPerPage, + searchTerm, + token, ); }) .then((paginatedData) => { @@ -389,6 +394,7 @@ function DocumentChunks({ handleGoBack: () => void; }) { const { t } = useTranslation(); + const token = useSelector(selectToken); const [isDarkTheme] = useDarkTheme(); const [paginatedChunks, setPaginatedChunks] = useState([]); const [page, setPage] = useState(1); @@ -406,7 +412,7 @@ function DocumentChunks({ setLoading(true); try { userService - .getDocumentChunks(document.id ?? '', page, perPage) + .getDocumentChunks(document.id ?? '', page, perPage, token) .then((response) => { if (!response.ok) { setLoading(false); @@ -431,13 +437,16 @@ function DocumentChunks({ const handleAddChunk = (title: string, text: string) => { try { userService - .addChunk({ - id: document.id ?? '', - text: text, - metadata: { - title: title, + .addChunk( + { + id: document.id ?? '', + text: text, + metadata: { + title: title, + }, }, - }) + token, + ) .then((response) => { if (!response.ok) { throw new Error('Failed to add chunk'); @@ -452,14 +461,17 @@ function DocumentChunks({ const handleUpdateChunk = (title: string, text: string, chunk: ChunkType) => { try { userService - .updateChunk({ - id: document.id ?? '', - chunk_id: chunk.doc_id, - text: text, - metadata: { - title: title, + .updateChunk( + { + id: document.id ?? '', + chunk_id: chunk.doc_id, + text: text, + metadata: { + title: title, + }, }, - }) + token, + ) .then((response) => { if (!response.ok) { throw new Error('Failed to update chunk'); @@ -474,7 +486,7 @@ function DocumentChunks({ const handleDeleteChunk = (chunk: ChunkType) => { try { userService - .deleteChunk(document.id ?? '', chunk.doc_id) + .deleteChunk(document.id ?? '', chunk.doc_id, token) .then((response) => { if (!response.ok) { throw new Error('Failed to delete chunk'); diff --git a/frontend/src/settings/General.tsx b/frontend/src/settings/General.tsx index 86c4f4ab..ce63355b 100644 --- a/frontend/src/settings/General.tsx +++ b/frontend/src/settings/General.tsx @@ -8,6 +8,7 @@ import { useDarkTheme } from '../hooks'; import { selectChunks, selectPrompt, + selectToken, selectTokenLimit, setChunks, setModalStateDeleteConv, @@ -21,6 +22,7 @@ export default function General() { t, i18n: { changeLanguage }, } = useTranslation(); + const token = useSelector(selectToken); const themes = [ { value: 'Light', label: t('settings.general.light') }, { value: 'Dark', label: t('settings.general.dark') }, @@ -64,7 +66,7 @@ export default function General() { React.useEffect(() => { const handleFetchPrompts = async () => { try { - const response = await userService.getPrompts(); + const response = await userService.getPrompts(token); if (!response.ok) { throw new Error('Failed to fetch prompts'); } diff --git a/frontend/src/settings/Logs.tsx b/frontend/src/settings/Logs.tsx index 49a2e7a5..45c2a62b 100644 --- a/frontend/src/settings/Logs.tsx +++ b/frontend/src/settings/Logs.tsx @@ -1,5 +1,6 @@ -import React, { useState, useEffect, useRef, useCallback } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; import userService from '../api/services/userService'; import ChevronRight from '../assets/chevron-right.svg'; @@ -7,10 +8,12 @@ import CopyButton from '../components/CopyButton'; import Dropdown from '../components/Dropdown'; import SkeletonLoader from '../components/SkeletonLoader'; import { useLoaderState } from '../hooks'; +import { selectToken } from '../preferences/preferenceSlice'; import { APIKeyData, LogData } from './types'; export default function Logs() { const { t } = useTranslation(); + const token = useSelector(selectToken); const [chatbots, setChatbots] = useState([]); const [selectedChatbot, setSelectedChatbot] = useState(); const [logs, setLogs] = useState([]); @@ -22,7 +25,7 @@ export default function Logs() { const fetchChatbots = async () => { setLoadingChatbots(true); try { - const response = await userService.getAPIKeys(); + const response = await userService.getAPIKeys(token); if (!response.ok) { throw new Error('Failed to fetch Chatbots'); } @@ -38,11 +41,14 @@ export default function Logs() { const fetchLogs = async () => { setLoadingLogs(true); try { - const response = await userService.getLogs({ - page: page, - api_key_id: selectedChatbot?.id, - page_size: 10, - }); + const response = await userService.getLogs( + { + page: page, + api_key_id: selectedChatbot?.id, + page_size: 10, + }, + token, + ); if (!response.ok) { throw new Error('Failed to fetch logs'); } diff --git a/frontend/src/settings/Prompts.tsx b/frontend/src/settings/Prompts.tsx index ed1d82d7..9a707f60 100644 --- a/frontend/src/settings/Prompts.tsx +++ b/frontend/src/settings/Prompts.tsx @@ -1,9 +1,11 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; import userService from '../api/services/userService'; import Dropdown from '../components/Dropdown'; import { ActiveState, PromptProps } from '../models/misc'; +import { selectToken } from '../preferences/preferenceSlice'; import PromptsModal from '../preferences/PromptsModal'; export default function Prompts({ @@ -24,6 +26,7 @@ export default function Prompts({ setEditPromptName(name); onSelectPrompt(name, id, type); }; + const token = useSelector(selectToken); const [newPromptName, setNewPromptName] = React.useState(''); const [newPromptContent, setNewPromptContent] = React.useState(''); const [editPromptName, setEditPromptName] = React.useState(''); @@ -42,10 +45,13 @@ export default function Prompts({ const handleAddPrompt = async () => { try { - const response = await userService.createPrompt({ - name: newPromptName, - content: newPromptContent, - }); + const response = await userService.createPrompt( + { + name: newPromptName, + content: newPromptContent, + }, + token, + ); if (!response.ok) { throw new Error('Failed to add prompt'); } @@ -68,7 +74,7 @@ export default function Prompts({ const handleDeletePrompt = (id: string) => { setPrompts(prompts.filter((prompt) => prompt.id !== id)); userService - .deletePrompt({ id }) + .deletePrompt({ id }, token) .then((response) => { if (!response.ok) { throw new Error('Failed to delete prompt'); @@ -84,7 +90,7 @@ export default function Prompts({ const handleFetchPromptContent = async (id: string) => { try { - const response = await userService.getSinglePrompt(id); + const response = await userService.getSinglePrompt(id, token); if (!response.ok) { throw new Error('Failed to fetch prompt content'); } @@ -97,11 +103,14 @@ export default function Prompts({ const handleSaveChanges = (id: string, type: string) => { userService - .updatePrompt({ - id: id, - name: editPromptName, - content: editPromptContent, - }) + .updatePrompt( + { + id: id, + name: editPromptName, + content: editPromptContent, + }, + token, + ) .then((response) => { if (!response.ok) { throw new Error('Failed to update prompt'); diff --git a/frontend/src/settings/ToolConfig.tsx b/frontend/src/settings/ToolConfig.tsx index 0be3a776..cceb27df 100644 --- a/frontend/src/settings/ToolConfig.tsx +++ b/frontend/src/settings/ToolConfig.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { useSelector } from 'react-redux'; import userService from '../api/services/userService'; import ArrowLeft from '../assets/arrow-left.svg'; @@ -9,6 +10,7 @@ import Dropdown from '../components/Dropdown'; import Input from '../components/Input'; import AddActionModal from '../modals/AddActionModal'; import { ActiveState } from '../models/misc'; +import { selectToken } from '../preferences/preferenceSlice'; import { APIActionType, APIToolType, UserToolType } from './types'; export default function ToolConfig({ @@ -20,6 +22,7 @@ export default function ToolConfig({ setTool: (tool: UserToolType | APIToolType) => void; handleGoBack: () => void; }) { + const token = useSelector(selectToken); const [authKey, setAuthKey] = React.useState( 'token' in tool.config ? tool.config.token : '', ); @@ -56,22 +59,25 @@ export default function ToolConfig({ const handleSaveChanges = () => { userService - .updateTool({ - id: tool.id, - name: tool.name, - displayName: tool.displayName, - description: tool.description, - config: tool.name === 'api_tool' ? tool.config : { token: authKey }, - actions: 'actions' in tool ? tool.actions : [], - status: tool.status, - }) + .updateTool( + { + id: tool.id, + name: tool.name, + displayName: tool.displayName, + description: tool.description, + config: tool.name === 'api_tool' ? tool.config : { token: authKey }, + actions: 'actions' in tool ? tool.actions : [], + status: tool.status, + }, + token, + ) .then(() => { handleGoBack(); }); }; const handleDelete = () => { - userService.deleteTool({ id: tool.id }).then(() => { + userService.deleteTool({ id: tool.id }, token).then(() => { handleGoBack(); }); }; diff --git a/frontend/src/settings/Tools.tsx b/frontend/src/settings/Tools.tsx index e697bb96..222392a2 100644 --- a/frontend/src/settings/Tools.tsx +++ b/frontend/src/settings/Tools.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; import userService from '../api/services/userService'; import CogwheelIcon from '../assets/cogwheel.svg'; @@ -10,11 +11,13 @@ import Spinner from '../components/Spinner'; import { useDarkTheme } from '../hooks'; import AddToolModal from '../modals/AddToolModal'; import { ActiveState } from '../models/misc'; +import { selectToken } from '../preferences/preferenceSlice'; import ToolConfig from './ToolConfig'; import { APIToolType, UserToolType } from './types'; export default function Tools() { const { t } = useTranslation(); + const token = useSelector(selectToken); const [isDarkTheme] = useDarkTheme(); const [searchTerm, setSearchTerm] = React.useState(''); const [addToolModalState, setAddToolModalState] = @@ -28,7 +31,7 @@ export default function Tools() { const getUserTools = () => { setLoading(true); userService - .getUserTools() + .getUserTools(token) .then((res) => { return res.json(); }) @@ -44,7 +47,7 @@ export default function Tools() { const updateToolStatus = (toolId: string, newStatus: boolean) => { userService - .updateToolStatus({ id: toolId, status: newStatus }) + .updateToolStatus({ id: toolId, status: newStatus }, token) .then(() => { setUserTools((prevTools) => prevTools.map((tool) => @@ -68,7 +71,7 @@ export default function Tools() { const handleToolAdded = (toolId: string) => { userService - .getUserTools() + .getUserTools(token) .then((res) => res.json()) .then((data) => { const newTool = data.tools.find( diff --git a/frontend/src/settings/index.tsx b/frontend/src/settings/index.tsx index 918e4d15..cd504858 100644 --- a/frontend/src/settings/index.tsx +++ b/frontend/src/settings/index.tsx @@ -11,6 +11,7 @@ import { selectSourceDocs, setPaginatedDocuments, setSourceDocs, + selectToken, } from '../preferences/preferenceSlice'; import Analytics from './Analytics'; import APIKeys from './APIKeys'; @@ -28,6 +29,7 @@ export default function Settings() { null, ); + const token = useSelector(selectToken); const documents = useSelector(selectSourceDocs); const paginatedDocuments = useSelector(selectPaginatedDocuments); const updateWidgetScreenshot = (screenshot: File | null) => { @@ -41,7 +43,7 @@ export default function Settings() { const handleDeleteClick = (index: number, doc: Doc) => { userService - .deletePath(doc.id ?? '') + .deletePath(doc.id ?? '', token) .then((response) => { if (response.ok && documents) { if (paginatedDocuments) { diff --git a/frontend/src/upload/Upload.tsx b/frontend/src/upload/Upload.tsx index f97473d5..d44d65b2 100644 --- a/frontend/src/upload/Upload.tsx +++ b/frontend/src/upload/Upload.tsx @@ -9,21 +9,22 @@ import WebsiteCollect from '../assets/website_collect.svg'; import Dropdown from '../components/Dropdown'; import Input from '../components/Input'; import ToggleSwitch from '../components/ToggleSwitch'; +import WrapperModal from '../modals/WrapperModal'; import { ActiveState, Doc } from '../models/misc'; import { getDocs } from '../preferences/preferenceApi'; import { + selectSourceDocs, + selectToken, setSelectedDocs, setSourceDocs, - selectSourceDocs, } from '../preferences/preferenceSlice'; -import WrapperModal from '../modals/WrapperModal'; +import { IngestorDefaultConfigs } from '../upload/types/ingestor'; import { - IngestorType, + FormField, IngestorConfig, IngestorFormSchemas, - FormField, + IngestorType, } from './types/ingestor'; -import { IngestorDefaultConfigs } from '../upload/types/ingestor'; function Upload({ receivedFile = [], @@ -40,6 +41,7 @@ function Upload({ close: () => void; onSuccessfulUpload?: () => void; }) { + const token = useSelector(selectToken); const [docName, setDocName] = useState(receivedFile[0]?.name); const [remoteName, setRemoteName] = useState(''); const [files, setfiles] = useState(receivedFile); @@ -297,12 +299,12 @@ function Upload({ if ((progress?.percentage ?? 0) < 100) { timeoutID = setTimeout(() => { userService - .getTaskStatus(progress?.taskId as string) + .getTaskStatus(progress?.taskId as string, null) .then((data) => data.json()) .then((data) => { if (data.status == 'SUCCESS') { if (data.result.limited === true) { - getDocs().then((data) => { + getDocs(token).then((data) => { dispatch(setSourceDocs(data)); dispatch( setSelectedDocs( @@ -322,7 +324,7 @@ function Upload({ }, ); } else { - getDocs().then((data) => { + getDocs(token).then((data) => { dispatch(setSourceDocs(data)); const docIds = new Set( (Array.isArray(sourceDocs) && @@ -413,6 +415,7 @@ function Upload({ }, 3000); }; xhr.open('POST', `${apiHost + '/api/upload'}`); + xhr.setRequestHeader('Authorization', `Bearer ${token}`); xhr.send(formData); };