Add project documentation and beads configuration
- 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
This commit is contained in:
parent
62e299192d
commit
9f613b1901
12 changed files with 933 additions and 1 deletions
498
PRD.md
Normal file
498
PRD.md
Normal file
|
|
@ -0,0 +1,498 @@
|
|||
# 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) | - |
|
||||
Loading…
Add table
Add a link
Reference in a new issue