From 90847e7cb684249ef8e97d42d6835ef465908199 Mon Sep 17 00:00:00 2001 From: Dustin Loring Date: Sun, 15 Dec 2024 16:30:10 -0500 Subject: [PATCH 01/14] Update ConnectionsTab.tsx checks to make sure the entered username and token is correct before accepting it. Let you disconnect also. --- .../settings/connections/ConnectionsTab.tsx | 123 +++++++++++++++--- 1 file changed, 104 insertions(+), 19 deletions(-) diff --git a/app/components/settings/connections/ConnectionsTab.tsx b/app/components/settings/connections/ConnectionsTab.tsx index 4fe43d96e..760436712 100644 --- a/app/components/settings/connections/ConnectionsTab.tsx +++ b/app/components/settings/connections/ConnectionsTab.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { toast } from 'react-toastify'; import Cookies from 'js-cookie'; import { logStore } from '~/lib/stores/logs'; @@ -6,16 +6,76 @@ import { logStore } from '~/lib/stores/logs'; export default function ConnectionsTab() { const [githubUsername, setGithubUsername] = useState(Cookies.get('githubUsername') || ''); const [githubToken, setGithubToken] = useState(Cookies.get('githubToken') || ''); + const [isConnected, setIsConnected] = useState(false); + const [isVerifying, setIsVerifying] = useState(false); - const handleSaveConnection = () => { - Cookies.set('githubUsername', githubUsername); - Cookies.set('githubToken', githubToken); - logStore.logSystem('GitHub connection settings updated', { - username: githubUsername, - hasToken: !!githubToken, - }); - toast.success('GitHub credentials saved successfully!'); - Cookies.set('git:github.com', JSON.stringify({ username: githubToken, password: 'x-oauth-basic' })); + useEffect(() => { + // Check if credentials exist and verify them + if (githubUsername && githubToken) { + verifyGitHubCredentials(); + } + }, []); + + const verifyGitHubCredentials = async () => { + setIsVerifying(true); + try { + const response = await fetch('https://api.github.com/user', { + headers: { + Authorization: `Bearer ${githubToken}`, + }, + }); + + if (response.ok) { + const data = await response.json(); + if (data.login === githubUsername) { + setIsConnected(true); + return true; + } + } + setIsConnected(false); + return false; + } catch (error) { + console.error('Error verifying GitHub credentials:', error); + setIsConnected(false); + return false; + } finally { + setIsVerifying(false); + } + }; + + const handleSaveConnection = async () => { + if (!githubUsername || !githubToken) { + toast.error('Please provide both GitHub username and token'); + return; + } + + setIsVerifying(true); + const isValid = await verifyGitHubCredentials(); + + if (isValid) { + Cookies.set('githubUsername', githubUsername); + Cookies.set('githubToken', githubToken); + logStore.logSystem('GitHub connection settings updated', { + username: githubUsername, + hasToken: !!githubToken, + }); + toast.success('GitHub credentials verified and saved successfully!'); + Cookies.set('git:github.com', JSON.stringify({ username: githubToken, password: 'x-oauth-basic' })); + setIsConnected(true); + } else { + toast.error('Invalid GitHub credentials. Please check your username and token.'); + } + }; + + const handleDisconnect = () => { + Cookies.remove('githubUsername'); + Cookies.remove('githubToken'); + Cookies.remove('git:github.com'); + setGithubUsername(''); + setGithubToken(''); + setIsConnected(false); + logStore.logSystem('GitHub connection removed'); + toast.success('GitHub connection removed successfully!'); }; return ( @@ -28,7 +88,8 @@ export default function ConnectionsTab() { type="text" value={githubUsername} onChange={(e) => setGithubUsername(e.target.value)} - className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" + disabled={isVerifying} + className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor disabled:opacity-50" />
@@ -37,17 +98,41 @@ export default function ConnectionsTab() { type="password" value={githubToken} onChange={(e) => setGithubToken(e.target.value)} - className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" + disabled={isVerifying} + className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor disabled:opacity-50" />
-
- +
+ {!isConnected ? ( + + ) : ( + + )} + {isConnected && ( + +
+ Connected to GitHub + + )}
); From 21bfcfcec4f4e3ad486f09bbe6b18744dcd1e0fa Mon Sep 17 00:00:00 2001 From: Dustin Loring Date: Sun, 15 Dec 2024 16:34:08 -0500 Subject: [PATCH 02/14] quick fix --- app/components/settings/connections/ConnectionsTab.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/components/settings/connections/ConnectionsTab.tsx b/app/components/settings/connections/ConnectionsTab.tsx index 760436712..fb0dadf7c 100644 --- a/app/components/settings/connections/ConnectionsTab.tsx +++ b/app/components/settings/connections/ConnectionsTab.tsx @@ -3,6 +3,12 @@ import { toast } from 'react-toastify'; import Cookies from 'js-cookie'; import { logStore } from '~/lib/stores/logs'; +interface GitHubUserResponse { + login: string; + id: number; + [key: string]: any; // for other properties we don't explicitly need +} + export default function ConnectionsTab() { const [githubUsername, setGithubUsername] = useState(Cookies.get('githubUsername') || ''); const [githubToken, setGithubToken] = useState(Cookies.get('githubToken') || ''); @@ -26,7 +32,7 @@ export default function ConnectionsTab() { }); if (response.ok) { - const data = await response.json(); + const data = (await response.json()) as GitHubUserResponse; if (data.login === githubUsername) { setIsConnected(true); return true; From 356715ccd7cc359efa83ebef183724fe061ae768 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Mon, 16 Dec 2024 23:55:51 +0530 Subject: [PATCH 03/14] minor bugfix --- app/commit.json | 2 +- .../settings/features/FeaturesTab.tsx | 4 +- app/components/sidebar/HistoryItem.tsx | 51 +++++++++++-------- app/lib/runtime/action-runner.ts | 7 +-- 4 files changed, 37 insertions(+), 27 deletions(-) diff --git a/app/commit.json b/app/commit.json index b3f011d2b..d1a303367 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "49b02dd885919e24a201f07b1a7b0fd0371b4f85" , "version": "0.0.1" } +{ "commit": "25e6bb3e848b67c9337f1cca2779f1af6ddc8fed" } diff --git a/app/components/settings/features/FeaturesTab.tsx b/app/components/settings/features/FeaturesTab.tsx index bad8850ef..0af3d401c 100644 --- a/app/components/settings/features/FeaturesTab.tsx +++ b/app/components/settings/features/FeaturesTab.tsx @@ -65,7 +65,9 @@ export default function FeaturesTab() { className="flex-1 p-2 ml-auto rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary focus:outline-none focus:ring-2 focus:ring-bolt-elements-focus transition-all text-sm min-w-[100px]" > {PromptLibrary.getList().map((x) => ( - + ))}
diff --git a/app/components/sidebar/HistoryItem.tsx b/app/components/sidebar/HistoryItem.tsx index b228edbb7..cd5c8b16d 100644 --- a/app/components/sidebar/HistoryItem.tsx +++ b/app/components/sidebar/HistoryItem.tsx @@ -4,6 +4,7 @@ import * as Dialog from '@radix-ui/react-dialog'; import { type ChatHistoryItem } from '~/lib/persistence'; import WithTooltip from '~/components/ui/Tooltip'; import { useEditChatDescription } from '~/lib/hooks'; +import { forwardRef, type ForwardedRef } from 'react'; interface HistoryItemProps { item: ChatHistoryItem; @@ -103,25 +104,31 @@ export function HistoryItem({ item, onDelete, onDuplicate, exportChat }: History ); } -const ChatActionButton = ({ - toolTipContent, - icon, - className, - onClick, -}: { - toolTipContent: string; - icon: string; - className?: string; - onClick: (event: React.MouseEvent) => void; - btnTitle?: string; -}) => { - return ( - - - - - )} - + providerKey={key} + provider={provider} + credentials={credentials[key]} + isExpanded={expandedProviders[key]} + onToggle={() => toggleProvider(key)} + onUpdateCredentials={(updates) => updateProviderCredentials(key, updates)} + onSave={() => handleSaveConnection(key)} + onDisconnect={() => handleDisconnect(key)} + /> ))} ); diff --git a/app/components/settings/connections/ProviderCard.tsx b/app/components/settings/connections/ProviderCard.tsx new file mode 100644 index 000000000..83dc44b0f --- /dev/null +++ b/app/components/settings/connections/ProviderCard.tsx @@ -0,0 +1,120 @@ +import React from 'react'; +import type { GitProvider, ProviderCredentials } from '~/utils/gitProviders'; + +interface ProviderCardProps { + providerKey: string; + provider: GitProvider; + credentials: ProviderCredentials; + isExpanded: boolean; + onToggle: () => void; + onUpdateCredentials: (updates: Partial) => void; + onSave: () => void; + onDisconnect: () => void; +} + +export function ProviderCard({ + providerKey, + provider, + credentials, + isExpanded, + onToggle, + onUpdateCredentials, + onSave, + onDisconnect, +}: ProviderCardProps) { + return ( +
+
+
+

+ {provider.title} Connection +

+ {credentials.username && ( + + ({credentials.username}) + + )} +
+
+ {credentials.isConnected && ( +
+
+ Connected +
+ )} +
+
+
+
+
+ + {isExpanded && ( +
+
+

{provider.instructions}

+
    + {provider.tokenSetupSteps.map((step, index) => ( +
  • {step}
  • + ))} +
+
+ +
+
+ + onUpdateCredentials({ username: e.target.value })} + disabled={credentials.isVerifying} + className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" + /> +
+
+ + onUpdateCredentials({ token: e.target.value })} + disabled={credentials.isVerifying} + className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" + /> +
+
+ +
+ {!credentials.isConnected ? ( + + ) : ( + + )} +
+
+ )} +
+ ); + } \ No newline at end of file diff --git a/app/lib/hooks/useCredentials.ts b/app/lib/hooks/useCredentials.ts index b6abbba6e..569ca0a23 100644 --- a/app/lib/hooks/useCredentials.ts +++ b/app/lib/hooks/useCredentials.ts @@ -1,6 +1,7 @@ import { toast } from 'react-toastify'; import Cookies from 'js-cookie'; import type { GitAuth } from 'isomorphic-git'; +import { logStore } from '../stores/logs'; let masterKey: CryptoKey | null = null; @@ -33,7 +34,7 @@ const initializeMasterKey = async (): Promise => { return true; } catch (error) { - console.error('Failed to initialize master key:', error); + logStore.logError('Failed to initialize master key:', error); return false; } }; @@ -77,7 +78,7 @@ const decrypt = async (encryptedText: string): Promise => { return decoder.decode(decryptedData); } catch (error) { - console.error('Decryption failed:', error); + logStore.logError('Decryption failed:', error); throw error; } }; @@ -117,7 +118,7 @@ const getLegacyCredentials = async (domain: string): Promise => return { username, password: token }; } catch (error) { - console.error('Failed to decrypt legacy credentials:', error); + logStore.logError('Failed to decrypt legacy credentials:', error); const provider = domain.split('.')[0]; Cookies.remove(`${provider}Username`); @@ -148,15 +149,15 @@ const migrateLegacyCredentials = async (domain: string, auth: GitAuth): Promise< legacyKeys.forEach((key) => { if (Cookies.get(key)) { Cookies.remove(key); - console.log(`Removed legacy cookie: ${key}`); + logStore.logSystem(`Removed legacy cookie: ${key}`); } }); - console.log(`Successfully migrated ${provider} credentials to new format and cleaned up legacy data`); + logStore.logSystem(`Successfully migrated ${provider} credentials to new format and cleaned up legacy data`); return true; } catch (error) { - console.error('Failed to migrate legacy credentials:', error); + logStore.logError('Failed to migrate legacy credentials:', error); return false; } }; @@ -179,7 +180,7 @@ const getNewFormatCredentials = async (domain: string): Promise return { username, password }; } catch (error) { - console.error('Failed to parse or decrypt Git Cookie:', error); + logStore.logError('Failed to parse or decrypt Git Cookie:', error); Cookies.remove(domain); return null; @@ -220,10 +221,10 @@ const saveGitAuth = async (url: string, auth: GitAuth) => { const domain = getDomain(url); - if (!auth.username || !auth.password) { - toast.error('Username and token are required'); - return; - } + // if (!auth.username || !auth.password) { + // toast.error('Username and token are required'); + // return; + // } try { const encryptedCreds = await encrypt( @@ -233,11 +234,28 @@ const saveGitAuth = async (url: string, auth: GitAuth) => { }), ); Cookies.set(domain, encryptedCreds); - toast.success(`${domain} credentials saved successfully!`); + logStore.logSystem(`${domain} connection settings updated`, { + username: auth.username, + hasToken: !!auth.password, + }); + toast.success(`${domain} credentials verified and saved successfully!`); + } catch (error) { + logStore.logError('Failed to encrypt credentials:', error); + toast.error('Failed to save credentials securely'); + } +}; + +const removeGitAuth = async (url: string) => { + const domain = getDomain(url); + + try { + Cookies.remove(domain); + logStore.logSystem(`${domain} connection removed`); + toast.success(`${domain} connection removed successfully!`); } catch (error) { - console.error('Failed to encrypt credentials:', error); + logStore.logError('Failed to encrypt credentials:', error); toast.error('Failed to save credentials securely'); } }; -export { lookupSavedPassword, saveGitAuth, isEncryptionInitialized, ensureEncryption }; +export { lookupSavedPassword, saveGitAuth, removeGitAuth, isEncryptionInitialized, ensureEncryption }; diff --git a/app/lib/hooks/useGitProviders.ts b/app/lib/hooks/useGitProviders.ts new file mode 100644 index 000000000..9d1f46128 --- /dev/null +++ b/app/lib/hooks/useGitProviders.ts @@ -0,0 +1,164 @@ +import { useState, useEffect } from 'react'; +import { toast } from 'react-toastify'; +import { lookupSavedPassword, saveGitAuth, ensureEncryption, removeGitAuth } from './useCredentials'; +import { gitProviders, type ProviderState, type ProviderCredentials } from '~/utils/gitProviders'; + +const initialCredentials: ProviderState = { + github: { username: '', token: '', isConnected: false, isVerifying: false }, + gitlab: { username: '', token: '', isConnected: false, isVerifying: false }, +}; + +export function useGitProviders() { + const [credentials, setCredentials] = useState(initialCredentials); + const [expandedProviders, setExpandedProviders] = useState>({}); + + useEffect(() => { + initializeEncryption(); + }, []); + + const initializeEncryption = async () => { + const success = await ensureEncryption(); + if (success) { + loadSavedCredentials(); + } + }; + + const loadSavedCredentials = async () => { + for (const [key, provider] of Object.entries(gitProviders)) { + const auth = await lookupSavedPassword(provider.url); + if (auth?.username && auth?.password) { + setCredentials(prev => ({ + ...prev, + [key]: { + ...prev[key], + username: auth.username, + token: auth.password, + isConnected: true, + }, + })); + } + } + }; + + const verifyCredentials = async (providerKey: string, username: string, token: string) => { + const provider = gitProviders[providerKey]; + setCredentials(prev => ({ + ...prev, + [providerKey]: { ...prev[providerKey], isVerifying: true }, + })); + + try { + const apiUrl = providerKey === 'github' + ? 'https://api.github.com/user' + : 'https://gitlab.com/api/v4/user'; + + // Different authorization header format for GitHub and GitLab + const headers = providerKey === 'github' + ? { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + } + : { + 'PRIVATE-TOKEN': token, + 'Content-Type': 'application/json', + Accept: 'application/json', + }; + + const response = await fetch(apiUrl, { headers }); + const data = await response.json(); + console.log('response',data) + + // Verify the response data + const isValid = response.ok && ( + (providerKey === 'github' && data.login === username) || + (providerKey === 'gitlab' && data.username === username) + ); + + setCredentials(prev => ({ + ...prev, + [providerKey]: { + ...prev[providerKey], + isConnected: isValid, + isVerifying: false, + }, + })); + + if (!isValid && response.ok) { + toast.error(`The ${provider.title} token is valid but belongs to a different user.`); + } + + return isValid; + } catch (error) { + console.error(`Error verifying ${provider.title} credentials:`, error); + setCredentials(prev => ({ + ...prev, + [providerKey]: { + ...prev[providerKey], + isConnected: false, + isVerifying: false, + }, + })); + return false; + } + }; + + const handleSaveConnection = async (providerKey: string) => { + const provider = gitProviders[providerKey]; + const { username, token } = credentials[providerKey]; + + if (!username || !token) { + toast.error(`Please provide both ${provider.title} username and token`); + return; + } + + const isValid = await verifyCredentials(providerKey, username, token); + + if (isValid) { + await saveGitAuth(provider.url, { username, password: token }); + } else { + toast.error(`Invalid ${provider.title} credentials. Please check your username and token.`); + } + }; + + const handleDisconnect = async (providerKey: string) => { + const provider = gitProviders[providerKey]; + await removeGitAuth(provider.url); + setCredentials(prev => ({ + ...prev, + [providerKey]: { + ...prev[providerKey], + username: '', + token: '', + isConnected: false, + }, + })); + }; + + const updateProviderCredentials = ( + providerKey: string, + updates: Partial + ) => { + setCredentials(prev => ({ + ...prev, + [providerKey]: { ...prev[providerKey], ...updates }, + })); + }; + + const toggleProvider = (provider: string) => { + setExpandedProviders(prev => ({ + ...prev, + [provider]: !prev[provider], + })); + }; + + return { + providers: gitProviders, + credentials, + expandedProviders, + handleSaveConnection, + handleDisconnect, + updateProviderCredentials, + toggleProvider, + }; +} \ No newline at end of file diff --git a/app/utils/gitProviders.ts b/app/utils/gitProviders.ts new file mode 100644 index 000000000..415a6ee64 --- /dev/null +++ b/app/utils/gitProviders.ts @@ -0,0 +1,42 @@ +export interface GitProvider { + url: string; + title: string; + instructions: string; + tokenSetupSteps: string[]; +} + +export interface ProviderCredentials { + username: string; + token: string; + isConnected: boolean; + isVerifying: boolean; +} + +export type ProviderState = Record; + +export const gitProviders: Record = { + github: { + url: 'github.com', + title: 'GitHub', + instructions: 'Enter your GitHub username and personal access token.', + tokenSetupSteps: [ + '1. Go to GitHub.com → Settings → Developer settings → Personal access tokens → Tokens (classic)', + '2. Generate new token (classic) with these scopes:', + ' • repo (Full control of private repositories)', + ' • workflow (Optional: Update GitHub Action workflows)', + '3. Copy the generated token and paste it here', + ], + }, + gitlab: { + url: 'gitlab.com', + title: 'GitLab', + instructions: 'To set up GitLab access:', + tokenSetupSteps: [ + '1. Go to GitLab.com → Profile Settings → Access Tokens', + '2. Create a new token with these scopes:', + ' • api (Full API access)', + ' • write_repository (Read/write access)', + '3. Copy the generated token and paste it here', + ], + }, +}; \ No newline at end of file From 300d01b9ac6a2a9317428994711b7d9d75193fef Mon Sep 17 00:00:00 2001 From: Arne Durr Date: Tue, 17 Dec 2024 14:59:40 +0100 Subject: [PATCH 12/14] Some lint cleanup --- .../settings/connections/ConnectionsTab.tsx | 1 - .../settings/connections/ProviderCard.tsx | 178 ++++++++---------- app/lib/hooks/useCredentials.ts | 7 +- app/lib/hooks/useGitProviders.ts | 65 ++++--- app/utils/gitProviders.ts | 2 +- 5 files changed, 117 insertions(+), 136 deletions(-) diff --git a/app/components/settings/connections/ConnectionsTab.tsx b/app/components/settings/connections/ConnectionsTab.tsx index 99ee88f55..0e4f8f8c6 100644 --- a/app/components/settings/connections/ConnectionsTab.tsx +++ b/app/components/settings/connections/ConnectionsTab.tsx @@ -18,7 +18,6 @@ export default function ConnectionsTab() { {Object.entries(providers).map(([key, provider]) => ( -
-
-

- {provider.title} Connection -

- {credentials.username && ( - - ({credentials.username}) - - )} -
-
- {credentials.isConnected && ( -
-
- Connected -
- )} -
-
-
-
+ return ( +
+
+
+

{provider.title} Connection

+ {credentials.username && ( + ({credentials.username}) + )} +
+
+ {credentials.isConnected && ( +
+
+ Connected +
+ )} +
+
+
+
+
+ + {isExpanded && ( +
+
+

{provider.instructions}

+
    + {provider.tokenSetupSteps.map((step, index) => ( +
  • {step}
  • + ))} +
+
+ +
+
+ + onUpdateCredentials({ username: e.target.value })} + disabled={credentials.isVerifying} + className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" + /> +
+
+ + onUpdateCredentials({ token: e.target.value })} + disabled={credentials.isVerifying} + className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" + />
- - {isExpanded && ( -
-
-

{provider.instructions}

-
    - {provider.tokenSetupSteps.map((step, index) => ( -
  • {step}
  • - ))} -
-
- -
-
- - onUpdateCredentials({ username: e.target.value })} - disabled={credentials.isVerifying} - className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" - /> -
-
- - onUpdateCredentials({ token: e.target.value })} - disabled={credentials.isVerifying} - className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" - /> -
-
- -
- {!credentials.isConnected ? ( - - ) : ( - - )} -
-
+
+ +
+ {!credentials.isConnected ? ( + + ) : ( + )}
- ); - } \ No newline at end of file +
+ )} +
+ ); +} diff --git a/app/lib/hooks/useCredentials.ts b/app/lib/hooks/useCredentials.ts index 569ca0a23..932635ab6 100644 --- a/app/lib/hooks/useCredentials.ts +++ b/app/lib/hooks/useCredentials.ts @@ -1,7 +1,7 @@ import { toast } from 'react-toastify'; import Cookies from 'js-cookie'; import type { GitAuth } from 'isomorphic-git'; -import { logStore } from '../stores/logs'; +import { logStore } from '~/lib/stores/logs'; let masterKey: CryptoKey | null = null; @@ -221,11 +221,6 @@ const saveGitAuth = async (url: string, auth: GitAuth) => { const domain = getDomain(url); - // if (!auth.username || !auth.password) { - // toast.error('Username and token are required'); - // return; - // } - try { const encryptedCreds = await encrypt( JSON.stringify({ diff --git a/app/lib/hooks/useGitProviders.ts b/app/lib/hooks/useGitProviders.ts index 9d1f46128..3c294bf89 100644 --- a/app/lib/hooks/useGitProviders.ts +++ b/app/lib/hooks/useGitProviders.ts @@ -18,6 +18,7 @@ export function useGitProviders() { const initializeEncryption = async () => { const success = await ensureEncryption(); + if (success) { loadSavedCredentials(); } @@ -26,8 +27,9 @@ export function useGitProviders() { const loadSavedCredentials = async () => { for (const [key, provider] of Object.entries(gitProviders)) { const auth = await lookupSavedPassword(provider.url); + if (auth?.username && auth?.password) { - setCredentials(prev => ({ + setCredentials((prev) => ({ ...prev, [key]: { ...prev[key], @@ -42,40 +44,39 @@ export function useGitProviders() { const verifyCredentials = async (providerKey: string, username: string, token: string) => { const provider = gitProviders[providerKey]; - setCredentials(prev => ({ + setCredentials((prev) => ({ ...prev, [providerKey]: { ...prev[providerKey], isVerifying: true }, })); try { - const apiUrl = providerKey === 'github' - ? 'https://api.github.com/user' - : 'https://gitlab.com/api/v4/user'; + const apiUrl = providerKey === 'github' ? 'https://api.github.com/user' : 'https://gitlab.com/api/v4/user'; // Different authorization header format for GitHub and GitLab - const headers = providerKey === 'github' - ? { - Authorization: `Bearer ${token}`, - 'Content-Type': 'application/json', - Accept: 'application/json', - } - : { - 'PRIVATE-TOKEN': token, - 'Content-Type': 'application/json', - Accept: 'application/json', - }; + const headers = + providerKey === 'github' + ? { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + } + : { + 'PRIVATE-TOKEN': token, + 'Content-Type': 'application/json', + Accept: 'application/json', + }; const response = await fetch(apiUrl, { headers }); const data = await response.json(); - console.log('response',data) - + console.log('response', data); + // Verify the response data - const isValid = response.ok && ( - (providerKey === 'github' && data.login === username) || - (providerKey === 'gitlab' && data.username === username) - ); - - setCredentials(prev => ({ + const isValid = + response.ok && + ((providerKey === 'github' && data.login === username) || + (providerKey === 'gitlab' && data.username === username)); + + setCredentials((prev) => ({ ...prev, [providerKey]: { ...prev[providerKey], @@ -91,7 +92,7 @@ export function useGitProviders() { return isValid; } catch (error) { console.error(`Error verifying ${provider.title} credentials:`, error); - setCredentials(prev => ({ + setCredentials((prev) => ({ ...prev, [providerKey]: { ...prev[providerKey], @@ -99,6 +100,7 @@ export function useGitProviders() { isVerifying: false, }, })); + return false; } }; @@ -124,7 +126,7 @@ export function useGitProviders() { const handleDisconnect = async (providerKey: string) => { const provider = gitProviders[providerKey]; await removeGitAuth(provider.url); - setCredentials(prev => ({ + setCredentials((prev) => ({ ...prev, [providerKey]: { ...prev[providerKey], @@ -135,18 +137,15 @@ export function useGitProviders() { })); }; - const updateProviderCredentials = ( - providerKey: string, - updates: Partial - ) => { - setCredentials(prev => ({ + const updateProviderCredentials = (providerKey: string, updates: Partial) => { + setCredentials((prev) => ({ ...prev, [providerKey]: { ...prev[providerKey], ...updates }, })); }; const toggleProvider = (provider: string) => { - setExpandedProviders(prev => ({ + setExpandedProviders((prev) => ({ ...prev, [provider]: !prev[provider], })); @@ -161,4 +160,4 @@ export function useGitProviders() { updateProviderCredentials, toggleProvider, }; -} \ No newline at end of file +} diff --git a/app/utils/gitProviders.ts b/app/utils/gitProviders.ts index 415a6ee64..2fe005ac2 100644 --- a/app/utils/gitProviders.ts +++ b/app/utils/gitProviders.ts @@ -39,4 +39,4 @@ export const gitProviders: Record = { '3. Copy the generated token and paste it here', ], }, -}; \ No newline at end of file +}; From 1c66d4e8668cdb6a27864287d82e3043ad2113c4 Mon Sep 17 00:00:00 2001 From: Arne Durr Date: Tue, 17 Dec 2024 15:51:30 +0100 Subject: [PATCH 13/14] fixed type errors in useGitProviders --- app/lib/hooks/useGitProviders.ts | 45 ++++++++++++++++---------------- app/utils/gitProviders.ts | 8 ++++++ 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/app/lib/hooks/useGitProviders.ts b/app/lib/hooks/useGitProviders.ts index 3c294bf89..a94f39e0a 100644 --- a/app/lib/hooks/useGitProviders.ts +++ b/app/lib/hooks/useGitProviders.ts @@ -1,7 +1,7 @@ import { useState, useEffect } from 'react'; import { toast } from 'react-toastify'; import { lookupSavedPassword, saveGitAuth, ensureEncryption, removeGitAuth } from './useCredentials'; -import { gitProviders, type ProviderState, type ProviderCredentials } from '~/utils/gitProviders'; +import { gitProviders, type ProviderState, type ProviderCredentials, type GitHubUser, type GitLabUser } from '~/utils/gitProviders'; const initialCredentials: ProviderState = { github: { username: '', token: '', isConnected: false, isVerifying: false }, @@ -33,8 +33,8 @@ export function useGitProviders() { ...prev, [key]: { ...prev[key], - username: auth.username, - token: auth.password, + username: auth.username || '', + token: auth.password || '', isConnected: true, }, })); @@ -53,34 +53,35 @@ export function useGitProviders() { const apiUrl = providerKey === 'github' ? 'https://api.github.com/user' : 'https://gitlab.com/api/v4/user'; // Different authorization header format for GitHub and GitLab - const headers = - providerKey === 'github' - ? { - Authorization: `Bearer ${token}`, - 'Content-Type': 'application/json', - Accept: 'application/json', - } - : { - 'PRIVATE-TOKEN': token, - 'Content-Type': 'application/json', - Accept: 'application/json', - }; - - const response = await fetch(apiUrl, { headers }); + let headers; + if (providerKey === 'github') { + headers = { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }; + } else { + headers = { + 'PRIVATE-TOKEN': token, + 'Content-Type': 'application/json', + Accept: 'application/json', + }; + } + + const response = await fetch(apiUrl, { headers: headers }); const data = await response.json(); - console.log('response', data); // Verify the response data const isValid = - response.ok && - ((providerKey === 'github' && data.login === username) || - (providerKey === 'gitlab' && data.username === username)); + response.ok && + ((providerKey === 'github' && (data as GitHubUser).login === username) || + (providerKey === 'gitlab' && (data as GitLabUser).username === username)); setCredentials((prev) => ({ ...prev, [providerKey]: { ...prev[providerKey], - isConnected: isValid, + isConnected: !!isValid, isVerifying: false, }, })); diff --git a/app/utils/gitProviders.ts b/app/utils/gitProviders.ts index 2fe005ac2..880de2ba9 100644 --- a/app/utils/gitProviders.ts +++ b/app/utils/gitProviders.ts @@ -12,6 +12,14 @@ export interface ProviderCredentials { isVerifying: boolean; } +export interface GitHubUser { + login: string; +} + +export interface GitLabUser { + username: string; +} + export type ProviderState = Record; export const gitProviders: Record = { From 761f142b18fdf80620d2cbc94a8e7e26ae133a4d Mon Sep 17 00:00:00 2001 From: Arne Durr Date: Tue, 17 Dec 2024 15:57:50 +0100 Subject: [PATCH 14/14] Again some lint cleanup --- app/lib/hooks/useGitProviders.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/app/lib/hooks/useGitProviders.ts b/app/lib/hooks/useGitProviders.ts index a94f39e0a..782bdaa68 100644 --- a/app/lib/hooks/useGitProviders.ts +++ b/app/lib/hooks/useGitProviders.ts @@ -1,7 +1,13 @@ import { useState, useEffect } from 'react'; import { toast } from 'react-toastify'; import { lookupSavedPassword, saveGitAuth, ensureEncryption, removeGitAuth } from './useCredentials'; -import { gitProviders, type ProviderState, type ProviderCredentials, type GitHubUser, type GitLabUser } from '~/utils/gitProviders'; +import { + gitProviders, + type ProviderState, + type ProviderCredentials, + type GitHubUser, + type GitLabUser, +} from '~/utils/gitProviders'; const initialCredentials: ProviderState = { github: { username: '', token: '', isConnected: false, isVerifying: false }, @@ -54,6 +60,7 @@ export function useGitProviders() { // Different authorization header format for GitHub and GitLab let headers; + if (providerKey === 'github') { headers = { Authorization: `Bearer ${token}`, @@ -68,14 +75,14 @@ export function useGitProviders() { }; } - const response = await fetch(apiUrl, { headers: headers }); + const response = await fetch(apiUrl, { headers }); const data = await response.json(); // Verify the response data const isValid = - response.ok && - ((providerKey === 'github' && (data as GitHubUser).login === username) || - (providerKey === 'gitlab' && (data as GitLabUser).username === username)); + response.ok && + ((providerKey === 'github' && (data as GitHubUser).login === username) || + (providerKey === 'gitlab' && (data as GitLabUser).username === username)); setCredentials((prev) => ({ ...prev,