feat: upgrade to .NET 6, refactor everything
This commit is contained in:
@@ -1,136 +1,134 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using NodaTime;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
namespace PluralKit.API;
|
||||
|
||||
[ApiController]
|
||||
[ApiVersion("2.0")]
|
||||
[Route("v{version:apiVersion}")]
|
||||
public class DiscordControllerV2: PKControllerBase
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("2.0")]
|
||||
[Route("v{version:apiVersion}")]
|
||||
public class DiscordControllerV2: PKControllerBase
|
||||
public DiscordControllerV2(IServiceProvider svc) : base(svc) { }
|
||||
|
||||
|
||||
[HttpGet("systems/@me/guilds/{guild_id}")]
|
||||
public async Task<IActionResult> SystemGuildGet(ulong guild_id)
|
||||
{
|
||||
public DiscordControllerV2(IServiceProvider svc) : base(svc) { }
|
||||
var system = await ResolveSystem("@me");
|
||||
var settings = await _repo.GetSystemGuild(guild_id, system.Id, false);
|
||||
if (settings == null)
|
||||
throw Errors.SystemGuildNotFound;
|
||||
|
||||
PKMember member = null;
|
||||
if (settings.AutoproxyMember != null)
|
||||
member = await _repo.GetMember(settings.AutoproxyMember.Value);
|
||||
|
||||
[HttpGet("systems/@me/guilds/{guild_id}")]
|
||||
public async Task<IActionResult> SystemGuildGet(ulong guild_id)
|
||||
return Ok(settings.ToJson(member?.Hid));
|
||||
}
|
||||
|
||||
[HttpPatch("systems/@me/guilds/{guild_id}")]
|
||||
public async Task<IActionResult> DoSystemGuildPatch(ulong guild_id, [FromBody] JObject data)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
var settings = await _repo.GetSystemGuild(guild_id, system.Id, false);
|
||||
if (settings == null)
|
||||
throw Errors.SystemGuildNotFound;
|
||||
|
||||
MemberId? memberId = null;
|
||||
if (data.ContainsKey("autoproxy_member"))
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
var settings = await _repo.GetSystemGuild(guild_id, system.Id, defaultInsert: false);
|
||||
if (settings == null)
|
||||
throw Errors.SystemGuildNotFound;
|
||||
|
||||
PKMember member = null;
|
||||
if (settings.AutoproxyMember != null)
|
||||
member = await _repo.GetMember(settings.AutoproxyMember.Value);
|
||||
|
||||
return Ok(settings.ToJson(member?.Hid));
|
||||
}
|
||||
|
||||
[HttpPatch("systems/@me/guilds/{guild_id}")]
|
||||
public async Task<IActionResult> DoSystemGuildPatch(ulong guild_id, [FromBody] JObject data)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
var settings = await _repo.GetSystemGuild(guild_id, system.Id, defaultInsert: false);
|
||||
if (settings == null)
|
||||
throw Errors.SystemGuildNotFound;
|
||||
|
||||
MemberId? memberId = null;
|
||||
if (data.ContainsKey("autoproxy_member"))
|
||||
if (data["autoproxy_member"].Type != JTokenType.Null)
|
||||
{
|
||||
if (data["autoproxy_member"].Type != JTokenType.Null)
|
||||
{
|
||||
var member = await ResolveMember(data.Value<string>("autoproxy_member"));
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
var member = await ResolveMember(data.Value<string>("autoproxy_member"));
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
|
||||
memberId = member.Id;
|
||||
}
|
||||
memberId = member.Id;
|
||||
}
|
||||
else
|
||||
memberId = settings.AutoproxyMember;
|
||||
}
|
||||
else
|
||||
{
|
||||
memberId = settings.AutoproxyMember;
|
||||
}
|
||||
|
||||
var patch = SystemGuildPatch.FromJson(data, memberId);
|
||||
var patch = SystemGuildPatch.FromJson(data, memberId);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
|
||||
// this is less than great, but at least it's legible
|
||||
if (patch.AutoproxyMember.Value == null)
|
||||
if (patch.AutoproxyMode.IsPresent)
|
||||
{
|
||||
if (patch.AutoproxyMode.Value == AutoproxyMode.Member)
|
||||
throw Errors.MissingAutoproxyMember;
|
||||
}
|
||||
else if (settings.AutoproxyMode == AutoproxyMode.Member)
|
||||
// this is less than great, but at least it's legible
|
||||
if (patch.AutoproxyMember.Value == null)
|
||||
if (patch.AutoproxyMode.IsPresent)
|
||||
{
|
||||
if (patch.AutoproxyMode.Value == AutoproxyMode.Member)
|
||||
throw Errors.MissingAutoproxyMember;
|
||||
}
|
||||
else if (settings.AutoproxyMode == AutoproxyMode.Member)
|
||||
{
|
||||
throw Errors.MissingAutoproxyMember;
|
||||
}
|
||||
|
||||
var newSettings = await _repo.UpdateSystemGuild(system.Id, guild_id, patch);
|
||||
var newSettings = await _repo.UpdateSystemGuild(system.Id, guild_id, patch);
|
||||
|
||||
PKMember? newMember = null;
|
||||
if (newSettings.AutoproxyMember != null)
|
||||
newMember = await _repo.GetMember(newSettings.AutoproxyMember.Value);
|
||||
return Ok(newSettings.ToJson(newMember?.Hid));
|
||||
}
|
||||
PKMember? newMember = null;
|
||||
if (newSettings.AutoproxyMember != null)
|
||||
newMember = await _repo.GetMember(newSettings.AutoproxyMember.Value);
|
||||
return Ok(newSettings.ToJson(newMember?.Hid));
|
||||
}
|
||||
|
||||
[HttpGet("members/{memberRef}/guilds/{guild_id}")]
|
||||
public async Task<IActionResult> MemberGuildGet(string memberRef, ulong guild_id)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberError;
|
||||
[HttpGet("members/{memberRef}/guilds/{guild_id}")]
|
||||
public async Task<IActionResult> MemberGuildGet(string memberRef, ulong guild_id)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberError;
|
||||
|
||||
var settings = await _repo.GetMemberGuild(guild_id, member.Id, defaultInsert: false);
|
||||
if (settings == null)
|
||||
throw Errors.MemberGuildNotFound;
|
||||
var settings = await _repo.GetMemberGuild(guild_id, member.Id, false);
|
||||
if (settings == null)
|
||||
throw Errors.MemberGuildNotFound;
|
||||
|
||||
return Ok(settings.ToJson());
|
||||
}
|
||||
return Ok(settings.ToJson());
|
||||
}
|
||||
|
||||
[HttpPatch("members/{memberRef}/guilds/{guild_id}")]
|
||||
public async Task<IActionResult> DoMemberGuildPatch(string memberRef, ulong guild_id, [FromBody] JObject data)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberError;
|
||||
[HttpPatch("members/{memberRef}/guilds/{guild_id}")]
|
||||
public async Task<IActionResult> DoMemberGuildPatch(string memberRef, ulong guild_id, [FromBody] JObject data)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberError;
|
||||
|
||||
var settings = await _repo.GetMemberGuild(guild_id, member.Id, defaultInsert: false);
|
||||
if (settings == null)
|
||||
throw Errors.MemberGuildNotFound;
|
||||
var settings = await _repo.GetMemberGuild(guild_id, member.Id, false);
|
||||
if (settings == null)
|
||||
throw Errors.MemberGuildNotFound;
|
||||
|
||||
var patch = MemberGuildPatch.FromJson(data);
|
||||
var patch = MemberGuildPatch.FromJson(data);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
|
||||
var newSettings = await _repo.UpdateMemberGuild(member.Id, guild_id, patch);
|
||||
return Ok(newSettings.ToJson());
|
||||
}
|
||||
var newSettings = await _repo.UpdateMemberGuild(member.Id, guild_id, patch);
|
||||
return Ok(newSettings.ToJson());
|
||||
}
|
||||
|
||||
[HttpGet("messages/{messageId}")]
|
||||
public async Task<ActionResult<JObject>> MessageGet(ulong messageId)
|
||||
{
|
||||
var msg = await _db.Execute(c => _repo.GetMessage(c, messageId));
|
||||
if (msg == null)
|
||||
throw Errors.MessageNotFound;
|
||||
[HttpGet("messages/{messageId}")]
|
||||
public async Task<ActionResult<JObject>> MessageGet(ulong messageId)
|
||||
{
|
||||
var msg = await _db.Execute(c => _repo.GetMessage(c, messageId));
|
||||
if (msg == null)
|
||||
throw Errors.MessageNotFound;
|
||||
|
||||
var ctx = this.ContextFor(msg.System);
|
||||
return msg.ToJson(ctx, APIVersion.V2);
|
||||
}
|
||||
var ctx = ContextFor(msg.System);
|
||||
return msg.ToJson(ctx, APIVersion.V2);
|
||||
}
|
||||
}
|
@@ -1,142 +1,135 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
namespace PluralKit.API;
|
||||
|
||||
[ApiController]
|
||||
[ApiVersion("2.0")]
|
||||
[Route("v{version:apiVersion}")]
|
||||
public class GroupControllerV2: PKControllerBase
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("2.0")]
|
||||
[Route("v{version:apiVersion}")]
|
||||
public class GroupControllerV2: PKControllerBase
|
||||
public GroupControllerV2(IServiceProvider svc) : base(svc) { }
|
||||
|
||||
[HttpGet("systems/{systemRef}/groups")]
|
||||
public async Task<IActionResult> GetSystemGroups(string systemRef, [FromQuery] bool with_members)
|
||||
{
|
||||
public GroupControllerV2(IServiceProvider svc) : base(svc) { }
|
||||
var system = await ResolveSystem(systemRef);
|
||||
if (system == null)
|
||||
throw Errors.SystemNotFound;
|
||||
|
||||
[HttpGet("systems/{systemRef}/groups")]
|
||||
public async Task<IActionResult> GetSystemGroups(string systemRef, [FromQuery] bool with_members)
|
||||
var ctx = ContextFor(system);
|
||||
|
||||
if (with_members && !system.MemberListPrivacy.CanAccess(ctx))
|
||||
throw Errors.UnauthorizedMemberList;
|
||||
|
||||
if (!system.GroupListPrivacy.CanAccess(User.ContextFor(system)))
|
||||
throw Errors.UnauthorizedGroupList;
|
||||
|
||||
var groups = _repo.GetSystemGroups(system.Id);
|
||||
|
||||
var j_groups = await groups
|
||||
.Where(g => g.Visibility.CanAccess(ctx))
|
||||
.Select(g => g.ToJson(ctx, needsMembersArray: with_members))
|
||||
.ToListAsync();
|
||||
|
||||
if (with_members && !system.MemberListPrivacy.CanAccess(ctx))
|
||||
throw Errors.UnauthorizedMemberList;
|
||||
|
||||
if (with_members && j_groups.Count > 0)
|
||||
{
|
||||
var system = await ResolveSystem(systemRef);
|
||||
if (system == null)
|
||||
throw Errors.SystemNotFound;
|
||||
var q = await _repo.GetGroupMemberInfo(await groups.Select(x => x.Id).ToListAsync());
|
||||
|
||||
var ctx = this.ContextFor(system);
|
||||
|
||||
if (with_members && !system.MemberListPrivacy.CanAccess(ctx))
|
||||
throw Errors.UnauthorizedMemberList;
|
||||
|
||||
if (!system.GroupListPrivacy.CanAccess(User.ContextFor(system)))
|
||||
throw Errors.UnauthorizedGroupList;
|
||||
|
||||
var groups = _repo.GetSystemGroups(system.Id);
|
||||
|
||||
var j_groups = await groups
|
||||
.Where(g => g.Visibility.CanAccess(ctx))
|
||||
.Select(g => g.ToJson(ctx, needsMembersArray: with_members))
|
||||
.ToListAsync();
|
||||
|
||||
if (with_members && !system.MemberListPrivacy.CanAccess(ctx))
|
||||
throw Errors.UnauthorizedMemberList;
|
||||
|
||||
if (with_members && j_groups.Count > 0)
|
||||
{
|
||||
var q = await _repo.GetGroupMemberInfo(await groups.Select(x => x.Id).ToListAsync());
|
||||
|
||||
foreach (var row in q)
|
||||
if (row.MemberVisibility.CanAccess(ctx))
|
||||
((JArray)j_groups.Find(x => x.Value<string>("id") == row.Group)["members"]).Add(row.MemberUuid);
|
||||
}
|
||||
|
||||
return Ok(j_groups);
|
||||
foreach (var row in q)
|
||||
if (row.MemberVisibility.CanAccess(ctx))
|
||||
((JArray)j_groups.Find(x => x.Value<string>("id") == row.Group)["members"]).Add(row.MemberUuid);
|
||||
}
|
||||
|
||||
[HttpPost("groups")]
|
||||
public async Task<IActionResult> GroupCreate([FromBody] JObject data)
|
||||
return Ok(j_groups);
|
||||
}
|
||||
|
||||
[HttpPost("groups")]
|
||||
public async Task<IActionResult> GroupCreate([FromBody] JObject data)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
|
||||
// Check group cap
|
||||
var existingGroupCount = await _repo.GetSystemGroupCount(system.Id);
|
||||
var groupLimit = system.GroupLimitOverride ?? Limits.MaxGroupCount;
|
||||
if (existingGroupCount >= groupLimit)
|
||||
throw Errors.GroupLimitReached;
|
||||
|
||||
var patch = GroupPatch.FromJson(data);
|
||||
patch.AssertIsValid();
|
||||
if (!patch.Name.IsPresent)
|
||||
patch.Errors.Add(new ValidationError("name", "Key 'name' is required when creating new group."));
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
|
||||
using var conn = await _db.Obtain();
|
||||
using var tx = await conn.BeginTransactionAsync();
|
||||
|
||||
var newGroup = await _repo.CreateGroup(system.Id, patch.Name.Value, conn);
|
||||
newGroup = await _repo.UpdateGroup(newGroup.Id, patch, conn);
|
||||
|
||||
_ = _dispatch.Dispatch(newGroup.Id, new UpdateDispatchData()
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
Event = DispatchEvent.CREATE_GROUP,
|
||||
EventData = patch.ToJson(),
|
||||
});
|
||||
|
||||
// Check group cap
|
||||
var existingGroupCount = await _repo.GetSystemGroupCount(system.Id);
|
||||
var groupLimit = system.GroupLimitOverride ?? Limits.MaxGroupCount;
|
||||
if (existingGroupCount >= groupLimit)
|
||||
throw Errors.GroupLimitReached;
|
||||
await tx.CommitAsync();
|
||||
|
||||
var patch = GroupPatch.FromJson(data);
|
||||
patch.AssertIsValid();
|
||||
if (!patch.Name.IsPresent)
|
||||
patch.Errors.Add(new ValidationError("name", $"Key 'name' is required when creating new group."));
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
return Ok(newGroup.ToJson(LookupContext.ByOwner));
|
||||
}
|
||||
|
||||
using var conn = await _db.Obtain();
|
||||
using var tx = await conn.BeginTransactionAsync();
|
||||
[HttpGet("groups/{groupRef}")]
|
||||
public async Task<IActionResult> GroupGet(string groupRef)
|
||||
{
|
||||
var group = await ResolveGroup(groupRef);
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFound;
|
||||
|
||||
var newGroup = await _repo.CreateGroup(system.Id, patch.Name.Value, conn);
|
||||
newGroup = await _repo.UpdateGroup(newGroup.Id, patch, conn);
|
||||
var system = await _repo.GetSystem(group.System);
|
||||
|
||||
return Ok(group.ToJson(ContextFor(group), system.Hid));
|
||||
}
|
||||
|
||||
_ = _dispatch.Dispatch(newGroup.Id, new UpdateDispatchData()
|
||||
{
|
||||
Event = DispatchEvent.CREATE_GROUP,
|
||||
EventData = patch.ToJson(),
|
||||
});
|
||||
[HttpPatch("groups/{groupRef}")]
|
||||
public async Task<IActionResult> DoGroupPatch(string groupRef, [FromBody] JObject data)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
var group = await ResolveGroup(groupRef);
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFound;
|
||||
if (group.System != system.Id)
|
||||
throw Errors.NotOwnGroupError;
|
||||
|
||||
var patch = GroupPatch.FromJson(data);
|
||||
|
||||
await tx.CommitAsync();
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
|
||||
return Ok(newGroup.ToJson(LookupContext.ByOwner));
|
||||
}
|
||||
var newGroup = await _repo.UpdateGroup(group.Id, patch);
|
||||
return Ok(newGroup.ToJson(LookupContext.ByOwner));
|
||||
}
|
||||
|
||||
[HttpGet("groups/{groupRef}")]
|
||||
public async Task<IActionResult> GroupGet(string groupRef)
|
||||
{
|
||||
var group = await ResolveGroup(groupRef);
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFound;
|
||||
[HttpDelete("groups/{groupRef}")]
|
||||
public async Task<IActionResult> GroupDelete(string groupRef)
|
||||
{
|
||||
var group = await ResolveGroup(groupRef);
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFound;
|
||||
|
||||
var system = await _repo.GetSystem(group.System);
|
||||
var system = await ResolveSystem("@me");
|
||||
if (system.Id != group.System)
|
||||
throw Errors.NotOwnGroupError;
|
||||
|
||||
return Ok(group.ToJson(this.ContextFor(group), systemStr: system.Hid));
|
||||
}
|
||||
await _repo.DeleteGroup(group.Id);
|
||||
|
||||
[HttpPatch("groups/{groupRef}")]
|
||||
public async Task<IActionResult> DoGroupPatch(string groupRef, [FromBody] JObject data)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
var group = await ResolveGroup(groupRef);
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFound;
|
||||
if (group.System != system.Id)
|
||||
throw Errors.NotOwnGroupError;
|
||||
|
||||
var patch = GroupPatch.FromJson(data);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
|
||||
var newGroup = await _repo.UpdateGroup(group.Id, patch);
|
||||
return Ok(newGroup.ToJson(LookupContext.ByOwner));
|
||||
}
|
||||
|
||||
[HttpDelete("groups/{groupRef}")]
|
||||
public async Task<IActionResult> GroupDelete(string groupRef)
|
||||
{
|
||||
var group = await ResolveGroup(groupRef);
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFound;
|
||||
|
||||
var system = await ResolveSystem("@me");
|
||||
if (system.Id != group.System)
|
||||
throw Errors.NotOwnGroupError;
|
||||
|
||||
await _repo.DeleteGroup(group.Id);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
return NoContent();
|
||||
}
|
||||
}
|
@@ -1,281 +1,272 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dapper;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
namespace PluralKit.API;
|
||||
|
||||
[ApiController]
|
||||
[ApiVersion("2.0")]
|
||||
[Route("v{version:apiVersion}")]
|
||||
public class GroupMemberControllerV2: PKControllerBase
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("2.0")]
|
||||
[Route("v{version:apiVersion}")]
|
||||
public class GroupMemberControllerV2: PKControllerBase
|
||||
public GroupMemberControllerV2(IServiceProvider svc) : base(svc) { }
|
||||
|
||||
[HttpGet("groups/{groupRef}/members")]
|
||||
public async Task<IActionResult> GetGroupMembers(string groupRef)
|
||||
{
|
||||
public GroupMemberControllerV2(IServiceProvider svc) : base(svc) { }
|
||||
var group = await ResolveGroup(groupRef);
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFound;
|
||||
|
||||
[HttpGet("groups/{groupRef}/members")]
|
||||
public async Task<IActionResult> GetGroupMembers(string groupRef)
|
||||
var ctx = ContextFor(group);
|
||||
|
||||
if (!group.ListPrivacy.CanAccess(ctx))
|
||||
throw Errors.UnauthorizedGroupMemberList;
|
||||
|
||||
var members = _repo.GetGroupMembers(group.Id).Where(m => m.MemberVisibility.CanAccess(ctx));
|
||||
|
||||
var o = new JArray();
|
||||
|
||||
await foreach (var member in members)
|
||||
o.Add(member.ToJson(ctx, v: APIVersion.V2));
|
||||
|
||||
return Ok(o);
|
||||
}
|
||||
|
||||
[HttpPost("groups/{groupRef}/members/add")]
|
||||
public async Task<IActionResult> AddGroupMembers(string groupRef, [FromBody] JArray memberRefs)
|
||||
{
|
||||
if (memberRefs.Count == 0)
|
||||
throw Errors.GenericBadRequest;
|
||||
|
||||
var system = await ResolveSystem("@me");
|
||||
|
||||
var group = await ResolveGroup(groupRef);
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFound;
|
||||
if (group.System != system.Id)
|
||||
throw Errors.NotOwnGroupError;
|
||||
|
||||
var members = new List<MemberId>();
|
||||
|
||||
foreach (var JmemberRef in memberRefs)
|
||||
{
|
||||
var group = await ResolveGroup(groupRef);
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFound;
|
||||
var memberRef = JmemberRef.Value<string>();
|
||||
var member = await ResolveMember(memberRef);
|
||||
|
||||
var ctx = this.ContextFor(group);
|
||||
// todo: have a list of these errors instead of immediately throwing
|
||||
|
||||
if (!group.ListPrivacy.CanAccess(ctx))
|
||||
throw Errors.UnauthorizedGroupMemberList;
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFoundWithRef(memberRef);
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberErrorWithRef(memberRef);
|
||||
|
||||
var members = _repo.GetGroupMembers(group.Id).Where(m => m.MemberVisibility.CanAccess(ctx));
|
||||
|
||||
var o = new JArray();
|
||||
|
||||
await foreach (var member in members)
|
||||
o.Add(member.ToJson(ctx, v: APIVersion.V2));
|
||||
|
||||
return Ok(o);
|
||||
members.Add(member.Id);
|
||||
}
|
||||
|
||||
[HttpPost("groups/{groupRef}/members/add")]
|
||||
public async Task<IActionResult> AddGroupMembers(string groupRef, [FromBody] JArray memberRefs)
|
||||
var existingMembers = await _repo.GetGroupMembers(group.Id).Select(x => x.Id).ToListAsync();
|
||||
members = members.Where(x => !existingMembers.Contains(x)).ToList();
|
||||
|
||||
if (members.Count > 0)
|
||||
await _repo.AddMembersToGroup(group.Id, members);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost("groups/{groupRef}/members/remove")]
|
||||
public async Task<IActionResult> RemoveGroupMembers(string groupRef, [FromBody] JArray memberRefs)
|
||||
{
|
||||
if (memberRefs.Count == 0)
|
||||
throw Errors.GenericBadRequest;
|
||||
|
||||
var system = await ResolveSystem("@me");
|
||||
|
||||
var group = await ResolveGroup(groupRef);
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFound;
|
||||
if (group.System != system.Id)
|
||||
throw Errors.NotOwnGroupError;
|
||||
|
||||
var members = new List<MemberId>();
|
||||
|
||||
foreach (var JmemberRef in memberRefs)
|
||||
{
|
||||
if (memberRefs.Count == 0)
|
||||
throw Errors.GenericBadRequest;
|
||||
var memberRef = JmemberRef.Value<string>();
|
||||
var member = await ResolveMember(memberRef);
|
||||
|
||||
var system = await ResolveSystem("@me");
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFoundWithRef(memberRef);
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberErrorWithRef(memberRef);
|
||||
|
||||
members.Add(member.Id);
|
||||
}
|
||||
|
||||
await _repo.RemoveMembersFromGroup(group.Id, members);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost("groups/{groupRef}/members/overwrite")]
|
||||
public async Task<IActionResult> OverwriteGroupMembers(string groupRef, [FromBody] JArray memberRefs)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
|
||||
var group = await ResolveGroup(groupRef);
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFound;
|
||||
if (group.System != system.Id)
|
||||
throw Errors.NotOwnGroupError;
|
||||
|
||||
var members = new List<MemberId>();
|
||||
|
||||
foreach (var JmemberRef in memberRefs)
|
||||
{
|
||||
var memberRef = JmemberRef.Value<string>();
|
||||
var member = await ResolveMember(memberRef);
|
||||
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFoundWithRef(memberRef);
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberErrorWithRef(memberRef);
|
||||
|
||||
members.Add(member.Id);
|
||||
}
|
||||
|
||||
await _repo.ClearGroupMembers(group.Id);
|
||||
|
||||
if (members.Count > 0)
|
||||
await _repo.AddMembersToGroup(group.Id, members);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("members/{memberRef}/groups")]
|
||||
public async Task<IActionResult> GetMemberGroups(string memberRef)
|
||||
{
|
||||
var member = await ResolveMember(memberRef);
|
||||
var ctx = ContextFor(member);
|
||||
|
||||
var system = await _repo.GetSystem(member.System);
|
||||
if (!system.GroupListPrivacy.CanAccess(ctx))
|
||||
throw Errors.UnauthorizedGroupList;
|
||||
|
||||
var groups = _repo.GetMemberGroups(member.Id).Where(g => g.Visibility.CanAccess(ctx));
|
||||
|
||||
var o = new JArray();
|
||||
|
||||
await foreach (var group in groups)
|
||||
o.Add(group.ToJson(ctx));
|
||||
|
||||
return Ok(o);
|
||||
}
|
||||
|
||||
[HttpPost("members/{memberRef}/groups/add")]
|
||||
public async Task<IActionResult> AddMemberGroups(string memberRef, [FromBody] JArray groupRefs)
|
||||
{
|
||||
if (groupRefs.Count == 0)
|
||||
throw Errors.GenericBadRequest;
|
||||
|
||||
var system = await ResolveSystem("@me");
|
||||
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberError;
|
||||
|
||||
var groups = new List<GroupId>();
|
||||
|
||||
foreach (var JgroupRef in groupRefs)
|
||||
{
|
||||
var groupRef = JgroupRef.Value<string>();
|
||||
var group = await ResolveGroup(groupRef);
|
||||
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFound;
|
||||
if (group.System != system.Id)
|
||||
throw Errors.NotOwnGroupError;
|
||||
throw Errors.NotOwnGroupErrorWithRef(groupRef);
|
||||
|
||||
var members = new List<MemberId>();
|
||||
|
||||
foreach (var JmemberRef in memberRefs)
|
||||
{
|
||||
var memberRef = JmemberRef.Value<string>();
|
||||
var member = await ResolveMember(memberRef);
|
||||
|
||||
// todo: have a list of these errors instead of immediately throwing
|
||||
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFoundWithRef(memberRef);
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberErrorWithRef(memberRef);
|
||||
|
||||
members.Add(member.Id);
|
||||
}
|
||||
|
||||
var existingMembers = await _repo.GetGroupMembers(group.Id).Select(x => x.Id).ToListAsync();
|
||||
members = members.Where(x => !existingMembers.Contains(x)).ToList();
|
||||
|
||||
if (members.Count > 0)
|
||||
await _repo.AddMembersToGroup(group.Id, members);
|
||||
|
||||
return NoContent();
|
||||
groups.Add(group.Id);
|
||||
}
|
||||
|
||||
[HttpPost("groups/{groupRef}/members/remove")]
|
||||
public async Task<IActionResult> RemoveGroupMembers(string groupRef, [FromBody] JArray memberRefs)
|
||||
var existingGroups = await _repo.GetMemberGroups(member.Id).Select(x => x.Id).ToListAsync();
|
||||
groups = groups.Where(x => !existingGroups.Contains(x)).ToList();
|
||||
|
||||
if (groups.Count > 0)
|
||||
await _repo.AddGroupsToMember(member.Id, groups);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost("members/{memberRef}/groups/remove")]
|
||||
public async Task<IActionResult> RemoveMemberGroups(string memberRef, [FromBody] JArray groupRefs)
|
||||
{
|
||||
if (groupRefs.Count == 0)
|
||||
throw Errors.GenericBadRequest;
|
||||
|
||||
var system = await ResolveSystem("@me");
|
||||
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberError;
|
||||
|
||||
var groups = new List<GroupId>();
|
||||
|
||||
foreach (var JgroupRef in groupRefs)
|
||||
{
|
||||
if (memberRefs.Count == 0)
|
||||
throw Errors.GenericBadRequest;
|
||||
|
||||
var system = await ResolveSystem("@me");
|
||||
|
||||
var groupRef = JgroupRef.Value<string>();
|
||||
var group = await ResolveGroup(groupRef);
|
||||
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFound;
|
||||
throw Errors.GroupNotFoundWithRef(groupRef);
|
||||
if (group.System != system.Id)
|
||||
throw Errors.NotOwnGroupError;
|
||||
throw Errors.NotOwnGroupErrorWithRef(groupRef);
|
||||
|
||||
var members = new List<MemberId>();
|
||||
|
||||
foreach (var JmemberRef in memberRefs)
|
||||
{
|
||||
var memberRef = JmemberRef.Value<string>();
|
||||
var member = await ResolveMember(memberRef);
|
||||
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFoundWithRef(memberRef);
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberErrorWithRef(memberRef);
|
||||
|
||||
members.Add(member.Id);
|
||||
}
|
||||
|
||||
await _repo.RemoveMembersFromGroup(group.Id, members);
|
||||
|
||||
return NoContent();
|
||||
groups.Add(group.Id);
|
||||
}
|
||||
|
||||
[HttpPost("groups/{groupRef}/members/overwrite")]
|
||||
public async Task<IActionResult> OverwriteGroupMembers(string groupRef, [FromBody] JArray memberRefs)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
await _repo.RemoveGroupsFromMember(member.Id, groups);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost("members/{memberRef}/groups/overwrite")]
|
||||
public async Task<IActionResult> OverwriteMemberGroups(string memberRef, [FromBody] JArray groupRefs)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberError;
|
||||
|
||||
var groups = new List<GroupId>();
|
||||
|
||||
foreach (var JgroupRef in groupRefs)
|
||||
{
|
||||
var groupRef = JgroupRef.Value<string>();
|
||||
var group = await ResolveGroup(groupRef);
|
||||
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFound;
|
||||
throw Errors.GroupNotFoundWithRef(groupRef);
|
||||
if (group.System != system.Id)
|
||||
throw Errors.NotOwnGroupError;
|
||||
throw Errors.NotOwnGroupErrorWithRef(groupRef);
|
||||
|
||||
var members = new List<MemberId>();
|
||||
|
||||
foreach (var JmemberRef in memberRefs)
|
||||
{
|
||||
var memberRef = JmemberRef.Value<string>();
|
||||
var member = await ResolveMember(memberRef);
|
||||
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFoundWithRef(memberRef);
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberErrorWithRef(memberRef);
|
||||
|
||||
members.Add(member.Id);
|
||||
}
|
||||
|
||||
await _repo.ClearGroupMembers(group.Id);
|
||||
|
||||
if (members.Count > 0)
|
||||
await _repo.AddMembersToGroup(group.Id, members);
|
||||
|
||||
return NoContent();
|
||||
groups.Add(group.Id);
|
||||
}
|
||||
|
||||
await _repo.ClearMemberGroups(member.Id);
|
||||
|
||||
[HttpGet("members/{memberRef}/groups")]
|
||||
public async Task<IActionResult> GetMemberGroups(string memberRef)
|
||||
{
|
||||
var member = await ResolveMember(memberRef);
|
||||
var ctx = this.ContextFor(member);
|
||||
|
||||
var system = await _repo.GetSystem(member.System);
|
||||
if (!system.GroupListPrivacy.CanAccess(ctx))
|
||||
throw Errors.UnauthorizedGroupList;
|
||||
|
||||
var groups = _repo.GetMemberGroups(member.Id).Where(g => g.Visibility.CanAccess(ctx));
|
||||
|
||||
var o = new JArray();
|
||||
|
||||
await foreach (var group in groups)
|
||||
o.Add(group.ToJson(ctx));
|
||||
|
||||
return Ok(o);
|
||||
}
|
||||
|
||||
[HttpPost("members/{memberRef}/groups/add")]
|
||||
public async Task<IActionResult> AddMemberGroups(string memberRef, [FromBody] JArray groupRefs)
|
||||
{
|
||||
if (groupRefs.Count == 0)
|
||||
throw Errors.GenericBadRequest;
|
||||
|
||||
var system = await ResolveSystem("@me");
|
||||
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberError;
|
||||
|
||||
var groups = new List<GroupId>();
|
||||
|
||||
foreach (var JgroupRef in groupRefs)
|
||||
{
|
||||
var groupRef = JgroupRef.Value<string>();
|
||||
var group = await ResolveGroup(groupRef);
|
||||
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFound;
|
||||
if (group.System != system.Id)
|
||||
throw Errors.NotOwnGroupErrorWithRef(groupRef);
|
||||
|
||||
groups.Add(group.Id);
|
||||
}
|
||||
|
||||
var existingGroups = await _repo.GetMemberGroups(member.Id).Select(x => x.Id).ToListAsync();
|
||||
groups = groups.Where(x => !existingGroups.Contains(x)).ToList();
|
||||
|
||||
if (groups.Count > 0)
|
||||
await _repo.AddGroupsToMember(member.Id, groups);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost("members/{memberRef}/groups/remove")]
|
||||
public async Task<IActionResult> RemoveMemberGroups(string memberRef, [FromBody] JArray groupRefs)
|
||||
{
|
||||
if (groupRefs.Count == 0)
|
||||
throw Errors.GenericBadRequest;
|
||||
|
||||
var system = await ResolveSystem("@me");
|
||||
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberError;
|
||||
|
||||
var groups = new List<GroupId>();
|
||||
|
||||
foreach (var JgroupRef in groupRefs)
|
||||
{
|
||||
var groupRef = JgroupRef.Value<string>();
|
||||
var group = await ResolveGroup(groupRef);
|
||||
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFoundWithRef(groupRef);
|
||||
if (group.System != system.Id)
|
||||
throw Errors.NotOwnGroupErrorWithRef(groupRef);
|
||||
|
||||
groups.Add(group.Id);
|
||||
}
|
||||
|
||||
await _repo.RemoveGroupsFromMember(member.Id, groups);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost("members/{memberRef}/groups/overwrite")]
|
||||
public async Task<IActionResult> OverwriteMemberGroups(string memberRef, [FromBody] JArray groupRefs)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberError;
|
||||
|
||||
var groups = new List<GroupId>();
|
||||
|
||||
foreach (var JgroupRef in groupRefs)
|
||||
{
|
||||
var groupRef = JgroupRef.Value<string>();
|
||||
var group = await ResolveGroup(groupRef);
|
||||
|
||||
if (group == null)
|
||||
throw Errors.GroupNotFoundWithRef(groupRef);
|
||||
if (group.System != system.Id)
|
||||
throw Errors.NotOwnGroupErrorWithRef(groupRef);
|
||||
|
||||
groups.Add(group.Id);
|
||||
}
|
||||
|
||||
await _repo.ClearMemberGroups(member.Id);
|
||||
|
||||
if (groups.Count > 0)
|
||||
await _repo.AddGroupsToMember(member.Id, groups);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
if (groups.Count > 0)
|
||||
await _repo.AddGroupsToMember(member.Id, groups);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
}
|
@@ -1,122 +1,117 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
namespace PluralKit.API;
|
||||
|
||||
[ApiController]
|
||||
[ApiVersion("2.0")]
|
||||
[Route("v{version:apiVersion}")]
|
||||
public class MemberControllerV2: PKControllerBase
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("2.0")]
|
||||
[Route("v{version:apiVersion}")]
|
||||
public class MemberControllerV2: PKControllerBase
|
||||
public MemberControllerV2(IServiceProvider svc) : base(svc) { }
|
||||
|
||||
|
||||
[HttpGet("systems/{systemRef}/members")]
|
||||
public async Task<IActionResult> GetSystemMembers(string systemRef)
|
||||
{
|
||||
public MemberControllerV2(IServiceProvider svc) : base(svc) { }
|
||||
var system = await ResolveSystem(systemRef);
|
||||
if (system == null)
|
||||
throw Errors.SystemNotFound;
|
||||
|
||||
var ctx = ContextFor(system);
|
||||
|
||||
[HttpGet("systems/{systemRef}/members")]
|
||||
public async Task<IActionResult> GetSystemMembers(string systemRef)
|
||||
if (!system.MemberListPrivacy.CanAccess(ContextFor(system)))
|
||||
throw Errors.UnauthorizedMemberList;
|
||||
|
||||
var members = _repo.GetSystemMembers(system.Id);
|
||||
return Ok(await members
|
||||
.Where(m => m.MemberVisibility.CanAccess(ctx))
|
||||
.Select(m => m.ToJson(ctx, v: APIVersion.V2))
|
||||
.ToListAsync());
|
||||
}
|
||||
|
||||
[HttpPost("members")]
|
||||
public async Task<IActionResult> MemberCreate([FromBody] JObject data)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
|
||||
var memberCount = await _repo.GetSystemMemberCount(system.Id);
|
||||
var memberLimit = system.MemberLimitOverride ?? Limits.MaxMemberCount;
|
||||
if (memberCount >= memberLimit)
|
||||
throw Errors.MemberLimitReached;
|
||||
|
||||
var patch = MemberPatch.FromJSON(data);
|
||||
patch.AssertIsValid();
|
||||
if (!patch.Name.IsPresent)
|
||||
patch.Errors.Add(new ValidationError("name", "Key 'name' is required when creating new member."));
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
|
||||
using var conn = await _db.Obtain();
|
||||
using var tx = await conn.BeginTransactionAsync();
|
||||
|
||||
var newMember = await _repo.CreateMember(system.Id, patch.Name.Value, conn);
|
||||
newMember = await _repo.UpdateMember(newMember.Id, patch, conn);
|
||||
|
||||
_ = _dispatch.Dispatch(newMember.Id, new()
|
||||
{
|
||||
var system = await ResolveSystem(systemRef);
|
||||
if (system == null)
|
||||
throw Errors.SystemNotFound;
|
||||
Event = DispatchEvent.CREATE_MEMBER,
|
||||
EventData = patch.ToJson(),
|
||||
});
|
||||
|
||||
var ctx = this.ContextFor(system);
|
||||
await tx.CommitAsync();
|
||||
|
||||
if (!system.MemberListPrivacy.CanAccess(this.ContextFor(system)))
|
||||
throw Errors.UnauthorizedMemberList;
|
||||
return Ok(newMember.ToJson(LookupContext.ByOwner, v: APIVersion.V2));
|
||||
}
|
||||
|
||||
var members = _repo.GetSystemMembers(system.Id);
|
||||
return Ok(await members
|
||||
.Where(m => m.MemberVisibility.CanAccess(ctx))
|
||||
.Select(m => m.ToJson(ctx, v: APIVersion.V2))
|
||||
.ToListAsync());
|
||||
}
|
||||
[HttpGet("members/{memberRef}")]
|
||||
public async Task<IActionResult> MemberGet(string memberRef)
|
||||
{
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
|
||||
[HttpPost("members")]
|
||||
public async Task<IActionResult> MemberCreate([FromBody] JObject data)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
var system = await _repo.GetSystem(member.System);
|
||||
|
||||
var memberCount = await _repo.GetSystemMemberCount(system.Id);
|
||||
var memberLimit = system.MemberLimitOverride ?? Limits.MaxMemberCount;
|
||||
if (memberCount >= memberLimit)
|
||||
throw Errors.MemberLimitReached;
|
||||
return Ok(member.ToJson(ContextFor(member), systemStr: system.Hid, v: APIVersion.V2));
|
||||
}
|
||||
|
||||
var patch = MemberPatch.FromJSON(data);
|
||||
patch.AssertIsValid();
|
||||
if (!patch.Name.IsPresent)
|
||||
patch.Errors.Add(new ValidationError("name", $"Key 'name' is required when creating new member."));
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
[HttpPatch("members/{memberRef}")]
|
||||
public async Task<IActionResult> DoMemberPatch(string memberRef, [FromBody] JObject data)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberError;
|
||||
|
||||
using var conn = await _db.Obtain();
|
||||
using var tx = await conn.BeginTransactionAsync();
|
||||
var patch = MemberPatch.FromJSON(data, APIVersion.V2);
|
||||
|
||||
var newMember = await _repo.CreateMember(system.Id, patch.Name.Value, conn);
|
||||
newMember = await _repo.UpdateMember(newMember.Id, patch, conn);
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
|
||||
_ = _dispatch.Dispatch(newMember.Id, new()
|
||||
{
|
||||
Event = DispatchEvent.CREATE_MEMBER,
|
||||
EventData = patch.ToJson(),
|
||||
});
|
||||
var newMember = await _repo.UpdateMember(member.Id, patch);
|
||||
return Ok(newMember.ToJson(LookupContext.ByOwner, v: APIVersion.V2));
|
||||
}
|
||||
|
||||
await tx.CommitAsync();
|
||||
[HttpDelete("members/{memberRef}")]
|
||||
public async Task<IActionResult> MemberDelete(string memberRef)
|
||||
{
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
|
||||
return Ok(newMember.ToJson(LookupContext.ByOwner, v: APIVersion.V2));
|
||||
}
|
||||
var system = await ResolveSystem("@me");
|
||||
if (system.Id != member.System)
|
||||
throw Errors.NotOwnMemberError;
|
||||
|
||||
[HttpGet("members/{memberRef}")]
|
||||
public async Task<IActionResult> MemberGet(string memberRef)
|
||||
{
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
await _repo.DeleteMember(member.Id);
|
||||
|
||||
var system = await _repo.GetSystem(member.System);
|
||||
|
||||
return Ok(member.ToJson(this.ContextFor(member), systemStr: system.Hid, v: APIVersion.V2));
|
||||
}
|
||||
|
||||
[HttpPatch("members/{memberRef}")]
|
||||
public async Task<IActionResult> DoMemberPatch(string memberRef, [FromBody] JObject data)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberError;
|
||||
|
||||
var patch = MemberPatch.FromJSON(data, APIVersion.V2);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
|
||||
var newMember = await _repo.UpdateMember(member.Id, patch);
|
||||
return Ok(newMember.ToJson(LookupContext.ByOwner, v: APIVersion.V2));
|
||||
}
|
||||
|
||||
[HttpDelete("members/{memberRef}")]
|
||||
public async Task<IActionResult> MemberDelete(string memberRef)
|
||||
{
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
throw Errors.MemberNotFound;
|
||||
|
||||
var system = await ResolveSystem("@me");
|
||||
if (system.Id != member.System)
|
||||
throw Errors.NotOwnMemberError;
|
||||
|
||||
await _repo.DeleteMember(member.Id);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
return NoContent();
|
||||
}
|
||||
}
|
@@ -1,30 +1,26 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PluralKit.API
|
||||
namespace PluralKit.API;
|
||||
|
||||
[ApiController]
|
||||
[ApiVersion("2.0")]
|
||||
[Route("v{version:apiVersion}")]
|
||||
public class PrivateControllerV2: PKControllerBase
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("2.0")]
|
||||
[Route("v{version:apiVersion}")]
|
||||
public class PrivateControllerV2: PKControllerBase
|
||||
public PrivateControllerV2(IServiceProvider svc) : base(svc) { }
|
||||
|
||||
[HttpGet("meta")]
|
||||
public async Task<ActionResult<JObject>> Meta()
|
||||
{
|
||||
public PrivateControllerV2(IServiceProvider svc) : base(svc) { }
|
||||
var shards = await _repo.GetShards();
|
||||
var stats = await _repo.GetStats();
|
||||
|
||||
[HttpGet("meta")]
|
||||
public async Task<ActionResult<JObject>> Meta()
|
||||
{
|
||||
var shards = await _repo.GetShards();
|
||||
var stats = await _repo.GetStats();
|
||||
var o = new JObject();
|
||||
o.Add("shards", shards.ToJSON());
|
||||
o.Add("stats", stats.ToJson());
|
||||
|
||||
var o = new JObject();
|
||||
o.Add("shards", shards.ToJSON());
|
||||
o.Add("stats", stats.ToJson());
|
||||
|
||||
return Ok(o);
|
||||
}
|
||||
return Ok(o);
|
||||
}
|
||||
}
|
@@ -1,254 +1,255 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dapper;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using NodaTime;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
namespace PluralKit.API;
|
||||
|
||||
[ApiController]
|
||||
[ApiVersion("2.0")]
|
||||
[Route("v{version:apiVersion}")]
|
||||
public class SwitchControllerV2: PKControllerBase
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("2.0")]
|
||||
[Route("v{version:apiVersion}")]
|
||||
public class SwitchControllerV2: PKControllerBase
|
||||
public SwitchControllerV2(IServiceProvider svc) : base(svc) { }
|
||||
|
||||
|
||||
[HttpGet("systems/{systemRef}/switches")]
|
||||
public async Task<IActionResult> GetSystemSwitches(string systemRef,
|
||||
[FromQuery(Name = "before")] Instant? before,
|
||||
[FromQuery(Name = "limit")] int? limit)
|
||||
{
|
||||
public SwitchControllerV2(IServiceProvider svc) : base(svc) { }
|
||||
var system = await ResolveSystem(systemRef);
|
||||
if (system == null)
|
||||
throw Errors.SystemNotFound;
|
||||
|
||||
var ctx = ContextFor(system);
|
||||
|
||||
[HttpGet("systems/{systemRef}/switches")]
|
||||
public async Task<IActionResult> GetSystemSwitches(string systemRef, [FromQuery(Name = "before")] Instant? before, [FromQuery(Name = "limit")] int? limit)
|
||||
if (!system.FrontHistoryPrivacy.CanAccess(ctx))
|
||||
throw Errors.UnauthorizedFrontHistory;
|
||||
|
||||
if (before == null)
|
||||
before = SystemClock.Instance.GetCurrentInstant();
|
||||
|
||||
if (limit == null || limit > 100)
|
||||
limit = 100;
|
||||
|
||||
var res = await _db.Execute(conn => conn.QueryAsync<SwitchesReturnNew>(
|
||||
@"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 @Limit;",
|
||||
new { System = system.Id, Before = before, Limit = limit }
|
||||
));
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
[HttpGet("systems/{systemRef}/fronters")]
|
||||
public async Task<IActionResult> GetSystemFronters(string systemRef)
|
||||
{
|
||||
var system = await ResolveSystem(systemRef);
|
||||
if (system == null)
|
||||
throw Errors.SystemNotFound;
|
||||
|
||||
var ctx = ContextFor(system);
|
||||
|
||||
if (!system.FrontPrivacy.CanAccess(ctx))
|
||||
throw Errors.UnauthorizedCurrentFronters;
|
||||
|
||||
var sw = await _repo.GetLatestSwitch(system.Id);
|
||||
if (sw == null)
|
||||
return NoContent();
|
||||
|
||||
var members = _db.Execute(conn => _repo.GetSwitchMembers(conn, sw.Id));
|
||||
return Ok(new FrontersReturnNew
|
||||
{
|
||||
var system = await ResolveSystem(systemRef);
|
||||
if (system == null)
|
||||
throw Errors.SystemNotFound;
|
||||
Timestamp = sw.Timestamp,
|
||||
Members = await members.Select(m => m.ToJson(ctx, v: APIVersion.V2)).ToListAsync(),
|
||||
Uuid = sw.Uuid,
|
||||
});
|
||||
}
|
||||
|
||||
var ctx = this.ContextFor(system);
|
||||
|
||||
if (!system.FrontHistoryPrivacy.CanAccess(ctx))
|
||||
throw Errors.UnauthorizedFrontHistory;
|
||||
[HttpPost("systems/@me/switches")]
|
||||
public async Task<IActionResult> SwitchCreate([FromBody] PostSwitchParams data)
|
||||
{
|
||||
if (data.Members.Distinct().Count() != data.Members.Count)
|
||||
throw Errors.DuplicateMembersInList;
|
||||
|
||||
if (before == null)
|
||||
before = SystemClock.Instance.GetCurrentInstant();
|
||||
var system = await ResolveSystem("@me");
|
||||
|
||||
if (limit == null || limit > 100)
|
||||
limit = 100;
|
||||
if (data.Timestamp != null && await _repo.GetSwitches(system.Id).Select(x => x.Timestamp)
|
||||
.ContainsAsync(data.Timestamp.Value))
|
||||
throw Errors.SameSwitchTimestampError;
|
||||
|
||||
var res = await _db.Execute(conn => conn.QueryAsync<SwitchesReturnNew>(
|
||||
@"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 @Limit;", new { System = system.Id, Before = before, Limit = limit }));
|
||||
return Ok(res);
|
||||
var members = new List<PKMember>();
|
||||
|
||||
foreach (var memberRef in data.Members)
|
||||
{
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
// todo: which member
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberErrorWithRef(memberRef);
|
||||
members.Add(member);
|
||||
}
|
||||
|
||||
[HttpGet("systems/{systemRef}/fronters")]
|
||||
public async Task<IActionResult> GetSystemFronters(string systemRef)
|
||||
// We get the current switch, if it exists
|
||||
var latestSwitch = await _repo.GetLatestSwitch(system.Id);
|
||||
if (latestSwitch != null && (data.Timestamp == null || data.Timestamp > latestSwitch.Timestamp))
|
||||
{
|
||||
var system = await ResolveSystem(systemRef);
|
||||
if (system == null)
|
||||
throw Errors.SystemNotFound;
|
||||
var latestSwitchMembers = _db.Execute(conn => _repo.GetSwitchMembers(conn, latestSwitch.Id));
|
||||
|
||||
var ctx = this.ContextFor(system);
|
||||
|
||||
if (!system.FrontPrivacy.CanAccess(ctx))
|
||||
throw Errors.UnauthorizedCurrentFronters;
|
||||
|
||||
var sw = await _repo.GetLatestSwitch(system.Id);
|
||||
if (sw == null)
|
||||
return NoContent();
|
||||
|
||||
var members = _db.Execute(conn => _repo.GetSwitchMembers(conn, sw.Id));
|
||||
return Ok(new FrontersReturnNew
|
||||
{
|
||||
Timestamp = sw.Timestamp,
|
||||
Members = await members.Select(m => m.ToJson(ctx, v: APIVersion.V2)).ToListAsync(),
|
||||
Uuid = sw.Uuid,
|
||||
});
|
||||
// Bail if this switch is identical to the latest one
|
||||
if (await latestSwitchMembers.Select(m => m.Hid)
|
||||
.SequenceEqualAsync(members.Select(m => m.Hid).ToAsyncEnumerable()))
|
||||
throw Errors.SameSwitchMembersError;
|
||||
}
|
||||
|
||||
var newSwitch =
|
||||
await _db.Execute(conn => _repo.AddSwitch(conn, system.Id, members.Select(m => m.Id).ToList()));
|
||||
if (data.Timestamp != null)
|
||||
await _repo.MoveSwitch(newSwitch.Id, data.Timestamp.Value);
|
||||
|
||||
[HttpPost("systems/@me/switches")]
|
||||
public async Task<IActionResult> SwitchCreate([FromBody] PostSwitchParams data)
|
||||
return Ok(new FrontersReturnNew
|
||||
{
|
||||
if (data.Members.Distinct().Count() != data.Members.Count)
|
||||
Uuid = newSwitch.Uuid,
|
||||
Timestamp = data.Timestamp != null ? data.Timestamp.Value : newSwitch.Timestamp,
|
||||
Members = members.Select(x => x.ToJson(LookupContext.ByOwner, v: APIVersion.V2)),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("systems/{systemRef}/switches/{switchRef}")]
|
||||
public async Task<IActionResult> SwitchGet(string systemRef, string switchRef)
|
||||
{
|
||||
if (!Guid.TryParse(switchRef, out var switchId))
|
||||
throw Errors.InvalidSwitchId;
|
||||
|
||||
var system = await ResolveSystem(systemRef);
|
||||
if (system == null)
|
||||
throw Errors.SystemNotFound;
|
||||
|
||||
var sw = await _repo.GetSwitchByUuid(switchId);
|
||||
if (sw == null || system.Id != sw.System)
|
||||
throw Errors.SwitchNotFoundPublic;
|
||||
|
||||
var ctx = ContextFor(system);
|
||||
|
||||
if (!system.FrontHistoryPrivacy.CanAccess(ctx))
|
||||
throw Errors.SwitchNotFoundPublic;
|
||||
|
||||
var members = _db.Execute(conn => _repo.GetSwitchMembers(conn, sw.Id));
|
||||
return Ok(new FrontersReturnNew
|
||||
{
|
||||
Uuid = sw.Uuid,
|
||||
Timestamp = sw.Timestamp,
|
||||
Members = await members.Select(m => m.ToJson(ctx, v: APIVersion.V2)).ToListAsync()
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPatch("systems/@me/switches/{switchRef}")]
|
||||
public async Task<IActionResult> SwitchPatch(string switchRef, [FromBody] JObject data)
|
||||
{
|
||||
// for now, don't need to make a PatchObject for this, since it's only one param
|
||||
|
||||
if (!Guid.TryParse(switchRef, out var switchId))
|
||||
throw Errors.InvalidSwitchId;
|
||||
|
||||
var valueStr = data.Value<string>("timestamp").NullIfEmpty();
|
||||
if (valueStr == null)
|
||||
throw new ModelParseError(new List<ValidationError> { new("timestamp", "Key 'timestamp' is required.") });
|
||||
|
||||
var value = Instant.FromDateTimeOffset(DateTime.Parse(valueStr).ToUniversalTime());
|
||||
|
||||
var system = await ResolveSystem("@me");
|
||||
if (system == null)
|
||||
throw Errors.SystemNotFound;
|
||||
|
||||
var sw = await _repo.GetSwitchByUuid(switchId);
|
||||
if (sw == null || system.Id != sw.System)
|
||||
throw Errors.SwitchNotFoundPublic;
|
||||
|
||||
if (await _repo.GetSwitches(system.Id).Select(x => x.Timestamp).ContainsAsync(value))
|
||||
throw Errors.SameSwitchTimestampError;
|
||||
|
||||
await _repo.MoveSwitch(sw.Id, value);
|
||||
|
||||
var members = await _db.Execute(conn => _repo.GetSwitchMembers(conn, sw.Id)).ToListAsync();
|
||||
return Ok(new FrontersReturnNew
|
||||
{
|
||||
Uuid = sw.Uuid,
|
||||
Timestamp = sw.Timestamp,
|
||||
Members = members.Select(x => x.ToJson(LookupContext.ByOwner, v: APIVersion.V2))
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPatch("systems/@me/switches/{switchRef}/members")]
|
||||
public async Task<IActionResult> SwitchMemberPatch(string switchRef, [FromBody] JArray data)
|
||||
{
|
||||
if (!Guid.TryParse(switchRef, out var switchId))
|
||||
|
||||
if (data.Distinct().Count() != data.Count)
|
||||
throw Errors.DuplicateMembersInList;
|
||||
|
||||
var system = await ResolveSystem("@me");
|
||||
var system = await ResolveSystem("@me");
|
||||
|
||||
if (data.Timestamp != null && await _repo.GetSwitches(system.Id).Select(x => x.Timestamp).ContainsAsync(data.Timestamp.Value))
|
||||
throw Errors.SameSwitchTimestampError;
|
||||
var sw = await _repo.GetSwitchByUuid(switchId);
|
||||
if (sw == null)
|
||||
throw Errors.SwitchNotFound;
|
||||
|
||||
var members = new List<PKMember>();
|
||||
var members = new List<PKMember>();
|
||||
|
||||
foreach (var memberRef in data.Members)
|
||||
{
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
// todo: which member
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberErrorWithRef(memberRef);
|
||||
members.Add(member);
|
||||
}
|
||||
|
||||
// We get the current switch, if it exists
|
||||
var latestSwitch = await _repo.GetLatestSwitch(system.Id);
|
||||
if (latestSwitch != null && (data.Timestamp == null || data.Timestamp > latestSwitch.Timestamp))
|
||||
{
|
||||
var latestSwitchMembers = _db.Execute(conn => _repo.GetSwitchMembers(conn, latestSwitch.Id));
|
||||
|
||||
// Bail if this switch is identical to the latest one
|
||||
if (await latestSwitchMembers.Select(m => m.Hid).SequenceEqualAsync(members.Select(m => m.Hid).ToAsyncEnumerable()))
|
||||
throw Errors.SameSwitchMembersError;
|
||||
}
|
||||
|
||||
var newSwitch = await _db.Execute(conn => _repo.AddSwitch(conn, system.Id, members.Select(m => m.Id).ToList()));
|
||||
if (data.Timestamp != null)
|
||||
await _repo.MoveSwitch(newSwitch.Id, data.Timestamp.Value);
|
||||
|
||||
return Ok(new FrontersReturnNew
|
||||
{
|
||||
Uuid = newSwitch.Uuid,
|
||||
Timestamp = data.Timestamp != null ? data.Timestamp.Value : newSwitch.Timestamp,
|
||||
Members = members.Select(x => x.ToJson(LookupContext.ByOwner, v: APIVersion.V2)),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("systems/{systemRef}/switches/{switchRef}")]
|
||||
public async Task<IActionResult> SwitchGet(string systemRef, string switchRef)
|
||||
foreach (var JmemberRef in data)
|
||||
{
|
||||
if (!Guid.TryParse(switchRef, out var switchId))
|
||||
throw Errors.InvalidSwitchId;
|
||||
var memberRef = JmemberRef.Value<string>();
|
||||
|
||||
var system = await ResolveSystem(systemRef);
|
||||
if (system == null)
|
||||
throw Errors.SystemNotFound;
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
// todo: which member
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberErrorWithRef(memberRef);
|
||||
|
||||
var sw = await _repo.GetSwitchByUuid(switchId);
|
||||
if (sw == null || system.Id != sw.System)
|
||||
throw Errors.SwitchNotFoundPublic;
|
||||
|
||||
var ctx = this.ContextFor(system);
|
||||
|
||||
if (!system.FrontHistoryPrivacy.CanAccess(ctx))
|
||||
throw Errors.SwitchNotFoundPublic;
|
||||
|
||||
var members = _db.Execute(conn => _repo.GetSwitchMembers(conn, sw.Id));
|
||||
return Ok(new FrontersReturnNew
|
||||
{
|
||||
Uuid = sw.Uuid,
|
||||
Timestamp = sw.Timestamp,
|
||||
Members = await members.Select(m => m.ToJson(ctx, v: APIVersion.V2)).ToListAsync()
|
||||
});
|
||||
members.Add(member);
|
||||
}
|
||||
|
||||
[HttpPatch("systems/@me/switches/{switchRef}")]
|
||||
public async Task<IActionResult> SwitchPatch(string switchRef, [FromBody] JObject data)
|
||||
var latestSwitchMembers = _db.Execute(conn => _repo.GetSwitchMembers(conn, sw.Id));
|
||||
|
||||
if (await latestSwitchMembers.Select(m => m.Hid)
|
||||
.SequenceEqualAsync(members.Select(m => m.Hid).ToAsyncEnumerable()))
|
||||
throw Errors.SameSwitchMembersError;
|
||||
|
||||
await _db.Execute(conn => _repo.EditSwitch(conn, sw.Id, members.Select(x => x.Id).ToList()));
|
||||
return Ok(new FrontersReturnNew
|
||||
{
|
||||
// for now, don't need to make a PatchObject for this, since it's only one param
|
||||
Uuid = sw.Uuid,
|
||||
Timestamp = sw.Timestamp,
|
||||
Members = members.Select(x => x.ToJson(LookupContext.ByOwner, v: APIVersion.V2))
|
||||
});
|
||||
}
|
||||
|
||||
if (!Guid.TryParse(switchRef, out var switchId))
|
||||
throw Errors.InvalidSwitchId;
|
||||
[HttpDelete("systems/@me/switches/{switchRef}")]
|
||||
public async Task<IActionResult> SwitchDelete(string switchRef)
|
||||
{
|
||||
if (!Guid.TryParse(switchRef, out var switchId))
|
||||
throw Errors.InvalidSwitchId;
|
||||
|
||||
var valueStr = data.Value<string>("timestamp").NullIfEmpty();
|
||||
if (valueStr == null)
|
||||
throw new ModelParseError(new List<ValidationError>() { new ValidationError("timestamp", $"Key 'timestamp' is required.") });
|
||||
var system = await ResolveSystem("@me");
|
||||
var sw = await _repo.GetSwitchByUuid(switchId);
|
||||
if (sw == null || system.Id != sw.System)
|
||||
throw Errors.SwitchNotFoundPublic;
|
||||
|
||||
var value = Instant.FromDateTimeOffset(DateTime.Parse(valueStr).ToUniversalTime());
|
||||
await _repo.DeleteSwitch(sw.Id);
|
||||
|
||||
var system = await ResolveSystem("@me");
|
||||
if (system == null)
|
||||
throw Errors.SystemNotFound;
|
||||
|
||||
var sw = await _repo.GetSwitchByUuid(switchId);
|
||||
if (sw == null || system.Id != sw.System)
|
||||
throw Errors.SwitchNotFoundPublic;
|
||||
|
||||
if (await _repo.GetSwitches(system.Id).Select(x => x.Timestamp).ContainsAsync(value))
|
||||
throw Errors.SameSwitchTimestampError;
|
||||
|
||||
await _repo.MoveSwitch(sw.Id, value);
|
||||
|
||||
var members = await _db.Execute(conn => _repo.GetSwitchMembers(conn, sw.Id)).ToListAsync();
|
||||
return Ok(new FrontersReturnNew
|
||||
{
|
||||
Uuid = sw.Uuid,
|
||||
Timestamp = sw.Timestamp,
|
||||
Members = members.Select(x => x.ToJson(LookupContext.ByOwner, v: APIVersion.V2)),
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPatch("systems/@me/switches/{switchRef}/members")]
|
||||
public async Task<IActionResult> SwitchMemberPatch(string switchRef, [FromBody] JArray data)
|
||||
{
|
||||
if (!Guid.TryParse(switchRef, out var switchId))
|
||||
|
||||
if (data.Distinct().Count() != data.Count)
|
||||
throw Errors.DuplicateMembersInList;
|
||||
|
||||
var system = await ResolveSystem("@me");
|
||||
|
||||
var sw = await _repo.GetSwitchByUuid(switchId);
|
||||
if (sw == null)
|
||||
throw Errors.SwitchNotFound;
|
||||
|
||||
var members = new List<PKMember>();
|
||||
|
||||
foreach (var JmemberRef in data)
|
||||
{
|
||||
var memberRef = JmemberRef.Value<string>();
|
||||
|
||||
var member = await ResolveMember(memberRef);
|
||||
if (member == null)
|
||||
// todo: which member
|
||||
throw Errors.MemberNotFound;
|
||||
if (member.System != system.Id)
|
||||
throw Errors.NotOwnMemberErrorWithRef(memberRef);
|
||||
|
||||
members.Add(member);
|
||||
}
|
||||
|
||||
var latestSwitchMembers = _db.Execute(conn => _repo.GetSwitchMembers(conn, sw.Id));
|
||||
|
||||
if (await latestSwitchMembers.Select(m => m.Hid).SequenceEqualAsync(members.Select(m => m.Hid).ToAsyncEnumerable()))
|
||||
throw Errors.SameSwitchMembersError;
|
||||
|
||||
await _db.Execute(conn => _repo.EditSwitch(conn, sw.Id, members.Select(x => x.Id).ToList()));
|
||||
return Ok(new FrontersReturnNew
|
||||
{
|
||||
Uuid = sw.Uuid,
|
||||
Timestamp = sw.Timestamp,
|
||||
Members = members.Select(x => x.ToJson(LookupContext.ByOwner, v: APIVersion.V2)),
|
||||
});
|
||||
}
|
||||
|
||||
[HttpDelete("systems/@me/switches/{switchRef}")]
|
||||
public async Task<IActionResult> SwitchDelete(string switchRef)
|
||||
{
|
||||
if (!Guid.TryParse(switchRef, out var switchId))
|
||||
throw Errors.InvalidSwitchId;
|
||||
|
||||
var system = await ResolveSystem("@me");
|
||||
var sw = await _repo.GetSwitchByUuid(switchId);
|
||||
if (sw == null || system.Id != sw.System)
|
||||
throw Errors.SwitchNotFoundPublic;
|
||||
|
||||
await _repo.DeleteSwitch(sw.Id);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
return NoContent();
|
||||
}
|
||||
}
|
@@ -1,41 +1,37 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
namespace PluralKit.API;
|
||||
|
||||
[ApiController]
|
||||
[ApiVersion("2.0")]
|
||||
[Route("v{version:apiVersion}/systems")]
|
||||
public class SystemControllerV2: PKControllerBase
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("2.0")]
|
||||
[Route("v{version:apiVersion}/systems")]
|
||||
public class SystemControllerV2: PKControllerBase
|
||||
public SystemControllerV2(IServiceProvider svc) : base(svc) { }
|
||||
|
||||
[HttpGet("{systemRef}")]
|
||||
public async Task<IActionResult> SystemGet(string systemRef)
|
||||
{
|
||||
public SystemControllerV2(IServiceProvider svc) : base(svc) { }
|
||||
var system = await ResolveSystem(systemRef);
|
||||
if (system == null) throw Errors.SystemNotFound;
|
||||
return Ok(system.ToJson(ContextFor(system), APIVersion.V2));
|
||||
}
|
||||
|
||||
[HttpGet("{systemRef}")]
|
||||
public async Task<IActionResult> SystemGet(string systemRef)
|
||||
{
|
||||
var system = await ResolveSystem(systemRef);
|
||||
if (system == null) throw Errors.SystemNotFound;
|
||||
else return Ok(system.ToJson(this.ContextFor(system), v: APIVersion.V2));
|
||||
}
|
||||
[HttpPatch("@me")]
|
||||
public async Task<IActionResult> DoSystemPatch([FromBody] JObject data)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
var patch = SystemPatch.FromJSON(data, APIVersion.V2);
|
||||
|
||||
[HttpPatch("@me")]
|
||||
public async Task<IActionResult> DoSystemPatch([FromBody] JObject data)
|
||||
{
|
||||
var system = await ResolveSystem("@me");
|
||||
var patch = SystemPatch.FromJSON(data, APIVersion.V2);
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
|
||||
var newSystem = await _repo.UpdateSystem(system.Id, patch);
|
||||
return Ok(newSystem.ToJson(LookupContext.ByOwner, v: APIVersion.V2));
|
||||
}
|
||||
var newSystem = await _repo.UpdateSystem(system.Id, patch);
|
||||
return Ok(newSystem.ToJson(LookupContext.ByOwner, APIVersion.V2));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user