347 lines
12 KiB
Python
347 lines
12 KiB
Python
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)
|