mirror of
https://github.com/Jetsparrow/jetherald.git
synced 2026-01-20 23:56:08 +03:00
rewrite working with db.
This commit is contained in:
parent
4cdeb8a7c7
commit
31c6ced153
@ -21,7 +21,8 @@ public class ListCommand : IChatCommand
|
|||||||
|
|
||||||
var msg = update.Message;
|
var msg = update.Message;
|
||||||
var chatid = msg.Chat.Id;
|
var chatid = msg.Chat.Id;
|
||||||
var topics = await Db.GetTopicsForSub(NamespacedId.Telegram(chatid));
|
using var ctx = await Db.GetContext();
|
||||||
|
var topics = await ctx.GetTopicsForSub(NamespacedId.Telegram(chatid));
|
||||||
|
|
||||||
return topics.Any()
|
return topics.Any()
|
||||||
? "Topics:\n" + string.Join("\n", topics)
|
? "Topics:\n" + string.Join("\n", topics)
|
||||||
|
|||||||
@ -25,7 +25,8 @@ public class SubscribeCommand : IChatCommand
|
|||||||
var chat = NamespacedId.Telegram(args.Message.Chat.Id);
|
var chat = NamespacedId.Telegram(args.Message.Chat.Id);
|
||||||
var token = cmd.Parameters[0];
|
var token = cmd.Parameters[0];
|
||||||
|
|
||||||
var topic = await Db.GetTopicForSub(token, chat);
|
using var ctx = await Db.GetContext();
|
||||||
|
var topic = await ctx.GetTopicForSub(token, chat);
|
||||||
|
|
||||||
if (topic == null)
|
if (topic == null)
|
||||||
return "topic not found";
|
return "topic not found";
|
||||||
@ -35,7 +36,8 @@ public class SubscribeCommand : IChatCommand
|
|||||||
return "token mismatch";
|
return "token mismatch";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await Db.CreateSubscription(topic.TopicId, chat);
|
await ctx.CreateSubscription(topic.TopicId, chat);
|
||||||
|
ctx.Commit();
|
||||||
return $"subscribed to {topic.Name}";
|
return $"subscribed to {topic.Name}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,9 @@ public class UnsubscribeCommand : IChatCommand
|
|||||||
var chat = NamespacedId.Telegram(msg.Chat.Id);
|
var chat = NamespacedId.Telegram(msg.Chat.Id);
|
||||||
|
|
||||||
var topicName = cmd.Parameters[0];
|
var topicName = cmd.Parameters[0];
|
||||||
int affected = await Db.RemoveSubscription(topicName, chat);
|
using var ctx = await Db.GetContext();
|
||||||
|
int affected = await ctx.RemoveSubscription(topicName, chat);
|
||||||
|
ctx.Commit();
|
||||||
if (affected >= 1)
|
if (affected >= 1)
|
||||||
return $"unsubscribed from {topicName}";
|
return $"unsubscribed from {topicName}";
|
||||||
else
|
else
|
||||||
|
|||||||
@ -67,7 +67,8 @@ public class HeartbeatController : ControllerBase
|
|||||||
{
|
{
|
||||||
var heart = args.Heart ?? "General";
|
var heart = args.Heart ?? "General";
|
||||||
|
|
||||||
var t = await Db.GetTopic(args.Topic);
|
var ctx = await Db.GetContext();
|
||||||
|
var t = await ctx.GetTopic(args.Topic);
|
||||||
if (t == null)
|
if (t == null)
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
else if (!t.WriteToken.Equals(args.WriteToken, StringComparison.Ordinal))
|
else if (!t.WriteToken.Equals(args.WriteToken, StringComparison.Ordinal))
|
||||||
@ -76,8 +77,8 @@ public class HeartbeatController : ControllerBase
|
|||||||
if (Timeouts.IsTimedOut(t.TopicId))
|
if (Timeouts.IsTimedOut(t.TopicId))
|
||||||
return StatusCode(StatusCodes.Status429TooManyRequests);
|
return StatusCode(StatusCodes.Status429TooManyRequests);
|
||||||
|
|
||||||
var wasBeating = await Db.ReportHeartbeat(t.TopicId, heart, args.ExpiryTimeout);
|
var wasBeating = await ctx.ReportHeartbeat(t.TopicId, heart, args.ExpiryTimeout);
|
||||||
|
ctx.Commit();
|
||||||
if (wasBeating == 0)
|
if (wasBeating == 0)
|
||||||
await Herald.BroadcastMessageRaw(t.TopicId, $"!{t.Description}!:\nHeart \"{heart}\" has started beating at {DateTime.UtcNow:O}");
|
await Herald.BroadcastMessageRaw(t.TopicId, $"!{t.Description}!:\nHeart \"{heart}\" has started beating at {DateTime.UtcNow:O}");
|
||||||
|
|
||||||
|
|||||||
@ -52,7 +52,12 @@ public class ReportController : ControllerBase
|
|||||||
|
|
||||||
private async Task<IActionResult> DoReport(ReportArgs args)
|
private async Task<IActionResult> DoReport(ReportArgs args)
|
||||||
{
|
{
|
||||||
var t = await Db.GetTopic(args.Topic);
|
Contracts.Topic t;
|
||||||
|
using (var ctx = await Db.GetContext())
|
||||||
|
{
|
||||||
|
t = await ctx.GetTopic(args.Topic);
|
||||||
|
}
|
||||||
|
|
||||||
if (t == null)
|
if (t == null)
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
else if (!t.WriteToken.Equals(args.WriteToken, StringComparison.OrdinalIgnoreCase))
|
else if (!t.WriteToken.Equals(args.WriteToken, StringComparison.OrdinalIgnoreCase))
|
||||||
|
|||||||
@ -31,9 +31,10 @@ public class AdminToolsController : Controller
|
|||||||
[HttpGet, Route("ui/admintools/invites")]
|
[HttpGet, Route("ui/admintools/invites")]
|
||||||
public async Task<IActionResult> ViewInvites()
|
public async Task<IActionResult> ViewInvites()
|
||||||
{
|
{
|
||||||
var invites = await Db.GetInvites();
|
using var ctx = await Db.GetContext();
|
||||||
var plans = await Db.GetPlans();
|
var invites = await ctx.GetInvites();
|
||||||
var roles = await Db.GetRoles();
|
var plans = await ctx.GetPlans();
|
||||||
|
var roles = await ctx.GetRoles();
|
||||||
return View(new ViewInvitesModel
|
return View(new ViewInvitesModel
|
||||||
{
|
{
|
||||||
Invites = invites.ToArray(),
|
Invites = invites.ToArray(),
|
||||||
@ -52,7 +53,9 @@ public class AdminToolsController : Controller
|
|||||||
[HttpPost, Route("ui/admintools/invites/create")]
|
[HttpPost, Route("ui/admintools/invites/create")]
|
||||||
public async Task<IActionResult> CreateInvite(CreateInviteRequest req)
|
public async Task<IActionResult> CreateInvite(CreateInviteRequest req)
|
||||||
{
|
{
|
||||||
await Db.CreateUserInvite(req.PlanId, req.RoleId, TokenHelper.GetToken(AuthCfg.InviteCodeLength));
|
using var ctx = await Db.GetContext();
|
||||||
|
await ctx.CreateUserInvite(req.PlanId, req.RoleId, TokenHelper.GetToken(AuthCfg.InviteCodeLength));
|
||||||
|
ctx.Commit();
|
||||||
return RedirectToAction(nameof(ViewInvites));
|
return RedirectToAction(nameof(ViewInvites));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,9 +21,10 @@ public class DashboardController : Controller
|
|||||||
public async Task<IActionResult> Index()
|
public async Task<IActionResult> Index()
|
||||||
{
|
{
|
||||||
var login = HttpContext.User.GetUserLogin();
|
var login = HttpContext.User.GetUserLogin();
|
||||||
var user = await Db.GetUser(login);
|
using var ctx = await Db.GetContext();
|
||||||
var topics = await Db.GetTopicsForUser(user.UserId);
|
var user = await ctx.GetUser(login);
|
||||||
var hearts = await Db.GetHeartsForUser(user.UserId);
|
var topics = await ctx.GetTopicsForUser(user.UserId);
|
||||||
|
var hearts = await ctx.GetHeartsForUser(user.UserId);
|
||||||
var vm = new DashboardViewModel
|
var vm = new DashboardViewModel
|
||||||
{
|
{
|
||||||
Topics = topics.ToArray(),
|
Topics = topics.ToArray(),
|
||||||
|
|||||||
@ -51,7 +51,8 @@ public class LoginController : Controller
|
|||||||
|
|
||||||
ViewData["RedirectTo"] = PathStringOrDefault(redirect);
|
ViewData["RedirectTo"] = PathStringOrDefault(redirect);
|
||||||
|
|
||||||
var user = await Db.GetUser(req.Username);
|
using var ctx = await Db.GetContext();
|
||||||
|
var user = await ctx.GetUser(req.Username);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError("", "User not found");
|
ModelState.AddModelError("", "User not found");
|
||||||
|
|||||||
@ -19,7 +19,8 @@ public class ProfileController : Controller
|
|||||||
public async Task<IActionResult> Index()
|
public async Task<IActionResult> Index()
|
||||||
{
|
{
|
||||||
var login = HttpContext.User.GetUserLogin();
|
var login = HttpContext.User.GetUserLogin();
|
||||||
var user = await Db.GetUser(login);
|
using var ctx = await Db.GetContext();
|
||||||
|
var user = await ctx.GetUser(login);
|
||||||
|
|
||||||
var vm = new ProfileViewModel
|
var vm = new ProfileViewModel
|
||||||
{
|
{
|
||||||
|
|||||||
@ -69,13 +69,14 @@ public class RegistrationController : Controller
|
|||||||
|
|
||||||
ViewData["RedirectTo"] = PathStringOrDefault(redirect);
|
ViewData["RedirectTo"] = PathStringOrDefault(redirect);
|
||||||
|
|
||||||
var oldUser = await Db.GetUser(req.Login);
|
using var ctx = await Db.GetContext();
|
||||||
|
var oldUser = await ctx.GetUser(req.Login);
|
||||||
if (oldUser != null)
|
if (oldUser != null)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError("", "User already exists");
|
ModelState.AddModelError("", "User already exists");
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
var invite = await Db.GetInviteByCode(req.InviteCode);
|
var invite = await ctx.GetInviteByCode(req.InviteCode);
|
||||||
if (invite == null || invite.RedeemedBy != default)
|
if (invite == null || invite.RedeemedBy != default)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError("", "No unredeemed invite with this code found");
|
ModelState.AddModelError("", "No unredeemed invite with this code found");
|
||||||
@ -91,8 +92,9 @@ public class RegistrationController : Controller
|
|||||||
PasswordSalt = RandomNumberGenerator.GetBytes(64)
|
PasswordSalt = RandomNumberGenerator.GetBytes(64)
|
||||||
};
|
};
|
||||||
user.PasswordHash = AuthUtils.GetHashFor(req.Password, user.PasswordSalt, user.HashType);
|
user.PasswordHash = AuthUtils.GetHashFor(req.Password, user.PasswordSalt, user.HashType);
|
||||||
user = await Db.RegisterUser(user);
|
user = await ctx.RegisterUser(user);
|
||||||
await Db.RedeemInvite(invite.UserInviteId, user.UserId);
|
await ctx.RedeemInvite(invite.UserInviteId, user.UserId);
|
||||||
|
ctx.Commit();
|
||||||
var userIdentity = AuthUtils.CreateIdentity(user.UserId, user.Login, user.Name, user.Allow);
|
var userIdentity = AuthUtils.CreateIdentity(user.UserId, user.Login, user.Name, user.Allow);
|
||||||
var principal = new ClaimsPrincipal(userIdentity);
|
var principal = new ClaimsPrincipal(userIdentity);
|
||||||
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
|
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
|
||||||
|
|||||||
@ -36,7 +36,9 @@ public class TopicController : Controller
|
|||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
return View();
|
return View();
|
||||||
var userId = HttpContext.User.GetUserId();
|
var userId = HttpContext.User.GetUserId();
|
||||||
var topic = await Db.CreateTopic(userId, req.Name, req.Description);
|
using var ctx = await Db.GetContext();
|
||||||
|
var topic = await ctx.CreateTopic(userId, req.Name, req.Description);
|
||||||
|
ctx.Commit();
|
||||||
if (topic == null)
|
if (topic == null)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError("", "Unknown error");
|
ModelState.AddModelError("", "Unknown error");
|
||||||
@ -50,11 +52,12 @@ public class TopicController : Controller
|
|||||||
public async Task<IActionResult> ViewTopic(string topicName)
|
public async Task<IActionResult> ViewTopic(string topicName)
|
||||||
{
|
{
|
||||||
var userId = HttpContext.User.GetUserId();
|
var userId = HttpContext.User.GetUserId();
|
||||||
var topic = await Db.GetTopic(topicName);
|
using var ctx = await Db.GetContext();
|
||||||
|
var topic = await ctx.GetTopic(topicName);
|
||||||
if (topic == null || topic.CreatorId != userId)
|
if (topic == null || topic.CreatorId != userId)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
var hearts = await Db.GetHeartsForTopic(topic.TopicId);
|
var hearts = await ctx.GetHeartsForTopic(topic.TopicId);
|
||||||
var vm = new TopicViewModel
|
var vm = new TopicViewModel
|
||||||
{
|
{
|
||||||
Topic = topic,
|
Topic = topic,
|
||||||
|
|||||||
@ -12,7 +12,8 @@ public class AnonymousUserMassagerMiddleware : IMiddleware
|
|||||||
{
|
{
|
||||||
AnonymousPermissions = new Lazy<Task<string>>(async () =>
|
AnonymousPermissions = new Lazy<Task<string>>(async () =>
|
||||||
{
|
{
|
||||||
var anonymousUser = await db.GetUser("Anonymous");
|
using var ctx = await db.GetContext();
|
||||||
|
var anonymousUser = await ctx.GetUser("Anonymous");
|
||||||
return anonymousUser.Allow;
|
return anonymousUser.Allow;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,12 +91,12 @@ try
|
|||||||
// preflight checks
|
// preflight checks
|
||||||
{
|
{
|
||||||
var db = app.Services.GetService<Db>();
|
var db = app.Services.GetService<Db>();
|
||||||
|
using var ctx = await db.GetContext();
|
||||||
var adminUser = await db.GetUser("admin");
|
var adminUser = await ctx.GetUser("admin");
|
||||||
if (adminUser == null)
|
if (adminUser == null)
|
||||||
{
|
{
|
||||||
var adminRole = (await db.GetRoles()).First(r => r.Name == "admin");
|
var adminRole = (await ctx.GetRoles()).First(r => r.Name == "admin");
|
||||||
var unlimitedPlan = (await db.GetPlans()).First(p => p.Name == "unlimited");
|
var unlimitedPlan = (await ctx.GetPlans()).First(p => p.Name == "unlimited");
|
||||||
|
|
||||||
var authCfg = app.Services.GetService<IOptions<AuthConfig>>().Value;
|
var authCfg = app.Services.GetService<IOptions<AuthConfig>>().Value;
|
||||||
var password = Convert.ToBase64String(RandomNumberGenerator.GetBytes(48));
|
var password = Convert.ToBase64String(RandomNumberGenerator.GetBytes(48));
|
||||||
@ -110,7 +110,8 @@ try
|
|||||||
PlanId = unlimitedPlan.PlanId
|
PlanId = unlimitedPlan.PlanId
|
||||||
};
|
};
|
||||||
adminUser.PasswordHash = AuthUtils.GetHashFor(password, adminUser.PasswordSalt, adminUser.HashType);
|
adminUser.PasswordHash = AuthUtils.GetHashFor(password, adminUser.PasswordSalt, adminUser.HashType);
|
||||||
var newUser = await db.RegisterUser(adminUser);
|
var newUser = await ctx.RegisterUser(adminUser);
|
||||||
|
ctx.Commit();
|
||||||
log.Warn($"Created administrative account {adminUser.Login}:{password}. Be sure to save these credentials somewhere!");
|
log.Warn($"Created administrative account {adminUser.Login}:{password}. Be sure to save these credentials somewhere!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,288 +0,0 @@
|
|||||||
using MySql.Data.MySqlClient;
|
|
||||||
using Dapper;
|
|
||||||
using JetHerald.Options;
|
|
||||||
using JetHerald.Contracts;
|
|
||||||
|
|
||||||
namespace JetHerald.Services;
|
|
||||||
public class Db
|
|
||||||
{
|
|
||||||
public async Task<IEnumerable<Topic>> GetTopicsForUser(uint userId)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.QueryAsync<Topic>(
|
|
||||||
" SELECT * FROM topic WHERE CreatorId = @userId",
|
|
||||||
new { userId });
|
|
||||||
}
|
|
||||||
public async Task<IEnumerable<Plan>> GetPlans()
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.QueryAsync<Plan>("SELECT * FROM plan");
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Role>> GetRoles()
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.QueryAsync<Role>("SELECT * FROM role");
|
|
||||||
}
|
|
||||||
public async Task<IEnumerable<UserInvite>> GetInvites()
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.QueryAsync<UserInvite>("SELECT * FROM userinvite");
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Heart>> GetHeartsForUser(uint userId)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.QueryAsync<Heart>(
|
|
||||||
" SELECT h.* FROM heart h JOIN topic USING (TopicId) WHERE CreatorId = @userId",
|
|
||||||
new { userId });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task CreateUserInvite(uint planId, uint roleId, string inviteCode)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
await c.ExecuteAsync(@"
|
|
||||||
INSERT INTO userinvite
|
|
||||||
( PlanId, RoleId, InviteCode)
|
|
||||||
VALUES
|
|
||||||
(@planId, @roleId, @inviteCode)",
|
|
||||||
new { planId, roleId, inviteCode });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Topic> GetTopic(string name)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.QuerySingleOrDefaultAsync<Topic>(
|
|
||||||
"SELECT * FROM topic WHERE Name = @name",
|
|
||||||
new { name });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> DeleteTopic(string name, uint userId)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.ExecuteAsync(
|
|
||||||
" DELETE FROM topic WHERE Name = @name AND CreatorId = @userId",
|
|
||||||
new { name, userId });
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public async Task<Topic> GetTopicForSub(string token, NamespacedId sub)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.QuerySingleOrDefaultAsync<Topic>(
|
|
||||||
" SELECT t.*, ts.Sub " +
|
|
||||||
" FROM topic t " +
|
|
||||||
" LEFT JOIN topic_sub ts ON t.TopicId = ts.TopicId AND ts.Sub = @sub " +
|
|
||||||
" WHERE ReadToken = @token",
|
|
||||||
new { token, sub });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Heart>> GetHeartsForTopic(uint topicId)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.QueryAsync<Heart>(
|
|
||||||
" SELECT * FROM heart WHERE TopicId = @topicId",
|
|
||||||
new { topicId });
|
|
||||||
}
|
|
||||||
public async Task<User> GetUser(string login)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.QuerySingleOrDefaultAsync<User>(@"
|
|
||||||
SELECT u.*, up.*, ur.*
|
|
||||||
FROM user u
|
|
||||||
JOIN plan up ON u.PlanId = up.PlanId
|
|
||||||
JOIN role ur ON u.RoleId = ur.RoleId
|
|
||||||
WHERE u.Login = @login;",
|
|
||||||
new { login });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Topic> CreateTopic(uint user, string name, string descr)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
|
|
||||||
await c.OpenAsync();
|
|
||||||
|
|
||||||
await using var tx = await c.BeginTransactionAsync();
|
|
||||||
|
|
||||||
var topicsCount = await c.QuerySingleAsync<int>(
|
|
||||||
" SELECT COUNT(*) " +
|
|
||||||
" FROM user u " +
|
|
||||||
" LEFT JOIN topic t ON t.CreatorId = u.UserId " +
|
|
||||||
" WHERE u.UserId = @user",
|
|
||||||
new { user },
|
|
||||||
transaction: tx
|
|
||||||
);
|
|
||||||
|
|
||||||
var planTopicsCount = await c.QuerySingleAsync<int>(
|
|
||||||
" SELECT p.MaxTopics " +
|
|
||||||
" FROM user u " +
|
|
||||||
" LEFT JOIN plan p ON p.PlanId = u.PlanId " +
|
|
||||||
" WHERE u.UserId = @user",
|
|
||||||
new { user },
|
|
||||||
transaction: tx
|
|
||||||
);
|
|
||||||
|
|
||||||
if (topicsCount >= planTopicsCount) return null;
|
|
||||||
|
|
||||||
var topic = await c.QuerySingleOrDefaultAsync<Topic>(
|
|
||||||
" INSERT INTO topic " +
|
|
||||||
" ( CreatorId, Name, Description, ReadToken, WriteToken) " +
|
|
||||||
" VALUES " +
|
|
||||||
" (@CreatorId, @Name, @Description, @ReadToken, @WriteToken); " +
|
|
||||||
" SELECT * FROM topic WHERE TopicId = LAST_INSERT_ID(); ",
|
|
||||||
new Topic
|
|
||||||
{
|
|
||||||
CreatorId = user,
|
|
||||||
Name = name,
|
|
||||||
Description = descr,
|
|
||||||
ReadToken = TokenHelper.GetToken(),
|
|
||||||
WriteToken = TokenHelper.GetToken()
|
|
||||||
}, transaction: tx);
|
|
||||||
|
|
||||||
await tx.CommitAsync();
|
|
||||||
|
|
||||||
return topic;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<User> RegisterUser(User user)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
uint userId = await c.QuerySingleOrDefaultAsync<uint>(@"
|
|
||||||
INSERT INTO user
|
|
||||||
( Login, Name, PasswordHash, PasswordSalt, HashType, PlanId, RoleId)
|
|
||||||
VALUES
|
|
||||||
(@Login, @Name, @PasswordHash, @PasswordSalt, @HashType, @PlanId, @RoleId);",
|
|
||||||
param:user);
|
|
||||||
return await GetUser(user.Login);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task RedeemInvite(uint inviteId, uint userId)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
await c.ExecuteAsync(
|
|
||||||
@"UPDATE userinvite SET RedeemedBy = @userId WHERE UserInviteId = @inviteId",
|
|
||||||
new { inviteId, userId });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<UserInvite> GetInviteByCode(string inviteCode)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.QuerySingleOrDefaultAsync<UserInvite>(
|
|
||||||
" SELECT * FROM userinvite " +
|
|
||||||
" WHERE InviteCode = @inviteCode " +
|
|
||||||
" AND RedeemedBy IS NULL ",
|
|
||||||
new { inviteCode });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<NamespacedId>> GetSubsForTopic(uint topicId)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.QueryAsync<NamespacedId>(
|
|
||||||
" SELECT Sub " +
|
|
||||||
" FROM topic_sub " +
|
|
||||||
" WHERE TopicId = @topicid",
|
|
||||||
new { topicId });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Topic>> GetTopicsForSub(NamespacedId sub)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.QueryAsync<Topic>(
|
|
||||||
" SELECT t.*" +
|
|
||||||
" FROM topic_sub ts" +
|
|
||||||
" JOIN topic t USING (TopicId)" +
|
|
||||||
" WHERE ts.Sub = @sub",
|
|
||||||
new { sub });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task CreateSubscription(uint topicId, NamespacedId sub)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
await c.ExecuteAsync(
|
|
||||||
" INSERT INTO topic_sub" +
|
|
||||||
" (TopicId, Sub)" +
|
|
||||||
" VALUES" +
|
|
||||||
" (@topicId, @sub)",
|
|
||||||
new { topicId, sub });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> RemoveSubscription(string topicName, NamespacedId sub)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.ExecuteAsync(
|
|
||||||
" DELETE ts " +
|
|
||||||
" FROM topic_sub ts" +
|
|
||||||
" JOIN topic t USING (TopicId) " +
|
|
||||||
" WHERE t.Name = @topicName AND ts.Sub = @sub;",
|
|
||||||
new { topicName, sub });
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public async Task<int> ReportHeartbeat(uint topicId, string heart, int timeoutSeconds)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.QueryFirstAsync<int>(
|
|
||||||
@"CALL report_heartbeat(@topicId, @heart, @timeoutSeconds);",
|
|
||||||
new { topicId, heart, timeoutSeconds });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<HeartEvent>> ProcessHearts()
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.QueryAsync<HeartEvent>("CALL process_hearts();");
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task MarkHeartAttackReported(ulong id)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
await c.ExecuteAsync("UPDATE heartevent SET Status = 'reported' WHERE HeartEventId = @id", new { id });
|
|
||||||
}
|
|
||||||
|
|
||||||
#region authorization
|
|
||||||
|
|
||||||
public async Task RemoveSession(string sessionId)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
await c.ExecuteAsync("DELETE FROM usersession WHERE SessionId = @sessionId", new {sessionId});
|
|
||||||
}
|
|
||||||
public async Task<UserSession> GetSession(string sessionId)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
return await c.QuerySingleOrDefaultAsync<UserSession>(
|
|
||||||
"SELECT * FROM usersession WHERE SessionId = @sessionId",
|
|
||||||
new { sessionId });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task UpdateSession(string sessionId, byte[] data, DateTime expiryTs)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
await c.ExecuteAsync(@"
|
|
||||||
UPDATE usersession SET
|
|
||||||
SessionData = @data,
|
|
||||||
ExpiryTs = @expiryTs
|
|
||||||
WHERE SessionId = @sessionId;",
|
|
||||||
new { sessionId, data, expiryTs });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> CreateSession(string sessionId, byte[] data, DateTime expiryTs)
|
|
||||||
{
|
|
||||||
using var c = GetConnection();
|
|
||||||
await c.ExecuteAsync(@"
|
|
||||||
INSERT INTO usersession
|
|
||||||
(SessionId, SessionData, ExpiryTs)
|
|
||||||
VALUES
|
|
||||||
(@sessionId, @data, @expiryTs);",
|
|
||||||
new { sessionId, data, expiryTs });
|
|
||||||
return sessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public Db(IOptionsMonitor<ConnectionStrings> cfg)
|
|
||||||
{
|
|
||||||
Config = cfg;
|
|
||||||
}
|
|
||||||
IOptionsMonitor<ConnectionStrings> Config { get; }
|
|
||||||
public MySqlConnection GetConnection() => new(Config.CurrentValue.DefaultConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
238
JetHerald/Services/DbContext.cs
Normal file
238
JetHerald/Services/DbContext.cs
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
using System.Data;
|
||||||
|
using System.Threading;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using MySql.Data.MySqlClient;
|
||||||
|
using Dapper.Transaction;
|
||||||
|
using JetHerald.Options;
|
||||||
|
using JetHerald.Contracts;
|
||||||
|
|
||||||
|
namespace JetHerald.Services;
|
||||||
|
|
||||||
|
public class Db
|
||||||
|
{
|
||||||
|
public Db(IOptionsMonitor<ConnectionStrings> cfg)
|
||||||
|
{
|
||||||
|
Config = cfg;
|
||||||
|
}
|
||||||
|
IOptionsMonitor<ConnectionStrings> Config { get; }
|
||||||
|
MySqlConnection GetConnection() => new(Config.CurrentValue.DefaultConnection);
|
||||||
|
public async Task<DbContext> GetContext(
|
||||||
|
IsolationLevel lvl = IsolationLevel.RepeatableRead,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var conn = GetConnection();
|
||||||
|
if (conn.State != ConnectionState.Open)
|
||||||
|
await conn.OpenAsync();
|
||||||
|
|
||||||
|
var tran = await conn.BeginTransactionAsync(lvl, token);
|
||||||
|
return new DbContext(tran);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DbContext : IDisposable
|
||||||
|
{
|
||||||
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||||
|
public DbContext(IDbTransaction tran)
|
||||||
|
{
|
||||||
|
Tran = tran;
|
||||||
|
Conn = Tran.Connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDbConnection Conn;
|
||||||
|
IDbTransaction Tran;
|
||||||
|
|
||||||
|
public void Commit() => Tran.Commit();
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Tran.Dispose();
|
||||||
|
Conn.Dispose();
|
||||||
|
}
|
||||||
|
public Task<IEnumerable<Topic>> GetTopicsForUser(uint userId)
|
||||||
|
=> Tran.QueryAsync<Topic>(
|
||||||
|
" SELECT * FROM topic WHERE CreatorId = @userId",
|
||||||
|
new { userId });
|
||||||
|
public Task<IEnumerable<Plan>> GetPlans()
|
||||||
|
=> Tran.QueryAsync<Plan>("SELECT * FROM plan");
|
||||||
|
public Task<IEnumerable<Role>> GetRoles()
|
||||||
|
=> Tran.QueryAsync<Role>("SELECT * FROM role");
|
||||||
|
public Task<IEnumerable<UserInvite>> GetInvites()
|
||||||
|
=> Tran.QueryAsync<UserInvite>("SELECT * FROM userinvite");
|
||||||
|
|
||||||
|
public Task<IEnumerable<Heart>> GetHeartsForUser(uint userId)
|
||||||
|
=> Tran.QueryAsync<Heart>(
|
||||||
|
" SELECT h.* FROM heart h JOIN topic USING (TopicId) WHERE CreatorId = @userId",
|
||||||
|
new { userId });
|
||||||
|
|
||||||
|
public Task CreateUserInvite(uint planId, uint roleId, string inviteCode)
|
||||||
|
=> Tran.ExecuteAsync(@"
|
||||||
|
INSERT INTO userinvite
|
||||||
|
( PlanId, RoleId, InviteCode)
|
||||||
|
VALUES
|
||||||
|
(@planId, @roleId, @inviteCode)",
|
||||||
|
new { planId, roleId, inviteCode });
|
||||||
|
|
||||||
|
public Task<Topic> GetTopic(string name)
|
||||||
|
=> Tran.QuerySingleOrDefaultAsync<Topic>(
|
||||||
|
"SELECT * FROM topic WHERE Name = @name",
|
||||||
|
new { name });
|
||||||
|
|
||||||
|
public Task<int> DeleteTopic(string name, uint userId)
|
||||||
|
=> Tran.ExecuteAsync(
|
||||||
|
" DELETE FROM topic WHERE Name = @name AND CreatorId = @userId",
|
||||||
|
new { name, userId });
|
||||||
|
|
||||||
|
public Task<Topic> GetTopicForSub(string token, NamespacedId sub)
|
||||||
|
=> Tran.QuerySingleOrDefaultAsync<Topic>(
|
||||||
|
" SELECT t.*, ts.Sub " +
|
||||||
|
" FROM topic t " +
|
||||||
|
" LEFT JOIN topic_sub ts ON t.TopicId = ts.TopicId AND ts.Sub = @sub " +
|
||||||
|
" WHERE ReadToken = @token",
|
||||||
|
new { token, sub });
|
||||||
|
|
||||||
|
public Task<IEnumerable<Heart>> GetHeartsForTopic(uint topicId)
|
||||||
|
=> Tran.QueryAsync<Heart>(
|
||||||
|
" SELECT * FROM heart WHERE TopicId = @topicId",
|
||||||
|
new { topicId });
|
||||||
|
public Task<User> GetUser(string login)
|
||||||
|
=> Tran.QuerySingleOrDefaultAsync<User>(@"
|
||||||
|
SELECT u.*, up.*, ur.*
|
||||||
|
FROM user u
|
||||||
|
JOIN plan up ON u.PlanId = up.PlanId
|
||||||
|
JOIN role ur ON u.RoleId = ur.RoleId
|
||||||
|
WHERE u.Login = @login;",
|
||||||
|
new { login });
|
||||||
|
|
||||||
|
public async Task<Topic> CreateTopic(uint user, string name, string descr)
|
||||||
|
{
|
||||||
|
var topicsCount = await Tran.QuerySingleAsync<int>(
|
||||||
|
" SELECT COUNT(*) " +
|
||||||
|
" FROM user u " +
|
||||||
|
" LEFT JOIN topic t ON t.CreatorId = u.UserId " +
|
||||||
|
" WHERE u.UserId = @user",
|
||||||
|
new { user }
|
||||||
|
);
|
||||||
|
|
||||||
|
var planTopicsCount = await Tran.QuerySingleAsync<int>(
|
||||||
|
" SELECT p.MaxTopics " +
|
||||||
|
" FROM user u " +
|
||||||
|
" LEFT JOIN plan p ON p.PlanId = u.PlanId " +
|
||||||
|
" WHERE u.UserId = @user",
|
||||||
|
new { user }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (topicsCount >= planTopicsCount) return null;
|
||||||
|
|
||||||
|
var topic = await Tran.QuerySingleOrDefaultAsync<Topic>(
|
||||||
|
" INSERT INTO topic " +
|
||||||
|
" ( CreatorId, Name, Description, ReadToken, WriteToken) " +
|
||||||
|
" VALUES " +
|
||||||
|
" (@CreatorId, @Name, @Description, @ReadToken, @WriteToken); " +
|
||||||
|
" SELECT * FROM topic WHERE TopicId = LAST_INSERT_ID(); ",
|
||||||
|
new Topic
|
||||||
|
{
|
||||||
|
CreatorId = user,
|
||||||
|
Name = name,
|
||||||
|
Description = descr,
|
||||||
|
ReadToken = TokenHelper.GetToken(),
|
||||||
|
WriteToken = TokenHelper.GetToken()
|
||||||
|
});
|
||||||
|
return topic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User> RegisterUser(User user)
|
||||||
|
{
|
||||||
|
_ = await Tran.QuerySingleOrDefaultAsync<uint>(@"
|
||||||
|
INSERT INTO user
|
||||||
|
( Login, Name, PasswordHash, PasswordSalt, HashType, PlanId, RoleId)
|
||||||
|
VALUES
|
||||||
|
(@Login, @Name, @PasswordHash, @PasswordSalt, @HashType, @PlanId, @RoleId);",
|
||||||
|
param:user);
|
||||||
|
return await GetUser(user.Login);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task RedeemInvite(uint inviteId, uint userId)
|
||||||
|
=> Tran.ExecuteAsync(
|
||||||
|
@"UPDATE userinvite SET RedeemedBy = @userId WHERE UserInviteId = @inviteId",
|
||||||
|
new { inviteId, userId });
|
||||||
|
|
||||||
|
public Task<UserInvite> GetInviteByCode(string inviteCode)
|
||||||
|
=> Tran.QuerySingleOrDefaultAsync<UserInvite>(
|
||||||
|
" SELECT * FROM userinvite " +
|
||||||
|
" WHERE InviteCode = @inviteCode " +
|
||||||
|
" AND RedeemedBy IS NULL ",
|
||||||
|
new { inviteCode });
|
||||||
|
|
||||||
|
public Task<IEnumerable<NamespacedId>> GetSubsForTopic(uint topicId)
|
||||||
|
=> Tran.QueryAsync<NamespacedId>(
|
||||||
|
" SELECT Sub " +
|
||||||
|
" FROM topic_sub " +
|
||||||
|
" WHERE TopicId = @topicid",
|
||||||
|
new { topicId });
|
||||||
|
|
||||||
|
public Task<IEnumerable<Topic>> GetTopicsForSub(NamespacedId sub)
|
||||||
|
=> Tran.QueryAsync<Topic>(
|
||||||
|
" SELECT t.*" +
|
||||||
|
" FROM topic_sub ts" +
|
||||||
|
" JOIN topic t USING (TopicId)" +
|
||||||
|
" WHERE ts.Sub = @sub",
|
||||||
|
new { sub });
|
||||||
|
|
||||||
|
public Task CreateSubscription(uint topicId, NamespacedId sub)
|
||||||
|
=> Tran.ExecuteAsync(
|
||||||
|
" INSERT INTO topic_sub" +
|
||||||
|
" (TopicId, Sub)" +
|
||||||
|
" VALUES" +
|
||||||
|
" (@topicId, @sub)",
|
||||||
|
new { topicId, sub });
|
||||||
|
|
||||||
|
public Task<int> RemoveSubscription(string topicName, NamespacedId sub)
|
||||||
|
=> Tran.ExecuteAsync(
|
||||||
|
" DELETE ts " +
|
||||||
|
" FROM topic_sub ts" +
|
||||||
|
" JOIN topic t USING (TopicId) " +
|
||||||
|
" WHERE t.Name = @topicName AND ts.Sub = @sub;",
|
||||||
|
new { topicName, sub });
|
||||||
|
|
||||||
|
|
||||||
|
public Task<int> ReportHeartbeat(uint topicId, string heart, int timeoutSeconds)
|
||||||
|
=> Tran.QueryFirstAsync<int>(
|
||||||
|
@"CALL report_heartbeat(@topicId, @heart, @timeoutSeconds);",
|
||||||
|
new { topicId, heart, timeoutSeconds });
|
||||||
|
|
||||||
|
public Task<IEnumerable<HeartEvent>> ProcessHearts()
|
||||||
|
=> Tran.QueryAsync<HeartEvent>("CALL process_hearts();");
|
||||||
|
|
||||||
|
public Task MarkHeartAttackReported(ulong id)
|
||||||
|
=> Tran.ExecuteAsync("UPDATE heartevent SET Status = 'reported' WHERE HeartEventId = @id", new { id });
|
||||||
|
|
||||||
|
#region TicketStore
|
||||||
|
|
||||||
|
public Task RemoveSession(string sessionId)
|
||||||
|
=> Tran.ExecuteAsync("DELETE FROM usersession WHERE SessionId = @sessionId", new { sessionId });
|
||||||
|
public Task<UserSession> GetSession(string sessionId)
|
||||||
|
=> Tran.QuerySingleOrDefaultAsync<UserSession>(
|
||||||
|
"SELECT * FROM usersession WHERE SessionId = @sessionId",
|
||||||
|
new { sessionId });
|
||||||
|
|
||||||
|
public Task UpdateSession(string sessionId, byte[] data, DateTime expiryTs)
|
||||||
|
=> Tran.ExecuteAsync(@"
|
||||||
|
UPDATE usersession SET
|
||||||
|
SessionData = @data,
|
||||||
|
ExpiryTs = @expiryTs
|
||||||
|
WHERE SessionId = @sessionId;",
|
||||||
|
new { sessionId, data, expiryTs });
|
||||||
|
|
||||||
|
public async Task<string> CreateSession(string sessionId, byte[] data, DateTime expiryTs)
|
||||||
|
{
|
||||||
|
await Tran.ExecuteAsync(@"
|
||||||
|
INSERT INTO usersession
|
||||||
|
(SessionId, SessionData, ExpiryTs)
|
||||||
|
VALUES
|
||||||
|
(@sessionId, @data, @expiryTs);",
|
||||||
|
new { sessionId, data, expiryTs });
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
@ -21,7 +21,8 @@ public class DiscordCommands : BaseCommandModule
|
|||||||
_ = ctx.TriggerTypingAsync();
|
_ = ctx.TriggerTypingAsync();
|
||||||
|
|
||||||
var chat = NamespacedId.Discord(ctx.Channel.Id);
|
var chat = NamespacedId.Discord(ctx.Channel.Id);
|
||||||
var topic = await Db.GetTopicForSub(token, chat);
|
using var dbctx = await Db.GetContext();
|
||||||
|
var topic = await dbctx.GetTopicForSub(token, chat);
|
||||||
|
|
||||||
if (topic == null)
|
if (topic == null)
|
||||||
await ctx.RespondAsync("topic not found");
|
await ctx.RespondAsync("topic not found");
|
||||||
@ -31,7 +32,8 @@ public class DiscordCommands : BaseCommandModule
|
|||||||
await ctx.RespondAsync("token mismatch");
|
await ctx.RespondAsync("token mismatch");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await Db.CreateSubscription(topic.TopicId, chat);
|
await dbctx.CreateSubscription(topic.TopicId, chat);
|
||||||
|
dbctx.Commit();
|
||||||
await ctx.RespondAsync($"subscribed to {topic.Name}");
|
await ctx.RespondAsync($"subscribed to {topic.Name}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,8 +48,8 @@ public class DiscordCommands : BaseCommandModule
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
_ = ctx.TriggerTypingAsync();
|
_ = ctx.TriggerTypingAsync();
|
||||||
|
using var dbctx = await Db.GetContext();
|
||||||
int affected = await Db.RemoveSubscription(name, NamespacedId.Discord(ctx.Channel.Id));
|
int affected = await dbctx.RemoveSubscription(name, NamespacedId.Discord(ctx.Channel.Id));
|
||||||
if (affected >= 1)
|
if (affected >= 1)
|
||||||
await ctx.RespondAsync($"unsubscribed from {name}");
|
await ctx.RespondAsync($"unsubscribed from {name}");
|
||||||
else
|
else
|
||||||
|
|||||||
@ -24,14 +24,15 @@ public class HeartMonitor : BackgroundService
|
|||||||
await Task.Delay(1000 * 10, token);
|
await Task.Delay(1000 * 10, token);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var attacks = await Db.ProcessHearts();
|
using var ctx = await Db.GetContext();
|
||||||
|
var attacks = await ctx.ProcessHearts();
|
||||||
foreach (var a in attacks)
|
foreach (var a in attacks)
|
||||||
{
|
{
|
||||||
await Herald.BroadcastMessageRaw(
|
await Herald.BroadcastMessageRaw(
|
||||||
a.TopicId,
|
a.TopicId,
|
||||||
$"!{a.Description}!:\nHeart \"{a.Heart}\" stopped beating at {a.CreateTs:O}");
|
$"!{a.Description}!:\nHeart \"{a.Heart}\" stopped beating at {a.CreateTs:O}");
|
||||||
|
|
||||||
await Db.MarkHeartAttackReported(a.HeartEventId);
|
await ctx.MarkHeartAttackReported(a.HeartEventId);
|
||||||
|
|
||||||
if (token.IsCancellationRequested)
|
if (token.IsCancellationRequested)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -44,7 +44,9 @@ public partial class JetHeraldBot : IHostedService
|
|||||||
|
|
||||||
public async Task BroadcastMessageRaw(uint topicId, string formatted)
|
public async Task BroadcastMessageRaw(uint topicId, string formatted)
|
||||||
{
|
{
|
||||||
var chatIds = await Db.GetSubsForTopic(topicId);
|
IEnumerable<NamespacedId> chatIds;
|
||||||
|
using (var ctx = await Db.GetContext())
|
||||||
|
chatIds = await ctx.GetSubsForTopic(topicId);
|
||||||
foreach (var c in chatIds)
|
foreach (var c in chatIds)
|
||||||
await SendMessageRaw(c, formatted);
|
await SendMessageRaw(c, formatted);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,29 +13,40 @@ public class JetHeraldTicketStore : ITicketStore
|
|||||||
Db = db;
|
Db = db;
|
||||||
Cfg = cfg;
|
Cfg = cfg;
|
||||||
}
|
}
|
||||||
public Task RemoveAsync(string key)
|
public async Task RemoveAsync(string key)
|
||||||
=> Db.RemoveSession(key);
|
{
|
||||||
|
using var ctx = await Db.GetContext();
|
||||||
public Task RenewAsync(string key, AuthenticationTicket ticket)
|
await ctx.RemoveSession(key);
|
||||||
=> Db.UpdateSession(
|
ctx.Commit();
|
||||||
|
}
|
||||||
|
public async Task RenewAsync(string key, AuthenticationTicket ticket)
|
||||||
|
{
|
||||||
|
using var ctx = await Db.GetContext();
|
||||||
|
await ctx.UpdateSession(
|
||||||
key,
|
key,
|
||||||
TicketSerializer.Default.Serialize(ticket),
|
TicketSerializer.Default.Serialize(ticket),
|
||||||
ticket.Properties.ExpiresUtc.Value.DateTime);
|
ticket.Properties.ExpiresUtc.Value.DateTime);
|
||||||
|
ctx.Commit();
|
||||||
|
|
||||||
|
}
|
||||||
public async Task<AuthenticationTicket> RetrieveAsync(string key)
|
public async Task<AuthenticationTicket> RetrieveAsync(string key)
|
||||||
{
|
{
|
||||||
var userSession = await Db.GetSession(key);
|
using var ctx = await Db.GetContext();
|
||||||
|
var userSession = await ctx.GetSession(key);
|
||||||
return TicketSerializer.Default.Deserialize(userSession.SessionData);
|
return TicketSerializer.Default.Deserialize(userSession.SessionData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<string> StoreAsync(AuthenticationTicket ticket)
|
public async Task<string> StoreAsync(AuthenticationTicket ticket)
|
||||||
{
|
{
|
||||||
var cfg = Cfg.CurrentValue;
|
var cfg = Cfg.CurrentValue;
|
||||||
var bytes = RandomNumberGenerator.GetBytes(cfg.TicketIdLengthBytes);
|
var bytes = RandomNumberGenerator.GetBytes(cfg.TicketIdLengthBytes);
|
||||||
var key = Convert.ToBase64String(bytes);
|
var key = Convert.ToBase64String(bytes);
|
||||||
return Db.CreateSession(
|
using var ctx = await Db.GetContext();
|
||||||
|
await ctx.CreateSession(
|
||||||
key,
|
key,
|
||||||
TicketSerializer.Default.Serialize(ticket),
|
TicketSerializer.Default.Serialize(ticket),
|
||||||
ticket.Properties.ExpiresUtc.Value.DateTime);
|
ticket.Properties.ExpiresUtc.Value.DateTime);
|
||||||
|
ctx.Commit();
|
||||||
|
return key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user