├── .bowerrc
├── config
├── release.json
└── default.json
├── app.js
├── favicon.ico
├── public
└── src
│ ├── img
│ ├── favicon.ico
│ └── logo@2x.jpg
│ ├── views
│ ├── components
│ │ └── issue.html
│ ├── index.html
│ ├── 404.html
│ └── layout.html
│ ├── js
│ ├── libraries.js
│ ├── global.js
│ └── main.js
│ └── less
│ └── app.less
├── lib
├── controller
│ └── get_index.js
├── service
│ ├── limiter.js
│ ├── morgan.js
│ └── redis.js
├── api
│ ├── info.js
│ └── user.js
├── router.js
└── app.js
├── pm2
└── release.json
├── .travis.yml
├── bower.json
├── locales
├── zh.json
└── en.json
├── .gitignore
├── test
└── index.js
├── LICENSE
├── README.md
├── package.json
├── gulpfile.js
└── npm-shrinkwrap.json
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "public/bower"
3 | }
4 |
--------------------------------------------------------------------------------
/config/release.json:
--------------------------------------------------------------------------------
1 | {
2 | "publicPath": "public/dist"
3 | }
4 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | module.exports = require('./lib/app')
4 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alibaba-archive/spa-seed/HEAD/favicon.ico
--------------------------------------------------------------------------------
/public/src/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alibaba-archive/spa-seed/HEAD/public/src/img/favicon.ico
--------------------------------------------------------------------------------
/public/src/img/logo@2x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alibaba-archive/spa-seed/HEAD/public/src/img/logo@2x.jpg
--------------------------------------------------------------------------------
/public/src/views/components/issue.html:
--------------------------------------------------------------------------------
1 |
2 | Hello, teambition community!
3 |
4 |
--------------------------------------------------------------------------------
/public/src/js/libraries.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | /* global module, define */
3 |
4 | define(['jquery', 'bootstrap', 'thunks'], function () {})
5 |
--------------------------------------------------------------------------------
/lib/controller/get_index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | exports.getIndex = function * () {
4 | yield this.render('index', {
5 | user: this.state.user
6 | })
7 | }
8 |
--------------------------------------------------------------------------------
/pm2/release.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "spa",
3 | "script": "./app.js",
4 | "exec_mode": "cluster_mode",
5 | "instances": 2,
6 | "env": {
7 | "NODE_ENV": "release"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/public/src/views/index.html:
--------------------------------------------------------------------------------
1 |
2 | <%= it.__('welcome') %>
3 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/public/src/views/404.html:
--------------------------------------------------------------------------------
1 |
2 | <%= it.__('welcome') %>
3 |
4 | Not found!
5 |
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - "4"
5 | - "6"
6 | - "7"
7 | services:
8 | - redis-server
9 | before_script:
10 | - npm install -g bower
11 | - bower install
12 | - npm install
13 | - gulp
14 | script: "npm test"
15 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "spa-seed",
3 | "version": "0.6.0",
4 | "dependencies": {
5 | "requirejs": "^2.1.15",
6 | "jquery": "^2.2.0",
7 | "almond": "^0.3.1",
8 | "thunks": "^4.1.3",
9 | "bootstrap": "^3.3.6"
10 | },
11 | "devDependencies": {}
12 | }
13 |
--------------------------------------------------------------------------------
/locales/zh.json:
--------------------------------------------------------------------------------
1 | {
2 | "seoDescription": "Teambition 是一个简单,高效的项目协作工具,你可以在这里管理项目,跟踪任务进度,存储项目文件,让你的团队协作更高效。",
3 | "seoKeywords": "团队协作工具,团队管理工具,项目管理工具,项目协作工具,任务分配,任务进度,文件存储,文件共享,在线预览,知识管理,日程管理,项目管理,项目讨论,项目沟通",
4 | "seoTitle": "Teambition | SPA-Seed",
5 | "welcome": "欢迎使用 Teambition SPA-Seed!"
6 | }
7 |
--------------------------------------------------------------------------------
/public/src/js/global.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | /* global module, define */
3 |
4 | define([], function () {
5 | var script = document.getElementById('app-global')
6 | script.parentNode.removeChild(script)
7 |
8 | return {
9 | NAME: 'spa-seed',
10 | apiHost: script.getAttribute('data-apihost'),
11 | accountHost: script.getAttribute('data-accounthost')
12 | }
13 | })
14 |
--------------------------------------------------------------------------------
/lib/service/limiter.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const config = require('config')
4 | const ratelimit = require('toa-ratelimit')
5 | const redis = require('./redis')
6 |
7 | module.exports = ratelimit({
8 | redis: redis,
9 | prefix: config.limiter.prefix,
10 | duration: config.limiter.duration,
11 | getId: function () {
12 | return this.state.uid || this.state.ip
13 | },
14 | policy: config.limiter.policy
15 | })
16 |
--------------------------------------------------------------------------------
/public/src/js/main.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | require.config({
4 | paths: {
5 | 'jquery': '../bower/jquery/dist/jquery',
6 | 'bootstrap': '../bower/bootstrap/dist/js/bootstrap',
7 | 'thunks': '../bower/thunks/thunks'
8 | },
9 | shim: {
10 | 'bootstrap': ['jquery']
11 | }
12 | })
13 |
14 | require([
15 | 'jquery',
16 | 'thunks',
17 | 'global',
18 | 'bootstrap'
19 | ], function ($, thunks, global) {})
20 |
--------------------------------------------------------------------------------
/lib/service/morgan.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const toaMorgan = require('toa-morgan')
4 |
5 | module.exports = toaMorgan
6 | .token('remote-user', function () {
7 | return this.state.uid || '-'
8 | })
9 | .token('remote-addr', function () {
10 | return this.state.ip
11 | })
12 | .format('production', '[:date[iso]] INFO {"class":"morgan-spa","raw":"$$$:remote-user$:remote-addr$:method$:url$:status$:response-time$:user-agent"}')
13 |
--------------------------------------------------------------------------------
/locales/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "seoDescription": "Teambition is simple and efficient project collaboration tool. It helps you manage project, follow up project progress, and store files online, to make your teamwork much easier.",
3 | "seoKeywords": "team collaboration, team management, project management, project collaboration, assign tasks, task progress, file storage, file sharing, online preview, wiki, calendar, project discussion, project communication",
4 | "seoTitle": "Teambition | SPA-Seed",
5 | "welcome": "Welcome to use Teambition SPA-Seed!"
6 | }
7 |
--------------------------------------------------------------------------------
/config/default.json:
--------------------------------------------------------------------------------
1 | {
2 | "host": "http://localhost",
3 | "port": 3000,
4 | "logLevel": 6,
5 | "publicPath": "public/tmp",
6 | "langs": ["zh", "en"],
7 | "sessionName": "SID",
8 | "sessionSecret": ["sessionSecretXX1", "sessionSecretXX2"],
9 | "tokenSecret": ["tokenSecretXX1", "tokenSecretXX2"],
10 | "redisHosts": ["127.0.0.1:6379"],
11 | "limiter": {
12 | "prefix": "SPA",
13 | "duration": 60000,
14 | "policy": {
15 | "PUT": [60, 60000, 20, 60000],
16 | "POST": [20, 60000, 5, 60000],
17 | "DELETE": [20, 60000, 5, 60000]
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/service/redis.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const ilog = require('ilog')
4 | const config = require('config')
5 | const redis = require('thunk-redis')
6 |
7 | const client = redis.createClient(config.redisHosts)
8 |
9 | client
10 | .on('error', function (err) {
11 | err.class = 'thunk-redis'
12 | ilog.error(err)
13 | if (err.code === 'ENETUNREACH') throw err
14 | })
15 | .on('close', function (err) {
16 | err = err || new Error('Redis client closed!')
17 | err.class = 'thunk-redis'
18 | ilog.error(err)
19 | throw err
20 | })
21 |
22 | exports.client = client
23 |
--------------------------------------------------------------------------------
/public/src/less/app.less:
--------------------------------------------------------------------------------
1 | // Import variables and mixins
2 | @import "../../bower/bootstrap/less/variables.less";
3 | @import "../../bower/bootstrap/less/mixins.less";
4 |
5 | header {
6 | text-align: center;
7 | }
8 |
9 | .hello {
10 | color: blue;
11 | text-align: center;
12 | }
13 |
14 | .msg-404 {
15 | color: red;
16 | }
17 |
18 | #content {
19 | min-height: 100px;
20 | }
21 |
22 | footer {
23 | text-align: center;
24 |
25 | .logo {
26 | display: block;
27 | margin: 0 auto;
28 | width: 185px;
29 | height: 36px;
30 | background-size: contain;
31 | background-image: url('../img/logo@2x.jpg');
32 | background-repeat: no-repeat;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/api/info.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * POST /api/echo
5 | * 输出用户请求的内容
6 | * @api public
7 | */
8 | exports.echo = function * () {
9 | // 如果是异步业务,则必须返回 thunk 或 promise 等,参考 https://github.com/thunks/thunks
10 | this.body = yield this.parseBody()
11 | }
12 |
13 | /**
14 | * GET /api/info
15 | * 输出当前请求信息及平台信息
16 | * @api public
17 | */
18 | exports.getInfo = function () {
19 | this.body = {
20 | ip: this.state.ip,
21 | ips: this.ips,
22 | url: this.url,
23 | href: this.href,
24 | host: this.host,
25 | path: this.path,
26 | query: this.query,
27 | method: this.method,
28 | headers: this.headers,
29 | hostname: this.hostname,
30 | clientInfo: this.state.clientInfo
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # Compiled binary addons (http://nodejs.org/api/addons.html)
20 | build/Release
21 |
22 | # Dependency directory
23 | # Commenting this out is preferred by some people, see
24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
25 | node_modules
26 |
27 | public/dist
28 | public/bower
29 | public/tmp
30 |
31 | # Users Environment Variables
32 | .lock-wscript
33 |
34 | debug
35 | .DS_Store
36 |
--------------------------------------------------------------------------------
/lib/router.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Router = require('toa-router')
4 |
5 | const infoAPI = require('./api/info')
6 | const userAPI = require('./api/user')
7 | const indexView = require('./controller/get_index')
8 |
9 | // 参考 https://github.com/toajs/toa-router
10 | const router = exports.router = new Router()
11 | const apiRouter = exports.apiRouter = new Router('/api')
12 |
13 | // 配置静态资源路由和 views 路由
14 | router
15 | .get('', indexView.getIndex)
16 | .otherwise(function () {
17 | return this.render('404', {
18 | message: this.path + 'is not found!'
19 | })
20 | })
21 |
22 | // 配置 API 路由
23 | apiRouter
24 | .get('/info', infoAPI.getInfo)
25 | .post('/echo', infoAPI.echo)
26 |
27 | apiRouter.define('/auth')
28 | .post(userAPI.getToken)
29 | .put(userAPI.refreshToken)
30 |
--------------------------------------------------------------------------------
/lib/api/user.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * POST /api/auth
5 | * 如果验证成功,响应 token,否则响应对应的 http error
6 | * @api public
7 | */
8 | exports.getToken = function () {
9 | // 直接从 teambition session cookie 中获取用户信息 `./modules/cookieAuth.js`,不使用登录验证
10 | if (!this.state.user || !this.state.user._id) this.throw(401)
11 | var token = this.signToken(this.state.user)
12 | this.body = {
13 | user: this.decodeToken(token),
14 | token: token,
15 | authorization: 'Bearer ' + token
16 | }
17 | }
18 |
19 | /**
20 | * PUT /api/auth
21 | * 基于已有的 token 认证生成新的 token,拥有新的生命周期,请求头必须存在有效的 Authorization
22 | * @api public
23 | */
24 | exports.refreshToken = function () {
25 | var token = this.signToken(this.token)
26 | this.body = {
27 | user: this.decodeToken(token),
28 | token: token,
29 | authorization: 'Bearer ' + token
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const tman = require('tman')
4 | const thunk = require('thunks')()
5 | const supertest = require('supertest')
6 | const app = require('..')
7 |
8 | const request = supertest(app.server)
9 | const user = {
10 | id: 'abc',
11 | name: 'test',
12 | email: 'test@teambition.com'
13 | }
14 |
15 | tman.suite('SPA Seed', function () {
16 | tman.after(function * () {
17 | yield thunk.delay(1000)
18 | process.exit()
19 | })
20 |
21 | tman.it('get index view', function * () {
22 | yield request.get('')
23 | .expect(200)
24 | })
25 |
26 | tman.it('get favicon.ico', function * () {
27 | yield request.get('/favicon.ico')
28 | .expect(200)
29 | })
30 |
31 | tman.it('get /api/info', function * () {
32 | yield request.get('/api/info')
33 | .expect(200)
34 | .expect('content-type', /application\/json/)
35 | })
36 |
37 | tman.it('post /api/echo', function * () {
38 | yield request.post('/api/echo')
39 | .send(user)
40 | .expect(200)
41 | .expect('content-type', /application\/json/)
42 | .expect(user)
43 | })
44 | })
45 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015-2016 teambition
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/public/src/views/layout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%- it.__('seoTitle') %>
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | <%- it.body %>
16 |
19 | <% if (it.config.env == 'development') { %>
20 |
21 | <% } else { %>
22 |
23 |
24 | <% } %>
25 |
26 | "
6 | ],
7 | "version": "0.6.0",
8 | "main": "app.js",
9 | "repository": {
10 | "type": "git",
11 | "url": "git@github.com:teambition/spa-seed.git"
12 | },
13 | "engines": {
14 | "node": ">=4"
15 | },
16 | "homepage": "https://github.com/teambition/spa-seed",
17 | "keywords": [
18 | "toa",
19 | "spa",
20 | "seed",
21 | "teambition"
22 | ],
23 | "dependencies": {
24 | "config": "~1.24.0",
25 | "http-errors": "^1.5.1",
26 | "ilog": "^1.0.3",
27 | "proxy-addr": "^1.1.2",
28 | "thunk-redis": "^1.7.3",
29 | "thunks": "^4.7.5",
30 | "toa": "~2.4.2",
31 | "toa-body": "~1.4.1",
32 | "toa-compress": "~1.2.4",
33 | "toa-cookie-session": "^0.2.5",
34 | "toa-favicon": "~1.2.3",
35 | "toa-i18n": "^0.2.0",
36 | "toa-mejs": "^0.4.0",
37 | "toa-morgan": "^1.0.3",
38 | "toa-ratelimit": "^2.1.2",
39 | "toa-router": "~1.5.2",
40 | "toa-static": "~0.5.2",
41 | "toa-token": "~2.1.1",
42 | "ua-parser-js": "~0.7.12"
43 | },
44 | "devDependencies": {
45 | "gulp": "^3.9.1",
46 | "gulp-less": "^3.3.0",
47 | "gulp-mejs": "^0.5.0",
48 | "gulp-minify-css": "^1.2.4",
49 | "gulp-mocha": "^3.0.1",
50 | "gulp-rev-all": "^0.9.7",
51 | "gulp-rimraf": "^0.2.1",
52 | "gulp-rjs2": "^0.2.1",
53 | "gulp-sequence": "^0.4.6",
54 | "gulp-symlink": "^2.1.4",
55 | "gulp-uglify": "^2.0.0",
56 | "gulp-util": "^3.0.7",
57 | "merge2": "^1.0.2",
58 | "mocha": "^3.2.0",
59 | "standard": "^8.6.0",
60 | "supertest": "^2.0.1",
61 | "tman": "^1.6.4"
62 | },
63 | "scripts": {
64 | "start": "node app",
65 | "test": "standard && tman -t 10000"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/lib/app.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Toa = require('toa')
4 | const ilog = require('ilog')
5 | const config = require('config')
6 | const toaMejs = require('toa-mejs')
7 | const toaI18n = require('toa-i18n')
8 | const toaBody = require('toa-body')
9 | const toaToken = require('toa-token')
10 | const toaStatic = require('toa-static')
11 | const toaFavicon = require('toa-favicon')
12 | const toaCompress = require('toa-compress')
13 | const toaCookieSession = require('toa-cookie-session')
14 | const UAParser = require('ua-parser-js')
15 | const proxyaddr = require('proxy-addr')
16 |
17 | const routers = require('./router')
18 | const morgan = require('./service/morgan')
19 | const limiter = require('./service/limiter')
20 |
21 | ilog.level = config.logLevel
22 |
23 | /**
24 | * 启动服务
25 | */
26 | const app = module.exports = Toa()
27 |
28 | app.keys = config.sessionSecret
29 | app.config = {
30 | sessionName: config.sessionName
31 | }
32 | app.onerror = function (err) {
33 | if (err && err.status < 500) return
34 | ilog.error(err)
35 | }
36 |
37 | // 添加 ejs render 方法: `this.render(tplName, valueObj)`
38 | // 参考 https://github.com/toajs/toa-mejs
39 | toaMejs(app, `${config.publicPath}/views/**/*.html`, {
40 | layout: 'layout',
41 | locals: {
42 | config: {
43 | apiHost: '/api',
44 | host: config.host,
45 | env: app.config.env
46 | },
47 | locale: function () {
48 | return this.locale
49 | },
50 | ua: function () {
51 | return this.state.clientInfo
52 | },
53 | __: function () {
54 | return this.__.apply(this, arguments)
55 | },
56 | __n: function () {
57 | return this.__n.apply(this, arguments)
58 | }
59 | }
60 | })
61 |
62 | toaI18n(app, {
63 | cookie: 'lang',
64 | locales: config.langs,
65 | directory: './locales'
66 | })
67 |
68 | // token 认证支持,前端 app 应该使用 token 认证,防止 Cross-domain / CORS 攻击
69 | // 参考 https://github.com/toajs/toa-token
70 | toaToken(app, config.tokenSecret, {
71 | expiresInMinutes: 24 * 60
72 | })
73 |
74 | // 添加请求内容解析方法:`this.parseBody()`
75 | // 参考 https://github.com/toajs/toa-body
76 | toaBody(app)
77 | app.use(toaFavicon('favicon.ico'))
78 | app.use(toaStatic({
79 | root: config.publicPath,
80 | prefix: '/static',
81 | prunePrefix: false,
82 | maxCacheLength: app.config.env === 'development' ? -1 : 0
83 | }))
84 |
85 | app.use(morgan(app.config.env === 'production' ? 'production' : 'tiny'))
86 | app.use(toaCookieSession({name: config.sessionName}))
87 |
88 | const clientParser = new UAParser()
89 | app.use(function * () {
90 | this.state.ip = this.get('x-real-ip') || proxyaddr(this.req, 'uniquelocal')
91 | // 解析客户端配置信息
92 | this.state.clientInfo = clientParser.setUA(this.get('user-agent')).getResult()
93 |
94 | this.state.user = this.session.user || {}
95 | this.state.uid = this.state.user._id
96 | if (!this.state.uid) {
97 | try {
98 | this.state.uid = this.token._id
99 | } catch (e) {}
100 | }
101 | })
102 | app.use(limiter)
103 | app.use(toaCompress())
104 | app.use(routers.apiRouter.toThunk())
105 | app.use(routers.router.toThunk())
106 | // 启动 server
107 | app.listen(config.port, () => ilog.info({listen: config.port, message: 'App started'}))
108 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const gulp = require('gulp')
4 | const merge2 = require('merge2')
5 | const rjs = require('gulp-rjs2')
6 | const less = require('gulp-less')
7 | const mocha = require('gulp-mocha')
8 | const clean = require('gulp-rimraf')
9 | const uglify = require('gulp-uglify')
10 | const symlink = require('gulp-symlink')
11 | const RevAll = require('gulp-rev-all')
12 | const sequence = require('gulp-sequence')
13 | const minifyCSS = require('gulp-minify-css')
14 |
15 | const cdnPrefix = ''
16 | gulp.task('clean', function () {
17 | return gulp.src([
18 | 'public/dist',
19 | 'public/tmp'
20 | ], {read: false})
21 | .pipe(clean({force: true}))
22 | })
23 |
24 | gulp.task('mocha', function () {
25 | return gulp.src('test/*.js', {read: false})
26 | .pipe(mocha())
27 | })
28 |
29 | gulp.task('less', function () {
30 | return gulp.src('public/src/less/app.less')
31 | .pipe(less())
32 | .pipe(gulp.dest('public/tmp/static/css'))
33 | })
34 |
35 | gulp.task('js', function () {
36 | return gulp.src('public/src/js/**/*.js')
37 | .pipe(gulp.dest('public/tmp/static/js'))
38 | })
39 |
40 | gulp.task('img', function () {
41 | return gulp.src('public/src/img/**')
42 | .pipe(gulp.dest('public/tmp/static/img'))
43 | })
44 |
45 | gulp.task('views', function () {
46 | return gulp.src('public/src/views/**/*.html')
47 | .pipe(gulp.dest('public/tmp/views'))
48 | })
49 |
50 | gulp.task('bower', function () {
51 | return gulp.src('public/bower')
52 | .pipe(symlink('public/tmp/static/bower'))
53 | })
54 |
55 | gulp.task('bootstrap', function () {
56 | return gulp.src([
57 | 'public/bower/bootstrap/dist/fonts/**',
58 | 'public/bower/bootstrap/dist/css/bootstrap.css',
59 | 'public/bower/bootstrap/dist/css/bootstrap.css.map'
60 | ], {base: 'public/bower/bootstrap/dist'})
61 | .pipe(gulp.dest('public/tmp/static'))
62 | })
63 |
64 | gulp.task('rjs-lib', function () {
65 | return rjs({
66 | baseUrl: 'public/tmp/static/js',
67 | mainConfigFile: 'public/tmp/static/js/main.js',
68 | name: '../bower/almond/almond',
69 | out: 'lib.js',
70 | include: ['libraries'],
71 | insertRequire: ['libraries'],
72 | removeCombined: true,
73 | findNestedDependencies: true,
74 | optimizeCss: 'none',
75 | optimize: 'none',
76 | skipDirOptimize: true,
77 | wrap: false
78 | })
79 | .pipe(uglify())
80 | .pipe(gulp.dest('public/tmp/static/js'))
81 | })
82 |
83 | gulp.task('rjs-app', function () {
84 | return rjs({
85 | baseUrl: 'public/tmp/static/js',
86 | mainConfigFile: 'public/tmp/static/js/main.js',
87 | name: 'main',
88 | out: 'app.js',
89 | exclude: ['libraries'],
90 | removeCombined: true,
91 | findNestedDependencies: true,
92 | optimizeCss: 'none',
93 | optimize: 'none',
94 | skipDirOptimize: true,
95 | wrap: true
96 | })
97 | .pipe(uglify())
98 | .pipe(gulp.dest('public/tmp/static/js'))
99 | })
100 |
101 | gulp.task('revall', function () {
102 | let revAll = new RevAll({
103 | prefix: cdnPrefix,
104 | dontGlobal: [/\/favicon\.ico$/],
105 | dontRenameFile: [/\.html$/],
106 | dontUpdateReference: [/\.html$/],
107 | dontSearchFile: [/\.js$/, /images/]
108 | })
109 |
110 | return merge2([
111 | gulp.src('public/tmp/views/**/*.html'),
112 | gulp.src('public/tmp/static/css/*.css').pipe(minifyCSS({rebase: false})),
113 | gulp.src([
114 | 'public/tmp/static/js/app.js',
115 | 'public/tmp/static/js/lib.js'
116 | ]).pipe(uglify()),
117 | gulp.src([
118 | 'public/tmp/static/img/**',
119 | 'public/tmp/static/fonts/**'
120 | ])
121 | ])
122 | .pipe(revAll.revision())
123 | .pipe(gulp.dest('public/dist'))
124 | })
125 |
126 | gulp.task('watch', function () {
127 | gulp.watch('public/src/js/**/*.js', ['js'])
128 | gulp.watch('public/src/views/**/*.html', ['views'])
129 | gulp.watch('public/src/less/**/*.less', ['less'])
130 | gulp.watch('public/src/img/**', ['img'])
131 | })
132 |
133 | gulp.task('test', sequence('mocha'))
134 | gulp.task('dev', sequence('clean', ['js', 'less', 'img', 'views', 'bootstrap', 'bower']))
135 | gulp.task('build', sequence('dev', ['rjs-lib', 'rjs-app'], 'revall'))
136 | gulp.task('default', sequence('dev'))
137 |
--------------------------------------------------------------------------------
/npm-shrinkwrap.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "spa-seed",
3 | "version": "0.6.0",
4 | "dependencies": {
5 | "config": {
6 | "version": "1.24.0",
7 | "from": "config@>=1.24.0 <1.25.0",
8 | "resolved": "https://registry.npm.taobao.org/config/download/config-1.24.0.tgz",
9 | "dependencies": {
10 | "json5": {
11 | "version": "0.4.0",
12 | "from": "json5@0.4.0",
13 | "resolved": "https://registry.npmjs.org/json5/-/json5-0.4.0.tgz"
14 | }
15 | }
16 | },
17 | "http-errors": {
18 | "version": "1.5.1",
19 | "from": "http-errors@>=1.5.1 <2.0.0",
20 | "resolved": "https://registry.npm.taobao.org/http-errors/download/http-errors-1.5.1.tgz",
21 | "dependencies": {
22 | "inherits": {
23 | "version": "2.0.3",
24 | "from": "inherits@2.0.3",
25 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz"
26 | },
27 | "setprototypeof": {
28 | "version": "1.0.2",
29 | "from": "setprototypeof@1.0.2",
30 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz"
31 | },
32 | "statuses": {
33 | "version": "1.3.1",
34 | "from": "statuses@>=1.3.1 <2.0.0",
35 | "resolved": "https://registry.npm.taobao.org/statuses/download/statuses-1.3.1.tgz"
36 | }
37 | }
38 | },
39 | "ilog": {
40 | "version": "1.0.3",
41 | "from": "ilog@>=1.0.3 <2.0.0",
42 | "resolved": "https://registry.npm.taobao.org/ilog/download/ilog-1.0.3.tgz"
43 | },
44 | "proxy-addr": {
45 | "version": "1.1.2",
46 | "from": "proxy-addr@>=1.1.2 <2.0.0",
47 | "resolved": "https://registry.npm.taobao.org/proxy-addr/download/proxy-addr-1.1.2.tgz",
48 | "dependencies": {
49 | "forwarded": {
50 | "version": "0.1.0",
51 | "from": "forwarded@>=0.1.0 <0.2.0",
52 | "resolved": "https://registry.npm.taobao.org/forwarded/download/forwarded-0.1.0.tgz"
53 | },
54 | "ipaddr.js": {
55 | "version": "1.1.1",
56 | "from": "ipaddr.js@1.1.1",
57 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.1.1.tgz"
58 | }
59 | }
60 | },
61 | "thunk-redis": {
62 | "version": "1.7.3",
63 | "from": "thunk-redis@>=1.7.3 <2.0.0",
64 | "resolved": "https://registry.npm.taobao.org/thunk-redis/download/thunk-redis-1.7.3.tgz",
65 | "dependencies": {
66 | "respjs": {
67 | "version": "3.2.0",
68 | "from": "respjs@>=3.2.0 <4.0.0",
69 | "resolved": "https://registry.npm.taobao.org/respjs/download/respjs-3.2.0.tgz"
70 | }
71 | }
72 | },
73 | "thunks": {
74 | "version": "4.7.5",
75 | "from": "thunks@>=4.7.5 <5.0.0",
76 | "resolved": "https://registry.npm.taobao.org/thunks/download/thunks-4.7.5.tgz"
77 | },
78 | "toa": {
79 | "version": "2.4.2",
80 | "from": "toa@>=2.4.2 <2.5.0",
81 | "dependencies": {
82 | "accepts": {
83 | "version": "1.3.3",
84 | "from": "accepts@>=1.3.3 <1.4.0",
85 | "resolved": "https://registry.npm.taobao.org/accepts/download/accepts-1.3.3.tgz",
86 | "dependencies": {
87 | "negotiator": {
88 | "version": "0.6.1",
89 | "from": "negotiator@0.6.1",
90 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz"
91 | }
92 | }
93 | },
94 | "content-disposition": {
95 | "version": "0.5.2",
96 | "from": "content-disposition@>=0.5.2 <0.6.0",
97 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz"
98 | },
99 | "content-type": {
100 | "version": "1.0.2",
101 | "from": "content-type@>=1.0.2 <1.1.0",
102 | "resolved": "https://registry.npm.taobao.org/content-type/download/content-type-1.0.2.tgz"
103 | },
104 | "cookies": {
105 | "version": "0.6.2",
106 | "from": "cookies@>=0.6.2 <0.7.0",
107 | "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.6.2.tgz",
108 | "dependencies": {
109 | "depd": {
110 | "version": "1.1.0",
111 | "from": "depd@>=1.1.0 <1.2.0",
112 | "resolved": "https://registry.npm.taobao.org/depd/download/depd-1.1.0.tgz"
113 | },
114 | "keygrip": {
115 | "version": "1.0.1",
116 | "from": "keygrip@>=1.0.1 <1.1.0",
117 | "resolved": "https://registry.npm.taobao.org/keygrip/download/keygrip-1.0.1.tgz"
118 | }
119 | }
120 | },
121 | "delegates": {
122 | "version": "1.0.0",
123 | "from": "delegates@>=1.0.0 <1.1.0",
124 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz"
125 | },
126 | "destroy": {
127 | "version": "1.0.4",
128 | "from": "destroy@>=1.0.4 <1.1.0",
129 | "resolved": "https://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz"
130 | },
131 | "escape-html": {
132 | "version": "1.0.3",
133 | "from": "escape-html@>=1.0.3 <1.1.0",
134 | "resolved": "https://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz"
135 | },
136 | "fresh": {
137 | "version": "0.3.0",
138 | "from": "fresh@>=0.3.0 <0.4.0",
139 | "resolved": "https://registry.npm.taobao.org/fresh/download/fresh-0.3.0.tgz"
140 | },
141 | "mime-types": {
142 | "version": "2.1.13",
143 | "from": "mime-types@>=2.0.12 <3.0.0",
144 | "resolved": "https://registry.npm.taobao.org/mime-types/download/mime-types-2.1.13.tgz",
145 | "dependencies": {
146 | "mime-db": {
147 | "version": "1.25.0",
148 | "from": "mime-db@>=1.25.0 <1.26.0",
149 | "resolved": "https://registry.npm.taobao.org/mime-db/download/mime-db-1.25.0.tgz"
150 | }
151 | }
152 | },
153 | "parseurl": {
154 | "version": "1.3.1",
155 | "from": "parseurl@>=1.3.1 <1.4.0",
156 | "resolved": "https://registry.npm.taobao.org/parseurl/download/parseurl-1.3.1.tgz"
157 | },
158 | "statuses": {
159 | "version": "1.3.1",
160 | "from": "statuses@>=1.3.1 <2.0.0",
161 | "resolved": "https://registry.npm.taobao.org/statuses/download/statuses-1.3.1.tgz"
162 | },
163 | "type-is": {
164 | "version": "1.6.14",
165 | "from": "type-is@>=1.6.14 <1.7.0",
166 | "resolved": "https://registry.npm.taobao.org/type-is/download/type-is-1.6.14.tgz",
167 | "dependencies": {
168 | "media-typer": {
169 | "version": "0.3.0",
170 | "from": "media-typer@0.3.0",
171 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"
172 | }
173 | }
174 | },
175 | "vary": {
176 | "version": "1.1.0",
177 | "from": "vary@>=1.1.0 <1.2.0",
178 | "resolved": "https://registry.npm.taobao.org/vary/download/vary-1.1.0.tgz"
179 | }
180 | }
181 | },
182 | "toa-body": {
183 | "version": "1.4.1",
184 | "from": "toa-body@>=1.4.1 <1.5.0",
185 | "resolved": "https://registry.npmjs.org/toa-body/-/toa-body-1.4.1.tgz",
186 | "dependencies": {
187 | "inflation": {
188 | "version": "2.0.0",
189 | "from": "inflation@>=2.0.0 <3.0.0",
190 | "resolved": "https://registry.npmjs.org/inflation/-/inflation-2.0.0.tgz"
191 | },
192 | "qs": {
193 | "version": "6.3.0",
194 | "from": "qs@>=6.2.1 <7.0.0",
195 | "resolved": "https://registry.npm.taobao.org/qs/download/qs-6.3.0.tgz"
196 | },
197 | "raw-body": {
198 | "version": "2.1.7",
199 | "from": "raw-body@>=2.1.7 <3.0.0",
200 | "resolved": "https://registry.npm.taobao.org/raw-body/download/raw-body-2.1.7.tgz",
201 | "dependencies": {
202 | "bytes": {
203 | "version": "2.4.0",
204 | "from": "bytes@2.4.0",
205 | "resolved": "https://registry.npm.taobao.org/bytes/download/bytes-2.4.0.tgz"
206 | },
207 | "iconv-lite": {
208 | "version": "0.4.13",
209 | "from": "iconv-lite@0.4.13",
210 | "resolved": "https://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.13.tgz"
211 | },
212 | "unpipe": {
213 | "version": "1.0.0",
214 | "from": "unpipe@1.0.0",
215 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
216 | }
217 | }
218 | }
219 | }
220 | },
221 | "toa-compress": {
222 | "version": "1.2.4",
223 | "from": "toa-compress@>=1.2.4 <1.3.0",
224 | "resolved": "https://registry.npmjs.org/toa-compress/-/toa-compress-1.2.4.tgz",
225 | "dependencies": {
226 | "compressible": {
227 | "version": "2.0.9",
228 | "from": "compressible@>=2.0.8 <3.0.0",
229 | "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.9.tgz",
230 | "dependencies": {
231 | "mime-db": {
232 | "version": "1.25.0",
233 | "from": "mime-db@>=1.24.0 <2.0.0",
234 | "resolved": "https://registry.npm.taobao.org/mime-db/download/mime-db-1.25.0.tgz"
235 | }
236 | }
237 | }
238 | }
239 | },
240 | "toa-cookie-session": {
241 | "version": "0.2.5",
242 | "from": "toa-cookie-session@>=0.2.5 <0.3.0",
243 | "resolved": "https://registry.npmjs.org/toa-cookie-session/-/toa-cookie-session-0.2.5.tgz"
244 | },
245 | "toa-favicon": {
246 | "version": "1.2.3",
247 | "from": "toa-favicon@>=1.2.3 <1.3.0",
248 | "resolved": "https://registry.npmjs.org/toa-favicon/-/toa-favicon-1.2.3.tgz"
249 | },
250 | "toa-i18n": {
251 | "version": "0.2.0",
252 | "from": "toa-i18n@>=0.2.0 <0.3.0",
253 | "resolved": "https://registry.npmjs.org/toa-i18n/-/toa-i18n-0.2.0.tgz",
254 | "dependencies": {
255 | "i18n": {
256 | "version": "0.5.0",
257 | "from": "i18n@>=0.5.0 <0.6.0",
258 | "resolved": "https://registry.npmjs.org/i18n/-/i18n-0.5.0.tgz",
259 | "dependencies": {
260 | "sprintf": {
261 | "version": "0.1.5",
262 | "from": "sprintf@>=0.1.1",
263 | "resolved": "https://registry.npm.taobao.org/sprintf/download/sprintf-0.1.5.tgz"
264 | },
265 | "mustache": {
266 | "version": "2.3.0",
267 | "from": "mustache@*",
268 | "resolved": "https://registry.npm.taobao.org/mustache/download/mustache-2.3.0.tgz"
269 | },
270 | "debug": {
271 | "version": "2.4.4",
272 | "from": "debug@*",
273 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.4.4.tgz",
274 | "dependencies": {
275 | "ms": {
276 | "version": "0.7.2",
277 | "from": "ms@0.7.2",
278 | "resolved": "https://registry.npm.taobao.org/ms/download/ms-0.7.2.tgz"
279 | }
280 | }
281 | }
282 | }
283 | }
284 | }
285 | },
286 | "toa-mejs": {
287 | "version": "0.4.0",
288 | "from": "toa-mejs@>=0.4.0 <0.5.0",
289 | "resolved": "https://registry.npmjs.org/toa-mejs/-/toa-mejs-0.4.0.tgz",
290 | "dependencies": {
291 | "mejs": {
292 | "version": "0.8.2",
293 | "from": "mejs@>=0.8.1 <0.9.0",
294 | "resolved": "https://registry.npmjs.org/mejs/-/mejs-0.8.2.tgz",
295 | "dependencies": {
296 | "glob": {
297 | "version": "7.1.1",
298 | "from": "glob@>=7.0.5 <8.0.0",
299 | "resolved": "https://registry.npm.taobao.org/glob/download/glob-7.1.1.tgz",
300 | "dependencies": {
301 | "fs.realpath": {
302 | "version": "1.0.0",
303 | "from": "fs.realpath@>=1.0.0 <2.0.0",
304 | "resolved": "https://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz"
305 | },
306 | "inflight": {
307 | "version": "1.0.6",
308 | "from": "inflight@>=1.0.4 <2.0.0",
309 | "resolved": "https://registry.npm.taobao.org/inflight/download/inflight-1.0.6.tgz",
310 | "dependencies": {
311 | "wrappy": {
312 | "version": "1.0.2",
313 | "from": "wrappy@>=1.0.0 <2.0.0",
314 | "resolved": "https://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz"
315 | }
316 | }
317 | },
318 | "inherits": {
319 | "version": "2.0.3",
320 | "from": "inherits@>=2.0.0 <3.0.0",
321 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz"
322 | },
323 | "minimatch": {
324 | "version": "3.0.3",
325 | "from": "minimatch@>=3.0.2 <4.0.0",
326 | "resolved": "https://registry.npm.taobao.org/minimatch/download/minimatch-3.0.3.tgz",
327 | "dependencies": {
328 | "brace-expansion": {
329 | "version": "1.1.6",
330 | "from": "brace-expansion@>=1.0.0 <2.0.0",
331 | "resolved": "https://registry.npm.taobao.org/brace-expansion/download/brace-expansion-1.1.6.tgz",
332 | "dependencies": {
333 | "balanced-match": {
334 | "version": "0.4.2",
335 | "from": "balanced-match@>=0.4.1 <0.5.0",
336 | "resolved": "https://registry.npm.taobao.org/balanced-match/download/balanced-match-0.4.2.tgz"
337 | },
338 | "concat-map": {
339 | "version": "0.0.1",
340 | "from": "concat-map@0.0.1",
341 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
342 | }
343 | }
344 | }
345 | }
346 | },
347 | "once": {
348 | "version": "1.4.0",
349 | "from": "once@>=1.3.0 <2.0.0",
350 | "resolved": "https://registry.npm.taobao.org/once/download/once-1.4.0.tgz",
351 | "dependencies": {
352 | "wrappy": {
353 | "version": "1.0.2",
354 | "from": "wrappy@>=1.0.0 <2.0.0",
355 | "resolved": "https://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz"
356 | }
357 | }
358 | },
359 | "path-is-absolute": {
360 | "version": "1.0.1",
361 | "from": "path-is-absolute@>=1.0.0 <2.0.0",
362 | "resolved": "https://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz"
363 | }
364 | }
365 | }
366 | }
367 | }
368 | }
369 | },
370 | "toa-morgan": {
371 | "version": "1.0.3",
372 | "from": "toa-morgan@>=1.0.3 <2.0.0",
373 | "resolved": "https://registry.npmjs.org/toa-morgan/-/toa-morgan-1.0.3.tgz"
374 | },
375 | "toa-ratelimit": {
376 | "version": "2.1.2",
377 | "from": "toa-ratelimit@>=2.1.2 <3.0.0",
378 | "resolved": "https://registry.npmjs.org/toa-ratelimit/-/toa-ratelimit-2.1.2.tgz",
379 | "dependencies": {
380 | "thunk-ratelimiter": {
381 | "version": "2.2.0",
382 | "from": "thunk-ratelimiter@>=2.1.5 <3.0.0"
383 | }
384 | }
385 | },
386 | "toa-router": {
387 | "version": "1.5.2",
388 | "from": "toa-router@>=1.5.2 <1.6.0",
389 | "resolved": "https://registry.npmjs.org/toa-router/-/toa-router-1.5.2.tgz",
390 | "dependencies": {
391 | "methods": {
392 | "version": "1.1.2",
393 | "from": "methods@>=1.0.0 <2.0.0",
394 | "resolved": "https://registry.npm.taobao.org/methods/download/methods-1.1.2.tgz"
395 | },
396 | "route-trie": {
397 | "version": "1.2.7",
398 | "from": "route-trie@>=1.2.7 <2.0.0",
399 | "resolved": "https://registry.npmjs.org/route-trie/-/route-trie-1.2.7.tgz"
400 | }
401 | }
402 | },
403 | "toa-static": {
404 | "version": "0.5.2",
405 | "from": "toa-static@>=0.5.2 <0.6.0",
406 | "resolved": "https://registry.npmjs.org/toa-static/-/toa-static-0.5.2.tgz",
407 | "dependencies": {
408 | "file-cache": {
409 | "version": "0.3.3",
410 | "from": "file-cache@>=0.3.3 <0.4.0",
411 | "resolved": "https://registry.npmjs.org/file-cache/-/file-cache-0.3.3.tgz",
412 | "dependencies": {
413 | "compressible": {
414 | "version": "2.0.9",
415 | "from": "compressible@>=2.0.2 <3.0.0",
416 | "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.9.tgz",
417 | "dependencies": {
418 | "mime-db": {
419 | "version": "1.25.0",
420 | "from": "mime-db@>=1.24.0 <2.0.0",
421 | "resolved": "https://registry.npm.taobao.org/mime-db/download/mime-db-1.25.0.tgz"
422 | }
423 | }
424 | },
425 | "lrucache": {
426 | "version": "0.2.0",
427 | "from": "lrucache@>=0.2.0 <0.3.0",
428 | "resolved": "https://registry.npmjs.org/lrucache/-/lrucache-0.2.0.tgz"
429 | },
430 | "mime-types": {
431 | "version": "2.1.13",
432 | "from": "mime-types@>=2.0.12 <3.0.0",
433 | "resolved": "https://registry.npm.taobao.org/mime-types/download/mime-types-2.1.13.tgz",
434 | "dependencies": {
435 | "mime-db": {
436 | "version": "1.25.0",
437 | "from": "mime-db@>=1.24.0 <2.0.0",
438 | "resolved": "https://registry.npm.taobao.org/mime-db/download/mime-db-1.25.0.tgz"
439 | }
440 | }
441 | },
442 | "thunks": {
443 | "version": "3.5.2",
444 | "from": "thunks@>=3.1.1 <4.0.0",
445 | "resolved": "https://registry.npmjs.org/thunks/-/thunks-3.5.2.tgz"
446 | }
447 | }
448 | }
449 | }
450 | },
451 | "toa-token": {
452 | "version": "2.1.1",
453 | "from": "toa-token@>=2.1.1 <2.2.0",
454 | "resolved": "https://registry.npmjs.org/toa-token/-/toa-token-2.1.1.tgz",
455 | "dependencies": {
456 | "jsonwebtoken": {
457 | "version": "7.2.1",
458 | "from": "jsonwebtoken@>=7.1.9 <8.0.0",
459 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.2.1.tgz",
460 | "dependencies": {
461 | "joi": {
462 | "version": "6.10.1",
463 | "from": "joi@>=6.10.1 <7.0.0",
464 | "resolved": "https://registry.npm.taobao.org/joi/download/joi-6.10.1.tgz",
465 | "dependencies": {
466 | "hoek": {
467 | "version": "2.16.3",
468 | "from": "hoek@>=2.0.0 <3.0.0",
469 | "resolved": "https://registry.npm.taobao.org/hoek/download/hoek-2.16.3.tgz"
470 | },
471 | "topo": {
472 | "version": "1.1.0",
473 | "from": "topo@>=1.0.0 <2.0.0",
474 | "resolved": "https://registry.npm.taobao.org/topo/download/topo-1.1.0.tgz"
475 | },
476 | "isemail": {
477 | "version": "1.2.0",
478 | "from": "isemail@>=1.0.0 <2.0.0",
479 | "resolved": "https://registry.npm.taobao.org/isemail/download/isemail-1.2.0.tgz"
480 | },
481 | "moment": {
482 | "version": "2.17.1",
483 | "from": "moment@>=2.0.0 <3.0.0",
484 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.17.1.tgz"
485 | }
486 | }
487 | },
488 | "jws": {
489 | "version": "3.1.4",
490 | "from": "jws@>=3.1.4 <4.0.0",
491 | "resolved": "https://registry.npm.taobao.org/jws/download/jws-3.1.4.tgz",
492 | "dependencies": {
493 | "base64url": {
494 | "version": "2.0.0",
495 | "from": "base64url@>=2.0.0 <3.0.0",
496 | "resolved": "https://registry.npm.taobao.org/base64url/download/base64url-2.0.0.tgz"
497 | },
498 | "jwa": {
499 | "version": "1.1.5",
500 | "from": "jwa@>=1.1.4 <2.0.0",
501 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz",
502 | "dependencies": {
503 | "buffer-equal-constant-time": {
504 | "version": "1.0.1",
505 | "from": "buffer-equal-constant-time@1.0.1",
506 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz"
507 | },
508 | "ecdsa-sig-formatter": {
509 | "version": "1.0.9",
510 | "from": "ecdsa-sig-formatter@1.0.9",
511 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz"
512 | }
513 | }
514 | },
515 | "safe-buffer": {
516 | "version": "5.0.1",
517 | "from": "safe-buffer@>=5.0.1 <6.0.0",
518 | "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.0.1.tgz"
519 | }
520 | }
521 | },
522 | "lodash.once": {
523 | "version": "4.1.1",
524 | "from": "lodash.once@>=4.0.0 <5.0.0",
525 | "resolved": "https://registry.npm.taobao.org/lodash.once/download/lodash.once-4.1.1.tgz"
526 | },
527 | "ms": {
528 | "version": "0.7.2",
529 | "from": "ms@>=0.7.1 <0.8.0",
530 | "resolved": "https://registry.npm.taobao.org/ms/download/ms-0.7.2.tgz"
531 | },
532 | "xtend": {
533 | "version": "4.0.1",
534 | "from": "xtend@>=4.0.1 <5.0.0",
535 | "resolved": "https://registry.npm.taobao.org/xtend/download/xtend-4.0.1.tgz"
536 | }
537 | }
538 | }
539 | }
540 | },
541 | "ua-parser-js": {
542 | "version": "0.7.12",
543 | "from": "ua-parser-js@>=0.7.12 <0.8.0",
544 | "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.12.tgz"
545 | }
546 | }
547 | }
548 |
--------------------------------------------------------------------------------