refactor: update storage setting

pull/3298/head
Steven 1 year ago
parent f25c7d9b24
commit 320963098f

@ -2059,31 +2059,44 @@ definitions:
storageType: storageType:
$ref: '#/definitions/apiv1WorkspaceStorageSettingStorageType' $ref: '#/definitions/apiv1WorkspaceStorageSettingStorageType'
description: storage_type is the storage type. description: storage_type is the storage type.
activedExternalStorageId: filepathTemplate:
type: integer
format: int32
description: The id of actived external storage.
localStoragePathTemplate:
type: string type: string
title: |- title: |-
The template of local storage path. The template of file path.
e.g. assets/{timestamp}_{filename} e.g. assets/{timestamp}_{filename}
uploadSizeLimitMb: uploadSizeLimitMb:
type: string type: string
format: int64 format: int64
description: The max upload size in megabytes. description: The max upload size in megabytes.
s3Config:
$ref: '#/definitions/apiv1WorkspaceStorageSettingS3Config'
description: The S3 config.
apiv1WorkspaceStorageSettingS3Config:
type: object
properties:
accessKeyId:
type: string
accessKeySecret:
type: string
endpoint:
type: string
region:
type: string
bucket:
type: string
title: 'Reference: https://developers.cloudflare.com/r2/examples/aws/aws-sdk-go/'
apiv1WorkspaceStorageSettingStorageType: apiv1WorkspaceStorageSettingStorageType:
type: string type: string
enum: enum:
- STORAGE_TYPE_UNSPECIFIED - STORAGE_TYPE_UNSPECIFIED
- STORAGE_TYPE_DATABASE - STORAGE_TYPE_DATABASE
- STORAGE_TYPE_LOCAL - STORAGE_TYPE_LOCAL
- STORAGE_TYPE_EXTERNAL - STORAGE_TYPE_S3
default: STORAGE_TYPE_UNSPECIFIED default: STORAGE_TYPE_UNSPECIFIED
description: |2- description: |2-
- STORAGE_TYPE_DATABASE: STORAGE_TYPE_DATABASE is the database storage type. - STORAGE_TYPE_DATABASE: STORAGE_TYPE_DATABASE is the database storage type.
- STORAGE_TYPE_LOCAL: STORAGE_TYPE_LOCAL is the local storage type. - STORAGE_TYPE_LOCAL: STORAGE_TYPE_LOCAL is the local storage type.
- STORAGE_TYPE_EXTERNAL: STORAGE_TYPE_EXTERNAL is the external storage type. - STORAGE_TYPE_S3: STORAGE_TYPE_S3 is the S3 storage type.
googlerpcStatus: googlerpcStatus:
type: object type: object
properties: properties:

@ -2,10 +2,7 @@ package s3
import ( import (
"context" "context"
"fmt"
"io" "io"
"net/url"
"strings"
"time" "time"
"github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/aws"
@ -13,21 +10,17 @@ import (
"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" awss3 "github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
const LinkLifetime = 24 * time.Hour const LinkLifetime = 24 * time.Hour
type Config struct { type Config struct {
AccessKey string AccessKeyID string
SecretKey string AcesssKeySecret string
Bucket string Endpoint string
EndPoint string
Region string Region string
URLPrefix string Bucket string
URLSuffix string
PreSign bool
} }
type Client struct { type Client struct {
@ -36,32 +29,21 @@ type Client struct {
} }
func NewClient(ctx context.Context, config *Config) (*Client, error) { func NewClient(ctx context.Context, config *Config) (*Client, error) {
// For some s3-compatible object stores, converting the hostname is not required,
// and not setting this option will result in not being able to access the corresponding object store address.
// But Aliyun OSS should disable this option
hostnameImmutable := true
if strings.HasSuffix(config.EndPoint, "aliyuncs.com") {
hostnameImmutable = false
}
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: config.Endpoint,
SigningRegion: config.Region,
HostnameImmutable: hostnameImmutable,
}, nil }, nil
}) })
s3Config, err := s3config.LoadDefaultConfig(ctx,
awsConfig, err := s3config.LoadDefaultConfig(ctx,
s3config.WithEndpointResolverWithOptions(resolver), s3config.WithEndpointResolverWithOptions(resolver),
s3config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(config.AccessKey, config.SecretKey, "")), s3config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(config.AccessKeyID, config.AcesssKeySecret, "")),
s3config.WithRegion(config.Region), s3config.WithRegion(config.Region),
) )
if err != nil { if err != nil {
return nil, err return nil, errors.Wrap(err, "failed to load s3 config")
} }
client := awss3.NewFromConfig(awsConfig) client := awss3.NewFromConfig(s3Config)
return &Client{ return &Client{
Client: client, Client: client,
Config: config, Config: config,
@ -76,73 +58,14 @@ func (client *Client) UploadFile(ctx context.Context, filename string, fileType
Body: src, Body: src,
ContentType: aws.String(fileType), ContentType: aws.String(fileType),
} }
// Set ACL according to if url prefix is set.
if client.Config.URLPrefix == "" && !client.Config.PreSign {
putInput.ACL = types.ObjectCannedACL(*aws.String("public-read"))
}
uploadOutput, err := uploader.Upload(ctx, &putInput) uploadOutput, err := uploader.Upload(ctx, &putInput)
if err != nil { if err != nil {
return "", err return "", err
} }
link := uploadOutput.Location link := uploadOutput.Location
// If url prefix is set, use it as the file link.
if client.Config.URLPrefix != "" {
parts := strings.Split(filename, "/")
for i := range parts {
parts[i] = url.PathEscape(parts[i])
}
link = fmt.Sprintf("%s/%s%s", client.Config.URLPrefix, strings.Join(parts, "/"), client.Config.URLSuffix)
}
if link == "" { if link == "" {
return "", errors.New("failed to get file link") return "", errors.New("failed to get file link")
} }
if client.Config.PreSign {
return client.PreSignLink(ctx, link)
}
return link, nil return link, nil
} }
// PreSignLink generates a pre-signed URL for the given sourceLink.
// If the link does not belong to the configured storage endpoint, it is returned as-is.
// If the link belongs to the storage, the function generates a pre-signed URL using the AWS S3 client.
func (client *Client) PreSignLink(ctx context.Context, sourceLink string) (string, error) {
u, err := url.Parse(sourceLink)
if err != nil {
return "", errors.Wrapf(err, "parse URL")
}
// if link doesn't belong to storage, then return as-is.
// the empty hostname is corner-case for AWS native endpoint.
endpointURL, err := url.Parse(client.Config.EndPoint)
if err != nil {
return "", errors.Wrapf(err, "parse Endpoint URL")
}
endpointHost := endpointURL.Hostname()
if client.Config.Bucket != "" && !strings.Contains(endpointHost, client.Config.Bucket) {
endpointHost = fmt.Sprintf("%s.%s", client.Config.Bucket, endpointHost)
}
if client.Config.EndPoint != "" && !strings.Contains(endpointHost, u.Hostname()) {
return sourceLink, nil
}
filename := u.Path
if prefixLen := len(client.Config.URLPrefix); len(filename) >= prefixLen {
filename = filename[prefixLen:]
}
if suffixLen := len(client.Config.URLSuffix); len(filename) >= suffixLen {
filename = filename[:len(filename)-suffixLen]
}
filename = strings.Trim(filename, "/")
if strings.HasPrefix(filename, client.Config.Bucket) {
filename = strings.Trim(filename[len(client.Config.Bucket):], "/")
}
req, err := awss3.NewPresignClient(client.Client).PresignGetObject(ctx, &awss3.GetObjectInput{
Bucket: aws.String(client.Config.Bucket),
Key: aws.String(filename),
}, awss3.WithPresignExpires(LinkLifetime))
if err != nil {
return "", errors.Wrapf(err, "pre-sign link")
}
return req.URL, nil
}

