from datetime import datetime from fastapi import FastAPI, HTTPException, Query, Response, status from .models import ChatMessage, Favorite, Follow, Prompt, Rating, User from .schemas import ( AuthResponse, ChatMessageCreateRequest, ChatMessageResponse, CreatorDetailResponse, FavoriteCreateRequest, FavoriteResponse, FollowResponse, LoginRequest, ProfileUpdateRequest, PromptCreateRequest, PromptResponse, PromptUpdateRequest, RatingCreateRequest, RatingResponse, RegisterRequest, UserResponse, ) from .store import store app = FastAPI(title="OnlyPrompt API", version="1.0.0") def get_current_user() -> User: user = store.users.get(store.current_user_id) if user is None: raise HTTPException(status_code=404, detail="Current user not found.") return user @app.post("/api/auth/register", response_model=AuthResponse, status_code=status.HTTP_201_CREATED) def register(payload: RegisterRequest) -> AuthResponse: if any(user.email == payload.email for user in store.users.values()): raise HTTPException(status_code=400, detail="Email already exists.") if any(user.username == payload.username for user in store.users.values()): raise HTTPException(status_code=400, detail="Username already exists.") now = datetime.utcnow() user_id = store.next_id("user") user = User( id=user_id, email=payload.email, password_hash=payload.password, full_name=payload.full_name, username=payload.username, bio="", location="", avatar_url="", role=payload.role, is_verified=False, created_at=now, ) store.users[user_id] = user store.current_user_id = user_id return AuthResponse(message="User created successfully.", user=UserResponse.model_validate(user)) @app.post("/api/auth/login", response_model=AuthResponse) def login(payload: LoginRequest) -> AuthResponse: user = next((item for item in store.users.values() if item.email == payload.email), None) if user is None or user.password_hash != payload.password: raise HTTPException(status_code=401, detail="Invalid email or password.") store.current_user_id = user.id return AuthResponse(message="Login successful.", user=UserResponse.model_validate(user)) @app.get("/api/prompts", response_model=list[PromptResponse]) def list_prompts( category: str | None = Query(default=None), search: str | None = Query(default=None), ) -> list[PromptResponse]: prompts = list(store.prompts.values()) if category: prompts = [prompt for prompt in prompts if prompt.category.lower() == category.lower()] if search: term = search.lower() prompts = [ prompt for prompt in prompts if term in prompt.title.lower() or term in prompt.description.lower() ] return [PromptResponse.model_validate(prompt) for prompt in prompts] @app.get("/api/prompts/{prompt_id}", response_model=PromptResponse) def get_prompt(prompt_id: int) -> PromptResponse: prompt = store.prompts.get(prompt_id) if prompt is None: raise HTTPException(status_code=404, detail="Prompt not found.") return PromptResponse.model_validate(prompt) @app.post("/api/prompts", response_model=PromptResponse, status_code=status.HTTP_201_CREATED) def create_prompt(payload: PromptCreateRequest) -> PromptResponse: creator = store.users.get(payload.creator_id) if creator is None or creator.role != "creator": raise HTTPException(status_code=404, detail="Creator not found.") prompt = Prompt( id=store.next_id("prompt"), title=payload.title, description=payload.description, content=payload.content, image_url=payload.image_url, category=payload.category, price=payload.price, creator_id=payload.creator_id, created_at=datetime.utcnow(), ) store.prompts[prompt.id] = prompt return PromptResponse.model_validate(prompt) @app.put("/api/prompts/{prompt_id}", response_model=PromptResponse) def update_prompt(prompt_id: int, payload: PromptUpdateRequest) -> PromptResponse: prompt = store.prompts.get(prompt_id) if prompt is None: raise HTTPException(status_code=404, detail="Prompt not found.") updates = payload.model_dump(exclude_unset=True) for field, value in updates.items(): setattr(prompt, field, value) return PromptResponse.model_validate(prompt) @app.delete("/api/prompts/{prompt_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_prompt(prompt_id: int) -> Response: if prompt_id not in store.prompts: raise HTTPException(status_code=404, detail="Prompt not found.") del store.prompts[prompt_id] return Response(status_code=status.HTTP_204_NO_CONTENT) @app.get("/api/creators", response_model=list[UserResponse]) def list_creators(sort: str | None = Query(default=None)) -> list[UserResponse]: creators = [user for user in store.users.values() if user.role == "creator"] if sort == "new": creators.sort(key=lambda item: item.created_at, reverse=True) elif sort == "popular": creators.sort( key=lambda item: sum(1 for follow in store.follows.values() if follow.creator_id == item.id), reverse=True, ) elif sort == "top_rated": def creator_rating(user: User) -> float: creator_prompt_ids = [prompt.id for prompt in store.prompts.values() if prompt.creator_id == user.id] ratings = [rating.score for rating in store.ratings.values() if rating.prompt_id in creator_prompt_ids] return sum(ratings) / len(ratings) if ratings else 0 creators.sort(key=creator_rating, reverse=True) return [UserResponse.model_validate(creator) for creator in creators] @app.get("/api/creators/{creator_id}", response_model=CreatorDetailResponse) def get_creator(creator_id: int) -> CreatorDetailResponse: creator = store.users.get(creator_id) if creator is None or creator.role != "creator": raise HTTPException(status_code=404, detail="Creator not found.") prompts = [prompt for prompt in store.prompts.values() if prompt.creator_id == creator_id] return CreatorDetailResponse( creator=UserResponse.model_validate(creator), prompts=[PromptResponse.model_validate(prompt) for prompt in prompts], ) @app.get("/api/prompts/{prompt_id}/ratings", response_model=list[RatingResponse]) def list_ratings(prompt_id: int) -> list[RatingResponse]: if prompt_id not in store.prompts: raise HTTPException(status_code=404, detail="Prompt not found.") ratings = [rating for rating in store.ratings.values() if rating.prompt_id == prompt_id] return [RatingResponse.model_validate(rating) for rating in ratings] @app.post("/api/prompts/{prompt_id}/ratings", response_model=RatingResponse, status_code=status.HTTP_201_CREATED) def create_rating(prompt_id: int, payload: RatingCreateRequest) -> RatingResponse: if prompt_id not in store.prompts: raise HTTPException(status_code=404, detail="Prompt not found.") if payload.user_id not in store.users: raise HTTPException(status_code=404, detail="User not found.") rating = Rating( id=store.next_id("rating"), prompt_id=prompt_id, user_id=payload.user_id, score=payload.score, comment=payload.comment, created_at=datetime.utcnow(), ) store.ratings[rating.id] = rating return RatingResponse.model_validate(rating) @app.get("/api/favorites", response_model=list[FavoriteResponse]) def list_favorites() -> list[FavoriteResponse]: current_user = get_current_user() favorites = [item for item in store.favorites.values() if item.user_id == current_user.id] return [FavoriteResponse.model_validate(item) for item in favorites] @app.post("/api/favorites", response_model=FavoriteResponse, status_code=status.HTTP_201_CREATED) def create_favorite(payload: FavoriteCreateRequest) -> FavoriteResponse: current_user = get_current_user() if payload.prompt_id not in store.prompts: raise HTTPException(status_code=404, detail="Prompt not found.") existing = next( ( item for item in store.favorites.values() if item.user_id == current_user.id and item.prompt_id == payload.prompt_id ), None, ) if existing is not None: return FavoriteResponse.model_validate(existing) favorite = Favorite( id=store.next_id("favorite"), user_id=current_user.id, prompt_id=payload.prompt_id, created_at=datetime.utcnow(), ) store.favorites[favorite.id] = favorite return FavoriteResponse.model_validate(favorite) @app.delete("/api/favorites/{prompt_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_favorite(prompt_id: int) -> Response: current_user = get_current_user() favorite_id = next( ( item.id for item in store.favorites.values() if item.user_id == current_user.id and item.prompt_id == prompt_id ), None, ) if favorite_id is None: raise HTTPException(status_code=404, detail="Favorite not found.") del store.favorites[favorite_id] return Response(status_code=status.HTTP_204_NO_CONTENT) @app.post("/api/follows/{creator_id}", response_model=FollowResponse, status_code=status.HTTP_201_CREATED) def create_follow(creator_id: int) -> FollowResponse: current_user = get_current_user() creator = store.users.get(creator_id) if creator is None or creator.role != "creator": raise HTTPException(status_code=404, detail="Creator not found.") existing = next( ( item for item in store.follows.values() if item.follower_id == current_user.id and item.creator_id == creator_id ), None, ) if existing is not None: return FollowResponse.model_validate(existing) follow = Follow( id=store.next_id("follow"), follower_id=current_user.id, creator_id=creator_id, created_at=datetime.utcnow(), ) store.follows[follow.id] = follow return FollowResponse.model_validate(follow) @app.delete("/api/follows/{creator_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_follow(creator_id: int) -> Response: current_user = get_current_user() follow_id = next( ( item.id for item in store.follows.values() if item.follower_id == current_user.id and item.creator_id == creator_id ), None, ) if follow_id is None: raise HTTPException(status_code=404, detail="Follow not found.") del store.follows[follow_id] return Response(status_code=status.HTTP_204_NO_CONTENT) @app.get("/api/profile", response_model=UserResponse) def get_profile() -> UserResponse: return UserResponse.model_validate(get_current_user()) @app.put("/api/profile", response_model=UserResponse) def update_profile(payload: ProfileUpdateRequest) -> UserResponse: user = get_current_user() updates = payload.model_dump(exclude_unset=True) for field, value in updates.items(): setattr(user, field, value) return UserResponse.model_validate(user) @app.get("/api/chats/{user_id}", response_model=list[ChatMessageResponse]) def get_chat(user_id: int) -> list[ChatMessageResponse]: current_user = get_current_user() if user_id not in store.users: raise HTTPException(status_code=404, detail="User not found.") messages = [ message for message in store.chat_messages.values() if {message.sender_id, message.receiver_id} == {current_user.id, user_id} ] messages.sort(key=lambda item: item.created_at) return [ChatMessageResponse.model_validate(message) for message in messages] @app.post("/api/chats", response_model=ChatMessageResponse, status_code=status.HTTP_201_CREATED) def create_chat_message(payload: ChatMessageCreateRequest) -> ChatMessageResponse: current_user = get_current_user() if payload.receiver_id not in store.users: raise HTTPException(status_code=404, detail="Receiver not found.") message = ChatMessage( id=store.next_id("chat_message"), sender_id=current_user.id, receiver_id=payload.receiver_id, content=payload.content, created_at=datetime.utcnow(), ) store.chat_messages[message.id] = message return ChatMessageResponse.model_validate(message)