Song 5 vuotta sitten
vanhempi
sitoutus
564628cdd4
9 muutettua tiedostoa jossa 140 lisäystä ja 22 poistoa
  1. 4 14
      README.md
  2. 5 2
      app.js
  3. 1 0
      package.json
  4. 50 5
      routes.js
  5. 1 1
      utils.js
  6. 56 0
      views/configure.ejs
  7. 2 0
      views/footer.ejs
  8. 11 0
      views/header.ejs
  9. 10 0
      views/message.ejs

+ 4 - 14
README.md

@@ -9,24 +9,14 @@
 3. 安装依赖:`npm i`。
 3. 安装依赖:`npm i`。
 4. 安装 pm2:`npm i -g pm2`。
 4. 安装 pm2:`npm i -g pm2`。
 5. 使用 Nginx 反代我们的 Node.js 服务,默认端口 3000。
 5. 使用 Nginx 反代我们的 Node.js 服务,默认端口 3000。
-6. 使用 pm2 启动应用:`pm2 start ./app.js --name wechat-message-push-service`。
 
 
 ### 微信公众平台端配置
 ### 微信公众平台端配置
-1. 首先前往[此页面](https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index) 拿到 APP_ID 以及 APP_SECRET。
+1. 首先前往[此页面](https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index)拿到 APP_ID 以及 APP_SECRET。
 2. 填写接口配置信息,URL 填 `https://你的域名/verify`,TOKEN 随意,之后点击验证。
 2. 填写接口配置信息,URL 填 `https://你的域名/verify`,TOKEN 随意,之后点击验证。
 3. 使用微信扫描下方的测试号二维码,拿到你的 OPEN_ID。
 3. 使用微信扫描下方的测试号二维码,拿到你的 OPEN_ID。
 4. 新增模板消息模板,模板标题随意,模板内容填 `{{text.DATA}}`,提交后可以拿到 TEMPLATE_ID。
 4. 新增模板消息模板,模板标题随意,模板内容填 `{{text.DATA}}`,提交后可以拿到 TEMPLATE_ID。
 
 
 ### 启动服务
 ### 启动服务
-1. 至此,我们已经拿到所有我们需要的信息,接下来设置环境变量,方法很多,例如可以通过在应用的根目录创建 .env 文件,设置以下环境变量:
-    ```
-    APP_ID=wx*****
-    APP_SECRET=*****
-    TOKEN=你的 TOKEN
-    TEMPLATE_ID=****
-    OPEN_ID=****
-    PORT=3000
-    ```
-
-2. 使用 `pm2 restart wechat-message-push-service` 重启服务。
-3. 之后访问 `https://你的域名/push?content=Hi`,如果微信能够收到消息一条内容为 Hi 的模板消息,则配置成功。 
+1. 使用 pm2 启动应用:`pm2 start ./app.js --name wechat-message-push-service`。
+2. 访问首页填写配置项并提交。
+2. 之后访问 `https://你的域名/push?content=Hi`,如果微信能够收到消息一条内容为 Hi 的模板消息,则配置成功。 

+ 5 - 2
app.js

@@ -13,6 +13,11 @@ const app = express();
 requestToken(app);
 requestToken(app);
 setInterval(() => requestToken(app), 100 * 60 * 1000);
 setInterval(() => requestToken(app), 100 * 60 * 1000);
 
 
+const port = parseInt(process.env.PORT || "3000");
+app.set("port", port);
+app.set("views", path.join(__dirname, "views"));
+app.set("view engine", "ejs");
+
 app.use(logger("dev"));
 app.use(logger("dev"));
 app.use(express.json());
 app.use(express.json());
 app.use(express.urlencoded({ extended: false }));
 app.use(express.urlencoded({ extended: false }));
@@ -21,8 +26,6 @@ app.use(express.static(path.join(__dirname, "public")));
 
 
 app.use("/", indexRouter);
 app.use("/", indexRouter);
 
 
-const port = parseInt(process.env.PORT || "3000");
-app.set("port", port);
 const server = http.createServer(app);
 const server = http.createServer(app);
 
 
 server.listen(port);
 server.listen(port);

+ 1 - 0
package.json

@@ -10,6 +10,7 @@
     "cookie-parser": "~1.4.4",
     "cookie-parser": "~1.4.4",
     "debug": "~2.6.9",
     "debug": "~2.6.9",
     "dotenv": "^8.2.0",
     "dotenv": "^8.2.0",
+    "ejs": "^3.1.5",
     "express": "~4.16.1",
     "express": "~4.16.1",
     "morgan": "~1.9.1"
     "morgan": "~1.9.1"
   },
   },

+ 50 - 5
routes.js

@@ -2,12 +2,55 @@ const express = require("express");
 const router = express.Router();
 const router = express.Router();
 const crypto = require("crypto");
 const crypto = require("crypto");
 const axios = require("axios");
 const axios = require("axios");
