add pretty apple device names

This commit is contained in:
miloschwartz
2026-01-15 17:59:45 -08:00
parent 6c8757f230
commit ff5e12655f
9 changed files with 480 additions and 39 deletions

150
server/db/ios_models.json Normal file
View File

@@ -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"
}

201
server/db/mac_models.json Normal file
View File

@@ -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"
}

View File

@@ -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<string, string> = JSON.parse(
readFileSync(iosModelsFile, "utf-8")
);
const macModels: Record<string, string> = JSON.parse(
readFileSync(macModelsFile, "utf-8")
);
export async function getUniqueClientName(orgId: string): Promise<string> {
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"
);
}

View File

@@ -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<VerifyDeviceWebAuthResponse>(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
}

View File

@@ -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
};

View File

@@ -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<ReturnType<typeof queryClients>>[0] & {
type ClientWithSites = Omit<
Awaited<ReturnType<typeof queryClients>>[0],
"deviceModel"
> & {
sites: Array<{
siteId: number;
siteName: string | null;
siteNiceId: string | null;
}>;
olmUpdateAvailable?: boolean;
};
type OlmWithUpdateAvailable = ClientWithSites;
export type ListClientsResponse = {
clients: Array<
Awaited<ReturnType<typeof queryClients>>[0] & {
sites: Array<{
siteId: number;
siteName: string | null;
siteNiceId: string | null;
}>;
olmUpdateAvailable?: boolean;
}
>;
clients: Array<ClientWithSites>;
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<ListClientsResponse>(res, {
data: {
clients: clientsWithSites,
clients: olmsWithUpdates,
pagination: {
total: totalCount,
limit,

View File

@@ -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,

View File

@@ -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<ListUserOlmsResponse>(res, {
data: {
olms: userOlms,

View File

@@ -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);
});
}