add user list page

This commit is contained in:
Basique Evangelist 2022-05-19 21:16:11 +03:00
parent 6b6883bb20
commit c5d4db1c4c
No known key found for this signature in database
GPG Key ID: 0EBE8665AC252DF3
5 changed files with 152 additions and 24 deletions

View File

@ -0,0 +1,61 @@
using JetHerald.Authorization;
using JetHerald.Contracts;
using JetHerald.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace JetHerald.Controllers.Ui;
[Permission("admin.users")]
[Route("ui/admin/users")]
public class AdminUsersController : Controller
{
Db Db { get; }
public AdminUsersController(Db db)
{
Db = db;
}
[HttpGet]
public async Task<IActionResult> Index()
{
using var ctx = await Db.GetContext();
var users = await ctx.GetUsers();
var plans = await ctx.GetPlans();
var roles = await ctx.GetRoles();
return View(new AdminUsersModel
{
Users = users.ToArray(),
Plans = plans.ToDictionary(p => p.PlanId),
Roles = roles.ToDictionary(r => r.RoleId)
});
}
public class SetPermsRequest
{
[BindProperty(Name = "planId"), BindRequired]
public uint PlanId { get; set; }
[BindProperty(Name = "roleId"), BindRequired]
public uint RoleId { get; set; }
}
[Permission("admin.users.setperms")]
[HttpPost, Route("setperms/{userId?}")]
public async Task<IActionResult> SetPerms([FromRoute] uint userId, SetPermsRequest req)
{
using var ctx = await Db.GetContext();
await ctx.UpdatePerms(userId, req.PlanId, req.RoleId);
ctx.Commit();
return RedirectToAction(nameof(Index));
}
}
public class AdminUsersModel
{
public User[] Users { get; set; }
public Dictionary<uint, Plan> Plans { get; set; }
public Dictionary<uint, Role> Roles { get; set; }
}

View File

