feat(quick-260630-gbh-01): avatar storage endpoints + enriched /auth/me

- Add avatarPath String? column to User model (migration: add_user_avatar)
- POST /users/me/avatar: 2MB limit, image/png/jpeg/webp allowlist, writes to user-files/avatars/{userId}.{ext}
- GET /users/me/avatar: streams avatar with Cache-Control: no-store
- AuthService.getMe(): returns isLocalUser + hasAvatar without leaking passwordHash/ldapDn
- AuthController GET /auth/me: now returns enriched profile via getMe()
This commit is contained in:
2026-06-30 11:57:33 +02:00
parent dcba4b9977
commit 0fba45d2c2
5 changed files with 159 additions and 3 deletions
+34
View File
@@ -182,6 +182,40 @@ export class AuthService {
this.logger.log(`Password reset completed for user ${resetToken.userId}`);
}
/**
* Return enriched profile for the currently authenticated user.
* T-gbh-03: Only public fields + isLocalUser/hasAvatar returned — never
* passwordHash or ldapDn.
*/
async getMe(userId: string) {
const user = await this.prisma.user.findUnique({
where: { id: userId },
select: {
id: true,
username: true,
displayName: true,
role: true,
tenantId: true,
mustChangePassword: true,
passwordHash: true,
ldapDn: true,
avatarPath: true,
},
});
if (!user) {
return null;
}
const { passwordHash, ldapDn, avatarPath, ...publicFields } = user;
return {
...publicFields,
isLocalUser: !!passwordHash && !ldapDn,
hasAvatar: !!avatarPath,
};
}
/**
* Change password for the currently logged-in user.
* Verifies current password before allowing change.