Merge branch 'dev' of gitea.51mars.com:Oak-Team/oak-general-business into dev

This commit is contained in:
Xu Chang 2025-07-09 10:21:53 +08:00
commit 341027ca82
28 changed files with 471 additions and 666 deletions

View File

@ -11,6 +11,7 @@ import { mergeUser } from './user';
import { cloneDeep } from 'oak-domain/lib/utils/lodash';
import { sendEmail } from '../utils/email';
import { isEmail, isMobile } from 'oak-domain/lib/utils/validator';
import { getAndCheckPassportByEmail } from '../utils/passport';
async function makeDistinguishException(userId, context, message) {
const [user] = await context.select('user', {
data: {
@ -468,6 +469,16 @@ export async function loginByMobile(params, context) {
if (captchaRow.expired) {
throw new OakUserException('验证码已经过期');
}
await context.operate('captcha', {
id: await generateNewIdAsync(),
action: 'update',
data: {
expired: true
},
filter: {
id: captchaRow.id
}
}, {});
// 到这里说明验证码已经通过
return await setupMobile(mobile, env, context);
}
@ -560,49 +571,7 @@ export async function loginByAccount(params, context) {
assert(account);
const accountType = isEmail(account) ? 'email' : (isMobile(account) ? 'mobile' : 'loginName');
if (accountType === 'email') {
// const application = context.getApplication();
// const { system } = application!;
// const [applicationPassport] = await context.select('applicationPassport',
// {
// data: {
// id: 1,
// passportId: 1,
// passport: {
// id: 1,
// config: 1,
// type: 1,
// },
// applicationId: 1,
// },
// filter: {
// applicationId: application?.id!,
// passport: {
// type: 'email'
// },
// }
// },
// {
// dontCollect: true,
// }
// );
// assert(applicationPassport?.passport);
// const config = applicationPassport.passport.config as EmailConfig;
// const emailConfig = system?.config.Emails?.find((ele) => ele.account === config.account);
// assert(emailConfig);
// const emailSuffixes = config.emailSuffixes;
// // 检查邮箱后缀是否满足配置
// if (emailSuffixes?.length! > 0) {
// let isValid = false;
// for (const suffix of emailSuffixes!) {
// if (account.endsWith(suffix)) {
// isValid = true;
// break;
// }
// }
// if (!isValid) {
// throw new OakUserException('error::user.emailSuffixIsInvalid');
// }
// }
const { config, emailConfig } = await getAndCheckPassportByEmail(context, account);
const existEmail = await context.select('email', {
data: {
id: 1,
@ -980,46 +949,7 @@ export async function loginByEmail(params, context) {
}
};
const closeRootMode = context.openRootMode();
const application = context.getApplication();
const { system } = application;
const [applicationPassport] = await context.select('applicationPassport', {
data: {
id: 1,
passportId: 1,
passport: {
id: 1,
config: 1,
type: 1,
},
applicationId: 1,
},
filter: {
applicationId: application?.id,
passport: {
type: 'email'
},
}
}, {
dontCollect: true,
});
assert(applicationPassport?.passport);
const config = applicationPassport.passport.config;
const emailConfig = system?.config.Emails?.find((ele) => ele.account === config.account);
assert(emailConfig);
const emailSuffixes = config.emailSuffixes;
// 检查邮箱后缀是否满足配置
if (emailSuffixes?.length > 0) {
let isValid = false;
for (const suffix of emailSuffixes) {
if (email.endsWith(suffix)) {
isValid = true;
break;
}
}
if (!isValid) {
throw new OakUserException('邮箱后缀不符合要求');
}
}
const { config, emailConfig } = await getAndCheckPassportByEmail(context, email);
if (disableRegister) {
const [existEmail] = await context.select('email', {
data: {
@ -1073,6 +1003,16 @@ export async function bindByMobile(params, context) {
throw new OakUserException('验证码已经过期');
}
// 到这里说明验证码已经通过
await context.operate('captcha', {
id: await generateNewIdAsync(),
action: 'update',
data: {
expired: true
},
filter: {
id: captchaRow.id
}
}, {});
// 检查当前user是否已绑定mobile
const [boundMobile] = await context.select('mobile', {
data: {
@ -1233,46 +1173,7 @@ export async function bindByEmail(params, context) {
}
};
const closeRootMode = context.openRootMode();
const application = context.getApplication();
const { system } = application;
const [applicationPassport] = await context.select('applicationPassport', {
data: {
id: 1,
passportId: 1,
passport: {
id: 1,
config: 1,
type: 1,
},
applicationId: 1,
},
filter: {
applicationId: application?.id,
passport: {
type: 'email'
},
}
}, {
dontCollect: true,
});
assert(applicationPassport?.passport);
const config = applicationPassport.passport.config;
const emailConfig = system?.config.Emails?.find((ele) => ele.account === config.account);
assert(emailConfig);
const emailSuffixes = config.emailSuffixes;
// 检查邮箱后缀是否满足配置
if (emailSuffixes?.length > 0) {
let isValid = false;
for (const suffix of emailSuffixes) {
if (email.endsWith(suffix)) {
isValid = true;
break;
}
}
if (!isValid) {
throw new OakUserException('邮箱后缀不符合要求');
}
}
const { config, emailConfig } = await getAndCheckPassportByEmail(context, email);
const [otherUserEmail] = await context.select('email', {
data: {
id: 1,
@ -2054,6 +1955,7 @@ export async function sendCaptchaByMobile({ mobile, env, type: captchaType, }, c
const [count1, count2] = await Promise.all([
context.count('captcha', {
filter: {
origin: 'mobile',
visitorId,
$$createAt$$: {
$gt: now - 3600 * 1000,
@ -2105,10 +2007,11 @@ export async function sendCaptchaByMobile({ mobile, env, type: captchaType, }, c
code = mobile.substring(11 - digit);
}
else {
code = Math.floor(Math.random() * Math.pow(10, digit)).toString();
while (code.length < digit) {
code += '0';
}
// code = Math.floor(Math.random() * Math.pow(10, digit)).toString();
// while (code.length < digit) {
// code += '0';
// }
code = Array.from({ length: digit }, () => Math.floor(Math.random() * 10)).join('');
}
const id = await generateNewIdAsync();
await context.operate('captcha', {
@ -2137,6 +2040,17 @@ export async function sendCaptchaByMobile({ mobile, env, type: captchaType, }, c
closeRootMode();
throw new OakUserException('您的操作太迅捷啦,请稍候再点吧');
}
await context.operate('captcha', {
id: await generateNewIdAsync(),
action: 'update',
data: {
expired: true
},
filter: {
id: captcha.id
}
}, {});
code = await getCode();
}
else {
code = await getCode();
@ -2168,49 +2082,10 @@ export async function sendCaptchaByEmail({ email, env, type: captchaType, }, con
if (type === 'web' || type === 'native') {
visitorId = env.visitorId;
}
const application = context.getApplication();
const { system } = application;
const [applicationPassport] = await context.select('applicationPassport', {
data: {
id: 1,
passportId: 1,
passport: {
id: 1,
config: 1,
type: 1,
},
applicationId: 1,
},
filter: {
applicationId: application?.id,
passport: {
type: 'email'
},
}
}, {
dontCollect: true,
});
assert(applicationPassport?.passport);
const config = applicationPassport.passport.config;
const emailConfig = system?.config.Emails?.find((ele) => ele.account === config.account);
assert(emailConfig);
const { config, emailConfig } = await getAndCheckPassportByEmail(context, email);
const duration = config.codeDuration || 5;
const digit = config.digit || 4;
const mockSend = config.mockSend;
const emailSuffixes = config.emailSuffixes;
// 检查邮箱后缀是否满足配置
if (emailSuffixes?.length > 0) {
let isValid = false;
for (const suffix of emailSuffixes) {
if (email.endsWith(suffix)) {
isValid = true;
break;
}
}
if (!isValid) {
throw new OakUserException('邮箱后缀不符合要求');
}
}
let emailOptions = {
// host: emailConfig.host,
// port: emailConfig.port,
@ -2229,6 +2104,7 @@ export async function sendCaptchaByEmail({ email, env, type: captchaType, }, con
const [count1, count2] = await Promise.all([
context.count('captcha', {
filter: {
origin: 'email',
visitorId,
$$createAt$$: {
$gt: now - 3600 * 1000,
@ -2276,10 +2152,11 @@ export async function sendCaptchaByEmail({ email, env, type: captchaType, }, con
});
const getCode = async () => {
let code;
code = Math.floor(Math.random() * Math.random() * Math.pow(10, digit)).toString();
while (code.length < digit) {
code += '0';
}
// code = Math.floor(Math.random() * Math.random() * Math.pow(10, digit)).toString();
// while (code.length < digit) {
// code += '0';
// }
code = Array.from({ length: digit }, () => Math.floor(Math.random() * 10)).join('');
const id = await generateNewIdAsync();
await context.operate('captcha', {
id: await generateNewIdAsync(),
@ -2307,6 +2184,17 @@ export async function sendCaptchaByEmail({ email, env, type: captchaType, }, con
closeRootMode();
throw new OakUserException('您的操作太迅捷啦,请稍候再点吧');
}
await context.operate('captcha', {
id: await generateNewIdAsync(),
action: 'update',
data: {
expired: true
},
filter: {
id: captcha.id
}
}, {});
code = await getCode();
}
else {
code = await getCode();

View File

@ -35,6 +35,24 @@ export default function Web(props) {
</Form>
</Col>
<Col flex="auto">
<Divider orientation="left" className={Styles.title}>
网站-微信支付
</Divider>
<Form colon={true} labelAlign="left" layout="vertical" style={{ marginTop: 10 }}>
<Form.Item label="appId">
<>
<Input placeholder="请输入appId" type="text" value={config?.wechatPay?.appId} onChange={(e) => setValue(`wechatPay.appId`, e.target.value)}/>
</>
</Form.Item>
<Form.Item label="appSecret">
<>
<Input placeholder="请输入appSecret" type="text" value={config?.wechatPay?.appSecret} onChange={(e) => setValue(`wechatPay.appSecret`, e.target.value)}/>
</>
</Form.Item>
</Form>
</Col>
<Col flex="auto">
<Divider orientation="left" className={Styles.title}>
location
@ -68,61 +86,5 @@ export default function Web(props) {
</Form.Item>
</Form>
</Col>
{/* <Col flex="auto">
<Divider orientation="left" className={Styles.title}>
网站-授权方式
</Divider>
<Form
colon={true}
labelAlign="left"
layout="vertical"
style={{ marginTop: 10 }}
>
<Form.Item label="passport"
//name="passport"
>
<>
<Select
mode="multiple"
allowClear
style={{ width: '100%' }}
placeholder="请选择授权方式"
value={config?.passport as Passport[]}
onChange={(value: Passport[]) => {
if (value.includes('wechat') && value.includes('wechatPublic')) {
message.warning('微信网站和微信公众号中,只能选择一个')
return;
}
setValue(`passport`, value);
}}
options={
[
{
label: '邮箱',
value: 'email',
},
{
label: '手机号',
value: 'mobile',
},
{
label: '微信网站',
value: 'wechat',
},
{
label: '微信公众号',
value: 'wechatPublic',
},
] as Array<{
label: string;
value: Passport;
}>
}
/>
</>
</Form.Item>
</Form>
</Col> */}
</Space>);
}

View File

@ -5,7 +5,6 @@ export default function WechatPublic(props) {
const [open, setModal] = useState(false);
const [messageType, setMessageType] = useState('');
const { config, setValue, cleanKey, removeItem, isService = true } = props;
const templateMsgs = config?.templateMsgs || {};
return (<Space direction="vertical" size="middle" style={{ display: 'flex' }}>
<Row>
<Card className={Styles.tips}>

View File

@ -1,7 +1,7 @@
import { Style } from '../../../../types/Style';
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../../oak-app-domain").EntityDict, keyof import("../../../../oak-app-domain").EntityDict, false, {
style: Style;
entity: "application" | "system" | "platform";
entity: "system" | "platform" | "application";
entityId: string;
name: string;
}>) => React.ReactElement;

View File

@ -24,7 +24,7 @@ declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends key
loadingIcon?: import("react").ReactNode;
disabled?: boolean | undefined;
onClick?: ((event: import("react").MouseEvent<HTMLButtonElement, MouseEvent>) => unknown) | undefined;
type?: "button" | "submit" | "reset" | undefined;
type?: "button" | "reset" | "submit" | undefined;
shape?: "default" | "rounded" | "rectangular" | undefined;
children?: import("react").ReactNode;
} & Pick<import("react").ClassAttributes<HTMLButtonElement> & import("react").ButtonHTMLAttributes<HTMLButtonElement>, "id" | "onMouseDown" | "onMouseUp" | "onTouchEnd" | "onTouchStart"> & {

View File

@ -1,6 +1,6 @@
import { EntityDict } from '../../../oak-app-domain';
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<EntityDict, keyof EntityDict, false, {
type: "login" | "bind";
type: "bind" | "login";
url: string;
size: undefined;
}>) => React.ReactElement;

View File

@ -29,6 +29,10 @@ export type WebConfig = {
domain?: string;
enable?: boolean;
};
wechatPay?: {
appId: string;
appSecret: string;
};
passport?: Passport[];
location: {
protocol: 'http:' | 'https:';
@ -36,14 +40,12 @@ export type WebConfig = {
port: string;
};
};
export type WechatPublicTemplateMsgsConfig = Record<string, string>;
export type WechatPublicConfig = {
type: 'wechatPublic';
isService: boolean;
appId: string;
appSecret: string;
originalId?: string;
templateMsgs?: WechatPublicTemplateMsgsConfig;
server?: {
url?: string;
token: string;

View File

@ -29,6 +29,10 @@ export type WebConfig = {
domain?: string;
enable?: boolean;
};
wechatPay?: {
appId: string;
appSecret: string;
};
passport?: Passport[];
location: {
protocol: "http:" | "https:";
@ -36,14 +40,12 @@ export type WebConfig = {
port: string;
};
};
export type WechatPublicTemplateMsgsConfig = Record<string, string>;
export type WechatPublicConfig = {
type: "wechatPublic";
isService: boolean;
appId: string;
appSecret: string;
originalId?: string;
templateMsgs?: WechatPublicTemplateMsgsConfig;
server?: {
url?: string;
token: string;

View File

@ -1,2 +1,2 @@
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
export default _default;

View File

@ -14,7 +14,7 @@ export declare function createToDo<ED extends EntityDict & BaseEntityDict, T ext
redirectTo: EntityDict['toDo']['OpSchema']['redirectTo'];
entity: any;
entityId: string;
}, userIds?: string[]): Promise<0 | 1>;
}, userIds?: string[]): Promise<1 | 0>;
/**
* todo例程entity对象上进行action操作时filtertodo完成
* entity的action的后trigger中调用

View File

@ -9,10 +9,10 @@ export type QiniuLiveConfig = {
liveHost: string;
hub: string;
publishDomain: string;
playDomainType: 'rtmp' | 'hls' | 'flv';
playDomainType?: 'rtmp' | 'hls' | 'flv';
playDomain: string;
playBackDomain: string;
publishSecurity: 'none' | 'static' | 'expiry' | 'expiry_sk';
publishSecurity?: 'none' | 'static' | 'expiry' | 'expiry_sk';
publishKey: string;
playKey: string;
};

7
es/utils/passport.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
import { EntityDict } from "../oak-app-domain";
import { BRC } from "../types/RuntimeCxt";
import { EmailConfig } from '../oak-app-domain/Passport/Schema';
export declare function getAndCheckPassportByEmail<ED extends EntityDict>(context: BRC<ED>, email: string): Promise<{
emailConfig: import("../types/Config").EmailConfig;
config: EmailConfig;
}>;

48
es/utils/passport.js Normal file
View File

@ -0,0 +1,48 @@
import { assert } from 'oak-domain/lib/utils/assert';
import { OakUserException } from 'oak-domain/lib/types';
export async function getAndCheckPassportByEmail(context, email) {
const application = context.getApplication();
const { system } = application;
const [applicationPassport] = await context.select('applicationPassport', {
data: {
id: 1,
passportId: 1,
passport: {
id: 1,
config: 1,
type: 1,
},
applicationId: 1,
},
filter: {
applicationId: application?.id,
passport: {
type: 'email',
},
},
}, {
dontCollect: true,
});
assert(applicationPassport?.passport);
const config = applicationPassport.passport.config;
const emailConfig = system?.config.Emails?.find((ele) => ele.account === config.account);
assert(emailConfig);
const emailSuffixes = config.emailSuffixes;
// 检查邮箱后缀是否满足配置
if (emailSuffixes?.length > 0) {
let isValid = false;
for (const suffix of emailSuffixes) {
if (email.endsWith(suffix)) {
isValid = true;
break;
}
}
if (!isValid) {
throw new OakUserException('error::user.emailSuffixIsInvalid');
}
}
return {
emailConfig,
config
};
}

View File

@ -15,6 +15,7 @@ const user_1 = require("./user");
const lodash_1 = require("oak-domain/lib/utils/lodash");
const email_1 = require("../utils/email");
const validator_1 = require("oak-domain/lib/utils/validator");
const passport_1 = require("../utils/passport");
async function makeDistinguishException(userId, context, message) {
const [user] = await context.select('user', {
data: {
@ -472,6 +473,16 @@ async function loginByMobile(params, context) {
if (captchaRow.expired) {
throw new types_1.OakUserException('验证码已经过期');
}
await context.operate('captcha', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'update',
data: {
expired: true
},
filter: {
id: captchaRow.id
}
}, {});
// 到这里说明验证码已经通过
return await setupMobile(mobile, env, context);
}
@ -566,49 +577,7 @@ async function loginByAccount(params, context) {
(0, assert_1.assert)(account);
const accountType = (0, validator_1.isEmail)(account) ? 'email' : ((0, validator_1.isMobile)(account) ? 'mobile' : 'loginName');
if (accountType === 'email') {
// const application = context.getApplication();
// const { system } = application!;
// const [applicationPassport] = await context.select('applicationPassport',
// {
// data: {
// id: 1,
// passportId: 1,
// passport: {
// id: 1,
// config: 1,
// type: 1,
// },
// applicationId: 1,
// },
// filter: {
// applicationId: application?.id!,
// passport: {
// type: 'email'
// },
// }
// },
// {
// dontCollect: true,
// }
// );
// assert(applicationPassport?.passport);
// const config = applicationPassport.passport.config as EmailConfig;
// const emailConfig = system?.config.Emails?.find((ele) => ele.account === config.account);
// assert(emailConfig);
// const emailSuffixes = config.emailSuffixes;
// // 检查邮箱后缀是否满足配置
// if (emailSuffixes?.length! > 0) {
// let isValid = false;
// for (const suffix of emailSuffixes!) {
// if (account.endsWith(suffix)) {
// isValid = true;
// break;
// }
// }
// if (!isValid) {
// throw new OakUserException('error::user.emailSuffixIsInvalid');
// }
// }
const { config, emailConfig } = await (0, passport_1.getAndCheckPassportByEmail)(context, account);
const existEmail = await context.select('email', {
data: {
id: 1,
@ -987,46 +956,7 @@ async function loginByEmail(params, context) {
}
};
const closeRootMode = context.openRootMode();
const application = context.getApplication();
const { system } = application;
const [applicationPassport] = await context.select('applicationPassport', {
data: {
id: 1,
passportId: 1,
passport: {
id: 1,
config: 1,
type: 1,
},
applicationId: 1,
},
filter: {
applicationId: application?.id,
passport: {
type: 'email'
},
}
}, {
dontCollect: true,
});
(0, assert_1.assert)(applicationPassport?.passport);
const config = applicationPassport.passport.config;
const emailConfig = system?.config.Emails?.find((ele) => ele.account === config.account);
(0, assert_1.assert)(emailConfig);
const emailSuffixes = config.emailSuffixes;
// 检查邮箱后缀是否满足配置
if (emailSuffixes?.length > 0) {
let isValid = false;
for (const suffix of emailSuffixes) {
if (email.endsWith(suffix)) {
isValid = true;
break;
}
}
if (!isValid) {
throw new types_1.OakUserException('邮箱后缀不符合要求');
}
}
const { config, emailConfig } = await (0, passport_1.getAndCheckPassportByEmail)(context, email);
if (disableRegister) {
const [existEmail] = await context.select('email', {
data: {
@ -1081,6 +1011,16 @@ async function bindByMobile(params, context) {
throw new types_1.OakUserException('验证码已经过期');
}
// 到这里说明验证码已经通过
await context.operate('captcha', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'update',
data: {
expired: true
},
filter: {
id: captchaRow.id
}
}, {});
// 检查当前user是否已绑定mobile
const [boundMobile] = await context.select('mobile', {
data: {
@ -1242,46 +1182,7 @@ async function bindByEmail(params, context) {
}
};
const closeRootMode = context.openRootMode();
const application = context.getApplication();
const { system } = application;
const [applicationPassport] = await context.select('applicationPassport', {
data: {
id: 1,
passportId: 1,
passport: {
id: 1,
config: 1,
type: 1,
},
applicationId: 1,
},
filter: {
applicationId: application?.id,
passport: {
type: 'email'
},
}
}, {
dontCollect: true,
});
(0, assert_1.assert)(applicationPassport?.passport);
const config = applicationPassport.passport.config;
const emailConfig = system?.config.Emails?.find((ele) => ele.account === config.account);
(0, assert_1.assert)(emailConfig);
const emailSuffixes = config.emailSuffixes;
// 检查邮箱后缀是否满足配置
if (emailSuffixes?.length > 0) {
let isValid = false;
for (const suffix of emailSuffixes) {
if (email.endsWith(suffix)) {
isValid = true;
break;
}
}
if (!isValid) {
throw new types_1.OakUserException('邮箱后缀不符合要求');
}
}
const { config, emailConfig } = await (0, passport_1.getAndCheckPassportByEmail)(context, email);
const [otherUserEmail] = await context.select('email', {
data: {
id: 1,
@ -2070,6 +1971,7 @@ async function sendCaptchaByMobile({ mobile, env, type: captchaType, }, context)
const [count1, count2] = await Promise.all([
context.count('captcha', {
filter: {
origin: 'mobile',
visitorId,
$$createAt$$: {
$gt: now - 3600 * 1000,
@ -2121,10 +2023,11 @@ async function sendCaptchaByMobile({ mobile, env, type: captchaType, }, context)
code = mobile.substring(11 - digit);
}
else {
code = Math.floor(Math.random() * Math.pow(10, digit)).toString();
while (code.length < digit) {
code += '0';
}
// code = Math.floor(Math.random() * Math.pow(10, digit)).toString();
// while (code.length < digit) {
// code += '0';
// }
code = Array.from({ length: digit }, () => Math.floor(Math.random() * 10)).join('');
}
const id = await (0, uuid_1.generateNewIdAsync)();
await context.operate('captcha', {
@ -2153,6 +2056,17 @@ async function sendCaptchaByMobile({ mobile, env, type: captchaType, }, context)
closeRootMode();
throw new types_1.OakUserException('您的操作太迅捷啦,请稍候再点吧');
}
await context.operate('captcha', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'update',
data: {
expired: true
},
filter: {
id: captcha.id
}
}, {});
code = await getCode();
}
else {
code = await getCode();
@ -2185,49 +2099,10 @@ async function sendCaptchaByEmail({ email, env, type: captchaType, }, context) {
if (type === 'web' || type === 'native') {
visitorId = env.visitorId;
}
const application = context.getApplication();
const { system } = application;
const [applicationPassport] = await context.select('applicationPassport', {
data: {
id: 1,
passportId: 1,
passport: {
id: 1,
config: 1,
type: 1,
},
applicationId: 1,
},
filter: {
applicationId: application?.id,
passport: {
type: 'email'
},
}
}, {
dontCollect: true,
});
(0, assert_1.assert)(applicationPassport?.passport);
const config = applicationPassport.passport.config;
const emailConfig = system?.config.Emails?.find((ele) => ele.account === config.account);
(0, assert_1.assert)(emailConfig);
const { config, emailConfig } = await (0, passport_1.getAndCheckPassportByEmail)(context, email);
const duration = config.codeDuration || 5;
const digit = config.digit || 4;
const mockSend = config.mockSend;
const emailSuffixes = config.emailSuffixes;
// 检查邮箱后缀是否满足配置
if (emailSuffixes?.length > 0) {
let isValid = false;
for (const suffix of emailSuffixes) {
if (email.endsWith(suffix)) {
isValid = true;
break;
}
}
if (!isValid) {
throw new types_1.OakUserException('邮箱后缀不符合要求');
}
}
let emailOptions = {
// host: emailConfig.host,
// port: emailConfig.port,
@ -2246,6 +2121,7 @@ async function sendCaptchaByEmail({ email, env, type: captchaType, }, context) {
const [count1, count2] = await Promise.all([
context.count('captcha', {
filter: {
origin: 'email',
visitorId,
$$createAt$$: {
$gt: now - 3600 * 1000,
@ -2293,10 +2169,11 @@ async function sendCaptchaByEmail({ email, env, type: captchaType, }, context) {
});
const getCode = async () => {
let code;
code = Math.floor(Math.random() * Math.random() * Math.pow(10, digit)).toString();
while (code.length < digit) {
code += '0';
}
// code = Math.floor(Math.random() * Math.random() * Math.pow(10, digit)).toString();
// while (code.length < digit) {
// code += '0';
// }
code = Array.from({ length: digit }, () => Math.floor(Math.random() * 10)).join('');
const id = await (0, uuid_1.generateNewIdAsync)();
await context.operate('captcha', {
id: await (0, uuid_1.generateNewIdAsync)(),
@ -2324,6 +2201,17 @@ async function sendCaptchaByEmail({ email, env, type: captchaType, }, context) {
closeRootMode();
throw new types_1.OakUserException('您的操作太迅捷啦,请稍候再点吧');
}
await context.operate('captcha', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'update',
data: {
expired: true
},
filter: {
id: captcha.id
}
}, {});
code = await getCode();
}
else {
code = await getCode();

View File

@ -29,6 +29,10 @@ export type WebConfig = {
domain?: string;
enable?: boolean;
};
wechatPay?: {
appId: string;
appSecret: string;
};
passport?: Passport[];
location: {
protocol: 'http:' | 'https:';
@ -36,14 +40,12 @@ export type WebConfig = {
port: string;
};
};
export type WechatPublicTemplateMsgsConfig = Record<string, string>;
export type WechatPublicConfig = {
type: 'wechatPublic';
isService: boolean;
appId: string;
appSecret: string;
originalId?: string;
templateMsgs?: WechatPublicTemplateMsgsConfig;
server?: {
url?: string;
token: string;

View File

@ -29,6 +29,10 @@ export type WebConfig = {
domain?: string;
enable?: boolean;
};
wechatPay?: {
appId: string;
appSecret: string;
};
passport?: Passport[];
location: {
protocol: "http:" | "https:";
@ -36,14 +40,12 @@ export type WebConfig = {
port: string;
};
};
export type WechatPublicTemplateMsgsConfig = Record<string, string>;
export type WechatPublicConfig = {
type: "wechatPublic";
isService: boolean;
appId: string;
appSecret: string;
originalId?: string;
templateMsgs?: WechatPublicTemplateMsgsConfig;
server?: {
url?: string;
token: string;

View File

@ -1,2 +1,2 @@
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
export default _default;

View File

@ -9,10 +9,10 @@ export type QiniuLiveConfig = {
liveHost: string;
hub: string;
publishDomain: string;
playDomainType: 'rtmp' | 'hls' | 'flv';
playDomainType?: 'rtmp' | 'hls' | 'flv';
playDomain: string;
playBackDomain: string;
publishSecurity: 'none' | 'static' | 'expiry' | 'expiry_sk';
publishSecurity?: 'none' | 'static' | 'expiry' | 'expiry_sk';
publishKey: string;
playKey: string;
};

7
lib/utils/passport.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
import { EntityDict } from "../oak-app-domain";
import { BRC } from "../types/RuntimeCxt";
import { EmailConfig } from '../oak-app-domain/Passport/Schema';
export declare function getAndCheckPassportByEmail<ED extends EntityDict>(context: BRC<ED>, email: string): Promise<{
emailConfig: import("../types/Config").EmailConfig;
config: EmailConfig;
}>;

52
lib/utils/passport.js Normal file
View File

@ -0,0 +1,52 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAndCheckPassportByEmail = void 0;
const assert_1 = require("oak-domain/lib/utils/assert");
const types_1 = require("oak-domain/lib/types");
async function getAndCheckPassportByEmail(context, email) {
const application = context.getApplication();
const { system } = application;
const [applicationPassport] = await context.select('applicationPassport', {
data: {
id: 1,
passportId: 1,
passport: {
id: 1,
config: 1,
type: 1,
},
applicationId: 1,
},
filter: {
applicationId: application?.id,
passport: {
type: 'email',
},
},
}, {
dontCollect: true,
});
(0, assert_1.assert)(applicationPassport?.passport);
const config = applicationPassport.passport.config;
const emailConfig = system?.config.Emails?.find((ele) => ele.account === config.account);
(0, assert_1.assert)(emailConfig);
const emailSuffixes = config.emailSuffixes;
// 检查邮箱后缀是否满足配置
if (emailSuffixes?.length > 0) {
let isValid = false;
for (const suffix of emailSuffixes) {
if (email.endsWith(suffix)) {
isValid = true;
break;
}
}
if (!isValid) {
throw new types_1.OakUserException('error::user.emailSuffixIsInvalid');
}
}
return {
emailConfig,
config
};
}
exports.getAndCheckPassportByEmail = getAndCheckPassportByEmail;

View File

@ -44,6 +44,7 @@ import { sendEmail } from '../utils/email';
import { EmailConfig } from '../oak-app-domain/Passport/Schema';
import { isEmail, isMobile } from 'oak-domain/lib/utils/validator';
import { EmailOptions } from '../types/Email';
import { getAndCheckPassportByEmail } from '../utils/passport';
async function makeDistinguishException<ED extends EntityDict>(userId: string, context: BRC<ED>, message?: string) {
const [user] = await context.select(
@ -641,6 +642,20 @@ export async function loginByMobile<ED extends EntityDict>(
if (captchaRow.expired) {
throw new OakUserException('验证码已经过期');
}
await context.operate(
'captcha',
{
id: await generateNewIdAsync(),
action: 'update',
data: {
expired: true
},
filter: {
id: captchaRow.id!
}
},
{}
);
// 到这里说明验证码已经通过
return await setupMobile<ED>(mobile, env, context);
@ -762,50 +777,9 @@ export async function loginByAccount<ED extends EntityDict>(
assert(account);
const accountType = isEmail(account) ? 'email' : (isMobile(account) ? 'mobile' : 'loginName');
if (accountType === 'email') {
// const application = context.getApplication();
// const { system } = application!;
// const [applicationPassport] = await context.select('applicationPassport',
// {
// data: {
// id: 1,
// passportId: 1,
// passport: {
// id: 1,
// config: 1,
// type: 1,
// },
// applicationId: 1,
// },
// filter: {
// applicationId: application?.id!,
// passport: {
// type: 'email'
// },
// }
// },
// {
// dontCollect: true,
// }
// );
// assert(applicationPassport?.passport);
// const config = applicationPassport.passport.config as EmailConfig;
// const emailConfig = system?.config.Emails?.find((ele) => ele.account === config.account);
// assert(emailConfig);
// const emailSuffixes = config.emailSuffixes;
// // 检查邮箱后缀是否满足配置
// if (emailSuffixes?.length! > 0) {
// let isValid = false;
// for (const suffix of emailSuffixes!) {
// if (account.endsWith(suffix)) {
// isValid = true;
// break;
// }
// }
// if (!isValid) {
// throw new OakUserException('error::user.emailSuffixIsInvalid');
// }
// }
const { config, emailConfig } = await getAndCheckPassportByEmail(context, account);
const existEmail = await context.select(
'email',
{
@ -1244,51 +1218,9 @@ export async function loginByEmail<ED extends EntityDict>(
}
};
const closeRootMode = context.openRootMode();
const application = context.getApplication();
const { system } = application!;
const [applicationPassport] = await context.select('applicationPassport',
{
data: {
id: 1,
passportId: 1,
passport: {
id: 1,
config: 1,
type: 1,
},
applicationId: 1,
},
filter: {
applicationId: application?.id!,
passport: {
type: 'email'
},
}
},
{
dontCollect: true,
}
);
assert(applicationPassport?.passport);
const config = applicationPassport.passport.config as EmailConfig;
const emailConfig = system?.config.Emails?.find((ele) => ele.account === config.account);
assert(emailConfig);
const emailSuffixes = config.emailSuffixes;
// 检查邮箱后缀是否满足配置
if (emailSuffixes?.length! > 0) {
let isValid = false;
for (const suffix of emailSuffixes!) {
if (email.endsWith(suffix)) {
isValid = true;
break;
}
}
const { config, emailConfig } = await getAndCheckPassportByEmail(context, email);
if (!isValid) {
throw new OakUserException('邮箱后缀不符合要求')
}
}
if (disableRegister) {
const [existEmail] = await context.select(
'email',
@ -1359,8 +1291,22 @@ export async function bindByMobile<ED extends EntityDict>(
if (captchaRow.expired) {
throw new OakUserException('验证码已经过期');
}
// 到这里说明验证码已经通过
await context.operate(
'captcha',
{
id: await generateNewIdAsync(),
action: 'update',
data: {
expired: true
},
filter: {
id: captchaRow.id!
}
},
{}
);
// 检查当前user是否已绑定mobile
const [boundMobile] = await context.select(
'mobile',
@ -1379,6 +1325,7 @@ export async function bindByMobile<ED extends EntityDict>(
forUpdate: true,
}
)
if (boundMobile) {
//用户已绑定的mobile与当前输入的mobile一致
if (boundMobile.mobile === mobile) {
@ -1567,51 +1514,8 @@ export async function bindByEmail<ED extends EntityDict>(
const closeRootMode = context.openRootMode();
const application = context.getApplication();
const { system } = application!;
const [applicationPassport] = await context.select('applicationPassport',
{
data: {
id: 1,
passportId: 1,
passport: {
id: 1,
config: 1,
type: 1,
},
applicationId: 1,
},
filter: {
applicationId: application?.id!,
passport: {
type: 'email'
},
}
},
{
dontCollect: true,
}
);
assert(applicationPassport?.passport);
const config = applicationPassport.passport.config as EmailConfig;
const emailConfig = system?.config.Emails?.find((ele) => ele.account === config.account);
assert(emailConfig);
const emailSuffixes = config.emailSuffixes;
const { config, emailConfig } = await getAndCheckPassportByEmail(context, email);
// 检查邮箱后缀是否满足配置
if (emailSuffixes?.length! > 0) {
let isValid = false;
for (const suffix of emailSuffixes!) {
if (email.endsWith(suffix)) {
isValid = true;
break;
}
}
if (!isValid) {
throw new OakUserException('邮箱后缀不符合要求')
}
}
const [otherUserEmail] = await context.select(
'email',
@ -2730,6 +2634,7 @@ export async function sendCaptchaByMobile<ED extends EntityDict>(
'captcha',
{
filter: {
origin: 'mobile',
visitorId,
$$createAt$$: {
$gt: now - 3600 * 1000,
@ -2790,10 +2695,11 @@ export async function sendCaptchaByMobile<ED extends EntityDict>(
if (mockSend) {
code = mobile.substring(11 - digit);
} else {
code = Math.floor(Math.random() * Math.pow(10, digit)).toString();
while (code.length < digit) {
code += '0';
}
// code = Math.floor(Math.random() * Math.pow(10, digit)).toString();
// while (code.length < digit) {
// code += '0';
// }
code = Array.from({ length: digit }, () => Math.floor(Math.random() * 10)).join('');
}
const id = await generateNewIdAsync();
@ -2826,7 +2732,22 @@ export async function sendCaptchaByMobile<ED extends EntityDict>(
if (now - (captcha.$$createAt$$! as number) < captchaDuration) {
closeRootMode();
throw new OakUserException('您的操作太迅捷啦,请稍候再点吧');
}
}
await context.operate(
'captcha',
{
id: await generateNewIdAsync(),
action: 'update',
data: {
expired: true
},
filter: {
id: captcha.id!
}
},
{}
);
code = await getCode();
} else {
code = await getCode();
}
@ -2872,54 +2793,11 @@ export async function sendCaptchaByEmail<ED extends EntityDict>(
if (type === 'web' || type === 'native') {
visitorId = env.visitorId;
}
const application = context.getApplication();
const { system } = application!;
const [applicationPassport] = await context.select('applicationPassport',
{
data: {
id: 1,
passportId: 1,
passport: {
id: 1,
config: 1,
type: 1,
},
applicationId: 1,
},
filter: {
applicationId: application?.id!,
passport: {
type: 'email'
},
}
},
{
dontCollect: true,
}
);
assert(applicationPassport?.passport);
const config = applicationPassport.passport.config as EmailConfig;
const emailConfig = system?.config.Emails?.find((ele) => ele.account === config.account);
assert(emailConfig);
const { config, emailConfig } = await getAndCheckPassportByEmail(context, email);
const duration = config.codeDuration || 5;
const digit = config.digit || 4;
const mockSend = config.mockSend;
const emailSuffixes = config.emailSuffixes;
// 检查邮箱后缀是否满足配置
if (emailSuffixes?.length! > 0) {
let isValid = false;
for (const suffix of emailSuffixes!) {
if (email.endsWith(suffix)) {
isValid = true;
break;
}
}
if (!isValid) {
throw new OakUserException('邮箱后缀不符合要求')
}
}
let emailOptions: EmailOptions = {
// host: emailConfig.host,
@ -2942,6 +2820,7 @@ export async function sendCaptchaByEmail<ED extends EntityDict>(
'captcha',
{
filter: {
origin: 'email',
visitorId,
$$createAt$$: {
$gt: now - 3600 * 1000,
@ -3000,10 +2879,11 @@ export async function sendCaptchaByEmail<ED extends EntityDict>(
const getCode = async () => {
let code: string;
code = Math.floor(Math.random() * Math.random() * Math.pow(10, digit)).toString();
while (code.length < digit) {
code += '0';
}
// code = Math.floor(Math.random() * Math.random() * Math.pow(10, digit)).toString();
// while (code.length < digit) {
// code += '0';
// }
code = Array.from({ length: digit }, () => Math.floor(Math.random() * 10)).join('');
const id = await generateNewIdAsync();
await context.operate(
'captcha',
@ -3036,6 +2916,21 @@ export async function sendCaptchaByEmail<ED extends EntityDict>(
closeRootMode();
throw new OakUserException('您的操作太迅捷啦,请稍候再点吧');
}
await context.operate(
'captcha',
{
id: await generateNewIdAsync(),
action: 'update',
data: {
expired: true
},
filter: {
id: captcha.id!
}
},
{}
);
code = await getCode();
} else {
code = await getCode();
}

View File

@ -91,6 +91,47 @@ export default function Web(props: {
</Form>
</Col>
<Col flex="auto">
<Divider orientation="left" className={Styles.title}>
-
</Divider>
<Form
colon={true}
labelAlign="left"
layout="vertical"
style={{ marginTop: 10 }}
>
<Form.Item label="appId"
//name="appId"
>
<>
<Input
placeholder="请输入appId"
type="text"
value={config?.wechatPay?.appId}
onChange={(e) =>
setValue(`wechatPay.appId`, e.target.value)
}
/>
</>
</Form.Item>
<Form.Item label="appSecret"
//name="appSecret"
>
<>
<Input
placeholder="请输入appSecret"
type="text"
value={config?.wechatPay?.appSecret}
onChange={(e) =>
setValue(`wechatPay.appSecret`, e.target.value)
}
/>
</>
</Form.Item>
</Form>
</Col>
<Col flex="auto">
<Divider orientation="left" className={Styles.title}>
location
@ -158,62 +199,6 @@ export default function Web(props: {
</Form.Item>
</Form>
</Col>
{/* <Col flex="auto">
<Divider orientation="left" className={Styles.title}>
-
</Divider>
<Form
colon={true}
labelAlign="left"
layout="vertical"
style={{ marginTop: 10 }}
>
<Form.Item label="passport"
//name="passport"
>
<>
<Select
mode="multiple"
allowClear
style={{ width: '100%' }}
placeholder="请选择授权方式"
value={config?.passport as Passport[]}
onChange={(value: Passport[]) => {
if (value.includes('wechat') && value.includes('wechatPublic')) {
message.warning('微信网站和微信公众号中,只能选择一个')
return;
}
setValue(`passport`, value);
}}
options={
[
{
label: '邮箱',
value: 'email',
},
{
label: '手机号',
value: 'mobile',
},
{
label: '微信网站',
value: 'wechat',
},
{
label: '微信公众号',
value: 'wechatPublic',
},
] as Array<{
label: string;
value: Passport;
}>
}
/>
</>
</Form.Item>
</Form>
</Col> */}
</Space>
);
}

View File

@ -35,7 +35,6 @@ export default function WechatPublic(props: {
const [messageType, setMessageType] = useState('');
const { config, setValue, cleanKey, removeItem, isService = true } = props;
const templateMsgs = config?.templateMsgs || {};
return (
<Space direction="vertical" size="middle" style={{ display: 'flex' }}>
<Row>

View File

@ -33,6 +33,10 @@ export type WebConfig = {
domain?: string;
enable?: boolean; //启用扫码登录
};
wechatPay?: {
appId: string;
appSecret: string; //微信支付
};
passport?: Passport[];
location: {
protocol: 'http:' | 'https:';
@ -41,7 +45,6 @@ export type WebConfig = {
}
};
export type WechatPublicTemplateMsgsConfig = Record<string, string>; // key值代表messageTypeIdvalue的值代表对应的templateIddata的转换改成message上的函数注入
export type WechatPublicConfig = {
type: 'wechatPublic';
@ -49,7 +52,6 @@ export type WechatPublicConfig = {
appId: string;
appSecret: string;
originalId?: string; //原始id
templateMsgs?: WechatPublicTemplateMsgsConfig;
server?: {
url?: string; //服务器地址(URL)
token: string; //令牌(Token)

View File

@ -12,10 +12,10 @@ export type QiniuLiveConfig = {
liveHost: string; // 七牛直播云接口域名
hub: string; // 直播空间名,
publishDomain: string; // 推流域名
playDomainType: 'rtmp' | 'hls' | 'flv'; //拉流域名类型
playDomainType?: 'rtmp' | 'hls' | 'flv'; //拉流域名类型
playDomain: string; // 拉流域名
playBackDomain: string; // 直播回放存储域名
publishSecurity: 'none' | 'static' | 'expiry' | 'expiry_sk'; //推流鉴权方式:'none':无校验鉴权;'static':静态鉴权;'expiry':限时鉴权;'expiry_sk':限时鉴权sk
publishSecurity?: 'none' | 'static' | 'expiry' | 'expiry_sk'; //推流鉴权方式:'none':无校验鉴权;'static':静态鉴权;'expiry':限时鉴权;'expiry_sk':限时鉴权sk
publishKey: string; // 直播空间限时鉴权密钥 用于static和expiry类型的推流鉴权方式 expiry_sk使用accessKey和secretKey
playKey: string; // 拉流密钥(防盗链主密钥) 若为空则为未开启防盗链
}

View File

@ -39,7 +39,7 @@ export async function getLivestream<ED extends EntityDict & BaseEntityDict, Cxt
} = getConfig(context.getApplication()!.system!.config!, 'Live', origin);
assert(origin === 'qiniu');
const { hub, liveHost, publishDomain, playDomainType, playDomain, playKey, publishKey, publishSecurity } = config as QiniuLiveConfig;
const r = await (instance as QiniuCloudInstance).getLiveStream(hub, streamTitle, liveHost, publishDomain, playDomainType, playDomain, expireAt, publishSecurity, publishKey, playKey);
const r = await (instance as QiniuCloudInstance).getLiveStream(hub, streamTitle, liveHost, publishDomain, playDomainType!, playDomain, expireAt, publishSecurity!, publishKey, playKey);
return {
streamTitle,
hub,
@ -83,7 +83,7 @@ export async function getStreamObj<ED extends EntityDict & BaseEntityDict, Cxt e
assert(origin === 'qiniu');
const { playDomainType, publishDomain, publishSecurity, publishKey, playDomain, playKey, hub } = config as QiniuLiveConfig;
const r = (instance as QiniuCloudInstance).getStreamObj(hub, streamTitle, expireAt, publishDomain, playDomainType, playDomain, publishSecurity, publishKey, playKey);
const r = (instance as QiniuCloudInstance).getStreamObj(hub, streamTitle, expireAt, publishDomain, playDomainType!, playDomain, publishSecurity!, publishKey, playKey);
return {
streamTitle,
hub,

63
src/utils/passport.ts Normal file
View File

@ -0,0 +1,63 @@
import { EntityDict } from "../oak-app-domain";
import { RuntimeCxt, BRC } from "../types/RuntimeCxt";
import { EmailConfig } from '../oak-app-domain/Passport/Schema';
import { assert } from 'oak-domain/lib/utils/assert';
import { OakUserException } from 'oak-domain/lib/types';
export async function getAndCheckPassportByEmail<ED extends EntityDict>(
context: BRC<ED>,
email: string
) {
const application = context.getApplication();
const { system } = application!;
const [applicationPassport] = await context.select(
'applicationPassport',
{
data: {
id: 1,
passportId: 1,
passport: {
id: 1,
config: 1,
type: 1,
},
applicationId: 1,
},
filter: {
applicationId: application?.id!,
passport: {
type: 'email',
},
},
},
{
dontCollect: true,
}
);
assert(applicationPassport?.passport);
const config = applicationPassport.passport.config as EmailConfig;
const emailConfig = system?.config.Emails?.find(
(ele) => ele.account === config.account
);
assert(emailConfig);
const emailSuffixes = config.emailSuffixes;
// 检查邮箱后缀是否满足配置
if (emailSuffixes?.length! > 0) {
let isValid = false;
for (const suffix of emailSuffixes!) {
if (email.endsWith(suffix)) {
isValid = true;
break;
}
}
if (!isValid) {
throw new OakUserException('error::user.emailSuffixIsInvalid');
}
}
return {
emailConfig,
config
};
}

View File

@ -118,6 +118,8 @@ export const system: System[] = [
publishKey: '',
playKey: '',
playBackDomain: '',
playDomainType: 'rtmp',
publishSecurity: 'none',
},
},
Account: {