[image-service] first commit

This commit is contained in:
Ittipat Lusuk 2026-05-08 10:41:29 +07:00
commit 88351993cf
6 changed files with 153 additions and 0 deletions

12
Dockerfile Normal file
View file

@ -0,0 +1,12 @@
FROM python:3.11-slim
WORKDIR /app
ENV TZ=Asia/Bangkok
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "main.py"]

16
README.md Normal file
View file

@ -0,0 +1,16 @@
# Image service
Image management enapoint:
## Upload Images
### POST /image/{folder}/upload
```
curl -X POST http://localhost/image/page_drink_n/upload \
-F "files=@img1.png" \
-F "files=@img2.png"
```
### POST /inter/{country}/image/{folder}/upload
```
curl -X POST http://localhost/inter/tha/image/page_drink_disable_n2/upload \
-F "files=@bn_hot_america_no.png"
```

24
docker-compose.yml Normal file
View file

@ -0,0 +1,24 @@
version: "3.9"
services:
app:
build: .
container_name: taobin-image-container
ports:
- "8125:8125"
environment:
- PYTHONUNBUFFERED=1
volumes:
- ~/repo/taobin_project:/taobin_project
restart: always
command: uvicorn main:app --host 0.0.0.0 --port 8125
nginx:
image: nginx:latest
container_name: nginx-image-container
volumes:
- ~/repo/taobin_project:/taobin_project
- ./nginx.conf:/etc/nginx/conf.d/default.conf
ports:
- "8126:80"

80
main.py Normal file
View file

@ -0,0 +1,80 @@
from fastapi import FastAPI, UploadFile, File, HTTPException, Query
from fastapi.responses import FileResponse
from pathlib import Path
from typing import Optional
import shutil
import uuid
app = FastAPI()
BASE_DIR = Path("/taobin_project")
ALLOWED_FOLDERS = {"page_drink_n", "page_drink_press_n", "page_drink_disable_n2"}
ALLOWED_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif", ".webp"}
def validate_folder(folder: str):
if folder not in ALLOWED_FOLDERS:
raise HTTPException(400, f"folder must be {ALLOWED_FOLDERS}")
def validate_ext(filename: str):
ext = Path(filename).suffix.lower()
if ext not in ALLOWED_EXTENSIONS:
raise HTTPException(400, f"file not allow {ext}")
return ext
def get_image_dir(folder: str, country: Optional[str] = None) -> Path:
"""
no country /taobin_project/image/{folder}/
has country /taobin_project/inter/{country}/image/{folder}/
"""
if country:
path = BASE_DIR / "inter" / country / "image" / folder
else:
path = BASE_DIR / "image" / folder
path.mkdir(parents=True, exist_ok=True)
return path
# ─────────────────────────────────────────
# UPLOAD
# ─────────────────────────────────────────
@app.post("/image/{folder}/upload")
async def upload_images(
folder: str,
files: list[UploadFile] = File(...)
):
validate_folder(folder)
saved = []
for file in files:
ext = validate_ext(file.filename)
filename = Path(file.filename).name
dest = get_image_dir(folder) / filename
with open(dest, "wb") as f:
shutil.copyfileobj(file.file, f)
saved.append({
"filename": filename,
"url": f"/image/{folder}/{filename}"
})
return {"uploaded": saved}
@app.post("/inter/{country}/image/{folder}/upload")
async def upload_inter_images(
country: str,
folder: str,
files: list[UploadFile] = File(...)
):
validate_folder(folder)
saved = []
for file in files:
ext = validate_ext(file.filename)
filename = Path(file.filename).name
dest = get_image_dir(folder, country) / filename
with open(dest, "wb") as f:
shutil.copyfileobj(file.file, f)
saved.append({
"filename": filename,
"url": f"/inter/{country}/image/{folder}/{filename}"
})
return {"uploaded": saved}

18
nginx.conf Normal file
View file

@ -0,0 +1,18 @@
server {
listen 80;
# /image/{folder}/...
location ~ ^/image/(page_drink_n|page_drink_press_n|page_drink_disable_n2)/(.*)$ {
alias /taobin_project/image/$1/$2;
}
# /inter/{country}/image/{folder}/...
location ~ ^/inter/([^/]+)/image/(page_drink_n|page_drink_press_n|page_drink_disable_n2)/(.*)$ {
alias /taobin_project/inter/$1/image/$2/$3;
}
# Block any path
location / {
return 403;
}
}

3
requirements.txt Normal file
View file

@ -0,0 +1,3 @@
fastapi
uvicorn
python-multipart