Merge pull request #85 from Moonlight-Panel/RemoveOldSupportSystem

Removed old support system
This commit is contained in:
Marcel Baumgartner 2023-04-21 16:39:03 +02:00 committed by GitHub
commit b790c31606
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 1099 additions and 528 deletions

View file

@ -30,7 +30,6 @@ public class DataContext : DbContext
public DbSet<AuditLogEntry> AuditLog { get; set; }
public DbSet<ErrorLogEntry> ErrorLog { get; set; }
public DbSet<SecurityLogEntry> SecurityLog { get; set; }
public DbSet<SupportMessage> SupportMessages { get; set; }
public DbSet<SharedDomain> SharedDomains { get; set; }
public DbSet<Domain> Domains { get; set; }

View file

@ -1,17 +0,0 @@
using Moonlight.App.Models.Misc;
namespace Moonlight.App.Database.Entities;
public class SupportMessage
{
public int Id { get; set; }
public string Message { get; set; } = "";
public User? Sender { get; set; } = null;
public User? Recipient { get; set; } = null;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public bool IsQuestion { get; set; } = false;
public QuestionType Type { get; set; }
public string Answer { get; set; } = "";
public bool IsSystem { get; set; } = false;
public bool IsSupport { get; set; } = false;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,67 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class RemovedOldSupportChatModel : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "SupportMessages");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "SupportMessages",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
RecipientId = table.Column<int>(type: "int", nullable: true),
SenderId = table.Column<int>(type: "int", nullable: true),
Answer = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
IsQuestion = table.Column<bool>(type: "tinyint(1)", nullable: false),
IsSupport = table.Column<bool>(type: "tinyint(1)", nullable: false),
IsSystem = table.Column<bool>(type: "tinyint(1)", nullable: false),
Message = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Type = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_SupportMessages", x => x.Id);
table.ForeignKey(
name: "FK_SupportMessages_Users_RecipientId",
column: x => x.RecipientId,
principalTable: "Users",
principalColumn: "Id");
table.ForeignKey(
name: "FK_SupportMessages_Users_SenderId",
column: x => x.SenderId,
principalTable: "Users",
principalColumn: "Id");
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_SupportMessages_RecipientId",
table: "SupportMessages",
column: "RecipientId");
migrationBuilder.CreateIndex(
name: "IX_SupportMessages_SenderId",
table: "SupportMessages",
column: "SenderId");
}
}
}

View file

@ -695,50 +695,6 @@ namespace Moonlight.App.Database.Migrations
b.ToTable("SupportChatMessages");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Answer")
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<bool>("IsQuestion")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsSupport")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsSystem")
.HasColumnType("tinyint(1)");
b.Property<string>("Message")
.IsRequired()
.HasColumnType("longtext");
b.Property<int?>("RecipientId")
.HasColumnType("int");
b.Property<int?>("SenderId")
.HasColumnType("int");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("RecipientId");
b.HasIndex("SenderId");
b.ToTable("SupportMessages");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.Property<int>("Id")
@ -1010,21 +966,6 @@ namespace Moonlight.App.Database.Migrations
b.Navigation("Sender");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Recipient")
.WithMany()
.HasForeignKey("RecipientId");
b.HasOne("Moonlight.App.Database.Entities.User", "Sender")
.WithMany()
.HasForeignKey("SenderId");
b.Navigation("Recipient");
b.Navigation("Sender");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Subscription", "CurrentSubscription")

View file

@ -1,44 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Moonlight.App.Database;
using Moonlight.App.Database.Entities;
namespace Moonlight.App.Repositories;
public class SupportMessageRepository : IDisposable
{
private readonly DataContext DataContext;
public SupportMessageRepository(DataContext dataContext)
{
DataContext = dataContext;
}
public DbSet<SupportMessage> Get()
{
return DataContext.SupportMessages;
}
public SupportMessage Add(SupportMessage message)
{
var x = DataContext.SupportMessages.Add(message);
DataContext.SaveChanges();
return x.Entity;
}
public void Update(SupportMessage message)
{
DataContext.SupportMessages.Update(message);
DataContext.SaveChanges();
}
public void Delete(SupportMessage message)
{
DataContext.SupportMessages.Remove(message);
DataContext.SaveChanges();
}
public void Dispose()
{
DataContext.Dispose();
}
}

View file

