diff --git a/.env b/.env deleted file mode 100644 index 97d22a55..00000000 --- a/.env +++ /dev/null @@ -1,34 +0,0 @@ -# Database configuration -DB_USER=postgres -DB_PASSWORD=password -DB_NAME=worklenz_db -DB_HOST=db -DB_PORT=5432 -DB_MAX_CLIENTS=50 - -# Server configuration -NODE_ENV=development -PORT=3000 -SESSION_NAME=worklenz.sid -SESSION_SECRET=worklenz-session-secret -COOKIE_SECRET=worklenz-cookie-secret - -# CORS -SOCKET_IO_CORS=http://localhost:5000 -SERVER_CORS=* - -# Storage configuration (MinIO) -STORAGE_PROVIDER=s3 -AWS_REGION=us-east-1 -AWS_BUCKET=worklenz-bucket -S3_ACCESS_KEY_ID=minioadmin -S3_SECRET_ACCESS_KEY=minioadmin -S3_URL=http://minio:9000 - -# Application URLs -HOSTNAME=localhost:5000 -FRONTEND_URL=http://localhost:5000 - -# For local development, set these to the frontend service -LOGIN_FAILURE_REDIRECT=http://localhost:5000 -LOGIN_SUCCESS_REDIRECT=http://localhost:5000/auth/authenticate \ No newline at end of file diff --git a/.env.example b/.env.example deleted file mode 100644 index 9765d3cc..00000000 --- a/.env.example +++ /dev/null @@ -1,34 +0,0 @@ -# Database configuration -DB_USER=postgres -DB_PASSWORD=your_db_password -DB_NAME=worklenz_db -DB_HOST=localhost -DB_PORT=5432 -DB_MAX_CLIENTS=50 - -# Server configuration -NODE_ENV=development -PORT=3000 -SESSION_NAME=worklenz.sid -SESSION_SECRET=your_session_secret -COOKIE_SECRET=your_cookie_secret - -# CORS -SOCKET_IO_CORS=http://localhost:5000 -SERVER_CORS=* - -# Storage configuration -STORAGE_PROVIDER=s3 -AWS_REGION=your_aws_region -AWS_BUCKET=your_bucket_name -S3_ACCESS_KEY_ID=your_access_key_id -S3_SECRET_ACCESS_KEY=your_secret_access_key -S3_URL=your_s3_url - -# Application URLs -HOSTNAME=localhost:5000 -FRONTEND_URL=http://localhost:5000 - -# For local development -LOGIN_FAILURE_REDIRECT=http://localhost:5000 -LOGIN_SUCCESS_REDIRECT=http://localhost:5000/auth/authenticate \ No newline at end of file diff --git a/README.md b/README.md index 92ed1a90..a6885d95 100644 --- a/README.md +++ b/README.md @@ -358,34 +358,88 @@ const s3Client = new S3Client({ ### Environment Configuration -The `.env` file includes the necessary configuration for using MinIO: +The project uses the following environment file structure: -``` -STORAGE_PROVIDER=s3 -AWS_REGION=us-east-1 -AWS_BUCKET=worklenz-bucket -S3_ACCESS_KEY_ID=minioadmin -S3_SECRET_ACCESS_KEY=minioadmin -S3_URL=http://minio:9000 +- **Frontend**: + - `worklenz-frontend/.env.development` - Development environment variables + - `worklenz-frontend/.env.production` - Production build variables + +- **Backend**: + - `worklenz-backend/.env` - Backend environment variables + +### Setting Up Environment Files + +The Docker environment script will create or overwrite all environment files: + +```bash +# For HTTP/WS +./update-docker-env.sh your-hostname + +# For HTTPS/WSS +./update-docker-env.sh your-hostname true ``` -When the backend service starts, it will use these environment variables to connect to MinIO for file storage. +This script generates properly configured environment files for both development and production environments. -## Development +## Docker Deployment -For development, you can use the provided Docker setup which includes all necessary dependencies. The code will be running inside containers, but you can still edit files locally and see changes reflected in real-time. +### Local Development with Docker -## Production Deployment +1. Set up the environment files: + ```bash + # For HTTP/WS + ./update-docker-env.sh + + # For HTTPS/WSS + ./update-docker-env.sh localhost true + ``` -For production deployment: +2. Run the application using Docker Compose: + ```bash + docker-compose up -d + ``` -1. Update the `.env` file with production settings -2. Build custom Docker images or use the provided ones -3. Deploy using Docker Compose or a container orchestration platform like Kubernetes +3. Access the application: + - Frontend: http://localhost:5000 + - Backend API: http://localhost:3000 (or https://localhost:3000 with SSL) -For MinIO in production, consider: -- Setting up proper credentials (change default minioadmin/minioadmin) -- Configuring persistent storage -- Setting up proper networking and access controls -- Using multiple MinIO instances for high availability +### Remote Server Deployment + +When deploying to a remote server: + +1. Set up the environment files with your server's hostname: + ```bash + # For HTTP/WS + ./update-docker-env.sh your-server-hostname + + # For HTTPS/WSS + ./update-docker-env.sh your-server-hostname true + ``` + + This ensures that the frontend correctly connects to the backend API. + +2. Pull and run the latest Docker images: + ```bash + docker-compose pull + docker-compose up -d + ``` + +3. Access the application through your server's hostname: + - Frontend: http://your-server-hostname:5000 + - Backend API: http://your-server-hostname:3000 + +### Environment Configuration + +The Docker setup uses environment variables to configure the services: + +- Frontend: + - `VITE_API_URL`: URL of the backend API (default: http://backend:3000 for container networking) + - `VITE_SOCKET_URL`: WebSocket URL for real-time communication (default: ws://backend:3000) + +- Backend: + - Database connection parameters + - Storage configuration + - Other backend settings + +For custom configuration, edit the `.env` file or the `update-docker-env.sh` script. diff --git a/docker-compose.yml b/docker-compose.yml index 02dab3ee..2008f850 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,12 +1,16 @@ services: frontend: - image: docker.io/chamikajaycey/worklenz-frontend:latest + build: + context: ./worklenz-frontend + dockerfile: Dockerfile container_name: worklenz_frontend ports: - "5000:5000" depends_on: backend: condition: service_started + env_file: + - ./worklenz-frontend/.env.production networks: - worklenz @@ -20,36 +24,8 @@ services: condition: service_healthy minio: condition: service_started - environment: - - AWS_REGION=${AWS_REGION:-us-east-1} - - BACKEND_PUBLIC_DIR - - BACKEND_VIEWS_DIR - - COMMIT_BUILD_IMMEDIATELY - - COOKIE_SECRET - - DB_HOST=${DB_HOST:-db} - - DB_MAX_CLIENTS - - DB_NAME=${DB_NAME:-worklenz_db} - - DB_PASSWORD=${DB_PASSWORD:-password} - - DB_PORT=${DB_PORT:-5432} - - DB_USER=${DB_USER:-postgres} - - GOOGLE_CALLBACK_URL - - GOOGLE_CLIENT_ID - - GOOGLE_CLIENT_SECRET - - HOSTNAME - - LOGIN_FAILURE_REDIRECT - - NODE_ENV=${NODE_ENV:-development} - - PORT=${PORT:-3000} - - SESSION_NAME - - SESSION_SECRET - - SLACK_WEBHOOK - - SOCKET_IO_CORS - - SOURCE_EMAIL - - USE_PG_NATIVE - - STORAGE_PROVIDER=${STORAGE_PROVIDER:-s3} - - AWS_BUCKET=${BUCKET:-worklenz-bucket} - - AWS_ACCESS_KEY_ID=${S3_ACCESS_KEY_ID:-minioadmin} - - AWS_SECRET_ACCESS_KEY=${S3_SECRET_ACCESS_KEY:-minioadmin} - - S3_URL=${S3_URL:-http://minio:9000} + env_file: + - ./worklenz-backend/.env networks: - worklenz @@ -113,7 +89,23 @@ services: - worklenz volumes: - worklenz_postgres_data:/var/lib/postgresql/data - - ./worklenz-backend/database/:/docker-entrypoint-initdb.d + - type: bind + source: ./worklenz-backend/database + target: /docker-entrypoint-initdb.d + consistency: cached + command: > + bash -c ' + if command -v apt-get >/dev/null 2>&1; then + apt-get update && apt-get install -y dos2unix + elif command -v apk >/dev/null 2>&1; then + apk add --no-cache dos2unix + fi && + find /docker-entrypoint-initdb.d -type f -name "*.sh" -exec sh -c '\'' + dos2unix "{}" 2>/dev/null || true + chmod +x "{}" + '\'' \; && + exec docker-entrypoint.sh postgres + ' volumes: worklenz_postgres_data: diff --git a/update-docker-env.sh b/update-docker-env.sh new file mode 100755 index 00000000..bfcca1ae --- /dev/null +++ b/update-docker-env.sh @@ -0,0 +1,133 @@ +#!/bin/bash + +# Script to set environment variables for Docker deployment +# Usage: ./update-docker-env.sh [hostname] [use_ssl] + +# Default hostname if not provided +DEFAULT_HOSTNAME="localhost" +HOSTNAME=${1:-$DEFAULT_HOSTNAME} + +# Check if SSL should be used +USE_SSL=${2:-false} + +# Set protocol prefixes based on SSL flag +if [ "$USE_SSL" = "true" ]; then + HTTP_PREFIX="https://" + WS_PREFIX="wss://" +else + HTTP_PREFIX="http://" + WS_PREFIX="ws://" +fi + +# Frontend URLs +FRONTEND_URL="${HTTP_PREFIX}${HOSTNAME}:5000" +MINIO_DASHBOARD_URL="${HTTP_PREFIX}${HOSTNAME}:9001" + +# Create or overwrite frontend .env.development file +mkdir -p worklenz-frontend +cat > worklenz-frontend/.env.development << EOL +# API Connection +VITE_API_URL=http://localhost:3000 +VITE_SOCKET_URL=ws://localhost:3000 + +# Application Environment +VITE_APP_TITLE=Worklenz +VITE_APP_ENV=development + +# Mixpanel +VITE_MIXPANEL_TOKEN= + +# Recaptcha +VITE_ENABLE_RECAPTCHA=false +VITE_RECAPTCHA_SITE_KEY= + +# Session ID +VITE_WORKLENZ_SESSION_ID=worklenz-session-id +EOL + +# Create frontend .env.production file +cat > worklenz-frontend/.env.production << EOL +# API Connection +VITE_API_URL=${HTTP_PREFIX}${HOSTNAME}:3000 +VITE_SOCKET_URL=${WS_PREFIX}${HOSTNAME}:3000 + +# Application Environment +VITE_APP_TITLE=Worklenz +VITE_APP_ENV=production + +# Mixpanel +VITE_MIXPANEL_TOKEN= + +# Recaptcha +VITE_ENABLE_RECAPTCHA=false +VITE_RECAPTCHA_SITE_KEY= + +# Session ID +VITE_WORKLENZ_SESSION_ID=worklenz-session-id +EOL + +# Create backend environment file +mkdir -p worklenz-backend +cat > worklenz-backend/.env << EOL +# Server +NODE_ENV=production +PORT=3000 +SESSION_NAME=worklenz.sid +SESSION_SECRET=change_me_in_production +COOKIE_SECRET=change_me_in_production + +# CORS +SOCKET_IO_CORS=${FRONTEND_URL} +SERVER_CORS=${FRONTEND_URL} + +# Database +DB_HOST=db +DB_PORT=5432 +DB_USER=postgres +DB_PASSWORD=password +DB_NAME=worklenz_db +DB_MAX_CLIENTS=50 +USE_PG_NATIVE=true + +# Storage Configuration +STORAGE_PROVIDER=s3 +AWS_REGION=us-east-1 +AWS_BUCKET=worklenz-bucket +AWS_ACCESS_KEY_ID=minioadmin +AWS_SECRET_ACCESS_KEY=minioadmin +S3_URL=http://minio:9000 + +# Backend Directories +BACKEND_PUBLIC_DIR=./public +BACKEND_VIEWS_DIR=./views + +# Host +HOSTNAME=${HOSTNAME} +FRONTEND_URL=${FRONTEND_URL} + +# Email +SOURCE_EMAIL=no-reply@example.com + +# Notifications +SLACK_WEBHOOK= + +# Other Settings +COMMIT_BUILD_IMMEDIATELY=true + +# JWT Secret +JWT_SECRET=change_me_in_production +EOL + +echo "Environment configuration updated for ${HOSTNAME} with" $([ "$USE_SSL" = "true" ] && echo "HTTPS/WSS" || echo "HTTP/WS") +echo "Created/updated environment files:" +echo "- worklenz-frontend/.env.development (development)" +echo "- worklenz-frontend/.env.production (production build)" +echo "- worklenz-backend/.env" +echo +echo "To run with Docker Compose, use: docker-compose up -d" +echo +echo "Frontend URL: ${FRONTEND_URL}" +echo "API URL: ${HTTP_PREFIX}${HOSTNAME}:3000" +echo "Socket URL: ${WS_PREFIX}${HOSTNAME}:3000" +echo "MinIO Dashboard URL: ${MINIO_DASHBOARD_URL}" +echo "CORS is configured to allow requests from: ${FRONTEND_URL}" \ No newline at end of file diff --git a/worklenz-backend/.env.example b/worklenz-backend/.env.example deleted file mode 100644 index 145f2411..00000000 --- a/worklenz-backend/.env.example +++ /dev/null @@ -1,76 +0,0 @@ -# Server -NODE_ENV=development -PORT=3000 -SESSION_NAME=worklenz.sid -SESSION_SECRET="your_session_secret" -COOKIE_SECRET="your_cookie_secret" - -# CORS -SOCKET_IO_CORS=http://localhost:5000 -SERVER_CORS=* - -# Database -DB_USER=postgres -DB_PASSWORD=your_db_password -DB_NAME=worklenz_db -DB_HOST=localhost -DB_PORT=5432 -DB_MAX_CLIENTS=50 - -# Google Login -GOOGLE_CLIENT_ID="your_google_client_id" -GOOGLE_CLIENT_SECRET="your_google_client_secret" -GOOGLE_CALLBACK_URL="http://localhost:5000/secure/google/verify" -LOGIN_FAILURE_REDIRECT="http://localhost:5000/auth/authenticating" -LOGIN_SUCCESS_REDIRECT="http://localhost:5000/auth/authenticating" - -# CLI -ANGULAR_DIST_DIR="path/to/frontend/dist" -ANGULAR_SRC_DIR="path/to/frontend" -BACKEND_PUBLIC_DIR="path/to/backend/public" -BACKEND_VIEWS_DIR="path/to/backend/views" -COMMIT_BUILD_IMMEDIATELY=false - -# HOST -HOSTNAME=localhost:5000 - -# SLACK -SLACK_WEBHOOK=your_slack_webhook_url -USE_PG_NATIVE=false - -# JWT SECRET -JWT_SECRET=your_jwt_secret - -# FRONTEND_URL -FRONTEND_URL=http://localhost:5000 - -# STORAGE -STORAGE_PROVIDER=s3 # values s3 or azure - -# AWS -AWS_REGION="your_aws_region" -AWS_ACCESS_KEY_ID="your_aws_access_key_id" -AWS_SECRET_ACCESS_KEY="your_aws_secret_access_key" -AWS_BUCKET="your_s3_bucket" -S3_URL="your_s3_url" - -# Azure Storage -AZURE_STORAGE_ACCOUNT_NAME="your_storage_account_name" -AZURE_STORAGE_CONTAINER="your_storage_container" -AZURE_STORAGE_ACCOUNT_KEY="your_storage_account_key" -AZURE_STORAGE_URL="your_storage_url" - -# DIRECTPAY -DP_STAGE=DEV -DP_URL=your_url -DP_MERCHANT_ID=your_merchant_id -DP_SECRET_KEY=your_secret_key -DP_API_KEY=your_api_key - -CONTACT_US_EMAIL=support@example.com - -GOOGLE_CAPTCHA_SECRET_KEY=your_captcha_secret_key -GOOGLE_CAPTCHA_PASS_SCORE=0.8 - -# Email Cronjobs -ENABLE_EMAIL_CRONJOBS=true \ No newline at end of file diff --git a/worklenz-backend/src/app.ts b/worklenz-backend/src/app.ts index 4fead2d9..4da00a0e 100644 --- a/worklenz-backend/src/app.ts +++ b/worklenz-backend/src/app.ts @@ -55,21 +55,25 @@ const allowedOrigins = [ isProduction() ? [ `http://localhost:5000`, - `http://127.0.0.1:5000` - ] + `http://127.0.0.1:5000`, + process.env.SERVER_CORS || "", // Add hostname from env + process.env.FRONTEND_URL || "" // Support FRONTEND_URL as well + ].filter(Boolean) // Remove empty strings : [ "http://localhost:3000", "http://localhost:5173", "http://127.0.0.1:5173", "http://127.0.0.1:3000", "http://127.0.0.1:5000", - `http://localhost:5000` - ] + `http://localhost:5000`, + process.env.SERVER_CORS || "", // Add hostname from env + process.env.FRONTEND_URL || "" // Support FRONTEND_URL as well + ].filter(Boolean) // Remove empty strings ].flat(); app.use(cors({ origin: (origin, callback) => { - if (!origin || allowedOrigins.includes(origin)) { + if (!isProduction() || !origin || allowedOrigins.includes(origin)) { callback(null, true); } else { console.log("Blocked origin:", origin, process.env.NODE_ENV); diff --git a/worklenz-backend/src/routes/public/index.ts b/worklenz-backend/src/routes/public/index.ts index 956c4ab4..5d088d6c 100644 --- a/worklenz-backend/src/routes/public/index.ts +++ b/worklenz-backend/src/routes/public/index.ts @@ -5,5 +5,8 @@ import safeControllerFunction from "../../shared/safe-controller-function"; const public_router = express.Router(); public_router.post("/new-subscriber", safeControllerFunction(ClientsController.addSubscriber)); +public_router.get("/health", (req, res) => { + res.status(200).json({ status: "ok" }); +}); export default public_router; diff --git a/worklenz-frontend/Dockerfile b/worklenz-frontend/Dockerfile index ac1f820f..37dffae6 100644 --- a/worklenz-frontend/Dockerfile +++ b/worklenz-frontend/Dockerfile @@ -7,6 +7,11 @@ COPY package.json package-lock.json ./ RUN npm ci COPY . . + +# Create env-config.js dynamically during build +RUN echo "window.VITE_API_URL='${VITE_API_URL:-http://backend:3000}';" > ./public/env-config.js && \ + echo "window.VITE_SOCKET_URL='${VITE_SOCKET_URL:-ws://backend:3000}';" >> ./public/env-config.js + RUN npm run build FROM node:22-alpine AS production @@ -16,5 +21,17 @@ WORKDIR /app RUN npm install -g serve COPY --from=build /app/build /app/build +COPY --from=build /app/public/env-config.js /app/build/env-config.js + +# Create start.sh script +RUN echo '#!/bin/sh' > /app/start.sh && \ + echo '# Update env-config.js with runtime environment variables' >> /app/start.sh && \ + echo 'cat > /app/build/env-config.js << EOL' >> /app/start.sh && \ + echo 'window.VITE_API_URL="${VITE_API_URL:-http://backend:3000}";' >> /app/start.sh && \ + echo 'window.VITE_SOCKET_URL="${VITE_SOCKET_URL:-ws://backend:3000}";' >> /app/start.sh && \ + echo 'EOL' >> /app/start.sh && \ + echo 'exec serve -s build -l 5000' >> /app/start.sh && \ + chmod +x /app/start.sh + EXPOSE 5000 -CMD ["serve", "-s", "build", "-l", "5000"] \ No newline at end of file +CMD ["/app/start.sh"] \ No newline at end of file diff --git a/worklenz-frontend/index.html b/worklenz-frontend/index.html index bad8d4a3..86abad6e 100644 --- a/worklenz-frontend/index.html +++ b/worklenz-frontend/index.html @@ -12,6 +12,8 @@ rel="stylesheet" />