644 lines
17 KiB
JavaScript
644 lines
17 KiB
JavaScript
|
|
/**
|
|||
|
|
* 認證相關 API 場景模組
|
|||
|
|
*
|
|||
|
|
* 此模組提供可重複使用的認證相關場景,包括:
|
|||
|
|
* - 註冊(帳號密碼、第三方平台)
|
|||
|
|
* - 登入(帳號密碼、第三方平台)
|
|||
|
|
* - Token 刷新
|
|||
|
|
* - 密碼重設流程
|
|||
|
|
*
|
|||
|
|
* 使用方式:
|
|||
|
|
* import { registerWithCredentials, loginWithCredentials } from './scenarios/apis/auth.js';
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import http from 'k6/http';
|
|||
|
|
import { check, sleep } from 'k6';
|
|||
|
|
import { Rate, Trend } from 'k6/metrics';
|
|||
|
|
|
|||
|
|
// 可選的自定義指標
|
|||
|
|
const registerSuccessRate = new Rate('auth_register_success');
|
|||
|
|
const loginSuccessRate = new Rate('auth_login_success');
|
|||
|
|
const registerDuration = new Trend('auth_register_duration');
|
|||
|
|
const loginDuration = new Trend('auth_login_duration');
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 使用帳號密碼註冊
|
|||
|
|
* @param {Object} options - 配置選項
|
|||
|
|
* @param {string} options.baseUrl - API 基礎 URL
|
|||
|
|
* @param {string} options.loginId - 登入 ID(email 或 phone)
|
|||
|
|
* @param {string} options.password - 密碼
|
|||
|
|
* @param {string} options.accountType - 帳號類型(email/phone/any)
|
|||
|
|
* @param {Object} options.customMetrics - 自定義指標對象(可選)
|
|||
|
|
* @returns {Object} 註冊結果,包含 tokens 和響應
|
|||
|
|
*/
|
|||
|
|
export function registerWithCredentials(options = {}) {
|
|||
|
|
const {
|
|||
|
|
baseUrl = __ENV.BASE_URL || 'http://localhost:8888',
|
|||
|
|
loginId = `test_${Date.now()}@example.com`,
|
|||
|
|
password = 'Test123456!',
|
|||
|
|
accountType = 'email',
|
|||
|
|
customMetrics = null,
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
const url = `${baseUrl}/api/v1/auth/register`;
|
|||
|
|
const payload = JSON.stringify({
|
|||
|
|
auth_method: 'credentials',
|
|||
|
|
login_id: loginId,
|
|||
|
|
credentials: {
|
|||
|
|
password: password,
|
|||
|
|
password_confirm: password,
|
|||
|
|
account_type: accountType,
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const params = {
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
tags: {
|
|||
|
|
name: 'auth_register_credentials',
|
|||
|
|
api: 'auth',
|
|||
|
|
method: 'register',
|
|||
|
|
auth_type: 'credentials',
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const startTime = Date.now();
|
|||
|
|
const res = http.post(url, payload, params);
|
|||
|
|
const duration = Date.now() - startTime;
|
|||
|
|
|
|||
|
|
// 解析響應
|
|||
|
|
let result = null;
|
|||
|
|
let responseData = null;
|
|||
|
|
|
|||
|
|
if (res.status === 200) {
|
|||
|
|
try {
|
|||
|
|
result = JSON.parse(res.body);
|
|||
|
|
|
|||
|
|
// 支持兩種響應格式:
|
|||
|
|
// 1. 直接返回 LoginResp: { access_token, refresh_token, uid, ... }
|
|||
|
|
// 2. 包裝在 Resp 中: { code, message, data: { access_token, ... } }
|
|||
|
|
if (result.data && typeof result.data === 'object') {
|
|||
|
|
// 格式 2: 包裝在 Resp 中
|
|||
|
|
responseData = result.data;
|
|||
|
|
} else if (result.access_token) {
|
|||
|
|
// 格式 1: 直接返回 LoginResp
|
|||
|
|
responseData = result;
|
|||
|
|
} else {
|
|||
|
|
// 無法識別的格式,記錄響應以便調試
|
|||
|
|
console.warn('Unexpected response format. Full response:', JSON.stringify(result));
|
|||
|
|
console.warn('Response keys:', Object.keys(result));
|
|||
|
|
responseData = result;
|
|||
|
|
}
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('Failed to parse register response:', e);
|
|||
|
|
console.error('Response body:', res.body);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 檢查響應結果
|
|||
|
|
const success = check(res, {
|
|||
|
|
'register status is 200': (r) => r.status === 200,
|
|||
|
|
'register has access_token': () => {
|
|||
|
|
return responseData && responseData.access_token && responseData.access_token.length > 0;
|
|||
|
|
},
|
|||
|
|
'register has refresh_token': () => {
|
|||
|
|
return responseData && responseData.refresh_token && responseData.refresh_token.length > 0;
|
|||
|
|
},
|
|||
|
|
'register has uid': () => {
|
|||
|
|
return responseData && responseData.uid && responseData.uid.length > 0;
|
|||
|
|
},
|
|||
|
|
}, { name: 'register_checks' });
|
|||
|
|
|
|||
|
|
// 使用自定義指標(如果提供)
|
|||
|
|
if (customMetrics) {
|
|||
|
|
customMetrics.registerSuccessRate.add(success);
|
|||
|
|
customMetrics.registerDuration.add(duration);
|
|||
|
|
} else {
|
|||
|
|
// 使用預設指標
|
|||
|
|
registerSuccessRate.add(success);
|
|||
|
|
registerDuration.add(duration);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
success,
|
|||
|
|
status: res.status,
|
|||
|
|
response: result,
|
|||
|
|
responseData: responseData,
|
|||
|
|
tokens: responseData && responseData.access_token ? {
|
|||
|
|
accessToken: responseData.access_token,
|
|||
|
|
refreshToken: responseData.refresh_token,
|
|||
|
|
uid: responseData.uid,
|
|||
|
|
} : null,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 使用第三方平台註冊
|
|||
|
|
* @param {Object} options - 配置選項
|
|||
|
|
* @param {string} options.baseUrl - API 基礎 URL
|
|||
|
|
* @param {string} options.loginId - 登入 ID
|
|||
|
|
* @param {string} options.provider - 平台名稱(google/line/apple)
|
|||
|
|
* @param {string} options.token - 平台提供的 Token
|
|||
|
|
* @param {Object} options.customMetrics - 自定義指標對象(可選)
|
|||
|
|
* @returns {Object} 註冊結果
|
|||
|
|
*/
|
|||
|
|
export function registerWithPlatform(options = {}) {
|
|||
|
|
const {
|
|||
|
|
baseUrl = __ENV.BASE_URL || 'https://localhost:8888',
|
|||
|
|
loginId = `platform_${Date.now()}@example.com`,
|
|||
|
|
provider = 'google',
|
|||
|
|
token = 'mock_platform_token',
|
|||
|
|
customMetrics = null,
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
const url = `${baseUrl}/api/v1/auth/register`;
|
|||
|
|
const payload = JSON.stringify({
|
|||
|
|
auth_method: 'platform',
|
|||
|
|
login_id: loginId,
|
|||
|
|
platform: {
|
|||
|
|
provider: provider,
|
|||
|
|
token: token,
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const params = {
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
tags: {
|
|||
|
|
name: 'auth_register_platform',
|
|||
|
|
api: 'auth',
|
|||
|
|
method: 'register',
|
|||
|
|
auth_type: 'platform',
|
|||
|
|
provider: provider,
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const startTime = Date.now();
|
|||
|
|
const res = http.post(url, payload, params);
|
|||
|
|
const duration = Date.now() - startTime;
|
|||
|
|
|
|||
|
|
const success = check(res, {
|
|||
|
|
'register platform status is 200': (r) => r.status === 200,
|
|||
|
|
'register platform has tokens': (r) => {
|
|||
|
|
try {
|
|||
|
|
const body = JSON.parse(r.body);
|
|||
|
|
return body.access_token && body.refresh_token;
|
|||
|
|
} catch {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
}, { name: 'register_platform_checks' });
|
|||
|
|
|
|||
|
|
if (customMetrics) {
|
|||
|
|
customMetrics.registerSuccessRate?.add(success);
|
|||
|
|
customMetrics.registerDuration?.add(duration);
|
|||
|
|
} else {
|
|||
|
|
registerSuccessRate.add(success);
|
|||
|
|
registerDuration.add(duration);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let result = null;
|
|||
|
|
if (res.status === 200) {
|
|||
|
|
try {
|
|||
|
|
result = JSON.parse(res.body);
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('Failed to parse register platform response:', e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
success,
|
|||
|
|
status: res.status,
|
|||
|
|
response: result,
|
|||
|
|
tokens: result ? {
|
|||
|
|
accessToken: result.access_token,
|
|||
|
|
refreshToken: result.refresh_token,
|
|||
|
|
uid: result.uid,
|
|||
|
|
} : null,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 使用帳號密碼登入
|
|||
|
|
* @param {Object} options - 配置選項
|
|||
|
|
* @param {string} options.baseUrl - API 基礎 URL
|
|||
|
|
* @param {string} options.loginId - 登入 ID
|
|||
|
|
* @param {string} options.password - 密碼
|
|||
|
|
* @param {Object} options.customMetrics - 自定義指標對象(可選)
|
|||
|
|
* @returns {Object} 登入結果
|
|||
|
|
*/
|
|||
|
|
export function loginWithCredentials(options = {}) {
|
|||
|
|
const {
|
|||
|
|
baseUrl = __ENV.BASE_URL || 'http://localhost:8888',
|
|||
|
|
loginId,
|
|||
|
|
password,
|
|||
|
|
customMetrics = null,
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
if (!loginId || !password) {
|
|||
|
|
throw new Error('loginId and password are required for credentials login');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const url = `${baseUrl}/api/v1/auth/sessions`;
|
|||
|
|
const payload = JSON.stringify({
|
|||
|
|
auth_method: 'credentials',
|
|||
|
|
login_id: loginId,
|
|||
|
|
credentials: {
|
|||
|
|
password: password,
|
|||
|
|
password_confirm: password,
|
|||
|
|
account_type: 'email',
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const params = {
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
tags: {
|
|||
|
|
name: 'auth_login_credentials',
|
|||
|
|
api: 'auth',
|
|||
|
|
method: 'login',
|
|||
|
|
auth_type: 'credentials',
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const startTime = Date.now();
|
|||
|
|
const res = http.post(url, payload, params);
|
|||
|
|
const duration = Date.now() - startTime;
|
|||
|
|
|
|||
|
|
// 解析響應
|
|||
|
|
let result = null;
|
|||
|
|
let responseData = null;
|
|||
|
|
|
|||
|
|
if (res.status === 200) {
|
|||
|
|
try {
|
|||
|
|
result = JSON.parse(res.body);
|
|||
|
|
|
|||
|
|
// 支持兩種響應格式:
|
|||
|
|
// 1. 直接返回 LoginResp: { access_token, refresh_token, uid, ... }
|
|||
|
|
// 2. 包裝在 Resp 中: { code, message, data: { access_token, ... } }
|
|||
|
|
if (result.data && typeof result.data === 'object') {
|
|||
|
|
// 格式 2: 包裝在 Resp 中
|
|||
|
|
responseData = result.data;
|
|||
|
|
} else if (result.access_token) {
|
|||
|
|
// 格式 1: 直接返回 LoginResp
|
|||
|
|
responseData = result;
|
|||
|
|
} else {
|
|||
|
|
// 無法識別的格式,記錄響應以便調試
|
|||
|
|
console.warn('Unexpected login response format. Full response:', JSON.stringify(result));
|
|||
|
|
console.warn('Response keys:', Object.keys(result));
|
|||
|
|
responseData = result;
|
|||
|
|
}
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('Failed to parse login response:', e);
|
|||
|
|
console.error('Response body:', res.body);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 檢查響應結果
|
|||
|
|
const success = check(res, {
|
|||
|
|
'login status is 200': (r) => r.status === 200,
|
|||
|
|
'login has access_token': () => {
|
|||
|
|
return responseData && responseData.access_token && responseData.access_token.length > 0;
|
|||
|
|
},
|
|||
|
|
'login has refresh_token': () => {
|
|||
|
|
return responseData && responseData.refresh_token && responseData.refresh_token.length > 0;
|
|||
|
|
},
|
|||
|
|
}, { name: 'login_checks' });
|
|||
|
|
|
|||
|
|
if (customMetrics) {
|
|||
|
|
customMetrics.loginSuccessRate?.add(success);
|
|||
|
|
customMetrics.loginDuration?.add(duration);
|
|||
|
|
} else {
|
|||
|
|
loginSuccessRate.add(success);
|
|||
|
|
loginDuration.add(duration);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
success,
|
|||
|
|
status: res.status,
|
|||
|
|
response: result,
|
|||
|
|
responseData: responseData,
|
|||
|
|
tokens: responseData && responseData.access_token ? {
|
|||
|
|
accessToken: responseData.access_token,
|
|||
|
|
refreshToken: responseData.refresh_token,
|
|||
|
|
uid: responseData.uid,
|
|||
|
|
} : null,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 使用第三方平台登入
|
|||
|
|
* @param {Object} options - 配置選項
|
|||
|
|
* @param {string} options.baseUrl - API 基礎 URL
|
|||
|
|
* @param {string} options.loginId - 登入 ID
|
|||
|
|
* @param {string} options.provider - 平台名稱
|
|||
|
|
* @param {string} options.token - 平台 Token
|
|||
|
|
* @param {Object} options.customMetrics - 自定義指標對象(可選)
|
|||
|
|
* @returns {Object} 登入結果
|
|||
|
|
*/
|
|||
|
|
export function loginWithPlatform(options = {}) {
|
|||
|
|
const {
|
|||
|
|
baseUrl = __ENV.BASE_URL || 'https://localhost:8888',
|
|||
|
|
loginId,
|
|||
|
|
provider = 'google',
|
|||
|
|
token = 'mock_platform_token',
|
|||
|
|
customMetrics = null,
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
if (!loginId) {
|
|||
|
|
throw new Error('loginId is required for platform login');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const url = `${baseUrl}/api/v1/auth/sessions`;
|
|||
|
|
const payload = JSON.stringify({
|
|||
|
|
auth_method: 'platform',
|
|||
|
|
login_id: loginId,
|
|||
|
|
platform: {
|
|||
|
|
provider: provider,
|
|||
|
|
token: token,
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const params = {
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
tags: {
|
|||
|
|
name: 'auth_login_platform',
|
|||
|
|
api: 'auth',
|
|||
|
|
method: 'login',
|
|||
|
|
auth_type: 'platform',
|
|||
|
|
provider: provider,
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const startTime = Date.now();
|
|||
|
|
const res = http.post(url, payload, params);
|
|||
|
|
const duration = Date.now() - startTime;
|
|||
|
|
|
|||
|
|
const success = check(res, {
|
|||
|
|
'login platform status is 200': (r) => r.status === 200,
|
|||
|
|
'login platform has tokens': (r) => {
|
|||
|
|
try {
|
|||
|
|
const body = JSON.parse(r.body);
|
|||
|
|
return body.access_token && body.refresh_token;
|
|||
|
|
} catch {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
}, { name: 'login_platform_checks' });
|
|||
|
|
|
|||
|
|
if (customMetrics) {
|
|||
|
|
customMetrics.loginSuccessRate?.add(success);
|
|||
|
|
customMetrics.loginDuration?.add(duration);
|
|||
|
|
} else {
|
|||
|
|
loginSuccessRate.add(success);
|
|||
|
|
loginDuration.add(duration);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let result = null;
|
|||
|
|
if (res.status === 200) {
|
|||
|
|
try {
|
|||
|
|
result = JSON.parse(res.body);
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('Failed to parse login platform response:', e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
success,
|
|||
|
|
status: res.status,
|
|||
|
|
response: result,
|
|||
|
|
tokens: result ? {
|
|||
|
|
accessToken: result.access_token,
|
|||
|
|
refreshToken: result.refresh_token,
|
|||
|
|
uid: result.uid,
|
|||
|
|
} : null,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 刷新 Access Token
|
|||
|
|
* @param {Object} options - 配置選項
|
|||
|
|
* @param {string} options.baseUrl - API 基礎 URL
|
|||
|
|
* @param {string} options.accessToken - 當前的 Access Token
|
|||
|
|
* @param {string} options.refreshToken - Refresh Token
|
|||
|
|
* @param {Object} options.customMetrics - 自定義指標對象(可選)
|
|||
|
|
* @returns {Object} 刷新結果
|
|||
|
|
*/
|
|||
|
|
export function refreshToken(options = {}) {
|
|||
|
|
const {
|
|||
|
|
baseUrl = __ENV.BASE_URL || 'https://localhost:8888',
|
|||
|
|
accessToken,
|
|||
|
|
refreshToken,
|
|||
|
|
customMetrics = null,
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
if (!accessToken || !refreshToken) {
|
|||
|
|
throw new Error('accessToken and refreshToken are required');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const url = `${baseUrl}/api/v1/auth/sessions/refresh`;
|
|||
|
|
const payload = JSON.stringify({
|
|||
|
|
access_token: accessToken,
|
|||
|
|
refresh_token: refreshToken,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const params = {
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
tags: {
|
|||
|
|
name: 'auth_refresh_token',
|
|||
|
|
api: 'auth',
|
|||
|
|
method: 'refresh_token',
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const res = http.post(url, payload, params);
|
|||
|
|
|
|||
|
|
const success = check(res, {
|
|||
|
|
'refresh token status is 200': (r) => r.status === 200,
|
|||
|
|
'refresh token has new access_token': (r) => {
|
|||
|
|
try {
|
|||
|
|
const body = JSON.parse(r.body);
|
|||
|
|
console.log('refresh token response:', body.data);
|
|||
|
|
return body.data.access_token && body.data.access_token.length > 0;
|
|||
|
|
} catch {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
}, { name: 'refresh_token_checks' });
|
|||
|
|
|
|||
|
|
let result = null;
|
|||
|
|
if (res.status === 200) {
|
|||
|
|
try {
|
|||
|
|
result = JSON.parse(res.body.data);
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('Failed to parse refresh token response:', e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
success,
|
|||
|
|
status: res.status,
|
|||
|
|
response: result,
|
|||
|
|
tokens: result ? {
|
|||
|
|
accessToken: result.access_token,
|
|||
|
|
refreshToken: result.refresh_token,
|
|||
|
|
} : null,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 請求密碼重設驗證碼
|
|||
|
|
* @param {Object} options - 配置選項
|
|||
|
|
* @param {string} options.baseUrl - API 基礎 URL
|
|||
|
|
* @param {string} options.identifier - 使用者帳號(email 或 phone)
|
|||
|
|
* @param {string} options.accountType - 帳號類型(email/phone)
|
|||
|
|
* @returns {Object} 請求結果
|
|||
|
|
*/
|
|||
|
|
export function requestPasswordReset(options = {}) {
|
|||
|
|
const {
|
|||
|
|
baseUrl = __ENV.BASE_URL || 'http://localhost:8888',
|
|||
|
|
identifier,
|
|||
|
|
accountType = 'email',
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
if (!identifier) {
|
|||
|
|
throw new Error('identifier is required');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const url = `${baseUrl}/api/v1/auth/password-resets/request`;
|
|||
|
|
const payload = JSON.stringify({
|
|||
|
|
identifier: identifier,
|
|||
|
|
account_type: accountType,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const params = {
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
tags: {
|
|||
|
|
name: 'auth_request_password_reset',
|
|||
|
|
api: 'auth',
|
|||
|
|
method: 'request_password_reset',
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const res = http.post(url, payload, params);
|
|||
|
|
|
|||
|
|
const success = check(res, {
|
|||
|
|
'request password reset status is 200': (r) => r.status === 200,
|
|||
|
|
}, { name: 'request_password_reset_checks' });
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
success,
|
|||
|
|
status: res.status,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 驗證密碼重設驗證碼
|
|||
|
|
* @param {Object} options - 配置選項
|
|||
|
|
* @param {string} options.baseUrl - API 基礎 URL
|
|||
|
|
* @param {string} options.identifier - 使用者帳號
|
|||
|
|
* @param {string} options.verifyCode - 驗證碼
|
|||
|
|
* @returns {Object} 驗證結果
|
|||
|
|
*/
|
|||
|
|
export function verifyPasswordResetCode(options = {}) {
|
|||
|
|
const {
|
|||
|
|
baseUrl = __ENV.BASE_URL || 'http://localhost:8888',
|
|||
|
|
identifier,
|
|||
|
|
verifyCode,
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
if (!identifier || !verifyCode) {
|
|||
|
|
throw new Error('identifier and verifyCode are required');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const url = `${baseUrl}/api/v1/auth/password-resets/verify`;
|
|||
|
|
const payload = JSON.stringify({
|
|||
|
|
identifier: identifier,
|
|||
|
|
verify_code: verifyCode,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const params = {
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
tags: {
|
|||
|
|
name: 'auth_verify_password_reset_code',
|
|||
|
|
api: 'auth',
|
|||
|
|
method: 'verify_password_reset_code',
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const res = http.post(url, payload, params);
|
|||
|
|
|
|||
|
|
const success = check(res, {
|
|||
|
|
'verify password reset code status is 200': (r) => r.status === 200,
|
|||
|
|
}, { name: 'verify_password_reset_code_checks' });
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
success,
|
|||
|
|
status: res.status,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 執行密碼重設
|
|||
|
|
* @param {Object} options - 配置選項
|
|||
|
|
* @param {string} options.baseUrl - API 基礎 URL
|
|||
|
|
* @param {string} options.identifier - 使用者帳號
|
|||
|
|
* @param {string} options.verifyCode - 驗證碼
|
|||
|
|
* @param {string} options.newPassword - 新密碼
|
|||
|
|
* @returns {Object} 重設結果
|
|||
|
|
*/
|
|||
|
|
export function resetPassword(options = {}) {
|
|||
|
|
const {
|
|||
|
|
baseUrl = __ENV.BASE_URL || 'https://localhost:8888',
|
|||
|
|
identifier,
|
|||
|
|
verifyCode,
|
|||
|
|
newPassword,
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
if (!identifier || !verifyCode || !newPassword) {
|
|||
|
|
throw new Error('identifier, verifyCode, and newPassword are required');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const url = `${baseUrl}/api/v1/auth/password-resets`;
|
|||
|
|
const payload = JSON.stringify({
|
|||
|
|
identifier: identifier,
|
|||
|
|
verify_code: verifyCode,
|
|||
|
|
password: newPassword,
|
|||
|
|
password_confirm: newPassword,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const params = {
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
tags: {
|
|||
|
|
name: 'auth_reset_password',
|
|||
|
|
api: 'auth',
|
|||
|
|
method: 'reset_password',
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const res = http.put(url, payload, params);
|
|||
|
|
|
|||
|
|
const success = check(res, {
|
|||
|
|
'reset password status is 200': (r) => r.status === 200,
|
|||
|
|
}, { name: 'reset_password_checks' });
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
success,
|
|||
|
|
status: res.status,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|