feat: upgrade to .NET 6, refactor everything
This commit is contained in:
@@ -1,34 +1,32 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
namespace PluralKit.API;
|
||||
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("v{version:apiVersion}/a")]
|
||||
public class AccountController: ControllerBase
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("v{version:apiVersion}/a")]
|
||||
public class AccountController: ControllerBase
|
||||
private readonly IDatabase _db;
|
||||
private readonly ModelRepository _repo;
|
||||
|
||||
public AccountController(IDatabase db, ModelRepository repo)
|
||||
{
|
||||
private readonly IDatabase _db;
|
||||
private readonly ModelRepository _repo;
|
||||
public AccountController(IDatabase db, ModelRepository repo)
|
||||
{
|
||||
_db = db;
|
||||
_repo = repo;
|
||||
}
|
||||
_db = db;
|
||||
_repo = repo;
|
||||
}
|
||||
|
||||
[HttpGet("{aid}")]
|
||||
public async Task<ActionResult<JObject>> GetSystemByAccount(ulong aid)
|
||||
{
|
||||
var system = await _repo.GetSystemByAccount(aid);
|
||||
if (system == null)
|
||||
return NotFound("Account not found.");
|
||||
[HttpGet("{aid}")]
|
||||
public async Task<ActionResult<JObject>> GetSystemByAccount(ulong aid)
|
||||
{
|
||||
var system = await _repo.GetSystemByAccount(aid);
|
||||
if (system == null)
|
||||
return NotFound("Account not found.");
|
||||
|
||||
return Ok(system.ToJson(User.ContextFor(system)));
|
||||
}
|
||||
return Ok(system.ToJson(User.ContextFor(system)));
|
||||
}
|
||||
}
|
@@ -1,6 +1,3 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dapper;
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@@ -10,117 +7,115 @@ using Newtonsoft.Json.Linq;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
namespace PluralKit.API;
|
||||
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("v{version:apiVersion}/m")]
|
||||
public class MemberController: ControllerBase
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("v{version:apiVersion}/m")]
|
||||
public class MemberController: ControllerBase
|
||||
private readonly IDatabase _db;
|
||||
private readonly ModelRepository _repo;
|
||||
private readonly IAuthorizationService _auth;
|
||||
|
||||
public MemberController(IAuthorizationService auth, IDatabase db, ModelRepository repo)
|
||||
{
|
||||
private readonly IDatabase _db;
|
||||
private readonly ModelRepository _repo;
|
||||
private readonly IAuthorizationService _auth;
|
||||
_auth = auth;
|
||||
_db = db;
|
||||
_repo = repo;
|
||||
}
|
||||
|
||||
public MemberController(IAuthorizationService auth, IDatabase db, ModelRepository repo)
|
||||
[HttpGet("{hid}")]
|
||||
public async Task<ActionResult<JObject>> GetMember(string hid)
|
||||
{
|
||||
var member = await _repo.GetMemberByHid(hid);
|
||||
if (member == null) return NotFound("Member not found.");
|
||||
|
||||
return Ok(member.ToJson(User.ContextFor(member), true));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<JObject>> PostMember([FromBody] JObject properties)
|
||||
{
|
||||
if (!properties.ContainsKey("name"))
|
||||
return BadRequest("Member name must be specified.");
|
||||
|
||||
var systemId = User.CurrentSystem();
|
||||
var systemData = await _repo.GetSystem(systemId);
|
||||
|
||||
await using var conn = await _db.Obtain();
|
||||
|
||||
// Enforce per-system member limit
|
||||
var memberCount = await conn.QuerySingleAsync<int>("select count(*) from members where system = @System",
|
||||
new { System = systemId });
|
||||
var memberLimit = systemData?.MemberLimitOverride ?? Limits.MaxMemberCount;
|
||||
if (memberCount >= memberLimit)
|
||||
return BadRequest($"Member limit reached ({memberCount} / {memberLimit}).");
|
||||
|
||||
await using var tx = await conn.BeginTransactionAsync();
|
||||
var member = await _repo.CreateMember(systemId, properties.Value<string>("name"), conn);
|
||||
|
||||
var patch = MemberPatch.FromJSON(properties);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
{
|
||||
_auth = auth;
|
||||
_db = db;
|
||||
_repo = repo;
|
||||
await tx.RollbackAsync();
|
||||
|
||||
var err = patch.Errors[0];
|
||||
if (err is FieldTooLongError)
|
||||
return BadRequest($"Field {err.Key} is too long "
|
||||
+ $"({(err as FieldTooLongError).ActualLength} > {(err as FieldTooLongError).MaxLength}).");
|
||||
if (err.Text != null)
|
||||
return BadRequest(err.Text);
|
||||
return BadRequest($"Field {err.Key} is invalid.");
|
||||
}
|
||||
|
||||
[HttpGet("{hid}")]
|
||||
public async Task<ActionResult<JObject>> GetMember(string hid)
|
||||
{
|
||||
var member = await _repo.GetMemberByHid(hid);
|
||||
if (member == null) return NotFound("Member not found.");
|
||||
member = await _repo.UpdateMember(member.Id, patch, conn);
|
||||
await tx.CommitAsync();
|
||||
return Ok(member.ToJson(User.ContextFor(member), true));
|
||||
}
|
||||
|
||||
return Ok(member.ToJson(User.ContextFor(member), needsLegacyProxyTags: true));
|
||||
[HttpPatch("{hid}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<JObject>> PatchMember(string hid, [FromBody] JObject changes)
|
||||
{
|
||||
var member = await _repo.GetMemberByHid(hid);
|
||||
if (member == null) return NotFound("Member not found.");
|
||||
|
||||
var res = await _auth.AuthorizeAsync(User, member, "EditMember");
|
||||
if (!res.Succeeded) return Unauthorized($"Member '{hid}' is not part of your system.");
|
||||
|
||||
var patch = MemberPatch.FromJSON(changes);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
{
|
||||
var err = patch.Errors[0];
|
||||
if (err is FieldTooLongError)
|
||||
return BadRequest($"Field {err.Key} is too long "
|
||||
+ $"({(err as FieldTooLongError).ActualLength} > {(err as FieldTooLongError).MaxLength}).");
|
||||
if (err.Text != null)
|
||||
return BadRequest(err.Text);
|
||||
return BadRequest($"Field {err.Key} is invalid.");
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<JObject>> PostMember([FromBody] JObject properties)
|
||||
{
|
||||
if (!properties.ContainsKey("name"))
|
||||
return BadRequest("Member name must be specified.");
|
||||
var newMember = await _repo.UpdateMember(member.Id, patch);
|
||||
return Ok(newMember.ToJson(User.ContextFor(newMember), true));
|
||||
}
|
||||
|
||||
var systemId = User.CurrentSystem();
|
||||
var systemData = await _repo.GetSystem(systemId);
|
||||
[HttpDelete("{hid}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult> DeleteMember(string hid)
|
||||
{
|
||||
var member = await _repo.GetMemberByHid(hid);
|
||||
if (member == null) return NotFound("Member not found.");
|
||||
|
||||
await using var conn = await _db.Obtain();
|
||||
var res = await _auth.AuthorizeAsync(User, member, "EditMember");
|
||||
if (!res.Succeeded) return Unauthorized($"Member '{hid}' is not part of your system.");
|
||||
|
||||
// Enforce per-system member limit
|
||||
var memberCount = await conn.QuerySingleAsync<int>("select count(*) from members where system = @System", new { System = systemId });
|
||||
var memberLimit = systemData?.MemberLimitOverride ?? Limits.MaxMemberCount;
|
||||
if (memberCount >= memberLimit)
|
||||
return BadRequest($"Member limit reached ({memberCount} / {memberLimit}).");
|
||||
|
||||
await using var tx = await conn.BeginTransactionAsync();
|
||||
var member = await _repo.CreateMember(systemId, properties.Value<string>("name"), conn);
|
||||
|
||||
var patch = MemberPatch.FromJSON(properties);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
{
|
||||
await tx.RollbackAsync();
|
||||
|
||||
var err = patch.Errors[0];
|
||||
if (err is FieldTooLongError)
|
||||
return BadRequest($"Field {err.Key} is too long "
|
||||
+ $"({(err as FieldTooLongError).ActualLength} > {(err as FieldTooLongError).MaxLength}).");
|
||||
else if (err.Text != null)
|
||||
return BadRequest(err.Text);
|
||||
else
|
||||
return BadRequest($"Field {err.Key} is invalid.");
|
||||
}
|
||||
|
||||
member = await _repo.UpdateMember(member.Id, patch, conn);
|
||||
await tx.CommitAsync();
|
||||
return Ok(member.ToJson(User.ContextFor(member), needsLegacyProxyTags: true));
|
||||
}
|
||||
|
||||
[HttpPatch("{hid}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<JObject>> PatchMember(string hid, [FromBody] JObject changes)
|
||||
{
|
||||
var member = await _repo.GetMemberByHid(hid);
|
||||
if (member == null) return NotFound("Member not found.");
|
||||
|
||||
var res = await _auth.AuthorizeAsync(User, member, "EditMember");
|
||||
if (!res.Succeeded) return Unauthorized($"Member '{hid}' is not part of your system.");
|
||||
|
||||
var patch = MemberPatch.FromJSON(changes);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
{
|
||||
var err = patch.Errors[0];
|
||||
if (err is FieldTooLongError)
|
||||
return BadRequest($"Field {err.Key} is too long "
|
||||
+ $"({(err as FieldTooLongError).ActualLength} > {(err as FieldTooLongError).MaxLength}).");
|
||||
else if (err.Text != null)
|
||||
return BadRequest(err.Text);
|
||||
else
|
||||
return BadRequest($"Field {err.Key} is invalid.");
|
||||
}
|
||||
|
||||
var newMember = await _repo.UpdateMember(member.Id, patch);
|
||||
return Ok(newMember.ToJson(User.ContextFor(newMember), needsLegacyProxyTags: true));
|
||||
}
|
||||
|
||||
[HttpDelete("{hid}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult> DeleteMember(string hid)
|
||||
{
|
||||
var member = await _repo.GetMemberByHid(hid);
|
||||
if (member == null) return NotFound("Member not found.");
|
||||
|
||||
var res = await _auth.AuthorizeAsync(User, member, "EditMember");
|
||||
if (!res.Succeeded) return Unauthorized($"Member '{hid}' is not part of your system.");
|
||||
|
||||
await _repo.DeleteMember(member.Id);
|
||||
return Ok();
|
||||
}
|
||||
await _repo.DeleteMember(member.Id);
|
||||
return Ok();
|
||||
}
|
||||
}
|
@@ -1,37 +1,31 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using NodaTime;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
namespace PluralKit.API;
|
||||
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("v{version:apiVersion}/msg")]
|
||||
public class MessageController: ControllerBase
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("v{version:apiVersion}/msg")]
|
||||
public class MessageController: ControllerBase
|
||||
private readonly IDatabase _db;
|
||||
private readonly ModelRepository _repo;
|
||||
|
||||
public MessageController(ModelRepository repo, IDatabase db)
|
||||
{
|
||||
private readonly IDatabase _db;
|
||||
private readonly ModelRepository _repo;
|
||||
_repo = repo;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public MessageController(ModelRepository repo, IDatabase db)
|
||||
{
|
||||
_repo = repo;
|
||||
_db = db;
|
||||
}
|
||||
[HttpGet("{mid}")]
|
||||
public async Task<ActionResult<JObject>> GetMessage(ulong mid)
|
||||
{
|
||||
var msg = await _db.Execute(c => _repo.GetMessage(c, mid));
|
||||
if (msg == null) return NotFound("Message not found.");
|
||||
|
||||
[HttpGet("{mid}")]
|
||||
public async Task<ActionResult<JObject>> GetMessage(ulong mid)
|
||||
{
|
||||
var msg = await _db.Execute(c => _repo.GetMessage(c, mid));
|
||||
if (msg == null) return NotFound("Message not found.");
|
||||
|
||||
return msg.ToJson(User.ContextFor(msg.System), APIVersion.V1);
|
||||
}
|
||||
return msg.ToJson(User.ContextFor(msg.System), APIVersion.V1);
|
||||
}
|
||||
}
|
@@ -1,40 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using System.Linq;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
namespace PluralKit.API;
|
||||
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("v{version:apiVersion}")]
|
||||
public class MetaController: ControllerBase
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("v{version:apiVersion}")]
|
||||
public class MetaController: ControllerBase
|
||||
private readonly IDatabase _db;
|
||||
private readonly ModelRepository _repo;
|
||||
|
||||
public MetaController(IDatabase db, ModelRepository repo)
|
||||
{
|
||||
private readonly IDatabase _db;
|
||||
private readonly ModelRepository _repo;
|
||||
public MetaController(IDatabase db, ModelRepository repo)
|
||||
{
|
||||
_db = db;
|
||||
_repo = repo;
|
||||
}
|
||||
_db = db;
|
||||
_repo = repo;
|
||||
}
|
||||
|
||||
[HttpGet("meta")]
|
||||
public async Task<ActionResult<JObject>> GetMeta()
|
||||
{
|
||||
await using var conn = await _db.Obtain();
|
||||
var shards = await _repo.GetShards();
|
||||
[HttpGet("meta")]
|
||||
public async Task<ActionResult<JObject>> GetMeta()
|
||||
{
|
||||
await using var conn = await _db.Obtain();
|
||||
var shards = await _repo.GetShards();
|
||||
|
||||
var o = new JObject();
|
||||
o.Add("shards", shards.ToJSON());
|
||||
o.Add("version", BuildInfoService.Version);
|
||||
var o = new JObject();
|
||||
o.Add("shards", shards.ToJSON());
|
||||
o.Add("version", BuildInfoService.Version);
|
||||
|
||||
return Ok(o);
|
||||
}
|
||||
return Ok(o);
|
||||
}
|
||||
}
|
@@ -1,12 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dapper;
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
@@ -16,183 +10,189 @@ using NodaTime;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
namespace PluralKit.API;
|
||||
|
||||
public struct SwitchesReturn
|
||||
{
|
||||
public struct SwitchesReturn
|
||||
[JsonProperty("timestamp")] public Instant Timestamp { get; set; }
|
||||
[JsonProperty("members")] public IEnumerable<string> Members { get; set; }
|
||||
}
|
||||
|
||||
public struct FrontersReturn
|
||||
{
|
||||
[JsonProperty("timestamp")] public Instant Timestamp { get; set; }
|
||||
[JsonProperty("members")] public IEnumerable<JObject> Members { get; set; }
|
||||
}
|
||||
|
||||
public struct PostSwitchParams
|
||||
{
|
||||
public Instant? Timestamp { get; set; }
|
||||
public ICollection<string> Members { get; set; }
|
||||
}
|
||||
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("v{version:apiVersion}/s")]
|
||||
public class SystemController: ControllerBase
|
||||
{
|
||||
private readonly IDatabase _db;
|
||||
private readonly ModelRepository _repo;
|
||||
private readonly IAuthorizationService _auth;
|
||||
|
||||
public SystemController(IDatabase db, IAuthorizationService auth, ModelRepository repo)
|
||||
{
|
||||
[JsonProperty("timestamp")] public Instant Timestamp { get; set; }
|
||||
[JsonProperty("members")] public IEnumerable<string> Members { get; set; }
|
||||
_db = db;
|
||||
_auth = auth;
|
||||
_repo = repo;
|
||||
}
|
||||
|
||||
public struct FrontersReturn
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<JObject>> GetOwnSystem()
|
||||
{
|
||||
[JsonProperty("timestamp")] public Instant Timestamp { get; set; }
|
||||
[JsonProperty("members")] public IEnumerable<JObject> Members { get; set; }
|
||||
var system = await _repo.GetSystem(User.CurrentSystem());
|
||||
return system.ToJson(User.ContextFor(system));
|
||||
}
|
||||
|
||||
public struct PostSwitchParams
|
||||
[HttpGet("{hid}")]
|
||||
public async Task<ActionResult<JObject>> GetSystem(string hid)
|
||||
{
|
||||
public Instant? Timestamp { get; set; }
|
||||
public ICollection<string> Members { get; set; }
|
||||
var system = await _repo.GetSystemByHid(hid);
|
||||
if (system == null) return NotFound("System not found.");
|
||||
return Ok(system.ToJson(User.ContextFor(system)));
|
||||
}
|
||||
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("v{version:apiVersion}/s")]
|
||||
public class SystemController: ControllerBase
|
||||
[HttpGet("{hid}/members")]
|
||||
public async Task<ActionResult<IEnumerable<JObject>>> GetMembers(string hid)
|
||||
{
|
||||
private readonly IDatabase _db;
|
||||
private readonly ModelRepository _repo;
|
||||
private readonly IAuthorizationService _auth;
|
||||
var system = await _repo.GetSystemByHid(hid);
|
||||
if (system == null)
|
||||
return NotFound("System not found.");
|
||||
|
||||
public SystemController(IDatabase db, IAuthorizationService auth, ModelRepository repo)
|
||||
if (!system.MemberListPrivacy.CanAccess(User.ContextFor(system)))
|
||||
return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view member list.");
|
||||
|
||||
var members = _repo.GetSystemMembers(system.Id);
|
||||
return Ok(await members
|
||||
.Where(m => m.MemberVisibility.CanAccess(User.ContextFor(system)))
|
||||
.Select(m => m.ToJson(User.ContextFor(system), needsLegacyProxyTags: true))
|
||||
.ToListAsync());
|
||||
}
|
||||
|
||||
[HttpGet("{hid}/switches")]
|
||||
public async Task<ActionResult<IEnumerable<SwitchesReturn>>> GetSwitches(
|
||||
string hid, [FromQuery(Name = "before")] Instant? before)
|
||||
{
|
||||
if (before == null) before = SystemClock.Instance.GetCurrentInstant();
|
||||
|
||||
var system = await _repo.GetSystemByHid(hid);
|
||||
if (system == null) return NotFound("System not found.");
|
||||
|
||||
var auth = await _auth.AuthorizeAsync(User, system, "ViewFrontHistory");
|
||||
if (!auth.Succeeded)
|
||||
return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view front history.");
|
||||
|
||||
var res = await _db.Execute(conn => conn.QueryAsync<SwitchesReturn>(
|
||||
@"select *, array(
|
||||
select members.hid from switch_members, members
|
||||
where switch_members.switch = switches.id and members.id = switch_members.member
|
||||
) as members from switches
|
||||
where switches.system = @System and switches.timestamp < @Before
|
||||
order by switches.timestamp desc
|
||||
limit 100;",
|
||||
new { System = system.Id, Before = before }
|
||||
));
|
||||
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
[HttpGet("{hid}/fronters")]
|
||||
public async Task<ActionResult<FrontersReturn>> GetFronters(string hid)
|
||||
{
|
||||
var system = await _repo.GetSystemByHid(hid);
|
||||
if (system == null) return NotFound("System not found.");
|
||||
|
||||
var auth = await _auth.AuthorizeAsync(User, system, "ViewFront");
|
||||
if (!auth.Succeeded) return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view fronter.");
|
||||
|
||||
var sw = await _repo.GetLatestSwitch(system.Id);
|
||||
if (sw == null) return NotFound("System has no registered switches.");
|
||||
|
||||
var members = _db.Execute(conn => _repo.GetSwitchMembers(conn, sw.Id));
|
||||
return Ok(new FrontersReturn
|
||||
{
|
||||
_db = db;
|
||||
_auth = auth;
|
||||
_repo = repo;
|
||||
Timestamp = sw.Timestamp,
|
||||
Members = await members.Select(m => m.ToJson(User.ContextFor(system), true)).ToListAsync()
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPatch]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<JObject>> EditSystem([FromBody] JObject changes)
|
||||
{
|
||||
var system = await _repo.GetSystem(User.CurrentSystem());
|
||||
|
||||
var patch = SystemPatch.FromJSON(changes);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
{
|
||||
var err = patch.Errors[0];
|
||||
if (err is FieldTooLongError)
|
||||
return BadRequest($"Field {err.Key} is too long "
|
||||
+ $"({(err as FieldTooLongError).ActualLength} > {(err as FieldTooLongError).MaxLength}).");
|
||||
|
||||
return BadRequest($"Field {err.Key} is invalid.");
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<JObject>> GetOwnSystem()
|
||||
system = await _repo.UpdateSystem(system!.Id, patch);
|
||||
return Ok(system.ToJson(User.ContextFor(system)));
|
||||
}
|
||||
|
||||
[HttpPost("switches")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> PostSwitch([FromBody] PostSwitchParams param)
|
||||
{
|
||||
if (param.Members.Distinct().Count() != param.Members.Count)
|
||||
return BadRequest("Duplicate members in member list.");
|
||||
|
||||
await using var conn = await _db.Obtain();
|
||||
|
||||
// We get the current switch, if it exists
|
||||
var latestSwitch = await _repo.GetLatestSwitch(User.CurrentSystem());
|
||||
if (latestSwitch != null)
|
||||
{
|
||||
var system = await _repo.GetSystem(User.CurrentSystem());
|
||||
return system.ToJson(User.ContextFor(system));
|
||||
var latestSwitchMembers = _repo.GetSwitchMembers(conn, latestSwitch.Id);
|
||||
|
||||
// Bail if this switch is identical to the latest one
|
||||
if (await latestSwitchMembers.Select(m => m.Hid).SequenceEqualAsync(param.Members.ToAsyncEnumerable()))
|
||||
return BadRequest("New members identical to existing fronters.");
|
||||
}
|
||||
|
||||
[HttpGet("{hid}")]
|
||||
public async Task<ActionResult<JObject>> GetSystem(string hid)
|
||||
// Resolve member objects for all given IDs
|
||||
var membersList =
|
||||
(await conn.QueryAsync<PKMember>("select * from members where hid = any(@Hids)",
|
||||
new { Hids = param.Members })).ToList();
|
||||
|
||||
foreach (var member in membersList)
|
||||
if (member.System != User.CurrentSystem())
|
||||
return BadRequest($"Cannot switch to member '{member.Hid}' not in system.");
|
||||
|
||||
// membersList is in DB order, and we want it in actual input order
|
||||
// so we go through a dict and map the original input appropriately
|
||||
var membersDict = membersList.ToDictionary(m => m.Hid);
|
||||
|
||||
var membersInOrder = new List<PKMember>();
|
||||
// We do this without .Select() since we want to have the early return bail if it doesn't find the member
|
||||
foreach (var givenMemberId in param.Members)
|
||||
{
|
||||
var system = await _repo.GetSystemByHid(hid);
|
||||
if (system == null) return NotFound("System not found.");
|
||||
return Ok(system.ToJson(User.ContextFor(system)));
|
||||
if (!membersDict.TryGetValue(givenMemberId, out var member))
|
||||
return BadRequest($"Member '{givenMemberId}' not found.");
|
||||
membersInOrder.Add(member);
|
||||
}
|
||||
|
||||
[HttpGet("{hid}/members")]
|
||||
public async Task<ActionResult<IEnumerable<JObject>>> GetMembers(string hid)
|
||||
{
|
||||
var system = await _repo.GetSystemByHid(hid);
|
||||
if (system == null)
|
||||
return NotFound("System not found.");
|
||||
|
||||
if (!system.MemberListPrivacy.CanAccess(User.ContextFor(system)))
|
||||
return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view member list.");
|
||||
|
||||
var members = _repo.GetSystemMembers(system.Id);
|
||||
return Ok(await members
|
||||
.Where(m => m.MemberVisibility.CanAccess(User.ContextFor(system)))
|
||||
.Select(m => m.ToJson(User.ContextFor(system), needsLegacyProxyTags: true))
|
||||
.ToListAsync());
|
||||
}
|
||||
|
||||
[HttpGet("{hid}/switches")]
|
||||
public async Task<ActionResult<IEnumerable<SwitchesReturn>>> GetSwitches(string hid, [FromQuery(Name = "before")] Instant? before)
|
||||
{
|
||||
if (before == null) before = SystemClock.Instance.GetCurrentInstant();
|
||||
|
||||
var system = await _repo.GetSystemByHid(hid);
|
||||
if (system == null) return NotFound("System not found.");
|
||||
|
||||
var auth = await _auth.AuthorizeAsync(User, system, "ViewFrontHistory");
|
||||
if (!auth.Succeeded) return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view front history.");
|
||||
|
||||
var res = await _db.Execute(conn => conn.QueryAsync<SwitchesReturn>(
|
||||
@"select *, array(
|
||||
select members.hid from switch_members, members
|
||||
where switch_members.switch = switches.id and members.id = switch_members.member
|
||||
) as members from switches
|
||||
where switches.system = @System and switches.timestamp < @Before
|
||||
order by switches.timestamp desc
|
||||
limit 100;", new { System = system.Id, Before = before }));
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
[HttpGet("{hid}/fronters")]
|
||||
public async Task<ActionResult<FrontersReturn>> GetFronters(string hid)
|
||||
{
|
||||
var system = await _repo.GetSystemByHid(hid);
|
||||
if (system == null) return NotFound("System not found.");
|
||||
|
||||
var auth = await _auth.AuthorizeAsync(User, system, "ViewFront");
|
||||
if (!auth.Succeeded) return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view fronter.");
|
||||
|
||||
var sw = await _repo.GetLatestSwitch(system.Id);
|
||||
if (sw == null) return NotFound("System has no registered switches.");
|
||||
|
||||
var members = _db.Execute(conn => _repo.GetSwitchMembers(conn, sw.Id));
|
||||
return Ok(new FrontersReturn
|
||||
{
|
||||
Timestamp = sw.Timestamp,
|
||||
Members = await members.Select(m => m.ToJson(User.ContextFor(system), needsLegacyProxyTags: true)).ToListAsync()
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPatch]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<JObject>> EditSystem([FromBody] JObject changes)
|
||||
{
|
||||
var system = await _repo.GetSystem(User.CurrentSystem());
|
||||
|
||||
var patch = SystemPatch.FromJSON(changes);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
{
|
||||
var err = patch.Errors[0];
|
||||
if (err is FieldTooLongError)
|
||||
return BadRequest($"Field {err.Key} is too long "
|
||||
+ $"({(err as FieldTooLongError).ActualLength} > {(err as FieldTooLongError).MaxLength}).");
|
||||
|
||||
return BadRequest($"Field {err.Key} is invalid.");
|
||||
}
|
||||
|
||||
system = await _repo.UpdateSystem(system!.Id, patch);
|
||||
return Ok(system.ToJson(User.ContextFor(system)));
|
||||
}
|
||||
|
||||
[HttpPost("switches")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> PostSwitch([FromBody] PostSwitchParams param)
|
||||
{
|
||||
if (param.Members.Distinct().Count() != param.Members.Count)
|
||||
return BadRequest("Duplicate members in member list.");
|
||||
|
||||
await using var conn = await _db.Obtain();
|
||||
|
||||
// We get the current switch, if it exists
|
||||
var latestSwitch = await _repo.GetLatestSwitch(User.CurrentSystem());
|
||||
if (latestSwitch != null)
|
||||
{
|
||||
var latestSwitchMembers = _repo.GetSwitchMembers(conn, latestSwitch.Id);
|
||||
|
||||
// Bail if this switch is identical to the latest one
|
||||
if (await latestSwitchMembers.Select(m => m.Hid).SequenceEqualAsync(param.Members.ToAsyncEnumerable()))
|
||||
return BadRequest("New members identical to existing fronters.");
|
||||
}
|
||||
|
||||
// Resolve member objects for all given IDs
|
||||
var membersList = (await conn.QueryAsync<PKMember>("select * from members where hid = any(@Hids)", new { Hids = param.Members })).ToList();
|
||||
|
||||
foreach (var member in membersList)
|
||||
if (member.System != User.CurrentSystem())
|
||||
return BadRequest($"Cannot switch to member '{member.Hid}' not in system.");
|
||||
|
||||
// membersList is in DB order, and we want it in actual input order
|
||||
// so we go through a dict and map the original input appropriately
|
||||
var membersDict = membersList.ToDictionary(m => m.Hid);
|
||||
|
||||
var membersInOrder = new List<PKMember>();
|
||||
// We do this without .Select() since we want to have the early return bail if it doesn't find the member
|
||||
foreach (var givenMemberId in param.Members)
|
||||
{
|
||||
if (!membersDict.TryGetValue(givenMemberId, out var member))
|
||||
return BadRequest($"Member '{givenMemberId}' not found.");
|
||||
membersInOrder.Add(member);
|
||||
}
|
||||
|
||||
// Finally, log the switch (yay!)
|
||||
await _repo.AddSwitch(conn, User.CurrentSystem(), membersInOrder.Select(m => m.Id).ToList());
|
||||
return NoContent();
|
||||
}
|
||||
// Finally, log the switch (yay!)
|
||||
await _repo.AddSwitch(conn, User.CurrentSystem(), membersInOrder.Select(m => m.Id).ToList());
|
||||
return NoContent();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user