Soumik-404 commited on
Commit
7bacd48
·
1 Parent(s): 545b3c4
Files changed (1) hide show
  1. api/server.py +20 -7
api/server.py CHANGED
@@ -33,10 +33,11 @@ from typing import Annotated, Any, Dict, List, Optional
33
  from urllib.parse import urlparse
34
 
35
  import httpx
36
- from fastapi import FastAPI, File, Form, HTTPException, UploadFile, status
37
  from fastapi.middleware.cors import CORSMiddleware
38
  from fastapi.middleware.gzip import GZipMiddleware
39
  from fastapi.responses import PlainTextResponse
 
40
  from pydantic import BaseModel, field_validator
41
 
42
  from core import ConversionError, ConversionResult, DocumentConverter, SUPPORTED_EXTENSIONS
@@ -56,9 +57,19 @@ _thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=MAX_WORKERS)
56
 
57
  _converter = DocumentConverter()
58
 
 
 
59
  logger.info("Thread pool initialised with %d workers", MAX_WORKERS)
60
 
61
 
 
 
 
 
 
 
 
 
62
  # ---------------------------------------------------------------------------
63
  # Self-ping
64
  # ---------------------------------------------------------------------------
@@ -315,7 +326,7 @@ def _batch_result_from_ok(result: ConversionResult) -> BatchFileResult:
315
  # ---------------------------------------------------------------------------
316
 
317
  @app.get("/health", tags=["System"], summary="Liveness check")
318
- async def health():
319
  """Return server status and uptime in seconds."""
320
  return {
321
  "success": True,
@@ -327,7 +338,7 @@ async def health():
327
 
328
 
329
  @app.get("/info", tags=["System"], summary="Server and environment information")
330
- async def info():
331
  """Return application version, platform details, and operational limits."""
332
  import platform
333
 
@@ -345,7 +356,7 @@ async def info():
345
 
346
 
347
  @app.get("/formats", tags=["System"], summary="Supported file formats by category")
348
- async def list_formats():
349
  """Return all supported file extensions, grouped by document category."""
350
  by_category = {
351
  "documents": [e for e in SUPPORTED_EXTENSIONS if e in {".pdf", ".docx", ".doc", ".epub"}],
@@ -366,7 +377,7 @@ async def list_formats():
366
 
367
 
368
  @app.get("/spacy-labels", tags=["System"], summary="Available spaCy NER labels for field extraction")
369
- async def list_spacy_labels():
370
  """Return spaCy Named Entity Recognition labels available for structured extraction mappings."""
371
  from extraction.spacy_extractor import VALID_SPACY_LABELS
372
 
@@ -410,6 +421,7 @@ async def convert_file(
410
  "Example: {\"company\": {\"source_type\": \"entity\", \"label\": \"ORG\"}}"
411
  ),
412
  ),
 
413
  ):
414
  """Convert a single uploaded file to Markdown.
415
 
@@ -481,7 +493,7 @@ async def convert_file(
481
  tags=["Convert"],
482
  summary="Convert a public URL to Markdown",
483
  )
484
- async def convert_url(body: UrlRequest):
485
  """Convert a public HTTP/HTTPS URL to Markdown.
486
 
487
  When ``return_json=true``, the URL content is fetched as raw bytes first
@@ -562,6 +574,7 @@ async def convert_url(body: UrlRequest):
562
  )
563
  async def batch_files(
564
  files: Annotated[List[UploadFile], File(description="Files to convert — maximum 10")],
 
565
  ):
566
  """Convert up to 10 uploaded files in a single request.
567
 
@@ -626,7 +639,7 @@ async def batch_files(
626
  tags=["Batch"],
627
  summary="Convert multiple URLs (up to 20)",
628
  )
629
- async def batch_urls(body: BatchUrlRequest):
630
  """Convert up to 20 public URLs in a single request.
631
 
