diff --git a/apps/backend/src/services/admin/admin-platform-config-mappers.js b/apps/backend/src/services/admin/admin-platform-config-mappers.js new file mode 100644 index 00000000..3f99c806 --- /dev/null +++ b/apps/backend/src/services/admin/admin-platform-config-mappers.js @@ -0,0 +1,151 @@ +// @ts-check + +export function maskSecret(value) { + const normalized = String(value || '').trim() + if (!normalized) { + return '' + } + + if (normalized.length <= 10) { + return `${normalized.slice(0, 2)}****${normalized.slice(-2)}` + } + + return `${normalized.slice(0, 6)}****${normalized.slice(-6)}` +} + +export function maskPhone(value) { + const normalized = String(value || '').trim() + if (!normalized) { + return '' + } + + if (normalized.length < 7) { + return normalized + } + + return `${normalized.slice(0, 3)}****${normalized.slice(-4)}` +} + +export function mapAdminKhhaoSourceConfig(config = {}) { + return { + enabled: config.enabled !== false, + baseUrl: String(config.baseUrl || '').trim(), + username: String(config.username || '').trim(), + password: String(config.password || '').trim(), + maxCaptchaAttempts: Number(config.maxCaptchaAttempts || 3) || 3, + autoSync: { + enabled: config.autoSync?.enabled === true, + intervalMinutes: Number(config.autoSync?.intervalMinutes || 3) || 3, + pages: Number(config.autoSync?.pages || 2) || 2, + pageSize: Number(config.autoSync?.pageSize || 20) || 20, + }, + } +} + +export function mapAdminKhhaoSyncState(syncState = {}) { + return { + syncFromCreatedAt: String(syncState.syncFromCreatedAt || '').trim(), + lastRunStartedAt: String(syncState.lastRunStartedAt || '').trim(), + lastRunFinishedAt: String(syncState.lastRunFinishedAt || '').trim(), + lastRunStatus: String(syncState.lastRunStatus || '').trim(), + lastErrorMessage: String(syncState.lastErrorMessage || '').trim(), + fetchedCount: Number(syncState.fetchedCount || 0), + syncedCount: Number(syncState.syncedCount || 0), + ignoredCount: Number(syncState.ignoredCount || 0), + watchMode: String(syncState.watchMode || 'normal').trim() || 'normal', + watchOrders: Array.isArray(syncState.watchOrders) + ? syncState.watchOrders.map((item) => ({ + platformOrderId: String(item?.platformOrderId || '').trim(), + shopId: String(item?.shopId || '').trim(), + shopName: String(item?.shopName || '').trim(), + itemTitle: String(item?.itemTitle || '').trim(), + payStatus: String(item?.payStatus || '').trim(), + detectedAt: String(item?.detectedAt || '').trim(), + lastSeenAt: String(item?.lastSeenAt || '').trim(), + })) + : [], + } +} + +export function mapAdminKuaishouEticketShopItem(item = {}) { + return { + shopId: String(item.shopId || '').trim(), + kshopName: String(item.kshopName || '').trim(), + cookie: String(item.cookie || '').trim(), + cookieMasked: maskSecret(item.cookie), + hasCookie: Boolean(String(item.cookie || '').trim()), + userAvatar: String(item.userAvatar || '').trim(), + enabled: item.enabled !== false, + } +} + +export function mapAdminKuaishouEticketSourceConfig(config = {}, shops = []) { + return { + enabled: config.enabled !== false, + baseUrl: String(config.baseUrl || '').trim(), + shops: shops.map((item) => mapAdminKuaishouEticketShopItem(item)), + } +} + +export function mapAdminCloudtentaclesSourceConfig(config = {}) { + return { + enabled: config.enabled !== false, + baseUrl: String(config.baseUrl || '').trim(), + username: String(config.username || '').trim(), + password: String(config.password || '').trim(), + phone: String(config.phone || '').trim(), + deviceId: String(config.deviceId || '-').trim() || '-', + deviceType: Number(config.deviceType || 0), + } +} + +export function mapAdminCloudtentaclesSession(session = {}) { + return { + token: String(session.token || '').trim(), + tokenMasked: maskSecret(session.token), + baseUrl: String(session.baseUrl || '').trim(), + username: String(session.username || '').trim(), + phoneMasked: maskPhone(session.phone), + loggedInAt: String(session.loggedInAt || '').trim(), + deviceId: String(session.deviceId || '-').trim() || '-', + deviceType: Number(session.deviceType || 0), + hasToken: Boolean(String(session.token || '').trim()), + } +} + +export function mapAdminKuaishouCloudFulfillmentItem(item = {}) { + return { + id: String(item.id || '').trim(), + enabled: item.enabled !== false, + priority: Number(item.priority || 100) || 100, + provider: String(item.provider || 'khhao').trim() || 'khhao', + platform: String(item.platform || 'kuaishou').trim() || 'kuaishou', + shopId: String(item.shopId || '').trim(), + internalSkuCode: String(item.internalSkuCode || '').trim(), + internalSkuName: String(item.internalSkuName || '').trim(), + externalSkuCode: String(item.externalSkuCode || '').trim(), + externalItemId: String(item.externalItemId || '').trim(), + externalSkuName: String(item.externalSkuName || '').trim(), + resolvedSkuName: String(item.resolvedSkuName || '').trim(), + cloudSourceKey: String(item.cloudSourceKey || 'default').trim() || 'default', + cloudSkuId: Number(item.cloudSkuId || 0) || 0, + cloudSkuName: String(item.cloudSkuName || '').trim(), + vnKey: String(item.vnKey || '').trim(), + autoBuyEnabled: item.autoBuyEnabled !== false, + minAssetReserve: Number(item.minAssetReserve || 0) || 0, + autoReturnNumberAfterDispatch: item.autoReturnNumberAfterDispatch === true, + autoConsumeAfterDispatch: item.autoConsumeAfterDispatch === true, + kuaishouConsumeShopId: String(item.kuaishouConsumeShopId || '').trim(), + kuaishouConsumeShopName: String(item.kuaishouConsumeShopName || '').trim(), + notes: String(item.notes || '').trim(), + } +} + +export function mapAdminKuaishouCloudFulfillmentSource(config = {}) { + return { + enabled: config.enabled !== false, + items: (Array.isArray(config.items) ? config.items : []).map((item) => + mapAdminKuaishouCloudFulfillmentItem(item), + ), + } +} diff --git a/apps/backend/src/services/admin/admin-platform-config-mappers.test.js b/apps/backend/src/services/admin/admin-platform-config-mappers.test.js new file mode 100644 index 00000000..6e4bea1a --- /dev/null +++ b/apps/backend/src/services/admin/admin-platform-config-mappers.test.js @@ -0,0 +1,196 @@ +import test from 'node:test' +import assert from 'node:assert/strict' + +import { + mapAdminCloudtentaclesSession, + mapAdminKhhaoSourceConfig, + mapAdminKhhaoSyncState, + mapAdminKuaishouCloudFulfillmentSource, + mapAdminKuaishouEticketSourceConfig, + maskPhone, + maskSecret, +} from './admin-platform-config-mappers.js' + +test('maskSecret preserves edges while hiding middle characters', () => { + assert.equal(maskSecret('abcdef1234567890'), 'abcdef****567890') + assert.equal(maskSecret('12345678'), '12****78') + assert.equal(maskSecret(''), '') +}) + +test('maskPhone hides middle digits for mobile numbers', () => { + assert.equal(maskPhone('13812345678'), '138****5678') + assert.equal(maskPhone('123456'), '123456') + assert.equal(maskPhone(''), '') +}) + +test('mapAdminKhhaoSourceConfig normalizes defaults and nested auto sync values', () => { + assert.deepEqual( + mapAdminKhhaoSourceConfig({ + enabled: true, + baseUrl: ' https://admin.khhao.com ', + username: ' user ', + password: ' pass ', + maxCaptchaAttempts: '0', + autoSync: { + enabled: true, + intervalMinutes: '', + pages: '0', + pageSize: '50', + }, + }), + { + enabled: true, + baseUrl: 'https://admin.khhao.com', + username: 'user', + password: 'pass', + maxCaptchaAttempts: 3, + autoSync: { + enabled: true, + intervalMinutes: 3, + pages: 2, + pageSize: 50, + }, + }, + ) +}) + +test('mapAdminKhhaoSyncState normalizes watch orders and fallback values', () => { + assert.deepEqual( + mapAdminKhhaoSyncState({ + watchMode: '', + fetchedCount: '8', + watchOrders: [ + { + platformOrderId: ' A1 ', + shopId: ' S1 ', + shopName: ' 店铺 ', + itemTitle: ' 商品 ', + payStatus: ' unpaid ', + detectedAt: ' t1 ', + lastSeenAt: ' t2 ', + }, + ], + }), + { + syncFromCreatedAt: '', + lastRunStartedAt: '', + lastRunFinishedAt: '', + lastRunStatus: '', + lastErrorMessage: '', + fetchedCount: 8, + syncedCount: 0, + ignoredCount: 0, + watchMode: 'normal', + watchOrders: [ + { + platformOrderId: 'A1', + shopId: 'S1', + shopName: '店铺', + itemTitle: '商品', + payStatus: 'unpaid', + detectedAt: 't1', + lastSeenAt: 't2', + }, + ], + }, + ) +}) + +test('mapAdminKuaishouEticketSourceConfig maps shops and masks cookies', () => { + assert.deepEqual( + mapAdminKuaishouEticketSourceConfig( + { enabled: true, baseUrl: ' https://s.kwaixiaodian.com ' }, + [{ shopId: ' 1 ', kshopName: ' A店 ', cookie: 'cookie-abcdef123456', enabled: true }], + ), + { + enabled: true, + baseUrl: 'https://s.kwaixiaodian.com', + shops: [ + { + shopId: '1', + kshopName: 'A店', + cookie: 'cookie-abcdef123456', + cookieMasked: 'cookie****123456', + hasCookie: true, + userAvatar: '', + enabled: true, + }, + ], + }, + ) +}) + +test('mapAdminCloudtentaclesSession derives masked fields and token presence', () => { + assert.deepEqual( + mapAdminCloudtentaclesSession({ + token: 'abcdef1234567890', + baseUrl: ' https://123.207.217.176 ', + username: ' admin ', + phone: '13812345678', + loggedInAt: ' 2026-05-04T10:00:00.000Z ', + deviceId: ' dev-1 ', + deviceType: '2', + }), + { + token: 'abcdef1234567890', + tokenMasked: 'abcdef****567890', + baseUrl: 'https://123.207.217.176', + username: 'admin', + phoneMasked: '138****5678', + loggedInAt: '2026-05-04T10:00:00.000Z', + deviceId: 'dev-1', + deviceType: 2, + hasToken: true, + }, + ) +}) + +test('mapAdminKuaishouCloudFulfillmentSource normalizes items and defaults', () => { + assert.deepEqual( + mapAdminKuaishouCloudFulfillmentSource({ + enabled: true, + items: [ + { + id: ' item-1 ', + provider: '', + platform: '', + cloudSourceKey: '', + cloudSkuId: '0', + autoConsumeAfterDispatch: true, + kuaishouConsumeShopId: ' ks1 ', + notes: ' note ', + }, + ], + }), + { + enabled: true, + items: [ + { + id: 'item-1', + enabled: true, + priority: 100, + provider: 'khhao', + platform: 'kuaishou', + shopId: '', + internalSkuCode: '', + internalSkuName: '', + externalSkuCode: '', + externalItemId: '', + externalSkuName: '', + resolvedSkuName: '', + cloudSourceKey: 'default', + cloudSkuId: 0, + cloudSkuName: '', + vnKey: '', + autoBuyEnabled: true, + minAssetReserve: 0, + autoReturnNumberAfterDispatch: false, + autoConsumeAfterDispatch: true, + kuaishouConsumeShopId: 'ks1', + kuaishouConsumeShopName: '', + notes: 'note', + }, + ], + }, + ) +}) diff --git a/apps/backend/src/services/admin/admin-platform-config-service.js b/apps/backend/src/services/admin/admin-platform-config-service.js index b60be883..bd934285 100644 --- a/apps/backend/src/services/admin/admin-platform-config-service.js +++ b/apps/backend/src/services/admin/admin-platform-config-service.js @@ -80,6 +80,16 @@ import { formatFenToAmount } from '../../utils/money.js' import { syncConfiguredFulfillmentBindings } from '../bootstrap/fulfillment-bootstrap-service.js' import { normalizeProductName } from '../order/product-match-service.js' import { resolveDisplayShopName } from './admin-read-shared-helpers.js' +import { + mapAdminCloudtentaclesSession, + mapAdminCloudtentaclesSourceConfig, + mapAdminKhhaoSourceConfig, + mapAdminKhhaoSyncState, + mapAdminKuaishouCloudFulfillmentSource, + mapAdminKuaishouEticketSourceConfig, + maskPhone, + maskSecret, +} from './admin-platform-config-mappers.js' /** @typedef {import('../../types/admin-write-inputs.js').AdminAgisoShopConfigSaveInput} AdminAgisoShopConfigSaveInput */ /** @typedef {import('../../types/admin-write-inputs.js').AdminKhhaoSourceConfigInput} AdminKhhaoSourceConfigInput */ @@ -259,39 +269,8 @@ export function getAdminKhhaoSourceConfig() { return { filePath: getKhhaoSourcesFilePath(), stateFilePath: getKhhaoSyncStateFilePath(), - source: { - enabled: config.enabled !== false, - baseUrl: String(config.baseUrl || '').trim(), - username: String(config.username || '').trim(), - password: String(config.password || '').trim(), - maxCaptchaAttempts: Number(config.maxCaptchaAttempts || 3) || 3, - autoSync: { - enabled: config.autoSync?.enabled === true, - intervalMinutes: Number(config.autoSync?.intervalMinutes || 3) || 3, - pages: Number(config.autoSync?.pages || 2) || 2, - pageSize: Number(config.autoSync?.pageSize || 20) || 20, - }, - }, - syncState: { - syncFromCreatedAt: String(syncState.syncFromCreatedAt || '').trim(), - lastRunStartedAt: String(syncState.lastRunStartedAt || '').trim(), - lastRunFinishedAt: String(syncState.lastRunFinishedAt || '').trim(), - lastRunStatus: String(syncState.lastRunStatus || '').trim(), - lastErrorMessage: String(syncState.lastErrorMessage || '').trim(), - fetchedCount: Number(syncState.fetchedCount || 0), - syncedCount: Number(syncState.syncedCount || 0), - ignoredCount: Number(syncState.ignoredCount || 0), - watchMode: String(syncState.watchMode || 'normal').trim() || 'normal', - watchOrders: Array.isArray(syncState.watchOrders) ? syncState.watchOrders.map((item) => ({ - platformOrderId: String(item?.platformOrderId || '').trim(), - shopId: String(item?.shopId || '').trim(), - shopName: String(item?.shopName || '').trim(), - itemTitle: String(item?.itemTitle || '').trim(), - payStatus: String(item?.payStatus || '').trim(), - detectedAt: String(item?.detectedAt || '').trim(), - lastSeenAt: String(item?.lastSeenAt || '').trim(), - })) : [], - }, + source: mapAdminKhhaoSourceConfig(config), + syncState: mapAdminKhhaoSyncState(syncState), } } @@ -300,11 +279,7 @@ export function getAdminKuaishouEticketSourceConfig() { return { filePath: getKuaishouEticketSourceFilePath(), - source: { - enabled: config.enabled !== false, - baseUrl: String(config.baseUrl || '').trim(), - shops: listKuaishouEticketShopConfigs(config).map((item) => mapAdminKuaishouEticketShopItem(item)), - }, + source: mapAdminKuaishouEticketSourceConfig(config, listKuaishouEticketShopConfigs(config)), } } @@ -318,11 +293,7 @@ export function updateAdminKuaishouEticketSourceConfig(payload = /** @type {Admi return { filePath: getKuaishouEticketSourceFilePath(), - source: { - enabled: saved.enabled !== false, - baseUrl: String(saved.baseUrl || '').trim(), - shops: listKuaishouEticketShopConfigs(saved).map((item) => mapAdminKuaishouEticketShopItem(item)), - }, + source: mapAdminKuaishouEticketSourceConfig(saved, listKuaishouEticketShopConfigs(saved)), } } @@ -346,39 +317,8 @@ export function updateAdminKhhaoSourceConfig(payload = /** @type {AdminKhhaoSour return { filePath: getKhhaoSourcesFilePath(), stateFilePath: getKhhaoSyncStateFilePath(), - source: { - enabled: saved.enabled !== false, - baseUrl: String(saved.baseUrl || '').trim(), - username: String(saved.username || '').trim(), - password: String(saved.password || '').trim(), - maxCaptchaAttempts: Number(saved.maxCaptchaAttempts || 3) || 3, - autoSync: { - enabled: saved.autoSync?.enabled === true, - intervalMinutes: Number(saved.autoSync?.intervalMinutes || 3) || 3, - pages: Number(saved.autoSync?.pages || 2) || 2, - pageSize: Number(saved.autoSync?.pageSize || 20) || 20, - }, - }, - syncState: { - syncFromCreatedAt: String(syncState.syncFromCreatedAt || '').trim(), - lastRunStartedAt: String(syncState.lastRunStartedAt || '').trim(), - lastRunFinishedAt: String(syncState.lastRunFinishedAt || '').trim(), - lastRunStatus: String(syncState.lastRunStatus || '').trim(), - lastErrorMessage: String(syncState.lastErrorMessage || '').trim(), - fetchedCount: Number(syncState.fetchedCount || 0), - syncedCount: Number(syncState.syncedCount || 0), - ignoredCount: Number(syncState.ignoredCount || 0), - watchMode: String(syncState.watchMode || 'normal').trim() || 'normal', - watchOrders: Array.isArray(syncState.watchOrders) ? syncState.watchOrders.map((item) => ({ - platformOrderId: String(item?.platformOrderId || '').trim(), - shopId: String(item?.shopId || '').trim(), - shopName: String(item?.shopName || '').trim(), - itemTitle: String(item?.itemTitle || '').trim(), - payStatus: String(item?.payStatus || '').trim(), - detectedAt: String(item?.detectedAt || '').trim(), - lastSeenAt: String(item?.lastSeenAt || '').trim(), - })) : [], - }, + source: mapAdminKhhaoSourceConfig(saved), + syncState: mapAdminKhhaoSyncState(syncState), } } @@ -496,15 +436,7 @@ export function getAdminCloudtentaclesSourceConfig() { return { filePath: getCloudtentaclesSourcesFilePath(), sessionFilePath: getCloudtentaclesSessionFilePath(), - source: { - enabled: config.enabled !== false, - baseUrl: String(config.baseUrl || '').trim(), - username: String(config.username || '').trim(), - password: String(config.password || '').trim(), - phone: String(config.phone || '').trim(), - deviceId: String(config.deviceId || '-').trim() || '-', - deviceType: Number(config.deviceType || 0), - }, + source: mapAdminCloudtentaclesSourceConfig(config), session: mapAdminCloudtentaclesSession(session), } } @@ -527,15 +459,7 @@ export function updateAdminCloudtentaclesSourceConfig(payload = /** @type {Admin return { filePath: getCloudtentaclesSourcesFilePath(), sessionFilePath: getCloudtentaclesSessionFilePath(), - source: { - enabled: saved.enabled !== false, - baseUrl: String(saved.baseUrl || '').trim(), - username: String(saved.username || '').trim(), - password: String(saved.password || '').trim(), - phone: String(saved.phone || '').trim(), - deviceId: String(saved.deviceId || '-').trim() || '-', - deviceType: Number(saved.deviceType || 0), - }, + source: mapAdminCloudtentaclesSourceConfig(saved), session: mapAdminCloudtentaclesSession(session), } } @@ -810,34 +734,7 @@ export function getAdminKuaishouCloudFulfillmentConfig() { return { filePath: getKuaishouCloudFulfillmentFilePath(), - source: { - enabled: config.enabled !== false, - items: (Array.isArray(config.items) ? config.items : []).map((item) => ({ - id: String(item.id || '').trim(), - enabled: item.enabled !== false, - priority: Number(item.priority || 100) || 100, - provider: String(item.provider || 'khhao').trim() || 'khhao', - platform: String(item.platform || 'kuaishou').trim() || 'kuaishou', - shopId: String(item.shopId || '').trim(), - internalSkuCode: String(item.internalSkuCode || '').trim(), - internalSkuName: String(item.internalSkuName || '').trim(), - externalSkuCode: String(item.externalSkuCode || '').trim(), - externalItemId: String(item.externalItemId || '').trim(), - externalSkuName: String(item.externalSkuName || '').trim(), - resolvedSkuName: String(item.resolvedSkuName || '').trim(), - cloudSourceKey: String(item.cloudSourceKey || 'default').trim() || 'default', - cloudSkuId: Number(item.cloudSkuId || 0) || 0, - cloudSkuName: String(item.cloudSkuName || '').trim(), - vnKey: String(item.vnKey || '').trim(), - autoBuyEnabled: item.autoBuyEnabled !== false, - minAssetReserve: Number(item.minAssetReserve || 0) || 0, - autoReturnNumberAfterDispatch: item.autoReturnNumberAfterDispatch === true, - autoConsumeAfterDispatch: item.autoConsumeAfterDispatch === true, - kuaishouConsumeShopId: String(item.kuaishouConsumeShopId || '').trim(), - kuaishouConsumeShopName: String(item.kuaishouConsumeShopName || '').trim(), - notes: String(item.notes || '').trim(), - })), - }, + source: mapAdminKuaishouCloudFulfillmentSource(config), } } @@ -854,34 +751,7 @@ export async function updateAdminKuaishouCloudFulfillmentConfig( return { filePath: getKuaishouCloudFulfillmentFilePath(), - source: { - enabled: saved.enabled !== false, - items: (Array.isArray(saved.items) ? saved.items : []).map((item) => ({ - id: String(item.id || '').trim(), - enabled: item.enabled !== false, - priority: Number(item.priority || 100) || 100, - provider: String(item.provider || 'khhao').trim() || 'khhao', - platform: String(item.platform || 'kuaishou').trim() || 'kuaishou', - shopId: String(item.shopId || '').trim(), - internalSkuCode: String(item.internalSkuCode || '').trim(), - internalSkuName: String(item.internalSkuName || '').trim(), - externalSkuCode: String(item.externalSkuCode || '').trim(), - externalItemId: String(item.externalItemId || '').trim(), - externalSkuName: String(item.externalSkuName || '').trim(), - resolvedSkuName: String(item.resolvedSkuName || '').trim(), - cloudSourceKey: String(item.cloudSourceKey || 'default').trim() || 'default', - cloudSkuId: Number(item.cloudSkuId || 0) || 0, - cloudSkuName: String(item.cloudSkuName || '').trim(), - vnKey: String(item.vnKey || '').trim(), - autoBuyEnabled: item.autoBuyEnabled !== false, - minAssetReserve: Number(item.minAssetReserve || 0) || 0, - autoReturnNumberAfterDispatch: item.autoReturnNumberAfterDispatch === true, - autoConsumeAfterDispatch: item.autoConsumeAfterDispatch === true, - kuaishouConsumeShopId: String(item.kuaishouConsumeShopId || '').trim(), - kuaishouConsumeShopName: String(item.kuaishouConsumeShopName || '').trim(), - notes: String(item.notes || '').trim(), - })), - }, + source: mapAdminKuaishouCloudFulfillmentSource(saved), } } @@ -1256,44 +1126,6 @@ function findMatchingObservedBinding(bindings, observed) { return (Array.isArray(bindings) ? bindings : []).find((binding) => matchesObservedProduct(binding, observed)) || null } -function maskSecret(value) { - const normalized = String(value || '').trim() - if (!normalized) { - return '' - } - - if (normalized.length <= 10) { - return `${normalized.slice(0, 2)}****${normalized.slice(-2)}` - } - - return `${normalized.slice(0, 6)}****${normalized.slice(-6)}` -} - -function maskPhone(value) { - const normalized = String(value || '').trim() - if (!normalized) { - return '' - } - - if (normalized.length < 7) { - return normalized - } - - return `${normalized.slice(0, 3)}****${normalized.slice(-4)}` -} - -function mapAdminKuaishouEticketShopItem(item = {}) { - return { - shopId: String(item.shopId || '').trim(), - kshopName: String(item.kshopName || '').trim(), - cookie: String(item.cookie || '').trim(), - cookieMasked: maskSecret(item.cookie), - hasCookie: Boolean(String(item.cookie || '').trim()), - userAvatar: String(item.userAvatar || '').trim(), - enabled: item.enabled !== false, - } -} - function resolveKuaishouEticketAdminContext(payload = {}) { const savedSource = getKuaishouEticketSourceConfig() const requestedShopId = String(payload.shopId || '').trim() @@ -1331,20 +1163,6 @@ function hasCloudtentaclesCredentialContextChanged(current, next) { ) } -function mapAdminCloudtentaclesSession(session = {}) { - return { - token: String(session.token || '').trim(), - tokenMasked: maskSecret(session.token), - baseUrl: String(session.baseUrl || '').trim(), - username: String(session.username || '').trim(), - phoneMasked: maskPhone(session.phone), - loggedInAt: String(session.loggedInAt || '').trim(), - deviceId: String(session.deviceId || '-').trim() || '-', - deviceType: Number(session.deviceType || 0), - hasToken: Boolean(String(session.token || '').trim()), - } -} - function isPlainObject(value) { return Boolean(value) && typeof value === 'object' && !Array.isArray(value) }