Improved ticket system ui and some backend code
This commit is contained in:
@@ -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");
|
||||
|
||||
|
||||
@@ -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/>");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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}";
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user