chore: implement webhook store

This commit is contained in:
Steven 2023-11-24 22:45:38 +08:00
parent 436eb0e591
commit 1a5bce49c2
15 changed files with 760 additions and 52 deletions

View File

@ -9,6 +9,8 @@
- [ActivityVersionUpdatePayload](#memos-store-ActivityVersionUpdatePayload)
- [store/common.proto](#store_common-proto)
- [RowStatus](#memos-store-RowStatus)
- [store/inbox.proto](#store_inbox-proto)
- [InboxMessage](#memos-store-InboxMessage)
@ -26,6 +28,9 @@
- [UserSettingKey](#memos-store-UserSettingKey)
- [store/webhook.proto](#store_webhook-proto)
- [Webhook](#memos-store-Webhook)
- [Scalar Value Types](#scalar-value-types)
@ -101,6 +106,19 @@
<a name="memos-store-RowStatus"></a>
### RowStatus
| Name | Number | Description |
| ---- | ------ | ----------- |
| ROW_STATUS_UNSPECIFIED | 0 | |
| NORMAL | 1 | |
| ARCHIVED | 2 | |
@ -275,6 +293,43 @@
<a name="store_webhook-proto"></a>
<p align="right"><a href="#top">Top</a></p>
## store/webhook.proto
<a name="memos-store-Webhook"></a>
### Webhook
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| id | [int32](#int32) | | |
| created_ts | [int64](#int64) | | |
| updated_ts | [int64](#int64) | | |
| creator_id | [int32](#int32) | | |
| row_status | [RowStatus](#memos-store-RowStatus) | | |
| name | [string](#string) | | |
| url | [string](#string) | | |
## Scalar Value Types
| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby |

View File

@ -10,6 +10,7 @@ import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
@ -19,25 +20,93 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type RowStatus int32
const (
RowStatus_ROW_STATUS_UNSPECIFIED RowStatus = 0
RowStatus_NORMAL RowStatus = 1
RowStatus_ARCHIVED RowStatus = 2
)
// Enum value maps for RowStatus.
var (
RowStatus_name = map[int32]string{
0: "ROW_STATUS_UNSPECIFIED",
1: "NORMAL",
2: "ARCHIVED",
}
RowStatus_value = map[string]int32{
"ROW_STATUS_UNSPECIFIED": 0,
"NORMAL": 1,
"ARCHIVED": 2,
}
)
func (x RowStatus) Enum() *RowStatus {
p := new(RowStatus)
*p = x
return p
}
func (x RowStatus) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (RowStatus) Descriptor() protoreflect.EnumDescriptor {
return file_store_common_proto_enumTypes[0].Descriptor()
}
func (RowStatus) Type() protoreflect.EnumType {
return &file_store_common_proto_enumTypes[0]
}
func (x RowStatus) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use RowStatus.Descriptor instead.
func (RowStatus) EnumDescriptor() ([]byte, []int) {
return file_store_common_proto_rawDescGZIP(), []int{0}
}
var File_store_common_proto protoreflect.FileDescriptor
var file_store_common_proto_rawDesc = []byte{
0x0a, 0x12, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72,
0x65, 0x42, 0x96, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e,
0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x50, 0x72, 0x6f,
0x74, 0x6f, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x75, 0x73, 0x65, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0xa2,
0x02, 0x03, 0x4d, 0x53, 0x58, 0xaa, 0x02, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x53, 0x74,
0x6f, 0x72, 0x65, 0xca, 0x02, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x53, 0x74, 0x6f, 0x72,
0x65, 0xe2, 0x02, 0x17, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x5c,
0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x4d, 0x65,
0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
0x65, 0x2a, 0x41, 0x0a, 0x09, 0x52, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a,
0x0a, 0x16, 0x52, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53,
0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f,
0x52, 0x4d, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x52, 0x43, 0x48, 0x49, 0x56,
0x45, 0x44, 0x10, 0x02, 0x42, 0x96, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d,
0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x73, 0x65, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x65, 0x6d,
0x6f, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x73, 0x74, 0x6f,
0x72, 0x65, 0xa2, 0x02, 0x03, 0x4d, 0x53, 0x58, 0xaa, 0x02, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x73,
0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xca, 0x02, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x53,
0x74, 0x6f, 0x72, 0x65, 0xe2, 0x02, 0x17, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02,
0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var file_store_common_proto_goTypes = []interface{}{}
var (
file_store_common_proto_rawDescOnce sync.Once
file_store_common_proto_rawDescData = file_store_common_proto_rawDesc
)
func file_store_common_proto_rawDescGZIP() []byte {
file_store_common_proto_rawDescOnce.Do(func() {
file_store_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_store_common_proto_rawDescData)
})
return file_store_common_proto_rawDescData
}
var file_store_common_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_store_common_proto_goTypes = []interface{}{
(RowStatus)(0), // 0: memos.store.RowStatus
}
var file_store_common_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
@ -56,13 +125,14 @@ func file_store_common_proto_init() {
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_store_common_proto_rawDesc,
NumEnums: 0,
NumEnums: 1,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_store_common_proto_goTypes,
DependencyIndexes: file_store_common_proto_depIdxs,
EnumInfos: file_store_common_proto_enumTypes,
}.Build()
File_store_common_proto = out.File
file_store_common_proto_rawDesc = nil

View File

@ -0,0 +1,214 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.31.0
// protoc (unknown)
// source: store/webhook.proto
package store
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Webhook struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
CreatedTs int64 `protobuf:"varint,2,opt,name=created_ts,json=createdTs,proto3" json:"created_ts,omitempty"`
UpdatedTs int64 `protobuf:"varint,3,opt,name=updated_ts,json=updatedTs,proto3" json:"updated_ts,omitempty"`
CreatorId int32 `protobuf:"varint,4,opt,name=creator_id,json=creatorId,proto3" json:"creator_id,omitempty"`
RowStatus RowStatus `protobuf:"varint,5,opt,name=row_status,json=rowStatus,proto3,enum=memos.store.RowStatus" json:"row_status,omitempty"`
Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"`
Url string `protobuf:"bytes,7,opt,name=url,proto3" json:"url,omitempty"`
}
func (x *Webhook) Reset() {
*x = Webhook{}
if protoimpl.UnsafeEnabled {
mi := &file_store_webhook_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Webhook) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Webhook) ProtoMessage() {}
func (x *Webhook) ProtoReflect() protoreflect.Message {
mi := &file_store_webhook_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Webhook.ProtoReflect.Descriptor instead.
func (*Webhook) Descriptor() ([]byte, []int) {
return file_store_webhook_proto_rawDescGZIP(), []int{0}
}
func (x *Webhook) GetId() int32 {
if x != nil {
return x.Id
}
return 0
}
func (x *Webhook) GetCreatedTs() int64 {
if x != nil {
return x.CreatedTs
}
return 0
}
func (x *Webhook) GetUpdatedTs() int64 {
if x != nil {
return x.UpdatedTs
}
return 0
}
func (x *Webhook) GetCreatorId() int32 {
if x != nil {
return x.CreatorId
}
return 0
}
func (x *Webhook) GetRowStatus() RowStatus {
if x != nil {
return x.RowStatus
}
return RowStatus_ROW_STATUS_UNSPECIFIED
}
func (x *Webhook) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Webhook) GetUrl() string {
if x != nil {
return x.Url
}
return ""
}
var File_store_webhook_proto protoreflect.FileDescriptor
var file_store_webhook_proto_rawDesc = []byte{
0x0a, 0x13, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f,
0x72, 0x65, 0x1a, 0x12, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd3, 0x01, 0x0a, 0x07, 0x57, 0x65, 0x62, 0x68, 0x6f,
0x6f, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02,
0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x73,
0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x54,
0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x73, 0x18,
0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x54, 0x73,
0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04,
0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x12,
0x35, 0x0a, 0x0a, 0x72, 0x6f, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20,
0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72,
0x65, 0x2e, 0x52, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x09, 0x72, 0x6f, 0x77,
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72,
0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x42, 0x97, 0x01, 0x0a,
0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65,
0x42, 0x0c, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x73, 0x65,
0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0xa2, 0x02, 0x03, 0x4d, 0x53,
0x58, 0xaa, 0x02, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xca,
0x02, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xe2, 0x02, 0x17,
0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d,
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x3a,
0x3a, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_store_webhook_proto_rawDescOnce sync.Once
file_store_webhook_proto_rawDescData = file_store_webhook_proto_rawDesc
)
func file_store_webhook_proto_rawDescGZIP() []byte {
file_store_webhook_proto_rawDescOnce.Do(func() {
file_store_webhook_proto_rawDescData = protoimpl.X.CompressGZIP(file_store_webhook_proto_rawDescData)
})
return file_store_webhook_proto_rawDescData
}
var file_store_webhook_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_store_webhook_proto_goTypes = []interface{}{
(*Webhook)(nil), // 0: memos.store.Webhook
(RowStatus)(0), // 1: memos.store.RowStatus
}
var file_store_webhook_proto_depIdxs = []int32{
1, // 0: memos.store.Webhook.row_status:type_name -> memos.store.RowStatus
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_store_webhook_proto_init() }
func file_store_webhook_proto_init() {
if File_store_webhook_proto != nil {
return
}
file_store_common_proto_init()
if !protoimpl.UnsafeEnabled {
file_store_webhook_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Webhook); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_store_webhook_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_store_webhook_proto_goTypes,
DependencyIndexes: file_store_webhook_proto_depIdxs,
MessageInfos: file_store_webhook_proto_msgTypes,
}.Build()
File_store_webhook_proto = out.File
file_store_webhook_proto_rawDesc = nil
file_store_webhook_proto_goTypes = nil
file_store_webhook_proto_depIdxs = nil
}

View File

@ -3,3 +3,11 @@ syntax = "proto3";
package memos.store;
option go_package = "gen/store";
enum RowStatus {
ROW_STATUS_UNSPECIFIED = 0;
NORMAL = 1;
ARCHIVED = 2;
}

23
proto/store/webhook.proto Normal file
View File

@ -0,0 +1,23 @@
syntax = "proto3";
package memos.store;
import "store/common.proto";
option go_package = "gen/store";
message Webhook {
int32 id = 1;
int64 created_ts = 2;
int64 updated_ts = 3;
int32 creator_id = 4;
RowStatus row_status = 5;
string name = 6;
string url = 7;
}

View File

@ -133,3 +133,14 @@ CREATE TABLE `inbox` (
`status` TEXT NOT NULL,
`message` TEXT NOT NULL
);
-- webhook
CREATE TABLE `webhook` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`row_status` TEXT NOT NULL DEFAULT 'NORMAL',
`creator_id` INT NOT NULL,
`name` TEXT NOT NULL,
`url` TEXT NOT NULL
);

View File

@ -68,18 +68,6 @@ func (d *DB) ListStorages(ctx context.Context, find *store.FindStorage) ([]*stor
return list, nil
}
func (d *DB) GetStorage(ctx context.Context, find *store.FindStorage) (*store.Storage, error) {
list, err := d.ListStorages(ctx, find)
if err != nil {
return nil, err
}
if len(list) == 0 {
return nil, nil
}
return list[0], nil
}
func (d *DB) UpdateStorage(ctx context.Context, update *store.UpdateStorage) (*store.Storage, error) {
set, args := []string{}, []any{}
if update.Name != nil {

121
store/db/mysql/webhook.go Normal file
View File

@ -0,0 +1,121 @@
package mysql
import (
"context"
"strings"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store"
)
func (d *DB) CreateWebhook(ctx context.Context, create *storepb.Webhook) (*storepb.Webhook, error) {
fields := []string{"`name`", "`url`", "`creator_id`"}
placeholder := []string{"?", "?", "?"}
args := []any{create.Name, create.Url, create.CreatorId}
if create.Id != 0 {
fields = append(fields, "`id`")
placeholder = append(placeholder, "?")
args = append(args, create.Id)
}
stmt := "INSERT INTO `webhook` (" + strings.Join(fields, ", ") + ") VALUES (" + strings.Join(placeholder, ", ") + ")"
result, err := d.db.ExecContext(ctx, stmt, args...)
if err != nil {
return nil, err
}
id, err := result.LastInsertId()
if err != nil {
return nil, err
}
create.Id = int32(id)
return create, nil
}
func (d *DB) ListWebhooks(ctx context.Context, find *store.FindWebhook) ([]*storepb.Webhook, error) {
where, args := []string{"1 = 1"}, []any{}
if find.ID != nil {
where, args = append(where, "`id` = ?"), append(args, *find.ID)
}
if find.CreatorID != nil {
where, args = append(where, "`creator_id` = ?"), append(args, *find.CreatorID)
}
rows, err := d.db.QueryContext(ctx, "SELECT `id`, `created_ts`, `updated_ts`, `row_status`, `creator_id`, `name`, `url` FROM `webhook` WHERE "+strings.Join(where, " AND ")+" ORDER BY `id` DESC",
args...,
)
if err != nil {
return nil, err
}
defer rows.Close()
list := []*storepb.Webhook{}
for rows.Next() {
webhook := &storepb.Webhook{}
var rowStatus string
if err := rows.Scan(
&webhook.Id,
&webhook.CreatedTs,
&webhook.UpdatedTs,
&rowStatus,
&webhook.CreatorId,
&webhook.Name,
&webhook.Url,
); err != nil {
return nil, err
}
webhook.RowStatus = storepb.RowStatus(storepb.RowStatus_value[rowStatus])
list = append(list, webhook)
}
if err := rows.Err(); err != nil {
return nil, err
}
return list, nil
}
func (d *DB) GetWebhook(ctx context.Context, find *store.FindWebhook) (*storepb.Webhook, error) {
list, err := d.ListWebhooks(ctx, find)
if err != nil {
return nil, err
}
if len(list) == 0 {
return nil, nil
}
return list[0], nil
}
func (d *DB) UpdateWebhook(ctx context.Context, update *store.UpdateWebhook) (*storepb.Webhook, error) {
set, args := []string{}, []any{}
if update.RowStatus != nil {
set, args = append(set, "`row_status` = ?"), append(args, update.RowStatus.String())
}
if update.Name != nil {
set, args = append(set, "`name` = ?"), append(args, *update.Name)
}
if update.URL != nil {
set, args = append(set, "`url` = ?"), append(args, *update.URL)
}
args = append(args, update.ID)
stmt := "UPDATE `webhook` SET " + strings.Join(set, ", ") + " WHERE `id` = ?"
_, err := d.db.ExecContext(ctx, stmt, args...)
if err != nil {
return nil, err
}
webhook, err := d.GetWebhook(ctx, &store.FindWebhook{ID: &update.ID})
if err != nil {
return nil, err
}
return webhook, nil
}
func (d *DB) DeleteWebhook(ctx context.Context, delete *store.DeleteWebhook) error {
_, err := d.db.ExecContext(ctx, "DELETE FROM `webhook` WHERE `id` = ?", delete.ID)
return err
}

View File

@ -97,19 +97,6 @@ func (d *DB) ListIdentityProviders(ctx context.Context, find *store.FindIdentity
return identityProviders, nil
}
func (d *DB) GetIdentityProvider(ctx context.Context, find *store.FindIdentityProvider) (*store.IdentityProvider, error) {
list, err := d.ListIdentityProviders(ctx, find)
if err != nil {
return nil, err
}
if len(list) == 0 {
return nil, nil
}
identityProvider := list[0]
return identityProvider, nil
}
func (d *DB) UpdateIdentityProvider(ctx context.Context, update *store.UpdateIdentityProvider) (*store.IdentityProvider, error) {
set, args := []string{}, []any{}
if v := update.Name; v != nil {

View File

@ -144,3 +144,16 @@ CREATE TABLE inbox (
status TEXT NOT NULL,
message TEXT NOT NULL DEFAULT '{}'
);
-- webhook
CREATE TABLE webhook (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
creator_id INTEGER NOT NULL,
name TEXT NOT NULL,
url TEXT NOT NULL
);
CREATE INDEX idx_webhook_creator_id ON webhook (creator_id);

View File

@ -72,18 +72,6 @@ func (d *DB) ListStorages(ctx context.Context, find *store.FindStorage) ([]*stor
return list, nil
}
func (d *DB) GetStorage(ctx context.Context, find *store.FindStorage) (*store.Storage, error) {
list, err := d.ListStorages(ctx, find)
if err != nil {
return nil, err
}
if len(list) == 0 {
return nil, nil
}
return list[0], nil
}
func (d *DB) UpdateStorage(ctx context.Context, update *store.UpdateStorage) (*store.Storage, error) {
set, args := []string{}, []any{}
if update.Name != nil {

126
store/db/sqlite/webhook.go Normal file
View File

@ -0,0 +1,126 @@
package sqlite
import (
"context"
"strings"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store"
)
func (d *DB) CreateWebhook(ctx context.Context, create *storepb.Webhook) (*storepb.Webhook, error) {
fields := []string{"`name`", "`url`", "`creator_id`"}
placeholder := []string{"?", "?", "?"}
args := []any{create.Name, create.Url, create.CreatorId}
if create.Id != 0 {
fields = append(fields, "`id`")
placeholder = append(placeholder, "?")
args = append(args, create.Id)
}
stmt := "INSERT INTO `webhook` (" + strings.Join(fields, ", ") + ") VALUES (" + strings.Join(placeholder, ", ") + ") RETURNING `id`, `created_ts`, `updated_ts`, `row_status`"
var rowStatus string
if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(
&create.Id,
&create.CreatedTs,
&create.UpdatedTs,
&rowStatus,
); err != nil {
return nil, err
}
create.RowStatus = storepb.RowStatus(storepb.RowStatus_value[rowStatus])
webhook := create
return webhook, nil
}
func (d *DB) ListWebhooks(ctx context.Context, find *store.FindWebhook) ([]*storepb.Webhook, error) {
where, args := []string{"1 = 1"}, []any{}
if find.ID != nil {
where, args = append(where, "id = ?"), append(args, *find.ID)
}
if find.CreatorID != nil {
where, args = append(where, "creator_id = ?"), append(args, *find.CreatorID)
}
rows, err := d.db.QueryContext(ctx, `
SELECT
id,
created_ts,
updated_ts,
row_status,
creator_id,
name,
url
FROM webhook
WHERE `+strings.Join(where, " AND ")+`
ORDER BY id DESC`,
args...,
)
if err != nil {
return nil, err
}
defer rows.Close()
list := []*storepb.Webhook{}
for rows.Next() {
webhook := &storepb.Webhook{}
var rowStatus string
if err := rows.Scan(
&webhook.Id,
&webhook.CreatedTs,
&webhook.UpdatedTs,
&rowStatus,
&webhook.CreatorId,
&webhook.Name,
&webhook.Url,
); err != nil {
return nil, err
}
webhook.RowStatus = storepb.RowStatus(storepb.RowStatus_value[rowStatus])
list = append(list, webhook)
}
if err := rows.Err(); err != nil {
return nil, err
}
return list, nil
}
func (d *DB) UpdateWebhook(ctx context.Context, update *store.UpdateWebhook) (*storepb.Webhook, error) {
set, args := []string{}, []any{}
if update.RowStatus != nil {
set, args = append(set, "row_status = ?"), append(args, update.RowStatus.String())
}
if update.Name != nil {
set, args = append(set, "name = ?"), append(args, *update.Name)
}
if update.URL != nil {
set, args = append(set, "url = ?"), append(args, *update.URL)
}
args = append(args, update.ID)
stmt := "UPDATE `webhook` SET " + strings.Join(set, ", ") + " WHERE `id` = ? RETURNING `id`, `created_ts`, `updated_ts`, `row_status`, `creator_id`, `name`, `url`"
webhook := &storepb.Webhook{}
var rowStatus string
if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(
&webhook.Id,
&webhook.CreatedTs,
&webhook.UpdatedTs,
&rowStatus,
&webhook.CreatorId,
&webhook.Name,
&webhook.Url,
); err != nil {
return nil, err
}
webhook.RowStatus = storepb.RowStatus(storepb.RowStatus_value[rowStatus])
return webhook, nil
}
func (d *DB) DeleteWebhook(ctx context.Context, delete *store.DeleteWebhook) error {
_, err := d.db.ExecContext(ctx, "DELETE FROM `webhook` WHERE `id` = ?", delete.ID)
return err
}

View File

@ -70,7 +70,6 @@ type Driver interface {
// IdentityProvider model related methods.
CreateIdentityProvider(ctx context.Context, create *IdentityProvider) (*IdentityProvider, error)
ListIdentityProviders(ctx context.Context, find *FindIdentityProvider) ([]*IdentityProvider, error)
GetIdentityProvider(ctx context.Context, find *FindIdentityProvider) (*IdentityProvider, error)
UpdateIdentityProvider(ctx context.Context, update *UpdateIdentityProvider) (*IdentityProvider, error)
DeleteIdentityProvider(ctx context.Context, delete *DeleteIdentityProvider) error
@ -82,7 +81,6 @@ type Driver interface {
// Storage model related methods.
CreateStorage(ctx context.Context, create *Storage) (*Storage, error)
ListStorages(ctx context.Context, find *FindStorage) ([]*Storage, error)
GetStorage(ctx context.Context, find *FindStorage) (*Storage, error)
UpdateStorage(ctx context.Context, update *UpdateStorage) (*Storage, error)
DeleteStorage(ctx context.Context, delete *DeleteStorage) error
@ -91,4 +89,10 @@ type Driver interface {
ListInboxes(ctx context.Context, find *FindInbox) ([]*Inbox, error)
UpdateInbox(ctx context.Context, update *UpdateInbox) (*Inbox, error)
DeleteInbox(ctx context.Context, delete *DeleteInbox) error
// Webhook model related methods.
CreateWebhook(ctx context.Context, create *storepb.Webhook) (*storepb.Webhook, error)
ListWebhooks(ctx context.Context, find *FindWebhook) ([]*storepb.Webhook, error)
UpdateWebhook(ctx context.Context, update *UpdateWebhook) (*storepb.Webhook, error)
DeleteWebhook(ctx context.Context, delete *DeleteWebhook) error
}

50
store/webhook.go Normal file
View File

@ -0,0 +1,50 @@
package store
import (
"context"
storepb "github.com/usememos/memos/proto/gen/store"
)
type FindWebhook struct {
ID *int32
CreatorID *int32
}
type UpdateWebhook struct {
ID int32
RowStatus *storepb.RowStatus
Name *string
URL *string
}
type DeleteWebhook struct {
ID int32
}
func (s *Store) CreateWebhook(ctx context.Context, create *storepb.Webhook) (*storepb.Webhook, error) {
return s.driver.CreateWebhook(ctx, create)
}
func (s *Store) ListWebhooks(ctx context.Context, find *FindWebhook) ([]*storepb.Webhook, error) {
return s.driver.ListWebhooks(ctx, find)
}
func (s *Store) GetWebhooks(ctx context.Context, find *FindWebhook) (*storepb.Webhook, error) {
list, err := s.ListWebhooks(ctx, find)
if err != nil {
return nil, err
}
if len(list) == 0 {
return nil, nil
}
return list[0], nil
}
func (s *Store) UpdateWebhook(ctx context.Context, update *UpdateWebhook) (*storepb.Webhook, error) {
return s.driver.UpdateWebhook(ctx, update)
}
func (s *Store) DeleteWebhook(ctx context.Context, delete *DeleteWebhook) error {
return s.driver.DeleteWebhook(ctx, delete)
}

View File

@ -0,0 +1,50 @@
package teststore
import (
"context"
"testing"
"github.com/stretchr/testify/require"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store"
)
func TestWebhookStore(t *testing.T) {
ctx := context.Background()
ts := NewTestingStore(ctx, t)
user, err := createTestingHostUser(ctx, ts)
require.NoError(t, err)
webhook, err := ts.CreateWebhook(ctx, &storepb.Webhook{
CreatorId: user.ID,
Name: "test_webhook",
Url: "https://example.com",
RowStatus: storepb.RowStatus_NORMAL,
})
require.NoError(t, err)
require.Equal(t, "test_webhook", webhook.Name)
require.Equal(t, user.ID, webhook.CreatorId)
webhooks, err := ts.ListWebhooks(ctx, &store.FindWebhook{
CreatorID: &user.ID,
})
require.NoError(t, err)
require.Equal(t, 1, len(webhooks))
require.Equal(t, webhook, webhooks[0])
newName := "test_webhook_new"
updatedWebhook, err := ts.UpdateWebhook(ctx, &store.UpdateWebhook{
ID: webhook.Id,
Name: &newName,
})
require.NoError(t, err)
require.Equal(t, newName, updatedWebhook.Name)
require.Equal(t, webhook.CreatorId, updatedWebhook.CreatorId)
err = ts.DeleteWebhook(ctx, &store.DeleteWebhook{
ID: webhook.Id,
})
require.NoError(t, err)
webhooks, err = ts.ListWebhooks(ctx, &store.FindWebhook{
CreatorID: &user.ID,
})
require.NoError(t, err)
require.Equal(t, 0, len(webhooks))
}