🎉 now you can push message to wechat

This commit is contained in:
Song
2021-01-17 16:45:52 +08:00
parent 0e9c7d9086
commit d36680aea1
7 changed files with 142 additions and 51 deletions
+5 -6
View File
@@ -7,16 +7,15 @@ async function initializeTokenStore() {
users.forEach((user) => { users.forEach((user) => {
if (user.wechatAppId) { if (user.wechatAppId) {
tokenStore.set(user.prefix, { tokenStore.set(user.prefix, {
appId: user.wechatAppId, wechatAppId: user.wechatAppId,
appSecret: user.wechatAppSecret, wechatAppSecret: user.wechatAppSecret,
templateId: user.wechatTemplateId, wechatTemplateId: user.wechatTemplateId,
openId: user.wechatOpenId, wechatOpenId: user.wechatOpenId,
serverVerifyToken: user.wechatVerifyToken, wechatVerifyToken: user.wechatVerifyToken,
token: '', token: '',
}); });
} }
}); });
console.debug(tokenStore);
console.log('Token store initialized.'); console.log('Token store initialized.');
} }
+10 -5
View File
@@ -3,8 +3,9 @@ const { tokenStore } = require('./token');
const config = require('../config'); const config = require('../config');
async function refreshToken() { async function refreshToken() {
for (const item of tokenStore) { for (let [key, value] of tokenStore) {
item.token = await this.requestToken(); value.token = await requestToken(value.wechatAppId, value.wechatAppSecret);
tokenStore.set(key, value);
} }
console.log('Token refreshed.'); console.log('Token refreshed.');
} }
@@ -15,9 +16,13 @@ async function requestToken(appId, appSecret) {
let res = await axios.get( let res = await axios.get(
`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appId}&secret=${appSecret}` `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appId}&secret=${appSecret}`
); );
console.log(res); // console.debug(res);
if (res && res.data && res.data.access_token) { if (res && res.data) {
token = res.data.access_token; if (res.data.access_token) {
token = res.data.access_token;
} else {
console.error(res.data);
}
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);
+10 -16
View File
@@ -3,36 +3,30 @@ const config = require('../config');
exports.userRequired = (req, res, next) => { exports.userRequired = (req, res, next) => {
if (req.session.user) { if (req.session.user) {
if (req.session.user.isBlocked) { if (req.session.user.isBlocked) {
return res.render('message', { req.flash('message', '用户账户被禁用,请联系管理员');
isError: true, return res.redirect('/');
message: '用户账户被禁用,请联系管理员',
link: '/feedback',
});
} }
} else { } else {
return res.render('message', { req.flash('message', '用户尚未登录,请登录');
isError: false, return res.redirect('/login');
message: '用户尚未登录,请登录',
link: '/login',
});
} }
next(); next();
}; };
exports.adminRequired = (req, res, next) => { exports.adminRequired = (req, res, next) => {
if (!req.session.user || !req.session.user.isAdmin) { if (!req.session.user || !req.session.user.isAdmin) {
return res.render('message', { req.flash('message', '需要管理员权限');
isError: true, return res.redirect('/login');
message: '需要超级管理员权限',
});
} }
next(); next();
}; };
exports.allowRegister = (req, res, next) => { exports.allowRegister = (req, res, next) => {
if (!config.allowRegister) { if (!config.allowRegister) {
req.flash('message', '管理员未开放注册!'); if (!req.session.user || !req.session.user.isAdmin) {
return res.redirect('/'); req.flash('message', '管理员未开放注册!');
return res.redirect('/');
}
} }
next(); next();
}; };
+47 -12
View File
@@ -2,7 +2,12 @@ const express = require('express');
const router = express.Router(); const router = express.Router();
const { User } = require('../models'); const { User } = require('../models');
const { tokenStore } = require('../common/token'); const { tokenStore } = require('../common/token');
const { allowRegister } = require('../middlewares/web_auth'); const { requestToken } = require('../common/wechat');
const {
userRequired,
adminRequired,
allowRegister,
} = require('../middlewares/web_auth');
const config = require('../config'); const config = require('../config');
router.get('/', (req, res, next) => { router.get('/', (req, res, next) => {
@@ -12,7 +17,9 @@ router.get('/', (req, res, next) => {
}); });
router.get('/login', (req, res, next) => { router.get('/login', (req, res, next) => {
res.render('login'); res.render('login', {
message: req.flash('message'),
});
}); });
router.post('/login', async (req, res, next) => { router.post('/login', async (req, res, next) => {
@@ -45,7 +52,7 @@ router.post('/login', async (req, res, next) => {
}); });
}); });
router.get('/logout', (req, res, next) => { router.get('/logout', userRequired, (req, res, next) => {
req.session.user = undefined; req.session.user = undefined;
req.flash('message', '已退出登录'); req.flash('message', '已退出登录');
res.redirect('/'); res.redirect('/');
@@ -67,7 +74,13 @@ router.post('/register', allowRegister, async (req, res, next) => {
} }
}); });
router.post('/configure', async (req, res, next) => { router.get('/configure', userRequired, (req, res, next) => {
res.locals.message = req.flash('message');
res.render('configure', req.session.user);
});
router.post('/configure', userRequired, async (req, res, next) => {
let id = req.session.user.id;
let user = { let user = {
username: req.body.username, username: req.body.username,
password: req.body.password, password: req.body.password,
@@ -80,19 +93,41 @@ router.post('/configure', async (req, res, next) => {
wechatOpenId: req.body.wechatOpenId, wechatOpenId: req.body.wechatOpenId,
wechatVerifyToken: req.body.wechatVerifyToken, wechatVerifyToken: req.body.wechatVerifyToken,
}; };
for (let field in user) {
let value = user[field];
value = value.trim();
if (value) {
user[field] = value;
} else {
delete user[field];
}
}
let message = '';
try { try {
user = await User.create(user); let userObj = await User.findOne({
tokenStore.set(user.prefix, { where: {
appId: user.wechatAppId, id: id,
appSecret: user.wechatAppSecret, },
templateId: user.wechatTemplateId,
openId: user.wechatOpenId,
wechatVerifyToken: user.wechatVerifyToken,
token: '',
}); });
if (userObj) {
await userObj.update(user);
}
req.session.user = userObj;
tokenStore.set(userObj.prefix, {
appId: userObj.wechatAppId,
appSecret: userObj.wechatAppSecret,
templateId: userObj.wechatTemplateId,
openId: userObj.wechatOpenId,
wechatVerifyToken: userObj.wechatVerifyToken,
token: requestToken(userObj.wechatAppId, userObj.wechatAppSecret),
});
message = '配置更新成功';
} catch (e) { } catch (e) {
console.error(e); console.error(e);
message = e.message;
} }
req.flash('message', message);
res.redirect('/configure');
}); });
module.exports = router; module.exports = router;
+10 -4
View File
@@ -6,10 +6,6 @@ const { tokenStore } = require('../common/token');
const router = express.Router(); const router = express.Router();
router.get('/:userPrefix/configure', (req, res, next) => {
res.render('configure');
});
router.all('/:userPrefix/verify', (req, res, next) => { router.all('/:userPrefix/verify', (req, res, next) => {
// 验证消息来自微信服务器:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html // 验证消息来自微信服务器:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
const userPrefix = req.params.userPrefix; const userPrefix = req.params.userPrefix;
@@ -25,6 +21,16 @@ router.all('/:userPrefix/verify', (req, res, next) => {
} }
}); });
router.all('/:userPrefix/:description', async (req, res, next) => {
const userPrefix = req.params.userPrefix;
let message = {
type: '0',
title: '无标题',
description: req.params.description,
};
res.json(await processMessage(userPrefix, message));
});
router.all('/:userPrefix', async (req, res, next) => { router.all('/:userPrefix', async (req, res, next) => {
const userPrefix = req.params.userPrefix; const userPrefix = req.params.userPrefix;
let message = { let message = {
+58 -7
View File
@@ -3,48 +3,99 @@
<div class="normal-container"> <div class="normal-container">
<div> <div>
<h2 class="title">配置页面</h2> <h2 class="title">配置页面</h2>
<%- include('./partials/message') %>
<form action="/configure" method="post"> <form action="/configure" method="post">
<div class="field"> <div class="field">
<label class="label">APP ID</label> <label class="label">APP ID</label>
<div class="control"> <div class="control">
<input class="input" name="APP_ID" type="text" required placeholder="请输入 APP ID"> <input class="input" name="wechatAppId" value="<%- wechatAppId %>" type="text" required placeholder="请输入 APP ID">
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label class="label">APP SECRET</label> <label class="label">APP SECRET</label>
<div class="control"> <div class="control">
<input class="input" name="APP_SECRET" type="text" required placeholder="请输入 APP SECRET"> <input class="input" name="wechatAppSecret" value="<%- wechatAppSecret %>" type="text" required placeholder="请输入 APP SECRET">
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label class="label">OPEN ID</label> <label class="label">OPEN ID</label>
<div class="control"> <div class="control">
<input class="input" name="OPEN_ID" type="text" required placeholder="请输入你的 OPEN ID"> <input class="input" name="wechatOpenId" value="<%- wechatOpenId %>" type="text" required placeholder="请输入你的 OPEN ID">
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label class="label">TEMPLATE ID</label> <label class="label">TEMPLATE ID</label>
<div class="control"> <div class="control">
<input class="input" name="TEMPLATE_ID" type="text" required placeholder="请输入一个模板通知 ID"> <input class="input" name="wechatTemplateId" value="<%- wechatTemplateId %>" type="text" required placeholder="请输入一个模板通知 ID">
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label class="label">TOKEN</label> <label class="label">TOKEN</label>
<div class="control"> <div class="control">
<input class="input" name="TOKEN" type="text" required placeholder="请输入你的 TOKEN"> <input class="input" name="wechatVerifyToken" value="<%- wechatVerifyToken %>" type="text" required placeholder="请输入你的 TOKEN">
</div>
</div>
<div class="field">
<label class="label">前缀</label>
<article class="message">
<div class="message-body">
前缀默认和用户名相同,如非必要,无需修改
</div>
</article>
<div class="control">
<input class="input" name="prefix" value="<%- prefix %>" type="text" placeholder="请输入你的 TOKEN">
</div>
</div>
<div class="field">
<label class="label">ACCESS TOKEN</label>
<article class="message">
<div class="message-body">
Access Token 可防止未授权者利用本系统向你发送消息,留空则不做该检查
</div>
</article>
<div class="control">
<input class="input" name="accessToken" value="<%- accessToken %>" type="text" placeholder="请设置新的 ACCESS TOKEN(可不填)">
</div>
</div>
<div class="field">
<label class="label">邮箱</label>
<article class="message">
<div class="message-body">
后续版本将支持选择将消息发送到邮箱
</div>
</article>
<div class="control">
<input class="input" name="email" value="<%- email %>" type="text" placeholder="请输入你的邮箱(可不填)">
</div>
</div>
<div class="field">
<label class="label">用户名</label>
<div class="control">
<input class="input" name="username" value="<%- username %>" type="text" placeholder="请输入新的用户名">
</div>
</div>
<div class="field">
<label class="label">密码</label>
<div class="control">
<input class="input" name="password" type="text" placeholder="请输入新的密码">
</div> </div>
</div> </div>
<div class="field is-grouped is-grouped-right"> <div class="field is-grouped is-grouped-right">
<div class="control"> <div class="control">
<input type="submit" class="button is-link" value="提交"> <input type="submit" class="button is-light" value="提交">
</div> </div>
<div class="control"> <div class="control">
<input type="reset" class="button is-link is-light" value="重置"> <input type="reset" class="button is-light" value="重置">
</div> </div>
</div> </div>
</form> </form>
+2 -1
View File
@@ -15,13 +15,14 @@
<div id="mainNavbar" class="navbar-menu"> <div id="mainNavbar" class="navbar-menu">
<div class="navbar-start"> <div class="navbar-start">
<a class="navbar-item" href="/"> 首页 </a> <a class="navbar-item" href="/"> 首页 </a>
<a class="navbar-item" target="_blank" href="https://github.com/songquanpeng/message-pusher"> 帮助 </a> <a class="navbar-item" target="_blank" href="https://github.com/songquanpeng/message-pusher/blob/master/README.md"> 帮助 </a>
<a class="navbar-item" target="_blank" href="https://iamazing.cn/page/message-pusher"> 关于 </a> <a class="navbar-item" target="_blank" href="https://iamazing.cn/page/message-pusher"> 关于 </a>
</div> </div>
<div class="navbar-end"> <div class="navbar-end">
<div class="navbar-item"> <div class="navbar-item">
<div class="buttons"> <div class="buttons">
<% if (isLogged) { %> <% if (isLogged) { %>
<a class="button is-light" href="/configure">配置</a>
<a class="button is-light" href="/logout">退出</a> <a class="button is-light" href="/logout">退出</a>
<% } else { %> <% } else { %>
<a class="button is-light" href="/register">注册</a> <a class="button is-light" href="/register">注册</a>