import http from 'http'; import https from 'https'; /** * 账户管理系统 NodeJS SDK * * 该SDK提供了与账户管理系统API交互的功能,支持账号的获取、上传、更新等操作。 * 使用ESM格式,兼容Node.js环境。 * * @example * ```javascript * import AccountsManagerSDK, { AccountStatus } from './accounts-manager-sdk.js'; * * // 基本用法 * const sdk = new AccountsManagerSDK({ * baseUrl: 'https://api.example.com' * }); * * // 指定默认ownerId和platform * const sdk = new AccountsManagerSDK({ * baseUrl: 'https://api.example.com', * ownerId: 'owner123', * platform: 'platform1' * }); * * // 使用默认ownerId和platform,获取2个账号 * const accounts = await sdk.getAccounts(2); * console.log(accounts); * * // 使用默认ownerId上传账号 * const items = [ * { * platform: 'platform1', * customId: 'user123', * data: { username: 'user123', password: 'pass123' }, * status: AccountStatus.AVAILABLE * } * ]; * const result = await sdk.uploadAccounts(items); * console.log(result); * * // 使用默认ownerId更新账号状态 * const updateResult = await sdk.updateAccountStatus(1, AccountStatus.USED); * console.log(updateResult); * ``` */ /** * 业务状态码枚举 * @enum {number} */ export const BusinessCode = { /** 请求成功 */ Success: 0, /** 资源不存在 */ NoResource: 1001, /** 参数无效 */ InvalidParams: 2001, /** 资源冲突 */ ResourceConflict: 3001, /** 权限不足 */ PermissionDenied: 4001, /** 业务错误 */ BusinessError: 5001, }; /** * 账户状态枚举 * @enum {string} */ export const AccountStatus = { /** 可用 */ AVAILABLE: 'available', /** 已使用 */ USED: 'used', /** 已锁定 */ LOCKED: 'locked', /** 已禁用 */ DISABLED: 'disabled', /** 已过期 */ EXPIRED: 'expired', }; /** * 通用响应格式 * @template T - 响应数据类型 */ export class ApiResponse { /** * 业务状态码 * @type {BusinessCode} */ code; /** * 响应消息 * @type {string} */ message; /** * 响应数据 * @type {T|null} */ data; /** * 创建API响应实例 * @param {BusinessCode} code - 业务状态码 * @param {string} message - 响应消息 * @param {T|null} data - 响应数据 */ constructor(code, message, data = null) { this.code = code; this.message = message; this.data = data; } /** * 检查响应是否成功 * @returns {boolean} 是否成功 */ isSuccess() { return this.code === BusinessCode.Success; } } /** * 账户数据模型 */ export class Account { /** * 账户ID,自增主键 * @type {number} */ id; /** * 所有者ID * @type {string} */ ownerId; /** * 平台名称 * @type {string} */ platform; /** * 自定义ID * @type {string} */ customId; /** * 账户数据,JSON格式字符串 * @type {string} */ data; /** * 账户状态 * @type {string} */ status; /** * 备注,可选 * @type {string|undefined} */ notes; /** * 锁定时间,可选 * @type {Date|undefined} */ lockedAt; /** * 创建时间 * @type {Date} */ createdAt; /** * 更新时间 * @type {Date} */ updatedAt; /** * 创建账户实例 * @param {object} data - 账户数据 * @param {number} data.id - 账户ID * @param {string} data.ownerId - 所有者ID * @param {string} data.platform - 平台名称 * @param {string} data.customId - 自定义ID * @param {string} data.data - 账户数据 * @param {string} data.status - 账户状态 * @param {string} [data.notes] - 备注 * @param {string} [data.lockedAt] - 锁定时间 * @param {string} data.createdAt - 创建时间 * @param {string} data.updatedAt - 更新时间 */ constructor({ id, ownerId, platform, customId, data, status, notes, lockedAt, createdAt, updatedAt }) { this.id = id; this.ownerId = ownerId; this.platform = platform; this.customId = customId; this.data = data; this.status = status; this.notes = notes; this.lockedAt = lockedAt ? new Date(lockedAt) : undefined; this.createdAt = new Date(createdAt); this.updatedAt = new Date(updatedAt); } /** * 解析账户数据为对象 * @returns {object} 解析后的账户数据 */ parseData() { try { return JSON.parse(this.data); } catch (error) { console.error('Failed to parse account data:', error); return {}; } } } /** * 脚本上传项 */ export class ScriptUploadItem { /** * 平台名称 * @type {string} */ platform; /** * 自定义ID * @type {string} */ customId; /** * 账户数据,JSON格式字符串 * @type {string} */ data; /** * 账户状态,可选,默认为"available" * @type {string|undefined} */ status; /** * 创建脚本上传项实例 * @param {object} data - 上传项数据 * @param {string} data.platform - 平台名称 * @param {string} data.customId - 自定义ID * @param {string|object} data.data - 账户数据,可以是对象或JSON字符串 * @param {string} [data.status] - 账户状态 */ constructor({ platform, customId, data, status = 'available' }) { this.platform = platform; this.customId = customId; this.data = typeof data === 'string' ? data : JSON.stringify(data); this.status = status; } /** * 将实例转换为普通对象 * @returns {object} 普通对象 */ toObject() { return { platform: this.platform, customId: this.customId, data: this.data, status: this.status, }; } } /** * 账户管理系统SDK */ export default class AccountsManagerSDK { /** * API基础URL * @type {string} */ baseUrl; /** * 创建SDK实例 * @param {object} options - 配置选项 * @param {string} options.baseUrl - API基础URL * @param {string} [options.ownerId] - 默认所有者ID(可选) * @param {string} [options.platform] - 默认平台名称(可选) * @example * ```javascript * // 基本用法 * const sdk = new AccountsManagerSDK({ * baseUrl: 'https://api.example.com' * }); * * // 指定默认ownerId和platform * const sdk = new AccountsManagerSDK({ * baseUrl: 'https://api.example.com', * ownerId: 'owner123', * platform: 'platform1' * }); * ``` */ constructor({ baseUrl, ownerId, platform }) { if (!baseUrl) { throw new Error('baseUrl is required'); } this.baseUrl = baseUrl; this.ownerId = ownerId; this.platform = platform; } /** * 发送HTTP请求 * @private * @param {string} path - 请求路径 * @param {object} options - 请求选项 * @param {string} [options.method='GET'] - 请求方法 * @param {object} [options.headers] - 请求头 * @param {string|object} [options.body] - 请求体 * @returns {Promise} 响应数据 */ async _request(path, options = {}) { const { method = 'GET', headers = {}, body = null, } = options; const url = new URL(path, this.baseUrl); const isHttps = url.protocol === 'https:'; const requestOptions = { hostname: url.hostname, port: url.port || (isHttps ? 443 : 80), path: url.pathname + url.search, method, headers: { 'Content-Type': 'application/json', ...headers, }, }; if (body) { const bodyStr = typeof body === 'string' ? body : JSON.stringify(body); requestOptions.headers['Content-Length'] = Buffer.byteLength(bodyStr); } return new Promise((resolve, reject) => { const req = (isHttps ? https : http).request(requestOptions, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { try { const jsonData = JSON.parse(data); resolve(jsonData); } catch (error) { reject(new Error(`Failed to parse response: ${error.message}`)); } }); }); req.on('error', (error) => { reject(new Error(`Request failed: ${error.message}`)); }); if (body) { const bodyStr = typeof body === 'string' ? body : JSON.stringify(body); req.write(bodyStr); } req.end(); }); } /** * 获取账号 * @param {number} [count=1] - 获取数量,默认1,最大100 * @param {string} [ownerId] - 所有者ID(可选,如果未提供则使用初始化时设置的值) * @param {string} [platform] - 平台名称(可选,如果未提供则使用初始化时设置的值) * @returns {Promise>>} API响应 * @example * ```javascript * // 使用初始化时设置的ownerId和platform,获取2个账号 * const result = await sdk.getAccounts(2); * * // 覆盖初始化时设置的ownerId和platform * const result = await sdk.getAccounts(2, 'owner456', 'platform2'); * * if (result.isSuccess()) { * console.log('获取的账号:', result.data); * } else { * console.error('获取失败:', result.message); * } * ``` */ async getAccounts(count = 1, ownerId, platform) { try { // 使用传入的参数或默认值 const finalOwnerId = ownerId || this.ownerId; const finalPlatform = platform || this.platform; if (!finalOwnerId) { throw new Error('ownerId is required (either in constructor or method call)'); } if (!finalPlatform) { throw new Error('platform is required (either in constructor or method call)'); } const query = new URLSearchParams({ platform: finalPlatform, count: Math.min(Math.max(count, 1), 100).toString(), }); const response = await this._request(`/s/v1/${finalOwnerId}/acquire?${query}`); return new ApiResponse(response.code, response.message, response.data); } catch (error) { return new ApiResponse(BusinessCode.BusinessError, error.message); } } /** * 更新账户状态 * @param {number} accountId - 账户ID * @param {string} newStatus - 新状态 * @param {string} [ownerId] - 所有者ID(可选,如果未提供则使用初始化时设置的值) * @param {string} [notes] - 备注 * @returns {Promise>} API响应 * @example * ```javascript * // 使用初始化时设置的ownerId * const result = await sdk.updateAccountStatus(1, 'used', undefined, '测试使用'); * * // 覆盖初始化时设置的ownerId * const result = await sdk.updateAccountStatus(1, 'used', 'owner456', '测试使用'); * * if (result.isSuccess()) { * console.log('更新成功:', result.data); * } else { * console.error('更新失败:', result.message); * } * ``` */ async updateAccountStatus(accountId, newStatus, ownerId, notes) { try { // 使用传入的参数或默认值 const finalOwnerId = ownerId || this.ownerId; if (!finalOwnerId) { throw new Error('ownerId is required (either in constructor or method call)'); } let path = `/s/v1/${finalOwnerId}/update/${accountId}/${encodeURIComponent(newStatus)}`; if (notes) { path += `?notes=${encodeURIComponent(notes)}`; } const response = await this._request(path); return new ApiResponse(response.code, response.message, response.data); } catch (error) { return new ApiResponse(BusinessCode.BusinessError, error.message); } } /** * 上传账户 * @param {Array} items - 账户数据数组,每个字符串是"----"分隔的数据,格式为"customId----data" * @param {string} [status] - 账户状态(可选,默认为AccountStatus.AVAILABLE) * @param {string} [ownerId] - 所有者ID(可选,如果未提供则使用初始化时设置的值) * @param {string} [platform] - 平台名称(可选,如果未提供则使用初始化时设置的值) * @returns {Promise>} API响应 * @example * ```javascript * // 简化的账户数据字符串数组 * const items = [ * 'user123----{"username":"user123","password":"pass123"}', * 'user456----{"username":"user456","password":"pass456"}', * 'admin@qq.com----{"email":"admin@qq.com","role":"admin"}' * ]; * * // 使用初始化时设置的ownerId和platform,默认状态 * const result = await sdk.uploadAccounts(items); * * // 指定状态为used * const result = await sdk.uploadAccounts(items, AccountStatus.USED); * * // 覆盖初始化时设置的ownerId和platform * const result = await sdk.uploadAccounts(items, AccountStatus.USED, 'owner456', 'platform2'); * * if (result.isSuccess()) { * console.log('上传成功:', result.data); * } else { * console.error('上传失败:', result.message); * } * ``` */ async uploadAccounts(items, status, ownerId, platform) { try { // 使用传入的参数或默认值 const finalStatus = status || AccountStatus.AVAILABLE; const finalOwnerId = ownerId || this.ownerId; const finalPlatform = platform || this.platform; if (!finalOwnerId) { throw new Error('ownerId is required (either in constructor or method call)'); } if (!finalPlatform) { throw new Error('platform is required (either in constructor or method call)'); } // 解析每个字符串并创建ScriptUploadItem实例 const uploadItems = items.map(item => { // 使用"----"分割字符串 const parts = item.split('----'); if (parts.length < 2) { throw new Error(`Invalid item format: "${item}". Expected format: "customId----data"`); } const customId = parts[0].trim(); // 使用完整的原始字符串作为data const data = item; // 使用完整的原始字符串作为data // 创建新的ScriptUploadItem实例 return new ScriptUploadItem({ platform: finalPlatform, customId, data, status: finalStatus, }); }); const body = uploadItems.map(item => ({ platform: item.platform, customId: item.customId, data: item.data, status: item.status, })); const response = await this._request(`/s/v1/${finalOwnerId}/upload`, { method: 'POST', body, }); return new ApiResponse(response.code, response.message, response.data); } catch (error) { return new ApiResponse(BusinessCode.BusinessError, error.message); } } }