Initial commit
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using RobotNet10.ScriptEngine;
|
||||
using RobotNet10.ScriptEngine.HubContexts;
|
||||
using RobotNet10.ScriptEngine.Shared;
|
||||
|
||||
namespace RobotNet10.ScriptEngine.Hubs;
|
||||
|
||||
/// <summary>
|
||||
/// SignalR Hub for managing script files.
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
public class FileManagerHub(FileManager _fileManager, ConsoleHubContext _consoleHubContext) : Hub
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when a client disconnects.
|
||||
/// </summary>
|
||||
public override Task OnDisconnectedAsync(Exception? exception)
|
||||
{
|
||||
// Revoke edit permission if this connection had it
|
||||
_fileManager.RevokeEditPermission(Context.ConnectionId);
|
||||
|
||||
return base.OnDisconnectedAsync(exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root folder structure with all files and folders.
|
||||
/// </summary>
|
||||
public async Task<ScriptFolderDto> GetRootFolder()
|
||||
{
|
||||
return await _fileManager.GetRootFolderAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current state of the FileManager.
|
||||
/// </summary>
|
||||
public Task<ScriptEngineState> GetState()
|
||||
{
|
||||
return Task.FromResult(_fileManager.State);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requests edit permission. If another connection has permission, it will be revoked and notified.
|
||||
/// </summary>
|
||||
public Task<bool> RequestEditPermission()
|
||||
{
|
||||
var previousConnectionId = _fileManager.RequestEditPermission(Context.ConnectionId);
|
||||
|
||||
if (previousConnectionId != null && previousConnectionId != Context.ConnectionId)
|
||||
{
|
||||
// Notify the previous connection that their permission was revoked
|
||||
// Only notify if it's a different connection (not the same one reconnecting)
|
||||
try
|
||||
{
|
||||
_ = Clients.Client(previousConnectionId).SendAsync("EditPermissionRevoked", Context.UserIdentifier);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore if connection is already disconnected
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Revokes edit permission for the current connection.
|
||||
/// </summary>
|
||||
public Task RevokeEditPermission()
|
||||
{
|
||||
_fileManager.RevokeEditPermission(Context.ConnectionId);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the current connection has edit permission.
|
||||
/// </summary>
|
||||
public Task<bool> HasEditPermission()
|
||||
{
|
||||
return Task.FromResult(_fileManager.HasEditPermission(Context.ConnectionId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves file content.
|
||||
/// </summary>
|
||||
public async Task SaveFile(string relativePath, string content)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _fileManager.SaveFileAsync(relativePath, content, Context.ConnectionId);
|
||||
_consoleHubContext.LogInfo($"File saved successfully: {relativePath}");
|
||||
|
||||
// Notify other clients that file was saved (fire-and-forget)
|
||||
_ = Clients.Others.SendAsync("FileSaved", relativePath, Context.UserIdentifier);
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
_consoleHubContext.LogError($"Failed to save file '{relativePath}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_consoleHubContext.LogError($"Error saving file '{relativePath}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new file.
|
||||
/// </summary>
|
||||
public async Task CreateFile(string relativePath, string content)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _fileManager.CreateFileAsync(relativePath, content, Context.ConnectionId);
|
||||
_consoleHubContext.LogInfo($"File created successfully: {relativePath}");
|
||||
|
||||
// Notify other clients that file was created (fire-and-forget)
|
||||
_ = Clients.Others.SendAsync("FileCreated", relativePath, Context.UserIdentifier);
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
_consoleHubContext.LogError($"Failed to create file '{relativePath}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
_consoleHubContext.LogWarning($"Failed to create file '{relativePath}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_consoleHubContext.LogError($"Error creating file '{relativePath}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new folder.
|
||||
/// </summary>
|
||||
public Task CreateFolder(string relativePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
_fileManager.CreateFolder(relativePath, Context.ConnectionId);
|
||||
_consoleHubContext.LogInfo($"Folder created successfully: {relativePath}");
|
||||
|
||||
// Notify other clients that folder was created (fire-and-forget)
|
||||
_ = Clients.Others.SendAsync("FolderCreated", relativePath, Context.UserIdentifier);
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
_consoleHubContext.LogError($"Failed to create folder '{relativePath}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
_consoleHubContext.LogWarning($"Failed to create folder '{relativePath}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_consoleHubContext.LogError($"Error creating folder '{relativePath}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a file.
|
||||
/// </summary>
|
||||
public Task DeleteFile(string relativePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
_fileManager.DeleteFile(relativePath, Context.ConnectionId);
|
||||
_consoleHubContext.LogInfo($"File deleted successfully: {relativePath}");
|
||||
|
||||
// Notify other clients that file was deleted (fire-and-forget)
|
||||
_ = Clients.Others.SendAsync("FileDeleted", relativePath, Context.UserIdentifier);
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
_consoleHubContext.LogError($"Failed to delete file '{relativePath}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
_consoleHubContext.LogWarning($"Failed to delete file '{relativePath}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_consoleHubContext.LogError($"Error deleting file '{relativePath}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a folder.
|
||||
/// </summary>
|
||||
public Task DeleteFolder(string relativePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
_fileManager.DeleteFolder(relativePath, Context.ConnectionId);
|
||||
_consoleHubContext.LogInfo($"Folder deleted successfully: {relativePath}");
|
||||
|
||||
// Notify other clients that folder was deleted (fire-and-forget)
|
||||
_ = Clients.Others.SendAsync("FolderDeleted", relativePath, Context.UserIdentifier);
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
_consoleHubContext.LogError($"Failed to delete folder '{relativePath}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
catch (DirectoryNotFoundException ex)
|
||||
{
|
||||
_consoleHubContext.LogWarning($"Failed to delete folder '{relativePath}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_consoleHubContext.LogError($"Error deleting folder '{relativePath}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a backup of all scripts.
|
||||
/// </summary>
|
||||
public async Task<string> CreateBackup(string? backupName = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var backupFileName = await _fileManager.CreateBackupAsync(backupName);
|
||||
_consoleHubContext.LogInfo($"Backup created successfully: {backupFileName}");
|
||||
|
||||
// Notify all clients that backup was created (fire-and-forget)
|
||||
_ = Clients.All.SendAsync("BackupCreated", backupFileName, Context.UserIdentifier);
|
||||
|
||||
return backupFileName;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_consoleHubContext.LogError($"Error creating backup: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists available backup files.
|
||||
/// </summary>
|
||||
public Task<ScriptBackupInfo[]> ListBackups()
|
||||
{
|
||||
var backups = _fileManager.ListBackups();
|
||||
return Task.FromResult(backups);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores scripts from a backup.
|
||||
/// </summary>
|
||||
public async Task RestoreBackup(string backupFileName, bool replaceExisting = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _fileManager.RestoreBackupAsync(backupFileName, replaceExisting);
|
||||
_consoleHubContext.LogInfo($"Backup restored successfully: {backupFileName}");
|
||||
|
||||
// Notify all clients that backup was restored (fire-and-forget)
|
||||
_ = Clients.All.SendAsync("BackupRestored", backupFileName, Context.UserIdentifier);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_consoleHubContext.LogError($"Error restoring backup '{backupFileName}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a backup file.
|
||||
/// </summary>
|
||||
public Task DeleteBackup(string backupFileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
_fileManager.DeleteBackup(backupFileName);
|
||||
_consoleHubContext.LogInfo($"Backup deleted successfully: {backupFileName}");
|
||||
|
||||
// Notify all clients that backup was deleted (fire-and-forget)
|
||||
_ = Clients.All.SendAsync("BackupDeleted", backupFileName, Context.UserIdentifier);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_consoleHubContext.LogError($"Error deleting backup '{backupFileName}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user