""" main.py FastAPI app สำหรับ Hugging Face Space (Docker) - ดึงข้อมูล e-GP ของ SUT ครั้งเดียวตอน startup แล้ว cache ไว้ใน memory - เปิด endpoint ให้ frontend (Lovable) เรียกไปแสดงผล - มี endpoint /refresh สำหรับสั่งดึงข้อมูลใหม่แบบ manual (ไม่ auto-refresh) """ from fastapi import FastAPI, Query from fastapi.middleware.cors import CORSMiddleware from contextlib import asynccontextmanager from typing import Optional import logging from scraper import scrape_all logging.basicConfig(level=logging.INFO) logger = logging.getLogger("sut-tender-api") # cache เก็บข้อมูลไว้ใน memory ของ process CACHE: dict = {"data": [], "last_updated": None, "error": None} @asynccontextmanager async def lifespan(app: FastAPI): # ดึงข้อมูลครั้งเดียวตอน startup ตามที่ตกลงกันไว้ refresh_cache() yield app = FastAPI(title="SUT Tender API", lifespan=lifespan) # เปิด CORS ให้ Lovable (หรือ origin ใดๆ) เรียกได้ app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) def refresh_cache(): import datetime try: logger.info("Scraping SUT e-GP data...") data = scrape_all() CACHE["data"] = data CACHE["last_updated"] = datetime.datetime.utcnow().isoformat() CACHE["error"] = None logger.info(f"Done. Got {len(data)} items.") except Exception as e: logger.exception("Scrape failed") CACHE["error"] = str(e) @app.get("/") def root(): return { "status": "ok", "items_cached": len(CACHE["data"]), "last_updated": CACHE["last_updated"], "error": CACHE["error"], } @app.get("/api/tenders") def get_tenders( keyword: Optional[str] = Query( None, description="กรองคำในชื่อโครงการ รองรับหลายคำคั่นด้วย comma เช่น 'server,คอมพิวเตอร์,computer' (เจอคำใดคำหนึ่งก็ติด)", ), limit: int = Query(50, ge=1, le=500), offset: int = Query(0, ge=0), ): items = CACHE["data"] if keyword: # แตกคำด้วย comma แล้ว trim whitespace, ตัดคำว่างทิ้ง keywords = [kw.strip().lower() for kw in keyword.split(",") if kw.strip()] if keywords: items = [ i for i in items if any(kw in i["title"].lower() for kw in keywords) ] total = len(items) items = items[offset: offset + limit] return { "total": total, "limit": limit, "offset": offset, "last_updated": CACHE["last_updated"], "items": items, } @app.post("/refresh") def refresh(): """สั่งดึงข้อมูลใหม่แบบ manual (ไม่ auto-refresh ตามที่ตกลงไว้)""" refresh_cache() return {"status": "refreshed", "items_cached": len(CACHE["data"]), "error": CACHE["error"]}