Compare commits

...

23 Commits

Author SHA1 Message Date
Chamika J
a50ef47a52 Merge pull request #93 from Worklenz/development
Development
2025-04-28 17:23:04 +05:30
Chamika J
db4240d99b Merge pull request #92 from chamikaJ/fix/docker-compose-fix
Enhance backend API with health check endpoint and update environment…
2025-04-28 16:28:01 +05:30
chamiakJ
bf1d48709c Enhance backend API with health check endpoint and update environment script
- Added a new health check endpoint to the public router in index.ts, returning a simple status response.
- Updated update-docker-env.sh to include the MinIO Dashboard URL in the environment configuration and output messages.
2025-04-28 16:22:54 +05:30
Chamika J
c3c0c288a8 Merge pull request #91 from chamikaJ/fix/docker-compose-fix
Refactor start.sh script creation in Dockerfile
2025-04-28 15:40:07 +05:30
chamiakJ
79e8bb3734 Refactor start.sh script creation in Dockerfile
- Updated the Dockerfile to create the start.sh script in a more structured manner, improving readability and maintainability.
- Ensured that the script dynamically updates env-config.js with runtime environment variables for API and WebSocket URLs.
2025-04-28 15:36:52 +05:30
Chamika J
a6884440a0 Merge pull request #90 from chamikaJ/fix/docker-compose-fix
Refactor frontend service configuration in docker-compose.yml
2025-04-28 15:22:39 +05:30
chamiakJ
b9e5f396fd Refactor frontend service configuration in docker-compose.yml
- Changed the frontend service to build from a local context instead of using a pre-built image.
- Specified the Dockerfile for the frontend build process.
2025-04-28 15:20:39 +05:30
Chamika J
fc40ebcaba Merge pull request #89 from chamikaJ/fix/docker-compose-fix
Enhance environment configuration for frontend and CORS support
2025-04-28 13:04:16 +05:30
chamiakJ
54642037d3 Enhance environment configuration for frontend and CORS support
- Updated update-docker-env.sh to define FRONTEND_URL and streamline CORS settings.
- Modified app.ts to include SERVER_CORS and FRONTEND_URL in allowed origins for both production and development environments.
- Improved output messages in update-docker-env.sh to provide clearer information on URLs and CORS configuration.
2025-04-28 13:02:29 +05:30
Chamika J
0778089ff3 Merge pull request #88 from chamikaJ/fix/docker-compose-fix
Remove legacy environment files and update Docker configuration for e…
2025-04-28 12:44:15 +05:30
chamiakJ
ac2afd6949 Remove legacy environment files and update Docker configuration for environment management
- Deleted .env and .env.example files to streamline environment variable management.
- Updated docker-compose.yml to utilize env_file for frontend and backend services.
- Enhanced update-docker-env.sh to create separate environment files for development and production.
- Revised README.md to reflect the new environment file structure and setup instructions.
2025-04-28 12:37:41 +05:30
Chamika J
8162ce65cb Merge pull request #87 from chamikaJ/fix/docker-compose-fix
Enhance WebSocket support and update environment configuration
2025-04-28 11:43:52 +05:30
chamiakJ
6e4bdea1c2 Enhance WebSocket support and update environment configuration
- Added VITE_SOCKET_URL to docker-compose.yml for WebSocket connection.
- Updated update-docker-env.sh to handle SSL options for WebSocket URLs.
- Modified Dockerfile to include VITE_SOCKET_URL in env-config.js.
- Implemented getSocketUrl function in frontend to retrieve WebSocket URL.
- Refactored socket configuration to use centralized socket URL from environment settings.
2025-04-28 11:43:16 +05:30
Chamika J
daf8ec2e0a Merge pull request #86 from chamikaJ/fix/docker-compose-fix
Fix/docker compose fix
2025-04-28 11:37:14 +05:30
chamiakJ
2a3ae31e4e Enhance Docker deployment with environment variable configuration
- Added environment variable setup in docker-compose.yml for VITE_API_URL.
- Introduced update-docker-env.sh script to create/update .env file for local and remote deployments.
- Updated Dockerfile to dynamically create env-config.js during build.
- Modified frontend to load environment configuration from env-config.js.
- Refactored API client to use centralized config for API URL.
2025-04-28 11:32:44 +05:30
chamikaJ
9c27c41a5e Add script to inject environment variables in Dockerfile
- Created a start.sh script to set environment variables for the application.
- Updated CMD to execute the new script instead of directly serving the build.
2025-04-25 16:42:45 +05:30
chamikaJ
a328da679c Update docker-compose.yml to use bind mount for database initialization and add script execution for dos2unix; modify CORS origin check in app.ts for production environment 2025-04-25 12:30:15 +05:30
Chamika J
122496513b Merge pull request #82 from chamikaJ/fix/socket-event-not-updating
Refactor socket session user structure and update user ID retrieval
2025-04-22 21:21:23 +05:30
chamiakJ
7363c4c692 Refactor socket session user structure and update user ID retrieval
- Changed the user structure in ISocketSession to include an object with an 'id' property.
- Updated the getLoggedInUserIdFromSocket function to return the user ID directly from the new structure.
2025-04-22 21:19:13 +05:30
Chamika J
012e683240 Merge pull request #80 from chamikaJ/react-version
Update security contact email in README.md
2025-04-21 22:11:09 +05:30
chamiakJ
b3a37df4be Update security contact email in README.md 2025-04-21 22:10:41 +05:30
Chamika J
cb94b19e61 Merge pull request #79 from chamikaJ/react-version
Update README.md and scripts for improved setup and service management
2025-04-21 20:56:40 +05:30
chamiakJ
50c4f1a6ac Update README.md and scripts for improved setup and service management
- Changed repository URLs in README.md for consistency.
- Enhanced start.sh with a service health check function to verify if services are running and responding.
- Improved output messages in start.sh and stop.sh for better user experience.
- Added checks for port conflicts and ensured proper stopping of services in stop.sh.
2025-04-21 20:54:50 +05:30
19 changed files with 483 additions and 275 deletions

