-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 08168d1
Showing
66 changed files
with
2,225 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
name: Continious Integration | ||
|
||
on: | ||
push: | ||
|
||
jobs: | ||
run-tests: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Set up mongo | ||
run: | | ||
docker run --rm -d -p 27017:27017 mongo:latest | ||
- name: Set up imgproxy | ||
working-directory: imgproxy | ||
run: | | ||
docker run --rm -d --env-file env.list -v $(pwd)/data:/data:ro -v $(pwd)/filesystem:/sharedfs:ro -p 8080:8080 flagmansupport/imgproxy:latest | ||
- name: Set up python | ||
uses: actions/setup-python@v5 | ||
with: | ||
python-version: '3.11' | ||
|
||
- name: Install dependencies | ||
run: | | ||
python -m pip install -q --upgrade pip | ||
python -m pip install -q -r requirements.txt -r requirements_dev.txt | ||
- name: Run tests | ||
run: | | ||
pytest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
.vscode | ||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
|
||
# C extensions | ||
*.so | ||
|
||
# Distribution / packaging | ||
.Python | ||
build/ | ||
develop-eggs/ | ||
dist/ | ||
downloads/ | ||
eggs/ | ||
.eggs/ | ||
lib/ | ||
lib64/ | ||
parts/ | ||
sdist/ | ||
var/ | ||
wheels/ | ||
*.egg-info/ | ||
.installed.cfg | ||
*.egg | ||
|
||
# PyInstaller | ||
# Usually these files are written by a python script from a template | ||
# before PyInstaller builds the exe, so as to inject date/other infos into it. | ||
*.manifest | ||
*.spec | ||
|
||
# Installer logs | ||
pip-log.txt | ||
pip-delete-this-directory.txt | ||
|
||
# Unit test / coverage reports | ||
htmlcov/ | ||
.tox/ | ||
.coverage | ||
.coverage.* | ||
.cache | ||
nosetests.xml | ||
coverage.xml | ||
*.cover | ||
.hypothesis/ | ||
.pytest_cache/ | ||
|
||
# Translations | ||
*.mo | ||
*.pot | ||
|
||
# Django stuff: | ||
*.log | ||
local_settings.py | ||
db.sqlite3 | ||
db.sqlite3-journal | ||
|
||
# Flask stuff: | ||
instance/ | ||
.webassets-cache | ||
|
||
# Scrapy stuff: | ||
.scrapy | ||
|
||
# Sphinx documentation | ||
docs/_build/ | ||
|
||
# PyBuilder | ||
target/ | ||
|
||
# Jupyter Notebook | ||
.ipynb_checkpoints | ||
|
||
# IPython | ||
profile_default/ | ||
ipython_config.py | ||
|
||
# pyenv | ||
.python-version | ||
|
||
# pipenv | ||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. | ||
# However, in case of collaboration, if having platform-specific dependencies or dependencies | ||
# having no cross-platform support, pipenv may install dependencies that don't work, or not | ||
# install all needed dependencies. | ||
#Pipfile.lock | ||
|
||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow | ||
__pypackages__/ | ||
|
||
# Celery stuff | ||
celerybeat-schedule | ||
celerybeat.pid | ||
|
||
# SageMath parsed files | ||
*.sage.py | ||
|
||
# Environments | ||
.env | ||
.venv | ||
env/ | ||
venv/ | ||
ENV/ | ||
env.bak/ | ||
venv.bak/ | ||
|
||
# Spyder project settings | ||
.spyderproject | ||
.spyproject | ||
|
||
# Rope project settings | ||
.ropeproject | ||
|
||
# mkdocs documentation | ||
/site | ||
|
||
# mypy | ||
.mypy_cache/ | ||
.dmypy.json | ||
dmypy.json | ||
|
||
# Pyre type checker | ||
.pyre/ | ||
|
||
# pytype static type analyzer | ||
.pytype/ | ||
|
||
# Cython debug symbols | ||
cython_debug/ | ||
|
||
# CMake | ||
CMakeFiles/ | ||
CMakeCache.txt | ||
CMakeScripts/ | ||
CTestTestfile.cmake | ||
cmake_install.cmake | ||
install_manifest.txt | ||
Makefile | ||
*.cmake | ||
*.pot | ||
|
||
# Poetry | ||
# According to pypa/poetry#1915, it is recommended to include poetry.lock in version control. | ||
# However, in case of collaboration, if having poetry.lock present causes conflicts, package | ||
# managers like pipenv and pip-tools recommend to include it nevertheless. | ||
#poetry.lock | ||
|
||
# Pylance (https://github.com/microsoft/pylance-release) | ||
# Pylance is a static type checker for Python. It is recommended to include its config file | ||
# in version control when using Pylance. Following entries were added after running Pylance | ||
# in a fresh project. | ||
.pylance-config.json | ||
local_storage/ | ||
project_context.txt | ||
|
||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
FROM python:3.11-alpine | ||
|
||
RUN mkdir /app | ||
WORKDIR /app | ||
|
||
COPY . . | ||
|
||
RUN pip install -r requirements.txt | ||
|
||
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080", "--workers", "1"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
from fastapi import FastAPI | ||
|
||
from app.api.processing import router as processing_router | ||
from app.api.images import router as images_router | ||
|
||
|
||
app = FastAPI( | ||
title="MediaFlower API", | ||
description="An API for processing and managing images, including variant creation and storage management.", # noqa | ||
version="1.0.0", | ||
) | ||
|
||
app.include_router(processing_router, prefix="/images", tags=["Create"]) | ||
app.include_router(images_router, tags=["Images"]) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from fastapi import Depends, HTTPException, status | ||
from fastapi.security import HTTPBasic, HTTPBasicCredentials | ||
|
||
from app.core.settings import USERNAME, PASSWORD | ||
|
||
security = HTTPBasic() | ||
|
||
|
||
def authorize( | ||
credentials: HTTPBasicCredentials = Depends(security), | ||
): | ||
if credentials.username != USERNAME or credentials.password != PASSWORD: | ||
raise HTTPException( | ||
status_code=status.HTTP_401_UNAUTHORIZED, | ||
detail="Incorrect username or password", | ||
headers={"WWW-Authenticate": "Basic"}, | ||
) | ||
return credentials.username |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
from fastapi import APIRouter, HTTPException, Query, Depends, Response, status | ||
from typing import Optional | ||
|
||
from app.core.db_init import db | ||
from app.models.db.image_metadata import ImageMetadataDB | ||
from app.api.auth import authorize | ||
from app.models.py_object_id import PyObjectId | ||
from app.schemas.image_metadata import ImagesResponse | ||
from app.services.db_operations import ( | ||
mark_image_to_delete, | ||
ImageNotFoundError, | ||
ImageAlreadyDeletedError, | ||
) | ||
|
||
router = APIRouter() | ||
|
||
|
||
@router.get( | ||
"/images", | ||
summary="Get Images", | ||
description="Retrieve a list of images with optional filtering by project name and image name.", # noqa | ||
response_description="A list of images with pagination details.", | ||
) | ||
async def get_images( | ||
project_name: Optional[str] = None, | ||
image_name: Optional[str] = None, | ||
page: int = Query(1, ge=1), | ||
limit: int = Query(10, ge=1, le=100), | ||
_: str = Depends(authorize), | ||
): | ||
query = {"deleted": False} | ||
if project_name: | ||
query["project_name"] = project_name | ||
if image_name: | ||
query["image_name"] = image_name | ||
|
||
skip = (page - 1) * limit | ||
cursor = db.images.find(query).skip(skip).limit(limit) | ||
total_count = await db.images.count_documents(query) | ||
images = await cursor.to_list(length=limit) | ||
|
||
images = [ImageMetadataDB(**image).model_dump() for image in images] | ||
return ImagesResponse( | ||
page=page, limit=limit, total_count=total_count, images=images | ||
) | ||
|
||
|
||
@router.delete( | ||
"/image/{id}", | ||
summary="Delete Image", | ||
description="Mark image as deleted by its ID.", | ||
response_description="The image has been marked as deleted.", | ||
status_code=status.HTTP_204_NO_CONTENT, | ||
) | ||
async def delete_image( | ||
id: PyObjectId, | ||
_: str = Depends(authorize), | ||
): | ||
try: | ||
await mark_image_to_delete(id) | ||
except ImageNotFoundError as e: | ||
raise HTTPException( | ||
status_code=status.HTTP_404_NOT_FOUND, detail=str(e) | ||
) | ||
except ImageAlreadyDeletedError as e: | ||
raise HTTPException( | ||
status_code=status.HTTP_400_BAD_REQUEST, detail=str(e) | ||
) | ||
|
||
return Response(status_code=status.HTTP_204_NO_CONTENT) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
from typing import Annotated | ||
from fastapi import APIRouter, UploadFile, File, Query, Depends | ||
from fastapi.responses import Response | ||
|
||
from app.models.filetype_metadata import FileTypeMetadata | ||
from app.services.image_processing.service import handle_image_processing | ||
from app.helpers.image_downloader import download_image | ||
from app.services.s3_operations import download_image_from_s3 | ||
from app.api.auth import authorize | ||
from app.helpers.make_dependable import make_dependable | ||
|
||
router = APIRouter() | ||
|
||
|
||
@router.post( | ||
"/file", | ||
summary="Create from Image File", | ||
description="Upload and process an image file, generating various image variants.", # noqa | ||
response_description="The metadata of the processed image.", | ||
) | ||
async def process_image_file( | ||
project_name: str, | ||
image_type: str, | ||
file_type_metadata: Annotated[ | ||
FileTypeMetadata, Depends(make_dependable(FileTypeMetadata)) | ||
], | ||
file: UploadFile = File(...), | ||
_: str = Depends(authorize), | ||
): | ||
raw_bytes = await file.read() | ||
response = await handle_image_processing( | ||
project_name, image_type, raw_bytes, file_type_metadata | ||
) | ||
return Response( | ||
content=response.model_dump_json(), media_type="application/json" | ||
) | ||
|
||
|
||
@router.post( | ||
"/url", | ||
summary="Create from Image URL", | ||
description="Download and process an image from a URL, generating various image variants.", # noqa | ||
response_description="The metadata of the processed image.", | ||
) | ||
async def process_image_url( | ||
project_name: str, | ||
image_type: str, | ||
file_type_metadata: Annotated[ | ||
FileTypeMetadata, Depends(make_dependable(FileTypeMetadata)) | ||
], | ||
url: str = Query(...), | ||
_: str = Depends(authorize), | ||
): | ||
raw_bytes = await download_image(url) | ||
response = await handle_image_processing( | ||
project_name, image_type, raw_bytes, file_type_metadata | ||
) | ||
return Response( | ||
content=response.model_dump_json(), media_type="application/json" | ||
) | ||
|
||
|
||
@router.post( | ||
"/s3", | ||
summary="Create from S3 Image", | ||
description="Download and process an image from S3, generating various image variants.", # noqa | ||
response_description="The metadata of the processed image.", | ||
) | ||
async def process_image_s3( | ||
project_name: str, | ||
image_type: str, | ||
file_type_metadata: Annotated[ | ||
FileTypeMetadata, Depends(make_dependable(FileTypeMetadata)) | ||
], | ||
s3_bucket: str, | ||
s3_key: str, | ||
_: str = Depends(authorize), | ||
): | ||
raw_bytes = await download_image_from_s3(s3_bucket, s3_key) | ||
response = await handle_image_processing( | ||
project_name, image_type, raw_bytes, file_type_metadata | ||
) | ||
|
||
return Response( | ||
content=response.model_dump_json(), media_type="application/json" | ||
) |
Empty file.
Oops, something went wrong.