refactor(fingerprint): start taking fingerprint snapshots in new table

This commit is contained in:
Varun Narravula
2026-01-20 06:48:40 -08:00
committed by Owen Schwartz
parent adf3d0347b
commit 1f077d7ec2
11 changed files with 307 additions and 131 deletions

View File

@@ -780,7 +780,7 @@ export const olms = pgTable("olms", {
archived: boolean("archived").notNull().default(false) archived: boolean("archived").notNull().default(false)
}); });
export const fingerprints = pgTable("fingerprints", { export const currentFingerprint = pgTable("currentFingerprint", {
fingerprintId: serial("id").primaryKey(), fingerprintId: serial("id").primaryKey(),
olmId: text("olmId") olmId: text("olmId")
@@ -792,7 +792,7 @@ export const fingerprints = pgTable("fingerprints", {
username: text("username"), username: text("username"),
hostname: text("hostname"), hostname: text("hostname"),
platform: text("platform"), // macos | windows | linux | ios | android | unknown platform: text("platform"),
osVersion: text("osVersion"), osVersion: text("osVersion"),
kernelVersion: text("kernelVersion"), kernelVersion: text("kernelVersion"),
arch: text("arch"), arch: text("arch"),
@@ -801,6 +801,29 @@ export const fingerprints = pgTable("fingerprints", {
platformFingerprint: varchar("platformFingerprint") platformFingerprint: varchar("platformFingerprint")
}); });
export const fingerprintSnapshots = pgTable("fingerprintSnapshots", {
snapshotId: serial("id").primaryKey(),
fingerprintId: integer("fingerprintId")
.references(() => currentFingerprint.fingerprintId, {
onDelete: "cascade"
})
.notNull(),
username: text("username"),
hostname: text("hostname"),
platform: text("platform"),
osVersion: text("osVersion"),
kernelVersion: text("kernelVersion"),
arch: text("arch"),
deviceModel: text("deviceModel"),
serialNumber: text("serialNumber"),
platformFingerprint: varchar("platformFingerprint"),
hash: text("hash").notNull(),
collectedAt: integer("collectedAt").notNull()
});
export const olmSessions = pgTable("clientSession", { export const olmSessions = pgTable("clientSession", {
sessionId: varchar("id").primaryKey(), sessionId: varchar("id").primaryKey(),
olmId: varchar("olmId") olmId: varchar("olmId")

View File

@@ -497,7 +497,7 @@ export const olms = sqliteTable("olms", {
archived: integer("archived", { mode: "boolean" }).notNull().default(false) archived: integer("archived", { mode: "boolean" }).notNull().default(false)
}); });
export const fingerprints = sqliteTable("fingerprints", { export const currentFingerprint = sqliteTable("currentFingerprint", {
fingerprintId: integer("id").primaryKey({ autoIncrement: true }), fingerprintId: integer("id").primaryKey({ autoIncrement: true }),
olmId: text("olmId") olmId: text("olmId")
@@ -509,7 +509,7 @@ export const fingerprints = sqliteTable("fingerprints", {
username: text("username"), username: text("username"),
hostname: text("hostname"), hostname: text("hostname"),
platform: text("platform"), // macos | windows | linux | ios | android | unknown platform: text("platform"),
osVersion: text("osVersion"), osVersion: text("osVersion"),
kernelVersion: text("kernelVersion"), kernelVersion: text("kernelVersion"),
arch: text("arch"), arch: text("arch"),
@@ -518,6 +518,29 @@ export const fingerprints = sqliteTable("fingerprints", {
platformFingerprint: text("platformFingerprint") platformFingerprint: text("platformFingerprint")
}); });
export const fingerprintSnapshots = sqliteTable("fingerprintSnapshots", {
snapshotId: integer("id").primaryKey({ autoIncrement: true }),
fingerprintId: integer("fingerprintId")
.references(() => currentFingerprint.fingerprintId, {
onDelete: "cascade"
})
.notNull(),
username: text("username"),
hostname: text("hostname"),
platform: text("platform"),
osVersion: text("osVersion"),
kernelVersion: text("kernelVersion"),
arch: text("arch"),
deviceModel: text("deviceModel"),
serialNumber: text("serialNumber"),
platformFingerprint: text("platformFingerprint"),
hash: text("hash").notNull(),
collectedAt: integer("collectedAt").notNull()
});
export const twoFactorBackupCodes = sqliteTable("twoFactorBackupCodes", { export const twoFactorBackupCodes = sqliteTable("twoFactorBackupCodes", {
codeId: integer("id").primaryKey({ autoIncrement: true }), codeId: integer("id").primaryKey({ autoIncrement: true }),
userId: text("userId") userId: text("userId")

View File

@@ -21,7 +21,15 @@ import type { Request, Response, NextFunction } from "express";
import { build } from "@server/build"; import { build } from "@server/build";
import { getOrgTierData } from "@server/lib/billing"; import { getOrgTierData } from "@server/lib/billing";
import { TierId } from "@server/lib/billing/tiers"; import { TierId } from "@server/lib/billing/tiers";
import { approvals, clients, db, users, olms, fingerprints, type Approval } from "@server/db"; import {
approvals,
clients,
db,
users,
olms,
currentFingerprint,
type Approval
} from "@server/db";
import { eq, isNull, sql, not, and, desc } from "drizzle-orm"; import { eq, isNull, sql, not, and, desc } from "drizzle-orm";
import response from "@server/lib/response"; import response from "@server/lib/response";
import { getUserDeviceName } from "@server/db/names"; import { getUserDeviceName } from "@server/db/names";
@@ -92,14 +100,14 @@ async function queryApprovals(
}, },
clientName: clients.name, clientName: clients.name,
niceId: clients.niceId, niceId: clients.niceId,
deviceModel: fingerprints.deviceModel, deviceModel: currentFingerprint.deviceModel,
fingerprintPlatform: fingerprints.platform, fingerprintPlatform: currentFingerprint.platform,
fingerprintOsVersion: fingerprints.osVersion, fingerprintOsVersion: currentFingerprint.osVersion,
fingerprintKernelVersion: fingerprints.kernelVersion, fingerprintKernelVersion: currentFingerprint.kernelVersion,
fingerprintArch: fingerprints.arch, fingerprintArch: currentFingerprint.arch,
fingerprintSerialNumber: fingerprints.serialNumber, fingerprintSerialNumber: currentFingerprint.serialNumber,
fingerprintUsername: fingerprints.username, fingerprintUsername: currentFingerprint.username,
fingerprintHostname: fingerprints.hostname fingerprintHostname: currentFingerprint.hostname
}) })
.from(approvals) .from(approvals)
.innerJoin(users, and(eq(approvals.userId, users.userId))) .innerJoin(users, and(eq(approvals.userId, users.userId)))
@@ -111,7 +119,7 @@ async function queryApprovals(
) )
) )
.leftJoin(olms, eq(clients.clientId, olms.clientId)) .leftJoin(olms, eq(clients.clientId, olms.clientId))
.leftJoin(fingerprints, eq(olms.olmId, fingerprints.olmId)) .leftJoin(currentFingerprint, eq(olms.olmId, currentFingerprint.olmId))
.where( .where(
and( and(
eq(approvals.orgId, orgId), eq(approvals.orgId, orgId),
@@ -146,15 +154,15 @@ async function queryApprovals(
const fingerprint = hasFingerprintData const fingerprint = hasFingerprintData
? { ? {
platform: approval.fingerprintPlatform || null, platform: approval.fingerprintPlatform || null,
osVersion: approval.fingerprintOsVersion || null, osVersion: approval.fingerprintOsVersion || null,
kernelVersion: approval.fingerprintKernelVersion || null, kernelVersion: approval.fingerprintKernelVersion || null,
arch: approval.fingerprintArch || null, arch: approval.fingerprintArch || null,
deviceModel: approval.deviceModel || null, deviceModel: approval.deviceModel || null,
serialNumber: approval.fingerprintSerialNumber || null, serialNumber: approval.fingerprintSerialNumber || null,
username: approval.fingerprintUsername || null, username: approval.fingerprintUsername || null,
hostname: approval.fingerprintHostname || null hostname: approval.fingerprintHostname || null
} }
: null; : null;
const { const {

View File

@@ -1,7 +1,7 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db, olms } from "@server/db"; import { db, olms } from "@server/db";
import { clients, fingerprints } from "@server/db"; import { clients, currentFingerprint } from "@server/db";
import { eq, and } from "drizzle-orm"; import { eq, and } from "drizzle-orm";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
@@ -30,7 +30,10 @@ async function query(clientId?: number, niceId?: string, orgId?: string) {
.from(clients) .from(clients)
.where(eq(clients.clientId, clientId)) .where(eq(clients.clientId, clientId))
.leftJoin(olms, eq(clients.clientId, olms.clientId)) .leftJoin(olms, eq(clients.clientId, olms.clientId))
.leftJoin(fingerprints, eq(olms.olmId, fingerprints.olmId)) .leftJoin(
currentFingerprint,
eq(olms.olmId, currentFingerprint.olmId)
)
.limit(1); .limit(1);
return res; return res;
} else if (niceId && orgId) { } else if (niceId && orgId) {
@@ -39,7 +42,10 @@ async function query(clientId?: number, niceId?: string, orgId?: string) {
.from(clients) .from(clients)
.where(and(eq(clients.niceId, niceId), eq(clients.orgId, orgId))) .where(and(eq(clients.niceId, niceId), eq(clients.orgId, orgId)))
.leftJoin(olms, eq(clients.clientId, olms.clientId)) .leftJoin(olms, eq(clients.clientId, olms.clientId))
.leftJoin(fingerprints, eq(olms.olmId, fingerprints.olmId)) .leftJoin(
currentFingerprint,
eq(olms.olmId, currentFingerprint.olmId)
)
.limit(1); .limit(1);
return res; return res;
} }
@@ -125,24 +131,25 @@ export async function getClient(
// Replace name with device name if OLM exists // Replace name with device name if OLM exists
let clientName = client.clients.name; let clientName = client.clients.name;
if (client.olms) { if (client.olms) {
const model = client.fingerprints?.deviceModel || null; const model = client.currentFingerprint?.deviceModel || null;
clientName = getUserDeviceName(model, client.clients.name); clientName = getUserDeviceName(model, client.clients.name);
} }
// Build fingerprint data if available // Build fingerprint data if available
const fingerprintData = client.fingerprints const fingerprintData = client.currentFingerprint
? { ? {
username: client.fingerprints.username || null, username: client.currentFingerprint.username || null,
hostname: client.fingerprints.hostname || null, hostname: client.currentFingerprint.hostname || null,
platform: client.fingerprints.platform || null, platform: client.currentFingerprint.platform || null,
osVersion: client.fingerprints.osVersion || null, osVersion: client.currentFingerprint.osVersion || null,
kernelVersion: client.fingerprints.kernelVersion || null, kernelVersion:
arch: client.fingerprints.arch || null, client.currentFingerprint.kernelVersion || null,
deviceModel: client.fingerprints.deviceModel || null, arch: client.currentFingerprint.arch || null,
serialNumber: client.fingerprints.serialNumber || null, deviceModel: client.currentFingerprint.deviceModel || null,
firstSeen: client.fingerprints.firstSeen || null, serialNumber: client.currentFingerprint.serialNumber || null,
lastSeen: client.fingerprints.lastSeen || null firstSeen: client.currentFingerprint.firstSeen || null,
} lastSeen: client.currentFingerprint.lastSeen || null
}
: null; : null;
const data: GetClientResponse = { const data: GetClientResponse = {

View File

@@ -6,7 +6,7 @@ import {
sites, sites,
userClients, userClients,
clientSitesAssociationsCache, clientSitesAssociationsCache,
fingerprints currentFingerprint
} from "@server/db"; } from "@server/db";
import logger from "@server/logger"; import logger from "@server/logger";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
@@ -143,20 +143,20 @@ function queryClients(
olmArchived: olms.archived, olmArchived: olms.archived,
archived: clients.archived, archived: clients.archived,
blocked: clients.blocked, blocked: clients.blocked,
deviceModel: fingerprints.deviceModel, deviceModel: currentFingerprint.deviceModel,
fingerprintPlatform: fingerprints.platform, fingerprintPlatform: currentFingerprint.platform,
fingerprintOsVersion: fingerprints.osVersion, fingerprintOsVersion: currentFingerprint.osVersion,
fingerprintKernelVersion: fingerprints.kernelVersion, fingerprintKernelVersion: currentFingerprint.kernelVersion,
fingerprintArch: fingerprints.arch, fingerprintArch: currentFingerprint.arch,
fingerprintSerialNumber: fingerprints.serialNumber, fingerprintSerialNumber: currentFingerprint.serialNumber,
fingerprintUsername: fingerprints.username, fingerprintUsername: currentFingerprint.username,
fingerprintHostname: fingerprints.hostname fingerprintHostname: currentFingerprint.hostname
}) })
.from(clients) .from(clients)
.leftJoin(orgs, eq(clients.orgId, orgs.orgId)) .leftJoin(orgs, eq(clients.orgId, orgs.orgId))
.leftJoin(olms, eq(clients.clientId, olms.clientId)) .leftJoin(olms, eq(clients.clientId, olms.clientId))
.leftJoin(users, eq(clients.userId, users.userId)) .leftJoin(users, eq(clients.userId, users.userId))
.leftJoin(fingerprints, eq(olms.olmId, fingerprints.olmId)) .leftJoin(currentFingerprint, eq(olms.olmId, currentFingerprint.olmId))
.where(and(...conditions)); .where(and(...conditions));
} }

View File

@@ -0,0 +1,132 @@
import { sha256 } from "@oslojs/crypto/sha2";
import { encodeHexLowerCase } from "@oslojs/encoding";
import { currentFingerprint, db, fingerprintSnapshots, Olm } from "@server/db";
import { desc, eq } from "drizzle-orm";
function fingerprintHash(fp: any): string {
const canonical = {
username: fp.username ?? null,
hostname: fp.hostname ?? null,
platform: fp.platform ?? null,
osVersion: fp.osVersion ?? null,
kernelVersion: fp.kernelVersion ?? null,
arch: fp.arch ?? null,
deviceModel: fp.deviceModel ?? null,
serialNumber: fp.serialNumber ?? null,
platformFingerprint: fp.platformFingerprint ?? null
};
return encodeHexLowerCase(
sha256(new TextEncoder().encode(JSON.stringify(canonical)))
);
}
export async function handleFingerprintInsertion(olm: Olm, fingerprint: any) {
if (!fingerprint || !olm.olmId || Object.keys(fingerprint).length < 1) {
return;
}
const hash = fingerprintHash(fingerprint);
const now = Math.floor(Date.now() / 1000);
const [current] = await db
.select()
.from(currentFingerprint)
.where(eq(currentFingerprint.olmId, olm.olmId))
.limit(1);
if (!current) {
const [inserted] = await db
.insert(currentFingerprint)
.values({
olmId: olm.olmId,
firstSeen: now,
lastSeen: now,
username: fingerprint.username,
hostname: fingerprint.hostname,
platform: fingerprint.platform,
osVersion: fingerprint.osVersion,
kernelVersion: fingerprint.kernelVersion,
arch: fingerprint.arch,
deviceModel: fingerprint.deviceModel,
serialNumber: fingerprint.serialNumber,
platformFingerprint: fingerprint.platformFingerprint
})
.returning();
await db.insert(fingerprintSnapshots).values({
fingerprintId: inserted.fingerprintId,
username: fingerprint.username,
hostname: fingerprint.hostname,
platform: fingerprint.platform,
osVersion: fingerprint.osVersion,
kernelVersion: fingerprint.kernelVersion,
arch: fingerprint.arch,
deviceModel: fingerprint.deviceModel,
serialNumber: fingerprint.serialNumber,
platformFingerprint: fingerprint.platformFingerprint,
hash,
collectedAt: now
});
return;
}
// Get most recent snapshot hash
const [latestSnapshot] = await db
.select({ hash: fingerprintSnapshots.hash })
.from(fingerprintSnapshots)
.where(eq(fingerprintSnapshots.fingerprintId, current.fingerprintId))
.orderBy(desc(fingerprintSnapshots.collectedAt))
.limit(1);
const changed = !latestSnapshot || latestSnapshot.hash !== hash;
if (changed) {
// Insert snapshot if it has changed
await db.insert(fingerprintSnapshots).values({
fingerprintId: current.fingerprintId,
username: fingerprint.username,
hostname: fingerprint.hostname,
platform: fingerprint.platform,
osVersion: fingerprint.osVersion,
kernelVersion: fingerprint.kernelVersion,
arch: fingerprint.arch,
deviceModel: fingerprint.deviceModel,
serialNumber: fingerprint.serialNumber,
platformFingerprint: fingerprint.platformFingerprint,
hash,
collectedAt: now
});
// Update current fingerprint fully
await db
.update(currentFingerprint)
.set({
lastSeen: now,
username: fingerprint.username,
hostname: fingerprint.hostname,
platform: fingerprint.platform,
osVersion: fingerprint.osVersion,
kernelVersion: fingerprint.kernelVersion,
arch: fingerprint.arch,
deviceModel: fingerprint.deviceModel,
serialNumber: fingerprint.serialNumber,
platformFingerprint: fingerprint.platformFingerprint
})
.where(eq(currentFingerprint.fingerprintId, current.fingerprintId));
} else {
// No change, so only bump lastSeen
await db
.update(currentFingerprint)
.set({ lastSeen: now })
.where(eq(currentFingerprint.fingerprintId, current.fingerprintId));
}
}

View File

@@ -1,6 +1,6 @@
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from "express";
import { db } from "@server/db"; import { db } from "@server/db";
import { olms, clients, fingerprints } from "@server/db"; import { olms, clients, currentFingerprint } from "@server/db";
import { eq, and } from "drizzle-orm"; import { eq, and } from "drizzle-orm";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -66,16 +66,14 @@ export async function getUserOlm(
.select() .select()
.from(olms) .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)) .leftJoin(
currentFingerprint,
eq(olms.olmId, currentFingerprint.olmId)
)
.limit(1); .limit(1);
if (!result || !result.olms) { if (!result || !result.olms) {
return next( return next(createHttpError(HttpCode.NOT_FOUND, "Olm not found"));
createHttpError(
HttpCode.NOT_FOUND,
"Olm not found"
)
);
} }
const olm = result.olms; const olm = result.olms;
@@ -98,12 +96,13 @@ export async function getUserOlm(
} }
// Replace name with device name // Replace name with device name
const model = result.fingerprints?.deviceModel || null; const model = result.currentFingerprint?.deviceModel || null;
const newName = getUserDeviceName(model, olm.name); const newName = getUserDeviceName(model, olm.name);
const responseData = blocked !== undefined const responseData =
? { ...olm, name: newName, blocked } blocked !== undefined
: { ...olm, name: newName }; ? { ...olm, name: newName, blocked }
: { ...olm, name: newName };
return response(res, { return response(res, {
data: responseData, data: responseData,

View File

@@ -1,5 +1,5 @@
import { disconnectClient, getClientConfigVersion } from "#dynamic/routers/ws"; import { disconnectClient, getClientConfigVersion } from "#dynamic/routers/ws";
import { clientPostureSnapshots, db, fingerprints } from "@server/db"; import { clientPostureSnapshots, db, currentFingerprint } from "@server/db";
import { MessageHandler } from "@server/routers/ws"; import { MessageHandler } from "@server/routers/ws";
import { clients, olms, Olm } from "@server/db"; import { clients, olms, Olm } from "@server/db";
import { eq, lt, isNull, and, or } from "drizzle-orm"; import { eq, lt, isNull, and, or } from "drizzle-orm";
@@ -11,6 +11,7 @@ import { encodeHexLowerCase } from "@oslojs/encoding";
import { sha256 } from "@oslojs/crypto/sha2"; import { sha256 } from "@oslojs/crypto/sha2";
import { sendOlmSyncMessage } from "./sync"; import { sendOlmSyncMessage } from "./sync";
import { OlmErrorCodes } from "./error"; import { OlmErrorCodes } from "./error";
import { handleFingerprintInsertion } from "./fingerprintingUtils";
// Track if the offline checker interval is running // Track if the offline checker interval is running
let offlineCheckerInterval: NodeJS.Timeout | null = null; let offlineCheckerInterval: NodeJS.Timeout | null = null;
@@ -173,15 +174,25 @@ export const handleOlmPingMessage: MessageHandler = async (context) => {
} }
// get the version // get the version
logger.debug(`handleOlmPingMessage: About to get config version for olmId: ${olm.olmId}`); logger.debug(
`handleOlmPingMessage: About to get config version for olmId: ${olm.olmId}`
);
const configVersion = await getClientConfigVersion(olm.olmId); const configVersion = await getClientConfigVersion(olm.olmId);
logger.debug(`handleOlmPingMessage: Got config version: ${configVersion} (type: ${typeof configVersion})`); logger.debug(
`handleOlmPingMessage: Got config version: ${configVersion} (type: ${typeof configVersion})`
);
if (configVersion == null || configVersion === undefined) { if (configVersion == null || configVersion === undefined) {
logger.debug(`handleOlmPingMessage: could not get config version from server for olmId: ${olm.olmId}`); logger.debug(
`handleOlmPingMessage: could not get config version from server for olmId: ${olm.olmId}`
);
} }
if (message.configVersion != null && configVersion != null && configVersion != message.configVersion) { if (
message.configVersion != null &&
configVersion != null &&
configVersion != message.configVersion
) {
logger.debug( logger.debug(
`handleOlmPingMessage: Olm ping with outdated config version: ${message.configVersion} (current: ${configVersion})` `handleOlmPingMessage: Olm ping with outdated config version: ${message.configVersion} (current: ${configVersion})`
); );
@@ -204,55 +215,14 @@ export const handleOlmPingMessage: MessageHandler = async (context) => {
.set({ archived: false }) .set({ archived: false })
.where(eq(olms.olmId, olm.olmId)); .where(eq(olms.olmId, olm.olmId));
} }
await handleFingerprintInsertion(olm, fingerprint);
} catch (error) { } catch (error) {
logger.error("Error handling ping message", { error }); logger.error("Error handling ping message", { error });
} }
const now = Math.floor(Date.now() / 1000); const now = Math.floor(Date.now() / 1000);
if (fingerprint && olm.olmId) {
const [existingFingerprint] = await db
.select()
.from(fingerprints)
.where(eq(fingerprints.olmId, olm.olmId))
.limit(1);
if (!existingFingerprint) {
await db.insert(fingerprints).values({
olmId: olm.olmId,
firstSeen: now,
lastSeen: now,
username: fingerprint.username,
hostname: fingerprint.hostname,
platform: fingerprint.platform,
osVersion: fingerprint.osVersion,
kernelVersion: fingerprint.kernelVersion,
arch: fingerprint.arch,
deviceModel: fingerprint.deviceModel,
serialNumber: fingerprint.serialNumber,
platformFingerprint: fingerprint.platformFingerprint
});
} else {
await db
.update(fingerprints)
.set({
lastSeen: now,
username: fingerprint.username,
hostname: fingerprint.hostname,
platform: fingerprint.platform,
osVersion: fingerprint.osVersion,
kernelVersion: fingerprint.kernelVersion,
arch: fingerprint.arch,
deviceModel: fingerprint.deviceModel,
serialNumber: fingerprint.serialNumber,
platformFingerprint: fingerprint.platformFingerprint
})
.where(eq(fingerprints.olmId, olm.olmId));
}
}
if (postures && olm.clientId) { if (postures && olm.clientId) {
await db.insert(clientPostureSnapshots).values({ await db.insert(clientPostureSnapshots).values({
clientId: olm.clientId, clientId: olm.clientId,

View File

@@ -1,4 +1,9 @@
import { clientPostureSnapshots, db, fingerprints, orgs } from "@server/db"; import {
clientPostureSnapshots,
db,
currentFingerprint,
orgs
} from "@server/db";
import { MessageHandler } from "@server/routers/ws"; import { MessageHandler } from "@server/routers/ws";
import { import {
clients, clients,
@@ -48,12 +53,12 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => {
if (fingerprint) { if (fingerprint) {
const [existingFingerprint] = await db const [existingFingerprint] = await db
.select() .select()
.from(fingerprints) .from(currentFingerprint)
.where(eq(fingerprints.olmId, olm.olmId)) .where(eq(currentFingerprint.olmId, olm.olmId))
.limit(1); .limit(1);
if (!existingFingerprint) { if (!existingFingerprint) {
await db.insert(fingerprints).values({ await db.insert(currentFingerprint).values({
olmId: olm.olmId, olmId: olm.olmId,
firstSeen: now, firstSeen: now,
lastSeen: now, lastSeen: now,
@@ -75,16 +80,16 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => {
existingFingerprint.platform !== fingerprint.platform || existingFingerprint.platform !== fingerprint.platform ||
existingFingerprint.osVersion !== fingerprint.osVersion || existingFingerprint.osVersion !== fingerprint.osVersion ||
existingFingerprint.kernelVersion !== existingFingerprint.kernelVersion !==
fingerprint.kernelVersion || fingerprint.kernelVersion ||
existingFingerprint.arch !== fingerprint.arch || existingFingerprint.arch !== fingerprint.arch ||
existingFingerprint.deviceModel !== fingerprint.deviceModel || existingFingerprint.deviceModel !== fingerprint.deviceModel ||
existingFingerprint.serialNumber !== fingerprint.serialNumber || existingFingerprint.serialNumber !== fingerprint.serialNumber ||
existingFingerprint.platformFingerprint !== existingFingerprint.platformFingerprint !==
fingerprint.platformFingerprint; fingerprint.platformFingerprint;
if (hasChanges) { if (hasChanges) {
await db await db
.update(fingerprints) .update(currentFingerprint)
.set({ .set({
lastSeen: now, lastSeen: now,
username: fingerprint.username, username: fingerprint.username,
@@ -97,7 +102,7 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => {
serialNumber: fingerprint.serialNumber, serialNumber: fingerprint.serialNumber,
platformFingerprint: fingerprint.platformFingerprint platformFingerprint: fingerprint.platformFingerprint
}) })
.where(eq(fingerprints.olmId, olm.olmId)); .where(eq(currentFingerprint.olmId, olm.olmId));
} }
} }
} }

View File

@@ -1,5 +1,5 @@
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from "express";
import { db, fingerprints } from "@server/db"; import { db, currentFingerprint } from "@server/db";
import { olms } from "@server/db"; import { olms } from "@server/db";
import { eq, count, desc } from "drizzle-orm"; import { eq, count, desc } from "drizzle-orm";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
@@ -104,13 +104,16 @@ export async function listUserOlms(
.select() .select()
.from(olms) .from(olms)
.where(eq(olms.userId, userId)) .where(eq(olms.userId, userId))
.leftJoin(fingerprints, eq(olms.olmId, fingerprints.olmId)) .leftJoin(
currentFingerprint,
eq(olms.olmId, currentFingerprint.olmId)
)
.orderBy(desc(olms.dateCreated)) .orderBy(desc(olms.dateCreated))
.limit(limit) .limit(limit)
.offset(offset); .offset(offset);
const userOlms = list.map((item) => { const userOlms = list.map((item) => {
const model = item.fingerprints?.deviceModel || null; const model = item.currentFingerprint?.deviceModel || null;
const newName = getUserDeviceName(model, item.olms.name); const newName = getUserDeviceName(model, item.olms.name);
return { return {

View File

@@ -1,4 +1,4 @@
import { db, fingerprints, olms } from "@server/db"; import { db, currentFingerprint, olms } from "@server/db";
import logger from "@server/logger"; import logger from "@server/logger";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import { and, eq } from "drizzle-orm"; import { and, eq } from "drizzle-orm";
@@ -55,18 +55,24 @@ export async function recoverOlmWithFingerprint(
const result = await db const result = await db
.select({ .select({
olm: olms, olm: olms,
fingerprint: fingerprints fingerprint: currentFingerprint
}) })
.from(olms) .from(olms)
.innerJoin(fingerprints, eq(fingerprints.olmId, olms.olmId)) .innerJoin(
currentFingerprint,
eq(currentFingerprint.olmId, olms.olmId)
)
.where( .where(
and( and(
eq(olms.userId, userId), eq(olms.userId, userId),
eq(olms.archived, false), eq(olms.archived, false),
eq(fingerprints.platformFingerprint, platformFingerprint) eq(
currentFingerprint.platformFingerprint,
platformFingerprint
)
) )
) )
.orderBy(fingerprints.lastSeen); .orderBy(currentFingerprint.lastSeen);
if (!result || result.length == 0) { if (!result || result.length == 0) {
return next( return next(