#!/bin/bash # إصلاح Frontend وربطه بالـ Backend set -e # الألوان GREEN='\033[0;32m' BLUE='\033[0;34m' YELLOW='\033[1;33m' RED='\033[0;31m' NC='\033[0m' echo -e "${BLUE}" echo "==========================================" echo " 🔧 إصلاح Frontend وربطه بالـ Backend" echo " 🚀 Fix Frontend and Link to Backend" echo "==========================================" echo -e "${NC}" # 1. فحص Backend echo -e "${YELLOW}1. فحص Backend...${NC}" BACKEND_RESPONSE=$(curl -s https://cursor-backend.workers.dev/health) echo "Backend Response: $BACKEND_RESPONSE" if echo "$BACKEND_RESPONSE" | grep -q '"status":"healthy"'; then echo -e "${GREEN}✅ Backend يعمل${NC}" else echo -e "${RED}❌ Backend لا يعمل${NC}" exit 1 fi # 2. إصلاح Frontend Environment echo -e "${YELLOW}2. إصلاح Frontend Environment...${NC}" cd frontend # إنشاء .env.production صحيح cat > .env.production << 'EOF' VITE_BACKEND_URL=https://cursor-backend.workers.dev VITE_WS_URL=wss://cursor-backend.workers.dev VITE_NODE_ENV=production VITE_APP_NAME=Cursor AI IDE VITE_APP_VERSION=1.0.0 EOF echo -e "${GREEN}✅ تم إنشاء .env.production${NC}" # 3. إصلاح App.tsx للاتصال بالـ Backend echo -e "${YELLOW}3. إصلاح App.tsx للاتصال بالـ Backend...${NC}" cat > src/App.tsx << 'EOF' import React, { useState, useEffect } from 'react'; import { Sidebar } from './components/Sidebar'; import { MonacoEditor } from './components/MonacoEditor'; import { ChatAssistant } from './components/ChatAssistant'; import { ProviderForm } from './components/ProviderForm'; import { ToolPanel } from './components/ToolPanel'; import { StatusBar } from './components/StatusBar'; import { NotificationContainer } from './components/Notification'; // Backend URLs const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || 'https://cursor-backend.workers.dev'; const WS_URL = import.meta.env.VITE_WS_URL || 'wss://cursor-backend.workers.dev'; function App() { const [selectedFile, setSelectedFile] = useState(null); const [files, setFiles] = useState([]); const [showChat, setShowChat] = useState(false); const [showProviderForm, setShowProviderForm] = useState(false); const [showTools, setShowTools] = useState(false); const [socket, setSocket] = useState(null); const [apiKeys, setApiKeys] = useState>({}); const [selectedProvider, setSelectedProvider] = useState('openai'); const [notifications, setNotifications] = useState([]); const [currentLine, setCurrentLine] = useState(1); const [currentColumn, setCurrentColumn] = useState(1); const [lineCount, setLineCount] = useState(0); const [gitBranch, setGitBranch] = useState(''); const [isConnected, setIsConnected] = useState(false); const [isLoading, setIsLoading] = useState(true); useEffect(() => { // Initialize connection initializeApp(); }, []); const initializeApp = async () => { try { setIsLoading(true); // Test backend connection const healthResponse = await fetch(`${BACKEND_URL}/health`); if (healthResponse.ok) { const healthData = await healthResponse.json(); console.log('Backend connected:', healthData); setIsConnected(true); // Load workspace files await loadWorkspaceFiles(); // Initialize WebSocket initializeWebSocket(); } else { throw new Error('Backend not responding'); } } catch (error) { console.error('Failed to connect to backend:', error); addNotification({ type: 'error', title: 'Connection Failed', message: 'Could not connect to backend. Please check your connection.' }); } finally { setIsLoading(false); } }; const initializeWebSocket = () => { try { const ws = new WebSocket(WS_URL); setSocket(ws); ws.onopen = () => { console.log('WebSocket connected'); setIsConnected(true); }; ws.onclose = () => { console.log('WebSocket disconnected'); setIsConnected(false); }; ws.onerror = (error) => { console.error('WebSocket error:', error); setIsConnected(false); }; } catch (error) { console.error('Failed to initialize WebSocket:', error); } }; const loadWorkspaceFiles = async () => { try { const response = await fetch(`${BACKEND_URL}/api/workspace/files`); if (response.ok) { const data = await response.json(); setFiles(data.files || []); console.log('Loaded files:', data.files); } else { throw new Error('Failed to load files'); } } catch (error) { console.error('Failed to load workspace files:', error); addNotification({ type: 'error', title: 'Failed to load files', message: 'Could not load workspace files from backend' }); } }; const handleFileSelect = (filePath: string) => { setSelectedFile(filePath); }; const handleApiKeySave = (provider: string, apiKey: string) => { setApiKeys(prev => ({ ...prev, [provider]: apiKey })); setShowProviderForm(false); addNotification({ type: 'success', title: 'API Key Saved', message: `API key for ${provider} has been saved successfully` }); }; const handleOpenCodeServer = () => { window.open('https://vscode.dev', '_blank'); }; const addNotification = (notification: any) => { const id = Date.now().toString(); setNotifications(prev => [...prev, { ...notification, id }]); }; const removeNotification = (id: string) => { setNotifications(prev => prev.filter(n => n.id !== id)); }; const getLanguageFromExtension = (filename: string) => { const ext = filename.split('.').pop()?.toLowerCase(); const languageMap: Record = { 'js': 'javascript', 'jsx': 'javascript', 'ts': 'typescript', 'tsx': 'typescript', 'py': 'python', 'java': 'java', 'cpp': 'cpp', 'c': 'c', 'cs': 'csharp', 'go': 'go', 'rs': 'rust', 'php': 'php', 'rb': 'ruby', 'html': 'html', 'css': 'css', 'scss': 'scss', 'json': 'json', 'xml': 'xml', 'yaml': 'yaml', 'yml': 'yaml', 'md': 'markdown', 'sql': 'sql', 'sh': 'shell', 'bash': 'shell', 'dockerfile': 'dockerfile', }; return languageMap[ext || ''] || 'plaintext'; }; if (isLoading) { return (

