diff --git a/api/errors/resource_error_reason.pb.go b/api/errors/resource_error_reason.pb.go index 7e728d9..a7298b7 100644 --- a/api/errors/resource_error_reason.pb.go +++ b/api/errors/resource_error_reason.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 +// protoc-gen-go v1.32.0 // protoc v4.24.4 // source: resource_error_reason.proto @@ -42,6 +42,7 @@ const ( Reason_NotExistDirectory Reason = 15 Reason_NotExistResource Reason = 16 Reason_Params Reason = 17 + Reason_AccessResource Reason = 18 ) // Enum value maps for Reason. @@ -65,6 +66,7 @@ var ( 15: "NotExistDirectory", 16: "NotExistResource", 17: "Params", + 18: "AccessResource", } Reason_value = map[string]int32{ "NotFound": 0, @@ -85,6 +87,7 @@ var ( "NotExistDirectory": 15, "NotExistResource": 16, "Params": 17, + "AccessResource": 18, } ) @@ -121,7 +124,7 @@ var file_resource_error_reason_proto_rawDesc = []byte{ 0x0a, 0x1b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, 0x1a, 0x13, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2a, 0xe0, 0x05, 0x0a, 0x06, 0x52, 0x65, 0x61, 0x73, 0x6f, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2a, 0x91, 0x06, 0x0a, 0x06, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x00, 0x1a, 0x12, 0xb2, 0x45, 0x0f, 0xe4, 0xb8, 0x8d, 0xe5, 0xad, 0x98, 0xe5, 0x9c, 0xa8, 0xe6, 0x95, 0xb0, 0xe6, 0x8d, 0xae, 0x12, 0x24, 0x0a, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, @@ -167,8 +170,11 @@ var file_resource_error_reason_proto_rawDesc = []byte{ 0x10, 0x10, 0x1a, 0x12, 0xb2, 0x45, 0x0f, 0xe8, 0xb5, 0x84, 0xe6, 0xba, 0x90, 0xe4, 0xb8, 0x8d, 0xe5, 0xad, 0x98, 0xe5, 0x9c, 0xa8, 0x12, 0x1b, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x10, 0x11, 0x1a, 0x0f, 0xb2, 0x45, 0x0c, 0xe5, 0x8f, 0x82, 0xe6, 0x95, 0xb0, 0xe9, 0x94, 0x99, - 0xe8, 0xaf, 0xaf, 0x1a, 0x04, 0xa0, 0x45, 0xc8, 0x01, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x2f, 0x3b, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0xe8, 0xaf, 0xaf, 0x12, 0x2f, 0x0a, 0x0e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x10, 0x12, 0x1a, 0x1b, 0xb2, 0x45, 0x18, 0xe8, 0xae, 0xbf, 0xe9, + 0x97, 0xae, 0xe8, 0xb5, 0x84, 0xe6, 0xba, 0x90, 0xe6, 0x96, 0x87, 0xe4, 0xbb, 0xb6, 0xe5, 0xbc, + 0x82, 0xe5, 0xb8, 0xb8, 0x1a, 0x04, 0xa0, 0x45, 0xc8, 0x01, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x2f, + 0x3b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/errors/resource_error_reason.proto b/api/errors/resource_error_reason.proto index 0b16bb8..fa432a8 100644 --- a/api/errors/resource_error_reason.proto +++ b/api/errors/resource_error_reason.proto @@ -27,6 +27,8 @@ enum Reason { NotExistDirectory = 15[(errors.message)="文件夹不存在"]; NotExistResource = 16[(errors.message)="资源不存在"]; Params = 17[(errors.message)="参数错误"]; + AccessResource = 18[(errors.message)="访问资源文件异常"]; + } diff --git a/api/errors/resource_error_reason_errors.pb.go b/api/errors/resource_error_reason_errors.pb.go index 677ea6b..63dd380 100644 --- a/api/errors/resource_error_reason_errors.pb.go +++ b/api/errors/resource_error_reason_errors.pb.go @@ -298,3 +298,19 @@ func ParamsFormat(format string, args ...any) *errors.Error { func Params() *errors.Error { return errors.New(200, Reason_Params.String(), "参数错误") } + +func IsAccessResource(err error) bool { + if err == nil { + return false + } + e := errors.FromError(err) + return e.Reason == Reason_AccessResource.String() && e.Code == 200 +} + +func AccessResourceFormat(format string, args ...any) *errors.Error { + return errors.New(200, Reason_AccessResource.String(), "访问资源文件异常:"+fmt.Sprintf(format, args...)) +} + +func AccessResource() *errors.Error { + return errors.New(200, Reason_AccessResource.String(), "访问资源文件异常") +} diff --git a/api/file/openapi.yaml b/api/file/openapi.yaml index 5b8490b..f8f3735 100644 --- a/api/file/openapi.yaml +++ b/api/file/openapi.yaml @@ -3,453 +3,8 @@ openapi: 3.0.3 info: - title: Service API + title: "" version: 0.0.1 -paths: - /resource/client/v1/file/sha: - get: - tags: - - Service - operationId: Service_GetFileBySha - parameters: - - name: sha - in: query - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/File' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /resource/client/v1/upload/prepare: - post: - tags: - - Service - operationId: Service_PrepareUploadFile - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PrepareUploadFileRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/PrepareUploadFileReply' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /resource/v1/directories: - get: - tags: - - Service - operationId: Service_AllDirectory - parameters: - - name: parentId - in: query - schema: - type: integer - format: uint32 - - name: app - in: query - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/AllDirectoryReply' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /resource/v1/directory: - put: - tags: - - Service - operationId: Service_UpdateDirectory - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateDirectoryRequest' - required: true - responses: - "200": - description: OK - content: {} - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - post: - tags: - - Service - operationId: Service_AddDirectory - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/AddDirectoryRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/Directory' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - delete: - tags: - - Service - operationId: Service_DeleteDirectory - parameters: - - name: id - in: query - schema: - type: integer - format: uint32 - - name: app - in: query - schema: - type: string - responses: - "200": - description: OK - content: {} - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /resource/v1/file: - put: - tags: - - Service - operationId: Service_UpdateFile - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateFileRequest' - required: true - responses: - "200": - description: OK - content: {} - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - post: - tags: - - Service - operationId: Service_DeleteFile - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteFileRequest' - required: true - responses: - "200": - description: OK - content: {} - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /resource/v1/file/sha: - get: - tags: - - Service - operationId: Service_GetFileBySha - parameters: - - name: sha - in: query - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/File' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /resource/v1/files: - get: - tags: - - Service - operationId: Service_PageFile - parameters: - - name: directoryId - in: query - schema: - type: integer - format: uint32 - - name: app - in: query - schema: - type: string - - name: name - in: query - schema: - type: string - - name: page - in: query - schema: - type: integer - format: uint32 - - name: pageSize - in: query - schema: - type: integer - format: uint32 - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/PageFileReply' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /resource/v1/upload/prepare: - post: - tags: - - Service - operationId: Service_PrepareUploadFile - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PrepareUploadFileRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/PrepareUploadFileReply' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' +paths: {} components: - schemas: - AddDirectoryRequest: - type: object - properties: - parentId: - type: integer - format: uint32 - name: - type: string - app: - type: string - AllDirectoryReply: - type: object - properties: - list: - type: array - items: - $ref: '#/components/schemas/Directory' - DeleteFileRequest: - type: object - properties: - ids: - type: array - items: - type: integer - format: uint32 - app: - type: string - directoryId: - type: integer - format: uint32 - Directory: - type: object - properties: - id: - type: integer - format: uint32 - parentId: - type: integer - format: uint32 - app: - type: string - name: - type: string - createdAt: - type: integer - format: uint32 - updatedAt: - type: integer - format: uint32 - File: - type: object - properties: - id: - type: integer - format: uint32 - directoryId: - type: integer - format: uint32 - name: - type: string - type: - type: string - size: - type: integer - format: uint32 - sha: - type: string - src: - type: string - storage: - type: string - createdAt: - type: integer - format: uint32 - GoogleProtobufAny: - type: object - properties: - '@type': - type: string - description: The type of the serialized message. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. - PageFileReply: - type: object - properties: - list: - type: array - items: - $ref: '#/components/schemas/File' - total: - type: integer - format: uint32 - PrepareUploadFileReply: - type: object - properties: - uploaded: - type: boolean - src: - type: string - chunkSize: - type: integer - format: uint32 - chunkCount: - type: integer - format: uint32 - uploadId: - type: string - uploadChunks: - type: array - items: - type: integer - format: uint32 - sha: - type: string - PrepareUploadFileRequest: - type: object - properties: - directoryId: - type: integer - format: uint32 - directoryPath: - type: string - app: - type: string - name: - type: string - sha: - type: string - size: - type: integer - format: uint32 - Status: - type: object - properties: - code: - type: integer - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - format: int32 - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/GoogleProtobufAny' - description: A list of messages that carry the error details. There is a common set of message types for APIs to use. - description: 'The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).' - UpdateDirectoryRequest: - type: object - properties: - id: - type: integer - format: uint32 - app: - type: string - name: - type: string - UpdateFileRequest: - type: object - properties: - id: - type: integer - format: uint32 - app: - type: string - name: - type: string - directoryId: - type: integer - format: uint32 -tags: - - name: Service + schemas: {} diff --git a/api/file/resource_file.proto b/api/file/resource_file.proto index b37f8f5..a6689ed 100644 --- a/api/file/resource_file.proto +++ b/api/file/resource_file.proto @@ -22,6 +22,9 @@ message GetFileRequest{ uint32 width =2; uint32 height =3; string mode = 4; + bool is_range = 5; + uint32 start = 6; + uint32 end = 7; } message GetFileReply{ diff --git a/api/file/v1/resource_file.pb.go b/api/file/v1/resource_file.pb.go index 4c09006..dc3fb1f 100644 --- a/api/file/v1/resource_file.pb.go +++ b/api/file/v1/resource_file.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 +// protoc-gen-go v1.32.0 // protoc v4.24.4 // source: resource_file.proto @@ -138,10 +138,13 @@ type GetFileRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Src string `protobuf:"bytes,1,opt,name=src,proto3" json:"src,omitempty"` - Width uint32 `protobuf:"varint,2,opt,name=width,proto3" json:"width,omitempty"` - Height uint32 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"` - Mode string `protobuf:"bytes,4,opt,name=mode,proto3" json:"mode,omitempty"` + Src string `protobuf:"bytes,1,opt,name=src,proto3" json:"src,omitempty"` + Width uint32 `protobuf:"varint,2,opt,name=width,proto3" json:"width,omitempty"` + Height uint32 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"` + Mode string `protobuf:"bytes,4,opt,name=mode,proto3" json:"mode,omitempty"` + IsRange bool `protobuf:"varint,5,opt,name=is_range,json=isRange,proto3" json:"is_range,omitempty"` + Start uint32 `protobuf:"varint,6,opt,name=start,proto3" json:"start,omitempty"` + End uint32 `protobuf:"varint,7,opt,name=end,proto3" json:"end,omitempty"` } func (x *GetFileRequest) Reset() { @@ -204,6 +207,27 @@ func (x *GetFileRequest) GetMode() string { return "" } +func (x *GetFileRequest) GetIsRange() bool { + if x != nil { + return x.IsRange + } + return false +} + +func (x *GetFileRequest) GetStart() uint32 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *GetFileRequest) GetEnd() uint32 { + if x != nil { + return x.End + } + return 0 +} + type GetFileReply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -895,100 +919,104 @@ var file_resource_file_proto_rawDesc = []byte{ 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x6d, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x03, 0x73, 0x72, 0x63, 0x18, 0x01, + 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0xb0, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x03, 0x73, 0x72, 0x63, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, + 0x73, 0x72, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x72, 0x61, 0x6e, 0x67, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x52, 0x61, 0x6e, 0x67, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x36, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x46, + 0x69, 0x6c, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, + 0x6d, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x69, 0x6d, 0x65, + 0x22, 0x30, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x42, 0x79, 0x53, 0x68, 0x61, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x03, 0x73, 0x68, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x73, - 0x72, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x36, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x52, - 0x65, 0x70, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x69, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x69, 0x6d, 0x65, 0x22, 0x30, 0x0a, 0x13, - 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x42, 0x79, 0x53, 0x68, 0x61, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x03, 0x73, 0x68, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x73, 0x68, 0x61, 0x22, 0xd4, - 0x01, 0x0a, 0x18, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, - 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x64, - 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x12, 0x25, - 0x0a, 0x0e, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x61, 0x70, 0x70, - 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, - 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, - 0x03, 0x73, 0x68, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, - 0x02, 0x10, 0x01, 0x52, 0x03, 0x73, 0x68, 0x61, 0x12, 0x1b, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x2a, 0x02, 0x20, 0x00, 0x52, - 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xc2, 0x02, 0x0a, 0x16, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, - 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, - 0x12, 0x1f, 0x0a, 0x08, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x48, 0x00, 0x52, 0x08, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x88, 0x01, - 0x01, 0x12, 0x15, 0x0a, 0x03, 0x73, 0x72, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, - 0x52, 0x03, 0x73, 0x72, 0x63, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x63, 0x68, 0x75, 0x6e, - 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, 0x09, - 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b, - 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0d, 0x48, 0x03, 0x52, 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x88, - 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x04, 0x52, 0x08, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, - 0x64, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x63, - 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x75, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x15, 0x0a, 0x03, 0x73, 0x68, 0x61, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x05, 0x52, 0x03, 0x73, 0x68, 0x61, 0x88, 0x01, 0x01, - 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x42, 0x06, 0x0a, - 0x04, 0x5f, 0x73, 0x72, 0x63, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, - 0x73, 0x69, 0x7a, 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, - 0x69, 0x64, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x73, 0x68, 0x61, 0x22, 0xbf, 0x01, 0x0a, 0x0f, 0x50, - 0x61, 0x67, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, - 0x0a, 0x0c, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0d, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x2a, 0x02, 0x20, 0x00, 0x52, 0x0b, 0x64, - 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x03, 0x61, 0x70, - 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, - 0x52, 0x03, 0x61, 0x70, 0x70, 0x12, 0x17, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1b, - 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x07, 0xfa, 0x42, - 0x04, 0x2a, 0x02, 0x20, 0x00, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x09, 0x70, - 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, - 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x18, 0x64, 0x20, 0x00, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, - 0x69, 0x7a, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x52, 0x0a, 0x0d, - 0x50, 0x61, 0x67, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, - 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x76, 0x31, - 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x05, 0x74, - 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x05, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, - 0x22, 0x90, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x2a, 0x02, 0x20, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x19, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x61, 0x70, 0x70, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x0c, 0x64, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x07, 0xfa, - 0x42, 0x04, 0x2a, 0x02, 0x20, 0x00, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x79, 0x49, 0x64, 0x22, 0x76, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x46, 0x69, 0x6c, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0d, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x92, 0x01, 0x02, 0x08, 0x01, 0x52, - 0x03, 0x69, 0x64, 0x73, 0x12, 0x19, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x61, 0x70, 0x70, 0x12, - 0x2a, 0x0a, 0x0c, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x2a, 0x02, 0x20, 0x00, 0x52, 0x0b, - 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x75, 0x0a, 0x11, 0x55, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x07, - 0xfa, 0x42, 0x04, 0x7a, 0x02, 0x10, 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x24, 0x0a, - 0x09, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x75, 0x70, 0x6c, 0x6f, 0x61, - 0x64, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0d, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x2a, 0x02, 0x20, 0x00, 0x52, 0x05, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x22, 0x35, 0x0a, 0x0f, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, - 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x72, 0x63, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x73, 0x72, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x68, 0x61, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x68, 0x61, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x76, - 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x68, 0x61, 0x22, 0xd4, 0x01, 0x0a, 0x18, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x55, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, + 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x5f, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x69, 0x72, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x03, 0x61, 0x70, 0x70, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, + 0x03, 0x61, 0x70, 0x70, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x19, 0x0a, 0x03, 0x73, 0x68, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x73, 0x68, 0x61, 0x12, 0x1b, 0x0a, 0x04, + 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x2a, + 0x02, 0x20, 0x00, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xc2, 0x02, 0x0a, 0x16, 0x50, 0x72, + 0x65, 0x70, 0x61, 0x72, 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1f, 0x0a, 0x08, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x08, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x15, 0x0a, 0x03, 0x73, 0x72, 0x63, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x01, 0x52, 0x03, 0x73, 0x72, 0x63, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, + 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x48, 0x02, 0x52, 0x09, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, + 0x12, 0x24, 0x0a, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x03, 0x52, 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x04, 0x52, 0x08, 0x75, 0x70, 0x6c, + 0x6f, 0x61, 0x64, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0d, 0x52, + 0x0c, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x15, 0x0a, + 0x03, 0x73, 0x68, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x05, 0x52, 0x03, 0x73, 0x68, + 0x61, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, + 0x64, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x73, 0x72, 0x63, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, 0x68, + 0x75, 0x6e, 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x68, 0x75, + 0x6e, 0x6b, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x75, 0x70, 0x6c, + 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x73, 0x68, 0x61, 0x22, 0xbf, + 0x01, 0x0a, 0x0f, 0x50, 0x61, 0x67, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x0c, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x2a, 0x02, 0x20, + 0x00, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x12, 0x19, + 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x61, 0x70, 0x70, 0x12, 0x17, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x88, + 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, + 0x42, 0x07, 0xfa, 0x42, 0x04, 0x2a, 0x02, 0x20, 0x00, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, + 0x26, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x18, 0x64, 0x20, 0x00, 0x52, 0x08, 0x70, + 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x22, 0x52, 0x0a, 0x0d, 0x50, 0x61, 0x67, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x12, 0x1c, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x08, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x12, + 0x19, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, + 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x22, 0x90, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, + 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x2a, 0x02, 0x20, 0x00, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x61, 0x70, 0x70, 0x12, 0x1b, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, + 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x0c, 0x64, + 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0d, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x2a, 0x02, 0x20, 0x00, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, 0x76, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x03, + 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x92, 0x01, + 0x02, 0x08, 0x01, 0x52, 0x03, 0x69, 0x64, 0x73, 0x12, 0x19, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, + 0x61, 0x70, 0x70, 0x12, 0x2a, 0x0a, 0x0c, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, + 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x2a, 0x02, + 0x20, 0x00, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x22, + 0x75, 0x0a, 0x11, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x7a, 0x02, 0x10, 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x24, 0x0a, 0x09, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x75, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x2a, 0x02, 0x20, 0x00, 0x52, + 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x35, 0x0a, 0x0f, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x72, 0x63, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x72, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x73, + 0x68, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x68, 0x61, 0x42, 0x09, 0x5a, + 0x07, 0x2e, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/file/v1/resource_file.pb.validate.go b/api/file/v1/resource_file.pb.validate.go index 02938a9..40c8193 100644 --- a/api/file/v1/resource_file.pb.validate.go +++ b/api/file/v1/resource_file.pb.validate.go @@ -190,6 +190,12 @@ func (m *GetFileRequest) validate(all bool) error { // no validation rules for Mode + // no validation rules for IsRange + + // no validation rules for Start + + // no validation rules for End + if len(errors) > 0 { return GetFileRequestMultiError(errors) } diff --git a/go.mod b/go.mod index f502a56..f964044 100644 --- a/go.mod +++ b/go.mod @@ -49,6 +49,7 @@ require ( github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-playground/assert/v2 v2.2.0 // indirect github.com/go-playground/form/v4 v4.2.0 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/go-resty/resty/v2 v2.11.0 // indirect diff --git a/go.sum b/go.sum index 55ce456..7e3e255 100644 --- a/go.sum +++ b/go.sum @@ -1174,8 +1174,9 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/form/v4 v4.2.0 h1:N1wh+Goz61e6w66vo8vJkQt+uwZSoLz50kZPJWR8eic= github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= diff --git a/internal/biz/file/biz.go b/internal/biz/file/biz.go index 2a94c58..27e2a83 100644 --- a/internal/biz/file/biz.go +++ b/internal/biz/file/biz.go @@ -1,8 +1,11 @@ package file import ( + "bytes" "io" "mime" + "os" + "path/filepath" "strings" "sync" @@ -103,9 +106,23 @@ func (u *UseCase) GetFile(ctx kratosx.Context, in *GetFileRequest) (*GetFileResp if err != nil { return nil, errors.NotExistResource() } - rb, _ := io.ReadAll(reader) - fileMime := mime.TypeByExtension("." + u.factory.GetType(in.Src)) + var rb []byte + if in.IsRange { + file := reader.(*os.File) + if _, err := file.Seek(in.Start, io.SeekStart); err != nil { + return nil, errors.AccessResourceFormat(err.Error()) + } + tempReader := bytes.NewBuffer([]byte{}) + if _, err := io.CopyN(tempReader, file, in.End-in.Start+1); err != nil { + return nil, errors.AccessResourceFormat(err.Error()) + } + rb, _ = io.ReadAll(tempReader) + } else { + rb, _ = io.ReadAll(reader) + } + + fileMime := mime.TypeByExtension(filepath.Ext(in.Src)) if fileMime == "" { fileMime = u.factory.FileMime(rb) } diff --git a/internal/biz/file/types.go b/internal/biz/file/types.go index 1a0d091..b8064c0 100644 --- a/internal/biz/file/types.go +++ b/internal/biz/file/types.go @@ -43,10 +43,13 @@ type UploadFileReply struct { } type GetFileRequest struct { - Src string `json:"src"` - Width int `json:"width"` - Height int `json:"height"` - Mode string `json:"mode"` + Src string `json:"src"` + Width int `json:"width"` + Height int `json:"height"` + Mode string `json:"mode"` + IsRange bool `json:"is_range"` + Start int64 `json:"start"` + End int64 `json:"end"` } type GetFileResponse struct { diff --git a/internal/router/blob.go b/internal/router/blob.go index f694c16..057db10 100644 --- a/internal/router/blob.go +++ b/internal/router/blob.go @@ -1,16 +1,47 @@ package router import ( - "context" + "bytes" + "net/http" + "strconv" + "strings" - "github.com/go-kratos/kratos/v2/transport/http" + thttp "github.com/go-kratos/kratos/v2/transport/http" + "github.com/limes-cloud/resource/api/errors" pb "github.com/limes-cloud/resource/api/file/v1" + "github.com/limes-cloud/resource/internal/pkg/image" "github.com/limes-cloud/resource/internal/service" ) -func SrcBlob(srv *service.FileService) http.HandlerFunc { - return func(ctx http.Context) error { +type ResponseWriterWrapper struct { + body *bytes.Buffer + header http.Header + code int +} + +func NewWriter() *ResponseWriterWrapper { + return &ResponseWriterWrapper{body: bytes.NewBufferString(""), header: make(http.Header)} +} + +func (w *ResponseWriterWrapper) Header() http.Header { + return w.header +} + +func (w *ResponseWriterWrapper) WriteHeader(statusCode int) { + w.code = statusCode +} + +func (w *ResponseWriterWrapper) Write(b []byte) (int, error) { + return w.body.Write(b) +} + +func (w *ResponseWriterWrapper) WriteString(s string) (int, error) { + return w.body.WriteString(s) +} + +func SrcBlob(srv *service.FileService) thttp.HandlerFunc { + return func(ctx thttp.Context) error { var in pb.GetFileRequest if err := ctx.BindQuery(&in); err != nil { return err @@ -18,14 +49,69 @@ func SrcBlob(srv *service.FileService) http.HandlerFunc { if err := ctx.BindVars(&in); err != nil { return err } - h := ctx.Middleware(func(ctx context.Context, req any) (any, error) { - return srv.GetFile(ctx, req.(*pb.GetFileRequest)) - }) - out, err := h(ctx, &in) - if err != nil { - return err + + blw := NewWriter() + + fs := http.FileServer(http.Dir(srv.Config().LocalDir)) + fs = http.StripPrefix(srv.Config().ServerPath, fs) + fs.ServeHTTP(blw, ctx.Request()) + + // 处理图片裁剪 + cType := blw.header.Get("Content-Type") + if strings.Contains(cType, "image/") && in.Width > 0 && in.Height > 0 { + blw.header.Del("Content-Length") + tp := strings.Split(cType, "/")[1] + rb := blw.body.Bytes() + if img, err := image.New(tp, rb); err == nil { + if in.Mode == "" { + in.Mode = image.AspectFill + } + if nrb, err := img.Resize(int(in.Width), int(in.Height), in.Mode); err == nil { + blw.body = bytes.NewBuffer(nrb) + blw.header.Set("Content-Length", strconv.Itoa(len(nrb))) + } + } } - reply := out.(*pb.GetFileReply) - return ctx.Blob(200, reply.Mime, reply.Data) + + // 处理返回 + header := ctx.Response().Header() + for key := range blw.header { + header.Set(key, blw.header.Get(key)) + } + + ctx.Response().WriteHeader(blw.code) + if _, err := ctx.Response().Write(blw.body.Bytes()); err != nil { + return errors.System() + } + + return nil } } + +// func SrcBlob(srv *service.FileService) http.HandlerFunc { +// return func(ctx http.Context) error { +// var in pb.GetFileRequest +// if err := ctx.BindQuery(&in); err != nil { +// return err +// } +// if err := ctx.BindVars(&in); err != nil { +// return err +// } +// +// h := ctx.Middleware(func(ctx context.Context, req any) (any, error) { +// return srv.GetFile(ctx, req.(*pb.GetFileRequest)) +// }) +// out, err := h(ctx, &in) +// if err != nil { +// return err +// } +// reply := out.(*pb.GetFileReply) +// header := ctx.Response().Header() +// header.Set("Content-Length", fmt.Sprint(len(reply.Data))) +// if in.IsRange { +// header.Set("Content-Range", fmt.Sprintf("bytes %d-%d", in.Start, in.End)) +// } +// +// return ctx.Blob(200, reply.Mime, reply.Data) +// } +// } diff --git a/internal/service/file.go b/internal/service/file.go index 5c1ac9a..4a18743 100644 --- a/internal/service/file.go +++ b/internal/service/file.go @@ -27,6 +27,10 @@ func NewFile(conf *config.Config) *FileService { } } +func (fs *FileService) Config() *config.Config { + return fs.conf +} + // AllDirectory 获取目录 func (fs *FileService) AllDirectory(ctx context.Context, in *pb.AllDirectoryRequest) (*pb.AllDirectoryReply, error) { list, err := fs.uc.AllDirectoryByParentID(kratosx.MustContext(ctx), in.ParentId, in.App)