restructure imports to work better with docker
This commit is contained in:
parent
4a4d5fe4dd
commit
844e015dfa
14 changed files with 145 additions and 36 deletions
50
Dockerfile.dev
Normal file
50
Dockerfile.dev
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Development Docker image with hot reload
|
||||
FROM python:3.13-slim
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
libc6-dev \
|
||||
zlib1g \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create non-root user for development
|
||||
RUN groupadd -r appuser && useradd -r -g appuser appuser
|
||||
|
||||
RUN mkdir -p /home/appuser
|
||||
RUN chown -R appuser:appuser /home/appuser
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
COPY LICENSE README.md ./
|
||||
|
||||
# Install hatch for dependency management
|
||||
RUN pip install --no-cache-dir hatch
|
||||
|
||||
# Copy pyproject.toml first for dependency caching
|
||||
COPY pyproject.toml ./
|
||||
|
||||
# Generate and install dependencies
|
||||
RUN hatch dep show requirements > requirements.txt
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy templates directory (needed at runtime)
|
||||
COPY templates/ ./templates/
|
||||
|
||||
# Change ownership of the app directory to appuser
|
||||
RUN chown -R appuser:appuser /app
|
||||
|
||||
# Switch to non-root user
|
||||
USER appuser
|
||||
|
||||
# Set environment variables
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
# Expose port
|
||||
EXPOSE 80
|
||||
|
||||
# The source code will be mounted as a volume
|
||||
# Install in editable mode and run with auto-reload
|
||||
CMD ["sh", "-c", "pip install -e . && uvicorn random_access.main:app --host 0.0.0.0 --port 80 --reload --reload-dir /app/src"]
|
||||
20
docker-compose.dev.yml
Normal file
20
docker-compose.dev.yml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# Development overrides for docker-compose
|
||||
# Use with: docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
|
||||
|
||||
services:
|
||||
# Override the api service to use the development configuration
|
||||
api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
env_file: .env
|
||||
ports:
|
||||
- "8000:80"
|
||||
volumes:
|
||||
# Mount source code for hot reload
|
||||
- ./src:/app/src:ro
|
||||
- ./pyproject.toml:/app/pyproject.toml:ro
|
||||
# Mount templates if they change during development
|
||||
- ./templates:/app/templates:ro
|
||||
healthcheck:
|
||||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/')"]
|
||||
36
run.sh
Executable file
36
run.sh
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/bash
|
||||
# Script to aid with running Docker containers for development and production environments
|
||||
|
||||
case "$1" in
|
||||
"dev")
|
||||
echo "🚀 Starting development environment with hot reload..."
|
||||
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up --build
|
||||
;;
|
||||
"prod")
|
||||
echo "🚀 Starting production environment..."
|
||||
docker-compose up --build
|
||||
;;
|
||||
"stop")
|
||||
echo "🛑 Stopping all services..."
|
||||
docker-compose down
|
||||
;;
|
||||
"logs")
|
||||
echo "📝 Showing logs..."
|
||||
docker-compose logs -f
|
||||
;;
|
||||
"clean")
|
||||
echo "🧹 Cleaning up containers and images..."
|
||||
docker-compose down --rmi all --volumes
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {dev|prod|stop|logs|clean}"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " dev - Start development environment with hot reload"
|
||||
echo " prod - Start production environment"
|
||||
echo " stop - Stop all services"
|
||||
echo " logs - Show logs"
|
||||
echo " clean - Clean up everything"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
|
@ -4,4 +4,10 @@ __version__ = "0.0.1"
|
|||
__author__ = "Micha R. Albert"
|
||||
__email__ = "info@micha.zone"
|
||||
|
||||
__all__ = ["__version__", "__author__", "__email__"]
|
||||
# Import the main app for easier access
|
||||
try:
|
||||
from .main import app
|
||||
__all__ = ["__version__", "__author__", "__email__", "app"]
|
||||
except ImportError:
|
||||
# If imports fail (e.g., missing env vars), just export metadata
|
||||
__all__ = ["__version__", "__author__", "__email__"]
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ from fastapi import Depends, HTTPException, status
|
|||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||
from pyairtable.formulas import match
|
||||
|
||||
from database import get_session_by_token_cached
|
||||
from settings import settings
|
||||
from .database import get_session_by_token_cached
|
||||
from .settings import settings
|
||||
|
||||
# Create HTTPBearer security scheme
|
||||
security = HTTPBearer(
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ from aiocache.serializers import PickleSerializer
|
|||
from pyairtable import Api as AirtableApi
|
||||
from pyairtable.formulas import match
|
||||
|
||||
from settings import settings
|
||||
from .settings import settings
|
||||
|
||||
logger = logging.getLogger("uvicorn.error")
|
||||
|
||||
|
|
@ -548,7 +548,7 @@ async def check_display_name_exists(display_name: str, users_table) -> bool:
|
|||
async def cleanup_expired_sessions_worker(sessions_table):
|
||||
"""Background worker to clean up expired sessions."""
|
||||
# Import here to avoid circular imports (auth imports database)
|
||||
from auth_utils import is_session_expired
|
||||
from .auth_utils import is_session_expired
|
||||
|
||||
# Run cleanup immediately at startup
|
||||
logger.info("Starting initial expired session cleanup at startup")
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import logging.handlers
|
|||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
from settings import settings
|
||||
from .settings import settings
|
||||
|
||||
|
||||
class JSONFormatter(logging.Formatter):
|
||||
|
|
|
|||
|
|
@ -16,21 +16,21 @@ from slowapi import Limiter
|
|||
from slowapi.errors import RateLimitExceeded
|
||||
from slowapi.middleware import SlowAPIMiddleware
|
||||
|
||||
from database import (
|
||||
from .database import (
|
||||
airtable_write_worker,
|
||||
cleanup_expired_sessions_worker,
|
||||
get_airtable_base,
|
||||
get_table,
|
||||
validate_all_schemas,
|
||||
)
|
||||
from logging_config import setup_logging, get_logger, log_request_context, log_performance_context
|
||||
from routes.auth import create_auth_router
|
||||
from routes.items import create_items_router, create_user_items_router
|
||||
from routes.system import create_system_router
|
||||
from routes.users import create_users_router
|
||||
from security import SecurityHeaders, get_client_ip
|
||||
from settings import settings
|
||||
from slack_integration import create_slack_app, setup_slack_handlers
|
||||
from .logging_config import setup_logging, get_logger, log_request_context, log_performance_context
|
||||
from .routes.auth import create_auth_router
|
||||
from .routes.items import create_items_router, create_user_items_router
|
||||
from .routes.system import create_system_router
|
||||
from .routes.users import create_users_router
|
||||
from .security import SecurityHeaders, get_client_ip
|
||||
from .settings import settings
|
||||
from .slack_integration import create_slack_app, setup_slack_handlers
|
||||
|
||||
# Setup enhanced logging that integrates with uvicorn
|
||||
setup_logging()
|
||||
|
|
|
|||
|
|
@ -14,14 +14,13 @@ from fastapi.templating import Jinja2Templates
|
|||
from pydantic import BaseModel, Field
|
||||
from slowapi import Limiter
|
||||
|
||||
from auth_utils import (
|
||||
from ..auth_utils import (
|
||||
decode_oidc_state,
|
||||
get_session_by_token,
|
||||
hash_token,
|
||||
is_session_expired,
|
||||
)
|
||||
|
||||
from database import (
|
||||
from ..database import (
|
||||
check_display_name_exists,
|
||||
create_session,
|
||||
create_user,
|
||||
|
|
@ -29,14 +28,14 @@ from database import (
|
|||
get_user_record,
|
||||
update_user_and_session,
|
||||
)
|
||||
from security import (
|
||||
from ..security import (
|
||||
create_safe_error_response,
|
||||
generate_secure_token,
|
||||
get_client_ip,
|
||||
validate_airtable_id,
|
||||
)
|
||||
from settings import settings
|
||||
from slack_integration import get_slack_user_id
|
||||
from ..settings import settings
|
||||
from ..slack_integration import get_slack_user_id
|
||||
|
||||
# Rate limiter for auth endpoints
|
||||
limiter = Limiter(key_func=get_client_ip)
|
||||
|
|
|
|||
|
|
@ -8,22 +8,20 @@ from fastapi.security import HTTPAuthorizationCredentials
|
|||
from pydantic import BaseModel, Field, field_validator
|
||||
from slowapi import Limiter
|
||||
|
||||
from auth_utils import extract_and_validate_auth, get_auth_credentials
|
||||
from database import (
|
||||
from ..auth_utils import extract_and_validate_auth, get_auth_credentials
|
||||
from ..database import (
|
||||
add_item_to_user,
|
||||
get_all_items,
|
||||
get_item_by_id,
|
||||
)
|
||||
from database import get_user_items as get_user_items_cached
|
||||
from database import (
|
||||
invalidate_user_items_cache,
|
||||
get_user_items as get_user_items_cached,
|
||||
)
|
||||
from security import (
|
||||
from ..security import (
|
||||
create_safe_error_response,
|
||||
get_client_ip,
|
||||
validate_airtable_id,
|
||||
)
|
||||
from settings import settings
|
||||
from ..settings import settings
|
||||
|
||||
# Rate limiter for item endpoints
|
||||
limiter = Limiter(key_func=get_client_ip)
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ from pydantic import BaseModel, Field
|
|||
from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler
|
||||
from slowapi import Limiter
|
||||
|
||||
from security import get_client_ip
|
||||
from settings import settings
|
||||
from ..security import get_client_ip
|
||||
from ..settings import settings
|
||||
|
||||
# Rate limiter for system endpoints
|
||||
limiter = Limiter(key_func=get_client_ip)
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ from fastapi.security import HTTPAuthorizationCredentials
|
|||
from pydantic import BaseModel, Field
|
||||
from slowapi import Limiter
|
||||
|
||||
from auth_utils import extract_and_validate_auth, get_auth_credentials
|
||||
from security import get_client_ip
|
||||
from settings import settings
|
||||
from ..auth_utils import extract_and_validate_auth, get_auth_credentials
|
||||
from ..security import get_client_ip
|
||||
from ..settings import settings
|
||||
|
||||
# Rate limiter for user endpoints
|
||||
limiter = Limiter(key_func=get_client_ip)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from typing import Any
|
|||
|
||||
from fastapi import HTTPException, Request, status
|
||||
|
||||
from settings import settings
|
||||
from .settings import settings
|
||||
|
||||
# Validation patterns
|
||||
AIRTABLE_ID_PATTERN = re.compile(r"^rec[A-Za-z0-9]{14}$")
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from zoneinfo import ZoneInfo
|
|||
from slack_bolt.async_app import AsyncAck, AsyncApp, AsyncRespond, AsyncSay
|
||||
from slack_bolt.response import BoltResponse
|
||||
|
||||
from database import (
|
||||
from .database import (
|
||||
get_all_games,
|
||||
get_detailed_user_items_for_slack,
|
||||
get_game_record,
|
||||
|
|
@ -17,7 +17,7 @@ from database import (
|
|||
get_user_by_slack_id,
|
||||
get_user_sessions,
|
||||
)
|
||||
from settings import settings
|
||||
from .settings import settings
|
||||
|
||||
|
||||
def get_ordinal_suffix(day: int) -> str:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue