diff --git a/Directory.Packages.props b/Directory.Packages.props index 53f66bc..5df0b8e 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -2,11 +2,11 @@ true true - 8.1.22 + 8.1.23 - + @@ -31,8 +31,8 @@ - - + + @@ -50,7 +50,7 @@ - + @@ -108,7 +108,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Documents/SCRIPTS_MSSQL.md b/Documents/SCRIPTS_MSSQL.md index cbf9096..3e80d8d 100644 --- a/Documents/SCRIPTS_MSSQL.md +++ b/Documents/SCRIPTS_MSSQL.md @@ -1,35 +1,3 @@ -# app_info - -```sql -CREATE TABLE [appinfo] ( - [Id] varchar(32) NOT NULL, - [TeamId] varchar(32) NOT NULL, - [Name] varchar(100) NOT NULL, - [Secret] varchar(255) NOT NULL, - [Description] varchar(500) NULL, - [Status] int NOT NULL, - [CreateTime] datetime DEFAULT getdate() NOT NULL, - [UpdateTime] datetime DEFAULT getdate() NOT NULL, - PRIMARY KEY CLUSTERED ([Id]) -) -GO - -ALTER TABLE [appinfo] SET (LOCK_ESCALATION = TABLE) -GO - -CREATE NONCLUSTERED INDEX [IDX_APP_INFO_STATUS] -ON [appinfo] ( - [Status] ASC -) -GO - -CREATE NONCLUSTERED INDEX [IDX_APP_INFO_TEAM_ID] -ON [appinfo] ( - [TeamId] ASC -) -GO -``` - # operate_log ```sql @@ -73,8 +41,10 @@ GO ```sql CREATE TABLE [configuration] ( [Id] bigint NOT NULL, - [AppId] varchar(32) NOT NULL, - [Environment] varchar(50) NOT NULL, + [TeamId] varchar(32) NOT NULL, + [Name] varchar(100) NOT NULL, + [Secret] varchar(255) NOT NULL, + [Description] varchar(500) NULL, [Status] int NOT NULL, [Version] varchar(20) NULL, [PublishTime] datetime NULL, @@ -91,14 +61,14 @@ GO CREATE UNIQUE NONCLUSTERED INDEX [IDX_CONFIG_UNIQUE] ON [configuration] ( - [AppId] ASC, - [Environment] ASC + [TeamId] ASC, + [Name] ASC ) GO -CREATE NONCLUSTERED INDEX [IDX_CONFIG_APP_ID] +CREATE NONCLUSTERED INDEX [IDX_CONFIG_TEAM_ID] ON [configuration] ( - [AppId] ASC + [TeamId] ASC ) GO diff --git a/Documents/SCRIPTS_MYSQL.md b/Documents/SCRIPTS_MYSQL.md index 5ece6ff..d915de3 100644 --- a/Documents/SCRIPTS_MYSQL.md +++ b/Documents/SCRIPTS_MYSQL.md @@ -1,21 +1,3 @@ -# app_info - -```sql -CREATE TABLE `app_info` ( - `Id` varchar(32) NOT NULL, - `TeamId` varchar(32) NOT NULL, - `Name` varchar(100) NOT NULL, - `Secret` varchar(255) NOT NULL, - `Description` varchar(500) NULL DEFAULT NULL, - `Status` int NOT NULL, - `CreateTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `UpdateTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`Id`) USING BTREE, - INDEX `IDX_APP_INFO_STATUS`(`Status` ASC) USING BTREE, - INDEX `IDX_APP_INFO_TEAM_ID`(`TeamId` ASC) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic; -``` - # operate_log ```sql @@ -40,8 +22,10 @@ CREATE TABLE `operate_log` ( ```sql CREATE TABLE `configuration` ( `Id` bigint NOT NULL, - `AppId` varchar(32) NOT NULL, - `Environment` varchar(50) NOT NULL, + `TeamId` varchar(32) NOT NULL, + `Name` varchar(100) NOT NULL, + `Secret` varchar(255) NOT NULL, + `Description` varchar(500) NULL DEFAULT NULL, `Status` int NOT NULL, `Version` varchar(20) NULL DEFAULT NULL, `PublishTime` datetime NULL DEFAULT NULL, @@ -50,8 +34,8 @@ CREATE TABLE `configuration` ( `CreatedBy` varchar(64) NOT NULL, `UpdatedBy` varchar(64) NOT NULL, PRIMARY KEY (`Id`) USING BTREE, - UNIQUE INDEX `IDX_CONFIG_UNIQUE`(`AppId` ASC, `Environment` ASC) USING BTREE, - INDEX `IDX_CONFIG_APP_ID`(`AppId` ASC) USING BTREE, + UNIQUE INDEX `IDX_CONFIG_UNIQUE`(`TeamId` ASC, `Name` ASC) USING BTREE, + INDEX `IDX_CONFIG_TEAM_ID`(`TeamId` ASC) USING BTREE, INDEX `IDX_CONFIG_STATUS`(`Status` ASC) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic; ``` diff --git a/Documents/SCRIPTS_PGSQL.md b/Documents/SCRIPTS_PGSQL.md index b92e864..e093293 100644 --- a/Documents/SCRIPTS_PGSQL.md +++ b/Documents/SCRIPTS_PGSQL.md @@ -1,27 +1,3 @@ -# app_info - -```sql -CREATE TABLE "public"."appinfo" ( - "Id" varchar(32) COLLATE "pg_catalog"."default" NOT NULL, - "TeamId" varchar(32) COLLATE "pg_catalog"."default" NOT NULL, - "Name" varchar(100) COLLATE "pg_catalog"."default" NOT NULL, - "Secret" varchar(255) COLLATE "pg_catalog"."default" NOT NULL, - "Description" varchar(500) COLLATE "pg_catalog"."default", - "Status" int4 NOT NULL, - "CreateTime" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "UpdateTime" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY ("Id") -) -; - -CREATE INDEX "IDX_APP_INFO_STATUS" ON "public"."appinfo" USING btree ( - "Status" "pg_catalog"."int4_ops" ASC NULLS LAST -); -CREATE INDEX "IDX_APP_INFO_TEAM_ID" ON "public"."appinfo" USING btree ( - "TeamId" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST -); -``` - # operate_log ```sql @@ -54,8 +30,10 @@ CREATE INDEX "IDX_OPERATE_LOG_USER_NAME" ON "public"."operate_log" USING btree ( ```sql CREATE TABLE "public"."configuration" ( "Id" int8 NOT NULL, - "AppId" varchar(32) COLLATE "pg_catalog"."default" NOT NULL, - "Environment" varchar(25) COLLATE "pg_catalog"."default" NOT NULL, + "TeamId" varchar(32) COLLATE "pg_catalog"."default" NOT NULL, + "Name" varchar(100) COLLATE "pg_catalog"."default" NOT NULL, + "Secret" varchar(255) COLLATE "pg_catalog"."default" NOT NULL, + "Description" varchar(500) COLLATE "pg_catalog"."default", "Status" int4 NOT NULL, "Version" varchar(20) COLLATE "pg_catalog"."default", "PublishTime" timestamp(6), @@ -64,18 +42,17 @@ CREATE TABLE "public"."configuration" ( "CreatedBy" varchar(255) COLLATE "pg_catalog"."default" NOT NULL, "UpdatedBy" date NOT NULL, PRIMARY KEY ("Id") -) -; +); -CREATE INDEX "IDX_CONFIG_APP_ID" ON "public"."configuration" USING btree ( - "AppId" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST +CREATE INDEX "IDX_CONFIG_TEAM_ID" ON "public"."configuration" USING btree ( + "TeamId" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST ); CREATE INDEX "IDX_CONFIG_STATUS" ON "public"."configuration" USING btree ( "Status" "pg_catalog"."int4_ops" ASC NULLS LAST ); CREATE UNIQUE INDEX "IDX_CONFIG_UNIQUE" ON "public"."configuration" USING btree ( - "AppId" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST, - "Environment" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST + "TeamId" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST, + "Name" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST ); ``` diff --git a/Documents/SCRIPTS_SQLITE.md b/Documents/SCRIPTS_SQLITE.md index c64d806..5cc0deb 100644 --- a/Documents/SCRIPTS_SQLITE.md +++ b/Documents/SCRIPTS_SQLITE.md @@ -1,30 +1,4 @@ -# app_info - -```sqlite -CREATE TABLE "app_info" ( - "Id" text(32) NOT NULL, - "TeamId" text(32) NOT NULL, - "Name" text(50) NOT NULL, - "Code" text(50) NOT NULL, - "Secret" text(50) NOT NULL, - "Description" text(500), - "Status" integer NOT NULL DEFAULT 1, - "CreateTime" text NOT NULL, - "UpdateTime" text NOT NULL, - PRIMARY KEY ("Id") -); - -CREATE INDEX "IDX_APP_INFO_STATUS" -ON "app_info" ( - "Status" ASC -); -CREATE INDEX "IDX_APP_INFO_TEAM_ID" -ON "app_info" ( - "TeamId" ASC -); -``` - -# operate_log +# operate_log ```sqlite CREATE TABLE "operate_log" ( @@ -58,8 +32,10 @@ ON "operate_log" ( ```sqlite CREATE TABLE "configuration" ( "Id" integer NOT NULL, - "AppId" text(32) NOT NULL, - "Environment" text NOT NULL, + "TeamId" text(32) NOT NULL, + "Name" text NOT NULL, + "Secret" text(50) NOT NULL, + "Description" text(500), "Status" integer NOT NULL DEFAULT 1, "Version" text, "PublishTime" text, @@ -70,9 +46,9 @@ CREATE TABLE "configuration" ( PRIMARY KEY ("Id") ); -CREATE INDEX "IDX_CONFIG_APP_ID" +CREATE INDEX "IDX_CONFIG_TEAM_ID" ON "configuration" ( - "AppId" ASC + "TeamId" ASC ); CREATE INDEX "IDX_CONFIG_STATUS" ON "configuration" ( @@ -80,8 +56,8 @@ ON "configuration" ( ); CREATE UNIQUE INDEX "IDX_CONFIG_UNIQUE" ON "configuration" ( - "AppId" ASC, - "Environment" ASC + "TeamId" ASC, + "Name" ASC ); ``` diff --git a/README.md b/README.md index c7bff31..aa0ef47 100644 --- a/README.md +++ b/README.md @@ -199,9 +199,8 @@ app.Run(); { "Starfish": { "Host": "http://localhost:5000", - "App": "Starfish.Sample.Blazor", - "Secret": "123456", - "Env": "Development" + "Id": "5lNc9zQGdG7", + "Secret": "123456" } } ``` @@ -249,12 +248,13 @@ See the Swagger UI at [http://localhost:5229/swagger](http://localhost:5229/swag - Docker support. / 支持Docker部署。 - MongoDB support. / 支持MongoDB。 - User registration. / 用户注册。 + - Yaml support. / 支持Yaml。 ## v1.3 - Multiple node deployment. / 支持多节点部署。 - Common configuration. / 公共配置。 - - Customized environments. / 自定义环境。 + - Real-time connections refreshing. / 实时连接信息刷新。 ## v2.0 diff --git a/Source/Starfish.Client/Clients/SocketConfigurationClient.cs b/Source/Starfish.Client/Clients/SocketConfigurationClient.cs index 6f43c34..8795142 100644 --- a/Source/Starfish.Client/Clients/SocketConfigurationClient.cs +++ b/Source/Starfish.Client/Clients/SocketConfigurationClient.cs @@ -9,7 +9,7 @@ internal class SocketConfigurationClient : IConfigurationClient public SocketConfigurationClient(Uri host, string id, string secret) { - _uri = new Uri($"{host.AbsoluteUri}ws?app={id}&secret={secret}"); + _uri = new Uri($"{host.AbsoluteUri}ws?id={id}&secret={secret}"); // _client.Options.SetRequestHeader(Constants.RequestHeaders.Team, team); // _client.Options.SetRequestHeader(Constants.RequestHeaders.App, app); // _client.Options.SetRequestHeader(Constants.RequestHeaders.Secret, secret); diff --git a/Source/Starfish.Service/Application/ConnectionContainer.cs b/Source/Starfish.Service/Application/ConnectionContainer.cs index 7db40bf..7157ebc 100644 --- a/Source/Starfish.Service/Application/ConnectionContainer.cs +++ b/Source/Starfish.Service/Application/ConnectionContainer.cs @@ -9,7 +9,7 @@ public class ConnectionContainer { public event EventHandler OnConnected; - private static readonly ConcurrentDictionary _connections = new(StringComparer.OrdinalIgnoreCase); + private static readonly ConcurrentDictionary _connections = new(StringComparer.OrdinalIgnoreCase); private readonly IConfigurationApplicationService _service; @@ -23,22 +23,35 @@ public ConnectionContainer(IConfigurationApplicationService service) private async void OnClientConnected(object sender, ClientConnectedEventArgs e) { var raw = await _service.GetArchiveAsync(e.ConfigId); + if (string.IsNullOrEmpty(raw)) + { + return; + } + if (_connections.TryGetValue(e.ConfigId, out var connection)) { await connection.Channel.Writer.WriteAsync(Tuple.Create(e.ConnectionId, raw)); } } - public ConnectionInfo GetOrAdd(string configId, string connectionId) + public ConnectionChannel GetOrAdd(string configId, string connectionId, string connectionType) { - var connection = _connections.AddOrUpdate(configId, _ => ConnectionInfo.New(connectionId), (_, info) => - { - info.AddClient(connectionId); - return info; - }); + var channel = _connections.GetOrAdd(configId, _ => ConnectionChannel.New(configId)); + + channel.AddClient(connectionId, connectionType); OnConnected?.Invoke(this, new ClientConnectedEventArgs { ConfigId = configId, ConnectionId = connectionId }); - return connection; + return channel; + } + + public List GetConnections(string configId) + { + return _connections.TryGetValue(configId, out var info) ? info.Connections : null; + } + + public List GetConnections() + { + return _connections.SelectMany(t => t.Value.Connections ?? []).ToList(); } public void Remove(string configId, string connectionId) @@ -49,7 +62,7 @@ public void Remove(string configId, string connectionId) } info.RemoveClient(connectionId); - if (info.Clients.Count == 0) + if (info.Connections.Count == 0) { _connections.TryRemove(configId, out _); } @@ -68,34 +81,61 @@ public async Task HandleAsync(ConfigurationArchiveUpdatedEvent @event, MessageCo public class ClientConnectedEventArgs : EventArgs { public string ConfigId { get; set; } + public string ConnectionId { get; set; } + + public DateTime ConnectedTime { get; set; } } } -public class ConnectionInfo +public class ConnectionChannel { - public List Clients { get; } = []; + private ConnectionChannel(string configId) + { + ConfigId = configId; + Connections = []; + } + + public string ConfigId { get; } + + public List Connections { get; private set; } public Channel> Channel { get; private init; } - public static ConnectionInfo New(string connectionId) + public static ConnectionChannel New(string configId) { - var info = new ConnectionInfo + var info = new ConnectionChannel(configId) { Channel = System.Threading.Channels.Channel.CreateUnbounded>() }; - info.AddClient(connectionId); return info; } - public void AddClient(string connectionId) + public void AddClient(string connectionId, string type) { - Clients.Add(connectionId); + Connections.Add(new ConnectionInfo + { + ConfigurationId = ConfigId, + ConnectionId = connectionId, + ConnectionType = type, + ConnectedTime = DateTime.Now + }); } public void RemoveClient(string connectionId) { - Clients.Remove(connectionId); + Connections.RemoveAll(t => t.ConnectionId == connectionId); } +} + +public class ConnectionInfo +{ + public string ConfigurationId { get; set; } + + public string ConnectionId { get; set; } + + public string ConnectionType { get; set; } + + public DateTime ConnectedTime { get; set; } } \ No newline at end of file diff --git a/Source/Starfish.Service/Application/Subscribers/TokenEventSubscriber.cs b/Source/Starfish.Service/Application/Subscribers/TokenEventSubscriber.cs index 7bef37d..c1bccf5 100644 --- a/Source/Starfish.Service/Application/Subscribers/TokenEventSubscriber.cs +++ b/Source/Starfish.Service/Application/Subscribers/TokenEventSubscriber.cs @@ -27,7 +27,7 @@ public Task HandleAsync(UserAuthSucceedEvent message, MessageContext messageCont Type = "refresh_token", Token = message.RefreshToken, Issues = message.TokenIssueTime, - Subject = message.UserId.ToString(), + Subject = message.UserId, Expires = message.TokenIssueTime.AddDays(30) }; return _bus.SendAsync(command, cancellationToken); diff --git a/Source/Starfish.Service/Domain/Business/ConfigurationGeneralBusiness.cs b/Source/Starfish.Service/Domain/Business/ConfigurationGeneralBusiness.cs index a17bf6e..7a166df 100644 --- a/Source/Starfish.Service/Domain/Business/ConfigurationGeneralBusiness.cs +++ b/Source/Starfish.Service/Domain/Business/ConfigurationGeneralBusiness.cs @@ -96,6 +96,11 @@ protected override async Task InsertAsync(CancellationToken cancellationToken = } var aggregate = Configuration.Create(TeamId, Name); + if (!string.IsNullOrWhiteSpace(Secret)) + { + aggregate.SetSecret(Secret); + } + await ConfigurationRepository.InsertAsync(aggregate, true, cancellationToken); Id = aggregate.Id; } diff --git a/Source/Starfish.Service/Domain/Business/ConfigurationPublishBusiness.cs b/Source/Starfish.Service/Domain/Business/ConfigurationPublishBusiness.cs index 88f937d..5ade11e 100644 --- a/Source/Starfish.Service/Domain/Business/ConfigurationPublishBusiness.cs +++ b/Source/Starfish.Service/Domain/Business/ConfigurationPublishBusiness.cs @@ -1,5 +1,4 @@ using Nerosoft.Euonia.Business; -using Nerosoft.Euonia.Claims; using Nerosoft.Euonia.Domain; using Nerosoft.Starfish.Service; diff --git a/Source/Starfish.Service/Domain/Business/ConfigurationSecretBusiness.cs b/Source/Starfish.Service/Domain/Business/ConfigurationSecretBusiness.cs index 52a98f8..ec604af 100644 --- a/Source/Starfish.Service/Domain/Business/ConfigurationSecretBusiness.cs +++ b/Source/Starfish.Service/Domain/Business/ConfigurationSecretBusiness.cs @@ -22,7 +22,7 @@ protected async Task ExecuteAsync(string id, string secret, CancellationToken ca throw new ConfigurationNotFoundException(id); } - var permission = await TeamRepository.CheckPermissionAsync(id, Identity.UserId, cancellationToken); + var permission = await TeamRepository.CheckPermissionAsync(aggregate.TeamId, Identity.UserId, cancellationToken); switch (permission) { diff --git a/Source/Starfish.Service/Domain/Repositories/IConfigurationRepository.cs b/Source/Starfish.Service/Domain/Repositories/IConfigurationRepository.cs index 96bd4af..2577dc0 100644 --- a/Source/Starfish.Service/Domain/Repositories/IConfigurationRepository.cs +++ b/Source/Starfish.Service/Domain/Repositories/IConfigurationRepository.cs @@ -16,5 +16,5 @@ public interface IConfigurationRepository : IBaseRepository> GetItemListAsync(string id, string key, int skip, int count, CancellationToken cancellationToken = default); - Task GetItemCountAsync(string id, string key, CancellationToken cancellationToken = default); + Task GetItemCountAsync(string id, string key, Func,IQueryable> action, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/Source/Starfish.Service/Repository/Contexts/RelationalDatabaseModelBuilder.cs b/Source/Starfish.Service/Repository/Contexts/RelationalDatabaseModelBuilder.cs index 60f1a0f..c81a44d 100644 --- a/Source/Starfish.Service/Repository/Contexts/RelationalDatabaseModelBuilder.cs +++ b/Source/Starfish.Service/Repository/Contexts/RelationalDatabaseModelBuilder.cs @@ -166,6 +166,10 @@ protected override ModelBuilder ConfigureConfigurationArchive(ModelBuilder model entity.Property(t => t.Id) .IsRequired(); + + entity.HasOne(t => t.Configuration) + .WithOne(t => t.Archive) + .OnDelete(DeleteBehavior.Cascade); }); } diff --git a/Source/Starfish.Service/Repository/Repositories/ConfigurationRepository.cs b/Source/Starfish.Service/Repository/Repositories/ConfigurationRepository.cs index 2e9aa70..55bfd35 100644 --- a/Source/Starfish.Service/Repository/Repositories/ConfigurationRepository.cs +++ b/Source/Starfish.Service/Repository/Repositories/ConfigurationRepository.cs @@ -33,7 +33,7 @@ public Task> GetItemListAsync(string id, string key, int .AsNoTracking(); var expressions = new List>> { - t=>t.ConfigurationId == id + t => t.ConfigurationId == id }; if (!string.IsNullOrWhiteSpace(key)) @@ -49,15 +49,26 @@ public Task> GetItemListAsync(string id, string key, int .ToListAsync(cancellationToken); } - public Task GetItemCountAsync(string id, string key, CancellationToken cancellationToken = default) + public Task GetItemCountAsync(string id, string key, Func, IQueryable> action, CancellationToken cancellationToken = default) { var query = Context.Set() .AsQueryable(); + + if (action != null) + { + query = action(query); + } + var expressions = new List>> { - t=>t.ConfigurationId == id + t => t.Id > 0 }; + if (!string.IsNullOrWhiteSpace(id)) + { + expressions.Add(t => t.ConfigurationId == id); + } + if (!string.IsNullOrWhiteSpace(key)) { expressions.Add(t => t.Key.Contains(key)); diff --git a/Source/Starfish.Service/UseCases/Configs/GetConfigurationDetailUseCase.cs b/Source/Starfish.Service/UseCases/Configs/GetConfigurationDetailUseCase.cs index 23ba1f4..b4e03c3 100644 --- a/Source/Starfish.Service/UseCases/Configs/GetConfigurationDetailUseCase.cs +++ b/Source/Starfish.Service/UseCases/Configs/GetConfigurationDetailUseCase.cs @@ -1,8 +1,6 @@ using Nerosoft.Euonia.Application; -using Nerosoft.Euonia.Linq; using Nerosoft.Euonia.Mapping; using Nerosoft.Starfish.Domain; -using Nerosoft.Starfish.Repository; using Nerosoft.Starfish.Transit; namespace Nerosoft.Starfish.UseCases; diff --git a/Source/Starfish.Service/UseCases/Configs/GetConfigurationItemCountUseCase.cs b/Source/Starfish.Service/UseCases/Configs/GetConfigurationItemCountUseCase.cs index 5abb5c5..e1d3260 100644 --- a/Source/Starfish.Service/UseCases/Configs/GetConfigurationItemCountUseCase.cs +++ b/Source/Starfish.Service/UseCases/Configs/GetConfigurationItemCountUseCase.cs @@ -1,4 +1,5 @@ using Nerosoft.Euonia.Application; +using Nerosoft.Euonia.Claims; using Nerosoft.Starfish.Domain; namespace Nerosoft.Starfish.UseCases; @@ -27,20 +28,37 @@ internal record GetConfigurationItemCountInput(string Id, string Key) : IUseCase internal class GetConfigurationItemCountUseCase : IGetConfigurationItemCountUseCase { private readonly IConfigurationRepository _repository; + private readonly UserPrincipal _identity; /// /// 构造函数 /// /// - public GetConfigurationItemCountUseCase(IConfigurationRepository repository) + /// + public GetConfigurationItemCountUseCase(IConfigurationRepository repository, UserPrincipal identity) { _repository = repository; + _identity = identity; } /// public Task ExecuteAsync(GetConfigurationItemCountInput input, CancellationToken cancellationToken = default) { - return _repository.GetItemCountAsync(input.Id, input.Key, cancellationToken) + Func, IQueryable> action = null; + if (!_identity.IsInRoles(["SA"])) + { + var configSet = _repository.Context.Set(); + var teamSet = _repository.Context.Set(); + var teamMemberSet = _repository.Context.Set(); + var query = from c in configSet + join t in teamSet on c.TeamId equals t.Id + join tm in teamMemberSet on t.Id equals tm.TeamId + where tm.UserId == _identity.UserId + select c.Id; + action = q => q.Where(t => query.Contains(t.ConfigurationId)); + } + + return _repository.GetItemCountAsync(input.Id, input.Key, action, cancellationToken) .ContinueWith(t => new GetConfigurationItemCountOutput(t.Result), cancellationToken); } } \ No newline at end of file diff --git a/Source/Starfish.Webapi/Controllers/DashboardController.cs b/Source/Starfish.Webapi/Controllers/DashboardController.cs new file mode 100644 index 0000000..b9c761a --- /dev/null +++ b/Source/Starfish.Webapi/Controllers/DashboardController.cs @@ -0,0 +1,86 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Nerosoft.Starfish.Application; +using Nerosoft.Starfish.Transit; + +namespace Nerosoft.Starfish.Webapi.Controllers; + +/// +/// 客户端 +/// +[Route("api/[controller]")] +[ApiController] +public class DashboardController : ControllerBase +{ + private readonly ConnectionContainer _container; + private readonly IConfigurationApplicationService _configService; + private readonly ITeamApplicationService _teamService; + + /// + public DashboardController(ConnectionContainer container, IConfigurationApplicationService configService, ITeamApplicationService teamService) + { + _container = container; + _configService = configService; + _teamService = teamService; + } + + /// + /// 获取配置数量 + /// + /// + [HttpGet("configurations/count")] + public async Task GetConfigurationCountAsync() + { + var count = await _configService.CountAsync(new ConfigurationCriteria(), HttpContext.RequestAborted); + return Ok(count); + } + + /// + /// 获取配置项数量 + /// + /// + [HttpGet("configurations/items/count")] + public async Task GetConfigurationItemCountAsync() + { + var count = await _configService.GetItemCountAsync(string.Empty, string.Empty, HttpContext.RequestAborted); + return Ok(count); + } + + /// + /// 获取当前连接数量 + /// + /// + [HttpGet("connections/count")] + public async Task GetClientCountAsync() + { + var count = await Task.Run(() => _container.GetConnections().Count); + return Ok(count); + } + + /// + /// 获取连接列表 + /// + /// + [HttpGet("connections")] + public async Task GetConnectionsAsync() + { + var connections = await Task.Run(() => + { + return _container.GetConnections() + .OrderByDescending(t => t.ConnectedTime) + .Take(10); + }); + return Ok(connections); + } + + /// + /// 获取团队数量 + /// + /// + [HttpGet("teams/count")] + public async Task GetTeamCountAsync() + { + var count = await _teamService.CountAsync(new TeamCriteria(), HttpContext.RequestAborted); + return Ok(count); + } +} \ No newline at end of file diff --git a/Source/Starfish.Webapi/Controllers/EventStreamController.cs b/Source/Starfish.Webapi/Controllers/EventStreamController.cs index 17ab672..839647e 100644 --- a/Source/Starfish.Webapi/Controllers/EventStreamController.cs +++ b/Source/Starfish.Webapi/Controllers/EventStreamController.cs @@ -38,7 +38,7 @@ public async Task HandleAsync(string id, string teamId, string name, string secr Response.Headers.Append(HeaderNames.Connection, "close"); try { - var connection = _container.GetOrAdd(configId, HttpContext.Connection.Id); + var connection = _container.GetOrAdd(configId, HttpContext.Connection.Id, "event-stream"); while (await connection.Channel.Reader.WaitToReadAsync(HttpContext.RequestAborted)) { diff --git a/Source/Starfish.Webapi/Controllers/WebSocketController.cs b/Source/Starfish.Webapi/Controllers/WebSocketController.cs index f4366f0..7d9cde7 100644 --- a/Source/Starfish.Webapi/Controllers/WebSocketController.cs +++ b/Source/Starfish.Webapi/Controllers/WebSocketController.cs @@ -37,7 +37,7 @@ public async Task HandleAsync(string id, string teamId, string name, string secr using var socket = await HttpContext.WebSockets.AcceptWebSocketAsync(); - var connection = _container.GetOrAdd(configId, HttpContext.Connection.Id); + var connection = _container.GetOrAdd(configId, HttpContext.Connection.Id, "websocket"); await Task.WhenAny(MonitorChannelAsync(connection.Channel, socket), MonitorClientAsync(socket)); @@ -98,6 +98,5 @@ private Task AuthAsync(string id, string teamId, string name, string sec var service = HttpContext.RequestServices.GetRequiredService(); return service.AuthorizeAsync(id, teamId, name, secret, HttpContext.RequestAborted); - } } \ No newline at end of file diff --git a/Source/Starfish.Webapi/Startup.cs b/Source/Starfish.Webapi/Startup.cs index e3ec420..ae16185 100644 --- a/Source/Starfish.Webapi/Startup.cs +++ b/Source/Starfish.Webapi/Startup.cs @@ -46,12 +46,11 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApp { app.UseDeveloperExceptionPage(); app.UseSwagger(); - } - + lifetime.ApplicationStarted.Register(() => OnStarted(app)); lifetime.ApplicationStopping.Register(() => OnStopping(app)); - lifetime.ApplicationStopped.Register(() => OnStarted(app)); + lifetime.ApplicationStopped.Register(() => OnStopped(app)); app.UseWebSockets(); @@ -79,16 +78,19 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApp private void OnStarted(IApplicationBuilder app) { + app.ApplicationServices.GetService().CreateLogger().LogInformation("Application started"); //"On-started" logic } private void OnStopping(IApplicationBuilder app) { + app.ApplicationServices.GetService().CreateLogger().LogInformation("Application stopping"); //"On-stopping" logic } private void OnStopped(IApplicationBuilder app) { + app.ApplicationServices.GetService().CreateLogger().LogInformation("Application stopped"); //"On-stopped" logic } } \ No newline at end of file diff --git a/Source/Starfish.Webapp/Pages/Home.razor b/Source/Starfish.Webapp/Pages/Home.razor index 91cb421..e8162ce 100644 --- a/Source/Starfish.Webapp/Pages/Home.razor +++ b/Source/Starfish.Webapp/Pages/Home.razor @@ -2,4 +2,114 @@ @attribute [Authorize] +@inject IDashboardApi DashboardApi + Home + + + + + +
+

