169 lines
5.2 KiB
C#

using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using OnlyPrompt.Backend.ApiModels.UserProfile;
using OnlyPrompt.Backend.Database;
using OnlyPrompt.Backend.Database.Models;
using OnlyPrompt.Backend.Utils;
using System.ComponentModel.DataAnnotations;
namespace OnlyPrompt.Backend.Controllers
{
[ApiController]
[Route("api/v1/profiles")]
[Authorize(Roles = ModelConstants.UserRole)]
public class ProfileController : BaseController
{
private static ValidationProblem SlugExistsProblem = TypedResults.ValidationProblem(new Dictionary<string, string[]>
{
{ nameof(UserProfileModel.Slug), new[] { "Slug already exists." } }
});
private static ValidationProblem UserNameExistsProblem = TypedResults.ValidationProblem(new Dictionary<string, string[]>
{
{ nameof(UserModel.UserName), new[] { "Username is already taken." } }
});
public ProfileController(OnlyPromptContext db, IMapper mapper) : base(db, mapper)
{
}
[HttpGet("self")]
public async Task<Results<NotFound<string>, Ok<ApiUserProfile>>> GetSelfProfileAsync()
{
var userId = User.GetUserId();
if (userId is null)
return TypedResults.NotFound("Profile not found.");
var profile = await _db.UserProfiles
.Where(up => up.Id == userId.Value)
.Select(up => new ApiUserProfile(
up.DisplayName,
up.Slug,
up.Bio,
up.AvatarUrl,
up.Specialities,
_db.Reviews.Where(r => r.Prompt.CreatorId == up.Id).Average(r => (double?)r.Rating) ?? 0.0,
_db.Subscriptions.Count(s => s.SubscribedToId == up.Id)
))
.FirstOrDefaultAsync();
if (profile is null)
return TypedResults.NotFound("Profile not found.");
return TypedResults.Ok(profile);
}
[HttpGet("{id}")]
public async Task<Results<NotFound<string>, Ok<ApiUserProfile>>> GetProfileAsync(Identifier id)
{
var userId = User.GetUserId();
var profile = await _db.UserProfiles.OfIdentifer(id)
.ProjectTo<ApiUserProfile>(_mapper.ConfigurationProvider)
.FirstOrDefaultAsync();
if (profile is null)
return TypedResults.NotFound("Profile not found or is private.");
return TypedResults.Ok(profile);
}
[HttpGet]
public async Task<ApiCreatorCard[]> GetCreatorsAsync(
[Range(0, int.MaxValue)] int offset = 0,
[Range(1, 100)] int limit = 20,
[FromQuery] string sort = "popular",
[FromQuery] string? search = null
)
{
var userId = User.GetUserId();
var query = _db.UserProfiles.Where(up => up.Id != userId);
if (string.IsNullOrWhiteSpace(search) == false)
query = query.Where(up =>
up.DisplayName.Contains(search) ||
up.Slug.Contains(search) ||
up.User.UserName.Contains(search) ||
(up.Bio != null && up.Bio.Contains(search)));
var projected = query.Select(up => new ApiCreatorCard(
up.Id,
up.DisplayName,
up.Slug,
up.Bio,
up.AvatarUrl,
_db.Reviews.Where(r => r.Prompt.CreatorId == up.Id).Average(r => (double?)r.Rating) ?? 0.0,
_db.Subscriptions.Count(s => s.SubscribedToId == up.Id),
_db.Prompts.Count(p => p.CreatorId == up.Id),
_db.Subscriptions.Any(s => s.SubscribedToId == up.Id && s.SubscriberId == userId)
));
var allCreators = await projected.ToArrayAsync();
return (sort switch
{
"rating" => allCreators.OrderByDescending(c => c.AverageRating),
"new" => allCreators.OrderByDescending(c => c.UserId),
"prompts" => allCreators.OrderByDescending(c => c.PromptCount),
_ => allCreators.OrderByDescending(c => c.Subscribers),
}).Skip(offset).Take(limit).ToArray();
}
[HttpPut]
public async Task<Results<ValidationProblem, NotFound<string>, Ok<ApiUserProfile>>> UpdateProfileAsync([FromBody] ApiUpdateProfileRequest request)
{
var self = await GetUserProfileAsync();
if (self is null)
return TypedResults.NotFound("Profile not found.");
var user = await GetUserAsync();
if (user is null)
return TypedResults.NotFound("User not found.");
if (string.IsNullOrEmpty(request.UserName) == false)
{
if (await _db.Users.AnyAsync(u => u.UserName == request.UserName && u.Id != user.Id))
return UserNameExistsProblem;
user.UserName = request.UserName;
}
if (string.IsNullOrEmpty(request.Slug) == false)
{
if (await _db.UserProfiles.AnyAsync(up => up.Slug == request.Slug && up.Id != self.Id))
return SlugExistsProblem;
self.Slug = request.Slug;
}
if(request.AvatarUrl is not null)
self.AvatarUrl = request.AvatarUrl;
if(request.Bio is not null)
self.Bio = request.Bio;
if(request.Specialities is not null)
self.Specialities = request.Specialities;
if (string.IsNullOrEmpty(request.DisplayName) == false)
self.DisplayName = request.DisplayName;
self.IsPublic = request.IsPublic;
await _db.SaveChangesAsync();
var result = new ApiUserProfile(
self.DisplayName,
self.Slug,
self.Bio,
self.AvatarUrl,
self.Specialities,
await _db.Reviews.Where(r => r.Prompt.CreatorId == self.Id).AverageAsync(r => (double?)r.Rating) ?? 0.0,
await _db.Subscriptions.CountAsync(s => s.SubscribedToId == self.Id)
);
return TypedResults.Ok(result);
}
}
}