feat: update workspace profile

This commit is contained in:
Steven 2024-06-03 22:41:51 +08:00
parent 15ca4fe7ac
commit d51d180a29
17 changed files with 332 additions and 282 deletions

View File

@ -6,18 +6,13 @@ option go_package = "gen/api/v1";
enum RowStatus { enum RowStatus {
ROW_STATUS_UNSPECIFIED = 0; ROW_STATUS_UNSPECIFIED = 0;
NORMAL = 1; NORMAL = 1;
ARCHIVED = 2; ARCHIVED = 2;
} }
enum Visibility { enum Visibility {
VISIBILITY_UNSPECIFIED = 0; VISIBILITY_UNSPECIFIED = 0;
PRIVATE = 1; PRIVATE = 1;
WORKSPACE = 2; WORKSPACE = 2;
PUBLIC = 3; PUBLIC = 3;
} }

View File

@ -30,9 +30,7 @@ message Subscription {
enum PlanType { enum PlanType {
PLAN_TYPE_UNSPECIFIED = 0; PLAN_TYPE_UNSPECIFIED = 0;
FREE = 1; FREE = 1;
PRO = 2; PRO = 2;
} }

View File

@ -41,6 +41,9 @@ message WorkspaceProfile {
string custom_script = 6; string custom_script = 6;
// The url of custom favicon provider. // The url of custom favicon provider.
string favicon_provider = 7; string favicon_provider = 7;
// The owner name.
// Format: "users/{id}"
string owner = 8;
} }
message WorkspaceSetting { message WorkspaceSetting {

View File

@ -1502,6 +1502,7 @@
| custom_style | [string](#string) | | The custom style. | | custom_style | [string](#string) | | The custom style. |
| custom_script | [string](#string) | | The custom script. | | custom_script | [string](#string) | | The custom script. |
| favicon_provider | [string](#string) | | The url of custom favicon provider. | | favicon_provider | [string](#string) | | The url of custom favicon provider. |
| owner | [string](#string) | | The owner name. Format: "users/{id}" |

View File

@ -41,6 +41,9 @@ type WorkspaceProfile struct {
CustomScript string `protobuf:"bytes,6,opt,name=custom_script,json=customScript,proto3" json:"custom_script,omitempty"` CustomScript string `protobuf:"bytes,6,opt,name=custom_script,json=customScript,proto3" json:"custom_script,omitempty"`
// The url of custom favicon provider. // The url of custom favicon provider.
FaviconProvider string `protobuf:"bytes,7,opt,name=favicon_provider,json=faviconProvider,proto3" json:"favicon_provider,omitempty"` FaviconProvider string `protobuf:"bytes,7,opt,name=favicon_provider,json=faviconProvider,proto3" json:"favicon_provider,omitempty"`
// The owner name.
// Format: "users/{id}"
Owner string `protobuf:"bytes,8,opt,name=owner,proto3" json:"owner,omitempty"`
} }
func (x *WorkspaceProfile) Reset() { func (x *WorkspaceProfile) Reset() {
@ -124,6 +127,13 @@ func (x *WorkspaceProfile) GetFaviconProvider() string {
return "" return ""
} }
func (x *WorkspaceProfile) GetOwner() string {
if x != nil {
return x.Owner
}
return ""
}
type WorkspaceSetting struct { type WorkspaceSetting struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -594,7 +604,7 @@ var file_api_v1_workspace_service_proto_rawDesc = []byte{
0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66,
0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
0x84, 0x02, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x9a, 0x02, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f,
0x66, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
@ -610,112 +620,113 @@ var file_api_v1_workspace_service_proto_rawDesc = []byte{
0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x66, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x66,
0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18,
0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e, 0x50, 0x72, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e, 0x50, 0x72,
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0x82, 0x03, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18,
0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x22, 0x82, 0x03, 0x0a,
0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x52, 0x0a, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6b, 0x65, 0x79,
0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x75, 0x70, 0x18, 0x02, 0x20, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x4b,
0x01, 0x28, 0x08, 0x52, 0x0c, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x75, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x67,
0x70, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x65, 0x6e, 0x61, 0x62, 0x6c,
0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x75, 0x70, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61,
0x65, 0x55, 0x72, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x6e, 0x63, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69,
0x74, 0x79, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x75,
0x6f, 0x6d, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
0x6d, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x52, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x23, 0x0a,
0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x49, 0x0a, 0x0b, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x05,
0x61, 0x75, 0x74, 0x6f, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x63, 0x72, 0x69,
0x0b, 0x32, 0x28, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x70, 0x74, 0x12, 0x49, 0x0a, 0x0b, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75,
0x2e, 0x41, 0x75, 0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e,
0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75,
0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x47, 0x0a, 0x12, 0x64, 0x65, 0x66, 0x61, 0x75, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x6c, 0x74, 0x5f, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x67, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x47, 0x0a,
0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x12, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c,
0x76, 0x31, 0x2e, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x11, 0x64, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x73, 0x6c, 0x61, 0x73,
0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c,
0x12, 0x29, 0x0a, 0x10, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x74, 0x79, 0x52, 0x11, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x69, 0x73, 0x69,
0x69, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x66, 0x61, 0x76, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f,
0x63, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0x7a, 0x0a, 0x1a, 0x41, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09,
0x75, 0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x52, 0x0f, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65,
0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x72, 0x22, 0x7a, 0x0a, 0x1a, 0x41, 0x75, 0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x57,
0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12,
0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x72, 0x6f,
0x6f, 0x6e, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01,
0x6d, 0x61, 0x78, 0x5f, 0x6b, 0x65, 0x65, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x6f, 0x6e, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69,
0x6d, 0x61, 0x78, 0x4b, 0x65, 0x65, 0x70, 0x22, 0x1c, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x6b, 0x65, 0x65, 0x70, 0x18, 0x03,
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x4b, 0x65, 0x65, 0x70, 0x22, 0x1c, 0x0a,
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x57, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x1a, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f,
0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x57, 0x0a, 0x1b, 0x47,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x70, 0x72,
0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x6c,
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x1c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73,
0x0a, 0x1a, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x70, 0x72, 0x6f,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x57, 0x0a, 0x1b, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73,
0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65,
0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x73, 0x73, 0x74, 0x22, 0x57, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01,
0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x73, 0x65, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x96, 0x01, 0x0a, 0x1d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69,
0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x6e, 0x67, 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x96, 0x01, 0x0a, 0x1d,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53,
0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e,
0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f,
0x67, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x07,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67,
0x73, 0x6b, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0x5a, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46,
0x0a, 0x1e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0x5a, 0x0a, 0x1e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f,
0x12, 0x38, 0x0a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65,
0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e,
0x67, 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x32, 0xea, 0x03, 0x0a, 0x10, 0x57, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
0x8d, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x32, 0xea, 0x03, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65,
0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x28, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x8d, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72,
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x28, 0x2e,
0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74,
0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70,
0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x74, 0x1a, 0x29, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x61, 0x70, 0x69,
0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x70, 0x72,
0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x8d, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72,
0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x28, 0x2e,
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74,
0x8d, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x28, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70,
0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x29, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31,
0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74,
0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f,
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12,
0xb5, 0x01, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70,
0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x2b, 0x2e, 0x73, 0x6c, 0x61,
0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70,
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0xda, 0x41, 0x13, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x61, 0x70, 0x69,
0x67, 0x2c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x82, 0xd3, 0xe4, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x73, 0x65,
0x93, 0x02, 0x24, 0x3a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x32, 0x19, 0x2f, 0x61, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0xb5, 0x01, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x42, 0xb3, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x12, 0x2b, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e,
0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x15, 0x57, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53,
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e,
0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64,
0x6d, 0x2f, 0x79, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x6c, 0x66, 0x68, 0x6f, 0x73, 0x74, 0x65, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74,
0x2f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0xda, 0x41, 0x13,
0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d,
0x53, 0x41, 0x58, 0xaa, 0x02, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x41, 0x70, 0x69, 0x2e, 0x61, 0x73, 0x6b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69,
0x56, 0x31, 0xca, 0x02, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x6e, 0x67, 0x32, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b,
0x31, 0xe2, 0x02, 0x18, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x42, 0xb3, 0x01,
0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0e, 0x53, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x6c, 0x61, 0x73, 0x68, 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x76, 0x31, 0x42, 0x15, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72,
0x72, 0x6f, 0x74, 0x6f, 0x33, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x36, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x79, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x6c, 0x66,
0x68, 0x6f, 0x73, 0x74, 0x65, 0x64, 0x2f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2f, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70,
0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x53, 0x41, 0x58, 0xaa, 0x02, 0x0c, 0x53, 0x6c, 0x61, 0x73,
0x68, 0x2e, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68,
0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c,
0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
0x74, 0x61, 0xea, 0x02, 0x0e, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a,
0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

View File

@ -1169,3 +1169,8 @@ definitions:
faviconProvider: faviconProvider:
type: string type: string
description: The url of custom favicon provider. description: The url of custom favicon provider.
owner:
type: string
title: |-
The owner name.
Format: "users/{id}"

View File

@ -11,14 +11,14 @@ import (
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
apiv1pb "github.com/yourselfhosted/slash/proto/gen/api/v1" v1pb "github.com/yourselfhosted/slash/proto/gen/api/v1"
storepb "github.com/yourselfhosted/slash/proto/gen/store" storepb "github.com/yourselfhosted/slash/proto/gen/store"
"github.com/yourselfhosted/slash/server/metric" "github.com/yourselfhosted/slash/server/metric"
"github.com/yourselfhosted/slash/server/service/license" "github.com/yourselfhosted/slash/server/service/license"
"github.com/yourselfhosted/slash/store" "github.com/yourselfhosted/slash/store"
) )
func (s *APIV1Service) GetAuthStatus(ctx context.Context, _ *apiv1pb.GetAuthStatusRequest) (*apiv1pb.GetAuthStatusResponse, error) { func (s *APIV1Service) GetAuthStatus(ctx context.Context, _ *v1pb.GetAuthStatusRequest) (*v1pb.GetAuthStatusResponse, error) {
user, err := getCurrentUser(ctx, s.Store) user, err := getCurrentUser(ctx, s.Store)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err) return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
@ -26,12 +26,12 @@ func (s *APIV1Service) GetAuthStatus(ctx context.Context, _ *apiv1pb.GetAuthStat
if user == nil { if user == nil {
return nil, status.Errorf(codes.Unauthenticated, "user not found") return nil, status.Errorf(codes.Unauthenticated, "user not found")
} }
return &apiv1pb.GetAuthStatusResponse{ return &v1pb.GetAuthStatusResponse{
User: convertUserFromStore(user), User: convertUserFromStore(user),
}, nil }, nil
} }
func (s *APIV1Service) SignIn(ctx context.Context, request *apiv1pb.SignInRequest) (*apiv1pb.SignInResponse, error) { func (s *APIV1Service) SignIn(ctx context.Context, request *v1pb.SignInRequest) (*v1pb.SignInResponse, error) {
user, err := s.Store.GetUser(ctx, &store.FindUser{ user, err := s.Store.GetUser(ctx, &store.FindUser{
Email: &request.Email, Email: &request.Email,
}) })
@ -64,12 +64,12 @@ func (s *APIV1Service) SignIn(ctx context.Context, request *apiv1pb.SignInReques
} }
metric.Enqueue("user sign in") metric.Enqueue("user sign in")
return &apiv1pb.SignInResponse{ return &v1pb.SignInResponse{
User: convertUserFromStore(user), User: convertUserFromStore(user),
}, nil }, nil
} }
func (s *APIV1Service) SignUp(ctx context.Context, request *apiv1pb.SignUpRequest) (*apiv1pb.SignUpResponse, error) { func (s *APIV1Service) SignUp(ctx context.Context, request *v1pb.SignUpRequest) (*v1pb.SignUpResponse, error) {
enableSignUpSetting, err := s.Store.GetWorkspaceSetting(ctx, &store.FindWorkspaceSetting{ enableSignUpSetting, err := s.Store.GetWorkspaceSetting(ctx, &store.FindWorkspaceSetting{
Key: storepb.WorkspaceSettingKey_WORKSAPCE_SETTING_ENABLE_SIGNUP, Key: storepb.WorkspaceSettingKey_WORKSAPCE_SETTING_ENABLE_SIGNUP,
}) })
@ -131,12 +131,12 @@ func (s *APIV1Service) SignUp(ctx context.Context, request *apiv1pb.SignUpReques
} }
metric.Enqueue("user sign up") metric.Enqueue("user sign up")
return &apiv1pb.SignUpResponse{ return &v1pb.SignUpResponse{
User: convertUserFromStore(user), User: convertUserFromStore(user),
}, nil }, nil
} }
func (*APIV1Service) SignOut(ctx context.Context, _ *apiv1pb.SignOutRequest) (*apiv1pb.SignOutResponse, error) { func (*APIV1Service) SignOut(ctx context.Context, _ *v1pb.SignOutRequest) (*v1pb.SignOutResponse, error) {
// Set the cookie header to expire access token. // Set the cookie header to expire access token.
if err := grpc.SetHeader(ctx, metadata.New(map[string]string{ if err := grpc.SetHeader(ctx, metadata.New(map[string]string{
"Set-Cookie": fmt.Sprintf("%s=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=Strict", AccessTokenCookieName), "Set-Cookie": fmt.Sprintf("%s=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=Strict", AccessTokenCookieName),
@ -144,5 +144,5 @@ func (*APIV1Service) SignOut(ctx context.Context, _ *apiv1pb.SignOutRequest) (*a
return nil, status.Errorf(codes.Internal, "failed to set grpc header, error: %v", err) return nil, status.Errorf(codes.Internal, "failed to set grpc header, error: %v", err)
} }
return &apiv1pb.SignOutResponse{}, nil return &v1pb.SignOutResponse{}, nil
} }

View File

@ -8,14 +8,14 @@ import (
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
apiv1pb "github.com/yourselfhosted/slash/proto/gen/api/v1" v1pb "github.com/yourselfhosted/slash/proto/gen/api/v1"
storepb "github.com/yourselfhosted/slash/proto/gen/store" storepb "github.com/yourselfhosted/slash/proto/gen/store"
"github.com/yourselfhosted/slash/server/metric" "github.com/yourselfhosted/slash/server/metric"
"github.com/yourselfhosted/slash/server/service/license" "github.com/yourselfhosted/slash/server/service/license"
"github.com/yourselfhosted/slash/store" "github.com/yourselfhosted/slash/store"
) )
func (s *APIV1Service) ListCollections(ctx context.Context, _ *apiv1pb.ListCollectionsRequest) (*apiv1pb.ListCollectionsResponse, error) { func (s *APIV1Service) ListCollections(ctx context.Context, _ *v1pb.ListCollectionsRequest) (*v1pb.ListCollectionsResponse, error) {
user, err := getCurrentUser(ctx, s.Store) user, err := getCurrentUser(ctx, s.Store)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err) return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
@ -41,18 +41,18 @@ func (s *APIV1Service) ListCollections(ctx context.Context, _ *apiv1pb.ListColle
} }
collections = append(collections, sharedCollections...) collections = append(collections, sharedCollections...)
convertedCollections := []*apiv1pb.Collection{} convertedCollections := []*v1pb.Collection{}
for _, collection := range collections { for _, collection := range collections {
convertedCollections = append(convertedCollections, convertCollectionFromStore(collection)) convertedCollections = append(convertedCollections, convertCollectionFromStore(collection))
} }
response := &apiv1pb.ListCollectionsResponse{ response := &v1pb.ListCollectionsResponse{
Collections: convertedCollections, Collections: convertedCollections,
} }
return response, nil return response, nil
} }
func (s *APIV1Service) GetCollection(ctx context.Context, request *apiv1pb.GetCollectionRequest) (*apiv1pb.GetCollectionResponse, error) { func (s *APIV1Service) GetCollection(ctx context.Context, request *v1pb.GetCollectionRequest) (*v1pb.GetCollectionResponse, error) {
collection, err := s.Store.GetCollection(ctx, &store.FindCollection{ collection, err := s.Store.GetCollection(ctx, &store.FindCollection{
ID: &request.Id, ID: &request.Id,
}) })
@ -70,13 +70,13 @@ func (s *APIV1Service) GetCollection(ctx context.Context, request *apiv1pb.GetCo
if collection.Visibility == storepb.Visibility_PRIVATE && collection.CreatorId != user.ID { if collection.Visibility == storepb.Visibility_PRIVATE && collection.CreatorId != user.ID {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied") return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
} }
response := &apiv1pb.GetCollectionResponse{ response := &v1pb.GetCollectionResponse{
Collection: convertCollectionFromStore(collection), Collection: convertCollectionFromStore(collection),
} }
return response, nil return response, nil
} }
func (s *APIV1Service) GetCollectionByName(ctx context.Context, request *apiv1pb.GetCollectionByNameRequest) (*apiv1pb.GetCollectionByNameResponse, error) { func (s *APIV1Service) GetCollectionByName(ctx context.Context, request *v1pb.GetCollectionByNameRequest) (*v1pb.GetCollectionByNameResponse, error) {
collection, err := s.Store.GetCollection(ctx, &store.FindCollection{ collection, err := s.Store.GetCollection(ctx, &store.FindCollection{
Name: &request.Name, Name: &request.Name,
}) })
@ -97,13 +97,13 @@ func (s *APIV1Service) GetCollectionByName(ctx context.Context, request *apiv1pb
return nil, status.Errorf(codes.PermissionDenied, "Permission denied") return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
} }
} }
response := &apiv1pb.GetCollectionByNameResponse{ response := &v1pb.GetCollectionByNameResponse{
Collection: convertCollectionFromStore(collection), Collection: convertCollectionFromStore(collection),
} }
return response, nil return response, nil
} }
func (s *APIV1Service) CreateCollection(ctx context.Context, request *apiv1pb.CreateCollectionRequest) (*apiv1pb.CreateCollectionResponse, error) { func (s *APIV1Service) CreateCollection(ctx context.Context, request *v1pb.CreateCollectionRequest) (*v1pb.CreateCollectionResponse, error) {
if request.Collection.Name == "" || request.Collection.Title == "" { if request.Collection.Name == "" || request.Collection.Title == "" {
return nil, status.Errorf(codes.InvalidArgument, "name and title are required") return nil, status.Errorf(codes.InvalidArgument, "name and title are required")
} }
@ -137,14 +137,14 @@ func (s *APIV1Service) CreateCollection(ctx context.Context, request *apiv1pb.Cr
return nil, status.Errorf(codes.Internal, "failed to create collection, err: %v", err) return nil, status.Errorf(codes.Internal, "failed to create collection, err: %v", err)
} }
response := &apiv1pb.CreateCollectionResponse{ response := &v1pb.CreateCollectionResponse{
Collection: convertCollectionFromStore(collection), Collection: convertCollectionFromStore(collection),
} }
metric.Enqueue("collection create") metric.Enqueue("collection create")
return response, nil return response, nil
} }
func (s *APIV1Service) UpdateCollection(ctx context.Context, request *apiv1pb.UpdateCollectionRequest) (*apiv1pb.UpdateCollectionResponse, error) { func (s *APIV1Service) UpdateCollection(ctx context.Context, request *v1pb.UpdateCollectionRequest) (*v1pb.UpdateCollectionResponse, error) {
if request.UpdateMask == nil || len(request.UpdateMask.Paths) == 0 { if request.UpdateMask == nil || len(request.UpdateMask.Paths) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "updateMask is required") return nil, status.Errorf(codes.InvalidArgument, "updateMask is required")
} }
@ -189,13 +189,13 @@ func (s *APIV1Service) UpdateCollection(ctx context.Context, request *apiv1pb.Up
return nil, status.Errorf(codes.Internal, "failed to update collection, err: %v", err) return nil, status.Errorf(codes.Internal, "failed to update collection, err: %v", err)
} }
response := &apiv1pb.UpdateCollectionResponse{ response := &v1pb.UpdateCollectionResponse{
Collection: convertCollectionFromStore(collection), Collection: convertCollectionFromStore(collection),
} }
return response, nil return response, nil
} }
func (s *APIV1Service) DeleteCollection(ctx context.Context, request *apiv1pb.DeleteCollectionRequest) (*apiv1pb.DeleteCollectionResponse, error) { func (s *APIV1Service) DeleteCollection(ctx context.Context, request *v1pb.DeleteCollectionRequest) (*v1pb.DeleteCollectionResponse, error) {
user, err := getCurrentUser(ctx, s.Store) user, err := getCurrentUser(ctx, s.Store)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err) return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
@ -219,12 +219,12 @@ func (s *APIV1Service) DeleteCollection(ctx context.Context, request *apiv1pb.De
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to delete collection, err: %v", err) return nil, status.Errorf(codes.Internal, "failed to delete collection, err: %v", err)
} }
response := &apiv1pb.DeleteCollectionResponse{} response := &v1pb.DeleteCollectionResponse{}
return response, nil return response, nil
} }
func convertCollectionFromStore(collection *storepb.Collection) *apiv1pb.Collection { func convertCollectionFromStore(collection *storepb.Collection) *v1pb.Collection {
return &apiv1pb.Collection{ return &v1pb.Collection{
Id: collection.Id, Id: collection.Id,
CreatorId: collection.CreatorId, CreatorId: collection.CreatorId,
CreatedTime: timestamppb.New(time.Unix(collection.CreatedTs, 0)), CreatedTime: timestamppb.New(time.Unix(collection.CreatedTs, 0)),
@ -233,6 +233,6 @@ func convertCollectionFromStore(collection *storepb.Collection) *apiv1pb.Collect
Title: collection.Title, Title: collection.Title,
Description: collection.Description, Description: collection.Description,
ShortcutIds: collection.ShortcutIds, ShortcutIds: collection.ShortcutIds,
Visibility: apiv1pb.Visibility(collection.Visibility), Visibility: v1pb.Visibility(collection.Visibility),
} }
} }

