├── .gitignore ├── config ├── default.json └── db.js ├── models └── url.js ├── requests └── api.http ├── index.js ├── package.json ├── routes ├── index.js └── url.js ├── .vscode └── launch.json ├── readme.md └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "mongoURI": "mongodb://localhost:27017/url-shorten-service", 3 | "baseUrl": "http://localhost:5000" 4 | } -------------------------------------------------------------------------------- /models/url.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const urlSchema = new mongoose.Schema({ 4 | urlCode: String, 5 | longUrl: String 6 | }); 7 | 8 | module.exports = mongoose.model('Url', urlSchema); -------------------------------------------------------------------------------- /requests/api.http: -------------------------------------------------------------------------------- 1 | POST http://localhost:5000/api/url/shorten 2 | Content-Type: application/json 3 | 4 | { 5 | "longUrl": "https://www.amazon.com/Apple-MacBook-13-inch-1-4GHz-quad-core/dp/B07V49KGVQ/ref=sr_1_1?keywords=macbook+pro&qid=1566373249&s=gateway&sr=8-1" 6 | } -------------------------------------------------------------------------------- /config/db.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const config = require('config'); 3 | const db = config.get('mongoURI'); 4 | 5 | const connectDB = async () => { 6 | try { 7 | await mongoose.connect(db, { 8 | useNewUrlParser: true 9 | }); 10 | console.log(`MongoDB Connected to: ${db}`); 11 | } catch (error) { 12 | console.error(error.message); 13 | process.exit(1); 14 | } 15 | } 16 | 17 | module.exports = connectDB; -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const connectDB = require('./config/db'); 3 | 4 | const app = express(); 5 | 6 | // connect to database 7 | connectDB(); 8 | 9 | app.use(express.json({ 10 | extended: false 11 | })); 12 | 13 | // routes 14 | 15 | app.use('/', require('./routes/index')); 16 | app.use('/api/url', require('./routes/url')); 17 | 18 | const port = 5000; 19 | 20 | app.listen(port, () => { 21 | console.log(`Server running on port ${port}`); 22 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "url-shortener-service", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "repository": "git@github.com:MudOnTire/url-shortener-service.git", 6 | "author": "MudOnTire <895157882@qq.com>", 7 | "license": "MIT", 8 | "scripts": { 9 | "start": "node index.js", 10 | "dev": "nodemon index.js" 11 | }, 12 | "dependencies": { 13 | "config": "^3.2.2", 14 | "express": "^4.17.1", 15 | "mongoose": "^5.6.9", 16 | "shortid": "^2.2.14", 17 | "valid-url": "^1.0.9" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | const epxress = require("express"); 2 | const router = epxress.Router(); 3 | const Url = require('../models/url'); 4 | 5 | router.get('/:code', async (req, res, next) => { 6 | try { 7 | const urlCode = req.params.code; 8 | const url = await Url.findOne({ urlCode }); 9 | if (url) { 10 | res.redirect(url.longUrl); 11 | } else { 12 | res.status(404).json("No url found"); 13 | } 14 | } catch (error) { 15 | res.status(500).json("Server error"); 16 | } 17 | }); 18 | 19 | module.exports = router; -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "nodemon", 11 | "runtimeExecutable": "nodemon", 12 | "program": "${workspaceFolder}/index.js", 13 | "restart": true, 14 | "console": "integratedTerminal", 15 | "internalConsoleOptions": "neverOpen" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /routes/url.js: -------------------------------------------------------------------------------- 1 | const epxress = require("express"); 2 | const router = epxress.Router(); 3 | const validUrl = require('valid-url'); 4 | const shortId = require('shortid'); 5 | const config = require('config'); 6 | const Url = require('../models/url'); 7 | 8 | router.post('/shorten', async (req, res, next) => { 9 | const { longUrl } = req.body; 10 | if (validUrl.isUri(longUrl)) { 11 | try { 12 | let url = await Url.findOne({ longUrl }); 13 | if (url) { 14 | res.json({ 15 | shortUrl: `${config.get('baseUrl')}/${url.urlCode}` 16 | }); 17 | } else { 18 | const urlCode = shortId.generate(); 19 | url = new Url({ 20 | longUrl, 21 | urlCode 22 | }); 23 | await url.save(); 24 | res.json({ 25 | shortUrl: `${config.get('baseUrl')}/${urlCode}` 26 | }); 27 | } 28 | } catch (error) { 29 | res.status(500).json('Server error'); 30 | } 31 | } else { 32 | res.status(401).json('Invalid long url'); 33 | } 34 | }); 35 | 36 | module.exports = router; -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 短链接我们或多或少都使用过,所谓短链接就是根据较长的原链接url生成一段较短的链接,访问短链接可以跳转到对应的原链接,这样做好处在于:1. url更加美观;2. 便于保存和传播;3. 某些网站内容发布有字数限制,短链接可以节约字数。 2 | 3 | 短链接实现的原理非常简单,可以概括为: 4 | 1. 为每个原链接生成不重复的唯一短链接 5 | 2. 将原链接和对应短链接成对保存到数据库 6 | 3. 访问短链接时,web服务器将目标重定向到对应的原链接 7 | 8 | 根据以上思路,我们自己也可以分分钟实现一个短链接生成服务。本文示例使用 node + express + mongodb。 9 | 10 | # 1. 初始化项目 11 | 12 | ## (1). 安装如下依赖: 13 | 14 | **package.json:** 15 | 16 | ``` 17 | "dependencies": { 18 | "config": "^3.2.2", // 读取项目配置 19 | "express": "^4.17.1", // web服务器 20 | "mongoose": "^5.6.9", // 操作mongodb 21 | "shortid": "^2.2.14", // 生成不重复的唯一Id 22 | "valid-url": "^1.0.9" // 判断url格式是否正确 23 | } 24 | ``` 25 | 26 | ## (2). 增加项目配置: 27 | 28 | 主要用于存放MongoDB的连接字符串和短链接的base url。 29 | 30 | **config/default.json:** 31 | 32 | ``` 33 | { 34 | "mongoURI": "mongodb://localhost:27017/url-shorten-service", 35 | "baseUrl": "http://localhost:5000" 36 | } 37 | ``` 38 | 39 | ## (3). 增加MongoDB连接方法 40 | 41 | **config/db.js:** 42 | 43 | ``` 44 | const mongoose = require('mongoose'); 45 | const config = require('config'); 46 | const db = config.get('mongoURI'); 47 | 48 | const connectDB = async () => { 49 | try { 50 | await mongoose.connect(db, { 51 | useNewUrlParser: true 52 | }); 53 | console.log(`MongoDB Connected to: ${db}`); 54 | } catch (error) { 55 | console.error(error.message); 56 | process.exit(1); 57 | } 58 | } 59 | 60 | module.exports = connectDB; 61 | ``` 62 | 63 | ## (4). 启动express: 64 | 65 | **index.js:** 66 | 67 | ``` 68 | const express = require('express'); 69 | const connectDB = require('./config/db'); 70 | 71 | const app = express(); 72 | 73 | // 连接MongoDB 74 | connectDB(); 75 | 76 | app.use(express.json({ 77 | extended: false 78 | })); 79 | 80 | // 路由,稍后设置 81 | app.use('/', require('./routes/index')); 82 | app.use('/api/url', require('./routes/url')); 83 | 84 | const port = 5000; 85 | 86 | app.listen(port, () => { 87 | console.log(`Server running on port ${port}`); 88 | }); 89 | ``` 90 | 91 | # 2. 定义数据库模型 92 | 93 | 我们需要将原链接和对应短链接保存到数据库,简单起见,我们只需要保存一个短链接编码,相应的短链接可以使用base url和编码拼接而成。 94 | 95 | **models/url.js:** 96 | 97 | ``` 98 | const mongoose = require('mongoose'); 99 | 100 | const urlSchema = new mongoose.Schema({ 101 | urlCode: String, 102 | longUrl: String 103 | }); 104 | 105 | module.exports = mongoose.model('Url', urlSchema); 106 | ``` 107 | 108 | # 3. 生成短链接编码 109 | 110 | 这是我们实现的关键一步,思路是:用户传入一个长链接,我们首先使用 [valid-url](https://www.npmjs.com/package/valid-url) 判断传入的url是否合法,不合法则返回错误,如果合法我们在数据库中搜索是否有该长链接的记录,如果有则直接返回该条记录,如果没有则生成一条新记录,并生成对应的短链接。借助于 [shortId](https://www.npmjs.com/package/shortid),我们可以很方便的生成一个不重复的唯一编码。 111 | 112 | **routes/url.js:** 113 | 114 | ``` 115 | const epxress = require("express"); 116 | const router = epxress.Router(); 117 | const validUrl = require('valid-url'); 118 | const shortId = require('shortid'); 119 | const config = require('config'); 120 | const Url = require('../models/url'); 121 | 122 | router.post('/shorten', async (req, res, next) => { 123 | const { longUrl } = req.body; 124 | if (validUrl.isUri(longUrl)) { 125 | try { 126 | let url = await Url.findOne({ longUrl }); 127 | if (url) { 128 | res.json({ 129 | shortUrl: `${config.get('baseUrl')}/${url.urlCode}` 130 | }); 131 | } else { 132 | const urlCode = shortId.generate(); 133 | url = new Url({ 134 | longUrl, 135 | urlCode 136 | }); 137 | await url.save(); 138 | res.json({ 139 | shortUrl: `${config.get('baseUrl')}/${urlCode}` 140 | }); 141 | } 142 | } catch (error) { 143 | res.status(500).json('Server error'); 144 | } 145 | } else { 146 | res.status(401).json('Invalid long url'); 147 | } 148 | }); 149 | 150 | module.exports = router; 151 | ``` 152 | 153 | # 4. 访问短链接跳转到原链接 154 | 155 | 最后一步非常简单,当用户访问我们生成的短链接时,我们根据url中的短链接编码查询到对应记录,如果存在对应记录我们使用express的`res.redirect`方法将访问重定向至原链接,如果不存在则返回错误。 156 | 157 | **routes/index.js** 158 | 159 | ``` 160 | const epxress = require("express"); 161 | const router = epxress.Router(); 162 | const Url = require('../models/url'); 163 | 164 | router.get('/:code', async (req, res, next) => { 165 | try { 166 | const urlCode = req.params.code; 167 | const url = await Url.findOne({ urlCode }); 168 | if (url) { 169 | // 重定向至原链接 170 | res.redirect(url.longUrl); 171 | } else { 172 | res.status(404).json("No url found"); 173 | } 174 | } catch (error) { 175 | res.status(500).json("Server error"); 176 | } 177 | }); 178 | 179 | module.exports = router; 180 | ``` 181 | 182 | **测试一下:** 183 | 184 | ![send request](http://lc-PX2vd1LW.cn-n1.lcfile.com/417bbdd2c4f467425bac/1.png) 185 | 186 | **访问短链接:** 187 | 188 | ![clipboard.png](/img/bVbwF5x) 189 | 190 | 这样,一个简单的短链接生成服务就完成了,往往在我们看来很神奇的技术其实背后的原理和实现很简单,希望本文对大家有所启发。 191 | 192 | >最后,推荐大家使用[Fundebug](https://www.fundebug.com/?utm_source=MudOnTire),一款很好用的BUG监控工具~ 193 | 194 | 本文Demo地址:https://github.com/MudOnTire/url-shortener-service 195 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | accepts@~1.3.7: 6 | version "1.3.7" 7 | resolved "https://registry.npm.taobao.org/accepts/download/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" 8 | integrity sha1-UxvHJlF6OytB+FACHGzBXqq1B80= 9 | dependencies: 10 | mime-types "~2.1.24" 11 | negotiator "0.6.2" 12 | 13 | array-flatten@1.1.1: 14 | version "1.1.1" 15 | resolved "https://registry.npm.taobao.org/array-flatten/download/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 16 | integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= 17 | 18 | async@2.6.2: 19 | version "2.6.2" 20 | resolved "https://registry.npm.taobao.org/async/download/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" 21 | integrity sha1-GDMOp+bjE4h/XS8qkEusb+TdU4E= 22 | dependencies: 23 | lodash "^4.17.11" 24 | 25 | bluebird@3.5.1: 26 | version "3.5.1" 27 | resolved "https://registry.npm.taobao.org/bluebird/download/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" 28 | integrity sha1-2VUfnemPH82h5oPRfukaBgLuLrk= 29 | 30 | body-parser@1.19.0: 31 | version "1.19.0" 32 | resolved "https://registry.npm.taobao.org/body-parser/download/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" 33 | integrity sha1-lrJwnlfJxOCab9Zqj9l5hE9p8Io= 34 | dependencies: 35 | bytes "3.1.0" 36 | content-type "~1.0.4" 37 | debug "2.6.9" 38 | depd "~1.1.2" 39 | http-errors "1.7.2" 40 | iconv-lite "0.4.24" 41 | on-finished "~2.3.0" 42 | qs "6.7.0" 43 | raw-body "2.4.0" 44 | type-is "~1.6.17" 45 | 46 | bson@^1.1.1, bson@~1.1.1: 47 | version "1.1.1" 48 | resolved "https://registry.npm.taobao.org/bson/download/bson-1.1.1.tgz#4330f5e99104c4e751e7351859e2d408279f2f13" 49 | integrity sha1-QzD16ZEExOdR5zUYWeLUCCefLxM= 50 | 51 | bytes@3.1.0: 52 | version "3.1.0" 53 | resolved "https://registry.npm.taobao.org/bytes/download/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" 54 | integrity sha1-9s95M6Ng4FiPqf3oVlHNx/gF0fY= 55 | 56 | config@^3.2.2: 57 | version "3.2.2" 58 | resolved "https://registry.npm.taobao.org/config/download/config-3.2.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fconfig%2Fdownload%2Fconfig-3.2.2.tgz#c7d31bb2a9d30013a905ff420101e3b1ba58eead" 59 | integrity sha1-x9MbsqnTABOpBf9CAQHjsbpY7q0= 60 | dependencies: 61 | json5 "^1.0.1" 62 | 63 | content-disposition@0.5.3: 64 | version "0.5.3" 65 | resolved "https://registry.npm.taobao.org/content-disposition/download/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" 66 | integrity sha1-4TDK9+cnkIfFYWwgB9BIVpiYT70= 67 | dependencies: 68 | safe-buffer "5.1.2" 69 | 70 | content-type@~1.0.4: 71 | version "1.0.4" 72 | resolved "https://registry.npm.taobao.org/content-type/download/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" 73 | integrity sha1-4TjMdeBAxyexlm/l5fjJruJW/js= 74 | 75 | cookie-signature@1.0.6: 76 | version "1.0.6" 77 | resolved "https://registry.npm.taobao.org/cookie-signature/download/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" 78 | integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= 79 | 80 | cookie@0.4.0: 81 | version "0.4.0" 82 | resolved "https://registry.npm.taobao.org/cookie/download/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" 83 | integrity sha1-vrQ35wIrO21JAZ0IhmUwPr6cFLo= 84 | 85 | debug@2.6.9: 86 | version "2.6.9" 87 | resolved "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 88 | integrity sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8= 89 | dependencies: 90 | ms "2.0.0" 91 | 92 | debug@3.1.0: 93 | version "3.1.0" 94 | resolved "https://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" 95 | integrity sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE= 96 | dependencies: 97 | ms "2.0.0" 98 | 99 | depd@~1.1.2: 100 | version "1.1.2" 101 | resolved "https://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" 102 | integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= 103 | 104 | destroy@~1.0.4: 105 | version "1.0.4" 106 | resolved "https://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 107 | integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= 108 | 109 | ee-first@1.1.1: 110 | version "1.1.1" 111 | resolved "https://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 112 | integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= 113 | 114 | encodeurl@~1.0.2: 115 | version "1.0.2" 116 | resolved "https://registry.npm.taobao.org/encodeurl/download/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" 117 | integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= 118 | 119 | escape-html@~1.0.3: 120 | version "1.0.3" 121 | resolved "https://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 122 | integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= 123 | 124 | etag@~1.8.1: 125 | version "1.8.1" 126 | resolved "https://registry.npm.taobao.org/etag/download/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 127 | integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= 128 | 129 | express@^4.17.1: 130 | version "4.17.1" 131 | resolved "https://registry.npm.taobao.org/express/download/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" 132 | integrity sha1-RJH8OGBc9R+GKdOcK10Cb5ikwTQ= 133 | dependencies: 134 | accepts "~1.3.7" 135 | array-flatten "1.1.1" 136 | body-parser "1.19.0" 137 | content-disposition "0.5.3" 138 | content-type "~1.0.4" 139 | cookie "0.4.0" 140 | cookie-signature "1.0.6" 141 | debug "2.6.9" 142 | depd "~1.1.2" 143 | encodeurl "~1.0.2" 144 | escape-html "~1.0.3" 145 | etag "~1.8.1" 146 | finalhandler "~1.1.2" 147 | fresh "0.5.2" 148 | merge-descriptors "1.0.1" 149 | methods "~1.1.2" 150 | on-finished "~2.3.0" 151 | parseurl "~1.3.3" 152 | path-to-regexp "0.1.7" 153 | proxy-addr "~2.0.5" 154 | qs "6.7.0" 155 | range-parser "~1.2.1" 156 | safe-buffer "5.1.2" 157 | send "0.17.1" 158 | serve-static "1.14.1" 159 | setprototypeof "1.1.1" 160 | statuses "~1.5.0" 161 | type-is "~1.6.18" 162 | utils-merge "1.0.1" 163 | vary "~1.1.2" 164 | 165 | finalhandler@~1.1.2: 166 | version "1.1.2" 167 | resolved "https://registry.npm.taobao.org/finalhandler/download/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" 168 | integrity sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0= 169 | dependencies: 170 | debug "2.6.9" 171 | encodeurl "~1.0.2" 172 | escape-html "~1.0.3" 173 | on-finished "~2.3.0" 174 | parseurl "~1.3.3" 175 | statuses "~1.5.0" 176 | unpipe "~1.0.0" 177 | 178 | forwarded@~0.1.2: 179 | version "0.1.2" 180 | resolved "https://registry.npm.taobao.org/forwarded/download/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" 181 | integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= 182 | 183 | fresh@0.5.2: 184 | version "0.5.2" 185 | resolved "https://registry.npm.taobao.org/fresh/download/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" 186 | integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= 187 | 188 | http-errors@1.7.2: 189 | version "1.7.2" 190 | resolved "https://registry.npm.taobao.org/http-errors/download/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" 191 | integrity sha1-T1ApzxMjnzEDblsuVSkrz7zIXI8= 192 | dependencies: 193 | depd "~1.1.2" 194 | inherits "2.0.3" 195 | setprototypeof "1.1.1" 196 | statuses ">= 1.5.0 < 2" 197 | toidentifier "1.0.0" 198 | 199 | http-errors@~1.7.2: 200 | version "1.7.3" 201 | resolved "https://registry.npm.taobao.org/http-errors/download/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" 202 | integrity sha1-bGGeT5xgMIw4UZSYwU+7EKrOuwY= 203 | dependencies: 204 | depd "~1.1.2" 205 | inherits "2.0.4" 206 | setprototypeof "1.1.1" 207 | statuses ">= 1.5.0 < 2" 208 | toidentifier "1.0.0" 209 | 210 | iconv-lite@0.4.24: 211 | version "0.4.24" 212 | resolved "https://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 213 | integrity sha1-ICK0sl+93CHS9SSXSkdKr+czkIs= 214 | dependencies: 215 | safer-buffer ">= 2.1.2 < 3" 216 | 217 | inherits@2.0.3: 218 | version "2.0.3" 219 | resolved "https://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz?cache=0&sync_timestamp=1560975547815&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Finherits%2Fdownload%2Finherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 220 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 221 | 222 | inherits@2.0.4: 223 | version "2.0.4" 224 | resolved "https://registry.npm.taobao.org/inherits/download/inherits-2.0.4.tgz?cache=0&sync_timestamp=1560975547815&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Finherits%2Fdownload%2Finherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 225 | integrity sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w= 226 | 227 | ipaddr.js@1.9.0: 228 | version "1.9.0" 229 | resolved "https://registry.npm.taobao.org/ipaddr.js/download/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" 230 | integrity sha1-N9905DCg5HVQ/lSi3v4w2KzZX2U= 231 | 232 | json5@^1.0.1: 233 | version "1.0.1" 234 | resolved "https://registry.npm.taobao.org/json5/download/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" 235 | integrity sha1-d5+wAYYE+oVOrL9iUhgNg1Q+Pb4= 236 | dependencies: 237 | minimist "^1.2.0" 238 | 239 | kareem@2.3.0: 240 | version "2.3.0" 241 | resolved "https://registry.npm.taobao.org/kareem/download/kareem-2.3.0.tgz#ef33c42e9024dce511eeaf440cd684f3af1fc769" 242 | integrity sha1-7zPELpAk3OUR7q9EDNaE868fx2k= 243 | 244 | lodash@^4.17.11: 245 | version "4.17.15" 246 | resolved "https://registry.npm.taobao.org/lodash/download/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" 247 | integrity sha1-tEf2ZwoEVbv+7dETku/zMOoJdUg= 248 | 249 | media-typer@0.3.0: 250 | version "0.3.0" 251 | resolved "https://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 252 | integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= 253 | 254 | memory-pager@^1.0.2: 255 | version "1.5.0" 256 | resolved "https://registry.npm.taobao.org/memory-pager/download/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" 257 | integrity sha1-2HUWVdItOEaCdByXLyw9bfo+ZrU= 258 | 259 | merge-descriptors@1.0.1: 260 | version "1.0.1" 261 | resolved "https://registry.npm.taobao.org/merge-descriptors/download/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 262 | integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= 263 | 264 | methods@~1.1.2: 265 | version "1.1.2" 266 | resolved "https://registry.npm.taobao.org/methods/download/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 267 | integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= 268 | 269 | mime-db@1.40.0: 270 | version "1.40.0" 271 | resolved "https://registry.npm.taobao.org/mime-db/download/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" 272 | integrity sha1-plBX6ZjbCQ9zKmj2wnbTh9QSbDI= 273 | 274 | mime-types@~2.1.24: 275 | version "2.1.24" 276 | resolved "https://registry.npm.taobao.org/mime-types/download/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" 277 | integrity sha1-tvjQs+lR77d97eyhlM/20W9nb4E= 278 | dependencies: 279 | mime-db "1.40.0" 280 | 281 | mime@1.6.0: 282 | version "1.6.0" 283 | resolved "https://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" 284 | integrity sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE= 285 | 286 | minimist@^1.2.0: 287 | version "1.2.0" 288 | resolved "https://registry.npm.taobao.org/minimist/download/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 289 | integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= 290 | 291 | mongodb-core@3.2.7: 292 | version "3.2.7" 293 | resolved "https://registry.npm.taobao.org/mongodb-core/download/mongodb-core-3.2.7.tgz#a8ef1fe764a192c979252dacbc600dc88d77e28f" 294 | integrity sha1-qO8f52Shksl5JS2svGANyI134o8= 295 | dependencies: 296 | bson "^1.1.1" 297 | require_optional "^1.0.1" 298 | safe-buffer "^5.1.2" 299 | optionalDependencies: 300 | saslprep "^1.0.0" 301 | 302 | mongodb@3.2.7: 303 | version "3.2.7" 304 | resolved "https://registry.npm.taobao.org/mongodb/download/mongodb-3.2.7.tgz#8ba149e4be708257cad0dea72aebb2bbb311a7ac" 305 | integrity sha1-i6FJ5L5wglfK0N6nKuuyu7MRp6w= 306 | dependencies: 307 | mongodb-core "3.2.7" 308 | safe-buffer "^5.1.2" 309 | 310 | mongoose-legacy-pluralize@1.0.2: 311 | version "1.0.2" 312 | resolved "https://registry.npm.taobao.org/mongoose-legacy-pluralize/download/mongoose-legacy-pluralize-1.0.2.tgz#3ba9f91fa507b5186d399fb40854bff18fb563e4" 313 | integrity sha1-O6n5H6UHtRhtOZ+0CFS/8Y+1Y+Q= 314 | 315 | mongoose@^5.6.9: 316 | version "5.6.9" 317 | resolved "https://registry.npm.taobao.org/mongoose/download/mongoose-5.6.9.tgz#86bdf258c5348a5c4a6acaf944d7baa73e3d1707" 318 | integrity sha1-hr3yWMU0ilxKasr5RNe6pz49Fwc= 319 | dependencies: 320 | async "2.6.2" 321 | bson "~1.1.1" 322 | kareem "2.3.0" 323 | mongodb "3.2.7" 324 | mongodb-core "3.2.7" 325 | mongoose-legacy-pluralize "1.0.2" 326 | mpath "0.6.0" 327 | mquery "3.2.1" 328 | ms "2.1.2" 329 | regexp-clone "1.0.0" 330 | safe-buffer "5.1.2" 331 | sift "7.0.1" 332 | sliced "1.0.1" 333 | 334 | mpath@0.6.0: 335 | version "0.6.0" 336 | resolved "https://registry.npm.taobao.org/mpath/download/mpath-0.6.0.tgz#aa922029fca4f0f641f360e74c5c1b6a4c47078e" 337 | integrity sha1-qpIgKfyk8PZB82DnTFwbakxHB44= 338 | 339 | mquery@3.2.1: 340 | version "3.2.1" 341 | resolved "https://registry.npm.taobao.org/mquery/download/mquery-3.2.1.tgz#8b059a49cdae0a8a9e804284ef64c2f58d3ac05d" 342 | integrity sha1-iwWaSc2uCoqegEKE72TC9Y06wF0= 343 | dependencies: 344 | bluebird "3.5.1" 345 | debug "3.1.0" 346 | regexp-clone "^1.0.0" 347 | safe-buffer "5.1.2" 348 | sliced "1.0.1" 349 | 350 | ms@2.0.0: 351 | version "2.0.0" 352 | resolved "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 353 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 354 | 355 | ms@2.1.1: 356 | version "2.1.1" 357 | resolved "https://registry.npm.taobao.org/ms/download/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" 358 | integrity sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo= 359 | 360 | ms@2.1.2: 361 | version "2.1.2" 362 | resolved "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 363 | integrity sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk= 364 | 365 | nanoid@^2.0.0: 366 | version "2.0.3" 367 | resolved "https://registry.npm.taobao.org/nanoid/download/nanoid-2.0.3.tgz#dde999e173bc9d7bd2ee2746b89909ade98e075e" 368 | integrity sha1-3emZ4XO8nXvS7idGuJkJremOB14= 369 | 370 | negotiator@0.6.2: 371 | version "0.6.2" 372 | resolved "https://registry.npm.taobao.org/negotiator/download/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" 373 | integrity sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs= 374 | 375 | on-finished@~2.3.0: 376 | version "2.3.0" 377 | resolved "https://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 378 | integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= 379 | dependencies: 380 | ee-first "1.1.1" 381 | 382 | parseurl@~1.3.3: 383 | version "1.3.3" 384 | resolved "https://registry.npm.taobao.org/parseurl/download/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" 385 | integrity sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ= 386 | 387 | path-to-regexp@0.1.7: 388 | version "0.1.7" 389 | resolved "https://registry.npm.taobao.org/path-to-regexp/download/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" 390 | integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= 391 | 392 | proxy-addr@~2.0.5: 393 | version "2.0.5" 394 | resolved "https://registry.npm.taobao.org/proxy-addr/download/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" 395 | integrity sha1-NMvWSi2B9LH9IedvnwbIpFKZ7jQ= 396 | dependencies: 397 | forwarded "~0.1.2" 398 | ipaddr.js "1.9.0" 399 | 400 | qs@6.7.0: 401 | version "6.7.0" 402 | resolved "https://registry.npm.taobao.org/qs/download/qs-6.7.0.tgz?cache=0&sync_timestamp=1566009952956&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fqs%2Fdownload%2Fqs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" 403 | integrity sha1-QdwaAV49WB8WIXdr4xr7KHapsbw= 404 | 405 | range-parser@~1.2.1: 406 | version "1.2.1" 407 | resolved "https://registry.npm.taobao.org/range-parser/download/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" 408 | integrity sha1-PPNwI9GZ4cJNGlW4SADC8+ZGgDE= 409 | 410 | raw-body@2.4.0: 411 | version "2.4.0" 412 | resolved "https://registry.npm.taobao.org/raw-body/download/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" 413 | integrity sha1-oc5vucm8NWylLoklarWQWeE9AzI= 414 | dependencies: 415 | bytes "3.1.0" 416 | http-errors "1.7.2" 417 | iconv-lite "0.4.24" 418 | unpipe "1.0.0" 419 | 420 | regexp-clone@1.0.0, regexp-clone@^1.0.0: 421 | version "1.0.0" 422 | resolved "https://registry.npm.taobao.org/regexp-clone/download/regexp-clone-1.0.0.tgz#222db967623277056260b992626354a04ce9bf63" 423 | integrity sha1-Ii25Z2IydwViYLmSYmNUoEzpv2M= 424 | 425 | require_optional@^1.0.1: 426 | version "1.0.1" 427 | resolved "https://registry.npm.taobao.org/require_optional/download/require_optional-1.0.1.tgz#4cf35a4247f64ca3df8c2ef208cc494b1ca8fc2e" 428 | integrity sha1-TPNaQkf2TKPfjC7yCMxJSxyo/C4= 429 | dependencies: 430 | resolve-from "^2.0.0" 431 | semver "^5.1.0" 432 | 433 | resolve-from@^2.0.0: 434 | version "2.0.0" 435 | resolved "https://registry.npm.taobao.org/resolve-from/download/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" 436 | integrity sha1-lICrIOlP+h2egKgEx+oUdhGWa1c= 437 | 438 | safe-buffer@5.1.2: 439 | version "5.1.2" 440 | resolved "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 441 | integrity sha1-mR7GnSluAxN0fVm9/St0XDX4go0= 442 | 443 | safe-buffer@^5.1.2: 444 | version "5.2.0" 445 | resolved "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" 446 | integrity sha1-t02uxJsRSPiMZLaNSbHoFcHy9Rk= 447 | 448 | "safer-buffer@>= 2.1.2 < 3": 449 | version "2.1.2" 450 | resolved "https://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 451 | integrity sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo= 452 | 453 | saslprep@^1.0.0: 454 | version "1.0.3" 455 | resolved "https://registry.npm.taobao.org/saslprep/download/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226" 456 | integrity sha1-TAL5RrVs9UKX40e6EJPnrKxM8iY= 457 | dependencies: 458 | sparse-bitfield "^3.0.3" 459 | 460 | semver@^5.1.0: 461 | version "5.7.1" 462 | resolved "https://registry.npm.taobao.org/semver/download/semver-5.7.1.tgz?cache=0&sync_timestamp=1565627380363&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 463 | integrity sha1-qVT5Ma66UI0we78Gnv8MAclhFvc= 464 | 465 | send@0.17.1: 466 | version "0.17.1" 467 | resolved "https://registry.npm.taobao.org/send/download/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" 468 | integrity sha1-wdiwWfeQD3Rm3Uk4vcROEd2zdsg= 469 | dependencies: 470 | debug "2.6.9" 471 | depd "~1.1.2" 472 | destroy "~1.0.4" 473 | encodeurl "~1.0.2" 474 | escape-html "~1.0.3" 475 | etag "~1.8.1" 476 | fresh "0.5.2" 477 | http-errors "~1.7.2" 478 | mime "1.6.0" 479 | ms "2.1.1" 480 | on-finished "~2.3.0" 481 | range-parser "~1.2.1" 482 | statuses "~1.5.0" 483 | 484 | serve-static@1.14.1: 485 | version "1.14.1" 486 | resolved "https://registry.npm.taobao.org/serve-static/download/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" 487 | integrity sha1-Zm5jbcTwEPfvKZcKiKZ0MgiYsvk= 488 | dependencies: 489 | encodeurl "~1.0.2" 490 | escape-html "~1.0.3" 491 | parseurl "~1.3.3" 492 | send "0.17.1" 493 | 494 | setprototypeof@1.1.1: 495 | version "1.1.1" 496 | resolved "https://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" 497 | integrity sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM= 498 | 499 | shortid@^2.2.14: 500 | version "2.2.14" 501 | resolved "https://registry.npm.taobao.org/shortid/download/shortid-2.2.14.tgz#80db6aafcbc3e3a46850b3c88d39e051b84c8d18" 502 | integrity sha1-gNtqr8vD46RoULPIjTngUbhMjRg= 503 | dependencies: 504 | nanoid "^2.0.0" 505 | 506 | sift@7.0.1: 507 | version "7.0.1" 508 | resolved "https://registry.npm.taobao.org/sift/download/sift-7.0.1.tgz#47d62c50b159d316f1372f8b53f9c10cd21a4b08" 509 | integrity sha1-R9YsULFZ0xbxNy+LU/nBDNIaSwg= 510 | 511 | sliced@1.0.1: 512 | version "1.0.1" 513 | resolved "https://registry.npm.taobao.org/sliced/download/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41" 514 | integrity sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E= 515 | 516 | sparse-bitfield@^3.0.3: 517 | version "3.0.3" 518 | resolved "https://registry.npm.taobao.org/sparse-bitfield/download/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11" 519 | integrity sha1-/0rm5oZWBWuks+eSqzM004JzyhE= 520 | dependencies: 521 | memory-pager "^1.0.2" 522 | 523 | "statuses@>= 1.5.0 < 2", statuses@~1.5.0: 524 | version "1.5.0" 525 | resolved "https://registry.npm.taobao.org/statuses/download/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" 526 | integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= 527 | 528 | toidentifier@1.0.0: 529 | version "1.0.0" 530 | resolved "https://registry.npm.taobao.org/toidentifier/download/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" 531 | integrity sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM= 532 | 533 | type-is@~1.6.17, type-is@~1.6.18: 534 | version "1.6.18" 535 | resolved "https://registry.npm.taobao.org/type-is/download/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" 536 | integrity sha1-TlUs0F3wlGfcvE73Od6J8s83wTE= 537 | dependencies: 538 | media-typer "0.3.0" 539 | mime-types "~2.1.24" 540 | 541 | unpipe@1.0.0, unpipe@~1.0.0: 542 | version "1.0.0" 543 | resolved "https://registry.npm.taobao.org/unpipe/download/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 544 | integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= 545 | 546 | utils-merge@1.0.1: 547 | version "1.0.1" 548 | resolved "https://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" 549 | integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= 550 | 551 | valid-url@^1.0.9: 552 | version "1.0.9" 553 | resolved "https://registry.npm.taobao.org/valid-url/download/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" 554 | integrity sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA= 555 | 556 | vary@~1.1.2: 557 | version "1.1.2" 558 | resolved "https://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" 559 | integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= 560 | --------------------------------------------------------------------------------