@ -2,18 +2,18 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages> <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly> <ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<PreserveCompilationContext>false</PreserveCompilationContext> <PreserveCompilationContext>false</PreserveCompilationContext>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Dapper.Transaction" Version="2.0.35.2" /> <PackageReference Include="Dapper.Transaction" Version="2.0.35.2" />
<PackageReference Include="DSharpPlus" Version="4.1.0" /> <PackageReference Include="DSharpPlus" Version="4.2.0" />
<PackageReference Include="DSharpPlus.CommandsNext" Version="4.1.0" /> <PackageReference Include="DSharpPlus.CommandsNext" Version="4.2.0" />
<PackageReference Include="MySql.Data" Version="8.0.28" /> <PackageReference Include="MySql.Data" Version="8.0.28" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.0.0-rc2" /> <PackageReference Include="NLog.Web.AspNetCore" Version="5.0.0-rc2" />
<PackageReference Include="Telegram.Bot.Extensions.Polling" Version="1.0.2" /> <PackageReference Include="Telegram.Bot.Extensions.Polling" Version="1.0.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -47,26 +47,39 @@ public class DbContext : IDisposable
Tran.Dispose(); Tran.Dispose();
Conn.Dispose(); Conn.Dispose();
} }
public Task<IEnumerable<Topic>> GetTopicsForUser(uint userId) public Task<IEnumerable<Topic>> GetTopicsForUser(uint userId)
=> Tran.QueryAsync<Topic>( => Tran.QueryAsync<Topic>(
" SELECT * FROM topic WHERE CreatorId = @userId", " SELECT * FROM topic WHERE CreatorId = @userId",
new { userId }); new { userId });
public Task<IEnumerable<Plan>> GetPlans()
public Task UpdatePerms(uint userId, uint planId, uint roleId)
=> Tran.ExecuteAsync(@"
UPDATE user
SET PlanId = @planId,
RoleId = @roleId
WHERE UserId = @userId",
new { userId, planId, roleId });
public Task<IEnumerable<Plan>> GetPlans()
=> Tran.QueryAsync<Plan>("SELECT * FROM plan"); => Tran.QueryAsync<Plan>("SELECT * FROM plan");
public Task<IEnumerable<Role>> GetRoles() public Task<IEnumerable<Role>> GetRoles()
=> Tran.QueryAsync<Role>("SELECT * FROM role"); => Tran.QueryAsync<Role>("SELECT * FROM role");
public Task<IEnumerable<UserInvite>> GetInvites() public Task<IEnumerable<UserInvite>> GetInvites()
=> Tran.QueryAsync<UserInvite>(@" => Tran.QueryAsync<UserInvite>(@"
SELECT ui.*, u.Login as RedeemedByLogin SELECT ui.*, u.Login as RedeemedByLogin
FROM userinvite ui FROM userinvite ui
LEFT JOIN user u ON ui.RedeemedBy = u.UserId"); LEFT JOIN user u ON ui.RedeemedBy = u.UserId");
public Task<IEnumerable<User>> GetUsers()
=> Tran.QueryAsync<User>(@"
SELECT u.*
FROM user u;");
public Task<IEnumerable<Heart>> GetHeartsForUser(uint userId) public Task<IEnumerable<Heart>> GetHeartsForUser(uint userId)
=> Tran.QueryAsync<Heart>( => Tran.QueryAsync<Heart>(
" SELECT h.* FROM heart h JOIN topic USING (TopicId) WHERE CreatorId = @userId", " SELECT h.* FROM heart h JOIN topic USING (TopicId) WHERE CreatorId = @userId",
new { userId }); new { userId });
public Task CreateUserInvite(uint planId, uint roleId, string inviteCode) public Task CreateUserInvite(uint planId, uint roleId, string inviteCode)
=> Tran.ExecuteAsync(@" => Tran.ExecuteAsync(@"
INSERT INTO userinvite INSERT INTO userinvite
( PlanId, RoleId, InviteCode) ( PlanId, RoleId, InviteCode)
@ -97,11 +110,11 @@ public class DbContext : IDisposable
" WHERE ReadToken = @token", " WHERE ReadToken = @token",
new { token, sub }); new { token, sub });
public Task<IEnumerable<Heart>> GetHeartsForTopic(uint topicId) public Task<IEnumerable<Heart>> GetHeartsForTopic(uint topicId)
=> Tran.QueryAsync<Heart>( => Tran.QueryAsync<Heart>(
" SELECT * FROM heart WHERE TopicId = @topicId", " SELECT * FROM heart WHERE TopicId = @topicId",
new { topicId }); new { topicId });
public Task<User> GetUser(string login) public Task<User> GetUser(string login)
=> Tran.QuerySingleOrDefaultAsync<User>(@" => Tran.QuerySingleOrDefaultAsync<User>(@"
SELECT u.*, up.*, ur.* SELECT u.*, up.*, ur.*
FROM user u FROM user u
@ -154,30 +167,30 @@ public class DbContext : IDisposable
( Login, Name, PasswordHash, PasswordSalt, HashType, PlanId, RoleId) ( Login, Name, PasswordHash, PasswordSalt, HashType, PlanId, RoleId)
VALUES VALUES
(@Login, @Name, @PasswordHash, @PasswordSalt, @HashType, @PlanId, @RoleId);", (@Login, @Name, @PasswordHash, @PasswordSalt, @HashType, @PlanId, @RoleId);",
param:user); param: user);
return await GetUser(user.Login); return await GetUser(user.Login);
} }
public Task RedeemInvite(uint inviteId, uint userId) public Task RedeemInvite(uint inviteId, uint userId)
=> Tran.ExecuteAsync( => Tran.ExecuteAsync(
@"UPDATE userinvite SET RedeemedBy = @userId WHERE UserInviteId = @inviteId", @"UPDATE userinvite SET RedeemedBy = @userId WHERE UserInviteId = @inviteId",
new { inviteId, userId }); new { inviteId, userId });
public Task<UserInvite> GetInviteByCode(string inviteCode) public Task<UserInvite> GetInviteByCode(string inviteCode)
=> Tran.QuerySingleOrDefaultAsync<UserInvite>( => Tran.QuerySingleOrDefaultAsync<UserInvite>(
" SELECT * FROM userinvite " + " SELECT * FROM userinvite " +
" WHERE InviteCode = @inviteCode " + " WHERE InviteCode = @inviteCode " +
" AND RedeemedBy IS NULL ", " AND RedeemedBy IS NULL ",
new { inviteCode }); new { inviteCode });
public Task<IEnumerable<NamespacedId>> GetSubsForTopic(uint topicId) public Task<IEnumerable<NamespacedId>> GetSubsForTopic(uint topicId)
=> Tran.QueryAsync<NamespacedId>( => Tran.QueryAsync<NamespacedId>(
" SELECT Sub " + " SELECT Sub " +
" FROM topic_sub " + " FROM topic_sub " +
" WHERE TopicId = @topicid", " WHERE TopicId = @topicid",
new { topicId }); new { topicId });
public Task<IEnumerable<Topic>> GetTopicsForSub(NamespacedId sub) public Task<IEnumerable<Topic>> GetTopicsForSub(NamespacedId sub)
=> Tran.QueryAsync<Topic>( => Tran.QueryAsync<Topic>(
" SELECT t.*" + " SELECT t.*" +
" FROM topic_sub ts" + " FROM topic_sub ts" +
@ -185,7 +198,7 @@ public class DbContext : IDisposable
" WHERE ts.Sub = @sub", " WHERE ts.Sub = @sub",
new { sub }); new { sub });
public Task CreateSubscription(uint topicId, NamespacedId sub) public Task CreateSubscription(uint topicId, NamespacedId sub)
=> Tran.ExecuteAsync( => Tran.ExecuteAsync(
" INSERT INTO topic_sub" + " INSERT INTO topic_sub" +
" (TopicId, Sub)" + " (TopicId, Sub)" +
@ -193,7 +206,7 @@ public class DbContext : IDisposable
" (@topicId, @sub)", " (@topicId, @sub)",
new { topicId, sub }); new { topicId, sub });
public Task<int> RemoveSubscription(string topicName, NamespacedId sub) public Task<int> RemoveSubscription(string topicName, NamespacedId sub)
=> Tran.ExecuteAsync( => Tran.ExecuteAsync(
" DELETE ts " + " DELETE ts " +
" FROM topic_sub ts" + " FROM topic_sub ts" +
@ -202,7 +215,7 @@ public class DbContext : IDisposable
new { topicName, sub }); new { topicName, sub });
public Task<int> ReportHeartbeat(uint topicId, string heart, int timeoutSeconds) public Task<int> ReportHeartbeat(uint topicId, string heart, int timeoutSeconds)
=> Tran.QueryFirstAsync<int>( => Tran.QueryFirstAsync<int>(
@"CALL report_heartbeat(@topicId, @heart, @timeoutSeconds);", @"CALL report_heartbeat(@topicId, @heart, @timeoutSeconds);",
new { topicId, heart, timeoutSeconds }); new { topicId, heart, timeoutSeconds });
@ -215,14 +228,14 @@ public class DbContext : IDisposable
#region TicketStore #region TicketStore
public Task RemoveSession(string sessionId) public Task RemoveSession(string sessionId)
=> Tran.ExecuteAsync("DELETE FROM usersession WHERE SessionId = @sessionId", new { sessionId }); => Tran.ExecuteAsync("DELETE FROM usersession WHERE SessionId = @sessionId", new { sessionId });
public Task<UserSession> GetSession(string sessionId) public Task<UserSession> GetSession(string sessionId)
=> Tran.QuerySingleOrDefaultAsync<UserSession>( => Tran.QuerySingleOrDefaultAsync<UserSession>(
"SELECT * FROM usersession WHERE SessionId = @sessionId", "SELECT * FROM usersession WHERE SessionId = @sessionId",
new { sessionId }); new { sessionId });
public Task UpdateSession(string sessionId, byte[] data, DateTime expiryTs) public Task UpdateSession(string sessionId, byte[] data, DateTime expiryTs)
=> Tran.ExecuteAsync(@" => Tran.ExecuteAsync(@"
UPDATE usersession SET UPDATE usersession SET
SessionData = @data, SessionData = @data,

View File

@ -0,0 +1,50 @@
@model AdminUsersModel
<h3>Invites</h3>
<ul class="issues-list">
@foreach (var user in Model.Users)
{
<li>
@user.Name <span class="username">@@@user.Login</span> (
@if (Context.UserCan("admin.users.setperms"))
{
<form asp-controller="AdminUsers" asp-action="SetPerms" asp-route-userid="@user.UserId" method="POST"
enctype="application/x-www-form-urlencoded" style="display:inline">
r:<select name="roleId" required class="blueunderline">
@foreach (var role in Model.Roles.Values)
{
if (role.RoleId == user.RoleId)
{
<option value="@role.RoleId" selected>@role.Name</option>
}
else
{
<option value="@role.RoleId">@role.Name</option>
}
}
</select>,
p:<select name="planId" required class="blueunderline">
@foreach (var plan in Model.Plans.Values)
{
if (plan.PlanId == user.PlanId)
{
<option value="@plan.PlanId" selected>@plan.Name</option>
}
else
{
<option value="@plan.PlanId">@plan.Name</option>
}
}
</select>
<input type="submit" value="Set permissions">
</form>
}
else
{
@: r:@Model.Roles[user.RoleId].Name,
@: p:@Model.Plans[user.PlanId].Name
}
)
</li>
}
</ul>

View File

@ -395,4 +395,8 @@ td.numeric {
a.show-button { a.show-button {
display: inline; display: inline;
}
.username {
color: gray;
} }