fix(keysync): swallow expected supabase errors by code/status, not just message
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "ChatApp",
|
"productName": "ChatApp",
|
||||||
"version": "0.4.3",
|
"version": "0.4.4",
|
||||||
"identifier": "com.meinname.chatapp",
|
"identifier": "com.meinname.chatapp",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "pnpm vite:dev",
|
"beforeDevCommand": "pnpm vite:dev",
|
||||||
|
|||||||
@@ -164,19 +164,12 @@ async function syncOneConversationGaps(ctx: SyncCtx, convId: string): Promise<vo
|
|||||||
try {
|
try {
|
||||||
await shareConvKeyToDevice(supabase, convId, dev.id, pgHexToBytes(dev.public_key), ownCtx);
|
await shareConvKeyToDevice(supabase, convId, dev.id, pgHexToBytes(dev.public_key), ownCtx);
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
// Backfill is best-effort. Common silent failures:
|
// Backfill is best-effort. Most common silent failures:
|
||||||
// - This device hasn't been wrapped for us yet either, so
|
// - tryGetConvKey couldn't unwrap (another peer will fill the gap).
|
||||||
// tryGetConvKey couldn't unwrap (another peer will fill the gap).
|
|
||||||
// - RLS rejects because the recipient's owner is a pending (not-yet
|
// - RLS rejects because the recipient's owner is a pending (not-yet
|
||||||
// accepted) DM member, or has been removed from the conv.
|
// accepted) DM member, or was removed from the conv.
|
||||||
// Any of these are recoverable — log only at debug level.
|
// Both are recoverable / expected, swallow without spam.
|
||||||
const msg = err instanceof Error ? err.message : String(err);
|
if (isExpectedShareFailure(err)) continue;
|
||||||
const isExpected =
|
|
||||||
msg.includes('does not have it yet') ||
|
|
||||||
msg.includes('row-level security') ||
|
|
||||||
msg.includes('403') ||
|
|
||||||
msg.includes('Forbidden');
|
|
||||||
if (!isExpected) {
|
|
||||||
console.warn('keySync: shareConvKeyToDevice gap-fill failed', {
|
console.warn('keySync: shareConvKeyToDevice gap-fill failed', {
|
||||||
convId,
|
convId,
|
||||||
recipient: dev.id,
|
recipient: dev.id,
|
||||||
@@ -185,6 +178,21 @@ async function syncOneConversationGaps(ctx: SyncCtx, convId: string): Promise<vo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isExpectedShareFailure(err: unknown): boolean {
|
||||||
|
if (!err || typeof err !== 'object') return false;
|
||||||
|
const e = err as { message?: string; code?: string; details?: string; status?: number };
|
||||||
|
const code = (e.code ?? '').toString();
|
||||||
|
const status = e.status;
|
||||||
|
const haystack = (e.message ?? '') + ' ' + (e.details ?? '');
|
||||||
|
return (
|
||||||
|
status === 403 ||
|
||||||
|
code === '42501' || // postgres: insufficient_privilege (RLS)
|
||||||
|
code === '23505' || // unique_violation
|
||||||
|
haystack.includes('row-level security') ||
|
||||||
|
haystack.includes('does not have it yet') ||
|
||||||
|
haystack.includes('Forbidden')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function wrapForOneDevice(
|
async function wrapForOneDevice(
|
||||||
|
|||||||
Reference in New Issue
Block a user