diff --git a/README.md b/README.md
index a0eab3b..5ef62ef 100644
--- a/README.md
+++ b/README.md
@@ -142,6 +142,25 @@ volumes:
### Environment Variables
+#### Port Configuration
+
+Readur supports flexible port configuration:
+
+```bash
+# Method 1: Specify full server address
+SERVER_ADDRESS=0.0.0.0:3000
+
+# Method 2: Use separate host and port (recommended)
+SERVER_HOST=0.0.0.0
+SERVER_PORT=3000
+
+# For development: Configure frontend port
+CLIENT_PORT=5173
+BACKEND_PORT=8000
+```
+
+#### Security Configuration
+
Create a `.env` file for your secrets:
```bash
@@ -243,6 +262,8 @@ services:
- "traefik.http.routers.readur.tls.certresolver=letsencrypt"
```
+> ๐ **For detailed reverse proxy configurations** including Apache, Caddy, custom ports, load balancing, and advanced scenarios, see [REVERSE_PROXY.md](./REVERSE_PROXY.md).
+
### Health Checks
Add health checks to your Docker configuration:
diff --git a/docker-compose.yml b/docker-compose.yml
index 863521e..0252534 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,34 +1,100 @@
-services:
- readur:
- build: .
- ports:
- - "8000:8000"
- environment:
- - DATABASE_URL=postgresql://readur:readur_password@postgres:5432/readur
- - JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
- - UPLOAD_PATH=/app/uploads
- - WATCH_FOLDER=/app/watch
- - RUST_BACKTRACE=1
- volumes:
- - uploads:/app/uploads
- - watch:/app/watch
- depends_on:
- - postgres
- restart: unless-stopped
+version: '3.8'
+services:
postgres:
- image: postgres:15
+ image: postgres:16-alpine
environment:
- - POSTGRES_USER=readur
- - POSTGRES_PASSWORD=readur_password
- - POSTGRES_DB=readur
+ POSTGRES_USER: readur
+ POSTGRES_PASSWORD: readur
+ POSTGRES_DB: readur
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- - "5433:5432"
- restart: unless-stopped
+ - "5432:5432"
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U readur"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+ readur:
+ build: .
+ environment:
+ # Database configuration
+ DATABASE_URL: postgresql://readur:readur@postgres/readur
+
+ # Server configuration - choose one of these methods:
+ # Method 1: Use SERVER_ADDRESS for full control
+ # SERVER_ADDRESS: 0.0.0.0:8080
+
+ # Method 2: Use SERVER_HOST and SERVER_PORT separately
+ SERVER_HOST: 0.0.0.0
+ SERVER_PORT: 8080
+
+ # Security
+ JWT_SECRET: your-secret-key-change-this-in-production
+
+ # File paths
+ UPLOAD_PATH: /app/uploads
+ WATCH_FOLDER: /app/watch
+
+ # OCR configuration
+ OCR_LANGUAGE: eng
+ CONCURRENT_OCR_JOBS: 4
+ OCR_TIMEOUT_SECONDS: 300
+ MAX_FILE_SIZE_MB: 50
+
+ # Performance
+ MEMORY_LIMIT_MB: 512
+ CPU_PRIORITY: normal
+
+ # File watching
+ ALLOWED_FILE_TYPES: pdf,txt,doc,docx,png,jpg,jpeg
+ WATCH_INTERVAL_SECONDS: 30
+ FILE_STABILITY_CHECK_MS: 1000
+ MAX_FILE_AGE_HOURS: 24
+
+ ports:
+ # Map container port to host port
+ # Format: "host_port:container_port"
+ - "8080:8080"
+
+ volumes:
+ # Persistent storage for uploads
+ - readur_uploads:/app/uploads
+
+ # Watch folder - can be mapped to a host directory
+ - ./watch:/app/watch
+
+ # Or use a named volume for watch folder
+ # - readur_watch:/app/watch
+
+ depends_on:
+ postgres:
+ condition: service_healthy
+
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:8080/api/health"]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+ start_period: 40s
+
+ # Optional: Nginx reverse proxy example
+ nginx:
+ image: nginx:alpine
+ ports:
+ - "80:80"
+ - "443:443"
+ volumes:
+ - ./nginx.conf:/etc/nginx/nginx.conf:ro
+ - ./ssl:/etc/nginx/ssl:ro
+ depends_on:
+ - readur
+ profiles:
+ - with-proxy
volumes:
- uploads:
- watch:
- postgres_data:
\ No newline at end of file
+ postgres_data:
+ readur_uploads:
+ readur_watch:
\ No newline at end of file
diff --git a/DEPLOYMENT_SUMMARY.md b/docs/DEPLOYMENT_SUMMARY.md
similarity index 100%
rename from DEPLOYMENT_SUMMARY.md
rename to docs/DEPLOYMENT_SUMMARY.md
diff --git a/QUEUE_IMPROVEMENTS.md b/docs/QUEUE_IMPROVEMENTS.md
similarity index 100%
rename from QUEUE_IMPROVEMENTS.md
rename to docs/QUEUE_IMPROVEMENTS.md
diff --git a/docs/REVERSE_PROXY.md b/docs/REVERSE_PROXY.md
new file mode 100644
index 0000000..ab2381f
--- /dev/null
+++ b/docs/REVERSE_PROXY.md
@@ -0,0 +1,430 @@
+# Reverse Proxy Configuration Guide for Readur
+
+This guide covers various deployment scenarios for running Readur behind reverse proxies with configurable ports.
+
+## Table of Contents
+- [Port Configuration](#port-configuration)
+- [Docker Deployment](#docker-deployment)
+- [Nginx Configuration](#nginx-configuration)
+- [Traefik Configuration](#traefik-configuration)
+- [Apache Configuration](#apache-configuration)
+- [Caddy Configuration](#caddy-configuration)
+- [Common Scenarios](#common-scenarios)
+- [Troubleshooting](#troubleshooting)
+
+## Port Configuration
+
+Readur supports flexible port configuration through environment variables:
+
+### Server Port Configuration
+
+```bash
+# Method 1: Full address specification
+SERVER_ADDRESS=0.0.0.0:3000
+
+# Method 2: Separate host and port (recommended)
+SERVER_HOST=0.0.0.0
+SERVER_PORT=3000
+```
+
+### Development Port Configuration
+
+For frontend development with Vite:
+
+```bash
+# Backend API port
+BACKEND_PORT=8000
+
+# Frontend dev server port
+CLIENT_PORT=5173
+```
+
+## Docker Deployment
+
+### Basic Docker Run
+
+```bash
+# Run on custom port 3000
+docker run -d \
+ -e SERVER_PORT=3000 \
+ -e DATABASE_URL=postgresql://user:pass@host/db \
+ -e JWT_SECRET=your-secret-key \
+ -p 3000:3000 \
+ readur:latest
+```
+
+### Docker Compose with Custom Ports
+
+```yaml
+version: '3.8'
+
+services:
+ readur:
+ image: readur:latest
+ environment:
+ SERVER_PORT: 3000
+ DATABASE_URL: postgresql://readur:readur@postgres/readur
+ JWT_SECRET: ${JWT_SECRET}
+ ports:
+ - "3000:3000"
+```
+
+## Nginx Configuration
+
+### Basic Reverse Proxy
+
+```nginx
+server {
+ listen 80;
+ server_name readur.example.com;
+
+ location / {
+ proxy_pass http://localhost:3000;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ # Large file uploads for documents
+ client_max_body_size 100M;
+
+ # Extended timeouts for OCR processing
+ proxy_connect_timeout 300s;
+ proxy_send_timeout 300s;
+ proxy_read_timeout 300s;
+ }
+}
+```
+
+### SSL Configuration
+
+```nginx
+server {
+ listen 443 ssl http2;
+ server_name readur.example.com;
+
+ ssl_certificate /path/to/cert.pem;
+ ssl_certificate_key /path/to/key.pem;
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_ciphers HIGH:!aNULL:!MD5;
+
+ location / {
+ proxy_pass http://localhost:3000;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto https;
+
+ # WebSocket support (future feature)
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+}
+```
+
+### Subpath Configuration
+
+To serve Readur under a subpath like `/readur/`:
+
+```nginx
+location /readur/ {
+ # Remove prefix when passing to backend
+ rewrite ^/readur/(.*) /$1 break;
+
+ proxy_pass http://localhost:3000;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header X-Forwarded-Prefix /readur;
+}
+```
+
+## Traefik Configuration
+
+### Docker Labels
+
+```yaml
+version: '3.8'
+
+services:
+ readur:
+ image: readur:latest
+ environment:
+ SERVER_PORT: 3000
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.readur.rule=Host(`readur.example.com`)"
+ - "traefik.http.routers.readur.tls=true"
+ - "traefik.http.routers.readur.tls.certresolver=letsencrypt"
+ - "traefik.http.services.readur.loadbalancer.server.port=3000"
+ # Middleware for large uploads
+ - "traefik.http.middlewares.readur-upload.buffering.maxRequestBodyBytes=104857600"
+ - "traefik.http.routers.readur.middlewares=readur-upload"
+```
+
+### Static Configuration
+
+```yaml
+# traefik.yml
+http:
+ routers:
+ readur:
+ rule: "Host(`readur.example.com`)"
+ service: readur
+ tls:
+ certResolver: letsencrypt
+ middlewares:
+ - readur-headers
+
+ services:
+ readur:
+ loadBalancer:
+ servers:
+ - url: "http://localhost:3000"
+ healthCheck:
+ path: /api/health
+ interval: 30s
+
+ middlewares:
+ readur-headers:
+ headers:
+ customRequestHeaders:
+ X-Forwarded-Proto: https
+```
+
+## Apache Configuration
+
+### Basic Reverse Proxy
+
+```apache
+
+ ServerName readur.example.com
+
+ ProxyRequests Off
+ ProxyPreserveHost On
+
+ ProxyPass / http://localhost:3000/
+ ProxyPassReverse / http://localhost:3000/
+
+ # Large file uploads
+ LimitRequestBody 104857600
+
+ # Extended timeouts
+ ProxyTimeout 300
+
+```
+
+### SSL Configuration
+
+```apache
+
+ ServerName readur.example.com
+
+ SSLEngine on
+ SSLCertificateFile /path/to/cert.pem
+ SSLCertificateKeyFile /path/to/key.pem
+
+ ProxyRequests Off
+ ProxyPreserveHost On
+
+ ProxyPass / http://localhost:3000/
+ ProxyPassReverse / http://localhost:3000/
+
+ RequestHeader set X-Forwarded-Proto "https"
+
+```
+
+## Caddy Configuration
+
+### Caddyfile
+
+```caddy
+readur.example.com {
+ reverse_proxy localhost:3000 {
+ header_up X-Real-IP {remote_host}
+ header_up X-Forwarded-Proto {scheme}
+
+ # Health check
+ health_uri /api/health
+ health_interval 30s
+
+ # Extended timeouts
+ transport http {
+ dial_timeout 10s
+ response_header_timeout 300s
+ }
+ }
+
+ # Large file uploads
+ request_body {
+ max_size 100MB
+ }
+}
+```
+
+## Common Scenarios
+
+### Multiple Instances with Load Balancing
+
+#### Nginx
+```nginx
+upstream readur_pool {
+ least_conn;
+ server readur1:3000 max_fails=3 fail_timeout=30s;
+ server readur2:3000 max_fails=3 fail_timeout=30s;
+ server readur3:3000 max_fails=3 fail_timeout=30s;
+}
+
+server {
+ location / {
+ proxy_pass http://readur_pool;
+ # ... other proxy settings
+ }
+}
+```
+
+#### Docker Compose Scale
+```bash
+# Scale to 3 instances
+docker compose up -d --scale readur=3
+```
+
+### Blue-Green Deployment
+
+```nginx
+# nginx.conf
+upstream readur_blue {
+ server blue:3000;
+}
+
+upstream readur_green {
+ server green:3000;
+}
+
+# Switch between blue and green
+upstream readur_current {
+ server blue:3000; # Change to green:3000 for switch
+}
+
+server {
+ location / {
+ proxy_pass http://readur_current;
+ }
+}
+```
+
+### Rate Limiting
+
+```nginx
+# Define rate limit zones
+limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
+limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/m;
+
+server {
+ # Apply to API endpoints
+ location /api/ {
+ limit_req zone=api burst=20 nodelay;
+ proxy_pass http://localhost:3000;
+ }
+
+ # Stricter for auth
+ location /api/auth/ {
+ limit_req zone=auth burst=5 nodelay;
+ proxy_pass http://localhost:3000;
+ }
+}
+```
+
+## Troubleshooting
+
+### Common Issues
+
+1. **502 Bad Gateway**
+ - Check if Readur is running on the configured port
+ - Verify `SERVER_PORT` environment variable
+ - Check container logs: `docker logs readur`
+
+2. **Large File Upload Failures**
+ - Increase `client_max_body_size` in nginx
+ - Adjust `MAX_FILE_SIZE_MB` in Readur config
+ - Check proxy timeout settings
+
+3. **WebSocket Connection Issues** (future feature)
+ - Ensure `proxy_http_version 1.1` is set
+ - Include Upgrade and Connection headers
+
+4. **CORS Errors**
+ - Readur includes CORS middleware
+ - Ensure `X-Forwarded-Proto` header is set correctly
+ - Check if frontend URL matches expected origin
+
+### Health Check Monitoring
+
+```bash
+# Direct health check
+curl http://localhost:3000/api/health
+
+# Through reverse proxy
+curl https://readur.example.com/api/health
+
+# Expected response
+{"status":"ok"}
+```
+
+### Debug Headers
+
+Add these to your reverse proxy for debugging:
+
+```nginx
+add_header X-Proxy-Debug "nginx" always;
+add_header X-Upstream-Addr $upstream_addr always;
+add_header X-Upstream-Status $upstream_status always;
+```
+
+## Security Recommendations
+
+1. **Always use HTTPS** in production
+2. **Implement rate limiting** to prevent abuse
+3. **Set security headers**:
+ ```nginx
+ add_header X-Frame-Options "SAMEORIGIN" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header X-XSS-Protection "1; mode=block" always;
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
+ ```
+
+4. **Restrict access** to health check endpoint if needed:
+ ```nginx
+ location /api/health {
+ allow 10.0.0.0/8;
+ deny all;
+ proxy_pass http://localhost:3000;
+ }
+ ```
+
+5. **Use strong JWT secrets** and rotate them regularly
+6. **Monitor access logs** for suspicious activity
+
+## Performance Optimization
+
+1. **Enable caching** for static assets:
+ ```nginx
+ location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
+ proxy_pass http://localhost:3000;
+ expires 1y;
+ add_header Cache-Control "public, immutable";
+ }
+ ```
+
+2. **Use HTTP/2** for better performance
+3. **Enable gzip compression**:
+ ```nginx
+ gzip on;
+ gzip_types text/plain text/css application/json application/javascript;
+ gzip_min_length 1000;
+ ```
+
+4. **Configure connection pooling** for database
+5. **Use CDN** for static assets in production
\ No newline at end of file
diff --git a/TESTING.md b/docs/TESTING.md
similarity index 100%
rename from TESTING.md
rename to docs/TESTING.md
diff --git a/WATCH_FOLDER.md b/docs/WATCH_FOLDER.md
similarity index 100%
rename from WATCH_FOLDER.md
rename to docs/WATCH_FOLDER.md
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 7759654..cb2a8df 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -1,12 +1,17 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
+// Support environment variables for development
+const BACKEND_PORT = process.env.BACKEND_PORT || '8000'
+const CLIENT_PORT = process.env.CLIENT_PORT || '5173'
+
export default defineConfig({
plugins: [react()],
server: {
+ port: parseInt(CLIENT_PORT),
proxy: {
'/api': {
- target: 'http://localhost:8000',
+ target: `http://localhost:${BACKEND_PORT}`,
changeOrigin: true,
},
},
diff --git a/nginx.conf b/nginx.conf
new file mode 100644
index 0000000..234164c
--- /dev/null
+++ b/nginx.conf
@@ -0,0 +1,145 @@
+events {
+ worker_connections 1024;
+}
+
+http {
+ # Upstream configuration
+ upstream readur {
+ server readur:8080;
+ }
+
+ # Rate limiting
+ limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
+ limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/m;
+
+ server {
+ listen 80;
+ server_name localhost;
+
+ # Redirect HTTP to HTTPS (uncomment in production)
+ # return 301 https://$server_name$request_uri;
+
+ # For development without SSL
+ location / {
+ proxy_pass http://readur;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+ }
+
+ # HTTPS server configuration
+ server {
+ listen 443 ssl http2;
+ server_name localhost;
+
+ # SSL configuration (update paths as needed)
+ ssl_certificate /etc/nginx/ssl/cert.pem;
+ ssl_certificate_key /etc/nginx/ssl/key.pem;
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_ciphers HIGH:!aNULL:!MD5;
+ ssl_prefer_server_ciphers on;
+
+ # Security headers
+ add_header X-Frame-Options "SAMEORIGIN" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header X-XSS-Protection "1; mode=block" always;
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
+
+ # Max upload size
+ client_max_body_size 100M;
+ client_body_timeout 300s;
+
+ # Main application
+ location / {
+ proxy_pass http://readur;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ # WebSocket support (if needed in future)
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+
+ # Timeouts for long-running OCR operations
+ proxy_connect_timeout 300s;
+ proxy_send_timeout 300s;
+ proxy_read_timeout 300s;
+ }
+
+ # API rate limiting
+ location /api/ {
+ limit_req zone=api burst=20 nodelay;
+
+ proxy_pass http://readur;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+
+ # Stricter rate limiting for auth endpoints
+ location /api/auth/ {
+ limit_req zone=auth burst=5 nodelay;
+
+ proxy_pass http://readur;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+
+ # Health check endpoint (no rate limiting)
+ location /api/health {
+ proxy_pass http://readur;
+ proxy_set_header Host $host;
+ access_log off;
+ }
+
+ # Static assets caching
+ location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
+ proxy_pass http://readur;
+ proxy_set_header Host $host;
+ expires 1y;
+ add_header Cache-Control "public, immutable";
+ }
+ }
+}
+
+# Subpath example - serving readur under /readur/
+# server {
+# listen 80;
+# server_name example.com;
+#
+# location /readur/ {
+# # Remove /readur prefix when passing to backend
+# rewrite ^/readur/(.*) /$1 break;
+#
+# proxy_pass http://readur;
+# proxy_set_header Host $host;
+# proxy_set_header X-Real-IP $remote_addr;
+# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+# proxy_set_header X-Forwarded-Proto $scheme;
+# proxy_set_header X-Forwarded-Prefix /readur;
+# }
+# }
+
+# Multiple instances example
+# upstream readur_pool {
+# least_conn;
+# server readur1:8080;
+# server readur2:8080;
+# server readur3:8080;
+# }
+#
+# server {
+# listen 80;
+#
+# location / {
+# proxy_pass http://readur_pool;
+# # ... rest of proxy configuration
+# }
+# }
\ No newline at end of file
diff --git a/run_all_tests.sh b/run_all_tests.sh
deleted file mode 100755
index a5ed68c..0000000
--- a/run_all_tests.sh
+++ /dev/null
@@ -1,229 +0,0 @@
-#!/bin/bash
-
-# Readur - Complete Test Suite Runner
-# This script runs all unit tests and integration tests for the Readur project
-
-set -e
-
-echo "๐งช Readur Complete Test Suite"
-echo "=============================="
-
-# Colors for output
-RED='\033[0;31m'
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-BLUE='\033[0;34m'
-NC='\033[0m' # No Color
-
-# Function to print colored output
-print_step() {
- echo -e "${BLUE}๐ $1${NC}"
-}
-
-print_success() {
- echo -e "${GREEN}โ
$1${NC}"
-}
-
-print_warning() {
- echo -e "${YELLOW}โ ๏ธ $1${NC}"
-}
-
-print_error() {
- echo -e "${RED}โ $1${NC}"
-}
-
-# Check if PostgreSQL is running (for integration tests)
-check_postgres() {
- if ! command -v psql >/dev/null 2>&1; then
- print_warning "PostgreSQL not found. Integration tests may fail."
- return 1
- fi
-
- if ! pg_isready >/dev/null 2>&1; then
- print_warning "PostgreSQL is not running. Integration tests may fail."
- return 1
- fi
-
- return 0
-}
-
-# Backend Unit Tests
-run_backend_unit_tests() {
- print_step "Running Backend Unit Tests"
-
- if cargo test --lib; then
- print_success "Backend unit tests passed"
- return 0
- else
- print_error "Backend unit tests failed"
- return 1
- fi
-}
-
-# Backend Integration Tests
-run_backend_integration_tests() {
- print_step "Running Backend Integration Tests"
-
- if ! check_postgres; then
- print_warning "Skipping integration tests - PostgreSQL not available"
- return 0
- fi
-
- # Check if server is running
- if ! curl -s http://localhost:8000/api/health >/dev/null 2>&1; then
- print_warning "Server not running at localhost:8000"
- print_warning "Start server with: cargo run"
- print_warning "Skipping integration tests"
- return 0
- fi
-
- if RUST_BACKTRACE=1 cargo test --test integration_tests; then
- print_success "Backend integration tests passed"
- return 0
- else
- print_error "Backend integration tests failed"
- return 1
- fi
-}
-
-# Frontend Tests
-run_frontend_tests() {
- print_step "Running Frontend Tests"
-
- if [ ! -d "frontend" ]; then
- print_error "Frontend directory not found"
- return 1
- fi
-
- cd frontend
-
- if [ ! -f "package.json" ]; then
- print_error "package.json not found in frontend directory"
- cd ..
- return 1
- fi
-
- # Install dependencies if node_modules doesn't exist
- if [ ! -d "node_modules" ]; then
- print_step "Installing frontend dependencies..."
- npm install
- fi
-
- if npm test -- --run; then
- print_success "Frontend tests completed"
- cd ..
- return 0
- else
- print_warning "Frontend tests had failures (this is expected - work in progress)"
- cd ..
- return 0 # Don't fail the overall script for frontend test issues
- fi
-}
-
-# Test Coverage (optional)
-generate_coverage() {
- print_step "Generating Test Coverage (optional)"
-
- # Backend coverage
- if command -v cargo-tarpaulin >/dev/null 2>&1; then
- print_step "Generating backend coverage..."
- cargo tarpaulin --out Html --output-dir coverage/ >/dev/null 2>&1 || true
- print_success "Backend coverage generated in coverage/"
- else
- print_warning "cargo-tarpaulin not installed. Run: cargo install cargo-tarpaulin"
- fi
-
- # Frontend coverage
- if [ -d "frontend" ]; then
- cd frontend
- if npm run test:coverage >/dev/null 2>&1; then
- print_success "Frontend coverage generated in frontend/coverage/"
- fi
- cd ..
- fi
-}
-
-# Main execution
-main() {
- echo "Starting test suite at $(date)"
- echo ""
-
- # Track overall success
- overall_success=true
-
- # Run backend unit tests
- if ! run_backend_unit_tests; then
- overall_success=false
- fi
-
- echo ""
-
- # Run backend integration tests
- if ! run_backend_integration_tests; then
- overall_success=false
- fi
-
- echo ""
-
- # Run frontend tests (don't fail overall on frontend issues)
- run_frontend_tests
-
- echo ""
-
- # Generate coverage if requested
- if [ "$1" = "--coverage" ]; then
- generate_coverage
- echo ""
- fi
-
- # Summary
- echo "=============================="
- if [ "$overall_success" = true ]; then
- print_success "Test Suite Completed Successfully!"
- echo ""
- echo "๐ Test Summary:"
- echo " โ
Backend Unit Tests: PASSED"
- echo " โ
Backend Integration Tests: PASSED"
- echo " ๐ Frontend Tests: IN PROGRESS (28/75 passing)"
- echo ""
- echo "๐ All critical backend tests are passing!"
- exit 0
- else
- print_error "Test Suite Failed"
- echo ""
- echo "โ Some backend tests failed. Check output above for details."
- echo ""
- echo "๐ก Troubleshooting tips:"
- echo " โข Ensure PostgreSQL is running"
- echo " โข Check DATABASE_URL environment variable"
- echo " โข Start server: cargo run"
- echo " โข Run with debug: RUST_BACKTRACE=1 cargo test"
- exit 1
- fi
-}
-
-# Handle script arguments
-case "$1" in
- --help|-h)
- echo "Usage: $0 [OPTIONS]"
- echo ""
- echo "Options:"
- echo " --coverage Generate test coverage reports"
- echo " --help, -h Show this help message"
- echo ""
- echo "Examples:"
- echo " $0 # Run all tests"
- echo " $0 --coverage # Run all tests and generate coverage"
- echo ""
- echo "Prerequisites:"
- echo " โข Rust toolchain"
- echo " โข PostgreSQL (for integration tests)"
- echo " โข Node.js (for frontend tests)"
- echo ""
- echo "For detailed testing documentation, see TESTING.md"
- exit 0
- ;;
- *)
- main "$@"
- ;;
-esac
diff --git a/run_tests.sh b/run_tests.sh
deleted file mode 100755
index 80517ee..0000000
--- a/run_tests.sh
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/bin/bash
-
-# Test runner script for Readur
-# This script runs tests in different modes to handle dependencies
-
-echo "๐งช Readur Test Runner"
-echo "===================="
-
-# Function to run tests with specific configuration
-run_tests() {
- local mode="$1"
- local flags="$2"
- local description="$3"
-
- echo ""
- echo "๐ Running $description"
- echo "Command: cargo test $flags"
- echo "-------------------------------------------"
-
- if cargo test $flags; then
- echo "โ
$description: PASSED"
- else
- echo "โ $description: FAILED"
- return 1
- fi
-}
-
-# Check if Docker is available for integration tests
-check_docker() {
- if command -v docker &> /dev/null && docker info &> /dev/null; then
- echo "๐ณ Docker is available - integration tests can run"
- return 0
- else
- echo "โ ๏ธ Docker not available - skipping integration tests"
- return 1
- fi
-}
-
-# Main test execution
-echo "Starting test execution..."
-
-# 1. Run unit tests without OCR dependencies (fastest)
-run_tests "unit" "--lib --no-default-features -- --skip database --skip integration" "Unit tests (no OCR/DB dependencies)"
-unit_result=$?
-
-# 2. Run unit tests with OCR dependencies (requires tesseract)
-if command -v tesseract &> /dev/null; then
- echo "๐ท Tesseract OCR available - running OCR tests"
- run_tests "ocr" "--lib --features ocr -- --skip database --skip integration" "Unit tests with OCR support"
- ocr_result=$?
-else
- echo "โ ๏ธ Tesseract not available - skipping OCR tests"
- echo " Install with: sudo apt-get install tesseract-ocr tesseract-ocr-eng"
- ocr_result=0 # Don't fail if tesseract isn't available
-fi
-
-# 3. Run integration tests (requires Docker for PostgreSQL)
-if check_docker; then
- run_tests "integration" "--lib --features ocr" "Integration tests (requires Docker/PostgreSQL)"
- integration_result=$?
-else
- integration_result=0 # Don't fail if Docker isn't available
-fi
-
-# Summary
-echo ""
-echo "๐ Test Summary"
-echo "==============="
-echo "Unit tests (basic): $([ $unit_result -eq 0 ] && echo "โ
PASSED" || echo "โ FAILED")"
-echo "Unit tests (with OCR): $([ $ocr_result -eq 0 ] && echo "โ
PASSED" || echo "โ ๏ธ SKIPPED")"
-echo "Integration tests: $([ $integration_result -eq 0 ] && echo "โ
PASSED" || echo "โ ๏ธ SKIPPED")"
-
-# Exit with appropriate code
-if [ $unit_result -eq 0 ]; then
- echo ""
- echo "๐ Core functionality tests passed!"
- echo "Your code changes are working correctly."
- exit 0
-else
- echo ""
- echo "๐ฅ Some tests failed. Please check the output above."
- exit 1
-fi
\ No newline at end of file
diff --git a/src/config.rs b/src/config.rs
index a2a2303..9af6a09 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -31,8 +31,16 @@ impl Config {
Ok(Config {
database_url: env::var("DATABASE_URL")
.unwrap_or_else(|_| "postgresql://readur:readur@localhost/readur".to_string()),
- server_address: env::var("SERVER_ADDRESS")
- .unwrap_or_else(|_| "0.0.0.0:8000".to_string()),
+ server_address: {
+ // Support both SERVER_ADDRESS (full address) and SERVER_PORT (just port)
+ if let Ok(addr) = env::var("SERVER_ADDRESS") {
+ addr
+ } else {
+ let host = env::var("SERVER_HOST").unwrap_or_else(|_| "0.0.0.0".to_string());
+ let port = env::var("SERVER_PORT").unwrap_or_else(|_| "8000".to_string());
+ format!("{}:{}", host, port)
+ }
+ },
jwt_secret: env::var("JWT_SECRET")
.unwrap_or_else(|_| "your-secret-key".to_string()),
upload_path: env::var("UPLOAD_PATH")
diff --git a/test_watch_folder.sh b/test_watch_folder.sh
deleted file mode 100755
index 880c1ec..0000000
--- a/test_watch_folder.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash
-
-# Test script for watch folder functionality
-echo "Testing watch folder functionality..."
-
-# Create a test watch folder if it doesn't exist
-mkdir -p ./watch
-
-echo "Creating test files in watch folder..."
-
-# Create a test text file
-echo "This is a test document for OCR processing." > ./watch/test_document.txt
-
-# Create a test PDF file (mock content)
-echo "%PDF-1.4 Mock PDF for testing" > ./watch/test_document.pdf
-
-# Create a test image file (mock content)
-echo "Mock PNG image content" > ./watch/test_image.png
-
-echo "Test files created in ./watch/ folder:"
-ls -la ./watch/
-
-echo ""
-echo "Watch folder setup complete!"
-echo "You can now:"
-echo "1. Start the readur application"
-echo "2. Copy OCR-able files to the ./watch/ folder"
-echo "3. Monitor the logs to see files being processed"
-echo ""
-echo "Supported file types: PDF, PNG, JPG, JPEG, TIFF, BMP, TXT, DOC, DOCX"
-echo ""
-echo "Environment variables for configuration:"
-echo "- WATCH_FOLDER: Path to watch folder (default: ./watch)"
-echo "- WATCH_INTERVAL_SECONDS: Polling interval (default: 30)"
-echo "- FILE_STABILITY_CHECK_MS: File stability check time (default: 500)"
-echo "- MAX_FILE_AGE_HOURS: Skip files older than this (default: none)"
-echo "- FORCE_POLLING_WATCH: Force polling mode (default: auto-detect)"
\ No newline at end of file