fix(invites): show email invites in space settings pending list
listSpaceInvites now queries both space_invites and identity_invites tables, merging results so email-based invites (via /invite endpoint) appear in the Pending Invites section. revokeSpaceInvite also falls through to identity_invites if not found in space_invites. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4a43ecdee0
commit
858457c056
|
|
@ -1067,12 +1067,28 @@ export async function getSpaceInviteByToken(token: string): Promise<StoredSpaceI
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listSpaceInvites(spaceSlug: string): Promise<StoredSpaceInvite[]> {
|
export async function listSpaceInvites(spaceSlug: string): Promise<StoredSpaceInvite[]> {
|
||||||
const rows = await sql`
|
const [spaceRows, identityRows] = await Promise.all([
|
||||||
SELECT * FROM space_invites
|
sql`SELECT * FROM space_invites WHERE space_slug = ${spaceSlug} ORDER BY created_at DESC`,
|
||||||
WHERE space_slug = ${spaceSlug}
|
sql`SELECT * FROM identity_invites WHERE space_slug = ${spaceSlug} ORDER BY created_at DESC`,
|
||||||
ORDER BY created_at DESC
|
]);
|
||||||
`;
|
const spaceInvites = await Promise.all(spaceRows.map(rowToInvite));
|
||||||
return Promise.all(rows.map(rowToInvite));
|
const identityInvites = await Promise.all(identityRows.map(async (row: any) => {
|
||||||
|
const emailDecrypted = await decryptField(row.email_enc);
|
||||||
|
return {
|
||||||
|
id: row.id,
|
||||||
|
spaceSlug: row.space_slug,
|
||||||
|
email: emailDecrypted ?? row.email ?? null,
|
||||||
|
role: row.space_role || 'member',
|
||||||
|
token: row.token,
|
||||||
|
invitedBy: row.invited_by_user_id,
|
||||||
|
status: row.status === 'claimed' ? 'accepted' : row.status,
|
||||||
|
createdAt: new Date(row.created_at).getTime(),
|
||||||
|
expiresAt: new Date(row.expires_at).getTime(),
|
||||||
|
acceptedAt: row.claimed_at ? new Date(row.claimed_at).getTime() : null,
|
||||||
|
acceptedByDid: row.claimed_by_user_id || null,
|
||||||
|
} as StoredSpaceInvite;
|
||||||
|
}));
|
||||||
|
return [...spaceInvites, ...identityInvites].sort((a, b) => b.createdAt - a.createdAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function acceptSpaceInvite(token: string, acceptedByDid: string): Promise<StoredSpaceInvite | null> {
|
export async function acceptSpaceInvite(token: string, acceptedByDid: string): Promise<StoredSpaceInvite | null> {
|
||||||
|
|
@ -1091,7 +1107,13 @@ export async function revokeSpaceInvite(id: string, spaceSlug: string): Promise<
|
||||||
UPDATE space_invites SET status = 'revoked'
|
UPDATE space_invites SET status = 'revoked'
|
||||||
WHERE id = ${id} AND space_slug = ${spaceSlug} AND status = 'pending'
|
WHERE id = ${id} AND space_slug = ${spaceSlug} AND status = 'pending'
|
||||||
`;
|
`;
|
||||||
return result.count > 0;
|
if (result.count > 0) return true;
|
||||||
|
// Also check identity_invites (email invites with space_slug)
|
||||||
|
const result2 = await sql`
|
||||||
|
UPDATE identity_invites SET status = 'revoked'
|
||||||
|
WHERE id = ${id} AND space_slug = ${spaceSlug} AND status = 'pending'
|
||||||
|
`;
|
||||||
|
return result2.count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue