Browse Source

feat: 整合mongo数据库

master
Alvis Zhao 5 years ago
parent
commit
2f04adf2ab
  1. 0
      docs/api.yml
  2. 77
      docs/swagger.yml
  3. 237
      extension/js/popup.js
  4. 8
      extension/popup.html
  5. 1
      server/Dockerfile
  6. 21
      server/bootstrap.js
  7. 39
      server/config/index.js
  8. 79
      server/db.js
  9. 66
      server/db/index.js
  10. 51
      server/docker-compose.yml
  11. 44
      server/index.js
  12. 8
      server/package.json
  13. 43
      server/router/v1/index.js
  14. 18
      server/yarn.lock

0
docs/api.yml

77
docs/swagger.yml

@ -1,35 +1,22 @@
openapi: "3.0.0" openapi: "3.0.0"
servers:
- url: http://192.168.31.161:3000/v1
description: 测试服务器
info: info:
title: 书签同步助手后端接口 title: 书签同步助手后端接口
version: 0.0.1 version: 1.0.0
tags: tags:
- name: User - name: Version
description: 用户管理 description: 接口版本号
- name: Bookmark - name: Bookmark
description: 书签管理 description: 书签管理
paths: paths:
/user/login: /version:
post: get:
operationId: userLogin operationId: getVersion
summary: 用户登录 summary: 获得接口版本号
tags: tags:
- User - Version
requestBody:
description: 登录信息。
required: true
content:
application/json:
schema:
type: object
properties:
account:
description: 账号
type: string
example: 'admin'
password:
description: 密码
type: string
example: 'secret'
responses: responses:
'200': '200':
description: 200 response description: 200 response
@ -43,36 +30,15 @@ paths:
message: message:
type: string type: string
description: 响应信息,响应码为非20000时表示错误信息 description: 响应信息,响应码为非20000时表示错误信息
examples: data:
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:
type: string type: string
description: 响应信息,响应码为非20000时表示错误信息 description: 版本号信息
examples: examples:
20000: 20000:
value: { value: {
"code": 20000, "code": 20000,
"message": "OK" "message": "OK",
"data": "1.0.0"
} }
/bookmark: /bookmark:
get: get:
@ -110,7 +76,7 @@ paths:
tags: tags:
- Bookmark - Bookmark
requestBody: requestBody:
description: 需要添加的职工信息,注意 employeeId 字段是无效的。 description: 书签列表
required: true required: true
content: content:
application/json: application/json:
@ -137,19 +103,16 @@ paths:
"code": 20000, "code": 20000,
"message": "OK" "message": "OK"
} }
components: components:
schemas: schemas:
ResponseCode: ResponseCode:
type: integer type: integer
enum: enum:
- 20000 - 20000
- 40000
- 40100
description: > description: >
响应码: 响应码:
* `20000` - 表明调用成功 * `20000` - 表明调用成功
* `40000` - 用户名或密码错误
* `40001` - token已过期
Bookmark: Bookmark:
type: object type: object
properties: properties:
@ -159,12 +122,6 @@ components:
parentId: parentId:
description: 父节点的ID,根节点没有此属性 description: 父节点的ID,根节点没有此属性
type: string type: string
dateAdded:
description: 书签节点创建时的时间戳
type: integer
dateGroupModified:
description: 书签文件夹内容的最后更新时间戳,书签节点没有此属性
type: integer
index: index:
description: 书签在父节点中的索引,根节点没有此属性 description: 书签在父节点中的索引,根节点没有此属性
type: integer type: integer

237
extension/popup.js → extension/js/popup.js

