Skip to content
Open
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
23 changes: 23 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
__pycache__/
*.pyc
*.pyo
*.pyd
*.swp
.Python
venv/
.env
.env.*
*.log
.git
.gitignore
*.md
tests/
.pytest_cache/
.ipynb_checkpoints/
models/
patternshield.db
*.png
*.jpg
*.jpeg
*.csv
*.tsv
33 changes: 33 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FLASK_ENV=development
DEBUG=True
SECRET_KEY=dev-secret-key
PORT=5000
HOST=0.0.0.0
MAX_WORKERS=4

DATABASE_URL=postgresql://user:password@postgres:5432/patternshield
DB_POOL_SIZE=10
POSTGRES_DB=patternshield
POSTGRES_USER=user
POSTGRES_PASSWORD=password

REDIS_URL=redis://redis:6379/0
CACHE_TTL=3600

CORS_ORIGINS=https://yourdomain.com,https://api.yourdomain.com
API_KEY_HEADER=X-API-Key
ALLOWED_API_KEYS=key1,key2

RATE_LIMIT_ENABLED=True
RATE_LIMIT_PER_MINUTE=100 per minute

LOG_LEVEL=INFO
LOG_FORMAT=json

ENABLE_METRICS=True
SENTRY_DSN=

MODEL_PATH=/app/models
TRANSFORMER_MODEL_NAME=distilbert_darkpattern
JWT_SECRET=jwt-secret
JWT_ALGORITHM=HS256
33 changes: 33 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FLASK_ENV=production
DEBUG=False
SECRET_KEY=replace-this-secret
PORT=5000
HOST=0.0.0.0
MAX_WORKERS=4

DATABASE_URL=postgresql://user:password@postgres:5432/patternshield
DB_POOL_SIZE=10
POSTGRES_DB=patternshield
POSTGRES_USER=user
POSTGRES_PASSWORD=password

REDIS_URL=redis://redis:6379/0
CACHE_TTL=3600

CORS_ORIGINS=https://yourdomain.com,https://api.yourdomain.com
API_KEY_HEADER=X-API-Key
ALLOWED_API_KEYS=key1,key2

RATE_LIMIT_ENABLED=True
RATE_LIMIT_PER_MINUTE=100 per minute

LOG_LEVEL=INFO
LOG_FORMAT=json

ENABLE_METRICS=True
SENTRY_DSN=

MODEL_PATH=/app/models
TRANSFORMER_MODEL_NAME=distilbert_darkpattern
JWT_SECRET=jwt-secret
JWT_ALGORITHM=HS256
33 changes: 33 additions & 0 deletions .env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FLASK_ENV=production
DEBUG=False
SECRET_KEY=replace-this-secret
PORT=5000
HOST=0.0.0.0
MAX_WORKERS=4

DATABASE_URL=postgresql://user:password@postgres:5432/patternshield
DB_POOL_SIZE=10
POSTGRES_DB=patternshield
POSTGRES_USER=user
POSTGRES_PASSWORD=password

REDIS_URL=redis://redis:6379/0
CACHE_TTL=3600

CORS_ORIGINS=https://yourdomain.com,https://api.yourdomain.com
API_KEY_HEADER=X-API-Key
ALLOWED_API_KEYS=key1,key2

RATE_LIMIT_ENABLED=True
RATE_LIMIT_PER_MINUTE=100 per minute

LOG_LEVEL=INFO
LOG_FORMAT=json

ENABLE_METRICS=True
SENTRY_DSN=

MODEL_PATH=/app/models
TRANSFORMER_MODEL_NAME=distilbert_darkpattern
JWT_SECRET=jwt-secret
JWT_ALGORITHM=HS256
57 changes: 57 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: CI/CD

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r backend/requirements.txt
pip install pytest flake8 black bandit pip-audit
- name: Lint
run: |
black --check backend
flake8 backend
- name: Security scan
run: |
bandit -r backend
pip-audit -r backend/requirements.txt
continue-on-error: true
- name: Run tests
env:
FLASK_ENV: test
run: pytest

build:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- name: Build Docker image
uses: docker/build-push-action@v6
with:
context: .
push: false
tags: patternshield/app:latest

deploy:
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy
run: |
echo "Run deployment script or action here"
32 changes: 32 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# PatternShield API

## Authentication
- Send API key in header `${API_KEY_HEADER}`.
- For transformer/ensemble endpoints also include `Authorization: Bearer <token>`.

## Endpoints
### `GET /health`
Returns service status.

### `GET /health/ready`
Checks database, cache, and model availability.

### `POST /analyze`
- Body: `{ "text": "...", "element_type": "div", "color": "#000000" }`
- Requires API key.

