提交 bbcd1233 编写于 作者: DCloud_JSON's avatar DCloud_JSON

更新uni-id-co

上级 36491053
const db = uniCloud.database() const db = uniCloud.database()
const dbCmd = db.command const dbCmd = db.command
const userCollectionName = 'uni-id-users' const userCollectionName = 'uni-id-users'
const userCollection = db.collection(userCollectionName) const userCollection = db.collection(userCollectionName)
const verifyCollectionName = 'opendb-verify-codes' const verifyCollectionName = 'opendb-verify-codes'
const verifyCollection = db.collection(verifyCollectionName) const verifyCollection = db.collection(verifyCollectionName)
const deviceCollectionName = 'uni-id-device' const deviceCollectionName = 'uni-id-device'
const deviceCollection = db.collection(deviceCollectionName) const deviceCollection = db.collection(deviceCollectionName)
const openDataCollectionName = 'opendb-open-data' const openDataCollectionName = 'opendb-open-data'
const openDataCollection = db.collection(openDataCollectionName) const openDataCollection = db.collection(openDataCollectionName)
const USER_IDENTIFIER = { const USER_IDENTIFIER = {
username: 'username', username: 'username',
mobile: 'mobile', mobile: 'mobile',
email: 'email', email: 'email',
wx_unionid: 'wechat-account', wx_unionid: 'wechat-account',
'wx_openid.app': 'wechat-account', 'wx_openid.app': 'wechat-account',
'wx_openid.mp': 'wechat-account', 'wx_openid.mp': 'wechat-account',
'wx_openid.h5': 'wechat-account', 'wx_openid.h5': 'wechat-account',
'wx_openid.web': 'wechat-account', 'wx_openid.web': 'wechat-account',
qq_unionid: 'qq-account', qq_unionid: 'qq-account',
'qq_openid.app': 'qq-account', 'qq_openid.app': 'qq-account',
'qq_openid.mp': 'qq-account', 'qq_openid.mp': 'qq-account',
ali_openid: 'alipay-account', ali_openid: 'alipay-account',
apple_openid: 'alipay-account' apple_openid: 'alipay-account'
} }
const USER_STATUS = { const USER_STATUS = {
NORMAL: 0, NORMAL: 0,
BANNED: 1, BANNED: 1,
AUDITING: 2, AUDITING: 2,
AUDIT_FAILED: 3, AUDIT_FAILED: 3,
CLOSED: 4 CLOSED: 4
} }
const CAPTCHA_SCENE = { const CAPTCHA_SCENE = {
REGISTER: 'register', REGISTER: 'register',
LOGIN_BY_PWD: 'login-by-pwd', LOGIN_BY_PWD: 'login-by-pwd',
LOGIN_BY_SMS: 'login-by-sms', LOGIN_BY_SMS: 'login-by-sms',
RESET_PWD_BY_SMS: 'reset-pwd-by-sms', RESET_PWD_BY_SMS: 'reset-pwd-by-sms',
RESET_PWD_BY_EMAIL: 'reset-pwd-by-email', RESET_PWD_BY_EMAIL: 'reset-pwd-by-email',
SEND_SMS_CODE: 'send-sms-code', SEND_SMS_CODE: 'send-sms-code',
SEND_EMAIL_CODE: 'send-email-code', SEND_EMAIL_CODE: 'send-email-code',
BIND_MOBILE_BY_SMS: 'bind-mobile-by-sms', BIND_MOBILE_BY_SMS: 'bind-mobile-by-sms',
SET_PWD_BY_SMS: 'set-pwd-by-sms' SET_PWD_BY_SMS: 'set-pwd-by-sms'
} }
const LOG_TYPE = { const LOG_TYPE = {
LOGOUT: 'logout', LOGOUT: 'logout',
LOGIN: 'login', LOGIN: 'login',
REGISTER: 'register', REGISTER: 'register',
RESET_PWD_BY_SMS: 'reset-pwd', RESET_PWD_BY_SMS: 'reset-pwd',
RESET_PWD_BY_EMAIL: 'reset-pwd', RESET_PWD_BY_EMAIL: 'reset-pwd',
BIND_MOBILE: 'bind-mobile', BIND_MOBILE: 'bind-mobile',
BIND_WEIXIN: 'bind-weixin', BIND_WEIXIN: 'bind-weixin',
BIND_QQ: 'bind-qq', BIND_QQ: 'bind-qq',
BIND_APPLE: 'bind-apple', BIND_APPLE: 'bind-apple',
BIND_ALIPAY: 'bind-alipay', BIND_ALIPAY: 'bind-alipay',
UNBIND_WEIXIN: 'unbind-weixin', UNBIND_WEIXIN: 'unbind-weixin',
UNBIND_QQ: 'unbind-qq', UNBIND_QQ: 'unbind-qq',
UNBIND_ALIPAY: 'unbind-alipay', UNBIND_ALIPAY: 'unbind-alipay',
UNBIND_APPLE: 'unbind-apple' UNBIND_APPLE: 'unbind-apple'
} }
const SMS_SCENE = { const SMS_SCENE = {
LOGIN_BY_SMS: 'login-by-sms', LOGIN_BY_SMS: 'login-by-sms',
RESET_PWD_BY_SMS: 'reset-pwd-by-sms', RESET_PWD_BY_SMS: 'reset-pwd-by-sms',
BIND_MOBILE_BY_SMS: 'bind-mobile-by-sms', BIND_MOBILE_BY_SMS: 'bind-mobile-by-sms',
SET_PWD_BY_SMS: 'set-pwd-by-sms' SET_PWD_BY_SMS: 'set-pwd-by-sms'
} }
const EMAIL_SCENE = { const EMAIL_SCENE = {
REGISTER: 'register', REGISTER: 'register',
LOGIN_BY_EMAIL: 'login-by-email', LOGIN_BY_EMAIL: 'login-by-email',
RESET_PWD_BY_EMAIL: 'reset-pwd-by-email', RESET_PWD_BY_EMAIL: 'reset-pwd-by-email',
BIND_EMAIL: 'bind-email' BIND_EMAIL: 'bind-email'
} }
module.exports = { module.exports = {
db, db,
dbCmd, dbCmd,
userCollection, userCollection,
verifyCollection, verifyCollection,
deviceCollection, deviceCollection,
openDataCollection, openDataCollection,
USER_IDENTIFIER, USER_IDENTIFIER,
USER_STATUS, USER_STATUS,
CAPTCHA_SCENE, CAPTCHA_SCENE,
LOG_TYPE, LOG_TYPE,
SMS_SCENE, SMS_SCENE,
EMAIL_SCENE EMAIL_SCENE
} }
const ERROR = { const ERROR = {
ACCOUNT_EXISTS: 'uni-id-account-exists', ACCOUNT_EXISTS: 'uni-id-account-exists',
ACCOUNT_NOT_EXISTS: 'uni-id-account-not-exists', ACCOUNT_NOT_EXISTS: 'uni-id-account-not-exists',
ACCOUNT_NOT_EXISTS_IN_CURRENT_APP: 'uni-id-account-not-exists-in-current-app', ACCOUNT_NOT_EXISTS_IN_CURRENT_APP: 'uni-id-account-not-exists-in-current-app',
ACCOUNT_CONFLICT: 'uni-id-account-conflict', ACCOUNT_CONFLICT: 'uni-id-account-conflict',
ACCOUNT_BANNED: 'uni-id-account-banned', ACCOUNT_BANNED: 'uni-id-account-banned',
ACCOUNT_AUDITING: 'uni-id-account-auditing', ACCOUNT_AUDITING: 'uni-id-account-auditing',
ACCOUNT_AUDIT_FAILED: 'uni-id-account-audit-failed', ACCOUNT_AUDIT_FAILED: 'uni-id-account-audit-failed',
ACCOUNT_CLOSED: 'uni-id-account-closed', ACCOUNT_CLOSED: 'uni-id-account-closed',
CAPTCHA_REQUIRED: 'uni-id-captcha-required', CAPTCHA_REQUIRED: 'uni-id-captcha-required',
PASSWORD_ERROR: 'uni-id-password-error', PASSWORD_ERROR: 'uni-id-password-error',
PASSWORD_ERROR_EXCEED_LIMIT: 'uni-id-password-error-exceed-limit', PASSWORD_ERROR_EXCEED_LIMIT: 'uni-id-password-error-exceed-limit',
INVALID_USERNAME: 'uni-id-invalid-username', INVALID_USERNAME: 'uni-id-invalid-username',
INVALID_PASSWORD: 'uni-id-invalid-password', INVALID_PASSWORD: 'uni-id-invalid-password',
INVALID_PASSWORD_SUPER: 'uni-id-invalid-password-super', INVALID_PASSWORD_SUPER: 'uni-id-invalid-password-super',
INVALID_PASSWORD_STRONG: 'uni-id-invalid-password-strong', INVALID_PASSWORD_STRONG: 'uni-id-invalid-password-strong',
INVALID_PASSWORD_MEDIUM: 'uni-id-invalid-password-medium', INVALID_PASSWORD_MEDIUM: 'uni-id-invalid-password-medium',
INVALID_PASSWORD_WEAK: 'uni-id-invalid-password-weak', INVALID_PASSWORD_WEAK: 'uni-id-invalid-password-weak',
INVALID_MOBILE: 'uni-id-invalid-mobile', INVALID_MOBILE: 'uni-id-invalid-mobile',
INVALID_EMAIL: 'uni-id-invalid-email', INVALID_EMAIL: 'uni-id-invalid-email',
INVALID_NICKNAME: 'uni-id-invalid-nickname', INVALID_NICKNAME: 'uni-id-invalid-nickname',
INVALID_PARAM: 'uni-id-invalid-param', INVALID_PARAM: 'uni-id-invalid-param',
PARAM_REQUIRED: 'uni-id-param-required', PARAM_REQUIRED: 'uni-id-param-required',
GET_THIRD_PARTY_ACCOUNT_FAILED: 'uni-id-get-third-party-account-failed', GET_THIRD_PARTY_ACCOUNT_FAILED: 'uni-id-get-third-party-account-failed',
GET_THIRD_PARTY_USER_INFO_FAILED: 'uni-id-get-third-party-user-info-failed', GET_THIRD_PARTY_USER_INFO_FAILED: 'uni-id-get-third-party-user-info-failed',
MOBILE_VERIFY_CODE_ERROR: 'uni-id-mobile-verify-code-error', MOBILE_VERIFY_CODE_ERROR: 'uni-id-mobile-verify-code-error',
EMAIL_VERIFY_CODE_ERROR: 'uni-id-email-verify-code-error', EMAIL_VERIFY_CODE_ERROR: 'uni-id-email-verify-code-error',
ADMIN_EXISTS: 'uni-id-admin-exists', ADMIN_EXISTS: 'uni-id-admin-exists',
PERMISSION_ERROR: 'uni-id-permission-error', PERMISSION_ERROR: 'uni-id-permission-error',
SYSTEM_ERROR: 'uni-id-system-error', SYSTEM_ERROR: 'uni-id-system-error',
SET_INVITE_CODE_FAILED: 'uni-id-set-invite-code-failed', SET_INVITE_CODE_FAILED: 'uni-id-set-invite-code-failed',
INVALID_INVITE_CODE: 'uni-id-invalid-invite-code', INVALID_INVITE_CODE: 'uni-id-invalid-invite-code',
CHANGE_INVITER_FORBIDDEN: 'uni-id-change-inviter-forbidden', CHANGE_INVITER_FORBIDDEN: 'uni-id-change-inviter-forbidden',
BIND_CONFLICT: 'uni-id-bind-conflict', BIND_CONFLICT: 'uni-id-bind-conflict',
UNBIND_FAIL: 'uni-id-unbind-failed', UNBIND_FAIL: 'uni-id-unbind-failed',
UNBIND_NOT_SUPPORTED: 'uni-id-unbind-not-supported', UNBIND_NOT_SUPPORTED: 'uni-id-unbind-not-supported',
UNBIND_UNIQUE_LOGIN: 'uni-id-unbind-unique-login', UNBIND_UNIQUE_LOGIN: 'uni-id-unbind-unique-login',
UNBIND_PASSWORD_NOT_EXISTS: 'uni-id-unbind-password-not-exists', UNBIND_PASSWORD_NOT_EXISTS: 'uni-id-unbind-password-not-exists',
UNBIND_MOBILE_NOT_EXISTS: 'uni-id-unbind-mobile-not-exists', UNBIND_MOBILE_NOT_EXISTS: 'uni-id-unbind-mobile-not-exists',
UNSUPPORTED_REQUEST: 'uni-id-unsupported-request', UNSUPPORTED_REQUEST: 'uni-id-unsupported-request',
ILLEGAL_REQUEST: 'uni-id-illegal-request' ILLEGAL_REQUEST: 'uni-id-illegal-request'
} }
function isUniIdError (errCode) { function isUniIdError (errCode) {
return Object.values(ERROR).includes(errCode) return Object.values(ERROR).includes(errCode)
} }
class UniCloudError extends Error { class UniCloudError extends Error {
constructor (options) { constructor (options) {
super(options.message) super(options.message)
this.errMsg = options.message || '' this.errMsg = options.message || ''
this.errCode = options.code this.errCode = options.code
} }
} }
module.exports = { module.exports = {
ERROR, ERROR,
isUniIdError, isUniIdError,
UniCloudError UniCloudError
} }
const { ERROR } = require('./error') const { ERROR } = require('./error')
function getHttpClientInfo () { function getHttpClientInfo () {
const requestId = this.getUniCloudRequestId() const requestId = this.getUniCloudRequestId()
const { clientIP, userAgent, source, secretType = 'none' } = this.getClientInfo() const { clientIP, userAgent, source, secretType = 'none' } = this.getClientInfo()
const { clientInfo = {} } = JSON.parse(this.getHttpInfo().body) const { clientInfo = {} } = JSON.parse(this.getHttpInfo().body)
return { return {
...clientInfo, ...clientInfo,
clientIP, clientIP,
userAgent, userAgent,
source, source,
secretType, secretType,
requestId requestId
} }
} }
function getHttpUniIdToken () { function getHttpUniIdToken () {
const { uniIdToken = '' } = JSON.parse(this.getHttpInfo().body) const { uniIdToken = '' } = JSON.parse(this.getHttpInfo().body)
return uniIdToken return uniIdToken
} }
function verifyHttpMethod () { function verifyHttpMethod () {
const { headers, httpMethod } = this.getHttpInfo() const { headers, httpMethod } = this.getHttpInfo()
if (!/^application\/json/.test(headers['content-type']) || httpMethod.toUpperCase() !== 'POST') { if (!/^application\/json/.test(headers['content-type']) || httpMethod.toUpperCase() !== 'POST') {
throw { throw {
errCode: ERROR.UNSUPPORTED_REQUEST, errCode: ERROR.UNSUPPORTED_REQUEST,
errMsg: 'unsupported request' errMsg: 'unsupported request'
} }
} }
} }
function universal () { function universal () {
if (this.getClientInfo().source === 'http') { if (this.getClientInfo().source === 'http') {
verifyHttpMethod.call(this) verifyHttpMethod.call(this)
this.getParams()[0] = JSON.parse(this.getHttpInfo().body).params this.getParams()[0] = JSON.parse(this.getHttpInfo().body).params
this.getUniversalClientInfo = getHttpClientInfo.bind(this) this.getUniversalClientInfo = getHttpClientInfo.bind(this)
this.getUniversalUniIdToken = getHttpUniIdToken.bind(this) this.getUniversalUniIdToken = getHttpUniIdToken.bind(this)
} else { } else {
this.getUniversalClientInfo = this.getClientInfo this.getUniversalClientInfo = this.getClientInfo
this.getUniversalUniIdToken = this.getUniIdToken this.getUniversalUniIdToken = this.getUniIdToken
} }
} }
module.exports = universal module.exports = universal
function batchFindObjctValue (obj = {}, keys = []) { function batchFindObjctValue (obj = {}, keys = []) {
const values = {} const values = {}
for (let i = 0; i < keys.length; i++) { for (let i = 0; i < keys.length; i++) {
const key = keys[i] const key = keys[i]
const keyPath = key.split('.') const keyPath = key.split('.')
let currentKey = keyPath.shift() let currentKey = keyPath.shift()
let result = obj let result = obj
while (currentKey) { while (currentKey) {
if (!result) { if (!result) {
break break
} }
result = result[currentKey] result = result[currentKey]
currentKey = keyPath.shift() currentKey = keyPath.shift()
} }
values[key] = result values[key] = result
} }
return values return values
} }
function getType (val) { function getType (val) {
return Object.prototype.toString.call(val).slice(8, -1).toLowerCase() return Object.prototype.toString.call(val).slice(8, -1).toLowerCase()
} }
function hasOwn (obj, key) { function hasOwn (obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key) return Object.prototype.hasOwnProperty.call(obj, key)
} }
function isValidString (val) { function isValidString (val) {
return val && getType(val) === 'string' return val && getType(val) === 'string'
} }
function isPlainObject (obj) { function isPlainObject (obj) {
return getType(obj) === 'object' return getType(obj) === 'object'
} }
function isFn (fn) { function isFn (fn) {
// 务必注意AsyncFunction // 务必注意AsyncFunction
return typeof fn === 'function' return typeof fn === 'function'
} }
// 获取文件后缀,只添加几种图片类型供客服消息接口使用 // 获取文件后缀,只添加几种图片类型供客服消息接口使用
const mime2ext = { const mime2ext = {
'image/png': 'png', 'image/png': 'png',
'image/jpeg': 'jpg', 'image/jpeg': 'jpg',
'image/gif': 'gif', 'image/gif': 'gif',
'image/svg+xml': 'svg', 'image/svg+xml': 'svg',
'image/bmp': 'bmp', 'image/bmp': 'bmp',
'image/webp': 'webp' 'image/webp': 'webp'
} }
function getExtension (contentType) { function getExtension (contentType) {
return mime2ext[contentType] return mime2ext[contentType]
} }
const isSnakeCase = /_(\w)/g const isSnakeCase = /_(\w)/g
const isCamelCase = /[A-Z]/g const isCamelCase = /[A-Z]/g
function snake2camel (value) { function snake2camel (value) {
return value.replace(isSnakeCase, (_, c) => (c ? c.toUpperCase() : '')) return value.replace(isSnakeCase, (_, c) => (c ? c.toUpperCase() : ''))
} }
function camel2snake (value) { function camel2snake (value) {
return value.replace(isCamelCase, str => '_' + str.toLowerCase()) return value.replace(isCamelCase, str => '_' + str.toLowerCase())
} }
function parseObjectKeys (obj, type) { function parseObjectKeys (obj, type) {
let parserReg, parser let parserReg, parser
switch (type) { switch (type) {
case 'snake2camel': case 'snake2camel':
parser = snake2camel parser = snake2camel
parserReg = isSnakeCase parserReg = isSnakeCase
break break
case 'camel2snake': case 'camel2snake':
parser = camel2snake parser = camel2snake
parserReg = isCamelCase parserReg = isCamelCase
break break
} }
for (const key in obj) { for (const key in obj) {
if (hasOwn(obj, key)) { if (hasOwn(obj, key)) {
if (parserReg.test(key)) { if (parserReg.test(key)) {
const keyCopy = parser(key) const keyCopy = parser(key)
obj[keyCopy] = obj[key] obj[keyCopy] = obj[key]
delete obj[key] delete obj[key]
if (isPlainObject(obj[keyCopy])) { if (isPlainObject(obj[keyCopy])) {
obj[keyCopy] = parseObjectKeys(obj[keyCopy], type) obj[keyCopy] = parseObjectKeys(obj[keyCopy], type)
} else if (Array.isArray(obj[keyCopy])) { } else if (Array.isArray(obj[keyCopy])) {
obj[keyCopy] = obj[keyCopy].map((item) => { obj[keyCopy] = obj[keyCopy].map((item) => {
return parseObjectKeys(item, type) return parseObjectKeys(item, type)
}) })
} }
} }
} }
} }
return obj return obj
} }
function snake2camelJson (obj) { function snake2camelJson (obj) {
return parseObjectKeys(obj, 'snake2camel') return parseObjectKeys(obj, 'snake2camel')
} }
function camel2snakeJson (obj) { function camel2snakeJson (obj) {
return parseObjectKeys(obj, 'camel2snake') return parseObjectKeys(obj, 'camel2snake')
} }
function getOffsetDate (offset) { function getOffsetDate (offset) {
return new Date( return new Date(
Date.now() + (new Date().getTimezoneOffset() + (offset || 0) * 60) * 60000 Date.now() + (new Date().getTimezoneOffset() + (offset || 0) * 60) * 60000
) )
} }
function getDateStr (date, separator = '-') { function getDateStr (date, separator = '-') {
date = date || new Date() date = date || new Date()
const dateArr = [] const dateArr = []
dateArr.push(date.getFullYear()) dateArr.push(date.getFullYear())
dateArr.push(('00' + (date.getMonth() + 1)).substr(-2)) dateArr.push(('00' + (date.getMonth() + 1)).substr(-2))
dateArr.push(('00' + date.getDate()).substr(-2)) dateArr.push(('00' + date.getDate()).substr(-2))
return dateArr.join(separator) return dateArr.join(separator)
} }
function getTimeStr (date, separator = ':') { function getTimeStr (date, separator = ':') {
date = date || new Date() date = date || new Date()
const timeArr = [] const timeArr = []
timeArr.push(('00' + date.getHours()).substr(-2)) timeArr.push(('00' + date.getHours()).substr(-2))
timeArr.push(('00' + date.getMinutes()).substr(-2)) timeArr.push(('00' + date.getMinutes()).substr(-2))
timeArr.push(('00' + date.getSeconds()).substr(-2)) timeArr.push(('00' + date.getSeconds()).substr(-2))
return timeArr.join(separator) return timeArr.join(separator)
} }
function getFullTimeStr (date) { function getFullTimeStr (date) {
date = date || new Date() date = date || new Date()
return getDateStr(date) + ' ' + getTimeStr(date) return getDateStr(date) + ' ' + getTimeStr(date)
} }
function getDistinctArray (arr) { function getDistinctArray (arr) {
return Array.from(new Set(arr)) return Array.from(new Set(arr))
} }
/** /**
* 拼接url * 拼接url
* @param {string} base 基础路径 * @param {string} base 基础路径
* @param {string} path 在基础路径上拼接的路径 * @param {string} path 在基础路径上拼接的路径
* @returns * @returns
*/ */
function resolveUrl (base, path) { function resolveUrl (base, path) {
if (/^https?:/.test(path)) { if (/^https?:/.test(path)) {
return path return path
} }
return base + path return base + path
} }
function getVerifyCode (len = 6) { function getVerifyCode (len = 6) {
let code = '' let code = ''
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
code += Math.floor(Math.random() * 10) code += Math.floor(Math.random() * 10)
} }
return code return code
} }
function coverMobile (mobile) { function coverMobile (mobile) {
if (typeof mobile !== 'string') { if (typeof mobile !== 'string') {
return mobile return mobile
} }
return mobile.slice(0, 3) + '****' + mobile.slice(7) return mobile.slice(0, 3) + '****' + mobile.slice(7)
} }
function getNonceStr (length = 16) { function getNonceStr (length = 16) {
let str = '' let str = ''
while (str.length < length) { while (str.length < length) {
str += Math.random().toString(32).substring(2) str += Math.random().toString(32).substring(2)
} }
return str.substring(0, length) return str.substring(0, length)
} }
try { try {
require('lodash.merge') require('lodash.merge')
} catch (error) { } catch (error) {
console.error('uni-id-co缺少依赖,请在uniCloud/cloudfunctions/uni-id-co目录执行 npm install 安装依赖') console.error('uni-id-co缺少依赖,请在uniCloud/cloudfunctions/uni-id-co目录执行 npm install 安装依赖')
throw error throw error
} }
function isMatchUserApp (userAppList, matchAppList) { function isMatchUserApp (userAppList, matchAppList) {
if (userAppList === undefined || userAppList === null) { if (userAppList === undefined || userAppList === null) {
return true return true
} }
if (getType(userAppList) !== 'array') { if (getType(userAppList) !== 'array') {
return false return false
} }
if (userAppList.includes('*')) { if (userAppList.includes('*')) {
return true return true
} }
if (getType(matchAppList) === 'string') { if (getType(matchAppList) === 'string') {
matchAppList = [matchAppList] matchAppList = [matchAppList]
} }
return userAppList.some(item => matchAppList.includes(item)) return userAppList.some(item => matchAppList.includes(item))
} }
module.exports = { module.exports = {
getType, getType,
isValidString, isValidString,
batchFindObjctValue, batchFindObjctValue,
isPlainObject, isPlainObject,
isFn, isFn,
getDistinctArray, getDistinctArray,
getFullTimeStr, getFullTimeStr,
resolveUrl, resolveUrl,
getOffsetDate, getOffsetDate,
camel2snakeJson, camel2snakeJson,
snake2camelJson, snake2camelJson,
getExtension, getExtension,
getVerifyCode, getVerifyCode,
coverMobile, coverMobile,
getNonceStr, getNonceStr,
isMatchUserApp isMatchUserApp
} }
// 各接口权限配置,未配置接口表示允许任何用户访问(包括未登录用户) // 各接口权限配置,未配置接口表示允许任何用户访问(包括未登录用户)
module.exports = { module.exports = {
// 管理接口 // 管理接口
addUser: { addUser: {
// auth: true // 已登录用户方可操作,配置角色或权限时此项可不写 // auth: true // 已登录用户方可操作,配置角色或权限时此项可不写
role: ['admin'] // 允许进行此操作的角色,包含任一角色均可操作。 role: ['admin'] // 允许进行此操作的角色,包含任一角色均可操作。
// permission: [] // 允许进行此操作的权限,包含任一权限均可操作。 // permission: [] // 允许进行此操作的权限,包含任一权限均可操作。
// 权限角色均配置时,用户拥有任一权限或任一角色均可操作 // 权限角色均配置时,用户拥有任一权限或任一角色均可操作
}, },
updateUser: { updateUser: {
role: ['admin'] role: ['admin']
}, },
authorizeAppLogin: { authorizeAppLogin: {
role: ['admin'] role: ['admin']
}, },
removeAuthorizedApp: { removeAuthorizedApp: {
role: ['admin'] role: ['admin']
}, },
setAuthorizedApp: { setAuthorizedApp: {
role: ['admin'] role: ['admin']
}, },
// 用户接口 // 用户接口
closeAccount: { closeAccount: {
auth: true auth: true
}, },
updatePwd: { updatePwd: {
auth: true auth: true
}, },
logout: { logout: {
auth: true auth: true
}, },
bindMobileBySms: { bindMobileBySms: {
auth: true auth: true
}, },
bindMobileByUniverify: { bindMobileByUniverify: {
auth: true auth: true
}, },
bindMobileByMpWeixin: { bindMobileByMpWeixin: {
auth: true auth: true
}, },
bindAlipay: { bindAlipay: {
auth: true auth: true
}, },
bindApple: { bindApple: {
auth: true auth: true
}, },
bindQQ: { bindQQ: {
auth: true auth: true
}, },
bindWeixin: { bindWeixin: {
auth: true auth: true
}, },
acceptInvite: { acceptInvite: {
auth: true auth: true
}, },
getInvitedUser: { getInvitedUser: {
auth: true auth: true
}, },
setPushCid: { setPushCid: {
auth: true auth: true
}, },
getAccountInfo: { getAccountInfo: {
auth: true auth: true
}, },
unbindWeixin: { unbindWeixin: {
auth: true auth: true
}, },
unbindAlipay: { unbindAlipay: {
auth: true auth: true
}, },
unbindQQ: { unbindQQ: {
auth: true auth: true
}, },
unbindApple: { unbindApple: {
auth: true auth: true
}, },
setPwd: { setPwd: {
auth: true auth: true
} }
} }
const word = { const word = {
login: 'login', login: 'login',
'verify-mobile': 'verify phone number' 'verify-mobile': 'verify phone number'
} }
const sentence = { const sentence = {
'uni-id-account-exists': 'Account exists', 'uni-id-account-exists': 'Account exists',
'uni-id-account-not-exists': 'Account does not exists', 'uni-id-account-not-exists': 'Account does not exists',
'uni-id-account-not-exists-in-current-app': 'Account does not exists in current app', 'uni-id-account-not-exists-in-current-app': 'Account does not exists in current app',
'uni-id-account-conflict': 'User account conflict', 'uni-id-account-conflict': 'User account conflict',
'uni-id-account-banned': 'Account has been banned', 'uni-id-account-banned': 'Account has been banned',
'uni-id-account-auditing': 'Account audit in progress', 'uni-id-account-auditing': 'Account audit in progress',
'uni-id-account-audit-failed': 'Account audit failed', 'uni-id-account-audit-failed': 'Account audit failed',
'uni-id-account-closed': 'Account has been closed', 'uni-id-account-closed': 'Account has been closed',
'uni-id-captcha-required': 'Captcha required', 'uni-id-captcha-required': 'Captcha required',
'uni-id-password-error': 'Password error', 'uni-id-password-error': 'Password error',
'uni-id-password-error-exceed-limit': 'The number of password errors is excessive', 'uni-id-password-error-exceed-limit': 'The number of password errors is excessive',
'uni-id-invalid-username': 'Invalid username', 'uni-id-invalid-username': 'Invalid username',
'uni-id-invalid-password': 'invalid password', 'uni-id-invalid-password': 'invalid password',
'uni-id-invalid-password-super': 'Passwords must have 8-16 characters and contain uppercase letters, lowercase letters, numbers, and symbols.', 'uni-id-invalid-password-super': 'Passwords must have 8-16 characters and contain uppercase letters, lowercase letters, numbers, and symbols.',
'uni-id-invalid-password-strong': 'Passwords must have 8-16 characters and contain letters, numbers and symbols.', 'uni-id-invalid-password-strong': 'Passwords must have 8-16 characters and contain letters, numbers and symbols.',
'uni-id-invalid-password-medium': 'Passwords must have 8-16 characters and contain at least two of the following: letters, numbers, and symbols.', 'uni-id-invalid-password-medium': 'Passwords must have 8-16 characters and contain at least two of the following: letters, numbers, and symbols.',
'uni-id-invalid-password-weak': 'Passwords must have 6-16 characters and contain letters and numbers.', 'uni-id-invalid-password-weak': 'Passwords must have 6-16 characters and contain letters and numbers.',
'uni-id-invalid-mobile': 'Invalid mobile phone number', 'uni-id-invalid-mobile': 'Invalid mobile phone number',
'uni-id-invalid-email': 'Invalid email address', 'uni-id-invalid-email': 'Invalid email address',
'uni-id-invalid-nickname': 'Invalid nickname', 'uni-id-invalid-nickname': 'Invalid nickname',
'uni-id-invalid-param': 'Invalid parameter', 'uni-id-invalid-param': 'Invalid parameter',
'uni-id-param-required': 'Parameter required: {param}', 'uni-id-param-required': 'Parameter required: {param}',
'uni-id-get-third-party-account-failed': 'Get third party account failed', 'uni-id-get-third-party-account-failed': 'Get third party account failed',
'uni-id-get-third-party-user-info-failed': 'Get third party user info failed', 'uni-id-get-third-party-user-info-failed': 'Get third party user info failed',
'uni-id-mobile-verify-code-error': 'Verify code error or expired', 'uni-id-mobile-verify-code-error': 'Verify code error or expired',
'uni-id-email-verify-code-error': 'Verify code error or expired', 'uni-id-email-verify-code-error': 'Verify code error or expired',
'uni-id-admin-exists': 'Administrator exists', 'uni-id-admin-exists': 'Administrator exists',
'uni-id-permission-error': 'Permission denied', 'uni-id-permission-error': 'Permission denied',
'uni-id-system-error': 'System error', 'uni-id-system-error': 'System error',
'uni-id-set-invite-code-failed': 'Set invite code failed', 'uni-id-set-invite-code-failed': 'Set invite code failed',
'uni-id-invalid-invite-code': 'Invalid invite code', 'uni-id-invalid-invite-code': 'Invalid invite code',
'uni-id-change-inviter-forbidden': 'Change inviter is not allowed', 'uni-id-change-inviter-forbidden': 'Change inviter is not allowed',
'uni-id-bind-conflict': 'This account has been bound', 'uni-id-bind-conflict': 'This account has been bound',
'uni-id-admin-exist-in-other-apps': 'Administrator is registered in other consoles', 'uni-id-admin-exist-in-other-apps': 'Administrator is registered in other consoles',
'uni-id-unbind-failed': 'Please bind first and then unbind', 'uni-id-unbind-failed': 'Please bind first and then unbind',
'uni-id-unbind-not-supported': 'Unbinding is not supported', 'uni-id-unbind-not-supported': 'Unbinding is not supported',
'uni-id-unbind-mobile-not-exists': 'This is the only way to login at the moment, please bind your phone number and then try to unbind', 'uni-id-unbind-mobile-not-exists': 'This is the only way to login at the moment, please bind your phone number and then try to unbind',
'uni-id-unbind-password-not-exists': 'Please set a password first', 'uni-id-unbind-password-not-exists': 'Please set a password first',
'uni-id-unsupported-request': 'Unsupported request', 'uni-id-unsupported-request': 'Unsupported request',
'uni-id-illegal-request': 'Illegal request' 'uni-id-illegal-request': 'Illegal request'
} }
module.exports = { module.exports = {
...word, ...word,
...sentence ...sentence
} }
let lang = { let lang = {
'zh-Hans': require('./zh-hans'), 'zh-Hans': require('./zh-hans'),
en: require('./en') en: require('./en')
} }
function mergeLanguage (lang1, lang2) { function mergeLanguage (lang1, lang2) {
const localeList = Object.keys(lang1) const localeList = Object.keys(lang1)
localeList.push(...Object.keys(lang2)) localeList.push(...Object.keys(lang2))
const result = {} const result = {}
for (let i = 0; i < localeList.length; i++) { for (let i = 0; i < localeList.length; i++) {
const locale = localeList[i] const locale = localeList[i]
result[locale] = Object.assign({}, lang1[locale], lang2[locale]) result[locale] = Object.assign({}, lang1[locale], lang2[locale])
} }
return result return result
} }
try { try {
const langPath = require.resolve('uni-config-center/uni-id/lang/index.js') const langPath = require.resolve('uni-config-center/uni-id/lang/index.js')
lang = mergeLanguage(lang, require(langPath)) lang = mergeLanguage(lang, require(langPath))
} catch (error) { } } catch (error) { }
module.exports = lang module.exports = lang
const word = { const word = {
login: '登录', login: '登录',
'verify-mobile': '验证手机号' 'verify-mobile': '验证手机号'
} }
const sentence = { const sentence = {
'uni-id-account-exists': '此账号已注册', 'uni-id-account-exists': '此账号已注册',
'uni-id-account-not-exists': '此账号未注册', 'uni-id-account-not-exists': '此账号未注册',
'uni-id-account-not-exists-in-current-app': '此账号未在该应用注册', 'uni-id-account-not-exists-in-current-app': '此账号未在该应用注册',
'uni-id-account-conflict': '用户账号冲突', 'uni-id-account-conflict': '用户账号冲突',
'uni-id-account-banned': '此账号已封禁', 'uni-id-account-banned': '此账号已封禁',
'uni-id-account-auditing': '此账号正在审核中', 'uni-id-account-auditing': '此账号正在审核中',
'uni-id-account-audit-failed': '此账号审核失败', 'uni-id-account-audit-failed': '此账号审核失败',
'uni-id-account-closed': '此账号已注销', 'uni-id-account-closed': '此账号已注销',
'uni-id-captcha-required': '请输入图形验证码', 'uni-id-captcha-required': '请输入图形验证码',
'uni-id-password-error': '密码错误', 'uni-id-password-error': '密码错误',
'uni-id-password-error-exceed-limit': '密码错误次数过多,请稍后再试', 'uni-id-password-error-exceed-limit': '密码错误次数过多,请稍后再试',
'uni-id-invalid-username': '用户名不合法', 'uni-id-invalid-username': '用户名不合法',
'uni-id-invalid-password': '密码不合法', 'uni-id-invalid-password': '密码不合法',
'uni-id-invalid-password-super': '密码必须包含大小写字母、数字和特殊符号,长度8-16位', 'uni-id-invalid-password-super': '密码必须包含大小写字母、数字和特殊符号,长度8-16位',
'uni-id-invalid-password-strong': '密码必须包含字母、数字和特殊符号,长度8-16位不合法', 'uni-id-invalid-password-strong': '密码必须包含字母、数字和特殊符号,长度8-16位不合法',
'uni-id-invalid-password-medium': '密码必须为字母、数字和特殊符号任意两种的组合,长度8-16位', 'uni-id-invalid-password-medium': '密码必须为字母、数字和特殊符号任意两种的组合,长度8-16位',
'uni-id-invalid-password-weak': '密码必须包含字母和数字,长度6-16位', 'uni-id-invalid-password-weak': '密码必须包含字母和数字,长度6-16位',
'uni-id-invalid-mobile': '手机号码不合法', 'uni-id-invalid-mobile': '手机号码不合法',
'uni-id-invalid-email': '邮箱不合法', 'uni-id-invalid-email': '邮箱不合法',
'uni-id-invalid-nickname': '昵称不合法', 'uni-id-invalid-nickname': '昵称不合法',
'uni-id-invalid-param': '参数错误', 'uni-id-invalid-param': '参数错误',
'uni-id-param-required': '缺少参数: {param}', 'uni-id-param-required': '缺少参数: {param}',
'uni-id-get-third-party-account-failed': '获取第三方账号失败', 'uni-id-get-third-party-account-failed': '获取第三方账号失败',
'uni-id-get-third-party-user-info-failed': '获取用户信息失败', 'uni-id-get-third-party-user-info-failed': '获取用户信息失败',
'uni-id-mobile-verify-code-error': '手机验证码错误或已过期', 'uni-id-mobile-verify-code-error': '手机验证码错误或已过期',
'uni-id-email-verify-code-error': '邮箱验证码错误或已过期', 'uni-id-email-verify-code-error': '邮箱验证码错误或已过期',
'uni-id-admin-exists': '超级管理员已存在', 'uni-id-admin-exists': '超级管理员已存在',
'uni-id-permission-error': '权限错误', 'uni-id-permission-error': '权限错误',
'uni-id-system-error': '系统错误', 'uni-id-system-error': '系统错误',
'uni-id-set-invite-code-failed': '设置邀请码失败', 'uni-id-set-invite-code-failed': '设置邀请码失败',
'uni-id-invalid-invite-code': '邀请码不可用', 'uni-id-invalid-invite-code': '邀请码不可用',
'uni-id-change-inviter-forbidden': '禁止修改邀请人', 'uni-id-change-inviter-forbidden': '禁止修改邀请人',
'uni-id-bind-conflict': '此账号已被绑定', 'uni-id-bind-conflict': '此账号已被绑定',
'uni-id-admin-exist-in-other-apps': '超级管理员已在其他控制台注册', 'uni-id-admin-exist-in-other-apps': '超级管理员已在其他控制台注册',
'uni-id-unbind-failed': '请先绑定后再解绑', 'uni-id-unbind-failed': '请先绑定后再解绑',
'uni-id-unbind-not-supported': '不支持解绑', 'uni-id-unbind-not-supported': '不支持解绑',
'uni-id-unbind-mobile-not-exists': '这是当前唯一登录方式,请绑定手机号后再尝试解绑', 'uni-id-unbind-mobile-not-exists': '这是当前唯一登录方式,请绑定手机号后再尝试解绑',
'uni-id-unbind-password-not-exists': '请先设置密码在尝试解绑', 'uni-id-unbind-password-not-exists': '请先设置密码在尝试解绑',
'uni-id-unsupported-request': '不支持的请求方式', 'uni-id-unsupported-request': '不支持的请求方式',
'uni-id-illegal-request': '非法请求' 'uni-id-illegal-request': '非法请求'
} }
module.exports = { module.exports = {
...word, ...word,
...sentence ...sentence
} }
# 说明 # 说明
此目录内为uni-id-co基础能力,不建议直接修改。如果你发现有些逻辑加入会更好,或者此部分代码有Bug可以向我们提交PR,仓库地址:[]()。如果有特殊的需求也可以在[论坛](https://ask.dcloud.net.cn/)提出,我们可以讨论下如何实现。 此目录内为uni-id-co基础能力,不建议直接修改。如果你发现有些逻辑加入会更好,或者此部分代码有Bug可以向我们提交PR,仓库地址:[]()。如果有特殊的需求也可以在[论坛](https://ask.dcloud.net.cn/)提出,我们可以讨论下如何实现。
\ No newline at end of file
const AlipayBase = require('../alipayBase') const AlipayBase = require('../alipayBase')
const protocols = require('./protocols') const protocols = require('./protocols')
module.exports = class Auth extends AlipayBase { module.exports = class Auth extends AlipayBase {
constructor (options) { constructor (options) {
super(options) super(options)
this._protocols = protocols this._protocols = protocols
} }
async code2Session (code) { async code2Session (code) {
const result = await this._exec('alipay.system.oauth.token', { const result = await this._exec('alipay.system.oauth.token', {
grantType: 'authorization_code', grantType: 'authorization_code',
code code
}) })
return result return result
} }
} }
module.exports = { module.exports = {
code2Session: { code2Session: {
// args (fromArgs) { // args (fromArgs) {
// return fromArgs // return fromArgs
// }, // },
returnValue: { returnValue: {
openid: 'userId' openid: 'userId'
} }
} }
} }
const { const {
camel2snakeJson, camel2snakeJson,
snake2camelJson, snake2camelJson,
getOffsetDate, getOffsetDate,
getFullTimeStr getFullTimeStr
} = require('../../../common/utils') } = require('../../../common/utils')
const crypto = require('crypto') const crypto = require('crypto')
const ALIPAY_ALGORITHM_MAPPING = { const ALIPAY_ALGORITHM_MAPPING = {
RSA: 'RSA-SHA1', RSA: 'RSA-SHA1',
RSA2: 'RSA-SHA256' RSA2: 'RSA-SHA256'
} }
module.exports = class AlipayBase { module.exports = class AlipayBase {
constructor (options = {}) { constructor (options = {}) {
if (!options.appId) throw new Error('appId required') if (!options.appId) throw new Error('appId required')
if (!options.privateKey) throw new Error('privateKey required') if (!options.privateKey) throw new Error('privateKey required')
const defaultOptions = { const defaultOptions = {
gateway: 'https://openapi.alipay.com/gateway.do', gateway: 'https://openapi.alipay.com/gateway.do',
timeout: 5000, timeout: 5000,
charset: 'utf-8', charset: 'utf-8',
version: '1.0', version: '1.0',
signType: 'RSA2', signType: 'RSA2',
timeOffset: -new Date().getTimezoneOffset() / 60, timeOffset: -new Date().getTimezoneOffset() / 60,
keyType: 'PKCS8' keyType: 'PKCS8'
} }
if (options.sandbox) { if (options.sandbox) {
options.gateway = 'https://openapi.alipaydev.com/gateway.do' options.gateway = 'https://openapi.alipaydev.com/gateway.do'
} }
this.options = Object.assign({}, defaultOptions, options) this.options = Object.assign({}, defaultOptions, options)
const privateKeyType = const privateKeyType =
this.options.keyType === 'PKCS8' ? 'PRIVATE KEY' : 'RSA PRIVATE KEY' this.options.keyType === 'PKCS8' ? 'PRIVATE KEY' : 'RSA PRIVATE KEY'
this.options.privateKey = this._formatKey( this.options.privateKey = this._formatKey(
this.options.privateKey, this.options.privateKey,
privateKeyType privateKeyType
) )
if (this.options.alipayPublicKey) { if (this.options.alipayPublicKey) {
this.options.alipayPublicKey = this._formatKey( this.options.alipayPublicKey = this._formatKey(
this.options.alipayPublicKey, this.options.alipayPublicKey,
'PUBLIC KEY' 'PUBLIC KEY'
) )
} }
} }
_formatKey (key, type) { _formatKey (key, type) {
return `-----BEGIN ${type}-----\n${key}\n-----END ${type}-----` return `-----BEGIN ${type}-----\n${key}\n-----END ${type}-----`
} }
_formatUrl (url, params) { _formatUrl (url, params) {
let requestUrl = url let requestUrl = url
// 需要放在 url 中的参数列表 // 需要放在 url 中的参数列表
const urlArgs = [ const urlArgs = [
'app_id', 'app_id',
'method', 'method',
'format', 'format',
'charset', 'charset',
'sign_type', 'sign_type',
'sign', 'sign',
'timestamp', 'timestamp',
'version', 'version',
'notify_url', 'notify_url',
'return_url', 'return_url',
'auth_token', 'auth_token',
'app_auth_token' 'app_auth_token'
] ]
for (const key in params) { for (const key in params) {
if (urlArgs.indexOf(key) > -1) { if (urlArgs.indexOf(key) > -1) {
const val = encodeURIComponent(params[key]) const val = encodeURIComponent(params[key])
requestUrl = `${requestUrl}${requestUrl.includes('?') ? '&' : '?' requestUrl = `${requestUrl}${requestUrl.includes('?') ? '&' : '?'
}${key}=${val}` }${key}=${val}`
// 删除 postData 中对应的数据 // 删除 postData 中对应的数据
delete params[key] delete params[key]
} }
} }
return { execParams: params, url: requestUrl } return { execParams: params, url: requestUrl }
} }
_getSign (method, params) { _getSign (method, params) {
const bizContent = params.bizContent || null const bizContent = params.bizContent || null
delete params.bizContent delete params.bizContent
const signParams = Object.assign({ const signParams = Object.assign({
method, method,
appId: this.options.appId, appId: this.options.appId,
charset: this.options.charset, charset: this.options.charset,
version: this.options.version, version: this.options.version,
signType: this.options.signType, signType: this.options.signType,
timestamp: getFullTimeStr(getOffsetDate(this.options.timeOffset)) timestamp: getFullTimeStr(getOffsetDate(this.options.timeOffset))
}, params) }, params)
if (bizContent) { if (bizContent) {
signParams.bizContent = JSON.stringify(camel2snakeJson(bizContent)) signParams.bizContent = JSON.stringify(camel2snakeJson(bizContent))
} }
// params key 驼峰转下划线 // params key 驼峰转下划线
const decamelizeParams = camel2snakeJson(signParams) const decamelizeParams = camel2snakeJson(signParams)
// 排序 // 排序
const signStr = Object.keys(decamelizeParams) const signStr = Object.keys(decamelizeParams)
.sort() .sort()
.map((key) => { .map((key) => {
let data = decamelizeParams[key] let data = decamelizeParams[key]
if (Array.prototype.toString.call(data) !== '[object String]') { if (Array.prototype.toString.call(data) !== '[object String]') {
data = JSON.stringify(data) data = JSON.stringify(data)
} }
return `${key}=${data}` return `${key}=${data}`
}) })
.join('&') .join('&')
// 计算签名 // 计算签名
const sign = crypto const sign = crypto
.createSign(ALIPAY_ALGORITHM_MAPPING[this.options.signType]) .createSign(ALIPAY_ALGORITHM_MAPPING[this.options.signType])
.update(signStr, 'utf8') .update(signStr, 'utf8')
.sign(this.options.privateKey, 'base64') .sign(this.options.privateKey, 'base64')
return Object.assign(decamelizeParams, { sign }) return Object.assign(decamelizeParams, { sign })
} }
async _exec (method, params = {}, option = {}) { async _exec (method, params = {}, option = {}) {
// 计算签名 // 计算签名
const signData = this._getSign(method, params) const signData = this._getSign(method, params)
const { url, execParams } = this._formatUrl(this.options.gateway, signData) const { url, execParams } = this._formatUrl(this.options.gateway, signData)
const { status, data } = await uniCloud.httpclient.request(url, { const { status, data } = await uniCloud.httpclient.request(url, {
method: 'POST', method: 'POST',
data: execParams, data: execParams,
// 按 text 返回(为了验签) // 按 text 返回(为了验签)
dataType: 'text', dataType: 'text',
timeout: this.options.timeout timeout: this.options.timeout
}) })
if (status !== 200) throw new Error('request fail') if (status !== 200) throw new Error('request fail')
/** /**
* 示例响应格式 * 示例响应格式
* {"alipay_trade_precreate_response": * {"alipay_trade_precreate_response":
* {"code": "10000","msg": "Success","out_trade_no": "111111","qr_code": "https:\/\/"}, * {"code": "10000","msg": "Success","out_trade_no": "111111","qr_code": "https:\/\/"},
* "sign": "abcde=" * "sign": "abcde="
* } * }
* 或者 * 或者
* {"error_response": * {"error_response":
* {"code":"40002","msg":"Invalid Arguments","sub_code":"isv.code-invalid","sub_msg":"授权码code无效"}, * {"code":"40002","msg":"Invalid Arguments","sub_code":"isv.code-invalid","sub_msg":"授权码code无效"},
* } * }
*/ */
const result = JSON.parse(data) const result = JSON.parse(data)
const responseKey = `${method.replace(/\./g, '_')}_response` const responseKey = `${method.replace(/\./g, '_')}_response`
const response = result[responseKey] const response = result[responseKey]
const errorResponse = result.error_response const errorResponse = result.error_response
if (response) { if (response) {
// 按字符串验签 // 按字符串验签
const validateSuccess = option.validateSign ? this._checkResponseSign(data, responseKey) : true const validateSuccess = option.validateSign ? this._checkResponseSign(data, responseKey) : true
if (validateSuccess) { if (validateSuccess) {
if (!response.code || response.code === '10000') { if (!response.code || response.code === '10000') {
const errCode = 0 const errCode = 0
const errMsg = response.msg || '' const errMsg = response.msg || ''
return { return {
errCode, errCode,
errMsg, errMsg,
...snake2camelJson(response) ...snake2camelJson(response)
} }
} }
const msg = response.sub_code ? `${response.sub_code} ${response.sub_msg}` : `${response.msg || 'unkonwn error'}` const msg = response.sub_code ? `${response.sub_code} ${response.sub_msg}` : `${response.msg || 'unkonwn error'}`
throw new Error(msg) throw new Error(msg)
} else { } else {
throw new Error('check sign error') throw new Error('check sign error')
} }
} else if (errorResponse) { } else if (errorResponse) {
throw new Error(errorResponse.sub_msg || errorResponse.msg || 'request fail') throw new Error(errorResponse.sub_msg || errorResponse.msg || 'request fail')
} }
throw new Error('request fail') throw new Error('request fail')
} }
_checkResponseSign (signStr, responseKey) { _checkResponseSign (signStr, responseKey) {
if (!this.options.alipayPublicKey || this.options.alipayPublicKey === '') { if (!this.options.alipayPublicKey || this.options.alipayPublicKey === '') {
console.warn('options.alipayPublicKey is empty') console.warn('options.alipayPublicKey is empty')
// 支付宝公钥不存在时不做验签 // 支付宝公钥不存在时不做验签
return true return true
} }
// 带验签的参数不存在时返回失败 // 带验签的参数不存在时返回失败
if (!signStr) { return false } if (!signStr) { return false }
// 根据服务端返回的结果截取需要验签的目标字符串 // 根据服务端返回的结果截取需要验签的目标字符串
const validateStr = this._getSignStr(signStr, responseKey) const validateStr = this._getSignStr(signStr, responseKey)
// 服务端返回的签名 // 服务端返回的签名
const serverSign = JSON.parse(signStr).sign const serverSign = JSON.parse(signStr).sign
// 参数存在,并且是正常的结果(不包含 sub_code)时才验签 // 参数存在,并且是正常的结果(不包含 sub_code)时才验签
const verifier = crypto.createVerify(ALIPAY_ALGORITHM_MAPPING[this.options.signType]) const verifier = crypto.createVerify(ALIPAY_ALGORITHM_MAPPING[this.options.signType])
verifier.update(validateStr, 'utf8') verifier.update(validateStr, 'utf8')
return verifier.verify(this.options.alipayPublicKey, serverSign, 'base64') return verifier.verify(this.options.alipayPublicKey, serverSign, 'base64')
} }
_getSignStr (originStr, responseKey) { _getSignStr (originStr, responseKey) {
// 待签名的字符串 // 待签名的字符串
let validateStr = originStr.trim() let validateStr = originStr.trim()
// 找到 xxx_response 开始的位置 // 找到 xxx_response 开始的位置
const startIndex = originStr.indexOf(`${responseKey}"`) const startIndex = originStr.indexOf(`${responseKey}"`)
// 找到最后一个 “"sign"” 字符串的位置(避免) // 找到最后一个 “"sign"” 字符串的位置(避免)
const lastIndex = originStr.lastIndexOf('"sign"') const lastIndex = originStr.lastIndexOf('"sign"')
/** /**
* 删除 xxx_response 及之前的字符串 * 删除 xxx_response 及之前的字符串
* 假设原始字符串为 * 假设原始字符串为
* {"xxx_response":{"code":"10000"},"sign":"jumSvxTKwn24G5sAIN"} * {"xxx_response":{"code":"10000"},"sign":"jumSvxTKwn24G5sAIN"}
* 删除后变为 * 删除后变为
* :{"code":"10000"},"sign":"jumSvxTKwn24G5sAIN"} * :{"code":"10000"},"sign":"jumSvxTKwn24G5sAIN"}
*/ */
validateStr = validateStr.substr(startIndex + responseKey.length + 1) validateStr = validateStr.substr(startIndex + responseKey.length + 1)
/** /**
* 删除最后一个 "sign" 及之后的字符串 * 删除最后一个 "sign" 及之后的字符串
* 删除后变为 * 删除后变为
* :{"code":"10000"}, * :{"code":"10000"},
* {} 之间就是待验签的字符串 * {} 之间就是待验签的字符串
*/ */
validateStr = validateStr.substr(0, lastIndex) validateStr = validateStr.substr(0, lastIndex)
// 删除第一个 { 之前的任何字符 // 删除第一个 { 之前的任何字符
validateStr = validateStr.replace(/^[^{]*{/g, '{') validateStr = validateStr.replace(/^[^{]*{/g, '{')
// 删除最后一个 } 之后的任何字符 // 删除最后一个 } 之后的任何字符
validateStr = validateStr.replace(/\}([^}]*)$/g, '}') validateStr = validateStr.replace(/\}([^}]*)$/g, '}')
return validateStr return validateStr
} }
} }
const rsaPublicKeyPem = require('../rsa-public-key-pem') const rsaPublicKeyPem = require('../rsa-public-key-pem')
let authKeysCache = null let authKeysCache = null
module.exports = class Auth { module.exports = class Auth {
constructor (options) { constructor (options) {
this.options = Object.assign({ this.options = Object.assign({
baseUrl: 'https://appleid.apple.com', baseUrl: 'https://appleid.apple.com',
timeout: 10000 timeout: 10000
}, options) }, options)
} }
async _fetch (url, options) { async _fetch (url, options) {
const { baseUrl } = this.options const { baseUrl } = this.options
return uniCloud.httpclient.request(baseUrl + url, options) return uniCloud.httpclient.request(baseUrl + url, options)
} }
async verifyIdentityToken (identityToken) { async verifyIdentityToken (identityToken) {
// 解密出kid,拿取key // 解密出kid,拿取key
const jwtHeader = identityToken.split('.')[0] const jwtHeader = identityToken.split('.')[0]
const { kid } = JSON.parse(Buffer.from(jwtHeader, 'base64').toString()) const { kid } = JSON.parse(Buffer.from(jwtHeader, 'base64').toString())
let authKeys let authKeys
if (authKeysCache) { if (authKeysCache) {
authKeys = authKeysCache authKeys = authKeysCache
} else { } else {
authKeys = await this.getAuthKeys() authKeys = await this.getAuthKeys()
authKeysCache = authKeys authKeysCache = authKeys
} }
const usedKey = authKeys.find(item => item.kid === kid) const usedKey = authKeys.find(item => item.kid === kid)
/** /**
* identityToken 格式 * identityToken 格式
* *
* { * {
* iss: 'https://appleid.apple.com', * iss: 'https://appleid.apple.com',
* aud: 'io.dcloud.hellouniapp', * aud: 'io.dcloud.hellouniapp',
* exp: 1610626724, * exp: 1610626724,
* iat: 1610540324, * iat: 1610540324,
* sub: '000628.30119d332d9b45a3be4a297f9391fd5c.0403', * sub: '000628.30119d332d9b45a3be4a297f9391fd5c.0403',
* c_hash: 'oFfgewoG36cJX00KUbj45A', * c_hash: 'oFfgewoG36cJX00KUbj45A',
* email: 'x2awmap99s@privaterelay.appleid.com', * email: 'x2awmap99s@privaterelay.appleid.com',
* email_verified: 'true', * email_verified: 'true',
* is_private_email: 'true', * is_private_email: 'true',
* auth_time: 1610540324, * auth_time: 1610540324,
* nonce_supported: true * nonce_supported: true
* } * }
*/ */
const payload = require('jsonwebtoken').verify( const payload = require('jsonwebtoken').verify(
identityToken, identityToken,
rsaPublicKeyPem(usedKey.n, usedKey.e), rsaPublicKeyPem(usedKey.n, usedKey.e),
{ {
algorithms: usedKey.alg algorithms: usedKey.alg
} }
) )
if (payload.iss !== 'https://appleid.apple.com' || payload.aud !== this.options.bundleId) { if (payload.iss !== 'https://appleid.apple.com' || payload.aud !== this.options.bundleId) {
throw new Error('Invalid identity token') throw new Error('Invalid identity token')
} }
return { return {
openid: payload.sub, openid: payload.sub,
email: payload.email, email: payload.email,
emailVerified: payload.email_verified === 'true', emailVerified: payload.email_verified === 'true',
isPrivateEmail: payload.is_private_email === 'true' isPrivateEmail: payload.is_private_email === 'true'
} }
} }
async getAuthKeys () { async getAuthKeys () {
const { status, data } = await this._fetch('/auth/keys', { const { status, data } = await this._fetch('/auth/keys', {
method: 'GET', method: 'GET',
dataType: 'json', dataType: 'json',
timeout: this.options.timeout timeout: this.options.timeout
}) })
if (status !== 200) throw new Error('request https://appleid.apple.com/auth/keys fail') if (status !== 200) throw new Error('request https://appleid.apple.com/auth/keys fail')
return data.keys return data.keys
} }
} }
// http://stackoverflow.com/questions/18835132/xml-to-pem-in-node-js // http://stackoverflow.com/questions/18835132/xml-to-pem-in-node-js
/* eslint-disable camelcase */ /* eslint-disable camelcase */
function rsaPublicKeyPem (modulus_b64, exponent_b64) { function rsaPublicKeyPem (modulus_b64, exponent_b64) {
const modulus = Buffer.from(modulus_b64, 'base64') const modulus = Buffer.from(modulus_b64, 'base64')
const exponent = Buffer.from(exponent_b64, 'base64') const exponent = Buffer.from(exponent_b64, 'base64')
let modulus_hex = modulus.toString('hex') let modulus_hex = modulus.toString('hex')
let exponent_hex = exponent.toString('hex') let exponent_hex = exponent.toString('hex')
modulus_hex = prepadSigned(modulus_hex) modulus_hex = prepadSigned(modulus_hex)
exponent_hex = prepadSigned(exponent_hex) exponent_hex = prepadSigned(exponent_hex)
const modlen = modulus_hex.length / 2 const modlen = modulus_hex.length / 2
const explen = exponent_hex.length / 2 const explen = exponent_hex.length / 2
const encoded_modlen = encodeLengthHex(modlen) const encoded_modlen = encodeLengthHex(modlen)
const encoded_explen = encodeLengthHex(explen) const encoded_explen = encodeLengthHex(explen)
const encoded_pubkey = '30' + const encoded_pubkey = '30' +
encodeLengthHex( encodeLengthHex(
modlen + modlen +
explen + explen +
encoded_modlen.length / 2 + encoded_modlen.length / 2 +
encoded_explen.length / 2 + 2 encoded_explen.length / 2 + 2
) + ) +
'02' + encoded_modlen + modulus_hex + '02' + encoded_modlen + modulus_hex +
'02' + encoded_explen + exponent_hex '02' + encoded_explen + exponent_hex
const der_b64 = Buffer.from(encoded_pubkey, 'hex').toString('base64') const der_b64 = Buffer.from(encoded_pubkey, 'hex').toString('base64')
const pem = '-----BEGIN RSA PUBLIC KEY-----\n' + const pem = '-----BEGIN RSA PUBLIC KEY-----\n' +
der_b64.match(/.{1,64}/g).join('\n') + der_b64.match(/.{1,64}/g).join('\n') +
'\n-----END RSA PUBLIC KEY-----\n' '\n-----END RSA PUBLIC KEY-----\n'
return pem return pem
} }
function prepadSigned (hexStr) { function prepadSigned (hexStr) {
const msb = hexStr[0] const msb = hexStr[0]
if (msb < '0' || msb > '7') { if (msb < '0' || msb > '7') {
return '00' + hexStr return '00' + hexStr
} else { } else {
return hexStr return hexStr
} }
} }
function toHex (number) { function toHex (number) {
const nstr = number.toString(16) const nstr = number.toString(16)
if (nstr.length % 2) return '0' + nstr if (nstr.length % 2) return '0' + nstr
return nstr return nstr
} }
// encode ASN.1 DER length field // encode ASN.1 DER length field
// if <=127, short form // if <=127, short form
// if >=128, long form // if >=128, long form
function encodeLengthHex (n) { function encodeLengthHex (n) {
if (n <= 127) return toHex(n) if (n <= 127) return toHex(n)
else { else {
const n_hex = toHex(n) const n_hex = toHex(n)
const length_of_length_byte = 128 + n_hex.length / 2 // 0x80+numbytes const length_of_length_byte = 128 + n_hex.length / 2 // 0x80+numbytes
return toHex(length_of_length_byte) + n_hex return toHex(length_of_length_byte) + n_hex
} }
} }
module.exports = rsaPublicKeyPem module.exports = rsaPublicKeyPem
const WxAccount = require('./weixin/account/index') const WxAccount = require('./weixin/account/index')
const QQAccount = require('./qq/account/index') const QQAccount = require('./qq/account/index')
const AliAccount = require('./alipay/account/index') const AliAccount = require('./alipay/account/index')
const AppleAccount = require('./apple/account/index') const AppleAccount = require('./apple/account/index')
const createApi = require('./share/create-api') const createApi = require('./share/create-api')
module.exports = { module.exports = {
initWeixin: function () { initWeixin: function () {
const oauthConfig = this.configUtils.getOauthConfig({ provider: 'weixin' }) const oauthConfig = this.configUtils.getOauthConfig({ provider: 'weixin' })
return createApi(WxAccount, { return createApi(WxAccount, {
appId: oauthConfig.appid, appId: oauthConfig.appid,
secret: oauthConfig.appsecret secret: oauthConfig.appsecret
}) })
}, },
initQQ: function () { initQQ: function () {
const oauthConfig = this.configUtils.getOauthConfig({ provider: 'qq' }) const oauthConfig = this.configUtils.getOauthConfig({ provider: 'qq' })
return createApi(QQAccount, { return createApi(QQAccount, {
appId: oauthConfig.appid, appId: oauthConfig.appid,
secret: oauthConfig.appsecret secret: oauthConfig.appsecret
}) })
}, },
initAlipay: function () { initAlipay: function () {
const oauthConfig = this.configUtils.getOauthConfig({ provider: 'alipay' }) const oauthConfig = this.configUtils.getOauthConfig({ provider: 'alipay' })
return createApi(AliAccount, { return createApi(AliAccount, {
appId: oauthConfig.appid, appId: oauthConfig.appid,
privateKey: oauthConfig.privateKey privateKey: oauthConfig.privateKey
}) })
}, },
initApple: function () { initApple: function () {
const oauthConfig = this.configUtils.getOauthConfig({ provider: 'apple' }) const oauthConfig = this.configUtils.getOauthConfig({ provider: 'apple' })
return createApi(AppleAccount, { return createApi(AppleAccount, {
bundleId: oauthConfig.bundleId bundleId: oauthConfig.bundleId
}) })
} }
} }
const { const {
UniCloudError UniCloudError
} = require('../../../../common/error') } = require('../../../../common/error')
const { const {
resolveUrl resolveUrl
} = require('../../../../common/utils') } = require('../../../../common/utils')
const { const {
callQQOpenApi callQQOpenApi
} = require('../normalize') } = require('../normalize')
module.exports = class Auth { module.exports = class Auth {
constructor (options) { constructor (options) {
this.options = Object.assign({ this.options = Object.assign({
baseUrl: 'https://graph.qq.com', baseUrl: 'https://graph.qq.com',
timeout: 5000 timeout: 5000
}, options) }, options)
} }
async _requestQQOpenapi ({ name, url, data, options }) { async _requestQQOpenapi ({ name, url, data, options }) {
const defaultOptions = { const defaultOptions = {
method: 'GET', method: 'GET',
dataType: 'json', dataType: 'json',
dataAsQueryString: true, dataAsQueryString: true,
timeout: this.options.timeout timeout: this.options.timeout
} }
const result = await callQQOpenApi({ const result = await callQQOpenApi({
name: `auth.${name}`, name: `auth.${name}`,
url: resolveUrl(this.options.baseUrl, url), url: resolveUrl(this.options.baseUrl, url),
data, data,
options, options,
defaultOptions defaultOptions
}) })
return result return result
} }
async getUserInfo ({ async getUserInfo ({
accessToken, accessToken,
openid openid
} = {}) { } = {}) {
const url = '/user/get_user_info' const url = '/user/get_user_info'
const result = await this._requestQQOpenapi({ const result = await this._requestQQOpenapi({
name: 'getUserInfo', name: 'getUserInfo',
url, url,
data: { data: {
oauthConsumerKey: this.options.appId, oauthConsumerKey: this.options.appId,
accessToken, accessToken,
openid openid
} }
}) })
return { return {
nickname: result.nickname, nickname: result.nickname,
avatar: result.figureurl_qq_1 avatar: result.figureurl_qq_1
} }
} }
async getOpenidByToken ({ async getOpenidByToken ({
accessToken accessToken
} = {}) { } = {}) {
const url = '/oauth2.0/me' const url = '/oauth2.0/me'
const result = await this._requestQQOpenapi({ const result = await this._requestQQOpenapi({
name: 'getOpenidByToken', name: 'getOpenidByToken',
url, url,
data: { data: {
accessToken, accessToken,
unionid: 1, unionid: 1,
fmt: 'json' fmt: 'json'
} }
}) })
if (result.clientId !== this.options.appId) { if (result.clientId !== this.options.appId) {
throw new UniCloudError({ throw new UniCloudError({
code: 'APPID_NOT_MATCH', code: 'APPID_NOT_MATCH',
message: 'appid not match' message: 'appid not match'
}) })
} }
return { return {
openid: result.openid, openid: result.openid,
unionid: result.unionid unionid: result.unionid
} }
} }
async code2Session ({ async code2Session ({
code code
} = {}) { } = {}) {
const url = 'https://api.q.qq.com/sns/jscode2session' const url = 'https://api.q.qq.com/sns/jscode2session'
const result = await this._requestQQOpenapi({ const result = await this._requestQQOpenapi({
name: 'getOpenidByToken', name: 'getOpenidByToken',
url, url,
data: { data: {
grant_type: 'authorization_code', grant_type: 'authorization_code',
appid: this.options.appId, appid: this.options.appId,
secret: this.options.secret, secret: this.options.secret,
js_code: code js_code: code
} }
}) })
return result return result
} }
} }
const { const {
UniCloudError UniCloudError
} = require('../../../common/error') } = require('../../../common/error')
const { const {
camel2snakeJson, camel2snakeJson,
snake2camelJson snake2camelJson
} = require('../../../common/utils') } = require('../../../common/utils')
function generateApiResult (apiName, data) { function generateApiResult (apiName, data) {
if (data.ret || data.error) { if (data.ret || data.error) {
// 这三种都是qq的错误码规范 // 这三种都是qq的错误码规范
const code = data.ret || data.error || data.errcode || -2 const code = data.ret || data.error || data.errcode || -2
const message = data.msg || data.error_description || data.errmsg || `${apiName} fail` const message = data.msg || data.error_description || data.errmsg || `${apiName} fail`
throw new UniCloudError({ throw new UniCloudError({
code, code,
message message
}) })
} else { } else {
delete data.ret delete data.ret
delete data.msg delete data.msg
delete data.error delete data.error
delete data.error_description delete data.error_description
delete data.errcode delete data.errcode
delete data.errmsg delete data.errmsg
return { return {
...data, ...data,
errMsg: `${apiName} ok`, errMsg: `${apiName} ok`,
errCode: 0 errCode: 0
} }
} }
} }
function nomalizeError (apiName, error) { function nomalizeError (apiName, error) {
throw new UniCloudError({ throw new UniCloudError({
code: error.code || -2, code: error.code || -2,
message: error.message || `${apiName} fail` message: error.message || `${apiName} fail`
}) })
} }
async function callQQOpenApi ({ async function callQQOpenApi ({
name, name,
url, url,
data, data,
options, options,
defaultOptions defaultOptions
}) { }) {
options = Object.assign({}, defaultOptions, options, { data: camel2snakeJson(Object.assign({}, data)) }) options = Object.assign({}, defaultOptions, options, { data: camel2snakeJson(Object.assign({}, data)) })
let result let result
try { try {
result = await uniCloud.httpclient.request(url, options) result = await uniCloud.httpclient.request(url, options)
} catch (e) { } catch (e) {
return nomalizeError(name, e) return nomalizeError(name, e)
} }
let resData = result.data let resData = result.data
const contentType = result.headers['content-type'] const contentType = result.headers['content-type']
if ( if (
Buffer.isBuffer(resData) && Buffer.isBuffer(resData) &&
(contentType.indexOf('text/plain') === 0 || (contentType.indexOf('text/plain') === 0 ||
contentType.indexOf('application/json') === 0) contentType.indexOf('application/json') === 0)
) { ) {
try { try {
resData = JSON.parse(resData.toString()) resData = JSON.parse(resData.toString())
} catch (e) { } catch (e) {
resData = resData.toString() resData = resData.toString()
} }
} else if (Buffer.isBuffer(resData)) { } else if (Buffer.isBuffer(resData)) {
resData = { resData = {
buffer: resData, buffer: resData,
contentType contentType
} }
} }
return snake2camelJson( return snake2camelJson(
generateApiResult( generateApiResult(
name, name,
resData || { resData || {
errCode: -2, errCode: -2,
errMsg: 'Request failed' errMsg: 'Request failed'
} }
) )
) )
} }
module.exports = { module.exports = {
callQQOpenApi callQQOpenApi
} }
const { const {
isFn, isFn,
isPlainObject isPlainObject
} = require('../../../common/utils') } = require('../../../common/utils')
// 注意:不进行递归处理 // 注意:不进行递归处理
function parseParams (params = {}, rule) { function parseParams (params = {}, rule) {
if (!rule || !params) { if (!rule || !params) {
return params return params
} }
const internalKeys = ['_pre', '_purify', '_post'] const internalKeys = ['_pre', '_purify', '_post']
// 转换之前的处理 // 转换之前的处理
if (rule._pre) { if (rule._pre) {
params = rule._pre(params) params = rule._pre(params)
} }
// 净化参数 // 净化参数
let purify = { shouldDelete: new Set([]) } let purify = { shouldDelete: new Set([]) }
if (rule._purify) { if (rule._purify) {
const _purify = rule._purify const _purify = rule._purify
for (const purifyKey in _purify) { for (const purifyKey in _purify) {
_purify[purifyKey] = new Set(_purify[purifyKey]) _purify[purifyKey] = new Set(_purify[purifyKey])
} }
purify = Object.assign(purify, _purify) purify = Object.assign(purify, _purify)
} }
if (isPlainObject(rule)) { if (isPlainObject(rule)) {
for (const key in rule) { for (const key in rule) {
const parser = rule[key] const parser = rule[key]
if (isFn(parser) && internalKeys.indexOf(key) === -1) { if (isFn(parser) && internalKeys.indexOf(key) === -1) {
params[key] = parser(params) params[key] = parser(params)
} else if (typeof parser === 'string' && internalKeys.indexOf(key) === -1) { } else if (typeof parser === 'string' && internalKeys.indexOf(key) === -1) {
// 直接转换属性名称的删除旧属性名 // 直接转换属性名称的删除旧属性名
params[key] = params[parser] params[key] = params[parser]
purify.shouldDelete.add(parser) purify.shouldDelete.add(parser)
} }
} }
} else if (isFn(rule)) { } else if (isFn(rule)) {
params = rule(params) params = rule(params)
} }
if (purify.shouldDelete) { if (purify.shouldDelete) {
for (const item of purify.shouldDelete) { for (const item of purify.shouldDelete) {
delete params[item] delete params[item]
} }
} }
// 转换之后的处理 // 转换之后的处理
if (rule._post) { if (rule._post) {
params = rule._post(params) params = rule._post(params)
} }
return params return params
} }
function createApi (ApiClass, options) { function createApi (ApiClass, options) {
const apiInstance = new ApiClass(options) const apiInstance = new ApiClass(options)
return new Proxy(apiInstance, { return new Proxy(apiInstance, {
get: function (obj, prop) { get: function (obj, prop) {
if (typeof obj[prop] === 'function' && prop.indexOf('_') !== 0 && obj._protocols && obj._protocols[prop]) { if (typeof obj[prop] === 'function' && prop.indexOf('_') !== 0 && obj._protocols && obj._protocols[prop]) {
const protocol = obj._protocols[prop] const protocol = obj._protocols[prop]
return async function (params) { return async function (params) {
params = parseParams(params, protocol.args) params = parseParams(params, protocol.args)
let result = await obj[prop](params) let result = await obj[prop](params)
result = parseParams(result, protocol.returnValue) result = parseParams(result, protocol.returnValue)
return result return result
} }
} else { } else {
return obj[prop] return obj[prop]
} }
} }
}) })
} }
module.exports = createApi module.exports = createApi
const { const {
callWxOpenApi, callWxOpenApi,
buildUrl buildUrl
} = require('../normalize') } = require('../normalize')
module.exports = class Auth { module.exports = class Auth {
constructor (options) { constructor (options) {
this.options = Object.assign({ this.options = Object.assign({
baseUrl: 'https://api.weixin.qq.com', baseUrl: 'https://api.weixin.qq.com',
timeout: 5000 timeout: 5000
}, options) }, options)
} }
async _requestWxOpenapi ({ name, url, data, options }) { async _requestWxOpenapi ({ name, url, data, options }) {
const defaultOptions = { const defaultOptions = {
method: 'GET', method: 'GET',
dataType: 'json', dataType: 'json',
dataAsQueryString: true, dataAsQueryString: true,
timeout: this.options.timeout timeout: this.options.timeout
} }
const result = await callWxOpenApi({ const result = await callWxOpenApi({
name: `auth.${name}`, name: `auth.${name}`,
url: `${this.options.baseUrl}${buildUrl(url, data)}`, url: `${this.options.baseUrl}${buildUrl(url, data)}`,
data, data,
options, options,
defaultOptions defaultOptions
}) })
return result return result
} }
async code2Session (code) { async code2Session (code) {
const url = '/sns/jscode2session' const url = '/sns/jscode2session'
const result = await this._requestWxOpenapi({ const result = await this._requestWxOpenapi({
name: 'code2Session', name: 'code2Session',
url, url,
data: { data: {
grant_type: 'authorization_code', grant_type: 'authorization_code',
appid: this.options.appId, appid: this.options.appId,
secret: this.options.secret, secret: this.options.secret,
js_code: code js_code: code
} }
}) })
return result return result
} }
async getOauthAccessToken (code) { async getOauthAccessToken (code) {
const url = '/sns/oauth2/access_token' const url = '/sns/oauth2/access_token'
const result = await this._requestWxOpenapi({ const result = await this._requestWxOpenapi({
name: 'getOauthAccessToken', name: 'getOauthAccessToken',
url, url,
data: { data: {
grant_type: 'authorization_code', grant_type: 'authorization_code',
appid: this.options.appId, appid: this.options.appId,
secret: this.options.secret, secret: this.options.secret,
code code
} }
}) })
if (result.expiresIn) { if (result.expiresIn) {
result.expired = Date.now() + result.expiresIn * 1000 result.expired = Date.now() + result.expiresIn * 1000
// delete result.expiresIn // delete result.expiresIn
} }
return result return result
} }
async getUserInfo ({ async getUserInfo ({
accessToken, accessToken,
openid openid
} = {}) { } = {}) {
const url = '/sns/userinfo' const url = '/sns/userinfo'
const { const {
nickname, nickname,
headimgurl: avatar headimgurl: avatar
} = await this._requestWxOpenapi({ } = await this._requestWxOpenapi({
name: 'getUserInfo', name: 'getUserInfo',
url, url,
data: { data: {
accessToken, accessToken,
openid, openid,
appid: this.options.appId, appid: this.options.appId,
secret: this.options.secret, secret: this.options.secret,
scope: 'snsapi_userinfo' scope: 'snsapi_userinfo'
} }
}) })
return { return {
nickname, nickname,
avatar avatar
} }
} }
async getPhoneNumber (accessToken, code) { async getPhoneNumber (accessToken, code) {
const url = `/wxa/business/getuserphonenumber?access_token=${accessToken}` const url = `/wxa/business/getuserphonenumber?access_token=${accessToken}`
const { phoneInfo } = await this._requestWxOpenapi({ const { phoneInfo } = await this._requestWxOpenapi({
name: 'getPhoneNumber', name: 'getPhoneNumber',
url, url,
data: { data: {
code code
}, },
options: { options: {
method: 'POST', method: 'POST',
dataAsQueryString: false, dataAsQueryString: false,
headers: { headers: {
'content-type': 'application/json' 'content-type': 'application/json'
} }
} }
}) })
return { return {
purePhoneNumber: phoneInfo.purePhoneNumber purePhoneNumber: phoneInfo.purePhoneNumber
} }
} }
} }
let redisEnable = null let redisEnable = null
function getRedisEnable() { function getRedisEnable() {
// 未用到的时候不调用redis接口,节省一些连接数 // 未用到的时候不调用redis接口,节省一些连接数
if (redisEnable !== null) { if (redisEnable !== null) {
return redisEnable return redisEnable
} }
try { try {
uniCloud.redis() uniCloud.redis()
redisEnable = true redisEnable = true
} catch (error) { } catch (error) {
redisEnable = false redisEnable = false
} }
return redisEnable return redisEnable
} }
module.exports = { module.exports = {
getRedisEnable getRedisEnable
} }
\ No newline at end of file
module.exports = { module.exports = {
addUser: require('./add-user'), addUser: require('./add-user'),
updateUser: require('./update-user') updateUser: require('./update-user')
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册