浏览代码

制作开发获取SDK接口与后台页面编辑新的公众平台

zhibin 6 年之前
父节点
当前提交
661ad89f50

+ 14 - 0
.vscode/launch.json

@@ -0,0 +1,14 @@
+{
+  // 使用 IntelliSense 了解相关属性。 
+  // 悬停以查看现有属性的描述。
+  // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
+  "version": "0.2.0",
+  "configurations": [
+    {
+      "type": "node",
+      "request": "launch",
+      "name": "启动程序",
+      "program": "${workspaceFolder}\\app.js"
+    }
+  ]
+}

+ 2 - 0
app.js

@@ -1,10 +1,12 @@
 const Koa = require('koa');
+const bodyParser = require('koa-bodyparser');
 const static = require('koa-static');
 const path = require('path');
 const router = require('./router');
 const sys = require('./config/sys.json');
 
 const app = new Koa();
+app.use(bodyParser());
 app.use(router.routes());
 app.use(router.allowedMethods());
 app.use(static(path.join(__dirname, sys.static_dir), { handle: true }));

+ 1 - 1
config/sys.json

@@ -3,7 +3,7 @@
   "mail": {
     "service": "qq",
     "account": "854556519@qq.com",
-    "pass": "gjoiykrnepbabebi"
+    "pass": "tnivcizsasmvbbaa"
   },
   "port": 8000,
   "pro_name": "WeChat",

+ 10 - 1
package.json

@@ -11,5 +11,14 @@
     "url": "http://192.168.0.115:3000/bill/WeChat.git"
   },
   "author": "",
-  "license": "ISC"
+  "license": "ISC",
+  "dependencies": {
+    "koa": "^2.7.0",
+    "koa-bodyparser": "^4.2.1",
+    "koa-router": "^7.4.0",
+    "koa-static": "^5.0.0",
+    "nodemailer": "^5.1.1",
+    "request": "^2.88.0",
+    "sha1": "^1.1.1"
+  }
 }

+ 1 - 1
router/error.js

