mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
wip
This commit is contained in:
@@ -11,6 +11,7 @@ import backend.data.block
|
||||
import backend.data.db
|
||||
import backend.data.user
|
||||
import backend.server.routers.v1
|
||||
import backend.server.v2.store.routes
|
||||
import backend.util.service
|
||||
import backend.util.settings
|
||||
|
||||
@@ -62,7 +63,10 @@ app = fastapi.FastAPI(
|
||||
|
||||
app.add_exception_handler(ValueError, handle_internal_http_error(400))
|
||||
app.add_exception_handler(500, handle_internal_http_error(500))
|
||||
app.include_router(backend.server.routers.v1.v1_router, tags=["v1"])
|
||||
app.include_router(backend.server.routers.v1.v1_router, tags=["v1"], prefix="/api")
|
||||
app.include_router(
|
||||
backend.server.v2.store.routes.router, tags=["v2"], prefix="/api/store"
|
||||
)
|
||||
|
||||
|
||||
@app.get(path="/health", tags=["health"], dependencies=[])
|
||||
|
||||
@@ -40,8 +40,7 @@ logger = logging.getLogger(__name__)
|
||||
_user_credit_model = get_user_credit_model()
|
||||
|
||||
# Define the API routes
|
||||
v1_router = APIRouter(prefix="/api")
|
||||
|
||||
v1_router = APIRouter()
|
||||
|
||||
v1_router.dependencies.append(Depends(auth_middleware))
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import logging
|
||||
|
||||
import backend.server.v2.store.exceptions
|
||||
import backend.server.v2.store.model
|
||||
import prisma.enums
|
||||
import prisma.errors
|
||||
import prisma.models
|
||||
import prisma.types
|
||||
|
||||
import backend.server.v2.store.exceptions
|
||||
import backend.server.v2.store.model
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -84,7 +85,9 @@ async def get_store_agents(
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting store agents: {str(e)}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError("Failed to fetch store agents") from e
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to fetch store agents"
|
||||
) from e
|
||||
|
||||
|
||||
async def get_store_agent_details(
|
||||
@@ -99,7 +102,9 @@ async def get_store_agent_details(
|
||||
|
||||
if not agent:
|
||||
logger.warning(f"Agent not found: {username}/{agent_name}")
|
||||
raise backend.server.v2.store.exceptions.AgentNotFoundError(f"Agent {username}/{agent_name} not found")
|
||||
raise backend.server.v2.store.exceptions.AgentNotFoundError(
|
||||
f"Agent {username}/{agent_name} not found"
|
||||
)
|
||||
|
||||
logger.debug(f"Found agent details for {username}/{agent_name}")
|
||||
return backend.server.v2.store.model.StoreAgentDetails(
|
||||
@@ -120,7 +125,9 @@ async def get_store_agent_details(
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting store agent details: {str(e)}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError("Failed to fetch agent details") from e
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to fetch agent details"
|
||||
) from e
|
||||
|
||||
|
||||
async def get_store_creators(
|
||||
@@ -199,7 +206,9 @@ async def get_store_creators(
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting store creators: {str(e)}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError("Failed to fetch store creators") from e
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to fetch store creators"
|
||||
) from e
|
||||
|
||||
|
||||
async def get_store_creator_details(
|
||||
@@ -215,7 +224,9 @@ async def get_store_creator_details(
|
||||
|
||||
if not creator:
|
||||
logger.warning(f"Creator not found: {username}")
|
||||
raise backend.server.v2.store.exceptions.CreatorNotFoundError(f"Creator {username} not found")
|
||||
raise backend.server.v2.store.exceptions.CreatorNotFoundError(
|
||||
f"Creator {username} not found"
|
||||
)
|
||||
|
||||
logger.debug(f"Found creator details for {username}")
|
||||
return backend.server.v2.store.model.CreatorDetails(
|
||||
@@ -232,7 +243,9 @@ async def get_store_creator_details(
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting store creator details: {str(e)}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError("Failed to fetch creator details") from e
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to fetch creator details"
|
||||
) from e
|
||||
|
||||
|
||||
async def get_store_submissions(
|
||||
@@ -339,7 +352,9 @@ async def create_store_submission(
|
||||
logger.warning(
|
||||
f"Agent not found for user {user_id}: {agent_id} v{agent_version}"
|
||||
)
|
||||
raise backend.server.v2.store.exceptions.AgentNotFoundError("Agent not found for this user")
|
||||
raise backend.server.v2.store.exceptions.AgentNotFoundError(
|
||||
"Agent not found for this user"
|
||||
)
|
||||
|
||||
listing = await prisma.models.StoreListing.prisma().find_first(
|
||||
where=prisma.types.StoreListingWhereInput(
|
||||
@@ -348,7 +363,9 @@ async def create_store_submission(
|
||||
)
|
||||
if listing is not None:
|
||||
logger.warning(f"Listing already exists for agent {agent_id}")
|
||||
raise backend.server.v2.store.exceptions.ListingExistsError("Listing already exists for this agent")
|
||||
raise backend.server.v2.store.exceptions.ListingExistsError(
|
||||
"Listing already exists for this agent"
|
||||
)
|
||||
|
||||
# Create the store listing
|
||||
listing = await prisma.models.StoreListing.prisma().create(
|
||||
@@ -384,11 +401,16 @@ async def create_store_submission(
|
||||
rating=0.0,
|
||||
)
|
||||
|
||||
except (backend.server.v2.store.exceptions.AgentNotFoundError, backend.server.v2.store.exceptions.ListingExistsError):
|
||||
except (
|
||||
backend.server.v2.store.exceptions.AgentNotFoundError,
|
||||
backend.server.v2.store.exceptions.ListingExistsError,
|
||||
):
|
||||
raise
|
||||
except prisma.errors.PrismaError as e:
|
||||
logger.error(f"Database error creating store submission: {str(e)}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError("Failed to create store submission") from e
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to create store submission"
|
||||
) from e
|
||||
|
||||
|
||||
async def update_profile(
|
||||
@@ -455,7 +477,9 @@ async def update_profile(
|
||||
)
|
||||
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")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to update profile"
|
||||
)
|
||||
|
||||
return backend.server.v2.store.model.CreatorDetails(
|
||||
name=updated_profile.name,
|
||||
@@ -470,4 +494,6 @@ async def update_profile(
|
||||
|
||||
except prisma.errors.PrismaError as e:
|
||||
logger.error(f"Database error updating profile: {str(e)}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError("Failed to update profile") from e
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to update profile"
|
||||
) from e
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
from datetime import datetime
|
||||
|
||||
import backend.server.v2.store.db as db
|
||||
import prisma.errors
|
||||
import prisma.models
|
||||
import pytest
|
||||
from backend.server.v2.store.model import CreatorDetails
|
||||
from prisma import Prisma, register
|
||||
|
||||
import backend.server.v2.store.db as db
|
||||
import backend.server.v2.store.exceptions
|
||||
from backend.server.v2.store.model import CreatorDetails
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def setup_prisma():
|
||||
try:
|
||||
register(Prisma())
|
||||
except:
|
||||
except backend.server.v2.store.exceptions.DatabaseError:
|
||||
pass
|
||||
yield
|
||||
|
||||
|
||||
@@ -33,26 +33,32 @@ class StorageUploadError(MediaUploadError):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class StoreError(Exception):
|
||||
"""Base exception for store-related errors"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class AgentNotFoundError(StoreError):
|
||||
"""Raised when an agent is not found"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class CreatorNotFoundError(StoreError):
|
||||
"""Raised when a creator is not found"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ListingExistsError(StoreError):
|
||||
"""Raised when trying to create a listing that already exists"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class DatabaseError(StoreError):
|
||||
"""Raised when there is an error interacting with the database"""
|
||||
pass
|
||||
|
||||
pass
|
||||
|
||||
@@ -2,10 +2,11 @@ import logging
|
||||
import os
|
||||
import uuid
|
||||
|
||||
import backend.server.v2.store.exceptions
|
||||
import fastapi
|
||||
import supabase
|
||||
|
||||
import backend.server.v2.store.exceptions
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ALLOWED_IMAGE_TYPES = {"image/jpeg", "image/png", "image/gif", "image/webp"}
|
||||
@@ -19,7 +20,9 @@ async def upload_media(user_id: str, file: fastapi.UploadFile) -> str:
|
||||
supabase_key = os.environ["SUPABASE_KEY"]
|
||||
except KeyError as e:
|
||||
logger.error(f"Missing required environment variable: {str(e)}")
|
||||
raise backend.server.v2.store.exceptions.StorageConfigError("Missing storage configuration") from e
|
||||
raise backend.server.v2.store.exceptions.StorageConfigError(
|
||||
"Missing storage configuration"
|
||||
) from e
|
||||
|
||||
try:
|
||||
# Validate file type
|
||||
@@ -42,12 +45,16 @@ async def upload_media(user_id: str, file: fastapi.UploadFile) -> str:
|
||||
file_size += len(chunk)
|
||||
if file_size > MAX_FILE_SIZE:
|
||||
logger.warning(f"File size too large: {file_size} bytes")
|
||||
raise backend.server.v2.store.exceptions.FileSizeTooLargeError("File too large. Maximum size is 50MB")
|
||||
raise backend.server.v2.store.exceptions.FileSizeTooLargeError(
|
||||
"File too large. Maximum size is 50MB"
|
||||
)
|
||||
except backend.server.v2.store.exceptions.FileSizeTooLargeError:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error reading file chunks: {str(e)}")
|
||||
raise backend.server.v2.store.exceptions.FileReadError("Failed to read uploaded file") from e
|
||||
raise backend.server.v2.store.exceptions.FileReadError(
|
||||
"Failed to read uploaded file"
|
||||
) from e
|
||||
|
||||
# Reset file pointer
|
||||
await file.seek(0)
|
||||
@@ -80,10 +87,14 @@ async def upload_media(user_id: str, file: fastapi.UploadFile) -> str:
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Supabase storage error: {str(e)}")
|
||||
raise backend.server.v2.store.exceptions.StorageUploadError("Failed to upload file to storage") from e
|
||||
raise backend.server.v2.store.exceptions.StorageUploadError(
|
||||
"Failed to upload file to storage"
|
||||
) from e
|
||||
|
||||
except backend.server.v2.store.exceptions.MediaUploadError:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.exception("Unexpected error in upload_media")
|
||||
raise backend.server.v2.store.exceptions.MediaUploadError("Unexpected error during media upload") from e
|
||||
raise backend.server.v2.store.exceptions.MediaUploadError(
|
||||
"Unexpected error during media upload"
|
||||
) from e
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import io
|
||||
import unittest.mock
|
||||
|
||||
import backend.server.v2.store.exceptions
|
||||
import backend.server.v2.store.media
|
||||
import fastapi
|
||||
import pytest
|
||||
import starlette.datastructures
|
||||
|
||||
import backend.server.v2.store.exceptions
|
||||
import backend.server.v2.store.media
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_env_vars(monkeypatch):
|
||||
|
||||
@@ -271,7 +271,6 @@ class Secrets(UpdateTrackingModel["Secrets"], BaseSettings):
|
||||
# Add more secret fields as needed
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
secrets_dir=get_secrets_path(),
|
||||
env_file=".env",
|
||||
env_file_encoding="utf-8",
|
||||
extra="allow",
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import asyncio
|
||||
import random
|
||||
from datetime import datetime, timedelta
|
||||
from prisma import Prisma
|
||||
from faker import Faker
|
||||
from datetime import datetime
|
||||
|
||||
import prisma.enums
|
||||
from faker import Faker
|
||||
from prisma import Prisma
|
||||
|
||||
faker = Faker()
|
||||
|
||||
@@ -19,7 +20,9 @@ MAX_GRAPHS_PER_USER = 5 # Total graphs: 500-2500 (NUM_USERS * MIN/MAX_GRAPHS)
|
||||
|
||||
# Per-graph entities
|
||||
MIN_NODES_PER_GRAPH = 2 # Each graph will have between 2-5 nodes
|
||||
MAX_NODES_PER_GRAPH = 5 # Total nodes: 1000-2500 (GRAPHS_PER_USER * NUM_USERS * MIN/MAX_NODES)
|
||||
MAX_NODES_PER_GRAPH = (
|
||||
5 # Total nodes: 1000-2500 (GRAPHS_PER_USER * NUM_USERS * MIN/MAX_NODES)
|
||||
)
|
||||
|
||||
# Additional per-user entities
|
||||
MIN_PRESETS_PER_USER = 1 # Each user will have between 1-2 presets
|
||||
@@ -29,7 +32,9 @@ MAX_AGENTS_PER_USER = 10 # Total agents: 500-5000 (NUM_USERS * MIN/MAX_AGENTS)
|
||||
|
||||
# Execution and review records
|
||||
MIN_EXECUTIONS_PER_GRAPH = 1 # Each graph will have between 1-5 execution records
|
||||
MAX_EXECUTIONS_PER_GRAPH = 20 # Total executions: 1000-5000 (TOTAL_GRAPHS * MIN/MAX_EXECUTIONS)
|
||||
MAX_EXECUTIONS_PER_GRAPH = (
|
||||
20 # Total executions: 1000-5000 (TOTAL_GRAPHS * MIN/MAX_EXECUTIONS)
|
||||
)
|
||||
MIN_REVIEWS_PER_VERSION = 1 # Each version will have between 1-3 reviews
|
||||
MAX_REVIEWS_PER_VERSION = 5 # Total reviews depends on number of versions created
|
||||
|
||||
@@ -44,11 +49,11 @@ async def main():
|
||||
for _ in range(NUM_USERS):
|
||||
user = await db.user.create(
|
||||
data={
|
||||
'id': str(faker.uuid4()),
|
||||
'email': faker.unique.email(),
|
||||
'name': faker.name(),
|
||||
'metadata': prisma.Json({}),
|
||||
'integrations': '',
|
||||
"id": str(faker.uuid4()),
|
||||
"email": faker.unique.email(),
|
||||
"name": faker.name(),
|
||||
"metadata": prisma.Json({}),
|
||||
"integrations": "",
|
||||
}
|
||||
)
|
||||
users.append(user)
|
||||
@@ -59,9 +64,9 @@ async def main():
|
||||
for _ in range(NUM_AGENT_BLOCKS):
|
||||
block = await db.agentblock.create(
|
||||
data={
|
||||
'name': f"{faker.word()}_{str(faker.uuid4())[:8]}",
|
||||
'inputSchema': '{}',
|
||||
'outputSchema': '{}',
|
||||
"name": f"{faker.word()}_{str(faker.uuid4())[:8]}",
|
||||
"inputSchema": "{}",
|
||||
"outputSchema": "{}",
|
||||
}
|
||||
)
|
||||
agent_blocks.append(block)
|
||||
@@ -70,32 +75,36 @@ async def main():
|
||||
agent_graphs = []
|
||||
print(f"Inserting {NUM_USERS * MAX_GRAPHS_PER_USER} agent graphs")
|
||||
for user in users:
|
||||
for _ in range(random.randint(MIN_GRAPHS_PER_USER, MAX_GRAPHS_PER_USER)): # Adjust the range to create more graphs per user if desired
|
||||
for _ in range(
|
||||
random.randint(MIN_GRAPHS_PER_USER, MAX_GRAPHS_PER_USER)
|
||||
): # Adjust the range to create more graphs per user if desired
|
||||
graph = await db.agentgraph.create(
|
||||
data={
|
||||
'name': faker.sentence(nb_words=3),
|
||||
'description': faker.text(max_nb_chars=200),
|
||||
'userId': user.id,
|
||||
'isActive': True,
|
||||
'isTemplate': False,
|
||||
"name": faker.sentence(nb_words=3),
|
||||
"description": faker.text(max_nb_chars=200),
|
||||
"userId": user.id,
|
||||
"isActive": True,
|
||||
"isTemplate": False,
|
||||
}
|
||||
)
|
||||
agent_graphs.append(graph)
|
||||
|
||||
# Insert AgentNodes
|
||||
agent_nodes = []
|
||||
print(f"Inserting {NUM_USERS * MAX_GRAPHS_PER_USER * MAX_NODES_PER_GRAPH} agent nodes")
|
||||
print(
|
||||
f"Inserting {NUM_USERS * MAX_GRAPHS_PER_USER * MAX_NODES_PER_GRAPH} agent nodes"
|
||||
)
|
||||
for graph in agent_graphs:
|
||||
num_nodes = random.randint(MIN_NODES_PER_GRAPH, MAX_NODES_PER_GRAPH)
|
||||
for _ in range(num_nodes): # Create 5 AgentNodes per graph
|
||||
block = random.choice(agent_blocks)
|
||||
node = await db.agentnode.create(
|
||||
data={
|
||||
'agentBlockId': block.id,
|
||||
'agentGraphId': graph.id,
|
||||
'agentGraphVersion': graph.version,
|
||||
'constantInput': '{}',
|
||||
'metadata': '{}',
|
||||
"agentBlockId": block.id,
|
||||
"agentGraphId": graph.id,
|
||||
"agentGraphVersion": graph.version,
|
||||
"constantInput": "{}",
|
||||
"metadata": "{}",
|
||||
}
|
||||
)
|
||||
agent_nodes.append(node)
|
||||
@@ -109,12 +118,12 @@ async def main():
|
||||
graph = random.choice(agent_graphs)
|
||||
preset = await db.agentpreset.create(
|
||||
data={
|
||||
'name': faker.sentence(nb_words=3),
|
||||
'description': faker.text(max_nb_chars=200),
|
||||
'userId': user.id,
|
||||
'agentId': graph.id,
|
||||
'agentVersion': graph.version,
|
||||
'isActive': True,
|
||||
"name": faker.sentence(nb_words=3),
|
||||
"description": faker.text(max_nb_chars=200),
|
||||
"userId": user.id,
|
||||
"agentId": graph.id,
|
||||
"agentVersion": graph.version,
|
||||
"isActive": True,
|
||||
}
|
||||
)
|
||||
agent_presets.append(preset)
|
||||
@@ -129,14 +138,14 @@ async def main():
|
||||
preset = random.choice(agent_presets)
|
||||
user_agent = await db.useragent.create(
|
||||
data={
|
||||
'userId': user.id,
|
||||
'agentId': graph.id,
|
||||
'agentVersion': graph.version,
|
||||
'agentPresetId': preset.id,
|
||||
'isFavorite': random.choice([True, False]),
|
||||
'isCreatedByUser': random.choice([True, False]),
|
||||
'isArchived': random.choice([True, False]),
|
||||
'isDeleted': random.choice([True, False]),
|
||||
"userId": user.id,
|
||||
"agentId": graph.id,
|
||||
"agentVersion": graph.version,
|
||||
"agentPresetId": preset.id,
|
||||
"isFavorite": random.choice([True, False]),
|
||||
"isCreatedByUser": random.choice([True, False]),
|
||||
"isArchived": random.choice([True, False]),
|
||||
"isDeleted": random.choice([True, False]),
|
||||
}
|
||||
)
|
||||
user_agents.append(user_agent)
|
||||
@@ -144,24 +153,34 @@ async def main():
|
||||
# Insert AgentGraphExecutions
|
||||
# Insert AgentGraphExecutions
|
||||
agent_graph_executions = []
|
||||
print(f"Inserting {NUM_USERS * MAX_GRAPHS_PER_USER * MAX_EXECUTIONS_PER_GRAPH} agent graph executions")
|
||||
print(
|
||||
f"Inserting {NUM_USERS * MAX_GRAPHS_PER_USER * MAX_EXECUTIONS_PER_GRAPH} agent graph executions"
|
||||
)
|
||||
graph_execution_data = []
|
||||
for graph in agent_graphs:
|
||||
user = random.choice(users)
|
||||
num_executions = random.randint(MIN_EXECUTIONS_PER_GRAPH, MAX_EXECUTIONS_PER_GRAPH)
|
||||
num_executions = random.randint(
|
||||
MIN_EXECUTIONS_PER_GRAPH, MAX_EXECUTIONS_PER_GRAPH
|
||||
)
|
||||
for _ in range(num_executions):
|
||||
matching_presets = [p for p in agent_presets if p.agentId == graph.id]
|
||||
preset = random.choice(matching_presets) if matching_presets and random.random() < 0.5 else None
|
||||
|
||||
graph_execution_data.append({
|
||||
'agentGraphId': graph.id,
|
||||
'agentGraphVersion': graph.version,
|
||||
'userId': user.id,
|
||||
'executionStatus': prisma.enums.AgentExecutionStatus.COMPLETED,
|
||||
'startedAt': faker.date_time_this_year(),
|
||||
'agentPresetId': preset.id if preset else None,
|
||||
})
|
||||
|
||||
preset = (
|
||||
random.choice(matching_presets)
|
||||
if matching_presets and random.random() < 0.5
|
||||
else None
|
||||
)
|
||||
|
||||
graph_execution_data.append(
|
||||
{
|
||||
"agentGraphId": graph.id,
|
||||
"agentGraphVersion": graph.version,
|
||||
"userId": user.id,
|
||||
"executionStatus": prisma.enums.AgentExecutionStatus.COMPLETED,
|
||||
"startedAt": faker.date_time_this_year(),
|
||||
"agentPresetId": preset.id if preset else None,
|
||||
}
|
||||
)
|
||||
|
||||
agent_graph_executions = await db.agentgraphexecution.create_many(
|
||||
data=graph_execution_data
|
||||
)
|
||||
@@ -169,18 +188,24 @@ async def main():
|
||||
agent_graph_executions = await db.agentgraphexecution.find_many()
|
||||
|
||||
# Insert AgentNodeExecutions
|
||||
print(f"Inserting {NUM_USERS * MAX_GRAPHS_PER_USER * MAX_EXECUTIONS_PER_GRAPH} agent node executions")
|
||||
print(
|
||||
f"Inserting {NUM_USERS * MAX_GRAPHS_PER_USER * MAX_EXECUTIONS_PER_GRAPH} agent node executions"
|
||||
)
|
||||
node_execution_data = []
|
||||
for execution in agent_graph_executions:
|
||||
nodes = [node for node in agent_nodes if node.agentGraphId == execution.agentGraphId]
|
||||
nodes = [
|
||||
node for node in agent_nodes if node.agentGraphId == execution.agentGraphId
|
||||
]
|
||||
for node in nodes:
|
||||
node_execution_data.append({
|
||||
'agentGraphExecutionId': execution.id,
|
||||
'agentNodeId': node.id,
|
||||
'executionStatus': prisma.enums.AgentExecutionStatus.COMPLETED,
|
||||
'addedTime': datetime.now(),
|
||||
})
|
||||
|
||||
node_execution_data.append(
|
||||
{
|
||||
"agentGraphExecutionId": execution.id,
|
||||
"agentNodeId": node.id,
|
||||
"executionStatus": prisma.enums.AgentExecutionStatus.COMPLETED,
|
||||
"addedTime": datetime.now(),
|
||||
}
|
||||
)
|
||||
|
||||
agent_node_executions = await db.agentnodeexecution.create_many(
|
||||
data=node_execution_data
|
||||
)
|
||||
@@ -188,43 +213,49 @@ async def main():
|
||||
agent_node_executions = await db.agentnodeexecution.find_many()
|
||||
|
||||
# Insert AgentNodeExecutionInputOutput
|
||||
print(f"Inserting {NUM_USERS * MAX_GRAPHS_PER_USER * MAX_EXECUTIONS_PER_GRAPH} agent node execution input/outputs")
|
||||
print(
|
||||
f"Inserting {NUM_USERS * MAX_GRAPHS_PER_USER * MAX_EXECUTIONS_PER_GRAPH} agent node execution input/outputs"
|
||||
)
|
||||
input_output_data = []
|
||||
for node_execution in agent_node_executions:
|
||||
# Input data
|
||||
input_output_data.append({
|
||||
'name': 'input1',
|
||||
'data': '{}',
|
||||
'time': datetime.now(),
|
||||
'referencedByInputExecId': node_execution.id,
|
||||
})
|
||||
input_output_data.append(
|
||||
{
|
||||
"name": "input1",
|
||||
"data": "{}",
|
||||
"time": datetime.now(),
|
||||
"referencedByInputExecId": node_execution.id,
|
||||
}
|
||||
)
|
||||
# Output data
|
||||
input_output_data.append({
|
||||
'name': 'output1',
|
||||
'data': '{}',
|
||||
'time': datetime.now(),
|
||||
'referencedByOutputExecId': node_execution.id,
|
||||
})
|
||||
input_output_data.append(
|
||||
{
|
||||
"name": "output1",
|
||||
"data": "{}",
|
||||
"time": datetime.now(),
|
||||
"referencedByOutputExecId": node_execution.id,
|
||||
}
|
||||
)
|
||||
|
||||
await db.agentnodeexecutioninputoutput.create_many(
|
||||
data=input_output_data
|
||||
)
|
||||
await db.agentnodeexecutioninputoutput.create_many(data=input_output_data)
|
||||
|
||||
# Insert AgentGraphExecutionSchedules
|
||||
agent_graph_execution_schedules = []
|
||||
print(f"Inserting {NUM_USERS * MAX_GRAPHS_PER_USER} agent graph execution schedules")
|
||||
print(
|
||||
f"Inserting {NUM_USERS * MAX_GRAPHS_PER_USER} agent graph execution schedules"
|
||||
)
|
||||
for graph in agent_graphs:
|
||||
user = random.choice(users)
|
||||
schedule = await db.agentgraphexecutionschedule.create(
|
||||
data={
|
||||
'id': str(faker.uuid4()),
|
||||
'agentGraphId': graph.id,
|
||||
'agentGraphVersion': graph.version,
|
||||
'schedule': '* * * * *',
|
||||
'isEnabled': True,
|
||||
'inputData': '{}',
|
||||
'userId': user.id,
|
||||
'lastUpdated': datetime.now(),
|
||||
"id": str(faker.uuid4()),
|
||||
"agentGraphId": graph.id,
|
||||
"agentGraphVersion": graph.version,
|
||||
"schedule": "* * * * *",
|
||||
"isEnabled": True,
|
||||
"inputData": "{}",
|
||||
"userId": user.id,
|
||||
"lastUpdated": datetime.now(),
|
||||
}
|
||||
)
|
||||
agent_graph_execution_schedules.append(schedule)
|
||||
@@ -236,13 +267,13 @@ async def main():
|
||||
if len(nodes) >= 2:
|
||||
source_node = nodes[0]
|
||||
sink_node = nodes[1]
|
||||
link = await db.agentnodelink.create(
|
||||
await db.agentnodelink.create(
|
||||
data={
|
||||
'agentNodeSourceId': source_node.id,
|
||||
'sourceName': 'output1',
|
||||
'agentNodeSinkId': sink_node.id,
|
||||
'sinkName': 'input1',
|
||||
'isStatic': False,
|
||||
"agentNodeSourceId": source_node.id,
|
||||
"sourceName": "output1",
|
||||
"agentNodeSinkId": sink_node.id,
|
||||
"sinkName": "input1",
|
||||
"isStatic": False,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -250,12 +281,12 @@ async def main():
|
||||
print(f"Inserting {NUM_USERS} analytics details")
|
||||
for user in users:
|
||||
for _ in range(1):
|
||||
analytic_detail = await db.analyticsdetails.create(
|
||||
await db.analyticsdetails.create(
|
||||
data={
|
||||
'userId': user.id,
|
||||
'type': faker.word(),
|
||||
'data': prisma.Json({}),
|
||||
'dataIndex': faker.word(),
|
||||
"userId": user.id,
|
||||
"type": faker.word(),
|
||||
"data": prisma.Json({}),
|
||||
"dataIndex": faker.word(),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -263,12 +294,12 @@ async def main():
|
||||
print(f"Inserting {NUM_USERS} analytics metrics")
|
||||
for user in users:
|
||||
for _ in range(1):
|
||||
metric = await db.analyticsmetrics.create(
|
||||
await db.analyticsmetrics.create(
|
||||
data={
|
||||
'userId': user.id,
|
||||
'analyticMetric': faker.word(),
|
||||
'value': random.uniform(0, 100),
|
||||
'dataString': faker.word(),
|
||||
"userId": user.id,
|
||||
"analyticMetric": faker.word(),
|
||||
"value": random.uniform(0, 100),
|
||||
"dataString": faker.word(),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -277,14 +308,18 @@ async def main():
|
||||
for user in users:
|
||||
for _ in range(1):
|
||||
block = random.choice(agent_blocks)
|
||||
credit = await db.userblockcredit.create(
|
||||
await db.userblockcredit.create(
|
||||
data={
|
||||
'transactionKey': str(faker.uuid4()),
|
||||
'userId': user.id,
|
||||
'blockId': block.id,
|
||||
'amount': random.randint(1, 100),
|
||||
'type': prisma.enums.UserBlockCreditType.TOP_UP if random.random() < 0.5 else prisma.enums.UserBlockCreditType.USAGE,
|
||||
'metadata': prisma.Json({}),
|
||||
"transactionKey": str(faker.uuid4()),
|
||||
"userId": user.id,
|
||||
"blockId": block.id,
|
||||
"amount": random.randint(1, 100),
|
||||
"type": (
|
||||
prisma.enums.UserBlockCreditType.TOP_UP
|
||||
if random.random() < 0.5
|
||||
else prisma.enums.UserBlockCreditType.USAGE
|
||||
),
|
||||
"metadata": prisma.Json({}),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -294,12 +329,12 @@ async def main():
|
||||
for user in users:
|
||||
profile = await db.profile.create(
|
||||
data={
|
||||
'userId': user.id,
|
||||
'name': user.name or faker.name(),
|
||||
'username': faker.unique.user_name(),
|
||||
'description': faker.text(),
|
||||
'links': [faker.url() for _ in range(3)],
|
||||
'avatarUrl': faker.image_url(),
|
||||
"userId": user.id,
|
||||
"name": user.name or faker.name(),
|
||||
"username": faker.unique.user_name(),
|
||||
"description": faker.text(),
|
||||
"links": [faker.url() for _ in range(3)],
|
||||
"avatarUrl": faker.image_url(),
|
||||
}
|
||||
)
|
||||
profiles.append(profile)
|
||||
@@ -311,10 +346,10 @@ async def main():
|
||||
user = random.choice(users)
|
||||
listing = await db.storelisting.create(
|
||||
data={
|
||||
'agentId': graph.id,
|
||||
'agentVersion': graph.version,
|
||||
'owningUserId': user.id,
|
||||
'isApproved': random.choice([True, False]),
|
||||
"agentId": graph.id,
|
||||
"agentVersion": graph.version,
|
||||
"owningUserId": user.id,
|
||||
"isApproved": random.choice([True, False]),
|
||||
}
|
||||
)
|
||||
store_listings.append(listing)
|
||||
@@ -326,19 +361,19 @@ async def main():
|
||||
graph = [g for g in agent_graphs if g.id == listing.agentId][0]
|
||||
version = await db.storelistingversion.create(
|
||||
data={
|
||||
'agentId': graph.id,
|
||||
'agentVersion': graph.version,
|
||||
'slug': faker.slug(),
|
||||
'name': graph.name or faker.sentence(nb_words=3),
|
||||
'subHeading': faker.sentence(),
|
||||
'videoUrl': faker.url(),
|
||||
'imageUrls': [faker.image_url() for _ in range(3)],
|
||||
'description': faker.text(),
|
||||
'categories': [faker.word() for _ in range(3)],
|
||||
'isFeatured': random.choice([True, False]),
|
||||
'isAvailable': True,
|
||||
'isApproved': random.choice([True, False]),
|
||||
'storeListingId': listing.id,
|
||||
"agentId": graph.id,
|
||||
"agentVersion": graph.version,
|
||||
"slug": faker.slug(),
|
||||
"name": graph.name or faker.sentence(nb_words=3),
|
||||
"subHeading": faker.sentence(),
|
||||
"videoUrl": faker.url(),
|
||||
"imageUrls": [faker.image_url() for _ in range(3)],
|
||||
"description": faker.text(),
|
||||
"categories": [faker.word() for _ in range(3)],
|
||||
"isFeatured": random.choice([True, False]),
|
||||
"isAvailable": True,
|
||||
"isApproved": random.choice([True, False]),
|
||||
"storeListingId": listing.id,
|
||||
}
|
||||
)
|
||||
store_listing_versions.append(version)
|
||||
@@ -349,12 +384,12 @@ async def main():
|
||||
num_reviews = random.randint(MIN_REVIEWS_PER_VERSION, MAX_REVIEWS_PER_VERSION)
|
||||
for _ in range(num_reviews):
|
||||
user = random.choice(users)
|
||||
review = await db.storelistingreview.create(
|
||||
await db.storelistingreview.create(
|
||||
data={
|
||||
'storeListingVersionId': version.id,
|
||||
'reviewByUserId': user.id,
|
||||
'score': random.randint(1, 5),
|
||||
'comments': faker.text(),
|
||||
"storeListingVersionId": version.id,
|
||||
"reviewByUserId": user.id,
|
||||
"score": random.randint(1, 5),
|
||||
"comments": faker.text(),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -363,37 +398,44 @@ async def main():
|
||||
for listing in store_listings:
|
||||
version = random.choice(store_listing_versions)
|
||||
reviewer = random.choice(users)
|
||||
status: prisma.enums.SubmissionStatus = random.choice([prisma.enums.SubmissionStatus.PENDING, prisma.enums.SubmissionStatus.APPROVED, prisma.enums.SubmissionStatus.REJECTED])
|
||||
submission = await db.storelistingsubmission.create(
|
||||
status: prisma.enums.SubmissionStatus = random.choice(
|
||||
[
|
||||
prisma.enums.SubmissionStatus.PENDING,
|
||||
prisma.enums.SubmissionStatus.APPROVED,
|
||||
prisma.enums.SubmissionStatus.REJECTED,
|
||||
]
|
||||
)
|
||||
await db.storelistingsubmission.create(
|
||||
data={
|
||||
'storeListingId': listing.id,
|
||||
'storeListingVersionId': version.id,
|
||||
'reviewerId': reviewer.id,
|
||||
'Status': status,
|
||||
'reviewComments': faker.text(),
|
||||
"storeListingId": listing.id,
|
||||
"storeListingVersionId": version.id,
|
||||
"reviewerId": reviewer.id,
|
||||
"Status": status,
|
||||
"reviewComments": faker.text(),
|
||||
}
|
||||
)
|
||||
|
||||
# Insert APIKeys
|
||||
print(f"Inserting {NUM_USERS} api keys")
|
||||
for user in users:
|
||||
api_key = await db.apikey.create(
|
||||
await db.apikey.create(
|
||||
data={
|
||||
'name': faker.word(),
|
||||
'prefix': str(faker.uuid4())[:8],
|
||||
'postfix': str(faker.uuid4())[-8:],
|
||||
'key': str(faker.sha256()),
|
||||
'status': prisma.enums.APIKeyStatus.ACTIVE,
|
||||
'permissions': [
|
||||
"name": faker.word(),
|
||||
"prefix": str(faker.uuid4())[:8],
|
||||
"postfix": str(faker.uuid4())[-8:],
|
||||
"key": str(faker.sha256()),
|
||||
"status": prisma.enums.APIKeyStatus.ACTIVE,
|
||||
"permissions": [
|
||||
prisma.enums.APIKeyPermission.EXECUTE_GRAPH,
|
||||
prisma.enums.APIKeyPermission.READ_GRAPH
|
||||
prisma.enums.APIKeyPermission.READ_GRAPH,
|
||||
],
|
||||
'description': faker.text(),
|
||||
'userId': user.id,
|
||||
"description": faker.text(),
|
||||
"userId": user.id,
|
||||
}
|
||||
)
|
||||
|
||||
await db.disconnect()
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -38,3 +38,38 @@ export async function login(values: z.infer<typeof loginFormSchema>) {
|
||||
redirect("/");
|
||||
});
|
||||
}
|
||||
|
||||
export async function signup(values: z.infer<typeof loginFormSchema>) {
|
||||
"use server";
|
||||
return await Sentry.withServerActionInstrumentation(
|
||||
"signup",
|
||||
{},
|
||||
async () => {
|
||||
const supabase = createServerClient();
|
||||
|
||||
if (!supabase) {
|
||||
redirect("/error");
|
||||
}
|
||||
|
||||
// We are sure that the values are of the correct type because zod validates the form
|
||||
const { data, error } = await supabase.auth.signUp(values);
|
||||
|
||||
if (error) {
|
||||
if (error.message.includes("P0001")) {
|
||||
return "Please join our waitlist for your turn: https://agpt.co/waitlist";
|
||||
}
|
||||
if (error.code?.includes("user_already_exists")) {
|
||||
redirect("/login");
|
||||
}
|
||||
return error.message;
|
||||
}
|
||||
|
||||
if (data.session) {
|
||||
await supabase.auth.setSession(data.session);
|
||||
}
|
||||
|
||||
revalidatePath("/", "layout");
|
||||
redirect("/");
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -203,14 +203,34 @@ export default function LoginPage() {
|
||||
className="flex w-full justify-center"
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
onClick={async () => {
|
||||
setIsLoading(true);
|
||||
const values = form.getValues();
|
||||
const result = await login(values);
|
||||
if (result) {
|
||||
setFeedback(result);
|
||||
}
|
||||
setIsLoading(false);
|
||||
}}
|
||||
>
|
||||
Log in
|
||||
{isLoading ? <FaSpinner className="animate-spin" /> : "Log in"}
|
||||
</Button>
|
||||
<Button
|
||||
className="flex w-full justify-center"
|
||||
type="button"
|
||||
disabled={isLoading}
|
||||
onClick={async () => {
|
||||
setIsLoading(true);
|
||||
const values = form.getValues();
|
||||
const result = await signup(values);
|
||||
if (result) {
|
||||
setFeedback(result);
|
||||
}
|
||||
setIsLoading(false);
|
||||
}}
|
||||
>
|
||||
{isLoading ? <FaSpinner className="animate-spin" /> : "Sign up"}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-full text-center">
|
||||
<Link href={"/signup"} className="w-fit text-xs hover:underline">
|
||||
Create a new Account
|
||||
</Link>
|
||||
</div>
|
||||
</form>
|
||||
<p className="text-sm text-red-500">{feedback}</p>
|
||||
|
||||
@@ -10,6 +10,8 @@ import TallyPopupSimple from "@/components/TallyPopup";
|
||||
import { GoogleAnalytics } from "@next/third-parties/google";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
|
||||
import { createServerClient } from "@/lib/supabase/server";
|
||||
|
||||
// Import Fonts
|
||||
import { GeistSans } from "geist/font/sans";
|
||||
import { GeistMono } from "geist/font/mono";
|
||||
@@ -21,15 +23,22 @@ export const metadata: Metadata = {
|
||||
description: "Your one stop shop to creating AI Agents",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const supabase = createServerClient();
|
||||
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession();
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={cn("antialiased transition-colors", inter.className)}>
|
||||
<Providers
|
||||
initialSession={session}
|
||||
attribute="class"
|
||||
defaultTheme="light"
|
||||
// Feel free to remove this line if you want to use the system theme by default
|
||||
|
||||
@@ -4,19 +4,21 @@ import * as React from "react";
|
||||
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
||||
import { ThemeProviderProps } from "next-themes";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import { AuthProvider } from "@/components/providers/AuthContext";
|
||||
import SupabaseProvider from "@/components/providers/SupabaseProvider";
|
||||
import CredentialsProvider from "@/components/integrations/credentials-provider";
|
||||
import { Session } from "@supabase/supabase-js";
|
||||
|
||||
export function Providers({ children, ...props }: ThemeProviderProps) {
|
||||
export function Providers({
|
||||
children,
|
||||
initialSession,
|
||||
...props
|
||||
}: ThemeProviderProps & { initialSession: Session | null }) {
|
||||
return (
|
||||
<NextThemesProvider {...props}>
|
||||
<SupabaseProvider>
|
||||
<AuthProvider>
|
||||
<CredentialsProvider>
|
||||
<TooltipProvider>{children}</TooltipProvider>
|
||||
</CredentialsProvider>
|
||||
</AuthProvider>
|
||||
<SupabaseProvider initialSession={initialSession}>
|
||||
<CredentialsProvider>
|
||||
<TooltipProvider>{children}</TooltipProvider>
|
||||
</CredentialsProvider>
|
||||
</SupabaseProvider>
|
||||
</NextThemesProvider>
|
||||
);
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
"use server";
|
||||
import { createServerClient } from "@/lib/supabase/server";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { redirect } from "next/navigation";
|
||||
import { z } from "zod";
|
||||
|
||||
const SignupFormSchema = z.object({
|
||||
email: z.string().email().min(2).max(64),
|
||||
password: z.string().min(6).max(64),
|
||||
});
|
||||
|
||||
export async function signup(values: z.infer<typeof SignupFormSchema>) {
|
||||
"use server";
|
||||
return await Sentry.withServerActionInstrumentation(
|
||||
"signup",
|
||||
{},
|
||||
async () => {
|
||||
const supabase = createServerClient();
|
||||
|
||||
if (!supabase) {
|
||||
redirect("/error");
|
||||
}
|
||||
|
||||
// We are sure that the values are of the correct type because zod validates the form
|
||||
const { data, error } = await supabase.auth.signUp(values);
|
||||
|
||||
if (error) {
|
||||
if (error.message.includes("P0001")) {
|
||||
return "Please join our waitlist for your turn: https://agpt.co/waitlist";
|
||||
}
|
||||
if (error.code?.includes("user_already_exists")) {
|
||||
redirect("/login");
|
||||
}
|
||||
return error.message;
|
||||
}
|
||||
|
||||
if (data.session) {
|
||||
await supabase.auth.setSession(data.session);
|
||||
}
|
||||
|
||||
revalidatePath("/", "layout");
|
||||
redirect("/");
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
"use client";
|
||||
import useUser from "@/hooks/useUser";
|
||||
import { signup } from "./actions";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { PasswordInput } from "@/components/PasswordInput";
|
||||
import { FaGoogle, FaGithub, FaDiscord, FaSpinner } from "react-icons/fa";
|
||||
import { useState } from "react";
|
||||
import { useSupabase } from "@/components/providers/SupabaseProvider";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
|
||||
const signupFormSchema = z.object({
|
||||
email: z.string().email().min(2).max(64),
|
||||
password: z.string().min(6).max(64),
|
||||
agreeToTerms: z.boolean().refine((value) => value === true, {
|
||||
message: "You must agree to the Terms of Use and Privacy Policy",
|
||||
}),
|
||||
});
|
||||
|
||||
export default function LoginPage() {
|
||||
const { supabase, isLoading: isSupabaseLoading } = useSupabase();
|
||||
const { user, isLoading: isUserLoading } = useUser();
|
||||
const [feedback, setFeedback] = useState<string | null>(null);
|
||||
const router = useRouter();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const form = useForm<z.infer<typeof signupFormSchema>>({
|
||||
resolver: zodResolver(signupFormSchema),
|
||||
defaultValues: {
|
||||
email: "",
|
||||
password: "",
|
||||
agreeToTerms: false,
|
||||
},
|
||||
});
|
||||
|
||||
if (user) {
|
||||
console.log("User exists, redirecting to home");
|
||||
router.push("/");
|
||||
}
|
||||
|
||||
if (isUserLoading || isSupabaseLoading || user) {
|
||||
return (
|
||||
<div className="flex h-[80vh] items-center justify-center">
|
||||
<FaSpinner className="mr-2 h-16 w-16 animate-spin" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!supabase) {
|
||||
return (
|
||||
<div>
|
||||
User accounts are disabled because Supabase client is unavailable
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
async function handleSignInWithProvider(
|
||||
provider: "google" | "github" | "discord",
|
||||
) {
|
||||
const { data, error } = await supabase!.auth.signInWithOAuth({
|
||||
provider: provider,
|
||||
options: {
|
||||
redirectTo:
|
||||
process.env.AUTH_CALLBACK_URL ??
|
||||
`http://localhost:3000/auth/callback`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!error) {
|
||||
setFeedback(null);
|
||||
return;
|
||||
}
|
||||
setFeedback(error.message);
|
||||
}
|
||||
|
||||
const onSignup = async (data: z.infer<typeof signupFormSchema>) => {
|
||||
if (await form.trigger()) {
|
||||
setIsLoading(true);
|
||||
const error = await signup(data);
|
||||
setIsLoading(false);
|
||||
if (error) {
|
||||
setFeedback(error);
|
||||
return;
|
||||
}
|
||||
setFeedback(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-[80vh] items-center justify-center">
|
||||
<div className="w-full max-w-md space-y-6 rounded-lg p-8 shadow-md">
|
||||
<h1 className="text-lg font-medium">Create a New Account</h1>
|
||||
{/* <div className="mb-6 space-y-2">
|
||||
<Button
|
||||
className="w-full"
|
||||
onClick={() => handleSignInWithProvider("google")}
|
||||
variant="outline"
|
||||
type="button"
|
||||
disabled={isLoading}
|
||||
>
|
||||
<FaGoogle className="mr-2 h-4 w-4" />
|
||||
Sign in with Google
|
||||
</Button>
|
||||
<Button
|
||||
className="w-full"
|
||||
onClick={() => handleSignInWithProvider("github")}
|
||||
variant="outline"
|
||||
type="button"
|
||||
disabled={isLoading}
|
||||
>
|
||||
<FaGithub className="mr-2 h-4 w-4" />
|
||||
Sign in with GitHub
|
||||
</Button>
|
||||
<Button
|
||||
className="w-full"
|
||||
onClick={() => handleSignInWithProvider("discord")}
|
||||
variant="outline"
|
||||
type="button"
|
||||
disabled={isLoading}
|
||||
>
|
||||
<FaDiscord className="mr-2 h-4 w-4" />
|
||||
Sign in with Discord
|
||||
</Button>
|
||||
</div> */}
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSignup)}>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem className="mb-4">
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="user@email.com" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="password"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<FormControl>
|
||||
<PasswordInput placeholder="password" {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Password needs to be at least 6 characters long
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="agreeToTerms"
|
||||
render={({ field }) => (
|
||||
<FormItem className="mt-4 flex flex-row items-start space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="space-y-1 leading-none">
|
||||
<FormLabel>
|
||||
I agree to the{" "}
|
||||
<Link
|
||||
href="https://auto-gpt.notion.site/Terms-of-Use-11400ef5bece80d0b087d7831c5fd6bf"
|
||||
className="underline"
|
||||
>
|
||||
Terms of Use
|
||||
</Link>{" "}
|
||||
and{" "}
|
||||
<Link
|
||||
href="https://www.notion.so/auto-gpt/Privacy-Policy-ab11c9c20dbd4de1a15dcffe84d77984"
|
||||
className="underline"
|
||||
>
|
||||
Privacy Policy
|
||||
</Link>
|
||||
</FormLabel>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="mb-6 mt-8 flex w-full space-x-4">
|
||||
<Button
|
||||
className="flex w-full justify-center"
|
||||
variant="outline"
|
||||
type="button"
|
||||
onClick={form.handleSubmit(onSignup)}
|
||||
disabled={isLoading}
|
||||
>
|
||||
Sign up
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-full text-center">
|
||||
<Link href={"/login"} className="w-fit text-xs hover:underline">
|
||||
Already a member? Log In here
|
||||
</Link>
|
||||
</div>
|
||||
</form>
|
||||
<p className="text-sm text-red-500">{feedback}</p>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { FeaturedStoreCard } from "@/components/agptui/FeaturedStoreCard";
|
||||
import {
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/**
|
||||
* Authentication Context and Provider for Supabase Auth
|
||||
*
|
||||
* This module provides authentication state management using Supabase.
|
||||
*
|
||||
* Usage:
|
||||
* 1. Wrap your app with AuthProvider:
|
||||
* ```tsx
|
||||
* <AuthProvider>
|
||||
* <App />
|
||||
* </AuthProvider>
|
||||
* ```
|
||||
*
|
||||
* 2. Access auth state in child components using useAuth hook:
|
||||
* ```tsx
|
||||
* const MyComponent = () => {
|
||||
* const { user, session, role } = useAuth();
|
||||
*
|
||||
* if (!user) return <div>Please log in</div>;
|
||||
*
|
||||
* return <div>Welcome {user.email}</div>;
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* The context provides:
|
||||
* - user: Current authenticated User object or null
|
||||
* - session: Current Session object or null
|
||||
* - role: User's role string or null
|
||||
*/
|
||||
|
||||
import { User, Session } from "@supabase/supabase-js";
|
||||
import { createContext, useContext } from "react";
|
||||
import { createServerClient } from "@/lib/supabase/server";
|
||||
|
||||
interface AuthContextType {
|
||||
user: User | null;
|
||||
session: Session | null;
|
||||
role: string | null;
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||
|
||||
export async function AuthProvider({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const supabase = createServerClient();
|
||||
|
||||
if (!supabase) {
|
||||
console.error("Could not create Supabase client");
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
data: { user },
|
||||
error: userError,
|
||||
} = await supabase.auth.getUser();
|
||||
const {
|
||||
data: { session },
|
||||
error: sessionError,
|
||||
} = await supabase.auth.getSession();
|
||||
|
||||
if (userError) {
|
||||
console.error("Error fetching user:", userError);
|
||||
}
|
||||
|
||||
if (sessionError) {
|
||||
console.error("Error fetching session:", sessionError);
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthContext.Provider
|
||||
value={{
|
||||
user: user || null,
|
||||
session: session || null,
|
||||
role: user?.role || null,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useAuth = () => {
|
||||
const context = useContext(AuthContext);
|
||||
if (context === undefined) {
|
||||
throw new Error("useAuth must be used inside AuthProvider");
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { createClient } from "@/lib/supabase/client";
|
||||
import { SupabaseClient } from "@supabase/supabase-js";
|
||||
import { Session } from "@supabase/supabase-js";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { createContext, useContext, useEffect, useState } from "react";
|
||||
import AutoGPTServerAPI from "@/lib/autogpt-server-api";
|
||||
@@ -15,9 +16,12 @@ const Context = createContext<SupabaseContextType | undefined>(undefined);
|
||||
|
||||
export default function SupabaseProvider({
|
||||
children,
|
||||
initialSession,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
initialSession: Session | null;
|
||||
}) {
|
||||
const [session, setSession] = useState<Session | null>(initialSession);
|
||||
const [supabase, setSupabase] = useState<SupabaseClient | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const router = useRouter();
|
||||
@@ -34,6 +38,7 @@ export default function SupabaseProvider({
|
||||
const {
|
||||
data: { subscription },
|
||||
} = client.auth.onAuthStateChange((event, session) => {
|
||||
setSession(session);
|
||||
if (event === "SIGNED_IN") {
|
||||
api.createUser();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,13 @@ import {
|
||||
NodeExecutionResult,
|
||||
OAuth2Credentials,
|
||||
User,
|
||||
StoreAgentsResponse,
|
||||
StoreAgentDetails,
|
||||
CreatorsResponse,
|
||||
CreatorDetails,
|
||||
StoreSubmissionsResponse,
|
||||
StoreSubmissionRequest,
|
||||
StoreSubmission,
|
||||
} from "./types";
|
||||
|
||||
export default class BaseAutoGPTServerAPI {
|
||||
@@ -28,7 +35,7 @@ export default class BaseAutoGPTServerAPI {
|
||||
|
||||
constructor(
|
||||
baseUrl: string = process.env.NEXT_PUBLIC_AGPT_SERVER_URL ||
|
||||
"http://localhost:8006/api/v1",
|
||||
"http://localhost:8006/api/",
|
||||
wsUrl: string = process.env.NEXT_PUBLIC_AGPT_WS_SERVER_URL ||
|
||||
"ws://localhost:8001/ws",
|
||||
supabaseClient: SupabaseClient | null = null,
|
||||
@@ -234,6 +241,70 @@ export default class BaseAutoGPTServerAPI {
|
||||
return this._request("POST", "/analytics/log_raw_analytics", analytic);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
/////////// V2 STORE API /////////////////
|
||||
/////////////////////////////////////////
|
||||
|
||||
getStoreAgents(params?: {
|
||||
featured?: boolean;
|
||||
creator?: string;
|
||||
sorted_by?: string;
|
||||
search_query?: string;
|
||||
category?: string;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}): Promise<StoreAgentsResponse> {
|
||||
return this._get("/store/agents", params);
|
||||
}
|
||||
|
||||
getStoreAgent(
|
||||
username: string,
|
||||
agentName: string,
|
||||
): Promise<StoreAgentDetails> {
|
||||
return this._get(`/store/agents/${username}/${agentName}`);
|
||||
}
|
||||
|
||||
getStoreCreators(params?: {
|
||||
featured?: boolean;
|
||||
search_query?: string;
|
||||
sorted_by?: string;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}): Promise<CreatorsResponse> {
|
||||
return this._get("/store/creators", params);
|
||||
}
|
||||
|
||||
getStoreCreator(username: string): Promise<CreatorDetails> {
|
||||
return this._get(`/store/creator/${username}`);
|
||||
}
|
||||
|
||||
getStoreSubmissions(params?: {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}): Promise<StoreSubmissionsResponse> {
|
||||
return this._get("/store/submissions", params);
|
||||
}
|
||||
|
||||
createStoreSubmission(
|
||||
submission: StoreSubmissionRequest,
|
||||
): Promise<StoreSubmission> {
|
||||
return this._request("POST", "/store/submissions", submission);
|
||||
}
|
||||
|
||||
uploadStoreSubmissionMedia(file: File): Promise<string> {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
return this._request("POST", "/store/submissions/media", formData);
|
||||
}
|
||||
|
||||
updateStoreProfile(profile: CreatorDetails): Promise<CreatorDetails> {
|
||||
return this._request("PUT", "/store/profile", profile);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
/////////// INTERNAL FUNCTIONS ////////////
|
||||
//////////////////////////////??///////////
|
||||
|
||||
private async _get(path: string, query?: Record<string, any>) {
|
||||
return this._request("GET", path, query);
|
||||
}
|
||||
|
||||
@@ -312,3 +312,93 @@ export type AnalyticsDetails = {
|
||||
data: { [key: string]: any };
|
||||
index: string;
|
||||
};
|
||||
|
||||
export type Pagination = {
|
||||
total_items: number;
|
||||
total_pages: number;
|
||||
current_page: number;
|
||||
page_size: number;
|
||||
};
|
||||
|
||||
export type StoreAgent = {
|
||||
slug: string;
|
||||
agent_name: string;
|
||||
agent_image: string;
|
||||
creator: string;
|
||||
creator_avatar: string;
|
||||
sub_heading: string;
|
||||
description: string;
|
||||
runs: number;
|
||||
rating: number;
|
||||
};
|
||||
|
||||
export type StoreAgentsResponse = {
|
||||
agents: StoreAgent[];
|
||||
pagination: Pagination;
|
||||
};
|
||||
|
||||
export type StoreAgentDetails = {
|
||||
slug: string;
|
||||
agent_name: string;
|
||||
agent_video: string;
|
||||
agent_image: string[];
|
||||
creator: string;
|
||||
creator_avatar: string;
|
||||
sub_heading: string;
|
||||
description: string;
|
||||
categories: string[];
|
||||
runs: number;
|
||||
rating: number;
|
||||
versions: string[];
|
||||
};
|
||||
|
||||
export type Creator = {
|
||||
name: string;
|
||||
username: string;
|
||||
description: string;
|
||||
avatar_url: string;
|
||||
num_agents: number;
|
||||
};
|
||||
|
||||
export type CreatorsResponse = {
|
||||
creators: Creator[];
|
||||
pagination: Pagination;
|
||||
};
|
||||
|
||||
export type CreatorDetails = {
|
||||
name: string;
|
||||
username: string;
|
||||
description: string;
|
||||
links: string[];
|
||||
avatar_url: string;
|
||||
agent_rating: number;
|
||||
agent_runs: number;
|
||||
top_categories: string[];
|
||||
};
|
||||
|
||||
export type StoreSubmission = {
|
||||
name: string;
|
||||
description: string;
|
||||
image_urls: string[];
|
||||
date_submitted: string;
|
||||
status: string;
|
||||
runs: number;
|
||||
rating: number;
|
||||
};
|
||||
|
||||
export type StoreSubmissionsResponse = {
|
||||
submissions: StoreSubmission[];
|
||||
pagination: Pagination;
|
||||
};
|
||||
|
||||
export type StoreSubmissionRequest = {
|
||||
agent_id: string;
|
||||
agent_version: number;
|
||||
slug: string;
|
||||
name: string;
|
||||
sub_heading: string;
|
||||
video_url?: string;
|
||||
image_urls: string[];
|
||||
description: string;
|
||||
categories: string[];
|
||||
};
|
||||
|
||||
@@ -31,6 +31,6 @@ export function createServerClient() {
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
return null;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user