feat(apiv2): GET endpoints except guilds
- ResolveT methods in ControllerBase - ContextFor methods in ControllerBase
This commit is contained in:
		@@ -35,7 +35,8 @@ namespace PluralKit.API
 | 
				
			|||||||
            if (systemRef == "@me")
 | 
					            if (systemRef == "@me")
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                HttpContext.Items.TryGetValue("SystemId", out var systemId);
 | 
					                HttpContext.Items.TryGetValue("SystemId", out var systemId);
 | 
				
			||||||
                if (systemId == null) return null;
 | 
					                if (systemId == null)
 | 
				
			||||||
 | 
					                    throw APIErrors.GenericAuthError;
 | 
				
			||||||
                return _repo.GetSystem((SystemId)systemId);
 | 
					                return _repo.GetSystem((SystemId)systemId);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -51,11 +52,47 @@ namespace PluralKit.API
 | 
				
			|||||||
            return null;
 | 
					            return null;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public LookupContext LookupContextFor(PKSystem target)
 | 
					        protected Task<PKMember?> ResolveMember(string memberRef)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (Guid.TryParse(memberRef, out var guid))
 | 
				
			||||||
 | 
					                return _repo.GetMemberByGuid(guid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (_shortIdRegex.IsMatch(memberRef))
 | 
				
			||||||
 | 
					                return _repo.GetMemberByHid(memberRef);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected Task<PKGroup?> ResolveGroup(string groupRef)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (Guid.TryParse(groupRef, out var guid))
 | 
				
			||||||
 | 
					                return _repo.GetGroupByGuid(guid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (_shortIdRegex.IsMatch(groupRef))
 | 
				
			||||||
 | 
					                return _repo.GetGroupByHid(groupRef);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public LookupContext ContextFor(PKSystem system)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            HttpContext.Items.TryGetValue("SystemId", out var systemId);
 | 
					            HttpContext.Items.TryGetValue("SystemId", out var systemId);
 | 
				
			||||||
            if (systemId == null) return LookupContext.ByNonOwner;
 | 
					            if (systemId == null) return LookupContext.ByNonOwner;
 | 
				
			||||||
            return target.Id == (SystemId)systemId ? LookupContext.ByOwner : LookupContext.ByNonOwner;
 | 
					            return ((SystemId)systemId) == system.Id ? LookupContext.ByOwner : LookupContext.ByNonOwner;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public LookupContext ContextFor(PKMember member)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            HttpContext.Items.TryGetValue("SystemId", out var systemId);
 | 
				
			||||||
 | 
					            if (systemId == null) return LookupContext.ByNonOwner;
 | 
				
			||||||
 | 
					            return ((SystemId)systemId) == member.System ? LookupContext.ByOwner : LookupContext.ByNonOwner;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public LookupContext ContextFor(PKGroup group)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            HttpContext.Items.TryGetValue("SystemId", out var systemId);
 | 
				
			||||||
 | 
					            if (systemId == null) return LookupContext.ByNonOwner;
 | 
				
			||||||
 | 
					            return ((SystemId)systemId) == group.System ? LookupContext.ByOwner : LookupContext.ByNonOwner;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
@@ -16,13 +17,23 @@ namespace PluralKit.API
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        public GroupControllerV2(IServiceProvider svc) : base(svc) { }
 | 
					        public GroupControllerV2(IServiceProvider svc) : base(svc) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [HttpGet("systems/{system_id}/groups")]
 | 
					        [HttpGet("systems/{systemRef}/groups")]
 | 
				
			||||||
        public async Task<IActionResult> GetSystemGroups(string system_id)
 | 
					        public async Task<IActionResult> GetSystemGroups(string systemRef)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new ObjectResult("Unimplemented")
 | 
					            var system = await ResolveSystem(systemRef);
 | 
				
			||||||
            {
 | 
					            if (system == null)
 | 
				
			||||||
                StatusCode = 501
 | 
					                throw APIErrors.SystemNotFound;
 | 
				
			||||||
            };
 | 
					
 | 
				
			||||||
 | 
					            var ctx = this.ContextFor(system);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!system.GroupListPrivacy.CanAccess(User.ContextFor(system)))
 | 
				
			||||||
 | 
					                throw APIErrors.UnauthorizedGroupList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var groups = _repo.GetSystemGroups(system.Id);
 | 
				
			||||||
 | 
					            return Ok(await groups
 | 
				
			||||||
 | 
					                .Where(g => g.Visibility.CanAccess(ctx))
 | 
				
			||||||
 | 
					                .Select(g => g.ToJson(ctx))
 | 
				
			||||||
 | 
					                .ToListAsync());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [HttpPost("groups")]
 | 
					        [HttpPost("groups")]
 | 
				
			||||||
@@ -34,13 +45,16 @@ namespace PluralKit.API
 | 
				
			|||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [HttpGet("groups/{group_id}")]
 | 
					        [HttpGet("groups/{groupRef}")]
 | 
				
			||||||
        public async Task<IActionResult> GroupGet(string group_id)
 | 
					        public async Task<IActionResult> GroupGet(string groupRef)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new ObjectResult("Unimplemented")
 | 
					            var group = await ResolveGroup(groupRef);
 | 
				
			||||||
            {
 | 
					            if (group == null)
 | 
				
			||||||
                StatusCode = 501
 | 
					                throw APIErrors.GroupNotFound;
 | 
				
			||||||
            };
 | 
					
 | 
				
			||||||
 | 
					            var system = await _repo.GetSystem(group.System);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return Ok(group.ToJson(this.ContextFor(group), systemStr: system.Hid));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [HttpPatch("groups/{group_id}")]
 | 
					        [HttpPatch("groups/{group_id}")]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,13 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Newtonsoft.Json.Linq;
 | 
					using Newtonsoft.Json.Linq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using PluralKit.Core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace PluralKit.API
 | 
					namespace PluralKit.API
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [ApiController]
 | 
					    [ApiController]
 | 
				
			||||||
@@ -14,22 +17,46 @@ namespace PluralKit.API
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        public GroupMemberControllerV2(IServiceProvider svc) : base(svc) { }
 | 
					        public GroupMemberControllerV2(IServiceProvider svc) : base(svc) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [HttpGet("groups/{group_id}/members")]
 | 
					        [HttpGet("groups/{groupRef}/members")]
 | 
				
			||||||
        public async Task<IActionResult> GetGroupMembers(string group_id)
 | 
					        public async Task<IActionResult> GetGroupMembers(string groupRef)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new ObjectResult("Unimplemented")
 | 
					            var group = await ResolveGroup(groupRef);
 | 
				
			||||||
            {
 | 
					            if (group == null)
 | 
				
			||||||
                StatusCode = 501
 | 
					                throw APIErrors.GroupNotFound;
 | 
				
			||||||
            };
 | 
					
 | 
				
			||||||
 | 
					            var ctx = this.ContextFor(group);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!group.ListPrivacy.CanAccess(ctx))
 | 
				
			||||||
 | 
					                throw APIErrors.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);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [HttpGet("members/{member_id}/groups")]
 | 
					        [HttpGet("members/{memberRef}/groups")]
 | 
				
			||||||
        public async Task<IActionResult> GetMemberGroups(string member_id)
 | 
					        public async Task<IActionResult> GetMemberGroups(string memberRef)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new ObjectResult("Unimplemented")
 | 
					            var member = await ResolveMember(memberRef);
 | 
				
			||||||
            {
 | 
					            var ctx = this.ContextFor(member);
 | 
				
			||||||
                StatusCode = 501
 | 
					
 | 
				
			||||||
            };
 | 
					            var system = await _repo.GetSystem(member.System);
 | 
				
			||||||
 | 
					            if (!system.GroupListPrivacy.CanAccess(ctx))
 | 
				
			||||||
 | 
					                throw APIErrors.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);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [HttpPut("groups/{group_id}/members/{member_id}")]
 | 
					        [HttpPut("groups/{group_id}/members/{member_id}")]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
