chore: polish code with little update (#182)
- Run Docker container as non-root user (appuser) to minimize security risks - Add Docker HEALTHCHECK for better container orchestration - Make CORS configurable via ALLOWED_ORIGINS env var with security warning - Replace assertions with proper error handling (TypeError/ValueError) - Add 30s timeout to HTTP requests to prevent hanging connections - Disable auto-reload in production uvicorn settings
This commit is contained in:
@@ -8,6 +8,15 @@ RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
|
||||
|
||||
COPY ./api /app/api
|
||||
|
||||
ENV PORT=80
|
||||
# Create non-root user
|
||||
RUN groupadd -r appuser && useradd -r -g appuser appuser && \
|
||||
chown -R appuser:appuser /app
|
||||
|
||||
USER appuser
|
||||
|
||||
ENV PORT=8080
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/api/v1/models').read()"
|
||||
|
||||
CMD ["sh", "-c", "uvicorn api.app:app --host 0.0.0.0 --port ${PORT}"]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
import uvicorn
|
||||
from fastapi import FastAPI
|
||||
@@ -23,9 +24,16 @@ logging.basicConfig(
|
||||
)
|
||||
app = FastAPI(**config)
|
||||
|
||||
allowed_origins = os.environ.get("ALLOWED_ORIGINS", "*")
|
||||
origins_list = [origin.strip() for origin in allowed_origins.split(",")] if allowed_origins != "*" else ["*"]
|
||||
|
||||
# Warn if CORS allows all origins
|
||||
if origins_list == ["*"]:
|
||||
logging.warning("CORS is configured to allow all origins (*). Set ALLOWED_ORIGINS environment variable to restrict access.")
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_origins=origins_list, # nosec - configurable via ALLOWED_ORIGINS env var
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
@@ -61,4 +69,5 @@ async def validation_exception_handler(request, exc):
|
||||
handler = Mangum(app)
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)
|
||||
# Bind to 0.0.0.0 for container environments, network is handled by network policies and load balancers
|
||||
uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=False) # nosec B104
|
||||
|
||||
@@ -7,8 +7,6 @@ from botocore.exceptions import ClientError
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||
|
||||
from api.setting import DEFAULT_API_KEYS
|
||||
|
||||
api_key_param = os.environ.get("API_KEY_PARAM_NAME")
|
||||
api_key_secret_arn = os.environ.get("API_KEY_SECRET_ARN")
|
||||
api_key_env = os.environ.get("API_KEY")
|
||||
@@ -31,8 +29,9 @@ elif api_key_secret_arn:
|
||||
elif api_key_env:
|
||||
api_key = api_key_env
|
||||
else:
|
||||
# For local use only.
|
||||
api_key = DEFAULT_API_KEYS
|
||||
raise RuntimeError(
|
||||
"API Key is not configured. Please set up your API Key."
|
||||
)
|
||||
|
||||
security = HTTPBearer()
|
||||
|
||||
|
||||
@@ -310,7 +310,8 @@ class BedrockModel(BaseChatModel):
|
||||
if message.role != "system":
|
||||
# ignore system messages here
|
||||
continue
|
||||
assert isinstance(message.content, str)
|
||||
if not isinstance(message.content, str):
|
||||
raise TypeError(f"System message content must be a string, got {type(message.content).__name__}")
|
||||
system_prompts.append({"text": message.content})
|
||||
|
||||
return system_prompts
|
||||
@@ -580,7 +581,8 @@ class BedrockModel(BaseChatModel):
|
||||
tool_config["toolChoice"] = {"auto": {}}
|
||||
else:
|
||||
# Specific tool to use
|
||||
assert "function" in chat_request.tool_choice
|
||||
if "function" not in chat_request.tool_choice:
|
||||
raise ValueError("tool_choice must contain 'function' key when specifying a specific tool")
|
||||
tool_config["toolChoice"] = {"tool": {"name": chat_request.tool_choice["function"].get("name", "")}}
|
||||
args["toolConfig"] = tool_config
|
||||
# add Additional fields to enable extend thinking
|
||||
@@ -784,7 +786,7 @@ class BedrockModel(BaseChatModel):
|
||||
return base64.b64decode(image_data), content_type.group(1)
|
||||
|
||||
# Send a request to the image URL
|
||||
response = requests.get(image_url)
|
||||
response = requests.get(image_url, timeout=30)
|
||||
# Check if the request was successful
|
||||
if response.status_code == 200:
|
||||
content_type = response.headers.get("Content-Type")
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import os
|
||||
|
||||
DEFAULT_API_KEYS = "bedrock"
|
||||
|
||||
API_ROUTE_PREFIX = os.environ.get("API_ROUTE_PREFIX", "/api/v1")
|
||||
|
||||
TITLE = "Amazon Bedrock Proxy APIs"
|
||||
|
||||
Reference in New Issue
Block a user