@Resources.IDS_HOME_LABEL_CONNECTIONS

+

@ConnectionCount

+
+ +
+ +
+
+
+
+ + + +
+

@Resources.IDS_HOME_LABEL_CONFIGURATIONS

+

@ConfigurationItemCount/@ConfigurationCount

+
+ +
+ +
+
+
+
+ + + +
+

@Resources.IDS_HOME_LABEL_TEAMS

+

@TeamCount

+
+ +
+ +
+
+
+
+
+ + + + + + + @* + @context.ConfigurationId + *@ + + + + + + + + +@code { + + [CascadingParameter] + private Task AuthenticationState { get; set; } + + private UserPrincipal Identity { get; set; } + + private int ConnectionCount { get; set; } + + private int TeamCount { get; set; } + + private int ConfigurationCount { get; set; } + + private int ConfigurationItemCount { get; set; } + + private GridItemsProvider _provider; + + private PaginationState Pagination { get; } = new() { ItemsPerPage = Constants.Query.Count }; + + protected override async Task OnInitializedAsync() + { + var user = await AuthenticationState; + + Identity = new UserPrincipal(user.User); + + var tasks = new List + { + DashboardApi.GetConnectionCountAsync().ContinueWith(task => ConnectionCount= task.Result.EnsureSuccess()), + DashboardApi.GetTeamCountAsync().ContinueWith(task => TeamCount= task.Result.EnsureSuccess()), + DashboardApi.GetConfigurationCountAsync().ContinueWith(task=> ConfigurationCount = task.Result.EnsureSuccess()), + DashboardApi.GetConfigurationItemCountAsync().ContinueWith(task=> ConfigurationItemCount = task.Result.EnsureSuccess()) + }; + await Task.WhenAll(tasks).Guard(); + + _provider = async request => + { + List items = null; + await DashboardApi.GetConnectionsListAsync(request.CancellationToken) + .EnsureSuccess(result => items = result, request.CancellationToken); + return GridItemsProviderResult.From(items, 0); + }; + } + +} \ No newline at end of file diff --git a/Source/Starfish.Webapp/Pages/User/Index.razor b/Source/Starfish.Webapp/Pages/User/Index.razor index 2f01f54..882e060 100644 --- a/Source/Starfish.Webapp/Pages/User/Index.razor +++ b/Source/Starfish.Webapp/Pages/User/Index.razor @@ -106,5 +106,4 @@ { await DialogService.ShowDialogAsync(id, new DialogParameters { PreventDismissOnOverlayClick = true }); } - } \ No newline at end of file diff --git a/Source/Starfish.Webapp/Properties/Resources.resx b/Source/Starfish.Webapp/Properties/Resources.resx index b20cb57..249e0c5 100644 --- a/Source/Starfish.Webapp/Properties/Resources.resx +++ b/Source/Starfish.Webapp/Properties/Resources.resx @@ -342,6 +342,30 @@ Sync to Redis + + Configuration + + + Connected Time + + + Connection Id + + + Connection Type + + + Configurations + + + Configuration items + + + Connections + + + Teams + Login diff --git a/Source/Starfish.Webapp/Properties/Resources.zh-Hans.resx b/Source/Starfish.Webapp/Properties/Resources.zh-Hans.resx index 9bc2745..67f30c1 100644 --- a/Source/Starfish.Webapp/Properties/Resources.zh-Hans.resx +++ b/Source/Starfish.Webapp/Properties/Resources.zh-Hans.resx @@ -192,9 +192,24 @@ 删除配置 + + 描述 + Id + + 名称 + + + 发布时间 + + + 状态 + + + 版本 + 配置详情 @@ -327,6 +342,30 @@ 同步到Redis + + 配置 + + + 连接时间 + + + 连接Id + + + 连接方式 + + + 配置 + + + 配置项 + + + 连接 + + + 团队 + 登录 @@ -561,19 +600,4 @@ 重置密码 - - 描述 - - - 名称 - - - 发布时间 - - - 状态 - - - 版本 - \ No newline at end of file diff --git a/Source/Starfish.Webapp/Properties/Resources.zh-Hant.resx b/Source/Starfish.Webapp/Properties/Resources.zh-Hant.resx index 7c06b55..34cd414 100644 --- a/Source/Starfish.Webapp/Properties/Resources.zh-Hant.resx +++ b/Source/Starfish.Webapp/Properties/Resources.zh-Hant.resx @@ -192,15 +192,51 @@ 刪除配置 + + 描述 + Id + + 名稱 + + + 發佈時間 + + + 狀態 + + + 版本 + + + 配置詳情 + + + 描述 + + + 名稱 + + + 密鑰 + + + 選擇團隊 + + + 新增配置 + 從Json創建 從文本創建 + + 編輯配置 + 編輯Json @@ -234,6 +270,15 @@ + + 名稱 + + + 狀態 + + + 團隊 + @@ -297,6 +342,36 @@ 同步到Redis + + 配置 + + + 配置Id + + + 配置名稱 + + + 連接時間 + + + 連接Id + + + 連接方式 + + + 配置 + + + 配置項 + + + 連接 + + + 團隊 + 登入 @@ -531,49 +606,4 @@ 重設密碼 - - 描述 - - - 名稱 - - - 發佈時間 - - - 狀態 - - - 版本 - - - 配置詳情 - - - 描述 - - - 名稱 - - - 密鑰 - - - 選擇團隊 - - - 新增配置 - - - 編輯配置 - - - 名稱 - - - 狀態 - - - 團隊 - \ No newline at end of file diff --git a/Source/Starfish.Webapp/Rest/Defines/IDashboardApi.cs b/Source/Starfish.Webapp/Rest/Defines/IDashboardApi.cs new file mode 100644 index 0000000..ffed27e --- /dev/null +++ b/Source/Starfish.Webapp/Rest/Defines/IDashboardApi.cs @@ -0,0 +1,22 @@ +using Nerosoft.Starfish.Transit; +using Refit; + +namespace Nerosoft.Starfish.Webapp.Rest; + +public interface IDashboardApi +{ + [Get("/api/dashboard/connections/count")] + Task> GetConnectionCountAsync(CancellationToken cancellationToken = default); + + [Get("/api/dashboard/connections")] + Task>> GetConnectionsListAsync(CancellationToken cancellationToken = default); + + [Get("/api/dashboard/configurations/count")] + Task> GetConfigurationCountAsync(CancellationToken cancellationToken = default); + + [Get("/api/dashboard/configurations/items/count")] + Task> GetConfigurationItemCountAsync(CancellationToken cancellationToken = default); + + [Get("/api/dashboard/teams/count")] + Task> GetTeamCountAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/Source/Starfish.Webapp/Rest/ServiceCollectionExtensions.cs b/Source/Starfish.Webapp/Rest/ServiceCollectionExtensions.cs index 1aeb5a8..a519c44 100644 --- a/Source/Starfish.Webapp/Rest/ServiceCollectionExtensions.cs +++ b/Source/Starfish.Webapp/Rest/ServiceCollectionExtensions.cs @@ -40,7 +40,8 @@ public static IServiceCollection AddHttpClientApi(this IServiceCollection servic .AddTransient(provider => provider.GetRestService(HTTP_CLIENT_NAME)) .AddTransient(provider => provider.GetRestService(HTTP_CLIENT_NAME)) .AddTransient(provider => provider.GetRestService(HTTP_CLIENT_NAME)) - .AddTransient(provider => provider.GetRestService(HTTP_CLIENT_NAME)); + .AddTransient(provider => provider.GetRestService(HTTP_CLIENT_NAME)) + .AddTransient(provider => provider.GetRestService(HTTP_CLIENT_NAME)); services.AddHttpClient(HTTP_CLIENT_NAME, (provider, client) => { diff --git a/Source/Starfish.Webapp/wwwroot/appsettings.json b/Source/Starfish.Webapp/wwwroot/appsettings.json index fe1aecb..a4e849a 100644 --- a/Source/Starfish.Webapp/wwwroot/appsettings.json +++ b/Source/Starfish.Webapp/wwwroot/appsettings.json @@ -1,8 +1,8 @@ { "Starfish": { - "AppId": "starfish.webapp", - "AppSecret": "0oDjfcWJiO", - "Environment": "DEV", + "Id": "5lNc9zQGdG7", + "Name": "starfish/webapp/appsettings.dev", + "Secret": "0oDjfcWJiO", "Host": "http://localhost:5229" }, "Api": {