# services/ — business logic Each module holds the domain logic behind the asset extraction endpoint. Routers stay thin; heavy lifting lives here so it can be unit-tested in isolation. ## Services | File | What it does | |------|--------------| | `fetcher.py` | Safe async HTML fetch: SSRF block-list, redirect/size/timeout caps, content-type check. | | `resolver.py` | Turn `src`/`href`/CSS `url()` references into absolute URLs. | | `asset_service.py` | Orchestrates fetch → parse → extract → group. | ## `extractors/` — asset category extractors Each extractor parses the (BeautifulSoup) document and returns absolute URLs for one category. See [`extractors/README.md`](./extractors/README.md). ## Conventions - Services raise a domain-specific exception (e.g. `FetchError`, `AssetExtractError`) that the router maps to an HTTP response. - URL resolution is centralized in `resolver.py` — every extractor should funnel its raw references through it to avoid duplicated/drifted logic.