diff --git a/docs/api.yml b/docs/api.yml deleted file mode 100644 index e69de29..0000000 diff --git a/docs/swagger.yml b/docs/swagger.yml index 1b544ce..faa5a7f 100644 --- a/docs/swagger.yml +++ b/docs/swagger.yml @@ -1,35 +1,22 @@ openapi: "3.0.0" +servers: +- url: http://192.168.31.161:3000/v1 + description: 测试服务器 info: title: 书签同步助手后端接口 - version: 0.0.1 + version: 1.0.0 tags: - - name: User - description: 用户管理 + - name: Version + description: 接口版本号 - name: Bookmark description: 书签管理 paths: - /user/login: - post: - operationId: userLogin - summary: 用户登录 + /version: + get: + operationId: getVersion + summary: 获得接口版本号 tags: - - User - requestBody: - description: 登录信息。 - required: true - content: - application/json: - schema: - type: object - properties: - account: - description: 账号 - type: string - example: 'admin' - password: - description: 密码 - type: string - example: 'secret' + - Version responses: '200': description: 200 response @@ -43,36 +30,15 @@ paths: message: type: string description: 响应信息,响应码为非20000时表示错误信息 - examples: - 20000: - value: { - "code": 20000, - "message": "OK" - } - /user/logout: - post: - operationId: userLogout - summary: 登录注销 - tags: - - User - responses: - '200': - description: 200 response - content: - application/json: - schema: - type: object - properties: - code: - $ref: '#/components/schemas/ResponseCode' - message: + data: type: string - description: 响应信息,响应码为非20000时表示错误信息 + description: 版本号信息 examples: 20000: value: { "code": 20000, - "message": "OK" + "message": "OK", + "data": "1.0.0" } /bookmark: get: @@ -110,7 +76,7 @@ paths: tags: - Bookmark requestBody: - description: 需要添加的职工信息,注意 employeeId 字段是无效的。 + description: 书签列表 required: true content: application/json: @@ -137,19 +103,16 @@ paths: "code": 20000, "message": "OK" } + components: schemas: ResponseCode: type: integer enum: - 20000 - - 40000 - - 40100 description: > 响应码: * `20000` - 表明调用成功 - * `40000` - 用户名或密码错误 - * `40001` - token已过期 Bookmark: type: object properties: @@ -159,12 +122,6 @@ components: parentId: description: 父节点的ID,根节点没有此属性 type: string - dateAdded: - description: 书签节点创建时的时间戳 - type: integer - dateGroupModified: - description: 书签文件夹内容的最后更新时间戳,书签节点没有此属性 - type: integer index: description: 书签在父节点中的索引,根节点没有此属性 type: integer diff --git a/extension/popup.js b/extension/js/popup.js similarity index 58% rename from extension/popup.js rename to extension/js/popup.js index 970ed87..ae56979 100644 --- a/extension/popup.js +++ b/extension/js/popup.js @@ -1,11 +1,12 @@ let api = {}; +let serverApi = {}; // chrome bookmark api wrapper -(function(api) { +(function (api) { /** * 获得整个书签树 */ const getTree = () => { - return new Promise(resolve => { + return new Promise((resolve) => { chrome.bookmarks.getTree(resolve); }); }; @@ -18,12 +19,12 @@ let api = {}; * 返回的书签数组中是不包含children字段的,即不包含子节点以下的节点 * @param {String} id 父书签组id */ - const getChildren = id => { - return new Promise(resolve => { + const getChildren = (id) => { + return new Promise((resolve) => { chrome.bookmarks.getChildren(id, resolve); }); }; - const getChildrenAsync = async id => { + const getChildrenAsync = async (id) => { return await getChildren(id); }; @@ -32,12 +33,12 @@ let api = {}; * 返回的书签数组中包含children字段,即包含子节点以下的节点 * @param {String} id 父书签组id */ - const getSubTree = id => { - return new Promise(resolve => { + const getSubTree = (id) => { + return new Promise((resolve) => { chrome.bookmarks.getSubTree(id, resolve); }); }; - const getSubTreeAsync = async id => { + const getSubTreeAsync = async (id) => { return await getSubTree(id); }; @@ -45,12 +46,12 @@ let api = {}; * 删除指定id的书签 * @param {String} id 需要删除的书签的id */ - const remove = id => { - return new Promise(resolve => { + const remove = (id) => { + return new Promise((resolve) => { chrome.bookmarks.remove(id, resolve); }); }; - const removeAsync = async id => { + const removeAsync = async (id) => { return await remove(id); }; @@ -58,12 +59,12 @@ let api = {}; * 删除指定id的空书签组,如果书签组下有子书签或子书签组,删除将失败 * @param {String} id 需要删除的书签文件夹id */ - const removeTree = id => { - return new Promise(resolve => { + const removeTree = (id) => { + return new Promise((resolve) => { chrome.bookmarks.removeTree(id, resolve); }); }; - const removeTreeAsync = async id => { + const removeTreeAsync = async (id) => { await removeTree(id); }; @@ -75,12 +76,12 @@ let api = {}; * string (optional) title * string (optional) url 如果为NULL或者不填,则代表一个书签组文件夹 */ - const create = bookmark => { - return new Promise(resolve => { + const create = (bookmark) => { + return new Promise((resolve) => { chrome.bookmarks.create(bookmark, resolve); }); }; - const createAsync = async bookmark => { + const createAsync = async (bookmark) => { return await create(bookmark); }; @@ -92,6 +93,65 @@ let api = {}; api.createAsync = createAsync; })(api); +// backend server api wrapper +(function (api) { + const API_VERSION = "v1"; + let serverAddress = ""; + + const setServerAddress = (address) => { + serverAddress = address; + }; + + const getServerAddress = () => { + return serverAddress; + }; + + const getVersion = () => { + return new Promise((resolve, reject) => { + $.getJSON(`${serverAddress}/${API_VERSION}/version`, (response) => { + const { code, message, data } = response; + if (code === 20000) { + resolve(data); + } else { + reject(new Error(message)); + } + }); + }); + }; + + const uploadBookmarks = data => { + return new Promise((resolve, reject) => { + $.post(`${serverAddress}/${API_VERSION}/bookmark`, data, (response) => { + const { code, message, data } = response; + if (code === 20000) { + resolve(data); + } else { + reject(new Error(message)); + } + }, 'json'); + }); + }; + + const downloadBookmarks = () => { + return new Promise((resolve, reject) => { + $.getJSON(`${serverAddress}/${API_VERSION}/bookmark`, (response) => { + const { code, message, data } = response; + if (code === 20000) { + resolve(data); + } else { + reject(new Error(message)); + } + }); + }); + }; + + api.setServerAddress = setServerAddress; + api.getServerAddress = getServerAddress; + api.getVersion = getVersion; + api.uploadBookmarks = uploadBookmarks; + api.downloadBookmarks = downloadBookmarks; +})(serverApi); + function Node() { /** * id ( string ) @@ -105,11 +165,11 @@ function Node() { /** * 书签节点创建时的时间戳 */ - this.dateAdded = 0; + this.dateAdded = undefined; /** * 书签文件夹内容的最后更新时间戳,书签节点没有此属性 */ - this.dateGroupModified = 0; + this.dateGroupModified = undefined; /** * 书签在父节点中的索引,根节点没有此属性 */ @@ -143,7 +203,7 @@ let BookmarkTreeNodeList = []; const BOOKMARK = 0; const BOOKEMARK_FOLDER = 1; const PROTO = "http"; -const SERVER_ADDRESS = "127.0.0.1"; +const SERVER_ADDRESS = "192.168.31.161"; const SERVER_PORT = "3000"; const SERVER_URL = `${PROTO}://${SERVER_ADDRESS}:${SERVER_PORT}`; @@ -162,8 +222,6 @@ async function getBookmarkMap() { let bookmarkNode = new Node(); bookmarkNode.id = node.id; bookmarkNode.parentId = node.parentId; - // bookmarkNode.dateAdded = node.dateAdded; - // bookmarkNode.dateGroupModified = node.dateGroupModified; bookmarkNode.index = node.index; bookmarkNode.title = node.title; bookmarkNode.url = node.url; @@ -200,76 +258,84 @@ async function getBookmarkMap() { return localMap; } -/** - * 获得浏览器书签的数组 - */ -async function getBookmarkList() { - let localTree = await api.getTreeAsync(); - let localList = []; - - async function addToList(localTree) { - for (let i = 0; i < localTree.length; i++) { - let node = localTree[i]; - - let bookmarkNode = new Node(); - bookmarkNode.id = node.id; - bookmarkNode.parentId = node.parentId; - // bookmarkNode.dateAdded = node.dateAdded; - // bookmarkNode.dateGroupModified = node.dateGroupModified; - bookmarkNode.index = node.index; - bookmarkNode.title = node.title; - bookmarkNode.url = node.url; - bookmarkNode.type = - typeof node.dateGroupModified === "undefined" - ? BOOKMARK - : BOOKEMARK_FOLDER; - bookmarkNode.root = typeof node.parentId === "undefined"; - // 根节点没有 dateGroupModified 属性,但是应该是个书签组类型 - if (bookmarkNode.root) { - bookmarkNode.type = BOOKEMARK_FOLDER; +function tree2List(tree) { + let cacheMap = {}; + function add2Map(tree) { + for (let item of tree) { + // 给书签文件创建group属性 + if (item.id === "0" || item.id === "1" || item.id === "2" || typeof item.dateGroupModified === 'number') { + item.group = true } + cacheMap[item.id] = item; + if (Array.isArray(item.children)) { + add2Map(item.children); + } + } + } + add2Map(tree); - localList.push(bookmarkNode); - - if (Array.isArray(node.children) && node.children.length > 0) { - await addToList(node.children) + for (let key in cacheMap) { + let item = cacheMap[key]; + if (typeof item.parentId === "string") { + if (!Array.isArray(cacheMap[item.parentId].nodes)) { + cacheMap[item.parentId].nodes = []; } + cacheMap[item.parentId].nodes.push(item.id); } } - await addToList(localTree); - return localList; + let list = []; + for (let key in cacheMap) { + const item = cacheMap[key]; + list.push({ + id: item.id, + parentId: item.parentId, + title: item.title, + url: item.url, + index: item.index, + group: item.group, + nodes: item.nodes, + }); + } + + return list; +} +/** + * 获得浏览器书签的数组 + */ +async function getBookmarkList() { + let localTree = await api.getTreeAsync(); + return tree2List(localTree); } /** * 数组转map表 - * @param {Object} bookmarkList + * @param {Object} bookmarkList */ function listToMap(bookmarkList) { - let map = {} - for(let i = 0; i < bookmarkList.length; i++) { - let bookmark = bookmarkList[i] - map[bookmark.id] = bookmark + let map = {}; + for (let i = 0; i < bookmarkList.length; i++) { + let bookmark = bookmarkList[i]; + map[bookmark.id] = bookmark; } - function getDepth(bookmark, depth) { - if (parseInt(bookmark.id) === 0) { - return depth - } else { - return getDepth(map[bookmark.parentId], depth + 1) - } + if (parseInt(bookmark.id) === 0) { + return depth; + } else { + return getDepth(map[bookmark.parentId], depth + 1); + } } - let finalMap = {} - for(let i = 0; i < bookmarkList.length; i++) { - let bookmark = bookmarkList[i] - let finalDepth = getDepth(bookmark, 0) - if (!Array.isArray(finalMap[finalDepth])) { - finalMap[finalDepth] = [] - } - finalMap[finalDepth].push(bookmark) + let finalMap = {}; + for (let i = 0; i < bookmarkList.length; i++) { + let bookmark = bookmarkList[i]; + let finalDepth = getDepth(bookmark, 0); + if (!Array.isArray(finalMap[finalDepth])) { + finalMap[finalDepth] = []; + } + finalMap[finalDepth].push(bookmark); } - return finalMap + return finalMap; } async function removeAllBookmarks() { @@ -304,7 +370,7 @@ async function restore(remoteBookmarkArray) { parentId: getNewbookmarkId(array[depth - 1], bookmark.parentId), index: bookmark.index, title: bookmark.title, - url: bookmark.url + url: bookmark.url, }); bookmark.newId = newBookmark.id; console.log( @@ -319,30 +385,25 @@ async function restore(remoteBookmarkArray) { $("#upload").on("click", async () => { let bookmarkArray = await getBookmarkList(); - $.ajax({ - type: "POST", - url: `${SERVER_URL}/bookmarks`, - contentType: "application/json;charset=utf-8", - dataType: "json", - data: JSON.stringify(bookmarkArray), - success: function() {}, - error: function() {} + console.log(bookmarkArray) + serverApi.uploadBookmarks({ + bookmarks: bookmarkArray }); }); -$("#download").on("click", function() { - $.ajax({ - type: "GET", - url: `${SERVER_URL}/bookmarks`, - success: function(result) { - if (result.code === 0) { - restore(result.data); - } - } - }); +$("#download").on("click", async () => { + const bookmarkArray = await serverApi.downloadBookmarks(); + console.log(bookmarkArray); }); $("#test").on("click", async () => { let array = await getBookmarkList(); console.log(array); }); + +$("#connect").on("click", async () => { + const serverAddress = $("#server").val() || SERVER_URL; + serverApi.setServerAddress(serverAddress); + const version = await serverApi.getVersion(); + $("#version").html(version); +}); diff --git a/extension/popup.html b/extension/popup.html index 5b63458..93714cf 100644 --- a/extension/popup.html +++ b/extension/popup.html @@ -7,9 +7,15 @@ Document +
+ 服务器地址 + + + +
- + \ No newline at end of file diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 0000000..1261a5a --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1 @@ +FROM node:12.16.1 diff --git a/server/bootstrap.js b/server/bootstrap.js new file mode 100644 index 0000000..69b9ce1 --- /dev/null +++ b/server/bootstrap.js @@ -0,0 +1,21 @@ +const express = require("express"); +const cors = require("cors"); +const app = express(); +const routerV1 = require("./router/v1"); +const port = 3000; + +app.use(cors()); +app.use(express.json({ + limit: '50mb' +})); // for parsing application/json +app.use(express.urlencoded({ + limit: '50mb', + parameterLimit: 10000, + extended: true +})); // for parsing application/x-www-form-urlencoded + +app.use("/v1", routerV1); + +app.listen(port, () => { + console.log("server is running at port " + port); +}); diff --git a/server/config/index.js b/server/config/index.js new file mode 100644 index 0000000..425c119 --- /dev/null +++ b/server/config/index.js @@ -0,0 +1,39 @@ +const MONGO_SERVER = "localhost"; +const MONGO_PORT = 27017; +const MONGO_USER = "root"; +const MONGO_PASSWORD = "P9DeuFJ32d4img=="; +const MONGO_DB = "data"; + +const getMongoServer = () => { + return process.env.MONGO_SERVER || MONGO_SERVER; +}; + +const getMongoPort = () => { + return process.env.MONGO_PORT || MONGO_PORT; +}; + +const getMongoUser = () => { + return process.env.MONGO_USER || MONGO_USER; +}; + +const getMongoPassword = () => { + return process.env.MONGO_PASSWORD || MONGO_PASSWORD; +}; + +const getMongoDB = () => { + return process.env.MONGO_DB || MONGO_DB; +}; + +const getMongoUrl = () => { + return ( + process.env.MONGO_URL || + `mongodb://${getMongoUser()}:${getMongoPassword()}@${getMongoServer()}:${getMongoPort()}` + ); +}; + +exports.getMongoServer = getMongoServer; +exports.getMongoPort = getMongoPort; +exports.getMongoUser = getMongoUser; +exports.getMongoPassword = getMongoPassword; +exports.getMongoDB = getMongoDB; +exports.getMongoUrl = getMongoUrl; diff --git a/server/db.js b/server/db.js deleted file mode 100644 index 610b477..0000000 --- a/server/db.js +++ /dev/null @@ -1,79 +0,0 @@ -// const MongoClient = require('mongodb').MongoClient -const assert = require("assert"); -const fs = require("fs"); -const path = require("path"); -const crypto = require('crypto'); - -// const DATABASE_NAME = 'bookmarksdb' -// const DATABASE_SERVER = '192.168.31.201' -// const DATABASE_PORT = 27017 -// const USER = encodeURIComponent('root'); -// const PASSWORD = encodeURIComponent('root123'); -// const AUTHMECHANISM = 'SCRAM-SHA-1'; -// const DATABASE_URL = `mongodb://${USER}:${PASSWORD}@${DATABASE_SERVER}:${DATABASE_PORT}/?authMechanism=${AUTHMECHANISM}&authSource=${DATABASE_NAME}` -// const DATABASE_URL = `mongodb://${DATABASE_SERVER}:${DATABASE_PORT}/${DATABASE_NAME}` -// const COLLECTION = 'bookmarks' - -// const inertData = (db, callback) => { -// const collection = db.collection('bookmarks') -// let data = [{ -// id: '0', -// title: "根书签" -// }] -// collection.insertMany(data, (error, result) => { -// assert.equal(null, error) -// assert(1, result.result.n) -// assert(1, result.ops.length) -// callback(result) -// }) -// } - -exports.connect = () => { - // const client = new MongoClient(DATABASE_URL) - // client.connect((error) => { - // assert.equal(null, error) - // const db = client.db(DATABASE_NAME) - // console.log('connect ' + DATABASE_URL + ' successfully') - // inertData(db, result => { - // client.close() - // }) - // }) -}; - -exports.save = (bookmarkArray, callback) => { - const bookmarkStr = JSON.stringify(bookmarkArray); - // 计算hash - const hash = crypto - .createHash("sha256") - .update(JSON.stringify(bookmarkArray)) - .digest("base64"); - console.log(hash); - fs.writeFile( - path.join("database", Date.now() + ".json"), - bookmarkStr, - err => { - callback(err); - } - ); -}; - -exports.get = callback => { - fs.readdir(path.join("database"), (err, files) => { - if (err) throw err; - let latestFile = 0; - for (let i = 0; i < files.length; i++) { - let filename = files[i]; - const time = filename.substr(0, filename.length - 5); - if (time > latestFile) { - latestFile = time; - } - } - fs.readFile( - path.join("database", latestFile + ".json"), - "utf8", - (error, data) => { - callback(error, JSON.parse(data)); - } - ); - }); -}; diff --git a/server/db/index.js b/server/db/index.js new file mode 100644 index 0000000..5158041 --- /dev/null +++ b/server/db/index.js @@ -0,0 +1,66 @@ +const MongoClient = require("mongodb").MongoClient; +const assert = require("assert"); +const fs = require("fs"); +const path = require("path"); +const crypto = require("crypto"); + +const config = require("../config"); + +const connect = () => { + return new Promise((resolve, reject) => { + const url = config.getMongoUrl(); + MongoClient.connect(url, { useNewUrlParser: true }, (error, client) => { + if (error) { + reject(error); + } else { + resolve(client); + } + }); + }); +}; + +exports.appendNewRecord = async (data) => { + const client = await connect(); + const db = client.db(config.getMongoDB()); + const collection = db.collection("bookmarks"); + + return new Promise((resolve, reject) => { + collection.insertOne( + { + insertTime: Date.now(), + bookmarks: data, + }, + function (err, result) { + assert.equal(err, null); + if (err) { + reject(err); + } else { + resolve(result); + } + } + ); + }); +}; + +exports.getLatestRecord = async () => { + const client = await connect(); + const db = client.db(config.getMongoDB()); + const collection = db.collection("bookmarks"); + return new Promise((resolve, reject) => { + resolve([]); + collection.aggregate({ + $group: { _id: '$insertTime' } + }, {}, function(error, cursor) { + console.log('get latest') + assert.equal(err, null); + if (error) { + reject(error) + } else { + cursor.toArray(function(err, documents) { + console.log(documents) + callback(documents); + }); + } + }); + }); +}; diff --git a/server/docker-compose.yml b/server/docker-compose.yml new file mode 100644 index 0000000..fc2dcba --- /dev/null +++ b/server/docker-compose.yml @@ -0,0 +1,51 @@ +version: '3' +services: + mongo: + image: mongo:4.2.5 + container_name: backend-db + restart: always + networks: + - backend + environment: + MONGO_INITDB_ROOT_USERNAME: root + MONGO_INITDB_ROOT_PASSWORD: P9DeuFJ32d4img== + MONGO_INITDB_DATABASE: bookmarks + volumes: + - /mnt/data/:/data/db + - "/etc/timezone:/etc/timezone:ro" + - "/etc/localtime:/etc/localtime:ro" + ports: + - "27017:27017" + mongo-express: + image: mongo-express + container_name: backend-db-web + restart: always + ports: + - 8081:8081 + networks: + - backend + environment: + ME_CONFIG_MONGODB_ADMINUSERNAME: root + ME_CONFIG_MONGODB_ADMINPASSWORD: P9DeuFJ32d4img== + volumes: + - "/etc/timezone:/etc/timezone:ro" + - "/etc/localtime:/etc/localtime:ro" + depends_on: + - mongo + api: + build: . + image: backend-api + container_name: backend-api + restart: always + ports: + - 3000:3000 + networks: + - backend + volumes: + - "/etc/timezone:/etc/timezone:ro" + - "/etc/localtime:/etc/localtime:ro" + depends_on: + - mongo + +networks: + backend: diff --git a/server/index.js b/server/index.js deleted file mode 100644 index acf2862..0000000 --- a/server/index.js +++ /dev/null @@ -1,44 +0,0 @@ -const express = require('express'); -const cors = require('cors'); -const app = express(); -const port = 3000; - -const SUCCESS_CODE = 0 - - -const db = require('./db') -db.connect() - -app.use(cors()) -app.use(express.json()) // for parsing application/json -app.use(express.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded - -app.get('/', (req, res) => { - res.json({ - code: SUCCESS_CODE, - data: { - version: '0.1' - } - }) -}) - -app.get('/bookmarks', function (req, res) { - console.log('donwload bookmarks') - db.get((error, bookmarkArray) => { - if (error) throw error - res.json({ - code: SUCCESS_CODE, - data: bookmarkArray - }) - }) -}) - -app.post('/bookmarks', function (req, res) { - db.save(req.body, function(err) { - res.json({ - code: SUCCESS_CODE - }) - }) -}) - -app.listen(port, () => { console.log("server is running at port " + port) }) diff --git a/server/package.json b/server/package.json index bd3cb90..f093803 100644 --- a/server/package.json +++ b/server/package.json @@ -1,16 +1,18 @@ { "name": "bookmarks-server", "version": "1.0.0", - "main": "index.js", - "devDependencies": {}, + "main": "bootstrap.js.js", "scripts": { + "start": "node bootstrap.js", + "dev": "supervisor bootstrap.js", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "description": "", + "devDependencies": {}, "dependencies": { - "cors": "^2.8.5", + "cors": "2.8.5", "express": "4.17.1", "mongodb": "3.5.4" } diff --git a/server/router/v1/index.js b/server/router/v1/index.js new file mode 100644 index 0000000..230ed09 --- /dev/null +++ b/server/router/v1/index.js @@ -0,0 +1,43 @@ +const express = require("express"); +const router = express.Router(); + +const db = require("../../db"); + +const API_VERSION = "1.0.0"; +const SUCCESS_CODE = 20000; + +// simple logger for this router's requests +// all requests to this router will first hit this middleware +router.use(function (req, res, next) { + console.log("%s %s %s", req.method, req.url, req.path); + next(); +}); + +router.get("/version", (req, res) => { + res.json({ + code: SUCCESS_CODE, + message: "OK", + data: API_VERSION, + }); +}); + +router.get("/bookmark", async (req, res) => { + const data = await db.getLatestRecord(); + console.log('get all bookmarks') + console.log(data) + res.json({ + code: SUCCESS_CODE, + message: "OK", + data, + }); +}); + +router.post("/bookmark", function (req, res) { + db.appendNewRecord(req.body.bookmarks); + res.json({ + code: SUCCESS_CODE, + message: "OK", + }); +}); + +module.exports = router; diff --git a/server/yarn.lock b/server/yarn.lock index 047dc31..8a6dd6e 100644 --- a/server/yarn.lock +++ b/server/yarn.lock @@ -39,6 +39,22 @@ body-parser@1.19.0: raw-body "2.4.0" type-is "~1.6.17" +body-parser@^1.19.0: + version "1.19.0" + resolved "https://registry.npm.taobao.org/body-parser/download/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha1-lrJwnlfJxOCab9Zqj9l5hE9p8Io= + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + bson@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.3.tgz#aa82cb91f9a453aaa060d6209d0675114a8154d3" @@ -76,7 +92,7 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cors@^2.8.5: +cors@2.8.5: version "2.8.5" resolved "https://registry.npm.taobao.org/cors/download/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" integrity sha1-6sEdpRWS3Ya58G9uesKTs9+HXSk=