170 lines
5.3 KiB
C#
170 lines
5.3 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)
|
|
.Where(up => up.IsPublic || up.Id == userId)
|
|
.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);
|
|
}
|
|
|
|
}
|
|
}
|