feat: add link metadata endpoints

pull/5917/head
boojack 4 weeks ago
parent c7242324a1
commit 9c5c604944

@ -6,6 +6,7 @@ import (
"net"
"net/http"
"net/url"
"time"
"github.com/pkg/errors"
"golang.org/x/net/html"
@ -14,7 +15,10 @@ import (
var ErrInternalIP = errors.New("internal IP addresses are not allowed")
const maxHTMLMetaBytes = 512 * 1024
var httpClient = &http.Client{
Timeout: 5 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if err := validateURL(req.URL.String()); err != nil {
return errors.Wrap(err, "redirect to internal IP")
@ -51,9 +55,7 @@ func GetHTMLMeta(urlStr string) (*HTMLMeta, error) {
return nil, errors.New("not a HTML page")
}
// TODO: limit the size of the response body
htmlMeta := extractHTMLMeta(response.Body)
htmlMeta := extractHTMLMeta(io.LimitReader(response.Body, maxHTMLMetaBytes))
enrichSiteMeta(response.Request.URL, htmlMeta)
return htmlMeta, nil
}

@ -2,21 +2,55 @@ package httpgetter
import (
"errors"
"io"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
type roundTripFunc func(*http.Request) (*http.Response, error)
func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req)
}
func TestGetHTMLMeta(t *testing.T) {
tests := []struct {
urlStr string
htmlMeta HTMLMeta
}{}
for _, test := range tests {
metadata, err := GetHTMLMeta(test.urlStr)
require.NoError(t, err)
require.Equal(t, test.htmlMeta, *metadata)
originalHTTPClient := httpClient
t.Cleanup(func() {
httpClient = originalHTTPClient
})
httpClient = &http.Client{
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
require.Equal(t, "http://93.184.216.34/article", req.URL.String())
return &http.Response{
StatusCode: http.StatusOK,
Header: http.Header{"Content-Type": []string{"text/html; charset=utf-8"}},
Body: io.NopCloser(strings.NewReader(`<!doctype html>
<html>
<head>
<title>Fallback title</title>
<meta name="description" content="Fallback description">
<meta property="og:title" content="Open Graph title">
<meta property="og:description" content="Open Graph description">
<meta property="og:image" content="https://example.com/cover.png">
</head>
<body>ignored</body>
</html>`)),
Request: req,
}, nil
}),
}
metadata, err := GetHTMLMeta("http://93.184.216.34/article")
require.NoError(t, err)
require.Equal(t, HTMLMeta{
Title: "Open Graph title",
Description: "Open Graph description",
Image: "https://example.com/cover.png",
}, *metadata)
}
func TestGetHTMLMetaForInternal(t *testing.T) {
@ -30,3 +64,7 @@ func TestGetHTMLMetaForInternal(t *testing.T) {
t.Errorf("Expected error for resolved internal IP, got %v", err)
}
}
func TestHTTPClientHasTimeout(t *testing.T) {
require.NotZero(t, httpClient.Timeout)
}

@ -126,6 +126,17 @@ service MemoService {
rpc GetMemoByShare(GetMemoByShareRequest) returns (Memo) {
option (google.api.http) = {get: "/api/v1/shares/{share_id}"};
}
// GetLinkMetadata gets metadata for a link.
rpc GetLinkMetadata(GetLinkMetadataRequest) returns (LinkMetadata) {
option (google.api.http) = {get: "/api/v1/memos/-/linkMetadata"};
}
// BatchGetLinkMetadata gets metadata for links.
rpc BatchGetLinkMetadata(BatchGetLinkMetadataRequest) returns (BatchGetLinkMetadataResponse) {
option (google.api.http) = {
post: "/api/v1/memos/-/linkMetadata:batchGet"
body: "*"
};
}
}
enum Visibility {
@ -595,3 +606,32 @@ message GetMemoByShareRequest {
// Required. The share token extracted from the share URL (/s/{share_id}).
string share_id = 1 [(google.api.field_behavior) = REQUIRED];
}
message GetLinkMetadataRequest {
// Required. The link URL.
string url = 1 [(google.api.field_behavior) = REQUIRED];
}
message BatchGetLinkMetadataRequest {
// Required. The link URLs.
repeated string urls = 1 [(google.api.field_behavior) = REQUIRED];
}
message BatchGetLinkMetadataResponse {
// The link metadata list, in the same order as the input URLs.
repeated LinkMetadata link_metadata = 1;
}
message LinkMetadata {
// The original link URL.
string url = 1;
// The link title.
string title = 2;
// The link description.
string description = 3;
// The link image URL.
string image = 4;
}

@ -83,6 +83,12 @@ const (
// MemoServiceGetMemoByShareProcedure is the fully-qualified name of the MemoService's
// GetMemoByShare RPC.
MemoServiceGetMemoByShareProcedure = "/memos.api.v1.MemoService/GetMemoByShare"
// MemoServiceGetLinkMetadataProcedure is the fully-qualified name of the MemoService's
// GetLinkMetadata RPC.
MemoServiceGetLinkMetadataProcedure = "/memos.api.v1.MemoService/GetLinkMetadata"
// MemoServiceBatchGetLinkMetadataProcedure is the fully-qualified name of the MemoService's
// BatchGetLinkMetadata RPC.
MemoServiceBatchGetLinkMetadataProcedure = "/memos.api.v1.MemoService/BatchGetLinkMetadata"
)
// MemoServiceClient is a client for the memos.api.v1.MemoService service.
@ -124,6 +130,10 @@ type MemoServiceClient interface {
// GetMemoByShare resolves a share token to its memo. No authentication required.
// Returns NOT_FOUND if the token is invalid or expired.
GetMemoByShare(context.Context, *connect.Request[v1.GetMemoByShareRequest]) (*connect.Response[v1.Memo], error)
// GetLinkMetadata gets metadata for a link.
GetLinkMetadata(context.Context, *connect.Request[v1.GetLinkMetadataRequest]) (*connect.Response[v1.LinkMetadata], error)
// BatchGetLinkMetadata gets metadata for links.
BatchGetLinkMetadata(context.Context, *connect.Request[v1.BatchGetLinkMetadataRequest]) (*connect.Response[v1.BatchGetLinkMetadataResponse], error)
}
// NewMemoServiceClient constructs a client for the memos.api.v1.MemoService service. By default, it
@ -245,29 +255,43 @@ func NewMemoServiceClient(httpClient connect.HTTPClient, baseURL string, opts ..
connect.WithSchema(memoServiceMethods.ByName("GetMemoByShare")),
connect.WithClientOptions(opts...),
),
getLinkMetadata: connect.NewClient[v1.GetLinkMetadataRequest, v1.LinkMetadata](
httpClient,
baseURL+MemoServiceGetLinkMetadataProcedure,
connect.WithSchema(memoServiceMethods.ByName("GetLinkMetadata")),
connect.WithClientOptions(opts...),
),
batchGetLinkMetadata: connect.NewClient[v1.BatchGetLinkMetadataRequest, v1.BatchGetLinkMetadataResponse](
httpClient,
baseURL+MemoServiceBatchGetLinkMetadataProcedure,
connect.WithSchema(memoServiceMethods.ByName("BatchGetLinkMetadata")),
connect.WithClientOptions(opts...),
),
}
}
// memoServiceClient implements MemoServiceClient.
type memoServiceClient struct {
createMemo *connect.Client[v1.CreateMemoRequest, v1.Memo]
listMemos *connect.Client[v1.ListMemosRequest, v1.ListMemosResponse]
getMemo *connect.Client[v1.GetMemoRequest, v1.Memo]
updateMemo *connect.Client[v1.UpdateMemoRequest, v1.Memo]
deleteMemo *connect.Client[v1.DeleteMemoRequest, emptypb.Empty]
setMemoAttachments *connect.Client[v1.SetMemoAttachmentsRequest, emptypb.Empty]
listMemoAttachments *connect.Client[v1.ListMemoAttachmentsRequest, v1.ListMemoAttachmentsResponse]
setMemoRelations *connect.Client[v1.SetMemoRelationsRequest, emptypb.Empty]
listMemoRelations *connect.Client[v1.ListMemoRelationsRequest, v1.ListMemoRelationsResponse]
createMemoComment *connect.Client[v1.CreateMemoCommentRequest, v1.Memo]
listMemoComments *connect.Client[v1.ListMemoCommentsRequest, v1.ListMemoCommentsResponse]
listMemoReactions *connect.Client[v1.ListMemoReactionsRequest, v1.ListMemoReactionsResponse]
upsertMemoReaction *connect.Client[v1.UpsertMemoReactionRequest, v1.Reaction]
deleteMemoReaction *connect.Client[v1.DeleteMemoReactionRequest, emptypb.Empty]
createMemoShare *connect.Client[v1.CreateMemoShareRequest, v1.MemoShare]
listMemoShares *connect.Client[v1.ListMemoSharesRequest, v1.ListMemoSharesResponse]
deleteMemoShare *connect.Client[v1.DeleteMemoShareRequest, emptypb.Empty]
getMemoByShare *connect.Client[v1.GetMemoByShareRequest, v1.Memo]
createMemo *connect.Client[v1.CreateMemoRequest, v1.Memo]
listMemos *connect.Client[v1.ListMemosRequest, v1.ListMemosResponse]
getMemo *connect.Client[v1.GetMemoRequest, v1.Memo]
updateMemo *connect.Client[v1.UpdateMemoRequest, v1.Memo]
deleteMemo *connect.Client[v1.DeleteMemoRequest, emptypb.Empty]
setMemoAttachments *connect.Client[v1.SetMemoAttachmentsRequest, emptypb.Empty]
listMemoAttachments *connect.Client[v1.ListMemoAttachmentsRequest, v1.ListMemoAttachmentsResponse]
setMemoRelations *connect.Client[v1.SetMemoRelationsRequest, emptypb.Empty]
listMemoRelations *connect.Client[v1.ListMemoRelationsRequest, v1.ListMemoRelationsResponse]
createMemoComment *connect.Client[v1.CreateMemoCommentRequest, v1.Memo]
listMemoComments *connect.Client[v1.ListMemoCommentsRequest, v1.ListMemoCommentsResponse]
listMemoReactions *connect.Client[v1.ListMemoReactionsRequest, v1.ListMemoReactionsResponse]
upsertMemoReaction *connect.Client[v1.UpsertMemoReactionRequest, v1.Reaction]
deleteMemoReaction *connect.Client[v1.DeleteMemoReactionRequest, emptypb.Empty]
createMemoShare *connect.Client[v1.CreateMemoShareRequest, v1.MemoShare]
listMemoShares *connect.Client[v1.ListMemoSharesRequest, v1.ListMemoSharesResponse]
deleteMemoShare *connect.Client[v1.DeleteMemoShareRequest, emptypb.Empty]
getMemoByShare *connect.Client[v1.GetMemoByShareRequest, v1.Memo]
getLinkMetadata *connect.Client[v1.GetLinkMetadataRequest, v1.LinkMetadata]
batchGetLinkMetadata *connect.Client[v1.BatchGetLinkMetadataRequest, v1.BatchGetLinkMetadataResponse]
}
// CreateMemo calls memos.api.v1.MemoService.CreateMemo.
@ -360,6 +384,16 @@ func (c *memoServiceClient) GetMemoByShare(ctx context.Context, req *connect.Req
return c.getMemoByShare.CallUnary(ctx, req)
}
// GetLinkMetadata calls memos.api.v1.MemoService.GetLinkMetadata.
func (c *memoServiceClient) GetLinkMetadata(ctx context.Context, req *connect.Request[v1.GetLinkMetadataRequest]) (*connect.Response[v1.LinkMetadata], error) {
return c.getLinkMetadata.CallUnary(ctx, req)
}
// BatchGetLinkMetadata calls memos.api.v1.MemoService.BatchGetLinkMetadata.
func (c *memoServiceClient) BatchGetLinkMetadata(ctx context.Context, req *connect.Request[v1.BatchGetLinkMetadataRequest]) (*connect.Response[v1.BatchGetLinkMetadataResponse], error) {
return c.batchGetLinkMetadata.CallUnary(ctx, req)
}
// MemoServiceHandler is an implementation of the memos.api.v1.MemoService service.
type MemoServiceHandler interface {
// CreateMemo creates a memo.
@ -399,6 +433,10 @@ type MemoServiceHandler interface {
// GetMemoByShare resolves a share token to its memo. No authentication required.
// Returns NOT_FOUND if the token is invalid or expired.
GetMemoByShare(context.Context, *connect.Request[v1.GetMemoByShareRequest]) (*connect.Response[v1.Memo], error)
// GetLinkMetadata gets metadata for a link.
GetLinkMetadata(context.Context, *connect.Request[v1.GetLinkMetadataRequest]) (*connect.Response[v1.LinkMetadata], error)
// BatchGetLinkMetadata gets metadata for links.
BatchGetLinkMetadata(context.Context, *connect.Request[v1.BatchGetLinkMetadataRequest]) (*connect.Response[v1.BatchGetLinkMetadataResponse], error)
}
// NewMemoServiceHandler builds an HTTP handler from the service implementation. It returns the path
@ -516,6 +554,18 @@ func NewMemoServiceHandler(svc MemoServiceHandler, opts ...connect.HandlerOption
connect.WithSchema(memoServiceMethods.ByName("GetMemoByShare")),
connect.WithHandlerOptions(opts...),
)
memoServiceGetLinkMetadataHandler := connect.NewUnaryHandler(
MemoServiceGetLinkMetadataProcedure,
svc.GetLinkMetadata,
connect.WithSchema(memoServiceMethods.ByName("GetLinkMetadata")),
connect.WithHandlerOptions(opts...),
)
memoServiceBatchGetLinkMetadataHandler := connect.NewUnaryHandler(
MemoServiceBatchGetLinkMetadataProcedure,
svc.BatchGetLinkMetadata,
connect.WithSchema(memoServiceMethods.ByName("BatchGetLinkMetadata")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.MemoService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case MemoServiceCreateMemoProcedure:
@ -554,6 +604,10 @@ func NewMemoServiceHandler(svc MemoServiceHandler, opts ...connect.HandlerOption
memoServiceDeleteMemoShareHandler.ServeHTTP(w, r)
case MemoServiceGetMemoByShareProcedure:
memoServiceGetMemoByShareHandler.ServeHTTP(w, r)
case MemoServiceGetLinkMetadataProcedure:
memoServiceGetLinkMetadataHandler.ServeHTTP(w, r)
case MemoServiceBatchGetLinkMetadataProcedure:
memoServiceBatchGetLinkMetadataHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
@ -634,3 +688,11 @@ func (UnimplementedMemoServiceHandler) DeleteMemoShare(context.Context, *connect
func (UnimplementedMemoServiceHandler) GetMemoByShare(context.Context, *connect.Request[v1.GetMemoByShareRequest]) (*connect.Response[v1.Memo], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.GetMemoByShare is not implemented"))
}
func (UnimplementedMemoServiceHandler) GetLinkMetadata(context.Context, *connect.Request[v1.GetLinkMetadataRequest]) (*connect.Response[v1.LinkMetadata], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.GetLinkMetadata is not implemented"))
}
func (UnimplementedMemoServiceHandler) BatchGetLinkMetadata(context.Context, *connect.Request[v1.BatchGetLinkMetadataRequest]) (*connect.Response[v1.BatchGetLinkMetadataResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.BatchGetLinkMetadata is not implemented"))
}

@ -1966,6 +1966,213 @@ func (x *GetMemoByShareRequest) GetShareId() string {
return ""
}
type GetLinkMetadataRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Required. The link URL.
Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetLinkMetadataRequest) Reset() {
*x = GetLinkMetadataRequest{}
mi := &file_api_v1_memo_service_proto_msgTypes[29]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetLinkMetadataRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetLinkMetadataRequest) ProtoMessage() {}
func (x *GetLinkMetadataRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_memo_service_proto_msgTypes[29]
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 GetLinkMetadataRequest.ProtoReflect.Descriptor instead.
func (*GetLinkMetadataRequest) Descriptor() ([]byte, []int) {
return file_api_v1_memo_service_proto_rawDescGZIP(), []int{29}
}
func (x *GetLinkMetadataRequest) GetUrl() string {
if x != nil {
return x.Url
}
return ""
}
type BatchGetLinkMetadataRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Required. The link URLs.
Urls []string `protobuf:"bytes,1,rep,name=urls,proto3" json:"urls,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *BatchGetLinkMetadataRequest) Reset() {
*x = BatchGetLinkMetadataRequest{}
mi := &file_api_v1_memo_service_proto_msgTypes[30]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *BatchGetLinkMetadataRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BatchGetLinkMetadataRequest) ProtoMessage() {}
func (x *BatchGetLinkMetadataRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_memo_service_proto_msgTypes[30]
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 BatchGetLinkMetadataRequest.ProtoReflect.Descriptor instead.
func (*BatchGetLinkMetadataRequest) Descriptor() ([]byte, []int) {
return file_api_v1_memo_service_proto_rawDescGZIP(), []int{30}
}
func (x *BatchGetLinkMetadataRequest) GetUrls() []string {
if x != nil {
return x.Urls
}
return nil
}
type BatchGetLinkMetadataResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The link metadata list, in the same order as the input URLs.
LinkMetadata []*LinkMetadata `protobuf:"bytes,1,rep,name=link_metadata,json=linkMetadata,proto3" json:"link_metadata,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *BatchGetLinkMetadataResponse) Reset() {
*x = BatchGetLinkMetadataResponse{}
mi := &file_api_v1_memo_service_proto_msgTypes[31]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *BatchGetLinkMetadataResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BatchGetLinkMetadataResponse) ProtoMessage() {}
func (x *BatchGetLinkMetadataResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_memo_service_proto_msgTypes[31]
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 BatchGetLinkMetadataResponse.ProtoReflect.Descriptor instead.
func (*BatchGetLinkMetadataResponse) Descriptor() ([]byte, []int) {
return file_api_v1_memo_service_proto_rawDescGZIP(), []int{31}
}
func (x *BatchGetLinkMetadataResponse) GetLinkMetadata() []*LinkMetadata {
if x != nil {
return x.LinkMetadata
}
return nil
}
type LinkMetadata struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The original link URL.
Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
// The link title.
Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
// The link description.
Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
// The link image URL.
Image string `protobuf:"bytes,4,opt,name=image,proto3" json:"image,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *LinkMetadata) Reset() {
*x = LinkMetadata{}
mi := &file_api_v1_memo_service_proto_msgTypes[32]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *LinkMetadata) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*LinkMetadata) ProtoMessage() {}
func (x *LinkMetadata) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_memo_service_proto_msgTypes[32]
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 LinkMetadata.ProtoReflect.Descriptor instead.
func (*LinkMetadata) Descriptor() ([]byte, []int) {
return file_api_v1_memo_service_proto_rawDescGZIP(), []int{32}
}
func (x *LinkMetadata) GetUrl() string {
if x != nil {
return x.Url
}
return ""
}
func (x *LinkMetadata) GetTitle() string {
if x != nil {
return x.Title
}
return ""
}
func (x *LinkMetadata) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *LinkMetadata) GetImage() string {
if x != nil {
return x.Image
}
return ""
}
// Computed properties of a memo.
type Memo_Property struct {
state protoimpl.MessageState `protogen:"open.v1"`
@ -1981,7 +2188,7 @@ type Memo_Property struct {
func (x *Memo_Property) Reset() {
*x = Memo_Property{}
mi := &file_api_v1_memo_service_proto_msgTypes[29]
mi := &file_api_v1_memo_service_proto_msgTypes[33]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1993,7 +2200,7 @@ func (x *Memo_Property) String() string {
func (*Memo_Property) ProtoMessage() {}
func (x *Memo_Property) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_memo_service_proto_msgTypes[29]
mi := &file_api_v1_memo_service_proto_msgTypes[33]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -2058,7 +2265,7 @@ type MemoRelation_Memo struct {
func (x *MemoRelation_Memo) Reset() {
*x = MemoRelation_Memo{}
mi := &file_api_v1_memo_service_proto_msgTypes[30]
mi := &file_api_v1_memo_service_proto_msgTypes[34]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -2070,7 +2277,7 @@ func (x *MemoRelation_Memo) String() string {
func (*MemoRelation_Memo) ProtoMessage() {}
func (x *MemoRelation_Memo) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_memo_service_proto_msgTypes[30]
mi := &file_api_v1_memo_service_proto_msgTypes[34]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -2275,14 +2482,25 @@ const file_api_v1_memo_service_proto_rawDesc = "" +
"\x04name\x18\x01 \x01(\tB\x1e\xe0A\x02\xfaA\x18\n" +
"\x16memos.api.v1/MemoShareR\x04name\"7\n" +
"\x15GetMemoByShareRequest\x12\x1e\n" +
"\bshare_id\x18\x01 \x01(\tB\x03\xe0A\x02R\ashareId*P\n" +
"\bshare_id\x18\x01 \x01(\tB\x03\xe0A\x02R\ashareId\"/\n" +
"\x16GetLinkMetadataRequest\x12\x15\n" +
"\x03url\x18\x01 \x01(\tB\x03\xe0A\x02R\x03url\"6\n" +
"\x1bBatchGetLinkMetadataRequest\x12\x17\n" +
"\x04urls\x18\x01 \x03(\tB\x03\xe0A\x02R\x04urls\"_\n" +
"\x1cBatchGetLinkMetadataResponse\x12?\n" +
"\rlink_metadata\x18\x01 \x03(\v2\x1a.memos.api.v1.LinkMetadataR\flinkMetadata\"n\n" +
"\fLinkMetadata\x12\x10\n" +
"\x03url\x18\x01 \x01(\tR\x03url\x12\x14\n" +
"\x05title\x18\x02 \x01(\tR\x05title\x12 \n" +
"\vdescription\x18\x03 \x01(\tR\vdescription\x12\x14\n" +
"\x05image\x18\x04 \x01(\tR\x05image*P\n" +
"\n" +
"Visibility\x12\x1a\n" +
"\x16VISIBILITY_UNSPECIFIED\x10\x00\x12\v\n" +
"\aPRIVATE\x10\x01\x12\r\n" +
"\tPROTECTED\x10\x02\x12\n" +
"\n" +
"\x06PUBLIC\x10\x032\xee\x12\n" +
"\x06PUBLIC\x10\x032\x8b\x15\n" +
"\vMemoService\x12e\n" +
"\n" +
"CreateMemo\x12\x1f.memos.api.v1.CreateMemoRequest\x1a\x12.memos.api.v1.Memo\"\"\xdaA\x04memo\x82\xd3\xe4\x93\x02\x15:\x04memo\"\r/api/v1/memos\x12f\n" +
@ -2305,7 +2523,9 @@ const file_api_v1_memo_service_proto_rawDesc = "" +
"memo_share\"\x1f/api/v1/{parent=memos/*}/shares\x12\x8d\x01\n" +
"\x0eListMemoShares\x12#.memos.api.v1.ListMemoSharesRequest\x1a$.memos.api.v1.ListMemoSharesResponse\"0\xdaA\x06parent\x82\xd3\xe4\x93\x02!\x12\x1f/api/v1/{parent=memos/*}/shares\x12\x7f\n" +
"\x0fDeleteMemoShare\x12$.memos.api.v1.DeleteMemoShareRequest\x1a\x16.google.protobuf.Empty\".\xdaA\x04name\x82\xd3\xe4\x93\x02!*\x1f/api/v1/{name=memos/*/shares/*}\x12l\n" +
"\x0eGetMemoByShare\x12#.memos.api.v1.GetMemoByShareRequest\x1a\x12.memos.api.v1.Memo\"!\x82\xd3\xe4\x93\x02\x1b\x12\x19/api/v1/shares/{share_id}B\xa8\x01\n" +
"\x0eGetMemoByShare\x12#.memos.api.v1.GetMemoByShareRequest\x1a\x12.memos.api.v1.Memo\"!\x82\xd3\xe4\x93\x02\x1b\x12\x19/api/v1/shares/{share_id}\x12y\n" +
"\x0fGetLinkMetadata\x12$.memos.api.v1.GetLinkMetadataRequest\x1a\x1a.memos.api.v1.LinkMetadata\"$\x82\xd3\xe4\x93\x02\x1e\x12\x1c/api/v1/memos/-/linkMetadata\x12\x9f\x01\n" +
"\x14BatchGetLinkMetadata\x12).memos.api.v1.BatchGetLinkMetadataRequest\x1a*.memos.api.v1.BatchGetLinkMetadataResponse\"0\x82\xd3\xe4\x93\x02*:\x01*\"%/api/v1/memos/-/linkMetadata:batchGetB\xa8\x01\n" +
"\x10com.memos.api.v1B\x10MemoServiceProtoP\x01Z0github.com/usememos/memos/proto/gen/api/v1;apiv1\xa2\x02\x03MAX\xaa\x02\fMemos.Api.V1\xca\x02\fMemos\\Api\\V1\xe2\x02\x18Memos\\Api\\V1\\GPBMetadata\xea\x02\x0eMemos::Api::V1b\x06proto3"
var (
@ -2321,67 +2541,71 @@ func file_api_v1_memo_service_proto_rawDescGZIP() []byte {
}
var file_api_v1_memo_service_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_api_v1_memo_service_proto_msgTypes = make([]protoimpl.MessageInfo, 31)
var file_api_v1_memo_service_proto_msgTypes = make([]protoimpl.MessageInfo, 35)
var file_api_v1_memo_service_proto_goTypes = []any{
(Visibility)(0), // 0: memos.api.v1.Visibility
(MemoRelation_Type)(0), // 1: memos.api.v1.MemoRelation.Type
(*Reaction)(nil), // 2: memos.api.v1.Reaction
(*Memo)(nil), // 3: memos.api.v1.Memo
(*Location)(nil), // 4: memos.api.v1.Location
(*CreateMemoRequest)(nil), // 5: memos.api.v1.CreateMemoRequest
(*ListMemosRequest)(nil), // 6: memos.api.v1.ListMemosRequest
(*ListMemosResponse)(nil), // 7: memos.api.v1.ListMemosResponse
(*GetMemoRequest)(nil), // 8: memos.api.v1.GetMemoRequest
(*UpdateMemoRequest)(nil), // 9: memos.api.v1.UpdateMemoRequest
(*DeleteMemoRequest)(nil), // 10: memos.api.v1.DeleteMemoRequest
(*SetMemoAttachmentsRequest)(nil), // 11: memos.api.v1.SetMemoAttachmentsRequest
(*ListMemoAttachmentsRequest)(nil), // 12: memos.api.v1.ListMemoAttachmentsRequest
(*ListMemoAttachmentsResponse)(nil), // 13: memos.api.v1.ListMemoAttachmentsResponse
(*MemoRelation)(nil), // 14: memos.api.v1.MemoRelation
(*SetMemoRelationsRequest)(nil), // 15: memos.api.v1.SetMemoRelationsRequest
(*ListMemoRelationsRequest)(nil), // 16: memos.api.v1.ListMemoRelationsRequest
(*ListMemoRelationsResponse)(nil), // 17: memos.api.v1.ListMemoRelationsResponse
(*CreateMemoCommentRequest)(nil), // 18: memos.api.v1.CreateMemoCommentRequest
(*ListMemoCommentsRequest)(nil), // 19: memos.api.v1.ListMemoCommentsRequest
(*ListMemoCommentsResponse)(nil), // 20: memos.api.v1.ListMemoCommentsResponse
(*ListMemoReactionsRequest)(nil), // 21: memos.api.v1.ListMemoReactionsRequest
(*ListMemoReactionsResponse)(nil), // 22: memos.api.v1.ListMemoReactionsResponse
(*UpsertMemoReactionRequest)(nil), // 23: memos.api.v1.UpsertMemoReactionRequest
(*DeleteMemoReactionRequest)(nil), // 24: memos.api.v1.DeleteMemoReactionRequest
(*MemoShare)(nil), // 25: memos.api.v1.MemoShare
(*CreateMemoShareRequest)(nil), // 26: memos.api.v1.CreateMemoShareRequest
(*ListMemoSharesRequest)(nil), // 27: memos.api.v1.ListMemoSharesRequest
(*ListMemoSharesResponse)(nil), // 28: memos.api.v1.ListMemoSharesResponse
(*DeleteMemoShareRequest)(nil), // 29: memos.api.v1.DeleteMemoShareRequest
(*GetMemoByShareRequest)(nil), // 30: memos.api.v1.GetMemoByShareRequest
(*Memo_Property)(nil), // 31: memos.api.v1.Memo.Property
(*MemoRelation_Memo)(nil), // 32: memos.api.v1.MemoRelation.Memo
(*timestamppb.Timestamp)(nil), // 33: google.protobuf.Timestamp
(State)(0), // 34: memos.api.v1.State
(*Attachment)(nil), // 35: memos.api.v1.Attachment
(*fieldmaskpb.FieldMask)(nil), // 36: google.protobuf.FieldMask
(*emptypb.Empty)(nil), // 37: google.protobuf.Empty
(Visibility)(0), // 0: memos.api.v1.Visibility
(MemoRelation_Type)(0), // 1: memos.api.v1.MemoRelation.Type
(*Reaction)(nil), // 2: memos.api.v1.Reaction
(*Memo)(nil), // 3: memos.api.v1.Memo
(*Location)(nil), // 4: memos.api.v1.Location
(*CreateMemoRequest)(nil), // 5: memos.api.v1.CreateMemoRequest
(*ListMemosRequest)(nil), // 6: memos.api.v1.ListMemosRequest
(*ListMemosResponse)(nil), // 7: memos.api.v1.ListMemosResponse
(*GetMemoRequest)(nil), // 8: memos.api.v1.GetMemoRequest
(*UpdateMemoRequest)(nil), // 9: memos.api.v1.UpdateMemoRequest
(*DeleteMemoRequest)(nil), // 10: memos.api.v1.DeleteMemoRequest
(*SetMemoAttachmentsRequest)(nil), // 11: memos.api.v1.SetMemoAttachmentsRequest
(*ListMemoAttachmentsRequest)(nil), // 12: memos.api.v1.ListMemoAttachmentsRequest
(*ListMemoAttachmentsResponse)(nil), // 13: memos.api.v1.ListMemoAttachmentsResponse
(*MemoRelation)(nil), // 14: memos.api.v1.MemoRelation
(*SetMemoRelationsRequest)(nil), // 15: memos.api.v1.SetMemoRelationsRequest
(*ListMemoRelationsRequest)(nil), // 16: memos.api.v1.ListMemoRelationsRequest
(*ListMemoRelationsResponse)(nil), // 17: memos.api.v1.ListMemoRelationsResponse
(*CreateMemoCommentRequest)(nil), // 18: memos.api.v1.CreateMemoCommentRequest
(*ListMemoCommentsRequest)(nil), // 19: memos.api.v1.ListMemoCommentsRequest
(*ListMemoCommentsResponse)(nil), // 20: memos.api.v1.ListMemoCommentsResponse
(*ListMemoReactionsRequest)(nil), // 21: memos.api.v1.ListMemoReactionsRequest
(*ListMemoReactionsResponse)(nil), // 22: memos.api.v1.ListMemoReactionsResponse
(*UpsertMemoReactionRequest)(nil), // 23: memos.api.v1.UpsertMemoReactionRequest
(*DeleteMemoReactionRequest)(nil), // 24: memos.api.v1.DeleteMemoReactionRequest
(*MemoShare)(nil), // 25: memos.api.v1.MemoShare
(*CreateMemoShareRequest)(nil), // 26: memos.api.v1.CreateMemoShareRequest
(*ListMemoSharesRequest)(nil), // 27: memos.api.v1.ListMemoSharesRequest
(*ListMemoSharesResponse)(nil), // 28: memos.api.v1.ListMemoSharesResponse
(*DeleteMemoShareRequest)(nil), // 29: memos.api.v1.DeleteMemoShareRequest
(*GetMemoByShareRequest)(nil), // 30: memos.api.v1.GetMemoByShareRequest
(*GetLinkMetadataRequest)(nil), // 31: memos.api.v1.GetLinkMetadataRequest
(*BatchGetLinkMetadataRequest)(nil), // 32: memos.api.v1.BatchGetLinkMetadataRequest
(*BatchGetLinkMetadataResponse)(nil), // 33: memos.api.v1.BatchGetLinkMetadataResponse
(*LinkMetadata)(nil), // 34: memos.api.v1.LinkMetadata
(*Memo_Property)(nil), // 35: memos.api.v1.Memo.Property
(*MemoRelation_Memo)(nil), // 36: memos.api.v1.MemoRelation.Memo
(*timestamppb.Timestamp)(nil), // 37: google.protobuf.Timestamp
(State)(0), // 38: memos.api.v1.State
(*Attachment)(nil), // 39: memos.api.v1.Attachment
(*fieldmaskpb.FieldMask)(nil), // 40: google.protobuf.FieldMask
(*emptypb.Empty)(nil), // 41: google.protobuf.Empty
}
var file_api_v1_memo_service_proto_depIdxs = []int32{
33, // 0: memos.api.v1.Reaction.create_time:type_name -> google.protobuf.Timestamp
34, // 1: memos.api.v1.Memo.state:type_name -> memos.api.v1.State
33, // 2: memos.api.v1.Memo.create_time:type_name -> google.protobuf.Timestamp
33, // 3: memos.api.v1.Memo.update_time:type_name -> google.protobuf.Timestamp
37, // 0: memos.api.v1.Reaction.create_time:type_name -> google.protobuf.Timestamp
38, // 1: memos.api.v1.Memo.state:type_name -> memos.api.v1.State
37, // 2: memos.api.v1.Memo.create_time:type_name -> google.protobuf.Timestamp
37, // 3: memos.api.v1.Memo.update_time:type_name -> google.protobuf.Timestamp
0, // 4: memos.api.v1.Memo.visibility:type_name -> memos.api.v1.Visibility
35, // 5: memos.api.v1.Memo.attachments:type_name -> memos.api.v1.Attachment
39, // 5: memos.api.v1.Memo.attachments:type_name -> memos.api.v1.Attachment
14, // 6: memos.api.v1.Memo.relations:type_name -> memos.api.v1.MemoRelation
2, // 7: memos.api.v1.Memo.reactions:type_name -> memos.api.v1.Reaction
31, // 8: memos.api.v1.Memo.property:type_name -> memos.api.v1.Memo.Property
35, // 8: memos.api.v1.Memo.property:type_name -> memos.api.v1.Memo.Property
4, // 9: memos.api.v1.Memo.location:type_name -> memos.api.v1.Location
3, // 10: memos.api.v1.CreateMemoRequest.memo:type_name -> memos.api.v1.Memo
34, // 11: memos.api.v1.ListMemosRequest.state:type_name -> memos.api.v1.State
38, // 11: memos.api.v1.ListMemosRequest.state:type_name -> memos.api.v1.State
3, // 12: memos.api.v1.ListMemosResponse.memos:type_name -> memos.api.v1.Memo
3, // 13: memos.api.v1.UpdateMemoRequest.memo:type_name -> memos.api.v1.Memo
36, // 14: memos.api.v1.UpdateMemoRequest.update_mask:type_name -> google.protobuf.FieldMask
35, // 15: memos.api.v1.SetMemoAttachmentsRequest.attachments:type_name -> memos.api.v1.Attachment
35, // 16: memos.api.v1.ListMemoAttachmentsResponse.attachments:type_name -> memos.api.v1.Attachment
32, // 17: memos.api.v1.MemoRelation.memo:type_name -> memos.api.v1.MemoRelation.Memo
32, // 18: memos.api.v1.MemoRelation.related_memo:type_name -> memos.api.v1.MemoRelation.Memo
40, // 14: memos.api.v1.UpdateMemoRequest.update_mask:type_name -> google.protobuf.FieldMask
39, // 15: memos.api.v1.SetMemoAttachmentsRequest.attachments:type_name -> memos.api.v1.Attachment
39, // 16: memos.api.v1.ListMemoAttachmentsResponse.attachments:type_name -> memos.api.v1.Attachment
36, // 17: memos.api.v1.MemoRelation.memo:type_name -> memos.api.v1.MemoRelation.Memo
36, // 18: memos.api.v1.MemoRelation.related_memo:type_name -> memos.api.v1.MemoRelation.Memo
1, // 19: memos.api.v1.MemoRelation.type:type_name -> memos.api.v1.MemoRelation.Type
14, // 20: memos.api.v1.SetMemoRelationsRequest.relations:type_name -> memos.api.v1.MemoRelation
14, // 21: memos.api.v1.ListMemoRelationsResponse.relations:type_name -> memos.api.v1.MemoRelation
@ -2389,51 +2613,56 @@ var file_api_v1_memo_service_proto_depIdxs = []int32{
3, // 23: memos.api.v1.ListMemoCommentsResponse.memos:type_name -> memos.api.v1.Memo
2, // 24: memos.api.v1.ListMemoReactionsResponse.reactions:type_name -> memos.api.v1.Reaction
2, // 25: memos.api.v1.UpsertMemoReactionRequest.reaction:type_name -> memos.api.v1.Reaction
33, // 26: memos.api.v1.MemoShare.create_time:type_name -> google.protobuf.Timestamp
33, // 27: memos.api.v1.MemoShare.expire_time:type_name -> google.protobuf.Timestamp
37, // 26: memos.api.v1.MemoShare.create_time:type_name -> google.protobuf.Timestamp
37, // 27: memos.api.v1.MemoShare.expire_time:type_name -> google.protobuf.Timestamp
25, // 28: memos.api.v1.CreateMemoShareRequest.memo_share:type_name -> memos.api.v1.MemoShare
25, // 29: memos.api.v1.ListMemoSharesResponse.memo_shares:type_name -> memos.api.v1.MemoShare
5, // 30: memos.api.v1.MemoService.CreateMemo:input_type -> memos.api.v1.CreateMemoRequest
6, // 31: memos.api.v1.MemoService.ListMemos:input_type -> memos.api.v1.ListMemosRequest
8, // 32: memos.api.v1.MemoService.GetMemo:input_type -> memos.api.v1.GetMemoRequest
9, // 33: memos.api.v1.MemoService.UpdateMemo:input_type -> memos.api.v1.UpdateMemoRequest
10, // 34: memos.api.v1.MemoService.DeleteMemo:input_type -> memos.api.v1.DeleteMemoRequest
11, // 35: memos.api.v1.MemoService.SetMemoAttachments:input_type -> memos.api.v1.SetMemoAttachmentsRequest
12, // 36: memos.api.v1.MemoService.ListMemoAttachments:input_type -> memos.api.v1.ListMemoAttachmentsRequest
15, // 37: memos.api.v1.MemoService.SetMemoRelations:input_type -> memos.api.v1.SetMemoRelationsRequest
16, // 38: memos.api.v1.MemoService.ListMemoRelations:input_type -> memos.api.v1.ListMemoRelationsRequest
18, // 39: memos.api.v1.MemoService.CreateMemoComment:input_type -> memos.api.v1.CreateMemoCommentRequest
19, // 40: memos.api.v1.MemoService.ListMemoComments:input_type -> memos.api.v1.ListMemoCommentsRequest
21, // 41: memos.api.v1.MemoService.ListMemoReactions:input_type -> memos.api.v1.ListMemoReactionsRequest
23, // 42: memos.api.v1.MemoService.UpsertMemoReaction:input_type -> memos.api.v1.UpsertMemoReactionRequest
24, // 43: memos.api.v1.MemoService.DeleteMemoReaction:input_type -> memos.api.v1.DeleteMemoReactionRequest
26, // 44: memos.api.v1.MemoService.CreateMemoShare:input_type -> memos.api.v1.CreateMemoShareRequest
27, // 45: memos.api.v1.MemoService.ListMemoShares:input_type -> memos.api.v1.ListMemoSharesRequest
29, // 46: memos.api.v1.MemoService.DeleteMemoShare:input_type -> memos.api.v1.DeleteMemoShareRequest
30, // 47: memos.api.v1.MemoService.GetMemoByShare:input_type -> memos.api.v1.GetMemoByShareRequest
3, // 48: memos.api.v1.MemoService.CreateMemo:output_type -> memos.api.v1.Memo
7, // 49: memos.api.v1.MemoService.ListMemos:output_type -> memos.api.v1.ListMemosResponse
3, // 50: memos.api.v1.MemoService.GetMemo:output_type -> memos.api.v1.Memo
3, // 51: memos.api.v1.MemoService.UpdateMemo:output_type -> memos.api.v1.Memo
37, // 52: memos.api.v1.MemoService.DeleteMemo:output_type -> google.protobuf.Empty
37, // 53: memos.api.v1.MemoService.SetMemoAttachments:output_type -> google.protobuf.Empty
13, // 54: memos.api.v1.MemoService.ListMemoAttachments:output_type -> memos.api.v1.ListMemoAttachmentsResponse
37, // 55: memos.api.v1.MemoService.SetMemoRelations:output_type -> google.protobuf.Empty
17, // 56: memos.api.v1.MemoService.ListMemoRelations:output_type -> memos.api.v1.ListMemoRelationsResponse
3, // 57: memos.api.v1.MemoService.CreateMemoComment:output_type -> memos.api.v1.Memo
20, // 58: memos.api.v1.MemoService.ListMemoComments:output_type -> memos.api.v1.ListMemoCommentsResponse
22, // 59: memos.api.v1.MemoService.ListMemoReactions:output_type -> memos.api.v1.ListMemoReactionsResponse
2, // 60: memos.api.v1.MemoService.UpsertMemoReaction:output_type -> memos.api.v1.Reaction
37, // 61: memos.api.v1.MemoService.DeleteMemoReaction:output_type -> google.protobuf.Empty
25, // 62: memos.api.v1.MemoService.CreateMemoShare:output_type -> memos.api.v1.MemoShare
28, // 63: memos.api.v1.MemoService.ListMemoShares:output_type -> memos.api.v1.ListMemoSharesResponse
37, // 64: memos.api.v1.MemoService.DeleteMemoShare:output_type -> google.protobuf.Empty
3, // 65: memos.api.v1.MemoService.GetMemoByShare:output_type -> memos.api.v1.Memo
48, // [48:66] is the sub-list for method output_type
30, // [30:48] is the sub-list for method input_type
30, // [30:30] is the sub-list for extension type_name
30, // [30:30] is the sub-list for extension extendee
0, // [0:30] is the sub-list for field type_name
34, // 30: memos.api.v1.BatchGetLinkMetadataResponse.link_metadata:type_name -> memos.api.v1.LinkMetadata
5, // 31: memos.api.v1.MemoService.CreateMemo:input_type -> memos.api.v1.CreateMemoRequest
6, // 32: memos.api.v1.MemoService.ListMemos:input_type -> memos.api.v1.ListMemosRequest
8, // 33: memos.api.v1.MemoService.GetMemo:input_type -> memos.api.v1.GetMemoRequest
9, // 34: memos.api.v1.MemoService.UpdateMemo:input_type -> memos.api.v1.UpdateMemoRequest
10, // 35: memos.api.v1.MemoService.DeleteMemo:input_type -> memos.api.v1.DeleteMemoRequest
11, // 36: memos.api.v1.MemoService.SetMemoAttachments:input_type -> memos.api.v1.SetMemoAttachmentsRequest
12, // 37: memos.api.v1.MemoService.ListMemoAttachments:input_type -> memos.api.v1.ListMemoAttachmentsRequest
15, // 38: memos.api.v1.MemoService.SetMemoRelations:input_type -> memos.api.v1.SetMemoRelationsRequest
16, // 39: memos.api.v1.MemoService.ListMemoRelations:input_type -> memos.api.v1.ListMemoRelationsRequest
18, // 40: memos.api.v1.MemoService.CreateMemoComment:input_type -> memos.api.v1.CreateMemoCommentRequest
19, // 41: memos.api.v1.MemoService.ListMemoComments:input_type -> memos.api.v1.ListMemoCommentsRequest
21, // 42: memos.api.v1.MemoService.ListMemoReactions:input_type -> memos.api.v1.ListMemoReactionsRequest
23, // 43: memos.api.v1.MemoService.UpsertMemoReaction:input_type -> memos.api.v1.UpsertMemoReactionRequest
24, // 44: memos.api.v1.MemoService.DeleteMemoReaction:input_type -> memos.api.v1.DeleteMemoReactionRequest
26, // 45: memos.api.v1.MemoService.CreateMemoShare:input_type -> memos.api.v1.CreateMemoShareRequest
27, // 46: memos.api.v1.MemoService.ListMemoShares:input_type -> memos.api.v1.ListMemoSharesRequest
29, // 47: memos.api.v1.MemoService.DeleteMemoShare:input_type -> memos.api.v1.DeleteMemoShareRequest
30, // 48: memos.api.v1.MemoService.GetMemoByShare:input_type -> memos.api.v1.GetMemoByShareRequest
31, // 49: memos.api.v1.MemoService.GetLinkMetadata:input_type -> memos.api.v1.GetLinkMetadataRequest
32, // 50: memos.api.v1.MemoService.BatchGetLinkMetadata:input_type -> memos.api.v1.BatchGetLinkMetadataRequest
3, // 51: memos.api.v1.MemoService.CreateMemo:output_type -> memos.api.v1.Memo
7, // 52: memos.api.v1.MemoService.ListMemos:output_type -> memos.api.v1.ListMemosResponse
3, // 53: memos.api.v1.MemoService.GetMemo:output_type -> memos.api.v1.Memo
3, // 54: memos.api.v1.MemoService.UpdateMemo:output_type -> memos.api.v1.Memo
41, // 55: memos.api.v1.MemoService.DeleteMemo:output_type -> google.protobuf.Empty
41, // 56: memos.api.v1.MemoService.SetMemoAttachments:output_type -> google.protobuf.Empty
13, // 57: memos.api.v1.MemoService.ListMemoAttachments:output_type -> memos.api.v1.ListMemoAttachmentsResponse
41, // 58: memos.api.v1.MemoService.SetMemoRelations:output_type -> google.protobuf.Empty
17, // 59: memos.api.v1.MemoService.ListMemoRelations:output_type -> memos.api.v1.ListMemoRelationsResponse
3, // 60: memos.api.v1.MemoService.CreateMemoComment:output_type -> memos.api.v1.Memo
20, // 61: memos.api.v1.MemoService.ListMemoComments:output_type -> memos.api.v1.ListMemoCommentsResponse
22, // 62: memos.api.v1.MemoService.ListMemoReactions:output_type -> memos.api.v1.ListMemoReactionsResponse
2, // 63: memos.api.v1.MemoService.UpsertMemoReaction:output_type -> memos.api.v1.Reaction
41, // 64: memos.api.v1.MemoService.DeleteMemoReaction:output_type -> google.protobuf.Empty
25, // 65: memos.api.v1.MemoService.CreateMemoShare:output_type -> memos.api.v1.MemoShare
28, // 66: memos.api.v1.MemoService.ListMemoShares:output_type -> memos.api.v1.ListMemoSharesResponse
41, // 67: memos.api.v1.MemoService.DeleteMemoShare:output_type -> google.protobuf.Empty
3, // 68: memos.api.v1.MemoService.GetMemoByShare:output_type -> memos.api.v1.Memo
34, // 69: memos.api.v1.MemoService.GetLinkMetadata:output_type -> memos.api.v1.LinkMetadata
33, // 70: memos.api.v1.MemoService.BatchGetLinkMetadata:output_type -> memos.api.v1.BatchGetLinkMetadataResponse
51, // [51:71] is the sub-list for method output_type
31, // [31:51] is the sub-list for method input_type
31, // [31:31] is the sub-list for extension type_name
31, // [31:31] is the sub-list for extension extendee
0, // [0:31] is the sub-list for field type_name
}
func init() { file_api_v1_memo_service_proto_init() }
@ -2451,7 +2680,7 @@ func file_api_v1_memo_service_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v1_memo_service_proto_rawDesc), len(file_api_v1_memo_service_proto_rawDesc)),
NumEnums: 2,
NumMessages: 31,
NumMessages: 35,
NumExtensions: 0,
NumServices: 1,
},

@ -891,6 +891,68 @@ func local_request_MemoService_GetMemoByShare_0(ctx context.Context, marshaler r
return msg, metadata, err
}
var filter_MemoService_GetLinkMetadata_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
func request_MemoService_GetLinkMetadata_0(ctx context.Context, marshaler runtime.Marshaler, client MemoServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetLinkMetadataRequest
metadata runtime.ServerMetadata
)
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_MemoService_GetLinkMetadata_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GetLinkMetadata(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_MemoService_GetLinkMetadata_0(ctx context.Context, marshaler runtime.Marshaler, server MemoServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetLinkMetadataRequest
metadata runtime.ServerMetadata
)
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_MemoService_GetLinkMetadata_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GetLinkMetadata(ctx, &protoReq)
return msg, metadata, err
}
func request_MemoService_BatchGetLinkMetadata_0(ctx context.Context, marshaler runtime.Marshaler, client MemoServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq BatchGetLinkMetadataRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.BatchGetLinkMetadata(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_MemoService_BatchGetLinkMetadata_0(ctx context.Context, marshaler runtime.Marshaler, server MemoServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq BatchGetLinkMetadataRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.BatchGetLinkMetadata(ctx, &protoReq)
return msg, metadata, err
}
// RegisterMemoServiceHandlerServer registers the http handlers for service MemoService to "mux".
// UnaryRPC :call MemoServiceServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@ -1257,6 +1319,46 @@ func RegisterMemoServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
}
forward_MemoService_GetMemoByShare_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodGet, pattern_MemoService_GetLinkMetadata_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.MemoService/GetLinkMetadata", runtime.WithHTTPPathPattern("/api/v1/memos/-/linkMetadata"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_MemoService_GetLinkMetadata_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_MemoService_GetLinkMetadata_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_MemoService_BatchGetLinkMetadata_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.MemoService/BatchGetLinkMetadata", runtime.WithHTTPPathPattern("/api/v1/memos/-/linkMetadata:batchGet"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_MemoService_BatchGetLinkMetadata_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_MemoService_BatchGetLinkMetadata_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@ -1603,47 +1705,85 @@ func RegisterMemoServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
}
forward_MemoService_GetMemoByShare_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodGet, pattern_MemoService_GetLinkMetadata_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.MemoService/GetLinkMetadata", runtime.WithHTTPPathPattern("/api/v1/memos/-/linkMetadata"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_MemoService_GetLinkMetadata_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MemoService_GetLinkMetadata_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_MemoService_BatchGetLinkMetadata_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.MemoService/BatchGetLinkMetadata", runtime.WithHTTPPathPattern("/api/v1/memos/-/linkMetadata:batchGet"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_MemoService_BatchGetLinkMetadata_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MemoService_BatchGetLinkMetadata_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_MemoService_CreateMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "memos"}, ""))
pattern_MemoService_ListMemos_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "memos"}, ""))
pattern_MemoService_GetMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "memos", "name"}, ""))
pattern_MemoService_UpdateMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "memos", "memo.name"}, ""))
pattern_MemoService_DeleteMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "memos", "name"}, ""))
pattern_MemoService_SetMemoAttachments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "attachments"}, ""))
pattern_MemoService_ListMemoAttachments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "attachments"}, ""))
pattern_MemoService_SetMemoRelations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "relations"}, ""))
pattern_MemoService_ListMemoRelations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "relations"}, ""))
pattern_MemoService_CreateMemoComment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "comments"}, ""))
pattern_MemoService_ListMemoComments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "comments"}, ""))
pattern_MemoService_ListMemoReactions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "reactions"}, ""))
pattern_MemoService_UpsertMemoReaction_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "reactions"}, ""))
pattern_MemoService_DeleteMemoReaction_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", "memos", "reactions", "name"}, ""))
pattern_MemoService_CreateMemoShare_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "parent", "shares"}, ""))
pattern_MemoService_ListMemoShares_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "parent", "shares"}, ""))
pattern_MemoService_DeleteMemoShare_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", "memos", "shares", "name"}, ""))
pattern_MemoService_GetMemoByShare_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "shares", "share_id"}, ""))
pattern_MemoService_CreateMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "memos"}, ""))
pattern_MemoService_ListMemos_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "memos"}, ""))
pattern_MemoService_GetMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "memos", "name"}, ""))
pattern_MemoService_UpdateMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "memos", "memo.name"}, ""))
pattern_MemoService_DeleteMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "memos", "name"}, ""))
pattern_MemoService_SetMemoAttachments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "attachments"}, ""))
pattern_MemoService_ListMemoAttachments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "attachments"}, ""))
pattern_MemoService_SetMemoRelations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "relations"}, ""))
pattern_MemoService_ListMemoRelations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "relations"}, ""))
pattern_MemoService_CreateMemoComment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "comments"}, ""))
pattern_MemoService_ListMemoComments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "comments"}, ""))
pattern_MemoService_ListMemoReactions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "reactions"}, ""))
pattern_MemoService_UpsertMemoReaction_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "reactions"}, ""))
pattern_MemoService_DeleteMemoReaction_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", "memos", "reactions", "name"}, ""))
pattern_MemoService_CreateMemoShare_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "parent", "shares"}, ""))
pattern_MemoService_ListMemoShares_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "parent", "shares"}, ""))
pattern_MemoService_DeleteMemoShare_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", "memos", "shares", "name"}, ""))
pattern_MemoService_GetMemoByShare_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "shares", "share_id"}, ""))
pattern_MemoService_GetLinkMetadata_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"api", "v1", "memos", "-", "linkMetadata"}, ""))
pattern_MemoService_BatchGetLinkMetadata_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"api", "v1", "memos", "-", "linkMetadata"}, "batchGet"))
)
var (
forward_MemoService_CreateMemo_0 = runtime.ForwardResponseMessage
forward_MemoService_ListMemos_0 = runtime.ForwardResponseMessage
forward_MemoService_GetMemo_0 = runtime.ForwardResponseMessage
forward_MemoService_UpdateMemo_0 = runtime.ForwardResponseMessage
forward_MemoService_DeleteMemo_0 = runtime.ForwardResponseMessage
forward_MemoService_SetMemoAttachments_0 = runtime.ForwardResponseMessage
forward_MemoService_ListMemoAttachments_0 = runtime.ForwardResponseMessage
forward_MemoService_SetMemoRelations_0 = runtime.ForwardResponseMessage
forward_MemoService_ListMemoRelations_0 = runtime.ForwardResponseMessage
forward_MemoService_CreateMemoComment_0 = runtime.ForwardResponseMessage
forward_MemoService_ListMemoComments_0 = runtime.ForwardResponseMessage
forward_MemoService_ListMemoReactions_0 = runtime.ForwardResponseMessage
forward_MemoService_UpsertMemoReaction_0 = runtime.ForwardResponseMessage
forward_MemoService_DeleteMemoReaction_0 = runtime.ForwardResponseMessage
forward_MemoService_CreateMemoShare_0 = runtime.ForwardResponseMessage
forward_MemoService_ListMemoShares_0 = runtime.ForwardResponseMessage
forward_MemoService_DeleteMemoShare_0 = runtime.ForwardResponseMessage
forward_MemoService_GetMemoByShare_0 = runtime.ForwardResponseMessage
forward_MemoService_CreateMemo_0 = runtime.ForwardResponseMessage
forward_MemoService_ListMemos_0 = runtime.ForwardResponseMessage
forward_MemoService_GetMemo_0 = runtime.ForwardResponseMessage
forward_MemoService_UpdateMemo_0 = runtime.ForwardResponseMessage
forward_MemoService_DeleteMemo_0 = runtime.ForwardResponseMessage
forward_MemoService_SetMemoAttachments_0 = runtime.ForwardResponseMessage
forward_MemoService_ListMemoAttachments_0 = runtime.ForwardResponseMessage
forward_MemoService_SetMemoRelations_0 = runtime.ForwardResponseMessage
forward_MemoService_ListMemoRelations_0 = runtime.ForwardResponseMessage
forward_MemoService_CreateMemoComment_0 = runtime.ForwardResponseMessage
forward_MemoService_ListMemoComments_0 = runtime.ForwardResponseMessage
forward_MemoService_ListMemoReactions_0 = runtime.ForwardResponseMessage
forward_MemoService_UpsertMemoReaction_0 = runtime.ForwardResponseMessage
forward_MemoService_DeleteMemoReaction_0 = runtime.ForwardResponseMessage
forward_MemoService_CreateMemoShare_0 = runtime.ForwardResponseMessage
forward_MemoService_ListMemoShares_0 = runtime.ForwardResponseMessage
forward_MemoService_DeleteMemoShare_0 = runtime.ForwardResponseMessage
forward_MemoService_GetMemoByShare_0 = runtime.ForwardResponseMessage
forward_MemoService_GetLinkMetadata_0 = runtime.ForwardResponseMessage
forward_MemoService_BatchGetLinkMetadata_0 = runtime.ForwardResponseMessage
)

@ -20,24 +20,26 @@ import (
const _ = grpc.SupportPackageIsVersion9
const (
MemoService_CreateMemo_FullMethodName = "/memos.api.v1.MemoService/CreateMemo"
MemoService_ListMemos_FullMethodName = "/memos.api.v1.MemoService/ListMemos"
MemoService_GetMemo_FullMethodName = "/memos.api.v1.MemoService/GetMemo"
MemoService_UpdateMemo_FullMethodName = "/memos.api.v1.MemoService/UpdateMemo"
MemoService_DeleteMemo_FullMethodName = "/memos.api.v1.MemoService/DeleteMemo"
MemoService_SetMemoAttachments_FullMethodName = "/memos.api.v1.MemoService/SetMemoAttachments"
MemoService_ListMemoAttachments_FullMethodName = "/memos.api.v1.MemoService/ListMemoAttachments"
MemoService_SetMemoRelations_FullMethodName = "/memos.api.v1.MemoService/SetMemoRelations"
MemoService_ListMemoRelations_FullMethodName = "/memos.api.v1.MemoService/ListMemoRelations"
MemoService_CreateMemoComment_FullMethodName = "/memos.api.v1.MemoService/CreateMemoComment"
MemoService_ListMemoComments_FullMethodName = "/memos.api.v1.MemoService/ListMemoComments"
MemoService_ListMemoReactions_FullMethodName = "/memos.api.v1.MemoService/ListMemoReactions"
MemoService_UpsertMemoReaction_FullMethodName = "/memos.api.v1.MemoService/UpsertMemoReaction"
MemoService_DeleteMemoReaction_FullMethodName = "/memos.api.v1.MemoService/DeleteMemoReaction"
MemoService_CreateMemoShare_FullMethodName = "/memos.api.v1.MemoService/CreateMemoShare"
MemoService_ListMemoShares_FullMethodName = "/memos.api.v1.MemoService/ListMemoShares"
MemoService_DeleteMemoShare_FullMethodName = "/memos.api.v1.MemoService/DeleteMemoShare"
MemoService_GetMemoByShare_FullMethodName = "/memos.api.v1.MemoService/GetMemoByShare"
MemoService_CreateMemo_FullMethodName = "/memos.api.v1.MemoService/CreateMemo"
MemoService_ListMemos_FullMethodName = "/memos.api.v1.MemoService/ListMemos"
MemoService_GetMemo_FullMethodName = "/memos.api.v1.MemoService/GetMemo"
MemoService_UpdateMemo_FullMethodName = "/memos.api.v1.MemoService/UpdateMemo"
MemoService_DeleteMemo_FullMethodName = "/memos.api.v1.MemoService/DeleteMemo"
MemoService_SetMemoAttachments_FullMethodName = "/memos.api.v1.MemoService/SetMemoAttachments"
MemoService_ListMemoAttachments_FullMethodName = "/memos.api.v1.MemoService/ListMemoAttachments"
MemoService_SetMemoRelations_FullMethodName = "/memos.api.v1.MemoService/SetMemoRelations"
MemoService_ListMemoRelations_FullMethodName = "/memos.api.v1.MemoService/ListMemoRelations"
MemoService_CreateMemoComment_FullMethodName = "/memos.api.v1.MemoService/CreateMemoComment"
MemoService_ListMemoComments_FullMethodName = "/memos.api.v1.MemoService/ListMemoComments"
MemoService_ListMemoReactions_FullMethodName = "/memos.api.v1.MemoService/ListMemoReactions"
MemoService_UpsertMemoReaction_FullMethodName = "/memos.api.v1.MemoService/UpsertMemoReaction"
MemoService_DeleteMemoReaction_FullMethodName = "/memos.api.v1.MemoService/DeleteMemoReaction"
MemoService_CreateMemoShare_FullMethodName = "/memos.api.v1.MemoService/CreateMemoShare"
MemoService_ListMemoShares_FullMethodName = "/memos.api.v1.MemoService/ListMemoShares"
MemoService_DeleteMemoShare_FullMethodName = "/memos.api.v1.MemoService/DeleteMemoShare"
MemoService_GetMemoByShare_FullMethodName = "/memos.api.v1.MemoService/GetMemoByShare"
MemoService_GetLinkMetadata_FullMethodName = "/memos.api.v1.MemoService/GetLinkMetadata"
MemoService_BatchGetLinkMetadata_FullMethodName = "/memos.api.v1.MemoService/BatchGetLinkMetadata"
)
// MemoServiceClient is the client API for MemoService service.
@ -81,6 +83,10 @@ type MemoServiceClient interface {
// GetMemoByShare resolves a share token to its memo. No authentication required.
// Returns NOT_FOUND if the token is invalid or expired.
GetMemoByShare(ctx context.Context, in *GetMemoByShareRequest, opts ...grpc.CallOption) (*Memo, error)
// GetLinkMetadata gets metadata for a link.
GetLinkMetadata(ctx context.Context, in *GetLinkMetadataRequest, opts ...grpc.CallOption) (*LinkMetadata, error)
// BatchGetLinkMetadata gets metadata for links.
BatchGetLinkMetadata(ctx context.Context, in *BatchGetLinkMetadataRequest, opts ...grpc.CallOption) (*BatchGetLinkMetadataResponse, error)
}
type memoServiceClient struct {
@ -271,6 +277,26 @@ func (c *memoServiceClient) GetMemoByShare(ctx context.Context, in *GetMemoBySha
return out, nil
}
func (c *memoServiceClient) GetLinkMetadata(ctx context.Context, in *GetLinkMetadataRequest, opts ...grpc.CallOption) (*LinkMetadata, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(LinkMetadata)
err := c.cc.Invoke(ctx, MemoService_GetLinkMetadata_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *memoServiceClient) BatchGetLinkMetadata(ctx context.Context, in *BatchGetLinkMetadataRequest, opts ...grpc.CallOption) (*BatchGetLinkMetadataResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(BatchGetLinkMetadataResponse)
err := c.cc.Invoke(ctx, MemoService_BatchGetLinkMetadata_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// MemoServiceServer is the server API for MemoService service.
// All implementations must embed UnimplementedMemoServiceServer
// for forward compatibility.
@ -312,6 +338,10 @@ type MemoServiceServer interface {
// GetMemoByShare resolves a share token to its memo. No authentication required.
// Returns NOT_FOUND if the token is invalid or expired.
GetMemoByShare(context.Context, *GetMemoByShareRequest) (*Memo, error)
// GetLinkMetadata gets metadata for a link.
GetLinkMetadata(context.Context, *GetLinkMetadataRequest) (*LinkMetadata, error)
// BatchGetLinkMetadata gets metadata for links.
BatchGetLinkMetadata(context.Context, *BatchGetLinkMetadataRequest) (*BatchGetLinkMetadataResponse, error)
mustEmbedUnimplementedMemoServiceServer()
}
@ -376,6 +406,12 @@ func (UnimplementedMemoServiceServer) DeleteMemoShare(context.Context, *DeleteMe
func (UnimplementedMemoServiceServer) GetMemoByShare(context.Context, *GetMemoByShareRequest) (*Memo, error) {
return nil, status.Error(codes.Unimplemented, "method GetMemoByShare not implemented")
}
func (UnimplementedMemoServiceServer) GetLinkMetadata(context.Context, *GetLinkMetadataRequest) (*LinkMetadata, error) {
return nil, status.Error(codes.Unimplemented, "method GetLinkMetadata not implemented")
}
func (UnimplementedMemoServiceServer) BatchGetLinkMetadata(context.Context, *BatchGetLinkMetadataRequest) (*BatchGetLinkMetadataResponse, error) {
return nil, status.Error(codes.Unimplemented, "method BatchGetLinkMetadata not implemented")
}
func (UnimplementedMemoServiceServer) mustEmbedUnimplementedMemoServiceServer() {}
func (UnimplementedMemoServiceServer) testEmbeddedByValue() {}
@ -721,6 +757,42 @@ func _MemoService_GetMemoByShare_Handler(srv interface{}, ctx context.Context, d
return interceptor(ctx, in, info, handler)
}
func _MemoService_GetLinkMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetLinkMetadataRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MemoServiceServer).GetLinkMetadata(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MemoService_GetLinkMetadata_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MemoServiceServer).GetLinkMetadata(ctx, req.(*GetLinkMetadataRequest))
}
return interceptor(ctx, in, info, handler)
}
func _MemoService_BatchGetLinkMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(BatchGetLinkMetadataRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MemoServiceServer).BatchGetLinkMetadata(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MemoService_BatchGetLinkMetadata_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MemoServiceServer).BatchGetLinkMetadata(ctx, req.(*BatchGetLinkMetadataRequest))
}
return interceptor(ctx, in, info, handler)
}
// MemoService_ServiceDesc is the grpc.ServiceDesc for MemoService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@ -800,6 +872,14 @@ var MemoService_ServiceDesc = grpc.ServiceDesc{
MethodName: "GetMemoByShare",
Handler: _MemoService_GetMemoByShare_Handler,
},
{
MethodName: "GetLinkMetadata",
Handler: _MemoService_GetLinkMetadata_Handler,
},
{
MethodName: "BatchGetLinkMetadata",
Handler: _MemoService_BatchGetLinkMetadata_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/v1/memo_service.proto",

@ -643,6 +643,56 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Status'
/api/v1/memos/-/linkMetadata:
get:
tags:
- MemoService
description: GetLinkMetadata gets metadata for a link.
operationId: MemoService_GetLinkMetadata
parameters:
- name: url
in: query
description: Required. The link URL.
schema:
type: string
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/LinkMetadata'
default:
description: Default error response
content:
application/json:
schema:
$ref: '#/components/schemas/Status'
/api/v1/memos/-/linkMetadata:batchGet:
post:
tags:
- MemoService
description: BatchGetLinkMetadata gets metadata for links.
operationId: MemoService_BatchGetLinkMetadata
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/BatchGetLinkMetadataRequest'
required: true
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/BatchGetLinkMetadataResponse'
default:
description: Default error response
content:
application/json:
schema:
$ref: '#/components/schemas/Status'
/api/v1/memos/{memo}:
get:
tags:
@ -2217,6 +2267,24 @@ components:
type: array
items:
type: string
BatchGetLinkMetadataRequest:
required:
- urls
type: object
properties:
urls:
type: array
items:
type: string
description: Required. The link URLs.
BatchGetLinkMetadataResponse:
type: object
properties:
linkMetadata:
type: array
items:
$ref: '#/components/schemas/LinkMetadata'
description: The link metadata list, in the same order as the input URLs.
BatchGetUsersRequest:
type: object
properties:
@ -2699,6 +2767,21 @@ components:
so a single entry like "project/.*" matches all tags under that prefix.
Exact tag names are also valid (they are trivially valid regex patterns).
description: Tag metadata configuration.
LinkMetadata:
type: object
properties:
url:
type: string
description: The original link URL.
title:
type: string
description: The link title.
description:
type: string
description: The link description.
image:
type: string
description: The link image URL.
LinkedIdentity:
type: object
properties:

@ -29,9 +29,11 @@ var PublicMethods = map[string]struct{}{
"/memos.api.v1.IdentityProviderService/ListIdentityProviders": {},
// Memo Service - public memos (visibility filtering done in service layer)
"/memos.api.v1.MemoService/GetMemo": {},
"/memos.api.v1.MemoService/ListMemos": {},
"/memos.api.v1.MemoService/ListMemoComments": {},
"/memos.api.v1.MemoService/GetMemo": {},
"/memos.api.v1.MemoService/ListMemos": {},
"/memos.api.v1.MemoService/ListMemoComments": {},
"/memos.api.v1.MemoService/GetLinkMetadata": {},
"/memos.api.v1.MemoService/BatchGetLinkMetadata": {},
// Memo sharing - share-token endpoints require no authentication
"/memos.api.v1.MemoService/GetMemoByShare": {},

@ -27,6 +27,8 @@ func TestPublicMethodsArePublic(t *testing.T) {
// Memo Service
"/memos.api.v1.MemoService/GetMemo",
"/memos.api.v1.MemoService/ListMemos",
"/memos.api.v1.MemoService/GetLinkMetadata",
"/memos.api.v1.MemoService/BatchGetLinkMetadata",
}
for _, method := range publicMethods {

@ -415,6 +415,22 @@ func (s *ConnectServiceHandler) GetMemoByShare(ctx context.Context, req *connect
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetLinkMetadata(ctx context.Context, req *connect.Request[v1pb.GetLinkMetadataRequest]) (*connect.Response[v1pb.LinkMetadata], error) {
resp, err := s.APIV1Service.GetLinkMetadata(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) BatchGetLinkMetadata(ctx context.Context, req *connect.Request[v1pb.BatchGetLinkMetadataRequest]) (*connect.Response[v1pb.BatchGetLinkMetadataResponse], error) {
resp, err := s.APIV1Service.BatchGetLinkMetadata(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
// AttachmentService
func (s *ConnectServiceHandler) CreateAttachment(ctx context.Context, req *connect.Request[v1pb.CreateAttachmentRequest]) (*connect.Response[v1pb.Attachment], error) {

@ -0,0 +1,103 @@
package v1
import (
"context"
"fmt"
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/usememos/memos/internal/httpgetter"
v1pb "github.com/usememos/memos/proto/gen/api/v1"
)
func TestGetLinkMetadata(t *testing.T) {
originalFetchHTMLMeta := fetchHTMLMeta
t.Cleanup(func() {
fetchHTMLMeta = originalFetchHTMLMeta
})
fetchHTMLMeta = func(url string) (*httpgetter.HTMLMeta, error) {
require.Equal(t, "https://example.com/article", url)
return &httpgetter.HTMLMeta{
Title: "Example title",
Description: "Example description",
Image: "https://example.com/cover.png",
}, nil
}
metadata, err := (&APIV1Service{}).GetLinkMetadata(context.Background(), &v1pb.GetLinkMetadataRequest{
Url: "https://example.com/article",
})
require.NoError(t, err)
require.Equal(t, "https://example.com/article", metadata.Url)
require.Equal(t, "Example title", metadata.Title)
require.Equal(t, "Example description", metadata.Description)
require.Equal(t, "https://example.com/cover.png", metadata.Image)
}
func TestGetLinkMetadataEmptyURL(t *testing.T) {
_, err := (&APIV1Service{}).GetLinkMetadata(context.Background(), &v1pb.GetLinkMetadataRequest{})
require.Error(t, err)
require.Equal(t, codes.InvalidArgument, status.Code(err))
}
func TestGetLinkMetadataInternalURL(t *testing.T) {
_, err := (&APIV1Service{}).GetLinkMetadata(context.Background(), &v1pb.GetLinkMetadataRequest{
Url: "http://192.168.0.1",
})
require.Error(t, err)
require.Equal(t, codes.InvalidArgument, status.Code(err))
}
func TestBatchGetLinkMetadata(t *testing.T) {
originalFetchHTMLMeta := fetchHTMLMeta
t.Cleanup(func() {
fetchHTMLMeta = originalFetchHTMLMeta
})
var fetchedURLs []string
fetchHTMLMeta = func(url string) (*httpgetter.HTMLMeta, error) {
fetchedURLs = append(fetchedURLs, url)
return &httpgetter.HTMLMeta{
Title: fmt.Sprintf("Title for %s", url),
Description: fmt.Sprintf("Description for %s", url),
Image: fmt.Sprintf("%s/cover.png", url),
}, nil
}
response, err := (&APIV1Service{}).BatchGetLinkMetadata(context.Background(), &v1pb.BatchGetLinkMetadataRequest{
Urls: []string{
"https://example.com/one",
"https://example.com/two",
},
})
require.NoError(t, err)
require.Equal(t, []string{"https://example.com/one", "https://example.com/two"}, fetchedURLs)
require.Len(t, response.LinkMetadata, 2)
require.Equal(t, "https://example.com/one", response.LinkMetadata[0].Url)
require.Equal(t, "Title for https://example.com/one", response.LinkMetadata[0].Title)
require.Equal(t, "https://example.com/two", response.LinkMetadata[1].Url)
require.Equal(t, "Title for https://example.com/two", response.LinkMetadata[1].Title)
}
func TestBatchGetLinkMetadataEmptyURLs(t *testing.T) {
_, err := (&APIV1Service{}).BatchGetLinkMetadata(context.Background(), &v1pb.BatchGetLinkMetadataRequest{})
require.Error(t, err)
require.Equal(t, codes.InvalidArgument, status.Code(err))
}
func TestBatchGetLinkMetadataTooManyURLs(t *testing.T) {
urls := make([]string, maxBatchGetLinkMetadata+1)
for i := range urls {
urls[i] = fmt.Sprintf("https://example.com/%d", i)
}
_, err := (&APIV1Service{}).BatchGetLinkMetadata(context.Background(), &v1pb.BatchGetLinkMetadataRequest{
Urls: urls,
})
require.Error(t, err)
require.Equal(t, codes.InvalidArgument, status.Code(err))
}

@ -13,6 +13,7 @@ import (
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
"github.com/usememos/memos/internal/httpgetter"
"github.com/usememos/memos/internal/webhook"
v1pb "github.com/usememos/memos/proto/gen/api/v1"
storepb "github.com/usememos/memos/proto/gen/store"
@ -24,6 +25,10 @@ import (
// CreateMemo when it is called internally (e.g., from CreateMemoComment).
type suppressSSEKey struct{}
const maxBatchGetLinkMetadata = 10
var fetchHTMLMeta = httpgetter.GetHTMLMeta
func withSuppressSSE(ctx context.Context) context.Context {
return context.WithValue(ctx, suppressSSEKey{}, true)
}
@ -382,6 +387,52 @@ func (s *APIV1Service) GetMemo(ctx context.Context, request *v1pb.GetMemoRequest
return memoMessage, nil
}
// GetLinkMetadata gets metadata for a link.
func (*APIV1Service) GetLinkMetadata(_ context.Context, request *v1pb.GetLinkMetadataRequest) (*v1pb.LinkMetadata, error) {
return getLinkMetadata(request.GetUrl())
}
// BatchGetLinkMetadata gets metadata for links.
func (*APIV1Service) BatchGetLinkMetadata(_ context.Context, request *v1pb.BatchGetLinkMetadataRequest) (*v1pb.BatchGetLinkMetadataResponse, error) {
if len(request.Urls) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "urls are required")
}
if len(request.Urls) > maxBatchGetLinkMetadata {
return nil, status.Errorf(codes.InvalidArgument, "too many urls (max %d)", maxBatchGetLinkMetadata)
}
linkMetadata := make([]*v1pb.LinkMetadata, 0, len(request.Urls))
for _, url := range request.Urls {
metadata, err := getLinkMetadata(url)
if err != nil {
return nil, err
}
linkMetadata = append(linkMetadata, metadata)
}
return &v1pb.BatchGetLinkMetadataResponse{
LinkMetadata: linkMetadata,
}, nil
}
func getLinkMetadata(inputURL string) (*v1pb.LinkMetadata, error) {
url := strings.TrimSpace(inputURL)
if url == "" {
return nil, status.Errorf(codes.InvalidArgument, "url is required")
}
htmlMeta, err := fetchHTMLMeta(url)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to fetch link metadata: %v", err)
}
return &v1pb.LinkMetadata{
Url: inputURL,
Title: htmlMeta.Title,
Description: htmlMeta.Description,
Image: htmlMeta.Image,
}, nil
}
func (s *APIV1Service) UpdateMemo(ctx context.Context, request *v1pb.UpdateMemoRequest) (*v1pb.Memo, error) {
memoUID, err := ExtractMemoUIDFromName(request.Memo.Name)
if err != nil {

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