refactor: implement s3 storage

This commit is contained in:
Steven 2024-05-02 21:28:06 +08:00
parent 355ea352aa
commit 26545c855c
55 changed files with 842 additions and 858 deletions

View File

@ -1755,36 +1755,7 @@ paths:
format: date-time format: date-time
tags: tags:
- UserService - UserService
/o/r/{uid}: /file/{name}:
get:
summary: GetResourceBinary returns a resource binary by name.
operationId: ResourceService_GetResourceBinary2
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/apiHttpBody'
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/googlerpcStatus'
parameters:
- name: uid
description: The user defined id of the resource.
in: path
required: true
type: string
- name: name
description: |-
The name of the resource.
Format: resources/{id}
id is the system generated unique identifier.
in: query
required: false
type: string
tags:
- ResourceService
/o/{name}:
get: get:
summary: GetResourceBinary returns a resource binary by name. summary: GetResourceBinary returns a resource binary by name.
operationId: ResourceService_GetResourceBinary operationId: ResourceService_GetResourceBinary
@ -1814,7 +1785,7 @@ paths:
type: string type: string
tags: tags:
- ResourceService - ResourceService
/o/{name}/avatar: /file/{name}/avatar:
get: get:
summary: GetUserAvatarBinary gets the avatar of a user. summary: GetUserAvatarBinary gets the avatar of a user.
operationId: UserService_GetUserAvatarBinary operationId: UserService_GetUserAvatarBinary
@ -1849,6 +1820,35 @@ paths:
format: byte format: byte
tags: tags:
- UserService - UserService
/o/r/{uid}:
get:
summary: GetResourceBinary returns a resource binary by name.
operationId: ResourceService_GetResourceBinary2
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/apiHttpBody'
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/googlerpcStatus'
parameters:
- name: uid
description: The user defined id of the resource.
in: path
required: true
type: string
- name: name
description: |-
The name of the resource.
Format: resources/{id}
id is the system generated unique identifier.
in: query
required: false
type: string
tags:
- ResourceService
definitions: definitions:
MemoServiceSetMemoRelationsBody: MemoServiceSetMemoRelationsBody:
type: object type: object

View File

@ -6,66 +6,76 @@ import (
"time" "time"
"github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/aws"
s3config "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/feature/s3/manager" "github.com/aws/aws-sdk-go-v2/feature/s3/manager"
awss3 "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/pkg/errors" "github.com/pkg/errors"
storepb "github.com/usememos/memos/proto/gen/store"
) )
const LinkLifetime = 24 * time.Hour const presignLifetimeSecs = 7 * 24 * 60 * 60
type Config struct {
AccessKeyID string
AcesssKeySecret string
Endpoint string
Region string
Bucket string
}
type Client struct { type Client struct {
Client *awss3.Client Client *s3.Client
Config *Config Bucket *string
} }
func NewClient(ctx context.Context, config *Config) (*Client, error) { func NewClient(ctx context.Context, s3Config *storepb.WorkspaceStorageSetting_S3Config) (*Client, error) {
resolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...any) (aws.Endpoint, error) { resolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...any) (aws.Endpoint, error) {
return aws.Endpoint{ return aws.Endpoint{
URL: config.Endpoint, URL: s3Config.Endpoint,
}, nil }, nil
}) })
s3Config, err := s3config.LoadDefaultConfig(ctx, cfg, err := config.LoadDefaultConfig(ctx,
s3config.WithEndpointResolverWithOptions(resolver), config.WithEndpointResolverWithOptions(resolver),
s3config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(config.AccessKeyID, config.AcesssKeySecret, "")), config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(s3Config.AccessKeyId, s3Config.AccessKeySecret, "")),
s3config.WithRegion(config.Region), config.WithRegion(s3Config.Region),
) )
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to load s3 config") return nil, errors.Wrap(err, "failed to load s3 config")
} }
client := awss3.NewFromConfig(s3Config) client := s3.NewFromConfig(cfg)
return &Client{ return &Client{
Client: client, Client: client,
Config: config, Bucket: aws.String(s3Config.Bucket),
}, nil }, nil
} }
func (client *Client) UploadFile(ctx context.Context, filename string, fileType string, src io.Reader) (string, error) { // UploadObject uploads an object to S3.
func (client *Client) UploadObject(ctx context.Context, key string, fileType string, content io.Reader) (string, error) {
uploader := manager.NewUploader(client.Client) uploader := manager.NewUploader(client.Client)
putInput := awss3.PutObjectInput{ putInput := s3.PutObjectInput{
Bucket: aws.String(client.Config.Bucket), Bucket: client.Bucket,
Key: aws.String(filename), Key: aws.String(key),
Body: src,
ContentType: aws.String(fileType), ContentType: aws.String(fileType),
Body: content,
} }
uploadOutput, err := uploader.Upload(ctx, &putInput) result, err := uploader.Upload(ctx, &putInput)
if err != nil { if err != nil {
return "", err return "", err
} }
link := uploadOutput.Location resultKey := result.Key
if link == "" { if resultKey == nil || *resultKey == "" {
return "", errors.New("failed to get file link") return "", errors.New("failed to get file key")
} }
return link, nil return *resultKey, nil
}
// PresignGetObject presigns an object in S3.
func (client *Client) PresignGetObject(ctx context.Context, bucket, key string) (string, error) {
presignClient := s3.NewPresignClient(client.Client)
presignResult, err := presignClient.PresignGetObject(context.TODO(), &s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
}, func(opts *s3.PresignOptions) {
opts.Expires = time.Duration(presignLifetimeSecs * int64(time.Second))
})
if err != nil {
return "", errors.Wrap(err, "failed to presign put object")
}
return presignResult.URL, nil
} }

View File

@ -36,9 +36,12 @@ service ResourceService {
// GetResourceBinary returns a resource binary by name. // GetResourceBinary returns a resource binary by name.
rpc GetResourceBinary(GetResourceBinaryRequest) returns (google.api.HttpBody) { rpc GetResourceBinary(GetResourceBinaryRequest) returns (google.api.HttpBody) {
option (google.api.http) = { option (google.api.http) = {
get: "/o/{name=resources/*}" get: "/file/{name=resources/*}"
additional_bindings {get: "/o/r/{uid}"} additional_bindings {
// DEPRECATED: Will be removed in the future. Use `/file/{name}` instead.
get: "/o/r/{uid}"
}
}; };
option (google.api.method_signature) = "name,uid"; option (google.api.method_signature) = "name,uid";
} }

View File

@ -29,7 +29,7 @@ service UserService {
} }
// GetUserAvatarBinary gets the avatar of a user. // GetUserAvatarBinary gets the avatar of a user.
rpc GetUserAvatarBinary(GetUserAvatarBinaryRequest) returns (google.api.HttpBody) { rpc GetUserAvatarBinary(GetUserAvatarBinaryRequest) returns (google.api.HttpBody) {
option (google.api.http) = {get: "/o/{name=users/*}/avatar"}; option (google.api.http) = {get: "/file/{name=users/*}/avatar"};
option (google.api.method_signature) = "name"; option (google.api.method_signature) = "name";
} }
// CreateUser creates a new user. // CreateUser creates a new user.

View File

