Skip to content

Commit 7fd377b

Browse files
committed
feat: implement JWT authentication and token management in frontend and backend
1 parent fe02bf9 commit 7fd377b

File tree

17 files changed

+453
-178
lines changed

17 files changed

+453
-178
lines changed

application/api/answer/routes.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ def save_conversation(
122122
source_log_docs,
123123
tool_calls,
124124
llm,
125+
decoded_token,
125126
index=None,
126127
api_key=None,
127128
):
@@ -180,7 +181,7 @@ def save_conversation(
180181

181182
completion = llm.gen(model=gpt_model, messages=messages_summary, max_tokens=30)
182183
conversation_data = {
183-
"user": "local",
184+
"user": decoded_token.get("sub"),
184185
"date": datetime.datetime.utcnow(),
185186
"name": completion,
186187
"queries": [
@@ -221,6 +222,7 @@ def complete_stream(
221222
retriever,
222223
conversation_id,
223224
user_api_key,
225+
decoded_token,
224226
isNoneDoc=False,
225227
index=None,
226228
should_save_conversation=True,
@@ -271,6 +273,7 @@ def complete_stream(
271273
source_log_docs,
272274
tool_calls,
273275
llm,
276+
decoded_token,
274277
index,
275278
api_key=user_api_key,
276279
)
@@ -286,7 +289,7 @@ def complete_stream(
286289
{
287290
"action": "stream_answer",
288291
"level": "info",
289-
"user": "local",
292+
"user": decoded_token.get("sub"),
290293
"api_key": user_api_key,
291294
"question": question,
292295
"response": response_full,
@@ -381,15 +384,21 @@ def post(self):
381384
source = {"active_docs": data_key.get("source")}
382385
retriever_name = data_key.get("retriever", retriever_name)
383386
user_api_key = data["api_key"]
387+
decoded_token = {"sub": data_key.get("user")}
384388

385389
elif "active_docs" in data:
386390
source = {"active_docs": data["active_docs"]}
387391
retriever_name = get_retriever(data["active_docs"]) or retriever_name
388392
user_api_key = None
393+
decoded_token = request.decoded_token
389394

390395
else:
391396
source = {}
392397
user_api_key = None
398+
decoded_token = request.decoded_token
399+
400+
if not decoded_token:
401+
return bad_request(401, "Unauthorized")
393402

394403
logger.info(
395404
f"/stream - request_data: {data}, source: {source}",
@@ -429,6 +438,7 @@ def post(self):
429438
retriever=retriever,
430439
conversation_id=conversation_id,
431440
user_api_key=user_api_key,
441+
decoded_token=decoded_token,
432442
isNoneDoc=data.get("isNoneDoc"),
433443
index=index,
434444
should_save_conversation=save_conv,
@@ -521,13 +531,21 @@ def post(self):
521531
source = {"active_docs": data_key.get("source")}
522532
retriever_name = data_key.get("retriever", retriever_name)
523533
user_api_key = data["api_key"]
534+
decoded_token = {"sub": data_key.get("user")}
535+
524536
elif "active_docs" in data:
525537
source = {"active_docs": data["active_docs"]}
526538
retriever_name = get_retriever(data["active_docs"]) or retriever_name
527539
user_api_key = None
540+
decoded_token = request.decoded_token
541+
528542
else:
529543
source = {}
530544
user_api_key = None
545+
decoded_token = request.decoded_token
546+
547+
if not decoded_token:
548+
return bad_request(401, "Unauthorized")
531549

532550
prompt = get_prompt(prompt_id)
533551

@@ -614,6 +632,7 @@ def post(self):
614632
source_log_docs,
615633
tool_calls,
616634
llm,
635+
decoded_token,
617636
api_key=user_api_key,
618637
)
619638
)
@@ -623,7 +642,7 @@ def post(self):
623642
{
624643
"action": "api_answer",
625644
"level": "info",
626-
"user": "local",
645+
"user": decoded_token.get("sub"),
627646
"api_key": user_api_key,
628647
"question": question,
629648
"response": response_full,
@@ -692,12 +711,17 @@ def post(self):
692711
chunks = int(data_key.get("chunks", 2))
693712
source = {"active_docs": data_key.get("source")}
694713
user_api_key = data["api_key"]
714+
decoded_token = {"sub": data_key.get("user")}
715+
695716
elif "active_docs" in data:
696717
source = {"active_docs": data["active_docs"]}
697718
user_api_key = None
719+
decoded_token = request.decoded_token
720+
698721
else:
699722
source = {}
700723
user_api_key = None
724+
decoded_token = request.decoded_token
701725

702726
logger.info(
703727
f"/api/answer - request_data: {data}, source: {source}",
@@ -723,7 +747,7 @@ def post(self):
723747
{
724748
"action": "api_search",
725749
"level": "info",
726-
"user": "local",
750+
"user": decoded_token.get("sub"),
727751
"api_key": user_api_key,
728752
"question": question,
729753
"sources": docs,

application/api/user/routes.py

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from application.agents.tools.tool_manager import ToolManager
1616

1717
from application.api.user.tasks import ingest, ingest_remote
18-
1918
from application.core.mongo_db import MongoDB
2019
from application.core.settings import settings
2120
from application.extensions import api
@@ -110,11 +109,18 @@ class GetConversations(Resource):
110109
description="Retrieve a list of the latest 30 conversations (excluding API key conversations)",
111110
)
112111
def get(self):
112+
decoded_token = request.decoded_token
113+
if not decoded_token:
114+
return make_response(jsonify({"success": False}), 401)
113115
try:
114-
conversations = conversations_collection.find(
115-
{"api_key": {"$exists": False}}
116-
).sort("date", -1).limit(30)
117-
116+
conversations = (
117+
conversations_collection.find(
118+
{"api_key": {"$exists": False}, "user": decoded_token.get("sub")}
119+
)
120+
.sort("date", -1)
121+
.limit(30)
122+
)
123+
118124
list_conversations = [
119125
{"id": str(conversation["_id"]), "name": conversation["name"]}
120126
for conversation in conversations
@@ -132,6 +138,9 @@ class GetSingleConversation(Resource):
132138
params={"id": "The conversation ID"},
133139
)
134140
def get(self):
141+
decoded_token = request.decoded_token
142+
if not decoded_token:
143+
return make_response(jsonify({"success": False}), 401)
135144
conversation_id = request.args.get("id")
136145
if not conversation_id:
137146
return make_response(
@@ -140,7 +149,7 @@ def get(self):
140149

141150
try:
142151
conversation = conversations_collection.find_one(
143-
{"_id": ObjectId(conversation_id)}
152+
{"_id": ObjectId(conversation_id), "user": decoded_token.get("sub")}
144153
)
145154
if not conversation:
146155
return make_response(jsonify({"status": "not found"}), 404)
@@ -227,7 +236,7 @@ def post(self):
227236
{
228237
"$unset": {
229238
f"queries.{data['question_index']}.feedback": "",
230-
f"queries.{data['question_index']}.feedback_timestamp": ""
239+
f"queries.{data['question_index']}.feedback_timestamp": "",
231240
}
232241
},
233242
)
@@ -240,8 +249,12 @@ def post(self):
240249
},
241250
{
242251
"$set": {
243-
f"queries.{data['question_index']}.feedback": data["feedback"],
244-
f"queries.{data['question_index']}.feedback_timestamp": datetime.datetime.now(datetime.timezone.utc)
252+
f"queries.{data['question_index']}.feedback": data[
253+
"feedback"
254+
],
255+
f"queries.{data['question_index']}.feedback_timestamp": datetime.datetime.now(
256+
datetime.timezone.utc
257+
),
245258
}
246259
},
247260
)
@@ -1211,7 +1224,13 @@ class GetMessageAnalytics(Resource):
12111224
required=False,
12121225
description="Filter option for analytics",
12131226
default="last_30_days",
1214-
enum=["last_hour", "last_24_hour", "last_7_days", "last_15_days", "last_30_days"],
1227+
enum=[
1228+
"last_hour",
1229+
"last_24_hour",
1230+
"last_7_days",
1231+
"last_15_days",
1232+
"last_30_days",
1233+
],
12151234
),
12161235
},
12171236
)
@@ -1244,35 +1263,30 @@ def post(self):
12441263
else:
12451264
if filter_option in ["last_7_days", "last_15_days", "last_30_days"]:
12461265
filter_days = (
1247-
6 if filter_option == "last_7_days"
1248-
else 14 if filter_option == "last_15_days"
1249-
else 29
1266+
6
1267+
if filter_option == "last_7_days"
1268+
else 14 if filter_option == "last_15_days" else 29
12501269
)
12511270
else:
12521271
return make_response(
12531272
jsonify({"success": False, "message": "Invalid option"}), 400
12541273
)
12551274
start_date = end_date - datetime.timedelta(days=filter_days)
12561275
start_date = start_date.replace(hour=0, minute=0, second=0, microsecond=0)
1257-
end_date = end_date.replace(hour=23, minute=59, second=59, microsecond=999999)
1276+
end_date = end_date.replace(
1277+
hour=23, minute=59, second=59, microsecond=999999
1278+
)
12581279
group_format = "%Y-%m-%d"
12591280

12601281
try:
12611282
pipeline = [
12621283
# Initial match for API key if provided
1263-
{
1264-
"$match": {
1265-
"api_key": api_key if api_key else {"$exists": False}
1266-
}
1267-
},
1284+
{"$match": {"api_key": api_key if api_key else {"$exists": False}}},
12681285
{"$unwind": "$queries"},
12691286
# Match queries within the time range
12701287
{
12711288
"$match": {
1272-
"queries.timestamp": {
1273-
"$gte": start_date,
1274-
"$lte": end_date
1275-
}
1289+
"queries.timestamp": {"$gte": start_date, "$lte": end_date}
12761290
}
12771291
},
12781292
# Group by formatted timestamp
@@ -1281,14 +1295,14 @@ def post(self):
12811295
"_id": {
12821296
"$dateToString": {
12831297
"format": group_format,
1284-
"date": "$queries.timestamp"
1298+
"date": "$queries.timestamp",
12851299
}
12861300
},
1287-
"count": {"$sum": 1}
1301+
"count": {"$sum": 1},
12881302
}
12891303
},
12901304
# Sort by timestamp
1291-
{"$sort": {"_id": 1}}
1305+
{"$sort": {"_id": 1}},
12921306
]
12931307

