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) }