@ -63,25 +63,32 @@ message WorkspaceCustomProfile {
} }
message WorkspaceStorageSetting { message WorkspaceStorageSetting {
// storage_type is the storage type.
StorageType storage_type = 1;
// The id of actived external storage.
optional int32 actived_external_storage_id = 2;
// The template of local storage path.
// e.g. assets/{timestamp}_{filename}
string local_storage_path_template = 3;
// The max upload size in megabytes.
int64 upload_size_limit_mb = 4;
enum StorageType { enum StorageType {
STORAGE_TYPE_UNSPECIFIED = 0; STORAGE_TYPE_UNSPECIFIED = 0;
// STORAGE_TYPE_DATABASE is the database storage type. // STORAGE_TYPE_DATABASE is the database storage type.
STORAGE_TYPE_DATABASE = 1; STORAGE_TYPE_DATABASE = 1;
// STORAGE_TYPE_LOCAL is the local storage type. // STORAGE_TYPE_LOCAL is the local storage type.
STORAGE_TYPE_LOCAL = 2; STORAGE_TYPE_LOCAL = 2;
// STORAGE_TYPE_EXTERNAL is the external storage type. // STORAGE_TYPE_S3 is the S3 storage type.
STORAGE_TYPE_EXTERNAL = 3; STORAGE_TYPE_S3 = 3;
}
// storage_type is the storage type.
StorageType storage_type = 1;
// The template of file path.
// e.g. assets/{timestamp}_{filename}
string filepath_template = 2;
// The max upload size in megabytes.
int64 upload_size_limit_mb = 3;
// Reference: https://developers.cloudflare.com/r2/examples/aws/aws-sdk-go/
message S3Config {
string access_key_id = 1;
string access_key_secret = 2;
string endpoint = 3;
string region = 4;
string bucket = 5;
} }
// The S3 config.
S3Config s3_config = 4;
} }
message WorkspaceMemoRelatedSetting { message WorkspaceMemoRelatedSetting {

@ -29,8 +29,8 @@ const (
WorkspaceStorageSetting_STORAGE_TYPE_DATABASE WorkspaceStorageSetting_StorageType = 1 WorkspaceStorageSetting_STORAGE_TYPE_DATABASE WorkspaceStorageSetting_StorageType = 1
// STORAGE_TYPE_LOCAL is the local storage type. // STORAGE_TYPE_LOCAL is the local storage type.
WorkspaceStorageSetting_STORAGE_TYPE_LOCAL WorkspaceStorageSetting_StorageType = 2 WorkspaceStorageSetting_STORAGE_TYPE_LOCAL WorkspaceStorageSetting_StorageType = 2
// STORAGE_TYPE_EXTERNAL is the external storage type. // STORAGE_TYPE_S3 is the S3 storage type.
WorkspaceStorageSetting_STORAGE_TYPE_EXTERNAL WorkspaceStorageSetting_StorageType = 3 WorkspaceStorageSetting_STORAGE_TYPE_S3 WorkspaceStorageSetting_StorageType = 3
) )
// Enum value maps for WorkspaceStorageSetting_StorageType. // Enum value maps for WorkspaceStorageSetting_StorageType.
@ -39,13 +39,13 @@ var (
0: "STORAGE_TYPE_UNSPECIFIED", 0: "STORAGE_TYPE_UNSPECIFIED",
1: "STORAGE_TYPE_DATABASE", 1: "STORAGE_TYPE_DATABASE",
2: "STORAGE_TYPE_LOCAL", 2: "STORAGE_TYPE_LOCAL",
3: "STORAGE_TYPE_EXTERNAL", 3: "STORAGE_TYPE_S3",
} }
WorkspaceStorageSetting_StorageType_value = map[string]int32{ WorkspaceStorageSetting_StorageType_value = map[string]int32{
"STORAGE_TYPE_UNSPECIFIED": 0, "STORAGE_TYPE_UNSPECIFIED": 0,
"STORAGE_TYPE_DATABASE": 1, "STORAGE_TYPE_DATABASE": 1,
"STORAGE_TYPE_LOCAL": 2, "STORAGE_TYPE_LOCAL": 2,
"STORAGE_TYPE_EXTERNAL": 3, "STORAGE_TYPE_S3": 3,
} }
) )
@ -360,13 +360,13 @@ type WorkspaceStorageSetting struct {
// storage_type is the storage type. // storage_type is the storage type.
StorageType WorkspaceStorageSetting_StorageType `protobuf:"varint,1,opt,name=storage_type,json=storageType,proto3,enum=memos.api.v1.WorkspaceStorageSetting_StorageType" json:"storage_type,omitempty"` StorageType WorkspaceStorageSetting_StorageType `protobuf:"varint,1,opt,name=storage_type,json=storageType,proto3,enum=memos.api.v1.WorkspaceStorageSetting_StorageType" json:"storage_type,omitempty"`
// The id of actived external storage. // The template of file path.
ActivedExternalStorageId *int32 `protobuf:"varint,2,opt,name=actived_external_storage_id,json=activedExternalStorageId,proto3,oneof" json:"actived_external_storage_id,omitempty"`
// The template of local storage path.
// e.g. assets/{timestamp}_{filename} // e.g. assets/{timestamp}_{filename}
LocalStoragePathTemplate string `protobuf:"bytes,3,opt,name=local_storage_path_template,json=localStoragePathTemplate,proto3" json:"local_storage_path_template,omitempty"` FilepathTemplate string `protobuf:"bytes,2,opt,name=filepath_template,json=filepathTemplate,proto3" json:"filepath_template,omitempty"`
// The max upload size in megabytes. // The max upload size in megabytes.
UploadSizeLimitMb int64 `protobuf:"varint,4,opt,name=upload_size_limit_mb,json=uploadSizeLimitMb,proto3" json:"upload_size_limit_mb,omitempty"` UploadSizeLimitMb int64 `protobuf:"varint,3,opt,name=upload_size_limit_mb,json=uploadSizeLimitMb,proto3" json:"upload_size_limit_mb,omitempty"`
// The S3 config.
S3Config *WorkspaceStorageSetting_S3Config `protobuf:"bytes,4,opt,name=s3_config,json=s3Config,proto3" json:"s3_config,omitempty"`
} }
func (x *WorkspaceStorageSetting) Reset() { func (x *WorkspaceStorageSetting) Reset() {
@ -408,16 +408,9 @@ func (x *WorkspaceStorageSetting) GetStorageType() WorkspaceStorageSetting_Stora
return WorkspaceStorageSetting_STORAGE_TYPE_UNSPECIFIED return WorkspaceStorageSetting_STORAGE_TYPE_UNSPECIFIED
} }
func (x *WorkspaceStorageSetting) GetActivedExternalStorageId() int32 { func (x *WorkspaceStorageSetting) GetFilepathTemplate() string {
if x != nil && x.ActivedExternalStorageId != nil {
return *x.ActivedExternalStorageId
}
return 0
}
func (x *WorkspaceStorageSetting) GetLocalStoragePathTemplate() string {
if x != nil { if x != nil {
return x.LocalStoragePathTemplate return x.FilepathTemplate
} }
return "" return ""
} }
@ -429,6 +422,13 @@ func (x *WorkspaceStorageSetting) GetUploadSizeLimitMb() int64 {
return 0 return 0
} }
func (x *WorkspaceStorageSetting) GetS3Config() *WorkspaceStorageSetting_S3Config {
if x != nil {
return x.S3Config
}
return nil
}
type WorkspaceMemoRelatedSetting struct { type WorkspaceMemoRelatedSetting struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -668,6 +668,86 @@ func (x *SetWorkspaceSettingRequest) GetSetting() *WorkspaceSetting {
return nil return nil
} }
// Reference: https://developers.cloudflare.com/r2/examples/aws/aws-sdk-go/
type WorkspaceStorageSetting_S3Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AccessKeyId string `protobuf:"bytes,1,opt,name=access_key_id,json=accessKeyId,proto3" json:"access_key_id,omitempty"`
AccessKeySecret string `protobuf:"bytes,2,opt,name=access_key_secret,json=accessKeySecret,proto3" json:"access_key_secret,omitempty"`
Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
Region string `protobuf:"bytes,4,opt,name=region,proto3" json:"region,omitempty"`
Bucket string `protobuf:"bytes,5,opt,name=bucket,proto3" json:"bucket,omitempty"`
}
func (x *WorkspaceStorageSetting_S3Config) Reset() {
*x = WorkspaceStorageSetting_S3Config{}
if protoimpl.UnsafeEnabled {
mi := &file_api_v1_workspace_setting_service_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *WorkspaceStorageSetting_S3Config) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WorkspaceStorageSetting_S3Config) ProtoMessage() {}
func (x *WorkspaceStorageSetting_S3Config) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_workspace_setting_service_proto_msgTypes[9]
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 WorkspaceStorageSetting_S3Config.ProtoReflect.Descriptor instead.
func (*WorkspaceStorageSetting_S3Config) Descriptor() ([]byte, []int) {
return file_api_v1_workspace_setting_service_proto_rawDescGZIP(), []int{3, 0}
}
func (x *WorkspaceStorageSetting_S3Config) GetAccessKeyId() string {
if x != nil {
return x.AccessKeyId
}
return ""
}
func (x *WorkspaceStorageSetting_S3Config) GetAccessKeySecret() string {
if x != nil {
return x.AccessKeySecret
}
return ""
}
func (x *WorkspaceStorageSetting_S3Config) GetEndpoint() string {
if x != nil {
return x.Endpoint
}
return ""
}
func (x *WorkspaceStorageSetting_S3Config) GetRegion() string {
if x != nil {
return x.Region
}
return ""
}
func (x *WorkspaceStorageSetting_S3Config) GetBucket() string {
if x != nil {
return x.Bucket
}
return ""
}
var File_api_v1_workspace_setting_service_proto protoreflect.FileDescriptor var File_api_v1_workspace_setting_service_proto protoreflect.FileDescriptor
var file_api_v1_workspace_setting_service_proto_rawDesc = []byte{ var file_api_v1_workspace_setting_service_proto_rawDesc = []byte{
@ -729,104 +809,112 @@ var file_api_v1_workspace_setting_service_proto_rawDesc = []byte{
0x6f, 0x67, 0x6f, 0x55, 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x6f, 0x67, 0x6f, 0x55, 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65,
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x1e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x1e,
0x0a, 0x0a, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x0a, 0x0a, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x22, 0xbe, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x22, 0xb8,
0x03, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x04, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72,
0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x54, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x54, 0x0a, 0x0c, 0x73, 0x74,
0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
0x32, 0x31, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x32, 0x31, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e,
0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54,
0x79, 0x70, 0x65, 0x52, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65,
0x12, 0x42, 0x0a, 0x1b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x65, 0x6d,
0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x66, 0x69, 0x6c,
0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x18, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, 0x65, 0x70, 0x61, 0x74, 0x68, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a,
0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x14, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d,
0x64, 0x88, 0x01, 0x01, 0x12, 0x3d, 0x0a, 0x1b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x69, 0x74, 0x5f, 0x6d, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x75, 0x70, 0x6c,
0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x4d, 0x62, 0x12, 0x4b,
0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x0a, 0x09, 0x73, 0x33, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28,
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x50, 0x61, 0x74, 0x68, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x0b, 0x32, 0x2e, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31,
0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x14, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x69, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x33, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x03, 0x52, 0x11, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6d, 0x67, 0x52, 0x08, 0x73, 0x33, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0xa6, 0x01, 0x0a, 0x08,
0x69, 0x74, 0x4d, 0x62, 0x22, 0x79, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x53, 0x33, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x22, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x65,
0x73, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11,
0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65,
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b,
0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70,
0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70,
0x6f, 0x69, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x04,
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06,
0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75,
0x63, 0x6b, 0x65, 0x74, 0x22, 0x73, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54,
0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x18, 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, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
0x00, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50,
0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12,
0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x4f, 0x43, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x4f, 0x43,
0x41, 0x4c, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f,
0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x10, 0x03, 0x42, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x33, 0x10, 0x03, 0x22, 0x8e, 0x01, 0x0a, 0x1b, 0x57, 0x6f,
0x1e, 0x0a, 0x1c, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x6c, 0x61, 0x74,
0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x22, 0x65, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x36, 0x0a, 0x17, 0x64, 0x69, 0x73,
0x8e, 0x01, 0x0a, 0x1b, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x6d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x76, 0x69, 0x73,
0x6f, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x69, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x64, 0x69, 0x73, 0x61,
0x36, 0x0a, 0x17, 0x64, 0x69, 0x73, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x56, 0x69, 0x73, 0x69, 0x62, 0x6c,
0x69, 0x63, 0x5f, 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x65, 0x12, 0x37, 0x0a, 0x18, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x77, 0x69, 0x74,
0x52, 0x15, 0x64, 0x69, 0x73, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x68, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20,
0x56, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x12, 0x37, 0x0a, 0x18, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x01, 0x28, 0x08, 0x52, 0x15, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x57, 0x69, 0x74, 0x68,
0x61, 0x79, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x1e, 0x0a, 0x1c, 0x4c, 0x69,
0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69,
0x61, 0x79, 0x57, 0x69, 0x74, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5b, 0x0a, 0x1d, 0x4c, 0x69,
0x22, 0x1e, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x22, 0x5b, 0x0a, 0x1d, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x3a, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74,
0x69, 0x6e, 0x67, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x35, 0x0a,
0x1a, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74,
0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x22, 0x56, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73,
0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x38, 0x0a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74,
0x69, 0x6e, 0x67, 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x32, 0xf0, 0x03, 0x0a,
0x17, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x94, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73,
0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x67, 0x73, 0x12, 0x2a, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76,
0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53,
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b,
0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69,
0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69,
0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x73,
0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e,
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72,
0x93, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x73,
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x35, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x57, 0x6f,
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x56,
0x0a, 0x1a, 0x53, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x07,
0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e,
0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72,
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x73,
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x32, 0xf0, 0x03, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x73,
0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x12, 0x94, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73,
0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2a, 0x2e, 0x6d,
0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74,
0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b,
0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f,
0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
0x2f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x93, 0x01, 0x0a, 0x13, 0x47, 0x65,
0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x67, 0x12, 0x28, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31,
0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74,
0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65,
0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73,
0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x32, 0xda, 0x41, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x61, 0x70, 0x69,
0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e,
0x61, 0x6d, 0x65, 0x3d, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2f, 0x2a, 0x7d, 0x12,
0xa7, 0x01, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x28, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x28, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70,
0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31,
0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x67, 0x22, 0x32, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x67, 0x22, 0x46, 0xda, 0x41, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x82, 0xd3, 0xe4,
0x12, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x93, 0x02, 0x36, 0x3a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x32, 0x2b, 0x2f, 0x61,
0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f,
0x67, 0x73, 0x2f, 0x2a, 0x7d, 0x12, 0xa7, 0x01, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x7b, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x73, 0x65,
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x28, 0x2e, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2f, 0x2a, 0x7d, 0x42, 0xb4, 0x01, 0x0a, 0x10, 0x63, 0x6f,
0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x1c,
0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30,
0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x73, 0x65, 0x6d, 0x65,
0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x46, 0xda, 0x41, 0x07, 0x73, 0x65, 0x74, 0x74, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
0x69, 0x6e, 0x67, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x36, 0x3a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31,
0x6e, 0x67, 0x32, 0x2b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0xa2, 0x02, 0x03, 0x4d, 0x41, 0x58, 0xaa, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x41,
0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x6e, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70,
0x61, 0x6d, 0x65, 0x3d, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2f, 0x2a, 0x7d, 0x42, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, 0x69,
0xb4, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea,
0x69, 0x2e, 0x76, 0x31, 0x42, 0x1c, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x02, 0x0e, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31,
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
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 (
@ -842,7 +930,7 @@ func file_api_v1_workspace_setting_service_proto_rawDescGZIP() []byte {
} }
var file_api_v1_workspace_setting_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_api_v1_workspace_setting_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_api_v1_workspace_setting_service_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_api_v1_workspace_setting_service_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
var file_api_v1_workspace_setting_service_proto_goTypes = []interface{}{ var file_api_v1_workspace_setting_service_proto_goTypes = []interface{}{
(WorkspaceStorageSetting_StorageType)(0), // 0: memos.api.v1.WorkspaceStorageSetting.StorageType (WorkspaceStorageSetting_StorageType)(0), // 0: memos.api.v1.WorkspaceStorageSetting.StorageType
(*WorkspaceSetting)(nil), // 1: memos.api.v1.WorkspaceSetting (*WorkspaceSetting)(nil), // 1: memos.api.v1.WorkspaceSetting
@ -854,6 +942,7 @@ var file_api_v1_workspace_setting_service_proto_goTypes = []interface{}{
(*ListWorkspaceSettingsResponse)(nil), // 7: memos.api.v1.ListWorkspaceSettingsResponse (*ListWorkspaceSettingsResponse)(nil), // 7: memos.api.v1.ListWorkspaceSettingsResponse
(*GetWorkspaceSettingRequest)(nil), // 8: memos.api.v1.GetWorkspaceSettingRequest (*GetWorkspaceSettingRequest)(nil), // 8: memos.api.v1.GetWorkspaceSettingRequest
(*SetWorkspaceSettingRequest)(nil), // 9: memos.api.v1.SetWorkspaceSettingRequest (*SetWorkspaceSettingRequest)(nil), // 9: memos.api.v1.SetWorkspaceSettingRequest
(*WorkspaceStorageSetting_S3Config)(nil), // 10: memos.api.v1.WorkspaceStorageSetting.S3Config
} }
var file_api_v1_workspace_setting_service_proto_depIdxs = []int32{ var file_api_v1_workspace_setting_service_proto_depIdxs = []int32{
2, // 0: memos.api.v1.WorkspaceSetting.general_setting:type_name -> memos.api.v1.WorkspaceGeneralSetting 2, // 0: memos.api.v1.WorkspaceSetting.general_setting:type_name -> memos.api.v1.WorkspaceGeneralSetting
@ -861,19 +950,20 @@ var file_api_v1_workspace_setting_service_proto_depIdxs = []int32{
5, // 2: memos.api.v1.WorkspaceSetting.memo_related_setting:type_name -> memos.api.v1.WorkspaceMemoRelatedSetting 5, // 2: memos.api.v1.WorkspaceSetting.memo_related_setting:type_name -> memos.api.v1.WorkspaceMemoRelatedSetting
3, // 3: memos.api.v1.WorkspaceGeneralSetting.custom_profile:type_name -> memos.api.v1.WorkspaceCustomProfile 3, // 3: memos.api.v1.WorkspaceGeneralSetting.custom_profile:type_name -> memos.api.v1.WorkspaceCustomProfile
0, // 4: memos.api.v1.WorkspaceStorageSetting.storage_type:type_name -> memos.api.v1.WorkspaceStorageSetting.StorageType 0, // 4: memos.api.v1.WorkspaceStorageSetting.storage_type:type_name -> memos.api.v1.WorkspaceStorageSetting.StorageType
1, // 5: memos.api.v1.ListWorkspaceSettingsResponse.settings:type_name -> memos.api.v1.WorkspaceSetting 10, // 5: memos.api.v1.WorkspaceStorageSetting.s3_config:type_name -> memos.api.v1.WorkspaceStorageSetting.S3Config
1, // 6: memos.api.v1.SetWorkspaceSettingRequest.setting:type_name -> memos.api.v1.WorkspaceSetting 1, // 6: memos.api.v1.ListWorkspaceSettingsResponse.settings:type_name -> memos.api.v1.WorkspaceSetting
6, // 7: memos.api.v1.WorkspaceSettingService.ListWorkspaceSettings:input_type -> memos.api.v1.ListWorkspaceSettingsRequest 1, // 7: memos.api.v1.SetWorkspaceSettingRequest.setting:type_name -> memos.api.v1.WorkspaceSetting
8, // 8: memos.api.v1.WorkspaceSettingService.GetWorkspaceSetting:input_type -> memos.api.v1.GetWorkspaceSettingRequest 6, // 8: memos.api.v1.WorkspaceSettingService.ListWorkspaceSettings:input_type -> memos.api.v1.ListWorkspaceSettingsRequest
9, // 9: memos.api.v1.WorkspaceSettingService.SetWorkspaceSetting:input_type -> memos.api.v1.SetWorkspaceSettingRequest 8, // 9: memos.api.v1.WorkspaceSettingService.GetWorkspaceSetting:input_type -> memos.api.v1.GetWorkspaceSettingRequest
7, // 10: memos.api.v1.WorkspaceSettingService.ListWorkspaceSettings:output_type -> memos.api.v1.ListWorkspaceSettingsResponse 9, // 10: memos.api.v1.WorkspaceSettingService.SetWorkspaceSetting:input_type -> memos.api.v1.SetWorkspaceSettingRequest
1, // 11: memos.api.v1.WorkspaceSettingService.GetWorkspaceSetting:output_type -> memos.api.v1.WorkspaceSetting 7, // 11: memos.api.v1.WorkspaceSettingService.ListWorkspaceSettings:output_type -> memos.api.v1.ListWorkspaceSettingsResponse
1, // 12: memos.api.v1.WorkspaceSettingService.SetWorkspaceSetting:output_type -> memos.api.v1.WorkspaceSetting 1, // 12: memos.api.v1.WorkspaceSettingService.GetWorkspaceSetting:output_type -> memos.api.v1.WorkspaceSetting
10, // [10:13] is the sub-list for method output_type 1, // 13: memos.api.v1.WorkspaceSettingService.SetWorkspaceSetting:output_type -> memos.api.v1.WorkspaceSetting
7, // [7:10] is the sub-list for method input_type 11, // [11:14] is the sub-list for method output_type
7, // [7:7] is the sub-list for extension type_name 8, // [8:11] is the sub-list for method input_type
7, // [7:7] is the sub-list for extension extendee 8, // [8:8] is the sub-list for extension type_name
0, // [0:7] is the sub-list for field type_name 8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
} }
func init() { file_api_v1_workspace_setting_service_proto_init() } func init() { file_api_v1_workspace_setting_service_proto_init() }
@ -990,20 +1080,31 @@ func file_api_v1_workspace_setting_service_proto_init() {
return nil return nil
} }
} }
file_api_v1_workspace_setting_service_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*WorkspaceStorageSetting_S3Config); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
} }
file_api_v1_workspace_setting_service_proto_msgTypes[0].OneofWrappers = []interface{}{ file_api_v1_workspace_setting_service_proto_msgTypes[0].OneofWrappers = []interface{}{
(*WorkspaceSetting_GeneralSetting)(nil), (*WorkspaceSetting_GeneralSetting)(nil),
(*WorkspaceSetting_StorageSetting)(nil), (*WorkspaceSetting_StorageSetting)(nil),
(*WorkspaceSetting_MemoRelatedSetting)(nil), (*WorkspaceSetting_MemoRelatedSetting)(nil),
} }
file_api_v1_workspace_setting_service_proto_msgTypes[3].OneofWrappers = []interface{}{}
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_api_v1_workspace_setting_service_proto_rawDesc, RawDescriptor: file_api_v1_workspace_setting_service_proto_rawDesc,
NumEnums: 1, NumEnums: 1,
NumMessages: 9, NumMessages: 10,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

