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:
55
README.md
55
README.md
@@ -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.
|
||||
|
||||
|
||||
@@ -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
34
update-docker-env.sh
Executable 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"
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
|
||||
31
worklenz-frontend/src/config/env.ts
Normal file
31
worklenz-frontend/src/config/env.ts
Normal 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(),
|
||||
};
|
||||
Reference in New Issue
Block a user