From 88351993cf0ff774dbe91072746145ec5de989e9 Mon Sep 17 00:00:00 2001 From: Ittipat Lusuk Date: Fri, 8 May 2026 10:41:29 +0700 Subject: [PATCH] [image-service] first commit --- Dockerfile | 12 +++++++ README.md | 16 ++++++++++ docker-compose.yml | 24 ++++++++++++++ main.py | 80 ++++++++++++++++++++++++++++++++++++++++++++++ nginx.conf | 18 +++++++++++ requirements.txt | 3 ++ 6 files changed, 153 insertions(+) create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 docker-compose.yml create mode 100644 main.py create mode 100644 nginx.conf create mode 100644 requirements.txt diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7273fa6 --- /dev/null +++ b/Dockerfile @@ -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"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7c81d60 --- /dev/null +++ b/README.md @@ -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" +``` \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..44ae71e --- /dev/null +++ b/docker-compose.yml @@ -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" + \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..046ff18 --- /dev/null +++ b/main.py @@ -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} \ No newline at end of file diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..e70f71c --- /dev/null +++ b/nginx.conf @@ -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; + } +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6d7503c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +fastapi +uvicorn +python-multipart \ No newline at end of file