Marketing sample migration to AGNext (#234)
|
@ -27,7 +27,9 @@
|
|||
"version": "8.0.302"
|
||||
},
|
||||
"ghcr.io/elanhasson/devcontainer-features/dotnet-aspire-daily:1": {},
|
||||
"ghcr.io/devcontainers/features/azure-cli:1": {}
|
||||
"ghcr.io/devcontainers/features/azure-cli:1": {},
|
||||
"ghcr.io/azure/azure-dev/azd:0": {},
|
||||
"ghcr.io/devcontainers/features/node:1": {}
|
||||
},
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
|
|
@ -21,18 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AI.DevTeam", "sam
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AI.DevTeam.Dapr", "samples\gh-flow\src\Microsoft.AI.DevTeam.Dapr\Microsoft.AI.DevTeam.Dapr.csproj", "{A7677950-18F1-42FF-8018-870395417465}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "marketing", "marketing", "{1FF691E4-E27D-4A7E-861C-4D6291B6EE35}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4225F3BA-A39D-4680-945E-F2869E98AEA2}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marketing", "samples\marketing\src\backend\Marketing.csproj", "{62F276F3-9184-4908-A7FB-065B4E491BE2}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "seed-memory", "samples\gh-flow\src\seed-memory\seed-memory.csproj", "{EF5DF177-F4F2-49D5-9E1C-2E37869238D8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowsApp", "samples\WorkflowsApp\WorkflowsApp.csproj", "{92CAAA29-8633-4984-B169-29BB89E0EA23}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflowsapp", "workflowsapp", "{65CF8F20-D740-46AC-A869-FA609D960A09}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E6A25E68-EA75-4294-9B45-3CF2BB3B4ACD}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
|
@ -52,6 +42,20 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greeter.AgentHost", "sample
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greeter.AgentWorker", "samples\Greeter\Greeter.AgentWorker\Greeter.AgentWorker.csproj", "{7BA721F2-EE46-4A85-A8C8-3695C4ADF93E}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "marketing-team", "marketing-team", "{DD53D185-5C0A-4B45-BF4D-F597F899E671}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketing.AgentHost", "samples\marketing-team\src\Marketing.AgentHost\Marketing.AgentHost.csproj", "{5504D650-D013-4F5E-9A42-EDC9B4A36E1D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketing.Agents", "samples\marketing-team\src\Marketing.Agents\Marketing.Agents.csproj", "{62D6E39D-E452-47C3-B756-B8B6CE26C3E4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketing.AppHost", "samples\marketing-team\src\Marketing.AppHost\Marketing.AppHost.csproj", "{8E998B80-202E-4751-9FC4-0623C024AF25}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketing.Backend", "samples\marketing-team\src\Marketing.Backend\Marketing.Backend.csproj", "{BE82D56A-0281-4B01-9B99-D2F051585AF0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketing.ServiceDefaults", "samples\marketing-team\src\Marketing.ServiceDefaults\Marketing.ServiceDefaults.csproj", "{85A49174-5545-49D2-B5FD-BEDC9A401AB3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketing.Shared", "samples\marketing-team\src\Marketing.Shared\Marketing.Shared.csproj", "{C95DB5F8-6BDB-446E-B0B8-1A1D7F716C42}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -78,18 +82,10 @@ Global
|
|||
{A7677950-18F1-42FF-8018-870395417465}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A7677950-18F1-42FF-8018-870395417465}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A7677950-18F1-42FF-8018-870395417465}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{62F276F3-9184-4908-A7FB-065B4E491BE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{62F276F3-9184-4908-A7FB-065B4E491BE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{62F276F3-9184-4908-A7FB-065B4E491BE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{62F276F3-9184-4908-A7FB-065B4E491BE2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EF5DF177-F4F2-49D5-9E1C-2E37869238D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EF5DF177-F4F2-49D5-9E1C-2E37869238D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EF5DF177-F4F2-49D5-9E1C-2E37869238D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EF5DF177-F4F2-49D5-9E1C-2E37869238D8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{92CAAA29-8633-4984-B169-29BB89E0EA23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{92CAAA29-8633-4984-B169-29BB89E0EA23}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{92CAAA29-8633-4984-B169-29BB89E0EA23}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{92CAAA29-8633-4984-B169-29BB89E0EA23}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{20E5C8C3-CE40-4FC3-96F8-B4A2C51936E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{20E5C8C3-CE40-4FC3-96F8-B4A2C51936E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{20E5C8C3-CE40-4FC3-96F8-B4A2C51936E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
@ -114,6 +110,30 @@ Global
|
|||
{7BA721F2-EE46-4A85-A8C8-3695C4ADF93E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7BA721F2-EE46-4A85-A8C8-3695C4ADF93E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7BA721F2-EE46-4A85-A8C8-3695C4ADF93E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5504D650-D013-4F5E-9A42-EDC9B4A36E1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5504D650-D013-4F5E-9A42-EDC9B4A36E1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5504D650-D013-4F5E-9A42-EDC9B4A36E1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5504D650-D013-4F5E-9A42-EDC9B4A36E1D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{62D6E39D-E452-47C3-B756-B8B6CE26C3E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{62D6E39D-E452-47C3-B756-B8B6CE26C3E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{62D6E39D-E452-47C3-B756-B8B6CE26C3E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{62D6E39D-E452-47C3-B756-B8B6CE26C3E4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8E998B80-202E-4751-9FC4-0623C024AF25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8E998B80-202E-4751-9FC4-0623C024AF25}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8E998B80-202E-4751-9FC4-0623C024AF25}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8E998B80-202E-4751-9FC4-0623C024AF25}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BE82D56A-0281-4B01-9B99-D2F051585AF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BE82D56A-0281-4B01-9B99-D2F051585AF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BE82D56A-0281-4B01-9B99-D2F051585AF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BE82D56A-0281-4B01-9B99-D2F051585AF0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{85A49174-5545-49D2-B5FD-BEDC9A401AB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{85A49174-5545-49D2-B5FD-BEDC9A401AB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{85A49174-5545-49D2-B5FD-BEDC9A401AB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{85A49174-5545-49D2-B5FD-BEDC9A401AB3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C95DB5F8-6BDB-446E-B0B8-1A1D7F716C42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C95DB5F8-6BDB-446E-B0B8-1A1D7F716C42}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C95DB5F8-6BDB-446E-B0B8-1A1D7F716C42}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C95DB5F8-6BDB-446E-B0B8-1A1D7F716C42}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -126,12 +146,7 @@ Global
|
|||
{50809508-F830-4553-9C4E-C802E0A0F690} = {E0E93575-7187-4975-8D72-6F285CD01767}
|
||||
{79981945-61F7-4E1A-8949-7808FD75471B} = {50809508-F830-4553-9C4E-C802E0A0F690}
|
||||
{A7677950-18F1-42FF-8018-870395417465} = {50809508-F830-4553-9C4E-C802E0A0F690}
|
||||
{1FF691E4-E27D-4A7E-861C-4D6291B6EE35} = {943853E7-513D-45EA-870F-549CFC0AF8E8}
|
||||
{4225F3BA-A39D-4680-945E-F2869E98AEA2} = {1FF691E4-E27D-4A7E-861C-4D6291B6EE35}
|
||||
{62F276F3-9184-4908-A7FB-065B4E491BE2} = {4225F3BA-A39D-4680-945E-F2869E98AEA2}
|
||||
{EF5DF177-F4F2-49D5-9E1C-2E37869238D8} = {943853E7-513D-45EA-870F-549CFC0AF8E8}
|
||||
{92CAAA29-8633-4984-B169-29BB89E0EA23} = {65CF8F20-D740-46AC-A869-FA609D960A09}
|
||||
{65CF8F20-D740-46AC-A869-FA609D960A09} = {943853E7-513D-45EA-870F-549CFC0AF8E8}
|
||||
{20E5C8C3-CE40-4FC3-96F8-B4A2C51936E9} = {290F9824-BAD3-4703-B9B7-FE9C4BE3A1CF}
|
||||
{B9188ADC-D322-4B38-B3D6-95338E89C34B} = {290F9824-BAD3-4703-B9B7-FE9C4BE3A1CF}
|
||||
{320B05A6-4E1B-4B15-B3F6-745819D2BF22} = {943853E7-513D-45EA-870F-549CFC0AF8E8}
|
||||
|
@ -139,6 +154,13 @@ Global
|
|||
{E45990FD-85B3-44A2-8646-4AB2E868BC5F} = {320B05A6-4E1B-4B15-B3F6-745819D2BF22}
|
||||
{590BACCE-7310-4D7B-9618-46496F2EB171} = {320B05A6-4E1B-4B15-B3F6-745819D2BF22}
|
||||
{7BA721F2-EE46-4A85-A8C8-3695C4ADF93E} = {320B05A6-4E1B-4B15-B3F6-745819D2BF22}
|
||||
{DD53D185-5C0A-4B45-BF4D-F597F899E671} = {943853E7-513D-45EA-870F-549CFC0AF8E8}
|
||||
{5504D650-D013-4F5E-9A42-EDC9B4A36E1D} = {DD53D185-5C0A-4B45-BF4D-F597F899E671}
|
||||
{62D6E39D-E452-47C3-B756-B8B6CE26C3E4} = {DD53D185-5C0A-4B45-BF4D-F597F899E671}
|
||||
{8E998B80-202E-4751-9FC4-0623C024AF25} = {DD53D185-5C0A-4B45-BF4D-F597F899E671}
|
||||
{BE82D56A-0281-4B01-9B99-D2F051585AF0} = {DD53D185-5C0A-4B45-BF4D-F597F899E671}
|
||||
{85A49174-5545-49D2-B5FD-BEDC9A401AB3} = {DD53D185-5C0A-4B45-BF4D-F597F899E671}
|
||||
{C95DB5F8-6BDB-446E-B0B8-1A1D7F716C42} = {DD53D185-5C0A-4B45-BF4D-F597F899E671}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C9250809-2B94-4552-99DE-333E4646A16F}
|
||||
|
|
|
@ -2,76 +2,75 @@
|
|||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="AspNetCore.Authentication.ApiKey" Version="8.0.0" />
|
||||
<PackageVersion Include="AspNetCore.Authentication.ApiKey" Version="8.0.1" />
|
||||
<PackageVersion Include="Aspire.Azure.AI.OpenAI" Version="8.0.1-preview.8.24267.1" />
|
||||
<PackageVersion Include="Aspire.Hosting.AppHost" Version="8.0.1" />
|
||||
<PackageVersion Include="Aspire.Hosting.Azure.ApplicationInsights" Version="8.0.1" />
|
||||
<PackageVersion Include="Aspire.Hosting.Azure.CognitiveServices" Version="8.0.1" />
|
||||
<PackageVersion Include="Aspire.Hosting.NodeJs" Version="8.0.1" />
|
||||
<PackageVersion Include="Aspire.Hosting.Orleans" Version="8.0.1" />
|
||||
<PackageVersion Include="Aspire.Hosting.Qdrant" Version="8.0.1" />
|
||||
<PackageVersion Include="Aspire.Hosting.Redis" Version="8.0.1" />
|
||||
<PackageVersion Include="Azure.Data.Tables" Version="12.8.1" />
|
||||
<PackageVersion Include="Aspire.Hosting.AppHost" Version="8.0.2" />
|
||||
<PackageVersion Include="Aspire.Hosting.Azure.ApplicationInsights" Version="8.0.2" />
|
||||
<PackageVersion Include="Aspire.Hosting.Azure.CognitiveServices" Version="8.0.2" />
|
||||
<PackageVersion Include="Aspire.Hosting.NodeJs" Version="8.0.2" />
|
||||
<PackageVersion Include="Aspire.Hosting.Orleans" Version="8.0.2" />
|
||||
<PackageVersion Include="Aspire.Hosting.Qdrant" Version="8.0.2" />
|
||||
<PackageVersion Include="Aspire.Hosting.Redis" Version="8.0.2" />
|
||||
<PackageVersion Include="Azure.AI.OpenAI" Version="1.0.0-beta.17" />
|
||||
<PackageVersion Include="Azure.Data.Tables" Version="12.8.3" />
|
||||
<PackageVersion Include="Azure.Identity" Version="1.12.0" />
|
||||
<PackageVersion Include="Azure.ResourceManager.ContainerInstance" Version="1.2.0" />
|
||||
<PackageVersion Include="Azure.ResourceManager.ContainerInstance" Version="1.2.1" />
|
||||
<PackageVersion Include="Azure.Storage.Files.Shares" Version="12.18.0" />
|
||||
<PackageVersion Include="CloudNative.CloudEvents.SystemTextJson" Version="2.7.1" />
|
||||
<PackageVersion Include="Dapr.Actors" Version="1.13.0" />
|
||||
<PackageVersion Include="Dapr.Actors.AspNetCore" Version="1.13.0" />
|
||||
<PackageVersion Include="Dapr.AspNetCore" Version="1.13.0" />
|
||||
<PackageVersion Include="Dapr.Client" Version="1.13.0" />
|
||||
<PackageVersion Include="Elsa" Version="3.0.6" />
|
||||
<PackageVersion Include="Elsa.EntityFrameworkCore" Version="3.0.6" />
|
||||
<PackageVersion Include="Elsa.EntityFrameworkCore.Sqlite" Version="3.0.6" />
|
||||
<PackageVersion Include="Elsa.Http" Version="3.0.6" />
|
||||
<PackageVersion Include="Elsa.Identity" Version="3.0.6" />
|
||||
<PackageVersion Include="Elsa.Workflows.Api" Version="3.0.6" />
|
||||
<PackageVersion Include="Elsa.Workflows.Core" Version="3.0.6" />
|
||||
<PackageVersion Include="Dapr.Actors" Version="1.13.1" />
|
||||
<PackageVersion Include="Dapr.Actors.AspNetCore" Version="1.13.1" />
|
||||
<PackageVersion Include="Dapr.AspNetCore" Version="1.13.1" />
|
||||
<PackageVersion Include="Dapr.Client" Version="1.13.1" />
|
||||
<PackageVersion Include="Elsa" Version="3.1.3" />
|
||||
<PackageVersion Include="Elsa.EntityFrameworkCore" Version="3.1.3" />
|
||||
<PackageVersion Include="Elsa.EntityFrameworkCore.Sqlite" Version="3.1.3" />
|
||||
<PackageVersion Include="Elsa.Http" Version="3.1.3" />
|
||||
<PackageVersion Include="Elsa.Identity" Version="3.1.3" />
|
||||
<PackageVersion Include="Elsa.Workflows.Api" Version="3.1.3" />
|
||||
<PackageVersion Include="Elsa.Workflows.Core" Version="3.1.3" />
|
||||
<PackageVersion Include="Elsa.Workflows.Designer" Version="3.0.0-preview.727" />
|
||||
<PackageVersion Include="Elsa.Workflows.Management" Version="3.0.6" />
|
||||
<PackageVersion Include="Elsa.Workflows.Management" Version="3.1.3" />
|
||||
<PackageVersion Include="Grpc.AspNetCore" Version="2.63.0" />
|
||||
<PackageVersion Include="Grpc.Net.ClientFactory" Version="2.63.0" />
|
||||
<PackageVersion Include="Grpc.Tools" Version="2.63.0" />
|
||||
<PackageVersion Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Azure" Version="1.7.2" />
|
||||
<PackageVersion Include="Grpc.Tools" Version="2.64.0" />
|
||||
<PackageVersion Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Azure" Version="1.7.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.FileExtensions" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.FileExtensions" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="8.6.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="8.7.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Clustering.Cosmos" Version="8.1.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Persistence.Cosmos" Version="8.1.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Reminders" Version="8.1.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Reminders.Cosmos" Version="8.1.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Runtime" Version="8.1.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Sdk" Version="8.1.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Serialization.Protobuf" Version="8.1.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Server" Version="8.1.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Streaming" Version="8.1.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Streaming.EventHubs" Version="8.1.0" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.15.0" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Qdrant" Version="1.10.0-alpha" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Plugins.Memory" Version="1.10.0-alpha" />
|
||||
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.2" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Clustering.Cosmos" Version="8.2.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Persistence.Cosmos" Version="8.2.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Reminders" Version="8.2.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Reminders.Cosmos" Version="8.2.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Runtime" Version="8.2.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Sdk" Version="8.2.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Serialization.Protobuf" Version="8.2.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Server" Version="8.2.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Streaming" Version="8.2.0" />
|
||||
<PackageVersion Include="Microsoft.Orleans.Streaming.EventHubs" Version="8.2.0" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.15.1" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Qdrant" Version="1.15.1-alpha" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Plugins.Memory" Version="1.15.1-alpha" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageVersion Include="Octokit" Version="10.0.0" />
|
||||
<PackageVersion Include="Octokit.Webhooks.AspNetCore" Version="2.0.3" />
|
||||
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.1" />
|
||||
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.8.1" />
|
||||
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.8.1" />
|
||||
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.8.1" />
|
||||
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.8.0" />
|
||||
<PackageVersion Include="OrleansDashboard" Version="8.0.0" />
|
||||
<PackageVersion Include="Octokit" Version="13.0.1" />
|
||||
<PackageVersion Include="Octokit.Webhooks.AspNetCore" Version="2.2.2" />
|
||||
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
|
||||
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
|
||||
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
|
||||
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
|
||||
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.9.0" />
|
||||
<PackageVersion Include="OrleansDashboard" Version="8.2.0" />
|
||||
<PackageVersion Include="PdfPig" Version="0.1.9-alpha-20240324-e7896" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.4.1" />
|
||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.6.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,11 +1,8 @@
|
|||
using Microsoft.AI.Agents.Worker;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.AddServiceDefaults();
|
||||
builder.Services.AddProblemDetails();
|
||||
builder.Services.AddGrpc();
|
||||
builder.Logging.SetMinimumLevel(LogLevel.Information);
|
||||
|
||||
builder.AddServiceDefaults();
|
||||
builder.AddAgentService();
|
||||
|
||||
var app = builder.Build();
|
||||
|
|
|
@ -4,9 +4,12 @@ using AgentId = Microsoft.AI.Agents.Worker.Client.AgentId;
|
|||
|
||||
namespace Greeter.AgentWorker;
|
||||
|
||||
internal sealed class Client(ILogger<Client> logger, AgentWorkerRuntime runtime) : AgentBase(new ClientContext(logger, runtime))
|
||||
public sealed class AgentClient(ILogger<AgentClient> logger, AgentWorkerRuntime runtime) : AgentBase(new ClientContext(logger, runtime))
|
||||
{
|
||||
private sealed class ClientContext(ILogger<Client> logger, AgentWorkerRuntime runtime) : IAgentContext
|
||||
public async ValueTask PublishEventAsync(Event @event) => await PublishEvent(@event);
|
||||
public async ValueTask<RpcResponse> SendRequestAsync(AgentId target, string method, Dictionary<string, string> parameters) => await RequestAsync(target, method, parameters);
|
||||
|
||||
private sealed class ClientContext(ILogger<AgentClient> logger, AgentWorkerRuntime runtime) : IAgentContext
|
||||
{
|
||||
public AgentId AgentId { get; } = new AgentId("client", Guid.NewGuid().ToString());
|
||||
public AgentBase? AgentInstance { get; set; }
|
|
@ -11,7 +11,7 @@ builder.AddServiceDefaults();
|
|||
var agentBuilder = builder.AddAgentWorker("https://agenthost");
|
||||
agentBuilder.AddAgent<GreetingAgent>("greeter");
|
||||
builder.Services.AddHostedService<MyBackgroundService>();
|
||||
builder.Services.AddSingleton<Client>();
|
||||
builder.Services.AddSingleton<AgentClient>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
@ -21,7 +21,7 @@ app.Run();
|
|||
|
||||
internal sealed class GreetingAgent(IAgentContext context, ILogger<GreetingAgent> logger) : AgentBase(context)
|
||||
{
|
||||
protected override Task HandleEvent(Microsoft.AI.Agents.Abstractions.Event @event)
|
||||
protected override Task HandleEvent(Event @event)
|
||||
{
|
||||
logger.LogInformation("[{Id}] Received event: '{Event}'.", AgentId, @event);
|
||||
return base.HandleEvent(@event);
|
||||
|
@ -34,7 +34,7 @@ internal sealed class GreetingAgent(IAgentContext context, ILogger<GreetingAgent
|
|||
}
|
||||
}
|
||||
|
||||
internal sealed class MyBackgroundService(ILogger<MyBackgroundService> logger, Client client) : BackgroundService
|
||||
internal sealed class MyBackgroundService(ILogger<MyBackgroundService> logger, AgentClient client) : BackgroundService
|
||||
{
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
|
@ -44,7 +44,7 @@ internal sealed class MyBackgroundService(ILogger<MyBackgroundService> logger, C
|
|||
{
|
||||
var generatedCodeId = Guid.NewGuid().ToString();
|
||||
var instanceId = Guid.NewGuid().ToString();
|
||||
var response = await client.RequestAsync(
|
||||
var response = await client.SendRequestAsync(
|
||||
new AgentId("greeter", "foo"),
|
||||
"echo",
|
||||
new Dictionary<string, string> { ["message"] = "Hello, agents!" }).ConfigureAwait(false);
|
||||
|
|
|
@ -2,7 +2,6 @@ using Microsoft.AI.Agents.Abstractions;
|
|||
using Microsoft.AI.Agents.Orleans;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Memory;
|
||||
using Orleans.Runtime;
|
||||
|
||||
namespace Microsoft.AI.DevTeam;
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ using Microsoft.AI.Agents.Orleans;
|
|||
using Microsoft.AI.DevTeam.Events;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Memory;
|
||||
using Orleans.Runtime;
|
||||
|
||||
namespace Microsoft.AI.DevTeam;
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ using Microsoft.AI.DevTeam.Events;
|
|||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using Microsoft.SemanticKernel.Memory;
|
||||
using Orleans.Runtime;
|
||||
|
||||
namespace Microsoft.AI.DevTeam;
|
||||
[ImplicitStreamSubscription(Consts.MainNamespace)]
|
||||
|
|
|
@ -3,7 +3,6 @@ using Microsoft.AI.Agents.Orleans;
|
|||
using Microsoft.AI.DevTeam.Events;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Memory;
|
||||
using Orleans.Runtime;
|
||||
|
||||
namespace Microsoft.AI.DevTeam;
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using Microsoft.AI.Agents.Abstractions;
|
||||
using Microsoft.AI.Agents.Orleans;
|
||||
using Microsoft.AI.DevTeam.Events;
|
||||
using Orleans.Runtime;
|
||||
using Orleans.Timers;
|
||||
|
||||
namespace Microsoft.AI.DevTeam;
|
||||
|
|
|
@ -6,9 +6,9 @@ using Octokit.Webhooks.Events;
|
|||
using Octokit.Webhooks.Events.IssueComment;
|
||||
using Octokit.Webhooks.Events.Issues;
|
||||
using Octokit.Webhooks.Models;
|
||||
using Orleans.Runtime;
|
||||
|
||||
namespace Microsoft.AI.DevTeam;
|
||||
|
||||
public sealed class GithubWebHookProcessor : WebhookEventProcessor
|
||||
{
|
||||
private readonly ILogger<GithubWebHookProcessor> _logger;
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
.azure
|
|
@ -1,40 +0,0 @@
|
|||
# [In progress] Marketing Saple application
|
||||
|
||||
This is a demo application that showcase the different features of the AI Agent framework.
|
||||
There are five agents in this application that control the different areas of the UI autonomously.
|
||||
|
||||
The agents are designed to be able to interact with each other and the user to achieve their goals.
|
||||
To do that each agent has
|
||||
|
||||
![Agents](readme-media/screenshot.png)
|
||||
|
||||
![Agents](readme-media/agents.png)
|
||||
|
||||
## Requirements to run locally
|
||||
### Frontend
|
||||
The latest version of Node.js and npm
|
||||
|
||||
### Backend
|
||||
Visual Studio or Visual Studio code and the latest version of dotnet
|
||||
|
||||
## Running with azd
|
||||
To run the application with azd, you need to have the azd cli installed. You can install it following the instructions here:
|
||||
|
||||
https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd?tabs=winget-windows%2Cbrew-mac%2Cscript-linux
|
||||
|
||||
|
||||
|
||||
Then you can run the following command to start the application:
|
||||
```powers
|
||||
|
||||
## How to run the application locally
|
||||
|
||||
Execute Run.ps1. IF you are missing the config file the script will create an empty one for you and ask you to fill it out.
|
||||
```
|
||||
.\run.ps1
|
||||
```
|
||||
|
||||
## How to debug the application locally
|
||||
To debug the backend, you can simply open the solution in Visual Studio, and press F5 to start debugging.
|
||||
Remember to copy `appsettings.local.template.json` to `appsettings.json` and fill out the values.</p>
|
||||
The frontend is a NodeJS React application. You can debug it using Visual Studio code.
|
|
@ -1,20 +0,0 @@
|
|||
# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json
|
||||
|
||||
name: marketing
|
||||
metadata:
|
||||
template: azd-init@1.8.2
|
||||
services:
|
||||
backend:
|
||||
project: "src/backend"
|
||||
host: containerapp
|
||||
language: dotnet
|
||||
docker:
|
||||
path: Dockerfile
|
||||
context: ../../../../
|
||||
frontend:
|
||||
project: "src/frontend"
|
||||
host: containerapp
|
||||
language: ts
|
||||
dist: build
|
||||
docker:
|
||||
path: Dockerfile
|
|
@ -1,135 +0,0 @@
|
|||
{
|
||||
"analysisServicesServers": "as",
|
||||
"apiManagementService": "apim-",
|
||||
"appConfigurationStores": "appcs-",
|
||||
"appManagedEnvironments": "cae-",
|
||||
"appContainerApps": "ca-",
|
||||
"authorizationPolicyDefinitions": "policy-",
|
||||
"automationAutomationAccounts": "aa-",
|
||||
"blueprintBlueprints": "bp-",
|
||||
"blueprintBlueprintsArtifacts": "bpa-",
|
||||
"cacheRedis": "redis-",
|
||||
"cdnProfiles": "cdnp-",
|
||||
"cdnProfilesEndpoints": "cdne-",
|
||||
"cognitiveServicesAccounts": "cog-",
|
||||
"cognitiveServicesFormRecognizer": "cog-fr-",
|
||||
"cognitiveServicesTextAnalytics": "cog-ta-",
|
||||
"computeAvailabilitySets": "avail-",
|
||||
"computeCloudServices": "cld-",
|
||||
"computeDiskEncryptionSets": "des",
|
||||
"computeDisks": "disk",
|
||||
"computeDisksOs": "osdisk",
|
||||
"computeGalleries": "gal",
|
||||
"computeSnapshots": "snap-",
|
||||
"computeVirtualMachines": "vm",
|
||||
"computeVirtualMachineScaleSets": "vmss-",
|
||||
"containerInstanceContainerGroups": "ci",
|
||||
"containerRegistryRegistries": "cr",
|
||||
"containerServiceManagedClusters": "aks-",
|
||||
"databricksWorkspaces": "dbw-",
|
||||
"dataFactoryFactories": "adf-",
|
||||
"dataLakeAnalyticsAccounts": "dla",
|
||||
"dataLakeStoreAccounts": "dls",
|
||||
"dataMigrationServices": "dms-",
|
||||
"dBforMySQLServers": "mysql-",
|
||||
"dBforPostgreSQLServers": "psql-",
|
||||
"devicesIotHubs": "iot-",
|
||||
"devicesProvisioningServices": "provs-",
|
||||
"devicesProvisioningServicesCertificates": "pcert-",
|
||||
"documentDBDatabaseAccounts": "cosmos-",
|
||||
"eventGridDomains": "evgd-",
|
||||
"eventGridDomainsTopics": "evgt-",
|
||||
"eventGridEventSubscriptions": "evgs-",
|
||||
"eventHubNamespaces": "evhns-",
|
||||
"eventHubNamespacesEventHubs": "evh-",
|
||||
"hdInsightClustersHadoop": "hadoop-",
|
||||
"hdInsightClustersHbase": "hbase-",
|
||||
"hdInsightClustersKafka": "kafka-",
|
||||
"hdInsightClustersMl": "mls-",
|
||||
"hdInsightClustersSpark": "spark-",
|
||||
"hdInsightClustersStorm": "storm-",
|
||||
"hybridComputeMachines": "arcs-",
|
||||
"insightsActionGroups": "ag-",
|
||||
"insightsComponents": "appi-",
|
||||
"keyVaultVaults": "kv-",
|
||||
"kubernetesConnectedClusters": "arck",
|
||||
"kustoClusters": "dec",
|
||||
"kustoClustersDatabases": "dedb",
|
||||
"logicIntegrationAccounts": "ia-",
|
||||
"logicWorkflows": "logic-",
|
||||
"machineLearningServicesWorkspaces": "mlw-",
|
||||
"managedIdentityUserAssignedIdentities": "id-",
|
||||
"managementManagementGroups": "mg-",
|
||||
"migrateAssessmentProjects": "migr-",
|
||||
"networkApplicationGateways": "agw-",
|
||||
"networkApplicationSecurityGroups": "asg-",
|
||||
"networkAzureFirewalls": "afw-",
|
||||
"networkBastionHosts": "bas-",
|
||||
"networkConnections": "con-",
|
||||
"networkDnsZones": "dnsz-",
|
||||
"networkExpressRouteCircuits": "erc-",
|
||||
"networkFirewallPolicies": "afwp-",
|
||||
"networkFirewallPoliciesWebApplication": "waf",
|
||||
"networkFirewallPoliciesRuleGroups": "wafrg",
|
||||
"networkFrontDoors": "fd-",
|
||||
"networkFrontdoorWebApplicationFirewallPolicies": "fdfp-",
|
||||
"networkLoadBalancersExternal": "lbe-",
|
||||
"networkLoadBalancersInternal": "lbi-",
|
||||
"networkLoadBalancersInboundNatRules": "rule-",
|
||||
"networkLocalNetworkGateways": "lgw-",
|
||||
"networkNatGateways": "ng-",
|
||||
"networkNetworkInterfaces": "nic-",
|
||||
"networkNetworkSecurityGroups": "nsg-",
|
||||
"networkNetworkSecurityGroupsSecurityRules": "nsgsr-",
|
||||
"networkNetworkWatchers": "nw-",
|
||||
"networkPrivateDnsZones": "pdnsz-",
|
||||
"networkPrivateLinkServices": "pl-",
|
||||
"networkPublicIPAddresses": "pip-",
|
||||
"networkPublicIPPrefixes": "ippre-",
|
||||
"networkRouteFilters": "rf-",
|
||||
"networkRouteTables": "rt-",
|
||||
"networkRouteTablesRoutes": "udr-",
|
||||
"networkTrafficManagerProfiles": "traf-",
|
||||
"networkVirtualNetworkGateways": "vgw-",
|
||||
"networkVirtualNetworks": "vnet-",
|
||||
"networkVirtualNetworksSubnets": "snet-",
|
||||
"networkVirtualNetworksVirtualNetworkPeerings": "peer-",
|
||||
"networkVirtualWans": "vwan-",
|
||||
"networkVpnGateways": "vpng-",
|
||||
"networkVpnGatewaysVpnConnections": "vcn-",
|
||||
"networkVpnGatewaysVpnSites": "vst-",
|
||||
"notificationHubsNamespaces": "ntfns-",
|
||||
"notificationHubsNamespacesNotificationHubs": "ntf-",
|
||||
"operationalInsightsWorkspaces": "log-",
|
||||
"portalDashboards": "dash-",
|
||||
"powerBIDedicatedCapacities": "pbi-",
|
||||
"purviewAccounts": "pview-",
|
||||
"recoveryServicesVaults": "rsv-",
|
||||
"resourcesResourceGroups": "rg-",
|
||||
"searchSearchServices": "srch-",
|
||||
"serviceBusNamespaces": "sb-",
|
||||
"serviceBusNamespacesQueues": "sbq-",
|
||||
"serviceBusNamespacesTopics": "sbt-",
|
||||
"serviceEndPointPolicies": "se-",
|
||||
"serviceFabricClusters": "sf-",
|
||||
"signalRServiceSignalR": "sigr",
|
||||
"sqlManagedInstances": "sqlmi-",
|
||||
"sqlServers": "sql-",
|
||||
"sqlServersDataWarehouse": "sqldw-",
|
||||
"sqlServersDatabases": "sqldb-",
|
||||
"sqlServersDatabasesStretch": "sqlstrdb-",
|
||||
"storageStorageAccounts": "st",
|
||||
"storageStorageAccountsVm": "stvm",
|
||||
"storSimpleManagers": "ssimp",
|
||||
"streamAnalyticsCluster": "asa-",
|
||||
"synapseWorkspaces": "syn",
|
||||
"synapseWorkspacesAnalyticsWorkspaces": "synw",
|
||||
"synapseWorkspacesSqlPoolsDedicated": "syndp",
|
||||
"synapseWorkspacesSqlPoolsSpark": "synsp",
|
||||
"timeSeriesInsightsEnvironments": "tsi-",
|
||||
"webServerFarms": "plan-",
|
||||
"webSitesAppService": "app-",
|
||||
"webSitesAppServiceEnvironment": "ase-",
|
||||
"webSitesFunctions": "func-",
|
||||
"webStaticSites": "stapp-"
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
param name string
|
||||
param location string = resourceGroup().location
|
||||
param tags object = {}
|
||||
|
||||
param identityName string
|
||||
param containerRegistryName string
|
||||
param containerAppsEnvironmentName string
|
||||
param applicationInsightsName string
|
||||
param allowedOrigins array
|
||||
param exists bool
|
||||
@secure()
|
||||
param appDefinition object
|
||||
|
||||
var appSettingsArray = filter(array(appDefinition.settings), i => i.name != '')
|
||||
var secrets = map(filter(appSettingsArray, i => i.?secret != null), i => {
|
||||
name: i.name
|
||||
value: i.value
|
||||
secretRef: i.?secretRef ?? take(replace(replace(toLower(i.name), '_', '-'), '.', '-'), 32)
|
||||
})
|
||||
var env = map(filter(appSettingsArray, i => i.?secret == null), i => {
|
||||
name: i.name
|
||||
value: i.value
|
||||
})
|
||||
|
||||
resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
|
||||
name: identityName
|
||||
location: location
|
||||
}
|
||||
|
||||
resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' existing = {
|
||||
name: containerRegistryName
|
||||
}
|
||||
|
||||
resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' existing = {
|
||||
name: containerAppsEnvironmentName
|
||||
}
|
||||
|
||||
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = {
|
||||
name: applicationInsightsName
|
||||
}
|
||||
|
||||
resource acrPullRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
|
||||
scope: containerRegistry
|
||||
name: guid(subscription().id, resourceGroup().id, identity.id, 'acrPullRole')
|
||||
properties: {
|
||||
roleDefinitionId: subscriptionResourceId(
|
||||
'Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
|
||||
principalType: 'ServicePrincipal'
|
||||
principalId: identity.properties.principalId
|
||||
}
|
||||
}
|
||||
|
||||
module fetchLatestImage '../modules/fetch-container-image.bicep' = {
|
||||
name: '${name}-fetch-image'
|
||||
params: {
|
||||
exists: exists
|
||||
name: name
|
||||
}
|
||||
}
|
||||
|
||||
resource app 'Microsoft.App/containerApps@2023-05-02-preview' = {
|
||||
name: name
|
||||
location: location
|
||||
tags: union(tags, {'azd-service-name': 'backend' })
|
||||
dependsOn: [ acrPullRole ]
|
||||
identity: {
|
||||
type: 'UserAssigned'
|
||||
userAssignedIdentities: { '${identity.id}': {} }
|
||||
}
|
||||
properties: {
|
||||
managedEnvironmentId: containerAppsEnvironment.id
|
||||
configuration: {
|
||||
ingress: {
|
||||
external: true
|
||||
targetPort: 5244
|
||||
transport: 'auto'
|
||||
corsPolicy: {
|
||||
allowedOrigins: union(allowedOrigins, [
|
||||
// define additional allowed origins here
|
||||
])
|
||||
}
|
||||
}
|
||||
registries: [
|
||||
{
|
||||
server: '${containerRegistryName}.azurecr.io'
|
||||
identity: identity.id
|
||||
}
|
||||
]
|
||||
secrets: union([
|
||||
],
|
||||
map(secrets, secret => {
|
||||
name: secret.secretRef
|
||||
value: secret.value
|
||||
}))
|
||||
}
|
||||
template: {
|
||||
containers: [
|
||||
{
|
||||
image: fetchLatestImage.outputs.?containers[?0].?image ?? 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
|
||||
name: 'main'
|
||||
env: union([
|
||||
{
|
||||
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
|
||||
value: applicationInsights.properties.ConnectionString
|
||||
}
|
||||
{
|
||||
name: 'PORT'
|
||||
value: '5244'
|
||||
}
|
||||
],
|
||||
env,
|
||||
map(secrets, secret => {
|
||||
name: secret.name
|
||||
secretRef: secret.secretRef
|
||||
}))
|
||||
resources: {
|
||||
cpu: json('1.0')
|
||||
memory: '2.0Gi'
|
||||
}
|
||||
}
|
||||
]
|
||||
scale: {
|
||||
minReplicas: 1
|
||||
maxReplicas: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output defaultDomain string = containerAppsEnvironment.properties.defaultDomain
|
||||
output name string = app.name
|
||||
output uri string = 'https://${app.properties.configuration.ingress.fqdn}'
|
||||
output id string = app.id
|
|
@ -1,132 +0,0 @@
|
|||
param name string
|
||||
param location string = resourceGroup().location
|
||||
param tags object = {}
|
||||
|
||||
param identityName string
|
||||
param containerRegistryName string
|
||||
param containerAppsEnvironmentName string
|
||||
param applicationInsightsName string
|
||||
param apiUrls array
|
||||
param exists bool
|
||||
@secure()
|
||||
param appDefinition object
|
||||
|
||||
var appSettingsArray = filter(array(appDefinition.settings), i => i.name != '')
|
||||
var secrets = map(filter(appSettingsArray, i => i.?secret != null), i => {
|
||||
name: i.name
|
||||
value: i.value
|
||||
secretRef: i.?secretRef ?? take(replace(replace(toLower(i.name), '_', '-'), '.', '-'), 32)
|
||||
})
|
||||
var env = map(filter(appSettingsArray, i => i.?secret == null), i => {
|
||||
name: i.name
|
||||
value: i.value
|
||||
})
|
||||
|
||||
resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
|
||||
name: identityName
|
||||
location: location
|
||||
}
|
||||
|
||||
resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' existing = {
|
||||
name: containerRegistryName
|
||||
}
|
||||
|
||||
resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' existing = {
|
||||
name: containerAppsEnvironmentName
|
||||
}
|
||||
|
||||
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = {
|
||||
name: applicationInsightsName
|
||||
}
|
||||
|
||||
resource acrPullRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
|
||||
scope: containerRegistry
|
||||
name: guid(subscription().id, resourceGroup().id, identity.id, 'acrPullRole')
|
||||
properties: {
|
||||
roleDefinitionId: subscriptionResourceId(
|
||||
'Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
|
||||
principalType: 'ServicePrincipal'
|
||||
principalId: identity.properties.principalId
|
||||
}
|
||||
}
|
||||
|
||||
module fetchLatestImage '../modules/fetch-container-image.bicep' = {
|
||||
name: '${name}-fetch-image'
|
||||
params: {
|
||||
exists: exists
|
||||
name: name
|
||||
}
|
||||
}
|
||||
|
||||
resource app 'Microsoft.App/containerApps@2023-05-02-preview' = {
|
||||
name: name
|
||||
location: location
|
||||
tags: union(tags, {'azd-service-name': 'frontend' })
|
||||
dependsOn: [ acrPullRole ]
|
||||
identity: {
|
||||
type: 'UserAssigned'
|
||||
userAssignedIdentities: { '${identity.id}': {} }
|
||||
}
|
||||
properties: {
|
||||
managedEnvironmentId: containerAppsEnvironment.id
|
||||
configuration: {
|
||||
ingress: {
|
||||
external: true
|
||||
targetPort: 3000
|
||||
transport: 'auto'
|
||||
}
|
||||
registries: [
|
||||
{
|
||||
server: '${containerRegistryName}.azurecr.io'
|
||||
identity: identity.id
|
||||
}
|
||||
]
|
||||
secrets: union([
|
||||
],
|
||||
map(secrets, secret => {
|
||||
name: secret.secretRef
|
||||
value: secret.value
|
||||
}))
|
||||
}
|
||||
template: {
|
||||
containers: [
|
||||
{
|
||||
image: fetchLatestImage.outputs.?containers[?0].?image ?? 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
|
||||
name: 'main'
|
||||
env: union([
|
||||
{
|
||||
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
|
||||
value: applicationInsights.properties.ConnectionString
|
||||
}
|
||||
{
|
||||
name: 'BACKEND_BASE_URL'
|
||||
value: apiUrls[0]
|
||||
}
|
||||
{
|
||||
name: 'PORT'
|
||||
value: '3000'
|
||||
}
|
||||
],
|
||||
env,
|
||||
map(secrets, secret => {
|
||||
name: secret.name
|
||||
secretRef: secret.secretRef
|
||||
}))
|
||||
resources: {
|
||||
cpu: json('1.0')
|
||||
memory: '2.0Gi'
|
||||
}
|
||||
}
|
||||
]
|
||||
scale: {
|
||||
minReplicas: 1
|
||||
maxReplicas: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output defaultDomain string = containerAppsEnvironment.properties.defaultDomain
|
||||
output name string = app.name
|
||||
output uri string = 'https://${app.properties.configuration.ingress.fqdn}'
|
||||
output id string = app.id
|
|
@ -1,135 +0,0 @@
|
|||
targetScope = 'subscription'
|
||||
|
||||
@minLength(1)
|
||||
@maxLength(64)
|
||||
@description('Name of the environment that can be used as part of naming resource convention')
|
||||
param environmentName string
|
||||
|
||||
@minLength(1)
|
||||
@description('Primary location for all resources')
|
||||
param location string
|
||||
|
||||
param backendExists bool
|
||||
@secure()
|
||||
param backendDefinition object
|
||||
param frontendExists bool
|
||||
@secure()
|
||||
param frontendDefinition object
|
||||
|
||||
@description('Id of the user or app to assign application roles')
|
||||
param principalId string
|
||||
|
||||
// Tags that should be applied to all resources.
|
||||
//
|
||||
// Note that 'azd-service-name' tags should be applied separately to service host resources.
|
||||
// Example usage:
|
||||
// tags: union(tags, { 'azd-service-name': <service name in azure.yaml> })
|
||||
var tags = {
|
||||
'azd-env-name': environmentName
|
||||
}
|
||||
|
||||
var abbrs = loadJsonContent('./abbreviations.json')
|
||||
var resourceToken = toLower(uniqueString(subscription().id, environmentName, location))
|
||||
|
||||
resource rg 'Microsoft.Resources/resourceGroups@2022-09-01' = {
|
||||
name: 'rg-${environmentName}'
|
||||
location: location
|
||||
tags: tags
|
||||
}
|
||||
|
||||
module monitoring './shared/monitoring.bicep' = {
|
||||
name: 'monitoring'
|
||||
params: {
|
||||
location: location
|
||||
tags: tags
|
||||
logAnalyticsName: '${abbrs.operationalInsightsWorkspaces}${resourceToken}'
|
||||
applicationInsightsName: '${abbrs.insightsComponents}${resourceToken}'
|
||||
}
|
||||
scope: rg
|
||||
}
|
||||
|
||||
module dashboard './shared/dashboard-web.bicep' = {
|
||||
name: 'dashboard'
|
||||
params: {
|
||||
name: '${abbrs.portalDashboards}${resourceToken}'
|
||||
applicationInsightsName: monitoring.outputs.applicationInsightsName
|
||||
location: location
|
||||
tags: tags
|
||||
}
|
||||
scope: rg
|
||||
}
|
||||
|
||||
module registry './shared/registry.bicep' = {
|
||||
name: 'registry'
|
||||
params: {
|
||||
location: location
|
||||
tags: tags
|
||||
name: '${abbrs.containerRegistryRegistries}${resourceToken}'
|
||||
}
|
||||
scope: rg
|
||||
}
|
||||
|
||||
module keyVault './shared/keyvault.bicep' = {
|
||||
name: 'keyvault'
|
||||
params: {
|
||||
location: location
|
||||
tags: tags
|
||||
name: '${abbrs.keyVaultVaults}${resourceToken}'
|
||||
principalId: principalId
|
||||
}
|
||||
scope: rg
|
||||
}
|
||||
|
||||
module appsEnv './shared/apps-env.bicep' = {
|
||||
name: 'apps-env'
|
||||
params: {
|
||||
name: '${abbrs.appManagedEnvironments}${resourceToken}'
|
||||
location: location
|
||||
tags: tags
|
||||
applicationInsightsName: monitoring.outputs.applicationInsightsName
|
||||
logAnalyticsWorkspaceName: monitoring.outputs.logAnalyticsWorkspaceName
|
||||
}
|
||||
scope: rg
|
||||
}
|
||||
|
||||
module backend './app/backend.bicep' = {
|
||||
name: 'backend'
|
||||
params: {
|
||||
name: '${abbrs.appContainerApps}backend-${resourceToken}'
|
||||
location: location
|
||||
tags: tags
|
||||
identityName: '${abbrs.managedIdentityUserAssignedIdentities}backend-${resourceToken}'
|
||||
applicationInsightsName: monitoring.outputs.applicationInsightsName
|
||||
containerAppsEnvironmentName: appsEnv.outputs.name
|
||||
containerRegistryName: registry.outputs.name
|
||||
exists: backendExists
|
||||
appDefinition: backendDefinition
|
||||
allowedOrigins: [
|
||||
'https://${abbrs.appContainerApps}frontend-${resourceToken}.${appsEnv.outputs.domain}'
|
||||
]
|
||||
}
|
||||
scope: rg
|
||||
}
|
||||
|
||||
module frontend './app/frontend.bicep' = {
|
||||
name: 'frontend'
|
||||
params: {
|
||||
name: '${abbrs.appContainerApps}frontend-${resourceToken}'
|
||||
location: location
|
||||
tags: tags
|
||||
identityName: '${abbrs.managedIdentityUserAssignedIdentities}frontend-${resourceToken}'
|
||||
applicationInsightsName: monitoring.outputs.applicationInsightsName
|
||||
containerAppsEnvironmentName: appsEnv.outputs.name
|
||||
containerRegistryName: registry.outputs.name
|
||||
exists: frontendExists
|
||||
appDefinition: frontendDefinition
|
||||
apiUrls: [
|
||||
backend.outputs.uri
|
||||
]
|
||||
}
|
||||
scope: rg
|
||||
}
|
||||
|
||||
output AZURE_CONTAINER_REGISTRY_ENDPOINT string = registry.outputs.loginServer
|
||||
output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name
|
||||
output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint
|
|
@ -1,59 +0,0 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"environmentName": {
|
||||
"value": "${AZURE_ENV_NAME}"
|
||||
},
|
||||
"location": {
|
||||
"value": "${AZURE_LOCATION}"
|
||||
},
|
||||
"backendExists": {
|
||||
"value": "${SERVICE_BACKEND_RESOURCE_EXISTS=false}"
|
||||
},
|
||||
"backendDefinition": {
|
||||
"value": {
|
||||
"settings": [
|
||||
{
|
||||
"name": "",
|
||||
"value": "${VAR}",
|
||||
"_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.",
|
||||
"_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR} to use the value of 'VAR' from the current environment."
|
||||
},
|
||||
{
|
||||
"name": "",
|
||||
"value": "${VAR_S}",
|
||||
"secret": true,
|
||||
"_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.",
|
||||
"_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR_S} to use the value of 'VAR_S' from the current environment."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"frontendExists": {
|
||||
"value": "${SERVICE_FRONTEND_RESOURCE_EXISTS=false}"
|
||||
},
|
||||
"frontendDefinition": {
|
||||
"value": {
|
||||
"settings": [
|
||||
{
|
||||
"name": "",
|
||||
"value": "${VAR}",
|
||||
"_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.",
|
||||
"_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR} to use the value of 'VAR' from the current environment."
|
||||
},
|
||||
{
|
||||
"name": "",
|
||||
"value": "${VAR_S}",
|
||||
"secret": true,
|
||||
"_comment_name": "The name of the environment variable when running in Azure. If empty, ignored.",
|
||||
"_comment_value": "The value to provide. This can be a fixed literal, or an expression like ${VAR_S} to use the value of 'VAR_S' from the current environment."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"principalId": {
|
||||
"value": "${AZURE_PRINCIPAL_ID}"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
param exists bool
|
||||
param name string
|
||||
|
||||
resource existingApp 'Microsoft.App/containerApps@2023-05-02-preview' existing = if (exists) {
|
||||
name: name
|
||||
}
|
||||
|
||||
output containers array = exists ? existingApp.properties.template.containers : []
|
|
@ -1,33 +0,0 @@
|
|||
param name string
|
||||
param location string = resourceGroup().location
|
||||
param tags object = {}
|
||||
|
||||
param logAnalyticsWorkspaceName string
|
||||
param applicationInsightsName string = ''
|
||||
|
||||
resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-10-01' = {
|
||||
name: name
|
||||
location: location
|
||||
tags: tags
|
||||
properties: {
|
||||
appLogsConfiguration: {
|
||||
destination: 'log-analytics'
|
||||
logAnalyticsConfiguration: {
|
||||
customerId: logAnalyticsWorkspace.properties.customerId
|
||||
sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
|
||||
}
|
||||
}
|
||||
daprAIConnectionString: applicationInsights.properties.ConnectionString
|
||||
}
|
||||
}
|
||||
|
||||
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
|
||||
name: logAnalyticsWorkspaceName
|
||||
}
|
||||
|
||||
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = {
|
||||
name: applicationInsightsName
|
||||
}
|
||||
|
||||
output name string = containerAppsEnvironment.name
|
||||
output domain string = containerAppsEnvironment.properties.defaultDomain
|
|
@ -1,31 +0,0 @@
|
|||
param name string
|
||||
param location string = resourceGroup().location
|
||||
param tags object = {}
|
||||
|
||||
@description('Service principal that should be granted read access to the KeyVault. If unset, no service principal is granted access by default')
|
||||
param principalId string = ''
|
||||
|
||||
var defaultAccessPolicies = !empty(principalId) ? [
|
||||
{
|
||||
objectId: principalId
|
||||
permissions: { secrets: [ 'get', 'list' ] }
|
||||
tenantId: subscription().tenantId
|
||||
}
|
||||
] : []
|
||||
|
||||
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
|
||||
name: name
|
||||
location: location
|
||||
tags: tags
|
||||
properties: {
|
||||
tenantId: subscription().tenantId
|
||||
sku: { family: 'A', name: 'standard' }
|
||||
enabledForTemplateDeployment: true
|
||||
accessPolicies: union(defaultAccessPolicies, [
|
||||
// define access policies here
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
output endpoint string = keyVault.properties.vaultUri
|
||||
output name string = keyVault.name
|
|
@ -1,34 +0,0 @@
|
|||
param logAnalyticsName string
|
||||
param applicationInsightsName string
|
||||
param location string = resourceGroup().location
|
||||
param tags object = {}
|
||||
|
||||
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = {
|
||||
name: logAnalyticsName
|
||||
location: location
|
||||
tags: tags
|
||||
properties: any({
|
||||
retentionInDays: 30
|
||||
features: {
|
||||
searchVersion: 1
|
||||
}
|
||||
sku: {
|
||||
name: 'PerGB2018'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
|
||||
name: applicationInsightsName
|
||||
location: location
|
||||
tags: tags
|
||||
kind: 'web'
|
||||
properties: {
|
||||
Application_Type: 'web'
|
||||
WorkspaceResourceId: logAnalytics.id
|
||||
}
|
||||
}
|
||||
|
||||
output applicationInsightsName string = applicationInsights.name
|
||||
output logAnalyticsWorkspaceId string = logAnalytics.id
|
||||
output logAnalyticsWorkspaceName string = logAnalytics.name
|
|
@ -1,36 +0,0 @@
|
|||
param name string
|
||||
param location string = resourceGroup().location
|
||||
param tags object = {}
|
||||
|
||||
param adminUserEnabled bool = true
|
||||
param anonymousPullEnabled bool = false
|
||||
param dataEndpointEnabled bool = false
|
||||
param encryption object = {
|
||||
status: 'disabled'
|
||||
}
|
||||
param networkRuleBypassOptions string = 'AzureServices'
|
||||
param publicNetworkAccess string = 'Enabled'
|
||||
param sku object = {
|
||||
name: 'Standard'
|
||||
}
|
||||
param zoneRedundancy string = 'Disabled'
|
||||
|
||||
// 2023-01-01-preview needed for anonymousPullEnabled
|
||||
resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' = {
|
||||
name: name
|
||||
location: location
|
||||
tags: tags
|
||||
sku: sku
|
||||
properties: {
|
||||
adminUserEnabled: adminUserEnabled
|
||||
anonymousPullEnabled: anonymousPullEnabled
|
||||
dataEndpointEnabled: dataEndpointEnabled
|
||||
encryption: encryption
|
||||
networkRuleBypassOptions: networkRuleBypassOptions
|
||||
publicNetworkAccess: publicNetworkAccess
|
||||
zoneRedundancy: zoneRedundancy
|
||||
}
|
||||
}
|
||||
|
||||
output loginServer string = containerRegistry.properties.loginServer
|
||||
output name string = containerRegistry.name
|
|
@ -1,96 +0,0 @@
|
|||
# Next Steps after `azd init`
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Next Steps](#next-steps)
|
||||
2. [What was added](#what-was-added)
|
||||
3. [Billing](#billing)
|
||||
4. [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Next Steps
|
||||
|
||||
### Define environment variables for running services
|
||||
|
||||
1. Modify or add environment variables to configure the running application. Environment variables can be configured by updating the `settings` node(s) for each service in [main.parameters.json](./infra/main.parameters.json).
|
||||
2. For services using a database, environment variables have been pre-configured under the `env` node in the following files to allow connection to the database. Modify the name of these variables as needed to match your application.
|
||||
- [app/backend.bicep](./infra/app/backend.bicep)
|
||||
- [app/frontend.bicep](./infra/app/frontend.bicep)
|
||||
3. For services using Redis, environment variables will not show up under `env` explicitly, but are available as: `REDIS_ENDPOINT`, `REDIS_HOST`, `REDIS_PASSWORD`, and `REDIS_PORT`.
|
||||
|
||||
### Provision infrastructure and deploy application code
|
||||
|
||||
Run `azd up` to provision your infrastructure and deploy to Azure in one step (or run `azd provision` then `azd deploy` to accomplish the tasks separately). Visit the service endpoints listed to see your application up-and-running!
|
||||
|
||||
To troubleshoot any issues, see [troubleshooting](#troubleshooting).
|
||||
|
||||
### Configure CI/CD pipeline
|
||||
|
||||
1. Create a workflow pipeline file locally. The following starters are available:
|
||||
- [Deploy with GitHub Actions](https://github.com/Azure-Samples/azd-starter-bicep/blob/main/.github/workflows/azure-dev.yml)
|
||||
- [Deploy with Azure Pipelines](https://github.com/Azure-Samples/azd-starter-bicep/blob/main/.azdo/pipelines/azure-dev.yml)
|
||||
2. Run `azd pipeline config -e <environment name>` to configure the deployment pipeline to connect securely to Azure. An environment name is specified here to configure the pipeline with a different environment for isolation purposes. Run `azd env list` and `azd env set` to reselect the default environment after this step.
|
||||
|
||||
## What was added
|
||||
|
||||
### Infrastructure configuration
|
||||
|
||||
To describe the infrastructure and application, `azure.yaml` along with Infrastructure as Code files using Bicep were added with the following directory structure:
|
||||
|
||||
```yaml
|
||||
- azure.yaml # azd project configuration
|
||||
- infra/ # Infrastructure as Code (bicep) files
|
||||
- main.bicep # main deployment module
|
||||
- app/ # Application resource modules
|
||||
- shared/ # Shared resource modules
|
||||
- modules/ # Library modules
|
||||
```
|
||||
|
||||
Each bicep file declares resources to be provisioned. The resources are provisioned when running `azd up` or `azd provision`.
|
||||
|
||||
- [app/backend.bicep](./infra/app/backend.bicep) - Azure Container Apps resources to host the 'backend' service.
|
||||
- [app/frontend.bicep](./infra/app/frontend.bicep) - Azure Container Apps resources to host the 'frontend' service.
|
||||
- [shared/keyvault.bicep](./infra/shared/keyvault.bicep) - Azure KeyVault to store secrets.
|
||||
- [shared/monitoring.bicep](./infra/shared/monitoring.bicep) - Azure Log Analytics workspace and Application Insights to log and store instrumentation logs.
|
||||
- [shared/registry.bicep](./infra/shared/registry.bicep) - Azure Container Registry to store docker images.
|
||||
|
||||
More information about [Bicep](https://aka.ms/bicep) language.
|
||||
|
||||
### Build from source (no Dockerfile)
|
||||
|
||||
#### Build with Buildpacks using Oryx
|
||||
|
||||
If your project does not contain a Dockerfile, we will use [Buildpacks](https://buildpacks.io/) using [Oryx](https://github.com/microsoft/Oryx/blob/main/doc/README.md) to create an image for the services in `azure.yaml` and get your containerized app onto Azure.
|
||||
|
||||
To produce and run the docker image locally:
|
||||
|
||||
1. Run `azd package` to build the image.
|
||||
2. Copy the *Image Tag* shown.
|
||||
3. Run `docker run -it <Image Tag>` to run the image locally.
|
||||
|
||||
#### Exposed port
|
||||
|
||||
Oryx will automatically set `PORT` to a default value of `80`. Additionally, it will auto-configure supported web servers such as `gunicorn` and `ASP .NET Core` to listen to the target `PORT`. If your application already listens to the port specified by the `PORT` variable, the application will work out-of-the-box. Otherwise, you may need to perform one of the steps below:
|
||||
|
||||
1. Update your application code or configuration to listen to the port specified by the `PORT` variable
|
||||
1. (Alternatively) Search for `targetPort` in a .bicep file under the `infra/app` folder, and update the variable to match the port used by the application.
|
||||
|
||||
## Billing
|
||||
|
||||
Visit the *Cost Management + Billing* page in Azure Portal to track current spend. For more information about how you're billed, and how you can monitor the costs incurred in your Azure subscriptions, visit [billing overview](https://learn.microsoft.com/azure/developer/intro/azure-developer-billing).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Q: I visited the service endpoint listed, and I'm seeing a blank or error page.
|
||||
|
||||
A: Your service may have failed to start or misconfigured. To investigate further:
|
||||
|
||||
1. Click on the resource group link shown to visit Azure Portal.
|
||||
2. Navigate to the specific Azure Container App resource for the service.
|
||||
3. Select *Monitoring -> Log stream* under the navigation pane.
|
||||
4. Observe the log output to identify any errors.
|
||||
5. If there are no errors, ensure that the ingress target port matches the port that your service listens on:
|
||||
1. Under *Settings -> Ingress*, ensure the *Target port* matches the desired port.
|
||||
2. After modifying this setting, also update the `targetPort` setting in the .bicep file for the service under `infra/app`.
|
||||
6. If logs are written to disk, examine the local logs or debug the application by using the *Console* to connect to a shell within the running container.
|
||||
|
||||
For additional information about setting up your `azd` project, visit our official [docs](https://learn.microsoft.com/azure/developer/azure-developer-cli/make-azd-compatible?pivots=azd-convert).
|
Before Width: | Height: | Size: 905 KiB |
Before Width: | Height: | Size: 2.4 MiB |
|
@ -1,13 +0,0 @@
|
|||
if(Test-Path src/backend/appsettings.json) {
|
||||
Write-Verbose "appsettings.json already exists" -Verbose
|
||||
} else {
|
||||
Copy-Item src/backend/appsettings.local.template.json src/backend/appsettings.json
|
||||
Write-Verbose "appsettings.json created" -Verbose
|
||||
}
|
||||
|
||||
if((Get-Content .\src\backend\appsettings.local.template.json -Raw | Select-String "<mandatory>") -ne $null) {
|
||||
Write-Error "Please update the appsettings.json file with the correct values" -ErrorAction Stop
|
||||
}
|
||||
|
||||
$backendProc = Start-Process powershell -ArgumentList '-NoExit', '-Command cd src/backend/; dotnet run'
|
||||
$frontendProc = Start-Process powershell -ArgumentList '-NoExit', '-Command cd src/frontend/; npm run dev'
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ -f src/backend/appsettings.json ]; then
|
||||
echo "appsettings.json already exists"
|
||||
else
|
||||
cp src/backend/appsettings.local.template.json src/backend/appsettings.json
|
||||
echo "appsettings.json created"
|
||||
fi
|
||||
|
||||
if grep -q "<mandatory>" src/backend/appsettings.local.template.json; then
|
||||
echo "Please update the appsettings.json file with the correct values" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
gnome-terminal -- bash -c "cd src/backend/ && dotnet run; exec bash"
|
||||
gnome-terminal -- bash -c "cd src/frontend/ && npm run dev; exec bash"
|
|
@ -1,485 +0,0 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from `dotnet new gitignore`
|
||||
|
||||
# dotenv files
|
||||
.env
|
||||
appsettings.json
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# Tye
|
||||
.tye/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.tlog
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||
*.vbp
|
||||
|
||||
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||
*.dsw
|
||||
*.dsp
|
||||
|
||||
# Visual Studio 6 technical files
|
||||
*.ncb
|
||||
*.aps
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# Visual Studio History (VSHistory) files
|
||||
.vshistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
# VS Code files for those working on multiple tools
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Windows Installer files from build outputs
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
.idea
|
||||
|
||||
##
|
||||
## Visual studio for Mac
|
||||
##
|
||||
|
||||
|
||||
# globs
|
||||
Makefile.in
|
||||
*.userprefs
|
||||
*.usertasks
|
||||
config.make
|
||||
config.status
|
||||
aclocal.m4
|
||||
install-sh
|
||||
autom4te.cache/
|
||||
*.tar.gz
|
||||
tarballs/
|
||||
test-results/
|
||||
|
||||
# Mac bundle stuff
|
||||
*.dmg
|
||||
*.app
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# Vim temporary swap files
|
||||
*.swp
|
|
@ -1,79 +0,0 @@
|
|||
using Marketing.Events;
|
||||
using Marketing.Options;
|
||||
using Microsoft.AI.Agents.Abstractions;
|
||||
using Microsoft.AI.Agents.Orleans;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Memory;
|
||||
using Orleans.Runtime;
|
||||
|
||||
namespace Marketing.Agents;
|
||||
|
||||
[ImplicitStreamSubscription(Consts.OrleansNamespace)]
|
||||
public class CommunityManager : AiAgent<CommunityManagerState>
|
||||
{
|
||||
protected override string Namespace => Consts.OrleansNamespace;
|
||||
|
||||
private readonly ILogger<GraphicDesigner> _logger;
|
||||
|
||||
public CommunityManager([PersistentState("state", "messages")] IPersistentState<AgentState<CommunityManagerState>> state, Kernel kernel, ISemanticTextMemory memory, ILogger<GraphicDesigner> logger)
|
||||
: base(state, memory, kernel)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task HandleEvent(Event item)
|
||||
{
|
||||
if (item?.Type is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
}
|
||||
|
||||
switch (item.Type)
|
||||
{
|
||||
case nameof(EventTypes.UserConnected):
|
||||
// The user reconnected, let's send the last message if we have one
|
||||
var lastMessage = _state.State.History.LastOrDefault()?.Message;
|
||||
if (lastMessage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await SendDesignedCreatedEvent(lastMessage, item.Data["UserId"]);
|
||||
break;
|
||||
|
||||
case nameof(EventTypes.ArticleCreated):
|
||||
{
|
||||
var article = item.Data["article"];
|
||||
|
||||
_logger.LogInformation($"[{nameof(GraphicDesigner)}] Event {nameof(EventTypes.ArticleCreated)}. Article: {{Article}}", article);
|
||||
|
||||
var context = new KernelArguments { ["input"] = AppendChatHistory(article) };
|
||||
string socialMediaPost = await CallFunction(CommunityManagerPrompts.WritePost, context);
|
||||
_state.State.Data.WrittenSocialMediaPost = socialMediaPost;
|
||||
await SendDesignedCreatedEvent(socialMediaPost, item.Data["UserId"]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendDesignedCreatedEvent(string socialMediaPost, string userId)
|
||||
{
|
||||
await PublishEvent(new Event
|
||||
{
|
||||
Namespace = this.GetPrimaryKeyString(),
|
||||
Type = nameof(EventTypes.SocialMediaPostCreated),
|
||||
Data = new Dictionary<string, string>
|
||||
{
|
||||
["UserId"] = userId,
|
||||
[nameof(socialMediaPost)] = socialMediaPost,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Task<string> GetArticle()
|
||||
{
|
||||
return Task.FromResult(_state.State.Data.WrittenSocialMediaPost);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
namespace Marketing.Agents;
|
||||
|
||||
public static class CommunityManagerPrompts
|
||||
{
|
||||
public const string WritePost = """
|
||||
You are a Marketing community manager writer.
|
||||
Write a tweet to promote what it is described bellow.
|
||||
The tweet cannot be longer than 280 characters
|
||||
Input: {{$input}}
|
||||
""";
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
namespace Marketing.Agents;
|
||||
|
||||
[GenerateSerializer]
|
||||
public class CommunityManagerState
|
||||
{
|
||||
[Id(0)]
|
||||
public string WrittenSocialMediaPost { get; set; } = "";
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
namespace Marketing.Agents;
|
||||
|
||||
[GenerateSerializer]
|
||||
public class GraphicDesignerState
|
||||
{
|
||||
[Id(0)]
|
||||
public string ImageUrl { get; set; } = "";
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
using Marketing.Events;
|
||||
using Marketing.Options;
|
||||
using Microsoft.AI.Agents.Abstractions;
|
||||
using Microsoft.AI.Agents.Orleans;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Memory;
|
||||
using Microsoft.SemanticKernel.TextToImage;
|
||||
using Orleans.Runtime;
|
||||
|
||||
namespace Marketing.Agents;
|
||||
|
||||
[ImplicitStreamSubscription(Consts.OrleansNamespace)]
|
||||
public class GraphicDesigner : AiAgent<GraphicDesignerState>
|
||||
{
|
||||
protected override string Namespace => Consts.OrleansNamespace;
|
||||
|
||||
private readonly ILogger<GraphicDesigner> _logger;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public GraphicDesigner([PersistentState("state", "messages")] IPersistentState<AgentState<GraphicDesignerState>> state, Kernel kernel, ISemanticTextMemory memory, ILogger<GraphicDesigner> logger, IConfiguration configuration)
|
||||
: base(state, memory, kernel)
|
||||
{
|
||||
_logger = logger;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public override async Task HandleEvent(Event item)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(item);
|
||||
|
||||
switch (item.Type)
|
||||
{
|
||||
case nameof(EventTypes.UserConnected):
|
||||
// The user reconnected, let's send the last message if we have one
|
||||
var lastMessage = _state.State.History.LastOrDefault()?.Message;
|
||||
if (lastMessage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await SendDesignedCreatedEvent(lastMessage, item.Data["UserId"]);
|
||||
|
||||
break;
|
||||
case nameof(EventTypes.ArticleCreated):
|
||||
//TODO
|
||||
_logger.LogInformation($"[{nameof(GraphicDesigner)}] Event {nameof(EventTypes.ArticleCreated)}.");
|
||||
var article = item.Data["article"];
|
||||
var dallEService = _kernel.GetRequiredService<ITextToImageService>();
|
||||
var imageUri = await dallEService.GenerateImageAsync(article, 1024, 1024);
|
||||
|
||||
_state.State.Data.ImageUrl = imageUri;
|
||||
|
||||
await SendDesignedCreatedEvent(imageUri, item.Data["UserId"]);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendDesignedCreatedEvent(string imageUri, string userId)
|
||||
{
|
||||
await PublishEvent(new Event
|
||||
{
|
||||
Namespace = this.GetPrimaryKeyString(),
|
||||
Type = nameof(EventTypes.GraphicDesignCreated),
|
||||
Data = new Dictionary<string, string> {
|
||||
{ "UserId", userId },
|
||||
{ nameof(imageUri), imageUri}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
|
||||
namespace Marketing.Agents;
|
||||
public static class GraphicDesignerPrompts
|
||||
{
|
||||
public const string GenerateImage = """
|
||||
You are a Marketing community manager graphic designer.
|
||||
Bellow is a campaign that you need to create a image for.
|
||||
Create an image of maximum 500x500 pixels that could be use in social medias as a marketing image.
|
||||
Input: {{$input}}
|
||||
""";
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
using Marketing.Events;
|
||||
using Marketing.Options;
|
||||
using Marketing.SignalRHub;
|
||||
using Microsoft.AI.Agents.Abstractions;
|
||||
using Microsoft.AI.Agents.Orleans;
|
||||
|
||||
namespace Marketing.Agents;
|
||||
|
||||
[ImplicitStreamSubscription(Consts.OrleansNamespace)]
|
||||
public class SignalRAgent : Agent
|
||||
{
|
||||
protected override string Namespace => Consts.OrleansNamespace;
|
||||
|
||||
private readonly ILogger<SignalRAgent> _logger;
|
||||
private readonly ISignalRService _signalRClient;
|
||||
|
||||
public SignalRAgent(ILogger<SignalRAgent> logger, ISignalRService signalRClient)
|
||||
{
|
||||
_logger = logger;
|
||||
_signalRClient = signalRClient;
|
||||
}
|
||||
|
||||
public override async Task HandleEvent(Event item)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(item);
|
||||
|
||||
switch (item.Type)
|
||||
{
|
||||
case nameof(EventTypes.ArticleCreated):
|
||||
var writtenArticle = item.Data["article"];
|
||||
await _signalRClient.SendMessageToSpecificClient(item.Data["UserId"], writtenArticle, AgentTypes.Chat);
|
||||
break;
|
||||
|
||||
case nameof(EventTypes.GraphicDesignCreated):
|
||||
var imageUrl = item.Data["imageUri"];
|
||||
await _signalRClient.SendMessageToSpecificClient(item.Data["UserId"], imageUrl, AgentTypes.GraphicDesigner);
|
||||
break;
|
||||
|
||||
case nameof(EventTypes.SocialMediaPostCreated):
|
||||
var post = item.Data["socialMediaPost"];
|
||||
await _signalRClient.SendMessageToSpecificClient(item.Data["UserId"], post, AgentTypes.CommunityManager);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
namespace Marketing.Agents;
|
||||
public interface IWriter : IGrainWithStringKey
|
||||
{
|
||||
Task<string> GetArticle();
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
using Marketing.Events;
|
||||
using Marketing.Options;
|
||||
using Microsoft.AI.Agents.Abstractions;
|
||||
using Microsoft.AI.Agents.Orleans;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Memory;
|
||||
using Orleans.Runtime;
|
||||
|
||||
namespace Marketing.Agents;
|
||||
|
||||
[ImplicitStreamSubscription(Consts.OrleansNamespace)]
|
||||
public class Writer : AiAgent<WriterState>, IWriter
|
||||
{
|
||||
protected override string Namespace => Consts.OrleansNamespace;
|
||||
|
||||
private readonly ILogger<GraphicDesigner> _logger;
|
||||
|
||||
public Writer([PersistentState("state", "messages")] IPersistentState<AgentState<WriterState>> state, Kernel kernel, ISemanticTextMemory memory, ILogger<GraphicDesigner> logger)
|
||||
: base(state, memory, kernel)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task HandleEvent(Event item)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(item);
|
||||
switch (item.Type)
|
||||
{
|
||||
case nameof(EventTypes.UserConnected):
|
||||
// The user reconnected, let's send the last message if we have one
|
||||
var lastMessage = _state.State.History.LastOrDefault()?.Message;
|
||||
if (lastMessage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await SendDesignedCreatedEvent(lastMessage, item.Data["UserId"]);
|
||||
|
||||
break;
|
||||
|
||||
case nameof(EventTypes.UserChatInput):
|
||||
{
|
||||
var userMessage = item.Data["userMessage"];
|
||||
_logger.LogInformation($"[{nameof(GraphicDesigner)}] Event {nameof(EventTypes.UserChatInput)}. UserMessage: {{UserMessage}}", userMessage);
|
||||
|
||||
var context = new KernelArguments { ["input"] = AppendChatHistory(userMessage) };
|
||||
string newArticle = await CallFunction(WriterPrompts.Write, context);
|
||||
|
||||
await SendDesignedCreatedEvent(newArticle, item.Data["UserId"]);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendDesignedCreatedEvent(string article, string userId)
|
||||
{
|
||||
await PublishEvent(new Event
|
||||
{
|
||||
Namespace = this.GetPrimaryKeyString(),
|
||||
Type = nameof(EventTypes.ArticleCreated),
|
||||
Data = new Dictionary<string, string> {
|
||||
{ "UserId", userId },
|
||||
{ nameof(article), article },
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Task<string> GetArticle()
|
||||
{
|
||||
return Task.FromResult(_state.State.Data.WrittenArticle!);
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
|
||||
namespace Marketing.Agents;
|
||||
public static class WriterPrompts
|
||||
{
|
||||
public const string Write = """
|
||||
You are a Marketing writer.
|
||||
Write up to three paragraphs for a campaign to promote what it is described bellow.
|
||||
Bellow are a series of inputs from the user that you can use to create the campaign.
|
||||
If the input talks about twitter or images, dismiss it and return the same as before.
|
||||
Input: {{$input}}
|
||||
""";
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
namespace Marketing.Agents;
|
||||
|
||||
[GenerateSerializer]
|
||||
public class WriterState
|
||||
{
|
||||
[Id(0)]
|
||||
public string? WrittenArticle { get; set; }
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
using Marketing.Agents;
|
||||
using Marketing.Events;
|
||||
using Marketing.Options;
|
||||
using Microsoft.AI.Agents.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Orleans.Runtime;
|
||||
|
||||
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
||||
|
||||
namespace Marketing.Controller;
|
||||
|
||||
[GenerateSerializer]
|
||||
public class Asd
|
||||
{
|
||||
[Id(0)]
|
||||
public required string Name { get; set; }
|
||||
}
|
||||
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class Articles : ControllerBase
|
||||
{
|
||||
private readonly IClusterClient _client;
|
||||
|
||||
public Articles(IClusterClient client)
|
||||
{
|
||||
_client = client;
|
||||
}
|
||||
|
||||
// GET api/<Post>/5
|
||||
[HttpGet("{id}")]
|
||||
public async Task<string> Get(string id)
|
||||
{
|
||||
var grain = _client.GetGrain<IWriter>(id);
|
||||
string article = await grain.GetArticle();
|
||||
return article;
|
||||
}
|
||||
|
||||
// PUT api/<Post>/5
|
||||
[HttpPut("{UserId}")]
|
||||
public async Task<string> Put(string UserId, [FromBody] string userMessage)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(UserId);
|
||||
var streamProvider = _client.GetStreamProvider("StreamProvider");
|
||||
var streamId = StreamId.Create(Consts.OrleansNamespace, UserId);
|
||||
var stream = streamProvider.GetStream<Event>(streamId);
|
||||
|
||||
var data = new Dictionary<string, string>
|
||||
{
|
||||
{ nameof(UserId), UserId.ToString() },
|
||||
{ nameof(userMessage), userMessage },
|
||||
};
|
||||
|
||||
await stream.OnNextAsync(new Event
|
||||
{
|
||||
Namespace = UserId,
|
||||
Type = nameof(EventTypes.UserChatInput),
|
||||
Data = data
|
||||
});
|
||||
|
||||
return $"Task {UserId} accepted";
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 5274
|
||||
EXPOSE 11111
|
||||
EXPOSE 30000
|
||||
|
||||
ENV ASPNETCORE_URLS=http://+:5274
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
ARG configuration=Release
|
||||
COPY . .
|
||||
RUN dotnet restore "samples/marketing/src/backend/Marketing.csproj"
|
||||
WORKDIR "samples/marketing/src/backend"
|
||||
RUN dotnet build "Marketing.csproj" -c $configuration -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
ARG configuration=Release
|
||||
RUN dotnet publish "Marketing.csproj" -c $configuration -o /app/publish
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "Marketing.dll"]
|
|
@ -1,10 +0,0 @@
|
|||
namespace Marketing.Events;
|
||||
|
||||
public enum EventTypes
|
||||
{
|
||||
UserChatInput,
|
||||
ArticleCreated,
|
||||
UserConnected,
|
||||
GraphicDesignCreated,
|
||||
SocialMediaPostCreated,
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Services\**" />
|
||||
<Content Remove="Services\**" />
|
||||
<EmbeddedResource Remove="Services\**" />
|
||||
<None Remove="Services\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
|
||||
<UserSecretsId>c073c86e-8483-4956-942f-331fd09172d4</UserSecretsId>
|
||||
<AnalysisMode>All</AnalysisMode>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Octokit.Webhooks.AspNetCore" />
|
||||
<PackageReference Include="Octokit" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Qdrant" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Plugins.Memory" />
|
||||
<PackageReference Include="Microsoft.Orleans.Server" />
|
||||
<PackageReference Include="Microsoft.Orleans.Sdk" />
|
||||
<PackageReference Include="Microsoft.Orleans.Runtime" />
|
||||
<PackageReference Include="Microsoft.Orleans.Persistence.Cosmos" />
|
||||
<PackageReference Include="Microsoft.Orleans.Clustering.Cosmos" />
|
||||
<PackageReference Include="Microsoft.Orleans.Reminders.Cosmos" />
|
||||
<PackageReference Include="Microsoft.Orleans.Streaming.EventHubs" />
|
||||
<PackageReference Include="Microsoft.Orleans.Reminders" />
|
||||
<PackageReference Include="Microsoft.Orleans.Streaming" />
|
||||
<PackageReference Include="OrleansDashboard" />
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" />
|
||||
<PackageReference Include="Microsoft.Extensions.Azure" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" />
|
||||
<PackageReference Include="Azure.ResourceManager.ContainerInstance" />
|
||||
<PackageReference Include="Azure.Storage.Files.Shares" />
|
||||
<PackageReference Include="Azure.Data.Tables" />
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.AI.Agents.Orleans\Microsoft.AI.Agents.Orleans.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,37 +0,0 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.002.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marketing", "Marketing.csproj", "{491A6E2F-A0D0-4723-80A2-B0F60091E39D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AI.Agents.Orleans", "..\..\..\..\src\Microsoft.AI.Agents.Orleans\Microsoft.AI.Agents.Orleans.csproj", "{4C246005-605C-4F7F-8D67-839A5640587A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AI.Agents", "..\..\..\..\src\Microsoft.AI.Agents\Microsoft.AI.Agents.csproj", "{5CB1E5D6-33BF-4E09-BDE3-29F8A24F2158}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{491A6E2F-A0D0-4723-80A2-B0F60091E39D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{491A6E2F-A0D0-4723-80A2-B0F60091E39D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{491A6E2F-A0D0-4723-80A2-B0F60091E39D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{491A6E2F-A0D0-4723-80A2-B0F60091E39D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4C246005-605C-4F7F-8D67-839A5640587A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4C246005-605C-4F7F-8D67-839A5640587A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4C246005-605C-4F7F-8D67-839A5640587A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4C246005-605C-4F7F-8D67-839A5640587A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5CB1E5D6-33BF-4E09-BDE3-29F8A24F2158}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5CB1E5D6-33BF-4E09-BDE3-29F8A24F2158}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5CB1E5D6-33BF-4E09-BDE3-29F8A24F2158}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5CB1E5D6-33BF-4E09-BDE3-29F8A24F2158}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {A6288EF9-5DDB-45A9-AA9A-EB9FDB18D82F}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -1,6 +0,0 @@
|
|||
namespace Marketing.Options;
|
||||
|
||||
public static class Consts
|
||||
{
|
||||
public const string OrleansNamespace = "default";
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Marketing.Options;
|
||||
|
||||
public class OpenAIOptions
|
||||
{
|
||||
// Embeddings
|
||||
[Required]
|
||||
public required string EmbeddingsEndpoint { get; set; }
|
||||
[Required]
|
||||
public required string EmbeddingsApiKey { get; set; }
|
||||
[Required]
|
||||
public required string EmbeddingsDeploymentOrModelId { get; set; }
|
||||
|
||||
// Chat
|
||||
[Required]
|
||||
public required string ChatEndpoint { get; set; }
|
||||
[Required]
|
||||
public required string ChatApiKey { get; set; }
|
||||
[Required]
|
||||
public required string ChatDeploymentOrModelId { get; set; }
|
||||
|
||||
// TextToImage
|
||||
[Required]
|
||||
public required string ImageEndpoint { get; set; }
|
||||
[Required]
|
||||
public required string ImageApiKey { get; set; }
|
||||
// When using OpenAI, this is not required.
|
||||
public required string ImageDeploymentOrModelId { get; set; }
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Marketing.Options;
|
||||
public class QdrantOptions
|
||||
{
|
||||
[Required]
|
||||
public required string Endpoint { get; set; }
|
||||
[Required]
|
||||
public required int VectorSize { get; set; }
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
using System.Text.Json;
|
||||
using Azure;
|
||||
using Azure.AI.OpenAI;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.Extensions.Http.Resilience;
|
||||
using Microsoft.SemanticKernel.Memory;
|
||||
using Microsoft.SemanticKernel.Connectors.Qdrant;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using Marketing.SignalRHub;
|
||||
using Marketing.Options;
|
||||
using Orleans.Serialization;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Services.AddTransient(CreateKernel);
|
||||
builder.Services.AddTransient(CreateMemory);
|
||||
builder.Services.AddHttpClient();
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddApplicationInsightsTelemetry();
|
||||
builder.Services.AddSwaggerGen();
|
||||
builder.Services.AddSignalR();
|
||||
builder.Services.AddSingleton<ISignalRService, SignalRService>();
|
||||
|
||||
// Allow any CORS origin if in DEV
|
||||
const string AllowDebugOriginPolicy = "AllowDebugOrigin";
|
||||
if (builder.Environment.IsDevelopment())
|
||||
{
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy(AllowDebugOriginPolicy, builder =>
|
||||
{
|
||||
builder
|
||||
.WithOrigins("http://localhost:3000") // client url
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod()
|
||||
.AllowCredentials();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
builder.Services.AddOptions<OpenAIOptions>()
|
||||
.Configure<IConfiguration>((settings, configuration) =>
|
||||
{
|
||||
configuration.GetSection(nameof(OpenAIOptions)).Bind(settings);
|
||||
})
|
||||
.ValidateDataAnnotations()
|
||||
.ValidateOnStart();
|
||||
|
||||
builder.Services.AddOptions<QdrantOptions>()
|
||||
.Configure<IConfiguration>((settings, configuration) =>
|
||||
{
|
||||
configuration.GetSection(nameof(QdrantOptions)).Bind(settings);
|
||||
})
|
||||
.ValidateDataAnnotations()
|
||||
.ValidateOnStart();
|
||||
|
||||
builder.Host.UseOrleans(siloBuilder =>
|
||||
{
|
||||
siloBuilder.UseLocalhostClustering()
|
||||
.AddMemoryStreams("StreamProvider")
|
||||
.AddMemoryGrainStorage("PubSubStore")
|
||||
.AddMemoryGrainStorage("messages");
|
||||
siloBuilder.UseInMemoryReminderService();
|
||||
siloBuilder.UseDashboard(x => x.HostSelf = true);
|
||||
});
|
||||
|
||||
builder.Services.Configure<JsonSerializerOptions>(options =>
|
||||
{
|
||||
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.UseRouting();
|
||||
app.UseCors(AllowDebugOriginPolicy);
|
||||
app.MapControllers();
|
||||
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(c =>
|
||||
{
|
||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
|
||||
});
|
||||
|
||||
app.Map("/dashboard", x => x.UseOrleansDashboard());
|
||||
app.MapHub<ArticleHub>("/articlehub");
|
||||
app.Run();
|
||||
|
||||
static ISemanticTextMemory CreateMemory(IServiceProvider provider)
|
||||
{
|
||||
OpenAIOptions openAiConfig = provider.GetRequiredService<IOptions<OpenAIOptions>>().Value;
|
||||
QdrantOptions qdrantConfig = provider.GetRequiredService<IOptions<QdrantOptions>>().Value;
|
||||
|
||||
var loggerFactory = LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder
|
||||
.SetMinimumLevel(LogLevel.Debug)
|
||||
.AddConsole()
|
||||
.AddDebug();
|
||||
});
|
||||
|
||||
var memoryBuilder = new MemoryBuilder();
|
||||
return memoryBuilder.WithLoggerFactory(loggerFactory)
|
||||
.WithQdrantMemoryStore(qdrantConfig.Endpoint, qdrantConfig.VectorSize)
|
||||
.WithAzureOpenAITextEmbeddingGeneration(openAiConfig.EmbeddingsDeploymentOrModelId, openAiConfig.EmbeddingsEndpoint, openAiConfig.EmbeddingsApiKey)
|
||||
.Build();
|
||||
}
|
||||
|
||||
static Kernel CreateKernel(IServiceProvider provider)
|
||||
{
|
||||
OpenAIOptions openAiConfig = provider.GetRequiredService<IOptions<OpenAIOptions>>().Value;
|
||||
var clientOptions = new OpenAIClientOptions();
|
||||
clientOptions.Retry.NetworkTimeout = TimeSpan.FromMinutes(5);
|
||||
var builder = Kernel.CreateBuilder();
|
||||
builder.Services.AddLogging(c => c.AddConsole().AddDebug().SetMinimumLevel(LogLevel.Debug));
|
||||
|
||||
// Chat
|
||||
var openAIClient = new OpenAIClient(new Uri(openAiConfig.ChatEndpoint), new AzureKeyCredential(openAiConfig.ChatApiKey), clientOptions);
|
||||
if (openAiConfig.ChatEndpoint.Contains(".azure", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
builder.Services.AddAzureOpenAIChatCompletion(openAiConfig.ChatDeploymentOrModelId, openAIClient);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
builder.Services.AddOpenAIChatCompletion(openAiConfig.ChatDeploymentOrModelId, openAIClient);
|
||||
}
|
||||
|
||||
// Text to Image
|
||||
openAIClient = new OpenAIClient(new Uri(openAiConfig.ImageEndpoint), new AzureKeyCredential(openAiConfig.ImageApiKey), clientOptions);
|
||||
if (openAiConfig.ImageEndpoint.Contains(".azure", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrEmpty(nameof(openAiConfig.ImageDeploymentOrModelId), openAiConfig.ImageDeploymentOrModelId);
|
||||
builder.Services.AddAzureOpenAITextToImage(openAiConfig.ImageDeploymentOrModelId, openAIClient);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Services.AddOpenAITextToImage(openAiConfig.ImageApiKey);
|
||||
}
|
||||
|
||||
// Embeddings
|
||||
openAIClient = new OpenAIClient(new Uri(openAiConfig.EmbeddingsEndpoint), new AzureKeyCredential(openAiConfig.EmbeddingsApiKey), clientOptions);
|
||||
if (openAiConfig.EmbeddingsEndpoint.Contains(".azure", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
builder.Services.AddAzureOpenAITextEmbeddingGeneration(openAiConfig.EmbeddingsDeploymentOrModelId, openAIClient);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Services.AddOpenAITextEmbeddingGeneration(openAiConfig.EmbeddingsDeploymentOrModelId, openAIClient);
|
||||
}
|
||||
|
||||
builder.Services.ConfigureHttpClientDefaults(c =>
|
||||
{
|
||||
c.AddStandardResilienceHandler().Configure(o =>
|
||||
{
|
||||
o.Retry.MaxRetryAttempts = 5;
|
||||
o.Retry.BackoffType = Polly.DelayBackoffType.Exponential;
|
||||
});
|
||||
});
|
||||
return builder.Build();
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:59668",
|
||||
"sslPort": 44354
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5244",
|
||||
"launchUrl": "dashboard",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7227;http://localhost:5244",
|
||||
"launchUrl": "dashboard",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
namespace Marketing.SignalRHub;
|
||||
|
||||
public enum AgentTypes
|
||||
{
|
||||
Chat,
|
||||
CommunityManager,
|
||||
GraphicDesigner,
|
||||
Auditor,
|
||||
Accountant,
|
||||
Librarian
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
namespace Marketing.SignalRHub;
|
||||
|
||||
using Microsoft.AI.Agents.Abstractions;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Orleans.Runtime;
|
||||
using Marketing.Options;
|
||||
using Marketing.Events;
|
||||
|
||||
public class ArticleHub : Hub<IArticleHub>
|
||||
{
|
||||
public override async Task OnConnectedAsync()
|
||||
{
|
||||
await base.OnConnectedAsync();
|
||||
}
|
||||
|
||||
public override async Task OnDisconnectedAsync(Exception? exception)
|
||||
{
|
||||
SignalRConnectionsDB.ConnectionIdByUser.TryRemove(Context.ConnectionId, out _);
|
||||
await base.OnDisconnectedAsync(exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is called when a new message from the client arrives.
|
||||
/// </summary>
|
||||
/// <param name="frontEndMessage"></param>
|
||||
/// <param name="clusterClient"></param>
|
||||
/// <returns></returns>
|
||||
public async Task ProcessMessage(FrontEndMessage frontEndMessage, IClusterClient clusterClient)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(frontEndMessage);
|
||||
|
||||
var streamProvider = clusterClient.GetStreamProvider("StreamProvider");
|
||||
var streamId = StreamId.Create(Consts.OrleansNamespace, frontEndMessage.UserId);
|
||||
var stream = streamProvider.GetStream<Event>(streamId);
|
||||
|
||||
var data = new Dictionary<string, string>
|
||||
{
|
||||
{ "UserId", frontEndMessage.UserId },
|
||||
{ "userMessage", frontEndMessage.Message},
|
||||
};
|
||||
|
||||
await stream.OnNextAsync(new Event
|
||||
{
|
||||
Namespace = frontEndMessage.UserId,
|
||||
Type = nameof(EventTypes.UserChatInput),
|
||||
Data = data
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public async Task ConnectToAgent(string UserId, IClusterClient clusterClient)
|
||||
{
|
||||
var frontEndMessage = new FrontEndMessage()
|
||||
{
|
||||
UserId = UserId,
|
||||
Message = "Connected to agents",
|
||||
Agent = AgentTypes.Chat.ToString()
|
||||
};
|
||||
|
||||
SignalRConnectionsDB.ConnectionIdByUser.AddOrUpdate(UserId, Context.ConnectionId, (key, oldValue) => Context.ConnectionId);
|
||||
|
||||
// Notify the agents that a new user got connected.
|
||||
var streamProvider = clusterClient.GetStreamProvider("StreamProvider");
|
||||
var streamId = StreamId.Create(Consts.OrleansNamespace, frontEndMessage.UserId);
|
||||
var stream = streamProvider.GetStream<Event>(streamId);
|
||||
var data = new Dictionary<string, string>
|
||||
{
|
||||
{ "UserId", frontEndMessage.UserId },
|
||||
{ "userMessage", frontEndMessage.Message},
|
||||
};
|
||||
await stream.OnNextAsync(new Event
|
||||
{
|
||||
Namespace = frontEndMessage.UserId,
|
||||
Type = nameof(EventTypes.UserConnected),
|
||||
Data = data
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
namespace Marketing.SignalRHub;
|
||||
|
||||
public class FrontEndMessage
|
||||
{
|
||||
public required string UserId { get; set; }
|
||||
public required string Message { get; set; }
|
||||
public required string Agent { get; set; }
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
namespace Marketing.SignalRHub;
|
||||
|
||||
public interface IArticleHub
|
||||
{
|
||||
public Task ConnectToAgent(string UserId);
|
||||
|
||||
public Task ChatMessage(FrontEndMessage frontEndMessage, IClusterClient clusterClient);
|
||||
|
||||
public Task SendMessageToSpecificClient(string userId, string message);
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
namespace Marketing.SignalRHub;
|
||||
public interface ISignalRService
|
||||
{
|
||||
Task SendMessageToSpecificClient(string userId, string message, AgentTypes agentType);
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Marketing.SignalRHub;
|
||||
public static class SignalRConnectionsDB
|
||||
{
|
||||
public static ConcurrentDictionary<string, string> ConnectionIdByUser { get; } = new ConcurrentDictionary<string, string>();
|
||||
public static ConcurrentDictionary<string, string> AllConnections { get; } = new ConcurrentDictionary<string, string>();
|
||||
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace Marketing.SignalRHub;
|
||||
|
||||
public class SignalRService : ISignalRService
|
||||
{
|
||||
private readonly IHubContext<ArticleHub> _hubContext;
|
||||
public SignalRService(IHubContext<ArticleHub> hubContext)
|
||||
{
|
||||
_hubContext = hubContext;
|
||||
}
|
||||
|
||||
public async Task SendMessageToSpecificClient(string userId, string message, AgentTypes agentType)
|
||||
{
|
||||
var connectionId = SignalRConnectionsDB.ConnectionIdByUser[userId];
|
||||
var frontEndMessage = new FrontEndMessage()
|
||||
{
|
||||
UserId = userId,
|
||||
Message = message,
|
||||
Agent = agentType.ToString()
|
||||
};
|
||||
await _hubContext.Clients.Client(connectionId).SendAsync("ReceiveMessage", frontEndMessage);
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Information",
|
||||
"Microsoft.AspNetCore.SignalR": "Debug",
|
||||
"Microsoft.AspNetCore.Http.Connections": "Debug"
|
||||
}
|
||||
},
|
||||
|
||||
"ApplicationInsights": {
|
||||
"ConnectionString": "InstrumentationKey=<mandatory>;IngestionEndpoint=https://<mandatory>.applicationinsights.azure.com/"
|
||||
},
|
||||
|
||||
"AllowedHosts": "*",
|
||||
|
||||
"OpenAIOptions": {
|
||||
"ChatDeploymentOrModelId": "gpt-4-32",
|
||||
"ChatEndpoint": "https://<mandatory>.openai.azure.com/",
|
||||
"ChatApiKey": "<mandatory>",
|
||||
"EmbeddingsDeploymentOrModelId": "text-embedding-ada-002",
|
||||
"EmbeddingsEndpoint": "https://<mandatory>.openai.azure.com/",
|
||||
"EmbeddingsApiKey": "<mandatory>",
|
||||
"ImageDeploymentOrModelId":"dalle3",
|
||||
"ImageEndpoint": "https://<mandatory>.openai.azure.com/",
|
||||
"ImageApiKey": "<mandatory>"
|
||||
},
|
||||
|
||||
"QdrantOptions": {
|
||||
"Endpoint": "http://qdrant:6333",
|
||||
"VectorSize": "1536"
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/.infra/
|
||||
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
.yarn/install-state.gz
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
|
@ -1,2 +0,0 @@
|
|||
legacy-peer-deps=true
|
||||
strict-peer-dependencies=false
|
|
@ -1,43 +0,0 @@
|
|||
FROM refinedev/node:18 AS base
|
||||
|
||||
FROM base AS deps
|
||||
|
||||
RUN apk add --no-cache libc6-compat
|
||||
|
||||
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
|
||||
|
||||
RUN \
|
||||
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
|
||||
elif [ -f package-lock.json ]; then npm ci; \
|
||||
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
|
||||
else echo "Lockfile not found." && exit 1; \
|
||||
fi
|
||||
|
||||
FROM base AS builder
|
||||
|
||||
COPY --from=deps /app/refine/node_modules ./node_modules
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm run build
|
||||
|
||||
FROM base AS runner
|
||||
|
||||
ENV NODE_ENV production
|
||||
|
||||
COPY --from=builder /app/refine/public ./public
|
||||
|
||||
RUN mkdir .next
|
||||
RUN chown refine:nodejs .next
|
||||
|
||||
COPY --from=builder --chown=refine:nodejs /app/refine/.next/standalone ./
|
||||
COPY --from=builder --chown=refine:nodejs /app/refine/.next/static ./.next/static
|
||||
|
||||
USER refine
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
ENV PORT 3000
|
||||
ENV HOSTNAME "0.0.0.0"
|
||||
|
||||
CMD ["node", "server.js"]
|
|
@ -1,11 +0,0 @@
|
|||
## TODO: describe the frontend app
|
||||
|
||||
## How I started
|
||||
```
|
||||
npm -i
|
||||
```
|
||||
|
||||
## How to run it
|
||||
```shell
|
||||
npm run dev
|
||||
```
|
|
@ -1,17 +0,0 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
output: 'standalone',
|
||||
images: {
|
||||
domains: ['dalleproduse.blob.core.windows.net'],
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'dalleproduse.blob.core.windows.net',
|
||||
port: '**',
|
||||
pathname: '**',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
|
@ -1,52 +0,0 @@
|
|||
{
|
||||
"name": "Learning",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_OPTIONS=--max_old_space_size=4096 refine dev",
|
||||
"build": "refine build",
|
||||
"start": "refine start",
|
||||
"lint": "eslint '**/*.{js,jsx,ts,tsx}'",
|
||||
"refine": "refine"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.8.2",
|
||||
"@emotion/styled": "^11.8.1",
|
||||
"@fontsource/roboto": "^4.5.8",
|
||||
"@microsoft/signalr": "^8.0.0",
|
||||
"@mui/icons-material": "^5.8.3",
|
||||
"@mui/lab": "^5.0.0-alpha.85",
|
||||
"@mui/material": "^5.8.6",
|
||||
"@mui/x-data-grid": "^6.6.0",
|
||||
"@refinedev/cli": "^2.16.21",
|
||||
"@refinedev/core": "^4.47.1",
|
||||
"@refinedev/devtools": "^1.1.32",
|
||||
"@refinedev/kbar": "^1.3.6",
|
||||
"@refinedev/mui": "^5.14.4",
|
||||
"@refinedev/nextjs-router": "^6.0.0",
|
||||
"@refinedev/react-hook-form": "^4.8.14",
|
||||
"@refinedev/simple-rest": "^5.0.1",
|
||||
"js-cookie": "^3.0.5",
|
||||
"next": "14.1.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-flippy": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/node": "^18.0.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@typescript-eslint/parser": "5.48.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.1.0",
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"refine": {
|
||||
"projectId": "Z0Y3hf-6BZFyJ-VWX5Qq"
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 376 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 2.4 KiB |
|
@ -1,23 +0,0 @@
|
|||
import { AuthPage } from "@components/auth-page";
|
||||
import { authProviderServer } from "@providers/auth-provider";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default async function ForgotPassword() {
|
||||
const data = await getData();
|
||||
|
||||
if (data.authenticated) {
|
||||
redirect(data?.redirectTo || "/");
|
||||
}
|
||||
|
||||
return <AuthPage type="forgotPassword" />;
|
||||
}
|
||||
|
||||
async function getData() {
|
||||
const { authenticated, redirectTo, error } = await authProviderServer.check();
|
||||
|
||||
return {
|
||||
authenticated,
|
||||
redirectTo,
|
||||
error,
|
||||
};
|
||||
}
|
Before Width: | Height: | Size: 7.2 KiB |
|
@ -1,68 +0,0 @@
|
|||
import { DevtoolsProvider } from "@providers/devtools";
|
||||
import { Refine } from "@refinedev/core";
|
||||
import { RefineKbar, RefineKbarProvider } from "@refinedev/kbar";
|
||||
import { RefineSnackbarProvider, notificationProvider } from "@refinedev/mui";
|
||||
import routerProvider from "@refinedev/nextjs-router";
|
||||
import { Metadata } from "next";
|
||||
import { cookies } from "next/headers";
|
||||
import React, { Suspense } from "react";
|
||||
|
||||
import { ColorModeContextProvider } from "@contexts/color-mode";
|
||||
import { authProvider } from "@providers/auth-provider";
|
||||
import { dataProvider } from "@providers/data-provider";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Refine",
|
||||
description: "Generated by create refine app",
|
||||
icons: {
|
||||
icon: "/favicon.ico",
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const cookieStore = cookies();
|
||||
const theme = cookieStore.get("theme");
|
||||
const defaultMode = theme?.value === "dark" ? "dark" : "light";
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<Suspense>
|
||||
<RefineKbarProvider>
|
||||
<ColorModeContextProvider defaultMode={defaultMode}>
|
||||
<RefineSnackbarProvider>
|
||||
<DevtoolsProvider>
|
||||
<Refine
|
||||
routerProvider={routerProvider}
|
||||
dataProvider={dataProvider}
|
||||
notificationProvider={notificationProvider}
|
||||
authProvider={authProvider}
|
||||
resources={[
|
||||
{
|
||||
name: "marketing-app",
|
||||
list: "/marketing",
|
||||
}
|
||||
]}
|
||||
options={{
|
||||
syncWithLocation: true,
|
||||
warnWhenUnsavedChanges: true,
|
||||
useNewQueryKeys: true,
|
||||
projectId: "Z0Y3hf-6BZFyJ-VWX5Qq",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
<RefineKbar />
|
||||
</Refine>
|
||||
</DevtoolsProvider>
|
||||
</RefineSnackbarProvider>
|
||||
</ColorModeContextProvider>
|
||||
</RefineKbarProvider>
|
||||
</Suspense>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
import { AuthPage } from "@components/auth-page";
|
||||
import { authProviderServer } from "@providers/auth-provider";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default async function Login() {
|
||||
const data = await getData();
|
||||
|
||||
if (data.authenticated) {
|
||||
redirect(data?.redirectTo || "/");
|
||||
}
|
||||
|
||||
return <AuthPage type="login" />;
|
||||
}
|
||||
|
||||
async function getData() {
|
||||
const { authenticated, redirectTo, error } = await authProviderServer.check();
|
||||
|
||||
return {
|
||||
authenticated,
|
||||
redirectTo,
|
||||
error,
|
||||
};
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import * as React from 'react';
|
||||
import List from '@mui/material/List';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import Public from '@mui/icons-material/Public';
|
||||
import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
|
||||
import HandshakeTwoToneIcon from '@mui/icons-material/HandshakeTwoTone';
|
||||
import WorkspacePremiumTwoToneIcon from '@mui/icons-material/WorkspacePremiumTwoTone';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import LightbulbIcon from '@mui/icons-material/Lightbulb';
|
||||
import { Button, Container, Grid, TextField } from '@mui/material';
|
||||
|
||||
const data = [
|
||||
{ icon: <HandshakeTwoToneIcon />, label: 'Bank vs Mrs Peters - Settled - chf 1.5M - 1 year' },
|
||||
{ icon: <WorkspacePremiumTwoToneIcon />, label: 'Bank vs Mr Pertussi - Won - chf0 - 4 years' },
|
||||
{ icon: <Public />, label: 'Bank vs Governnent - Public Case - chf 3.7M - 10 years' },
|
||||
];
|
||||
|
||||
const FireNav = styled(List)<{ component?: React.ElementType }>({
|
||||
'& .MuiListItemButton-root': {
|
||||
paddingLeft: 24,
|
||||
paddingRight: 24,
|
||||
},
|
||||
'& .MuiListItemIcon-root': {
|
||||
minWidth: 0,
|
||||
marginRight: 16,
|
||||
},
|
||||
'& .MuiSvgIcon-root': {
|
||||
fontSize: 20,
|
||||
},
|
||||
});
|
||||
|
||||
type Message = {
|
||||
sender: string;
|
||||
text: any;
|
||||
};
|
||||
|
||||
type ChatProps = {
|
||||
messages: Message[];
|
||||
setMessages: (messages: Message[]) => void;
|
||||
sendMessage: (message: string, agent: string) => void;
|
||||
};
|
||||
|
||||
export default function Chat({ messages, setMessages, sendMessage }: ChatProps) {
|
||||
const [open, setOpen] = React.useState(true);
|
||||
const [message, setMessage] = React.useState<string>('');
|
||||
|
||||
const handleSend = (message:string) => {
|
||||
setMessages([...messages, { sender: 'user', text: message }]);
|
||||
sendMessage(message, "chat");
|
||||
};
|
||||
|
||||
return (
|
||||
<FireNav component="nav" disablePadding>
|
||||
<ListItemButton
|
||||
alignItems="flex-start"
|
||||
onClick={() => setOpen(!open)}
|
||||
sx={{
|
||||
px: 3,
|
||||
pt: 2.5,
|
||||
pb: open ? 0 : 2.5,
|
||||
'&:hover, &:focus': { '& #arrowdownicon': { opacity: open ? 1 : 0 } },
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ my: 0, opacity: 1, class: "menuicon" }}>
|
||||
<LightbulbIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Chat"
|
||||
primaryTypographyProps={{
|
||||
fontSize: 15,
|
||||
fontWeight: 'medium',
|
||||
lineHeight: '20px',
|
||||
mb: '2px',
|
||||
}}
|
||||
secondary="What would you like the campaing to be about?"
|
||||
secondaryTypographyProps={{
|
||||
noWrap: true,
|
||||
fontSize: 12,
|
||||
lineHeight: '16px',
|
||||
color: open ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0.5)',
|
||||
}}
|
||||
sx={{ my: 0 }}
|
||||
/>
|
||||
<KeyboardArrowDown
|
||||
id="arrowdownicon"
|
||||
sx={{
|
||||
mr: -1,
|
||||
opacity: 0,
|
||||
transform: open ? 'rotate(-180deg)' : 'rotate(0)',
|
||||
transition: '0.2s',
|
||||
}}
|
||||
/>
|
||||
</ListItemButton>
|
||||
{open && (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', height: 'calc(100vh - 50px)' }}>
|
||||
<Container maxWidth={false} style={{ overflowY: 'auto', flex: '1 0 auto' }}>
|
||||
<div style={{ margin: '0 auto' }}>
|
||||
{messages.map((message, index) => (
|
||||
<div key={index} style={{
|
||||
margin: '10px',
|
||||
padding: '10px',
|
||||
borderRadius: '10px',
|
||||
backgroundColor: message.sender === 'user' ? '#d1e7dd' : '#d4e2d4',
|
||||
alignSelf: message.sender === 'user' ? 'flex-end' : 'flex-start',
|
||||
maxWidth: '80%',
|
||||
wordWrap: 'break-word'
|
||||
}}>
|
||||
<strong>{message.sender}:</strong> {message.text}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
<Container maxWidth={false} style={{ height: '150px' }}>
|
||||
<Grid container spacing={1} alignItems="flex-end">
|
||||
<Grid item xs={11}>
|
||||
<TextField
|
||||
value={message}
|
||||
onChange={(e) => setMessage(e.target.value)}
|
||||
fullWidth
|
||||
inputProps={{ style: { height: 'auto' } }}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleSend((e.target as HTMLInputElement).value);
|
||||
setMessage('');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={1}>
|
||||
<Button style={{ height: '100%' }} onClick={() => {
|
||||
handleSend(message);
|
||||
setMessage('');
|
||||
}}>
|
||||
Send
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
</div>
|
||||
)}
|
||||
</FireNav>
|
||||
);
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import * as React from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import List from '@mui/material/List';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import AppShortcut from '@mui/icons-material/AttachMoney';
|
||||
import LoopIcon from '@mui/icons-material/Loop';
|
||||
import { Card, CardContent, Typography } from '@mui/material';
|
||||
import Image from 'next/image';
|
||||
|
||||
const FireNav = styled(List)<{ component?: React.ElementType }>({
|
||||
'& .MuiListItemButton-root': {
|
||||
paddingLeft: 24,
|
||||
paddingRight: 24,
|
||||
},
|
||||
'& .MuiListItemIcon-root': {
|
||||
minWidth: 0,
|
||||
marginRight: 16,
|
||||
},
|
||||
'& .MuiSvgIcon-root': {
|
||||
fontSize: 20,
|
||||
},
|
||||
});
|
||||
|
||||
type CommunityManagerProps = {
|
||||
article: string;
|
||||
open: boolean;
|
||||
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
imgUrl: string;
|
||||
};
|
||||
|
||||
export default function CommunityManager({ article, open, setOpen, imgUrl }: CommunityManagerProps) {
|
||||
console.log(`[CommunityManager] Rendering. Url: '${imgUrl}'`);
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex' }}>
|
||||
<FireNav component="nav" disablePadding>
|
||||
<Box
|
||||
sx={{
|
||||
//bgcolor: open ? 'rgba(71, 98, 130, 0.2)' : null,
|
||||
//pb: open ? 2 : 0,
|
||||
}}
|
||||
>
|
||||
<ListItemButton
|
||||
alignItems="flex-start"
|
||||
onClick={() => setOpen(!open)}
|
||||
sx={{
|
||||
px: 3,
|
||||
pt: 2.5,
|
||||
pb: open ? 0 : 2.5,
|
||||
'&:hover, &:focus': { '& #arrowdownicon': { opacity: open ? 1 : 0 } },
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ my: 0, opacity: 1, class: "menuicon" }}>
|
||||
<AppShortcut />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Social Media posts"
|
||||
primaryTypographyProps={{
|
||||
fontSize: 15,
|
||||
fontWeight: 'medium',
|
||||
lineHeight: '20px',
|
||||
mb: '2px',
|
||||
}}
|
||||
secondary="Posts in social media"
|
||||
secondaryTypographyProps={{
|
||||
noWrap: true,
|
||||
fontSize: 12,
|
||||
lineHeight: '16px',
|
||||
color: open ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0.5)',
|
||||
}}
|
||||
sx={{ my: 0 }}
|
||||
/>
|
||||
<KeyboardArrowDown
|
||||
id="arrowdownicon"
|
||||
sx={{
|
||||
mr: -1,
|
||||
opacity: 0,
|
||||
transform: open ? 'rotate(-180deg)' : 'rotate(0)',
|
||||
transition: '0.2s',
|
||||
}}
|
||||
/>
|
||||
</ListItemButton>
|
||||
{open && (
|
||||
article === '' || article === null ? (
|
||||
<Box>
|
||||
<LoopIcon
|
||||
sx={{
|
||||
animation: "spin 2s linear infinite",
|
||||
"@keyframes spin": {
|
||||
"0%": {
|
||||
transform: "rotate(360deg)",
|
||||
},
|
||||
"100%": {
|
||||
transform: "rotate(0deg)",
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Typography variant="h5" component="div">
|
||||
Social media posts on X
|
||||
</Typography>
|
||||
<p>{article}</p>
|
||||
{imgUrl && (
|
||||
<div style={{ width: '100%', height: '500px', position: 'relative' }}>
|
||||
<Image
|
||||
layout='fill'
|
||||
objectFit='cover'
|
||||
src={imgUrl}
|
||||
alt="Graphic designer is working on an image ..."
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
)}
|
||||
</Box>
|
||||
</FireNav>
|
||||
</Box>
|
||||
);
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import * as React from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import List from '@mui/material/List';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
|
||||
import Public from '@mui/icons-material/Public';
|
||||
import HandshakeTwoToneIcon from '@mui/icons-material/HandshakeTwoTone';
|
||||
import WorkspacePremiumTwoToneIcon from '@mui/icons-material/WorkspacePremiumTwoTone';
|
||||
import GavelIcon from '@mui/icons-material/Gavel';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { green, pink } from '@mui/material/colors';
|
||||
import AttachMoneyIcon from '@mui/icons-material/AttachMoney';
|
||||
|
||||
const data = [
|
||||
{ icon: <HandshakeTwoToneIcon sx={{ color: green[500] }} />, label: 'IPA discount form 9-9 - eur 15k' },
|
||||
{ icon: <GavelIcon sx={{ color: green[500] }} />, label: '2x1 brewery birthday - eur 250k' },
|
||||
{ icon: <HandshakeTwoToneIcon sx={{ color: green[500] }} />, label: 'Summer day 1 - CHF 180k' },
|
||||
{ icon: <HandshakeTwoToneIcon sx={{ color: pink[500] }} />, label: 'Worldcup promo 1.5M' },
|
||||
];
|
||||
|
||||
const FireNav = styled(List)<{ component?: React.ElementType }>({
|
||||
'& .MuiListItemButton-root': {
|
||||
paddingLeft: 24,
|
||||
paddingRight: 24,
|
||||
},
|
||||
'& .MuiListItemIcon-root': {
|
||||
minWidth: 0,
|
||||
marginRight: 16,
|
||||
},
|
||||
'& .MuiSvgIcon-root': {
|
||||
fontSize: 20,
|
||||
},
|
||||
});
|
||||
|
||||
export default function CostList() {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
console.log(`[Marketing] Rendering.`);
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex' }}>
|
||||
<FireNav component="nav" disablePadding>
|
||||
<Box
|
||||
sx={{
|
||||
//bgcolor: open ? 'rgba(71, 98, 130, 0.2)' : null,
|
||||
//pb: open ? 2 : 0,
|
||||
}}
|
||||
>
|
||||
<ListItemButton
|
||||
alignItems="flex-start"
|
||||
onClick={() => setOpen(!open)}
|
||||
sx={{
|
||||
px: 3,
|
||||
pt: 2.5,
|
||||
pb: open ? 0 : 2.5,
|
||||
'&:hover, &:focus': { '& #arrowdownicon': { opacity: open ? 1 : 0 } },
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ my: 0, opacity: 1, class: "menuicon"}}>
|
||||
<AttachMoneyIcon/>
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Economy of similar cases"
|
||||
primaryTypographyProps={{
|
||||
fontSize: 15,
|
||||
fontWeight: 'medium',
|
||||
lineHeight: '20px',
|
||||
mb: '2px',
|
||||
}}
|
||||
secondary="Cost of similar cases in the past"
|
||||
secondaryTypographyProps={{
|
||||
noWrap: true,
|
||||
fontSize: 12,
|
||||
lineHeight: '16px',
|
||||
color: open ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0.5)',
|
||||
}}
|
||||
sx={{ my: 0 }}
|
||||
/>
|
||||
<KeyboardArrowDown
|
||||
id="arrowdownicon"
|
||||
sx={{
|
||||
mr: -1,
|
||||
opacity: 0,
|
||||
transform: open ? 'rotate(-180deg)' : 'rotate(0)',
|
||||
transition: '0.2s',
|
||||
}}
|
||||
/>
|
||||
</ListItemButton>
|
||||
{open &&
|
||||
data.map((item) => (
|
||||
<ListItemButton
|
||||
key={item.label}
|
||||
sx={{ py: 0, minHeight: 32, color: 'rgba(255,255,255,.8)' }}
|
||||
>
|
||||
<ListItemIcon sx={{ color: 'inherit' }}>
|
||||
{item.icon}
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={item.label}
|
||||
primaryTypographyProps={{ fontSize: 14, fontWeight: 'medium' }}
|
||||
/>
|
||||
</ListItemButton>
|
||||
))}
|
||||
</Box>
|
||||
</FireNav>
|
||||
</Box>
|
||||
);
|
||||
}
|
|
@ -1,170 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import * as React from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import List from '@mui/material/List';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
|
||||
import Public from '@mui/icons-material/Public';
|
||||
import HandshakeTwoToneIcon from '@mui/icons-material/HandshakeTwoTone';
|
||||
import WorkspacePremiumTwoToneIcon from '@mui/icons-material/WorkspacePremiumTwoTone';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import FolderIcon from '@mui/icons-material/Folder';
|
||||
import ExpandLess from '@mui/icons-material/ExpandLess';
|
||||
import ExpandMore from '@mui/icons-material/ExpandMore';
|
||||
import Collapse from '@mui/material/Collapse';
|
||||
import FilePresentIcon from '@mui/icons-material/FilePresent';
|
||||
|
||||
const data = [
|
||||
{ icon: <HandshakeTwoToneIcon />, label: 'Internal guidance for marketing campaigns' },
|
||||
{ icon: <WorkspacePremiumTwoToneIcon />, label: 'Belgium law on marketing of alcohol' },
|
||||
{ icon: <Public />, label: 'something else' },
|
||||
];
|
||||
|
||||
const FireNav = styled(List)<{ component?: React.ElementType }>({
|
||||
'& .MuiListItemButton-root': {
|
||||
paddingLeft: 24,
|
||||
paddingRight: 24,
|
||||
},
|
||||
'& .MuiListItemIcon-root': {
|
||||
minWidth: 0,
|
||||
marginRight: 16,
|
||||
},
|
||||
'& .MuiSvgIcon-root': {
|
||||
fontSize: 20,
|
||||
},
|
||||
});
|
||||
|
||||
export default function RelevantDocumentList() {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
console.log(`[Marketing] Rendering.`);
|
||||
|
||||
const [courtCasesOpen, setCourtCasesOpen] = React.useState(true);
|
||||
const courtCasesOpenHandleClick = () => {
|
||||
setCourtCasesOpen(!courtCasesOpen);
|
||||
};
|
||||
|
||||
const [lawOpen, setLawOpen] = React.useState(true);
|
||||
const LawOpenHandleClick = () => {
|
||||
setLawOpen(!lawOpen);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex' }}>
|
||||
<FireNav component="nav" disablePadding>
|
||||
<Box
|
||||
sx={{
|
||||
bgcolor: open ? 'rgba(71, 98, 130, 0.2)' : null,
|
||||
pb: open ? 2 : 0,
|
||||
}}
|
||||
>
|
||||
<ListItemButton
|
||||
alignItems="flex-start"
|
||||
onClick={() => setOpen(!open)}
|
||||
sx={{
|
||||
px: 3,
|
||||
pt: 2.5,
|
||||
pb: open ? 0 : 2.5,
|
||||
'&:hover, &:focus': { '& #arrowdownicon': { opacity: open ? 1 : 0 } },
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ my: 0, opacity: 1, class: "menuicon" }}>
|
||||
<FilePresentIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Relevant files"
|
||||
primaryTypographyProps={{
|
||||
fontSize: 15,
|
||||
fontWeight: 'medium',
|
||||
lineHeight: '20px',
|
||||
mb: '2px',
|
||||
}}
|
||||
secondary="Files that might be relevant to your case."
|
||||
secondaryTypographyProps={{
|
||||
noWrap: true,
|
||||
fontSize: 12,
|
||||
lineHeight: '16px',
|
||||
color: open ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0.5)',
|
||||
}}
|
||||
sx={{ my: 0 }}
|
||||
/>
|
||||
<KeyboardArrowDown
|
||||
id="arrowdownicon"
|
||||
sx={{
|
||||
mr: -1,
|
||||
opacity: 0,
|
||||
transform: open ? 'rotate(-180deg)' : 'rotate(0)',
|
||||
transition: '0.2s',
|
||||
}}
|
||||
/>
|
||||
</ListItemButton>
|
||||
{open && (
|
||||
<Box>
|
||||
<List
|
||||
sx={{ bgcolor: 'background.paper', textAlign: 'left' }}
|
||||
component="nav"
|
||||
aria-labelledby="nested-list-subheader"
|
||||
>
|
||||
{/* Court cases */}
|
||||
<List>
|
||||
<ListItemButton onClick={courtCasesOpenHandleClick}>
|
||||
<ListItemIcon>
|
||||
<FolderIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Internal docs" />
|
||||
{courtCasesOpen ? <ExpandLess /> : <ExpandMore />}
|
||||
</ListItemButton>
|
||||
<Collapse in={courtCasesOpen} timeout="auto" unmountOnExit>
|
||||
<List component="div" disablePadding>
|
||||
<ListItemButton>
|
||||
<ListItemIcon sx={{ pl: 4 }}>
|
||||
<img src="/static/icons/docs.png" height={20} width={20} />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Marketing campaings general guidelines" />
|
||||
</ListItemButton>
|
||||
<ListItemButton>
|
||||
<ListItemIcon sx={{ pl: 4 }}>
|
||||
<img src="/static/icons/pdf.png" height={20} width={20} />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Marketing regulations in Belgium" />
|
||||
</ListItemButton>
|
||||
</List>
|
||||
</Collapse>
|
||||
</List>
|
||||
{/* Laws */}
|
||||
<List>
|
||||
<ListItemButton onClick={LawOpenHandleClick}>
|
||||
<ListItemIcon>
|
||||
<FolderIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Public " />
|
||||
{lawOpen ? <ExpandLess /> : <ExpandMore />}
|
||||
</ListItemButton>
|
||||
<Collapse in={lawOpen} timeout="auto" unmountOnExit>
|
||||
<List component="div" disablePadding>
|
||||
<ListItemButton>
|
||||
<ListItemIcon sx={{ pl: 4 }}>
|
||||
<img src="/static/icons/edge.png" height={20} width={20} />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Worldwide discount" />
|
||||
</ListItemButton>
|
||||
<ListItemButton>
|
||||
<ListItemIcon sx={{ pl: 4 }}>
|
||||
<img src="/static/icons/edge.png" height={20} width={20} />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Color week - T-Shitrs 2022" />
|
||||
</ListItemButton>
|
||||
</List>
|
||||
</Collapse>
|
||||
</List>
|
||||
</List>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</FireNav>
|
||||
</Box>
|
||||
);
|
||||
}
|
|
@ -1,187 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import '@fontsource/roboto/300.css';
|
||||
import '@fontsource/roboto/400.css';
|
||||
import '@fontsource/roboto/500.css';
|
||||
import '@fontsource/roboto/700.css';
|
||||
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { styled, ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Divider from '@mui/material/Divider';
|
||||
|
||||
import StakeholderList from './stakeholders/stakeholders';
|
||||
import CostList from './costs/cost';
|
||||
import RelevantDocumentList from './docs/docs';
|
||||
import Chat from './chat/chat';
|
||||
import CommunityManager from './community-manager/community-manager';
|
||||
import { Container, Grid } from '@mui/material';
|
||||
import { HubConnectionBuilder, HubConnection, LogLevel } from '@microsoft/signalr';
|
||||
|
||||
type SignalRMessage = {
|
||||
userId: string;
|
||||
message: string;
|
||||
agent: string;
|
||||
};
|
||||
|
||||
export default function Marketing() {
|
||||
const Item = styled(Paper)(({ theme }) => ({
|
||||
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
|
||||
...theme.typography.body2,
|
||||
padding: theme.spacing(0),
|
||||
textAlign: 'center',
|
||||
color: theme.palette.text.secondary,
|
||||
}));
|
||||
|
||||
// Add this style
|
||||
const Background = styled('div')({
|
||||
backgroundImage: `url(/static/background1.webp)`,
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundSize: 'cover',
|
||||
height: '100vh',
|
||||
});
|
||||
|
||||
const [connection, setConnection] = React.useState<HubConnection>();
|
||||
|
||||
const userId = 'Carlos';
|
||||
//Chat component state
|
||||
const [messages, setMessages] = React.useState<{ sender: string; text: any; }[]>([]);
|
||||
|
||||
//Community manager state
|
||||
const [ article, setArticle ] = useState<string>('');
|
||||
const [ imgUrl, setImgUrl ] = useState<string>('');
|
||||
const [ communityManagerOpen, setCommunityManagerOpen ] = useState<boolean>(false);
|
||||
|
||||
const createSignalRConnection = async (userId: string) => {
|
||||
try {
|
||||
// initi the connection
|
||||
const connection = new HubConnectionBuilder()
|
||||
.withUrl(`http://localhost:5244/articlehub`)
|
||||
.configureLogging(LogLevel.Information)
|
||||
.build();
|
||||
|
||||
//setup handler
|
||||
connection.on('ReceiveMessage', (message: SignalRMessage) => {
|
||||
console.log(`[MainPage][${message.userId}] Received message from ${message.agent}: ${message.message}`);
|
||||
if (message.agent === 'Chat') {
|
||||
const newMessage = { sender: 'agent', text: message.message };
|
||||
setMessages(prevMessages => [...prevMessages, newMessage]);
|
||||
}
|
||||
if (message.agent === 'CommunityManager') {
|
||||
setArticle(message.message);
|
||||
}
|
||||
if (message.agent === 'GraphicDesigner') {
|
||||
setImgUrl(message.message);
|
||||
}
|
||||
});
|
||||
|
||||
connection.onclose(async () => {
|
||||
console.log(`[MainPage] Connection closed.`);
|
||||
|
||||
try {
|
||||
await connection.start();
|
||||
console.log(`Connection ID: ${connection.connectionId}`);
|
||||
await connection.invoke('ConnectToAgent', userId);
|
||||
console.log(`[MainPage] Connection re-established.`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
|
||||
await connection.start();
|
||||
console.log(`Connection ID: ${connection.connectionId}`);
|
||||
await connection.invoke('ConnectToAgent', userId);
|
||||
|
||||
setConnection(connection);
|
||||
console.log(`[MainPage] Connection established.`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const setMessagesInUI = async (messages: { sender: string; text: any; }[]) => {
|
||||
await setMessages(messages);
|
||||
}
|
||||
|
||||
const sendMessage = async (message: string, agent: string) => {
|
||||
if (connection) {
|
||||
const frontEndMessage:SignalRMessage = {
|
||||
userId: userId,
|
||||
message: message,
|
||||
agent: agent
|
||||
};
|
||||
console.log(`[MainPage][${{agent}}] Sending message: ${message}`);
|
||||
await connection.invoke('ProcessMessage', frontEndMessage);
|
||||
console.log(`[MainPage][${{agent}}] message sent`);
|
||||
} else {
|
||||
console.error(`[MainPage] Connection not established.`);
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
createSignalRConnection(userId);
|
||||
}, []);
|
||||
|
||||
console.log(`[Marketing] Rendering.`);
|
||||
return (
|
||||
<Background>
|
||||
<Container maxWidth="xl" disableGutters >
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={6}>
|
||||
<Paper elevation={0} style={{ border: '1px solid #000' }}>
|
||||
<Chat messages={messages} setMessages={setMessagesInUI} sendMessage={sendMessage}/>
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Stack spacing={0}>
|
||||
<ThemeProvider
|
||||
theme={createTheme({
|
||||
components: {
|
||||
MuiListItemButton: {
|
||||
defaultProps: {
|
||||
disableTouchRipple: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
// primary: { main: 'rgb(102, 157, 246)' },
|
||||
// background: { paper: 'rgb(5, 30, 52)' },
|
||||
primary: { main: '#006BD6' },
|
||||
background: { paper: 'grey' },
|
||||
},
|
||||
})}
|
||||
>
|
||||
<Item>
|
||||
<Paper elevation={0}>
|
||||
<StakeholderList />
|
||||
</Paper>
|
||||
<Divider />
|
||||
</Item>
|
||||
<Item>
|
||||
<Paper elevation={0}>
|
||||
<CostList />
|
||||
</Paper>
|
||||
<Divider />
|
||||
</Item>
|
||||
<Item>
|
||||
<Paper elevation={0}>
|
||||
<RelevantDocumentList />
|
||||
</Paper>
|
||||
<Divider />
|
||||
</Item>
|
||||
<Item>
|
||||
<Paper elevation={0}>
|
||||
<CommunityManager article={article} open={communityManagerOpen} setOpen={setCommunityManagerOpen} imgUrl={imgUrl}/>
|
||||
</Paper>
|
||||
<Divider />
|
||||
</Item>
|
||||
</ThemeProvider>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
</Background>
|
||||
);
|
||||
}
|
|
@ -1,218 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import React, { useRef, useState } from 'react';
|
||||
|
||||
import '@fontsource/roboto/300.css';
|
||||
import '@fontsource/roboto/400.css';
|
||||
import '@fontsource/roboto/500.css';
|
||||
import '@fontsource/roboto/700.css';
|
||||
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { Avatar, Box, Container } from '@mui/material';
|
||||
import Badge, { BadgeProps } from '@mui/material/Badge';
|
||||
import { Typography } from '@mui/material';
|
||||
import List from '@mui/material/List';
|
||||
import ListItemAvatar from '@mui/material/ListItemAvatar';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
|
||||
import PersonIcon from '@mui/icons-material/Person';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import { Title } from '@refinedev/mui';
|
||||
|
||||
const GreenStyledBadge = styled(Badge)(({ theme }) => ({
|
||||
'& .MuiBadge-badge': {
|
||||
backgroundColor: '#44b700',
|
||||
color: '#44b700',
|
||||
boxShadow: `0 0 0 2px ${theme.palette.background.paper}`,
|
||||
'&::after': {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRadius: '50%',
|
||||
animation: 'ripple 1.2s infinite ease-in-out',
|
||||
border: '1px solid currentColor',
|
||||
content: '""',
|
||||
},
|
||||
},
|
||||
'@keyframes ripple': {
|
||||
'0%': {
|
||||
transform: 'scale(.8)',
|
||||
opacity: 1,
|
||||
},
|
||||
'100%': {
|
||||
transform: 'scale(2.4)',
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const RedStyledBadge = styled(Badge)(({ theme }) => ({
|
||||
'& .MuiBadge-badge': {
|
||||
backgroundColor: 'red',
|
||||
color: '#44b700',
|
||||
boxShadow: `0 0 0 2px ${theme.palette.background.paper}`,
|
||||
'&::after': {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRadius: '50%',
|
||||
animation: 'ripple 1.2s infinite ease-in-out',
|
||||
border: '1px solid currentColor',
|
||||
content: '""',
|
||||
},
|
||||
},
|
||||
'@keyframes ripple': {
|
||||
'0%': {
|
||||
transform: 'scale(.8)',
|
||||
opacity: 1,
|
||||
},
|
||||
'100%': {
|
||||
transform: 'scale(2.4)',
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export default function StakeholderList() {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
console.log(`[Marketing] Rendering.`);
|
||||
return (
|
||||
<Box sx={{ display: 'flex' }}>
|
||||
<List sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}>
|
||||
<ListItemButton
|
||||
alignItems="flex-start"
|
||||
onClick={() => setOpen(!open)}
|
||||
sx={{
|
||||
px: 3,
|
||||
pt: 2.5,
|
||||
pb: open ? 0 : 2.5,
|
||||
'&:hover, &:focus': { '& #arrowdownicon': { opacity: open ? 1 : 0 } },
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ my: 0, opacity: 1, class: "menuicon" }}>
|
||||
<PersonIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Auditor"
|
||||
primaryTypographyProps={{
|
||||
fontSize: 15,
|
||||
fontWeight: 'medium',
|
||||
lineHeight: '20px',
|
||||
mb: '2px',
|
||||
}}
|
||||
secondary="Auditing rules"
|
||||
secondaryTypographyProps={{
|
||||
noWrap: true,
|
||||
fontSize: 12,
|
||||
lineHeight: '16px',
|
||||
color: open ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0.5)',
|
||||
}}
|
||||
sx={{ my: 0 }}
|
||||
/>
|
||||
<KeyboardArrowDown
|
||||
id="arrowdownicon"
|
||||
sx={{
|
||||
mr: -1,
|
||||
opacity: 0,
|
||||
transform: open ? 'rotate(-180deg)' : 'rotate(0)',
|
||||
transition: '0.2s',
|
||||
}}
|
||||
/>
|
||||
</ListItemButton>
|
||||
{open && (
|
||||
<Box>
|
||||
<ListItemButton alignItems="flex-start">
|
||||
<ListItemAvatar>
|
||||
<Avatar alt="Language check" src="/static/check.png" />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary="Language check"
|
||||
/>
|
||||
</ListItemButton>
|
||||
<ListItemButton alignItems="flex-start">
|
||||
<ListItemAvatar>
|
||||
<Avatar alt="Financial check" src="/static/check.png" />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary="Financial check"
|
||||
/>
|
||||
</ListItemButton>
|
||||
<ListItemButton alignItems="flex-start">
|
||||
<ListItemAvatar>
|
||||
<Avatar alt="Auto approval" src="/static/check.png" />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary="Auto approval"
|
||||
/>
|
||||
</ListItemButton>
|
||||
</Box>
|
||||
)}
|
||||
{open && (
|
||||
<p>Questions? These are relevant stakeholders for you:</p>
|
||||
)}
|
||||
{open && (
|
||||
<Box>
|
||||
<ListItemButton alignItems="flex-start">
|
||||
<ListItemAvatar>
|
||||
<RedStyledBadge
|
||||
overlap="circular"
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
||||
variant="dot"
|
||||
>
|
||||
<Avatar alt="Lawrence Law" src="/static/face2.jpg" />
|
||||
</RedStyledBadge>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary="Lina Maria"
|
||||
secondary={
|
||||
<React.Fragment>
|
||||
<Typography
|
||||
sx={{ display: 'inline' }}
|
||||
component="span"
|
||||
variant="body2"
|
||||
color="text.primary"
|
||||
>
|
||||
General Attorney
|
||||
</Typography>
|
||||
</React.Fragment>
|
||||
}
|
||||
/>
|
||||
</ListItemButton>
|
||||
<ListItemButton alignItems="flex-start">
|
||||
<ListItemAvatar>
|
||||
<GreenStyledBadge
|
||||
overlap="circular"
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
||||
variant="dot"
|
||||
>
|
||||
<Avatar alt="Remy Sharp" src="/static/face.jpg" />
|
||||
</GreenStyledBadge>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary="Lawrence Gevaert"
|
||||
secondary={
|
||||
<React.Fragment>
|
||||
<Typography
|
||||
sx={{ display: 'inline' }}
|
||||
component="span"
|
||||
variant="body2"
|
||||
color="text.primary"
|
||||
>
|
||||
Marketing Manager
|
||||
</Typography>
|
||||
</React.Fragment>
|
||||
}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</Box>
|
||||
)}
|
||||
</List>
|
||||
</Box>
|
||||
);
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import { Authenticated } from "@refinedev/core";
|
||||
import { ErrorComponent } from "@refinedev/mui";
|
||||
import { Suspense } from "react";
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<Suspense>
|
||||
<Authenticated key="not-found">
|
||||
<ErrorComponent />
|
||||
</Authenticated>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import { Suspense } from "react";
|
||||
|
||||
import { Authenticated } from "@refinedev/core";
|
||||
import { NavigateToResource } from "@refinedev/nextjs-router";
|
||||
|
||||
export default function IndexPage() {
|
||||
return (
|
||||
<Suspense>
|
||||
<Authenticated key="home-page">
|
||||
<NavigateToResource />
|
||||
</Authenticated>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
import { AuthPage } from "@components/auth-page";
|
||||
import { authProviderServer } from "@providers/auth-provider";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default async function Register() {
|
||||
const data = await getData();
|
||||
|
||||
if (data.authenticated) {
|
||||
redirect(data?.redirectTo || "/");
|
||||
}
|
||||
|
||||
return <AuthPage type="register" />;
|
||||
}
|
||||
|
||||
async function getData() {
|
||||
const { authenticated, redirectTo, error } = await authProviderServer.check();
|
||||
|
||||
return {
|
||||
authenticated,
|
||||
redirectTo,
|
||||
error,
|
||||
};
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
"use client";
|
||||
import type { AuthPageProps } from "@refinedev/core";
|
||||
import { AuthPage as AuthPageBase } from "@refinedev/mui";
|
||||
|
||||
export const AuthPage = (props: AuthPageProps) => {
|
||||
return (
|
||||
<AuthPageBase
|
||||
{...props}
|
||||
formProps={{
|
||||
defaultValues: { email: "demo@refine.dev", password: "demodemo" },
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -1,80 +0,0 @@
|
|||
import { ColorModeContext } from "@contexts/color-mode";
|
||||
import DarkModeOutlined from "@mui/icons-material/DarkModeOutlined";
|
||||
import LightModeOutlined from "@mui/icons-material/LightModeOutlined";
|
||||
import AppBar from "@mui/material/AppBar";
|
||||
import Avatar from "@mui/material/Avatar";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { useGetIdentity } from "@refinedev/core";
|
||||
import { HamburgerMenu, RefineThemedLayoutV2HeaderProps } from "@refinedev/mui";
|
||||
import React, { useContext } from "react";
|
||||
|
||||
type IUser = {
|
||||
id: number;
|
||||
name: string;
|
||||
avatar: string;
|
||||
};
|
||||
|
||||
export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
|
||||
sticky = true,
|
||||
}) => {
|
||||
const { mode, setMode } = useContext(ColorModeContext);
|
||||
|
||||
const { data: user } = useGetIdentity<IUser>();
|
||||
|
||||
return (
|
||||
<AppBar position={sticky ? "sticky" : "relative"}>
|
||||
<Toolbar>
|
||||
<Stack
|
||||
direction="row"
|
||||
width="100%"
|
||||
justifyContent="flex-end"
|
||||
alignItems="center"
|
||||
>
|
||||
<HamburgerMenu />
|
||||
<Stack
|
||||
direction="row"
|
||||
width="100%"
|
||||
justifyContent="flex-end"
|
||||
alignItems="center"
|
||||
>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
setMode();
|
||||
}}
|
||||
>
|
||||
{mode === "dark" ? <LightModeOutlined /> : <DarkModeOutlined />}
|
||||
</IconButton>
|
||||
|
||||
{(user?.avatar || user?.name) && (
|
||||
<Stack
|
||||
direction="row"
|
||||
gap="16px"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
{user?.name && (
|
||||
<Typography
|
||||
sx={{
|
||||
display: {
|
||||
xs: "none",
|
||||
sm: "inline-block",
|
||||
},
|
||||
}}
|
||||
variant="subtitle2"
|
||||
>
|
||||
{user?.name}
|
||||
</Typography>
|
||||
)}
|
||||
<Avatar src={user?.avatar} alt={user?.name} />
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
);
|
||||
};
|
|
@ -1,11 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import { Header } from "@components/header";
|
||||
import { ThemedLayoutV2 } from "@refinedev/mui";
|
||||
import React from "react";
|
||||
|
||||
export const ThemedLayout = ({ children }: React.PropsWithChildren) => {
|
||||
return (
|
||||
<ThemedLayoutV2 Header={() => <Header sticky />}>{children}</ThemedLayoutV2>
|
||||
);
|
||||
};
|
|
@ -1,72 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import CssBaseline from "@mui/material/CssBaseline";
|
||||
import GlobalStyles from "@mui/material/GlobalStyles";
|
||||
import { ThemeProvider } from "@mui/material/styles";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
import { RefineThemes } from "@refinedev/mui";
|
||||
import Cookies from "js-cookie";
|
||||
import React, {
|
||||
PropsWithChildren,
|
||||
createContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
|
||||
type ColorModeContextType = {
|
||||
mode: string;
|
||||
setMode: () => void;
|
||||
};
|
||||
|
||||
export const ColorModeContext = createContext<ColorModeContextType>(
|
||||
{} as ColorModeContextType
|
||||
);
|
||||
|
||||
type ColorModeContextProviderProps = {
|
||||
defaultMode?: string;
|
||||
};
|
||||
|
||||
export const ColorModeContextProvider: React.FC<
|
||||
PropsWithChildren<ColorModeContextProviderProps>
|
||||
> = ({ children, defaultMode }) => {
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
const [mode, setMode] = useState(defaultMode || "light");
|
||||
|
||||
useEffect(() => {
|
||||
setIsMounted(true);
|
||||
}, []);
|
||||
|
||||
const systemTheme = useMediaQuery(`(prefers-color-scheme: dark)`);
|
||||
|
||||
useEffect(() => {
|
||||
if (isMounted) {
|
||||
const theme = Cookies.get("theme") || (systemTheme ? "dark" : "light");
|
||||
setMode(theme);
|
||||
}
|
||||
}, [isMounted, systemTheme]);
|
||||
|
||||
const toggleTheme = () => {
|
||||
const nextTheme = mode === "light" ? "dark" : "light";
|
||||
|
||||
setMode(nextTheme);
|
||||
Cookies.set("theme", nextTheme);
|
||||
};
|
||||
|
||||
return (
|
||||
<ColorModeContext.Provider
|
||||
value={{
|
||||
setMode: toggleTheme,
|
||||
mode,
|
||||
}}
|
||||
>
|
||||
<ThemeProvider
|
||||
// you can change the theme colors here. example: mode === "light" ? RefineThemes.Magenta : RefineThemes.MagentaDark
|
||||
theme={mode === "light" ? RefineThemes.Blue : RefineThemes.BlueDark}
|
||||
>
|
||||
<CssBaseline />
|
||||
<GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} />
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</ColorModeContext.Provider>
|
||||
);
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
import "@refinedev/mui";
|
||||
|
||||
export interface CustomTheme {
|
||||
// Add custom variables here like below:
|
||||
// status: {
|
||||
// danger: string;
|
||||
// };
|
||||
}
|
||||
|
||||
declare module "@refinedev/mui" {
|
||||
interface Theme extends import("@refinedev/mui").Theme, CustomTheme {}
|
||||
interface ThemeOptions
|
||||
extends import("@refinedev/mui").ThemeOptions,
|
||||
CustomTheme {}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
import { AuthBindings } from "@refinedev/core";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
export const authProviderServer: Pick<AuthBindings, "check"> = {
|
||||
check: async () => {
|
||||
const cookieStore = cookies();
|
||||
const auth = cookieStore.get("auth");
|
||||
|
||||
if (auth) {
|
||||
return {
|
||||
authenticated: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
authenticated: false,
|
||||
logout: true,
|
||||
redirectTo: "/login",
|
||||
};
|
||||
},
|
||||
};
|
|
@ -1,91 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import { AuthBindings } from "@refinedev/core";
|
||||
import Cookies from "js-cookie";
|
||||
|
||||
const mockUsers = [
|
||||
{
|
||||
name: "John Doe",
|
||||
email: "johndoe@mail.com",
|
||||
roles: ["admin"],
|
||||
avatar: "https://i.pravatar.cc/150?img=1",
|
||||
},
|
||||
{
|
||||
name: "Jane Doe",
|
||||
email: "janedoe@mail.com",
|
||||
roles: ["editor"],
|
||||
avatar: "https://i.pravatar.cc/150?img=1",
|
||||
},
|
||||
];
|
||||
|
||||
export const authProvider: AuthBindings = {
|
||||
login: async ({ email, username, password, remember }) => {
|
||||
// Suppose we actually send a request to the back end here.
|
||||
const user = mockUsers[0];
|
||||
|
||||
if (user) {
|
||||
Cookies.set("auth", JSON.stringify(user), {
|
||||
expires: 30, // 30 days
|
||||
path: "/",
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
redirectTo: "/",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
name: "LoginError",
|
||||
message: "Invalid username or password",
|
||||
},
|
||||
};
|
||||
},
|
||||
logout: async () => {
|
||||
Cookies.remove("auth", { path: "/" });
|
||||
return {
|
||||
success: true,
|
||||
redirectTo: "/login",
|
||||
};
|
||||
},
|
||||
check: async () => {
|
||||
const auth = Cookies.get("auth");
|
||||
if (auth) {
|
||||
return {
|
||||
authenticated: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
authenticated: false,
|
||||
logout: true,
|
||||
redirectTo: "/login",
|
||||
};
|
||||
},
|
||||
getPermissions: async () => {
|
||||
const auth = Cookies.get("auth");
|
||||
if (auth) {
|
||||
const parsedUser = JSON.parse(auth);
|
||||
return parsedUser.roles;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getIdentity: async () => {
|
||||
const auth = Cookies.get("auth");
|
||||
if (auth) {
|
||||
const parsedUser = JSON.parse(auth);
|
||||
return parsedUser;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onError: async (error) => {
|
||||
if (error.response?.status === 401) {
|
||||
return {
|
||||
logout: true,
|
||||
};
|
||||
}
|
||||
|
||||
return { error };
|
||||
},
|
||||
};
|
|
@ -1,2 +0,0 @@
|
|||
export * from "./auth-provider";
|
||||
export * from "./auth-provider.server";
|
|
@ -1,7 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import dataProviderSimpleRest from "@refinedev/simple-rest";
|
||||
|
||||
const API_URL = "https://api.fake-rest.refine.dev";
|
||||
|
||||
export const dataProvider = dataProviderSimpleRest(API_URL);
|