"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BackendRuntimeContext = void 0; const assert_1 = require("oak-domain/lib/utils/assert"); const Exception_1 = require("../types/Exception"); const Exception_2 = require("oak-domain/lib/types/Exception"); const constants_1 = require("../constants"); const uuid_1 = require("oak-domain/lib/utils/uuid"); const types_1 = require("oak-domain/lib/types"); const Projection_1 = require("../types/Projection"); const wechatQrCode_1 = require("../aspects/wechatQrCode"); const BackendRuntimeContext_1 = require("oak-frontend-base/lib/context/BackendRuntimeContext"); const lodash_1 = require("oak-domain/lib/utils/lodash"); const domain_1 = require("../utils/domain"); const user_1 = require("../utils/user"); const version_1 = require("oak-domain/lib/utils/version"); const validator_1 = require("oak-domain/lib/utils/validator"); /** * general数据结构要求的后台上下文 */ class BackendRuntimeContext extends BackendRuntimeContext_1.BackendRuntimeContext { application; token; amIRoot; amIReallyRoot; rootMode; userId; platformManager; appVersion; applicationProjection = (0, lodash_1.cloneDeep)(Projection_1.applicationProjection); async refineOpRecords() { const isRoot = this.isRoot(); const isPlatformManager = this.platformManager; for (const opRecord of this.opRecords) { if (opRecord.a === 's') { const { d } = opRecord; for (const entity in d) { if (entity === 'wechatQrCode') { // todo 小程序码此时去微信服务器获得码数据 const wechatQrCodeListObj = d[entity]; for (const id in wechatQrCodeListObj) { const wechatQrCodeData = wechatQrCodeListObj[id]; if (wechatQrCodeData.hasOwnProperty('buffer') && wechatQrCodeData.type === 'wechatMpWxaCode') { const buffer = await (0, wechatQrCode_1.getMpUnlimitWxaCode)(id, this); Object.assign(wechatQrCodeData, { buffer, }); } } } else if (entity === 'application') { if (!(isRoot || isPlatformManager)) { const rowDict = d[entity]; for (const id in rowDict) { const { config } = rowDict[id]; if (config) { // application中可能的保密信息 (0, lodash_1.unset)(config, 'appSecret'); (0, lodash_1.unset)(config, 'server.token'); (0, lodash_1.unset)(config, 'server.mode'); (0, lodash_1.unset)(config, 'server.encodingAESKey'); (0, lodash_1.unset)(config, 'wechat.appSecret'); (0, lodash_1.unset)(config, 'wechatNative.appSecret'); } } } } else if (['system', 'platform'].includes(entity)) { if (!(isRoot || isPlatformManager)) { const rowDict = d[entity]; for (const id in rowDict) { const { config } = rowDict[id]; if (config) { // server/platform中可能的保密信息 if (config.Account) { for (const k in config.Account) { if (config.Account[k] instanceof Array) { config.Account[k]?.forEach((ele) => { (0, lodash_1.unset)(ele, 'securityKey'); (0, lodash_1.unset)(ele, 'secretKey'); (0, lodash_1.unset)(ele, 'webApiKey'); (0, lodash_1.unset)(ele, 'accessKeySecret'); }); } } } if (config.Sms) { for (const k in config.Sms) { if (config.Sms[k] instanceof Array) { config.Sms[k]?.forEach((ele) => { (0, lodash_1.unset)(ele, 'securityKey'); (0, lodash_1.unset)(ele, 'secretKey'); (0, lodash_1.unset)(ele, 'webApiKey'); (0, lodash_1.unset)(ele, 'accessKeySecret'); }); } } } if (config.Emails) { config.Emails?.forEach((ele) => { (0, lodash_1.unset)(ele, 'password'); }); } } } } } else if (entity === 'user') { for (const id in d[entity]) { const userData = d[entity][id]; const { password, verifyPasswordAt } = userData; if (password && verifyPasswordAt !== null) { userData.password = (0, user_1.maskPassword)(password); } } } } } } } async setPlatformManager(tokenValue, userId) { if (tokenValue) { // 前台传递 const result = await this.select('platform', { data: { id: 1, }, filter: { userRelation$entity: { user: { token$user: { $or: [ { value: tokenValue, }, { oldValue: tokenValue, }, ], } }, relationId: { $in: ["platform-owner" /* RelationId.Platform.Owner */, "platform-manager" /* RelationId.Platform.Manager */] } }, }, }, { dontCollect: true, blockTrigger: true, }); if (result.length > 0) { this.platformManager = true; } else { this.platformManager = false; } return; } if (userId) { // 若是后台环境,用userId来查询处理 const result = await this.select('platform', { data: { id: 1, }, filter: { userRelation$entity: { userId, relationId: { $in: ["platform-owner" /* RelationId.Platform.Owner */, "platform-manager" /* RelationId.Platform.Manager */] } }, }, }, { dontCollect: true }); if (result.length > 0) { this.platformManager = true; } else { this.platformManager = false; } } } async setTokenValue(tokenValue, userId) { if (tokenValue) { // 前台传递 const result = await this.select('token', { data: { id: 1, ableState: 1, user: { id: 1, userState: 1, isRoot: 1, }, applicationId: 1, userId: 1, value: 1, player: { id: 1, isRoot: 1, }, playerId: 1, }, filter: { $or: [ { value: tokenValue, }, { oldValue: tokenValue, // refreshedAt: { // $gte: Date.now() - 300 * 1000, // }, }, ], }, }, { dontCollect: true, blockTrigger: true, }); if (result.length === 0) { console.log(`构建BackendRuntimeContext对应tokenValue「${tokenValue}找不到相关的user`); throw new Exception_1.OakTokenExpiredException(); } const token = result[0]; if (token.ableState === 'disabled') { console.log(`构建BackendRuntimeContext对应tokenValue「${tokenValue}已经被disable`); throw new Exception_1.OakTokenExpiredException(); } const { user, player } = token; this.amIRoot = user?.isRoot; this.amIReallyRoot = player?.isRoot; this.token = token; this.userId = token.userId; return; } if (userId) { // 若是后台环境,用userId来查询处理 const [user] = await this.select('user', { data: { id: 1, isRoot: 1, }, filter: { id: userId }, }, { dontCollect: true }); (0, assert_1.assert)(user, '初始化context时有userId但查询不到user'); this.amIRoot = user.isRoot; this.amIReallyRoot = user.isRoot; this.userId = userId; } } async setApplication(appId) { const result = await this.select('application', { data: this.applicationProjection, filter: { id: appId, }, }, { dontCollect: true, blockTrigger: true, }); (0, assert_1.assert)(result.length > 0, `构建BackendRuntimeContext对应appId「${appId}」找不到application`); this.application = result[0]; } async initialize(data, later) { await super.initialize(data); if (data) { const closeRootMode = this.openRootMode(); try { const { a: appId, t: tokenValue, rm, userId, v } = data; this.appVersion = v; const promises = []; if (appId) { promises.push(this.setApplication(appId)); } if (tokenValue || userId) { promises.push(this.setTokenValue(tokenValue, userId)); promises.push(this.setPlatformManager(tokenValue, userId)); } if (promises.length > 0) { await Promise.all(promises); } if (!rm) { closeRootMode(); } } catch (err) { closeRootMode(); throw err; } } else { // 否则是后台模式,默认用root this.rootMode = true; } } getApplicationId(allowNull) { if (!allowNull) { if (!this.application) { throw new Exception_1.OakApplicationLoadingException(); } return this.application.id; } return this.application?.id; } getSystemId(allowNull) { if (!allowNull) { if (!this.application) { throw new Exception_1.OakApplicationLoadingException(); } return this.application.systemId; } return this.application?.systemId; } getApplication(allowNull) { if (!allowNull) { if (!this.application) { throw new Exception_1.OakApplicationLoadingException(); } return this.application; } return this.application; } openRootMode() { if (this.rootMode) { return () => undefined; } this.rootMode = true; return () => (this.rootMode = false); } getTokenValue(allowUnloggedIn) { if (this.rootMode) { return constants_1.ROOT_TOKEN_ID; } if (!this.token && !allowUnloggedIn) { throw new Exception_2.OakUnloggedInException(); } return this.token?.value; } getToken(allowUnloggedIn) { if (!this.token && !allowUnloggedIn) { throw new Exception_2.OakUnloggedInException(); } if (this.token) { const { userState } = this.token.user; if (['disabled', 'merged'].includes(userState) && !this.isReallyRoot()) { throw new Exception_1.OakUserDisabledException(); } } return this.token; } getCurrentUserId(allowUnloggedIn) { if (this.userId) { return this.userId; } const token = this.getToken(allowUnloggedIn); return token?.userId; } setCurrentUserId(userId) { (0, assert_1.assert)(this.isReallyRoot); this.userId = userId; } async getSerializedData() { const data = await super.getSerializedData(); // 后台物化上下文直接存userId; return { ...data, a: this.application?.id, rm: this.rootMode, userId: this.getCurrentUserId(true), v: this.appVersion, }; } isRoot() { if (this.rootMode) { return true; } return !!this.amIRoot; } isReallyRoot() { return !!this.amIReallyRoot; } async sendMessage(data) { return this.operate('message', { id: await (0, uuid_1.generateNewIdAsync)(), action: 'create', data, }, { dontCollect: true, }); } allowUserUpdate() { if (this.isReallyRoot()) { return true; } const userInfo = this.token?.user; if (userInfo) { const { userState } = userInfo; if (userState === 'disabled') { throw new Exception_1.OakUserDisabledException('您的帐号已经被禁用,请联系客服'); } else if (['merged'].includes(userState)) { throw new Exception_1.OakTokenExpiredException('您的登录状态有异常,请重新登录 '); } else { (0, assert_1.assert)(userState === 'normal' || userState === 'shadow'); } return true; } throw new Exception_2.OakUnloggedInException('您尚未登录'); } sortDomains(domains) { return domains.sort((a, b) => { const getPriority = (domain) => { if (typeof domain.url !== 'string') return 3; const lowerItem = domain.url.toLowerCase(); // 1. 检查是否是localhost if (lowerItem === 'localhost') return 2; // 2. 检查是否是IPv4或IPv6 if ((0, validator_1.isIP)(domain.url)) return 1; // 3. 其他情况认为是域名 return 0; }; return getPriority(a) - getPriority(b); }); } /** * 获得当前系统的访问路径,根据application/system/domain之间的关联 * http://www.xxx.com/oak-api */ composeAccessPath() { const application = this.getApplication(); const { system, domainId, domain } = application; if (domainId) { (0, assert_1.assert)(domain); return (0, domain_1.composeServerUrl)(domain); } const { domain$system: domains } = system; (0, assert_1.assert)(domains && domains.length > 0); const findDomain = this.sortDomains(domains)?.[0]; return (0, domain_1.composeServerUrl)(findDomain); } async tryDeduceException(err) { if (this.application && this.appVersion) { const { soaVersion } = this.application; if (soaVersion && (0, version_1.compareVersion)(this.appVersion, soaVersion) < 0) { // 说明客户端可以升级 return new types_1.OakApplicationHasToUpgrade(); } } } ; } exports.BackendRuntimeContext = BackendRuntimeContext; ; exports.default = BackendRuntimeContext;