# GitCaddy API Reference Complete technical reference for developers integrating with or using GitCaddy, a self-hosted Git repository management platform. ## Table of Contents - [Introduction](#introduction) - [Authentication](#authentication) - [Personal Access Tokens](#personal-access-tokens) - [OAuth2 Authentication](#oauth2-authentication) - [WebAuthn (Passkeys)](#webauthn-passkeys) - [REST API Endpoints](#rest-api-endpoints) - [Repository APIs](#repository-apis) - [Issue APIs](#issue-apis) - [Pull Request APIs](#pull-request-apis) - [Organization APIs](#organization-apis) - [User APIs](#user-apis) - [Package Registry APIs](#package-registry-apis) - [Actions APIs](#actions-apis) - [Vault APIs](#vault-apis) - [Git Protocol](#git-protocol) - [WebSocket APIs](#websocket-apis) - [Frontend JavaScript APIs](#frontend-javascript-apis) - [Core Utilities](#core-utilities) - [DOM Manipulation](#dom-manipulation) - [Web Components](#web-components) - [Vue Components](#vue-components) - [Error Codes](#error-codes) - [Rate Limiting](#rate-limiting) - [Webhooks](#webhooks) - [Code Examples](#code-examples) ## Introduction GitCaddy provides comprehensive REST APIs for repository management, collaboration tools, CI/CD workflows, package registry, and enterprise features. All API endpoints are accessible at: ``` https://your-gitcaddy-instance.com/api/v1/ ``` **Base URL Format:** ``` https://{instance}/api/v1/{endpoint} ``` **Content Type:** All requests and responses use `application/json` unless otherwise specified. **API Versioning:** Current stable version: `v1` ## Authentication GitCaddy supports multiple authentication methods for API access. ### Personal Access Tokens Personal access tokens (PATs) provide programmatic access to the GitCaddy API. **Creating a Token:** 1. Navigate to User Settings → Applications → Manage Access Tokens 2. Click "Generate New Token" 3. Set token name and scopes 4. Copy the generated token (shown only once) **Using Tokens:** Include the token in the `Authorization` header: ```http Authorization: token YOUR_ACCESS_TOKEN ``` **Example Request:** ```bash curl -H "Authorization: token abc123def456" \ https://gitcaddy.example.com/api/v1/user ``` **Token Scopes:** | Scope | Description | |-------|-------------| | `repo` | Full access to repositories | | `repo:status` | Read-only access to repository status | | `public_repo` | Access to public repositories only | | `admin:org` | Full organization administration | | `write:org` | Write access to organizations | | `read:org` | Read-only organization access | | `admin:public_key` | Manage public SSH keys | | `admin:repo_hook` | Manage repository webhooks | | `admin:org_hook` | Manage organization webhooks | | `notification` | Access notifications | | `user` | Full user profile access | | `read:user` | Read-only user profile access | | `user:email` | Access user email addresses | | `delete_repo` | Delete repositories | | `package` | Access package registry | | `admin:gpg_key` | Manage GPG keys | | `admin:application` | Manage OAuth applications | ### OAuth2 Authentication GitCaddy supports OAuth2 for third-party application integration. **OAuth2 Flow:** 1. **Register Application:** - User Settings → Applications → OAuth2 Applications - Create new OAuth2 application - Note `client_id` and `client_secret` 2. **Authorization Request:** ```http GET /login/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&response_type=code&state={state} ``` 3. **Token Exchange:** ```http POST /login/oauth/access_token Content-Type: application/json { "client_id": "your_client_id", "client_secret": "your_client_secret", "code": "authorization_code", "grant_type": "authorization_code", "redirect_uri": "https://your-app.com/callback" } ``` **Response:** ```json { "access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a", "token_type": "bearer", "scope": "repo,user" } ``` 4. **Using Access Token:** ```http Authorization: Bearer gho_16C7e42F292c6912E7710c838347Ae178B4a ``` **Supported OAuth2 Providers:** GitCaddy can authenticate users via external OAuth2 providers: - GitHub - GitLab - Bitbucket - Google - Microsoft Azure AD - Custom OpenID Connect providers ### WebAuthn (Passkeys) GitCaddy supports WebAuthn for passwordless authentication and two-factor authentication. **Registration API:** ```javascript // Client-side registration const response = await fetch('/user/settings/security/webauthn/register', { method: 'GET', credentials: 'include' }); const options = await response.json(); // Create credential const credential = await navigator.credentials.create({ publicKey: options }); // Send credential to server await fetch('/user/settings/security/webauthn/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'My Security Key', credential: credential }) }); ``` **Authentication API:** ```javascript // Get authentication challenge const response = await fetch('/user/webauthn/assertion', { method: 'GET' }); const options = await response.json(); // Get credential const assertion = await navigator.credentials.get({ publicKey: options }); // Verify assertion await fetch('/user/webauthn/assertion', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(assertion) }); ``` ## REST API Endpoints ### Repository APIs #### List User Repositories ```http GET /api/v1/user/repos ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `page` | integer | No | Page number (default: 1) | | `limit` | integer | No | Page size (default: 10, max: 50) | | `type` | string | No | Filter by type: `owner`, `collaborator`, `member` | **Response:** ```json [ { "id": 1, "owner": { "id": 1, "login": "username", "full_name": "User Name", "avatar_url": "https://gitcaddy.example.com/avatars/1" }, "name": "my-repo", "full_name": "username/my-repo", "description": "Repository description", "private": false, "fork": false, "parent": null, "empty": false, "mirror": false, "size": 1024, "html_url": "https://gitcaddy.example.com/username/my-repo", "ssh_url": "git@gitcaddy.example.com:username/my-repo.git", "clone_url": "https://gitcaddy.example.com/username/my-repo.git", "website": "", "stars_count": 5, "forks_count": 2, "watchers_count": 3, "open_issues_count": 1, "default_branch": "main", "archived": false, "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-15T12:30:00Z", "permissions": { "admin": true, "push": true, "pull": true } } ] ``` #### Create Repository ```http POST /api/v1/user/repos ``` **Request Body:** ```json { "name": "new-repo", "description": "My new repository", "private": false, "auto_init": true, "gitignores": "Go", "license": "MIT", "readme": "Default", "default_branch": "main" } ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `name` | string | Yes | Repository name | | `description` | string | No | Repository description | | `private` | boolean | No | Create as private (default: false) | | `auto_init` | boolean | No | Initialize with README (default: false) | | `gitignores` | string | No | .gitignore template name | | `license` | string | No | License template (MIT, Apache-2.0, GPL-3.0, etc.) | | `readme` | string | No | README template | | `default_branch` | string | No | Default branch name (default: main) | | `trust_model` | string | No | Trust model: `default`, `collaborator`, `committer`, `collaboratorcommitter` | **Response:** `201 Created` ```json { "id": 2, "name": "new-repo", "full_name": "username/new-repo", "private": false, "html_url": "https://gitcaddy.example.com/username/new-repo", "clone_url": "https://gitcaddy.example.com/username/new-repo.git", "created_at": "2024-01-20T10:00:00Z" } ``` #### Get Repository ```http GET /api/v1/repos/{owner}/{repo} ``` **Response:** ```json { "id": 1, "owner": { "id": 1, "login": "username", "full_name": "User Name" }, "name": "my-repo", "full_name": "username/my-repo", "description": "Repository description", "private": false, "default_branch": "main", "permissions": { "admin": true, "push": true, "pull": true } } ``` #### Delete Repository ```http DELETE /api/v1/repos/{owner}/{repo} ``` **Response:** `204 No Content` #### List Repository Branches ```http GET /api/v1/repos/{owner}/{repo}/branches ``` **Response:** ```json [ { "name": "main", "commit": { "id": "abc123def456", "message": "Initial commit", "url": "https://gitcaddy.example.com/username/my-repo/commit/abc123def456" }, "protected": true, "user_can_push": true, "user_can_merge": true } ] ``` #### Create Branch ```http POST /api/v1/repos/{owner}/{repo}/branches ``` **Request Body:** ```json { "new_branch_name": "feature-branch", "old_branch_name": "main" } ``` #### Get Branch Protection ```http GET /api/v1/repos/{owner}/{repo}/branch_protections/{branch} ``` **Response:** ```json { "branch_name": "main", "enable_push": false, "enable_push_whitelist": true, "push_whitelist_usernames": ["admin"], "push_whitelist_teams": ["core-team"], "push_whitelist_deploy_keys": false, "enable_merge_whitelist": true, "merge_whitelist_usernames": ["maintainer"], "merge_whitelist_teams": ["reviewers"], "enable_status_check": true, "status_check_contexts": ["ci/tests", "ci/lint"], "required_approvals": 2, "enable_approvals_whitelist": false, "block_on_rejected_reviews": true, "dismiss_stale_approvals": true, "require_signed_commits": true, "protected_file_patterns": "*.lock", "unprotected_file_patterns": "docs/*" } ``` #### List Repository Tags ```http GET /api/v1/repos/{owner}/{repo}/tags ``` **Response:** ```json [ { "name": "v1.0.0", "commit": { "sha": "abc123def456", "url": "https://gitcaddy.example.com/username/my-repo/commit/abc123def456" }, "zipball_url": "https://gitcaddy.example.com/username/my-repo/archive/v1.0.0.zip", "tarball_url": "https://gitcaddy.example.com/username/my-repo/archive/v1.0.0.tar.gz" } ] ``` #### Get File Contents ```http GET /api/v1/repos/{owner}/{repo}/contents/{filepath}?ref={branch} ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `ref` | string | No | Branch, tag, or commit SHA (default: default branch) | **Response:** ```json { "name": "README.md", "path": "README.md", "sha": "abc123", "type": "file", "size": 1024, "encoding": "base64", "content": "IyBNeVJlcG8KClRoaXMgaXMgYSBSRUFETUUgZmlsZS4=", "target": null, "url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/contents/README.md", "html_url": "https://gitcaddy.example.com/username/my-repo/src/branch/main/README.md", "git_url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/git/blobs/abc123", "download_url": "https://gitcaddy.example.com/username/my-repo/raw/branch/main/README.md" } ``` #### Create/Update File ```http POST /api/v1/repos/{owner}/{repo}/contents/{filepath} ``` **Request Body:** ```json { "content": "base64_encoded_content", "message": "Create README.md", "branch": "main", "sha": "abc123def456", "author": { "name": "Author Name", "email": "author@example.com" }, "committer": { "name": "Committer Name", "email": "committer@example.com" }, "dates": { "author": "2024-01-20T10:00:00Z", "committer": "2024-01-20T10:00:00Z" }, "signoff": false, "new_branch": "feature-branch" } ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `content` | string | Yes | Base64-encoded file content | | `message` | string | Yes | Commit message | | `branch` | string | No | Branch name (default: default branch) | | `sha` | string | No | Blob SHA for update (required when updating) | | `new_branch` | string | No | Create new branch for commit | #### Delete File ```http DELETE /api/v1/repos/{owner}/{repo}/contents/{filepath} ``` **Request Body:** ```json { "message": "Delete file", "sha": "abc123def456", "branch": "main" } ``` #### Fork Repository ```http POST /api/v1/repos/{owner}/{repo}/forks ``` **Request Body:** ```json { "organization": "my-org", "name": "forked-repo" } ``` #### List Forks ```http GET /api/v1/repos/{owner}/{repo}/forks ``` #### Mirror Repository ```http POST /api/v1/repos/migrate ``` **Request Body:** ```json { "clone_addr": "https://github.com/user/repo.git", "auth_username": "username", "auth_password": "password_or_token", "uid": 1, "repo_name": "mirrored-repo", "mirror": true, "private": false, "description": "Mirrored repository" } ``` ### Issue APIs #### List Repository Issues ```http GET /api/v1/repos/{owner}/{repo}/issues ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `state` | string | No | Filter by state: `open`, `closed`, `all` (default: open) | | `labels` | string | No | Comma-separated label IDs or names | | `q` | string | No | Search query | | `type` | string | No | Filter by type: `issues`, `pulls` | | `milestones` | string | No | Comma-separated milestone names | | `since` | string | No | Only issues updated after this time (ISO 8601) | | `before` | string | No | Only issues updated before this time (ISO 8601) | | `created_by` | string | No | Filter by creator username | | `assigned_by` | string | No | Filter by assignee username | | `mentioned_by` | string | No | Filter by mentioned username | | `page` | integer | No | Page number | | `limit` | integer | No | Page size | **Response:** ```json [ { "id": 1, "url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/issues/1", "html_url": "https://gitcaddy.example.com/username/my-repo/issues/1", "number": 1, "user": { "id": 1, "login": "username", "full_name": "User Name" }, "title": "Bug: Application crashes on startup", "body": "Detailed description of the issue...", "labels": [ { "id": 1, "name": "bug", "color": "#ee0701", "description": "Something isn't working" } ], "milestone": { "id": 1, "title": "v1.0", "description": "First release", "state": "open", "due_on": "2024-12-31T23:59:59Z" }, "assignees": [ { "id": 2, "login": "developer" } ], "state": "open", "is_locked": false, "comments": 3, "created_at": "2024-01-15T10:00:00Z", "updated_at": "2024-01-16T14:30:00Z", "closed_at": null, "due_date": null, "pull_request": null } ] ``` #### Create Issue ```http POST /api/v1/repos/{owner}/{repo}/issues ``` **Request Body:** ```json { "title": "New issue title", "body": "Issue description with **markdown** support", "assignees": ["developer1", "developer2"], "labels": [1, 2], "milestone": 1, "closed": false, "due_date": "2024-12-31T23:59:59Z", "ref": "main" } ``` **Response:** `201 Created` #### Get Issue ```http GET /api/v1/repos/{owner}/{repo}/issues/{index} ``` #### Update Issue ```http PATCH /api/v1/repos/{owner}/{repo}/issues/{index} ``` **Request Body:** ```json { "title": "Updated title", "body": "Updated description", "state": "closed", "assignees": ["user1"], "labels": [1, 3], "milestone": 2, "due_date": "2024-12-31T23:59:59Z", "unset_due_date": false } ``` #### List Issue Comments ```http GET /api/v1/repos/{owner}/{repo}/issues/{index}/comments ``` **Response:** ```json [ { "id": 1, "html_url": "https://gitcaddy.example.com/username/my-repo/issues/1#issuecomment-1", "pull_request_url": "", "issue_url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/issues/1", "user": { "id": 1, "login": "username" }, "original_author": "", "original_author_id": 0, "body": "This is a comment on the issue", "created_at": "2024-01-15T11:00:00Z", "updated_at": "2024-01-15T11:00:00Z" } ] ``` #### Create Issue Comment ```http POST /api/v1/repos/{owner}/{repo}/issues/{index}/comments ``` **Request Body:** ```json { "body": "Comment text with **markdown** support" } ``` #### Edit Issue Comment ```http PATCH /api/v1/repos/{owner}/{repo}/issues/comments/{id} ``` #### Delete Issue Comment ```http DELETE /api/v1/repos/{owner}/{repo}/issues/comments/{id} ``` #### List Issue Labels ```http GET /api/v1/repos/{owner}/{repo}/labels ``` **Response:** ```json [ { "id": 1, "name": "bug", "color": "#ee0701", "description": "Something isn't working", "url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/labels/1" } ] ``` #### Create Label ```http POST /api/v1/repos/{owner}/{repo}/labels ``` **Request Body:** ```json { "name": "enhancement", "color": "#a2eeef", "description": "New feature or request" } ``` #### Track Time on Issue ```http POST /api/v1/repos/{owner}/{repo}/issues/{index}/times ``` **Request Body:** ```json { "time": 3600, "created": "2024-01-15T10:00:00Z", "user_name": "username" } ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `time` | integer | Yes | Time in seconds | | `created` | string | No | Timestamp (ISO 8601) | | `user_name` | string | No | Username (admin only) | #### List Tracked Times ```http GET /api/v1/repos/{owner}/{repo}/issues/{index}/times ``` ### Pull Request APIs #### List Pull Requests ```http GET /api/v1/repos/{owner}/{repo}/pulls ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `state` | string | No | Filter by state: `open`, `closed`, `all` | | `sort` | string | No | Sort by: `oldest`, `recentupdate`, `leastupdate`, `mostcomment`, `leastcomment`, `priority` | | `milestone` | integer | No | Filter by milestone ID | | `labels` | string | No | Comma-separated label IDs | | `page` | integer | No | Page number | | `limit` | integer | No | Page size | **Response:** ```json [ { "id": 1, "url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/pulls/1", "number": 1, "user": { "id": 1, "login": "contributor" }, "title": "Add new feature", "body": "This PR adds a new feature...", "labels": [], "milestone": null, "assignees": [], "state": "open", "is_locked": false, "comments": 2, "html_url": "https://gitcaddy.example.com/username/my-repo/pulls/1", "diff_url": "https://gitcaddy.example.com/username/my-repo/pulls/1.diff", "patch_url": "https://gitcaddy.example.com/username/my-repo/pulls/1.patch", "mergeable": true, "merged": false, "merged_at": null, "merge_commit_sha": null, "merged_by": null, "base": { "label": "main", "ref": "main", "sha": "abc123", "repo_id": 1, "repo": { "id": 1, "name": "my-repo", "full_name": "username/my-repo" } }, "head": { "label": "contributor:feature-branch", "ref": "feature-branch", "sha": "def456", "repo_id": 2, "repo": { "id": 2, "name": "my-repo", "full_name": "contributor/my-repo" } }, "merge_base": "abc123", "due_date": null, "created_at": "2024-01-15T10:00:00Z", "updated_at": "2024-01-16T14:30:00Z", "closed_at": null } ] ``` #### Create Pull Request ```http POST /api/v1/repos/{owner}/{repo}/pulls ``` **Request Body:** ```json { "title": "Add new feature", "body": "Detailed description of changes", "head": "feature-branch", "base": "main", "assignees": ["reviewer1"], "labels": [1], "milestone": 1, "due_date": "2024-12-31T23:59:59Z" } ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `title` | string | Yes | PR title | | `body` | string | No | PR description | | `head` | string | Yes | Branch containing changes | | `base` | string | Yes | Branch to merge into | | `assignees` | array | No | Array of usernames | | `labels` | array | No | Array of label IDs | | `milestone` | integer | No | Milestone ID | #### Get Pull Request ```http GET /api/v1/repos/{owner}/{repo}/pulls/{index} ``` #### Update Pull Request ```http PATCH /api/v1/repos/{owner}/{repo}/pulls/{index} ``` **Request Body:** ```json { "title": "Updated title", "body": "Updated description", "base": "main", "assignees": ["reviewer1", "reviewer2"], "labels": [1, 2], "milestone": 1, "state": "closed", "due_date": "2024-12-31T23:59:59Z" } ``` #### Merge Pull Request ```http POST /api/v1/repos/{owner}/{repo}/pulls/{index}/merge ``` **Request Body:** ```json { "Do": "merge", "MergeMessageField": "Merge pull request #1", "MergeTitleField": "Add new feature", "delete_branch_after_merge": true, "force_merge": false, "head_commit_id": "def456", "merge_when_checks_succeed": false } ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `Do` | string | Yes | Merge method: `merge`, `rebase`, `rebase-merge`, `squash`, `manually-merged` | | `MergeMessageField` | string | No | Merge commit message | | `MergeTitleField` | string | No | Merge commit title | | `delete_branch_after_merge` | boolean | No | Delete head branch after merge | | `force_merge` | boolean | No | Force merge even if checks fail | | `head_commit_id` | string | No | Expected head commit SHA | #### List PR Reviews ```http GET /api/v1/repos/{owner}/{repo}/pulls/{index}/reviews ``` **Response:** ```json [ { "id": 1, "user": { "id": 2, "login": "reviewer" }, "body": "Looks good to me!", "commit_id": "def456", "state": "APPROVED", "html_url": "https://gitcaddy.example.com/username/my-repo/pulls/1#pullrequestreview-1", "submitted_at": "2024-01-16T10:00:00Z" } ] ``` #### Create PR Review ```http POST /api/v1/repos/{owner}/{repo}/pulls/{index}/reviews ``` **Request Body:** ```json { "body": "Review comment", "event": "APPROVED", "comments": [ { "path": "src/main.go", "body": "Consider refactoring this function", "old_position": 0, "new_position": 42 } ] } ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `body` | string | No | Overall review comment | | `event` | string | Yes | Review state: `APPROVED`, `REQUEST_CHANGES`, `COMMENT` | | `comments` | array | No | Array of line comments | #### Submit PR Review ```http POST /api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id} ``` #### Dismiss PR Review ```http POST /api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}/dismissals ``` **Request Body:** ```json { "message": "Outdated review" } ``` #### Request AI Code Review ```http POST /api/v1/repos/{owner}/{repo}/pulls/{index}/ai-review ``` **Request Body:** ```json { "provider": "openai", "model": "gpt-4" } ``` **Response:** ```json { "review_id": 42, "status": "pending", "message": "AI review requested. Results will be posted as comments." } ``` ### Organization APIs #### List Organizations ```http GET /api/v1/orgs ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `page` | integer | No | Page number | | `limit` | integer | No | Page size | #### Get Organization ```http GET /api/v1/orgs/{org} ``` **Response:** ```json { "id": 1, "username": "my-org", "full_name": "My Organization", "avatar_url": "https://gitcaddy.example.com/avatars/1", "description": "Organization description", "website": "https://example.com", "location": "San Francisco, CA", "visibility": "public", "repo_admin_change_team_access": false, "username": "my-org" } ``` #### Create Organization ```http POST /api/v1/orgs ``` **Request Body:** ```json { "username": "new-org", "full_name": "New Organization", "description": "Organization description", "website": "https://example.com", "location": "San Francisco, CA", "visibility": "public", "repo_admin_change_team_access": false } ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `username` | string | Yes | Organization username | | `full_name` | string | No | Display name | | `description` | string | No | Organization description | | `website` | string | No | Website URL | | `location` | string | No | Location | | `visibility` | string | No | Visibility: `public`, `limited`, `private` | #### Edit Organization ```http PATCH /api/v1/orgs/{org} ``` #### Delete Organization ```http DELETE /api/v1/orgs/{org} ``` #### List Organization Teams ```http GET /api/v1/orgs/{org}/teams ``` **Response:** ```json [ { "id": 1, "name": "core-team", "description": "Core development team", "organization": { "id": 1, "username": "my-org", "full_name": "My Organization" }, "permission": "admin", "can_create_org_repo": true, "includes_all_repositories": false, "units": [ "repo.code", "repo.issues", "repo.pulls", "repo.releases", "repo.wiki" ] } ] ``` #### Create Team ```http POST /api/v1/orgs/{org}/teams ``` **Request Body:** ```json { "name": "new-team", "description": "Team description", "permission": "write", "can_create_org_repo": false, "includes_all_repositories": false, "units": [ "repo.code", "repo.issues", "repo.pulls" ] } ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `name` | string | Yes | Team name | | `description` | string | No | Team description | | `permission` | string | No | Permission level: `read`, `write`, `admin` | | `units` | array | No | Repository unit permissions | #### List Team Members ```http GET /api/v1/teams/{id}/members ``` #### Add Team Member ```http PUT /api/v1/teams/{id}/members/{username} ``` #### Remove Team Member ```http DELETE /api/v1/teams/{id}/members/{username} ``` #### List Team Repositories ```http GET /api/v1/teams/{id}/repos ``` #### Add Team Repository ```http PUT /api/v1/teams/{id}/repos/{org}/{repo} ``` ### User APIs #### Get Authenticated User ```http GET /api/v1/user ``` **Response:** ```json { "id": 1, "login": "username", "full_name": "User Name", "email": "user@example.com", "avatar_url": "https://gitcaddy.example.com/avatars/1", "language": "en-US", "is_admin": false, "last_login": "2024-01-20T10:00:00Z", "created": "2023-01-01T00:00:00Z", "restricted": false, "active": true, "prohibit_login": false, "location": "San Francisco", "website": "https://example.com", "description": "User bio", "visibility": "public", "followers_count": 10, "following_count": 5, "starred_repos_count": 20 } ``` #### Get User ```http GET /api/v1/users/{username} ``` #### List User Emails ```http GET /api/v1/user/emails ``` **Response:** ```json [ { "email": "user@example.com", "verified": true, "primary": true }, { "email": "alternate@example.com", "verified": false, "primary": false } ] ``` #### Add Email ```http POST /api/v1/user/emails ``` **Request Body:** ```json { "emails": ["new@example.com"] } ``` #### Delete Email ```http DELETE /api/v1/user/emails ``` **Request Body:** ```json { "emails": ["old@example.com"] } ``` #### List SSH Keys ```http GET /api/v1/user/keys ``` **Response:** ```json [ { "id": 1, "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...", "url": "https://gitcaddy.example.com/api/v1/user/keys/1", "title": "Work Laptop", "fingerprint": "SHA256:abc123def456", "created_at": "2024-01-01T00:00:00Z", "read_only": false } ] ``` #### Add SSH Key ```http POST /api/v1/user/keys ``` **Request Body:** ```json { "title": "My Key", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...", "read_only": false } ``` #### Delete SSH Key ```http DELETE /api/v1/user/keys/{id} ``` #### List GPG Keys ```http GET /api/v1/user/gpg_keys ``` **Response:** ```json [ { "id": 1, "primary_key_id": "", "key_id": "ABC123DEF456", "public_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n...", "emails": [ { "email": "user@example.com", "verified": true } ], "subkeys": [], "can_sign": true, "can_encrypt_comms": true, "can_encrypt_storage": true, "can_certify": true, "created_at": "2024-01-01T00:00:00Z", "expires_at": "2025-01-01T00:00:00Z" } ] ``` #### Add GPG Key ```http POST /api/v1/user/gpg_keys ``` **Request Body:** ```json { "armored_public_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n..." } ``` #### List Starred Repositories ```http GET /api/v1/user/starred ``` #### Star Repository ```http PUT /api/v1/user/starred/{owner}/{repo} ``` #### Unstar Repository ```http DELETE /api/v1/user/starred/{owner}/{repo} ``` #### List Watched Repositories ```http GET /api/v1/user/subscriptions ``` #### Watch Repository ```http PUT /api/v1/repos/{owner}/{repo}/subscription ``` #### Unwatch Repository ```http DELETE /api/v1/repos/{owner}/{repo}/subscription ``` ### Package Registry APIs GitCaddy supports multiple package formats with dedicated API endpoints. #### List Packages ```http GET /api/v1/packages/{owner} ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `type` | string | No | Package type: `npm`, `maven`, `docker`, `pypi`, `alpine`, etc. | | `q` | string | No | Search query | | `page` | integer | No | Page number | | `limit` | integer | No | Page size | **Response:** ```json [ { "id": 1, "name": "my-package", "version": "1.0.0", "type": "npm", "owner": { "id": 1, "login": "username" }, "repository": { "id": 1, "name": "my-repo", "full_name": "username/my-repo" }, "creator": { "id": 1, "login": "username" }, "html_url": "https://gitcaddy.example.com/username/-/packages/npm/my-package/1.0.0", "created_at": "2024-01-15T10:00:00Z" } ] ``` #### Get Package ```http GET /api/v1/packages/{owner}/{type}/{name}/{version} ``` #### Delete Package ```http DELETE /api/v1/packages/{owner}/{type}/{name}/{version} ``` #### NPM Package API **Configure npm registry:** ```bash npm config set registry https://gitcaddy.example.com/api/packages/{owner}/npm/ npm config set //gitcaddy.example.com/api/packages/{owner}/npm/:_authToken {token} ``` **Publish package:** ```bash npm publish ``` **Install package:** ```bash npm install @{owner}/{package} ``` #### Docker Registry API **Login:** ```bash docker login gitcaddy.example.com ``` **Tag and push:** ```bash docker tag myimage gitcaddy.example.com/{owner}/{image}:{tag} docker push gitcaddy.example.com/{owner}/{image}:{tag} ``` **Pull:** ```bash docker pull gitcaddy.example.com/{owner}/{image}:{tag} ``` #### Maven Package API **Configure in `pom.xml`:** ```xml gitcaddy https://gitcaddy.example.com/api/packages/{owner}/maven gitcaddy https://gitcaddy.example.com/api/packages/{owner}/maven ``` **Configure authentication in `~/.m2/settings.xml`:** ```xml gitcaddy {username} {token} ``` #### PyPI Package API **Configure pip:** ```bash pip config set global.index-url https://gitcaddy.example.com/api/packages/{owner}/pypi/simple ``` **Upload with twine:** ```bash twine upload --repository-url https://gitcaddy.example.com/api/packages/{owner}/pypi dist/* ``` ### Actions APIs #### List Workflow Runs ```http GET /api/v1/repos/{owner}/{repo}/actions/runs ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `status` | string | No | Filter by status: `success`, `failure`, `waiting`, `running` | | `event` | string | No | Filter by event: `push`, `pull_request`, `schedule` | | `actor` | string | No | Filter by actor username | | `branch` | string | No | Filter by branch | | `page` | integer | No | Page number | | `limit` | integer | No | Page size | **Response:** ```json { "total_count": 10, "workflow_runs": [ { "id": 1, "name": "CI", "head_branch": "main", "head_sha": "abc123def456", "run_number": 42, "event": "push", "status": "completed", "conclusion": "success", "workflow_id": 1, "url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/actions/runs/1", "html_url": "https://gitcaddy.example.com/username/my-repo/actions/runs/1", "created_at": "2024-01-20T10:00:00Z", "updated_at": "2024-01-20T10:05:00Z", "run_started_at": "2024-01-20T10:00:30Z" } ] } ``` #### Get Workflow Run ```http GET /api/v1/repos/{owner}/{repo}/actions/runs/{run_id} ``` #### Cancel Workflow Run ```http POST /api/v1/repos/{owner}/{repo}/actions/runs/{run_id}/cancel ``` #### Rerun Workflow ```http POST /api/v1/repos/{owner}/{repo}/actions/runs/{run_id}/rerun ``` #### List Workflow Jobs ```http GET /api/v1/repos/{owner}/{repo}/actions/runs/{run_id}/jobs ``` **Response:** ```json { "total_count": 3, "jobs": [ { "id": 1, "run_id": 1, "name": "build", "status": "completed", "conclusion": "success", "started_at": "2024-01-20T10:00:30Z", "completed_at": "2024-01-20T10:03:00Z", "url": "https://gitcaddy.example.com/api/v1/repos/username/my-repo/actions/jobs/1", "html_url": "https://gitcaddy.example.com/username/my-repo/actions/runs/1/jobs/1" } ] } ``` #### Get Job Logs ```http GET /api/v1/repos/{owner}/{repo}/actions/jobs/{job_id}/logs ``` **Response:** Plain text log output #### List Repository Secrets ```http GET /api/v1/repos/{owner}/{repo}/actions/secrets ``` **Response:** ```json { "total_count": 2, "secrets": [ { "name": "API_KEY", "created_at": "2024-01-15T10:00:00Z", "updated_at": "2024-01-15T10:00:00Z" } ] } ``` #### Create/Update Secret ```http PUT /api/v1/repos/{owner}/{repo}/actions/secrets/{secret_name} ``` **Request Body:** ```json { "value": "secret_value" } ``` #### Delete Secret ```http DELETE /api/v1/repos/{owner}/{repo}/actions/secrets/{secret_name} ``` #### List Runners ```http GET /api/v1/repos/{owner}/{repo}/actions/runners ``` **Response:** ```json { "total_count": 1, "runners": [ { "id": 1, "name": "runner-1", "os": "linux", "status": "online", "busy": false, "labels": [ { "id": 1, "name": "ubuntu-latest", "type": "read-only" } ] } ] } ``` #### Register Runner ```http POST /api/v1/repos/{owner}/{repo}/actions/runners/registration-token ``` **Response:** ```json { "token": "RUNNER_REGISTRATION_TOKEN", "expires_at": "2024-01-20T11:00:00Z" } ``` ### Vault APIs Enterprise-grade secrets management with encryption and audit logging. #### List Vault Secrets ```http GET /api/v1/repos/{owner}/{repo}/vault/secrets ``` **Response:** ```json { "total_count": 5, "secrets": [ { "id": "secret-id-123", "name": "database_password", "description": "Production database password", "version": 3, "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-15T10:00:00Z", "created_by": { "id": 1, "login": "admin" }, "last_accessed_at": "2024-01-20T09:30:00Z", "access_count": 42 } ] } ``` #### Create Vault Secret ```http POST /api/v1/repos/{owner}/{repo}/vault/secrets ``` **Request Body:** ```json { "name": "api_key", "description": "External API key", "value": "secret_value_encrypted", "metadata": { "environment": "production", "service": "payment-gateway" } } ``` **Response:** `201 Created` ```json { "id": "secret-id-456", "name": "api_key", "version": 1, "created_at": "2024-01-20T10:00:00Z" } ``` #### Get Vault Secret ```http GET /api/v1/repos/{owner}/{repo}/vault/secrets/{secret_id} ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `version` | integer | No | Specific version (default: latest) | **Response:** ```json { "id": "secret-id-456", "name": "api_key", "description": "External API key", "value": "decrypted_secret_value", "version": 1, "created_at": "2024-01-20T10:00:00Z", "metadata": { "environment": "production", "service": "payment-gateway" } } ``` #### Update Vault Secret ```http PUT /api/v1/repos/{owner}/{repo}/vault/secrets/{secret_id} ``` **Request Body:** ```json { "value": "new_secret_value", "description": "Updated description" } ``` **Response:** ```json { "id": "secret-id-456", "version": 2, "updated_at": "2024-01-20T11:00:00Z" } ``` #### Delete Vault Secret ```http DELETE /api/v1/repos/{owner}/{repo}/vault/secrets/{secret_id} ``` **Response:** `204 No Content` #### List Secret Versions ```http GET /api/v1/repos/{owner}/{repo}/vault/secrets/{secret_id}/versions ``` **Response:** ```json { "total_count": 3, "versions": [ { "version": 3, "created_at": "2024-01-20T10:00:00Z", "created_by": { "id": 1, "login": "admin" } }, { "version": 2, "created_at": "2024-01-15T10:00:00Z", "created_by": { "id": 1, "login": "admin" } } ] } ``` #### Get Vault Audit Log ```http GET /api/v1/repos/{owner}/{repo}/vault/audit ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `secret_id` | string | No | Filter by secret ID | | `action` | string | No | Filter by action: `create`, `read`, `update`, `delete` | | `user` | string | No | Filter by username | | `since` | string | No | Start date (ISO 8601) | | `before` | string | No | End date (ISO 8601) | | `page` | integer | No | Page number | | `limit` | integer | No | Page size | **Response:** ```json { "total_count": 100, "entries": [ { "id": "audit-log-123", "secret_id": "secret-id-456", "secret_name": "api_key", "action": "read", "user": { "id": 2, "login": "developer" }, "ip_address": "192.168.1.100", "user_agent": "GitCaddy-Actions/1.0", "timestamp": "2024-01-20T10:30:00Z", "success": true } ] } ``` #### Generate CI/CD Token ```http POST /api/v1/repos/{owner}/{repo}/vault/tokens ``` **Request Body:** ```json { "name": "ci-token", "secret_ids": ["secret-id-456", "secret-id-789"], "expires_at": "2024-12-31T23:59:59Z", "read_only": true } ``` **Response:** ```json { "token": "vault_token_abc123def456", "expires_at": "2024-12-31T23:59:59Z" } ``` ## Git Protocol GitCaddy supports standard Git protocols for repository operations. ### HTTP(S) Protocol **Clone:** ```bash git clone https://gitcaddy.example.com/username/repo.git ``` **Authentication:** Use personal access token as password: ```bash git clone https://username:token@gitcaddy.example.com/username/repo.git ``` Or configure credential helper: ```bash git config --global credential.helper store git clone https://gitcaddy.example.com/username/repo.git # Enter username and token when prompted ``` ### SSH Protocol **Clone:** ```bash git clone git@gitcaddy.example.com:username/repo.git ``` **Setup:** 1. Generate SSH key: ```bash ssh-keygen -t ed25519 -C "your_email@example.com" ``` 2. Add public key to GitCaddy: - User Settings → SSH/GPG Keys → Add Key - Paste contents of `~/.ssh/id_ed25519.pub` 3. Configure SSH: ```bash # ~/.ssh/config Host gitcaddy.example.com User git IdentityFile ~/.ssh/id_ed25519 ``` ### Git LFS **Enable LFS for repository:** ```bash git lfs install git lfs track "*.psd" git add .gitattributes git commit -m "Enable LFS for PSD files" ``` **Configuration:** GitCaddy automatically handles LFS objects when pushing/pulling. **LFS API Endpoints:** ```http POST /api/v1/repos/{owner}/{repo}/lfs/objects/batch ``` **Request Body:** ```json { "operation": "upload", "transfers": ["basic"], "objects": [ { "oid": "abc123def456...", "size": 1048576 } ] } ``` ## WebSocket APIs GitCaddy uses WebSocket connections for real-time features. ### EventSource (Server-Sent Events) **Time Tracking Updates:** ```javascript const eventSource = new EventSource('/user/events'); eventSource.addEventListener('timetrack', (event) => { const data = JSON.parse(event.data); console.log('Time tracking update:', data); // Update stopwatch UI }); eventSource.addEventListener('error', (error) => { console.error('EventSource error:', error); }); ``` **Notification Updates:** ```javascript const notificationSource = new EventSource('/api/v1/notifications/events'); notificationSource.addEventListener('notification', (event) => { const notification = JSON.parse(event.data); console.log('New notification:', notification); // Display notification toast }); ``` ### SharedWorker **Shared stopwatch across tabs:** ```javascript // Initialize shared worker for time tracking const worker = new SharedWorker('/assets/js/stopwatch-worker.js'); worker.port.start(); worker.port.postMessage({ action: 'start', issueId: 123 }); worker.port.onmessage = (event) => { const { action, elapsed } = event.data; if (action === 'tick') { updateStopwatchDisplay(elapsed); } }; ``` ## Frontend JavaScript APIs GitCaddy provides modular JavaScript APIs for frontend functionality. ### Core Utilities #### DOM Manipulation ```javascript import {$, $$} from './modules/utils/dom.js'; // Select single element const button = $('#submit-button'); // Select multiple elements const listItems = $$('.list-item'); // Event delegation $(document).on('click', '.dynamic-button', (event) => { console.log('Button clicked:', event.target); }); ``` #### HTML Escaping ```javascript import {htmlEscape} from './modules/utils/html.js'; const userInput = ''; const safe = htmlEscape(userInput); // Result: <script>alert("xss")</script> ``` #### Date/Time Utilities ```javascript import {parseISODateTime, formatDatetime} from './modules/utils/time.js'; const date = parseISODateTime('2024-01-20T10:00:00Z'); const formatted = formatDatetime(date, 'short'); // "Jan 20, 2024" ``` #### Color Utilities ```javascript import {useLightTextOnBackground} from './modules/utils/color.js'; const bgColor = '#ff0000'; const useLight = useLightTextOnBackground(bgColor); // Returns true if light text should be used on this background ``` #### Glob Pattern Matching ```javascript import {matchGlobPattern} from './modules/utils/glob.js'; const pattern = '*.js'; const filename = 'script.js'; const matches = matchGlobPattern(pattern, filename); // true ``` ### DOM Manipulation #### Creating Elements ```javascript import {createElementFromHTML} from './modules/utils/dom.js'; const html = '
Message
'; const element = createElementFromHTML(html); document.body.appendChild(element); ``` #### Toggle Classes ```javascript import {toggleClass} from './modules/utils/dom.js'; const element = $('#my-element'); toggleClass(element, 'active', true); // Add class toggleClass(element, 'hidden', false); // Remove class ``` ### Web Components #### Absolute Date Component ```html ``` **JavaScript API:** ```javascript const dateElement = document.querySelector('absolute-date'); dateElement.setAttribute('date', '2024-01-21T12:00:00Z'); ``` #### Origin URL Component ```html ``` Displays sanitized origin URL with proper formatting. #### Overflow Menu Component ```html ``` Automatically creates overflow menu for items that don't fit. ### Vue Components #### File Tree Component ```javascript import {createApp} from 'vue'; import FileTreeView from './modules/features/repo-file-tree.js'; const app = createApp({ components: { FileTreeView }, data() { return { files: [ {name: 'src', type: 'dir', children: [...]}, {name: 'README.md', type: 'file'} ] }; } }); app.mount('#file-tree'); ``` **Template:** ```html ``` #### Context Popup Component ```javascript import {createContextPopup} from './modules/features/context-popup.js'; const popup = createContextPopup({ items: [ {label: 'Edit', action: () => console.log('Edit')}, {label: 'Delete', action: () => console.log('Delete')} ], position: {x: event.clientX, y: event.clientY} }); popup.show(); ``` ### Tooltip System ```javascript import {createTippy} from './modules/features/tippy.js'; // Simple tooltip createTippy('#my-element', { content: 'Tooltip text' }); // HTML content createTippy('.help-icon', { content: 'Help: This is help text', allowHTML: true }); // Interactive tooltip createTippy('.interactive', { content: document.querySelector('#tooltip-content'), interactive: true, trigger: 'click' }); ``` ### Toast Notifications ```javascript import {showToast} from './modules/features/toast.js'; // Success toast showToast('Operation completed successfully', 'success'); // Error toast showToast('An error occurred', 'error'); // Warning toast showToast('Please review your changes', 'warning'); // Info toast showToast('New version available', 'info'); // Custom duration showToast('Auto-closing in 5 seconds', 'info', 5000); ``` ### Markdown Rendering ```javascript import {renderMarkdown} from './modules/features/markup.js'; const markdown = '# Heading\n\nParagraph with **bold** text.'; const html = await renderMarkdown(markdown); document.querySelector('#content').innerHTML = html; ``` **Features:** - GitHub-flavored markdown - Syntax highlighting - Math rendering (KaTeX) - Mermaid diagrams - Task lists - Emoji support ### Mermaid Diagrams ```javascript import {renderMermaid} from './modules/features/mermaid.js'; const diagramCode = ` graph TD A[Start] --> B[Process] B --> C[End] `; await renderMermaid('#diagram-container', diagramCode); ``` ### SortableJS Integration ```javascript import Sortable from 'sortablejs'; const projectBoard = document.querySelector('#project-board'); Sortable.create(projectBoard, { animation: 150, handle: '.card-handle', onEnd: (event) => { const cardId = event.item.dataset.cardId; const newPosition = event.newIndex; // Update card position via API fetch(`/api/v1/projects/cards/${cardId}/move`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({position: newPosition}) }); } }); ``` ## Error Codes GitCaddy uses standard HTTP status codes with detailed error responses. ### HTTP Status Codes | Code | Status | Description | |------|--------|-------------| | 200 | OK | Request succeeded | | 201 | Created | Resource created successfully | | 204 | No Content | Request succeeded with no response body | | 400 | Bad Request | Invalid request parameters | | 401 | Unauthorized | Authentication required | | 403 | Forbidden | Insufficient permissions | | 404 | Not Found | Resource not found | | 409 | Conflict | Resource conflict (e.g., duplicate name) | | 422 | Unprocessable Entity | Validation error | | 429 | Too Many Requests | Rate limit exceeded | | 500 | Internal Server Error | Server error | | 503 | Service Unavailable | Service temporarily unavailable | ### Error Response Format ```json { "message": "Human-readable error message", "errors": [ { "field": "email", "message": "Email is required" } ], "url": "https://docs.gitcaddy.example.com/api/errors/validation" } ``` ### Common Error Codes **Authentication Errors:** ```json { "message": "Unauthorized: Invalid or expired token", "code": "INVALID_TOKEN" } ``` **Validation Errors:** ```json { "message": "Validation failed", "errors": [ { "field": "name", "message": "Repository name already exists" }, { "field": "description", "message": "Description must be less than 500 characters" } ] } ``` **Permission Errors:** ```json { "message": "Forbidden: You do not have permission to perform this action", "required_permission": "admin" } ``` **Rate Limit Errors:** ```json { "message": "API rate limit exceeded", "limit": 5000, "remaining": 0, "reset": "2024-01-20T11:00:00Z" } ``` **Resource Not Found:** ```json { "message": "Repository not found", "resource": "repository", "id": "username/nonexistent-repo" } ``` ## Rate Limiting GitCaddy implements rate limiting to prevent abuse. ### Rate Limit Headers All API responses include rate limit information: ```http X-RateLimit-Limit: 5000 X-RateLimit-Remaining: 4999 X-RateLimit-Reset: 1705750800 ``` | Header | Description | |--------|-------------| | `X-RateLimit-Limit` | Maximum requests per hour | | `X-RateLimit-Remaining` | Remaining requests in current window | | `X-RateLimit-Reset` | Unix timestamp when limit resets | ### Rate Limits by Authentication | Authentication | Requests/Hour | |----------------|---------------| | Unauthenticated | 60 | | Authenticated (PAT) | 5,000 | | OAuth Application | 5,000 | | Enterprise License | 15,000 | ### Handling Rate Limits ```javascript async function makeAPIRequest(url) { const response = await fetch(url, { headers: { 'Authorization': 'token YOUR_TOKEN' } }); const remaining = response.headers.get('X-RateLimit-Remaining'); const reset = response.headers.get('X-RateLimit-Reset'); if (response.status === 429) { const resetDate = new Date(reset * 1000); const waitTime = resetDate - new Date(); console.log(`Rate limited. Retry after ${waitTime}ms`); await new Promise(resolve => setTimeout(resolve, waitTime)); return makeAPIRequest(url); // Retry } if (remaining < 100) { console.warn(`Low rate limit: ${remaining} requests remaining`); } return response.json(); } ``` ## Webhooks GitCaddy can send HTTP POST requests to external URLs when events occur. ### Creating Webhooks **Via API:** ```http POST /api/v1/repos/{owner}/{repo}/hooks ``` **Request Body:** ```json { "type": "gitea", "config": { "url": "https://example.com/webhook", "content_type": "json", "secret": "webhook_secret" }, "events": [ "push", "pull_request", "issues", "issue_comment", "release" ], "active": true } ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `type` | string | Yes | Webhook type: `gitea`, `gogs`, `slack`, `discord` | | `config.url` | string | Yes | Webhook URL | | `config.content_type` | string | No | Content type: `json`, `form` | | `config.secret` | string | No | Secret for signature verification | | `events` | array | Yes | Events to trigger webhook | | `active` | boolean | No | Enable webhook (default: true) | ### Webhook Events | Event | Description | |-------|-------------| | `push` | Git push to repository | | `create` | Branch or tag created | | `delete` | Branch or tag deleted | | `fork` | Repository forked | | `issues` | Issue opened, closed, edited | | `issue_comment` | Comment on issue | | `pull_request` | PR opened, closed, merged, edited | | `pull_request_review` | PR review submitted | | `pull_request_review_comment` | Comment on PR review | | `release` | Release published | | `repository` | Repository created, deleted | | `wiki` | Wiki page created, edited | ### Webhook Payload **Push Event:** ```json { "ref": "refs/heads/main", "before": "abc123def456", "after": "def456ghi789", "compare_url": "https://gitcaddy.example.com/username/repo/compare/abc123...def456", "commits": [ { "id": "def456ghi789", "message": "Fix bug in authentication", "url": "https://gitcaddy.example.com/username/repo/commit/def456ghi789", "author": { "name": "Developer", "email": "dev@example.com", "username": "developer" }, "committer": { "name": "Developer", "email": "dev@example.com", "username": "developer" }, "timestamp": "2024-01-20T10:00:00Z", "added": ["new-file.js"], "removed": [], "modified": ["existing-file.js"] } ], "repository": { "id": 1, "name": "repo", "full_name": "username/repo", "html_url": "https://gitcaddy.example.com/username/repo", "private": false, "owner": { "id": 1, "login": "username", "full_name": "User Name" } }, "pusher": { "id": 1, "login": "username", "full_name": "User Name" }, "sender": { "id": 1, "login": "username", "full_name": "User Name" } } ``` **Pull Request Event:** ```json { "action": "opened", "number": 1, "pull_request": { "id": 1, "number": 1, "user": { "id": 2, "login": "contributor" }, "title": "Add new feature", "body": "Description of changes", "state": "open", "html_url": "https://gitcaddy.example.com/username/repo/pulls/1", "diff_url": "https://gitcaddy.example.com/username/repo/pulls/1.diff", "patch_url": "https://gitcaddy.example.com/username/repo/pulls/1.patch", "merged": false, "mergeable": true, "base": { "label": "main", "ref": "main", "sha": "abc123" }, "head": { "label": "contributor:feature", "ref": "feature", "sha": "def456" }, "created_at": "2024-01-20T10:00:00Z", "updated_at": "2024-01-20T10:00:00Z" }, "repository": { "id": 1, "name": "repo", "full_name": "username/repo" }, "sender": { "id": 2, "login": "contributor" } } ``` ### Webhook Security **Signature Verification:** GitCaddy signs webhook payloads with HMAC-SHA256 using the configured secret. ```http X-Gitea-Signature: sha256=abc123def456... ``` **Verification Example (Node.js):** ```javascript const crypto = require('crypto'); function verifyWebhook(payload, signature, secret) { const hmac = crypto.createHmac('sha256', secret); hmac.update(payload); const calculatedSignature = 'sha256=' + hmac.digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(calculatedSignature) ); } // Express middleware app.post('/webhook', (req, res) => { const signature = req.headers['x-gitea-signature']; const payload = JSON.stringify(req.body); if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) { return res.status(401).send('Invalid signature'); } // Process webhook res.status(200).send('OK'); }); ``` ### Testing Webhooks **Manual Test:** ```bash curl -X POST https://gitcaddy.example.com/api/v1/repos/username/repo/hooks/1/test \ -H "Authorization: token YOUR_TOKEN" ``` **View Webhook Deliveries:** ```http GET /api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries ``` **Response:** ```json [ { "id": 1, "uuid": "abc-123-def-456", "url": "https://example.com/webhook", "request": { "headers": { "Content-Type": "application/json", "X-Gitea-Event": "push", "X-Gitea-Signature": "sha256=..." }, "body": "{...}" }, "response": { "status": 200, "headers": {}, "body": "OK" }, "delivered_at": "2024-01-20T10:00:00Z", "duration": 0.123 } ] ``` ## Code Examples ### Complete Repository Workflow ```javascript // Create repository async function createRepository(name, description) { const response = await fetch('https://gitcaddy.example.com/api/v1/user/repos', { method: 'POST', headers: { 'Authorization': 'token YOUR_TOKEN', 'Content-Type': 'application/json' }, body: JSON.stringify({ name: name, description: description, private: false, auto_init: true, license: 'MIT' }) }); return response.json(); } // Add file to repository async function addFile(owner, repo, path, content, message) { const encodedContent = btoa(content); const response = await fetch( `https://gitcaddy.example.com/api/v1/repos/${owner}/${repo}/contents/${path}`, { method: 'POST', headers: { 'Authorization': 'token YOUR_TOKEN', 'Content-Type': 'application/json' }, body: JSON.stringify({ content: encodedContent, message: message, branch: 'main' }) } ); return response.json(); } // Create pull request async function createPullRequest(owner, repo, title, head, base) { const response = await fetch( `https://gitcaddy.example.com/api/v1/repos/${owner}/${repo}/pulls`, { method: 'POST', headers: { 'Authorization': 'token YOUR_TOKEN', 'Content-Type': 'application/json' }, body: JSON.stringify({ title: title, head: head, base: base, body: 'Please review these changes' }) } ); return response.json(); } // Usage (async () => { const repo = await createRepository('my-project', 'My awesome project'); console.log('Repository created:', repo.html_url); const file = await addFile( 'username', 'my-project', 'src/index.js', 'console.log("Hello World");', 'Add index.js' ); console.log('File added:', file.commit.sha); const pr = await createPullRequest( 'username', 'my-project', 'Add feature', 'feature-branch', 'main' ); console.log('Pull request created:', pr.html_url); })(); ``` ### Issue Management ```python import requests class GitCaddyClient: def __init__(self, base_url, token): self.base_url = base_url self.headers = { 'Authorization': f'token {token}', 'Content-Type': 'application/json' } def create_issue(self, owner, repo, title, body, labels=None): url = f'{self.base_url}/api/v1/repos/{owner}/{repo}/issues' data = { 'title': title, 'body': body } if labels: data['labels'] = labels response = requests.post(url, json=data, headers=self.headers) return response.json() def add_comment(self, owner, repo, issue_number, comment): url = f'{self.base_url}/api/v1/repos/{owner}/{repo}/issues/{issue_number}/comments' data = {'body': comment} response = requests.post(url, json=data, headers=self.headers) return response.json() def close_issue(self, owner, repo, issue_number): url = f'{self.base_url}/api/v1/repos/{owner}/{repo}/issues/{issue_number}' data = {'state': 'closed'} response = requests.patch(url, json=data, headers=self.headers) return response.json() def track_time(self, owner, repo, issue_number, seconds): url = f'{self.base_url}/api/v1/repos/{owner}/{repo}/issues/{issue_number}/times' data = {'time': seconds} response = requests.post(url, json=data, headers=self.headers) return response.json() # Usage client = GitCaddyClient('https://gitcaddy.example.com', 'YOUR_TOKEN') # Create issue issue = client.create_issue( 'username', 'my-repo', 'Bug: Login not working', 'Users cannot log in with correct credentials', labels=[1, 2] # bug, high-priority ) print(f'Issue created: #{issue["number"]}') # Add comment comment = client.add_comment( 'username', 'my-repo', issue['number'], 'Investigating the issue...' ) # Track time (2 hours) time_entry = client.track_time('username', 'my-repo', issue['number'], 7200) print(f'Tracked {time_entry["time"]}s') # Close issue closed = client.close_issue('username', 'my-repo', issue['number']) print(f'Issue closed: {closed["state"]}') ``` ### CI/CD Integration ```yaml # .gitcaddy/workflows/ci.yml name: CI on: push: branches: [main, develop] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' - name: Install dependencies run: npm ci - name: Run tests run: npm test - name: Get secrets from Vault id: vault uses: actions/vault-secrets@v1 with: secrets: | database_password api_key - name: Deploy if: github.ref == 'refs/heads/main' run: | echo "Deploying with API key: ${{ steps.vault.outputs.api_key }}" npm run deploy env: DATABASE_PASSWORD: ${{ steps.vault.outputs.database_password }} API_KEY: ${{ steps.vault.outputs.api_key }} ``` ### Webhook Handler ```go package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io/ioutil" "log" "net/http" ) type PushEvent struct { Ref string `json:"ref"` Before string `json:"before"` After string `json:"after"` Repository struct { Name string `json:"name"` FullName string `json:"full_name"` } `json:"repository"` Pusher struct { Login string `json:"login"` } `json:"pusher"` Commits []struct { ID string `json:"id"` Message string `json:"message"` Author struct { Name string `json:"name"` Email string `json:"email"` } `json:"author"` } `json:"commits"` } func verifySignature(payload []byte, signature, secret string) bool { mac := hmac.New(sha256.New, []byte(secret)) mac.Write(payload) expectedMAC := "sha256=" + hex.EncodeToString(mac.Sum(nil)) return hmac.Equal([]byte(signature), []byte(expectedMAC)) } func handleWebhook(w http.ResponseWriter, r *http.Request) { payload, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, "Error reading request body", http.StatusBadRequest) return } defer r.Body.Close() signature := r.Header.Get("X-Gitea-Signature") if !verifySignature(payload, signature, "webhook_secret") { http.Error(w, "Invalid signature", http.StatusUnauthorized) return } event := r.Header.Get("X-Gitea-Event") switch event { case "push": var pushEvent PushEvent if err := json.Unmarshal(payload, &pushEvent); err != nil { http.Error(w, "Error parsing JSON", http.StatusBadRequest) return } log.Printf("Push to %s by %s", pushEvent.Repository.FullName, pushEvent.Pusher.Login) log.Printf("Commits: %d", len(pushEvent.Commits)) for _, commit := range pushEvent.Commits { log.Printf(" - %s: %s", commit.ID[:7], commit.Message) } case "pull_request": log.Printf("Pull request event received") default: log.Printf("Unknown event: %s", event) } w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "Webhook processed") } func main() { http.HandleFunc("/webhook", handleWebhook) log.Println("Webhook server listening on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) } ``` ### Package Publishing ```bash #!/bin/bash # publish-npm.sh - Publish npm package to GitCaddy registry GITCADDY_URL="https://gitcaddy.example.com" OWNER="username" TOKEN="YOUR_TOKEN" # Configure npm npm config set registry "${GITCADDY_URL}/api/packages/${OWNER}/npm/" npm config set "//${GITCADDY_URL#https://}/api/packages/${OWNER}/npm/:_authToken" "${TOKEN}" # Update version npm version patch # Publish npm publish echo "Package published to GitCaddy registry" ``` ```dockerfile # Dockerfile - Build and push Docker image to GitCaddy registry FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --production COPY . . EXPOSE 3000 CMD ["node", "server.js"] # Build and push: # docker build -t gitcaddy.example.com/username/myapp:latest . # docker login gitcaddy.example.com # docker push gitcaddy.example.com/username/myapp:latest ``` --- For additional support and documentation, visit the [GitCaddy Documentation](https://docs.gitcaddy.example.com) or join the community forum.