Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ JWT_ALGORITHM=HS256
JWT_ACCESS_TOKEN_EXPIRE_DAYS=30

# Database Settings
POSTGRES_URL="postgresql://:your-db-password@POSTGRES_HOST:POSTGRES_PORT/POSTGRES_DB"
POSTGRES_HOST=db
POSTGRES_DB=mydb
POSTGRES_USER=myuser
POSTGRES_PORT=5432
POSTGRES_PASSWORD=mypassword
POSTGRES_POOL_SIZE=5
POSTGRES_MAX_OVERFLOW=10

Expand Down
4 changes: 1 addition & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ WORKDIR /app

# Set non-sensitive environment variables
ARG APP_ENV=production
ARG POSTGRES_URL

ENV APP_ENV=${APP_ENV} \
PYTHONFAULTHANDLER=1 \
PYTHONUNBUFFERED=1 \
PYTHONHASHSEED=random \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100 \
POSTGRES_URL=${POSTGRES_URL}
PIP_DEFAULT_TIMEOUT=100

# Install system dependencies
RUN apt-get update && apt-get install -y \
Expand Down
52 changes: 45 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ install:
pip install uv
uv sync

DOCKER_COMPOSE ?= docker-compose

set-env:
@if [ -z "$(ENV)" ]; then \
echo "ENV is not set. Usage: make set-env ENV=development|staging|production"; \
Expand Down Expand Up @@ -65,7 +67,12 @@ docker-build-env:
@./scripts/build-docker.sh $(ENV)

docker-run:
docker run -p 8000:8000 fastapi-langgraph-template
@ENV_FILE=.env.development; \
if [ ! -f $$ENV_FILE ]; then \
echo "Environment file $$ENV_FILE not found. Please create it."; \
exit 1; \
fi; \
APP_ENV=development $(DOCKER_COMPOSE) --env-file $$ENV_FILE up -d --build db app

docker-run-env:
@if [ -z "$(ENV)" ]; then \
Expand All @@ -76,7 +83,13 @@ docker-run-env:
echo "ENV is not valid. Must be one of: development, staging, production"; \
exit 1; \
fi
@./scripts/run-docker.sh $(ENV)
@ENV_FILE=.env.$(ENV); \
if [ ! -f $$ENV_FILE ]; then \
echo "Environment file $$ENV_FILE not found. Please create it."; \
exit 1; \
fi; \
APP_ENV=$(ENV) $(DOCKER_COMPOSE) --env-file $$ENV_FILE up -d --build db app
# @./scripts/ensure-db-user.sh $(ENV)

docker-logs:
@if [ -z "$(ENV)" ]; then \
Expand All @@ -87,7 +100,12 @@ docker-logs:
echo "ENV is not valid. Must be one of: development, staging, production"; \
exit 1; \
fi
@./scripts/logs-docker.sh $(ENV)
@ENV_FILE=.env.$(ENV); \
if [ ! -f $$ENV_FILE ]; then \
echo "Environment file $$ENV_FILE not found. Please create it."; \
exit 1; \
fi; \
APP_ENV=$(ENV) $(DOCKER_COMPOSE) --env-file $$ENV_FILE logs -f app db

docker-stop:
@if [ -z "$(ENV)" ]; then \
Expand All @@ -98,7 +116,12 @@ docker-stop:
echo "ENV is not valid. Must be one of: development, staging, production"; \
exit 1; \
fi
@./scripts/stop-docker.sh $(ENV)
@ENV_FILE=.env.$(ENV); \
if [ ! -f $$ENV_FILE ]; then \
echo "Environment file $$ENV_FILE not found. Please create it."; \
exit 1; \
fi; \
APP_ENV=$(ENV) $(DOCKER_COMPOSE) --env-file $$ENV_FILE down

# Docker Compose commands for the entire stack
docker-compose-up:
Expand All @@ -110,21 +133,36 @@ docker-compose-up:
echo "ENV is not valid. Must be one of: development, staging, production"; \
exit 1; \
fi
APP_ENV=$(ENV) docker-compose up -d
@ENV_FILE=.env.$(ENV); \
if [ ! -f $$ENV_FILE ]; then \
echo "Environment file $$ENV_FILE not found. Please create it."; \
exit 1; \
fi; \
APP_ENV=$(ENV) $(DOCKER_COMPOSE) --env-file $$ENV_FILE up -d

docker-compose-down:
@if [ -z "$(ENV)" ]; then \
echo "ENV is not set. Usage: make docker-compose-down ENV=development|staging|production"; \
exit 1; \
fi
APP_ENV=$(ENV) docker-compose down
@ENV_FILE=.env.$(ENV); \
if [ ! -f $$ENV_FILE ]; then \
echo "Environment file $$ENV_FILE not found. Please create it."; \
exit 1; \
fi; \
APP_ENV=$(ENV) $(DOCKER_COMPOSE) --env-file $$ENV_FILE down

