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
+52 -2
View File
@@ -90,11 +90,59 @@ def _get_redis_settings() -> RedisSettings:
async def on_startup(ctx: dict[str, Any]) -> None:
"""Called when worker starts."""
logger.info("ARQ worker starting...")
# Initialize Redis singleton (same as API lifespan)
from app.core.auth import init_redis
await init_redis()
# Initialize service container
from app.core.service_container import get_container
container = get_container()
await container.initialize()
# Initialize plugin registry and discover built-in plugins
from app.plugins.registry import get_registry
from app.core.db import get_engine
from app.core.event_bus import get_event_bus
from app.core.webhook_dispatcher import register_webhook_event_handlers
from sqlalchemy import select as sa_select
from app.models.plugin import Plugin as PluginModel
from sqlalchemy.ext.asyncio import async_sessionmaker
registry = get_registry()
registry.initialize(get_engine(), app=None)
registry.discover_builtins()
event_bus = get_event_bus()
async_session = async_sessionmaker(get_engine(), expire_on_commit=False)
# Activate plugins that are marked active in DB (register event handlers)
async with async_session() as db:
for name in registry.resolve_load_order():
plugin = registry.get_plugin(name)
if plugin is None:
continue
result = await db.execute(
sa_select(PluginModel).where(PluginModel.name == name)
)
plugin_record = result.scalar_one_or_none()
if plugin_record is None or not plugin_record.active:
continue
try:
await plugin.on_activate(db, container, event_bus)
logger.info(f"Worker: activated plugin {name}")
except Exception as exc:
logger.error(f"Worker: failed to activate plugin {name}: {exc}")
await db.commit()
# Register webhook dispatcher on the event bus
register_webhook_event_handlers(event_bus)
logger.info("Worker: webhook event handlers registered")
# Register search providers (normally done by app startup)
try:
from app.core.db import get_session_factory
from app.plugins.builtins.unified_search.provider_registry import auto_register_providers
factory = get_session_factory()
factory = async_session
async with factory() as db:
await auto_register_providers(db)
logger.info("Search providers registered for worker")
@@ -105,6 +153,8 @@ async def on_startup(ctx: dict[str, Any]) -> None:
async def on_shutdown(ctx: dict[str, Any]) -> None:
"""Called when worker shuts down."""
logger.info("ARQ worker shutting down...")
from app.core.auth import close_redis
await close_redis()
# ---------------------------------------------------------------------------