### `POST /analyze/transformer`
- Body same as above.
- Requires API key and JWT.

### `POST /analyze/ensemble`
- Combines transformer and rule-based outputs.

### `GET /metrics`
Prometheus metrics endpoint.

## Errors
- 400 validation error
- 401 authentication failure
- 429 rate limited
- 503 model unavailable
35 changes: 35 additions & 0 deletions DEPLOYMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Deployment Guide

## Prerequisites
- Docker and Docker Compose
- Python 3.12
- Access to PostgreSQL and Redis services

## Local Development
1. Copy `.env.example` to `.env` and adjust values.
2. Run `make install`.
3. Start the API: `python -m backend.app`.

## Docker Deployment
1. Build the image: `make docker-build`.
2. Start stack: `docker-compose --profile development up --build`.

## Cloud Deployment
- Push the built image to your registry.
- Provision PostgreSQL and Redis.
- Deploy using the provided `docker-compose.yml` or translate to your orchestration platform.

## Kubernetes (Optional)
- Convert services to deployments and apply ingress with TLS termination.

## Environment Variables
See `.env.example` for full list of supported options.

## Troubleshooting
- Check `/health/ready` endpoint for dependency status.
- Inspect logs with `make logs`.
- Verify database connectivity and credentials.

## Rollback
- Redeploy previous stable image tag.
- Restore database from backups if schema changes were applied.
55 changes: 55 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# syntax=docker/dockerfile:1.6

ARG PYTHON_VERSION=3.12

FROM python:${PYTHON_VERSION}-slim AS builder

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1

RUN apt-get update \
&& apt-get install -y --no-install-recommends build-essential gcc curl \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY backend/requirements.txt /app/requirements.txt

RUN python -m venv /opt/venv \
&& . /opt/venv/bin/activate \
&& pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir -r /app/requirements.txt

FROM python:${PYTHON_VERSION}-slim

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1

ARG APP_ENV=production
ENV APP_ENV=${APP_ENV}

RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser

RUN apt-get update \
&& apt-get install -y --no-install-recommends curl \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

WORKDIR /app

COPY backend /app/backend

ENV FLASK_APP=backend.app:create_app

USER appuser

HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
CMD curl -f http://localhost:5000/health/live || exit 1

EXPOSE 5000

ENTRYPOINT ["gunicorn", "-c", "backend/gunicorn.conf.py", "backend.app:create_app()"]
48 changes: 48 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.PHONY: help install test lint security-scan docker-build docker-run docker-compose-up docker-compose-down migrate logs shell deploy-staging deploy-production health

help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?##' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

install: ## Install dependencies
pip install -r backend/requirements.txt

test: ## Run tests
FLASK_ENV=test pytest

lint: ## Run formatting and lint checks
black --check backend
flake8 backend

security-scan: ## Run security checks
bandit -r backend || true
pip-audit -r backend/requirements.txt || true

docker-build: ## Build Docker image
docker build --build-arg APP_ENV=${FLASK_ENV:-production} -t patternshield/app:latest .

docker-run: ## Run Docker container
docker run --env-file .env -p 5000:5000 patternshield/app:latest

docker-compose-up: ## Start all services
docker-compose up --build -d

docker-compose-down: ## Stop all services
docker-compose down

migrate: ## Run database migrations
alembic upgrade head

logs: ## Tail application logs
docker-compose logs -f app

shell: ## Open shell in container
docker-compose exec app /bin/sh

deploy-staging: ## Deploy to staging
./scripts/deploy.sh staging

deploy-production: ## Deploy to production
./scripts/deploy.sh production

health: ## Check health endpoint
curl -f http://localhost:5000/health
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,18 @@ Created as a demonstration of ML evaluation capabilities for AI/ML role applicat
**Last Updated**: November 25, 2025
**Version**: 1.0
**Status**: Production-ready evaluation framework

## 🐳 Containerized API
- Build and run locally with Docker: `make docker-compose-up`
- Health checks available at `/health` and `/health/ready`.
- Metrics exposed at `/metrics` for Prometheus scraping.

## 🔐 Security & Configuration
- All secrets come from environment variables; see `.env.example`.
- API key header defaults to `X-API-Key`; JWT required for transformer endpoints.
- CORS whitelist configurable via `CORS_ORIGINS`.

## 📦 Deployment
- Use `make docker-build` to produce the production image.
- `scripts/deploy.sh` offers an interactive deployment helper for staging/production.
- `DEPLOYMENT.md` contains more detailed guidance.
1 change: 1 addition & 0 deletions backend/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Backend package for PatternShield."""
Loading
Loading