refactor: remove API key from clone functionality

This commit is contained in:
Sam Chau
2024-09-07 21:37:25 +09:30
parent 77a4411ca7
commit 03059638ce
6 changed files with 1152 additions and 1177 deletions

View File

@@ -5,7 +5,6 @@ from .branches.manager import Branch_Manager
from .operations.manager import GitOperations
from .repo.unlink import unlink_repository
from .repo.clone import clone_repository
from .auth.authenticate import validate_git_token
from ..settings_utils import save_settings
import logging
@@ -18,6 +17,7 @@ REPO_PATH = '/app/data/db'
branch_manager = Branch_Manager(REPO_PATH)
git_operations = GitOperations(REPO_PATH)
@bp.route('/clone', methods=['POST'])
def handle_clone_repository():
try:
@@ -25,25 +25,21 @@ def handle_clone_repository():
logger.info(f"Received new settings: {new_settings}")
# Validate required fields
required_fields = ['gitRepo', 'gitToken']
for field in required_fields:
if field not in new_settings:
logger.error(f"Missing required field: {field}")
return jsonify({"error": f"Missing required field: {field}"}), 400
# Validate Git token
if not validate_git_token(new_settings['gitRepo'], new_settings['gitToken']):
logger.warning("Invalid Git token provided")
return jsonify({"error": "Invalid Git token. Please check your credentials and try again."}), 401
if 'gitRepo' not in new_settings:
logger.error("Missing required field: gitRepo")
return jsonify({"error": "Missing required field: gitRepo"}), 400
# Attempt to clone the repository
success, message = clone_repository(new_settings['gitRepo'], REPO_PATH, new_settings['gitToken'])
success, message = clone_repository(new_settings['gitRepo'], REPO_PATH)
if success:
# Only save the settings if the clone was successful
save_settings(new_settings)
# Only save the repository URL if the clone was successful
save_settings({'gitRepo': new_settings['gitRepo']})
logger.info("Settings updated and repository cloned successfully")
return jsonify({"message": "Repository cloned and settings updated successfully"}), 200
return jsonify({
"message":
"Repository cloned and settings updated successfully"
}), 200
else:
logger.error(f"Failed to clone repository: {message}")
return jsonify({"error": message}), 400
@@ -64,11 +60,13 @@ def get_status():
logger.error(f"Failed to retrieve git status: {message}")
return jsonify({'success': False, 'error': message}), 400
@bp.route('/branch', methods=['POST'])
def create_branch():
branch_name = request.json.get('name')
base_branch = request.json.get('base', 'main')
logger.debug(f"Received request to create branch {branch_name} from {base_branch}")
logger.debug(
f"Received request to create branch {branch_name} from {base_branch}")
success, result = branch_manager.create(branch_name, base_branch)
if success:
logger.debug(f"Successfully created branch: {branch_name}")
@@ -77,6 +75,7 @@ def create_branch():
logger.error(f"Failed to create branch: {result}")
return jsonify({'success': False, 'error': result}), 400
@bp.route('/branches', methods=['GET'])
def get_branches():
logger.debug("Received request for branches")
@@ -88,6 +87,7 @@ def get_branches():
logger.error(f"Failed to retrieve branches: {result}")
return jsonify({'success': False, 'error': result}), 400
@bp.route('/checkout', methods=['POST'])
def checkout_branch():
branch_name = request.json.get('branch')
@@ -100,6 +100,7 @@ def checkout_branch():
logger.error(f"Failed to checkout branch: {result}")
return jsonify({'success': False, 'error': result}), 400
@bp.route('/branch/<branch_name>', methods=['DELETE'])
def delete_branch(branch_name):
logger.debug(f"Received request to delete branch: {branch_name}")
@@ -110,25 +111,31 @@ def delete_branch(branch_name):
else:
logger.error(f"Failed to delete branch: {result}")
return jsonify({'success': False, 'error': result}), 400
@bp.route('/branch/push', methods=['POST'])
def push_branch():
data = request.json
logger.debug(f"Received request to push branch: {data}")
branch_name = data.get('branch')
if not branch_name:
return jsonify({"success": False, "error": "Branch name is required"}), 400
return jsonify({
"success": False,
"error": "Branch name is required"
}), 400
success, result = branch_manager.push(branch_name)
if success:
return jsonify({"success": True, "data": result}), 200
else:
return jsonify({"success": False, "error": result["error"]}), 500
@bp.route('/push', methods=['POST'])
def push_files():
files = request.json.get('files', [])
user_commit_message = request.json.get('commit_message', "Commit and push staged files")
user_commit_message = request.json.get('commit_message',
"Commit and push staged files")
logger.debug(f"Received request to push files: {files}")
commit_message = generate_commit_message(user_commit_message, files)
success, message = git_operations.push(files, commit_message)
@@ -139,11 +146,15 @@ def push_files():
logger.error(f"Error pushing files: {message}")
return jsonify({'success': False, 'error': message}), 400
@bp.route('/revert', methods=['POST'])
def revert_file():
file_path = request.json.get('file_path')
if not file_path:
return jsonify({'success': False, 'error': "File path is required."}), 400
return jsonify({
'success': False,
'error': "File path is required."
}), 400
success, message = git_operations.revert(file_path)
if success:
return jsonify({'success': True, 'message': message}), 200
@@ -151,6 +162,7 @@ def revert_file():
logger.error(f"Error reverting file: {message}")
return jsonify({'success': False, 'error': message}), 400
@bp.route('/revert-all', methods=['POST'])
def revert_all():
success, message = git_operations.revert_all()
@@ -160,11 +172,15 @@ def revert_all():
logger.error(f"Error reverting all changes: {message}")
return jsonify({'success': False, 'error': message}), 400
@bp.route('/file', methods=['DELETE'])
def delete_file():
file_path = request.json.get('file_path')
if not file_path:
return jsonify({'success': False, 'error': "File path is required."}), 400
return jsonify({
'success': False,
'error': "File path is required."
}), 400
success, message = git_operations.delete(file_path)
if success:
return jsonify({'success': True, 'message': message}), 200
@@ -172,6 +188,7 @@ def delete_file():
logger.error(f"Error deleting file: {message}")
return jsonify({'success': False, 'error': message}), 400
@bp.route('/pull', methods=['POST'])
def pull_branch():
branch_name = request.json.get('branch')
@@ -182,6 +199,7 @@ def pull_branch():
logger.error(f"Error pulling branch: {message}")
return jsonify({'success': False, 'error': message}), 400
@bp.route('/diff', methods=['POST'])
def diff_file():
file_path = request.json.get('file_path')
@@ -190,8 +208,13 @@ def diff_file():
logger.debug(f"Diff for file {file_path}: {diff}")
return jsonify({'success': True, 'diff': diff if diff else ""}), 200
except Exception as e:
logger.error(f"Error getting diff for file {file_path}: {str(e)}", exc_info=True)
return jsonify({'success': False, 'error': f"Error getting diff for file: {str(e)}"}), 400
logger.error(f"Error getting diff for file {file_path}: {str(e)}",
exc_info=True)
return jsonify({
'success': False,
'error': f"Error getting diff for file: {str(e)}"
}), 400
@bp.route('/stage', methods=['POST'])
def handle_stage_files():
@@ -202,6 +225,7 @@ def handle_stage_files():
else:
return jsonify({'success': False, 'error': message}), 400
@bp.route('/unlink', methods=['POST'])
def unlink():
data = request.get_json()
@@ -212,6 +236,7 @@ def unlink():
else:
return jsonify({'success': False, 'error': message}), 400
def generate_commit_message(user_message, files):
file_changes = []
for file in files:
@@ -223,4 +248,4 @@ def generate_commit_message(user_message, files):
file_changes.append(f"Update: {file}")
commit_message = f"{user_message}\n\nChanges:\n" + "\n".join(file_changes)
return commit_message
return commit_message