@ -87,8 +87,8 @@ const (
WorkspaceStorageSetting_STORAGE_TYPE_DATABASE WorkspaceStorageSetting_StorageType = 1 WorkspaceStorageSetting_STORAGE_TYPE_DATABASE WorkspaceStorageSetting_StorageType = 1
// STORAGE_TYPE_LOCAL is the local storage type. // STORAGE_TYPE_LOCAL is the local storage type.
WorkspaceStorageSetting_STORAGE_TYPE_LOCAL WorkspaceStorageSetting_StorageType = 2 WorkspaceStorageSetting_STORAGE_TYPE_LOCAL WorkspaceStorageSetting_StorageType = 2
// STORAGE_TYPE_EXTERNAL is the external storage type. // STORAGE_TYPE_S3 is the S3 storage type.
WorkspaceStorageSetting_STORAGE_TYPE_EXTERNAL WorkspaceStorageSetting_StorageType = 3 WorkspaceStorageSetting_STORAGE_TYPE_S3 WorkspaceStorageSetting_StorageType = 3
) )
// Enum value maps for WorkspaceStorageSetting_StorageType. // Enum value maps for WorkspaceStorageSetting_StorageType.
@ -97,13 +97,13 @@ var (
0: "STORAGE_TYPE_UNSPECIFIED", 0: "STORAGE_TYPE_UNSPECIFIED",
1: "STORAGE_TYPE_DATABASE", 1: "STORAGE_TYPE_DATABASE",
2: "STORAGE_TYPE_LOCAL", 2: "STORAGE_TYPE_LOCAL",
3: "STORAGE_TYPE_EXTERNAL", 3: "STORAGE_TYPE_S3",
} }
WorkspaceStorageSetting_StorageType_value = map[string]int32{ WorkspaceStorageSetting_StorageType_value = map[string]int32{
"STORAGE_TYPE_UNSPECIFIED": 0, "STORAGE_TYPE_UNSPECIFIED": 0,
"STORAGE_TYPE_DATABASE": 1, "STORAGE_TYPE_DATABASE": 1,
"STORAGE_TYPE_LOCAL": 2, "STORAGE_TYPE_LOCAL": 2,
"STORAGE_TYPE_EXTERNAL": 3, "STORAGE_TYPE_S3": 3,
} }
) )
@ -485,13 +485,13 @@ type WorkspaceStorageSetting struct {
// storage_type is the storage type. // storage_type is the storage type.
StorageType WorkspaceStorageSetting_StorageType `protobuf:"varint,1,opt,name=storage_type,json=storageType,proto3,enum=memos.store.WorkspaceStorageSetting_StorageType" json:"storage_type,omitempty"` StorageType WorkspaceStorageSetting_StorageType `protobuf:"varint,1,opt,name=storage_type,json=storageType,proto3,enum=memos.store.WorkspaceStorageSetting_StorageType" json:"storage_type,omitempty"`
// The id of actived external storage. // The template of file path.
ActivedExternalStorageId *int32 `protobuf:"varint,2,opt,name=actived_external_storage_id,json=activedExternalStorageId,proto3,oneof" json:"actived_external_storage_id,omitempty"`
// The template of local storage path.
// e.g. assets/{timestamp}_{filename} // e.g. assets/{timestamp}_{filename}
LocalStoragePathTemplate string `protobuf:"bytes,3,opt,name=local_storage_path_template,json=localStoragePathTemplate,proto3" json:"local_storage_path_template,omitempty"` FilepathTemplate string `protobuf:"bytes,2,opt,name=filepath_template,json=filepathTemplate,proto3" json:"filepath_template,omitempty"`
// The max upload size in megabytes. // The max upload size in megabytes.
UploadSizeLimitMb int64 `protobuf:"varint,4,opt,name=upload_size_limit_mb,json=uploadSizeLimitMb,proto3" json:"upload_size_limit_mb,omitempty"` UploadSizeLimitMb int64 `protobuf:"varint,3,opt,name=upload_size_limit_mb,json=uploadSizeLimitMb,proto3" json:"upload_size_limit_mb,omitempty"`
// The S3 config.
S3Config *WorkspaceStorageSetting_S3Config `protobuf:"bytes,4,opt,name=s3_config,json=s3Config,proto3" json:"s3_config,omitempty"`
} }
func (x *WorkspaceStorageSetting) Reset() { func (x *WorkspaceStorageSetting) Reset() {
@ -533,16 +533,9 @@ func (x *WorkspaceStorageSetting) GetStorageType() WorkspaceStorageSetting_Stora
return WorkspaceStorageSetting_STORAGE_TYPE_UNSPECIFIED return WorkspaceStorageSetting_STORAGE_TYPE_UNSPECIFIED
} }
func (x *WorkspaceStorageSetting) GetActivedExternalStorageId() int32 { func (x *WorkspaceStorageSetting) GetFilepathTemplate() string {
if x != nil && x.ActivedExternalStorageId != nil {
return *x.ActivedExternalStorageId
}
return 0
}
func (x *WorkspaceStorageSetting) GetLocalStoragePathTemplate() string {
if x != nil { if x != nil {
return x.LocalStoragePathTemplate return x.FilepathTemplate
} }
return "" return ""
} }
@ -554,6 +547,13 @@ func (x *WorkspaceStorageSetting) GetUploadSizeLimitMb() int64 {
return 0 return 0
} }
func (x *WorkspaceStorageSetting) GetS3Config() *WorkspaceStorageSetting_S3Config {
if x != nil {
return x.S3Config
}
return nil
}
type WorkspaceMemoRelatedSetting struct { type WorkspaceMemoRelatedSetting struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -611,6 +611,86 @@ func (x *WorkspaceMemoRelatedSetting) GetDisplayWithUpdateTime() bool {
return false return false
} }
// Reference: https://developers.cloudflare.com/r2/examples/aws/aws-sdk-go/
type WorkspaceStorageSetting_S3Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AccessKeyId string `protobuf:"bytes,1,opt,name=access_key_id,json=accessKeyId,proto3" json:"access_key_id,omitempty"`
AccessKeySecret string `protobuf:"bytes,2,opt,name=access_key_secret,json=accessKeySecret,proto3" json:"access_key_secret,omitempty"`
Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
Region string `protobuf:"bytes,4,opt,name=region,proto3" json:"region,omitempty"`
Bucket string `protobuf:"bytes,5,opt,name=bucket,proto3" json:"bucket,omitempty"`
}
func (x *WorkspaceStorageSetting_S3Config) Reset() {
*x = WorkspaceStorageSetting_S3Config{}
if protoimpl.UnsafeEnabled {
mi := &file_store_workspace_setting_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *WorkspaceStorageSetting_S3Config) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WorkspaceStorageSetting_S3Config) ProtoMessage() {}
func (x *WorkspaceStorageSetting_S3Config) ProtoReflect() protoreflect.Message {
mi := &file_store_workspace_setting_proto_msgTypes[6]
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 WorkspaceStorageSetting_S3Config.ProtoReflect.Descriptor instead.
func (*WorkspaceStorageSetting_S3Config) Descriptor() ([]byte, []int) {
return file_store_workspace_setting_proto_rawDescGZIP(), []int{4, 0}
}
func (x *WorkspaceStorageSetting_S3Config) GetAccessKeyId() string {
if x != nil {
return x.AccessKeyId
}
return ""
}
func (x *WorkspaceStorageSetting_S3Config) GetAccessKeySecret() string {
if x != nil {
return x.AccessKeySecret
}
return ""
}
func (x *WorkspaceStorageSetting_S3Config) GetEndpoint() string {
if x != nil {
return x.Endpoint
}
return ""
}
func (x *WorkspaceStorageSetting_S3Config) GetRegion() string {
if x != nil {
return x.Region
}
return ""
}
func (x *WorkspaceStorageSetting_S3Config) GetBucket() string {
if x != nil {
return x.Bucket
}
return ""
}
var File_store_workspace_setting_proto protoreflect.FileDescriptor var File_store_workspace_setting_proto protoreflect.FileDescriptor
var file_store_workspace_setting_proto_rawDesc = []byte{ var file_store_workspace_setting_proto_rawDesc = []byte{
@ -678,67 +758,74 @@ var file_store_workspace_setting_proto_rawDesc = []byte{
0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x70, 0x70, 0x65, 0x52, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x70, 0x70, 0x65,
0x61, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x61, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70,
0x70, 0x65, 0x61, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x22, 0xbd, 0x03, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x70, 0x65, 0x61, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x22, 0xb6, 0x04, 0x0a, 0x17, 0x57, 0x6f, 0x72,
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74,
0x74, 0x69, 0x6e, 0x67, 0x12, 0x53, 0x0a, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x53, 0x0a, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f,
0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x6d, 0x65, 0x6d, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x6d, 0x65, 0x6d,
0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x73, 0x74, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x73, 0x74,
0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x42, 0x0a, 0x1b, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c,
0x69, 0x76, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x02,
0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x54, 0x65,
0x52, 0x18, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x14, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64,
0x6c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x3d, 0x0a, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x62, 0x18, 0x03,
0x1b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x70, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x7a, 0x65,
0x61, 0x74, 0x68, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x4d, 0x62, 0x12, 0x4a, 0x0a, 0x09, 0x73, 0x33, 0x5f, 0x63, 0x6f,
0x28, 0x09, 0x52, 0x18, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6d, 0x65, 0x6d,
0x50, 0x61, 0x74, 0x68, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x14, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
0x74, 0x5f, 0x6d, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x75, 0x70, 0x6c, 0x6f, 0x2e, 0x53, 0x33, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x73, 0x33, 0x43, 0x6f, 0x6e,
0x61, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x4d, 0x62, 0x22, 0x79, 0x0a, 0x66, 0x69, 0x67, 0x1a, 0xa6, 0x01, 0x0a, 0x08, 0x53, 0x33, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x0b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x12, 0x22, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69,
0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b,
0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x65, 0x79, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6b,
0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x65, 0x79, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x41, 0x53, 0x45, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74,
0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01,
0x15, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06,
0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x10, 0x03, 0x42, 0x1e, 0x0a, 0x1c, 0x5f, 0x61, 0x63, 0x74, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65,
0x69, 0x76, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x05,
0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x22, 0x8e, 0x01, 0x0a, 0x1b, 0x57, 0x6f, 0x72, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x73, 0x0a, 0x0b,
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x53,
0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x36, 0x0a, 0x17, 0x64, 0x69, 0x73, 0x61, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50,
0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x76, 0x69, 0x73, 0x69, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x4f,
0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x64, 0x69, 0x73, 0x61, 0x6c, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41,
0x6c, 0x6f, 0x77, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x56, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x53, 0x45, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f,
0x12, 0x37, 0x0a, 0x18, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f,
0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x33, 0x10,
0x28, 0x08, 0x52, 0x15, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x57, 0x69, 0x74, 0x68, 0x55, 0x03, 0x22, 0x8e, 0x01, 0x0a, 0x1b, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d,
0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x2a, 0xbb, 0x01, 0x0a, 0x13, 0x57, 0x6f, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x4b, 0x65, 0x67, 0x12, 0x36, 0x0a, 0x17, 0x64, 0x69, 0x73, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x75,
0x79, 0x12, 0x25, 0x0a, 0x21, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01,
0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x28, 0x08, 0x52, 0x15, 0x64, 0x69, 0x73, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x75, 0x62, 0x6c,
0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x57, 0x4f, 0x52, 0x4b, 0x69, 0x63, 0x56, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x12, 0x37, 0x0a, 0x18, 0x64, 0x69, 0x73,
0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x42, 0x41, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
0x53, 0x49, 0x43, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x64, 0x69, 0x73,
0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x70, 0x6c, 0x61, 0x79, 0x57, 0x69, 0x74, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69,
0x41, 0x4c, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x6d, 0x65, 0x2a, 0xbb, 0x01, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x21, 0x57, 0x4f,
0x45, 0x10, 0x03, 0x12, 0x22, 0x0a, 0x1e, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f,
0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x4d, 0x45, 0x4d, 0x4f, 0x5f, 0x52, 0x45, 0x4b, 0x45, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
0x4c, 0x41, 0x54, 0x45, 0x44, 0x10, 0x04, 0x42, 0xa0, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53,
0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x15, 0x57, 0x6f, 0x72, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x42, 0x41, 0x53, 0x49, 0x43, 0x10, 0x01, 0x12, 0x1d,
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x0a, 0x19, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54,
0x74, 0x6f, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x49, 0x4e, 0x47, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x1d, 0x0a,
0x2f, 0x75, 0x73, 0x65, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x19, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0xa2, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x10, 0x03, 0x12, 0x22, 0x0a, 0x1e,
0x02, 0x03, 0x4d, 0x53, 0x58, 0xaa, 0x02, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x53, 0x74, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e,
0x6f, 0x72, 0x65, 0xca, 0x02, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x47, 0x5f, 0x4d, 0x45, 0x4d, 0x4f, 0x5f, 0x52, 0x45, 0x4c, 0x41, 0x54, 0x45, 0x44, 0x10, 0x04,
0x65, 0xe2, 0x02, 0x17, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x5c, 0x42, 0xa0, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x73,
0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x4d, 0x65, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x15, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53,
0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x29, 0x67,
0x6f, 0x33, 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 ( var (
@ -754,7 +841,7 @@ func file_store_workspace_setting_proto_rawDescGZIP() []byte {
} }
var file_store_workspace_setting_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_store_workspace_setting_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_store_workspace_setting_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_store_workspace_setting_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_store_workspace_setting_proto_goTypes = []interface{}{ var file_store_workspace_setting_proto_goTypes = []interface{}{
(WorkspaceSettingKey)(0), // 0: memos.store.WorkspaceSettingKey (WorkspaceSettingKey)(0), // 0: memos.store.WorkspaceSettingKey
(WorkspaceStorageSetting_StorageType)(0), // 1: memos.store.WorkspaceStorageSetting.StorageType (WorkspaceStorageSetting_StorageType)(0), // 1: memos.store.WorkspaceStorageSetting.StorageType
@ -764,6 +851,7 @@ var file_store_workspace_setting_proto_goTypes = []interface{}{
(*WorkspaceCustomProfile)(nil), // 5: memos.store.WorkspaceCustomProfile (*WorkspaceCustomProfile)(nil), // 5: memos.store.WorkspaceCustomProfile
(*WorkspaceStorageSetting)(nil), // 6: memos.store.WorkspaceStorageSetting (*WorkspaceStorageSetting)(nil), // 6: memos.store.WorkspaceStorageSetting
(*WorkspaceMemoRelatedSetting)(nil), // 7: memos.store.WorkspaceMemoRelatedSetting (*WorkspaceMemoRelatedSetting)(nil), // 7: memos.store.WorkspaceMemoRelatedSetting
(*WorkspaceStorageSetting_S3Config)(nil), // 8: memos.store.WorkspaceStorageSetting.S3Config
} }
var file_store_workspace_setting_proto_depIdxs = []int32{ var file_store_workspace_setting_proto_depIdxs = []int32{
0, // 0: memos.store.WorkspaceSetting.key:type_name -> memos.store.WorkspaceSettingKey 0, // 0: memos.store.WorkspaceSetting.key:type_name -> memos.store.WorkspaceSettingKey
@ -773,11 +861,12 @@ var file_store_workspace_setting_proto_depIdxs = []int32{
7, // 4: memos.store.WorkspaceSetting.memo_related_setting:type_name -> memos.store.WorkspaceMemoRelatedSetting 7, // 4: memos.store.WorkspaceSetting.memo_related_setting:type_name -> memos.store.WorkspaceMemoRelatedSetting
5, // 5: memos.store.WorkspaceGeneralSetting.custom_profile:type_name -> memos.store.WorkspaceCustomProfile 5, // 5: memos.store.WorkspaceGeneralSetting.custom_profile:type_name -> memos.store.WorkspaceCustomProfile
1, // 6: memos.store.WorkspaceStorageSetting.storage_type:type_name -> memos.store.WorkspaceStorageSetting.StorageType 1, // 6: memos.store.WorkspaceStorageSetting.storage_type:type_name -> memos.store.WorkspaceStorageSetting.StorageType
7, // [7:7] is the sub-list for method output_type 8, // 7: memos.store.WorkspaceStorageSetting.s3_config:type_name -> memos.store.WorkspaceStorageSetting.S3Config
7, // [7:7] is the sub-list for method input_type 8, // [8:8] is the sub-list for method output_type
7, // [7:7] is the sub-list for extension type_name 8, // [8:8] is the sub-list for method input_type
7, // [7:7] is the sub-list for extension extendee 8, // [8:8] is the sub-list for extension type_name
0, // [0:7] is the sub-list for field type_name 8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
} }
func init() { file_store_workspace_setting_proto_init() } func init() { file_store_workspace_setting_proto_init() }
@ -858,6 +947,18 @@ func file_store_workspace_setting_proto_init() {
return nil return nil
} }
} }
file_store_workspace_setting_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*WorkspaceStorageSetting_S3Config); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
} }
file_store_workspace_setting_proto_msgTypes[0].OneofWrappers = []interface{}{ file_store_workspace_setting_proto_msgTypes[0].OneofWrappers = []interface{}{
(*WorkspaceSetting_BasicSetting)(nil), (*WorkspaceSetting_BasicSetting)(nil),
@ -865,14 +966,13 @@ func file_store_workspace_setting_proto_init() {
(*WorkspaceSetting_StorageSetting)(nil), (*WorkspaceSetting_StorageSetting)(nil),
(*WorkspaceSetting_MemoRelatedSetting)(nil), (*WorkspaceSetting_MemoRelatedSetting)(nil),
} }
file_store_workspace_setting_proto_msgTypes[4].OneofWrappers = []interface{}{}
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_store_workspace_setting_proto_rawDesc, RawDescriptor: file_store_workspace_setting_proto_rawDesc,
NumEnums: 2, NumEnums: 2,
NumMessages: 6, NumMessages: 7,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },

@ -55,25 +55,32 @@ message WorkspaceCustomProfile {
} }
message WorkspaceStorageSetting { message WorkspaceStorageSetting {
// storage_type is the storage type.
StorageType storage_type = 1;
// The id of actived external storage.
optional int32 actived_external_storage_id = 2;
// The template of local storage path.
// e.g. assets/{timestamp}_{filename}
string local_storage_path_template = 3;
// The max upload size in megabytes.
int64 upload_size_limit_mb = 4;
enum StorageType { enum StorageType {
STORAGE_TYPE_UNSPECIFIED = 0; STORAGE_TYPE_UNSPECIFIED = 0;
// STORAGE_TYPE_DATABASE is the database storage type. // STORAGE_TYPE_DATABASE is the database storage type.
STORAGE_TYPE_DATABASE = 1; STORAGE_TYPE_DATABASE = 1;
// STORAGE_TYPE_LOCAL is the local storage type. // STORAGE_TYPE_LOCAL is the local storage type.
STORAGE_TYPE_LOCAL = 2; STORAGE_TYPE_LOCAL = 2;
// STORAGE_TYPE_EXTERNAL is the external storage type. // STORAGE_TYPE_S3 is the S3 storage type.
STORAGE_TYPE_EXTERNAL = 3; STORAGE_TYPE_S3 = 3;
}
// storage_type is the storage type.
StorageType storage_type = 1;
// The template of file path.
// e.g. assets/{timestamp}_{filename}
string filepath_template = 2;
// The max upload size in megabytes.
int64 upload_size_limit_mb = 3;
// Reference: https://developers.cloudflare.com/r2/examples/aws/aws-sdk-go/
message S3Config {
string access_key_id = 1;
string access_key_secret = 2;
string endpoint = 3;
string region = 4;
string bucket = 5;
} }
// The S3 config.
S3Config s3_config = 4;
} }
message WorkspaceMemoRelatedSetting { message WorkspaceMemoRelatedSetting {

@ -254,12 +254,12 @@ func SaveResourceBlob(ctx context.Context, s *store.Store, create *store.Resourc
} }
if workspaceStorageSetting.StorageType == storepb.WorkspaceStorageSetting_STORAGE_TYPE_LOCAL { if workspaceStorageSetting.StorageType == storepb.WorkspaceStorageSetting_STORAGE_TYPE_LOCAL {
localStoragePath := "assets/{timestamp}_{filename}" filepathTemplate := "assets/{timestamp}_{filename}"
if workspaceStorageSetting.LocalStoragePathTemplate != "" { if workspaceStorageSetting.FilepathTemplate != "" {
localStoragePath = workspaceStorageSetting.LocalStoragePathTemplate filepathTemplate = workspaceStorageSetting.FilepathTemplate
} }
internalPath := localStoragePath internalPath := filepathTemplate
if !strings.Contains(internalPath, "{filename}") { if !strings.Contains(internalPath, "{filename}") {
internalPath = filepath.Join(internalPath, "{filename}") internalPath = filepath.Join(internalPath, "{filename}")
} }
@ -287,46 +287,29 @@ func SaveResourceBlob(ctx context.Context, s *store.Store, create *store.Resourc
} }
create.InternalPath = internalPath create.InternalPath = internalPath
create.Blob = nil create.Blob = nil
} else if workspaceStorageSetting.StorageType == storepb.WorkspaceStorageSetting_STORAGE_TYPE_EXTERNAL { } else if workspaceStorageSetting.StorageType == storepb.WorkspaceStorageSetting_STORAGE_TYPE_S3 {
if workspaceStorageSetting.ActivedExternalStorageId == nil { s3Config := workspaceStorageSetting.S3Config
return errors.Errorf("No actived external storage found")
}
storage, err := s.GetStorage(ctx, &store.FindStorage{ID: workspaceStorageSetting.ActivedExternalStorageId})
if err != nil {
return errors.Wrap(err, "Failed to find actived external storage")
}
if storage == nil {
return errors.Errorf("Storage %d not found", *workspaceStorageSetting.ActivedExternalStorageId)
}
if storage.Type != storepb.Storage_S3 {
return errors.Errorf("Unsupported storage type: %s", storage.Type.String())
}
s3Config := storage.Config.GetS3Config()
if s3Config == nil { if s3Config == nil {
return errors.Errorf("S3 config not found") return errors.Errorf("No actived external storage found")
} }
s3Client, err := s3.NewClient(ctx, &s3.Config{ s3Client, err := s3.NewClient(ctx, &s3.Config{
AccessKey: s3Config.AccessKey, AccessKeyID: s3Config.AccessKeyId,
SecretKey: s3Config.SecretKey, AcesssKeySecret: s3Config.AccessKeySecret,
EndPoint: s3Config.EndPoint, Endpoint: s3Config.Endpoint,
Region: s3Config.Region, Region: s3Config.Region,
Bucket: s3Config.Bucket, Bucket: s3Config.Bucket,
URLPrefix: s3Config.UrlPrefix,
URLSuffix: s3Config.UrlSuffix,
PreSign: s3Config.PreSign,
}) })
if err != nil { if err != nil {
return errors.Wrap(err, "Failed to create s3 client") return errors.Wrap(err, "Failed to create s3 client")
} }
filePath := s3Config.Path filepathTemplate := workspaceStorageSetting.FilepathTemplate
if !strings.Contains(filePath, "{filename}") { if !strings.Contains(filepathTemplate, "{filename}") {
filePath = filepath.Join(filePath, "{filename}") filepathTemplate = filepath.Join(filepathTemplate, "{filename}")
} }
filePath = replacePathTemplate(filePath, create.Filename) filepathTemplate = replacePathTemplate(filepathTemplate, create.Filename)
r := bytes.NewReader(create.Blob) r := bytes.NewReader(create.Blob)
link, err := s3Client.UploadFile(ctx, filePath, create.Type, r) 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")
} }

