Files
agent 255e8dcf71 feat: initial scaffold of kanban_cpp v2 (issue 0130)
Frontend C++ ImGui (main.cpp + 4 paneles) + backend Go (HTTP + SQLite + fsnotify + SSE).
Reusa parse/scan/watch funcs del registry (issue 0130a).
2026-05-22 22:19:47 +02:00

138 lines
3.4 KiB
Go

package main
import (
"database/sql"
"encoding/json"
"log"
"path/filepath"
"strings"
"fn-registry/functions/infra"
)
func (s *Server) ingestAll() error {
issues, err := infra.ScanIssuesDir(s.issuesDir)
if err != nil {
return err
}
for _, iss := range issues {
if err := s.upsertIssueRow(iss); err != nil {
log.Printf("upsert issue %s: %v", iss.ID, err)
}
}
flows, err := infra.ScanFlowsDir(s.flowsDir)
if err != nil {
return err
}
for _, fl := range flows {
if err := s.upsertFlowRow(fl); err != nil {
log.Printf("upsert flow %s: %v", fl.ID, err)
}
}
log.Printf("ingested %d issues, %d flows", len(issues), len(flows))
return nil
}
func (s *Server) upsertIssueRow(iss infra.Issue) error {
body, err := readBody(iss.FilePath)
if err != nil {
return err
}
_, err = s.db.Exec(`
INSERT INTO issues (id,title,status,type,scope,priority,domain_json,tags_json,depends_json,blocks_json,related_json,flow_id,body,file_path,mtime_ns,created_at,updated_at,completed)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
ON CONFLICT(id) DO UPDATE SET
title=excluded.title, status=excluded.status, type=excluded.type, scope=excluded.scope,
priority=excluded.priority, domain_json=excluded.domain_json, tags_json=excluded.tags_json,
depends_json=excluded.depends_json, blocks_json=excluded.blocks_json, related_json=excluded.related_json,
flow_id=excluded.flow_id, body=excluded.body, file_path=excluded.file_path, mtime_ns=excluded.mtime_ns,
created_at=excluded.created_at, updated_at=excluded.updated_at, completed=excluded.completed
`,
iss.ID, iss.Title, iss.Status, iss.Type, iss.Scope, iss.Priority,
jsonOrEmpty(iss.Domain), jsonOrEmpty(iss.Tags),
jsonOrEmpty(iss.Depends), jsonOrEmpty(iss.Blocks), jsonOrEmpty(iss.Related),
iss.Flow, string(body), iss.FilePath, iss.MtimeNs,
iss.Created, iss.Updated, boolInt(iss.Completed),
)
return err
}
func (s *Server) upsertFlowRow(fl infra.Flow) error {
body, err := readBody(fl.FilePath)
if err != nil {
return err
}
_, err = s.db.Exec(`
INSERT INTO flows (id,title,status,kind,tags_json,body,file_path,mtime_ns)
VALUES (?,?,?,?,?,?,?,?)
ON CONFLICT(id) DO UPDATE SET
title=excluded.title, status=excluded.status, kind=excluded.kind,
tags_json=excluded.tags_json, body=excluded.body, file_path=excluded.file_path, mtime_ns=excluded.mtime_ns
`,
fl.ID, fl.Title, fl.Status, fl.Kind, jsonOrEmpty(fl.Tags),
string(body), fl.FilePath, fl.MtimeNs,
)
return err
}
func readBody(path string) ([]byte, error) {
_, body, err := infra.ParseIssueMd(path)
if err != nil {
return nil, err
}
return body, nil
}
func jsonOrEmpty(xs []string) string {
if len(xs) == 0 {
return "[]"
}
b, _ := json.Marshal(xs)
return string(b)
}
func boolInt(b bool) int {
if b {
return 1
}
return 0
}
func (s *Server) deleteIssue(id string) error {
_, err := s.db.Exec(`DELETE FROM issues WHERE id=?`, id)
return err
}
func (s *Server) deleteFlow(id string) error {
_, err := s.db.Exec(`DELETE FROM flows WHERE id=?`, id)
return err
}
func issueIDFromPath(path, issuesDir string) string {
rel, err := filepath.Rel(issuesDir, path)
if err != nil {
return ""
}
base := filepath.Base(rel)
if !strings.HasSuffix(base, ".md") {
return ""
}
name := strings.TrimSuffix(base, ".md")
for i, r := range name {
if r == '-' {
return name[:i]
}
if r < '0' || r > '9' {
if i == 0 {
return ""
}
return name[:i]
}
}
return name
}
var _ = sql.ErrNoRows