aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2025-03-09 13:22:18 +0000
committerMohamed Bassem <me@mbassem.com>2025-03-09 13:22:36 +0000
commit80a808048340f7f5e95e71c4ee649fdae7c8c565 (patch)
tree746f98855915c91695a5747fc4281bdba268aee1 /apps/web
parentf42a305fcbd68bf5983bdd75a784ea87e818fd2f (diff)
downloadkarakeep-80a808048340f7f5e95e71c4ee649fdae7c8c565.tar.zst
feat: Move background jobs with the admin actions. Fixes #1083
Diffstat (limited to 'apps/web')
-rw-r--r--apps/web/app/admin/actions/page.tsx5
-rw-r--r--apps/web/app/admin/background_jobs/page.tsx5
-rw-r--r--apps/web/app/admin/layout.tsx4
-rw-r--r--apps/web/components/admin/AdminActions.tsx159
-rw-r--r--apps/web/components/admin/BackgroundJobs.tsx257
-rw-r--r--apps/web/components/admin/ServerStats.tsx82
-rw-r--r--apps/web/lib/i18n/locales/en/translation.json4
7 files changed, 267 insertions, 249 deletions
diff --git a/apps/web/app/admin/actions/page.tsx b/apps/web/app/admin/actions/page.tsx
deleted file mode 100644
index 51f7e5d4..00000000
--- a/apps/web/app/admin/actions/page.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import AdminActions from "@/components/admin/AdminActions";
-
-export default function AdminActionsPage() {
- return <AdminActions />;
-}
diff --git a/apps/web/app/admin/background_jobs/page.tsx b/apps/web/app/admin/background_jobs/page.tsx
new file mode 100644
index 00000000..6a13dd64
--- /dev/null
+++ b/apps/web/app/admin/background_jobs/page.tsx
@@ -0,0 +1,5 @@
+import BackgroundJobs from "@/components/admin/BackgroundJobs";
+
+export default function BackgroundJobsPage() {
+ return <BackgroundJobs />;
+}
diff --git a/apps/web/app/admin/layout.tsx b/apps/web/app/admin/layout.tsx
index 7b20b7ad..20bd38bb 100644
--- a/apps/web/app/admin/layout.tsx
+++ b/apps/web/app/admin/layout.tsx
@@ -31,9 +31,9 @@ const adminSidebarItems = (
path: "/admin/users",
},
{
- name: t("common.actions"),
+ name: t("admin.background_jobs.background_jobs"),
icon: <Settings size={18} />,
- path: "/admin/actions",
+ path: "/admin/background_jobs",
},
];
diff --git a/apps/web/components/admin/AdminActions.tsx b/apps/web/components/admin/AdminActions.tsx
deleted file mode 100644
index fb151ac8..00000000
--- a/apps/web/components/admin/AdminActions.tsx
+++ /dev/null
@@ -1,159 +0,0 @@
-"use client";
-
-import { ActionButton } from "@/components/ui/action-button";
-import { toast } from "@/components/ui/use-toast";
-import { useTranslation } from "@/lib/i18n/client";
-import { api } from "@/lib/trpc";
-
-export default function AdminActions() {
- const { t } = useTranslation();
- const { mutate: recrawlLinks, isPending: isRecrawlPending } =
- api.admin.recrawlLinks.useMutation({
- onSuccess: () => {
- toast({
- description: "Recrawl enqueued",
- });
- },
- onError: (e) => {
- toast({
- variant: "destructive",
- description: e.message,
- });
- },
- });
-
- const { mutate: reindexBookmarks, isPending: isReindexPending } =
- api.admin.reindexAllBookmarks.useMutation({
- onSuccess: () => {
- toast({
- description: "Reindex enqueued",
- });
- },
- onError: (e) => {
- toast({
- variant: "destructive",
- description: e.message,
- });
- },
- });
-
- const { mutate: reprocessAssetsFixMode, isPending: isReprocessingPending } =
- api.admin.reprocessAssetsFixMode.useMutation({
- onSuccess: () => {
- toast({
- description: "Reprocessing enqueued",
- });
- },
- onError: (e) => {
- toast({
- variant: "destructive",
- description: e.message,
- });
- },
- });
-
- const {
- mutate: reRunInferenceOnAllBookmarks,
- isPending: isInferencePending,
- } = api.admin.reRunInferenceOnAllBookmarks.useMutation({
- onSuccess: () => {
- toast({
- description: "Inference jobs enqueued",
- });
- },
- onError: (e) => {
- toast({
- variant: "destructive",
- description: e.message,
- });
- },
- });
-
- const { mutateAsync: tidyAssets, isPending: isTidyAssetsPending } =
- api.admin.tidyAssets.useMutation({
- onSuccess: () => {
- toast({
- description: "Tidy assets request has been enqueued!",
- });
- },
- onError: (e) => {
- toast({
- variant: "destructive",
- description: e.message,
- });
- },
- });
-
- return (
- <div>
- <div className="mb-2 text-xl font-medium">{t("common.actions")}</div>
- <div className="flex flex-col gap-2 sm:w-1/2">
- <ActionButton
- variant="destructive"
- loading={isRecrawlPending}
- onClick={() =>
- recrawlLinks({ crawlStatus: "failure", runInference: true })
- }
- >
- {t("admin.actions.recrawl_failed_links_only")}
- </ActionButton>
- <ActionButton
- variant="destructive"
- loading={isRecrawlPending}
- onClick={() =>
- recrawlLinks({ crawlStatus: "all", runInference: true })
- }
- >
- {t("admin.actions.recrawl_all_links")}
- </ActionButton>
- <ActionButton
- variant="destructive"
- loading={isRecrawlPending}
- onClick={() =>
- recrawlLinks({ crawlStatus: "all", runInference: false })
- }
- >
- {t("admin.actions.recrawl_all_links")} (
- {t("admin.actions.without_inference")})
- </ActionButton>
- <ActionButton
- variant="destructive"
- loading={isInferencePending}
- onClick={() =>
- reRunInferenceOnAllBookmarks({ taggingStatus: "failure" })
- }
- >
- {t("admin.actions.regenerate_ai_tags_for_failed_bookmarks_only")}
- </ActionButton>
- <ActionButton
- variant="destructive"
- loading={isInferencePending}
- onClick={() => reRunInferenceOnAllBookmarks({ taggingStatus: "all" })}
- >
- {t("admin.actions.regenerate_ai_tags_for_all_bookmarks")}
- </ActionButton>
- <ActionButton
- variant="destructive"
- loading={isReindexPending}
- onClick={() => reindexBookmarks()}
- >
- {t("admin.actions.reindex_all_bookmarks")}
- </ActionButton>
- <ActionButton
- variant="destructive"
- loading={isReprocessingPending}
- onClick={() => reprocessAssetsFixMode()}
- >
- {t("admin.actions.reprocess_assets_fix_mode")}
- </ActionButton>
- <ActionButton
- variant="destructive"
- loading={isTidyAssetsPending}
- onClick={() => tidyAssets()}
- >
- {t("admin.actions.compact_assets")}
- </ActionButton>
- </div>
- </div>
- );
-}
diff --git a/apps/web/components/admin/BackgroundJobs.tsx b/apps/web/components/admin/BackgroundJobs.tsx
new file mode 100644
index 00000000..217e2ad9
--- /dev/null
+++ b/apps/web/components/admin/BackgroundJobs.tsx
@@ -0,0 +1,257 @@
+"use client";
+
+import { ActionButton } from "@/components/ui/action-button";
+import { toast } from "@/components/ui/use-toast";
+import { useTranslation } from "@/lib/i18n/client";
+import { api } from "@/lib/trpc";
+import { keepPreviousData } from "@tanstack/react-query";
+
+import LoadingSpinner from "../ui/spinner";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "../ui/table";
+
+function AdminActions() {
+ const { t } = useTranslation();
+ const { mutate: recrawlLinks, isPending: isRecrawlPending } =
+ api.admin.recrawlLinks.useMutation({
+ onSuccess: () => {
+ toast({
+ description: "Recrawl enqueued",
+ });
+ },
+ onError: (e) => {
+ toast({
+ variant: "destructive",
+ description: e.message,
+ });
+ },
+ });
+
+ const { mutate: reindexBookmarks, isPending: isReindexPending } =
+ api.admin.reindexAllBookmarks.useMutation({
+ onSuccess: () => {
+ toast({
+ description: "Reindex enqueued",
+ });
+ },
+ onError: (e) => {
+ toast({
+ variant: "destructive",
+ description: e.message,
+ });
+ },
+ });
+
+ const { mutate: reprocessAssetsFixMode, isPending: isReprocessingPending } =
+ api.admin.reprocessAssetsFixMode.useMutation({
+ onSuccess: () => {
+ toast({
+ description: "Reprocessing enqueued",
+ });
+ },
+ onError: (e) => {
+ toast({
+ variant: "destructive",
+ description: e.message,
+ });
+ },
+ });
+
+ const {
+ mutate: reRunInferenceOnAllBookmarks,
+ isPending: isInferencePending,
+ } = api.admin.reRunInferenceOnAllBookmarks.useMutation({
+ onSuccess: () => {
+ toast({
+ description: "Inference jobs enqueued",
+ });
+ },
+ onError: (e) => {
+ toast({
+ variant: "destructive",
+ description: e.message,
+ });
+ },
+ });
+
+ const { mutateAsync: tidyAssets, isPending: isTidyAssetsPending } =
+ api.admin.tidyAssets.useMutation({
+ onSuccess: () => {
+ toast({
+ description: "Tidy assets request has been enqueued!",
+ });
+ },
+ onError: (e) => {
+ toast({
+ variant: "destructive",
+ description: e.message,
+ });
+ },
+ });
+
+ return (
+ <div className="flex flex-col gap-2 sm:w-1/2">
+ <ActionButton
+ variant="destructive"
+ loading={isRecrawlPending}
+ onClick={() =>
+ recrawlLinks({ crawlStatus: "failure", runInference: true })
+ }
+ >
+ {t("admin.actions.recrawl_failed_links_only")}
+ </ActionButton>
+ <ActionButton
+ variant="destructive"
+ loading={isRecrawlPending}
+ onClick={() => recrawlLinks({ crawlStatus: "all", runInference: true })}
+ >
+ {t("admin.actions.recrawl_all_links")}
+ </ActionButton>
+ <ActionButton
+ variant="destructive"
+ loading={isRecrawlPending}
+ onClick={() =>
+ recrawlLinks({ crawlStatus: "all", runInference: false })
+ }
+ >
+ {t("admin.actions.recrawl_all_links")} (
+ {t("admin.actions.without_inference")})
+ </ActionButton>
+ <ActionButton
+ variant="destructive"
+ loading={isInferencePending}
+ onClick={() =>
+ reRunInferenceOnAllBookmarks({ taggingStatus: "failure" })
+ }
+ >
+ {t("admin.actions.regenerate_ai_tags_for_failed_bookmarks_only")}
+ </ActionButton>
+ <ActionButton
+ variant="destructive"
+ loading={isInferencePending}
+ onClick={() => reRunInferenceOnAllBookmarks({ taggingStatus: "all" })}
+ >
+ {t("admin.actions.regenerate_ai_tags_for_all_bookmarks")}
+ </ActionButton>
+ <ActionButton
+ variant="destructive"
+ loading={isReindexPending}
+ onClick={() => reindexBookmarks()}
+ >
+ {t("admin.actions.reindex_all_bookmarks")}
+ </ActionButton>
+ <ActionButton
+ variant="destructive"
+ loading={isReprocessingPending}
+ onClick={() => reprocessAssetsFixMode()}
+ >
+ {t("admin.actions.reprocess_assets_fix_mode")}
+ </ActionButton>
+ <ActionButton
+ variant="destructive"
+ loading={isTidyAssetsPending}
+ onClick={() => tidyAssets()}
+ >
+ {t("admin.actions.compact_assets")}
+ </ActionButton>
+ </div>
+ );
+}
+
+export default function BackgroundJobs() {
+ const { t } = useTranslation();
+ const { data: serverStats } = api.admin.backgroundJobsStats.useQuery(
+ undefined,
+ {
+ refetchInterval: 1000,
+ placeholderData: keepPreviousData,
+ },
+ );
+
+ if (!serverStats) {
+ return <LoadingSpinner />;
+ }
+
+ return (
+ <div className="flex flex-col gap-4">
+ <div className="mb-2 text-xl font-medium">
+ {t("admin.background_jobs.background_jobs")}
+ </div>
+ <div className="sm:w-1/2">
+ <Table className="rounded-md border">
+ <TableHeader className="bg-gray-200">
+ <TableHead>{t("admin.background_jobs.job")}</TableHead>
+ <TableHead>{t("admin.background_jobs.queued")}</TableHead>
+ <TableHead>{t("admin.background_jobs.pending")}</TableHead>
+ <TableHead>{t("admin.background_jobs.failed")}</TableHead>
+ </TableHeader>
+ <TableBody>
+ <TableRow>
+ <TableCell className="lg:w-2/3">
+ {t("admin.background_jobs.crawler_jobs")}
+ </TableCell>
+ <TableCell>{serverStats.crawlStats.queued}</TableCell>
+ <TableCell>{serverStats.crawlStats.pending}</TableCell>
+ <TableCell>{serverStats.crawlStats.failed}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell>{t("admin.background_jobs.indexing_jobs")}</TableCell>
+ <TableCell>{serverStats.indexingStats.queued}</TableCell>
+ <TableCell>-</TableCell>
+ <TableCell>-</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell>{t("admin.background_jobs.inference_jobs")}</TableCell>
+ <TableCell>{serverStats.inferenceStats.queued}</TableCell>
+ <TableCell>{serverStats.inferenceStats.pending}</TableCell>
+ <TableCell>{serverStats.inferenceStats.failed}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell>
+ {t("admin.background_jobs.tidy_assets_jobs")}
+ </TableCell>
+ <TableCell>{serverStats.tidyAssetsStats.queued}</TableCell>
+ <TableCell>-</TableCell>
+ <TableCell>-</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell>{t("admin.background_jobs.video_jobs")}</TableCell>
+ <TableCell>{serverStats.videoStats.queued}</TableCell>
+ <TableCell>-</TableCell>
+ <TableCell>-</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell>{t("admin.background_jobs.webhook_jobs")}</TableCell>
+ <TableCell>{serverStats.webhookStats.queued}</TableCell>
+ <TableCell>-</TableCell>
+ <TableCell>-</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell>
+ {t("admin.background_jobs.asset_preprocessing_jobs")}
+ </TableCell>
+ <TableCell>
+ {serverStats.assetPreprocessingStats.queued}
+ </TableCell>
+ <TableCell>-</TableCell>
+ <TableCell>-</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell>{t("admin.background_jobs.feed_jobs")}</TableCell>
+ <TableCell>{serverStats.feedStats.queued}</TableCell>
+ <TableCell>-</TableCell>
+ <TableCell>-</TableCell>
+ </TableRow>
+ </TableBody>
+ </Table>
+ </div>
+ <AdminActions />
+ </div>
+ );
+}
diff --git a/apps/web/components/admin/ServerStats.tsx b/apps/web/components/admin/ServerStats.tsx
index ded2c19f..0842ac77 100644
--- a/apps/web/components/admin/ServerStats.tsx
+++ b/apps/web/components/admin/ServerStats.tsx
@@ -1,14 +1,6 @@
"use client";
import LoadingSpinner from "@/components/ui/spinner";
-import {
- Table,
- TableBody,
- TableCell,
- TableHead,
- TableHeader,
- TableRow,
-} from "@/components/ui/table";
import { useClientConfig } from "@/lib/clientConfig";
import { useTranslation } from "@/lib/i18n/client";
import { api } from "@/lib/trpc";
@@ -64,7 +56,7 @@ function ReleaseInfo() {
export default function ServerStats() {
const { t } = useTranslation();
const { data: serverStats } = api.admin.stats.useQuery(undefined, {
- refetchInterval: 1000,
+ refetchInterval: 5000,
placeholderData: keepPreviousData,
});
@@ -99,78 +91,6 @@ export default function ServerStats() {
<ReleaseInfo />
</div>
</div>
-
- <div className="sm:w-1/2">
- <div className="mb-2 mt-8 text-xl font-medium">
- {t("admin.background_jobs.background_jobs")}
- </div>
- <Table className="rounded-md border">
- <TableHeader className="bg-gray-200">
- <TableHead>{t("admin.background_jobs.job")}</TableHead>
- <TableHead>{t("admin.background_jobs.queued")}</TableHead>
- <TableHead>{t("admin.background_jobs.pending")}</TableHead>
- <TableHead>{t("admin.background_jobs.failed")}</TableHead>
- </TableHeader>
- <TableBody>
- <TableRow>
- <TableCell className="lg:w-2/3">
- {t("admin.background_jobs.crawler_jobs")}
- </TableCell>
- <TableCell>{serverStats.crawlStats.queued}</TableCell>
- <TableCell>{serverStats.crawlStats.pending}</TableCell>
- <TableCell>{serverStats.crawlStats.failed}</TableCell>
- </TableRow>
- <TableRow>
- <TableCell>{t("admin.background_jobs.indexing_jobs")}</TableCell>
- <TableCell>{serverStats.indexingStats.queued}</TableCell>
- <TableCell>-</TableCell>
- <TableCell>-</TableCell>
- </TableRow>
- <TableRow>
- <TableCell>{t("admin.background_jobs.inference_jobs")}</TableCell>
- <TableCell>{serverStats.inferenceStats.queued}</TableCell>
- <TableCell>{serverStats.inferenceStats.pending}</TableCell>
- <TableCell>{serverStats.inferenceStats.failed}</TableCell>
- </TableRow>
- <TableRow>
- <TableCell>
- {t("admin.background_jobs.tidy_assets_jobs")}
- </TableCell>
- <TableCell>{serverStats.tidyAssetsStats.queued}</TableCell>
- <TableCell>-</TableCell>
- <TableCell>-</TableCell>
- </TableRow>
- <TableRow>
- <TableCell>{t("admin.background_jobs.video_jobs")}</TableCell>
- <TableCell>{serverStats.videoStats.queued}</TableCell>
- <TableCell>-</TableCell>
- <TableCell>-</TableCell>
- </TableRow>
- <TableRow>
- <TableCell>{t("admin.background_jobs.webhook_jobs")}</TableCell>
- <TableCell>{serverStats.webhookStats.queued}</TableCell>
- <TableCell>-</TableCell>
- <TableCell>-</TableCell>
- </TableRow>
- <TableRow>
- <TableCell>
- {t("admin.background_jobs.asset_preprocessing_jobs")}
- </TableCell>
- <TableCell>
- {serverStats.assetPreprocessingStats.queued}
- </TableCell>
- <TableCell>-</TableCell>
- <TableCell>-</TableCell>
- </TableRow>
- <TableRow>
- <TableCell>{t("admin.background_jobs.feed_jobs")}</TableCell>
- <TableCell>{serverStats.feedStats.queued}</TableCell>
- <TableCell>-</TableCell>
- <TableCell>-</TableCell>
- </TableRow>
- </TableBody>
- </Table>
- </div>
</div>
);
}
diff --git a/apps/web/lib/i18n/locales/en/translation.json b/apps/web/lib/i18n/locales/en/translation.json
index 5ba4882c..72210678 100644
--- a/apps/web/lib/i18n/locales/en/translation.json
+++ b/apps/web/lib/i18n/locales/en/translation.json
@@ -177,10 +177,10 @@
"indexing_jobs": "Indexing Jobs",
"inference_jobs": "Inference Jobs",
"tidy_assets_jobs": "Tidy Assets Jobs",
- "video_jobs": "Video Jobs",
+ "video_jobs": "Video Donwload Jobs",
"webhook_jobs": "Webhook Jobs",
"asset_preprocessing_jobs": "Asset Preprocessing Jobs",
- "feed_jobs": "Feed Jobs",
+ "feed_jobs": "RSS Feed Jobs",
"job": "Job",
"queued": "Queued",
"pending": "Pending",