diff --git a/docker-compose.yaml b/docker-compose.yaml index aba9bba..7f8af1b 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -22,6 +22,13 @@ services: - CACHE_ENABLED=false - CACHE_STORE=memory - WEBSOCKETS_ENABLED=true + - CORS_ENABLED=true + - 'CORS_ORIGIN=https://launch.jumpstartscaling.com,http://localhost:4321' + - CORS_METHODS=GET,POST,PATCH,DELETE + - CORS_ALLOWED_HEADERS=Content-Type,Authorization + - CORS_EXPOSED_HEADERS=Content-Range + - CORS_CREDENTIALS=true + - CORS_MAX_AGE=86400 labels: - "traefik.enable=true" - "traefik.http.routers.directus.rule=Host(`spark.jumpstartscaling.com`)" diff --git a/frontend/src/components/admin/SystemStatusBar.tsx b/frontend/src/components/admin/SystemStatusBar.tsx new file mode 100644 index 0000000..8b67f63 --- /dev/null +++ b/frontend/src/components/admin/SystemStatusBar.tsx @@ -0,0 +1,163 @@ +import { useState, useEffect } from 'react'; +import { getDirectusClient, readItems } from '@/lib/directus/client'; + +interface SystemStatus { + coreApi: 'online' | 'offline' | 'checking'; + database: 'connected' | 'disconnected' | 'checking'; + wpConnection: 'ready' | 'error' | 'checking'; +} + +interface LogEntry { + time: string; + message: string; + type: 'info' | 'success' | 'warning' | 'error'; +} + +export default function SystemStatusBar() { + const [status, setStatus] = useState({ + coreApi: 'checking', + database: 'checking', + wpConnection: 'checking' + }); + + const [logs, setLogs] = useState([]); + const [showLogs, setShowLogs] = useState(false); + + useEffect(() => { + checkSystemStatus(); + const interval = setInterval(checkSystemStatus, 30000); // Check every 30 seconds + return () => clearInterval(interval); + }, []); + + const checkSystemStatus = async () => { + try { + const client = getDirectusClient(); + + // Test database connection by fetching a single site + const sites = await client.request( + readItems('sites', { limit: 1 }) + ); + + setStatus({ + coreApi: 'online', + database: 'connected', + wpConnection: 'ready' + }); + + addLog('System check passed', 'success'); + } catch (error) { + console.error('Status check failed:', error); + setStatus({ + coreApi: 'offline', + database: 'disconnected', + wpConnection: 'error' + }); + addLog(`System check failed: ${error instanceof Error ? error.message : 'Unknown error'}`, 'error'); + } + }; + + const addLog = (message: string, type: LogEntry['type']) => { + const newLog: LogEntry = { + time: new Date().toLocaleTimeString(), + message, + type + }; + setLogs(prev => [newLog, ...prev].slice(0, 50)); // Keep last 50 logs + }; + + const getStatusColor = (state: string) => { + switch (state) { + case 'online': + case 'connected': + case 'ready': + return 'text-green-400'; + case 'offline': + case 'disconnected': + case 'error': + return 'text-red-400'; + case 'checking': + return 'text-yellow-400'; + default: + return 'text-slate-400'; + } + }; + + const getLogColor = (type: LogEntry['type']) => { + switch (type) { + case 'success': + return 'text-green-400'; + case 'error': + return 'text-red-400'; + case 'warning': + return 'text-yellow-400'; + default: + return 'text-slate-400'; + } + }; + + return ( +
+ {/* Main Status Bar */} +
+
+ {/* Title */} +

+ API & Logistics +

+ + {/* Status Items */} +
+
+ Core API + + {status.coreApi.charAt(0).toUpperCase() + status.coreApi.slice(1)} + +
+ +
+ Database (Directus + + {status.database.charAt(0).toUpperCase() + status.database.slice(1)} + +
+ +
+ WP Connection + + {status.wpConnection.charAt(0).toUpperCase() + status.wpConnection.slice(1)} + +
+
+ + {/* Toggle Logs Button */} + +
+
+ + {/* Processing Log Panel */} + {showLogs && ( +
+
+
+ {logs.length === 0 ? ( +
No recent activity
+ ) : ( + logs.map((log, index) => ( +
+ [{log.time}] + {log.message} +
+ )) + )} +
+
+
+ )} +
+ ); +} diff --git a/frontend/src/layouts/AdminLayout.astro b/frontend/src/layouts/AdminLayout.astro index a30497a..65cf4a2 100644 --- a/frontend/src/layouts/AdminLayout.astro +++ b/frontend/src/layouts/AdminLayout.astro @@ -6,6 +6,10 @@ interface Props { const { title } = Astro.props; const currentPath = Astro.url.pathname; +import SystemStatus from '@/components/admin/SystemStatus'; +import SystemStatusBar from '@/components/admin/SystemStatusBar'; + + const navGroups = [ { title: 'Command Station', @@ -169,6 +173,10 @@ function isActive(href: string) { ))} +
+ +
+ + + + diff --git a/scripts/fix_cors.js b/scripts/fix_cors.js new file mode 100644 index 0000000..dcca327 --- /dev/null +++ b/scripts/fix_cors.js @@ -0,0 +1,97 @@ +#!/usr/bin/env node + +/** + * Fix Directus CORS Settings + * Allow launch.jumpstartscaling.com to access spark.jumpstartscaling.com + */ + +const DIRECTUS_URL = 'https://spark.jumpstartscaling.com'; +const ADMIN_TOKEN = 'SufWLAbsqmbbqF_gg5I70ng8wE1zXt-a'; + +async function makeRequest(endpoint, method = 'GET', body = null) { + const options = { + method, + headers: { + 'Authorization': `Bearer ${ADMIN_TOKEN}`, + 'Content-Type': 'application/json' + } + }; + + if (body) { + options.body = JSON.stringify(body); + } + + const response = await fetch(`${DIRECTUS_URL}${endpoint}`, options); + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`API Error: ${response.status} - ${errorText}`); + } + return response.json(); +} + +async function fixCORS() { + console.log('\nšŸ”§ FIXING DIRECTUS CORS SETTINGS\n'); + console.log('═'.repeat(60)); + + console.log('\nšŸ“‹ Current Issue:'); + console.log(' Origin: https://launch.jumpstartscaling.com'); + console.log(' Target: https://spark.jumpstartscaling.com'); + console.log(' Error: No Access-Control-Allow-Origin header\n'); + + console.log('āš ļø IMPORTANT: CORS settings must be configured in Directus environment variables.\n'); + console.log('The following environment variables need to be set in Coolify:\n'); + + console.log('CORS_ENABLED=true'); + console.log('CORS_ORIGIN=https://launch.jumpstartscaling.com,http://localhost:4321'); + console.log('CORS_METHODS=GET,POST,PATCH,DELETE'); + console.log('CORS_ALLOWED_HEADERS=Content-Type,Authorization'); + console.log('CORS_EXPOSED_HEADERS=Content-Range'); + console.log('CORS_CREDENTIALS=true'); + console.log('CORS_MAX_AGE=86400\n'); + + console.log('═'.repeat(60)); + console.log('šŸ“ INSTRUCTIONS:\n'); + console.log('1. Go to Coolify: http://72.61.15.216:8000'); + console.log('2. Find the Directus service'); + console.log('3. Go to Environment Variables'); + console.log('4. Add the CORS variables listed above'); + console.log('5. Restart the Directus service\n'); + console.log('OR update docker-compose.yaml with these values and redeploy.\n'); + console.log('═'.repeat(60) + '\n'); + + // Test current CORS + console.log('šŸ” Testing if CORS is already configured...\n'); + + try { + // Try a simple request from Node (won't have CORS issue) + const test = await makeRequest('/server/info'); + console.log('āœ… API is accessible (from Node.js)'); + console.log('āš ļø Browser CORS restriction is separate - needs environment variables\n'); + } catch (err) { + console.log('āŒ API test failed:', err.message, '\n'); + } + + return { + instructions: 'Add CORS environment variables to Directus service in Coolify', + variables: { + CORS_ENABLED: 'true', + CORS_ORIGIN: 'https://launch.jumpstartscaling.com,http://localhost:4321', + CORS_METHODS: 'GET,POST,PATCH,DELETE', + CORS_ALLOWED_HEADERS: 'Content-Type,Authorization', + CORS_EXPOSED_HEADERS: 'Content-Range', + CORS_CREDENTIALS: 'true', + CORS_MAX_AGE: '86400' + } + }; +} + +fixCORS() + .then(result => { + const fs = require('fs'); + fs.writeFileSync('cors_fix_instructions.json', JSON.stringify(result, null, 2)); + console.log('šŸ“„ Instructions saved to: cors_fix_instructions.json\n'); + }) + .catch(err => { + console.error('āŒ Error:', err.message); + process.exit(1); + });