chore: auto-commit (28 archivos)

- app.md
- auth.go
- chat.go
- chat.log
- db.go
- frontend/package.json
- frontend/pnpm-lock.yaml
- frontend/src/App.tsx
- frontend/src/Root.tsx
- frontend/src/api.ts
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-08 00:27:18 +02:00
parent c915e721af
commit bee688e574
28 changed files with 3601 additions and 300 deletions
+92 -15
View File
@@ -71,12 +71,14 @@ func handleUpdateColumn(db *DB) http.HandlerFunc {
Position *int `json:"position"`
Location *string `json:"location"`
Width *int `json:"width"`
WIPLimit *int `json:"wip_limit"`
IsDone *bool `json:"is_done"`
}
if err := infra.HTTPParseBody(r, &body, maxBodyBytes); err != nil {
badRequest(w, err.Error())
return
}
if err := db.UpdateColumn(id, ColumnPatch{Name: body.Name, Position: body.Position, Location: body.Location, Width: body.Width}); err != nil {
if err := db.UpdateColumn(id, ColumnPatch{Name: body.Name, Position: body.Position, Location: body.Location, Width: body.Width, WIPLimit: body.WIPLimit, IsDone: body.IsDone}); err != nil {
serverError(w, err)
return
}
@@ -116,10 +118,11 @@ func handleReorderColumns(db *DB) http.HandlerFunc {
func handleCreateCard(db *DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var body struct {
ColumnID string `json:"column_id"`
Requester string `json:"requester"`
Title string `json:"title"`
Description string `json:"description"`
ColumnID string `json:"column_id"`
Requester string `json:"requester"`
Title string `json:"title"`
Description string `json:"description"`
AssigneeID *string `json:"assignee_id"`
}
if err := infra.HTTPParseBody(r, &body, maxBodyBytes); err != nil {
badRequest(w, err.Error())
@@ -129,7 +132,14 @@ func handleCreateCard(db *DB) http.HandlerFunc {
badRequest(w, "column_id and title required")
return
}
c, err := db.CreateCard(body.ColumnID, body.Requester, body.Title, body.Description)
actor, _ := infra.UserIDFromContext(r.Context(), userCtxKey)
c, err := db.CreateCard(body.ColumnID, body.Requester, body.Title, body.Description, actor)
if err == nil && body.AssigneeID != nil && *body.AssigneeID != "" {
err = db.UpdateCardWithActor(c.ID, CardPatch{AssigneeID: body.AssigneeID, HasAssignee: true}, actor)
if err == nil {
c.AssigneeID = body.AssigneeID
}
}
if err != nil {
serverError(w, err)
return
@@ -142,17 +152,38 @@ func handleCreateCard(db *DB) http.HandlerFunc {
func handleUpdateCard(db *DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
var body struct {
Requester *string `json:"requester"`
Title *string `json:"title"`
Description *string `json:"description"`
Color *string `json:"color"`
}
if err := infra.HTTPParseBody(r, &body, maxBodyBytes); err != nil {
var raw map[string]any
if err := infra.HTTPParseBody(r, &raw, maxBodyBytes); err != nil {
badRequest(w, err.Error())
return
}
if err := db.UpdateCard(id, CardPatch{Requester: body.Requester, Title: body.Title, Description: body.Description, Color: body.Color}); err != nil {
patch := CardPatch{}
if v, ok := raw["requester"].(string); ok {
patch.Requester = &v
}
if v, ok := raw["title"].(string); ok {
patch.Title = &v
}
if v, ok := raw["description"].(string); ok {
patch.Description = &v
}
if v, ok := raw["color"].(string); ok {
patch.Color = &v
}
if v, ok := raw["locked"].(bool); ok {
patch.Locked = &v
}
if v, present := raw["assignee_id"]; present {
patch.HasAssignee = true
if v == nil {
empty := ""
patch.AssigneeID = &empty
} else if s, ok := v.(string); ok {
patch.AssigneeID = &s
}
}
actor, _ := infra.UserIDFromContext(r.Context(), userCtxKey)
if err := db.UpdateCardWithActor(id, patch, actor); err != nil {
serverError(w, err)
return
}
@@ -188,7 +219,8 @@ func handleMoveCard(db *DB) http.HandlerFunc {
badRequest(w, "column_id required")
return
}
if err := db.MoveCard(id, body.ColumnID, body.OrderedIDs); err != nil {
actor, _ := infra.UserIDFromContext(r.Context(), userCtxKey)
if err := db.MoveCard(id, body.ColumnID, body.OrderedIDs, actor); err != nil {
if strings.Contains(err.Error(), "not found") {
notFound(w, "card not found")
return
@@ -213,8 +245,49 @@ func handleCardHistory(db *DB) http.HandlerFunc {
}
}
// GET /api/trash
func handleListTrash(db *DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
cards, err := db.ListDeletedCards()
if err != nil {
serverError(w, err)
return
}
infra.HTTPJSONResponse(w, http.StatusOK, cards)
}
}
// POST /api/cards/{id}/restore
func handleRestoreCard(db *DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
if err := db.RestoreCard(id); err != nil {
serverError(w, err)
return
}
w.WriteHeader(http.StatusNoContent)
}
}
// DELETE /api/cards/{id}/purge
func handlePurgeCard(db *DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
if err := db.PurgeCard(id); err != nil {
serverError(w, err)
return
}
w.WriteHeader(http.StatusNoContent)
}
}
func apiRoutes(db *DB, chatWorkdir string, logger *ChatLogger) []infra.Route {
return []infra.Route{
{Method: "POST", Path: "/api/auth/register", Handler: handleRegister(db)},
{Method: "POST", Path: "/api/auth/login", Handler: handleLogin(db)},
{Method: "POST", Path: "/api/auth/logout", Handler: handleLogout(db)},
{Method: "GET", Path: "/api/me", Handler: handleMe(db)},
{Method: "GET", Path: "/api/users", Handler: handleListUsers(db)},
{Method: "GET", Path: "/api/board", Handler: handleGetBoard(db)},
{Method: "POST", Path: "/api/columns", Handler: handleCreateColumn(db)},
{Method: "POST", Path: "/api/columns/reorder", Handler: handleReorderColumns(db)},
@@ -225,6 +298,10 @@ func apiRoutes(db *DB, chatWorkdir string, logger *ChatLogger) []infra.Route {
{Method: "DELETE", Path: "/api/cards/{id}", Handler: handleDeleteCard(db)},
{Method: "POST", Path: "/api/cards/{id}/move", Handler: handleMoveCard(db)},
{Method: "GET", Path: "/api/cards/{id}/history", Handler: handleCardHistory(db)},
{Method: "GET", Path: "/api/trash", Handler: handleListTrash(db)},
{Method: "POST", Path: "/api/cards/{id}/restore", Handler: handleRestoreCard(db)},
{Method: "DELETE", Path: "/api/cards/{id}/purge", Handler: handlePurgeCard(db)},
{Method: "POST", Path: "/api/chat", Handler: handleChat(db, chatWorkdir, logger)},
{Method: "GET", Path: "/api/metrics", Handler: handleMetrics(db)},
}
}