using XFEExtension.NetCore.WinUIHelper.Utilities;
using XFEWorkflow.Controls;
using XFEWorkflow.Core.Runtime;
using XFEWorkflow.Infrastructure.Execution;
using XFEWorkflow.Infrastructure.Plugins;
using XFEWorkflow.Infrastructure.Space;
using XFEWorkflow.Infrastructure.Storage;
using XFEWorkflow.Plugins.Desktop;
using XFEWorkflow.Plugins.WeCom;
namespace XFEWorkflow.Services;
public interface IWorkflowApplicationService
{
WorkflowRepository Workflows { get; }
PluginManager Plugins { get; }
RunCoordinator Runs { get; }
}
public sealed class AppServices : IWorkflowApplicationService, IAsyncDisposable
{
public static AppServices Current { get; } = new();
public WorkspacePaths Paths { get; }
public ClientState State { get; }
public CredentialVault Credentials { get; }
public SecretRedactor Redactor { get; } = new();
public WorkflowRepository Workflows { get; }
public PluginManager Plugins { get; }
public RunCoordinator Runs { get; }
public WorkflowScheduler Scheduler { get; }
public SpaceClient Space { get; }
public WorkflowLogService Logs { get; }
public PluginPageService PluginPages { get; }
public DesktopInputService Desktop { get; } = new();
public ScreenToolsService ScreenTools { get; } = new();
public CommandProcessHost Commands { get; }
public WeComHostService WeCom { get; }
public BrowserHost? Browser { get; private set; }
public Task Ready { get; private set; } = Task.CompletedTask;
private bool initialized;
private AppServices()
{
Paths = new(Utilities.AppPath.WorkspaceDirectory);
State = new(Paths); Credentials = new(State); Workflows = new(Paths); Plugins = new(Paths, State);
Runs = new(Paths, Plugins, Credentials, Redactor, Path.Combine(AppContext.BaseDirectory, "Runner", "XFEWorkflow.Runner.exe"));
Commands = new(Path.Combine(AppContext.BaseDirectory, "Runner", "XFEWorkflow.Runner.exe"));
WeCom = new(Path.Combine(Paths.Root, "PluginData", "WeCom"), Path.Combine(AppContext.BaseDirectory, "NativeBridge", "WeCom"));
WeCom.Log += message => XFEExtension.NetCore.XFEConsole.XFEConsole.Log.AddLog(Redactor.Redact(message));
Scheduler = new(Workflows, Runs); Space = new(State, Credentials);
Logs = new(Paths, Runs, Redactor);
PluginPages = new(this);
Runs.RunChanged += Logs.Observe;
Scheduler.Error += message => XFEExtension.NetCore.XFEConsole.XFEConsole.Log.AddLog(Redactor.Redact(message), XFEExtension.NetCore.XFEConsole.LogLevel.Error);
Plugins.Changed += () => { if (initialized) _ = RefreshPluginTriggersAsync(); };
Desktop.Backend = State.DesktopBackend;
Runs.Host = async (id, method, input, ct) =>
{
if (method.StartsWith("browser.", StringComparison.Ordinal)) return await (Browser ?? throw new InvalidOperationException("浏览器服务尚未启动")).InvokeAsync(id, method, input, ct);
if (method == "desktop.input") return await Desktop.ExecuteAsync(id, input, ct);
if (method.StartsWith("screen.", StringComparison.Ordinal)) return await ScreenTools.InvokeAsync(method, input, ct);
if (method == "command.run") return await Commands.ExecuteAsync(id, input, ct);
if (method.StartsWith("wecom.", StringComparison.Ordinal)) return await WeCom.InvokeAsync(id, method, input, ct);
throw new NotSupportedException(method);
};
Runs.ReleaseHost = async id => { await Desktop.ReleaseAsync(id); await Commands.ReleaseAsync(id); await WeCom.ReleaseAsync(id); if (Browser is not null) await Browser.ReleaseAsync(id); };
ServiceManager.RegisterService<IWorkflowApplicationService>(() => this);
}
public void Start(BrowserHost browser) { Browser = browser; browser.Paths = Paths; Ready = InitializeAsync(); }
private async Task InitializeAsync()
{
await Plugins.ImportBundledAsync(Path.Combine(AppContext.BaseDirectory, "BundledPlugins"));
await Runs.InitializeAsync();
foreach (var reference in Credentials.References) { try { Redactor.Add(Credentials.Get(reference)); } catch (System.Security.Cryptography.CryptographicException) { } }
if ((await Workflows.ListAsync()).Count == 0) await Workflows.SaveAsync(WorkflowTemplates.Welcome());
await Scheduler.StartAsync();
initialized = true;
}
private async Task RefreshPluginTriggersAsync()
{
try { await Scheduler.RefreshAsync(); }
catch (OperationCanceledException) { }
catch (Exception ex) { XFEExtension.NetCore.XFEConsole.XFEConsole.Log.AddLog(Redactor.Redact(ex.Message), XFEExtension.NetCore.XFEConsole.LogLevel.Error); }
}
public async ValueTask DisposeAsync()
{
initialized = false;
if (WorkflowEditorPage.Current is { } editor) await editor.SaveAllAsync();
if (MainPage.Current?.ViewModel.SelectedWorkflow is { } workflow) await Workflows.SaveAsync(workflow);
await Scheduler.DisposeAsync(); ScreenTools.CancelSelection(); Desktop.EmergencyStop(); await Runs.DisposeAsync();
await PluginPages.DisposeAsync();
await Commands.DisposeAsync();
await WeCom.DisposeAsync();
if (Browser is not null) await Browser.DisposeAsync();
Desktop.Dispose(); await State.DisposeAsync();
}
}
using XFEExtension.NetCore.WinUIHelper.Utilities;
using XFEWorkflow.Controls;
using XFEWorkflow.Core.Runtime;
using XFEWorkflow.Infrastructure.Execution;
using XFEWorkflow.Infrastructure.Plugins;
using XFEWorkflow.Infrastructure.Space;
using XFEWorkflow.Infrastructure.Storage;
using XFEWorkflow.Plugins.Desktop;
using XFEWorkflow.Plugins.WeCom;
namespace XFEWorkflow.Services;
public interface IWorkflowApplicationService
{
WorkflowRepository Workflows { get; }
PluginManager Plugins { get; }
RunCoordinator Runs { get; }
}
public sealed class AppServices : IWorkflowApplicationService, IAsyncDisposable
{
public static AppServices Current { get; } = new();
public WorkspacePaths Paths { get; }
public ClientState State { get; }
public CredentialVault Credentials { get; }
public SecretRedactor Redactor { get; } = new();
public WorkflowRepository Workflows { get; }
public PluginManager Plugins { get; }
public RunCoordinator Runs { get; }
public WorkflowScheduler Scheduler { get; }
public SpaceClient Space { get; }
public WorkflowLogService Logs { get; }
public PluginPageService PluginPages { get; }
public DesktopInputService Desktop { get; } = new();
public ScreenToolsService ScreenTools { get; } = new();
public CommandProcessHost Commands { get; }
public WeComHostService WeCom { get; }
public BrowserHost? Browser { get; private set; }
public Task Ready { get; private set; } = Task.CompletedTask;
private bool initialized;
private AppServices()
{
Paths = new(Utilities.AppPath.WorkspaceDirectory);
State = new(Paths); Credentials = new(State); Workflows = new(Paths); Plugins = new(Paths, State);
Runs = new(Paths, Plugins, Credentials, Redactor, Path.Combine(AppContext.BaseDirectory, "Runner", "XFEWorkflow.Runner.exe"));
Commands = new(Path.Combine(AppContext.BaseDirectory, "Runner", "XFEWorkflow.Runner.exe"));
WeCom = new(Path.Combine(Paths.Root, "PluginData", "WeCom"), Path.Combine(AppContext.BaseDirectory, "NativeBridge", "WeCom"));
WeCom.Log += message => XFEExtension.NetCore.XFEConsole.XFEConsole.Log.AddLog(Redactor.Redact(message));
Scheduler = new(Workflows, Runs); Space = new(State, Credentials);
Logs = new(Paths, Runs, Redactor);
PluginPages = new(this);
Runs.RunChanged += Logs.Observe;
Scheduler.Error += message => XFEExtension.NetCore.XFEConsole.XFEConsole.Log.AddLog(Redactor.Redact(message), XFEExtension.NetCore.XFEConsole.LogLevel.Error);
Plugins.Changed += () => { if (initialized) _ = RefreshPluginTriggersAsync(); };
Desktop.Backend = State.DesktopBackend;
Runs.Host = async (id, method, input, ct) =>
{
if (method.StartsWith("browser.", StringComparison.Ordinal)) return await (Browser ?? throw new InvalidOperationException("浏览器服务尚未启动")).InvokeAsync(id, method, input, ct);
if (method == "desktop.input") return await Desktop.ExecuteAsync(id, input, ct);
if (method.StartsWith("screen.", StringComparison.Ordinal)) return await ScreenTools.InvokeAsync(method, input, ct);
if (method == "command.run") return await Commands.ExecuteAsync(id, input, ct);
if (method.StartsWith("wecom.", StringComparison.Ordinal)) return await WeCom.InvokeAsync(id, method, input, ct);
throw new NotSupportedException(method);
};
Runs.ReleaseHost = async id => { await Desktop.ReleaseAsync(id); await Commands.ReleaseAsync(id); await WeCom.ReleaseAsync(id); if (Browser is not null) await Browser.ReleaseAsync(id); };
ServiceManager.RegisterService<IWorkflowApplicationService>(() => this);
}
public void Start(BrowserHost browser) { Browser = browser; browser.Paths = Paths; Ready = InitializeAsync(); }
private async Task InitializeAsync()
{
await Plugins.ImportBundledAsync(Path.Combine(AppContext.BaseDirectory, "BundledPlugins"));
await Runs.InitializeAsync();
foreach (var reference in Credentials.References) { try { Redactor.Add(Credentials.Get(reference)); } catch (System.Security.Cryptography.CryptographicException) { } }
if ((await Workflows.ListAsync()).Count == 0) await Workflows.SaveAsync(WorkflowTemplates.Welcome());
await Scheduler.StartAsync();
initialized = true;
}
private async Task RefreshPluginTriggersAsync()
{
try { await Scheduler.RefreshAsync(); }
catch (OperationCanceledException) { }
catch (Exception ex) { XFEExtension.NetCore.XFEConsole.XFEConsole.Log.AddLog(Redactor.Redact(ex.Message), XFEExtension.NetCore.XFEConsole.LogLevel.Error); }
}
public async ValueTask DisposeAsync()
{
initialized = false;
if (WorkflowEditorPage.Current is { } editor) await editor.SaveAllAsync();
if (MainPage.Current?.ViewModel.SelectedWorkflow is { } workflow) await Workflows.SaveAsync(workflow);
await Scheduler.DisposeAsync(); ScreenTools.CancelSelection(); Desktop.EmergencyStop(); await Runs.DisposeAsync();
await PluginPages.DisposeAsync();
await Commands.DisposeAsync();
await WeCom.DisposeAsync();
if (Browser is not null) await Browser.DisposeAsync();
Desktop.Dispose(); await State.DisposeAsync();
}
}