fix(backend): backfill device name/model/osVersion on touch + auto-register
Bug: existing devices registered before Frontend started sending x-device-name/
x-device-model/x-device-os headers stayed with NULL fields forever — DeviceLimit
sheet shows only platform label ("iPhone" without iOS version, no name).
Fix:
- touchDevice() now accepts optional { name, model, osVersion } and updates
these fields when headers are provided (existing-row backfill).
- requireUser auth middleware reads URL-encoded x-device-* headers + passes
them to both touchDevice() (existing) and registerDevice() (auto-register).
After deploy: next authenticated request from updated client backfills the
device record automatically (throttled per TOUCH_THROTTLE_MS = 1×/min).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
e1ba0ebeaf
commit
60f608d891
@ -161,13 +161,25 @@ export async function registerDevice(opts: {
|
|||||||
return { device: created, created: true };
|
return { device: created, created: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Touch lastSeenAt — wird in der Auth-Middleware bei jedem Request aufgerufen. */
|
/** Touch lastSeenAt — wird in der Auth-Middleware bei jedem Request aufgerufen.
|
||||||
export async function touchDevice(userId: string, deviceId: string): Promise<void> {
|
* Optional: backfillt name/model/osVersion wenn Headers da sind (für Devices
|
||||||
|
* die unter altem Build registriert wurden ohne diese Info). */
|
||||||
|
export async function touchDevice(
|
||||||
|
userId: string,
|
||||||
|
deviceId: string,
|
||||||
|
info?: { name?: string | null; model?: string | null; osVersion?: string | null },
|
||||||
|
): Promise<void> {
|
||||||
const db = usePrisma();
|
const db = usePrisma();
|
||||||
|
const data: { lastSeenAt: Date; name?: string; model?: string; osVersion?: string } = {
|
||||||
|
lastSeenAt: new Date(),
|
||||||
|
};
|
||||||
|
if (info?.name) data.name = info.name;
|
||||||
|
if (info?.model) data.model = info.model;
|
||||||
|
if (info?.osVersion) data.osVersion = info.osVersion;
|
||||||
await db.userDevice
|
await db.userDevice
|
||||||
.updateMany({
|
.updateMany({
|
||||||
where: { userId, deviceId },
|
where: { userId, deviceId },
|
||||||
data: { lastSeenAt: new Date() },
|
data,
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
/* race-safe: wenn Device gerade gelöscht wurde */
|
/* race-safe: wenn Device gerade gelöscht wurde */
|
||||||
|
|||||||
@ -53,11 +53,26 @@ export async function requireUser(
|
|||||||
const deviceId = getHeader(event, 'x-device-id');
|
const deviceId = getHeader(event, 'x-device-id');
|
||||||
if (!deviceId) return user;
|
if (!deviceId) return user;
|
||||||
|
|
||||||
|
// Frontend sendet name/model/osVersion als x-device-* Headers (URL-encoded)
|
||||||
|
function readEncoded(name: string): string | null {
|
||||||
|
const raw = getHeader(event, name);
|
||||||
|
if (!raw) return null;
|
||||||
|
try { return decodeURIComponent(raw); } catch { return raw; }
|
||||||
|
}
|
||||||
|
const deviceName = readEncoded('x-device-name');
|
||||||
|
const deviceModel = readEncoded('x-device-model');
|
||||||
|
const deviceOs = readEncoded('x-device-os');
|
||||||
|
|
||||||
const existing = await findUserDevice(user.id, deviceId);
|
const existing = await findUserDevice(user.id, deviceId);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
// Touch lastSeenAt, throttled auf 1×/min — fire-and-forget
|
// Touch lastSeenAt + Backfill name/model/osVersion falls noch nicht gesetzt,
|
||||||
|
// throttled auf 1×/min — fire-and-forget
|
||||||
if (Date.now() - existing.lastSeenAt.getTime() > TOUCH_THROTTLE_MS) {
|
if (Date.now() - existing.lastSeenAt.getTime() > TOUCH_THROTTLE_MS) {
|
||||||
touchDevice(user.id, deviceId).catch(() => {});
|
touchDevice(user.id, deviceId, {
|
||||||
|
name: deviceName,
|
||||||
|
model: deviceModel,
|
||||||
|
osVersion: deviceOs,
|
||||||
|
}).catch(() => {});
|
||||||
}
|
}
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
@ -73,6 +88,9 @@ export async function requireUser(
|
|||||||
userId: user.id,
|
userId: user.id,
|
||||||
deviceId,
|
deviceId,
|
||||||
platform,
|
platform,
|
||||||
|
name: deviceName,
|
||||||
|
model: deviceModel,
|
||||||
|
osVersion: deviceOs,
|
||||||
maxDevices: limits.maxAppDevices,
|
maxDevices: limits.maxAppDevices,
|
||||||
});
|
});
|
||||||
return user;
|
return user;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user