@@ -17,13 +18,23 @@ namespace PluralKit.API
 | 
				
			|||||||
        public MemberControllerV2(IServiceProvider svc) : base(svc) { }
 | 
					        public MemberControllerV2(IServiceProvider svc) : base(svc) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [HttpGet("systems/{system}/members")]
 | 
					        [HttpGet("systems/{systemRef}/members")]
 | 
				
			||||||
        public async Task<IActionResult> GetSystemMembers(string system)
 | 
					        public async Task<IActionResult> GetSystemMembers(string systemRef)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new ObjectResult("Unimplemented")
 | 
					            var system = await ResolveSystem(systemRef);
 | 
				
			||||||
            {
 | 
					            if (system == null)
 | 
				
			||||||
                StatusCode = 501
 | 
					                throw APIErrors.SystemNotFound;
 | 
				
			||||||
            };
 | 
					
 | 
				
			||||||
 | 
					            var ctx = this.ContextFor(system);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!system.MemberListPrivacy.CanAccess(this.ContextFor(system)))
 | 
				
			||||||
 | 
					                throw APIErrors.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")]
 | 
					        [HttpPost("members")]
 | 
				
			||||||
@@ -35,13 +46,16 @@ namespace PluralKit.API
 | 
				
			|||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [HttpGet("members/{member}")]
 | 
					        [HttpGet("members/{memberRef}")]
 | 
				
			||||||
        public async Task<IActionResult> MemberGet(string member)
 | 
					        public async Task<IActionResult> MemberGet(string memberRef)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new ObjectResult("Unimplemented")
 | 
					            var member = await ResolveMember(memberRef);
 | 
				
			||||||
            {
 | 
					            if (member == null)
 | 
				
			||||||
                StatusCode = 501
 | 
					                throw APIErrors.MemberNotFound;
 | 
				
			||||||
            };
 | 
					
 | 
				
			||||||
 | 
					            var system = await _repo.GetSystem(member.System);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return Ok(member.ToJson(this.ContextFor(member), systemStr: system.Hid, v: APIVersion.V2));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [HttpPatch("members/{member}")]
 | 
					        [HttpPatch("members/{member}")]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,8 @@ using Microsoft.AspNetCore.Mvc;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
