From 76bb3f1c95272c7760a666e2e13c793b456b5cef Mon Sep 17 00:00:00 2001 From: pancakes Date: Mon, 6 Jan 2025 18:01:57 +1000 Subject: [PATCH] [backend/api] Add instance rules endpoints --- .../Controllers/Web/InstanceController.cs | 113 ++++++++++++++++++ .../Schemas/Web/RuleCreateRequest.cs | 7 ++ Iceshrimp.Shared/Schemas/Web/RuleResponse.cs | 10 ++ .../Schemas/Web/RuleUpdateRequest.cs | 8 ++ 4 files changed, 138 insertions(+) create mode 100644 Iceshrimp.Shared/Schemas/Web/RuleCreateRequest.cs create mode 100644 Iceshrimp.Shared/Schemas/Web/RuleResponse.cs create mode 100644 Iceshrimp.Shared/Schemas/Web/RuleUpdateRequest.cs diff --git a/Iceshrimp.Backend/Controllers/Web/InstanceController.cs b/Iceshrimp.Backend/Controllers/Web/InstanceController.cs index e6aa2a8f..19e31c9f 100644 --- a/Iceshrimp.Backend/Controllers/Web/InstanceController.cs +++ b/Iceshrimp.Backend/Controllers/Web/InstanceController.cs @@ -4,12 +4,15 @@ using Iceshrimp.Backend.Controllers.Shared.Attributes; using Iceshrimp.Backend.Controllers.Web.Renderers; using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Database; +using Iceshrimp.Backend.Core.Database.Tables; using Iceshrimp.Backend.Core.Extensions; +using Iceshrimp.Backend.Core.Helpers; using Iceshrimp.Backend.Core.Middleware; using Iceshrimp.Backend.Core.Services; using Iceshrimp.Shared.Schemas.Web; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.RateLimiting; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; namespace Iceshrimp.Backend.Controllers.Web; @@ -42,6 +45,116 @@ public class InstanceController( }; } + [HttpGet("rules")] + [ProducesResults(HttpStatusCode.OK)] + public async Task> GetRules() + { + return await db.Rules + .OrderBy(p => p.Order) + .ThenBy(p => p.Id) + .Select(p => new RuleResponse { Id = p.Id, Text = p.Text, Description = p.Description }) + .ToListAsync(); + } + + [HttpPost("rules")] + [Authenticate] + [Authorize("role:moderator")] + [ProducesResults(HttpStatusCode.OK)] + public async Task CreateRule(RuleCreateRequest request) + { + var count = await db.Rules.CountAsync(); + + var rule = new Rule + { + Id = IdHelpers.GenerateSnowflakeId(), + Order = count + 1, + Text = request.Text, + Description = request.Description + }; + + db.Add(rule); + await db.SaveChangesAsync(); + + return new RuleResponse { Id = rule.Id, Text = rule.Text, Description = rule.Description }; + } + + [HttpPatch("rules/{id}")] + [Authenticate] + [Authorize("role:moderator")] + [ProducesResults(HttpStatusCode.OK)] + [ProducesErrors(HttpStatusCode.NotFound)] + public async Task UpdateRule(string id, RuleUpdateRequest request) + { + var rule = await db.Rules.FirstOrDefaultAsync(p => p.Id == id) + ?? throw GracefulException.RecordNotFound(); + + var count = await db.Rules.CountAsync(); + // order is defined here because I don't know why but request.Order is still nullable even if the if statement checks it isn't null + var order = request.Order ?? 0; + if (order > 0 && order != rule.Order && count != 1) + { + request.Order = Math.Min(order, count); + + if (order > rule.Order) + { + var rules = await db.Rules + .Where(p => rule.Order < p.Order && p.Order <= order) + .ToListAsync(); + + foreach (var r in rules) + r.Order -= 1; + + db.UpdateRange(rules); + } + else + { + var rules = await db.Rules + .Where(p => order <= p.Order && p.Order < rule.Order) + .ToListAsync(); + + foreach (var r in rules) + r.Order += 1; + + db.UpdateRange(rules); + } + + rule.Order = order; + } + + if (request.Text != null) + rule.Text = request.Text.Trim(); + + if (request.Description != null) + rule.Description = string.IsNullOrWhiteSpace(request.Description) ? null : request.Description.Trim(); + + db.Update(rule); + await db.SaveChangesAsync(); + + return new RuleResponse { Id = rule.Id, Text = rule.Text, Description = rule.Description }; + } + + [HttpDelete("rules/{id}")] + [Authenticate] + [Authorize("role:moderator")] + [ProducesResults(HttpStatusCode.OK)] + [ProducesErrors(HttpStatusCode.NotFound)] + public async Task DeleteRule(string id) + { + var rule = await db.Rules.FirstOrDefaultAsync(p => p.Id == id) + ?? throw GracefulException.RecordNotFound(); + + var rules = await db.Rules + .Where(p => p.Order > rule.Order) + .ToListAsync(); + + db.Remove(rule); + + foreach (var r in rules) + r.Order -= 1; + db.UpdateRange(rules); + await db.SaveChangesAsync(); + } + [HttpGet("staff")] [Authenticate] [Authorize] diff --git a/Iceshrimp.Shared/Schemas/Web/RuleCreateRequest.cs b/Iceshrimp.Shared/Schemas/Web/RuleCreateRequest.cs new file mode 100644 index 00000000..23fcb102 --- /dev/null +++ b/Iceshrimp.Shared/Schemas/Web/RuleCreateRequest.cs @@ -0,0 +1,7 @@ +namespace Iceshrimp.Shared.Schemas.Web; + +public class RuleCreateRequest +{ + public required string Text { get; set; } + public required string? Description { get; set; } +} diff --git a/Iceshrimp.Shared/Schemas/Web/RuleResponse.cs b/Iceshrimp.Shared/Schemas/Web/RuleResponse.cs new file mode 100644 index 00000000..39f79a2b --- /dev/null +++ b/Iceshrimp.Shared/Schemas/Web/RuleResponse.cs @@ -0,0 +1,10 @@ +using Iceshrimp.Shared.Helpers; + +namespace Iceshrimp.Shared.Schemas.Web; + +public class RuleResponse : IIdentifiable +{ + public required string Id { get; set; } + public required string Text { get; set; } + public required string? Description { get; set; } +} diff --git a/Iceshrimp.Shared/Schemas/Web/RuleUpdateRequest.cs b/Iceshrimp.Shared/Schemas/Web/RuleUpdateRequest.cs new file mode 100644 index 00000000..763c45e5 --- /dev/null +++ b/Iceshrimp.Shared/Schemas/Web/RuleUpdateRequest.cs @@ -0,0 +1,8 @@ +namespace Iceshrimp.Shared.Schemas.Web; + +public class RuleUpdateRequest +{ + public int? Order { get; set; } + public string? Text { get; set; } + public string? Description { get; set; } +}