|
4 | 4 | import io |
5 | 5 | import threading |
6 | 6 | import zipfile |
| 7 | +from collections.abc import Collection |
7 | 8 | from typing import Annotated |
8 | 9 | from uuid import UUID |
9 | 10 |
|
|
49 | 50 | from langflow.api.v1.mappers.deployments.sync import retry_flow_operation_on_deployment_guard |
50 | 51 | from langflow.api.v1.schemas import FlowListCreate |
51 | 52 | from langflow.initial_setup.constants import STARTER_FOLDER_NAME |
52 | | -from langflow.services.auth.utils import get_current_active_user |
| 53 | +from langflow.services.auth.utils import get_current_active_user, get_optional_user |
53 | 54 | from langflow.services.authorization import ( |
54 | 55 | FlowAction, |
55 | 56 | ensure_flow_permission, |
|
78 | 79 | # and FlowVersionError from the flow_version modules. |
79 | 80 | from langflow.services.database.models.folder.constants import DEFAULT_FOLDER_NAME |
80 | 81 | from langflow.services.database.models.folder.model import Folder |
81 | | -from langflow.services.deps import get_settings_service, get_storage_service |
| 82 | +from langflow.services.database.models.user.model import User |
| 83 | +from langflow.services.deps import get_catalog_policy_service, get_settings_service, get_storage_service |
82 | 84 | from langflow.services.storage.service import StorageService |
83 | 85 | from langflow.utils.compression import compress_response |
84 | 86 | from langflow.utils.i18n import translate_flow_notes, translate_starter_flows |
@@ -906,26 +908,60 @@ async def download_multiple_file( |
906 | 908 | _starter_flows_lock = asyncio.Lock() |
907 | 909 |
|
908 | 910 |
|
| 911 | +def _filter_basic_examples_by_catalog_policy( |
| 912 | + flows: list[FlowRead], |
| 913 | + *, |
| 914 | + blocked_template_keys: Collection[str], |
| 915 | +) -> list[FlowRead]: |
| 916 | + """Return a request-local view without exact blocked template keys.""" |
| 917 | + return [flow for flow in flows if flow.name_key not in blocked_template_keys] |
| 918 | + |
| 919 | + |
909 | 920 | @router.get("/basic_examples/", response_model=list[FlowRead], status_code=200) |
910 | 921 | async def read_basic_examples( |
911 | 922 | *, |
912 | 923 | session: DbSession, |
913 | 924 | request: Request, |
| 925 | + user: Annotated[User | None, Depends(get_optional_user)], |
| 926 | + include_blocked: bool = False, |
914 | 927 | ): |
915 | 928 | """Retrieve a list of basic example flows.""" |
| 929 | + if include_blocked and (user is None or not user.is_superuser): |
| 930 | + raise HTTPException( |
| 931 | + status_code=403, |
| 932 | + detail="Only superusers can include blocked catalog templates.", |
| 933 | + ) |
| 934 | + |
| 935 | + catalog_policy_snapshot = get_catalog_policy_service().snapshot |
916 | 936 | locale = getattr(request.state, "locale", "en") |
917 | 937 | translated_cache_key = f"starter_flows_{locale}" |
918 | 938 |
|
919 | 939 | # Fast path: translated result already cached for this locale |
920 | 940 | cached_translated = _starter_flows_translated_cache.get(translated_cache_key) |
921 | 941 | if cached_translated is not CACHE_MISS: |
922 | | - return compress_response(cached_translated) |
| 942 | + visible_flows = ( |
| 943 | + cached_translated |
| 944 | + if include_blocked |
| 945 | + else _filter_basic_examples_by_catalog_policy( |
| 946 | + cached_translated, |
| 947 | + blocked_template_keys=catalog_policy_snapshot.blocked_template_keys, |
| 948 | + ) |
| 949 | + ) |
| 950 | + return compress_response(visible_flows) |
923 | 951 |
|
924 | 952 | async with _starter_flows_lock: |
925 | 953 | # Double-check inside lock to prevent thundering herd |
926 | 954 | cached_translated = _starter_flows_translated_cache.get(translated_cache_key) |
927 | 955 | if cached_translated is not CACHE_MISS: |
928 | | - return compress_response(cached_translated) |
| 956 | + visible_flows = ( |
| 957 | + cached_translated |
| 958 | + if include_blocked |
| 959 | + else _filter_basic_examples_by_catalog_policy( |
| 960 | + cached_translated, |
| 961 | + blocked_template_keys=catalog_policy_snapshot.blocked_template_keys, |
| 962 | + ) |
| 963 | + ) |
| 964 | + return compress_response(visible_flows) |
929 | 965 |
|
930 | 966 | # Ensure raw DB data is cached |
931 | 967 | cached_flow_reads = _starter_flows_cache.get("starter_flows") |
@@ -967,7 +1003,15 @@ async def read_basic_examples( |
967 | 1003 |
|
968 | 1004 | _starter_flows_translated_cache.set(translated_cache_key, result) |
969 | 1005 |
|
970 | | - return compress_response(result) |
| 1006 | + visible_flows = ( |
| 1007 | + result |
| 1008 | + if include_blocked |
| 1009 | + else _filter_basic_examples_by_catalog_policy( |
| 1010 | + result, |
| 1011 | + blocked_template_keys=catalog_policy_snapshot.blocked_template_keys, |
| 1012 | + ) |
| 1013 | + ) |
| 1014 | + return compress_response(visible_flows) |
971 | 1015 |
|
972 | 1016 |
|
973 | 1017 | @router.post("/expand/", status_code=200, dependencies=[Depends(get_current_active_user)], include_in_schema=False) |
|
0 commit comments