96 lines
4.0 KiB
Markdown
96 lines
4.0 KiB
Markdown
# Forgejo Integration
|
|
|
|
The application syncs support tickets with Forgejo issues, providing a bridge between the customer-facing UI and internal issue tracking.
|
|
|
|
## Configuration
|
|
|
|
Two environment variables are required for the integration:
|
|
|
|
| Variable | Description |
|
|
|----------|-------------|
|
|
| `FORGEJO_URL` | Base URL of your Forgejo instance (e.g. `https://forgejo.example.com`) |
|
|
| `FORGEJO_API_TOKEN` | Personal access token with permission to create issues and comments |
|
|
|
|
See [Configuration](./configuration.md#forgejo) for the full reference.
|
|
|
|
## Ticket-to-Issue Sync
|
|
|
|
When a customer creates a ticket:
|
|
|
|
1. The ticket is saved to the database immediately.
|
|
2. A goroutine asynchronously calls the Forgejo API to create a corresponding issue:
|
|
- **Endpoint**: `POST /api/v1/repos/{owner}/{repo}/issues`
|
|
- **Title**: Same as the ticket title
|
|
- **Body**: Ticket description + a footer line identifying the submitting user's email
|
|
3. On success, the Forgejo issue number is stored on the ticket (`forgejo_issue_number`).
|
|
|
|
If the API call fails, the error is logged but the ticket remains valid. The issue number will be `NULL`, meaning no further syncing occurs for that ticket.
|
|
|
|
## Comment Sync
|
|
|
|
When a customer adds a comment to a ticket that has a linked Forgejo issue:
|
|
|
|
1. The comment is saved to the database immediately.
|
|
2. A goroutine asynchronously calls the Forgejo API to create a comment on the issue:
|
|
- **Endpoint**: `POST /api/v1/repos/{owner}/{repo}/issues/{number}/comments`
|
|
- **Body**: Comment text + a footer line identifying the commenting user's email
|
|
3. On success, the Forgejo comment ID is stored on the ticket comment (`forgejo_comment_id`).
|
|
|
|
Comments are only synced if `forgejo_issue_number` is set on the ticket.
|
|
|
|
## Webhook Setup
|
|
|
|
To receive events from Forgejo (e.g. issue closed), configure a webhook in each Forgejo repository:
|
|
|
|
### 1. Get the Webhook URL
|
|
|
|
The URL follows the pattern:
|
|
|
|
```
|
|
{BASE_URL}/webhooks/forgejo/{slug}
|
|
```
|
|
|
|
Where `{slug}` is the repo slug configured in the [admin panel](./admin-guide.md#repos). For example:
|
|
|
|
```
|
|
https://tickets.example.com/webhooks/forgejo/my-product
|
|
```
|
|
|
|
### 2. Configure in Forgejo
|
|
|
|
In the Forgejo repository settings, go to **Webhooks** and add a new webhook:
|
|
|
|
- **Target URL**: The webhook URL from step 1
|
|
- **HTTP Method**: POST
|
|
- **Content Type**: `application/json`
|
|
- **Secret**: Must match the webhook secret configured for this repo in the admin panel
|
|
- **Events**: Select "Issues" (or use "Custom Events" and check "Issues")
|
|
|
|
### 3. Signature Verification
|
|
|
|
All webhook requests are verified using HMAC-SHA256. The signature is read from the `X-Forgejo-Signature` header and compared against the computed HMAC of the request body using the repo's webhook secret.
|
|
|
|
Requests with missing or invalid signatures are rejected with `401 Unauthorized`.
|
|
|
|
## Auto-Close Flow
|
|
|
|
When an issue is closed in Forgejo, the following happens:
|
|
|
|
1. Forgejo sends a webhook POST to `{BASE_URL}/webhooks/forgejo/{slug}`.
|
|
2. The app verifies the HMAC-SHA256 signature against the repo's webhook secret.
|
|
3. The payload is parsed. Only `"action": "closed"` events are processed; all others return `200 OK` with no action.
|
|
4. The app looks up the ticket by `repo_id` and `forgejo_issue_number`.
|
|
5. If found, the ticket status is updated to `closed`.
|
|
6. A notification email is sent asynchronously to the ticket owner informing them their ticket has been resolved.
|
|
|
|
This enables the internal team to close issues directly in Forgejo and have the status reflected in the customer-facing UI automatically.
|
|
|
|
## Architecture Notes
|
|
|
|
- All Forgejo API calls (issue creation, comment creation) are **asynchronous** — they run in goroutines and don't block the HTTP response.
|
|
- The API client uses a 30-second HTTP timeout.
|
|
- Authentication uses the `Authorization: token {api_token}` header.
|
|
- Webhook processing is **synchronous** — the ticket status update happens before returning the response to Forgejo.
|
|
|
|
See [Admin Guide](./admin-guide.md#repos) for managing repos through the admin UI and [Deployment](./deployment.md) for webhook URL configuration in production.
|