UNPKG

dhis2-mcp-server

Version:

A Model Context Protocol server providing 108 tools for DHIS2 development, including code generators, debugging helpers, and documentation access for web and Android app development.

1,696 lines (1,457 loc) 65.8 kB
/** * DHIS2 Development Debugging and Troubleshooting Functions * Based on DHIS2 community issues and best practices */ export function diagnoseCORSIssues(args) { const { dhis2Instance, localDevelopmentUrl, browser, errorMessage = '', symptoms = [] } = args; const diagnosis = []; const solutions = []; // Analyze browser-specific issues if (browser === 'chrome') { diagnosis.push('Chrome has strict SameSite cookie policies since version 94+'); solutions.push('Use the --proxy flag when starting your DHIS2 app: `yarn start --proxy`'); solutions.push('Alternative: Start Chrome with disabled security for development: `google-chrome --disable-web-security --user-data-dir=/tmp/chrome-dev`'); } else if (browser === 'firefox') { diagnosis.push('Firefox recently changed SameSite cookie behavior to be more strict'); solutions.push('Temporarily modify Firefox settings:'); solutions.push(' 1. Open about:config'); solutions.push(' 2. Set `network.cookie.sameSite.laxByDefault` to `false`'); solutions.push(' 3. Set `network.cookie.sameSite.noneRequiresSecure` to `false`'); } // Analyze specific symptoms symptoms.forEach((symptom) => { switch (symptom) { case 'login_fails': diagnosis.push('Authentication requests are being blocked by CORS policy'); solutions.push('Add your development URL to DHIS2 CORS allowlist'); solutions.push('Check if DHIS2 instance supports cross-origin authentication'); break; case 'api_requests_blocked': diagnosis.push('API calls are failing due to cross-origin restrictions'); solutions.push('Configure proxy to route requests through same origin'); break; case 'cookies_not_sent': diagnosis.push('Browser is not sending cookies with cross-origin requests'); solutions.push('Use credentials: "include" in fetch requests'); solutions.push('Ensure SameSite cookie settings allow cross-origin'); break; case '302_errors': diagnosis.push('Login endpoint is redirecting, causing CORS preflight failure'); solutions.push('Use direct API authentication instead of login forms'); break; case 'preflight_failed': diagnosis.push('CORS preflight OPTIONS requests are failing'); solutions.push('Check DHIS2 server CORS configuration'); break; } }); // DHIS2 Play server specific issues if (dhis2Instance.includes('play.dhis2.org')) { diagnosis.push('DHIS2 Play instances use nginx with hardened security settings'); solutions.push('⚠️ DHIS2 Play instances prevent cross-site cookies completely'); solutions.push('Recommended: Use local DHIS2 instance for development'); solutions.push('Alternative: Run DHIS2 locally with Docker: `d2 cluster up`'); } return `# CORS Issues Diagnosis ## Configuration Details - **DHIS2 Instance**: ${dhis2Instance} - **Local Development**: ${localDevelopmentUrl} - **Browser**: ${browser.toUpperCase()} - **Error Message**: ${errorMessage} - **Symptoms**: ${symptoms.join(', ')} ## Diagnosis ${diagnosis.map(d => `- ${d}`).join('\n')} ## Recommended Solutions ### Immediate Solutions ${solutions.map((s, i) => `${i + 1}. ${s}`).join('\n')} ### Long-term Solutions 1. **Configure CORS Allowlist** - Login to your DHIS2 instance as admin - Go to Apps → System Settings → Access - Add your development URL to "CORS allowlist" 2. **Use Proxy Configuration** \`\`\`bash # Start with proxy (recommended) yarn start --proxy \`\`\` 3. **Local Development Instance** \`\`\`bash # Set up local DHIS2 instance npx @dhis2/cli cluster init d2 cluster up \`\`\` ## Testing CORS Configuration \`\`\`bash # Test CORS headers curl -H "Origin: ${localDevelopmentUrl}" \\ -H "Access-Control-Request-Method: GET" \\ -H "Access-Control-Request-Headers: X-Requested-With" \\ -X OPTIONS \\ ${dhis2Instance}/api/me # Expected response should include: # Access-Control-Allow-Origin: ${localDevelopmentUrl} # Access-Control-Allow-Credentials: true \`\`\` ## Browser Dev Tools Checklist 1. Open Network tab and check for: - OPTIONS requests (preflight) - Response headers with Access-Control-* - Cookie header presence in requests 2. Console errors related to: - "Cross-Origin Request Blocked" - "CORS policy" messages - SameSite warnings ## Alternative Development Approaches ${dhis2Instance.includes('play.dhis2.org') ? ` ⚠️ Since you're using DHIS2 Play, consider these alternatives: 1. **Local DHIS2 Instance (Recommended)** \`\`\`bash # Quick setup with Docker docker run -d -p 8080:8080 dhis2/core:2.40.4 \`\`\` 2. **Use DHIS2 CLI Cluster** \`\`\`bash npx @dhis2/cli cluster init my-cluster cd my-cluster d2 cluster up \`\`\` 3. **Request Dedicated Development Instance** - Contact your DHIS2 administrator - Request CORS configuration for development URLs ` : ''} ## Security Considerations ⚠️ **Development Only**: Never disable browser security in production ⚠️ **Temporary**: Revert Firefox settings after development ⚠️ **Credentials**: Use separate development credentials `; } export function generateCORSConfiguration(args) { const { allowedOrigins, dhis2Version = '2.40.4', includeSteps = true } = args; return `# DHIS2 CORS Configuration Guide ## System Settings Configuration ${includeSteps ? ` ### Step-by-Step Instructions 1. **Login to DHIS2** as a user with system administration privileges 2. **Navigate to System Settings** - Click on the Apps icon (grid icon) - Search for "System Settings" - Click on the System Settings app 3. **Configure CORS** - In the left sidebar, click "Access" - Scroll down to find "CORS allowlist" - Add your development URLs 4. **Save Changes** - Click "Save" at the bottom of the page - Wait for confirmation message ` : ''} ## CORS Allowlist Configuration ### URLs to Add ${allowedOrigins.map((url) => `- ${url}`).join('\n')} ### Configuration Format \`\`\` ${allowedOrigins.join('\n')} \`\`\` ## Advanced CORS Configuration (System Properties) For system administrators, you can also configure CORS via system properties: ### dhis.conf Configuration \`\`\`properties # CORS Configuration cors.allowedOrigins=${allowedOrigins.join(',')} cors.allowCredentials=true cors.allowedMethods=GET,POST,PUT,DELETE,OPTIONS,PATCH cors.allowedHeaders=Accept,Content-Type,Origin,X-Requested-With,Authorization cors.maxAge=3600 \`\`\` ## Environment-Specific Configurations ### Development Environment \`\`\` # Local development http://localhost:3000 http://localhost:3001 http://127.0.0.1:3000 # Common development ports http://localhost:8080 http://localhost:9000 \`\`\` ### Staging Environment \`\`\` https://staging-app.yourdomain.com https://test-app.yourdomain.com \`\`\` ### Production Environment \`\`\` https://app.yourdomain.com https://health-dashboard.yourdomain.com \`\`\` ## Validation Commands ### Test CORS Configuration \`\`\`bash # Test basic CORS curl -H "Origin: ${allowedOrigins[0]}" \\ ${dhis2Version ? `https://your-dhis2-instance.com/api/system/info` : 'https://your-dhis2-instance.com/api/system/info'} # Test with authentication curl -H "Origin: ${allowedOrigins[0]}" \\ -H "Authorization: Basic $(echo -n 'username:password' | base64)" \\ https://your-dhis2-instance.com/api/me # Test preflight request curl -H "Origin: ${allowedOrigins[0]}" \\ -H "Access-Control-Request-Method: POST" \\ -H "Access-Control-Request-Headers: Content-Type" \\ -X OPTIONS \\ https://your-dhis2-instance.com/api/dataElements \`\`\` ### Expected Response Headers \`\`\` Access-Control-Allow-Origin: ${allowedOrigins[0]} Access-Control-Allow-Credentials: true Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS, PATCH Access-Control-Allow-Headers: Accept, Content-Type, Origin, X-Requested-With, Authorization Access-Control-Max-Age: 3600 \`\`\` ## Troubleshooting Common Issues ### Issue: "CORS allowlist not found" **Solution**: Update to DHIS2 2.35+ (older versions use different settings) ### Issue: "Changes not taking effect" **Solutions**: 1. Clear browser cache completely 2. Restart DHIS2 server (if self-hosted) 3. Wait 5-10 minutes for changes to propagate 4. Check if nginx/reverse proxy needs updating ### Issue: "Still getting CORS errors" **Checklist**: - [ ] URLs match exactly (including protocol) - [ ] No trailing slashes mismatch - [ ] Case sensitivity check - [ ] Wildcard not used (DHIS2 doesn't support wildcards) - [ ] Browser cache cleared ## Security Best Practices ### Development - Only add localhost URLs for development - Use specific ports, not wildcards - Remove development URLs before production ### Production - Only add your production domain(s) - Use HTTPS URLs only - Regularly audit allowed origins - Document all entries with purpose ### Monitoring \`\`\`bash # Check current CORS settings via API curl -u admin:password \\ https://your-dhis2-instance.com/api/systemSettings/keyJsCorallowlist \`\`\` ## Version-Specific Notes ${dhis2Version >= '2.38' ? ` ### DHIS2 ${dhis2Version}+ - Full CORS support available - GUI configuration available - API endpoint for configuration ` : ` ### DHIS2 ${dhis2Version} - Limited CORS support - May require manual configuration - Check documentation for version-specific settings `} `; } export function debugAuthentication(args) { const { issueType, dhis2Instance, authMethod, errorDetails = {}, browserSettings = {} } = args; const diagnosis = []; const solutions = []; // Analyze authentication issue type switch (issueType) { case 'login_failure': diagnosis.push('Authentication credentials may be invalid or endpoint unreachable'); solutions.push('Verify username and password are correct'); solutions.push('Test credentials directly in DHIS2 web interface'); solutions.push('Check if account is locked or disabled'); break; case 'session_timeout': diagnosis.push('User session has expired or is not being maintained'); solutions.push('Implement session refresh mechanism'); solutions.push('Check session timeout settings in DHIS2'); solutions.push('Ensure cookies are being sent with requests'); break; case 'cookie_issues': diagnosis.push('Browser cookie handling issues affecting authentication'); solutions.push('Check SameSite cookie attributes'); solutions.push('Ensure cookies are enabled in browser'); solutions.push('Verify domain/path settings for cookies'); break; case 'token_problems': diagnosis.push('Authentication token is invalid, expired, or malformed'); solutions.push('Check token format and expiration'); solutions.push('Implement token refresh logic'); solutions.push('Verify token is included in Authorization header'); break; case 'proxy_auth': diagnosis.push('Proxy server authentication issues'); solutions.push('Check proxy credentials and configuration'); solutions.push('Verify proxy target URL is correct'); solutions.push('Ensure proxy is handling auth headers properly'); break; } // Analyze HTTP status codes if (errorDetails.httpStatus) { switch (errorDetails.httpStatus) { case 401: diagnosis.push('HTTP 401: Unauthorized - Invalid credentials'); solutions.push('Double-check username and password'); solutions.push('Ensure Authorization header is properly formatted'); break; case 403: diagnosis.push('HTTP 403: Forbidden - Valid credentials but insufficient permissions'); solutions.push('Check user authorities/permissions in DHIS2'); solutions.push('Verify user is assigned to appropriate user groups'); break; case 302: diagnosis.push('HTTP 302: Redirect - Authentication endpoint is redirecting'); solutions.push('Follow redirect chain to find final login endpoint'); solutions.push('Use direct API authentication instead of login forms'); break; case 404: diagnosis.push('HTTP 404: Not Found - Authentication endpoint does not exist'); solutions.push('Verify DHIS2 instance URL is correct'); solutions.push('Check if DHIS2 version supports the authentication method'); break; case 500: diagnosis.push('HTTP 500: Server Error - DHIS2 server experiencing issues'); solutions.push('Check DHIS2 server logs for detailed error information'); solutions.push('Verify database connectivity and server health'); break; } } return `# Authentication Debug Report ## Issue Details - **Issue Type**: ${issueType.replace(/_/g, ' ').toUpperCase()} - **DHIS2 Instance**: ${dhis2Instance} - **Auth Method**: ${authMethod.toUpperCase()} - **HTTP Status**: ${errorDetails.httpStatus || 'Not provided'} - **Error Message**: ${errorDetails.errorMessage || 'Not provided'} ## Diagnosis ${diagnosis.map(d => `- ${d}`).join('\n')} ## Recommended Solutions ${solutions.map((s, i) => `${i + 1}. ${s}`).join('\n')} ## Authentication Method Testing ${generateAuthMethodTests(authMethod, dhis2Instance)} ## Browser Settings Analysis ${browserSettings.cookiesEnabled !== undefined ? `- **Cookies Enabled**: ${browserSettings.cookiesEnabled ? 'Yes' : 'No'}` : ''} ${browserSettings.thirdPartyCookies !== undefined ? `- **Third-party Cookies**: ${browserSettings.thirdPartyCookies ? 'Enabled' : 'Disabled'}` : ''} ${browserSettings.sameSiteSettings ? `- **SameSite Settings**: ${browserSettings.sameSiteSettings}` : ''} ## Debug Commands ### Test Authentication Endpoint \`\`\`bash # Test basic authentication curl -X POST \\ -H "Content-Type: application/json" \\ -d '{"username":"your-username","password":"your-password"}' \\ ${dhis2Instance}/api/auth/login # Test current user endpoint curl -H "Authorization: Basic $(echo -n 'username:password' | base64)" \\ ${dhis2Instance}/api/me \`\`\` ### Network Analysis \`\`\`javascript // Browser console - check authentication headers fetch('${dhis2Instance}/api/me', { method: 'GET', headers: { 'Authorization': 'Basic ' + btoa('username:password') }, credentials: 'include' }).then(response => { console.log('Status:', response.status); console.log('Headers:', [...response.headers.entries()]); return response.json(); }).then(data => console.log('User data:', data)) .catch(err => console.error('Error:', err)); \`\`\` ## Browser Dev Tools Checklist 1. **Network Tab**: - [ ] Authentication request sent - [ ] Correct Authorization header present - [ ] Response status code - [ ] Set-Cookie headers in response 2. **Application Tab**: - [ ] Cookies stored correctly - [ ] Local/Session storage items - [ ] Cookie domain and path settings 3. **Console**: - [ ] No CORS errors - [ ] No cookie warnings - [ ] No security policy violations ## Common Authentication Patterns ### Basic Authentication \`\`\`javascript const credentials = btoa(\`\${username}:\${password}\`); fetch('/api/me', { headers: { 'Authorization': \`Basic \${credentials}\` } }); \`\`\` ### Cookie-based Authentication \`\`\`javascript // Login fetch('/dhis-web-commons-security/login.action', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: \`j_username=\${username}&j_password=\${password}\`, credentials: 'include' }); // Subsequent requests fetch('/api/me', { credentials: 'include' }); \`\`\` ## Environment-Specific Issues ### Development Environment - CORS configuration - Proxy authentication - Self-signed certificates - Local network restrictions ### Production Environment - Load balancer configuration - SSL/TLS certificate issues - Firewall restrictions - Session persistence ## Session Management Best Practices 1. **Session Timeout**: Configure appropriate timeout values 2. **Session Refresh**: Implement automatic session renewal 3. **Secure Storage**: Store credentials/tokens securely 4. **Logout Cleanup**: Clear all auth data on logout ## Security Considerations ⚠️ **Never log credentials in production** ⚠️ **Use HTTPS for authentication in production** ⚠️ **Implement proper session management** ⚠️ **Regular security audits of authentication flow** `; } function generateAuthMethodTests(authMethod, dhis2Instance) { switch (authMethod) { case 'basic': return `### Basic Authentication Test \`\`\`bash # Test basic auth credentials curl -v -u username:password ${dhis2Instance}/api/me # Check response headers curl -I -u username:password ${dhis2Instance}/api/me \`\`\``; case 'oauth2': return `### OAuth2 Authentication Test \`\`\`bash # Step 1: Get authorization code open "${dhis2Instance}/uaa/oauth/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI" # Step 2: Exchange code for token curl -X POST \\ -H "Content-Type: application/x-www-form-urlencoded" \\ -d "grant_type=authorization_code&code=AUTH_CODE&redirect_uri=YOUR_REDIRECT_URI" \\ -u "CLIENT_ID:CLIENT_SECRET" \\ ${dhis2Instance}/uaa/oauth/token \`\`\``; case 'cookie': return `### Cookie-based Authentication Test \`\`\`bash # Test cookie authentication curl -c cookies.txt -b cookies.txt \\ -d "j_username=username&j_password=password" \\ -X POST \\ ${dhis2Instance}/dhis-web-commons-security/login.action # Test with saved cookies curl -b cookies.txt ${dhis2Instance}/api/me \`\`\``; case 'token': return `### Token-based Authentication Test \`\`\`bash # Get token TOKEN=$(curl -s -X POST \\ -H "Content-Type: application/json" \\ -d '{"username":"your-username","password":"your-password"}' \\ ${dhis2Instance}/api/auth/token | jq -r '.token') # Use token curl -H "Authorization: Bearer $TOKEN" ${dhis2Instance}/api/me \`\`\``; default: return '### Custom Authentication Test\n```bash\n# Add your authentication test commands here\n```'; } } export function generateProxyConfiguration(args) { const { proxyType, targetInstance, localPort = 3000, authentication, sslOptions = {} } = args; return `# Proxy Configuration for DHIS2 Development ## ${proxyType.replace(/_/g, ' ').toUpperCase()} Configuration ${generateProxyConfig(proxyType, targetInstance, localPort, authentication, sslOptions)} ## Testing Proxy Configuration ### Test Proxy Connection \`\`\`bash # Test direct connection to DHIS2 curl ${targetInstance}/api/me # Test through proxy curl http://localhost:${localPort}/api/me \`\`\` ### Browser Testing 1. Start your application with proxy enabled 2. Open browser dev tools Network tab 3. Navigate to your application 4. Check that API requests go to localhost:${localPort} 5. Verify requests are forwarded to ${targetInstance} ## Common Proxy Issues and Solutions ### Issue: 502 Bad Gateway **Causes**: - Target DHIS2 instance is unreachable - Authentication credentials are incorrect - SSL certificate verification failing **Solutions**: - Verify target instance URL: ${targetInstance} - Check authentication credentials - ${sslOptions.secure === false ? 'SSL verification is disabled (good for development)' : 'Consider disabling SSL verification for development'} ### Issue: Authentication Not Working **Solutions**: - Ensure credentials are correct: ${authentication?.username || '[username]'} - Check if user account is locked - Verify proxy is forwarding auth headers ### Issue: CORS Still Occurring **Solutions**: - Restart development server after proxy configuration - Clear browser cache completely - Check proxy configuration syntax ## Security Considerations ### Development Environment ✅ **Safe for development**: - Using proxy to avoid CORS issues - Credentials stored in configuration files ${sslOptions.secure === false ? '- SSL verification disabled for development certificates' : ''} ⚠️ **Security reminders**: - Never commit credentials to version control - Use separate development credentials - Enable SSL verification in production ### Production Environment ❌ **Not suitable for production**: - This proxy configuration is for development only - Production should use proper authentication flows - Enable all security features for production deployment ## Environment Variables Create a \`.env.local\` file: \`\`\`bash # Proxy configuration DHIS2_PROXY_TARGET=${targetInstance} DHIS2_PROXY_USERNAME=${authentication?.username || 'your-username'} DHIS2_PROXY_PASSWORD=${authentication?.password || 'your-password'} PROXY_PORT=${localPort} \`\`\` ## Alternative Solutions ### 1. DHIS2 CLI Proxy \`\`\`bash # Use built-in DHIS2 CLI proxy yarn start --proxy \`\`\` ### 2. Local DHIS2 Instance \`\`\`bash # Set up local development instance npx @dhis2/cli cluster init d2 cluster up \`\`\` ### 3. Browser Extension - Use CORS browser extensions for development - ⚠️ Remember to disable in production `; } function generateProxyConfig(proxyType, targetInstance, localPort, authentication, sslOptions) { switch (proxyType) { case 'webpack_dev_server': return `### Webpack Dev Server Configuration \`\`\`javascript // webpack.config.js module.exports = { devServer: { port: ${localPort}, proxy: { '/api': { target: '${targetInstance}', changeOrigin: true, secure: ${sslOptions.secure !== false}, ${authentication ? `auth: '${authentication.username}:${authentication.password}',` : ''} headers: { 'Authorization': 'Basic ' + Buffer.from('${authentication?.username || 'username'}:${authentication?.password || 'password'}').toString('base64') } } } } }; \`\`\``; case 'create_react_app': return `### Create React App Configuration #### Option 1: package.json proxy field \`\`\`json { "name": "my-dhis2-app", "proxy": "${targetInstance}", "scripts": { "start": "react-scripts start" } } \`\`\` #### Option 2: setupProxy.js (for advanced configuration) \`\`\`javascript // src/setupProxy.js const { createProxyMiddleware } = require('http-proxy-middleware'); module.exports = function(app) { app.use( '/api', createProxyMiddleware({ target: '${targetInstance}', changeOrigin: true, secure: ${sslOptions.secure !== false}, ${authentication ? `auth: '${authentication.username}:${authentication.password}',` : ''} headers: { 'Authorization': 'Basic ' + Buffer.from('${authentication?.username || 'username'}:${authentication?.password || 'password'}').toString('base64') } }) ); }; \`\`\` #### Start with proxy \`\`\`bash npm start \`\`\``; case 'vite': return `### Vite Configuration \`\`\`javascript // vite.config.js import { defineConfig } from 'vite'; export default defineConfig({ server: { port: ${localPort}, proxy: { '/api': { target: '${targetInstance}', changeOrigin: true, secure: ${sslOptions.secure !== false}, configure: (proxy, options) => { proxy.on('proxyReq', (proxyReq, req, res) => { const auth = Buffer.from('${authentication?.username || 'username'}:${authentication?.password || 'password'}').toString('base64'); proxyReq.setHeader('Authorization', \`Basic \${auth}\`); }); } } } } }); \`\`\` #### Start development server \`\`\`bash npm run dev \`\`\``; case 'custom_express': return `### Custom Express Proxy Server \`\`\`javascript // proxy-server.js const express = require('express'); const { createProxyMiddleware } = require('http-proxy-middleware'); const app = express(); const proxyOptions = { target: '${targetInstance}', changeOrigin: true, secure: ${sslOptions.secure !== false}, ${authentication ? `auth: '${authentication.username}:${authentication.password}',` : ''} pathRewrite: { '^/proxy': '', // Remove /proxy prefix }, onProxyReq: (proxyReq, req, res) => { const auth = Buffer.from('${authentication?.username || 'username'}:${authentication?.password || 'password'}').toString('base64'); proxyReq.setHeader('Authorization', \`Basic \${auth}\`); }, logLevel: 'debug' }; app.use('/api', createProxyMiddleware(proxyOptions)); // Serve static files app.use(express.static('public')); app.listen(${localPort}, () => { console.log(\`Proxy server running on http://localhost:${localPort}\`); console.log(\`Forwarding to: ${targetInstance}\`); }); \`\`\` #### Start proxy server \`\`\`bash node proxy-server.js \`\`\``; default: return '### Custom Proxy Configuration\n```javascript\n// Add your proxy configuration here\n```'; } } export function resolveBuildIssues(args) { const { buildTool, issueType, errorMessage = '', packageManager = 'npm', nodeVersion, dependencies = {} } = args; const diagnosis = []; const solutions = []; // Analyze build tool specific issues if (buildTool === 'd2_cli_deprecated') { diagnosis.push('⚠️ The d2 CLI library has been deprecated'); solutions.push('Migrate to @dhis2/app-platform for modern DHIS2 development'); solutions.push('Use the migration assistant: `dhis2_migration_assistant` tool'); solutions.push('Reference: https://developers.dhis2.org/docs/app-platform/getting-started'); } // Analyze specific issue types switch (issueType) { case 'dependency_conflicts': diagnosis.push('Conflicting package versions or peer dependency issues'); solutions.push('Clear node_modules and package lock file'); solutions.push(`rm -rf node_modules ${packageManager === 'yarn' ? 'yarn.lock' : 'package-lock.json'}`); solutions.push(`${packageManager} install`); if (packageManager === 'yarn' && dependencies.dhis2Cli) { solutions.push('Ensure using yarn for DHIS2 projects (yarn.lock included)'); } break; case 'module_resolution': diagnosis.push('Module resolution failing - TypeScript or import path issues'); solutions.push('Check tsconfig.json paths configuration'); solutions.push('Verify all imports use correct file extensions'); solutions.push('Ensure moduleResolution is set to "node" in tsconfig.json'); break; case 'bundle_size': diagnosis.push('Bundle size exceeding limits or performance issues'); solutions.push('Implement code splitting with React.lazy()'); solutions.push('Use tree shaking to eliminate dead code'); solutions.push('Analyze bundle with: npm run build -- --analyze'); break; case 'tree_shaking': diagnosis.push('Dead code elimination not working properly'); solutions.push('Ensure using ES6 modules (import/export)'); solutions.push('Check sideEffects configuration in package.json'); solutions.push('Upgrade to latest @dhis2/cli-app-scripts version'); break; case 'compilation_errors': diagnosis.push('TypeScript or JavaScript compilation failing'); solutions.push('Check for TypeScript version compatibility'); solutions.push('Verify all types are properly imported'); solutions.push('Run type check separately: npm run type-check'); break; case 'memory_issues': diagnosis.push('Build process running out of memory'); solutions.push('Increase Node.js memory limit: --max-old-space-size=4096'); solutions.push('Use incremental builds when possible'); solutions.push('Consider splitting large applications'); break; } // Node.js version specific issues if (nodeVersion) { const majorVersion = parseInt(nodeVersion.split('.')[0]); if (majorVersion < 16) { diagnosis.push(`Node.js version ${nodeVersion} may be too old for modern DHIS2 development`); solutions.push('Upgrade to Node.js 16+ (LTS recommended)'); solutions.push('Use Node Version Manager (nvm) for easy version switching'); } else if (majorVersion >= 18) { diagnosis.push('Node.js 18+ may have stricter module resolution'); solutions.push('Ensure all dependencies support Node.js ' + majorVersion); } } return `# Build Issues Resolution ## Issue Analysis - **Build Tool**: ${buildTool.replace(/_/g, ' ').toUpperCase()} - **Issue Type**: ${issueType.replace(/_/g, ' ').toUpperCase()} - **Package Manager**: ${packageManager.toUpperCase()} - **Node Version**: ${nodeVersion || 'Not specified'} - **Error**: ${errorMessage} ## Diagnosis ${diagnosis.map(d => `- ${d}`).join('\n')} ## Immediate Solutions ${solutions.map((s, i) => `${i + 1}. ${s}`).join('\n')} ## Build Tool Specific Fixes ${generateBuildToolFixes(buildTool, dependencies)} ## Version Compatibility Check ### Current Dependencies ${Object.entries(dependencies).map(([key, version]) => `- **${key}**: ${version}`).join('\n')} ### Recommended Versions - **@dhis2/cli-app-scripts**: ^10.3.0+ - **@dhis2/app-platform**: ^11.0.0+ - **Node.js**: 16.x or 18.x (LTS) - **npm**: 8.x+ - **yarn**: 1.22.x+ ## Generic Build Troubleshooting ### Clear Build Artifacts \`\`\`bash # Clear all caches and artifacts rm -rf node_modules rm -rf dist rm -rf build rm -rf .cache ${packageManager === 'yarn' ? 'rm yarn.lock' : 'rm package-lock.json'} # Fresh install ${packageManager} install \`\`\` ### Memory Issues \`\`\`bash # Increase memory limit export NODE_OPTIONS="--max-old-space-size=4096" npm run build # Alternative: Set in package.json scripts "build": "NODE_OPTIONS='--max-old-space-size=4096' react-scripts build" \`\`\` ### Debug Build Process \`\`\`bash # Verbose build output npm run build -- --verbose # TypeScript compilation check npx tsc --noEmit # ESLint check npx eslint src/ \`\`\` ## Performance Optimization ### Bundle Analysis \`\`\`bash # Analyze bundle size npm run build -- --analyze # Alternative tools npx webpack-bundle-analyzer build/static/js/*.js npx source-map-explorer build/static/js/*.js \`\`\` ### Code Splitting Example \`\`\`javascript // Implement lazy loading const LazyComponent = React.lazy(() => import('./LazyComponent')); function App() { return ( <Suspense fallback={<div>Loading...</div>}> <LazyComponent /> </Suspense> ); } \`\`\` ### Tree Shaking Configuration \`\`\`json // package.json { "sideEffects": false, "main": "lib/index.js", "module": "es/index.js" } \`\`\` ## Environment-Specific Issues ### Development Build Issues - Hot reload not working - Slow compilation times - Memory leaks in watch mode ### Production Build Issues - Minification errors - Missing environment variables - Asset path issues ## Migration Recommendations ${buildTool === 'd2_cli_deprecated' ? ` ### Migrate from d2 CLI to App Platform 1. **Install App Platform** \`\`\`bash npm install --save-dev @dhis2/cli-app-scripts \`\`\` 2. **Update package.json scripts** \`\`\`json { "scripts": { "start": "d2-app-scripts start", "build": "d2-app-scripts build", "test": "d2-app-scripts test", "deploy": "d2-app-scripts deploy" } } \`\`\` 3. **Create d2.config.js** \`\`\`javascript const config = { type: 'app', name: 'my-app', title: 'My DHIS2 App', description: 'A DHIS2 application' }; module.exports = config; \`\`\` 4. **Update imports** - Replace d2 library imports - Use @dhis2/app-runtime for API calls - Update component imports to @dhis2/ui ` : ''} ## Support Resources - **DHIS2 Developer Portal**: https://developers.dhis2.org - **Community Forum**: https://community.dhis2.org/c/development - **GitHub Issues**: https://github.com/dhis2/app-platform/issues - **Documentation**: https://docs.dhis2.org ## Automated Diagnostics \`\`\`bash # Run comprehensive diagnostics npx @dhis2/cli-app-scripts doctor # Check environment setup npx envinfo --binaries --languages --utilities \`\`\` `; } function generateBuildToolFixes(buildTool, dependencies) { switch (buildTool) { case 'app_platform': return `### DHIS2 App Platform Fixes #### Update to Latest Version \`\`\`bash npm install --save-dev @dhis2/cli-app-scripts@latest \`\`\` #### Configuration Check \`\`\`javascript // d2.config.js validation const config = { type: 'app', name: 'my-app', // Must be valid package name title: 'My App', description: 'App description', // Optional: Custom entry points entryPoints: { app: './src/App.js' } }; module.exports = config; \`\`\` #### Common App Platform Issues 1. **Missing d2.config.js**: App Platform requires this configuration file 2. **Invalid app name**: Must follow npm package naming rules 3. **Missing manifest.webapp**: Required for DHIS2 app installation`; case 'webpack': return `### Webpack Configuration Fixes #### Update Webpack \`\`\`bash npm install --save-dev webpack@latest webpack-cli@latest \`\`\` #### Basic webpack.config.js \`\`\`javascript const path = require('path'); module.exports = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' }, module: { rules: [ { test: /\\.jsx?$/, exclude: /node_modules/, use: 'babel-loader' } ] } }; \`\`\``; case 'vite': return `### Vite Configuration Fixes #### Update Vite \`\`\`bash npm install --save-dev vite@latest @vitejs/plugin-react@latest \`\`\` #### vite.config.js \`\`\`javascript import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], build: { outDir: 'build' } }); \`\`\``; default: return '### Custom Build Tool Fixes\n```bash\n# Add specific fixes for your build tool\n```'; } } export function generatePerformanceOptimizations(args) { const { performanceIssue, metrics = {}, targetMetrics = {}, appComplexity = 'moderate' } = args; return `# DHIS2 App Performance Optimization ## Current Performance Analysis - **Issue Type**: ${performanceIssue.replace(/_/g, ' ').toUpperCase()} - **App Complexity**: ${appComplexity.toUpperCase()} ### Current Metrics ${Object.entries(metrics).map(([key, value]) => `- **${key.replace(/_/g, ' ').toUpperCase()}**: ${value}${getMetricUnit(key)}`).join('\n')} ### Target Metrics ${Object.entries(targetMetrics).map(([key, value]) => `- **${key.replace(/target_/g, '').replace(/_/g, ' ').toUpperCase()}**: ${value}${getMetricUnit(key.replace('target_', ''))}`).join('\n')} ## Optimization Strategy ${generateOptimizationStrategy(performanceIssue, metrics, appComplexity)} ## Implementation Guide ${generateImplementationGuide(performanceIssue)} ## Monitoring and Measurement ### Performance Testing Setup \`\`\`javascript // Performance measurement utility const measurePerformance = (name, fn) => { const start = performance.now(); const result = fn(); const end = performance.now(); console.log(\`\${name} took \${end - start} milliseconds\`); return result; }; // Usage example measurePerformance('Data Element Load', () => { // Load data elements }); \`\`\` ### Browser Performance API \`\`\`javascript // Track Core Web Vitals import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals'; getCLS(console.log); getFID(console.log); getFCP(console.log); getLCP(console.log); getTTFB(console.log); \`\`\` ## Performance Budget ${generatePerformanceBudget(appComplexity)} ## DHIS2-Specific Optimizations ${generateDHIS2Optimizations()} ## Monitoring Dashboard \`\`\`javascript // Simple performance dashboard component const PerformanceDashboard = () => { const [metrics, setMetrics] = React.useState({}); React.useEffect(() => { // Collect performance metrics const observer = new PerformanceObserver((list) => { const entries = list.getEntries(); setMetrics(prev => ({ ...prev, ...entries.reduce((acc, entry) => ({ ...acc, [entry.name]: entry.duration }), {}) })); }); observer.observe({ entryTypes: ['measure'] }); return () => observer.disconnect(); }, []); return ( <div> <h3>Performance Metrics</h3> {Object.entries(metrics).map(([name, value]) => ( <p key={name}>{name}: {value.toFixed(2)}ms</p> ))} </div> ); }; \`\`\` `; } function getMetricUnit(metric) { switch (metric) { case 'loadTime': case 'apiResponseTime': return 'ms'; case 'bundleSize': return 'KB'; case 'memoryUsage': return 'MB'; default: return ''; } } function generateOptimizationStrategy(issue, metrics, complexity) { switch (issue) { case 'slow_loading': return `### Slow Loading Optimization Strategy #### Immediate Actions (Quick Wins) 1. **Code Splitting**: Implement lazy loading for routes and components 2. **Bundle Analysis**: Identify and remove unnecessary dependencies 3. **Image Optimization**: Compress and lazy load images 4. **Preloading**: Preload critical resources #### Medium-term Actions 1. **Service Worker**: Implement caching strategies 2. **CDN**: Use CDN for static assets 3. **Database Optimization**: Optimize DHIS2 queries 4. **Component Optimization**: Memoize expensive components #### Long-term Actions 1. **Architecture Review**: Consider micro-frontends for complex apps 2. **Progressive Enhancement**: Implement progressive loading 3. **Performance Monitoring**: Set up continuous monitoring`; case 'memory_leaks': return `### Memory Leak Resolution Strategy #### Detection 1. **Chrome DevTools**: Use Memory tab to identify leaks 2. **Component Profiling**: Use React Profiler 3. **Event Listener Audit**: Check for unremoved listeners #### Common React Memory Leaks 1. **Unmounted Components**: Clean up subscriptions in useEffect 2. **Event Listeners**: Remove listeners in cleanup functions 3. **Timers**: Clear intervals and timeouts 4. **DOM References**: Avoid storing DOM nodes in state #### DHIS2-Specific Leaks 1. **Data Query Subscriptions**: Properly cleanup useDataQuery 2. **WebSocket Connections**: Close connections on unmount 3. **Large Datasets**: Implement virtual scrolling`; case 'api_bottlenecks': return `### API Performance Optimization #### Request Optimization 1. **Batch Requests**: Combine multiple API calls 2. **Pagination**: Implement proper pagination 3. **Field Selection**: Only request needed fields 4. **Caching**: Implement request caching #### DHIS2 API Optimization 1. **Query Parameters**: Use optimal filter and field parameters 2. **Bulk Operations**: Use bulk endpoints for mass operations 3. **Analytics Optimization**: Optimize analytics queries 4. **Metadata Caching**: Cache metadata locally`; default: return '### Generic Performance Strategy\n- Identify bottlenecks\n- Implement optimizations\n- Monitor improvements'; } } function generateImplementationGuide(issue) { switch (issue) { case 'slow_loading': return `### Code Splitting Implementation \`\`\`javascript // Route-based code splitting import { lazy, Suspense } from 'react'; const DataEntry = lazy(() => import('./pages/DataEntry')); const Reports = lazy(() => import('./pages/Reports')); function App() { return ( <Suspense fallback={<div>Loading...</div>}> <Routes> <Route path="/data-entry" element={<DataEntry />} /> <Route path="/reports" element={<Reports />} /> </Routes> </Suspense> ); } \`\`\` ### Bundle Optimization \`\`\`javascript // webpack.config.js optimization module.exports = { optimization: { splitChunks: { chunks: 'all', cacheGroups: { vendor: { test: /[\\\\/]node_modules[\\\\/]/, name: 'vendors', chunks: 'all', }, dhis2: { test: /[\\\\/]node_modules[\\\\/]@dhis2[\\\\/]/, name: 'dhis2', chunks: 'all', }, }, }, }, }; \`\`\``; case 'memory_leaks': return `### Memory Leak Prevention \`\`\`javascript // Proper cleanup in React hooks function MyComponent() { React.useEffect(() => { const subscription = dataService.subscribe(handleData); const interval = setInterval(updateData, 1000); return () => { subscription.unsubscribe(); clearInterval(interval); }; }, []); const handleData = React.useCallback((data) => { // Handle data }, []); return <div>Component content</div>; } \`\`\` ### DHIS2 Data Query Cleanup \`\`\`javascript // Proper useDataQuery usage function DataComponent() { const { loading, error, data, refetch } = useDataQuery(query); React.useEffect(() => { return () => { // Cleanup is handled automatically by useDataQuery // but you can add custom cleanup here if needed }; }, []); return loading ? <Loading /> : <DataDisplay data={data} />; } \`\`\``; default: return '### Implementation Guide\n```javascript\n// Add specific implementation details\n```'; } } function generatePerformanceBudget(complexity) { let budgets; switch (complexity) { case 'simple': budgets = { 'Bundle Size': '< 200KB', 'Load Time': '< 2 seconds', 'API Response': '< 500ms', 'Memory Usage': '< 50MB' }; break; case 'moderate': budgets = { 'Bundle Size': '< 500KB', 'Load Time': '< 3 seconds', 'API Response': '< 1 second', 'Memory Usage': '< 100MB' }; break; case 'complex': budgets = { 'Bundle Size': '< 1MB', 'Load Time': '< 5 seconds', 'API Response': '< 2 seconds', 'Memory Usage': '< 200MB' }; break; default: budgets = { 'Bundle Size': '< 500KB', 'Load Time': '< 3 seconds', 'API Response': '< 1 second', 'Memory Usage': '< 100MB' }; } return `### Performance Budget for ${complexity.toUpperCase()} App ${Object.entries(budgets).map(([metric, target]) => `- **${metric}**: ${target}`).join('\n')} ### Budget Configuration (webpack-bundle-analyzer) \`\`\`json { "budgets": [ { "type": "bundle", "name": "main", "maximumError": "${budgets['Bundle Size'].replace('< ', '').replace('KB', '').replace('MB', '000')}kb" } ] } \`\`\``; } function generateDHIS2Optimizations() { return `### API Request Optimization \`\`\`javascript // Optimize DHIS2 API requests const optimizedQuery = { dataElements: { resource: 'dataElements', params: { fields: 'id,displayName,valueType', // Only request needed fields filter: 'domainType:eq:AGGREGATE', // Filter server-side pageSize: 50, // Reasonable page size paging: true // Enable pagination } } }; \`\`\` ### Metadata Caching \`\`\`javascript // Cache metadata for performance const MetadataCache = { cache: new Map(), get(key) { const cached = this.cache.get(key); if (cached && Date.now() - cached.timestamp < 5 * 60 * 1000) { // 5 minutes return cached.data; } return null; }, set(key, data) { this.cache.set(key, { data, timestamp: Date.now() }); } }; \`\`\` ### Virtual Scrolling for Large Lists \`\`\`javascript // Use react-window for large datasets import { VariableSizeList } from 'react-window'; const VirtualizedDataElementList = ({ dataElements }) => { const getItemSize = (index) => 60; // Height of each item const renderItem = ({ index, style }) => ( <div style={style}> {dataElements[index].displayName} </div> ); return ( <VariableSizeList height={400} itemCount={dataElements.length} itemSize={getItemSize} itemData={dataElements} > {renderItem} </VariableSizeList> ); }; \`\`\` ### Debounced Search \`\`\`javascript // Debounce search requests import { useDebounce } from 'use-debounce'; const SearchableList = () => { const [searchTerm, setSearchTerm] = useState(''); const [debouncedSearchTerm] = useDebounce(searchTerm, 300); const query = { dataElements: { resource: 'dataElements', params: { filter: debouncedSearchTerm ? \`name:ilike:\${debouncedSearchTerm}\` : undefined } } }; const { data } = useDataQuery(query, { enabled: debouncedSearchTerm.length > 2 }); return ( <div> <input value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} placeholder="Search..." /> {/* Render results */} </div> ); }; \`\`\``; } export function validateEnvironment(args) { const { checkAll = false, components = [], nodeVersion, dhis2Instance, operatingSystem = 'unknown' } = args; const checks = checkAll ? [ 'node_version', 'npm_yarn', 'dhis2_cli', 'browser_settings', 'network_connectivity', 'cors_config' ] : components; return `# DHIS2 Development Environment Validation ## Environment Information - **Operating System**: ${operatingSystem.toUpperCase()} - **Node Version**: ${nodeVersion || 'Not provided'} - **DHIS2 Instance**: ${dhis2Instance || 'Not provided'} ## Validation Results ${checks.map((check) => generateEnvironmentCheck(check, { nodeVersion, dhis2Instance, operatingSystem })).join('\n\n')} ## Automated Validation Script \`\`\`bash #!/bin/bash # DHIS2 Environment Validator echo "🔍 DHIS2 Development Environment Validation" echo "============================================" # Node.js version check echo "📦 Checking Node.js version..." NODE_VERSION=$(node --version) echo "Node.js: $NODE_VERSION" if [[ "$NODE_VERSION" < "v16" ]]; then echo "❌ Node.js 16+ required" else echo "✅ Node.js version OK" fi # Package manager check echo "📦 Checking package managers..." if command -v npm &> /dev/null; then echo "✅ npm: $(npm --version)" else echo "❌ npm not found" fi if command -v yarn &> /dev/null; then echo "✅ yarn: $(yarn --version)" else echo "⚠️ yarn not found (recommended for DHIS2)" fi # DHIS2 CLI check echo "🔧 Checking DHIS2 CLI tools..." if command -v d2 &> /dev/null; then echo "✅ d2 CLI: $(d2 --version)" else echo "❌ d2 CLI not found" echo "Install: npm install -g @dhis2/cli" fi # Network connectivity check ${dhis2Instance ? ` echo "🌐 Testing DHIS2 instance connectivity..." if curl -Is "${dhis2Instance}/api/system/info" | head -n 1 | grep -q "200 OK"; then echo "✅ DHIS2 instance reachable" else echo "❌ Cannot reach DHIS2 instance" fi ` : ''} echo "✅ Environment validation complete" \`\`\` ## Fix Common Issues ### Node.js Version Issues \`\`\`bash # Install Node Version Manager curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash # Install and use Node.js 18 LTS nvm install 18 nvm use 18 nvm alias default 18 \`\`\` ### Package Manager Issues \`\`\`bash # Install/Update yarn (recommended for DHIS2) npm install -g yarn # Clear package caches npm cache clean --force yarn cache clean \`\`\` ### DHIS2 CLI Issues \`\`\`bash # Install DHIS2 CLI tools npm install -g @dhis2/cli # Verify installation d2 --version d2 --help \`\`\` ## Development Environment Setup ### Complete Setup Script \`\`\`bash #!/bin/bash # Complete DHIS2 development environment setup echo "🚀 Setting up DHIS2 development environment..." # Install Node.js 18 LTS (if using nvm) nvm install 18 nvm use 18 # Install global tools npm install -g @dhis2/cli yarn # Verify installations echo "Node.js: $(node --version)" echo "npm: $(npm --version)" echo "yarn: $(yarn --version)" echo "DHIS2 CLI: $(d2 --version)" # Create development project mkdir my-dhis2-app cd my-dhis2-app # Initialize DHIS2 app d2 app scripts init my-app cd my-app # Install dependencies yarn install echo "✅ DHIS2 development environment ready!" echo "Run 'yarn start' to begin development" \`\`\` ## IDE Configuration ### VS Code Extensions \`\`\`json { "recommendations": [ "ms-vscode.vscode-typescript-next", "bradlc.vscode-tailwindcss", "esbenp.prettier-vscode", "ms-vscode.vscode-eslint", "formulahendry.auto-rename-tag", "christian-kohler.path-intellisense" ] } \`\`\` ### VS Code Settings \`\`\`json { "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "typescript.preferences.importModuleSpecifier": "relative", "emmet.includeLanguages": { "javascript": "javascriptreact" } } \`\`\` ## Troubleshooting Common Environment Issues ### Permission Issues (macOS/Linux) \`\`\`bash # Fix npm permissions sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_mod