From 80e9e68d842fac570a3b2480d294bb65e43048b1 Mon Sep 17 00:00:00 2001 From: Pat O'Connor Date: Sun, 17 Sep 2023 17:43:52 -0700 Subject: [PATCH 1/5] Add csv export for bookmarks --- package-lock.json | 11 +++++++++++ package.json | 1 + src/bookmarks-db.js | 16 ++++++++++++++++ src/pages/admin/data.hbs | 9 ++++++--- src/routes/admin.js | 12 ++++++++++++ 5 files changed, 46 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f9d3893..b1d989f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "chalk": "^5.2.0", "connect-sqlite3": "^0.9.13", "cors": "^2.8.5", + "csv-stringify": "^6.4.2", "dotenv": "^16.0.3", "es6-promisify": "^7.0.0", "express": "^4.18.2", @@ -1197,6 +1198,11 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/csv-stringify": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.4.2.tgz", + "integrity": "sha512-DXIdnnCUQYjDKTu6TgCSzRDiAuLxDjhl4ErFP9FGMF3wzBGOVMg9bZTLaUcYtuvhXgNbeXPKeaRfpgyqE4xySw==" + }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -7025,6 +7031,11 @@ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" }, + "csv-stringify": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.4.2.tgz", + "integrity": "sha512-DXIdnnCUQYjDKTu6TgCSzRDiAuLxDjhl4ErFP9FGMF3wzBGOVMg9bZTLaUcYtuvhXgNbeXPKeaRfpgyqE4xySw==" + }, "data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", diff --git a/package.json b/package.json index 2f801a8..3e680e3 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "chalk": "^5.2.0", "connect-sqlite3": "^0.9.13", "cors": "^2.8.5", + "csv-stringify": "^6.4.2", "dotenv": "^16.0.3", "es6-promisify": "^7.0.0", "express": "^4.18.2", diff --git a/src/bookmarks-db.js b/src/bookmarks-db.js index 10b8e03..22854ee 100644 --- a/src/bookmarks-db.js +++ b/src/bookmarks-db.js @@ -189,6 +189,22 @@ export async function getBookmarks(limit = 10, offset = 0) { return undefined; } +export async function getBookmarksForCSVExport() { + // We use a try catch block in case of db errors + try { + const results = await db.all( + 'SELECT title,url,description,tags,created_at,updated_at from bookmarks', + ); + // These titles should be updated if the query changes above. + const columnTitles = {'title': 'title','url': 'url','description': 'description','tags': 'tags','created_at': 'created_at','updated_at': 'updated_at'}; + return [columnTitles].concat(results); + } catch (dbError) { + // Database connection error + console.error(dbError); + } + return undefined; +} + export async function getBookmarkCountForTags(tags) { const tagClauses = tags.map(() => `(tags like ? OR tags like ?)`).join(' AND '); const tagParams = tags.map((tag) => [`%${tag}% `, `%${tag}%`]).flat(); diff --git a/src/pages/admin/data.hbs b/src/pages/admin/data.hbs index 26c3118..f3eec9b 100644 --- a/src/pages/admin/data.hbs +++ b/src/pages/admin/data.hbs @@ -1,10 +1,13 @@

Download bookmarks

-

- You can download your bookmarks sqlite db - here. +

+ You can download your bookmarks as:

+

