diff --git a/README.md b/README.md
index d49f02b1..ea6103dc 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ BotSharp is in accordance with components principle strictly, decouples every pa
* Built-in multi-agents and conversation management.
* Support multiple LLM platforms.
* Support export/ import agent from other bot platforms directly.
-* Support different open source UI [Chatbot UI](src\Plugins\BotSharp.Plugin.ChatbotUI\Chatbot-UI.md), [HuggingChat UI](src\Plugins\BotSharp.Plugin.HuggingFace\HuggingChat-UI.md).
+* Support different open source UI [Chatbot UI](src/Plugins/BotSharp.Plugin.ChatbotUI/Chatbot-UI.md), [HuggingChat UI](src/Plugins/BotSharp.Plugin.HuggingFace/HuggingChat-UI.md).
* Integrate with popular message channels like Facebook Messenger, Slack and Telegram.
### Quick Started
@@ -34,7 +34,7 @@ BotSharp is in accordance with components principle strictly, decouples every pa
PS D:\> cd BotSharp
PS D:\BotSharp\> dotnet run -p .\src\WebStarter
```
-2. Run UI project, reference to [Chatbot UI](src\Plugins\BotSharp.Plugin.ChatbotUI\Chatbot-UI.md).
+2. Run UI project, reference to [Chatbot UI](src/Plugins/BotSharp.Plugin.ChatbotUI/Chatbot-UI.md).
### Extension Libraries
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs
index 5ca94ce6..28757dff 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs
@@ -1,3 +1,5 @@
+using BotSharp.Abstraction.Agents.Models;
+
namespace BotSharp.Abstraction.Agents;
///
@@ -5,7 +7,7 @@ namespace BotSharp.Abstraction.Agents;
///
public interface IAgentService
{
- void NewAgent();
- void DeleteAgent();
- void UpdateAgent();
+ Task CreateAgent(Agent agent);
+ Task DeleteAgent(string id);
+ Task UpdateAgent(Agent agent);
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/ICurrentUser.cs b/src/Infrastructure/BotSharp.Abstraction/Users/ICurrentUser.cs
new file mode 100644
index 00000000..f4498a22
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Users/ICurrentUser.cs
@@ -0,0 +1,9 @@
+namespace BotSharp.Abstraction.Users;
+
+public interface ICurrentUser
+{
+ string Id { get; }
+ string Email { get; }
+ string FirstName { get; }
+ string LastName { get; }
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs
new file mode 100644
index 00000000..f54782ce
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs
@@ -0,0 +1,10 @@
+using BotSharp.Abstraction.Users.Models;
+
+namespace BotSharp.Abstraction.Users;
+
+public interface IUserService
+{
+ Task CreateUser(User user);
+ Task GetToken(string authorization);
+ Task GetMyProfile();
+}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Models/Token.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Models/Token.cs
new file mode 100644
index 00000000..f9155a9c
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Users/Models/Token.cs
@@ -0,0 +1,10 @@
+namespace BotSharp.Abstraction.Users.Models;
+
+public class Token
+{
+ public string AccessToken { get; set; } = string.Empty;
+ public string RefreshToken { get; set; } = string.Empty;
+ public string TokenType { get; set; } = string.Empty;
+ public int ExpireTime { get; set; }
+ public string Scope { get; set; } = string.Empty;
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs
new file mode 100644
index 00000000..69810a13
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs
@@ -0,0 +1,13 @@
+namespace BotSharp.Abstraction.Users.Models;
+
+public class User
+{
+ public string Id { get; set; } = string.Empty;
+ public string FirstName { get; set; } = string.Empty;
+ public string LastName { get; set; } = string.Empty;
+ public string Email { get; set; } = string.Empty;
+ public string Salt { get; set; } = string.Empty;
+ public string Password { get; set; } = string.Empty;
+ public DateTime UpdatedTime { get; set; } = DateTime.UtcNow;
+ public DateTime CreatedTime { get; set; } = DateTime.UtcNow;
+}
diff --git a/src/Infrastructure/BotSharp.Core/Agents/AgentController.cs b/src/Infrastructure/BotSharp.Core/Agents/AgentController.cs
new file mode 100644
index 00000000..09d3cc24
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Agents/AgentController.cs
@@ -0,0 +1,24 @@
+using BotSharp.Abstraction.Agents;
+using BotSharp.Abstraction.ApiAdapters;
+using BotSharp.Core.Agents.ViewModels;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace BotSharp.Core.Agents;
+
+[Authorize]
+[ApiController]
+public class AgentController : ControllerBase, IApiAdapter
+{
+ private readonly IAgentService _agentService;
+ public AgentController(IAgentService agentService)
+ {
+ _agentService = agentService;
+ }
+
+ [HttpPost("/agent")]
+ public async Task CreateAgent(AgentCreationModel agent)
+ {
+ return await _agentService.CreateAgent(agent.ToAgent());
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs
new file mode 100644
index 00000000..574f483d
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs
@@ -0,0 +1,41 @@
+using BotSharp.Abstraction.Agents;
+using BotSharp.Abstraction.Agents.Models;
+using BotSharp.Core.Repository;
+using BotSharp.Core.Repository.Abstraction;
+using BotSharp.Core.Repository.DbTables;
+using EntityFrameworkCore.BootKit;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace BotSharp.Core.Agents.Services;
+
+public class AgentService : IAgentService
+{
+ private readonly IServiceProvider _services;
+ public AgentService(IServiceProvider services)
+ {
+ _services = services;
+ }
+
+ public async Task CreateAgent(Agent agent)
+ {
+ var db = _services.GetRequiredService();
+ var record = AgentRecord.FromAgent(agent);
+
+ db.Transaction(delegate
+ {
+ db.Add(record);
+ });
+
+ return record.Id;
+ }
+
+ public Task DeleteAgent(string id)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task UpdateAgent(Agent agent)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/Agents/ViewModels/AgentCreationModel.cs b/src/Infrastructure/BotSharp.Core/Agents/ViewModels/AgentCreationModel.cs
new file mode 100644
index 00000000..00bd7d92
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Agents/ViewModels/AgentCreationModel.cs
@@ -0,0 +1,18 @@
+using BotSharp.Abstraction.Agents.Models;
+
+namespace BotSharp.Core.Agents.ViewModels;
+
+public class AgentCreationModel
+{
+ public string Name { get; set; }
+ public string Description { get; set; }
+
+ public Agent ToAgent()
+ {
+ return new Agent
+ {
+ Name = Name,
+ Description = Description
+ };
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/Agents/ViewModels/AgentUpdateModel.cs b/src/Infrastructure/BotSharp.Core/Agents/ViewModels/AgentUpdateModel.cs
new file mode 100644
index 00000000..2f000c1f
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Agents/ViewModels/AgentUpdateModel.cs
@@ -0,0 +1,8 @@
+namespace BotSharp.Core.Agents.ViewModels;
+
+public class AgentUpdateModel
+{
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string Description { get; set; }
+}
diff --git a/src/Infrastructure/BotSharp.Core/Agents/ViewModels/AgentViewModel.cs b/src/Infrastructure/BotSharp.Core/Agents/ViewModels/AgentViewModel.cs
new file mode 100644
index 00000000..168525ae
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Agents/ViewModels/AgentViewModel.cs
@@ -0,0 +1,8 @@
+namespace BotSharp.Core.Agents.ViewModels;
+
+public class AgentViewModel
+{
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string Description { get; set; }
+}
diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
index 617c7071..e2080cfc 100644
--- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
+++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
@@ -67,14 +67,11 @@
+
-
-
-
-
diff --git a/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs
index f7e6b7e0..9296ec35 100644
--- a/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs
+++ b/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs
@@ -1,9 +1,10 @@
+using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Conversations;
-using BotSharp.Abstraction.TextGeneratives;
+using BotSharp.Abstraction.Users;
+using BotSharp.Core.Agents.Services;
using BotSharp.Core.Conversations;
-using BotSharp.Core.Plugins.TextGeneratives.LLamaSharp;
-using BotSharp.Core.Repository;
-using EntityFrameworkCore.BootKit;
+using BotSharp.Core.Users.Services;
+using BotSharp.Plugins.LLamaSharp;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -14,6 +15,9 @@ public static class BotSharpServiceCollectionExtensions
{
public static IServiceCollection AddBotSharp(this IServiceCollection services, IConfiguration config)
{
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
services.AddSingleton();
services.AddSingleton();
@@ -59,17 +63,22 @@ public static class BotSharpServiceCollectionExtensions
return DataContextHelper.GetDbContext(myDatabaseSettings, x);
});
- services.AddSingleton(x =>
+ services.AddScoped((IServiceProvider x) =>
{
- var settings = new LlamaSharpSettings();
- config.Bind("LlamaSharp", settings);
- return settings;
+ return DataContextHelper.GetDbContext(myDatabaseSettings, x);
});
- services.AddSingleton();
}
public static void RegisterPlugins(IServiceCollection services, IConfiguration config)
{
-
+ var settings = new LlamaSharpSettings();
+ config.Bind("LlamaSharp", settings);
+ services.AddSingleton(x =>
+ {
+
+ return settings;
+ });
+
+ // services.AddSingleton();
}
}
diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/Utilities.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/Utilities.cs
new file mode 100644
index 00000000..a75e6ce7
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Infrastructures/Utilities.cs
@@ -0,0 +1,23 @@
+namespace BotSharp.Core.Infrastructures;
+
+public static class Utilities
+{
+ public static string HashText(string password, string salt)
+ {
+ using var md5 = System.Security.Cryptography.MD5.Create();
+
+ var data = md5.ComputeHash(Encoding.UTF8.GetBytes(password + salt));
+ var sb = new StringBuilder();
+ foreach (var c in data)
+ {
+ sb.Append(c.ToString("x2"));
+ }
+ return sb.ToString();
+ }
+
+ public static (string, string) SplitAsTuple(this string str, string sep)
+ {
+ var splits = str.Split(sep);
+ return (splits[0], splits[1]);
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/Plugins/TextGeneratives/LLamaSharp/ChatCompletionProvider.cs b/src/Infrastructure/BotSharp.Core/Plugins/LLamaSharp/ChatCompletionProvider.cs
similarity index 96%
rename from src/Infrastructure/BotSharp.Core/Plugins/TextGeneratives/LLamaSharp/ChatCompletionProvider.cs
rename to src/Infrastructure/BotSharp.Core/Plugins/LLamaSharp/ChatCompletionProvider.cs
index 1550dd80..633165a1 100644
--- a/src/Infrastructure/BotSharp.Core/Plugins/TextGeneratives/LLamaSharp/ChatCompletionProvider.cs
+++ b/src/Infrastructure/BotSharp.Core/Plugins/LLamaSharp/ChatCompletionProvider.cs
@@ -1,10 +1,9 @@
using BotSharp.Abstraction.Models;
-using BotSharp.Abstraction.Plugins;
using BotSharp.Abstraction.TextGeneratives;
using LLama;
using System.IO;
-namespace BotSharp.Core.Plugins.TextGeneratives.LLamaSharp;
+namespace BotSharp.Plugins.LLamaSharp;
public class ChatCompletionProvider : IChatCompletionProvider, IBotSharpPlugin
{
diff --git a/src/Infrastructure/BotSharp.Core/Plugins/LLamaSharp/LLamaSharpPlugin.cs b/src/Infrastructure/BotSharp.Core/Plugins/LLamaSharp/LLamaSharpPlugin.cs
new file mode 100644
index 00000000..6146bc95
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Plugins/LLamaSharp/LLamaSharpPlugin.cs
@@ -0,0 +1,6 @@
+namespace BotSharp.Plugins.LLamaSharp;
+
+public class LLamaSharpPlugin : IBotSharpPlugin
+{
+
+}
diff --git a/src/Infrastructure/BotSharp.Core/Plugins/TextGeneratives/LLamaSharp/LlamaSharpSettings.cs b/src/Infrastructure/BotSharp.Core/Plugins/LLamaSharp/LlamaSharpSettings.cs
similarity index 88%
rename from src/Infrastructure/BotSharp.Core/Plugins/TextGeneratives/LLamaSharp/LlamaSharpSettings.cs
rename to src/Infrastructure/BotSharp.Core/Plugins/LLamaSharp/LlamaSharpSettings.cs
index 4720baf6..eae4e4a8 100644
--- a/src/Infrastructure/BotSharp.Core/Plugins/TextGeneratives/LLamaSharp/LlamaSharpSettings.cs
+++ b/src/Infrastructure/BotSharp.Core/Plugins/LLamaSharp/LlamaSharpSettings.cs
@@ -1,4 +1,4 @@
-namespace BotSharp.Core.Plugins.TextGeneratives.LLamaSharp;
+namespace BotSharp.Plugins.LLamaSharp;
public class LlamaSharpSettings
{
diff --git a/src/Infrastructure/BotSharp.Core/Plugins/TextGeneratives/LLamaSharp/LLamaSharpPlugin.cs b/src/Infrastructure/BotSharp.Core/Plugins/TextGeneratives/LLamaSharp/LLamaSharpPlugin.cs
deleted file mode 100644
index e518c52e..00000000
--- a/src/Infrastructure/BotSharp.Core/Plugins/TextGeneratives/LLamaSharp/LLamaSharpPlugin.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace BotSharp.Core.Plugins.TextGeneratives.LLamaSharp;
-
-public class LLamaSharpPlugin : IBotSharpPlugin
-{
-
-}
diff --git a/src/Infrastructure/BotSharp.Core/Repository/Abstraction/IAgentTable.cs b/src/Infrastructure/BotSharp.Core/Repository/Abstraction/IAgentTable.cs
new file mode 100644
index 00000000..053aab71
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Repository/Abstraction/IAgentTable.cs
@@ -0,0 +1,5 @@
+namespace BotSharp.Core.Repository.Abstraction;
+
+public interface IAgentTable
+{
+}
diff --git a/src/Infrastructure/BotSharp.Core/Repository/AgentDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/AgentDbContext.cs
new file mode 100644
index 00000000..97072369
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Repository/AgentDbContext.cs
@@ -0,0 +1,9 @@
+using BotSharp.Core.Repository.DbTables;
+
+namespace BotSharp.Core.Repository;
+
+public class AgentDbContext : Database
+{
+ public IQueryable User => Table();
+ public IQueryable Agent => Table();
+}
diff --git a/src/Infrastructure/BotSharp.Core/Repository/DataContextHelper.cs b/src/Infrastructure/BotSharp.Core/Repository/DataContextHelper.cs
index 3110218f..c58b903e 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/DataContextHelper.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/DataContextHelper.cs
@@ -1,4 +1,6 @@
+using BotSharp.Core.Repository.Abstraction;
using EntityFrameworkCore.BootKit;
+using Microsoft.Data.SqlClient;
using System.Data.Common;
namespace BotSharp.Core.Repository;
@@ -23,6 +25,18 @@ public static class DataContextHelper
IsRelational = false
});
}
+ else if (typeof(T) == typeof(AgentDbContext))
+ {
+ dc.BindDbContext(new DatabaseBind
+ {
+ ServiceProvider = serviceProvider,
+ MasterConnection = new SqlConnection(settings.Agent.Master),
+ SlaveConnections = settings.Agent.Slavers.Length == 0 ?
+ new List { new SqlConnection(settings.Agent.Master) } :
+ settings.Agent.Slavers.Select(x => new SqlConnection(x) as DbConnection).ToList(),
+ CreateDbIfNotExist = true
+ });
+ }
return dc;
}
}
diff --git a/src/Infrastructure/BotSharp.Core/Repository/DbTables/AgentRecord.cs b/src/Infrastructure/BotSharp.Core/Repository/DbTables/AgentRecord.cs
new file mode 100644
index 00000000..fcfe3e41
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Repository/DbTables/AgentRecord.cs
@@ -0,0 +1,37 @@
+using BotSharp.Abstraction.Agents.Models;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace BotSharp.Core.Repository.DbTables;
+
+[Table("Agent")]
+public class AgentRecord : DbRecord, IAgentTable
+{
+ [Required]
+ [MaxLength(64)]
+ public string Name { get; set; } = string.Empty;
+
+ [MaxLength(512)]
+ public string? Description { get; set; }
+
+ [Required]
+ [MaxLength(36)]
+ public string OwnerId { get; set; } = string.Empty;
+
+ [Required]
+ public DateTime CreatedDateTime { get; set; }
+
+ [Required]
+ public DateTime UpdatedDateTime { get; set; }
+
+ public static AgentRecord FromAgent(Agent agent)
+ {
+ return new AgentRecord
+ {
+ Id = agent.Id,
+ Name = agent.Name,
+ Description = agent.Description,
+ OwnerId = agent.OwerId
+ };
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/Repository/DbTables/UserRecord.cs b/src/Infrastructure/BotSharp.Core/Repository/DbTables/UserRecord.cs
new file mode 100644
index 00000000..ab357436
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Repository/DbTables/UserRecord.cs
@@ -0,0 +1,62 @@
+using BotSharp.Abstraction.Users.Models;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace BotSharp.Core.Repository.DbTables;
+
+[Table("User")]
+public class UserRecord : DbRecord, IAgentTable
+{
+ [Required]
+ [MaxLength(64)]
+ public string FirstName { get; set; } = string.Empty;
+
+ [Required]
+ [MaxLength(64)]
+ public string LastName { get; set; } = string.Empty;
+
+ [Required]
+ [MaxLength(64)]
+ public string Email { get; set; } = string.Empty;
+
+ [Required]
+ [StringLength(32)]
+ public string Salt { get; set; } = string.Empty;
+
+ [Required]
+ [MaxLength(256)]
+ public string Password { get; set; } = string.Empty;
+
+ [Required]
+ public DateTime UpdatedTime { get; set; } = DateTime.UtcNow;
+
+ [Required]
+ public DateTime CreatedTime { get; set; } = DateTime.UtcNow;
+
+ public static UserRecord FromUser(User user)
+ {
+ return new UserRecord
+ {
+ Id = user.Id,
+ FirstName = user.FirstName,
+ LastName = user.LastName,
+ Email = user.Email,
+ Password = user.Password
+ };
+ }
+
+ public User ToUser()
+ {
+ return new User
+ {
+ Id = Id,
+ FirstName = FirstName,
+ LastName = LastName,
+ Email = Email,
+ Salt = Salt,
+ Password = Password,
+ CreatedTime = CreatedTime,
+ UpdatedTime = UpdatedTime
+ };
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/Repository/MyDatabaseSettings.cs b/src/Infrastructure/BotSharp.Core/Repository/MyDatabaseSettings.cs
index 8227200e..1b68b80e 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/MyDatabaseSettings.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/MyDatabaseSettings.cs
@@ -6,4 +6,5 @@ public class MyDatabaseSettings : DatabaseSettings
{
public string[] Assemblies { get; set; }
public DbConnectionSetting MongoDb { get; set; }
+ public DbConnectionSetting Agent { get; set; }
}
diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/CurrentUser.cs b/src/Infrastructure/BotSharp.Core/Users/Services/CurrentUser.cs
new file mode 100644
index 00000000..f0ef926f
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Users/Services/CurrentUser.cs
@@ -0,0 +1,25 @@
+using BotSharp.Abstraction.Users;
+using Microsoft.AspNetCore.Http;
+using System.Security.Claims;
+
+namespace BotSharp.Core.Users.Services;
+
+public class CurrentUser : ICurrentUser
+{
+ private readonly IHttpContextAccessor _contextAccessor;
+ private IEnumerable _claims => _contextAccessor.HttpContext.User.Claims;
+
+ public CurrentUser(IHttpContextAccessor contextAccessor)
+ {
+ _contextAccessor = contextAccessor;
+ }
+
+ public string Id => _claims.First(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier").Value;
+
+
+ public string Email => _claims.First(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress").Value;
+
+ public string FirstName => _claims.First(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname").Value;
+
+ public string LastName => _claims.First(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname").Value;
+}
diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs
new file mode 100644
index 00000000..96991199
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs
@@ -0,0 +1,123 @@
+using BotSharp.Abstraction.Users;
+using BotSharp.Abstraction.Users.Models;
+using BotSharp.Core.Infrastructures;
+using BotSharp.Core.Repository.DbTables;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.IdentityModel.Tokens;
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+
+namespace BotSharp.Core.Users.Services;
+
+public class UserService : IUserService
+{
+ private readonly IServiceProvider _services;
+ private readonly ICurrentUser _user;
+
+ public UserService(IServiceProvider services, ICurrentUser user)
+ {
+ _services = services;
+ _user = user;
+ }
+
+ public async Task CreateUser(User user)
+ {
+ var db = _services.GetRequiredService();
+ var record = db.User.FirstOrDefault(x => x.Email == user.Email.ToLower());
+ if (record != null)
+ {
+ return record.ToUser();
+ }
+
+ record = UserRecord.FromUser(user);
+ record.Id = Guid.NewGuid().ToString();
+ record.Email = user.Email.ToLower();
+ record.Salt = Guid.NewGuid().ToString("N");
+ record.Password = Utilities.HashText(user.Password, record.Salt);
+
+ db.Transaction(delegate
+ {
+ db.Add(record);
+ });
+
+ return record.ToUser();
+ }
+
+ public async Task GetToken(string authorization)
+ {
+ var base64 = Encoding.UTF8.GetString(Convert.FromBase64String(authorization));
+ var (userEmail, password) = base64.SplitAsTuple(":");
+
+ var db = _services.GetRequiredService();
+ var record = db.User.FirstOrDefault(x => x.Email == userEmail);
+ if (record == null)
+ {
+ return default;
+ }
+
+ if (Utilities.HashText(password, record.Salt) != record.Password)
+ {
+ return default;
+ }
+
+ var accessToken = GenerateJwtToken(record);
+ var jwt = new JwtSecurityTokenHandler().ReadJwtToken(accessToken);
+ return new Token
+ {
+ AccessToken = accessToken,
+ ExpireTime = jwt.Payload.Exp.Value,
+ TokenType = "Bearer",
+ Scope = "api"
+ };
+ }
+
+ private string GenerateJwtToken(UserRecord user)
+ {
+ var config = _services.GetRequiredService();
+ var issuer = config["Jwt:Issuer"];
+ var audience = config["Jwt:Audience"];
+ var key = Encoding.ASCII.GetBytes(config["Jwt:Key"]);
+ var tokenDescriptor = new SecurityTokenDescriptor
+ {
+ Subject = new ClaimsIdentity(new[]
+ {
+ new Claim(JwtRegisteredClaimNames.NameId, user.Id),
+ new Claim(JwtRegisteredClaimNames.Email, user.Email),
+ new Claim(JwtRegisteredClaimNames.GivenName, user.FirstName),
+ new Claim(JwtRegisteredClaimNames.FamilyName, user.LastName),
+ new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
+ }),
+ Expires = DateTime.UtcNow.AddMinutes(5),
+ Issuer = issuer,
+ Audience = audience,
+ SigningCredentials = new SigningCredentials
+ (new SymmetricSecurityKey(key),
+ SecurityAlgorithms.HmacSha512Signature)
+ };
+ var tokenHandler = new JwtSecurityTokenHandler();
+ var token = tokenHandler.CreateToken(tokenDescriptor);
+ return tokenHandler.WriteToken(token);
+ }
+
+ public async Task GetMyProfile()
+ {
+ var userId = _user.Id;
+
+ var db = _services.GetRequiredService();
+ var user = (from u in db.User
+ where u.Id == userId
+ select new User
+ {
+ Id = u.Id,
+ Email = u.Email,
+ FirstName = u.FirstName,
+ LastName = u.LastName,
+ CreatedTime = u.CreatedTime,
+ UpdatedTime = u.UpdatedTime,
+ Password = u.Password,
+ }).FirstOrDefault();
+
+ return user;
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/Users/UserController.cs b/src/Infrastructure/BotSharp.Core/Users/UserController.cs
new file mode 100644
index 00000000..6be3be23
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Users/UserController.cs
@@ -0,0 +1,47 @@
+using BotSharp.Abstraction.ApiAdapters;
+using BotSharp.Abstraction.Users;
+using BotSharp.Abstraction.Users.Models;
+using BotSharp.Core.Users.ViewModels;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace BotSharp.Core.Users;
+
+[Authorize]
+[ApiController]
+public class UserController : ControllerBase, IApiAdapter
+{
+ private readonly IUserService _userService;
+ public UserController(IUserService userService)
+ {
+ _userService = userService;
+ }
+
+ [AllowAnonymous]
+ [HttpPost("/token")]
+ public async Task> GetToken()
+ {
+ var authcode = Request.Headers["Authorization"].ToString();
+ var token = await _userService.GetToken(authcode.Split(' ')[1]);
+ if (token == null)
+ {
+ return Unauthorized();
+ }
+ return Ok(token);
+ }
+
+ [AllowAnonymous]
+ [HttpPost("/user")]
+ public async Task CreateUser(UserCreationModel user)
+ {
+ var createdUser = await _userService.CreateUser(user.ToUser());
+ return UserViewModel.FromUser(createdUser);
+ }
+
+ [HttpGet("/user/my")]
+ public async Task GetMyUserProfile()
+ {
+ var user = await _userService.GetMyProfile();
+ return UserViewModel.FromUser(user);
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/Users/ViewModels/UserCreationModel.cs b/src/Infrastructure/BotSharp.Core/Users/ViewModels/UserCreationModel.cs
new file mode 100644
index 00000000..23aa7e08
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Users/ViewModels/UserCreationModel.cs
@@ -0,0 +1,22 @@
+using BotSharp.Abstraction.Users.Models;
+
+namespace BotSharp.Core.Users.ViewModels;
+
+public class UserCreationModel
+{
+ public string FirstName { get; set; }
+ public string LastName { get; set; }
+ public string Email { get; set; }
+ public string Password { get; set; }
+
+ public User ToUser()
+ {
+ return new User
+ {
+ FirstName = FirstName,
+ LastName = LastName,
+ Email = Email,
+ Password = Password
+ };
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/Users/ViewModels/UserViewModel.cs b/src/Infrastructure/BotSharp.Core/Users/ViewModels/UserViewModel.cs
new file mode 100644
index 00000000..ecb2c16b
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Users/ViewModels/UserViewModel.cs
@@ -0,0 +1,22 @@
+using BotSharp.Abstraction.Users.Models;
+
+namespace BotSharp.Core.Users.ViewModels;
+
+public class UserViewModel
+{
+ public string Id { get; set; }
+ public string FirstName { get; set; }
+ public string LastName { get; set; }
+ public string Email { get; set; }
+
+ public static UserViewModel FromUser(User user)
+ {
+ return new UserViewModel
+ {
+ Id = user.Id,
+ FirstName = user.FirstName,
+ LastName = user.LastName,
+ Email = user.Email
+ };
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/Using.cs b/src/Infrastructure/BotSharp.Core/Using.cs
index 61f87ce3..a12f7db8 100644
--- a/src/Infrastructure/BotSharp.Core/Using.cs
+++ b/src/Infrastructure/BotSharp.Core/Using.cs
@@ -4,4 +4,7 @@ global using System.Text;
global using System.Threading.Tasks;
global using BotSharp.Abstraction;
global using System.Linq;
-global using BotSharp.Abstraction.Plugins;
\ No newline at end of file
+global using BotSharp.Abstraction.Plugins;
+global using EntityFrameworkCore.BootKit;
+global using BotSharp.Core.Repository;
+global using BotSharp.Core.Repository.Abstraction;
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/AzureOpenAiServiceCollectionExtensions.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/AzureOpenAiServiceCollectionExtensions.cs
index 11a10e53..4e4d2c9e 100644
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/AzureOpenAiServiceCollectionExtensions.cs
+++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/AzureOpenAiServiceCollectionExtensions.cs
@@ -11,7 +11,7 @@ public static class AzureOpenAiServiceCollectionExtensions
public static IServiceCollection AddAzureOpenAiPlatform(this IServiceCollection services, IConfiguration config)
{
var settings = new AzureOpenAiSettings();
- config.Bind("AzureAi", settings);
+ config.Bind("AzureOpenAi", settings);
services.AddSingleton(x =>
{
diff --git a/src/Plugins/BotSharp.Plugin.ChatbotUI/BotSharp.Plugin.ChatbotUI.csproj b/src/Plugins/BotSharp.Plugin.ChatbotUI/BotSharp.Plugin.ChatbotUI.csproj
index f01a29ba..a3a5b80b 100644
--- a/src/Plugins/BotSharp.Plugin.ChatbotUI/BotSharp.Plugin.ChatbotUI.csproj
+++ b/src/Plugins/BotSharp.Plugin.ChatbotUI/BotSharp.Plugin.ChatbotUI.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/src/Plugins/BotSharp.Plugin.HuggingFace/BotSharp.Plugin.HuggingFace.csproj b/src/Plugins/BotSharp.Plugin.HuggingFace/BotSharp.Plugin.HuggingFace.csproj
index a27882a7..2bbca3fb 100644
--- a/src/Plugins/BotSharp.Plugin.HuggingFace/BotSharp.Plugin.HuggingFace.csproj
+++ b/src/Plugins/BotSharp.Plugin.HuggingFace/BotSharp.Plugin.HuggingFace.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/src/WebStarter/Program.cs b/src/WebStarter/Program.cs
index 6624336a..ae712573 100644
--- a/src/WebStarter/Program.cs
+++ b/src/WebStarter/Program.cs
@@ -1,18 +1,42 @@
using BotSharp.Core;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.IdentityModel.Tokens;
+using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
+
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHttpContextAccessor();
+// Add bearer authentication
+builder.Services.AddAuthentication(options =>
+{
+ options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+ options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+ options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
+}).AddJwtBearer(o =>
+{
+ o.TokenValidationParameters = new TokenValidationParameters
+ {
+ ValidIssuer = builder.Configuration["Jwt:Issuer"],
+ ValidAudience = builder.Configuration["Jwt:Audience"],
+ IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])),
+ ValidateIssuer = true,
+ ValidateAudience = true,
+ ValidateLifetime = false,
+ ValidateIssuerSigningKey = true
+ };
+});
+
// Add BotSharp
builder.Services.AddBotSharp(builder.Configuration);
-// builder.Services.AddAzureOpenAiPlatform(builder.Configuration);
+builder.Services.AddAzureOpenAiPlatform(builder.Configuration);
builder.Services.AddCors(options =>
{
@@ -34,6 +58,7 @@ if (app.Environment.IsDevelopment())
app.UseSwaggerUI();
}
+app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj
index fd6006b0..406935a2 100644
--- a/src/WebStarter/WebStarter.csproj
+++ b/src/WebStarter/WebStarter.csproj
@@ -22,6 +22,7 @@
+
diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json
index 32b9bfe9..d916bbbb 100644
--- a/src/WebStarter/appsettings.json
+++ b/src/WebStarter/appsettings.json
@@ -7,9 +7,10 @@
},
"AllowedHosts": "*",
- "OpenAi": {
- "userName": "",
- "password": ""
+ "Jwt": {
+ "Issuer": "botsharp",
+ "Audience": "botsharp",
+ "Key": "31ba6052aa6f4569901facc3a41fcb4a"
},
"LlamaSharp": {
@@ -33,7 +34,15 @@
"MongoDb": {
"Master": "mongodb://localhost:27017/chat-ui"
},
+ "Agent": {
+ "Master": "Data Source=(localdb)\\ProjectModels;Initial Catalog=Agent;Integrated Security=True;Connect Timeout=30;Encrypt=False;Trust Server Certificate=False;Application Intent=ReadWrite;Multi Subnet Failover=False",
+ "Slavers": []
+ },
"UseCamelCase": true,
"Assemblies": [ "BotSharp.Core" ]
+ },
+
+ "Providers": {
+ "ChatCompletionProvider": "BotSharp.Plugins.LLamaSharp.ChatCompletionProvider"
}
}
diff --git a/tests/UnitTest/UnitTest.csproj b/tests/UnitTest/UnitTest.csproj
index ed22915e..c3ac4f4e 100644
--- a/tests/UnitTest/UnitTest.csproj
+++ b/tests/UnitTest/UnitTest.csproj
@@ -10,10 +10,13 @@
-
-
-
-
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+