From 845fc737981e091991c619a4f79140a580d1bc37 Mon Sep 17 00:00:00 2001 From: luchenqun Date: Sun, 25 Jun 2017 15:24:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=B9=A6=E7=AD=BE=E7=9A=84?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 ++- database/db.js | 34 ++++++--- package.json | 1 + public/index.html | 2 +- .../controllers/settings-controller.js | 11 ++- public/views/home.html | 6 +- public/views/settings.html | 9 ++- routes/api.js | 73 ++++++++++++++++--- 8 files changed, 108 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 26611ed..86f0e5d 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,11 @@ - [x] 可以导入Chrome的书签导出文件,暂时做在设置里面。 - [x] 书签可以作为公有或者私有,公有可供所有人搜索。 - [x] 可以将搜索到其他用户的书签转存为自己的书签。 -- [ ] 可以将书签导出来,然后导入到浏览器。 +- [x] 可以将书签导出来,然后导入到浏览器。 - [x] 在热门标签里面,有在网上找的热门书签。可以转存收藏到自己书签里面,快捷键R随机查看热门书签。 - [x] 新增备忘录功能,有时候随手要做点纪录,就方便了。任意界面按快捷键A增加备忘录。双击备忘录可查看详情! - [x] 在设置的全局链接,可设置快捷键,用来在任何页面,快速打开设置的链接。 -- [ ] 适配手机平板。 +- [ ] 适配手机平板(无限搁置)。 4 主要用到的模块说明 ------------------ @@ -142,18 +142,19 @@ my-bookmark/ ``` "body-parser": bodyParser用于解析客户端请求的body中的内容,内部使用JSON编码处理 "connect-mongo": 用于将session存入MongoDB -"cheerio": 用于后端的jQuery +"cheerio": 用于后端的jQuery,解析从浏览器导出来上传到服务器的书签html文件 "cookie-parser": 处理每一个请求的cookie "crypto": 加密模块,主要用来加密用户的密码 "debug": 这个好像没用到,看名字好像调试的。 "download": 主要用来下载书签的favicon文件 "express": Web 应用程序框架 "express-session": session模块 -"jsdom": 用来解析从浏览器导出来上传到服务器的书签html文件 +"js-beautify": 用来格式化导出的书签的html文件 "morgan": 一个Node.js关于http请求的日志中间件 "multer": 文件上传模块 "mysql": sql数据库操作模块 "node-readability": 获取网页title(添加书签用到)跟内容(书签详情用到)模块。 +"path": 路径处理模块。 "request": http请求模块。主要用来获取热门书签数据。 "supervisor": 文件改变监视文件,开发使用。 "webshot": 网页截图模块。 diff --git a/database/db.js b/database/db.js index 77844f2..0d6c92b 100644 --- a/database/db.js +++ b/database/db.js @@ -546,13 +546,13 @@ db.getBookmarksNavigate = function(tags) { // var sql = "SELECT t.id as tag_id, t.name as tag_name, b.* FROM `tags` as t LEFT OUTER JOIN tags_bookmarks as tb ON t.id = tb.tag_id LEFT OUTER JOIN bookmarks as b ON tb.bookmark_id = b.id WHERE t.user_id='" + user_id + "' ORDER BY t.id ASC, b.click_count DESC"; var sql = ""; tags.forEach((tag, index) => { - var t = 't' + tag.id; - if (index >= 1) { - sql += " UNION " - } - sql += "(SELECT * FROM ((SELECT t.id AS tag_id, t.`name` as tag_name, t.sort, b.* FROM `tags` as t, `bookmarks`as b, `tags_bookmarks` as tb WHERE t.id = tb.tag_id AND b.id = tb.bookmark_id AND t.id = " + tag.id + " ORDER BY b.click_count DESC LIMIT 0, 16) UNION (SELECT t.id AS tag_id, t.`name` as tag_name, t.sort, b.* FROM `tags` as t, `bookmarks`as b, `tags_bookmarks` as tb WHERE t.id = tb.tag_id AND b.id = tb.bookmark_id AND t.id = " + tag.id + " ORDER BY b.created_at DESC LIMIT 0, 16)) as " + t + " ORDER BY " + t + ".click_count DESC, " + t + ".created_at DESC)"; - }) - // console.log('getBookmarksNavigate ', sql); + var t = 't' + tag.id; + if (index >= 1) { + sql += " UNION " + } + sql += "(SELECT * FROM ((SELECT t.id AS tag_id, t.`name` as tag_name, t.sort, b.* FROM `tags` as t, `bookmarks`as b, `tags_bookmarks` as tb WHERE t.id = tb.tag_id AND b.id = tb.bookmark_id AND t.id = " + tag.id + " ORDER BY b.click_count DESC LIMIT 0, 16) UNION (SELECT t.id AS tag_id, t.`name` as tag_name, t.sort, b.* FROM `tags` as t, `bookmarks`as b, `tags_bookmarks` as tb WHERE t.id = tb.tag_id AND b.id = tb.bookmark_id AND t.id = " + tag.id + " ORDER BY b.created_at DESC LIMIT 0, 16)) as " + t + " ORDER BY " + t + ".click_count DESC, " + t + ".created_at DESC)"; + }) + // console.log('getBookmarksNavigate ', sql); return new Promise(function(resolve, reject) { client.query(sql, (err, result) => { @@ -568,9 +568,9 @@ db.getBookmarksNavigate = function(tags) { db.getBookmarksCostomTag = function(user_id, perPageItems) { console.log('getBookmarksCostomTag', user_id, perPageItems); perPageItems = perPageItems || 50; - var sql1 = "(SELECT id, user_id, title, description, url, public, click_count, DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') as created_at, DATE_FORMAT(last_click, '%Y-%m-%d %H:%i:%s') as last_click FROM `bookmarks` WHERE `user_id` = '" + user_id + "' ORDER BY `click_count` DESC LIMIT 0, "+ perPageItems +")"; - var sql2 = "(SELECT id, user_id, title, description, url, public, click_count, DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') as created_at, DATE_FORMAT(last_click, '%Y-%m-%d %H:%i:%s') as last_click FROM `bookmarks` WHERE `user_id` = '" + user_id + "' ORDER BY `created_at` DESC LIMIT 0, "+ perPageItems +")"; - var sql3 = "(SELECT id, user_id, title, description, url, public, click_count, DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') as created_at, DATE_FORMAT(last_click, '%Y-%m-%d %H:%i:%s') as last_click FROM `bookmarks` WHERE `user_id` = '" + user_id + "' ORDER BY `last_click` DESC LIMIT 0, "+ perPageItems +")"; + var sql1 = "(SELECT id, user_id, title, description, url, public, click_count, DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') as created_at, DATE_FORMAT(last_click, '%Y-%m-%d %H:%i:%s') as last_click FROM `bookmarks` WHERE `user_id` = '" + user_id + "' ORDER BY `click_count` DESC LIMIT 0, " + perPageItems + ")"; + var sql2 = "(SELECT id, user_id, title, description, url, public, click_count, DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') as created_at, DATE_FORMAT(last_click, '%Y-%m-%d %H:%i:%s') as last_click FROM `bookmarks` WHERE `user_id` = '" + user_id + "' ORDER BY `created_at` DESC LIMIT 0, " + perPageItems + ")"; + var sql3 = "(SELECT id, user_id, title, description, url, public, click_count, DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') as created_at, DATE_FORMAT(last_click, '%Y-%m-%d %H:%i:%s') as last_click FROM `bookmarks` WHERE `user_id` = '" + user_id + "' ORDER BY `last_click` DESC LIMIT 0, " + perPageItems + ")"; var sql = sql1 + " UNION " + sql2 + " UNION " + sql3; return new Promise(function(resolve, reject) { @@ -751,6 +751,20 @@ db.getBookmarksByTag = function(params) { }) } +db.getExportBookmarksByTag = function(tag_id) { + var sql = "SELECT tags_bookmarks.tag_id, bookmarks.title, bookmarks.url, DATE_FORMAT(bookmarks.created_at, '%Y-%m-%d %H:%i:%s') as created_at, DATE_FORMAT(bookmarks.last_click, '%Y-%m-%d %H:%i:%s') as last_click FROM `tags_bookmarks`, `bookmarks` WHERE tags_bookmarks.tag_id = '" + tag_id + "' AND tags_bookmarks.bookmark_id = bookmarks.id ORDER BY bookmarks.click_count DESC"; + + return new Promise(function(resolve, reject) { + client.query(sql, (err, result) => { + if (err) { + reject(err); + } else { + resolve(result); + } + }); + }) +} + db.getBookmarksSearch = function(params) { var sql = "SELECT id, user_id, title, description, url, public, click_count, DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') as created_at, DATE_FORMAT(last_click, '%Y-%m-%d %H:%i:%s') as last_click FROM `bookmarks` WHERE 1=1"; diff --git a/package.json b/package.json index 44894ca..936ff33 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "download": "^5.0.3", "express": "~4.13.4", "express-session": "^1.14.1", + "js-beautify": "^1.6.14", "morgan": "^1.8.1", "multer": "^1.3.0", "mysql": "^2.11.1", diff --git a/public/index.html b/public/index.html index 35758c1..7b4b688 100644 --- a/public/index.html +++ b/public/index.html @@ -38,7 +38,7 @@
-     Copyleft ©All Rights Reserved LCQ(假装网站有个页脚)  |  联系我(QQ:530485521)  |  网站源码  |  V1.4.0  |  由于使用国外服务器,如果加载失败,请按F5,你懂的!(●'◡'●) + 我爱佳佳与这个世界  |  Copyleft ©All Rights Reserved LCQ  |  联系我(QQ:530485521)  |  网站源码  |  V1.5.0  |  由于使用国外服务器,如果加载失败,请按F5,你懂的!(●'◡'●)
diff --git a/public/scripts/controllers/settings-controller.js b/public/scripts/controllers/settings-controller.js index 304c48a..f15dc62 100644 --- a/public/scripts/controllers/settings-controller.js +++ b/public/scripts/controllers/settings-controller.js @@ -205,9 +205,14 @@ app.controller('settingsCtr', ['$scope', '$stateParams', '$filter', '$state', '$ } $scope.exportBookmark = function() { - toastr.warning('功能正在开发中,敬请期待......', '提示'); - return; - $window.open("api/download?fileName=lcq-20170304213023.html"); + var userId = $scope.user && $scope.user.id; + if (userId) { + // toastr.warning('功能正在开发中,敬请期待......', '提示'); + // return; + $window.open("api/download?userId=" + userId + "&type=exportbookmark"); + } else { + toastr.warning('用户信息无法获取到,请尝试按刷新网页再尝试!', '提示'); + } } function updateShowStyle(showStyle) { diff --git a/public/views/home.html b/public/views/home.html index 3bad919..ff575b3 100644 --- a/public/views/home.html +++ b/public/views/home.html @@ -11,15 +11,15 @@

7、如果公用一台电脑,如何区分我收藏的跟别人收藏的书签?

在线书签管理工具,帮助你快速记录你喜欢的网站,并可以随时随地查看这些站点,而不必拘泥于使用的浏览器。无论在什么地方,只要能接入网络,就能打开属于你自己的网络书签,看到自己收藏的页面网址。

主要功能

-

1、需要注册账号用户。(初步完成)

+

1、需要注册账号用户。(完成)

2、网站展示有三种展示方式:导航,标签,列表,卡片。其中导航以分类展示,分类顺序可以在书签分类下面拖动编辑。按照点击的次数从高到低在每个分类里面提取16个书签,再按照最近添加的书签提取前面的16个书签,然后合并起来。标签是一个快捷方式。列表以表格展示,显示书签详细类容,按照点击次数优先显示,点击次数相同,则按添加顺序优先。卡片以卡片方式显示,按照最近添加优先显示。这几种展示方式,可以在设置里面默认一种你常用的方式。(完成)

3、在书签分类里面,可以更新分类,删除分类,新增分类,对分类显示进行排序。分类的标签默认按照添加日期展示,但是可以点击表格的标题,按照点击次数,添加日期,最后点击从大到小进行排序。(完成)

4、可以按照指定添加时间段,指定分类目录,指定网址关键字等进行查询。(完成)

5、添加书签的时候,会自动获取title,供用户编辑。其中:Insert键打开添加页面,再次按Insert键保存书签,Esc取消添加。(完成)

6、可以导入Chrome的书签导出文件,暂时做在设置里面。(完成)

7、书签可以作为公有或者私有,公有可供所有人搜索。(完成)

-

8、可以将搜索到其他用户的书签转存为自己的书签。(已完成)

-

9、可以将书签导出来,然后导入到浏览器。(未完成)

+

8、可以将搜索到其他用户的书签转存为自己的书签。(完成)

+

9、可以将书签导出来,然后导入到浏览器。(完成)

10、在热门标签里面,有在网上找的热门书签。可以转存收藏到自己书签里面。快捷键R随机查看热门书签。(完成)

11、新增备忘录功能,有时候随手要做点纪录,就方便了。任意界面按快捷键A增加备忘录。双击备忘录可查看详情!(完成)

12、在设置的全局链接,可设置快捷键,用来在任何页面,快速打开设置的链接。(完成)

diff --git a/public/views/settings.html b/public/views/settings.html index 55d9049..6b753b6 100644 --- a/public/views/settings.html +++ b/public/views/settings.html @@ -6,7 +6,7 @@ 我的信息 - 上传或导出书签 + 上传导出 网站说明 @@ -123,6 +123,7 @@

4、导入的文件不能超过10M

5、如果重复导入,新的会覆盖旧的信息。

+

6、注意:从浏览器导入进系统的书签,没有浏览器目录的树形结构,在浏览器没有目录的标签,统统转入系统未分类目录下面。

导出为浏览器书签

@@ -139,15 +140,15 @@

7、如果公用一台电脑,如何区分我收藏的跟别人收藏的书签?

在线书签管理工具,帮助你快速记录你喜欢的网站,并可以随时随地查看这些站点,而不必拘泥于使用的浏览器。无论在什么地方,只要能接入网络,就能打开属于你自己的网络书签,看到自己收藏的页面网址。

主要功能

-

1、需要注册账号用户。(初步完成)

+

1、需要注册账号用户。(完成)

2、网站展示有三种展示方式:导航,标签,列表,卡片。其中导航以分类展示,分类顺序可以在书签分类下面拖动编辑。按照点击的次数从高到低在每个分类里面提取16个书签,再按照最近添加的书签提取前面的16个书签,然后合并起来。标签是一个快捷方式。列表以表格展示,显示书签详细类容,按照点击次数优先显示,点击次数相同,则按添加顺序优先。卡片以卡片方式显示,按照最近添加优先显示。这几种展示方式,可以在设置里面默认一种你常用的方式。(完成)

3、在书签分类里面,可以更新分类,删除分类,新增分类,对分类显示进行排序。分类的标签默认按照添加日期展示,但是可以点击表格的标题,按照点击次数,添加日期,最后点击从大到小进行排序。(完成)

4、可以按照指定添加时间段,指定分类目录,指定网址关键字等进行查询。(完成)

5、添加书签的时候,会自动获取title,供用户编辑。其中:Insert键打开添加页面,再次按Insert键保存书签,Esc取消添加。(完成)

6、可以导入Chrome的书签导出文件,暂时做在设置里面。(完成)

7、书签可以作为公有或者私有,公有可供所有人搜索。(完成)

-

8、可以将搜索到其他用户的书签转存为自己的书签。(已完成)

-

9、可以将书签导出来,然后导入到浏览器。(未完成)

+

8、可以将搜索到其他用户的书签转存为自己的书签。(完成)

+

9、可以将书签导出来,然后导入到浏览器。(完成)

10、在热门标签里面,有在网上找的热门书签。可以转存收藏到自己书签里面。快捷键R随机查看热门书签。(完成)

11、新增备忘录功能,有时候随手要做点纪录,就方便了。任意界面按快捷键A增加备忘录。双击备忘录可查看详情!(完成)

12、在设置的全局链接,可设置快捷键,用来在任何页面,快速打开设置的链接。(完成)

diff --git a/routes/api.js b/routes/api.js index ecc7efb..45a6a34 100644 --- a/routes/api.js +++ b/routes/api.js @@ -10,7 +10,8 @@ var webshot = require('webshot'); var fs = require('fs'); var request = require('request'); var cheerio = require('cheerio'); -const path = require('path'); +var path = require('path'); +var beautify_html = require('js-beautify').html; var storage = multer.diskStorage({ destination: function(req, file, cb) { @@ -19,7 +20,7 @@ var storage = multer.diskStorage({ filename: function(req, file, cb) { var now = new Date().format('yyyyMMddhhmmss') if (req.session.user) { - cb(null, req.session.username + '-' + now + '.html') + cb(null, 'importbookmark-' + req.session.username + '-' + now + '.html') } else { cb(null, "UnknowUser" + '-' + now + '.html') } @@ -1446,16 +1447,64 @@ api.post('/updateNote', function(req, res) { // 实现文件下载 api.get('/download', function(req, res) { - var fileName = req.query.fileName; - var filePath = path.join(path.resolve(__dirname, '..'), 'uploads', fileName); - console.log('download fileName = ', fileName, ', download filePath = ' + filePath); - res.download(filePath, function(err) { - if (err) { - console.log(err); - } else { - console.log('download filePath[ ' + filePath + ' ]success!'); - } - }); + console.log("download username = ", req.session.username); + + var userId = req.query.userId; + var type = req.query.type; + if (!req.session.user || req.session.user.id != userId) { + res.send(401); + return; + } + if (type == 'exportbookmark' && userId) { + db.getTags(userId) + .then((tags) => { + var meta = ''; + var init = 'Bookmarks

Bookmarks

'; + var $ = cheerio.load(init, { + decodeEntities: false, + xmlMode: true, + }); + + tags.forEach((tag, tagIndex) => { + $('#0').append('

' + tag.name + '

'); // 增加文件夹 + db.getExportBookmarksByTag(tag.id) + .then((bookmarks) => { + bookmarks.forEach((bookmark) => { + $('#' + bookmark.tag_id).append('
' + bookmark.title + '
'); + }); + if (tagIndex == tags.length - 1) { + console.log('export bookmarks document construct end...'); + var now = new Date().format('yyyyMMddhhmmss') + var fileName = 'exportbookmark-' + req.session.username + '-' + now + '.html'; + var filePath = path.join(path.resolve(__dirname, '..'), 'uploads', fileName); + fs.writeFile(filePath, beautify_html($.xml(), { + indent_size: 4, + }), function(err) { + if (err) { + console.log(err); + } else { + res.download(filePath, function(err) { + if (err) { + console.log(err); + } else { + console.log('download filePath[ ' + filePath + ' ]success!'); + } + }); + } + }) + } + }) + .catch((err) => { + console.log('getExportBookmarksByTag err', err); + }) + }) + }) + .catch((err) => { + console.log('exportbookmark err', err); + }); + } else { + res.send(401); + } }); function md5(str) {