Improved ticket system ui and some backend code

This commit is contained in:
Marcel Baumgartner
2023-08-09 00:35:49 +02:00
parent 388deacf60
commit c2c533675b
14 changed files with 1351 additions and 885 deletions

View File

@@ -45,9 +45,18 @@ public class DatabaseCheckupService
{
Logger.Info($"{migrations.Length} migrations pending. Updating now");
var backupHelper = new BackupHelper();
await backupHelper.CreateBackup(
PathBuilder.File("storage", "backups", $"{new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds()}.zip"));
try
{
var backupHelper = new BackupHelper();
await backupHelper.CreateBackup(
PathBuilder.File("storage", "backups", $"{new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds()}.zip"));
}
catch (Exception e)
{
Logger.Fatal("Unable to create backup");
Logger.Fatal(e);
Logger.Fatal("Moonlight will continue to start and apply the migrations without a backup");
}
Logger.Info("Applying migrations");

View File

@@ -1,4 +1,5 @@
using System.Text;
using Microsoft.AspNetCore.Components;
using Moonlight.App.Services;
namespace Moonlight.App.Helpers;
@@ -163,4 +164,22 @@ public static class Formatter
double gigabytes = (double)bytes / gbMultiplier;
return gigabytes;
}
public static RenderFragment FormatLineBreaks(string content)
{
return builder =>
{
int i = 0;
var arr = content.Split("\n", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
foreach (var line in arr)
{
builder.AddContent(i, line);
if (i++ != arr.Length - 1)
{
builder.AddMarkupContent(i, "<br/>");
}
}
};
}
}

View File

@@ -31,9 +31,8 @@ public class DiscordNotificationService
Client = new(config.WebHook);
AppUrl = configService.Get().Moonlight.AppUrl;
Event.On<User>("supportChat.new", this, OnNewSupportChat);
Event.On<SupportChatMessage>("supportChat.message", this, OnSupportChatMessage);
Event.On<User>("supportChat.close", this, OnSupportChatClose);
Event.On<Ticket>("tickets.new", this, OnNewTicket);
Event.On<Ticket>("tickets.status", this, OnTicketStatusUpdated);
Event.On<User>("user.rating", this, OnUserRated);
Event.On<User>("billing.completed", this, OnBillingCompleted);
Event.On<BlocklistIp>("ddos.add", this, OnIpBlockListed);
@@ -44,6 +43,24 @@ public class DiscordNotificationService
}
}
private async Task OnTicketStatusUpdated(Ticket ticket)
{
await SendNotification("", builder =>
{
builder.Title = "Ticket status has been updated";
builder.AddField("Issue topic", ticket.IssueTopic);
builder.AddField("Status", ticket.Status);
if (ticket.AssignedTo != null)
{
builder.AddField("Assigned to", $"{ticket.AssignedTo.FirstName} {ticket.AssignedTo.LastName}");
}
builder.Color = Color.Green;
builder.Url = $"{AppUrl}/admin/support/view/{ticket.Id}";
});
}
private async Task OnIpBlockListed(BlocklistIp blocklistIp)
{
await SendNotification("", builder =>
@@ -85,46 +102,14 @@ public class DiscordNotificationService
});
}
private async Task OnSupportChatClose(User user)
private async Task OnNewTicket(Ticket ticket)
{
await SendNotification("", builder =>
{
builder.Title = "A new support chat has been marked as closed";
builder.Color = Color.Red;
builder.AddField("Email", user.Email);
builder.AddField("Firstname", user.FirstName);
builder.AddField("Lastname", user.LastName);
builder.Url = $"{AppUrl}/admin/support/view/{user.Id}";
});
}
private async Task OnSupportChatMessage(SupportChatMessage message)
{
if(message.Sender == null)
return;
await SendNotification("", builder =>
{
builder.Title = "New message in support chat";
builder.Color = Color.Blue;
builder.AddField("Message", message.Content);
builder.Author = new EmbedAuthorBuilder()
.WithName($"{message.Sender.FirstName} {message.Sender.LastName}")
.WithIconUrl(ResourceService.Avatar(message.Sender));
builder.Url = $"{AppUrl}/admin/support/view/{message.Recipient.Id}";
});
}
private async Task OnNewSupportChat(User user)
{
await SendNotification("", builder =>
{
builder.Title = "A new support chat has been marked as active";
builder.Title = "A new ticket has been created";
builder.AddField("Issue topic", ticket.IssueTopic);
builder.Color = Color.Green;
builder.AddField("Email", user.Email);
builder.AddField("Firstname", user.FirstName);
builder.AddField("Lastname", user.LastName);
builder.Url = $"{AppUrl}/admin/support/view/{user.Id}";
builder.Url = $"{AppUrl}/admin/support/view/{ticket.Id}";
});
}

View File

