2026-04-12 03:45:01 +02:00

184 lines
5.4 KiB
C#

using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using OnlyPrompt.Backend.ApiModels.Prompt;
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/prompts")]
[Authorize(Roles = ModelConstants.UserRole)]
public class PromptController : BaseController
{
public PromptController(OnlyPromptContext db, IMapper mapper) : base(db, mapper)
{
}
private IQueryable<PromptModel> GetAccessiblePrompts(Guid userId)
{
return _db.Prompts.Where(
p => p.SubscriptionTier == null
|| p.Creator.Subscribers.Any(
sub => sub.SubscriberId == userId
&& p.SubscriptionTier!.Level <= sub.SubscriptionTier!.Level
)
);
}
[HttpGet("{id}")]
public async Task<Results<Ok<ApiPrompt>, NotFound<string>>> GetPromptAsync(Identifier id)
{
var userId = User.GetUserId();
var prompt = await GetAccessiblePrompts(userId.Value)
.OfIdentifer(id)
.FirstOrDefaultAsync();
if (prompt is null)
return TypedResults.NotFound("Prompt not found or no permission");
var apiPrompt = _mapper.Map<ApiPrompt>(prompt);
return TypedResults.Ok(apiPrompt);
}
[HttpDelete("{id}")]
public async Task<Results<NoContent, NotFound<string>>> DeletePromptAsync(Identifier id)
{
var userId = User.GetUserId();
var isAdmin = User.IsInRole(ModelConstants.AdminRole);
var count = await _db.Prompts
.OfIdentifer(id)
.Where(p => p.CreatorId == userId || isAdmin)
.ExecuteDeleteAsync();
if (count == 0)
return TypedResults.NotFound("Prompt not found or no permission");
return TypedResults.NoContent();
}
[HttpPost]
public async Task<Results<Ok<ApiPrompt>, NotFound<string>>> CreatePromptAsync([FromBody] ApiCreatePromptRequest request)
{
var userId = User.GetUserId();
var category = await _db.Categories.FindByIdentifierAsync(request.Category);
if (category is null)
return TypedResults.NotFound("Category not found");
SubscriptionTierModel? subscriptionTier = null;
if (request.SubscriptionTier.HasValue)
{
subscriptionTier = await _db.SubscriptionTiers.FirstOrDefaultAsync(
t => t.Level == request.SubscriptionTier.Value
&& t.UserId == userId
);
if (subscriptionTier is null)
return TypedResults.NotFound("Subscription tier not found");
}
var slug = request.Slug;
if (string.IsNullOrEmpty(slug))
slug = await SlugHelper.GenerateUniqueSlugAsync(request.Title, slug => _db.Prompts.AnyAsync(p => p.Slug == slug), ModelConstants.MaxSlugLength);
var prompt = new PromptModel
{
Id = Guid.NewGuid(),
Title = request.Title,
Description = request.Description,
Prompt = request.Content,
CreatorId = userId.Value,
SubscriptionTier = subscriptionTier,
Category = category,
Slug = slug
};
_db.Prompts.Add(prompt);
await _db.SaveChangesAsync();
var apiPrompt = _mapper.Map<ApiPrompt>(prompt);
return TypedResults.Ok(apiPrompt);
}
[HttpGet("{id}/reviews")]
public async Task<Ok<ApiReview[]>> GetReviewsAsync(Identifier id, [FromQuery] int offset = 0, [Range(1, 200)][FromQuery] int limit = 20)
{
var userId = User.GetUserId();
var accessiblePrompts = GetAccessiblePrompts(userId!.Value);
var reviews = await accessiblePrompts.Select(x => x.Reviews)
.Skip(offset)
.Take(limit)
.ProjectTo<ApiReview>(_mapper.ConfigurationProvider)
.ToArrayAsync();
return TypedResults.Ok(reviews);
}
[HttpPut("{id}/reviews")]
public async Task<Results<Ok<ApiReview>, BadRequest<string>, NotFound<string>>> AddReviewAsync(Identifier id, [FromBody] ApiCreateReviewRequest request)
{
var userId = User.GetUserId();
var prompt = await GetAccessiblePrompts(userId!.Value)
.OfIdentifer(id)
.FirstOrDefaultAsync();
if (prompt is null)
return TypedResults.NotFound("Prompt not found or no permission");
if(prompt.CreatorId == userId)
return TypedResults.BadRequest("Cannot review your own prompt");
var review = await _db.Reviews.FirstOrDefaultAsync(
r => r.PromptId == prompt.Id
&& r.ReviewerId == userId
);
if (review is null)
{
review = new ReviewModel
{
PromptId = prompt.Id,
ReviewerId = userId.Value,
Comment = request.Comment,
Rating = request.Rating
};
_db.Reviews.Add(review);
}
else
{
review.Comment = request.Comment;
review.Rating = request.Rating;
}
await _db.SaveChangesAsync();
var apiReview = _mapper.Map<ApiReview>(review);
return TypedResults.Ok(apiReview);
}
[HttpDelete("{promptId}/reviews/{reviewerId}")]
public async Task<Results<NoContent, NotFound<string>>> DeleteReviewAsync(Identifier promptId, Guid reviewerId)
{
var userId = User.GetUserId();
var isAdmin = User.IsInRole(ModelConstants.AdminRole);
var count = await _db.Reviews
.Where(
r => (promptId.Id.HasValue ? r.PromptId == promptId.Id : r.Prompt.Slug == promptId.Slug)
&& (r.ReviewerId == reviewerId || isAdmin)
)
.ExecuteDeleteAsync();
if (count == 0)
return TypedResults.NotFound("Review not found or no permission");
return TypedResults.NoContent();
}
}
}