├── .DS_Store ├── .gitignore ├── CHANGELOG.md ├── README.md ├── client ├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── README.md ├── assets │ └── README.md ├── components │ ├── Logo.vue │ └── README.md ├── layouts │ ├── README.md │ └── default.vue ├── middleware │ └── README.md ├── nuxt.config.js ├── package.json ├── pages │ ├── README.md │ ├── demo │ │ └── one.vue │ └── index.vue ├── plugins │ └── README.md ├── static │ ├── README.md │ └── favicon.ico └── store │ └── README.md ├── docker-compose.yaml ├── dockerfile └── server ├── app.js ├── config.js ├── controllers ├── app.js └── index.js ├── middleware └── index.js ├── models └── app.js ├── package-lock.json ├── package.json ├── routes.js └── utils └── tool.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmfe/eros-publish/0b831e65d26ca13c09d721c35afd4906b80f615c/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .nuxt 3 | datadir/* 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.0.1 2 | * [bugfix] 差分包 resCode 不对应 3 | 4 | # 1.0.0 5 | done -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | >时间就是金钱,热更新能避免 appStore 的审核,为 app 迭代提供强大动力。 2 | 3 | ## 开始之前 4 | 本章内容会与开发者一起在`本地构建热更新服务`,开始本章内容前,请再次确保您已经运行起 eros 开发的整套流程,**如果需要构建到服务端/自动构建平台上,我们建议您先了解一些后端基础,在尝试构建 `热更新`**。 5 | 6 | ## 原理 7 | [文档.](https://bmfe.github.io/eros-docs/#/zh-cn/advanced_diff) 8 | 9 | ## eros 为什么要做自己的热更新? 10 | 如果您之前熟读了我们的[入门指南](https://bmfe.github.io/eros-docs/#/zh-cn/tutorial_newcomer),大概就理解为什么 eros 要做`内置包热更新`,这里在列举一下。 11 | 12 | **相对于 weex 每次都走线上请求最新的 bundle,我们做内置包的设计是考虑到如下场景:** 13 | 14 | 1.发布了新页面。 15 | 16 | - weex 场景:新的页面,不做自身缓存策略的话,会加载远端的 bundle,首次加载会很慢,这样页面就会出来的比较慢。 17 | - eros 场景:客户在使用中还是访问老的页面,下次进入 app 更新访问新的,每次从本地读取 bundle,很快。 18 | 19 | 2.bundle 打包体积。 20 | 21 | - weex 场景:weex 推荐多页面,所以每个页面都是个 bundle,意味着使用时候如果不做特殊处理,按需引入,每个 bundle 会有很多重复的冗余代码,尽管 weex 相比 rn,bundle 的体积已经小很多了。 22 | - eros 场景:weex 在本地有一个公共的 js bundle (appboard),我们把公共逻辑都放入这里,每次打包都打一份代码,并把公共代码在客户端来进行拼接这行,虽然这样不太规范,**但这样的方式使我们的 bundle 大小减少了 60% +,100 多个页面,内置包大小仅仅为 2MB**。 23 | 24 | 3.用户在挂号这条业务线中走到了支付这一步,挂号流程改造,支付的时候多加了些参数。 25 | 26 | - weex 场景:发布了新的版本,如果没有做好自身的缓存兼容逻辑,支付页面跳转了新的页面,前端就每次都需要考虑兼容版本之间兼容。 27 | - eros 场景:客户端本地更新了最新的内置包,不会立即更新,用户完成此次 app 使用,下次进入 app 时才会提示更新。 28 | 29 | 4... 30 | 31 | 我们还能列出许多相似的场景,不难看出,内置包热更新的设计,更适于纯 weex native 的项目。 32 | 33 | ## 模拟 EROS 热更新 34 | ### 环境 35 | - node > 7.x.x 36 | - mongoDB > v3.4.9 37 | 38 | ### 工具 39 | 40 | - bsdiff/bspatch 需要在目标服务器或者本地开发时安装。 41 | - supervisor 开发 42 | - pm2 部署 43 | - Robo 3T (mongoDB 可视化工具) 44 | 45 | 安装都比较简单,装到全局即可,不过多赘述。 46 | 47 | ### clone 项目 48 | ``` 49 | $ git clone https://github.com/bmfe/eros-publish.git 50 | $ cd eros-publish/server 51 | $ npm i 52 | ``` 53 | ### 修改配置 54 | ``` 55 | // eros-publish/server/config.js 56 | module.exports = { 57 | db:'mongodb://localhost:27017/app', 58 | defaultPort: 3001, 59 | staticVirtualPath: '/static', 60 | staticRealPath: '/Users/yangmingzhe/Work/opensource/', 61 | 62 | zipReturn: 'diff' 63 | } 64 | ``` 65 | - `db`: 数据库服务地址 66 | - `defaultPort`: 热更新服务默认端口 67 | - `staticVirtualPath`: 静态虚拟地址 68 | - `staticRealPath`: 静态真实地址 69 | - `zipReturn`: 返回包的开关,`full` 是任何情况下都返回`全量包`, `diff` 则是任何情况下都返回`增量包` 70 | 71 | 这里说下 `staticVirtualPath` 和 `staticRealPath`,拿上面例子来说,访问虚拟地址 ('/static') ,其实是访问的真实地址 ('/Users/yangmingzhe/Work/opensource/') 下的资源。 72 | 73 | > http://localhost:3001/static => /Users/yangmingzhe/Work/opensource/ 74 | 75 | 所以当我们把自己的项目放到 opensource 下,就可以让别人通过`/static` 访问到你项目中的打包生成好的资源文件 `(dist)`。 76 | ### 开发调试 77 | 78 | - 开启数据库: 79 | 权限不足用sudo 80 | ``` 81 | $ mongod 82 | ``` 83 | 84 | - 打开 `Robo 3T` 可视化工具: 85 | 86 | 这时候会提示你链接到你的数据库,链接即可。 87 | 88 | - 运行后端服务 89 | ``` 90 | $ supervisor app.js 91 | ``` 92 | 看到如下界面后: 93 | 94 | 后端已经调通了。 95 | 96 | ### 接口 97 | 总共就 3 个接口,需要我们先熟悉一下: 98 | * 发布列表 99 | * path: `/app/list` 100 | * method: `GET` 101 | * params: 102 | * appName: app名称 103 | * 增加发布记录 104 | * path: `/app/add` 105 | * method: `POST` 106 | * params: 107 | * appName: { type: **String**, require: **true**, desc: **"app 名称"**}, 108 | * jsPath: { type: **String**, require: **true**, desc: **"js bundle 下载路径,也就是 eros.dev.js 中的 diff.proxy"**}, 109 | * iOS: { type: **String**, require: **true**, desc: **"ios 版本号"**}, 110 | * android: { type: **String**, require: **true**, desc: **"android 版本号"**}, 111 | * jsVersion: { type: **String**, require: **true**, desc: **"当前 bundle 的 md5 值"**}, 112 | * timestamp: {type: **Number**, require: **true**, desc: **"单当前包生成的时间**"} 113 | * 检查是否更新 114 | * path: `/app/check` 115 | * method: `POST` 116 | * params: 117 | * appName: { type: **String**, require: **true**, desc: **"app 名称"**}, 118 | * jsPath: { type: **String**, require: **true**, desc: **"js bundle 下载路径,也就是 eros.dev.js 中的 diff.proxy"**}, 119 | * iOS: { type: **String**, require: **true**, desc: **"ios 版本号"**}, 120 | * android: { type: **String**, require: **true**, desc: **"android 版本号"**}, 121 | * jsVersion: { type: **String**, require: **true**, desc: **"当前 bundle 的 md5 值"**} 122 | 123 | `/app/check`中 ios 和 android 二选一。 124 | 125 | ### 修改模板配置 126 | 1. 这里我们重新生成了一个名叫 eros-demo 的默认项目,在本地 `/Users/yangmingzhe/Work/opensource/` 绝对路径下,注意这个就是我上面填写的 `staticRealPath`。 127 | 2. 修改 eros.native.js 中的 `url.bundleUpdate`,由上面的接口我们能知道,需要 app 进行更新检测的接口是 `/app/check`,所以我们填写 `http://localhost:3001/app/check`即可 128 | 3. 修改 eros.dev.js 中的 diff 对象 129 | * `pwd`: 每次更新的全量包我们都存放到这个地址,这样每次差分包的生成都会遍历这个路径下的全量包生成,建议无论是在本地还是在服务器上,都先建好这个目录,我这里写我本地的路径一个新建的目录`/Users/yangmingzhe/Work/opensource/eros-diff-folder` 130 | * `proxy`: 能被用户访问到的路径,由于上面我们配置了 `staticVirtualPath`,这样我们就能在 `http://localhost:3001/static/` 访问到我们的 eros-demo 项目资源,填写路径为 `http://localhost:3001/static/eros-demo/dist/js/` 131 | 132 | ### 模拟更新 133 | 这里需要用到两条指令: 134 | - `$ eros pack -s url`: 构建全量包,内置到两端成为内置包,并将包信息发送 eros-publish 来记录。 135 | - `$ eros build -s url -d`: 构建最新全量包,遍历 eros.dev.js 下 diff.pwd 历史版本分别生成差分包,并将全量包信息发送服务器 136 | 137 | > 差分包 v1-v2 = md5(全量包 v1 md5 + 全量包 v2 md5) 138 | 139 | 首先我们构建模拟 iOS app v1.0.0 首次发版情况。 140 | 1. 修改业务代码,模拟我们 1.0.0 的功能,添加首页加一行代码: 141 | ``` 142 | 版本 1.0.0 143 | ``` 144 | 145 | 2. 修改 app 版本号为 1.0.0,[iOS 修改方法](https://bmfe.github.io/eros-docs/#/zh-cn/ios_config)。 146 | 3. 修改 `eros.native.json` 里的 `version.iOS` 为 1.0.0。 147 | 4. 执行 `$ eros pack -s http://localhost:3001/app/add`,打内置包,并记录此版本全量包信息。 148 | 5. 正式流程这步就可以提交 appStore 审核了,不过本地调试可以忽略此步。 149 | 6. 运行你的 app,关闭拦截器读取内置包,模拟用户,可以看到首页的 `版本 1.0.0`。 150 | 7. 我们修改本地的代码,如把首页文案变动一下 `版本 1.0.0-build.1`。 151 | 8. 执行 `$ eros build -s http://localhost:3001/app/add -d` 来生成差分包。 152 | 9. 重启 app,你就会发现 app 会弹出更新提示,点击立即更新即可看到文案变更为 `版本 1.0.0-build.1`。 153 | 154 | 至此我们就完成了一次 app 的发布,也就是`热更新`。 155 | 156 | 下面会有同学问,当我 app 逻辑做了变动,升级到了 1.0.1 怎么办呢? 157 | 158 | > 只要理解,**我们每次 app 发版都内置最新的 js bundles 给用户**就行了,继续以上步骤,app 版本和 js 版本都变为 1.0.1,然后在市场上发布应用,用户下载,就完成了一次客户端跨版本升级。 159 | 160 | ## 部署 EROS 热更新 161 | 部署只需要启动数据库,然后通过 `pm2/nohup` 来部署即可,注意写好上面涉及到的配置。 162 | ``` 163 | $ sudo nohup mongod & // 后台运行数据库 164 | $ sudo pm2 start app.js // 后台 node 服务 165 | ``` 166 | 167 | ## 最后 168 | eros-publish 是一个非常简单热更新服务逻辑,热更新发布最重要的还是要把脚手架和发布系统结合好,eros-publish 虽然是开箱即用,但有一些不足和改进的方向这里也要提一下: 169 | 170 | - 可以在 app 内置 websocket 与发布系统的 websocket,来实现定时定点,分区分片的更新推送,保证 app 可以进行灰度发布,更有甚者,可以按照用户习性来推送不同版本的全量包。 171 | - 更新的逻辑应该更多样化,分为强制更新,非强制更新,更新说明推送等。 172 | - 当更新包比较大的时候应该考虑断点续传,文件校验性的逻辑,不过以我们一百多个页面的 app 来说,整个全量包也才 2M 多。 173 | 174 | ## docker 部署方式 175 | sudo docker-compose up -d -------------------------------------------------------------------------------- /client/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_size = 2 6 | indent_style = space 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /client/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: 'babel-eslint', 4 | env: { 5 | browser: true, 6 | node: true 7 | }, 8 | extends: 'standard', 9 | // required to lint *.vue files 10 | plugins: [ 11 | 'html' 12 | ], 13 | // add your custom rules here 14 | rules: {}, 15 | globals: {} 16 | } 17 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | 4 | # logs 5 | npm-debug.log 6 | 7 | # Nuxt build 8 | .nuxt 9 | 10 | # Nuxt generate 11 | dist 12 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # nuxt-test 2 | 3 | > Nuxt.js project 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | $ npm install # Or yarn install 10 | 11 | # serve with hot reload at localhost:3000 12 | $ npm run dev 13 | 14 | # build for production and launch server 15 | $ npm run build 16 | $ npm start 17 | 18 | # generate static project 19 | $ npm run generate 20 | ``` 21 | 22 | For detailed explanation on how things work, checkout the [Nuxt.js docs](https://github.com/nuxt/nuxt.js). 23 | -------------------------------------------------------------------------------- /client/assets/README.md: -------------------------------------------------------------------------------- 1 | # ASSETS 2 | 3 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. 4 | 5 | More information about the usage of this directory in the documentation: 6 | https://nuxtjs.org/guide/assets#webpacked 7 | 8 | **This directory is not required, you can delete it if you don't want to use it.** 9 | -------------------------------------------------------------------------------- /client/components/Logo.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 80 | -------------------------------------------------------------------------------- /client/components/README.md: -------------------------------------------------------------------------------- 1 | # COMPONENTS 2 | 3 | The components directory contains your Vue.js Components. 4 | Nuxt.js doesn't supercharge these components. 5 | 6 | **This directory is not required, you can delete it if you don't want to use it.** 7 | -------------------------------------------------------------------------------- /client/layouts/README.md: -------------------------------------------------------------------------------- 1 | # LAYOUTS 2 | 3 | This directory contains your Application Layouts. 4 | 5 | More information about the usage of this directory in the documentation: 6 | https://nuxtjs.org/guide/views#layouts 7 | 8 | **This directory is not required, you can delete it if you don't want to use it.** 9 | -------------------------------------------------------------------------------- /client/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 53 | -------------------------------------------------------------------------------- /client/middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | This directory contains your Application Middleware. 4 | The middleware lets you define custom function to be ran before rendering a page or a group of pages (layouts). 5 | 6 | More information about the usage of this directory in the documentation: 7 | https://nuxtjs.org/guide/routing#middleware 8 | 9 | **This directory is not required, you can delete it if you don't want to use it.** 10 | -------------------------------------------------------------------------------- /client/nuxt.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | /* 3 | ** Headers of the page 4 | */ 5 | head: { 6 | title: 'nuxt-test', 7 | meta: [ 8 | { charset: 'utf-8' }, 9 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 10 | { hid: 'description', name: 'description', content: 'Nuxt.js project' } 11 | ], 12 | link: [ 13 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' } 14 | ] 15 | }, 16 | /* 17 | ** Customize the progress bar color 18 | */ 19 | loading: { color: '#3B8070' }, 20 | /* 21 | ** Build configuration 22 | */ 23 | build: { 24 | /* 25 | ** Run ESLint on save 26 | */ 27 | extend (config, ctx) { 28 | if (ctx.dev && ctx.isClient) { 29 | config.module.rules.push({ 30 | enforce: 'pre', 31 | test: /\.(js|vue)$/, 32 | loader: 'eslint-loader', 33 | exclude: /(node_modules)/ 34 | }) 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-test", 3 | "version": "1.0.0", 4 | "description": "Nuxt.js project", 5 | "author": "Mingzhe Yang ", 6 | "private": true, 7 | "scripts": { 8 | "dev": "nuxt", 9 | "build": "nuxt build", 10 | "start": "nuxt start", 11 | "generate": "nuxt generate", 12 | "lint": "eslint --ext .js,.vue --ignore-path .gitignore .", 13 | "precommit": "npm run lint" 14 | }, 15 | "dependencies": { 16 | "nuxt": "^1.0.0-rc11" 17 | }, 18 | "devDependencies": { 19 | "babel-eslint": "^7.2.3", 20 | "eslint": "^4.3.0", 21 | "eslint-config-standard": "^10.2.1", 22 | "eslint-loader": "^1.9.0", 23 | "eslint-plugin-html": "^3.1.1", 24 | "eslint-plugin-import": "^2.7.0", 25 | "eslint-plugin-node": "^5.1.1", 26 | "eslint-plugin-promise": "^3.5.0", 27 | "eslint-plugin-standard": "^3.0.1" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client/pages/README.md: -------------------------------------------------------------------------------- 1 | # PAGES 2 | 3 | This directory contains your Application Views and Routes. 4 | The framework reads all the .vue files inside this directory and create the router of your application. 5 | 6 | More information about the usage of this directory in the documentation: 7 | https://nuxtjs.org/guide/routing 8 | -------------------------------------------------------------------------------- /client/pages/demo/one.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | 17 | 47 | -------------------------------------------------------------------------------- /client/pages/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 32 | 33 | 63 | -------------------------------------------------------------------------------- /client/plugins/README.md: -------------------------------------------------------------------------------- 1 | # PLUGINS 2 | 3 | This directory contains your Javascript plugins that you want to run before instantiating the root vue.js application. 4 | 5 | More information about the usage of this directory in the documentation: 6 | https://nuxtjs.org/guide/plugins 7 | 8 | **This directory is not required, you can delete it if you don't want to use it.** 9 | -------------------------------------------------------------------------------- /client/static/README.md: -------------------------------------------------------------------------------- 1 | # STATIC 2 | 3 | This directory contains your static files. 4 | Each file inside this directory is mapped to /. 5 | 6 | Example: /static/robots.txt is mapped as /robots.txt. 7 | 8 | More information about the usage of this directory in the documentation: 9 | https://nuxtjs.org/guide/assets#static 10 | 11 | **This directory is not required, you can delete it if you don't want to use it.** 12 | -------------------------------------------------------------------------------- /client/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmfe/eros-publish/0b831e65d26ca13c09d721c35afd4906b80f615c/client/static/favicon.ico -------------------------------------------------------------------------------- /client/store/README.md: -------------------------------------------------------------------------------- 1 | # STORE 2 | 3 | This directory contains your Vuex Store files. 4 | Vuex Store option is implemented in the Nuxt.js framework. 5 | Creating a index.js file in this directory activate the option in the framework automatically. 6 | 7 | More information about the usage of this directory in the documentation: 8 | https://nuxtjs.org/guide/vuex-store 9 | 10 | **This directory is not required, you can delete it if you don't want to use it.** 11 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | eros-publish: 4 | image: eros-publish 5 | container_name: eros-publish 6 | restart: always 7 | build: . 8 | 9 | depends_on: 10 | - mongo 11 | ports: 12 | - 3008:3001 13 | volumes: 14 | - ./static:/root/Service/static 15 | mongo: 16 | image: mongo 17 | container_name: eros-publish-mongo 18 | restart: always 19 | volumes: 20 | - ./datadir:/data/db 21 | -------------------------------------------------------------------------------- /dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:11-alpine 2 | RUN mkdir -p /root/Service 3 | WORKDIR /root/Service 4 | COPY ./server /root/Service 5 | RUN npm config set unsafe-perm true && npm install -g cnpm --registry=https://registry.npm.taobao.org && cnpm install 6 | EXPOSE 3001 7 | CMD npm start -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const mongoose = require("mongoose"); 3 | const path = require("path"); 4 | const bodyParser = require("body-parser"); 5 | const cookieParser = require("cookie-parser"); 6 | const session = require("express-session"); 7 | const flash = require("connect-flash"); 8 | const routes = require("./routes"); 9 | const app = express(); 10 | const EROS_OPTIONS = require('./config') 11 | 12 | mongoose.connect(EROS_OPTIONS.db); 13 | app.use(EROS_OPTIONS.staticVirtualPath, express.static(EROS_OPTIONS.staticRealPath)) 14 | app.set("port", process.env.PORT || EROS_OPTIONS.defaultPort); 15 | 16 | // 提高安全性 17 | app.use(bodyParser.urlencoded({ extended: false })); 18 | app.use(cookieParser()); 19 | app.use(flash()); 20 | app.use(routes); 21 | 22 | app.listen(app.get("port"), function() { 23 | console.log("Server started on port " + app.get("port")); 24 | }); -------------------------------------------------------------------------------- /server/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | db:'mongodb://mongo:27017/app', 3 | defaultPort: 3001, 4 | staticVirtualPath: '/static', 5 | staticRealPath: './static', 6 | zipReturn: 'diff' // full 全量 7 | } 8 | -------------------------------------------------------------------------------- /server/controllers/app.js: -------------------------------------------------------------------------------- 1 | var AppModel = require("../models/app"), 2 | format = require("../utils/tool").format, 3 | md5 = require('js-md5'), 4 | config = require('../config') 5 | 6 | 7 | const add = (req, res, next) => { 8 | let appInfo = new AppModel(req.body) 9 | 10 | appInfo.save(() => { 11 | res.send(format({ 12 | data: 'success' 13 | })) 14 | }) 15 | } 16 | 17 | const list = (req, res, next) => { 18 | let { appName } = req.query 19 | 20 | AppModel.find({ appName }).sort({ _id : 'desc'}).exec((err, app) =>{ 21 | if (err) { return next(err) } 22 | if (!app) { return next(404) } 23 | res.send(format({ 24 | data: app 25 | })) 26 | }) 27 | } 28 | 29 | const check = (req, res, next) => { 30 | let { appName, jsVersion, isDiff = true } = req.query, 31 | platform = !!req.query.iOS ? 'iOS': 'android', 32 | version = req.query[platform], 33 | checkParams = { 34 | appName, 35 | } 36 | if(jsVersion) checkParams['jsVersion'] = jsVersion 37 | checkParams[platform] = version 38 | AppModel.find(checkParams, (err, apps) =>{ 39 | if (err) { return next(err) } 40 | requestZip({ 41 | res, apps, appName, platform, version, jsVersion, isDiff, next 42 | }) 43 | }) 44 | } 45 | 46 | const requestZip = ({res, apps, appName, platform, version, jsVersion, isDiff, next}) => { 47 | if(!appName || !platform || !version) { 48 | res.send(format({ 49 | resCode: 400, 50 | msg: "参数缺失", 51 | data: {} 52 | })) 53 | return 54 | } 55 | getNewestInfo({ appName, platform, version}).then(newests => { 56 | // console.log(newests) 57 | if (!newests || !newests.length) { 58 | res.send(format({ 59 | resCode: 400, 60 | msg: "无任何包信息", 61 | data: {} 62 | })) 63 | return 64 | } 65 | 66 | const fullZipPath = `${newests[0].jsPath}${newests[0].jsVersion}.zip` 67 | const diffZipPath = `${newests[0].jsPath}${md5(jsVersion + newests[0].jsVersion)}.zip` 68 | 69 | if(isDiff == 0 || isDiff === 'false' || isDiff === false) { 70 | // 请求全量包 71 | res.send(format({ 72 | msg: "请求全量包成功", 73 | data: { 74 | diff: false, 75 | path: fullZipPath 76 | } 77 | })) 78 | return 79 | } 80 | // 请求差分包 81 | if(!apps.length){ 82 | // 不存在jsVersion 当前包信息可能被篡改 直接返回最新版本全量包 83 | res.send(format({ 84 | resCode: 401, 85 | msg: "jsVersion 不存在", 86 | data: { 87 | diff: false, 88 | path: fullZipPath 89 | } 90 | })) 91 | }else { 92 | if(newests[0].jsVersion === jsVersion) { 93 | // 存在 jsVersion 并且是最新 94 | res.send(format({ 95 | resCode: 4000, 96 | msg: "当前版本已是最新,不需要更新" 97 | })) 98 | } else { 99 | // 存在 jsVersion 但不是最新 100 | res.send(format({ 101 | msg: "当前版本需要更新", 102 | data: { 103 | diff: true, 104 | jsVersion: newests[0].jsVersion, 105 | // path: `${newests[0].jsPath}${md5(jsVersion + newests[0].jsVersion)}.zip` 106 | path: config.zipReturn === 'diff' ? diffZipPath : fullZipPath 107 | } 108 | })) 109 | } 110 | 111 | } 112 | }) 113 | } 114 | 115 | const getNewestInfo = ({appName, platform, version}) => { 116 | let params = { 117 | appName 118 | } 119 | params[platform] = version 120 | return AppModel.find(params).sort({ timestamp : 'desc'}) 121 | 122 | 123 | } 124 | 125 | 126 | 127 | module.exports = { 128 | add, 129 | list, 130 | check 131 | } -------------------------------------------------------------------------------- /server/controllers/index.js: -------------------------------------------------------------------------------- 1 | var app = require('./app'); 2 | module.exports = { 3 | app 4 | } -------------------------------------------------------------------------------- /server/middleware/index.js: -------------------------------------------------------------------------------- 1 | const ensureAuthenticated = (req, res, next) => { 2 | // 一个Passport提供的函数 3 | if (req.isAuthenticated()) { 4 | next(); 5 | } else { 6 | req.flash("info", "You must be logged in to see this page."); 7 | res.redirect("/login"); 8 | } 9 | } 10 | 11 | module.exports = { 12 | ensureAuthenticated 13 | } -------------------------------------------------------------------------------- /server/models/app.js: -------------------------------------------------------------------------------- 1 | var mongoose = require("mongoose"), 2 | moment = require("moment") 3 | var modelSchema = new mongoose.Schema({ 4 | appName: { type: String, require: true }, 5 | jsPath: { type: String, require: true }, 6 | iOS: { type: String, require: true }, 7 | android: { type: String, require: true }, 8 | jsVersion: { type: String, require: true }, 9 | timestamp: {type: Number, require: true }, 10 | createTime: {type: String, default: moment().format('YYYY-MM-DD h:m:s')} 11 | }) 12 | 13 | modelSchema.methods.findbyAppName = (appName, callback) => { 14 | this.model('app').find({ appName }, callback) 15 | } 16 | 17 | var AppModel = mongoose.model("app", modelSchema) 18 | module.exports = AppModel -------------------------------------------------------------------------------- /server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eros-pub", 3 | "requires": true, 4 | "lockfileVersion": 1, 5 | "dependencies": { 6 | "accepts": { 7 | "version": "1.3.4", 8 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", 9 | "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", 10 | "requires": { 11 | "mime-types": "2.1.17", 12 | "negotiator": "0.6.1" 13 | } 14 | }, 15 | "array-flatten": { 16 | "version": "1.1.1", 17 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 18 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 19 | }, 20 | "async": { 21 | "version": "2.1.4", 22 | "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz", 23 | "integrity": "sha1-LSFgx3iAMuTdbL4lAvH5osj2zeQ=", 24 | "requires": { 25 | "lodash": "4.17.4" 26 | } 27 | }, 28 | "bcrypt-nodejs": { 29 | "version": "0.0.3", 30 | "resolved": "https://registry.npmjs.org/bcrypt-nodejs/-/bcrypt-nodejs-0.0.3.tgz", 31 | "integrity": "sha1-xgkX8m3CNWYVZsaBBhwwPCsohCs=" 32 | }, 33 | "bluebird": { 34 | "version": "3.5.1", 35 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", 36 | "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" 37 | }, 38 | "body-parser": { 39 | "version": "1.18.1", 40 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.1.tgz", 41 | "integrity": "sha512-KL2pZpGvy6xuZHgYUznB1Zfw4AoGMApfRanT5NafeLvglbaSM+4CCtmlyYOv66oYXqvKL1xpaFb94V/AZVUnYg==", 42 | "requires": { 43 | "bytes": "3.0.0", 44 | "content-type": "1.0.4", 45 | "debug": "2.6.8", 46 | "depd": "1.1.1", 47 | "http-errors": "1.6.2", 48 | "iconv-lite": "0.4.19", 49 | "on-finished": "2.3.0", 50 | "qs": "6.5.1", 51 | "raw-body": "2.3.2", 52 | "type-is": "1.6.15" 53 | } 54 | }, 55 | "bson": { 56 | "version": "1.0.4", 57 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.4.tgz", 58 | "integrity": "sha1-k8ENOeqltYQVy8QFLz5T5WKwtyw=" 59 | }, 60 | "buffer-shims": { 61 | "version": "1.0.0", 62 | "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", 63 | "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=" 64 | }, 65 | "bytes": { 66 | "version": "3.0.0", 67 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 68 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 69 | }, 70 | "connect-flash": { 71 | "version": "0.1.1", 72 | "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", 73 | "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA=" 74 | }, 75 | "content-disposition": { 76 | "version": "0.5.2", 77 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 78 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 79 | }, 80 | "content-type": { 81 | "version": "1.0.4", 82 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 83 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 84 | }, 85 | "cookie": { 86 | "version": "0.3.1", 87 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 88 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 89 | }, 90 | "cookie-parser": { 91 | "version": "1.4.3", 92 | "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz", 93 | "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=", 94 | "requires": { 95 | "cookie": "0.3.1", 96 | "cookie-signature": "1.0.6" 97 | } 98 | }, 99 | "cookie-signature": { 100 | "version": "1.0.6", 101 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 102 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 103 | }, 104 | "core-util-is": { 105 | "version": "1.0.2", 106 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 107 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 108 | }, 109 | "crc": { 110 | "version": "3.4.4", 111 | "resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz", 112 | "integrity": "sha1-naHpgOO9RPxck79as9ozeNheRms=" 113 | }, 114 | "debug": { 115 | "version": "2.6.8", 116 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", 117 | "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", 118 | "requires": { 119 | "ms": "2.0.0" 120 | } 121 | }, 122 | "depd": { 123 | "version": "1.1.1", 124 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 125 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" 126 | }, 127 | "destroy": { 128 | "version": "1.0.4", 129 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 130 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 131 | }, 132 | "ee-first": { 133 | "version": "1.1.1", 134 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 135 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 136 | }, 137 | "ejs": { 138 | "version": "1.0.0", 139 | "resolved": "https://registry.npmjs.org/ejs/-/ejs-1.0.0.tgz", 140 | "integrity": "sha1-ycYKSKRu5FL7MqccMXuV5aofyz0=" 141 | }, 142 | "encodeurl": { 143 | "version": "1.0.1", 144 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", 145 | "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" 146 | }, 147 | "es6-promise": { 148 | "version": "3.2.1", 149 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", 150 | "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" 151 | }, 152 | "escape-html": { 153 | "version": "1.0.3", 154 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 155 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 156 | }, 157 | "etag": { 158 | "version": "1.8.1", 159 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 160 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 161 | }, 162 | "express": { 163 | "version": "4.15.4", 164 | "resolved": "https://registry.npmjs.org/express/-/express-4.15.4.tgz", 165 | "integrity": "sha1-Ay4iU0ic+PzgJma+yj0R7XotrtE=", 166 | "requires": { 167 | "accepts": "1.3.4", 168 | "array-flatten": "1.1.1", 169 | "content-disposition": "0.5.2", 170 | "content-type": "1.0.4", 171 | "cookie": "0.3.1", 172 | "cookie-signature": "1.0.6", 173 | "debug": "2.6.8", 174 | "depd": "1.1.1", 175 | "encodeurl": "1.0.1", 176 | "escape-html": "1.0.3", 177 | "etag": "1.8.1", 178 | "finalhandler": "1.0.5", 179 | "fresh": "0.5.0", 180 | "merge-descriptors": "1.0.1", 181 | "methods": "1.1.2", 182 | "on-finished": "2.3.0", 183 | "parseurl": "1.3.2", 184 | "path-to-regexp": "0.1.7", 185 | "proxy-addr": "1.1.5", 186 | "qs": "6.5.0", 187 | "range-parser": "1.2.0", 188 | "send": "0.15.4", 189 | "serve-static": "1.12.4", 190 | "setprototypeof": "1.0.3", 191 | "statuses": "1.3.1", 192 | "type-is": "1.6.15", 193 | "utils-merge": "1.0.0", 194 | "vary": "1.1.1" 195 | }, 196 | "dependencies": { 197 | "qs": { 198 | "version": "6.5.0", 199 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz", 200 | "integrity": "sha512-fjVFjW9yhqMhVGwRExCXLhJKrLlkYSaxNWdyc9rmHlrVZbk35YHH312dFd7191uQeXkI3mKLZTIbSvIeFwFemg==" 201 | } 202 | } 203 | }, 204 | "express-session": { 205 | "version": "1.15.5", 206 | "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.15.5.tgz", 207 | "integrity": "sha512-BBVy6E/XqjB507wqe5T+7Ia2N/gtur/dT/fKmvGGKQqUrzI4dcBPGJgV4t2ciX7FoxZPhZcKTDTmb4+5nCyQOw==", 208 | "requires": { 209 | "cookie": "0.3.1", 210 | "cookie-signature": "1.0.6", 211 | "crc": "3.4.4", 212 | "debug": "2.6.8", 213 | "depd": "1.1.1", 214 | "on-headers": "1.0.1", 215 | "parseurl": "1.3.2", 216 | "uid-safe": "2.1.5", 217 | "utils-merge": "1.0.0" 218 | } 219 | }, 220 | "finalhandler": { 221 | "version": "1.0.5", 222 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.5.tgz", 223 | "integrity": "sha1-pwEwPSV6G8gv6lR6M+WuiVMXI98=", 224 | "requires": { 225 | "debug": "2.6.8", 226 | "encodeurl": "1.0.1", 227 | "escape-html": "1.0.3", 228 | "on-finished": "2.3.0", 229 | "parseurl": "1.3.2", 230 | "statuses": "1.3.1", 231 | "unpipe": "1.0.0" 232 | } 233 | }, 234 | "forwarded": { 235 | "version": "0.1.2", 236 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 237 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 238 | }, 239 | "fresh": { 240 | "version": "0.5.0", 241 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz", 242 | "integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44=" 243 | }, 244 | "hooks-fixed": { 245 | "version": "2.0.0", 246 | "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-2.0.0.tgz", 247 | "integrity": "sha1-oB2JTVKsf2WZu7H2PfycQR33DLo=" 248 | }, 249 | "http-errors": { 250 | "version": "1.6.2", 251 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 252 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 253 | "requires": { 254 | "depd": "1.1.1", 255 | "inherits": "2.0.3", 256 | "setprototypeof": "1.0.3", 257 | "statuses": "1.3.1" 258 | } 259 | }, 260 | "iconv-lite": { 261 | "version": "0.4.19", 262 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 263 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" 264 | }, 265 | "inherits": { 266 | "version": "2.0.3", 267 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 268 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 269 | }, 270 | "ipaddr.js": { 271 | "version": "1.4.0", 272 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.4.0.tgz", 273 | "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA=" 274 | }, 275 | "isarray": { 276 | "version": "1.0.0", 277 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 278 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 279 | }, 280 | "js-md5": { 281 | "version": "0.6.1", 282 | "resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.6.1.tgz", 283 | "integrity": "sha512-lyUTXOqMEaA9mm38mHxbTo83WsYAvMJm850kxJcRno3T2qL+e40B2G89E0/4r9TdAeB3jN0TdSVp/VHNI6/WyQ==" 284 | }, 285 | "kareem": { 286 | "version": "1.5.0", 287 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.5.0.tgz", 288 | "integrity": "sha1-4+QQHZ3P3imXadr0tNtk2JXRdEg=" 289 | }, 290 | "lodash": { 291 | "version": "4.17.4", 292 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", 293 | "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" 294 | }, 295 | "media-typer": { 296 | "version": "0.3.0", 297 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 298 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 299 | }, 300 | "merge-descriptors": { 301 | "version": "1.0.1", 302 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 303 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 304 | }, 305 | "methods": { 306 | "version": "1.1.2", 307 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 308 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 309 | }, 310 | "mime": { 311 | "version": "1.3.4", 312 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", 313 | "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" 314 | }, 315 | "mime-db": { 316 | "version": "1.30.0", 317 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", 318 | "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" 319 | }, 320 | "mime-types": { 321 | "version": "2.1.17", 322 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", 323 | "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", 324 | "requires": { 325 | "mime-db": "1.30.0" 326 | } 327 | }, 328 | "moment": { 329 | "version": "2.19.1", 330 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.19.1.tgz", 331 | "integrity": "sha1-VtoaLRy/AdOLfhr8McELz6GSkWc=" 332 | }, 333 | "mongodb": { 334 | "version": "2.2.33", 335 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.33.tgz", 336 | "integrity": "sha1-tTfEcdNKZlG0jzb9vyl1A0Dgi1A=", 337 | "requires": { 338 | "es6-promise": "3.2.1", 339 | "mongodb-core": "2.1.17", 340 | "readable-stream": "2.2.7" 341 | } 342 | }, 343 | "mongodb-core": { 344 | "version": "2.1.17", 345 | "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.17.tgz", 346 | "integrity": "sha1-pBizN6FKFJkPtRC5I97mqBMXPfg=", 347 | "requires": { 348 | "bson": "1.0.4", 349 | "require_optional": "1.0.1" 350 | } 351 | }, 352 | "mongoose": { 353 | "version": "4.12.3", 354 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.12.3.tgz", 355 | "integrity": "sha512-hZOKfZEakCvy+84MyZAv/2EL588SHghBrkhtl+LGBRv9QkPs4SY28a376q9CyN/y0JYinNcrrFMx6lgdQatz5Q==", 356 | "requires": { 357 | "async": "2.1.4", 358 | "bson": "1.0.4", 359 | "hooks-fixed": "2.0.0", 360 | "kareem": "1.5.0", 361 | "mongodb": "2.2.33", 362 | "mpath": "0.3.0", 363 | "mpromise": "0.5.5", 364 | "mquery": "2.3.2", 365 | "ms": "2.0.0", 366 | "muri": "1.3.0", 367 | "regexp-clone": "0.0.1", 368 | "sliced": "1.0.1" 369 | } 370 | }, 371 | "mpath": { 372 | "version": "0.3.0", 373 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.3.0.tgz", 374 | "integrity": "sha1-elj3iem1/TyUUgY0FXlg8mvV70Q=" 375 | }, 376 | "mpromise": { 377 | "version": "0.5.5", 378 | "resolved": "https://registry.npmjs.org/mpromise/-/mpromise-0.5.5.tgz", 379 | "integrity": "sha1-9bJCWddjrMIlewoMjG2Gb9UXMuY=" 380 | }, 381 | "mquery": { 382 | "version": "2.3.2", 383 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-2.3.2.tgz", 384 | "integrity": "sha512-KXWMypZSvhCuqRtza+HMQZdYw7PfFBjBTFvP31NNAq0OX0/NTIgpcDpkWQ2uTxk6vGQtwQ2elhwhs+ZvCA8OaA==", 385 | "requires": { 386 | "bluebird": "3.5.1", 387 | "debug": "2.6.9", 388 | "regexp-clone": "0.0.1", 389 | "sliced": "0.0.5" 390 | }, 391 | "dependencies": { 392 | "debug": { 393 | "version": "2.6.9", 394 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 395 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 396 | "requires": { 397 | "ms": "2.0.0" 398 | } 399 | }, 400 | "sliced": { 401 | "version": "0.0.5", 402 | "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz", 403 | "integrity": "sha1-XtwETKTrb3gW1Qui/GPiXY/kcH8=" 404 | } 405 | } 406 | }, 407 | "ms": { 408 | "version": "2.0.0", 409 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 410 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 411 | }, 412 | "muri": { 413 | "version": "1.3.0", 414 | "resolved": "https://registry.npmjs.org/muri/-/muri-1.3.0.tgz", 415 | "integrity": "sha512-FiaFwKl864onHFFUV/a2szAl7X0fxVlSKNdhTf+BM8i8goEgYut8u5P9MqQqIYwvaMxjzVESsoEm/2kfkFH1rg==" 416 | }, 417 | "negotiator": { 418 | "version": "0.6.1", 419 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 420 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 421 | }, 422 | "on-finished": { 423 | "version": "2.3.0", 424 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 425 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 426 | "requires": { 427 | "ee-first": "1.1.1" 428 | } 429 | }, 430 | "on-headers": { 431 | "version": "1.0.1", 432 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", 433 | "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" 434 | }, 435 | "parseurl": { 436 | "version": "1.3.2", 437 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 438 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 439 | }, 440 | "path-to-regexp": { 441 | "version": "0.1.7", 442 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 443 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 444 | }, 445 | "process-nextick-args": { 446 | "version": "1.0.7", 447 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", 448 | "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" 449 | }, 450 | "proxy-addr": { 451 | "version": "1.1.5", 452 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz", 453 | "integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=", 454 | "requires": { 455 | "forwarded": "0.1.2", 456 | "ipaddr.js": "1.4.0" 457 | } 458 | }, 459 | "qs": { 460 | "version": "6.5.1", 461 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 462 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" 463 | }, 464 | "random-bytes": { 465 | "version": "1.0.0", 466 | "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", 467 | "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" 468 | }, 469 | "range-parser": { 470 | "version": "1.2.0", 471 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 472 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 473 | }, 474 | "raw-body": { 475 | "version": "2.3.2", 476 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", 477 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", 478 | "requires": { 479 | "bytes": "3.0.0", 480 | "http-errors": "1.6.2", 481 | "iconv-lite": "0.4.19", 482 | "unpipe": "1.0.0" 483 | } 484 | }, 485 | "readable-stream": { 486 | "version": "2.2.7", 487 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", 488 | "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=", 489 | "requires": { 490 | "buffer-shims": "1.0.0", 491 | "core-util-is": "1.0.2", 492 | "inherits": "2.0.3", 493 | "isarray": "1.0.0", 494 | "process-nextick-args": "1.0.7", 495 | "string_decoder": "1.0.3", 496 | "util-deprecate": "1.0.2" 497 | } 498 | }, 499 | "regexp-clone": { 500 | "version": "0.0.1", 501 | "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz", 502 | "integrity": "sha1-p8LgmJH9vzj7sQ03b7cwA+aKxYk=" 503 | }, 504 | "require_optional": { 505 | "version": "1.0.1", 506 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", 507 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", 508 | "requires": { 509 | "resolve-from": "2.0.0", 510 | "semver": "5.4.1" 511 | } 512 | }, 513 | "resolve-from": { 514 | "version": "2.0.0", 515 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", 516 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" 517 | }, 518 | "safe-buffer": { 519 | "version": "5.1.1", 520 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 521 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 522 | }, 523 | "semver": { 524 | "version": "5.4.1", 525 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", 526 | "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" 527 | }, 528 | "send": { 529 | "version": "0.15.4", 530 | "resolved": "https://registry.npmjs.org/send/-/send-0.15.4.tgz", 531 | "integrity": "sha1-mF+qPihLAnPHkzZKNcZze9k5Bbk=", 532 | "requires": { 533 | "debug": "2.6.8", 534 | "depd": "1.1.1", 535 | "destroy": "1.0.4", 536 | "encodeurl": "1.0.1", 537 | "escape-html": "1.0.3", 538 | "etag": "1.8.1", 539 | "fresh": "0.5.0", 540 | "http-errors": "1.6.2", 541 | "mime": "1.3.4", 542 | "ms": "2.0.0", 543 | "on-finished": "2.3.0", 544 | "range-parser": "1.2.0", 545 | "statuses": "1.3.1" 546 | } 547 | }, 548 | "serve-static": { 549 | "version": "1.12.4", 550 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.4.tgz", 551 | "integrity": "sha1-m2qpjutyU8Tu3Ewfb9vKYJkBqWE=", 552 | "requires": { 553 | "encodeurl": "1.0.1", 554 | "escape-html": "1.0.3", 555 | "parseurl": "1.3.2", 556 | "send": "0.15.4" 557 | } 558 | }, 559 | "setprototypeof": { 560 | "version": "1.0.3", 561 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 562 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 563 | }, 564 | "sliced": { 565 | "version": "1.0.1", 566 | "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", 567 | "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" 568 | }, 569 | "statuses": { 570 | "version": "1.3.1", 571 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", 572 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" 573 | }, 574 | "string_decoder": { 575 | "version": "1.0.3", 576 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", 577 | "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", 578 | "requires": { 579 | "safe-buffer": "5.1.1" 580 | } 581 | }, 582 | "type-is": { 583 | "version": "1.6.15", 584 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", 585 | "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", 586 | "requires": { 587 | "media-typer": "0.3.0", 588 | "mime-types": "2.1.17" 589 | } 590 | }, 591 | "uid-safe": { 592 | "version": "2.1.5", 593 | "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", 594 | "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", 595 | "requires": { 596 | "random-bytes": "1.0.0" 597 | } 598 | }, 599 | "unpipe": { 600 | "version": "1.0.0", 601 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 602 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 603 | }, 604 | "util-deprecate": { 605 | "version": "1.0.2", 606 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 607 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 608 | }, 609 | "utils-merge": { 610 | "version": "1.0.0", 611 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", 612 | "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=" 613 | }, 614 | "vary": { 615 | "version": "1.1.1", 616 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", 617 | "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=" 618 | } 619 | } 620 | } 621 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eros-pub", 3 | "private": true, 4 | "scripts": { 5 | "start": "node app" 6 | }, 7 | "dependencies": { 8 | "bcrypt-nodejs": "0.0.3", 9 | "body-parser": "^1.6.5", 10 | "connect-flash": "^0.1.1", 11 | "cookie-parser": "^1.3.2", 12 | "ejs": "^1.0.0", 13 | "express": "^4.0.0", 14 | "express-session": "^1.7.6", 15 | "js-md5": "^0.6.1", 16 | "moment": "^2.19.1", 17 | "mongoose": "^4.2.9" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/routes.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | var express = require("express"); 4 | var router = express.Router(); 5 | var Controllers = require('./controllers'); 6 | 7 | // router.use((req, res, next) => { 8 | // res.locals.currentUser = req.user; 9 | // res.locals.errors = req.flash("error"); 10 | // res.locals.infos = req.flash("info"); 11 | // next(); 12 | // }); 13 | 14 | // router.get("/", (req, res, next)=>{ 15 | // Model.find() 16 | // .sort({ createdAt: "descending" }) 17 | // .exec((err, users) => { 18 | // if (err) { return next(err); } 19 | // res.render("index", { users: users }); 20 | // }); 21 | // }); 22 | 23 | 24 | router.post("/app/add", Controllers.app.add); 25 | router.get("/app/list", Controllers.app.list); 26 | router.get("/app/check", Controllers.app.check); 27 | 28 | 29 | module.exports = router; -------------------------------------------------------------------------------- /server/utils/tool.js: -------------------------------------------------------------------------------- 1 | const format = ({resCode = 0, msg = 'success', data = {}}) => { 2 | return { 3 | resCode, 4 | msg, 5 | data 6 | } 7 | } 8 | 9 | 10 | module.exports = { 11 | format 12 | } --------------------------------------------------------------------------------