@ -1,132 +0,0 @@
using Moonlight.App.Database.Entities;
using Moonlight.App.Services.Sessions;
namespace Moonlight.App.Services.Support;
public class SupportAdminService
{
private readonly SupportServerService SupportServerService;
private readonly IdentityService IdentityService;
private readonly MessageService MessageService;
public EventHandler<SupportMessage> OnNewMessage;
public EventHandler OnUpdateTyping;
private List<string> TypingUsers = new();
private User Self;
private User Recipient;
public SupportAdminService(
SupportServerService supportServerService,
IdentityService identityService,
MessageService messageService)
{
SupportServerService = supportServerService;
IdentityService = identityService;
MessageService = messageService;
}
public async Task Start(User user)
{
Self = (await IdentityService.Get())!;
Recipient = user;
MessageService.Subscribe<SupportClientService, SupportMessage>(
$"support.{Recipient.Id}.message",
this,
message =>
{
OnNewMessage?.Invoke(this, message);
return Task.CompletedTask;
});
MessageService.Subscribe<SupportClientService, User>(
$"support.{Self.Id}.typing",
this,
user =>
{
HandleTyping(user);
return Task.CompletedTask;
});
}
#region Typing
private void HandleTyping(User user)
{
var name = $"{user.FirstName} {user.LastName}";
lock (TypingUsers)
{
if (!TypingUsers.Contains(name))
{
TypingUsers.Add(name);
OnUpdateTyping!.Invoke(this, null!);
Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(5));
if (TypingUsers.Contains(name))
{
TypingUsers.Remove(name);
OnUpdateTyping!.Invoke(this, null!);
}
});
}
}
}
public string[] GetTypingUsers()
{
lock (TypingUsers)
{
return TypingUsers.ToArray();
}
}
public Task TriggerTyping()
{
Task.Run(async () =>
{
await MessageService.Emit($"support.{Recipient.Id}.admintyping", Self);
});
return Task.CompletedTask;
}
#endregion
public async Task<SupportMessage[]> GetMessages()
{
return await SupportServerService.GetMessages(Recipient);
}
public async Task SendMessage(string content)
{
var message = new SupportMessage()
{
Message = content
};
await SupportServerService.SendMessage(
Recipient,
message,
Self,
true
);
}
public async Task Close()
{
await SupportServerService.Close(Recipient);
}
public void Dispose()
{
MessageService.Unsubscribe($"support.{Recipient.Id}.message", this);
MessageService.Unsubscribe($"support.{Recipient.Id}.typing", this);
}
}

View file

@ -1,124 +0,0 @@
using Moonlight.App.Database.Entities;
using Moonlight.App.Services.Sessions;
namespace Moonlight.App.Services.Support;
public class SupportClientService : IDisposable
{
private readonly SupportServerService SupportServerService;
private readonly IdentityService IdentityService;
private readonly MessageService MessageService;
public EventHandler<SupportMessage> OnNewMessage;
public EventHandler OnUpdateTyping;
private List<string> TypingUsers = new();
private User Self;
public SupportClientService(
SupportServerService supportServerService,
IdentityService identityService,
MessageService messageService)
{
SupportServerService = supportServerService;
IdentityService = identityService;
MessageService = messageService;
}
public async Task Start()
{
Self = (await IdentityService.Get())!;
MessageService.Subscribe<SupportClientService, SupportMessage>(
$"support.{Self.Id}.message",
this,
message =>
{
OnNewMessage?.Invoke(this, message);
return Task.CompletedTask;
});
MessageService.Subscribe<SupportClientService, User>(
$"support.{Self.Id}.admintyping",
this,
user =>
{
HandleTyping(user);
return Task.CompletedTask;
});
}
#region Typing
private void HandleTyping(User user)
{
var name = $"{user.FirstName} {user.LastName}";
lock (TypingUsers)
{
if (!TypingUsers.Contains(name))
{
TypingUsers.Add(name);
OnUpdateTyping!.Invoke(this, null!);
Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(5));
if (TypingUsers.Contains(name))
{
TypingUsers.Remove(name);
OnUpdateTyping!.Invoke(this, null!);
}
});
}
}
}
public string[] GetTypingUsers()
{
lock (TypingUsers)
{
return TypingUsers.ToArray();
}
}
public Task TriggerTyping()
{
Task.Run(async () =>
{
await MessageService.Emit($"support.{Self.Id}.typing", Self);
});
return Task.CompletedTask;
}
#endregion
public async Task<SupportMessage[]> GetMessages()
{
return await SupportServerService.GetMessages(Self);
}
public async Task SendMessage(string content)
{
var message = new SupportMessage()
{
Message = content
};
await SupportServerService.SendMessage(
Self,
message,
Self
);
}
public void Dispose()
{
MessageService.Unsubscribe($"support.{Self.Id}.message", this);
MessageService.Unsubscribe($"support.{Self.Id}.admintyping", this);
}
}

View file

