diff options
Diffstat (limited to 'apps/cli/src/commands')
| -rw-r--r-- | apps/cli/src/commands/bookmarks.ts | 135 | ||||
| -rw-r--r-- | apps/cli/src/commands/lists.ts | 74 | ||||
| -rw-r--r-- | apps/cli/src/commands/tags.ts | 40 | ||||
| -rw-r--r-- | apps/cli/src/commands/whoami.ts | 10 |
4 files changed, 259 insertions, 0 deletions
diff --git a/apps/cli/src/commands/bookmarks.ts b/apps/cli/src/commands/bookmarks.ts new file mode 100644 index 00000000..264ad818 --- /dev/null +++ b/apps/cli/src/commands/bookmarks.ts @@ -0,0 +1,135 @@ +import * as fs from "node:fs"; +import { getAPIClient } from "@/lib/trpc"; +import { Command } from "@commander-js/extra-typings"; +import chalk from "chalk"; + +import type { ZBookmark } from "@hoarder/shared/types/bookmarks"; + +export const bookmarkCmd = new Command() + .name("bookmarks") + .description("Manipulating bookmarks"); + +function collect<T>(val: T, acc: T[]) { + acc.push(val); + return acc; +} + +function normalizeBookmark(bookmark: ZBookmark) { + const ret = { + ...bookmark, + tags: bookmark.tags.map((t) => t.name), + }; + + if (ret.content.type == "link" && ret.content.htmlContent) { + if (ret.content.htmlContent.length > 10) { + ret.content.htmlContent = + ret.content.htmlContent.substring(0, 10) + "... <CROPPED>"; + } + } + return ret; +} + +bookmarkCmd + .command("add") + .description("Creates a new bookmark") + .option( + "--link <link>", + "the link to add. Specify multiple times to add multiple links", + collect<string>, + [], + ) + .option( + "--note <note>", + "the note text to add. Specify multiple times to add multiple notes", + collect<string>, + [], + ) + .option("--stdin", "reads the data from stdin and store it as a note") + .action(async (opts) => { + const api = getAPIClient(); + + const promises = [ + ...opts.link.map((url) => + api.bookmarks.createBookmark.mutate({ type: "link", url }), + ), + ...opts.note.map((text) => + api.bookmarks.createBookmark.mutate({ type: "text", text }), + ), + ]; + + if (opts.stdin) { + const text = fs.readFileSync(0, "utf-8"); + promises.push( + api.bookmarks.createBookmark.mutate({ type: "text", text }), + ); + } + + const results = await Promise.allSettled(promises); + + for (const res of results) { + if (res.status == "fulfilled") { + console.log(normalizeBookmark(res.value)); + } else { + console.log(chalk.red(`Error: ${res.reason}`)); + } + } + }); + +bookmarkCmd + .command("get") + .description("fetch information about a bookmark") + .argument("<id>", "The id of the bookmark to get") + .action(async (id) => { + const api = getAPIClient(); + const resp = await api.bookmarks.getBookmark.query({ bookmarkId: id }); + console.log(normalizeBookmark(resp)); + }); + +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( + "--include-archived", + "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)); + }); + +bookmarkCmd + .command("delete") + .description("delete a bookmark") + .argument("<id>", "The id of the bookmark to delete") + .action(async (id) => { + const api = getAPIClient(); + await api.bookmarks.deleteBookmark.mutate({ bookmarkId: id }); + console.log(`Bookmark ${id} got deleted`); + }); diff --git a/apps/cli/src/commands/lists.ts b/apps/cli/src/commands/lists.ts new file mode 100644 index 00000000..abf6f78c --- /dev/null +++ b/apps/cli/src/commands/lists.ts @@ -0,0 +1,74 @@ +import { getAPIClient } from "@/lib/trpc"; +import { Command } from "@commander-js/extra-typings"; +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/src/commands/tags.ts b/apps/cli/src/commands/tags.ts new file mode 100644 index 00000000..f74f1df6 --- /dev/null +++ b/apps/cli/src/commands/tags.ts @@ -0,0 +1,40 @@ +import { getAPIClient } from "@/lib/trpc"; +import { Command } from "@commander-js/extra-typings"; +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/src/commands/whoami.ts b/apps/cli/src/commands/whoami.ts new file mode 100644 index 00000000..b55bfa67 --- /dev/null +++ b/apps/cli/src/commands/whoami.ts @@ -0,0 +1,10 @@ +import { getAPIClient } from "@/lib/trpc"; +import { Command } from "@commander-js/extra-typings"; + +export const whoamiCmd = new Command() + .name("whoami") + .description("returns info about the owner of this API key") + .action(async () => { + const resp = await getAPIClient().users.whoami.query(); + console.log(resp); + }); |
