Backend Documentation

เอกสารฉบับนี้เป็นคู่มือปฏิบัติการสำหรับทีมพัฒนา อธิบายขั้นตอนการเริ่มต้นสร้าง Backend Server จากศูนย์ โดยอ้างอิงจากเอกสารออกแบบที่จัดทำไว้แล้ว

หมวด การวางแผนต้นฉบับ planning/backend-documentation.md
planningbackendfastapihowto

แนวทางการเริ่มต้นพัฒนา Backend Server (How to Start)

โครงงาน: แอปตรวจสอบรูปภาพตัดต่อที่ถูกนำมาหลอกลวง (Scam Image Detection)

เอกสารฉบับนี้เป็นคู่มือปฏิบัติการสำหรับทีมพัฒนา อธิบายขั้นตอนการเริ่มต้นสร้าง Backend Server จากศูนย์ โดยอ้างอิงจากเอกสารออกแบบที่จัดทำไว้แล้ว

เอกสารออกแบบที่ต้องอ่านก่อนเริ่ม


สารบัญ

  1. โครงสร้างโฟลเดอร์ที่ต้องสร้าง
  2. ลำดับขั้นตอนการพัฒนา (Roadmap)
  3. Phase 1 - Foundation: ฐานรากของ Server
  4. Phase 2 - Auth: ระบบยืนยันตัวตน
  5. Phase 3 - Scan: ท่อประมวลผลภาพ
  6. Phase 4 - AI Inference: เชื่อมต่อโมเดล
  7. Phase 5 - Report & Admin
  8. Phase 6 - Cache, Storage & Polish
  9. Dependencies ทั้งหมด
  10. API Endpoints สรุปรวม
  11. Database Schema สรุปรวม
  12. Environment Variables
  13. คำสั่งเริ่มต้นใช้งาน

1. โครงสร้างโฟลเดอร์ที่ต้องสร้าง

อ้างอิงจาก design/server.md Section 2.1:

server/
├── app/
│   ├── __init__.py
│   ├── main.py                 # FastAPI Initialization + Lifespan
│   │
│   ├── core/                   # การตั้งค่าหลัก, ระบบความปลอดภัย, DB Connection
│   │   ├── __init__.py
│   │   ├── config.py           # Pydantic Settings - อ่านค่าจาก .env
│   │   ├── security.py         # Password Hashing (bcrypt) + JWT Token
│   │   └── database.py         # SQLAlchemy AsyncEngine + AsyncSession
│   │
│   ├── models/                 # SQLAlchemy ORM Models (ตาราง DB)
│   │   ├── __init__.py
│   │   ├── user.py             # users table
│   │   ├── scan.py             # scans table
│   │   ├── consent.py          # consent_logs table
│   │   └── report.py           # scam_reports table
│   │
│   ├── schemas/                # Pydantic Schemas (Request/Response Validation)
│   │   ├── __init__.py
│   │   ├── auth.py             # RegisterRequest, LoginRequest, TokenResponse
│   │   ├── scan.py             # ScanResponse, RiskSummary, LayerDetail
│   │   └── report.py           # ReportCreateRequest, ReportResponse
│   │
│   ├── repositories/           # Data Access Layer (Queries)
│   │   ├── __init__.py
│   │   ├── user_repo.py        # CRUD operations สำหรับ users
│   │   └── scan_repo.py        # CRUD operations สำหรับ scans + reports
│   │
│   ├── services/               # Business Logic Layer
│   │   ├── __init__.py
│   │   ├── auth_service.py     # Register, Login, Token Verification
│   │   ├── scan_service.py     # Multi-layer Analysis Orchestrator
│   │   ├── ocr_service.py      # Surya-OCR + Scam Keywords Detection
│   │   ├── exif_service.py     # EXIF Metadata Extraction
│   │   ├── inference_service.py # เชื่อมต่อ AI Inference (ONNX Runtime)
│   │   └── storage_service.py  # Local upload/heatmap files + /uploads URLs
│   │
│   ├── api/                    # Routing Layer (Controllers)
│   │   ├── __init__.py
│   │   ├── router.py           # รวม Router ทั้งหมดไว้ที่นี่
│   │   └── v1/
│   │       ├── __init__.py
│   │       ├── auth.py         # POST /register, POST /login, POST /refresh, GET /me, POST /logout
│   │       ├── scan.py         # POST /scan/, GET /scan/{scan_id} (เอกพจน์, prefix /api/v1)
│   │       ├── report.py       # POST /reports, GET /reports/my, GET /reports/categories
│   │       ├── history.py      # GET /history, GET /history/{scan_id}, DELETE /history/{scan_id}
│   │       └── admin.py        # /admin/* (login/users/reports/models/deploy/audit-logs/export-jobs)
│   │
│   └── utils/                  # Utility Functions
│       ├── __init__.py
│       ├── hashing.py          # SHA-256 Image Hash
│       └── risk_calculator.py  # Hybrid Risk Score Calculator (Worst-Case Trigger)
│
├── migrations/                 # Alembic Database Migrations
│   ├── env.py
│   ├── alembic.ini
│   └── versions/               # Migration Scripts ที่ auto-generate
│
├── tests/                      # Unit Tests + Integration Tests
│   ├── __init__.py
│   ├── test_auth.py
│   ├── test_scan.py
│   └── conftest.py             # Fixtures (TestClient, TestDB)
│
├── .env                        # Environment Variables (ห้าม commit)
├── .env.example                # ตัวอย่าง .env สำหรับทีมงาน
├── .gitignore
├── requirements.txt            # Python Dependencies
├── Dockerfile                  # Docker Image สำหรับ Deploy
├── docker-compose.yml          # Docker Compose (API + PostgreSQL + Redis)
└── howto.md                    # เอกสารนี้

