Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6fdab7f
#124 - update to aspire latest version
albx Oct 24, 2025
72f801a
#124 - update packages
albx Nov 7, 2025
fe945b7
#124 - refactor streaming info. removed unused entities
albx Nov 10, 2025
34300a8
#124 - update to .net 10
albx Nov 15, 2025
d87bd74
#124 - update packages
albx Nov 22, 2025
6eb8c42
#124 - removed old settings pages
albx Nov 22, 2025
d270aec
#124 - update aspire version
albx Dec 6, 2025
18547cc
#124 - moving to extension blocks
albx Dec 6, 2025
9b94d4e
#124 - setup UI for connected channels list
albx Dec 6, 2025
43c84a2
#124 - update aspire and nuget packages
albx Dec 19, 2025
18f347d
#124 - WIP channel form panel
albx Dec 24, 2025
5c51ba7
#124 - edit UI for channels
albx Dec 27, 2025
b4e456b
#124 - WIP connected channels management
albx Dec 29, 2025
a5d97c3
#124 - connected channels can now be added
albx Jan 2, 2026
f8e81b2
#124 - edit connected channel
albx Jan 2, 2026
e5b95fa
#124 - fix reverse proxy, fix dialog close
albx Jan 16, 2026
26b07d5
#124 - add channels page translations
albx Jan 16, 2026
7705a6d
#124 - WIP new streaming form
albx Jan 16, 2026
073ce23
#124 - refactor schedule streaming endpoint
albx Jan 27, 2026
069dead
#124 - fix import streaming
albx Jan 31, 2026
2018253
#124 - streaming detail and update packages
albx Feb 16, 2026
d60c5d5
#124 - fix prevent navigation message
albx Feb 16, 2026
90348b8
#124 - update aspire version to 13.1.2
albx Feb 28, 2026
94222b2
#124 - update aspire and all packages
albx Mar 28, 2026
84649a1
#124 - add translations for channels form. update packages and aspire…
albx Apr 4, 2026
0b5f47d
#124 - fix comments post review
albx Apr 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
416 changes: 0 additions & 416 deletions LemonBot.sln

This file was deleted.

40 changes: 40 additions & 0 deletions LemonBot.slnx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<Solution>
<Configurations>
<Platform Name="Any CPU" />
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Folder Name="/src/">
<Project Path="src/KITT.AppHost/KITT.AppHost.csproj" />
<Project Path="src/KITT.ServiceDefaults/KITT.ServiceDefaults.csproj" />
<Project Path="src/KITT.Services/KITT.Services.csproj" />
</Folder>
<Folder Name="/src/KITT/">
<Project Path="src/KITT.Core/KITT.Core.csproj" />
<Project Path="src/KITT.Telegram.Messages/KITT.Telegram.Messages.csproj" />
<Project Path="src/KITT.Web.App.UI/KITT.Web.App.UI.csproj" />
<Project Path="src/KITT.Web.App/KITT.Web.App.Client/KITT.Web.App.Client.csproj" />
<Project Path="src/KITT.Web.App/KITT.Web.App/KITT.Web.App.csproj" />
<Project Path="src/KITT.Web.Models/KITT.Web.Models.csproj" />
<Project Path="src/KITT.Web.Shared/KITT.Web.Shared.csproj" />
</Folder>
<Folder Name="/src/KITT/Cms/">
<Project Path="src/KITT.Cms.Settings/KITT.Cms.Settings.csproj" />
<Project Path="src/KITT.Cms.Web.Api/KITT.Cms.Web.Api.csproj" />
<Project Path="src/KITT.Cms.Web.App/KITT.Cms.Web.App.csproj" />
<Project Path="src/KITT.Cms.Web.Models/KITT.Cms.Web.Models.csproj" />
</Folder>
<Folder Name="/src/KITT/Proposals/">
<Project Path="src/KITT.Proposals.Web.Api/KITT.Proposals.Web.Api.csproj" />
<Project Path="src/KITT.Proposals.Web.App/KITT.Proposals.Web.App.csproj" />
<Project Path="src/KITT.Proposals.Web.Models/KITT.Proposals.Web.Models.csproj" />
</Folder>
<Folder Name="/src/KITT/Support/">
<Project Path="src/KITT.Support.Seeder/KITT.Support.Seeder.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/KITT.Cms.Web.Api.Test/KITT.Cms.Web.Api.Test.csproj" />
<Project Path="tests/KITT.Core.Test/KITT.Core.Test.csproj" />
<Project Path="tests/KITT.Web.Testing/KITT.Web.Testing.csproj" />
</Folder>
</Solution>
5 changes: 5 additions & 0 deletions aspire.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"appHost": {
"path": "src/KITT.AppHost/KITT.AppHost.csproj"
}
}
32 changes: 19 additions & 13 deletions src/KITT.AppHost/DistributedApplicationBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,28 @@ namespace KITT.AppHost;

