diff --git a/server/routes/taskmaster.js b/server/routes/taskmaster.js index 46588d0..191742b 100644 --- a/server/routes/taskmaster.js +++ b/server/routes/taskmaster.js @@ -528,7 +528,8 @@ router.get('/next/:projectName', async (req, res) => { console.warn('Failed to execute task-master CLI:', cliError.message); // Fallback to loading tasks and finding next one locally - const tasksResponse = await fetch(`${req.protocol}://${req.get('host')}/api/taskmaster/tasks/${encodeURIComponent(projectName)}`, { + // Use localhost to bypass proxy for internal server-to-server calls + const tasksResponse = await fetch(`http://localhost:${process.env.PORT || 3001}/api/taskmaster/tasks/${encodeURIComponent(projectName)}`, { headers: { 'Authorization': req.headers.authorization } diff --git a/src/components/ApiKeysSettings.jsx b/src/components/ApiKeysSettings.jsx index c2d427c..2687d83 100644 --- a/src/components/ApiKeysSettings.jsx +++ b/src/components/ApiKeysSettings.jsx @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'; import { Button } from './ui/button'; import { Input } from './ui/input'; import { Key, Plus, Trash2, Eye, EyeOff, Copy, Check, Github } from 'lucide-react'; +import { authenticatedFetch } from '../utils/api'; function ApiKeysSettings() { const [apiKeys, setApiKeys] = useState([]); @@ -23,19 +24,14 @@ function ApiKeysSettings() { const fetchData = async () => { try { setLoading(true); - const token = localStorage.getItem('auth-token'); // Fetch API keys - const apiKeysRes = await fetch('/api/settings/api-keys', { - headers: { 'Authorization': `Bearer ${token}` } - }); + const apiKeysRes = await authenticatedFetch('/api/settings/api-keys'); const apiKeysData = await apiKeysRes.json(); setApiKeys(apiKeysData.apiKeys || []); // Fetch GitHub tokens - const githubRes = await fetch('/api/settings/credentials?type=github_token', { - headers: { 'Authorization': `Bearer ${token}` } - }); + const githubRes = await authenticatedFetch('/api/settings/credentials?type=github_token'); const githubData = await githubRes.json(); setGithubTokens(githubData.credentials || []); } catch (error) { @@ -49,13 +45,8 @@ function ApiKeysSettings() { if (!newKeyName.trim()) return; try { - const token = localStorage.getItem('auth-token'); - const res = await fetch('/api/settings/api-keys', { + const res = await authenticatedFetch('/api/settings/api-keys', { method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, body: JSON.stringify({ keyName: newKeyName }) }); @@ -75,10 +66,8 @@ function ApiKeysSettings() { if (!confirm('Are you sure you want to delete this API key?')) return; try { - const token = localStorage.getItem('auth-token'); - await fetch(`/api/settings/api-keys/${keyId}`, { - method: 'DELETE', - headers: { 'Authorization': `Bearer ${token}` } + await authenticatedFetch(`/api/settings/api-keys/${keyId}`, { + method: 'DELETE' }); fetchData(); } catch (error) { @@ -88,13 +77,8 @@ function ApiKeysSettings() { const toggleApiKey = async (keyId, isActive) => { try { - const token = localStorage.getItem('auth-token'); - await fetch(`/api/settings/api-keys/${keyId}/toggle`, { + await authenticatedFetch(`/api/settings/api-keys/${keyId}/toggle`, { method: 'PATCH', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, body: JSON.stringify({ isActive: !isActive }) }); fetchData(); @@ -107,13 +91,8 @@ function ApiKeysSettings() { if (!newTokenName.trim() || !newGithubToken.trim()) return; try { - const token = localStorage.getItem('auth-token'); - const res = await fetch('/api/settings/credentials', { + const res = await authenticatedFetch('/api/settings/credentials', { method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, body: JSON.stringify({ credentialName: newTokenName, credentialType: 'github_token', @@ -137,10 +116,8 @@ function ApiKeysSettings() { if (!confirm('Are you sure you want to delete this GitHub token?')) return; try { - const token = localStorage.getItem('auth-token'); - await fetch(`/api/settings/credentials/${tokenId}`, { - method: 'DELETE', - headers: { 'Authorization': `Bearer ${token}` } + await authenticatedFetch(`/api/settings/credentials/${tokenId}`, { + method: 'DELETE' }); fetchData(); } catch (error) { @@ -150,13 +127,8 @@ function ApiKeysSettings() { const toggleGithubToken = async (tokenId, isActive) => { try { - const token = localStorage.getItem('auth-token'); - await fetch(`/api/settings/credentials/${tokenId}/toggle`, { + await authenticatedFetch(`/api/settings/credentials/${tokenId}/toggle`, { method: 'PATCH', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, body: JSON.stringify({ isActive: !isActive }) }); fetchData(); diff --git a/src/components/ChatInterface.jsx b/src/components/ChatInterface.jsx index a16e869..0ced685 100644 --- a/src/components/ChatInterface.jsx +++ b/src/components/ChatInterface.jsx @@ -1728,11 +1728,7 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess // Load Cursor default model from config useEffect(() => { if (provider === 'cursor') { - fetch('/api/cursor/config', { - headers: { - 'Authorization': `Bearer ${localStorage.getItem('auth-token')}` - } - }) + authenticatedFetch('/api/cursor/config') .then(res => res.json()) .then(data => { if (data.success && data.config?.model?.modelId) { @@ -3752,15 +3748,9 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess }); try { - const token = safeLocalStorage.getItem('auth-token'); - const headers = {}; - if (token) { - headers['Authorization'] = `Bearer ${token}`; - } - - const response = await fetch(`/api/projects/${selectedProject.name}/upload-images`, { + const response = await authenticatedFetch(`/api/projects/${selectedProject.name}/upload-images`, { method: 'POST', - headers: headers, + headers: {}, // Let browser set Content-Type for FormData body: formData }); diff --git a/src/components/CredentialsSettings.jsx b/src/components/CredentialsSettings.jsx index 4fcc13a..1150b9f 100644 --- a/src/components/CredentialsSettings.jsx +++ b/src/components/CredentialsSettings.jsx @@ -4,6 +4,7 @@ import { Input } from './ui/input'; import { Key, Plus, Trash2, Eye, EyeOff, Copy, Check, Github, ExternalLink } from 'lucide-react'; import { useVersionCheck } from '../hooks/useVersionCheck'; import { version } from '../../package.json'; +import { authenticatedFetch } from '../utils/api'; function CredentialsSettings() { const [apiKeys, setApiKeys] = useState([]); @@ -29,19 +30,14 @@ function CredentialsSettings() { const fetchData = async () => { try { setLoading(true); - const token = localStorage.getItem('auth-token'); // Fetch API keys - const apiKeysRes = await fetch('/api/settings/api-keys', { - headers: { 'Authorization': `Bearer ${token}` } - }); + const apiKeysRes = await authenticatedFetch('/api/settings/api-keys'); const apiKeysData = await apiKeysRes.json(); setApiKeys(apiKeysData.apiKeys || []); // Fetch GitHub credentials only - const credentialsRes = await fetch('/api/settings/credentials?type=github_token', { - headers: { 'Authorization': `Bearer ${token}` } - }); + const credentialsRes = await authenticatedFetch('/api/settings/credentials?type=github_token'); const credentialsData = await credentialsRes.json(); setGithubCredentials(credentialsData.credentials || []); } catch (error) { @@ -55,13 +51,8 @@ function CredentialsSettings() { if (!newKeyName.trim()) return; try { - const token = localStorage.getItem('auth-token'); - const res = await fetch('/api/settings/api-keys', { + const res = await authenticatedFetch('/api/settings/api-keys', { method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, body: JSON.stringify({ keyName: newKeyName }) }); @@ -81,10 +72,8 @@ function CredentialsSettings() { if (!confirm('Are you sure you want to delete this API key?')) return; try { - const token = localStorage.getItem('auth-token'); - await fetch(`/api/settings/api-keys/${keyId}`, { - method: 'DELETE', - headers: { 'Authorization': `Bearer ${token}` } + await authenticatedFetch(`/api/settings/api-keys/${keyId}`, { + method: 'DELETE' }); fetchData(); } catch (error) { @@ -94,13 +83,8 @@ function CredentialsSettings() { const toggleApiKey = async (keyId, isActive) => { try { - const token = localStorage.getItem('auth-token'); - await fetch(`/api/settings/api-keys/${keyId}/toggle`, { + await authenticatedFetch(`/api/settings/api-keys/${keyId}/toggle`, { method: 'PATCH', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, body: JSON.stringify({ isActive: !isActive }) }); fetchData(); @@ -113,13 +97,8 @@ function CredentialsSettings() { if (!newGithubName.trim() || !newGithubToken.trim()) return; try { - const token = localStorage.getItem('auth-token'); - const res = await fetch('/api/settings/credentials', { + const res = await authenticatedFetch('/api/settings/credentials', { method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, body: JSON.stringify({ credentialName: newGithubName, credentialType: 'github_token', @@ -145,10 +124,8 @@ function CredentialsSettings() { if (!confirm('Are you sure you want to delete this GitHub token?')) return; try { - const token = localStorage.getItem('auth-token'); - await fetch(`/api/settings/credentials/${credentialId}`, { - method: 'DELETE', - headers: { 'Authorization': `Bearer ${token}` } + await authenticatedFetch(`/api/settings/credentials/${credentialId}`, { + method: 'DELETE' }); fetchData(); } catch (error) { @@ -158,13 +135,8 @@ function CredentialsSettings() { const toggleGithubCredential = async (credentialId, isActive) => { try { - const token = localStorage.getItem('auth-token'); - await fetch(`/api/settings/credentials/${credentialId}/toggle`, { + await authenticatedFetch(`/api/settings/credentials/${credentialId}/toggle`, { method: 'PATCH', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, body: JSON.stringify({ isActive: !isActive }) }); fetchData(); diff --git a/src/components/ImageViewer.jsx b/src/components/ImageViewer.jsx index 15401a0..bda1458 100644 --- a/src/components/ImageViewer.jsx +++ b/src/components/ImageViewer.jsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react'; import { Button } from './ui/button'; import { X } from 'lucide-react'; +import { authenticatedFetch } from '../utils/api'; function ImageViewer({ file, onClose }) { const imagePath = `/api/projects/${file.projectName}/files/content?path=${encodeURIComponent(file.path)}`; @@ -18,16 +19,7 @@ function ImageViewer({ file, onClose }) { setError(null); setImageUrl(null); - const token = localStorage.getItem('auth-token'); - if (!token) { - setError('Missing authentication token'); - return; - } - - const response = await fetch(imagePath, { - headers: { - 'Authorization': `Bearer ${token}` - }, + const response = await authenticatedFetch(imagePath, { signal: controller.signal }); diff --git a/src/components/Settings.jsx b/src/components/Settings.jsx index 0f9ef24..d474f98 100644 --- a/src/components/Settings.jsx +++ b/src/components/Settings.jsx @@ -9,6 +9,7 @@ import ClaudeLogo from './ClaudeLogo'; import CursorLogo from './CursorLogo'; import CredentialsSettings from './CredentialsSettings'; import LoginModal from './LoginModal'; +import { authenticatedFetch } from '../utils/api'; function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { const { isDarkMode, toggleDarkMode } = useTheme(); @@ -135,14 +136,8 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { // Fetch Cursor MCP servers const fetchCursorMcpServers = async () => { try { - const token = localStorage.getItem('auth-token'); - const response = await fetch('/api/cursor/mcp', { - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - } - }); - + const response = await authenticatedFetch('/api/cursor/mcp'); + if (response.ok) { const data = await response.json(); setCursorMcpServers(data.servers || []); @@ -157,16 +152,9 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { // MCP API functions const fetchMcpServers = async () => { try { - const token = localStorage.getItem('auth-token'); - // Try to read directly from config files for complete details - const configResponse = await fetch('/api/mcp/config/read', { - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - } - }); - + const configResponse = await authenticatedFetch('/api/mcp/config/read'); + if (configResponse.ok) { const configData = await configResponse.json(); if (configData.success && configData.servers) { @@ -174,15 +162,10 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { return; } } - + // Fallback to Claude CLI - const cliResponse = await fetch('/api/mcp/cli/list', { - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - } - }); - + const cliResponse = await authenticatedFetch('/api/mcp/cli/list'); + if (cliResponse.ok) { const cliData = await cliResponse.json(); if (cliData.success && cliData.servers) { @@ -207,15 +190,10 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { return; } } - + // Final fallback to direct config reading - const response = await fetch('/api/mcp/servers?scope=user', { - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - } - }); - + const response = await authenticatedFetch('/api/mcp/servers?scope=user'); + if (response.ok) { const data = await response.json(); setMcpServers(data.servers || []); @@ -229,20 +207,14 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { const saveMcpServer = async (serverData) => { try { - const token = localStorage.getItem('auth-token'); - if (editingMcpServer) { // For editing, remove old server and add new one await deleteMcpServer(editingMcpServer.id, 'user'); } - + // Use Claude CLI to add the server - const response = await fetch('/api/mcp/cli/add', { + const response = await authenticatedFetch('/api/mcp/cli/add', { method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, body: JSON.stringify({ name: serverData.name, type: serverData.type, @@ -255,7 +227,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { env: serverData.config?.env || {} }) }); - + if (response.ok) { const result = await response.json(); if (result.success) { @@ -276,17 +248,11 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { const deleteMcpServer = async (serverId, scope = 'user') => { try { - const token = localStorage.getItem('auth-token'); - // Use Claude CLI to remove the server with proper scope - const response = await fetch(`/api/mcp/cli/remove/${serverId}?scope=${scope}`, { - method: 'DELETE', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - } + const response = await authenticatedFetch(`/api/mcp/cli/remove/${serverId}?scope=${scope}`, { + method: 'DELETE' }); - + if (response.ok) { const result = await response.json(); if (result.success) { @@ -307,15 +273,10 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { const testMcpServer = async (serverId, scope = 'user') => { try { - const token = localStorage.getItem('auth-token'); - const response = await fetch(`/api/mcp/servers/${serverId}/test?scope=${scope}`, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - } + const response = await authenticatedFetch(`/api/mcp/servers/${serverId}/test?scope=${scope}`, { + method: 'POST' }); - + if (response.ok) { const data = await response.json(); return data.testResult; @@ -332,15 +293,10 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { const discoverMcpTools = async (serverId, scope = 'user') => { try { - const token = localStorage.getItem('auth-token'); - const response = await fetch(`/api/mcp/servers/${serverId}/tools?scope=${scope}`, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - } + const response = await authenticatedFetch(`/api/mcp/servers/${serverId}/tools?scope=${scope}`, { + method: 'POST' }); - + if (response.ok) { const data = await response.json(); return data.toolsResult; @@ -441,13 +397,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { const checkClaudeAuthStatus = async () => { try { - const token = localStorage.getItem('auth-token'); - const response = await fetch('/api/cli/claude/status', { - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - } - }); + const response = await authenticatedFetch('/api/cli/claude/status'); if (response.ok) { const data = await response.json(); @@ -478,13 +428,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { const checkCursorAuthStatus = async () => { try { - const token = localStorage.getItem('auth-token'); - const response = await fetch('/api/cli/cursor/status', { - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - } - }); + const response = await authenticatedFetch('/api/cli/cursor/status'); if (response.ok) { const data = await response.json(); @@ -647,13 +591,8 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { try { if (mcpFormData.importMode === 'json') { // Use JSON import endpoint - const token = localStorage.getItem('auth-token'); - const response = await fetch('/api/mcp/cli/add-json', { + const response = await authenticatedFetch('/api/mcp/cli/add-json', { method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, body: JSON.stringify({ name: mcpFormData.name, jsonConfig: mcpFormData.jsonInput, @@ -661,7 +600,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { projectPath: mcpFormData.projectPath }) }); - + if (response.ok) { const result = await response.json(); if (result.success) { diff --git a/src/utils/api.js b/src/utils/api.js index 4ae75a7..9d4220a 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -1,15 +1,16 @@ // Utility function for authenticated API calls export const authenticatedFetch = (url, options = {}) => { + const isPlatform = import.meta.env.VITE_IS_PLATFORM === 'true'; const token = localStorage.getItem('auth-token'); - + const defaultHeaders = { 'Content-Type': 'application/json', }; - - if (token) { + + if (!isPlatform && token) { defaultHeaders['Authorization'] = `Bearer ${token}`; } - + return fetch(url, { ...options, headers: {