mirror of
https://github.com/fosrl/pangolin.git
synced 2026-10-23 21:19:07 +02:00
Add olm agentVersion to separate the library from the agent
This commit is contained in:
@@ -1488,6 +1488,7 @@ export const olms = pgTable(
|
|||||||
dateCreated: varchar("dateCreated").notNull(),
|
dateCreated: varchar("dateCreated").notNull(),
|
||||||
version: text("version"),
|
version: text("version"),
|
||||||
agent: text("agent"),
|
agent: text("agent"),
|
||||||
|
agentVersion: text("agentVersion"),
|
||||||
name: varchar("name"),
|
name: varchar("name"),
|
||||||
clientId: integer("clientId").references(() => clients.clientId, {
|
clientId: integer("clientId").references(() => clients.clientId, {
|
||||||
// we will switch this depending on the current org it wants to connect to
|
// we will switch this depending on the current org it wants to connect to
|
||||||
|
|||||||
@@ -843,6 +843,7 @@ export const olms = sqliteTable(
|
|||||||
dateCreated: text("dateCreated").notNull(),
|
dateCreated: text("dateCreated").notNull(),
|
||||||
version: text("version"),
|
version: text("version"),
|
||||||
agent: text("agent"),
|
agent: text("agent"),
|
||||||
|
agentVersion: text("agentVersion"),
|
||||||
name: text("name"),
|
name: text("name"),
|
||||||
clientId: integer("clientId").references(() => clients.clientId, {
|
clientId: integer("clientId").references(() => clients.clientId, {
|
||||||
// we will switch this depending on the current org it wants to connect to
|
// we will switch this depending on the current org it wants to connect to
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
import semver from "semver";
|
import semver from "semver";
|
||||||
|
|
||||||
|
// NEWT AND OLM
|
||||||
|
// Pass the newt/olm library version (newts.version / olms.version), not
|
||||||
|
// agentVersion: the agent may be an app embedding the library (the CLI,
|
||||||
|
// Pangolin Windows, ...) whose version is unrelated to the protocol support.
|
||||||
export function canCompress(
|
export function canCompress(
|
||||||
clientVersion: string | null | undefined,
|
clientVersion: string | null | undefined,
|
||||||
type: "newt" | "olm"
|
agent: "newt" | "olm"
|
||||||
): boolean {
|
): boolean {
|
||||||
try {
|
try {
|
||||||
if (!clientVersion) return false;
|
if (!clientVersion) return false;
|
||||||
// check if it is a valid semver
|
// check if it is a valid semver
|
||||||
if (!semver.valid(clientVersion)) return false;
|
if (!semver.valid(clientVersion)) return false;
|
||||||
if (type === "newt") {
|
if (agent === "newt") {
|
||||||
return semver.gte(clientVersion, "1.10.3");
|
return semver.gte(clientVersion, "1.10.3");
|
||||||
} else if (type === "olm") {
|
} else if (agent === "olm") {
|
||||||
return semver.gte(clientVersion, "1.4.3");
|
return semver.gte(clientVersion, "1.4.3");
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -23,6 +27,7 @@ export function canCompress(
|
|||||||
// sync message's `certs` array, instead of requiring each target to carry
|
// sync message's `certs` array, instead of requiring each target to carry
|
||||||
// its own inline `tlsCert`/`tlsKey` PEM data. Bump the version floor here to
|
// its own inline `tlsCert`/`tlsKey` PEM data. Bump the version floor here to
|
||||||
// match whatever release first ships the newt-side support.
|
// match whatever release first ships the newt-side support.
|
||||||
|
// NEWT
|
||||||
export function supportsCertReferences(
|
export function supportsCertReferences(
|
||||||
clientVersion: string | null | undefined
|
clientVersion: string | null | undefined
|
||||||
): boolean {
|
): boolean {
|
||||||
@@ -38,6 +43,7 @@ export function supportsCertReferences(
|
|||||||
// Whether this newt client understands newt/wg/client-identities/add, used to
|
// Whether this newt client understands newt/wg/client-identities/add, used to
|
||||||
// set Remote-* headers on requests through its HTTP proxy. Bump the version
|
// set Remote-* headers on requests through its HTTP proxy. Bump the version
|
||||||
// floor here to match whatever release first ships the newt-side support.
|
// floor here to match whatever release first ships the newt-side support.
|
||||||
|
// NEWT
|
||||||
export function supportsClientIdentities(
|
export function supportsClientIdentities(
|
||||||
clientVersion: string | null | undefined
|
clientVersion: string | null | undefined
|
||||||
): boolean {
|
): boolean {
|
||||||
@@ -49,3 +55,33 @@ export function supportsClientIdentities(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Whether this newt client understands the v2 subnet proxy target format.
|
||||||
|
// Older clients need targets converted back to v1 before they are sent.
|
||||||
|
// NEWT
|
||||||
|
export function supportsTargetsV2(
|
||||||
|
clientVersion: string | null | undefined
|
||||||
|
): boolean {
|
||||||
|
try {
|
||||||
|
if (!clientVersion) return false;
|
||||||
|
if (!semver.valid(clientVersion)) return false;
|
||||||
|
return semver.gte(clientVersion, "1.10.3");
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether this newt client understands the sync message sent when its config
|
||||||
|
// version falls behind the server's.
|
||||||
|
// NEWT
|
||||||
|
export function supportsSync(
|
||||||
|
clientVersion: string | null | undefined
|
||||||
|
): boolean {
|
||||||
|
try {
|
||||||
|
if (!clientVersion) return false;
|
||||||
|
if (!semver.valid(clientVersion)) return false;
|
||||||
|
return semver.gte(clientVersion, "1.14.0");
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -228,6 +228,7 @@ export type GetClientResponse = NonNullable<
|
|||||||
olmId: string | null;
|
olmId: string | null;
|
||||||
agent: string | null;
|
agent: string | null;
|
||||||
olmVersion: string | null;
|
olmVersion: string | null;
|
||||||
|
agentVersion: string | null;
|
||||||
userEmail: string | null;
|
userEmail: string | null;
|
||||||
userName: string | null;
|
userName: string | null;
|
||||||
userUsername: string | null;
|
userUsername: string | null;
|
||||||
@@ -421,6 +422,7 @@ export async function getClient(
|
|||||||
olmId: client.olms ? client.olms.olmId : null,
|
olmId: client.olms ? client.olms.olmId : null,
|
||||||
agent: client.olms?.agent || null,
|
agent: client.olms?.agent || null,
|
||||||
olmVersion: client.olms?.version || null,
|
olmVersion: client.olms?.version || null,
|
||||||
|
agentVersion: client.olms?.agentVersion || null,
|
||||||
userEmail: client.user?.email ?? null,
|
userEmail: client.user?.email ?? null,
|
||||||
userName: client.user?.name ?? null,
|
userName: client.user?.name ?? null,
|
||||||
userUsername: client.user?.username ?? null,
|
userUsername: client.user?.username ?? null,
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ function queryClientsBase() {
|
|||||||
userEmail: users.email,
|
userEmail: users.email,
|
||||||
niceId: clients.niceId,
|
niceId: clients.niceId,
|
||||||
agent: olms.agent,
|
agent: olms.agent,
|
||||||
|
agentVersion: olms.agentVersion,
|
||||||
approvalState: clients.approvalState,
|
approvalState: clients.approvalState,
|
||||||
olmArchived: olms.archived,
|
olmArchived: olms.archived,
|
||||||
archived: clients.archived,
|
archived: clients.archived,
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ function queryUserDevicesBase() {
|
|||||||
idpVariant: idpOidcConfig.variant,
|
idpVariant: idpOidcConfig.variant,
|
||||||
niceId: clients.niceId,
|
niceId: clients.niceId,
|
||||||
agent: olms.agent,
|
agent: olms.agent,
|
||||||
|
agentVersion: olms.agentVersion,
|
||||||
approvalState: clients.approvalState,
|
approvalState: clients.approvalState,
|
||||||
olmArchived: olms.archived,
|
olmArchived: olms.archived,
|
||||||
archived: clients.archived,
|
archived: clients.archived,
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ import {
|
|||||||
SubnetProxyTarget,
|
SubnetProxyTarget,
|
||||||
SubnetProxyTargetV2
|
SubnetProxyTargetV2
|
||||||
} from "@server/lib/ip";
|
} from "@server/lib/ip";
|
||||||
import { canCompress } from "@server/lib/clientVersionChecks";
|
import {
|
||||||
|
canCompress,
|
||||||
|
supportsTargetsV2
|
||||||
|
} from "@server/lib/clientVersionChecks";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { eq, inArray } from "drizzle-orm";
|
import { eq, inArray } from "drizzle-orm";
|
||||||
import semver from "semver";
|
|
||||||
|
|
||||||
const NEWT_V2_TARGETS_VERSION = ">=1.10.3";
|
|
||||||
|
|
||||||
export async function convertTargetsIfNecessary(
|
export async function convertTargetsIfNecessary(
|
||||||
newtId: string,
|
newtId: string,
|
||||||
@@ -27,10 +27,7 @@ export async function convertTargetsIfNecessary(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check the semver
|
// check the semver
|
||||||
if (
|
if (newt.version && !supportsTargetsV2(newt.version)) {
|
||||||
newt.version &&
|
|
||||||
!semver.satisfies(newt.version, NEWT_V2_TARGETS_VERSION)
|
|
||||||
) {
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`addTargets Newt version ${newt.version} does not support targets v2 falling back`
|
`addTargets Newt version ${newt.version} does not support targets v2 falling back`
|
||||||
);
|
);
|
||||||
@@ -167,10 +164,7 @@ export async function updateTargets(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check the semver
|
// check the semver
|
||||||
if (
|
if (newt.version && !supportsTargetsV2(newt.version)) {
|
||||||
newt.version &&
|
|
||||||
!semver.satisfies(newt.version, NEWT_V2_TARGETS_VERSION)
|
|
||||||
) {
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`addTargets Newt version ${newt.version} does not support targets v2 falling back`
|
`addTargets Newt version ${newt.version} does not support targets v2 falling back`
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ import { Newt } from "@server/db";
|
|||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { sendNewtSyncMessage } from "./sync";
|
import { sendNewtSyncMessage } from "./sync";
|
||||||
import semver from "semver";
|
|
||||||
import { recordSitePing } from "./pingAccumulator";
|
import { recordSitePing } from "./pingAccumulator";
|
||||||
|
import { supportsSync } from "@server/lib/clientVersionChecks";
|
||||||
|
|
||||||
const NEWT_SUPPORTS_SYNC_VERSION = ">=1.14.0";
|
|
||||||
const PONG = {
|
const PONG = {
|
||||||
message: {
|
message: {
|
||||||
type: "pong",
|
type: "pong",
|
||||||
@@ -50,10 +49,7 @@ export const handleNewtPingMessage: MessageHandler = async (context) => {
|
|||||||
// cross-region latency to the database.
|
// cross-region latency to the database.
|
||||||
recordSitePing(newt.siteId);
|
recordSitePing(newt.siteId);
|
||||||
|
|
||||||
if (
|
if (newt.version && !supportsSync(newt.version)) {
|
||||||
newt.version &&
|
|
||||||
!semver.satisfies(newt.version, NEWT_SUPPORTS_SYNC_VERSION)
|
|
||||||
) {
|
|
||||||
// Newt does not support the sync message so not checking - stop here -
|
// Newt does not support the sync message so not checking - stop here -
|
||||||
return PONG;
|
return PONG;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => {
|
|||||||
relay,
|
relay,
|
||||||
olmVersion,
|
olmVersion,
|
||||||
olmAgent,
|
olmAgent,
|
||||||
|
agentVersion,
|
||||||
orgId,
|
orgId,
|
||||||
userToken,
|
userToken,
|
||||||
pingResults,
|
pingResults,
|
||||||
@@ -92,9 +93,17 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => {
|
|||||||
await handleFingerprintInsertion(olm, fingerprint, postures);
|
await handleFingerprintInsertion(olm, fingerprint, postures);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Olm clients that predate agentVersion report the version of the app
|
||||||
|
// embedding olm (Pangolin Windows, the CLI, ...) in olmVersion, so it
|
||||||
|
// doubles as the agent version. Newer clients report the olm library
|
||||||
|
// version in olmVersion and the app version in agentVersion.
|
||||||
|
const reportedAgentVersion =
|
||||||
|
agentVersion === undefined ? olmVersion : agentVersion;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(olmVersion && olm.version !== olmVersion) ||
|
(olmVersion && olm.version !== olmVersion) ||
|
||||||
(olmAgent && olm.agent !== olmAgent) ||
|
(olmAgent && olm.agent !== olmAgent) ||
|
||||||
|
(reportedAgentVersion && olm.agentVersion !== reportedAgentVersion) ||
|
||||||
olm.archived
|
olm.archived
|
||||||
) {
|
) {
|
||||||
await db
|
await db
|
||||||
@@ -102,6 +111,7 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => {
|
|||||||
.set({
|
.set({
|
||||||
version: olmVersion,
|
version: olmVersion,
|
||||||
agent: olmAgent,
|
agent: olmAgent,
|
||||||
|
agentVersion: reportedAgentVersion,
|
||||||
archived: false
|
archived: false
|
||||||
})
|
})
|
||||||
.where(eq(olms.olmId, olm.olmId));
|
.where(eq(olms.olmId, olm.olmId));
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ export default async function ClientsPage(props: ClientsPageProps) {
|
|||||||
orgId: params.orgId,
|
orgId: params.orgId,
|
||||||
online: client.online,
|
online: client.online,
|
||||||
olmVersion: client.olmVersion || undefined,
|
olmVersion: client.olmVersion || undefined,
|
||||||
|
agentVersion: client.agentVersion || undefined,
|
||||||
olmUpdateAvailable: client.olmUpdateAvailable || false,
|
olmUpdateAvailable: client.olmUpdateAvailable || false,
|
||||||
userId: client.userId,
|
userId: client.userId,
|
||||||
username: client.username,
|
username: client.username,
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ export default async function ClientsPage(props: ClientsPageProps) {
|
|||||||
orgId: params.orgId,
|
orgId: params.orgId,
|
||||||
online: client.online,
|
online: client.online,
|
||||||
olmVersion: client.olmVersion || undefined,
|
olmVersion: client.olmVersion || undefined,
|
||||||
|
agentVersion: client.agentVersion || undefined,
|
||||||
olmUpdateAvailable: Boolean(client.olmUpdateAvailable),
|
olmUpdateAvailable: Boolean(client.olmUpdateAvailable),
|
||||||
userId: client.userId,
|
userId: client.userId,
|
||||||
username: client.username,
|
username: client.username,
|
||||||
|
|||||||
@@ -44,26 +44,29 @@ export default function SiteInfoCard({}: ClientInfoCardProps) {
|
|||||||
const data = useQuery(productUpdatesQueries.latestVersion(true));
|
const data = useQuery(productUpdatesQueries.latestVersion(true));
|
||||||
const latestPlatformVersions = data.data?.data;
|
const latestPlatformVersions = data.data?.data;
|
||||||
|
|
||||||
|
// agentVersion was added after olmVersion, and older olm clients report
|
||||||
|
// the app version in olmVersion, so fall back to it for clients that have
|
||||||
|
// not re-registered yet.
|
||||||
|
const agentVersion = client.agentVersion ?? client.olmVersion;
|
||||||
|
|
||||||
let updateAvailable = false;
|
let updateAvailable = false;
|
||||||
if (client.agent && client.olmVersion && latestPlatformVersions) {
|
if (client.agent && agentVersion && latestPlatformVersions) {
|
||||||
const agent = agentVersionMap[
|
const agent = agentVersionMap[
|
||||||
client.agent
|
client.agent
|
||||||
] as keyof LatestVersionResponse;
|
] as keyof LatestVersionResponse;
|
||||||
|
|
||||||
if (agent in latestPlatformVersions) {
|
if (agent in latestPlatformVersions) {
|
||||||
const agentVersion = latestPlatformVersions[agent];
|
const latestAgentVersion = latestPlatformVersions[agent];
|
||||||
|
|
||||||
updateAvailable = Boolean(
|
updateAvailable = Boolean(
|
||||||
semver.valid(client.olmVersion) &&
|
semver.valid(agentVersion) &&
|
||||||
semver.lt(client.olmVersion, agentVersion.latestVersion)
|
semver.lt(agentVersion, latestAgentVersion.latestVersion)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cols =
|
const cols =
|
||||||
2 +
|
2 + (userDisplayName ? 1 : 0) + (client.agent && agentVersion ? 1 : 0);
|
||||||
(userDisplayName ? 1 : 0) +
|
|
||||||
(client.agent && client.olmVersion ? 1 : 0);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Alert>
|
<Alert>
|
||||||
@@ -73,15 +76,13 @@ export default function SiteInfoCard({}: ClientInfoCardProps) {
|
|||||||
<InfoSectionTitle>{t("name")}</InfoSectionTitle>
|
<InfoSectionTitle>{t("name")}</InfoSectionTitle>
|
||||||
<InfoSectionContent>{client.name}</InfoSectionContent>
|
<InfoSectionContent>{client.name}</InfoSectionContent>
|
||||||
</InfoSection>
|
</InfoSection>
|
||||||
{client.agent && client.olmVersion ? (
|
{client.agent && agentVersion ? (
|
||||||
<InfoSection>
|
<InfoSection>
|
||||||
<InfoSectionTitle>{t("agent")}</InfoSectionTitle>
|
<InfoSectionTitle>{t("agent")}</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span>
|
<span>
|
||||||
{client.agent +
|
{client.agent + " v" + agentVersion}
|
||||||
" v" +
|
|
||||||
client.olmVersion}
|
|
||||||
</span>
|
</span>
|
||||||
{updateAvailable && (
|
{updateAvailable && (
|
||||||
<InfoPopup
|
<InfoPopup
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ export type ClientRow = {
|
|||||||
orgId: string;
|
orgId: string;
|
||||||
online: boolean;
|
online: boolean;
|
||||||
olmVersion?: string;
|
olmVersion?: string;
|
||||||
|
agentVersion?: string;
|
||||||
olmUpdateAvailable: boolean;
|
olmUpdateAvailable: boolean;
|
||||||
userId: string | null;
|
userId: string | null;
|
||||||
username: string | null;
|
username: string | null;
|
||||||
@@ -397,10 +398,16 @@ export default function MachineClientsTable({
|
|||||||
"Olm CLI": "olm"
|
"Olm CLI": "olm"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// agentVersion was added after olmVersion, and older olm
|
||||||
|
// clients report the app version in olmVersion, so fall
|
||||||
|
// back to it for rows that have not re-registered yet.
|
||||||
|
const displayedVersion =
|
||||||
|
originalRow.agentVersion ?? originalRow.olmVersion;
|
||||||
|
|
||||||
let updateAvailable = false;
|
let updateAvailable = false;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
originalRow.olmVersion &&
|
displayedVersion &&
|
||||||
originalRow.agent &&
|
originalRow.agent &&
|
||||||
latestPlatformVersions
|
latestPlatformVersions
|
||||||
) {
|
) {
|
||||||
@@ -409,13 +416,14 @@ export default function MachineClientsTable({
|
|||||||
] as keyof LatestVersionResponse;
|
] as keyof LatestVersionResponse;
|
||||||
|
|
||||||
if (agent in latestPlatformVersions) {
|
if (agent in latestPlatformVersions) {
|
||||||
const agentVersion = latestPlatformVersions[agent];
|
const latestAgentVersion =
|
||||||
|
latestPlatformVersions[agent];
|
||||||
|
|
||||||
updateAvailable = Boolean(
|
updateAvailable = Boolean(
|
||||||
semver.valid(originalRow.olmVersion) &&
|
semver.valid(displayedVersion) &&
|
||||||
semver.lt(
|
semver.lt(
|
||||||
originalRow.olmVersion,
|
displayedVersion,
|
||||||
agentVersion.latestVersion
|
latestAgentVersion.latestVersion
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -423,11 +431,11 @@ export default function MachineClientsTable({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
{originalRow.agent && originalRow.olmVersion ? (
|
{originalRow.agent && displayedVersion ? (
|
||||||
<Badge variant="secondary">
|
<Badge variant="secondary">
|
||||||
{originalRow.agent +
|
{originalRow.agent +
|
||||||
" v" +
|
" v" +
|
||||||
originalRow.olmVersion}
|
displayedVersion}
|
||||||
</Badge>
|
</Badge>
|
||||||
) : (
|
) : (
|
||||||
"-"
|
"-"
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export type ClientRow = {
|
|||||||
orgId: string;
|
orgId: string;
|
||||||
online: boolean;
|
online: boolean;
|
||||||
olmVersion?: string;
|
olmVersion?: string;
|
||||||
|
agentVersion?: string;
|
||||||
olmUpdateAvailable: boolean;
|
olmUpdateAvailable: boolean;
|
||||||
userId: string | null;
|
userId: string | null;
|
||||||
username: string | null;
|
username: string | null;
|
||||||
@@ -578,10 +579,16 @@ export default function UserDevicesTable({
|
|||||||
"Olm CLI": "olm"
|
"Olm CLI": "olm"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// agentVersion was added after olmVersion, and older olm
|
||||||
|
// clients report the app version in olmVersion, so fall
|
||||||
|
// back to it for rows that have not re-registered yet.
|
||||||
|
const displayedVersion =
|
||||||
|
originalRow.agentVersion ?? originalRow.olmVersion;
|
||||||
|
|
||||||
let updateAvailable = false;
|
let updateAvailable = false;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
originalRow.olmVersion &&
|
displayedVersion &&
|
||||||
originalRow.agent &&
|
originalRow.agent &&
|
||||||
latestPlatformVersions
|
latestPlatformVersions
|
||||||
) {
|
) {
|
||||||
@@ -590,13 +597,14 @@ export default function UserDevicesTable({
|
|||||||
] as keyof LatestVersionResponse;
|
] as keyof LatestVersionResponse;
|
||||||
|
|
||||||
if (agent in latestPlatformVersions) {
|
if (agent in latestPlatformVersions) {
|
||||||
const agentVersion = latestPlatformVersions[agent];
|
const latestAgentVersion =
|
||||||
|
latestPlatformVersions[agent];
|
||||||
|
|
||||||
updateAvailable = Boolean(
|
updateAvailable = Boolean(
|
||||||
semver.valid(originalRow.olmVersion) &&
|
semver.valid(displayedVersion) &&
|
||||||
semver.lt(
|
semver.lt(
|
||||||
originalRow.olmVersion,
|
displayedVersion,
|
||||||
agentVersion.latestVersion
|
latestAgentVersion.latestVersion
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -604,11 +612,11 @@ export default function UserDevicesTable({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
{originalRow.agent && originalRow.olmVersion ? (
|
{originalRow.agent && displayedVersion ? (
|
||||||
<Badge variant="secondary">
|
<Badge variant="secondary">
|
||||||
{originalRow.agent +
|
{originalRow.agent +
|
||||||
" v" +
|
" v" +
|
||||||
originalRow.olmVersion}
|
displayedVersion}
|
||||||
</Badge>
|
</Badge>
|
||||||
) : (
|
) : (
|
||||||
"-"
|
"-"
|
||||||
|
|||||||
Reference in New Issue
Block a user