34
.env
View File

@@ -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

View File

@@ -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

106
README.md
View File

@@ -62,7 +62,7 @@ These instructions will help you set up and run the Worklenz project on your loc
1. Clone the repository
```bash
git clone https://github.com/yourusername/worklenz.git
git clone https://github.com/Worklenz/worklenz.git
cd worklenz
```
@@ -124,7 +124,7 @@ The project includes a fully configured Docker setup with:
1. Clone the repository:
```bash
git clone https://github.com/yourusername/worklenz.git
git clone https://github.com/Worklenz/worklenz.git
cd worklenz
```
@@ -186,7 +186,7 @@ We welcome contributions from the community! If you'd like to contribute, please
If you believe you have found a security vulnerability in Worklenz, we encourage you to responsibly disclose this and not open a public issue. We will investigate all legitimate reports.
Email [your-security-email@example.com](mailto:your-security-email@example.com) to disclose any security vulnerabilities.
Email [info@worklenz.com](mailto:info@worklenz.com) to disclose any security vulnerabilities.
## License
@@ -272,8 +272,8 @@ The project includes a fully configured Docker setup with:
1. Clone the repository:
```bash
git clone https://github.com/yourusername/worklenz-react-v1.git
cd worklenz-react-v1
git clone https://github.com/Worklenz/worklenz.git
cd worklenz
```
2. Start the Docker containers (choose one option):
@@ -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.

View File

@@ -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:

158
start.sh Normal file → Executable file
View File

