-
Notifications
You must be signed in to change notification settings - Fork 0
Migrate to onion architecture with dynamic controller loading #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
0b71ca0
Migrate to onion architecture with dynamic controller loading
PianoNic 1f90dab
Update src/api/router_registry.py
PianoNic 8fce452
Update src/api/router_registry.py
PianoNic f5716ea
Refactor logging setup in router_registry.py to ensure handler is alw…
PianoNic 42750aa
Refactor static file and template mounting in app.py to use pathlib f…
PianoNic File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
This file contains hidden or 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 |
|---|---|---|
| @@ -1,6 +1,5 @@ | ||
| import uvicorn | ||
| from src.app import app | ||
| from src.api.app import app | ||
|
|
||
| if __name__ == "__main__": | ||
| print("Starting Transparent Image Cropper FastAPI app on http://0.0.0.0:5000") | ||
| uvicorn.run(app, host="0.0.0.0", port=5000) |
This file contains hidden or 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
This file was deleted.
Oops, something went wrong.
This file contains hidden or 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 @@ | ||
| # API layer: endpoints, interceptors, middlewares, etc. |
This file contains hidden or 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,30 @@ | ||
| from fastapi import FastAPI | ||
| from fastapi.staticfiles import StaticFiles | ||
| from fastapi.templating import Jinja2Templates | ||
| from src.api.router_registry import router_registry | ||
| from dotenv import load_dotenv | ||
| import os | ||
| import logging | ||
| from pathlib import Path | ||
|
|
||
| logging.basicConfig(level=logging.INFO) | ||
| load_dotenv() | ||
|
|
||
| app = FastAPI( | ||
| title="Smart Image Cropper API", | ||
| description="API for automatically cropping transparent areas and backgrounds from images", | ||
| version="1.0.0" | ||
| ) | ||
|
|
||
| base_dir = Path(__file__).parent.parent | ||
|
|
||
| # Mount static files and templates using pathlib for cleaner paths | ||
| static_dir = base_dir / "frontend" | ||
| app.mount("/static", StaticFiles(directory=str(static_dir)), name="static") | ||
|
|
||
| templates_dir = base_dir / "frontend" / "templates" | ||
| templates = Jinja2Templates(directory=str(templates_dir)) | ||
| app.state.templates = templates | ||
|
|
||
| # Auto-register all controllers | ||
| router_registry.auto_register(app) |
Empty file.
This file contains hidden or 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 @@ | ||
| import os | ||
| from fastapi import APIRouter | ||
|
|
||
| router = APIRouter() | ||
|
|
||
| @router.get("/api/app-info", tags=["App Info"]) | ||
| def app_info(): | ||
| environment = os.environ.get('FLASK_ENV', 'unknown environment') | ||
| version = os.environ.get('APP_VERSION', 'unknown version') | ||
| return {"environment": environment, "version": version} |
This file contains hidden or 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
This file contains hidden or 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,32 @@ | ||
| import os | ||
| from fastapi import Request | ||
| from fastapi.responses import HTMLResponse | ||
| from fastapi import APIRouter | ||
|
|
||
| router = APIRouter() | ||
|
|
||
| @router.get("/", response_class=HTMLResponse, include_in_schema=False) | ||
| def index(request: Request): | ||
| templates = request.app.state.templates | ||
| environment = os.environ.get('FLASK_ENV', 'unknown') | ||
| version = os.environ.get('APP_VERSION', 'unknown') | ||
| version_url = f"https://github.com/Pianonic/CropTransparent/releases/tag/{version}" | ||
| return templates.TemplateResponse("index.html", { | ||
| "request": request, | ||
| "environment": environment, | ||
| "version": version, | ||
| "version_url": version_url | ||
| }) | ||
|
|
||
| @router.get("/about", response_class=HTMLResponse, include_in_schema=False) | ||
| def about(request: Request): | ||
| templates = request.app.state.templates | ||
| environment = os.environ.get('FLASK_ENV', 'unknown') | ||
| version = os.environ.get('APP_VERSION', 'unknown') | ||
| version_url = f"https://github.com/Pianonic/CropTransparent/releases/tag/{version}" | ||
| return templates.TemplateResponse("about.html", { | ||
| "request": request, | ||
| "environment": environment, | ||
| "version": version, | ||
| "version_url": version_url | ||
| }) |
This file contains hidden or 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,73 @@ | ||
| import os | ||
| import importlib | ||
| import logging | ||
| from pathlib import Path | ||
| from typing import Optional | ||
| from fastapi import FastAPI, APIRouter | ||
| import colorlog | ||
|
|
||
| # Configure FastAPI/Uvicorn-style colored logging | ||
| handler = colorlog.StreamHandler() | ||
| handler.setFormatter(colorlog.ColoredFormatter( | ||
| '%(log_color)sINFO%(reset)s: %(message)s', | ||
| log_colors={ | ||
| 'DEBUG': 'blue', | ||
| 'INFO': 'green', | ||
| 'WARNING': 'yellow', | ||
| 'ERROR': 'red', | ||
| 'CRITICAL': 'bold_red', | ||
| } | ||
| )) | ||
|
|
||
| logger = colorlog.getLogger(__name__) | ||
| logger.addHandler(handler) | ||
| logger.setLevel(logging.INFO) | ||
| logger.propagate = False # Prevent duplicate logs | ||
|
|
||
PianoNic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| class RouterRegistry: | ||
| def __init__(self, controllers_path: str = "src.api.controllers"): | ||
| self.controllers_path = controllers_path | ||
| self.registered_count = 0 | ||
|
|
||
| def auto_register(self, app: FastAPI, controllers_dir: Optional[Path] = None) -> int: | ||
| """Auto-discover and register all controller routers""" | ||
| if controllers_dir is None: | ||
| # Get the directory where this registry file is located | ||
| registry_file = Path(__file__).resolve() | ||
| # This file is in /api/router_registry.py, controllers are in /api/controllers/ | ||
| controllers_dir = registry_file.parent / "controllers" | ||
|
|
||
| logger.info(f"Scanning controllers directory: {controllers_dir}") | ||
|
|
||
| if not controllers_dir.exists(): | ||
| logger.warning(f"Controllers directory not found: {controllers_dir}") | ||
| return 0 | ||
|
|
||
| self.registered_count = 0 | ||
|
|
||
| for file_path in controllers_dir.glob("*.py"): | ||
| if file_path.name.startswith("__") or file_path.name.startswith("."): | ||
| continue | ||
|
|
||
| module_name = f"{self.controllers_path}.{file_path.stem}" | ||
|
|
||
| try: | ||
| module = importlib.import_module(module_name) | ||
|
|
||
| if hasattr(module, 'router') and isinstance(module.router, APIRouter): | ||
| app.include_router(module.router) | ||
| logger.info(f"Registered: {file_path.stem}") | ||
| self.registered_count += 1 | ||
| else: | ||
| logger.debug(f"No router found in: {module_name}") | ||
|
|
||
| except ImportError as e: | ||
| logger.error(f"Import failed: {module_name} - {e}") | ||
| except Exception as e: | ||
| logger.error(f"Registration error: {module_name} - {e}") | ||
|
|
||
| logger.info(f"Successfully registered {self.registered_count} controller(s)") | ||
| return self.registered_count | ||
|
|
||
| # Global instance for easy use | ||
| router_registry = RouterRegistry() | ||
This file was deleted.
Oops, something went wrong.
This file contains hidden or 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 @@ | ||
| # Application layer: use cases and service interfaces |
This file contains hidden or 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,5 @@ | ||
| """Services package for image processing functionality.""" | ||
|
|
||
| from .image_service import auto_crop_image, crop_transparent_image, crop_by_background_color | ||
|
|
||
| __all__ = ['auto_crop_image', 'crop_transparent_image', 'crop_by_background_color'] |
1 change: 0 additions & 1 deletion
1
src/services/image_service.py → src/application/services/image_service.py
This file contains hidden or 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 |
|---|---|---|
| @@ -1,5 +1,4 @@ | ||
| from PIL import Image | ||
| from PIL.Image import Image as PILImage | ||
| import numpy as np | ||
| import io | ||
|
|
||
|
|
||
This file was deleted.
Oops, something went wrong.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.