oak-general-business/lib/context/BackendRuntimeContext.js

465 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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 = []; // 这里需要并行下面的await检查略过
if (appId) {
// @oak-ignore
promises.push(this.setApplication(appId));
}
if (tokenValue || userId) {
// @oak-ignore
promises.push(this.setTokenValue(tokenValue, userId));
// @oak-ignore
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) {
// @oak-ignore 这里直接return依赖上层await先跳过检查
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;