2. ลำดับขั้นตอนการพัฒนา (Roadmap)

แบ่งการพัฒนาออกเป็น 6 Phase ตามลำดับ ควรทำให้เสร็จทีละ Phase:

Phase ชื่อ เป้าหมาย ระยะเวลาประมาณ
1 Foundation สร้างโครงสร้าง, ตั้งค่า DB, รัน Server ได้ 1-2 วัน
2 Auth ระบบ Register / Login / JWT 2-3 วัน
3 Scan รับรูปภาพ, EXIF, OCR, Risk Score (Mock AI) 3-4 วัน
4 AI Inference เชื่อม ONNX Model จริง, สร้าง Heatmap 3-5 วัน
5 Report & Admin ระบบรายงานสแกม, Admin Endpoints 2-3 วัน
6 Cache, Storage & Polish Redis Cache, Local /uploads, Rate Limit, Tests 3-4 วัน

3. Phase 1 - Foundation: ฐานรากของ Server

3.1 สร้างโครงสร้างโฟลเดอร์

ภาษา bash
cd /home/panuwat/project/server
mkdir -p app/{core,models,schemas,repositories,services,api/v1,utils}
mkdir -p migrations/versions tests

touch app/__init__.py app/main.py
touch app/core/{__init__,config,security,database}.py
touch app/models/{__init__,user,scan,consent,report}.py
touch app/schemas/{__init__,auth,scan,report}.py
touch app/repositories/{__init__,user_repo,scan_repo}.py
touch app/services/{__init__,auth_service,scan_service,ocr_service,exif_service,inference_service,storage_service}.py
touch app/api/{__init__,router}.py
touch app/api/v1/{__init__,auth,scan,report,admin}.py
touch app/utils/{__init__,hashing,risk_calculator}.py
touch tests/{__init__,conftest,test_auth,test_scan}.py

3.2 สร้างไฟล์ app/core/config.py

ใช้ pydantic-settings อ่านค่า Environment Variables:

ภาษา python
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    # App
    APP_NAME: str = "ScamGuard API"
    APP_VERSION: str = "0.1.0"
    DEBUG: bool = True

    # Database
    DATABASE_URL: str = "postgresql+asyncpg://scamguard:password@localhost:5432/scamguard_db"

    # JWT
    JWT_SECRET_KEY: str = "change-this-in-production"
    JWT_ALGORITHM: str = "HS256"
    JWT_ACCESS_TOKEN_EXPIRE_MINUTES: int = 60

    # Redis
    REDIS_URL: str = "redis://localhost:6379/0"

    # Storage
    STORAGE_BACKEND: str = "local"  # "local" สำหรับ dev, "gcs" สำหรับ production
    LOCAL_UPLOAD_DIR: str = "./uploads"

    # AI Inference
    ONNX_MODEL_PATH: str = "../model/segformer/work_dirs/latest.onnx"

    # Rate Limit (tiered per minute — มติ DOC-02)
    RATE_LIMIT_GUEST_PER_MINUTE: int = 10
    RATE_LIMIT_USER_PER_MINUTE: int = 60
    RATE_LIMIT_ADMIN_PER_MINUTE: int = 300
    RATE_LIMIT_SCAN_CREATE_PER_MINUTE: int = 5

    class Config:
        env_file = ".env"
        env_file_encoding = "utf-8"