Connecting to Backend...

Please wait while we establish connection

); } return (
{/* Sidebar */} setShowChat(!showChat)} onShowProviderForm={() => setShowProviderForm(true)} onShowTools={() => setShowTools(!showTools)} onOpenCodeServer={handleOpenCodeServer} showChat={showChat} showTools={showTools} /> {/* Main Content */}
{/* Monaco Editor */} { console.log('File changed:', filePath); }} onSave={async (filePath, content) => { try { const response = await fetch(`${BACKEND_URL}/api/workspace/file/${filePath}`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ content }), }); if (response.ok) { addNotification({ type: 'success', title: 'File Saved', message: `${filePath} saved successfully` }); } else { throw new Error('Failed to save file'); } } catch (error) { addNotification({ type: 'error', title: 'Save Failed', message: `Failed to save ${filePath}` }); } }} onRun={async (filePath, content) => { try { const response = await fetch(`${BACKEND_URL}/api/execute`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ code: content, language: getLanguageFromExtension(filePath) }), }); const result = await response.json(); console.log('Code executed:', result); } catch (error) { console.error('Failed to run code:', error); } }} backendUrl={BACKEND_URL} /> {/* Chat Assistant */} {showChat && ( )}
{/* Tool Panel */} {showTools && ( { console.log('Executing tool:', toolName, params); }} onResult={(result) => { console.log('Tool result:', result); }} backendUrl={BACKEND_URL} /> )}
{/* Status Bar */} {/* Notifications */} {/* Provider Form Modal */} {showProviderForm && ( setShowProviderForm(false)} existingKeys={apiKeys} /> )}
); } export default App; EOF echo -e "${GREEN}✅ تم إصلاح App.tsx${NC}" # 4. إصلاح MonacoEditor للاتصال بالـ Backend echo -e "${YELLOW}4. إصلاح MonacoEditor للاتصال بالـ Backend...${NC}" cat > src/components/MonacoEditor.tsx << 'EOF' import React, { useRef, useEffect, useState } from 'react'; import { Editor } from '@monaco-editor/react'; import { useMonaco } from '@monaco-editor/react'; import { FileText, Save, Play, Terminal, Maximize2, Minimize2 } from 'lucide-react'; interface MonacoEditorProps { selectedFile: string | null; onFileChange: (filePath: string, content: string) => void; onSave: (filePath: string, content: string) => void; onRun: (filePath: string, content: string) => void; backendUrl: string; } export const MonacoEditor: React.FC = ({ selectedFile, onFileChange, onSave, onRun, backendUrl }) => { const monaco = useMonaco(); const editorRef = useRef(null); const [content, setContent] = useState(''); const [isFullscreen, setIsFullscreen] = useState(false); const [showTerminal, setShowTerminal] = useState(false); const [terminalOutput, setTerminalOutput] = useState(''); const [isLoading, setIsLoading] = useState(false); // Load file content when selectedFile changes useEffect(() => { if (selectedFile) { loadFileContent(selectedFile); } }, [selectedFile]); // Configure Monaco Editor useEffect(() => { if (monaco) { monaco.editor.setTheme('vs-dark'); // Add custom keybindings monaco.editor.addKeybindingRules([ { keybinding: monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, command: 'save-file', when: 'editorTextFocus' }, { keybinding: monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, command: 'run-code', when: 'editorTextFocus' } ]); // Register custom commands monaco.editor.registerCommand('save-file', () => { if (selectedFile) { handleSave(); } }); monaco.editor.registerCommand('run-code', () => { if (selectedFile) { handleRun(); } }); } }, [monaco, selectedFile]); const loadFileContent = async (filePath: string) => { try { setIsLoading(true); const response = await fetch(`${backendUrl}/api/workspace/file/${filePath}`); if (response.ok) { const data = await response.json(); setContent(data.content || ''); } else { throw new Error('Failed to load file'); } } catch (error) { console.error('Failed to load file:', error); setContent('// Error loading file'); } finally { setIsLoading(false); } }; const handleSave = async () => { if (selectedFile && content) { try { await onSave(selectedFile, content); console.log('File saved successfully'); } catch (error) { console.error('Failed to save file:', error); } } }; const handleRun = async () => { if (selectedFile && content) { try { setShowTerminal(true); setTerminalOutput('Running code...\n'); const response = await fetch(`${backendUrl}/api/execute`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ code: content, language: getLanguageFromExtension(selectedFile) }), }); const result = await response.json(); setTerminalOutput(prev => prev + result.output + '\n'); await onRun(selectedFile, content); } catch (error) { console.error('Failed to run code:', error); setTerminalOutput(prev => prev + `Error: ${error.message}\n`); } } }; const getLanguageFromExtension = (filename: string) => { const ext = filename.split('.').pop()?.toLowerCase(); const languageMap: Record = { 'js': 'javascript', 'jsx': 'javascript', 'ts': 'typescript', 'tsx': 'typescript', 'py': 'python', 'java': 'java', 'cpp': 'cpp', 'c': 'c', 'cs': 'csharp', 'go': 'go', 'rs': 'rust', 'php': 'php', 'rb': 'ruby', 'html': 'html', 'css': 'css', 'scss': 'scss', 'json': 'json', 'xml': 'xml', 'yaml': 'yaml', 'yml': 'yaml', 'md': 'markdown', 'sql': 'sql', 'sh': 'shell', 'bash': 'shell', 'dockerfile': 'dockerfile', }; return languageMap[ext || ''] || 'plaintext'; }; const handleEditorDidMount = (editor: any) => { editorRef.current = editor; editor.updateOptions({ fontSize: 14, fontFamily: 'Fira Code, Consolas, Monaco, monospace', lineNumbers: 'on', wordWrap: 'on', minimap: { enabled: true }, folding: true, bracketPairColorization: { enabled: true }, autoClosingBrackets: 'always', autoClosingQuotes: 'always', autoIndent: 'full', formatOnPaste: true, formatOnType: true, suggestOnTriggerCharacters: true, acceptSuggestionOnEnter: 'on', tabCompletion: 'on', wordBasedSuggestions: 'off', parameterHints: { enabled: true }, hover: { enabled: true }, contextmenu: true, mouseWheelZoom: true, smoothScrolling: true, cursorBlinking: 'blink', cursorSmoothCaretAnimation: true, renderWhitespace: 'selection', renderControlCharacters: false, renderIndentGuides: true, highlightActiveIndentGuide: true, rulers: [80, 120], scrollBeyondLastLine: false, automaticLayout: true, dragAndDrop: true, links: true, detectIndentation: true, insertSpaces: true, tabSize: 2, trimAutoWhitespace: true, largeFileOptimizations: true }); }; const handleEditorChange = (value: string | undefined) => { setContent(value || ''); if (selectedFile) { onFileChange(selectedFile, value || ''); } }; if (isLoading) { return (