docker-compose-logs:
@if [ -z "$(ENV)" ]; then \
echo "ENV is not set. Usage: make docker-compose-logs ENV=development|staging|production"; \
exit 1; \
fi
APP_ENV=$(ENV) docker-compose logs -f
@ENV_FILE=.env.$(ENV); \
if [ ! -f $$ENV_FILE ]; then \
echo "Environment file $$ENV_FILE not found. Please create it."; \
exit 1; \
fi; \
APP_ENV=$(ENV) $(DOCKER_COMPOSE) --env-file $$ENV_FILE logs -f

# Help
help:
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,14 @@ cp .env.example .env.[development|staging|production] # e.g. .env.development
### Database setup

1. Create a PostgreSQL database (e.g Supabase or local PostgreSQL)
2. Update the database connection string in your `.env` file:
2. Update the database connection settings in your `.env` file:

```bash
POSTGRES_URL="postgresql://:your-db-password@POSTGRES_HOST:POSTGRES_PORT/POSTGRES_DB"
POSTGRES_HOST=db
POSTGRES_PORT=5432
POSTGRES_DB=cool_db
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
```

- You don't have to create the tables manually, the ORM will handle that for you.But if you faced any issues,please run the `schemas.sql` file to create the tables manually.
Expand Down
6 changes: 5 additions & 1 deletion app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,11 @@ def __init__(self):
self.LOG_FORMAT = os.getenv("LOG_FORMAT", "json") # "json" or "console"

# Postgres Configuration
self.POSTGRES_URL = os.getenv("POSTGRES_URL", "")
self.POSTGRES_HOST = os.getenv("POSTGRES_HOST", "localhost")
self.POSTGRES_PORT = int(os.getenv("POSTGRES_PORT", "5432"))
self.POSTGRES_DB = os.getenv("POSTGRES_DB", "food_order_db")
self.POSTGRES_USER = os.getenv("POSTGRES_USER", "postgres")
self.POSTGRES_PASSWORD = os.getenv("POSTGRES_PASSWORD", "postgres")
self.POSTGRES_POOL_SIZE = int(os.getenv("POSTGRES_POOL_SIZE", "20"))
self.POSTGRES_MAX_OVERFLOW = int(os.getenv("POSTGRES_MAX_OVERFLOW", "10"))
self.CHECKPOINT_TABLES = ["checkpoint_blobs", "checkpoint_writes", "checkpoints"]
Expand Down
9 changes: 8 additions & 1 deletion app/core/langgraph/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
Literal,
Optional,
)
from urllib.parse import quote_plus