settings = Settings()

3.3 สร้างไฟล์ app/core/database.py

ใช้ SQLAlchemy AsyncEngine:

ภาษา python
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from sqlalchemy.orm import DeclarativeBase

from app.core.config import settings

engine = create_async_engine(settings.DATABASE_URL, echo=settings.DEBUG)
async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

class Base(DeclarativeBase):
    pass

async def get_db() -> AsyncSession:
    async with async_session() as session:
        yield session

3.4 สร้างไฟล์ app/main.py

ภาษา python
from contextlib import asynccontextmanager
from fastapi import FastAPI
from app.core.config import settings
from app.api.router import api_router

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup: สร้างตาราง DB, โหลด Model, etc.
    print(f"[Startup] {settings.APP_NAME} v{settings.APP_VERSION}")
    yield
    # Shutdown: ปิด connections
    print("[Shutdown] Cleaning up...")

app = FastAPI(
    title=settings.APP_NAME,
    version=settings.APP_VERSION,
    lifespan=lifespan,
)

app.include_router(api_router, prefix="/api/v1")

@app.get("/health")
async def health_check():
    return {"status": "ok", "version": settings.APP_VERSION}

3.5 สร้างไฟล์ app/api/router.py

ภาษา python
from fastapi import APIRouter
from app.api.v1 import auth, scan, report, admin

api_router = APIRouter()

api_router.include_router(auth.router, prefix="/auth", tags=["Authentication"])
api_router.include_router(scan.router, prefix="/scan", tags=["Scan & Analysis"])
api_router.include_router(report.router, prefix="/report", tags=["Scam Reports"])
api_router.include_router(admin.router, prefix="/admin", tags=["Admin"])

3.6 ทดสอบรัน

ภาษา bash
# ติดตั้ง dependencies เบื้องต้น
pip install fastapi uvicorn pydantic-settings

# รัน server
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

เปิด http://localhost:8000/docs จะเห็น Swagger UI อัตโนมัติ เปิด http://localhost:8000/health จะได้ {"status": "ok"}


4. Phase 2 - Auth: ระบบยืนยันตัวตน

4.1 ORM Model - app/models/user.py

อ้างอิงจาก design/server.md Section 4.1:

ภาษา python
from sqlalchemy import Column, Integer, String, Boolean, DateTime
from sqlalchemy.sql import func
from app.core.database import Base

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True, index=True)
    email = Column(String(255), unique=True, nullable=False, index=True)
    hashed_password = Column(String(255), nullable=False)
    full_name = Column(String(100))
    role = Column(String(20), nullable=False, default="user")  # user, researcher; admin แยกตาราง admins
    is_active = Column(Boolean, default=True)
    created_at = Column(DateTime(timezone=True), server_default=func.now())
    updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())

4.2 Security - app/core/security.py

ภาษา python
from datetime import datetime, timedelta, timezone
from passlib.context import CryptContext
from jose import jwt, JWTError
from app.core.config import settings

pwd_context = CryptContext(schemes=["bcrypt"])

def hash_password(password: str) -> str:
    return pwd_context.hash(password)

def verify_password(plain: str, hashed: str) -> bool:
    return pwd_context.verify(plain, hashed)

def create_access_token(data: dict) -> str:
    to_encode = data.copy()
    expire = datetime.now(timezone.utc) + timedelta(minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM)

def decode_access_token(token: str) -> dict | None:
    try:
        return jwt.decode(token, settings.JWT_SECRET_KEY, algorithms=[settings.JWT_ALGORITHM])
    except JWTError:
        return None

4.3 Pydantic Schemas - app/schemas/auth.py

