Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a50ef47a52 | ||
|
|
db4240d99b | ||
|
|
bf1d48709c | ||
|
|
c3c0c288a8 | ||
|
|
79e8bb3734 | ||
|
|
a6884440a0 | ||
|
|
b9e5f396fd | ||
|
|
fc40ebcaba | ||
|
|
54642037d3 | ||
|
|
0778089ff3 | ||
|
|
ac2afd6949 | ||
|
|
8162ce65cb | ||
|
|
6e4bdea1c2 | ||
|
|
daf8ec2e0a | ||
|
|
2a3ae31e4e | ||
|
|
9c27c41a5e | ||
|
|
a328da679c | ||
|
|
122496513b | ||
|
|
7363c4c692 | ||
|
|
012e683240 | ||
|
|
b3a37df4be | ||
|
|
cb94b19e61 | ||
|
|
50c4f1a6ac |
34
.env
34
.env
@@ -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
|
||||
34
.env.example
34
.env.example
@@ -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
106
README.md
@@ -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.
|
||||
|
||||
|
||||
@@ -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
158
start.sh
Normal file → Executable 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
45
stop.sh
Normal file → Executable 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
133
update-docker-env.sh
Executable 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}"
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export interface ISocketSession {
|
||||
session?: {
|
||||
passport?: { user?: string; }
|
||||
passport?: { user?: { id: string } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"]
|
||||
@@ -12,6 +12,8 @@
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<title>Worklenz</title>
|
||||
<!-- Environment configuration -->
|
||||
<script src="/env-config.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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');
|
||||
|
||||
56
worklenz-frontend/src/config/env.ts
Normal file
56
worklenz-frontend/src/config/env.ts
Normal 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(),
|
||||
};
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user