- Added CLAUDE.md with project overview and architecture details - Added PRD.md with product requirements - Added AGENTS.md for agent workflow documentation - Initialized beads issue tracking system - Added .gitattributes for Git configuration
498 lines
13 KiB
Markdown
498 lines
13 KiB
Markdown
# Product Requirements Document: bookstack-api
|
||
|
||
## 1. Projektübersicht
|
||
|
||
- **Ziel und Vision:**
|
||
Eine Go-Library für die Bookstack REST-API, die programmatischen Zugriff auf das Dokumentationssystem ermöglicht. Die Library wird in hqcli integriert und soll veröffentlichungsfähig sein.
|
||
|
||
- **Zielgruppe:**
|
||
- Primär: AI-Agenten (via hqcli mit `--json` Flag)
|
||
- Sekundär: Go-Entwickler, die Bookstack programmatisch nutzen wollen
|
||
|
||
- **Erfolgs-Metriken:**
|
||
- Vollständige Abdeckung der Kern-API (Books, Pages, Search)
|
||
- Nutzbar in hqcli für AI-gestützte Dokumentationssuche
|
||
- Veröffentlichung als eigenständiges Go-Modul
|
||
|
||
- **Projektscope:**
|
||
- **In Scope:** REST-API-Client für Bookstack, Iterator-basierte Pagination, Export-Funktionen
|
||
- **Out of Scope:** Webhooks, Caching, Admin-Funktionen (Users, Roles)
|
||
|
||
## 2. Funktionale Anforderungen
|
||
|
||
### Kern-Features
|
||
|
||
| Feature | Priorität | Version |
|
||
|---------|-----------|---------|
|
||
| Books: List, Get | P0 | v0.1 |
|
||
| Pages: List, Get | P0 | v0.1 |
|
||
| Search: All | P0 | v0.1 |
|
||
| Pages: Export (Markdown, PDF) | P1 | v0.2 |
|
||
| Chapters: List, Get | P1 | v0.2 |
|
||
| Shelves: List, Get | P1 | v0.2 |
|
||
| Pages: Create, Update | P1 | v0.3 |
|
||
| Pages: Delete | P2 | v0.4 |
|
||
| Attachments: CRUD | P2 | v0.4 |
|
||
| Comments: CRUD | P3 | v0.5 |
|
||
|
||
### User Stories
|
||
|
||
**US1: Dokumentation durchsuchen (AI-Agent)**
|
||
> Als AI-Agent möchte ich die Bookstack-Dokumentation durchsuchen können, um relevante Seiten für Benutzeranfragen zu finden.
|
||
|
||
```bash
|
||
hqcli docs search "deployment" --json
|
||
```
|
||
|
||
**US2: Seite abrufen**
|
||
> Als Benutzer möchte ich eine Dokumentationsseite anzeigen können, um deren Inhalt zu lesen.
|
||
|
||
```bash
|
||
hqcli docs page 123
|
||
hqcli docs page deployment-guide # via Slug
|
||
```
|
||
|
||
**US3: Bücher auflisten**
|
||
> Als Benutzer möchte ich alle verfügbaren Bücher sehen, um die Dokumentationsstruktur zu verstehen.
|
||
|
||
```bash
|
||
hqcli docs books --json
|
||
```
|
||
|
||
**US4: Seite exportieren**
|
||
> Als Benutzer möchte ich eine Seite als Markdown oder PDF exportieren können.
|
||
|
||
```bash
|
||
hqcli docs page 123 --export=md > page.md
|
||
hqcli docs page 123 --export=pdf > page.pdf
|
||
```
|
||
|
||
**US5: Seite im Browser öffnen**
|
||
> Als Benutzer möchte ich eine Seite schnell im Browser öffnen können.
|
||
|
||
```bash
|
||
hqcli docs open 123
|
||
```
|
||
|
||
### Detaillierte Workflows
|
||
|
||
**Workflow: Suche und Anzeige**
|
||
```
|
||
1. Benutzer/Agent führt Suche aus
|
||
2. API gibt Liste von Treffern zurück (ID, Typ, Name, Preview)
|
||
3. Benutzer/Agent wählt Treffer aus
|
||
4. Seite wird abgerufen und angezeigt (Markdown oder JSON)
|
||
```
|
||
|
||
**Workflow: Seite bearbeiten (v0.3)**
|
||
```
|
||
1. Seite abrufen (Get)
|
||
2. Inhalt lokal bearbeiten
|
||
3. Seite aktualisieren (Update)
|
||
```
|
||
|
||
### Feature-Prioritäten
|
||
|
||
- **Must-have (v1):** List, Get, Search für Books/Pages
|
||
- **Should-have (v1):** Export Markdown/PDF, Chapters, Shelves
|
||
- **Nice-to-have (v2):** Create, Update, Delete
|
||
- **Future:** Attachments, Comments, Image Gallery
|
||
|
||
## 3. Technische Anforderungen
|
||
|
||
- **Performance-Ziele:**
|
||
- API-Calls < 500ms (abhängig von Netzwerk)
|
||
- Iterator verarbeitet 10.000+ Einträge ohne Memory-Probleme
|
||
|
||
- **Concurrent User-Kapazität:**
|
||
Nicht zutreffend (Library, kein Server)
|
||
|
||
- **Real-time Features:**
|
||
Nicht zutreffend
|
||
|
||
- **Sicherheitsstandards:**
|
||
- Token-basierte Authentifizierung (Token ID + Secret)
|
||
- Keine Speicherung von Credentials (Aufrufer-Verantwortung)
|
||
|
||
- **Compliance-Vorgaben:**
|
||
Keine speziellen
|
||
|
||
- **Plattform-Support:**
|
||
- Go 1.21+
|
||
- Linux, macOS, Windows
|
||
|
||
## 4. Datenarchitektur
|
||
|
||
*Nicht zutreffend – keine eigene Datenhaltung*
|
||
|
||
### Bookstack-Hierarchie (extern)
|
||
|
||
```
|
||
Shelf (Regal)
|
||
└── Book (Buch)
|
||
├── Chapter (Kapitel)
|
||
│ └── Page (Seite)
|
||
└── Page (Seite)
|
||
```
|
||
|
||
### Datenstrukturen
|
||
|
||
```go
|
||
// Book repräsentiert ein Bookstack-Buch
|
||
type Book struct {
|
||
ID int `json:"id"`
|
||
Name string `json:"name"`
|
||
Slug string `json:"slug"`
|
||
Description string `json:"description"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
UpdatedAt time.Time `json:"updated_at"`
|
||
CreatedBy int `json:"created_by"`
|
||
UpdatedBy int `json:"updated_by"`
|
||
}
|
||
|
||
// Page repräsentiert eine Bookstack-Seite
|
||
type Page struct {
|
||
ID int `json:"id"`
|
||
BookID int `json:"book_id"`
|
||
ChapterID int `json:"chapter_id"`
|
||
Name string `json:"name"`
|
||
Slug string `json:"slug"`
|
||
HTML string `json:"html"`
|
||
RawHTML string `json:"raw_html"`
|
||
Markdown string `json:"markdown"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
UpdatedAt time.Time `json:"updated_at"`
|
||
}
|
||
|
||
// Chapter repräsentiert ein Bookstack-Kapitel
|
||
type Chapter struct {
|
||
ID int `json:"id"`
|
||
BookID int `json:"book_id"`
|
||
Name string `json:"name"`
|
||
Slug string `json:"slug"`
|
||
Description string `json:"description"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
UpdatedAt time.Time `json:"updated_at"`
|
||
}
|
||
|
||
// Shelf repräsentiert ein Bookstack-Regal
|
||
type Shelf struct {
|
||
ID int `json:"id"`
|
||
Name string `json:"name"`
|
||
Slug string `json:"slug"`
|
||
Description string `json:"description"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
UpdatedAt time.Time `json:"updated_at"`
|
||
}
|
||
|
||
// SearchResult repräsentiert ein Suchergebnis
|
||
type SearchResult struct {
|
||
ID int `json:"id"`
|
||
Name string `json:"name"`
|
||
Slug string `json:"slug"`
|
||
Type string `json:"type"` // page, chapter, book, bookshelf
|
||
URL string `json:"url"`
|
||
Preview string `json:"preview"`
|
||
}
|
||
```
|
||
|
||
## 5. API & Interface-Spezifikation
|
||
|
||
### Client-Initialisierung
|
||
|
||
```go
|
||
type Config struct {
|
||
BaseURL string // z.B. "https://docs.jakoubek.net"
|
||
TokenID string // API Token ID
|
||
TokenSecret string // API Token Secret
|
||
HTTPClient *http.Client // optional, für Tests/Mocking
|
||
}
|
||
|
||
func NewClient(cfg Config) *Client
|
||
```
|
||
|
||
**Beispiel:**
|
||
```go
|
||
client := bookstack.NewClient(bookstack.Config{
|
||
BaseURL: "https://docs.jakoubek.net",
|
||
TokenID: os.Getenv("BOOKSTACK_TOKEN_ID"),
|
||
TokenSecret: os.Getenv("BOOKSTACK_TOKEN_SECRET"),
|
||
})
|
||
```
|
||
|
||
### Service-Struktur
|
||
|
||
```go
|
||
type Client struct {
|
||
Books *BooksService
|
||
Pages *PagesService
|
||
Chapters *ChaptersService
|
||
Shelves *ShelvesService
|
||
Search *SearchService
|
||
}
|
||
```
|
||
|
||
### REST-Endpoints (Bookstack API)
|
||
|
||
| Service | Methode | Endpoint | Beschreibung |
|
||
|---------|---------|----------|--------------|
|
||
| Books | List | GET /api/books | Alle Bücher |
|
||
| Books | Get | GET /api/books/{id} | Einzelnes Buch |
|
||
| Pages | List | GET /api/pages | Alle Seiten |
|
||
| Pages | Get | GET /api/pages/{id} | Einzelne Seite |
|
||
| Pages | Create | POST /api/pages | Seite erstellen |
|
||
| Pages | Update | PUT /api/pages/{id} | Seite aktualisieren |
|
||
| Pages | Delete | DELETE /api/pages/{id} | Seite löschen |
|
||
| Pages | ExportMD | GET /api/pages/{id}/export/markdown | Markdown-Export |
|
||
| Pages | ExportPDF | GET /api/pages/{id}/export/pdf | PDF-Export |
|
||
| Chapters | List | GET /api/chapters | Alle Kapitel |
|
||
| Chapters | Get | GET /api/chapters/{id} | Einzelnes Kapitel |
|
||
| Shelves | List | GET /api/shelves | Alle Regale |
|
||
| Shelves | Get | GET /api/shelves/{id} | Einzelnes Regal |
|
||
| Search | All | GET /api/search?query=... | Volltextsuche |
|
||
|
||
### Authentifizierung
|
||
|
||
```
|
||
Authorization: Token <token_id>:<token_secret>
|
||
```
|
||
|
||
### Pagination (Iterator-Pattern)
|
||
|
||
```go
|
||
// ListAll gibt einen Iterator über alle Einträge zurück
|
||
// Nutzt Go 1.23+ iter.Seq oder eigene Implementation
|
||
func (s *BooksService) ListAll(ctx context.Context) iter.Seq2[*Book, error]
|
||
|
||
// Nutzung:
|
||
for book, err := range client.Books.ListAll(ctx) {
|
||
if err != nil {
|
||
return err
|
||
}
|
||
fmt.Println(book.Name)
|
||
}
|
||
```
|
||
|
||
**Begründung:** Iterator-Pattern ist Go-idiomatisch (ab 1.23), Memory-effizient und ermöglicht frühen Abbruch.
|
||
|
||
### Rate Limiting
|
||
|
||
- Bookstack: 180 Requests/Minute (default)
|
||
- **Keine Library-interne Behandlung** – Aufrufer muss Rate-Limiting selbst handhaben
|
||
- Bei 429-Response: `ErrRateLimited` zurückgeben
|
||
|
||
## 6. Benutzeroberfläche
|
||
|
||
*Nicht zutreffend – Library ohne UI*
|
||
|
||
### hqcli-Integration (separates Projekt)
|
||
|
||
```bash
|
||
# Bücher auflisten
|
||
hqcli docs books
|
||
hqcli docs books --json
|
||
|
||
# Seiten eines Buchs
|
||
hqcli docs pages --book=<id|slug>
|
||
|
||
# Seite anzeigen
|
||
hqcli docs page <id|slug>
|
||
hqcli docs page <id> --json
|
||
|
||
# Seite exportieren
|
||
hqcli docs page <id> --export=md
|
||
hqcli docs page <id> --export=pdf
|
||
|
||
# Suche
|
||
hqcli docs search "query"
|
||
hqcli docs search "query" --json
|
||
|
||
# Im Browser öffnen
|
||
hqcli docs open <id|slug>
|
||
```
|
||
|
||
**Output-Priorität:** `--json` für AI-Agenten ist Hauptanwendungsfall
|
||
|
||
## 7. Nicht-funktionale Anforderungen
|
||
|
||
- **Verfügbarkeit:**
|
||
Nicht zutreffend (Library)
|
||
|
||
- **Dependencies:**
|
||
Nur Go-Standardbibliothek (net/http, encoding/json, etc.)
|
||
|
||
- **Backward Compatibility:**
|
||
Semantic Versioning (v0.x während Entwicklung, v1.x nach Stabilisierung)
|
||
|
||
- **Logging-Strategie:**
|
||
Keine eigene Logging – Fehler werden als `error` zurückgegeben
|
||
|
||
- **Konfiguration:**
|
||
Via `Config`-Struct bei Client-Erstellung
|
||
|
||
## 8. Qualitätssicherung
|
||
|
||
### Definition of Done
|
||
|
||
- [ ] Alle public APIs dokumentiert (GoDoc)
|
||
- [ ] Unit-Tests für alle Services (≥80% Coverage)
|
||
- [ ] Integration-Tests gegen Mock-Server
|
||
- [ ] Beispiel-Code in examples/
|
||
- [ ] README mit Quick-Start
|
||
|
||
### Test-Anforderungen
|
||
|
||
```go
|
||
// Unit-Tests mit Mock-HTTP-Client
|
||
func TestBooksService_List(t *testing.T) {
|
||
server := httptest.NewServer(...)
|
||
client := bookstack.NewClient(bookstack.Config{
|
||
BaseURL: server.URL,
|
||
TokenID: "test",
|
||
TokenSecret: "test",
|
||
})
|
||
|
||
books, err := client.Books.List(ctx, nil)
|
||
// assertions...
|
||
}
|
||
```
|
||
|
||
### Launch-Kriterien v1.0
|
||
|
||
- [ ] Books, Pages, Search vollständig implementiert
|
||
- [ ] Export (Markdown, PDF) funktioniert
|
||
- [ ] Dokumentation vollständig
|
||
- [ ] Keine bekannten Bugs
|
||
- [ ] hqcli-Integration getestet
|
||
|
||
## 9. Technische Implementierungshinweise
|
||
|
||
### Go-Projektstruktur
|
||
|
||
```
|
||
bookstack-api/
|
||
├── bookstack.go # Client, Config, NewClient()
|
||
├── books.go # BooksService
|
||
├── pages.go # PagesService
|
||
├── chapters.go # ChaptersService
|
||
├── shelves.go # ShelvesService
|
||
├── search.go # SearchService
|
||
├── types.go # Alle Datenstrukturen
|
||
├── errors.go # Error-Typen
|
||
├── http.go # HTTP-Helfer, Request-Building
|
||
├── iterator.go # Pagination-Iterator
|
||
├── bookstack_test.go # Tests
|
||
├── README.md
|
||
├── go.mod
|
||
├── go.sum
|
||
└── examples/
|
||
└── basic/
|
||
└── main.go
|
||
```
|
||
|
||
### Error-Handling-Strategie
|
||
|
||
```go
|
||
// Definierte Error-Typen für häufige Fälle
|
||
var (
|
||
ErrNotFound = errors.New("bookstack: resource not found")
|
||
ErrUnauthorized = errors.New("bookstack: unauthorized")
|
||
ErrForbidden = errors.New("bookstack: forbidden")
|
||
ErrRateLimited = errors.New("bookstack: rate limited")
|
||
ErrBadRequest = errors.New("bookstack: bad request")
|
||
)
|
||
|
||
// APIError für detaillierte Fehlerinformationen
|
||
type APIError struct {
|
||
StatusCode int
|
||
Code int `json:"code"`
|
||
Message string `json:"message"`
|
||
Body []byte // Original Response Body
|
||
}
|
||
|
||
func (e *APIError) Error() string {
|
||
return fmt.Sprintf("bookstack: API error %d: %s", e.StatusCode, e.Message)
|
||
}
|
||
|
||
func (e *APIError) Is(target error) bool {
|
||
switch target {
|
||
case ErrNotFound:
|
||
return e.StatusCode == 404
|
||
case ErrUnauthorized:
|
||
return e.StatusCode == 401
|
||
case ErrForbidden:
|
||
return e.StatusCode == 403
|
||
case ErrRateLimited:
|
||
return e.StatusCode == 429
|
||
case ErrBadRequest:
|
||
return e.StatusCode == 400
|
||
}
|
||
return false
|
||
}
|
||
```
|
||
|
||
**Begründung:** `errors.Is()` ermöglicht einfache Fehlerprüfung, `APIError` bietet Details wenn nötig.
|
||
|
||
### HTTP-Wrapper
|
||
|
||
```go
|
||
// Interner HTTP-Helfer
|
||
func (c *Client) do(ctx context.Context, method, path string, body, result any) error {
|
||
// 1. Request bauen
|
||
// 2. Auth-Header setzen
|
||
// 3. Request ausführen
|
||
// 4. Response prüfen
|
||
// 5. Bei Fehler: APIError zurückgeben
|
||
// 6. Bei Erfolg: JSON in result unmarshalen
|
||
}
|
||
```
|
||
|
||
### Entwicklungs-Prioritäten
|
||
|
||
1. **Phase 1 (v0.1):** Foundation
|
||
- Client-Setup, Auth, HTTP-Wrapper
|
||
- Books.List, Books.Get
|
||
- Pages.List, Pages.Get
|
||
- Error-Handling
|
||
|
||
2. **Phase 2 (v0.2):** Core Features
|
||
- Search.All
|
||
- Pages.ExportMarkdown, Pages.ExportPDF
|
||
- Iterator für Pagination
|
||
- Chapters, Shelves
|
||
|
||
3. **Phase 3 (v0.3):** Write Operations
|
||
- Pages.Create
|
||
- Pages.Update
|
||
|
||
4. **Phase 4 (v1.0):** Release
|
||
- Dokumentation
|
||
- Beispiele
|
||
- CI/CD
|
||
- Veröffentlichung
|
||
|
||
### Potenzielle Risiken
|
||
|
||
| Risiko | Wahrscheinlichkeit | Mitigation |
|
||
|--------|-------------------|------------|
|
||
| API-Änderungen in Bookstack | Niedrig | Semantic Versioning, Tests |
|
||
| Rate-Limiting-Probleme | Mittel | Dokumentation für Aufrufer |
|
||
| Große PDF-Exports | Mittel | Streaming statt Buffer |
|
||
|
||
---
|
||
|
||
## Anhang: Bookstack API-Referenz
|
||
|
||
- **Basis-URL:** https://docs.jakoubek.net/api
|
||
- **Dokumentation:** https://docs.jakoubek.net/api/docs
|
||
- **Beispiele:** https://codeberg.org/bookstack/api-scripts
|
||
- **Rate Limit:** 180 req/min (konfigurierbar serverseitig)
|
||
|
||
### Pagination-Parameter
|
||
|
||
| Parameter | Beschreibung | Default |
|
||
|-----------|--------------|---------|
|
||
| count | Anzahl Ergebnisse | 100 (max 500) |
|
||
| offset | Start-Position | 0 |
|
||
| sort | Sortierung (+name, -created_at) | - |
|
||
| filter[field] | Filter (eq, ne, gt, lt, like) | - |
|