Skip to content

Commit 8181ee8

Browse files
authored
platform(fix): Fix missing Profiles (#9424)
### What's This PR About? This PR makes a few simple improvements to how user profiles are handled in the app: - **Always Have a Profile:** If a user doesn't already have a profile, the system now automatically creates one with some default info (including a fun, randomly generated username). This way, you never end up with a missing profile. - **Better Profile Updates:** Removes the creation of profiles on failed get requests
1 parent 6183ed5 commit 8181ee8

File tree

8 files changed

+273
-152
lines changed

8 files changed

+273
-152
lines changed

autogpt_platform/backend/backend/server/v2/store/db.py

+92-139
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import logging
2-
import random
32
from datetime import datetime
43
from typing import Optional
54

@@ -17,6 +16,25 @@
1716
logger = logging.getLogger(__name__)
1817

1918

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+
2038
async def get_store_agents(
2139
featured: bool = False,
2240
creator: str | None = None,
@@ -29,29 +47,7 @@ async def get_store_agents(
2947
logger.debug(
3048
f"Getting store agents. featured={featured}, creator={creator}, sorted_by={sorted_by}, search={search_query}, category={category}, page={page}"
3149
)
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)
5551

5652
where_clause = {}
5753
if featured:
@@ -93,8 +89,8 @@ async def get_store_agents(
9389
slug=agent.slug,
9490
agent_name=agent.agent_name,
9591
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 "",
9894
sub_heading=agent.sub_heading,
9995
description=agent.description,
10096
runs=agent.runs,
@@ -114,7 +110,7 @@ async def get_store_agents(
114110
),
115111
)
116112
except Exception as e:
117-
logger.error(f"Error getting store agents: {str(e)}")
113+
logger.error(f"Error getting store agents: {e}")
118114
raise backend.server.v2.store.exceptions.DatabaseError(
119115
"Failed to fetch store agents"
120116
) from e
@@ -156,7 +152,7 @@ async def get_store_agent_details(
156152
except backend.server.v2.store.exceptions.AgentNotFoundError:
157153
raise
158154
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}")
160156
raise backend.server.v2.store.exceptions.DatabaseError(
161157
"Failed to fetch agent details"
162158
) from e
@@ -270,7 +266,7 @@ async def get_store_creators(
270266
),
271267
)
272268
except Exception as e:
273-
logger.error(f"Error getting store creators: {str(e)}")
269+
logger.error(f"Error getting store creators: {e}")
274270
raise backend.server.v2.store.exceptions.DatabaseError(
275271
"Failed to fetch store creators"
276272
) from e
@@ -307,7 +303,7 @@ async def get_store_creator_details(
307303
except backend.server.v2.store.exceptions.CreatorNotFoundError:
308304
raise
309305
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}")
311307
raise backend.server.v2.store.exceptions.DatabaseError(
312308
"Failed to fetch creator details"
313309
) from e
@@ -366,7 +362,7 @@ async def get_store_submissions(
366362
)
367363

368364
except Exception as e:
369-
logger.error(f"Error fetching store submissions: {str(e)}")
365+
logger.error(f"Error fetching store submissions: {e}")
370366
# Return empty response rather than exposing internal errors
371367
return backend.server.v2.store.model.StoreSubmissionsResponse(
372368
submissions=[],
@@ -416,7 +412,7 @@ async def delete_store_submission(
416412
return True
417413

418414
except Exception as e:
419-
logger.error(f"Error deleting store submission: {str(e)}")
415+
logger.error(f"Error deleting store submission: {e}")
420416
return False
421417

422418

@@ -539,7 +535,7 @@ async def create_store_submission(
539535
):
540536
raise
541537
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}")
543539
raise backend.server.v2.store.exceptions.DatabaseError(
544540
"Failed to create store submission"
545541
) from e
@@ -579,15 +575,15 @@ async def create_store_review(
579575
)
580576

581577
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}")
583579
raise backend.server.v2.store.exceptions.DatabaseError(
584580
"Failed to create store review"
585581
) from e
586582

587583

588584
async def get_user_profile(
589585
user_id: str,
590-
) -> backend.server.v2.store.model.ProfileDetails:
586+
) -> backend.server.v2.store.model.ProfileDetails | None:
591587
logger.debug(f"Getting user profile for {user_id}")
592588

593589
try:
@@ -596,25 +592,7 @@ async def get_user_profile(
596592
)
597593

598594
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
618596
return backend.server.v2.store.model.ProfileDetails(
619597
name=profile.name,
620598
username=profile.username,
@@ -623,115 +601,90 @@ async def get_user_profile(
623601
avatar_url=profile.avatarUrl,
624602
)
625603
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
634608

635609

636-
async def update_or_create_profile(
610+
async def update_profile(
637611
user_id: str, profile: backend.server.v2.store.model.Profile
638612
) -> backend.server.v2.store.model.CreatorDetails:
639613
"""
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.
644615
Args:
645616
user_id: ID of the authenticated user
646617
profile: Updated profile details
647-
648618
Returns:
649-
CreatorDetails: The updated profile
650-
619+
CreatorDetails: The updated or created profile details
651620
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
654622
"""
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)
657624
try:
658-
# Sanitize username to only allow letters and hyphens
625+
# Sanitize username to allow only letters, numbers, and hyphens
659626
username = "".join(
660627
c if c.isalpha() or c == "-" or c.isnumeric() else ""
661628
for c in profile.username
662629
).lower()
663-
630+
# Check if profile exists for the given user_id
664631
existing_profile = await prisma.models.Profile.prisma().find_first(
665632
where={"userId": user_id}
666633
)
667-
668-
# If no profile exists, create a new one
669634
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."
684637
)
685638

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,
695645
)
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}"
715648
)
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"
731673
)
732674

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+
733686
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)
735688
raise backend.server.v2.store.exceptions.DatabaseError(
736689
"Failed to update profile"
737690
) from e
@@ -796,7 +749,7 @@ async def get_my_agents(
796749
),
797750
)
798751
except Exception as e:
799-
logger.error(f"Error getting my agents: {str(e)}")
752+
logger.error(f"Error getting my agents: {e}")
800753
raise backend.server.v2.store.exceptions.DatabaseError(
801754
"Failed to fetch my agents"
802755
) from e
@@ -840,7 +793,7 @@ async def get_agent(
840793
return graph
841794

842795
except Exception as e:
843-
logger.error(f"Error getting agent: {str(e)}")
796+
logger.error(f"Error getting agent: {e}")
844797
raise backend.server.v2.store.exceptions.DatabaseError(
845798
"Failed to fetch agent"
846799
) from e
@@ -905,7 +858,7 @@ async def review_store_submission(
905858
return submission
906859

907860
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}")
909862
raise backend.server.v2.store.exceptions.DatabaseError(
910863
"Failed to create store submission review"
911864
) from e

0 commit comments

Comments
 (0)