632
  URLs are processed concurrently. Per-item results include success/error
 
33
  from urllib.parse import urlparse
34
 
35
  import httpx
36
+ from fastapi import Depends, FastAPI, File, Form, HTTPException, Security, UploadFile, status
37
  from fastapi.middleware.cors import CORSMiddleware
38
  from fastapi.middleware.gzip import GZipMiddleware
39
  from fastapi.responses import PlainTextResponse
40
+ from fastapi.security import APIKeyHeader
41
  from pydantic import BaseModel, field_validator
42
 
43
  from core import ConversionError, ConversionResult, DocumentConverter, SUPPORTED_EXTENSIONS
 
57
 
58
  _converter = DocumentConverter()
59
 
60
+ _X_API_KEY = os.environ.get("X_API_KEY", "")
61
+
62
  logger.info("Thread pool initialised with %d workers", MAX_WORKERS)
63
 
64
 
65
+ def _validate_api_key(x_api_key: str = Security(APIKeyHeader(name="x-api-key", auto_error=False))) -> str:
66
+ if not _X_API_KEY:
67
+ return x_api_key
68
+ if x_api_key == _X_API_KEY:
69
+ return x_api_key
70
+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid x-api-key")
71
+
72
+
73
  # ---------------------------------------------------------------------------
74
  # Self-ping
75
  # ---------------------------------------------------------------------------
 
326
  # ---------------------------------------------------------------------------
327
 
328
  @app.get("/health", tags=["System"], summary="Liveness check")
329
+ async def health(_: str = Depends(_validate_api_token)):
330
  """Return server status and uptime in seconds."""
331
  return {
332
  "success": True,
 
338
 
339
 
340
  @app.get("/info", tags=["System"], summary="Server and environment information")
341
+ async def info(_: str = Depends(_validate_api_key)):
342
  """Return application version, platform details, and operational limits."""
343
  import platform
344
 
 
356
 
357
 
358
  @app.get("/formats", tags=["System"], summary="Supported file formats by category")
359
+ async def list_formats(_: str = Depends(_validate_api_key)):
360
  """Return all supported file extensions, grouped by document category."""
361
  by_category = {
362
  "documents": [e for e in SUPPORTED_EXTENSIONS if e in {".pdf", ".docx", ".doc", ".epub"}],
 
377
 
378
 
379
  @app.get("/spacy-labels", tags=["System"], summary="Available spaCy NER labels for field extraction")
380
+ async def list_spacy_labels(_: str = Depends(_validate_api_key)):
381
  """Return spaCy Named Entity Recognition labels available for structured extraction mappings."""
382
  from extraction.spacy_extractor import VALID_SPACY_LABELS
383
 
 
421
  "Example: {\"company\": {\"source_type\": \"entity\", \"label\": \"ORG\"}}"
422
  ),
423
  ),
424
+ _: str = Depends(_validate_api_key),
425
  ):
426
  """Convert a single uploaded file to Markdown.
427
 
 
493
  tags=["Convert"],
494
  summary="Convert a public URL to Markdown",
495
  )
496
+ async def convert_url(body: UrlRequest, _: str = Depends(_validate_api_key)):
497
  """Convert a public HTTP/HTTPS URL to Markdown.
498
 
499
  When ``return_json=true``, the URL content is fetched as raw bytes first
 
574
  )
575
  async def batch_files(
576
  files: Annotated[List[UploadFile], File(description="Files to convert — maximum 10")],
577
+ _: str = Depends(_validate_api_key),
578
  ):
579
  """Convert up to 10 uploaded files in a single request.
580
 
 
639
  tags=["Batch"],
640
  summary="Convert multiple URLs (up to 20)",
641
  )
642
+ async def batch_urls(body: BatchUrlRequest, _: str = Depends(_validate_api_key)):
643
  """Convert up to 20 public URLs in a single request.
644
 
645
  URLs are processed concurrently. Per-item results include success/error