@ -1,11 +1,12 @@
let api = {}; let api = {};
let serverApi = {};
// chrome bookmark api wrapper // chrome bookmark api wrapper
(function(api) { (function (api) {
/** /**
* 获得整个书签树 * 获得整个书签树
*/ */
const getTree = () => { const getTree = () => {
return new Promise(resolve => { return new Promise((resolve) => {
chrome.bookmarks.getTree(resolve); chrome.bookmarks.getTree(resolve);
}); });
}; };
@ -18,12 +19,12 @@ let api = {};
* 返回的书签数组中是不包含children字段的即不包含子节点以下的节点 * 返回的书签数组中是不包含children字段的即不包含子节点以下的节点
* @param {String} id 父书签组id * @param {String} id 父书签组id
*/ */
const getChildren = id => { const getChildren = (id) => {
return new Promise(resolve => { return new Promise((resolve) => {
chrome.bookmarks.getChildren(id, resolve); chrome.bookmarks.getChildren(id, resolve);
}); });
}; };
const getChildrenAsync = async id => { const getChildrenAsync = async (id) => {
return await getChildren(id); return await getChildren(id);
}; };
@ -32,12 +33,12 @@ let api = {};
* 返回的书签数组中包含children字段即包含子节点以下的节点 * 返回的书签数组中包含children字段即包含子节点以下的节点
* @param {String} id 父书签组id * @param {String} id 父书签组id
*/ */
const getSubTree = id => { const getSubTree = (id) => {
return new Promise(resolve => { return new Promise((resolve) => {
chrome.bookmarks.getSubTree(id, resolve); chrome.bookmarks.getSubTree(id, resolve);
}); });
}; };
const getSubTreeAsync = async id => { const getSubTreeAsync = async (id) => {
return await getSubTree(id); return await getSubTree(id);
}; };
@ -45,12 +46,12 @@ let api = {};
* 删除指定id的书签 * 删除指定id的书签
* @param {String} id 需要删除的书签的id * @param {String} id 需要删除的书签的id
*/ */
const remove = id => { const remove = (id) => {
return new Promise(resolve => { return new Promise((resolve) => {
chrome.bookmarks.remove(id, resolve); chrome.bookmarks.remove(id, resolve);
}); });
}; };
const removeAsync = async id => { const removeAsync = async (id) => {
return await remove(id); return await remove(id);
}; };
@ -58,12 +59,12 @@ let api = {};
* 删除指定id的空书签组如果书签组下有子书签或子书签组删除将失败 * 删除指定id的空书签组如果书签组下有子书签或子书签组删除将失败
* @param {String} id 需要删除的书签文件夹id * @param {String} id 需要删除的书签文件夹id
*/ */
const removeTree = id => { const removeTree = (id) => {
return new Promise(resolve => { return new Promise((resolve) => {
chrome.bookmarks.removeTree(id, resolve); chrome.bookmarks.removeTree(id, resolve);
}); });
}; };
const removeTreeAsync = async id => { const removeTreeAsync = async (id) => {
await removeTree(id); await removeTree(id);
}; };
@ -75,12 +76,12 @@ let api = {};
* string (optional) title * string (optional) title
* string (optional) url 如果为NULL或者不填则代表一个书签组文件夹 * string (optional) url 如果为NULL或者不填则代表一个书签组文件夹
*/ */
const create = bookmark => { const create = (bookmark) => {
return new Promise(resolve => { return new Promise((resolve) => {
chrome.bookmarks.create(bookmark, resolve); chrome.bookmarks.create(bookmark, resolve);
}); });
}; };
const createAsync = async bookmark => { const createAsync = async (bookmark) => {
return await create(bookmark); return await create(bookmark);
}; };
@ -92,6 +93,65 @@ let api = {};
api.createAsync = createAsync; api.createAsync = createAsync;
})(api); })(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() { function Node() {
/** /**
* id ( string ) * 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 BOOKMARK = 0;
const BOOKEMARK_FOLDER = 1; const BOOKEMARK_FOLDER = 1;
const PROTO = "http"; const PROTO = "http";
const SERVER_ADDRESS = "127.0.0.1"; const SERVER_ADDRESS = "192.168.31.161";
const SERVER_PORT = "3000"; const SERVER_PORT = "3000";
const SERVER_URL = `${PROTO}://${SERVER_ADDRESS}:${SERVER_PORT}`; const SERVER_URL = `${PROTO}://${SERVER_ADDRESS}:${SERVER_PORT}`;
@ -162,8 +222,6 @@ async function getBookmarkMap() {
let bookmarkNode = new Node(); let bookmarkNode = new Node();
bookmarkNode.id = node.id; bookmarkNode.id = node.id;
bookmarkNode.parentId = node.parentId; bookmarkNode.parentId = node.parentId;
// bookmarkNode.dateAdded = node.dateAdded;
// bookmarkNode.dateGroupModified = node.dateGroupModified;
bookmarkNode.index = node.index; bookmarkNode.index = node.index;
bookmarkNode.title = node.title; bookmarkNode.title = node.title;
bookmarkNode.url = node.url; bookmarkNode.url = node.url;
@ -200,45 +258,54 @@ async function getBookmarkMap() {
return localMap; return localMap;
} }
/** function tree2List(tree) {
* 获得浏览器书签的数组 let cacheMap = {};
*/ function add2Map(tree) {
async function getBookmarkList() { for (let item of tree) {
let localTree = await api.getTreeAsync(); // 给书签文件创建group属性
let localList = []; if (item.id === "0" || item.id === "1" || item.id === "2" || typeof item.dateGroupModified === 'number') {
item.group = true
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;
} }
cacheMap[item.id] = item;
if (Array.isArray(item.children)) {
add2Map(item.children);
}
}
}
add2Map(tree);
localList.push(bookmarkNode); for (let key in cacheMap) {
let item = cacheMap[key];
if (Array.isArray(node.children) && node.children.length > 0) { if (typeof item.parentId === "string") {
await addToList(node.children) if (!Array.isArray(cacheMap[item.parentId].nodes)) {
cacheMap[item.parentId].nodes = [];
}
cacheMap[item.parentId].nodes.push(item.id);
} }
} }
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,
});
} }
await addToList(localTree); return list;
return localList; }
/**
* 获得浏览器书签的数组
*/
async function getBookmarkList() {
let localTree = await api.getTreeAsync();
return tree2List(localTree);
} }
/** /**
@ -246,30 +313,29 @@ async function getBookmarkList() {
* @param {Object} bookmarkList * @param {Object} bookmarkList
*/ */
function listToMap(bookmarkList) { function listToMap(bookmarkList) {
let map = {} let map = {};
for(let i = 0; i < bookmarkList.length; i++) { for (let i = 0; i < bookmarkList.length; i++) {
let bookmark = bookmarkList[i] let bookmark = bookmarkList[i];
map[bookmark.id] = bookmark map[bookmark.id] = bookmark;
} }
function getDepth(bookmark, depth) { function getDepth(bookmark, depth) {
if (parseInt(bookmark.id) === 0) { if (parseInt(bookmark.id) === 0) {
return depth return depth;
} else { } else {
return getDepth(map[bookmark.parentId], depth + 1) return getDepth(map[bookmark.parentId], depth + 1);
} }
} }
let finalMap = {} let finalMap = {};
for(let i = 0; i < bookmarkList.length; i++) { for (let i = 0; i < bookmarkList.length; i++) {
let bookmark = bookmarkList[i] let bookmark = bookmarkList[i];
let finalDepth = getDepth(bookmark, 0) let finalDepth = getDepth(bookmark, 0);
if (!Array.isArray(finalMap[finalDepth])) { if (!Array.isArray(finalMap[finalDepth])) {
finalMap[finalDepth] = [] finalMap[finalDepth] = [];
} }
finalMap[finalDepth].push(bookmark) finalMap[finalDepth].push(bookmark);
} }
return finalMap return finalMap;
} }
async function removeAllBookmarks() { async function removeAllBookmarks() {
@ -304,7 +370,7 @@ async function restore(remoteBookmarkArray) {
parentId: getNewbookmarkId(array[depth - 1], bookmark.parentId), parentId: getNewbookmarkId(array[depth - 1], bookmark.parentId),
index: bookmark.index, index: bookmark.index,
title: bookmark.title, title: bookmark.title,
url: bookmark.url url: bookmark.url,
}); });
bookmark.newId = newBookmark.id; bookmark.newId = newBookmark.id;
console.log( console.log(
@ -319,30 +385,25 @@ async function restore(remoteBookmarkArray) {
$("#upload").on("click", async () => { $("#upload").on("click", async () => {
let bookmarkArray = await getBookmarkList(); let bookmarkArray = await getBookmarkList();
$.ajax({ console.log(bookmarkArray)
type: "POST", serverApi.uploadBookmarks({
url: `${SERVER_URL}/bookmarks`, bookmarks: bookmarkArray
contentType: "application/json;charset=utf-8",
dataType: "json",
data: JSON.stringify(bookmarkArray),
success: function() {},
error: function() {}
}); });
}); });
$("#download").on("click", function() { $("#download").on("click", async () => {
$.ajax({ const bookmarkArray = await serverApi.downloadBookmarks();
type: "GET", console.log(bookmarkArray);
url: `${SERVER_URL}/bookmarks`,
success: function(result) {
if (result.code === 0) {
restore(result.data);
}
}
});
}); });
$("#test").on("click", async () => { $("#test").on("click", async () => {
let array = await getBookmarkList(); let array = await getBookmarkList();
console.log(array); console.log(array);
}); });
$("#connect").on("click", async () => {
const serverAddress = $("#server").val() || SERVER_URL;
serverApi.setServerAddress(serverAddress);
const version = await serverApi.getVersion();
$("#version").html(version);
});

8
extension/popup.html

@ -7,9 +7,15 @@
<title>Document</title> <title>Document</title>
</head> </head>
<body style="width: 400px"> <body style="width: 400px">
<div>
<span>服务器地址</span>
<input id='server' type="text" value='' placeholder="http://192.168.31.161:3000">
<button id='connect' type="button" class="btn btn-default">连接服务器</button>
<span id='version'></span>
</div>
<button id='download' type="button" class="btn btn-default">恢复</button> <button id='download' type="button" class="btn btn-default">恢复</button>
<button id='upload' type="button" class="btn btn-default">备份</button> <button id='upload' type="button" class="btn btn-default">备份</button>
<button id='test' type="button" class="btn btn-default">测试</button> <button id='test' type="button" class="btn btn-default">测试</button>
<script src='./popup.js'></script> <script src='./js/popup.js'></script>
</body> </body>
</html> </html>

1
server/Dockerfile

@ -0,0 +1 @@
FROM node:12.16.1

21
server/bootstrap.js vendored

@ -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);
});

