fix: runtime deployment fixes for Docker Compose stack

- Add .gitignore for __pycache__, node_modules, .playwright-mcp
- Add CLAUDE.md project instructions
- docker-compose: remove host port exposure for internal services,
  remove Ollama container (use host), add CORS origin, bake
  NEXT_PUBLIC_API_URL at build time, run alembic migrations on
  gateway startup, add CPU-only torch pre-install
- gateway: add CORS middleware, graceful Slack degradation without
  bot token, fix None guard on slack_handler
- gateway pyproject: add aiohttp dependency for slack-bolt async
- llm-pool pyproject: install litellm from GitHub (removed from PyPI),
  enable hatch direct references
- portal: enable standalone output in next.config.ts
- Remove orphaned migration 003_phase2_audit_kb.py (renamed to 004)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-24 12:26:34 -06:00
parent d936bcf361
commit 0e0ea5fb66
9 changed files with 694 additions and 293 deletions

View File

@@ -62,16 +62,38 @@ app = FastAPI(
version="0.1.0",
)
# ---------------------------------------------------------------------------
# CORS — allow portal origin to call gateway API
# ---------------------------------------------------------------------------
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:3000",
"http://127.0.0.1:3000",
"http://100.64.0.10:3000",
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ---------------------------------------------------------------------------
# Slack bolt app — initialized at module import time.
# signing_secret="" is safe for local dev/testing; set via env in production.
# ---------------------------------------------------------------------------
slack_app = AsyncApp(
token=settings.slack_bot_token or None,
signing_secret=settings.slack_signing_secret or None,
# In HTTP mode (Events API), token_verification_enabled must be True
# slack-bolt validates signing_secret on every inbound request
)
slack_app: AsyncApp | None = None
if settings.slack_bot_token and settings.slack_signing_secret:
slack_app = AsyncApp(
token=settings.slack_bot_token,
signing_secret=settings.slack_signing_secret,
)
else:
import logging
logging.getLogger(__name__).warning(
"SLACK_BOT_TOKEN or SLACK_SIGNING_SECRET not set — Slack adapter disabled"
)
# Async Redis client — shared across all request handlers
_redis: Redis | None = None # type: ignore[type-arg]
@@ -88,16 +110,14 @@ def _get_redis() -> Redis: # type: ignore[type-arg]
# ---------------------------------------------------------------------------
# Register Slack event handlers
# ---------------------------------------------------------------------------
register_slack_handlers(
slack_app=slack_app,
redis=_get_redis(),
get_session=async_session_factory,
)
# ---------------------------------------------------------------------------
# Slack request handler — adapts slack-bolt AsyncApp to FastAPI
# ---------------------------------------------------------------------------
slack_handler = AsyncSlackRequestHandler(slack_app)
slack_handler: AsyncSlackRequestHandler | None = None
if slack_app is not None:
register_slack_handlers(
slack_app=slack_app,
redis=_get_redis(),
get_session=async_session_factory,
)
slack_handler = AsyncSlackRequestHandler(slack_app)
# ---------------------------------------------------------------------------
# Register channel routers
@@ -133,6 +153,8 @@ async def slack_events(request: Request) -> Response:
CRITICAL: This endpoint MUST return HTTP 200 within 3 seconds.
All LLM/heavy work is dispatched to Celery inside the event handlers.
"""
if slack_handler is None:
return Response(content='{"error":"Slack not configured"}', status_code=503, media_type="application/json")
return await slack_handler.handle(request)

View File

@@ -13,6 +13,7 @@ dependencies = [
"konstruct-orchestrator",
"fastapi[standard]>=0.115.0",
"slack-bolt>=1.22.0",
"aiohttp>=3.9.0",
"python-telegram-bot>=21.0",
"httpx>=0.28.0",
"redis>=5.0.0",