@ -162,28 +162,46 @@ func convertWorkspaceGeneralSettingToStore(setting *v1pb.WorkspaceGeneralSetting
return generalSetting return generalSetting
} }
func convertWorkspaceStorageSettingFromStore(setting *storepb.WorkspaceStorageSetting) *v1pb.WorkspaceStorageSetting { func convertWorkspaceStorageSettingFromStore(settingpb *storepb.WorkspaceStorageSetting) *v1pb.WorkspaceStorageSetting {
if setting == nil { if settingpb == nil {
return nil return nil
} }
return &v1pb.WorkspaceStorageSetting{ setting := &v1pb.WorkspaceStorageSetting{
StorageType: v1pb.WorkspaceStorageSetting_StorageType(setting.StorageType), StorageType: v1pb.WorkspaceStorageSetting_StorageType(settingpb.StorageType),
LocalStoragePathTemplate: setting.LocalStoragePathTemplate, FilepathTemplate: settingpb.FilepathTemplate,
UploadSizeLimitMb: setting.UploadSizeLimitMb, UploadSizeLimitMb: settingpb.UploadSizeLimitMb,
ActivedExternalStorageId: setting.ActivedExternalStorageId, }
if settingpb.S3Config != nil {
setting.S3Config = &v1pb.WorkspaceStorageSetting_S3Config{
AccessKeyId: settingpb.S3Config.AccessKeyId,
AccessKeySecret: settingpb.S3Config.AccessKeySecret,
Endpoint: settingpb.S3Config.Endpoint,
Region: settingpb.S3Config.Region,
Bucket: settingpb.S3Config.Bucket,
} }
}
return setting
} }
func convertWorkspaceStorageSettingToStore(setting *v1pb.WorkspaceStorageSetting) *storepb.WorkspaceStorageSetting { func convertWorkspaceStorageSettingToStore(setting *v1pb.WorkspaceStorageSetting) *storepb.WorkspaceStorageSetting {
if setting == nil { if setting == nil {
return nil return nil
} }
return &storepb.WorkspaceStorageSetting{ settingpb := &storepb.WorkspaceStorageSetting{
StorageType: storepb.WorkspaceStorageSetting_StorageType(setting.StorageType), StorageType: storepb.WorkspaceStorageSetting_StorageType(setting.StorageType),
LocalStoragePathTemplate: setting.LocalStoragePathTemplate, FilepathTemplate: setting.FilepathTemplate,
UploadSizeLimitMb: setting.UploadSizeLimitMb, UploadSizeLimitMb: setting.UploadSizeLimitMb,
ActivedExternalStorageId: setting.ActivedExternalStorageId,
} }
if setting.S3Config != nil {
settingpb.S3Config = &storepb.WorkspaceStorageSetting_S3Config{
AccessKeyId: setting.S3Config.AccessKeyId,
AccessKeySecret: setting.S3Config.AccessKeySecret,
Endpoint: setting.S3Config.Endpoint,
Region: setting.S3Config.Region,
Bucket: setting.S3Config.Bucket,
}
}
return settingpb
} }
func convertWorkspaceMemoRelatedSettingFromStore(setting *storepb.WorkspaceMemoRelatedSetting) *v1pb.WorkspaceMemoRelatedSetting { func convertWorkspaceMemoRelatedSettingFromStore(setting *storepb.WorkspaceMemoRelatedSetting) *v1pb.WorkspaceMemoRelatedSetting {

@ -22,7 +22,6 @@ import (
"github.com/usememos/memos/server/route/frontend" "github.com/usememos/memos/server/route/frontend"
"github.com/usememos/memos/server/route/resource" "github.com/usememos/memos/server/route/resource"
"github.com/usememos/memos/server/route/rss" "github.com/usememos/memos/server/route/rss"
resourcepresign "github.com/usememos/memos/server/service/resource_presign"
versionchecker "github.com/usememos/memos/server/service/version_checker" versionchecker "github.com/usememos/memos/server/service/version_checker"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
) )
@ -148,7 +147,6 @@ func (s *Server) Shutdown(ctx context.Context) {
} }
func (s *Server) StartBackgroundRunners(ctx context.Context) { func (s *Server) StartBackgroundRunners(ctx context.Context) {
go resourcepresign.RunPreSignLinks(ctx, s.Store)
go versionchecker.NewVersionChecker(s.Store, s.Profile).Start(ctx) go versionchecker.NewVersionChecker(s.Store, s.Profile).Start(ctx)
} }

