2
0
Files
gitcaddy-server/API.md
logikonline 8806fcecba
Some checks failed
Build and Release / Create Release (push) Successful in 0s
Build and Release / Unit Tests (push) Successful in 3m19s
Build and Release / Integration Tests (PostgreSQL) (push) Successful in 4m39s
Build and Release / Lint (push) Successful in 4m46s
Build and Release / Build Binary (linux/arm64) (push) Has been cancelled
Build and Release / Build Binaries (amd64, linux, linux-latest) (push) Has been cancelled
Build and Release / Build Binaries (amd64, windows, windows-latest) (push) Has been cancelled
Build and Release / Build Binaries (amd64, darwin, macos) (push) Has been cancelled
Build and Release / Build Binaries (arm64, darwin, macos) (push) Has been cancelled
docs(detached-note): add comprehensive API reference and user guide
Add API.md (3200+ lines) with complete REST API documentation covering authentication, repository management, issues, pull requests, organizations, package registry, Actions, and Vault APIs. Includes code examples and error handling.

Add GUIDE.md with user-focused documentation for getting started, repository operations, collaboration workflows, and CI/CD setup.

Implement documentation tab in repository view with automatic detection and rendering of API.md and GUIDE.md files alongside README. Add locale strings and template for doc tab navigation.
2026-01-27 22:43:56 -05:00

3226 lines
64 KiB
Markdown

# 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
<repositories>
<repository>
<id>gitcaddy</id>
<url>https://gitcaddy.example.com/api/packages/{owner}/maven</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>gitcaddy</id>
<url>https://gitcaddy.example.com/api/packages/{owner}/maven</url>
</repository>
</distributionManagement>
```
**Configure authentication in `~/.m2/settings.xml`:**
```xml
<servers>
<server>
<id>gitcaddy</id>
<username>{username}</username>
<password>{token}</password>
</server>
</servers>
```
#### 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 = '<script>alert("xss")</script>';
const safe = htmlEscape(userInput);
// Result: &lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;
```
#### 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 = '<div class="alert">Message</div>';
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
<absolute-date date="2024-01-20T10:00:00Z" format="short"></absolute-date>
```
**JavaScript API:**
```javascript
const dateElement = document.querySelector('absolute-date');
dateElement.setAttribute('date', '2024-01-21T12:00:00Z');
```
#### Origin URL Component
```html
<origin-url data-url="https://example.com/page"></origin-url>
```
Displays sanitized origin URL with proper formatting.
#### Overflow Menu Component
```html
<overflow-menu>
<button class="item">Action 1</button>
<button class="item">Action 2</button>
<button class="item">Action 3</button>
</overflow-menu>
```
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
<file-tree-view :files="files" @file-click="handleFileClick"></file-tree-view>
```
#### 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: '<strong>Help:</strong> 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.