@@ -4,12 +4,13 @@
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Print banner
echo -e "${GREEN}"
echo " __ __ _ _"
echo " \ \ / / | | | |"
echo " \ \ / / | | | |"
echo " \ \ /\ / /__ _ __| | _| | ___ _ __ ____"
echo " \ \/ \/ / _ \| '__| |/ / |/ _ \ '_ \|_ /"
echo " \ /\ / (_) | | | <| | __/ | | |/ /"
@@ -29,6 +30,51 @@ if [ ! -f .env ]; then
fi
fi
# Function to check if a service is running
check_service() {
local service_name=$1
local container_name=$2
local url=$3
local max_attempts=30
local attempt=1
echo -e "${BLUE}Checking ${service_name} service...${NC}"
# First check if the container is running
while [ $attempt -le $max_attempts ]; do
if docker ps | grep -q "${container_name}"; then
# Container is running
if [ -z "$url" ]; then
# No URL to check, assume service is up
echo -e "${GREEN}${NC} ${service_name} is running"
return 0
else
# Check if service endpoint is responding
if curl -s -f -o /dev/null "$url"; then
echo -e "${GREEN}${NC} ${service_name} is running and responding at ${url}"
return 0
else
if [ $attempt -eq $max_attempts ]; then
echo -e "${YELLOW}${NC} ${service_name} container is running but not responding at ${url}"
return 1
fi
fi
fi
else
if [ $attempt -eq $max_attempts ]; then
echo -e "${RED}${NC} ${service_name} failed to start"
return 1
fi
fi
echo -n "."
attempt=$((attempt+1))
sleep 1
done
return 1
}
# Check if Docker is installed
if ! command -v docker &> /dev/null; then
echo -e "${RED}Error: Docker is not installed or not in PATH${NC}"
@@ -37,7 +83,7 @@ if ! command -v docker &> /dev/null; then
fi
# Check if Docker daemon is running
echo "Running preflight checks..."
echo -e "${BLUE}Running preflight checks...${NC}"
if ! docker info &> /dev/null; then
echo -e "${RED}Error: Docker daemon is not running${NC}"
echo "Please start Docker and try again"
@@ -45,58 +91,64 @@ if ! docker info &> /dev/null; then
fi
echo -e "${GREEN}${NC} Docker is running"
# Check if Docker Compose is installed
if ! command -v docker compose &> /dev/null; then
echo "Warning: Docker Compose V2 not found, trying docker-compose command..."
if ! command -v docker-compose &> /dev/null; then
echo "Error: Docker Compose is not installed or not in PATH"
echo "Please install Docker Compose: https://docs.docker.com/compose/install/"
exit 1
# Determine Docker Compose command to use
DOCKER_COMPOSE_CMD=""
if command -v docker compose &> /dev/null; then
DOCKER_COMPOSE_CMD="docker compose"
echo -e "${GREEN}${NC} Using Docker Compose V2"
elif command -v docker-compose &> /dev/null; then
DOCKER_COMPOSE_CMD="docker-compose"
echo -e "${YELLOW}${NC} Using legacy Docker Compose"
else
echo -e "${RED}Error: Docker Compose is not installed or not in PATH${NC}"
echo "Please install Docker Compose: https://docs.docker.com/compose/install/"
exit 1
fi
# Check if any of the ports are already in use
ports=(3000 5000 9000 9001 5432)
for port in "${ports[@]}"; do
if lsof -i:"$port" > /dev/null 2>&1; then
echo -e "${YELLOW}⚠ Warning: Port $port is already in use. This may cause conflicts.${NC}"
fi
# Use docker-compose command instead
docker-compose down
docker-compose up -d
done
# Start the containers
echo -e "${BLUE}Starting Worklenz services...${NC}"
$DOCKER_COMPOSE_CMD down
$DOCKER_COMPOSE_CMD up -d
# Wait for services to fully initialize
echo -e "${BLUE}Waiting for services to initialize...${NC}"
echo "This may take a minute or two depending on your system..."
# Check each service
check_service "Database" "worklenz_db" ""
DB_STATUS=$?
check_service "MinIO" "worklenz_minio" "http://localhost:9000/minio/health/live"
MINIO_STATUS=$?
check_service "Backend" "worklenz_backend" "http://localhost:3000/api/health"
BACKEND_STATUS=$?
check_service "Frontend" "worklenz_frontend" "http://localhost:5000"
FRONTEND_STATUS=$?
# Display service URLs
echo -e "\n${BLUE}Service URLs:${NC}"
[ $FRONTEND_STATUS -eq 0 ] && echo " • Frontend: http://localhost:5000"
[ $BACKEND_STATUS -eq 0 ] && echo " • Backend API: http://localhost:3000"
[ $MINIO_STATUS -eq 0 ] && echo " • MinIO Console: http://localhost:9001 (login: minioadmin/minioadmin)"
# Check if all services are up
if [ $DB_STATUS -eq 0 ] && [ $MINIO_STATUS -eq 0 ] && [ $BACKEND_STATUS -eq 0 ] && [ $FRONTEND_STATUS -eq 0 ]; then
echo -e "\n${GREEN}✅ All Worklenz services are running successfully!${NC}"
else
# Use Docker Compose V2
docker compose down
docker compose up -d
echo -e "\n${YELLOW}⚠ Some services may not be running properly. Check the logs for more details:${NC}"
echo " $DOCKER_COMPOSE_CMD logs"
fi
# Wait for services to be ready
echo "Waiting for services to start..."
sleep 5
# Check if services are running
if docker ps | grep -q "worklenz_frontend"; then
echo -e "${GREEN}${NC} Frontend is running"
FRONTEND_URL="http://localhost:5000"
echo " Frontend URL: $FRONTEND_URL"
else
echo "✗ Frontend service failed to start"
fi
if docker ps | grep -q "worklenz_backend"; then
echo -e "${GREEN}${NC} Backend is running"
BACKEND_URL="http://localhost:3000"
echo " Backend URL: $BACKEND_URL"
else
echo "✗ Backend service failed to start"
fi
if docker ps | grep -q "worklenz_minio"; then
echo -e "${GREEN}${NC} MinIO is running"
MINIO_URL="http://localhost:9001"
echo " MinIO Console URL: $MINIO_URL (login: minioadmin/minioadmin)"
else
echo "✗ MinIO service failed to start"
fi
if docker ps | grep -q "worklenz_db"; then
echo -e "${GREEN}${NC} Database is running"
else
echo "✗ Database service failed to start"
fi
echo -e "\n${GREEN}Worklenz is now running!${NC}"
echo "You can access the application at: http://localhost:5000"
echo "To stop the services, run: docker compose down"
echo -e "\n${BLUE}Useful commands:${NC}"
echo " • View logs: $DOCKER_COMPOSE_CMD logs -f"
echo " • Stop services: ./stop.sh"