View File

@ -3,18 +3,18 @@ package v1
import ( import (
"context" "context"
apiv1pb "github.com/yourselfhosted/slash/proto/gen/api/v1" v1pb "github.com/yourselfhosted/slash/proto/gen/api/v1"
"github.com/yourselfhosted/slash/store" "github.com/yourselfhosted/slash/store"
) )
func convertRowStatusFromStore(rowStatus store.RowStatus) apiv1pb.RowStatus { func convertRowStatusFromStore(rowStatus store.RowStatus) v1pb.RowStatus {
switch rowStatus { switch rowStatus {
case store.Normal: case store.Normal:
return apiv1pb.RowStatus_NORMAL return v1pb.RowStatus_NORMAL
case store.Archived: case store.Archived:
return apiv1pb.RowStatus_ARCHIVED return v1pb.RowStatus_ARCHIVED
default: default:
return apiv1pb.RowStatus_ROW_STATUS_UNSPECIFIED return v1pb.RowStatus_ROW_STATUS_UNSPECIFIED
} }
} }

View File

@ -0,0 +1,5 @@
package v1
const (
UserNamePrefix = "users/"
)

View File

@ -16,13 +16,13 @@ import (
"google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
apiv1pb "github.com/yourselfhosted/slash/proto/gen/api/v1" v1pb "github.com/yourselfhosted/slash/proto/gen/api/v1"
storepb "github.com/yourselfhosted/slash/proto/gen/store" storepb "github.com/yourselfhosted/slash/proto/gen/store"
"github.com/yourselfhosted/slash/server/metric" "github.com/yourselfhosted/slash/server/metric"
"github.com/yourselfhosted/slash/store" "github.com/yourselfhosted/slash/store"
) )
func (s *APIV1Service) ListShortcuts(ctx context.Context, _ *apiv1pb.ListShortcutsRequest) (*apiv1pb.ListShortcutsResponse, error) { func (s *APIV1Service) ListShortcuts(ctx context.Context, _ *v1pb.ListShortcutsRequest) (*v1pb.ListShortcutsResponse, error) {
user, err := getCurrentUser(ctx, s.Store) user, err := getCurrentUser(ctx, s.Store)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err) return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
@ -42,7 +42,7 @@ func (s *APIV1Service) ListShortcuts(ctx context.Context, _ *apiv1pb.ListShortcu
} }
shortcutList = append(shortcutList, visibleShortcutList...) shortcutList = append(shortcutList, visibleShortcutList...)
shortcuts := []*apiv1pb.Shortcut{} shortcuts := []*v1pb.Shortcut{}
for _, shortcut := range shortcutList { for _, shortcut := range shortcutList {
composedShortcut, err := s.convertShortcutFromStorepb(ctx, shortcut) composedShortcut, err := s.convertShortcutFromStorepb(ctx, shortcut)
if err != nil { if err != nil {
@ -51,13 +51,13 @@ func (s *APIV1Service) ListShortcuts(ctx context.Context, _ *apiv1pb.ListShortcu
shortcuts = append(shortcuts, composedShortcut) shortcuts = append(shortcuts, composedShortcut)
} }
response := &apiv1pb.ListShortcutsResponse{ response := &v1pb.ListShortcutsResponse{
Shortcuts: shortcuts, Shortcuts: shortcuts,
} }
return response, nil return response, nil
} }
func (s *APIV1Service) GetShortcut(ctx context.Context, request *apiv1pb.GetShortcutRequest) (*apiv1pb.GetShortcutResponse, error) { func (s *APIV1Service) GetShortcut(ctx context.Context, request *v1pb.GetShortcutRequest) (*v1pb.GetShortcutResponse, error) {
shortcut, err := s.Store.GetShortcut(ctx, &store.FindShortcut{ shortcut, err := s.Store.GetShortcut(ctx, &store.FindShortcut{
ID: &request.Id, ID: &request.Id,
}) })
@ -83,13 +83,13 @@ func (s *APIV1Service) GetShortcut(ctx context.Context, request *apiv1pb.GetShor
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to convert shortcut, err: %v", err) return nil, status.Errorf(codes.Internal, "failed to convert shortcut, err: %v", err)
} }
response := &apiv1pb.GetShortcutResponse{ response := &v1pb.GetShortcutResponse{
Shortcut: composedShortcut, Shortcut: composedShortcut,
} }
return response, nil return response, nil
} }
func (s *APIV1Service) GetShortcutByName(ctx context.Context, request *apiv1pb.GetShortcutByNameRequest) (*apiv1pb.GetShortcutByNameResponse, error) { func (s *APIV1Service) GetShortcutByName(ctx context.Context, request *v1pb.GetShortcutByNameRequest) (*v1pb.GetShortcutByNameResponse, error) {
shortcut, err := s.Store.GetShortcut(ctx, &store.FindShortcut{ shortcut, err := s.Store.GetShortcut(ctx, &store.FindShortcut{
Name: &request.Name, Name: &request.Name,
}) })
@ -120,13 +120,13 @@ func (s *APIV1Service) GetShortcutByName(ctx context.Context, request *apiv1pb.G
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to convert shortcut, err: %v", err) return nil, status.Errorf(codes.Internal, "failed to convert shortcut, err: %v", err)
} }
response := &apiv1pb.GetShortcutByNameResponse{ response := &v1pb.GetShortcutByNameResponse{
Shortcut: composedShortcut, Shortcut: composedShortcut,
} }
return response, nil return response, nil
} }
func (s *APIV1Service) CreateShortcut(ctx context.Context, request *apiv1pb.CreateShortcutRequest) (*apiv1pb.CreateShortcutResponse, error) { func (s *APIV1Service) CreateShortcut(ctx context.Context, request *v1pb.CreateShortcutRequest) (*v1pb.CreateShortcutResponse, error) {
if request.Shortcut.Name == "" || request.Shortcut.Link == "" { if request.Shortcut.Name == "" || request.Shortcut.Link == "" {
return nil, status.Errorf(codes.InvalidArgument, "name and link are required") return nil, status.Errorf(codes.InvalidArgument, "name and link are required")
} }
@ -151,8 +151,8 @@ func (s *APIV1Service) CreateShortcut(ctx context.Context, request *apiv1pb.Crea
return nil, status.Errorf(codes.Internal, "failed to get workspace setting, err: %v", err) return nil, status.Errorf(codes.Internal, "failed to get workspace setting, err: %v", err)
} }
workspaceSetting := getWorkspaceSettingResponse.Setting workspaceSetting := getWorkspaceSettingResponse.Setting
visibility := apiv1pb.Visibility_PRIVATE visibility := v1pb.Visibility_PRIVATE
if workspaceSetting.DefaultVisibility != apiv1pb.Visibility_VISIBILITY_UNSPECIFIED { if workspaceSetting.DefaultVisibility != v1pb.Visibility_VISIBILITY_UNSPECIFIED {
visibility = workspaceSetting.DefaultVisibility visibility = workspaceSetting.DefaultVisibility
} }
shortcutCreate.Visibility = storepb.Visibility(visibility) shortcutCreate.Visibility = storepb.Visibility(visibility)
@ -176,14 +176,14 @@ func (s *APIV1Service) CreateShortcut(ctx context.Context, request *apiv1pb.Crea
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to convert shortcut, err: %v", err) return nil, status.Errorf(codes.Internal, "failed to convert shortcut, err: %v", err)
} }
response := &apiv1pb.CreateShortcutResponse{ response := &v1pb.CreateShortcutResponse{
Shortcut: composedShortcut, Shortcut: composedShortcut,
} }
metric.Enqueue("shortcut create") metric.Enqueue("shortcut create")
return response, nil return response, nil
} }
func (s *APIV1Service) UpdateShortcut(ctx context.Context, request *apiv1pb.UpdateShortcutRequest) (*apiv1pb.UpdateShortcutResponse, error) { func (s *APIV1Service) UpdateShortcut(ctx context.Context, request *v1pb.UpdateShortcutRequest) (*v1pb.UpdateShortcutResponse, error) {
if request.UpdateMask == nil || len(request.UpdateMask.Paths) == 0 { if request.UpdateMask == nil || len(request.UpdateMask.Paths) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "updateMask is required") return nil, status.Errorf(codes.InvalidArgument, "updateMask is required")
} }
@ -243,13 +243,13 @@ func (s *APIV1Service) UpdateShortcut(ctx context.Context, request *apiv1pb.Upda
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to convert shortcut, err: %v", err) return nil, status.Errorf(codes.Internal, "failed to convert shortcut, err: %v", err)
} }
response := &apiv1pb.UpdateShortcutResponse{ response := &v1pb.UpdateShortcutResponse{
Shortcut: composedShortcut, Shortcut: composedShortcut,
} }
return response, nil return response, nil
} }
func (s *APIV1Service) DeleteShortcut(ctx context.Context, request *apiv1pb.DeleteShortcutRequest) (*apiv1pb.DeleteShortcutResponse, error) { func (s *APIV1Service) DeleteShortcut(ctx context.Context, request *v1pb.DeleteShortcutRequest) (*v1pb.DeleteShortcutResponse, error) {
user, err := getCurrentUser(ctx, s.Store) user, err := getCurrentUser(ctx, s.Store)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err) return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
@ -273,11 +273,11 @@ func (s *APIV1Service) DeleteShortcut(ctx context.Context, request *apiv1pb.Dele
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to delete shortcut, err: %v", err) return nil, status.Errorf(codes.Internal, "failed to delete shortcut, err: %v", err)
} }
response := &apiv1pb.DeleteShortcutResponse{} response := &v1pb.DeleteShortcutResponse{}
return response, nil return response, nil
} }
func (s *APIV1Service) GetShortcutAnalytics(ctx context.Context, request *apiv1pb.GetShortcutAnalyticsRequest) (*apiv1pb.GetShortcutAnalyticsResponse, error) { func (s *APIV1Service) GetShortcutAnalytics(ctx context.Context, request *v1pb.GetShortcutAnalyticsRequest) (*v1pb.GetShortcutAnalyticsResponse, error) {
shortcut, err := s.Store.GetShortcut(ctx, &store.FindShortcut{ shortcut, err := s.Store.GetShortcut(ctx, &store.FindShortcut{
ID: &request.Id, ID: &request.Id,
}) })
@ -326,7 +326,7 @@ func (s *APIV1Service) GetShortcutAnalytics(ctx context.Context, request *apiv1p
} }
metric.Enqueue("shortcut analytics") metric.Enqueue("shortcut analytics")
response := &apiv1pb.GetShortcutAnalyticsResponse{ response := &v1pb.GetShortcutAnalyticsResponse{
References: mapToAnalyticsSlice(referenceMap), References: mapToAnalyticsSlice(referenceMap),
Devices: mapToAnalyticsSlice(deviceMap), Devices: mapToAnalyticsSlice(deviceMap),
Browsers: mapToAnalyticsSlice(browserMap), Browsers: mapToAnalyticsSlice(browserMap),
@ -334,15 +334,15 @@ func (s *APIV1Service) GetShortcutAnalytics(ctx context.Context, request *apiv1p
return response, nil return response, nil
} }
func mapToAnalyticsSlice(m map[string]int32) []*apiv1pb.GetShortcutAnalyticsResponse_AnalyticsItem { func mapToAnalyticsSlice(m map[string]int32) []*v1pb.GetShortcutAnalyticsResponse_AnalyticsItem {
analyticsSlice := make([]*apiv1pb.GetShortcutAnalyticsResponse_AnalyticsItem, 0) analyticsSlice := make([]*v1pb.GetShortcutAnalyticsResponse_AnalyticsItem, 0)
for key, value := range m { for key, value := range m {
analyticsSlice = append(analyticsSlice, &apiv1pb.GetShortcutAnalyticsResponse_AnalyticsItem{ analyticsSlice = append(analyticsSlice, &v1pb.GetShortcutAnalyticsResponse_AnalyticsItem{
Name: key, Name: key,
Count: value, Count: value,
}) })
} }
slices.SortFunc(analyticsSlice, func(i, j *apiv1pb.GetShortcutAnalyticsResponse_AnalyticsItem) int { slices.SortFunc(analyticsSlice, func(i, j *v1pb.GetShortcutAnalyticsResponse_AnalyticsItem) int {
return int(i.Count - j.Count) return int(i.Count - j.Count)
}) })
return analyticsSlice return analyticsSlice
@ -398,20 +398,20 @@ func (s *APIV1Service) createShortcutCreateActivity(ctx context.Context, shortcu
return nil return nil
} }
func (s *APIV1Service) convertShortcutFromStorepb(ctx context.Context, shortcut *storepb.Shortcut) (*apiv1pb.Shortcut, error) { func (s *APIV1Service) convertShortcutFromStorepb(ctx context.Context, shortcut *storepb.Shortcut) (*v1pb.Shortcut, error) {
composedShortcut := &apiv1pb.Shortcut{ composedShortcut := &v1pb.Shortcut{
Id: shortcut.Id, Id: shortcut.Id,
CreatorId: shortcut.CreatorId, CreatorId: shortcut.CreatorId,
CreatedTime: timestamppb.New(time.Unix(shortcut.CreatedTs, 0)), CreatedTime: timestamppb.New(time.Unix(shortcut.CreatedTs, 0)),
UpdatedTime: timestamppb.New(time.Unix(shortcut.UpdatedTs, 0)), UpdatedTime: timestamppb.New(time.Unix(shortcut.UpdatedTs, 0)),
RowStatus: apiv1pb.RowStatus(shortcut.RowStatus), RowStatus: v1pb.RowStatus(shortcut.RowStatus),
Name: shortcut.Name, Name: shortcut.Name,
Link: shortcut.Link, Link: shortcut.Link,
Title: shortcut.Title, Title: shortcut.Title,
Tags: shortcut.Tags, Tags: shortcut.Tags,
Description: shortcut.Description, Description: shortcut.Description,
Visibility: apiv1pb.Visibility(shortcut.Visibility), Visibility: v1pb.Visibility(shortcut.Visibility),
OgMetadata: &apiv1pb.OpenGraphMetadata{ OgMetadata: &v1pb.OpenGraphMetadata{
Title: shortcut.OgMetadata.Title, Title: shortcut.OgMetadata.Title,
Description: shortcut.OgMetadata.Description, Description: shortcut.OgMetadata.Description,
Image: shortcut.OgMetadata.Image, Image: shortcut.OgMetadata.Image,

View File

@ -6,25 +6,25 @@ import (
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
apiv1pb "github.com/yourselfhosted/slash/proto/gen/api/v1" v1pb "github.com/yourselfhosted/slash/proto/gen/api/v1"
) )
func (s *APIV1Service) GetSubscription(ctx context.Context, _ *apiv1pb.GetSubscriptionRequest) (*apiv1pb.GetSubscriptionResponse, error) { func (s *APIV1Service) GetSubscription(ctx context.Context, _ *v1pb.GetSubscriptionRequest) (*v1pb.GetSubscriptionResponse, error) {
subscription, err := s.LicenseService.LoadSubscription(ctx) subscription, err := s.LicenseService.LoadSubscription(ctx)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to load subscription: %v", err) return nil, status.Errorf(codes.Internal, "failed to load subscription: %v", err)
} }
return &apiv1pb.GetSubscriptionResponse{ return &v1pb.GetSubscriptionResponse{
Subscription: subscription, Subscription: subscription,
}, nil }, nil
} }
func (s *APIV1Service) UpdateSubscription(ctx context.Context, request *apiv1pb.UpdateSubscriptionRequest) (*apiv1pb.UpdateSubscriptionResponse, error) { func (s *APIV1Service) UpdateSubscription(ctx context.Context, request *v1pb.UpdateSubscriptionRequest) (*v1pb.UpdateSubscriptionResponse, error) {
subscription, err := s.LicenseService.UpdateSubscription(ctx, request.LicenseKey) subscription, err := s.LicenseService.UpdateSubscription(ctx, request.LicenseKey)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to load subscription: %v", err) return nil, status.Errorf(codes.Internal, "failed to load subscription: %v", err)
} }
return &apiv1pb.UpdateSubscriptionResponse{ return &v1pb.UpdateSubscriptionResponse{
Subscription: subscription, Subscription: subscription,
}, nil }, nil
} }

View File

@ -12,7 +12,7 @@ import (
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
apiv1pb "github.com/yourselfhosted/slash/proto/gen/api/v1" v1pb "github.com/yourselfhosted/slash/proto/gen/api/v1"
storepb "github.com/yourselfhosted/slash/proto/gen/store" storepb "github.com/yourselfhosted/slash/proto/gen/store"
"github.com/yourselfhosted/slash/server/service/license" "github.com/yourselfhosted/slash/server/service/license"
"github.com/yourselfhosted/slash/store" "github.com/yourselfhosted/slash/store"
@ -23,23 +23,23 @@ const (
BotID = 0 BotID = 0
) )
func (s *APIV1Service) ListUsers(ctx context.Context, _ *apiv1pb.ListUsersRequest) (*apiv1pb.ListUsersResponse, error) { func (s *APIV1Service) ListUsers(ctx context.Context, _ *v1pb.ListUsersRequest) (*v1pb.ListUsersResponse, error) {
users, err := s.Store.ListUsers(ctx, &store.FindUser{}) users, err := s.Store.ListUsers(ctx, &store.FindUser{})
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to list users: %v", err) return nil, status.Errorf(codes.Internal, "failed to list users: %v", err)
} }
userMessages := []*apiv1pb.User{} userMessages := []*v1pb.User{}
for _, user := range users { for _, user := range users {
userMessages = append(userMessages, convertUserFromStore(user)) userMessages = append(userMessages, convertUserFromStore(user))
} }
response := &apiv1pb.ListUsersResponse{ response := &v1pb.ListUsersResponse{
Users: userMessages, Users: userMessages,
} }
return response, nil return response, nil
} }
func (s *APIV1Service) GetUser(ctx context.Context, request *apiv1pb.GetUserRequest) (*apiv1pb.GetUserResponse, error) { func (s *APIV1Service) GetUser(ctx context.Context, request *v1pb.GetUserRequest) (*v1pb.GetUserResponse, error) {
user, err := s.Store.GetUser(ctx, &store.FindUser{ user, err := s.Store.GetUser(ctx, &store.FindUser{
ID: &request.Id, ID: &request.Id,
}) })
@ -51,13 +51,13 @@ func (s *APIV1Service) GetUser(ctx context.Context, request *apiv1pb.GetUserRequ
} }
userMessage := convertUserFromStore(user) userMessage := convertUserFromStore(user)
response := &apiv1pb.GetUserResponse{ response := &v1pb.GetUserResponse{
User: userMessage, User: userMessage,
} }
return response, nil return response, nil
} }
func (s *APIV1Service) CreateUser(ctx context.Context, request *apiv1pb.CreateUserRequest) (*apiv1pb.CreateUserResponse, error) { func (s *APIV1Service) CreateUser(ctx context.Context, request *v1pb.CreateUserRequest) (*v1pb.CreateUserResponse, error) {
passwordHash, err := bcrypt.GenerateFromPassword([]byte(request.User.Password), bcrypt.DefaultCost) passwordHash, err := bcrypt.GenerateFromPassword([]byte(request.User.Password), bcrypt.DefaultCost)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to hash password: %v", err) return nil, status.Errorf(codes.Internal, "failed to hash password: %v", err)
@ -82,13 +82,13 @@ func (s *APIV1Service) CreateUser(ctx context.Context, request *apiv1pb.CreateUs
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to create user: %v", err) return nil, status.Errorf(codes.Internal, "failed to create user: %v", err)
} }
response := &apiv1pb.CreateUserResponse{ response := &v1pb.CreateUserResponse{
User: convertUserFromStore(user), User: convertUserFromStore(user),
} }
return response, nil return response, nil
} }
func (s *APIV1Service) UpdateUser(ctx context.Context, request *apiv1pb.UpdateUserRequest) (*apiv1pb.UpdateUserResponse, error) { func (s *APIV1Service) UpdateUser(ctx context.Context, request *v1pb.UpdateUserRequest) (*v1pb.UpdateUserResponse, error) {
user, err := getCurrentUser(ctx, s.Store) user, err := getCurrentUser(ctx, s.Store)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err) return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
@ -114,12 +114,12 @@ func (s *APIV1Service) UpdateUser(ctx context.Context, request *apiv1pb.UpdateUs
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to update user: %v", err) return nil, status.Errorf(codes.Internal, "failed to update user: %v", err)
} }
return &apiv1pb.UpdateUserResponse{ return &v1pb.UpdateUserResponse{
User: convertUserFromStore(user), User: convertUserFromStore(user),
}, nil }, nil
} }
func (s *APIV1Service) DeleteUser(ctx context.Context, request *apiv1pb.DeleteUserRequest) (*apiv1pb.DeleteUserResponse, error) { func (s *APIV1Service) DeleteUser(ctx context.Context, request *v1pb.DeleteUserRequest) (*v1pb.DeleteUserResponse, error) {
user, err := getCurrentUser(ctx, s.Store) user, err := getCurrentUser(ctx, s.Store)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err) return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
@ -131,11 +131,11 @@ func (s *APIV1Service) DeleteUser(ctx context.Context, request *apiv1pb.DeleteUs
if err := s.Store.DeleteUser(ctx, &store.DeleteUser{ID: request.Id}); err != nil { if err := s.Store.DeleteUser(ctx, &store.DeleteUser{ID: request.Id}); err != nil {
return nil, status.Errorf(codes.Internal, "failed to delete user: %v", err) return nil, status.Errorf(codes.Internal, "failed to delete user: %v", err)
} }
response := &apiv1pb.DeleteUserResponse{} response := &v1pb.DeleteUserResponse{}
return response, nil return response, nil
} }
func (s *APIV1Service) ListUserAccessTokens(ctx context.Context, request *apiv1pb.ListUserAccessTokensRequest) (*apiv1pb.ListUserAccessTokensResponse, error) { func (s *APIV1Service) ListUserAccessTokens(ctx context.Context, request *v1pb.ListUserAccessTokensRequest) (*v1pb.ListUserAccessTokensResponse, error) {
user, err := getCurrentUser(ctx, s.Store) user, err := getCurrentUser(ctx, s.Store)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err) return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
@ -149,7 +149,7 @@ func (s *APIV1Service) ListUserAccessTokens(ctx context.Context, request *apiv1p
return nil, status.Errorf(codes.Internal, "failed to list access tokens: %v", err) return nil, status.Errorf(codes.Internal, "failed to list access tokens: %v", err)
} }
accessTokens := []*apiv1pb.UserAccessToken{} accessTokens := []*v1pb.UserAccessToken{}
for _, userAccessToken := range userAccessTokens { for _, userAccessToken := range userAccessTokens {
claims := &ClaimsMessage{} claims := &ClaimsMessage{}
_, err := jwt.ParseWithClaims(userAccessToken.AccessToken, claims, func(t *jwt.Token) (any, error) { _, err := jwt.ParseWithClaims(userAccessToken.AccessToken, claims, func(t *jwt.Token) (any, error) {
@ -168,7 +168,7 @@ func (s *APIV1Service) ListUserAccessTokens(ctx context.Context, request *apiv1p
continue continue
} }
userAccessToken := &apiv1pb.UserAccessToken{ userAccessToken := &v1pb.UserAccessToken{
AccessToken: userAccessToken.AccessToken, AccessToken: userAccessToken.AccessToken,
Description: userAccessToken.Description, Description: userAccessToken.Description,
IssuedAt: timestamppb.New(claims.IssuedAt.Time), IssuedAt: timestamppb.New(claims.IssuedAt.Time),
@ -180,16 +180,16 @@ func (s *APIV1Service) ListUserAccessTokens(ctx context.Context, request *apiv1p
} }
// Sort by issued time in descending order. // Sort by issued time in descending order.
slices.SortFunc(accessTokens, func(i, j *apiv1pb.UserAccessToken) int { slices.SortFunc(accessTokens, func(i, j *v1pb.UserAccessToken) int {
return int(i.IssuedAt.Seconds - j.IssuedAt.Seconds) return int(i.IssuedAt.Seconds - j.IssuedAt.Seconds)
}) })
response := &apiv1pb.ListUserAccessTokensResponse{ response := &v1pb.ListUserAccessTokensResponse{
AccessTokens: accessTokens, AccessTokens: accessTokens,
} }
return response, nil return response, nil
} }
func (s *APIV1Service) CreateUserAccessToken(ctx context.Context, request *apiv1pb.CreateUserAccessTokenRequest) (*apiv1pb.CreateUserAccessTokenResponse, error) { func (s *APIV1Service) CreateUserAccessToken(ctx context.Context, request *v1pb.CreateUserAccessTokenRequest) (*v1pb.CreateUserAccessTokenResponse, error) {
user, err := getCurrentUser(ctx, s.Store) user, err := getCurrentUser(ctx, s.Store)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err) return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
@ -228,7 +228,7 @@ func (s *APIV1Service) CreateUserAccessToken(ctx context.Context, request *apiv1
return nil, status.Errorf(codes.Internal, "failed to upsert access token to store: %v", err) return nil, status.Errorf(codes.Internal, "failed to upsert access token to store: %v", err)
} }
userAccessToken := &apiv1pb.UserAccessToken{ userAccessToken := &v1pb.UserAccessToken{
AccessToken: accessToken, AccessToken: accessToken,
Description: request.Description, Description: request.Description,
IssuedAt: timestamppb.New(claims.IssuedAt.Time), IssuedAt: timestamppb.New(claims.IssuedAt.Time),
@ -236,13 +236,13 @@ func (s *APIV1Service) CreateUserAccessToken(ctx context.Context, request *apiv1
if claims.ExpiresAt != nil { if claims.ExpiresAt != nil {
userAccessToken.ExpiresAt = timestamppb.New(claims.ExpiresAt.Time) userAccessToken.ExpiresAt = timestamppb.New(claims.ExpiresAt.Time)
} }
response := &apiv1pb.CreateUserAccessTokenResponse{ response := &v1pb.CreateUserAccessTokenResponse{
AccessToken: userAccessToken, AccessToken: userAccessToken,
} }
return response, nil return response, nil
} }
func (s *APIV1Service) DeleteUserAccessToken(ctx context.Context, request *apiv1pb.DeleteUserAccessTokenRequest) (*apiv1pb.DeleteUserAccessTokenResponse, error) { func (s *APIV1Service) DeleteUserAccessToken(ctx context.Context, request *v1pb.DeleteUserAccessTokenRequest) (*v1pb.DeleteUserAccessTokenResponse, error) {
user, err := getCurrentUser(ctx, s.Store) user, err := getCurrentUser(ctx, s.Store)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err) return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
@ -270,7 +270,7 @@ func (s *APIV1Service) DeleteUserAccessToken(ctx context.Context, request *apiv1
return nil, status.Errorf(codes.Internal, "failed to upsert user setting: %v", err) return nil, status.Errorf(codes.Internal, "failed to upsert user setting: %v", err)
} }
return &apiv1pb.DeleteUserAccessTokenResponse{}, nil return &v1pb.DeleteUserAccessTokenResponse{}, nil
} }
func (s *APIV1Service) UpsertAccessTokenToStore(ctx context.Context, user *store.User, accessToken, description string) error { func (s *APIV1Service) UpsertAccessTokenToStore(ctx context.Context, user *store.User, accessToken, description string) error {
@ -297,8 +297,8 @@ func (s *APIV1Service) UpsertAccessTokenToStore(ctx context.Context, user *store
return nil return nil
} }
func convertUserFromStore(user *store.User) *apiv1pb.User { func convertUserFromStore(user *store.User) *v1pb.User {
return &apiv1pb.User{ return &v1pb.User{
Id: int32(user.ID), Id: int32(user.ID),
RowStatus: convertRowStatusFromStore(user.RowStatus), RowStatus: convertRowStatusFromStore(user.RowStatus),
CreatedTime: timestamppb.New(time.Unix(user.CreatedTs, 0)), CreatedTime: timestamppb.New(time.Unix(user.CreatedTs, 0)),
@ -309,13 +309,13 @@ func convertUserFromStore(user *store.User) *apiv1pb.User {
} }
} }
func convertUserRoleFromStore(role store.Role) apiv1pb.Role { func convertUserRoleFromStore(role store.Role) v1pb.Role {
switch role { switch role {
case store.RoleAdmin: case store.RoleAdmin:
return apiv1pb.Role_ADMIN return v1pb.Role_ADMIN
case store.RoleUser: case store.RoleUser:
return apiv1pb.Role_USER return v1pb.Role_USER
default: default:
return apiv1pb.Role_ROLE_UNSPECIFIED return v1pb.Role_ROLE_UNSPECIFIED
} }
} }

View File

@ -7,22 +7,22 @@ import (
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
apiv1pb "github.com/yourselfhosted/slash/proto/gen/api/v1" v1pb "github.com/yourselfhosted/slash/proto/gen/api/v1"
storepb "github.com/yourselfhosted/slash/proto/gen/store" storepb "github.com/yourselfhosted/slash/proto/gen/store"
"github.com/yourselfhosted/slash/store" "github.com/yourselfhosted/slash/store"
) )
func (s *APIV1Service) GetUserSetting(ctx context.Context, request *apiv1pb.GetUserSettingRequest) (*apiv1pb.GetUserSettingResponse, error) { func (s *APIV1Service) GetUserSetting(ctx context.Context, request *v1pb.GetUserSettingRequest) (*v1pb.GetUserSettingResponse, error) {
userSetting, err := getUserSetting(ctx, s.Store, request.Id) userSetting, err := getUserSetting(ctx, s.Store, request.Id)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get user setting: %v", err) return nil, status.Errorf(codes.Internal, "failed to get user setting: %v", err)
} }
return &apiv1pb.GetUserSettingResponse{ return &v1pb.GetUserSettingResponse{
UserSetting: userSetting, UserSetting: userSetting,
}, nil }, nil
} }
func (s *APIV1Service) UpdateUserSetting(ctx context.Context, request *apiv1pb.UpdateUserSettingRequest) (*apiv1pb.UpdateUserSettingResponse, error) { func (s *APIV1Service) UpdateUserSetting(ctx context.Context, request *v1pb.UpdateUserSettingRequest) (*v1pb.UpdateUserSettingResponse, error) {
if request.UpdateMask == nil || len(request.UpdateMask.Paths) == 0 { if request.UpdateMask == nil || len(request.UpdateMask.Paths) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "update mask is empty") return nil, status.Errorf(codes.InvalidArgument, "update mask is empty")
} }
@ -61,12 +61,12 @@ func (s *APIV1Service) UpdateUserSetting(ctx context.Context, request *apiv1pb.U
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get user setting: %v", err) return nil, status.Errorf(codes.Internal, "failed to get user setting: %v", err)
} }
return &apiv1pb.UpdateUserSettingResponse{ return &v1pb.UpdateUserSettingResponse{
UserSetting: userSetting, UserSetting: userSetting,
}, nil }, nil
} }
func getUserSetting(ctx context.Context, s *store.Store, userID int32) (*apiv1pb.UserSetting, error) { func getUserSetting(ctx context.Context, s *store.Store, userID int32) (*v1pb.UserSetting, error) {
userSettings, err := s.ListUserSettings(ctx, &store.FindUserSetting{ userSettings, err := s.ListUserSettings(ctx, &store.FindUserSetting{
UserID: &userID, UserID: &userID,
}) })
@ -74,10 +74,10 @@ func getUserSetting(ctx context.Context, s *store.Store, userID int32) (*apiv1pb
return nil, errors.Wrap(err, "failed to find user setting") return nil, errors.Wrap(err, "failed to find user setting")
} }
userSetting := &apiv1pb.UserSetting{ userSetting := &v1pb.UserSetting{
Id: userID, Id: userID,
Locale: apiv1pb.UserSetting_LOCALE_EN, Locale: v1pb.UserSetting_LOCALE_EN,
ColorTheme: apiv1pb.UserSetting_COLOR_THEME_SYSTEM, ColorTheme: v1pb.UserSetting_COLOR_THEME_SYSTEM,
} }
for _, setting := range userSettings { for _, setting := range userSettings {
if setting.Key == storepb.UserSettingKey_LOCALE { if setting.Key == storepb.UserSettingKey_LOCALE {
@ -89,54 +89,54 @@ func getUserSetting(ctx context.Context, s *store.Store, userID int32) (*apiv1pb
return userSetting, nil return userSetting, nil
} }
func convertUserSettingLocaleToStore(locale apiv1pb.UserSetting_Locale) storepb.LocaleUserSetting { func convertUserSettingLocaleToStore(locale v1pb.UserSetting_Locale) storepb.LocaleUserSetting {
switch locale { switch locale {
case apiv1pb.UserSetting_LOCALE_EN: case v1pb.UserSetting_LOCALE_EN:
return storepb.LocaleUserSetting_EN return storepb.LocaleUserSetting_EN
case apiv1pb.UserSetting_LOCALE_ZH: case v1pb.UserSetting_LOCALE_ZH:
return storepb.LocaleUserSetting_ZH return storepb.LocaleUserSetting_ZH
case apiv1pb.UserSetting_LOCALE_FR: case v1pb.UserSetting_LOCALE_FR:
return storepb.LocaleUserSetting_FR return storepb.LocaleUserSetting_FR
default: default:
return storepb.LocaleUserSetting_LOCALE_USER_SETTING_UNSPECIFIED return storepb.LocaleUserSetting_LOCALE_USER_SETTING_UNSPECIFIED
} }
} }
func convertUserSettingLocaleFromStore(locale storepb.LocaleUserSetting) apiv1pb.UserSetting_Locale { func convertUserSettingLocaleFromStore(locale storepb.LocaleUserSetting) v1pb.UserSetting_Locale {
switch locale { switch locale {
case storepb.LocaleUserSetting_EN: case storepb.LocaleUserSetting_EN:
return apiv1pb.UserSetting_LOCALE_EN return v1pb.UserSetting_LOCALE_EN
case storepb.LocaleUserSetting_ZH: case storepb.LocaleUserSetting_ZH:
return apiv1pb.UserSetting_LOCALE_ZH return v1pb.UserSetting_LOCALE_ZH
case storepb.LocaleUserSetting_FR: case storepb.LocaleUserSetting_FR:
return apiv1pb.UserSetting_LOCALE_FR return v1pb.UserSetting_LOCALE_FR
default: default:
return apiv1pb.UserSetting_LOCALE_UNSPECIFIED return v1pb.UserSetting_LOCALE_UNSPECIFIED
} }
} }
func convertUserSettingColorThemeToStore(colorTheme apiv1pb.UserSetting_ColorTheme) storepb.ColorThemeUserSetting { func convertUserSettingColorThemeToStore(colorTheme v1pb.UserSetting_ColorTheme) storepb.ColorThemeUserSetting {
switch colorTheme { switch colorTheme {
case apiv1pb.UserSetting_COLOR_THEME_SYSTEM: case v1pb.UserSetting_COLOR_THEME_SYSTEM:
return storepb.ColorThemeUserSetting_SYSTEM return storepb.ColorThemeUserSetting_SYSTEM
case apiv1pb.UserSetting_COLOR_THEME_LIGHT: case v1pb.UserSetting_COLOR_THEME_LIGHT:
return storepb.ColorThemeUserSetting_LIGHT return storepb.ColorThemeUserSetting_LIGHT
case apiv1pb.UserSetting_COLOR_THEME_DARK: case v1pb.UserSetting_COLOR_THEME_DARK:
return storepb.ColorThemeUserSetting_DARK return storepb.ColorThemeUserSetting_DARK
default: default:
return storepb.ColorThemeUserSetting_COLOR_THEME_USER_SETTING_UNSPECIFIED return storepb.ColorThemeUserSetting_COLOR_THEME_USER_SETTING_UNSPECIFIED
} }
} }
func convertUserSettingColorThemeFromStore(colorTheme storepb.ColorThemeUserSetting) apiv1pb.UserSetting_ColorTheme { func convertUserSettingColorThemeFromStore(colorTheme storepb.ColorThemeUserSetting) v1pb.UserSetting_ColorTheme {
switch colorTheme { switch colorTheme {
case storepb.ColorThemeUserSetting_SYSTEM: case storepb.ColorThemeUserSetting_SYSTEM:
return apiv1pb.UserSetting_COLOR_THEME_SYSTEM return v1pb.UserSetting_COLOR_THEME_SYSTEM
case storepb.ColorThemeUserSetting_LIGHT: case storepb.ColorThemeUserSetting_LIGHT:
return apiv1pb.UserSetting_COLOR_THEME_LIGHT return v1pb.UserSetting_COLOR_THEME_LIGHT
case storepb.ColorThemeUserSetting_DARK: case storepb.ColorThemeUserSetting_DARK:
return apiv1pb.UserSetting_COLOR_THEME_DARK return v1pb.UserSetting_COLOR_THEME_DARK
default: default:
return apiv1pb.UserSetting_COLOR_THEME_UNSPECIFIED return v1pb.UserSetting_COLOR_THEME_UNSPECIFIED
} }
} }

View File

@ -11,20 +11,20 @@ import (
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/reflection" "google.golang.org/grpc/reflection"
apiv1pb "github.com/yourselfhosted/slash/proto/gen/api/v1" v1pb "github.com/yourselfhosted/slash/proto/gen/api/v1"
"github.com/yourselfhosted/slash/server/profile" "github.com/yourselfhosted/slash/server/profile"
"github.com/yourselfhosted/slash/server/service/license" "github.com/yourselfhosted/slash/server/service/license"
"github.com/yourselfhosted/slash/store" "github.com/yourselfhosted/slash/store"
) )
type APIV1Service struct { type APIV1Service struct {
apiv1pb.UnimplementedWorkspaceServiceServer v1pb.UnimplementedWorkspaceServiceServer
apiv1pb.UnimplementedSubscriptionServiceServer v1pb.UnimplementedSubscriptionServiceServer
apiv1pb.UnimplementedAuthServiceServer v1pb.UnimplementedAuthServiceServer
apiv1pb.UnimplementedUserServiceServer v1pb.UnimplementedUserServiceServer
apiv1pb.UnimplementedUserSettingServiceServer v1pb.UnimplementedUserSettingServiceServer
apiv1pb.UnimplementedShortcutServiceServer v1pb.UnimplementedShortcutServiceServer
apiv1pb.UnimplementedCollectionServiceServer v1pb.UnimplementedCollectionServiceServer
Secret string Secret string
Profile *profile.Profile Profile *profile.Profile
@ -52,13 +52,13 @@ func NewAPIV1Service(secret string, profile *profile.Profile, store *store.Store
grpcServerPort: grpcServerPort, grpcServerPort: grpcServerPort,
} }
apiv1pb.RegisterSubscriptionServiceServer(grpcServer, apiV1Service) v1pb.RegisterSubscriptionServiceServer(grpcServer, apiV1Service)
apiv1pb.RegisterWorkspaceServiceServer(grpcServer, apiV1Service) v1pb.RegisterWorkspaceServiceServer(grpcServer, apiV1Service)
apiv1pb.RegisterAuthServiceServer(grpcServer, apiV1Service) v1pb.RegisterAuthServiceServer(grpcServer, apiV1Service)
apiv1pb.RegisterUserServiceServer(grpcServer, apiV1Service) v1pb.RegisterUserServiceServer(grpcServer, apiV1Service)
apiv1pb.RegisterUserSettingServiceServer(grpcServer, apiV1Service) v1pb.RegisterUserSettingServiceServer(grpcServer, apiV1Service)
apiv1pb.RegisterShortcutServiceServer(grpcServer, apiV1Service) v1pb.RegisterShortcutServiceServer(grpcServer, apiV1Service)
apiv1pb.RegisterCollectionServiceServer(grpcServer, apiV1Service) v1pb.RegisterCollectionServiceServer(grpcServer, apiV1Service)
reflection.Register(grpcServer) reflection.Register(grpcServer)
return apiV1Service return apiV1Service
@ -81,25 +81,25 @@ func (s *APIV1Service) RegisterGateway(_ context.Context, e *echo.Echo) error {
} }
gwMux := runtime.NewServeMux() gwMux := runtime.NewServeMux()
if err := apiv1pb.RegisterSubscriptionServiceHandler(context.Background(), gwMux, conn); err != nil { if err := v1pb.RegisterSubscriptionServiceHandler(context.Background(), gwMux, conn); err != nil {
return err return err
} }
if err := apiv1pb.RegisterWorkspaceServiceHandler(context.Background(), gwMux, conn); err != nil { if err := v1pb.RegisterWorkspaceServiceHandler(context.Background(), gwMux, conn); err != nil {
return err return err
} }
if err := apiv1pb.RegisterAuthServiceHandler(context.Background(), gwMux, conn); err != nil { if err := v1pb.RegisterAuthServiceHandler(context.Background(), gwMux, conn); err != nil {
return err return err
} }
if err := apiv1pb.RegisterUserServiceHandler(context.Background(), gwMux, conn); err != nil { if err := v1pb.RegisterUserServiceHandler(context.Background(), gwMux, conn); err != nil {
return err return err
} }
if err := apiv1pb.RegisterUserSettingServiceHandler(context.Background(), gwMux, conn); err != nil { if err := v1pb.RegisterUserSettingServiceHandler(context.Background(), gwMux, conn); err != nil {
return err return err
} }
if err := apiv1pb.RegisterShortcutServiceHandler(context.Background(), gwMux, conn); err != nil { if err := v1pb.RegisterShortcutServiceHandler(context.Background(), gwMux, conn); err != nil {
return err return err
} }
if err := apiv1pb.RegisterCollectionServiceHandler(context.Background(), gwMux, conn); err != nil { if err := v1pb.RegisterCollectionServiceHandler(context.Background(), gwMux, conn); err != nil {
return err return err
} }
e.Any("/api/v1/*", echo.WrapHandler(gwMux)) e.Any("/api/v1/*", echo.WrapHandler(gwMux))

View File

@ -2,20 +2,22 @@ package v1
import ( import (
"context" "context"
"fmt"
"github.com/pkg/errors"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
apiv1pb "github.com/yourselfhosted/slash/proto/gen/api/v1" v1pb "github.com/yourselfhosted/slash/proto/gen/api/v1"
storepb "github.com/yourselfhosted/slash/proto/gen/store" storepb "github.com/yourselfhosted/slash/proto/gen/store"
"github.com/yourselfhosted/slash/store" "github.com/yourselfhosted/slash/store"
) )
func (s *APIV1Service) GetWorkspaceProfile(ctx context.Context, _ *apiv1pb.GetWorkspaceProfileRequest) (*apiv1pb.GetWorkspaceProfileResponse, error) { func (s *APIV1Service) GetWorkspaceProfile(ctx context.Context, _ *v1pb.GetWorkspaceProfileRequest) (*v1pb.GetWorkspaceProfileResponse, error) {
profile := &apiv1pb.WorkspaceProfile{ profile := &v1pb.WorkspaceProfile{
Mode: s.Profile.Mode, Mode: s.Profile.Mode,
Version: s.Profile.Version, Version: s.Profile.Version,
Plan: apiv1pb.PlanType_FREE, Plan: v1pb.PlanType_FREE,
} }
// Load subscription plan from license service. // Load subscription plan from license service.
@ -25,7 +27,7 @@ func (s *APIV1Service) GetWorkspaceProfile(ctx context.Context, _ *apiv1pb.GetWo
} }
profile.Plan = subscription.Plan profile.Plan = subscription.Plan
workspaceSetting, err := s.GetWorkspaceSetting(ctx, &apiv1pb.GetWorkspaceSettingRequest{}) workspaceSetting, err := s.GetWorkspaceSetting(ctx, &v1pb.GetWorkspaceSettingRequest{})
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get workspace setting: %v", err) return nil, status.Errorf(codes.Internal, "failed to get workspace setting: %v", err)
} }
@ -36,12 +38,20 @@ func (s *APIV1Service) GetWorkspaceProfile(ctx context.Context, _ *apiv1pb.GetWo
profile.CustomScript = setting.GetCustomScript() profile.CustomScript = setting.GetCustomScript()
profile.FaviconProvider = setting.GetFaviconProvider() profile.FaviconProvider = setting.GetFaviconProvider()
} }
return &apiv1pb.GetWorkspaceProfileResponse{ owner, err := s.GetInstanceOwner(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get instance owner: %v", err)
}
if owner != nil {
profile.Owner = fmt.Sprintf("%s%d", UserNamePrefix, owner.Id)
}
return &v1pb.GetWorkspaceProfileResponse{
Profile: profile, Profile: profile,
}, nil }, nil
} }
func (s *APIV1Service) GetWorkspaceSetting(ctx context.Context, _ *apiv1pb.GetWorkspaceSettingRequest) (*apiv1pb.GetWorkspaceSettingResponse, error) { func (s *APIV1Service) GetWorkspaceSetting(ctx context.Context, _ *v1pb.GetWorkspaceSettingRequest) (*v1pb.GetWorkspaceSettingResponse, error) {
isAdmin := false isAdmin := false
userID, ok := ctx.Value(userIDContextKey).(int32) userID, ok := ctx.Value(userIDContextKey).(int32)
if ok { if ok {
@ -57,7 +67,7 @@ func (s *APIV1Service) GetWorkspaceSetting(ctx context.Context, _ *apiv1pb.GetWo
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to list workspace settings: %v", err) return nil, status.Errorf(codes.Internal, "failed to list workspace settings: %v", err)
} }
workspaceSetting := &apiv1pb.WorkspaceSetting{ workspaceSetting := &v1pb.WorkspaceSetting{
EnableSignup: true, EnableSignup: true,
} }
for _, v := range workspaceSettings { for _, v := range workspaceSettings {
@ -70,7 +80,7 @@ func (s *APIV1Service) GetWorkspaceSetting(ctx context.Context, _ *apiv1pb.GetWo
} else if v.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_SCRIPT { } else if v.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_SCRIPT {
workspaceSetting.CustomScript = v.GetCustomScript() workspaceSetting.CustomScript = v.GetCustomScript()
} else if v.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_DEFAULT_VISIBILITY { } else if v.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_DEFAULT_VISIBILITY {
workspaceSetting.DefaultVisibility = apiv1pb.Visibility(v.GetDefaultVisibility()) workspaceSetting.DefaultVisibility = v1pb.Visibility(v.GetDefaultVisibility())
} else if v.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_FAVICON_PROVIDER { } else if v.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_FAVICON_PROVIDER {
workspaceSetting.FaviconProvider = v.GetFaviconProvider() workspaceSetting.FaviconProvider = v.GetFaviconProvider()
} else if isAdmin { } else if isAdmin {
@ -80,12 +90,12 @@ func (s *APIV1Service) GetWorkspaceSetting(ctx context.Context, _ *apiv1pb.GetWo
} }
} }
} }
return &apiv1pb.GetWorkspaceSettingResponse{ return &v1pb.GetWorkspaceSettingResponse{
Setting: workspaceSetting, Setting: workspaceSetting,
}, nil }, nil
} }
func (s *APIV1Service) UpdateWorkspaceSetting(ctx context.Context, request *apiv1pb.UpdateWorkspaceSettingRequest) (*apiv1pb.UpdateWorkspaceSettingResponse, error) { func (s *APIV1Service) UpdateWorkspaceSetting(ctx context.Context, request *v1pb.UpdateWorkspaceSettingRequest) (*v1pb.UpdateWorkspaceSettingResponse, error) {
if request.UpdateMask == nil || len(request.UpdateMask.Paths) == 0 { if request.UpdateMask == nil || len(request.UpdateMask.Paths) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "update mask is empty") return nil, status.Errorf(codes.InvalidArgument, "update mask is empty")
} }
@ -159,11 +169,33 @@ func (s *APIV1Service) UpdateWorkspaceSetting(ctx context.Context, request *apiv
} }
} }
getWorkspaceSettingResponse, err := s.GetWorkspaceSetting(ctx, &apiv1pb.GetWorkspaceSettingRequest{}) getWorkspaceSettingResponse, err := s.GetWorkspaceSetting(ctx, &v1pb.GetWorkspaceSettingRequest{})
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get workspace setting: %v", err) return nil, status.Errorf(codes.Internal, "failed to get workspace setting: %v", err)
} }
return &apiv1pb.UpdateWorkspaceSettingResponse{ return &v1pb.UpdateWorkspaceSettingResponse{
Setting: getWorkspaceSettingResponse.Setting, Setting: getWorkspaceSettingResponse.Setting,
}, nil }, nil
} }
var ownerCache *v1pb.User
func (s *APIV1Service) GetInstanceOwner(ctx context.Context) (*v1pb.User, error) {
if ownerCache != nil {
return ownerCache, nil
}
adminRole := store.RoleAdmin
user, err := s.Store.GetUser(ctx, &store.FindUser{
Role: &adminRole,
})
if err != nil {
return nil, errors.Wrapf(err, "failed to find admin")
}
if user == nil {
return nil, nil
}
ownerCache = convertUserFromStore(user)
return ownerCache, nil
}

