mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-09 07:08:09 -05:00
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
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import logging
|
||||
import random
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
@@ -17,6 +16,25 @@ from backend.data.graph import GraphModel
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def sanitize_query(query: str | None) -> str | None:
|
||||
if query is None:
|
||||
return query
|
||||
query = query.strip()[:100]
|
||||
return (
|
||||
query.replace("\\", "\\\\")
|
||||
.replace("%", "\\%")
|
||||
.replace("_", "\\_")
|
||||
.replace("[", "\\[")
|
||||
.replace("]", "\\]")
|
||||
.replace("'", "\\'")
|
||||
.replace('"', '\\"')
|
||||
.replace(";", "\\;")
|
||||
.replace("--", "\\--")
|
||||
.replace("/*", "\\/*")
|
||||
.replace("*/", "\\*/")
|
||||
)
|
||||
|
||||
|
||||
async def get_store_agents(
|
||||
featured: bool = False,
|
||||
creator: str | None = None,
|
||||
@@ -29,29 +47,7 @@ async def get_store_agents(
|
||||
logger.debug(
|
||||
f"Getting store agents. featured={featured}, creator={creator}, sorted_by={sorted_by}, search={search_query}, category={category}, page={page}"
|
||||
)
|
||||
sanitized_query = None
|
||||
# Sanitize and validate search query by escaping special characters
|
||||
if search_query is not None:
|
||||
sanitized_query = search_query.strip()
|
||||
if not sanitized_query or len(sanitized_query) > 100: # Reasonable length limit
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
f"Invalid search query: len({len(sanitized_query)}) query: {search_query}"
|
||||
)
|
||||
|
||||
# Escape special SQL characters
|
||||
sanitized_query = (
|
||||
sanitized_query.replace("\\", "\\\\")
|
||||
.replace("%", "\\%")
|
||||
.replace("_", "\\_")
|
||||
.replace("[", "\\[")
|
||||
.replace("]", "\\]")
|
||||
.replace("'", "\\'")
|
||||
.replace('"', '\\"')
|
||||
.replace(";", "\\;")
|
||||
.replace("--", "\\--")
|
||||
.replace("/*", "\\/*")
|
||||
.replace("*/", "\\*/")
|
||||
)
|
||||
sanitized_query = sanitize_query(search_query)
|
||||
|
||||
where_clause = {}
|
||||
if featured:
|
||||
@@ -93,8 +89,8 @@ async def get_store_agents(
|
||||
slug=agent.slug,
|
||||
agent_name=agent.agent_name,
|
||||
agent_image=agent.agent_image[0] if agent.agent_image else "",
|
||||
creator=agent.creator_username,
|
||||
creator_avatar=agent.creator_avatar,
|
||||
creator=agent.creator_username or "Needs Profile",
|
||||
creator_avatar=agent.creator_avatar or "",
|
||||
sub_heading=agent.sub_heading,
|
||||
description=agent.description,
|
||||
runs=agent.runs,
|
||||
@@ -114,7 +110,7 @@ async def get_store_agents(
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting store agents: {str(e)}")
|
||||
logger.error(f"Error getting store agents: {e}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to fetch store agents"
|
||||
) from e
|
||||
@@ -156,7 +152,7 @@ async def get_store_agent_details(
|
||||
except backend.server.v2.store.exceptions.AgentNotFoundError:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting store agent details: {str(e)}")
|
||||
logger.error(f"Error getting store agent details: {e}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to fetch agent details"
|
||||
) from e
|
||||
@@ -270,7 +266,7 @@ async def get_store_creators(
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting store creators: {str(e)}")
|
||||
logger.error(f"Error getting store creators: {e}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to fetch store creators"
|
||||
) from e
|
||||
@@ -307,7 +303,7 @@ async def get_store_creator_details(
|
||||
except backend.server.v2.store.exceptions.CreatorNotFoundError:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting store creator details: {str(e)}")
|
||||
logger.error(f"Error getting store creator details: {e}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to fetch creator details"
|
||||
) from e
|
||||
@@ -366,7 +362,7 @@ async def get_store_submissions(
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error fetching store submissions: {str(e)}")
|
||||
logger.error(f"Error fetching store submissions: {e}")
|
||||
# Return empty response rather than exposing internal errors
|
||||
return backend.server.v2.store.model.StoreSubmissionsResponse(
|
||||
submissions=[],
|
||||
@@ -416,7 +412,7 @@ async def delete_store_submission(
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting store submission: {str(e)}")
|
||||
logger.error(f"Error deleting store submission: {e}")
|
||||
return False
|
||||
|
||||
|
||||
@@ -539,7 +535,7 @@ async def create_store_submission(
|
||||
):
|
||||
raise
|
||||
except prisma.errors.PrismaError as e:
|
||||
logger.error(f"Database error creating store submission: {str(e)}")
|
||||
logger.error(f"Database error creating store submission: {e}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to create store submission"
|
||||
) from e
|
||||
@@ -579,7 +575,7 @@ async def create_store_review(
|
||||
)
|
||||
|
||||
except prisma.errors.PrismaError as e:
|
||||
logger.error(f"Database error creating store review: {str(e)}")
|
||||
logger.error(f"Database error creating store review: {e}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to create store review"
|
||||
) from e
|
||||
@@ -587,7 +583,7 @@ async def create_store_review(
|
||||
|
||||
async def get_user_profile(
|
||||
user_id: str,
|
||||
) -> backend.server.v2.store.model.ProfileDetails:
|
||||
) -> backend.server.v2.store.model.ProfileDetails | None:
|
||||
logger.debug(f"Getting user profile for {user_id}")
|
||||
|
||||
try:
|
||||
@@ -596,25 +592,7 @@ async def get_user_profile(
|
||||
)
|
||||
|
||||
if not profile:
|
||||
logger.warning(f"Profile not found for user {user_id}")
|
||||
new_profile = await prisma.models.Profile.prisma().create(
|
||||
data=prisma.types.ProfileCreateInput(
|
||||
userId=user_id,
|
||||
name="No Profile Data",
|
||||
username=f"{random.choice(['happy', 'clever', 'swift', 'bright', 'wise'])}-{random.choice(['fox', 'wolf', 'bear', 'eagle', 'owl'])}_{random.randint(1000,9999)}".lower(),
|
||||
description="No Profile Data",
|
||||
links=[],
|
||||
avatarUrl="",
|
||||
)
|
||||
)
|
||||
return backend.server.v2.store.model.ProfileDetails(
|
||||
name=new_profile.name,
|
||||
username=new_profile.username,
|
||||
description=new_profile.description,
|
||||
links=new_profile.links,
|
||||
avatar_url=new_profile.avatarUrl,
|
||||
)
|
||||
|
||||
return None
|
||||
return backend.server.v2.store.model.ProfileDetails(
|
||||
name=profile.name,
|
||||
username=profile.username,
|
||||
@@ -623,115 +601,90 @@ async def get_user_profile(
|
||||
avatar_url=profile.avatarUrl,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting user profile: {str(e)}")
|
||||
return backend.server.v2.store.model.ProfileDetails(
|
||||
name="No Profile Data",
|
||||
username="No Profile Data",
|
||||
description="No Profile Data",
|
||||
links=[],
|
||||
avatar_url="",
|
||||
)
|
||||
logger.error("Error getting user profile: %s", e)
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to get user profile"
|
||||
) from e
|
||||
|
||||
|
||||
async def update_or_create_profile(
|
||||
async def update_profile(
|
||||
user_id: str, profile: backend.server.v2.store.model.Profile
|
||||
) -> backend.server.v2.store.model.CreatorDetails:
|
||||
"""
|
||||
Update the store profile for a user. Creates a new profile if one doesn't exist.
|
||||
Only allows updating if the user_id matches the owning user.
|
||||
If a field is None, it will not overwrite the existing value in the case of an update.
|
||||
|
||||
Update the store profile for a user or create a new one if it doesn't exist.
|
||||
Args:
|
||||
user_id: ID of the authenticated user
|
||||
profile: Updated profile details
|
||||
|
||||
Returns:
|
||||
CreatorDetails: The updated profile
|
||||
|
||||
CreatorDetails: The updated or created profile details
|
||||
Raises:
|
||||
HTTPException: If user is not authorized to update this profile
|
||||
DatabaseError: If profile cannot be updated due to database issues
|
||||
DatabaseError: If there's an issue updating or creating the profile
|
||||
"""
|
||||
logger.info(f"Updating profile for user {user_id} data: {profile}")
|
||||
|
||||
logger.info("Updating profile for user %s with data: %s", user_id, profile)
|
||||
try:
|
||||
# Sanitize username to only allow letters and hyphens
|
||||
# Sanitize username to allow only letters, numbers, and hyphens
|
||||
username = "".join(
|
||||
c if c.isalpha() or c == "-" or c.isnumeric() else ""
|
||||
for c in profile.username
|
||||
).lower()
|
||||
|
||||
# Check if profile exists for the given user_id
|
||||
existing_profile = await prisma.models.Profile.prisma().find_first(
|
||||
where={"userId": user_id}
|
||||
)
|
||||
|
||||
# If no profile exists, create a new one
|
||||
if not existing_profile:
|
||||
logger.debug(
|
||||
f"No existing profile found. Creating new profile for user {user_id}"
|
||||
)
|
||||
# Create new profile since one doesn't exist
|
||||
new_profile = await prisma.models.Profile.prisma().create(
|
||||
data={
|
||||
"userId": user_id,
|
||||
"name": profile.name,
|
||||
"username": username,
|
||||
"description": profile.description,
|
||||
"links": profile.links or [],
|
||||
"avatarUrl": profile.avatar_url,
|
||||
"isFeatured": False,
|
||||
}
|
||||
raise backend.server.v2.store.exceptions.ProfileNotFoundError(
|
||||
f"Profile not found for user {user_id}. This should not be possible."
|
||||
)
|
||||
|
||||
return backend.server.v2.store.model.CreatorDetails(
|
||||
name=new_profile.name,
|
||||
username=new_profile.username,
|
||||
description=new_profile.description,
|
||||
links=new_profile.links,
|
||||
avatar_url=new_profile.avatarUrl or "",
|
||||
agent_rating=0.0,
|
||||
agent_runs=0,
|
||||
top_categories=[],
|
||||
# Verify that the user is authorized to update this profile
|
||||
if existing_profile.userId != user_id:
|
||||
logger.error(
|
||||
"Unauthorized update attempt for profile %s by user %s",
|
||||
existing_profile.userId,
|
||||
user_id,
|
||||
)
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
f"Unauthorized update attempt for profile {existing_profile.id} by user {user_id}"
|
||||
)
|
||||
else:
|
||||
logger.debug(f"Updating existing profile for user {user_id}")
|
||||
# Update only provided fields for the existing profile
|
||||
update_data = {}
|
||||
if profile.name is not None:
|
||||
update_data["name"] = profile.name
|
||||
if profile.username is not None:
|
||||
update_data["username"] = username
|
||||
if profile.description is not None:
|
||||
update_data["description"] = profile.description
|
||||
if profile.links is not None:
|
||||
update_data["links"] = profile.links
|
||||
if profile.avatar_url is not None:
|
||||
update_data["avatarUrl"] = profile.avatar_url
|
||||
|
||||
# Update the existing profile
|
||||
updated_profile = await prisma.models.Profile.prisma().update(
|
||||
where={"id": existing_profile.id},
|
||||
data=prisma.types.ProfileUpdateInput(**update_data),
|
||||
)
|
||||
if updated_profile is None:
|
||||
logger.error(f"Failed to update profile for user {user_id}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to update profile"
|
||||
)
|
||||
logger.debug("Updating existing profile for user %s", user_id)
|
||||
# Prepare update data, only including non-None values
|
||||
update_data = {}
|
||||
if profile.name is not None:
|
||||
update_data["name"] = profile.name
|
||||
if profile.username is not None:
|
||||
update_data["username"] = username
|
||||
if profile.description is not None:
|
||||
update_data["description"] = profile.description
|
||||
if profile.links is not None:
|
||||
update_data["links"] = profile.links
|
||||
if profile.avatar_url is not None:
|
||||
update_data["avatarUrl"] = profile.avatar_url
|
||||
|
||||
return backend.server.v2.store.model.CreatorDetails(
|
||||
name=updated_profile.name,
|
||||
username=updated_profile.username,
|
||||
description=updated_profile.description,
|
||||
links=updated_profile.links,
|
||||
avatar_url=updated_profile.avatarUrl or "",
|
||||
agent_rating=0.0,
|
||||
agent_runs=0,
|
||||
top_categories=[],
|
||||
# Update the existing profile
|
||||
updated_profile = await prisma.models.Profile.prisma().update(
|
||||
where={"id": existing_profile.id},
|
||||
data=prisma.types.ProfileUpdateInput(**update_data),
|
||||
)
|
||||
if updated_profile is None:
|
||||
logger.error("Failed to update profile for user %s", user_id)
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to update profile"
|
||||
)
|
||||
|
||||
return backend.server.v2.store.model.CreatorDetails(
|
||||
name=updated_profile.name,
|
||||
username=updated_profile.username,
|
||||
description=updated_profile.description,
|
||||
links=updated_profile.links,
|
||||
avatar_url=updated_profile.avatarUrl or "",
|
||||
agent_rating=0.0,
|
||||
agent_runs=0,
|
||||
top_categories=[],
|
||||
)
|
||||
|
||||
except prisma.errors.PrismaError as e:
|
||||
logger.error(f"Database error updating profile: {str(e)}")
|
||||
logger.error("Database error updating profile: %s", e)
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to update profile"
|
||||
) from e
|
||||
@@ -796,7 +749,7 @@ async def get_my_agents(
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting my agents: {str(e)}")
|
||||
logger.error(f"Error getting my agents: {e}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to fetch my agents"
|
||||
) from e
|
||||
@@ -840,7 +793,7 @@ async def get_agent(
|
||||
return graph
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting agent: {str(e)}")
|
||||
logger.error(f"Error getting agent: {e}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to fetch agent"
|
||||
) from e
|
||||
@@ -905,7 +858,7 @@ async def review_store_submission(
|
||||
return submission
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Could not create store submission review: {str(e)}")
|
||||
logger.error(f"Could not create store submission review: {e}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to create store submission review"
|
||||
) from e
|
||||
|
||||
@@ -158,6 +158,26 @@ async def test_create_store_submission(mocker):
|
||||
agentId="agent-id",
|
||||
agentVersion=1,
|
||||
owningUserId="user-id",
|
||||
StoreListingVersions=[
|
||||
prisma.models.StoreListingVersion(
|
||||
id="version-id",
|
||||
agentId="agent-id",
|
||||
agentVersion=1,
|
||||
slug="test-agent",
|
||||
name="Test Agent",
|
||||
description="Test description",
|
||||
createdAt=datetime.now(),
|
||||
updatedAt=datetime.now(),
|
||||
subHeading="Test heading",
|
||||
imageUrls=["image.jpg"],
|
||||
categories=["test"],
|
||||
isFeatured=False,
|
||||
isDeleted=False,
|
||||
version=1,
|
||||
isAvailable=True,
|
||||
isApproved=False,
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
# Mock prisma calls
|
||||
@@ -181,6 +201,7 @@ async def test_create_store_submission(mocker):
|
||||
# Verify results
|
||||
assert result.name == "Test Agent"
|
||||
assert result.description == "Test description"
|
||||
assert result.store_listing_version_id == "version-id"
|
||||
|
||||
# Verify mocks called correctly
|
||||
mock_agent_graph.return_value.find_first.assert_called_once()
|
||||
@@ -195,6 +216,7 @@ async def test_update_profile(mocker):
|
||||
id="profile-id",
|
||||
name="Test Creator",
|
||||
username="creator",
|
||||
userId="user-id",
|
||||
description="Test description",
|
||||
links=["link1"],
|
||||
avatarUrl="avatar.jpg",
|
||||
@@ -221,7 +243,7 @@ async def test_update_profile(mocker):
|
||||
)
|
||||
|
||||
# Call function
|
||||
result = await db.update_or_create_profile("user-id", profile)
|
||||
result = await db.update_profile("user-id", profile)
|
||||
|
||||
# Verify results
|
||||
assert result.username == "creator"
|
||||
@@ -237,7 +259,7 @@ async def test_get_user_profile(mocker):
|
||||
# Mock data
|
||||
mock_profile = prisma.models.Profile(
|
||||
id="profile-id",
|
||||
name="No Profile Data",
|
||||
name="Test User",
|
||||
username="testuser",
|
||||
description="Test description",
|
||||
links=["link1", "link2"],
|
||||
@@ -245,20 +267,22 @@ async def test_get_user_profile(mocker):
|
||||
isFeatured=False,
|
||||
createdAt=datetime.now(),
|
||||
updatedAt=datetime.now(),
|
||||
userId="user-id",
|
||||
)
|
||||
|
||||
# Mock prisma calls
|
||||
mock_profile_db = mocker.patch("prisma.models.Profile.prisma")
|
||||
mock_profile_db.return_value.find_unique = mocker.AsyncMock(
|
||||
mock_profile_db.return_value.find_first = mocker.AsyncMock(
|
||||
return_value=mock_profile
|
||||
)
|
||||
|
||||
# Call function
|
||||
result = await db.get_user_profile("user-id")
|
||||
|
||||
assert result is not None
|
||||
# Verify results
|
||||
assert result.name == "No Profile Data"
|
||||
assert result.username == "No Profile Data"
|
||||
assert result.description == "No Profile Data"
|
||||
assert result.links == []
|
||||
assert result.avatar_url == ""
|
||||
assert result.name == "Test User"
|
||||
assert result.username == "testuser"
|
||||
assert result.description == "Test description"
|
||||
assert result.links == ["link1", "link2"]
|
||||
assert result.avatar_url == "avatar.jpg"
|
||||
|
||||
@@ -42,6 +42,11 @@ async def get_profile(
|
||||
"""
|
||||
try:
|
||||
profile = await backend.server.v2.store.db.get_user_profile(user_id)
|
||||
if profile is None:
|
||||
return fastapi.responses.JSONResponse(
|
||||
status_code=404,
|
||||
content={"detail": "Profile not found"},
|
||||
)
|
||||
return profile
|
||||
except Exception:
|
||||
logger.exception("Exception occurred whilst getting user profile")
|
||||
@@ -77,7 +82,7 @@ async def update_or_create_profile(
|
||||
HTTPException: If there is an error updating the profile
|
||||
"""
|
||||
try:
|
||||
updated_profile = await backend.server.v2.store.db.update_or_create_profile(
|
||||
updated_profile = await backend.server.v2.store.db.update_profile(
|
||||
user_id=user_id, profile=profile
|
||||
)
|
||||
return updated_profile
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
CREATE OR REPLACE FUNCTION generate_username()
|
||||
RETURNS TEXT AS $$
|
||||
DECLARE
|
||||
-- Random username generation
|
||||
selected_adjective TEXT;
|
||||
selected_animal TEXT;
|
||||
random_int INT;
|
||||
generated_username TEXT;
|
||||
BEGIN
|
||||
FOR i IN 1..10 LOOP
|
||||
SELECT unnest
|
||||
INTO selected_adjective
|
||||
FROM (VALUES ('happy'), ('clever'), ('swift'), ('bright'), ('wise'), ('funny'), ('cool'), ('awesome'), ('amazing'), ('fantastic'), ('wonderful')) AS t(unnest)
|
||||
ORDER BY random()
|
||||
LIMIT 1;
|
||||
|
||||
SELECT unnest
|
||||
INTO selected_animal
|
||||
FROM (VALUES ('fox'), ('wolf'), ('bear'), ('eagle'), ('owl'), ('tiger'), ('lion'), ('elephant'), ('giraffe'), ('zebra')) AS t(unnest)
|
||||
ORDER BY random()
|
||||
LIMIT 1;
|
||||
|
||||
SELECT floor(random() * (99999 - 10000 + 1) + 10000)::int
|
||||
INTO random_int;
|
||||
|
||||
generated_username := lower(selected_adjective || '-' || selected_animal || '-' || random_int);
|
||||
|
||||
-- Check if username is already taken
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM platform."Profile" WHERE username = generated_username
|
||||
) THEN
|
||||
-- Username is unique, exit the loop
|
||||
EXIT;
|
||||
END IF;
|
||||
|
||||
-- If we've tried 10 times and still haven't found a unique username
|
||||
IF i = 10 THEN
|
||||
RAISE EXCEPTION 'Unable to generate unique username after 10 attempts';
|
||||
END IF;
|
||||
END LOOP;
|
||||
RETURN generated_username;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
CREATE OR REPLACE FUNCTION add_user_and_profile_to_platform()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
-- Exit early if NEW.id is null to prevent constraint violations
|
||||
IF NEW.id IS NULL THEN
|
||||
RAISE EXCEPTION 'Cannot create user/profile: id is null';
|
||||
END IF;
|
||||
|
||||
/*
|
||||
1) Insert into platform."User"
|
||||
(If you already have such a row or want different columns, adjust below.)
|
||||
*/
|
||||
INSERT INTO platform."User" (id, email, "updatedAt")
|
||||
VALUES (NEW.id, NEW.email, now());
|
||||
|
||||
/*
|
||||
2) Insert into platform."Profile"
|
||||
Adjust columns/types depending on how your "Profile" schema is defined:
|
||||
- "links" might be text[], jsonb, or something else in your table.
|
||||
- "avatarUrl" and "description" can be defaulted as well.
|
||||
*/
|
||||
INSERT INTO platform."Profile"
|
||||
("id", "userId", name, username, description, links, "avatarUrl", "updatedAt")
|
||||
VALUES
|
||||
(
|
||||
NEW.id,
|
||||
NEW.id,
|
||||
COALESCE(split_part(NEW.email, '@', 1), 'user'), -- handle null email
|
||||
platform.generate_username(),
|
||||
'I''m new here',
|
||||
'{}', -- empty array or empty JSON, depending on your column definition
|
||||
'',
|
||||
now()
|
||||
);
|
||||
|
||||
RETURN NEW;
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
-- Log the error details
|
||||
RAISE NOTICE 'Error in add_user_and_profile_to_platform: %', SQLERRM;
|
||||
-- Re-raise the error
|
||||
RAISE;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
-- Check if the auth schema and users table exist
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'auth'
|
||||
AND table_name = 'users'
|
||||
) THEN
|
||||
-- Drop the trigger if it exists
|
||||
DROP TRIGGER IF EXISTS user_added_to_platform ON auth.users;
|
||||
|
||||
-- Create the trigger
|
||||
CREATE TRIGGER user_added_to_platform
|
||||
AFTER INSERT ON auth.users
|
||||
FOR EACH ROW EXECUTE FUNCTION add_user_and_profile_to_platform();
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'platform'
|
||||
AND table_name = 'User'
|
||||
) AND EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'platform'
|
||||
AND table_name = 'Profile'
|
||||
) THEN
|
||||
INSERT INTO platform."Profile"
|
||||
("id", "userId", name, username, description, links, "avatarUrl", "updatedAt")
|
||||
SELECT
|
||||
u.id,
|
||||
u.id,
|
||||
COALESCE(split_part(u.email, '@', 1), 'user'),
|
||||
platform.generate_username(),
|
||||
'I''m new here',
|
||||
'{}',
|
||||
'',
|
||||
now()
|
||||
FROM platform."User" u
|
||||
LEFT JOIN platform."Profile" p ON u.id = p."userId"
|
||||
WHERE p.id IS NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -184,7 +184,7 @@ export default function CreditsPage() {
|
||||
<b>Note:</b> For your safety, we will top up your balance{" "}
|
||||
<b>at most once</b> per agent execution to prevent unintended
|
||||
excessive charges. Therefore, ensure that the automatic top-up
|
||||
amount is sufficient for your agent's operation.
|
||||
amount is sufficient for your agent's operation.
|
||||
</p>
|
||||
|
||||
{autoTopUpConfig?.amount ? (
|
||||
|
||||
@@ -36,9 +36,8 @@ export async function signup(values: z.infer<typeof signupFormSchema>) {
|
||||
if (data.session) {
|
||||
await supabase.auth.setSession(data.session);
|
||||
}
|
||||
console.log("Signed up");
|
||||
revalidatePath("/", "layout");
|
||||
redirect("/marketplace/profile");
|
||||
redirect("/");
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,9 @@ test.describe("Profile", () => {
|
||||
|
||||
// Verify email matches test worker's email
|
||||
const displayedHandle = await profilePage.getDisplayedName();
|
||||
test.expect(displayedHandle).toBe("No Profile Data");
|
||||
test.expect(displayedHandle).not.toBeNull();
|
||||
test.expect(displayedHandle).not.toBe("");
|
||||
test.expect(displayedHandle).toBeDefined();
|
||||
});
|
||||
|
||||
test("profile navigation is accessible from navbar", async ({ page }) => {
|
||||
|
||||
Reference in New Issue
Block a user