diff --git a/backend/app/__init__.py b/backend/app/__init__.py index 47b5ce4..a522a04 100644 --- a/backend/app/__init__.py +++ b/backend/app/__init__.py @@ -1,11 +1,13 @@ -from flask import Flask +# app/__init__.py + +import os +from flask import Flask, jsonify from flask_cors import CORS from .regex import bp as regex_bp from .format import bp as format_bp -from .settings import bp as settings_bp from .profile import bp as profile_bp from .git import bp as git_bp -import os +from .settings_utils import create_empty_settings_if_not_exists, load_settings REGEX_DIR = os.path.join('data', 'db', 'regex_patterns') FORMAT_DIR = os.path.join('data', 'db', 'custom_formats') @@ -15,16 +17,22 @@ def create_app(): app = Flask(__name__) CORS(app, resources={r"/*": {"origins": "*"}}) - # Initialize directories to avoid issues with non-existing directories + # Initialize directories and create empty settings file if it doesn't exist initialize_directories() + create_empty_settings_if_not_exists() # Register Blueprints app.register_blueprint(regex_bp) app.register_blueprint(format_bp) - app.register_blueprint(settings_bp) app.register_blueprint(profile_bp) - app.register_blueprint(git_bp) - + app.register_blueprint(git_bp) + + # Add settings route + @app.route('/settings', methods=['GET']) + def handle_settings(): + settings = load_settings() + return jsonify(settings), 200 + return app def initialize_directories(): diff --git a/backend/app/git/__init__.py b/backend/app/git/__init__.py index d787683..f1956b8 100644 --- a/backend/app/git/__init__.py +++ b/backend/app/git/__init__.py @@ -4,6 +4,9 @@ from .status.diff import get_diff from .branches.branches import Branch_Manager from .operations.operations import GitOperations from .unlink_repo import unlink_repository +from .clone_repo import clone_repository +from .authenticate import validate_git_token +from ..settings_utils import save_settings import logging logger = logging.getLogger(__name__) @@ -15,6 +18,41 @@ 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: + new_settings = request.json + 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 + + # Attempt to clone the repository + success, message = clone_repository(new_settings['gitRepo'], REPO_PATH, new_settings['gitToken']) + + if success: + # Only save the settings if the clone was successful + save_settings(new_settings) + logger.info("Settings updated and repository cloned successfully") + return jsonify({"message": "Repository cloned and settings updated successfully"}), 200 + else: + logger.error(f"Failed to clone repository: {message}") + return jsonify({"error": message}), 400 + + except Exception as e: + logger.exception("Unexpected error in clone_repository") + return jsonify({"error": f"Failed to clone repository: {str(e)}"}), 500 + + @bp.route('/status', methods=['GET']) def get_status(): logger.debug("Received request for git status") diff --git a/backend/app/git/unlink_repo.py b/backend/app/git/unlink_repo.py index ab0d025..da8cadd 100644 --- a/backend/app/git/unlink_repo.py +++ b/backend/app/git/unlink_repo.py @@ -3,25 +3,31 @@ import shutil from flask import Blueprint, jsonify import logging +# Import settings utilities +from ..settings_utils import save_settings, load_settings + logger = logging.getLogger(__name__) -repo_bp = Blueprint('repository', __name__, url_prefix='/repository') - -def unlink_repository(settings_manager): +def unlink_repository(repo_path): try: + # Load current settings + settings = load_settings() + if not settings: + return False, "Settings file not found or could not be loaded" + # Remove the .git folder - git_folder = os.path.join(settings_manager.repo_path, '.git') + git_folder = os.path.join(repo_path, '.git') if os.path.exists(git_folder): shutil.rmtree(git_folder) - logger.info(f"Removed .git folder from {settings_manager.repo_path}") + logger.info(f"Removed .git folder from {repo_path}") # Update settings - settings_manager.settings.pop('gitRepo', None) - settings_manager.settings.pop('gitToken', None) - settings_manager.save_settings(settings_manager.settings) + settings.pop('gitRepo', None) + settings.pop('gitToken', None) + save_settings(settings) logger.info("Updated settings to remove git information") return True, "Repository successfully unlinked" except Exception as e: logger.error(f"Error unlinking repository: {str(e)}", exc_info=True) - return False, f"Error unlinking repository: {str(e)}" \ No newline at end of file + return False, f"Error unlinking repository: {str(e)}" diff --git a/backend/app/settings.py b/backend/app/settings.py deleted file mode 100644 index ec9f0fe..0000000 --- a/backend/app/settings.py +++ /dev/null @@ -1,74 +0,0 @@ -import os -import yaml -import git -from flask import Blueprint, request, jsonify -import logging -from .git.clone_repo import clone_repository -from .git.authenticate import validate_git_token -from .settings_utils import load_settings, save_settings - -logging.basicConfig(level=logging.DEBUG) -logging.getLogger('git').setLevel(logging.WARNING) -logger = logging.getLogger(__name__) - -bp = Blueprint('settings', __name__, url_prefix='/settings') - -DATA_DIR = '/app/data' -DB_DIR = os.path.join(DATA_DIR, 'db') -SETTINGS_FILE = os.path.join(DATA_DIR, 'config', 'settings.yml') -REGEX_DIR = os.path.join(DB_DIR, 'regex_patterns') -FORMAT_DIR = os.path.join(DB_DIR, 'custom_formats') - -class SettingsManager: - def __init__(self): - self.settings = load_settings() - self.repo_url = self.settings.get('gitRepo') if self.settings else None - self.repo_path = DB_DIR - - def clone_repository(self): - return clone_repository(self.repo_url, self.repo_path, self.settings["gitToken"]) - - -settings_manager = SettingsManager() - -@bp.route('', methods=['GET']) -def handle_settings(): - settings = load_settings() - if not settings: - return jsonify({}), 204 - return jsonify(settings), 200 - -@bp.route('', methods=['POST']) -def update_settings(): - try: - new_settings = request.json - 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 - - # Attempt to clone the repository before saving settings - settings_manager.settings = new_settings - settings_manager.repo_url = new_settings['gitRepo'] - success, message = settings_manager.clone_repository() - - if success: - # Only save the settings if the clone was successful - save_settings(new_settings) - logger.info("Settings updated and repository cloned successfully") - return jsonify(new_settings), 200 - else: - logger.error(f"Failed to clone repository: {message}") - return jsonify({"error": message}), 400 - except Exception as e: - logger.exception("Unexpected error in update_settings") - return jsonify({"error": f"Failed to update settings: {str(e)}"}), 500 \ No newline at end of file diff --git a/backend/app/settings_utils.py b/backend/app/settings_utils.py index c2e53ef..880f976 100644 --- a/backend/app/settings_utils.py +++ b/backend/app/settings_utils.py @@ -21,4 +21,9 @@ def save_settings(settings): with open(SETTINGS_FILE, 'w') as file: yaml.dump(settings, file) except Exception as e: - pass \ No newline at end of file + pass + +def create_empty_settings_if_not_exists(): + if not os.path.exists(SETTINGS_FILE): + save_settings({}) + \ No newline at end of file diff --git a/frontend/src/api/api.js b/frontend/src/api/api.js index 47b2ebb..db5ce63 100644 --- a/frontend/src/api/api.js +++ b/frontend/src/api/api.js @@ -110,16 +110,6 @@ export const getSettings = async () => { } }; -export const saveSettings = async (settings) => { - try { - const response = await axios.post(`${API_BASE_URL}/settings`, settings); - return response.data; - } catch (error) { - console.error('Error saving settings:', error); - throw error; - } -}; - export const getGitStatus = async () => { try { const response = await axios.get(`${API_BASE_URL}/git/status`); @@ -247,6 +237,16 @@ export const getDiff = async (filePath) => { } }; +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 getProfiles = async () => { try { const response = await axios.get(`${API_BASE_URL}/profile`); diff --git a/frontend/src/components/settings/SettingsModal.jsx b/frontend/src/components/settings/ApiKeyModal.jsx similarity index 63% rename from frontend/src/components/settings/SettingsModal.jsx rename to frontend/src/components/settings/ApiKeyModal.jsx index ce1320d..f77ed67 100644 --- a/frontend/src/components/settings/SettingsModal.jsx +++ b/frontend/src/components/settings/ApiKeyModal.jsx @@ -1,24 +1,34 @@ 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 SettingsModal = ({ isOpen, onClose, onSave }) => { +const ApiKeyModal = ({ isOpen, onClose, onSubmit }) => { const [gitRepo, setGitRepo] = useState(''); - const [gitToken, setGitToken] = useState(''); + const [apiKey, setApiKey] = useState(''); const [loading, setLoading] = useState(false); - const handleSave = () => { - if (!gitRepo || !gitToken) { - alert("Please fill in all fields."); - return; - } - setLoading(true); - onSave({ gitRepo, gitToken, localRepoPath: 'data/db' }) - .finally(() => setLoading(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 ( - +
@@ -34,25 +44,25 @@ const SettingsModal = ({ isOpen, onClose, onSave }) => { setGitToken(e.target.value)} + 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" />
@@ -61,4 +71,4 @@ const SettingsModal = ({ isOpen, onClose, onSave }) => { ); }; -export default SettingsModal; +export default ApiKeyModal; \ No newline at end of file diff --git a/frontend/src/components/settings/SettingsPage.jsx b/frontend/src/components/settings/SettingsPage.jsx index 9d1160b..ef2bee9 100644 --- a/frontend/src/components/settings/SettingsPage.jsx +++ b/frontend/src/components/settings/SettingsPage.jsx @@ -1,7 +1,6 @@ import React, { useState, useEffect } from "react"; import { getSettings, - saveSettings, getGitStatus, addFiles, pushFiles, @@ -10,7 +9,7 @@ import { getDiff, unlinkRepo, } from "../../api/api"; -import SettingsModal from "./SettingsModal"; +import ApiKeyModal from "./ApiKeyModal"; import SettingsBranchModal from "./SettingsBranchModal"; import { FileText, @@ -63,11 +62,9 @@ const SettingsPage = () => { const fetchSettings = async () => { try { const fetchedSettings = await getSettings(); + setSettings(fetchedSettings); if (fetchedSettings) { - setSettings(fetchedSettings); await fetchGitStatus(); - } else { - setShowModal(true); } } catch (error) { console.error("Error fetching settings:", error); @@ -116,27 +113,6 @@ const SettingsPage = () => { ); }; - const handleSaveSettings = async (newSettings) => { - try { - setLoadingAction("save_settings"); // Set a loading state if needed - const response = await saveSettings(newSettings); - - if (response) { - setSettings(response); // Update the settings in the state - Alert.success("Settings saved successfully!"); - await fetchGitStatus(); // Optionally refresh the Git status after saving - } else { - Alert.error("Failed to save settings. Please try again."); - } - } catch (error) { - Alert.error("An unexpected error occurred while saving the settings."); - console.error("Error saving settings:", error); - } finally { - setLoadingAction(""); // Reset the loading state - setShowModal(false); // Close the modal after saving - } - }; - const fetchGitStatus = async () => { setLoadingStatus(true); try { @@ -504,6 +480,13 @@ const SettingsPage = () => { } }; + const handleLinkRepo = async () => { + setLoadingAction(""); + setShowModal(false); + await fetchSettings(); + }; + + const handleUnlinkRepo = async () => { if (window.confirm("Are you sure you want to unlink this repository? This action cannot be undone.")) { setLoadingAction("unlink_repo"); @@ -530,6 +513,14 @@ const SettingsPage = () => {

Git Repository Settings

+ {!settings && ( + + )} {settings && (
@@ -680,11 +671,6 @@ const SettingsPage = () => {
)} - setShowModal(false)} - onSave={(newSettings) => handleSaveSettings(newSettings)} - /> {settings && status && ( { commitMessage={currentChange.commit_message} /> )} + setShowModal(false)} + onSubmit={handleLinkRepo} + />
); };