Add staff password reset, raise code upload limit, and guard restore sizing.

Admins and technical staff can reset passwords via PATCH /users/:id/password with scoped permissions for technical users; deploy/source uploads allow up to 10GiB and block deploy when allocated storage is smaller than uploaded archive or DB dump, with an inline error modal.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
keyhan
2026-05-16 01:03:07 +03:30
parent 67b41ae311
commit a7293973d5
6 changed files with 332 additions and 40 deletions
+22
View File
@@ -44,6 +44,13 @@ class UpdateRoleDto {
role: UserRole;
}
class AdminSetPasswordDto {
@IsString()
@MinLength(8)
@MaxLength(64)
password: string;
}
@ApiTags('Users')
@ApiBearerAuth()
@Controller('users')
@@ -77,6 +84,21 @@ export class UsersController {
return this.usersService.adminCreate(dto);
}
@Patch(':id/password')
@Roles(UserRole.ADMIN, UserRole.TECHNICAL)
@ApiOperation({
summary:
'Set user password (Admin: any role; Technical: standard users only)',
})
async setPassword(
@Request() req: { user: { role: UserRole } },
@Param('id') id: string,
@Body() dto: AdminSetPasswordDto,
) {
await this.usersService.setPasswordByStaff(req.user.role, id, dto.password);
return { message: 'Password updated' };
}
@Patch(':id/role')
@Roles(UserRole.ADMIN)
@ApiOperation({ summary: 'Update user role (Admin only)' })
+27 -1
View File
@@ -1,4 +1,9 @@
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
import {
Injectable,
NotFoundException,
ConflictException,
ForbiddenException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, ILike } from 'typeorm';
import * as bcrypt from 'bcrypt';
@@ -106,4 +111,25 @@ export class UsersService {
async activate(id: string): Promise<void> {
await this.usersRepository.update(id, { isActive: true });
}
/**
* Staff-set password: admins may reset anyone; technical staff only standard (user) accounts.
*/
async setPasswordByStaff(
actorRole: UserRole,
targetUserId: string,
plainPassword: string,
): Promise<void> {
const user = await this.findById(targetUserId);
if (!user) {
throw new NotFoundException('User not found');
}
if (actorRole === UserRole.TECHNICAL && user.role !== UserRole.USER) {
throw new ForbiddenException(
'Technical staff can only reset passwords for standard (user) accounts',
);
}
user.password = await bcrypt.hash(plainPassword, 12);
await this.usersRepository.save(user);
}
}