fix: settings api calls that would fail.

This commit is contained in:
simos
2025-11-17 13:58:58 +01:00
parent 6219c273a2
commit f91f9f702d
7 changed files with 61 additions and 194 deletions

View File

@@ -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
}

View File

@@ -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();

View File

@@ -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
});

View File

@@ -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();

View File

@@ -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
});

View File

@@ -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,13 +136,7 @@ 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();
@@ -157,15 +152,8 @@ 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();
@@ -176,12 +164,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) {
}
// 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();
@@ -209,12 +192,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) {
}
// 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();
@@ -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,
@@ -276,15 +248,9 @@ 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) {
@@ -307,13 +273,8 @@ 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) {
@@ -332,13 +293,8 @@ 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) {
@@ -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,

View File

@@ -1,12 +1,13 @@
// 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}`;
}