fix(auth): fix invite flow UUID/slug mismatch and removeMember cleanup

- TenantController invite endpoints (list/create/revoke) were passing the
  tenant UUID from the URL param directly to AuthService methods that
  expect a slug, causing 404 on every invite operation. Now resolves
  tenant via findTenantOrFail() first and passes slug.
- removeMember now also deletes from public.users so removed members
  can no longer log in.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-03-07 05:34:47 -08:00
parent 915bd400c1
commit e31baa1f40
1 changed files with 13 additions and 6 deletions

View File

@ -274,6 +274,10 @@ export class TenantController {
} }
await qr.query(`DELETE FROM users WHERE id = $1`, [memberId]); await qr.query(`DELETE FROM users WHERE id = $1`, [memberId]);
// Also remove from global user index so the user can no longer log in
await this.dataSource.query(
`DELETE FROM public.users WHERE id = $1`, [memberId],
);
return { success: true }; return { success: true };
} finally { } finally {
await qr.release(); await qr.release();
@ -287,8 +291,9 @@ export class TenantController {
*/ */
@Get(':id/invites') @Get(':id/invites')
@Roles('admin') @Roles('admin')
async listInvites(@Param('id') tenantId: string) { async listInvites(@Param('id') id: string) {
const invites = await this.authService.listInvites(tenantId); const tenant = await this.findTenantOrFail(id);
const invites = await this.authService.listInvites(tenant.slug);
return invites.map((inv) => ({ return invites.map((inv) => ({
id: inv.id, id: inv.id,
email: inv.email, email: inv.email,
@ -306,12 +311,13 @@ export class TenantController {
@Post(':id/invites') @Post(':id/invites')
@Roles('admin') @Roles('admin')
async createInvite( async createInvite(
@Param('id') tenantId: string, @Param('id') id: string,
@Body() body: { email: string; role?: string }, @Body() body: { email: string; role?: string },
@Request() req: any, @Request() req: any,
) { ) {
const tenant = await this.findTenantOrFail(id);
const invite = await this.authService.createInvite( const invite = await this.authService.createInvite(
tenantId, tenant.slug,
body.email, body.email,
body.role || 'viewer', body.role || 'viewer',
req.user?.sub || 'system', req.user?.sub || 'system',
@ -333,10 +339,11 @@ export class TenantController {
@Delete(':id/invites/:inviteId') @Delete(':id/invites/:inviteId')
@Roles('admin') @Roles('admin')
async revokeInvite( async revokeInvite(
@Param('id') tenantId: string, @Param('id') id: string,
@Param('inviteId') inviteId: string, @Param('inviteId') inviteId: string,
) { ) {
await this.authService.revokeInvite(inviteId, tenantId); const tenant = await this.findTenantOrFail(id);
await this.authService.revokeInvite(inviteId, tenant.slug);
return { success: true }; return { success: true };
} }