bookstack-api/PRD.md
Oliver Jakoubek 9f613b1901 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
2026-01-30 09:38:32 +01:00

13 KiB
Raw Blame History

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.

hqcli docs search "deployment" --json

US2: Seite abrufen

Als Benutzer möchte ich eine Dokumentationsseite anzeigen können, um deren Inhalt zu lesen.

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.

hqcli docs books --json

US4: Seite exportieren

Als Benutzer möchte ich eine Seite als Markdown oder PDF exportieren können.

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.

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

// 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

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:

client := bookstack.NewClient(bookstack.Config{
    BaseURL:     "https://docs.jakoubek.net",
    TokenID:     os.Getenv("BOOKSTACK_TOKEN_ID"),
    TokenSecret: os.Getenv("BOOKSTACK_TOKEN_SECRET"),
})

Service-Struktur

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)

// 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)

# 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

// 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

// 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

// 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

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) -