Backend Documentation
เอกสารฉบับนี้เป็นคู่มือปฏิบัติการสำหรับทีมพัฒนา อธิบายขั้นตอนการเริ่มต้นสร้าง Backend Server จากศูนย์ โดยอ้างอิงจากเอกสารออกแบบที่จัดทำไว้แล้ว
แนวทางการเริ่มต้นพัฒนา Backend Server (How to Start)
โครงงาน: แอปตรวจสอบรูปภาพตัดต่อที่ถูกนำมาหลอกลวง (Scam Image Detection)
เอกสารฉบับนี้เป็นคู่มือปฏิบัติการสำหรับทีมพัฒนา อธิบายขั้นตอนการเริ่มต้นสร้าง Backend Server จากศูนย์ โดยอ้างอิงจากเอกสารออกแบบที่จัดทำไว้แล้ว
เอกสารออกแบบที่ต้องอ่านก่อนเริ่ม
- design/server.md - สถาปัตยกรรม Backend, Database Schema, API Specs
- design/architecture.md - สถาปัตยกรรมระบบรวม, Risk Scoring Pipeline
- design/model.md - AI Model Design (SegFormer)
- design/training.md - Training Workflow, ONNX Export
- Document/server/server.md - เอกสารสรุปภาพรวม Server
สารบัญ
- โครงสร้างโฟลเดอร์ที่ต้องสร้าง
- ลำดับขั้นตอนการพัฒนา (Roadmap)
- Phase 1 - Foundation: ฐานรากของ Server
- Phase 2 - Auth: ระบบยืนยันตัวตน
- Phase 3 - Scan: ท่อประมวลผลภาพ
- Phase 4 - AI Inference: เชื่อมต่อโมเดล
- Phase 5 - Report & Admin
- Phase 6 - Cache, Storage & Polish
- Dependencies ทั้งหมด
- API Endpoints สรุปรวม
- Database Schema สรุปรวม
- Environment Variables
- คำสั่งเริ่มต้นใช้งาน
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 สร้างโครงสร้างโฟลเดอร์
ภาษา bashcd /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:
ภาษา pythonfrom 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:
ภาษา pythonfrom 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
ภาษา pythonfrom 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
ภาษา pythonfrom 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:
ภาษา pythonfrom 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
ภาษา pythonfrom 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:
ภาษา pythonfrom 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
ภาษา pythonfrom 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:
ภาษา pythonfrom 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:
ภาษา pythondef 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):
ภาษา pythonasync 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:
ภาษา pythonfrom 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):
ภาษา pythonimport 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):
ภาษา pythonclass 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):
ภาษา pythonclass 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)
ภาษา pythonimport 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
การติดตั้ง
ภาษา bashcd /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