@ -656,7 +656,7 @@ var file_api_v1_resource_service_proto_rawDesc = []byte{
0x61, 0x73, 0x6b, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0x61, 0x73, 0x6b, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x22,
0x2b, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x2b, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x32, 0x95, 0x07, 0x0a, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x32, 0x98, 0x07, 0x0a,
0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x12, 0x72, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x12, 0x72, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
0x63, 0x65, 0x12, 0x23, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x63, 0x65, 0x12, 0x23, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76,
@ -688,44 +688,44 @@ var file_api_v1_resource_service_proto_rawDesc = []byte{
0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x29, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x29, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x6e, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x6e,
0x61, 0x6d, 0x65, 0x3d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x2a, 0x7d, 0x61, 0x6d, 0x65, 0x3d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x2a, 0x7d,
0x12, 0x89, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x8c, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x65, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x65, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x48, 0x74, 0x74, 0x70,
0x42, 0x6f, 0x64, 0x79, 0x22, 0x36, 0xda, 0x41, 0x08, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x75, 0x69, 0x42, 0x6f, 0x64, 0x79, 0x22, 0x39, 0xda, 0x41, 0x08, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x75, 0x69,
0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x5a, 0x0c, 0x12, 0x0a, 0x2f, 0x6f, 0x2f, 0x72, 0x2f, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x5a, 0x0c, 0x12, 0x0a, 0x2f, 0x6f, 0x2f, 0x72, 0x2f,
0x7b, 0x75, 0x69, 0x64, 0x7d, 0x12, 0x15, 0x2f, 0x6f, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x7b, 0x75, 0x69, 0x64, 0x7d, 0x12, 0x18, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x2f, 0x7b, 0x6e, 0x61,
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x2a, 0x7d, 0x12, 0x9b, 0x01, 0x0a, 0x6d, 0x65, 0x3d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x2a, 0x7d, 0x12,
0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x9b, 0x01, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
0x23, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x63, 0x65, 0x12, 0x23, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76,
0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e,
0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x4c, 0xda, 0x41, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22,
0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0xda, 0x41, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2c, 0x75, 0x70, 0x64,
0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x3a, 0x08, 0x72, 0x65, 0x73, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x3a, 0x08,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x32, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x32, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76,
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x72, 0x65, 0x31, 0x2f, 0x7b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x6e, 0x61, 0x6d, 0x65,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x2a, 0x7d, 0x12, 0x78, 0x0a, 0x0e, 0x44, 0x65, 0x3d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x2a, 0x7d, 0x12, 0x78, 0x0a,
0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x23, 0x2e, 0x6d, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12,
0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x23, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44,
0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71,
0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x29, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x29, 0xda, 0x41,
0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x2a, 0x1a, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x2a, 0x1a, 0x2f, 0x61, 0x70,
0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x72, 0x65, 0x73, 0x6f, 0x75,
0x73, 0x2f, 0x2a, 0x7d, 0x42, 0xac, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x2a, 0x7d, 0x42, 0xac, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e,
0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x14, 0x52, 0x65,
0x72, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f,
0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x73, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x65, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x2f, 0x75, 0x73, 0x65, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f,
0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31,
0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x4d, 0x41, 0x58, 0xaa, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x4d, 0x41, 0x58, 0xaa, 0x02, 0x0c, 0x4d,
0x73, 0x2e, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x4d, 0x65,
0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x4d, 0x65, 0x6d,
0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x6f, 0x73, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74,
0x74, 0x61, 0xea, 0x02, 0x0e, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0e, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x41,
0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

View File

@ -569,7 +569,7 @@ func RegisterResourceServiceHandlerServer(ctx context.Context, mux *runtime.Serv
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error var err error
var annotatedContext context.Context var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.ResourceService/GetResourceBinary", runtime.WithHTTPPathPattern("/o/{name=resources/*}")) annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.ResourceService/GetResourceBinary", runtime.WithHTTPPathPattern("/file/{name=resources/*}"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@ -796,7 +796,7 @@ func RegisterResourceServiceHandlerClient(ctx context.Context, mux *runtime.Serv
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error var err error
var annotatedContext context.Context var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.ResourceService/GetResourceBinary", runtime.WithHTTPPathPattern("/o/{name=resources/*}")) annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.ResourceService/GetResourceBinary", runtime.WithHTTPPathPattern("/file/{name=resources/*}"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@ -890,7 +890,7 @@ var (
pattern_ResourceService_GetResource_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "resources", "name"}, "")) pattern_ResourceService_GetResource_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "resources", "name"}, ""))
pattern_ResourceService_GetResourceBinary_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 2, 5, 2}, []string{"o", "resources", "name"}, "")) pattern_ResourceService_GetResourceBinary_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 2, 5, 2}, []string{"file", "resources", "name"}, ""))
pattern_ResourceService_GetResourceBinary_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"o", "r", "uid"}, "")) pattern_ResourceService_GetResourceBinary_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"o", "r", "uid"}, ""))

View File

@ -1269,7 +1269,7 @@ var file_api_v1_user_service_proto_rawDesc = []byte{
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f,
0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73,
0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0xb8, 0x0c, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0xbc, 0x0c, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x63, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x63, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73,
0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75,
@ -1290,97 +1290,97 @@ var file_api_v1_user_service_proto_rawDesc = []byte{
0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x22, 0x25, 0xda, 0x41, 0x04, 0x6e, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x22, 0x25, 0xda, 0x41, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x61, 0x70, 0x69, 0x2f,
0x76, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x76, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a,
0x7d, 0x12, 0x7e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x76, 0x61, 0x74, 0x7d, 0x12, 0x81, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x76, 0x61,
0x61, 0x72, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x12, 0x28, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x74, 0x61, 0x72, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x12, 0x28, 0x2e, 0x6d, 0x65, 0x6d, 0x6f,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41,
0x76, 0x61, 0x74, 0x61, 0x72, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x48, 0x74, 0x74, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x22, 0x27, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x6f, 0x2f, 0x7b, 0x6e, 0x61, 0x6d,
0x65, 0x3d, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x7d, 0x2f, 0x61, 0x76, 0x61, 0x74, 0x61,
0x72, 0x12, 0x65, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12,
0x1f, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43,
0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x12, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e,
0x55, 0x73, 0x65, 0x72, 0x22, 0x22, 0xda, 0x41, 0x04, 0x75, 0x73, 0x65, 0x72, 0x82, 0xd3, 0xe4,
0x93, 0x02, 0x15, 0x3a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x0d, 0x2f, 0x61, 0x70, 0x69, 0x2f,
0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x7f, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61,
0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x22, 0x3c, 0xda, 0x41, 0x10,
0x75, 0x73, 0x65, 0x72, 0x2c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x3a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x32, 0x1b, 0x2f, 0x61,
0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x6e, 0x61, 0x6d, 0x65,
0x3d, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x7d, 0x12, 0x6c, 0x0a, 0x0a, 0x44, 0x65, 0x6c,
0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65,
0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x22, 0x25, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x2a,
0x16, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x75,
0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x7d, 0x12, 0x7f, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73,
0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e, 0x6d, 0x65, 0x6d, 0x6f,
0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72,
0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x41, 0x76, 0x61, 0x74, 0x61, 0x72, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75,
0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69,
0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x2d, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x22, 0x2a, 0xda, 0x41, 0x04, 0x6e, 0x61,
0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x2f,
0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x7d, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x7d, 0x2f, 0x61,
0x2f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0xa5, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x65, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55,
0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x26, 0x73, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71,
0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x22, 0x22, 0xda, 0x41, 0x04, 0x75, 0x73, 0x65,
0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x0d, 0x2f,
0x67, 0x22, 0x4d, 0xda, 0x41, 0x13, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2c, 0x75, 0x70, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x7f, 0x0a, 0x0a,
0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x31, 0x3a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x6d,
0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x32, 0x26, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
0x31, 0x2f, 0x7b, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6d, 0x65,
0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x2f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x7d, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x22,
0x12, 0xa2, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x3c, 0xda, 0x41, 0x10, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f,
0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x29, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x6d, 0x61, 0x73, 0x6b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x3a, 0x04, 0x75, 0x73, 0x65, 0x72,
0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x32, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x75, 0x73, 0x65, 0x72, 0x2e,
0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x7d, 0x12, 0x6c, 0x0a,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x6d, 0x65,
0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,
0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67,
0x22, 0x33, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x12, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
0x24, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x75, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x25, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4,
0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x7d, 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x93, 0x02, 0x18, 0x2a, 0x16, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x6e, 0x61,
0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x3d, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x7d, 0x12, 0x7f, 0x0a, 0x0e, 0x47,
0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e,
0x2a, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74,
0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65,
0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76,
0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x2d, 0xda,
0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x36, 0xda, 0x41, 0x04, 0x6e, 0x41, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x61,
0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x3a, 0x01, 0x2a, 0x22, 0x24, 0x2f, 0x61,
0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x75, 0x73, 0x65, 0x72, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x75, 0x73, 0x65, 0x72,
0x73, 0x2f, 0x2a, 0x7d, 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x73, 0x2f, 0x2a, 0x7d, 0x2f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0xa5, 0x01, 0x0a,
0x6e, 0x73, 0x12, 0xac, 0x01, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69,
0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2a, 0x2e, 0x6d, 0x6e, 0x67, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76,
0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74,
0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6d, 0x65, 0x6d,
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x4d, 0xda, 0x41, 0x13, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x22, 0x4f, 0xda, 0x41, 0x11, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x67, 0x2c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x82, 0xd3, 0xe4,
0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35, 0x2a, 0x33, 0x2f, 0x61, 0x93, 0x02, 0x31, 0x3a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x32, 0x26, 0x2f, 0x61,
0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x75, 0x73, 0x65, 0x72, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x6e,
0x73, 0x2f, 0x2a, 0x7d, 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x61, 0x6d, 0x65, 0x3d, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x2f, 0x73, 0x65, 0x74, 0x74,
0x6e, 0x73, 0x2f, 0x7b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x7d, 0x12, 0xa2, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65,
0x7d, 0x42, 0xa8, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x29, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x10, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73,
0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x73, 0x65, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73,
0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72,
0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x4d, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70,
0x41, 0x58, 0xaa, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4,
0x31, 0xca, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x93, 0x02, 0x26, 0x12, 0x24, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x6e, 0x61,
0xe2, 0x02, 0x18, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x6d, 0x65, 0x3d, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x7d, 0x2f, 0x61, 0x63, 0x63, 0x65,
0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0e, 0x4d, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x15, 0x43, 0x72,
0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f,
0x6f, 0x74, 0x6f, 0x33, 0x6b, 0x65, 0x6e, 0x12, 0x2a, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63,
0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x1d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55,
0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x36,
0xda, 0x41, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x3a, 0x01, 0x2a,
0x22, 0x24, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d,
0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x7d, 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f,
0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0xac, 0x01, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74,
0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
0x12, 0x2a, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e,
0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73,
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
0x6d, 0x70, 0x74, 0x79, 0x22, 0x4f, 0xda, 0x41, 0x11, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x61, 0x63,
0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35,
0x2a, 0x33, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d,
0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x7d, 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f,
0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x2f, 0x7b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74,
0x6f, 0x6b, 0x65, 0x6e, 0x7d, 0x42, 0xa8, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65,
0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x10, 0x55, 0x73, 0x65, 0x72,
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, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31,
0xa2, 0x02, 0x03, 0x4d, 0x41, 0x58, 0xaa, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x41,
0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70,
0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, 0x69,
0x5c, 0x56, 0x31, 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, 0x31,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

View File

@ -810,7 +810,7 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error var err error
var annotatedContext context.Context var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/GetUserAvatarBinary", runtime.WithHTTPPathPattern("/o/{name=users/*}/avatar")) annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/GetUserAvatarBinary", runtime.WithHTTPPathPattern("/file/{name=users/*}/avatar"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@ -1140,7 +1140,7 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error var err error
var annotatedContext context.Context var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/GetUserAvatarBinary", runtime.WithHTTPPathPattern("/o/{name=users/*}/avatar")) annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/GetUserAvatarBinary", runtime.WithHTTPPathPattern("/file/{name=users/*}/avatar"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@ -1342,7 +1342,7 @@ var (
pattern_UserService_GetUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "")) pattern_UserService_GetUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, ""))
pattern_UserService_GetUserAvatarBinary_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 2, 5, 2, 2, 3}, []string{"o", "users", "name", "avatar"}, "")) pattern_UserService_GetUserAvatarBinary_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 2, 5, 2, 2, 3}, []string{"file", "users", "name", "avatar"}, ""))
pattern_UserService_CreateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "")) pattern_UserService_CreateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, ""))

View File

@ -0,0 +1,329 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.34.0
// protoc (unknown)
// source: store/resource.proto
package store
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
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 ResourceStorageType int32
const (
ResourceStorageType_RESOURCE_STORAGE_TYPE_UNSPECIFIED ResourceStorageType = 0
ResourceStorageType_LOCAL ResourceStorageType = 1
ResourceStorageType_S3 ResourceStorageType = 2
ResourceStorageType_EXTERNAL ResourceStorageType = 3
)
// Enum value maps for ResourceStorageType.
var (
ResourceStorageType_name = map[int32]string{
0: "RESOURCE_STORAGE_TYPE_UNSPECIFIED",
1: "LOCAL",
2: "S3",
3: "EXTERNAL",
}
ResourceStorageType_value = map[string]int32{
"RESOURCE_STORAGE_TYPE_UNSPECIFIED": 0,
"LOCAL": 1,
"S3": 2,
"EXTERNAL": 3,
}
)
func (x ResourceStorageType) Enum() *ResourceStorageType {
p := new(ResourceStorageType)
*p = x
return p
}
func (x ResourceStorageType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (ResourceStorageType) Descriptor() protoreflect.EnumDescriptor {
return file_store_resource_proto_enumTypes[0].Descriptor()
}
func (ResourceStorageType) Type() protoreflect.EnumType {
return &file_store_resource_proto_enumTypes[0]
}
func (x ResourceStorageType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use ResourceStorageType.Descriptor instead.
func (ResourceStorageType) EnumDescriptor() ([]byte, []int) {
return file_store_resource_proto_rawDescGZIP(), []int{0}
}
type ResourcePayload struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Types that are assignable to Payload:
//
// *ResourcePayload_S3Object_
Payload isResourcePayload_Payload `protobuf_oneof:"payload"`
}
func (x *ResourcePayload) Reset() {
*x = ResourcePayload{}
if protoimpl.UnsafeEnabled {
mi := &file_store_resource_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ResourcePayload) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ResourcePayload) ProtoMessage() {}
func (x *ResourcePayload) ProtoReflect() protoreflect.Message {
mi := &file_store_resource_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 ResourcePayload.ProtoReflect.Descriptor instead.
func (*ResourcePayload) Descriptor() ([]byte, []int) {
return file_store_resource_proto_rawDescGZIP(), []int{0}
}
func (m *ResourcePayload) GetPayload() isResourcePayload_Payload {
if m != nil {
return m.Payload
}
return nil
}
func (x *ResourcePayload) GetS3Object() *ResourcePayload_S3Object {
if x, ok := x.GetPayload().(*ResourcePayload_S3Object_); ok {
return x.S3Object
}
return nil
}
type isResourcePayload_Payload interface {
isResourcePayload_Payload()
}
type ResourcePayload_S3Object_ struct {
S3Object *ResourcePayload_S3Object `protobuf:"bytes,1,opt,name=s3_object,json=s3Object,proto3,oneof"`
}
func (*ResourcePayload_S3Object_) isResourcePayload_Payload() {}
type ResourcePayload_S3Object struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Bucket string `protobuf:"bytes,1,opt,name=bucket,proto3" json:"bucket,omitempty"`
Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
LastPresignedTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=last_presigned_time,json=lastPresignedTime,proto3" json:"last_presigned_time,omitempty"`
}
func (x *ResourcePayload_S3Object) Reset() {
*x = ResourcePayload_S3Object{}
if protoimpl.UnsafeEnabled {
mi := &file_store_resource_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ResourcePayload_S3Object) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ResourcePayload_S3Object) ProtoMessage() {}
func (x *ResourcePayload_S3Object) ProtoReflect() protoreflect.Message {
mi := &file_store_resource_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 ResourcePayload_S3Object.ProtoReflect.Descriptor instead.
func (*ResourcePayload_S3Object) Descriptor() ([]byte, []int) {
return file_store_resource_proto_rawDescGZIP(), []int{0, 0}
}
func (x *ResourcePayload_S3Object) GetBucket() string {
if x != nil {
return x.Bucket
}
return ""
}
func (x *ResourcePayload_S3Object) GetKey() string {
if x != nil {
return x.Key
}
return ""
}
func (x *ResourcePayload_S3Object) GetLastPresignedTime() *timestamppb.Timestamp {
if x != nil {
return x.LastPresignedTime
}
return nil
}
var File_store_resource_proto protoreflect.FileDescriptor
var file_store_resource_proto_rawDesc = []byte{
0x0a, 0x14, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x73, 0x74,
0x6f, 0x72, 0x65, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe5, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x44, 0x0a, 0x09, 0x73, 0x33, 0x5f, 0x6f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6d, 0x65,
0x6d, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
0x63, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x53, 0x33, 0x4f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x48, 0x00, 0x52, 0x08, 0x73, 0x33, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x1a, 0x80,
0x01, 0x0a, 0x08, 0x53, 0x33, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x62,
0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63,
0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4a, 0x0a, 0x13, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x72,
0x65, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x11,
0x6c, 0x61, 0x73, 0x74, 0x50, 0x72, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x69, 0x6d,
0x65, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2a, 0x5d, 0x0a, 0x13,
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54,
0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x21, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f,
0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53,
0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x4f,
0x43, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x06, 0x0a, 0x02, 0x53, 0x33, 0x10, 0x02, 0x12, 0x0c, 0x0a,
0x08, 0x45, 0x58, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x10, 0x03, 0x42, 0x98, 0x01, 0x0a, 0x0f,
0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42,
0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x73, 0x65,
0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0xa2, 0x02, 0x03, 0x4d, 0x53,
0x58, 0xaa, 0x02, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xca,
0x02, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xe2, 0x02, 0x17,
0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d,
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x3a,
0x3a, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_store_resource_proto_rawDescOnce sync.Once
file_store_resource_proto_rawDescData = file_store_resource_proto_rawDesc
)
func file_store_resource_proto_rawDescGZIP() []byte {
file_store_resource_proto_rawDescOnce.Do(func() {
file_store_resource_proto_rawDescData = protoimpl.X.CompressGZIP(file_store_resource_proto_rawDescData)
})
return file_store_resource_proto_rawDescData
}
var file_store_resource_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_store_resource_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_store_resource_proto_goTypes = []interface{}{
(ResourceStorageType)(0), // 0: memos.store.ResourceStorageType
(*ResourcePayload)(nil), // 1: memos.store.ResourcePayload
(*ResourcePayload_S3Object)(nil), // 2: memos.store.ResourcePayload.S3Object
(*timestamppb.Timestamp)(nil), // 3: google.protobuf.Timestamp
}
var file_store_resource_proto_depIdxs = []int32{
2, // 0: memos.store.ResourcePayload.s3_object:type_name -> memos.store.ResourcePayload.S3Object
3, // 1: memos.store.ResourcePayload.S3Object.last_presigned_time:type_name -> google.protobuf.Timestamp
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_store_resource_proto_init() }
func file_store_resource_proto_init() {
if File_store_resource_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_store_resource_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ResourcePayload); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_store_resource_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ResourcePayload_S3Object); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_store_resource_proto_msgTypes[0].OneofWrappers = []interface{}{
(*ResourcePayload_S3Object_)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_store_resource_proto_rawDesc,
NumEnums: 1,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_store_resource_proto_goTypes,
DependencyIndexes: file_store_resource_proto_depIdxs,
EnumInfos: file_store_resource_proto_enumTypes,
MessageInfos: file_store_resource_proto_msgTypes,
}.Build()
File_store_resource_proto = out.File
file_store_resource_proto_rawDesc = nil
file_store_resource_proto_goTypes = nil
file_store_resource_proto_depIdxs = nil
}

View File

@ -0,0 +1,26 @@
syntax = "proto3";
package memos.store;
import "google/protobuf/timestamp.proto";
option go_package = "gen/store";
enum ResourceStorageType {
RESOURCE_STORAGE_TYPE_UNSPECIFIED = 0;
LOCAL = 1;
S3 = 2;
EXTERNAL = 3;
}
message ResourcePayload {
oneof payload {
S3Object s3_object = 1;
}
message S3Object {
string bucket = 1;
string key = 2;
google.protobuf.Timestamp last_presigned_time = 3;
}
}

View File

@ -52,7 +52,7 @@ func (s *APIV1Service) SetMemoResources(ctx context.Context, request *v1pb.SetMe
return nil, status.Errorf(codes.InvalidArgument, "invalid resource name: %v", err) return nil, status.Errorf(codes.InvalidArgument, "invalid resource name: %v", err)
} }
updatedTs := time.Now().Unix() + int64(index) updatedTs := time.Now().Unix() + int64(index)
if _, err := s.Store.UpdateResource(ctx, &store.UpdateResource{ if err := s.Store.UpdateResource(ctx, &store.UpdateResource{
ID: id, ID: id,
MemoID: &memoID, MemoID: &memoID,
UpdatedTs: &updatedTs, UpdatedTs: &updatedTs,

View File

@ -6,7 +6,6 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
"net/url"
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
@ -50,17 +49,7 @@ func (s *APIV1Service) CreateResource(ctx context.Context, request *v1pb.CreateR
Filename: request.Resource.Filename, Filename: request.Resource.Filename,
Type: request.Resource.Type, Type: request.Resource.Type,
} }
if request.Resource.ExternalLink != "" {
// Only allow those external links scheme with http/https
linkURL, err := url.Parse(request.Resource.ExternalLink)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid external link: %v", err)
}
if linkURL.Scheme != "http" && linkURL.Scheme != "https" {
return nil, status.Errorf(codes.InvalidArgument, "invalid external link scheme: %v", linkURL.Scheme)
}
create.ExternalLink = request.Resource.ExternalLink
} else {
workspaceStorageSetting, err := s.Store.GetWorkspaceStorageSetting(ctx) workspaceStorageSetting, err := s.Store.GetWorkspaceStorageSetting(ctx)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get workspace storage setting: %v", err) return nil, status.Errorf(codes.Internal, "failed to get workspace storage setting: %v", err)
@ -78,7 +67,6 @@ func (s *APIV1Service) CreateResource(ctx context.Context, request *v1pb.CreateR
if err := SaveResourceBlob(ctx, s.Store, create); err != nil { if err := SaveResourceBlob(ctx, s.Store, create); err != nil {
return nil, status.Errorf(codes.Internal, "failed to save resource blob: %v", err) return nil, status.Errorf(codes.Internal, "failed to save resource blob: %v", err)
} }
}
if request.Resource.Memo != nil { if request.Resource.Memo != nil {
memoID, err := ExtractMemoIDFromName(*request.Resource.Memo) memoID, err := ExtractMemoIDFromName(*request.Resource.Memo)
@ -202,8 +190,8 @@ func (s *APIV1Service) GetResourceBinary(ctx context.Context, request *v1pb.GetR
} }
blob := resource.Blob blob := resource.Blob
if resource.InternalPath != "" { if resource.StorageType == storepb.ResourceStorageType_LOCAL {
resourcePath := filepath.FromSlash(resource.InternalPath) resourcePath := filepath.FromSlash(resource.Reference)
if !filepath.IsAbs(resourcePath) { if !filepath.IsAbs(resourcePath) {
resourcePath = filepath.Join(s.Profile.Data, resourcePath) resourcePath = filepath.Join(s.Profile.Data, resourcePath)
} }
@ -255,11 +243,12 @@ func (s *APIV1Service) UpdateResource(ctx context.Context, request *v1pb.UpdateR
} }
} }
resource, err := s.Store.UpdateResource(ctx, update) if err := s.Store.UpdateResource(ctx, update); err != nil {
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to update resource: %v", err) return nil, status.Errorf(codes.Internal, "failed to update resource: %v", err)
} }
return s.convertResourceFromStore(ctx, resource), nil return s.GetResource(ctx, &v1pb.GetResourceRequest{
Name: request.Resource.Name,
})
} }
func (s *APIV1Service) DeleteResource(ctx context.Context, request *v1pb.DeleteResourceRequest) (*emptypb.Empty, error) { func (s *APIV1Service) DeleteResource(ctx context.Context, request *v1pb.DeleteResourceRequest) (*emptypb.Empty, error) {
@ -296,10 +285,12 @@ func (s *APIV1Service) convertResourceFromStore(ctx context.Context, resource *s
Uid: resource.UID, Uid: resource.UID,
CreateTime: timestamppb.New(time.Unix(resource.CreatedTs, 0)), CreateTime: timestamppb.New(time.Unix(resource.CreatedTs, 0)),
Filename: resource.Filename, Filename: resource.Filename,
ExternalLink: resource.ExternalLink,
Type: resource.Type, Type: resource.Type,
Size: resource.Size, Size: resource.Size,
} }
if resource.StorageType == storepb.ResourceStorageType_EXTERNAL || resource.StorageType == storepb.ResourceStorageType_S3 {
resourceMessage.ExternalLink = resource.Reference
}
if resource.MemoID != nil { if resource.MemoID != nil {
memo, _ := s.Store.GetMemo(ctx, &store.FindMemo{ memo, _ := s.Store.GetMemo(ctx, &store.FindMemo{
ID: resource.MemoID, ID: resource.MemoID,
@ -330,7 +321,7 @@ func SaveResourceBlob(ctx context.Context, s *store.Store, create *store.Resourc
if !strings.Contains(internalPath, "{filename}") { if !strings.Contains(internalPath, "{filename}") {
internalPath = filepath.Join(internalPath, "{filename}") internalPath = filepath.Join(internalPath, "{filename}")
} }
internalPath = replacePathTemplate(internalPath, create.Filename) internalPath = replaceFilenameWithPathTemplate(internalPath, create.Filename)
internalPath = filepath.ToSlash(internalPath) internalPath = filepath.ToSlash(internalPath)
// Ensure the directory exists. // Ensure the directory exists.
@ -352,20 +343,15 @@ func SaveResourceBlob(ctx context.Context, s *store.Store, create *store.Resourc
if err := os.WriteFile(osPath, create.Blob, 0644); err != nil { if err := os.WriteFile(osPath, create.Blob, 0644); err != nil {
return errors.Wrap(err, "Failed to write file") return errors.Wrap(err, "Failed to write file")
} }
create.InternalPath = internalPath create.Reference = internalPath
create.Blob = nil create.Blob = nil
create.StorageType = storepb.ResourceStorageType_LOCAL
} else if workspaceStorageSetting.StorageType == storepb.WorkspaceStorageSetting_STORAGE_TYPE_S3 { } else if workspaceStorageSetting.StorageType == storepb.WorkspaceStorageSetting_STORAGE_TYPE_S3 {
s3Config := workspaceStorageSetting.S3Config s3Config := workspaceStorageSetting.S3Config
if s3Config == nil { if s3Config == nil {
return errors.Errorf("No actived external storage found") return errors.Errorf("No actived external storage found")
} }
s3Client, err := s3.NewClient(ctx, &s3.Config{ s3Client, err := s3.NewClient(ctx, s3Config)
AccessKeyID: s3Config.AccessKeyId,
AcesssKeySecret: s3Config.AccessKeySecret,
Endpoint: s3Config.Endpoint,
Region: s3Config.Region,
Bucket: s3Config.Bucket,
})
if err != nil { if err != nil {
return errors.Wrap(err, "Failed to create s3 client") return errors.Wrap(err, "Failed to create s3 client")
} }
@ -374,15 +360,28 @@ func SaveResourceBlob(ctx context.Context, s *store.Store, create *store.Resourc
if !strings.Contains(filepathTemplate, "{filename}") { if !strings.Contains(filepathTemplate, "{filename}") {
filepathTemplate = filepath.Join(filepathTemplate, "{filename}") filepathTemplate = filepath.Join(filepathTemplate, "{filename}")
} }
filepathTemplate = replacePathTemplate(filepathTemplate, create.Filename) filepathTemplate = replaceFilenameWithPathTemplate(filepathTemplate, create.Filename)
r := bytes.NewReader(create.Blob) key, err := s3Client.UploadObject(ctx, filepathTemplate, create.Type, bytes.NewReader(create.Blob))
link, err := s3Client.UploadFile(ctx, filepathTemplate, create.Type, r)
if err != nil { if err != nil {
return errors.Wrap(err, "Failed to upload via s3 client") return errors.Wrap(err, "Failed to upload via s3 client")
} }
presignURL, err := s3Client.PresignGetObject(ctx, s3Config.Bucket, key)
if err != nil {
return errors.Wrap(err, "Failed to presign via s3 client")
}
create.ExternalLink = link create.Reference = presignURL
create.Blob = nil create.Blob = nil
create.StorageType = storepb.ResourceStorageType_S3
create.Payload = &storepb.ResourcePayload{
Payload: &storepb.ResourcePayload_S3Object_{
S3Object: &storepb.ResourcePayload_S3Object{
Bucket: s3Config.Bucket,
Key: key,
LastPresignedTime: timestamppb.New(time.Now()),
},
},
}
} }
return nil return nil
@ -390,7 +389,7 @@ func SaveResourceBlob(ctx context.Context, s *store.Store, create *store.Resourc
var fileKeyPattern = regexp.MustCompile(`\{[a-z]{1,9}\}`) var fileKeyPattern = regexp.MustCompile(`\{[a-z]{1,9}\}`)
func replacePathTemplate(path, filename string) string { func replaceFilenameWithPathTemplate(path, filename string) string {
t := time.Now() t := time.Now()
path = fileKeyPattern.ReplaceAllStringFunc(path, func(s string) string { path = fileKeyPattern.ReplaceAllStringFunc(path, func(s string) string {
switch s { switch s {

View File

@ -520,7 +520,7 @@ func convertUserFromStore(user *store.User) *v1pb.User {
} }
// Use the avatar URL instead of raw base64 image data to reduce the response size. // Use the avatar URL instead of raw base64 image data to reduce the response size.
if user.AvatarURL != "" { if user.AvatarURL != "" {
userpb.AvatarUrl = fmt.Sprintf("/o/%s/avatar", userpb.Name) userpb.AvatarUrl = fmt.Sprintf("/file/%s/avatar", userpb.Name)
} }
return userpb return userpb
} }

View File

@ -112,7 +112,7 @@ func (s *APIV1Service) RegisterGateway(ctx context.Context, echoServer *echo.Ech
return err return err
} }
echoServer.Any("/api/v1/*", echo.WrapHandler(gwMux)) echoServer.Any("/api/v1/*", echo.WrapHandler(gwMux))
echoServer.Any("/o/*", echo.WrapHandler(gwMux)) echoServer.Any("/file/*", echo.WrapHandler(gwMux))
// GRPC web proxy. // GRPC web proxy.
options := []grpcweb.Option{ options := []grpcweb.Option{

View File

@ -2,6 +2,7 @@ package rss
import ( import (
"context" "context"
"fmt"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@ -13,6 +14,7 @@ import (
"github.com/yourselfhosted/gomark/ast" "github.com/yourselfhosted/gomark/ast"
"github.com/yourselfhosted/gomark/renderer" "github.com/yourselfhosted/gomark/renderer"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/server/profile" "github.com/usememos/memos/server/profile"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
) )
@ -124,10 +126,10 @@ func (s *RSSService) generateRSSFromMemoList(ctx context.Context, memoList []*st
if len(resources) > 0 { if len(resources) > 0 {
resource := resources[0] resource := resources[0]
enclosure := feeds.Enclosure{} enclosure := feeds.Enclosure{}
if resource.ExternalLink != "" { if resource.StorageType == storepb.ResourceStorageType_EXTERNAL || resource.StorageType == storepb.ResourceStorageType_S3 {
enclosure.Url = resource.ExternalLink enclosure.Url = resource.Reference
} else { } else {
enclosure.Url = baseURL + "/o/r/" + resource.UID enclosure.Url = fmt.Sprintf("%s/file/resources/%d", baseURL, resource.ID)
} }
enclosure.Length = strconv.Itoa(int(resource.Size)) enclosure.Length = strconv.Itoa(int(resource.Size))
enclosure.Type = resource.Type enclosure.Type = resource.Type

View File

@ -2,7 +2,6 @@ package mysql
import ( import (
"context" "context"
"database/sql"
"strings" "strings"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -133,13 +132,3 @@ func (d *DB) DeleteInbox(ctx context.Context, delete *store.DeleteInbox) error {
} }
return nil return nil
} }
func vacuumInbox(ctx context.Context, tx *sql.Tx) error {
stmt := "DELETE FROM `inbox` WHERE `sender_id` NOT IN (SELECT `id` FROM `user`)"
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil
}

View File

@ -2,7 +2,6 @@ package mysql
import ( import (
"context" "context"
"database/sql"
"fmt" "fmt"
"strings" "strings"
@ -206,20 +205,5 @@ func (d *DB) DeleteMemo(ctx context.Context, delete *store.DeleteMemo) error {
if _, err := result.RowsAffected(); err != nil { if _, err := result.RowsAffected(); err != nil {
return err return err
} }
if err := d.Vacuum(ctx); err != nil {
// Prevent linter warning.
return err
}
return nil
}
func vacuumMemo(ctx context.Context, tx *sql.Tx) error {
stmt := "DELETE FROM `memo` WHERE `creator_id` NOT IN (SELECT `id` FROM `user`)"
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil return nil
} }

View File

@ -2,7 +2,6 @@ package mysql
import ( import (
"context" "context"
"database/sql"
"strings" "strings"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
@ -69,12 +68,3 @@ func (d *DB) DeleteMemoOrganizer(ctx context.Context, delete *store.DeleteMemoOr
} }
return nil return nil
} }
func vacuumMemoOrganizer(ctx context.Context, tx *sql.Tx) error {
stmt := "DELETE FROM `memo_organizer` WHERE `memo_id` NOT IN (SELECT `id` FROM `memo`) OR `user_id` NOT IN (SELECT `id` FROM `user`)"
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil
}

View File

@ -2,7 +2,6 @@ package mysql
import ( import (
"context" "context"
"database/sql"
"strings" "strings"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
@ -89,10 +88,3 @@ func (d *DB) DeleteMemoRelation(ctx context.Context, delete *store.DeleteMemoRel
} }
return nil return nil
} }
func vacuumMemoRelations(ctx context.Context, tx *sql.Tx) error {
if _, err := tx.ExecContext(ctx, "DELETE FROM `memo_relation` WHERE `memo_id` NOT IN (SELECT `id` FROM `memo`) OR `related_memo_id` NOT IN (SELECT `id` FROM `memo`)"); err != nil {
return err
}
return nil
}

View File

@ -71,11 +71,12 @@ CREATE TABLE `resource` (
`updated_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`filename` TEXT NOT NULL, `filename` TEXT NOT NULL,
`blob` MEDIUMBLOB, `blob` MEDIUMBLOB,
`external_link` TEXT NOT NULL,
`type` VARCHAR(256) NOT NULL DEFAULT '', `type` VARCHAR(256) NOT NULL DEFAULT '',
`size` INT NOT NULL DEFAULT '0', `size` INT NOT NULL DEFAULT '0',
`internal_path` VARCHAR(256) NOT NULL DEFAULT '', `memo_id` INT DEFAULT NULL,
`memo_id` INT DEFAULT NULL `storage_type` VARCHAR(256) NOT NULL DEFAULT '',
`reference` VARCHAR(256) NOT NULL DEFAULT '',
`payload` TEXT NOT NULL
); );
-- tag -- tag
@ -95,14 +96,6 @@ CREATE TABLE `activity` (
`payload` TEXT NOT NULL `payload` TEXT NOT NULL
); );
-- storage
CREATE TABLE `storage` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(256) NOT NULL,
`type` VARCHAR(256) NOT NULL,
`config` TEXT NOT NULL
);
-- idp -- idp
CREATE TABLE `idp` ( CREATE TABLE `idp` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,

View File

@ -0,0 +1,16 @@
ALTER TABLE `resource`
ADD COLUMN `storage_type` VARCHAR(256) NOT NULL DEFAULT '',
ADD COLUMN `reference` VARCHAR(256) NOT NULL DEFAULT '',
ADD COLUMN `payload` TEXT NOT NULL;
UPDATE `resource`
SET `storage_type` = 'LOCAL', `reference` = `internal_path`
WHERE `internal_path` IS NOT NULL AND `internal_path` != '';
UPDATE `resource`
SET `storage_type` = 'EXTERNAL', `reference` = `external_link`
WHERE `external_link` IS NOT NULL AND `external_link` != '';
ALTER TABLE `resource`
DROP COLUMN `internal_path`,
DROP COLUMN `external_link`;

View File

@ -45,39 +45,6 @@ func (d *DB) GetDB() *sql.DB {
return d.db return d.db
} }
func (d *DB) Vacuum(ctx context.Context) error {
tx, err := d.db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
if err := vacuumMemo(ctx, tx); err != nil {
return err
}
if err := vacuumResource(ctx, tx); err != nil {
return err
}
if err := vacuumUserSetting(ctx, tx); err != nil {
return err
}
if err := vacuumMemoOrganizer(ctx, tx); err != nil {
return err
}
if err := vacuumMemoRelations(ctx, tx); err != nil {
return err
}
if err := vacuumInbox(ctx, tx); err != nil {
return err
}
if err := vacuumTag(ctx, tx); err != nil {
// Prevent revive warning.
return err
}
return tx.Commit()
}
func (d *DB) GetCurrentDBSize(ctx context.Context) (int64, error) { func (d *DB) GetCurrentDBSize(ctx context.Context) (int64, error) {
query := "SELECT SUM(`data_length` + `index_length`) AS `size` " + query := "SELECT SUM(`data_length` + `index_length`) AS `size` " +
" FROM information_schema.TABLES" + " FROM information_schema.TABLES" +

View File

@ -6,13 +6,29 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/pkg/errors"
"google.golang.org/protobuf/encoding/protojson"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
) )
func (d *DB) CreateResource(ctx context.Context, create *store.Resource) (*store.Resource, error) { func (d *DB) CreateResource(ctx context.Context, create *store.Resource) (*store.Resource, error) {
fields := []string{"`uid`", "`filename`", "`blob`", "`external_link`", "`type`", "`size`", "`creator_id`", "`internal_path`", "`memo_id`"} fields := []string{"`uid`", "`filename`", "`blob`", "`type`", "`size`", "`creator_id`", "`memo_id`", "`storage_type`", "`reference`", "`payload`"}
placeholder := []string{"?", "?", "?", "?", "?", "?", "?", "?", "?"} placeholder := []string{"?", "?", "?", "?", "?", "?", "?", "?", "?", "?"}
args := []any{create.UID, create.Filename, create.Blob, create.ExternalLink, create.Type, create.Size, create.CreatorID, create.InternalPath, create.MemoID} storageType := ""
if create.StorageType != storepb.ResourceStorageType_RESOURCE_STORAGE_TYPE_UNSPECIFIED {
storageType = create.StorageType.String()
}
payloadString := "{}"
if create.Payload != nil {
bytes, err := protojson.Marshal(create.Payload)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal resource payload")
}
payloadString = string(bytes)
}
args := []any{create.UID, create.Filename, create.Blob, create.Type, create.Size, create.CreatorID, create.MemoID, storageType, create.Reference, payloadString}
stmt := "INSERT INTO `resource` (" + strings.Join(fields, ", ") + ") VALUES (" + strings.Join(placeholder, ", ") + ")" stmt := "INSERT INTO `resource` (" + strings.Join(fields, ", ") + ") VALUES (" + strings.Join(placeholder, ", ") + ")"
result, err := d.db.ExecContext(ctx, stmt, args...) result, err := d.db.ExecContext(ctx, stmt, args...)
@ -51,12 +67,12 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
where = append(where, "`memo_id` IS NOT NULL") where = append(where, "`memo_id` IS NOT NULL")
} }
fields := []string{"`id`", "`uid`", "`filename`", "`external_link`", "`type`", "`size`", "`creator_id`", "UNIX_TIMESTAMP(`created_ts`)", "UNIX_TIMESTAMP(`updated_ts`)", "`internal_path`", "`memo_id`"} fields := []string{"`id`", "`uid`", "`filename`", "`type`", "`size`", "`creator_id`", "`created_ts`", "`updated_ts`", "`memo_id`", "`storage_type`", "`reference`", "`payload`"}
if find.GetBlob { if find.GetBlob {
fields = append(fields, "`blob`") fields = append(fields, "`blob`")
} }
query := fmt.Sprintf("SELECT %s FROM `resource` WHERE %s ORDER BY `updated_ts` DESC, `created_ts` DESC", strings.Join(fields, ", "), strings.Join(where, " AND ")) query := fmt.Sprintf("SELECT %s FROM `resource` WHERE %s ORDER BY `created_ts` DESC", strings.Join(fields, ", "), strings.Join(where, " AND "))
if find.Limit != nil { if find.Limit != nil {
query = fmt.Sprintf("%s LIMIT %d", query, *find.Limit) query = fmt.Sprintf("%s LIMIT %d", query, *find.Limit)
if find.Offset != nil { if find.Offset != nil {
@ -74,18 +90,21 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
for rows.Next() { for rows.Next() {
resource := store.Resource{} resource := store.Resource{}
var memoID sql.NullInt32 var memoID sql.NullInt32
var storageType string
var payloadBytes []byte
dests := []any{ dests := []any{
&resource.ID, &resource.ID,
&resource.UID, &resource.UID,
&resource.Filename, &resource.Filename,
&resource.ExternalLink,
&resource.Type, &resource.Type,
&resource.Size, &resource.Size,
&resource.CreatorID, &resource.CreatorID,
&resource.CreatedTs, &resource.CreatedTs,
&resource.UpdatedTs, &resource.UpdatedTs,
&resource.InternalPath,
&memoID, &memoID,
&storageType,
&resource.Reference,
&payloadBytes,
} }
if find.GetBlob { if find.GetBlob {
dests = append(dests, &resource.Blob) dests = append(dests, &resource.Blob)
@ -93,9 +112,16 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
if err := rows.Scan(dests...); err != nil { if err := rows.Scan(dests...); err != nil {
return nil, err return nil, err
} }
if memoID.Valid { if memoID.Valid {
resource.MemoID = &memoID.Int32 resource.MemoID = &memoID.Int32
} }
resource.StorageType = storepb.ResourceStorageType(storepb.ResourceStorageType_value[storageType])
payload := &storepb.ResourcePayload{}
if err := protojsonUnmarshaler.Unmarshal(payloadBytes, payload); err != nil {
return nil, err
}
resource.Payload = payload
list = append(list, &resource) list = append(list, &resource)
} }
@ -118,7 +144,7 @@ func (d *DB) GetResource(ctx context.Context, find *store.FindResource) (*store.
return list[0], nil return list[0], nil
} }
func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (*store.Resource, error) { func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) error {
set, args := []string{}, []any{} set, args := []string{}, []any{}
if v := update.UID; v != nil { if v := update.UID; v != nil {
@ -130,26 +156,20 @@ func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (
if v := update.Filename; v != nil { if v := update.Filename; v != nil {
set, args = append(set, "`filename` = ?"), append(args, *v) set, args = append(set, "`filename` = ?"), append(args, *v)
} }
if v := update.InternalPath; v != nil {
set, args = append(set, "`internal_path` = ?"), append(args, *v)
}
if v := update.ExternalLink; v != nil {
set, args = append(set, "`external_link` = ?"), append(args, *v)
}
if v := update.MemoID; v != nil { if v := update.MemoID; v != nil {
set, args = append(set, "`memo_id` = ?"), append(args, *v) set, args = append(set, "`memo_id` = ?"), append(args, *v)
} }
if v := update.Blob; v != nil {
set, args = append(set, "`blob` = ?"), append(args, v)
}
args = append(args, update.ID) args = append(args, update.ID)
stmt := "UPDATE `resource` SET " + strings.Join(set, ", ") + " WHERE `id` = ?" stmt := "UPDATE `resource` SET " + strings.Join(set, ", ") + " WHERE `id` = ?"
if _, err := d.db.ExecContext(ctx, stmt, args...); err != nil { result, err := d.db.ExecContext(ctx, stmt, args...)
return nil, err if err != nil {
return err
} }
if _, err := result.RowsAffected(); err != nil {
return d.GetResource(ctx, &store.FindResource{ID: &update.ID}) return err
}
return nil
} }
func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) error { func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) error {
@ -162,20 +182,5 @@ func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) e
return err return err
} }
if err := d.Vacuum(ctx); err != nil {
// Prevent linter warning.
return err
}
return nil
}
func vacuumResource(ctx context.Context, tx *sql.Tx) error {
stmt := "DELETE FROM `resource` WHERE `creator_id` NOT IN (SELECT `id` FROM `user`)"
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil return nil
} }

View File

@ -2,7 +2,6 @@ package mysql
import ( import (
"context" "context"
"database/sql"
"strings" "strings"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
@ -63,13 +62,3 @@ func (d *DB) DeleteTag(ctx context.Context, delete *store.DeleteTag) error {
} }
return nil return nil
} }
func vacuumTag(ctx context.Context, tx *sql.Tx) error {
stmt := "DELETE FROM `tag` WHERE `creator_id` NOT IN (SELECT `id` FROM `user`)"
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil
}

View File

@ -160,11 +160,5 @@ func (d *DB) DeleteUser(ctx context.Context, delete *store.DeleteUser) error {
if _, err := result.RowsAffected(); err != nil { if _, err := result.RowsAffected(); err != nil {
return err return err
} }
if err := d.Vacuum(ctx); err != nil {
// Prevent linter warning.
return err
}
return nil return nil
} }

View File

@ -2,7 +2,6 @@ package mysql
import ( import (
"context" "context"
"database/sql"
"strings" "strings"
storepb "github.com/usememos/memos/proto/gen/store" storepb "github.com/usememos/memos/proto/gen/store"
@ -55,13 +54,3 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting)
return userSettingList, nil return userSettingList, nil
} }
func vacuumUserSetting(ctx context.Context, tx *sql.Tx) error {
stmt := "DELETE FROM `user_setting` WHERE `user_id` NOT IN (SELECT `id` FROM `user`)"
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil
}

View File

@ -2,7 +2,6 @@ package postgres
import ( import (
"context" "context"
"database/sql"
"strings" "strings"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -133,13 +132,3 @@ func (d *DB) DeleteInbox(ctx context.Context, delete *store.DeleteInbox) error {
} }
return nil return nil
} }
func vacuumInbox(ctx context.Context, tx *sql.Tx) error {
stmt := `DELETE FROM inbox WHERE sender_id NOT IN (SELECT id FROM "user")`
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil
}

View File

@ -2,7 +2,6 @@ package postgres
import ( import (
"context" "context"
"database/sql"
"fmt" "fmt"
"strings" "strings"
@ -203,9 +202,3 @@ func (d *DB) DeleteMemo(ctx context.Context, delete *store.DeleteMemo) error {
} }
return nil return nil
} }
func vacuumMemo(ctx context.Context, tx *sql.Tx) error {
stmt := `DELETE FROM memo WHERE creator_id NOT IN (SELECT id FROM "user")`
_, err := tx.ExecContext(ctx, stmt)
return err
}

View File

@ -2,7 +2,6 @@ package postgres
import ( import (
"context" "context"
"database/sql"
"fmt" "fmt"
"strings" "strings"
@ -90,18 +89,3 @@ func (d *DB) DeleteMemoOrganizer(ctx context.Context, delete *store.DeleteMemoOr
} }
return nil return nil
} }
func vacuumMemoOrganizer(ctx context.Context, tx *sql.Tx) error {
stmt := `
DELETE FROM
memo_organizer
WHERE
memo_id NOT IN (SELECT id FROM memo)
OR user_id NOT IN (SELECT id FROM "user")`
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil
}

View File

@ -2,7 +2,6 @@ package postgres
import ( import (
"context" "context"
"database/sql"
"strings" "strings"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
@ -101,13 +100,3 @@ func (d *DB) DeleteMemoRelation(ctx context.Context, delete *store.DeleteMemoRel
} }
return nil return nil
} }
func vacuumMemoRelations(ctx context.Context, tx *sql.Tx) error {
if _, err := tx.ExecContext(ctx, `
DELETE FROM memo_relation
WHERE memo_id NOT IN (SELECT id FROM memo) OR related_memo_id NOT IN (SELECT id FROM memo)
`); err != nil {
return err
}
return nil
}

View File

@ -71,11 +71,12 @@ CREATE TABLE resource (
updated_ts BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW()), updated_ts BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW()),
filename TEXT NOT NULL, filename TEXT NOT NULL,
blob BYTEA, blob BYTEA,
external_link TEXT NOT NULL,
type TEXT NOT NULL DEFAULT '', type TEXT NOT NULL DEFAULT '',
size INTEGER NOT NULL DEFAULT 0, size INTEGER NOT NULL DEFAULT 0,
internal_path TEXT NOT NULL DEFAULT '', memo_id INTEGER DEFAULT NULL,
memo_id INTEGER DEFAULT NULL storage_type TEXT NOT NULL DEFAULT '',
reference TEXT NOT NULL DEFAULT '',
payload TEXT NOT NULL DEFAULT '{}'
); );
-- tag -- tag
@ -95,14 +96,6 @@ CREATE TABLE activity (
payload JSONB NOT NULL DEFAULT '{}' payload JSONB NOT NULL DEFAULT '{}'
); );
-- storage
CREATE TABLE storage (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
type TEXT NOT NULL,
config JSONB NOT NULL DEFAULT '{}'
);
-- idp -- idp
CREATE TABLE idp ( CREATE TABLE idp (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,

View File

@ -0,0 +1,16 @@
ALTER TABLE `resource`
ADD COLUMN `storage_type` VARCHAR(256) NOT NULL DEFAULT '',
ADD COLUMN `reference` VARCHAR(256) NOT NULL DEFAULT '',
ADD COLUMN `payload` TEXT NOT NULL;
UPDATE `resource`
SET `storage_type` = 'LOCAL', `reference` = `internal_path`
WHERE `internal_path` IS NOT NULL AND `internal_path` != '';
UPDATE `resource`
SET `storage_type` = 'EXTERNAL', `reference` = `external_link`
WHERE `external_link` IS NOT NULL AND `external_link` != '';
ALTER TABLE `resource`
DROP COLUMN `internal_path`,
DROP COLUMN `external_link`;

View File

@ -44,39 +44,6 @@ func (d *DB) GetDB() *sql.DB {
return d.db return d.db
} }
func (d *DB) Vacuum(ctx context.Context) error {
tx, err := d.db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
if err := vacuumMemo(ctx, tx); err != nil {
return err
}
if err := vacuumResource(ctx, tx); err != nil {
return err
}
if err := vacuumUserSetting(ctx, tx); err != nil {
return err
}
if err := vacuumMemoOrganizer(ctx, tx); err != nil {
return err
}
if err := vacuumMemoRelations(ctx, tx); err != nil {
return err
}
if err := vacuumInbox(ctx, tx); err != nil {
return err
}
if err := vacuumTag(ctx, tx); err != nil {
// Prevent revive warning.
return err
}
return tx.Commit()
}
func (*DB) GetCurrentDBSize(context.Context) (int64, error) { func (*DB) GetCurrentDBSize(context.Context) (int64, error) {
return 0, errors.New("unimplemented") return 0, errors.New("unimplemented")
} }

View File

@ -6,12 +6,28 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/pkg/errors"
"google.golang.org/protobuf/encoding/protojson"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
) )
func (d *DB) CreateResource(ctx context.Context, create *store.Resource) (*store.Resource, error) { func (d *DB) CreateResource(ctx context.Context, create *store.Resource) (*store.Resource, error) {
fields := []string{"uid", "filename", "blob", "external_link", "type", "size", "creator_id", "internal_path", "memo_id"} fields := []string{"uid", "filename", "blob", "type", "size", "creator_id", "memo_id", "storage_type", "reference", "payload"}
args := []any{create.UID, create.Filename, create.Blob, create.ExternalLink, create.Type, create.Size, create.CreatorID, create.InternalPath, create.MemoID} storageType := ""
if create.StorageType != storepb.ResourceStorageType_RESOURCE_STORAGE_TYPE_UNSPECIFIED {
storageType = create.StorageType.String()
}
payloadString := "{}"
if create.Payload != nil {
bytes, err := protojson.Marshal(create.Payload)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal resource payload")
}
payloadString = string(bytes)
}
args := []any{create.UID, create.Filename, create.Blob, create.Type, create.Size, create.CreatorID, create.MemoID, storageType, create.Reference, payloadString}
stmt := "INSERT INTO resource (" + strings.Join(fields, ", ") + ") VALUES (" + placeholders(len(args)) + ") RETURNING id, created_ts, updated_ts" stmt := "INSERT INTO resource (" + strings.Join(fields, ", ") + ") VALUES (" + placeholders(len(args)) + ") RETURNING id, created_ts, updated_ts"
if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(&create.ID, &create.CreatedTs, &create.UpdatedTs); err != nil { if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(&create.ID, &create.CreatedTs, &create.UpdatedTs); err != nil {
@ -42,7 +58,7 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
where = append(where, "memo_id IS NOT NULL") where = append(where, "memo_id IS NOT NULL")
} }
fields := []string{"id", "uid", "filename", "external_link", "type", "size", "creator_id", "created_ts", "updated_ts", "internal_path", "memo_id"} fields := []string{"id", "uid", "filename", "type", "size", "creator_id", "created_ts", "updated_ts", "memo_id", "storage_type", "reference", "payload"}
if find.GetBlob { if find.GetBlob {
fields = append(fields, "blob") fields = append(fields, "blob")
} }
@ -52,7 +68,7 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
%s %s
FROM resource FROM resource
WHERE %s WHERE %s
ORDER BY updated_ts DESC, created_ts DESC ORDER BY created_ts DESC
`, strings.Join(fields, ", "), strings.Join(where, " AND ")) `, strings.Join(fields, ", "), strings.Join(where, " AND "))
if find.Limit != nil { if find.Limit != nil {
query = fmt.Sprintf("%s LIMIT %d", query, *find.Limit) query = fmt.Sprintf("%s LIMIT %d", query, *find.Limit)
@ -71,18 +87,21 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
for rows.Next() { for rows.Next() {
resource := store.Resource{} resource := store.Resource{}
var memoID sql.NullInt32 var memoID sql.NullInt32
var storageType string
var payloadBytes []byte
dests := []any{ dests := []any{
&resource.ID, &resource.ID,
&resource.UID, &resource.UID,
&resource.Filename, &resource.Filename,
&resource.ExternalLink,
&resource.Type, &resource.Type,
&resource.Size, &resource.Size,
&resource.CreatorID, &resource.CreatorID,
&resource.CreatedTs, &resource.CreatedTs,
&resource.UpdatedTs, &resource.UpdatedTs,
&resource.InternalPath,
&memoID, &memoID,
&storageType,
&resource.Reference,
&payloadBytes,
} }
if find.GetBlob { if find.GetBlob {
dests = append(dests, &resource.Blob) dests = append(dests, &resource.Blob)
@ -90,9 +109,16 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
if err := rows.Scan(dests...); err != nil { if err := rows.Scan(dests...); err != nil {
return nil, err return nil, err
} }
if memoID.Valid { if memoID.Valid {
resource.MemoID = &memoID.Int32 resource.MemoID = &memoID.Int32
} }
resource.StorageType = storepb.ResourceStorageType(storepb.ResourceStorageType_value[storageType])
payload := &storepb.ResourcePayload{}
if err := protojsonUnmarshaler.Unmarshal(payloadBytes, payload); err != nil {
return nil, err
}
resource.Payload = payload
list = append(list, &resource) list = append(list, &resource)
} }
@ -103,7 +129,7 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
return list, nil return list, nil
} }
func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (*store.Resource, error) { func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) error {
set, args := []string{}, []any{} set, args := []string{}, []any{}
if v := update.UID; v != nil { if v := update.UID; v != nil {
@ -115,40 +141,20 @@ func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (
if v := update.Filename; v != nil { if v := update.Filename; v != nil {
set, args = append(set, "filename = "+placeholder(len(args)+1)), append(args, *v) set, args = append(set, "filename = "+placeholder(len(args)+1)), append(args, *v)
} }
if v := update.InternalPath; v != nil {
set, args = append(set, "internal_path = "+placeholder(len(args)+1)), append(args, *v)
}
if v := update.ExternalLink; v != nil {
set, args = append(set, "external_link = "+placeholder(len(args)+1)), append(args, *v)
}
if v := update.MemoID; v != nil { if v := update.MemoID; v != nil {
set, args = append(set, "memo_id = "+placeholder(len(args)+1)), append(args, *v) set, args = append(set, "memo_id = "+placeholder(len(args)+1)), append(args, *v)
} }
if v := update.Blob; v != nil {
set, args = append(set, "blob = "+placeholder(len(args)+1)), append(args, v)
}
fields := []string{"id", "uid", "filename", "external_link", "type", "size", "creator_id", "created_ts", "updated_ts", "internal_path"} stmt := `UPDATE resource SET ` + strings.Join(set, ", ") + ` WHERE id = ` + placeholder(len(args)+1)
stmt := `UPDATE resource SET ` + strings.Join(set, ", ") + ` WHERE id = ` + placeholder(len(args)+1) + ` RETURNING ` + strings.Join(fields, ", ")
args = append(args, update.ID) args = append(args, update.ID)
resource := store.Resource{} result, err := d.db.ExecContext(ctx, stmt, args...)
dests := []any{ if err != nil {
&resource.ID, return err
&resource.UID,
&resource.Filename,
&resource.ExternalLink,
&resource.Type,
&resource.Size,
&resource.CreatorID,
&resource.CreatedTs,
&resource.UpdatedTs,
&resource.InternalPath,
} }
if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(dests...); err != nil { if _, err := result.RowsAffected(); err != nil {
return nil, err return err
} }
return nil
return &resource, nil
} }
func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) error { func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) error {
@ -162,13 +168,3 @@ func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) e
} }
return nil return nil
} }
func vacuumResource(ctx context.Context, tx *sql.Tx) error {
stmt := `DELETE FROM resource WHERE creator_id NOT IN (SELECT id FROM "user")`
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil
}

View File

@ -2,7 +2,6 @@ package postgres
import ( import (
"context" "context"
"database/sql"
"strings" "strings"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
@ -69,13 +68,3 @@ func (d *DB) DeleteTag(ctx context.Context, delete *store.DeleteTag) error {
} }
return nil return nil
} }
func vacuumTag(ctx context.Context, tx *sql.Tx) error {
stmt := `DELETE FROM tag WHERE creator_id NOT IN (SELECT id FROM "user")`
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil
}

View File

@ -165,11 +165,5 @@ func (d *DB) DeleteUser(ctx context.Context, delete *store.DeleteUser) error {
if _, err := result.RowsAffected(); err != nil { if _, err := result.RowsAffected(); err != nil {
return err return err
} }
if err := d.Vacuum(ctx); err != nil {
// Prevent linter warning.
return err
}
return nil return nil
} }

View File

@ -2,7 +2,6 @@ package postgres
import ( import (
"context" "context"
"database/sql"
"strings" "strings"
storepb "github.com/usememos/memos/proto/gen/store" storepb "github.com/usememos/memos/proto/gen/store"
@ -68,13 +67,3 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting)
return userSettingList, nil return userSettingList, nil
} }
func vacuumUserSetting(ctx context.Context, tx *sql.Tx) error {
stmt := `DELETE FROM user_setting WHERE user_id NOT IN (SELECT id FROM "user")`
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil
}

View File

@ -2,7 +2,6 @@ package sqlite
import ( import (
"context" "context"
"database/sql"
"strings" "strings"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -124,22 +123,3 @@ func (d *DB) DeleteInbox(ctx context.Context, delete *store.DeleteInbox) error {
} }
return nil return nil
} }
func vacuumInbox(ctx context.Context, tx *sql.Tx) error {
stmt := `
DELETE FROM
inbox
WHERE
sender_id NOT IN (
SELECT
id
FROM
user
)`
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil
}

View File

@ -2,7 +2,6 @@ package sqlite
import ( import (
"context" "context"
"database/sql"
"fmt" "fmt"
"strings" "strings"
@ -187,20 +186,5 @@ func (d *DB) DeleteMemo(ctx context.Context, delete *store.DeleteMemo) error {
if _, err := result.RowsAffected(); err != nil { if _, err := result.RowsAffected(); err != nil {
return err return err
} }
if err := d.Vacuum(ctx); err != nil {
// Prevent linter warning.
return err
}
return nil
}
func vacuumMemo(ctx context.Context, tx *sql.Tx) error {
stmt := "DELETE FROM `memo` WHERE `creator_id` NOT IN (SELECT `id` FROM `user`)"
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil return nil
} }

View File

@ -2,7 +2,6 @@ package sqlite
import ( import (
"context" "context"
"database/sql"
"fmt" "fmt"
"strings" "strings"
@ -88,28 +87,3 @@ func (d *DB) DeleteMemoOrganizer(ctx context.Context, delete *store.DeleteMemoOr
} }
return nil return nil
} }
func vacuumMemoOrganizer(ctx context.Context, tx *sql.Tx) error {
stmt := `
DELETE FROM
memo_organizer
WHERE
memo_id NOT IN (
SELECT
id
FROM
memo
)
OR user_id NOT IN (
SELECT
id
FROM
user
)`
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil
}

View File

@ -2,7 +2,6 @@ package sqlite
import ( import (
"context" "context"
"database/sql"
"strings" "strings"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
@ -103,13 +102,3 @@ func (d *DB) DeleteMemoRelation(ctx context.Context, delete *store.DeleteMemoRel
} }
return nil return nil
} }
func vacuumMemoRelations(ctx context.Context, tx *sql.Tx) error {
if _, err := tx.ExecContext(ctx, `
DELETE FROM memo_relation
WHERE memo_id NOT IN (SELECT id FROM memo) OR related_memo_id NOT IN (SELECT id FROM memo)
`); err != nil {
return err
}
return nil
}

View File

@ -78,11 +78,12 @@ CREATE TABLE resource (
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')), updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
filename TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '',
blob BLOB DEFAULT NULL, blob BLOB DEFAULT NULL,
external_link TEXT NOT NULL DEFAULT '',
type TEXT NOT NULL DEFAULT '', type TEXT NOT NULL DEFAULT '',
size INTEGER NOT NULL DEFAULT 0, size INTEGER NOT NULL DEFAULT 0,
internal_path TEXT NOT NULL DEFAULT '', memo_id INTEGER,
memo_id INTEGER storage_type TEXT NOT NULL DEFAULT '',
reference TEXT NOT NULL DEFAULT '',
payload TEXT NOT NULL DEFAULT '{}'
); );
CREATE INDEX idx_resource_creator_id ON resource (creator_id); CREATE INDEX idx_resource_creator_id ON resource (creator_id);
@ -106,14 +107,6 @@ CREATE TABLE activity (
payload TEXT NOT NULL DEFAULT '{}' payload TEXT NOT NULL DEFAULT '{}'
); );
-- storage
CREATE TABLE storage (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
type TEXT NOT NULL,
config TEXT NOT NULL DEFAULT '{}'
);
-- idp -- idp
CREATE TABLE idp ( CREATE TABLE idp (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,

View File

@ -0,0 +1,17 @@
ALTER TABLE resource ADD COLUMN storage_type TEXT NOT NULL DEFAULT '';
ALTER TABLE resource ADD COLUMN reference TEXT NOT NULL DEFAULT '';
ALTER TABLE resource ADD COLUMN payload TEXT NOT NULL DEFAULT '{}';
UPDATE resource
SET storage_type = 'LOCAL', reference = internal_path
WHERE internal_path IS NOT NULL AND internal_path != '';
UPDATE resource
SET storage_type = 'EXTERNAL', reference = external_link
WHERE external_link IS NOT NULL AND external_link != '';
ALTER TABLE resource
DROP COLUMN internal_path,
DROP COLUMN external_link;

View File

@ -6,13 +6,29 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/pkg/errors"
"google.golang.org/protobuf/encoding/protojson"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
) )
func (d *DB) CreateResource(ctx context.Context, create *store.Resource) (*store.Resource, error) { func (d *DB) CreateResource(ctx context.Context, create *store.Resource) (*store.Resource, error) {
fields := []string{"`uid`", "`filename`", "`blob`", "`external_link`", "`type`", "`size`", "`creator_id`", "`internal_path`", "`memo_id`"} fields := []string{"`uid`", "`filename`", "`blob`", "`type`", "`size`", "`creator_id`", "`memo_id`", "`storage_type`", "`reference`", "`payload`"}
placeholder := []string{"?", "?", "?", "?", "?", "?", "?", "?", "?"} placeholder := []string{"?", "?", "?", "?", "?", "?", "?", "?", "?", "?"}
args := []any{create.UID, create.Filename, create.Blob, create.ExternalLink, create.Type, create.Size, create.CreatorID, create.InternalPath, create.MemoID} storageType := ""
if create.StorageType != storepb.ResourceStorageType_RESOURCE_STORAGE_TYPE_UNSPECIFIED {
storageType = create.StorageType.String()
}
payloadString := "{}"
if create.Payload != nil {
bytes, err := protojson.Marshal(create.Payload)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal resource payload")
}
payloadString = string(bytes)
}
args := []any{create.UID, create.Filename, create.Blob, create.Type, create.Size, create.CreatorID, create.MemoID, storageType, create.Reference, payloadString}
stmt := "INSERT INTO `resource` (" + strings.Join(fields, ", ") + ") VALUES (" + strings.Join(placeholder, ", ") + ") RETURNING `id`, `created_ts`, `updated_ts`" stmt := "INSERT INTO `resource` (" + strings.Join(fields, ", ") + ") VALUES (" + strings.Join(placeholder, ", ") + ") RETURNING `id`, `created_ts`, `updated_ts`"
if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(&create.ID, &create.CreatedTs, &create.UpdatedTs); err != nil { if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(&create.ID, &create.CreatedTs, &create.UpdatedTs); err != nil {
@ -44,12 +60,12 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
where = append(where, "`memo_id` IS NOT NULL") where = append(where, "`memo_id` IS NOT NULL")
} }
fields := []string{"`id`", "`uid`", "`filename`", "`external_link`", "`type`", "`size`", "`creator_id`", "`created_ts`", "`updated_ts`", "`internal_path`", "`memo_id`"} fields := []string{"`id`", "`uid`", "`filename`", "`type`", "`size`", "`creator_id`", "`created_ts`", "`updated_ts`", "`memo_id`", "`storage_type`", "`reference`", "`payload`"}
if find.GetBlob { if find.GetBlob {
fields = append(fields, "`blob`") fields = append(fields, "`blob`")
} }
query := fmt.Sprintf("SELECT %s FROM `resource` WHERE %s ORDER BY `updated_ts` DESC, `created_ts` DESC", strings.Join(fields, ", "), strings.Join(where, " AND ")) query := fmt.Sprintf("SELECT %s FROM `resource` WHERE %s ORDER BY `created_ts` DESC", strings.Join(fields, ", "), strings.Join(where, " AND "))
if find.Limit != nil { if find.Limit != nil {
query = fmt.Sprintf("%s LIMIT %d", query, *find.Limit) query = fmt.Sprintf("%s LIMIT %d", query, *find.Limit)
if find.Offset != nil { if find.Offset != nil {
@ -67,18 +83,21 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
for rows.Next() { for rows.Next() {
resource := store.Resource{} resource := store.Resource{}
var memoID sql.NullInt32 var memoID sql.NullInt32
var storageType string
var payloadBytes []byte
dests := []any{ dests := []any{
&resource.ID, &resource.ID,
&resource.UID, &resource.UID,
&resource.Filename, &resource.Filename,
&resource.ExternalLink,
&resource.Type, &resource.Type,
&resource.Size, &resource.Size,
&resource.CreatorID, &resource.CreatorID,
&resource.CreatedTs, &resource.CreatedTs,
&resource.UpdatedTs, &resource.UpdatedTs,
&resource.InternalPath,
&memoID, &memoID,
&storageType,
&resource.Reference,
&payloadBytes,
} }
if find.GetBlob { if find.GetBlob {
dests = append(dests, &resource.Blob) dests = append(dests, &resource.Blob)
@ -86,9 +105,16 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
if err := rows.Scan(dests...); err != nil { if err := rows.Scan(dests...); err != nil {
return nil, err return nil, err
} }
if memoID.Valid { if memoID.Valid {
resource.MemoID = &memoID.Int32 resource.MemoID = &memoID.Int32
} }
resource.StorageType = storepb.ResourceStorageType(storepb.ResourceStorageType_value[storageType])
payload := &storepb.ResourcePayload{}
if err := protojsonUnmarshaler.Unmarshal(payloadBytes, payload); err != nil {
return nil, err
}
resource.Payload = payload
list = append(list, &resource) list = append(list, &resource)
} }
@ -99,7 +125,7 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
return list, nil return list, nil
} }
func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (*store.Resource, error) { func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) error {
set, args := []string{}, []any{} set, args := []string{}, []any{}
if v := update.UID; v != nil { if v := update.UID; v != nil {
@ -111,40 +137,20 @@ func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (
if v := update.Filename; v != nil { if v := update.Filename; v != nil {
set, args = append(set, "`filename` = ?"), append(args, *v) set, args = append(set, "`filename` = ?"), append(args, *v)
} }
if v := update.InternalPath; v != nil {
set, args = append(set, "`internal_path` = ?"), append(args, *v)
}
if v := update.ExternalLink; v != nil {
set, args = append(set, "`external_link` = ?"), append(args, *v)
}
if v := update.MemoID; v != nil { if v := update.MemoID; v != nil {
set, args = append(set, "`memo_id` = ?"), append(args, *v) set, args = append(set, "`memo_id` = ?"), append(args, *v)
} }
if v := update.Blob; v != nil {
set, args = append(set, "`blob` = ?"), append(args, v)
}
args = append(args, update.ID) args = append(args, update.ID)
fields := []string{"`id`", "`uid`", "`filename`", "`external_link`", "`type`", "`size`", "`creator_id`", "`created_ts`", "`updated_ts`", "`internal_path`"} stmt := "UPDATE `resource` SET " + strings.Join(set, ", ") + " WHERE `id` = ?"
stmt := "UPDATE `resource` SET " + strings.Join(set, ", ") + " WHERE `id` = ? RETURNING " + strings.Join(fields, ", ") result, err := d.db.ExecContext(ctx, stmt, args...)
resource := store.Resource{} if err != nil {
dests := []any{ return errors.Wrap(err, "failed to update resource")
&resource.ID,
&resource.UID,
&resource.Filename,
&resource.ExternalLink,
&resource.Type,
&resource.Size,
&resource.CreatorID,
&resource.CreatedTs,
&resource.UpdatedTs,
&resource.InternalPath,
} }
if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(dests...); err != nil { if _, err := result.RowsAffected(); err != nil {
return nil, err return err
} }
return nil
return &resource, nil
} }
func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) error { func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) error {
@ -156,21 +162,5 @@ func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) e
if _, err := result.RowsAffected(); err != nil { if _, err := result.RowsAffected(); err != nil {
return err return err
} }
if err := d.Vacuum(ctx); err != nil {
// Prevent linter warning.
return err
}
return nil
}
func vacuumResource(ctx context.Context, tx *sql.Tx) error {
stmt := "DELETE FROM `resource` WHERE `creator_id` NOT IN (SELECT `id` FROM `user`)"
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil return nil
} }

View File

@ -58,56 +58,6 @@ func (d *DB) GetDB() *sql.DB {
return d.db return d.db
} }
func (d *DB) Vacuum(ctx context.Context) error {
tx, err := d.db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
if err := vacuumImpl(ctx, tx); err != nil {
return err
}
if err := tx.Commit(); err != nil {
return err
}
// Vacuum sqlite database file size after deleting resource.
if _, err := d.db.Exec("VACUUM"); err != nil {
return err
}
return nil
}
func vacuumImpl(ctx context.Context, tx *sql.Tx) error {
if err := vacuumMemo(ctx, tx); err != nil {
return err
}
if err := vacuumResource(ctx, tx); err != nil {
return err
}
if err := vacuumUserSetting(ctx, tx); err != nil {
return err
}
if err := vacuumMemoOrganizer(ctx, tx); err != nil {
return err
}
if err := vacuumMemoRelations(ctx, tx); err != nil {
return err
}
if err := vacuumInbox(ctx, tx); err != nil {
return err
}
if err := vacuumTag(ctx, tx); err != nil {
// Prevent revive warning.
return err
}
return nil
}
func (d *DB) GetCurrentDBSize(context.Context) (int64, error) { func (d *DB) GetCurrentDBSize(context.Context) (int64, error) {
fi, err := os.Stat(d.profile.DSN) fi, err := os.Stat(d.profile.DSN)
if err != nil { if err != nil {

View File

@ -2,7 +2,6 @@ package sqlite
import ( import (
"context" "context"
"database/sql"
"strings" "strings"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
@ -79,22 +78,3 @@ func (d *DB) DeleteTag(ctx context.Context, delete *store.DeleteTag) error {
} }
return nil return nil
} }
func vacuumTag(ctx context.Context, tx *sql.Tx) error {
stmt := `
DELETE FROM
tag
WHERE
creator_id NOT IN (
SELECT
id
FROM
user
)`
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil
}

View File

@ -169,11 +169,5 @@ func (d *DB) DeleteUser(ctx context.Context, delete *store.DeleteUser) error {
if _, err := result.RowsAffected(); err != nil { if _, err := result.RowsAffected(); err != nil {
return err return err
} }
if err := d.Vacuum(ctx); err != nil {
// Prevent linter warning.
return err
}
return nil return nil
} }

View File

@ -2,7 +2,6 @@ package sqlite
import ( import (
"context" "context"
"database/sql"
"strings" "strings"
storepb "github.com/usememos/memos/proto/gen/store" storepb "github.com/usememos/memos/proto/gen/store"
@ -67,22 +66,3 @@ func (d *DB) ListUserSettings(ctx context.Context, find *store.FindUserSetting)
return userSettingList, nil return userSettingList, nil
} }
func vacuumUserSetting(ctx context.Context, tx *sql.Tx) error {
stmt := `
DELETE FROM
user_setting
WHERE
user_id NOT IN (
SELECT
id
FROM
user
)`
_, err := tx.ExecContext(ctx, stmt)
if err != nil {
return err
}
return nil
}

View File

@ -12,7 +12,6 @@ type Driver interface {
Close() error Close() error
Migrate(ctx context.Context) error Migrate(ctx context.Context) error
Vacuum(ctx context.Context) error
// current file is driver // current file is driver
GetCurrentDBSize(ctx context.Context) (int64, error) GetCurrentDBSize(ctx context.Context) (int64, error)
@ -28,7 +27,7 @@ type Driver interface {
// Resource model related methods. // Resource model related methods.
CreateResource(ctx context.Context, create *Resource) (*Resource, error) CreateResource(ctx context.Context, create *Resource) (*Resource, error)
ListResources(ctx context.Context, find *FindResource) ([]*Resource, error) ListResources(ctx context.Context, find *FindResource) ([]*Resource, error)
UpdateResource(ctx context.Context, update *UpdateResource) (*Resource, error) UpdateResource(ctx context.Context, update *UpdateResource) error
DeleteResource(ctx context.Context, delete *DeleteResource) error DeleteResource(ctx context.Context, delete *DeleteResource) error
// Memo model related methods. // Memo model related methods.

View File

@ -2,18 +2,13 @@ package store
import ( import (
"context" "context"
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/usememos/memos/internal/util" "github.com/usememos/memos/internal/util"
) storepb "github.com/usememos/memos/proto/gen/store"
const (
// thumbnailImagePath is the directory to store image thumbnails.
thumbnailImagePath = ".thumbnail_cache"
) )
type Resource struct { type Resource struct {
@ -30,10 +25,13 @@ type Resource struct {
// Domain specific fields // Domain specific fields
Filename string Filename string
Blob []byte Blob []byte
InternalPath string
ExternalLink string
Type string Type string
Size int64 Size int64
StorageType storepb.ResourceStorageType
Reference string
Payload *storepb.ResourcePayload
// The related memo ID.
MemoID *int32 MemoID *int32
} }
@ -54,10 +52,7 @@ type UpdateResource struct {
UID *string UID *string
UpdatedTs *int64 UpdatedTs *int64
Filename *string Filename *string
InternalPath *string
ExternalLink *string
MemoID *int32 MemoID *int32
Blob []byte
} }
type DeleteResource struct { type DeleteResource struct {
@ -89,9 +84,9 @@ func (s *Store) GetResource(ctx context.Context, find *FindResource) (*Resource,
return resources[0], nil return resources[0], nil
} }
func (s *Store) UpdateResource(ctx context.Context, update *UpdateResource) (*Resource, error) { func (s *Store) UpdateResource(ctx context.Context, update *UpdateResource) error {
if update.UID != nil && !util.UIDMatcher.MatchString(*update.UID) { if update.UID != nil && !util.UIDMatcher.MatchString(*update.UID) {
return nil, errors.New("invalid uid") return errors.New("invalid uid")
} }
return s.driver.UpdateResource(ctx, update) return s.driver.UpdateResource(ctx, update)
} }
@ -106,19 +101,13 @@ func (s *Store) DeleteResource(ctx context.Context, delete *DeleteResource) erro
} }
// Delete the local file. // Delete the local file.
if resource.InternalPath != "" { if resource.StorageType == storepb.ResourceStorageType_LOCAL {
resourcePath := filepath.FromSlash(resource.InternalPath) p := filepath.FromSlash(resource.Reference)
if !filepath.IsAbs(resourcePath) { if !filepath.IsAbs(p) {
resourcePath = filepath.Join(s.Profile.Data, resourcePath) p = filepath.Join(s.Profile.Data, p)
} }
_ = os.Remove(resourcePath) _ = os.Remove(p)
} }
// Delete the thumbnail.
if util.HasPrefixes(resource.Type, "image/png", "image/jpeg") {
ext := filepath.Ext(resource.Filename)
thumbnailPath := filepath.Join(s.Profile.Data, thumbnailImagePath, fmt.Sprintf("%d%s", resource.ID, ext))
_ = os.Remove(thumbnailPath)
}
return s.driver.DeleteResource(ctx, delete) return s.driver.DeleteResource(ctx, delete)
} }

View File

@ -29,10 +29,6 @@ func (*Store) MigrateManually(context.Context) error {
return nil return nil
} }
func (s *Store) Vacuum(ctx context.Context) error {
return s.driver.Vacuum(ctx)
}
func (s *Store) Close() error { func (s *Store) Close() error {
return s.driver.Close() return s.driver.Close()
} }

View File

@ -18,8 +18,6 @@ func TestResourceStore(t *testing.T) {
CreatorID: 101, CreatorID: 101,
Filename: "test.epub", Filename: "test.epub",
Blob: []byte("test"), Blob: []byte("test"),
InternalPath: "",
ExternalLink: "",
Type: "application/epub+zip", Type: "application/epub+zip",
Size: 637607, Size: 637607,
}) })

View File

@ -13,13 +13,8 @@ const applyStyles = async (sourceElement: HTMLElement, clonedElement: HTMLElemen
} catch (error) { } catch (error) {
covertFailed = true; covertFailed = true;
} }
// NOTE: Get image blob from backend to avoid CORS error.
if (covertFailed) { if (covertFailed) {
try { throw new Error(`Failed to convert image to data URL: ${url}`);
(clonedElement as HTMLImageElement).src = await convertResourceToDataURL(`/o/get/image?url=${url}`);
} catch (error) {
// do nth
}
} }
} }

View File

@ -5,7 +5,7 @@ export const getResourceUrl = (resource: Resource) => {
return resource.externalLink; return resource.externalLink;
} }
return `${import.meta.env.VITE_API_BASE_URL || window.location.origin}/o/r/${resource.uid}`; return `${import.meta.env.VITE_API_BASE_URL || window.location.origin}/file/${resource.name}`;
}; };
export const getResourceType = (resource: Resource) => { export const getResourceType = (resource: Resource) => {

View File

@ -23,7 +23,7 @@ export default defineConfig({
target: devProxyServer, target: devProxyServer,
xfwd: true, xfwd: true,
}, },
"^/o/": { "^/file": {
target: devProxyServer, target: devProxyServer,
xfwd: true, xfwd: true,
}, },