1
1
import logging
2
- import random
3
2
from datetime import datetime
4
3
from typing import Optional
5
4
17
16
logger = logging .getLogger (__name__ )
18
17
19
18
19
+ def sanitize_query (query : str | None ) -> str | None :
20
+ if query is None :
21
+ return query
22
+ query = query .strip ()[:100 ]
23
+ return (
24
+ query .replace ("\\ " , "\\ \\ " )
25
+ .replace ("%" , "\\ %" )
26
+ .replace ("_" , "\\ _" )
27
+ .replace ("[" , "\\ [" )
28
+ .replace ("]" , "\\ ]" )
29
+ .replace ("'" , "\\ '" )
30
+ .replace ('"' , '\\ "' )
31
+ .replace (";" , "\\ ;" )
32
+ .replace ("--" , "\\ --" )
33
+ .replace ("/*" , "\\ /*" )
34
+ .replace ("*/" , "\\ */" )
35
+ )
36
+
37
+
20
38
async def get_store_agents (
21
39
featured : bool = False ,
22
40
creator : str | None = None ,
@@ -29,29 +47,7 @@ async def get_store_agents(
29
47
logger .debug (
30
48
f"Getting store agents. featured={ featured } , creator={ creator } , sorted_by={ sorted_by } , search={ search_query } , category={ category } , page={ page } "
31
49
)
32
- sanitized_query = None
33
- # Sanitize and validate search query by escaping special characters
34
- if search_query is not None :
35
- sanitized_query = search_query .strip ()
36
- if not sanitized_query or len (sanitized_query ) > 100 : # Reasonable length limit
37
- raise backend .server .v2 .store .exceptions .DatabaseError (
38
- f"Invalid search query: len({ len (sanitized_query )} ) query: { search_query } "
39
- )
40
-
41
- # Escape special SQL characters
42
- sanitized_query = (
43
- sanitized_query .replace ("\\ " , "\\ \\ " )
44
- .replace ("%" , "\\ %" )
45
- .replace ("_" , "\\ _" )
46
- .replace ("[" , "\\ [" )
47
- .replace ("]" , "\\ ]" )
48
- .replace ("'" , "\\ '" )
49
- .replace ('"' , '\\ "' )
50
- .replace (";" , "\\ ;" )
51
- .replace ("--" , "\\ --" )
52
- .replace ("/*" , "\\ /*" )
53
- .replace ("*/" , "\\ */" )
54
- )
50
+ sanitized_query = sanitize_query (search_query )
55
51
56
52
where_clause = {}
57
53
if featured :
@@ -93,8 +89,8 @@ async def get_store_agents(
93
89
slug = agent .slug ,
94
90
agent_name = agent .agent_name ,
95
91
agent_image = agent .agent_image [0 ] if agent .agent_image else "" ,
96
- creator = agent .creator_username ,
97
- creator_avatar = agent .creator_avatar ,
92
+ creator = agent .creator_username or "Needs Profile" ,
93
+ creator_avatar = agent .creator_avatar or "" ,
98
94
sub_heading = agent .sub_heading ,
99
95
description = agent .description ,
100
96
runs = agent .runs ,
@@ -114,7 +110,7 @@ async def get_store_agents(
114
110
),
115
111
)
116
112
except Exception as e :
117
- logger .error (f"Error getting store agents: { str ( e ) } " )
113
+ logger .error (f"Error getting store agents: { e } " )
118
114
raise backend .server .v2 .store .exceptions .DatabaseError (
119
115
"Failed to fetch store agents"
120
116
) from e
@@ -156,7 +152,7 @@ async def get_store_agent_details(
156
152
except backend .server .v2 .store .exceptions .AgentNotFoundError :
157
153
raise
158
154
except Exception as e :
159
- logger .error (f"Error getting store agent details: { str ( e ) } " )
155
+ logger .error (f"Error getting store agent details: { e } " )
160
156
raise backend .server .v2 .store .exceptions .DatabaseError (
161
157
"Failed to fetch agent details"
162
158
) from e
@@ -270,7 +266,7 @@ async def get_store_creators(
270
266
),
271
267
)
272
268
except Exception as e :
273
- logger .error (f"Error getting store creators: { str ( e ) } " )
269
+ logger .error (f"Error getting store creators: { e } " )
274
270
raise backend .server .v2 .store .exceptions .DatabaseError (
275
271
"Failed to fetch store creators"
276
272
) from e
@@ -307,7 +303,7 @@ async def get_store_creator_details(
307
303
except backend .server .v2 .store .exceptions .CreatorNotFoundError :
308
304
raise
309
305
except Exception as e :
310
- logger .error (f"Error getting store creator details: { str ( e ) } " )
306
+ logger .error (f"Error getting store creator details: { e } " )
311
307
raise backend .server .v2 .store .exceptions .DatabaseError (
312
308
"Failed to fetch creator details"
313
309
) from e
@@ -366,7 +362,7 @@ async def get_store_submissions(
366
362
)
367
363
368
364
except Exception as e :
369
- logger .error (f"Error fetching store submissions: { str ( e ) } " )
365
+ logger .error (f"Error fetching store submissions: { e } " )
370
366
# Return empty response rather than exposing internal errors
371
367
return backend .server .v2 .store .model .StoreSubmissionsResponse (
372
368
submissions = [],
@@ -416,7 +412,7 @@ async def delete_store_submission(
416
412
return True
417
413
418
414
except Exception as e :
419
- logger .error (f"Error deleting store submission: { str ( e ) } " )
415
+ logger .error (f"Error deleting store submission: { e } " )
420
416
return False
421
417
422
418
@@ -539,7 +535,7 @@ async def create_store_submission(
539
535
):
540
536
raise
541
537
except prisma .errors .PrismaError as e :
542
- logger .error (f"Database error creating store submission: { str ( e ) } " )
538
+ logger .error (f"Database error creating store submission: { e } " )
543
539
raise backend .server .v2 .store .exceptions .DatabaseError (
544
540
"Failed to create store submission"
545
541
) from e
@@ -579,15 +575,15 @@ async def create_store_review(
579
575
)
580
576
581
577
except prisma .errors .PrismaError as e :
582
- logger .error (f"Database error creating store review: { str ( e ) } " )
578
+ logger .error (f"Database error creating store review: { e } " )
583
579
raise backend .server .v2 .store .exceptions .DatabaseError (
584
580
"Failed to create store review"
585
581
) from e
586
582
587
583
588
584
async def get_user_profile (
589
585
user_id : str ,
590
- ) -> backend .server .v2 .store .model .ProfileDetails :
586
+ ) -> backend .server .v2 .store .model .ProfileDetails | None :
591
587
logger .debug (f"Getting user profile for { user_id } " )
592
588
593
589
try :
@@ -596,25 +592,7 @@ async def get_user_profile(
596
592
)
597
593
598
594
if not profile :
599
- logger .warning (f"Profile not found for user { user_id } " )
600
- new_profile = await prisma .models .Profile .prisma ().create (
601
- data = prisma .types .ProfileCreateInput (
602
- userId = user_id ,
603
- name = "No Profile Data" ,
604
- username = f"{ random .choice (['happy' , 'clever' , 'swift' , 'bright' , 'wise' ])} -{ random .choice (['fox' , 'wolf' , 'bear' , 'eagle' , 'owl' ])} _{ random .randint (1000 ,9999 )} " .lower (),
605
- description = "No Profile Data" ,
606
- links = [],
607
- avatarUrl = "" ,
608
- )
609
- )
610
- return backend .server .v2 .store .model .ProfileDetails (
611
- name = new_profile .name ,
612
- username = new_profile .username ,
613
- description = new_profile .description ,
614
- links = new_profile .links ,
615
- avatar_url = new_profile .avatarUrl ,
616
- )
617
-
595
+ return None
618
596
return backend .server .v2 .store .model .ProfileDetails (
619
597
name = profile .name ,
620
598
username = profile .username ,
@@ -623,115 +601,90 @@ async def get_user_profile(
623
601
avatar_url = profile .avatarUrl ,
624
602
)
625
603
except Exception as e :
626
- logger .error (f"Error getting user profile: { str (e )} " )
627
- return backend .server .v2 .store .model .ProfileDetails (
628
- name = "No Profile Data" ,
629
- username = "No Profile Data" ,
630
- description = "No Profile Data" ,
631
- links = [],
632
- avatar_url = "" ,
633
- )
604
+ logger .error ("Error getting user profile: %s" , e )
605
+ raise backend .server .v2 .store .exceptions .DatabaseError (
606
+ "Failed to get user profile"
607
+ ) from e
634
608
635
609
636
- async def update_or_create_profile (
610
+ async def update_profile (
637
611
user_id : str , profile : backend .server .v2 .store .model .Profile
638
612
) -> backend .server .v2 .store .model .CreatorDetails :
639
613
"""
640
- Update the store profile for a user. Creates a new profile if one doesn't exist.
641
- Only allows updating if the user_id matches the owning user.
642
- If a field is None, it will not overwrite the existing value in the case of an update.
643
-
614
+ Update the store profile for a user or create a new one if it doesn't exist.
644
615
Args:
645
616
user_id: ID of the authenticated user
646
617
profile: Updated profile details
647
-
648
618
Returns:
649
- CreatorDetails: The updated profile
650
-
619
+ CreatorDetails: The updated or created profile details
651
620
Raises:
652
- HTTPException: If user is not authorized to update this profile
653
- DatabaseError: If profile cannot be updated due to database issues
621
+ DatabaseError: If there's an issue updating or creating the profile
654
622
"""
655
- logger .info (f"Updating profile for user { user_id } data: { profile } " )
656
-
623
+ logger .info ("Updating profile for user %s with data: %s" , user_id , profile )
657
624
try :
658
- # Sanitize username to only allow letters and hyphens
625
+ # Sanitize username to allow only letters, numbers, and hyphens
659
626
username = "" .join (
660
627
c if c .isalpha () or c == "-" or c .isnumeric () else ""
661
628
for c in profile .username
662
629
).lower ()
663
-
630
+ # Check if profile exists for the given user_id
664
631
existing_profile = await prisma .models .Profile .prisma ().find_first (
665
632
where = {"userId" : user_id }
666
633
)
667
-
668
- # If no profile exists, create a new one
669
634
if not existing_profile :
670
- logger .debug (
671
- f"No existing profile found. Creating new profile for user { user_id } "
672
- )
673
- # Create new profile since one doesn't exist
674
- new_profile = await prisma .models .Profile .prisma ().create (
675
- data = {
676
- "userId" : user_id ,
677
- "name" : profile .name ,
678
- "username" : username ,
679
- "description" : profile .description ,
680
- "links" : profile .links or [],
681
- "avatarUrl" : profile .avatar_url ,
682
- "isFeatured" : False ,
683
- }
635
+ raise backend .server .v2 .store .exceptions .ProfileNotFoundError (
636
+ f"Profile not found for user { user_id } . This should not be possible."
684
637
)
685
638
686
- return backend .server .v2 .store .model .CreatorDetails (
687
- name = new_profile .name ,
688
- username = new_profile .username ,
689
- description = new_profile .description ,
690
- links = new_profile .links ,
691
- avatar_url = new_profile .avatarUrl or "" ,
692
- agent_rating = 0.0 ,
693
- agent_runs = 0 ,
694
- top_categories = [],
639
+ # Verify that the user is authorized to update this profile
640
+ if existing_profile .userId != user_id :
641
+ logger .error (
642
+ "Unauthorized update attempt for profile %s by user %s" ,
643
+ existing_profile .userId ,
644
+ user_id ,
695
645
)
696
- else :
697
- logger .debug (f"Updating existing profile for user { user_id } " )
698
- # Update only provided fields for the existing profile
699
- update_data = {}
700
- if profile .name is not None :
701
- update_data ["name" ] = profile .name
702
- if profile .username is not None :
703
- update_data ["username" ] = username
704
- if profile .description is not None :
705
- update_data ["description" ] = profile .description
706
- if profile .links is not None :
707
- update_data ["links" ] = profile .links
708
- if profile .avatar_url is not None :
709
- update_data ["avatarUrl" ] = profile .avatar_url
710
-
711
- # Update the existing profile
712
- updated_profile = await prisma .models .Profile .prisma ().update (
713
- where = {"id" : existing_profile .id },
714
- data = prisma .types .ProfileUpdateInput (** update_data ),
646
+ raise backend .server .v2 .store .exceptions .DatabaseError (
647
+ f"Unauthorized update attempt for profile { existing_profile .id } by user { user_id } "
715
648
)
716
- if updated_profile is None :
717
- logger .error (f"Failed to update profile for user { user_id } " )
718
- raise backend .server .v2 .store .exceptions .DatabaseError (
719
- "Failed to update profile"
720
- )
721
-
722
- return backend .server .v2 .store .model .CreatorDetails (
723
- name = updated_profile .name ,
724
- username = updated_profile .username ,
725
- description = updated_profile .description ,
726
- links = updated_profile .links ,
727
- avatar_url = updated_profile .avatarUrl or "" ,
728
- agent_rating = 0.0 ,
729
- agent_runs = 0 ,
730
- top_categories = [],
649
+
650
+ logger .debug ("Updating existing profile for user %s" , user_id )
651
+ # Prepare update data, only including non-None values
652
+ update_data = {}
653
+ if profile .name is not None :
654
+ update_data ["name" ] = profile .name
655
+ if profile .username is not None :
656
+ update_data ["username" ] = username
657
+ if profile .description is not None :
658
+ update_data ["description" ] = profile .description
659
+ if profile .links is not None :
660
+ update_data ["links" ] = profile .links
661
+ if profile .avatar_url is not None :
662
+ update_data ["avatarUrl" ] = profile .avatar_url
663
+
664
+ # Update the existing profile
665
+ updated_profile = await prisma .models .Profile .prisma ().update (
666
+ where = {"id" : existing_profile .id },
667
+ data = prisma .types .ProfileUpdateInput (** update_data ),
668
+ )
669
+ if updated_profile is None :
670
+ logger .error ("Failed to update profile for user %s" , user_id )
671
+ raise backend .server .v2 .store .exceptions .DatabaseError (
672
+ "Failed to update profile"
731
673
)
732
674
675
+ return backend .server .v2 .store .model .CreatorDetails (
676
+ name = updated_profile .name ,
677
+ username = updated_profile .username ,
678
+ description = updated_profile .description ,
679
+ links = updated_profile .links ,
680
+ avatar_url = updated_profile .avatarUrl or "" ,
681
+ agent_rating = 0.0 ,
682
+ agent_runs = 0 ,
683
+ top_categories = [],
684
+ )
685
+
733
686
except prisma .errors .PrismaError as e :
734
- logger .error (f "Database error updating profile: { str ( e ) } " )
687
+ logger .error ("Database error updating profile: %s" , e )
735
688
raise backend .server .v2 .store .exceptions .DatabaseError (
736
689
"Failed to update profile"
737
690
) from e
@@ -796,7 +749,7 @@ async def get_my_agents(
796
749
),
797
750
)
798
751
except Exception as e :
799
- logger .error (f"Error getting my agents: { str ( e ) } " )
752
+ logger .error (f"Error getting my agents: { e } " )
800
753
raise backend .server .v2 .store .exceptions .DatabaseError (
801
754
"Failed to fetch my agents"
802
755
) from e
@@ -840,7 +793,7 @@ async def get_agent(
840
793
return graph
841
794
842
795
except Exception as e :
843
- logger .error (f"Error getting agent: { str ( e ) } " )
796
+ logger .error (f"Error getting agent: { e } " )
844
797
raise backend .server .v2 .store .exceptions .DatabaseError (
845
798
"Failed to fetch agent"
846
799
) from e
@@ -905,7 +858,7 @@ async def review_store_submission(
905
858
return submission
906
859
907
860
except Exception as e :
908
- logger .error (f"Could not create store submission review: { str ( e ) } " )
861
+ logger .error (f"Could not create store submission review: { e } " )
909
862
raise backend .server .v2 .store .exceptions .DatabaseError (
910
863
"Failed to create store submission review"
911
864
) from e
0 commit comments