using Newtonsoft.Json.Linq;
 | 
					using Newtonsoft.Json.Linq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using NodaTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using PluralKit.Core;
 | 
					using PluralKit.Core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace PluralKit.API
 | 
					namespace PluralKit.API
 | 
				
			||||||
@@ -28,12 +30,25 @@ namespace PluralKit.API
 | 
				
			|||||||
            return Ok(o);
 | 
					            return Ok(o);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [HttpGet("messages/{message_id}")]
 | 
					        [HttpGet("messages/{messageId}")]
 | 
				
			||||||
        public async Task<IActionResult> MessageGet(ulong message_id)
 | 
					        public async Task<ActionResult<MessageReturn>> MessageGet(ulong messageId)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new ObjectResult("Unimplemented")
 | 
					            var msg = await _db.Execute(c => _repo.GetMessage(c, messageId));
 | 
				
			||||||
 | 
					            if (msg == null)
 | 
				
			||||||
 | 
					                throw APIErrors.MessageNotFound;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var ctx = this.ContextFor(msg.System);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // todo: don't rely on v1 stuff
 | 
				
			||||||
 | 
					            return new MessageReturn
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                StatusCode = 501
 | 
					                Timestamp = Instant.FromUnixTimeMilliseconds((long)(msg.Message.Mid >> 22) + 1420070400000),
 | 
				
			||||||
 | 
					                Id = msg.Message.Mid.ToString(),
 | 
				
			||||||
 | 
					                Channel = msg.Message.Channel.ToString(),
 | 
				
			||||||
 | 
					                Sender = msg.Message.Sender.ToString(),
 | 
				
			||||||
 | 
					                System = msg.System.ToJson(ctx, v: APIVersion.V2),
 | 
				
			||||||
 | 
					                Member = msg.Member.ToJson(ctx, v: APIVersion.V2),
 | 
				
			||||||
 | 
					                Original = msg.Message.OriginalMid?.ToString()
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,28 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using Dapper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
using Newtonsoft.Json.Linq;
 | 
					using Newtonsoft.Json.Linq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using NodaTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using PluralKit.Core;
 | 
					using PluralKit.Core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace PluralKit.API
 | 
					namespace PluralKit.API
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    public struct SwitchesReturnNew
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        [JsonProperty("timestamp")] public Instant Timestamp { get; set; }
 | 
				
			||||||
 | 
					        [JsonProperty("id")] public Guid Uuid { get; set; }
 | 
				
			||||||
 | 
					        [JsonProperty("members")] public IEnumerable<string> Members { get; set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [ApiController]
 | 
					    [ApiController]
 | 
				
			||||||
    [ApiVersion("2.0")]
 | 
					    [ApiVersion("2.0")]
 | 
				
			||||||
    [Route("v{version:apiVersion}")]
 | 
					    [Route("v{version:apiVersion}")]
 | 
				
			||||||
@@ -17,22 +31,57 @@ namespace PluralKit.API
 | 
				
			|||||||
        public SwitchControllerV2(IServiceProvider svc) : base(svc) { }
 | 
					        public SwitchControllerV2(IServiceProvider svc) : base(svc) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [HttpGet("systems/{system}/switches")]
 | 
					        [HttpGet("systems/{systemRef}/switches")]
 | 
				
			||||||
        public async Task<IActionResult> GetSystemSwitches(string system)
 | 
					        public async Task<IActionResult> GetSystemSwitches(string systemRef, [FromQuery(Name = "before")] Instant? before, [FromQuery(Name = "limit")] int? limit)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new ObjectResult("Unimplemented")
 | 
					            var system = await ResolveSystem(systemRef);
 | 
				
			||||||
            {
 | 
					            if (system == null)
 | 
				
			||||||
                StatusCode = 501
 | 
					                throw APIErrors.SystemNotFound;
 | 
				
			||||||
            };
 | 
					
 | 
				
			||||||
 | 
					            var ctx = this.ContextFor(system);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!system.FrontHistoryPrivacy.CanAccess(ctx))
 | 
				
			||||||
 | 
					                throw APIErrors.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/{system}/fronters")]
 | 
					        [HttpGet("systems/{systemRef}/fronters")]
 | 
				
			||||||
        public async Task<IActionResult> GetSystemFronters(string system)
 | 
					        public async Task<IActionResult> GetSystemFronters(string systemRef)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new ObjectResult("Unimplemented")
 | 
					            var system = await ResolveSystem(systemRef);
 | 
				
			||||||
 | 
					            if (system == null)
 | 
				
			||||||
 | 
					                throw APIErrors.SystemNotFound;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var ctx = this.ContextFor(system);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!system.FrontPrivacy.CanAccess(ctx))
 | 
				
			||||||
 | 
					                throw APIErrors.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 FrontersReturn
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                StatusCode = 501
 | 
					                Timestamp = sw.Timestamp,
 | 
				
			||||||
            };
 | 
					                Members = await members.Select(m => m.ToJson(ctx, v: APIVersion.V2)).ToListAsync()
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -46,13 +95,31 @@ namespace PluralKit.API
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [HttpGet("systems/{system}/switches/{switch_id}")]
 | 
					        [HttpGet("systems/{systemRef}/switches/{switchRef}")]
 | 
				
			||||||
        public async Task<IActionResult> SwitchGet(string system, string switch_id)
 | 
					        public async Task<IActionResult> SwitchGet(string systemRef, string switchRef)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new ObjectResult("Unimplemented")
 | 
					            if (!Guid.TryParse(switchRef, out var switchId))
 | 
				
			||||||
 | 
					                throw APIErrors.SwitchNotFound;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var system = await ResolveSystem(systemRef);
 | 
				
			||||||
 | 
					            if (system == null)
 | 
				
			||||||
 | 
					                throw APIErrors.SystemNotFound;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var ctx = this.ContextFor(system);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!system.FrontHistoryPrivacy.CanAccess(ctx))
 | 
				
			||||||
 | 
					                throw APIErrors.UnauthorizedFrontHistory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var sw = await _repo.GetSwitchByUuid(switchId);
 | 
				
			||||||
 | 
					            if (sw == null)
 | 
				
			||||||
 | 
					                throw APIErrors.SwitchNotFound;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var members = _db.Execute(conn => _repo.GetSwitchMembers(conn, sw.Id));
 | 
				
			||||||
 | 
					            return Ok(new FrontersReturn
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                StatusCode = 501
 | 
					                Timestamp = sw.Timestamp,
 | 
				
			||||||
            };
 | 
					                Members = await members.Select(m => m.ToJson(ctx, v: APIVersion.V2)).ToListAsync()
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [HttpPatch("systems/{system}/switches/{switch_id}")]
 | 
					        [HttpPatch("systems/{system}/switches/{switch_id}")]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ namespace PluralKit.API
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var system = await ResolveSystem(systemRef);
 | 
					            var system = await ResolveSystem(systemRef);
 | 
				
			||||||
            if (system == null) return NotFound();
 | 
					            if (system == null) return NotFound();
 | 
				
			||||||
            else return Ok(system.ToJson(LookupContextFor(system)));
 | 
					            else return Ok(system.ToJson(this.ContextFor(system), v: APIVersion.V2));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [HttpPatch("{system}")]
 | 
					        [HttpPatch("{system}")]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,11 +41,17 @@ namespace PluralKit.API
 | 
				
			|||||||
    public static class APIErrors
 | 
					    public static class APIErrors
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public static PKError GenericBadRequest = new(400, 0, "400: Bad Request");
 | 
					        public static PKError GenericBadRequest = new(400, 0, "400: Bad Request");
 | 
				
			||||||
 | 
					        public static PKError GenericAuthError = new(401, 0, "401: Missing or invalid Authorization header");
 | 
				
			||||||
        public static PKError SystemNotFound = new(404, 20001, "System not found.");
 | 
					        public static PKError SystemNotFound = new(404, 20001, "System not found.");
 | 
				
			||||||
        public static PKError MemberNotFound = new(404, 20002, "Member not found.");
 | 
					        public static PKError MemberNotFound = new(404, 20002, "Member not found.");
 | 
				
			||||||
        public static PKError GroupNotFound = new(404, 20003, "Group not found.");
 | 
					        public static PKError GroupNotFound = new(404, 20003, "Group not found.");
 | 
				
			||||||
 | 
					        public static PKError MessageNotFound = new(404, 20004, "Message not found.");
 | 
				
			||||||
 | 
					        public static PKError SwitchNotFound = new(404, 20005, "Switch not found.");
 | 
				
			||||||
        public static PKError UnauthorizedMemberList = new(403, 30001, "Unauthorized to view member list");
 | 
					        public static PKError UnauthorizedMemberList = new(403, 30001, "Unauthorized to view member list");
 | 
				
			||||||
        public static PKError UnauthorizedGroupList = new(403, 30002, "Unauthorized to view group list");
 | 
					        public static PKError UnauthorizedGroupList = new(403, 30002, "Unauthorized to view group list");
 | 
				
			||||||
 | 
					        public static PKError UnauthorizedGroupMemberList = new(403, 30003, "Unauthorized to view group member list");
 | 
				
			||||||
 | 
					        public static PKError UnauthorizedCurrentFronters = new(403, 30004, "Unauthorized to view current fronters.");
 | 
				
			||||||
 | 
					        public static PKError UnauthorizedFrontHistory = new(403, 30004, "Unauthorized to view front history.");
 | 
				
			||||||
        public static PKError Unimplemented = new(501, 50001, "Unimplemented");
 | 
					        public static PKError Unimplemented = new(501, 50001, "Unimplemented");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -16,6 +16,15 @@ namespace PluralKit.Core
 | 
				
			|||||||
            return _db.QueryStream<PKGroup>(query);
 | 
					            return _db.QueryStream<PKGroup>(query);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public IAsyncEnumerable<PKMember> GetGroupMembers(GroupId id)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var query = new Query("group_members")
 | 
				
			||||||
 | 
					                .Select("members.*")
 | 
				
			||||||
 | 
					                .Join("members", "group_members.member_id", "members.id")
 | 
				
			||||||
 | 
					                .Where("group_members.group_id", id);
 | 
				
			||||||
 | 
					            return _db.QueryStream<PKMember>(query);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // todo: add this to metrics tracking
 | 
					        // todo: add this to metrics tracking
 | 
				
			||||||
        public async Task AddGroupsToMember(MemberId member, IReadOnlyCollection<GroupId> groups)
 | 
					        public async Task AddGroupsToMember(MemberId member, IReadOnlyCollection<GroupId> groups)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user