Explorar o código

:tada: now you can push message to wechat

Song %!s(int64=4) %!d(string=hai) anos
pai
achega
d36680aea1
Modificáronse 7 ficheiros con 142 adicións e 51 borrados
  1. 5 6
      common/token.js
  2. 10 5
      common/wechat.js
  3. 10 16
      middlewares/web_auth.js
  4. 47 12
      routers/index.js
  5. 10 4
      routers/user.js
  6. 58 7
      views/configure.ejs
  7. 2 1
      views/partials/nav.ejs

+ 5 - 6
common/token.js

@@ -7,16 +7,15 @@ async function initializeTokenStore() {
   users.forEach((user) => {
     if (user.wechatAppId) {
       tokenStore.set(user.prefix, {
-        appId: user.wechatAppId,
-        appSecret: user.wechatAppSecret,
-        templateId: user.wechatTemplateId,
-        openId: user.wechatOpenId,
-        serverVerifyToken: user.wechatVerifyToken,
+        wechatAppId: user.wechatAppId,
+        wechatAppSecret: user.wechatAppSecret,
+        wechatTemplateId: user.wechatTemplateId,
+        wechatOpenId: user.wechatOpenId,
+        wechatVerifyToken: user.wechatVerifyToken,
         token: '',
       });
     }
   });
-  console.debug(tokenStore);
   console.log('Token store initialized.');
 }
 

+ 10 - 5
common/wechat.js

@@ -3,8 +3,9 @@ const { tokenStore } = require('./token');
 const config = require('../config');
 
 async function refreshToken() {
-  for (const item of tokenStore) {
-    item.token = await this.requestToken();
+  for (let [key, value] of tokenStore) {
+    value.token = await requestToken(value.wechatAppId, value.wechatAppSecret);
+    tokenStore.set(key, value);
   }
   console.log('Token refreshed.');
 }
@@ -15,9 +16,13 @@ async function requestToken(appId, appSecret) {
     let res = await axios.get(
       `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appId}&secret=${appSecret}`
     );
-    console.log(res);
-    if (res && res.data && res.data.access_token) {
-      token = res.data.access_token;
+    // console.debug(res);
+    if (res && res.data) {
+      if (res.data.access_token) {
+        token = res.data.access_token;
+      } else {
+        console.error(res.data);
+      }
     }
   } catch (e) {
     console.error(e);

+ 10 - 16
middlewares/web_auth.js

@@ -3,36 +3,30 @@ const config = require('../config');
 exports.userRequired = (req, res, next) => {
   if (req.session.user) {
     if (req.session.user.isBlocked) {
-      return res.render('message', {
-        isError: true,
-        message: '用户账户被禁用,请联系管理员',
-        link: '/feedback',
-      });
+      req.flash('message', '用户账户被禁用,请联系管理员');
+      return res.redirect('/');
     }
   } else {
-    return res.render('message', {
-      isError: false,
-      message: '用户尚未登录,请登录',
-      link: '/login',
-    });
+    req.flash('message', '用户尚未登录,请登录');
+    return res.redirect('/login');
   }
   next();
 };
 
 exports.adminRequired = (req, res, next) => {
   if (!req.session.user || !req.session.user.isAdmin) {
-    return res.render('message', {
-      isError: true,
-      message: '需要超级管理员权限',
-    });
+    req.flash('message', '需要管理员权限');
+    return res.redirect('/login');
   }
   next();
 };
 
 exports.allowRegister = (req, res, next) => {
   if (!config.allowRegister) {
-    req.flash('message', '管理员未开放注册!');
-    return res.redirect('/');
+    if (!req.session.user || !req.session.user.isAdmin) {
+      req.flash('message', '管理员未开放注册!');
+      return res.redirect('/');
+    }
   }
   next();
 };

+ 47 - 12
routers/index.js

@@ -2,7 +2,12 @@ const express = require('express');
 const router = express.Router();
 const { User } = require('../models');
 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');
 
 router.get('/', (req, res, next) => {
@@ -12,7 +17,9 @@ router.get('/', (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) => {
@@ -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.flash('message', '已退出登录');
   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 = {
     username: req.body.username,
     password: req.body.password,
@@ -80,19 +93,41 @@ router.post('/configure', async (req, res, next) => {
     wechatOpenId: req.body.wechatOpenId,
     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 {
-    user = await User.create(user);
-    tokenStore.set(user.prefix, {
-      appId: user.wechatAppId,
-      appSecret: user.wechatAppSecret,
-      templateId: user.wechatTemplateId,
-      openId: user.wechatOpenId,
-      wechatVerifyToken: user.wechatVerifyToken,
-      token: '',
+    let userObj = await User.findOne({
+      where: {
+        id: id,
+      },
     });
+    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) {
     console.error(e);
+    message = e.message;
   }
+  req.flash('message', message);
+  res.redirect('/configure');
 });
 
 module.exports = router;

+ 10 - 4
routers/user.js

@@ -6,10 +6,6 @@ const { tokenStore } = require('../common/token');
 
 const router = express.Router();
 
-router.get('/:userPrefix/configure', (req, res, next) => {
-  res.render('configure');
-});
-
 router.all('/:userPrefix/verify', (req, res, next) => {
   // 验证消息来自微信服务器:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
   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) => {
   const userPrefix = req.params.userPrefix;
   let message = {

+ 58 - 7
views/configure.ejs

@@ -3,48 +3,99 @@
 <div class="normal-container">
     <div>
         <h2 class="title">配置页面</h2>
+        <%- include('./partials/message') %>
         <form action="/configure" method="post">
             <div class="field">
                 <label class="label">APP ID</label>
                 <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 class="field">
                 <label class="label">APP SECRET</label>
                 <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 class="field">
                 <label class="label">OPEN ID</label>
                 <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 class="field">
                 <label class="label">TEMPLATE ID</label>
                 <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 class="field">
                 <label class="label">TOKEN</label>
                 <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 class="field is-grouped is-grouped-right">
                 <div class="control">
-                    <input type="submit" class="button is-link" value="提交">
+                    <input type="submit" class="button is-light" value="提交">
                 </div>
                 <div class="control">
-                    <input type="reset" class="button is-link is-light" value="重置">
+                    <input type="reset" class="button is-light" value="重置">
                 </div>
             </div>
         </form>

+ 2 - 1
views/partials/nav.ejs

@@ -15,13 +15,14 @@
         <div id="mainNavbar" class="navbar-menu">
             <div class="navbar-start">
                 <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>
             </div>
             <div class="navbar-end">
                 <div class="navbar-item">
                     <div class="buttons">
                         <% if (isLogged) { %>
+                            <a class="button is-light" href="/configure">配置</a>
                             <a class="button is-light" href="/logout">退出</a>
                         <% } else { %>
                             <a class="button is-light" href="/register">注册</a>