diff --git a/frontend/web/index.html b/frontend/web/index.html index 6dd635e..34ed763 100644 --- a/frontend/web/index.html +++ b/frontend/web/index.html @@ -6,6 +6,7 @@ Slash +
diff --git a/proto/gen/store/README.md b/proto/gen/store/README.md index a63faf4..bf634dd 100644 --- a/proto/gen/store/README.md +++ b/proto/gen/store/README.md @@ -416,6 +416,7 @@ | custom_style | [string](#string) | | | | custom_script | [string](#string) | | | | auto_backup | [AutoBackupWorkspaceSetting](#slash-store-AutoBackupWorkspaceSetting) | | | +| instance_url | [string](#string) | | | @@ -438,6 +439,7 @@ | WORKSPACE_SETTING_CUSTOM_STYLE | 4 | The custom style. | | WORKSPACE_SETTING_CUSTOM_SCRIPT | 5 | The custom script. | | WORKSPACE_SETTING_AUTO_BACKUP | 6 | The auto backup setting. | +| WORKSPACE_SETTING_INSTANCE_URL | 7 | The instance URL. | diff --git a/proto/gen/store/workspace_setting.pb.go b/proto/gen/store/workspace_setting.pb.go index 0d5c622..dadab80 100644 --- a/proto/gen/store/workspace_setting.pb.go +++ b/proto/gen/store/workspace_setting.pb.go @@ -36,6 +36,8 @@ const ( WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_SCRIPT WorkspaceSettingKey = 5 // The auto backup setting. WorkspaceSettingKey_WORKSPACE_SETTING_AUTO_BACKUP WorkspaceSettingKey = 6 + // The instance URL. + WorkspaceSettingKey_WORKSPACE_SETTING_INSTANCE_URL WorkspaceSettingKey = 7 ) // Enum value maps for WorkspaceSettingKey. @@ -48,6 +50,7 @@ var ( 4: "WORKSPACE_SETTING_CUSTOM_STYLE", 5: "WORKSPACE_SETTING_CUSTOM_SCRIPT", 6: "WORKSPACE_SETTING_AUTO_BACKUP", + 7: "WORKSPACE_SETTING_INSTANCE_URL", } WorkspaceSettingKey_value = map[string]int32{ "WORKSPACE_SETTING_KEY_UNSPECIFIED": 0, @@ -57,6 +60,7 @@ var ( "WORKSPACE_SETTING_CUSTOM_STYLE": 4, "WORKSPACE_SETTING_CUSTOM_SCRIPT": 5, "WORKSPACE_SETTING_AUTO_BACKUP": 6, + "WORKSPACE_SETTING_INSTANCE_URL": 7, } ) @@ -101,6 +105,7 @@ type WorkspaceSetting struct { // *WorkspaceSetting_CustomStyle // *WorkspaceSetting_CustomScript // *WorkspaceSetting_AutoBackup + // *WorkspaceSetting_InstanceUrl Value isWorkspaceSetting_Value `protobuf_oneof:"value"` } @@ -192,6 +197,13 @@ func (x *WorkspaceSetting) GetAutoBackup() *AutoBackupWorkspaceSetting { return nil } +func (x *WorkspaceSetting) GetInstanceUrl() string { + if x, ok := x.GetValue().(*WorkspaceSetting_InstanceUrl); ok { + return x.InstanceUrl + } + return "" +} + type isWorkspaceSetting_Value interface { isWorkspaceSetting_Value() } @@ -220,6 +232,10 @@ type WorkspaceSetting_AutoBackup struct { AutoBackup *AutoBackupWorkspaceSetting `protobuf:"bytes,7,opt,name=auto_backup,json=autoBackup,proto3,oneof"` } +type WorkspaceSetting_InstanceUrl struct { + InstanceUrl string `protobuf:"bytes,8,opt,name=instance_url,json=instanceUrl,proto3,oneof"` +} + func (*WorkspaceSetting_LicenseKey) isWorkspaceSetting_Value() {} func (*WorkspaceSetting_SecretSession) isWorkspaceSetting_Value() {} @@ -232,6 +248,8 @@ func (*WorkspaceSetting_CustomScript) isWorkspaceSetting_Value() {} func (*WorkspaceSetting_AutoBackup) isWorkspaceSetting_Value() {} +func (*WorkspaceSetting_InstanceUrl) isWorkspaceSetting_Value() {} + type AutoBackupWorkspaceSetting struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -305,7 +323,7 @@ var File_store_workspace_setting_proto protoreflect.FileDescriptor var file_store_workspace_setting_proto_rawDesc = []byte{ 0x0a, 0x1d, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, - 0x0b, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x22, 0xda, 0x02, 0x0a, + 0x0b, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x22, 0xff, 0x02, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x32, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x57, 0x6f, 0x72, @@ -327,43 +345,48 @@ var file_store_workspace_setting_proto_rawDesc = []byte{ 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x7a, 0x0a, 0x1a, 0x41, 0x75, 0x74, - 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x6f, 0x6e, - 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, - 0x78, 0x5f, 0x6b, 0x65, 0x65, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6d, 0x61, - 0x78, 0x4b, 0x65, 0x65, 0x70, 0x2a, 0x96, 0x02, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x0a, - 0x21, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, - 0x4e, 0x47, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, - 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, - 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20, 0x57, 0x4f, 0x52, 0x4b, 0x53, - 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x45, 0x43, - 0x52, 0x45, 0x54, 0x5f, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x23, 0x0a, - 0x1f, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x41, 0x50, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, - 0x4e, 0x47, 0x5f, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x53, 0x49, 0x47, 0x4e, 0x55, 0x50, - 0x10, 0x03, 0x12, 0x22, 0x0a, 0x1e, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, - 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x5f, 0x53, - 0x54, 0x59, 0x4c, 0x45, 0x10, 0x04, 0x12, 0x23, 0x0a, 0x1f, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, - 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x43, 0x55, 0x53, 0x54, - 0x4f, 0x4d, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x10, 0x05, 0x12, 0x21, 0x0a, 0x1d, 0x57, + 0x12, 0x23, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x75, 0x72, 0x6c, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x55, 0x72, 0x6c, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x7a, + 0x0a, 0x1a, 0x41, 0x75, 0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x57, 0x6f, 0x72, 0x6b, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x18, 0x0a, 0x07, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x65, + 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x63, 0x72, 0x6f, 0x6e, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x6b, 0x65, 0x65, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x4b, 0x65, 0x65, 0x70, 0x2a, 0xba, 0x02, 0x0a, 0x13, 0x57, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x4b, + 0x65, 0x79, 0x12, 0x25, 0x0a, 0x21, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, + 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, + 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x57, 0x4f, 0x52, + 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x4c, + 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20, + 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, + 0x47, 0x5f, 0x53, 0x45, 0x43, 0x52, 0x45, 0x54, 0x5f, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, + 0x10, 0x02, 0x12, 0x23, 0x0a, 0x1f, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x41, 0x50, 0x43, 0x45, 0x5f, + 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x53, + 0x49, 0x47, 0x4e, 0x55, 0x50, 0x10, 0x03, 0x12, 0x22, 0x0a, 0x1e, 0x57, 0x4f, 0x52, 0x4b, 0x53, + 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x43, 0x55, 0x53, + 0x54, 0x4f, 0x4d, 0x5f, 0x53, 0x54, 0x59, 0x4c, 0x45, 0x10, 0x04, 0x12, 0x23, 0x0a, 0x1f, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, - 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x42, 0x41, 0x43, 0x4b, 0x55, 0x50, 0x10, 0x06, 0x42, 0xa6, - 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x42, 0x15, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2f, 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, 0x73, 0x74, 0x6f, 0x72, 0x65, 0xa2, 0x02, 0x03, 0x53, - 0x53, 0x58, 0xaa, 0x02, 0x0b, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, - 0xca, 0x02, 0x0b, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xe2, 0x02, - 0x17, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x5c, 0x47, 0x50, 0x42, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, - 0x3a, 0x3a, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x5f, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x10, 0x05, + 0x12, 0x21, 0x0a, 0x1d, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, + 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x42, 0x41, 0x43, 0x4b, 0x55, + 0x50, 0x10, 0x06, 0x12, 0x22, 0x0a, 0x1e, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, + 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x43, + 0x45, 0x5f, 0x55, 0x52, 0x4c, 0x10, 0x07, 0x42, 0xa6, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, + 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x15, 0x57, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2f, 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, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0xa2, 0x02, 0x03, 0x53, 0x53, 0x58, 0xaa, 0x02, 0x0b, 0x53, 0x6c, + 0x61, 0x73, 0x68, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xca, 0x02, 0x0b, 0x53, 0x6c, 0x61, 0x73, + 0x68, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xe2, 0x02, 0x17, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, + 0x53, 0x74, 0x6f, 0x72, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0xea, 0x02, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x3a, 0x3a, 0x53, 0x74, 0x6f, 0x72, 0x65, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -433,6 +456,7 @@ func file_store_workspace_setting_proto_init() { (*WorkspaceSetting_CustomStyle)(nil), (*WorkspaceSetting_CustomScript)(nil), (*WorkspaceSetting_AutoBackup)(nil), + (*WorkspaceSetting_InstanceUrl)(nil), } type x struct{} out := protoimpl.TypeBuilder{ diff --git a/proto/store/workspace_setting.proto b/proto/store/workspace_setting.proto index 32d4473..fda6341 100644 --- a/proto/store/workspace_setting.proto +++ b/proto/store/workspace_setting.proto @@ -14,6 +14,7 @@ message WorkspaceSetting { string custom_style = 5; string custom_script = 6; AutoBackupWorkspaceSetting auto_backup = 7; + string instance_url = 8; } } @@ -31,6 +32,8 @@ enum WorkspaceSettingKey { WORKSPACE_SETTING_CUSTOM_SCRIPT = 5; // The auto backup setting. WORKSPACE_SETTING_AUTO_BACKUP = 6; + // The instance URL. + WORKSPACE_SETTING_INSTANCE_URL = 7; } message AutoBackupWorkspaceSetting { diff --git a/server/embed_frontend.go b/server/embed_frontend.go index fa7e0ee..f814dfd 100644 --- a/server/embed_frontend.go +++ b/server/embed_frontend.go @@ -2,18 +2,162 @@ package server import ( "embed" + "fmt" "io/fs" "net/http" + "strings" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "github.com/yourselfhosted/slash/internal/util" + storepb "github.com/yourselfhosted/slash/proto/gen/store" + "github.com/yourselfhosted/slash/server/profile" + "github.com/yourselfhosted/slash/store" ) //go:embed dist var embeddedFiles embed.FS +//go:embed dist/index.html +var rawIndexHTML string + +type FrontendService struct { + Profile *profile.Profile + Store *store.Store +} + +func NewFrontendService(profile *profile.Profile, store *store.Store) *FrontendService { + return &FrontendService{ + Profile: profile, + Store: store, + } +} + +func (s *FrontendService) Serve(e *echo.Echo) { + // Use echo static middleware to serve the built dist folder. + // refer: https://github.com/labstack/echo/blob/master/middleware/static.go + e.Use(middleware.StaticWithConfig(middleware.StaticConfig{ + Skipper: defaultRequestSkipper, + HTML5: true, + Filesystem: getFileSystem("dist"), + })) + + assetsGroup := e.Group("assets") + assetsGroup.Use(middleware.GzipWithConfig(middleware.GzipConfig{ + Skipper: defaultRequestSkipper, + Level: 5, + })) + assetsGroup.Use(func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + c.Response().Header().Set(echo.HeaderCacheControl, "max-age=31536000, immutable") + return next(c) + } + }) + assetsGroup.Use(middleware.StaticWithConfig(middleware.StaticConfig{ + Skipper: defaultRequestSkipper, + HTML5: true, + Filesystem: getFileSystem("dist/assets"), + })) + s.registerRoutes(e) +} + +func (s *FrontendService) registerRoutes(e *echo.Echo) { + e.GET("/robots.txt", func(c echo.Context) error { + ctx := c.Request().Context() + instanceURLSetting, err := s.Store.GetWorkspaceSetting(ctx, &store.FindWorkspaceSetting{ + Key: storepb.WorkspaceSettingKey_WORKSPACE_SETTING_INSTANCE_URL, + }) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to get instance URL system setting").SetInternal(err) + } + if instanceURLSetting == nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Instance URL system setting is not set") + } + instanceURL := instanceURLSetting.GetInstanceUrl() + robotsTxt := fmt.Sprintf(`User-agent: * +Allow: / +Host: %s +Sitemap: %s/sitemap.xml`, instanceURL, instanceURL) + return c.String(http.StatusOK, robotsTxt) + }) + + e.GET("/sitemap.xml", func(c echo.Context) error { + ctx := c.Request().Context() + instanceURLSetting, err := s.Store.GetWorkspaceSetting(ctx, &store.FindWorkspaceSetting{ + Key: storepb.WorkspaceSettingKey_WORKSPACE_SETTING_INSTANCE_URL, + }) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to get instance URL system setting").SetInternal(err) + } + if instanceURLSetting == nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Instance URL system setting is not set") + } + + instanceURL := instanceURLSetting.GetInstanceUrl() + urlsets := []string{} + // Append shortcut list. + shortcuts, err := s.Store.ListShortcuts(ctx, &store.FindShortcut{ + VisibilityList: []store.Visibility{store.VisibilityPublic}, + }) + if err != nil { + return err + } + for _, shortcut := range shortcuts { + urlsets = append(urlsets, fmt.Sprintf(`%s/s/%s`, instanceURL, shortcut.Name)) + } + // Append collection list. + collections, err := s.Store.ListCollections(ctx, &store.FindCollection{ + VisibilityList: []store.Visibility{store.VisibilityPublic}, + }) + if err != nil { + return err + } + for _, collection := range collections { + urlsets = append(urlsets, fmt.Sprintf(`%s/c/%s`, instanceURL, collection.Name)) + } + + sitemap := fmt.Sprintf(`%s`, strings.Join(urlsets, "\n")) + return c.XMLBlob(http.StatusOK, []byte(sitemap)) + }) + + e.GET("/s/:shortcutName", func(c echo.Context) error { + ctx := c.Request().Context() + shortcutName := c.Param("shortcutName") + shortcut, err := s.Store.GetShortcut(ctx, &store.FindShortcut{ + Name: &shortcutName, + }) + if err != nil { + return c.HTML(http.StatusOK, rawIndexHTML) + } + if shortcut == nil { + return c.HTML(http.StatusOK, rawIndexHTML) + } + + // Inject shortcut metadata into `index.html`. + indexHTML := strings.ReplaceAll(rawIndexHTML, "", generateShortcutMetadata(shortcut)) + return c.HTML(http.StatusOK, indexHTML) + }) +} + +func generateShortcutMetadata(shortcut *storepb.Shortcut) string { + metadataList := []string{ + fmt.Sprintf(`%s`, shortcut.OgMetadata.Title), + fmt.Sprintf(``, shortcut.OgMetadata.Description), + fmt.Sprintf(``, shortcut.OgMetadata.Title), + fmt.Sprintf(``, shortcut.OgMetadata.Description), + fmt.Sprintf(``, shortcut.OgMetadata.Image), + ``, + // Twitter related metadata. + fmt.Sprintf(``, shortcut.OgMetadata.Title), + fmt.Sprintf(``, shortcut.OgMetadata.Description), + fmt.Sprintf(``, shortcut.OgMetadata.Image), + ``, + fmt.Sprintf(``, shortcut.Link), + } + return strings.Join(metadataList, "\n") +} + func getFileSystem(path string) http.FileSystem { fs, err := fs.Sub(embeddedFiles, path) if err != nil { @@ -25,28 +169,5 @@ func getFileSystem(path string) http.FileSystem { func defaultRequestSkipper(c echo.Context) bool { path := c.Path() - return util.HasPrefixes(path, "/api/", "/s/*") -} - -func embedFrontend(e *echo.Echo) { - // Use echo static middleware to serve the built dist folder - // refer: https://github.com/labstack/echo/blob/master/middleware/static.go - e.Use(middleware.StaticWithConfig(middleware.StaticConfig{ - Skipper: defaultRequestSkipper, - HTML5: true, - Filesystem: getFileSystem("dist"), - })) - - assetsGroup := e.Group("assets") - assetsGroup.Use(func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - c.Response().Header().Set(echo.HeaderCacheControl, "max-age=31536000, immutable") - return next(c) - } - }) - assetsGroup.Use(middleware.StaticWithConfig(middleware.StaticConfig{ - Skipper: defaultRequestSkipper, - HTML5: true, - Filesystem: getFileSystem("dist/assets"), - })) + return util.HasPrefixes(path, "/api/") } diff --git a/server/server.go b/server/server.go index a053312..2ec2765 100644 --- a/server/server.go +++ b/server/server.go @@ -90,7 +90,9 @@ func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store }, })) - embedFrontend(e) + // Serve frontend. + frontendService := NewFrontendService(profile, store) + frontendService.Serve(e) // In dev mode, we'd like to set the const secret key to make signin session persistence. secret := "slash" diff --git a/store/db/postgres/workspace_setting.go b/store/db/postgres/workspace_setting.go index f96536d..8c38ede 100644 --- a/store/db/postgres/workspace_setting.go +++ b/store/db/postgres/workspace_setting.go @@ -39,6 +39,8 @@ func (d *DB) UpsertWorkspaceSetting(ctx context.Context, upsert *storepb.Workspa return nil, err } valueString = string(valueBytes) + } else if upsert.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_INSTANCE_URL { + valueString = upsert.GetInstanceUrl() } else { return nil, errors.New("invalid workspace setting key") } @@ -102,6 +104,8 @@ func (d *DB) ListWorkspaceSettings(ctx context.Context, find *store.FindWorkspac return nil, err } workspaceSetting.Value = &storepb.WorkspaceSetting_AutoBackup{AutoBackup: autoBackupSetting} + } else if workspaceSetting.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_INSTANCE_URL { + workspaceSetting.Value = &storepb.WorkspaceSetting_InstanceUrl{InstanceUrl: valueString} } else { continue } diff --git a/store/db/sqlite/workspace_setting.go b/store/db/sqlite/workspace_setting.go index 9950047..31638f8 100644 --- a/store/db/sqlite/workspace_setting.go +++ b/store/db/sqlite/workspace_setting.go @@ -39,6 +39,8 @@ func (d *DB) UpsertWorkspaceSetting(ctx context.Context, upsert *storepb.Workspa return nil, err } valueString = string(valueBytes) + } else if upsert.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_INSTANCE_URL { + valueString = upsert.GetInstanceUrl() } else { return nil, errors.New("invalid workspace setting key") } @@ -102,6 +104,8 @@ func (d *DB) ListWorkspaceSettings(ctx context.Context, find *store.FindWorkspac return nil, err } workspaceSetting.Value = &storepb.WorkspaceSetting_AutoBackup{AutoBackup: autoBackupSetting} + } else if workspaceSetting.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_INSTANCE_URL { + workspaceSetting.Value = &storepb.WorkspaceSetting_InstanceUrl{InstanceUrl: valueString} } else { continue }