39
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;

79
server/db.js

@ -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));
}
);
});
};

66
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);
});
}
});
});
};

51
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:

44
server/index.js

@ -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) })

8
server/package.json

@ -1,16 +1,18 @@
{ {
"name": "bookmarks-server", "name": "bookmarks-server",
"version": "1.0.0", "version": "1.0.0",
"main": "index.js", "main": "bootstrap.js.js",
"devDependencies": {},
"scripts": { "scripts": {
"start": "node bootstrap.js",
"dev": "supervisor bootstrap.js",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"description": "", "description": "",
"devDependencies": {},
"dependencies": { "dependencies": {
"cors": "^2.8.5", "cors": "2.8.5",
"express": "4.17.1", "express": "4.17.1",
"mongodb": "3.5.4" "mongodb": "3.5.4"
} }

43
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;

18
server/yarn.lock

@ -39,6 +39,22 @@ body-parser@1.19.0:
raw-body "2.4.0" raw-body "2.4.0"
type-is "~1.6.17" 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: bson@^1.1.1:
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.3.tgz#aa82cb91f9a453aaa060d6209d0675114a8154d3" 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" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
cors@^2.8.5: cors@2.8.5:
version "2.8.5" version "2.8.5"
resolved "https://registry.npm.taobao.org/cors/download/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" resolved "https://registry.npm.taobao.org/cors/download/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
integrity sha1-6sEdpRWS3Ya58G9uesKTs9+HXSk= integrity sha1-6sEdpRWS3Ya58G9uesKTs9+HXSk=

Loading…
Cancel
Save