@@ -24,29 +24,6 @@ public class TicketAdminService
BucketService = bucketService;
}
public async Task<Dictionary<Ticket, TicketMessage?>> GetAssigned()
{
return await TicketServerService.GetUserAssignedTickets(IdentityService.User);
}
public async Task<Dictionary<Ticket, TicketMessage?>> GetUnAssigned()
{
return await TicketServerService.GetUnAssignedTickets();
}
public async Task<Ticket> Create(string issueTopic, string issueDescription, string issueTries,
TicketSubject subject, int subjectId)
{
return await TicketServerService.Create(
IdentityService.User,
issueTopic,
issueDescription,
issueTries,
subject,
subjectId
);
}
public async Task<TicketMessage> Send(string content, IBrowserFile? file = null)
{
string? attachment = null;
@@ -83,13 +60,8 @@ public class TicketAdminService
return await TicketServerService.GetMessages(Ticket!);
}
public async Task Claim()
public async Task SetClaim(User? user)
{
await TicketServerService.Claim(Ticket!, IdentityService.User);
}
public async Task UnClaim()
{
await TicketServerService.Claim(Ticket!);
await TicketServerService.SetClaim(Ticket!, user);
}
}

View File

@@ -23,11 +23,6 @@ public class TicketClientService
IdentityService = identityService;
BucketService = bucketService;
}
public async Task<Dictionary<Ticket, TicketMessage?>> Get()
{
return await TicketServerService.GetUserTickets(IdentityService.User);
}
public async Task<Ticket> Create(string issueTopic, string issueDescription, string issueTries, TicketSubject subject, int subjectId)
{

View File

@@ -49,6 +49,12 @@ public class TicketServerService
// Do automatic stuff here
await SendSystemMessage(ticket, ConfigService.Get().Moonlight.Tickets.WelcomeMessage);
if (ticket.Subject != TicketSubject.Other)
{
await SendMessage(ticket, creatorUser, $"Subject :\n\n{ticket.Subject}: {ticket.SubjectId}");
}
//TODO: Check for opening times
return ticket;
@@ -137,85 +143,7 @@ public class TicketServerService
return message;
}
public Task<Dictionary<Ticket, TicketMessage?>> GetUserTickets(User u)
{
using var scope = ServiceScopeFactory.CreateScope();
var ticketRepo = scope.ServiceProvider.GetRequiredService<Repository<Ticket>>();
var tickets = ticketRepo
.Get()
.Include(x => x.CreatedBy)
.Include(x => x.Messages)
.Where(x => x.CreatedBy.Id == u.Id)
.Where(x => x.Status != TicketStatus.Closed)
.ToArray();
var result = new Dictionary<Ticket, TicketMessage?>();
foreach (var ticket in tickets)
{
var message = ticket.Messages
.OrderByDescending(x => x.Id)
.FirstOrDefault();
result.Add(ticket, message);
}
return Task.FromResult(result);
}
public Task<Dictionary<Ticket, TicketMessage?>> GetUserAssignedTickets(User u)
{
using var scope = ServiceScopeFactory.CreateScope();
var ticketRepo = scope.ServiceProvider.GetRequiredService<Repository<Ticket>>();
var tickets = ticketRepo
.Get()
.Include(x => x.CreatedBy)
.Include(x => x.Messages)
.Where(x => x.Status != TicketStatus.Closed)
.Where(x => x.AssignedTo.Id == u.Id)
.ToArray();
var result = new Dictionary<Ticket, TicketMessage?>();
foreach (var ticket in tickets)
{
var message = ticket.Messages
.OrderByDescending(x => x.Id)
.FirstOrDefault();
result.Add(ticket, message);
}
return Task.FromResult(result);
}
public Task<Dictionary<Ticket, TicketMessage?>> GetUnAssignedTickets()
{
using var scope = ServiceScopeFactory.CreateScope();
var ticketRepo = scope.ServiceProvider.GetRequiredService<Repository<Ticket>>();
var tickets = ticketRepo
.Get()
.Include(x => x.CreatedBy)
.Include(x => x.Messages)
.Include(x => x.AssignedTo)
.Where(x => x.AssignedTo == null)
.Where(x => x.Status != TicketStatus.Closed)
.ToArray();
var result = new Dictionary<Ticket, TicketMessage?>();
foreach (var ticket in tickets)
{
var message = ticket.Messages
.OrderByDescending(x => x.Id)
.FirstOrDefault();
result.Add(ticket, message);
}
return Task.FromResult(result);
}
public Task<TicketMessage[]> GetMessages(Ticket ticket)
{
using var scope = ServiceScopeFactory.CreateScope();
@@ -230,7 +158,7 @@ public class TicketServerService
return Task.FromResult(tickets.Messages.ToArray());
}
public async Task Claim(Ticket t, User? u = null)
public async Task SetClaim(Ticket t, User? u = null)
{
using var scope = ServiceScopeFactory.CreateScope();
var ticketRepo = scope.ServiceProvider.GetRequiredService<Repository<Ticket>>();
@@ -245,5 +173,8 @@ public class TicketServerService
await Event.Emit("tickets.status", ticket);
await Event.Emit($"tickets.{ticket.Id}.status", ticket);
var claimName = user == null ? "None" : user.FirstName + " " + user.LastName;
await SendSystemMessage(ticket, $"Ticked claim has been set to {claimName}");
}
}