Browse Source

feat: 完成书签从服务器恢复功能

master
Alvis Zhao 5 years ago
parent
commit
de67660b0e
  1. 5
      extension/popup.html
  2. 303
      extension/popup.js
  3. 59
      server/db.js
  4. 9
      server/index.js

5
extension/popup.html

@ -7,8 +7,9 @@ @@ -7,8 +7,9 @@
<title>Document</title>
</head>
<body style="width: 400px">
<button id='download' type="button" class="btn btn-default">从服务器同步书签</button>
<button id='upload' type="button" class="btn btn-default" id='upload'>同步书签至服务器</button>
<button id='download' 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>
<script src='./popup.js'></script>
</body>
</html>

303
extension/popup.js

@ -1,3 +1,97 @@ @@ -1,3 +1,97 @@
let api = {};
// chrome bookmark api wrapper
(function(api) {
/**
* 获得整个书签树
*/
const getTree = () => {
return new Promise(resolve => {
chrome.bookmarks.getTree(resolve);
});
};
const getTreeAsync = async () => {
return await getTree();
};
/**
* 获得特定父书签组下的所有子书签和子书签组
* 返回的书签数组中是不包含children字段的即不包含子节点以下的节点
* @param {String} id 父书签组id
*/
const getChildren = id => {
return new Promise(resolve => {
chrome.bookmarks.getChildren(id, resolve);
});
};
const getChildrenAsync = async id => {
return await getChildren(id);
};
/**
* 获得特定书签组下的所有书签
* 返回的书签数组中包含children字段即包含子节点以下的节点
* @param {String} id 父书签组id
*/
const getSubTree = id => {
return new Promise(resolve => {
chrome.bookmarks.getSubTree(id, resolve);
});
};
const getSubTreeAsync = async id => {
return await getSubTree(id);
};
/**
* 删除指定id的书签
* @param {String} id 需要删除的书签的id
*/
const remove = id => {
return new Promise(resolve => {
chrome.bookmarks.remove(id, resolve);
});
};
const removeAsync = async id => {
return await remove(id);
};
/**
* 删除指定id的空书签组如果书签组下有子书签或子书签组删除将失败
* @param {String} id 需要删除的书签文件夹id
*/
const removeTree = id => {
return new Promise(resolve => {
chrome.bookmarks.removeTree(id, resolve);
});
};
const removeTreeAsync = async id => {
await removeTree(id);
};
/**
* 创建一个书签
* @param {Object} bookmark
* string (optional) parentId 父书签组如果不填则默认在**其他书签**一栏中
* integer (optional) index
* string (optional) title
* string (optional) url 如果为NULL或者不填则代表一个书签组文件夹
*/
const create = bookmark => {
return new Promise(resolve => {
chrome.bookmarks.create(bookmark, resolve);
});
};
const createAsync = async bookmark => {
return await create(bookmark);
};
api.getTreeAsync = getTreeAsync;
api.getSubTreeAsync = getSubTreeAsync;
api.getChildrenAsync = getChildrenAsync;
api.removeAsync = removeAsync;
api.removeTreeAsync = removeTreeAsync;
api.createAsync = createAsync;
})(api);
function Node() {
/**
* id ( string )
@ -38,63 +132,201 @@ function Node() { @@ -38,63 +132,201 @@ function Node() {
* 是否为根节点
*/
this.root = false;
/**
* 子节点数组
*/
this.children = undefined;
}
let BookmarkTreeNodeList = [];
const BOOKMARK = 0;
const BOOKEMAR_FOLDER = 1;
const PROTO = 'http'
const SERVER_ADDRESS = '127.0.0.1'
const SERVER_PORT = '3000'
const SERVER_URL = `${PROTO}://${SERVER_ADDRESS}:${SERVER_PORT}`
const BOOKEMARK_FOLDER = 1;
const PROTO = "http";
const SERVER_ADDRESS = "127.0.0.1";
const SERVER_PORT = "3000";
const SERVER_URL = `${PROTO}://${SERVER_ADDRESS}:${SERVER_PORT}`;
/**
* 获得浏览器书签的map表
* 表的key值表示书签深度根目录深度为0
*/
async function getBookmarkMap() {
let localTree = await api.getTreeAsync();
let localMap = {};
async function addToMap(localTree, depth) {
for (let i = 0; i < localTree.length; i++) {
let node = localTree[i];
function addToList(node) {
let bookmarkNode = new Node();
bookmarkNode.id = node.id;
bookmarkNode.parentId = node.parentId;
bookmarkNode.dateAdded = node.dateAdded;
bookmarkNode.dateGroupModified = node.dateGroupModified;
// bookmarkNode.dateAdded = node.dateAdded;
// bookmarkNode.dateGroupModified = node.dateGroupModified;
bookmarkNode.index = node.index;
bookmarkNode.title = node.title;
bookmarkNode.url = node.url;
bookmarkNode.type =
typeof node.dateGroupNodified === "undefined" ? BOOKMARK : BOOKEMAR_FOLDER;
typeof node.dateGroupModified === "undefined"
? BOOKMARK
: BOOKEMARK_FOLDER;
bookmarkNode.root = typeof node.parentId === "undefined";
BookmarkTreeNodeList.push(bookmarkNode);
// 根节点没有 dateGroupModified 属性,但是应该是个书签组类型
if (bookmarkNode.root) {
bookmarkNode.type = BOOKEMARK_FOLDER;
}
if (!Array.isArray(localMap[depth])) {
localMap[depth] = [];
}
localMap[depth].push(bookmarkNode);
if (Array.isArray(node.children)) {
if (
bookmarkNode.type === BOOKEMARK_FOLDER &&
Array.isArray(node.children) &&
node.children.length > 0
) {
for (let j = 0; j < node.children.length; j++) {
addToList(node.children[j]);
let childNode = node.children[j];
let childTree = await api.getSubTreeAsync(childNode.id);
await addToMap(childTree, depth + 1);
}
}
}
}
await addToMap(localTree, 0);
return localMap;
}
function getBookmarkList(callback) {
BookmarkTreeNodeList = []
chrome.bookmarks.getTree(function(tree) {
for (let i = 0; i < tree.length; i++) {
let node = tree[i];
addToList(node);
/**
* 获得浏览器书签的数组
*/
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;
}
localList.push(bookmarkNode);
if (Array.isArray(node.children) && node.children.length > 0) {
await addToList(node.children)
}
}
}
await addToList(localTree);
return localList;
}
/**
* 数组转map表
* @param {Object} bookmarkList
*/
function listToMap(bookmarkList) {
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)
}
callback(null, BookmarkTreeNodeList);
}
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
}
async function removeAllBookmarks() {
let localTree = await api.getTreeAsync();
}
async function restore(remoteBookmarkArray) {
function getNewbookmarkId(array, parentId) {
for (let i = 0; i < array.length; i++) {
if (array[i].id === parentId) {
return array[i].newId;
}
}
}
let array = listToMap(remoteBookmarkArray);
for (let depth in array) {
const bookmarks = array[depth];
for (let i = 0; i < bookmarks.length; i++) {
const bookmark = bookmarks[i];
if (
bookmark.root ||
bookmark.id === "0" ||
bookmark.id === "1" ||
bookmark.id === "2"
) {
bookmark.newId = bookmark.id;
continue;
}
try {
const newBookmark = await api.createAsync({
parentId: getNewbookmarkId(array[depth - 1], bookmark.parentId),
index: bookmark.index,
title: bookmark.title,
url: bookmark.url
});
bookmark.newId = newBookmark.id;
console.log(
`restroe ${bookmark.title}:${bookmark.id} to ${newBookmark.title}:${newBookmark.id}`
);
} catch (error) {
console.error(error);
}
}
}
}
$("#upload").on("click", function() {
getBookmarkList(function(error, bookmarkArray) {
$("#upload").on("click", async () => {
let bookmarkArray = await getBookmarkList();
$.ajax({
type: "POST",
url: `${SERVER_URL}/bookmarks`,
contentType: 'application/json;charset=utf-8',
dataType: 'json',
contentType: "application/json;charset=utf-8",
dataType: "json",
data: JSON.stringify(bookmarkArray),
success: function() {
console.log('上传了 ' + bookmarkArray.length + ' 条书签')
},
error: function() {
console.error('上传失败')
}
});
success: function() {},
error: function() {}
});
});
@ -102,8 +334,15 @@ $("#download").on("click", function() { @@ -102,8 +334,15 @@ $("#download").on("click", function() {
$.ajax({
type: "GET",
url: `${SERVER_URL}/bookmarks`,
success: function(bookmarkArray) {
console.log('下载了 ' + bookmarkArray.length + ' 条书签')
success: function(result) {
if (result.code === 0) {
restore(result.data);
}
}
});
});
$("#test").on("click", async () => {
let array = await getBookmarkList();
console.log(array);
});

59
server/db.js

@ -1,7 +1,8 @@ @@ -1,7 +1,8 @@
// const MongoClient = require('mongodb').MongoClient
const assert = require('assert')
const fs = require('fs')
const path = require('path')
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'
@ -37,32 +38,42 @@ exports.connect = () => { @@ -37,32 +38,42 @@ exports.connect = () => {
// client.close()
// })
// })
}
};
exports.save = (bookmarkArray, callback) => {
console.log('save data')
const bookmarkStr = JSON.stringify(bookmarkArray)
fs.writeFile(path.join('database', Date.now() + '.json'), bookmarkStr, err => {
callback(err)
});
}
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 => {
console.log('get latest bookmarks')
fs.readdir(path.join('database'), (err, files ) => {
if (err) throw err
console.log(files)
let latestFile = 0
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)
console.log(time)
let filename = files[i];
const time = filename.substr(0, filename.length - 5);
if (time > latestFile) {
latestFile = time
latestFile = time;
}
}
fs.readFile(path.join('database', latestFile + '.json'), (error, data) => {
callback(error, JSON.parse(data.toString()))
})
})
}
fs.readFile(
path.join("database", latestFile + ".json"),
"utf8",
(error, data) => {
callback(error, JSON.parse(data));
}
);
});
};

9
server/index.js

@ -1,8 +1,6 @@ @@ -1,8 +1,6 @@
const express = require('express');
const cors = require('cors');
const app = express();
const path = require('path');
const fs = require('fs');
const port = 3000;
const SUCCESS_CODE = 0
@ -25,6 +23,7 @@ app.get('/', (req, res) => { @@ -25,6 +23,7 @@ app.get('/', (req, res) => {
})
app.get('/bookmarks', function (req, res) {
console.log('donwload bookmarks')
db.get((error, bookmarkArray) => {
if (error) throw error
res.json({
@ -35,13 +34,11 @@ app.get('/bookmarks', function (req, res) { @@ -35,13 +34,11 @@ app.get('/bookmarks', function (req, res) {
})
app.post('/bookmarks', function (req, res) {
const bookmarkArray = req.body
db.save(bookmarkArray, err => {
if (err) throw err
})
db.save(req.body, function(err) {
res.json({
code: SUCCESS_CODE
})
})
})
app.listen(port, () => { console.log("server is running at port " + port) })

Loading…
Cancel
Save