From eb826455b6bd099f9f0a019f20de141325938b39 Mon Sep 17 00:00:00 2001 From: boojack Date: Fri, 26 Jun 2026 09:03:24 +0800 Subject: [PATCH] chore(webhook): reveal-later signing secret flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generate webhook signing secrets server-side and let users reveal them on demand, replacing the create-dialog secret controls that surfaced internal mask state (Status / Generate & Copy / Clear / Pending) to users. - Add owner-gated GetUserWebhookSigningSecret RPC — the only path that returns the secret; list/create/update responses still omit it. - Generate the secret server-side on create (webhook.GenerateSigningSecret), so validity no longer depends on the client. - Rename UserWebhook.has_signing_secret -> signing_secret_set for parity with the existing api_key_set field. - Create dialog drops the secret section to a one-line note; the generated secret is shown once right after create and revealable from Edit later. --- internal/webhook/webhook.go | 12 + internal/webhook/webhook_test.go | 15 + proto/api/v1/user_service.proto | 23 +- .../v1/apiv1connect/user_service.connect.go | 85 +++-- proto/gen/api/v1/user_service.pb.go | 356 +++++++++++------- proto/gen/api/v1/user_service.pb.gw.go | 178 ++++++--- proto/gen/api/v1/user_service_grpc.pb.go | 94 +++-- proto/gen/openapi.yaml | 45 ++- server/router/api/v1/connect_services.go | 8 + server/router/api/v1/user_service.go | 45 ++- server/router/api/v1/user_webhook_test.go | 45 ++- web/src/components/CreateWebhookDialog.tsx | 298 ++++++++------- web/src/locales/en.json | 13 +- web/src/locales/zh-Hans.json | 13 +- web/src/types/proto/api/v1/user_service_pb.ts | 77 +++- 15 files changed, 901 insertions(+), 406 deletions(-) diff --git a/internal/webhook/webhook.go b/internal/webhook/webhook.go index 9a905651..b3df1bd4 100644 --- a/internal/webhook/webhook.go +++ b/internal/webhook/webhook.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "crypto/hmac" + "crypto/rand" "crypto/sha256" "encoding/base64" "encoding/json" @@ -104,6 +105,17 @@ func resolveSigningKey(secret string) ([]byte, error) { return []byte(secret), nil } +// GenerateSigningSecret returns a new Standard Webhooks signing secret in the +// "whsec_" form, backed by 32 cryptographically-random bytes — comfortably +// within the spec's 24–64 byte range. +func GenerateSigningSecret() (string, error) { + buf := make([]byte, 32) + if _, err := rand.Read(buf); err != nil { + return "", errors.Wrap(err, "failed to read random bytes for signing secret") + } + return "whsec_" + base64.StdEncoding.EncodeToString(buf), nil +} + // Post posts the message to webhook endpoint. func Post(requestPayload *WebhookRequestPayload) error { body, err := json.Marshal(requestPayload) diff --git a/internal/webhook/webhook_test.go b/internal/webhook/webhook_test.go index 7d50636c..2b41b3b9 100644 --- a/internal/webhook/webhook_test.go +++ b/internal/webhook/webhook_test.go @@ -41,6 +41,21 @@ func TestResolveSigningKey(t *testing.T) { }) } +func TestGenerateSigningSecret(t *testing.T) { + secret, err := GenerateSigningSecret() + require.NoError(t, err) + require.True(t, strings.HasPrefix(secret, "whsec_"), "generated secret must use the whsec_ prefix") + require.NoError(t, ValidateSigningSecret(secret), "generated secret must pass validation") + + key, err := resolveSigningKey(secret) + require.NoError(t, err) + require.Len(t, key, 32, "generated secret must decode to 32 raw bytes") + + other, err := GenerateSigningSecret() + require.NoError(t, err) + require.NotEqual(t, secret, other, "each generated secret must be unique") +} + func TestValidateSigningSecret(t *testing.T) { tests := []struct { name string diff --git a/proto/api/v1/user_service.proto b/proto/api/v1/user_service.proto index 16057e4d..a0871b6b 100644 --- a/proto/api/v1/user_service.proto +++ b/proto/api/v1/user_service.proto @@ -170,6 +170,14 @@ service UserService { option (google.api.method_signature) = "name"; } + // GetUserWebhookSigningSecret returns the signing secret for a webhook. + // The secret is returned only through this explicit, owner-gated call; it is + // never included in List/Create/Update responses. + rpc GetUserWebhookSigningSecret(GetUserWebhookSigningSecretRequest) returns (GetUserWebhookSigningSecretResponse) { + option (google.api.http) = {get: "/api/v1/{name=users/*/webhooks/*}:getSigningSecret"}; + option (google.api.method_signature) = "name"; + } + // ListUserNotifications lists notifications for a user. rpc ListUserNotifications(ListUserNotificationsRequest) returns (ListUserNotificationsResponse) { option (google.api.http) = {get: "/api/v1/{parent=users/*}/notifications"}; @@ -715,8 +723,8 @@ message UserWebhook { // This field is input-only; it is never returned in responses. string signing_secret = 6 [(google.api.field_behavior) = INPUT_ONLY]; - // Whether this webhook has a signing secret configured. - bool has_signing_secret = 7 [(google.api.field_behavior) = OUTPUT_ONLY]; + // Whether a signing secret is configured for this webhook. + bool signing_secret_set = 7 [(google.api.field_behavior) = OUTPUT_ONLY]; } message ListUserWebhooksRequest { @@ -753,6 +761,17 @@ message DeleteUserWebhookRequest { string name = 1 [(google.api.field_behavior) = REQUIRED]; } +message GetUserWebhookSigningSecretRequest { + // The name of the webhook whose signing secret to reveal. + // Format: users/{user}/webhooks/{webhook} + string name = 1 [(google.api.field_behavior) = REQUIRED]; +} + +message GetUserWebhookSigningSecretResponse { + // The signing secret, in the Standard Webhooks "whsec_" form. + string signing_secret = 1; +} + message UserNotification { option (google.api.resource) = { type: "memos.api.v1/UserNotification" diff --git a/proto/gen/api/v1/apiv1connect/user_service.connect.go b/proto/gen/api/v1/apiv1connect/user_service.connect.go index a99e3caa..120a5b7b 100644 --- a/proto/gen/api/v1/apiv1connect/user_service.connect.go +++ b/proto/gen/api/v1/apiv1connect/user_service.connect.go @@ -95,6 +95,9 @@ const ( // UserServiceDeleteUserWebhookProcedure is the fully-qualified name of the UserService's // DeleteUserWebhook RPC. UserServiceDeleteUserWebhookProcedure = "/memos.api.v1.UserService/DeleteUserWebhook" + // UserServiceGetUserWebhookSigningSecretProcedure is the fully-qualified name of the UserService's + // GetUserWebhookSigningSecret RPC. + UserServiceGetUserWebhookSigningSecretProcedure = "/memos.api.v1.UserService/GetUserWebhookSigningSecret" // UserServiceListUserNotificationsProcedure is the fully-qualified name of the UserService's // ListUserNotifications RPC. UserServiceListUserNotificationsProcedure = "/memos.api.v1.UserService/ListUserNotifications" @@ -155,6 +158,10 @@ type UserServiceClient interface { UpdateUserWebhook(context.Context, *connect.Request[v1.UpdateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error) // DeleteUserWebhook deletes a webhook for a user. DeleteUserWebhook(context.Context, *connect.Request[v1.DeleteUserWebhookRequest]) (*connect.Response[emptypb.Empty], error) + // GetUserWebhookSigningSecret returns the signing secret for a webhook. + // The secret is returned only through this explicit, owner-gated call; it is + // never included in List/Create/Update responses. + GetUserWebhookSigningSecret(context.Context, *connect.Request[v1.GetUserWebhookSigningSecretRequest]) (*connect.Response[v1.GetUserWebhookSigningSecretResponse], error) // ListUserNotifications lists notifications for a user. ListUserNotifications(context.Context, *connect.Request[v1.ListUserNotificationsRequest]) (*connect.Response[v1.ListUserNotificationsResponse], error) // UpdateUserNotification updates a notification. @@ -306,6 +313,12 @@ func NewUserServiceClient(httpClient connect.HTTPClient, baseURL string, opts .. connect.WithSchema(userServiceMethods.ByName("DeleteUserWebhook")), connect.WithClientOptions(opts...), ), + getUserWebhookSigningSecret: connect.NewClient[v1.GetUserWebhookSigningSecretRequest, v1.GetUserWebhookSigningSecretResponse]( + httpClient, + baseURL+UserServiceGetUserWebhookSigningSecretProcedure, + connect.WithSchema(userServiceMethods.ByName("GetUserWebhookSigningSecret")), + connect.WithClientOptions(opts...), + ), listUserNotifications: connect.NewClient[v1.ListUserNotificationsRequest, v1.ListUserNotificationsResponse]( httpClient, baseURL+UserServiceListUserNotificationsProcedure, @@ -329,31 +342,32 @@ func NewUserServiceClient(httpClient connect.HTTPClient, baseURL string, opts .. // userServiceClient implements UserServiceClient. type userServiceClient struct { - listUsers *connect.Client[v1.ListUsersRequest, v1.ListUsersResponse] - batchGetUsers *connect.Client[v1.BatchGetUsersRequest, v1.BatchGetUsersResponse] - getUser *connect.Client[v1.GetUserRequest, v1.User] - createUser *connect.Client[v1.CreateUserRequest, v1.User] - updateUser *connect.Client[v1.UpdateUserRequest, v1.User] - deleteUser *connect.Client[v1.DeleteUserRequest, emptypb.Empty] - listAllUserStats *connect.Client[v1.ListAllUserStatsRequest, v1.ListAllUserStatsResponse] - getUserStats *connect.Client[v1.GetUserStatsRequest, v1.UserStats] - getUserSetting *connect.Client[v1.GetUserSettingRequest, v1.UserSetting] - updateUserSetting *connect.Client[v1.UpdateUserSettingRequest, v1.UserSetting] - listUserSettings *connect.Client[v1.ListUserSettingsRequest, v1.ListUserSettingsResponse] - listLinkedIdentities *connect.Client[v1.ListLinkedIdentitiesRequest, v1.ListLinkedIdentitiesResponse] - createLinkedIdentity *connect.Client[v1.CreateLinkedIdentityRequest, v1.LinkedIdentity] - getLinkedIdentity *connect.Client[v1.GetLinkedIdentityRequest, v1.LinkedIdentity] - deleteLinkedIdentity *connect.Client[v1.DeleteLinkedIdentityRequest, emptypb.Empty] - listPersonalAccessTokens *connect.Client[v1.ListPersonalAccessTokensRequest, v1.ListPersonalAccessTokensResponse] - createPersonalAccessToken *connect.Client[v1.CreatePersonalAccessTokenRequest, v1.CreatePersonalAccessTokenResponse] - deletePersonalAccessToken *connect.Client[v1.DeletePersonalAccessTokenRequest, emptypb.Empty] - listUserWebhooks *connect.Client[v1.ListUserWebhooksRequest, v1.ListUserWebhooksResponse] - createUserWebhook *connect.Client[v1.CreateUserWebhookRequest, v1.UserWebhook] - updateUserWebhook *connect.Client[v1.UpdateUserWebhookRequest, v1.UserWebhook] - deleteUserWebhook *connect.Client[v1.DeleteUserWebhookRequest, emptypb.Empty] - listUserNotifications *connect.Client[v1.ListUserNotificationsRequest, v1.ListUserNotificationsResponse] - updateUserNotification *connect.Client[v1.UpdateUserNotificationRequest, v1.UserNotification] - deleteUserNotification *connect.Client[v1.DeleteUserNotificationRequest, emptypb.Empty] + listUsers *connect.Client[v1.ListUsersRequest, v1.ListUsersResponse] + batchGetUsers *connect.Client[v1.BatchGetUsersRequest, v1.BatchGetUsersResponse] + getUser *connect.Client[v1.GetUserRequest, v1.User] + createUser *connect.Client[v1.CreateUserRequest, v1.User] + updateUser *connect.Client[v1.UpdateUserRequest, v1.User] + deleteUser *connect.Client[v1.DeleteUserRequest, emptypb.Empty] + listAllUserStats *connect.Client[v1.ListAllUserStatsRequest, v1.ListAllUserStatsResponse] + getUserStats *connect.Client[v1.GetUserStatsRequest, v1.UserStats] + getUserSetting *connect.Client[v1.GetUserSettingRequest, v1.UserSetting] + updateUserSetting *connect.Client[v1.UpdateUserSettingRequest, v1.UserSetting] + listUserSettings *connect.Client[v1.ListUserSettingsRequest, v1.ListUserSettingsResponse] + listLinkedIdentities *connect.Client[v1.ListLinkedIdentitiesRequest, v1.ListLinkedIdentitiesResponse] + createLinkedIdentity *connect.Client[v1.CreateLinkedIdentityRequest, v1.LinkedIdentity] + getLinkedIdentity *connect.Client[v1.GetLinkedIdentityRequest, v1.LinkedIdentity] + deleteLinkedIdentity *connect.Client[v1.DeleteLinkedIdentityRequest, emptypb.Empty] + listPersonalAccessTokens *connect.Client[v1.ListPersonalAccessTokensRequest, v1.ListPersonalAccessTokensResponse] + createPersonalAccessToken *connect.Client[v1.CreatePersonalAccessTokenRequest, v1.CreatePersonalAccessTokenResponse] + deletePersonalAccessToken *connect.Client[v1.DeletePersonalAccessTokenRequest, emptypb.Empty] + listUserWebhooks *connect.Client[v1.ListUserWebhooksRequest, v1.ListUserWebhooksResponse] + createUserWebhook *connect.Client[v1.CreateUserWebhookRequest, v1.UserWebhook] + updateUserWebhook *connect.Client[v1.UpdateUserWebhookRequest, v1.UserWebhook] + deleteUserWebhook *connect.Client[v1.DeleteUserWebhookRequest, emptypb.Empty] + getUserWebhookSigningSecret *connect.Client[v1.GetUserWebhookSigningSecretRequest, v1.GetUserWebhookSigningSecretResponse] + listUserNotifications *connect.Client[v1.ListUserNotificationsRequest, v1.ListUserNotificationsResponse] + updateUserNotification *connect.Client[v1.UpdateUserNotificationRequest, v1.UserNotification] + deleteUserNotification *connect.Client[v1.DeleteUserNotificationRequest, emptypb.Empty] } // ListUsers calls memos.api.v1.UserService.ListUsers. @@ -466,6 +480,11 @@ func (c *userServiceClient) DeleteUserWebhook(ctx context.Context, req *connect. return c.deleteUserWebhook.CallUnary(ctx, req) } +// GetUserWebhookSigningSecret calls memos.api.v1.UserService.GetUserWebhookSigningSecret. +func (c *userServiceClient) GetUserWebhookSigningSecret(ctx context.Context, req *connect.Request[v1.GetUserWebhookSigningSecretRequest]) (*connect.Response[v1.GetUserWebhookSigningSecretResponse], error) { + return c.getUserWebhookSigningSecret.CallUnary(ctx, req) +} + // ListUserNotifications calls memos.api.v1.UserService.ListUserNotifications. func (c *userServiceClient) ListUserNotifications(ctx context.Context, req *connect.Request[v1.ListUserNotificationsRequest]) (*connect.Response[v1.ListUserNotificationsResponse], error) { return c.listUserNotifications.CallUnary(ctx, req) @@ -530,6 +549,10 @@ type UserServiceHandler interface { UpdateUserWebhook(context.Context, *connect.Request[v1.UpdateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error) // DeleteUserWebhook deletes a webhook for a user. DeleteUserWebhook(context.Context, *connect.Request[v1.DeleteUserWebhookRequest]) (*connect.Response[emptypb.Empty], error) + // GetUserWebhookSigningSecret returns the signing secret for a webhook. + // The secret is returned only through this explicit, owner-gated call; it is + // never included in List/Create/Update responses. + GetUserWebhookSigningSecret(context.Context, *connect.Request[v1.GetUserWebhookSigningSecretRequest]) (*connect.Response[v1.GetUserWebhookSigningSecretResponse], error) // ListUserNotifications lists notifications for a user. ListUserNotifications(context.Context, *connect.Request[v1.ListUserNotificationsRequest]) (*connect.Response[v1.ListUserNotificationsResponse], error) // UpdateUserNotification updates a notification. @@ -677,6 +700,12 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption connect.WithSchema(userServiceMethods.ByName("DeleteUserWebhook")), connect.WithHandlerOptions(opts...), ) + userServiceGetUserWebhookSigningSecretHandler := connect.NewUnaryHandler( + UserServiceGetUserWebhookSigningSecretProcedure, + svc.GetUserWebhookSigningSecret, + connect.WithSchema(userServiceMethods.ByName("GetUserWebhookSigningSecret")), + connect.WithHandlerOptions(opts...), + ) userServiceListUserNotificationsHandler := connect.NewUnaryHandler( UserServiceListUserNotificationsProcedure, svc.ListUserNotifications, @@ -741,6 +770,8 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption userServiceUpdateUserWebhookHandler.ServeHTTP(w, r) case UserServiceDeleteUserWebhookProcedure: userServiceDeleteUserWebhookHandler.ServeHTTP(w, r) + case UserServiceGetUserWebhookSigningSecretProcedure: + userServiceGetUserWebhookSigningSecretHandler.ServeHTTP(w, r) case UserServiceListUserNotificationsProcedure: userServiceListUserNotificationsHandler.ServeHTTP(w, r) case UserServiceUpdateUserNotificationProcedure: @@ -844,6 +875,10 @@ func (UnimplementedUserServiceHandler) DeleteUserWebhook(context.Context, *conne return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.DeleteUserWebhook is not implemented")) } +func (UnimplementedUserServiceHandler) GetUserWebhookSigningSecret(context.Context, *connect.Request[v1.GetUserWebhookSigningSecretRequest]) (*connect.Response[v1.GetUserWebhookSigningSecretResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.GetUserWebhookSigningSecret is not implemented")) +} + func (UnimplementedUserServiceHandler) ListUserNotifications(context.Context, *connect.Request[v1.ListUserNotificationsRequest]) (*connect.Response[v1.ListUserNotificationsResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUserNotifications is not implemented")) } diff --git a/proto/gen/api/v1/user_service.pb.go b/proto/gen/api/v1/user_service.pb.go index 65da069c..d3cfcef6 100644 --- a/proto/gen/api/v1/user_service.pb.go +++ b/proto/gen/api/v1/user_service.pb.go @@ -180,7 +180,7 @@ func (x UserNotification_Status) Number() protoreflect.EnumNumber { // Deprecated: Use UserNotification_Status.Descriptor instead. func (UserNotification_Status) EnumDescriptor() ([]byte, []int) { - return file_api_v1_user_service_proto_rawDescGZIP(), []int{36, 0} + return file_api_v1_user_service_proto_rawDescGZIP(), []int{38, 0} } type UserNotification_Type int32 @@ -229,7 +229,7 @@ func (x UserNotification_Type) Number() protoreflect.EnumNumber { // Deprecated: Use UserNotification_Type.Descriptor instead. func (UserNotification_Type) EnumDescriptor() ([]byte, []int) { - return file_api_v1_user_service_proto_rawDescGZIP(), []int{36, 1} + return file_api_v1_user_service_proto_rawDescGZIP(), []int{38, 1} } type User struct { @@ -2166,8 +2166,8 @@ type UserWebhook struct { // Optional. Signing secret used to HMAC-SHA256 sign the webhook request body. // This field is input-only; it is never returned in responses. SigningSecret string `protobuf:"bytes,6,opt,name=signing_secret,json=signingSecret,proto3" json:"signing_secret,omitempty"` - // Whether this webhook has a signing secret configured. - HasSigningSecret bool `protobuf:"varint,7,opt,name=has_signing_secret,json=hasSigningSecret,proto3" json:"has_signing_secret,omitempty"` + // Whether a signing secret is configured for this webhook. + SigningSecretSet bool `protobuf:"varint,7,opt,name=signing_secret_set,json=signingSecretSet,proto3" json:"signing_secret_set,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -2244,9 +2244,9 @@ func (x *UserWebhook) GetSigningSecret() string { return "" } -func (x *UserWebhook) GetHasSigningSecret() bool { +func (x *UserWebhook) GetSigningSecretSet() bool { if x != nil { - return x.HasSigningSecret + return x.SigningSecretSet } return false } @@ -2497,6 +2497,97 @@ func (x *DeleteUserWebhookRequest) GetName() string { return "" } +type GetUserWebhookSigningSecretRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The name of the webhook whose signing secret to reveal. + // Format: users/{user}/webhooks/{webhook} + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetUserWebhookSigningSecretRequest) Reset() { + *x = GetUserWebhookSigningSecretRequest{} + mi := &file_api_v1_user_service_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetUserWebhookSigningSecretRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUserWebhookSigningSecretRequest) ProtoMessage() {} + +func (x *GetUserWebhookSigningSecretRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_user_service_proto_msgTypes[36] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetUserWebhookSigningSecretRequest.ProtoReflect.Descriptor instead. +func (*GetUserWebhookSigningSecretRequest) Descriptor() ([]byte, []int) { + return file_api_v1_user_service_proto_rawDescGZIP(), []int{36} +} + +func (x *GetUserWebhookSigningSecretRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type GetUserWebhookSigningSecretResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The signing secret, in the Standard Webhooks "whsec_" form. + SigningSecret string `protobuf:"bytes,1,opt,name=signing_secret,json=signingSecret,proto3" json:"signing_secret,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetUserWebhookSigningSecretResponse) Reset() { + *x = GetUserWebhookSigningSecretResponse{} + mi := &file_api_v1_user_service_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetUserWebhookSigningSecretResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUserWebhookSigningSecretResponse) ProtoMessage() {} + +func (x *GetUserWebhookSigningSecretResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_user_service_proto_msgTypes[37] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetUserWebhookSigningSecretResponse.ProtoReflect.Descriptor instead. +func (*GetUserWebhookSigningSecretResponse) Descriptor() ([]byte, []int) { + return file_api_v1_user_service_proto_rawDescGZIP(), []int{37} +} + +func (x *GetUserWebhookSigningSecretResponse) GetSigningSecret() string { + if x != nil { + return x.SigningSecret + } + return "" +} + type UserNotification struct { state protoimpl.MessageState `protogen:"open.v1"` // The resource name of the notification. @@ -2524,7 +2615,7 @@ type UserNotification struct { func (x *UserNotification) Reset() { *x = UserNotification{} - mi := &file_api_v1_user_service_proto_msgTypes[36] + mi := &file_api_v1_user_service_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2536,7 +2627,7 @@ func (x *UserNotification) String() string { func (*UserNotification) ProtoMessage() {} func (x *UserNotification) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[36] + mi := &file_api_v1_user_service_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2549,7 +2640,7 @@ func (x *UserNotification) ProtoReflect() protoreflect.Message { // Deprecated: Use UserNotification.ProtoReflect.Descriptor instead. func (*UserNotification) Descriptor() ([]byte, []int) { - return file_api_v1_user_service_proto_rawDescGZIP(), []int{36} + return file_api_v1_user_service_proto_rawDescGZIP(), []int{38} } func (x *UserNotification) GetName() string { @@ -2649,7 +2740,7 @@ type ListUserNotificationsRequest struct { func (x *ListUserNotificationsRequest) Reset() { *x = ListUserNotificationsRequest{} - mi := &file_api_v1_user_service_proto_msgTypes[37] + mi := &file_api_v1_user_service_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2661,7 +2752,7 @@ func (x *ListUserNotificationsRequest) String() string { func (*ListUserNotificationsRequest) ProtoMessage() {} func (x *ListUserNotificationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[37] + mi := &file_api_v1_user_service_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2674,7 +2765,7 @@ func (x *ListUserNotificationsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListUserNotificationsRequest.ProtoReflect.Descriptor instead. func (*ListUserNotificationsRequest) Descriptor() ([]byte, []int) { - return file_api_v1_user_service_proto_rawDescGZIP(), []int{37} + return file_api_v1_user_service_proto_rawDescGZIP(), []int{39} } func (x *ListUserNotificationsRequest) GetParent() string { @@ -2715,7 +2806,7 @@ type ListUserNotificationsResponse struct { func (x *ListUserNotificationsResponse) Reset() { *x = ListUserNotificationsResponse{} - mi := &file_api_v1_user_service_proto_msgTypes[38] + mi := &file_api_v1_user_service_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2727,7 +2818,7 @@ func (x *ListUserNotificationsResponse) String() string { func (*ListUserNotificationsResponse) ProtoMessage() {} func (x *ListUserNotificationsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[38] + mi := &file_api_v1_user_service_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2740,7 +2831,7 @@ func (x *ListUserNotificationsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListUserNotificationsResponse.ProtoReflect.Descriptor instead. func (*ListUserNotificationsResponse) Descriptor() ([]byte, []int) { - return file_api_v1_user_service_proto_rawDescGZIP(), []int{38} + return file_api_v1_user_service_proto_rawDescGZIP(), []int{40} } func (x *ListUserNotificationsResponse) GetNotifications() []*UserNotification { @@ -2767,7 +2858,7 @@ type UpdateUserNotificationRequest struct { func (x *UpdateUserNotificationRequest) Reset() { *x = UpdateUserNotificationRequest{} - mi := &file_api_v1_user_service_proto_msgTypes[39] + mi := &file_api_v1_user_service_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2779,7 +2870,7 @@ func (x *UpdateUserNotificationRequest) String() string { func (*UpdateUserNotificationRequest) ProtoMessage() {} func (x *UpdateUserNotificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[39] + mi := &file_api_v1_user_service_proto_msgTypes[41] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2792,7 +2883,7 @@ func (x *UpdateUserNotificationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateUserNotificationRequest.ProtoReflect.Descriptor instead. func (*UpdateUserNotificationRequest) Descriptor() ([]byte, []int) { - return file_api_v1_user_service_proto_rawDescGZIP(), []int{39} + return file_api_v1_user_service_proto_rawDescGZIP(), []int{41} } func (x *UpdateUserNotificationRequest) GetNotification() *UserNotification { @@ -2819,7 +2910,7 @@ type DeleteUserNotificationRequest struct { func (x *DeleteUserNotificationRequest) Reset() { *x = DeleteUserNotificationRequest{} - mi := &file_api_v1_user_service_proto_msgTypes[40] + mi := &file_api_v1_user_service_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2831,7 +2922,7 @@ func (x *DeleteUserNotificationRequest) String() string { func (*DeleteUserNotificationRequest) ProtoMessage() {} func (x *DeleteUserNotificationRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[40] + mi := &file_api_v1_user_service_proto_msgTypes[42] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2844,7 +2935,7 @@ func (x *DeleteUserNotificationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteUserNotificationRequest.ProtoReflect.Descriptor instead. func (*DeleteUserNotificationRequest) Descriptor() ([]byte, []int) { - return file_api_v1_user_service_proto_rawDescGZIP(), []int{40} + return file_api_v1_user_service_proto_rawDescGZIP(), []int{42} } func (x *DeleteUserNotificationRequest) GetName() string { @@ -2867,7 +2958,7 @@ type UserStats_MemoTypeStats struct { func (x *UserStats_MemoTypeStats) Reset() { *x = UserStats_MemoTypeStats{} - mi := &file_api_v1_user_service_proto_msgTypes[42] + mi := &file_api_v1_user_service_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2879,7 +2970,7 @@ func (x *UserStats_MemoTypeStats) String() string { func (*UserStats_MemoTypeStats) ProtoMessage() {} func (x *UserStats_MemoTypeStats) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[42] + mi := &file_api_v1_user_service_proto_msgTypes[43] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2892,7 +2983,7 @@ func (x *UserStats_MemoTypeStats) ProtoReflect() protoreflect.Message { // Deprecated: Use UserStats_MemoTypeStats.ProtoReflect.Descriptor instead. func (*UserStats_MemoTypeStats) Descriptor() ([]byte, []int) { - return file_api_v1_user_service_proto_rawDescGZIP(), []int{9, 1} + return file_api_v1_user_service_proto_rawDescGZIP(), []int{9, 0} } func (x *UserStats_MemoTypeStats) GetLinkCount() int32 { @@ -2940,7 +3031,7 @@ type UserSetting_GeneralSetting struct { func (x *UserSetting_GeneralSetting) Reset() { *x = UserSetting_GeneralSetting{} - mi := &file_api_v1_user_service_proto_msgTypes[43] + mi := &file_api_v1_user_service_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2952,7 +3043,7 @@ func (x *UserSetting_GeneralSetting) String() string { func (*UserSetting_GeneralSetting) ProtoMessage() {} func (x *UserSetting_GeneralSetting) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[43] + mi := &file_api_v1_user_service_proto_msgTypes[45] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3003,7 +3094,7 @@ type UserSetting_TagMetadata struct { func (x *UserSetting_TagMetadata) Reset() { *x = UserSetting_TagMetadata{} - mi := &file_api_v1_user_service_proto_msgTypes[44] + mi := &file_api_v1_user_service_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3015,7 +3106,7 @@ func (x *UserSetting_TagMetadata) String() string { func (*UserSetting_TagMetadata) ProtoMessage() {} func (x *UserSetting_TagMetadata) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[44] + mi := &file_api_v1_user_service_proto_msgTypes[46] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3057,7 +3148,7 @@ type UserSetting_TagsSetting struct { func (x *UserSetting_TagsSetting) Reset() { *x = UserSetting_TagsSetting{} - mi := &file_api_v1_user_service_proto_msgTypes[45] + mi := &file_api_v1_user_service_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3069,7 +3160,7 @@ func (x *UserSetting_TagsSetting) String() string { func (*UserSetting_TagsSetting) ProtoMessage() {} func (x *UserSetting_TagsSetting) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[45] + mi := &file_api_v1_user_service_proto_msgTypes[47] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3103,7 +3194,7 @@ type UserSetting_WebhooksSetting struct { func (x *UserSetting_WebhooksSetting) Reset() { *x = UserSetting_WebhooksSetting{} - mi := &file_api_v1_user_service_proto_msgTypes[46] + mi := &file_api_v1_user_service_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3115,7 +3206,7 @@ func (x *UserSetting_WebhooksSetting) String() string { func (*UserSetting_WebhooksSetting) ProtoMessage() {} func (x *UserSetting_WebhooksSetting) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[46] + mi := &file_api_v1_user_service_proto_msgTypes[48] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3156,7 +3247,7 @@ type UserNotification_MemoCommentPayload struct { func (x *UserNotification_MemoCommentPayload) Reset() { *x = UserNotification_MemoCommentPayload{} - mi := &file_api_v1_user_service_proto_msgTypes[48] + mi := &file_api_v1_user_service_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3168,7 +3259,7 @@ func (x *UserNotification_MemoCommentPayload) String() string { func (*UserNotification_MemoCommentPayload) ProtoMessage() {} func (x *UserNotification_MemoCommentPayload) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[48] + mi := &file_api_v1_user_service_proto_msgTypes[50] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3181,7 +3272,7 @@ func (x *UserNotification_MemoCommentPayload) ProtoReflect() protoreflect.Messag // Deprecated: Use UserNotification_MemoCommentPayload.ProtoReflect.Descriptor instead. func (*UserNotification_MemoCommentPayload) Descriptor() ([]byte, []int) { - return file_api_v1_user_service_proto_rawDescGZIP(), []int{36, 0} + return file_api_v1_user_service_proto_rawDescGZIP(), []int{38, 0} } func (x *UserNotification_MemoCommentPayload) GetMemo() string { @@ -3230,7 +3321,7 @@ type UserNotification_MemoMentionPayload struct { func (x *UserNotification_MemoMentionPayload) Reset() { *x = UserNotification_MemoMentionPayload{} - mi := &file_api_v1_user_service_proto_msgTypes[49] + mi := &file_api_v1_user_service_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3242,7 +3333,7 @@ func (x *UserNotification_MemoMentionPayload) String() string { func (*UserNotification_MemoMentionPayload) ProtoMessage() {} func (x *UserNotification_MemoMentionPayload) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_user_service_proto_msgTypes[49] + mi := &file_api_v1_user_service_proto_msgTypes[51] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3255,7 +3346,7 @@ func (x *UserNotification_MemoMentionPayload) ProtoReflect() protoreflect.Messag // Deprecated: Use UserNotification_MemoMentionPayload.ProtoReflect.Descriptor instead. func (*UserNotification_MemoMentionPayload) Descriptor() ([]byte, []int) { - return file_api_v1_user_service_proto_rawDescGZIP(), []int{36, 1} + return file_api_v1_user_service_proto_rawDescGZIP(), []int{38, 1} } func (x *UserNotification_MemoMentionPayload) GetMemo() string { @@ -3353,10 +3444,7 @@ const file_api_v1_user_service_proto_rawDesc = "" + "\x17memo_created_timestamps\x18\a \x03(\v2\x1a.google.protobuf.TimestampR\x15memoCreatedTimestamps\x12R\n" + "\x17memo_updated_timestamps\x18\b \x03(\v2\x1a.google.protobuf.TimestampR\x15memoUpdatedTimestamps\x12!\n" + "\fpinned_memos\x18\x05 \x03(\tR\vpinnedMemos\x12(\n" + - "\x10total_memo_count\x18\x06 \x01(\x05R\x0etotalMemoCount\x1a;\n" + - "\rTagCountEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\x05R\x05value:\x028\x01\x1a\x8b\x01\n" + + "\x10total_memo_count\x18\x06 \x01(\x05R\x0etotalMemoCount\x1a\x8b\x01\n" + "\rMemoTypeStats\x12\x1d\n" + "\n" + "link_count\x18\x01 \x01(\x05R\tlinkCount\x12\x1d\n" + @@ -3365,7 +3453,10 @@ const file_api_v1_user_service_proto_rawDesc = "" + "\n" + "todo_count\x18\x03 \x01(\x05R\ttodoCount\x12\x1d\n" + "\n" + - "undo_count\x18\x04 \x01(\x05R\tundoCount:?\xeaA<\n" + + "undo_count\x18\x04 \x01(\x05R\tundoCount\x1a;\n" + + "\rTagCountEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\x05R\x05value:\x028\x01:?\xeaA<\n" + "\x16memos.api.v1/UserStats\x12\fusers/{user}*\tuserStats2\tuserStatsJ\x04\b\x02\x10\x03R\x17memo_display_timestamps\"D\n" + "\x13GetUserStatsRequest\x12-\n" + "\x04name\x18\x01 \x01(\tB\x19\xe0A\x02\xfaA\x13\n" + @@ -3486,7 +3577,7 @@ const file_api_v1_user_service_proto_rawDesc = "" + "\vupdate_time\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampB\x03\xe0A\x03R\n" + "updateTime\x12*\n" + "\x0esigning_secret\x18\x06 \x01(\tB\x03\xe0A\x04R\rsigningSecret\x121\n" + - "\x12has_signing_secret\x18\a \x01(\bB\x03\xe0A\x03R\x10hasSigningSecret\"6\n" + + "\x12signing_secret_set\x18\a \x01(\bB\x03\xe0A\x03R\x10signingSecretSet\"6\n" + "\x17ListUserWebhooksRequest\x12\x1b\n" + "\x06parent\x18\x01 \x01(\tB\x03\xe0A\x02R\x06parent\"Q\n" + "\x18ListUserWebhooksResponse\x125\n" + @@ -3499,7 +3590,11 @@ const file_api_v1_user_service_proto_rawDesc = "" + "\vupdate_mask\x18\x02 \x01(\v2\x1a.google.protobuf.FieldMaskR\n" + "updateMask\"3\n" + "\x18DeleteUserWebhookRequest\x12\x17\n" + - "\x04name\x18\x01 \x01(\tB\x03\xe0A\x02R\x04name\"\xda\b\n" + + "\x04name\x18\x01 \x01(\tB\x03\xe0A\x02R\x04name\"=\n" + + "\"GetUserWebhookSigningSecretRequest\x12\x17\n" + + "\x04name\x18\x01 \x01(\tB\x03\xe0A\x02R\x04name\"L\n" + + "#GetUserWebhookSigningSecretResponse\x12%\n" + + "\x0esigning_secret\x18\x01 \x01(\tR\rsigningSecret\"\xda\b\n" + "\x10UserNotification\x12\x1a\n" + "\x04name\x18\x01 \x01(\tB\x06\xe0A\x03\xe0A\bR\x04name\x121\n" + "\x06sender\x18\x02 \x01(\tB\x19\xe0A\x03\xfaA\x13\n" + @@ -3549,7 +3644,7 @@ const file_api_v1_user_service_proto_rawDesc = "" + "updateMask\"Z\n" + "\x1dDeleteUserNotificationRequest\x129\n" + "\x04name\x18\x01 \x01(\tB%\xe0A\x02\xfaA\x1f\n" + - "\x1dmemos.api.v1/UserNotificationR\x04name2\x82\x1d\n" + + "\x1dmemos.api.v1/UserNotificationR\x04name2\xca\x1e\n" + "\vUserService\x12c\n" + "\tListUsers\x12\x1e.memos.api.v1.ListUsersRequest\x1a\x1f.memos.api.v1.ListUsersResponse\"\x15\x82\xd3\xe4\x93\x02\x0f\x12\r/api/v1/users\x12{\n" + "\rBatchGetUsers\x12\".memos.api.v1.BatchGetUsersRequest\x1a#.memos.api.v1.BatchGetUsersResponse\"!\x82\xd3\xe4\x93\x02\x1b:\x01*\"\x16/api/v1/users:batchGet\x12b\n" + @@ -3575,7 +3670,8 @@ const file_api_v1_user_service_proto_rawDesc = "" + "\x10ListUserWebhooks\x12%.memos.api.v1.ListUserWebhooksRequest\x1a&.memos.api.v1.ListUserWebhooksResponse\"2\xdaA\x06parent\x82\xd3\xe4\x93\x02#\x12!/api/v1/{parent=users/*}/webhooks\x12\x9b\x01\n" + "\x11CreateUserWebhook\x12&.memos.api.v1.CreateUserWebhookRequest\x1a\x19.memos.api.v1.UserWebhook\"C\xdaA\x0eparent,webhook\x82\xd3\xe4\x93\x02,:\awebhook\"!/api/v1/{parent=users/*}/webhooks\x12\xa8\x01\n" + "\x11UpdateUserWebhook\x12&.memos.api.v1.UpdateUserWebhookRequest\x1a\x19.memos.api.v1.UserWebhook\"P\xdaA\x13webhook,update_mask\x82\xd3\xe4\x93\x024:\awebhook2)/api/v1/{webhook.name=users/*/webhooks/*}\x12\x85\x01\n" + - "\x11DeleteUserWebhook\x12&.memos.api.v1.DeleteUserWebhookRequest\x1a\x16.google.protobuf.Empty\"0\xdaA\x04name\x82\xd3\xe4\x93\x02#*!/api/v1/{name=users/*/webhooks/*}\x12\xa9\x01\n" + + "\x11DeleteUserWebhook\x12&.memos.api.v1.DeleteUserWebhookRequest\x1a\x16.google.protobuf.Empty\"0\xdaA\x04name\x82\xd3\xe4\x93\x02#*!/api/v1/{name=users/*/webhooks/*}\x12\xc5\x01\n" + + "\x1bGetUserWebhookSigningSecret\x120.memos.api.v1.GetUserWebhookSigningSecretRequest\x1a1.memos.api.v1.GetUserWebhookSigningSecretResponse\"A\xdaA\x04name\x82\xd3\xe4\x93\x024\x122/api/v1/{name=users/*/webhooks/*}:getSigningSecret\x12\xa9\x01\n" + "\x15ListUserNotifications\x12*.memos.api.v1.ListUserNotificationsRequest\x1a+.memos.api.v1.ListUserNotificationsResponse\"7\xdaA\x06parent\x82\xd3\xe4\x93\x02(\x12&/api/v1/{parent=users/*}/notifications\x12\xcb\x01\n" + "\x16UpdateUserNotification\x12+.memos.api.v1.UpdateUserNotificationRequest\x1a\x1e.memos.api.v1.UserNotification\"d\xdaA\x18notification,update_mask\x82\xd3\xe4\x93\x02C:\fnotification23/api/v1/{notification.name=users/*/notifications/*}\x12\x94\x01\n" + "\x16DeleteUserNotification\x12+.memos.api.v1.DeleteUserNotificationRequest\x1a\x16.google.protobuf.Empty\"5\xdaA\x04name\x82\xd3\xe4\x93\x02(*&/api/v1/{name=users/*/notifications/*}B\xa8\x01\n" + @@ -3594,7 +3690,7 @@ func file_api_v1_user_service_proto_rawDescGZIP() []byte { } var file_api_v1_user_service_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_api_v1_user_service_proto_msgTypes = make([]protoimpl.MessageInfo, 50) +var file_api_v1_user_service_proto_msgTypes = make([]protoimpl.MessageInfo, 52) var file_api_v1_user_service_proto_goTypes = []any{ (User_Role)(0), // 0: memos.api.v1.User.Role (UserSetting_Key)(0), // 1: memos.api.v1.UserSetting.Key @@ -3636,74 +3732,76 @@ var file_api_v1_user_service_proto_goTypes = []any{ (*CreateUserWebhookRequest)(nil), // 37: memos.api.v1.CreateUserWebhookRequest (*UpdateUserWebhookRequest)(nil), // 38: memos.api.v1.UpdateUserWebhookRequest (*DeleteUserWebhookRequest)(nil), // 39: memos.api.v1.DeleteUserWebhookRequest - (*UserNotification)(nil), // 40: memos.api.v1.UserNotification - (*ListUserNotificationsRequest)(nil), // 41: memos.api.v1.ListUserNotificationsRequest - (*ListUserNotificationsResponse)(nil), // 42: memos.api.v1.ListUserNotificationsResponse - (*UpdateUserNotificationRequest)(nil), // 43: memos.api.v1.UpdateUserNotificationRequest - (*DeleteUserNotificationRequest)(nil), // 44: memos.api.v1.DeleteUserNotificationRequest - nil, // 45: memos.api.v1.UserStats.TagCountEntry - (*UserStats_MemoTypeStats)(nil), // 46: memos.api.v1.UserStats.MemoTypeStats - (*UserSetting_GeneralSetting)(nil), // 47: memos.api.v1.UserSetting.GeneralSetting - (*UserSetting_TagMetadata)(nil), // 48: memos.api.v1.UserSetting.TagMetadata - (*UserSetting_TagsSetting)(nil), // 49: memos.api.v1.UserSetting.TagsSetting - (*UserSetting_WebhooksSetting)(nil), // 50: memos.api.v1.UserSetting.WebhooksSetting - nil, // 51: memos.api.v1.UserSetting.TagsSetting.TagsEntry - (*UserNotification_MemoCommentPayload)(nil), // 52: memos.api.v1.UserNotification.MemoCommentPayload - (*UserNotification_MemoMentionPayload)(nil), // 53: memos.api.v1.UserNotification.MemoMentionPayload - (State)(0), // 54: memos.api.v1.State - (*timestamppb.Timestamp)(nil), // 55: google.protobuf.Timestamp - (*fieldmaskpb.FieldMask)(nil), // 56: google.protobuf.FieldMask - (*color.Color)(nil), // 57: google.type.Color - (*emptypb.Empty)(nil), // 58: google.protobuf.Empty + (*GetUserWebhookSigningSecretRequest)(nil), // 40: memos.api.v1.GetUserWebhookSigningSecretRequest + (*GetUserWebhookSigningSecretResponse)(nil), // 41: memos.api.v1.GetUserWebhookSigningSecretResponse + (*UserNotification)(nil), // 42: memos.api.v1.UserNotification + (*ListUserNotificationsRequest)(nil), // 43: memos.api.v1.ListUserNotificationsRequest + (*ListUserNotificationsResponse)(nil), // 44: memos.api.v1.ListUserNotificationsResponse + (*UpdateUserNotificationRequest)(nil), // 45: memos.api.v1.UpdateUserNotificationRequest + (*DeleteUserNotificationRequest)(nil), // 46: memos.api.v1.DeleteUserNotificationRequest + (*UserStats_MemoTypeStats)(nil), // 47: memos.api.v1.UserStats.MemoTypeStats + nil, // 48: memos.api.v1.UserStats.TagCountEntry + (*UserSetting_GeneralSetting)(nil), // 49: memos.api.v1.UserSetting.GeneralSetting + (*UserSetting_TagMetadata)(nil), // 50: memos.api.v1.UserSetting.TagMetadata + (*UserSetting_TagsSetting)(nil), // 51: memos.api.v1.UserSetting.TagsSetting + (*UserSetting_WebhooksSetting)(nil), // 52: memos.api.v1.UserSetting.WebhooksSetting + nil, // 53: memos.api.v1.UserSetting.TagsSetting.TagsEntry + (*UserNotification_MemoCommentPayload)(nil), // 54: memos.api.v1.UserNotification.MemoCommentPayload + (*UserNotification_MemoMentionPayload)(nil), // 55: memos.api.v1.UserNotification.MemoMentionPayload + (State)(0), // 56: memos.api.v1.State + (*timestamppb.Timestamp)(nil), // 57: google.protobuf.Timestamp + (*fieldmaskpb.FieldMask)(nil), // 58: google.protobuf.FieldMask + (*color.Color)(nil), // 59: google.type.Color + (*emptypb.Empty)(nil), // 60: google.protobuf.Empty } var file_api_v1_user_service_proto_depIdxs = []int32{ 0, // 0: memos.api.v1.User.role:type_name -> memos.api.v1.User.Role - 54, // 1: memos.api.v1.User.state:type_name -> memos.api.v1.State - 55, // 2: memos.api.v1.User.create_time:type_name -> google.protobuf.Timestamp - 55, // 3: memos.api.v1.User.update_time:type_name -> google.protobuf.Timestamp + 56, // 1: memos.api.v1.User.state:type_name -> memos.api.v1.State + 57, // 2: memos.api.v1.User.create_time:type_name -> google.protobuf.Timestamp + 57, // 3: memos.api.v1.User.update_time:type_name -> google.protobuf.Timestamp 4, // 4: memos.api.v1.ListUsersResponse.users:type_name -> memos.api.v1.User 4, // 5: memos.api.v1.BatchGetUsersResponse.users:type_name -> memos.api.v1.User - 56, // 6: memos.api.v1.GetUserRequest.read_mask:type_name -> google.protobuf.FieldMask + 58, // 6: memos.api.v1.GetUserRequest.read_mask:type_name -> google.protobuf.FieldMask 4, // 7: memos.api.v1.CreateUserRequest.user:type_name -> memos.api.v1.User 4, // 8: memos.api.v1.UpdateUserRequest.user:type_name -> memos.api.v1.User - 56, // 9: memos.api.v1.UpdateUserRequest.update_mask:type_name -> google.protobuf.FieldMask - 46, // 10: memos.api.v1.UserStats.memo_type_stats:type_name -> memos.api.v1.UserStats.MemoTypeStats - 45, // 11: memos.api.v1.UserStats.tag_count:type_name -> memos.api.v1.UserStats.TagCountEntry - 55, // 12: memos.api.v1.UserStats.memo_created_timestamps:type_name -> google.protobuf.Timestamp - 55, // 13: memos.api.v1.UserStats.memo_updated_timestamps:type_name -> google.protobuf.Timestamp - 54, // 14: memos.api.v1.ListAllUserStatsRequest.state:type_name -> memos.api.v1.State + 58, // 9: memos.api.v1.UpdateUserRequest.update_mask:type_name -> google.protobuf.FieldMask + 47, // 10: memos.api.v1.UserStats.memo_type_stats:type_name -> memos.api.v1.UserStats.MemoTypeStats + 48, // 11: memos.api.v1.UserStats.tag_count:type_name -> memos.api.v1.UserStats.TagCountEntry + 57, // 12: memos.api.v1.UserStats.memo_created_timestamps:type_name -> google.protobuf.Timestamp + 57, // 13: memos.api.v1.UserStats.memo_updated_timestamps:type_name -> google.protobuf.Timestamp + 56, // 14: memos.api.v1.ListAllUserStatsRequest.state:type_name -> memos.api.v1.State 13, // 15: memos.api.v1.ListAllUserStatsResponse.stats:type_name -> memos.api.v1.UserStats - 47, // 16: memos.api.v1.UserSetting.general_setting:type_name -> memos.api.v1.UserSetting.GeneralSetting - 50, // 17: memos.api.v1.UserSetting.webhooks_setting:type_name -> memos.api.v1.UserSetting.WebhooksSetting - 49, // 18: memos.api.v1.UserSetting.tags_setting:type_name -> memos.api.v1.UserSetting.TagsSetting + 49, // 16: memos.api.v1.UserSetting.general_setting:type_name -> memos.api.v1.UserSetting.GeneralSetting + 52, // 17: memos.api.v1.UserSetting.webhooks_setting:type_name -> memos.api.v1.UserSetting.WebhooksSetting + 51, // 18: memos.api.v1.UserSetting.tags_setting:type_name -> memos.api.v1.UserSetting.TagsSetting 17, // 19: memos.api.v1.UpdateUserSettingRequest.setting:type_name -> memos.api.v1.UserSetting - 56, // 20: memos.api.v1.UpdateUserSettingRequest.update_mask:type_name -> google.protobuf.FieldMask + 58, // 20: memos.api.v1.UpdateUserSettingRequest.update_mask:type_name -> google.protobuf.FieldMask 17, // 21: memos.api.v1.ListUserSettingsResponse.settings:type_name -> memos.api.v1.UserSetting 22, // 22: memos.api.v1.ListLinkedIdentitiesResponse.linked_identities:type_name -> memos.api.v1.LinkedIdentity - 55, // 23: memos.api.v1.PersonalAccessToken.created_at:type_name -> google.protobuf.Timestamp - 55, // 24: memos.api.v1.PersonalAccessToken.expires_at:type_name -> google.protobuf.Timestamp - 55, // 25: memos.api.v1.PersonalAccessToken.last_used_at:type_name -> google.protobuf.Timestamp + 57, // 23: memos.api.v1.PersonalAccessToken.created_at:type_name -> google.protobuf.Timestamp + 57, // 24: memos.api.v1.PersonalAccessToken.expires_at:type_name -> google.protobuf.Timestamp + 57, // 25: memos.api.v1.PersonalAccessToken.last_used_at:type_name -> google.protobuf.Timestamp 28, // 26: memos.api.v1.ListPersonalAccessTokensResponse.personal_access_tokens:type_name -> memos.api.v1.PersonalAccessToken 28, // 27: memos.api.v1.CreatePersonalAccessTokenResponse.personal_access_token:type_name -> memos.api.v1.PersonalAccessToken - 55, // 28: memos.api.v1.UserWebhook.create_time:type_name -> google.protobuf.Timestamp - 55, // 29: memos.api.v1.UserWebhook.update_time:type_name -> google.protobuf.Timestamp + 57, // 28: memos.api.v1.UserWebhook.create_time:type_name -> google.protobuf.Timestamp + 57, // 29: memos.api.v1.UserWebhook.update_time:type_name -> google.protobuf.Timestamp 34, // 30: memos.api.v1.ListUserWebhooksResponse.webhooks:type_name -> memos.api.v1.UserWebhook 34, // 31: memos.api.v1.CreateUserWebhookRequest.webhook:type_name -> memos.api.v1.UserWebhook 34, // 32: memos.api.v1.UpdateUserWebhookRequest.webhook:type_name -> memos.api.v1.UserWebhook - 56, // 33: memos.api.v1.UpdateUserWebhookRequest.update_mask:type_name -> google.protobuf.FieldMask + 58, // 33: memos.api.v1.UpdateUserWebhookRequest.update_mask:type_name -> google.protobuf.FieldMask 4, // 34: memos.api.v1.UserNotification.sender_user:type_name -> memos.api.v1.User 2, // 35: memos.api.v1.UserNotification.status:type_name -> memos.api.v1.UserNotification.Status - 55, // 36: memos.api.v1.UserNotification.create_time:type_name -> google.protobuf.Timestamp + 57, // 36: memos.api.v1.UserNotification.create_time:type_name -> google.protobuf.Timestamp 3, // 37: memos.api.v1.UserNotification.type:type_name -> memos.api.v1.UserNotification.Type - 52, // 38: memos.api.v1.UserNotification.memo_comment:type_name -> memos.api.v1.UserNotification.MemoCommentPayload - 53, // 39: memos.api.v1.UserNotification.memo_mention:type_name -> memos.api.v1.UserNotification.MemoMentionPayload - 40, // 40: memos.api.v1.ListUserNotificationsResponse.notifications:type_name -> memos.api.v1.UserNotification - 40, // 41: memos.api.v1.UpdateUserNotificationRequest.notification:type_name -> memos.api.v1.UserNotification - 56, // 42: memos.api.v1.UpdateUserNotificationRequest.update_mask:type_name -> google.protobuf.FieldMask - 57, // 43: memos.api.v1.UserSetting.TagMetadata.background_color:type_name -> google.type.Color - 51, // 44: memos.api.v1.UserSetting.TagsSetting.tags:type_name -> memos.api.v1.UserSetting.TagsSetting.TagsEntry + 54, // 38: memos.api.v1.UserNotification.memo_comment:type_name -> memos.api.v1.UserNotification.MemoCommentPayload + 55, // 39: memos.api.v1.UserNotification.memo_mention:type_name -> memos.api.v1.UserNotification.MemoMentionPayload + 42, // 40: memos.api.v1.ListUserNotificationsResponse.notifications:type_name -> memos.api.v1.UserNotification + 42, // 41: memos.api.v1.UpdateUserNotificationRequest.notification:type_name -> memos.api.v1.UserNotification + 58, // 42: memos.api.v1.UpdateUserNotificationRequest.update_mask:type_name -> google.protobuf.FieldMask + 59, // 43: memos.api.v1.UserSetting.TagMetadata.background_color:type_name -> google.type.Color + 53, // 44: memos.api.v1.UserSetting.TagsSetting.tags:type_name -> memos.api.v1.UserSetting.TagsSetting.TagsEntry 34, // 45: memos.api.v1.UserSetting.WebhooksSetting.webhooks:type_name -> memos.api.v1.UserWebhook - 48, // 46: memos.api.v1.UserSetting.TagsSetting.TagsEntry.value:type_name -> memos.api.v1.UserSetting.TagMetadata + 50, // 46: memos.api.v1.UserSetting.TagsSetting.TagsEntry.value:type_name -> memos.api.v1.UserSetting.TagMetadata 5, // 47: memos.api.v1.UserService.ListUsers:input_type -> memos.api.v1.ListUsersRequest 7, // 48: memos.api.v1.UserService.BatchGetUsers:input_type -> memos.api.v1.BatchGetUsersRequest 9, // 49: memos.api.v1.UserService.GetUser:input_type -> memos.api.v1.GetUserRequest @@ -3726,36 +3824,38 @@ var file_api_v1_user_service_proto_depIdxs = []int32{ 37, // 66: memos.api.v1.UserService.CreateUserWebhook:input_type -> memos.api.v1.CreateUserWebhookRequest 38, // 67: memos.api.v1.UserService.UpdateUserWebhook:input_type -> memos.api.v1.UpdateUserWebhookRequest 39, // 68: memos.api.v1.UserService.DeleteUserWebhook:input_type -> memos.api.v1.DeleteUserWebhookRequest - 41, // 69: memos.api.v1.UserService.ListUserNotifications:input_type -> memos.api.v1.ListUserNotificationsRequest - 43, // 70: memos.api.v1.UserService.UpdateUserNotification:input_type -> memos.api.v1.UpdateUserNotificationRequest - 44, // 71: memos.api.v1.UserService.DeleteUserNotification:input_type -> memos.api.v1.DeleteUserNotificationRequest - 6, // 72: memos.api.v1.UserService.ListUsers:output_type -> memos.api.v1.ListUsersResponse - 8, // 73: memos.api.v1.UserService.BatchGetUsers:output_type -> memos.api.v1.BatchGetUsersResponse - 4, // 74: memos.api.v1.UserService.GetUser:output_type -> memos.api.v1.User - 4, // 75: memos.api.v1.UserService.CreateUser:output_type -> memos.api.v1.User - 4, // 76: memos.api.v1.UserService.UpdateUser:output_type -> memos.api.v1.User - 58, // 77: memos.api.v1.UserService.DeleteUser:output_type -> google.protobuf.Empty - 16, // 78: memos.api.v1.UserService.ListAllUserStats:output_type -> memos.api.v1.ListAllUserStatsResponse - 13, // 79: memos.api.v1.UserService.GetUserStats:output_type -> memos.api.v1.UserStats - 17, // 80: memos.api.v1.UserService.GetUserSetting:output_type -> memos.api.v1.UserSetting - 17, // 81: memos.api.v1.UserService.UpdateUserSetting:output_type -> memos.api.v1.UserSetting - 21, // 82: memos.api.v1.UserService.ListUserSettings:output_type -> memos.api.v1.ListUserSettingsResponse - 24, // 83: memos.api.v1.UserService.ListLinkedIdentities:output_type -> memos.api.v1.ListLinkedIdentitiesResponse - 22, // 84: memos.api.v1.UserService.CreateLinkedIdentity:output_type -> memos.api.v1.LinkedIdentity - 22, // 85: memos.api.v1.UserService.GetLinkedIdentity:output_type -> memos.api.v1.LinkedIdentity - 58, // 86: memos.api.v1.UserService.DeleteLinkedIdentity:output_type -> google.protobuf.Empty - 30, // 87: memos.api.v1.UserService.ListPersonalAccessTokens:output_type -> memos.api.v1.ListPersonalAccessTokensResponse - 32, // 88: memos.api.v1.UserService.CreatePersonalAccessToken:output_type -> memos.api.v1.CreatePersonalAccessTokenResponse - 58, // 89: memos.api.v1.UserService.DeletePersonalAccessToken:output_type -> google.protobuf.Empty - 36, // 90: memos.api.v1.UserService.ListUserWebhooks:output_type -> memos.api.v1.ListUserWebhooksResponse - 34, // 91: memos.api.v1.UserService.CreateUserWebhook:output_type -> memos.api.v1.UserWebhook - 34, // 92: memos.api.v1.UserService.UpdateUserWebhook:output_type -> memos.api.v1.UserWebhook - 58, // 93: memos.api.v1.UserService.DeleteUserWebhook:output_type -> google.protobuf.Empty - 42, // 94: memos.api.v1.UserService.ListUserNotifications:output_type -> memos.api.v1.ListUserNotificationsResponse - 40, // 95: memos.api.v1.UserService.UpdateUserNotification:output_type -> memos.api.v1.UserNotification - 58, // 96: memos.api.v1.UserService.DeleteUserNotification:output_type -> google.protobuf.Empty - 72, // [72:97] is the sub-list for method output_type - 47, // [47:72] is the sub-list for method input_type + 40, // 69: memos.api.v1.UserService.GetUserWebhookSigningSecret:input_type -> memos.api.v1.GetUserWebhookSigningSecretRequest + 43, // 70: memos.api.v1.UserService.ListUserNotifications:input_type -> memos.api.v1.ListUserNotificationsRequest + 45, // 71: memos.api.v1.UserService.UpdateUserNotification:input_type -> memos.api.v1.UpdateUserNotificationRequest + 46, // 72: memos.api.v1.UserService.DeleteUserNotification:input_type -> memos.api.v1.DeleteUserNotificationRequest + 6, // 73: memos.api.v1.UserService.ListUsers:output_type -> memos.api.v1.ListUsersResponse + 8, // 74: memos.api.v1.UserService.BatchGetUsers:output_type -> memos.api.v1.BatchGetUsersResponse + 4, // 75: memos.api.v1.UserService.GetUser:output_type -> memos.api.v1.User + 4, // 76: memos.api.v1.UserService.CreateUser:output_type -> memos.api.v1.User + 4, // 77: memos.api.v1.UserService.UpdateUser:output_type -> memos.api.v1.User + 60, // 78: memos.api.v1.UserService.DeleteUser:output_type -> google.protobuf.Empty + 16, // 79: memos.api.v1.UserService.ListAllUserStats:output_type -> memos.api.v1.ListAllUserStatsResponse + 13, // 80: memos.api.v1.UserService.GetUserStats:output_type -> memos.api.v1.UserStats + 17, // 81: memos.api.v1.UserService.GetUserSetting:output_type -> memos.api.v1.UserSetting + 17, // 82: memos.api.v1.UserService.UpdateUserSetting:output_type -> memos.api.v1.UserSetting + 21, // 83: memos.api.v1.UserService.ListUserSettings:output_type -> memos.api.v1.ListUserSettingsResponse + 24, // 84: memos.api.v1.UserService.ListLinkedIdentities:output_type -> memos.api.v1.ListLinkedIdentitiesResponse + 22, // 85: memos.api.v1.UserService.CreateLinkedIdentity:output_type -> memos.api.v1.LinkedIdentity + 22, // 86: memos.api.v1.UserService.GetLinkedIdentity:output_type -> memos.api.v1.LinkedIdentity + 60, // 87: memos.api.v1.UserService.DeleteLinkedIdentity:output_type -> google.protobuf.Empty + 30, // 88: memos.api.v1.UserService.ListPersonalAccessTokens:output_type -> memos.api.v1.ListPersonalAccessTokensResponse + 32, // 89: memos.api.v1.UserService.CreatePersonalAccessToken:output_type -> memos.api.v1.CreatePersonalAccessTokenResponse + 60, // 90: memos.api.v1.UserService.DeletePersonalAccessToken:output_type -> google.protobuf.Empty + 36, // 91: memos.api.v1.UserService.ListUserWebhooks:output_type -> memos.api.v1.ListUserWebhooksResponse + 34, // 92: memos.api.v1.UserService.CreateUserWebhook:output_type -> memos.api.v1.UserWebhook + 34, // 93: memos.api.v1.UserService.UpdateUserWebhook:output_type -> memos.api.v1.UserWebhook + 60, // 94: memos.api.v1.UserService.DeleteUserWebhook:output_type -> google.protobuf.Empty + 41, // 95: memos.api.v1.UserService.GetUserWebhookSigningSecret:output_type -> memos.api.v1.GetUserWebhookSigningSecretResponse + 44, // 96: memos.api.v1.UserService.ListUserNotifications:output_type -> memos.api.v1.ListUserNotificationsResponse + 42, // 97: memos.api.v1.UserService.UpdateUserNotification:output_type -> memos.api.v1.UserNotification + 60, // 98: memos.api.v1.UserService.DeleteUserNotification:output_type -> google.protobuf.Empty + 73, // [73:99] is the sub-list for method output_type + 47, // [47:73] is the sub-list for method input_type 47, // [47:47] is the sub-list for extension type_name 47, // [47:47] is the sub-list for extension extendee 0, // [0:47] is the sub-list for field type_name @@ -3772,7 +3872,7 @@ func file_api_v1_user_service_proto_init() { (*UserSetting_WebhooksSetting_)(nil), (*UserSetting_TagsSetting_)(nil), } - file_api_v1_user_service_proto_msgTypes[36].OneofWrappers = []any{ + file_api_v1_user_service_proto_msgTypes[38].OneofWrappers = []any{ (*UserNotification_MemoComment)(nil), (*UserNotification_MemoMention)(nil), } @@ -3782,7 +3882,7 @@ func file_api_v1_user_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v1_user_service_proto_rawDesc), len(file_api_v1_user_service_proto_rawDesc)), NumEnums: 4, - NumMessages: 50, + NumMessages: 52, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/gen/api/v1/user_service.pb.gw.go b/proto/gen/api/v1/user_service.pb.gw.go index d50afe14..fc00e1ec 100644 --- a/proto/gen/api/v1/user_service.pb.gw.go +++ b/proto/gen/api/v1/user_service.pb.gw.go @@ -1075,6 +1075,45 @@ func local_request_UserService_DeleteUserWebhook_0(ctx context.Context, marshale return msg, metadata, err } +func request_UserService_GetUserWebhookSigningSecret_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetUserWebhookSigningSecretRequest + metadata runtime.ServerMetadata + err error + ) + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + val, ok := pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := client.GetUserWebhookSigningSecret(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_UserService_GetUserWebhookSigningSecret_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetUserWebhookSigningSecretRequest + metadata runtime.ServerMetadata + err error + ) + val, ok := pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := server.GetUserWebhookSigningSecret(ctx, &protoReq) + return msg, metadata, err +} + var filter_UserService_ListUserNotifications_0 = &utilities.DoubleArray{Encoding: map[string]int{"parent": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} func request_UserService_ListUserNotifications_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -1694,6 +1733,26 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux } forward_UserService_DeleteUserWebhook_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodGet, pattern_UserService_GetUserWebhookSigningSecret_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/GetUserWebhookSigningSecret", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/webhooks/*}:getSigningSecret")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_UserService_GetUserWebhookSigningSecret_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_UserService_GetUserWebhookSigningSecret_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) mux.Handle(http.MethodGet, pattern_UserService_ListUserNotifications_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -2168,6 +2227,23 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux } forward_UserService_DeleteUserWebhook_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodGet, pattern_UserService_GetUserWebhookSigningSecret_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/GetUserWebhookSigningSecret", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/webhooks/*}:getSigningSecret")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_UserService_GetUserWebhookSigningSecret_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_UserService_GetUserWebhookSigningSecret_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) mux.Handle(http.MethodGet, pattern_UserService_ListUserNotifications_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -2223,57 +2299,59 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux } var ( - pattern_UserService_ListUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "")) - pattern_UserService_BatchGetUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "batchGet")) - pattern_UserService_GetUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "")) - pattern_UserService_CreateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "")) - pattern_UserService_UpdateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "user.name"}, "")) - pattern_UserService_DeleteUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "")) - pattern_UserService_ListAllUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "stats")) - pattern_UserService_GetUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "getStats")) - pattern_UserService_GetUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "name"}, "")) - pattern_UserService_UpdateUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "setting.name"}, "")) - pattern_UserService_ListUserSettings_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "settings"}, "")) - pattern_UserService_ListLinkedIdentities_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "linkedIdentities"}, "")) - pattern_UserService_CreateLinkedIdentity_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "linkedIdentities"}, "")) - pattern_UserService_GetLinkedIdentity_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "linkedIdentities", "name"}, "")) - pattern_UserService_DeleteLinkedIdentity_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "linkedIdentities", "name"}, "")) - pattern_UserService_ListPersonalAccessTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "personalAccessTokens"}, "")) - pattern_UserService_CreatePersonalAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "personalAccessTokens"}, "")) - pattern_UserService_DeletePersonalAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "personalAccessTokens", "name"}, "")) - pattern_UserService_ListUserWebhooks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, "")) - pattern_UserService_CreateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, "")) - pattern_UserService_UpdateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "webhook.name"}, "")) - pattern_UserService_DeleteUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "name"}, "")) - pattern_UserService_ListUserNotifications_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "notifications"}, "")) - pattern_UserService_UpdateUserNotification_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "notifications", "notification.name"}, "")) - pattern_UserService_DeleteUserNotification_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "notifications", "name"}, "")) + pattern_UserService_ListUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "")) + pattern_UserService_BatchGetUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "batchGet")) + pattern_UserService_GetUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "")) + pattern_UserService_CreateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "")) + pattern_UserService_UpdateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "user.name"}, "")) + pattern_UserService_DeleteUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "")) + pattern_UserService_ListAllUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "stats")) + pattern_UserService_GetUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "getStats")) + pattern_UserService_GetUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "name"}, "")) + pattern_UserService_UpdateUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "setting.name"}, "")) + pattern_UserService_ListUserSettings_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "settings"}, "")) + pattern_UserService_ListLinkedIdentities_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "linkedIdentities"}, "")) + pattern_UserService_CreateLinkedIdentity_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "linkedIdentities"}, "")) + pattern_UserService_GetLinkedIdentity_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "linkedIdentities", "name"}, "")) + pattern_UserService_DeleteLinkedIdentity_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "linkedIdentities", "name"}, "")) + pattern_UserService_ListPersonalAccessTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "personalAccessTokens"}, "")) + pattern_UserService_CreatePersonalAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "personalAccessTokens"}, "")) + pattern_UserService_DeletePersonalAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "personalAccessTokens", "name"}, "")) + pattern_UserService_ListUserWebhooks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, "")) + pattern_UserService_CreateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, "")) + pattern_UserService_UpdateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "webhook.name"}, "")) + pattern_UserService_DeleteUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "name"}, "")) + pattern_UserService_GetUserWebhookSigningSecret_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "name"}, "getSigningSecret")) + pattern_UserService_ListUserNotifications_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "notifications"}, "")) + pattern_UserService_UpdateUserNotification_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "notifications", "notification.name"}, "")) + pattern_UserService_DeleteUserNotification_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "notifications", "name"}, "")) ) var ( - forward_UserService_ListUsers_0 = runtime.ForwardResponseMessage - forward_UserService_BatchGetUsers_0 = runtime.ForwardResponseMessage - forward_UserService_GetUser_0 = runtime.ForwardResponseMessage - forward_UserService_CreateUser_0 = runtime.ForwardResponseMessage - forward_UserService_UpdateUser_0 = runtime.ForwardResponseMessage - forward_UserService_DeleteUser_0 = runtime.ForwardResponseMessage - forward_UserService_ListAllUserStats_0 = runtime.ForwardResponseMessage - forward_UserService_GetUserStats_0 = runtime.ForwardResponseMessage - forward_UserService_GetUserSetting_0 = runtime.ForwardResponseMessage - forward_UserService_UpdateUserSetting_0 = runtime.ForwardResponseMessage - forward_UserService_ListUserSettings_0 = runtime.ForwardResponseMessage - forward_UserService_ListLinkedIdentities_0 = runtime.ForwardResponseMessage - forward_UserService_CreateLinkedIdentity_0 = runtime.ForwardResponseMessage - forward_UserService_GetLinkedIdentity_0 = runtime.ForwardResponseMessage - forward_UserService_DeleteLinkedIdentity_0 = runtime.ForwardResponseMessage - forward_UserService_ListPersonalAccessTokens_0 = runtime.ForwardResponseMessage - forward_UserService_CreatePersonalAccessToken_0 = runtime.ForwardResponseMessage - forward_UserService_DeletePersonalAccessToken_0 = runtime.ForwardResponseMessage - forward_UserService_ListUserWebhooks_0 = runtime.ForwardResponseMessage - forward_UserService_CreateUserWebhook_0 = runtime.ForwardResponseMessage - forward_UserService_UpdateUserWebhook_0 = runtime.ForwardResponseMessage - forward_UserService_DeleteUserWebhook_0 = runtime.ForwardResponseMessage - forward_UserService_ListUserNotifications_0 = runtime.ForwardResponseMessage - forward_UserService_UpdateUserNotification_0 = runtime.ForwardResponseMessage - forward_UserService_DeleteUserNotification_0 = runtime.ForwardResponseMessage + forward_UserService_ListUsers_0 = runtime.ForwardResponseMessage + forward_UserService_BatchGetUsers_0 = runtime.ForwardResponseMessage + forward_UserService_GetUser_0 = runtime.ForwardResponseMessage + forward_UserService_CreateUser_0 = runtime.ForwardResponseMessage + forward_UserService_UpdateUser_0 = runtime.ForwardResponseMessage + forward_UserService_DeleteUser_0 = runtime.ForwardResponseMessage + forward_UserService_ListAllUserStats_0 = runtime.ForwardResponseMessage + forward_UserService_GetUserStats_0 = runtime.ForwardResponseMessage + forward_UserService_GetUserSetting_0 = runtime.ForwardResponseMessage + forward_UserService_UpdateUserSetting_0 = runtime.ForwardResponseMessage + forward_UserService_ListUserSettings_0 = runtime.ForwardResponseMessage + forward_UserService_ListLinkedIdentities_0 = runtime.ForwardResponseMessage + forward_UserService_CreateLinkedIdentity_0 = runtime.ForwardResponseMessage + forward_UserService_GetLinkedIdentity_0 = runtime.ForwardResponseMessage + forward_UserService_DeleteLinkedIdentity_0 = runtime.ForwardResponseMessage + forward_UserService_ListPersonalAccessTokens_0 = runtime.ForwardResponseMessage + forward_UserService_CreatePersonalAccessToken_0 = runtime.ForwardResponseMessage + forward_UserService_DeletePersonalAccessToken_0 = runtime.ForwardResponseMessage + forward_UserService_ListUserWebhooks_0 = runtime.ForwardResponseMessage + forward_UserService_CreateUserWebhook_0 = runtime.ForwardResponseMessage + forward_UserService_UpdateUserWebhook_0 = runtime.ForwardResponseMessage + forward_UserService_DeleteUserWebhook_0 = runtime.ForwardResponseMessage + forward_UserService_GetUserWebhookSigningSecret_0 = runtime.ForwardResponseMessage + forward_UserService_ListUserNotifications_0 = runtime.ForwardResponseMessage + forward_UserService_UpdateUserNotification_0 = runtime.ForwardResponseMessage + forward_UserService_DeleteUserNotification_0 = runtime.ForwardResponseMessage ) diff --git a/proto/gen/api/v1/user_service_grpc.pb.go b/proto/gen/api/v1/user_service_grpc.pb.go index 6ca98593..77c15035 100644 --- a/proto/gen/api/v1/user_service_grpc.pb.go +++ b/proto/gen/api/v1/user_service_grpc.pb.go @@ -20,31 +20,32 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - UserService_ListUsers_FullMethodName = "/memos.api.v1.UserService/ListUsers" - UserService_BatchGetUsers_FullMethodName = "/memos.api.v1.UserService/BatchGetUsers" - UserService_GetUser_FullMethodName = "/memos.api.v1.UserService/GetUser" - UserService_CreateUser_FullMethodName = "/memos.api.v1.UserService/CreateUser" - UserService_UpdateUser_FullMethodName = "/memos.api.v1.UserService/UpdateUser" - UserService_DeleteUser_FullMethodName = "/memos.api.v1.UserService/DeleteUser" - UserService_ListAllUserStats_FullMethodName = "/memos.api.v1.UserService/ListAllUserStats" - UserService_GetUserStats_FullMethodName = "/memos.api.v1.UserService/GetUserStats" - UserService_GetUserSetting_FullMethodName = "/memos.api.v1.UserService/GetUserSetting" - UserService_UpdateUserSetting_FullMethodName = "/memos.api.v1.UserService/UpdateUserSetting" - UserService_ListUserSettings_FullMethodName = "/memos.api.v1.UserService/ListUserSettings" - UserService_ListLinkedIdentities_FullMethodName = "/memos.api.v1.UserService/ListLinkedIdentities" - UserService_CreateLinkedIdentity_FullMethodName = "/memos.api.v1.UserService/CreateLinkedIdentity" - UserService_GetLinkedIdentity_FullMethodName = "/memos.api.v1.UserService/GetLinkedIdentity" - UserService_DeleteLinkedIdentity_FullMethodName = "/memos.api.v1.UserService/DeleteLinkedIdentity" - UserService_ListPersonalAccessTokens_FullMethodName = "/memos.api.v1.UserService/ListPersonalAccessTokens" - UserService_CreatePersonalAccessToken_FullMethodName = "/memos.api.v1.UserService/CreatePersonalAccessToken" - UserService_DeletePersonalAccessToken_FullMethodName = "/memos.api.v1.UserService/DeletePersonalAccessToken" - UserService_ListUserWebhooks_FullMethodName = "/memos.api.v1.UserService/ListUserWebhooks" - UserService_CreateUserWebhook_FullMethodName = "/memos.api.v1.UserService/CreateUserWebhook" - UserService_UpdateUserWebhook_FullMethodName = "/memos.api.v1.UserService/UpdateUserWebhook" - UserService_DeleteUserWebhook_FullMethodName = "/memos.api.v1.UserService/DeleteUserWebhook" - UserService_ListUserNotifications_FullMethodName = "/memos.api.v1.UserService/ListUserNotifications" - UserService_UpdateUserNotification_FullMethodName = "/memos.api.v1.UserService/UpdateUserNotification" - UserService_DeleteUserNotification_FullMethodName = "/memos.api.v1.UserService/DeleteUserNotification" + UserService_ListUsers_FullMethodName = "/memos.api.v1.UserService/ListUsers" + UserService_BatchGetUsers_FullMethodName = "/memos.api.v1.UserService/BatchGetUsers" + UserService_GetUser_FullMethodName = "/memos.api.v1.UserService/GetUser" + UserService_CreateUser_FullMethodName = "/memos.api.v1.UserService/CreateUser" + UserService_UpdateUser_FullMethodName = "/memos.api.v1.UserService/UpdateUser" + UserService_DeleteUser_FullMethodName = "/memos.api.v1.UserService/DeleteUser" + UserService_ListAllUserStats_FullMethodName = "/memos.api.v1.UserService/ListAllUserStats" + UserService_GetUserStats_FullMethodName = "/memos.api.v1.UserService/GetUserStats" + UserService_GetUserSetting_FullMethodName = "/memos.api.v1.UserService/GetUserSetting" + UserService_UpdateUserSetting_FullMethodName = "/memos.api.v1.UserService/UpdateUserSetting" + UserService_ListUserSettings_FullMethodName = "/memos.api.v1.UserService/ListUserSettings" + UserService_ListLinkedIdentities_FullMethodName = "/memos.api.v1.UserService/ListLinkedIdentities" + UserService_CreateLinkedIdentity_FullMethodName = "/memos.api.v1.UserService/CreateLinkedIdentity" + UserService_GetLinkedIdentity_FullMethodName = "/memos.api.v1.UserService/GetLinkedIdentity" + UserService_DeleteLinkedIdentity_FullMethodName = "/memos.api.v1.UserService/DeleteLinkedIdentity" + UserService_ListPersonalAccessTokens_FullMethodName = "/memos.api.v1.UserService/ListPersonalAccessTokens" + UserService_CreatePersonalAccessToken_FullMethodName = "/memos.api.v1.UserService/CreatePersonalAccessToken" + UserService_DeletePersonalAccessToken_FullMethodName = "/memos.api.v1.UserService/DeletePersonalAccessToken" + UserService_ListUserWebhooks_FullMethodName = "/memos.api.v1.UserService/ListUserWebhooks" + UserService_CreateUserWebhook_FullMethodName = "/memos.api.v1.UserService/CreateUserWebhook" + UserService_UpdateUserWebhook_FullMethodName = "/memos.api.v1.UserService/UpdateUserWebhook" + UserService_DeleteUserWebhook_FullMethodName = "/memos.api.v1.UserService/DeleteUserWebhook" + UserService_GetUserWebhookSigningSecret_FullMethodName = "/memos.api.v1.UserService/GetUserWebhookSigningSecret" + UserService_ListUserNotifications_FullMethodName = "/memos.api.v1.UserService/ListUserNotifications" + UserService_UpdateUserNotification_FullMethodName = "/memos.api.v1.UserService/UpdateUserNotification" + UserService_DeleteUserNotification_FullMethodName = "/memos.api.v1.UserService/DeleteUserNotification" ) // UserServiceClient is the client API for UserService service. @@ -98,6 +99,10 @@ type UserServiceClient interface { UpdateUserWebhook(ctx context.Context, in *UpdateUserWebhookRequest, opts ...grpc.CallOption) (*UserWebhook, error) // DeleteUserWebhook deletes a webhook for a user. DeleteUserWebhook(ctx context.Context, in *DeleteUserWebhookRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + // GetUserWebhookSigningSecret returns the signing secret for a webhook. + // The secret is returned only through this explicit, owner-gated call; it is + // never included in List/Create/Update responses. + GetUserWebhookSigningSecret(ctx context.Context, in *GetUserWebhookSigningSecretRequest, opts ...grpc.CallOption) (*GetUserWebhookSigningSecretResponse, error) // ListUserNotifications lists notifications for a user. ListUserNotifications(ctx context.Context, in *ListUserNotificationsRequest, opts ...grpc.CallOption) (*ListUserNotificationsResponse, error) // UpdateUserNotification updates a notification. @@ -334,6 +339,16 @@ func (c *userServiceClient) DeleteUserWebhook(ctx context.Context, in *DeleteUse return out, nil } +func (c *userServiceClient) GetUserWebhookSigningSecret(ctx context.Context, in *GetUserWebhookSigningSecretRequest, opts ...grpc.CallOption) (*GetUserWebhookSigningSecretResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetUserWebhookSigningSecretResponse) + err := c.cc.Invoke(ctx, UserService_GetUserWebhookSigningSecret_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *userServiceClient) ListUserNotifications(ctx context.Context, in *ListUserNotificationsRequest, opts ...grpc.CallOption) (*ListUserNotificationsResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListUserNotificationsResponse) @@ -415,6 +430,10 @@ type UserServiceServer interface { UpdateUserWebhook(context.Context, *UpdateUserWebhookRequest) (*UserWebhook, error) // DeleteUserWebhook deletes a webhook for a user. DeleteUserWebhook(context.Context, *DeleteUserWebhookRequest) (*emptypb.Empty, error) + // GetUserWebhookSigningSecret returns the signing secret for a webhook. + // The secret is returned only through this explicit, owner-gated call; it is + // never included in List/Create/Update responses. + GetUserWebhookSigningSecret(context.Context, *GetUserWebhookSigningSecretRequest) (*GetUserWebhookSigningSecretResponse, error) // ListUserNotifications lists notifications for a user. ListUserNotifications(context.Context, *ListUserNotificationsRequest) (*ListUserNotificationsResponse, error) // UpdateUserNotification updates a notification. @@ -497,6 +516,9 @@ func (UnimplementedUserServiceServer) UpdateUserWebhook(context.Context, *Update func (UnimplementedUserServiceServer) DeleteUserWebhook(context.Context, *DeleteUserWebhookRequest) (*emptypb.Empty, error) { return nil, status.Error(codes.Unimplemented, "method DeleteUserWebhook not implemented") } +func (UnimplementedUserServiceServer) GetUserWebhookSigningSecret(context.Context, *GetUserWebhookSigningSecretRequest) (*GetUserWebhookSigningSecretResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetUserWebhookSigningSecret not implemented") +} func (UnimplementedUserServiceServer) ListUserNotifications(context.Context, *ListUserNotificationsRequest) (*ListUserNotificationsResponse, error) { return nil, status.Error(codes.Unimplemented, "method ListUserNotifications not implemented") } @@ -923,6 +945,24 @@ func _UserService_DeleteUserWebhook_Handler(srv interface{}, ctx context.Context return interceptor(ctx, in, info, handler) } +func _UserService_GetUserWebhookSigningSecret_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetUserWebhookSigningSecretRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).GetUserWebhookSigningSecret(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: UserService_GetUserWebhookSigningSecret_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).GetUserWebhookSigningSecret(ctx, req.(*GetUserWebhookSigningSecretRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _UserService_ListUserNotifications_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ListUserNotificationsRequest) if err := dec(in); err != nil { @@ -1072,6 +1112,10 @@ var UserService_ServiceDesc = grpc.ServiceDesc{ MethodName: "DeleteUserWebhook", Handler: _UserService_DeleteUserWebhook_Handler, }, + { + MethodName: "GetUserWebhookSigningSecret", + Handler: _UserService_GetUserWebhookSigningSecret_Handler, + }, { MethodName: "ListUserNotifications", Handler: _UserService_ListUserNotifications_Handler, diff --git a/proto/gen/openapi.yaml b/proto/gen/openapi.yaml index 62fd3a3d..bdcbb2b0 100644 --- a/proto/gen/openapi.yaml +++ b/proto/gen/openapi.yaml @@ -2209,6 +2209,41 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' + /api/v1/users/{user}/webhooks/{webhook}:getSigningSecret: + get: + tags: + - UserService + description: |- + GetUserWebhookSigningSecret returns the signing secret for a webhook. + The secret is returned only through this explicit, owner-gated call; it is + never included in List/Create/Update responses. + operationId: UserService_GetUserWebhookSigningSecret + parameters: + - name: user + in: path + description: The user id. + required: true + schema: + type: string + - name: webhook + in: path + description: The webhook id. + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/GetUserWebhookSigningSecretResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' /api/v1/users/{user}:getStats: get: tags: @@ -2644,6 +2679,12 @@ components: allOf: - $ref: '#/components/schemas/User' description: The authenticated user's information. + GetUserWebhookSigningSecretResponse: + type: object + properties: + signingSecret: + type: string + description: The signing secret, in the Standard Webhooks "whsec_" form. GoogleProtobufAny: type: object properties: @@ -3993,10 +4034,10 @@ components: description: |- Optional. Signing secret used to HMAC-SHA256 sign the webhook request body. This field is input-only; it is never returned in responses. - hasSigningSecret: + signingSecretSet: readOnly: true type: boolean - description: Whether this webhook has a signing secret configured. + description: Whether a signing secret is configured for this webhook. description: UserWebhook represents a webhook owned by a user. tags: - name: AIService diff --git a/server/router/api/v1/connect_services.go b/server/router/api/v1/connect_services.go index 67cf32a5..0c266fa9 100644 --- a/server/router/api/v1/connect_services.go +++ b/server/router/api/v1/connect_services.go @@ -269,6 +269,14 @@ func (s *ConnectServiceHandler) DeleteUserWebhook(ctx context.Context, req *conn return connect.NewResponse(resp), nil } +func (s *ConnectServiceHandler) GetUserWebhookSigningSecret(ctx context.Context, req *connect.Request[v1pb.GetUserWebhookSigningSecretRequest]) (*connect.Response[v1pb.GetUserWebhookSigningSecretResponse], error) { + resp, err := s.APIV1Service.GetUserWebhookSigningSecret(ctx, req.Msg) + if err != nil { + return nil, convertGRPCError(err) + } + return connect.NewResponse(resp), nil +} + func (s *ConnectServiceHandler) ListUserNotifications(ctx context.Context, req *connect.Request[v1pb.ListUserNotificationsRequest]) (*connect.Response[v1pb.ListUserNotificationsResponse], error) { resp, err := s.APIV1Service.ListUserNotifications(ctx, req.Msg) if err != nil { diff --git a/server/router/api/v1/user_service.go b/server/router/api/v1/user_service.go index 90fa545c..46f6adb3 100644 --- a/server/router/api/v1/user_service.go +++ b/server/router/api/v1/user_service.go @@ -1095,7 +1095,16 @@ func (s *APIV1Service) CreateUserWebhook(ctx context.Context, request *v1pb.Crea if err := webhook.ValidateURL(strings.TrimSpace(request.Webhook.Url)); err != nil { return nil, err } - if err := webhook.ValidateSigningSecret(strings.TrimSpace(request.Webhook.SigningSecret)); err != nil { + // The signing secret is generated server-side so it always meets the Standard + // Webhooks length requirement. A client-supplied secret is still accepted (and + // validated) for backward compatibility. + signingSecret := strings.TrimSpace(request.Webhook.SigningSecret) + if signingSecret == "" { + signingSecret, err = webhook.GenerateSigningSecret() + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to generate signing secret: %v", err) + } + } else if err := webhook.ValidateSigningSecret(signingSecret); err != nil { return nil, err } @@ -1104,7 +1113,7 @@ func (s *APIV1Service) CreateUserWebhook(ctx context.Context, request *v1pb.Crea Id: webhookID, Title: request.Webhook.DisplayName, Url: strings.TrimSpace(request.Webhook.Url), - SigningSecret: strings.TrimSpace(request.Webhook.SigningSecret), + SigningSecret: signingSecret, } err = s.Store.AddUserWebhook(ctx, userID, webhook) @@ -1259,6 +1268,34 @@ func (s *APIV1Service) DeleteUserWebhook(ctx context.Context, request *v1pb.Dele return &emptypb.Empty{}, nil } +// GetUserWebhookSigningSecret reveals the signing secret for a single webhook. +// This is the only endpoint that returns the secret value; it is gated to the +// webhook owner (or an admin) and the secret is never included in list/create/update responses. +func (s *APIV1Service) GetUserWebhookSigningSecret(ctx context.Context, request *v1pb.GetUserWebhookSigningSecretRequest) (*v1pb.GetUserWebhookSigningSecretResponse, error) { + user, webhookID, err := s.resolveUserAndWebhookIDFromName(ctx, request.Name) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid webhook name: %v", err) + } + userID := user.ID + + if _, err := s.authorizeUserResourceAccess(ctx, userID, true); err != nil { + return nil, err + } + + webhooks, err := s.Store.GetUserWebhooks(ctx, userID) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get user webhooks: %v", err) + } + + for _, webhook := range webhooks { + if webhook.Id == webhookID { + return &v1pb.GetUserWebhookSigningSecretResponse{SigningSecret: webhook.SigningSecret}, nil + } + } + + return nil, status.Errorf(codes.NotFound, "webhook not found") +} + // Helper functions for webhook operations // generateUserWebhookID generates a unique ID for user webhooks. @@ -1274,7 +1311,7 @@ func convertUserWebhookFromUserSetting(webhook *storepb.WebhooksUserSetting_Webh Name: fmt.Sprintf("%s/webhooks/%s", BuildUserName(user.Username), webhook.Id), Url: webhook.Url, DisplayName: webhook.Title, - HasSigningSecret: webhook.SigningSecret != "", + SigningSecretSet: webhook.SigningSecret != "", // Note: create_time and update_time are not available in the user setting webhook structure // This is a limitation of storing webhooks in user settings vs the dedicated webhook table } @@ -1474,7 +1511,7 @@ func convertUserSettingFromStore(storeSetting *storepb.UserSetting, user *store. Name: fmt.Sprintf("%s/webhooks/%s", BuildUserName(user.Username), webhook.Id), Url: webhook.Url, DisplayName: webhook.Title, - HasSigningSecret: webhook.SigningSecret != "", + SigningSecretSet: webhook.SigningSecret != "", } apiWebhooks = append(apiWebhooks, apiWebhook) } diff --git a/server/router/api/v1/user_webhook_test.go b/server/router/api/v1/user_webhook_test.go index 8fa381ce..0f59116b 100644 --- a/server/router/api/v1/user_webhook_test.go +++ b/server/router/api/v1/user_webhook_test.go @@ -1,10 +1,15 @@ package v1 import ( + "context" + "strings" "testing" "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + v1pb "github.com/usememos/memos/proto/gen/api/v1" storepb "github.com/usememos/memos/proto/gen/store" "github.com/usememos/memos/store" ) @@ -26,5 +31,43 @@ func TestConvertUserWebhookFromUserSettingOmitsSigningSecret(t *testing.T) { require.Equal(t, "My Webhook", apiWebhook.DisplayName) require.Equal(t, "https://example.com/postreceive", apiWebhook.Url) require.Empty(t, apiWebhook.SigningSecret, "signing secret must never be returned in API responses") - require.True(t, apiWebhook.HasSigningSecret, "has_signing_secret must be true when secret is configured") + require.True(t, apiWebhook.SigningSecretSet, "signing_secret_set must be true when secret is configured") +} + +// TestUserWebhookSigningSecretLifecycle covers the reveal-later flow: a webhook +// created without a secret is auto-assigned one server-side (exposed only via the +// signing_secret_set flag), the owner can reveal the value, and a non-owner cannot. +func TestUserWebhookSigningSecretLifecycle(t *testing.T) { + svc := newIntegrationService(t) + ctx := context.Background() + + // First user initializes the store as host/admin; the test actors are regular users. + _, err := svc.Store.CreateUser(ctx, &store.User{Username: "host", Role: store.RoleAdmin, Email: "host@example.com"}) + require.NoError(t, err) + owner, err := svc.Store.CreateUser(ctx, &store.User{Username: "owner", Role: store.RoleUser, Email: "owner@example.com"}) + require.NoError(t, err) + other, err := svc.Store.CreateUser(ctx, &store.User{Username: "other", Role: store.RoleUser, Email: "other@example.com"}) + require.NoError(t, err) + + ownerCtx := userCtx(ctx, owner.ID) + otherCtx := userCtx(ctx, other.ID) + + // Create without supplying a secret -> the server generates one. + created, err := svc.CreateUserWebhook(ownerCtx, &v1pb.CreateUserWebhookRequest{ + Parent: "users/owner", + Webhook: &v1pb.UserWebhook{DisplayName: "deploy", Url: "https://example.com/postreceive"}, + }) + require.NoError(t, err) + require.True(t, created.SigningSecretSet, "create must auto-generate a signing secret") + require.Empty(t, created.SigningSecret, "create response must never carry the secret value") + + // The owner can reveal the generated value. + revealed, err := svc.GetUserWebhookSigningSecret(ownerCtx, &v1pb.GetUserWebhookSigningSecretRequest{Name: created.Name}) + require.NoError(t, err) + require.True(t, strings.HasPrefix(revealed.SigningSecret, "whsec_"), "revealed secret must be in whsec_ form") + + // A non-owner, non-admin user cannot reveal it. + _, err = svc.GetUserWebhookSigningSecret(otherCtx, &v1pb.GetUserWebhookSigningSecretRequest{Name: created.Name}) + require.Error(t, err) + require.Equal(t, codes.PermissionDenied, status.Code(err), "non-owner must be denied") } diff --git a/web/src/components/CreateWebhookDialog.tsx b/web/src/components/CreateWebhookDialog.tsx index 854f3e4f..b95b73ec 100644 --- a/web/src/components/CreateWebhookDialog.tsx +++ b/web/src/components/CreateWebhookDialog.tsx @@ -1,7 +1,7 @@ import { create } from "@bufbuild/protobuf"; import { FieldMaskSchema } from "@bufbuild/protobuf/wkt"; import copy from "copy-to-clipboard"; -import { CheckIcon, CopyIcon, Trash2Icon } from "lucide-react"; +import { CheckIcon, CopyIcon, EyeIcon } from "lucide-react"; import React, { useEffect, useState } from "react"; import { toast } from "react-hot-toast"; import { Button } from "@/components/ui/button"; @@ -24,20 +24,18 @@ interface Props { interface State { displayName: string; url: string; - signingSecret: string | undefined; } function CreateWebhookDialog({ open, onOpenChange, webhookName, onSuccess }: Props) { const t = useTranslate(); const currentUser = useCurrentUser(); const isCreating = webhookName === undefined; - const [state, setState] = useState({ - displayName: "", - url: "", - signingSecret: isCreating ? "" : undefined, - }); + const [state, setState] = useState({ displayName: "", url: "" }); const requestState = useLoading(false); + const secretState = useLoading(false); const [hasExistingSecret, setHasExistingSecret] = useState(false); + const [revealedSecret, setRevealedSecret] = useState(undefined); + const [createdSecret, setCreatedSecret] = useState(undefined); const [secretCopied, setSecretCopied] = useState(false); useEffect(() => { @@ -49,12 +47,8 @@ function CreateWebhookDialog({ open, onOpenChange, webhookName, onSuccess }: Pro .then((response) => { const webhook = response.webhooks.find((w) => w.name === webhookName); if (webhook) { - setState({ - displayName: webhook.displayName, - url: webhook.url, - signingSecret: undefined, - }); - setHasExistingSecret(webhook.hasSigningSecret); + setState({ displayName: webhook.displayName, url: webhook.url }); + setHasExistingSecret(webhook.signingSecretSet); } }); } @@ -62,62 +56,67 @@ function CreateWebhookDialog({ open, onOpenChange, webhookName, onSuccess }: Pro useEffect(() => { if (open && isCreating) { - setState({ - displayName: "", - url: "", - signingSecret: "", - }); + setState({ displayName: "", url: "" }); setHasExistingSecret(false); + setRevealedSecret(undefined); + setCreatedSecret(undefined); setSecretCopied(false); } }, [open, isCreating]); - const setPartialState = (partialState: Partial) => { - setState({ - ...state, - ...partialState, - }); - }; - const handleTitleInputChange = (e: React.ChangeEvent) => { - setPartialState({ - displayName: e.target.value, - }); + setState((prev) => ({ ...prev, displayName: e.target.value })); }; const handleUrlInputChange = (e: React.ChangeEvent) => { - setPartialState({ - url: e.target.value, - }); + setState((prev) => ({ ...prev, url: e.target.value })); }; - const handleGenerateAndCopy = () => { - const bytes = crypto.getRandomValues(new Uint8Array(32)); - const secret = "whsec_" + btoa(String.fromCharCode(...bytes)); - setPartialState({ signingSecret: secret }); + const handleCopySecret = (secret: string) => { copy(secret); setSecretCopied(true); setTimeout(() => setSecretCopied(false), 2000); }; - const handleClearSecret = () => { - setPartialState({ signingSecret: "" }); + const handleRevealSecret = async () => { + if (!webhookName) return; + try { + secretState.setLoading(); + const { signingSecret } = await userServiceClient.getUserWebhookSigningSecret({ name: webhookName }); + setRevealedSecret(signingSecret); + secretState.setFinish(); + } catch (error: unknown) { + handleError(error, toast.error, { context: "Reveal signing secret", onError: () => secretState.setError() }); + } }; - const normalizedSigningSecret = state.signingSecret?.trim() ?? ""; + // Lets a pre-existing webhook that has no secret adopt one. The secret is + // persisted immediately and revealed so the user can copy it once. + const handleGenerateSecret = async () => { + if (!webhookName) return; + const bytes = crypto.getRandomValues(new Uint8Array(32)); + const secret = "whsec_" + btoa(String.fromCharCode(...bytes)); + try { + secretState.setLoading(); + await userServiceClient.updateUserWebhook({ + webhook: { name: webhookName, signingSecret: secret }, + updateMask: create(FieldMaskSchema, { paths: ["signing_secret"] }), + }); + setHasExistingSecret(true); + setRevealedSecret(secret); + handleCopySecret(secret); + secretState.setFinish(); + } catch (error: unknown) { + handleError(error, toast.error, { context: "Generate signing secret", onError: () => secretState.setError() }); + } + }; - const getPendingLabel = () => { - const prefix = `${t("setting.webhook.create-dialog.signing-secret-pending")}: `; - if (state.signingSecret === undefined) { - return prefix + t("setting.webhook.create-dialog.signing-secret-pending-no-changes"); + // When closing after a successful create, refresh the parent list regardless of how the dialog is dismissed. + const handleOpenChange = (next: boolean) => { + if (!next && createdSecret !== undefined) { + onSuccess?.(); } - if (state.signingSecret === "") { - if (isCreating) { - return prefix + t("setting.webhook.create-dialog.signing-secret-pending-no-changes"); - } - return prefix + t("setting.webhook.create-dialog.signing-secret-pending-cleared"); - } - return prefix + t("setting.webhook.create-dialog.signing-secret-pending-generated"); + onOpenChange(next); }; const handleSaveBtnClick = async () => { @@ -134,30 +133,32 @@ function CreateWebhookDialog({ open, onOpenChange, webhookName, onSuccess }: Pro try { requestState.setLoading(); if (isCreating) { - await userServiceClient.createUserWebhook({ + // The signing secret is generated server-side; reveal it once so the user can copy it without reopening. + const created = await userServiceClient.createUserWebhook({ parent: currentUser.name, - webhook: { - displayName: state.displayName, - url: state.url, - signingSecret: normalizedSigningSecret, - }, + webhook: { displayName: state.displayName, url: state.url }, }); - } else { - const updateMaskPaths = ["display_name", "url"]; - if (state.signingSecret !== undefined) { - updateMaskPaths.push("signing_secret"); + let secret: string | undefined; + try { + const response = await userServiceClient.getUserWebhookSigningSecret({ name: created.name }); + secret = response.signingSecret; + } catch { + // Reveal failed — the secret is still set and can be revealed later from the edit dialog. } - await userServiceClient.updateUserWebhook({ - webhook: { - name: webhookName, - displayName: state.displayName, - url: state.url, - ...(state.signingSecret !== undefined && { signingSecret: normalizedSigningSecret }), - }, - updateMask: create(FieldMaskSchema, { paths: updateMaskPaths }), - }); + requestState.setFinish(); + if (secret !== undefined) { + setCreatedSecret(secret); + return; + } + onSuccess?.(); + onOpenChange(false); + return; } + await userServiceClient.updateUserWebhook({ + webhook: { name: webhookName, displayName: state.displayName, url: state.url }, + updateMask: create(FieldMaskSchema, { paths: ["display_name", "url"] }), + }); onSuccess?.(); onOpenChange(false); requestState.setFinish(); @@ -169,91 +170,108 @@ function CreateWebhookDialog({ open, onOpenChange, webhookName, onSuccess }: Pro } }; + const renderSecretField = (secret: string) => ( +
+ + +
+ ); + + const renderSigningSecret = () => { + if (isCreating) { + return {t("setting.webhook.create-dialog.signing-secret-auto-note")}; + } + if (revealedSecret !== undefined) { + return renderSecretField(revealedSecret); + } + if (hasExistingSecret) { + return ( +
+ {t("setting.webhook.create-dialog.signing-secret-configured")} + +
+ ); + } + return ( +
+ {t("setting.webhook.create-dialog.signing-secret-not-configured")} + +
+ ); + }; + return ( - + {isCreating ? t("setting.webhook.create-dialog.create-webhook") : t("setting.webhook.create-dialog.edit-webhook")} -
+ {createdSecret !== undefined ? (
- - + + {t("setting.webhook.create-dialog.signing-secret-created-note")} + {renderSecretField(createdSecret)}
-
- - -
-
-
+ ) : ( +
+
+ + +
+
+ + +
+
- - {t("setting.webhook.create-dialog.signing-secret-status")}:{" "} - {hasExistingSecret - ? t("setting.webhook.create-dialog.signing-secret-configured") - : t("setting.webhook.create-dialog.signing-secret-not-configured")} - -
-
- - - {getPendingLabel()} + {renderSigningSecret()}
-
+ )} - - + {createdSecret !== undefined ? ( + + ) : ( + <> + + + + )}
diff --git a/web/src/locales/en.json b/web/src/locales/en.json index 4f027016..7e454f8a 100644 --- a/web/src/locales/en.json +++ b/web/src/locales/en.json @@ -759,21 +759,18 @@ "webhook": { "create-dialog": { "an-easy-to-remember-name": "An easy-to-remember name", - "clear-secret": "Clear", - "copied": "Copied", + "copy-secret": "Copy signing secret", "create-webhook": "Create webhook", "create-webhook-success": "Webhook `{{name}}` created", "edit-webhook": "Edit webhook", - "generate-and-copy-secret": "Generate & Copy", + "generate-secret": "Generate", "payload-url": "Payload URL", + "reveal-secret": "Reveal", "signing-secret": "Signing Secret", + "signing-secret-auto-note": "A signing secret is generated automatically. You can reveal it after creating.", "signing-secret-configured": "Configured", + "signing-secret-created-note": "Webhook created. Copy the signing secret now — you can also reveal it later from Edit.", "signing-secret-not-configured": "Not configured", - "signing-secret-pending": "Pending", - "signing-secret-pending-cleared": "cleared", - "signing-secret-pending-generated": "new secret", - "signing-secret-pending-no-changes": "no changes", - "signing-secret-status": "Status", "title": "Title", "url-example-post-receive": "https://example.com/postreceive" }, diff --git a/web/src/locales/zh-Hans.json b/web/src/locales/zh-Hans.json index 77e05d20..80ab1fb1 100644 --- a/web/src/locales/zh-Hans.json +++ b/web/src/locales/zh-Hans.json @@ -669,21 +669,18 @@ "webhook": { "create-dialog": { "an-easy-to-remember-name": "请输入一个容易记住的标题", - "clear-secret": "置空", - "copied": "已复制", + "copy-secret": "复制签名密钥", "create-webhook": "创建 Webhook", "create-webhook-success": "Webhook `{{name}}` 已创建", "edit-webhook": "编辑 Webhook", - "generate-and-copy-secret": "生成并复制", + "generate-secret": "生成", "payload-url": "请输入有效的 URL", + "reveal-secret": "显示", "signing-secret": "签名密钥", + "signing-secret-auto-note": "签名密钥将自动生成,创建后可在此查看。", "signing-secret-configured": "已配置", + "signing-secret-created-note": "Webhook 已创建。请立即复制签名密钥,之后也可在编辑中查看。", "signing-secret-not-configured": "未配置", - "signing-secret-pending": "待保存", - "signing-secret-pending-cleared": "置空", - "signing-secret-pending-generated": "新生成", - "signing-secret-pending-no-changes": "密钥无改动", - "signing-secret-status": "状态", "title": "标题", "url-example-post-receive": "https://example.com/postreceive" }, diff --git a/web/src/types/proto/api/v1/user_service_pb.ts b/web/src/types/proto/api/v1/user_service_pb.ts index 5e87db69..4a385aed 100644 --- a/web/src/types/proto/api/v1/user_service_pb.ts +++ b/web/src/types/proto/api/v1/user_service_pb.ts @@ -20,7 +20,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file api/v1/user_service.proto. */ export const file_api_v1_user_service: GenFile = /*@__PURE__*/ - fileDesc("ChlhcGkvdjEvdXNlcl9zZXJ2aWNlLnByb3RvEgxtZW1vcy5hcGkudjEi1gMKBFVzZXISEQoEbmFtZRgBIAEoCUID4EEIEioKBHJvbGUYAiABKA4yFy5tZW1vcy5hcGkudjEuVXNlci5Sb2xlQgPgQQISFQoIdXNlcm5hbWUYAyABKAlCA+BBAhISCgVlbWFpbBgEIAEoCUID4EEBEhkKDGRpc3BsYXlfbmFtZRgFIAEoCUID4EEBEhcKCmF2YXRhcl91cmwYBiABKAlCA+BBARIYCgtkZXNjcmlwdGlvbhgHIAEoCUID4EEBEhUKCHBhc3N3b3JkGAggASgJQgPgQQQSJwoFc3RhdGUYCSABKA4yEy5tZW1vcy5hcGkudjEuU3RhdGVCA+BBAhI0CgtjcmVhdGVfdGltZRgKIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAxI0Cgt1cGRhdGVfdGltZRgLIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAyIxCgRSb2xlEhQKEFJPTEVfVU5TUEVDSUZJRUQQABIJCgVBRE1JThACEggKBFVTRVIQAzo36kE0ChFtZW1vcy5hcGkudjEvVXNlchIMdXNlcnMve3VzZXJ9GgRuYW1lKgV1c2VyczIEdXNlciJzChBMaXN0VXNlcnNSZXF1ZXN0EhYKCXBhZ2Vfc2l6ZRgBIAEoBUID4EEBEhcKCnBhZ2VfdG9rZW4YAiABKAlCA+BBARITCgZmaWx0ZXIYAyABKAlCA+BBARIZCgxzaG93X2RlbGV0ZWQYBCABKAhCA+BBASJjChFMaXN0VXNlcnNSZXNwb25zZRIhCgV1c2VycxgBIAMoCzISLm1lbW9zLmFwaS52MS5Vc2VyEhcKD25leHRfcGFnZV90b2tlbhgCIAEoCRISCgp0b3RhbF9zaXplGAMgASgFIikKFEJhdGNoR2V0VXNlcnNSZXF1ZXN0EhEKCXVzZXJuYW1lcxgBIAMoCSI6ChVCYXRjaEdldFVzZXJzUmVzcG9uc2USIQoFdXNlcnMYASADKAsyEi5tZW1vcy5hcGkudjEuVXNlciJtCg5HZXRVc2VyUmVxdWVzdBInCgRuYW1lGAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyEjIKCXJlYWRfbWFzaxgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE1hc2tCA+BBASKIAQoRQ3JlYXRlVXNlclJlcXVlc3QSKAoEdXNlchgBIAEoCzISLm1lbW9zLmFwaS52MS5Vc2VyQgbgQQLgQQQSFAoHdXNlcl9pZBgCIAEoCUID4EEBEhoKDXZhbGlkYXRlX29ubHkYAyABKAhCA+BBARIXCgpyZXF1ZXN0X2lkGAQgASgJQgPgQQEijAEKEVVwZGF0ZVVzZXJSZXF1ZXN0EiUKBHVzZXIYASABKAsyEi5tZW1vcy5hcGkudjEuVXNlckID4EECEjQKC3VwZGF0ZV9tYXNrGAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLkZpZWxkTWFza0ID4EECEhoKDWFsbG93X21pc3NpbmcYAyABKAhCA+BBASJQChFEZWxldGVVc2VyUmVxdWVzdBInCgRuYW1lGAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyEhIKBWZvcmNlGAIgASgIQgPgQQEitAQKCVVzZXJTdGF0cxIRCgRuYW1lGAEgASgJQgPgQQgSPgoPbWVtb190eXBlX3N0YXRzGAMgASgLMiUubWVtb3MuYXBpLnYxLlVzZXJTdGF0cy5NZW1vVHlwZVN0YXRzEjgKCXRhZ19jb3VudBgEIAMoCzIlLm1lbW9zLmFwaS52MS5Vc2VyU3RhdHMuVGFnQ291bnRFbnRyeRI7ChdtZW1vX2NyZWF0ZWRfdGltZXN0YW1wcxgHIAMoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASOwoXbWVtb191cGRhdGVkX3RpbWVzdGFtcHMYCCADKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEhQKDHBpbm5lZF9tZW1vcxgFIAMoCRIYChB0b3RhbF9tZW1vX2NvdW50GAYgASgFGi8KDVRhZ0NvdW50RW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgFOgI4ARpfCg1NZW1vVHlwZVN0YXRzEhIKCmxpbmtfY291bnQYASABKAUSEgoKY29kZV9jb3VudBgCIAEoBRISCgp0b2RvX2NvdW50GAMgASgFEhIKCnVuZG9fY291bnQYBCABKAU6P+pBPAoWbWVtb3MuYXBpLnYxL1VzZXJTdGF0cxIMdXNlcnMve3VzZXJ9Kgl1c2VyU3RhdHMyCXVzZXJTdGF0c0oECAIQA1IXbWVtb19kaXNwbGF5X3RpbWVzdGFtcHMiPgoTR2V0VXNlclN0YXRzUmVxdWVzdBInCgRuYW1lGAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyIlcKF0xpc3RBbGxVc2VyU3RhdHNSZXF1ZXN0EicKBXN0YXRlGAEgASgOMhMubWVtb3MuYXBpLnYxLlN0YXRlQgPgQQESEwoGZmlsdGVyGAIgASgJQgPgQQEiQgoYTGlzdEFsbFVzZXJTdGF0c1Jlc3BvbnNlEiYKBXN0YXRzGAEgAygLMhcubWVtb3MuYXBpLnYxLlVzZXJTdGF0cyKyBgoLVXNlclNldHRpbmcSEQoEbmFtZRgBIAEoCUID4EEIEkMKD2dlbmVyYWxfc2V0dGluZxgCIAEoCzIoLm1lbW9zLmFwaS52MS5Vc2VyU2V0dGluZy5HZW5lcmFsU2V0dGluZ0gAEkUKEHdlYmhvb2tzX3NldHRpbmcYBSABKAsyKS5tZW1vcy5hcGkudjEuVXNlclNldHRpbmcuV2ViaG9va3NTZXR0aW5nSAASPQoMdGFnc19zZXR0aW5nGAYgASgLMiUubWVtb3MuYXBpLnYxLlVzZXJTZXR0aW5nLlRhZ3NTZXR0aW5nSAAaVwoOR2VuZXJhbFNldHRpbmcSEwoGbG9jYWxlGAEgASgJQgPgQQESHAoPbWVtb192aXNpYmlsaXR5GAMgASgJQgPgQQESEgoFdGhlbWUYBCABKAlCA+BBARpbCgtUYWdNZXRhZGF0YRIxChBiYWNrZ3JvdW5kX2NvbG9yGAEgASgLMhIuZ29vZ2xlLnR5cGUuQ29sb3JCA+BBARIZCgxibHVyX2NvbnRlbnQYAiABKAhCA+BBARqlAQoLVGFnc1NldHRpbmcSQgoEdGFncxgBIAMoCzIvLm1lbW9zLmFwaS52MS5Vc2VyU2V0dGluZy5UYWdzU2V0dGluZy5UYWdzRW50cnlCA+BBARpSCglUYWdzRW50cnkSCwoDa2V5GAEgASgJEjQKBXZhbHVlGAIgASgLMiUubWVtb3MuYXBpLnYxLlVzZXJTZXR0aW5nLlRhZ01ldGFkYXRhOgI4ARo+Cg9XZWJob29rc1NldHRpbmcSKwoId2ViaG9va3MYASADKAsyGS5tZW1vcy5hcGkudjEuVXNlcldlYmhvb2siPwoDS2V5EhMKD0tFWV9VTlNQRUNJRklFRBAAEgsKB0dFTkVSQUwQARIMCghXRUJIT09LUxAEEggKBFRBR1MQBTpd6kFaChhtZW1vcy5hcGkudjEvVXNlclNldHRpbmcSI3VzZXJzL3t1c2VybmFtZX0vc2V0dGluZ3Mve3NldHRpbmd9Kgx1c2VyU2V0dGluZ3MyC3VzZXJTZXR0aW5nQgcKBXZhbHVlIkcKFUdldFVzZXJTZXR0aW5nUmVxdWVzdBIuCgRuYW1lGAEgASgJQiDgQQL6QRoKGG1lbW9zLmFwaS52MS9Vc2VyU2V0dGluZyKBAQoYVXBkYXRlVXNlclNldHRpbmdSZXF1ZXN0Ei8KB3NldHRpbmcYASABKAsyGS5tZW1vcy5hcGkudjEuVXNlclNldHRpbmdCA+BBAhI0Cgt1cGRhdGVfbWFzaxgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE1hc2tCA+BBAiJ1ChdMaXN0VXNlclNldHRpbmdzUmVxdWVzdBIpCgZwYXJlbnQYASABKAlCGeBBAvpBEwoRbWVtb3MuYXBpLnYxL1VzZXISFgoJcGFnZV9zaXplGAIgASgFQgPgQQESFwoKcGFnZV90b2tlbhgDIAEoCUID4EEBInQKGExpc3RVc2VyU2V0dGluZ3NSZXNwb25zZRIrCghzZXR0aW5ncxgBIAMoCzIZLm1lbW9zLmFwaS52MS5Vc2VyU2V0dGluZxIXCg9uZXh0X3BhZ2VfdG9rZW4YAiABKAkSEgoKdG90YWxfc2l6ZRgDIAEoBSLqAQoOTGlua2VkSWRlbnRpdHkSEQoEbmFtZRgBIAEoCUID4EEIEjcKCGlkcF9uYW1lGAIgASgJQiXgQQP6QR8KHW1lbW9zLmFwaS52MS9JZGVudGl0eVByb3ZpZGVyEhcKCmV4dGVybl91aWQYAyABKAlCA+BBAzpz6kFwChttZW1vcy5hcGkudjEvTGlua2VkSWRlbnRpdHkSL3VzZXJzL3t1c2VyfS9saW5rZWRJZGVudGl0aWVzL3tsaW5rZWRfaWRlbnRpdHl9KhBsaW5rZWRJZGVudGl0aWVzMg5saW5rZWRJZGVudGl0eSJIChtMaXN0TGlua2VkSWRlbnRpdGllc1JlcXVlc3QSKQoGcGFyZW50GAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyIlcKHExpc3RMaW5rZWRJZGVudGl0aWVzUmVzcG9uc2USNwoRbGlua2VkX2lkZW50aXRpZXMYASADKAsyHC5tZW1vcy5hcGkudjEuTGlua2VkSWRlbnRpdHkiywEKG0NyZWF0ZUxpbmtlZElkZW50aXR5UmVxdWVzdBIpCgZwYXJlbnQYASABKAlCGeBBAvpBEwoRbWVtb3MuYXBpLnYxL1VzZXISNwoIaWRwX25hbWUYAiABKAlCJeBBAvpBHwodbWVtb3MuYXBpLnYxL0lkZW50aXR5UHJvdmlkZXISEQoEY29kZRgDIAEoCUID4EECEhkKDHJlZGlyZWN0X3VyaRgEIAEoCUID4EECEhoKDWNvZGVfdmVyaWZpZXIYBSABKAlCA+BBASJNChhHZXRMaW5rZWRJZGVudGl0eVJlcXVlc3QSMQoEbmFtZRgBIAEoCUIj4EEC+kEdChttZW1vcy5hcGkudjEvTGlua2VkSWRlbnRpdHkiUAobRGVsZXRlTGlua2VkSWRlbnRpdHlSZXF1ZXN0EjEKBG5hbWUYASABKAlCI+BBAvpBHQobbWVtb3MuYXBpLnYxL0xpbmtlZElkZW50aXR5IvICChNQZXJzb25hbEFjY2Vzc1Rva2VuEhEKBG5hbWUYASABKAlCA+BBCBIYCgtkZXNjcmlwdGlvbhgCIAEoCUID4EEBEjMKCmNyZWF0ZWRfYXQYAyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQgPgQQMSMwoKZXhwaXJlc19hdBgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBARI1CgxsYXN0X3VzZWRfYXQYBSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQgPgQQM6jAHqQYgBCiBtZW1vcy5hcGkudjEvUGVyc29uYWxBY2Nlc3NUb2tlbhI5dXNlcnMve3VzZXJ9L3BlcnNvbmFsQWNjZXNzVG9rZW5zL3twZXJzb25hbF9hY2Nlc3NfdG9rZW59KhRwZXJzb25hbEFjY2Vzc1Rva2VuczITcGVyc29uYWxBY2Nlc3NUb2tlbiJ9Ch9MaXN0UGVyc29uYWxBY2Nlc3NUb2tlbnNSZXF1ZXN0EikKBnBhcmVudBgBIAEoCUIZ4EEC+kETChFtZW1vcy5hcGkudjEvVXNlchIWCglwYWdlX3NpemUYAiABKAVCA+BBARIXCgpwYWdlX3Rva2VuGAMgASgJQgPgQQEikgEKIExpc3RQZXJzb25hbEFjY2Vzc1Rva2Vuc1Jlc3BvbnNlEkEKFnBlcnNvbmFsX2FjY2Vzc190b2tlbnMYASADKAsyIS5tZW1vcy5hcGkudjEuUGVyc29uYWxBY2Nlc3NUb2tlbhIXCg9uZXh0X3BhZ2VfdG9rZW4YAiABKAkSEgoKdG90YWxfc2l6ZRgDIAEoBSKFAQogQ3JlYXRlUGVyc29uYWxBY2Nlc3NUb2tlblJlcXVlc3QSKQoGcGFyZW50GAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyEhgKC2Rlc2NyaXB0aW9uGAIgASgJQgPgQQESHAoPZXhwaXJlc19pbl9kYXlzGAMgASgFQgPgQQEidAohQ3JlYXRlUGVyc29uYWxBY2Nlc3NUb2tlblJlc3BvbnNlEkAKFXBlcnNvbmFsX2FjY2Vzc190b2tlbhgBIAEoCzIhLm1lbW9zLmFwaS52MS5QZXJzb25hbEFjY2Vzc1Rva2VuEg0KBXRva2VuGAIgASgJIloKIERlbGV0ZVBlcnNvbmFsQWNjZXNzVG9rZW5SZXF1ZXN0EjYKBG5hbWUYASABKAlCKOBBAvpBIgogbWVtb3MuYXBpLnYxL1BlcnNvbmFsQWNjZXNzVG9rZW4i6AEKC1VzZXJXZWJob29rEgwKBG5hbWUYASABKAkSCwoDdXJsGAIgASgJEhQKDGRpc3BsYXlfbmFtZRgDIAEoCRI0CgtjcmVhdGVfdGltZRgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAxI0Cgt1cGRhdGVfdGltZRgFIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAxIbCg5zaWduaW5nX3NlY3JldBgGIAEoCUID4EEEEh8KEmhhc19zaWduaW5nX3NlY3JldBgHIAEoCEID4EEDIi4KF0xpc3RVc2VyV2ViaG9va3NSZXF1ZXN0EhMKBnBhcmVudBgBIAEoCUID4EECIkcKGExpc3RVc2VyV2ViaG9va3NSZXNwb25zZRIrCgh3ZWJob29rcxgBIAMoCzIZLm1lbW9zLmFwaS52MS5Vc2VyV2ViaG9vayJgChhDcmVhdGVVc2VyV2ViaG9va1JlcXVlc3QSEwoGcGFyZW50GAEgASgJQgPgQQISLwoHd2ViaG9vaxgCIAEoCzIZLm1lbW9zLmFwaS52MS5Vc2VyV2ViaG9va0ID4EECInwKGFVwZGF0ZVVzZXJXZWJob29rUmVxdWVzdBIvCgd3ZWJob29rGAEgASgLMhkubWVtb3MuYXBpLnYxLlVzZXJXZWJob29rQgPgQQISLwoLdXBkYXRlX21hc2sYAiABKAsyGi5nb29nbGUucHJvdG9idWYuRmllbGRNYXNrIi0KGERlbGV0ZVVzZXJXZWJob29rUmVxdWVzdBIRCgRuYW1lGAEgASgJQgPgQQIiogcKEFVzZXJOb3RpZmljYXRpb24SFAoEbmFtZRgBIAEoCUIG4EED4EEIEikKBnNlbmRlchgCIAEoCUIZ4EED+kETChFtZW1vcy5hcGkudjEvVXNlchIsCgtzZW5kZXJfdXNlchgIIAEoCzISLm1lbW9zLmFwaS52MS5Vc2VyQgPgQQMSOgoGc3RhdHVzGAMgASgOMiUubWVtb3MuYXBpLnYxLlVzZXJOb3RpZmljYXRpb24uU3RhdHVzQgPgQQESNAoLY3JlYXRlX3RpbWUYBCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQgPgQQMSNgoEdHlwZRgFIAEoDjIjLm1lbW9zLmFwaS52MS5Vc2VyTm90aWZpY2F0aW9uLlR5cGVCA+BBAxJOCgxtZW1vX2NvbW1lbnQYBiABKAsyMS5tZW1vcy5hcGkudjEuVXNlck5vdGlmaWNhdGlvbi5NZW1vQ29tbWVudFBheWxvYWRCA+BBA0gAEk4KDG1lbW9fbWVudGlvbhgHIAEoCzIxLm1lbW9zLmFwaS52MS5Vc2VyTm90aWZpY2F0aW9uLk1lbW9NZW50aW9uUGF5bG9hZEID4EEDSAAabAoSTWVtb0NvbW1lbnRQYXlsb2FkEgwKBG1lbW8YASABKAkSFAoMcmVsYXRlZF9tZW1vGAIgASgJEhQKDG1lbW9fc25pcHBldBgDIAEoCRIcChRyZWxhdGVkX21lbW9fc25pcHBldBgEIAEoCRpsChJNZW1vTWVudGlvblBheWxvYWQSDAoEbWVtbxgBIAEoCRIUCgxyZWxhdGVkX21lbW8YAiABKAkSFAoMbWVtb19zbmlwcGV0GAMgASgJEhwKFHJlbGF0ZWRfbWVtb19zbmlwcGV0GAQgASgJIjoKBlN0YXR1cxIWChJTVEFUVVNfVU5TUEVDSUZJRUQQABIKCgZVTlJFQUQQARIMCghBUkNISVZFRBACIkAKBFR5cGUSFAoQVFlQRV9VTlNQRUNJRklFRBAAEhAKDE1FTU9fQ09NTUVOVBABEhAKDE1FTU9fTUVOVElPThACOnDqQW0KHW1lbW9zLmFwaS52MS9Vc2VyTm90aWZpY2F0aW9uEil1c2Vycy97dXNlcn0vbm90aWZpY2F0aW9ucy97bm90aWZpY2F0aW9ufRoEbmFtZSoNbm90aWZpY2F0aW9uczIMbm90aWZpY2F0aW9uQgkKB3BheWxvYWQijwEKHExpc3RVc2VyTm90aWZpY2F0aW9uc1JlcXVlc3QSKQoGcGFyZW50GAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyEhYKCXBhZ2Vfc2l6ZRgCIAEoBUID4EEBEhcKCnBhZ2VfdG9rZW4YAyABKAlCA+BBARITCgZmaWx0ZXIYBCABKAlCA+BBASJvCh1MaXN0VXNlck5vdGlmaWNhdGlvbnNSZXNwb25zZRI1Cg1ub3RpZmljYXRpb25zGAEgAygLMh4ubWVtb3MuYXBpLnYxLlVzZXJOb3RpZmljYXRpb24SFwoPbmV4dF9wYWdlX3Rva2VuGAIgASgJIpABCh1VcGRhdGVVc2VyTm90aWZpY2F0aW9uUmVxdWVzdBI5Cgxub3RpZmljYXRpb24YASABKAsyHi5tZW1vcy5hcGkudjEuVXNlck5vdGlmaWNhdGlvbkID4EECEjQKC3VwZGF0ZV9tYXNrGAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLkZpZWxkTWFza0ID4EECIlQKHURlbGV0ZVVzZXJOb3RpZmljYXRpb25SZXF1ZXN0EjMKBG5hbWUYASABKAlCJeBBAvpBHwodbWVtb3MuYXBpLnYxL1VzZXJOb3RpZmljYXRpb24ygh0KC1VzZXJTZXJ2aWNlEmMKCUxpc3RVc2VycxIeLm1lbW9zLmFwaS52MS5MaXN0VXNlcnNSZXF1ZXN0Gh8ubWVtb3MuYXBpLnYxLkxpc3RVc2Vyc1Jlc3BvbnNlIhWC0+STAg8SDS9hcGkvdjEvdXNlcnMSewoNQmF0Y2hHZXRVc2VycxIiLm1lbW9zLmFwaS52MS5CYXRjaEdldFVzZXJzUmVxdWVzdBojLm1lbW9zLmFwaS52MS5CYXRjaEdldFVzZXJzUmVzcG9uc2UiIYLT5JMCGzoBKiIWL2FwaS92MS91c2VyczpiYXRjaEdldBJiCgdHZXRVc2VyEhwubWVtb3MuYXBpLnYxLkdldFVzZXJSZXF1ZXN0GhIubWVtb3MuYXBpLnYxLlVzZXIiJdpBBG5hbWWC0+STAhgSFi9hcGkvdjEve25hbWU9dXNlcnMvKn0SZQoKQ3JlYXRlVXNlchIfLm1lbW9zLmFwaS52MS5DcmVhdGVVc2VyUmVxdWVzdBoSLm1lbW9zLmFwaS52MS5Vc2VyIiLaQQR1c2VygtPkkwIVOgR1c2VyIg0vYXBpL3YxL3VzZXJzEn8KClVwZGF0ZVVzZXISHy5tZW1vcy5hcGkudjEuVXBkYXRlVXNlclJlcXVlc3QaEi5tZW1vcy5hcGkudjEuVXNlciI82kEQdXNlcix1cGRhdGVfbWFza4LT5JMCIzoEdXNlcjIbL2FwaS92MS97dXNlci5uYW1lPXVzZXJzLyp9EmwKCkRlbGV0ZVVzZXISHy5tZW1vcy5hcGkudjEuRGVsZXRlVXNlclJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiJdpBBG5hbWWC0+STAhgqFi9hcGkvdjEve25hbWU9dXNlcnMvKn0SfgoQTGlzdEFsbFVzZXJTdGF0cxIlLm1lbW9zLmFwaS52MS5MaXN0QWxsVXNlclN0YXRzUmVxdWVzdBomLm1lbW9zLmFwaS52MS5MaXN0QWxsVXNlclN0YXRzUmVzcG9uc2UiG4LT5JMCFRITL2FwaS92MS91c2VyczpzdGF0cxJ6CgxHZXRVc2VyU3RhdHMSIS5tZW1vcy5hcGkudjEuR2V0VXNlclN0YXRzUmVxdWVzdBoXLm1lbW9zLmFwaS52MS5Vc2VyU3RhdHMiLtpBBG5hbWWC0+STAiESHy9hcGkvdjEve25hbWU9dXNlcnMvKn06Z2V0U3RhdHMSggEKDkdldFVzZXJTZXR0aW5nEiMubWVtb3MuYXBpLnYxLkdldFVzZXJTZXR0aW5nUmVxdWVzdBoZLm1lbW9zLmFwaS52MS5Vc2VyU2V0dGluZyIw2kEEbmFtZYLT5JMCIxIhL2FwaS92MS97bmFtZT11c2Vycy8qL3NldHRpbmdzLyp9EqgBChFVcGRhdGVVc2VyU2V0dGluZxImLm1lbW9zLmFwaS52MS5VcGRhdGVVc2VyU2V0dGluZ1JlcXVlc3QaGS5tZW1vcy5hcGkudjEuVXNlclNldHRpbmciUNpBE3NldHRpbmcsdXBkYXRlX21hc2uC0+STAjQ6B3NldHRpbmcyKS9hcGkvdjEve3NldHRpbmcubmFtZT11c2Vycy8qL3NldHRpbmdzLyp9EpUBChBMaXN0VXNlclNldHRpbmdzEiUubWVtb3MuYXBpLnYxLkxpc3RVc2VyU2V0dGluZ3NSZXF1ZXN0GiYubWVtb3MuYXBpLnYxLkxpc3RVc2VyU2V0dGluZ3NSZXNwb25zZSIy2kEGcGFyZW50gtPkkwIjEiEvYXBpL3YxL3twYXJlbnQ9dXNlcnMvKn0vc2V0dGluZ3MSqQEKFExpc3RMaW5rZWRJZGVudGl0aWVzEikubWVtb3MuYXBpLnYxLkxpc3RMaW5rZWRJZGVudGl0aWVzUmVxdWVzdBoqLm1lbW9zLmFwaS52MS5MaXN0TGlua2VkSWRlbnRpdGllc1Jlc3BvbnNlIjraQQZwYXJlbnSC0+STAisSKS9hcGkvdjEve3BhcmVudD11c2Vycy8qfS9saW5rZWRJZGVudGl0aWVzEqcBChRDcmVhdGVMaW5rZWRJZGVudGl0eRIpLm1lbW9zLmFwaS52MS5DcmVhdGVMaW5rZWRJZGVudGl0eVJlcXVlc3QaHC5tZW1vcy5hcGkudjEuTGlua2VkSWRlbnRpdHkiRtpBD3BhcmVudCxpZHBfbmFtZYLT5JMCLjoBKiIpL2FwaS92MS97cGFyZW50PXVzZXJzLyp9L2xpbmtlZElkZW50aXRpZXMSkwEKEUdldExpbmtlZElkZW50aXR5EiYubWVtb3MuYXBpLnYxLkdldExpbmtlZElkZW50aXR5UmVxdWVzdBocLm1lbW9zLmFwaS52MS5MaW5rZWRJZGVudGl0eSI42kEEbmFtZYLT5JMCKxIpL2FwaS92MS97bmFtZT11c2Vycy8qL2xpbmtlZElkZW50aXRpZXMvKn0SkwEKFERlbGV0ZUxpbmtlZElkZW50aXR5EikubWVtb3MuYXBpLnYxLkRlbGV0ZUxpbmtlZElkZW50aXR5UmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSI42kEEbmFtZYLT5JMCKyopL2FwaS92MS97bmFtZT11c2Vycy8qL2xpbmtlZElkZW50aXRpZXMvKn0SuQEKGExpc3RQZXJzb25hbEFjY2Vzc1Rva2VucxItLm1lbW9zLmFwaS52MS5MaXN0UGVyc29uYWxBY2Nlc3NUb2tlbnNSZXF1ZXN0Gi4ubWVtb3MuYXBpLnYxLkxpc3RQZXJzb25hbEFjY2Vzc1Rva2Vuc1Jlc3BvbnNlIj7aQQZwYXJlbnSC0+STAi8SLS9hcGkvdjEve3BhcmVudD11c2Vycy8qfS9wZXJzb25hbEFjY2Vzc1Rva2VucxK2AQoZQ3JlYXRlUGVyc29uYWxBY2Nlc3NUb2tlbhIuLm1lbW9zLmFwaS52MS5DcmVhdGVQZXJzb25hbEFjY2Vzc1Rva2VuUmVxdWVzdBovLm1lbW9zLmFwaS52MS5DcmVhdGVQZXJzb25hbEFjY2Vzc1Rva2VuUmVzcG9uc2UiOILT5JMCMjoBKiItL2FwaS92MS97cGFyZW50PXVzZXJzLyp9L3BlcnNvbmFsQWNjZXNzVG9rZW5zEqEBChlEZWxldGVQZXJzb25hbEFjY2Vzc1Rva2VuEi4ubWVtb3MuYXBpLnYxLkRlbGV0ZVBlcnNvbmFsQWNjZXNzVG9rZW5SZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IjzaQQRuYW1lgtPkkwIvKi0vYXBpL3YxL3tuYW1lPXVzZXJzLyovcGVyc29uYWxBY2Nlc3NUb2tlbnMvKn0SlQEKEExpc3RVc2VyV2ViaG9va3MSJS5tZW1vcy5hcGkudjEuTGlzdFVzZXJXZWJob29rc1JlcXVlc3QaJi5tZW1vcy5hcGkudjEuTGlzdFVzZXJXZWJob29rc1Jlc3BvbnNlIjLaQQZwYXJlbnSC0+STAiMSIS9hcGkvdjEve3BhcmVudD11c2Vycy8qfS93ZWJob29rcxKbAQoRQ3JlYXRlVXNlcldlYmhvb2sSJi5tZW1vcy5hcGkudjEuQ3JlYXRlVXNlcldlYmhvb2tSZXF1ZXN0GhkubWVtb3MuYXBpLnYxLlVzZXJXZWJob29rIkPaQQ5wYXJlbnQsd2ViaG9va4LT5JMCLDoHd2ViaG9vayIhL2FwaS92MS97cGFyZW50PXVzZXJzLyp9L3dlYmhvb2tzEqgBChFVcGRhdGVVc2VyV2ViaG9vaxImLm1lbW9zLmFwaS52MS5VcGRhdGVVc2VyV2ViaG9va1JlcXVlc3QaGS5tZW1vcy5hcGkudjEuVXNlcldlYmhvb2siUNpBE3dlYmhvb2ssdXBkYXRlX21hc2uC0+STAjQ6B3dlYmhvb2syKS9hcGkvdjEve3dlYmhvb2submFtZT11c2Vycy8qL3dlYmhvb2tzLyp9EoUBChFEZWxldGVVc2VyV2ViaG9vaxImLm1lbW9zLmFwaS52MS5EZWxldGVVc2VyV2ViaG9va1JlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiMNpBBG5hbWWC0+STAiMqIS9hcGkvdjEve25hbWU9dXNlcnMvKi93ZWJob29rcy8qfRKpAQoVTGlzdFVzZXJOb3RpZmljYXRpb25zEioubWVtb3MuYXBpLnYxLkxpc3RVc2VyTm90aWZpY2F0aW9uc1JlcXVlc3QaKy5tZW1vcy5hcGkudjEuTGlzdFVzZXJOb3RpZmljYXRpb25zUmVzcG9uc2UiN9pBBnBhcmVudILT5JMCKBImL2FwaS92MS97cGFyZW50PXVzZXJzLyp9L25vdGlmaWNhdGlvbnMSywEKFlVwZGF0ZVVzZXJOb3RpZmljYXRpb24SKy5tZW1vcy5hcGkudjEuVXBkYXRlVXNlck5vdGlmaWNhdGlvblJlcXVlc3QaHi5tZW1vcy5hcGkudjEuVXNlck5vdGlmaWNhdGlvbiJk2kEYbm90aWZpY2F0aW9uLHVwZGF0ZV9tYXNrgtPkkwJDOgxub3RpZmljYXRpb24yMy9hcGkvdjEve25vdGlmaWNhdGlvbi5uYW1lPXVzZXJzLyovbm90aWZpY2F0aW9ucy8qfRKUAQoWRGVsZXRlVXNlck5vdGlmaWNhdGlvbhIrLm1lbW9zLmFwaS52MS5EZWxldGVVc2VyTm90aWZpY2F0aW9uUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSI12kEEbmFtZYLT5JMCKComL2FwaS92MS97bmFtZT11c2Vycy8qL25vdGlmaWNhdGlvbnMvKn1CqAEKEGNvbS5tZW1vcy5hcGkudjFCEFVzZXJTZXJ2aWNlUHJvdG9QAVowZ2l0aHViLmNvbS91c2VtZW1vcy9tZW1vcy9wcm90by9nZW4vYXBpL3YxO2FwaXYxogIDTUFYqgIMTWVtb3MuQXBpLlYxygIMTWVtb3NcQXBpXFYx4gIYTWVtb3NcQXBpXFYxXEdQQk1ldGFkYXRh6gIOTWVtb3M6OkFwaTo6VjFiBnByb3RvMw", [file_api_v1_common, file_google_api_annotations, file_google_api_client, file_google_api_field_behavior, file_google_api_resource, file_google_protobuf_empty, file_google_protobuf_field_mask, file_google_protobuf_timestamp, file_google_type_color]); + fileDesc("ChlhcGkvdjEvdXNlcl9zZXJ2aWNlLnByb3RvEgxtZW1vcy5hcGkudjEi1gMKBFVzZXISEQoEbmFtZRgBIAEoCUID4EEIEioKBHJvbGUYAiABKA4yFy5tZW1vcy5hcGkudjEuVXNlci5Sb2xlQgPgQQISFQoIdXNlcm5hbWUYAyABKAlCA+BBAhISCgVlbWFpbBgEIAEoCUID4EEBEhkKDGRpc3BsYXlfbmFtZRgFIAEoCUID4EEBEhcKCmF2YXRhcl91cmwYBiABKAlCA+BBARIYCgtkZXNjcmlwdGlvbhgHIAEoCUID4EEBEhUKCHBhc3N3b3JkGAggASgJQgPgQQQSJwoFc3RhdGUYCSABKA4yEy5tZW1vcy5hcGkudjEuU3RhdGVCA+BBAhI0CgtjcmVhdGVfdGltZRgKIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAxI0Cgt1cGRhdGVfdGltZRgLIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAyIxCgRSb2xlEhQKEFJPTEVfVU5TUEVDSUZJRUQQABIJCgVBRE1JThACEggKBFVTRVIQAzo36kE0ChFtZW1vcy5hcGkudjEvVXNlchIMdXNlcnMve3VzZXJ9GgRuYW1lKgV1c2VyczIEdXNlciJzChBMaXN0VXNlcnNSZXF1ZXN0EhYKCXBhZ2Vfc2l6ZRgBIAEoBUID4EEBEhcKCnBhZ2VfdG9rZW4YAiABKAlCA+BBARITCgZmaWx0ZXIYAyABKAlCA+BBARIZCgxzaG93X2RlbGV0ZWQYBCABKAhCA+BBASJjChFMaXN0VXNlcnNSZXNwb25zZRIhCgV1c2VycxgBIAMoCzISLm1lbW9zLmFwaS52MS5Vc2VyEhcKD25leHRfcGFnZV90b2tlbhgCIAEoCRISCgp0b3RhbF9zaXplGAMgASgFIikKFEJhdGNoR2V0VXNlcnNSZXF1ZXN0EhEKCXVzZXJuYW1lcxgBIAMoCSI6ChVCYXRjaEdldFVzZXJzUmVzcG9uc2USIQoFdXNlcnMYASADKAsyEi5tZW1vcy5hcGkudjEuVXNlciJtCg5HZXRVc2VyUmVxdWVzdBInCgRuYW1lGAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyEjIKCXJlYWRfbWFzaxgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE1hc2tCA+BBASKIAQoRQ3JlYXRlVXNlclJlcXVlc3QSKAoEdXNlchgBIAEoCzISLm1lbW9zLmFwaS52MS5Vc2VyQgbgQQLgQQQSFAoHdXNlcl9pZBgCIAEoCUID4EEBEhoKDXZhbGlkYXRlX29ubHkYAyABKAhCA+BBARIXCgpyZXF1ZXN0X2lkGAQgASgJQgPgQQEijAEKEVVwZGF0ZVVzZXJSZXF1ZXN0EiUKBHVzZXIYASABKAsyEi5tZW1vcy5hcGkudjEuVXNlckID4EECEjQKC3VwZGF0ZV9tYXNrGAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLkZpZWxkTWFza0ID4EECEhoKDWFsbG93X21pc3NpbmcYAyABKAhCA+BBASJQChFEZWxldGVVc2VyUmVxdWVzdBInCgRuYW1lGAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyEhIKBWZvcmNlGAIgASgIQgPgQQEitAQKCVVzZXJTdGF0cxIRCgRuYW1lGAEgASgJQgPgQQgSPgoPbWVtb190eXBlX3N0YXRzGAMgASgLMiUubWVtb3MuYXBpLnYxLlVzZXJTdGF0cy5NZW1vVHlwZVN0YXRzEjgKCXRhZ19jb3VudBgEIAMoCzIlLm1lbW9zLmFwaS52MS5Vc2VyU3RhdHMuVGFnQ291bnRFbnRyeRI7ChdtZW1vX2NyZWF0ZWRfdGltZXN0YW1wcxgHIAMoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASOwoXbWVtb191cGRhdGVkX3RpbWVzdGFtcHMYCCADKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEhQKDHBpbm5lZF9tZW1vcxgFIAMoCRIYChB0b3RhbF9tZW1vX2NvdW50GAYgASgFGl8KDU1lbW9UeXBlU3RhdHMSEgoKbGlua19jb3VudBgBIAEoBRISCgpjb2RlX2NvdW50GAIgASgFEhIKCnRvZG9fY291bnQYAyABKAUSEgoKdW5kb19jb3VudBgEIAEoBRovCg1UYWdDb3VudEVudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoBToCOAE6P+pBPAoWbWVtb3MuYXBpLnYxL1VzZXJTdGF0cxIMdXNlcnMve3VzZXJ9Kgl1c2VyU3RhdHMyCXVzZXJTdGF0c0oECAIQA1IXbWVtb19kaXNwbGF5X3RpbWVzdGFtcHMiPgoTR2V0VXNlclN0YXRzUmVxdWVzdBInCgRuYW1lGAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyIlcKF0xpc3RBbGxVc2VyU3RhdHNSZXF1ZXN0EicKBXN0YXRlGAEgASgOMhMubWVtb3MuYXBpLnYxLlN0YXRlQgPgQQESEwoGZmlsdGVyGAIgASgJQgPgQQEiQgoYTGlzdEFsbFVzZXJTdGF0c1Jlc3BvbnNlEiYKBXN0YXRzGAEgAygLMhcubWVtb3MuYXBpLnYxLlVzZXJTdGF0cyKyBgoLVXNlclNldHRpbmcSEQoEbmFtZRgBIAEoCUID4EEIEkMKD2dlbmVyYWxfc2V0dGluZxgCIAEoCzIoLm1lbW9zLmFwaS52MS5Vc2VyU2V0dGluZy5HZW5lcmFsU2V0dGluZ0gAEkUKEHdlYmhvb2tzX3NldHRpbmcYBSABKAsyKS5tZW1vcy5hcGkudjEuVXNlclNldHRpbmcuV2ViaG9va3NTZXR0aW5nSAASPQoMdGFnc19zZXR0aW5nGAYgASgLMiUubWVtb3MuYXBpLnYxLlVzZXJTZXR0aW5nLlRhZ3NTZXR0aW5nSAAaVwoOR2VuZXJhbFNldHRpbmcSEwoGbG9jYWxlGAEgASgJQgPgQQESHAoPbWVtb192aXNpYmlsaXR5GAMgASgJQgPgQQESEgoFdGhlbWUYBCABKAlCA+BBARpbCgtUYWdNZXRhZGF0YRIxChBiYWNrZ3JvdW5kX2NvbG9yGAEgASgLMhIuZ29vZ2xlLnR5cGUuQ29sb3JCA+BBARIZCgxibHVyX2NvbnRlbnQYAiABKAhCA+BBARqlAQoLVGFnc1NldHRpbmcSQgoEdGFncxgBIAMoCzIvLm1lbW9zLmFwaS52MS5Vc2VyU2V0dGluZy5UYWdzU2V0dGluZy5UYWdzRW50cnlCA+BBARpSCglUYWdzRW50cnkSCwoDa2V5GAEgASgJEjQKBXZhbHVlGAIgASgLMiUubWVtb3MuYXBpLnYxLlVzZXJTZXR0aW5nLlRhZ01ldGFkYXRhOgI4ARo+Cg9XZWJob29rc1NldHRpbmcSKwoId2ViaG9va3MYASADKAsyGS5tZW1vcy5hcGkudjEuVXNlcldlYmhvb2siPwoDS2V5EhMKD0tFWV9VTlNQRUNJRklFRBAAEgsKB0dFTkVSQUwQARIMCghXRUJIT09LUxAEEggKBFRBR1MQBTpd6kFaChhtZW1vcy5hcGkudjEvVXNlclNldHRpbmcSI3VzZXJzL3t1c2VybmFtZX0vc2V0dGluZ3Mve3NldHRpbmd9Kgx1c2VyU2V0dGluZ3MyC3VzZXJTZXR0aW5nQgcKBXZhbHVlIkcKFUdldFVzZXJTZXR0aW5nUmVxdWVzdBIuCgRuYW1lGAEgASgJQiDgQQL6QRoKGG1lbW9zLmFwaS52MS9Vc2VyU2V0dGluZyKBAQoYVXBkYXRlVXNlclNldHRpbmdSZXF1ZXN0Ei8KB3NldHRpbmcYASABKAsyGS5tZW1vcy5hcGkudjEuVXNlclNldHRpbmdCA+BBAhI0Cgt1cGRhdGVfbWFzaxgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE1hc2tCA+BBAiJ1ChdMaXN0VXNlclNldHRpbmdzUmVxdWVzdBIpCgZwYXJlbnQYASABKAlCGeBBAvpBEwoRbWVtb3MuYXBpLnYxL1VzZXISFgoJcGFnZV9zaXplGAIgASgFQgPgQQESFwoKcGFnZV90b2tlbhgDIAEoCUID4EEBInQKGExpc3RVc2VyU2V0dGluZ3NSZXNwb25zZRIrCghzZXR0aW5ncxgBIAMoCzIZLm1lbW9zLmFwaS52MS5Vc2VyU2V0dGluZxIXCg9uZXh0X3BhZ2VfdG9rZW4YAiABKAkSEgoKdG90YWxfc2l6ZRgDIAEoBSLqAQoOTGlua2VkSWRlbnRpdHkSEQoEbmFtZRgBIAEoCUID4EEIEjcKCGlkcF9uYW1lGAIgASgJQiXgQQP6QR8KHW1lbW9zLmFwaS52MS9JZGVudGl0eVByb3ZpZGVyEhcKCmV4dGVybl91aWQYAyABKAlCA+BBAzpz6kFwChttZW1vcy5hcGkudjEvTGlua2VkSWRlbnRpdHkSL3VzZXJzL3t1c2VyfS9saW5rZWRJZGVudGl0aWVzL3tsaW5rZWRfaWRlbnRpdHl9KhBsaW5rZWRJZGVudGl0aWVzMg5saW5rZWRJZGVudGl0eSJIChtMaXN0TGlua2VkSWRlbnRpdGllc1JlcXVlc3QSKQoGcGFyZW50GAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyIlcKHExpc3RMaW5rZWRJZGVudGl0aWVzUmVzcG9uc2USNwoRbGlua2VkX2lkZW50aXRpZXMYASADKAsyHC5tZW1vcy5hcGkudjEuTGlua2VkSWRlbnRpdHkiywEKG0NyZWF0ZUxpbmtlZElkZW50aXR5UmVxdWVzdBIpCgZwYXJlbnQYASABKAlCGeBBAvpBEwoRbWVtb3MuYXBpLnYxL1VzZXISNwoIaWRwX25hbWUYAiABKAlCJeBBAvpBHwodbWVtb3MuYXBpLnYxL0lkZW50aXR5UHJvdmlkZXISEQoEY29kZRgDIAEoCUID4EECEhkKDHJlZGlyZWN0X3VyaRgEIAEoCUID4EECEhoKDWNvZGVfdmVyaWZpZXIYBSABKAlCA+BBASJNChhHZXRMaW5rZWRJZGVudGl0eVJlcXVlc3QSMQoEbmFtZRgBIAEoCUIj4EEC+kEdChttZW1vcy5hcGkudjEvTGlua2VkSWRlbnRpdHkiUAobRGVsZXRlTGlua2VkSWRlbnRpdHlSZXF1ZXN0EjEKBG5hbWUYASABKAlCI+BBAvpBHQobbWVtb3MuYXBpLnYxL0xpbmtlZElkZW50aXR5IvICChNQZXJzb25hbEFjY2Vzc1Rva2VuEhEKBG5hbWUYASABKAlCA+BBCBIYCgtkZXNjcmlwdGlvbhgCIAEoCUID4EEBEjMKCmNyZWF0ZWRfYXQYAyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQgPgQQMSMwoKZXhwaXJlc19hdBgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBARI1CgxsYXN0X3VzZWRfYXQYBSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQgPgQQM6jAHqQYgBCiBtZW1vcy5hcGkudjEvUGVyc29uYWxBY2Nlc3NUb2tlbhI5dXNlcnMve3VzZXJ9L3BlcnNvbmFsQWNjZXNzVG9rZW5zL3twZXJzb25hbF9hY2Nlc3NfdG9rZW59KhRwZXJzb25hbEFjY2Vzc1Rva2VuczITcGVyc29uYWxBY2Nlc3NUb2tlbiJ9Ch9MaXN0UGVyc29uYWxBY2Nlc3NUb2tlbnNSZXF1ZXN0EikKBnBhcmVudBgBIAEoCUIZ4EEC+kETChFtZW1vcy5hcGkudjEvVXNlchIWCglwYWdlX3NpemUYAiABKAVCA+BBARIXCgpwYWdlX3Rva2VuGAMgASgJQgPgQQEikgEKIExpc3RQZXJzb25hbEFjY2Vzc1Rva2Vuc1Jlc3BvbnNlEkEKFnBlcnNvbmFsX2FjY2Vzc190b2tlbnMYASADKAsyIS5tZW1vcy5hcGkudjEuUGVyc29uYWxBY2Nlc3NUb2tlbhIXCg9uZXh0X3BhZ2VfdG9rZW4YAiABKAkSEgoKdG90YWxfc2l6ZRgDIAEoBSKFAQogQ3JlYXRlUGVyc29uYWxBY2Nlc3NUb2tlblJlcXVlc3QSKQoGcGFyZW50GAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyEhgKC2Rlc2NyaXB0aW9uGAIgASgJQgPgQQESHAoPZXhwaXJlc19pbl9kYXlzGAMgASgFQgPgQQEidAohQ3JlYXRlUGVyc29uYWxBY2Nlc3NUb2tlblJlc3BvbnNlEkAKFXBlcnNvbmFsX2FjY2Vzc190b2tlbhgBIAEoCzIhLm1lbW9zLmFwaS52MS5QZXJzb25hbEFjY2Vzc1Rva2VuEg0KBXRva2VuGAIgASgJIloKIERlbGV0ZVBlcnNvbmFsQWNjZXNzVG9rZW5SZXF1ZXN0EjYKBG5hbWUYASABKAlCKOBBAvpBIgogbWVtb3MuYXBpLnYxL1BlcnNvbmFsQWNjZXNzVG9rZW4i6AEKC1VzZXJXZWJob29rEgwKBG5hbWUYASABKAkSCwoDdXJsGAIgASgJEhQKDGRpc3BsYXlfbmFtZRgDIAEoCRI0CgtjcmVhdGVfdGltZRgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAxI0Cgt1cGRhdGVfdGltZRgFIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCA+BBAxIbCg5zaWduaW5nX3NlY3JldBgGIAEoCUID4EEEEh8KEnNpZ25pbmdfc2VjcmV0X3NldBgHIAEoCEID4EEDIi4KF0xpc3RVc2VyV2ViaG9va3NSZXF1ZXN0EhMKBnBhcmVudBgBIAEoCUID4EECIkcKGExpc3RVc2VyV2ViaG9va3NSZXNwb25zZRIrCgh3ZWJob29rcxgBIAMoCzIZLm1lbW9zLmFwaS52MS5Vc2VyV2ViaG9vayJgChhDcmVhdGVVc2VyV2ViaG9va1JlcXVlc3QSEwoGcGFyZW50GAEgASgJQgPgQQISLwoHd2ViaG9vaxgCIAEoCzIZLm1lbW9zLmFwaS52MS5Vc2VyV2ViaG9va0ID4EECInwKGFVwZGF0ZVVzZXJXZWJob29rUmVxdWVzdBIvCgd3ZWJob29rGAEgASgLMhkubWVtb3MuYXBpLnYxLlVzZXJXZWJob29rQgPgQQISLwoLdXBkYXRlX21hc2sYAiABKAsyGi5nb29nbGUucHJvdG9idWYuRmllbGRNYXNrIi0KGERlbGV0ZVVzZXJXZWJob29rUmVxdWVzdBIRCgRuYW1lGAEgASgJQgPgQQIiNwoiR2V0VXNlcldlYmhvb2tTaWduaW5nU2VjcmV0UmVxdWVzdBIRCgRuYW1lGAEgASgJQgPgQQIiPQojR2V0VXNlcldlYmhvb2tTaWduaW5nU2VjcmV0UmVzcG9uc2USFgoOc2lnbmluZ19zZWNyZXQYASABKAkiogcKEFVzZXJOb3RpZmljYXRpb24SFAoEbmFtZRgBIAEoCUIG4EED4EEIEikKBnNlbmRlchgCIAEoCUIZ4EED+kETChFtZW1vcy5hcGkudjEvVXNlchIsCgtzZW5kZXJfdXNlchgIIAEoCzISLm1lbW9zLmFwaS52MS5Vc2VyQgPgQQMSOgoGc3RhdHVzGAMgASgOMiUubWVtb3MuYXBpLnYxLlVzZXJOb3RpZmljYXRpb24uU3RhdHVzQgPgQQESNAoLY3JlYXRlX3RpbWUYBCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQgPgQQMSNgoEdHlwZRgFIAEoDjIjLm1lbW9zLmFwaS52MS5Vc2VyTm90aWZpY2F0aW9uLlR5cGVCA+BBAxJOCgxtZW1vX2NvbW1lbnQYBiABKAsyMS5tZW1vcy5hcGkudjEuVXNlck5vdGlmaWNhdGlvbi5NZW1vQ29tbWVudFBheWxvYWRCA+BBA0gAEk4KDG1lbW9fbWVudGlvbhgHIAEoCzIxLm1lbW9zLmFwaS52MS5Vc2VyTm90aWZpY2F0aW9uLk1lbW9NZW50aW9uUGF5bG9hZEID4EEDSAAabAoSTWVtb0NvbW1lbnRQYXlsb2FkEgwKBG1lbW8YASABKAkSFAoMcmVsYXRlZF9tZW1vGAIgASgJEhQKDG1lbW9fc25pcHBldBgDIAEoCRIcChRyZWxhdGVkX21lbW9fc25pcHBldBgEIAEoCRpsChJNZW1vTWVudGlvblBheWxvYWQSDAoEbWVtbxgBIAEoCRIUCgxyZWxhdGVkX21lbW8YAiABKAkSFAoMbWVtb19zbmlwcGV0GAMgASgJEhwKFHJlbGF0ZWRfbWVtb19zbmlwcGV0GAQgASgJIjoKBlN0YXR1cxIWChJTVEFUVVNfVU5TUEVDSUZJRUQQABIKCgZVTlJFQUQQARIMCghBUkNISVZFRBACIkAKBFR5cGUSFAoQVFlQRV9VTlNQRUNJRklFRBAAEhAKDE1FTU9fQ09NTUVOVBABEhAKDE1FTU9fTUVOVElPThACOnDqQW0KHW1lbW9zLmFwaS52MS9Vc2VyTm90aWZpY2F0aW9uEil1c2Vycy97dXNlcn0vbm90aWZpY2F0aW9ucy97bm90aWZpY2F0aW9ufRoEbmFtZSoNbm90aWZpY2F0aW9uczIMbm90aWZpY2F0aW9uQgkKB3BheWxvYWQijwEKHExpc3RVc2VyTm90aWZpY2F0aW9uc1JlcXVlc3QSKQoGcGFyZW50GAEgASgJQhngQQL6QRMKEW1lbW9zLmFwaS52MS9Vc2VyEhYKCXBhZ2Vfc2l6ZRgCIAEoBUID4EEBEhcKCnBhZ2VfdG9rZW4YAyABKAlCA+BBARITCgZmaWx0ZXIYBCABKAlCA+BBASJvCh1MaXN0VXNlck5vdGlmaWNhdGlvbnNSZXNwb25zZRI1Cg1ub3RpZmljYXRpb25zGAEgAygLMh4ubWVtb3MuYXBpLnYxLlVzZXJOb3RpZmljYXRpb24SFwoPbmV4dF9wYWdlX3Rva2VuGAIgASgJIpABCh1VcGRhdGVVc2VyTm90aWZpY2F0aW9uUmVxdWVzdBI5Cgxub3RpZmljYXRpb24YASABKAsyHi5tZW1vcy5hcGkudjEuVXNlck5vdGlmaWNhdGlvbkID4EECEjQKC3VwZGF0ZV9tYXNrGAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLkZpZWxkTWFza0ID4EECIlQKHURlbGV0ZVVzZXJOb3RpZmljYXRpb25SZXF1ZXN0EjMKBG5hbWUYASABKAlCJeBBAvpBHwodbWVtb3MuYXBpLnYxL1VzZXJOb3RpZmljYXRpb24yyh4KC1VzZXJTZXJ2aWNlEmMKCUxpc3RVc2VycxIeLm1lbW9zLmFwaS52MS5MaXN0VXNlcnNSZXF1ZXN0Gh8ubWVtb3MuYXBpLnYxLkxpc3RVc2Vyc1Jlc3BvbnNlIhWC0+STAg8SDS9hcGkvdjEvdXNlcnMSewoNQmF0Y2hHZXRVc2VycxIiLm1lbW9zLmFwaS52MS5CYXRjaEdldFVzZXJzUmVxdWVzdBojLm1lbW9zLmFwaS52MS5CYXRjaEdldFVzZXJzUmVzcG9uc2UiIYLT5JMCGzoBKiIWL2FwaS92MS91c2VyczpiYXRjaEdldBJiCgdHZXRVc2VyEhwubWVtb3MuYXBpLnYxLkdldFVzZXJSZXF1ZXN0GhIubWVtb3MuYXBpLnYxLlVzZXIiJdpBBG5hbWWC0+STAhgSFi9hcGkvdjEve25hbWU9dXNlcnMvKn0SZQoKQ3JlYXRlVXNlchIfLm1lbW9zLmFwaS52MS5DcmVhdGVVc2VyUmVxdWVzdBoSLm1lbW9zLmFwaS52MS5Vc2VyIiLaQQR1c2VygtPkkwIVOgR1c2VyIg0vYXBpL3YxL3VzZXJzEn8KClVwZGF0ZVVzZXISHy5tZW1vcy5hcGkudjEuVXBkYXRlVXNlclJlcXVlc3QaEi5tZW1vcy5hcGkudjEuVXNlciI82kEQdXNlcix1cGRhdGVfbWFza4LT5JMCIzoEdXNlcjIbL2FwaS92MS97dXNlci5uYW1lPXVzZXJzLyp9EmwKCkRlbGV0ZVVzZXISHy5tZW1vcy5hcGkudjEuRGVsZXRlVXNlclJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiJdpBBG5hbWWC0+STAhgqFi9hcGkvdjEve25hbWU9dXNlcnMvKn0SfgoQTGlzdEFsbFVzZXJTdGF0cxIlLm1lbW9zLmFwaS52MS5MaXN0QWxsVXNlclN0YXRzUmVxdWVzdBomLm1lbW9zLmFwaS52MS5MaXN0QWxsVXNlclN0YXRzUmVzcG9uc2UiG4LT5JMCFRITL2FwaS92MS91c2VyczpzdGF0cxJ6CgxHZXRVc2VyU3RhdHMSIS5tZW1vcy5hcGkudjEuR2V0VXNlclN0YXRzUmVxdWVzdBoXLm1lbW9zLmFwaS52MS5Vc2VyU3RhdHMiLtpBBG5hbWWC0+STAiESHy9hcGkvdjEve25hbWU9dXNlcnMvKn06Z2V0U3RhdHMSggEKDkdldFVzZXJTZXR0aW5nEiMubWVtb3MuYXBpLnYxLkdldFVzZXJTZXR0aW5nUmVxdWVzdBoZLm1lbW9zLmFwaS52MS5Vc2VyU2V0dGluZyIw2kEEbmFtZYLT5JMCIxIhL2FwaS92MS97bmFtZT11c2Vycy8qL3NldHRpbmdzLyp9EqgBChFVcGRhdGVVc2VyU2V0dGluZxImLm1lbW9zLmFwaS52MS5VcGRhdGVVc2VyU2V0dGluZ1JlcXVlc3QaGS5tZW1vcy5hcGkudjEuVXNlclNldHRpbmciUNpBE3NldHRpbmcsdXBkYXRlX21hc2uC0+STAjQ6B3NldHRpbmcyKS9hcGkvdjEve3NldHRpbmcubmFtZT11c2Vycy8qL3NldHRpbmdzLyp9EpUBChBMaXN0VXNlclNldHRpbmdzEiUubWVtb3MuYXBpLnYxLkxpc3RVc2VyU2V0dGluZ3NSZXF1ZXN0GiYubWVtb3MuYXBpLnYxLkxpc3RVc2VyU2V0dGluZ3NSZXNwb25zZSIy2kEGcGFyZW50gtPkkwIjEiEvYXBpL3YxL3twYXJlbnQ9dXNlcnMvKn0vc2V0dGluZ3MSqQEKFExpc3RMaW5rZWRJZGVudGl0aWVzEikubWVtb3MuYXBpLnYxLkxpc3RMaW5rZWRJZGVudGl0aWVzUmVxdWVzdBoqLm1lbW9zLmFwaS52MS5MaXN0TGlua2VkSWRlbnRpdGllc1Jlc3BvbnNlIjraQQZwYXJlbnSC0+STAisSKS9hcGkvdjEve3BhcmVudD11c2Vycy8qfS9saW5rZWRJZGVudGl0aWVzEqcBChRDcmVhdGVMaW5rZWRJZGVudGl0eRIpLm1lbW9zLmFwaS52MS5DcmVhdGVMaW5rZWRJZGVudGl0eVJlcXVlc3QaHC5tZW1vcy5hcGkudjEuTGlua2VkSWRlbnRpdHkiRtpBD3BhcmVudCxpZHBfbmFtZYLT5JMCLjoBKiIpL2FwaS92MS97cGFyZW50PXVzZXJzLyp9L2xpbmtlZElkZW50aXRpZXMSkwEKEUdldExpbmtlZElkZW50aXR5EiYubWVtb3MuYXBpLnYxLkdldExpbmtlZElkZW50aXR5UmVxdWVzdBocLm1lbW9zLmFwaS52MS5MaW5rZWRJZGVudGl0eSI42kEEbmFtZYLT5JMCKxIpL2FwaS92MS97bmFtZT11c2Vycy8qL2xpbmtlZElkZW50aXRpZXMvKn0SkwEKFERlbGV0ZUxpbmtlZElkZW50aXR5EikubWVtb3MuYXBpLnYxLkRlbGV0ZUxpbmtlZElkZW50aXR5UmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSI42kEEbmFtZYLT5JMCKyopL2FwaS92MS97bmFtZT11c2Vycy8qL2xpbmtlZElkZW50aXRpZXMvKn0SuQEKGExpc3RQZXJzb25hbEFjY2Vzc1Rva2VucxItLm1lbW9zLmFwaS52MS5MaXN0UGVyc29uYWxBY2Nlc3NUb2tlbnNSZXF1ZXN0Gi4ubWVtb3MuYXBpLnYxLkxpc3RQZXJzb25hbEFjY2Vzc1Rva2Vuc1Jlc3BvbnNlIj7aQQZwYXJlbnSC0+STAi8SLS9hcGkvdjEve3BhcmVudD11c2Vycy8qfS9wZXJzb25hbEFjY2Vzc1Rva2VucxK2AQoZQ3JlYXRlUGVyc29uYWxBY2Nlc3NUb2tlbhIuLm1lbW9zLmFwaS52MS5DcmVhdGVQZXJzb25hbEFjY2Vzc1Rva2VuUmVxdWVzdBovLm1lbW9zLmFwaS52MS5DcmVhdGVQZXJzb25hbEFjY2Vzc1Rva2VuUmVzcG9uc2UiOILT5JMCMjoBKiItL2FwaS92MS97cGFyZW50PXVzZXJzLyp9L3BlcnNvbmFsQWNjZXNzVG9rZW5zEqEBChlEZWxldGVQZXJzb25hbEFjY2Vzc1Rva2VuEi4ubWVtb3MuYXBpLnYxLkRlbGV0ZVBlcnNvbmFsQWNjZXNzVG9rZW5SZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IjzaQQRuYW1lgtPkkwIvKi0vYXBpL3YxL3tuYW1lPXVzZXJzLyovcGVyc29uYWxBY2Nlc3NUb2tlbnMvKn0SlQEKEExpc3RVc2VyV2ViaG9va3MSJS5tZW1vcy5hcGkudjEuTGlzdFVzZXJXZWJob29rc1JlcXVlc3QaJi5tZW1vcy5hcGkudjEuTGlzdFVzZXJXZWJob29rc1Jlc3BvbnNlIjLaQQZwYXJlbnSC0+STAiMSIS9hcGkvdjEve3BhcmVudD11c2Vycy8qfS93ZWJob29rcxKbAQoRQ3JlYXRlVXNlcldlYmhvb2sSJi5tZW1vcy5hcGkudjEuQ3JlYXRlVXNlcldlYmhvb2tSZXF1ZXN0GhkubWVtb3MuYXBpLnYxLlVzZXJXZWJob29rIkPaQQ5wYXJlbnQsd2ViaG9va4LT5JMCLDoHd2ViaG9vayIhL2FwaS92MS97cGFyZW50PXVzZXJzLyp9L3dlYmhvb2tzEqgBChFVcGRhdGVVc2VyV2ViaG9vaxImLm1lbW9zLmFwaS52MS5VcGRhdGVVc2VyV2ViaG9va1JlcXVlc3QaGS5tZW1vcy5hcGkudjEuVXNlcldlYmhvb2siUNpBE3dlYmhvb2ssdXBkYXRlX21hc2uC0+STAjQ6B3dlYmhvb2syKS9hcGkvdjEve3dlYmhvb2submFtZT11c2Vycy8qL3dlYmhvb2tzLyp9EoUBChFEZWxldGVVc2VyV2ViaG9vaxImLm1lbW9zLmFwaS52MS5EZWxldGVVc2VyV2ViaG9va1JlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiMNpBBG5hbWWC0+STAiMqIS9hcGkvdjEve25hbWU9dXNlcnMvKi93ZWJob29rcy8qfRLFAQobR2V0VXNlcldlYmhvb2tTaWduaW5nU2VjcmV0EjAubWVtb3MuYXBpLnYxLkdldFVzZXJXZWJob29rU2lnbmluZ1NlY3JldFJlcXVlc3QaMS5tZW1vcy5hcGkudjEuR2V0VXNlcldlYmhvb2tTaWduaW5nU2VjcmV0UmVzcG9uc2UiQdpBBG5hbWWC0+STAjQSMi9hcGkvdjEve25hbWU9dXNlcnMvKi93ZWJob29rcy8qfTpnZXRTaWduaW5nU2VjcmV0EqkBChVMaXN0VXNlck5vdGlmaWNhdGlvbnMSKi5tZW1vcy5hcGkudjEuTGlzdFVzZXJOb3RpZmljYXRpb25zUmVxdWVzdBorLm1lbW9zLmFwaS52MS5MaXN0VXNlck5vdGlmaWNhdGlvbnNSZXNwb25zZSI32kEGcGFyZW50gtPkkwIoEiYvYXBpL3YxL3twYXJlbnQ9dXNlcnMvKn0vbm90aWZpY2F0aW9ucxLLAQoWVXBkYXRlVXNlck5vdGlmaWNhdGlvbhIrLm1lbW9zLmFwaS52MS5VcGRhdGVVc2VyTm90aWZpY2F0aW9uUmVxdWVzdBoeLm1lbW9zLmFwaS52MS5Vc2VyTm90aWZpY2F0aW9uImTaQRhub3RpZmljYXRpb24sdXBkYXRlX21hc2uC0+STAkM6DG5vdGlmaWNhdGlvbjIzL2FwaS92MS97bm90aWZpY2F0aW9uLm5hbWU9dXNlcnMvKi9ub3RpZmljYXRpb25zLyp9EpQBChZEZWxldGVVc2VyTm90aWZpY2F0aW9uEisubWVtb3MuYXBpLnYxLkRlbGV0ZVVzZXJOb3RpZmljYXRpb25SZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IjXaQQRuYW1lgtPkkwIoKiYvYXBpL3YxL3tuYW1lPXVzZXJzLyovbm90aWZpY2F0aW9ucy8qfUKoAQoQY29tLm1lbW9zLmFwaS52MUIQVXNlclNlcnZpY2VQcm90b1ABWjBnaXRodWIuY29tL3VzZW1lbW9zL21lbW9zL3Byb3RvL2dlbi9hcGkvdjE7YXBpdjGiAgNNQViqAgxNZW1vcy5BcGkuVjHKAgxNZW1vc1xBcGlcVjHiAhhNZW1vc1xBcGlcVjFcR1BCTWV0YWRhdGHqAg5NZW1vczo6QXBpOjpWMWIGcHJvdG8z", [file_api_v1_common, file_google_api_annotations, file_google_api_client, file_google_api_field_behavior, file_google_api_resource, file_google_protobuf_empty, file_google_protobuf_field_mask, file_google_protobuf_timestamp, file_google_type_color]); /** * @generated from message memos.api.v1.User @@ -1287,11 +1287,11 @@ export type UserWebhook = Message<"memos.api.v1.UserWebhook"> & { signingSecret: string; /** - * Whether this webhook has a signing secret configured. + * Whether a signing secret is configured for this webhook. * - * @generated from field: bool has_signing_secret = 7; + * @generated from field: bool signing_secret_set = 7; */ - hasSigningSecret: boolean; + signingSecretSet: boolean; }; /** @@ -1413,6 +1413,45 @@ export type DeleteUserWebhookRequest = Message<"memos.api.v1.DeleteUserWebhookRe export const DeleteUserWebhookRequestSchema: GenMessage = /*@__PURE__*/ messageDesc(file_api_v1_user_service, 35); +/** + * @generated from message memos.api.v1.GetUserWebhookSigningSecretRequest + */ +export type GetUserWebhookSigningSecretRequest = Message<"memos.api.v1.GetUserWebhookSigningSecretRequest"> & { + /** + * The name of the webhook whose signing secret to reveal. + * Format: users/{user}/webhooks/{webhook} + * + * @generated from field: string name = 1; + */ + name: string; +}; + +/** + * Describes the message memos.api.v1.GetUserWebhookSigningSecretRequest. + * Use `create(GetUserWebhookSigningSecretRequestSchema)` to create a new message. + */ +export const GetUserWebhookSigningSecretRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_api_v1_user_service, 36); + +/** + * @generated from message memos.api.v1.GetUserWebhookSigningSecretResponse + */ +export type GetUserWebhookSigningSecretResponse = Message<"memos.api.v1.GetUserWebhookSigningSecretResponse"> & { + /** + * The signing secret, in the Standard Webhooks "whsec_" form. + * + * @generated from field: string signing_secret = 1; + */ + signingSecret: string; +}; + +/** + * Describes the message memos.api.v1.GetUserWebhookSigningSecretResponse. + * Use `create(GetUserWebhookSigningSecretResponseSchema)` to create a new message. + */ +export const GetUserWebhookSigningSecretResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_api_v1_user_service, 37); + /** * @generated from message memos.api.v1.UserNotification */ @@ -1484,7 +1523,7 @@ export type UserNotification = Message<"memos.api.v1.UserNotification"> & { * Use `create(UserNotificationSchema)` to create a new message. */ export const UserNotificationSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_api_v1_user_service, 36); + messageDesc(file_api_v1_user_service, 38); /** * @generated from message memos.api.v1.UserNotification.MemoCommentPayload @@ -1526,7 +1565,7 @@ export type UserNotification_MemoCommentPayload = Message<"memos.api.v1.UserNoti * Use `create(UserNotification_MemoCommentPayloadSchema)` to create a new message. */ export const UserNotification_MemoCommentPayloadSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_api_v1_user_service, 36, 0); + messageDesc(file_api_v1_user_service, 38, 0); /** * @generated from message memos.api.v1.UserNotification.MemoMentionPayload @@ -1568,7 +1607,7 @@ export type UserNotification_MemoMentionPayload = Message<"memos.api.v1.UserNoti * Use `create(UserNotification_MemoMentionPayloadSchema)` to create a new message. */ export const UserNotification_MemoMentionPayloadSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_api_v1_user_service, 36, 1); + messageDesc(file_api_v1_user_service, 38, 1); /** * @generated from enum memos.api.v1.UserNotification.Status @@ -1594,7 +1633,7 @@ export enum UserNotification_Status { * Describes the enum memos.api.v1.UserNotification.Status. */ export const UserNotification_StatusSchema: GenEnum = /*@__PURE__*/ - enumDesc(file_api_v1_user_service, 36, 0); + enumDesc(file_api_v1_user_service, 38, 0); /** * @generated from enum memos.api.v1.UserNotification.Type @@ -1620,7 +1659,7 @@ export enum UserNotification_Type { * Describes the enum memos.api.v1.UserNotification.Type. */ export const UserNotification_TypeSchema: GenEnum = /*@__PURE__*/ - enumDesc(file_api_v1_user_service, 36, 1); + enumDesc(file_api_v1_user_service, 38, 1); /** * @generated from message memos.api.v1.ListUserNotificationsRequest @@ -1655,7 +1694,7 @@ export type ListUserNotificationsRequest = Message<"memos.api.v1.ListUserNotific * Use `create(ListUserNotificationsRequestSchema)` to create a new message. */ export const ListUserNotificationsRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_api_v1_user_service, 37); + messageDesc(file_api_v1_user_service, 39); /** * @generated from message memos.api.v1.ListUserNotificationsResponse @@ -1677,7 +1716,7 @@ export type ListUserNotificationsResponse = Message<"memos.api.v1.ListUserNotifi * Use `create(ListUserNotificationsResponseSchema)` to create a new message. */ export const ListUserNotificationsResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_api_v1_user_service, 38); + messageDesc(file_api_v1_user_service, 40); /** * @generated from message memos.api.v1.UpdateUserNotificationRequest @@ -1699,7 +1738,7 @@ export type UpdateUserNotificationRequest = Message<"memos.api.v1.UpdateUserNoti * Use `create(UpdateUserNotificationRequestSchema)` to create a new message. */ export const UpdateUserNotificationRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_api_v1_user_service, 39); + messageDesc(file_api_v1_user_service, 41); /** * @generated from message memos.api.v1.DeleteUserNotificationRequest @@ -1718,7 +1757,7 @@ export type DeleteUserNotificationRequest = Message<"memos.api.v1.DeleteUserNoti * Use `create(DeleteUserNotificationRequestSchema)` to create a new message. */ export const DeleteUserNotificationRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_api_v1_user_service, 40); + messageDesc(file_api_v1_user_service, 42); /** * @generated from service memos.api.v1.UserService @@ -1947,6 +1986,18 @@ export const UserService: GenService<{ input: typeof DeleteUserWebhookRequestSchema; output: typeof EmptySchema; }, + /** + * GetUserWebhookSigningSecret returns the signing secret for a webhook. + * The secret is returned only through this explicit, owner-gated call; it is + * never included in List/Create/Update responses. + * + * @generated from rpc memos.api.v1.UserService.GetUserWebhookSigningSecret + */ + getUserWebhookSigningSecret: { + methodKind: "unary"; + input: typeof GetUserWebhookSigningSecretRequestSchema; + output: typeof GetUserWebhookSigningSecretResponseSchema; + }, /** * ListUserNotifications lists notifications for a user. *