diff --git a/server/db/ios_models.json b/server/db/ios_models.json new file mode 100644 index 00000000..99fcbea1 --- /dev/null +++ b/server/db/ios_models.json @@ -0,0 +1,150 @@ +{ + "iPad1,1": "iPad", + "iPad2,1": "iPad 2", + "iPad2,2": "iPad 2", + "iPad2,3": "iPad 2", + "iPad2,4": "iPad 2", + "iPad3,1": "iPad 3rd Gen", + "iPad3,3": "iPad 3rd Gen", + "iPad3,2": "iPad 3rd Gen", + "iPad3,4": "iPad 4th Gen", + "iPad3,5": "iPad 4th Gen", + "iPad3,6": "iPad 4th Gen", + "iPad6,11": "iPad 9.7 5th Gen", + "iPad6,12": "iPad 9.7 5th Gen", + "iPad7,5": "iPad 9.7 6th Gen", + "iPad7,6": "iPad 9.7 6th Gen", + "iPad7,11": "iPad 10.2 7th Gen", + "iPad7,12": "iPad 10.2 7th Gen", + "iPad11,6": "iPad 10.2 8th Gen", + "iPad11,7": "iPad 10.2 8th Gen", + "iPad12,1": "iPad 10.2 9th Gen", + "iPad12,2": "iPad 10.2 9th Gen", + "iPad13,18": "iPad 10.9 10th Gen", + "iPad13,19": "iPad 10.9 10th Gen", + "iPad4,1": "iPad Air", + "iPad4,2": "iPad Air", + "iPad4,3": "iPad Air", + "iPad5,3": "iPad Air 2", + "iPad5,4": "iPad Air 2", + "iPad11,3": "iPad Air 3rd Gen", + "iPad11,4": "iPad Air 3rd Gen", + "iPad13,1": "iPad Air 4th Gen", + "iPad13,2": "iPad Air 4th Gen", + "iPad13,16": "iPad Air 5th Gen", + "iPad13,17": "iPad Air 5th Gen", + "iPad14,8": "iPad Air M2 11", + "iPad14,9": "iPad Air M2 11", + "iPad14,10": "iPad Air M2 13", + "iPad14,11": "iPad Air M2 13", + "iPad2,5": "iPad mini", + "iPad2,6": "iPad mini", + "iPad2,7": "iPad mini", + "iPad4,4": "iPad mini 2", + "iPad4,5": "iPad mini 2", + "iPad4,6": "iPad mini 2", + "iPad4,7": "iPad mini 3", + "iPad4,8": "iPad mini 3", + "iPad4,9": "iPad mini 3", + "iPad5,1": "iPad mini 4", + "iPad5,2": "iPad mini 4", + "iPad11,1": "iPad mini 5th Gen", + "iPad11,2": "iPad mini 5th Gen", + "iPad14,1": "iPad mini 6th Gen", + "iPad14,2": "iPad mini 6th Gen", + "iPad6,7": "iPad Pro 12.9", + "iPad6,8": "iPad Pro 12.9", + "iPad6,3": "iPad Pro 9.7", + "iPad6,4": "iPad Pro 9.7", + "iPad7,3": "iPad Pro 10.5", + "iPad7,4": "iPad Pro 10.5", + "iPad7,1": "iPad Pro 12.9", + "iPad7,2": "iPad Pro 12.9", + "iPad8,1": "iPad Pro 11", + "iPad8,2": "iPad Pro 11", + "iPad8,3": "iPad Pro 11", + "iPad8,4": "iPad Pro 11", + "iPad8,5": "iPad Pro 12.9", + "iPad8,6": "iPad Pro 12.9", + "iPad8,7": "iPad Pro 12.9", + "iPad8,8": "iPad Pro 12.9", + "iPad8,9": "iPad Pro 11", + "iPad8,10": "iPad Pro 11", + "iPad8,11": "iPad Pro 12.9", + "iPad8,12": "iPad Pro 12.9", + "iPad13,4": "iPad Pro 11", + "iPad13,5": "iPad Pro 11", + "iPad13,6": "iPad Pro 11", + "iPad13,7": "iPad Pro 11", + "iPad13,8": "iPad Pro 12.9", + "iPad13,9": "iPad Pro 12.9", + "iPad13,10": "iPad Pro 12.9", + "iPad13,11": "iPad Pro 12.9", + "iPad14,3": "iPad Pro 11", + "iPad14,4": "iPad Pro 11", + "iPad14,5": "iPad Pro 12.9", + "iPad14,6": "iPad Pro 12.9", + "iPad16,3": "iPad Pro M4 11", + "iPad16,4": "iPad Pro M4 11", + "iPad16,5": "iPad Pro M4 13", + "iPad16,6": "iPad Pro M4 13", + "iPhone1,1": "iPhone", + "iPhone1,2": "iPhone 3G", + "iPhone2,1": "iPhone 3GS", + "iPhone3,1": "iPhone 4", + "iPhone3,2": "iPhone 4", + "iPhone3,3": "iPhone 4", + "iPhone4,1": "iPhone 4S", + "iPhone5,1": "iPhone 5", + "iPhone5,2": "iPhone 5", + "iPhone5,3": "iPhone 5c", + "iPhone5,4": "iPhone 5c", + "iPhone6,1": "iPhone 5s", + "iPhone6,2": "iPhone 5s", + "iPhone7,2": "iPhone 6", + "iPhone7,1": "iPhone 6 Plus", + "iPhone8,1": "iPhone 6s", + "iPhone8,2": "iPhone 6s Plus", + "iPhone8,4": "iPhone SE", + "iPhone9,1": "iPhone 7", + "iPhone9,3": "iPhone 7", + "iPhone9,2": "iPhone 7 Plus", + "iPhone9,4": "iPhone 7 Plus", + "iPhone10,1": "iPhone 8", + "iPhone10,4": "iPhone 8", + "iPhone10,2": "iPhone 8 Plus", + "iPhone10,5": "iPhone 8 Plus", + "iPhone10,3": "iPhone X", + "iPhone10,6": "iPhone X", + "iPhone11,2": "iPhone Xs", + "iPhone11,6": "iPhone Xs Max", + "iPhone11,8": "iPhone XR", + "iPhone12,1": "iPhone 11", + "iPhone12,3": "iPhone 11 Pro", + "iPhone12,5": "iPhone 11 Pro Max", + "iPhone12,8": "iPhone SE", + "iPhone13,1": "iPhone 12 mini", + "iPhone13,2": "iPhone 12", + "iPhone13,3": "iPhone 12 Pro", + "iPhone13,4": "iPhone 12 Pro Max", + "iPhone14,4": "iPhone 13 mini", + "iPhone14,5": "iPhone 13", + "iPhone14,2": "iPhone 13 Pro", + "iPhone14,3": "iPhone 13 Pro Max", + "iPhone14,6": "iPhone SE", + "iPhone14,7": "iPhone 14", + "iPhone14,8": "iPhone 14 Plus", + "iPhone15,2": "iPhone 14 Pro", + "iPhone15,3": "iPhone 14 Pro Max", + "iPhone15,4": "iPhone 15", + "iPhone15,5": "iPhone 15 Plus", + "iPhone16,1": "iPhone 15 Pro", + "iPhone16,2": "iPhone 15 Pro Max", + "iPod1,1": "iPod touch Original", + "iPod2,1": "iPod touch 2nd", + "iPod3,1": "iPod touch 3rd Gen", + "iPod4,1": "iPod touch 4th", + "iPod5,1": "iPod touch 5th", + "iPod7,1": "iPod touch 6th Gen", + "iPod9,1": "iPod touch 7th Gen" +} \ No newline at end of file diff --git a/server/db/mac_models.json b/server/db/mac_models.json new file mode 100644 index 00000000..db473f3a --- /dev/null +++ b/server/db/mac_models.json @@ -0,0 +1,201 @@ +{ + "PowerMac4,4": "eMac", + "PowerMac6,4": "eMac", + "PowerBook2,1": "iBook", + "PowerBook2,2": "iBook", + "PowerBook4,1": "iBook", + "PowerBook4,2": "iBook", + "PowerBook4,3": "iBook", + "PowerBook6,3": "iBook", + "PowerBook6,5": "iBook", + "PowerBook6,7": "iBook", + "iMac,1": "iMac", + "PowerMac2,1": "iMac", + "PowerMac2,2": "iMac", + "PowerMac4,1": "iMac", + "PowerMac4,2": "iMac", + "PowerMac4,5": "iMac", + "PowerMac6,1": "iMac", + "PowerMac6,3*": "iMac", + "PowerMac6,3": "iMac", + "PowerMac8,1": "iMac", + "PowerMac8,2": "iMac", + "PowerMac12,1": "iMac", + "iMac4,1": "iMac", + "iMac4,2": "iMac", + "iMac5,2": "iMac", + "iMac5,1": "iMac", + "iMac6,1": "iMac", + "iMac7,1": "iMac", + "iMac8,1": "iMac", + "iMac9,1": "iMac", + "iMac10,1": "iMac", + "iMac11,1": "iMac", + "iMac11,2": "iMac", + "iMac11,3": "iMac", + "iMac12,1": "iMac", + "iMac12,2": "iMac", + "iMac13,1": "iMac", + "iMac13,2": "iMac", + "iMac14,1": "iMac", + "iMac14,3": "iMac", + "iMac14,2": "iMac", + "iMac14,4": "iMac", + "iMac15,1": "iMac", + "iMac16,1": "iMac", + "iMac16,2": "iMac", + "iMac17,1": "iMac", + "iMac18,1": "iMac", + "iMac18,2": "iMac", + "iMac18,3": "iMac", + "iMac19,2": "iMac", + "iMac19,1": "iMac", + "iMac20,1": "iMac", + "iMac20,2": "iMac", + "iMac21,2": "iMac", + "iMac21,1": "iMac", + "iMacPro1,1": "iMac Pro", + "PowerMac10,1": "Mac mini", + "PowerMac10,2": "Mac mini", + "Macmini1,1": "Mac mini", + "Macmini2,1": "Mac mini", + "Macmini3,1": "Mac mini", + "Macmini4,1": "Mac mini", + "Macmini5,1": "Mac mini", + "Macmini5,2": "Mac mini", + "Macmini5,3": "Mac mini", + "Macmini6,1": "Mac mini", + "Macmini6,2": "Mac mini", + "Macmini7,1": "Mac mini", + "Macmini8,1": "Mac mini", + "ADP3,2": "Mac mini", + "Macmini9,1": "Mac mini", + "Mac14,3": "Mac mini", + "Mac14,12": "Mac mini", + "MacPro1,1*": "Mac Pro", + "MacPro2,1": "Mac Pro", + "MacPro3,1": "Mac Pro", + "MacPro4,1": "Mac Pro", + "MacPro5,1": "Mac Pro", + "MacPro6,1": "Mac Pro", + "MacPro7,1": "Mac Pro", + "N/A*": "Power Macintosh", + "PowerMac1,1": "Power Macintosh", + "PowerMac3,1": "Power Macintosh", + "PowerMac3,3": "Power Macintosh", + "PowerMac3,4": "Power Macintosh", + "PowerMac3,5": "Power Macintosh", + "PowerMac3,6": "Power Macintosh", + "Mac13,1": "Mac Studio", + "Mac13,2": "Mac Studio", + "MacBook1,1": "MacBook", + "MacBook2,1": "MacBook", + "MacBook3,1": "MacBook", + "MacBook4,1": "MacBook", + "MacBook5,1": "MacBook", + "MacBook5,2": "MacBook", + "MacBook6,1": "MacBook", + "MacBook7,1": "MacBook", + "MacBook8,1": "MacBook", + "MacBook9,1": "MacBook", + "MacBook10,1": "MacBook", + "MacBookAir1,1": "MacBook Air", + "MacBookAir2,1": "MacBook Air", + "MacBookAir3,1": "MacBook Air", + "MacBookAir3,2": "MacBook Air", + "MacBookAir4,1": "MacBook Air", + "MacBookAir4,2": "MacBook Air", + "MacBookAir5,1": "MacBook Air", + "MacBookAir5,2": "MacBook Air", + "MacBookAir6,1": "MacBook Air", + "MacBookAir6,2": "MacBook Air", + "MacBookAir7,1": "MacBook Air", + "MacBookAir7,2": "MacBook Air", + "MacBookAir8,1": "MacBook Air", + "MacBookAir8,2": "MacBook Air", + "MacBookAir9,1": "MacBook Air", + "MacBookAir10,1": "MacBook Air", + "Mac14,2": "MacBook Air", + "MacBookPro1,1": "MacBook Pro", + "MacBookPro1,2": "MacBook Pro", + "MacBookPro2,2": "MacBook Pro", + "MacBookPro2,1": "MacBook Pro", + "MacBookPro3,1": "MacBook Pro", + "MacBookPro4,1": "MacBook Pro", + "MacBookPro5,1": "MacBook Pro", + "MacBookPro5,2": "MacBook Pro", + "MacBookPro5,5": "MacBook Pro", + "MacBookPro5,4": "MacBook Pro", + "MacBookPro5,3": "MacBook Pro", + "MacBookPro7,1": "MacBook Pro", + "MacBookPro6,2": "MacBook Pro", + "MacBookPro6,1": "MacBook Pro", + "MacBookPro8,1": "MacBook Pro", + "MacBookPro8,2": "MacBook Pro", + "MacBookPro8,3": "MacBook Pro", + "MacBookPro9,2": "MacBook Pro", + "MacBookPro9,1": "MacBook Pro", + "MacBookPro10,1": "MacBook Pro", + "MacBookPro10,2": "MacBook Pro", + "MacBookPro11,1": "MacBook Pro", + "MacBookPro11,2": "MacBook Pro", + "MacBookPro11,3": "MacBook Pro", + "MacBookPro12,1": "MacBook Pro", + "MacBookPro11,4": "MacBook Pro", + "MacBookPro11,5": "MacBook Pro", + "MacBookPro13,1": "MacBook Pro", + "MacBookPro13,2": "MacBook Pro", + "MacBookPro13,3": "MacBook Pro", + "MacBookPro14,1": "MacBook Pro", + "MacBookPro14,2": "MacBook Pro", + "MacBookPro14,3": "MacBook Pro", + "MacBookPro15,2": "MacBook Pro", + "MacBookPro15,1": "MacBook Pro", + "MacBookPro15,3": "MacBook Pro", + "MacBookPro15,4": "MacBook Pro", + "MacBookPro16,1": "MacBook Pro", + "MacBookPro16,3": "MacBook Pro", + "MacBookPro16,2": "MacBook Pro", + "MacBookPro16,4": "MacBook Pro", + "MacBookPro17,1": "MacBook Pro", + "MacBookPro18,3": "MacBook Pro", + "MacBookPro18,4": "MacBook Pro", + "MacBookPro18,1": "MacBook Pro", + "MacBookPro18,2": "MacBook Pro", + "Mac14,7": "MacBook Pro", + "Mac14,9": "MacBook Pro", + "Mac14,5": "MacBook Pro", + "Mac14,10": "MacBook Pro", + "Mac14,6": "MacBook Pro", + "PowerMac1,2": "Power Macintosh", + "PowerMac5,1": "Power Macintosh", + "PowerMac7,2": "Power Macintosh", + "PowerMac7,3": "Power Macintosh", + "PowerMac9,1": "Power Macintosh", + "PowerMac11,2": "Power Macintosh", + "PowerBook1,1": "PowerBook", + "PowerBook3,1": "PowerBook", + "PowerBook3,2": "PowerBook", + "PowerBook3,3": "PowerBook", + "PowerBook3,4": "PowerBook", + "PowerBook3,5": "PowerBook", + "PowerBook6,1": "PowerBook", + "PowerBook5,1": "PowerBook", + "PowerBook6,2": "PowerBook", + "PowerBook5,2": "PowerBook", + "PowerBook5,3": "PowerBook", + "PowerBook6,4": "PowerBook", + "PowerBook5,4": "PowerBook", + "PowerBook5,5": "PowerBook", + "PowerBook6,8": "PowerBook", + "PowerBook5,6": "PowerBook", + "PowerBook5,7": "PowerBook", + "PowerBook5,8": "PowerBook", + "PowerBook5,9": "PowerBook", + "RackMac1,1": "Xserve", + "RackMac1,2": "Xserve", + "RackMac3,1": "Xserve", + "Xserve1,1": "Xserve", + "Xserve2,1": "Xserve", + "Xserve3,1": "Xserve" +} \ No newline at end of file diff --git a/server/db/names.ts b/server/db/names.ts index 32b0a393..6f9e1230 100644 --- a/server/db/names.ts +++ b/server/db/names.ts @@ -16,6 +16,24 @@ if (!dev) { } export const names = JSON.parse(readFileSync(file, "utf-8")); +// Load iOS and Mac model mappings +let iosModelsFile: string; +let macModelsFile: string; +if (!dev) { + iosModelsFile = join(__DIRNAME, "ios_models.json"); + macModelsFile = join(__DIRNAME, "mac_models.json"); +} else { + iosModelsFile = join("server/db/ios_models.json"); + macModelsFile = join("server/db/mac_models.json"); +} + +const iosModels: Record = JSON.parse( + readFileSync(iosModelsFile, "utf-8") +); +const macModels: Record = JSON.parse( + readFileSync(macModelsFile, "utf-8") +); + export async function getUniqueClientName(orgId: string): Promise { let loops = 0; while (true) { @@ -159,3 +177,29 @@ export function generateName(): string { // clean out any non-alphanumeric characters except for dashes return name.replace(/[^a-z0-9-]/g, ""); } + +export function getMacDeviceName(macIdentifier?: string | null): string | null { + if (macIdentifier && macModels[macIdentifier]) { + return macModels[macIdentifier]; + } + return null; +} + +export function getIosDeviceName(iosIdentifier?: string | null): string | null { + if (iosIdentifier && iosModels[iosIdentifier]) { + return iosModels[iosIdentifier]; + } + return null; +} + +export function getUserDeviceName( + model: string | null, + fallBack: string | null +): string { + return ( + getMacDeviceName(model) || + getIosDeviceName(model) || + fallBack || + "Unknown Device" + ); +} diff --git a/server/routers/auth/verifyDeviceWebAuth.ts b/server/routers/auth/verifyDeviceWebAuth.ts index be0e0ff2..756749f5 100644 --- a/server/routers/auth/verifyDeviceWebAuth.ts +++ b/server/routers/auth/verifyDeviceWebAuth.ts @@ -10,6 +10,7 @@ import { eq, and, gt } from "drizzle-orm"; import { encodeHexLowerCase } from "@oslojs/encoding"; import { sha256 } from "@oslojs/crypto/sha2"; import { unauthorized } from "@server/auth/unauthorizedResponse"; +import { getIosDeviceName, getMacDeviceName } from "@server/db/names"; const bodySchema = z .object({ @@ -120,6 +121,11 @@ export async function verifyDeviceWebAuth( ); } + const deviceName = + getMacDeviceName(deviceCode.deviceName) || + getIosDeviceName(deviceCode.deviceName) || + deviceCode.deviceName; + // If verify is false, just return metadata without verifying if (!verify) { return response(res, { @@ -129,7 +135,7 @@ export async function verifyDeviceWebAuth( metadata: { ip: deviceCode.ip, city: deviceCode.city, - deviceName: deviceCode.deviceName, + deviceName: deviceName, applicationName: deviceCode.applicationName, createdAt: deviceCode.createdAt } diff --git a/server/routers/client/getClient.ts b/server/routers/client/getClient.ts index f054ce80..709eb1a5 100644 --- a/server/routers/client/getClient.ts +++ b/server/routers/client/getClient.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db, olms } from "@server/db"; -import { clients } from "@server/db"; +import { clients, fingerprints } from "@server/db"; import { eq, and } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; @@ -10,6 +10,7 @@ import logger from "@server/logger"; import stoi from "@server/lib/stoi"; import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; +import { getUserDeviceName } from "@server/db/names"; const getClientSchema = z.strictObject({ clientId: z @@ -29,6 +30,7 @@ async function query(clientId?: number, niceId?: string, orgId?: string) { .from(clients) .where(eq(clients.clientId, clientId)) .leftJoin(olms, eq(clients.clientId, olms.clientId)) + .leftJoin(fingerprints, eq(olms.olmId, fingerprints.olmId)) .limit(1); return res; } else if (niceId && orgId) { @@ -37,6 +39,7 @@ async function query(clientId?: number, niceId?: string, orgId?: string) { .from(clients) .where(and(eq(clients.niceId, niceId), eq(clients.orgId, orgId))) .leftJoin(olms, eq(clients.clientId, olms.clientId)) + .leftJoin(fingerprints, eq(olms.olmId, fingerprints.olmId)) .limit(1); return res; } @@ -105,8 +108,16 @@ export async function getClient( ); } + // Replace name with device name if OLM exists + let clientName = client.clients.name; + if (client.olms) { + const model = client.fingerprints?.deviceModel || null; + clientName = getUserDeviceName(model, client.clients.name); + } + const data: GetClientResponse = { ...client.clients, + name: clientName, olmId: client.olms ? client.olms.olmId : null }; diff --git a/server/routers/client/listClients.ts b/server/routers/client/listClients.ts index 18bc3e38..a157c674 100644 --- a/server/routers/client/listClients.ts +++ b/server/routers/client/listClients.ts @@ -5,7 +5,8 @@ import { roleClients, sites, userClients, - clientSitesAssociationsCache + clientSitesAssociationsCache, + fingerprints } from "@server/db"; import logger from "@server/logger"; import HttpCode from "@server/types/HttpCode"; @@ -27,6 +28,7 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; import NodeCache from "node-cache"; import semver from "semver"; +import { getUserDeviceName } from "@server/db/names"; const olmVersionCache = new NodeCache({ stdTTL: 3600 }); @@ -139,12 +141,14 @@ function queryClients( agent: olms.agent, olmArchived: olms.archived, archived: clients.archived, - blocked: clients.blocked + blocked: clients.blocked, + deviceModel: fingerprints.deviceModel }) .from(clients) .leftJoin(orgs, eq(clients.orgId, orgs.orgId)) .leftJoin(olms, eq(clients.clientId, olms.clientId)) .leftJoin(users, eq(clients.userId, users.userId)) + .leftJoin(fingerprints, eq(olms.olmId, fingerprints.olmId)) .where(and(...conditions)); } @@ -163,21 +167,22 @@ async function getSiteAssociations(clientIds: number[]) { .where(inArray(clientSitesAssociationsCache.clientId, clientIds)); } -type OlmWithUpdateAvailable = Awaited>[0] & { +type ClientWithSites = Omit< + Awaited>[0], + "deviceModel" +> & { + sites: Array<{ + siteId: number; + siteName: string | null; + siteNiceId: string | null; + }>; olmUpdateAvailable?: boolean; }; +type OlmWithUpdateAvailable = ClientWithSites; + export type ListClientsResponse = { - clients: Array< - Awaited>[0] & { - sites: Array<{ - siteId: number; - siteName: string | null; - siteNiceId: string | null; - }>; - olmUpdateAvailable?: boolean; - } - >; + clients: Array; pagination: { total: number; limit: number; offset: number }; }; @@ -307,11 +312,17 @@ export async function listClients( > ); - // Merge clients with their site associations - const clientsWithSites = clientsList.map((client) => ({ - ...client, - sites: sitesByClient[client.clientId] || [] - })); + // Merge clients with their site associations and replace name with device name + const clientsWithSites = clientsList.map((client) => { + const model = client.deviceModel || null; + const newName = getUserDeviceName(model, client.name); + const { deviceModel, ...clientWithoutDeviceModel } = client; + return { + ...clientWithoutDeviceModel, + name: newName, + sites: sitesByClient[client.clientId] || [] + }; + }); const latestOlVersionPromise = getLatestOlmVersion(); @@ -350,7 +361,7 @@ export async function listClients( return response(res, { data: { - clients: clientsWithSites, + clients: olmsWithUpdates, pagination: { total: totalCount, limit, diff --git a/server/routers/olm/getUserOlm.ts b/server/routers/olm/getUserOlm.ts index 03fa04bc..dc0bfde3 100644 --- a/server/routers/olm/getUserOlm.ts +++ b/server/routers/olm/getUserOlm.ts @@ -1,6 +1,6 @@ import { NextFunction, Request, Response } from "express"; import { db } from "@server/db"; -import { olms, clients } from "@server/db"; +import { olms, clients, fingerprints } from "@server/db"; import { eq, and } from "drizzle-orm"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; @@ -9,6 +9,7 @@ import { z } from "zod"; import { fromError } from "zod-validation-error"; import logger from "@server/logger"; import { OpenAPITags, registry } from "@server/openApi"; +import { getUserDeviceName } from "@server/db/names"; const paramsSchema = z .object({ @@ -61,12 +62,14 @@ export async function getUserOlm( const { olmId, userId } = parsedParams.data; const { orgId } = parsedQuery.data; - const [olm] = await db + const [result] = await db .select() .from(olms) - .where(and(eq(olms.userId, userId), eq(olms.olmId, olmId))); + .where(and(eq(olms.userId, userId), eq(olms.olmId, olmId))) + .leftJoin(fingerprints, eq(olms.olmId, fingerprints.olmId)) + .limit(1); - if (!olm) { + if (!result || !result.olms) { return next( createHttpError( HttpCode.NOT_FOUND, @@ -75,6 +78,8 @@ export async function getUserOlm( ); } + const olm = result.olms; + // If orgId is provided and olm has a clientId, fetch the client to check blocked status let blocked: boolean | undefined; if (orgId && olm.clientId) { @@ -92,9 +97,13 @@ export async function getUserOlm( blocked = client?.blocked ?? false; } + // Replace name with device name + const model = result.fingerprints?.deviceModel || null; + const newName = getUserDeviceName(model, olm.name); + const responseData = blocked !== undefined - ? { ...olm, blocked } - : olm; + ? { ...olm, name: newName, blocked } + : { ...olm, name: newName }; return response(res, { data: responseData, diff --git a/server/routers/olm/listUserOlms.ts b/server/routers/olm/listUserOlms.ts index 16585e9f..e8398798 100644 --- a/server/routers/olm/listUserOlms.ts +++ b/server/routers/olm/listUserOlms.ts @@ -1,5 +1,5 @@ import { NextFunction, Request, Response } from "express"; -import { db } from "@server/db"; +import { db, fingerprints } from "@server/db"; import { olms } from "@server/db"; import { eq, count, desc } from "drizzle-orm"; import HttpCode from "@server/types/HttpCode"; @@ -9,6 +9,7 @@ import { z } from "zod"; import { fromError } from "zod-validation-error"; import logger from "@server/logger"; import { OpenAPITags, registry } from "@server/openApi"; +import { getUserDeviceName } from "@server/db/names"; const querySchema = z.object({ limit: z @@ -99,22 +100,30 @@ export async function listUserOlms( const total = totalCountResult?.count || 0; // Get OLMs for the current user (including archived OLMs) - const userOlms = await db - .select({ - olmId: olms.olmId, - dateCreated: olms.dateCreated, - version: olms.version, - name: olms.name, - clientId: olms.clientId, - userId: olms.userId, - archived: olms.archived - }) + const list = await db + .select() .from(olms) .where(eq(olms.userId, userId)) + .leftJoin(fingerprints, eq(olms.olmId, fingerprints.olmId)) .orderBy(desc(olms.dateCreated)) .limit(limit) .offset(offset); + const userOlms = list.map((item) => { + const model = item.fingerprints?.deviceModel || null; + const newName = getUserDeviceName(model, item.olms.name); + + return { + olmId: item.olms.olmId, + dateCreated: item.olms.dateCreated, + version: item.olms.version, + name: newName, + clientId: item.olms.clientId, + userId: item.olms.userId, + archived: item.olms.archived + }; + }); + return response(res, { data: { olms: userOlms, diff --git a/src/components/DeviceLoginForm.tsx b/src/components/DeviceLoginForm.tsx index c6972d56..914b43b0 100644 --- a/src/components/DeviceLoginForm.tsx +++ b/src/components/DeviceLoginForm.tsx @@ -159,7 +159,7 @@ export default function DeviceLoginForm({ const cleanedInitialCode = initialCode.replace(/-/g, "").toUpperCase(); if (cleanedInitialCode && cleanedInitialCode.length === 8) { setValidatingInitialCode(true); - validateCode(cleanedInitialCode, true).finally(() => { + validateCode(cleanedInitialCode, false).finally(() => { setValidatingInitialCode(false); }); }