docs: add GitM design spec
Design document for Gitea repository sync tool with Go backend, Vue 3 frontend, SQLite storage, and single-binary deployment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
271
docs/superpowers/specs/2026-03-31-gitm-gitea-sync-design.md
Normal file
271
docs/superpowers/specs/2026-03-31-gitm-gitea-sync-design.md
Normal file
@@ -0,0 +1,271 @@
|
||||
# GitM - Gitea Repository Sync Tool Design
|
||||
|
||||
## Overview
|
||||
|
||||
GitM is a cross-platform tool that synchronizes all repositories from multiple Gitea servers to local storage. It runs as a single-binary web service with an embedded Vue 3 management UI, supporting both Windows and Linux.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **Backend**: Go + Gin
|
||||
- **Frontend**: Vue 3 + Element Plus + Pinia + Vite
|
||||
- **Database**: SQLite (mattn/go-sqlite3)
|
||||
- **Authentication**: JWT with simple password
|
||||
- **Deployment**: Single binary with embedded frontend (Go embed)
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ gitm (single binary) │
|
||||
│ │
|
||||
│ ┌───────────────┐ ┌───────────────┐ │
|
||||
│ │ Web UI │ │ API Server │ │
|
||||
│ │ (Vue3+EP) │ │ (Gin) │ │
|
||||
│ │ embedded │ │ │ │
|
||||
│ └───────┬───────┘ └───────┬───────┘ │
|
||||
│ │ │ │
|
||||
│ ┌───────┴──────────────────┴───────┐ │
|
||||
│ │ Business Logic │ │
|
||||
│ │ - Server management (CRUD) │ │
|
||||
│ │ - Repo discovery & sync │ │
|
||||
│ │ - Scheduled task runner │ │
|
||||
│ │ - Sync logging │ │
|
||||
│ └───────────────┬───────────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────────┴───────────────┐ │
|
||||
│ │ SQLite Database │ │
|
||||
│ └───────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────────────────────┐ │
|
||||
│ │ Local Storage │ │
|
||||
│ │ repos/ │ │
|
||||
│ │ ├── server1/ │ │
|
||||
│ │ │ ├── owner1/ │ │
|
||||
│ │ │ │ └── repo1.git/ │ │
|
||||
│ │ │ └── owner2/ │ │
|
||||
│ │ └── server2/ │ │
|
||||
│ └───────────────────────────────┘ │
|
||||
└───────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Data Model
|
||||
|
||||
### gitea_servers
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| id | INTEGER PK | Auto-increment ID |
|
||||
| name | TEXT NOT NULL | Display name / alias |
|
||||
| url | TEXT NOT NULL | Gitea server URL (e.g. `https://git.example.com`) |
|
||||
| token | TEXT NOT NULL | API access token |
|
||||
| sync_interval | INTEGER DEFAULT 0 | Sync interval in minutes (0 = manual only) |
|
||||
| last_sync_at | DATETIME | Last successful sync time |
|
||||
| status | TEXT DEFAULT 'active' | active / disabled |
|
||||
| created_at | DATETIME | Creation time |
|
||||
|
||||
### repos
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| id | INTEGER PK | Auto-increment ID |
|
||||
| server_id | INTEGER FK | References gitea_servers.id |
|
||||
| name | TEXT | Repository name |
|
||||
| full_name | TEXT | Full path (owner/repo) |
|
||||
| clone_url | TEXT | Clone URL |
|
||||
| local_path | TEXT | Local storage path |
|
||||
| size | INTEGER | Repository size in bytes |
|
||||
| last_sync_at | DATETIME | Last sync time |
|
||||
| sync_status | TEXT | syncing / success / failed / pending |
|
||||
| created_at | DATETIME | Discovery time |
|
||||
|
||||
### sync_logs
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| id | INTEGER PK | Auto-increment ID |
|
||||
| server_id | INTEGER FK | References gitea_servers.id |
|
||||
| repo_id | INTEGER FK | References repos.id (nullable for full sync) |
|
||||
| status | TEXT | success / failed / in_progress |
|
||||
| message | TEXT | Log message or error details |
|
||||
| started_at | DATETIME | Start time |
|
||||
| finished_at | DATETIME | End time |
|
||||
|
||||
### settings
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| key | TEXT PK | Setting key |
|
||||
| value | TEXT | Setting value |
|
||||
|
||||
Predefined settings: `admin_password`, `listen_addr` (default `:9000`), `repos_dir`, `max_concurrent` (default 3).
|
||||
|
||||
## Gitea API Integration
|
||||
|
||||
### Authentication
|
||||
|
||||
All API requests use `Authorization: token <token>` header or `token=<token>` query parameter.
|
||||
|
||||
### Core API Endpoints Used
|
||||
|
||||
1. **Validate token**: `GET /api/v1/user` - verify token and get user info
|
||||
2. **Search repos**: `GET /api/v1/repos/search?limit=50&page=1` - paginated list of all accessible repos
|
||||
3. **Admin repos** (if admin token): `GET /api/v1/admin/repos` - all repos on server
|
||||
|
||||
### Sync Flow
|
||||
|
||||
```
|
||||
For each active server (per schedule or manual trigger):
|
||||
|
||||
1. Validate token → GET /api/v1/user
|
||||
2. Discover repos → GET /api/v1/repos/search (paginate through all pages)
|
||||
3. Compare with database:
|
||||
- New repos → git clone --mirror <url> to repos/<server>/<owner>/<repo>.git
|
||||
- Existing repos → git fetch --all --prune (incremental update)
|
||||
- Repos removed from Gitea → mark as deleted in DB (optional cleanup)
|
||||
4. Record sync result to sync_logs
|
||||
5. Update repos table (last_sync_at, sync_status)
|
||||
```
|
||||
|
||||
### Concurrency Control
|
||||
|
||||
- Worker pool with configurable max concurrency (default: 3)
|
||||
- Each server sync is serialized (repos within a server sync concurrently up to limit)
|
||||
- Long-running sync operations return task_id for status polling
|
||||
|
||||
## API Routes
|
||||
|
||||
### Authentication
|
||||
|
||||
```
|
||||
POST /api/login # Password auth, returns JWT token
|
||||
```
|
||||
|
||||
### Settings
|
||||
|
||||
```
|
||||
GET /api/settings # Get all settings (password masked)
|
||||
PUT /api/settings # Update settings
|
||||
```
|
||||
|
||||
### Server Management
|
||||
|
||||
```
|
||||
GET /api/servers # List all servers
|
||||
POST /api/servers # Add server
|
||||
PUT /api/servers/:id # Update server
|
||||
DELETE /api/servers/:id # Delete server
|
||||
POST /api/servers/:id/test # Test connection (validate token)
|
||||
```
|
||||
|
||||
### Repository Management
|
||||
|
||||
```
|
||||
GET /api/servers/:id/repos # List repos for a server
|
||||
POST /api/servers/:id/discover # Trigger repo discovery from Gitea API
|
||||
```
|
||||
|
||||
### Sync Operations
|
||||
|
||||
```
|
||||
POST /api/servers/:id/sync # Trigger sync for one server
|
||||
POST /api/sync/all # Trigger sync for all servers
|
||||
GET /api/servers/:id/sync/status # Get current sync status
|
||||
```
|
||||
|
||||
### Logs & Stats
|
||||
|
||||
```
|
||||
GET /api/sync/logs # Sync logs (paginated, filterable)
|
||||
GET /api/sync/stats # Dashboard stats
|
||||
```
|
||||
|
||||
All routes except `/api/login` require `Authorization: Bearer <token>` header.
|
||||
|
||||
## Web UI Pages
|
||||
|
||||
1. **Dashboard** - Overview: server count, total repos, disk usage, recent sync status
|
||||
2. **Server Management** - Add/edit/delete Gitea servers, test connection, trigger sync
|
||||
3. **Repository List** - Filter by server, show name, size, sync status, last sync time
|
||||
4. **Sync Logs** - Sync history, filter by status (success/failed)
|
||||
5. **Settings** - Change password, listen address, storage path, concurrency config
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
gitm/
|
||||
├── main.go # Entry point: parse flags, start server
|
||||
├── go.mod
|
||||
├── go.sum
|
||||
├── internal/
|
||||
│ ├── config/ # Configuration management
|
||||
│ ├── database/ # SQLite init & migration
|
||||
│ ├── models/ # Data model definitions
|
||||
│ ├── gitea/ # Gitea API client
|
||||
│ ├── sync/ # Sync engine (clone/fetch/scheduler)
|
||||
│ ├── middleware/ # JWT auth middleware
|
||||
│ └── handler/ # HTTP handlers (API route handlers)
|
||||
├── web/ # Vue 3 frontend source
|
||||
│ ├── src/
|
||||
│ │ ├── views/ # Page components
|
||||
│ │ ├── components/ # Shared components
|
||||
│ │ ├── api/ # API call wrappers
|
||||
│ │ └── stores/ # Pinia state management
|
||||
│ ├── dist/ # Build output (embed target)
|
||||
│ └── vite.config.ts
|
||||
├── Makefile # Build & package commands
|
||||
└── docs/ # Documentation
|
||||
```
|
||||
|
||||
## Build & Run
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
# Build frontend
|
||||
cd web && npm install && npm run build
|
||||
|
||||
# Build binary
|
||||
go build -o gitm .
|
||||
|
||||
# Cross-compile
|
||||
GOOS=linux GOARCH=amd64 go build -o gitm-linux .
|
||||
GOOS=windows GOARCH=amd64 go build -o gitm.exe .
|
||||
```
|
||||
|
||||
### Run
|
||||
|
||||
```bash
|
||||
# First run (init database, set password)
|
||||
./gitm --init
|
||||
|
||||
# Normal run (default port 9000)
|
||||
./gitm
|
||||
|
||||
# Custom port
|
||||
./gitm --addr :9090
|
||||
|
||||
# Custom data directory
|
||||
./gitm --data /path/to/data
|
||||
```
|
||||
|
||||
### Runtime Flags
|
||||
|
||||
| Flag | Default | Description |
|
||||
|------|---------|-------------|
|
||||
| `--addr` | `:9000` | Listen address |
|
||||
| `--data` | `./data` | Data directory (SQLite DB + repos) |
|
||||
| `--init` | false | Initialize config and set password |
|
||||
|
||||
## Error Handling
|
||||
|
||||
- Gitea API errors: log and continue with next repo, mark failed in sync_logs
|
||||
- Git clone/fetch failures: retry once, then mark as failed
|
||||
- Network timeouts: 30s for API calls, no timeout for git operations (large repos)
|
||||
- Disk space: check available space before sync, warn if below 1GB
|
||||
|
||||
## Security
|
||||
|
||||
- Password hashed with bcrypt before storage
|
||||
- JWT tokens with configurable expiry (default 24h)
|
||||
- Gitea tokens encrypted at rest using AES-256 with a key derived from the admin password
|
||||
- API not accessible externally by default (bind to localhost, configurable)
|
||||
Reference in New Issue
Block a user