Compare commits
No commits in common. "workflow" and "main" have entirely different histories.
@ -13,18 +13,13 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-ses": "^3.577.0",
|
|
||||||
"@fastify/cors": "^8.2.0",
|
"@fastify/cors": "^8.2.0",
|
||||||
"@fastify/formbody": "^7.4.0",
|
"@fastify/formbody": "^7.4.0",
|
||||||
"@fastify/helmet": "^10.1.0",
|
"@fastify/helmet": "^10.1.0",
|
||||||
"@fastify/jwt": "^6.7.0",
|
"@fastify/jwt": "^6.7.0",
|
||||||
"@wecom/crypto": "^1.0.1",
|
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"fastify": "^4.14.1",
|
"fastify": "^4.14.1",
|
||||||
"fastify-plugin": "^4.5.0",
|
"fastify-plugin": "^4.5.0",
|
||||||
"fastify-xml-body-parser": "^2.2.0",
|
|
||||||
"form-data": "^4.0.0",
|
|
||||||
"mailgun.js": "^10.2.1",
|
|
||||||
"nodemailer": "^6.9.1",
|
"nodemailer": "^6.9.1",
|
||||||
"tracer": "^1.1.6"
|
"tracer": "^1.1.6"
|
||||||
},
|
},
|
||||||
|
@ -37,7 +37,6 @@ export class ApiServer {
|
|||||||
|
|
||||||
private registerPlugins() {
|
private registerPlugins() {
|
||||||
this.server.register(require('@fastify/formbody'))
|
this.server.register(require('@fastify/formbody'))
|
||||||
this.server.register(require('fastify-xml-body-parser'))
|
|
||||||
this.server.register(zReqParserPlugin)
|
this.server.register(zReqParserPlugin)
|
||||||
this.server.register(helmet, { hidePoweredBy: false })
|
this.server.register(helmet, { hidePoweredBy: false })
|
||||||
this.server.register(zTokenParserPlugin)
|
this.server.register(zTokenParserPlugin)
|
||||||
|
@ -12,6 +12,6 @@ class MailController extends BaseController {
|
|||||||
throw new ZError(10, 'params mismatch')
|
throw new ZError(10, 'params mismatch')
|
||||||
}
|
}
|
||||||
const result = await new MailQueue().addTaskToQueue(message)
|
const result = await new MailQueue().addTaskToQueue(message)
|
||||||
return { msgId: result.messageId, messageId: result.messageId }
|
return { msgId: result.messageId }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
import BaseController from 'common/base.controller'
|
|
||||||
import { ZError } from 'common/ZError'
|
|
||||||
import { role, router } from 'decorators/router'
|
|
||||||
import { getSignature, decrypt } from '@wecom/crypto'
|
|
||||||
|
|
||||||
class WorkFlowController extends BaseController {
|
|
||||||
@role('anon')
|
|
||||||
@router('get /workflow/notify')
|
|
||||||
async wxNotifyCheck(req, res) {
|
|
||||||
const token = process.env.WX_TOKEN
|
|
||||||
const aesKey = process.env.WX_AES_KEY
|
|
||||||
let { msg_signature, timestamp, nonce, echostr } = req.params
|
|
||||||
const signature = getSignature(token, timestamp, nonce, echostr)
|
|
||||||
if (msg_signature !== signature) {
|
|
||||||
throw new ZError(10, 'sign check failed')
|
|
||||||
}
|
|
||||||
const { message, id } = decrypt(aesKey, echostr)
|
|
||||||
res.send(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
@role('anon')
|
|
||||||
@router('post /workflow/notify')
|
|
||||||
async flowNotify(req, res) {
|
|
||||||
let { msg_signature, timestamp, nonce, xml } = req.params
|
|
||||||
const token = process.env.WX_TOKEN
|
|
||||||
const aesKey = process.env.WX_AES_KEY
|
|
||||||
const signature = getSignature(token, timestamp, nonce, xml.Encrypt)
|
|
||||||
if (msg_signature !== signature) {
|
|
||||||
throw new ZError(10, 'sign check failed')
|
|
||||||
}
|
|
||||||
const { message, id } = decrypt(aesKey, xml.Encrypt)
|
|
||||||
console.log(id)
|
|
||||||
console.log(message)
|
|
||||||
res.send('success')
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +1,27 @@
|
|||||||
import { singleton } from 'decorators/singleton'
|
import { singleton } from 'decorators/singleton'
|
||||||
import { createTransport, Transporter } from 'nodemailer'
|
import { createTransport, Transporter } from 'nodemailer'
|
||||||
import Mail from 'nodemailer/lib/mailer'
|
import Mail from 'nodemailer/lib/mailer'
|
||||||
import FormData from 'form-data'
|
|
||||||
import Mailgun, { InputFormData } from 'mailgun.js'
|
|
||||||
import { ZError } from 'common/ZError'
|
|
||||||
import * as aws from '@aws-sdk/client-ses'
|
|
||||||
|
|
||||||
@singleton
|
@singleton
|
||||||
export class MailService {
|
export class MailService {
|
||||||
private transporter: Transporter
|
private transporter: Transporter
|
||||||
private mailClient: any
|
|
||||||
private awsClient: any
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// const options = {
|
const options = {
|
||||||
// host: process.env.MAIL_SMTP_HOST,
|
host: process.env.MAIL_SMTP_HOST,
|
||||||
// secure: true,
|
secure: true,
|
||||||
// auth: {
|
auth: {
|
||||||
// user: process.env.MAIL_SMTP_USER,
|
user: process.env.MAIL_SMTP_USER,
|
||||||
// pass: process.env.MAIL_SMTP_PASS,
|
pass: process.env.MAIL_SMTP_PASS,
|
||||||
// },
|
|
||||||
// logger: true,
|
|
||||||
// debug: false,
|
|
||||||
// }
|
|
||||||
// // @ts-ignore
|
|
||||||
// this.transporter = createTransport(options, {})
|
|
||||||
|
|
||||||
const mailgun = new Mailgun(FormData)
|
|
||||||
this.mailClient = mailgun.client({ username: 'api', key: process.env.MAILGUN_API_KEY })
|
|
||||||
const ses = new aws.SES({
|
|
||||||
region: 'ap-southeast-1', // Your region will need to be updated
|
|
||||||
credentials: {
|
|
||||||
accessKeyId: process.env.AWS_ACCESS_KEY,
|
|
||||||
secretAccessKey: process.env.AWS_SECRET_KEY,
|
|
||||||
},
|
},
|
||||||
})
|
logger: true,
|
||||||
this.transporter = createTransport({
|
debug: false,
|
||||||
SES: { ses, aws },
|
}
|
||||||
})
|
// @ts-ignore
|
||||||
|
this.transporter = createTransport(options, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async send(message: Mail.Options) {
|
public async send(message: Mail.Options) {
|
||||||
await this.transporter.verify()
|
await this.transporter.verify()
|
||||||
return this.transporter.sendMail(message)
|
return this.transporter.sendMail(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendMailgun(message: Mail.Options) {
|
|
||||||
const domain = 'counterfire.games'
|
|
||||||
const sendResult = await this.mailClient.messages.create(domain, {
|
|
||||||
from: message.from,
|
|
||||||
to: message.to,
|
|
||||||
subject: message.subject,
|
|
||||||
html: message.html,
|
|
||||||
text: message.text,
|
|
||||||
})
|
|
||||||
if (sendResult.status !== 200) {
|
|
||||||
throw new ZError(20, sendResult.message)
|
|
||||||
}
|
|
||||||
return { messageId: sendResult.id }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
import { singleton } from 'decorators/singleton'
|
|
||||||
|
|
||||||
const WX_API_HOST = 'https://qyapi.weixin.qq.com'
|
|
||||||
@singleton
|
|
||||||
export class WechatWorkService {
|
|
||||||
private accessToken: string
|
|
||||||
private tokenExpire: number
|
|
||||||
private wxToken: string
|
|
||||||
private wxAesKey: string
|
|
||||||
private wxCorpId: string
|
|
||||||
private wxCorpSecret: string
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.wxToken = process.env.WX_TOKEN
|
|
||||||
this.wxAesKey = process.env.WX_AES_KEY
|
|
||||||
this.wxCorpId = process.env.WX_CORP_ID
|
|
||||||
this.wxCorpSecret = process.env.WX_CORP_SECRET
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取微信企业号的access_token
|
|
||||||
* https://developer.work.weixin.qq.com/resource/devtool
|
|
||||||
*/
|
|
||||||
public async refreshAccessToken() {
|
|
||||||
const url = `${WX_API_HOST}/cgi-bin/gettoken`
|
|
||||||
// use axios get url
|
|
||||||
let config = {
|
|
||||||
method: 'get',
|
|
||||||
maxBodyLength: Infinity,
|
|
||||||
url,
|
|
||||||
}
|
|
||||||
let response = await axios.request(config).then(response => {
|
|
||||||
return response.data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 获取审批申请详情
|
|
||||||
* https://developer.work.weixin.qq.com/devtool/interface/alone?id=18615
|
|
||||||
* @param spNo 审批单号
|
|
||||||
*/
|
|
||||||
public async fetchApprovalDetail(spNo: string) {
|
|
||||||
const url = `${WX_API_HOST}/cgi-bin/oa/getapprovaldetail`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据media_id获取文件
|
|
||||||
* https://developer.work.weixin.qq.com/devtool/interface/alone?id=18615
|
|
||||||
* @param mediaId
|
|
||||||
*/
|
|
||||||
public async fetchFile(mediaId: string) {
|
|
||||||
const url = `${WX_API_HOST}/cgi-bin/media/get`
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
import crypto from 'crypto'
|
|
||||||
const ENCODER = 'base64'
|
|
||||||
const REG_KEY = /^[0-9a-fA-F]{63,64}$/
|
|
||||||
|
|
||||||
export function isEncrypt(msg: string) {
|
|
||||||
return !REG_KEY.test(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function aesEncrypt(text: string, password: string, iv: string) {
|
|
||||||
var md5 = crypto.createHash('md5')
|
|
||||||
const key = md5.update(password).digest('hex')
|
|
||||||
let cipher = crypto.createCipheriv('aes-256-cbc', key, iv)
|
|
||||||
let encrypted = cipher.update(text, 'utf8', ENCODER)
|
|
||||||
encrypted += cipher.final(ENCODER)
|
|
||||||
return encrypted
|
|
||||||
}
|
|
||||||
|
|
||||||
export function aesDecrypt(encryptedText: string, password: string, iv: string) {
|
|
||||||
var md5 = crypto.createHash('md5')
|
|
||||||
const key = md5.update(password).digest('hex')
|
|
||||||
let decipher = crypto.createDecipheriv('aes-256-cbc', key, iv)
|
|
||||||
let decrypted = decipher.update(encryptedText, ENCODER, 'utf8')
|
|
||||||
return decrypted + decipher.final('utf8')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function sha512(password: string, salt: string) {
|
|
||||||
let hash = crypto.createHmac('sha512', salt)
|
|
||||||
hash.update(password)
|
|
||||||
let value = hash.digest('hex')
|
|
||||||
return {
|
|
||||||
salt: salt,
|
|
||||||
passwordHash: value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function genRandomString(length: number) {
|
|
||||||
return crypto
|
|
||||||
.randomBytes(Math.ceil(length / 2))
|
|
||||||
.toString('hex')
|
|
||||||
.slice(0, length)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function uuid() {
|
|
||||||
return crypto.randomUUID()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function md5(content: string) {
|
|
||||||
var md5 = crypto.createHash('md5')
|
|
||||||
return md5.update(content).digest('hex')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function sha1(content: string) {
|
|
||||||
var md5 = crypto.createHash('sha1')
|
|
||||||
return md5.update(content).digest('hex')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function base64Decode(str: string) {
|
|
||||||
return Buffer.from(str, ENCODER).toString()
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user