12941308
message_data = conversations_collection.aggregate(pipeline)
@@ -1511,11 +1525,21 @@ def post(self):
15111525
if filter_option == "last_hour":
15121526
start_date = end_date - datetime.timedelta(hours=1)
15131527
group_format = "%Y-%m-%d %H:%M:00"
1514-
date_field = {"$dateToString": {"format": group_format, "date": "$queries.feedback_timestamp"}}
1528+
date_field = {
1529+
"$dateToString": {
1530+
"format": group_format,
1531+
"date": "$queries.feedback_timestamp",
1532+
}
1533+
}
15151534
elif filter_option == "last_24_hour":
15161535
start_date = end_date - datetime.timedelta(hours=24)
15171536
group_format = "%Y-%m-%d %H:00"
1518-
date_field = {"$dateToString": {"format": group_format, "date": "$queries.feedback_timestamp"}}
1537+
date_field = {
1538+
"$dateToString": {
1539+
"format": group_format,
1540+
"date": "$queries.feedback_timestamp",
1541+
}
1542+
}
15191543
else:
15201544
if filter_option in ["last_7_days", "last_15_days", "last_30_days"]:
15211545
filter_days = (
@@ -1533,13 +1557,21 @@ def post(self):
15331557
hour=23, minute=59, second=59, microsecond=999999
15341558
)
15351559
group_format = "%Y-%m-%d"
1536-
date_field = {"$dateToString": {"format": group_format, "date": "$queries.feedback_timestamp"}}
1560+
date_field = {
1561+
"$dateToString": {
1562+
"format": group_format,
1563+
"date": "$queries.feedback_timestamp",
1564+
}
1565+
}
15371566

15381567
try:
15391568
match_stage = {
15401569
"$match": {
1541-
"queries.feedback_timestamp": {"$gte": start_date, "$lte": end_date},
1542-
"queries.feedback": {"$exists": True}
1570+
"queries.feedback_timestamp": {
1571+
"$gte": start_date,
1572+
"$lte": end_date,
1573+
},
1574+
"queries.feedback": {"$exists": True},
15431575
}
15441576
}
15451577
if api_key:

0 commit comments

Comments
 (0)