Files
nib-v1/cmd/demo.go
T
lerko d715b053e7 refactor(db): thread context.Context through all Store methods
Enables request-scoped cancellation, timeouts, and graceful shutdown
for all database operations across API handlers, CLI commands, and TUI.
2026-05-20 20:51:51 -04:00

141 lines
2.9 KiB
Go

package cmd
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
"github.com/lerko/nib/internal/db"
"github.com/spf13/cobra"
)
var demoCmd = &cobra.Command{
Use: "demo",
Short: "start server with pre-populated demo data",
RunE: runDemo,
}
func init() {
rootCmd.AddCommand(demoCmd)
}
type demoEntity struct {
Body string `json:"body"`
Glyph string `json:"glyph"`
Title *string `json:"title,omitempty"`
Description *string `json:"description,omitempty"`
TimeAnchor *string `json:"time_anchor,omitempty"`
Pinned bool `json:"pinned"`
Completed bool `json:"completed"`
Deleted bool `json:"deleted"`
CardType *string `json:"card_type,omitempty"`
CardData *string `json:"card_data,omitempty"`
Tags []string `json:"tags"`
}
func runDemo(cmd *cobra.Command, _ []string) error {
tmpDir, err := os.MkdirTemp("", "nib-demo-*")
if err != nil {
return err
}
dbPath := filepath.Join(tmpDir, "demo.db")
fmt.Printf("demo db: %s\n", dbPath)
store, err := db.Open(dbPath)
if err != nil {
return err
}
if err := seedDemo(cmd.Context(), store); err != nil {
store.Close()
return fmt.Errorf("seed demo data: %w", err)
}
store.Close()
os.Setenv("NIB_DB", dbPath)
return runServe(nil, nil)
}
func seedDemo(ctx context.Context, store *db.Store) error {
data, err := findDemoFile()
if err != nil {
return err
}
var entries []demoEntity
if err := json.Unmarshal(data, &entries); err != nil {
return fmt.Errorf("parse demo.json: %w", err)
}
now := time.Now().UTC()
for i, entry := range entries {
e := &db.Entity{
Body: entry.Body,
Glyph: db.Glyph(entry.Glyph),
Tags: entry.Tags,
}
if entry.Title != nil {
e.Title = entry.Title
}
if entry.Description != nil {
e.Description = entry.Description
}
if entry.TimeAnchor != nil {
e.TimeAnchor = entry.TimeAnchor
}
if entry.Pinned {
e.Pinned = true
}
if entry.Completed {
t := now.Add(-time.Duration(i) * time.Hour)
e.CompletedAt = &t
}
if err := store.Create(ctx, e); err != nil {
return fmt.Errorf("entity %d: %w", i, err)
}
if entry.CardType != nil {
ct := db.CardType(*entry.CardType)
if err := store.Promote(ctx, e.ID, ct, entry.CardData); err != nil {
return fmt.Errorf("promote entity %d: %w", i, err)
}
}
if entry.Deleted {
store.SoftDelete(ctx, e.ID)
}
}
fmt.Printf("seeded %d entities\n", len(entries))
return nil
}
func findDemoFile() ([]byte, error) {
candidates := []string{
"testdata/demo.json",
filepath.Join(execDir(), "testdata", "demo.json"),
}
for _, path := range candidates {
data, err := os.ReadFile(path)
if err == nil {
return data, nil
}
}
return nil, fmt.Errorf("demo.json not found (looked in: %v)", candidates)
}
func execDir() string {
exe, err := os.Executable()
if err != nil {
return "."
}
return filepath.Dir(exe)
}