It's less likely you'll need this, but you can also download your activitypub.db diff --git a/src/routes/admin.js b/src/routes/admin.js index 7ed9282..be9a3b7 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -2,6 +2,7 @@ import express from 'express'; import { domain, actorInfo, parseJSON } from '../util.js'; import { isAuthenticated } from '../session-auth.js'; import { lookupActorInfo, createFollowMessage, createUnfollowMessage, signAndSend, getInboxFromActorProfile } from '../activitypub.js'; +import { stringify as csvStringify } from 'csv-stringify/sync'; const DATA_PATH = '/app/.data'; @@ -91,6 +92,17 @@ router.get('/bookmarks.db', isAuthenticated, async (req, res) => { res.download(filePath); }); +router.get('/bookmarks.csv', isAuthenticated, async (req, res) => { + const bookmarksDb = req.app.get('bookmarksDb'); + const bookmarks = await bookmarksDb.getBookmarksForCSVExport(); + const result = csvStringify(bookmarks); + + res.setHeader('Content-Type', 'text/csv'); + res.setHeader('Content-Disposition', 'attachment; filename="bookmarks.csv"'); + + res.send(result); +}); + router.get('/activitypub.db', isAuthenticated, async (req, res) => { const filePath = `${DATA_PATH}/activitypub.db`; From 46b910c0b55efb623400d152347a359f99f16d15 Mon Sep 17 00:00:00 2001 From: Pat O'Connor Date: Sun, 17 Sep 2023 18:28:54 -0700 Subject: [PATCH 2/5] Fix eslint --- src/bookmarks-db.js | 6 ++---- src/routes/admin.js | 4 +++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bookmarks-db.js b/src/bookmarks-db.js index 22854ee..80e5f30 100644 --- a/src/bookmarks-db.js +++ b/src/bookmarks-db.js @@ -192,11 +192,9 @@ export async function getBookmarks(limit = 10, offset = 0) { export async function getBookmarksForCSVExport() { // We use a try catch block in case of db errors try { - const results = await db.all( - 'SELECT title,url,description,tags,created_at,updated_at from bookmarks', - ); + const results = await db.all('SELECT title,url,description,tags,created_at,updated_at from bookmarks'); // These titles should be updated if the query changes above. - const columnTitles = {'title': 'title','url': 'url','description': 'description','tags': 'tags','created_at': 'created_at','updated_at': 'updated_at'}; + const columnTitles = { title: 'title', url: 'url', description: 'description', tags: 'tags', created_at: 'created_at', updated_at: 'updated_at' }; return [columnTitles].concat(results); } catch (dbError) { // Database connection error diff --git a/src/routes/admin.js b/src/routes/admin.js index be9a3b7..fe2e76a 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -1,8 +1,10 @@ import express from 'express'; +/* eslint-disable import/no-unresolved, node/no-missing-import */ +import { stringify as csvStringify } from 'csv-stringify/sync'; // https://github.com/adaltas/node-csv/issues/323 +/* eslint-enable */ import { domain, actorInfo, parseJSON } from '../util.js'; import { isAuthenticated } from '../session-auth.js'; import { lookupActorInfo, createFollowMessage, createUnfollowMessage, signAndSend, getInboxFromActorProfile } from '../activitypub.js'; -import { stringify as csvStringify } from 'csv-stringify/sync'; const DATA_PATH = '/app/.data'; From 28e09184200496dd960ec84bc3a13b06eadaf7c2 Mon Sep 17 00:00:00 2001 From: Pat O'Connor Date: Tue, 19 Sep 2023 19:43:55 -0700 Subject: [PATCH 3/5] Derive csv headers and select statement fields from single list --- src/bookmarks-db.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/bookmarks-db.js b/src/bookmarks-db.js index 80e5f30..2ecfc5b 100644 --- a/src/bookmarks-db.js +++ b/src/bookmarks-db.js @@ -192,9 +192,12 @@ export async function getBookmarks(limit = 10, offset = 0) { export async function getBookmarksForCSVExport() { // We use a try catch block in case of db errors try { - const results = await db.all('SELECT title,url,description,tags,created_at,updated_at from bookmarks'); - // These titles should be updated if the query changes above. - const columnTitles = { title: 'title', url: 'url', description: 'description', tags: 'tags', created_at: 'created_at', updated_at: 'updated_at' }; + const headers = ['title', 'url', 'description', 'tags', 'created_at', 'updated_at']; + const selectHeaders = headers.join(','); + // This will create an object where the keys and values match. This will + // allow the csv stringifier to interpret this as a header row. + const columnTitles = Object.fromEntries(headers.map((header) => [header, header])); + const results = await db.all(`SELECT ${selectHeaders} from bookmarks`); return [columnTitles].concat(results); } catch (dbError) { // Database connection error From 0784c9de1a14a5209f1960b7e3ab8e7ee967691a Mon Sep 17 00:00:00 2001 From: Pat O'Connor Date: Tue, 19 Sep 2023 19:44:27 -0700 Subject: [PATCH 4/5] Style changes --- src/pages/admin/data.hbs | 2 +- src/routes/admin.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/admin/data.hbs b/src/pages/admin/data.hbs index f3eec9b..e84508d 100644 --- a/src/pages/admin/data.hbs +++ b/src/pages/admin/data.hbs @@ -1,7 +1,7 @@

Download bookmarks

-

+

You can download your bookmarks as: