Major database refactor (again)
This commit is contained in:
		| @@ -13,18 +13,20 @@ namespace PluralKit.API | ||||
|     [Route( "v{version:apiVersion}/a" )] | ||||
|     public class AccountController: ControllerBase | ||||
|     { | ||||
|         private IDataStore _data; | ||||
|  | ||||
|         public AccountController(IDataStore data) | ||||
|         private readonly IDatabase _db; | ||||
|         private readonly ModelRepository _repo; | ||||
|         public AccountController(IDatabase db, ModelRepository repo) | ||||
|         { | ||||
|             _data = data; | ||||
|             _db = db; | ||||
|             _repo = repo; | ||||
|         } | ||||
|  | ||||
|         [HttpGet("{aid}")] | ||||
|         public async Task<ActionResult<JObject>> GetSystemByAccount(ulong aid) | ||||
|         { | ||||
|             var system = await _data.GetSystemByAccount(aid); | ||||
|             if (system == null) return NotFound("Account not found."); | ||||
|             var system = await _db.Execute(c => _repo.GetSystemByAccount(c, aid)); | ||||
|             if (system == null) | ||||
|                 return NotFound("Account not found."); | ||||
|              | ||||
|             return Ok(system.ToJson(User.ContextFor(system))); | ||||
|         } | ||||
|   | ||||
| @@ -16,19 +16,21 @@ namespace PluralKit.API | ||||
|     [Route( "v{version:apiVersion}/m" )] | ||||
|     public class MemberController: ControllerBase | ||||
|     { | ||||
|         private IDatabase _db; | ||||
|         private IAuthorizationService _auth; | ||||
|         private readonly IDatabase _db; | ||||
|         private readonly ModelRepository _repo; | ||||
|         private readonly IAuthorizationService _auth; | ||||
|  | ||||
|         public MemberController(IAuthorizationService auth, IDatabase db) | ||||
|         public MemberController(IAuthorizationService auth, IDatabase db, ModelRepository repo) | ||||
|         { | ||||
|             _auth = auth; | ||||
|             _db = db; | ||||
|             _repo = repo; | ||||
|         } | ||||
|  | ||||
|         [HttpGet("{hid}")] | ||||
|         public async Task<ActionResult<JObject>> GetMember(string hid) | ||||
|         { | ||||
|             var member = await _db.Execute(conn => conn.QueryMemberByHid(hid)); | ||||
|             var member = await _db.Execute(conn => _repo.GetMemberByHid(conn, hid)); | ||||
|             if (member == null) return NotFound("Member not found."); | ||||
|  | ||||
|             return Ok(member.ToJson(User.ContextFor(member))); | ||||
| @@ -49,7 +51,7 @@ namespace PluralKit.API | ||||
|             if (memberCount >= Limits.MaxMemberCount) | ||||
|                 return BadRequest($"Member limit reached ({memberCount} / {Limits.MaxMemberCount})."); | ||||
|  | ||||
|             var member = await conn.CreateMember(system, properties.Value<string>("name")); | ||||
|             var member = await _repo.CreateMember(conn, system, properties.Value<string>("name")); | ||||
|             MemberPatch patch; | ||||
|             try | ||||
|             { | ||||
| @@ -60,7 +62,7 @@ namespace PluralKit.API | ||||
|                 return BadRequest(e.Message); | ||||
|             } | ||||
|              | ||||
|             member = await conn.UpdateMember(member.Id, patch); | ||||
|             member = await _repo.UpdateMember(conn, member.Id, patch); | ||||
|             return Ok(member.ToJson(User.ContextFor(member))); | ||||
|         } | ||||
|  | ||||
| @@ -70,7 +72,7 @@ namespace PluralKit.API | ||||
|         { | ||||
|             await using var conn = await _db.Obtain(); | ||||
|  | ||||
|             var member = await conn.QueryMemberByHid(hid); | ||||
|             var member = await _repo.GetMemberByHid(conn, hid); | ||||
|             if (member == null) return NotFound("Member not found."); | ||||
|              | ||||
|             var res = await _auth.AuthorizeAsync(User, member, "EditMember"); | ||||
| @@ -86,7 +88,7 @@ namespace PluralKit.API | ||||
|                 return BadRequest(e.Message); | ||||
|             } | ||||
|              | ||||
|             var newMember = await conn.UpdateMember(member.Id, patch); | ||||
|             var newMember = await _repo.UpdateMember(conn, member.Id, patch); | ||||
|             return Ok(newMember.ToJson(User.ContextFor(newMember))); | ||||
|         } | ||||
|          | ||||
| @@ -96,13 +98,13 @@ namespace PluralKit.API | ||||
|         { | ||||
|             await using var conn = await _db.Obtain(); | ||||
|  | ||||
|             var member = await conn.QueryMemberByHid(hid); | ||||
|             var member = await _repo.GetMemberByHid(conn, hid); | ||||
|             if (member == null) return NotFound("Member not found."); | ||||
|              | ||||
|             var res = await _auth.AuthorizeAsync(User, member, "EditMember"); | ||||
|             if (!res.Succeeded) return Unauthorized($"Member '{hid}' is not part of your system."); | ||||
|  | ||||
|             await conn.DeleteMember(member.Id); | ||||
|             await _repo.DeleteMember(conn, member.Id); | ||||
|             return Ok(); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -28,17 +28,19 @@ namespace PluralKit.API | ||||
|     [Route( "v{version:apiVersion}/msg" )] | ||||
|     public class MessageController: ControllerBase | ||||
|     { | ||||
|         private IDataStore _data; | ||||
|         private readonly IDatabase _db; | ||||
|         private readonly ModelRepository _repo; | ||||
|  | ||||
|         public MessageController(IDataStore _data) | ||||
|         public MessageController(ModelRepository repo, IDatabase db) | ||||
|         { | ||||
|             this._data = _data; | ||||
|             _repo = repo; | ||||
|             _db = db; | ||||
|         } | ||||
|  | ||||
|         [HttpGet("{mid}")] | ||||
|         public async Task<ActionResult<MessageReturn>> GetMessage(ulong mid) | ||||
|         { | ||||
|             var msg = await _data.GetMessage(mid); | ||||
|             var msg = await _db.Execute(c => _repo.GetMessage(c, mid)); | ||||
|             if (msg == null) return NotFound("Message not found."); | ||||
|  | ||||
|             return new MessageReturn | ||||
|   | ||||
| @@ -39,29 +39,29 @@ namespace PluralKit.API | ||||
|     [Route( "v{version:apiVersion}/s" )] | ||||
|     public class SystemController : ControllerBase | ||||
|     { | ||||
|         private IDataStore _data; | ||||
|         private IDatabase _db; | ||||
|         private IAuthorizationService _auth; | ||||
|         private readonly IDatabase _db; | ||||
|         private readonly ModelRepository _repo; | ||||
|         private readonly IAuthorizationService _auth; | ||||
|  | ||||
|         public SystemController(IDataStore data, IDatabase db, IAuthorizationService auth) | ||||
|         public SystemController(IDatabase db, IAuthorizationService auth, ModelRepository repo) | ||||
|         { | ||||
|             _data = data; | ||||
|             _db = db; | ||||
|             _auth = auth; | ||||
|             _repo = repo; | ||||
|         } | ||||
|  | ||||
|         [HttpGet] | ||||
|         [Authorize] | ||||
|         public async Task<ActionResult<JObject>> GetOwnSystem() | ||||
|         { | ||||
|             var system = await _db.Execute(c => c.QuerySystem(User.CurrentSystem())); | ||||
|             var system = await _db.Execute(c => _repo.GetSystem(c, User.CurrentSystem())); | ||||
|             return system.ToJson(User.ContextFor(system)); | ||||
|         } | ||||
|  | ||||
|         [HttpGet("{hid}")] | ||||
|         public async Task<ActionResult<JObject>> GetSystem(string hid) | ||||
|         { | ||||
|             var system = await _data.GetSystemByHid(hid); | ||||
|             var system = await _db.Execute(c => _repo.GetSystemByHid(c, hid)); | ||||
|             if (system == null) return NotFound("System not found."); | ||||
|             return Ok(system.ToJson(User.ContextFor(system))); | ||||
|         } | ||||
| @@ -69,13 +69,14 @@ namespace PluralKit.API | ||||
|         [HttpGet("{hid}/members")] | ||||
|         public async Task<ActionResult<IEnumerable<JObject>>> GetMembers(string hid) | ||||
|         { | ||||
|             var system = await _data.GetSystemByHid(hid); | ||||
|             if (system == null) return NotFound("System not found."); | ||||
|             var system = await _db.Execute(c => _repo.GetSystemByHid(c, hid)); | ||||
|             if (system == null) | ||||
|                 return NotFound("System not found."); | ||||
|  | ||||
|             if (!system.MemberListPrivacy.CanAccess(User.ContextFor(system))) | ||||
|                 return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view member list."); | ||||
|  | ||||
|             var members = _data.GetSystemMembers(system); | ||||
|             var members = _db.Execute(c => _repo.GetSystemMembers(c, system.Id)); | ||||
|             return Ok(await members | ||||
|                 .Where(m => m.MemberVisibility.CanAccess(User.ContextFor(system))) | ||||
|                 .Select(m => m.ToJson(User.ContextFor(system))) | ||||
| @@ -87,39 +88,40 @@ namespace PluralKit.API | ||||
|         { | ||||
|             if (before == null) before = SystemClock.Instance.GetCurrentInstant(); | ||||
|              | ||||
|             var system = await _data.GetSystemByHid(hid); | ||||
|             await using var conn = await _db.Obtain(); | ||||
|              | ||||
|             var system = await _repo.GetSystemByHid(conn, hid); | ||||
|             if (system == null) return NotFound("System not found."); | ||||
|  | ||||
|             var auth = await _auth.AuthorizeAsync(User, system, "ViewFrontHistory"); | ||||
|             if (!auth.Succeeded) return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view front history."); | ||||
|  | ||||
|             using (var conn = await _db.Obtain()) | ||||
|             { | ||||
|                 var res = await conn.QueryAsync<SwitchesReturn>( | ||||
|                     @"select *, array( | ||||
|             var res = await conn.QueryAsync<SwitchesReturn>( | ||||
|                 @"select *, array( | ||||
|                         select members.hid from switch_members, members | ||||
|                         where switch_members.switch = switches.id and members.id = switch_members.member | ||||
|                     ) as members from switches | ||||
|                     where switches.system = @System and switches.timestamp < @Before | ||||
|                     order by switches.timestamp desc | ||||
|                     limit 100;", new {System = system.Id, Before = before}); | ||||
|                 return Ok(res); | ||||
|             } | ||||
|             return Ok(res); | ||||
|         } | ||||
|  | ||||
|         [HttpGet("{hid}/fronters")] | ||||
|         public async Task<ActionResult<FrontersReturn>> GetFronters(string hid) | ||||
|         { | ||||
|             var system = await _data.GetSystemByHid(hid); | ||||
|             await using var conn = await _db.Obtain(); | ||||
|              | ||||
|             var system = await _repo.GetSystemByHid(conn, hid); | ||||
|             if (system == null) return NotFound("System not found."); | ||||
|              | ||||
|             var auth = await _auth.AuthorizeAsync(User, system, "ViewFront"); | ||||
|             if (!auth.Succeeded) return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view fronter."); | ||||
|              | ||||
|             var sw = await _data.GetLatestSwitch(system.Id); | ||||
|             var sw = await _repo.GetLatestSwitch(conn, system.Id); | ||||
|             if (sw == null) return NotFound("System has no registered switches.");  | ||||
|                  | ||||
|             var members = _data.GetSwitchMembers(sw); | ||||
|             var members = _repo.GetSwitchMembers(conn, sw.Id); | ||||
|             return Ok(new FrontersReturn | ||||
|             { | ||||
|                 Timestamp = sw.Timestamp, | ||||
| @@ -131,7 +133,8 @@ namespace PluralKit.API | ||||
|         [Authorize] | ||||
|         public async Task<ActionResult<JObject>> EditSystem([FromBody] JObject changes) | ||||
|         { | ||||
|             var system = await _db.Execute(c => c.QuerySystem(User.CurrentSystem())); | ||||
|             await using var conn = await _db.Obtain(); | ||||
|             var system = await _repo.GetSystem(conn, User.CurrentSystem()); | ||||
|  | ||||
|             SystemPatch patch; | ||||
|             try | ||||
| @@ -143,7 +146,7 @@ namespace PluralKit.API | ||||
|                 return BadRequest(e.Message); | ||||
|             } | ||||
|  | ||||
|             await _db.Execute(conn => conn.UpdateSystem(system.Id, patch)); | ||||
|             await _repo.UpdateSystem(conn, system!.Id, patch); | ||||
|             return Ok(system.ToJson(User.ContextFor(system))); | ||||
|         } | ||||
|  | ||||
| @@ -154,11 +157,13 @@ namespace PluralKit.API | ||||
|             if (param.Members.Distinct().Count() != param.Members.Count) | ||||
|                 return BadRequest("Duplicate members in member list."); | ||||
|              | ||||
|             await using var conn = await _db.Obtain(); | ||||
|  | ||||
|             // We get the current switch, if it exists | ||||
|             var latestSwitch = await _data.GetLatestSwitch(User.CurrentSystem()); | ||||
|             var latestSwitch = await _repo.GetLatestSwitch(conn, User.CurrentSystem()); | ||||
|             if (latestSwitch != null) | ||||
|             { | ||||
|                 var latestSwitchMembers = _data.GetSwitchMembers(latestSwitch); | ||||
|                 var latestSwitchMembers = _repo.GetSwitchMembers(conn, latestSwitch.Id); | ||||
|  | ||||
|                 // Bail if this switch is identical to the latest one | ||||
|                 if (await latestSwitchMembers.Select(m => m.Hid).SequenceEqualAsync(param.Members.ToAsyncEnumerable())) | ||||
| @@ -166,9 +171,7 @@ namespace PluralKit.API | ||||
|             } | ||||
|  | ||||
|             // Resolve member objects for all given IDs | ||||
|             IEnumerable<PKMember> membersList; | ||||
|             using (var conn = await _db.Obtain()) | ||||
|                 membersList = (await conn.QueryAsync<PKMember>("select * from members where hid = any(@Hids)", new {Hids = param.Members})).ToList(); | ||||
|             var membersList = (await conn.QueryAsync<PKMember>("select * from members where hid = any(@Hids)", new {Hids = param.Members})).ToList(); | ||||
|              | ||||
|             foreach (var member in membersList) | ||||
|                 if (member.System != User.CurrentSystem()) | ||||
| @@ -182,12 +185,13 @@ namespace PluralKit.API | ||||
|             // We do this without .Select() since we want to have the early return bail if it doesn't find the member | ||||
|             foreach (var givenMemberId in param.Members) | ||||
|             { | ||||
|                 if (!membersDict.TryGetValue(givenMemberId, out var member)) return BadRequest($"Member '{givenMemberId}' not found."); | ||||
|                 if (!membersDict.TryGetValue(givenMemberId, out var member))  | ||||
|                     return BadRequest($"Member '{givenMemberId}' not found."); | ||||
|                 membersInOrder.Add(member); | ||||
|             } | ||||
|  | ||||
|             // Finally, log the switch (yay!) | ||||
|             await _data.AddSwitch(User.CurrentSystem(), membersInOrder); | ||||
|             await _repo.AddSwitch(conn, User.CurrentSystem(), membersInOrder.Select(m => m.Id).ToList()); | ||||
|             return NoContent(); | ||||
|         } | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user