diff --git a/api/v1/resource.go b/api/v1/resource.go index 7c8b1adc..f09e2bd8 100644 --- a/api/v1/resource.go +++ b/api/v1/resource.go @@ -42,9 +42,6 @@ type Resource struct { ExternalLink string `json:"externalLink"` Type string `json:"type"` Size int64 `json:"size"` - - // Related fields - LinkedMemoAmount int `json:"linkedMemoAmount"` } type CreateResourceRequest struct { @@ -587,17 +584,16 @@ func checkResourceVisibility(ctx context.Context, s *store.Store, resourceID int func convertResourceFromStore(resource *store.Resource) *Resource { return &Resource{ - ID: resource.ID, - CreatorID: resource.CreatorID, - CreatedTs: resource.CreatedTs, - UpdatedTs: resource.UpdatedTs, - Filename: resource.Filename, - Blob: resource.Blob, - InternalPath: resource.InternalPath, - ExternalLink: resource.ExternalLink, - Type: resource.Type, - Size: resource.Size, - LinkedMemoAmount: resource.LinkedMemoAmount, + ID: resource.ID, + CreatorID: resource.CreatorID, + CreatedTs: resource.CreatedTs, + UpdatedTs: resource.UpdatedTs, + Filename: resource.Filename, + Blob: resource.Blob, + InternalPath: resource.InternalPath, + ExternalLink: resource.ExternalLink, + Type: resource.Type, + Size: resource.Size, } } diff --git a/api/v2/resource_service.go b/api/v2/resource_service.go new file mode 100644 index 00000000..26d0df7d --- /dev/null +++ b/api/v2/resource_service.go @@ -0,0 +1,54 @@ +package v2 + +import ( + "context" + + apiv2pb "github.com/usememos/memos/proto/gen/api/v2" + "github.com/usememos/memos/store" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type ResourceService struct { + apiv2pb.UnimplementedResourceServiceServer + + Store *store.Store +} + +// NewResourceService creates a new ResourceService. +func NewResourceService(store *store.Store) *ResourceService { + return &ResourceService{ + Store: store, + } +} + +func (s *ResourceService) ListResources(ctx context.Context, _ *apiv2pb.ListResourcesRequest) (*apiv2pb.ListResourcesResponse, error) { + user, err := getCurrentUser(ctx, s.Store) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err) + } + resources, err := s.Store.ListResources(ctx, &store.FindResource{ + CreatorID: &user.ID, + }) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to list tags: %v", err) + } + + response := &apiv2pb.ListResourcesResponse{} + for _, resource := range resources { + response.Resources = append(response.Resources, convertResourceFromStore(resource)) + } + return response, nil +} + +func convertResourceFromStore(resource *store.Resource) *apiv2pb.Resource { + return &apiv2pb.Resource{ + Id: resource.ID, + CreatedTs: resource.CreatedTs, + Filename: resource.Filename, + ExternalLink: resource.ExternalLink, + Type: resource.Type, + Size: resource.Size, + RelatedMemoId: resource.RelatedMemoID, + } +} diff --git a/api/v2/tag_service.go b/api/v2/tag_service.go index 1a5500d1..abdff9ce 100644 --- a/api/v2/tag_service.go +++ b/api/v2/tag_service.go @@ -34,7 +34,6 @@ func (s *TagService) ListTags(ctx context.Context, request *apiv2pb.ListTagsRequ for _, tag := range tags { response.Tags = append(response.Tags, convertTagFromStore(tag)) } - return response, nil } diff --git a/api/v2/v2.go b/api/v2/v2.go index 8044246b..4448a193 100644 --- a/api/v2/v2.go +++ b/api/v2/v2.go @@ -34,6 +34,7 @@ func NewAPIV2Service(secret string, profile *profile.Profile, store *store.Store apiv2pb.RegisterUserServiceServer(grpcServer, NewUserService(store, secret)) apiv2pb.RegisterMemoServiceServer(grpcServer, NewMemoService(store)) apiv2pb.RegisterTagServiceServer(grpcServer, NewTagService(store)) + apiv2pb.RegisterResourceServiceServer(grpcServer, NewResourceService(store)) reflection.Register(grpcServer) return &APIV2Service{ @@ -75,6 +76,9 @@ func (s *APIV2Service) RegisterGateway(ctx context.Context, e *echo.Echo) error if err := apiv2pb.RegisterTagServiceHandler(context.Background(), gwMux, conn); err != nil { return err } + if err := apiv2pb.RegisterResourceServiceHandler(context.Background(), gwMux, conn); err != nil { + return err + } e.Any("/api/v2/*", echo.WrapHandler(gwMux)) return nil diff --git a/proto/api/v2/resource_service.proto b/proto/api/v2/resource_service.proto new file mode 100644 index 00000000..f2cbbfcc --- /dev/null +++ b/proto/api/v2/resource_service.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package memos.api.v2; + +import "google/api/annotations.proto"; + +option go_package = "gen/api/v2"; + +service ResourceService { + rpc ListResources(ListResourcesRequest) returns (ListResourcesResponse) { + option (google.api.http) = {get: "/api/v2/resources"}; + } +} + +message Resource { + int32 id = 1; + int64 created_ts = 2; + string filename = 3; + string external_link = 4; + string type = 5; + int64 size = 6; + optional int32 related_memo_id = 7; +} + +message ListResourcesRequest {} + +message ListResourcesResponse { + repeated Resource resources = 1; +} diff --git a/proto/gen/api/v2/README.md b/proto/gen/api/v2/README.md index b015bbc4..4a39d082 100644 --- a/proto/gen/api/v2/README.md +++ b/proto/gen/api/v2/README.md @@ -17,6 +17,13 @@ - [MemoService](#memos-api-v2-MemoService) +- [api/v2/resource_service.proto](#api_v2_resource_service-proto) + - [ListResourcesRequest](#memos-api-v2-ListResourcesRequest) + - [ListResourcesResponse](#memos-api-v2-ListResourcesResponse) + - [Resource](#memos-api-v2-Resource) + + - [ResourceService](#memos-api-v2-ResourceService) + - [api/v2/system_service.proto](#api_v2_system_service-proto) - [GetSystemInfoRequest](#memos-api-v2-GetSystemInfoRequest) - [GetSystemInfoResponse](#memos-api-v2-GetSystemInfoResponse) @@ -209,6 +216,78 @@ + +

Top

+ +## api/v2/resource_service.proto + + + + + +### ListResourcesRequest + + + + + + + + + +### ListResourcesResponse + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| resources | [Resource](#memos-api-v2-Resource) | repeated | | + + + + + + + + +### Resource + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int32](#int32) | | | +| created_ts | [int64](#int64) | | | +| filename | [string](#string) | | | +| external_link | [string](#string) | | | +| type | [string](#string) | | | +| size | [int64](#int64) | | | +| related_memo_id | [int32](#int32) | optional | | + + + + + + + + + + + + + + +### ResourceService + + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| ListResources | [ListResourcesRequest](#memos-api-v2-ListResourcesRequest) | [ListResourcesResponse](#memos-api-v2-ListResourcesResponse) | | + + + + +

Top

diff --git a/proto/gen/api/v2/resource_service.pb.go b/proto/gen/api/v2/resource_service.pb.go new file mode 100644 index 00000000..c6b5faee --- /dev/null +++ b/proto/gen/api/v2/resource_service.pb.go @@ -0,0 +1,346 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc (unknown) +// source: api/v2/resource_service.proto + +package apiv2 + +import ( + _ "google.golang.org/genproto/googleapis/api/annotations" + 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 Resource 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"` + Filename string `protobuf:"bytes,3,opt,name=filename,proto3" json:"filename,omitempty"` + ExternalLink string `protobuf:"bytes,4,opt,name=external_link,json=externalLink,proto3" json:"external_link,omitempty"` + Type string `protobuf:"bytes,5,opt,name=type,proto3" json:"type,omitempty"` + Size int64 `protobuf:"varint,6,opt,name=size,proto3" json:"size,omitempty"` + RelatedMemoId *int32 `protobuf:"varint,7,opt,name=related_memo_id,json=relatedMemoId,proto3,oneof" json:"related_memo_id,omitempty"` +} + +func (x *Resource) Reset() { + *x = Resource{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v2_resource_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource) ProtoMessage() {} + +func (x *Resource) ProtoReflect() protoreflect.Message { + mi := &file_api_v2_resource_service_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 Resource.ProtoReflect.Descriptor instead. +func (*Resource) Descriptor() ([]byte, []int) { + return file_api_v2_resource_service_proto_rawDescGZIP(), []int{0} +} + +func (x *Resource) GetId() int32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Resource) GetCreatedTs() int64 { + if x != nil { + return x.CreatedTs + } + return 0 +} + +func (x *Resource) GetFilename() string { + if x != nil { + return x.Filename + } + return "" +} + +func (x *Resource) GetExternalLink() string { + if x != nil { + return x.ExternalLink + } + return "" +} + +func (x *Resource) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *Resource) GetSize() int64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *Resource) GetRelatedMemoId() int32 { + if x != nil && x.RelatedMemoId != nil { + return *x.RelatedMemoId + } + return 0 +} + +type ListResourcesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ListResourcesRequest) Reset() { + *x = ListResourcesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v2_resource_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListResourcesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListResourcesRequest) ProtoMessage() {} + +func (x *ListResourcesRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v2_resource_service_proto_msgTypes[1] + 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 ListResourcesRequest.ProtoReflect.Descriptor instead. +func (*ListResourcesRequest) Descriptor() ([]byte, []int) { + return file_api_v2_resource_service_proto_rawDescGZIP(), []int{1} +} + +type ListResourcesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Resources []*Resource `protobuf:"bytes,1,rep,name=resources,proto3" json:"resources,omitempty"` +} + +func (x *ListResourcesResponse) Reset() { + *x = ListResourcesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v2_resource_service_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListResourcesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListResourcesResponse) ProtoMessage() {} + +func (x *ListResourcesResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v2_resource_service_proto_msgTypes[2] + 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 ListResourcesResponse.ProtoReflect.Descriptor instead. +func (*ListResourcesResponse) Descriptor() ([]byte, []int) { + return file_api_v2_resource_service_proto_rawDescGZIP(), []int{2} +} + +func (x *ListResourcesResponse) GetResources() []*Resource { + if x != nil { + return x.Resources + } + return nil +} + +var File_api_v2_resource_service_proto protoreflect.FileDescriptor + +var file_api_v2_resource_service_proto_rawDesc = []byte{ + 0x0a, 0x1d, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x0c, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x1a, 0x1c, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe3, 0x01, 0x0a, 0x08, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 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, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, + 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, + 0x12, 0x2b, 0x0a, 0x0f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, + 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x49, 0x64, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, + 0x10, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x5f, 0x69, + 0x64, 0x22, 0x16, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4d, 0x0a, 0x15, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x34, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x32, 0x86, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x73, 0x0a, 0x0d, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x22, 0x2e, + 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x42, 0xac, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x42, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, + 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, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x32, + 0xa2, 0x02, 0x03, 0x4d, 0x41, 0x58, 0xaa, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x41, + 0x70, 0x69, 0x2e, 0x56, 0x32, 0xca, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, + 0x69, 0x5c, 0x56, 0x32, 0xe2, 0x02, 0x18, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, 0x69, + 0x5c, 0x56, 0x32, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, + 0x02, 0x0e, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x32, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_api_v2_resource_service_proto_rawDescOnce sync.Once + file_api_v2_resource_service_proto_rawDescData = file_api_v2_resource_service_proto_rawDesc +) + +func file_api_v2_resource_service_proto_rawDescGZIP() []byte { + file_api_v2_resource_service_proto_rawDescOnce.Do(func() { + file_api_v2_resource_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_v2_resource_service_proto_rawDescData) + }) + return file_api_v2_resource_service_proto_rawDescData +} + +var file_api_v2_resource_service_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_api_v2_resource_service_proto_goTypes = []interface{}{ + (*Resource)(nil), // 0: memos.api.v2.Resource + (*ListResourcesRequest)(nil), // 1: memos.api.v2.ListResourcesRequest + (*ListResourcesResponse)(nil), // 2: memos.api.v2.ListResourcesResponse +} +var file_api_v2_resource_service_proto_depIdxs = []int32{ + 0, // 0: memos.api.v2.ListResourcesResponse.resources:type_name -> memos.api.v2.Resource + 1, // 1: memos.api.v2.ResourceService.ListResources:input_type -> memos.api.v2.ListResourcesRequest + 2, // 2: memos.api.v2.ResourceService.ListResources:output_type -> memos.api.v2.ListResourcesResponse + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] 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_api_v2_resource_service_proto_init() } +func file_api_v2_resource_service_proto_init() { + if File_api_v2_resource_service_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_api_v2_resource_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v2_resource_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListResourcesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v2_resource_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListResourcesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_api_v2_resource_service_proto_msgTypes[0].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_api_v2_resource_service_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_api_v2_resource_service_proto_goTypes, + DependencyIndexes: file_api_v2_resource_service_proto_depIdxs, + MessageInfos: file_api_v2_resource_service_proto_msgTypes, + }.Build() + File_api_v2_resource_service_proto = out.File + file_api_v2_resource_service_proto_rawDesc = nil + file_api_v2_resource_service_proto_goTypes = nil + file_api_v2_resource_service_proto_depIdxs = nil +} diff --git a/proto/gen/api/v2/resource_service.pb.gw.go b/proto/gen/api/v2/resource_service.pb.gw.go new file mode 100644 index 00000000..f3d340d9 --- /dev/null +++ b/proto/gen/api/v2/resource_service.pb.gw.go @@ -0,0 +1,155 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: api/v2/resource_service.proto + +/* +Package apiv2 is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package apiv2 + +import ( + "context" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = metadata.Join + +func request_ResourceService_ListResources_0(ctx context.Context, marshaler runtime.Marshaler, client ResourceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListResourcesRequest + var metadata runtime.ServerMetadata + + msg, err := client.ListResources(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ResourceService_ListResources_0(ctx context.Context, marshaler runtime.Marshaler, server ResourceServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListResourcesRequest + var metadata runtime.ServerMetadata + + msg, err := server.ListResources(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterResourceServiceHandlerServer registers the http handlers for service ResourceService to "mux". +// UnaryRPC :call ResourceServiceServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterResourceServiceHandlerFromEndpoint instead. +func RegisterResourceServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ResourceServiceServer) error { + + mux.Handle("GET", pattern_ResourceService_ListResources_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) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v2.ResourceService/ListResources", runtime.WithHTTPPathPattern("/api/v2/resources")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ResourceService_ListResources_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_ResourceService_ListResources_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterResourceServiceHandlerFromEndpoint is same as RegisterResourceServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterResourceServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.DialContext(ctx, endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterResourceServiceHandler(ctx, mux, conn) +} + +// RegisterResourceServiceHandler registers the http handlers for service ResourceService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterResourceServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterResourceServiceHandlerClient(ctx, mux, NewResourceServiceClient(conn)) +} + +// RegisterResourceServiceHandlerClient registers the http handlers for service ResourceService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ResourceServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ResourceServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "ResourceServiceClient" to call the correct interceptors. +func RegisterResourceServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ResourceServiceClient) error { + + mux.Handle("GET", pattern_ResourceService_ListResources_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) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v2.ResourceService/ListResources", runtime.WithHTTPPathPattern("/api/v2/resources")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ResourceService_ListResources_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_ResourceService_ListResources_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_ResourceService_ListResources_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v2", "resources"}, "")) +) + +var ( + forward_ResourceService_ListResources_0 = runtime.ForwardResponseMessage +) diff --git a/proto/gen/api/v2/resource_service_grpc.pb.go b/proto/gen/api/v2/resource_service_grpc.pb.go new file mode 100644 index 00000000..2acc5cf3 --- /dev/null +++ b/proto/gen/api/v2/resource_service_grpc.pb.go @@ -0,0 +1,109 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: api/v2/resource_service.proto + +package apiv2 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + ResourceService_ListResources_FullMethodName = "/memos.api.v2.ResourceService/ListResources" +) + +// ResourceServiceClient is the client API for ResourceService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ResourceServiceClient interface { + ListResources(ctx context.Context, in *ListResourcesRequest, opts ...grpc.CallOption) (*ListResourcesResponse, error) +} + +type resourceServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewResourceServiceClient(cc grpc.ClientConnInterface) ResourceServiceClient { + return &resourceServiceClient{cc} +} + +func (c *resourceServiceClient) ListResources(ctx context.Context, in *ListResourcesRequest, opts ...grpc.CallOption) (*ListResourcesResponse, error) { + out := new(ListResourcesResponse) + err := c.cc.Invoke(ctx, ResourceService_ListResources_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ResourceServiceServer is the server API for ResourceService service. +// All implementations must embed UnimplementedResourceServiceServer +// for forward compatibility +type ResourceServiceServer interface { + ListResources(context.Context, *ListResourcesRequest) (*ListResourcesResponse, error) + mustEmbedUnimplementedResourceServiceServer() +} + +// UnimplementedResourceServiceServer must be embedded to have forward compatible implementations. +type UnimplementedResourceServiceServer struct { +} + +func (UnimplementedResourceServiceServer) ListResources(context.Context, *ListResourcesRequest) (*ListResourcesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListResources not implemented") +} +func (UnimplementedResourceServiceServer) mustEmbedUnimplementedResourceServiceServer() {} + +// UnsafeResourceServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ResourceServiceServer will +// result in compilation errors. +type UnsafeResourceServiceServer interface { + mustEmbedUnimplementedResourceServiceServer() +} + +func RegisterResourceServiceServer(s grpc.ServiceRegistrar, srv ResourceServiceServer) { + s.RegisterService(&ResourceService_ServiceDesc, srv) +} + +func _ResourceService_ListResources_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListResourcesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ResourceServiceServer).ListResources(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ResourceService_ListResources_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ResourceServiceServer).ListResources(ctx, req.(*ListResourcesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// ResourceService_ServiceDesc is the grpc.ServiceDesc for ResourceService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ResourceService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "memos.api.v2.ResourceService", + HandlerType: (*ResourceServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ListResources", + Handler: _ResourceService_ListResources_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "api/v2/resource_service.proto", +} diff --git a/store/resource.go b/store/resource.go index b72c12b6..797b332c 100644 --- a/store/resource.go +++ b/store/resource.go @@ -16,13 +16,15 @@ type Resource struct { UpdatedTs int64 // Domain specific fields - Filename string - Blob []byte - InternalPath string - ExternalLink string - Type string - Size int64 - LinkedMemoAmount int + Filename string + Blob []byte + InternalPath string + ExternalLink string + Type string + Size int64 + + // Related fields + RelatedMemoID *int32 } type FindResource struct { @@ -102,7 +104,7 @@ func (s *Store) ListResources(ctx context.Context, find *FindResource) ([]*Resou query := fmt.Sprintf(` SELECT - COUNT(DISTINCT memo_resource.memo_id) AS linked_memo_amount, + GROUP_CONCAT(memo_resource.memo_id) as related_memo_id, %s FROM resource LEFT JOIN memo_resource ON resource.id = memo_resource.resource_id @@ -127,7 +129,7 @@ func (s *Store) ListResources(ctx context.Context, find *FindResource) ([]*Resou for rows.Next() { resource := Resource{} dests := []any{ - &resource.LinkedMemoAmount, + &resource.RelatedMemoID, &resource.ID, &resource.Filename, &resource.ExternalLink, diff --git a/test/store/resource_test.go b/test/store/resource_test.go index 9a601076..86e1f138 100644 --- a/test/store/resource_test.go +++ b/test/store/resource_test.go @@ -24,12 +24,24 @@ func TestResourceStore(t *testing.T) { correctFilename := "test.epub" incorrectFilename := "test.png" - res, err := ts.GetResource(ctx, &store.FindResource{ + resource, err := ts.GetResource(ctx, &store.FindResource{ Filename: &correctFilename, }) require.NoError(t, err) - require.Equal(t, correctFilename, res.Filename) - require.Equal(t, int32(1), res.ID) + require.Equal(t, correctFilename, resource.Filename) + require.Equal(t, int32(1), resource.ID) + _, err = ts.UpsertMemoResource(ctx, &store.UpsertMemoResource{ + MemoID: 1, + ResourceID: resource.ID, + }) + require.NoError(t, err) + + resource, err = ts.GetResource(ctx, &store.FindResource{ + ID: &resource.ID, + }) + require.NoError(t, err) + require.Equal(t, *resource.RelatedMemoID, int32(1)) + notFoundResource, err := ts.GetResource(ctx, &store.FindResource{ Filename: &incorrectFilename, }) @@ -42,6 +54,7 @@ func TestResourceStore(t *testing.T) { CreatorID: &correctCreatorID, }) require.NoError(t, err) + notFoundResource, err = ts.GetResource(ctx, &store.FindResource{ CreatorID: &incorrectCreatorID, }) diff --git a/web/src/components/Header.tsx b/web/src/components/Header.tsx index f65d2857..31e15326 100644 --- a/web/src/components/Header.tsx +++ b/web/src/components/Header.tsx @@ -46,18 +46,18 @@ const Header = () => { title: t("daily-review.title"), icon: , }; - const exploreNavLink: NavLinkItem = { - id: "header-explore", - path: "/explore", - title: t("common.explore"), - icon: , - }; const resourcesNavLink: NavLinkItem = { id: "header-resources", path: "/resources", title: t("common.resources"), icon: , }; + const exploreNavLink: NavLinkItem = { + id: "header-explore", + path: "/explore", + title: t("common.explore"), + icon: , + }; const archivedNavLink: NavLinkItem = { id: "header-archived", path: "/archived", @@ -78,7 +78,7 @@ const Header = () => { }; const navLinks: NavLinkItem[] = !isVisitorMode - ? [homeNavLink, dailyReviewNavLink, exploreNavLink, resourcesNavLink, archivedNavLink, settingNavLink] + ? [homeNavLink, dailyReviewNavLink, resourcesNavLink, exploreNavLink, archivedNavLink, settingNavLink] : [exploreNavLink, authNavLink]; return ( diff --git a/web/src/components/ResourceCard.tsx b/web/src/components/ResourceCard.tsx index ba72a6b9..0fe45426 100644 --- a/web/src/components/ResourceCard.tsx +++ b/web/src/components/ResourceCard.tsx @@ -1,6 +1,5 @@ import { getDateTimeString } from "@/helpers/datetime"; import ResourceIcon from "./ResourceIcon"; -import "@/less/resource-card.less"; interface Props { resource: Resource; @@ -8,7 +7,7 @@ interface Props { const ResourceCard = ({ resource }: Props) => { return ( -
+
diff --git a/web/src/less/resource-card.less b/web/src/less/resource-card.less deleted file mode 100644 index 0b9e4dc1..00000000 --- a/web/src/less/resource-card.less +++ /dev/null @@ -1,25 +0,0 @@ -.resource-card { - @apply w-full p-2 relative flex flex-col justify-start hover:shadow hover:bg-slate-200 dark:hover:bg-slate-600 rounded-md; - - &:hover { - .resource-checkbox { - @apply flex hover:text-black; - } - .more-action-btn { - @apply flex; - } - } - - .resource-checkbox { - @apply hidden w-4 h-auto cursor-pointer hover:flex; - } - - .more-action-btn { - @apply hidden; - &:hover { - & + .more-action-btns-wrapper { - display: flex; - } - } - } -} diff --git a/web/src/types/modules/resource.d.ts b/web/src/types/modules/resource.d.ts index 275e98dc..da7b2380 100644 --- a/web/src/types/modules/resource.d.ts +++ b/web/src/types/modules/resource.d.ts @@ -10,8 +10,6 @@ interface Resource { externalLink: string; type: string; size: string; - - linkedMemoAmount: number; } interface ResourceCreate { diff --git a/web/src/types/proto/api/v2/resource_service_pb.d.ts b/web/src/types/proto/api/v2/resource_service_pb.d.ts new file mode 100644 index 00000000..c7f16054 --- /dev/null +++ b/web/src/types/proto/api/v2/resource_service_pb.d.ts @@ -0,0 +1,105 @@ +// @generated by protoc-gen-es v1.3.0 +// @generated from file api/v2/resource_service.proto (package memos.api.v2, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; +import { Message, proto3 } from "@bufbuild/protobuf"; + +/** + * @generated from message memos.api.v2.Resource + */ +export declare class Resource extends Message { + /** + * @generated from field: int32 id = 1; + */ + id: number; + + /** + * @generated from field: int64 created_ts = 2; + */ + createdTs: bigint; + + /** + * @generated from field: string filename = 3; + */ + filename: string; + + /** + * @generated from field: string external_link = 4; + */ + externalLink: string; + + /** + * @generated from field: string type = 5; + */ + type: string; + + /** + * @generated from field: int64 size = 6; + */ + size: bigint; + + /** + * @generated from field: optional int32 related_memo_id = 7; + */ + relatedMemoId?: number; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "memos.api.v2.Resource"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): Resource; + + static fromJson(jsonValue: JsonValue, options?: Partial): Resource; + + static fromJsonString(jsonString: string, options?: Partial): Resource; + + static equals(a: Resource | PlainMessage | undefined, b: Resource | PlainMessage | undefined): boolean; +} + +/** + * @generated from message memos.api.v2.ListResourcesRequest + */ +export declare class ListResourcesRequest extends Message { + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "memos.api.v2.ListResourcesRequest"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): ListResourcesRequest; + + static fromJson(jsonValue: JsonValue, options?: Partial): ListResourcesRequest; + + static fromJsonString(jsonString: string, options?: Partial): ListResourcesRequest; + + static equals(a: ListResourcesRequest | PlainMessage | undefined, b: ListResourcesRequest | PlainMessage | undefined): boolean; +} + +/** + * @generated from message memos.api.v2.ListResourcesResponse + */ +export declare class ListResourcesResponse extends Message { + /** + * @generated from field: repeated memos.api.v2.Resource resources = 1; + */ + resources: Resource[]; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "memos.api.v2.ListResourcesResponse"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): ListResourcesResponse; + + static fromJson(jsonValue: JsonValue, options?: Partial): ListResourcesResponse; + + static fromJsonString(jsonString: string, options?: Partial): ListResourcesResponse; + + static equals(a: ListResourcesResponse | PlainMessage | undefined, b: ListResourcesResponse | PlainMessage | undefined): boolean; +} + diff --git a/web/src/types/proto/api/v2/resource_service_pb.js b/web/src/types/proto/api/v2/resource_service_pb.js new file mode 100644 index 00000000..e252dd7c --- /dev/null +++ b/web/src/types/proto/api/v2/resource_service_pb.js @@ -0,0 +1,41 @@ +// @generated by protoc-gen-es v1.3.0 +// @generated from file api/v2/resource_service.proto (package memos.api.v2, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import { proto3 } from "@bufbuild/protobuf"; + +/** + * @generated from message memos.api.v2.Resource + */ +export const Resource = proto3.makeMessageType( + "memos.api.v2.Resource", + () => [ + { no: 1, name: "id", kind: "scalar", T: 5 /* ScalarType.INT32 */ }, + { no: 2, name: "created_ts", kind: "scalar", T: 3 /* ScalarType.INT64 */ }, + { no: 3, name: "filename", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 4, name: "external_link", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 5, name: "type", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 6, name: "size", kind: "scalar", T: 3 /* ScalarType.INT64 */ }, + { no: 7, name: "related_memo_id", kind: "scalar", T: 5 /* ScalarType.INT32 */, opt: true }, + ], +); + +/** + * @generated from message memos.api.v2.ListResourcesRequest + */ +export const ListResourcesRequest = proto3.makeMessageType( + "memos.api.v2.ListResourcesRequest", + [], +); + +/** + * @generated from message memos.api.v2.ListResourcesResponse + */ +export const ListResourcesResponse = proto3.makeMessageType( + "memos.api.v2.ListResourcesResponse", + () => [ + { no: 1, name: "resources", kind: "message", T: Resource, repeated: true }, + ], +); +