From 44bbb26db2eb9586973dc80f7ae708c2d4ef3dfa Mon Sep 17 00:00:00 2001 From: Afolabi Ayomide Date: Tue, 5 May 2026 14:11:33 +0100 Subject: [PATCH] feat(backend): add username-based public profile lookup endpoint Closes #283 --- backend/src/users/users.controller.ts | 15 ++++++++++++ backend/src/users/users.service.ts | 34 ++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/backend/src/users/users.controller.ts b/backend/src/users/users.controller.ts index db98814..08585a1 100644 --- a/backend/src/users/users.controller.ts +++ b/backend/src/users/users.controller.ts @@ -91,6 +91,21 @@ export class UsersController { return this.userService.getMyStats(req.user.id as string); } + @Get('by-username/:username') + async getPublicProfileByUsername( + @Param('username') username: string, + ): Promise<{ + id: string; + username: string | null; + xp: number; + badgesCount: number; + coursesCompleted: number; + avatarUrl: string | null; + bio: string | null; + }> { + return this.userService.findByUsername(username); + } + @Get(':id/public') async getPublicProfile(@Param('id') id: string): Promise<{ id: string; diff --git a/backend/src/users/users.service.ts b/backend/src/users/users.service.ts index 7f65b33..3c078f4 100644 --- a/backend/src/users/users.service.ts +++ b/backend/src/users/users.service.ts @@ -6,7 +6,7 @@ import { BadRequestException, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, MoreThan } from 'typeorm'; +import { ILike, Repository, MoreThan } from 'typeorm'; import * as crypto from 'crypto'; import * as bcrypt from 'bcrypt'; import { RegisterDto } from 'src/auth/dto/register.dto'; @@ -302,4 +302,36 @@ export class UserService { bio: user.bio ?? null, }; } + + async findByUsername(username: string): Promise<{ + id: string; + username: string | null; + xp: number; + badgesCount: number; + coursesCompleted: number; + avatarUrl: string | null; + bio: string | null; + }> { + const user = await this.userRepository.findOne({ + where: { username: ILike(username) }, + }); + + if (!user) { + throw new NotFoundException('User not found'); + } + + const badgesCount = await this.userBadgeRepository.count({ + where: { userId: user.id }, + }); + + return { + id: user.id, + username: user.username ?? user.name ?? null, + xp: this.resolveXp(user), + badgesCount, + coursesCompleted: user.coursesCompleted ?? 0, + avatarUrl: user.avatarUrl ?? null, + bio: user.bio ?? null, + }; + } }