fix: include api key in tenant websocket endpoint

This commit is contained in:
richjohnrambo
2026-09-09 17:32:49 +08:00
parent 23afc1b7f1
commit e0a6ee27bc
3 changed files with 13 additions and 7 deletions
+2 -2
View File
@@ -658,7 +658,7 @@ X-MeetingIM-APIKey: <预置的普通 API Key>
"data": {
"tenant": {"code": "acme", "name": "示例企业", "tenant_desc": null, "branding": {}},
"endpoints": {
"websocket": "wss://im.example.com/v0/channels",
"websocket": "wss://im.example.com/v0/channels?apikey=AQ...",
"api": "https://im.example.com/v1/im",
"file": "https://im.example.com/v0/file"
},
@@ -955,7 +955,7 @@ X-MeetingIM-APIKey: <预置的普通 API Key>
| 接口 | 输入示例 | 成功 `data` 示例 |
| --- | --- | --- |
| `POST /v1/im/tenants/resolve` | Header `X-Tenant-Code: default`;Body `{"tenant_code":"default","platform":"ios","app_version":"1.0.0"}` | `{"tenant":{"code":"default","name":"Default Tenant","tenant_desc":null,"branding":{}},"endpoints":{"websocket":"wss://api.118im.com/v0/channels","api":"https://api.118im.com/v1/im","file":"https://api.118im.com/v0/file"},"auth_methods":["password","sms"],"password_policy":{"min_length":8,"max_length":64},"app_update":{"type":"none"}}` |
| `POST /v1/im/tenants/resolve` | Header `X-Tenant-Code: default`;Body `{"tenant_code":"default","platform":"ios","app_version":"1.0.0"}` | `{"tenant":{"code":"default","name":"Default Tenant","tenant_desc":null,"branding":{}},"endpoints":{"websocket":"wss://api.118im.com/v0/channels?apikey=AQ...","api":"https://api.118im.com/v1/im","file":"https://api.118im.com/v0/file"},"auth_methods":["password","sms"],"password_policy":{"min_length":8,"max_length":64},"app_update":{"type":"none"}}` |
| `POST /v1/im/public/uploads` 企业注册/反馈附件上传 | Multipart `purpose=enterprise_license\|tenant_entry_feedback`、`file=@license.png` | `{"file_id":"file_01K...","purpose":"enterprise_license","mime_type":"image/png","size":1024000,"expires_at":"2026-08-25T08:30:15Z"}` |
| `POST /v1/im/tenant-applications` 企业注册申请 | `{"credit_code":"91310000MA1K000000","enterprise_email":"admin@example.com","license_file_id":"file_01K...","tenant_code_hint":"acme","client_context":{"platform":"android","app_version":"1.0.0"}}` | `{"application_id":"entapp_01K...","status":"received","estimated_review_hours":24,"submitted_at":"2026-08-24T08:30:15.123Z"}` |
| `POST /v1/im/tenant-entry-feedback` 企业入口问题反馈 | `{"description":"企业码 acme 提示不可用,无法进入企业。","contact":"user@example.com","tenant_code_hint":"acme","screenshot_file_ids":["file_01K..."],"client_context":{"platform":"ios","app_version":"1.0.0"}}` | `{"feedback_id":"entryfb_01K...","status":"received","submitted_at":"2026-08-24T08:35:15.123Z"}` |
+5 -1
View File
@@ -856,8 +856,12 @@ func imPublicEndpoints(req *http.Request) map[string]any {
} else if strings.HasPrefix(wsBase, "http://") {
wsBase = "ws://" + strings.TrimPrefix(wsBase, "http://")
}
wsEndpoint := strings.TrimRight(wsBase, "/") + "/v0/channels"
if apiKey := strings.TrimSpace(getAPIKey(req)); apiKey != "" {
wsEndpoint += "?apikey=" + url.QueryEscape(apiKey)
}
return map[string]any{
"websocket": strings.TrimRight(wsBase, "/") + "/v0/channels",
"websocket": wsEndpoint,
"api": strings.TrimRight(base, "/") + "/v1/im",
"file": strings.TrimRight(base, "/") + "/v0/file",
}
+6 -4
View File
@@ -1472,7 +1472,8 @@ func TestServeIMTenantResolveSuccessFromHeader(t *testing.T) {
globals.servingAt = "https://im.example.com/"
req := httptest.NewRequest(http.MethodPost, "/v1/im/tenants/resolve", nil)
req.Header.Set("X-MeetingIM-APIKey", makeTestAPIKey(t))
apiKey := makeTestAPIKey(t)
req.Header.Set("X-MeetingIM-APIKey", apiKey)
req.Header.Set("X-Tenant-Code", " acme ")
req.Header.Set("X-Request-ID", "req-1")
rec := httptest.NewRecorder()
@@ -1495,7 +1496,7 @@ func TestServeIMTenantResolveSuccessFromHeader(t *testing.T) {
t.Fatalf("tenant payload must not expose internal id: %#v", tenant)
}
endpoints := data["endpoints"].(map[string]any)
if endpoints["websocket"] != "wss://im.example.com/v0/channels" {
if endpoints["websocket"] != "wss://im.example.com/v0/channels?apikey="+url.QueryEscape(apiKey) {
t.Fatalf("unexpected websocket endpoint: %#v", endpoints)
}
}
@@ -1536,7 +1537,8 @@ func TestServeIMTenantResolveUsesForwardedPublicHost(t *testing.T) {
req.Host = "internal:6060"
req.Header.Set("X-Forwarded-Proto", "https")
req.Header.Set("X-Forwarded-Host", "api.118im.com")
req.Header.Set("X-MeetingIM-APIKey", makeTestAPIKey(t))
apiKey := makeTestAPIKey(t)
req.Header.Set("X-MeetingIM-APIKey", apiKey)
req.Header.Set("X-Tenant-Code", "acme")
rec := httptest.NewRecorder()
@@ -1547,7 +1549,7 @@ func TestServeIMTenantResolveUsesForwardedPublicHost(t *testing.T) {
}
data := decodeIMHTTPResponse(t, rec).Data.(map[string]any)
endpoints := data["endpoints"].(map[string]any)
if endpoints["websocket"] != "wss://api.118im.com/v0/channels" ||
if endpoints["websocket"] != "wss://api.118im.com/v0/channels?apikey="+url.QueryEscape(apiKey) ||
endpoints["api"] != "https://api.118im.com/v1/im" ||
endpoints["file"] != "https://api.118im.com/v0/file" {
t.Fatalf("unexpected endpoints: %#v", endpoints)