@turinhub/tale-js-sdk 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +56 -0
- package/dist/acl/index.d.ts +295 -0
- package/dist/acl/index.js +653 -0
- package/dist/acl/types.d.ts +142 -0
- package/dist/acl/types.js +1 -0
- package/dist/auth/index.d.ts +166 -0
- package/dist/auth/index.js +522 -0
- package/dist/auth/types.d.ts +127 -0
- package/dist/auth/types.js +1 -0
- package/dist/cms/file.d.ts +398 -0
- package/dist/cms/file.js +800 -0
- package/dist/cms/folder.d.ts +128 -0
- package/dist/cms/folder.js +294 -0
- package/dist/cms/index.d.ts +3 -0
- package/dist/cms/index.js +6 -0
- package/dist/cms/types.d.ts +157 -0
- package/dist/cms/types.js +1 -0
- package/dist/common/types.d.ts +91 -0
- package/dist/common/types.js +2 -0
- package/dist/errors.d.ts +52 -0
- package/dist/errors.js +109 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +12 -0
- package/dist/rbac/acl.d.ts +152 -0
- package/dist/rbac/acl.js +723 -0
- package/dist/rbac/index.d.ts +464 -0
- package/dist/rbac/index.js +1121 -0
- package/dist/rbac/rbac.d.ts +198 -0
- package/dist/rbac/rbac.js +984 -0
- package/dist/rbac/types.d.ts +125 -0
- package/dist/rbac/types.js +1 -0
- package/dist/rbac/user-group.d.ts +122 -0
- package/dist/rbac/user-group.js +570 -0
- package/dist/status.d.ts +40 -0
- package/dist/status.js +56 -0
- package/dist/task/index.d.ts +163 -0
- package/dist/task/index.js +495 -0
- package/dist/task/types.d.ts +116 -0
- package/dist/task/types.js +1 -0
- package/dist/task-type/index.d.ts +92 -0
- package/dist/task-type/index.js +256 -0
- package/dist/task-type/types.d.ts +54 -0
- package/dist/task-type/types.js +1 -0
- package/dist/token.d.ts +21 -0
- package/dist/token.js +114 -0
- package/dist/user/index.d.ts +267 -0
- package/dist/user/index.js +786 -0
- package/dist/user/types.d.ts +145 -0
- package/dist/user/types.js +1 -0
- package/dist/user-attribute/index.d.ts +186 -0
- package/dist/user-attribute/index.js +615 -0
- package/dist/user-attribute/types.d.ts +109 -0
- package/dist/user-attribute/types.js +1 -0
- package/dist/user-group/index.d.ts +231 -0
- package/dist/user-group/index.js +566 -0
- package/dist/user-group/types.d.ts +50 -0
- package/dist/user-group/types.js +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
import { getAppToken } from "../token.js";
|
|
2
|
+
import { ApiError, ConfigurationError, NetworkError } from "../errors.js";
|
|
3
|
+
/**
|
|
4
|
+
* Authenticates a user with username and password.
|
|
5
|
+
*
|
|
6
|
+
* @param credentials - User login credentials
|
|
7
|
+
* @param options - Optional configuration for the login request
|
|
8
|
+
* @returns Promise resolving to login response with user info and token
|
|
9
|
+
* @throws {ConfigurationError} When required environment variables (TALE_BASE_URL, TALE_APP_KEY) are missing
|
|
10
|
+
* @throws {ApiError} When authentication fails or returns invalid response
|
|
11
|
+
* @throws {NetworkError} When network request fails
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { login } from '@tale/client';
|
|
16
|
+
*
|
|
17
|
+
* try {
|
|
18
|
+
* const result = await login({
|
|
19
|
+
* username: 'johndoe',
|
|
20
|
+
* password: 'secure_password_123'
|
|
21
|
+
* });
|
|
22
|
+
* console.log('Login successful:', result.user.username);
|
|
23
|
+
* console.log('User token:', result.token.token);
|
|
24
|
+
* } catch (error) {
|
|
25
|
+
* console.error('Login failed:', error.message);
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* import { login } from '@tale/client';
|
|
32
|
+
*
|
|
33
|
+
* // Login with include parameters to control what data is returned
|
|
34
|
+
* const result = await login({
|
|
35
|
+
* username: 'johndoe',
|
|
36
|
+
* password: 'secure_password_123'
|
|
37
|
+
* }, {
|
|
38
|
+
* include_rbac: true, // Include roles and privileges (SDK default: false)
|
|
39
|
+
* include_user_groups: true, // Include user groups (SDK default: false)
|
|
40
|
+
* include_login_methods: true, // Include login methods (SDK default: false)
|
|
41
|
+
* include_attributes: true, // Include user attributes (SDK default: false)
|
|
42
|
+
* include_acl: true // Include ACL records (SDK default: false)
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @note The function requires the following environment variables:
|
|
47
|
+
* - TALE_BASE_URL: Base URL for Tale backend services
|
|
48
|
+
* - TALE_APP_KEY: Application key for authentication
|
|
49
|
+
* @note By default, all include parameters are set to false for better performance.
|
|
50
|
+
* Explicitly set them to true if you need the additional data.
|
|
51
|
+
*
|
|
52
|
+
* @param options.include_rbac Include roles and privileges; default false
|
|
53
|
+
* @param options.include_login_methods Include user login methods; default false
|
|
54
|
+
* @param options.include_user_groups Include user groups; default false
|
|
55
|
+
* @param options.include_attributes Include user attributes; default false
|
|
56
|
+
* @param options.include_acl Include ACL records; default false
|
|
57
|
+
*/
|
|
58
|
+
export async function login(credentials, options) {
|
|
59
|
+
// Validate required fields
|
|
60
|
+
if (!credentials.username || credentials.username.trim() === "") {
|
|
61
|
+
throw new ApiError("username is required", 400, "9400");
|
|
62
|
+
}
|
|
63
|
+
if (!credentials.password || credentials.password.trim() === "") {
|
|
64
|
+
throw new ApiError("password is required", 400, "9400");
|
|
65
|
+
}
|
|
66
|
+
// Determine base URL
|
|
67
|
+
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
68
|
+
const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
|
|
69
|
+
if (!base) {
|
|
70
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
71
|
+
}
|
|
72
|
+
// Get app key from environment variable
|
|
73
|
+
const appKey = env?.TALE_APP_KEY;
|
|
74
|
+
if (!appKey) {
|
|
75
|
+
throw new ConfigurationError("Missing required environment variable: TALE_APP_KEY");
|
|
76
|
+
}
|
|
77
|
+
// Build URL with query parameters
|
|
78
|
+
const url = new URL(String(base).replace(/\/+$/, "") + "/auth/v1/login/username-password");
|
|
79
|
+
// Add include parameters as query params (SDK defaults to false)
|
|
80
|
+
url.searchParams.set("include_rbac", String(options?.include_rbac ?? false));
|
|
81
|
+
url.searchParams.set("include_login_methods", String(options?.include_login_methods ?? false));
|
|
82
|
+
url.searchParams.set("include_user_groups", String(options?.include_user_groups ?? false));
|
|
83
|
+
url.searchParams.set("include_attributes", String(options?.include_attributes ?? false));
|
|
84
|
+
url.searchParams.set("include_acl", String(options?.include_acl ?? false));
|
|
85
|
+
// Build request body
|
|
86
|
+
const requestBody = {
|
|
87
|
+
username: credentials.username.trim(),
|
|
88
|
+
password: credentials.password,
|
|
89
|
+
app_key: appKey,
|
|
90
|
+
device_name: options?.device_name,
|
|
91
|
+
device_fingerprint: options?.device_fingerprint,
|
|
92
|
+
scope: options?.scope || "",
|
|
93
|
+
};
|
|
94
|
+
let response;
|
|
95
|
+
try {
|
|
96
|
+
response = await globalThis.fetch(url.toString(), {
|
|
97
|
+
method: "POST",
|
|
98
|
+
headers: {
|
|
99
|
+
"Content-Type": "application/json",
|
|
100
|
+
},
|
|
101
|
+
body: JSON.stringify(requestBody),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
throw new NetworkError(`Failed to authenticate user: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
106
|
+
}
|
|
107
|
+
let json;
|
|
108
|
+
try {
|
|
109
|
+
const responseJson = await response.json();
|
|
110
|
+
json = responseJson;
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
throw new ApiError(`Failed to parse login response: ${error instanceof Error ? error.message : "Invalid JSON"}`, response.status);
|
|
114
|
+
}
|
|
115
|
+
// Handle authentication errors based on response status
|
|
116
|
+
if (response.status === 400) {
|
|
117
|
+
const errorMsg = typeof json === "object" &&
|
|
118
|
+
json !== null &&
|
|
119
|
+
("message" in json || "msg" in json)
|
|
120
|
+
? String(json.message || json.msg)
|
|
121
|
+
: "Authentication failed";
|
|
122
|
+
throw new ApiError(errorMsg, response.status, "9400");
|
|
123
|
+
}
|
|
124
|
+
if (response.status === 403) {
|
|
125
|
+
throw new ApiError("Account is frozen or access forbidden", response.status, "9403");
|
|
126
|
+
}
|
|
127
|
+
if (response.status === 401) {
|
|
128
|
+
throw new ApiError("Invalid app_key or authentication failed", response.status, "9401");
|
|
129
|
+
}
|
|
130
|
+
if (!response.ok) {
|
|
131
|
+
const errorMsg = typeof json === "object" &&
|
|
132
|
+
json !== null &&
|
|
133
|
+
("message" in json || "msg" in json)
|
|
134
|
+
? String(json.message || json.msg)
|
|
135
|
+
: "Authentication failed";
|
|
136
|
+
throw new ApiError(errorMsg, response.status);
|
|
137
|
+
}
|
|
138
|
+
// Handle response data structure - check if data is wrapped in a 'data' field
|
|
139
|
+
const responseData = json.data || json;
|
|
140
|
+
// Validate response structure
|
|
141
|
+
if (!responseData || !responseData.user || !responseData.token) {
|
|
142
|
+
console.error("Invalid response structure:", { json, responseData });
|
|
143
|
+
throw new ApiError("Invalid login response: missing required data", response.status);
|
|
144
|
+
}
|
|
145
|
+
// Build standard response structure
|
|
146
|
+
const loginResponse = {
|
|
147
|
+
app: responseData.app,
|
|
148
|
+
user: responseData.user,
|
|
149
|
+
token: responseData.token,
|
|
150
|
+
user_roles: responseData.user_roles || [],
|
|
151
|
+
user_privileges: responseData.user_privileges || [],
|
|
152
|
+
user_groups: responseData.user_groups || [],
|
|
153
|
+
user_login_methods: responseData.user_login_methods || [],
|
|
154
|
+
};
|
|
155
|
+
return loginResponse;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Validates a user token.
|
|
159
|
+
*
|
|
160
|
+
* @param token - User token to validate
|
|
161
|
+
* @param options - Optional configuration for validation
|
|
162
|
+
* @returns Promise resolving to validation result
|
|
163
|
+
* @throws {ConfigurationError} When required environment variables are missing
|
|
164
|
+
* @throws {ApiError} When validation fails
|
|
165
|
+
* @throws {NetworkError} When network request fails
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* import { validateToken } from '@tale/client';
|
|
170
|
+
*
|
|
171
|
+
* try {
|
|
172
|
+
* const result = await validateToken('tu_user_token_here');
|
|
173
|
+
* console.log('Token is valid');
|
|
174
|
+
* } catch (error) {
|
|
175
|
+
* console.error('Token validation failed:', error.message);
|
|
176
|
+
* }
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
export async function validateToken(token, options) {
|
|
180
|
+
// Validate required fields
|
|
181
|
+
if (!token || token.trim() === "") {
|
|
182
|
+
throw new ApiError("token is required", 400, "9400");
|
|
183
|
+
}
|
|
184
|
+
// Determine base URL
|
|
185
|
+
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
186
|
+
const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
|
|
187
|
+
if (!base) {
|
|
188
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
189
|
+
}
|
|
190
|
+
const url = new URL(String(base).replace(/\/+$/, "") + "/auth/v1/assert_token");
|
|
191
|
+
if (options?.scope) {
|
|
192
|
+
url.searchParams.append("scope", options.scope);
|
|
193
|
+
}
|
|
194
|
+
let response;
|
|
195
|
+
try {
|
|
196
|
+
response = await globalThis.fetch(url.toString(), {
|
|
197
|
+
method: "GET",
|
|
198
|
+
headers: {
|
|
199
|
+
"Content-Type": "application/json",
|
|
200
|
+
"x-t-token": token.trim(),
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
throw new NetworkError(`Failed to validate token: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
206
|
+
}
|
|
207
|
+
// Token is valid if we get a 200 response
|
|
208
|
+
return response.ok;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Initiates SMS login by sending verification code for login or registration.
|
|
212
|
+
*
|
|
213
|
+
* @param phone - Phone number for sending SMS
|
|
214
|
+
* @param options - Optional configuration for the request
|
|
215
|
+
* @returns Promise resolving to SMS sending response
|
|
216
|
+
* @throws {ConfigurationError} When required environment variables are missing
|
|
217
|
+
* @throws {ApiError} When SMS sending fails or returns invalid response
|
|
218
|
+
* @throws {NetworkError} When network request fails
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```typescript
|
|
222
|
+
* import { loginWithSms } from '@tale/client';
|
|
223
|
+
*
|
|
224
|
+
* try {
|
|
225
|
+
* const result = await loginWithSms('+8613800138000');
|
|
226
|
+
* console.log('SMS sent:', result.sms_id);
|
|
227
|
+
* console.log('Type:', result.type); // 'login' or 'register'
|
|
228
|
+
* } catch (error) {
|
|
229
|
+
* console.error('SMS sending failed:', error.message);
|
|
230
|
+
* }
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
export async function loginWithSms(phone, options) {
|
|
234
|
+
// Validate required fields
|
|
235
|
+
if (!phone || phone.trim() === "") {
|
|
236
|
+
throw new ApiError("phone number is required", 400, "9400");
|
|
237
|
+
}
|
|
238
|
+
// Determine base URL from environment variables
|
|
239
|
+
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
240
|
+
const base = env?.TALE_BASE_URL;
|
|
241
|
+
if (!base) {
|
|
242
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
243
|
+
}
|
|
244
|
+
// Use provided app token or get one from token service
|
|
245
|
+
const authToken = options?.appToken ?? (await getAppToken());
|
|
246
|
+
const url = new URL(String(base).replace(/\/+$/, "") + "/auth/v1/sms/login_or_register");
|
|
247
|
+
url.searchParams.append("phone", phone.trim());
|
|
248
|
+
let response;
|
|
249
|
+
try {
|
|
250
|
+
response = await globalThis.fetch(url.toString(), {
|
|
251
|
+
method: "GET",
|
|
252
|
+
headers: {
|
|
253
|
+
"x-t-token": authToken,
|
|
254
|
+
"Content-Type": "application/json",
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
throw new NetworkError(`Failed to send SMS verification: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
260
|
+
}
|
|
261
|
+
let json;
|
|
262
|
+
try {
|
|
263
|
+
const responseJson = await response.json();
|
|
264
|
+
json = responseJson;
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
throw new ApiError(`Failed to parse SMS response: ${error instanceof Error ? error.message : "Invalid JSON"}`, response.status);
|
|
268
|
+
}
|
|
269
|
+
// Handle API errors
|
|
270
|
+
if (response.status === 400) {
|
|
271
|
+
const errorMsg = typeof json === "object" &&
|
|
272
|
+
json !== null &&
|
|
273
|
+
("message" in json || "msg" in json)
|
|
274
|
+
? String(json.message || json.msg)
|
|
275
|
+
: "Invalid phone number format";
|
|
276
|
+
throw new ApiError(errorMsg, response.status, "9400");
|
|
277
|
+
}
|
|
278
|
+
if (response.status === 429) {
|
|
279
|
+
throw new ApiError("SMS sending rate limit exceeded", response.status, "9705");
|
|
280
|
+
}
|
|
281
|
+
if (!response.ok) {
|
|
282
|
+
const errorMsg = typeof json === "object" &&
|
|
283
|
+
json !== null &&
|
|
284
|
+
("message" in json || "msg" in json)
|
|
285
|
+
? String(json.message || json.msg)
|
|
286
|
+
: "SMS sending failed";
|
|
287
|
+
throw new ApiError(errorMsg, response.status);
|
|
288
|
+
}
|
|
289
|
+
// Handle response data structure - check if data is wrapped in a 'data' field
|
|
290
|
+
const responseData = json.data || json;
|
|
291
|
+
// Validate response structure
|
|
292
|
+
if (!responseData || !responseData.sms_id || !responseData.type) {
|
|
293
|
+
throw new ApiError("Invalid SMS response: missing required data", response.status);
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
app_key: responseData.app_key,
|
|
297
|
+
phone: responseData.phone,
|
|
298
|
+
type: responseData.type,
|
|
299
|
+
sms_id: responseData.sms_id,
|
|
300
|
+
expired_at: responseData.expired_at,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Verifies SMS code and authenticates user (login or register).
|
|
305
|
+
*
|
|
306
|
+
* @param request - SMS verification request with sms_id, code, and optional user data
|
|
307
|
+
* @param options - Optional configuration for the request
|
|
308
|
+
* @returns Promise resolving to login response with user info and token
|
|
309
|
+
* @throws {ConfigurationError} When required environment variables are missing
|
|
310
|
+
* @throws {ApiError} When verification fails or returns invalid response
|
|
311
|
+
* @throws {NetworkError} When network request fails
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* ```typescript
|
|
315
|
+
* import { verifySmsCode } from '@tale/client';
|
|
316
|
+
*
|
|
317
|
+
* try {
|
|
318
|
+
* const result = await verifySmsCode({
|
|
319
|
+
* sms_id: 'uuid-here',
|
|
320
|
+
* sms_type: 'login',
|
|
321
|
+
* verification_code: '123456'
|
|
322
|
+
* });
|
|
323
|
+
* console.log('Login successful:', result.user.user_id);
|
|
324
|
+
* console.log('User token:', result.token.token);
|
|
325
|
+
* } catch (error) {
|
|
326
|
+
* console.error('SMS verification failed:', error.message);
|
|
327
|
+
* }
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
export async function verifySmsCode(request, options) {
|
|
331
|
+
// Validate required fields
|
|
332
|
+
if (!request.sms_id || request.sms_id.trim() === "") {
|
|
333
|
+
throw new ApiError("sms_id is required", 400, "9400");
|
|
334
|
+
}
|
|
335
|
+
if (!request.sms_type || !["login", "register"].includes(request.sms_type)) {
|
|
336
|
+
throw new ApiError('sms_type must be "login" or "register"', 400, "9400");
|
|
337
|
+
}
|
|
338
|
+
if (!request.verification_code || request.verification_code.trim() === "") {
|
|
339
|
+
throw new ApiError("verification_code is required", 400, "9400");
|
|
340
|
+
}
|
|
341
|
+
// Determine base URL
|
|
342
|
+
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
343
|
+
const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
|
|
344
|
+
if (!base) {
|
|
345
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
346
|
+
}
|
|
347
|
+
// Use provided app token or get one from token service
|
|
348
|
+
const authToken = options?.appToken ?? (await getAppToken(options));
|
|
349
|
+
const url = new URL(String(base).replace(/\/+$/, "") + "/auth/v1/sms/verify");
|
|
350
|
+
url.searchParams.append("sms_id", request.sms_id.trim());
|
|
351
|
+
url.searchParams.append("sms_type", request.sms_type);
|
|
352
|
+
url.searchParams.append("verification_code", request.verification_code.trim());
|
|
353
|
+
let response;
|
|
354
|
+
try {
|
|
355
|
+
response = await globalThis.fetch(url.toString(), {
|
|
356
|
+
method: "GET",
|
|
357
|
+
headers: {
|
|
358
|
+
"x-t-token": authToken,
|
|
359
|
+
"Content-Type": "application/json",
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
catch (error) {
|
|
364
|
+
throw new NetworkError(`Failed to verify SMS code: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
365
|
+
}
|
|
366
|
+
let json;
|
|
367
|
+
try {
|
|
368
|
+
const responseJson = await response.json();
|
|
369
|
+
json = responseJson;
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
throw new ApiError(`Failed to parse SMS verification response: ${error instanceof Error ? error.message : "Invalid JSON"}`, response.status);
|
|
373
|
+
}
|
|
374
|
+
// Handle verification errors
|
|
375
|
+
if (response.status === 400) {
|
|
376
|
+
const errorMsg = typeof json === "object" &&
|
|
377
|
+
json !== null &&
|
|
378
|
+
("message" in json || "msg" in json)
|
|
379
|
+
? String(json.message || json.msg)
|
|
380
|
+
: "SMS verification failed";
|
|
381
|
+
throw new ApiError(errorMsg, response.status, "9739");
|
|
382
|
+
}
|
|
383
|
+
if (response.status === 403) {
|
|
384
|
+
throw new ApiError("Account is frozen or access forbidden", response.status, "9403");
|
|
385
|
+
}
|
|
386
|
+
if (!response.ok) {
|
|
387
|
+
const errorMsg = typeof json === "object" &&
|
|
388
|
+
json !== null &&
|
|
389
|
+
("message" in json || "msg" in json)
|
|
390
|
+
? String(json.message || json.msg)
|
|
391
|
+
: "SMS verification failed";
|
|
392
|
+
throw new ApiError(errorMsg, response.status);
|
|
393
|
+
}
|
|
394
|
+
// Handle response data structure - check if data is wrapped in a 'data' field
|
|
395
|
+
const responseData = json.data || json;
|
|
396
|
+
// Validate response structure
|
|
397
|
+
if (!responseData || !responseData.user || !responseData.token) {
|
|
398
|
+
throw new ApiError("Invalid SMS verification response: missing required data", response.status);
|
|
399
|
+
}
|
|
400
|
+
// Build standard response structure
|
|
401
|
+
const smsLoginResponse = {
|
|
402
|
+
app: responseData.app,
|
|
403
|
+
user: responseData.user,
|
|
404
|
+
token: responseData.token,
|
|
405
|
+
user_roles: responseData.user_roles || [],
|
|
406
|
+
user_privileges: responseData.user_privileges || [],
|
|
407
|
+
user_groups: responseData.user_groups || [],
|
|
408
|
+
user_login_methods: responseData.user_login_methods || [],
|
|
409
|
+
};
|
|
410
|
+
return smsLoginResponse;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Registers a new user with SMS verification and optional additional information.
|
|
414
|
+
*
|
|
415
|
+
* @param request - Registration request with SMS verification and optional user data
|
|
416
|
+
* @param options - Optional configuration for the request
|
|
417
|
+
* @returns Promise resolving to registration response with user info and token
|
|
418
|
+
* @throws {ConfigurationError} When required environment variables are missing
|
|
419
|
+
* @throws {ApiError} When registration fails or returns invalid response
|
|
420
|
+
* @throws {NetworkError} When network request fails
|
|
421
|
+
*
|
|
422
|
+
* @example
|
|
423
|
+
* ```typescript
|
|
424
|
+
* import { registerWithSms } from '@tale/client';
|
|
425
|
+
*
|
|
426
|
+
* try {
|
|
427
|
+
* const result = await registerWithSms({
|
|
428
|
+
* sms_id: 'uuid-here',
|
|
429
|
+
* sms_type: 'register',
|
|
430
|
+
* verification_code: '123456',
|
|
431
|
+
* username: 'newuser',
|
|
432
|
+
* password_encrypted: 'encrypted_password'
|
|
433
|
+
* });
|
|
434
|
+
* console.log('Registration successful:', result.user.user_id);
|
|
435
|
+
* console.log('User token:', result.token.token);
|
|
436
|
+
* } catch (error) {
|
|
437
|
+
* console.error('Registration failed:', error.message);
|
|
438
|
+
* }
|
|
439
|
+
* ```
|
|
440
|
+
*/
|
|
441
|
+
export async function registerWithSms(request, options) {
|
|
442
|
+
// Validate required fields for registration
|
|
443
|
+
if (request.sms_type !== "register") {
|
|
444
|
+
throw new ApiError('sms_type must be "register" for registration', 400, "9400");
|
|
445
|
+
}
|
|
446
|
+
// Determine base URL
|
|
447
|
+
const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
|
|
448
|
+
const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
|
|
449
|
+
if (!base) {
|
|
450
|
+
throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
|
|
451
|
+
}
|
|
452
|
+
// Use provided app token or get one from token service
|
|
453
|
+
const authToken = options?.appToken ?? (await getAppToken(options));
|
|
454
|
+
const url = String(base).replace(/\/+$/, "") + "/auth/v1/sms/register/verify";
|
|
455
|
+
// Build request body
|
|
456
|
+
const requestBody = {
|
|
457
|
+
sms_id: request.sms_id.trim(),
|
|
458
|
+
sms_type: request.sms_type,
|
|
459
|
+
verification_code: request.verification_code.trim(),
|
|
460
|
+
username: request.username?.trim(),
|
|
461
|
+
password_encrypted: request.password_encrypted,
|
|
462
|
+
};
|
|
463
|
+
let response;
|
|
464
|
+
try {
|
|
465
|
+
response = await globalThis.fetch(url, {
|
|
466
|
+
method: "POST",
|
|
467
|
+
headers: {
|
|
468
|
+
"x-t-token": authToken,
|
|
469
|
+
"Content-Type": "application/json",
|
|
470
|
+
},
|
|
471
|
+
body: JSON.stringify(requestBody),
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
catch (error) {
|
|
475
|
+
throw new NetworkError(`Failed to register with SMS: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
476
|
+
}
|
|
477
|
+
let json;
|
|
478
|
+
try {
|
|
479
|
+
const responseJson = await response.json();
|
|
480
|
+
json = responseJson;
|
|
481
|
+
}
|
|
482
|
+
catch (error) {
|
|
483
|
+
throw new ApiError(`Failed to parse registration response: ${error instanceof Error ? error.message : "Invalid JSON"}`, response.status);
|
|
484
|
+
}
|
|
485
|
+
// Handle registration errors
|
|
486
|
+
if (response.status === 400) {
|
|
487
|
+
const errorMsg = typeof json === "object" &&
|
|
488
|
+
json !== null &&
|
|
489
|
+
("message" in json || "msg" in json)
|
|
490
|
+
? String(json.message || json.msg)
|
|
491
|
+
: "SMS registration failed";
|
|
492
|
+
throw new ApiError(errorMsg, response.status, "9739");
|
|
493
|
+
}
|
|
494
|
+
if (response.status === 403) {
|
|
495
|
+
throw new ApiError("Account is frozen or access forbidden", response.status, "9403");
|
|
496
|
+
}
|
|
497
|
+
if (!response.ok) {
|
|
498
|
+
const errorMsg = typeof json === "object" &&
|
|
499
|
+
json !== null &&
|
|
500
|
+
("message" in json || "msg" in json)
|
|
501
|
+
? String(json.message || json.msg)
|
|
502
|
+
: "SMS registration failed";
|
|
503
|
+
throw new ApiError(errorMsg, response.status);
|
|
504
|
+
}
|
|
505
|
+
// Handle response data structure - check if data is wrapped in a 'data' field
|
|
506
|
+
const responseData = json.data || json;
|
|
507
|
+
// Validate response structure
|
|
508
|
+
if (!responseData || !responseData.user || !responseData.token) {
|
|
509
|
+
throw new ApiError("Invalid registration response: missing required data", response.status);
|
|
510
|
+
}
|
|
511
|
+
// Build standard response structure
|
|
512
|
+
const registrationResponse = {
|
|
513
|
+
app: responseData.app,
|
|
514
|
+
user: responseData.user,
|
|
515
|
+
token: responseData.token,
|
|
516
|
+
user_roles: responseData.user_roles || [],
|
|
517
|
+
user_privileges: responseData.user_privileges || [],
|
|
518
|
+
user_groups: responseData.user_groups || [],
|
|
519
|
+
user_login_methods: responseData.user_login_methods || [],
|
|
520
|
+
};
|
|
521
|
+
return registrationResponse;
|
|
522
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { AppInfo, User, UserGroup, Role, Privilege, CommonOptions } from "../common/types.js";
|
|
2
|
+
export type { AppInfo, UserGroup };
|
|
3
|
+
/**
|
|
4
|
+
* Auth user information (extends User with non-null remark)
|
|
5
|
+
*/
|
|
6
|
+
export interface AuthUser extends Omit<User, "remark"> {
|
|
7
|
+
remark: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* User token information
|
|
11
|
+
*/
|
|
12
|
+
export interface UserToken {
|
|
13
|
+
token: string;
|
|
14
|
+
scope: string;
|
|
15
|
+
device_name?: string;
|
|
16
|
+
device_fingerprint?: string;
|
|
17
|
+
granted_at: string;
|
|
18
|
+
expired_at: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Auth role (extends base Role with additional fields)
|
|
22
|
+
*/
|
|
23
|
+
export interface AuthRole extends Role {
|
|
24
|
+
assigned_at: string;
|
|
25
|
+
expires_at?: string;
|
|
26
|
+
is_active: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Auth privilege (extends base Privilege with additional fields)
|
|
30
|
+
*/
|
|
31
|
+
export interface AuthPrivilege extends Privilege {
|
|
32
|
+
granted_directly: boolean;
|
|
33
|
+
role_id?: string;
|
|
34
|
+
assigned_at: string;
|
|
35
|
+
expires_at?: string;
|
|
36
|
+
is_active: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Auth user login method
|
|
40
|
+
*/
|
|
41
|
+
export interface AuthUserLoginMethod {
|
|
42
|
+
method_type: string;
|
|
43
|
+
identifier: string;
|
|
44
|
+
is_active: boolean;
|
|
45
|
+
created_at: string;
|
|
46
|
+
last_used_at?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface LoginRequest {
|
|
49
|
+
username: string;
|
|
50
|
+
password: string;
|
|
51
|
+
}
|
|
52
|
+
export interface LoginOptions {
|
|
53
|
+
baseUrl?: string;
|
|
54
|
+
device_name?: string;
|
|
55
|
+
device_fingerprint?: string;
|
|
56
|
+
scope?: string;
|
|
57
|
+
include_rbac?: boolean;
|
|
58
|
+
include_login_methods?: boolean;
|
|
59
|
+
include_user_groups?: boolean;
|
|
60
|
+
include_attributes?: boolean;
|
|
61
|
+
include_acl?: boolean;
|
|
62
|
+
}
|
|
63
|
+
export interface LoginResponse {
|
|
64
|
+
app: AppInfo;
|
|
65
|
+
user: AuthUser;
|
|
66
|
+
token: UserToken;
|
|
67
|
+
user_roles: AuthRole[];
|
|
68
|
+
user_privileges: AuthPrivilege[];
|
|
69
|
+
user_groups: UserGroup[];
|
|
70
|
+
user_login_methods: AuthUserLoginMethod[];
|
|
71
|
+
}
|
|
72
|
+
export interface LoginJson {
|
|
73
|
+
app: AppInfo;
|
|
74
|
+
user: AuthUser;
|
|
75
|
+
token: UserToken;
|
|
76
|
+
user_roles: AuthRole[];
|
|
77
|
+
user_privileges: AuthPrivilege[];
|
|
78
|
+
user_groups: UserGroup[];
|
|
79
|
+
user_login_methods: AuthUserLoginMethod[];
|
|
80
|
+
}
|
|
81
|
+
export interface LoginWithSmsOptions {
|
|
82
|
+
appToken?: string;
|
|
83
|
+
}
|
|
84
|
+
export interface VerifySmsOptions extends CommonOptions {
|
|
85
|
+
device_name?: string;
|
|
86
|
+
device_fingerprint?: string;
|
|
87
|
+
scope?: string;
|
|
88
|
+
}
|
|
89
|
+
export interface SendSmsResponse {
|
|
90
|
+
app_key: string;
|
|
91
|
+
phone: string;
|
|
92
|
+
type: "login" | "register";
|
|
93
|
+
sms_id: string;
|
|
94
|
+
expired_at: string;
|
|
95
|
+
}
|
|
96
|
+
export interface SendSmsJson {
|
|
97
|
+
app_key: string;
|
|
98
|
+
phone: string;
|
|
99
|
+
type: "login" | "register";
|
|
100
|
+
sms_id: string;
|
|
101
|
+
expired_at: string;
|
|
102
|
+
}
|
|
103
|
+
export interface VerifySmsRequest {
|
|
104
|
+
sms_id: string;
|
|
105
|
+
sms_type: "login" | "register";
|
|
106
|
+
verification_code: string;
|
|
107
|
+
username?: string;
|
|
108
|
+
password_encrypted?: string;
|
|
109
|
+
}
|
|
110
|
+
export interface SmsLoginResponse {
|
|
111
|
+
app: AppInfo;
|
|
112
|
+
user: AuthUser;
|
|
113
|
+
token: UserToken;
|
|
114
|
+
user_roles: AuthRole[];
|
|
115
|
+
user_privileges: AuthPrivilege[];
|
|
116
|
+
user_groups: UserGroup[];
|
|
117
|
+
user_login_methods: AuthUserLoginMethod[];
|
|
118
|
+
}
|
|
119
|
+
export interface SmsLoginJson {
|
|
120
|
+
app: AppInfo;
|
|
121
|
+
user: AuthUser;
|
|
122
|
+
token: UserToken;
|
|
123
|
+
user_roles: AuthRole[];
|
|
124
|
+
user_privileges: AuthPrivilege[];
|
|
125
|
+
user_groups: UserGroup[];
|
|
126
|
+
user_login_methods: AuthUserLoginMethod[];
|
|
127
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|