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

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,