View File

@@ -10,24 +10,21 @@ from ..auth.authenticate import validate_git_token
logger = logging.getLogger(__name__)
def clone_repository(repo_url, repo_path, git_token):
try:
if not validate_git_token(repo_url, git_token):
logger.error("Invalid Git token provided")
return False, "Invalid Git token. Please check your credentials and try again."
def clone_repository(repo_url, repo_path):
try:
temp_dir = f"{repo_path}_temp"
backup_dir = f"{repo_path}_backup"
logger.info(f"Cloning repository from {repo_url} to {temp_dir}")
auth_repo_url = repo_url.replace('https://', f'https://{git_token}:x-oauth-basic@')
try:
repo = git.Repo.clone_from(auth_repo_url, temp_dir)
repo = git.Repo.clone_from(repo_url, temp_dir)
logger.info("Repository cloned successfully")
except GitCommandError as e:
if "remote: Repository not found" in str(e):
logger.info("Repository not found. Creating a new empty repository.")
logger.info(
"Repository not found. Creating a new empty repository.")
repo = git.Repo.init(temp_dir)
repo.create_remote('origin', repo_url)
else:
@@ -37,7 +34,8 @@ def clone_repository(repo_url, repo_path, git_token):
try:
repo.head.reference
except ValueError:
logger.info("Repository is empty. Initializing with basic structure.")
logger.info(
"Repository is empty. Initializing with basic structure.")
_initialize_empty_repo(repo)
if os.path.exists(repo_path):
@@ -55,11 +53,16 @@ def clone_repository(repo_url, repo_path, git_token):
logger.info(f"Creating missing folder: {folder_name}")
os.makedirs(folder_path)
cloned_files = [f for f in os.listdir(folder_path) if f.endswith('.yml')]
cloned_files = [
f for f in os.listdir(folder_path) if f.endswith('.yml')
]
cloned_ids = set(int(f.split('.')[0]) for f in cloned_files)
if os.path.exists(backup_folder_path):
local_files = [f for f in os.listdir(backup_folder_path) if f.endswith('.yml')]
local_files = [
f for f in os.listdir(backup_folder_path)
if f.endswith('.yml')
]
for file_name in local_files:
old_file_path = os.path.join(backup_folder_path, file_name)
with open(old_file_path, 'r') as file:
@@ -90,15 +93,21 @@ def clone_repository(repo_url, repo_path, git_token):
shutil.move(backup_dir, repo_path)
return False, f"Unexpected error: {str(e)}"
def _initialize_empty_repo(repo):
# Create basic folder structure
os.makedirs(os.path.join(repo.working_tree_dir, 'regex_patterns'), exist_ok=True)
os.makedirs(os.path.join(repo.working_tree_dir, 'custom_formats'), exist_ok=True)
os.makedirs(os.path.join(repo.working_tree_dir, 'quality_profiles'), exist_ok=True)
os.makedirs(os.path.join(repo.working_tree_dir, 'regex_patterns'),
exist_ok=True)
os.makedirs(os.path.join(repo.working_tree_dir, 'custom_formats'),
exist_ok=True)
os.makedirs(os.path.join(repo.working_tree_dir, 'quality_profiles'),
exist_ok=True)
# Create a README file
with open(os.path.join(repo.working_tree_dir, 'README.md'), 'w') as f:
f.write("# Profilarr Repository\n\nThis repository contains regex patterns, custom formats and quality profiles.")
f.write(
"# Profilarr Repository\n\nThis repository contains regex patterns, custom formats and quality profiles."
)
repo.git.add(A=True)
repo.index.commit("Initial commit: Basic repository structure")
@@ -108,4 +117,6 @@ def _initialize_empty_repo(repo):
origin.push('main')
origin.push('main:main')
logger.info(f"Initialized empty repository with basic structure and pushed to main")
logger.info(
f"Initialized empty repository with basic structure and pushed to main"
)

