diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4862899 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,109 @@ +# Multi-stage Dockerfile for Rustelo application +FROM rust:1.75-slim-bullseye as builder + +# Build arguments for feature selection +ARG CARGO_FEATURES="production" +ARG NO_DEFAULT_FEATURES="true" + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + pkg-config \ + libssl-dev \ + curl \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +# Install Node.js and npm for frontend builds +RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \ + && apt-get install -y nodejs + +# Install cargo-leptos for building the application +RUN cargo install cargo-leptos --version 0.2.20 + +# Set working directory +WORKDIR /app + +# Copy package.json and install node dependencies first (for better caching) +COPY package.json package-lock.json* ./ +RUN npm ci --only=production + +# Copy Cargo files for dependency caching +COPY Cargo.toml Cargo.lock ./ +COPY server/Cargo.toml ./server/ +COPY client/Cargo.toml ./client/ +COPY shared/Cargo.toml ./shared/ + +# Create dummy source files to build dependencies +RUN mkdir -p server/src client/src shared/src && \ + echo "fn main() {}" > server/src/main.rs && \ + echo "pub fn lib() {}" > server/src/lib.rs && \ + echo "pub fn lib() {}" > client/src/lib.rs && \ + echo "pub fn lib() {}" > shared/src/lib.rs + +# Build dependencies +RUN cargo build --release --bin server +RUN cargo leptos build --release + +# Remove dummy source files +RUN rm -rf server/src client/src shared/src + +# Copy the actual source code +COPY . . + +# Build the application with specified features +RUN if [ "$NO_DEFAULT_FEATURES" = "true" ]; then \ + cargo leptos build --release --features "$CARGO_FEATURES" --no-default-features; \ + else \ + cargo leptos build --release --features "$CARGO_FEATURES"; \ + fi + +# Runtime stage +FROM debian:bullseye-slim + +# Install runtime dependencies +RUN apt-get update && apt-get install -y \ + ca-certificates \ + libssl1.1 \ + && rm -rf /var/lib/apt/lists/* + +# Create app user +RUN useradd -m -u 1000 app + +# Set working directory +WORKDIR /app + +# Copy built application from builder stage +COPY --from=builder /app/target/release/server /app/server +COPY --from=builder /app/target/site /app/target/site +COPY --from=builder /app/public /app/public + +# Copy configuration files +COPY --from=builder /app/config.toml /app/config.toml +COPY --from=builder /app/config.prod.toml /app/config.prod.toml + +# Copy templates and other runtime assets +COPY --from=builder /app/templates /app/templates +COPY --from=builder /app/migrations /app/migrations + +# Create necessary directories +RUN mkdir -p /app/logs /app/uploads /app/tmp /app/cache /app/data /app/backups + +# Change ownership to app user +RUN chown -R app:app /app + +# Switch to app user +USER app + +# Expose port +EXPOSE 3030 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:3030/health || exit 1 + +# Set environment variables +ENV RUST_LOG=info +ENV ENVIRONMENT=production + +# Run the application +CMD ["./server"] diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 0000000..ad3c534 --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,58 @@ +# Development Dockerfile for Rustelo application with hot reload support +FROM rust:1.75-slim-bullseye + +# Build arguments for feature selection (defaults for development) +ARG CARGO_FEATURES="auth,content-db,crypto,email,metrics,examples" +ARG NO_DEFAULT_FEATURES="false" + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + pkg-config \ + libssl-dev \ + curl \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +# Install Node.js and npm for frontend builds +RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \ + && apt-get install -y nodejs + +# Install cargo-leptos for building the application +RUN cargo install cargo-leptos --version 0.2.20 + +# Install cargo-watch for hot reloading +RUN cargo install cargo-watch + +# Set working directory +WORKDIR /app + +# Copy package.json and install node dependencies +COPY package.json package-lock.json* ./ +RUN npm ci + +# Copy Cargo files +COPY Cargo.toml Cargo.lock ./ +COPY server/Cargo.toml ./server/ +COPY client/Cargo.toml ./client/ +COPY shared/Cargo.toml ./shared/ + +# Create app user but don't switch to it yet (for development flexibility) +RUN useradd -m -u 1000 app + +# Create necessary directories +RUN mkdir -p /app/logs /app/uploads /app/tmp /app/cache /app/data /app/backups + +# Change ownership to app user +RUN chown -R app:app /app + +# Expose ports +EXPOSE 3030 3031 + +# Set environment variables +ENV RUST_LOG=debug +ENV ENVIRONMENT=development +ENV CARGO_FEATURES=${CARGO_FEATURES} +ENV NO_DEFAULT_FEATURES=${NO_DEFAULT_FEATURES} + +# Default command for development with hot reload +CMD ["cargo", "leptos", "watch"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..decbdc4 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,177 @@ +version: '3.8' + +services: + # Main application + app: + build: + context: . + dockerfile: Dockerfile + ports: + - "3030:3030" + environment: + - RUST_LOG=info + - ENVIRONMENT=production + - DATABASE_URL=postgresql://postgres:password@db:5432/rustelo_prod + - REDIS_URL=redis://redis:6379 + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + volumes: + - ./uploads:/app/uploads + - ./logs:/app/logs + - ./data:/app/data + - ./backups:/app/backups + networks: + - app-network + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3030/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + # Development version with hot reload + app-dev: + build: + context: . + dockerfile: Dockerfile.dev + ports: + - "3030:3030" + - "3031:3031" # Hot reload port + environment: + - RUST_LOG=debug + - ENVIRONMENT=development + - DATABASE_URL=postgresql://postgres:password@db:5432/rustelo_dev + - REDIS_URL=redis://redis:6379 + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + volumes: + - .:/app + - ./target:/app/target + - ./uploads:/app/uploads + - ./logs:/app/logs + networks: + - app-network + profiles: + - dev + + # PostgreSQL database + db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=rustelo_prod + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=password + - POSTGRES_INITDB_ARGS=--auth-host=scram-sha-256 + volumes: + - postgres_data:/var/lib/postgresql/data + - ./migrations:/docker-entrypoint-initdb.d + ports: + - "5432:5432" + networks: + - app-network + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + + # Redis for caching and sessions + redis: + image: redis:7-alpine + command: redis-server --appendonly yes + volumes: + - redis_data:/data + ports: + - "6379:6379" + networks: + - app-network + restart: unless-stopped + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + # Prometheus for metrics collection + prometheus: + image: prom/prometheus:latest + ports: + - "9090:9090" + volumes: + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus_data:/prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/usr/share/prometheus/console_libraries' + - '--web.console.templates=/usr/share/prometheus/consoles' + - '--web.enable-lifecycle' + networks: + - app-network + restart: unless-stopped + + # Grafana for metrics visualization + grafana: + image: grafana/grafana:latest + ports: + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + volumes: + - grafana_data:/var/lib/grafana + - ./monitoring/grafana/dashboards:/var/lib/grafana/dashboards + - ./monitoring/grafana/provisioning:/etc/grafana/provisioning + networks: + - app-network + restart: unless-stopped + + # Nginx reverse proxy (optional) + nginx: + image: nginx:alpine + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + - ./certs:/etc/nginx/certs + depends_on: + - app + networks: + - app-network + restart: unless-stopped + profiles: + - proxy + + # Database migration runner + migrate: + build: + context: . + dockerfile: Dockerfile + command: ["./server", "--migrate"] + environment: + - DATABASE_URL=postgresql://postgres:password@db:5432/rustelo_prod + depends_on: + db: + condition: service_healthy + networks: + - app-network + profiles: + - migrate + +volumes: + postgres_data: + redis_data: + prometheus_data: + grafana_data: + +networks: + app-network: + driver: bridge