mirror of https://github.com/usememos/memos.git
Merge 2dbaddc6a5 into bfad0708e2
This commit is contained in:
commit
c515079f12
|
|
@ -147,6 +147,16 @@ message WorkspaceSetting {
|
|||
}
|
||||
// The S3 config.
|
||||
S3Config s3_config = 4;
|
||||
// The maximum size in pixels for the largest dimension of thumbnail images.
|
||||
int32 thumbnail_max_size = 5;
|
||||
// The JPEG quality (0-100) used when downscaling uploaded images.
|
||||
int32 jpeg_quality = 6;
|
||||
// The JPEG quality (0-100) used when generating thumbnails.
|
||||
int32 thumbnail_jpeg_quality = 7;
|
||||
// The maximum size in pixels for the largest dimension when storing images.
|
||||
// Images larger than this will be downscaled before storage.
|
||||
// Set to 0 to disable downscaling.
|
||||
int32 image_max_size = 8;
|
||||
}
|
||||
|
||||
// Memo-related workspace settings and policies.
|
||||
|
|
|
|||
|
|
@ -428,7 +428,6 @@ type GetUserRequest struct {
|
|||
// Supports both numeric IDs and username strings:
|
||||
// - users/{id} (e.g., users/101)
|
||||
// - users/{username} (e.g., users/steven)
|
||||
//
|
||||
// Format: users/{id_or_username}
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
// Optional. The fields to return in the response.
|
||||
|
|
|
|||
|
|
@ -589,7 +589,17 @@ type WorkspaceSetting_StorageSetting struct {
|
|||
// The max upload size in megabytes.
|
||||
UploadSizeLimitMb int64 `protobuf:"varint,3,opt,name=upload_size_limit_mb,json=uploadSizeLimitMb,proto3" json:"upload_size_limit_mb,omitempty"`
|
||||
// The S3 config.
|
||||
S3Config *WorkspaceSetting_StorageSetting_S3Config `protobuf:"bytes,4,opt,name=s3_config,json=s3Config,proto3" json:"s3_config,omitempty"`
|
||||
S3Config *WorkspaceSetting_StorageSetting_S3Config `protobuf:"bytes,4,opt,name=s3_config,json=s3Config,proto3" json:"s3_config,omitempty"`
|
||||
// The maximum size in pixels for the largest dimension of thumbnail images.
|
||||
ThumbnailMaxSize int32 `protobuf:"varint,5,opt,name=thumbnail_max_size,json=thumbnailMaxSize,proto3" json:"thumbnail_max_size,omitempty"`
|
||||
// The JPEG quality (0-100) used when downscaling uploaded images.
|
||||
JpegQuality int32 `protobuf:"varint,6,opt,name=jpeg_quality,json=jpegQuality,proto3" json:"jpeg_quality,omitempty"`
|
||||
// The JPEG quality (0-100) used when generating thumbnails.
|
||||
ThumbnailJpegQuality int32 `protobuf:"varint,7,opt,name=thumbnail_jpeg_quality,json=thumbnailJpegQuality,proto3" json:"thumbnail_jpeg_quality,omitempty"`
|
||||
// The maximum size in pixels for the largest dimension when storing images.
|
||||
// Images larger than this will be downscaled before storage.
|
||||
// Set to 0 to disable downscaling.
|
||||
ImageMaxSize int32 `protobuf:"varint,8,opt,name=image_max_size,json=imageMaxSize,proto3" json:"image_max_size,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
|
@ -652,6 +662,34 @@ func (x *WorkspaceSetting_StorageSetting) GetS3Config() *WorkspaceSetting_Storag
|
|||
return nil
|
||||
}
|
||||
|
||||
func (x *WorkspaceSetting_StorageSetting) GetThumbnailMaxSize() int32 {
|
||||
if x != nil {
|
||||
return x.ThumbnailMaxSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WorkspaceSetting_StorageSetting) GetJpegQuality() int32 {
|
||||
if x != nil {
|
||||
return x.JpegQuality
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WorkspaceSetting_StorageSetting) GetThumbnailJpegQuality() int32 {
|
||||
if x != nil {
|
||||
return x.ThumbnailJpegQuality
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WorkspaceSetting_StorageSetting) GetImageMaxSize() int32 {
|
||||
if x != nil {
|
||||
return x.ImageMaxSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Memo-related workspace settings and policies.
|
||||
type WorkspaceSetting_MemoRelatedSetting struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
|
|
@ -935,7 +973,7 @@ const file_api_v1_workspace_service_proto_rawDesc = "" +
|
|||
"\aversion\x18\x02 \x01(\tR\aversion\x12\x12\n" +
|
||||
"\x04mode\x18\x03 \x01(\tR\x04mode\x12!\n" +
|
||||
"\finstance_url\x18\x06 \x01(\tR\vinstanceUrl\"\x1c\n" +
|
||||
"\x1aGetWorkspaceProfileRequest\"\x97\x11\n" +
|
||||
"\x1aGetWorkspaceProfileRequest\"\xc4\x12\n" +
|
||||
"\x10WorkspaceSetting\x12\x17\n" +
|
||||
"\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x12X\n" +
|
||||
"\x0fgeneral_setting\x18\x02 \x01(\v2-.memos.api.v1.WorkspaceSetting.GeneralSettingH\x00R\x0egeneralSetting\x12X\n" +
|
||||
|
|
@ -955,12 +993,16 @@ const file_api_v1_workspace_service_proto_rawDesc = "" +
|
|||
"\x05title\x18\x01 \x01(\tR\x05title\x12 \n" +
|
||||
"\vdescription\x18\x02 \x01(\tR\vdescription\x12\x19\n" +
|
||||
"\blogo_url\x18\x03 \x01(\tR\alogoUrl\x12\x16\n" +
|
||||
"\x06locale\x18\x04 \x01(\tR\x06locale\x1a\xbe\x04\n" +
|
||||
"\x06locale\x18\x04 \x01(\tR\x06locale\x1a\xeb\x05\n" +
|
||||
"\x0eStorageSetting\x12\\\n" +
|
||||
"\fstorage_type\x18\x01 \x01(\x0e29.memos.api.v1.WorkspaceSetting.StorageSetting.StorageTypeR\vstorageType\x12+\n" +
|
||||
"\x11filepath_template\x18\x02 \x01(\tR\x10filepathTemplate\x12/\n" +
|
||||
"\x14upload_size_limit_mb\x18\x03 \x01(\x03R\x11uploadSizeLimitMb\x12S\n" +
|
||||
"\ts3_config\x18\x04 \x01(\v26.memos.api.v1.WorkspaceSetting.StorageSetting.S3ConfigR\bs3Config\x1a\xcc\x01\n" +
|
||||
"\ts3_config\x18\x04 \x01(\v26.memos.api.v1.WorkspaceSetting.StorageSetting.S3ConfigR\bs3Config\x12,\n" +
|
||||
"\x12thumbnail_max_size\x18\x05 \x01(\x05R\x10thumbnailMaxSize\x12!\n" +
|
||||
"\fjpeg_quality\x18\x06 \x01(\x05R\vjpegQuality\x124\n" +
|
||||
"\x16thumbnail_jpeg_quality\x18\a \x01(\x05R\x14thumbnailJpegQuality\x12$\n" +
|
||||
"\x0eimage_max_size\x18\b \x01(\x05R\fimageMaxSize\x1a\xcc\x01\n" +
|
||||
"\bS3Config\x12\"\n" +
|
||||
"\raccess_key_id\x18\x01 \x01(\tR\vaccessKeyId\x12*\n" +
|
||||
"\x11access_key_secret\x18\x02 \x01(\tR\x0faccessKeySecret\x12\x1a\n" +
|
||||
|
|
|
|||
|
|
@ -15,19 +15,13 @@ paths:
|
|||
parameters:
|
||||
- name: pageSize
|
||||
in: query
|
||||
description: |-
|
||||
The maximum number of activities to return.
|
||||
The service may return fewer than this value.
|
||||
If unspecified, at most 100 activities will be returned.
|
||||
The maximum value is 1000; values above 1000 will be coerced to 1000.
|
||||
description: "The maximum number of activities to return.\r\n The service may return fewer than this value.\r\n If unspecified, at most 100 activities will be returned.\r\n The maximum value is 1000; values above 1000 will be coerced to 1000."
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
- name: pageToken
|
||||
in: query
|
||||
description: |-
|
||||
A page token, received from a previous `ListActivities` call.
|
||||
Provide this to retrieve the subsequent page.
|
||||
description: "A page token, received from a previous `ListActivities` call.\r\n Provide this to retrieve the subsequent page."
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
|
|
@ -78,35 +72,23 @@ paths:
|
|||
parameters:
|
||||
- name: pageSize
|
||||
in: query
|
||||
description: |-
|
||||
Optional. The maximum number of attachments to return.
|
||||
The service may return fewer than this value.
|
||||
If unspecified, at most 50 attachments will be returned.
|
||||
The maximum value is 1000; values above 1000 will be coerced to 1000.
|
||||
description: "Optional. The maximum number of attachments to return.\r\n The service may return fewer than this value.\r\n If unspecified, at most 50 attachments will be returned.\r\n The maximum value is 1000; values above 1000 will be coerced to 1000."
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
- name: pageToken
|
||||
in: query
|
||||
description: |-
|
||||
Optional. A page token, received from a previous `ListAttachments` call.
|
||||
Provide this to retrieve the subsequent page.
|
||||
description: "Optional. A page token, received from a previous `ListAttachments` call.\r\n Provide this to retrieve the subsequent page."
|
||||
schema:
|
||||
type: string
|
||||
- name: filter
|
||||
in: query
|
||||
description: |-
|
||||
Optional. Filter to apply to the list results.
|
||||
Example: "type=image/png" or "filename:*.jpg"
|
||||
Supported operators: =, !=, <, <=, >, >=, :
|
||||
Supported fields: filename, type, size, create_time, memo
|
||||
description: "Optional. Filter to apply to the list results.\r\n Example: \"type=image/png\" or \"filename:*.jpg\"\r\n Supported operators: =, !=, <, <=, >, >=, :\r\n Supported fields: filename, type, size, create_time, memo"
|
||||
schema:
|
||||
type: string
|
||||
- name: orderBy
|
||||
in: query
|
||||
description: |-
|
||||
Optional. The order to sort results by.
|
||||
Example: "create_time desc" or "filename asc"
|
||||
description: "Optional. The order to sort results by.\r\n Example: \"create_time desc\" or \"filename asc\""
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
|
|
@ -130,9 +112,7 @@ paths:
|
|||
parameters:
|
||||
- name: attachmentId
|
||||
in: query
|
||||
description: |-
|
||||
Optional. The attachment ID to use for this attachment.
|
||||
If empty, a unique ID will be generated.
|
||||
description: "Optional. The attachment ID to use for this attachment.\r\n If empty, a unique ID will be generated."
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
|
|
@ -243,9 +223,7 @@ paths:
|
|||
post:
|
||||
tags:
|
||||
- AuthService
|
||||
description: |-
|
||||
CreateSession authenticates a user and creates a new session.
|
||||
Returns the authenticated user information upon successful authentication.
|
||||
description: "CreateSession authenticates a user and creates a new session.\r\n Returns the authenticated user information upon successful authentication."
|
||||
operationId: AuthService_CreateSession
|
||||
requestBody:
|
||||
content:
|
||||
|
|
@ -270,9 +248,7 @@ paths:
|
|||
get:
|
||||
tags:
|
||||
- AuthService
|
||||
description: |-
|
||||
GetCurrentSession returns the current active session information.
|
||||
This method is idempotent and safe, suitable for checking current session state.
|
||||
description: "GetCurrentSession returns the current active session information.\r\n This method is idempotent and safe, suitable for checking current session state."
|
||||
operationId: AuthService_GetCurrentSession
|
||||
responses:
|
||||
"200":
|
||||
|
|
@ -290,9 +266,7 @@ paths:
|
|||
delete:
|
||||
tags:
|
||||
- AuthService
|
||||
description: |-
|
||||
DeleteSession terminates the current user session.
|
||||
This is an idempotent operation that invalidates the user's authentication.
|
||||
description: "DeleteSession terminates the current user session.\r\n This is an idempotent operation that invalidates the user's authentication."
|
||||
operationId: AuthService_DeleteSession
|
||||
responses:
|
||||
"200":
|
||||
|
|
@ -331,9 +305,7 @@ paths:
|
|||
parameters:
|
||||
- name: identityProviderId
|
||||
in: query
|
||||
description: |-
|
||||
Optional. The ID to use for the identity provider, which will become the final component of the resource name.
|
||||
If not provided, the system will generate one.
|
||||
description: "Optional. The ID to use for the identity provider, which will become the final component of the resource name.\r\n If not provided, the system will generate one."
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
|
|
@ -417,9 +389,7 @@ paths:
|
|||
type: string
|
||||
- name: updateMask
|
||||
in: query
|
||||
description: |-
|
||||
Required. The update mask applies to the resource. Only the top level fields of
|
||||
IdentityProvider are supported.
|
||||
description: "Required. The update mask applies to the resource. Only the top level fields of\r\n IdentityProvider are supported."
|
||||
schema:
|
||||
type: string
|
||||
format: field-mask
|
||||
|
|
@ -511,9 +481,7 @@ paths:
|
|||
get:
|
||||
tags:
|
||||
- MarkdownService
|
||||
description: |-
|
||||
GetLinkMetadata returns metadata for a given link.
|
||||
This is useful for generating link previews.
|
||||
description: "GetLinkMetadata returns metadata for a given link.\r\n This is useful for generating link previews."
|
||||
operationId: MarkdownService_GetLinkMetadata
|
||||
parameters:
|
||||
- name: link
|
||||
|
|
@ -538,9 +506,7 @@ paths:
|
|||
post:
|
||||
tags:
|
||||
- MarkdownService
|
||||
description: |-
|
||||
ParseMarkdown parses the given markdown content and returns a list of nodes.
|
||||
This is a utility method that transforms markdown text into structured nodes.
|
||||
description: "ParseMarkdown parses the given markdown content and returns a list of nodes.\r\n This is a utility method that transforms markdown text into structured nodes."
|
||||
operationId: MarkdownService_ParseMarkdown
|
||||
requestBody:
|
||||
content:
|
||||
|
|
@ -565,9 +531,7 @@ paths:
|
|||
post:
|
||||
tags:
|
||||
- MarkdownService
|
||||
description: |-
|
||||
RestoreMarkdownNodes restores the given nodes to markdown content.
|
||||
This is the inverse operation of ParseMarkdown.
|
||||
description: "RestoreMarkdownNodes restores the given nodes to markdown content.\r\n This is the inverse operation of ParseMarkdown."
|
||||
operationId: MarkdownService_RestoreMarkdownNodes
|
||||
requestBody:
|
||||
content:
|
||||
|
|
@ -592,9 +556,7 @@ paths:
|
|||
post:
|
||||
tags:
|
||||
- MarkdownService
|
||||
description: |-
|
||||
StringifyMarkdownNodes stringify the given nodes to plain text content.
|
||||
This removes all markdown formatting and returns plain text.
|
||||
description: "StringifyMarkdownNodes stringify the given nodes to plain text content.\r\n This removes all markdown formatting and returns plain text."
|
||||
operationId: MarkdownService_StringifyMarkdownNodes
|
||||
requestBody:
|
||||
content:
|
||||
|
|
@ -624,26 +586,18 @@ paths:
|
|||
parameters:
|
||||
- name: pageSize
|
||||
in: query
|
||||
description: |-
|
||||
Optional. The maximum number of memos to return.
|
||||
The service may return fewer than this value.
|
||||
If unspecified, at most 50 memos will be returned.
|
||||
The maximum value is 1000; values above 1000 will be coerced to 1000.
|
||||
description: "Optional. The maximum number of memos to return.\r\n The service may return fewer than this value.\r\n If unspecified, at most 50 memos will be returned.\r\n The maximum value is 1000; values above 1000 will be coerced to 1000."
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
- name: pageToken
|
||||
in: query
|
||||
description: |-
|
||||
Optional. A page token, received from a previous `ListMemos` call.
|
||||
Provide this to retrieve the subsequent page.
|
||||
description: "Optional. A page token, received from a previous `ListMemos` call.\r\n Provide this to retrieve the subsequent page."
|
||||
schema:
|
||||
type: string
|
||||
- name: state
|
||||
in: query
|
||||
description: |-
|
||||
Optional. The state of the memos to list.
|
||||
Default to `NORMAL`. Set to `ARCHIVED` to list archived memos.
|
||||
description: "Optional. The state of the memos to list.\r\n Default to `NORMAL`. Set to `ARCHIVED` to list archived memos."
|
||||
schema:
|
||||
enum:
|
||||
- STATE_UNSPECIFIED
|
||||
|
|
@ -653,20 +607,12 @@ paths:
|
|||
format: enum
|
||||
- name: orderBy
|
||||
in: query
|
||||
description: |-
|
||||
Optional. The order to sort results by.
|
||||
Default to "display_time desc".
|
||||
Supports comma-separated list of fields following AIP-132.
|
||||
Example: "pinned desc, display_time desc" or "create_time asc"
|
||||
Supported fields: pinned, display_time, create_time, update_time, name
|
||||
description: "Optional. The order to sort results by.\r\n Default to \"display_time desc\".\r\n Supports comma-separated list of fields following AIP-132.\r\n Example: \"pinned desc, display_time desc\" or \"create_time asc\"\r\n Supported fields: pinned, display_time, create_time, update_time, name"
|
||||
schema:
|
||||
type: string
|
||||
- name: filter
|
||||
in: query
|
||||
description: |-
|
||||
Optional. Filter to apply to the list results.
|
||||
Filter is a CEL expression to filter memos.
|
||||
Refer to `Shortcut.filter`.
|
||||
description: "Optional. Filter to apply to the list results.\r\n Filter is a CEL expression to filter memos.\r\n Refer to `Shortcut.filter`."
|
||||
schema:
|
||||
type: string
|
||||
- name: showDeleted
|
||||
|
|
@ -695,9 +641,7 @@ paths:
|
|||
parameters:
|
||||
- name: memoId
|
||||
in: query
|
||||
description: |-
|
||||
Optional. The memo ID to use for this memo.
|
||||
If empty, a unique ID will be generated.
|
||||
description: "Optional. The memo ID to use for this memo.\r\n If empty, a unique ID will be generated."
|
||||
schema:
|
||||
type: string
|
||||
- name: validateOnly
|
||||
|
|
@ -744,9 +688,7 @@ paths:
|
|||
type: string
|
||||
- name: readMask
|
||||
in: query
|
||||
description: |-
|
||||
Optional. The fields to return in the response.
|
||||
If not specified, all fields are returned.
|
||||
description: "Optional. The fields to return in the response.\r\n If not specified, all fields are returned."
|
||||
schema:
|
||||
type: string
|
||||
format: field-mask
|
||||
|
|
@ -1198,28 +1140,18 @@ paths:
|
|||
parameters:
|
||||
- name: pageSize
|
||||
in: query
|
||||
description: |-
|
||||
Optional. The maximum number of users to return.
|
||||
The service may return fewer than this value.
|
||||
If unspecified, at most 50 users will be returned.
|
||||
The maximum value is 1000; values above 1000 will be coerced to 1000.
|
||||
description: "Optional. The maximum number of users to return.\r\n The service may return fewer than this value.\r\n If unspecified, at most 50 users will be returned.\r\n The maximum value is 1000; values above 1000 will be coerced to 1000."
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
- name: pageToken
|
||||
in: query
|
||||
description: |-
|
||||
Optional. A page token, received from a previous `ListUsers` call.
|
||||
Provide this to retrieve the subsequent page.
|
||||
description: "Optional. A page token, received from a previous `ListUsers` call.\r\n Provide this to retrieve the subsequent page."
|
||||
schema:
|
||||
type: string
|
||||
- name: filter
|
||||
in: query
|
||||
description: |-
|
||||
Optional. Filter to apply to the list results.
|
||||
Example: "username == 'steven'"
|
||||
Supported operators: ==
|
||||
Supported fields: username
|
||||
description: "Optional. Filter to apply to the list results.\r\n Example: \"username == 'steven'\"\r\n Supported operators: ==\r\n Supported fields: username"
|
||||
schema:
|
||||
type: string
|
||||
- name: showDeleted
|
||||
|
|
@ -1248,10 +1180,7 @@ paths:
|
|||
parameters:
|
||||
- name: userId
|
||||
in: query
|
||||
description: |-
|
||||
Optional. The user ID to use for this user.
|
||||
If empty, a unique ID will be generated.
|
||||
Must match the pattern [a-z0-9-]+
|
||||
description: "Optional. The user ID to use for this user.\r\n If empty, a unique ID will be generated.\r\n Must match the pattern [a-z0-9-]+"
|
||||
schema:
|
||||
type: string
|
||||
- name: validateOnly
|
||||
|
|
@ -1261,9 +1190,7 @@ paths:
|
|||
type: boolean
|
||||
- name: requestId
|
||||
in: query
|
||||
description: |-
|
||||
Optional. An idempotency token that can be used to ensure that multiple
|
||||
requests to create a user have the same result.
|
||||
description: "Optional. An idempotency token that can be used to ensure that multiple\r\n requests to create a user have the same result."
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
|
|
@ -1289,11 +1216,7 @@ paths:
|
|||
get:
|
||||
tags:
|
||||
- UserService
|
||||
description: |-
|
||||
GetUser gets a user by ID or username.
|
||||
Supports both numeric IDs and username strings:
|
||||
- users/{id} (e.g., users/101)
|
||||
- users/{username} (e.g., users/steven)
|
||||
description: "GetUser gets a user by ID or username.\r\n Supports both numeric IDs and username strings:\r\n - users/{id} (e.g., users/101)\r\n - users/{username} (e.g., users/steven)"
|
||||
operationId: UserService_GetUser
|
||||
parameters:
|
||||
- name: user
|
||||
|
|
@ -1304,9 +1227,7 @@ paths:
|
|||
type: string
|
||||
- name: readMask
|
||||
in: query
|
||||
description: |-
|
||||
Optional. The fields to return in the response.
|
||||
If not specified, all fields are returned.
|
||||
description: "Optional. The fields to return in the response.\r\n If not specified, all fields are returned."
|
||||
schema:
|
||||
type: string
|
||||
format: field-mask
|
||||
|
|
@ -1533,35 +1454,23 @@ paths:
|
|||
type: string
|
||||
- name: pageSize
|
||||
in: query
|
||||
description: |-
|
||||
Optional. The maximum number of inboxes to return.
|
||||
The service may return fewer than this value.
|
||||
If unspecified, at most 50 inboxes will be returned.
|
||||
The maximum value is 1000; values above 1000 will be coerced to 1000.
|
||||
description: "Optional. The maximum number of inboxes to return.\r\n The service may return fewer than this value.\r\n If unspecified, at most 50 inboxes will be returned.\r\n The maximum value is 1000; values above 1000 will be coerced to 1000."
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
- name: pageToken
|
||||
in: query
|
||||
description: |-
|
||||
Optional. A page token, received from a previous `ListInboxes` call.
|
||||
Provide this to retrieve the subsequent page.
|
||||
description: "Optional. A page token, received from a previous `ListInboxes` call.\r\n Provide this to retrieve the subsequent page."
|
||||
schema:
|
||||
type: string
|
||||
- name: filter
|
||||
in: query
|
||||
description: |-
|
||||
Optional. Filter to apply to the list results.
|
||||
Example: "status=UNREAD" or "type=MEMO_COMMENT"
|
||||
Supported operators: =, !=
|
||||
Supported fields: status, type, sender, create_time
|
||||
description: "Optional. Filter to apply to the list results.\r\n Example: \"status=UNREAD\" or \"type=MEMO_COMMENT\"\r\n Supported operators: =, !=\r\n Supported fields: status, type, sender, create_time"
|
||||
schema:
|
||||
type: string
|
||||
- name: orderBy
|
||||
in: query
|
||||
description: |-
|
||||
Optional. The order to sort results by.
|
||||
Example: "create_time desc" or "status asc"
|
||||
description: "Optional. The order to sort results by.\r\n Example: \"create_time desc\" or \"status asc\""
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
|
|
@ -1647,19 +1556,13 @@ paths:
|
|||
type: string
|
||||
- name: pageSize
|
||||
in: query
|
||||
description: |-
|
||||
Optional. The maximum number of settings to return.
|
||||
The service may return fewer than this value.
|
||||
If unspecified, at most 50 settings will be returned.
|
||||
The maximum value is 1000; values above 1000 will be coerced to 1000.
|
||||
description: "Optional. The maximum number of settings to return.\r\n The service may return fewer than this value.\r\n If unspecified, at most 50 settings will be returned.\r\n The maximum value is 1000; values above 1000 will be coerced to 1000."
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
- name: pageToken
|
||||
in: query
|
||||
description: |-
|
||||
Optional. A page token, received from a previous `ListUserSettings` call.
|
||||
Provide this to retrieve the subsequent page.
|
||||
description: "Optional. A page token, received from a previous `ListUserSettings` call.\r\n Provide this to retrieve the subsequent page."
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
|
|
@ -2214,15 +2117,11 @@ components:
|
|||
name:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: |-
|
||||
The name of the activity.
|
||||
Format: activities/{id}
|
||||
description: "The name of the activity.\r\n Format: activities/{id}"
|
||||
creator:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: |-
|
||||
The name of the creator.
|
||||
Format: users/{user}
|
||||
description: "The name of the creator.\r\n Format: users/{user}"
|
||||
type:
|
||||
readOnly: true
|
||||
enum:
|
||||
|
|
@ -2257,14 +2156,10 @@ components:
|
|||
properties:
|
||||
memo:
|
||||
type: string
|
||||
description: |-
|
||||
The memo name of comment.
|
||||
Format: memos/{memo}
|
||||
description: "The memo name of comment.\r\n Format: memos/{memo}"
|
||||
relatedMemo:
|
||||
type: string
|
||||
description: |-
|
||||
The name of related memo.
|
||||
Format: memos/{memo}
|
||||
description: "The name of related memo.\r\n Format: memos/{memo}"
|
||||
description: ActivityMemoCommentPayload represents the payload of a memo comment activity.
|
||||
ActivityPayload:
|
||||
type: object
|
||||
|
|
@ -2281,9 +2176,7 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The name of the attachment.
|
||||
Format: attachments/{attachment}
|
||||
description: "The name of the attachment.\r\n Format: attachments/{attachment}"
|
||||
createTime:
|
||||
readOnly: true
|
||||
type: string
|
||||
|
|
@ -2309,9 +2202,7 @@ components:
|
|||
description: Output only. The size of the attachment in bytes.
|
||||
memo:
|
||||
type: string
|
||||
description: |-
|
||||
Optional. The related memo. Refer to `Memo.name`.
|
||||
Format: memos/{memo}
|
||||
description: "Optional. The related memo. Refer to `Memo.name`.\r\n Format: memos/{memo}"
|
||||
AutoLinkNode:
|
||||
type: object
|
||||
properties:
|
||||
|
|
@ -2373,14 +2264,10 @@ components:
|
|||
properties:
|
||||
username:
|
||||
type: string
|
||||
description: |-
|
||||
The username to sign in with.
|
||||
Required field for password-based authentication.
|
||||
description: "The username to sign in with.\r\n Required field for password-based authentication."
|
||||
password:
|
||||
type: string
|
||||
description: |-
|
||||
The password to sign in with.
|
||||
Required field for password-based authentication.
|
||||
description: "The password to sign in with.\r\n Required field for password-based authentication."
|
||||
description: Nested message for password-based authentication credentials.
|
||||
CreateSessionRequest_SSOCredentials:
|
||||
required:
|
||||
|
|
@ -2391,20 +2278,14 @@ components:
|
|||
properties:
|
||||
idpId:
|
||||
type: integer
|
||||
description: |-
|
||||
The ID of the SSO provider.
|
||||
Required field to identify the SSO provider.
|
||||
description: "The ID of the SSO provider.\r\n Required field to identify the SSO provider."
|
||||
format: int32
|
||||
code:
|
||||
type: string
|
||||
description: |-
|
||||
The authorization code from the SSO provider.
|
||||
Required field for completing the SSO flow.
|
||||
description: "The authorization code from the SSO provider.\r\n Required field for completing the SSO flow."
|
||||
redirectUri:
|
||||
type: string
|
||||
description: |-
|
||||
The redirect URI used in the SSO flow.
|
||||
Required field for security validation.
|
||||
description: "The redirect URI used in the SSO flow.\r\n Required field for security validation."
|
||||
description: Nested message for SSO authentication credentials.
|
||||
CreateSessionResponse:
|
||||
type: object
|
||||
|
|
@ -2415,9 +2296,7 @@ components:
|
|||
description: The authenticated user information.
|
||||
lastAccessedAt:
|
||||
type: string
|
||||
description: |-
|
||||
Last time the session was accessed.
|
||||
Used for sliding expiration calculation (last_accessed_time + 2 weeks).
|
||||
description: "Last time the session was accessed.\r\n Used for sliding expiration calculation (last_accessed_time + 2 weeks)."
|
||||
format: date-time
|
||||
DeleteMemoTagRequest:
|
||||
required:
|
||||
|
|
@ -2427,9 +2306,7 @@ components:
|
|||
properties:
|
||||
parent:
|
||||
type: string
|
||||
description: |-
|
||||
Required. The parent, who owns the tags.
|
||||
Format: memos/{memo}. Use "memos/-" to delete all tags.
|
||||
description: "Required. The parent, who owns the tags.\r\n Format: memos/{memo}. Use \"memos/-\" to delete all tags."
|
||||
tag:
|
||||
type: string
|
||||
description: Required. The tag name to delete.
|
||||
|
|
@ -2480,9 +2357,7 @@ components:
|
|||
$ref: '#/components/schemas/User'
|
||||
lastAccessedAt:
|
||||
type: string
|
||||
description: |-
|
||||
Last time the session was accessed.
|
||||
Used for sliding expiration calculation (last_accessed_time + 2 weeks).
|
||||
description: "Last time the session was accessed.\r\n Used for sliding expiration calculation (last_accessed_time + 2 weeks)."
|
||||
format: date-time
|
||||
GoogleProtobufAny:
|
||||
type: object
|
||||
|
|
@ -2536,9 +2411,7 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the identity provider.
|
||||
Format: identityProviders/{idp}
|
||||
description: "The resource name of the identity provider.\r\n Format: identityProviders/{idp}"
|
||||
type:
|
||||
enum:
|
||||
- TYPE_UNSPECIFIED
|
||||
|
|
@ -2573,21 +2446,15 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the inbox.
|
||||
Format: inboxes/{inbox}
|
||||
description: "The resource name of the inbox.\r\n Format: inboxes/{inbox}"
|
||||
sender:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: |-
|
||||
The sender of the inbox notification.
|
||||
Format: users/{user}
|
||||
description: "The sender of the inbox notification.\r\n Format: users/{user}"
|
||||
receiver:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: |-
|
||||
The receiver of the inbox notification.
|
||||
Format: users/{user}
|
||||
description: "The receiver of the inbox notification.\r\n Format: users/{user}"
|
||||
status:
|
||||
enum:
|
||||
- STATUS_UNSPECIFIED
|
||||
|
|
@ -2657,10 +2524,7 @@ components:
|
|||
description: The activities.
|
||||
nextPageToken:
|
||||
type: string
|
||||
description: |-
|
||||
A token to retrieve the next page of results.
|
||||
Pass this value in the page_token field in the subsequent call to `ListActivities`
|
||||
method to retrieve the next page of results.
|
||||
description: "A token to retrieve the next page of results.\r\n Pass this value in the page_token field in the subsequent call to `ListActivities`\r\n method to retrieve the next page of results."
|
||||
ListAllUserStatsResponse:
|
||||
type: object
|
||||
properties:
|
||||
|
|
@ -2679,9 +2543,7 @@ components:
|
|||
description: The list of attachments.
|
||||
nextPageToken:
|
||||
type: string
|
||||
description: |-
|
||||
A token that can be sent as `page_token` to retrieve the next page.
|
||||
If this field is omitted, there are no subsequent pages.
|
||||
description: "A token that can be sent as `page_token` to retrieve the next page.\r\n If this field is omitted, there are no subsequent pages."
|
||||
totalSize:
|
||||
type: integer
|
||||
description: The total count of attachments (may be approximate).
|
||||
|
|
@ -2704,9 +2566,7 @@ components:
|
|||
description: The list of inboxes.
|
||||
nextPageToken:
|
||||
type: string
|
||||
description: |-
|
||||
A token that can be sent as `page_token` to retrieve the next page.
|
||||
If this field is omitted, there are no subsequent pages.
|
||||
description: "A token that can be sent as `page_token` to retrieve the next page.\r\n If this field is omitted, there are no subsequent pages."
|
||||
totalSize:
|
||||
type: integer
|
||||
description: The total count of inboxes (may be approximate).
|
||||
|
|
@ -2781,9 +2641,7 @@ components:
|
|||
description: The list of memos.
|
||||
nextPageToken:
|
||||
type: string
|
||||
description: |-
|
||||
A token that can be sent as `page_token` to retrieve the next page.
|
||||
If this field is omitted, there are no subsequent pages.
|
||||
description: "A token that can be sent as `page_token` to retrieve the next page.\r\n If this field is omitted, there are no subsequent pages."
|
||||
totalSize:
|
||||
type: integer
|
||||
description: The total count of memos (may be approximate).
|
||||
|
|
@ -2847,9 +2705,7 @@ components:
|
|||
description: The list of user settings.
|
||||
nextPageToken:
|
||||
type: string
|
||||
description: |-
|
||||
A token that can be sent as `page_token` to retrieve the next page.
|
||||
If this field is omitted, there are no subsequent pages.
|
||||
description: "A token that can be sent as `page_token` to retrieve the next page.\r\n If this field is omitted, there are no subsequent pages."
|
||||
totalSize:
|
||||
type: integer
|
||||
description: The total count of settings (may be approximate).
|
||||
|
|
@ -2873,9 +2729,7 @@ components:
|
|||
description: The list of users.
|
||||
nextPageToken:
|
||||
type: string
|
||||
description: |-
|
||||
A token that can be sent as `page_token` to retrieve the next page.
|
||||
If this field is omitted, there are no subsequent pages.
|
||||
description: "A token that can be sent as `page_token` to retrieve the next page.\r\n If this field is omitted, there are no subsequent pages."
|
||||
totalSize:
|
||||
type: integer
|
||||
description: The total count of users (may be approximate).
|
||||
|
|
@ -2913,9 +2767,7 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the memo.
|
||||
Format: memos/{memo}, memo is the user defined id or uuid.
|
||||
description: "The resource name of the memo.\r\n Format: memos/{memo}, memo is the user defined id or uuid."
|
||||
state:
|
||||
enum:
|
||||
- STATE_UNSPECIFIED
|
||||
|
|
@ -2927,9 +2779,7 @@ components:
|
|||
creator:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: |-
|
||||
The name of the creator.
|
||||
Format: users/{user}
|
||||
description: "The name of the creator.\r\n Format: users/{user}"
|
||||
createTime:
|
||||
readOnly: true
|
||||
type: string
|
||||
|
|
@ -2995,9 +2845,7 @@ components:
|
|||
parent:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: |-
|
||||
Output only. The name of the parent memo.
|
||||
Format: memos/{memo}
|
||||
description: "Output only. The name of the parent memo.\r\n Format: memos/{memo}"
|
||||
snippet:
|
||||
readOnly: true
|
||||
type: string
|
||||
|
|
@ -3035,9 +2883,7 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the memo.
|
||||
Format: memos/{memo}
|
||||
description: "The resource name of the memo.\r\n Format: memos/{memo}"
|
||||
snippet:
|
||||
readOnly: true
|
||||
type: string
|
||||
|
|
@ -3223,21 +3069,14 @@ components:
|
|||
name:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the reaction.
|
||||
Format: reactions/{reaction}
|
||||
description: "The resource name of the reaction.\r\n Format: reactions/{reaction}"
|
||||
creator:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the creator.
|
||||
Format: users/{user}
|
||||
description: "The resource name of the creator.\r\n Format: users/{user}"
|
||||
contentId:
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the content.
|
||||
For memo reactions, this should be the memo's resource name.
|
||||
Format: memos/{memo}
|
||||
description: "The resource name of the content.\r\n For memo reactions, this should be the memo's resource name.\r\n Format: memos/{memo}"
|
||||
reactionType:
|
||||
type: string
|
||||
description: "Required. The type of reaction (e.g., \"\U0001F44D\", \"❤️\", \"\U0001F604\")."
|
||||
|
|
@ -3264,9 +3103,7 @@ components:
|
|||
properties:
|
||||
parent:
|
||||
type: string
|
||||
description: |-
|
||||
Required. The parent, who owns the tags.
|
||||
Format: memos/{memo}. Use "memos/-" to rename all tags.
|
||||
description: "Required. The parent, who owns the tags.\r\n Format: memos/{memo}. Use \"memos/-\" to rename all tags."
|
||||
oldTag:
|
||||
type: string
|
||||
description: Required. The old tag name to rename.
|
||||
|
|
@ -3297,9 +3134,7 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
Required. The resource name of the memo.
|
||||
Format: memos/{memo}
|
||||
description: "Required. The resource name of the memo.\r\n Format: memos/{memo}"
|
||||
attachments:
|
||||
type: array
|
||||
items:
|
||||
|
|
@ -3313,9 +3148,7 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
Required. The resource name of the memo.
|
||||
Format: memos/{memo}
|
||||
description: "Required. The resource name of the memo.\r\n Format: memos/{memo}"
|
||||
relations:
|
||||
type: array
|
||||
items:
|
||||
|
|
@ -3328,9 +3161,7 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the shortcut.
|
||||
Format: users/{user}/shortcuts/{shortcut}
|
||||
description: "The resource name of the shortcut.\r\n Format: users/{user}/shortcuts/{shortcut}"
|
||||
title:
|
||||
type: string
|
||||
description: The title of the shortcut.
|
||||
|
|
@ -3373,9 +3204,7 @@ components:
|
|||
type: string
|
||||
usePathStyle:
|
||||
type: boolean
|
||||
description: |-
|
||||
S3 configuration for cloud storage backend.
|
||||
Reference: https://developers.cloudflare.com/r2/examples/aws/aws-sdk-go/
|
||||
description: "S3 configuration for cloud storage backend.\r\n Reference: https://developers.cloudflare.com/r2/examples/aws/aws-sdk-go/"
|
||||
StrikethroughNode:
|
||||
type: object
|
||||
properties:
|
||||
|
|
@ -3473,9 +3302,7 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
Required. The resource name of the memo.
|
||||
Format: memos/{memo}
|
||||
description: "Required. The resource name of the memo.\r\n Format: memos/{memo}"
|
||||
reaction:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Reaction'
|
||||
|
|
@ -3489,9 +3316,7 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the user.
|
||||
Format: users/{user}
|
||||
description: "The resource name of the user.\r\n Format: users/{user}"
|
||||
role:
|
||||
enum:
|
||||
- ROLE_UNSPECIFIED
|
||||
|
|
@ -3543,9 +3368,7 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the access token.
|
||||
Format: users/{user}/accessTokens/{access_token}
|
||||
description: "The resource name of the access token.\r\n Format: users/{user}/accessTokens/{access_token}"
|
||||
accessToken:
|
||||
readOnly: true
|
||||
type: string
|
||||
|
|
@ -3568,9 +3391,7 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the session.
|
||||
Format: users/{user}/sessions/{session}
|
||||
description: "The resource name of the session.\r\n Format: users/{user}/sessions/{session}"
|
||||
sessionId:
|
||||
readOnly: true
|
||||
type: string
|
||||
|
|
@ -3583,9 +3404,7 @@ components:
|
|||
lastAccessedTime:
|
||||
readOnly: true
|
||||
type: string
|
||||
description: |-
|
||||
The timestamp when the session was last accessed.
|
||||
Used for sliding expiration calculation (last_accessed_time + 2 weeks).
|
||||
description: "The timestamp when the session was last accessed.\r\n Used for sliding expiration calculation (last_accessed_time + 2 weeks)."
|
||||
format: date-time
|
||||
clientInfo:
|
||||
readOnly: true
|
||||
|
|
@ -3615,10 +3434,7 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The name of the user setting.
|
||||
Format: users/{user}/settings/{setting}, {setting} is the key for the setting.
|
||||
For example, "users/123/settings/GENERAL" for general settings.
|
||||
description: "The name of the user setting.\r\n Format: users/{user}/settings/{setting}, {setting} is the key for the setting.\r\n For example, \"users/123/settings/GENERAL\" for general settings."
|
||||
generalSetting:
|
||||
$ref: '#/components/schemas/UserSetting_GeneralSetting'
|
||||
sessionsSetting:
|
||||
|
|
@ -3648,10 +3464,7 @@ components:
|
|||
description: The default visibility of the memo.
|
||||
theme:
|
||||
type: string
|
||||
description: |-
|
||||
The preferred theme of the user.
|
||||
This references a CSS file in the web/public/themes/ directory.
|
||||
If not set, the default theme will be used.
|
||||
description: "The preferred theme of the user.\r\n This references a CSS file in the web/public/themes/ directory.\r\n If not set, the default theme will be used."
|
||||
description: General user settings configuration.
|
||||
UserSetting_SessionsSetting:
|
||||
type: object
|
||||
|
|
@ -3676,9 +3489,7 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The resource name of the user whose stats these are.
|
||||
Format: users/{user}
|
||||
description: "The resource name of the user whose stats these are.\r\n Format: users/{user}"
|
||||
memoDisplayTimestamps:
|
||||
type: array
|
||||
items:
|
||||
|
|
@ -3726,9 +3537,7 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The name of the webhook.
|
||||
Format: users/{user}/webhooks/{webhook}
|
||||
description: "The name of the webhook.\r\n Format: users/{user}/webhooks/{webhook}"
|
||||
url:
|
||||
type: string
|
||||
description: The URL to send the webhook to.
|
||||
|
|
@ -3751,9 +3560,7 @@ components:
|
|||
properties:
|
||||
owner:
|
||||
type: string
|
||||
description: |-
|
||||
The name of instance owner.
|
||||
Format: users/{user}
|
||||
description: "The name of instance owner.\r\n Format: users/{user}"
|
||||
version:
|
||||
type: string
|
||||
description: Version is the current version of instance.
|
||||
|
|
@ -3769,9 +3576,7 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: |-
|
||||
The name of the workspace setting.
|
||||
Format: workspace/settings/{setting}
|
||||
description: "The name of the workspace setting.\r\n Format: workspace/settings/{setting}"
|
||||
generalSetting:
|
||||
$ref: '#/components/schemas/WorkspaceSetting_GeneralSetting'
|
||||
storageSetting:
|
||||
|
|
@ -3784,9 +3589,7 @@ components:
|
|||
properties:
|
||||
theme:
|
||||
type: string
|
||||
description: |-
|
||||
theme is the name of the selected theme.
|
||||
This references a CSS file in the web/public/themes/ directory.
|
||||
description: "theme is the name of the selected theme.\r\n This references a CSS file in the web/public/themes/ directory."
|
||||
disallowUserRegistration:
|
||||
type: boolean
|
||||
description: disallow_user_registration disallows user registration.
|
||||
|
|
@ -3805,10 +3608,7 @@ components:
|
|||
description: custom_profile is the custom profile.
|
||||
weekStartDayOffset:
|
||||
type: integer
|
||||
description: |-
|
||||
week_start_day_offset is the week start day offset from Sunday.
|
||||
0: Sunday, 1: Monday, 2: Tuesday, 3: Wednesday, 4: Thursday, 5: Friday, 6: Saturday
|
||||
Default is Sunday.
|
||||
description: "week_start_day_offset is the week start day offset from Sunday.\r\n 0: Sunday, 1: Monday, 2: Tuesday, 3: Wednesday, 4: Thursday, 5: Friday, 6: Saturday\r\n Default is Sunday."
|
||||
format: int32
|
||||
disallowChangeUsername:
|
||||
type: boolean
|
||||
|
|
@ -3867,9 +3667,7 @@ components:
|
|||
format: enum
|
||||
filepathTemplate:
|
||||
type: string
|
||||
description: |-
|
||||
The template of file path.
|
||||
e.g. assets/{timestamp}_{filename}
|
||||
description: "The template of file path.\r\n e.g. assets/{timestamp}_{filename}"
|
||||
uploadSizeLimitMb:
|
||||
type: string
|
||||
description: The max upload size in megabytes.
|
||||
|
|
@ -3877,6 +3675,22 @@ components:
|
|||
allOf:
|
||||
- $ref: '#/components/schemas/StorageSetting_S3Config'
|
||||
description: The S3 config.
|
||||
thumbnailMaxSize:
|
||||
type: integer
|
||||
description: The maximum size in pixels for the largest dimension of thumbnail images.
|
||||
format: int32
|
||||
jpegQuality:
|
||||
type: integer
|
||||
description: The JPEG quality (0-100) used when downscaling uploaded images.
|
||||
format: int32
|
||||
thumbnailJpegQuality:
|
||||
type: integer
|
||||
description: The JPEG quality (0-100) used when generating thumbnails.
|
||||
format: int32
|
||||
imageMaxSize:
|
||||
type: integer
|
||||
description: "The maximum size in pixels for the largest dimension when storing images.\r\n Images larger than this will be downscaled before storage.\r\n Set to 0 to disable downscaling."
|
||||
format: int32
|
||||
description: Storage configuration settings for workspace attachments.
|
||||
tags:
|
||||
- name: ActivityService
|
||||
|
|
|
|||
|
|
@ -509,7 +509,17 @@ type WorkspaceStorageSetting struct {
|
|||
// The max upload size in megabytes.
|
||||
UploadSizeLimitMb int64 `protobuf:"varint,3,opt,name=upload_size_limit_mb,json=uploadSizeLimitMb,proto3" json:"upload_size_limit_mb,omitempty"`
|
||||
// The S3 config.
|
||||
S3Config *StorageS3Config `protobuf:"bytes,4,opt,name=s3_config,json=s3Config,proto3" json:"s3_config,omitempty"`
|
||||
S3Config *StorageS3Config `protobuf:"bytes,4,opt,name=s3_config,json=s3Config,proto3" json:"s3_config,omitempty"`
|
||||
// The maximum size in pixels for the largest dimension of thumbnail images.
|
||||
ThumbnailMaxSize int32 `protobuf:"varint,5,opt,name=thumbnail_max_size,json=thumbnailMaxSize,proto3" json:"thumbnail_max_size,omitempty"`
|
||||
// The JPEG quality (0-100) used when downscaling uploaded images.
|
||||
JpegQuality int32 `protobuf:"varint,6,opt,name=jpeg_quality,json=jpegQuality,proto3" json:"jpeg_quality,omitempty"`
|
||||
// The JPEG quality (0-100) used when generating thumbnails.
|
||||
ThumbnailJpegQuality int32 `protobuf:"varint,7,opt,name=thumbnail_jpeg_quality,json=thumbnailJpegQuality,proto3" json:"thumbnail_jpeg_quality,omitempty"`
|
||||
// The maximum size in pixels for the largest dimension when storing images.
|
||||
// Images larger than this will be downscaled before storage.
|
||||
// Set to 0 to disable downscaling.
|
||||
ImageMaxSize int32 `protobuf:"varint,8,opt,name=image_max_size,json=imageMaxSize,proto3" json:"image_max_size,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
|
@ -572,6 +582,34 @@ func (x *WorkspaceStorageSetting) GetS3Config() *StorageS3Config {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (x *WorkspaceStorageSetting) GetThumbnailMaxSize() int32 {
|
||||
if x != nil {
|
||||
return x.ThumbnailMaxSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WorkspaceStorageSetting) GetJpegQuality() int32 {
|
||||
if x != nil {
|
||||
return x.JpegQuality
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WorkspaceStorageSetting) GetThumbnailJpegQuality() int32 {
|
||||
if x != nil {
|
||||
return x.ThumbnailJpegQuality
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *WorkspaceStorageSetting) GetImageMaxSize() int32 {
|
||||
if x != nil {
|
||||
return x.ImageMaxSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Reference: https://developers.cloudflare.com/r2/examples/aws/aws-sdk-go/
|
||||
type StorageS3Config struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
|
|
@ -804,12 +842,16 @@ const file_store_workspace_setting_proto_rawDesc = "" +
|
|||
"\x05title\x18\x01 \x01(\tR\x05title\x12 \n" +
|
||||
"\vdescription\x18\x02 \x01(\tR\vdescription\x12\x19\n" +
|
||||
"\blogo_url\x18\x03 \x01(\tR\alogoUrl\x12\x16\n" +
|
||||
"\x06locale\x18\x04 \x01(\tR\x06locale\"\xd5\x02\n" +
|
||||
"\x06locale\x18\x04 \x01(\tR\x06locale\"\x82\x04\n" +
|
||||
"\x17WorkspaceStorageSetting\x12S\n" +
|
||||
"\fstorage_type\x18\x01 \x01(\x0e20.memos.store.WorkspaceStorageSetting.StorageTypeR\vstorageType\x12+\n" +
|
||||
"\x11filepath_template\x18\x02 \x01(\tR\x10filepathTemplate\x12/\n" +
|
||||
"\x14upload_size_limit_mb\x18\x03 \x01(\x03R\x11uploadSizeLimitMb\x129\n" +
|
||||
"\ts3_config\x18\x04 \x01(\v2\x1c.memos.store.StorageS3ConfigR\bs3Config\"L\n" +
|
||||
"\ts3_config\x18\x04 \x01(\v2\x1c.memos.store.StorageS3ConfigR\bs3Config\x12,\n" +
|
||||
"\x12thumbnail_max_size\x18\x05 \x01(\x05R\x10thumbnailMaxSize\x12!\n" +
|
||||
"\fjpeg_quality\x18\x06 \x01(\x05R\vjpegQuality\x124\n" +
|
||||
"\x16thumbnail_jpeg_quality\x18\a \x01(\x05R\x14thumbnailJpegQuality\x12$\n" +
|
||||
"\x0eimage_max_size\x18\b \x01(\x05R\fimageMaxSize\"L\n" +
|
||||
"\vStorageType\x12\x1c\n" +
|
||||
"\x18STORAGE_TYPE_UNSPECIFIED\x10\x00\x12\f\n" +
|
||||
"\bDATABASE\x10\x01\x12\t\n" +
|
||||
|
|
|
|||
|
|
@ -83,6 +83,16 @@ message WorkspaceStorageSetting {
|
|||
int64 upload_size_limit_mb = 3;
|
||||
// The S3 config.
|
||||
StorageS3Config s3_config = 4;
|
||||
// The maximum size in pixels for the largest dimension of thumbnail images.
|
||||
int32 thumbnail_max_size = 5;
|
||||
// The JPEG quality (0-100) used when downscaling uploaded images.
|
||||
int32 jpeg_quality = 6;
|
||||
// The JPEG quality (0-100) used when generating thumbnails.
|
||||
int32 thumbnail_jpeg_quality = 7;
|
||||
// The maximum size in pixels for the largest dimension when storing images.
|
||||
// Images larger than this will be downscaled before storage.
|
||||
// Set to 0 to disable downscaling.
|
||||
int32 image_max_size = 8;
|
||||
}
|
||||
|
||||
// Reference: https://developers.cloudflare.com/r2/examples/aws/aws-sdk-go/
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"image"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
|
@ -96,6 +97,20 @@ func (s *APIV1Service) CreateAttachment(ctx context.Context, request *v1pb.Creat
|
|||
create.Size = int64(size)
|
||||
create.Blob = request.Attachment.Content
|
||||
|
||||
// Downscale images before storing them if they are larger than the configured max dimension
|
||||
// ImageMaxSize of 0 means no downscaling should be performed
|
||||
if util.HasPrefixes(create.Type, SupportedThumbnailMimeTypes...) && workspaceStorageSetting.ImageMaxSize > 0 {
|
||||
downscaledBlob, err := downscaleImage(create.Blob, int(workspaceStorageSetting.ImageMaxSize), int(workspaceStorageSetting.JpegQuality))
|
||||
if err != nil {
|
||||
// Log the error but continue with the original image if downscaling fails
|
||||
slog.Warn("failed to downscale image attachment", slog.Any("error", err), slog.String("filename", create.Filename))
|
||||
} else {
|
||||
// Update the blob and size with the downscaled version
|
||||
create.Blob = downscaledBlob
|
||||
create.Size = int64(len(downscaledBlob))
|
||||
}
|
||||
}
|
||||
|
||||
if err := SaveAttachmentBlob(ctx, s.Profile, s.Store, create); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to save attachment blob: %v", err)
|
||||
}
|
||||
|
|
@ -523,13 +538,73 @@ func (s *APIV1Service) GetAttachmentBlob(attachment *store.Attachment) ([]byte,
|
|||
return attachment.Blob, nil
|
||||
}
|
||||
|
||||
const (
|
||||
// thumbnailMaxSize is the maximum size in pixels for the largest dimension of the thumbnail image.
|
||||
thumbnailMaxSize = 600
|
||||
)
|
||||
func downscaleImage(imageBlob []byte, maxDimension int, quality int) ([]byte, error) {
|
||||
// Detect the image format before decoding
|
||||
reader := bytes.NewReader(imageBlob)
|
||||
_, formatName, err := image.DecodeConfig(reader)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to detect image format")
|
||||
}
|
||||
|
||||
// Reset reader position for actual decoding
|
||||
if _, err := reader.Seek(0, 0); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to reset reader position")
|
||||
}
|
||||
|
||||
// Decode the image with auto-orientation support
|
||||
img, err := imaging.Decode(reader, imaging.AutoOrientation(true))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode image")
|
||||
}
|
||||
|
||||
// Get original dimensions
|
||||
width := img.Bounds().Dx()
|
||||
height := img.Bounds().Dy()
|
||||
var targetWidth, targetHeight int
|
||||
|
||||
// Only resize if the image is larger than maxDimension
|
||||
if max(width, height) > maxDimension {
|
||||
if width >= height {
|
||||
// Landscape or square - constrain width, maintain aspect ratio for height
|
||||
targetWidth = maxDimension
|
||||
targetHeight = 0
|
||||
} else {
|
||||
// Portrait - constrain height, maintain aspect ratio for width
|
||||
targetWidth = 0
|
||||
targetHeight = maxDimension
|
||||
}
|
||||
} else {
|
||||
// Do not modify small images
|
||||
return imageBlob, nil
|
||||
}
|
||||
|
||||
// Resize the image to the calculated dimensions
|
||||
resizedImage := imaging.Resize(img, targetWidth, targetHeight, imaging.Lanczos)
|
||||
|
||||
// Encode the image based on the original format
|
||||
var buf bytes.Buffer
|
||||
if formatName == "png" {
|
||||
// Preserve PNG format for PNG images
|
||||
if err := imaging.Encode(&buf, resizedImage, imaging.PNG); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to encode PNG image")
|
||||
}
|
||||
} else {
|
||||
// Encode as JPEG for all other formats
|
||||
if err := imaging.Encode(&buf, resizedImage, imaging.JPEG, imaging.JPEGQuality(quality)); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to encode JPEG image")
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// getOrGenerateThumbnail returns the thumbnail image of the attachment.
|
||||
func (s *APIV1Service) getOrGenerateThumbnail(attachment *store.Attachment) ([]byte, error) {
|
||||
workspaceStorageSetting, err := s.Store.GetWorkspaceStorageSetting(context.Background())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get workspace storage setting")
|
||||
}
|
||||
|
||||
thumbnailCacheFolder := filepath.Join(s.Profile.Data, ThumbnailCacheFolder)
|
||||
if err := os.MkdirAll(thumbnailCacheFolder, os.ModePerm); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create thumbnail cache folder")
|
||||
|
|
@ -545,39 +620,19 @@ func (s *APIV1Service) getOrGenerateThumbnail(attachment *store.Attachment) ([]b
|
|||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get attachment blob")
|
||||
}
|
||||
img, err := imaging.Decode(bytes.NewReader(blob), imaging.AutoOrientation(true))
|
||||
|
||||
// Downscale the image
|
||||
thumbnailBlob, err := downscaleImage(blob, int(workspaceStorageSetting.ThumbnailMaxSize), int(workspaceStorageSetting.ThumbnailJpegQuality))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode thumbnail image")
|
||||
return nil, errors.Wrap(err, "failed to downscale image")
|
||||
}
|
||||
|
||||
// The largest dimension is set to thumbnailMaxSize and the smaller dimension is scaled proportionally.
|
||||
// Small images are not enlarged.
|
||||
width := img.Bounds().Dx()
|
||||
height := img.Bounds().Dy()
|
||||
var thumbnailWidth, thumbnailHeight int
|
||||
|
||||
// Only resize if the image is larger than thumbnailMaxSize
|
||||
if max(width, height) > thumbnailMaxSize {
|
||||
if width >= height {
|
||||
// Landscape or square - constrain width, maintain aspect ratio for height
|
||||
thumbnailWidth = thumbnailMaxSize
|
||||
thumbnailHeight = 0
|
||||
} else {
|
||||
// Portrait - constrain height, maintain aspect ratio for width
|
||||
thumbnailWidth = 0
|
||||
thumbnailHeight = thumbnailMaxSize
|
||||
}
|
||||
} else {
|
||||
// Keep original dimensions for small images
|
||||
thumbnailWidth = width
|
||||
thumbnailHeight = height
|
||||
}
|
||||
|
||||
// Resize the image to the calculated dimensions.
|
||||
thumbnailImage := imaging.Resize(img, thumbnailWidth, thumbnailHeight, imaging.Lanczos)
|
||||
if err := imaging.Save(thumbnailImage, filePath); err != nil {
|
||||
// Save the thumbnail to disk
|
||||
if err := os.WriteFile(filePath, thumbnailBlob, 0644); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to save thumbnail file")
|
||||
}
|
||||
|
||||
return thumbnailBlob, nil
|
||||
}
|
||||
|
||||
thumbnailFile, err := os.Open(filePath)
|
||||
|
|
|
|||
|
|
@ -211,9 +211,13 @@ func convertWorkspaceStorageSettingFromStore(settingpb *storepb.WorkspaceStorage
|
|||
return nil
|
||||
}
|
||||
setting := &v1pb.WorkspaceSetting_StorageSetting{
|
||||
StorageType: v1pb.WorkspaceSetting_StorageSetting_StorageType(settingpb.StorageType),
|
||||
FilepathTemplate: settingpb.FilepathTemplate,
|
||||
UploadSizeLimitMb: settingpb.UploadSizeLimitMb,
|
||||
StorageType: v1pb.WorkspaceSetting_StorageSetting_StorageType(settingpb.StorageType),
|
||||
FilepathTemplate: settingpb.FilepathTemplate,
|
||||
UploadSizeLimitMb: settingpb.UploadSizeLimitMb,
|
||||
ThumbnailMaxSize: settingpb.ThumbnailMaxSize,
|
||||
JpegQuality: settingpb.JpegQuality,
|
||||
ThumbnailJpegQuality: settingpb.ThumbnailJpegQuality,
|
||||
ImageMaxSize: settingpb.ImageMaxSize,
|
||||
}
|
||||
if settingpb.S3Config != nil {
|
||||
setting.S3Config = &v1pb.WorkspaceSetting_StorageSetting_S3Config{
|
||||
|
|
@ -233,9 +237,13 @@ func convertWorkspaceStorageSettingToStore(setting *v1pb.WorkspaceSetting_Storag
|
|||
return nil
|
||||
}
|
||||
settingpb := &storepb.WorkspaceStorageSetting{
|
||||
StorageType: storepb.WorkspaceStorageSetting_StorageType(setting.StorageType),
|
||||
FilepathTemplate: setting.FilepathTemplate,
|
||||
UploadSizeLimitMb: setting.UploadSizeLimitMb,
|
||||
StorageType: storepb.WorkspaceStorageSetting_StorageType(setting.StorageType),
|
||||
FilepathTemplate: setting.FilepathTemplate,
|
||||
UploadSizeLimitMb: setting.UploadSizeLimitMb,
|
||||
ThumbnailMaxSize: setting.ThumbnailMaxSize,
|
||||
JpegQuality: setting.JpegQuality,
|
||||
ThumbnailJpegQuality: setting.ThumbnailJpegQuality,
|
||||
ImageMaxSize: setting.ImageMaxSize,
|
||||
}
|
||||
if setting.S3Config != nil {
|
||||
settingpb.S3Config = &storepb.StorageS3Config{
|
||||
|
|
|
|||
|
|
@ -175,9 +175,12 @@ func (s *Store) GetWorkspaceMemoRelatedSetting(ctx context.Context) (*storepb.Wo
|
|||
}
|
||||
|
||||
const (
|
||||
defaultWorkspaceStorageType = storepb.WorkspaceStorageSetting_DATABASE
|
||||
defaultWorkspaceUploadSizeLimitMb = 30
|
||||
defaultWorkspaceFilepathTemplate = "assets/{timestamp}_{filename}"
|
||||
defaultWorkspaceStorageType = storepb.WorkspaceStorageSetting_DATABASE
|
||||
defaultWorkspaceUploadSizeLimitMb = 30
|
||||
defaultWorkspaceFilepathTemplate = "assets/{timestamp}_{filename}"
|
||||
defaultThumbnailMaxSize = 600
|
||||
defaultJPEGQuality = 85
|
||||
defaultThumbnailJPEGQuality = 75
|
||||
)
|
||||
|
||||
func (s *Store) GetWorkspaceStorageSetting(ctx context.Context) (*storepb.WorkspaceStorageSetting, error) {
|
||||
|
|
@ -201,6 +204,16 @@ func (s *Store) GetWorkspaceStorageSetting(ctx context.Context) (*storepb.Worksp
|
|||
if workspaceStorageSetting.FilepathTemplate == "" {
|
||||
workspaceStorageSetting.FilepathTemplate = defaultWorkspaceFilepathTemplate
|
||||
}
|
||||
if workspaceStorageSetting.ThumbnailMaxSize == 0 {
|
||||
workspaceStorageSetting.ThumbnailMaxSize = defaultThumbnailMaxSize
|
||||
}
|
||||
if workspaceStorageSetting.JpegQuality == 0 {
|
||||
workspaceStorageSetting.JpegQuality = defaultJPEGQuality
|
||||
}
|
||||
if workspaceStorageSetting.ThumbnailJpegQuality == 0 {
|
||||
workspaceStorageSetting.ThumbnailJpegQuality = defaultThumbnailJPEGQuality
|
||||
}
|
||||
// Note: ImageMaxSize of 0 is a valid value meaning "no downscaling", so we don't apply a default
|
||||
s.workspaceSettingCache.Set(ctx, storepb.WorkspaceSettingKey_STORAGE.String(), &storepb.WorkspaceSetting{
|
||||
Key: storepb.WorkspaceSettingKey_STORAGE,
|
||||
Value: &storepb.WorkspaceSetting_StorageSetting{StorageSetting: workspaceStorageSetting},
|
||||
|
|
|
|||
|
|
@ -39,6 +39,19 @@ const StorageSection = observer(() => {
|
|||
if (workspaceStorageSetting.uploadSizeLimitMb <= 0) {
|
||||
return false;
|
||||
}
|
||||
// imageMaxSize can be 0 (meaning no downscaling) or positive
|
||||
if (workspaceStorageSetting.imageMaxSize < 0) {
|
||||
return false;
|
||||
}
|
||||
if (workspaceStorageSetting.jpegQuality < 1 || workspaceStorageSetting.jpegQuality > 100) {
|
||||
return false;
|
||||
}
|
||||
if (workspaceStorageSetting.thumbnailMaxSize <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (workspaceStorageSetting.thumbnailJpegQuality < 1 || workspaceStorageSetting.thumbnailJpegQuality > 100) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const origin = WorkspaceSetting_StorageSetting.fromPartial(
|
||||
workspaceStore.getWorkspaceSettingByKey(WorkspaceSetting_Key.STORAGE)?.storageSetting || {},
|
||||
|
|
@ -73,6 +86,54 @@ const StorageSection = observer(() => {
|
|||
setWorkspaceStorageSetting(update);
|
||||
};
|
||||
|
||||
const handleThumbnailMaxSizeChanged = async (event: React.FocusEvent<HTMLInputElement>) => {
|
||||
let num = parseInt(event.target.value);
|
||||
if (Number.isNaN(num)) {
|
||||
num = 0;
|
||||
}
|
||||
const update: WorkspaceSetting_StorageSetting = {
|
||||
...workspaceStorageSetting,
|
||||
thumbnailMaxSize: num,
|
||||
};
|
||||
setWorkspaceStorageSetting(update);
|
||||
};
|
||||
|
||||
const handleJpegQualityChanged = async (event: React.FocusEvent<HTMLInputElement>) => {
|
||||
let num = parseInt(event.target.value);
|
||||
if (Number.isNaN(num)) {
|
||||
num = 0;
|
||||
}
|
||||
const update: WorkspaceSetting_StorageSetting = {
|
||||
...workspaceStorageSetting,
|
||||
jpegQuality: num,
|
||||
};
|
||||
setWorkspaceStorageSetting(update);
|
||||
};
|
||||
|
||||
const handleThumbnailJpegQualityChanged = async (event: React.FocusEvent<HTMLInputElement>) => {
|
||||
let num = parseInt(event.target.value);
|
||||
if (Number.isNaN(num)) {
|
||||
num = 0;
|
||||
}
|
||||
const update: WorkspaceSetting_StorageSetting = {
|
||||
...workspaceStorageSetting,
|
||||
thumbnailJpegQuality: num,
|
||||
};
|
||||
setWorkspaceStorageSetting(update);
|
||||
};
|
||||
|
||||
const handleImageMaxSizeChanged = async (event: React.FocusEvent<HTMLInputElement>) => {
|
||||
let num = parseInt(event.target.value);
|
||||
if (Number.isNaN(num)) {
|
||||
num = 0;
|
||||
}
|
||||
const update: WorkspaceSetting_StorageSetting = {
|
||||
...workspaceStorageSetting,
|
||||
imageMaxSize: num,
|
||||
};
|
||||
setWorkspaceStorageSetting(update);
|
||||
};
|
||||
|
||||
const handleFilepathTemplateChanged = async (event: React.FocusEvent<HTMLInputElement>) => {
|
||||
const update: WorkspaceSetting_StorageSetting = {
|
||||
...workspaceStorageSetting,
|
||||
|
|
@ -171,7 +232,7 @@ const StorageSection = observer(() => {
|
|||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<Input className="w-16 font-mono" value={workspaceStorageSetting.uploadSizeLimitMb} onChange={handleMaxUploadSizeChanged} />
|
||||
<Input className="w-20 font-mono" value={workspaceStorageSetting.uploadSizeLimitMb} onChange={handleMaxUploadSizeChanged} />
|
||||
</div>
|
||||
{workspaceStorageSetting.storageType !== WorkspaceSetting_StorageSetting_StorageType.DATABASE && (
|
||||
<div className="w-full flex flex-row justify-between items-center">
|
||||
|
|
@ -240,6 +301,81 @@ const StorageSection = observer(() => {
|
|||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="w-full flex flex-row justify-between items-center">
|
||||
<div className="flex flex-row items-center">
|
||||
<span className="text-muted-foreground mr-1">Maximum image size (px)</span>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<HelpCircleIcon className="w-4 h-auto" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>
|
||||
Maximum size in pixels for the largest dimension when storing images. Images larger than this will be downscaled. Set to 0
|
||||
to disable downscaling (default: 0, no downscaling).
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<Input className="w-20 font-mono" value={workspaceStorageSetting.imageMaxSize} onChange={handleImageMaxSizeChanged} />
|
||||
</div>
|
||||
<div className="w-full flex flex-row justify-between items-center">
|
||||
<div className="flex flex-row items-center">
|
||||
<span className="text-muted-foreground mr-1">JPEG quality</span>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<HelpCircleIcon className="w-4 h-auto" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>
|
||||
JPEG quality (0-100) used when downscaling uploaded images. Higher values = better quality but larger file size (default:
|
||||
85).
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<Input className="w-20 font-mono" value={workspaceStorageSetting.jpegQuality} onChange={handleJpegQualityChanged} />
|
||||
</div>
|
||||
<div className="w-full flex flex-row justify-between items-center">
|
||||
<div className="flex flex-row items-center">
|
||||
<span className="text-muted-foreground mr-1">Thumbnail size (px)</span>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<HelpCircleIcon className="w-4 h-auto" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Maximum size in pixels for the largest dimension of thumbnail images (default: 600).</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<Input className="w-20 font-mono" value={workspaceStorageSetting.thumbnailMaxSize} onChange={handleThumbnailMaxSizeChanged} />
|
||||
</div>
|
||||
<div className="w-full flex flex-row justify-between items-center">
|
||||
<div className="flex flex-row items-center">
|
||||
<span className="text-muted-foreground mr-1">Thumbnail JPEG quality</span>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<HelpCircleIcon className="w-4 h-auto" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>JPEG quality (0-100) used when generating thumbnails. Lower values save space (default: 75).</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<Input
|
||||
className="w-20 font-mono"
|
||||
value={workspaceStorageSetting.thumbnailJpegQuality}
|
||||
onChange={handleThumbnailJpegQualityChanged}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Button disabled={!allowSaveStorageSetting} onClick={saveWorkspaceStorageSetting}>
|
||||
{t("common.save")}
|
||||
|
|
|
|||
|
|
@ -141,7 +141,21 @@ export interface WorkspaceSetting_StorageSetting {
|
|||
/** The max upload size in megabytes. */
|
||||
uploadSizeLimitMb: number;
|
||||
/** The S3 config. */
|
||||
s3Config?: WorkspaceSetting_StorageSetting_S3Config | undefined;
|
||||
s3Config?:
|
||||
| WorkspaceSetting_StorageSetting_S3Config
|
||||
| undefined;
|
||||
/** The maximum size in pixels for the largest dimension of thumbnail images. */
|
||||
thumbnailMaxSize: number;
|
||||
/** The JPEG quality (0-100) used when downscaling uploaded images. */
|
||||
jpegQuality: number;
|
||||
/** The JPEG quality (0-100) used when generating thumbnails. */
|
||||
thumbnailJpegQuality: number;
|
||||
/**
|
||||
* The maximum size in pixels for the largest dimension when storing images.
|
||||
* Images larger than this will be downscaled before storage.
|
||||
* Set to 0 to disable downscaling.
|
||||
*/
|
||||
imageMaxSize: number;
|
||||
}
|
||||
|
||||
/** Storage type enumeration for different storage backends. */
|
||||
|
|
@ -705,6 +719,10 @@ function createBaseWorkspaceSetting_StorageSetting(): WorkspaceSetting_StorageSe
|
|||
filepathTemplate: "",
|
||||
uploadSizeLimitMb: 0,
|
||||
s3Config: undefined,
|
||||
thumbnailMaxSize: 0,
|
||||
jpegQuality: 0,
|
||||
thumbnailJpegQuality: 0,
|
||||
imageMaxSize: 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -722,6 +740,18 @@ export const WorkspaceSetting_StorageSetting: MessageFns<WorkspaceSetting_Storag
|
|||
if (message.s3Config !== undefined) {
|
||||
WorkspaceSetting_StorageSetting_S3Config.encode(message.s3Config, writer.uint32(34).fork()).join();
|
||||
}
|
||||
if (message.thumbnailMaxSize !== 0) {
|
||||
writer.uint32(40).int32(message.thumbnailMaxSize);
|
||||
}
|
||||
if (message.jpegQuality !== 0) {
|
||||
writer.uint32(48).int32(message.jpegQuality);
|
||||
}
|
||||
if (message.thumbnailJpegQuality !== 0) {
|
||||
writer.uint32(56).int32(message.thumbnailJpegQuality);
|
||||
}
|
||||
if (message.imageMaxSize !== 0) {
|
||||
writer.uint32(64).int32(message.imageMaxSize);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
|
|
@ -764,6 +794,38 @@ export const WorkspaceSetting_StorageSetting: MessageFns<WorkspaceSetting_Storag
|
|||
message.s3Config = WorkspaceSetting_StorageSetting_S3Config.decode(reader, reader.uint32());
|
||||
continue;
|
||||
}
|
||||
case 5: {
|
||||
if (tag !== 40) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.thumbnailMaxSize = reader.int32();
|
||||
continue;
|
||||
}
|
||||
case 6: {
|
||||
if (tag !== 48) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.jpegQuality = reader.int32();
|
||||
continue;
|
||||
}
|
||||
case 7: {
|
||||
if (tag !== 56) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.thumbnailJpegQuality = reader.int32();
|
||||
continue;
|
||||
}
|
||||
case 8: {
|
||||
if (tag !== 64) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.imageMaxSize = reader.int32();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
|
|
@ -784,6 +846,10 @@ export const WorkspaceSetting_StorageSetting: MessageFns<WorkspaceSetting_Storag
|
|||
message.s3Config = (object.s3Config !== undefined && object.s3Config !== null)
|
||||
? WorkspaceSetting_StorageSetting_S3Config.fromPartial(object.s3Config)
|
||||
: undefined;
|
||||
message.thumbnailMaxSize = object.thumbnailMaxSize ?? 0;
|
||||
message.jpegQuality = object.jpegQuality ?? 0;
|
||||
message.thumbnailJpegQuality = object.thumbnailJpegQuality ?? 0;
|
||||
message.imageMaxSize = object.imageMaxSize ?? 0;
|
||||
return message;
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue