aboutsummaryrefslogtreecommitdiffstats
path: root/apps/cli/src/commands
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2024-04-24 11:37:35 +0100
committerMohamedBassem <me@mbassem.com>2024-04-24 12:21:55 +0100
commit3352a3ea393849550573deff8d774ba6bf149471 (patch)
treea7d78db4fadbe7123c7d5045f5a537965382b84a /apps/cli/src/commands
parentaf0cf9c1ee10901ab91b04a1d73afdcb2191a88f (diff)
downloadkarakeep-3352a3ea393849550573deff8d774ba6bf149471.tar.zst
build(cli): Prepare for publishing CLI to npm
Diffstat (limited to 'apps/cli/src/commands')
-rw-r--r--apps/cli/src/commands/bookmarks.ts135
-rw-r--r--apps/cli/src/commands/lists.ts74
-rw-r--r--apps/cli/src/commands/tags.ts40
-rw-r--r--apps/cli/src/commands/whoami.ts10
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);
+ });