update the docker-compose
This commit is contained in:
parent
d8c349f4fb
commit
2026e0fbfb
12
README.md
12
README.md
@ -46,8 +46,8 @@ A FastAPI application with Docker and docker-compose support.
|
|||||||
BOXAPI_LOG_DIR=/app/logs/boxapi
|
BOXAPI_LOG_DIR=/app/logs/boxapi
|
||||||
BOXAPI_LOG_LEVEL=all # or "error" for production
|
BOXAPI_LOG_LEVEL=all # or "error" for production
|
||||||
|
|
||||||
# Redis for pagination (optional but recommended)
|
# Redis for pagination (optional but recommended)
|
||||||
REDIS_URL=redis://localhost:6379/0
|
REDIS_URL=redis://redis:6379/0 # Use 'redis' hostname in Docker
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Build and start the application:**
|
2. **Build and start the application:**
|
||||||
@ -191,6 +191,7 @@ The application uses environment variables for configuration. Copy `env.example`
|
|||||||
|
|
||||||
### Redis Configuration (for pagination)
|
### Redis Configuration (for pagination)
|
||||||
- `REDIS_URL`: Redis connection URL (default: `redis://localhost:6379/0`)
|
- `REDIS_URL`: Redis connection URL (default: `redis://localhost:6379/0`)
|
||||||
|
- **Note**: Redis is optional but recommended for pagination. If Redis is not available, the system will work without pagination (posts may be duplicated on subsequent calls).
|
||||||
|
|
||||||
### Database Configuration
|
### Database Configuration
|
||||||
- `POSTGRES_DB`: Database name (default: `fastapi_db`)
|
- `POSTGRES_DB`: Database name (default: `fastapi_db`)
|
||||||
@ -290,6 +291,13 @@ The application includes health checks that can be accessed at:
|
|||||||
- Check if PostgreSQL container is running: `docker-compose ps`
|
- Check if PostgreSQL container is running: `docker-compose ps`
|
||||||
- Check database logs: `docker-compose logs postgres`
|
- Check database logs: `docker-compose logs postgres`
|
||||||
|
|
||||||
|
5. **Redis connection issues:**
|
||||||
|
- Redis is optional - the system will work without it (posts may be duplicated)
|
||||||
|
- If you want Redis: `docker-compose up redis` to start Redis service
|
||||||
|
- Check Redis logs: `docker-compose logs redis`
|
||||||
|
- For Docker: use `REDIS_URL=redis://redis:6379/0`
|
||||||
|
- For local development: use `REDIS_URL=redis://localhost:6379/0`
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
1. Fork the repository
|
1. Fork the repository
|
||||||
|
|||||||
@ -16,6 +16,8 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
@ -45,9 +47,24 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- fastapi-network
|
- fastapi-network
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
networks:
|
||||||
|
- fastapi-network
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
fastapi-network:
|
fastapi-network:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
|
redis_data:
|
||||||
@ -1,22 +1,64 @@
|
|||||||
# utils/seen_store.py
|
# utils/seen_store.py
|
||||||
import os, redis
|
import os, redis
|
||||||
from typing import Iterable
|
from typing import Iterable, Optional
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
REDIS_URL = os.environ.get("REDIS_URL", "redis://localhost:6379/0")
|
REDIS_URL = os.environ.get("REDIS_URL", "redis://localhost:6379/0")
|
||||||
r = redis.from_url(REDIS_URL, decode_responses=True)
|
_redis_client: Optional[redis.Redis] = None
|
||||||
|
|
||||||
|
def _get_redis_client() -> Optional[redis.Redis]:
|
||||||
|
"""Get Redis client with error handling"""
|
||||||
|
global _redis_client
|
||||||
|
if _redis_client is not None:
|
||||||
|
return _redis_client
|
||||||
|
|
||||||
|
try:
|
||||||
|
_redis_client = redis.from_url(REDIS_URL, decode_responses=True)
|
||||||
|
# Test the connection
|
||||||
|
_redis_client.ping()
|
||||||
|
logger.info("✅ Redis connection established")
|
||||||
|
return _redis_client
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"⚠️ Redis connection failed: {e}")
|
||||||
|
logger.warning("📝 Pagination will be disabled - posts may be duplicated")
|
||||||
|
_redis_client = None
|
||||||
|
return None
|
||||||
|
|
||||||
def seen_key(username: str) -> str:
|
def seen_key(username: str) -> str:
|
||||||
return f"ig:seen:{username}"
|
return f"ig:seen:{username}"
|
||||||
|
|
||||||
def mark_seen(username: str, ids: Iterable[str]) -> None:
|
def mark_seen(username: str, ids: Iterable[str]) -> None:
|
||||||
if not ids: return
|
"""Mark posts as seen (Redis optional)"""
|
||||||
r.sadd(seen_key(username), *ids)
|
if not ids:
|
||||||
|
return
|
||||||
|
|
||||||
|
r = _get_redis_client()
|
||||||
|
if r is None:
|
||||||
|
return # Skip if Redis is not available
|
||||||
|
|
||||||
|
try:
|
||||||
|
r.sadd(seen_key(username), *ids)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"⚠️ Failed to mark posts as seen: {e}")
|
||||||
|
|
||||||
def filter_unseen(username: str, ids: Iterable[str]) -> list[str]:
|
def filter_unseen(username: str, ids: Iterable[str]) -> list[str]:
|
||||||
# pipeline for speed
|
"""Filter out already seen posts (Redis optional)"""
|
||||||
key = seen_key(username)
|
r = _get_redis_client()
|
||||||
pipe = r.pipeline()
|
if r is None:
|
||||||
for i in ids:
|
# If Redis is not available, return all IDs (no filtering)
|
||||||
pipe.sismember(key, i)
|
return list(ids)
|
||||||
flags = pipe.execute()
|
|
||||||
return [i for i, seen in zip(ids, flags) if not seen]
|
try:
|
||||||
|
# pipeline for speed
|
||||||
|
key = seen_key(username)
|
||||||
|
pipe = r.pipeline()
|
||||||
|
for i in ids:
|
||||||
|
pipe.sismember(key, i)
|
||||||
|
flags = pipe.execute()
|
||||||
|
return [i for i, seen in zip(ids, flags) if not seen]
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"⚠️ Failed to filter unseen posts: {e}")
|
||||||
|
# Fallback: return all IDs if Redis fails
|
||||||
|
return list(ids)
|
||||||
Loading…
Reference in New Issue
Block a user