Initial commit: core-api service extraction from portainer-core
Build and Push / build (release) Successful in 43s

This commit is contained in:
2025-12-11 15:52:59 +01:00
commit 488a4e8a91
49 changed files with 9478 additions and 0 deletions
+168
View File
@@ -0,0 +1,168 @@
"""
Tools Controller
Provides utility tool endpoints including:
- Web scraping and content extraction
- DNS lookups
"""
from fastapi import APIRouter, HTTPException, status
from src.controllers.base import BaseController
from src.logging_config import get_logger
from src.web_scraper.schemas import WebScraperRequest, WebScraperResponse
from src.web_scraper.service import WebScraperService
from src.web_scraper.exceptions import FetchError, ScrapingError
from src.dns.schemas import DNSLookupRequest, DNSLookupResponse
from src.dns.service import DNSService
from src.dns.exceptions import DNSQueryError
logger = get_logger(__name__)
class ToolsController(BaseController):
"""
Controller for utility tools
Provides endpoints for:
- Web scraping and content extraction
- DNS lookups
"""
def __init__(self):
super().__init__(prefix="/tools", tags=["Tools"])
# Initialize services (could be dependency injected for testing)
self.scraper_service = WebScraperService()
self.dns_service = DNSService()
def create_router(self) -> APIRouter:
"""Create and configure the router"""
router = APIRouter(prefix=self.prefix, tags=self.tags)
@router.post(
"/scrape",
response_model=WebScraperResponse,
status_code=status.HTTP_200_OK,
summary="Scrape website content",
description="""
Scrape and extract main content from a website.
Uses trafilatura for intelligent content extraction (articles, blog posts, documentation),
with BeautifulSoup as fallback. Perfect for feeding webpage content to LLMs.
**Features:**
- Intelligent main content extraction
- Removes navigation, ads, footers
- Optional link extraction
- Configurable content length limits
**Rate Limiting:** None (internal network use only)
"""
)
async def scrape_website(request: WebScraperRequest) -> WebScraperResponse:
"""
Scrape a website and extract its main content
Args:
request: Scraping request with URL and options
Returns:
Extracted content with metadata
Raises:
HTTPException: 400 for fetch errors, 500 for processing errors
"""
try:
logger.info(f"Received scrape request for: {request.url}")
result = await self.scraper_service.scrape_url(request)
return result
except FetchError as e:
logger.warning(f"Fetch failed: {str(e)}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Failed to fetch URL: {str(e)}"
)
except ScrapingError as e:
logger.error(f"Scraping failed: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to extract content: {str(e)}"
)
except Exception as e:
logger.error(f"Unexpected error: {str(e)}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="An unexpected error occurred"
)
@router.post(
"/dns/lookup",
response_model=DNSLookupResponse,
status_code=status.HTTP_200_OK,
summary="Perform DNS lookup",
description="""
Perform DNS lookups for various record types.
Uses dnspython for reliable DNS queries with support for multiple record types
and custom nameservers. Perfect for troubleshooting DNS issues and checking
domain configurations.
**Supported Record Types:**
- A: IPv4 address records
- AAAA: IPv6 address records
- MX: Mail exchange records
- TXT: Text records (SPF, DKIM, etc.)
- CNAME: Canonical name records
- NS: Nameserver records
- SOA: Start of authority records
- PTR: Pointer records (reverse DNS)
- CAA: Certification authority authorization
- SRV: Service records
**Features:**
- Custom nameserver support (e.g., 8.8.8.8, 1.1.1.1)
- Query time measurement
- Detailed error messages
**Rate Limiting:** None (internal network use only)
"""
)
async def dns_lookup(request: DNSLookupRequest) -> DNSLookupResponse:
"""
Perform DNS lookup for a domain
Args:
request: DNS lookup request with domain, record type, and optional nameserver
Returns:
DNS lookup results with records and metadata
Raises:
HTTPException: 400 for invalid queries, 500 for processing errors
"""
try:
logger.info(f"Received DNS lookup request for: {request.domain} ({request.record_type})")
result = await self.dns_service.lookup(request)
return result
except DNSQueryError as e:
logger.warning(f"DNS query error: {str(e)}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"DNS query failed: {str(e)}"
)
except Exception as e:
logger.error(f"Unexpected error during DNS lookup: {str(e)}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="An unexpected error occurred during DNS lookup"
)
return router
# Create controller instance
tools_controller = ToolsController()