@@ -36,7 +36,7 @@ async function error(ctx, next) {
       if (!sys.debug && adminMail) {
         await mailSend({
           to: adminMail,
-          subject: '4维时代错误提醒',
+          subject: '4维时代公众号SDK平台错误提醒',
           html: `<h1>${e.message}</h1><div>${e.stack ? e.stack.replace(/\n/ig, '<br>') : e}</div>`
         });
       } else {

+ 54 - 0
router/share.js

@@ -0,0 +1,54 @@
+const Router = require('koa-router');
+const router = new Router();
+const path = require('path')
+const WeChat = require('../util/wechat')
+const fs = require('fs')
+const configLocal = path.join(__dirname, '../static/wechats.json')
+
+const wcConfig = {
+  get () {
+    return JSON.parse(fs.readFileSync(configLocal).toString())
+  },
+  set (data) {
+    fs.writeFileSync(configLocal, JSON.stringify(data))
+  }
+}
+
+
+router.get('/', async ctx => {
+  let { name, uri, callback } = ctx.request.query
+  let config = wcConfig.get().find(it => it.name === name)
+
+  if (!config) {
+    return ctx.body = '该服务号不存在' 
+  }
+
+  let token = await WeChat.getAccessToken(config.AppID, config.AppSecret)
+  let ticket = await WeChat.getTicket(token)
+  let timestamp = WeChat.grentTimeStamp()
+  let nonceStr = WeChat.grentNonceStr()
+  let signature = await WeChat.getSignature(ticket, nonceStr, timestamp, uri)
+  let result = {
+    appId: config.AppID,
+    timestamp,
+    nonceStr,
+    signature
+  }
+  
+  ctx.body = `${callback}(${JSON.stringify(result)})`
+})
+
+router.post('/repect', async ctx => {
+  let { data } = ctx.request.body
+
+  data.forEach(item => {
+    item.domains = item.domains || []
+  })
+
+  wcConfig.set(data)
+  ctx.body = {
+    code: 200
+  }
+})
+
+module.exports = exports = { path: '/share', router };

+ 587 - 0
static/css/bootstrap-theme.css

@@ -0,0 +1,587 @@
+/*!
+ * Bootstrap v3.3.7 (http://getbootstrap.com)
+ * Copyright 2011-2016 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+.btn-default,
+.btn-primary,
+.btn-success,
+.btn-info,
+.btn-warning,
+.btn-danger {
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
+}
+.btn-default:active,
+.btn-primary:active,
+.btn-success:active,
+.btn-info:active,
+.btn-warning:active,
+.btn-danger:active,
+.btn-default.active,
+.btn-primary.active,
+.btn-success.active,
+.btn-info.active,
+.btn-warning.active,
+.btn-danger.active {
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+}
+.btn-default.disabled,
+.btn-primary.disabled,
+.btn-success.disabled,
+.btn-info.disabled,
+.btn-warning.disabled,
+.btn-danger.disabled,
+.btn-default[disabled],
+.btn-primary[disabled],
+.btn-success[disabled],
+.btn-info[disabled],
+.btn-warning[disabled],
+.btn-danger[disabled],
+fieldset[disabled] .btn-default,
+fieldset[disabled] .btn-primary,
+fieldset[disabled] .btn-success,
+fieldset[disabled] .btn-info,
+fieldset[disabled] .btn-warning,
+fieldset[disabled] .btn-danger {
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.btn-default .badge,
+.btn-primary .badge,
+.btn-success .badge,
+.btn-info .badge,
+.btn-warning .badge,
+.btn-danger .badge {
+  text-shadow: none;
+}
+.btn:active,
+.btn.active {
+  background-image: none;
+}
+.btn-default {
+  text-shadow: 0 1px 0 #fff;
+  background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
+  background-image:      -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
+  background-image:         linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #dbdbdb;
+  border-color: #ccc;
+}
+.btn-default:hover,
+.btn-default:focus {
+  background-color: #e0e0e0;
+  background-position: 0 -15px;
+}
+.btn-default:active,
+.btn-default.active {
+  background-color: #e0e0e0;
+  border-color: #dbdbdb;
+}
+.btn-default.disabled,
+.btn-default[disabled],
+fieldset[disabled] .btn-default,
+.btn-default.disabled:hover,
+.btn-default[disabled]:hover,
+fieldset[disabled] .btn-default:hover,
+.btn-default.disabled:focus,
+.btn-default[disabled]:focus,
+fieldset[disabled] .btn-default:focus,
+.btn-default.disabled.focus,
+.btn-default[disabled].focus,
+fieldset[disabled] .btn-default.focus,
+.btn-default.disabled:active,
+.btn-default[disabled]:active,
+fieldset[disabled] .btn-default:active,
+.btn-default.disabled.active,
+.btn-default[disabled].active,
+fieldset[disabled] .btn-default.active {
+  background-color: #e0e0e0;
+  background-image: none;
+}
+.btn-primary {
+  background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
+  background-image:      -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
+  background-image:         linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #245580;
+}
+.btn-primary:hover,
+.btn-primary:focus {
+  background-color: #265a88;
+  background-position: 0 -15px;
+}
+.btn-primary:active,
+.btn-primary.active {
+  background-color: #265a88;
+  border-color: #245580;
+}
+.btn-primary.disabled,
+.btn-primary[disabled],
+fieldset[disabled] .btn-primary,
+.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled.focus,
+.btn-primary[disabled].focus,
+fieldset[disabled] .btn-primary.focus,
+.btn-primary.disabled:active,
+.btn-primary[disabled]:active,
+fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active,
+.btn-primary[disabled].active,
+fieldset[disabled] .btn-primary.active {
+  background-color: #265a88;
+  background-image: none;
+}
+.btn-success {
+  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
+  background-image:      -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
+  background-image:         linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #3e8f3e;
+}
+.btn-success:hover,
+.btn-success:focus {
+  background-color: #419641;
+  background-position: 0 -15px;
+}
+.btn-success:active,
+.btn-success.active {
+  background-color: #419641;
+  border-color: #3e8f3e;
+}
+.btn-success.disabled,
+.btn-success[disabled],
+fieldset[disabled] .btn-success,
+.btn-success.disabled:hover,
+.btn-success[disabled]:hover,
+fieldset[disabled] .btn-success:hover,
+.btn-success.disabled:focus,
+.btn-success[disabled]:focus,
+fieldset[disabled] .btn-success:focus,
+.btn-success.disabled.focus,
+.btn-success[disabled].focus,
+fieldset[disabled] .btn-success.focus,
+.btn-success.disabled:active,
+.btn-success[disabled]:active,
+fieldset[disabled] .btn-success:active,
+.btn-success.disabled.active,
+.btn-success[disabled].active,
+fieldset[disabled] .btn-success.active {
+  background-color: #419641;
+  background-image: none;
+}
+.btn-info {
+  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
+  background-image:      -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
+  background-image:         linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #28a4c9;
+}
+.btn-info:hover,
+.btn-info:focus {
+  background-color: #2aabd2;
+  background-position: 0 -15px;
+}
+.btn-info:active,
+.btn-info.active {
+  background-color: #2aabd2;
+  border-color: #28a4c9;
+}
+.btn-info.disabled,
+.btn-info[disabled],
+fieldset[disabled] .btn-info,
+.btn-info.disabled:hover,
+.btn-info[disabled]:hover,
+fieldset[disabled] .btn-info:hover,
+.btn-info.disabled:focus,
+.btn-info[disabled]:focus,
+fieldset[disabled] .btn-info:focus,
+.btn-info.disabled.focus,
+.btn-info[disabled].focus,
+fieldset[disabled] .btn-info.focus,
+.btn-info.disabled:active,
+.btn-info[disabled]:active,
+fieldset[disabled] .btn-info:active,
+.btn-info.disabled.active,
+.btn-info[disabled].active,
+fieldset[disabled] .btn-info.active {
+  background-color: #2aabd2;
+  background-image: none;
+}
+.btn-warning {
+  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
+  background-image:      -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
+  background-image:         linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #e38d13;
+}
+.btn-warning:hover,
+.btn-warning:focus {
+  background-color: #eb9316;
+  background-position: 0 -15px;
+}
+.btn-warning:active,
+.btn-warning.active {
+  background-color: #eb9316;
+  border-color: #e38d13;
+}
+.btn-warning.disabled,
+.btn-warning[disabled],
+fieldset[disabled] .btn-warning,
+.btn-warning.disabled:hover,
+.btn-warning[disabled]:hover,
+fieldset[disabled] .btn-warning:hover,
+.btn-warning.disabled:focus,
+.btn-warning[disabled]:focus,
+fieldset[disabled] .btn-warning:focus,
+.btn-warning.disabled.focus,
+.btn-warning[disabled].focus,
+fieldset[disabled] .btn-warning.focus,
+.btn-warning.disabled:active,
+.btn-warning[disabled]:active,
+fieldset[disabled] .btn-warning:active,
+.btn-warning.disabled.active,
+.btn-warning[disabled].active,
+fieldset[disabled] .btn-warning.active {
+  background-color: #eb9316;
+  background-image: none;
+}
+.btn-danger {
+  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
+  background-image:      -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
+  background-image:         linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-color: #b92c28;
+}
+.btn-danger:hover,
+.btn-danger:focus {
+  background-color: #c12e2a;
+  background-position: 0 -15px;
+}
+.btn-danger:active,
+.btn-danger.active {
+  background-color: #c12e2a;
+  border-color: #b92c28;
+}
+.btn-danger.disabled,
+.btn-danger[disabled],
+fieldset[disabled] .btn-danger,
+.btn-danger.disabled:hover,
+.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger:hover,
+.btn-danger.disabled:focus,
+.btn-danger[disabled]:focus,
+fieldset[disabled] .btn-danger:focus,
+.btn-danger.disabled.focus,
+.btn-danger[disabled].focus,
+fieldset[disabled] .btn-danger.focus,
+.btn-danger.disabled:active,
+.btn-danger[disabled]:active,
+fieldset[disabled] .btn-danger:active,
+.btn-danger.disabled.active,
+.btn-danger[disabled].active,
+fieldset[disabled] .btn-danger.active {
+  background-color: #c12e2a;
+  background-image: none;
+}
+.thumbnail,
+.img-thumbnail {
+  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+          box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+}
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus {
+  background-color: #e8e8e8;
+  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+  background-image:      -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
+  background-image:         linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
+  background-repeat: repeat-x;
+}
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+  background-color: #2e6da4;
+  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+  background-image:      -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
+  background-image:         linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
+  background-repeat: repeat-x;
+}
+.navbar-default {
+  background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
+  background-image:      -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
+  background-image:         linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
+}
+.navbar-default .navbar-nav > .open > a,
+.navbar-default .navbar-nav > .active > a {
+  background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
+  background-image:      -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
+  background-image:         linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
+  background-repeat: repeat-x;
+  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
+}
+.navbar-brand,
+.navbar-nav > li > a {
+  text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
+}
+.navbar-inverse {
+  background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
+  background-image:      -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
+  background-image:         linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+  background-repeat: repeat-x;
+  border-radius: 4px;
+}
+.navbar-inverse .navbar-nav > .open > a,
+.navbar-inverse .navbar-nav > .active > a {
+  background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
+  background-image:      -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
+  background-image:         linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
+  background-repeat: repeat-x;
+  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
+          box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
+}
+.navbar-inverse .navbar-brand,
+.navbar-inverse .navbar-nav > li > a {
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
+}
+.navbar-static-top,
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+  border-radius: 0;
+}
+@media (max-width: 767px) {
+  .navbar .navbar-nav .open .dropdown-menu > .active > a,
+  .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
+  .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
+    color: #fff;
+    background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+    background-image:      -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
+    background-image:         linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
+    background-repeat: repeat-x;
+  }
+}
+.alert {
+  text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
+}
+.alert-success {
+  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
+  background-image:      -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
+  background-image:         linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #b2dba1;
+}
+.alert-info {
+  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
+  background-image:      -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
+  background-image:         linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #9acfea;
+}
+.alert-warning {
+  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
+  background-image:      -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
+  background-image:         linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #f5e79e;
+}
+.alert-danger {
+  background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
+  background-image:      -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
+  background-image:         linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #dca7a7;
+}
+.progress {
+  background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
+  background-image:      -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
+  background-image:         linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar {
+  background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
+  background-image:      -o-linear-gradient(top, #337ab7 0%, #286090 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
+  background-image:         linear-gradient(to bottom, #337ab7 0%, #286090 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-success {
+  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
+  background-image:      -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
+  background-image:         linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-info {
+  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
+  background-image:      -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
+  background-image:         linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-warning {
+  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
+  background-image:      -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
+  background-image:         linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-danger {
+  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
+  background-image:      -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
+  background-image:         linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
+  background-repeat: repeat-x;
+}
+.progress-bar-striped {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.list-group {
+  border-radius: 4px;
+  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+          box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+}
+.list-group-item.active,
+.list-group-item.active:hover,
+.list-group-item.active:focus {
+  text-shadow: 0 -1px 0 #286090;
+  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
+  background-image:      -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
+  background-image:         linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #2b669a;
+}
+.list-group-item.active .badge,
+.list-group-item.active:hover .badge,
+.list-group-item.active:focus .badge {
+  text-shadow: none;
+}
+.panel {
+  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
+          box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
+}
+.panel-default > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+  background-image:      -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
+  background-image:         linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-primary > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+  background-image:      -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
+  background-image:         linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-success > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
+  background-image:      -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
+  background-image:         linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-info > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
+  background-image:      -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
+  background-image:         linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-warning > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
+  background-image:      -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
+  background-image:         linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
+  background-repeat: repeat-x;
+}
+.panel-danger > .panel-heading {
+  background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
+  background-image:      -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
+  background-image:         linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
+  background-repeat: repeat-x;
+}
+.well {
+  background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
+  background-image:      -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
+  background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
+  background-image:         linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
+  background-repeat: repeat-x;
+  border-color: #dcdcdc;
+  -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
+          box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
+}
+/*# sourceMappingURL=bootstrap-theme.css.map */

文件差异内容过多而无法显示
+ 1 - 0
static/css/bootstrap-theme.css.map


文件差异内容过多而无法显示
+ 6 - 0
static/css/bootstrap-theme.min.css


文件差异内容过多而无法显示
+ 1 - 0
static/css/bootstrap-theme.min.css.map


文件差异内容过多而无法显示
+ 6757 - 0
static/css/bootstrap.css


文件差异内容过多而无法显示
+ 1 - 0
static/css/bootstrap.css.map


文件差异内容过多而无法显示
+ 6 - 0
static/css/bootstrap.min.css


文件差异内容过多而无法显示
+ 1 - 0
static/css/bootstrap.min.css.map


二进制
static/fonts/glyphicons-halflings-regular.eot


文件差异内容过多而无法显示
+ 288 - 0
static/fonts/glyphicons-halflings-regular.svg


二进制
static/fonts/glyphicons-halflings-regular.ttf


二进制
static/fonts/glyphicons-halflings-regular.woff


二进制
static/fonts/glyphicons-halflings-regular.woff2


+ 250 - 0
static/index.html

@@ -0,0 +1,250 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <meta http-equiv="X-UA-Compatible" content="ie=edge">
+  <link rel="stylesheet" href="./css/bootstrap.min.css">
+  <title>公众号管理</title>
+  <style>
+    body {
+      padding: 10px;
+      width: 90%;
+      margin: 0 auto;
+    }
+
+    #add-domain {
+      cursor: pointer;
+    }
+
+    .mt20 {
+      margin: 20px 0;
+    }
+  </style>
+</head>
+
+<body>
+  <table class="table table-bordered table-hover" id="data-all">
+    <thead>
+      <tr>
+        <th>公众号</th>
+        <th>可调域名</th>
+        <th>AppID</th>
+        <th>AppSecret</th>
+        <th>操作</th>
+      </tr>
+    </thead>
+    <tbody></tbody>
+  </table>
+
+  <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#myModal">添加</button>
+
+  <div class="panel panel-default mt20">
+    <div class="panel-heading">如何使用</div>
+    <div class="panel-body">
+      <p>1.将新申请的公众号AppID,AppSecret添加入列表中,并编写公众号名称(不能重复)</p>
+      <p>2.将本网站的IP <strong>120.24.64.23</strong> 加入公众号白名单内</p>
+      <p>3.将要使用微信SDK的网站的域名设置为公众号安全域名</p>
+      <p>4.使用接口 <strong>https://www.4dage.com/wechat/jssdk/share/</strong> 或 <strong>http://www.4dage.com/wechat/jssdk/share/</strong> 即可获取签名参数</p>
+      <p>例如:</p>
+      <pre>
+$.ajax({
+  url: 'https://www.4dage.com/wechat/jssdk/share/',
+  type: "post",
+  data: {
+    'uri': location.href.split('#')[0],   //当前页面地址
+    'name': "四维测试服务号"               //哪个公众号,对应上方的名称
+  },
+  dataType: "jsonp",
+  jsonpCallback: "success_jsonp",
+  success: function (data, textStatus) {
+    wx.config({
+      debug: false,
+      appId: data.appId,
+      timestamp: data.timestamp,
+      nonceStr: data.nonceStr,
+      signature: data.signature,
+      jsApiList: ['checkJsApi', 'onMenuShareTimeline',
+        'onMenuShareAppMessage', 'onMenuShareQQ',
+        'onMenuShareWeibo', 'hideMenuItems',
+        'showMenuItems', 'hideAllNonBaseMenuItem',
+        'showAllNonBaseMenuItem', 'translateVoice',
+        'startRecord', 'stopRecord', 'onRecordEnd',
+        'playVoice', 'pauseVoice', 'stopVoice',
+        'uploadVoice', 'downloadVoice', 'chooseImage',
+        'previewImage', 'uploadImage', 'downloadImage',
+        'getNetworkType', 'openLocation', 'getLocation',
+        'hideOptionMenu', 'showOptionMenu', 'closeWindow',
+        'scanQRCode', 'chooseWXPay',
+        'openProductSpecificView', 'addCard', 'chooseCard',
+        'openCard']
+    });
+  },
+  error: function (XMLHttpRequest, textStatus, errorThrown) {
+    console.log("jsonp.error:" + textStatus);
+  }
+});
+      </pre>
+    </div>
+  </div>
+  <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+    <div class="modal-dialog" role="document">
+      <div class="modal-content">
+        <div class="modal-header">
+          <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+          <h4 class="modal-title" id="myModalLabel">公众号信息修改</h4>
+        </div>
+        <div class="modal-body">
+
+          <form>
+            <div class="form-group">
+              <label for="exampleInputEmail1">公众号</label>
+              <input type="input" class="form-control" placeholder="公众号名称" id="name">
+            </div>
+            <div class="form-group">
+              <label for="exampleInputPassword1">可调域名 <a id="add-domain">+</a></label>
+              <input type="input" class="form-control domains" placeholder="可调域名">
+              <input type="input" class="form-control domains" placeholder="可调域名">
+            </div>
+            <div class="form-group">
+              <label for="exampleInputPassword1">AppID</label>
+              <input type="input" class="form-control" id="appid" placeholder="AppID">
+            </div>
+            <div class="form-group">
+              <label for="exampleInputPassword1">AppSecret</label>
+              <input type="input" class="form-control" id="appsecret" placeholder="AppSecret">
+            </div>
+          </form>
+
+        </div>
+        <div class="modal-footer">
+          <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
+          <button type="button" class="btn btn-primary" id="submit">确定</button>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <script src="./js/jquery-3.3.1.min.js"></script>
+  <script src="./js/bootstrap.min.js"></script>
+  <script>
+    function getData() {
+      $.ajax({
+        url: '/wechat/jssdk/wechats.json',
+        method: 'get',
+        dataType: 'JSON',
+        success: function (data) {
+          window.wechats = data
+
+          let items = data.map((item, index) => {
+            let domains = item.domains.map(i => `<p>${i}</p>`).join('')
+            return `
+              <tr>
+                <td>${item.name}</td>
+                <td>${domains}</td>
+                <td>${item.AppID}</td>
+                <td>${item.AppSecret}</td>
+                <td>
+                  <div class="btn-group" index="${index}">
+                    <a class="btn btn-danger" oper="delete">删除</a>
+                    <a class="btn btn-warning"  data-toggle="modal" data-target="#myModal">修改</a>
+                  </div>
+                </td>  
+              </tr>
+            `
+          })
+          $('#data-all tbody').html(items.join(''))
+        }
+      })
+    }
+
+    function updateData() {
+      $.ajax({
+        url: '/wechat/jssdk/share/repect',
+        method: 'post',
+        dataType: 'JSON',
+        data: {
+          data: wechats
+        },
+        success: getData
+      })
+
+      $('#myModal').modal('hide')
+    }
+
+    $('#add-domain').on('click', function() {
+      $(this).parent().parent().append('<input type="input" class="form-control domains" placeholder="可调域名">')
+    })
+
+    $('#myModal').on('show.bs.modal', function (event) {
+      let that = event.relatedTarget.parentNode
+      let index = that.getAttribute('index')
+
+      if (index === null) {
+        $('#myModalLabel').html('公众号信息添加')
+        $('#name').val('')
+        $('#appid').val('')
+        $('#appsecret').val('')
+        Array.from($('.domains')).forEach((item, i) => {
+          i ? $(item).remove() : $(item).val('')
+        })
+        $('#submit').removeAttr('index')
+      } else if (wechats[index]) {
+        let wechat = wechats[index]
+        let $domainParent = $('.domains').parent()
+        let domains = wechat.domains.map(i => `<input type="input" class="form-control domains" value="${i}" placeholder="可调域名">`).join('')
+
+        $('#myModalLabel').html('公众号信息修改')
+        $('#name').val(wechat.name)
+        $('#appid').val(wechat.AppID)
+        $('#appsecret').val(wechat.AppSecret)
+        $('.domains').remove()
+        $domainParent.append(domains)
+        $('#submit').attr('index', index)
+      }
+    })
+
+
+    $('#submit').on('click', function () {
+      let index = this.getAttribute('index')
+      let data = {
+        name: $('#name').val(),
+        AppID: $('#appid').val(),
+        AppSecret: $('#appsecret').val(),
+        domains: []
+      }
+
+      Array.from($('.domains')).forEach(item => {
+        let ip = $(item).val()
+        if (ip) {
+          data.domains.push(ip)
+        }
+      })
+
+      if (index !== null) {
+        wechats[index] = data
+      } else {
+        wechats.push(data)
+      }
+
+      updateData();
+    })
+
+
+    $('#data-all tbody').on('click', ev => {
+      let $that = $(ev.target)
+      if ($that.attr('oper') !== 'delete' || !confirm('删除后无法恢复你确定要删除吗?')) return;
+      let index = $that.parent().attr('index')
+
+      if (index !== null) {
+        wechats.splice(Number(index), 1)
+        updateData();
+      }
+    })
+
+    getData()
+  </script>
+</body>
+
+</html>

文件差异内容过多而无法显示
+ 2377 - 0
static/js/bootstrap.js


文件差异内容过多而无法显示
+ 7 - 0
static/js/bootstrap.min.js


文件差异内容过多而无法显示
+ 2 - 0
static/js/jquery-3.3.1.min.js


+ 13 - 0
static/js/npm.js

@@ -0,0 +1,13 @@
+// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.
+require('../../js/transition.js')
+require('../../js/alert.js')
+require('../../js/button.js')
+require('../../js/carousel.js')
+require('../../js/collapse.js')
+require('../../js/dropdown.js')
+require('../../js/modal.js')
+require('../../js/tooltip.js')
+require('../../js/popover.js')
+require('../../js/scrollspy.js')
+require('../../js/tab.js')
+require('../../js/affix.js')

文件差异内容过多而无法显示
+ 1 - 0
static/wechats.json


+ 41 - 0
util/mailSend.js

@@ -0,0 +1,41 @@
+const mailer = require('nodemailer');
+const sys = require('../config/sys');
+
+const mailrg = /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
+const transporter = mailer.createTransport({
+  service: sys.mail.service,
+  auth: {
+    user: sys.mail.account,
+    pass: sys.mail.pass
+  }
+});
+
+/**
+ * 邮件发送
+ * @param {to, subject, html} receive 
+ *      to string 接收者
+ *      subject string 邮件标题
+ *      html string 邮件内容
+ * @return Pomise
+ */
+function send(receive) {
+  return new Promise((resolve, reject) => {
+    let {to, subject = '邮件发送', html} = receive;
+
+    if (!mailrg.test(to)) {
+      return reject('mail option to format Incorrect');
+    } else if (!html || html.length === '') {
+      return reject('mail option html format Incorrect');
+    }
+
+    transporter.sendMail({from: sys.mail.account, to, subject, html}, (err, info) => {
+      if (err) {
+        reject(err);
+      } else {
+        resolve(info);
+      }
+    });
+  });
+}
+
+module.exports = exports = send;

+ 0 - 44
util/pubtool.js

@@ -1,44 +0,0 @@
-const crypto = require('crypto');
-
-
-/**
- * 生成指定位数的随机字符串
- * @param digit number 要生成随机字符串的位数
- * @return string 随机字符串
- */
-function randomChar(digit = 4) {
-  let strs = [];
-  
-  do {
-    let str = Math.random().toString(16).substr(2);
-    if (str.length >= digit) {
-      strs.push(str.substr(0, digit));
-      digit = 0;
-    } else {
-      strs.push(str);
-      digit -= str.length
-    }
-  } while(digit);
-
-  return strs.join('');
-}
-
-
-/**
- * 用md5加密指定字符串
- * @param encStr string 要加密的字符串
- * @param pow number 要加密的次数
- * @return string 加密后的字符串
- */
-function hashEncryption(encStr, pow = 1) {
-  for (let i = 0; i < pow; i++) {
-    encStr = crypto.createHash('md5').update(encStr, 'utf8').digest('hex');
-  }
-  return encStr;
-}
-
-console.log(hashEncryption('admin', 2))
-
-module.exports = exports = {
-  randomChar, hashEncryption
-}

+ 110 - 0
util/wechat.js

@@ -0,0 +1,110 @@
+const request = require('request')
+const sha1 = require('sha1')
+
+
+// 创建唯一的不重复的随机字符串
+function grentNonceStr() {
+  return Math.random().toString(36).substr(2, 15)
+}
+
+// 获取单签时间戳,精确到秒
+function grentTimeStamp() {
+  return parseInt(Date.now() / 1000) + ''
+}
+
+// 创建缓存器
+function createCache() {
+  let cache = {}
+  let cacheTime = 7000 * 1000
+
+  return {
+    get(key) {
+      return cache[key]
+    },
+    set(key, val) {
+      cache[key] = val
+      setTimeout(() => delete cache[key], cacheTime)
+    }
+  }
+}
+
+
+// 获取access_token 需要参数 appid secret
+const getAccessToken = (() => {
+  let store = createCache()
+
+  function getToken(appid, secret) {
+    let uri = 'https://api.weixin.qq.com/cgi-bin/token?' +
+      'grant_type=client_credential&appid=' + appid +
+      '&secret=' + secret;
+
+    return new Promise((resolve, reject) => {
+      request(uri, function (error, response, body) {
+        if (error) {
+          reject(error)
+        } else if (response.statusCode === 200) {
+          body = JSON.parse(body);
+          resolve(body.access_token)
+        } else {
+          reject(error)
+        }
+      })
+    })
+  }
+
+  return async (appid, secret) => {
+    if (!store.get(appid)) {
+      let token = await getToken(appid, secret)
+      store.set(appid, token)
+    }
+
+    return store.get(appid)
+  }
+})();
+
+
+// 获取ticket 需要参数access_token
+const getTicket = (() => {
+  let store = createCache()
+
+  function getTicket(accessToken) {
+    let uri = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?' +
+      'access_token=' + accessToken + '&type=jsapi';
+
+    return new Promise((resolve, reject) => {
+      request(uri, function (error, response, body) {
+        if (error) {
+          reject(error)
+        } else if (response.statusCode === 200) {
+          let content = JSON.parse(body);
+          resolve(content.ticket)
+        } else {
+          reject(error)
+        }
+      })
+    })
+  }
+
+  return async (accessToken) => {
+    if (!store.get(accessToken)) {
+      let ticket = await getTicket(accessToken)
+      store.set(accessToken, ticket)
+    }
+
+    return store.get(accessToken)
+  }
+})();
+
+// 获取计算签名
+function getSignature(ticket, noncestr, timestamp, url) {
+  console.log(`jsapi_ticket=${ticket}&noncestr=${noncestr}&timestamp=${timestamp}&url=${url}`)
+  return sha1(`jsapi_ticket=${ticket}&noncestr=${noncestr}&timestamp=${timestamp}&url=${url}`)
+}
+
+module.exports = {
+  getAccessToken,
+  getTicket,
+  getSignature,
+  grentNonceStr,
+  grentTimeStamp
+}

+ 1 - 0
wechats.json

@@ -0,0 +1 @@
+undefined