-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
875 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,34 @@ | ||
# linkace-import-firefox | ||
Import Firefox JSON bookmarks to https://www.linkace.org/ | ||
|
||
Import Firefox JSON bookmarks to [LinkAce](https://www.linkace.org/) self-hosted bookmark archive. | ||
|
||
## Installation | ||
|
||
Clone and run `npm i`. | ||
|
||
## Usage | ||
|
||
With LinkAce .env in current working directory: | ||
``` | ||
node index.js bookmarks.json | ||
``` | ||
Or: | ||
|
||
``` | ||
node index.js --bookmarks=bookmarks.json | ||
``` | ||
|
||
With path to LinkAce .env file: | ||
``` | ||
node index.js --env=/path/to/.env bookmarks.json | ||
``` | ||
Or: | ||
``` | ||
node index.js --env=/path/to/.env --bookmarks=bookmarks.json | ||
``` | ||
|
||
To start from specific URL: | ||
|
||
``` | ||
node index.js bookmarks.json --from=https://example.com | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { readFile } from 'node:fs/promises'; | ||
import dotenv from 'dotenv'; | ||
import { parseArgs } from './lib/parse-args.js'; | ||
import { getDbClient } from './lib/get-db-client.js'; | ||
import { importBookmarks } from './lib/import-bookmarks.js'; | ||
|
||
/** | ||
* @typedef { import( './types/firefox-bookmarks.js' ).Bookmarks } Bookmarks | ||
*/ | ||
|
||
async function main() | ||
{ | ||
const args = parseArgs( process.argv.slice( 2 ) ); | ||
|
||
const bookmarksPath = args.bookmarks || args._[0]; | ||
|
||
if ( !bookmarksPath ) | ||
{ | ||
console.log( | ||
`Usage: | ||
With LinkAce .env in current working directory: | ||
${process.argv[0]} ${process.argv[1]} bookmarks.json | ||
Or: | ||
${process.argv[0]} ${process.argv[1]} --bookmarks=bookmarks.json | ||
With path to LinkAce .env file: | ||
${process.argv[0]} ${process.argv[1]} --env=/path/to/.env bookmarks.json | ||
Or: | ||
${process.argv[0]} ${process.argv[1]} --env=/path/to/.env --bookmarks=bookmarks.json | ||
To start from specific URL: | ||
${process.argv[0]} ${process.argv[1]} bookmarks.json --from=https://example.com | ||
`, | ||
); | ||
|
||
process.exit( 1 ); | ||
} | ||
|
||
const jsonData = await readFile( bookmarksPath, 'utf8' ); | ||
/** @type { Bookmarks } */ | ||
const bookmarks = JSON.parse( jsonData ); | ||
|
||
bookmarks.title = 'Firefox'; | ||
|
||
dotenv.config({ | ||
path: args.env, | ||
}); | ||
|
||
const client = getDbClient(); | ||
|
||
await client.connect(); | ||
|
||
try | ||
{ | ||
const importedCount = await importBookmarks( client, bookmarks, args.from ); | ||
|
||
console.log( `Imported ${importedCount} bookmarks` ); | ||
} | ||
finally | ||
{ | ||
await client.end(); | ||
} | ||
} | ||
|
||
main() | ||
.catch( | ||
( error ) => | ||
{ | ||
console.error( error ); | ||
process.exit( 1 ); | ||
}, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "esnext", | ||
"module": "NodeNext", | ||
"lib": ["esnext"], | ||
|
||
"checkJs": true, | ||
"noEmit": true, | ||
|
||
"strict": true, | ||
|
||
"noUnusedLocals": true, | ||
"noUnusedParameters": true, | ||
"noImplicitReturns": true, | ||
"noFallthroughCasesInSwitch": true, | ||
"noUncheckedIndexedAccess": true, | ||
"forceConsistentCasingInFileNames": true, | ||
|
||
"types": [ | ||
"node" | ||
] | ||
}, | ||
"exclude": [ | ||
"node_modules", | ||
"**/node_modules/*" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/** | ||
* @typedef { import( 'pg' ).Client } Client | ||
*/ | ||
|
||
/** | ||
* @param { Client } client | ||
* @param { number } linkId | ||
* @param { number } listId | ||
*/ | ||
export async function insertLinkList( client, linkId, listId ) | ||
{ | ||
await client.query( | ||
'INSERT INTO link_lists(link_id, list_id) VALUES ($1, $2)', | ||
[linkId, listId], | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/** | ||
* @typedef { import( 'pg' ).Client } Client | ||
*/ | ||
|
||
/** | ||
* @param { Client } client | ||
* @param { number } linkId | ||
* @param { number } tagId | ||
*/ | ||
export async function insertLinkTag( client, linkId, tagId ) | ||
{ | ||
await client.query( | ||
'INSERT INTO link_tags(link_id, tag_id) VALUES ($1, $2)', | ||
[linkId, tagId], | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { getUserId } from '../get-user-id.js'; | ||
|
||
/** | ||
* @typedef { import( 'pg' ).Client } Client | ||
* @typedef { import( './insert-link.types.js' ).LinkRecord } LinkRecord | ||
*/ | ||
|
||
/** | ||
* @param { Client } client | ||
* @param { LinkRecord } link | ||
*/ | ||
export async function insertLink( client, link ) | ||
{ | ||
const userId = getUserId(); | ||
const icon = ( | ||
( | ||
link.icon | ||
&& ( link.icon.length < 191 ) | ||
) | ||
? link.icon | ||
: null | ||
); | ||
const result = await client.query( | ||
`INSERT INTO | ||
links(user_id, url, title, created_at, updated_at, icon) | ||
VALUES | ||
($1, $2, $3, $4, $5, $6) | ||
RETURNING id`, | ||
[userId, link.url, link.title, link.createdAt, link.updatedAt, icon], | ||
); | ||
|
||
return Number( result.rows[0].id ); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export type LinkRecord = { | ||
url: string, | ||
title: string, | ||
createdAt: Date, | ||
updatedAt: Date, | ||
icon?: string, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { getUserId } from '../get-user-id.js'; | ||
|
||
/** | ||
* @typedef { import( 'pg' ).Client } Client | ||
*/ | ||
|
||
/** | ||
* @param { Client } client | ||
* @param { string } name | ||
*/ | ||
export async function insertList( client, name ) | ||
{ | ||
const userId = getUserId(); | ||
const result = await client.query( | ||
'INSERT INTO lists(user_id, name) VALUES ($1, $2) RETURNING id', | ||
[userId, name], | ||
); | ||
|
||
return Number( result.rows[0].id ); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { getUserId } from '../get-user-id.js'; | ||
|
||
/** | ||
* @typedef { import( 'pg' ).Client } Client | ||
*/ | ||
|
||
/** | ||
* @param { Client } client | ||
* @param { string } tag | ||
*/ | ||
export async function insertTag( client, tag ) | ||
{ | ||
const userId = getUserId(); | ||
const result = await client.query( | ||
'INSERT INTO tags(user_id, name) VALUES ($1, $2) RETURNING id', | ||
[userId, tag], | ||
); | ||
|
||
return Number( result.rows[0].id ); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { getUserId } from '../get-user-id.js'; | ||
|
||
/** | ||
* @typedef { import( 'pg' ).Client } Client | ||
*/ | ||
|
||
/** | ||
* @param { Client } client | ||
*/ | ||
export async function selectLists( client ) | ||
{ | ||
const userId = getUserId(); | ||
const result = await client.query( | ||
'SELECT id, name FROM lists WHERE user_id = $1', | ||
[userId], | ||
); | ||
|
||
/** @type { Map<string, number> } */ | ||
const lists = new Map(); | ||
|
||
for ( const row of result.rows ) | ||
{ | ||
lists.set( row.name, row.id ); | ||
} | ||
|
||
return lists; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { getUserId } from '../get-user-id.js'; | ||
|
||
/** | ||
* @typedef { import( 'pg' ).Client } Client | ||
*/ | ||
|
||
/** | ||
* @param { Client } client | ||
*/ | ||
export async function selectTags( client ) | ||
{ | ||
const userId = getUserId(); | ||
const result = await client.query( | ||
'SELECT id, name FROM tags WHERE user_id = $1', | ||
[userId], | ||
); | ||
|
||
/** @type { Map<string, number> } */ | ||
const tags = new Map(); | ||
|
||
for ( const row of result.rows ) | ||
{ | ||
tags.set( row.name, row.id ); | ||
} | ||
|
||
return tags; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/** | ||
* @typedef { import( '../types/firefox-bookmarks.js' ).Bookmarks } Bookmarks | ||
* @typedef { import( './get-bookmarks.types.js' ).ContainerWithTitles } ContainerWithTitles | ||
* @typedef { import( './get-bookmarks.types.js' ).BookmarkWithTitles } BookmarkWithTitles | ||
*/ | ||
|
||
/** | ||
* @param { Bookmarks } bookmarks | ||
* @returns { Generator<BookmarkWithTitles, void, void> } | ||
*/ | ||
export function* getBookmarks( bookmarks ) | ||
{ | ||
/** @type { Array<ContainerWithTitles> } */ | ||
const items = [bookmarks]; | ||
/** @type { ContainerWithTitles | undefined } */ | ||
let current; | ||
|
||
while ( (current = items.pop()) ) | ||
{ | ||
if ( current.children === undefined ) | ||
{ | ||
continue; | ||
} | ||
|
||
const currentTitles = [ | ||
...(current.titles ?? []), | ||
current.title, | ||
]; | ||
|
||
for ( const child of current.children ) | ||
{ | ||
if ( child.type === 'text/x-moz-place' ) | ||
{ | ||
if ( child.uri.startsWith( 'place:' ) ) | ||
{ | ||
continue; | ||
} | ||
|
||
/** @type { BookmarkWithTitles } */(child).titles = currentTitles; | ||
|
||
yield child; | ||
} | ||
else | ||
{ | ||
/** @type { ContainerWithTitles } */(child).titles = currentTitles; | ||
|
||
items.push( child ); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import type { | ||
ItemContainer, | ||
ItemPlace, | ||
} from '../types/firefox-bookmarks.js'; | ||
|
||
export type ContainerWithTitles = ItemContainer & { | ||
titles?: Array<string>, | ||
}; | ||
|
||
export type BookmarkWithTitles = ItemPlace & { | ||
titles?: Array<string>, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import pg from 'pg'; | ||
|
||
export function getDbClient() | ||
{ | ||
if ( process.env.DB_CONNECTION !== 'pgsql' ) | ||
{ | ||
throw new Error( 'Only PostgreSQL database supported' ); | ||
} | ||
|
||
return new pg.Client({ | ||
host: process.env.DB_HOST, | ||
port: process.env.DB_PORT ? Number( process.env.DB_PORT ) : undefined, | ||
database: process.env.DB_DATABASE, | ||
user: process.env.DB_USERNAME, | ||
password: process.env.DB_PASSWORD, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export function getUserId() | ||
{ | ||
return process.env.LINKACE_USER_ID ? Number( process.env.LINKACE_USER_ID ) : 1; | ||
} |
Oops, something went wrong.