Phase 1: Fix all critical release blockers (B1-B10)

B1: Remove duplicate get_redis() — singleton no longer overwritten
B2: Plugin routes now enforce activation status via require_active_plugin()
B3: Fix UploadFile ForwardRef error — remove functools.wraps from wrap_plugin_route
B4: DMS upload uses true streaming via save_stream() instead of RAM accumulation
B5: Worker on_startup registers plugin event handlers + webhook dispatcher
B6: Implement send_password_reset_email job, remove raw token logging
B7: Webhook SSRF protection (IP validation, no redirects), secret removed from response
B8: RLS repair migration 0044 + separate crm_runtime DB user (NOSUPERUSER, NOBYPASSRLS)
B9: Fix .env.docker.example AUTH_SECRET → SECRET_KEY
B10: Remove Redis default password, remove exposed DB/Redis ports

Also: add frontend_url to config, add SMTP settings to .env.docker.example,
update prestart.sh to use MIGRATION_DATABASE_URL for alembic.
This commit is contained in:
Agent Zero
2026-07-26 20:45:42 +02:00
parent 7a14973c68
commit 5ec1fc9b05
32 changed files with 1781 additions and 76 deletions
+15 -5
View File
@@ -6,7 +6,7 @@ import time
import traceback
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException, Request
from fastapi import FastAPI, HTTPException, Request, Depends
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
@@ -208,6 +208,11 @@ async def lifespan(app: FastAPI):
init_permission_registry(active_plugin_names)
logger.info("Permission registry initialized with %d active plugins", len(active_plugin_names))
# Register webhook dispatcher on the event bus
from app.core.webhook_dispatcher import register_webhook_event_handlers
register_webhook_event_handlers(event_bus)
logger.info("Webhook event handlers registered")
# Register field definitions from active plugins only
from app.core.permission_registry import get_permission_registry
for name in active_plugin_names:
@@ -368,9 +373,10 @@ def create_app() -> FastAPI:
app.include_router(errors.router)
# ── Register plugin routes for all built-in plugins ──
# Routes are registered here (before app start); activation status
# is enforced at runtime via require_permission and plugin checks.
# Routes are registered at app creation time so OpenAPI docs are complete.
# Activation status is enforced per-request via require_active_plugin().
import importlib
from app.deps import require_active_plugin
# Discover all built-in plugin modules and register their routes
plugin_modules = [
"app.plugins.builtins.tags",
@@ -399,6 +405,7 @@ def create_app() -> FastAPI:
for attr_name in dir(mod):
attr = getattr(mod, attr_name)
if isinstance(attr, type) and hasattr(attr, "manifest") and hasattr(attr.manifest, "routes"):
plugin_name = getattr(attr.manifest, "name", mod_name.split(".")[-1])
for route_def in attr.manifest.routes:
try:
router_module = importlib.import_module(route_def.module)
@@ -407,13 +414,16 @@ def create_app() -> FastAPI:
for route in router.routes:
if hasattr(route, 'endpoint'):
route.endpoint = wrap_plugin_route(route.endpoint)
app.include_router(router)
# Add active-plugin check as a router-level dependency
app.include_router(
router,
dependencies=[Depends(require_active_plugin(plugin_name))],
)
except Exception as exc:
logger.error(f"Failed to register route {route_def.module}.{route_def.router_attr}: {exc}")
break
except Exception as exc:
logger.error(f"Failed to register plugin routes for {mod_name}: {exc}")
# Do NOT register plugin routes here — lifespan() handles it for active plugins only
# ── Serve frontend static files (SPA) ──────────────────────────────
# Mount built frontend assets (JS, CSS, images)