|
|
from typing import Annotated |
|
|
from uuid import UUID |
|
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query |
|
|
from loguru import logger |
|
|
|
|
|
from langflow.api.utils import CurrentActiveUser, check_langflow_version |
|
|
from langflow.services.auth import utils as auth_utils |
|
|
from langflow.services.deps import get_settings_service, get_store_service |
|
|
from langflow.services.store.exceptions import CustomError |
|
|
from langflow.services.store.schema import ( |
|
|
CreateComponentResponse, |
|
|
DownloadComponentResponse, |
|
|
ListComponentResponseModel, |
|
|
StoreComponentCreate, |
|
|
TagResponse, |
|
|
UsersLikesResponse, |
|
|
) |
|
|
|
|
|
router = APIRouter(prefix="/store", tags=["Components Store"]) |
|
|
|
|
|
|
|
|
def get_user_store_api_key(user: CurrentActiveUser): |
|
|
if not user.store_api_key: |
|
|
raise HTTPException(status_code=400, detail="You must have a store API key set.") |
|
|
try: |
|
|
return auth_utils.decrypt_api_key(user.store_api_key, get_settings_service()) |
|
|
except Exception as e: |
|
|
raise HTTPException(status_code=500, detail="Failed to decrypt API key. Please set a new one.") from e |
|
|
|
|
|
|
|
|
def get_optional_user_store_api_key(user: CurrentActiveUser): |
|
|
if not user.store_api_key: |
|
|
return None |
|
|
try: |
|
|
return auth_utils.decrypt_api_key(user.store_api_key, get_settings_service()) |
|
|
except Exception: |
|
|
logger.exception("Failed to decrypt API key") |
|
|
return user.store_api_key |
|
|
|
|
|
|
|
|
@router.get("/check/") |
|
|
async def check_if_store_is_enabled(): |
|
|
return { |
|
|
"enabled": get_settings_service().settings.store, |
|
|
} |
|
|
|
|
|
|
|
|
@router.get("/check/api_key") |
|
|
async def check_if_store_has_api_key( |
|
|
api_key: Annotated[str | None, Depends(get_optional_user_store_api_key)], |
|
|
): |
|
|
if api_key is None: |
|
|
return {"has_api_key": False, "is_valid": False} |
|
|
|
|
|
try: |
|
|
is_valid = await get_store_service().check_api_key(api_key) |
|
|
except Exception as e: |
|
|
raise HTTPException(status_code=400, detail=str(e)) from e |
|
|
|
|
|
return {"has_api_key": api_key is not None, "is_valid": is_valid} |
|
|
|
|
|
|
|
|
@router.post("/components/", status_code=201) |
|
|
async def share_component( |
|
|
component: StoreComponentCreate, |
|
|
store_api_key: Annotated[str, Depends(get_user_store_api_key)], |
|
|
) -> CreateComponentResponse: |
|
|
try: |
|
|
await check_langflow_version(component) |
|
|
return await get_store_service().upload(store_api_key, component) |
|
|
except Exception as exc: |
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc |
|
|
|
|
|
|
|
|
@router.patch("/components/{component_id}", status_code=201) |
|
|
async def update_shared_component( |
|
|
component_id: UUID, |
|
|
component: StoreComponentCreate, |
|
|
store_api_key: Annotated[str, Depends(get_user_store_api_key)], |
|
|
) -> CreateComponentResponse: |
|
|
try: |
|
|
await check_langflow_version(component) |
|
|
return await get_store_service().update(store_api_key, component_id, component) |
|
|
except Exception as exc: |
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc |
|
|
|
|
|
|
|
|
@router.get("/components/") |
|
|
async def get_components( |
|
|
*, |
|
|
component_id: Annotated[str | None, Query()] = None, |
|
|
search: Annotated[str | None, Query()] = None, |
|
|
private: Annotated[bool | None, Query()] = None, |
|
|
is_component: Annotated[bool | None, Query()] = None, |
|
|
tags: Annotated[list[str] | None, Query()] = None, |
|
|
sort: Annotated[list[str] | None, Query()] = None, |
|
|
liked: Annotated[bool, Query()] = False, |
|
|
filter_by_user: Annotated[bool, Query()] = False, |
|
|
fields: Annotated[list[str] | None, Query()] = None, |
|
|
page: int = 1, |
|
|
limit: int = 10, |
|
|
store_api_key: Annotated[str | None, Depends(get_optional_user_store_api_key)], |
|
|
) -> ListComponentResponseModel: |
|
|
try: |
|
|
return await get_store_service().get_list_component_response_model( |
|
|
component_id=component_id, |
|
|
search=search, |
|
|
private=private, |
|
|
is_component=is_component, |
|
|
fields=fields, |
|
|
tags=tags, |
|
|
sort=sort, |
|
|
liked=liked, |
|
|
filter_by_user=filter_by_user, |
|
|
page=page, |
|
|
limit=limit, |
|
|
store_api_key=store_api_key, |
|
|
) |
|
|
except CustomError as exc: |
|
|
raise HTTPException(status_code=exc.status_code, detail=str(exc)) from exc |
|
|
except Exception as exc: |
|
|
raise HTTPException(status_code=500, detail=str(exc)) from exc |
|
|
|
|
|
|
|
|
@router.get("/components/{component_id}") |
|
|
async def download_component( |
|
|
component_id: UUID, |
|
|
store_api_key: Annotated[str, Depends(get_user_store_api_key)], |
|
|
) -> DownloadComponentResponse: |
|
|
try: |
|
|
component = await get_store_service().download(store_api_key, component_id) |
|
|
except CustomError as exc: |
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc |
|
|
except Exception as exc: |
|
|
raise HTTPException(status_code=500, detail=str(exc)) from exc |
|
|
|
|
|
if component is None: |
|
|
raise HTTPException(status_code=400, detail="Component not found") |
|
|
|
|
|
return component |
|
|
|
|
|
|
|
|
@router.get("/tags", response_model=list[TagResponse]) |
|
|
async def get_tags(): |
|
|
try: |
|
|
return await get_store_service().get_tags() |
|
|
except CustomError as exc: |
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc |
|
|
except Exception as exc: |
|
|
raise HTTPException(status_code=500, detail=str(exc)) from exc |
|
|
|
|
|
|
|
|
@router.get("/users/likes", response_model=list[UsersLikesResponse]) |
|
|
async def get_list_of_components_liked_by_user( |
|
|
store_api_key: Annotated[str, Depends(get_user_store_api_key)], |
|
|
): |
|
|
try: |
|
|
return await get_store_service().get_user_likes(store_api_key) |
|
|
except CustomError as exc: |
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc |
|
|
except Exception as exc: |
|
|
raise HTTPException(status_code=500, detail=str(exc)) from exc |
|
|
|
|
|
|
|
|
@router.post("/users/likes/{component_id}") |
|
|
async def like_component( |
|
|
component_id: UUID, |
|
|
store_api_key: Annotated[str, Depends(get_user_store_api_key)], |
|
|
) -> UsersLikesResponse: |
|
|
try: |
|
|
store_service = get_store_service() |
|
|
result = await store_service.like_component(store_api_key, str(component_id)) |
|
|
likes_count = await store_service.get_component_likes_count(str(component_id), store_api_key) |
|
|
|
|
|
return UsersLikesResponse(likes_count=likes_count, liked_by_user=result) |
|
|
except CustomError as exc: |
|
|
raise HTTPException(status_code=exc.status_code, detail=str(exc)) from exc |
|
|
except Exception as exc: |
|
|
raise HTTPException(status_code=500, detail=str(exc)) from exc |
|
|
|