Neatly packaged for docker

This commit is contained in:
Frederik Beimgraben 2025-09-01 05:00:02 +02:00
parent 1cae3b3479
commit 21c8153519
33 changed files with 2669 additions and 365 deletions

69
.env.example Normal file
View File

@ -0,0 +1,69 @@
# STUPA PDF API Configuration Example
# Copy this file to .env and update with your values
# Database Configuration
MYSQL_HOST=db
MYSQL_PORT=3306
MYSQL_DB=stupa
MYSQL_USER=stupa
MYSQL_PASSWORD=your_secure_password_here
MYSQL_ROOT_PASSWORD=your_secure_root_password_here
# Authentication
# Master key for admin access - keep this secure!
MASTER_KEY=your_secure_master_key_here
# Rate Limiting
# Requests per minute per IP address
RATE_IP_PER_MIN=60
# Requests per minute per application key
RATE_KEY_PER_MIN=30
# Application Settings
# Timezone for the application
TZ=Europe/Berlin
# PDF Templates (paths inside container - don't change unless you know what you're doing)
QSM_TEMPLATE=/app/assets/qsm.pdf
VSM_TEMPLATE=/app/assets/vsm.pdf
# Frontend Configuration
NODE_ENV=production
# Optional: CORS Configuration (for production)
# CORS_ORIGINS=["https://your-domain.com"]
# Optional: Debug Mode (never enable in production)
# DEBUG=false
# Optional: Database Connection Pool
# DB_POOL_SIZE=10
# DB_MAX_OVERFLOW=20
# Optional: File Upload Limits
# MAX_UPLOAD_SIZE=104857600 # 100MB in bytes
# MAX_ATTACHMENTS_PER_APP=30
# Optional: Session Configuration
# SESSION_TIMEOUT=3600 # 1 hour in seconds
# Optional: Email Configuration (for future notifications)
# SMTP_HOST=smtp.example.com
# SMTP_PORT=587
# SMTP_USER=notifications@example.com
# SMTP_PASSWORD=smtp_password_here
# SMTP_FROM=noreply@example.com
# Optional: Logging Configuration
# LOG_LEVEL=INFO
# LOG_FILE=/var/log/stupa-api.log
# Optional: Backup Configuration
# BACKUP_ENABLED=true
# BACKUP_SCHEDULE="0 2 * * *" # Daily at 2 AM
# BACKUP_RETENTION_DAYS=30
# Production Security Headers (uncomment for production)
# SECURE_HEADERS=true
# HSTS_ENABLED=true
# CSP_ENABLED=true

251
.gitignore vendored
View File

@ -1,164 +1,119 @@
# ---> Python
# Byte-compiled / optimized / DLL files
# Python
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.coverage
.mypy_cache/
.dmypy.json
dmypy.json
.ruff_cache/
# Pyre type checker
.pyre/
# Backend specific
backend/build/
backend/dist/
backend/.eggs/
backend/venv/
backend/env/
backend/.venv/
# pytype static type analyzer
.pytype/
# Frontend specific
frontend/node_modules/
frontend/dist/
frontend/build/
frontend/.parcel-cache/
frontend/.next/
frontend/.nuxt/
frontend/.vuepress/
frontend/.docusaurus/
frontend/npm-debug.log*
frontend/yarn-debug.log*
frontend/yarn-error.log*
frontend/lerna-debug.log*
frontend/.pnpm-debug.log*
# Cython debug symbols
cython_debug/
# Environment files
.env
.env.*
!.env.example
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
# Docker
docker-compose.override.yml
# Database
*.sqlite
*.sqlite3
*.db
mysql-data/
# Logs
logs/
*.log
# Testing
coverage/
.nyc_output/
test-results/
junit.xml
# Temporary files
tmp/
temp/
*.tmp
*.temp
*.bak
*.backup
*.orig
# OS files
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msm
*.msp
*.lnk
# Documentation build
docs/_build/
docs/.doctrees/
site/
# Backups
backups/
*.sql
*.dump
# Project specific
.ropeproject/
uploads/
downloads/
cache/
# Certificates
*.pem
*.key
*.crt
*.csr
*.der
*.p12
*.pfx
# Monitoring
prometheus_data/
grafana_data/
# PDF
*.pdf

181
Makefile Normal file
View File

