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
}