Four fields were still frozen because changing them would have bypassed the record that makes the change traceable, and a user's login data could not be corrected at all. Locking a field is the wrong trade: a wrong value stays in the system forever. The fix is to let the edit write the same history entry the dedicated endpoint writes. - A colony's apiary is editable through PATCH and writes the same ColonyApiaryHistory entry POST /colonies/:id/move writes, so the location history never skips a stand the colony was recorded on. - A honey batch's storage container is editable through PATCH and writes the same HoneyBatchRelocation entry POST /honey-batches/:id/relocate writes. - A stock item's quantity is editable and books the difference as an ADJUSTMENT movement, so the stored level stays the sum of its movements. - A role's key is editable (system roles were already rejected outright by RolesService.update, which keeps the seed's ADMIN lookup intact). - A user's e-mail - the login identity - is editable. A typo there previously locked the account out for good. - New POST /users/:id/reset-password lets an administrator set someone else's password: there is no self-service reset flow, so a forgotten password was the one thing no edit could repair. It revokes every refresh token and clears the permission cache exactly as a self-service change does, refuses to act on your own account (that goes through /profile/password, which proves ownership by asking for the current one), and never writes the password to the log. Frontend: - Users had no edit UI at all, although PATCH /users/:id existed. New EditUserDialog covers name, e-mail, role and an optional new password. - Colony, honey batch and stock item forms gained the newly editable fields, each with a note saying the correction is recorded as a move, a relocation or a stock adjustment. - The role dialog gained the key field. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
73 lines
2.2 KiB
TypeScript
73 lines
2.2 KiB
TypeScript
import { Body, Controller, Get, Param, Patch, Post } from '@nestjs/common';
|
|
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
|
import { RequirePermission } from '../auth/decorators/require-permission.decorator';
|
|
import { CurrentUser } from '../auth/decorators/current-user.decorator';
|
|
import type { AuthenticatedUser } from '../auth/types/authenticated-user';
|
|
import { CreateUserDto } from './dto/create-user.dto';
|
|
import { UpdateUserDto } from './dto/update-user.dto';
|
|
import { ResetUserPasswordDto } from './dto/reset-user-password.dto';
|
|
import { UsersService } from './users.service';
|
|
|
|
@ApiTags('users')
|
|
@ApiBearerAuth()
|
|
@Controller('users')
|
|
export class UsersController {
|
|
constructor(private readonly usersService: UsersService) {}
|
|
|
|
@RequirePermission('users:read')
|
|
@Get()
|
|
findAll() {
|
|
return this.usersService.findAll();
|
|
}
|
|
|
|
@RequirePermission('users:create')
|
|
@Post()
|
|
create(@Body() dto: CreateUserDto, @CurrentUser() actor: AuthenticatedUser) {
|
|
return this.usersService.create(dto, actor.id);
|
|
}
|
|
|
|
@RequirePermission('users:read')
|
|
@Get(':id')
|
|
findOne(@Param('id') id: string) {
|
|
return this.usersService.findSummaryById(id);
|
|
}
|
|
|
|
@RequirePermission('users:update')
|
|
@Patch(':id')
|
|
update(
|
|
@Param('id') id: string,
|
|
@Body() dto: UpdateUserDto,
|
|
@CurrentUser() actor: AuthenticatedUser,
|
|
) {
|
|
return this.usersService.update(id, dto, actor.id);
|
|
}
|
|
|
|
@RequirePermission('users:deactivate')
|
|
@Post(':id/deactivate')
|
|
deactivate(@Param('id') id: string, @CurrentUser() actor: AuthenticatedUser) {
|
|
return this.usersService.deactivate(id, actor.id);
|
|
}
|
|
|
|
@RequirePermission('users:deactivate')
|
|
@Post(':id/activate')
|
|
activate(@Param('id') id: string, @CurrentUser() actor: AuthenticatedUser) {
|
|
return this.usersService.activate(id, actor.id);
|
|
}
|
|
|
|
@RequirePermission('users:update')
|
|
@Post(':id/reset-password')
|
|
resetPassword(
|
|
@Param('id') id: string,
|
|
@Body() dto: ResetUserPasswordDto,
|
|
@CurrentUser() actor: AuthenticatedUser,
|
|
) {
|
|
return this.usersService.resetPassword(id, dto, actor.id);
|
|
}
|
|
|
|
@RequirePermission('users:update')
|
|
@Post(':id/mfa/reset')
|
|
resetMfa(@Param('id') id: string, @CurrentUser() actor: AuthenticatedUser) {
|
|
return this.usersService.resetMfa(id, actor.id);
|
|
}
|
|
}
|