init db and docker

This commit is contained in:
Harivansh Rathi 2025-12-30 16:15:46 +05:30
commit 1a980a7a70
5 changed files with 178 additions and 0 deletions

25
.gitignore vendored Normal file
View file

@ -0,0 +1,25 @@
# Python
__pycache__/
*.py[cod]
*$py.class
.venv/
venv/
*.egg-info/
# Environment
.env
.env.local
# IDE
.idea/
.vscode/
*.swp
*.swo
# Testing
.pytest_cache/
.coverage
htmlcov/
# Docker
docker-compose.override.yml

18
Dockerfile Normal file
View file

@ -0,0 +1,18 @@
FROM python:3.12-slim
WORKDIR /app
# Install uv for fast package management
RUN pip install uv
# Copy dependency files
COPY pyproject.toml .
# Install dependencies
RUN uv pip install --system -e .
# Copy application code
COPY app/ app/
# Run with uvicorn
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

43
db/init.sql Normal file
View file

@ -0,0 +1,43 @@
-- URL Shortener Database Schema
CREATE TABLE IF NOT EXISTS urls (
id BIGSERIAL PRIMARY KEY,
short_code VARCHAR(10) UNIQUE NOT NULL,
original_url TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
expires_at TIMESTAMP WITH TIME ZONE,
click_count BIGINT DEFAULT 0,
-- Metadata
user_agent TEXT,
ip_address INET
);
-- Index for fast lookups by short_code (most common query)
CREATE INDEX idx_urls_short_code ON urls(short_code);
-- Index for cleanup of expired URLs
CREATE INDEX idx_urls_expires_at ON urls(expires_at) WHERE expires_at IS NOT NULL;
-- Analytics table (for click tracking)
CREATE TABLE IF NOT EXISTS clicks (
id BIGSERIAL PRIMARY KEY,
short_code VARCHAR(10) NOT NULL,
clicked_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
ip_address INET,
user_agent TEXT,
referer TEXT,
country_code VARCHAR(2)
);
-- Partition-friendly index (clicks will be high volume)
CREATE INDEX idx_clicks_short_code ON clicks(short_code);
CREATE INDEX idx_clicks_clicked_at ON clicks(clicked_at);
-- Function to increment click count (atomic)
CREATE OR REPLACE FUNCTION increment_click_count(code VARCHAR(10))
RETURNS VOID AS $$
BEGIN
UPDATE urls SET click_count = click_count + 1 WHERE short_code = code;
END;
$$ LANGUAGE plpgsql;

62
docker-compose.yml Normal file
View file

@ -0,0 +1,62 @@
services:
# PostgreSQL - Primary database
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: urlshortner
POSTGRES_PASSWORD: localdev
POSTGRES_DB: urlshortner
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U urlshortner"]
interval: 5s
timeout: 5s
retries: 5
# Redis - Caching layer
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
# API Server (can scale with --scale api=3)
api:
build:
context: .
dockerfile: Dockerfile
environment:
DATABASE_URL: postgresql://urlshortner:localdev@postgres:5432/urlshortner
REDIS_URL: redis://redis:6379
MACHINE_ID: 1
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "8000" # Random port mapping for scaling
# Nginx - Load balancer
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- api
volumes:
postgres_data:
redis_data:

30
pyproject.toml Normal file
View file

@ -0,0 +1,30 @@
[project]
name = "url-shortner"
version = "0.1.0"
description = "Distributed URL shortening service for learning system design"
requires-python = ">=3.11"
dependencies = [
"fastapi>=0.109.0",
"uvicorn[standard]>=0.27.0",
"asyncpg>=0.29.0",
"redis>=5.0.0",
"pydantic>=2.5.0",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0.0",
"pytest-asyncio>=0.23.0",
"httpx>=0.26.0",
"ruff>=0.1.0",
]
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.ruff.lint]
select = ["E", "F", "I", "UP"]
[tool.pytest.ini_options]
asyncio_mode = "auto"