Files
tutti/services/tuttid/api/routes_user_projects.go
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

81 lines
2.4 KiB
Go

package api
import (
"bytes"
"encoding/json"
"io"
"net/http"
tuttigenerated "github.com/tutti-os/tutti/services/tuttid/api/generated"
"github.com/tutti-os/tutti/services/tuttid/apierrors"
tuttitypes "github.com/tutti-os/tutti/services/tuttid/types"
)
func registerUserProjectRoutes(mux *http.ServeMux, wrapper *tuttigenerated.ServerInterfaceWrapper) {
mux.HandleFunc("/v1/user-projects", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
wrapper.ListUserProjects(w, r)
case http.MethodPost:
wrapper.UseUserProject(w, r)
case http.MethodDelete:
wrapper.DeleteUserProject(w, r)
default:
tuttitypes.WriteMethodNotAllowed(w)
}
})
mux.HandleFunc("/v1/user-projects/check", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
tuttitypes.WriteMethodNotAllowed(w)
return
}
wrapper.CheckUserProjectPath(w, r)
})
mux.HandleFunc("/v1/user-projects/move", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
tuttitypes.WriteMethodNotAllowed(w)
return
}
if !moveRequestHasBeforeProjectID(w, r) {
return
}
wrapper.MoveUserProject(w, r)
})
mux.HandleFunc("/v1/user-projects/pin", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
tuttitypes.WriteMethodNotAllowed(w)
return
}
wrapper.PinUserProject(w, r)
})
}
// The generated Go pointer cannot distinguish an omitted required-nullable
// field from an explicit JSON null. Preserve that transport distinction here;
// the strict generated decoder still owns all other shape validation.
func moveRequestHasBeforeProjectID(w http.ResponseWriter, r *http.Request) bool {
body, err := io.ReadAll(r.Body)
if err != nil {
tuttitypes.WriteJSON(w, http.StatusBadRequest, protocolErrorResponse(apierrors.MalformedRequest(
apierrors.WithDeveloperMessage("cannot read user project move request body"),
)))
return false
}
r.Body = io.NopCloser(bytes.NewReader(body))
var fields map[string]json.RawMessage
if err := json.Unmarshal(body, &fields); err != nil {
return true
}
if _, ok := fields["beforeProjectId"]; ok {
return true
}
tuttitypes.WriteJSON(w, http.StatusBadRequest, protocolErrorResponse(apierrors.MalformedRequest(
apierrors.WithDeveloperMessage("beforeProjectId is required"),
apierrors.WithParams(map[string]any{"field": "beforeProjectId"}),
)))
return false
}