public static class DistributedApplicationBuilderExtensions
{
public static IResourceBuilder<IResourceWithConnectionString> AddKittDatabase(this IDistributedApplicationBuilder builder)
extension(IDistributedApplicationBuilder builder)
{
ArgumentNullException.ThrowIfNull(builder);

if (!builder.ExecutionContext.IsPublishMode)
public IResourceBuilder<IResourceWithConnectionString> AddKittDatabase()
{
var kittSql = builder.AddSqlServer(ServiceNames.Sql)
.WithContainerName("sqlserver-local")
.WithLifetime(ContainerLifetime.Persistent)
.WithDataVolume("kitt-data");
ArgumentNullException.ThrowIfNull(builder);

return kittSql.AddDatabase(ServiceNames.Database, databaseName: "KITT");
}
else
{
return builder.AddConnectionString(ServiceNames.Database);
if (!builder.ExecutionContext.IsPublishMode)
{
var kittSql = builder.AddSqlServer(ServiceNames.Sql)
.WithDataVolume("kitt-data")
.WithDbGate(containerBuilder =>
{
containerBuilder.WithExplicitStart();
});

return kittSql.AddDatabase(ServiceNames.Database, databaseName: "KITT");
}
else
{
return builder.AddConnectionString(ServiceNames.Database);
}
}
}

}
19 changes: 9 additions & 10 deletions src/KITT.AppHost/KITT.AppHost.csproj
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<Sdk Name="Aspire.AppHost.Sdk" Version="9.5.1" />
<Project Sdk="Aspire.AppHost.Sdk/13.2.1">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>9088a778-e43f-44ea-8c00-27313c124862</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.5.1" />
<PackageReference Include="Aspire.Hosting.Azure" Version="9.5.1" />
<PackageReference Include="Aspire.Hosting.Azure.AppContainers" Version="9.5.1" />
<PackageReference Include="Aspire.Hosting.Azure.KeyVault" Version="9.5.1" />
<PackageReference Include="Aspire.Hosting.Azure.Sql" Version="9.5.1" />
<PackageReference Include="AzureKeyVaultEmulator.Aspire.Hosting" Version="2.6.0" />
<PackageReference Include="Aspire.Hosting.Azure" Version="13.2.1" />
<PackageReference Include="Aspire.Hosting.Azure.AppContainers" Version="13.2.1" />
<PackageReference Include="Aspire.Hosting.Azure.KeyVault" Version="13.2.1" />
<PackageReference Include="Aspire.Hosting.Azure.Sql" Version="13.2.1" />
<PackageReference Include="Aspire.Hosting.Azure.Storage" Version="13.2.1" />
<PackageReference Include="AzureKeyVaultEmulator.Aspire.Hosting" Version="2.9.0" />
<PackageReference Include="CommunityToolkit.Aspire.Hosting.SqlServer.Extensions" Version="13.1.1" />
</ItemGroup>

<ItemGroup>
Expand Down
28 changes: 28 additions & 0 deletions src/KITT.AppHost/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.Extensions.Hosting;
using KITT.Services;
using KITT.AppHost;
using Aspire.Hosting.Azure;

var builder = DistributedApplication.CreateBuilder(args);

Expand All @@ -22,10 +23,37 @@
var kittDb = builder.AddKittDatabase();
#endregion

#region Azure Storage Account
var azureStorage = builder.AddAzureStorage(ServiceNames.DataStorageAccount)
.ConfigureInfrastructure(infra =>
{
var storageAccountResource = infra.GetProvisionableResources()
.OfType<Azure.Provisioning.Storage.StorageAccount>()
.SingleOrDefault();

if (storageAccountResource is not null)
{
storageAccountResource.Kind = Azure.Provisioning.Storage.StorageKind.StorageV2;
storageAccountResource.Sku = new()
{
Name = Azure.Provisioning.Storage.StorageSkuName.StandardLrs
};
}
})
.RunAsEmulator(azurite =>
{
azurite.WithDataVolume("kittdatastorage-data");
});

var settingsTables = azureStorage.AddTables(ServiceNames.SettingsTables);
#endregion

