feat: owner read-only view-as from team users table
Allow org owners to inspect any member's data scope via dual JWT; server blocks mutations and team page stays reachable for return. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -26,8 +26,7 @@ import {
|
||||
} from './dto';
|
||||
import { JwtAuthGuard } from './jwt-auth.guard';
|
||||
import { CurrentUser } from './current-user.decorator';
|
||||
|
||||
type AuthActor = { userId: string; organizationId: string };
|
||||
import type { Actor } from '../common/record-scope';
|
||||
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
@@ -69,7 +68,7 @@ export class AuthController {
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('switch-organization')
|
||||
switchOrganization(
|
||||
@CurrentUser() user: AuthActor,
|
||||
@CurrentUser() user: Actor,
|
||||
@Body() dto: SwitchOrganizationDto,
|
||||
) {
|
||||
return this.authService.switchOrganization(user.userId, dto.organizationId);
|
||||
@@ -77,38 +76,55 @@ export class AuthController {
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('organizations')
|
||||
listOrganizations(@CurrentUser() user: AuthActor) {
|
||||
listOrganizations(@CurrentUser() user: Actor) {
|
||||
return this.authService.listMyOrganizations(user.userId);
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('me')
|
||||
me(@CurrentUser() user: AuthActor) {
|
||||
return this.authService.me(user.userId, user.organizationId);
|
||||
me(@CurrentUser() user: Actor) {
|
||||
return this.authService.me(user.userId, user.organizationId, user);
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('view-as')
|
||||
viewAs(
|
||||
@CurrentUser() user: Actor,
|
||||
@Body() body: { targetUserId?: string },
|
||||
) {
|
||||
if (!body.targetUserId?.trim()) {
|
||||
throw new BadRequestException('کاربر هدف مشخص نیست.');
|
||||
}
|
||||
return this.authService.viewAs(user, body.targetUserId.trim());
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Patch('me')
|
||||
updateMe(@CurrentUser() user: AuthActor, @Body() dto: UpdateProfileDto) {
|
||||
updateMe(@CurrentUser() user: Actor, @Body() dto: UpdateProfileDto) {
|
||||
return this.authService.updateMe(user.userId, user.organizationId, dto);
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Patch('me/prior-archive')
|
||||
setPriorLens(
|
||||
@CurrentUser() user: AuthActor,
|
||||
@CurrentUser() user: Actor,
|
||||
@Body() body: { lens?: string },
|
||||
) {
|
||||
if (body.lens !== 'archive' && body.lens !== 'current') {
|
||||
throw new BadRequestException('حالت بایگانی نامعتبر است');
|
||||
}
|
||||
return this.authService.setPriorLens(user.userId, user.organizationId, body.lens);
|
||||
return this.authService.setPriorLens(
|
||||
user.userId,
|
||||
user.organizationId,
|
||||
body.lens,
|
||||
user,
|
||||
);
|
||||
}
|
||||
|
||||
/** عکس پروفایل کاربر جاری */
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('me/avatar')
|
||||
async avatar(@CurrentUser() user: AuthActor) {
|
||||
async avatar(@CurrentUser() user: Actor) {
|
||||
const file = await this.authService.openAvatar(user.userId);
|
||||
return new StreamableFile(file.stream, { type: file.mimeType });
|
||||
}
|
||||
@@ -122,7 +138,7 @@ export class AuthController {
|
||||
}),
|
||||
)
|
||||
uploadAvatar(
|
||||
@CurrentUser() user: AuthActor,
|
||||
@CurrentUser() user: Actor,
|
||||
@UploadedFile() file: Express.Multer.File,
|
||||
) {
|
||||
return this.authService.saveAvatar(user.userId, user.organizationId, file);
|
||||
@@ -130,7 +146,7 @@ export class AuthController {
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Delete('me/avatar')
|
||||
removeAvatar(@CurrentUser() user: AuthActor) {
|
||||
removeAvatar(@CurrentUser() user: Actor) {
|
||||
return this.authService.removeAvatar(user.userId, user.organizationId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user