├── .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 |
6 | 7 |
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 | --------------------------------------------------------------------------------