feat(projects): add workspace path security validation and align github credentials implementation across components

This commit is contained in:
simos
2025-11-04 09:29:21 +00:00
parent 0181883c8a
commit 255aed0b01
3 changed files with 190 additions and 31 deletions

View File

@@ -33,11 +33,11 @@ function ApiKeysSettings() {
setApiKeys(apiKeysData.apiKeys || []);
// Fetch GitHub tokens
const githubRes = await fetch('/api/settings/github-tokens', {
const githubRes = await fetch('/api/settings/credentials?type=github_token', {
headers: { 'Authorization': `Bearer ${token}` }
});
const githubData = await githubRes.json();
setGithubTokens(githubData.tokens || []);
setGithubTokens(githubData.credentials || []);
} catch (error) {
console.error('Error fetching settings:', error);
} finally {
@@ -108,15 +108,16 @@ function ApiKeysSettings() {
try {
const token = localStorage.getItem('auth-token');
const res = await fetch('/api/settings/github-tokens', {
const res = await fetch('/api/settings/credentials', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
tokenName: newTokenName,
githubToken: newGithubToken
credentialName: newTokenName,
credentialType: 'github_token',
credentialValue: newGithubToken
})
});
@@ -137,7 +138,7 @@ function ApiKeysSettings() {
try {
const token = localStorage.getItem('auth-token');
await fetch(`/api/settings/github-tokens/${tokenId}`, {
await fetch(`/api/settings/credentials/${tokenId}`, {
method: 'DELETE',
headers: { 'Authorization': `Bearer ${token}` }
});
@@ -150,7 +151,7 @@ function ApiKeysSettings() {
const toggleGithubToken = async (tokenId, isActive) => {
try {
const token = localStorage.getItem('auth-token');
await fetch(`/api/settings/github-tokens/${tokenId}/toggle`, {
await fetch(`/api/settings/credentials/${tokenId}/toggle`, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${token}`,
@@ -349,7 +350,7 @@ function ApiKeysSettings() {
className="flex items-center justify-between p-3 border rounded-lg"
>
<div className="flex-1">
<div className="font-medium">{token.token_name}</div>
<div className="font-medium">{token.credential_name}</div>
<div className="text-xs text-muted-foreground mt-1">
Added: {new Date(token.created_at).toLocaleDateString()}
</div>

View File

@@ -13,7 +13,7 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
const [workspacePath, setWorkspacePath] = useState('');
const [githubUrl, setGithubUrl] = useState('');
const [selectedGithubToken, setSelectedGithubToken] = useState('');
const [useExistingToken, setUseExistingToken] = useState(true);
const [tokenMode, setTokenMode] = useState('stored'); // 'stored' | 'new' | 'none'
const [newGithubToken, setNewGithubToken] = useState('');
// UI state
@@ -44,10 +44,10 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
const loadGithubTokens = async () => {
try {
setLoadingTokens(true);
const response = await api.get('/settings/github-tokens');
const response = await api.get('/settings/credentials?type=github_token');
const data = await response.json();
const activeTokens = (data.tokens || []).filter(t => t.is_active);
const activeTokens = (data.credentials || []).filter(t => t.is_active);
setAvailableTokens(activeTokens);
// Auto-select first token if available
@@ -122,9 +122,9 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
if (workspaceType === 'new' && githubUrl) {
payload.githubUrl = githubUrl.trim();
if (useExistingToken && selectedGithubToken) {
if (tokenMode === 'stored' && selectedGithubToken) {
payload.githubTokenId = parseInt(selectedGithubToken);
} else if (!useExistingToken && newGithubToken) {
} else if (tokenMode === 'new' && newGithubToken) {
payload.newGithubToken = newGithubToken.trim();
}
}
@@ -364,9 +364,9 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
{/* Token Selection Tabs */}
<div className="grid grid-cols-3 gap-2 mb-4">
<button
onClick={() => setUseExistingToken(true)}
onClick={() => setTokenMode('stored')}
className={`px-3 py-2 text-sm font-medium rounded-lg transition-colors ${
useExistingToken
tokenMode === 'stored'
? 'bg-blue-500 text-white'
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
}`}
@@ -374,9 +374,9 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
Stored Token
</button>
<button
onClick={() => setUseExistingToken(false)}
onClick={() => setTokenMode('new')}
className={`px-3 py-2 text-sm font-medium rounded-lg transition-colors ${
!useExistingToken && (selectedGithubToken || newGithubToken)
tokenMode === 'new'
? 'bg-blue-500 text-white'
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
}`}
@@ -385,12 +385,12 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
</button>
<button
onClick={() => {
setUseExistingToken(true);
setTokenMode('none');
setSelectedGithubToken('');
setNewGithubToken('');
}}
className={`px-3 py-2 text-sm font-medium rounded-lg transition-colors ${
!selectedGithubToken && !newGithubToken
tokenMode === 'none'
? 'bg-green-500 text-white'
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
}`}
@@ -399,7 +399,7 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
</button>
</div>
{useExistingToken ? (
{tokenMode === 'stored' ? (
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Select Token
@@ -412,12 +412,12 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
<option value="">-- Select a token --</option>
{availableTokens.map((token) => (
<option key={token.id} value={token.id}>
{token.token_name}
{token.credential_name}
</option>
))}
</select>
</div>
) : (
) : tokenMode === 'new' ? (
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
GitHub Token
@@ -433,7 +433,7 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
This token will be used only for this operation
</p>
</div>
)}
) : null}
</>
) : (
<div className="space-y-4">
@@ -498,9 +498,9 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
<div className="flex justify-between text-sm">
<span className="text-gray-600 dark:text-gray-400">Authentication:</span>
<span className="text-xs text-gray-900 dark:text-white">
{useExistingToken && selectedGithubToken
? `Using stored token: ${availableTokens.find(t => t.id.toString() === selectedGithubToken)?.token_name || 'Unknown'}`
: newGithubToken
{tokenMode === 'stored' && selectedGithubToken
? `Using stored token: ${availableTokens.find(t => t.id.toString() === selectedGithubToken)?.credential_name || 'Unknown'}`
: tokenMode === 'new' && newGithubToken
? 'Using provided token'
: 'No authentication'}
</span>