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
+32 -9
View File
@@ -106,13 +106,29 @@ def _get_sync_engine():
@pytest.fixture(scope="session", autouse=True)
def db_setup():
"""Drop and recreate all tables once per test session."""
"""Drop and recreate all tables once per test session.
Uses SET lock_timeout to prevent deadlocks when multiple test processes
try to DROP SCHEMA simultaneously. Falls back to TRUNCATE if DROP fails.
"""
sync_eng = _get_sync_engine()
with sync_eng.connect() as conn:
# Drop all tables and types
conn.execute(text("DROP SCHEMA public CASCADE;"))
conn.execute(text("CREATE SCHEMA public;"))
conn.execute(text("GRANT ALL ON SCHEMA public TO leocrm;"))
# Set a short lock timeout to prevent deadlocks
conn.execute(text("SET lock_timeout = '5s';"))
try:
conn.execute(text("DROP SCHEMA IF EXISTS public CASCADE;"))
conn.execute(text("CREATE SCHEMA public;"))
conn.execute(text("GRANT ALL ON SCHEMA public TO leocrm;"))
except Exception:
# If DROP SCHEMA deadlocks, fall back to TRUNCATE all tables
conn.rollback()
conn.execute(text("SET lock_timeout = '5s';"))
conn.execute(text(
"DO $$ DECLARE r RECORD; BEGIN "
"FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname='public') "
"LOOP EXECUTE 'TRUNCATE TABLE public.' || quote_ident(r.tablename) || ' CASCADE'; "
"END LOOP; END $$;"
))
conn.commit()
sync_eng.dispose()
@@ -126,12 +142,19 @@ def db_setup():
asyncio.get_event_loop().run_until_complete(_create())
yield
# Cleanup after session
# Cleanup after session — use TRUNCATE instead of DROP to avoid deadlocks
sync_eng = _get_sync_engine()
with sync_eng.connect() as conn:
conn.execute(text("DROP SCHEMA public CASCADE;"))
conn.execute(text("CREATE SCHEMA public;"))
conn.execute(text("GRANT ALL ON SCHEMA public TO leocrm;"))
conn.execute(text("SET lock_timeout = '5s';"))
try:
conn.execute(text(
"DO $$ DECLARE r RECORD; BEGIN "
"FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname='public') "
"LOOP EXECUTE 'TRUNCATE TABLE public.' || quote_ident(r.tablename) || ' CASCADE'; "
"END LOOP; END $$;"
))
except Exception:
pass
conn.commit()
sync_eng.dispose()