mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-30 17:13:15 +00:00
feat: update authentication handling and integrate token usage across frontend and backend
This commit is contained in:
@@ -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})},
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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) &&
|
||||
|
||||
@@ -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<HTMLDivElement>(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();
|
||||
|
||||
@@ -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<string>('');
|
||||
|
||||
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();
|
||||
|
||||
@@ -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<string>('');
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
})
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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<string>('');
|
||||
const [modalState, setModalState] = useState<ActiveState>('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<ChunkType[]>([]);
|
||||
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');
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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<APIKeyData[]>([]);
|
||||
const [selectedChatbot, setSelectedChatbot] = useState<APIKeyData | null>();
|
||||
const [logs, setLogs] = useState<LogData[]>([]);
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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<string>(
|
||||
'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();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<File[]>(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);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user