+const fs = require("fs");
+const requestToken = require("./utils").requestToken;
 
 
-router.get("/", (req, res, next) => {
-  res.send("OK");
+router.all("/", (req, res, next) => {
+  fs.promises
+    .access("./.env")
+    .then(() => {
+      res.render("message", {
+        message: "服务已在运行。",
+      });
+    })
+    .catch(() => {
+      res.render("configure");
+    });
+});
+
+router.post("/configure", (req, res, next) => {
+  fs.promises
+    .access("./.env")
+    .then(() => {
+      res.render("message", {
+        message: ".env 文件已经存在,请手动删除该文件后重试!",
+      });
+    })
+    .catch(() => {
+      let content =
+        `APP_ID=${req.body.APP_ID}\n` +
+        `APP_SECRET=${req.body.APP_SECRET}\n` +
+        `TOKEN=${req.body.TOKEN}\n` +
+        `TEMPLATE_ID=${req.body.TEMPLATE_ID}\n` +
+        `OPEN_ID=${req.body.OPEN_ID}`;
+      fs.promises
+        .writeFile("./.env", content, "utf8")
+        .then(() => {
+          res.render("message", {
+            message:
+              ".env 文件写入成功,程序即将自动关闭以应用写入的新的环境变量,需要进程守护程序自动重启应用或者手动重启。",
+          });
+          process.exit();
+        })
+        .catch((e) => {
+          res.render("message", {
+            message: "在尝试写入 .env 文件时发生错误:" + e,
+          });
+        });
+    });
 });
 });
 
 
-router.get("/verify", (req, res, next) => {
+router.all("/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 { signature, timestamp, nonce, echostr } = req.query;
   const { signature, timestamp, nonce, echostr } = req.query;
   const token = process.env.TOKEN;
   const token = process.env.TOKEN;
@@ -21,10 +64,9 @@ router.get("/verify", (req, res, next) => {
   }
   }
 });
 });
 
 
-router.get("/push", (req, res, next) => {
+router.all("/push", (req, res, next) => {
   // Reference: https://mp.weixin.qq.com/debug/cgi-bin/readtmpl?t=tmplmsg/faq_tmpl
   // Reference: https://mp.weixin.qq.com/debug/cgi-bin/readtmpl?t=tmplmsg/faq_tmpl
   let content = req.query.content || req.body.content;
   let content = req.query.content || req.body.content;
-  console.log(`Get content: ${content}`);
   let access_token = req.app.access_token;
   let access_token = req.app.access_token;
   let request_data = {
   let request_data = {
     touser: process.env.OPEN_ID,
     touser: process.env.OPEN_ID,
@@ -37,6 +79,9 @@ router.get("/push", (req, res, next) => {
       request_data
       request_data
     )
     )
     .then((response) => {
     .then((response) => {
+      if (response.data && response.data.errcode === "40001") {
+        requestToken(req.app);
+      }
       res.json(response.data);
       res.json(response.data);
     })
     })
     .catch((error) => {
     .catch((error) => {

+ 1 - 1
utils.js

@@ -9,7 +9,7 @@ module.exports = {
       )
       )
       .then((res) => {
       .then((res) => {
         if (res.data && res.data.access_token) {
         if (res.data && res.data.access_token) {
-          console.log("Get token: ", res.data.access_token);
+          console.log("Token requested.");
           token = res.data.access_token;
           token = res.data.access_token;
           app.access_token = token;
           app.access_token = token;
         } else {
         } else {

+ 56 - 0
views/configure.ejs

@@ -0,0 +1,56 @@
+<%- include('./header') %>
+<h2 class="subtitle">配置页面</h2>
+<article class="message is-warning">
+    <div class="message-header">
+        <p>警告</p>
+    </div>
+    <div class="message-body">
+        提交后将在程序的根目录创建一个 .env 文件,之后程序将自动关闭,请确保你已经使用了诸如 pm2 的进程守护程序来自动重启本程序。提交之后如果想要对配置内容进行修改,则你必须手动删除 .env 文件。
+    </div>
+</article>
+<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">
+        </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">
+        </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">
+        </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">
+        </div>
+    </div>
+
+    <div class="field">
+        <label class="label">TOKEN</label>
+        <div class="control">
+            <input class="input" name="TOKEN" type="text" required placeholder="请输入你的 TOKEN">
+        </div>
+    </div>
+
+    <div class="field is-grouped is-grouped-right">
+        <div class="control">
+            <input type="submit" class="button is-link" value="提交">
+        </div>
+        <div class="control">
+            <input type="reset" class="button is-link is-light" value="重置">
+        </div>
+    </div>
+</form>
+<%- include('./footer') %>

+ 2 - 0
views/footer.ejs

@@ -0,0 +1,2 @@
+</div>
+</body>

+ 11 - 0
views/header.ejs

@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="zh">
+<head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css">
+    <title>WeChat Message Push Service</title>
+</head>
+<body style="width: 100%;">
+<div style="margin: auto; max-width: 960px">
+    <h1 class="title" style="margin-top: 16px"><a style="color: black; text-decoration: none">微信消息通知服务</a></h1>

+ 10 - 0
views/message.ejs

@@ -0,0 +1,10 @@
+<%- include('./header') %>
+<article class="message is-primary">
+    <div class="message-header">
+        <p>注意</p>
+    </div>
+    <div class="message-body">
+        <%= message %>
+    </div>
+</article>
+<%- include('./footer') %>