View File

@ -9,7 +9,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
apiv1pb "github.com/yourselfhosted/slash/proto/gen/api/v1" v1pb "github.com/yourselfhosted/slash/proto/gen/api/v1"
storepb "github.com/yourselfhosted/slash/proto/gen/store" storepb "github.com/yourselfhosted/slash/proto/gen/store"
"github.com/yourselfhosted/slash/server/profile" "github.com/yourselfhosted/slash/server/profile"
"github.com/yourselfhosted/slash/server/service/license/lemonsqueezy" "github.com/yourselfhosted/slash/server/service/license/lemonsqueezy"
@ -23,7 +23,7 @@ type LicenseService struct {
Profile *profile.Profile Profile *profile.Profile
Store *store.Store Store *store.Store
cachedSubscription *apiv1pb.Subscription cachedSubscription *v1pb.Subscription
} }
// NewLicenseService creates a new LicenseService. // NewLicenseService creates a new LicenseService.
@ -31,21 +31,21 @@ func NewLicenseService(profile *profile.Profile, store *store.Store) *LicenseSer
return &LicenseService{ return &LicenseService{
Profile: profile, Profile: profile,
Store: store, Store: store,
cachedSubscription: &apiv1pb.Subscription{ cachedSubscription: &v1pb.Subscription{
Plan: apiv1pb.PlanType_FREE, Plan: v1pb.PlanType_FREE,
}, },
} }
} }
func (s *LicenseService) LoadSubscription(ctx context.Context) (*apiv1pb.Subscription, error) { func (s *LicenseService) LoadSubscription(ctx context.Context) (*v1pb.Subscription, error) {
workspaceSetting, err := s.Store.GetWorkspaceSetting(ctx, &store.FindWorkspaceSetting{ workspaceSetting, err := s.Store.GetWorkspaceSetting(ctx, &store.FindWorkspaceSetting{
Key: storepb.WorkspaceSettingKey_WORKSPACE_SETTING_LICENSE_KEY, Key: storepb.WorkspaceSettingKey_WORKSPACE_SETTING_LICENSE_KEY,
}) })
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to get workspace setting") return nil, errors.Wrap(err, "failed to get workspace setting")
} }
subscription := &apiv1pb.Subscription{ subscription := &v1pb.Subscription{
Plan: apiv1pb.PlanType_FREE, Plan: v1pb.PlanType_FREE,
} }
licenseKey := "" licenseKey := ""
if workspaceSetting != nil { if workspaceSetting != nil {
@ -68,7 +68,7 @@ func (s *LicenseService) LoadSubscription(ctx context.Context) (*apiv1pb.Subscri
return subscription, nil return subscription, nil
} }
func (s *LicenseService) UpdateSubscription(ctx context.Context, licenseKey string) (*apiv1pb.Subscription, error) { func (s *LicenseService) UpdateSubscription(ctx context.Context, licenseKey string) (*v1pb.Subscription, error) {
if licenseKey == "" { if licenseKey == "" {
return nil, errors.New("license key is required") return nil, errors.New("license key is required")
} }
@ -91,12 +91,12 @@ func (s *LicenseService) UpdateSubscription(ctx context.Context, licenseKey stri
return s.LoadSubscription(ctx) return s.LoadSubscription(ctx)
} }
func (s *LicenseService) GetSubscription(ctx context.Context) (*apiv1pb.Subscription, error) { func (s *LicenseService) GetSubscription(ctx context.Context) (*v1pb.Subscription, error) {
subscription, err := s.LoadSubscription(ctx) subscription, err := s.LoadSubscription(ctx)
if err != nil || subscription.Plan == apiv1pb.PlanType_PLAN_TYPE_UNSPECIFIED { if err != nil || subscription.Plan == v1pb.PlanType_PLAN_TYPE_UNSPECIFIED {
// nolint // nolint
return &apiv1pb.Subscription{ return &v1pb.Subscription{
Plan: apiv1pb.PlanType_FREE, Plan: v1pb.PlanType_FREE,
}, nil }, nil
} }
return subscription, nil return subscription, nil
@ -111,7 +111,7 @@ func (s *LicenseService) IsFeatureEnabled(feature FeatureType) bool {
} }
type ValidateResult struct { type ValidateResult struct {
Plan apiv1pb.PlanType Plan v1pb.PlanType
ExpiresTime time.Time ExpiresTime time.Time
} }
@ -127,12 +127,12 @@ func validateLicenseKey(licenseKey string) (*ValidateResult, error) {
// Try to parse the license key as a JWT token. // Try to parse the license key as a JWT token.
claims, _ := parseLicenseKey(licenseKey) claims, _ := parseLicenseKey(licenseKey)
if claims != nil { if claims != nil {
plan := apiv1pb.PlanType(apiv1pb.PlanType_value[claims.Plan]) plan := v1pb.PlanType(v1pb.PlanType_value[claims.Plan])
if plan == apiv1pb.PlanType_PLAN_TYPE_UNSPECIFIED { if plan == v1pb.PlanType_PLAN_TYPE_UNSPECIFIED {
return nil, errors.New("invalid plan") return nil, errors.New("invalid plan")
} }
return &ValidateResult{ return &ValidateResult{
Plan: apiv1pb.PlanType(apiv1pb.PlanType_value[claims.Plan]), Plan: v1pb.PlanType(v1pb.PlanType_value[claims.Plan]),
ExpiresTime: claims.ExpiresAt.Time, ExpiresTime: claims.ExpiresAt.Time,
}, nil }, nil
} }
@ -144,7 +144,7 @@ func validateLicenseKey(licenseKey string) (*ValidateResult, error) {
} }
if validateResponse.Valid { if validateResponse.Valid {
result := &ValidateResult{ result := &ValidateResult{
Plan: apiv1pb.PlanType_PRO, Plan: v1pb.PlanType_PRO,
} }
if validateResponse.LicenseKey.ExpiresAt != nil && *validateResponse.LicenseKey.ExpiresAt != "" { if validateResponse.LicenseKey.ExpiresAt != nil && *validateResponse.LicenseKey.ExpiresAt != "" {
expiresTime, err := time.Parse(time.RFC3339Nano, *validateResponse.LicenseKey.ExpiresAt) expiresTime, err := time.Parse(time.RFC3339Nano, *validateResponse.LicenseKey.ExpiresAt)