memos/server/router/api/v1/connect_handler.go

81 lines
2.6 KiB
Go

package v1
import (
"net/http"
"connectrpc.com/connect"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/usememos/memos/proto/gen/api/v1/apiv1connect"
)
// ConnectServiceHandler wraps APIV1Service to implement Connect handler interfaces.
// It adapts the existing gRPC service implementations to work with Connect's
// request/response wrapper types.
//
// This wrapper pattern allows us to:
// - Reuse existing gRPC service implementations
// - Support both native gRPC and Connect protocols
// - Maintain a single source of truth for business logic.
type ConnectServiceHandler struct {
*APIV1Service
}
// NewConnectServiceHandler creates a new Connect service handler.
func NewConnectServiceHandler(svc *APIV1Service) *ConnectServiceHandler {
return &ConnectServiceHandler{APIV1Service: svc}
}
// RegisterConnectHandlers registers all Connect service handlers on the given mux.
func (s *ConnectServiceHandler) RegisterConnectHandlers(mux *http.ServeMux, opts ...connect.HandlerOption) {
// Register all service handlers
handlers := []struct {
path string
handler http.Handler
}{
wrap(apiv1connect.NewInstanceServiceHandler(s, opts...)),
wrap(apiv1connect.NewAuthServiceHandler(s, opts...)),
wrap(apiv1connect.NewUserServiceHandler(s, opts...)),
wrap(apiv1connect.NewMemoServiceHandler(s, opts...)),
wrap(apiv1connect.NewAttachmentServiceHandler(s, opts...)),
wrap(apiv1connect.NewShortcutServiceHandler(s, opts...)),
wrap(apiv1connect.NewActivityServiceHandler(s, opts...)),
wrap(apiv1connect.NewIdentityProviderServiceHandler(s, opts...)),
}
for _, h := range handlers {
mux.Handle(h.path, h.handler)
}
}
// wrap converts (path, handler) return value to a struct for cleaner iteration.
func wrap(path string, handler http.Handler) struct {
path string
handler http.Handler
} {
return struct {
path string
handler http.Handler
}{path, handler}
}
// convertGRPCError converts gRPC status errors to Connect errors.
// This preserves the error code semantics between the two protocols.
func convertGRPCError(err error) error {
if err == nil {
return nil
}
if st, ok := status.FromError(err); ok {
return connect.NewError(grpcCodeToConnectCode(st.Code()), err)
}
return connect.NewError(connect.CodeInternal, err)
}
// grpcCodeToConnectCode converts gRPC status codes to Connect error codes.
// gRPC and Connect use the same error code semantics, so this is a direct cast.
// See: https://connectrpc.com/docs/protocol/#error-codes
func grpcCodeToConnectCode(code codes.Code) connect.Code {
return connect.Code(code)
}