feat: add production configuration with environment variables

- Add .env file for production deployment with reverse proxy
- Add docker-compose.prod.yml for production profile
- Add docker-compose.override.yml for local development
- Update docker-compose.yml with all configurable variables
- Update frontend to use VITE_* environment variables
- Update backend to support CORS_ORIGINS and WEBHOOK_BASE_URL
- Add vite.config.ts allowedHosts for reverse proxy
- Add documentation for docker-compose and reverse proxy setup

All URLs are now configurable via environment variables:
- VITE_API_URL: Backend API endpoint
- VITE_WEBHOOK_BASE_URL: Webhook base URL
- VITE_INSTALL_SCRIPT_URL: Install script URL
- VITE_APP_URL: Frontend URL
- CORS_ORIGINS: Allowed CORS origins
- WEBHOOK_BASE_URL: Backend webhook base URL
This commit is contained in:
Luca Sacchi Ricciardi
2026-04-03 18:49:53 +02:00
parent 92217897ca
commit 26879acba4
12 changed files with 607 additions and 39 deletions

15
frontend/.env.development Normal file
View File

@@ -0,0 +1,15 @@
# LogWhispererAI - Frontend Environment Variables (Development)
# These variables are exposed to the browser (must start with VITE_)
# Backend API URL
VITE_API_URL=http://192.168.254.79:3001
# Base URL for webhook endpoints
VITE_WEBHOOK_BASE_URL=http://192.168.254.79:3001/webhook
# Install script URL
VITE_INSTALL_SCRIPT_URL=http://192.168.254.79:3001/install.sh
# Application identification
VITE_APP_NAME=LogWhispererAI
VITE_APP_URL=http://192.168.254.79:5173

View File

@@ -49,7 +49,8 @@ interface ApiResponse {
message?: string;
}
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000';
// Environment configuration
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001';
export const InteractiveDemo: React.FC = () => {
const [selectedLog, setSelectedLog] = useState<string | null>(null);

View File

@@ -22,21 +22,56 @@ export const OnboardingWizard: React.FC<OnboardingWizardProps> = ({ onComplete }
const [isGenerating, setIsGenerating] = useState(false);
const stepRef = useRef<HTMLDivElement>(null);
// Focus management for accessibility
// Focus management for accessibility - only when step changes, not on initial mount
useEffect(() => {
if (stepRef.current) {
stepRef.current.focus();
// Only focus if this is a step change (not initial mount)
// This prevents auto-scroll to the onboarding section on page load
if (stepRef.current && document.activeElement !== document.body) {
stepRef.current.focus({ preventScroll: true });
}
}, [currentStep]);
const generateWebhook = () => {
// Environment configuration
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001';
const WEBHOOK_BASE_URL = import.meta.env.VITE_WEBHOOK_BASE_URL || `${API_URL}/webhook`;
const INSTALL_SCRIPT_URL = import.meta.env.VITE_INSTALL_SCRIPT_URL || `${API_URL}/install.sh`;
const APP_NAME = import.meta.env.VITE_APP_NAME || 'LogWhispererAI';
const generateWebhook = async () => {
setIsGenerating(true);
// Simulate generation delay
setTimeout(() => {
const uuid = crypto.randomUUID();
setWebhookUrl(`https://logwhisperer.ai/webhook/${uuid}`);
try {
const response = await fetch(`${API_URL}/api/webhook`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.success && data.uuid) {
// Use configured webhook base URL instead of API response
setWebhookUrl(`${WEBHOOK_BASE_URL}/${data.uuid}`);
} else {
throw new Error(data.message || 'Errore nella generazione del webhook');
}
} catch (error) {
console.error('Error generating webhook:', error);
// Fallback: generate locally if API fails
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
setWebhookUrl(`${WEBHOOK_BASE_URL}/${uuid}`);
} finally {
setIsGenerating(false);
}, 1000);
}
};
const copyWebhook = () => {
@@ -46,7 +81,7 @@ export const OnboardingWizard: React.FC<OnboardingWizardProps> = ({ onComplete }
};
const copyCurlCommand = () => {
const command = `curl -fsSL https://logwhisperer.ai/install.sh | bash -s -- --webhook ${webhookUrl}`;
const command = `curl -fsSL ${INSTALL_SCRIPT_URL} | bash -s -- --webhook ${webhookUrl}`;
navigator.clipboard.writeText(command);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
@@ -68,8 +103,8 @@ export const OnboardingWizard: React.FC<OnboardingWizardProps> = ({ onComplete }
const getInstallCommand = () => {
return webhookUrl
? `curl -fsSL https://logwhisperer.ai/install.sh | bash -s -- --webhook ${webhookUrl}`
: 'curl -fsSL https://logwhisperer.ai/install.sh | bash';
? `curl -fsSL ${INSTALL_SCRIPT_URL} | bash -s -- --webhook ${webhookUrl}`
: `curl -fsSL ${INSTALL_SCRIPT_URL} | bash`;
};
const steps = [

View File

@@ -10,5 +10,13 @@ export default defineConfig({
watch: {
usePolling: true,
},
// Allow requests from reverse proxy host
// Add your production domain here
allowedHosts: [
'localhost',
'127.0.0.1',
'.lab.home.lucasacchi.net', // Allow all subdomains
'logwhispererai.lab.home.lucasacchi.net',
],
},
})