Conversation
Introduces backend API endpoints and service logic for installing, managing, and uninstalling a Gitea-based Git server. Adds a new frontend page for Git server management, updates the sidebar and routing, and includes supporting API methods and styles for the new feature.
- Add 'serverkit update --branch <name>' to switch branches - Add 'serverkit branch' command to show current branch - Skip version check for non-main branches (dev mode) - Show commit hash and branch in update output - Warn users when running on non-main branch Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Introduces GITEA_INTEGRATION_PLAN.md outlining the architecture, UI/UX, backend API, database changes, and implementation phases for integrating Gitea-based Git functionality into ServerKit, including resource warnings and webhook support.
There was a problem hiding this comment.
Pull request overview
This PR introduces integrated Git server management by adding Gitea as a first-class feature in ServerKit. The implementation spans CLI, backend API, and frontend UI layers to enable self-hosted Git repository management.
Changes:
- Enhanced CLI with branch switching capabilities for Git-based project management
- Added comprehensive Gitea management UI with installation, configuration, and access control
- Implemented backend services and API endpoints for Gitea lifecycle management (install, start, stop, uninstall)
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| serverkit | Enhanced update command with branch switching support and new branch command |
| frontend/src/pages/Git.jsx | New Git management page with installation wizard and server controls |
| frontend/src/styles/pages/_git.less | Complete styling for Git page including tabs, status cards, and modals |
| frontend/src/styles/main.less | Import for new Git page styles |
| frontend/src/services/api.js | API client methods for Git server endpoints |
| frontend/src/components/Sidebar.jsx | Added Git navigation item to sidebar |
| frontend/src/App.jsx | Routing for new Git page |
| backend/app/services/git_service.py | Service layer for Gitea installation and management |
| backend/app/api/git.py | REST API endpoints for Git server operations |
| backend/app/init.py | Blueprint registration for Git API |
| GITEA_INTEGRATION_PLAN.md | Architecture and implementation planning documentation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Only include password in response if it was generated | ||
| if generated_password: | ||
| response['admin_password'] = admin_password | ||
| response['warning'] = 'Save these credentials - password shown only once!' | ||
|
|
There was a problem hiding this comment.
The generated admin_password is returned in the API response (lines 726-727) and could be logged or exposed in API responses. This sensitive credential should be handled more securely and only displayed once to the user through a secure channel.
| # Only include password in response if it was generated | |
| if generated_password: | |
| response['admin_password'] = admin_password | |
| response['warning'] = 'Save these credentials - password shown only once!' |
| # ======================================== | ||
|
|
||
| GITEA_APP_NAME = 'serverkit-gitea' | ||
| GITEA_CONFIG_FILE = os.path.join(CONFIG_DIR, 'gitea.json') |
There was a problem hiding this comment.
The CONFIG_DIR variable is referenced without the cls prefix (line 584). It should be cls.CONFIG_DIR to properly reference the class variable.
| git checkout "$target_branch" 2>/dev/null || git checkout -b "$target_branch" "origin/$target_branch" | ||
| git reset --hard "origin/$target_branch" |
There was a problem hiding this comment.
The git checkout command may fail silently if the remote branch doesn't exist. The fallback creates a new local branch tracking the remote, but there's no error handling if the remote branch doesn't exist. This could leave the system in an inconsistent state. Add validation to check if the remote branch exists before attempting checkout.
| git checkout "$target_branch" 2>/dev/null || git checkout -b "$target_branch" "origin/$target_branch" | |
| git reset --hard "origin/$target_branch" | |
| if git show-ref --verify --quiet "refs/remotes/origin/$target_branch"; then | |
| git checkout "$target_branch" 2>/dev/null || git checkout -b "$target_branch" "origin/$target_branch" | |
| git reset --hard "origin/$target_branch" | |
| else | |
| if type print_error >/dev/null 2>&1; then | |
| print_error "Remote branch 'origin/$target_branch' does not exist. Aborting update." | |
| else | |
| echo -e "${RED}Remote branch 'origin/$target_branch' does not exist. Aborting update.${NC}" | |
| fi | |
| exit 1 | |
| fi |
| onClick={() => { | ||
| navigator.clipboard.writeText(`git clone ssh://git@${window.location.hostname}:${status?.ssh_port}/user/repo.git`); | ||
| toast.success('SSH URL copied to clipboard'); | ||
| }} | ||
| > | ||
| Copy SSH URL | ||
| </button> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| )} | ||
|
|
||
| {activeTab === 'access' && ( | ||
| <div className="access-tab"> | ||
| <div className="info-card"> | ||
| <h3>HTTP Access</h3> | ||
| <p className="text-muted">Access Gitea through your web browser</p> | ||
| <div className="access-url"> | ||
| <code>http://{window.location.hostname}:{status?.http_port}</code> | ||
| <button | ||
| className="btn btn-sm btn-secondary" | ||
| onClick={() => { | ||
| navigator.clipboard.writeText(`http://${window.location.hostname}:${status?.http_port}`); | ||
| toast.success('URL copied'); | ||
| }} |
There was a problem hiding this comment.
The clipboard write operations (lines 339 and 360) do not handle potential errors. The navigator.clipboard API can fail for various reasons (permissions, insecure context, etc.). Add error handling with try-catch blocks to gracefully handle failures and inform the user.
| # Save config with admin credentials and ports | ||
| config = { | ||
| 'admin_user': admin_user, | ||
| 'admin_email': admin_email, | ||
| 'http_port': http_port, | ||
| 'ssh_port': ssh_port, | ||
| 'db_password': variables.get('DB_PASSWORD'), | ||
| 'installed_at': datetime.now().isoformat(), | ||
| 'version': '1.21' | ||
| } | ||
| cls._save_gitea_config(config) |
There was a problem hiding this comment.
Storing the admin_password in plaintext in the configuration file (line 710) poses a security risk. Even though a comment mentions it should be encrypted, the password is saved directly without encryption. Consider removing it from config storage or implementing proper encryption.
| force_update=true | ||
| shift | ||
| ;; | ||
| --branch|-b) |
There was a problem hiding this comment.
The argument parsing does not validate the branch parameter value. If --branch is provided without a value (e.g., at the end of arguments), $2 will be empty or the next argument, potentially causing unexpected behavior. Add validation to ensure target_branch is not empty after parsing.
| --branch|-b) | |
| --branch|-b) | |
| if [[ -z "$2" || "$2" == -* ]]; then | |
| echo -e "${RED}Error:${NC} --branch requires a branch name argument" >&2 | |
| exit 1 | |
| fi |
| @git_bp.route('/install', methods=['POST']) | ||
| @jwt_required() | ||
| def install(): | ||
| """Install Gitea with PostgreSQL.""" | ||
| data = request.get_json() or {} | ||
|
|
||
| result = GitService.install_gitea( | ||
| admin_user=data.get('adminUser', 'admin'), | ||
| admin_email=data.get('adminEmail'), | ||
| admin_password=data.get('adminPassword') | ||
| ) |
There was a problem hiding this comment.
The install endpoint does not validate the admin_email parameter on the API layer. While frontend validation exists, backend validation is essential for API security. Add validation to ensure admin_email is provided and properly formatted before proceeding with installation.
| # Save config with admin credentials | ||
| config = { | ||
| 'admin_user': admin_user, | ||
| 'admin_email': admin_email, | ||
| 'admin_password': admin_password, # Should be encrypted |
There was a problem hiding this comment.
The documentation shows storing admin_password in plaintext with a comment 'Should be encrypted' (line 252). This same security issue exists in the actual implementation. The documentation should be updated to reflect proper security practices or the note should emphasize this is a known security concern that must be addressed.
| # Save config with admin credentials | |
| config = { | |
| 'admin_user': admin_user, | |
| 'admin_email': admin_email, | |
| 'admin_password': admin_password, # Should be encrypted | |
| # Save config (do NOT store admin password in plaintext) | |
| config = { | |
| 'admin_user': admin_user, | |
| 'admin_email': admin_email, |
| # Remove from database | ||
| db.session.delete(app) | ||
| db.session.commit() | ||
|
|
||
| # Remove config | ||
| if os.path.exists(cls.GITEA_CONFIG_FILE): | ||
| os.remove(cls.GITEA_CONFIG_FILE) |
There was a problem hiding this comment.
The uninstall_gitea method has a potential database rollback issue. If os.remove(cls.GITEA_CONFIG_FILE) fails after the database commit (line 756), the Application will be deleted from the database but the config file will remain, causing an inconsistent state. Consider wrapping all operations in a try-catch or moving the file deletion before the database commit.
| def install_gitea(cls, admin_user: str = 'admin', | ||
| admin_email: str = None, | ||
| admin_password: str = None) -> Dict: |
There was a problem hiding this comment.
Missing validation for required admin_email parameter. The email is required for Gitea installation but the function accepts None without validation, which could lead to installation failures.
This PR introduces first-class Git server management in ServerKit by integrating Gitea across the UI, API, and CLI layers.
What’s included
Why this matters
Notes