@ -1,138 +0,0 @@
using Logging.Net;
using Microsoft.EntityFrameworkCore;
using Moonlight.App.Database.Entities;
using Moonlight.App.Repositories;
namespace Moonlight.App.Services.Support;
public class SupportServerService : IDisposable
{
private SupportMessageRepository SupportMessageRepository;
private MessageService MessageService;
private UserRepository UserRepository;
private readonly IServiceScopeFactory ServiceScopeFactory;
private IServiceScope ServiceScope;
public SupportServerService(IServiceScopeFactory serviceScopeFactory)
{
ServiceScopeFactory = serviceScopeFactory;
Task.Run(Run);
}
public async Task SendMessage(User r, SupportMessage message, User s, bool isSupport = false)
{
var recipient = UserRepository.Get().First(x => x.Id == r.Id);
var sender = UserRepository.Get().First(x => x.Id == s.Id);
Task.Run(async () =>
{
try
{
message.CreatedAt = DateTime.UtcNow;
message.Sender = sender;
message.Recipient = recipient;
message.IsSupport = isSupport;
SupportMessageRepository.Add(message);
await MessageService.Emit($"support.{recipient.Id}.message", message);
if (!recipient.SupportPending)
{
recipient.SupportPending = true;
UserRepository.Update(recipient);
if (!message.IsSupport)
{
var systemMessage = new SupportMessage()
{
Recipient = recipient,
Sender = null,
IsSystem = true,
Message = "The support team has been notified. Please be patient"
};
SupportMessageRepository.Add(systemMessage);
await MessageService.Emit($"support.{recipient.Id}.message", systemMessage);
}
await MessageService.Emit($"support.new", recipient);
Logger.Info("Support ticket created: " + recipient.Id);
//TODO: Ping or so
}
}
catch (Exception e)
{
Logger.Error("Error sending message");
Logger.Error(e);
}
});
}
public async Task Close(User user)
{
var recipient = UserRepository.Get().First(x => x.Id == user.Id);
recipient.SupportPending = false;
UserRepository.Update(recipient);
var systemMessage = new SupportMessage()
{
Recipient = recipient,
Sender = null,
IsSystem = true,
Message = "The ticket is now closed. Type a message to open it again"
};
SupportMessageRepository.Add(systemMessage);
await MessageService.Emit($"support.{recipient.Id}.message", systemMessage);
await MessageService.Emit($"support.close", recipient);
}
public Task<SupportMessage[]> GetMessages(User r)
{
var recipient = UserRepository.Get().First(x => x.Id == r.Id);
var messages = SupportMessageRepository
.Get()
.Include(x => x.Recipient)
.Include(x => x.Sender)
.Where(x => x.Recipient.Id == recipient.Id)
.AsEnumerable()
.TakeLast(50)
.OrderBy(x => x.Id)
.ToArray();
return Task.FromResult(messages);
}
private Task Run()
{
ServiceScope = ServiceScopeFactory.CreateScope();
SupportMessageRepository = ServiceScope
.ServiceProvider
.GetRequiredService<SupportMessageRepository>();
MessageService = ServiceScope
.ServiceProvider
.GetRequiredService<MessageService>();
UserRepository = ServiceScope
.ServiceProvider
.GetRequiredService<UserRepository>();
return Task.CompletedTask;
}
public void Dispose()
{
SupportMessageRepository.Dispose();
UserRepository.Dispose();
ServiceScope.Dispose();
}
}

View file

@ -19,7 +19,6 @@ using Moonlight.App.Services.Notifications;
using Moonlight.App.Services.OAuth2;
using Moonlight.App.Services.Sessions;
using Moonlight.App.Services.Statistics;
using Moonlight.App.Services.Support;
using Moonlight.App.Services.SupportChat;
namespace Moonlight
@ -63,7 +62,6 @@ namespace Moonlight
builder.Services.AddScoped<ServerRepository>();
builder.Services.AddScoped<ServerBackupRepository>();
builder.Services.AddScoped<ImageRepository>();
builder.Services.AddScoped<SupportMessageRepository>();
builder.Services.AddScoped<DomainRepository>();
builder.Services.AddScoped<SharedDomainRepository>();
builder.Services.AddScoped<RevokeRepository>();
@ -125,11 +123,6 @@ namespace Moonlight
builder.Services.AddScoped<MailService>();
builder.Services.AddSingleton<TrashMailDetectorService>();
// Support TODO: Remove
builder.Services.AddSingleton<SupportServerService>();
builder.Services.AddScoped<SupportAdminService>();
builder.Services.AddScoped<SupportClientService>();
// Support chat
builder.Services.AddSingleton<SupportChatServerService>();
builder.Services.AddScoped<SupportChatClientService>();
@ -176,9 +169,6 @@ namespace Moonlight
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
// Support service
var supportServerService = app.Services.GetRequiredService<SupportServerService>();
// AutoStart services
_ = app.Services.GetRequiredService<CleanupService>();
_ = app.Services.GetRequiredService<DiscordBotService>();

View file

@ -164,6 +164,7 @@
NavigationManager.LocationChanged += (sender, args) => { SessionService.Refresh(); };
/*
MessageService.Subscribe<MainLayout, SupportMessage>(
$"support.{User.Id}.message",
this,
@ -173,7 +174,7 @@
{
await ToastService.Info($"Support: {message.Message}");
}
});
});*/
RunDelayedMenu(0);
RunDelayedMenu(1);