├── .editorconfig
├── .gitignore
├── .jshintignore
├── .jshintrc
├── .travis.yml
├── .yo-rc.json
├── Dockerfile
├── README.md
├── Wechat.jpeg
├── alipay.jpeg
├── ci.sh
├── circle.yml
├── client
├── README.md
├── app_table.vue
├── app_version.vue
├── bundle.js
├── index.html
├── login.vue
├── main.js
├── select.vue
├── table.vue
├── version.vue
└── zpagenav.vue
├── common
└── models
│ ├── app.js
│ ├── app.json
│ ├── appversion.js
│ ├── appversion.json
│ ├── version.js
│ └── version.json
├── daovoice.png
├── data.json
├── deployment.yaml
├── jsconfig.json
├── package.json
└── server
├── bin
├── automigration-db.js
└── create-admin.js
├── boot
├── authentication.js
└── root.js
├── component-config.json
├── config.json
├── datasources.json
├── middleware.json
├── middleware.production.json
├── model-config.json
├── private
├── certificate.pem
├── certrequest.csr
├── privatekey.pem
└── ssl_cert.js
└── server.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # http://editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 | indent_style = space
9 | indent_size = 2
10 | end_of_line = lf
11 | charset = utf-8
12 | trim_trailing_whitespace = true
13 | insert_final_newline = true
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.csv
2 | *.dat
3 | *.iml
4 | *.log
5 | *.out
6 | *.pid
7 | *.seed
8 | *.sublime-*
9 | *.swo
10 | *.swp
11 | *.tgz
12 | *.xml
13 | .DS_Store
14 | .idea
15 | .project
16 | .strong-pm
17 | coverage
18 | node_modules
19 | npm-debug.log
20 |
--------------------------------------------------------------------------------
/.jshintignore:
--------------------------------------------------------------------------------
1 | /client/
2 | /node_modules/
3 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": true,
5 | "camelcase": true,
6 | "eqeqeq": true,
7 | "eqnull": true,
8 | "immed": true,
9 | "indent": 2,
10 | "latedef": "nofunc",
11 | "newcap": true,
12 | "nonew": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": false,
18 | "trailing": true,
19 | "sub": true,
20 | "maxlen": 80
21 | }
22 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "4.1.2"
4 |
5 | script:
6 | - bash ./ci.sh
7 |
--------------------------------------------------------------------------------
/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-loopback": {}
3 | }
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # FROM ubuntu
2 | # MAINTAINER longshao
3 |
4 |
5 | # RUN apt-get update
6 | # RUN apt-get install -y nodejs
7 | # #RUN apt-get install -y nodejs=0.6.12~dfsg1-1ubuntu1
8 |
9 |
10 | # # Run app using node
11 | # CMD apt-get update; apt-get install -y nodejs; npm install; node /server/server.js
12 | # # CMD ["/usr/bin/node", "/server/server.js"]
13 |
14 | FROM node:argon
15 |
16 | # Create app directory
17 | RUN mkdir -p /usr/src/app
18 | WORKDIR /usr/src/app
19 |
20 | # Install app dependencies
21 | COPY package.json /usr/src/app/
22 | RUN npm install
23 |
24 | # Bundle app source
25 | COPY . /usr/src/app
26 |
27 | EXPOSE 4000
28 |
29 | # CMD [ "npm", "run build:js" ]
30 | #CMD [ "node", "." ]
31 | CMD npm run build:js && node .
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # generator-loopback-vue ``热更新,热部署 前后端分离实践``
2 | [![License][license-img]][license-url]
3 | [![NPM Version][npm-img]][npm-url]
4 | [![Node Version][node-image]][node-url]
5 | [![Build Status][travis-img]][travis-url]
6 | [![Downloads][downloads-image]][downloads-url]
7 |
8 | [](https://nodei.co/npm/generator-loopback-vue/)
9 |
10 | [travis-img]: https://travis-ci.org/qxl1231/generator-loopback-vue.svg?branch=master
11 | [travis-url]: https://travis-ci.org/qxl1231/generator-loopback-vue
12 | [npm-img]: https://img.shields.io/npm/v/generator-loopback-vue.svg
13 | [npm-url]: https://npmjs.org/package/generator-loopback-vue
14 | [david-img]: https://img.shields.io/david/qxl1231/generator-loopback-vue.svg
15 | [david-url]: https://david-dm.org/qxl1231/generator-loopback-vue
16 | [downloads-image]: https://img.shields.io/npm/dm/generator-loopback-vue.svg
17 | [downloads-url]: https://npmjs.org/package/generator-loopback-vue
18 | [license-img]: http://img.shields.io/badge/license-MIT-green.svg
19 | [license-url]: http://opensource.org/licenses/MIT
20 | [node-image]: https://img.shields.io/badge/node.js-v4.0.0-blue.svg
21 | [node-url]: http://nodejs.org/download/
22 |
23 | ## 首先,为什么需要前后端分离?强烈推荐看下
24 | 
25 | > http://2014.jsconf.cn/slides/herman-taobaoweb
26 |
27 | ## 以下是本项目的feature:(loopback中国讨论组-群号:575600225)
28 | - Strongloop是是如何通过一行命令和代码实现restful API接口,包括CRUD等14个接口
29 | - model是如何定义的,以及model-relation 定义,以及ACL接口权限控制等
30 | - CI持续集成的配置,Docker容器,docker部署文件
31 | - **如何登录鉴权,以及接口权限控制**
32 | - vue1.0/2.0 是如何玩?以及vue-resouce的使用,vue-router,webpack,babel等
33 | - loopback自带的authenticate 权限控制,accesstoken机制,credentials
34 | - strong-pm 部署命令,以及slc主要命令
35 | - 如何把项目部署在daocloud中,以及集成daovoice服务
36 | - ***增加了个vue-pagenav 组件的使用 实现分页功能***
37 |
38 |
39 | ## Docker镜像
40 | > 最新版本: latest
41 | > 镜像地址: daocloud.io/qxl1231/lb-vue2
42 |
43 | ## 在线demo:(使用第三方daocloud平台(免费版),每天手动启动,如遇服务挂了,请见谅^.^)
44 | > http://loopback-vue.daoapp.io/
45 | 帐号:test,密码:testpwd
46 |
47 | > http://loopback-vue.daoapp.io/explorer
48 |
49 |
50 | ## 部分截图
51 | 
52 | > 集成了daovoice玩玩哈哈~~~炫酷!客服系统很赞,还有统计图标等,注意这不是广告!!确实不错哦
53 | 
54 |
55 | 
56 |
57 | The project is generated by [LoopBack](http://loopback.io).+[vue.js](http://vuejs.org).
58 |
59 | ## 如何启动:(国内用cnpm(先运行npm i cnpm -g),国外用npm)
60 | ```
61 | 1. npm i
62 | 2. 修改datasources.json 中的数据库配置比如:localhost:27017
63 | 3. node server/bin/create-admin.js 添加管理员帐号,密码
64 | 3.npm run build:js & node .(cold reload) --hot reload npm run watch:js
65 | ```
66 |
67 | ## 遇到问题1:loopback+vue 不能运行
68 | > ```1.npm install 2.npm run build:js 3.node . ```
69 |
70 | ## 问题2:热部署
71 | > To use hot reload, please try this command:
72 | ```npm run watch:js & node . ```
73 |
74 | ## 热启动:
75 | > ```npm run watch:js & node . ```
76 |
77 |
78 | ## 问题3:If you have error, try this:
79 |
80 | ```
81 | npm install
82 | vueify-insert-css vue-hot-reload-api
83 | babel-core babel-preset-es2015
84 | babel-plugin-transform-runtime babel-runtime@5
85 | --save-dev
86 | ```
87 |
88 | ## Hot reloading detail:
89 | > https://github.com/vuejs/vueify
90 |
91 | ## 其他help===>loopback 常用命令行:
92 | > - slc loopback 初始化项目
93 | > - slc loopback:datasource
94 | > - slc loopback:model
95 | > - slc loopback:relation
96 | > - slc loopback boot-script
97 |
98 |
99 | ## others:deploy and status
100 |
101 | > - slc deploy http://usr:pwd@localhost:port
102 | > - slpmctl -C http://usr:pwd@localhost:8701 ls
103 |
104 | > - slpmctl -C http://usr:pwd@domain:8701 status
105 |
106 | > - pm2 start -n weather app.js
107 |
108 | > - pm2 start -n app_update_server server.js
109 |
110 | ## LICENSE
111 |
112 | MIT
113 |
114 |
115 | ## 捐赠
116 | | No | ID | github |
117 | | ------------- |:-------------:| -----:|
118 | | 1 | JLF | https://github.com/cnJLF |
119 | | 2 | 萧大大 | https://github.com/freemember007 |
120 | | 3 | you | |
121 | | 4 | are next | |
122 |
123 | > ❤❤❤❤感谢我的兄弟们大力支持❤❤❤❤❤❤:
124 | - No.1 [JLF]:https://github.com/cnJLF
125 | - No.2 [萧大大]:https://github.com/freemember007
126 |
127 |
128 |  | 
129 |
--------------------------------------------------------------------------------
/Wechat.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qxl1231/generator-loopback-vue/60648743b1db9cfeedc09190639b87eea17af048/Wechat.jpeg
--------------------------------------------------------------------------------
/alipay.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qxl1231/generator-loopback-vue/60648743b1db9cfeedc09190639b87eea17af048/alipay.jpeg
--------------------------------------------------------------------------------
/ci.sh:
--------------------------------------------------------------------------------
1 | npm i
2 | npm run build:js
3 |
4 |
5 |
--------------------------------------------------------------------------------
/circle.yml:
--------------------------------------------------------------------------------
1 | machine:
2 | node:
3 | version: 5
4 |
5 | test:
6 | override:
7 | - bash ./ci.sh
8 |
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | ## Client
2 |
3 | This is the place for your application front-end files.
4 |
--------------------------------------------------------------------------------
/client/app_table.vue:
--------------------------------------------------------------------------------
1 |
2 |
37 |
38 |
39 |
40 |
136 |
--------------------------------------------------------------------------------
/client/app_version.vue:
--------------------------------------------------------------------------------
1 |
2 |
45 |
46 |
47 |
159 |
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | appVersion
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/client/login.vue:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
64 |
65 |
66 |
194 |
--------------------------------------------------------------------------------
/client/main.js:
--------------------------------------------------------------------------------
1 | var Vue = require('vue');
2 | var table = require('./table.vue');
3 | var Version = require('./version.vue');
4 | var login = require('./login.vue');
5 | var select = require('./select.vue');
6 | var app_version = require('./app_version.vue');
7 | var app_table = require('./app_table.vue');
8 |
9 | var VueRouter = require('vue-router');
10 |
11 | Vue.use(require('vue-resource'));
12 | Vue.use(VueRouter);
13 |
14 | var router = new VueRouter();
15 |
16 | var app = Vue.extend({});
17 |
18 | router.map({
19 | '/': { //登录
20 | component: login
21 | },
22 | '/login': {
23 | component: login
24 | },
25 | /* 404路由 */
26 | '*': {
27 | component: login
28 | },
29 | '/appversion': { //主页
30 | component: table
31 | },
32 | '/select': {
33 | component: select
34 | },
35 | '/app_version': { //主页
36 | component: app_table
37 | }
38 | });
39 |
40 | router.start(app, "#app");
41 |
--------------------------------------------------------------------------------
/client/select.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
选择操作
4 |
5 |
6 |
7 |
27 |
28 |
29 |
30 |
31 |
57 |
--------------------------------------------------------------------------------
/client/table.vue:
--------------------------------------------------------------------------------
1 |
2 |
44 |
45 |
46 |
47 |
48 |
49 |
158 |
--------------------------------------------------------------------------------
/client/version.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
17 |
60 |
61 |
62 |
63 |
64 |
65 |
196 |
--------------------------------------------------------------------------------
/client/zpagenav.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/common/models/app.js:
--------------------------------------------------------------------------------
1 | module.exports = function (App) {
2 | App.prototype.checkVersion = function checkVersion(device_deploy_uuid, device_app_version, device_platform, cb) {
3 | var compatible_binary = false;
4 | var update_available = false;
5 | var comp_result = false;
6 | var zipurl = '';
7 |
8 | this.versions(function (err, results) {
9 | if (err) {
10 | return cb(err);
11 | }
12 | var bestUpdate = {compatible_binary: false, update_available: false, device_deploy_uuid: '0.0.0', zipurl: '' };
13 |
14 | for (i = 0; i < results.length; i++) {
15 | var latest_h5 = results[i]['h5Version'];
16 | var obj = results[i];
17 | var max = results[i]['binMax'];
18 | var min = results[i]['binMin'];
19 | var versionType = results[i]['versionType'];
20 |
21 | if (device_platform.toLowerCase() == versionType.toLowerCase()) {
22 | var comp_result = compareH5(device_deploy_uuid, device_app_version, latest_h5, max, min, obj);
23 |
24 | if (comp_result.compatible_binary && !bestUpdate.compatible_binary) {
25 | bestUpdate = comp_result;
26 | } else if (comp_result.update_available && !bestUpdate.update_available) {
27 | bestUpdate = comp_result;
28 | } else if (comp_result.compatible_binary && comp_result.update_available && (compare(comp_result.device_deploy_uuid, bestUpdate.device_deploy_uuid) === 1)){
29 | bestUpdate = comp_result;
30 | }
31 | }
32 | }
33 |
34 | cb(null, bestUpdate.compatible_binary, bestUpdate.update_available, {uuid: bestUpdate.device_deploy_uuid, url: bestUpdate.zipurl});
35 |
36 | });
37 | };
38 |
39 | var compareH5 = function compareH5(device_deploy_uuid, device_app_version, latest_h5, max, min, obj, cb) {
40 |
41 | var update = {};
42 | update.device_deploy_uuid = '';
43 | update.zipurl = '';
44 | update.compatible_binary = false;
45 | update.update_available = false;
46 |
47 | if (compare(device_app_version, min) >= 0 && compare(max, device_app_version) >= 0) {
48 | update.compatible_binary = true;
49 | if (compare(latest_h5, device_deploy_uuid) === 1) {
50 | update.update_available = true;
51 | update.device_deploy_uuid = latest_h5;
52 | update.zipurl = obj['url'];
53 | }
54 | }
55 |
56 | return update;
57 | };
58 |
59 | var compare = function compare(v1, v2) {
60 | // var v1_tmp = v1, v2_tmp = v2;
61 | // v1 = v1.replace(/\./g, '');
62 | // v2 = v2.replace(/\./g, '');
63 | // // console.log("去掉小数点:", v1, v2);
64 | // var len = Math.max(v1.length, v2.length);
65 | // v1 = v1 + Array(len - v1.length + 1).join(0);
66 | // v2 = v2 + Array(len - v2.length + 1).join(0);
67 | // // console.log("补长对齐:", v1, v2);
68 | // v1 = parseInt(v1);
69 | // v2 = parseInt(v2);
70 | // // console.log("去掉前导0:", v1, v2);
71 | // // console.log("大的是:", Math.max(v1, v2));
72 | // var ret = v1 > v2 ? 1 : v1 == v2 ? 0 : -1;
73 | // // console.log("compare:", v1_tmp, " ? ", v2_tmp, " : ", ret);
74 | // return ret;
75 |
76 | var newVersion = v1, curVersion = v2;
77 | if (curVersion === undefined) {
78 | curVersion = '0.0.0';
79 | }
80 | console.log('newVersion: ' + newVersion + ' , curVersion: ' + curVersion);
81 | var newVersions = newVersion.split('.');
82 | var curVersions = curVersion.split('.');
83 | var length = newVersions.length;
84 | for (var i = 0; i < length; i++) {
85 | var newVersionValue = newVersions[i] === undefined ? 0 : newVersions[i];
86 | var newVer = parseInt(newVersionValue);
87 | var curVersionValue = curVersions[i] === undefined ? 0 : curVersions[i];
88 | var curVer = parseInt(curVersionValue);
89 | console.log('newVer : ' + newVer + ', curVer: ' + curVer);
90 | if (newVer < curVer) {
91 | return -1;
92 | } else if(newVer > curVer) {
93 | return 1;
94 | }
95 | }
96 | return 0;
97 | }
98 |
99 | App.remoteMethod('checkVersion', {
100 | isStatic: false,
101 |
102 | accepts: [
103 | {arg: 'device_deploy_uuid', type: 'string'},
104 | {arg: 'device_app_version', type: 'string'},
105 | {arg: 'device_platform', type: 'string'}
106 | ],
107 |
108 | returns: [
109 | {arg: 'compatible_binary', type: 'boolean'},
110 | {arg: 'update_available', type: 'boolean'},
111 | {arg: 'update', type: 'object'}
112 | ],
113 |
114 | http: {path: '/updates/check', verb: 'post'}
115 | });
116 |
117 | App.prototype.checkAppVersion = function checkAppVersion(cb) {
118 | console.log('checkAppVersion......');
119 | this.appversions(function (err, results) {
120 | if (err) {
121 | return cb(err);
122 | }
123 | var bestUpdate = results[0];
124 | for (i = 0; i < results.length; i++) {
125 | var latest_verCode = results[i]['verCode'];
126 | var obj = results[i];
127 | console.log('bestUpdate : ' + JSON.stringify(bestUpdate) + ' , latest_verCode: ' + latest_verCode);
128 | if (compare(latest_verCode, bestUpdate.verCode) === 1) {
129 | bestUpdate = obj;
130 | }
131 | }
132 | if (bestUpdate != null) {
133 | cb(null, bestUpdate.verCode, bestUpdate.releaseNotes, bestUpdate.apkPath);
134 | } else {
135 | cb(null, '', '', '');
136 | }
137 | });
138 | };
139 |
140 | App.remoteMethod('checkAppVersion', {
141 | isStatic: false,
142 |
143 | accepts: [],
144 |
145 | returns: [
146 | {arg: 'verCode', type: 'string'},
147 | {arg: 'releaseNotes', type: 'string'},
148 | {arg: 'apkPath', type: 'string'}
149 | ],
150 |
151 | http: {path: '/updates/checkApp', verb: 'get'}
152 | });
153 | };
154 |
--------------------------------------------------------------------------------
/common/models/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "base": "PersistedModel",
4 | "idInjection": false,
5 | "options": {
6 | "validateUpsert": true
7 | },
8 | "properties": {
9 | "id": {
10 | "type": "number",
11 | "id": true,
12 | "required": true
13 | },
14 | "name": {
15 | "type": "string",
16 | "required": true
17 | },
18 | "createTime": {
19 | "type": "date"
20 | },
21 | "updateTime": {
22 | "type": "date"
23 | }
24 | },
25 | "validations": [],
26 | "relations": {
27 | "versions": {
28 | "type": "hasMany",
29 | "model": "version",
30 | "foreignKey": ""
31 | },
32 | "appversions": {
33 | "type": "hasMany",
34 | "model": "appversion",
35 | "foreignKey": ""
36 | }
37 | },
38 | "acls": [
39 | {
40 | "accessType": "*",
41 | "principalType": "ROLE",
42 | "principalId": "$everyone",
43 | "permission": "DENY"
44 | },
45 | {
46 | "accessType": "*",
47 | "principalType": "ROLE",
48 | "principalId": "admin",
49 | "permission": "ALLOW"
50 | },
51 | {
52 | "principalType": "ROLE",
53 | "principalId": "$everyone",
54 | "permission": "ALLOW",
55 | "property": "checkVersion"
56 | },
57 | {
58 | "principalType": "ROLE",
59 | "principalId": "$everyone",
60 | "permission": "ALLOW",
61 | "property": "checkAppVersion"
62 | }
63 | ],
64 | "methods": {}
65 | }
66 |
--------------------------------------------------------------------------------
/common/models/appversion.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(Appversion) {
4 |
5 | };
6 |
--------------------------------------------------------------------------------
/common/models/appversion.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "appversion",
3 | "base": "PersistedModel",
4 | "idInjection": true,
5 | "options": {
6 | "validateUpsert": true
7 | },
8 | "properties": {
9 | "verCode": {
10 | "type": "string",
11 | "required": true
12 | },
13 | "releaseNotes": {
14 | "type": "string",
15 | "required": true
16 | },
17 | "apkPath": {
18 | "type": "string",
19 | "required": true
20 | }
21 | },
22 | "validations": [],
23 | "relations": {},
24 | "acls": [
25 | {
26 | "accessType": "*",
27 | "principalType": "ROLE",
28 | "principalId": "$everyone",
29 | "permission": "DENY"
30 | },
31 | {
32 | "accessType": "*",
33 | "principalType": "ROLE",
34 | "principalId": "admin",
35 | "permission": "ALLOW"
36 | }
37 | ],
38 | "methods": {}
39 | }
40 |
--------------------------------------------------------------------------------
/common/models/version.js:
--------------------------------------------------------------------------------
1 | module.exports = function(Version) {
2 |
3 | };
4 |
--------------------------------------------------------------------------------
/common/models/version.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "version",
3 | "base": "PersistedModel",
4 | "idInjection": true,
5 | "options": {
6 | "validateUpsert": true
7 | },
8 | "properties": {
9 | "versionType": {
10 | "type": "string",
11 | "required": true
12 | },
13 | "url": {
14 | "type": "string"
15 | },
16 | "createTime": {
17 | "type": "date"
18 | },
19 | "h5Version": {
20 | "type": "string"
21 | },
22 | "binMax": {
23 | "type": "string"
24 | },
25 | "binMin": {
26 | "type": "string"
27 | },
28 | "updateTime": {
29 | "type": "date"
30 | }
31 | },
32 | "validations": [],
33 | "relations": {},
34 | "acls": [
35 | {
36 | "accessType": "*",
37 | "principalType": "ROLE",
38 | "principalId": "$everyone",
39 | "permission": "DENY"
40 | },
41 | {
42 | "accessType": "*",
43 | "principalType": "ROLE",
44 | "principalId": "admin",
45 | "permission": "ALLOW"
46 | }
47 | ],
48 | "methods": {}
49 | }
50 |
--------------------------------------------------------------------------------
/daovoice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qxl1231/generator-loopback-vue/60648743b1db9cfeedc09190639b87eea17af048/daovoice.png
--------------------------------------------------------------------------------
/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "ids": {
3 | "User": 2,
4 | "AccessToken": 1,
5 | "ACL": 1,
6 | "RoleMapping": 2,
7 | "Role": 2,
8 | "app": 1,
9 | "version": 1,
10 | "appversion": 1
11 | },
12 | "models": {
13 | "User": {
14 | "1": "{\"username\":\"test\",\"password\":\"$2a$10$6lu5b7RFyuJFG8Nf.PfaLO6Qz/EGYPCrXO7zD1tzxc/uHw9LQ29Li\",\"id\":1,\"emailVerified\":true,\"verificationToken\":null}"
15 | },
16 | "AccessToken": {},
17 | "ACL": {},
18 | "RoleMapping": {
19 | "1": "{\"principalType\":\"USER\",\"principalId\":\"1\",\"roleId\":1,\"id\":1}"
20 | },
21 | "Role": {
22 | "1": "{\"name\":\"admin\",\"created\":\"2016-11-30T00:45:55.533Z\",\"modified\":\"2016-11-30T00:45:55.533Z\",\"id\":1}"
23 | },
24 | "app": {},
25 | "version": {},
26 | "appversion": {}
27 | }
28 | }
--------------------------------------------------------------------------------
/deployment.yaml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES6",
4 | "module": "commonjs"
5 | }
6 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "generator-loopback-vue",
3 | "version": "2.1.4",
4 | "main": "server/server.js",
5 | "scripts": {
6 | "build:js": "browserify -t vueify ${npm_package_config_src_js} > ${npm_package_config_dist_js}",
7 | "pretest": "jshint .",
8 | "watch:js": "watchify -p browserify-hmr -t vueify ${npm_package_config_src_js} -o ${npm_package_config_dist_js} -dv"
9 | },
10 | "dependencies": {
11 | "compression": "^1.0.3",
12 | "cors": "^2.5.2",
13 | "loopback": "^2.22.0",
14 | "loopback-boot": "^2.6.5",
15 | "loopback-component-explorer": "^2.1.0",
16 | "loopback-connector-mongodb": "^1.13.0",
17 | "loopback-datasource-juggler": "^2.39.0",
18 | "serve-favicon": "^2.0.1",
19 | "vue-pagenav": "^1.5.0",
20 | "vue-resource": "^0.1.17",
21 | "vue-router": "^0.7.11",
22 | "vueify": "^6.0.0"
23 | },
24 | "devDependencies": {
25 | "babel-plugin-transform-runtime": "^6.1.2",
26 | "babel-preset-es2015": "^6.1.2",
27 | "babel-runtime": "^6.0.14",
28 | "browserify": "^12.0.1",
29 | "browserify-hmr": "^0.3.1",
30 | "jshint": "^2.5.6",
31 | "mermaid": "^0.5.5",
32 | "vue": "^1.0.7",
33 | "vue-hot-reload-api": "^1.2.1",
34 | "vueify-insert-css": "^1.0.0",
35 | "watchify": "^3.6.0"
36 | },
37 | "repository": {
38 | "type": "git",
39 | "url": "https://github.com/qxl1231/generator-loopback-vue.git"
40 | },
41 | "keywords": [
42 | "loopback-vue"," yeoman-generator",
43 | "node",
44 | "js"
45 | ],
46 | "author": "qiangxl:wechat(278931058)",
47 | "license": "MIT",
48 | "description": "loopback+vue+vue-resource,前后端分离模板,vue page分页功能,authenticate 权限控制,accesstoken机制,credentials,CI,docker ,hot reload vue",
49 | "config": {
50 | "src_js": "client/main.js",
51 | "dist_js": "client/bundle.js"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/server/bin/automigration-db.js:
--------------------------------------------------------------------------------
1 | /* jshint node:true */
2 | 'use strict';
3 | var app = require('../server');
4 |
5 | var dataSource = app.dataSources.appversion;
6 |
7 | dataSource.automigrate(function(err){
8 | if (err) {
9 | console.log("Migrate model db error:", err);
10 | return;
11 | }
12 |
13 | console.log("Migrate model db success");
14 | dataSource.disconnect(function(err){
15 | if (err) throw err;
16 | });
17 | });
18 |
19 |
20 |
--------------------------------------------------------------------------------
/server/bin/create-admin.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var app = require(path.resolve(__dirname, '../server'));
3 |
4 | app.models.User.validations.email =
5 | [
6 | { validation: 'format',
7 | allowNull: true,
8 | allowBlank: true,
9 | message: 'Must provide a valid email',
10 | with: /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
11 | options: {} },
12 | { validation: 'uniqueness',
13 | message: 'Email already exists',
14 | options: {async: true}}
15 | ];
16 |
17 | app.models.User.create([{
18 | username: 'test',
19 | password: 'testpwd'
20 | }], function(err, users) {
21 | if (err) throw err;
22 | console.log('Admin account created.');
23 |
24 | app.models.User.generateVerificationToken(users[0].id, function(err, token){
25 | app.models.User.confirm(users[0].id, token.id, null, function(err, result) {
26 | if (err) throw err;
27 | console.log('Automatic verified; Ok to login');
28 | });
29 | });
30 |
31 | //create the admin role
32 | app.models.Role.create({
33 | name: 'admin'
34 | }, function(err, role) {
35 | if (err) throw err;
36 |
37 | console.log('Created role:', role);
38 |
39 | //assign an admin
40 | role.principals.create({
41 | principalType: app.models.RoleMapping.USER,
42 | principalId: users[0].id
43 | }, function(err, principal) {
44 | if (err) throw err;
45 |
46 | console.log('Created principal:', principal);
47 | // app.dataSources.appversion.disconnect(function(err){
48 | // if (err) throw err;
49 | // });
50 | });
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/server/boot/authentication.js:
--------------------------------------------------------------------------------
1 | module.exports = function enableAuthentication(server) {
2 | // enable authentication
3 | server.enableAuth();
4 | };
5 |
--------------------------------------------------------------------------------
/server/boot/root.js:
--------------------------------------------------------------------------------
1 | module.exports = function(server) {
2 | // Install a `/` route that returns server status
3 | var router = server.loopback.Router();
4 | //router.get('/', server.loopback.status());
5 | server.use(router);
6 | };
7 |
--------------------------------------------------------------------------------
/server/component-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "loopback-component-explorer": {
3 | "mountPath": "/explorer"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/server/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "restApiRoot": "/api/v1",
3 | "host": "0.0.0.0",
4 | "port": 4000,
5 | "https-port": 4015,
6 | "remoting": {
7 | "context": {
8 | "enableHttpContext": false
9 | },
10 | "rest": {
11 | "normalizeHttpPath": false,
12 | "xml": false
13 | },
14 | "json": {
15 | "strict": false,
16 | "limit": "100kb"
17 | },
18 | "urlencoded": {
19 | "extended": true,
20 | "limit": "100kb"
21 | },
22 | "cors": false,
23 | "errorHandler": {
24 | "disableStackTrace": false
25 | }
26 | },
27 | "legacyExplorer": false
28 | }
29 |
--------------------------------------------------------------------------------
/server/datasources.json:
--------------------------------------------------------------------------------
1 | {
2 | "db": {
3 | "name": "db",
4 | "connector": "memory",
5 | "file":"data.json"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/server/middleware.json:
--------------------------------------------------------------------------------
1 | {
2 | "initial:before": {
3 | "loopback#favicon": {}
4 | },
5 | "initial": {
6 | "compression": {},
7 | "cors": {
8 | "params": {
9 | "origin": true,
10 | "credentials": true,
11 | "maxAge": 86400
12 | }
13 | }
14 | },
15 | "session": {},
16 | "auth": {},
17 | "parse": {},
18 | "routes": {
19 | "loopback#rest": {
20 | "paths": [
21 | "${restApiRoot}"
22 | ]
23 | }
24 | },
25 | "files": {
26 | "loopback#static": {
27 | "params": "$!../client"
28 | }
29 | },
30 | "final": {
31 | "loopback#urlNotFound": {}
32 | },
33 | "final:after": {
34 | "loopback#errorHandler": {}
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/server/middleware.production.json:
--------------------------------------------------------------------------------
1 | {
2 | "final:after": {
3 | "loopback#errorHandler": {
4 | "params": {
5 | "includeStack": false
6 | }
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/server/model-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "_meta": {
3 | "sources": [
4 | "loopback/common/models",
5 | "loopback/server/models",
6 | "../common/models",
7 | "./models"
8 | ],
9 | "mixins": [
10 | "loopback/common/mixins",
11 | "loopback/server/mixins",
12 | "../common/mixins",
13 | "./mixins"
14 | ]
15 | },
16 | "User": {
17 | "dataSource": "db"
18 | },
19 | "AccessToken": {
20 | "dataSource": "db",
21 | "public": false
22 | },
23 | "ACL": {
24 | "dataSource": "db",
25 | "public": false
26 | },
27 | "RoleMapping": {
28 | "dataSource": "db",
29 | "public": false
30 | },
31 | "Role": {
32 | "dataSource": "db",
33 | "public": false
34 | },
35 | "app": {
36 | "dataSource": "db",
37 | "public": true
38 | },
39 | "version": {
40 | "dataSource": "db",
41 | "public": true
42 | },
43 | "appversion": {
44 | "dataSource": "db",
45 | "public": true
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/server/private/certificate.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICQTCCAaoCCQCH2M7U6KhP0zANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJD
3 | TjENMAsGA1UECBMEd3V4aTENMAsGA1UEBxMEd3V4aTETMBEGA1UEChMKTGl0dGxl
4 | c3dhbjEjMCEGCSqGSIb3DQEJARYUbGl0dGxlc3dhbkBtaWRlYS5jb20wHhcNMTYx
5 | MjEzMDY1ODI1WhcNMTcwMTEyMDY1ODI1WjBlMQswCQYDVQQGEwJDTjENMAsGA1UE
6 | CBMEd3V4aTENMAsGA1UEBxMEd3V4aTETMBEGA1UEChMKTGl0dGxlc3dhbjEjMCEG
7 | CSqGSIb3DQEJARYUbGl0dGxlc3dhbkBtaWRlYS5jb20wgZ8wDQYJKoZIhvcNAQEB
8 | BQADgY0AMIGJAoGBAK+C3Y1uhhJAY2H3st2OVa8PnczcniO5B5AWyP+Z0TjsWJwv
9 | lfKYHDmMDVpSlHJC+//8+rZZPg6IEWPnB1oIRzyr+CXnpqxORNfq28xdFHve7pzy
10 | SxEwKjN0WFf8fW0ylxxaL4v5nymGLy5fBacinv9RCJ9F6zlWppxlvzSvEG+xAgMB
11 | AAEwDQYJKoZIhvcNAQEFBQADgYEAkYoWAa+LuBIF72gWU7F+qllXiLYZDP+ZnBgK
12 | xOwwjZ57kFbrV/m0IbONYboxWtTc+hjNhkMSXOt3HPzAetJtcwmmq/Ofk0ry6OhV
13 | T+YwiwHjMEJQYOxYf/wVv9J5ubxDt/ijd26Or2sL5qaSczO6kYziMBVxStUXoGtG
14 | MpVzImA=
15 | -----END CERTIFICATE-----
16 |
--------------------------------------------------------------------------------
/server/private/certrequest.csr:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE REQUEST-----
2 | MIIBvjCCAScCAQAwZTELMAkGA1UEBhMCQ04xDTALBgNVBAgTBHd1eGkxDTALBgNV
3 | BAcTBHd1eGkxEzARBgNVBAoTCkxpdHRsZXN3YW4xIzAhBgkqhkiG9w0BCQEWFGxp
4 | dHRsZXN3YW5AbWlkZWEuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCv
5 | gt2NboYSQGNh97LdjlWvD53M3J4juQeQFsj/mdE47FicL5XymBw5jA1aUpRyQvv/
6 | /Pq2WT4OiBFj5wdaCEc8q/gl56asTkTX6tvMXRR73u6c8ksRMCozdFhX/H1tMpcc
7 | Wi+L+Z8phi8uXwWnIp7/UQifRes5VqacZb80rxBvsQIDAQABoBkwFwYJKoZIhvcN
8 | AQkHMQoTCDEyMzQ1Njc4MA0GCSqGSIb3DQEBBQUAA4GBAClB/ND2vpOqmokHLWKa
9 | s5x5SLaA2uVbB+hv7Qquhi7Y72F/6SVN6VXdyd7J2VV8xLdQXijuiJiPU+KIphb7
10 | 5UInRTkxUU6ZxMT8iTCZ88WgflZGcAS3F8SuOQlWpQb8KrCVW32e2JYfg5HehfNl
11 | DGYhTVtvJjD4Xf4ssjbx3ixi
12 | -----END CERTIFICATE REQUEST-----
13 |
--------------------------------------------------------------------------------
/server/private/privatekey.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICWwIBAAKBgQCvgt2NboYSQGNh97LdjlWvD53M3J4juQeQFsj/mdE47FicL5Xy
3 | mBw5jA1aUpRyQvv//Pq2WT4OiBFj5wdaCEc8q/gl56asTkTX6tvMXRR73u6c8ksR
4 | MCozdFhX/H1tMpccWi+L+Z8phi8uXwWnIp7/UQifRes5VqacZb80rxBvsQIDAQAB
5 | AoGABAvpXcRplOwlHnIejpskgJfElJ+Vic9PTFQqKDJ8r2pLlLZIJ8K4C9+dwzJa
6 | N9QxiTJk+WVPV/htJjeCZOIB+mtaNTNBV7o4ACseCCGJdgnjxwTRtrHrNamwdoLb
7 | He+ie3q2OR8DN3W8h1QYUN6DPr9QZ5O89UJHa3PX/zMjJq0CQQDWbm3bC4pRygPk
8 | kJKjE9jMH4tMfESEl5+xjB9TDWDXCEESJrIgPErPTPiVIqiDJGopDIMOnWXi1OPR
9 | c78r7NODAkEA0Yjyl3EeVU3dfgr4vaEnVZfaJRc513g3x9vxnnz1smbaZsAyP6lF
10 | OVecL0M70GxOlLq7HJ0d9Xuy8ZJFX4AluwJAcKbnM9KmQj29OdDyGJaOqP5Rckc+
11 | v+HgVahltH4syAtgFCccIW9LZgjr932TxgHVe+dE2uK56icp4JnMb0kl/QJATW4I
12 | B4KbrPY7NUqkpJ6axHx69l3g2AjqxAY5AVI4ERcE+hdn+jJ5DDmd07FKtzhuyXM/
13 | ldFRstdlRvPAgwv95QJAHqLbQRH797nxoBbiK/f+vTA70THq3/HftOsWRPt+SCDa
14 | 24cmL5qMMGKl1fUidpqFB+3/YTN2F+3QXxJrEFR6mA==
15 | -----END RSA PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/server/private/ssl_cert.js:
--------------------------------------------------------------------------------
1 | // Copyright IBM Corp. 2014,2015. All Rights Reserved.
2 | // Node module: strong-gateway
3 | // US Government Users Restricted Rights - Use, duplication or disclosure
4 | // restricted by GSA ADP Schedule Contract with IBM Corp.
5 |
6 | var crypto = require('crypto');
7 | var tls = require('tls');
8 | var fs = require('fs');
9 | var path = require('path');
10 |
11 | exports.privateKey = fs.readFileSync(path.join(__dirname, 'privatekey.pem'))
12 | .toString();
13 | exports.certificate = fs.readFileSync(path.join(__dirname, 'certificate.pem'))
14 | .toString();
15 |
16 | if (typeof tls.createSecureContext === 'function') {
17 | exports.credentials = tls.createSecureContext(
18 | {key: exports.privateKey, cert: exports.certificate});
19 | } else {
20 | exports.credentials = crypto.createCredentials(
21 | {key: exports.privateKey, cert: exports.certificate});
22 | }
23 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | // var loopback = require('loopback');
2 | // var boot = require('loopback-boot');
3 | // var path = require('path');
4 | //
5 | // var app = module.exports = loopback();
6 | //
7 | // app.start = function () {
8 | // // start the web server
9 | // return app.listen(function () {
10 | // app.emit('started');
11 | // var baseUrl = app.get('url').replace(/\/$/, '');
12 | // console.log('Web server listening at: %s', baseUrl);
13 | // if (app.get('loopback-component-explorer')) {
14 | // var explorerPath = app.get('loopback-component-explorer').mountPath;
15 | // console.log('Browse your REST API at %s%s', baseUrl, explorerPath);
16 | // }
17 | // });
18 | // };
19 | //
20 | // //var staticPath = null;
21 | // //
22 | // //staticPath = path.resolve(__dirname, '../client/');
23 | // //console.log("Running app in development mode");
24 | // //
25 | // //app.use(loopback.static(staticPath));
26 | //
27 | // // Bootstrap the application, configure models, datasources and middleware.
28 | // // Sub-apps like REST API are mounted via boot scripts.
29 | // boot(app, __dirname, function (err) {
30 | // if (err) throw err;
31 | //
32 | // // start the server if `$ node server.js`
33 | // if (require.main === module)
34 | // app.start();
35 | // });
36 |
37 | var loopback = require('loopback');
38 | var boot = require('loopback-boot');
39 |
40 | var http = require('http');
41 | var https = require('https');
42 | var sslCert = require('./private/ssl_cert');
43 | var httpsOptions = {
44 | key: sslCert.privateKey,
45 | cert: sslCert.certificate
46 | };
47 | var app = module.exports = loopback();
48 |
49 | // boot scripts mount components like REST API
50 | boot(app, __dirname);
51 |
52 | app.start = function() {
53 | // start the web server
54 | // return app.listen(function() {
55 | // app.emit('started');
56 | // var baseUrl = app.get('url').replace(/\/$/, '');
57 | // console.log('Web server listening at: %s', baseUrl);
58 | // if (app.get('loopback-component-explorer')) {
59 | // var explorerPath = app.get('loopback-component-explorer').mountPath;
60 | // console.log('Browse your REST API at %s%s', baseUrl, explorerPath);
61 | // }
62 | // });
63 |
64 | var port = app.get('port');
65 | var host = app.get('host');
66 | var httpServer = http.createServer(app).listen(port, host, function() {
67 |
68 | var httpsPort = app.get('https-port');
69 | var httpsServer = https.createServer(httpsOptions, app).listen(httpsPort,
70 | host, function() {
71 |
72 | app.emit('started');
73 |
74 | app.close = function(cb) {
75 | app.removeAllListeners('started');
76 | app.removeAllListeners('loaded');
77 | httpServer.close(function() {
78 | httpsServer.close(cb);
79 | });
80 | };
81 | });
82 | });
83 | };
84 |
85 | // start the server if `$ node server.js`
86 | if (require.main === module) {
87 | app.start();
88 | }
--------------------------------------------------------------------------------