Update environment configuration, Docker setup, and frontend/backend dependencies

- Updated .env.example and .env files for backend and frontend with placeholder values.
- Enhanced .gitignore to include additional files and directories.
- Modified docker-compose.yml to change image names and improve service health checks.
- Updated README.md and SETUP_THE_PROJECT.md for clearer setup instructions.
- Added database initialization scripts and SQL files for structured database setup.
- Updated frontend Dockerfile to use Node.js 22 and adjusted package.json scripts.
- Improved error handling and logging in start scripts for better debugging.
- Added reCAPTCHA support in the signup page with conditional loading based on environment variables.
This commit is contained in:
chamikaJ
2025-04-18 17:10:56 +05:30
parent 8825b0410a
commit e42819ef64
34 changed files with 948 additions and 376 deletions

View File

@@ -5,13 +5,14 @@ VITE_APP_TITLE=Worklenz
VITE_APP_ENV=development
# Mixpanel
VITE_MIXPANEL_TOKEN=bb330b6bd25db4a6c988da89046f4b80
VITE_MIXPANEL_TOKEN=mixpanel-token
# Recaptcha
VITE_RECAPTCHA_SITE_KEY=6LeUWjYqAAAAAFhi9Z8KPeiix3RRjxoZtJhLJZXb
VITE_ENABLE_RECAPTCHA=false
VITE_RECAPTCHA_SITE_KEY=recaptcha-site-key
# Session ID
VITE_WORKLENZ_SESSION_ID=worklenz.sid
VITE_WORKLENZ_SESSION_ID=worklenz-session-id
# Google Login
VITE_ENABLE_GOOGLE_LOGIN=true
VITE_ENABLE_GOOGLE_LOGIN=false

View File

@@ -1,4 +1,4 @@
FROM node:18-alpine AS build
FROM node:22-alpine AS build
WORKDIR /app
@@ -9,7 +9,7 @@ RUN npm ci
COPY . .
RUN npm run build
FROM node:18-alpine AS production
FROM node:22-alpine AS production
WORKDIR /app

View File