45
stop.sh Normal file → Executable file
View File

@@ -2,18 +2,49 @@
# Colors for terminal output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
echo -e "${RED}Stopping Worklenz Docker Environment...${NC}"
# Print banner
echo -e "${RED}"
echo " __ __ _ _"
echo " \ \ / / | | | |"
echo " \ \ /\ / /__ _ __| | _| | ___ _ __ ____"
echo " \ \/ \/ / _ \| '__| |/ / |/ _ \ '_ \|_ /"
echo " \ /\ / (_) | | | <| | __/ | | |/ /"
echo " \/ \/ \___/|_| |_|\_\_|\___|_| |_/___|"
echo ""
echo " W O R K L E N Z "
echo -e "${NC}"
echo -e "${BLUE}Stopping Worklenz Docker Environment...${NC}"
# Check which Docker Compose command to use
# Determine Docker Compose command to use
DOCKER_COMPOSE_CMD=""
if command -v docker compose &> /dev/null; then
# Docker Compose V2
docker compose down
DOCKER_COMPOSE_CMD="docker compose"
echo -e "${GREEN}${NC} Using Docker Compose V2"
elif command -v docker-compose &> /dev/null; then
DOCKER_COMPOSE_CMD="docker-compose"
echo -e "${YELLOW}${NC} Using legacy Docker Compose"
else
# Legacy Docker Compose
docker-compose down
echo -e "${RED}Error: Docker Compose is not installed or not in PATH${NC}"
echo "Please install Docker Compose: https://docs.docker.com/compose/install/"
exit 1
fi
echo -e "${GREEN}Worklenz services have been stopped.${NC}"
# Stop the containers
echo -e "${BLUE}Stopping all services...${NC}"
$DOCKER_COMPOSE_CMD down
# Check if containers are still running
if docker ps | grep -q "worklenz_"; then
echo -e "${YELLOW}⚠ Some Worklenz containers are still running. Forcing stop...${NC}"
docker stop $(docker ps -q --filter "name=worklenz_")
echo -e "${GREEN}${NC} Forced stop completed."
else
echo -e "${GREEN}${NC} All Worklenz services have been stopped successfully."
fi
echo -e "\n${BLUE}To start Worklenz again, run:${NC} ./start.sh"

133
update-docker-env.sh Executable file
View File

@@ -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}"

View File

@@ -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

View File

@@ -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);

View File

@@ -1,5 +1,5 @@
export interface ISocketSession {
session?: {
passport?: { user?: string; }
passport?: { user?: { id: string } }
}
}

View File

@@ -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;

View File

