-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathclient_helper.go
More file actions
237 lines (197 loc) · 6.56 KB
/
Copy pathclient_helper.go
File metadata and controls
237 lines (197 loc) · 6.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
package dify
import (
"encoding/json"
"fmt"
"resty.dev/v3"
)
// getOrCreateDatasetAPIKey gets existing dataset API keys or creates a new one if none exist
func getOrCreateDatasetAPIKey(consoleClient *resty.Client) (string, error) {
// First, try to get existing API keys
var keysResp DatasetAPIKeysResponse
response, err := consoleClient.R().
SetContentType("application/json").
SetResult(&keysResp).
Get("/console/api/datasets/api-keys")
if err != nil {
return "", fmt.Errorf("failed to get dataset API keys: %w", err)
}
if response.IsError() {
return "", fmt.Errorf("failed to get dataset API keys with status %d: %s", response.StatusCode(), response.String())
}
// If we have existing keys, use the first one
if len(keysResp.Data) > 0 {
return keysResp.Data[0].Token, nil
}
// No existing keys, create a new one
var newKeyResp DatasetAPIKey
response, err = consoleClient.R().
SetContentType("application/json").
SetResult(&newKeyResp).
Post("/console/api/datasets/api-keys")
if err != nil {
return "", fmt.Errorf("failed to create dataset API key: %w", err)
}
if response.IsError() {
return "", fmt.Errorf("failed to create dataset API key with status %d: %s", response.StatusCode(), response.String())
}
return newKeyResp.Token, nil
}
// getOrCreateDatasetAPIKeyWithRetry gets existing dataset API keys or creates a new one with retry on console token expiry
// This method uses console API (/console/api/datasets/api-keys) so it needs refresh token support
func (c *client) getOrCreateDatasetAPIKeyWithRetry() (string, error) {
var result string
var resultErr error
_, err := c.executeConsoleWithRetry(func() (*resty.Response, error) {
// First, try to get existing API keys
var keysResp DatasetAPIKeysResponse
resp, err := c.consoleClient.R().
SetContentType("application/json").
SetResult(&keysResp).
Get("/console/api/datasets/api-keys")
if err != nil {
resultErr = fmt.Errorf("failed to get dataset API keys: %w", err)
return resp, err
}
if resp.IsError() {
resultErr = fmt.Errorf("failed to get dataset API keys with status %d: %s", resp.StatusCode(), resp.String())
return resp, nil // Don't return error here, let executeWithRetry handle 401
}
// If we have existing keys, use the first one
if len(keysResp.Data) > 0 {
result = keysResp.Data[0].Token
return resp, nil
}
// No existing keys, create a new one
var newKeyResp DatasetAPIKey
resp, err = c.consoleClient.R().
SetContentType("application/json").
SetResult(&newKeyResp).
Post("/console/api/datasets/api-keys")
if err != nil {
resultErr = fmt.Errorf("failed to create dataset API key: %w", err)
return resp, err
}
if resp.IsError() {
resultErr = fmt.Errorf("failed to create dataset API key with status %d: %s", resp.StatusCode(), resp.String())
return resp, nil // Don't return error here, let executeWithRetry handle 401
}
result = newKeyResp.Token
return resp, nil
})
if err != nil {
return "", err
}
if resultErr != nil {
return "", resultErr
}
return result, nil
}
// refreshAccessToken refreshes the access token using the refresh token
func (c *client) refreshAccessToken() error {
refreshReq := &RefreshTokenRequest{
RefreshToken: c.refreshToken,
}
var refreshResp RefreshTokenResponse
response, err := c.consoleClient.R().
SetContentType("application/json").
SetBody(refreshReq).
SetResult(&refreshResp).
Post("/console/api/refresh-token")
if err != nil {
return fmt.Errorf("refresh token request failed: %w", err)
}
if response.IsError() {
return fmt.Errorf("refresh token failed with status %d: %s", response.StatusCode(), response.String())
}
if refreshResp.Result != "success" {
return fmt.Errorf("refresh token failed: %s", refreshResp.Result)
}
// Update the console client with new access token
c.consoleClient.Header().Set("Authorization", "Bearer "+refreshResp.Data.AccessToken)
// Update the refresh token
c.refreshToken = refreshResp.Data.RefreshToken
return nil
}
// executeConsoleWithRetry executes a console API request with automatic token refresh on 401 errors
// This should only be used for /console/api/ endpoints
func (c *client) executeConsoleWithRetry(requestFunc func() (*resty.Response, error)) (*resty.Response, error) {
// First attempt
response, err := requestFunc()
if err != nil {
return response, err
}
// Check if we got a 401 unauthorized error (only for console API)
if response.StatusCode() == 401 {
// Try to refresh the access token
if refreshErr := c.refreshAccessToken(); refreshErr != nil {
return response, fmt.Errorf("failed to refresh console access token: %w", refreshErr)
}
// Retry the request with the new token
response, err = requestFunc()
}
return response, err
}
func buildResponse[T any](response *resty.Response, val *T) *Response[T] {
if response.IsError() {
var errResp struct {
Code string `json:"code"`
Message string `json:"message"`
Status int `json:"status"`
}
if err := json.Unmarshal(response.Bytes(), &errResp); err != nil {
errResp.Code = "unknown_error"
errResp.Message = "An unknown error occurred"
}
return &Response[T]{
Response: response,
Result: nil,
Code: errResp.Code,
Message: errResp.Message,
Status: errResp.Status,
}
}
resp := &Response[T]{
Response: response,
Result: val,
}
return resp
}
// LoginRequest represents the login request payload
type LoginRequest struct {
Email string `json:"email"`
Password string `json:"password"`
Language string `json:"language"`
RememberMe bool `json:"remember_me"`
}
// LoginResponse represents the login response
type LoginResponse struct {
Result string `json:"result"`
Data struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
} `json:"data"`
}
// DatasetAPIKey represents a dataset API key
type DatasetAPIKey struct {
ID string `json:"id"`
Type string `json:"type"`
Token string `json:"token"`
LastUsedAt *int64 `json:"last_used_at"`
CreatedAt int64 `json:"created_at"`
}
// DatasetAPIKeysResponse represents the response when getting dataset API keys
type DatasetAPIKeysResponse struct {
Data []DatasetAPIKey `json:"data"`
}
// RefreshTokenRequest represents the refresh token request payload
type RefreshTokenRequest struct {
RefreshToken string `json:"refresh_token"`
}
// RefreshTokenResponse represents the refresh token response
type RefreshTokenResponse struct {
Result string `json:"result"`
Data struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
} `json:"data"`
}