Skip to content

Commit 2f3bef5

Browse files
committed
Add AI Chat
1 parent c27fb5f commit 2f3bef5

File tree

3 files changed

+111
-0
lines changed

3 files changed

+111
-0
lines changed

MyApp.ServiceInterface/Data/CustomUserSession.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public override void PopulateFromClaims(IRequest httpReq, ClaimsPrincipal princi
2121
public class AdditionalUserClaimsPrincipalFactory(
2222
UserManager<ApplicationUser> userManager,
2323
RoleManager<IdentityRole> roleManager,
24+
IApiKeySource apiKeySource,
2425
IOptions<IdentityOptions> optionsAccessor)
2526
: UserClaimsPrincipalFactory<ApplicationUser,IdentityRole>(userManager, roleManager, optionsAccessor)
2627
{
@@ -35,6 +36,15 @@ public override async Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
3536
{
3637
claims.Add(new Claim(JwtClaimTypes.Picture, user.ProfileUrl));
3738
}
39+
40+
// Add Users latest API Key to Auth Cookie (Allows [ValidateApiKey] with User Auth)
41+
var latestApiKey = (await apiKeySource.GetApiKeysByUserIdAsync(user.Id))
42+
.OrderByDescending(x => x.CreatedDate)
43+
.FirstOrDefault();
44+
if (latestApiKey != null)
45+
{
46+
claims.Add(new Claim(JwtClaimTypes.ApiKey, latestApiKey.Key));
47+
}
3848

3949
identity.AddClaims(claims);
4050
return principal;

MyApp/Configure.AI.Chat.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using ServiceStack.AI;
2+
3+
[assembly: HostingStartup(typeof(MyApp.ConfigureAiChat))]
4+
5+
namespace MyApp;
6+
7+
public class ConfigureAiChat : IHostingStartup
8+
{
9+
public void Configure(IWebHostBuilder builder) => builder
10+
.ConfigureServices((context, services) => {
11+
12+
// Docs: https://docs.servicestack.net/ai-chat-api
13+
services.AddPlugin(new ChatFeature {
14+
EnableProviders = [
15+
"servicestack",
16+
// "groq",
17+
// "google_free",
18+
// "openrouter_free",
19+
// "ollama",
20+
// "google",
21+
// "anthropic",
22+
// "openai",
23+
// "grok",
24+
// "qwen",
25+
// "z.ai",
26+
// "mistral",
27+
// "openrouter",
28+
]
29+
});
30+
31+
// Persist AI Chat History, enables analytics at /admin-ui/chat
32+
services.AddSingleton<IChatStore, DbChatStore>();
33+
// Or store history in monthly partitioned tables in PostgreSQL:
34+
// services.AddSingleton<IChatStore, PostgresChatStore>();
35+
36+
// Add AI Chat link to /metadata
37+
services.ConfigurePlugin<MetadataFeature>(feature => {
38+
feature.AddPluginLink("/chat", "AI Chat");
39+
});
40+
});
41+
}

MyApp/Configure.ApiKeys.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using MyApp.Data;
2+
using ServiceStack;
3+
using ServiceStack.Data;
4+
using ServiceStack.OrmLite;
5+
using ServiceStack.Configuration;
6+
7+
[assembly: HostingStartup(typeof(MyApp.ConfigureApiKeys))]
8+
9+
namespace MyApp;
10+
11+
public class ConfigureApiKeys : IHostingStartup
12+
{
13+
public void Configure(IWebHostBuilder builder) => builder
14+
.ConfigureServices(services =>
15+
{
16+
services.AddPlugin(new ApiKeysFeature
17+
{
18+
// Optional: Available Scopes Admin Users can assign to any API Key
19+
// Features = [
20+
// "Paid",
21+
// "Tracking",
22+
// ],
23+
// Optional: Available Features Admin Users can assign to any API Key
24+
// Scopes = [
25+
// "todo:read",
26+
// "todo:write",
27+
// ],
28+
29+
// Optional: Limit available Scopes Users can assign to their own API Keys
30+
// UserScopes = [
31+
// "todo:read",
32+
// ],
33+
// Optional: Limit available Features Users can assign to their own API Keys
34+
// UserFeatures = [
35+
// "Tracking",
36+
// ],
37+
});
38+
})
39+
.ConfigureAppHost(appHost =>
40+
{
41+
using var db = appHost.Resolve<IDbConnectionFactory>().Open();
42+
var feature = appHost.GetPlugin<ApiKeysFeature>();
43+
feature.InitSchema(db);
44+
45+
// Optional: Create API Key for specified Users on Startup
46+
if (feature.ApiKeyCount(db) == 0 && db.TableExists(IdentityUsers.TableName))
47+
{
48+
var createApiKeysFor = new [] { "[email protected]", "[email protected]", "[email protected]" };
49+
var users = IdentityUsers.GetByUserNames(db, createApiKeysFor);
50+
foreach (var user in users)
51+
{
52+
List<string> scopes = user.UserName == "[email protected]"
53+
? [RoleNames.Admin]
54+
: [];
55+
feature.Insert(db,
56+
new() { Name = "Seed API Key", UserId = user.Id, UserName = user.UserName, Scopes = scopes });
57+
}
58+
}
59+
});
60+
}

0 commit comments

Comments
 (0)