-
Notifications
You must be signed in to change notification settings - Fork 590
Feat: Implemented spawing and closing of backend & sync microservice from tauri application itself #1009
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
Feat: Implemented spawing and closing of backend & sync microservice from tauri application itself #1009
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| import asyncio | ||
| import os | ||
| import platform | ||
| import signal | ||
| from fastapi import APIRouter | ||
| from pydantic import BaseModel | ||
| from app.logging.setup_logging import get_logger | ||
|
|
||
| logger = get_logger(__name__) | ||
|
|
||
| router = APIRouter(tags=["Shutdown"]) | ||
|
|
||
|
|
||
| class ShutdownResponse(BaseModel): | ||
| """Response model for shutdown endpoint.""" | ||
|
|
||
| status: str | ||
| message: str | ||
|
|
||
|
|
||
| async def _delayed_shutdown(delay: float = 0.5): | ||
| """ | ||
| Shutdown the server after a short delay to allow the response to be sent. | ||
|
|
||
| Args: | ||
| delay: Seconds to wait before signaling shutdown | ||
| """ | ||
| await asyncio.sleep(delay) | ||
| logger.info("Backend shutdown initiated, exiting process...") | ||
|
|
||
| if platform.system() == "Windows": | ||
| # Windows: SIGTERM doesn't work reliably with uvicorn subprocesses | ||
| os._exit(0) | ||
| else: | ||
| # Unix (Linux/macOS): SIGTERM allows cleanup handlers to run | ||
| os.kill(os.getpid(), signal.SIGTERM) | ||
|
|
||
|
|
||
| @router.post("/shutdown", response_model=ShutdownResponse) | ||
| async def shutdown(): | ||
| """ | ||
| Gracefully shutdown the PictoPy backend. | ||
|
|
||
| This endpoint schedules backend server termination after response is sent. | ||
| The frontend is responsible for shutting down the sync service separately. | ||
|
|
||
| Returns: | ||
| ShutdownResponse with status and message | ||
| """ | ||
| logger.info("Shutdown request received for PictoPy backend") | ||
|
|
||
| # Define callback to handle potential exceptions in the background task | ||
| def _handle_shutdown_exception(task: asyncio.Task): | ||
| try: | ||
| task.result() | ||
| except Exception as e: | ||
| logger.error(f"Shutdown task failed: {e}") | ||
|
|
||
| # Schedule backend shutdown after response is sent | ||
| shutdown_task = asyncio.create_task(_delayed_shutdown()) | ||
| shutdown_task.add_done_callback(_handle_shutdown_exception) | ||
|
|
||
| return ShutdownResponse( | ||
| status="shutting_down", | ||
| message="PictoPy backend shutdown initiated.", | ||
| ) |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -6,6 +6,7 @@ | |||||
| import os | ||||||
| import json | ||||||
|
|
||||||
| from app.config.settings import DATABASE_PATH, THUMBNAIL_IMAGES_PATH | ||||||
| from uvicorn import Config, Server | ||||||
| from fastapi import FastAPI | ||||||
| from fastapi.middleware.cors import CORSMiddleware | ||||||
|
|
@@ -25,6 +26,7 @@ | |||||
| from app.routes.images import router as images_router | ||||||
| from app.routes.face_clusters import router as face_clusters_router | ||||||
| from app.routes.user_preferences import router as user_preferences_router | ||||||
| from app.routes.shutdown import router as shutdown_router | ||||||
| from fastapi.openapi.utils import get_openapi | ||||||
| from app.logging.setup_logging import ( | ||||||
| configure_uvicorn_logging, | ||||||
|
|
@@ -38,6 +40,12 @@ | |||||
| # Configure Uvicorn logging to use our custom formatter | ||||||
| configure_uvicorn_logging("backend") | ||||||
|
|
||||||
| path = os.path.dirname(DATABASE_PATH) | ||||||
| os.makedirs(path, exist_ok=True) | ||||||
|
|
||||||
| thumbnail_path = os.path.dirname(THUMBNAIL_IMAGES_PATH) | ||||||
| os.makedirs(thumbnail_path, exist_ok=True) | ||||||
|
Comment on lines
+46
to
+47
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Thumbnail directory won't be created correctly.
🐛 Fix: Create the thumbnail directory directly-thumbnail_path = os.path.dirname(THUMBNAIL_IMAGES_PATH)
-os.makedirs(thumbnail_path, exist_ok=True)
+os.makedirs(THUMBNAIL_IMAGES_PATH, exist_ok=True)🤖 Prompt for AI Agents |
||||||
|
|
||||||
|
|
||||||
| @asynccontextmanager | ||||||
| async def lifespan(app: FastAPI): | ||||||
|
|
@@ -130,6 +138,7 @@ async def root(): | |||||
| app.include_router( | ||||||
| user_preferences_router, prefix="/user-preferences", tags=["User Preferences"] | ||||||
| ) | ||||||
| app.include_router(shutdown_router, tags=["Shutdown"]) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate "Shutdown" tag will appear in OpenAPI spec. The Remove the tag from one location: ♻️ Fix: Remove duplicate tag-app.include_router(shutdown_router, tags=["Shutdown"])
+app.include_router(shutdown_router)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
|
|
||||||
|
|
||||||
| # Entry point for running with: python3 main.py | ||||||
|
|
@@ -138,7 +147,6 @@ async def root(): | |||||
| logger = get_logger(__name__) | ||||||
| logger.info("Starting PictoPy backend server...") | ||||||
|
|
||||||
| # Create a simple config with log_config=None to disable Uvicorn's default logging | ||||||
| config = Config( | ||||||
| app=app, | ||||||
| host="localhost", | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -71,3 +71,4 @@ ruff>=0.0.241 | |
| psutil>=5.9.5 | ||
| pytest-asyncio>=1.0.0 | ||
| setuptools==66.1.1 | ||
| platformdirs==4.5.1 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1117,14 +1117,9 @@ | |
| "in": "query", | ||
| "required": false, | ||
| "schema": { | ||
| "allOf": [ | ||
| { | ||
| "$ref": "#/components/schemas/InputType" | ||
| } | ||
| ], | ||
| "$ref": "#/components/schemas/InputType", | ||
| "description": "Choose input type: 'path' or 'base64'", | ||
| "default": "path", | ||
| "title": "Input Type" | ||
| "default": "path" | ||
| }, | ||
| "description": "Choose input type: 'path' or 'base64'" | ||
| } | ||
|
|
@@ -1237,7 +1232,7 @@ | |
| "content": { | ||
| "application/json": { | ||
| "schema": { | ||
| "$ref": "#/components/schemas/app__schemas__user_preferences__ErrorResponse" | ||
| "$ref": "#/components/schemas/ErrorResponse" | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -1277,7 +1272,7 @@ | |
| "content": { | ||
| "application/json": { | ||
| "schema": { | ||
| "$ref": "#/components/schemas/app__schemas__user_preferences__ErrorResponse" | ||
| "$ref": "#/components/schemas/ErrorResponse" | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -1287,7 +1282,7 @@ | |
| "content": { | ||
| "application/json": { | ||
| "schema": { | ||
| "$ref": "#/components/schemas/app__schemas__user_preferences__ErrorResponse" | ||
| "$ref": "#/components/schemas/ErrorResponse" | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -1304,6 +1299,29 @@ | |
| } | ||
| } | ||
| } | ||
| }, | ||
| "/shutdown": { | ||
| "post": { | ||
| "tags": [ | ||
| "Shutdown", | ||
| "Shutdown" | ||
| ], | ||
| "summary": "Shutdown", | ||
| "description": "Gracefully shutdown the PictoPy backend.\n\nThis endpoint schedules backend server termination after response is sent.\nThe frontend is responsible for shutting down the sync service separately.\n\nReturns:\n ShutdownResponse with status and message", | ||
| "operationId": "shutdown_shutdown_post", | ||
| "responses": { | ||
| "200": { | ||
| "description": "Successful Response", | ||
| "content": { | ||
| "application/json": { | ||
| "schema": { | ||
| "$ref": "#/components/schemas/ShutdownResponse" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+1303
to
1325
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate tag in the shutdown endpoint. The tags array contains This likely stems from the router being defined with 🤖 Prompt for AI Agents |
||
| }, | ||
| "components": { | ||
|
|
@@ -1619,6 +1637,30 @@ | |
| ], | ||
| "title": "DeleteFoldersResponse" | ||
| }, | ||
| "ErrorResponse": { | ||
| "properties": { | ||
| "success": { | ||
| "type": "boolean", | ||
| "title": "Success" | ||
| }, | ||
| "error": { | ||
| "type": "string", | ||
| "title": "Error" | ||
| }, | ||
| "message": { | ||
| "type": "string", | ||
| "title": "Message" | ||
| } | ||
| }, | ||
| "type": "object", | ||
| "required": [ | ||
| "success", | ||
| "error", | ||
| "message" | ||
| ], | ||
| "title": "ErrorResponse", | ||
| "description": "Error response model" | ||
| }, | ||
| "FaceSearchRequest": { | ||
| "properties": { | ||
| "path": { | ||
|
|
@@ -2204,6 +2246,7 @@ | |
| "metadata": { | ||
| "anyOf": [ | ||
| { | ||
| "additionalProperties": true, | ||
| "type": "object" | ||
| }, | ||
| { | ||
|
|
@@ -2425,6 +2468,25 @@ | |
| ], | ||
| "title": "RenameClusterResponse" | ||
| }, | ||
| "ShutdownResponse": { | ||
| "properties": { | ||
| "status": { | ||
| "type": "string", | ||
| "title": "Status" | ||
| }, | ||
| "message": { | ||
| "type": "string", | ||
| "title": "Message" | ||
| } | ||
| }, | ||
| "type": "object", | ||
| "required": [ | ||
| "status", | ||
| "message" | ||
| ], | ||
| "title": "ShutdownResponse", | ||
| "description": "Response model for shutdown endpoint." | ||
| }, | ||
| "SuccessResponse": { | ||
| "properties": { | ||
| "success": { | ||
|
|
@@ -2897,30 +2959,6 @@ | |
| "error" | ||
| ], | ||
| "title": "ErrorResponse" | ||
| }, | ||
| "app__schemas__user_preferences__ErrorResponse": { | ||
| "properties": { | ||
| "success": { | ||
| "type": "boolean", | ||
| "title": "Success" | ||
| }, | ||
| "error": { | ||
| "type": "string", | ||
| "title": "Error" | ||
| }, | ||
| "message": { | ||
| "type": "string", | ||
| "title": "Message" | ||
| } | ||
| }, | ||
| "type": "object", | ||
| "required": [ | ||
| "success", | ||
| "error", | ||
| "message" | ||
| ], | ||
| "title": "ErrorResponse", | ||
| "description": "Error response model" | ||
| } | ||
| } | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.