@ -1,110 +0,0 @@
package resourcepresign
import (
"context"
"log/slog"
"strings"
"time"
"github.com/pkg/errors"
"github.com/usememos/memos/plugin/storage/s3"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store"
)
// RunPreSignLinks is a background runner that pre-signs external links stored in the database.
// It uses S3 client to generate presigned URLs and updates the corresponding resources in the store.
func RunPreSignLinks(ctx context.Context, dataStore *store.Store) {
for {
if err := signExternalLinks(ctx, dataStore); err != nil {
slog.Error("failed to pre-sign links", err)
} else {
slog.Debug("pre-signed links")
}
select {
case <-time.After(s3.LinkLifetime / 2):
case <-ctx.Done():
return
}
}
}
func signExternalLinks(ctx context.Context, dataStore *store.Store) error {
objectStore, err := findObjectStorage(ctx, dataStore)
if err != nil {
return errors.Wrapf(err, "find object storage")
}
if objectStore == nil || !objectStore.Config.PreSign {
// object storage not set or not supported
return nil
}
resources, err := dataStore.ListResources(ctx, &store.FindResource{
GetBlob: false,
})
if err != nil {
return errors.Wrapf(err, "list resources")
}
for _, resource := range resources {
if resource.ExternalLink == "" {
// not for object store
continue
}
if strings.Contains(resource.ExternalLink, "?") && time.Since(time.Unix(resource.UpdatedTs, 0)) < s3.LinkLifetime/2 {
// resource not signed (hack for migration)
// resource was recently updated - skipping
continue
}
newLink, err := objectStore.PreSignLink(ctx, resource.ExternalLink)
if err != nil {
slog.Error("failed to pre-sign link", err)
continue
}
now := time.Now().Unix()
if _, err := dataStore.UpdateResource(ctx, &store.UpdateResource{
ID: resource.ID,
UpdatedTs: &now,
ExternalLink: &newLink,
}); err != nil {
return errors.Wrapf(err, "update resource %d link to %q", resource.ID, newLink)
}
}
return nil
}
// findObjectStorage returns current default storage if it's S3-compatible or nil otherwise.
// Returns error only in case of internal problems (ie: database or configuration issues).
// May return nil client and nil error.
func findObjectStorage(ctx context.Context, dataStore *store.Store) (*s3.Client, error) {
workspaceStorageSetting, err := dataStore.GetWorkspaceStorageSetting(ctx)
if err != nil {
return nil, errors.Wrap(err, "Failed to find workspaceStorageSetting")
}
if workspaceStorageSetting.StorageType != storepb.WorkspaceStorageSetting_STORAGE_TYPE_EXTERNAL || workspaceStorageSetting.ActivedExternalStorageId == nil {
return nil, nil
}
storage, err := dataStore.GetStorage(ctx, &store.FindStorage{ID: workspaceStorageSetting.ActivedExternalStorageId})
if err != nil {
return nil, errors.Wrap(err, "Failed to find storage")
}
if storage == nil || storage.Type != storepb.Storage_S3 {
return nil, nil
}
s3Config := storage.Config.GetS3Config()
return s3.NewClient(ctx, &s3.Config{
AccessKey: s3Config.AccessKey,
SecretKey: s3Config.SecretKey,
EndPoint: s3Config.EndPoint,
Region: s3Config.Region,
Bucket: s3Config.Bucket,
URLPrefix: s3Config.UrlPrefix,
URLSuffix: s3Config.UrlSuffix,
PreSign: s3Config.PreSign,
})
}

