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.
This commit is contained in:
chamiakJ
2025-04-28 11:32:44 +05:30
parent 9c27c41a5e
commit 2a3ae31e4e
9 changed files with 140 additions and 9 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

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

@@ -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,13 +20,13 @@ 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 inject environment variables
# Create a script to start server and dynamically update env-config.js
RUN echo '#!/bin/sh\n\
cat > /app/build/env.js << EOL\n\
window.env = {\n\
VITE_API_URL: "${VITE_API_URL}"\n\
};\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

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(),
};