feat(cloudtentacles): 后端 Admin 层多账号支持 - admin-service/context/routes/types 改造

This commit is contained in:
yml
2026-05-20 00:35:19 +08:00
parent e812ea2cc4
commit f6acc0b9de
9 changed files with 1386 additions and 839 deletions
File diff suppressed because it is too large Load Diff
@@ -1,29 +1,33 @@
// @ts-check
import {
getCloudtentaclesSourceConfig,
getCloudtentaclesSourceByKey,
getCloudtentaclesSourcesFilePath,
saveCloudtentaclesSourceConfig,
} from '../../platforms/cloudtentacles/source-config-service.js'
listCloudtentaclesSources,
saveCloudtentaclesSourceByKey,
saveCloudtentaclesSourcesList,
deleteCloudtentaclesSourceByKey,
} from "../../platforms/cloudtentacles/source-config-service.js";
import {
clearCloudtentaclesSessionState,
clearCloudtentaclesSessionStateByKey,
getCloudtentaclesSessionFilePath,
getCloudtentaclesSessionState,
saveCloudtentaclesSessionState,
} from '../../platforms/cloudtentacles/session-state-service.js'
getCloudtentaclesSessionStateByKey,
saveCloudtentaclesSessionStateByKey,
getAllCloudtentaclesSessionStates,
} from "../../platforms/cloudtentacles/session-state-service.js";
import {
loginCloudtentaclesSession,
sendCloudtentaclesSmsCode,
validateCloudtentaclesSession,
} from '../../platforms/cloudtentacles/session-service.js'
} from "../../platforms/cloudtentacles/session-service.js";
import {
buyCloudtentaclesSku,
getCloudtentaclesAsset,
getCloudtentaclesCategories,
listCloudtentaclesSku,
useCloudtentaclesSku,
} from '../../platforms/cloudtentacles/catalog-service.js'
import { getCloudtentaclesKnapsack } from '../../platforms/cloudtentacles/knapsack-service.js'
} from "../../platforms/cloudtentacles/catalog-service.js";
import { getCloudtentaclesKnapsack } from "../../platforms/cloudtentacles/knapsack-service.js";
import {
appointCloudtentaclesVirtualNumber,
backCloudtentaclesVirtualNumber,
@@ -32,22 +36,25 @@ import {
getCloudtentaclesBindUrl,
listCloudtentaclesVirtualNumbers,
verifyCloudtentaclesLoginCode,
} from '../../platforms/cloudtentacles/virtual-number-service.js'
import { runCloudtentaclesFullDebugFlow } from '../../platforms/cloudtentacles/debug-flow-service.js'
import { hasCloudtentaclesCredentialContextChanged, pickFirstNonEmpty } from './context.js'
} from "../../platforms/cloudtentacles/virtual-number-service.js";
import { runCloudtentaclesFullDebugFlow } from "../../platforms/cloudtentacles/debug-flow-service.js";
import {
hasCloudtentaclesCredentialContextChanged,
pickFirstNonEmpty,
} from "./context.js";
import {
buildAdminCloudtentaclesLoginResult,
buildAdminCloudtentaclesPersistedSessionPayload,
buildAdminCloudtentaclesValidateResult,
resolveAdminCloudtentaclesCredentialPayload,
resolveAdminCloudtentaclesSessionPayload,
} from './cloudtentacles.js'
import { normalizeAdminCloudtentaclesSourceConfigPayload } from './writes.js'
} from "./cloudtentacles.js";
import { normalizeAdminCloudtentaclesSourceConfigPayload } from "./writes.js";
import {
mapAdminCloudtentaclesSession,
mapAdminCloudtentaclesSourceConfig,
maskPhone,
} from './mappers.js'
} from "./mappers.js";
/** @typedef {import('../../../types/admin-write-inputs.js').AdminCloudtentaclesSourceConfigInput} AdminCloudtentaclesSourceConfigInput */
/** @typedef {import('../../../types/admin-write-inputs.js').AdminCloudtentaclesSendSmsCodeInput} AdminCloudtentaclesSendSmsCodeInput */
@@ -59,192 +66,319 @@ import {
/** @typedef {import('../../../types/admin-write-inputs.js').AdminCloudtentaclesVirtualNumberInput} AdminCloudtentaclesVirtualNumberInput */
/** @typedef {import('../../../types/admin-write-inputs.js').AdminCloudtentaclesFullFlowInput} AdminCloudtentaclesFullFlowInput */
export function getAdminCloudtentaclesSourceConfig() {
const config = getCloudtentaclesSourceConfig()
const session = getCloudtentaclesSessionState()
export function listAdminCloudtentaclesSources() {
const list = listCloudtentaclesSources();
const sessions = getAllCloudtentaclesSessionStates();
const sources = Array.isArray(list.sources)
? list.sources.map((s) => mapAdminCloudtentaclesSourceConfig(s))
: [];
const mappedSessions = {};
for (const [key, session] of Object.entries(sessions.sessions || {})) {
mappedSessions[key] = mapAdminCloudtentaclesSession(session);
}
return {
filePath: getCloudtentaclesSourcesFilePath(),
sessionFilePath: getCloudtentaclesSessionFilePath(),
source: mapAdminCloudtentaclesSourceConfig(config),
session: mapAdminCloudtentaclesSession(session),
}
enabled: list.enabled,
sources,
sessions: mappedSessions,
};
}
/** @param {AdminCloudtentaclesSourceConfigInput} [payload] */
export function updateAdminCloudtentaclesSourceConfig(payload = /** @type {AdminCloudtentaclesSourceConfigInput} */ ({})) {
const current = getCloudtentaclesSourceConfig()
const saved = saveCloudtentaclesSourceConfig(normalizeAdminCloudtentaclesSourceConfigPayload(payload))
const shouldClearSession = hasCloudtentaclesCredentialContextChanged(current, saved)
const session = shouldClearSession ? clearCloudtentaclesSessionState() : getCloudtentaclesSessionState()
export function updateAdminCloudtentaclesSourceConfig(
payload = /** @type {AdminCloudtentaclesSourceConfigInput} */ ({})
) {
// Batch save: if payload.sources is an array, save the whole list
if (Array.isArray(payload.sources)) {
const normalizedList = payload.sources.map((item) =>
normalizeAdminCloudtentaclesSourceConfigPayload(item)
);
const savedList = saveCloudtentaclesSourcesList({
enabled: payload.enabled !== false,
sources: normalizedList,
});
const sessions = getAllCloudtentaclesSessionStates();
const sources = Array.isArray(savedList.sources)
? savedList.sources.map((s) => mapAdminCloudtentaclesSourceConfig(s))
: [];
const mappedSessions = {};
for (const [key, session] of Object.entries(sessions.sessions || {})) {
mappedSessions[key] = mapAdminCloudtentaclesSession(session);
}
return {
filePath: getCloudtentaclesSourcesFilePath(),
sessionFilePath: getCloudtentaclesSessionFilePath(),
enabled: savedList.enabled,
sources,
sessions: mappedSessions,
};
}
// Single source save
const sourceKey = String(payload.sourceKey || "").trim() || "default";
const current = getCloudtentaclesSourceByKey(sourceKey);
const normalized = normalizeAdminCloudtentaclesSourceConfigPayload({
...payload,
sourceKey,
});
const saved = saveCloudtentaclesSourceByKey(sourceKey, normalized);
const shouldClearSession = hasCloudtentaclesCredentialContextChanged(
current,
saved
);
const session = shouldClearSession
? (clearCloudtentaclesSessionStateByKey(sourceKey),
getCloudtentaclesSessionStateByKey(sourceKey))
: getCloudtentaclesSessionStateByKey(sourceKey);
return {
filePath: getCloudtentaclesSourcesFilePath(),
sessionFilePath: getCloudtentaclesSessionFilePath(),
source: mapAdminCloudtentaclesSourceConfig(saved),
session: mapAdminCloudtentaclesSession(session),
};
}
/** @param {string} sourceKey */
export function deleteAdminCloudtentaclesSource(sourceKey) {
const key = String(sourceKey || "").trim();
if (!key) {
throw new Error("sourceKey is required for deletion");
}
clearCloudtentaclesSessionStateByKey(key);
deleteCloudtentaclesSourceByKey(key);
}
/** @param {AdminCloudtentaclesSendSmsCodeInput} [payload] */
export async function sendAdminCloudtentaclesSmsCode(payload = /** @type {AdminCloudtentaclesSendSmsCodeInput} */ ({})) {
const result = await sendCloudtentaclesSmsCode(resolveAdminCloudtentaclesCredentialPayload(payload))
export async function sendAdminCloudtentaclesSmsCode(
payload = /** @type {AdminCloudtentaclesSendSmsCodeInput} */ ({})
) {
const result = await sendCloudtentaclesSmsCode(
resolveAdminCloudtentaclesCredentialPayload(payload)
);
return {
...result,
phoneMasked: maskPhone(result.phone),
}
};
}
/** @param {AdminCloudtentaclesTestLoginInput} [payload] */
export async function testAdminCloudtentaclesLogin(payload = /** @type {AdminCloudtentaclesTestLoginInput} */ ({})) {
const savedSource = getCloudtentaclesSourceConfig()
const credentialContext = resolveAdminCloudtentaclesCredentialPayload(payload, { savedSource })
export async function testAdminCloudtentaclesLogin(
payload = /** @type {AdminCloudtentaclesTestLoginInput} */ ({})
) {
const sourceKey = String(payload.sourceKey || "").trim() || "default";
const savedSource = getCloudtentaclesSourceByKey(sourceKey) || {};
const credentialContext = resolveAdminCloudtentaclesCredentialPayload(
payload,
{ savedSource }
);
const session = await loginCloudtentaclesSession({
...credentialContext,
code: String(payload.code || '').trim(),
})
const savedSession = saveCloudtentaclesSessionState(buildAdminCloudtentaclesPersistedSessionPayload(session, {
code: String(payload.code || "").trim(),
});
const savedSession = saveCloudtentaclesSessionStateByKey(
sourceKey,
buildAdminCloudtentaclesPersistedSessionPayload(session, {
deviceId: credentialContext.deviceId,
deviceType: credentialContext.deviceType,
}))
})
);
return buildAdminCloudtentaclesLoginResult(session, savedSession)
return buildAdminCloudtentaclesLoginResult(session, savedSession, sourceKey);
}
/** @param {AdminCloudtentaclesValidateSessionInput} [payload] */
export async function validateAdminCloudtentaclesSession(payload = /** @type {AdminCloudtentaclesValidateSessionInput} */ ({})) {
const savedSource = getCloudtentaclesSourceConfig()
const persistedSession = getCloudtentaclesSessionState()
const sessionContext = resolveAdminCloudtentaclesSessionPayload(payload, { savedSource, persistedSession })
const session = await validateCloudtentaclesSession(sessionContext)
const savedSession = saveCloudtentaclesSessionState(buildAdminCloudtentaclesPersistedSessionPayload(session, {
username: pickFirstNonEmpty([persistedSession.username, savedSource.username]),
export async function validateAdminCloudtentaclesSession(
payload = /** @type {AdminCloudtentaclesValidateSessionInput} */ ({})
) {
const sourceKey = String(payload.sourceKey || "").trim() || "default";
const savedSource = getCloudtentaclesSourceByKey(sourceKey) || {};
const persistedSession = getCloudtentaclesSessionStateByKey(sourceKey) || {};
const sessionContext = resolveAdminCloudtentaclesSessionPayload(payload, {
savedSource,
persistedSession,
});
const session = await validateCloudtentaclesSession(sessionContext);
const savedSession = saveCloudtentaclesSessionStateByKey(
sourceKey,
buildAdminCloudtentaclesPersistedSessionPayload(session, {
username: pickFirstNonEmpty([
persistedSession.username,
savedSource.username,
]),
phone: pickFirstNonEmpty([persistedSession.phone, savedSource.phone]),
deviceId: sessionContext.deviceId,
deviceType: sessionContext.deviceType,
}))
})
);
return buildAdminCloudtentaclesValidateResult(session, savedSession)
return buildAdminCloudtentaclesValidateResult(
session,
savedSession,
sourceKey
);
}
/** @param {AdminCloudtentaclesCatalogQueryInput} [payload] */
export async function getAdminCloudtentaclesAsset(payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({})) {
return getCloudtentaclesAsset(resolveAdminCloudtentaclesSessionPayload(payload))
export async function getAdminCloudtentaclesAsset(
payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({})
) {
return getCloudtentaclesAsset(
resolveAdminCloudtentaclesSessionPayload(payload)
);
}
/** @param {AdminCloudtentaclesCatalogQueryInput} [payload] */
export async function getAdminCloudtentaclesCategories(payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({})) {
return getCloudtentaclesCategories(resolveAdminCloudtentaclesSessionPayload(payload))
export async function getAdminCloudtentaclesCategories(
payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({})
) {
return getCloudtentaclesCategories(
resolveAdminCloudtentaclesSessionPayload(payload)
);
}
/** @param {AdminCloudtentaclesCatalogQueryInput} [payload] */
export async function getAdminCloudtentaclesSkuList(payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({})) {
return listCloudtentaclesSku(resolveAdminCloudtentaclesSessionPayload(payload))
export async function getAdminCloudtentaclesSkuList(
payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({})
) {
return listCloudtentaclesSku(
resolveAdminCloudtentaclesSessionPayload(payload)
);
}
/** @param {AdminCloudtentaclesSkuBuyInput} [payload] */
export async function buyAdminCloudtentaclesSku(payload = /** @type {AdminCloudtentaclesSkuBuyInput} */ ({})) {
const context = resolveAdminCloudtentaclesSessionPayload(payload)
export async function buyAdminCloudtentaclesSku(
payload = /** @type {AdminCloudtentaclesSkuBuyInput} */ ({})
) {
const context = resolveAdminCloudtentaclesSessionPayload(payload);
return buyCloudtentaclesSku({
...context,
id: payload.id,
count: payload.count,
})
});
}
/** @param {AdminCloudtentaclesSkuUseInput} [payload] */
export async function useAdminCloudtentaclesSku(payload = /** @type {AdminCloudtentaclesSkuUseInput} */ ({})) {
const context = resolveAdminCloudtentaclesSessionPayload(payload)
export async function useAdminCloudtentaclesSku(
payload = /** @type {AdminCloudtentaclesSkuUseInput} */ ({})
) {
const context = resolveAdminCloudtentaclesSessionPayload(payload);
return useCloudtentaclesSku({
...context,
id: payload.id,
virtualNumberId: payload.virtualNumberId,
phone: payload.phone,
})
});
}
/** @param {AdminCloudtentaclesCatalogQueryInput} [payload] */
export async function getAdminCloudtentaclesKnapsack(payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({})) {
return getCloudtentaclesKnapsack(resolveAdminCloudtentaclesSessionPayload(payload))
export async function getAdminCloudtentaclesKnapsack(
payload = /** @type {AdminCloudtentaclesCatalogQueryInput} */ ({})
) {
return getCloudtentaclesKnapsack(
resolveAdminCloudtentaclesSessionPayload(payload)
);
}
/** @param {AdminCloudtentaclesVirtualNumberInput} [payload] */
export async function listAdminCloudtentaclesVirtualNumbers(payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})) {
const context = resolveAdminCloudtentaclesSessionPayload(payload)
export async function listAdminCloudtentaclesVirtualNumbers(
payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})
) {
const context = resolveAdminCloudtentaclesSessionPayload(payload);
return listCloudtentaclesVirtualNumbers({
...context,
key: payload.key,
})
});
}
/** @param {AdminCloudtentaclesVirtualNumberInput} [payload] */
export async function appointAdminCloudtentaclesVirtualNumber(payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})) {
const context = resolveAdminCloudtentaclesSessionPayload(payload)
export async function appointAdminCloudtentaclesVirtualNumber(
payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})
) {
const context = resolveAdminCloudtentaclesSessionPayload(payload);
return appointCloudtentaclesVirtualNumber({
...context,
key: payload.key,
})
});
}
/** @param {AdminCloudtentaclesVirtualNumberInput} [payload] */
export async function generateAdminCloudtentaclesLoginCode(payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})) {
const context = resolveAdminCloudtentaclesSessionPayload(payload)
export async function generateAdminCloudtentaclesLoginCode(
payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})
) {
const context = resolveAdminCloudtentaclesSessionPayload(payload);
return generateCloudtentaclesLoginCode({
...context,
key: payload.key,
id: payload.id,
})
});
}
/** @param {AdminCloudtentaclesVirtualNumberInput} [payload] */
export async function fetchAdminCloudtentaclesVirtualNumberCode(payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})) {
const context = resolveAdminCloudtentaclesSessionPayload(payload)
export async function fetchAdminCloudtentaclesVirtualNumberCode(
payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})
) {
const context = resolveAdminCloudtentaclesSessionPayload(payload);
return fetchCloudtentaclesVirtualNumberCode({
...context,
key: payload.key,
phone: payload.phone,
})
});
}
/** @param {AdminCloudtentaclesVirtualNumberInput} [payload] */
export async function verifyAdminCloudtentaclesLoginCode(payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})) {
const context = resolveAdminCloudtentaclesSessionPayload(payload)
export async function verifyAdminCloudtentaclesLoginCode(
payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})
) {
const context = resolveAdminCloudtentaclesSessionPayload(payload);
return verifyCloudtentaclesLoginCode({
...context,
key: payload.key,
id: payload.id,
code: payload.code,
})
});
}
/** @param {AdminCloudtentaclesVirtualNumberInput} [payload] */
export async function getAdminCloudtentaclesBindUrl(payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})) {
const context = resolveAdminCloudtentaclesSessionPayload(payload)
export async function getAdminCloudtentaclesBindUrl(
payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})
) {
const context = resolveAdminCloudtentaclesSessionPayload(payload);
return getCloudtentaclesBindUrl({
...context,
key: payload.key,
id: payload.id,
})
});
}
/** @param {AdminCloudtentaclesVirtualNumberInput} [payload] */
export async function backAdminCloudtentaclesVirtualNumber(payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})) {
const context = resolveAdminCloudtentaclesSessionPayload(payload)
export async function backAdminCloudtentaclesVirtualNumber(
payload = /** @type {AdminCloudtentaclesVirtualNumberInput} */ ({})
) {
const context = resolveAdminCloudtentaclesSessionPayload(payload);
return backCloudtentaclesVirtualNumber({
...context,
key: payload.key,
id: payload.id,
})
});
}
/** @param {AdminCloudtentaclesFullFlowInput} [payload] */
export async function runAdminCloudtentaclesFullFlow(payload = /** @type {AdminCloudtentaclesFullFlowInput} */ ({})) {
const context = resolveAdminCloudtentaclesSessionPayload(payload)
export async function runAdminCloudtentaclesFullFlow(
payload = /** @type {AdminCloudtentaclesFullFlowInput} */ ({})
) {
const context = resolveAdminCloudtentaclesSessionPayload(payload);
return runCloudtentaclesFullDebugFlow({
...context,
skuId: payload.skuId,
skuCount: payload.skuCount,
vnKey: payload.vnKey,
})
});
}
@@ -1,104 +1,139 @@
// @ts-check
import { getCloudtentaclesSourceConfig } from '../../platforms/cloudtentacles/source-config-service.js'
import { getCloudtentaclesSessionState } from '../../platforms/cloudtentacles/session-state-service.js'
import { pickFirstNonEmpty, resolveCloudtentaclesAdminContext } from './context.js'
import { maskPhone, maskSecret } from './mappers.js'
import { getCloudtentaclesSourceByKey } from "../../platforms/cloudtentacles/source-config-service.js";
import { getCloudtentaclesSessionStateByKey } from "../../platforms/cloudtentacles/session-state-service.js";
import {
pickFirstNonEmpty,
resolveCloudtentaclesAdminContext,
} from "./context.js";
import { maskPhone, maskSecret } from "./mappers.js";
const DEFAULT_CLOUDTENTACLES_BASE_URL = 'https://123.207.217.176'
const DEFAULT_CLOUDTENTACLES_BASE_URL = "https://123.207.217.176";
function _resolveSourceKey(payload = {}) {
return String(payload.sourceKey || "").trim() || "default";
}
export function resolveAdminCloudtentaclesCredentialPayload(
payload = {},
options = {},
options = {}
) {
const sourceKey = _resolveSourceKey(payload);
const savedSource = /** @type {Record<string, unknown>} */ (
options.savedSource || getCloudtentaclesSourceConfig()
)
const defaultBaseUrl = String(options.defaultBaseUrl || DEFAULT_CLOUDTENTACLES_BASE_URL).trim()
options.savedSource || getCloudtentaclesSourceByKey(sourceKey) || {}
);
const defaultBaseUrl = String(
options.defaultBaseUrl || DEFAULT_CLOUDTENTACLES_BASE_URL
).trim();
return {
baseUrl: pickFirstNonEmpty([payload.baseUrl, savedSource.baseUrl, defaultBaseUrl]),
sourceKey,
baseUrl: pickFirstNonEmpty([
payload.baseUrl,
savedSource.baseUrl,
defaultBaseUrl,
]),
username: pickFirstNonEmpty([payload.username, savedSource.username]),
password: pickFirstNonEmpty([payload.password, savedSource.password]),
phone: pickFirstNonEmpty([payload.phone, savedSource.phone]),
deviceId: pickFirstNonEmpty([payload.deviceId, savedSource.deviceId, '-']),
deviceId: pickFirstNonEmpty([payload.deviceId, savedSource.deviceId, "-"]),
deviceType: payload.deviceType ?? savedSource.deviceType,
}
};
}
export function resolveAdminCloudtentaclesSessionPayload(
payload = {},
options = {},
options = {}
) {
const sourceKey = _resolveSourceKey(payload);
const savedSource = /** @type {Record<string, unknown>} */ (
options.savedSource || getCloudtentaclesSourceConfig()
)
options.savedSource || getCloudtentaclesSourceByKey(sourceKey) || {}
);
const persistedSession = /** @type {Record<string, unknown>} */ (
options.persistedSession || getCloudtentaclesSessionState()
)
options.persistedSession ||
getCloudtentaclesSessionStateByKey(sourceKey) ||
{}
);
return resolveCloudtentaclesAdminContext(payload, {
savedSource,
persistedSession,
sourceKey,
defaultBaseUrl: options.defaultBaseUrl || DEFAULT_CLOUDTENTACLES_BASE_URL,
})
});
}
export function buildAdminCloudtentaclesPersistedSessionPayload(
session = {},
options = {},
options = {}
) {
return {
token: String(session.token || '').trim(),
baseUrl: String(session.baseUrl || '').trim(),
token: String(session.token || "").trim(),
baseUrl: String(session.baseUrl || "").trim(),
username: pickFirstNonEmpty([options.username, session.username]),
phone: pickFirstNonEmpty([options.phone, session.phone]),
loggedInAt: String(session.loggedInAt || '').trim(),
deviceId: pickFirstNonEmpty([options.deviceId, session.deviceId, '-']),
loggedInAt: String(session.loggedInAt || "").trim(),
deviceId: pickFirstNonEmpty([options.deviceId, session.deviceId, "-"]),
deviceType: options.deviceType ?? session.deviceType,
}
};
}
export function buildAdminCloudtentaclesSessionSummary(
session = {},
savedSession = {},
sourceKey = "default"
) {
const permissions = Array.isArray(session.permissions) ? session.permissions : []
const permissions = Array.isArray(session.permissions)
? session.permissions
: [];
return {
sourceKey,
tokenMasked: maskSecret(session.token),
permissionCount: permissions.length,
permissions,
persisted: Boolean(savedSession.token),
}
};
}
export function buildAdminCloudtentaclesLoginResult(
session = {},
savedSession = {},
sourceKey = "default"
) {
return {
baseUrl: String(session.baseUrl || '').trim(),
username: String(session.username || '').trim(),
sourceKey,
baseUrl: String(session.baseUrl || "").trim(),
username: String(session.username || "").trim(),
phoneMasked: maskPhone(session.phone),
loggedInAt: String(session.loggedInAt || '').trim(),
responseMessage: String(session.responseMessage || '').trim(),
token: String(session.token || '').trim(),
session: buildAdminCloudtentaclesSessionSummary(session, savedSession),
loggedInAt: String(session.loggedInAt || "").trim(),
responseMessage: String(session.responseMessage || "").trim(),
token: String(session.token || "").trim(),
session: buildAdminCloudtentaclesSessionSummary(
session,
savedSession,
sourceKey
),
userInfo: session.userInfo,
asset: session.asset,
}
};
}
export function buildAdminCloudtentaclesValidateResult(
session = {},
savedSession = {},
sourceKey = "default"
) {
return {
baseUrl: String(session.baseUrl || '').trim(),
loggedInAt: String(session.loggedInAt || '').trim(),
session: buildAdminCloudtentaclesSessionSummary(session, savedSession),
sourceKey,
baseUrl: String(session.baseUrl || "").trim(),
loggedInAt: String(session.loggedInAt || "").trim(),
session: buildAdminCloudtentaclesSessionSummary(
session,
savedSession,
sourceKey
),
userInfo: session.userInfo,
asset: session.asset,
}
};
}
@@ -2,67 +2,104 @@
export function pickFirstNonEmpty(values) {
for (const value of values) {
const normalized = String(value || '').trim()
const normalized = String(value || "").trim();
if (normalized) {
return normalized
return normalized;
}
}
return ''
return "";
}
export function isPlainObject(value) {
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
export function resolveKuaishouEticketAdminContext(
payload = {},
options = {},
) {
const savedSource = /** @type {Record<string, unknown>} */ (options.savedSource || {})
const findShopConfig = /** @type {(shopId: string, source: Record<string, unknown>) => unknown} */ (
export function resolveKuaishouEticketAdminContext(payload = {}, options = {}) {
const savedSource = /** @type {Record<string, unknown>} */ (
options.savedSource || {}
);
const findShopConfig =
/** @type {(shopId: string, source: Record<string, unknown>) => unknown} */ (
options.findShopConfig || (() => null)
)
const getFirstAvailableShop = /** @type {(source: Record<string, unknown>) => unknown} */ (
);
const getFirstAvailableShop =
/** @type {(source: Record<string, unknown>) => unknown} */ (
options.getFirstAvailableShop || (() => null)
)
const defaultBaseUrl = String(options.defaultBaseUrl || 'https://s.kwaixiaodian.com').trim()
const requestedShopId = String(payload.shopId || '').trim()
);
const defaultBaseUrl = String(
options.defaultBaseUrl || "https://s.kwaixiaodian.com"
).trim();
const requestedShopId = String(payload.shopId || "").trim();
const configuredShop = /** @type {Record<string, unknown> | null} */ (
requestedShopId
? findShopConfig(requestedShopId, savedSource)
: getFirstAvailableShop(savedSource)
)
);
return {
baseUrl: pickFirstNonEmpty([payload.baseUrl, savedSource.baseUrl, defaultBaseUrl]),
baseUrl: pickFirstNonEmpty([
payload.baseUrl,
savedSource.baseUrl,
defaultBaseUrl,
]),
cookie: pickFirstNonEmpty([payload.cookie, configuredShop?.cookie]),
shopId: pickFirstNonEmpty([requestedShopId, configuredShop?.shopId]),
shop: configuredShop,
}
};
}
export function resolveCloudtentaclesAdminContext(
payload = {},
options = {},
) {
const savedSource = /** @type {Record<string, unknown>} */ (options.savedSource || {})
const persistedSession = /** @type {Record<string, unknown>} */ (options.persistedSession || {})
const defaultBaseUrl = String(options.defaultBaseUrl || 'https://123.207.217.176').trim()
import { getCloudtentaclesSourceByKey } from "../../platforms/cloudtentacles/source-config-service.js";
import { getCloudtentaclesSessionStateByKey } from "../../platforms/cloudtentacles/session-state-service.js";
export function resolveCloudtentaclesAdminContext(payload = {}, options = {}) {
const sourceKey = String(
payload.sourceKey || options.sourceKey || "default"
).trim();
const savedSource = /** @type {Record<string, unknown>} */ (
options.savedSource || getCloudtentaclesSourceByKey(sourceKey) || {}
);
const persistedSession = /** @type {Record<string, unknown>} */ (
options.persistedSession ||
getCloudtentaclesSessionStateByKey(sourceKey) ||
{}
);
const defaultBaseUrl = String(
options.defaultBaseUrl || "https://123.207.217.176"
).trim();
return {
baseUrl: pickFirstNonEmpty([payload.baseUrl, savedSource.baseUrl, defaultBaseUrl]),
sourceKey,
baseUrl: pickFirstNonEmpty([
payload.baseUrl,
savedSource.baseUrl,
defaultBaseUrl,
]),
token: pickFirstNonEmpty([payload.token, persistedSession.token]),
deviceId: pickFirstNonEmpty([payload.deviceId, savedSource.deviceId, persistedSession.deviceId, '-']),
deviceType: payload.deviceType ?? savedSource.deviceType ?? persistedSession.deviceType,
}
deviceId: pickFirstNonEmpty([
payload.deviceId,
savedSource.deviceId,
persistedSession.deviceId,
"-",
]),
deviceType:
payload.deviceType ??
savedSource.deviceType ??
persistedSession.deviceType,
};
}
export function hasCloudtentaclesCredentialContextChanged(current = {}, next = {}) {
export function hasCloudtentaclesCredentialContextChanged(
current = {},
next = {}
) {
return (
String(current.baseUrl || '').trim() !== String(next.baseUrl || '').trim()
|| String(current.username || '').trim() !== String(next.username || '').trim()
|| String(current.phone || '').trim() !== String(next.phone || '').trim()
|| String(current.deviceId || '').trim() !== String(next.deviceId || '').trim()
|| Number(current.deviceType || 0) !== Number(next.deviceType || 0)
)
String(current.baseUrl || "").trim() !==
String(next.baseUrl || "").trim() ||
String(current.username || "").trim() !==
String(next.username || "").trim() ||
String(current.phone || "").trim() !== String(next.phone || "").trim() ||
String(current.deviceId || "").trim() !==
String(next.deviceId || "").trim() ||
Number(current.deviceType || 0) !== Number(next.deviceType || 0)
);
}
@@ -1,110 +1,113 @@
// @ts-check
export function maskSecret(value) {
const normalized = String(value || '').trim()
const normalized = String(value || "").trim();
if (!normalized) {
return ''
return "";
}
if (normalized.length <= 10) {
return `${normalized.slice(0, 2)}****${normalized.slice(-2)}`
return `${normalized.slice(0, 2)}****${normalized.slice(-2)}`;
}
return `${normalized.slice(0, 6)}****${normalized.slice(-6)}`
return `${normalized.slice(0, 6)}****${normalized.slice(-6)}`;
}
export function maskPhone(value) {
const normalized = String(value || '').trim()
const normalized = String(value || "").trim();
if (!normalized) {
return ''
return "";
}
if (normalized.length < 7) {
return normalized
return normalized;
}
return `${normalized.slice(0, 3)}****${normalized.slice(-4)}`
return `${normalized.slice(0, 3)}****${normalized.slice(-4)}`;
}
export function mapAdminKuaishouEticketShopItem(item = {}) {
return {
shopId: String(item.shopId || '').trim(),
kshopName: String(item.kshopName || '').trim(),
cookie: String(item.cookie || '').trim(),
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(),
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(),
baseUrl: String(config.baseUrl || "").trim(),
shops: shops.map((item) => mapAdminKuaishouEticketShopItem(item)),
}
};
}
export function mapAdminCloudtentaclesSourceConfig(config = {}) {
return {
key: String(config.key || "").trim(),
label: String(config.label || "").trim(),
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() || '-',
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(),
token: String(session.token || "").trim(),
tokenMasked: maskSecret(session.token),
baseUrl: String(session.baseUrl || '').trim(),
username: String(session.username || '').trim(),
baseUrl: String(session.baseUrl || "").trim(),
username: String(session.username || "").trim(),
phoneMasked: maskPhone(session.phone),
loggedInAt: String(session.loggedInAt || '').trim(),
deviceId: String(session.deviceId || '-').trim() || '-',
loggedInAt: String(session.loggedInAt || "").trim(),
deviceId: String(session.deviceId || "-").trim() || "-",
deviceType: Number(session.deviceType || 0),
hasToken: Boolean(String(session.token || '').trim()),
}
hasToken: Boolean(String(session.token || "").trim()),
};
}
export function mapAdminKuaishouCloudFulfillmentItem(item = {}) {
return {
id: String(item.id || '').trim(),
id: String(item.id || "").trim(),
enabled: item.enabled !== false,
priority: Number(item.priority || 100) || 100,
provider: String(item.provider || '91kaquan').trim() || '91kaquan',
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',
provider: String(item.provider || "91kaquan").trim() || "91kaquan",
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(),
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(),
}
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),
mapAdminKuaishouCloudFulfillmentItem(item)
),
}
};
}
@@ -1,13 +1,13 @@
export {
getAdminAgisoShopConfigs,
updateAdminAgisoShopConfigs,
} from './agiso-service.js'
} from "./agiso-service.js";
export {
getAdminNinetyoneOrders,
retryAdminNinetyoneOrder,
failAdminNinetyoneOrder,
} from './ninetyone-service.js'
} from "./ninetyone-service.js";
export {
getAdminKuaishouEticketSourceConfig,
@@ -15,10 +15,11 @@ export {
queryAdminKuaishouEticketDetail,
queryAdminKuaishouEticketShopInfo,
consumeAdminKuaishouEticket,
} from './kuaishou-eticket-service.js'
} from "./kuaishou-eticket-service.js";
export {
getAdminCloudtentaclesSourceConfig,
listAdminCloudtentaclesSources,
deleteAdminCloudtentaclesSource,
updateAdminCloudtentaclesSourceConfig,
sendAdminCloudtentaclesSmsCode,
testAdminCloudtentaclesLogin,
@@ -37,18 +38,18 @@ export {
getAdminCloudtentaclesBindUrl,
backAdminCloudtentaclesVirtualNumber,
runAdminCloudtentaclesFullFlow,
} from './cloudtentacles-service.js'
} from "./cloudtentacles-service.js";
export {
getAdminFulfillmentBindingConfigs,
lookupAdminFulfillmentBindingOrder,
updateAdminFulfillmentBindingConfigs,
} from './fulfillment-bindings-service.js'
} from "./fulfillment-bindings-service.js";
export {
getAdminKuaishouCloudFulfillmentConfig,
updateAdminKuaishouCloudFulfillmentConfig,
} from './kuaishou-cloud-fulfillment-service.js'
} from "./kuaishou-cloud-fulfillment-service.js";
export {
getAdminNotificationConfig,
@@ -57,4 +58,4 @@ export {
getAdminScheduledJobsConfig,
updateAdminScheduledJobsConfig,
runAdminScheduledJobNow,
} from './notification-service.js'
} from "./notification-service.js";
@@ -1,101 +1,120 @@
// @ts-check
import { applyOptionalStringField } from './domain.js'
import { applyOptionalStringField } from "./domain.js";
export function buildAdminAgisoShopConfigUpdate(item, current = {}) {
const shopId = String(item?.shopId || '').trim()
const shopId = String(item?.shopId || "").trim();
if (!shopId) {
return null
return null;
}
const next = /** @type {Record<string, unknown>} */ ({ ...current })
const shopName = String(item?.shopName || '').trim()
const accessToken = String(item?.accessToken || '').trim()
const messageTemplate = String(item?.messageTemplate || '').trim()
const autoDeliveryMessageTemplate = String(item?.autoDeliveryMessageTemplate || '').trim()
const appSecret = String(item?.appSecret || '').trim()
const apiVersion = String(item?.apiVersion || '').trim()
const sendMessageEndpoint = String(item?.sendMessageEndpoint || '').trim()
const next = /** @type {Record<string, unknown>} */ ({ ...current });
const shopName = String(item?.shopName || "").trim();
const accessToken = String(item?.accessToken || "").trim();
const messageTemplate = String(item?.messageTemplate || "").trim();
const autoDeliveryMessageTemplate = String(
item?.autoDeliveryMessageTemplate || ""
).trim();
const appSecret = String(item?.appSecret || "").trim();
const apiVersion = String(item?.apiVersion || "").trim();
const sendMessageEndpoint = String(item?.sendMessageEndpoint || "").trim();
if (shopName) {
next.shopName = shopName
next.shopName = shopName;
} else {
delete next.shopName
delete next.shopName;
}
if (accessToken) {
next.accessToken = accessToken
next.accessToken = accessToken;
}
if (messageTemplate) {
next.messageTemplate = messageTemplate
} else if (typeof item?.messageTemplate === 'string') {
delete next.messageTemplate
next.messageTemplate = messageTemplate;
} else if (typeof item?.messageTemplate === "string") {
delete next.messageTemplate;
}
if (autoDeliveryMessageTemplate) {
next.autoDeliveryMessageTemplate = autoDeliveryMessageTemplate
} else if (typeof item?.autoDeliveryMessageTemplate === 'string') {
delete next.autoDeliveryMessageTemplate
next.autoDeliveryMessageTemplate = autoDeliveryMessageTemplate;
} else if (typeof item?.autoDeliveryMessageTemplate === "string") {
delete next.autoDeliveryMessageTemplate;
}
if (appSecret) {
next.appSecret = appSecret
next.appSecret = appSecret;
}
if (apiVersion) {
next.apiVersion = apiVersion
next.apiVersion = apiVersion;
}
if (sendMessageEndpoint) {
next.sendMessageEndpoint = sendMessageEndpoint
next.sendMessageEndpoint = sendMessageEndpoint;
}
if (typeof item?.enabled === 'boolean') {
next.enabled = item.enabled
if (typeof item?.enabled === "boolean") {
next.enabled = item.enabled;
}
if (!next.accessToken) {
return null
return null;
}
return { shopId, config: next }
return { shopId, config: next };
}
export function buildAdminAgisoMessagingSavePayload(
payload = {},
options = {},
options = {}
) {
const rawItems = Array.isArray(payload.shops) ? payload.shops : []
const currentDefaults = /** @type {Record<string, unknown>} */ (options.currentDefaults || {})
const currentMap = /** @type {Record<string, Record<string, unknown>>} */ (options.currentMap || {})
const nextDefaults = /** @type {Record<string, unknown>} */ ({ ...currentDefaults })
const nextMap = /** @type {Record<string, Record<string, unknown>>} */ ({})
const rawItems = Array.isArray(payload.shops) ? payload.shops : [];
const currentDefaults = /** @type {Record<string, unknown>} */ (
options.currentDefaults || {}
);
const currentMap = /** @type {Record<string, Record<string, unknown>>} */ (
options.currentMap || {}
);
const nextDefaults = /** @type {Record<string, unknown>} */ ({
...currentDefaults,
});
const nextMap = /** @type {Record<string, Record<string, unknown>>} */ ({});
applyOptionalStringField(nextDefaults, 'messageTemplate', payload.defaults)
applyOptionalStringField(nextDefaults, 'autoDeliveryMessageTemplate', payload.defaults)
applyOptionalStringField(nextDefaults, "messageTemplate", payload.defaults);
applyOptionalStringField(
nextDefaults,
"autoDeliveryMessageTemplate",
payload.defaults
);
for (const item of rawItems) {
const shopId = String(item?.shopId || '').trim()
const shopId = String(item?.shopId || "").trim();
if (!shopId) {
continue
continue;
}
const updated = buildAdminAgisoShopConfigUpdate(item, currentMap[shopId] || {})
const updated = buildAdminAgisoShopConfigUpdate(
item,
currentMap[shopId] || {}
);
if (!updated) {
continue
continue;
}
nextMap[updated.shopId] = updated.config
nextMap[updated.shopId] = updated.config;
}
return {
defaults: nextDefaults,
shops: nextMap,
}
};
}
export function normalizeAdminCloudtentaclesSourceConfigPayload(payload = {}) {
const sourceKey =
String(payload.sourceKey || payload.key || "").trim() || "default";
return {
key: sourceKey,
label: String(payload.label || "").trim(),
enabled: payload.enabled !== false,
baseUrl: String(payload.baseUrl || '').trim() || 'https://123.207.217.176',
username: String(payload.username || '').trim(),
password: String(payload.password || '').trim(),
phone: String(payload.phone || '').trim(),
deviceId: String(payload.deviceId || '-').trim() || '-',
baseUrl: String(payload.baseUrl || "").trim() || "https://123.207.217.176",
username: String(payload.username || "").trim(),
password: String(payload.password || "").trim(),
phone: String(payload.phone || "").trim(),
deviceId: String(payload.deviceId || "-").trim() || "-",
deviceType: Number(payload.deviceType || 0),
}
};
}
+5 -1
View File
@@ -1,6 +1,6 @@
// @ts-check
export {}
export {};
/**
* @typedef {import('./admin-read-inputs.js').AdminInventoryListQueryInput} AdminInventoryRouteQuery
@@ -161,3 +161,7 @@ export {}
/**
* @typedef {{ id?: string }} AdminEntityRouteParams
*/
/**
* @typedef {{ sourceKey?: string }} AdminCloudtentaclesSourceRouteParams
*/
+11 -1
View File
@@ -1,6 +1,6 @@
// @ts-check
export {}
export {};
/**
* @typedef {{
@@ -202,6 +202,7 @@ export {}
/**
* @typedef {{
* sourceKey?: string
* enabled?: boolean
* baseUrl?: string
* username?: string
@@ -209,11 +210,13 @@ export {}
* phone?: string
* deviceId?: string
* deviceType?: number | string
* sources?: AdminCloudtentaclesSourceConfigInput[]
* }} AdminCloudtentaclesSourceConfigInput
*/
/**
* @typedef {{
* sourceKey?: string
* baseUrl?: string
* username?: string
* phone?: string
@@ -224,6 +227,7 @@ export {}
/**
* @typedef {{
* sourceKey?: string
* baseUrl?: string
* username?: string
* password?: string
@@ -236,6 +240,7 @@ export {}
/**
* @typedef {{
* sourceKey?: string
* baseUrl?: string
* token?: string
* deviceId?: string
@@ -245,6 +250,7 @@ export {}
/**
* @typedef {{
* sourceKey?: string
* baseUrl?: string
* token?: string
* deviceId?: string
@@ -254,6 +260,7 @@ export {}
/**
* @typedef {{
* sourceKey?: string
* baseUrl?: string
* token?: string
* deviceId?: string
@@ -265,6 +272,7 @@ export {}
/**
* @typedef {{
* sourceKey?: string
* baseUrl?: string
* token?: string
* deviceId?: string
@@ -277,6 +285,7 @@ export {}
/**
* @typedef {{
* sourceKey?: string
* baseUrl?: string
* token?: string
* deviceId?: string
@@ -290,6 +299,7 @@ export {}
/**
* @typedef {{
* sourceKey?: string
* baseUrl?: string
* token?: string
* deviceId?: string