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