feat: implement dev mode

This commit is contained in:
Sam Chau
2024-09-08 01:14:08 +09:30
parent 03059638ce
commit 450fcb4973
6 changed files with 1242 additions and 1001 deletions

View File

@@ -5,6 +5,7 @@ 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 check_dev_mode
from ..settings_utils import save_settings
import logging
@@ -249,3 +250,9 @@ def generate_commit_message(user_message, files):
commit_message = f"{user_message}\n\nChanges:\n" + "\n".join(file_changes)
return commit_message
@bp.route('/dev', methods=['GET'])
def dev_mode():
is_dev_mode = check_dev_mode()
return jsonify({'devMode': is_dev_mode}), 200

View File

@@ -2,33 +2,95 @@
import subprocess
import logging
import os
from ...settings_utils import load_settings
logger = logging.getLogger(__name__)
def get_github_token():
token = os.environ.get('GITHUB_TOKEN')
if token:
logger.info("GitHub token retrieved from environment variable")
else:
logger.warning("GitHub token not found in environment variables")
return token
def validate_git_token(repo_url, git_token):
logger.info(f"Validating git token for repo: {repo_url}")
try:
parts = repo_url.strip('/').split('/')
if len(parts) < 2:
logger.error(f"Invalid repo URL format: {repo_url}")
return False
repo_owner, repo_name = parts[-2], parts[-1].replace('.git', '')
logger.info(f"Parsed repo owner: {repo_owner}, repo name: {repo_name}")
api_url = f"https://api.github.com/repos/{repo_owner}/{repo_name}"
logger.debug(f"GitHub API URL: {api_url}")
curl_command = [
'curl', '-s', '-o', '/dev/null', '-w', '%{http_code}',
'-H', f'Authorization: Bearer {git_token}',
'-H', 'Accept: application/vnd.github+json',
api_url
'curl', '-s', '-o', '/dev/null', '-w', '%{http_code}', '-H',
f'Authorization: Bearer {git_token}', '-H',
'Accept: application/vnd.github+json', api_url
]
logger.debug(f"Executing curl command: {' '.join(curl_command)}")
result = subprocess.run(curl_command, capture_output=True, text=True)
http_status_code = int(result.stdout.strip())
logger.info(f"API response status code: {http_status_code}")
if http_status_code == 200:
return True
elif http_status_code == 401:
return False
logger.info(
"Token has access to the repository. Checking write permissions."
)
permissions_url = f"{api_url}/collaborators/{repo_owner}/permission"
logger.debug(f"Permissions check URL: {permissions_url}")
permissions_command = [
'curl', '-s', '-H', f'Authorization: Bearer {git_token}', '-H',
'Accept: application/vnd.github+json', permissions_url
]
logger.debug(
f"Executing permissions check command: {' '.join(permissions_command)}"
)
permissions_result = subprocess.run(permissions_command,
capture_output=True,
text=True)
permissions_data = permissions_result.stdout.strip()
logger.debug(f"Permissions data: {permissions_data}")
has_write_access = any(perm in permissions_data
for perm in ['admin', 'write', 'maintain'])
logger.info(f"Write access: {has_write_access}")
return has_write_access
else:
logger.warning(
f"Token validation failed with status code: {http_status_code}"
)
return False
except Exception as e:
return False
logger.exception(f"Error validating git token: {str(e)}")
return False
def check_dev_mode():
logger.info("Checking dev mode")
settings = load_settings()
if not settings:
logger.warning("Settings not found")
return False
if 'gitRepo' not in settings:
logger.warning("Git repo URL not found in settings")
return False
repo_url = settings['gitRepo']
logger.info(f"Git repo URL from settings: {repo_url}")
github_token = get_github_token()
if not github_token:
logger.warning("GitHub token not available")
return False
is_dev_mode = validate_git_token(repo_url, github_token)
logger.info(f"Dev mode status: {is_dev_mode}")
return is_dev_mode

View File

@@ -20,5 +20,7 @@ services:
- backend_data:/app/data
environment:
- FLASK_ENV=development
env_file:
- .env
volumes:
backend_data:

View File

@@ -3,336 +3,356 @@ 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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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'};
}
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;
}
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;
}
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 => {
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;
}
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;
}
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;
}
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;
}
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 checkDevMode = async () => {
try {
const response = await axios.get(`${API_BASE_URL}/git/dev`);
return response.data;
} catch (error) {
console.error('Error checking dev mode:', error);
throw error;
}
};

View File

@@ -6,7 +6,8 @@ const CommitSection = ({
commitMessage,
setCommitMessage,
hasIncomingChanges,
funMessage
funMessage,
isDevMode
}) => {
const hasUnstagedChanges = status.outgoing_changes.some(
change => !change.staged || (change.staged && change.modified)
@@ -18,24 +19,43 @@ const CommitSection = ({
return (
<div className='mt-4'>
{hasAnyChanges || hasIncomingChanges ? (
{isDevMode ? (
<>
{hasStagedChanges && (
{hasAnyChanges || hasIncomingChanges ? (
<>
<h3 className='text-sm font-semibold text-gray-100 mb-4'>
Commit Message:
</h3>
<Textarea
value={commitMessage}
onChange={e => setCommitMessage(e.target.value)}
placeholder='Enter your commit message here...'
className='w-full p-2 text-sm text-gray-200 bg-gray-600 border border-gray-500 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 resize-y h-[75px] mb-2'
/>
{hasStagedChanges && (
<>
<h3 className='text-sm font-semibold text-gray-100 mb-4'>
Commit Message:
</h3>
<Textarea
value={commitMessage}
onChange={e =>
setCommitMessage(e.target.value)
}
placeholder='Enter your commit message here...'
className='w-full p-2 text-sm text-gray-200 bg-gray-600 border border-gray-500 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 resize-y h-[75px] mb-2'
/>
</>
)}
{hasUnstagedChanges && !hasStagedChanges && (
<p className='text-yellow-400 text-sm mb-2'>
You have unstaged changes. Stage your
changes before committing.
</p>
)}
</>
) : (
<div className='text-gray-300 text-sm italic'>
{funMessage}
</div>
)}
</>
) : (
<div className='text-gray-300 text-sm italic'>{funMessage}</div>
<div className='text-gray-300 text-sm italic'>
Developer mode is disabled. Commit functionality is not
available.
</div>
)}
</div>
);

File diff suppressed because it is too large Load Diff