View File

@@ -3,306 +3,336 @@ import axios from 'axios';
const API_BASE_URL = 'http://localhost:5000';
export const getRegexes = async () => {
try {
const response = await axios.get(`${API_BASE_URL}/regex`);
return response.data;
} catch (error) {
console.error('Error fetching regexes:', error);
throw error;
}
try {
const response = await axios.get(`${API_BASE_URL}/regex`);
return response.data;
} catch (error) {
console.error('Error fetching regexes:', error);
throw error;
}
};
export const saveRegex = async (regex) => {
try {
const response = await axios.post(`${API_BASE_URL}/regex`, regex);
return response.data;
} catch (error) {
console.error('Error saving regex:', error);
throw error;
}
export const saveRegex = async regex => {
try {
const response = await axios.post(`${API_BASE_URL}/regex`, regex);
return response.data;
} catch (error) {
console.error('Error saving regex:', error);
throw error;
}
};
export const updateRegex = async (id, regex) => {
try {
const response = await axios.put(`${API_BASE_URL}/regex/${id}`, regex);
return response.data;
} catch (error) {
console.error('Error updating regex:', error);
throw error;
}
try {
const response = await axios.put(`${API_BASE_URL}/regex/${id}`, regex);
return response.data;
} catch (error) {
console.error('Error updating regex:', error);
throw error;
}
};
export const deleteRegex = async (id, force = false) => {
try {
const response = await axios.delete(`${API_BASE_URL}/regex/${id}${force ? '?force=true' : ''}`, {
validateStatus: (status) => {
return status >= 200 && status < 300 || status === 400 || status === 409;
}
});
return response.data;
} catch (error) {
console.error('Error deleting regex:', error);
throw error;
}
try {
const response = await axios.delete(
`${API_BASE_URL}/regex/${id}${force ? '?force=true' : ''}`,
{
validateStatus: status => {
return (
(status >= 200 && status < 300) || status === 400 || status === 409
);
}
}
);
return response.data;
} catch (error) {
console.error('Error deleting regex:', error);
throw error;
}
};
export const getFormats = async () => {
try {
const response = await axios.get(`${API_BASE_URL}/format`);
return response.data;
} catch (error) {
console.error('Error fetching formats:', error);
throw error;
}
try {
const response = await axios.get(`${API_BASE_URL}/format`);
return response.data;
} catch (error) {
console.error('Error fetching formats:', error);
throw error;
}
};
export const saveFormat = async (format) => {
try {
const response = await axios.post(`${API_BASE_URL}/format`, format);
return response.data;
} catch (error) {
console.error('Error saving format:', error);
throw error;
}
export const saveFormat = async format => {
try {
const response = await axios.post(`${API_BASE_URL}/format`, format);
return response.data;
} catch (error) {
console.error('Error saving format:', error);
throw error;
}
};
export const updateFormat = async (id, format) => {
try {
const response = await axios.put(`${API_BASE_URL}/format/${id}`, format);
return response.data;
} catch (error) {
console.error('Error updating format:', error);
throw error;
}
try {
const response = await axios.put(`${API_BASE_URL}/format/${id}`, format);
return response.data;
} catch (error) {
console.error('Error updating format:', error);
throw error;
}
};
export const deleteFormat = async (id, force = false) => {
try {
const response = await axios.delete(`${API_BASE_URL}/format/${id}${force ? '?force=true' : ''}`, {
validateStatus: (status) => {
return status >= 200 && status < 300 || status === 400 || status === 409;
}
});
return response.data;
} catch (error) {
console.error('Error deleting format:', error);
throw error;
}
try {
const response = await axios.delete(
`${API_BASE_URL}/format/${id}${force ? '?force=true' : ''}`,
{
validateStatus: status => {
return (
(status >= 200 && status < 300) || status === 400 || status === 409
);
}
}
);
return response.data;
} catch (error) {
console.error('Error deleting format:', error);
throw error;
}
};
export const createRegex101Link = async (regexData) => {
try {
const response = await axios.post(`${API_BASE_URL}/regex/regex101`, regexData);
return response.data;
} catch (error) {
console.error('Error creating regex101 link:', error);
throw error;
}
export const createRegex101Link = async regexData => {
try {
const response = await axios.post(
`${API_BASE_URL}/regex/regex101`,
regexData
);
return response.data;
} catch (error) {
console.error('Error creating regex101 link:', error);
throw error;
}
};
export const getSettings = async () => {
try {
const response = await axios.get(`${API_BASE_URL}/settings`);
return response.data;
} catch (error) {
console.error('Error fetching settings:', error);
throw error;
}
try {
const response = await axios.get(`${API_BASE_URL}/settings`);
return response.data;
} catch (error) {
console.error('Error fetching settings:', error);
throw error;
}
};
export const getGitStatus = async () => {
try {
const response = await axios.get(`${API_BASE_URL}/git/status`);
return response.data;
} catch (error) {
console.error('Error fetching Git status:', error);
throw error;
}
try {
const response = await axios.get(`${API_BASE_URL}/git/status`);
return response.data;
} catch (error) {
console.error('Error fetching Git status:', error);
throw error;
}
};
export const getBranches = async () => {
try {
const response = await axios.get(`${API_BASE_URL}/git/branches`);
return response.data;
} catch (error) {
console.error('Error fetching branches:', error);
throw error;
}
try {
const response = await axios.get(`${API_BASE_URL}/git/branches`);
return response.data;
} catch (error) {
console.error('Error fetching branches:', error);
throw error;
}
};
export const checkoutBranch = async (branchName) => {
try {
const response = await axios.post(`${API_BASE_URL}/git/checkout`, { branch: branchName });
return response.data;
} catch (error) {
console.error('Error checking out branch:', error);
throw error;
}
export const checkoutBranch = async branchName => {
try {
const response = await axios.post(`${API_BASE_URL}/git/checkout`, {
branch: branchName
});
return response.data;
} catch (error) {
console.error('Error checking out branch:', error);
throw error;
}
};
export const createBranch = async (branchName, baseBranch) => {
try {
const response = await axios.post(`${API_BASE_URL}/git/branch`, { name: branchName, base: baseBranch });
return response.data;
} catch (error) {
console.error('Error creating branch:', error);
throw error;
}
try {
const response = await axios.post(`${API_BASE_URL}/git/branch`, {
name: branchName,
base: baseBranch
});
return response.data;
} catch (error) {
console.error('Error creating branch:', error);
throw error;
}
};
export const deleteBranch = async (branchName) => {
try {
const response = await axios.delete(`${API_BASE_URL}/git/branch/${branchName}`);
return response.data;
} catch (error) {
console.error('Error deleting branch:', error);
throw error;
}
export const deleteBranch = async branchName => {
try {
const response = await axios.delete(
`${API_BASE_URL}/git/branch/${branchName}`
);
return response.data;
} catch (error) {
console.error('Error deleting branch:', error);
throw error;
}
};
export const addFiles = async (files) => {
try {
const response = await axios.post(`${API_BASE_URL}/git/stage`, { files });
return response.data;
} catch (error) {
console.error('Error staging files:', error);
throw error;
}
export const addFiles = async files => {
try {
const response = await axios.post(`${API_BASE_URL}/git/stage`, {files});
return response.data;
} catch (error) {
console.error('Error staging files:', error);
throw error;
}
};
export const pushFiles = async (files, commitMessage) => {
try {
const response = await axios.post(`${API_BASE_URL}/git/push`, {
files,
commit_message: commitMessage
});
return response.data;
} catch (error) {
console.error('Error pushing files:', error);
throw error;
}
try {
const response = await axios.post(`${API_BASE_URL}/git/push`, {
files,
commit_message: commitMessage
});
return response.data;
} catch (error) {
console.error('Error pushing files:', error);
throw error;
}
};
export const revertFile = async (filePath) => {
try {
const response = await axios.post(`${API_BASE_URL}/git/revert`, {
file_path: filePath
});
return response.data;
} catch (error) {
console.error('Error reverting file:', error);
throw error;
}
export const revertFile = async filePath => {
try {
const response = await axios.post(`${API_BASE_URL}/git/revert`, {
file_path: filePath
});
return response.data;
} catch (error) {
console.error('Error reverting file:', error);
throw error;
}
};
export const revertAll = async () => {
try {
const response = await axios.post(`${API_BASE_URL}/git/revert-all`);
return response.data;
} catch (error) {
console.error('Error reverting all changes:', error);
throw error;
}
try {
const response = await axios.post(`${API_BASE_URL}/git/revert-all`);
return response.data;
} catch (error) {
console.error('Error reverting all changes:', error);
throw error;
}
};
export const deleteFile = async (filePath) => {
try {
const response = await axios.delete(`${API_BASE_URL}/git/file`, {
data: { file_path: filePath },
});
return response.data;
} catch (error) {
console.error('Error deleting file:', error);
return { success: false, error: 'Error deleting file' };
}
export const deleteFile = async filePath => {
try {
const response = await axios.delete(`${API_BASE_URL}/git/file`, {
data: {file_path: filePath}
});
return response.data;
} catch (error) {
console.error('Error deleting file:', error);
return {success: false, error: 'Error deleting file'};
}
};
export const pullBranch = async (branchName) => {
try {
const response = await axios.post(`${API_BASE_URL}/git/pull`, { branch: branchName });
return response.data;
} catch (error) {
console.error('Error pulling branch:', error);
throw error;
}
export const pullBranch = async branchName => {
try {
const response = await axios.post(`${API_BASE_URL}/git/pull`, {
branch: branchName
});
return response.data;
} catch (error) {
console.error('Error pulling branch:', error);
throw error;
}
};
export const getDiff = async (filePath) => {
try {
const response = await axios.post(`${API_BASE_URL}/git/diff`, { file_path: filePath });
return response.data;
} catch (error) {
console.error('Error fetching diff:', error);
throw error;
}
export const getDiff = async filePath => {
try {
const response = await axios.post(`${API_BASE_URL}/git/diff`, {
file_path: filePath
});
return response.data;
} catch (error) {
console.error('Error fetching diff:', error);
throw error;
}
};
export const cloneRepo = async (gitRepo, gitToken) => {
try {
const response = await axios.post(`${API_BASE_URL}/git/clone`, { gitRepo, gitToken });
return response.data;
} catch (error) {
console.error('Error cloning repository:', error);
throw error;
}
export const cloneRepo = async gitRepo => {
try {
const response = await axios.post(`${API_BASE_URL}/git/clone`, {
gitRepo
});
return response.data;
} catch (error) {
console.error('Error cloning repository:', error);
throw error;
}
};
export const getProfiles = async () => {
try {
const response = await axios.get(`${API_BASE_URL}/profile`);
return response.data;
} catch (error) {
console.error('Error fetching profiles:', error);
throw error;
}
try {
const response = await axios.get(`${API_BASE_URL}/profile`);
return response.data;
} catch (error) {
console.error('Error fetching profiles:', error);
throw error;
}
};
export const saveProfile = async (profile) => {
try {
const response = await axios.post(`${API_BASE_URL}/profile`, profile);
return response.data;
} catch (error) {
console.error('Error saving profile:', error);
throw error;
}
export const saveProfile = async profile => {
try {
const response = await axios.post(`${API_BASE_URL}/profile`, profile);
return response.data;
} catch (error) {
console.error('Error saving profile:', error);
throw error;
}
};
export const updateProfile = async (id, profile) => {
try {
const response = await axios.put(`${API_BASE_URL}/profile/${id}`, profile);
return response.data;
} catch (error) {
console.error('Error updating profile:', error);
throw error;
}
try {
const response = await axios.put(`${API_BASE_URL}/profile/${id}`, profile);
return response.data;
} catch (error) {
console.error('Error updating profile:', error);
throw error;
}
};
export const deleteProfile = async (id) => {
try {
const response = await axios.delete(`${API_BASE_URL}/profile/${id}`);
return response.data;
} catch (error) {
console.error('Error deleting profile:', error);
throw error;
}
export const deleteProfile = async id => {
try {
const response = await axios.delete(`${API_BASE_URL}/profile/${id}`);
return response.data;
} catch (error) {
console.error('Error deleting profile:', error);
throw error;
}
};
export const unlinkRepo = async (removeFiles = false) => {
try {
const response = await axios.post(`${API_BASE_URL}/git/unlink`, { removeFiles });
return response.data;
} catch (error) {
console.error('Error unlinking repository:', error);
throw error;
}
try {
const response = await axios.post(`${API_BASE_URL}/git/unlink`, {
removeFiles
});
return response.data;
} catch (error) {
console.error('Error unlinking repository:', error);
throw error;
}
};
export const pushBranchToRemote = async (branchName) => {
try {
const response = await axios.post(`${API_BASE_URL}/git/branch/push`, { branch: branchName });
return response.data;
} catch (error) {
console.error('Error pushing branch to remote:', error);
throw error;
}
};
export const pushBranchToRemote = async branchName => {
try {
const response = await axios.post(`${API_BASE_URL}/git/branch/push`, {
branch: branchName
});
return response.data;
} catch (error) {
console.error('Error pushing branch to remote:', error);
throw error;
}
};

View File

@@ -1,74 +0,0 @@
import React, { useState } from 'react';
import Modal from '../ui/Modal';
import { Loader } from 'lucide-react';
import { cloneRepo } from '../../api/api';
import Alert from '../ui/Alert';
const ApiKeyModal = ({ isOpen, onClose, onSubmit }) => {
const [gitRepo, setGitRepo] = useState('');
const [apiKey, setApiKey] = useState('');
const [loading, setLoading] = useState(false);
const handleSubmit = async () => {
if (!gitRepo || !apiKey) {
Alert.error("Please fill in all fields.");
return;
}
setLoading(true);
try {
const response = await cloneRepo(gitRepo, apiKey);
Alert.success(response.message || "Repository cloned successfully!");
onSubmit();
} catch (error) {
Alert.error("An unexpected error occurred while cloning the repository.");
console.error("Error cloning repository:", error);
} finally {
setLoading(false);
}
};
return (
<Modal isOpen={isOpen} onClose={onClose} title="Link Git Repository">
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">Git Repository URL:</label>
<input
type="text"
value={gitRepo}
onChange={(e) => setGitRepo(e.target.value)}
className="w-full p-2 border rounded bg-gray-900 text-gray-100 border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="https://github.com/your-repo.git"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">GitHub Access Token:</label>
<input
type="password"
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
className="w-full p-2 border rounded bg-gray-900 text-gray-100 border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Your GitHub Token"
/>
</div>
<div className="mt-6 flex justify-end">
<button
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition-colors flex items-center"
disabled={loading}
onClick={handleSubmit}
>
{loading ? (
<>
<Loader size={16} className="animate-spin mr-2" />
Cloning...
</>
) : (
'Clone Repository'
)}
</button>
</div>
</div>
</Modal>
);
};
export default ApiKeyModal;

View File

@@ -0,0 +1,71 @@
import React, {useState} from 'react';
import Modal from '../ui/Modal';
import {Loader} from 'lucide-react';
import {cloneRepo} from '../../api/api';
import Alert from '../ui/Alert';
const LinkRepoModal = ({isOpen, onClose, onSubmit}) => {
const [gitRepo, setGitRepo] = useState('');
const [loading, setLoading] = useState(false);
const handleSubmit = async () => {
if (!gitRepo) {
Alert.error('Please enter a repository URL.');
return;
}
setLoading(true);
try {
const response = await cloneRepo(gitRepo);
Alert.success(
response.message || 'Repository linked successfully!'
);
onSubmit();
} catch (error) {
Alert.error(
'An unexpected error occurred while linking the repository.'
);
console.error('Error linking repository:', error);
} finally {
setLoading(false);
}
};
return (
<Modal isOpen={isOpen} onClose={onClose} title='Link Git Repository'>
<div className='space-y-4'>
<div>
<label className='block text-sm font-medium text-gray-300 mb-2'>
Git Repository URL:
</label>
<input
type='text'
value={gitRepo}
onChange={e => setGitRepo(e.target.value)}
className='w-full p-2 border rounded bg-gray-900 text-gray-100 border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500'
placeholder='https://github.com/your-repo.git'
/>
</div>
<div className='mt-6 flex justify-end'>
<button
className='bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition-colors flex items-center'
disabled={loading}
onClick={handleSubmit}>
{loading ? (
<>
<Loader
size={16}
className='animate-spin mr-2'
/>
Linking...
</>
) : (
'Link Repository'
)}
</button>
</div>
</div>
</Modal>
);
};
export default LinkRepoModal;

File diff suppressed because it is too large Load Diff