chore(webhook): reveal-later signing secret flow

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.
pull/6051/head
boojack 2 weeks ago
parent c6d65bcf3f
commit eb826455b6

@ -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_<base64>" form, backed by 32 cryptographically-random bytes — comfortably
// within the spec's 2464 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)

@ -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

@ -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_<base64>" form.
string signing_secret = 1;
}
message UserNotification {
option (google.api.resource) = {
type: "memos.api.v1/UserNotification"

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

@ -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_<base64>" 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,
},

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

@ -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,

@ -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_<base64>" 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

@ -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 {

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

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

@ -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<State>({
displayName: "",
url: "",
signingSecret: isCreating ? "" : undefined,
});
const [state, setState] = useState<State>({ displayName: "", url: "" });
const requestState = useLoading(false);
const secretState = useLoading(false);
const [hasExistingSecret, setHasExistingSecret] = useState(false);
const [revealedSecret, setRevealedSecret] = useState<string | undefined>(undefined);
const [createdSecret, setCreatedSecret] = useState<string | undefined>(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<State>) => {
setState({
...state,
...partialState,
});
};
const handleTitleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setPartialState({
displayName: e.target.value,
});
setState((prev) => ({ ...prev, displayName: e.target.value }));
};
const handleUrlInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
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() ?? "";
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");
// 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() });
}
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");
};
// 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?.();
}
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) => (
<div className="flex items-center gap-2">
<Input readOnly type="text" value={secret} className="flex-1 font-mono text-xs" />
<Button
type="button"
variant="outline"
size="icon"
onClick={() => handleCopySecret(secret)}
aria-label={t("setting.webhook.create-dialog.copy-secret")}
>
{secretCopied ? <CheckIcon className="h-4 w-4 text-success" /> : <CopyIcon className="h-4 w-4" />}
</Button>
</div>
);
const renderSigningSecret = () => {
if (isCreating) {
return <span className="text-xs text-muted-foreground">{t("setting.webhook.create-dialog.signing-secret-auto-note")}</span>;
}
if (revealedSecret !== undefined) {
return renderSecretField(revealedSecret);
}
if (hasExistingSecret) {
return (
<div className="flex items-center gap-2">
<span className="flex-1 text-xs text-muted-foreground">{t("setting.webhook.create-dialog.signing-secret-configured")}</span>
<Button type="button" variant="outline" size="sm" onClick={handleRevealSecret} disabled={secretState.isLoading}>
<EyeIcon className="mr-1 h-3.5 w-3.5" />
{t("setting.webhook.create-dialog.reveal-secret")}
</Button>
</div>
);
}
return (
<div className="flex items-center gap-2">
<span className="flex-1 text-xs text-muted-foreground">{t("setting.webhook.create-dialog.signing-secret-not-configured")}</span>
<Button type="button" variant="outline" size="sm" onClick={handleGenerateSecret} disabled={secretState.isLoading}>
{t("setting.webhook.create-dialog.generate-secret")}
</Button>
</div>
);
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<Dialog open={open} onOpenChange={handleOpenChange}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle>
{isCreating ? t("setting.webhook.create-dialog.create-webhook") : t("setting.webhook.create-dialog.edit-webhook")}
</DialogTitle>
</DialogHeader>
<div className="flex flex-col gap-4">
<div className="grid gap-2">
<Label htmlFor="displayName">
{t("setting.webhook.create-dialog.title")} <span className="text-destructive">*</span>
</Label>
<Input
id="displayName"
type="text"
placeholder={t("setting.webhook.create-dialog.an-easy-to-remember-name")}
value={state.displayName}
onChange={handleTitleInputChange}
/>
</div>
{createdSecret !== undefined ? (
<div className="grid gap-2">
<Label htmlFor="url">
{t("setting.webhook.create-dialog.payload-url")} <span className="text-destructive">*</span>
</Label>
<Input
id="url"
type="text"
placeholder={t("setting.webhook.create-dialog.url-example-post-receive")}
value={state.url}
onChange={handleUrlInputChange}
/>
<Label>{t("setting.webhook.create-dialog.signing-secret")}</Label>
<span className="text-xs text-muted-foreground">{t("setting.webhook.create-dialog.signing-secret-created-note")}</span>
{renderSecretField(createdSecret)}
</div>
<div className="grid gap-2">
<div className="flex items-center gap-2">
<Label>{t("setting.webhook.create-dialog.signing-secret")}</Label>
<span className="text-xs text-muted-foreground">
{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")}
</span>
) : (
<div className="flex flex-col gap-4">
<div className="grid gap-2">
<Label htmlFor="displayName">
{t("setting.webhook.create-dialog.title")} <span className="text-destructive">*</span>
</Label>
<Input
id="displayName"
type="text"
placeholder={t("setting.webhook.create-dialog.an-easy-to-remember-name")}
value={state.displayName}
onChange={handleTitleInputChange}
/>
</div>
<div className="flex items-center gap-2">
<Button
type="button"
variant="outline"
size="sm"
onClick={handleGenerateAndCopy}
aria-label={t("setting.webhook.create-dialog.generate-and-copy-secret")}
>
{secretCopied ? (
<>
<CheckIcon className="mr-1 h-3.5 w-3.5 text-success" />
{t("setting.webhook.create-dialog.copied")}
</>
) : (
<>
<CopyIcon className="mr-1 h-3.5 w-3.5" />
{t("setting.webhook.create-dialog.generate-and-copy-secret")}
</>
)}
</Button>
<Button
type="button"
variant="outline"
size="sm"
onClick={handleClearSecret}
disabled={!state.signingSecret && !hasExistingSecret}
aria-label={t("setting.webhook.create-dialog.clear-secret")}
>
<Trash2Icon className="mr-1 h-3.5 w-3.5" />
{t("setting.webhook.create-dialog.clear-secret")}
</Button>
<span className="text-xs text-muted-foreground">{getPendingLabel()}</span>
<div className="grid gap-2">
<Label htmlFor="url">
{t("setting.webhook.create-dialog.payload-url")} <span className="text-destructive">*</span>
</Label>
<Input
id="url"
type="text"
placeholder={t("setting.webhook.create-dialog.url-example-post-receive")}
value={state.url}
onChange={handleUrlInputChange}
/>
</div>
<div className="grid gap-2">
<Label>{t("setting.webhook.create-dialog.signing-secret")}</Label>
{renderSigningSecret()}
</div>
</div>
</div>
)}
<DialogFooter>
<Button variant="ghost" disabled={requestState.isLoading} onClick={() => onOpenChange(false)}>
{t("common.cancel")}
</Button>
<Button disabled={requestState.isLoading} onClick={handleSaveBtnClick}>
{isCreating ? t("common.create") : t("common.save")}
</Button>
{createdSecret !== undefined ? (
<Button onClick={() => handleOpenChange(false)}>{t("common.close")}</Button>
) : (
<>
<Button variant="ghost" disabled={requestState.isLoading} onClick={() => onOpenChange(false)}>
{t("common.cancel")}
</Button>
<Button disabled={requestState.isLoading} onClick={handleSaveBtnClick}>
{isCreating ? t("common.create") : t("common.save")}
</Button>
</>
)}
</DialogFooter>
</DialogContent>
</Dialog>

@ -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"
},

@ -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"
},

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save