@ -0,0 +1,181 @@
# STUPA PDF API Makefile
# Convenient commands for development and deployment
.PHONY: help
help: ## Show this help message
@echo 'Usage: make [target]'
@echo ''
@echo 'Targets:'
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-20s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
.PHONY: install
install: ## Initial setup - create .env and install dependencies
@echo "Setting up STUPA PDF API..."
@chmod +x scripts/*.sh
@./scripts/create-env.sh
@echo "Setup complete! Run 'make start' to begin."
.PHONY: start
start: ## Start all services
@docker compose up -d
@echo "Services starting..."
@echo "Frontend: http://localhost:3000"
@echo "API: http://localhost:8000"
@echo "API Docs: http://localhost:8000/docs"
@echo "Database UI: http://localhost:8080"
.PHONY: stop
stop: ## Stop all services
@docker compose down
.PHONY: restart
restart: ## Restart all services
@docker compose restart
.PHONY: status
status: ## Show service status
@docker compose ps
.PHONY: logs
logs: ## Show logs for all services
@docker compose logs -f
.PHONY: logs-api
logs-api: ## Show API logs
@docker compose logs -f api
.PHONY: logs-frontend
logs-frontend: ## Show frontend logs
@docker compose logs -f frontend
.PHONY: logs-db
logs-db: ## Show database logs
@docker compose logs -f db
.PHONY: build
build: ## Build all services
@docker compose build
.PHONY: build-api
build-api: ## Build API service
@docker compose build api
.PHONY: build-frontend
build-frontend: ## Build frontend service
@docker compose build frontend
.PHONY: shell-api
shell-api: ## Open shell in API container
@docker compose exec api /bin/bash
.PHONY: shell-frontend
shell-frontend: ## Open shell in frontend container
@docker compose exec frontend /bin/sh
.PHONY: shell-db
shell-db: ## Open MySQL client
@docker compose exec db mysql -u root -p$${MYSQL_ROOT_PASSWORD} $${MYSQL_DB}
.PHONY: migrate
migrate: ## Run database migrations
@echo "Running database migrations..."
@./scripts/dev.sh migrate
.PHONY: test
test: ## Run all tests
@echo "Running tests..."
@./scripts/dev.sh test
.PHONY: lint
lint: ## Run linters
@echo "Running linters..."
@./scripts/dev.sh lint
.PHONY: format
format: ## Format code
@echo "Formatting code..."
@./scripts/dev.sh format
.PHONY: clean
clean: ## Clean up temporary files and caches
@echo "Cleaning up..."
@find backend -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
@find backend -type f -name "*.pyc" -delete 2>/dev/null || true
@docker system prune -f
.PHONY: reset
reset: ## Reset everything (WARNING: deletes all data!)
@echo "WARNING: This will delete all data!"
@read -p "Are you sure? Type 'yes' to continue: " confirm && \
if [ "$$confirm" = "yes" ]; then \
docker compose down -v; \
docker compose down --rmi local; \
echo "Reset complete!"; \
else \
echo "Aborted."; \
fi
.PHONY: backup
backup: ## Create database backup
@mkdir -p backups
@docker compose exec -T db mysqldump -u root -p$${MYSQL_ROOT_PASSWORD} $${MYSQL_DB} > backups/backup-$$(date +%Y%m%d-%H%M%S).sql
@echo "Backup created in backups/"
.PHONY: dev
dev: ## Start in development mode with hot reload
@docker compose -f docker-compose.yml -f docker-compose.watch.yml up
.PHONY: prod
prod: ## Start in production mode
@docker compose up -d
.PHONY: update
update: ## Update dependencies
@echo "Updating backend dependencies..."
@cd backend && pip-compile --upgrade requirements.in -o requirements.txt
@echo "Updating frontend dependencies..."
@cd frontend && npm update
.PHONY: docs
docs: ## Open documentation in browser
@echo "Opening documentation..."
@python -m webbrowser "file://$$(pwd)/docs/README.md" || open docs/README.md || xdg-open docs/README.md
.PHONY: api-docs
api-docs: ## Open API documentation in browser
@echo "Opening API documentation..."
@python -m webbrowser "http://localhost:8000/docs" || open "http://localhost:8000/docs" || xdg-open "http://localhost:8000/docs"
.PHONY: check-env
check-env: ## Check if .env file exists
@if [ ! -f .env ]; then \
echo "ERROR: .env file not found!"; \
echo "Run 'make install' to create one."; \
exit 1; \
else \
echo ".env file exists ✓"; \
fi
.PHONY: validate
validate: check-env ## Validate configuration
@echo "Validating configuration..."
@docker compose config > /dev/null
@echo "Configuration is valid ✓"
.PHONY: ps
ps: ## Show running containers
@docker ps --filter "label=com.docker.compose.project=stupa-pdf-api"
.PHONY: stats
stats: ## Show container resource usage
@docker stats --no-stream $$(docker compose ps -q)
.PHONY: version
version: ## Show version information
@echo "STUPA PDF API Version Information:"
@echo "Backend: Python 3.11 with FastAPI"
@echo "Frontend: React 18 with TypeScript"
@echo "Database: MySQL 8.0"
@grep -m1 version frontend/package.json || echo "Frontend version not found"
@docker --version
@docker compose version

273
README.md
View File

@ -1,42 +1,249 @@
# stupa-pdf-api
# STUPA PDF API
## Recent Updates
A comprehensive system for managing student parliament (STUPA) funding applications with PDF processing, form generation, and document management capabilities.
### Dark Mode Loading Screen Fix
- Fixed white background issue in loading screens when using dark mode
- The LoadingSpinner component now properly respects the theme settings
## 🚀 Features
### File Attachments Feature
- Users can now upload up to 30 attachments per application
- Maximum total size: 100MB
- Files are stored securely in the database
- Full CRUD operations with progress tracking
- See [ATTACHMENT_FEATURE.md](ATTACHMENT_FEATURE.md) for details
- **PDF Processing**: Parse and extract structured data from uploaded PDF forms
- **Dynamic Form Generation**: Generate filled PDF forms from application data
- **Document Management**: Upload and manage supporting documents (up to 30 files, 100MB total)
- **Comparison Offers**: Manage cost comparison requirements with 3-offer validation
- **Secure Access**: Application-specific keys and master key authentication
- **Modern UI**: React-based frontend with Material-UI components
- **API Documentation**: Auto-generated OpenAPI/Swagger documentation
- **Rate Limiting**: Configurable rate limits for API protection
- **Database UI**: Integrated Adminer for database management
### Comparison Offers Feature
- Each cost position now requires 3 comparison offers
- Alternative: Users can provide a justification if offers are not applicable
- Visual indicators show completion status
- Offers can be linked to uploaded attachments
- See [COMPARISON_OFFERS_FEATURE.md](COMPARISON_OFFERS_FEATURE.md) for details
## 📋 Prerequisites
### Rate Limiting Configuration
- API rate limiting is now disabled for localhost connections
- Includes localhost (127.0.0.1) and Docker internal IPs (172.x.x.x)
- Production deployments maintain rate limiting for external IPs
- Docker Engine 20.10+
- Docker Compose 2.0+
- Git
- 4GB RAM minimum (8GB recommended)
- 2GB free disk space
## Database Migrations
## 🏗️ Project Structure
Run these migrations to add the new features:
```bash
# Add attachment tables
mysql -u your_user -p your_database < src/migrations/add_attachments_tables.sql
# Fix attachment data column if needed
mysql -u your_user -p your_database < src/migrations/alter_attachments_data_column.sql
# Add comparison offers tables
mysql -u your_user -p your_database < src/migrations/add_comparison_offers_tables.sql
```
stupa-pdf-api/
├── backend/ # FastAPI backend application
│ ├── src/ # Source code
│ │ ├── migrations/ # Database migrations
│ │ ├── service_api.py # Main API application
│ │ ├── pdf_filler.py # PDF generation
│ │ ├── pdf_to_struct.py # PDF parsing
│ │ └── ...
│ ├── assets/ # PDF templates
│ ├── requirements.txt # Python dependencies
│ └── Dockerfile # Backend container definition
├── frontend/ # React frontend application
│ ├── src/ # Source code
│ ├── public/ # Static assets
│ ├── package.json # Node dependencies
│ ├── nginx.conf # Nginx configuration
│ └── Dockerfile # Frontend container definition
├── docs/ # Documentation
│ ├── README.md # Documentation index
│ ├── ARCHITECTURE.md # System architecture
│ ├── API_REFERENCE.md # API documentation
│ └── ...
├── scripts/ # Helper scripts
│ ├── create-env.sh # Environment setup script
│ └── dev.sh # Development helper script
├── docker-compose.yml # Multi-container orchestration
└── README.md # This file
```
## ⚡ Quick Start
### 1. Clone the Repository
```bash
git clone https://github.com/your-org/stupa-pdf-api.git
cd stupa-pdf-api
```
### 2. Setup Environment
```bash
# Make scripts executable
chmod +x scripts/*.sh
# Create .env file with secure defaults
./scripts/create-env.sh
```
### 3. Start Services
```bash
docker compose up -d
```
### 4. Access the Application
- **Frontend**: http://localhost:3000
- **API Docs**: http://localhost:8000/docs
- **Database UI**: http://localhost:8080
## 🛠️ Development
### Using the Development Script
```bash
# Start all services
./scripts/dev.sh start
# View logs
./scripts/dev.sh logs api
# Open shell in container
./scripts/dev.sh shell frontend
# Run database migrations
./scripts/dev.sh migrate
# More commands
./scripts/dev.sh help
```
### Manual Development Setup
#### Backend Development
```bash
cd backend
python -m venv venv
source venv/bin/activate # Linux/macOS
pip install -r requirements.txt
uvicorn src.service_api:app --reload
```
#### Frontend Development
```bash
cd frontend
npm install
npm run dev
```
## 📚 Documentation
Comprehensive documentation is available in the `/docs` directory:
- [Documentation Index](./docs/README.md)
- [Architecture Overview](./docs/ARCHITECTURE.md)
- [Installation Guide](./docs/INSTALLATION.md)
- [Quick Start Guide](./docs/QUICK_START.md)
- [API Reference](./docs/API_REFERENCE.md)
- [Development Guide](./docs/DEVELOPMENT.md)
## 🔧 Configuration
Key environment variables:
```env
# Database
MYSQL_DB=stupa
MYSQL_USER=stupa
MYSQL_PASSWORD=<generated>
# Authentication
MASTER_KEY=<generated>
# Rate Limiting
RATE_IP_PER_MIN=60
RATE_KEY_PER_MIN=30
# Application
TZ=Europe/Berlin
```
See [Configuration Guide](./docs/CONFIGURATION.md) for all options.
## 🗄️ Database Migrations
Run migrations after setup:
```bash
./scripts/dev.sh migrate
```
Or manually:
```bash
docker compose exec -T db mysql -u root -p${MYSQL_ROOT_PASSWORD} ${MYSQL_DB} < backend/src/migrations/add_attachments_tables.sql
docker compose exec -T db mysql -u root -p${MYSQL_ROOT_PASSWORD} ${MYSQL_DB} < backend/src/migrations/add_comparison_offers_tables.sql
# ... other migrations
```
## 🧪 Testing
```bash
# Run all tests
./scripts/dev.sh test
# Run linters
./scripts/dev.sh lint
# Format code
./scripts/dev.sh format
```
## 🚢 Deployment
See [Deployment Guide](./docs/DEPLOYMENT.md) for production deployment instructions.
### Basic Production Setup
1. Use environment-specific `.env` file
2. Enable SSL/TLS termination
3. Configure proper domain names
4. Set up backup procedures
5. Monitor logs and metrics
## 🔒 Security
- Application-specific access keys
- Master key for admin access
- Rate limiting per IP and key
- Input validation and sanitization
- SQL injection prevention via ORM
- XSS protection in React
- Secure password hashing
See [Security Guide](./docs/SECURITY.md) for best practices.
## 📊 API Endpoints
Key endpoints:
- `POST /upload` - Upload PDF and create application
- `GET /applications/{id}` - Get application data
- `POST /applications/{id}/attachments` - Upload attachments
- `GET /applications/{id}/costs/{index}/offers` - Get comparison offers
- `PUT /applications/{id}` - Update application
Full API documentation available at http://localhost:8000/docs when running.
## 🤝 Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Run tests and linting
5. Submit a pull request
See [Contributing Guide](./docs/CONTRIBUTING.md) for details.
## 📝 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## 🆘 Support
- Check the [FAQ](./docs/FAQ.md)
- Review [Troubleshooting Guide](./docs/TROUBLESHOOTING.md)
- Create an issue for bugs or features
- Contact the development team
## 🏆 Acknowledgments
- Built with FastAPI, React, and MySQL
- PDF processing powered by PyPDF2 and ReportLab
- UI components from Material-UI
---
**Version**: 1.0.0
**Last Updated**: 2024

Binary file not shown.

Binary file not shown.

View File

@ -355,6 +355,11 @@ app = FastAPI(title="STUPA PDF API", version="1.0.0")
def _startup():
init_db()
@app.get("/")
def root():
"""Root endpoint for health checks"""
return {"message": "STUPA PDF API"}
# Helper function to check if we should skip rate limiting
def should_skip_rate_limit(ip: str = "") -> bool:
"""Check if rate limiting should be skipped for localhost/Docker IPs"""

66
dev.sh
View File

@ -1,66 +0,0 @@
#!/bin/bash
# Development script for running the STUPA PDF API with Docker Compose watch mode
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${GREEN}Starting STUPA PDF API in development mode with watch...${NC}"
# Check if Docker is running
if ! docker info > /dev/null 2>&1; then
echo -e "${RED}Docker is not running. Please start Docker first.${NC}"
exit 1
fi
# Check if docker compose supports watch
if ! docker compose version | grep -q "2\.[2-9][0-9]\|2\.[1-9][0-9][0-9]"; then
echo -e "${YELLOW}Warning: Docker Compose watch requires version 2.22.0 or higher${NC}"
echo -e "${YELLOW}Your version: $(docker compose version)${NC}"
fi
# Create .env file if it doesn't exist
if [ ! -f .env ]; then
echo -e "${YELLOW}Creating .env file with default values...${NC}"
cat > .env << EOF
# MySQL Configuration
MYSQL_DB=stupa
MYSQL_USER=stupa
MYSQL_PASSWORD=secret
MYSQL_ROOT_PASSWORD=rootsecret
# Application Configuration
MASTER_KEY=change_me_in_production
RATE_IP_PER_MIN=120
RATE_KEY_PER_MIN=60
# Timezone
TZ=Europe/Berlin
EOF
echo -e "${GREEN}.env file created. Please update the MASTER_KEY for production use.${NC}"
fi
# Build the development image
echo -e "${GREEN}Building development image...${NC}"
docker compose -f docker-compose.watch.yml build
# Start services with watch
echo -e "${GREEN}Starting services with file watching enabled...${NC}"
echo -e "${YELLOW}Changes to files in ./src will automatically reload the API${NC}"
echo -e "${YELLOW}Changes to requirements.txt will rebuild the container${NC}"
echo -e "${YELLOW}Changes to assets will be synced immediately${NC}"
echo ""
echo -e "${GREEN}Services:${NC}"
echo -e " - API: http://localhost:8000"
echo -e " - API Docs: http://localhost:8000/docs"
echo -e " - Adminer: http://localhost:8080"
echo ""
echo -e "${YELLOW}Press Ctrl+C to stop${NC}"
# Run docker compose with watch
exec docker compose -f docker-compose.watch.yml up --watch

View File

@ -1,5 +1,3 @@
version: "3.9"
services:
db:
image: mysql:8.0
@ -33,7 +31,7 @@ services:
api:
build:
context: .
context: ./backend
dockerfile: Dockerfile.dev
container_name: stupa_api
restart: unless-stopped
@ -67,16 +65,16 @@ services:
develop:
watch:
- action: sync
path: ./src
path: ./backend/src
target: /app/src
- action: rebuild
path: ./requirements.txt
path: ./backend/requirements.txt
- action: sync
path: ./assets
path: ./backend/assets
target: /app/assets
volumes:
- ./src:/app/src
- ./assets:/app/assets
- ./backend/src:/app/src
- ./backend/assets:/app/assets
adminer:
image: adminer:4

View File

@ -1,5 +1,3 @@
version: "3.9"
services:
db:
image: mysql:8.0
@ -33,7 +31,7 @@ services:
api:
build:
context: .
context: ./backend
dockerfile: Dockerfile
container_name: stupa_api
restart: unless-stopped
@ -65,6 +63,24 @@ services:
timeout: 5s
retries: 6
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: stupa_frontend
restart: unless-stopped
depends_on:
- api
ports:
- "3001:80"
environment:
- NODE_ENV=production
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost/ || exit 1"]
interval: 10s
timeout: 5s
retries: 6
adminer:
image: adminer:4
container_name: stupa_adminer

469
docs/API_REFERENCE.md Normal file
View File

@ -0,0 +1,469 @@
# API Reference
This document provides a comprehensive reference for all API endpoints in the STUPA PDF API system.
## Base URL
- **Development**: `http://localhost:8000`
- **Production**: `https://your-domain.com/api`
## Authentication
The API uses two types of authentication:
### Application Key Authentication
- **Header**: `X-PA-KEY`
- **Purpose**: Access specific application data
- **Scope**: Read/write access to single application
### Master Key Authentication
- **Header**: `X-MASTER-KEY`
- **Purpose**: Administrative access
- **Scope**: Full access to all resources
### Query Parameter Authentication
For endpoints that need to work with direct browser access (like PDF downloads), you can use:
- **Parameter**: `key`
- **Example**: `/applications/25-0001?format=pdf&key=your-app-key`
## Response Format
All API responses follow this general structure:
```json
{
"data": { ... }, // For successful responses
"detail": "...", // For error messages
"status": "success" // Optional status indicator
}
```
## Error Responses
```json
{
"detail": "Error message here"
}
```
Common HTTP status codes:
- `200 OK` - Success
- `201 Created` - Resource created
- `400 Bad Request` - Invalid request data
- `401 Unauthorized` - Missing or invalid authentication
- `403 Forbidden` - Insufficient permissions
- `404 Not Found` - Resource not found
- `429 Too Many Requests` - Rate limit exceeded
- `500 Internal Server Error` - Server error
## Endpoints
### Health Check
#### GET /
Check if the API is running.
**Response:**
```json
{
"message": "STUPA PDF API"
}
```
---
### PDF Upload and Processing
#### POST /upload
Upload a PDF form and create a new application.
**Request:**
- **Content-Type**: `multipart/form-data`
- **Body**:
- `file` (required): PDF file to upload
- `form_type` (required): Type of form (`"pa_form"` or `"vsm_form"`)
**Response:**
```json
{
"pa_id": "25-0001",
"pa_key": "generated-secure-key",
"data": {
"projectBeginn": "2025-01-01",
"name": "Student Project",
// ... other parsed data
}
}
```
**Example:**
```bash
curl -X POST "http://localhost:8000/upload" \
-F "file=@application.pdf" \
-F "form_type=pa_form"
```
---
### Application Management
#### GET /applications/{pa_id}
Retrieve application data.
**Parameters:**
- `pa_id` (path): Application ID (e.g., "25-0001")
- `format` (query): Response format (`"json"` or `"pdf"`)
- `key` (query): Application key (alternative to header)
**Headers:**
- `X-PA-KEY` or `X-MASTER-KEY`
**Response (JSON):**
```json
{
"pa_id": "25-0001",
"created_at": "2024-01-01T00:00:00",
"updated_at": "2024-01-01T00:00:00",
"data": {
"projectBeginn": "2025-01-01",
"name": "Student Project",
// ... full application data
}
}
```
**Response (PDF):**
Binary PDF file with filled form.
---
#### PUT /applications/{pa_id}
Update application data.
**Parameters:**
- `pa_id` (path): Application ID
**Headers:**
- `X-PA-KEY` or `X-MASTER-KEY`
**Request Body:**
```json
{
"projectBeginn": "2025-01-01",
"name": "Updated Project Name",
// ... fields to update
}
```
**Response:**
```json
{
"pa_id": "25-0001",
"data": {
// ... updated application data
}
}
```
---
#### DELETE /applications/{pa_id}
Delete an application (admin only).
**Parameters:**
- `pa_id` (path): Application ID
**Headers:**
- `X-MASTER-KEY` (required)
**Response:**
```json
{
"detail": "Application deleted successfully"
}
```
---
### Attachment Management
#### GET /applications/{pa_id}/attachments
List all attachments for an application.
**Parameters:**
- `pa_id` (path): Application ID
**Headers:**
- `X-PA-KEY` or `X-MASTER-KEY`
**Response:**
```json
{
"attachments": [
{
"id": 1,
"filename": "receipt.pdf",
"content_type": "application/pdf",
"size": 102400,
"created_at": "2024-01-01T00:00:00"
}
],
"total_count": 1,
"total_size": 102400
}
```
---
#### POST /applications/{pa_id}/attachments
Upload a new attachment.
**Parameters:**
- `pa_id` (path): Application ID
**Headers:**
- `X-PA-KEY` or `X-MASTER-KEY`
**Request:**
- **Content-Type**: `multipart/form-data`
- **Body**:
- `file` (required): File to upload
**Response:**
```json
{
"id": 1,
"filename": "receipt.pdf",
"content_type": "application/pdf",
"size": 102400,
"created_at": "2024-01-01T00:00:00"
}
```
**Limits:**
- Maximum 30 attachments per application
- Maximum 100MB total size per application
- Maximum 50MB per file
---
#### GET /applications/{pa_id}/attachments/{attachment_id}
Download a specific attachment.
**Parameters:**
- `pa_id` (path): Application ID
- `attachment_id` (path): Attachment ID
**Headers:**
- `X-PA-KEY` or `X-MASTER-KEY`
**Response:**
Binary file data
---
#### DELETE /applications/{pa_id}/attachments/{attachment_id}
Delete an attachment.
**Parameters:**
- `pa_id` (path): Application ID
- `attachment_id` (path): Attachment ID
**Headers:**
- `X-PA-KEY` or `X-MASTER-KEY`
**Response:**
```json
{
"detail": "Attachment deleted successfully"
}
```
---
### Comparison Offers
#### GET /applications/{pa_id}/costs/{cost_position_index}/offers
Get comparison offers for a cost position.
**Parameters:**
- `pa_id` (path): Application ID
- `cost_position_index` (path): Cost position index (0-based)
**Headers:**
- `X-PA-KEY` or `X-MASTER-KEY`
**Response:**
```json
{
"cost_position_index": 0,
"offers": [
{
"id": 1,
"supplier_name": "Supplier A",
"amount": 1000.00,
"description": "Standard package",
"url": "https://supplier-a.com/quote",
"attachment_id": null,
"is_preferred": false,
"created_at": "2024-01-01T00:00:00"
}
],
"no_offers_required": false,
"justification": null
}
```
---
#### POST /applications/{pa_id}/costs/{cost_position_index}/offers
Create a new comparison offer.
**Parameters:**
- `pa_id` (path): Application ID
- `cost_position_index` (path): Cost position index
**Headers:**
- `X-PA-KEY` or `X-MASTER-KEY`
**Request Body:**
```json
{
"supplier_name": "Supplier A",
"amount": 1000.00,
"description": "Standard package",
"url": "https://supplier-a.com/quote",
"attachment_id": null,
"is_preferred": false
}
```
**Response:**
```json
{
"id": 1,
"supplier_name": "Supplier A",
"amount": 1000.00,
"description": "Standard package",
"url": "https://supplier-a.com/quote",
"attachment_id": null,
"is_preferred": false,
"created_at": "2024-01-01T00:00:00"
}
```
---
#### PUT /applications/{pa_id}/costs/{cost_position_index}/offers/{offer_id}/preferred
Set an offer as preferred.
**Parameters:**
- `pa_id` (path): Application ID
- `cost_position_index` (path): Cost position index
- `offer_id` (path): Offer ID
**Headers:**
- `X-PA-KEY` or `X-MASTER-KEY`
**Response:**
```json
{
"detail": "Preferred offer updated successfully"
}
```
---
#### DELETE /applications/{pa_id}/costs/{cost_position_index}/offers/{offer_id}
Delete a comparison offer.
**Parameters:**
- `pa_id` (path): Application ID
- `cost_position_index` (path): Cost position index
- `offer_id` (path): Offer ID
**Headers:**
- `X-PA-KEY` or `X-MASTER-KEY`
**Response:**
```json
{
"detail": "Offer deleted successfully"
}
```
---
#### PUT /applications/{pa_id}/costs/{cost_position_index}/justification
Update justification for not requiring comparison offers.
**Parameters:**
- `pa_id` (path): Application ID
- `cost_position_index` (path): Cost position index
**Headers:**
- `X-PA-KEY` or `X-MASTER-KEY`
**Request Body:**
```json
{
"no_offers_required": true,
"justification": "This is a unique service provider with no alternatives..."
}
```
**Response:**
```json
{
"detail": "Justification updated successfully"
}
```
---
## Rate Limiting
The API implements rate limiting to prevent abuse:
- **Per IP**: 60 requests per minute (configurable)
- **Per Key**: 30 requests per minute (configurable)
- **Excluded**: localhost and Docker internal IPs
Rate limit headers in responses:
- `X-RateLimit-Limit`: Maximum requests allowed
- `X-RateLimit-Remaining`: Requests remaining
- `X-RateLimit-Reset`: Unix timestamp when limit resets
---
## WebSocket Endpoints
Currently, the API does not provide WebSocket endpoints. All communication is via REST HTTP.
---
## Pagination
For endpoints that return lists, pagination may be implemented in future versions using:
- `limit`: Number of items per page
- `offset`: Starting position
- `page`: Page number (alternative to offset)
---
## Versioning
The API currently does not use versioning. Future versions may implement versioning via:
- URL path: `/v1/applications`
- Header: `Accept: application/vnd.stupa.v1+json`
---
## OpenAPI Documentation
Complete OpenAPI 3.0 documentation is available at:
- **Swagger UI**: `/docs`
- **ReDoc**: `/redoc`
- **OpenAPI JSON**: `/openapi.json`
These provide interactive documentation with the ability to test endpoints directly.

181
docs/ARCHITECTURE.md Normal file
View File

@ -0,0 +1,181 @@
# Architecture Overview
## System Architecture
The STUPA PDF API is built using a microservices architecture with clear separation of concerns between the frontend, backend, and database layers.
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ │ │ │ │ │
│ Frontend │────▶│ API Gateway │────▶│ Backend │
│ (React/TS) │ │ (Nginx) │ │ (FastAPI) │
│ │ │ │ │ │
└─────────────────┘ └─────────────────┘ └────────┬────────┘
┌─────────────────┐ ┌─────────────────┐
│ │ │ │
│ Database │ │ File Storage │
│ (MySQL) │ │ (Base64 DB) │
│ │ │ │
└─────────────────┘ └─────────────────┘
```
## Components
### Frontend (React Application)
- **Technology**: React 18 with TypeScript
- **State Management**: With State
- **UI Framework**: Material-UI
- **Build Tool**: Vite
- **Features**:
- Single Page Application (SPA)
- Responsive design
- Real-time form validation
- File upload management
- PDF preview capabilities
### API Gateway (Nginx)
- **Purpose**: Reverse proxy and static file serving
- **Features**:
- Route `/api/*` requests to backend
- Serve static frontend assets
- Handle CORS
- SSL termination (production)
- Request buffering
- Gzip compression
### Backend (FastAPI)
- **Framework**: FastAPI (Python 3.11)
- **ORM**: SQLAlchemy
- **PDF Processing**: PyPDF2, ReportLab
- **Features**:
- RESTful API design
- Automatic API documentation
- Request validation
- Rate limiting
- Authentication middleware
- PDF parsing and generation
### Database (MySQL)
- **Version**: MySQL 8.0
- **Character Set**: utf8mb4
- **Features**:
- Relational data model
- Foreign key constraints
- Indexes for performance
- Transaction support
## Data Flow
### Application Creation Flow
```
1. User uploads PDF → Frontend
2. Frontend sends PDF to API → POST /upload
3. Backend parses PDF → Extracts structured data
4. Backend creates application → Stores in database
5. Backend returns application ID and key
6. Frontend redirects to application view
```
### PDF Generation Flow
```
1. User requests PDF → GET /applications/{id}?format=pdf
2. Backend loads application data
3. Backend fills PDF template
4. Backend returns filled PDF
5. Frontend displays/downloads PDF
```
## Security Architecture
### Authentication Layers
1. **Application Key** (`X-PA-KEY`)
- Generated per application
- Allows read/write access to specific application
- Stored as SHA-256 hash
2. **Master Key** (`X-MASTER-KEY`)
- Environment variable
- Full admin access
- Never exposed to frontend
### Security Features
- Rate limiting per IP and per key
- SQL injection prevention (ORM)
- XSS protection (React)
- CORS configuration
- Input validation
- Secure password hashing
## Database Schema
### Core Tables
- `applications` - Main application data
- `application_keys` - Authentication keys
- `attachments` - File storage (Base64)
- `application_attachments` - Link table
- `comparison_offers` - Cost comparison data
- `cost_position_justifications` - Justification text
### Key Relationships
- Applications ↔ Keys (1:N)
- Applications ↔ Attachments (N:N)
- Applications ↔ Comparison Offers (1:N)
## Scalability Considerations
### Horizontal Scaling
- Stateless API design
- Database connection pooling
- Load balancer ready
- Containerized deployment
### Performance Optimizations
- Database indexes on foreign keys
- Lazy loading of attachments
- Efficient PDF streaming
- Response caching headers
- Gzip compression
## Development vs Production
### Development Environment
- Hot reloading (frontend & backend)
- Debug logging
- Local file storage
- Relaxed CORS
- Default credentials
### Production Environment
- Optimized builds
- Error tracking
- Cloud storage ready
- Strict CORS
- Secret management
- SSL/TLS encryption
## Future Architecture Considerations
### Potential Enhancements
1. **Microservice Separation**
- PDF service
- Authentication service
- Notification service
2. **External Storage**
- S3-compatible object storage
- CDN for static assets
3. **Caching Layer**
- Redis for session management
- Application data caching
4. **Message Queue**
- Async PDF generation
- Email notifications
5. **Monitoring**
- Application metrics
- Performance monitoring
- Error tracking

422
docs/CONFIGURATION.md Normal file
View File

@ -0,0 +1,422 @@
# Configuration Guide
This guide covers all configuration options for the STUPA PDF API system.
## Environment Variables
The application is configured using environment variables. These can be set in a `.env` file in the project root or passed directly to Docker.
### Database Configuration
| Variable | Description | Default | Required |
|----------|-------------|---------|----------|
| `MYSQL_HOST` | MySQL server hostname | `db` | Yes |
| `MYSQL_PORT` | MySQL server port | `3306` | Yes |
| `MYSQL_DB` | Database name | `stupa` | Yes |
| `MYSQL_USER` | Database user | `stupa` | Yes |
| `MYSQL_PASSWORD` | Database password | None | Yes |
| `MYSQL_ROOT_PASSWORD` | MySQL root password | None | Yes |
**Example:**
```env
MYSQL_HOST=db
MYSQL_PORT=3306
MYSQL_DB=stupa
MYSQL_USER=stupa
MYSQL_PASSWORD=secure_password_here
MYSQL_ROOT_PASSWORD=secure_root_password_here
```
### Authentication
| Variable | Description | Default | Required |
|----------|-------------|---------|----------|
| `MASTER_KEY` | Master key for admin access | None | Yes |
**Security Notes:**
- Generate a strong, random master key
- Never commit the master key to version control
- Rotate the master key periodically
- Use different keys for each environment
**Example:**
```env
MASTER_KEY=your_very_secure_master_key_here
```
### Rate Limiting
| Variable | Description | Default | Required |
|----------|-------------|---------|----------|
| `RATE_IP_PER_MIN` | Requests per minute per IP | `60` | No |
| `RATE_KEY_PER_MIN` | Requests per minute per key | `30` | No |
**Notes:**
- Rate limiting is disabled for localhost and Docker internal IPs
- Adjust based on your expected traffic
- Set to `0` to disable rate limiting (not recommended for production)
**Example:**
```env
RATE_IP_PER_MIN=60
RATE_KEY_PER_MIN=30
```
### Application Settings
| Variable | Description | Default | Required |
|----------|-------------|---------|----------|
| `TZ` | Timezone for the application | `Europe/Berlin` | No |
| `QSM_TEMPLATE` | Path to QSM PDF template | `/app/assets/qsm.pdf` | No |
| `VSM_TEMPLATE` | Path to VSM PDF template | `/app/assets/vsm.pdf` | No |
**Example:**
```env
TZ=Europe/Berlin
QSM_TEMPLATE=/app/assets/qsm.pdf
VSM_TEMPLATE=/app/assets/vsm.pdf
```
### Frontend Configuration
| Variable | Description | Default | Required |
|----------|-------------|---------|----------|
| `NODE_ENV` | Node environment | `production` | No |
| `VITE_API_URL` | API URL for frontend | `/api` | No |
**Example:**
```env
NODE_ENV=production
VITE_API_URL=/api
```
## Advanced Configuration
### CORS Configuration
For production deployments, configure CORS appropriately:
```python
# In service_api.py (for reference)
CORS_ORIGINS = os.getenv("CORS_ORIGINS", "").split(",")
# Or hardcode in production
CORS_ORIGINS = ["https://your-domain.com"]
```
### Database Connection Pool
Configure connection pooling for better performance:
```env
# Optional database pool settings
DB_POOL_SIZE=10
DB_MAX_OVERFLOW=20
DB_POOL_TIMEOUT=30
DB_POOL_RECYCLE=3600
```
### File Upload Limits
Control file upload constraints:
```env
# Maximum upload size in bytes (100MB)
MAX_UPLOAD_SIZE=104857600
# Maximum attachments per application
MAX_ATTACHMENTS_PER_APP=30
# Maximum size per attachment (50MB)
MAX_ATTACHMENT_SIZE=52428800
```
### Logging Configuration
Configure logging levels and outputs:
```env
# Log level: DEBUG, INFO, WARNING, ERROR, CRITICAL
LOG_LEVEL=INFO
# Log format: json, text
LOG_FORMAT=json
# Log file path (optional)
LOG_FILE=/var/log/stupa-api.log
# Enable access logs
ENABLE_ACCESS_LOGS=true
```
### Session Configuration
Control session behavior:
```env
# Session timeout in seconds (1 hour)
SESSION_TIMEOUT=3600
# Session cookie settings
SESSION_COOKIE_SECURE=true
SESSION_COOKIE_HTTPONLY=true
SESSION_COOKIE_SAMESITE=strict
```
## Docker Compose Configuration
### Port Mapping
Default port configuration in `docker-compose.yml`:
```yaml
services:
frontend:
ports:
- "3000:80" # Frontend on port 3000
api:
ports:
- "8000:8000" # API on port 8000
db:
ports:
- "3306:3306" # MySQL on port 3306
adminer:
ports:
- "8080:8080" # Adminer on port 8080
```
To change ports, modify the left side of the mapping:
```yaml
ports:
- "8080:80" # Change frontend to port 8080
```
### Volume Configuration
Persistent data storage:
```yaml
volumes:
db_data: # MySQL data
```
For development, you can add more volumes:
```yaml
volumes:
- ./backend/src:/app/src # Hot reload for backend
- ./frontend/src:/usr/share/nginx/html # Hot reload for frontend
```
### Network Configuration
Services communicate on an internal Docker network. To customize:
```yaml
networks:
stupa_network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
```
## Production Configuration
### SSL/TLS Configuration
For HTTPS in production, use a reverse proxy:
```nginx
server {
listen 443 ssl http2;
server_name your-domain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:3000;
}
location /api/ {
proxy_pass http://localhost:8000/;
}
}
```
### Environment-Specific Files
Create environment-specific configuration:
```bash
# Development
.env.development
# Staging
.env.staging
# Production
.env.production
```
Load the appropriate file:
```bash
docker compose --env-file .env.production up -d
```
### Security Headers
Add security headers in production:
```env
# Enable security headers
SECURE_HEADERS=true
# HSTS settings
HSTS_ENABLED=true
HSTS_MAX_AGE=31536000
HSTS_INCLUDE_SUBDOMAINS=true
# CSP settings
CSP_ENABLED=true
CSP_POLICY="default-src 'self'"
```
### Backup Configuration
Configure automated backups:
```env
# Enable backups
BACKUP_ENABLED=true
# Backup schedule (cron format)
BACKUP_SCHEDULE="0 2 * * *"
# Backup retention days
BACKUP_RETENTION_DAYS=30
# Backup destination
BACKUP_PATH=/backups
# S3 backup (optional)
BACKUP_S3_BUCKET=stupa-backups
BACKUP_S3_REGION=eu-central-1
```
## Performance Tuning
### MySQL Configuration
Optimize MySQL for your workload:
```cnf
# my.cnf
[mysqld]
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
innodb_flush_method = O_DIRECT
max_connections = 200
```
### API Worker Configuration
Configure Uvicorn workers:
```env
# Number of worker processes
WEB_CONCURRENCY=4
# Worker class
WORKER_CLASS=uvicorn.workers.UvicornWorker
# Worker timeout
WORKER_TIMEOUT=300
```
### Frontend Build Optimization
For production builds:
```env
# Enable production optimizations
VITE_BUILD_COMPRESS=true
VITE_BUILD_SOURCEMAP=false
VITE_BUILD_MINIFY=true
```
## Monitoring Configuration
### Health Check Configuration
Configure health check parameters:
```env
# Health check interval in seconds
HEALTH_CHECK_INTERVAL=30
# Health check timeout
HEALTH_CHECK_TIMEOUT=5
# Health check retries
HEALTH_CHECK_RETRIES=3
```
### Metrics Configuration
Enable metrics collection:
```env
# Enable Prometheus metrics
ENABLE_METRICS=true
METRICS_PORT=9090
# Enable application metrics
COLLECT_APP_METRICS=true
```
## Troubleshooting Configuration Issues
### Validation
Validate your configuration:
```bash
# Check Docker Compose configuration
docker compose config
# Test environment variables
docker compose run --rm api env
# Verify database connection
docker compose exec api python -c "from service_api import get_db; db = next(get_db()); print('DB OK')"
```
### Common Issues
1. **Database Connection Failed**
- Check `MYSQL_HOST` is correct
- Ensure database service is healthy
- Verify credentials match
2. **Rate Limiting Too Restrictive**
- Increase `RATE_IP_PER_MIN`
- Check if IP is excluded
3. **File Upload Fails**
- Check `MAX_UPLOAD_SIZE`
- Verify disk space
- Check nginx client_max_body_size
4. **CORS Errors**
- Configure `CORS_ORIGINS`
- Check frontend `VITE_API_URL`
## Configuration Best Practices
1. **Use Strong Passwords**
- Minimum 20 characters
- Use password generator
- Different for each environment
2. **Separate Environments**
- Never use production credentials in development
- Use different master keys
- Isolate databases
3. **Version Control**
- Commit `.env.example`
- Never commit `.env`
- Document all variables
4. **Regular Updates**
- Review configuration quarterly
- Update dependencies
- Rotate credentials
5. **Monitoring**
- Log configuration changes
- Alert on failures
- Track performance metrics

223
docs/INSTALLATION.md Normal file
View File

@ -0,0 +1,223 @@
# Installation Guide
This guide will walk you through installing and setting up the STUPA PDF API system on your local machine or server.
## Prerequisites
### System Requirements
- **Operating System**: Linux, macOS, or Windows (with WSL2)
- **Memory**: Minimum 4GB RAM (8GB recommended)
- **Storage**: At least 2GB free disk space
- **Network**: Internet connection for downloading dependencies
### Required Software
- **Docker**: Version 20.10 or higher
- **Docker Compose**: Version 2.0 or higher
- **Git**: For cloning the repository
- **Text Editor**: For editing configuration files
### Optional Software
- **Make**: For using the Makefile commands
- **Node.js**: Only if developing the frontend locally
- **Python 3.11**: Only if developing the backend locally
## Installation Steps
### 1. Clone the Repository
```bash
git clone https://github.com/your-org/stupa-pdf-api.git
cd stupa-pdf-api
```
### 2. Create Environment Configuration
Use the provided script to create your `.env` file:
```bash
./scripts/create-env.sh
```
This script will:
- Ask for configuration values (with sensible defaults)
- Generate secure passwords automatically
- Create a `.env` file with all required variables
Alternatively, create `.env` manually:
```bash
cp .env.example .env
# Edit .env with your preferred editor
```
### 3. Build and Start Services
```bash
# Build all services
docker compose build
# Start all services in detached mode
docker compose up -d
```
This will start:
- MySQL database (port 3306)
- Backend API (port 8000)
- Frontend application (port 3000)
- Adminer database UI (port 8080)
### 4. Initialize Database
The database will be automatically initialized when the MySQL container starts. To run migrations manually:
```bash
# Execute migrations
docker compose exec -T db mysql -u root -p${MYSQL_ROOT_PASSWORD} ${MYSQL_DB} < backend/src/migrations/add_comparison_offers_tables.sql
docker compose exec -T db mysql -u root -p${MYSQL_ROOT_PASSWORD} ${MYSQL_DB} < backend/src/migrations/add_attachments_tables.sql
docker compose exec -T db mysql -u root -p${MYSQL_ROOT_PASSWORD} ${MYSQL_DB} < backend/src/migrations/add_url_to_comparison_offers.sql
docker compose exec -T db mysql -u root -p${MYSQL_ROOT_PASSWORD} ${MYSQL_DB} < backend/src/migrations/add_is_preferred_to_comparison_offers.sql
```
### 5. Verify Installation
Check that all services are running:
```bash
docker compose ps
```
All services should show as "Up" and "Healthy".
Test the installation:
```bash
# Test API endpoint
curl http://localhost:8000/
# Test frontend
curl http://localhost:3000/
# You should see responses from both services
```
## Post-Installation
### Access Points
After successful installation, you can access:
- **Frontend Application**: http://localhost:3000
- **API Documentation**: http://localhost:8000/docs
- **Alternative API Docs**: http://localhost:8000/redoc
- **Database UI (Adminer)**: http://localhost:8080
- Server: `db`
- Username: `root` or your configured `MYSQL_USER`
- Password: Your configured password
- Database: Your configured `MYSQL_DB`
### Initial Setup
1. **Test PDF Upload**:
- Navigate to http://localhost:3000
- Upload a test PDF form
- Verify it's processed correctly
2. **Create Test Application**:
- Use the frontend to create a new application
- Note the generated application ID and key
3. **Verify Database**:
- Access Adminer at http://localhost:8080
- Check that tables are created
- Verify test data is stored
## Troubleshooting Installation
### Common Issues
#### Docker Compose Not Found
```bash
# Install Docker Compose V2
docker compose version
# If not found, update Docker Desktop or install manually
```
#### Port Already in Use
```bash
# Find process using port (example for port 3000)
lsof -i :3000 # macOS/Linux
netstat -ano | findstr :3000 # Windows
# Change port in docker-compose.yml or stop conflicting service
```
#### Permission Denied
```bash
# Add user to docker group (Linux)
sudo usermod -aG docker $USER
# Log out and back in for changes to take effect
```
#### Database Connection Failed
```bash
# Check MySQL container logs
docker compose logs db
# Verify environment variables
docker compose config
# Ensure database is healthy
docker compose ps db
```
### Resetting Installation
To completely reset the installation:
```bash
# Stop all services
docker compose down
# Remove volumes (WARNING: Deletes all data)
docker compose down -v
# Remove all images
docker compose down --rmi all
# Start fresh
docker compose up -d
```
## Development Installation
For local development without Docker:
### Backend Development
```bash
cd backend
python -m venv venv
source venv/bin/activate # Linux/macOS
# or
venv\Scripts\activate # Windows
pip install -r requirements.txt
# Set environment variables
export MYSQL_HOST=localhost
# ... other variables
uvicorn src.service_api:app --reload
```
### Frontend Development
```bash
cd frontend
npm install
npm run dev
```
See [Development Guide](./DEVELOPMENT.md) for more details.
## Next Steps
- Review the [Configuration Guide](./CONFIGURATION.md) for advanced settings
- Check the [Quick Start Guide](./QUICK_START.md) to begin using the system
- Read the [API Reference](./API_REFERENCE.md) for integration options

204
docs/QUICK_START.md Normal file
View File

@ -0,0 +1,204 @@
# Quick Start Guide
Get up and running with STUPA PDF API in 5 minutes!
## Prerequisites
- Docker and Docker Compose installed
- Git installed
- 5 minutes of your time
## 🚀 Quick Setup
### 1. Clone and Navigate
```bash
git clone https://github.com/your-org/stupa-pdf-api.git
cd stupa-pdf-api
```
### 2. Generate Configuration
```bash
# Make the script executable
chmod +x scripts/create-env.sh
# Run the configuration script
./scripts/create-env.sh
```
Just press Enter to accept all defaults for a quick local setup.
### 3. Start Everything
```bash
docker compose up -d
```
Wait about 30 seconds for all services to start.
### 4. Verify It's Working
```bash
# Check all services are running
docker compose ps
# Test the API
curl http://localhost:8000/
# Should return: {"message":"STUPA PDF API"}
```
## 🎯 Your First Application
### Using the Web Interface
1. **Open the Frontend**
- Navigate to http://localhost:3000
- You'll see the STUPA PDF application interface
2. **Upload a PDF Form**
- Click "Upload PDF" or drag and drop a PDF form
- The system will parse and extract data automatically
3. **Review Application Data**
- Check the extracted information
- Edit any fields if needed
- Add comparison offers for cost positions
4. **Save Your Work**
- Note the Application ID (e.g., "25-0001")
- Save the Application Key securely (shown once!)
### Using the API Directly
```bash
# Upload a PDF and create application
curl -X POST "http://localhost:8000/upload" \
-F "file=@your-form.pdf" \
-F "form_type=pa_form"
# Response includes:
# - pa_id: Your application ID
# - pa_key: Your access key (save this!)
```
## 📝 Common Tasks
### View Your Application
```bash
# Using the web interface
http://localhost:3000/applications/YOUR_PA_ID?key=YOUR_PA_KEY
# Using the API
curl "http://localhost:8000/applications/YOUR_PA_ID?format=json" \
-H "X-PA-KEY: YOUR_PA_KEY"
```
### Generate Filled PDF
```bash
# Download filled PDF
curl "http://localhost:8000/applications/YOUR_PA_ID?format=pdf" \
-H "X-PA-KEY: YOUR_PA_KEY" \
-o filled-form.pdf
```
### Add Attachments
```bash
# Upload an attachment
curl -X POST "http://localhost:8000/applications/YOUR_PA_ID/attachments" \
-H "X-PA-KEY: YOUR_PA_KEY" \
-F "file=@receipt.pdf"
```
### Manage Comparison Offers
Use the web interface to:
1. Click on any cost position
2. Add at least 3 comparison offers
3. Select your preferred offer
4. Save changes
## 🛠️ Quick Admin Tasks
### Access the Database
1. Open http://localhost:8080 (Adminer)
2. Login with:
- System: MySQL
- Server: `db`
- Username: `root`
- Password: (from your .env file)
- Database: `stupa`
### View API Documentation
- **Interactive Docs**: http://localhost:8000/docs
- **Alternative Docs**: http://localhost:8000/redoc
### Check Logs
```bash
# All services
docker compose logs -f
# Specific service
docker compose logs -f api
docker compose logs -f frontend
docker compose logs -f db
```
## 🔧 Quick Troubleshooting
### Services Won't Start
```bash
# Check for port conflicts
docker compose ps
# Restart everything
docker compose restart
# Full reset
docker compose down
docker compose up -d
```
### Can't Access Frontend
1. Check if port 3000 is already in use
2. Wait 30 seconds after starting services
3. Try http://127.0.0.1:3000 instead
### API Returns 429 (Too Many Requests)
The API has rate limiting. Default: 60 requests/minute per IP.
### Lost Application Key
Application keys cannot be recovered. You'll need to:
1. Use the master key (from .env) for admin access
2. Or create a new application
## 📚 Next Steps
Now that you're up and running:
1. **Explore the Frontend**: Try all features in the web interface
2. **Read API Docs**: Check http://localhost:8000/docs
3. **Customize Settings**: Edit `.env` for your needs
4. **Add Test Data**: Create sample applications
5. **Plan Deployment**: See [Deployment Guide](./DEPLOYMENT.md)
## 🆘 Getting Help
- Check [FAQ](./FAQ.md) for common questions
- See [Troubleshooting Guide](./TROUBLESHOOTING.md) for issues
- Review [API Reference](./API_REFERENCE.md) for integration
---
**Congratulations!** 🎉 You now have a working STUPA PDF API system!

98
docs/README.md Normal file
View File

@ -0,0 +1,98 @@
# STUPA PDF API Documentation
Welcome to the STUPA PDF API documentation. This directory contains comprehensive documentation for the STUPA PDF API system, which handles PDF processing, form filling, and application management for student parliament funding applications.
## Documentation Structure
### 📚 Main Documentation
- **[Architecture Overview](./ARCHITECTURE.md)** - System architecture, components, and design decisions
- **[API Reference](./API_REFERENCE.md)** - Complete API endpoint documentation
- **[Frontend Guide](./FRONTEND_GUIDE.md)** - Frontend development and usage guide
- **[Database Schema](./DATABASE_SCHEMA.md)** - Database structure and relationships
### 🚀 Getting Started
- **[Installation Guide](./INSTALLATION.md)** - Step-by-step installation instructions
- **[Configuration Guide](./CONFIGURATION.md)** - Environment variables and configuration options
- **[Quick Start](./QUICK_START.md)** - Get up and running quickly
### 💻 Development
- **[Development Guide](./DEVELOPMENT.md)** - Development workflow and best practices
- **[Contributing Guide](./CONTRIBUTING.md)** - How to contribute to the project
- **[Testing Guide](./TESTING.md)** - Testing strategies and procedures
### 🔧 Features
- **[Attachment Feature](./ATTACHMENT_FEATURE.md)** - File attachment functionality
- **[Comparison Offers Feature](./COMPARISON_OFFERS_FEATURE.md)** - Managing comparison offers
- **[PDF Processing](./PDF_PROCESSING.md)** - PDF generation and parsing
### 🚢 Deployment
- **[Deployment Guide](./DEPLOYMENT.md)** - Production deployment instructions
- **[Docker Guide](./DOCKER.md)** - Docker configuration and usage
- **[Security Guide](./SECURITY.md)** - Security best practices and considerations
### 🔍 Troubleshooting
- **[FAQ](./FAQ.md)** - Frequently asked questions
- **[Troubleshooting Guide](./TROUBLESHOOTING.md)** - Common issues and solutions
- **[Migration Guide](./MIGRATION.md)** - Database migration procedures
## Quick Links
- **API Base URL**: `http://localhost:8000` (development)
- **Frontend URL**: `http://localhost:3000` (development)
- **Database UI (Adminer)**: `http://localhost:8080` (development)
## Project Overview
The STUPA PDF API is a comprehensive system for managing student parliament funding applications. It provides:
- **PDF Processing**: Parse uploaded PDFs and extract structured data
- **Form Generation**: Generate filled PDF forms from application data
- **Application Management**: Create, read, update, and delete applications
- **File Attachments**: Upload and manage supporting documents
- **Comparison Offers**: Manage comparison offers for cost positions
- **Authentication**: Secure access with application keys and master keys
- **Rate Limiting**: Protect against abuse with configurable rate limits
## Technology Stack
### Backend
- **FastAPI** - Modern Python web framework
- **SQLAlchemy** - SQL toolkit and ORM
- **MySQL** - Relational database
- **PyPDF2** - PDF processing
- **Python 3.11** - Programming language
### Frontend
- **React 18** - UI library
- **TypeScript** - Type-safe JavaScript
- **Material-UI** - Component library
- **Zustand** - State management
- **Vite** - Build tool
### Infrastructure
- **Docker** - Containerization
- **Docker Compose** - Multi-container orchestration
- **Nginx** - Web server and reverse proxy
## Getting Help
1. Check the relevant documentation section
2. Search the [FAQ](./FAQ.md)
3. Look through the [Troubleshooting Guide](./TROUBLESHOOTING.md)
4. Create an issue in the project repository
## Documentation Updates
This documentation is actively maintained. If you find any errors or areas that need improvement, please:
1. Check the [Contributing Guide](./CONTRIBUTING.md)
2. Submit a pull request with your improvements
3. Or create an issue describing what needs to be updated
Last updated: 2024

128
scripts/create-env.sh Executable file
View File

@ -0,0 +1,128 @@
#!/bin/bash
# STUPA PDF API Environment Setup Script
# This script helps create a .env file with all required configurations
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Default values
DEFAULT_MYSQL_DB="stupa"
DEFAULT_MYSQL_USER="stupa"
DEFAULT_MYSQL_PORT="3306"
DEFAULT_RATE_IP_PER_MIN="60"
DEFAULT_RATE_KEY_PER_MIN="30"
echo -e "${GREEN}STUPA PDF API - Environment Configuration${NC}"
echo "=========================================="
echo ""
# Check if .env already exists
if [ -f .env ]; then
echo -e "${YELLOW}Warning: .env file already exists!${NC}"
read -p "Do you want to overwrite it? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted."
exit 1
fi
echo ""
fi
# Function to generate random password
generate_password() {
openssl rand -base64 32 | tr -d "=+/" | cut -c1-25
}
# Database Configuration
echo -e "${GREEN}Database Configuration${NC}"
echo "----------------------"
read -p "MySQL Database name [${DEFAULT_MYSQL_DB}]: " MYSQL_DB
MYSQL_DB=${MYSQL_DB:-$DEFAULT_MYSQL_DB}
read -p "MySQL Username [${DEFAULT_MYSQL_USER}]: " MYSQL_USER
MYSQL_USER=${MYSQL_USER:-$DEFAULT_MYSQL_USER}
read -p "MySQL Port [${DEFAULT_MYSQL_PORT}]: " MYSQL_PORT
MYSQL_PORT=${MYSQL_PORT:-$DEFAULT_MYSQL_PORT}
# Generate secure passwords
echo ""
echo -e "${YELLOW}Generating secure passwords...${NC}"
MYSQL_PASSWORD=$(generate_password)
MYSQL_ROOT_PASSWORD=$(generate_password)
MASTER_KEY=$(generate_password)
echo "MySQL Password: ${MYSQL_PASSWORD}"
echo "MySQL Root Password: ${MYSQL_ROOT_PASSWORD}"
echo "Master Key: ${MASTER_KEY}"
echo ""
# Rate Limiting Configuration
echo -e "${GREEN}Rate Limiting Configuration${NC}"
echo "---------------------------"
read -p "Rate limit per IP per minute [${DEFAULT_RATE_IP_PER_MIN}]: " RATE_IP_PER_MIN
RATE_IP_PER_MIN=${RATE_IP_PER_MIN:-$DEFAULT_RATE_IP_PER_MIN}
read -p "Rate limit per key per minute [${DEFAULT_RATE_KEY_PER_MIN}]: " RATE_KEY_PER_MIN
RATE_KEY_PER_MIN=${RATE_KEY_PER_MIN:-$DEFAULT_RATE_KEY_PER_MIN}
# Application Configuration
echo ""
echo -e "${GREEN}Application Configuration${NC}"
echo "------------------------"
read -p "Timezone [Europe/Berlin]: " TZ
TZ=${TZ:-"Europe/Berlin"}
# Create .env file
echo ""
echo -e "${GREEN}Creating .env file...${NC}"
cat > .env << EOF
# STUPA PDF API Configuration
# Generated on $(date)
# Database Configuration
MYSQL_HOST=db
MYSQL_PORT=${MYSQL_PORT}
MYSQL_DB=${MYSQL_DB}
MYSQL_USER=${MYSQL_USER}
MYSQL_PASSWORD=${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
# Authentication
MASTER_KEY=${MASTER_KEY}
# Rate Limiting
RATE_IP_PER_MIN=${RATE_IP_PER_MIN}
RATE_KEY_PER_MIN=${RATE_KEY_PER_MIN}
# Application Settings
TZ=${TZ}
# PDF Templates (paths inside container)
QSM_TEMPLATE=/app/assets/qsm.pdf
VSM_TEMPLATE=/app/assets/vsm.pdf
# Frontend Configuration
NODE_ENV=production
EOF
echo -e "${GREEN}✓ .env file created successfully!${NC}"
echo ""
echo -e "${YELLOW}Important Security Notes:${NC}"
echo "1. Keep the .env file secure and never commit it to version control"
echo "2. Save the passwords in a secure password manager"
echo "3. The Master Key is used for admin access - keep it safe!"
echo ""
echo -e "${GREEN}Next Steps:${NC}"
echo "1. Review the .env file and adjust values if needed"
echo "2. Run 'docker compose up -d' to start the application"
echo "3. Access the application at http://localhost:3000"
echo "4. Access the API directly at http://localhost:8000"
echo "5. Access Adminer (database UI) at http://localhost:8080"

321
scripts/dev.sh Executable file
View File

@ -0,0 +1,321 @@
#!/bin/bash
# STUPA PDF API Development Helper Script
# This script provides convenient commands for development tasks
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Project directories
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BACKEND_DIR="${PROJECT_ROOT}/backend"
FRONTEND_DIR="${PROJECT_ROOT}/frontend"
# Function to display usage
usage() {
echo -e "${GREEN}STUPA PDF API Development Helper${NC}"
echo "================================="
echo ""
echo "Usage: $0 [command] [options]"
echo ""
echo "Commands:"
echo " start Start all services"
echo " stop Stop all services"
echo " restart Restart all services"
echo " status Show status of all services"
echo " logs [service] Show logs (optionally for specific service)"
echo " build [service] Build services (optionally specific service)"
echo " shell [service] Open shell in service container"
echo " db Open MySQL client"
echo " migrate Run database migrations"
echo " reset Reset everything (WARNING: deletes data)"
echo " test Run tests"
echo " lint Run linters"
echo " format Format code"
echo " clean Clean up temporary files and caches"
echo ""
echo "Services: api, frontend, db, adminer"
echo ""
echo "Examples:"
echo " $0 start # Start all services"
echo " $0 logs api # Show API logs"
echo " $0 shell frontend # Open shell in frontend container"
echo " $0 db # Connect to database"
}
# Check if docker compose is available
check_docker() {
if ! command -v docker &> /dev/null; then
echo -e "${RED}Docker is not installed${NC}"
exit 1
fi
}
# Start services
start_services() {
echo -e "${GREEN}Starting all services...${NC}"
cd "${PROJECT_ROOT}"
docker compose up -d
echo -e "${GREEN}Services started!${NC}"
echo ""
echo "Access points:"
echo " Frontend: http://localhost:3000"
echo " API: http://localhost:8000"
echo " API Docs: http://localhost:8000/docs"
echo " Adminer: http://localhost:8080"
}
# Stop services
stop_services() {
echo -e "${YELLOW}Stopping all services...${NC}"
cd "${PROJECT_ROOT}"
docker compose down
echo -e "${GREEN}Services stopped!${NC}"
}
# Restart services
restart_services() {
echo -e "${YELLOW}Restarting all services...${NC}"
cd "${PROJECT_ROOT}"
docker compose restart
echo -e "${GREEN}Services restarted!${NC}"
}
# Show status
show_status() {
echo -e "${BLUE}Service Status:${NC}"
cd "${PROJECT_ROOT}"
docker compose ps
}
# Show logs
show_logs() {
cd "${PROJECT_ROOT}"
if [ -z "$1" ]; then
docker compose logs -f
else
docker compose logs -f "$1"
fi
}
# Build services
build_services() {
cd "${PROJECT_ROOT}"
if [ -z "$1" ]; then
echo -e "${GREEN}Building all services...${NC}"
docker compose build
else
echo -e "${GREEN}Building $1...${NC}"
docker compose build "$1"
fi
echo -e "${GREEN}Build complete!${NC}"
}
# Open shell in container
open_shell() {
if [ -z "$1" ]; then
echo -e "${RED}Please specify a service${NC}"
echo "Available services: api, frontend, db, adminer"
exit 1
fi
cd "${PROJECT_ROOT}"
case "$1" in
api)
docker compose exec api /bin/bash
;;
frontend)
docker compose exec frontend /bin/sh
;;
db)
docker compose exec db /bin/bash
;;
adminer)
docker compose exec adminer /bin/sh
;;
*)
echo -e "${RED}Unknown service: $1${NC}"
exit 1
;;
esac
}
# Connect to database
connect_db() {
echo -e "${BLUE}Connecting to database...${NC}"
cd "${PROJECT_ROOT}"
# Load .env file
if [ -f .env ]; then
export $(cat .env | grep -v '^#' | xargs)
fi
docker compose exec db mysql -u root -p${MYSQL_ROOT_PASSWORD} ${MYSQL_DB}
}
# Run migrations
run_migrations() {
echo -e "${GREEN}Running database migrations...${NC}"
cd "${PROJECT_ROOT}"
# Load .env file
if [ -f .env ]; then
export $(cat .env | grep -v '^#' | xargs)
fi
# Find all migration files
for migration in ${BACKEND_DIR}/src/migrations/*.sql; do
if [ -f "$migration" ]; then
echo -e "${BLUE}Applying $(basename "$migration")...${NC}"
docker compose exec -T db mysql -u root -p${MYSQL_ROOT_PASSWORD} ${MYSQL_DB} < "$migration" 2>/dev/null || true
fi
done
echo -e "${GREEN}Migrations complete!${NC}"
}
# Reset everything
reset_all() {
echo -e "${RED}WARNING: This will delete all data!${NC}"
read -p "Are you sure? (yes/NO): " -r
echo
if [[ $REPLY == "yes" ]]; then
cd "${PROJECT_ROOT}"
echo -e "${YELLOW}Stopping services...${NC}"
docker compose down -v
echo -e "${YELLOW}Removing images...${NC}"
docker compose down --rmi local
echo -e "${GREEN}Reset complete!${NC}"
else
echo "Aborted."
fi
}
# Run tests
run_tests() {
echo -e "${GREEN}Running tests...${NC}"
# Backend tests
if [ -d "${BACKEND_DIR}/tests" ]; then
echo -e "${BLUE}Running backend tests...${NC}"
cd "${PROJECT_ROOT}"
docker compose exec api pytest
fi
# Frontend tests
if [ -f "${FRONTEND_DIR}/package.json" ]; then
echo -e "${BLUE}Running frontend tests...${NC}"
cd "${PROJECT_ROOT}"
docker compose exec frontend npm test
fi
echo -e "${GREEN}Tests complete!${NC}"
}
# Run linters
run_lint() {
echo -e "${GREEN}Running linters...${NC}"
# Backend linting
echo -e "${BLUE}Linting backend...${NC}"
cd "${PROJECT_ROOT}"
docker compose exec api flake8 src/ || true
docker compose exec api mypy src/ || true
# Frontend linting
echo -e "${BLUE}Linting frontend...${NC}"
docker compose exec frontend npm run lint || true
echo -e "${GREEN}Linting complete!${NC}"
}
# Format code
format_code() {
echo -e "${GREEN}Formatting code...${NC}"
# Backend formatting
echo -e "${BLUE}Formatting backend...${NC}"
cd "${PROJECT_ROOT}"
docker compose exec api black src/
docker compose exec api isort src/
# Frontend formatting
echo -e "${BLUE}Formatting frontend...${NC}"
docker compose exec frontend npm run format || true
echo -e "${GREEN}Formatting complete!${NC}"
}
# Clean up
clean_up() {
echo -e "${YELLOW}Cleaning up...${NC}"
# Python cache
find "${BACKEND_DIR}" -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
find "${BACKEND_DIR}" -type f -name "*.pyc" -delete 2>/dev/null || true
# Node modules (if you want to clean them)
# rm -rf "${FRONTEND_DIR}/node_modules"
# Docker cleanup
docker system prune -f
echo -e "${GREEN}Cleanup complete!${NC}"
}
# Main script logic
check_docker
case "$1" in
start)
start_services
;;
stop)
stop_services
;;
restart)
restart_services
;;
status)
show_status
;;
logs)
show_logs "$2"
;;
build)
build_services "$2"
;;
shell)
open_shell "$2"
;;
db)
connect_db
;;
migrate)
run_migrations
;;
reset)
reset_all
;;
test)
run_tests
;;
lint)
run_lint
;;
format)
format_code
;;
clean)
clean_up
;;
*)
usage
;;
esac

View File

@ -1,32 +0,0 @@
-- Migration: Add attachment tables
-- Description: Add tables for storing file attachments for applications
-- Date: 2024
-- Create attachments table to store file data
CREATE TABLE IF NOT EXISTS `attachments` (
`id` INT NOT NULL AUTO_INCREMENT,
`filename` VARCHAR(255) NOT NULL,
`content_type` VARCHAR(100) NOT NULL,
`size` INT NOT NULL,
`data` LONGTEXT NOT NULL, -- Base64 encoded file data
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
INDEX `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Create junction table to link applications and attachments
CREATE TABLE IF NOT EXISTS `application_attachments` (
`id` INT NOT NULL AUTO_INCREMENT,
`application_id` INT NOT NULL,
`attachment_id` INT NOT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_app_attachment` (`application_id`, `attachment_id`),
INDEX `idx_application_id` (`application_id`),
INDEX `idx_attachment_id` (`attachment_id`),
INDEX `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Note: Foreign key constraints are not added because the Application table
-- doesn't have a direct foreign key relationship setup in the current schema.
-- The application_id references the id column in the applications table.

View File

@ -1,41 +0,0 @@
-- Migration: Add comparison offers tables
-- Description: Add tables for storing comparison offers for cost positions
-- Date: 2024
-- Create comparison offers table
CREATE TABLE IF NOT EXISTS `comparison_offers` (
`id` INT NOT NULL AUTO_INCREMENT,
`application_id` INT NOT NULL,
`cost_position_index` INT NOT NULL,
`supplier_name` VARCHAR(255) NOT NULL,
`amount` INT NOT NULL COMMENT 'Amount in cents',
`description` TEXT,
`attachment_id` INT,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_offer_supplier` (`application_id`, `cost_position_index`, `supplier_name`),
INDEX `idx_application_id` (`application_id`),
INDEX `idx_cost_position` (`application_id`, `cost_position_index`),
INDEX `idx_attachment_id` (`attachment_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Create cost position justifications table
CREATE TABLE IF NOT EXISTS `cost_position_justifications` (
`id` INT NOT NULL AUTO_INCREMENT,
`application_id` INT NOT NULL,
`cost_position_index` INT NOT NULL,
`no_offers_required` INT NOT NULL DEFAULT 0 COMMENT 'Boolean: 1 if no offers are required',
`justification` TEXT COMMENT 'Required when no_offers_required is 1',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_position_justification` (`application_id`, `cost_position_index`),
INDEX `idx_application_id` (`application_id`),
INDEX `idx_no_offers` (`no_offers_required`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Note: Foreign key constraints are not added because the Application table
-- doesn't have a direct foreign key relationship setup in the current schema.
-- The application_id references the id column in the applications table.
-- The attachment_id references the id column in the attachments table.

View File

@ -1,10 +0,0 @@
-- Add is_preferred column to comparison_offers table
ALTER TABLE comparison_offers
ADD COLUMN is_preferred BOOLEAN NOT NULL DEFAULT FALSE;
-- Add index for efficient queries
CREATE INDEX idx_comparison_offers_preferred
ON comparison_offers(application_id, cost_position_index, is_preferred);
-- Ensure only one offer per cost position can be preferred
-- This is enforced at the application level

View File

@ -1,14 +0,0 @@
-- Migration: Add URL column to comparison_offers table
-- Description: Add optional URL field to store links to online offers
-- Date: 2024
-- Add URL column to comparison_offers table
ALTER TABLE `comparison_offers`
ADD COLUMN `url` VARCHAR(500) DEFAULT NULL COMMENT 'Optional URL to offer document or webpage'
AFTER `description`;
-- Add index for URL column for better search performance
CREATE INDEX `idx_url` ON `comparison_offers` (`url`);
-- Update existing constraint to ensure either URL or attachment is provided
-- Note: This is handled at application level since MySQL doesn't support complex CHECK constraints

View File

@ -1,10 +0,0 @@
-- Migration: Alter attachments table data column to LONGTEXT
-- Description: Change data column from TEXT to LONGTEXT to support larger files
-- Date: 2024
-- Check if the attachments table exists and alter the data column
ALTER TABLE `attachments`
MODIFY COLUMN `data` LONGTEXT NOT NULL COMMENT 'Base64 encoded file data';
-- This migration fixes the issue where TEXT type (max 65,535 bytes) was too small
-- for Base64 encoded files. LONGTEXT supports up to 4GB of data.