diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8a1ca13 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +.venv +__pycache__ +.pytest_cache +.git +test/ +*.db +server.log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..966d31d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.11-slim + +# Set the working directory +WORKDIR /app + +# Copy the requirements file and install dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the rest of the application code +COPY . . + +# Ensure the database directory exists for SQLite persistence +RUN mkdir -p /app/db + +# Expose the application port +EXPOSE 8000 + +# Command to start the application +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/cache/__init__.py b/cache/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cache/redis_client.py b/cache/redis_client.py index b54de85..259f03c 100644 --- a/cache/redis_client.py +++ b/cache/redis_client.py @@ -1,3 +1,7 @@ import redis +import os -redis_client = redis.Redis(host="localhost", port=6379, decode_responses=True) +redis_host = os.getenv("REDIS_HOST", "localhost") +redis_port = int(os.getenv("REDIS_PORT", 6379)) + +redis_client = redis.Redis(host=redis_host, port=redis_port, decode_responses=True) diff --git a/db/__init__.py b/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/db/urls.db b/db/urls.db index dc569a6..d41cbb0 100644 Binary files a/db/urls.db and b/db/urls.db differ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5c7bf36 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +services: + web: + build: . + ports: + - "8000:8000" + volumes: + - ./db:/app/db + environment: + - REDIS_HOST=redis + - REDIS_PORT=6379 + depends_on: + - redis + + redis: + image: "redis:alpine" + ports: + - "6379:6379" diff --git a/main.py b/main.py index e4186f4..dcb9bfc 100644 --- a/main.py +++ b/main.py @@ -37,19 +37,24 @@ def shorten_url(request: schemas.URLCreate, db: Session = Depends(get_db)): crud.create_url(db, short_code, request.long_url) - # set it in cache for a hour - redis_client.set(short_code, request.long_url, ex=3600) + # set it in cache for a hour, but don't fail if redis is down + try: + redis_client.set(short_code, request.long_url, ex=3600) + except Exception: + print("Redis is unavailable, skipping cache set") return {"short_url": f"http://localhost:8000/{short_code}"} @app.get("/{short_code}") def redirect_url(short_code: str, db: Session = Depends(get_db)): - # check the redis - check_url = redis_client.get(short_code) - - if check_url: - return RedirectResponse(url=check_url) + # check the redis, but don't fail if redis is down + try: + check_url = redis_client.get(short_code) + if check_url: + return RedirectResponse(url=check_url) + except Exception: + print("Redis is unavailable, skipping cache check") # get url from the db db_url = crud.get_url(db, short_code) @@ -57,5 +62,10 @@ def redirect_url(short_code: str, db: Session = Depends(get_db)): if not db_url: raise HTTPException(status_code=404, detail="URL not found") - redis_client.set(short_code, db_url.long_url) + # update cache if possible + try: + redis_client.set(short_code, db_url.long_url) + except Exception: + pass + return RedirectResponse(url=db_url.long_url) diff --git a/requirements.txt b/requirements.txt index f3770a4..f87e051 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,7 @@ fastapi uvicorn sqlalchemy redis +pydantic +pytest +httpx +pytest-mock