@@ -1,6 +1,6 @@
# Worklenz - React Application
# Worklenz - React Frontend
Worklenz is a task management application built with React and bundled using [Vite](https://vitejs.dev/).
Worklenz is a project management application built with React, TypeScript, and Ant Design. The project is bundled using [Vite](https://vitejs.dev/).
## Table of Contents
- [Getting Started](#getting-started)
@@ -15,11 +15,11 @@ To get started with the project, follow these steps:
1. **Clone the repository**:
```bash
git clone https://github.com/Worklenz/worklenz-v2.git
git clone https://github.com/Worklenz/worklenz.git
```
2. **Navigate to the project directory**:
```bash
cd worklenz-v2
cd worklenz/worklenz-frontend
```
3. **Install dependencies**:
```bash
@@ -29,7 +29,7 @@ To get started with the project, follow these steps:
```bash
npm run dev
```
5. Open [http://localhost:3000](http://localhost:3000) in your browser to view the application.
5. Open [http://localhost:5000](http://localhost:5000) in your browser to view the application.
## Available Scripts
@@ -38,7 +38,7 @@ In the project directory, you can run:
### `npm run dev`
Runs the app in the development mode.\
Open [http://localhost:5173](http://localhost:5173) to view it in the browser.
Open [http://localhost:5000](http://localhost:5000) to view it in the browser.
The page will reload if you make edits.\
You will also see any lint errors in the console.
@@ -58,7 +58,22 @@ Open [http://localhost:4173](http://localhost:4173) to preview the build.
## Project Structure
The project structure is organized as follows:
The project is organized around a feature-based structure:
```
src/
├── components/ # Reusable UI components
├── hooks/ # Custom React hooks
├── lib/ # Feature-specific logic
├── pages/ # Route components
├── services/ # API services
├── shared/ # Shared utilities, constants, and types
├── store/ # Global state management
├── types/ # TypeScript type definitions
├── utils/ # Utility functions
├── App.tsx # Main application component
└── main.tsx # Application entry point
```
## Contributing
@@ -72,6 +87,9 @@ Contributions are welcome! If you'd like to contribute, please follow these step
## Learn More
To learn more about Vite, check out the [Vite documentation](https://vitejs.dev/guide/).
To learn more about the technologies used in this project:
To learn React, check out the [React documentation](https://reactjs.org/).
- [React Documentation](https://react.dev/)
- [TypeScript Documentation](https://www.typescriptlang.org/docs/)
- [Ant Design Documentation](https://ant.design/docs/react/introduce)
- [Vite Documentation](https://vitejs.dev/guide/)

View File

@@ -5,7 +5,7 @@
"scripts": {
"start": "vite",
"prebuild": "node scripts/copy-tinymce.js",
"build": "node --max-old-space-size=4096 node_modules/.bin/vite build",
"build": "vite build",
"dev-build": "vite build",
"serve": "vite preview",
"format": "prettier --write ."

View File

@@ -8,7 +8,7 @@ type EmptyListPlaceholderProps = {
};
const EmptyListPlaceholder = ({
imageSrc = 'https://app.worklenz.com/assets/images/empty-box.webp',
imageSrc = '/assets/images/empty-box.webp',
imageHeight = 60,
text,
}: EmptyListPlaceholderProps) => {

View File

@@ -24,6 +24,16 @@ import logger from '@/utils/errorLogger';
import alertService from '@/services/alerts/alertService';
import { WORKLENZ_REDIRECT_PROJ_KEY } from '@/shared/constants';
// Define the global grecaptcha type
declare global {
interface Window {
grecaptcha?: {
ready: (callback: () => void) => void;
execute: (siteKey: string, options: { action: string }) => Promise<string>;
};
}
}
const SignupPage = () => {
const [form] = Form.useForm();
const navigate = useNavigate();
@@ -58,6 +68,7 @@ const SignupPage = () => {
};
const enableGoogleLogin = import.meta.env.VITE_ENABLE_GOOGLE_LOGIN === 'true' || false;
const enableRecaptcha = import.meta.env.VITE_ENABLE_RECAPTCHA === 'true' && import.meta.env.VITE_RECAPTCHA_SITE_KEY && import.meta.env.VITE_RECAPTCHA_SITE_KEY !== 'recaptcha-site-key';
useEffect(() => {
trackMixpanelEvent(evt_signup_page_visit);
@@ -79,26 +90,35 @@ const SignupPage = () => {
}, [trackMixpanelEvent]);
useEffect(() => {
const script = document.createElement('script');
script.src = `https://www.google.com/recaptcha/api.js?render=${import.meta.env.VITE_RECAPTCHA_SITE_KEY}`;
script.async = true;
script.defer = true;
document.body.appendChild(script);
return () => {
if (script && script.parentNode) {
script.parentNode.removeChild(script);
// Only load recaptcha script if recaptcha is enabled and site key is valid
if (enableRecaptcha && import.meta.env.VITE_RECAPTCHA_SITE_KEY) {
// Check if site key is not the placeholder value
if (import.meta.env.VITE_RECAPTCHA_SITE_KEY === 'recaptcha-site-key') {
console.warn('Using placeholder reCAPTCHA site key. Please set a valid key in your environment variables.');
return;
}
const recaptchaElements = document.getElementsByClassName('grecaptcha-badge');
while (recaptchaElements.length > 0) {
const element = recaptchaElements[0];
if (element.parentNode) {
element.parentNode.removeChild(element);
const script = document.createElement('script');
script.src = `https://www.google.com/recaptcha/api.js?render=${import.meta.env.VITE_RECAPTCHA_SITE_KEY}`;
script.async = true;
script.defer = true;
document.body.appendChild(script);
return () => {
if (script && script.parentNode) {
script.parentNode.removeChild(script);
}
}
};
}, []);
const recaptchaElements = document.getElementsByClassName('grecaptcha-badge');
while (recaptchaElements.length > 0) {
const element = recaptchaElements[0];
if (element.parentNode) {
element.parentNode.removeChild(element);
}
}
};
}
}, [enableRecaptcha]);
const getInvitationQueryParams = () => {
const params = [`team=${urlParams.teamId}`, `teamMember=${urlParams.teamMemberId}`];
@@ -109,33 +129,72 @@ const SignupPage = () => {
};
const getRecaptchaToken = async () => {
return new Promise<string>(resolve => {
window.grecaptcha?.ready(() => {
window.grecaptcha
?.execute(import.meta.env.VITE_RECAPTCHA_SITE_KEY, { action: 'signup' })
.then((token: string) => {
resolve(token);
});
if (!enableRecaptcha) return '';
// Check if site key is valid
if (!import.meta.env.VITE_RECAPTCHA_SITE_KEY || import.meta.env.VITE_RECAPTCHA_SITE_KEY === 'recaptcha-site-key') {
console.warn('Invalid reCAPTCHA site key. Skipping reCAPTCHA verification.');
return 'skip-verification';
}
try {
return new Promise<string>((resolve, reject) => {
if (!window.grecaptcha) {
reject('reCAPTCHA not loaded');
return;
}
window.grecaptcha.ready(() => {
window.grecaptcha!
.execute(import.meta.env.VITE_RECAPTCHA_SITE_KEY, { action: 'signup' })
.then((token: string) => {
resolve(token);
})
.catch((error: any) => {
console.error('reCAPTCHA execution error:', error);
reject(error);
});
});
});
});
} catch (error) {
console.error('Error getting reCAPTCHA token:', error);
return '';
}
};
const onFinish = async (values: IUserSignUpRequest) => {
try {
setValidating(true);
const token = await getRecaptchaToken();
if (enableRecaptcha) {
try {
const token = await getRecaptchaToken();
if (!token) {
logger.error('Failed to get reCAPTCHA token');
alertService.error(t('reCAPTCHAVerificationError'), t('reCAPTCHAVerificationErrorMessage'));
return;
}
if (!token) {
logger.error('Failed to get reCAPTCHA token');
alertService.error(t('reCAPTCHAVerificationError'), t('reCAPTCHAVerificationErrorMessage'));
return;
}
// Skip verification if we're using the special token due to invalid site key
if (token !== 'skip-verification') {
const verifyToken = await authApiService.verifyRecaptchaToken(token);
const veriftToken = await authApiService.verifyRecaptchaToken(token);
if (!veriftToken.done) {
logger.error('Failed to verify reCAPTCHA token');
return;
if (!verifyToken.done) {
logger.error('Failed to verify reCAPTCHA token');
return;
}
}
} catch (error) {
logger.error('reCAPTCHA error:', error);
// Continue with sign up even if reCAPTCHA fails in development
if (import.meta.env.DEV) {
console.warn('Continuing signup despite reCAPTCHA error in development mode');
} else {
alertService.error(t('reCAPTCHAVerificationError'), t('reCAPTCHAVerificationErrorMessage'));
return;
}
}
}
const body = {