From 49e96d53b39be0fa5e872fee765a917ae4dbe87f Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Mon, 11 Dec 2023 11:01:55 +0100 Subject: [PATCH] Update README with enhanced instructions for Elsa server and studio The README updates include specific instructions for running the Elsa server on port 5001 and the Elsa studio application on port 6001. These enhanced guidelines provide comprehensive steps for configuring the applications regarding authentication, module usage, and use of API endpoints. --- README.md | 80 ++++++++++++++++++++--- design/screenshots/elsa-studio-login.png | Bin 0 -> 31324 bytes 2 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 design/screenshots/elsa-studio-login.png diff --git a/README.md b/README.md index 60600579c..0c5a3e587 100644 --- a/README.md +++ b/README.md @@ -369,14 +369,21 @@ dotnet new web -n "ElsaServer" -f net8.0 cd ElsaServer dotnet add package Elsa --prerelease dotnet add package Elsa.EntityFrameworkCore --prerelease +dotnet add package Elsa.EntityFrameworkCore.Sqlite --prerelease dotnet add package Elsa.Identity --prerelease dotnet add package Elsa.Scheduling --prerelease dotnet add package Elsa.Workflows.Api --prerelease +dotnet add package Elsa.CSharp --prerelease ``` Next, open Program.cs file and replace its contents with the following code: ```csharp +using Elsa.EntityFrameworkCore.Modules.Management; +using Elsa.EntityFrameworkCore.Modules.Runtime; +using Elsa.Extensions; + +var builder = WebApplication.CreateBuilder(args); builder.Services.AddElsa(elsa => { // Configure Management layer to use EF Core. @@ -388,7 +395,7 @@ builder.Services.AddElsa(elsa => // Default Identity features for authentication/authorization. elsa.UseIdentity(identity => { - identity.TokenOptions = options => options.SigningKey = "secret signing key for tokens"; + identity.TokenOptions = options => options.SigningKey = "sufficiently-large-secret-signing-key"; //This key needs to be at least 256 bits long. identity.UseAdminUserProvider(); }); @@ -399,7 +406,7 @@ builder.Services.AddElsa(elsa => elsa.UseWorkflowsApi(); // Setup a SignalR hub for real-time updates from the server. - els.UseRealTimeWorkflows(); + elsa.UseRealTimeWorkflows(); // Enable C# workflow expressions elsa.UseCSharp(); @@ -428,8 +435,12 @@ builder.Services.AddCors(cors => cors // Add Health Checks. builder.Services.AddHealthChecks(); -// Configure ASP.NET's middleware pipeline. +// Build the web application. +var app = builder.Build(); + +// Configure web application's middleware pipeline. app.UseCors(); +app.UseRouting(); // Required for SignalR. app.UseAuthentication(); app.UseAuthorization(); app.UseWorkflowsApi(); // Use Elsa API endpoints. @@ -439,6 +450,34 @@ app.UseWorkflowsSignalRHubs(); // Optional SignalR integration. Elsa Studio uses app.Run(); ``` +To run the application on port 5001, execute the following command: + +```shell +dotnet run --urls "https://localhost:5001" +``` + +The above example demonstrates how to: + +- Configure Elsa to use Entity Framework Core for persistence. +- Configure Elsa to use ASP.NET Core Identity for authentication/authorization. +- Configure Elsa to use ASP.NET Core authentication/authorization. +- Expose Elsa API endpoints. +- Setup a SignalR hub for real-time updates from the server. +- Enable C# workflow expressions. +- Enable HTTP activities. +- Enable timer activities. +- Register custom activities from the application, if any. +- Register custom workflows from the application, if any. +- Configure CORS to allow designer app hosted on a different origin to invoke the APIs. +- Add Health Checks. +- Configure ASP.NET's middleware pipeline. +- Use Elsa API endpoints. +- Use Elsa middleware to handle HTTP requests mapped to HTTP Endpoint activities. +- Use SignalR integration. Elsa Studio uses SignalR to receive real-time updates from the server. +- Run the application on port 5001. + +Now that we have an Elsa Server running, let's create an Elsa Studio application. + ### Create Elsa Studio Create a new Blazor WebAssembly application using the following command: @@ -454,21 +493,21 @@ dotnet add package Elsa.Studio.Login.BlazorWasm --prerelease Next, open Program.cs file and replace its contents with the following code: ```csharp -using Elsa.Studio.Backend.Extensions; using Elsa.Studio.Dashboard.Extensions; using Elsa.Studio.Shell; using Elsa.Studio.Shell.Extensions; using Elsa.Studio.Workflows.Extensions; using Elsa.Studio.Contracts; using Elsa.Studio.Core.BlazorWasm.Extensions; +using Elsa.Studio.Extensions; using Elsa.Studio.Login.BlazorWasm.Extensions; +using Elsa.Studio.Login.HttpMessageHandlers; using Elsa.Studio.Workflows.Designer.Extensions; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; // Build the host. var builder = WebAssemblyHostBuilder.CreateDefault(args); -var configuration = builder.Configuration; // Register root components. builder.RootComponents.Add("#app"); @@ -478,7 +517,9 @@ builder.RootComponents.RegisterCustomElsaStudioElements(); // Register shell services and modules. builder.Services.AddCore(); builder.Services.AddShell(); -builder.Services.AddRemoteBackend(configureElsaClientBuilderOptions: elsaClient => elsaClient.ConfigureHttpClientBuilder = httpClientBuilder => httpClientBuilder.AddHttpMessageHandler()); +builder.Services.AddRemoteBackend( + options => configuration.GetSection("Backend").Bind(options), + configureElsaClientBuilderOptions: elsaClient => elsaClient.ConfigureHttpClientBuilder = httpClientBuilder => httpClientBuilder.AddHttpMessageHandler()); builder.Services.AddLoginModule(); builder.Services.AddDashboardModule(); builder.Services.AddWorkflowsModule(); @@ -494,9 +535,12 @@ await startupTaskRunner.RunStartupTasksAsync(); await app.RunAsync(); ``` -For a cleaner project structure, eliminate the following directories and files: +For a cleaner project structure, delete the following directories and files: - wwwroot/css +- Pages +- App.razor +- MainLayout.razor Within the wwwroot directory, create a new appsettings.json file and populate it with the subsequent content: @@ -560,19 +604,33 @@ Finally, open the wwwroot/index.html file and replace its content with the code ``` -To see your application in action, execute the following command: +Run the following command to start the application on port 6001: ```shell -dotnet run +dotnet run --urls "https://localhost:6001" ``` -Your application should now be accessible at https://localhost:5001. The port number might vary based on your configuration. By default, you can log in using: +Your application should now be accessible at https://localhost:6001. + +![Elsa Studio Login](./design/screenshots/elsa-studio-login.png) + +By default, you can log in using: ``` Username: admin Password: password ``` +In the above example, we have: + +- Created and configured an Elsa Studio Blazor WebAssembly application. +- Configured the backend URL to point to the Elsa Server application. +- Configured the application to use the `AuthenticatingApiHttpMessageHandler` to authenticate requests to the backend. +- Configured the application to use the `Elsa.Studio.Login.BlazorWasm` module for authentication. +- Configured the application to use the `Elsa.Studio.Dashboard` module for the dashboard. +- Configured the application to use the `Elsa.Studio.Workflows` module for workflows. +- Run the application on port 6001. + ## Building from Source To build Elsa from source, you'll need: @@ -596,3 +654,5 @@ To run the Elsa.AllInOne.Web project, run the following command: ```shell dotnet run --project src/Elsa.AllInOne.Web/Elsa.AllInOne.Web.csproj ``` + +The AllInOne project is a sample application that demonstrates how to use Elsa Server + Elsa Studio in a single application. \ No newline at end of file diff --git a/design/screenshots/elsa-studio-login.png b/design/screenshots/elsa-studio-login.png new file mode 100644 index 0000000000000000000000000000000000000000..486dbbda974b0faf07f0fd9e5e6ff2895f34410f GIT binary patch literal 31324 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 J3hjNb{Tqi>tzrNG literal 0 HcmV?d00001