อ้างอิง Request/Response จาก design/server.md Section 5.1:

ภาษา python
from pydantic import BaseModel, EmailStr

class RegisterRequest(BaseModel):
    email: EmailStr
    password: str
    full_name: str
    system_consent: bool = True
    research_consent: bool = False

class LoginRequest(BaseModel):
    username: str  # email
    password: str

class TokenResponse(BaseModel):
    access_token: str
    token_type: str = "bearer"
    user: dict

class UserResponse(BaseModel):
    id: int
    email: str
    full_name: str
    role: str
    message: str = "User registered successfully"

4.4 API Route - app/api/v1/auth.py

ภาษา python
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
from app.schemas.auth import RegisterRequest, LoginRequest, TokenResponse, UserResponse
# ... import service functions

router = APIRouter()

@router.post("/register", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
async def register(body: RegisterRequest, db: AsyncSession = Depends(get_db)):
    # 1. ตรวจสอบว่า email ซ้ำหรือไม่
    # 2. สร้าง User ใหม่ + hash password
    # 3. บันทึก consent_logs
    # 4. return UserResponse
    ...

@router.post("/login", response_model=TokenResponse)
async def login(body: LoginRequest, db: AsyncSession = Depends(get_db)):
    # 1. ค้นหา user จาก email
    # 2. ตรวจสอบ password
    # 3. สร้าง JWT Token
    # 4. return TokenResponse
    ...

5. Phase 3 - Scan: ท่อประมวลผลภาพ

5.1 ORM Model - app/models/scan.py

อ้างอิงจาก design/server.md Section 4.2:

ภาษา python
from sqlalchemy import Column, String, Integer, Float, Text, DateTime, ForeignKey
from sqlalchemy.dialects.postgresql import UUID, JSONB
from sqlalchemy.sql import func
import uuid
from app.core.database import Base

class Scan(Base):
    __tablename__ = "scans"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    user_id = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
    image_hash = Column(String(64), nullable=False, index=True)  # SHA-256
    raw_image_url = Column(String(512), nullable=False)
    heatmap_image_url = Column(String(512))

    # Risk Scores (0-100)
    text_score = Column(Integer, nullable=False, default=0)
    visual_score = Column(Integer, nullable=False, default=0)
    source_score = Column(Integer, nullable=False, default=0)
    total_risk_score = Column(Integer, nullable=False, default=0)

    # Analysis Details
    exif_data = Column(JSONB)
    ocr_text = Column(Text)
    scam_keywords_found = Column(JSONB)
    reverse_search_results = Column(JSONB)
    ai_gen_probability = Column(Float, default=0.0)

    status = Column(String(20), nullable=False, default="pending")
    created_at = Column(DateTime(timezone=True), server_default=func.now())
    completed_at = Column(DateTime(timezone=True))

5.2 Risk Calculator

อ้างอิงจาก design/architecture.md Section 5.1:

ภาษา python
def calculate_risk_score(text_score: int, visual_score: int, source_score: int) -> dict:
    """
    Recommended Hybrid Approach (Worst-Case Trigger with Multi-Factor Breakdown):
    1. ประเมิน 3 มิติแยกอิสระ 0-100%
    2. ยึดมิติที่เสี่ยงสูงสุดเป็นฐาน S_base = max(visual, text, source)
    3. Multi-factor Compounding: +5 คะแนนต่อมิติรองที่มีคะแนน >= 40 (สูงสุดไม่เกิน 100)
    4. Visual >= 80 บังคับเป็น High

    Risk Grades:
      0-39   = low
      40-69  = medium
      70-100 = high
    """
    text_score = max(0, min(100, int(text_score)))
    visual_score = max(0, min(100, int(visual_score)))
    source_score = max(0, min(100, int(source_score)))

    base_score = max(text_score, visual_score, source_score)
    scores = [text_score, visual_score, source_score]
    secondary_high = sum(1 for s in scores if s >= 40) - (1 if base_score >= 40 else 0)
    compounding_bonus = secondary_high * 5
    total = min(100, base_score + compounding_bonus)

    if total >= 70 or visual_score >= 80:
        grade = "high"
        total = max(70, total)
    elif total >= 40:
        grade = "medium"
    else:
        grade = "low"

    return {
        "total_risk_score": total,
        "grade": grade,
        "primary_factor": "visual" if visual_score == base_score else ("textual" if text_score == base_score else "source"),
        "is_multi_risk": secondary_high > 0,
        "breakdown": {
            "visual": {"score": visual_score, "level": "high" if visual_score >= 70 else ("medium" if visual_score >= 40 else "low")},
            "textual": {"score": text_score, "level": "high" if text_score >= 70 else ("medium" if text_score >= 40 else "low")},
            "source": {"score": source_score, "level": "high" if source_score >= 70 else ("medium" if source_score >= 40 else "low")},
        },
    }

5.3 Scan Service - app/services/scan_service.py

ขั้นตอนการทำงานหลัก (อ้างอิงจาก design/architecture.md Section 5, Multi-layer Pipeline):

ภาษา python
async def analyze_image(file_bytes: bytes, user_id: int, db) -> dict:
    """
    Multi-layer Analysis Pipeline:
    1. Validate file type + size (max 10MB, JPG/PNG only)
    2. คำนวณ SHA-256 Hash
    3. ตรวจ Redis Cache (Cache Hit -> return ผลเดิม)
    4. Task 1: EXIF Metadata Extraction
    5. Task 2: OCR + Scam Keywords Analysis
    6. Task 3: Visual Forgery Detection (ส่งไป AI Inference)
    7. Task 4: Reverse Image Search (optional)
    8. Task 5: AI-Generated Image Detection
    9. รวม Overall Risk Score (Hybrid Worst-Case Trigger + Multi-factor Compounding)
    10. บันทึกลง PostgreSQL + เขียน Redis Cache
    11. return ผลลัพธ์ JSON
    """
    pass

5.4 API Route - app/api/v1/scan.py

อ้างอิง Request/Response จาก design/server.md Section 5.2:

ภาษา python
from fastapi import APIRouter, UploadFile, File, Depends

router = APIRouter()

@router.post("/")
async def create_scan(file: UploadFile = File(...), ...):
    """
    POST /api/v1/scan/
    Request: Multipart/Form-Data (file: binary JPG/PNG)
    Response: JSON ตาม design/server.md Section 5.2.1
    """
    # 1. Validate file type + size
    # 2. อ่าน bytes
    # 3. เรียก scan_service.analyze_image()
    # 4. return ScanResponse
    ...

@router.get("/{scan_id}")
async def get_scan(scan_id: str, ...):
    """
    GET /api/v1/scan/{scan_id}
    Response: JSON เดียวกับ POST /scan
    """
    ...

6. Phase 4 - AI Inference: เชื่อมต่อโมเดล

6.1 ONNX Runtime Service - app/services/inference_service.py

เชื่อมต่อกับโมเดล SegFormer ที่ Export เป็น ONNX แล้ว (อ้างอิง design/model.md + design/training.md Section 13):

ภาษา python
import onnxruntime as ort
import numpy as np
from PIL import Image

class InferenceService:
    def __init__(self, model_path: str):
        self.session = ort.InferenceSession(model_path)

    def predict(self, image_bytes: bytes) -> dict:
        """
        Input:  ภาพ RGB -> Tensor [1, 3, 512, 512]
        Output: Probability Map (Heatmap) + Segmentation Mask + Risk Score
        """
        # 1. Decode image from bytes
        # 2. Resize to 512x512
        # 3. Normalize (mean, std)
        # 4. Run forward pass
        # 5. Post-process: Mask, Heatmap, Score
        ...

    def generate_heatmap(self, prob_map: np.ndarray, original_image) -> bytes:
        """
        แปลง Probability Map เป็นภาพ Heatmap (สีแดง=เสี่ยง, สีน้ำเงิน=ปลอดภัย)
        ซ้อนทับ (Overlay) กับภาพต้นฉบับ
        """
        ...

6.2 การเตรียมไฟล์ ONNX

ก่อนใช้งานจริง ต้อง Export โมเดลจาก PyTorch Checkpoint:

ภาษา bash
# ใน environment ที่มี mmsegmentation
cd /home/panuwat/project/model/segformer

# Export checkpoint เป็น ONNX (ดูเพิ่มเติมใน model/segformer/README.md)
python -m mmseg.tools.pytorch2onnx \
    configs/segformer_mit-b2-v1.py \
    work_dirs/latest.pth \
    --output-file work_dirs/latest.onnx \
    --input-img data/test_sample.jpg \
    --shape 512 512

7. Phase 5 - Report & Admin

7.1 ORM Models

consent_logs (อ้างอิง design/server.md Section 4.3):

ภาษา python
class ConsentLog(Base):
    __tablename__ = "consent_logs"
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"))
    system_consent = Column(Boolean, nullable=False, default=True)
    research_consent = Column(Boolean, nullable=False, default=False)
    ip_address = Column(String(45))
    user_agent = Column(Text)
    created_at = Column(DateTime(timezone=True), server_default=func.now())

scam_reports (อ้างอิง design/server.md Section 4.4):

ภาษา python
class ScamReport(Base):
    __tablename__ = "scam_reports"
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"))
    scan_id = Column(UUID(as_uuid=True), ForeignKey("scans.id", ondelete="SET NULL"))
    reason = Column(Text, nullable=False)
    status = Column(String(20), nullable=False, default="pending")  # pending, approved, rejected
    moderated_by = Column(Integer, ForeignKey("admins.id"))
    moderated_at = Column(DateTime(timezone=True))
    created_at = Column(DateTime(timezone=True), server_default=func.now())

7.2 API Routes

ภาษา python
# app/api/v1/report.py
@router.post("/")
async def create_report(body: ReportCreateRequest, ...):
    """POST /api/v1/reports - ผู้ใช้รายงานภาพหลอกลวง"""
    ...

# app/api/v1/admin.py (มีเฉพาะ deploy/dry-run สำหรับจัดการโมเดล)
# POST /api/v1/admin/models/{id}/deploy และ /dry-run สำหรับ deploy โมเดล

@router.post("/model")
async def update_model(...):
    """POST /api/v1/admin/model - Admin อัปโหลด ONNX Model ใหม่ (Hot Reload)"""
    ...

8. Phase 6 - Cache, Storage & Polish

8.1 Redis Cache (อ้างอิง design/server.md Section 2.2.5)

ภาษา python
import redis.asyncio as redis

# Key pattern: scan:hash:{image_sha256}
# TTL: 7 วัน (604800 วินาที)

async def check_cache(image_hash: str) -> dict | None:
    cached = await redis_client.get(f"scan:hash:{image_hash}")
    if cached:
        return json.loads(cached)  # Cache Hit
    return None  # Cache Miss

async def set_cache(image_hash: str, result: dict):
    await redis_client.setex(
        f"scan:hash:{image_hash}",
        604800,  # 7 days
        json.dumps(result)
    )

8.2 Local File Storage (code v1)

อ้างอิง design/server.md Section 2.2.2:

โครงสร้างไฟล์ปัจจุบัน:
  LOCAL_UPLOAD_DIR/                         <- default ./uploads
  /uploads/{filename}                       <- FastAPI static URL

Cloud Object Storage/Presigned URL เป็น future deployment option ไม่ใช่ implementation ปัจจุบัน

8.3 Rate Limiting (อ้างอิง design/server.md Section 6)

  • ผู้ใช้ทั่วไป: สแกนได้สูงสุด 60 ครั้ง/ชั่วโมง
  • ใช้ slowapi หรือ custom middleware + Redis counter

8.4 Input Validation (อ้างอิง design/server.md Section 6)

  • Allowed file types: image/jpeg, image/png
  • Max file size: 10MB
  • ถ้าไม่ผ่าน -> return HTTP 400 Bad Request

8.5 Graceful Degradation

หาก AI Inference หรือ Google Vision API Timeout ให้คำนวณ Risk Score จากมิติที่สำเร็จ (ตัดมิติที่ล้มเหลวทิ้ง — EXIF มีไว้แสดงผลเท่านั้น ไม่ร่วมคำนวณ) และบันทึก Error Log


9. Dependencies ทั้งหมด

requirements.txt

ภาษา txt
# Web Framework
fastapi>=0.115.0
uvicorn[standard]>=0.30.0
pydantic[email]>=2.0
pydantic-settings>=2.0

# Database
sqlalchemy[asyncio]>=2.0
asyncpg>=0.30.0
alembic>=1.14.0

# Auth & Security
python-jose[cryptography]>=3.3.0
passlib[bcrypt]>=1.7.4

# Redis
redis[hiredis]>=5.0

# Image Processing
Pillow>=10.0
python-multipart>=0.0.9

# AI Inference
onnxruntime>=1.18.0
numpy>=1.26,<2

# OCR (ติดตั้งแยก - ใช้ทรัพยากรสูง)
# surya-ocr

# EXIF
piexif>=1.1.3

# Rate Limiting
slowapi>=0.1.9

# Testing
pytest>=8.0
pytest-asyncio>=0.23
httpx>=0.27

การติดตั้ง

ภาษา bash
cd /home/panuwat/project/server

# สร้าง Virtual Environment
python -m venv venv
source venv/bin/activate

# ติดตั้ง dependencies
pip install -r requirements.txt

10. API Endpoints สรุปรวม

อ้างอิงจาก design/server.md Section 5 และ Document/server/server.md Section 5:

Method Endpoint หน้าที่ Auth
GET /health Health Check -
POST /api/v1/auth/register สมัครสมาชิก -
POST /api/v1/auth/login เข้าสู่ระบบ รับ JWT Token -
POST /api/v1/scan/ อัปโหลดรูปภาพเพื่อตรวจวิเคราะห์ (เอกพจน์) User
GET /api/v1/scan/{scan_id} ดูผลลัพธ์การสแกน User
POST /api/v1/reports รายงานภาพหลอกลวง (พหูพจน์) User
GET /api/v1/history ประวัติการสแกน User
POST /api/v1/admin/models/{id}/deploy Deploy โมเดล Admin (super)

11. Database Schema สรุปรวม

อ้างอิงจาก design/server.md Section 4:

ตาราง หน้าที่ คอลัมน์สำคัญ
users บัญชีผู้ใช้ id, email, hashed_password, role, is_active
scans ประวัติการสแกน id(UUID), user_id, image_hash, scores, exif_data, ocr_text, status
consent_logs การยินยอม PDPA user_id, system_consent, research_consent
scam_reports รายงานภาพหลอกลวง user_id, scan_id, reason, status, moderated_by

12. Environment Variables

สร้างไฟล์ .env (ห้าม commit ขึ้น git):

ภาษา env
# App
APP_NAME=ScamGuard API
APP_VERSION=0.1.0
DEBUG=true

# Database (PostgreSQL)
DATABASE_URL=postgresql+asyncpg://scamguard:password@localhost:5432/scamguard_db

# JWT
JWT_SECRET_KEY=your-super-secret-key-change-in-production
JWT_ALGORITHM=HS256
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=60

# Redis
REDIS_URL=redis://localhost:6379/0

# Storage
STORAGE_BACKEND=local
LOCAL_UPLOAD_DIR=./uploads

# AI Model
ONNX_MODEL_PATH=../model/segformer/work_dirs/latest.onnx

# Rate Limit (tiered per minute — มติ DOC-02)
RATE_LIMIT_GUEST_PER_MINUTE=10
RATE_LIMIT_USER_PER_MINUTE=60
RATE_LIMIT_ADMIN_PER_MINUTE=300
RATE_LIMIT_SCAN_CREATE_PER_MINUTE=5

13. คำสั่งเริ่มต้นใช้งาน

ด้วย Docker Compose (แนะนำ)

ภาษา bash
# สร้าง docker-compose.yml ที่มี PostgreSQL + Redis + API
docker compose up -d

# ดู logs
docker compose logs -f api

ด้วย Manual (สำหรับ Dev)

ภาษา bash
# 1. ติดตั้ง PostgreSQL + Redis ในเครื่อง (หรือใช้ Docker แค่ DB)
docker run -d --name scamguard-db \
  -e POSTGRES_USER=scamguard \
  -e POSTGRES_PASSWORD=password \
  -e POSTGRES_DB=scamguard_db \
  -p 5432:5432 postgres:16

docker run -d --name scamguard-redis \
  -p 6379:6379 redis:7-alpine

# 2. Activate venv + install
cd /home/panuwat/project/server
source venv/bin/activate
pip install -r requirements.txt

# 3. Run DB migrations
alembic upgrade head

# 4. Start server
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

# 5. เปิด Swagger UI
# -> http://localhost:8000/docs