feat: implement plugin architecture and application settings with Svelte UI

- Added plugin base and loader for backend extensibility
- Implemented application settings management with config persistence
- Created Svelte-based frontend with Dashboard and Settings pages
- Added API routes for plugins, tasks, and settings
- Updated documentation and specifications
- Improved project structure and developer tools
This commit is contained in:
2025-12-20 20:48:18 +03:00
parent ce703322c2
commit 2d8cae563f
98 changed files with 7894 additions and 5021 deletions

155
backend/src/app.py Normal file → Executable file
View File

@@ -1,77 +1,78 @@
# [DEF:AppModule:Module]
# @SEMANTICS: app, main, entrypoint, fastapi
# @PURPOSE: The main entry point for the FastAPI application. It initializes the app, configures CORS, sets up dependencies, includes API routers, and defines the WebSocket endpoint for log streaming.
# @LAYER: UI (API)
# @RELATION: Depends on the dependency module and API route modules.
import sys
from pathlib import Path
# Add project root to sys.path to allow importing superset_tool
# Assuming app.py is in backend/src/
project_root = Path(__file__).resolve().parent.parent.parent
sys.path.append(str(project_root))
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends
from fastapi.middleware.cors import CORSMiddleware
import asyncio
from .dependencies import get_task_manager
from .core.logger import logger
from .api.routes import plugins, tasks
# [DEF:App:Global]
# @SEMANTICS: app, fastapi, instance
# @PURPOSE: The global FastAPI application instance.
app = FastAPI(
title="Superset Tools API",
description="API for managing Superset automation tools and plugins.",
version="1.0.0",
)
# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Adjust this in production
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Include API routes
app.include_router(plugins.router, prefix="/plugins", tags=["Plugins"])
app.include_router(tasks.router, prefix="/tasks", tags=["Tasks"])
# [DEF:WebSocketEndpoint:Endpoint]
# @SEMANTICS: websocket, logs, streaming, real-time
# @PURPOSE: Provides a WebSocket endpoint for clients to connect to and receive real-time log entries for a specific task.
@app.websocket("/ws/logs/{task_id}")
async def websocket_endpoint(websocket: WebSocket, task_id: str, task_manager=Depends(get_task_manager)):
await websocket.accept()
logger.info(f"WebSocket connection established for task {task_id}")
try:
# Send initial logs if any
initial_logs = task_manager.get_task_logs(task_id)
for log_entry in initial_logs:
await websocket.send_json(log_entry.dict())
# Keep connection alive, ideally stream new logs as they come
# This part requires a more sophisticated log streaming mechanism (e.g., queues, pub/sub)
# For now, it will just keep the connection open and send initial logs.
while True:
await asyncio.sleep(1) # Keep connection alive, send heartbeat or check for new logs
# In a real system, new logs would be pushed here
except WebSocketDisconnect:
logger.info(f"WebSocket connection disconnected for task {task_id}")
except Exception as e:
logger.error(f"WebSocket error for task {task_id}: {e}")
# [/DEF]
# [DEF:RootEndpoint:Endpoint]
# @SEMANTICS: root, healthcheck
# @PURPOSE: A simple root endpoint to confirm that the API is running.
@app.get("/")
async def read_root():
return {"message": "Superset Tools API is running"}
# [/DEF]
# [DEF:AppModule:Module]
# @SEMANTICS: app, main, entrypoint, fastapi
# @PURPOSE: The main entry point for the FastAPI application. It initializes the app, configures CORS, sets up dependencies, includes API routers, and defines the WebSocket endpoint for log streaming.
# @LAYER: UI (API)
# @RELATION: Depends on the dependency module and API route modules.
import sys
from pathlib import Path
# Add project root to sys.path to allow importing superset_tool
# Assuming app.py is in backend/src/
project_root = Path(__file__).resolve().parent.parent.parent
sys.path.append(str(project_root))
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends
from fastapi.middleware.cors import CORSMiddleware
import asyncio
from .dependencies import get_task_manager
from .core.logger import logger
from .api.routes import plugins, tasks, settings
# [DEF:App:Global]
# @SEMANTICS: app, fastapi, instance
# @PURPOSE: The global FastAPI application instance.
app = FastAPI(
title="Superset Tools API",
description="API for managing Superset automation tools and plugins.",
version="1.0.0",
)
# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Adjust this in production
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Include API routes
app.include_router(plugins.router, prefix="/plugins", tags=["Plugins"])
app.include_router(tasks.router, prefix="/tasks", tags=["Tasks"])
app.include_router(settings.router, prefix="/settings", tags=["Settings"])
# [DEF:WebSocketEndpoint:Endpoint]
# @SEMANTICS: websocket, logs, streaming, real-time
# @PURPOSE: Provides a WebSocket endpoint for clients to connect to and receive real-time log entries for a specific task.
@app.websocket("/ws/logs/{task_id}")
async def websocket_endpoint(websocket: WebSocket, task_id: str, task_manager=Depends(get_task_manager)):
await websocket.accept()
logger.info(f"WebSocket connection established for task {task_id}")
try:
# Send initial logs if any
initial_logs = task_manager.get_task_logs(task_id)
for log_entry in initial_logs:
await websocket.send_json(log_entry.dict())
# Keep connection alive, ideally stream new logs as they come
# This part requires a more sophisticated log streaming mechanism (e.g., queues, pub/sub)
# For now, it will just keep the connection open and send initial logs.
while True:
await asyncio.sleep(1) # Keep connection alive, send heartbeat or check for new logs
# In a real system, new logs would be pushed here
except WebSocketDisconnect:
logger.info(f"WebSocket connection disconnected for task {task_id}")
except Exception as e:
logger.error(f"WebSocket error for task {task_id}: {e}")
# [/DEF]
# [DEF:RootEndpoint:Endpoint]
# @SEMANTICS: root, healthcheck
# @PURPOSE: A simple root endpoint to confirm that the API is running.
@app.get("/")
async def read_root():
return {"message": "Superset Tools API is running"}
# [/DEF]