feat(P3.T2): DeviceRecord.revokedAt + revokeDevice wrapper

Extend DeviceRecord with revokedAt field, update registerDevice and
listOwnDevices selects to include revoked_at, add revokeDevice RPC
helper, update db-types to reflect T1 migration schema, and add
vitest coverage for all new behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
byGalax
2026-05-16 18:52:34 +02:00
parent 540b506f91
commit 5b0aa24a8d
3 changed files with 69 additions and 2 deletions
+13 -2
View File
@@ -16,6 +16,7 @@ export interface DeviceRecord {
name: string;
platform: DevicePlatform;
lastSeenAt: string;
revokedAt: string | null;
}
export async function registerDevice(
@@ -32,7 +33,7 @@ export async function registerDevice(
name: params.name,
platform: params.platform,
})
.select('id, name, platform, last_seen_at')
.select('id, name, platform, last_seen_at, revoked_at')
.single();
if (error) throw error;
@@ -41,6 +42,7 @@ export async function registerDevice(
name: data.name,
platform: data.platform,
lastSeenAt: data.last_seen_at,
revokedAt: data.revoked_at,
};
}
@@ -50,7 +52,7 @@ export async function listOwnDevices(client: AppSupabaseClient): Promise<DeviceR
const { data, error } = await client
.from('devices')
.select('id, name, platform, last_seen_at')
.select('id, name, platform, last_seen_at, revoked_at')
.eq('user_id', session.user.id)
.order('last_seen_at', { ascending: false });
if (error) throw error;
@@ -60,6 +62,7 @@ export async function listOwnDevices(client: AppSupabaseClient): Promise<DeviceR
name: row.name,
platform: row.platform,
lastSeenAt: row.last_seen_at,
revokedAt: row.revoked_at,
}));
}
@@ -74,6 +77,14 @@ export async function touchDeviceLastSeen(
if (error) throw error;
}
export async function revokeDevice(
client: AppSupabaseClient,
deviceId: string,
): Promise<void> {
const { error } = await client.rpc('revoke_device', { p_device_id: deviceId });
if (error) throw new Error(error.message);
}
// Intentional re-exports so app layers only need @chat-app/shared/auth.
export type { SecretStore } from './secure-storage';
export { toBase64 as base64FromBytes, fromBase64 as bytesFromBase64 };