from asgiref.sync import sync_to_async
from langchain_core.messages import (
Expand Down Expand Up @@ -98,8 +99,14 @@ async def _get_connection_pool(self) -> AsyncConnectionPool:
# Configure pool size based on environment
max_size = settings.POSTGRES_POOL_SIZE

connection_url = (
"postgresql://"
f"{quote_plus(settings.POSTGRES_USER)}:{quote_plus(settings.POSTGRES_PASSWORD)}"
f"@{settings.POSTGRES_HOST}:{settings.POSTGRES_PORT}/{settings.POSTGRES_DB}"
)

self._connection_pool = AsyncConnectionPool(
settings.POSTGRES_URL,
connection_url,
open=False,
max_size=max_size,
kwargs={
Expand Down
7 changes: 6 additions & 1 deletion app/services/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,13 @@ def __init__(self):
max_overflow = settings.POSTGRES_MAX_OVERFLOW

# Create engine with appropriate pool configuration
connection_url = (
f"postgresql://{settings.POSTGRES_USER}:{settings.POSTGRES_PASSWORD}"
f"@{settings.POSTGRES_HOST}:{settings.POSTGRES_PORT}/{settings.POSTGRES_DB}"
)

self.engine = create_engine(
settings.POSTGRES_URL,
connection_url,
pool_pre_ping=True,
poolclass=QueuePool,
pool_size=pool_size,
Expand Down
29 changes: 24 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
version: '3.8'

services:
db:
image: pgvector/pgvector:pg16
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
restart: always
networks:
- monitoring

# Single API service with dynamic environment
app:
build:
Expand All @@ -16,11 +35,10 @@ services:
- .env.${APP_ENV:-development}
environment:
- APP_ENV=${APP_ENV:-development}
# Pass sensitive variables at runtime
- LLM_API_KEY=${LLM_API_KEY:-dummy-key-for-development}
- LANGFUSE_PUBLIC_KEY=${LANGFUSE_PUBLIC_KEY:-}
- LANGFUSE_SECRET_KEY=${LANGFUSE_SECRET_KEY:-}
- JWT_SECRET_KEY=${JWT_SECRET_KEY:-supersecretkeythatshouldbechangedforproduction}
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
Expand Down Expand Up @@ -79,4 +97,5 @@ networks:
driver: bridge

volumes:
grafana-storage:
grafana-storage:
postgres-data:
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ dependencies = [
"langchain-community>=0.3.20",
"tqdm>=4.67.1",
"colorama>=0.4.6",
"ddgs>=9.6.0",
]

[project.optional-dependencies]
Expand Down
18 changes: 16 additions & 2 deletions scripts/build-docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,27 @@ set +a

# Print confirmation with masked values
echo "Environment: $ENV"
echo "Database: *********$(echo $POSTGRES_URL | sed 's/.*@/@/')"
# Add a helper to mask any set values
mask_env() {
local value="$1"
if [ -z "$value" ]; then
echo "Not set"
else
echo "********"
fi
}

echo "Environment: $ENV"
# Mask database connection metadata instead of printing it directly
echo "Database host: $(mask_env "${POSTGRES_HOST:-${DB_HOST:-}}")"
echo "Database port: $(mask_env "${POSTGRES_PORT:-${DB_PORT:-}}")"
echo "Database name: $(mask_env "${POSTGRES_DB:-${DB_NAME:-}}")"
echo "Database user: $(mask_env "${POSTGRES_USER:-${DB_USER:-}}")"
echo "API keys: ******** (masked for security)"

# Build the Docker image with secrets but without showing them in console output
docker build --no-cache \
--build-arg APP_ENV="$ENV" \
--build-arg POSTGRES_URL="$POSTGRES_URL" \
--build-arg LLM_API_KEY="$LLM_API_KEY" \
--build-arg LANGFUSE_PUBLIC_KEY="$LANGFUSE_PUBLIC_KEY" \
--build-arg LANGFUSE_SECRET_KEY="$LANGFUSE_SECRET_KEY" \
Expand Down
21 changes: 8 additions & 13 deletions scripts/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ set -e
# Print initial environment values (before loading .env)
echo "Starting with these environment variables:"
echo "APP_ENV: ${APP_ENV:-development}"
if [[ -n "$POSTGRES_URL" && "$POSTGRES_URL" == *"@"* ]]; then
INITIAL_DB_DISPLAY=$(echo "$POSTGRES_URL" | sed 's/.*@/@/')
echo "Initial Database URL: *********$INITIAL_DB_DISPLAY"
else
echo "Initial Database URL: ${POSTGRES_URL:-Not set}"
fi
echo "Initial Database Host: $( [[ -n ${POSTGRES_HOST:-${DB_HOST:-}} ]] && echo 'set' || echo 'Not set' )"
echo "Initial Database Port: $( [[ -n ${POSTGRES_PORT:-${DB_PORT:-}} ]] && echo 'set' || echo 'Not set' )"
echo "Initial Database Name: $( [[ -n ${POSTGRES_DB:-${DB_NAME:-}} ]] && echo 'set' || echo 'Not set' )"
echo "Initial Database User: $( [[ -n ${POSTGRES_USER:-${DB_USER:-}} ]] && echo 'set' || echo 'Not set' )"

# Load environment variables from the appropriate .env file
if [ -f ".env.${APP_ENV}" ]; then
Expand Down Expand Up @@ -73,13 +71,10 @@ fi
echo -e "\nFinal environment configuration:"
echo "Environment: ${APP_ENV:-development}"

# Show only the part after @ for database URL (for security)
if [[ -n "$POSTGRES_URL" && "$POSTGRES_URL" == *"@"* ]]; then
DB_DISPLAY=$(echo "$POSTGRES_URL" | sed 's/.*@/@/')
echo "Database URL: *********$DB_DISPLAY"
else
echo "Database URL: ${POSTGRES_URL:-Not set}"
fi
echo "Database Host: $( [[ -n ${POSTGRES_HOST:-${DB_HOST:-}} ]] && echo 'set' || echo 'Not set' )"
echo "Database Port: $( [[ -n ${POSTGRES_PORT:-${DB_PORT:-}} ]] && echo 'set' || echo 'Not set' )"
echo "Database Name: $( [[ -n ${POSTGRES_DB:-${DB_NAME:-}} ]] && echo 'set' || echo 'Not set' )"
echo "Database User: $( [[ -n ${POSTGRES_USER:-${DB_USER:-}} ]] && echo 'set' || echo 'Not set' )"

echo "LLM Model: ${LLM_MODEL:-Not set}"
echo "Debug Mode: ${DEBUG:-false}"
Expand Down
Loading
Loading