Loading file...

); } return (
{/* Editor Header */}
{selectedFile || 'No file selected'} {selectedFile && ( {getLanguageFromExtension(selectedFile)} )}
{/* Editor and Terminal */}
{/* Monaco Editor */}
{/* Terminal Panel */} {showTerminal && (
Terminal
                {terminalOutput || 'Terminal ready...\n'}
              
)}
{/* Status Bar */}
Ready {selectedFile && ( {selectedFile} )}
UTF-8 2 spaces Monaco Editor
); }; EOF echo -e "${GREEN}✅ تم إصلاح MonacoEditor${NC}" # 5. إصلاح ChatAssistant للاتصال بالـ Backend echo -e "${YELLOW}5. إصلاح ChatAssistant للاتصال بالـ Backend...${NC}" cat > src/components/ChatAssistant.tsx << 'EOF' import React, { useState, useEffect, useRef } from 'react'; import { Send, Bot, User, Loader2, X } from 'lucide-react'; interface Message { id: string; type: 'user' | 'assistant'; content: string; timestamp: Date; provider?: string; } interface ChatAssistantProps { socket: WebSocket | null; apiKeys: Record; selectedProvider: string; onProviderChange: (provider: string) => void; backendUrl: string; } export const ChatAssistant: React.FC = ({ socket, apiKeys, selectedProvider, onProviderChange, backendUrl }) => { const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); const [isTyping, setIsTyping] = useState(false); const messagesEndRef = useRef(null); useEffect(() => { if (socket) { socket.onmessage = (event) => { try { const data = JSON.parse(event.data); if (data.type === 'chat-response') { const newMessage: Message = { id: Date.now().toString(), type: 'assistant', content: data.response, timestamp: new Date(), provider: data.provider }; setMessages(prev => [...prev, newMessage]); setIsTyping(false); } else if (data.type === 'typing-start') { setIsTyping(true); } else if (data.type === 'typing-stop') { setIsTyping(false); } else if (data.type === 'error') { const errorMessage: Message = { id: Date.now().toString(), type: 'assistant', content: `Error: ${data.error}`, timestamp: new Date() }; setMessages(prev => [...prev, errorMessage]); setIsTyping(false); } } catch (error) { console.error('Failed to parse WebSocket message:', error); } }; } }, [socket]); useEffect(() => { scrollToBottom(); }, [messages]); const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }; const sendMessage = async () => { if (!input.trim() || !apiKeys[selectedProvider]) { return; } const userMessage: Message = { id: Date.now().toString(), type: 'user', content: input, timestamp: new Date() }; setMessages(prev => [...prev, userMessage]); setInput(''); if (socket && socket.readyState === WebSocket.OPEN) { // Send via WebSocket socket.send(JSON.stringify({ type: 'chat', content: input, provider: selectedProvider, apiKey: apiKeys[selectedProvider], model: getModelForProvider(selectedProvider) })); } else { // Fallback to HTTP API try { const response = await fetch(`${backendUrl}/api/chat`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ message: input, provider: selectedProvider, apiKey: apiKeys[selectedProvider], model: getModelForProvider(selectedProvider) }), }); const data = await response.json(); const assistantMessage: Message = { id: Date.now().toString(), type: 'assistant', content: data.response, timestamp: new Date(), provider: selectedProvider }; setMessages(prev => [...prev, assistantMessage]); } catch (error) { console.error('Failed to send message:', error); const errorMessage: Message = { id: Date.now().toString(), type: 'assistant', content: 'Failed to send message. Please check your connection.', timestamp: new Date() }; setMessages(prev => [...prev, errorMessage]); } } }; const getModelForProvider = (provider: string) => { const models: Record = { openai: 'gpt-4', anthropic: 'claude-3-sonnet-20240229', google: 'gemini-pro', mistral: 'mistral-large-latest', openrouter: 'meta-llama/llama-2-70b-chat' }; return models[provider] || 'gpt-4'; }; const handleKeyPress = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }; const clearChat = () => { setMessages([]); }; return (
{/* Chat Header */}
AI Assistant
{socket?.readyState === WebSocket.OPEN ? 'Connected' : 'Disconnected'}
{/* Messages */}
{messages.length === 0 && (

Start a conversation with the AI assistant

Make sure to set your API key in settings

)} {messages.map((message) => (
{message.type === 'assistant' && ( )} {message.type === 'user' && ( )}

{message.content}

{message.timestamp.toLocaleTimeString()} {message.provider && ( via {message.provider} )}
))} {isTyping && (
)}
{/* Input */}
setInput(e.target.value)} onKeyPress={handleKeyPress} placeholder={ apiKeys[selectedProvider] ? "Ask me anything..." : "Please set your API key in settings first" } disabled={!apiKeys[selectedProvider]} className="flex-1 bg-cursor-bg border border-cursor-border rounded px-3 py-2 text-sm focus:outline-none focus:border-cursor-accent disabled:opacity-50" />
); }; EOF echo -e "${GREEN}✅ تم إصلاح ChatAssistant${NC}" # 6. بناء Frontend echo -e "${YELLOW}6. بناء Frontend...${NC}" npm run build echo -e "${GREEN}✅ تم بناء Frontend${NC}" # 7. رفع Frontend إلى Cloudflare Pages echo -e "${YELLOW}7. رفع Frontend إلى Cloudflare Pages...${NC}" cd .. # إنشاء سكريبت رفع Frontend cat > deploy-frontend-fixed.sh << 'EOF' #!/bin/bash API_TOKEN="avRH6WSd0ueXkJqbQpDdnseVo9fy-fUSIJ1pdrWC" ACCOUNT_ID="76f5b050419f112f1e9c5fbec1b3970d" PROJECT_NAME="cursor-ide" # رفع Frontend echo "رفع Frontend..." cd frontend/dist # إنشاء manifest cat > manifest.json << 'MANIFEST' { "index.html": "index.html", "assets/index.css": "assets/index.css", "assets/index.js": "assets/index.js" } MANIFEST # ضغط الملفات zip -r ../frontend-deploy.zip . manifest.json cd .. # رفع الملفات curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/pages/projects/$PROJECT_NAME/deployments" \ -H "Authorization: Bearer $API_TOKEN" \ -F "files=@frontend-deploy.zip" \ -F "manifest=@manifest.json" echo "تم رفع Frontend" EOF chmod +x deploy-frontend-fixed.sh ./deploy-frontend-fixed.sh echo -e "${GREEN}✅ تم رفع Frontend${NC}" # 8. اختبار التطبيق الكامل echo -e "${YELLOW}8. اختبار التطبيق الكامل...${NC}" echo -e "${YELLOW}اختبار Backend:${NC}" BACKEND_TEST=$(curl -s https://cursor-backend.workers.dev/health) echo "$BACKEND_TEST" echo -e "\n${YELLOW}اختبار Frontend:${NC}" FRONTEND_TEST=$(curl -s -w "%{http_code}" https://cursor-ide.pages.dev -o /dev/null) echo "Frontend Status: $FRONTEND_TEST" # 9. تقرير النتائج echo -e "\n${GREEN}==========================================" echo " 🎉 تم إصلاح Frontend وربطه بالـ Backend! 🎉" echo "==========================================" echo -e "${NC}" echo -e "${GREEN}✅ Backend: https://cursor-backend.workers.dev${NC}" echo -e "${GREEN}✅ Frontend: https://cursor-ide.pages.dev${NC}" echo -e "\n${YELLOW}📋 اختبار التطبيق:${NC}" echo "1. 🌐 افتح: https://cursor-ide.pages.dev" echo "2. 🔑 أضف مفاتيح API للمزودين" echo "3. 🧪 اختبر وظائف التطبيق" echo -e "\n${BLUE}🔗 روابط مفيدة:${NC}" echo "Backend Health: https://cursor-backend.workers.dev/health" echo "API Providers: https://cursor-backend.workers.dev/api/providers" echo "API Tools: https://cursor-backend.workers.dev/api/tools" echo "Workspace Files: https://cursor-backend.workers.dev/api/workspace/files" echo -e "\n${GREEN}🎉 Frontend مربوط بالـ Backend ويعمل بشكل صحيح!${NC}"