Compare commits

...

10 Commits

Author SHA1 Message Date
CounterFire2023
21caa93461 邮件使用aws的ses 2024-05-20 15:05:40 +08:00
CounterFire2023
ad77e4cf3d 增加mailgun发送接口 2024-05-16 10:13:53 +08:00
CounterFire2023
62ccb927b2 修改邮件结果返回格式 2024-05-15 14:12:43 +08:00
zhl
42df8debb0 添加企业微信相关接口 2023-04-06 10:11:13 +08:00
zhl
5e56a91bd3 change message form of workflow notify 2023-04-05 18:03:20 +08:00
zhl
c8926d99be change sth 2023-04-05 18:01:32 +08:00
zhl
4b1ad8511d change sth 2023-04-05 17:56:09 +08:00
zhl
3551e5084c 增加xml格式的支持 2023-04-05 17:51:41 +08:00
zhl
6948958cf3 add post of workflow notify 2023-04-05 17:42:53 +08:00
zhl
f494a89f5c add 企业微信通知测试接口 2023-04-05 16:41:08 +08:00
8 changed files with 1156 additions and 13 deletions

View File

@ -13,13 +13,18 @@
"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"
}, },

View File

@ -37,6 +37,7 @@ 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)

View File

@ -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 } return { msgId: result.messageId, messageId: result.messageId }
} }
} }

View File

@ -0,0 +1,36 @@
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')
}
}

View File

@ -1,27 +1,61 @@
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, })
debug: false, this.transporter = createTransport({
} 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 }
}
} }

View File

@ -0,0 +1,53 @@
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`
}
}

View File

@ -0,0 +1,59 @@
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()
}

957
yarn.lock

File diff suppressed because it is too large Load Diff