Merge pull request #208 from Moonlight-Panel/RewriteSessionSystem
Rewritten session system to match new standarts and be more performant
This commit is contained in:
commit
92705837ba
|
@ -1,16 +0,0 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Services.Interop;
|
||||
|
||||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public class Session
|
||||
{
|
||||
public string Ip { get; set; } = "N/A";
|
||||
public string Url { get; set; } = "N/A";
|
||||
public string Device { get; set; } = "N/A";
|
||||
public User? User { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public NavigationManager Navigation { get; set; }
|
||||
public AlertService AlertService { get; set; }
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
using Moonlight.App.Models.Misc;
|
||||
|
||||
namespace Moonlight.App.Repositories;
|
||||
|
||||
public class SessionRepository
|
||||
{
|
||||
private readonly List<Session> Sessions;
|
||||
|
||||
public SessionRepository()
|
||||
{
|
||||
Sessions = new();
|
||||
}
|
||||
|
||||
public Session[] Get()
|
||||
{
|
||||
lock (Sessions)
|
||||
{
|
||||
return Sessions.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(Session session)
|
||||
{
|
||||
lock (Sessions)
|
||||
{
|
||||
Sessions.Add(session);
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete(Session session)
|
||||
{
|
||||
lock (Sessions)
|
||||
{
|
||||
Sessions.Remove(session);
|
||||
}
|
||||
}
|
||||
}
|
56
Moonlight/App/Services/Sessions/SessionClientService.cs
Normal file
56
Moonlight/App/Services/Sessions/SessionClientService.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Repositories;
|
||||
using Moonlight.App.Services.Interop;
|
||||
|
||||
namespace Moonlight.App.Services.Sessions;
|
||||
|
||||
public class SessionClientService
|
||||
{
|
||||
public readonly Guid Uuid = Guid.NewGuid();
|
||||
public readonly DateTime CreateTimestamp = DateTime.UtcNow;
|
||||
public User? User { get; private set; }
|
||||
|
||||
public readonly IdentityService IdentityService;
|
||||
public readonly AlertService AlertService;
|
||||
public readonly NavigationManager NavigationManager;
|
||||
public readonly IJSRuntime JsRuntime;
|
||||
|
||||
private readonly SessionServerService SessionServerService;
|
||||
private readonly Repository<User> UserRepository;
|
||||
|
||||
public SessionClientService(
|
||||
IdentityService identityService,
|
||||
AlertService alertService,
|
||||
NavigationManager navigationManager,
|
||||
IJSRuntime jsRuntime,
|
||||
SessionServerService sessionServerService,
|
||||
Repository<User> userRepository)
|
||||
{
|
||||
IdentityService = identityService;
|
||||
AlertService = alertService;
|
||||
NavigationManager = navigationManager;
|
||||
JsRuntime = jsRuntime;
|
||||
SessionServerService = sessionServerService;
|
||||
UserRepository = userRepository;
|
||||
}
|
||||
|
||||
public async Task Start()
|
||||
{
|
||||
User = await IdentityService.Get();
|
||||
|
||||
if (User != null) // Track users last visit
|
||||
{
|
||||
User.LastVisitedAt = DateTime.UtcNow;
|
||||
UserRepository.Update(User);
|
||||
}
|
||||
|
||||
await SessionServerService.Register(this);
|
||||
}
|
||||
|
||||
public async Task Stop()
|
||||
{
|
||||
await SessionServerService.UnRegister(this);
|
||||
}
|
||||
}
|
64
Moonlight/App/Services/Sessions/SessionServerService.cs
Normal file
64
Moonlight/App/Services/Sessions/SessionServerService.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Events;
|
||||
|
||||
namespace Moonlight.App.Services.Sessions;
|
||||
|
||||
public class SessionServerService
|
||||
{
|
||||
private readonly List<SessionClientService> Sessions = new();
|
||||
private readonly EventSystem Event;
|
||||
|
||||
public SessionServerService(EventSystem eventSystem)
|
||||
{
|
||||
Event = eventSystem;
|
||||
}
|
||||
|
||||
public async Task Register(SessionClientService sessionClientService)
|
||||
{
|
||||
lock (Sessions)
|
||||
{
|
||||
if(!Sessions.Contains(sessionClientService))
|
||||
Sessions.Add(sessionClientService);
|
||||
}
|
||||
|
||||
await Event.Emit("sessions.add", sessionClientService);
|
||||
}
|
||||
public async Task UnRegister(SessionClientService sessionClientService)
|
||||
{
|
||||
lock (Sessions)
|
||||
{
|
||||
if(Sessions.Contains(sessionClientService))
|
||||
Sessions.Remove(sessionClientService);
|
||||
}
|
||||
|
||||
await Event.Emit("sessions.remove", sessionClientService);
|
||||
}
|
||||
|
||||
public Task<SessionClientService[]> GetSessions()
|
||||
{
|
||||
lock (Sessions)
|
||||
{
|
||||
return Task.FromResult(Sessions.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ReloadUserSessions(User user)
|
||||
{
|
||||
var sessions = await GetSessions();
|
||||
|
||||
foreach (var session in sessions)
|
||||
{
|
||||
if (session.User != null && session.User.Id == user.Id)
|
||||
{
|
||||
try
|
||||
{
|
||||
session.NavigationManager.NavigateTo(session.NavigationManager.Uri, true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Models.Misc;
|
||||
using Moonlight.App.Repositories;
|
||||
using Moonlight.App.Services.Interop;
|
||||
|
||||
namespace Moonlight.App.Services.Sessions;
|
||||
|
||||
public class SessionService
|
||||
{
|
||||
private readonly SessionRepository SessionRepository;
|
||||
private Repository<User> UserRepository;
|
||||
private readonly IdentityService IdentityService;
|
||||
private readonly NavigationManager NavigationManager;
|
||||
private readonly AlertService AlertService;
|
||||
private readonly DateTimeService DateTimeService;
|
||||
|
||||
private Session? OwnSession;
|
||||
|
||||
public SessionService(
|
||||
SessionRepository sessionRepository,
|
||||
IdentityService identityService,
|
||||
NavigationManager navigationManager,
|
||||
AlertService alertService,
|
||||
DateTimeService dateTimeService,
|
||||
Repository<User> userRepository)
|
||||
{
|
||||
SessionRepository = sessionRepository;
|
||||
IdentityService = identityService;
|
||||
NavigationManager = navigationManager;
|
||||
AlertService = alertService;
|
||||
DateTimeService = dateTimeService;
|
||||
UserRepository = userRepository;
|
||||
}
|
||||
|
||||
public async Task Register()
|
||||
{
|
||||
var user = await IdentityService.Get();
|
||||
|
||||
OwnSession = new Session()
|
||||
{
|
||||
Ip = IdentityService.GetIp(),
|
||||
Url = NavigationManager.Uri,
|
||||
Device = IdentityService.GetDevice(),
|
||||
CreatedAt = DateTimeService.GetCurrent(),
|
||||
User = user,
|
||||
Navigation = NavigationManager,
|
||||
AlertService = AlertService
|
||||
};
|
||||
|
||||
SessionRepository.Add(OwnSession);
|
||||
|
||||
if (user != null) // Track last session init of user as last visited timestamp
|
||||
{
|
||||
user.LastVisitedAt = DateTimeService.GetCurrent();
|
||||
UserRepository.Update(user);
|
||||
}
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
OwnSession.Url = NavigationManager.Uri;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
SessionRepository.Delete(OwnSession);
|
||||
}
|
||||
|
||||
public Session[] GetAll()
|
||||
{
|
||||
return SessionRepository.Get();
|
||||
}
|
||||
|
||||
public void ReloadUserSessions(User user)
|
||||
{
|
||||
foreach (var session in SessionRepository.Get())
|
||||
{
|
||||
if(session.User != null && session.User.Id == user.Id)
|
||||
session.Navigation.NavigateTo(session.Navigation.Uri, true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -44,7 +44,7 @@ public class StatisticsCaptureService
|
|||
var domainsRepo = scope.ServiceProvider.GetRequiredService<Repository<Domain>>();
|
||||
var webspacesRepo = scope.ServiceProvider.GetRequiredService<Repository<WebSpace>>();
|
||||
var databasesRepo = scope.ServiceProvider.GetRequiredService<Repository<MySqlDatabase>>();
|
||||
var sessionService = scope.ServiceProvider.GetRequiredService<SessionService>();
|
||||
var sessionService = scope.ServiceProvider.GetRequiredService<SessionServerService>();
|
||||
|
||||
void AddEntry(string chart, int value)
|
||||
{
|
||||
|
@ -61,7 +61,7 @@ public class StatisticsCaptureService
|
|||
AddEntry("domainsCount", domainsRepo.Get().Count());
|
||||
AddEntry("webspacesCount", webspacesRepo.Get().Count());
|
||||
AddEntry("databasesCount", databasesRepo.Get().Count());
|
||||
AddEntry("sessionsCount", sessionService.GetAll().Length);
|
||||
AddEntry("sessionsCount", (await sessionService.GetSessions()).Length);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
@ -152,7 +152,6 @@ namespace Moonlight
|
|||
builder.Services.AddDbContext<DataContext>();
|
||||
|
||||
// Repositories
|
||||
builder.Services.AddSingleton<SessionRepository>();
|
||||
builder.Services.AddScoped<UserRepository>();
|
||||
builder.Services.AddScoped<NodeRepository>();
|
||||
builder.Services.AddScoped<ServerRepository>();
|
||||
|
@ -179,7 +178,6 @@ namespace Moonlight
|
|||
builder.Services.AddScoped<CookieService>();
|
||||
builder.Services.AddScoped<IdentityService>();
|
||||
builder.Services.AddScoped<IpLocateService>();
|
||||
builder.Services.AddScoped<SessionService>();
|
||||
builder.Services.AddScoped<AlertService>();
|
||||
builder.Services.AddScoped<SmartTranslateService>();
|
||||
builder.Services.AddScoped<UserService>();
|
||||
|
@ -215,6 +213,9 @@ namespace Moonlight
|
|||
builder.Services.AddScoped<SubscriptionService>();
|
||||
builder.Services.AddScoped<SubscriptionAdminService>();
|
||||
|
||||
builder.Services.AddScoped<SessionClientService>();
|
||||
builder.Services.AddSingleton<SessionServerService>();
|
||||
|
||||
// Loggers
|
||||
builder.Services.AddScoped<MailService>();
|
||||
builder.Services.AddSingleton<TrashMailDetectorService>();
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
@inject IJSRuntime JsRuntime
|
||||
@inject IdentityService IdentityService
|
||||
@inject SessionService SessionService
|
||||
@inject SessionClientService SessionClientService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject EventSystem Event
|
||||
@inject ToastService ToastService
|
||||
|
@ -215,12 +215,10 @@
|
|||
}
|
||||
catch (Exception){ /* ignore errors to make sure that the session call is executed */ }
|
||||
|
||||
await SessionService.Register();
|
||||
await SessionClientService.Start();
|
||||
|
||||
NavigationManager.LocationChanged += async (_, _) =>
|
||||
{
|
||||
SessionService.Refresh();
|
||||
|
||||
if (!NavigationManager.Uri.Contains("/server/"))
|
||||
await DynamicBackgroundService.Reset();
|
||||
};
|
||||
|
@ -255,7 +253,7 @@
|
|||
|
||||
public async void Dispose()
|
||||
{
|
||||
SessionService.Close();
|
||||
await SessionClientService.Stop();
|
||||
|
||||
await KeyListenerService.DisposeAsync();
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
@inject IJSRuntime JsRuntime
|
||||
@inject IdentityService IdentityService
|
||||
@inject SessionService SessionService
|
||||
@inject SessionClientService SessionClientService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<GlobalErrorBoundary>
|
||||
|
@ -106,9 +106,7 @@
|
|||
await JsRuntime.InvokeVoidAsync("KTDrawer.createInstances");
|
||||
await JsRuntime.InvokeVoidAsync("createSnow");
|
||||
|
||||
await SessionService.Register();
|
||||
|
||||
NavigationManager.LocationChanged += (sender, args) => { SessionService.Refresh(); };
|
||||
await SessionClientService.Start();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
@ -117,9 +115,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public async void Dispose()
|
||||
{
|
||||
SessionService.Close();
|
||||
await SessionClientService.Stop();
|
||||
}
|
||||
|
||||
private void AddBodyAttribute(string attribute, string value)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
@inject UserRepository UserRepository
|
||||
@inject UserService UserService
|
||||
@inject SessionService SessionService
|
||||
@inject SessionServerService SessionServerService
|
||||
@inject ToastService ToastService
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
|
||||
|
@ -174,7 +174,7 @@
|
|||
user.Status = User!.Status;
|
||||
UserRepository.Update(user);
|
||||
|
||||
SessionService.ReloadUserSessions(User);
|
||||
await SessionServerService.ReloadUserSessions(User);
|
||||
|
||||
await ToastService.Success(SmartTranslateService.Translate("Successfully updated user"));
|
||||
}
|
||||
|
@ -191,7 +191,7 @@
|
|||
await UserService.ChangePassword(User!, NewPassword, true);
|
||||
NewPassword = "";
|
||||
|
||||
SessionService.ReloadUserSessions(User);
|
||||
await SessionServerService.ReloadUserSessions(User!);
|
||||
|
||||
await ToastService.Success(SmartTranslateService.Translate("Successfully updated password"));
|
||||
}
|
||||
|
|
|
@ -8,9 +8,10 @@
|
|||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Services.Interop
|
||||
|
||||
@inject SessionService SessionService
|
||||
@inject SessionServerService SessionServerService
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
@inject AlertService AlertService
|
||||
@inject ToastService ToastService
|
||||
|
||||
<OnlyAdmin>
|
||||
<AdminSessionNavigation Index="1"/>
|
||||
|
@ -44,8 +45,8 @@
|
|||
}
|
||||
else
|
||||
{
|
||||
<Table TableItem="Session" Items="AllSessions" PageSize="25" TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3" TableHeadClass="fw-bold text-muted">
|
||||
<Column TableItem="Session" Title="@(SmartTranslateService.Translate("Email"))" Field="@(x => x.User.Email)" Sortable="true" Filterable="true" Width="20%">
|
||||
<Table TableItem="SessionClientService" Items="AllSessions" PageSize="25" TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3" TableHeadClass="fw-bold text-muted">
|
||||
<Column TableItem="SessionClientService" Title="@(SmartTranslateService.Translate("Email"))" Field="@(x => x.User.Email)" Sortable="true" Filterable="true" Width="20%">
|
||||
<Template>
|
||||
@if (context.User == null)
|
||||
{
|
||||
|
@ -57,25 +58,33 @@
|
|||
}
|
||||
</Template>
|
||||
</Column>
|
||||
<Column TableItem="Session" Title="@(SmartTranslateService.Translate("IP"))" Field="@(x => x.Ip)" Sortable="true" Filterable="true" Width="10%"/>
|
||||
<Column TableItem="Session" Title="@(SmartTranslateService.Translate("URL"))" Field="@(x => x.Url)" Sortable="true" Filterable="true" Width="10%"/>
|
||||
<Column TableItem="Session" Title="@(SmartTranslateService.Translate("Device"))" Field="@(x => x.Device)" Sortable="true" Filterable="true" Width="10%"/>
|
||||
<Column TableItem="Session" Title="@(SmartTranslateService.Translate("Time"))" Field="@(x => x.CreatedAt)" Sortable="true" Filterable="true" Width="10%">
|
||||
<Column TableItem="SessionClientService" Title="@(SmartTranslateService.Translate("IP"))" Field="@(x => x.Uuid)" Sortable="false" Filterable="false" Width="10%">
|
||||
<Template>
|
||||
@(context.IdentityService.GetIp())
|
||||
</Template>
|
||||
</Column>
|
||||
<Column TableItem="SessionClientService" Title="@(SmartTranslateService.Translate("URL"))" Field="@(x => x.NavigationManager.Uri)" Sortable="true" Filterable="true" Width="10%"/>
|
||||
<Column TableItem="SessionClientService" Title="@(SmartTranslateService.Translate("Device"))" Field="@(x => x.Uuid)" Sortable="false" Filterable="false" Width="10%">
|
||||
<Template>
|
||||
@(context.IdentityService.GetDevice())
|
||||
</Template>
|
||||
</Column>
|
||||
<Column TableItem="SessionClientService" Title="@(SmartTranslateService.Translate("Time"))" Field="@(x => x.CreateTimestamp)" Sortable="true" Filterable="true" Width="10%">
|
||||
<Template>
|
||||
@{
|
||||
var time = Formatter.FormatUptime((DateTime.UtcNow - context.CreatedAt).TotalMilliseconds);
|
||||
var time = Formatter.FormatUptime((DateTime.UtcNow - context.CreateTimestamp).TotalMilliseconds);
|
||||
}
|
||||
<span>@(time)</span>
|
||||
</Template>
|
||||
</Column>
|
||||
<Column TableItem="Session" Title="@(SmartTranslateService.Translate("Actions"))" Field="@(x => x.Ip)" Sortable="false" Filterable="false" Width="10%">
|
||||
<Column TableItem="SessionClientService" Title="@(SmartTranslateService.Translate("Actions"))" Field="@(x => x.Uuid)" Sortable="false" Filterable="false" Width="10%">
|
||||
<Template>
|
||||
<button @onclick="() => Navigate(context)" class="btn btn-sm btn-primary">
|
||||
<TL>Change url</TL>
|
||||
</button>
|
||||
</Template>
|
||||
</Column>
|
||||
<Column TableItem="Session" Title="" Field="@(x => x.Ip)" Sortable="false" Filterable="false" Width="10%">
|
||||
<Column TableItem="SessionClientService" Title="" Field="@(x => x.Uuid)" Sortable="false" Filterable="false" Width="10%">
|
||||
<Template>
|
||||
<button @onclick="() => Message(context)" class="btn btn-sm btn-warning">
|
||||
<TL>Message</TL>
|
||||
|
@ -92,11 +101,11 @@
|
|||
|
||||
@code
|
||||
{
|
||||
private Session[]? AllSessions;
|
||||
private SessionClientService[]? AllSessions;
|
||||
|
||||
private Task Load(LazyLoader arg)
|
||||
private async Task Load(LazyLoader arg)
|
||||
{
|
||||
AllSessions = SessionService.GetAll();
|
||||
AllSessions = await SessionServerService.GetSessions();
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
|
@ -109,35 +118,30 @@
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn("Error autorefreshing sessions");
|
||||
Logger.Warn("Error auto refreshing sessions");
|
||||
Logger.Warn(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task Refresh()
|
||||
{
|
||||
AllSessions = SessionService.GetAll();
|
||||
AllSessions = await SessionServerService.GetSessions();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async Task Navigate(Session session)
|
||||
private async Task Navigate(SessionClientService session)
|
||||
{
|
||||
var url = await AlertService.Text("URL", SmartTranslateService.Translate("Enter url"), "");
|
||||
|
||||
if (url == null)
|
||||
return;
|
||||
|
||||
if (url == "")
|
||||
if (string.IsNullOrEmpty(url))
|
||||
return;
|
||||
|
||||
if (url == "null")
|
||||
return;
|
||||
|
||||
session.Navigation.NavigateTo(url, true);
|
||||
session.NavigationManager.NavigateTo(url, true);
|
||||
}
|
||||
|
||||
private async Task MessageAll()
|
||||
|
@ -157,22 +161,30 @@
|
|||
|
||||
if (b)
|
||||
{
|
||||
foreach (var session in SessionService.GetAll())
|
||||
foreach (var session in AllSessions!)
|
||||
{
|
||||
try
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await session.AlertService.Warning("Admin Message", message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn("Error sending user a alert");
|
||||
Logger.Warn(e);
|
||||
}
|
||||
try
|
||||
{
|
||||
await session.AlertService.Warning("Admin Message", message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn("Error sending user a alert");
|
||||
Logger.Warn(e);
|
||||
|
||||
var translation = SmartTranslateService.Translate("An unknown error occured while sending admin message to user: ");
|
||||
var identifier = session.User != null ? session.User.Email : session.Uuid.ToString();
|
||||
|
||||
await ToastService.Warning(translation + identifier);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Message(Session session)
|
||||
private async Task Message(SessionClientService session)
|
||||
{
|
||||
var message = await AlertService.Text(
|
||||
SmartTranslateService.Translate("Enter message"),
|
||||
|
@ -191,12 +203,14 @@
|
|||
{
|
||||
try
|
||||
{
|
||||
await session.AlertService.Warning("Admin Message", message);
|
||||
await session.AlertService.Warning("Admin Message", message!);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn("Error sending user a alert");
|
||||
Logger.Warn(e);
|
||||
|
||||
await ToastService.Warning(SmartTranslateService.Translate("An unknown error occured while sending admin message"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue