196 lines
6.5 KiB
Markdown
196 lines
6.5 KiB
Markdown
# GiteaCustomApi
|
|
|
|
Gitea repository metadata cache + query API for git.hangman-lab.top.
|
|
|
|
## Overview
|
|
|
|
A lightweight Go service that:
|
|
- Caches repository metadata (id, name, owner, visibility, url) from Gitea MySQL database
|
|
- Refreshes every 5 hours via full sync
|
|
- Listens for Gitea webhook events (repo create/delete) for real-time cache updates
|
|
- Exposes a `/list?username=xxx` endpoint that queries cached repos + live collaborator permissions from MySQL
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ VPS.git │
|
|
│ ┌──────────────┐ ┌─────────────┐ ┌──────────────────┐ │
|
|
│ │ Gitea DB │ │ go-server │ │ Nginx │ │
|
|
│ │ (MySQL) │◄──│ (cache+api) │ │ /c-api -> :8080 │ │
|
|
│ └──────────────┘ └─────────────┘ └──────────────────┘ │
|
|
│ ▲ │
|
|
│ ┌────────┴────────┐ │
|
|
│ │ Gitea Webhook │ │
|
|
│ │ (create/delete)│ │
|
|
│ └─────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Tech Stack
|
|
|
|
- **Language**: Go
|
|
- **Database**: SQLite (local cache)
|
|
- **Source DB**: Gitea MySQL (read-only, same docker network)
|
|
- **HTTP**: Standard library `net/http`
|
|
- **Deployment**: Docker + Docker Compose on vps.git
|
|
- **API Key**: Rotates every 10 minutes, stored in Docker volume at `/data/api-key`
|
|
|
|
## Authentication
|
|
|
|
All `/list` and `/webhook/gitea` endpoints require `Authorization: Bearer <api-key>` header.
|
|
|
|
### API Key Rotation
|
|
|
|
- API key is generated every **10 minutes** by the c-api service itself
|
|
- Stored in the Docker volume at `/data/api-key`
|
|
- Clients must fetch the latest key before each request
|
|
|
|
### Client-side key fetch
|
|
|
|
Before calling any c-api endpoint, fetch the current key:
|
|
|
|
```bash
|
|
API_KEY=$(ssh root@vps.git "cat /path/to/api-key")
|
|
curl -H "Authorization: Bearer $API_KEY" "https://git.hangman-lab.top/c-api/list?username=xxx"
|
|
```
|
|
|
|
> **Note**: Script-side clients (e.g. `list-projs`) should perform this key fetch as part of their request flow. The key changes every 10 minutes so it must be re-fetched each time.
|
|
|
|
## API
|
|
|
|
### `GET /list?username={username}`
|
|
|
|
> **Requires**: `Authorization: Bearer <api-key>` header
|
|
|
|
Returns all repositories visible to the given Gitea user.
|
|
|
|
**Response** (JSON):
|
|
```json
|
|
[
|
|
{
|
|
"name": "ClawSkills",
|
|
"owner": "lyn",
|
|
"url": "https://git.hangman-lab.top/lyn/ClawSkills.git",
|
|
"is_private": false,
|
|
"can_write": true
|
|
}
|
|
]
|
|
```
|
|
|
|
`can_write` is determined at query time by checking:
|
|
- User is the repo owner, OR
|
|
- User has explicit access via `access` table, OR
|
|
- User is a member of a team that has access to the repo
|
|
|
|
### `POST /webhook/gitea`
|
|
|
|
Receives Gitea webhook events (create/delete only).
|
|
|
|
Supported events:
|
|
- `repository.create` — insert new repo into cache
|
|
- `repository.delete` — remove repo from cache
|
|
|
|
### `GET /health`
|
|
|
|
Returns `{"status": "ok"}`.
|
|
|
|
## Data Model
|
|
|
|
### SQLite cache table: `repos`
|
|
|
|
| Column | Type | Notes |
|
|
|--------------|---------|------------------------------|
|
|
| id | INTEGER | Gitea repo ID (primary key) |
|
|
| name | TEXT | |
|
|
| owner | TEXT | Gitea username of owner |
|
|
| is_private | INTEGER | 0 or 1 |
|
|
| url | TEXT | Full .git HTTPS URL |
|
|
| updated_at | INTEGER | Unix timestamp of last sync |
|
|
|
|
## Configuration
|
|
|
|
Environment variables:
|
|
|
|
| Variable | Default | Description |
|
|
|---------------|---------|------------------------------|
|
|
| `DB_HOST` | `mysql` | MySQL container hostname |
|
|
| `DB_USER` | `root` | MySQL username |
|
|
| `DB_PASS` | — | MySQL password |
|
|
| `DB_NAME` | `giteadb` | MySQL database name |
|
|
| `SQLITE_PATH` | `/data/cache.db` | SQLite file path |
|
|
| `API_KEY_FILE` | `/data/api-key` | Path for rotating api-key |
|
|
| `WEBHOOK_SECRET` | — | Gitea webhook secret token |
|
|
| `PORT` | `8080` | HTTP listen port |
|
|
|
|
## Docker
|
|
|
|
### Dockerfile
|
|
|
|
```dockerfile
|
|
FROM golang:1.22-alpine AS build
|
|
WORKDIR /app
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
COPY *.go ./
|
|
RUN CGO_ENABLED=0 GOOS=linux go build -o server .
|
|
CMD ["./server"]
|
|
```
|
|
|
|
### docker-compose.yml (fragment to merge into vps.git compose)
|
|
|
|
```yaml
|
|
services:
|
|
gitea-custom-api:
|
|
build: ./gitea-custom-api
|
|
container_name: gitea-custom-api
|
|
restart: unless-stopped
|
|
environment:
|
|
DB_HOST: mysql
|
|
DB_USER: root
|
|
DB_PASS: ${MYSQL_ROOT_PASSWORD}
|
|
DB_NAME: giteadb
|
|
SQLITE_PATH: /data/cache.db
|
|
WEBHOOK_SECRET: ${GITEA_WEBHOOK_SECRET}
|
|
PORT: 8080
|
|
volumes:
|
|
- ./gitea-custom-api/data:/data
|
|
- ./gitea-custom-api/api-key:/data/api-key
|
|
networks:
|
|
- git-network
|
|
|
|
networks:
|
|
git-network:
|
|
external: true
|
|
```
|
|
|
|
### Nginx location (merge into vps.git nginx config)
|
|
|
|
```nginx
|
|
location /c-api/ {
|
|
proxy_pass http://localhost:8080/;
|
|
}
|
|
```
|
|
|
|
## Gitea Webhook Setup
|
|
|
|
In Gitea admin panel → Webhooks → Add webhook:
|
|
- **URL**: `https://git.hangman-lab.top/c-api/webhook/gitea`
|
|
- **HTTP Method**: POST
|
|
- **Content Type**: `application/json`
|
|
- **Secret**: set a strong token, pass via `WEBHOOK_SECRET`
|
|
- **Events**: Repository: Create, Repository: Delete
|
|
|
|
## Refresh Strategy
|
|
|
|
1. **Startup**: full sync of all non-archived repos from `repository` table
|
|
2. **Every 5 hours**: full sync (upsert based on repo id)
|
|
3. **Webhook**: incremental create/delete for real-time updates
|
|
4. **On cache miss during query**: fall back to live MySQL query
|
|
|
|
## Out of Scope
|
|
|
|
- Authentication on the API endpoint (internal service, only accessible via nginx on vps.git)
|
|
- Caching collaborator permissions (queried live per request)
|
|
- Handling repo rename/transfer (caught by 5h refresh)
|