aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2024-04-02 16:42:53 +0100
committerMohamedBassem <me@mbassem.com>2024-04-02 16:42:53 +0100
commitc206aa254c39175812c0e1d0e012a6a841ea5d50 (patch)
tree26fd1b2b33528b33b735fdcd0d2a3e93ed39c887
parentd02fa5056a07dee699cd4b3868c2ffb673c133c2 (diff)
downloadkarakeep-c206aa254c39175812c0e1d0e012a6a841ea5d50.tar.zst
feature: Include server version in the admin UI. Fixes #66
-rw-r--r--.github/workflows/docker.yml2
-rw-r--r--apps/cli/index.ts2
-rw-r--r--apps/web/app/dashboard/admin/page.tsx55
-rw-r--r--apps/web/lib/clientConfig.tsx2
-rw-r--r--apps/workers/index.ts4
-rw-r--r--docker/Dockerfile9
-rw-r--r--docs/docs/03-configuration.md25
-rw-r--r--packages/shared/config.ts7
8 files changed, 92 insertions, 14 deletions
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index b9e83afa..d6de38fd 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -32,6 +32,7 @@ jobs:
uses: docker/build-push-action@v5
with:
context: .
+ build-args: SERVER_VERSION=nightly
file: docker/Dockerfile
target: ${{ matrix.package }}
platforms: linux/amd64,linux/arm64
@@ -45,6 +46,7 @@ jobs:
if: github.event_name == 'release'
with:
context: .
+ build-args: SERVER_VERSION=${{ github.event.release.name }}
file: docker/Dockerfile
target: ${{ matrix.package }}
platforms: linux/amd64,linux/arm64
diff --git a/apps/cli/index.ts b/apps/cli/index.ts
index 03699e55..4d0adafb 100644
--- a/apps/cli/index.ts
+++ b/apps/cli/index.ts
@@ -19,7 +19,7 @@ const program = new Command()
.makeOptionMandatory(true)
.env("HOARDER_SERVER_ADDR"),
)
- .version("0.1.0");
+ .version(process.env.SERVER_VERSION ?? "nightly");
program.addCommand(bookmarkCmd);
program.addCommand(whoamiCmd);
diff --git a/apps/web/app/dashboard/admin/page.tsx b/apps/web/app/dashboard/admin/page.tsx
index eb80cb03..0db1130f 100644
--- a/apps/web/app/dashboard/admin/page.tsx
+++ b/apps/web/app/dashboard/admin/page.tsx
@@ -13,11 +13,58 @@ import {
TableRow,
} from "@/components/ui/table";
import { toast } from "@/components/ui/use-toast";
+import { useClientConfig } from "@/lib/clientConfig";
import { api } from "@/lib/trpc";
-import { keepPreviousData } from "@tanstack/react-query";
+import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { Trash } from "lucide-react";
import { useSession } from "next-auth/react";
+const REPO_LATEST_RELEASE_API =
+ "https://api.github.com/repos/mohamedbassem/hoarder-app/releases/latest";
+const REPO_RELEASE_PAGE =
+ "https://github.com/MohamedBassem/hoarder-app/releases";
+
+function useLatestRelease() {
+ const { data } = useQuery({
+ queryKey: ["latest-release"],
+ queryFn: async () => {
+ const res = await fetch(REPO_LATEST_RELEASE_API);
+ if (!res.ok) {
+ return undefined;
+ }
+ const data = (await res.json()) as { name: string };
+ return data.name;
+ },
+ staleTime: 60 * 60 * 1000,
+ enabled: !useClientConfig().disableNewReleaseCheck,
+ });
+ return data;
+}
+
+function ReleaseInfo() {
+ const currentRelease = useClientConfig().serverVersion ?? "not set";
+ const latestRelease = useLatestRelease();
+
+ let newRelease;
+ if (latestRelease && currentRelease != latestRelease) {
+ newRelease = (
+ <a
+ href={REPO_RELEASE_PAGE}
+ target="_blank"
+ className="text-blue-500"
+ rel="noreferrer"
+ >
+ (New release available: {latestRelease})
+ </a>
+ );
+ }
+ return (
+ <p className="text-nowrap">
+ {currentRelease} {newRelease}
+ </p>
+ );
+}
+
function ActionsSection() {
const { mutate: recrawlLinks, isPending: isRecrawlPending } =
api.admin.recrawlAllLinks.useMutation({
@@ -95,6 +142,12 @@ function ServerStatsSection() {
<TableCell>Num Bookmarks</TableCell>
<TableCell>{serverStats.numBookmarks}</TableCell>
</TableRow>
+ <TableRow>
+ <TableCell className="w-2/3">Server Version</TableCell>
+ <TableCell>
+ <ReleaseInfo />
+ </TableCell>
+ </TableRow>
</TableBody>
</Table>
<Separator />
diff --git a/apps/web/lib/clientConfig.tsx b/apps/web/lib/clientConfig.tsx
index d63b169c..50e9774d 100644
--- a/apps/web/lib/clientConfig.tsx
+++ b/apps/web/lib/clientConfig.tsx
@@ -7,6 +7,8 @@ export const ClientConfigCtx = createContext<ClientConfig>({
auth: {
disableSignups: false,
},
+ serverVersion: undefined,
+ disableNewReleaseCheck: true,
});
export function useClientConfig() {
diff --git a/apps/workers/index.ts b/apps/workers/index.ts
index 24bdc67b..687d9ced 100644
--- a/apps/workers/index.ts
+++ b/apps/workers/index.ts
@@ -1,11 +1,15 @@
import "dotenv/config";
+import serverConfig from "@hoarder/shared/config";
+import logger from "@hoarder/shared/logger";
+
import { CrawlerWorker } from "./crawlerWorker";
import { shutdownPromise } from "./exit";
import { OpenAiWorker } from "./openaiWorker";
import { SearchIndexingWorker } from "./searchWorker";
async function main() {
+ logger.info(`Workers version: ${serverConfig.serverVersion ?? "not set"}`);
const [crawler, openai, search] = [
await CrawlerWorker.build(),
OpenAiWorker.build(),
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 8551bbc7..948e70ef 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -34,6 +34,9 @@ RUN pnpm next experimental-compile
FROM --platform=$BUILDPLATFORM node:21-alpine AS web
WORKDIR /app
+ARG SERVER_VERSION=nightly
+ENV SERVER_VERSION=${SERVER_VERSION}
+
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
@@ -72,6 +75,9 @@ RUN --mount=type=cache,id=pnpm_workers,target=/pnpm/store pnpm deploy --node-lin
FROM --platform=$BUILDPLATFORM node:21-alpine AS workers
WORKDIR /app
+ARG SERVER_VERSION=nightly
+ENV SERVER_VERSION=${SERVER_VERSION}
+
COPY --from=workers_builder /prod apps/workers
RUN corepack enable
@@ -93,6 +99,9 @@ RUN --mount=type=cache,id=pnpm_cli,target=/pnpm/store pnpm deploy --node-linker=
FROM --platform=$BUILDPLATFORM node:21-alpine AS cli
WORKDIR /app
+ARG SERVER_VERSION=nightly
+ENV SERVER_VERSION=${SERVER_VERSION}
+
COPY --from=cli_builder /prod apps/cli
RUN corepack enable
diff --git a/docs/docs/03-configuration.md b/docs/docs/03-configuration.md
index 1cb1da76..8bf8a069 100644
--- a/docs/docs/03-configuration.md
+++ b/docs/docs/03-configuration.md
@@ -2,18 +2,19 @@
The app is mainly configured by environment variables. All the used environment variables are listed in [packages/shared/config.ts](https://github.com/MohamedBassem/hoarder-app/blob/main/packages/shared/config.ts). The most important ones are:
-| Name | Required | Default | Description |
-| ----------------- | ------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
-| DATA_DIR | Yes | Not set | The path for the persistent data directory. This is where the db and the uploaded assets live. |
-| NEXTAUTH_URL | Yes | Not set | Should point to the address of your server. The app will function without it, but will redirect you to wrong addresses on signout for example. |
-| NEXTAUTH_SECRET | Yes | Not set | Random string used to sign the JWT tokens. Generate one with `openssl rand -base64 36`. |
-| REDIS_HOST | Yes | localhost | The address of redis used by background jobs |
-| REDIS_PORT | Yes | 6379 | The port of redis used by background jobs |
-| REDIS_DB_IDX | No | Not set | The db idx to use with redis. It defaults to 0 (in the client) so you don't usually need to set it unless you explicitly want another db. |
-| MEILI_ADDR | No | Not set | The address of meilisearch. If not set, Search will be disabled. E.g. (`http://meilisearch:7700`) |
-| MEILI_MASTER_KEY | Only in Prod and if search is enabled | Not set | The master key configured for meilisearch. Not needed in development environment. Generate one with `openssl rand -base64 36` |
-| DISABLE_SIGNUPS | No | false | If enabled, no new signups will be allowed and the signup button will be disabled in the UI |
-| MAX_ASSET_SIZE_MB | No | 4 | Sets the maximum allowed asset size (in MB) to be uploaded |
+| Name | Required | Default | Description |
+| ------------------------- | ------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
+| DATA_DIR | Yes | Not set | The path for the persistent data directory. This is where the db and the uploaded assets live. |
+| NEXTAUTH_URL | Yes | Not set | Should point to the address of your server. The app will function without it, but will redirect you to wrong addresses on signout for example. |
+| NEXTAUTH_SECRET | Yes | Not set | Random string used to sign the JWT tokens. Generate one with `openssl rand -base64 36`. |
+| REDIS_HOST | Yes | localhost | The address of redis used by background jobs |
+| REDIS_PORT | Yes | 6379 | The port of redis used by background jobs |
+| REDIS_DB_IDX | No | Not set | The db idx to use with redis. It defaults to 0 (in the client) so you don't usually need to set it unless you explicitly want another db. |
+| MEILI_ADDR | No | Not set | The address of meilisearch. If not set, Search will be disabled. E.g. (`http://meilisearch:7700`) |
+| MEILI_MASTER_KEY | Only in Prod and if search is enabled | Not set | The master key configured for meilisearch. Not needed in development environment. Generate one with `openssl rand -base64 36` |
+| DISABLE_SIGNUPS | No | false | If enabled, no new signups will be allowed and the signup button will be disabled in the UI |
+| MAX_ASSET_SIZE_MB | No | 4 | Sets the maximum allowed asset size (in MB) to be uploaded |
+| DISABLE_NEW_RELEASE_CHECK | No | false | If set to true, latest release check will be disabled in the admin panel. |
## Inference Configs (For automatic tagging)
diff --git a/packages/shared/config.ts b/packages/shared/config.ts
index c1cc371a..11140c3b 100644
--- a/packages/shared/config.ts
+++ b/packages/shared/config.ts
@@ -29,6 +29,9 @@ const allEnv = z.object({
DATA_DIR: z.string().default(""),
MAX_ASSET_SIZE_MB: z.coerce.number().default(4),
INFERENCE_LANG: z.string().default("english"),
+ // Build only flag
+ SERVER_VERSION: z.string().optional(),
+ DISABLE_NEW_RELEASE_CHECK: stringBool("false"),
});
const serverConfigSchema = allEnv.transform((val) => {
@@ -69,6 +72,8 @@ const serverConfigSchema = allEnv.transform((val) => {
: undefined,
dataDir: val.DATA_DIR,
maxAssetSizeMb: val.MAX_ASSET_SIZE_MB,
+ serverVersion: val.SERVER_VERSION,
+ disableNewReleaseCheck: val.DISABLE_NEW_RELEASE_CHECK,
};
});
@@ -79,6 +84,8 @@ export const clientConfig = {
auth: {
disableSignups: serverConfig.auth.disableSignups,
},
+ serverVersion: serverConfig.serverVersion,
+ disableNewReleaseCheck: serverConfig.disableNewReleaseCheck,
};
export type ClientConfig = typeof clientConfig;