import { BadRequestException, Body, Controller, Delete, ForbiddenException, NotFoundException, Get, Param, Post, Put, Request, UseGuards, } from '@nestjs/common'; import { UserService } from './user.service'; import { CombinedAuthGuard } from '../auth/combined-auth.guard'; import { UpdateUserDto } from './dto/update-user.dto'; import { I18nService } from '../i18n/i18n.service'; @Controller('users') @UseGuards(CombinedAuthGuard) export class UserController { constructor( private readonly userService: UserService, private readonly i18nService: I18nService, ) { } // --- API Key Management --- @Get('api-key') async getApiKey(@Request() req) { const apiKey = await this.userService.getOrCreateApiKey(req.user.id); return { apiKey }; } @Post('api-key/rotate') async rotateApiKey(@Request() req) { const apiKey = await this.userService.regenerateApiKey(req.user.id); return { apiKey }; } // --- Profile --- @Get('me') async getMe(@Request() req) { const user = await this.userService.findOneById(req.user.id); if (!user) throw new NotFoundException(this.i18nService.getErrorMessage('userNotFound')); let isNotebookEnabled = true; if (user.tenantId) { const settings = await this.userService.getTenantSettings(user.tenantId); isNotebookEnabled = settings?.isNotebookEnabled ?? true; } return { id: user.id, username: user.username, role: user.role, tenantId: user.tenantId, isAdmin: user.isAdmin, isNotebookEnabled, }; } @Get() async findAll(@Request() req) { const callerRole = req.user.role; if (callerRole !== 'SUPER_ADMIN' && callerRole !== 'TENANT_ADMIN') { throw new ForbiddenException(this.i18nService.getErrorMessage('adminOnlyViewList')); } if (callerRole === 'SUPER_ADMIN') { return this.userService.findAll(); } else { return this.userService.findByTenantId(req.user.tenantId); } } @Put('password') async changePassword( @Request() req, @Body() body: { currentPassword: string; newPassword: string }, ) { const { currentPassword, newPassword } = body; if (!currentPassword || !newPassword) { throw new BadRequestException(this.i18nService.getErrorMessage('passwordsRequired')); } if (newPassword.length < 6) { throw new BadRequestException(this.i18nService.getErrorMessage('newPasswordMinLength')); } return this.userService.changePassword( req.user.id, currentPassword, newPassword, ); } @Post() async createUser( @Request() req, @Body() body: { username: string; password: string; role?: string }, ) { const callerRole = req.user.role; if (callerRole !== 'SUPER_ADMIN' && callerRole !== 'TENANT_ADMIN') { throw new ForbiddenException(this.i18nService.getErrorMessage('adminOnlyCreateUser')); } const { username, password } = body; if (!username || !password) { throw new BadRequestException(this.i18nService.getErrorMessage('usernamePasswordRequired')); } if (password.length < 6) { throw new BadRequestException(this.i18nService.getErrorMessage('passwordMinLength')); } // Determine target role based on caller's role and requested role let targetRole = 'USER'; let isAdmin = false; if (callerRole === 'SUPER_ADMIN') { // Super Admin can create TENANT_ADMIN or USER. Default to requested role, fallback to USER. targetRole = body.role === 'TENANT_ADMIN' ? 'TENANT_ADMIN' : 'USER'; isAdmin = targetRole === 'TENANT_ADMIN'; } else if (callerRole === 'TENANT_ADMIN') { // Tenant Admin can ONLY create regular users. targetRole = 'USER'; isAdmin = false; } // Pass the calculated params to the service return this.userService.createUser(username, password, isAdmin, req.user.tenantId, targetRole as any); } @Put(':id') async updateUser( @Request() req, @Body() body: UpdateUserDto, @Param('id') id: string, ) { const callerRole = req.user.role; if (callerRole !== 'SUPER_ADMIN' && callerRole !== 'TENANT_ADMIN') { throw new ForbiddenException(this.i18nService.getErrorMessage('adminOnlyUpdateUser')); } // 更新するユーザー情報を取得 const userToUpdate = await this.userService.findOneById(id); if (!userToUpdate) { throw new NotFoundException(this.i18nService.getErrorMessage('userNotFound')); } if (callerRole === 'TENANT_ADMIN' && userToUpdate.tenantId !== req.user.tenantId) { throw new ForbiddenException('Cannot modify users outside your tenant'); } // Prevent modifying the builtin admin account if (userToUpdate.username === 'admin') { throw new ForbiddenException(this.i18nService.getErrorMessage('cannotModifyBuiltinAdmin')); } // Role modification logic if (body.role && userToUpdate.role !== body.role) { if (callerRole !== 'SUPER_ADMIN') { throw new ForbiddenException('Only Super Admins can change user roles.'); } if (userToUpdate.role === 'SUPER_ADMIN') { throw new ForbiddenException('Cannot modify the role of another Super Admin.'); } // Sync isAdmin based on the newly selected role if (body.role === 'TENANT_ADMIN' || body.role === 'SUPER_ADMIN') { body.isAdmin = true; } else { body.isAdmin = false; } } return this.userService.updateUser(id, body); } @Delete(':id') async deleteUser( @Request() req, @Param('id') id: string, ) { const callerRole = req.user.role; if (callerRole !== 'SUPER_ADMIN' && callerRole !== 'TENANT_ADMIN') { throw new ForbiddenException(this.i18nService.getErrorMessage('adminOnlyDeleteUser')); } // 管理者が自身を削除するのを防止 if (req.user.id === id) { throw new BadRequestException(this.i18nService.getErrorMessage('cannotDeleteSelf')); } // 削除するユーザー情報を取得 const userToDelete = await this.userService.findOneById(id); if (!userToDelete) { throw new NotFoundException(this.i18nService.getErrorMessage('userNotFound')); } if (callerRole === 'TENANT_ADMIN' && userToDelete.tenantId !== req.user.tenantId) { throw new ForbiddenException('Cannot delete users outside your tenant'); } // ビルトインadminアカウントの削除を阻止 if (userToDelete.username === 'admin') { throw new ForbiddenException(this.i18nService.getErrorMessage('cannotDeleteBuiltinAdmin')); } return this.userService.deleteUser(id); } }