Skip to content

Commit

Permalink
experiment: use request handler for update indicator (#1640)
Browse files Browse the repository at this point in the history
  • Loading branch information
manuel-rw committed Dec 13, 2024
1 parent 8a088db commit 170d290
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 61 deletions.
2 changes: 1 addition & 1 deletion apps/nextjs/src/components/layout/header/user.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const UserButton = async () => {
return (
<UserAvatarMenu availableUpdates={isAdmin ? data : undefined}>
<UnstyledButton>
<Indicator disabled={!data || data.length === 0 || !isAdmin} size={15} processing withBorder>
<Indicator disabled={data.length === 0 || !isAdmin} size={15} processing withBorder>
<CurrentUserAvatar size="md" />
</Indicator>
</UnstyledButton>
Expand Down
2 changes: 1 addition & 1 deletion apps/nextjs/src/components/user-avatar-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { CurrentLanguageCombobox } from "./language/current-language-combobox";

interface UserAvatarMenuProps {
children: ReactNode;
availableUpdates: RouterOutputs["updateChecker"]["getAvailableUpdates"];
availableUpdates?: RouterOutputs["updateChecker"]["getAvailableUpdates"];
}

export const UserAvatarMenu = ({ children, availableUpdates }: UserAvatarMenuProps) => {
Expand Down
14 changes: 4 additions & 10 deletions packages/api/src/router/update-checker.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import { createSubPubChannel } from "@homarr/redis";
import { updateCheckerRequestHandler } from "@homarr/request-handler/update-checker";

import { createTRPCRouter, protectedProcedure } from "../trpc";

export const updateCheckerRouter = createTRPCRouter({
getAvailableUpdates: protectedProcedure.query(async () => {
const channel = createSubPubChannel<{
availableUpdates: { name: string | null; contentHtml?: string; url: string; tag_name: string }[];
}>("homarr:update", {
persist: true,
});

const data = await channel.getLastDataAsync();

return data?.availableUpdates;
const handler = updateCheckerRequestHandler.handler({});
const data = await handler.getCachedOrUpdatedDataAsync({});
return data.data.availableUpdates;
}),
});
53 changes: 4 additions & 49 deletions packages/cron-jobs/src/jobs/update-checker.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,13 @@
import { Octokit } from "octokit";
import { compareSemVer, isValidSemVer } from "semver-parser";

import { EVERY_HOUR } from "@homarr/cron-jobs-core/expressions";
import { logger } from "@homarr/log";
import { createSubPubChannel } from "@homarr/redis";
import { updateCheckerRequestHandler } from "@homarr/request-handler/update-checker";

import packageJson from "../../../../package.json";
import { createCronJob } from "../lib";

export const updateCheckerJob = createCronJob("updateChecker", EVERY_HOUR, {
runOnStart: true,
}).withCallback(async () => {
const octokit = new Octokit();
const releases = await octokit.rest.repos.listReleases({
owner: "homarr-labs",
repo: "homarr",
});

const currentVersion = (packageJson as { version: string }).version;
const availableReleases = [];

for (const release of releases.data) {
if (!isValidSemVer(release.tag_name)) {
logger.warn(`Unable to parse semantic tag '${release.tag_name}'. Update check might not work.`);
continue;
}

availableReleases.push(release);
}

const availableNewerReleases = availableReleases
.filter((release) => compareSemVer(release.tag_name, currentVersion) > 0)
.sort((releaseA, releaseB) => compareSemVer(releaseB.tag_name, releaseA.tag_name));
if (availableReleases.length > 0) {
logger.info(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
`Update checker found a new available version: ${availableReleases[0]!.tag_name}. Current version is ${currentVersion}`,
);
} else {
logger.debug(`Update checker did not find any available updates. Current version is ${currentVersion}`);
}

const channel = createSubPubChannel<{
availableUpdates: { name: string | null; contentHtml?: string; url: string; tag_name: string }[];
}>("homarr:update", {
persist: true,
});

await channel.publishAsync({
availableUpdates: availableNewerReleases.map((release) => ({
name: release.name,
contentHtml: release.body_html,
url: release.html_url,
tag_name: release.tag_name,
})),
const handler = updateCheckerRequestHandler.handler({});
await handler.getCachedOrUpdatedDataAsync({
forceUpdate: true,
});
});
59 changes: 59 additions & 0 deletions packages/request-handler/src/update-checker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import dayjs from "dayjs";
import { Octokit } from "octokit";
import { compareSemVer, isValidSemVer } from "semver-parser";

import { logger } from "@homarr/log";
import { createChannelWithLatestAndEvents } from "@homarr/redis";
import { createCachedRequestHandler } from "@homarr/request-handler/lib/cached-request-handler";

import packageJson from "../../../package.json";

export const updateCheckerRequestHandler = createCachedRequestHandler({
queryKey: "",
cacheDuration: dayjs.duration(1, "hour"),
async requestAsync(_) {
const octokit = new Octokit();
const releases = await octokit.rest.repos.listReleases({
owner: "homarr-labs",
repo: "homarr",
});

const currentVersion = (packageJson as { version: string }).version;
const availableReleases = [];

for (const release of releases.data) {
if (!isValidSemVer(release.tag_name)) {
logger.warn(`Unable to parse semantic tag '${release.tag_name}'. Update check might not work.`);
continue;
}

availableReleases.push(release);
}

const availableNewerReleases = availableReleases
.filter((release) => compareSemVer(release.tag_name, currentVersion) > 0)
.sort((releaseA, releaseB) => compareSemVer(releaseB.tag_name, releaseA.tag_name));
if (availableReleases.length > 0) {
logger.info(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
`Update checker found a new available version: ${availableReleases[0]!.tag_name}. Current version is ${currentVersion}`,
);
} else {
logger.debug(`Update checker did not find any available updates. Current version is ${currentVersion}`);
}

return {
availableUpdates: availableNewerReleases.map((release) => ({
name: release.name,
contentHtml: release.body_html,
url: release.html_url,
tag_name: release.tag_name,
})),
};
},
createRedisChannel() {
return createChannelWithLatestAndEvents<{
availableUpdates: { name: string | null; contentHtml?: string; url: string; tag_name: string }[];
}>("homarr:update");
},
});

0 comments on commit 170d290

Please sign in to comment.