Files
task-master/internal/server/seed.go
T
skrueckenandCursor 472a2fa72e Add global web nav with project groups and tree sidebar.
Restructure the UI into top-level tabs and a scrollable project tree, and persist optional project groups so demos and settings can organize many projects.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-27 22:43:03 +01:00

162 lines
4.8 KiB
Go

package server
import (
"fmt"
"os"
"strings"
"time"
"task/internal/sqlite"
"task/internal/ticket"
)
// SeedDemo populates sample users/projects/tickets when the DB has no projects yet.
// No external backends (YouTrack etc.) — the web UI talks only to this tm_server.
func (s *Server) SeedDemo() error {
projects, err := s.Projects.List()
if err != nil {
return err
}
if len(projects) > 0 {
return nil
}
users, err := s.DB.ListUsers()
if err != nil {
return err
}
if len(users) == 0 {
return fmt.Errorf("seed: no users (bootstrap admin first)")
}
admin := users[0]
for _, u := range users {
if u.IsAdmin {
admin = u
break
}
}
member, err := s.DB.CreateUser("alice", "alice", false)
if err != nil {
member, err = s.DB.GetUserByUsername("alice")
if err != nil {
return fmt.Errorf("seed alice: %w", err)
}
}
type seedTicket struct {
Title, Desc string
Status ticket.Status
}
demos := []struct {
Name string
Tickets []seedTicket
}{
{
Name: "Test",
Tickets: []seedTicket{
{Title: "Board & Login prüfen", Desc: "Mit admin/admin einloggen, Board öffnen, Status ändern.\n\n- [ ] Login\n- [ ] Ticket editieren\n- [ ] Timer starten", Status: ticket.StatusInProgress},
{Title: "Zeit auf Ticket buchen", Desc: "Im Ticket-Detail Timer oder manuelle Minuten buchen, dann Report prüfen.", Status: ticket.StatusTodo},
{Title: "Members & Rechte", Desc: "alice als Member ist schon angelegt — Memberships-Tab ansehen.", Status: ticket.StatusTodo},
{Title: "TUI anbinden", Desc: "Settings → Token (write) erzeugen → in `tm` Projekt-Edit backend=taskmaster eintragen (URL, Token, diese Project-ID).", Status: ticket.StatusTodo},
{Title: "Seed abgeschlossen", Desc: "Beispiel-Ticket im Status DONE.", Status: ticket.StatusDone},
},
},
{
Name: "Website Relaunch",
Tickets: []seedTicket{
{Title: "Landing Hero entwerfen", Desc: "Dunkles Layout, klare CTAs.\n\n- [ ] Moodboard\n- [ ] Wireframe", Status: ticket.StatusInProgress},
{Title: "CMS-Inhalte migrieren", Desc: "Alte Seiten nach Markdown überführen.", Status: ticket.StatusTodo},
{Title: "Impressum & Datenschutz", Desc: "Rechtstexte finalisieren.", Status: ticket.StatusDone},
},
},
{
Name: "API Platform",
Tickets: []seedTicket{
{Title: "Auth-Middleware härten", Desc: "Session-Cookies dokumentieren.", Status: ticket.StatusInProgress},
{Title: "Rate-Limits festlegen", Desc: "Pro User und pro IP.", Status: ticket.StatusTodo},
{Title: "Health-Check", Desc: "Bereits live.", Status: ticket.StatusDone},
},
},
}
for i := len(demos) + 1; i <= 50; i++ {
demos = append(demos, struct {
Name string
Tickets []seedTicket
}{
Name: fmt.Sprintf("Sample %02d", i),
Tickets: []seedTicket{
{Title: "Starter ticket", Desc: "Seed-Platzhalter.", Status: ticket.StatusTodo},
},
})
}
now := time.Now().UTC()
created := make([]struct{ ID, Name string }, 0, len(demos))
for _, d := range demos {
p, err := s.Projects.Create("", d.Name)
if err != nil {
return err
}
created = append(created, struct{ ID, Name string }{p.ID, p.Name})
_ = s.DB.SetMember(p.ID, admin.ID, sqlite.RoleOwner)
_ = s.DB.SetMember(p.ID, member.ID, sqlite.RoleMember)
// Explicit local-only sync (no remote backend in the test environment).
_ = s.Projects.SetSyncBackend(sqlite.SyncBackend{
ProjectID: p.ID,
Kind: "none",
})
var firstID string
for i, st := range d.Tickets {
t, err := s.Tickets.Create(ticket.Ticket{
Title: st.Title,
Description: st.Desc,
Status: st.Status,
ProjectID: p.ID,
})
if err != nil {
return err
}
_, _ = s.DB.AppendRevision("ticket", t.ID, p.ID, "seed", admin.ID, t)
if i == 0 {
firstID = t.ID
}
}
if firstID != "" {
_, err := s.Times.Create(ticket.TimeEntry{
TicketID: firstID,
ProjectID: p.ID,
Start: now.Add(-2 * time.Hour),
End: now.Add(-90 * time.Minute),
Duration: 30 * time.Minute,
Source: ticket.SourceManual,
Note: "seed",
})
if err != nil {
return err
}
}
}
kunden, err := s.DB.CreateProjectGroup("Kunden")
if err != nil {
return fmt.Errorf("seed group Kunden: %w", err)
}
intern, err := s.DB.CreateProjectGroup("Intern")
if err != nil {
return fmt.Errorf("seed group Intern: %w", err)
}
for _, c := range created {
switch {
case c.Name == "Website Relaunch" || c.Name == "API Platform" || strings.HasPrefix(c.Name, "Sample 0"):
_ = s.Projects.SetGroup(c.ID, kunden.ID)
case c.Name == "Test" || strings.HasPrefix(c.Name, "Sample 1") || strings.HasPrefix(c.Name, "Sample 2"):
_ = s.Projects.SetGroup(c.ID, intern.ID)
}
}
fmt.Fprintf(os.Stderr, "seeded demo: %d projects, 2 groups (users %s + alice/alice), sync=none\n", len(demos), admin.Username)
return nil
}