(
userId: string,
installationId: string,
githubCode: string,
)
| 68 | return await this.userRepository.findOne({ |
| 69 | where: { email }, |
| 70 | }); |
| 71 | } |
| 72 | |
| 73 | async deleteUser(userId: string): Promise<void> { |
| 74 | const user = await this.userRepository.findOne({ |
| 75 | where: { id: userId }, |
| 76 | relations: ['chats', 'projects'], |
| 77 | }); |
| 78 | |
| 79 | if (!user) { |
| 80 | throw new NotFoundException(`User with ID ${userId} not found`); |
| 81 | } |
| 82 | |
| 83 | // Hard delete the user (cascades will handle related entities) |
| 84 | await this.userRepository.remove(user); |
| 85 | } |
| 86 | |
| 87 | /** |
| 88 | * Change the name shown next to this user's projects. |
| 89 | * |
| 90 | * The column has no unique constraint and never has, so this cannot |
| 91 | * promise uniqueness without a migration that would fail on whatever |
| 92 | * duplicates already exist. What it does promise is that a rename never |
| 93 | * *creates* a collision with a name already taken — which is the case |
| 94 | * anyone would actually hit. |
| 95 | */ |
| 96 | async updateUsername(userId: string, raw: string): Promise<User> { |
| 97 | // Collapse whitespace: names are shown in the gallery, where a name of |
| 98 | // spaces or one padded into a different column width is a defacement. |
| 99 | const username = raw.replace(/\s+/g, ' ').trim(); |
| 100 | |
| 101 | if (username.length < 3 || username.length > 32) { |
| 102 | throw new BadRequestException( |
| 103 | 'A username is between 3 and 32 characters.', |
| 104 | ); |
| 105 | } |
| 106 | // Anything that renders as itself, minus the characters that make a name |
| 107 | // read as markup or a url somewhere it is displayed, and control |
| 108 | // characters. Inner spaces survive; they were collapsed above. |
| 109 | if (/[<>/\\@"'` -]/.test(username)) { |
| 110 | throw new BadRequestException( |
| 111 | 'A username cannot contain < > / \\ @ or quotes.', |
| 112 | ); |
no test coverage detected