mirror of
https://github.com/Donkie/Spoolman.git
synced 2026-10-23 10:32:19 -05:00
The external SpoolmanDB catalog (filaments.json, thousands of entries) was served whole via GET /external/filament and filtered in the browser, so every client downloaded the entire catalog just to search it. Add a backend GET /external/filament/search?query=&limit= endpoint that filters the catalog server-side (same word-AND, case-insensitive semantics as the old client filter), memoizing the parsed catalog by file mtime. client_v2 now calls it instead of downloading the whole file. The existing /external/filament endpoint is unchanged, so external API consumers are unaffected. The small materials.json is still fetched whole for new-filament presets. Claude-Session: https://claude.ai/code/session_01VQQtwPBPiMCoZCpBbbqS64
72 lines
1.7 KiB
Python
72 lines
1.7 KiB
Python
"""External database API."""
|
|
|
|
import logging
|
|
from typing import Annotated
|
|
|
|
from fastapi import APIRouter, Query
|
|
from fastapi.responses import FileResponse
|
|
|
|
from spoolman.externaldb import (
|
|
ExternalFilament,
|
|
ExternalMaterial,
|
|
get_filaments_file,
|
|
get_materials_file,
|
|
search_filaments,
|
|
)
|
|
|
|
router = APIRouter(
|
|
prefix="/external",
|
|
tags=["external"],
|
|
)
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@router.get(
|
|
"/filament",
|
|
name="Get all external filaments",
|
|
response_model_exclude_none=True,
|
|
response_model=list[ExternalFilament],
|
|
)
|
|
async def filaments() -> FileResponse:
|
|
"""Get all external filaments."""
|
|
return FileResponse(path=get_filaments_file(), media_type="application/json")
|
|
|
|
|
|
@router.get(
|
|
"/filament/search",
|
|
name="Search external filaments",
|
|
response_model_exclude_none=True,
|
|
)
|
|
async def search_external_filaments(
|
|
query: Annotated[
|
|
str,
|
|
Query(
|
|
description="Search query, matched word-by-word against manufacturer, name and material.",
|
|
examples=["polymaker pla"],
|
|
),
|
|
],
|
|
limit: Annotated[
|
|
int,
|
|
Query(ge=1, le=100, description="Maximum number of results to return."),
|
|
] = 20,
|
|
) -> list[ExternalFilament]:
|
|
"""Search the external filament catalog.
|
|
|
|
Filters server-side so clients don't have to download the entire catalog just to
|
|
search it.
|
|
"""
|
|
return search_filaments(query, limit)
|
|
|
|
|
|
@router.get(
|
|
"/material",
|
|
name="Get all external materials",
|
|
response_model_exclude_none=True,
|
|
response_model=list[ExternalMaterial],
|
|
)
|
|
async def materials() -> FileResponse:
|
|
"""Get all external materials."""
|
|
return FileResponse(path=get_materials_file(), media_type="application/json")
|