@@ -15,7 +15,10 @@ export function log_error(error: any) {
export function getLoggedInUserIdFromSocket(socket: Socket): string | null {
const {session} = socket.request as ISocketSession;
return session?.passport?.user || null;
if (session?.passport?.user?.id) {
return session.passport.user.id;
}
return null;
}
export async function notifyProjectUpdates(socket: Socket, taskId: string) {

View File

@@ -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"]
CMD ["/app/start.sh"]

View File

@@ -12,6 +12,8 @@
rel="stylesheet"
/>
<title>Worklenz</title>
<!-- Environment configuration -->
<script src="/env-config.js"></script>
</head>
<body>

View File

@@ -2,6 +2,7 @@ import axios, { AxiosError } from 'axios';
import alertService from '@/services/alerts/alertService';
import logger from '@/utils/errorLogger';
import config from '@/config/env';
export const getCsrfToken = (): string | null => {
const match = document.cookie.split('; ').find(cookie => cookie.startsWith('XSRF-TOKEN='));
@@ -16,7 +17,7 @@ export const getCsrfToken = (): string | null => {
export const refreshCsrfToken = async (): Promise<string | null> => {
try {
// Make a GET request to the server to get a fresh CSRF token
await axios.get(`${import.meta.env.VITE_API_URL}/csrf-token`, { withCredentials: true });
await axios.get(`${config.apiUrl}/csrf-token`, { withCredentials: true });
return getCsrfToken();
} catch (error) {
console.error('Failed to refresh CSRF token:', error);
@@ -25,7 +26,7 @@ export const refreshCsrfToken = async (): Promise<string | null> => {
};
const apiClient = axios.create({
baseURL: import.meta.env.VITE_API_URL,
baseURL: config.apiUrl,
withCredentials: true,
headers: {
'Content-Type': 'application/json',

View File

@@ -6,13 +6,14 @@ import { IHomeTasksModel, IHomeTasksConfig } from '@/types/home/home-page.types'
import { IMyTask } from '@/types/home/my-tasks.types';
import { IProject } from '@/types/project/project.types';
import { getCsrfToken } from '../api-client';
import config from '@/config/env';
const rootUrl = '/home';
const api = createApi({
reducerPath: 'homePageApi',
baseQuery: fetchBaseQuery({
baseUrl: `${import.meta.env.VITE_API_URL}${API_BASE_URL}`,
baseUrl: `${config.apiUrl}${API_BASE_URL}`,
prepareHeaders: headers => {
headers.set('X-CSRF-Token', getCsrfToken() || '');
headers.set('Content-Type', 'application/json');

View File

@@ -6,13 +6,14 @@ import { IProjectsViewModel } from '@/types/project/projectsViewModel.types';
import { IServerResponse } from '@/types/common.types';
import { IProjectMembersViewModel } from '@/types/projectMember.types';
import { getCsrfToken } from '../api-client';
import config from '@/config/env';
const rootUrl = '/projects';
export const projectsApi = createApi({
reducerPath: 'projectsApi',
baseQuery: fetchBaseQuery({
baseUrl: `${import.meta.env.VITE_API_URL}${API_BASE_URL}`,
baseUrl: `${config.apiUrl}${API_BASE_URL}`,
prepareHeaders: headers => {
headers.set('X-CSRF-Token', getCsrfToken() || '');
headers.set('Content-Type', 'application/json');

View File

@@ -0,0 +1,56 @@
/**
* Environment configuration
* Reads from window environment variables (set by env-config.js)
* Falls back to import.meta.env variables (set during build time)
* Falls back to development defaults
*/
declare global {
interface Window {
VITE_API_URL?: string;
VITE_SOCKET_URL?: string;
}
}
export const getApiUrl = (): string => {
// First check runtime-injected environment variables
if (window.VITE_API_URL) {
return window.VITE_API_URL;
}
// Then check build-time environment variables
if (import.meta.env.VITE_API_URL) {
return import.meta.env.VITE_API_URL;
}
// Default for development
return 'http://localhost:3000';
};
export const getSocketUrl = (): string => {
// First check runtime-injected environment variables
if (window.VITE_SOCKET_URL) {
return window.VITE_SOCKET_URL;
}
// Then check build-time environment variables
if (import.meta.env.VITE_SOCKET_URL) {
return import.meta.env.VITE_SOCKET_URL;
}
// Default based on API URL (convert http->ws or https->wss)
const apiUrl = getApiUrl();
if (apiUrl.startsWith('https://')) {
return apiUrl.replace('https://', 'wss://');
} else if (apiUrl.startsWith('http://')) {
return apiUrl.replace('http://', 'ws://');
}
// Final fallback
return 'ws://localhost:3000';
};
export default {
apiUrl: getApiUrl(),
socketUrl: getSocketUrl(),
};

View File

@@ -1,5 +1,7 @@
import config from '@/config/env';
export const SOCKET_CONFIG = {
url: import.meta.env.VITE_SOCKET_URL || 'ws://localhost:3000',
url: config.socketUrl,
options: {
transports: ['websocket'],
path: '/socket',