Merge pull request #86 from chamikaJ/fix/docker-compose-fix

Fix/docker compose fix
This commit is contained in:
Chamika J
2025-04-28 11:37:14 +05:30
committed by GitHub
10 changed files with 165 additions and 7 deletions

View File

@@ -389,3 +389,58 @@ For MinIO in production, consider:
- Setting up proper networking and access controls
- Using multiple MinIO instances for high availability
## Docker Deployment
### Local Development with Docker
1. Set up the environment variables:
```bash
./update-docker-env.sh
```
This will create a `.env` file with default settings for local development.
2. Run the application using Docker Compose:
```bash
docker-compose up -d
```
3. Access the application:
- Frontend: http://localhost:5000
- Backend API: http://localhost:3000
### Remote Server Deployment
When deploying to a remote server:
1. Set up the environment variables with your server's hostname:
```bash
./update-docker-env.sh your-server-hostname
```
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)
- 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

@@ -7,6 +7,8 @@ services:
depends_on:
backend:
condition: service_started
environment:
- VITE_API_URL=${VITE_API_URL:-http://backend:3000}
networks:
- worklenz
@@ -113,7 +115,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:

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

@@ -0,0 +1,34 @@
#!/bin/bash
# Script to set environment variables for Docker deployment
# Usage: ./update-docker-env.sh [hostname]
# Default hostname if not provided
DEFAULT_HOSTNAME="localhost"
HOSTNAME=${1:-$DEFAULT_HOSTNAME}
# Create or update root .env file
cat > .env << EOL
# Frontend Configuration
VITE_API_URL=http://${HOSTNAME}:3000
# Backend Configuration
DB_HOST=db
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=password
DB_NAME=worklenz_db
NODE_ENV=development
PORT=3000
# Storage Configuration
AWS_REGION=us-east-1
STORAGE_PROVIDER=s3
BUCKET=worklenz-bucket
S3_ACCESS_KEY_ID=minioadmin
S3_SECRET_ACCESS_KEY=minioadmin
S3_URL=http://minio:9000
EOL
echo "Environment configuration updated for ${HOSTNAME}"
echo "To run with Docker Compose, use: docker-compose up -d"

View File

@@ -69,7 +69,7 @@ const allowedOrigins = [
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

@@ -7,6 +7,10 @@ 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
RUN npm run build
FROM node:22-alpine AS production
@@ -16,5 +20,16 @@ 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 a script to start server and dynamically update env-config.js
RUN echo '#!/bin/sh\n\
# Update env-config.js with runtime environment variables\n\
cat > /app/build/env-config.js << EOL\n\
window.VITE_API_URL="${VITE_API_URL:-http://backend:3000}";\n\
EOL\n\
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,31 @@
/**
* Environment configuration
* Reads from window.VITE_API_URL (set by env-config.js)
* Falls back to import.meta.env.VITE_API_URL (set during build time)
* Falls back to a development default
*/
declare global {
interface Window {
VITE_API_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 default {
apiUrl: getApiUrl(),
};