#region CMS
var cmsApi = builder.AddProject<Projects.KITT_Cms_Web_Api>(ServiceNames.CmsApi)
.WithReference(kittDb)
.WaitFor(kittDb)
.WithReference(settingsTables)
.WaitFor(settingsTables)
.WithEnvironment("Identity__TenantId", tenantId)
.WithEnvironment("Identity__Cms__AppId", cmsApiAppId)
.PublishAsAzureContainerApp((_, app) =>
Expand Down
54 changes: 54 additions & 0 deletions src/KITT.Cms.Settings/ConnectedChannelsRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using Azure.Data.Tables;
using KITT.Cms.Settings.Models;

namespace KITT.Cms.Settings;

public class ConnectedChannelsRepository : IConnectedChannelsRepository
{
private readonly TableClient _tableClient;

internal const string ConnectedChannelsTableName = "SettingsChannels";

public ConnectedChannelsRepository(TableServiceClient tableServiceClient)
{
ArgumentNullException.ThrowIfNull(tableServiceClient);
_tableClient = CreateTableIfNotExist(tableServiceClient) ?? throw new ArgumentNullException("tableClient");
Comment thread
albx marked this conversation as resolved.
}

private static TableClient? CreateTableIfNotExist(TableServiceClient tableServiceClient)
{
var tableClient = tableServiceClient.GetTableClient(ConnectedChannelsTableName);
tableClient.CreateIfNotExists();

return tableClient;
}

public async Task<ConnectedChannel[]> GetConnectedChannelsAsync(string userId)
{
var channelsQuery = _tableClient.QueryAsync<ConnectedChannel>(c => c.PartitionKey == userId);
var channels = new List<ConnectedChannel>();

await foreach (var channel in channelsQuery)
{
channels.Add(channel);
}

return channels.OrderBy(c => c.Name).ToArray();
}

public Task<ConnectedChannel?> GetConnectedChannelAsync(Guid channelId, string userId)
{
var channels = _tableClient.Query<ConnectedChannel>(c => c.PartitionKey == userId && c.RowKey == channelId.ToString());
return Task.FromResult(channels.SingleOrDefault());
}

public async Task SaveAsync(ConnectedChannel channel)
{
await _tableClient.UpsertEntityAsync(channel, TableUpdateMode.Replace);
}

public async Task DeleteAsync(Guid channelId, string userId)
{
await _tableClient.DeleteEntityAsync(userId, channelId.ToString());
}
}
14 changes: 14 additions & 0 deletions src/KITT.Cms.Settings/IConnectedChannelsRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using KITT.Cms.Settings.Models;

namespace KITT.Cms.Settings;

public interface IConnectedChannelsRepository
{
Task<ConnectedChannel[]> GetConnectedChannelsAsync(string userId);

Task<ConnectedChannel?> GetConnectedChannelAsync(Guid channelId, string userId);

Task SaveAsync(ConnectedChannel channel);

Task DeleteAsync(Guid channelId, string userId);
}
13 changes: 13 additions & 0 deletions src/KITT.Cms.Settings/KITT.Cms.Settings.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Data.Tables" Version="12.11.0" />
</ItemGroup>

</Project>
7 changes: 7 additions & 0 deletions src/KITT.Cms.Settings/Models/ChannelType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace KITT.Cms.Settings.Models;

public enum ChannelType
{
Twitch,
YouTube
}
21 changes: 21 additions & 0 deletions src/KITT.Cms.Settings/Models/ConnectedChannel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Azure;
using Azure.Data.Tables;

namespace KITT.Cms.Settings.Models;

public class ConnectedChannel : ITableEntity
{
public required string PartitionKey { get; set; } = string.Empty;

public required string RowKey { get; set; } = string.Empty;

public DateTimeOffset? Timestamp { get; set; }

public ETag ETag { get; set; }

public required string Name { get; set; } = string.Empty;

public required string Url { get; set; } = string.Empty;

public required ChannelType Type { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using KITT.Cms.Settings;
using KITT.Cms.Settings.Models;
using KITT.Cms.Web.Models.Settings;

namespace KITT.Cms.Web.Api.Endpoints.Services;

public class ChannelsEndpointsServices(IConnectedChannelsRepository connectedChannelsRepository)
{
public async Task<ChannelModel[]> GetChannelsAsync(string userId)
{
var channels = await connectedChannelsRepository.GetConnectedChannelsAsync(userId);
return channels.Select(c => new ChannelModel
{
Id = Guid.Parse(c.RowKey),
Name = c.Name,
Url = c.Url,
Type = c.Type
}).ToArray();
}

public async Task<ChannelModel?> GetChannelAsync(Guid channelId, string userId)
{
var channel = await connectedChannelsRepository.GetConnectedChannelAsync(channelId, userId);
if (channel is null)
{
return null;
}

return new ChannelModel
{
Id = Guid.Parse(channel.RowKey),
Name = channel.Name,
Url = channel.Url,
Type = channel.Type
};
}

public async Task<ChannelModel> CreateChannelAsync(ChannelModel model, string userId)
{
model.Id = Guid.NewGuid();

var channelEntity = MapToConnectedChannel(model, userId);
await connectedChannelsRepository.SaveAsync(channelEntity);

return model;
}

public async Task DeleteChannelAsync(Guid channelId, string userId)
{
await connectedChannelsRepository.DeleteAsync(channelId, userId);
}

public async Task UpdateChannelAsync(Guid channelId, ChannelModel model, string userId)
{
var connectedChannel = await connectedChannelsRepository.GetConnectedChannelAsync(channelId, userId);
if (connectedChannel is null)
{
throw new ArgumentOutOfRangeException(nameof(channelId));
}

connectedChannel.Name = model.Name;
connectedChannel.Url = model.Url;
connectedChannel.Type = model.Type;

await connectedChannelsRepository.SaveAsync(connectedChannel);
}

private static ConnectedChannel MapToConnectedChannel(ChannelModel model, string userId)
{
return new()
{
RowKey = model.Id.ToString(),
PartitionKey = userId,
Name = model.Name,
Url = model.Url,
Type = model.Type
};
}
}
Loading
Loading