@ -141,7 +141,7 @@ func (s *Store) GetWorkspaceMemoRelatedSetting(ctx context.Context) (*storepb.Wo
const ( const (
defaultWorkspaceStorageType = storepb.WorkspaceStorageSetting_STORAGE_TYPE_DATABASE defaultWorkspaceStorageType = storepb.WorkspaceStorageSetting_STORAGE_TYPE_DATABASE
defaultWorkspaceUploadSizeLimitMb = 30 defaultWorkspaceUploadSizeLimitMb = 30
defaultWorkspaceLocalStoragePathTemplate = "assets/{timestamp}_{filename}" defaultWorkspaceFilepathTemplate = "assets/{timestamp}_{filename}"
) )
func (s *Store) GetWorkspaceStorageSetting(ctx context.Context) (*storepb.WorkspaceStorageSetting, error) { func (s *Store) GetWorkspaceStorageSetting(ctx context.Context) (*storepb.WorkspaceStorageSetting, error) {
@ -162,8 +162,8 @@ func (s *Store) GetWorkspaceStorageSetting(ctx context.Context) (*storepb.Worksp
if workspaceStorageSetting.UploadSizeLimitMb == 0 { if workspaceStorageSetting.UploadSizeLimitMb == 0 {
workspaceStorageSetting.UploadSizeLimitMb = defaultWorkspaceUploadSizeLimitMb workspaceStorageSetting.UploadSizeLimitMb = defaultWorkspaceUploadSizeLimitMb
} }
if workspaceStorageSetting.LocalStoragePathTemplate == "" { if workspaceStorageSetting.FilepathTemplate == "" {
workspaceStorageSetting.LocalStoragePathTemplate = defaultWorkspaceLocalStoragePathTemplate workspaceStorageSetting.FilepathTemplate = defaultWorkspaceFilepathTemplate
} }
return workspaceStorageSetting, nil return workspaceStorageSetting, nil
} }

@ -1,257 +0,0 @@
import { Button, IconButton, Input, Checkbox, Typography } from "@mui/joy";
import React, { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
import { storageServiceClient } from "@/grpcweb";
import { S3Config, Storage, Storage_Type } from "@/types/proto/api/v1/storage_service";
import { useTranslate } from "@/utils/i18n";
import { generateDialog } from "./Dialog";
import Icon from "./Icon";
import LearnMore from "./LearnMore";
import RequiredBadge from "./RequiredBadge";
interface Props extends DialogProps {
storage?: Storage;
confirmCallback?: () => void;
}
const CreateStorageServiceDialog: React.FC<Props> = (props: Props) => {
const t = useTranslate();
const { destroy, storage, confirmCallback } = props;
const [basicInfo, setBasicInfo] = useState({
title: "",
});
const [type] = useState<Storage_Type>(Storage_Type.S3);
const [s3Config, setS3Config] = useState<S3Config>({
endPoint: "",
region: "",
accessKey: "",
secretKey: "",
path: "",
bucket: "",
urlPrefix: "",
urlSuffix: "",
preSign: false,
});
const isCreating = storage === undefined;
useEffect(() => {
if (storage) {
setBasicInfo({
title: storage.title,
});
if (storage.type === "S3") {
setS3Config(S3Config.fromPartial(storage.config?.s3Config || {}));
}
}
}, []);
const handleCloseBtnClick = () => {
destroy();
};
const allowConfirmAction = () => {
if (basicInfo.title === "") {
return false;
}
if (type === "S3") {
if (
s3Config.endPoint === "" ||
s3Config.region === "" ||
s3Config.accessKey === "" ||
s3Config.secretKey === "" ||
s3Config.bucket === ""
) {
return false;
}
}
return true;
};
const handleConfirmBtnClick = async () => {
try {
if (isCreating) {
await storageServiceClient.createStorage({
storage: Storage.fromPartial({
title: basicInfo.title,
type: type,
config: {
s3Config: s3Config,
},
}),
});
} else {
await storageServiceClient.updateStorage({
storage: Storage.fromPartial({
id: storage?.id,
title: basicInfo.title,
type: type,
config: {
s3Config: s3Config,
},
}),
updateMask: ["title", "config"],
});
}
} catch (error: any) {
console.error(error);
toast.error(error.response.data.message);
}
if (confirmCallback) {
confirmCallback();
}
destroy();
};
const setPartialS3Config = (state: Partial<S3Config>) => {
setS3Config({
...s3Config,
...state,
});
};
return (
<>
<div className="dialog-header-container">
<span>{t(isCreating ? "setting.storage-section.create-storage" : "setting.storage-section.update-storage")}</span>
<IconButton size="sm" onClick={handleCloseBtnClick}>
<Icon.X className="w-5 h-auto" />
</IconButton>
</div>
<div className="dialog-content-container min-w-[19rem]">
<Typography className="!mb-1" level="body-md">
{t("common.name")}
<RequiredBadge />
</Typography>
<Input
className="mb-2"
placeholder={t("common.name")}
value={basicInfo.title}
onChange={(e) =>
setBasicInfo({
...basicInfo,
title: e.target.value,
})
}
fullWidth
/>
<Typography className="!mb-1" level="body-md">
{t("setting.storage-section.endpoint")}
<RequiredBadge />
</Typography>
<Input
className="mb-2"
placeholder={t("setting.storage-section.s3-compatible-url")}
value={s3Config.endPoint}
onChange={(e) => setPartialS3Config({ endPoint: e.target.value })}
fullWidth
/>
<Typography className="!mb-1" level="body-md">
{t("setting.storage-section.region")}
<RequiredBadge />
</Typography>
<Input
className="mb-2"
placeholder={t("setting.storage-section.region-placeholder")}
value={s3Config.region}
onChange={(e) => setPartialS3Config({ region: e.target.value })}
fullWidth
/>
<Typography className="!mb-1" level="body-md">
{t("setting.storage-section.accesskey")}
<RequiredBadge />
</Typography>
<Input
className="mb-2"
placeholder={t("setting.storage-section.accesskey-placeholder")}
value={s3Config.accessKey}
onChange={(e) => setPartialS3Config({ accessKey: e.target.value })}
fullWidth
/>
<Typography className="!mb-1" level="body-md">
{t("setting.storage-section.secretkey")}
<RequiredBadge />
</Typography>
<Input
className="mb-2"
placeholder={t("setting.storage-section.secretkey-placeholder")}
value={s3Config.secretKey}
onChange={(e) => setPartialS3Config({ secretKey: e.target.value })}
fullWidth
/>
<Typography className="!mb-1" level="body-md">
{t("setting.storage-section.bucket")}
<RequiredBadge />
</Typography>
<Input
className="mb-2"
placeholder={t("setting.storage-section.bucket-placeholder")}
value={s3Config.bucket}
onChange={(e) => setPartialS3Config({ bucket: e.target.value })}
fullWidth
/>
<div className="flex flex-row items-center mb-1">
<Typography level="body-md">{t("setting.storage-section.path")}</Typography>
<LearnMore
className="ml-1"
title={t("setting.storage-section.path-description")}
url="https://usememos.com/docs/advanced-settings/local-storage"
/>
</div>
<Input
className="mb-2"
placeholder={t("setting.storage-section.path-placeholder") + "/{year}/{month}/{filename}"}
value={s3Config.path}
onChange={(e) => setPartialS3Config({ path: e.target.value })}
fullWidth
/>
<Typography className="!mb-1" level="body-md">
{t("setting.storage-section.url-prefix")}
</Typography>
<Input
className="mb-2"
placeholder={t("setting.storage-section.url-prefix-placeholder")}
value={s3Config.urlPrefix}
onChange={(e) => setPartialS3Config({ urlPrefix: e.target.value })}
fullWidth
/>
<Typography className="!mb-1" level="body-md">
{t("setting.storage-section.url-suffix")}
</Typography>
<Input
className="mb-2"
placeholder={t("setting.storage-section.url-suffix-placeholder")}
value={s3Config.urlSuffix}
onChange={(e) => setPartialS3Config({ urlSuffix: e.target.value })}
fullWidth
/>
<Checkbox
className="mb-2"
label={t("setting.storage-section.presign-placeholder")}
checked={s3Config.preSign}
onChange={(e) => setPartialS3Config({ preSign: e.target.checked })}
/>
<div className="mt-2 w-full flex flex-row justify-end items-center space-x-1">
<Button variant="plain" color="neutral" onClick={handleCloseBtnClick}>
{t("common.cancel")}
</Button>
<Button onClick={handleConfirmBtnClick} disabled={!allowConfirmAction()}>
{t(isCreating ? "common.create" : "common.update")}
</Button>
</div>
</div>
</>
);
};
function showCreateStorageServiceDialog(storage?: Storage, confirmCallback?: () => void) {
generateDialog(
{
className: "create-storage-service-dialog",
dialogName: "create-storage-service-dialog",
},
CreateStorageServiceDialog,
{ storage, confirmCallback },
);
}
export default showCreateStorageServiceDialog;

@ -1,38 +1,21 @@
import { import { Button, Divider, Input, List, ListItem, Radio, RadioGroup, Tooltip } from "@mui/joy";
Button,
Divider,
Dropdown,
Input,
List,
ListItem,
Menu,
MenuButton,
MenuItem,
Radio,
RadioGroup,
Select,
Tooltip,
Option,
} from "@mui/joy";
import { isEqual } from "lodash-es"; import { isEqual } from "lodash-es";
import { useEffect, useMemo, useState } from "react"; import { useMemo, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { storageServiceClient } from "@/grpcweb";
import { WorkspaceSettingPrefix, useWorkspaceSettingStore } from "@/store/v1"; import { WorkspaceSettingPrefix, useWorkspaceSettingStore } from "@/store/v1";
import { Storage } from "@/types/proto/api/v1/storage_service"; import {
import { WorkspaceStorageSetting, WorkspaceStorageSetting_StorageType } from "@/types/proto/api/v1/workspace_setting_service"; WorkspaceStorageSetting,
WorkspaceStorageSetting_S3Config,
WorkspaceStorageSetting_StorageType,
} from "@/types/proto/api/v1/workspace_setting_service";
import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting"; import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import showCreateStorageServiceDialog from "../CreateStorageServiceDialog";
import { showCommonDialog } from "../Dialog/CommonDialog";
import Icon from "../Icon"; import Icon from "../Icon";
import LearnMore from "../LearnMore";
const StorageSection = () => { const StorageSection = () => {
const t = useTranslate(); const t = useTranslate();
const workspaceSettingStore = useWorkspaceSettingStore(); const workspaceSettingStore = useWorkspaceSettingStore();
const [storageList, setStorageList] = useState<Storage[]>([]);
const [workspaceStorageSetting, setWorkspaceStorageSetting] = useState<WorkspaceStorageSetting>( const [workspaceStorageSetting, setWorkspaceStorageSetting] = useState<WorkspaceStorageSetting>(
WorkspaceStorageSetting.fromPartial( WorkspaceStorageSetting.fromPartial(
workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.WORKSPACE_SETTING_STORAGE)?.storageSetting || {}, workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.WORKSPACE_SETTING_STORAGE)?.storageSetting || {},
@ -48,26 +31,23 @@ const StorageSection = () => {
workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.WORKSPACE_SETTING_STORAGE)?.storageSetting || {}, workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.WORKSPACE_SETTING_STORAGE)?.storageSetting || {},
); );
if (workspaceStorageSetting.storageType === WorkspaceStorageSetting_StorageType.STORAGE_TYPE_LOCAL) { if (workspaceStorageSetting.storageType === WorkspaceStorageSetting_StorageType.STORAGE_TYPE_LOCAL) {
if (workspaceStorageSetting.localStoragePathTemplate.length === 0) { if (workspaceStorageSetting.filepathTemplate.length === 0) {
return false; return false;
} }
} else if (workspaceStorageSetting.storageType === WorkspaceStorageSetting_StorageType.STORAGE_TYPE_EXTERNAL) { } else if (workspaceStorageSetting.storageType === WorkspaceStorageSetting_StorageType.STORAGE_TYPE_S3) {
if (!workspaceStorageSetting.activedExternalStorageId || workspaceStorageSetting.activedExternalStorageId === 0) { if (
workspaceStorageSetting.s3Config?.accessKeyId.length === 0 ||
workspaceStorageSetting.s3Config?.accessKeySecret.length === 0 ||
workspaceStorageSetting.s3Config?.endpoint.length === 0 ||
workspaceStorageSetting.s3Config?.region.length === 0 ||
workspaceStorageSetting.s3Config?.bucket.length === 0
) {
return false; return false;
} }
} }
return !isEqual(origin, workspaceStorageSetting); return !isEqual(origin, workspaceStorageSetting);
}, [workspaceStorageSetting, workspaceSettingStore.getState()]); }, [workspaceStorageSetting, workspaceSettingStore.getState()]);
useEffect(() => {
fetchStorageList();
}, []);
const fetchStorageList = async () => {
const { storages } = await storageServiceClient.listStorages({});
setStorageList(storages);
};
const handleMaxUploadSizeChanged = async (event: React.FocusEvent<HTMLInputElement>) => { const handleMaxUploadSizeChanged = async (event: React.FocusEvent<HTMLInputElement>) => {
let num = parseInt(event.target.value); let num = parseInt(event.target.value);
if (Number.isNaN(num)) { if (Number.isNaN(num)) {
@ -80,26 +60,49 @@ const StorageSection = () => {
setWorkspaceStorageSetting(update); setWorkspaceStorageSetting(update);
}; };
const handleLocalStoragePathTemplateChanged = async (event: React.FocusEvent<HTMLInputElement>) => { const handleFilepathTemplateChanged = async (event: React.FocusEvent<HTMLInputElement>) => {
const update: WorkspaceStorageSetting = { const update: WorkspaceStorageSetting = {
...workspaceStorageSetting, ...workspaceStorageSetting,
localStoragePathTemplate: event.target.value, filepathTemplate: event.target.value,
}; };
setWorkspaceStorageSetting(update); setWorkspaceStorageSetting(update);
}; };
const handleStorageTypeChanged = async (storageType: WorkspaceStorageSetting_StorageType) => { const handlePartialS3ConfigChanged = async (s3Config: Partial<WorkspaceStorageSetting_S3Config>) => {
const update: WorkspaceStorageSetting = { const update: WorkspaceStorageSetting = {
...workspaceStorageSetting, ...workspaceStorageSetting,
storageType: storageType, s3Config: WorkspaceStorageSetting_S3Config.fromPartial({
...workspaceStorageSetting.s3Config,
...s3Config,
}),
}; };
setWorkspaceStorageSetting(update); setWorkspaceStorageSetting(update);
}; };
const handleActivedExternalStorageIdChanged = async (activedExternalStorageId: number) => { const handleS3ConfigAccessKeyIdChanged = async (event: React.FocusEvent<HTMLInputElement>) => {
handlePartialS3ConfigChanged({ accessKeyId: event.target.value });
};
const handleS3ConfigAccessKeySecretChanged = async (event: React.FocusEvent<HTMLInputElement>) => {
handlePartialS3ConfigChanged({ accessKeySecret: event.target.value });
};
const handleS3ConfigEndpointChanged = async (event: React.FocusEvent<HTMLInputElement>) => {
handlePartialS3ConfigChanged({ endpoint: event.target.value });
};
const handleS3ConfigRegionChanged = async (event: React.FocusEvent<HTMLInputElement>) => {
handlePartialS3ConfigChanged({ region: event.target.value });
};
const handleS3ConfigBucketChanged = async (event: React.FocusEvent<HTMLInputElement>) => {
handlePartialS3ConfigChanged({ bucket: event.target.value });
};
const handleStorageTypeChanged = async (storageType: WorkspaceStorageSetting_StorageType) => {
const update: WorkspaceStorageSetting = { const update: WorkspaceStorageSetting = {
...workspaceStorageSetting, ...workspaceStorageSetting,
activedExternalStorageId: activedExternalStorageId, storageType: storageType,
}; };
setWorkspaceStorageSetting(update); setWorkspaceStorageSetting(update);
}; };
@ -112,24 +115,6 @@ const StorageSection = () => {
toast.success("Updated"); toast.success("Updated");
}; };
const handleDeleteStorage = (storage: Storage) => {
showCommonDialog({
title: t("setting.storage-section.delete-storage"),
content: t("setting.storage-section.warning-text", { name: storage.title }),
style: "danger",
dialogName: "delete-storage-dialog",
onConfirm: async () => {
try {
await storageServiceClient.deleteStorage({ id: storage.id });
} catch (error: any) {
console.error(error);
toast.error(error.response.data.message);
}
await fetchStorageList();
},
});
};
return ( return (
<div className="w-full flex flex-col gap-2 pt-2 pb-4"> <div className="w-full flex flex-col gap-2 pt-2 pb-4">
<div className="font-medium text-gray-700 dark:text-gray-500">{t("setting.storage-section.current-storage")}</div> <div className="font-medium text-gray-700 dark:text-gray-500">{t("setting.storage-section.current-storage")}</div>
@ -143,7 +128,7 @@ const StorageSection = () => {
> >
<Radio value={WorkspaceStorageSetting_StorageType.STORAGE_TYPE_DATABASE} label={t("setting.storage-section.type-database")} /> <Radio value={WorkspaceStorageSetting_StorageType.STORAGE_TYPE_DATABASE} label={t("setting.storage-section.type-database")} />
<Radio value={WorkspaceStorageSetting_StorageType.STORAGE_TYPE_LOCAL} label={t("setting.storage-section.type-local")} /> <Radio value={WorkspaceStorageSetting_StorageType.STORAGE_TYPE_LOCAL} label={t("setting.storage-section.type-local")} />
<Radio value={WorkspaceStorageSetting_StorageType.STORAGE_TYPE_EXTERNAL} disabled={storageList.length === 0} label={"S3"} /> <Radio value={WorkspaceStorageSetting_StorageType.STORAGE_TYPE_S3} label={"S3"} />
</RadioGroup> </RadioGroup>
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
@ -161,30 +146,47 @@ const StorageSection = () => {
onChange={handleMaxUploadSizeChanged} onChange={handleMaxUploadSizeChanged}
/> />
</div> </div>
{workspaceStorageSetting.storageType === WorkspaceStorageSetting_StorageType.STORAGE_TYPE_LOCAL && ( {workspaceStorageSetting.storageType !== WorkspaceStorageSetting_StorageType.STORAGE_TYPE_DATABASE && (
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<span className="text-gray-700 dark:text-gray-500 mr-1">Local file path template</span> <span className="text-gray-700 dark:text-gray-500 mr-1">Filepath template</span>
<Input <Input
defaultValue={workspaceStorageSetting.localStoragePathTemplate} defaultValue={workspaceStorageSetting.filepathTemplate}
placeholder="assets/{timestamp}_{filename}" placeholder="assets/{timestamp}_{filename}"
onChange={handleLocalStoragePathTemplateChanged} onChange={handleFilepathTemplateChanged}
/> />
</div> </div>
)} )}
{workspaceStorageSetting.storageType === WorkspaceStorageSetting_StorageType.STORAGE_TYPE_EXTERNAL && ( {workspaceStorageSetting.storageType === WorkspaceStorageSetting_StorageType.STORAGE_TYPE_S3 && (
<>
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<span className="text-gray-700 dark:text-gray-500 mr-1">Actived storage</span> <span className="text-gray-700 dark:text-gray-500 mr-1">Access key id</span>
<Select <Input
onChange={(_, value) => handleActivedExternalStorageIdChanged(value as number)} defaultValue={workspaceStorageSetting.s3Config?.accessKeyId}
defaultValue={workspaceStorageSetting.activedExternalStorageId} placeholder=""
> onChange={handleS3ConfigAccessKeyIdChanged}
{storageList.map((storage) => ( />
<Option key={storage.id} value={storage.id}> </div>
{storage.title} <div className="w-full flex flex-row justify-between items-center">
</Option> <span className="text-gray-700 dark:text-gray-500 mr-1">Access key secret</span>
))} <Input
</Select> defaultValue={workspaceStorageSetting.s3Config?.accessKeySecret}
placeholder=""
onChange={handleS3ConfigAccessKeySecretChanged}
/>
</div>
<div className="w-full flex flex-row justify-between items-center">
<span className="text-gray-700 dark:text-gray-500 mr-1">Endpoint</span>
<Input defaultValue={workspaceStorageSetting.s3Config?.endpoint} placeholder="" onChange={handleS3ConfigEndpointChanged} />
</div>
<div className="w-full flex flex-row justify-between items-center">
<span className="text-gray-700 dark:text-gray-500 mr-1">Region</span>
<Input defaultValue={workspaceStorageSetting.s3Config?.region} placeholder="" onChange={handleS3ConfigRegionChanged} />
</div>
<div className="w-full flex flex-row justify-between items-center">
<span className="text-gray-700 dark:text-gray-500 mr-1">Bucket</span>
<Input defaultValue={workspaceStorageSetting.s3Config?.bucket} placeholder="" onChange={handleS3ConfigBucketChanged} />
</div> </div>
</>
)} )}
<div> <div>
<Button disabled={!allowSaveStorageSetting} onClick={saveWorkspaceStorageSetting}> <Button disabled={!allowSaveStorageSetting} onClick={saveWorkspaceStorageSetting}>
@ -192,41 +194,6 @@ const StorageSection = () => {
</Button> </Button>
</div> </div>
<Divider className="!my-2" /> <Divider className="!my-2" />
<div className="mb-2 w-full flex flex-row justify-between items-center gap-1">
<div className="flex items-center gap-1">
<span className="font-mono text-sm text-gray-400">{t("setting.storage-section.storage-services")}</span>
<LearnMore url="https://usememos.com/docs/advanced-settings/cloudflare-r2" />
</div>
<Button onClick={() => showCreateStorageServiceDialog(undefined, fetchStorageList)}>{t("common.create")}</Button>
</div>
<div className="w-full flex flex-col">
{storageList.map((storage) => (
<div
key={storage.id}
className="py-2 w-full border-t last:border-b dark:border-zinc-700 flex flex-row items-center justify-between"
>
<div className="flex flex-row items-center">
<p className="ml-2">{storage.title}</p>
</div>
<div className="flex flex-row items-center">
<Dropdown>
<MenuButton size="sm">
<Icon.MoreVertical className="w-4 h-auto" />
</MenuButton>
<Menu placement="bottom-end" size="sm">
<MenuItem onClick={() => showCreateStorageServiceDialog(storage, fetchStorageList)}>{t("common.edit")}</MenuItem>
<MenuItem onClick={() => handleDeleteStorage(storage)}>{t("common.delete")}</MenuItem>
</Menu>
</Dropdown>
</div>
</div>
))}
{storageList.length === 0 && (
<div className="w-full text-sm dark:border-zinc-700 opacity-60 flex flex-row items-center justify-between">
<p className="">No storage service found.</p>
</div>
)}
</div>
<div className="w-full mt-4"> <div className="w-full mt-4">
<p className="text-sm">{t("common.learn-more")}:</p> <p className="text-sm">{t("common.learn-more")}:</p>
<List component="ul" marker="disc" size="sm"> <List component="ul" marker="disc" size="sm">

@ -6,7 +6,6 @@ import { InboxServiceDefinition } from "./types/proto/api/v1/inbox_service";
import { LinkServiceDefinition } from "./types/proto/api/v1/link_service"; import { LinkServiceDefinition } from "./types/proto/api/v1/link_service";
import { MemoServiceDefinition } from "./types/proto/api/v1/memo_service"; import { MemoServiceDefinition } from "./types/proto/api/v1/memo_service";
import { ResourceServiceDefinition } from "./types/proto/api/v1/resource_service"; import { ResourceServiceDefinition } from "./types/proto/api/v1/resource_service";
import { StorageServiceDefinition } from "./types/proto/api/v1/storage_service";
import { TagServiceDefinition } from "./types/proto/api/v1/tag_service"; import { TagServiceDefinition } from "./types/proto/api/v1/tag_service";
import { UserServiceDefinition } from "./types/proto/api/v1/user_service"; import { UserServiceDefinition } from "./types/proto/api/v1/user_service";
import { WebhookServiceDefinition } from "./types/proto/api/v1/webhook_service"; import { WebhookServiceDefinition } from "./types/proto/api/v1/webhook_service";
@ -44,6 +43,4 @@ export const webhookServiceClient = clientFactory.create(WebhookServiceDefinitio
export const linkServiceClient = clientFactory.create(LinkServiceDefinition, channel); export const linkServiceClient = clientFactory.create(LinkServiceDefinition, channel);
export const storageServiceClient = clientFactory.create(StorageServiceDefinition, channel);
export const identityProviderServiceClient = clientFactory.create(IdentityProviderServiceDefinition, channel); export const identityProviderServiceClient = clientFactory.create(IdentityProviderServiceDefinition, channel);

Loading…
Cancel
Save