diff options
| author | MohamedBassem <me@mbassem.com> | 2024-04-24 11:08:30 +0100 |
|---|---|---|
| committer | MohamedBassem <me@mbassem.com> | 2024-04-24 11:08:30 +0100 |
| commit | af0cf9c1ee10901ab91b04a1d73afdcb2191a88f (patch) | |
| tree | c1d46b198ef7964fbc2199477affc96ed2016932 | |
| parent | 5dac180f486cbc6bb202debd5dde996a9c8204b4 (diff) | |
| download | karakeep-af0cf9c1ee10901ab91b04a1d73afdcb2191a88f.tar.zst | |
feature(cli): Add ability to manipulate lists, tags and update bookmarks
| -rw-r--r-- | apps/cli/commands/bookmarks.ts | 23 | ||||
| -rw-r--r-- | apps/cli/commands/lists.ts | 74 | ||||
| -rw-r--r-- | apps/cli/commands/tags.ts | 40 | ||||
| -rw-r--r-- | apps/cli/index.ts | 4 | ||||
| -rw-r--r-- | apps/cli/package.json | 9 | ||||
| -rw-r--r-- | pnpm-lock.yaml | 40 |
6 files changed, 186 insertions, 4 deletions
diff --git a/apps/cli/commands/bookmarks.ts b/apps/cli/commands/bookmarks.ts index 55c87b05..0da3dd71 100644 --- a/apps/cli/commands/bookmarks.ts +++ b/apps/cli/commands/bookmarks.ts @@ -86,6 +86,27 @@ bookmarkCmd }); bookmarkCmd + .command("update") + .description("Archive a bookmark") + .option("--title <title>", "If set, the bookmark's title will be updated") + .option("--note <note>", "If set, the bookmark's note will be updated") + .option("--archive", "If set, the bookmark will be archived") + .option("--no-archive", "If set, the bookmark will be unarchived") + .option("--favourite", "If set, the bookmark will be favourited") + .option("--no-favourite", "If set, the bookmark will be unfavourited") + .argument("<id>", "The id of the bookmark to get") + .action(async (id, opts) => { + const api = getAPIClient(); + const resp = await api.bookmarks.updateBookmark.mutate({ + bookmarkId: id, + archived: opts.archive, + favourited: opts.favourite, + title: opts.title, + }); + console.log(resp); + }); + +bookmarkCmd .command("list") .description("list all bookmarks") .option( @@ -93,10 +114,12 @@ bookmarkCmd "If set, archived bookmarks will be fetched as well", false, ) + .option("--list-id <id>", "If set, only items from that list will be fetched") .action(async (opts) => { const api = getAPIClient(); const resp = await api.bookmarks.getBookmarks.query({ archived: opts.includeArchived ? undefined : false, + listId: opts.listId, }); console.log(resp.bookmarks.map(normalizeBookmark)); }); diff --git a/apps/cli/commands/lists.ts b/apps/cli/commands/lists.ts new file mode 100644 index 00000000..099b7869 --- /dev/null +++ b/apps/cli/commands/lists.ts @@ -0,0 +1,74 @@ +import { Command } from "@commander-js/extra-typings"; +import { getAPIClient } from "lib/trpc"; +import { getBorderCharacters, table } from "table"; + +import { listsToTree } from "@hoarder/shared/utils/listUtils"; + +export const listsCmd = new Command() + .name("lists") + .description("Manipulating lists"); + +listsCmd + .command("list") + .description("Lists all lists") + .action(async () => { + const api = getAPIClient(); + + const resp = await api.lists.list.query(); + const { allPaths } = listsToTree(resp.lists); + + const data: string[][] = [["Id", "Name"]]; + + allPaths.forEach((path) => { + const name = path.map((p) => `${p.icon} ${p.name}`).join(" / "); + const id = path[path.length - 1].id; + data.push([id, name]); + }); + console.log( + table(data, { border: getBorderCharacters("ramac"), singleLine: true }), + ); + }); + +listsCmd + .command("delete") + .description("Deletes a list") + .argument("<id>", "The id of the list") + .action(async (id) => { + const api = getAPIClient(); + + await api.lists.delete.mutate({ + listId: id, + }); + console.log("Successfully deleted list with id:", id); + }); + +listsCmd + .command("add-bookmark") + .description("Add a bookmark to list") + .requiredOption("--list <id>", "The id of the list") + .requiredOption("--bookmark <bookmark>", "The id of the bookmark") + .action(async (opts) => { + const api = getAPIClient(); + + await api.lists.addToList.mutate({ + listId: opts.list, + bookmarkId: opts.bookmark, + }); + console.log("Successfully added bookmark from list"); + }); + +listsCmd + .command("remove-bookmark") + .description("Remove a bookmark from list") + .requiredOption("--list <id>", "The id of the list") + .requiredOption("--bookmark <bookmark>", "The id of the bookmark") + .action(async (opts) => { + const api = getAPIClient(); + + await api.lists.removeFromList.mutate({ + listId: opts.list, + bookmarkId: opts.bookmark, + }); + + console.log("Successfully removed bookmark from list"); + }); diff --git a/apps/cli/commands/tags.ts b/apps/cli/commands/tags.ts new file mode 100644 index 00000000..f9df83cd --- /dev/null +++ b/apps/cli/commands/tags.ts @@ -0,0 +1,40 @@ +import { Command } from "@commander-js/extra-typings"; +import { getAPIClient } from "lib/trpc"; +import { getBorderCharacters, table } from "table"; + +export const tagsCmd = new Command() + .name("tags") + .description("Manipulating tags"); + +tagsCmd + .command("list") + .description("Lists all tags") + .action(async () => { + const api = getAPIClient(); + + const tags = (await api.tags.list.query()).tags; + tags.sort((a, b) => b.count - a.count); + + const data: string[][] = [["Id", "Name", "Num bookmarks"]]; + + tags.forEach((tag) => { + data.push([tag.id, tag.name, tag.count.toString()]); + }); + console.log( + table(data, { border: getBorderCharacters("ramac"), singleLine: true }), + ); + }); + +tagsCmd + .command("delete") + .description("Delete a tag") + .argument("<id>", "The id of the tag") + .action(async (id) => { + const api = getAPIClient(); + + await api.tags.delete.mutate({ + tagId: id, + }); + + console.log("Successfully delete the tag with id:", id); + }); diff --git a/apps/cli/index.ts b/apps/cli/index.ts index 4d0adafb..5971b811 100644 --- a/apps/cli/index.ts +++ b/apps/cli/index.ts @@ -1,5 +1,7 @@ import { Command, Option } from "@commander-js/extra-typings"; import { bookmarkCmd } from "commands/bookmarks"; +import { listsCmd } from "commands/lists"; +import { tagsCmd } from "commands/tags"; import { whoamiCmd } from "commands/whoami"; import { setGlobalOptions } from "lib/globals"; @@ -22,6 +24,8 @@ const program = new Command() .version(process.env.SERVER_VERSION ?? "nightly"); program.addCommand(bookmarkCmd); +program.addCommand(listsCmd); +program.addCommand(tagsCmd); program.addCommand(whoamiCmd); setGlobalOptions(program.opts()); diff --git a/apps/cli/package.json b/apps/cli/package.json index 05ab0d0f..87c02c3d 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -5,16 +5,17 @@ "private": true, "dependencies": { "@commander-js/extra-typings": "^12.0.1", - "@hoarder/trpc": "workspace:^0.1.0", "@hoarder/shared": "workspace:^0.1.0", + "@hoarder/trpc": "workspace:^0.1.0", "@hoarder/tsconfig": "workspace:^0.1.0", - "@tsconfig/node21": "^21.0.1", - "tsx": "^4.7.1", "@trpc/client": "11.0.0-next-beta.308", "@trpc/server": "11.0.0-next-beta.308", + "@tsconfig/node21": "^21.0.1", "chalk": "^5.3.0", "commander": "^12.0.0", - "superjson": "^2.2.1" + "superjson": "^2.2.1", + "table": "^6.8.2", + "tsx": "^4.7.1" }, "devDependencies": { "@hoarder/eslint-config": "workspace:^0.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 754fe314..a1010473 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -200,6 +200,9 @@ importers: superjson: specifier: ^2.2.1 version: 2.2.1 + table: + specifier: ^6.8.2 + version: 6.8.2 tsx: specifier: ^4.7.1 version: 4.7.1 @@ -4784,6 +4787,10 @@ packages: resolution: {integrity: sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==} engines: {node: '>=4'} + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + astring@1.8.6: resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==} hasBin: true @@ -8223,6 +8230,9 @@ packages: lodash.throttle@4.1.1: resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} @@ -10830,6 +10840,10 @@ packages: resolution: {integrity: sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==} engines: {node: '>=6'} + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + slugify@1.6.6: resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==} engines: {node: '>=8.0.0'} @@ -11175,6 +11189,10 @@ packages: resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==} engines: {node: ^14.18.0 || >=16.0.0} + table@6.8.2: + resolution: {integrity: sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==} + engines: {node: '>=10.0.0'} + tailwind-merge@2.2.1: resolution: {integrity: sha512-o+2GTLkthfa5YUt4JxPfzMIpQzZ3adD1vLVkvKE1Twl9UAhGsEbIZhHHZVRttyW177S8PDJI3bTQNaebyofK3Q==} @@ -18822,6 +18840,9 @@ snapshots: astral-regex@1.0.0: dev: false + astral-regex@2.0.0: + dev: false + astring@1.8.6: {} async-limiter@1.0.1: @@ -23488,6 +23509,9 @@ snapshots: lodash.throttle@4.1.1: {} + lodash.truncate@4.4.2: + dev: false + lodash.uniq@4.5.0: dev: false @@ -27348,6 +27372,13 @@ snapshots: is-fullwidth-code-point: 2.0.0 dev: false + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: false + slugify@1.6.6: dev: false @@ -27759,6 +27790,15 @@ snapshots: tslib: 2.6.2 dev: true + table@6.8.2: + dependencies: + ajv: 8.12.0 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: false + tailwind-merge@2.2.1: dependencies: '@babel/runtime': 7.23.9 |
