From 1e22ebc930ca45790cfa115ab4aba36b08450d84 Mon Sep 17 00:00:00 2001 From: "Minjie.ZHOU" Date: Tue, 21 Oct 2025 00:48:33 +0800 Subject: [PATCH 01/17] docs: document dynamic role management rollout --- CHANGELOG.md | 3 + .../Controllers/AdminController.cs | 7 +- .../Controllers/HomeController.cs | 2 +- .../Controllers/RoleController.cs | 208 +++++++++++++ .../Controllers/UserController.cs | 23 +- .../Models/RoleVM.cs | 27 ++ .../Models/UserVM.cs | 4 +- .../Resources/Messages.cs | 2 + .../Resources/Messages.en-US.resx | 6 + .../Resources/Messages.zh-CN.resx | 6 + .../SystemRoleConstants.cs | 13 + .../IRoleDefinitionRepository.cs | 8 + .../RoleDefinition.cs | 37 +++ .../UserRole.cs | 20 +- .../EnsureTables.cs | 1 + .../FreesqlRepositoryServiceRegister.cs | 1 + .../RoleDefinitionRepository.cs | 13 + .../SysInitRepository.cs | 49 ++- .../MongodbRepositoryServiceRegister.cs | 4 +- .../RoleDefinitionRepository.cs | 17 ++ .../SysInitRepository.cs | 54 +++- .../IPermissionService.cs | 4 + .../IRoleService.cs | 16 + .../IUserService.cs | 6 +- .../PermissionService.cs | 56 +++- src/AgileConfig.Server.Service/RoleService.cs | 139 +++++++++ .../ServiceCollectionExt.cs | 1 + src/AgileConfig.Server.Service/UserService.cs | 63 +++- .../react-ui-antd/config/routes.ts | 7 + .../react-ui-antd/src/locales/en-US/menu.ts | 1 + .../react-ui-antd/src/locales/en-US/pages.ts | 24 ++ .../react-ui-antd/src/locales/zh-CN/menu.ts | 1 + .../react-ui-antd/src/locales/zh-CN/pages.ts | 24 ++ .../react-ui-antd/src/pages/Role/data.d.ts | 17 ++ .../react-ui-antd/src/pages/Role/index.tsx | 279 ++++++++++++++++++ .../src/pages/User/comps/updateUser.tsx | 58 ++-- .../react-ui-antd/src/pages/User/data.d.ts | 5 +- .../react-ui-antd/src/pages/User/index.tsx | 128 +++++--- .../react-ui-antd/src/services/role.ts | 36 +++ 39 files changed, 1237 insertions(+), 133 deletions(-) create mode 100644 src/AgileConfig.Server.Apisite/Controllers/RoleController.cs create mode 100644 src/AgileConfig.Server.Apisite/Models/RoleVM.cs create mode 100644 src/AgileConfig.Server.Common/SystemRoleConstants.cs create mode 100644 src/AgileConfig.Server.Data.Abstraction/IRoleDefinitionRepository.cs create mode 100644 src/AgileConfig.Server.Data.Entity/RoleDefinition.cs create mode 100644 src/AgileConfig.Server.Data.Repository.Freesql/RoleDefinitionRepository.cs create mode 100644 src/AgileConfig.Server.Data.Repository.Mongodb/RoleDefinitionRepository.cs create mode 100644 src/AgileConfig.Server.IService/IRoleService.cs create mode 100644 src/AgileConfig.Server.Service/RoleService.cs create mode 100644 src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/data.d.ts create mode 100644 src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx create mode 100644 src/AgileConfig.Server.UI/react-ui-antd/src/services/role.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c84dc19a..66ddc98d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Change log ------------------------------ +[Unreleased] +* Introduced dynamic role management with CRUD APIs and UI allowing custom permission assignments. + [1.10.0] * Use publish timeline virtual id to compare the version between client. To enable this feature the client should use version >=1.8.0 . diff --git a/src/AgileConfig.Server.Apisite/Controllers/AdminController.cs b/src/AgileConfig.Server.Apisite/Controllers/AdminController.cs index 9fab1f10..7167c1d9 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/AdminController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/AdminController.cs @@ -79,7 +79,8 @@ private async Task LoginSuccessful(string userName) { var user = (await _userService.GetUsersByNameAsync(userName)).First(); var userRoles = await _userService.GetUserRolesAsync(user.Id); - var jwt = _jwtService.GetToken(user.Id, user.UserName, userRoles.Any(r => r == Role.Admin || r == Role.SuperAdmin)); + var jwt = _jwtService.GetToken(user.Id, user.UserName, + userRoles.Any(r => r.Id == SystemRoleConstants.AdminId || r.Id == SystemRoleConstants.SuperAdminId)); var userFunctions = await _permissionService.GetUserPermission(user.Id); _tinyEventBus.Fire(new LoginEvent(user.UserName)); @@ -89,7 +90,7 @@ private async Task LoginSuccessful(string userName) status = "ok", token = jwt, type = "Bearer", - currentAuthority = userRoles.Select(r => r.ToString()), + currentAuthority = userRoles.Select(r => r.Code), currentFunctions = userFunctions }; } @@ -140,7 +141,7 @@ public async Task OidcLoginByCode(string code) Source = UserSource.SSO }; await _userService.AddAsync(newUser); - await _userService.UpdateUserRolesAsync(newUser.Id, new List { Role.NormalUser }); + await _userService.UpdateUserRolesAsync(newUser.Id, new List { SystemRoleConstants.OperatorId }); } else if (user.Status == UserStatus.Deleted) { diff --git a/src/AgileConfig.Server.Apisite/Controllers/HomeController.cs b/src/AgileConfig.Server.Apisite/Controllers/HomeController.cs index 24cde12c..dea61ad2 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/HomeController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/HomeController.cs @@ -69,7 +69,7 @@ public async Task Current() { userId = userId, userName, - currentAuthority = userRoles.Select(r => r.ToString()), + currentAuthority = userRoles.Select(r => r.Code), currentFunctions = userFunctions } }); diff --git a/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs b/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs new file mode 100644 index 00000000..8b88f0d1 --- /dev/null +++ b/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs @@ -0,0 +1,208 @@ +using AgileConfig.Server.Apisite.Filters; +using AgileConfig.Server.Apisite.Models; +using AgileConfig.Server.Common; +using AgileConfig.Server.Common.Resources; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.IService; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; + +namespace AgileConfig.Server.Apisite.Controllers +{ + [Authorize] + public class RoleController : Controller + { + private readonly IRoleService _roleService; + + private static readonly IReadOnlyList SupportedFunctions = new List + { + "GLOBAL_" + Functions.App_Add, + "GLOBAL_" + Functions.App_Edit, + "GLOBAL_" + Functions.App_Delete, + "GLOBAL_" + Functions.App_Auth, + + "GLOBAL_" + Functions.Config_Add, + "GLOBAL_" + Functions.Config_Edit, + "GLOBAL_" + Functions.Config_Delete, + "GLOBAL_" + Functions.Config_Publish, + "GLOBAL_" + Functions.Config_Offline, + + "GLOBAL_" + Functions.Node_Add, + "GLOBAL_" + Functions.Node_Delete, + + "GLOBAL_" + Functions.Client_Disconnect, + + "GLOBAL_" + Functions.User_Add, + "GLOBAL_" + Functions.User_Edit, + "GLOBAL_" + Functions.User_Delete, + + "GLOBAL_" + Functions.Role_Add, + "GLOBAL_" + Functions.Role_Edit, + "GLOBAL_" + Functions.Role_Delete + }; + + public RoleController(IRoleService roleService) + { + _roleService = roleService; + } + + [HttpGet] + public async Task List() + { + var roles = await _roleService.GetAllAsync(); + var vms = roles.Select(ToViewModel).OrderByDescending(r => r.IsSystem).ThenBy(r => r.Name).ToList(); + + return Json(new + { + success = true, + data = vms + }); + } + + [HttpGet] + public IActionResult SupportedPermissions() + { + return Json(new + { + success = true, + data = SupportedFunctions + }); + } + + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Role.Add", Functions.Role_Add })] + [HttpPost] + public async Task Add([FromBody] RoleVM model) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + try + { + var role = new RoleDefinition + { + Id = model.Id, + Code = model.Code, + Name = model.Name, + Description = model.Description ?? string.Empty, + IsSystem = false + }; + + await _roleService.CreateAsync(role, model.Functions ?? Enumerable.Empty()); + + return Json(new { success = true }); + } + catch (Exception ex) + { + return Json(new + { + success = false, + message = ex.Message + }); + } + } + + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Role.Edit", Functions.Role_Edit })] + [HttpPost] + public async Task Edit([FromBody] RoleVM model) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + try + { + var role = new RoleDefinition + { + Id = model.Id, + Code = model.Code, + Name = model.Name, + Description = model.Description ?? string.Empty, + IsSystem = model.IsSystem + }; + + var result = await _roleService.UpdateAsync(role, model.Functions ?? Enumerable.Empty()); + + return Json(new + { + success = result, + message = result ? string.Empty : Messages.UpdateRoleFailed + }); + } + catch (Exception ex) + { + return Json(new + { + success = false, + message = ex.Message + }); + } + } + + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Role.Delete", Functions.Role_Delete })] + [HttpPost] + public async Task Delete(string id) + { + if (string.IsNullOrWhiteSpace(id)) + { + throw new ArgumentNullException(nameof(id)); + } + + try + { + var result = await _roleService.DeleteAsync(id); + return Json(new + { + success = result, + message = result ? string.Empty : Messages.DeleteRoleFailed + }); + } + catch (Exception ex) + { + return Json(new + { + success = false, + message = ex.Message + }); + } + } + + private static RoleVM ToViewModel(RoleDefinition role) + { + return new RoleVM + { + Id = role.Id, + Code = role.Code, + Name = role.Name, + Description = role.Description, + IsSystem = role.IsSystem, + Functions = ParseFunctions(role.FunctionsJson) + }; + } + + private static List ParseFunctions(string json) + { + if (string.IsNullOrWhiteSpace(json)) + { + return new List(); + } + + try + { + var funcs = JsonSerializer.Deserialize>(json); + return funcs ?? new List(); + } + catch + { + return new List(); + } + } + } +} diff --git a/src/AgileConfig.Server.Apisite/Controllers/UserController.cs b/src/AgileConfig.Server.Apisite/Controllers/UserController.cs index 2057e8b2..43891047 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/UserController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/UserController.cs @@ -66,8 +66,9 @@ public async Task Search(string userName, string team, int curren Id = item.Id, UserName = item.UserName, Team = item.Team, - UserRoles = roles, - UserRoleNames = roles.Select(r => r.ToDesc()).ToList() + UserRoleIds = roles.Select(r => r.Id).ToList(), + UserRoleNames = roles.Select(r => r.Name).ToList(), + UserRoleCodes = roles.Select(r => r.Code).ToList() }; vms.Add(vm); } @@ -112,7 +113,13 @@ public async Task Add([FromBody] UserVM model) user.UserName = model.UserName; var addUserResult = await _userService.AddAsync(user); - var addUserRoleResult = await _userService.UpdateUserRolesAsync(user.Id, model.UserRoles); + var roleIds = model.UserRoleIds?.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList() ?? new List(); + if (!roleIds.Any()) + { + roleIds.Add(SystemRoleConstants.OperatorId); + } + + var addUserRoleResult = await _userService.UpdateUserRolesAsync(user.Id, roleIds); if (addUserResult) { @@ -149,7 +156,13 @@ public async Task Edit([FromBody] UserVM model) user.UpdateTime = DateTime.Now; var result = await _userService.UpdateAsync(user); - var reuslt1 = await _userService.UpdateUserRolesAsync(user.Id, model.UserRoles); + var roleIds = model.UserRoleIds?.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList() ?? new List(); + if (!roleIds.Any()) + { + roleIds.Add(SystemRoleConstants.OperatorId); + } + + var reuslt1 = await _userService.UpdateUserRolesAsync(user.Id, roleIds); if (result) { @@ -235,7 +248,7 @@ public async Task Delete(string userId) [HttpGet] public async Task AdminUsers() { - var adminUsers = await _userService.GetUsersByRoleAsync(Role.Admin); + var adminUsers = await _userService.GetUsersByRoleAsync(SystemRoleConstants.AdminId); adminUsers = adminUsers.Where(x => x.Status == UserStatus.Normal).ToList(); return Json(new { diff --git a/src/AgileConfig.Server.Apisite/Models/RoleVM.cs b/src/AgileConfig.Server.Apisite/Models/RoleVM.cs new file mode 100644 index 00000000..a56d7680 --- /dev/null +++ b/src/AgileConfig.Server.Apisite/Models/RoleVM.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; + +namespace AgileConfig.Server.Apisite.Models +{ + [ExcludeFromCodeCoverage] + public class RoleVM + { + public string Id { get; set; } + + [Required] + [MaxLength(64)] + public string Code { get; set; } + + [Required] + [MaxLength(128)] + public string Name { get; set; } + + [MaxLength(512)] + public string Description { get; set; } + + public bool IsSystem { get; set; } + + public List Functions { get; set; } + } +} diff --git a/src/AgileConfig.Server.Apisite/Models/UserVM.cs b/src/AgileConfig.Server.Apisite/Models/UserVM.cs index 7248bb03..1764623f 100644 --- a/src/AgileConfig.Server.Apisite/Models/UserVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/UserVM.cs @@ -23,10 +23,12 @@ public class UserVM [MaxLength(50, ErrorMessage = "团队长度不能超过50位")] public string Team { get; set; } - public List UserRoles { get; set; } + public List UserRoleIds { get; set; } public List UserRoleNames { get; set; } + public List UserRoleCodes { get; set; } + public UserStatus Status { get; set; } } } diff --git a/src/AgileConfig.Server.Common/Resources/Messages.cs b/src/AgileConfig.Server.Common/Resources/Messages.cs index a2ae1ae5..5cb71c48 100644 --- a/src/AgileConfig.Server.Common/Resources/Messages.cs +++ b/src/AgileConfig.Server.Common/Resources/Messages.cs @@ -195,5 +195,7 @@ public static string GetString(string name, CultureInfo culture, params object[] public static string UpdateUserFailed => GetString(nameof(UpdateUserFailed)); public static string ResetUserPasswordFailed => GetString(nameof(ResetUserPasswordFailed)); public static string DeleteUserFailed => GetString(nameof(DeleteUserFailed)); + public static string UpdateRoleFailed => GetString(nameof(UpdateRoleFailed)); + public static string DeleteRoleFailed => GetString(nameof(DeleteRoleFailed)); } } diff --git a/src/AgileConfig.Server.Common/Resources/Messages.en-US.resx b/src/AgileConfig.Server.Common/Resources/Messages.en-US.resx index c04b66f9..9242dd2a 100644 --- a/src/AgileConfig.Server.Common/Resources/Messages.en-US.resx +++ b/src/AgileConfig.Server.Common/Resources/Messages.en-US.resx @@ -421,4 +421,10 @@ Failed to delete user, please check error logs + + Failed to update role, please check error logs + + + Failed to delete role, please check error logs + diff --git a/src/AgileConfig.Server.Common/Resources/Messages.zh-CN.resx b/src/AgileConfig.Server.Common/Resources/Messages.zh-CN.resx index 5249cfb0..1834d13e 100644 --- a/src/AgileConfig.Server.Common/Resources/Messages.zh-CN.resx +++ b/src/AgileConfig.Server.Common/Resources/Messages.zh-CN.resx @@ -421,4 +421,10 @@ 删除用户失败,请查看错误日志 + + 更新角色失败,请查看错误日志 + + + 删除角色失败,请查看错误日志 + diff --git a/src/AgileConfig.Server.Common/SystemRoleConstants.cs b/src/AgileConfig.Server.Common/SystemRoleConstants.cs new file mode 100644 index 00000000..007979de --- /dev/null +++ b/src/AgileConfig.Server.Common/SystemRoleConstants.cs @@ -0,0 +1,13 @@ +namespace AgileConfig.Server.Common +{ + public static class SystemRoleConstants + { + public const string SuperAdminId = "00000000-0000-0000-0000-000000000001"; + public const string AdminId = "00000000-0000-0000-0000-000000000002"; + public const string OperatorId = "00000000-0000-0000-0000-000000000003"; + + public const string SuperAdminCode = "SuperAdmin"; + public const string AdminCode = "Admin"; + public const string OperatorCode = "NormalUser"; + } +} diff --git a/src/AgileConfig.Server.Data.Abstraction/IRoleDefinitionRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IRoleDefinitionRepository.cs new file mode 100644 index 00000000..0b40d70c --- /dev/null +++ b/src/AgileConfig.Server.Data.Abstraction/IRoleDefinitionRepository.cs @@ -0,0 +1,8 @@ +using AgileConfig.Server.Data.Entity; + +namespace AgileConfig.Server.Data.Abstraction +{ + public interface IRoleDefinitionRepository : IRepository + { + } +} diff --git a/src/AgileConfig.Server.Data.Entity/RoleDefinition.cs b/src/AgileConfig.Server.Data.Entity/RoleDefinition.cs new file mode 100644 index 00000000..e44a5f31 --- /dev/null +++ b/src/AgileConfig.Server.Data.Entity/RoleDefinition.cs @@ -0,0 +1,37 @@ +using FreeSql.DataAnnotations; +using MongoDB.Bson.Serialization.Attributes; +using System; + +namespace AgileConfig.Server.Data.Entity +{ + [Table(Name = "agc_role_definition")] + [OraclePrimaryKeyName("agc_role_definition_pk")] + public class RoleDefinition : IEntity + { + [Column(Name = "id", StringLength = 64)] + public string Id { get; set; } + + [Column(Name = "code", StringLength = 64)] + public string Code { get; set; } + + [Column(Name = "name", StringLength = 128)] + public string Name { get; set; } + + [Column(Name = "description", StringLength = 512)] + public string Description { get; set; } + + [Column(Name = "is_system")] + public bool IsSystem { get; set; } + + [Column(Name = "functions", StringLength = -1)] + public string FunctionsJson { get; set; } + + [Column(Name = "create_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + + [Column(Name = "update_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime? UpdateTime { get; set; } + } +} diff --git a/src/AgileConfig.Server.Data.Entity/UserRole.cs b/src/AgileConfig.Server.Data.Entity/UserRole.cs index 78b7b16a..e5c1adc1 100644 --- a/src/AgileConfig.Server.Data.Entity/UserRole.cs +++ b/src/AgileConfig.Server.Data.Entity/UserRole.cs @@ -1,14 +1,12 @@ using FreeSql.DataAnnotations; using System; -using System.ComponentModel; using MongoDB.Bson.Serialization.Attributes; -using AgileConfig.Server.Common; namespace AgileConfig.Server.Data.Entity { [Table(Name = "agc_user_role")] [OraclePrimaryKeyName("agc_user_role_pk")] - public class UserRole: IEntity + public class UserRole : IEntity { [Column(Name = "id", StringLength = 36)] public string Id { get; set; } @@ -16,22 +14,14 @@ public class UserRole: IEntity [Column(Name = "user_id", StringLength = 50)] public string UserId { get; set; } + [Column(Name = "role_id", StringLength = 64)] + public string RoleId { get; set; } + [Column(Name = "role")] - public Role Role { get; set; } + public int? LegacyRoleValue { get; set; } [Column(Name = "create_time")] [BsonDateTimeOptions(Kind = DateTimeKind.Local)] public DateTime CreateTime { get; set; } - - } - - public enum Role - { - [Description("Super Administrator")] - SuperAdmin = 0, - [Description("Administrator")] - Admin = 1, - [Description("Operator")] - NormalUser = 2, } } diff --git a/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs b/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs index f3f29ae7..4cf252ea 100644 --- a/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs +++ b/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs @@ -76,6 +76,7 @@ public static void Ensure(IFreeSql instance) instance.CodeFirst.SyncStructure(); instance.CodeFirst.SyncStructure(); instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); instance.CodeFirst.SyncStructure(); instance.CodeFirst.SyncStructure(); instance.CodeFirst.SyncStructure(); diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/FreesqlRepositoryServiceRegister.cs b/src/AgileConfig.Server.Data.Repository.Freesql/FreesqlRepositoryServiceRegister.cs index 55cf7580..732489cc 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/FreesqlRepositoryServiceRegister.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/FreesqlRepositoryServiceRegister.cs @@ -17,6 +17,7 @@ public void AddFixedRepositories(IServiceCollection sc) sc.AddScoped(); sc.AddScoped(); sc.AddScoped(); + sc.AddScoped(); sc.AddSingleton(); } diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/RoleDefinitionRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/RoleDefinitionRepository.cs new file mode 100644 index 00000000..da122765 --- /dev/null +++ b/src/AgileConfig.Server.Data.Repository.Freesql/RoleDefinitionRepository.cs @@ -0,0 +1,13 @@ +using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Data.Freesql; + +namespace AgileConfig.Server.Data.Repository.Freesql +{ + public class RoleDefinitionRepository : FreesqlRepository, IRoleDefinitionRepository + { + public RoleDefinitionRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) + { + } + } +} diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs index e1cfbfe6..e2c6daca 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs @@ -2,6 +2,9 @@ using AgileConfig.Server.Data.Abstraction; using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; +using System; +using System.Collections.Generic; +using System.Text.Json; namespace AgileConfig.Server.Data.Repository.Freesql; @@ -47,6 +50,8 @@ public bool InitSa(string password) var sql = freeSqlFactory.Create(); + EnsureSystemRoles(sql); + var user = new User(); user.Id = SystemSettings.SuperAdminId; user.Password = password; @@ -58,18 +63,21 @@ public bool InitSa(string password) sql.Insert(user).ExecuteAffrows(); + var now = DateTime.Now; var userRoles = new List(); userRoles.Add(new UserRole() { Id = Guid.NewGuid().ToString("N"), - Role = Role.SuperAdmin, - UserId = SystemSettings.SuperAdminId + RoleId = SystemRoleConstants.SuperAdminId, + UserId = SystemSettings.SuperAdminId, + CreateTime = now }); userRoles.Add(new UserRole() { Id = Guid.NewGuid().ToString("N"), - Role = Role.Admin, - UserId = SystemSettings.SuperAdminId + RoleId = SystemRoleConstants.AdminId, + UserId = SystemSettings.SuperAdminId, + CreateTime = now }); sql.Insert(userRoles).ExecuteAffrows(); @@ -111,4 +119,37 @@ public bool InitDefaultApp(string appName) return true; } + + private static void EnsureSystemRoles(IFreeSql sql) + { + EnsureRole(sql, SystemRoleConstants.SuperAdminId, SystemRoleConstants.SuperAdminCode, "Super Administrator"); + EnsureRole(sql, SystemRoleConstants.AdminId, SystemRoleConstants.AdminCode, "Administrator"); + EnsureRole(sql, SystemRoleConstants.OperatorId, SystemRoleConstants.OperatorCode, "Operator"); + } + + private static void EnsureRole(IFreeSql sql, string id, string code, string name) + { + var role = sql.Select().Where(x => x.Id == id).First(); + if (role == null) + { + sql.Insert(new RoleDefinition + { + Id = id, + Code = code, + Name = name, + Description = name, + IsSystem = true, + FunctionsJson = JsonSerializer.Serialize(new List()), + CreateTime = DateTime.Now + }).ExecuteAffrows(); + } + else + { + role.Code = code; + role.Name = name; + role.Description = name; + role.IsSystem = true; + sql.Update().SetSource(role).ExecuteAffrows(); + } + } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/MongodbRepositoryServiceRegister.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/MongodbRepositoryServiceRegister.cs index 8ad9bf5a..6f0c23c8 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/MongodbRepositoryServiceRegister.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/MongodbRepositoryServiceRegister.cs @@ -1,4 +1,5 @@ -using AgileConfig.Server.Data.Abstraction.DbProvider; +using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Abstraction.DbProvider; namespace AgileConfig.Server.Data.Repository.Mongodb { @@ -15,6 +16,7 @@ public void AddFixedRepositories(IServiceCollection sc) sc.AddScoped(); sc.AddScoped(); sc.AddScoped(); + sc.AddScoped(); sc.AddSingleton(); } diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/RoleDefinitionRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/RoleDefinitionRepository.cs new file mode 100644 index 00000000..3da65404 --- /dev/null +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/RoleDefinitionRepository.cs @@ -0,0 +1,17 @@ +using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using Microsoft.Extensions.Configuration; + +namespace AgileConfig.Server.Data.Repository.Mongodb +{ + public class RoleDefinitionRepository : MongodbRepository, IRoleDefinitionRepository + { + public RoleDefinitionRepository(string? connectionString) : base(connectionString) + { + } + + public RoleDefinitionRepository(IConfiguration configuration) : base(configuration) + { + } + } +} diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs index f2a7b174..04652ea2 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs @@ -1,4 +1,9 @@ using AgileConfig.Server.Common; +using AgileConfig.Server.Data.Entity; +using MongoDB.Driver; +using System; +using System.Collections.Generic; +using System.Text.Json; namespace AgileConfig.Server.Data.Repository.Mongodb; @@ -14,6 +19,7 @@ public SysInitRepository(IConfiguration configuration) private MongodbAccess _settingAccess => new MongodbAccess(_connectionString); private MongodbAccess _userAccess => new MongodbAccess(_connectionString); private MongodbAccess _userRoleAccess => new MongodbAccess(_connectionString); + private MongodbAccess _roleAccess => new MongodbAccess(_connectionString); private MongodbAccess _appAccess => new MongodbAccess(_connectionString); private readonly IConfiguration _configuration; @@ -48,6 +54,8 @@ public bool InitSa(string password) var newSalt = Guid.NewGuid().ToString("N"); password = Encrypt.Md5((password + newSalt)); + EnsureSystemRoles(); + var user = new User(); user.Id = SystemSettings.SuperAdminId; user.Password = password; @@ -59,18 +67,21 @@ public bool InitSa(string password) _userAccess.Collection.InsertOne(user); + var now = DateTime.Now; var userRoles = new List(); userRoles.Add(new UserRole() { Id = Guid.NewGuid().ToString("N"), - Role = Role.SuperAdmin, - UserId = SystemSettings.SuperAdminId + RoleId = SystemRoleConstants.SuperAdminId, + UserId = SystemSettings.SuperAdminId, + CreateTime = now }); userRoles.Add(new UserRole() { Id = Guid.NewGuid().ToString("N"), - Role = Role.Admin, - UserId = SystemSettings.SuperAdminId + RoleId = SystemRoleConstants.AdminId, + UserId = SystemSettings.SuperAdminId, + CreateTime = now }); _userRoleAccess.Collection.InsertMany(userRoles); @@ -111,4 +122,39 @@ public bool InitDefaultApp(string appName) return true; } + + private void EnsureSystemRoles() + { + EnsureRole(SystemRoleConstants.SuperAdminId, SystemRoleConstants.SuperAdminCode, "Super Administrator"); + EnsureRole(SystemRoleConstants.AdminId, SystemRoleConstants.AdminCode, "Administrator"); + EnsureRole(SystemRoleConstants.OperatorId, SystemRoleConstants.OperatorCode, "Operator"); + } + + private void EnsureRole(string id, string code, string name) + { + var role = _roleAccess.MongoQueryable.FirstOrDefault(x => x.Id == id); + if (role == null) + { + _roleAccess.Collection.InsertOne(new RoleDefinition + { + Id = id, + Code = code, + Name = name, + Description = name, + IsSystem = true, + FunctionsJson = JsonSerializer.Serialize(new List()), + CreateTime = DateTime.Now + }); + } + else + { + role.Code = code; + role.Name = name; + role.Description = name; + role.IsSystem = true; + role.FunctionsJson = role.FunctionsJson ?? JsonSerializer.Serialize(new List()); + role.UpdateTime = DateTime.Now; + _roleAccess.Collection.ReplaceOne(x => x.Id == id, role, new ReplaceOptions { IsUpsert = true }); + } + } } \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IPermissionService.cs b/src/AgileConfig.Server.IService/IPermissionService.cs index 8ec6f6b8..a3a57104 100644 --- a/src/AgileConfig.Server.IService/IPermissionService.cs +++ b/src/AgileConfig.Server.IService/IPermissionService.cs @@ -25,6 +25,10 @@ public static class Functions public const string User_Add = "USER_ADD"; public const string User_Edit = "USER_EDIT"; public const string User_Delete = "USER_DELETE"; + + public const string Role_Add = "ROLE_ADD"; + public const string Role_Edit = "ROLE_EDIT"; + public const string Role_Delete = "ROLE_DELETE"; } public interface IPermissionService diff --git a/src/AgileConfig.Server.IService/IRoleService.cs b/src/AgileConfig.Server.IService/IRoleService.cs new file mode 100644 index 00000000..8d18aab6 --- /dev/null +++ b/src/AgileConfig.Server.IService/IRoleService.cs @@ -0,0 +1,16 @@ +using AgileConfig.Server.Data.Entity; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AgileConfig.Server.IService +{ + public interface IRoleService + { + Task> GetAllAsync(); + Task GetAsync(string id); + Task GetByCodeAsync(string code); + Task CreateAsync(RoleDefinition role, IEnumerable functions); + Task UpdateAsync(RoleDefinition role, IEnumerable functions); + Task DeleteAsync(string id); + } +} diff --git a/src/AgileConfig.Server.IService/IUserService.cs b/src/AgileConfig.Server.IService/IUserService.cs index 9bc566fb..c001ef00 100644 --- a/src/AgileConfig.Server.IService/IUserService.cs +++ b/src/AgileConfig.Server.IService/IUserService.cs @@ -12,7 +12,7 @@ public interface IUserService: IDisposable Task GetUserAsync(string userId); Task> GetUsersByNameAsync(string userName); - Task> GetUserRolesAsync(string userId); + Task> GetUserRolesAsync(string userId); Task AddAsync(User user); @@ -21,12 +21,12 @@ public interface IUserService: IDisposable Task UpdateAsync(User user); - Task UpdateUserRolesAsync(string userId, List roles); + Task UpdateUserRolesAsync(string userId, List roleIds); Task ValidateUserPassword(string userName, string password); - Task> GetUsersByRoleAsync(Role role); + Task> GetUsersByRoleAsync(string roleId); } } diff --git a/src/AgileConfig.Server.Service/PermissionService.cs b/src/AgileConfig.Server.Service/PermissionService.cs index 018d39a1..85487db7 100644 --- a/src/AgileConfig.Server.Service/PermissionService.cs +++ b/src/AgileConfig.Server.Service/PermissionService.cs @@ -4,21 +4,26 @@ using System.Threading.Tasks; using System.Linq; using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Common; +using System.Text.Json; namespace AgileConfig.Server.Service { public class PermissionService : IPermissionService { private readonly IUserRoleRepository _userRoleRepository; + private readonly IRoleDefinitionRepository _roleDefinitionRepository; private readonly IUserAppAuthRepository _userAppAuthRepository; private readonly IAppRepository _appRepository; public PermissionService( IUserRoleRepository userRoleRepository, + IRoleDefinitionRepository roleDefinitionRepository, IUserAppAuthRepository userAppAuthRepository, IAppRepository appRepository) { _userRoleRepository = userRoleRepository; + _roleDefinitionRepository = roleDefinitionRepository; _userAppAuthRepository = userAppAuthRepository; _appRepository = appRepository; } @@ -43,7 +48,10 @@ public PermissionService( "GLOBAL_" + Functions.User_Add, "GLOBAL_" + Functions.User_Edit, - "GLOBAL_" + Functions.User_Delete + "GLOBAL_" + Functions.User_Delete, + "GLOBAL_" + Functions.Role_Add, + "GLOBAL_" + Functions.Role_Edit, + "GLOBAL_" + Functions.Role_Delete ]; private static readonly List Template_NormalAdminPermissions = @@ -56,6 +64,9 @@ public PermissionService( "GLOBAL_" + Functions.User_Add, "GLOBAL_" + Functions.User_Edit, "GLOBAL_" + Functions.User_Delete, + "GLOBAL_" + Functions.Role_Add, + "GLOBAL_" + Functions.Role_Edit, + "GLOBAL_" + Functions.Role_Delete, "APP_{0}_" + Functions.App_Delete, "APP_{0}_" + Functions.App_Edit, @@ -186,26 +197,57 @@ private async Task> GetNormalUserFunctions(string userId) public async Task> GetUserPermission(string userId) { var userRoles = await _userRoleRepository.QueryAsync(x => x.UserId == userId); - if (userRoles.Any(x=>x.Role == Role.SuperAdmin)) + var roleIds = userRoles.Select(x => x.RoleId).Distinct().ToList(); + if (!roleIds.Any()) { - return Template_SuperAdminPermissions; + return new List(); + } + + var roleDefinitions = await _roleDefinitionRepository.QueryAsync(x => roleIds.Contains(x.Id)); + var systemRoles = roleDefinitions.Where(r => r.IsSystem).ToList(); + var customRoles = roleDefinitions.Where(r => !r.IsSystem).ToList(); + + var customFunctions = customRoles.SelectMany(GetRoleFunctions).ToList(); + + if (systemRoles.Any(r => r.Id == SystemRoleConstants.SuperAdminId)) + { + return Template_SuperAdminPermissions.Concat(customFunctions).Distinct().ToList(); } var userFunctions = new List(); - // Compute permissions for regular administrators. - if (userRoles.Any(x=>x.Role == Role.Admin)) + if (systemRoles.Any(r => r.Id == SystemRoleConstants.AdminId)) { userFunctions.AddRange(await GetAdminUserFunctions(userId)); } - // Compute permissions for regular users. - if (userRoles.Any(x => x.Role == Role.NormalUser)) + + if (systemRoles.Any(r => r.Id == SystemRoleConstants.OperatorId)) { userFunctions.AddRange(await GetNormalUserFunctions(userId)); } + userFunctions.AddRange(customFunctions); + return userFunctions.Distinct().ToList(); } + private static IEnumerable GetRoleFunctions(RoleDefinition role) + { + if (role == null || string.IsNullOrWhiteSpace(role.FunctionsJson)) + { + return Enumerable.Empty(); + } + + try + { + var functions = JsonSerializer.Deserialize>(role.FunctionsJson); + return functions ?? Enumerable.Empty(); + } + catch + { + return Enumerable.Empty(); + } + } + /// /// Retrieve applications where the user has been explicitly authorized. /// diff --git a/src/AgileConfig.Server.Service/RoleService.cs b/src/AgileConfig.Server.Service/RoleService.cs new file mode 100644 index 00000000..a5aaeb0f --- /dev/null +++ b/src/AgileConfig.Server.Service/RoleService.cs @@ -0,0 +1,139 @@ +using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.IService; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; + +namespace AgileConfig.Server.Service +{ + public class RoleService : IRoleService + { + private readonly IRoleDefinitionRepository _roleDefinitionRepository; + private readonly IUserRoleRepository _userRoleRepository; + + public RoleService(IRoleDefinitionRepository roleDefinitionRepository, IUserRoleRepository userRoleRepository) + { + _roleDefinitionRepository = roleDefinitionRepository; + _userRoleRepository = userRoleRepository; + } + + public async Task CreateAsync(RoleDefinition role, IEnumerable functions) + { + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + + if (string.IsNullOrWhiteSpace(role.Id)) + { + role.Id = Guid.NewGuid().ToString("N"); + } + + if (await ExistsWithSameCode(role)) + { + throw new InvalidOperationException($"Role code '{role.Code}' already exists."); + } + + role.CreateTime = DateTime.Now; + role.FunctionsJson = SerializeFunctions(functions); + + await _roleDefinitionRepository.InsertAsync(role); + return role; + } + + public async Task DeleteAsync(string id) + { + var role = await _roleDefinitionRepository.GetAsync(id); + if (role == null) + { + return false; + } + + if (role.IsSystem) + { + throw new InvalidOperationException("System roles cannot be deleted."); + } + + var userRoles = await _userRoleRepository.QueryAsync(x => x.RoleId == id); + if (userRoles.Any()) + { + await _userRoleRepository.DeleteAsync(userRoles); + } + + await _roleDefinitionRepository.DeleteAsync(role); + return true; + } + + public async Task> GetAllAsync() + { + return await _roleDefinitionRepository.AllAsync(); + } + + public Task GetAsync(string id) + { + return _roleDefinitionRepository.GetAsync(id); + } + + public async Task GetByCodeAsync(string code) + { + var roles = await _roleDefinitionRepository.QueryAsync(x => x.Code == code); + return roles.FirstOrDefault(); + } + + public async Task UpdateAsync(RoleDefinition role, IEnumerable functions) + { + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + + var dbRole = await _roleDefinitionRepository.GetAsync(role.Id); + if (dbRole == null) + { + return false; + } + + if (dbRole.IsSystem && !string.Equals(dbRole.Code, role.Code, StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException("System role code cannot be changed."); + } + + if (!string.Equals(dbRole.Code, role.Code, StringComparison.OrdinalIgnoreCase) && await ExistsWithSameCode(role)) + { + throw new InvalidOperationException($"Role code '{role.Code}' already exists."); + } + + dbRole.Code = role.Code; + dbRole.Name = role.Name; + dbRole.Description = role.Description; + dbRole.IsSystem = role.IsSystem; + dbRole.FunctionsJson = SerializeFunctions(functions); + dbRole.UpdateTime = DateTime.Now; + + await _roleDefinitionRepository.UpdateAsync(dbRole); + return true; + } + + private async Task ExistsWithSameCode(RoleDefinition role) + { + if (string.IsNullOrWhiteSpace(role.Code)) + { + return false; + } + + var sameCodeRoles = await _roleDefinitionRepository.QueryAsync(x => x.Code == role.Code); + return sameCodeRoles.Any(x => !string.Equals(x.Id, role.Id, StringComparison.OrdinalIgnoreCase)); + } + + private static string SerializeFunctions(IEnumerable functions) + { + var normalized = functions?.Where(f => !string.IsNullOrWhiteSpace(f)).Select(f => f.Trim()).Distinct().ToList() + ?? new List(); + + return JsonSerializer.Serialize(normalized); + } + } +} diff --git a/src/AgileConfig.Server.Service/ServiceCollectionExt.cs b/src/AgileConfig.Server.Service/ServiceCollectionExt.cs index ba797c69..48290faa 100644 --- a/src/AgileConfig.Server.Service/ServiceCollectionExt.cs +++ b/src/AgileConfig.Server.Service/ServiceCollectionExt.cs @@ -26,6 +26,7 @@ public static void AddBusinessServices(this IServiceCollection sc) sc.AddScoped(); sc.AddScoped(); sc.AddScoped(); + sc.AddScoped(); sc.AddScoped(); sc.AddScoped(); diff --git a/src/AgileConfig.Server.Service/UserService.cs b/src/AgileConfig.Server.Service/UserService.cs index 343545d8..bd1b176f 100644 --- a/src/AgileConfig.Server.Service/UserService.cs +++ b/src/AgileConfig.Server.Service/UserService.cs @@ -13,12 +13,14 @@ public class UserService : IUserService { private readonly IUserRepository _userRepository; private readonly IUserRoleRepository _userRoleRepository; + private readonly IRoleDefinitionRepository _roleDefinitionRepository; - public UserService(IUserRepository userRepository, IUserRoleRepository userRoleRepository) + public UserService(IUserRepository userRepository, IUserRoleRepository userRoleRepository, IRoleDefinitionRepository roleDefinitionRepository) { _userRepository = userRepository; _userRoleRepository = userRoleRepository; + _roleDefinitionRepository = roleDefinitionRepository; } public async Task AddAsync(User user) @@ -51,11 +53,41 @@ public Task GetUserAsync(string id) } - public async Task> GetUserRolesAsync(string userId) + public async Task> GetUserRolesAsync(string userId) { var userRoles = await _userRoleRepository.QueryAsync(x => x.UserId == userId); + var migratedRoles = new List(); + foreach (var userRole in userRoles) + { + if (string.IsNullOrEmpty(userRole.RoleId) && userRole.LegacyRoleValue.HasValue) + { + var mappedRoleId = MapLegacyRole(userRole.LegacyRoleValue.Value); + if (!string.IsNullOrEmpty(mappedRoleId)) + { + userRole.RoleId = mappedRoleId; + userRole.LegacyRoleValue = null; + if (userRole.CreateTime == default) + { + userRole.CreateTime = DateTime.Now; + } + migratedRoles.Add(userRole); + } + } + } - return userRoles.Select(x => x.Role).ToList(); + if (migratedRoles.Any()) + { + await _userRoleRepository.UpdateAsync(migratedRoles); + } + + var roleIds = userRoles.Select(x => x.RoleId).Distinct().ToList(); + if (!roleIds.Any()) + { + return new List(); + } + + var roles = await _roleDefinitionRepository.QueryAsync(x => roleIds.Contains(x.Id)); + return roles.OrderBy(r => roleIds.IndexOf(r.Id)).ToList(); } @@ -65,18 +97,21 @@ public async Task UpdateAsync(User user) return true; } - public async Task UpdateUserRolesAsync(string userId, List roles) + public async Task UpdateUserRolesAsync(string userId, List roleIds) { var dbUserRoles = await _userRoleRepository.QueryAsync(x => x.UserId == userId); await _userRoleRepository.DeleteAsync(dbUserRoles); var userRoles = new List(); - roles.ForEach(x => + var now = DateTime.Now; + roleIds.Distinct().ToList().ForEach(x => { userRoles.Add(new UserRole { Id = Guid.NewGuid().ToString("N"), UserId = userId, - Role = x + RoleId = x, + LegacyRoleValue = null, + CreateTime = now }); }); @@ -88,6 +123,7 @@ public void Dispose() { _userRepository.Dispose(); _userRoleRepository.Dispose(); + _roleDefinitionRepository.Dispose(); } public Task> GetAll() @@ -111,11 +147,22 @@ public async Task ValidateUserPassword(string userName, string password) return false; } - public async Task> GetUsersByRoleAsync(Role role) + public async Task> GetUsersByRoleAsync(string roleId) { - var userRoles = await _userRoleRepository.QueryAsync(x => x.Role == role); + var userRoles = await _userRoleRepository.QueryAsync(x => x.RoleId == roleId); var userIds = userRoles.Select(x => x.UserId).Distinct().ToList(); return await _userRepository.QueryAsync(x => userIds.Contains(x.Id)); } + + private static string MapLegacyRole(int legacyRole) + { + return legacyRole switch + { + 0 => SystemRoleConstants.SuperAdminId, + 1 => SystemRoleConstants.AdminId, + 2 => SystemRoleConstants.OperatorId, + _ => string.Empty + }; + } } } diff --git a/src/AgileConfig.Server.UI/react-ui-antd/config/routes.ts b/src/AgileConfig.Server.UI/react-ui-antd/config/routes.ts index c8bdaee9..751e3850 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/config/routes.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/config/routes.ts @@ -91,6 +91,13 @@ component: './User', authority: ['Admin'], }, + { + name: 'list.role-list', + icon: 'SafetyCertificate', + path: '/roles', + component: './Role', + authority: ['Admin'], + }, { name: 'list.logs-list', icon: 'Bars', diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/menu.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/menu.ts index f1a6286c..67e3e8fc 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/menu.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/menu.ts @@ -57,5 +57,6 @@ export default { 'menu.list.service-list': 'Services', 'menu.list.config-list': 'Configurations', 'menu.list.user-list': 'User', + 'menu.list.role-list': 'Role', 'menu.list.logs-list': 'Log', }; diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts index 1dac9c9c..2587ff70 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts @@ -274,6 +274,30 @@ export default { 'pages.user.confirm_reset': 'Are you sure to reset user', 'pages.user.confirm_delete': 'Are you sure to delete user', 'pages.user.reset_password_default': "'s password to default password【123456】?", + 'pages.role.table.cols.name': 'Name', + 'pages.role.table.cols.code': 'Code', + 'pages.role.table.cols.description': 'Description', + 'pages.role.table.cols.system': 'System Role', + 'pages.role.system.yes': 'Yes', + 'pages.role.system.no': 'No', + 'pages.role.table.cols.functions': 'Permissions', + 'pages.role.table.cols.action': 'Action', + 'pages.role.table.cols.action.add': 'Add Role', + 'pages.role.table.cols.action.edit': 'Edit', + 'pages.role.table.cols.action.delete': 'Delete', + 'pages.role.form.title.add': 'Add Role', + 'pages.role.form.title.edit': 'Edit Role', + 'pages.role.form.code': 'Code', + 'pages.role.form.name': 'Name', + 'pages.role.form.description': 'Description', + 'pages.role.form.functions': 'Permissions', + 'pages.role.confirm_delete': 'Are you sure you want to delete this role?', + 'pages.role.delete_success': 'Role deleted successfully', + 'pages.role.delete_fail': 'Failed to delete role', + 'pages.role.save_success': 'Role saved successfully', + 'pages.role.save_fail': 'Failed to save role', + 'pages.role.load_failed': 'Failed to load roles', + 'pages.role.permissions.load_failed': 'Failed to load permissions', // Service Management 'pages.service.table.cols.servicename': 'Service Name', diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/menu.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/menu.ts index 093efa0a..2b996625 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/menu.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/menu.ts @@ -27,6 +27,7 @@ export default { 'menu.list.node-list': '节点', 'menu.list.config-list': '配置项', 'menu.list.user-list': '用户', + 'menu.list.role-list': '角色', 'menu.list.client-list': '客户端', 'menu.list.service-list': '服务', 'menu.list.logs-list': '日志', diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts index e5f0f783..77b4b87d 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts @@ -272,6 +272,30 @@ export default { 'pages.user.confirm_reset': '确定重置用户', 'pages.user.confirm_delete': '确定删除用户', 'pages.user.reset_password_default': '的密码为默认密码【123456】?', + 'pages.role.table.cols.name': '名称', + 'pages.role.table.cols.code': '编码', + 'pages.role.table.cols.description': '描述', + 'pages.role.table.cols.system': '系统角色', + 'pages.role.system.yes': '是', + 'pages.role.system.no': '否', + 'pages.role.table.cols.functions': '权限', + 'pages.role.table.cols.action': '操作', + 'pages.role.table.cols.action.add': '新增角色', + 'pages.role.table.cols.action.edit': '编辑', + 'pages.role.table.cols.action.delete': '删除', + 'pages.role.form.title.add': '新增角色', + 'pages.role.form.title.edit': '编辑角色', + 'pages.role.form.code': '角色编码', + 'pages.role.form.name': '角色名称', + 'pages.role.form.description': '描述', + 'pages.role.form.functions': '权限', + 'pages.role.confirm_delete': '确定删除该角色吗?', + 'pages.role.delete_success': '删除角色成功', + 'pages.role.delete_fail': '删除角色失败', + 'pages.role.save_success': '保存角色成功', + 'pages.role.save_fail': '保存角色失败', + 'pages.role.load_failed': '加载角色失败', + 'pages.role.permissions.load_failed': '加载权限列表失败', // Service Management 'pages.service.table.cols.servicename': '服务名', diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/data.d.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/data.d.ts new file mode 100644 index 00000000..c3d6147a --- /dev/null +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/data.d.ts @@ -0,0 +1,17 @@ +export type RoleItem = { + id: string; + code: string; + name: string; + description?: string; + isSystem: boolean; + functions: string[]; +}; + +export type RoleFormValues = { + id?: string; + code: string; + name: string; + description?: string; + functions: string[]; + isSystem?: boolean; +}; diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx new file mode 100644 index 00000000..d493abbe --- /dev/null +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx @@ -0,0 +1,279 @@ +import { ExclamationCircleOutlined, PlusOutlined } from '@ant-design/icons'; +import { PageContainer } from '@ant-design/pro-layout'; +import ProTable, { ActionType, ProColumns } from '@ant-design/pro-table'; +import { Button, message, Modal, Space, Tag } from 'antd'; +import { ModalForm, ProFormSelect, ProFormText } from '@ant-design/pro-form'; +import React, { useEffect, useRef, useState } from 'react'; +import { useIntl } from 'umi'; +import type { RoleFormValues, RoleItem } from './data'; +import { createRole, deleteRole, fetchSupportedRolePermissions, queryRoles, updateRole } from '@/services/role'; + +const { confirm } = Modal; + +const RolePage: React.FC = () => { + const actionRef = useRef(); + const intl = useIntl(); + const [createModalVisible, setCreateModalVisible] = useState(false); + const [updateModalVisible, setUpdateModalVisible] = useState(false); + const [currentRole, setCurrentRole] = useState(); + const [supportedPermissions, setSupportedPermissions] = useState([]); + + useEffect(() => { + loadPermissions(); + }, []); + + const loadPermissions = async () => { + try { + const response = await fetchSupportedRolePermissions(); + if (response?.success && Array.isArray(response.data)) { + setSupportedPermissions(response.data); + } else { + message.error(intl.formatMessage({ id: 'pages.role.permissions.load_failed', defaultMessage: 'Failed to load permissions' })); + } + } catch (error) { + message.error(intl.formatMessage({ id: 'pages.role.permissions.load_failed', defaultMessage: 'Failed to load permissions' })); + } + }; + + const permissionOptions = supportedPermissions.map((item) => ({ + value: item, + label: item, + })); + + const handleCreate = async (values: RoleFormValues) => { + const hide = message.loading(intl.formatMessage({ id: 'saving', defaultMessage: 'Saving...' })); + try { + const response = await createRole(values); + hide(); + if (response?.success) { + message.success(intl.formatMessage({ id: 'pages.role.save_success', defaultMessage: 'Role saved successfully' })); + return true; + } + message.error(response?.message || intl.formatMessage({ id: 'pages.role.save_fail', defaultMessage: 'Failed to save role' })); + return false; + } catch (error) { + hide(); + message.error(intl.formatMessage({ id: 'pages.role.save_fail', defaultMessage: 'Failed to save role' })); + return false; + } + }; + + const handleUpdate = async (values: RoleFormValues) => { + const hide = message.loading(intl.formatMessage({ id: 'saving', defaultMessage: 'Saving...' })); + try { + const response = await updateRole(values); + hide(); + if (response?.success) { + message.success(intl.formatMessage({ id: 'pages.role.save_success', defaultMessage: 'Role saved successfully' })); + return true; + } + message.error(response?.message || intl.formatMessage({ id: 'pages.role.save_fail', defaultMessage: 'Failed to save role' })); + return false; + } catch (error) { + hide(); + message.error(intl.formatMessage({ id: 'pages.role.save_fail', defaultMessage: 'Failed to save role' })); + return false; + } + }; + + const handleDelete = (role: RoleItem) => { + confirm({ + icon: , + title: intl.formatMessage({ id: 'pages.role.confirm_delete', defaultMessage: 'Are you sure to delete this role?' }), + content: `${role.name}`, + onOk: async () => { + const hide = message.loading(intl.formatMessage({ id: 'deleting', defaultMessage: 'Deleting...' })); + try { + const response = await deleteRole(role.id); + hide(); + if (response?.success) { + message.success(intl.formatMessage({ id: 'pages.role.delete_success', defaultMessage: 'Role deleted successfully' })); + actionRef.current?.reload(); + } else { + message.error(response?.message || intl.formatMessage({ id: 'pages.role.delete_fail', defaultMessage: 'Failed to delete role' })); + } + } catch (error) { + hide(); + message.error(intl.formatMessage({ id: 'pages.role.delete_fail', defaultMessage: 'Failed to delete role' })); + } + }, + }); + }; + + const columns: ProColumns[] = [ + { + title: intl.formatMessage({ id: 'pages.role.table.cols.name', defaultMessage: 'Name' }), + dataIndex: 'name', + }, + { + title: intl.formatMessage({ id: 'pages.role.table.cols.code', defaultMessage: 'Code' }), + dataIndex: 'code', + }, + { + title: intl.formatMessage({ id: 'pages.role.table.cols.description', defaultMessage: 'Description' }), + dataIndex: 'description', + search: false, + }, + { + title: intl.formatMessage({ id: 'pages.role.table.cols.system', defaultMessage: 'System Role' }), + dataIndex: 'isSystem', + search: false, + render: (_, record) => ( + + {record.isSystem + ? intl.formatMessage({ id: 'pages.role.system.yes', defaultMessage: 'Yes' }) + : intl.formatMessage({ id: 'pages.role.system.no', defaultMessage: 'No' })} + + ), + }, + { + title: intl.formatMessage({ id: 'pages.role.table.cols.functions', defaultMessage: 'Permissions' }), + dataIndex: 'functions', + search: false, + render: (_, record) => ( + + {record.functions?.map((fn) => ( + {fn} + ))} + + ), + }, + { + title: intl.formatMessage({ id: 'pages.role.table.cols.action', defaultMessage: 'Action' }), + valueType: 'option', + render: (_, record) => [ + { + setCurrentRole(record); + setUpdateModalVisible(true); + }} + > + {intl.formatMessage({ id: 'pages.role.table.cols.action.edit', defaultMessage: 'Edit' })} + , + !record.isSystem ? ( + + ) : null, + ], + }, + ]; + + return ( + + + actionRef={actionRef} + rowKey="id" + search={false} + columns={columns} + request={async () => { + const response = await queryRoles(); + return { + data: response?.data || [], + success: response?.success ?? false, + }; + }} + toolBarRender={() => [ + , + ]} + /> + + + title={intl.formatMessage({ id: 'pages.role.form.title.add', defaultMessage: 'Add Role' })} + visible={createModalVisible} + onVisibleChange={setCreateModalVisible} + initialValues={{ functions: [] }} + onFinish={async (values) => { + const success = await handleCreate(values); + if (success) { + setCreateModalVisible(false); + actionRef.current?.reload(); + } + return success; + }} + > + + + + + + + {updateModalVisible && ( + + title={intl.formatMessage({ id: 'pages.role.form.title.edit', defaultMessage: 'Edit Role' })} + visible={updateModalVisible} + onVisibleChange={(visible) => { + setUpdateModalVisible(visible); + if (!visible) { + setCurrentRole(undefined); + } + }} + initialValues={{ + ...currentRole, + functions: currentRole?.functions || [], + }} + onFinish={async (values) => { + const success = await handleUpdate({ ...values, id: currentRole?.id, isSystem: currentRole?.isSystem }); + if (success) { + setUpdateModalVisible(false); + setCurrentRole(undefined); + actionRef.current?.reload(); + } + return success; + }} + > + + + + + + )} + + ); +}; + +export default RolePage; diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/comps/updateUser.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/comps/updateUser.tsx index 6d1c16f6..da1506b7 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/comps/updateUser.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/comps/updateUser.tsx @@ -1,32 +1,34 @@ import { useIntl } from "@/.umi/plugin-locale/localeExports"; -import { getAuthority } from "@/utils/authority"; import { ModalForm, ProFormSelect, ProFormText } from "@ant-design/pro-form"; import React from 'react'; import { UserItem } from "../data"; + +type RoleOption = { + value: string; + label: string; + code?: string; + isSystem?: boolean; +}; + export type UpdateUserProps = { onSubmit: (values: UserItem) => Promise; onCancel: () => void; updateModalVisible: boolean; value: UserItem | undefined ; - setValue: React.Dispatch> + setValue: React.Dispatch>; + roleOptions: RoleOption[]; + defaultRoleIds: string[]; }; const UpdateForm : React.FC = (props)=>{ const intl = useIntl(); - const hasUserRole = (role:string) => { - const authority = getAuthority(); - if (Array.isArray(authority)) { - if (authority.find(x=> x === role)) { - return true; - } - } - - return false; - } return ( - 0 ? props.value.userRoleIds : props.defaultRoleIds + }} visible={props.updateModalVisible} modalProps={ { @@ -62,31 +64,11 @@ const UpdateForm : React.FC = (props)=>{ }, ]} label={intl.formatMessage({id: 'pages.user.form.usertype'})} - name="userRoles" - mode="multiple" - options = { - hasUserRole('SuperAdmin')?[ - { - value: 1, - label: intl.formatMessage({ - id: 'pages.user.usertype.admin', - }), - }, - { - value: 2, - label: intl.formatMessage({ - id: 'pages.user.usertype.normaluser', - }), - } - ]:[{ - value: 2, - label: intl.formatMessage({ - id: 'pages.user.usertype.normaluser', - }), - }] - } + name="userRoleIds" + mode="multiple" + options = {props.roleOptions} > - + ); } diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/data.d.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/data.d.ts index 6fec930c..12caf431 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/data.d.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/data.d.ts @@ -3,8 +3,9 @@ export type UserItem = { userName: string, team: string, status: number, - userRoles: number[], - userRoleNames: string[] + userRoleIds: string[], + userRoleNames: string[], + userRoleCodes: string[] }; export type UserListParams = { name?: string; diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/index.tsx index b5a02096..4089482e 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/index.tsx @@ -2,15 +2,23 @@ import { ExclamationCircleOutlined, PlusOutlined } from '@ant-design/icons'; import { PageContainer } from '@ant-design/pro-layout'; import ProTable, { ActionType, ProColumns } from '@ant-design/pro-table'; import { Button, FormInstance, message,Modal, Space, Tag } from 'antd'; -import React, { useState, useRef } from 'react'; +import React, { useState, useRef, useEffect } from 'react'; import { UserItem } from './data'; import { queryUsers, addUser, delUser, editUser, resetPassword } from './service'; +import { queryRoles } from '@/services/role'; import { useIntl, getIntl, getLocale } from 'umi'; import { ModalForm, ProFormSelect, ProFormText } from '@ant-design/pro-form'; import UpdateUser from './comps/updateUser'; import { getAuthority } from '@/utils/authority'; const { confirm } = Modal; + +type RoleOption = { + value: string; + label: string; + code: string; + isSystem: boolean; +}; const handleAdd = async (fields: UserItem) => { const intl = getIntl(getLocale()); const hide = message.loading(intl.formatMessage({ @@ -125,14 +133,19 @@ const hasUserRole = (role:string) => { } const checkUserListModifyPermission = (user:UserItem) => { - const authMap = { 'SuperAdmin': 0,'Admin':1,'NormalUser':2}; + const authMap:Record = { SuperAdmin: 0, Admin: 1, NormalUser: 2 }; let currentAuthNum = 2; const roles = getAuthority(); - if (Array.isArray(roles)) { - let max = roles.map(x=> authMap[x]).sort((a, b) => a - b)[0]; - currentAuthNum = max; + if (Array.isArray(roles) && roles.length > 0) { + const sorted = roles.map((x:string) => authMap[x] ?? 3).sort((a, b) => a - b); + if (sorted.length > 0) { + currentAuthNum = sorted[0]; + } } - let userAuthNum = user.userRoles.sort((a, b) => a - b)[0]; + + const userCodes = user.userRoleCodes || []; + const userSorted = userCodes.map(code => authMap[code] ?? 3).sort((a, b) => a - b); + const userAuthNum = userSorted.length > 0 ? userSorted[0] : 3; return currentAuthNum < userAuthNum; } @@ -145,6 +158,39 @@ const userList:React.FC = () => { const [createModalVisible, handleModalVisible] = useState(false); const [updateModalVisible, setUpdateModalVisible] = useState(false); const [currentRow, setCurrentRow] = useState(); + const [roleOptions, setRoleOptions] = useState([]); + + useEffect(() => { + loadRoles(); + }, []); + + const loadRoles = async () => { + try { + const response = await queryRoles(); + if (response?.success && Array.isArray(response.data)) { + const options = response.data.map((role: any) => ({ + value: role.id, + label: role.name, + code: role.code, + isSystem: role.isSystem, + })); + setRoleOptions(options); + } else { + message.error(intl.formatMessage({ id: 'pages.role.load_failed', defaultMessage: 'Failed to load roles' })); + } + } catch (error) { + message.error(intl.formatMessage({ id: 'pages.role.load_failed', defaultMessage: 'Failed to load roles' })); + } + }; + + const getDefaultRoleIds = () => { + const normalRole = roleOptions.find(option => option.code === 'NormalUser'); + return normalRole ? [normalRole.value] : []; + }; + + const availableRoleOptions = hasUserRole('SuperAdmin') + ? roleOptions + : roleOptions.filter(option => option.code !== 'SuperAdmin'); const columns: ProColumns[] = [ { title: intl.formatMessage({ @@ -162,20 +208,27 @@ const userList:React.FC = () => { title: intl.formatMessage({ id: 'pages.user.table.cols.usertype', }), - dataIndex: 'userRoles', + dataIndex: 'userRoleNames', search: false, renderFormItem: (_, { defaultRender }) => { return defaultRender(_); }, render: (_, record) => ( - {record.userRoleNames?.map((name:string) => ( - - {name} - - ))} + {record.userRoleNames?.map((name:string, index:number) => { + const code = record.userRoleCodes?.[index]; + let color = 'blue'; + if (code === 'SuperAdmin') { + color = 'red'; + } else if (code === 'Admin') { + color = 'gold'; + } + return ( + + {name} + + ); + })} ), }, @@ -290,7 +343,17 @@ const userList:React.FC = () => { } width="400px" visible={createModalVisible} - onVisibleChange={handleModalVisible} + initialValues={{ + userRoleIds: getDefaultRoleIds(), + }} + onVisibleChange={(visible) => { + handleModalVisible(visible); + if (visible) { + addFormRef.current?.setFieldsValue({ userRoleIds: getDefaultRoleIds() }); + } else { + addFormRef.current?.resetFields(); + } + }} onFinish={ async (value) => { const success = await handleAdd(value as UserItem); @@ -341,30 +404,11 @@ const userList:React.FC = () => { label={intl.formatMessage({ id: 'pages.user.form.usertype' })} - name="userRoles" - mode="multiple" - options = {hasUserRole('SuperAdmin')?[ - { - value: 1, - label: intl.formatMessage({ - id: 'pages.user.usertype.admin' - }), - }, - { - value: 2, - label: intl.formatMessage({ - id: 'pages.user.usertype.normaluser' - }), - } - ]:[ - { - value: 2, - label: intl.formatMessage({ - id: 'pages.user.usertype.normaluser' - }), - }]} + name="userRoleIds" + mode="multiple" + options = {availableRoleOptions} > - + { @@ -373,6 +417,8 @@ const userList:React.FC = () => { value={currentRow} setValue={setCurrentRow} updateModalVisible={updateModalVisible} + roleOptions={availableRoleOptions} + defaultRoleIds={getDefaultRoleIds()} onCancel={ () => { setCurrentRow(undefined); @@ -389,10 +435,10 @@ const userList:React.FC = () => { actionRef.current.reload(); } } - addFormRef.current?.resetFields(); } - }/> - } + } + /> + } ); diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/services/role.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/services/role.ts new file mode 100644 index 00000000..6c5de07e --- /dev/null +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/services/role.ts @@ -0,0 +1,36 @@ +import request from '@/utils/request'; + +export async function queryRoles() { + return request('role/list', { + method: 'GET', + }); +} + +export async function fetchSupportedRolePermissions() { + return request('role/supportedPermissions', { + method: 'GET', + }); +} + +export async function createRole(data: any) { + return request('role/add', { + method: 'POST', + data, + }); +} + +export async function updateRole(data: any) { + return request('role/edit', { + method: 'POST', + data, + }); +} + +export async function deleteRole(id: string) { + return request('role/delete', { + method: 'POST', + params: { + id, + }, + }); +} From 0e97d91ea8ccc0e8f61862299b72ce2140dc5b17 Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Tue, 21 Oct 2025 02:05:54 +0800 Subject: [PATCH 02/17] Update namespace fix compile error --- .../Controllers/RoleController.cs | 6 +-- .../IRoleDefinitionRepository.cs | 2 +- src/AgileConfig.Server.Data.Entity/Role.cs | 37 +++++++++++++++++++ .../RoleDefinition.cs | 7 ++-- .../UserRole.cs | 1 + .../EnsureTables.cs | 2 +- .../RoleDefinitionRepository.cs | 2 +- .../SysInitRepository.cs | 6 +-- .../RoleDefinitionRepository.cs | 2 +- .../SysInitRepository.cs | 4 +- .../IRoleService.cs | 10 ++--- .../IUserService.cs | 2 +- .../PermissionService.cs | 2 +- src/AgileConfig.Server.Service/RoleService.cs | 12 +++--- src/AgileConfig.Server.Service/UserService.cs | 4 +- 15 files changed, 69 insertions(+), 30 deletions(-) create mode 100644 src/AgileConfig.Server.Data.Entity/Role.cs diff --git a/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs b/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs index 8b88f0d1..7a835bc2 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs @@ -85,7 +85,7 @@ public async Task Add([FromBody] RoleVM model) try { - var role = new RoleDefinition + var role = new Role { Id = model.Id, Code = model.Code, @@ -119,7 +119,7 @@ public async Task Edit([FromBody] RoleVM model) try { - var role = new RoleDefinition + var role = new Role() { Id = model.Id, Code = model.Code, @@ -174,7 +174,7 @@ public async Task Delete(string id) } } - private static RoleVM ToViewModel(RoleDefinition role) + private static RoleVM ToViewModel(Role role) { return new RoleVM { diff --git a/src/AgileConfig.Server.Data.Abstraction/IRoleDefinitionRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IRoleDefinitionRepository.cs index 0b40d70c..0c948825 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IRoleDefinitionRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IRoleDefinitionRepository.cs @@ -2,7 +2,7 @@ namespace AgileConfig.Server.Data.Abstraction { - public interface IRoleDefinitionRepository : IRepository + public interface IRoleDefinitionRepository : IRepository { } } diff --git a/src/AgileConfig.Server.Data.Entity/Role.cs b/src/AgileConfig.Server.Data.Entity/Role.cs new file mode 100644 index 00000000..26509541 --- /dev/null +++ b/src/AgileConfig.Server.Data.Entity/Role.cs @@ -0,0 +1,37 @@ +using FreeSql.DataAnnotations; +using MongoDB.Bson.Serialization.Attributes; +using System; + +namespace AgileConfig.Server.Data.Entity +{ + [Table(Name = "agc_role_definition")] + [OraclePrimaryKeyName("agc_role_definition_pk")] + public class RoleDefinition : IEntity + { + [Column(Name = "id", StringLength = 64)] + public string Id { get; set; } + + [Column(Name = "code", StringLength = 64)] + public string Code { get; set; } + + [Column(Name = "name", StringLength = 128)] + public string Name { get; set; } + + [Column(Name = "description", StringLength = 512)] + public string Description { get; set; } + + [Column(Name = "is_system")] + public bool IsSystem { get; set; } + + [Column(Name = "functions", StringLength = -1)] + public string FunctionsJson { get; set; } + + [Column(Name = "create_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + + [Column(Name = "update_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime? UpdateTime { get; set; } + } +} diff --git a/src/AgileConfig.Server.Data.Entity/RoleDefinition.cs b/src/AgileConfig.Server.Data.Entity/RoleDefinition.cs index e44a5f31..b8b5c5ee 100644 --- a/src/AgileConfig.Server.Data.Entity/RoleDefinition.cs +++ b/src/AgileConfig.Server.Data.Entity/RoleDefinition.cs @@ -1,12 +1,13 @@ using FreeSql.DataAnnotations; using MongoDB.Bson.Serialization.Attributes; using System; +using AgileConfig.Server.Common; namespace AgileConfig.Server.Data.Entity { - [Table(Name = "agc_role_definition")] - [OraclePrimaryKeyName("agc_role_definition_pk")] - public class RoleDefinition : IEntity + [Table(Name = "agc_role")] + [OraclePrimaryKeyName("agc_role_pk")] + public class Role : IEntity { [Column(Name = "id", StringLength = 64)] public string Id { get; set; } diff --git a/src/AgileConfig.Server.Data.Entity/UserRole.cs b/src/AgileConfig.Server.Data.Entity/UserRole.cs index e5c1adc1..018860c8 100644 --- a/src/AgileConfig.Server.Data.Entity/UserRole.cs +++ b/src/AgileConfig.Server.Data.Entity/UserRole.cs @@ -1,5 +1,6 @@ using FreeSql.DataAnnotations; using System; +using AgileConfig.Server.Common; using MongoDB.Bson.Serialization.Attributes; namespace AgileConfig.Server.Data.Entity diff --git a/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs b/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs index 4cf252ea..e198e59a 100644 --- a/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs +++ b/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs @@ -76,7 +76,7 @@ public static void Ensure(IFreeSql instance) instance.CodeFirst.SyncStructure(); instance.CodeFirst.SyncStructure(); instance.CodeFirst.SyncStructure(); - instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); instance.CodeFirst.SyncStructure(); instance.CodeFirst.SyncStructure(); instance.CodeFirst.SyncStructure(); diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/RoleDefinitionRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/RoleDefinitionRepository.cs index da122765..02605e62 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/RoleDefinitionRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/RoleDefinitionRepository.cs @@ -4,7 +4,7 @@ namespace AgileConfig.Server.Data.Repository.Freesql { - public class RoleDefinitionRepository : FreesqlRepository, IRoleDefinitionRepository + public class RoleDefinitionRepository : FreesqlRepository, IRoleDefinitionRepository { public RoleDefinitionRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) { diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs index e2c6daca..fadeaba3 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs @@ -129,10 +129,10 @@ private static void EnsureSystemRoles(IFreeSql sql) private static void EnsureRole(IFreeSql sql, string id, string code, string name) { - var role = sql.Select().Where(x => x.Id == id).First(); + var role = sql.Select().Where(x => x.Id == id).First(); if (role == null) { - sql.Insert(new RoleDefinition + sql.Insert(new Role { Id = id, Code = code, @@ -149,7 +149,7 @@ private static void EnsureRole(IFreeSql sql, string id, string code, string name role.Name = name; role.Description = name; role.IsSystem = true; - sql.Update().SetSource(role).ExecuteAffrows(); + sql.Update().SetSource(role).ExecuteAffrows(); } } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/RoleDefinitionRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/RoleDefinitionRepository.cs index 3da65404..b3f7a870 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/RoleDefinitionRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/RoleDefinitionRepository.cs @@ -4,7 +4,7 @@ namespace AgileConfig.Server.Data.Repository.Mongodb { - public class RoleDefinitionRepository : MongodbRepository, IRoleDefinitionRepository + public class RoleDefinitionRepository : MongodbRepository, IRoleDefinitionRepository { public RoleDefinitionRepository(string? connectionString) : base(connectionString) { diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs index 04652ea2..6d40e5b1 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs @@ -19,7 +19,7 @@ public SysInitRepository(IConfiguration configuration) private MongodbAccess _settingAccess => new MongodbAccess(_connectionString); private MongodbAccess _userAccess => new MongodbAccess(_connectionString); private MongodbAccess _userRoleAccess => new MongodbAccess(_connectionString); - private MongodbAccess _roleAccess => new MongodbAccess(_connectionString); + private MongodbAccess _roleAccess => new MongodbAccess(_connectionString); private MongodbAccess _appAccess => new MongodbAccess(_connectionString); private readonly IConfiguration _configuration; @@ -135,7 +135,7 @@ private void EnsureRole(string id, string code, string name) var role = _roleAccess.MongoQueryable.FirstOrDefault(x => x.Id == id); if (role == null) { - _roleAccess.Collection.InsertOne(new RoleDefinition + _roleAccess.Collection.InsertOne(new Role { Id = id, Code = code, diff --git a/src/AgileConfig.Server.IService/IRoleService.cs b/src/AgileConfig.Server.IService/IRoleService.cs index 8d18aab6..d5abaea7 100644 --- a/src/AgileConfig.Server.IService/IRoleService.cs +++ b/src/AgileConfig.Server.IService/IRoleService.cs @@ -6,11 +6,11 @@ namespace AgileConfig.Server.IService { public interface IRoleService { - Task> GetAllAsync(); - Task GetAsync(string id); - Task GetByCodeAsync(string code); - Task CreateAsync(RoleDefinition role, IEnumerable functions); - Task UpdateAsync(RoleDefinition role, IEnumerable functions); + Task> GetAllAsync(); + Task GetAsync(string id); + Task GetByCodeAsync(string code); + Task CreateAsync(Role role, IEnumerable functions); + Task UpdateAsync(Role role, IEnumerable functions); Task DeleteAsync(string id); } } diff --git a/src/AgileConfig.Server.IService/IUserService.cs b/src/AgileConfig.Server.IService/IUserService.cs index c001ef00..29ed7639 100644 --- a/src/AgileConfig.Server.IService/IUserService.cs +++ b/src/AgileConfig.Server.IService/IUserService.cs @@ -12,7 +12,7 @@ public interface IUserService: IDisposable Task GetUserAsync(string userId); Task> GetUsersByNameAsync(string userName); - Task> GetUserRolesAsync(string userId); + Task> GetUserRolesAsync(string userId); Task AddAsync(User user); diff --git a/src/AgileConfig.Server.Service/PermissionService.cs b/src/AgileConfig.Server.Service/PermissionService.cs index 85487db7..8145ced5 100644 --- a/src/AgileConfig.Server.Service/PermissionService.cs +++ b/src/AgileConfig.Server.Service/PermissionService.cs @@ -230,7 +230,7 @@ public async Task> GetUserPermission(string userId) return userFunctions.Distinct().ToList(); } - private static IEnumerable GetRoleFunctions(RoleDefinition role) + private static IEnumerable GetRoleFunctions(Role role) { if (role == null || string.IsNullOrWhiteSpace(role.FunctionsJson)) { diff --git a/src/AgileConfig.Server.Service/RoleService.cs b/src/AgileConfig.Server.Service/RoleService.cs index a5aaeb0f..7edb9c70 100644 --- a/src/AgileConfig.Server.Service/RoleService.cs +++ b/src/AgileConfig.Server.Service/RoleService.cs @@ -20,7 +20,7 @@ public RoleService(IRoleDefinitionRepository roleDefinitionRepository, IUserRole _userRoleRepository = userRoleRepository; } - public async Task CreateAsync(RoleDefinition role, IEnumerable functions) + public async Task CreateAsync(Role role, IEnumerable functions) { if (role == null) { @@ -67,23 +67,23 @@ public async Task DeleteAsync(string id) return true; } - public async Task> GetAllAsync() + public async Task> GetAllAsync() { return await _roleDefinitionRepository.AllAsync(); } - public Task GetAsync(string id) + public Task GetAsync(string id) { return _roleDefinitionRepository.GetAsync(id); } - public async Task GetByCodeAsync(string code) + public async Task GetByCodeAsync(string code) { var roles = await _roleDefinitionRepository.QueryAsync(x => x.Code == code); return roles.FirstOrDefault(); } - public async Task UpdateAsync(RoleDefinition role, IEnumerable functions) + public async Task UpdateAsync(Role role, IEnumerable functions) { if (role == null) { @@ -117,7 +117,7 @@ public async Task UpdateAsync(RoleDefinition role, IEnumerable fun return true; } - private async Task ExistsWithSameCode(RoleDefinition role) + private async Task ExistsWithSameCode(Role role) { if (string.IsNullOrWhiteSpace(role.Code)) { diff --git a/src/AgileConfig.Server.Service/UserService.cs b/src/AgileConfig.Server.Service/UserService.cs index bd1b176f..4e4198d9 100644 --- a/src/AgileConfig.Server.Service/UserService.cs +++ b/src/AgileConfig.Server.Service/UserService.cs @@ -53,7 +53,7 @@ public Task GetUserAsync(string id) } - public async Task> GetUserRolesAsync(string userId) + public async Task> GetUserRolesAsync(string userId) { var userRoles = await _userRoleRepository.QueryAsync(x => x.UserId == userId); var migratedRoles = new List(); @@ -83,7 +83,7 @@ public async Task> GetUserRolesAsync(string userId) var roleIds = userRoles.Select(x => x.RoleId).Distinct().ToList(); if (!roleIds.Any()) { - return new List(); + return new List(); } var roles = await _roleDefinitionRepository.QueryAsync(x => roleIds.Contains(x.Id)); From ff419d3cf46b86a4a5d8f24879cda9ee0ec7d551 Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Sun, 2 Nov 2025 22:23:01 +0800 Subject: [PATCH 03/17] fix error --- src/AgileConfig.Server.Apisite/appsettings.Development.json | 2 +- src/AgileConfig.Server.Data.Entity/Role.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/AgileConfig.Server.Apisite/appsettings.Development.json b/src/AgileConfig.Server.Apisite/appsettings.Development.json index d53837f4..153499ac 100644 --- a/src/AgileConfig.Server.Apisite/appsettings.Development.json +++ b/src/AgileConfig.Server.Apisite/appsettings.Development.json @@ -31,7 +31,7 @@ "removeServiceInterval": 0, // Remove a service if it has been unresponsive longer than this many seconds; <= 0 keeps the service. Default is 0. "pathBase": "", // When using reverse proxies, set this to the base path (must start with /xxx). "adminConsole": true, - "saPassword": "123456", // Password for the super administrator account. + "saPassword": "", // Password for the super administrator account. "defaultApp": "myapp", // Default application to create on every restart. "cluster": false, // Cluster mode: automatically join the node list on startup, discover container IP, default port 5000 (ideal for Docker Compose). "preview_mode": false, diff --git a/src/AgileConfig.Server.Data.Entity/Role.cs b/src/AgileConfig.Server.Data.Entity/Role.cs index 26509541..92ed1a3f 100644 --- a/src/AgileConfig.Server.Data.Entity/Role.cs +++ b/src/AgileConfig.Server.Data.Entity/Role.cs @@ -1,6 +1,7 @@ using FreeSql.DataAnnotations; using MongoDB.Bson.Serialization.Attributes; using System; +using AgileConfig.Server.Common; namespace AgileConfig.Server.Data.Entity { From 32003909e0c4c5a725ff1fedd8100da71911543c Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Sun, 2 Nov 2025 22:26:25 +0800 Subject: [PATCH 04/17] update --- src/AgileConfig.Server.Common/SystemRoleConstants.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AgileConfig.Server.Common/SystemRoleConstants.cs b/src/AgileConfig.Server.Common/SystemRoleConstants.cs index 007979de..1a768ed7 100644 --- a/src/AgileConfig.Server.Common/SystemRoleConstants.cs +++ b/src/AgileConfig.Server.Common/SystemRoleConstants.cs @@ -8,6 +8,6 @@ public static class SystemRoleConstants public const string SuperAdminCode = "SuperAdmin"; public const string AdminCode = "Admin"; - public const string OperatorCode = "NormalUser"; + public const string OperatorCode = "Operator"; } } From 1075a31cc0e3be751b5490d3afe147d2a7e7070b Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Mon, 3 Nov 2025 02:56:11 +0800 Subject: [PATCH 05/17] complete role control --- .../Controllers/RoleController.cs | 136 +++---- .../Filters/PermissionCheckAttribute.cs | 5 +- .../Models/RoleVM.cs | 4 - src/AgileConfig.Server.Data.Entity/Role.cs | 38 -- ...nfig.Server.Data.Repository.Freesql.csproj | 1 + .../SysInitRepository.cs | 208 ++++++---- .../PermissionService.cs | 380 +++++++++--------- .../Authorized/AuthorizedElement.tsx | 10 +- .../react-ui-antd/src/locales/en-US/pages.ts | 30 +- .../react-ui-antd/src/locales/zh-CN/pages.ts | 28 +- .../react-ui-antd/src/pages/Role/data.d.ts | 2 - .../react-ui-antd/src/pages/Role/index.tsx | 41 +- .../src/pages/User/comps/updateUser.tsx | 10 +- .../react-ui-antd/src/pages/User/index.tsx | 34 +- 14 files changed, 478 insertions(+), 449 deletions(-) delete mode 100644 src/AgileConfig.Server.Data.Entity/Role.cs diff --git a/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs b/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs index 7a835bc2..ba1b917d 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs @@ -21,29 +21,29 @@ public class RoleController : Controller private static readonly IReadOnlyList SupportedFunctions = new List { - "GLOBAL_" + Functions.App_Add, - "GLOBAL_" + Functions.App_Edit, - "GLOBAL_" + Functions.App_Delete, - "GLOBAL_" + Functions.App_Auth, + Functions.App_Add, + Functions.App_Edit, + Functions.App_Delete, + Functions.App_Auth, - "GLOBAL_" + Functions.Config_Add, - "GLOBAL_" + Functions.Config_Edit, - "GLOBAL_" + Functions.Config_Delete, - "GLOBAL_" + Functions.Config_Publish, - "GLOBAL_" + Functions.Config_Offline, + Functions.Config_Add, + Functions.Config_Edit, + Functions.Config_Delete, + Functions.Config_Publish, + Functions.Config_Offline, - "GLOBAL_" + Functions.Node_Add, - "GLOBAL_" + Functions.Node_Delete, + Functions.Node_Add, + Functions.Node_Delete, - "GLOBAL_" + Functions.Client_Disconnect, + Functions.Client_Disconnect, - "GLOBAL_" + Functions.User_Add, - "GLOBAL_" + Functions.User_Edit, - "GLOBAL_" + Functions.User_Delete, + Functions.User_Add, + Functions.User_Edit, + Functions.User_Delete, - "GLOBAL_" + Functions.Role_Add, - "GLOBAL_" + Functions.Role_Edit, - "GLOBAL_" + Functions.Role_Delete + Functions.Role_Add, + Functions.Role_Edit, + Functions.Role_Delete }; public RoleController(IRoleService roleService) @@ -55,7 +55,13 @@ public RoleController(IRoleService roleService) public async Task List() { var roles = await _roleService.GetAllAsync(); - var vms = roles.Select(ToViewModel).OrderByDescending(r => r.IsSystem).ThenBy(r => r.Name).ToList(); + // Filter out Super Administrator role to prevent it from being assigned through the frontend + var vms = roles + .Where(r => r.Id != SystemRoleConstants.SuperAdminId) + .Select(ToViewModel) + .OrderByDescending(r => r.IsSystem) + .ThenBy(r => r.Name) + .ToList(); return Json(new { @@ -83,29 +89,17 @@ public async Task Add([FromBody] RoleVM model) throw new ArgumentNullException(nameof(model)); } - try - { - var role = new Role - { - Id = model.Id, - Code = model.Code, - Name = model.Name, - Description = model.Description ?? string.Empty, - IsSystem = false - }; - - await _roleService.CreateAsync(role, model.Functions ?? Enumerable.Empty()); - - return Json(new { success = true }); - } - catch (Exception ex) + var role = new Role { - return Json(new - { - success = false, - message = ex.Message - }); - } + Id = model.Id, + Name = model.Name, + Description = model.Description ?? string.Empty, + IsSystem = false + }; + + await _roleService.CreateAsync(role, model.Functions ?? Enumerable.Empty()); + + return Json(new { success = true }); } [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Role.Edit", Functions.Role_Edit })] @@ -117,33 +111,21 @@ public async Task Edit([FromBody] RoleVM model) throw new ArgumentNullException(nameof(model)); } - try + var role = new Role() { - var role = new Role() - { - Id = model.Id, - Code = model.Code, - Name = model.Name, - Description = model.Description ?? string.Empty, - IsSystem = model.IsSystem - }; - - var result = await _roleService.UpdateAsync(role, model.Functions ?? Enumerable.Empty()); - - return Json(new - { - success = result, - message = result ? string.Empty : Messages.UpdateRoleFailed - }); - } - catch (Exception ex) + Id = model.Id, + Name = model.Name, + Description = model.Description ?? string.Empty, + IsSystem = model.IsSystem + }; + + var result = await _roleService.UpdateAsync(role, model.Functions ?? Enumerable.Empty()); + + return Json(new { - return Json(new - { - success = false, - message = ex.Message - }); - } + success = result, + message = result ? string.Empty : Messages.UpdateRoleFailed + }); } [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Role.Delete", Functions.Role_Delete })] @@ -155,23 +137,12 @@ public async Task Delete(string id) throw new ArgumentNullException(nameof(id)); } - try - { - var result = await _roleService.DeleteAsync(id); - return Json(new - { - success = result, - message = result ? string.Empty : Messages.DeleteRoleFailed - }); - } - catch (Exception ex) + var result = await _roleService.DeleteAsync(id); + return Json(new { - return Json(new - { - success = false, - message = ex.Message - }); - } + success = result, + message = result ? string.Empty : Messages.DeleteRoleFailed + }); } private static RoleVM ToViewModel(Role role) @@ -179,7 +150,6 @@ private static RoleVM ToViewModel(Role role) return new RoleVM { Id = role.Id, - Code = role.Code, Name = role.Name, Description = role.Description, IsSystem = role.IsSystem, diff --git a/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs b/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs index 21cc5083..5e6752c5 100644 --- a/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs +++ b/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs @@ -191,7 +191,6 @@ protected static readonly } }.ToFrozenDictionary(); - protected const string GlobalMatchPatten = "GLOBAL_{0}"; protected const string AppMatchPatten = "APP_{0}_{1}"; private readonly IPermissionService _permissionService; @@ -232,8 +231,8 @@ public override async Task OnActionExecutionAsync(ActionExecutingContext context var userFunctions = await _permissionService.GetUserPermission(userId); //judge global - var matchKey = string.Format(GlobalMatchPatten, _functionKey); - if (userFunctions.Contains(matchKey)) + var matchKey = _functionKey; + if (userFunctions.Contains(_functionKey)) { await base.OnActionExecutionAsync(context, next); return; diff --git a/src/AgileConfig.Server.Apisite/Models/RoleVM.cs b/src/AgileConfig.Server.Apisite/Models/RoleVM.cs index a56d7680..8b3d6191 100644 --- a/src/AgileConfig.Server.Apisite/Models/RoleVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/RoleVM.cs @@ -9,10 +9,6 @@ public class RoleVM { public string Id { get; set; } - [Required] - [MaxLength(64)] - public string Code { get; set; } - [Required] [MaxLength(128)] public string Name { get; set; } diff --git a/src/AgileConfig.Server.Data.Entity/Role.cs b/src/AgileConfig.Server.Data.Entity/Role.cs deleted file mode 100644 index 92ed1a3f..00000000 --- a/src/AgileConfig.Server.Data.Entity/Role.cs +++ /dev/null @@ -1,38 +0,0 @@ -using FreeSql.DataAnnotations; -using MongoDB.Bson.Serialization.Attributes; -using System; -using AgileConfig.Server.Common; - -namespace AgileConfig.Server.Data.Entity -{ - [Table(Name = "agc_role_definition")] - [OraclePrimaryKeyName("agc_role_definition_pk")] - public class RoleDefinition : IEntity - { - [Column(Name = "id", StringLength = 64)] - public string Id { get; set; } - - [Column(Name = "code", StringLength = 64)] - public string Code { get; set; } - - [Column(Name = "name", StringLength = 128)] - public string Name { get; set; } - - [Column(Name = "description", StringLength = 512)] - public string Description { get; set; } - - [Column(Name = "is_system")] - public bool IsSystem { get; set; } - - [Column(Name = "functions", StringLength = -1)] - public string FunctionsJson { get; set; } - - [Column(Name = "create_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime CreateTime { get; set; } - - [Column(Name = "update_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime? UpdateTime { get; set; } - } -} diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/AgileConfig.Server.Data.Repository.Freesql.csproj b/src/AgileConfig.Server.Data.Repository.Freesql/AgileConfig.Server.Data.Repository.Freesql.csproj index 2173543c..929a7219 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/AgileConfig.Server.Data.Repository.Freesql.csproj +++ b/src/AgileConfig.Server.Data.Repository.Freesql/AgileConfig.Server.Data.Repository.Freesql.csproj @@ -9,6 +9,7 @@ + diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs index fadeaba3..6e4325f7 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs @@ -1,7 +1,8 @@ -using AgileConfig.Server.Common; +using AgileConfig.Server.Common; using AgileConfig.Server.Data.Abstraction; using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; +using AgileConfig.Server.IService; using System; using System.Collections.Generic; using System.Text.Json; @@ -20,15 +21,15 @@ public SysInitRepository(IFreeSqlFactory freeSqlFactory) public string? GetDefaultEnvironmentFromDb() { var setting = freeSqlFactory.Create().Select().Where(x => x.Id == SystemSettings.DefaultEnvironmentKey) - .ToOne(); + .ToOne(); - return setting?.Value; - } + return setting?.Value; + } public string? GetJwtTokenSecret() { var setting = freeSqlFactory.Create().Select().Where(x => x.Id == SystemSettings.DefaultJwtSecretKey) - .ToOne(); + .ToOne(); return setting?.Value; } @@ -40,116 +41,183 @@ public void SaveInitSetting(Setting setting) public bool InitSa(string password) { - if (string.IsNullOrEmpty(password)) - { - throw new ArgumentNullException(nameof(password)); - } + if (string.IsNullOrEmpty(password)) + { + throw new ArgumentNullException(nameof(password)); + } var newSalt = Guid.NewGuid().ToString("N"); - password = Encrypt.Md5((password + newSalt)); + password = Encrypt.Md5((password + newSalt)); - var sql = freeSqlFactory.Create(); + var sql = freeSqlFactory.Create(); - EnsureSystemRoles(sql); + EnsureSystemRoles(sql); - var user = new User(); + var user = new User(); user.Id = SystemSettings.SuperAdminId; - user.Password = password; + user.Password = password; user.Salt = newSalt; user.Status = UserStatus.Normal; user.Team = ""; user.CreateTime = DateTime.Now; user.UserName = SystemSettings.SuperAdminUserName; - sql.Insert(user).ExecuteAffrows(); + sql.Insert(user).ExecuteAffrows(); var now = DateTime.Now; var userRoles = new List(); - userRoles.Add(new UserRole() - { - Id = Guid.NewGuid().ToString("N"), - RoleId = SystemRoleConstants.SuperAdminId, + userRoles.Add(new UserRole() + { + Id = Guid.NewGuid().ToString("N"), + RoleId = SystemRoleConstants.SuperAdminId, UserId = SystemSettings.SuperAdminId, CreateTime = now }); - userRoles.Add(new UserRole() - { - Id = Guid.NewGuid().ToString("N"), - RoleId = SystemRoleConstants.AdminId, - UserId = SystemSettings.SuperAdminId, - CreateTime = now + userRoles.Add(new UserRole() + { + Id = Guid.NewGuid().ToString("N"), +RoleId = SystemRoleConstants.AdminId, + UserId = SystemSettings.SuperAdminId, + CreateTime = now }); sql.Insert(userRoles).ExecuteAffrows(); - return true; + return true; } - public bool HasSa() + public bool HasSa() { - var anySa = freeSqlFactory.Create().Select().Any(x => x.Id == SystemSettings.SuperAdminId); + var anySa = freeSqlFactory.Create().Select().Any(x => x.Id == SystemSettings.SuperAdminId); - return anySa; + return anySa; } public bool InitDefaultApp(string appName) { if (string.IsNullOrEmpty(appName)) { - throw new ArgumentNullException(nameof(appName)); - } + throw new ArgumentNullException(nameof(appName)); + } var sql = freeSqlFactory.Create(); var anyDefaultApp = sql.Select().Any(x => x.Id == appName); - ; - if (!anyDefaultApp) - { - sql.Insert(new App() - { - Id = appName, - Name = appName, - Group = "", - Secret = "", - CreateTime = DateTime.Now, - Enabled = true, - Type = AppType.PRIVATE, - AppAdmin = SystemSettings.SuperAdminId - }).ExecuteAffrows(); + ; + if (!anyDefaultApp) + { + sql.Insert(new App() + { + Id = appName, + Name = appName, + Group = "", + Secret = "", +CreateTime = DateTime.Now, + Enabled = true, + Type = AppType.PRIVATE, + AppAdmin = SystemSettings.SuperAdminId + }).ExecuteAffrows(); } - return true; + return true; } private static void EnsureSystemRoles(IFreeSql sql) { - EnsureRole(sql, SystemRoleConstants.SuperAdminId, SystemRoleConstants.SuperAdminCode, "Super Administrator"); - EnsureRole(sql, SystemRoleConstants.AdminId, SystemRoleConstants.AdminCode, "Administrator"); - EnsureRole(sql, SystemRoleConstants.OperatorId, SystemRoleConstants.OperatorCode, "Operator"); + // Super Admin gets all permissions + var superAdminPermissions = GetSuperAdminPermissions(); +EnsureRole(sql, SystemRoleConstants.SuperAdminId, SystemRoleConstants.SuperAdminCode, "Super Administrator", superAdminPermissions); + + // Admin gets all permissions + var adminPermissions = GetAdminPermissions(); + EnsureRole(sql, SystemRoleConstants.AdminId, SystemRoleConstants.AdminCode, "Administrator", adminPermissions); + + // Operator gets limited permissions + var operatorPermissions = GetOperatorPermissions(); + EnsureRole(sql, SystemRoleConstants.OperatorId, SystemRoleConstants.OperatorCode, "Operator", operatorPermissions); } - private static void EnsureRole(IFreeSql sql, string id, string code, string name) + private static List GetSuperAdminPermissions() { - var role = sql.Select().Where(x => x.Id == id).First(); - if (role == null) +// SuperAdmin has all permissions +return new List { - sql.Insert(new Role - { - Id = id, - Code = code, - Name = name, - Description = name, - IsSystem = true, - FunctionsJson = JsonSerializer.Serialize(new List()), - CreateTime = DateTime.Now - }).ExecuteAffrows(); - } - else + Functions.App_Add, + Functions.App_Edit, + Functions.App_Delete, + Functions.App_Auth, + + Functions.Config_Add, + Functions.Config_Edit, + Functions.Config_Delete, + Functions.Config_Publish, + Functions.Config_Offline, + + Functions.Node_Add, + Functions.Node_Delete, + + Functions.Client_Disconnect, + + Functions.User_Add, + Functions.User_Edit, + Functions.User_Delete, + + Functions.Role_Add, + Functions.Role_Edit, + Functions.Role_Delete + }; + } + + private static List GetAdminPermissions() + { + // Admin has all permissions same as SuperAdmin + return GetSuperAdminPermissions(); + } + + private static List GetOperatorPermissions() + { + // Operator has limited permissions: + // - App: Add, Edit + // - Config: Add, Edit, Delete, Publish, Offline + return new List { - role.Code = code; - role.Name = name; - role.Description = name; - role.IsSystem = true; - sql.Update().SetSource(role).ExecuteAffrows(); + Functions.App_Add, + Functions.App_Edit, + + Functions.Config_Add, +Functions.Config_Edit, + Functions.Config_Delete, + Functions.Config_Publish, + Functions.Config_Offline + }; + } + + private static void EnsureRole(IFreeSql sql, string id, string code, string name, List functions) + { + var role = sql.Select().Where(x => x.Id == id).First(); + var functionsJson = JsonSerializer.Serialize(functions); + + if (role == null) + { + sql.Insert(new Role + { + Id = id, + Code = code, +Name = name, + Description = name, + IsSystem = true, + FunctionsJson = functionsJson, + CreateTime = DateTime.Now + }).ExecuteAffrows(); + } + else + { + role.Code = code; + role.Name = name; + role.Description = name; + role.IsSystem = true; + role.FunctionsJson = functionsJson; + role.UpdateTime = DateTime.Now; + sql.Update().SetSource(role).ExecuteAffrows(); } } -} \ No newline at end of file +} diff --git a/src/AgileConfig.Server.Service/PermissionService.cs b/src/AgileConfig.Server.Service/PermissionService.cs index 8145ced5..44363908 100644 --- a/src/AgileConfig.Server.Service/PermissionService.cs +++ b/src/AgileConfig.Server.Service/PermissionService.cs @@ -1,4 +1,4 @@ -using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Data.Entity; using AgileConfig.Server.IService; using System.Collections.Generic; using System.Threading.Tasks; @@ -11,278 +11,278 @@ namespace AgileConfig.Server.Service { public class PermissionService : IPermissionService { - private readonly IUserRoleRepository _userRoleRepository; - private readonly IRoleDefinitionRepository _roleDefinitionRepository; - private readonly IUserAppAuthRepository _userAppAuthRepository; - private readonly IAppRepository _appRepository; + private readonly IUserRoleRepository _userRoleRepository; + private readonly IRoleDefinitionRepository _roleDefinitionRepository; + private readonly IUserAppAuthRepository _userAppAuthRepository; + private readonly IAppRepository _appRepository; - public PermissionService( - IUserRoleRepository userRoleRepository, + public PermissionService( + IUserRoleRepository userRoleRepository, IRoleDefinitionRepository roleDefinitionRepository, - IUserAppAuthRepository userAppAuthRepository, - IAppRepository appRepository) + IUserAppAuthRepository userAppAuthRepository, + IAppRepository appRepository) { _userRoleRepository = userRoleRepository; _roleDefinitionRepository = roleDefinitionRepository; - _userAppAuthRepository = userAppAuthRepository; - _appRepository = appRepository; - } + _userAppAuthRepository = userAppAuthRepository; + _appRepository = appRepository; + } private static readonly List Template_SuperAdminPermissions = [ - "GLOBAL_" + Functions.App_Add, - "GLOBAL_" + Functions.App_Delete, - "GLOBAL_" + Functions.App_Edit, - "GLOBAL_" + Functions.App_Auth, - - "GLOBAL_" + Functions.Config_Add, - "GLOBAL_" + Functions.Config_Delete, - "GLOBAL_" + Functions.Config_Edit, - "GLOBAL_" + Functions.Config_Offline, - "GLOBAL_" + Functions.Config_Publish, - - "GLOBAL_" + Functions.Node_Add, - "GLOBAL_" + Functions.Node_Delete, - - "GLOBAL_" + Functions.Client_Disconnect, - - "GLOBAL_" + Functions.User_Add, - "GLOBAL_" + Functions.User_Edit, - "GLOBAL_" + Functions.User_Delete, - "GLOBAL_" + Functions.Role_Add, - "GLOBAL_" + Functions.Role_Edit, - "GLOBAL_" + Functions.Role_Delete + Functions.App_Add, + Functions.App_Delete, + Functions.App_Edit, + Functions.App_Auth, + + Functions.Config_Add, + Functions.Config_Delete, + Functions.Config_Edit, + Functions.Config_Offline, + Functions.Config_Publish, + + Functions.Node_Add, + Functions.Node_Delete, + +Functions.Client_Disconnect, + + Functions.User_Add, + Functions.User_Edit, + Functions.User_Delete, + Functions.Role_Add, + Functions.Role_Edit, + Functions.Role_Delete ]; private static readonly List Template_NormalAdminPermissions = [ - "GLOBAL_" + Functions.App_Add, - "GLOBAL_" + Functions.Node_Add, - "GLOBAL_" + Functions.Node_Delete, - "GLOBAL_" + Functions.Client_Disconnect, - - "GLOBAL_" + Functions.User_Add, - "GLOBAL_" + Functions.User_Edit, - "GLOBAL_" + Functions.User_Delete, - "GLOBAL_" + Functions.Role_Add, - "GLOBAL_" + Functions.Role_Edit, - "GLOBAL_" + Functions.Role_Delete, - - "APP_{0}_" + Functions.App_Delete, - "APP_{0}_" + Functions.App_Edit, - "APP_{0}_" + Functions.App_Auth, - - "APP_{0}_" + Functions.Config_Add, - "APP_{0}_" + Functions.Config_Delete, - "APP_{0}_" + Functions.Config_Edit, - "APP_{0}_" + Functions.Config_Offline, - "APP_{0}_" + Functions.Config_Publish - ]; - - private static readonly List Template_NormalUserPermissions_Edit = + Functions.App_Add, + Functions.Node_Add, + Functions.Node_Delete, + Functions.Client_Disconnect, + + Functions.User_Add, + Functions.User_Edit, + Functions.User_Delete, + Functions.Role_Add, + Functions.Role_Edit, + Functions.Role_Delete, + + "APP_{0}_" + Functions.App_Delete, + "APP_{0}_" + Functions.App_Edit, + "APP_{0}_" + Functions.App_Auth, + + "APP_{0}_" + Functions.Config_Add, + "APP_{0}_" + Functions.Config_Delete, + "APP_{0}_" + Functions.Config_Edit, + "APP_{0}_" + Functions.Config_Offline, + "APP_{0}_" + Functions.Config_Publish + ]; + + private static readonly List Template_NormalUserPermissions_Edit = [ "APP_{0}_" + Functions.Config_Add, - "APP_{0}_" + Functions.Config_Delete, - "APP_{0}_" + Functions.Config_Edit + "APP_{0}_" + Functions.Config_Delete, + "APP_{0}_" + Functions.Config_Edit ]; private static readonly List Template_NormalUserPermissions_Publish = [ "APP_{0}_" + Functions.Config_Offline, - "APP_{0}_" + Functions.Config_Publish - ]; + "APP_{0}_" + Functions.Config_Publish + ]; - private async Task> GetAdminUserFunctions(string userId) + private async Task> GetAdminUserFunctions(string userId) { var userFunctions = new List(); - // Retrieve applications where the user is an administrator. - var adminApps = await GetUserAdminApps(userId); - Template_NormalAdminPermissions.Where(x => x.StartsWith("GLOBAL_")).ToList().ForEach( - key => { - userFunctions.Add(key); - } - ); - Template_NormalUserPermissions_Edit.Where(x => x.StartsWith("GLOBAL_")).ToList().ForEach( - key => { - userFunctions.Add(key); - } - ); - Template_NormalUserPermissions_Publish.Where(x => x.StartsWith("GLOBAL_")).ToList().ForEach( - key => { - userFunctions.Add(key); - } - ); - foreach (var app in adminApps) - { - foreach (var temp in Template_NormalAdminPermissions) - { - if (temp.StartsWith("APP_{0}_")) - { - userFunctions.Add(string.Format(temp, app.Id)); - } + // Retrieve applications where the user is an administrator. + var adminApps = await GetUserAdminApps(userId); + Template_NormalAdminPermissions.Where(x => !x.StartsWith("APP_")).ToList().ForEach( + key => { + userFunctions.Add(key); + } + ); + Template_NormalUserPermissions_Edit.Where(x => !x.StartsWith("APP_")).ToList().ForEach( + key => { + userFunctions.Add(key); + } + ); + Template_NormalUserPermissions_Publish.Where(x => !x.StartsWith("APP_")).ToList().ForEach( + key => { + userFunctions.Add(key); + } + ); + foreach (var app in adminApps) + { + foreach (var temp in Template_NormalAdminPermissions) + { + if (temp.StartsWith("APP_{0}_")) + { + userFunctions.Add(string.Format(temp, app.Id)); + } } - } + } //EditConfigPermissionKey var editPermissionApps = await GetUserAuthApp(userId, EditConfigPermissionKey); - foreach (var app in editPermissionApps) - { - foreach (var temp in Template_NormalUserPermissions_Edit) - { - if (temp.StartsWith("APP_{0}_")) - { - userFunctions.Add(string.Format(temp, app.Id)); - } - } + foreach (var app in editPermissionApps) + { + foreach (var temp in Template_NormalUserPermissions_Edit) + { + if (temp.StartsWith("APP_{0}_")) + { + userFunctions.Add(string.Format(temp, app.Id)); + } + } } - //PublishConfigPermissionKey - var publishPermissionApps = await GetUserAuthApp(userId, PublishConfigPermissionKey); - foreach (var app in publishPermissionApps) + //PublishConfigPermissionKey + var publishPermissionApps = await GetUserAuthApp(userId, PublishConfigPermissionKey); + foreach (var app in publishPermissionApps) { - foreach (var temp in Template_NormalUserPermissions_Publish) - { - if (temp.StartsWith("APP_{0}_")) - { - userFunctions.Add(string.Format(temp, app.Id)); - } - } - } + foreach (var temp in Template_NormalUserPermissions_Publish) + { + if (temp.StartsWith("APP_{0}_")) + { + userFunctions.Add(string.Format(temp, app.Id)); + } + } + } return userFunctions; - } + } private async Task> GetNormalUserFunctions(string userId) { - var userFunctions = new List(); - //EditConfigPermissionKey - var editPermissionApps = await GetUserAuthApp(userId, EditConfigPermissionKey); + var userFunctions = new List(); + //EditConfigPermissionKey + var editPermissionApps = await GetUserAuthApp(userId, EditConfigPermissionKey); foreach (var app in editPermissionApps) - { + { foreach (var temp in Template_NormalUserPermissions_Edit) { - if (temp.StartsWith("GLOBAL_")) - { - userFunctions.Add(temp); - } - if (temp.StartsWith("APP_{0}_")) - { - userFunctions.Add(string.Format(temp, app.Id)); - } - } - } - //PublishConfigPermissionKey - var publishPermissionApps = await GetUserAuthApp(userId, PublishConfigPermissionKey); - foreach (var app in publishPermissionApps) + if (!temp.StartsWith("APP_")) + { + userFunctions.Add(temp); + } + if (temp.StartsWith("APP_{0}_")) { + userFunctions.Add(string.Format(temp, app.Id)); + } + } + } + //PublishConfigPermissionKey + var publishPermissionApps = await GetUserAuthApp(userId, PublishConfigPermissionKey); + foreach (var app in publishPermissionApps) + { foreach (var temp in Template_NormalUserPermissions_Publish) - { - if (temp.StartsWith("GLOBAL_")) - { - userFunctions.Add(temp); - } - if (temp.StartsWith("APP_{0}_")) - { - userFunctions.Add(string.Format(temp, app.Id)); - } - } - } + { + if (!temp.StartsWith("APP_")) + { + userFunctions.Add(temp); + } + if (temp.StartsWith("APP_{0}_")) + { + userFunctions.Add(string.Format(temp, app.Id)); + } + } + } return userFunctions; } /// - /// Retrieve the permission template for a user based on roles. + /// Retrieve the permission template for a user based on roles. /// - /// Identifier of the user requesting permissions. + /// Identifier of the user requesting permissions. /// List of permission keys granted to the user. public async Task> GetUserPermission(string userId) { - var userRoles = await _userRoleRepository.QueryAsync(x => x.UserId == userId); - var roleIds = userRoles.Select(x => x.RoleId).Distinct().ToList(); - if (!roleIds.Any()) - { - return new List(); - } + var userRoles = await _userRoleRepository.QueryAsync(x => x.UserId == userId); + var roleIds = userRoles.Select(x => x.RoleId).Distinct().ToList(); + if (!roleIds.Any()) + { + return new List(); + } - var roleDefinitions = await _roleDefinitionRepository.QueryAsync(x => roleIds.Contains(x.Id)); - var systemRoles = roleDefinitions.Where(r => r.IsSystem).ToList(); - var customRoles = roleDefinitions.Where(r => !r.IsSystem).ToList(); + var roleDefinitions = await _roleDefinitionRepository.QueryAsync(x => roleIds.Contains(x.Id)); + var systemRoles = roleDefinitions.Where(r => r.IsSystem).ToList(); + var customRoles = roleDefinitions.Where(r => !r.IsSystem).ToList(); - var customFunctions = customRoles.SelectMany(GetRoleFunctions).ToList(); + var customFunctions = customRoles.SelectMany(GetRoleFunctions).ToList(); - if (systemRoles.Any(r => r.Id == SystemRoleConstants.SuperAdminId)) - { - return Template_SuperAdminPermissions.Concat(customFunctions).Distinct().ToList(); - } + if (systemRoles.Any(r => r.Id == SystemRoleConstants.SuperAdminId)) + { + return Template_SuperAdminPermissions.Concat(customFunctions).Distinct().ToList(); + } - var userFunctions = new List(); + var userFunctions = new List(); if (systemRoles.Any(r => r.Id == SystemRoleConstants.AdminId)) { - userFunctions.AddRange(await GetAdminUserFunctions(userId)); + userFunctions.AddRange(await GetAdminUserFunctions(userId)); } - if (systemRoles.Any(r => r.Id == SystemRoleConstants.OperatorId)) + if (systemRoles.Any(r => r.Id == SystemRoleConstants.OperatorId)) { - userFunctions.AddRange(await GetNormalUserFunctions(userId)); - } + userFunctions.AddRange(await GetNormalUserFunctions(userId)); + } - userFunctions.AddRange(customFunctions); + userFunctions.AddRange(customFunctions); - return userFunctions.Distinct().ToList(); + return userFunctions.Distinct().ToList(); } private static IEnumerable GetRoleFunctions(Role role) { - if (role == null || string.IsNullOrWhiteSpace(role.FunctionsJson)) - { - return Enumerable.Empty(); + if (role == null || string.IsNullOrWhiteSpace(role.FunctionsJson)) + { + return Enumerable.Empty(); } - try - { - var functions = JsonSerializer.Deserialize>(role.FunctionsJson); - return functions ?? Enumerable.Empty(); + try + { + var functions = JsonSerializer.Deserialize>(role.FunctionsJson); + return functions ?? Enumerable.Empty(); } catch - { - return Enumerable.Empty(); - } + { + return Enumerable.Empty(); + } } - /// - /// Retrieve applications where the user has been explicitly authorized. + /// + /// Retrieve applications where the user has been explicitly authorized. /// - /// Identifier of the user whose application authorizations are requested. - /// Permission key used to filter authorized applications. + /// Identifier of the user whose application authorizations are requested. + /// Permission key used to filter authorized applications. /// List of applications the user can access for the specified permission. private async Task> GetUserAuthApp(string userId, string authPermissionKey) { var apps = new List(); - var userAuths = - await _userAppAuthRepository.QueryAsync(x => x.UserId == userId && x.Permission == authPermissionKey); - foreach (var appAuth in userAuths) - { + var userAuths = + await _userAppAuthRepository.QueryAsync(x => x.UserId == userId && x.Permission == authPermissionKey); + foreach (var appAuth in userAuths) + { var app = await _appRepository.GetAsync(appAuth.AppId); - if (app!= null) - { - apps.Add(app); - } - } + if (app!= null) + { + apps.Add(app); + } + } - return apps; - } + return apps; + } - /// - /// Retrieve applications managed by the user. + /// + /// Retrieve applications managed by the user. /// /// Identifier of the user who administers the applications. /// List of applications where the user is the administrator. private async Task> GetUserAdminApps(string userId) { - return await _appRepository.QueryAsync(x => x.AppAdmin == userId); + return await _appRepository.QueryAsync(x => x.AppAdmin == userId); } public string EditConfigPermissionKey => "EDIT_CONFIG"; - public string PublishConfigPermissionKey => "PUBLISH_CONFIG"; + public string PublishConfigPermissionKey => "PUBLISH_CONFIG"; } } diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/components/Authorized/AuthorizedElement.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/components/Authorized/AuthorizedElement.tsx index 5e9d4f08..6525299c 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/components/Authorized/AuthorizedElement.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/components/Authorized/AuthorizedElement.tsx @@ -12,11 +12,12 @@ export const checkUserPermission = (functions:string[],judgeKey:string, appid:st if (appid) { appId = appid ; } - let matchKey = ('GLOBAL_'+ judgeKey); - let key = functions.find(x=>x === matchKey); + // Check for global permission (without GLOBAL_ prefix) + let key = functions.find(x=>x === judgeKey); if (key) return true; - matchKey = ('APP_'+ appId + '_' + judgeKey); + // Check for app-specific permission + let matchKey = ('APP_'+ appId + '_' + judgeKey); key = functions.find(x=>x === matchKey); if (key) return true; @@ -28,7 +29,7 @@ const AuthorizedEle: React.FunctionComponent = (props)=>{ let functions:string[] = []; if (props.authority) { functions = props.authority; - } else { +} else { functions = getFunctions(); } @@ -36,4 +37,3 @@ const AuthorizedEle: React.FunctionComponent = (props)=>{ }; export default AuthorizedEle; - diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts index 2587ff70..54795148 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts @@ -158,7 +158,7 @@ export default { 'pages.client.disconnect_message': `Are you sure to disconnect the client ?`, 'pages.logs.table.appname': `AppName`, - 'pages.logs.table.type': 'Type', + 'pages.logs.table.type': 'Role', 'pages.logs.table.type.0': 'Normal', 'pages.logs.table.type.1': 'Warn', 'pages.logs.table.time': `LogTime`, @@ -252,7 +252,7 @@ export default { // User Management 'pages.user.table.cols.username': 'Username', 'pages.user.table.cols.team': 'Team', - 'pages.user.table.cols.usertype': 'Type', + 'pages.user.table.cols.userrole': 'Role', 'pages.user.table.cols.status': 'Status', 'pages.user.table.cols.action': 'Action', 'pages.user.table.cols.action.edit': 'Edit', @@ -264,18 +264,14 @@ export default { 'pages.user.form.username': 'Username', 'pages.user.form.password': 'Password', 'pages.user.form.team': 'Team', - 'pages.user.form.usertype': 'Type', + 'pages.user.form.userrole': 'Role', 'pages.user.form.status': 'Status', - 'pages.user.usertype.normaluser': 'Operator', - 'pages.user.usertype.admin': 'Administrator', - 'pages.user.usertype.superadmin': 'Super Administrator', 'pages.user.status.normal': 'Normal', 'pages.user.status.deleted': 'Deleted', 'pages.user.confirm_reset': 'Are you sure to reset user', 'pages.user.confirm_delete': 'Are you sure to delete user', 'pages.user.reset_password_default': "'s password to default password【123456】?", 'pages.role.table.cols.name': 'Name', - 'pages.role.table.cols.code': 'Code', 'pages.role.table.cols.description': 'Description', 'pages.role.table.cols.system': 'System Role', 'pages.role.system.yes': 'Yes', @@ -287,7 +283,6 @@ export default { 'pages.role.table.cols.action.delete': 'Delete', 'pages.role.form.title.add': 'Add Role', 'pages.role.form.title.edit': 'Edit Role', - 'pages.role.form.code': 'Code', 'pages.role.form.name': 'Name', 'pages.role.form.description': 'Description', 'pages.role.form.functions': 'Permissions', @@ -298,6 +293,25 @@ export default { 'pages.role.save_fail': 'Failed to save role', 'pages.role.load_failed': 'Failed to load roles', 'pages.role.permissions.load_failed': 'Failed to load permissions', + 'pages.role.permissions.all': 'All', + 'pages.role.permissions.APP_ADD': 'Add App', + 'pages.role.permissions.APP_EDIT': 'Edit App', + 'pages.role.permissions.APP_DELETE': 'Delete App', + 'pages.role.permissions.APP_AUTH': 'Authorize App', + 'pages.role.permissions.CONFIG_ADD': 'Add Config', + 'pages.role.permissions.CONFIG_EDIT': 'Edit Config', + 'pages.role.permissions.CONFIG_DELETE': 'Delete Config', + 'pages.role.permissions.CONFIG_PUBLISH': 'Publish Config', + 'pages.role.permissions.CONFIG_OFFLINE': 'Offline Config', + 'pages.role.permissions.NODE_ADD': 'Add Node', + 'pages.role.permissions.NODE_DELETE': 'Delete Node', + 'pages.role.permissions.CLIENT_DISCONNECT': 'Disconnect Client', + 'pages.role.permissions.USER_ADD': 'Add User', + 'pages.role.permissions.USER_EDIT': 'Edit User', + 'pages.role.permissions.USER_DELETE': 'Delete User', + 'pages.role.permissions.ROLE_ADD': 'Add Role', + 'pages.role.permissions.ROLE_EDIT': 'Edit Role', + 'pages.role.permissions.ROLE_DELETE': 'Delete Role', // Service Management 'pages.service.table.cols.servicename': 'Service Name', diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts index 77b4b87d..7c61030a 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts @@ -250,7 +250,7 @@ export default { // User management 'pages.user.table.cols.username': '用户名', 'pages.user.table.cols.team': '团队', - 'pages.user.table.cols.usertype': '类型', + 'pages.user.table.cols.userrole': '角色', 'pages.user.table.cols.status': '状态', 'pages.user.table.cols.action': '操作', 'pages.user.table.cols.action.edit': '编辑', @@ -262,18 +262,14 @@ export default { 'pages.user.form.username': '用户名', 'pages.user.form.password': '密码', 'pages.user.form.team': '团队', - 'pages.user.form.usertype': '类型', + 'pages.user.form.userrole': '角色', 'pages.user.form.status': '状态', - 'pages.user.usertype.normaluser': '操作员', - 'pages.user.usertype.admin': '管理员', - 'pages.user.usertype.superadmin': '超级管理员', 'pages.user.status.normal': '正常', 'pages.user.status.deleted': '已删除', 'pages.user.confirm_reset': '确定重置用户', 'pages.user.confirm_delete': '确定删除用户', 'pages.user.reset_password_default': '的密码为默认密码【123456】?', 'pages.role.table.cols.name': '名称', - 'pages.role.table.cols.code': '编码', 'pages.role.table.cols.description': '描述', 'pages.role.table.cols.system': '系统角色', 'pages.role.system.yes': '是', @@ -285,7 +281,6 @@ export default { 'pages.role.table.cols.action.delete': '删除', 'pages.role.form.title.add': '新增角色', 'pages.role.form.title.edit': '编辑角色', - 'pages.role.form.code': '角色编码', 'pages.role.form.name': '角色名称', 'pages.role.form.description': '描述', 'pages.role.form.functions': '权限', @@ -296,6 +291,25 @@ export default { 'pages.role.save_fail': '保存角色失败', 'pages.role.load_failed': '加载角色失败', 'pages.role.permissions.load_failed': '加载权限列表失败', + 'pages.role.permissions.all': '所有权限', + 'pages.role.permissions.APP_ADD': '新增应用', + 'pages.role.permissions.APP_EDIT': '编辑应用', + 'pages.role.permissions.APP_DELETE': '删除应用', + 'pages.role.permissions.APP_AUTH': '应用授权', + 'pages.role.permissions.CONFIG_ADD': '新增配置', + 'pages.role.permissions.CONFIG_EDIT': '编辑配置', + 'pages.role.permissions.CONFIG_DELETE': '删除配置', + 'pages.role.permissions.CONFIG_PUBLISH': '发布配置', + 'pages.role.permissions.CONFIG_OFFLINE': '下线配置', + 'pages.role.permissions.NODE_ADD': '新增节点', + 'pages.role.permissions.NODE_DELETE': '删除节点', + 'pages.role.permissions.CLIENT_DISCONNECT': '断开客户端', + 'pages.role.permissions.USER_ADD': '新增用户', + 'pages.role.permissions.USER_EDIT': '编辑用户', + 'pages.role.permissions.USER_DELETE': '删除用户', + 'pages.role.permissions.ROLE_ADD': '新增角色', + 'pages.role.permissions.ROLE_EDIT': '编辑角色', + 'pages.role.permissions.ROLE_DELETE': '删除角色', // Service Management 'pages.service.table.cols.servicename': '服务名', diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/data.d.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/data.d.ts index c3d6147a..e7d45f5d 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/data.d.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/data.d.ts @@ -1,6 +1,5 @@ export type RoleItem = { id: string; - code: string; name: string; description?: string; isSystem: boolean; @@ -9,7 +8,6 @@ export type RoleItem = { export type RoleFormValues = { id?: string; - code: string; name: string; description?: string; functions: string[]; diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx index d493abbe..7ed394fa 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx @@ -37,7 +37,7 @@ const RolePage: React.FC = () => { const permissionOptions = supportedPermissions.map((item) => ({ value: item, - label: item, + label: intl.formatMessage({ id: `pages.role.permissions.${item}`, defaultMessage: item }), })); const handleCreate = async (values: RoleFormValues) => { @@ -105,10 +105,6 @@ const RolePage: React.FC = () => { title: intl.formatMessage({ id: 'pages.role.table.cols.name', defaultMessage: 'Name' }), dataIndex: 'name', }, - { - title: intl.formatMessage({ id: 'pages.role.table.cols.code', defaultMessage: 'Code' }), - dataIndex: 'code', - }, { title: intl.formatMessage({ id: 'pages.role.table.cols.description', defaultMessage: 'Description' }), dataIndex: 'description', @@ -130,13 +126,18 @@ const RolePage: React.FC = () => { title: intl.formatMessage({ id: 'pages.role.table.cols.functions', defaultMessage: 'Permissions' }), dataIndex: 'functions', search: false, - render: (_, record) => ( - - {record.functions?.map((fn) => ( - {fn} - ))} - - ), + render: (_, record) => { + if (record.functions?.length === supportedPermissions.length) { + return {intl.formatMessage({ id: 'pages.role.permissions.all', defaultMessage: 'All' })}; + } + return ( + + {record.functions?.map((fn) => ( + {intl.formatMessage({ id: `pages.role.permissions.${fn}`, defaultMessage: fn })} + ))} + + ); + }, }, { title: intl.formatMessage({ id: 'pages.role.table.cols.action', defaultMessage: 'Action' }), @@ -169,8 +170,11 @@ const RolePage: React.FC = () => { columns={columns} request={async () => { const response = await queryRoles(); + const data = response?.data || []; + // filter super admin role + const filteredData = data.filter((role: RoleItem) => role.name !== 'Super Administrator'); return { - data: response?.data || [], + data: filteredData, success: response?.success ?? false, }; }} @@ -203,11 +207,6 @@ const RolePage: React.FC = () => { return success; }} > - { return success; }} > - = (props)=>{ required: true, }, ]} - label={intl.formatMessage({id: 'pages.user.form.usertype'})} + label={intl.formatMessage({id: 'pages.user.form.userrole'})} name="userRoleIds" mode="multiple" - options = {props.roleOptions} - > - + options={props.roleOptions.map(r => ({ + value: r.value, + label: r.label, + }))} + /> ); } diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/index.tsx index 4089482e..f3a796d7 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/User/index.tsx @@ -133,20 +133,32 @@ const hasUserRole = (role:string) => { } const checkUserListModifyPermission = (user:UserItem) => { + // Lower number means higher privilege const authMap:Record = { SuperAdmin: 0, Admin: 1, NormalUser: 2 }; - let currentAuthNum = 2; - const roles = getAuthority(); - if (Array.isArray(roles) && roles.length > 0) { - const sorted = roles.map((x:string) => authMap[x] ?? 3).sort((a, b) => a - b); - if (sorted.length > 0) { - currentAuthNum = sorted[0]; + const myRoles = getAuthority(); + if (!Array.isArray(myRoles) || myRoles.length === 0) return false; + + // If current user is SuperAdmin -> can edit anyone except themselves (optional) + if (myRoles.includes('SuperAdmin')) { + // Prevent editing own account if desired + if (user.userName && user.userName === (typeof localStorage !== 'undefined' ? localStorage.getItem('currentUserName') : undefined)) { + return false; // disallow self-edit via list (modal still available maybe) } + return true; } - const userCodes = user.userRoleCodes || []; - const userSorted = userCodes.map(code => authMap[code] ?? 3).sort((a, b) => a - b); - const userAuthNum = userSorted.length > 0 ? userSorted[0] : 3; + // Determine current user's minimal privilege level + const currentAuthNum = myRoles + .map(r => authMap[r] ?? 999) + .reduce((min, v) => v < min ? v : min, 999); + + // Determine target user's minimal privilege level + const targetCodes = user.userRoleCodes || []; + const userAuthNum = targetCodes.length > 0 + ? targetCodes.map(c => authMap[c] ?? 999).reduce((min, v) => v < min ? v : min, 999) + : 999; + // Allow edit only if current privilege strictly higher than target (numerically lower) return currentAuthNum < userAuthNum; } @@ -206,7 +218,7 @@ const userList:React.FC = () => { }, { title: intl.formatMessage({ - id: 'pages.user.table.cols.usertype', + id: 'pages.user.table.cols.userrole', }), dataIndex: 'userRoleNames', search: false, @@ -402,7 +414,7 @@ const userList:React.FC = () => { }, ]} label={intl.formatMessage({ - id: 'pages.user.form.usertype' + id: 'pages.user.form.userrole' })} name="userRoleIds" mode="multiple" From 4935a8d0794369c0ea291ae774ee542e0501d2d0 Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Tue, 11 Nov 2025 01:13:34 +0800 Subject: [PATCH 06/17] fix obvious issue --- src/Agile.Config.Protocol/VMS.cs | 40 +- src/AgileConfig.Server.Apisite/ASCII_FONT.cs | 11 +- ...AgileConfig - Backup.Server.Apisite.csproj | 65 - .../AgileConfig.Server.Apisite.xml | 1011 ++++----- src/AgileConfig.Server.Apisite/Appsettings.cs | 114 +- .../ConfigureJwtBearerOptions.cs | 2 +- .../Controllers/AdminController.cs | 538 +++-- .../Controllers/AppController.cs | 570 +++-- .../Controllers/ConfigController.cs | 1249 +++++------ .../Controllers/HomeController.cs | 162 +- .../Controllers/RemoteOPController.cs | 188 +- .../RemoteServerProxyController.cs | 172 +- .../Controllers/ReportController.cs | 294 ++- .../Controllers/RoleController.cs | 264 ++- .../Controllers/SSOController.cs | 90 +- .../Controllers/ServerNodeController.cs | 206 +- .../Controllers/ServiceController.cs | 273 +-- .../Controllers/SysLogController.cs | 61 +- .../Controllers/UserController.cs | 412 ++-- .../Controllers/api/AppController.cs | 430 ++-- .../Controllers/api/ConfigController.cs | 367 ++-- .../Controllers/api/Models/ApiAppVM.cs | 118 +- .../Controllers/api/Models/ApiConfigVM.cs | 125 +- .../Controllers/api/Models/ApiNodeVM.cs | 79 +- .../api/Models/ApiPublishTimelineVM.cs | 79 +- .../api/Models/ApiServiceInfoVM.cs | 29 +- .../Controllers/api/Models/HeartbeatParam.cs | 11 +- .../api/Models/HeartbeatResultVM.cs | 12 +- .../api/Models/RegisterResultVM.cs | 11 +- .../api/Models/RegisterServiceInfoVM.cs | 60 +- .../Controllers/api/NodeController.cs | 189 +- .../api/RegisterCenterController.cs | 221 +- .../AdmBasicAuthenticationAttribute.cs | 45 +- .../AppBasicAuthenticationAttribute.cs | 45 +- .../Filters/ModelValidAttribute.cs | 34 +- .../Filters/PermissionCheckAttribute.cs | 413 ++-- .../PermissionCheckByBasicAttribute.cs | 59 +- src/AgileConfig.Server.Apisite/InitService.cs | 146 +- .../Metrics/IMeterService.cs | 21 +- .../Metrics/MeterService.cs | 233 +- .../Models/AppAuthVM.cs | 17 +- .../Models/AppVM.cs | 171 +- .../Models/Binders/EnvQueryStringBinder.cs | 55 +- .../Models/ChangePasswordVM.cs | 17 +- .../Models/ConfigVM.cs | 53 +- .../Models/EnvString.cs | 19 +- .../Models/InitPasswordVM.cs | 15 +- .../Models/LoginVM.cs | 16 +- .../Models/Mapping/ModelMappingExtension.cs | 274 ++- .../Models/PublishLogVM.cs | 17 +- .../Models/RoleVM.cs | 24 +- .../Models/SaveJsonVM.cs | 47 +- .../Models/ServerNodeVM.cs | 29 +- .../Models/ServerStatusReport.cs | 23 +- .../Models/ServiceInfoVM.cs | 10 +- .../Models/UserVM.cs | 43 +- src/AgileConfig.Server.Apisite/Program.cs | 112 +- src/AgileConfig.Server.Apisite/Startup.cs | 252 +-- .../StartupExtension.cs | 141 +- .../UIExtension/ReactUIMiddleware.cs | 261 ++- .../UIExtension/UIFileBag.cs | 26 +- .../Utilites/ControllerExt.cs | 74 +- .../Utilites/IPExt.cs | 47 +- .../Websocket/IWebsocketCollection.cs | 51 +- .../MessageHandlers/MessageHandler.cs | 39 +- .../MessageHandlers/OldMessageHandler.cs | 26 +- .../WebsocketMessageHandlers.cs | 1 + .../Websocket/WebsocketCollection.cs | 311 ++- .../Websocket/WebsocketHandlerMiddleware.cs | 317 ++- src/AgileConfig.Server.Apisite/nlog.config | 33 +- .../DictionaryConvertToJson.cs | 231 +- .../EfLoggerProvider.cs | 49 +- src/AgileConfig.Server.Common/Encrypt.cs | 20 +- src/AgileConfig.Server.Common/EnumExt.cs | 30 +- src/AgileConfig.Server.Common/EnvAccessor.cs | 49 +- .../EventBus/ITinyEventBus.cs | 34 +- .../EventBus/ServiceCollectionExt.cs | 17 +- .../EventBus/TinyEventBus.cs | 120 +- .../ExceptionHandlerMiddleware.cs | 75 +- src/AgileConfig.Server.Common/FunctionUtil.cs | 186 +- src/AgileConfig.Server.Common/Global.cs | 16 +- src/AgileConfig.Server.Common/HttpExt.cs | 103 +- src/AgileConfig.Server.Common/IEntity.cs | 11 +- .../JsonConfigurationFileParser.cs | 172 +- .../Resources/Messages.cs | 492 +++-- .../RestClient/DefaultRestClient.cs | 110 +- .../RestClient/IRestClient.cs | 18 +- .../RestClient/ServiceCollectionEx.cs | 13 +- .../SystemRoleConstants.cs | 19 +- .../SystemSettings.cs | 2 +- .../DbConfig/DbConfigInfo.cs | 30 +- .../DbConfig/DbConfigInfoFactory.cs | 94 +- .../DbConfig/IDbConfigInfo.cs | 13 +- .../DbConfig/IDbConfigInfoFactory.cs | 9 +- .../DbConfig/ServiceCollectionExtension.cs | 20 +- .../IAppInheritancedRepository.cs | 9 +- .../IAppRepository.cs | 9 +- .../IConfigPublishedRepository.cs | 9 +- .../IConfigRepository.cs | 9 +- .../IFunctionRepository.cs | 7 + .../IPublishDetailRepository.cs | 9 +- .../IPublishTimelineRepository.cs | 11 +- .../IRepository.cs | 42 +- .../IRepositoryServiceRegister.cs | 49 +- .../IRoleDefinitionRepository.cs | 9 +- .../IRoleFunctionRepository.cs | 7 + .../IServerNodeRepository.cs | 9 +- .../IServiceInfoRepository.cs | 9 +- .../ISettingRepository.cs | 9 +- .../ISysInitRepository.cs | 8 +- .../ISysLogRepository.cs | 9 +- .../IUow.cs | 20 +- .../IUserAppAuthRepository.cs | 9 +- .../IUserRepository.cs | 9 +- .../IUserRoleRepository.cs | 9 +- src/AgileConfig.Server.Data.Entity/App.cs | 107 +- .../AppInheritanced.cs | 39 +- src/AgileConfig.Server.Data.Entity/Config.cs | 154 +- .../ConfigPublished.cs | 82 +- .../Function.cs | 36 + .../PublishDetail.cs | 53 +- .../PublishTimeline.cs | 52 +- .../RoleDefinition.cs | 50 +- .../RoleFunction.cs | 24 + .../ServerNode.cs | 64 +- .../ServiceInfo.cs | 134 +- src/AgileConfig.Server.Data.Entity/Setting.cs | 33 +- src/AgileConfig.Server.Data.Entity/SysLog.cs | 62 +- src/AgileConfig.Server.Data.Entity/User.cs | 95 +- .../UserAppAuth.cs | 32 +- .../UserRole.cs | 38 +- .../EnsureTables.cs | 153 +- .../EnvFreeSqlFactory.cs | 25 +- .../FluentApi.cs | 75 +- .../FreeSqlContext.cs | 60 +- .../FreeSqlDbContextFactory.cs | 12 +- .../FreeSqlUow.cs | 88 +- .../FreesqlRepository.cs | 162 +- .../IFreeSqlFactory.cs | 9 +- .../IMyFreeSQL.cs | 9 +- .../MyFreeSQL.cs | 133 +- .../ServiceCollectionExt.cs | 17 +- .../MongodbAccess.cs | 24 +- .../MongodbRepository.cs | 79 +- .../MongodbUow.cs | 96 +- .../AppInheritancedRepository.cs | 17 +- .../AppRepository.cs | 17 +- .../ConfigPublishedRepository.cs | 17 +- .../ConfigRepository.cs | 17 +- .../FreesqlRepositoryServiceRegister.cs | 84 +- .../FunctionRepository.cs | 15 + .../PublishDetailRepository.cs | 17 +- .../PublishTimelineRepository.cs | 33 +- .../RoleDefinitionRepository.cs | 11 +- .../RoleFunctionRepository.cs | 15 + .../ServerNodeRepository.cs | 17 +- .../ServiceInfoRepository.cs | 17 +- .../SettingRepository.cs | 17 +- .../SysInitRepository.cs | 306 +-- .../SysLogRepository.cs | 18 +- .../UserAppAuthRepository.cs | 18 +- .../UserRepository.cs | 18 +- .../UserRoleRepository.cs | 17 +- .../AppRepository.cs | 2 +- .../ConfigRepository.cs | 2 +- .../FunctionRepository.cs | 13 + .../MongodbRepositoryExt.cs | 2 +- .../MongodbRepositoryServiceRegister.cs | 104 +- .../PublishDetailRepository.cs | 2 +- .../PublishTimelineRepository.cs | 6 +- .../RoleDefinitionRepository.cs | 19 +- .../RoleFunctionRepository.cs | 13 + .../ServerNodeRepository.cs | 2 +- .../ServiceInfoRepository.cs | 2 +- .../SettingRepository.cs | 2 +- .../SysInitRepository.cs | 57 +- .../SysLogRepository.cs | 2 +- .../UserAppAuthRepository.cs | 2 +- .../UserRepository.cs | 2 +- .../UserRoleRepository.cs | 2 +- .../RepositoryExtension.cs | 124 +- .../AddAppSuccessful.cs | 21 +- .../AddConfigSuccessful.cs | 21 +- .../AddNodeSuccessful.cs | 21 +- .../AddUserSuccessful.cs | 21 +- .../CancelEditConfigSomeSuccessful.cs | 23 +- .../CancelEditConfigSuccessful.cs | 21 +- .../ChangeUserPasswordSuccessful.cs | 17 +- .../DeleteAppSuccessful.cs | 21 +- .../DeleteConfigSuccessful.cs | 21 +- .../DeleteNodeSuccessful.cs | 21 +- .../DeleteSomeConfigSuccessful.cs | 21 +- .../DeleteUserSuccessful.cs | 21 +- .../DisableOrEnableAppSuccessful.cs | 21 +- .../DiscoinnectSuccessful.cs | 21 +- .../EditAppSuccessful.cs | 21 +- .../EditConfigSuccessful.cs | 21 +- .../EditUserSuccessful.cs | 21 +- .../InitSaPasswordSuccessful.cs | 9 +- src/AgileConfig.Server.Event/LoginEvent.cs | 17 +- .../PublishConfigSuccessful.cs | 21 +- .../RegisterAServiceSuccessful.cs | 25 +- .../ResetUserPasswordSuccessful.cs | 21 +- .../RollbackConfigSuccessful.cs | 21 +- .../ServiceInfoStatusUpdateEvents.cs | 42 +- .../UnRegisterAServiceSuccessful.cs | 26 +- .../ConfigStatusUpdateEventHandlers.cs | 199 +- .../ServiceInfoUpdateHandlers.cs | 316 ++- .../SystemEventHandlers.cs | 924 ++++---- .../IAdmBasicAuthService.cs | 15 +- .../IAppBasicAuthService.cs | 15 +- .../IAppService.cs | 68 +- .../IBasicAuthService.cs | 15 +- .../IConfigService.cs | 473 ++-- .../IEventHandlerRegister.cs | 88 +- .../IPermissionService.cs | 87 +- .../IRegisterCenterService.cs | 23 +- .../IRemoteServerNodeProxy.cs | 188 +- .../IRoleService.cs | 24 +- .../IServerNodeService.cs | 41 +- .../IServiceHealthCheckService.cs | 18 +- .../IServiceInfoService.cs | 39 +- .../ISettingService.cs | 66 +- .../ISysLogService.cs | 23 +- .../ISystemInitializationService.cs | 2 + .../IUserService.cs | 35 +- src/AgileConfig.Server.OIDC/IOidcClient.cs | 15 +- src/AgileConfig.Server.OIDC/OidcClient.cs | 134 +- src/AgileConfig.Server.OIDC/OidcSetting.cs | 69 +- .../ServiceCollectionExt.cs | 16 +- .../ConfigfileOidcSettingProvider.cs | 77 +- .../SettingProvider/IOidcSettingProvider.cs | 11 +- .../BasicTokenEndpointAuthMethod.cs | 32 +- .../ITokenEndpointAuthMethod.cs | 11 +- .../NoneTokenEndpointAuthMethod.cs | 25 +- .../PostTokenEndpointAuthMethod.cs | 29 +- .../TokenEndpointAuthMethodFactory.cs | 34 +- .../AdmBasicAuthService.cs | 53 +- .../AppBasicAuthService.cs | 155 +- src/AgileConfig.Server.Service/AppService.cs | 560 +++-- .../ConfigService.cs | 1760 +++++++-------- ...ConfigStatusUpdateEventHandlersRegister.cs | 7 +- .../EventHandlerRegister.cs | 7 +- ...ceInfoStatusUpdateEventHandlersRegister.cs | 13 +- .../SystemEventHandlersRegister.cs | 1 - src/AgileConfig.Server.Service/JwtService.cs | 46 +- .../PermissionService.cs | 353 +-- .../RegisterCenterService.cs | 234 +- .../RemoteServerNodeProxy.cs | 347 ++- src/AgileConfig.Server.Service/RoleService.cs | 217 +- .../ServerNodeService.cs | 198 +- .../ServiceCollectionExt.cs | 53 +- .../ServiceHealthCheckService.cs | 72 +- .../ServiceInfoService.cs | 218 +- .../SettingService.cs | 133 +- .../SysLogService.cs | 161 +- .../SystemInitializationService.cs | 411 +++- src/AgileConfig.Server.Service/UserService.cs | 227 +- .../react-ui-antd/config/routes.ts | 81 +- .../react-ui-antd/src/layouts/BasicLayout.tsx | 30 +- .../react-ui-antd/src/locales/en-US/pages.ts | 10 + .../react-ui-antd/src/locales/zh-CN/pages.ts | 10 + .../react-ui-antd/src/models/functionKeys.ts | 48 +- .../react-ui-antd/src/models/login.ts | 13 +- .../react-ui-antd/src/models/user.ts | 6 +- .../react-ui-antd/src/pages/Apps/index.tsx | 41 +- .../react-ui-antd/src/pages/Clients/index.tsx | 41 +- .../react-ui-antd/src/pages/Configs/index.tsx | 272 ++- .../react-ui-antd/src/pages/Home/index.tsx | 49 +- .../react-ui-antd/src/pages/Logs/index.tsx | 44 +- .../react-ui-antd/src/pages/Nodes/index.tsx | 72 +- .../src/pages/Nodes/index_new.tsx | 289 --- .../react-ui-antd/src/pages/Role/index.tsx | 61 +- .../src/pages/Services/index.tsx | 58 +- .../react-ui-antd/src/pages/User/index.tsx | 154 +- .../react-ui-antd/src/utils/authority.ts | 36 + .../react-ui-antd/src/utils/permission.ts | 34 + .../react-ui-antd/src/utils/permission.tsx | 34 + .../react-ui-antd/tsconfig.json | 1 - .../DictionaryConvertToJsonTests.cs | 290 +-- .../DbConfig/DbConfigInfoFactoryTests.cs | 63 +- .../FreeSQLTests.cs | 85 +- .../FreeSqlUowTests.cs | 283 ++- .../EnsureTablesTests.cs | 181 +- .../ISettingServiceTests.cs | 89 +- .../PostgreSQL/AppServiceTests.cs | 63 +- .../PostgreSQL/ConfigServiceTests.cs | 63 +- .../PostgreSQL/ServerNodeServiceTests.cs | 64 +- .../PostgreSQL/SettingServiceTests.cs | 63 +- .../PostgreSQL/SysLogServiceTests.cs | 64 +- .../mongodb/AppServiceTests.cs | 97 +- .../mongodb/Basic.cs | 48 +- .../mongodb/ConfigServiceTests.cs | 118 +- .../mongodb/ServerNodeServiceTests.cs | 86 +- .../mongodb/SettingServiceTests.cs | 75 +- .../mongodb/SysLogServiceTests.cs | 17 +- .../mysql/AppServiceTests.cs | 60 +- .../mysql/ConfigServiceTests.cs | 67 +- .../mysql/ServerNodeServiceTests.cs | 71 +- .../mysql/SettingServiceTests.cs | 67 +- .../mysql/SysLogServiceTests.cs | 71 +- .../oracle/AppServiceTests.cs | 27 +- .../oracle/ConfigServiceTests.cs | 28 +- .../oracle/ServerNodeServiceTests.cs | 29 +- .../oracle/SettingServiceTests.cs | 28 +- .../oracle/SysLogServiceTests.cs | 29 +- .../sqlite/AppServiceTests.cs | 746 ++++--- .../sqlite/BasicTestService.cs | 100 +- .../sqlite/ConfigServiceTests.cs | 1906 ++++++++--------- .../sqlite/ServerNodeServiceTests.cs | 453 ++-- .../sqlite/SettingServiceTests.cs | 399 ++-- .../sqlite/SysLogServiceTests.cs | 404 ++-- .../sqlserver/AppServiceTests.cs | 27 +- .../sqlserver/ConfigServiceTests.cs | 29 +- .../sqlserver/ServerNodeServiceTests.cs | 30 +- .../sqlserver/SettingServiceTests.cs | 29 +- .../sqlserver/SysLogServiceTests.cs | 30 +- .../BasicAuthenticationAttributeTests.cs | 119 +- test/ApiSiteTests/TestApiConfigController.cs | 42 +- test/ApiSiteTests/TestAppController.cs | 119 +- .../Websocket/WebsocketCollectionTests.cs | 79 +- 321 files changed, 15902 insertions(+), 17285 deletions(-) delete mode 100644 src/AgileConfig.Server.Apisite/AgileConfig - Backup.Server.Apisite.csproj create mode 100644 src/AgileConfig.Server.Data.Abstraction/IFunctionRepository.cs create mode 100644 src/AgileConfig.Server.Data.Abstraction/IRoleFunctionRepository.cs create mode 100644 src/AgileConfig.Server.Data.Entity/Function.cs create mode 100644 src/AgileConfig.Server.Data.Entity/RoleFunction.cs create mode 100644 src/AgileConfig.Server.Data.Repository.Freesql/FunctionRepository.cs create mode 100644 src/AgileConfig.Server.Data.Repository.Freesql/RoleFunctionRepository.cs create mode 100644 src/AgileConfig.Server.Data.Repository.Mongodb/FunctionRepository.cs create mode 100644 src/AgileConfig.Server.Data.Repository.Mongodb/RoleFunctionRepository.cs delete mode 100644 src/AgileConfig.Server.UI/react-ui-antd/src/pages/Nodes/index_new.tsx create mode 100644 src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.ts create mode 100644 src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.tsx diff --git a/src/Agile.Config.Protocol/VMS.cs b/src/Agile.Config.Protocol/VMS.cs index 929bbcbe..9b2da05f 100644 --- a/src/Agile.Config.Protocol/VMS.cs +++ b/src/Agile.Config.Protocol/VMS.cs @@ -1,28 +1,22 @@ -using System; +namespace Agile.Config.Protocol; -namespace Agile.Config.Protocol +public class ActionConst { - public class ActionConst - { - public const string Offline = "offline"; - public const string Reload = "reload"; - public const string Ping = "ping"; - } + public const string Offline = "offline"; + public const string Reload = "reload"; + public const string Ping = "ping"; +} - public class ActionModule - { - public const string RegisterCenter = "r"; - public const string ConfigCenter = "c"; - } +public class ActionModule +{ + public const string RegisterCenter = "r"; + public const string ConfigCenter = "c"; +} - public class WebsocketAction - { - public WebsocketAction() - { - } - public string Module { get; set; } - public string Action { get; set; } +public class WebsocketAction +{ + public string Module { get; set; } + public string Action { get; set; } - public string Data { get; set; } - } -} + public string Data { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/ASCII_FONT.cs b/src/AgileConfig.Server.Apisite/ASCII_FONT.cs index 84acf3d9..82bf3066 100644 --- a/src/AgileConfig.Server.Apisite/ASCII_FONT.cs +++ b/src/AgileConfig.Server.Apisite/ASCII_FONT.cs @@ -1,9 +1,8 @@ using Figgle; -namespace AgileConfig.Server.Apisite +namespace AgileConfig.Server.Apisite; + +[EmbedFiggleFont("Font", "ogre")] +internal static partial class ASCII_FONT { - [EmbedFiggleFont(memberName: "Font", fontName: "ogre")] - internal static partial class ASCII_FONT - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/AgileConfig - Backup.Server.Apisite.csproj b/src/AgileConfig.Server.Apisite/AgileConfig - Backup.Server.Apisite.csproj deleted file mode 100644 index 8c424cc6..00000000 --- a/src/AgileConfig.Server.Apisite/AgileConfig - Backup.Server.Apisite.csproj +++ /dev/null @@ -1,65 +0,0 @@ - - - - net8.0 - InProcess - 1.8 - 1.8 - 1.6.20 - Linux - 1.7.3 - kklldog - kklldog - - - - bin\Debug\AgileConfig.Server.Apisite.xml - 1701;1702;1591;1573 - - - - bin\Release\AgileConfig.Server.Apisite.xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Always - - - PreserveNewest - - - - - - - - - - - - diff --git a/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.xml b/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.xml index f204ec7e..95c42e6e 100644 --- a/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.xml +++ b/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.xml @@ -1,499 +1,516 @@ + - - AgileConfig.Server.Apisite - - - - - 是否演示模式 - - - - - 是否控制台模式 - - - - - is password inited ? - - - - - - 初始化密码 - - - - - - 应用操作接口 - - - - - 获取所有应用 - - - - - - 根据id获取应用 - - 应用id - - - - - 添加应用 - - 应用模型 - - - - - 编辑应用 - - 应用id - 编辑后的应用模型 - - - - - 删除应用 - - 应用id - - - - - 发布某个应用的待发布配置项 - - 应用id - 环境 - - - - - 查询某个应用的发布历史 - - 应用id - 环境 - - - - - 回滚某个应用的发布版本,回滚到 historyId 指定的时刻 - - 发布历史 - 环境 - - - - - 根据appid查所有发布的配置项 , 包括继承过来的配置项. - 注意: 这个接口用的不是用户名密码的认证,用的是appid + secret的认证 - - 应用id - 环境 - - - - - 根据应用id查找配置,这些配置有可能是未发布的配置 。请跟 config/app/{appId} 接口加以区分。 - - 应用id - 环境 - - - - - 根据编号获取配置项的详情 - - 配置id - 环境 - - - - - 添加一个配置项 - - 配置模型 - 环境 - - - - - 编辑一个配置 - - 编号 - 模型 - 环境 - - - - - 删除一个配置 - - 配置id - 环境 - - - - - restful api 返回的 app 模型 - - - - - 是否可继承 - - - - - id - - - - - name - - - - - 密钥 - - - - - 是否启用 - - - - - 关联的app - - - - - 管理员 - - - - - id - - - - - 应用 - - - - - 组 - - - - - 键 - - - - - 值 - - - - - 状态 - - - - - 在线状态 - - - - - 编辑状态 - - - - - 描述 - - - - - 节点地址 - - - - - 备注 - - - - - 状态 - - - - - 最后响应时间 - - - - - 编号 - - - - - 应用id - - - - - 发布时间 - - - - - 发布者 - - - - - 发布版本序号 - - - - - 发布日志 - - - - - 环境 - - - - - 节点操作接口 - - - - - 获取所有节点 - - - - - - 添加节点 - - 节点模型 - - - - - 删除节点 - - 节点地址 - - - - - 在启动跟禁用之间进行切换 - - 应用标识。 - - - - - 获取所有可以继承的app - - - - - - 保存app的授权信息 - - 授权视图模型。 - - - - - 是否只是修改了描述信息 - - 新的配置。 - 旧的配置。 - - - - - 按多条件进行搜索 - - 应用id - 分组 - 键 - 在线状态 - 分页大小 - 当前页 - - - - - 发布所有待发布的配置项 - - - - - - 预览上传的json文件 - - - - - - 导出json文件 - - 应用id - - - - - 获取待发布的明细 - - 应用id - - - - - 获取发布详情的历史 - - 应用标识。 - - - - - 获取json格式的配置 - - 应用id - - - - - 这个Controller用来接受控制台节点发送过来的命令 - - - - - 这个Controller是控制台网页跟后台的接口,不要跟RemoteOp那个Controller混淆 - - - - - 通知一个节点的某个客户端离线 - - 节点地址。 - 客户端标识。 - - - - - 通知某个节点让所有的客户端刷新配置项 - - 节点地址。 - - - - - 通知某个节点个某个客户端刷新配置项 - - 节点地址。 - 客户端标识。 - - - - - 这个Controller用来上报节点的一些情况,比如链接了多少客户端等信息 - - - - - 获取本节点的客户端信息 - - - - - - 获取某个节点的客户端信息 - - 节点地址。 - - - - - 获取App数量 - - - - - - 获取Config项目数量 - - - - - - 获取Config项目数量 - - - - - - 获取所有节点的状态信息 - - - - - - 对请求进行basic认证的filter - - - - - 对请求进行basic认证的filter - - - - - 因为 attribute 不能传递 func 参数,所有从 action 的参数内获取 appId 的操作只能提前内置在一个静态字典内。 - - - - - 权限判断,用户信息从basic认证的头部取 - - - - - 获取用户名,优先从Claim获取,没有从Basic认证的头部取 - - 控制器实例。 - - - - - 获取用户Id,优先从Claim获取,没有从Basic认证的头部取用户名后从数据库查Id - - 控制器实例。 - 用户服务。 - - - - + + AgileConfig.Server.Apisite + + + + + 是否演示模式 + + + + + 是否控制台模式 + + + + + is password inited ? + + + + + + 初始化密码 + + + + + + 应用操作接口 + + + + + 获取所有应用 + + + + + + 根据id获取应用 + + 应用id + + + + + 添加应用 + + 应用模型 + + + + + 编辑应用 + + 应用id + 编辑后的应用模型 + + + + + 删除应用 + + 应用id + + + + + 发布某个应用的待发布配置项 + + 应用id + 环境 + + + + + 查询某个应用的发布历史 + + 应用id + 环境 + + + + + 回滚某个应用的发布版本,回滚到 historyId 指定的时刻 + + 发布历史 + 环境 + + + + + 根据appid查所有发布的配置项 , 包括继承过来的配置项. + 注意: 这个接口用的不是用户名密码的认证,用的是appid + secret的认证 + + 应用id + 环境 + + + + + 根据应用id查找配置,这些配置有可能是未发布的配置 。请跟 config/app/{appId} 接口加以区分。 + + 应用id + 环境 + + + + + 根据编号获取配置项的详情 + + 配置id + 环境 + + + + + 添加一个配置项 + + 配置模型 + 环境 + + + + + 编辑一个配置 + + 编号 + 模型 + 环境 + + + + + 删除一个配置 + + 配置id + 环境 + + + + + restful api 返回的 app 模型 + + + + + 是否可继承 + + + + + id + + + + + name + + + + + 密钥 + + + + + 是否启用 + + + + + 关联的app + + + + + 管理员 + + + + + id + + + + + 应用 + + + + + 组 + + + + + 键 + + + + + 值 + + + + + 状态 + + + + + 在线状态 + + + + + 编辑状态 + + + + + 描述 + + + + + 节点地址 + + + + + 备注 + + + + + 状态 + + + + + 最后响应时间 + + + + + 编号 + + + + + 应用id + + + + + 发布时间 + + + + + 发布者 + + + + + 发布版本序号 + + + + + 发布日志 + + + + + 环境 + + + + + 节点操作接口 + + + + + 获取所有节点 + + + + + + 添加节点 + + 节点模型 + + + + + 删除节点 + + 节点地址 + + + + + 在启动跟禁用之间进行切换 + + 应用标识。 + + + + + 获取所有可以继承的app + + + + + + 保存app的授权信息 + + 授权视图模型。 + + + + + 是否只是修改了描述信息 + + 新的配置。 + 旧的配置。 + + + + + 按多条件进行搜索 + + 应用id + 分组 + 键 + 在线状态 + 分页大小 + 当前页 + + + + + 发布所有待发布的配置项 + + + + + + 预览上传的json文件 + + + + + + 导出json文件 + + 应用id + + + + + 获取待发布的明细 + + 应用id + + + + + 获取发布详情的历史 + + 应用标识。 + + + + + 获取json格式的配置 + + 应用id + + + + + 这个Controller用来接受控制台节点发送过来的命令 + + + + + 这个Controller是控制台网页跟后台的接口,不要跟RemoteOp那个Controller混淆 + + + + + 通知一个节点的某个客户端离线 + + 节点地址。 + 客户端标识。 + + + + + 通知某个节点让所有的客户端刷新配置项 + + 节点地址。 + + + + + 通知某个节点个某个客户端刷新配置项 + + 节点地址。 + 客户端标识。 + + + + + 这个Controller用来上报节点的一些情况,比如链接了多少客户端等信息 + + + + + 获取本节点的客户端信息 + + + + + + 获取某个节点的客户端信息 + + 节点地址。 + + + + + 获取App数量 + + + + + + 获取Config项目数量 + + + + + + 获取Config项目数量 + + + + + + 获取所有节点的状态信息 + + + + + + 对请求进行basic认证的filter + + + + + 对请求进行basic认证的filter + + + + + 因为 attribute 不能传递 func 参数,所有从 action 的参数内获取 appId 的操作只能提前内置在一个静态字典内。 + + + + + 权限判断,用户信息从basic认证的头部取 + + + + + 获取用户名,优先从Claim获取,没有从Basic认证的头部取 + + 控制器实例。 + + + + + 获取用户Id,优先从Claim获取,没有从Basic认证的头部取用户名后从数据库查Id + + 控制器实例。 + 用户服务。 + + + + \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Appsettings.cs b/src/AgileConfig.Server.Apisite/Appsettings.cs index c1e30b3b..5bda6a77 100644 --- a/src/AgileConfig.Server.Apisite/Appsettings.cs +++ b/src/AgileConfig.Server.Apisite/Appsettings.cs @@ -1,58 +1,60 @@ -using AgileConfig.Server.Common; -using System; +using System; +using AgileConfig.Server.Common; -namespace AgileConfig.Server.Apisite +namespace AgileConfig.Server.Apisite; + +public class Appsettings { - public class Appsettings - { - /// - /// Indicates whether the server runs in demo mode. - /// - public static bool IsPreviewMode => "true".Equals(Global.Config["preview_mode"], StringComparison.CurrentCultureIgnoreCase); - /// - /// Indicates whether the admin console mode is enabled. - /// - public static bool IsAdminConsoleMode => "true".Equals(Global.Config["adminConsole"], StringComparison.CurrentCultureIgnoreCase); - - /// - /// Indicates whether the cluster auto-join feature is enabled. - /// - public static bool Cluster => "true".Equals(Global.Config["cluster"], StringComparison.CurrentCultureIgnoreCase); - - /// - /// path base - /// - public static string PathBase => Global.Config["pathBase"]; - - /// - /// Indicates whether single sign-on is enabled. - /// - public static bool SsoEnabled => "true".Equals(Global.Config["SSO:enabled"], StringComparison.CurrentCultureIgnoreCase); - - /// - /// SSO button text - /// - public static string SsoButtonText => Global.Config["SSO:loginButtonText"]; - - public static string OtlpLogsEndpoint => Global.Config["otlp:logs:endpoint"]; - - public static string OtlpLogsHeaders => Global.Config["otlp:logs:headers"]; - - public static string OtlpLogsProtocol => Global.Config["otlp:logs:protocol"]; - - public static string OtlpTracesEndpoint => Global.Config["otlp:traces:endpoint"]; - - public static string OtlpTracesProtocol => Global.Config["otlp:traces:protocol"]; - - public static string OtlpTracesHeaders => Global.Config["otlp:traces:headers"]; - - public static string OtlpMetricsEndpoint => Global.Config["otlp:metrics:endpoint"]; - - public static string OtlpMetricsProtocol => Global.Config["otlp:metrics:protocol"]; - - public static string OtlpMetricsHeaders => Global.Config["otlp:metrics:headers"]; - - public static string OtlpInstanceId => Global.Config["otlp:instanceId"]; - - } -} + /// + /// Indicates whether the server runs in demo mode. + /// + public static bool IsPreviewMode => + "true".Equals(Global.Config["preview_mode"], StringComparison.CurrentCultureIgnoreCase); + + /// + /// Indicates whether the admin console mode is enabled. + /// + public static bool IsAdminConsoleMode => + "true".Equals(Global.Config["adminConsole"], StringComparison.CurrentCultureIgnoreCase); + + /// + /// Indicates whether the cluster auto-join feature is enabled. + /// + public static bool Cluster => "true".Equals(Global.Config["cluster"], StringComparison.CurrentCultureIgnoreCase); + + /// + /// path base + /// + public static string PathBase => Global.Config["pathBase"]; + + /// + /// Indicates whether single sign-on is enabled. + /// + public static bool SsoEnabled => + "true".Equals(Global.Config["SSO:enabled"], StringComparison.CurrentCultureIgnoreCase); + + /// + /// SSO button text + /// + public static string SsoButtonText => Global.Config["SSO:loginButtonText"]; + + public static string OtlpLogsEndpoint => Global.Config["otlp:logs:endpoint"]; + + public static string OtlpLogsHeaders => Global.Config["otlp:logs:headers"]; + + public static string OtlpLogsProtocol => Global.Config["otlp:logs:protocol"]; + + public static string OtlpTracesEndpoint => Global.Config["otlp:traces:endpoint"]; + + public static string OtlpTracesProtocol => Global.Config["otlp:traces:protocol"]; + + public static string OtlpTracesHeaders => Global.Config["otlp:traces:headers"]; + + public static string OtlpMetricsEndpoint => Global.Config["otlp:metrics:endpoint"]; + + public static string OtlpMetricsProtocol => Global.Config["otlp:metrics:protocol"]; + + public static string OtlpMetricsHeaders => Global.Config["otlp:metrics:headers"]; + + public static string OtlpInstanceId => Global.Config["otlp:instanceId"]; +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/ConfigureJwtBearerOptions.cs b/src/AgileConfig.Server.Apisite/ConfigureJwtBearerOptions.cs index 4676b6ec..5879d0a4 100644 --- a/src/AgileConfig.Server.Apisite/ConfigureJwtBearerOptions.cs +++ b/src/AgileConfig.Server.Apisite/ConfigureJwtBearerOptions.cs @@ -15,7 +15,7 @@ public void Configure(JwtBearerOptions options) { ValidIssuer = jwtService.Issuer, ValidAudience = jwtService.Audience, - IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtService.GetSecurityKey())), + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtService.GetSecurityKey())) }; } diff --git a/src/AgileConfig.Server.Apisite/Controllers/AdminController.cs b/src/AgileConfig.Server.Apisite/Controllers/AdminController.cs index 7167c1d9..20c0cf70 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/AdminController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/AdminController.cs @@ -1,353 +1,317 @@ -using System; +using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using AgileConfig.Server.Apisite.Models; +using AgileConfig.Server.Apisite.Utilites; using AgileConfig.Server.Common; +using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Common.Resources; using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Event; using AgileConfig.Server.IService; +using AgileConfig.Server.OIDC; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using System.Linq; -using AgileConfig.Server.Apisite.Utilites; -using AgileConfig.Server.OIDC; -using System.Collections.Generic; -using AgileConfig.Server.Event; -using AgileConfig.Server.Common.EventBus; -namespace AgileConfig.Server.Apisite.Controllers +namespace AgileConfig.Server.Apisite.Controllers; + +public class AdminController : Controller { - public class AdminController : Controller + private readonly IJwtService _jwtService; + private readonly IOidcClient _oidcClient; + private readonly IPermissionService _permissionService; + private readonly ISettingService _settingService; + private readonly ISystemInitializationService _systemInitializationService; + private readonly ITinyEventBus _tinyEventBus; + private readonly IUserService _userService; + + public AdminController( + ISettingService settingService, + IUserService userService, + IPermissionService permissionService, + IJwtService jwtService, + IOidcClient oidcClient, + ITinyEventBus tinyEventBus, + ISystemInitializationService systemInitializationService + ) { - private readonly ISettingService _settingService; - private readonly IUserService _userService; - private readonly IPermissionService _permissionService; - private readonly IJwtService _jwtService; - private readonly IOidcClient _oidcClient; - private readonly ITinyEventBus _tinyEventBus; - private readonly ISystemInitializationService _systemInitializationService; - - public AdminController( - ISettingService settingService, - IUserService userService, - IPermissionService permissionService, - IJwtService jwtService, - IOidcClient oidcClient, - ITinyEventBus tinyEventBus, - ISystemInitializationService systemInitializationService - ) - { - _settingService = settingService; - _userService = userService; - _permissionService = permissionService; - _jwtService = jwtService; - _oidcClient = oidcClient; - _tinyEventBus = tinyEventBus; - _systemInitializationService = systemInitializationService; - } - + _settingService = settingService; + _userService = userService; + _permissionService = permissionService; + _jwtService = jwtService; + _oidcClient = oidcClient; + _tinyEventBus = tinyEventBus; + _systemInitializationService = systemInitializationService; + } - [HttpPost("admin/jwt/login")] - public async Task Login4AntdPro([FromBody] LoginVM model) - { - string userName = model.userName; - string password = model.password; - if (string.IsNullOrEmpty(password)) - { - return Json(new - { - status = "error", - message = Messages.PasswordCannotBeEmpty - }); - } - - var result = await _userService.ValidateUserPassword(userName, model.password); - if (result) - { - var response = await LoginSuccessful(userName); - return Json(response); - } + [HttpPost("admin/jwt/login")] + public async Task Login4AntdPro([FromBody] LoginVM model) + { + var userName = model.userName; + var password = model.password; + if (string.IsNullOrEmpty(password)) return Json(new { status = "error", - message = Messages.PasswordError + message = Messages.PasswordCannotBeEmpty }); + + var result = await _userService.ValidateUserPassword(userName, model.password); + if (result) + { + var response = await LoginSuccessful(userName); + return Json(response); } - private async Task LoginSuccessful(string userName) + return Json(new { - var user = (await _userService.GetUsersByNameAsync(userName)).First(); - var userRoles = await _userService.GetUserRolesAsync(user.Id); - var jwt = _jwtService.GetToken(user.Id, user.UserName, - userRoles.Any(r => r.Id == SystemRoleConstants.AdminId || r.Id == SystemRoleConstants.SuperAdminId)); - var userFunctions = await _permissionService.GetUserPermission(user.Id); + status = "error", + message = Messages.PasswordError + }); + } - _tinyEventBus.Fire(new LoginEvent(user.UserName)); + private async Task LoginSuccessful(string userName) + { + var user = (await _userService.GetUsersByNameAsync(userName)).First(); + var userRoles = await _userService.GetUserRolesAsync(user.Id); + var jwt = _jwtService.GetToken(user.Id, user.UserName, + userRoles.Any(r => r.Id == SystemRoleConstants.AdminId || r.Id == SystemRoleConstants.SuperAdminId)); + var userFunctions = await _permissionService.GetUserPermission(user.Id); - return new - { - status = "ok", - token = jwt, - type = "Bearer", - currentAuthority = userRoles.Select(r => r.Code), - currentFunctions = userFunctions - }; - } + _tinyEventBus.Fire(new LoginEvent(user.UserName)); - [HttpGet("admin/oidc/login")] - public async Task OidcLoginByCode(string code) + return new { - if (!Appsettings.SsoEnabled) - { - return BadRequest("SSO not enabled"); - } + status = "ok", + token = jwt, + type = "Bearer", + currentAuthority = userRoles.Select(r => r.Name), + currentFunctions = userFunctions + }; + } + + [HttpGet("admin/oidc/login")] + public async Task OidcLoginByCode(string code) + { + if (!Appsettings.SsoEnabled) return BadRequest("SSO not enabled"); - var tokens = await _oidcClient.Validate(code); + var tokens = await _oidcClient.Validate(code); - if (string.IsNullOrEmpty(tokens.IdToken)) + if (string.IsNullOrEmpty(tokens.IdToken)) + return Json(new { - return Json(new - { - message = "Code validate failed", - success = false - }); - } + message = "Code validate failed", + success = false + }); - var userInfo = _oidcClient.UnboxIdToken(tokens.IdToken); + var userInfo = _oidcClient.UnboxIdToken(tokens.IdToken); - if (string.IsNullOrEmpty(userInfo.Id) || string.IsNullOrEmpty(userInfo.UserName)) + if (string.IsNullOrEmpty(userInfo.Id) || string.IsNullOrEmpty(userInfo.UserName)) + return Json(new { - return Json(new - { - message = "IdToken invalid", - success = false - }); - } + message = "IdToken invalid", + success = false + }); - var user = (await _userService.GetUsersByNameAsync((string)userInfo.UserName)).FirstOrDefault(); + var user = (await _userService.GetUsersByNameAsync(userInfo.UserName)).FirstOrDefault(); - if (user == null) - { - //user first login to system, should be inserted into the DB - var newUser = new User() - { - Id = userInfo.Id, - UserName = userInfo.UserName, - Password = "/", - CreateTime = DateTime.Now, - Status = UserStatus.Normal, - Salt = "", - Source = UserSource.SSO - }; - await _userService.AddAsync(newUser); - await _userService.UpdateUserRolesAsync(newUser.Id, new List { SystemRoleConstants.OperatorId }); - } - else if (user.Status == UserStatus.Deleted) + if (user == null) + { + //user first login to system, should be inserted into the DB + var newUser = new User { - return Json(new - { - status = "error", - message = Messages.UserDeleted - }); - } - - var response = await LoginSuccessful(userInfo.UserName); - return Json(response); + Id = userInfo.Id, + UserName = userInfo.UserName, + Password = "/", + CreateTime = DateTime.Now, + Status = UserStatus.Normal, + Salt = "", + Source = UserSource.SSO + }; + await _userService.AddAsync(newUser); + await _userService.UpdateUserRolesAsync(newUser.Id, new List { SystemRoleConstants.OperatorId }); } - - /// - /// is password inited ? - /// - /// - [HttpGet] - public IActionResult PasswordInited() + else if (user.Status == UserStatus.Deleted) { - var has = _systemInitializationService.HasSa(); return Json(new { - success = true, - data = has + status = "error", + message = Messages.UserDeleted }); } - /// - /// Initialize the administrator password. - /// - /// - [HttpPost] - public IActionResult InitPassword([FromBody] InitPasswordVM model) + var response = await LoginSuccessful(userInfo.UserName); + return Json(response); + } + + /// + /// is password inited ? + /// + /// + [HttpGet] + public IActionResult PasswordInited() + { + var has = _systemInitializationService.HasSa(); + return Json(new { - var password = model.password; - var confirmPassword = model.confirmPassword; + success = true, + data = has + }); + } + + /// + /// Initialize the administrator password. + /// + /// + [HttpPost] + public IActionResult InitPassword([FromBody] InitPasswordVM model) + { + var password = model.password; + var confirmPassword = model.confirmPassword; - if (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(confirmPassword)) + if (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(confirmPassword)) + return Json(new { - return Json(new - { - message = Messages.PasswordCannotBeEmpty, - success = false - }); - } - - if (password.Length > 50 || confirmPassword.Length > 50) + message = Messages.PasswordCannotBeEmpty, + success = false + }); + + if (password.Length > 50 || confirmPassword.Length > 50) + return Json(new { - return Json(new - { - message = Messages.PasswordMaxLength50, - success = false - }); - } - - if (password != confirmPassword) + message = Messages.PasswordMaxLength50, + success = false + }); + + if (password != confirmPassword) + return Json(new { - return Json(new - { - message = Messages.PasswordMismatch, - success = false - }); - } - - if ( _systemInitializationService.HasSa()) + message = Messages.PasswordMismatch, + success = false + }); + + if (_systemInitializationService.HasSa()) + return Json(new { - return Json(new - { - message = Messages.PasswordAlreadySet, - success = false - }); - } + message = Messages.PasswordAlreadySet, + success = false + }); - var result = _systemInitializationService.TryInitSaPassword(password); + var result = _systemInitializationService.TryInitSaPassword(password); - if (result) - { - _tinyEventBus.Fire(new InitSaPasswordSuccessful()); - - return Json(new - { - success = true - }); - } - else + if (result) + { + _tinyEventBus.Fire(new InitSaPasswordSuccessful()); + + return Json(new { - return Json(new - { - message = Messages.InitPasswordFailed, - success = false - }); - } + success = true + }); } - [HttpPost] - [Authorize] - public async Task ChangePassword([FromBody] ChangePasswordVM model) + return Json(new { - if (Appsettings.IsPreviewMode) - { - return Json(new - { - success = false, - message = Messages.DemoModeNoPasswordChange - }); - } - - var password = model.password; - var confirmPassword = model.confirmPassword; - var oldPassword = model.oldPassword; - - if (string.IsNullOrEmpty(oldPassword) || string.IsNullOrEmpty(oldPassword)) + message = Messages.InitPasswordFailed, + success = false + }); + } + + [HttpPost] + [Authorize] + public async Task ChangePassword([FromBody] ChangePasswordVM model) + { + if (Appsettings.IsPreviewMode) + return Json(new { - return Json(new - { - message = Messages.OriginalPasswordCannotBeEmpty, - err_code = "err_resetpassword_01", - success = false - }); - } - - var userName = this.GetCurrentUserName(); - var validOld = await _userService.ValidateUserPassword(userName, oldPassword); - - if (!validOld) + success = false, + message = Messages.DemoModeNoPasswordChange + }); + + var password = model.password; + var confirmPassword = model.confirmPassword; + var oldPassword = model.oldPassword; + + if (string.IsNullOrEmpty(oldPassword) || string.IsNullOrEmpty(oldPassword)) + return Json(new { - return Json(new - { - message = Messages.OriginalPasswordError, - err_code = "err_resetpassword_02", - success = false - }); - } - - if (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(confirmPassword)) + message = Messages.OriginalPasswordCannotBeEmpty, + err_code = "err_resetpassword_01", + success = false + }); + + var userName = this.GetCurrentUserName(); + var validOld = await _userService.ValidateUserPassword(userName, oldPassword); + + if (!validOld) + return Json(new { - return Json(new - { - message = Messages.NewPasswordCannotBeEmpty, - err_code = "err_resetpassword_03", - success = false - }); - } - - if (password.Length > 50 || confirmPassword.Length > 50) + message = Messages.OriginalPasswordError, + err_code = "err_resetpassword_02", + success = false + }); + + if (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(confirmPassword)) + return Json(new { - return Json(new - { - message = Messages.NewPasswordMaxLength50, - err_code = "err_resetpassword_04", - success = false - }); - } - - if (password != confirmPassword) + message = Messages.NewPasswordCannotBeEmpty, + err_code = "err_resetpassword_03", + success = false + }); + + if (password.Length > 50 || confirmPassword.Length > 50) + return Json(new { - return Json(new - { - message = Messages.NewPasswordMismatch, - err_code = "err_resetpassword_05", - success = false - }); - } - - var users = await _userService.GetUsersByNameAsync(this.GetCurrentUserName()); - var user = users.Where(x => x.Status == UserStatus.Normal).FirstOrDefault(); - - if (user == null) + message = Messages.NewPasswordMaxLength50, + err_code = "err_resetpassword_04", + success = false + }); + + if (password != confirmPassword) + return Json(new { - return Json(new - { - message = Messages.UserNotFound, - err_code = "err_resetpassword_06", - success = false - }); - } - - user.Password = Encrypt.Md5(password + user.Salt); - var result = await _userService.UpdateAsync(user); - - if (result) + message = Messages.NewPasswordMismatch, + err_code = "err_resetpassword_05", + success = false + }); + + var users = await _userService.GetUsersByNameAsync(this.GetCurrentUserName()); + var user = users.Where(x => x.Status == UserStatus.Normal).FirstOrDefault(); + + if (user == null) + return Json(new { - _tinyEventBus.Fire(new ChangeUserPasswordSuccessful(user.UserName)); - - return Json(new - { - success = true - }); - } - else + message = Messages.UserNotFound, + err_code = "err_resetpassword_06", + success = false + }); + + user.Password = Encrypt.Md5(password + user.Salt); + var result = await _userService.UpdateAsync(user); + + if (result) + { + _tinyEventBus.Fire(new ChangeUserPasswordSuccessful(user.UserName)); + + return Json(new { - return Json(new - { - message = Messages.ChangePasswordFailed, - success = false - }); - } + success = true + }); } - public async Task Logoff() + return Json(new { - await HttpContext.SignOutAsync(); + message = Messages.ChangePasswordFailed, + success = false + }); + } - return Redirect("Login"); - } + public async Task Logoff() + { + await HttpContext.SignOutAsync(); + + return Redirect("Login"); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/AppController.cs b/src/AgileConfig.Server.Apisite/Controllers/AppController.cs index 2350a425..58d4fae6 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/AppController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/AppController.cs @@ -1,384 +1,348 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using AgileConfig.Server.Apisite.Filters; using AgileConfig.Server.Apisite.Models; +using AgileConfig.Server.Apisite.Models.Mapping; +using AgileConfig.Server.Apisite.Utilites; +using AgileConfig.Server.Common.EventBus; +using AgileConfig.Server.Common.Resources; using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Event; using AgileConfig.Server.IService; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using System.Linq; -using System.Collections.Generic; -using AgileConfig.Server.Apisite.Utilites; -using AgileConfig.Server.Common.EventBus; -using AgileConfig.Server.Event; -using AgileConfig.Server.Apisite.Models.Mapping; -using AgileConfig.Server.Common.Resources; -namespace AgileConfig.Server.Apisite.Controllers +namespace AgileConfig.Server.Apisite.Controllers; + +[Authorize] +[ModelVaildate] +public class AppController : Controller { - [Authorize] - [ModelVaildate] - public class AppController : Controller + private readonly IAppService _appService; + private readonly IPermissionService _permissionService; + private readonly ITinyEventBus _tinyEventBus; + private readonly IUserService _userService; + + public AppController(IAppService appService, + IPermissionService permissionService, + IUserService userService, + ITinyEventBus tinyEventBus) + { + _userService = userService; + _tinyEventBus = tinyEventBus; + _appService = appService; + _permissionService = permissionService; + } + + public async Task Search(string name, string id, string group, string sortField, + string ascOrDesc, bool tableGrouped, int current = 1, int pageSize = 20) { - private readonly IAppService _appService; - private readonly IPermissionService _permissionService; - private readonly IUserService _userService; - private readonly ITinyEventBus _tinyEventBus; - - public AppController(IAppService appService, - IPermissionService permissionService, - IUserService userService, - ITinyEventBus tinyEventBus) + if (current < 1) throw new ArgumentException(Messages.CurrentCannotBeLessThanOne); + + if (pageSize < 1) throw new ArgumentException(Messages.PageSizeCannotBeLessThanOne); + + var appListVms = new List(); + long count = 0; + if (!tableGrouped) { - _userService = userService; - _tinyEventBus = tinyEventBus; - _appService = appService; - _permissionService = permissionService; - } + var searchResult = + await _appService.SearchAsync(id, name, group, sortField, ascOrDesc, current, pageSize); + foreach (var app in searchResult.Apps) appListVms.Add(app.ToAppListVM()); - public async Task Search(string name, string id, string group, string sortField, - string ascOrDesc, bool tableGrouped, int current = 1, int pageSize = 20) + count = searchResult.Count; + } + else { - if (current < 1) + var searchResult = + await _appService.SearchGroupedAsync(id, name, group, sortField, ascOrDesc, current, pageSize); + foreach (var groupedApp in searchResult.GroupedApps) { - throw new ArgumentException(Messages.CurrentCannotBeLessThanOne); - } + var app = groupedApp.App; + var vm = app.ToAppListVM(); + vm.children = new List(); + foreach (var child in groupedApp.Children ?? []) vm.children.Add(child.App.ToAppListVM()); - if (pageSize < 1) - { - throw new ArgumentException(Messages.PageSizeCannotBeLessThanOne); + appListVms.Add(vm); } - var appListVms = new List(); - long count = 0; - if (!tableGrouped) - { - var searchResult = - await _appService.SearchAsync(id, name, group, sortField, ascOrDesc, current, pageSize); - foreach (var app in searchResult.Apps) - { - appListVms.Add(app.ToAppListVM()); - } - - count = searchResult.Count; - } - else - { - var searchResult = - await _appService.SearchGroupedAsync(id, name, group, sortField, ascOrDesc, current, pageSize); - foreach (var groupedApp in searchResult.GroupedApps) - { - var app = groupedApp.App; - var vm = app.ToAppListVM(); - vm.children = new List(); - foreach (var child in groupedApp.Children ?? []) - { - vm.children.Add(child.App.ToAppListVM()); - } - - appListVms.Add(vm); - } - - count = searchResult.Count; - } + count = searchResult.Count; + } - await AppendInheritancedInfo(appListVms); + await AppendInheritancedInfo(appListVms); - return Json(new - { - current, - pageSize, - success = true, - total = count, - data = appListVms - }); - } + return Json(new + { + current, + pageSize, + success = true, + total = count, + data = appListVms + }); + } - private async Task AppendInheritancedInfo(List list) + private async Task AppendInheritancedInfo(List list) + { + foreach (var appListVm in list) { - foreach (var appListVm in list) - { - var inheritancedApps = await _appService.GetInheritancedAppsAsync(appListVm.Id); - appListVm.inheritancedApps = appListVm.Inheritanced - ? new List() - : (inheritancedApps).Select(ia => ia.Id).ToList(); - appListVm.inheritancedAppNames = appListVm.Inheritanced - ? new List() - : (inheritancedApps).Select(ia => ia.Name).ToList(); - appListVm.AppAdminName = (await _userService.GetUserAsync(appListVm.AppAdmin))?.UserName; - if (appListVm.children != null) - { - await AppendInheritancedInfo(appListVm.children); - } - } + var inheritancedApps = await _appService.GetInheritancedAppsAsync(appListVm.Id); + appListVm.inheritancedApps = appListVm.Inheritanced + ? new List() + : inheritancedApps.Select(ia => ia.Id).ToList(); + appListVm.inheritancedAppNames = appListVm.Inheritanced + ? new List() + : inheritancedApps.Select(ia => ia.Name).ToList(); + appListVm.AppAdminName = (await _userService.GetUserAsync(appListVm.AppAdmin))?.UserName; + if (appListVm.children != null) await AppendInheritancedInfo(appListVm.children); } + } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "App.Add", Functions.App_Add })] - [HttpPost] - public async Task Add([FromBody] AppVM model) - { - ArgumentNullException.ThrowIfNull(model); + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "App.Add", Functions.App_Add })] + [HttpPost] + public async Task Add([FromBody] AppVM model) + { + ArgumentNullException.ThrowIfNull(model); - var oldApp = await _appService.GetAsync(model.Id); - if (oldApp != null) + var oldApp = await _appService.GetAsync(model.Id); + if (oldApp != null) + return Json(new { - return Json(new - { - success = false, - message = Messages.AppIdExists - }); - } + success = false, + message = Messages.AppIdExists + }); - var app = model.ToApp(); - app.CreateTime = DateTime.Now; + var app = model.ToApp(); + app.CreateTime = DateTime.Now; - var inheritanceApps = new List(); - if (!model.Inheritanced && model.inheritancedApps != null) + var inheritanceApps = new List(); + if (!model.Inheritanced && model.inheritancedApps != null) + { + var sort = 0; + model.inheritancedApps.ForEach(appId => { - var sort = 0; - model.inheritancedApps.ForEach(appId => + inheritanceApps.Add(new AppInheritanced { - inheritanceApps.Add(new AppInheritanced - { - Id = Guid.NewGuid().ToString("N"), - AppId = app.Id, - InheritancedAppId = appId, - Sort = sort++ - }); + Id = Guid.NewGuid().ToString("N"), + AppId = app.Id, + InheritancedAppId = appId, + Sort = sort++ }); - } - - var result = await _appService.AddAsync(app, inheritanceApps); - return Json(new - { - data = app, - success = result, - message = !result ? Messages.CreateAppFailed : "" }); } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "App.Edit", Functions.App_Edit })] - [HttpPost] - public async Task Edit([FromBody] AppVM model) + var result = await _appService.AddAsync(app, inheritanceApps); + return Json(new { - ArgumentNullException.ThrowIfNull(model); - - var app = await _appService.GetAsync(model.Id); - if (app == null) - { - return Json(new - { - success = false, - message = Messages.AppNotFound - }); - } + data = app, + success = result, + message = !result ? Messages.CreateAppFailed : "" + }); + } - if (Appsettings.IsPreviewMode && app.Name == "test_app") - { - return Json(new - { - success = false, - message = Messages.DemoModeNoTestAppEdit - }); - } + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "App.Edit", Functions.App_Edit })] + [HttpPost] + public async Task Edit([FromBody] AppVM model) + { + ArgumentNullException.ThrowIfNull(model); - app = model.ToApp(app); - app.UpdateTime = DateTime.Now; - var inheritanceApps = new List(); - if (!model.Inheritanced && model.inheritancedApps != null) + var app = await _appService.GetAsync(model.Id); + if (app == null) + return Json(new { - var sort = 0; - model.inheritancedApps.ForEach(appId => - { - inheritanceApps.Add(new AppInheritanced - { - Id = Guid.NewGuid().ToString("N"), - AppId = app.Id, - InheritancedAppId = appId, - Sort = sort++ - }); - }); - } + success = false, + message = Messages.AppNotFound + }); - var result = await _appService.UpdateAsync(app,inheritanceApps); + if (Appsettings.IsPreviewMode && app.Name == "test_app") return Json(new { - success = result, - message = !result ? Messages.UpdateAppFailed : "" + success = false, + message = Messages.DemoModeNoTestAppEdit }); - } - [HttpGet] - public async Task Get(string id) + app = model.ToApp(app); + app.UpdateTime = DateTime.Now; + var inheritanceApps = new List(); + if (!model.Inheritanced && model.inheritancedApps != null) { - ArgumentException.ThrowIfNullOrEmpty(id); - - var app = await _appService.GetAsync(id); - var vm = app.ToAppVM(); - - if (vm != null) + var sort = 0; + model.inheritancedApps.ForEach(appId => { - vm.inheritancedApps = (await _appService.GetInheritancedAppsAsync(id)).Select(x => x.Id).ToList(); - } - else - { - return NotFound(new + inheritanceApps.Add(new AppInheritanced { - success = false, - message = Messages.AppNotFound + Id = Guid.NewGuid().ToString("N"), + AppId = app.Id, + InheritancedAppId = appId, + Sort = sort++ }); - } - - return Json(new - { - success = true, - data = vm }); } - [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "App.DisableOrEnable", Functions.App_Edit })] - [HttpPost] - public async Task DisableOrEnable(string id) + var result = await _appService.UpdateAsync(app, inheritanceApps); + return Json(new { - ArgumentException.ThrowIfNullOrEmpty(id); + success = result, + message = !result ? Messages.UpdateAppFailed : "" + }); + } - var app = await _appService.GetAsync(id); - if (app == null) - { - return NotFound(new - { - success = false, - message = Messages.AppNotFound - }); - } + [HttpGet] + public async Task Get(string id) + { + ArgumentException.ThrowIfNullOrEmpty(id); - app.Enabled = !app.Enabled; - var result = await _appService.UpdateAsync(app); + var app = await _appService.GetAsync(id); + var vm = app.ToAppVM(); - return Json(new + if (vm != null) + vm.inheritancedApps = (await _appService.GetInheritancedAppsAsync(id)).Select(x => x.Id).ToList(); + else + return NotFound(new { - success = result, - message = !result ? Messages.UpdateAppFailed : "" + success = false, + message = Messages.AppNotFound }); - } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "App.Delete", Functions.App_Delete })] - [HttpPost] - public async Task Delete(string id) + return Json(new { - ArgumentException.ThrowIfNullOrEmpty(id); - - var app = await _appService.GetAsync(id); - if (app == null) - { - return NotFound(new - { - success = false, - message = Messages.AppNotFound - }); - } - - var result = await _appService.DeleteAsync(app); + success = true, + data = vm + }); + } - if (result) - { - _tinyEventBus.Fire(new DeleteAppSuccessful(app, this.GetCurrentUserName())); - } + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { "App.DisableOrEnable", Functions.App_Edit })] + [HttpPost] + public async Task DisableOrEnable(string id) + { + ArgumentException.ThrowIfNullOrEmpty(id); - return Json(new + var app = await _appService.GetAsync(id); + if (app == null) + return NotFound(new { - success = result, - message = !result ? Messages.UpdateAppFailed : "" + success = false, + message = Messages.AppNotFound }); - } - /// - /// Get all applications that can be inherited. - /// - /// - [HttpGet] - public async Task InheritancedApps(string currentAppId) + app.Enabled = !app.Enabled; + var result = await _appService.UpdateAsync(app); + + return Json(new { - var apps = await _appService.GetAllInheritancedAppsAsync(); - apps = apps.Where(a => a.Enabled).ToList(); - var self = apps.FirstOrDefault(a => a.Id == currentAppId); - if (self != null) - { - // Exclude the current application itself. - apps.Remove(self); - } + success = result, + message = !result ? Messages.UpdateAppFailed : "" + }); + } + + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "App.Delete", Functions.App_Delete })] + [HttpPost] + public async Task Delete(string id) + { + ArgumentException.ThrowIfNullOrEmpty(id); - var vms = apps.Select(x => + var app = await _appService.GetAsync(id); + if (app == null) + return NotFound(new { - return new - { - Id = x.Id, - Name = x.Name, - }; + success = false, + message = Messages.AppNotFound }); - return Json(new + var result = await _appService.DeleteAsync(app); + + if (result) _tinyEventBus.Fire(new DeleteAppSuccessful(app, this.GetCurrentUserName())); + + return Json(new + { + success = result, + message = !result ? Messages.UpdateAppFailed : "" + }); + } + + /// + /// Get all applications that can be inherited. + /// + /// + [HttpGet] + public async Task InheritancedApps(string currentAppId) + { + var apps = await _appService.GetAllInheritancedAppsAsync(); + apps = apps.Where(a => a.Enabled).ToList(); + var self = apps.FirstOrDefault(a => a.Id == currentAppId); + if (self != null) + // Exclude the current application itself. + apps.Remove(self); + + var vms = apps.Select(x => + { + return new { - success = true, - data = vms - }); - } + x.Id, x.Name + }; + }); - /// - /// Save application authorization information. - /// - /// View model containing authorization assignments. - /// Operation result. - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "App.Auth", Functions.App_Auth })] - [HttpPost] - public async Task SaveAppAuth([FromBody] AppAuthVM model) + return Json(new { - ArgumentNullException.ThrowIfNull(model); + success = true, + data = vms + }); + } - var result = await _appService.SaveUserAppAuth(model.AppId, model.EditConfigPermissionUsers, - _permissionService.EditConfigPermissionKey); - var result1 = await _appService.SaveUserAppAuth(model.AppId, model.PublishConfigPermissionUsers, - _permissionService.PublishConfigPermissionKey); + /// + /// Save application authorization information. + /// + /// View model containing authorization assignments. + /// Operation result. + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "App.Auth", Functions.App_Auth })] + [HttpPost] + public async Task SaveAppAuth([FromBody] AppAuthVM model) + { + ArgumentNullException.ThrowIfNull(model); - return Json(new - { - success = result && result1 - }); - } + var result = await _appService.SaveUserAppAuth(model.AppId, model.EditConfigPermissionUsers, + _permissionService.EditConfigPermissionKey); + var result1 = await _appService.SaveUserAppAuth(model.AppId, model.PublishConfigPermissionUsers, + _permissionService.PublishConfigPermissionKey); - [HttpGet] - public async Task GetUserAppAuth(string appId) + return Json(new { - ArgumentException.ThrowIfNullOrEmpty(appId); + success = result && result1 + }); + } - var result = new AppAuthVM - { - AppId = appId - }; - result.EditConfigPermissionUsers = - (await _appService.GetUserAppAuth(appId, _permissionService.EditConfigPermissionKey)).Select(x => x.Id) - .ToList(); - result.PublishConfigPermissionUsers = - (await _appService.GetUserAppAuth(appId, _permissionService.PublishConfigPermissionKey)) - .Select(x => x.Id).ToList(); + [HttpGet] + public async Task GetUserAppAuth(string appId) + { + ArgumentException.ThrowIfNullOrEmpty(appId); - return Json(new - { - success = true, - data = result - }); - } + var result = new AppAuthVM + { + AppId = appId + }; + result.EditConfigPermissionUsers = + (await _appService.GetUserAppAuth(appId, _permissionService.EditConfigPermissionKey)).Select(x => x.Id) + .ToList(); + result.PublishConfigPermissionUsers = + (await _appService.GetUserAppAuth(appId, _permissionService.PublishConfigPermissionKey)) + .Select(x => x.Id).ToList(); + + return Json(new + { + success = true, + data = result + }); + } - [HttpGet] - public async Task GetAppGroups() + [HttpGet] + public async Task GetAppGroups() + { + var groups = await _appService.GetAppGroups(); + return Json(new { - var groups = await _appService.GetAppGroups(); - return Json(new - { - success = true, - data = groups.OrderBy(x => x) - }); - } + success = true, + data = groups.OrderBy(x => x) + }); } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/ConfigController.cs b/src/AgileConfig.Server.Apisite/Controllers/ConfigController.cs index 17c38afd..6cf5d1f9 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/ConfigController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/ConfigController.cs @@ -1,78 +1,125 @@ using System; -using System.Threading.Tasks; +using System.Collections.Generic; using System.Linq; +using System.Text; +using System.Threading.Tasks; using AgileConfig.Server.Apisite.Filters; using AgileConfig.Server.Apisite.Models; +using AgileConfig.Server.Apisite.Utilites; +using AgileConfig.Server.Common; +using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Common.Resources; using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Event; using AgileConfig.Server.IService; -using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; -using System.Collections.Generic; -using Microsoft.AspNetCore.Http; -using AgileConfig.Server.Common; -using System.Text; -using AgileConfig.Server.Apisite.Utilites; -using AgileConfig.Server.Common.EventBus; -using AgileConfig.Server.Event; +using Microsoft.AspNetCore.Mvc; + +namespace AgileConfig.Server.Apisite.Controllers; -namespace AgileConfig.Server.Apisite.Controllers +[Authorize] +[ModelVaildate] +public class ConfigController : Controller { - [Authorize] - [ModelVaildate] - public class ConfigController : Controller + private readonly IAppService _appService; + private readonly IConfigService _configService; + private readonly ITinyEventBus _tinyEventBus; + private readonly IUserService _userService; + + public ConfigController( + IConfigService configService, + IAppService appService, + IUserService userService, + ITinyEventBus tinyEventBus + ) { - private readonly IConfigService _configService; - private readonly IAppService _appService; - private readonly IUserService _userService; - private readonly ITinyEventBus _tinyEventBus; - - public ConfigController( - IConfigService configService, - IAppService appService, - IUserService userService, - ITinyEventBus tinyEventBus - ) - { - _configService = configService; - _appService = appService; - _userService = userService; - _tinyEventBus = tinyEventBus; - } + _configService = configService; + _appService = appService; + _userService = userService; + _tinyEventBus = tinyEventBus; + } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Config.Add", Functions.Config_Add })] - [HttpPost] - public async Task Add([FromBody] ConfigVM model, EnvString env) - { - ArgumentNullException.ThrowIfNull(model); + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Config.Add", Functions.Config_Add })] + [HttpPost] + public async Task Add([FromBody] ConfigVM model, EnvString env) + { + ArgumentNullException.ThrowIfNull(model); - var app = await _appService.GetAsync(model.AppId); - if (app == null) + var app = await _appService.GetAsync(model.AppId); + if (app == null) + return Json(new { - return Json(new - { - success = false, - message = Messages.AppNotExists(model.AppId) - }); - } + success = false, + message = Messages.AppNotExists(model.AppId) + }); - var oldConfig = await _configService.GetByAppIdKeyEnv(model.AppId, model.Group, model.Key, env.Value); - if (oldConfig != null) + var oldConfig = await _configService.GetByAppIdKeyEnv(model.AppId, model.Group, model.Key, env.Value); + if (oldConfig != null) + return Json(new { + success = false, + message = Messages.ConfigExists + }); + + var config = new Config(); + config.Id = string.IsNullOrEmpty(model.Id) ? Guid.NewGuid().ToString("N") : model.Id; + config.Key = model.Key; + config.AppId = model.AppId; + config.Description = model.Description; + config.Value = model.Value; + config.Group = model.Group; + config.Status = ConfigStatus.Enabled; + config.CreateTime = DateTime.Now; + config.UpdateTime = null; + config.OnlineStatus = OnlineStatus.WaitPublish; + config.EditStatus = EditStatus.Add; + config.Env = env.Value; + + var result = await _configService.AddAsync(config, env.Value); + + if (result) _tinyEventBus.Fire(new AddConfigSuccessful(config, this.GetCurrentUserName())); + + return Json(new + { + success = result, + message = !result ? Messages.CreateConfigFailed : "", + data = config + }); + } + + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { "Config.AddRange", Functions.Config_Add })] + [HttpPost] + public async Task AddRange([FromBody] List model, EnvString env) + { + if (model == null || model.Count == 0) throw new ArgumentNullException("model"); + + var configs = await _configService.GetByAppIdAsync(model.First().AppId, env.Value); + + var oldDict = new Dictionary(); + configs.ForEach(item => { oldDict.Add(_configService.GenerateKey(item), item.Value); }); + + var addConfigs = new List(); + //judge if json key already in configs + foreach (var item in model) + { + var newkey = item.Key; + if (!string.IsNullOrEmpty(item.Group)) newkey = $"{item.Group}:{item.Key}"; + + if (oldDict.ContainsKey(newkey)) return Json(new { success = false, - message = Messages.ConfigExists + message = Messages.DuplicateConfig(item.Key) }); - } var config = new Config(); - config.Id = string.IsNullOrEmpty(model.Id) ? Guid.NewGuid().ToString("N") : model.Id; - config.Key = model.Key; - config.AppId = model.AppId; - config.Description = model.Description; - config.Value = model.Value; - config.Group = model.Group; + config.Id = Guid.NewGuid().ToString("N"); + config.Key = item.Key; + config.AppId = item.AppId; + config.Description = item.Description; + config.Value = item.Value; + config.Group = item.Group; config.Status = ConfigStatus.Enabled; config.CreateTime = DateTime.Now; config.UpdateTime = null; @@ -80,809 +127,623 @@ public async Task Add([FromBody] ConfigVM model, EnvString env) config.EditStatus = EditStatus.Add; config.Env = env.Value; - var result = await _configService.AddAsync(config, env.Value); + addConfigs.Add(config); + } - if (result) - { - _tinyEventBus.Fire(new AddConfigSuccessful(config, this.GetCurrentUserName())); - } + var result = await _configService.AddRangeAsync(addConfigs, env.Value); - return Json(new - { - success = result, - message = !result ? Messages.CreateConfigFailed : "", - data = config - }); + if (result) + { + var userName = this.GetCurrentUserName(); + addConfigs.ForEach(c => { _tinyEventBus.Fire(new AddConfigSuccessful(c, this.GetCurrentUserName())); }); } - [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "Config.AddRange", Functions.Config_Add })] - [HttpPost] - public async Task AddRange([FromBody] List model, EnvString env) + return Json(new { - if (model == null || model.Count == 0) - { - throw new ArgumentNullException("model"); - } - - var configs = await _configService.GetByAppIdAsync(model.First().AppId, env.Value); - - var oldDict = new Dictionary(); - configs.ForEach(item => { oldDict.Add(_configService.GenerateKey(item), item.Value); }); - - var addConfigs = new List(); - //judge if json key already in configs - foreach (var item in model) - { - var newkey = item.Key; - if (!string.IsNullOrEmpty(item.Group)) - { - newkey = $"{item.Group}:{item.Key}"; - } - - if (oldDict.ContainsKey(newkey)) - { - return Json(new - { - success = false, - message = Messages.DuplicateConfig(item.Key) - }); - } - - var config = new Config(); - config.Id = Guid.NewGuid().ToString("N"); - config.Key = item.Key; - config.AppId = item.AppId; - config.Description = item.Description; - config.Value = item.Value; - config.Group = item.Group; - config.Status = ConfigStatus.Enabled; - config.CreateTime = DateTime.Now; - config.UpdateTime = null; - config.OnlineStatus = OnlineStatus.WaitPublish; - config.EditStatus = EditStatus.Add; - config.Env = env.Value; - - addConfigs.Add(config); - } + success = result, + message = !result ? Messages.BatchCreateConfigFailed : "" + }); + } - var result = await _configService.AddRangeAsync(addConfigs, env.Value); + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { "Config.Edit", Functions.Config_Edit })] + [HttpPost] + public async Task Edit([FromBody] ConfigVM model, [FromQuery] EnvString env) + { + if (model == null) throw new ArgumentNullException("model"); - if (result) + var config = await _configService.GetAsync(model.Id, env.Value); + if (config == null) + return Json(new { - var userName = this.GetCurrentUserName(); - addConfigs.ForEach(c => - { - _tinyEventBus.Fire(new AddConfigSuccessful(c, this.GetCurrentUserName())); - }); - } + success = false, + message = Messages.ConfigNotFound + }); + var app = await _configService.GetByAppIdAsync(model.AppId, env.Value); + if (!app.Any()) return Json(new { - success = result, - message = !result ? Messages.BatchCreateConfigFailed : "" + success = false, + message = Messages.AppNotExists(model.AppId) }); - } - [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "Config.Edit", Functions.Config_Edit })] - [HttpPost] - public async Task Edit([FromBody] ConfigVM model, [FromQuery] EnvString env) + var oldConfig = new Config { - if (model == null) - { - throw new ArgumentNullException("model"); - } - - var config = await _configService.GetAsync(model.Id, env.Value); - if (config == null) - { + Key = config.Key, + Group = config.Group, + Value = config.Value + }; + if (config.Group != model.Group || config.Key != model.Key) + { + var anotherConfig = await _configService.GetByAppIdKeyEnv(model.AppId, model.Group, model.Key, env.Value); + if (anotherConfig != null) return Json(new { success = false, - message = Messages.ConfigNotFound + message = Messages.ConfigKeyExists }); - } + } - var app = await _configService.GetByAppIdAsync(model.AppId, env.Value); - if (!app.Any()) - { - return Json(new - { - success = false, - message = Messages.AppNotExists(model.AppId) - }); - } + config.AppId = model.AppId; + config.Description = model.Description; + config.Key = model.Key; + config.Value = model.Value; + config.Group = model.Group; + config.UpdateTime = DateTime.Now; + config.Env = env.Value; - var oldConfig = new Config - { - Key = config.Key, - Group = config.Group, - Value = config.Value - }; - if (config.Group != model.Group || config.Key != model.Key) - { - var anotherConfig = await _configService.GetByAppIdKeyEnv(model.AppId, model.Group, model.Key, env.Value); - if (anotherConfig != null) - { - return Json(new - { - success = false, - message = Messages.ConfigKeyExists - }); - } - } + if (!IsOnlyUpdateDescription(config, oldConfig)) + { + var isPublished = await _configService.IsPublishedAsync(config.Id, env.Value); + if (isPublished) + // When an already published configuration is modified, mark it as edited. + config.EditStatus = EditStatus.Edit; + else + // If it has never been published, keep the status as added. + config.EditStatus = EditStatus.Add; - config.AppId = model.AppId; - config.Description = model.Description; - config.Key = model.Key; - config.Value = model.Value; - config.Group = model.Group; - config.UpdateTime = DateTime.Now; - config.Env = env.Value; + config.OnlineStatus = OnlineStatus.WaitPublish; + } - if (!IsOnlyUpdateDescription(config, oldConfig)) - { - var isPublished = await _configService.IsPublishedAsync(config.Id, env.Value); - if (isPublished) - { - // When an already published configuration is modified, mark it as edited. - config.EditStatus = EditStatus.Edit; - } - else - { - // If it has never been published, keep the status as added. - config.EditStatus = EditStatus.Add; - } + var result = await _configService.UpdateAsync(config, env.Value); - config.OnlineStatus = OnlineStatus.WaitPublish; - } + if (result) _tinyEventBus.Fire(new EditConfigSuccessful(config, this.GetCurrentUserName())); - var result = await _configService.UpdateAsync(config, env.Value); + return Json(new + { + success = result, + message = !result ? "修改配置失败,请查看错误日志。" : "" + }); + } - if (result) - { - _tinyEventBus.Fire(new EditConfigSuccessful(config, this.GetCurrentUserName())); - } + /// + /// Determine whether only the description field changed. + /// + /// Configuration submitted by the client. + /// Existing configuration stored in the database. + /// True when only the description differs. + private bool IsOnlyUpdateDescription(Config newConfig, Config oldConfig) + { + return newConfig.Key == oldConfig.Key && newConfig.Group == oldConfig.Group && + newConfig.Value == oldConfig.Value; + } - return Json(new - { - success = result, - message = !result ? "修改配置失败,请查看错误日志。" : "" - }); - } + [HttpGet] + public async Task All(string env) + { + ISettingService.IfEnvEmptySetDefault(ref env); - /// - /// Determine whether only the description field changed. - /// - /// Configuration submitted by the client. - /// Existing configuration stored in the database. - /// True when only the description differs. - private bool IsOnlyUpdateDescription(Config newConfig, Config oldConfig) - { - return newConfig.Key == oldConfig.Key && newConfig.Group == oldConfig.Group && - newConfig.Value == oldConfig.Value; - } + var configs = await _configService.GetAllConfigsAsync(env); - [HttpGet] - public async Task All(string env) + return Json(new { - ISettingService.IfEnvEmptySetDefault(ref env); + success = true, + data = configs + }); + } - var configs = await _configService.GetAllConfigsAsync(env); + /// + /// Search configurations with multiple filter conditions. + /// + /// Application ID. + /// Configuration group. + /// Configuration key. + /// Filter by online status. + /// Number of items per page. + /// Current page number. + /// + [HttpGet] + public async Task Search(string appId, string group, string key, OnlineStatus? onlineStatus, + string sortField, string ascOrDesc, EnvString env, int pageSize = 20, int current = 1) + { + if (pageSize <= 0) throw new ArgumentException("pageSize can not less then 1 ."); - return Json(new - { - success = true, - data = configs - }); - } + if (current <= 0) throw new ArgumentException("pageIndex can not less then 1 ."); - /// - /// Search configurations with multiple filter conditions. - /// - /// Application ID. - /// Configuration group. - /// Configuration key. - /// Filter by online status. - /// Number of items per page. - /// Current page number. - /// - [HttpGet] - public async Task Search(string appId, string group, string key, OnlineStatus? onlineStatus, - string sortField, string ascOrDesc, EnvString env, int pageSize = 20, int current = 1) - { - if (pageSize <= 0) - { - throw new ArgumentException("pageSize can not less then 1 ."); - } + var configs = await _configService.Search(appId, group, key, env.Value); + configs = configs.Where(c => c.Status == ConfigStatus.Enabled).ToList(); + if (onlineStatus.HasValue) configs = configs.Where(c => c.OnlineStatus == onlineStatus).ToList(); - if (current <= 0) - { - throw new ArgumentException("pageIndex can not less then 1 ."); - } + if (sortField == "createTime") + { + if (ascOrDesc.StartsWith("asc")) + configs = configs.OrderBy(x => x.CreateTime).ToList(); + else + configs = configs.OrderByDescending(x => x.CreateTime).ToList(); + } - var configs = await _configService.Search(appId, group, key, env.Value); - configs = configs.Where(c => c.Status == ConfigStatus.Enabled).ToList(); - if (onlineStatus.HasValue) - { - configs = configs.Where(c => c.OnlineStatus == onlineStatus).ToList(); - } + if (sortField == "group") + { + if (ascOrDesc.StartsWith("asc")) + configs = configs.OrderBy(x => x.Group).ToList(); + else + configs = configs.OrderByDescending(x => x.Group).ToList(); + } - if (sortField == "createTime") - { - if (ascOrDesc.StartsWith("asc")) - { - configs = configs.OrderBy(x => x.CreateTime).ToList(); - } - else - { - configs = configs.OrderByDescending(x => x.CreateTime).ToList(); - } - } + var page = configs.Skip((current - 1) * pageSize).Take(pageSize).ToList(); + var total = configs.Count(); - if (sortField == "group") - { - if (ascOrDesc.StartsWith("asc")) - { - configs = configs.OrderBy(x => x.Group).ToList(); - } - else - { - configs = configs.OrderByDescending(x => x.Group).ToList(); - } - } + return Json(new + { + current, + pageSize, + success = true, + total, + data = page + }); + } - var page = configs.Skip((current - 1) * pageSize).Take(pageSize).ToList(); - var total = configs.Count(); + [HttpGet] + public async Task Get(string id, EnvString env) + { + if (string.IsNullOrEmpty(id)) throw new ArgumentNullException("id"); - return Json(new - { - current, - pageSize, - success = true, - total = total, - data = page - }); - } + var config = await _configService.GetAsync(id, env.Value); - [HttpGet] - public async Task Get(string id, EnvString env) + return Json(new { - if (string.IsNullOrEmpty(id)) - { - throw new ArgumentNullException("id"); - } + success = config != null, + data = config, + message = config == null ? "未找到对应的配置项。" : "" + }); + } - var config = await _configService.GetAsync(id, env.Value); + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { "Config.Delete", Functions.Config_Delete })] + [HttpPost] + public async Task Delete(string id, EnvString env) + { + if (string.IsNullOrEmpty(id)) throw new ArgumentNullException("id"); + var config = await _configService.GetAsync(id, env.Value); + if (config == null) return Json(new { - success = config != null, - data = config, - message = config == null ? "未找到对应的配置项。" : "" + success = false, + message = "未找到对应的配置项。" }); - } - [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "Config.Delete", Functions.Config_Delete })] - [HttpPost] - public async Task Delete(string id, EnvString env) + config.EditStatus = EditStatus.Deleted; + config.OnlineStatus = OnlineStatus.WaitPublish; + + var isPublished = await _configService.IsPublishedAsync(config.Id, env.Value); + if (!isPublished) + // If it has never been published, remove it directly. + config.Status = ConfigStatus.Deleted; + + var result = await _configService.UpdateAsync(config, env.Value); + if (result) _tinyEventBus.Fire(new DeleteConfigSuccessful(config, this.GetCurrentUserName())); + + return Json(new { - if (string.IsNullOrEmpty(id)) - { - throw new ArgumentNullException("id"); - } + success = result, + message = !result ? "删除配置失败,请查看错误日志" : "" + }); + } + + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { "Config.DeleteSome", Functions.Config_Delete })] + [HttpPost] + public async Task DeleteSome([FromBody] List ids, EnvString env) + { + if (ids == null) throw new ArgumentNullException("ids"); + + var deleteConfigs = new List(); + foreach (var id in ids) + { var config = await _configService.GetAsync(id, env.Value); if (config == null) - { return Json(new { success = false, message = "未找到对应的配置项。" }); - } config.EditStatus = EditStatus.Deleted; config.OnlineStatus = OnlineStatus.WaitPublish; var isPublished = await _configService.IsPublishedAsync(config.Id, env.Value); if (!isPublished) - { // If it has never been published, remove it directly. config.Status = ConfigStatus.Deleted; - } - var result = await _configService.UpdateAsync(config, env.Value); - if (result) - { - _tinyEventBus.Fire(new DeleteConfigSuccessful(config, this.GetCurrentUserName())); - } - - return Json(new - { - success = result, - message = !result ? "删除配置失败,请查看错误日志" : "" - }); + deleteConfigs.Add(config); } - [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "Config.DeleteSome", Functions.Config_Delete })] - [HttpPost] - public async Task DeleteSome([FromBody] List ids, EnvString env) - { - if (ids == null) - { - throw new ArgumentNullException("ids"); - } - - List deleteConfigs = new List(); + var result = await _configService.UpdateAsync(deleteConfigs, env.Value); + if (result) + _tinyEventBus.Fire(new DeleteSomeConfigSuccessful(deleteConfigs.First(), this.GetCurrentUserName())); - foreach (var id in ids) - { - var config = await _configService.GetAsync(id, env.Value); - if (config == null) - { - return Json(new - { - success = false, - message = "未找到对应的配置项。" - }); - } - - config.EditStatus = EditStatus.Deleted; - config.OnlineStatus = OnlineStatus.WaitPublish; + return Json(new + { + success = result, + message = !result ? "删除配置失败,请查看错误日志" : "" + }); + } - var isPublished = await _configService.IsPublishedAsync(config.Id, env.Value); - if (!isPublished) - { - // If it has never been published, remove it directly. - config.Status = ConfigStatus.Deleted; - } - deleteConfigs.Add(config); - } + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { "Config.Rollback", Functions.Config_Publish })] + [HttpPost] + public async Task Rollback(string publishTimelineId, EnvString env) + { + if (string.IsNullOrEmpty(publishTimelineId)) throw new ArgumentNullException("publishTimelineId"); - var result = await _configService.UpdateAsync(deleteConfigs, env.Value); - if (result) - { - _tinyEventBus.Fire(new DeleteSomeConfigSuccessful(deleteConfigs.First(), this.GetCurrentUserName())); - } + var result = await _configService.RollbackAsync(publishTimelineId, env.Value); - return Json(new - { - success = result, - message = !result ? "删除配置失败,请查看错误日志" : "" - }); + if (result) + { + var node = await _configService.GetPublishTimeLineNodeAsync(publishTimelineId, env.Value); + _tinyEventBus.Fire(new RollbackConfigSuccessful(node, this.GetCurrentUserName())); } - - [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "Config.Rollback", Functions.Config_Publish })] - [HttpPost] - public async Task Rollback(string publishTimelineId, EnvString env) + return Json(new { - if (string.IsNullOrEmpty(publishTimelineId)) - { - throw new ArgumentNullException("publishTimelineId"); - } + success = result, + message = !result ? "回滚失败,请查看错误日志。" : "" + }); + } - var result = await _configService.RollbackAsync(publishTimelineId, env.Value); + [HttpGet] + public async Task ConfigPublishedHistory(string configId, EnvString env) + { + if (string.IsNullOrEmpty(configId)) throw new ArgumentNullException("configId"); - if (result) - { - var node = await _configService.GetPublishTimeLineNodeAsync(publishTimelineId, env.Value); - _tinyEventBus.Fire(new RollbackConfigSuccessful(node, this.GetCurrentUserName())); - } + var configPublishedHistory = await _configService.GetConfigPublishedHistory(configId, env.Value); + var result = new List(); - return Json(new + foreach (var publishDetail in configPublishedHistory.OrderByDescending(x => x.Version)) + { + var timelineNode = + await _configService.GetPublishTimeLineNodeAsync(publishDetail.PublishTimelineId, env.Value); + result.Add(new { - success = result, - message = !result ? "回滚失败,请查看错误日志。" : "" + timelineNode, + config = publishDetail }); } - [HttpGet] - public async Task ConfigPublishedHistory(string configId, EnvString env) + return Json(new { - if (string.IsNullOrEmpty(configId)) - { - throw new ArgumentNullException("configId"); - } + success = true, + data = result + }); + ; + } - var configPublishedHistory = await _configService.GetConfigPublishedHistory(configId, env.Value); - var result = new List(); + /// + /// Publish all pending configuration items. + /// + /// + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { "Config.Publish", Functions.Config_Publish })] + [HttpPost] + public async Task Publish([FromBody] PublishLogVM model, EnvString env) + { + if (model == null) throw new ArgumentNullException("model"); - foreach (var publishDetail in configPublishedHistory.OrderByDescending(x => x.Version)) - { - var timelineNode = - await _configService.GetPublishTimeLineNodeAsync(publishDetail.PublishTimelineId, env.Value); - result.Add(new - { - timelineNode, - config = publishDetail - }); - } + if (string.IsNullOrEmpty(model.AppId)) throw new ArgumentNullException("appId"); - return Json(new - { - success = true, - data = result - }); - ; - } + var appId = model.AppId; + var userId = await this.GetCurrentUserId(_userService); + var ret = await _configService.Publish(appId, model.Ids, model.Log, userId, env.Value); - /// - /// Publish all pending configuration items. - /// - /// - [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "Config.Publish", Functions.Config_Publish })] - [HttpPost] - public async Task Publish([FromBody] PublishLogVM model, EnvString env) + if (ret.result) { - if (model == null) - { - throw new ArgumentNullException("model"); - } - - if (string.IsNullOrEmpty(model.AppId)) - { - throw new ArgumentNullException("appId"); - } - - var appId = model.AppId; - var userId = await this.GetCurrentUserId(_userService); - var ret = await _configService.Publish(appId, model.Ids, model.Log, userId, env.Value); + var timelineNode = await _configService.GetPublishTimeLineNodeAsync(ret.publishTimelineId, env.Value); + _tinyEventBus.Fire(new PublishConfigSuccessful(timelineNode, this.GetCurrentUserName())); + } - if (ret.result) - { - var timelineNode = await _configService.GetPublishTimeLineNodeAsync(ret.publishTimelineId, env.Value); - _tinyEventBus.Fire(new PublishConfigSuccessful(timelineNode, this.GetCurrentUserName())); - } + return Json(new + { + success = ret.result, + message = !ret.result ? "上线配置失败,请查看错误日志" : "" + }); + } + /// + /// Preview an uploaded JSON configuration file. + /// + /// + public IActionResult PreViewJsonFile() + { + var files = Request.Form.Files.ToList(); + if (!files.Any()) return Json(new { - success = ret.result, - message = !ret.result ? "上线配置失败,请查看错误日志" : "" + success = false, + message = "请上传Json文件" }); - } - /// - /// Preview an uploaded JSON configuration file. - /// - /// - public IActionResult PreViewJsonFile() + var jsonFile = files.First(); + using (var stream = jsonFile.OpenReadStream()) { - List files = Request.Form.Files.ToList(); - if (!files.Any()) - { - return Json(new - { - success = false, - message = "请上传Json文件" - }); - } + var dict = JsonConfigurationFileParser.Parse(stream); - var jsonFile = files.First(); - using (var stream = jsonFile.OpenReadStream()) + var addConfigs = new List(); + foreach (var key in dict.Keys) { - var dict = JsonConfigurationFileParser.Parse(stream); - - var addConfigs = new List(); - foreach (var key in dict.Keys) + var newKey = key; + var group = ""; + var paths = key.Split(":"); + if (paths.Length > 1) { - var newKey = key; - var group = ""; - var paths = key.Split(":"); - if (paths.Length > 1) - { - // For hierarchical keys, use the last segment as the key and the rest as the group. - newKey = paths[paths.Length - 1]; - group = string.Join(":", paths.ToList().Take(paths.Length - 1)); - } - - var config = new Config(); - config.Key = newKey; - config.Description = ""; - config.Value = dict[key]; - config.Group = group; - config.Id = Guid.NewGuid().ToString(); - addConfigs.Add(config); + // For hierarchical keys, use the last segment as the key and the rest as the group. + newKey = paths[paths.Length - 1]; + group = string.Join(":", paths.ToList().Take(paths.Length - 1)); } - return Json(new - { - success = true, - data = addConfigs - }); - } - } - - /// - /// Export an application's configurations as a JSON file. - /// - /// Application ID. - /// - public async Task ExportJson(string appId, EnvString env) - { - if (string.IsNullOrEmpty(appId)) - { - throw new ArgumentNullException("appId"); + var config = new Config(); + config.Key = newKey; + config.Description = ""; + config.Value = dict[key]; + config.Group = group; + config.Id = Guid.NewGuid().ToString(); + addConfigs.Add(config); } - var configs = await _configService.GetByAppIdAsync(appId, env.Value); - - var dict = new Dictionary(); - configs.ForEach(x => + return Json(new { - var key = _configService.GenerateKey(x); - dict.Add(key, x.Value); + success = true, + data = addConfigs }); + } + } - var json = DictionaryConvertToJson.ToJson(dict); + /// + /// Export an application's configurations as a JSON file. + /// + /// Application ID. + /// + public async Task ExportJson(string appId, EnvString env) + { + if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException("appId"); - return File(Encoding.UTF8.GetBytes(json), "application/json", $"{appId}.json"); - } + var configs = await _configService.GetByAppIdAsync(appId, env.Value); - /// - /// Get counts of configuration changes that are waiting to be published. - /// - /// Application ID. - /// - public async Task WaitPublishStatus(string appId, EnvString env) + var dict = new Dictionary(); + configs.ForEach(x => { - if (string.IsNullOrEmpty(appId)) - { - throw new ArgumentNullException("appId"); - } + var key = _configService.GenerateKey(x); + dict.Add(key, x.Value); + }); - var configs = await _configService.Search(appId, "", "", env.Value); - configs = configs.Where(x => x.Status == ConfigStatus.Enabled && x.EditStatus != EditStatus.Commit) - .ToList(); + var json = DictionaryConvertToJson.ToJson(dict); - var addCount = configs.Count(x => x.EditStatus == EditStatus.Add); - var editCount = configs.Count(x => x.EditStatus == EditStatus.Edit); - var deleteCount = configs.Count(x => x.EditStatus == EditStatus.Deleted); + return File(Encoding.UTF8.GetBytes(json), "application/json", $"{appId}.json"); + } - return Json(new - { - success = true, - data = new - { - addCount, - editCount, - deleteCount - } - }); - } + /// + /// Get counts of configuration changes that are waiting to be published. + /// + /// Application ID. + /// + public async Task WaitPublishStatus(string appId, EnvString env) + { + if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException("appId"); - /// - /// Retrieve the publish history details for an application. - /// - /// Application ID. - /// - public async Task PublishHistory(string appId, EnvString env) + var configs = await _configService.Search(appId, "", "", env.Value); + configs = configs.Where(x => x.Status == ConfigStatus.Enabled && x.EditStatus != EditStatus.Commit) + .ToList(); + + var addCount = configs.Count(x => x.EditStatus == EditStatus.Add); + var editCount = configs.Count(x => x.EditStatus == EditStatus.Edit); + var deleteCount = configs.Count(x => x.EditStatus == EditStatus.Deleted); + + return Json(new { - if (string.IsNullOrEmpty(appId)) + success = true, + data = new { - throw new ArgumentNullException("appId"); + addCount, + editCount, + deleteCount } + }); + } - var history = await _configService.GetPublishDetailListAsync(appId, env.Value); + /// + /// Retrieve the publish history details for an application. + /// + /// Application ID. + /// + public async Task PublishHistory(string appId, EnvString env) + { + if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException("appId"); - var result = new List(); - foreach (var publishDetails in history.GroupBy(x => x.Version).OrderByDescending(g => g.Key)) - { - var data = publishDetails.ToList(); - result.Add(new - { - key = publishDetails.Key, - timelineNode = - await _configService.GetPublishTimeLineNodeAsync(data.FirstOrDefault()?.PublishTimelineId, env.Value), - list = data - }); - } + var history = await _configService.GetPublishDetailListAsync(appId, env.Value); - return Json(new - { - success = true, - data = result + var result = new List(); + foreach (var publishDetails in history.GroupBy(x => x.Version).OrderByDescending(g => g.Key)) + { + var data = publishDetails.ToList(); + result.Add(new + { + key = publishDetails.Key, + timelineNode = + await _configService.GetPublishTimeLineNodeAsync(data.FirstOrDefault()?.PublishTimelineId, + env.Value), + list = data }); } - public async Task CancelEdit(string configId, EnvString env) + return Json(new { - if (string.IsNullOrEmpty(configId)) - { - throw new ArgumentNullException("configId"); - } + success = true, + data = result + }); + } - var result = await _configService.CancelEdit(new List() { configId }, env.Value); + public async Task CancelEdit(string configId, EnvString env) + { + if (string.IsNullOrEmpty(configId)) throw new ArgumentNullException("configId"); - if (result) - { - var config = await _configService.GetAsync(configId, env.Value); - _tinyEventBus.Fire(new CancelEditConfigSuccessful(config, this.GetCurrentUserName())); - } + var result = await _configService.CancelEdit(new List { configId }, env.Value); - return Json(new - { - success = true - }); + if (result) + { + var config = await _configService.GetAsync(configId, env.Value); + _tinyEventBus.Fire(new CancelEditConfigSuccessful(config, this.GetCurrentUserName())); } - public async Task CancelSomeEdit([FromBody] List ids, EnvString env) + return Json(new { - if (ids == null) - { - throw new ArgumentNullException("ids"); - } + success = true + }); + } - var result = await _configService.CancelEdit(ids, env.Value); + public async Task CancelSomeEdit([FromBody] List ids, EnvString env) + { + if (ids == null) throw new ArgumentNullException("ids"); - if (result) - { - var config = await _configService.GetAsync(ids.First(), env.Value); - _tinyEventBus.Fire(new CancelEditConfigSomeSuccessful(config, this.GetCurrentUserName())); - } + var result = await _configService.CancelEdit(ids, env.Value); - return Json(new - { - success = true - }); + if (result) + { + var config = await _configService.GetAsync(ids.First(), env.Value); + _tinyEventBus.Fire(new CancelEditConfigSomeSuccessful(config, this.GetCurrentUserName())); } - [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "Config.EvnSync", Functions.Config_Add })] - [HttpPost] - public async Task SyncEnv([FromBody] List toEnvs, [FromQuery] string appId, - [FromQuery] string currentEnv) + return Json(new { - if (toEnvs == null) - { - throw new ArgumentNullException("toEnvs"); - } + success = true + }); + } - if (string.IsNullOrEmpty(appId)) - { - throw new ArgumentNullException("appId"); - } + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { "Config.EvnSync", Functions.Config_Add })] + [HttpPost] + public async Task SyncEnv([FromBody] List toEnvs, [FromQuery] string appId, + [FromQuery] string currentEnv) + { + if (toEnvs == null) throw new ArgumentNullException("toEnvs"); - if (string.IsNullOrEmpty(currentEnv)) - { - throw new ArgumentNullException("currentEnv"); - } + if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException("appId"); - var app = await _appService.GetAsync(appId); - if (app == null) - { - return Json(new - { - success = false, - message = $"应用({appId})不存在。" - }); - } - - var result = await _configService.EnvSync(appId, currentEnv, toEnvs); + if (string.IsNullOrEmpty(currentEnv)) throw new ArgumentNullException("currentEnv"); + var app = await _appService.GetAsync(appId); + if (app == null) return Json(new { - success = result + success = false, + message = $"应用({appId})不存在。" }); - } - public async Task GetKvList(string appId, EnvString env) + var result = await _configService.EnvSync(appId, currentEnv, toEnvs); + + return Json(new { - if (string.IsNullOrEmpty(appId)) - { - throw new ArgumentNullException("appId"); - } + success = result + }); + } - var configs = await _configService.GetByAppIdAsync(appId, env.Value); - // When displaying text format, exclude deleted configurations. - configs = configs.Where(x => x.EditStatus != EditStatus.Deleted).ToList(); - var kvList = new List>(); - foreach (var config in configs) - { - kvList.Add(new KeyValuePair(_configService.GenerateKey(config), config.Value)); - } + public async Task GetKvList(string appId, EnvString env) + { + if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException("appId"); - kvList = kvList.OrderBy(x => x.Key).ToList(); - return Json(new - { - success = true, - data = kvList - }); - } + var configs = await _configService.GetByAppIdAsync(appId, env.Value); + // When displaying text format, exclude deleted configurations. + configs = configs.Where(x => x.EditStatus != EditStatus.Deleted).ToList(); + var kvList = new List>(); + foreach (var config in configs) + kvList.Add(new KeyValuePair(_configService.GenerateKey(config), config.Value)); - /// - /// Get configuration content in JSON format. - /// - /// Application ID. - /// - public async Task GetJson(string appId, EnvString env) + kvList = kvList.OrderBy(x => x.Key).ToList(); + return Json(new { - if (string.IsNullOrEmpty(appId)) - { - throw new ArgumentNullException("appId"); - } + success = true, + data = kvList + }); + } - var configs = await _configService.GetByAppIdAsync(appId, env.Value); - // When producing JSON, exclude deleted configurations. - configs = configs.Where(x => x.EditStatus != EditStatus.Deleted).ToList(); - var dict = new Dictionary(); - configs.ForEach(x => - { - var key = _configService.GenerateKey(x); - dict.Add(key, x.Value); - }); + /// + /// Get configuration content in JSON format. + /// + /// Application ID. + /// + public async Task GetJson(string appId, EnvString env) + { + if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException("appId"); - var json = DictionaryConvertToJson.ToJson(dict); + var configs = await _configService.GetByAppIdAsync(appId, env.Value); + // When producing JSON, exclude deleted configurations. + configs = configs.Where(x => x.EditStatus != EditStatus.Deleted).ToList(); + var dict = new Dictionary(); + configs.ForEach(x => + { + var key = _configService.GenerateKey(x); + dict.Add(key, x.Value); + }); - return Json(new - { - success = true, - data = json - }); - } + var json = DictionaryConvertToJson.ToJson(dict); - [HttpPost] - public async Task SaveJson([FromBody] SaveJsonVM data, string appId, EnvString env) + return Json(new { - if (string.IsNullOrEmpty(appId)) - { - throw new ArgumentNullException(nameof(appId)); - } + success = true, + data = json + }); + } - if (data == null) - { - throw new ArgumentNullException(nameof(data)); - } + [HttpPost] + public async Task SaveJson([FromBody] SaveJsonVM data, string appId, EnvString env) + { + if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException(nameof(appId)); - if (string.IsNullOrEmpty(data.json)) - { - throw new ArgumentNullException("data.json"); - } + if (data == null) throw new ArgumentNullException(nameof(data)); - var result = await _configService.SaveJsonAsync(data.json, appId, env.Value, data.isPatch); + if (string.IsNullOrEmpty(data.json)) throw new ArgumentNullException("data.json"); - return Json(new - { - success = result - }); - } + var result = await _configService.SaveJsonAsync(data.json, appId, env.Value, data.isPatch); - [HttpPost] - public async Task SaveKvList([FromBody] SaveKVListVM data, string appId, EnvString env) + return Json(new { - if (string.IsNullOrEmpty(appId)) - { - throw new ArgumentNullException(nameof(appId)); - } - - if (data == null) - { - throw new ArgumentNullException(nameof(data)); - } + success = result + }); + } - var validateResult = _configService.ValidateKvString(data.str); - if (!validateResult.Item1) - { - return Json(new - { - success = false, - message = validateResult.Item2 - }); - } + [HttpPost] + public async Task SaveKvList([FromBody] SaveKVListVM data, string appId, EnvString env) + { + if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException(nameof(appId)); - var result = await _configService.SaveKvListAsync(data.str, appId, env.Value, data.isPatch); + if (data == null) throw new ArgumentNullException(nameof(data)); + var validateResult = _configService.ValidateKvString(data.str); + if (!validateResult.Item1) return Json(new { - success = result + success = false, + message = validateResult.Item2 }); - } + + var result = await _configService.SaveKvListAsync(data.str, appId, env.Value, data.isPatch); + + return Json(new + { + success = result + }); } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/HomeController.cs b/src/AgileConfig.Server.Apisite/Controllers/HomeController.cs index dea61ad2..893225f7 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/HomeController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/HomeController.cs @@ -1,118 +1,110 @@ -using System; -using AgileConfig.Server.IService; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using System.Threading.Tasks; +using System; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using AgileConfig.Server.Apisite.Utilites; +using AgileConfig.Server.IService; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; -namespace AgileConfig.Server.Apisite.Controllers -{ - [Authorize] - public class HomeController : Controller - { - private readonly ISettingService _settingService; - private readonly IUserService _userService; - private readonly IPermissionService _permissionService; - private readonly ISystemInitializationService _systemInitializationService; - - public HomeController( - ISettingService settingService, - IUserService userService, - IPermissionService permissionService, - ISystemInitializationService systemInitializationService - ) - { - _settingService = settingService; - _userService = userService; - _permissionService = permissionService; - _systemInitializationService = systemInitializationService; - } +namespace AgileConfig.Server.Apisite.Controllers; - [AllowAnonymous] - public IActionResult IndexAsync() - { - if (!Appsettings.IsAdminConsoleMode) - { - return Content($"AgileConfig Node is running now , {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} ."); - } +[Authorize] +public class HomeController : Controller +{ + private readonly IPermissionService _permissionService; + private readonly ISettingService _settingService; + private readonly ISystemInitializationService _systemInitializationService; + private readonly IUserService _userService; - if (!_systemInitializationService.HasSa()) - { - return Redirect(Request.PathBase + "/ui#/user/initpassword"); - } + public HomeController( + ISettingService settingService, + IUserService userService, + IPermissionService permissionService, + ISystemInitializationService systemInitializationService + ) + { + _settingService = settingService; + _userService = userService; + _permissionService = permissionService; + _systemInitializationService = systemInitializationService; + } - return Redirect(Request.PathBase + "/ui"); - } + [AllowAnonymous] + public IActionResult IndexAsync() + { + if (!Appsettings.IsAdminConsoleMode) + return Content($"AgileConfig Node is running now , {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} ."); - public async Task Current() - { - string userName = this.GetCurrentUserName(); - if (string.IsNullOrEmpty(userName)) - { - return Json(new - { - currentUser = new - { - } - }); - } + if (!_systemInitializationService.HasSa()) return Redirect(Request.PathBase + "/ui#/user/initpassword"); - string userId = await this.GetCurrentUserId(_userService); - var userRoles = await _userService.GetUserRolesAsync(userId); - var userFunctions = await _permissionService.GetUserPermission(userId); + return Redirect(Request.PathBase + "/ui"); + } + public async Task Current() + { + var userName = this.GetCurrentUserName(); + if (string.IsNullOrEmpty(userName)) return Json(new { currentUser = new { - userId = userId, - userName, - currentAuthority = userRoles.Select(r => r.Code), - currentFunctions = userFunctions } }); - } - [AllowAnonymous] - public async Task Sys() + var userId = await this.GetCurrentUserId(_userService); + var userRoles = await _userService.GetUserRolesAsync(userId); + var userFunctions = await _permissionService.GetUserPermission(userId); + var userCategories = await _permissionService.GetUserCategories(userId); + + return Json(new { - string appVer = Assembly.GetAssembly(typeof(Program))?.GetName()?.Version?.ToString(); - string userName = this.GetCurrentUserName(); - if (string.IsNullOrEmpty(userName)) + currentUser = new { - return Json(new - { - appVer, - passwordInited = _systemInitializationService.HasSa(), - ssoEnabled = Appsettings.SsoEnabled, - ssoButtonText = Appsettings.SsoButtonText - }); + userId, + userName, + currentAuthority = userRoles.Select(r => r.Name), + currentFunctions = userFunctions, + currentCategories = userCategories } + }); + } - var envList = await _settingService.GetEnvironmentList(); + [AllowAnonymous] + public async Task Sys() + { + var appVer = Assembly.GetAssembly(typeof(Program))?.GetName()?.Version?.ToString(); + var userName = this.GetCurrentUserName(); + if (string.IsNullOrEmpty(userName)) return Json(new { appVer, passwordInited = _systemInitializationService.HasSa(), - envList, ssoEnabled = Appsettings.SsoEnabled, ssoButtonText = Appsettings.SsoButtonText }); - } - [AllowAnonymous] - public IActionResult Echo() + var envList = await _settingService.GetEnvironmentList(); + return Json(new { - return Content("ok"); - } + appVer, + passwordInited = _systemInitializationService.HasSa(), + envList, + ssoEnabled = Appsettings.SsoEnabled, + ssoButtonText = Appsettings.SsoButtonText + }); + } + [AllowAnonymous] + public IActionResult Echo() + { + return Content("ok"); + } - [AllowAnonymous] - public IActionResult GetIP() - { - return Content(String.Join(',', IpExt.GetEndpointIp())); - } + + [AllowAnonymous] + public IActionResult GetIP() + { + return Content(string.Join(',', IpExt.GetEndpointIp())); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/RemoteOPController.cs b/src/AgileConfig.Server.Apisite/Controllers/RemoteOPController.cs index c4dfb02f..fead6728 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/RemoteOPController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/RemoteOPController.cs @@ -6,116 +6,100 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -namespace AgileConfig.Server.Apisite.Controllers +namespace AgileConfig.Server.Apisite.Controllers; + +/// +/// Receives commands sent from other nodes. +/// +public class RemoteOpController : Controller { - /// - /// Receives commands sent from other nodes. - /// - public class RemoteOpController : Controller + private readonly IConfigService _configService; + private readonly ILogger _logger; + private readonly IServiceInfoService _serviceInfoService; + + public RemoteOpController(IConfigService configService, + IServiceInfoService serviceInfoService, + ILoggerFactory loggerFactory) { - private readonly IConfigService _configService; - private readonly IServiceInfoService _serviceInfoService; - private readonly ILogger _logger; - public RemoteOpController(IConfigService configService, - IServiceInfoService serviceInfoService, - ILoggerFactory loggerFactory) - { - _serviceInfoService = serviceInfoService; - _configService = configService; - _logger = loggerFactory.CreateLogger(); - } - - [HttpPost] - public IActionResult AllClientsDoActionAsync([FromBody]WebsocketAction action) - { - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } + _serviceInfoService = serviceInfoService; + _configService = configService; + _logger = loggerFactory.CreateLogger(); + } - WebsocketCollection.Instance.SendActionToAll(action); + [HttpPost] + public IActionResult AllClientsDoActionAsync([FromBody] WebsocketAction action) + { + if (action == null) throw new ArgumentNullException(nameof(action)); - return Json(new - { - success = true, - }); - } + WebsocketCollection.Instance.SendActionToAll(action); - [HttpPost] - public IActionResult AppClientsDoActionAsync([FromQuery]string appId,[FromQuery]string env, [FromBody]WebsocketAction action) + return Json(new { - if (string.IsNullOrEmpty(appId)) - { - throw new ArgumentNullException(nameof(appId)); - } - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - if (string.IsNullOrEmpty(env)) - { - throw new ArgumentNullException(nameof(env)); - } - - WebsocketCollection.Instance.SendActionToAppClients(appId, env, action); - - return Json(new - { - success = true, - }); - } - - [HttpPost] - public async Task OneClientDoActionAsync([FromQuery]string clientId, [FromBody]WebsocketAction action) + success = true + }); + } + + [HttpPost] + public IActionResult AppClientsDoActionAsync([FromQuery] string appId, [FromQuery] string env, + [FromBody] WebsocketAction action) + { + if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException(nameof(appId)); + if (action == null) throw new ArgumentNullException(nameof(action)); + if (string.IsNullOrEmpty(env)) throw new ArgumentNullException(nameof(env)); + + WebsocketCollection.Instance.SendActionToAppClients(appId, env, action); + + return Json(new { - var client = WebsocketCollection.Instance.Get(clientId); - if (client == null) - { - throw new Exception($"Can not find websocket client by id: {clientId}"); - } - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - await WebsocketCollection.Instance.SendActionToOne(client, action); - - return Json(new - { - success = true, - }); - } - - [HttpPost] - public IActionResult ClearConfigServiceCache() + success = true + }); + } + + [HttpPost] + public async Task OneClientDoActionAsync([FromQuery] string clientId, + [FromBody] WebsocketAction action) + { + var client = WebsocketCollection.Instance.Get(clientId); + if (client == null) throw new Exception($"Can not find websocket client by id: {clientId}"); + if (action == null) throw new ArgumentNullException(nameof(action)); + + await WebsocketCollection.Instance.SendActionToOne(client, action); + + return Json(new { - _configService.ClearCache(); - - _logger.LogInformation("Server clear all config's cache ."); - - return Json(new - { - success = true, - }); - } - - [HttpPost] - public IActionResult ClearServiceInfoCache() + success = true + }); + } + + [HttpPost] + public IActionResult ClearConfigServiceCache() + { + _configService.ClearCache(); + + _logger.LogInformation("Server clear all config's cache ."); + + return Json(new { - _serviceInfoService.ClearCache(); - - _logger.LogInformation("Server clear all serviceInfo's cache ."); - - return Json(new - { - success = true, - }); - } - - [HttpPost] - public IActionResult RegisterNode() + success = true + }); + } + + [HttpPost] + public IActionResult ClearServiceInfoCache() + { + _serviceInfoService.ClearCache(); + + _logger.LogInformation("Server clear all serviceInfo's cache ."); + + return Json(new { - return Content("ok"); - } + success = true + }); + } + + [HttpPost] + public IActionResult RegisterNode() + { + return Content("ok"); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/RemoteServerProxyController.cs b/src/AgileConfig.Server.Apisite/Controllers/RemoteServerProxyController.cs index 6f704a8e..62f527ba 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/RemoteServerProxyController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/RemoteServerProxyController.cs @@ -1,112 +1,112 @@ -using Agile.Config.Protocol; +using System.Threading.Tasks; +using Agile.Config.Protocol; using AgileConfig.Server.Apisite.Utilites; -using AgileConfig.Server.Common; using AgileConfig.Server.Common.EventBus; +using AgileConfig.Server.Common.Resources; +using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Event; using AgileConfig.Server.IService; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using System.Dynamic; -using System.Threading.Tasks; -using AgileConfig.Server.Common.Resources; -namespace AgileConfig.Server.Apisite.Controllers +namespace AgileConfig.Server.Apisite.Controllers; + +/// +/// Handles console web requests that proxy operations to remote server nodes (distinct from RemoteOpController). +/// +[Authorize] +public class RemoteServerProxyController : Controller { + private readonly ILogger _logger; + private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; + private readonly IServerNodeService _serverNodeService; + private readonly ITinyEventBus _tinyEventBus; + + public RemoteServerProxyController( + IRemoteServerNodeProxy remoteServerNodeProxy, + ILoggerFactory loggerFactory, + IServerNodeService serverNodeService, + ITinyEventBus tinyEventBus + ) + { + _serverNodeService = serverNodeService; + _remoteServerNodeProxy = remoteServerNodeProxy; + _tinyEventBus = tinyEventBus; + _logger = loggerFactory.CreateLogger(); + } + /// - /// Handles console web requests that proxy operations to remote server nodes (distinct from RemoteOpController). + /// Notify a node to disconnect a specific client. /// - [Authorize] - public class RemoteServerProxyController : Controller + /// Remote node address. + /// Client identifier to disconnect. + /// Operation result. + [HttpPost] + public async Task Client_Offline(string address, string clientId) { - private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; - private readonly ITinyEventBus _tinyEventBus; - private readonly ILogger _logger; + if (Appsettings.IsPreviewMode) + return Json(new + { + success = false, + message = Messages.DemoModeNoClientDisconnect + }); - public RemoteServerProxyController( - IRemoteServerNodeProxy remoteServerNodeProxy, - ILoggerFactory loggerFactory, - ITinyEventBus tinyEventBus - ) - { - _remoteServerNodeProxy = remoteServerNodeProxy; - _tinyEventBus = tinyEventBus; - _logger = loggerFactory.CreateLogger(); - } + var action = new WebsocketAction { Action = ActionConst.Offline, Module = ActionModule.ConfigCenter }; + var result = await _remoteServerNodeProxy.OneClientDoActionAsync(address, clientId, action); + if (result) _tinyEventBus.Fire(new DiscoinnectSuccessful(clientId, this.GetCurrentUserName())); + + _logger.LogInformation("Request remote node {0} 's action OneClientDoAction {1} .", address, + result ? "success" : "fail"); - /// - /// Notify a node to disconnect a specific client. - /// - /// Remote node address. - /// Client identifier to disconnect. - /// Operation result. - [HttpPost] - public async Task Client_Offline(string address, string clientId) + return Json(new { - if (Appsettings.IsPreviewMode) - { - return Json(new - { - success = false, - message = Messages.DemoModeNoClientDisconnect - }); - } + success = true + }); + } - var action = new WebsocketAction { Action = ActionConst.Offline, Module = ActionModule.ConfigCenter }; - var result = await _remoteServerNodeProxy.OneClientDoActionAsync(address, clientId, action); - if (result) + /// + /// Notify a node to instruct all clients to reload configuration. + /// + /// Remote node address. + /// Operation result. + [HttpPost] + public async Task AllClients_Reload() + { + var nodes = await _serverNodeService.GetAllNodesAsync(); + var action = new WebsocketAction { Action = ActionConst.Reload, Module = ActionModule.ConfigCenter }; + foreach (var node in nodes) + if (node.Status == NodeStatus.Online) { - _tinyEventBus.Fire(new DiscoinnectSuccessful(clientId, this.GetCurrentUserName())); + var result = await _remoteServerNodeProxy.AllClientsDoActionAsync(node.Id, action); + _logger.LogInformation("Request remote node {0} 's action AllClientsDoAction {1} .", node.Id, + result ? "success" : "fail"); } - _logger.LogInformation("Request remote node {0} 's action OneClientDoAction {1} .", address, - result ? "success" : "fail"); - - return Json(new - { - success = true, - }); - } - - /// - /// Notify a node to instruct all clients to reload configuration. - /// - /// Remote node address. - /// Operation result. - [HttpPost] - public async Task AllClients_Reload(string address) + return Json(new { - var action = new WebsocketAction { Action = ActionConst.Reload, Module = ActionModule.ConfigCenter }; - var result = await _remoteServerNodeProxy.AllClientsDoActionAsync(address, action); + success = true + }); + } - _logger.LogInformation("Request remote node {0} 's action AllClientsDoAction {1} .", address, - result ? "success" : "fail"); + /// + /// Notify a node to instruct a single client to reload configuration. + /// + /// Remote node address. + /// Client identifier to reload. + /// Operation result. + [HttpPost] + public async Task Client_Reload(string address, string clientId) + { + var action = new WebsocketAction { Action = ActionConst.Reload, Module = ActionModule.ConfigCenter }; + var result = await _remoteServerNodeProxy.OneClientDoActionAsync(address, clientId, action); - return Json(new - { - success = true, - }); - } + _logger.LogInformation("Request remote node {0} 's action OneClientDoAction {1} .", address, + result ? "success" : "fail"); - /// - /// Notify a node to instruct a single client to reload configuration. - /// - /// Remote node address. - /// Client identifier to reload. - /// Operation result. - [HttpPost] - public async Task Client_Reload(string address, string clientId) + return Json(new { - var action = new WebsocketAction { Action = ActionConst.Reload, Module = ActionModule.ConfigCenter }; - var result = await _remoteServerNodeProxy.OneClientDoActionAsync(address, clientId, action); - - _logger.LogInformation("Request remote node {0} 's action OneClientDoAction {1} .", address, - result ? "success" : "fail"); - - return Json(new - { - success = true, - }); - } + success = true + }); } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/ReportController.cs b/src/AgileConfig.Server.Apisite/Controllers/ReportController.cs index ae4e3945..cdb63186 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/ReportController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/ReportController.cs @@ -7,187 +7,175 @@ using AgileConfig.Server.IService; using Microsoft.AspNetCore.Mvc; -namespace AgileConfig.Server.Apisite.Controllers +namespace AgileConfig.Server.Apisite.Controllers; + +/// +/// Reports node status information, such as connected clients. +/// +public class ReportController : Controller { + private readonly IAppService _appService; + private readonly IConfigService _configService; + private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; + private readonly IServerNodeService _serverNodeService; + private readonly IServiceInfoService _serviceInfoService; + + public ReportController( + IConfigService configService, + IAppService appService, + IServerNodeService serverNodeService, + IRemoteServerNodeProxy remoteServerNodeProxy, + IServiceInfoService serviceInfoService) + { + _appService = appService; + _configService = configService; + _serverNodeService = serverNodeService; + _remoteServerNodeProxy = remoteServerNodeProxy; + _serviceInfoService = serviceInfoService; + } + /// - /// Reports node status information, such as connected clients. + /// Get client connections on the current node. /// - public class ReportController : Controller + /// + public IActionResult Clients() { - private readonly IConfigService _configService; - private readonly IAppService _appService; - private readonly IServerNodeService _serverNodeService; - private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; - private readonly IServiceInfoService _serviceInfoService; - - public ReportController( - IConfigService configService, - IAppService appService, - IServerNodeService serverNodeService, - IRemoteServerNodeProxy remoteServerNodeProxy, - IServiceInfoService serviceInfoService) - { - _appService = appService; - _configService = configService; - _serverNodeService = serverNodeService; - _remoteServerNodeProxy = remoteServerNodeProxy; - _serviceInfoService = serviceInfoService; - } + var report = WebsocketCollection.Instance.Report(); - /// - /// Get client connections on the current node. - /// - /// - public IActionResult Clients() - { - var report = WebsocketCollection.Instance.Report(); + return Json(report); + } - return Json(report); - } + /// + /// Get client connections on a specific node. + /// + /// Server address to inspect. + /// JSON result containing the client report. + public IActionResult ServerNodeClients(string address) + { + var report = _remoteServerNodeProxy.GetClientsReportAsync(address); - /// - /// Get client connections on a specific node. - /// - /// Server address to inspect. - /// JSON result containing the client report. - public IActionResult ServerNodeClients(string address) - { - var report = _remoteServerNodeProxy.GetClientsReportAsync(address); + return Json(report); + } - return Json(report); + public async Task SearchServerNodeClients(string address, string appId, string env, int current, + int pageSize) + { + if (current <= 0) throw new ArgumentException("current can not less than 1 ."); + if (pageSize <= 0) throw new ArgumentException("pageSize can not less than 1 ."); + var addressess = new List(); + var nodes = await _serverNodeService.GetAllNodesAsync(); + if (string.IsNullOrEmpty(address)) + { + addressess.AddRange(nodes.Where(x => x.Status == NodeStatus.Online).Select(n => n.Id.ToString())); + } + else + { + if (nodes.Any(x => + x.Status == NodeStatus.Online && + x.Id.ToString().Equals(address, StringComparison.CurrentCultureIgnoreCase))) + addressess.Add(address); } - public async Task SearchServerNodeClients(string address, string appId, string env, int current, int pageSize) + var clients = new List(); + foreach (var addr in addressess) { - if (current <= 0) - { - throw new ArgumentException("current can not less than 1 ."); - } - if (pageSize <= 0) - { - throw new ArgumentException("pageSize can not less than 1 ."); - } - var addressess = new List(); - var nodes = await _serverNodeService.GetAllNodesAsync(); - if (string.IsNullOrEmpty(address)) - { - addressess.AddRange(nodes.Where(x=>x.Status == NodeStatus.Online).Select(n => n.Id.ToString())); - } - else - { - if (nodes.Any(x=>x.Status == NodeStatus.Online && x.Id.ToString().Equals(address, StringComparison.CurrentCultureIgnoreCase))) - { - addressess.Add(address); - } - } + var report = await _remoteServerNodeProxy.GetClientsReportAsync(addr); + if (report != null && report.Infos != null) clients.AddRange(report.Infos); + } - var clients = new List(); - foreach (var addr in addressess) - { - var report = await _remoteServerNodeProxy.GetClientsReportAsync(addr); - if (report != null && report.Infos != null) - { - clients.AddRange(report.Infos); - } - } + // filter by env + if (!string.IsNullOrEmpty(env)) + clients = clients.Where(x => x.Env.Contains(env, StringComparison.CurrentCultureIgnoreCase)).ToList(); + // filter by appid + if (!string.IsNullOrEmpty(appId)) + clients = clients.Where(x => x.AppId.Contains(appId, StringComparison.CurrentCultureIgnoreCase)).ToList(); - // filter by env - if (!string.IsNullOrEmpty(env)) - { - clients = clients.Where(x => x.Env.Contains(env, StringComparison.CurrentCultureIgnoreCase)).ToList(); - } - // filter by appid - if (!string.IsNullOrEmpty(appId)) - { - clients = clients.Where(x => x.AppId.Contains(appId, StringComparison.CurrentCultureIgnoreCase)).ToList(); - } + var page = clients.OrderBy(i => i.Address).ThenBy(i => i.Id).Skip((current - 1) * pageSize).Take(pageSize); - var page = clients.OrderBy(i => i.Address).ThenBy(i => i.Id).Skip((current - 1) * pageSize).Take(pageSize); + return Json(new + { + current, + pageSize, + success = true, + total = clients.Count, + data = page + }); + } - return Json(new - { - current, - pageSize, - success = true, - total = clients.Count, - data = page - }); - } + /// + /// Get the number of enabled applications. + /// + /// + public async Task AppCount() + { + var appCount = await _appService.CountEnabledAppsAsync(); + return Json(appCount); + } - /// - /// Get the number of enabled applications. - /// - /// - public async Task AppCount() - { - var appCount = await _appService.CountEnabledAppsAsync(); - return Json(appCount); - } + /// + /// Get the number of enabled configuration items. + /// + /// + public async Task ConfigCount() + { + var configCount = await _configService.CountEnabledConfigsAsync(); + return Json(configCount); + } - /// - /// Get the number of enabled configuration items. - /// - /// - public async Task ConfigCount() - { - var configCount = await _configService.CountEnabledConfigsAsync(); - return Json(configCount); - } + /// + /// Get the number of server nodes. + /// + /// + public async Task NodeCount() + { + var nodeCount = (await _serverNodeService.GetAllNodesAsync()).Count; + return Json(nodeCount); + } - /// - /// Get the number of server nodes. - /// - /// - public async Task NodeCount() - { - var nodeCount = (await _serverNodeService.GetAllNodesAsync()).Count; - return Json(nodeCount); - } + /// + /// Get status information for all nodes. + /// + /// + public async Task RemoteNodesStatus() + { + var nodes = await _serverNodeService.GetAllNodesAsync(); + var result = new List(); - /// - /// Get status information for all nodes. - /// - /// - public async Task RemoteNodesStatus() + foreach (var serverNode in nodes) { - var nodes = await _serverNodeService.GetAllNodesAsync(); - var result = new List(); - - foreach (var serverNode in nodes) + if (serverNode.Status == NodeStatus.Offline) { - if (serverNode.Status == NodeStatus.Offline) - { - result.Add(new - { - n = serverNode, - server_status = new ClientInfos - { - ClientCount = 0, - Infos = new List() - } - }); - continue; - } result.Add(new { n = serverNode, - server_status = await _remoteServerNodeProxy.GetClientsReportAsync(serverNode.Id.ToString()) + server_status = new ClientInfos + { + ClientCount = 0, + Infos = new List() + } }); + continue; } - return Json(result); - } - - public async Task ServiceCount() - { - var services = await _serviceInfoService.GetAllServiceInfoAsync(); - var serviceCount = services.Count; - var serviceOnCount = services.Count(x => x.Status == ServiceStatus.Healthy); - return Json(new + result.Add(new { - serviceCount, - serviceOnCount + n = serverNode, + server_status = await _remoteServerNodeProxy.GetClientsReportAsync(serverNode.Id) }); } + + return Json(result); + } + + public async Task ServiceCount() + { + var services = await _serviceInfoService.GetAllServiceInfoAsync(); + var serviceCount = services.Count; + var serviceOnCount = services.Count(x => x.Status == ServiceStatus.Healthy); + return Json(new + { + serviceCount, + serviceOnCount + }); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs b/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs index ba1b917d..66223edd 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs @@ -1,178 +1,170 @@ -using AgileConfig.Server.Apisite.Filters; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using AgileConfig.Server.Apisite.Filters; using AgileConfig.Server.Apisite.Models; using AgileConfig.Server.Common; using AgileConfig.Server.Common.Resources; +using AgileConfig.Server.Data.Abstraction; using AgileConfig.Server.Data.Entity; using AgileConfig.Server.IService; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; -using System.Threading.Tasks; -namespace AgileConfig.Server.Apisite.Controllers +namespace AgileConfig.Server.Apisite.Controllers; + +[Authorize] +public class RoleController : Controller { - [Authorize] - public class RoleController : Controller + private static readonly IReadOnlyList SupportedFunctions = new List { - private readonly IRoleService _roleService; + Functions.App_Add, + Functions.App_Edit, + Functions.App_Delete, + Functions.App_Auth, - private static readonly IReadOnlyList SupportedFunctions = new List - { - Functions.App_Add, - Functions.App_Edit, - Functions.App_Delete, - Functions.App_Auth, + Functions.Config_Add, + Functions.Config_Edit, + Functions.Config_Delete, + Functions.Config_Publish, + Functions.Config_Offline, - Functions.Config_Add, - Functions.Config_Edit, - Functions.Config_Delete, - Functions.Config_Publish, - Functions.Config_Offline, + Functions.Node_Add, + Functions.Node_Delete, - Functions.Node_Add, - Functions.Node_Delete, + Functions.Client_Disconnect, - Functions.Client_Disconnect, + Functions.User_Add, + Functions.User_Edit, + Functions.User_Delete, - Functions.User_Add, - Functions.User_Edit, - Functions.User_Delete, + Functions.Role_Add, + Functions.Role_Edit, + Functions.Role_Delete + }; - Functions.Role_Add, - Functions.Role_Edit, - Functions.Role_Delete - }; + private readonly IRoleFunctionRepository _roleFunctionRepository; + private readonly IRoleService _roleService; - public RoleController(IRoleService roleService) - { - _roleService = roleService; - } + public RoleController(IRoleService roleService, IRoleFunctionRepository roleFunctionRepository) + { + _roleService = roleService; + _roleFunctionRepository = roleFunctionRepository; + } - [HttpGet] - public async Task List() - { - var roles = await _roleService.GetAllAsync(); - // Filter out Super Administrator role to prevent it from being assigned through the frontend - var vms = roles - .Where(r => r.Id != SystemRoleConstants.SuperAdminId) - .Select(ToViewModel) - .OrderByDescending(r => r.IsSystem) - .ThenBy(r => r.Name) - .ToList(); + [HttpGet] + public async Task List() + { + var roles = await _roleService.GetAllAsync(); + // Filter out Super Administrator role to prevent it from being assigned through the frontend + var vms = new List(); + foreach (var role in roles.Where(r => r.Id != SystemRoleConstants.SuperAdminId)) + vms.Add(await ToViewModel(role)); - return Json(new - { - success = true, - data = vms - }); - } + vms = vms.OrderByDescending(r => r.IsSystem) + .ThenBy(r => r.Name) + .ToList(); - [HttpGet] - public IActionResult SupportedPermissions() + return Json(new { - return Json(new - { - success = true, - data = SupportedFunctions - }); - } + success = true, + data = vms + }); + } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Role.Add", Functions.Role_Add })] - [HttpPost] - public async Task Add([FromBody] RoleVM model) + [HttpGet] + public IActionResult SupportedPermissions() + { + return Json(new { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - var role = new Role - { - Id = model.Id, - Name = model.Name, - Description = model.Description ?? string.Empty, - IsSystem = false - }; - - await _roleService.CreateAsync(role, model.Functions ?? Enumerable.Empty()); + success = true, + data = SupportedFunctions + }); + } - return Json(new { success = true }); - } + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Role.Add", Functions.Role_Add })] + [HttpPost] + public async Task Add([FromBody] RoleVM model) + { + if (model == null) throw new ArgumentNullException(nameof(model)); - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Role.Edit", Functions.Role_Edit })] - [HttpPost] - public async Task Edit([FromBody] RoleVM model) + var role = new Role { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } + Id = model.Id, + Name = model.Name, + Description = model.Description ?? string.Empty, + IsSystem = false + }; - var role = new Role() - { - Id = model.Id, - Name = model.Name, - Description = model.Description ?? string.Empty, - IsSystem = model.IsSystem - }; + await _roleService.CreateAsync(role, model.Functions ?? Enumerable.Empty()); - var result = await _roleService.UpdateAsync(role, model.Functions ?? Enumerable.Empty()); + return Json(new { success = true }); + } + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Role.Edit", Functions.Role_Edit })] + [HttpPost] + public async Task Edit([FromBody] RoleVM model) + { + if (model == null) throw new ArgumentNullException(nameof(model)); + + // Prevent editing SuperAdministrator role + if (model.Id == SystemRoleConstants.SuperAdminId) return Json(new { - success = result, - message = result ? string.Empty : Messages.UpdateRoleFailed + success = false, + message = "SuperAdministrator role cannot be edited" }); - } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Role.Delete", Functions.Role_Delete })] - [HttpPost] - public async Task Delete(string id) + var role = new Role { - if (string.IsNullOrWhiteSpace(id)) - { - throw new ArgumentNullException(nameof(id)); - } + Id = model.Id, + Name = model.Name, + Description = model.Description ?? string.Empty, + IsSystem = model.IsSystem + }; + + var result = await _roleService.UpdateAsync(role, model.Functions ?? Enumerable.Empty()); + + return Json(new + { + success = result, + message = result ? string.Empty : Messages.UpdateRoleFailed + }); + } + + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Role.Delete", Functions.Role_Delete })] + [HttpPost] + public async Task Delete(string id) + { + if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); - var result = await _roleService.DeleteAsync(id); + // Prevent deleting SuperAdministrator role + if (id == SystemRoleConstants.SuperAdminId) return Json(new { - success = result, - message = result ? string.Empty : Messages.DeleteRoleFailed + success = false, + message = "SuperAdministrator role cannot be deleted" }); - } - private static RoleVM ToViewModel(Role role) + var result = await _roleService.DeleteAsync(id); + return Json(new { - return new RoleVM - { - Id = role.Id, - Name = role.Name, - Description = role.Description, - IsSystem = role.IsSystem, - Functions = ParseFunctions(role.FunctionsJson) - }; - } - - private static List ParseFunctions(string json) - { - if (string.IsNullOrWhiteSpace(json)) - { - return new List(); - } + success = result, + message = result ? string.Empty : Messages.DeleteRoleFailed + }); + } - try - { - var funcs = JsonSerializer.Deserialize>(json); - return funcs ?? new List(); - } - catch - { - return new List(); - } - } + private async Task ToViewModel(Role role) + { + var roleFunctions = await _roleFunctionRepository.QueryAsync(x => x.RoleId == role.Id); + return new RoleVM + { + Id = role.Id, + Name = role.Name, + Description = role.Description, + IsSystem = role.IsSystem, + Functions = roleFunctions.Select(rf => rf.FunctionId).ToList() + }; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/SSOController.cs b/src/AgileConfig.Server.Apisite/Controllers/SSOController.cs index d512950c..4e29e614 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/SSOController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/SSOController.cs @@ -1,67 +1,55 @@ -using AgileConfig.Server.OIDC; +using System; +using AgileConfig.Server.OIDC; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using System; -namespace AgileConfig.Server.Apisite.Controllers +namespace AgileConfig.Server.Apisite.Controllers; + +public class SsoController : Controller { - public class SsoController : Controller + private readonly IOidcClient _oidcClient; + + public SsoController(IOidcClient oidcClient) { - private readonly IOidcClient _oidcClient; + _oidcClient = oidcClient; + } - public SsoController(IOidcClient oidcClient) - { - _oidcClient = oidcClient; - } + /// + /// pass the oidc code to frontend + /// + /// Authorization code returned by the OIDC provider. + /// Redirect to the admin UI with the provided code. + [AllowAnonymous] + public IActionResult Index(string code) + { + if (!Appsettings.IsAdminConsoleMode) + return Content($"AgileConfig Node is running now , {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} ."); - /// - /// pass the oidc code to frontend - /// - /// Authorization code returned by the OIDC provider. - /// Redirect to the admin UI with the provided code. - [AllowAnonymous] - public IActionResult Index(string code) - { - if (!Appsettings.IsAdminConsoleMode) - { - return Content($"AgileConfig Node is running now , {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} ."); - } + if (!Appsettings.SsoEnabled) return BadRequest("SSO not enabled"); - if (!Appsettings.SsoEnabled) - { - return BadRequest("SSO not enabled"); - } + return Redirect(Request.PathBase + "/ui#/oidc/login?code=" + code); + } - return Redirect(Request.PathBase + "/ui#/oidc/login?code=" + code); - } + public IActionResult Login() + { + if (!Appsettings.SsoEnabled) return BadRequest("SSO not enabled"); - public IActionResult Login() - { - if (!Appsettings.SsoEnabled) - { - return BadRequest("SSO not enabled"); - } + var url = _oidcClient.GetAuthorizeUrl(); - var url = _oidcClient.GetAuthorizeUrl(); - - return Redirect(url); - } + return Redirect(url); + } - public IActionResult LoginUrl() - { - if (!Appsettings.SsoEnabled) - { - return BadRequest("SSO not enabled"); - } + public IActionResult LoginUrl() + { + if (!Appsettings.SsoEnabled) return BadRequest("SSO not enabled"); - var url = _oidcClient.GetAuthorizeUrl(); + var url = _oidcClient.GetAuthorizeUrl(); - return Json(new - { - success = true, - data = url - }); - } + return Json(new + { + success = true, + data = url + }); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/ServerNodeController.cs b/src/AgileConfig.Server.Apisite/Controllers/ServerNodeController.cs index a1cd8ae1..39c298d8 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/ServerNodeController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/ServerNodeController.cs @@ -1,140 +1,124 @@ using System; +using System.Linq; using System.Threading.Tasks; using AgileConfig.Server.Apisite.Filters; using AgileConfig.Server.Apisite.Models; -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.IService; -using Microsoft.AspNetCore.Mvc; -using System.Linq; -using Microsoft.AspNetCore.Authorization; using AgileConfig.Server.Apisite.Utilites; using AgileConfig.Server.Common.EventBus; -using AgileConfig.Server.Event; using AgileConfig.Server.Common.Resources; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Event; +using AgileConfig.Server.IService; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; -namespace AgileConfig.Server.Apisite.Controllers -{ - [Authorize] - [ModelVaildate] - public class ServerNodeController : Controller - { - private readonly IServerNodeService _serverNodeService; - private readonly ISysLogService _sysLogService; - private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; - private readonly ITinyEventBus _tinyEventBus; - - public ServerNodeController(IServerNodeService serverNodeService, - ISysLogService sysLogService, - IRemoteServerNodeProxy remoteServerNodeProxy, - ITinyEventBus tinyEventBus - ) - { - _serverNodeService = serverNodeService; - _sysLogService = sysLogService; - _remoteServerNodeProxy = remoteServerNodeProxy; - _tinyEventBus = tinyEventBus; - } - - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Node.Add", Functions.Node_Add })] - [HttpPost] - public async Task Add([FromBody] ServerNodeVM model) - { - if (model == null) - { - throw new ArgumentNullException("model"); - } - model.Address = model.Address.TrimEnd('/'); - var oldNode = await _serverNodeService.GetAsync(model.Address); - if (oldNode != null) - { - return Json(new - { - success = false, - message = Messages.NodeAlreadyExists - }); - } +namespace AgileConfig.Server.Apisite.Controllers; - var node = new ServerNode(); - node.Id = model.Address.TrimEnd('/'); - node.Remark = model.Remark; - node.Status = NodeStatus.Offline; - node.CreateTime = DateTime.Now; +[Authorize] +[ModelVaildate] +public class ServerNodeController : Controller +{ + private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; + private readonly IServerNodeService _serverNodeService; + private readonly ISysLogService _sysLogService; + private readonly ITinyEventBus _tinyEventBus; - var result = await _serverNodeService.AddAsync(node); - if (result) - { - _tinyEventBus.Fire(new AddNodeSuccessful(node, this.GetCurrentUserName())); - await _remoteServerNodeProxy.TestEchoAsync(node.Id); - } + public ServerNodeController(IServerNodeService serverNodeService, + ISysLogService sysLogService, + IRemoteServerNodeProxy remoteServerNodeProxy, + ITinyEventBus tinyEventBus + ) + { + _serverNodeService = serverNodeService; + _sysLogService = sysLogService; + _remoteServerNodeProxy = remoteServerNodeProxy; + _tinyEventBus = tinyEventBus; + } + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Node.Add", Functions.Node_Add })] + [HttpPost] + public async Task Add([FromBody] ServerNodeVM model) + { + if (model == null) throw new ArgumentNullException("model"); + model.Address = model.Address.TrimEnd('/'); + var oldNode = await _serverNodeService.GetAsync(model.Address); + if (oldNode != null) return Json(new { - data = node, - success = result, - message = !result ? Messages.AddNodeFailed : "" + success = false, + message = Messages.NodeAlreadyExists }); + + var node = new ServerNode(); + node.Id = model.Address.TrimEnd('/'); + node.Remark = model.Remark; + node.Status = NodeStatus.Offline; + node.CreateTime = DateTime.Now; + + var result = await _serverNodeService.AddAsync(node); + if (result) + { + _tinyEventBus.Fire(new AddNodeSuccessful(node, this.GetCurrentUserName())); + await _remoteServerNodeProxy.TestEchoAsync(node.Id); } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Node.Delete", Functions.Node_Delete })] - [HttpPost] - public async Task Delete([FromBody] ServerNodeVM model) + return Json(new { - if (Appsettings.IsPreviewMode) - { - return Json(new - { - success = false, - message = Messages.DemoModeNoNodeDelete - }); - } - if (model == null) - { - throw new ArgumentNullException("model"); - } + data = node, + success = result, + message = !result ? Messages.AddNodeFailed : "" + }); + } - var node = await _serverNodeService.GetAsync(model.Address); - if (node == null) + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Node.Delete", Functions.Node_Delete })] + [HttpPost] + public async Task Delete([FromBody] ServerNodeVM model) + { + if (Appsettings.IsPreviewMode) + return Json(new { - return Json(new - { - success = false, - message = Messages.NodeNotFound - }); - } + success = false, + message = Messages.DemoModeNoNodeDelete + }); + if (model == null) throw new ArgumentNullException("model"); - var result = await _serverNodeService.DeleteAsync(node); - if (result) - { - _tinyEventBus.Fire(new DeleteNodeSuccessful(node, this.GetCurrentUserName())); - } + var node = await _serverNodeService.GetAsync(model.Address); + if (node == null) return Json(new { - success = result, - message = !result ? Messages.DeleteNodeFailed : "" + success = false, + message = Messages.NodeNotFound }); - } - [HttpGet] - public async Task All() + var result = await _serverNodeService.DeleteAsync(node); + if (result) _tinyEventBus.Fire(new DeleteNodeSuccessful(node, this.GetCurrentUserName())); + return Json(new { - var nodes = await _serverNodeService.GetAllNodesAsync(); + success = result, + message = !result ? Messages.DeleteNodeFailed : "" + }); + } - var vms = nodes.OrderBy(x => x.CreateTime).Select(x => - { - return new ServerNodeVM - { - Address = x.Id, - Remark = x.Remark, - LastEchoTime = x.LastEchoTime, - Status = x.Status - }; - }); + [HttpGet] + public async Task All() + { + var nodes = await _serverNodeService.GetAllNodesAsync(); - return Json(new + var vms = nodes.OrderBy(x => x.CreateTime).Select(x => + { + return new ServerNodeVM { - success = true, - data = vms - }); - } + Address = x.Id, + Remark = x.Remark, + LastEchoTime = x.LastEchoTime, + Status = x.Status + }; + }); + + return Json(new + { + success = true, + data = vms + }); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/ServiceController.cs b/src/AgileConfig.Server.Apisite/Controllers/ServiceController.cs index 8a741794..de0ba0a3 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/ServiceController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/ServiceController.cs @@ -1,185 +1,150 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using AgileConfig.Server.Apisite.Filters; using AgileConfig.Server.Apisite.Models; +using AgileConfig.Server.Common.EventBus; +using AgileConfig.Server.Common.Resources; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Event; using AgileConfig.Server.IService; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using System.Linq; -using System.Collections.Generic; -using System.Dynamic; -using AgileConfig.Server.Common; -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.Event; -using AgileConfig.Server.Common.EventBus; -using AgileConfig.Server.Common.Resources; -namespace AgileConfig.Server.Apisite.Controllers +namespace AgileConfig.Server.Apisite.Controllers; + +[Authorize] +[ModelVaildate] +public class ServiceController : Controller { - [Authorize] - [ModelVaildate] - public class ServiceController : Controller + private readonly IRegisterCenterService _registerCenterService; + private readonly IServiceInfoService _serviceInfoService; + private readonly ITinyEventBus _tinyEventBus; + + public ServiceController(IServiceInfoService serviceInfoService, + IRegisterCenterService registerCenterService, + ITinyEventBus tinyEventBus) { - private readonly IServiceInfoService _serviceInfoService; - private readonly IRegisterCenterService _registerCenterService; - private readonly ITinyEventBus _tinyEventBus; + _serviceInfoService = serviceInfoService; + _registerCenterService = registerCenterService; + _tinyEventBus = tinyEventBus; + } - public ServiceController(IServiceInfoService serviceInfoService, - IRegisterCenterService registerCenterService, - ITinyEventBus tinyEventBus) - { - _serviceInfoService = serviceInfoService; - _registerCenterService = registerCenterService; - _tinyEventBus = tinyEventBus; - } + [HttpPost] + public async Task Add([FromBody] ServiceInfoVM model) + { + if (model == null) throw new ArgumentNullException(nameof(model)); - [HttpPost] - public async Task Add([FromBody] ServiceInfoVM model) - { - if (model == null) + if (await _serviceInfoService.GetByServiceIdAsync(model.ServiceId) != null) + return Json(new { - throw new ArgumentNullException(nameof(model)); - } + success = false, + message = Messages.ServiceAlreadyExists + }); - if ((await _serviceInfoService.GetByServiceIdAsync(model.ServiceId)) != null) - { - return Json(new - { - success = false, - message = Messages.ServiceAlreadyExists - }); - } - - var service = new ServiceInfo(); - service.Ip = model.Ip; - service.Port = model.Port; - service.AlarmUrl = model.AlarmUrl; - service.CheckUrl = model.CheckUrl; - service.MetaData = model.MetaData; - service.ServiceId = model.ServiceId; - service.ServiceName = model.ServiceName; - service.RegisterWay = RegisterWay.Manual; - service.HeartBeatMode = model.HeartBeatMode; - var uniqueId = await _registerCenterService.RegisterAsync(service); - - _tinyEventBus.Fire(new ServiceRegisteredEvent(uniqueId)); + var service = new ServiceInfo(); + service.Ip = model.Ip; + service.Port = model.Port; + service.AlarmUrl = model.AlarmUrl; + service.CheckUrl = model.CheckUrl; + service.MetaData = model.MetaData; + service.ServiceId = model.ServiceId; + service.ServiceName = model.ServiceName; + service.RegisterWay = RegisterWay.Manual; + service.HeartBeatMode = model.HeartBeatMode; + var uniqueId = await _registerCenterService.RegisterAsync(service); + + _tinyEventBus.Fire(new ServiceRegisteredEvent(uniqueId)); + + return Json(new + { + success = true + }); + } + [HttpPost] + public async Task Remove(string id) + { + if (string.IsNullOrEmpty(id)) throw new ArgumentNullException("id"); + + var service = await _serviceInfoService.GetByUniqueIdAsync(id); + if (service == null) return Json(new { - success = true + success = false, + message = Messages.ServiceNotFound }); - } - [HttpPost] - public async Task Remove(string id) + await _registerCenterService.UnRegisterAsync(id); + + _tinyEventBus.Fire(new ServiceUnRegisterEvent(service.Id)); + + return Json(new { - if (string.IsNullOrEmpty(id)) - { - throw new ArgumentNullException("id"); - } + success = true + }); + } - var service = await _serviceInfoService.GetByUniqueIdAsync(id); - if (service == null) - { - return Json(new - { - success = false, - message = Messages.ServiceNotFound - }); - } + public async Task Search(string serviceName, string serviceId, ServiceStatus? status, + string sortField, string ascOrDesc, + int current = 1, int pageSize = 20) + { + if (current < 1) throw new ArgumentException(Messages.CurrentCannotBeLessThanOneService); + if (pageSize < 1) throw new ArgumentException(Messages.PageSizeCannotBeLessThanOneService); - await _registerCenterService.UnRegisterAsync(id); + var query = await _serviceInfoService.GetAllServiceInfoAsync(); + if (!string.IsNullOrWhiteSpace(serviceName)) + query = query.Where(x => x.ServiceName.Contains(serviceName)).ToList(); + if (!string.IsNullOrWhiteSpace(serviceId)) query = query.Where(x => x.ServiceId.Contains(serviceId)).ToList(); + if (status.HasValue) query = query.Where(x => x.Status == status).ToList(); - _tinyEventBus.Fire(new ServiceUnRegisterEvent(service.Id)); + query = query.OrderByDescending(x => x.RegisterTime).ToList(); - return Json(new - { - success = true - }); - } - - public async Task Search(string serviceName, string serviceId, ServiceStatus? status, - string sortField, string ascOrDesc, - int current = 1, int pageSize = 20) + if (sortField == "registerTime") { - if (current < 1) - { - throw new ArgumentException(Messages.CurrentCannotBeLessThanOneService); - } - if (pageSize < 1) - { - throw new ArgumentException(Messages.PageSizeCannotBeLessThanOneService); - } + if (ascOrDesc.StartsWith("asc")) + query = query.OrderBy(x => x.RegisterTime).ToList(); + else + query = query.OrderByDescending(x => x.RegisterTime).ToList(); + } - var query = await _serviceInfoService.GetAllServiceInfoAsync(); - if (!string.IsNullOrWhiteSpace(serviceName)) - { - query = query.Where(x => x.ServiceName.Contains(serviceName)).ToList(); - } - if (!string.IsNullOrWhiteSpace(serviceId)) - { - query = query.Where(x => x.ServiceId.Contains(serviceId)).ToList(); - } - if (status.HasValue) - { - query = query.Where(x => x.Status == status).ToList(); - } + if (sortField == "serviceName") + { + if (ascOrDesc.StartsWith("asc")) + query = query.OrderBy(x => x.ServiceName).ToList(); + else + query = query.OrderByDescending(x => x.ServiceName).ToList(); + } - query = query.OrderByDescending(x => x.RegisterTime).ToList(); - - if (sortField == "registerTime") - { - if (ascOrDesc.StartsWith("asc")) - { - query = query.OrderBy(x => x.RegisterTime).ToList(); - } - else - { - query = query.OrderByDescending(x => x.RegisterTime).ToList(); - } - } - if (sortField == "serviceName") - { - if (ascOrDesc.StartsWith("asc")) - { - query = query.OrderBy(x => x.ServiceName).ToList(); - } - else - { - query = query.OrderByDescending(x => x.ServiceName).ToList(); - } - } - var count = query.Count; - var page = query.Skip((current - 1) * pageSize).Take(pageSize).ToList(); - - var serviceVMs = new List(); - foreach (var service in page) - { - serviceVMs.Add(new ServiceInfoVM() - { - Id = service.Id, - Status = service.Status, - ServiceId = service.ServiceId, - ServiceName = service.ServiceName, - Ip = service.Ip, - Port = service.Port, - LastHeartBeat = service.LastHeartBeat, - MetaData = service.MetaData, - RegisterTime = service.RegisterTime, - HeartBeatMode = service.HeartBeatMode, - CheckUrl = service.CheckUrl, - AlarmUrl = service.AlarmUrl - }); - } + var count = query.Count; + var page = query.Skip((current - 1) * pageSize).Take(pageSize).ToList(); - return Json(new + var serviceVMs = new List(); + foreach (var service in page) + serviceVMs.Add(new ServiceInfoVM { - current, - pageSize, - success = true, - total = count, - data = serviceVMs + Id = service.Id, + Status = service.Status, + ServiceId = service.ServiceId, + ServiceName = service.ServiceName, + Ip = service.Ip, + Port = service.Port, + LastHeartBeat = service.LastHeartBeat, + MetaData = service.MetaData, + RegisterTime = service.RegisterTime, + HeartBeatMode = service.HeartBeatMode, + CheckUrl = service.CheckUrl, + AlarmUrl = service.AlarmUrl }); - } + + return Json(new + { + current, + pageSize, + success = true, + total = count, + data = serviceVMs + }); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/SysLogController.cs b/src/AgileConfig.Server.Apisite/Controllers/SysLogController.cs index ea178dc4..c484ba56 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/SysLogController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/SysLogController.cs @@ -5,44 +5,39 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace AgileConfig.Server.Apisite.Controllers +namespace AgileConfig.Server.Apisite.Controllers; + +[Authorize] +public class SysLogController : Controller { - [Authorize] - public class SysLogController : Controller + private readonly ISysLogService _sysLogService; + + public SysLogController(ISysLogService sysLogService) + { + _sysLogService = sysLogService; + } + + [HttpGet] + public async Task Search(string appId, SysLogType? logType, DateTime? startTime, DateTime? endTime, + int current = 1, int pageSize = 20) { - private readonly ISysLogService _sysLogService; + if (current <= 0) throw new ArgumentException("current can not less than 1 ."); + if (pageSize <= 0) throw new ArgumentException("pageSize can not less than 1 ."); - public SysLogController(ISysLogService sysLogService) + var pageList = + await _sysLogService.SearchPage(appId, logType, startTime, endTime?.Date.AddDays(1), pageSize, current); + var total = await _sysLogService.Count(appId, logType, startTime, endTime?.Date.AddDays(1)); + if (total % pageSize > 0) { - _sysLogService = sysLogService; } - [HttpGet] - public async Task Search(string appId, SysLogType? logType, DateTime? startTime, DateTime? endTime, int current = 1, int pageSize = 20) + return Json(new { - if (current <= 0) - { - throw new ArgumentException("current can not less than 1 ."); - } - if (pageSize <= 0) - { - throw new ArgumentException("pageSize can not less than 1 ."); - } - - var pageList = await _sysLogService.SearchPage(appId, logType, startTime, endTime?.Date.AddDays(1), pageSize, current); - var total = await _sysLogService.Count(appId, logType, startTime, endTime?.Date.AddDays(1)); - if ((total % pageSize) > 0) - { - } - - return Json(new - { - current, - pageSize, - success = true, - total = total, - data = pageList - }); - } + current, + pageSize, + success = true, + total, + data = pageList + }); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/UserController.cs b/src/AgileConfig.Server.Apisite/Controllers/UserController.cs index 43891047..976c5dae 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/UserController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/UserController.cs @@ -1,282 +1,234 @@ -using System; +using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using AgileConfig.Server.Apisite.Filters; -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.IService; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using System.Linq; using AgileConfig.Server.Apisite.Models; -using AgileConfig.Server.Common; -using System.Collections.Generic; using AgileConfig.Server.Apisite.Utilites; +using AgileConfig.Server.Common; using AgileConfig.Server.Common.EventBus; -using AgileConfig.Server.Event; using AgileConfig.Server.Common.Resources; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Event; +using AgileConfig.Server.IService; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; -namespace AgileConfig.Server.Apisite.Controllers +namespace AgileConfig.Server.Apisite.Controllers; + +[Authorize] +public class UserController : Controller { - [Authorize] - public class UserController : Controller + private const string DefaultPassword = "123456"; + private readonly ITinyEventBus _tinyEventBus; + private readonly IUserService _userService; + + public UserController(IUserService userService, + ITinyEventBus tinyEventBus + ) + { + _userService = userService; + _tinyEventBus = tinyEventBus; + } + + [HttpGet] + public async Task Search(string userName, string team, int current = 1, int pageSize = 20) { - private readonly IUserService _userService; - private readonly ITinyEventBus _tinyEventBus; + if (current <= 0) throw new ArgumentException(Messages.CurrentCannotBeLessThanOneUser); + if (pageSize <= 0) throw new ArgumentException(Messages.PageSizeCannotBeLessThanOneUser); - public UserController(IUserService userService, - ITinyEventBus tinyEventBus - ) + var users = await _userService.GetAll(); + users = users.Where(x => x.Status == UserStatus.Normal && x.Id != SystemSettings.SuperAdminId).ToList(); + if (!string.IsNullOrEmpty(userName)) + users = users.Where(x => x.UserName != null && x.UserName.Contains(userName)).ToList(); + if (!string.IsNullOrEmpty(team)) users = users.Where(x => x.Team != null && x.Team.Contains(team)).ToList(); + users = users.OrderByDescending(x => x.CreateTime).ToList(); + + var pageList = users.Skip((current - 1) * pageSize).Take(pageSize); + var total = users.Count; + + var vms = new List(); + foreach (var item in pageList) { - _userService = userService; - _tinyEventBus = tinyEventBus; + var roles = await _userService.GetUserRolesAsync(item.Id); + var vm = new UserVM + { + Id = item.Id, + UserName = item.UserName, + Team = item.Team, + UserRoleIds = roles.Select(r => r.Id).ToList(), + UserRoleNames = roles.Select(r => r.Name).ToList() + }; + vms.Add(vm); } - [HttpGet] - public async Task Search(string userName, string team, int current = 1, int pageSize = 20) + return Json(new { - if (current <= 0) - { - throw new ArgumentException(Messages.CurrentCannotBeLessThanOneUser); - } - if (pageSize <= 0) - { - throw new ArgumentException(Messages.PageSizeCannotBeLessThanOneUser); - } - - var users = await _userService.GetAll(); - users = users.Where(x => x.Status == UserStatus.Normal && x.Id != SystemSettings.SuperAdminId).ToList(); - if (!string.IsNullOrEmpty(userName)) - { - users = users.Where(x => x.UserName != null && x.UserName.Contains(userName)).ToList(); - } - if (!string.IsNullOrEmpty(team)) - { - users = users.Where(x => x.Team != null && x.Team.Contains(team)).ToList(); - } - users = users.OrderByDescending(x => x.CreateTime).ToList(); - - var pageList = users.Skip((current - 1) * pageSize).Take(pageSize); - var total = users.Count; + current, + pageSize, + success = true, + total, + data = vms + }); + } - var vms = new List(); - foreach (var item in pageList) - { - var roles = await _userService.GetUserRolesAsync(item.Id); - var vm = new UserVM - { - Id = item.Id, - UserName = item.UserName, - Team = item.Team, - UserRoleIds = roles.Select(r => r.Id).ToList(), - UserRoleNames = roles.Select(r => r.Name).ToList(), - UserRoleCodes = roles.Select(r => r.Code).ToList() - }; - vms.Add(vm); - } + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "User.Add", Functions.User_Add })] + [HttpPost] + public async Task Add([FromBody] UserVM model) + { + if (model == null) throw new ArgumentNullException(nameof(model)); + var oldUsers = await _userService.GetUsersByNameAsync(model.UserName); + if (oldUsers.Any(x => x.Status == UserStatus.Normal)) return Json(new { - current, - pageSize, - success = true, - total = total, - data = vms + success = false, + message = Messages.UserAlreadyExists(model.UserName) }); - } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "User.Add", Functions.User_Add })] - [HttpPost] - public async Task Add([FromBody] UserVM model) - { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - var oldUsers = await _userService.GetUsersByNameAsync(model.UserName); - if (oldUsers.Any(x=>x.Status == UserStatus.Normal)) - { - return Json(new - { - success = false, - message = Messages.UserAlreadyExists(model.UserName) - }); - } - - var user = new User(); - user.Id = Guid.NewGuid().ToString("N"); - var salt = Guid.NewGuid().ToString("N"); - user.Salt = salt; - user.Password = Encrypt.Md5(model.Password + salt); - user.Status = UserStatus.Normal; - user.Team = model.Team; - user.CreateTime = DateTime.Now; - user.UserName = model.UserName; - - var addUserResult = await _userService.AddAsync(user); - var roleIds = model.UserRoleIds?.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList() ?? new List(); - if (!roleIds.Any()) - { - roleIds.Add(SystemRoleConstants.OperatorId); - } + var user = new User(); + user.Id = Guid.NewGuid().ToString("N"); + var salt = Guid.NewGuid().ToString("N"); + user.Salt = salt; + user.Password = Encrypt.Md5(model.Password + salt); + user.Status = UserStatus.Normal; + user.Team = model.Team; + user.CreateTime = DateTime.Now; + user.UserName = model.UserName; - var addUserRoleResult = await _userService.UpdateUserRolesAsync(user.Id, roleIds); + var addUserResult = await _userService.AddAsync(user); + var roleIds = model.UserRoleIds?.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList() ?? + new List(); + if (!roleIds.Any()) roleIds.Add(SystemRoleConstants.OperatorId); - if (addUserResult) - { - _tinyEventBus.Fire(new AddUserSuccessful(user, this.GetCurrentUserName())); - } + var addUserRoleResult = await _userService.UpdateUserRolesAsync(user.Id, roleIds); - return Json(new - { - success = addUserResult && addUserRoleResult, - message = !(addUserResult && addUserRoleResult) ? Messages.AddUserFailed : "" - }); - } + if (addUserResult) _tinyEventBus.Fire(new AddUserSuccessful(user, this.GetCurrentUserName())); - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "User.Edit", Functions.User_Edit })] - [HttpPost] - public async Task Edit([FromBody] UserVM model) + return Json(new { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - var user = await _userService.GetUserAsync(model.Id); - if (user == null) - { - return Json(new - { - success = false, - message = Messages.UserNotFoundForOperation - }); - } - - user.Team = model.Team; - user.UpdateTime = DateTime.Now; - - var result = await _userService.UpdateAsync(user); - var roleIds = model.UserRoleIds?.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList() ?? new List(); - if (!roleIds.Any()) - { - roleIds.Add(SystemRoleConstants.OperatorId); - } - - var reuslt1 = await _userService.UpdateUserRolesAsync(user.Id, roleIds); + success = addUserResult && addUserRoleResult, + message = !(addUserResult && addUserRoleResult) ? Messages.AddUserFailed : "" + }); + } - if (result) - { - _tinyEventBus.Fire(new EditUserSuccessful(user, this.GetCurrentUserName())); - } + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "User.Edit", Functions.User_Edit })] + [HttpPost] + public async Task Edit([FromBody] UserVM model) + { + if (model == null) throw new ArgumentNullException(nameof(model)); + var user = await _userService.GetUserAsync(model.Id); + if (user == null) return Json(new { - success = result && reuslt1, - message = !(result && reuslt1) ? Messages.UpdateUserFailed : "" + success = false, + message = Messages.UserNotFoundForOperation }); - } - const string DefaultPassword = "123456"; + user.Team = model.Team; + user.UpdateTime = DateTime.Now; - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "User.ResetPassword", Functions.User_Edit })] - [HttpPost] - public async Task ResetPassword(string userId) - { - if (string.IsNullOrEmpty(userId)) - { - throw new ArgumentNullException("userId"); - } + var result = await _userService.UpdateAsync(user); + var roleIds = model.UserRoleIds?.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList() ?? + new List(); + if (!roleIds.Any()) roleIds.Add(SystemRoleConstants.OperatorId); - var user = await _userService.GetUserAsync(userId); - if (user == null) - { - return Json(new - { - success = false, - message = Messages.UserNotFoundForOperation - }); - } + var reuslt1 = await _userService.UpdateUserRolesAsync(user.Id, roleIds); - user.Password = Encrypt.Md5(DefaultPassword + user.Salt); + if (result) _tinyEventBus.Fire(new EditUserSuccessful(user, this.GetCurrentUserName())); - var result = await _userService.UpdateAsync(user); - if (result) - { - _tinyEventBus.Fire(new ResetUserPasswordSuccessful(this.GetCurrentUserName(), user.UserName)); - } + return Json(new + { + success = result && reuslt1, + message = !(result && reuslt1) ? Messages.UpdateUserFailed : "" + }); + } + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { "User.ResetPassword", Functions.User_Edit })] + [HttpPost] + public async Task ResetPassword(string userId) + { + if (string.IsNullOrEmpty(userId)) throw new ArgumentNullException("userId"); + + var user = await _userService.GetUserAsync(userId); + if (user == null) return Json(new { - success = result, - message = !result ? Messages.ResetUserPasswordFailed : "" + success = false, + message = Messages.UserNotFoundForOperation }); - } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "User.Delete", Functions.User_Delete })] - [HttpPost] - public async Task Delete(string userId) + user.Password = Encrypt.Md5(DefaultPassword + user.Salt); + + var result = await _userService.UpdateAsync(user); + if (result) _tinyEventBus.Fire(new ResetUserPasswordSuccessful(this.GetCurrentUserName(), user.UserName)); + + return Json(new { - if (string.IsNullOrEmpty(userId)) - { - throw new ArgumentNullException(nameof(userId)); - } + success = result, + message = !result ? Messages.ResetUserPasswordFailed : "" + }); + } - var user = await _userService.GetUserAsync(userId); - if (user == null) - { - return Json(new - { - success = false, - message = Messages.UserNotFoundForOperation - }); - } - - user.Status = UserStatus.Deleted; - var result = await _userService.UpdateAsync(user); - if (result) - { - _tinyEventBus.Fire(new DeleteUserSuccessful(user, this.GetCurrentUserName())); - } + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "User.Delete", Functions.User_Delete })] + [HttpPost] + public async Task Delete(string userId) + { + if (string.IsNullOrEmpty(userId)) throw new ArgumentNullException(nameof(userId)); + var user = await _userService.GetUserAsync(userId); + if (user == null) return Json(new { - success = result, - message = !result ? Messages.DeleteUserFailed : "" + success = false, + message = Messages.UserNotFoundForOperation }); - } - [HttpGet] - public async Task AdminUsers() + user.Status = UserStatus.Deleted; + var result = await _userService.UpdateAsync(user); + if (result) _tinyEventBus.Fire(new DeleteUserSuccessful(user, this.GetCurrentUserName())); + + return Json(new { - var adminUsers = await _userService.GetUsersByRoleAsync(SystemRoleConstants.AdminId); - adminUsers = adminUsers.Where(x => x.Status == UserStatus.Normal).ToList(); - return Json(new - { - success = true, - data = adminUsers.OrderBy(x=>x.Team).ThenBy(x => x.UserName).Select(x=> new UserVM { - Id = x.Id, - UserName = x.UserName, - Team = x.Team - }) - }); - } + success = result, + message = !result ? Messages.DeleteUserFailed : "" + }); + } - [HttpGet] - public async Task AllUsers() + [HttpGet] + public async Task AdminUsers() + { + var adminUsers = await _userService.GetUsersByRoleAsync(SystemRoleConstants.AdminId); + adminUsers = adminUsers.Where(x => x.Status == UserStatus.Normal).ToList(); + return Json(new { - var users = await _userService.GetAll(); - users = users.Where(x => x.Status == UserStatus.Normal && x.Id != SystemSettings.SuperAdminId).ToList(); + success = true, + data = adminUsers.OrderBy(x => x.Team).ThenBy(x => x.UserName).Select(x => new UserVM + { + Id = x.Id, + UserName = x.UserName, + Team = x.Team + }) + }); + } - return Json(new - { - success = true, - data = users.OrderBy(x => x.Team).ThenBy(x=>x.UserName).Select(x => new UserVM - { - Id = x.Id, - UserName = x.UserName, - Team = x.Team - }) - }); - } + [HttpGet] + public async Task AllUsers() + { + var users = await _userService.GetAll(); + users = users.Where(x => x.Status == UserStatus.Normal && x.Id != SystemSettings.SuperAdminId).ToList(); + + return Json(new + { + success = true, + data = users.OrderBy(x => x.Team).ThenBy(x => x.UserName).Select(x => new UserVM + { + Id = x.Id, + UserName = x.UserName, + Team = x.Team + }) + }); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/AppController.cs b/src/AgileConfig.Server.Apisite/Controllers/api/AppController.cs index 881b8dc1..25acb1e7 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/AppController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/AppController.cs @@ -1,288 +1,268 @@ -using AgileConfig.Server.Apisite.Controllers.api.Models; -using AgileConfig.Server.Apisite.Filters; -using AgileConfig.Server.Apisite.Models; -using AgileConfig.Server.IService; -using Microsoft.AspNetCore.Mvc; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using AgileConfig.Server.Apisite.Controllers.api.Models; +using AgileConfig.Server.Apisite.Filters; +using AgileConfig.Server.Apisite.Models; using AgileConfig.Server.Apisite.Models.Mapping; +using AgileConfig.Server.IService; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Infrastructure; -namespace AgileConfig.Server.Apisite.Controllers.api +namespace AgileConfig.Server.Apisite.Controllers.api; + +/// +/// Application management API. +/// +[TypeFilter(typeof(AdmBasicAuthenticationAttribute))] +[Route("api/[controller]")] +public class AppController : Controller { + private readonly Controllers.AppController _appController; + private readonly IAppService _appService; + private readonly Controllers.ConfigController _configController; + private readonly IConfigService _configService; + + public AppController(IAppService appService, + IConfigService configService, + Controllers.AppController appController, + Controllers.ConfigController configController) + { + _appService = appService; + _configService = configService; + + _appController = appController; + _configController = configController; + } + /// - /// Application management API. + /// Get all applications. /// - [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] - [Route("api/[controller]")] - public class AppController : Controller + /// + [HttpGet] + public async Task>> GetAll() { - private readonly IConfigService _configService; - private readonly IAppService _appService; + var apps = await _appService.GetAllAppsAsync(); + var vms = apps.Select(x => x.ToApiAppVM()); - private readonly Controllers.AppController _appController; - private readonly Controllers.ConfigController _configController; + return Json(vms); + } - public AppController(IAppService appService, - IConfigService configService, - Controllers.AppController appController, - Controllers.ConfigController configController) - { - _appService = appService; - _configService = configService; + /// + /// Get an application by its identifier. + /// + /// Application ID. + /// + [HttpGet("{id}")] + public async Task> GetById(string id) + { + var actionResult = await _appController.Get(id); + var status = actionResult as IStatusCodeActionResult; - _appController = appController; - _configController = configController; - } + var result = actionResult as JsonResult; + dynamic obj = result?.Value; - /// - /// Get all applications. - /// - /// - [HttpGet] - public async Task>> GetAll() + if (obj?.success ?? false) { - var apps = await _appService.GetAllAppsAsync(); - var vms = apps.Select(x => x.ToApiAppVM()); - - return Json(vms); + AppVM appVM = obj.data; + return Json(appVM.ToApiAppVM()); } - /// - /// Get an application by its identifier. - /// - /// Application ID. - /// - [HttpGet("{id}")] - public async Task> GetById(string id) + Response.StatusCode = status.StatusCode.Value; + return Json(new { - var actionResult = await _appController.Get(id); - var status = actionResult as IStatusCodeActionResult; - - var result = actionResult as JsonResult; - dynamic obj = result?.Value; + obj?.message + }); + } - if (obj?.success ?? false) - { - AppVM appVM = obj.data; - return Json(appVM.ToApiAppVM()); - } + /// + /// Create a new application. + /// + /// Application payload. + /// + [ProducesResponseType(201)] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "App.Add", Functions.App_Add })] + [HttpPost] + public async Task Add([FromBody] ApiAppVM model) + { + var requiredResult = CheckRequired(model); - Response.StatusCode = status.StatusCode.Value; + if (!requiredResult.Item1) + { + Response.StatusCode = 400; return Json(new { - obj?.message + message = requiredResult.Item2 }); } - /// - /// Create a new application. - /// - /// Application payload. - /// - [ProducesResponseType(201)] - [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "App.Add", Functions.App_Add })] - [HttpPost] - public async Task Add([FromBody] ApiAppVM model) - { - var requiredResult = CheckRequired(model); + _appController.ControllerContext.HttpContext = HttpContext; - if (!requiredResult.Item1) - { - Response.StatusCode = 400; - return Json(new - { - message = requiredResult.Item2 - }); - } + var result = await _appController.Add(model.ToAppVM()) as JsonResult; - _appController.ControllerContext.HttpContext = HttpContext; + dynamic obj = result?.Value; - var result = (await _appController.Add(model.ToAppVM())) as JsonResult; + if (obj?.success == true) return Created("/api/app/" + obj.data.Id, ""); - dynamic obj = result?.Value; + Response.StatusCode = 400; + return Json(new + { + obj?.message + }); + } - if (obj?.success == true) - { - return Created("/api/app/" + obj.data.Id, ""); - } + /// + /// Update an existing application. + /// + /// Application ID. + /// Application payload. + /// + [ProducesResponseType(200)] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "App.Edit", Functions.App_Edit })] + [HttpPut("{id}")] + public async Task Edit(string id, [FromBody] ApiAppVM model) + { + var requiredResult = CheckRequired(model); + if (!requiredResult.Item1) + { Response.StatusCode = 400; return Json(new { - obj?.message + message = requiredResult.Item2 }); } - /// - /// Update an existing application. - /// - /// Application ID. - /// Application payload. - /// - [ProducesResponseType(200)] - [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "App.Edit", Functions.App_Edit })] - [HttpPut("{id}")] - public async Task Edit(string id, [FromBody] ApiAppVM model) - { - var requiredResult = CheckRequired(model); + _appController.ControllerContext.HttpContext = HttpContext; - if (!requiredResult.Item1) - { - Response.StatusCode = 400; - return Json(new - { - message = requiredResult.Item2 - }); - } - - _appController.ControllerContext.HttpContext = HttpContext; - - model.Id = id; - var actionResult = await _appController.Edit(model.ToAppVM()); - var status = actionResult as IStatusCodeActionResult; - var result = actionResult as JsonResult; - - dynamic obj = result?.Value; - if (obj?.success ?? false) - { - return Ok(); - } + model.Id = id; + var actionResult = await _appController.Edit(model.ToAppVM()); + var status = actionResult as IStatusCodeActionResult; + var result = actionResult as JsonResult; - Response.StatusCode = status.StatusCode.Value; - return Json(new - { - obj?.message - }); - } + dynamic obj = result?.Value; + if (obj?.success ?? false) return Ok(); - /// - /// Delete an application. - /// - /// Application ID. - /// - [ProducesResponseType(204)] - [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "App.Delete", Functions.App_Delete })] - [HttpDelete("{id}")] - public async Task Delete(string id) + Response.StatusCode = status.StatusCode.Value; + return Json(new { - _appController.ControllerContext.HttpContext = HttpContext; + obj?.message + }); + } - var actionResult = await _appController.Delete(id); - var status = actionResult as IStatusCodeActionResult; - var result = actionResult as JsonResult; + /// + /// Delete an application. + /// + /// Application ID. + /// + [ProducesResponseType(204)] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), + Arguments = new object[] { "App.Delete", Functions.App_Delete })] + [HttpDelete("{id}")] + public async Task Delete(string id) + { + _appController.ControllerContext.HttpContext = HttpContext; - dynamic obj = result?.Value; - if (obj?.success ?? false) - { - return NoContent(); - } + var actionResult = await _appController.Delete(id); + var status = actionResult as IStatusCodeActionResult; + var result = actionResult as JsonResult; - Response.StatusCode = status.StatusCode.Value; - return Json(new - { - obj?.message - }); - } + dynamic obj = result?.Value; + if (obj?.success ?? false) return NoContent(); - /// - /// Publish pending configuration items of an application. - /// - /// Application ID. - /// Target environment. - /// - [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] - [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "Config.Publish_API", Functions.Config_Publish })] - [HttpPost("publish")] - public async Task Publish(string appId, EnvString env) + Response.StatusCode = status.StatusCode.Value; + return Json(new { - _configController.ControllerContext.HttpContext = HttpContext; + obj?.message + }); + } - var actionResult = await _configController.Publish(new PublishLogVM() - { - AppId = appId - }, env); - var status = actionResult as IStatusCodeActionResult; - var result = actionResult as JsonResult; + /// + /// Publish pending configuration items of an application. + /// + /// Application ID. + /// Target environment. + /// + [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), + Arguments = new object[] { "Config.Publish_API", Functions.Config_Publish })] + [HttpPost("publish")] + public async Task Publish(string appId, EnvString env) + { + _configController.ControllerContext.HttpContext = HttpContext; - dynamic obj = result?.Value; - if (obj?.success ?? false) - { - return Ok(); - } + var actionResult = await _configController.Publish(new PublishLogVM + { + AppId = appId + }, env); + var status = actionResult as IStatusCodeActionResult; + var result = actionResult as JsonResult; - Response.StatusCode = status.StatusCode.Value; - return Json(new - { - obj?.message - }); - } + dynamic obj = result?.Value; + if (obj?.success ?? false) return Ok(); - /// - /// Retrieve the publish history of an application. - /// - /// Application ID. - /// Target environment. - /// - [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] - [HttpGet("Publish_History")] - public async Task>> PublishHistory(string appId, EnvString env) + Response.StatusCode = status.StatusCode.Value; + return Json(new { - ArgumentException.ThrowIfNullOrEmpty(appId); + obj?.message + }); + } - var history = await _configService.GetPublishTimelineHistoryAsync(appId, env.Value); + /// + /// Retrieve the publish history of an application. + /// + /// Application ID. + /// Target environment. + /// + [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] + [HttpGet("Publish_History")] + public async Task>> PublishHistory(string appId, EnvString env) + { + ArgumentException.ThrowIfNullOrEmpty(appId); - history = history.OrderByDescending(x => x.Version).ToList(); + var history = await _configService.GetPublishTimelineHistoryAsync(appId, env.Value); - var vms = history.Select(x => x.ToApiPublishTimelimeVM()); + history = history.OrderByDescending(x => x.Version).ToList(); - return Json(vms); - } + var vms = history.Select(x => x.ToApiPublishTimelimeVM()); - /// - /// Roll back the application to the configuration at the specified publish history entry. - /// - /// Publish history identifier. - /// Target environment. - /// - [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] - [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "Config.Rollback_API", Functions.Config_Publish })] - [HttpPost("rollback")] - public async Task Rollback(string historyId, EnvString env) - { - _configController.ControllerContext.HttpContext = HttpContext; + return Json(vms); + } - var actionResult = await _configController.Rollback(historyId, env); - var status = actionResult as IStatusCodeActionResult; - var result = actionResult as JsonResult; + /// + /// Roll back the application to the configuration at the specified publish history entry. + /// + /// Publish history identifier. + /// Target environment. + /// + [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), + Arguments = new object[] { "Config.Rollback_API", Functions.Config_Publish })] + [HttpPost("rollback")] + public async Task Rollback(string historyId, EnvString env) + { + _configController.ControllerContext.HttpContext = HttpContext; - dynamic obj = result?.Value; - if (obj?.success ?? false) - { - return Ok(); - } + var actionResult = await _configController.Rollback(historyId, env); + var status = actionResult as IStatusCodeActionResult; + var result = actionResult as JsonResult; - Response.StatusCode = status.StatusCode.Value; - return Json(new - { - obj?.message - }); - } + dynamic obj = result?.Value; + if (obj?.success ?? false) return Ok(); - private (bool, string) CheckRequired(ApiAppVM model) + Response.StatusCode = status.StatusCode.Value; + return Json(new { - if (string.IsNullOrEmpty(model.Id)) - { - return (false, "Id is required"); - } - if (string.IsNullOrEmpty(model.Name)) - { - return (false, "Name is required"); - } + obj?.message + }); + } - return (true, ""); - } + private (bool, string) CheckRequired(ApiAppVM model) + { + if (string.IsNullOrEmpty(model.Id)) return (false, "Id is required"); + if (string.IsNullOrEmpty(model.Name)) return (false, "Name is required"); + + return (true, ""); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/ConfigController.cs b/src/AgileConfig.Server.Apisite/Controllers/api/ConfigController.cs index 088da988..c16c68b4 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/ConfigController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/ConfigController.cs @@ -15,241 +15,222 @@ // For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 -namespace AgileConfig.Server.Apisite.Controllers.api +namespace AgileConfig.Server.Apisite.Controllers.api; + +[Route("api/[controller]")] +public class ConfigController : Controller { - [Route("api/[controller]")] - public class ConfigController : Controller + private readonly IAppService _appService; + private readonly IMemoryCache _cacheMemory; + private readonly Controllers.ConfigController _configController; + private readonly IConfigService _configService; + private readonly IMeterService _meterService; + + public ConfigController( + IConfigService configService, + IAppService appService, + IMemoryCache cacheMemory, + IMeterService meterService, + Controllers.ConfigController configController + ) { - private readonly IConfigService _configService; - private readonly IAppService _appService; - private readonly IMemoryCache _cacheMemory; - private readonly IMeterService _meterService; - private readonly Controllers.ConfigController _configController; - - public ConfigController( - IConfigService configService, - IAppService appService, - IMemoryCache cacheMemory, - IMeterService meterService, - Controllers.ConfigController configController - ) - { - _configService = configService; - _appService = appService; - _cacheMemory = cacheMemory; - _meterService = meterService; - _configController = configController; - } - - /// - /// Retrieve all published configuration items for an application, including inherited entries. - /// Note: this endpoint authenticates with appId and secret instead of username and password. - /// - /// Application ID. - /// Target environment. - /// - [TypeFilter(typeof(AppBasicAuthenticationAttribute))] - [HttpGet("app/{appId}")] - public async Task>> GetAppConfig(string appId, [FromQuery] EnvString env) - { - ArgumentException.ThrowIfNullOrEmpty(appId); - - var app = await _appService.GetAsync(appId); - if (!app.Enabled) - { - return NotFound(); - } + _configService = configService; + _appService = appService; + _cacheMemory = cacheMemory; + _meterService = meterService; + _configController = configController; + } - var cacheKey = $"ConfigController_AppConfig_{appId}_{env.Value}"; - AppConfigsCache cache = null; - _cacheMemory?.TryGetValue(cacheKey, out cache); + /// + /// Retrieve all published configuration items for an application, including inherited entries. + /// Note: this endpoint authenticates with appId and secret instead of username and password. + /// + /// Application ID. + /// Target environment. + /// + [TypeFilter(typeof(AppBasicAuthenticationAttribute))] + [HttpGet("app/{appId}")] + public async Task>> GetAppConfig(string appId, [FromQuery] EnvString env) + { + ArgumentException.ThrowIfNullOrEmpty(appId); - if (cache == null) - { - cache = new AppConfigsCache(); + var app = await _appService.GetAsync(appId); + if (!app.Enabled) return NotFound(); - var publishTimelineId = await _configService.GetLastPublishTimelineVirtualIdAsync(appId, env.Value); - var appConfigs = await _configService.GetPublishedConfigsByAppIdWithInheritance(appId, env.Value); - var vms = appConfigs.Select(x => x.ToApiConfigVM()).ToList(); + var cacheKey = $"ConfigController_AppConfig_{appId}_{env.Value}"; + AppConfigsCache cache = null; + _cacheMemory?.TryGetValue(cacheKey, out cache); - cache.Key = cacheKey; - cache.Configs = vms; - cache.VirtualId = publishTimelineId; + if (cache == null) + { + cache = new AppConfigsCache(); - //cache 5 seconds to avoid too many db query - var cacheOp = new MemoryCacheEntryOptions() - .SetAbsoluteExpiration(TimeSpan.FromSeconds(5)); - _cacheMemory?.Set(cacheKey, cache, cacheOp); - } - - Response?.Headers?.Append("publish-time-line-id", cache.VirtualId); + var publishTimelineId = await _configService.GetLastPublishTimelineVirtualIdAsync(appId, env.Value); + var appConfigs = await _configService.GetPublishedConfigsByAppIdWithInheritance(appId, env.Value); + var vms = appConfigs.Select(x => x.ToApiConfigVM()).ToList(); - _meterService.PullAppConfigCounter?.Add(1, new("appId", appId), new("env", env)); + cache.Key = cacheKey; + cache.Configs = vms; + cache.VirtualId = publishTimelineId; - return cache.Configs; + //cache 5 seconds to avoid too many db query + var cacheOp = new MemoryCacheEntryOptions() + .SetAbsoluteExpiration(TimeSpan.FromSeconds(5)); + _cacheMemory?.Set(cacheKey, cache, cacheOp); } - /// - /// Retrieve configuration items for an application, which may include unpublished items. - /// - /// Application ID. - /// Target environment. - /// - [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] - [HttpGet()] - public async Task>> GetConfigs(string appId, EnvString env) - { - ArgumentException.ThrowIfNullOrEmpty(appId); - - var configs = await _configService.GetByAppIdAsync(appId, env.Value); + Response?.Headers?.Append("publish-time-line-id", cache.VirtualId); - return configs.Select(x => x.ToApiConfigVM()).ToList(); - } + _meterService.PullAppConfigCounter?.Add(1, new KeyValuePair("appId", appId), + new KeyValuePair("env", env)); - /// - /// Get configuration details by identifier. - /// - /// Configuration identifier. - /// Target environment. - /// - [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] - [HttpGet("{id}")] - public async Task> GetConfig(string id, EnvString env) - { - ArgumentException.ThrowIfNullOrEmpty(id); + return cache.Configs; + } - var config = await _configService.GetAsync(id, env.Value); - if (config == null || config.Status == ConfigStatus.Deleted) - { - return NotFound(); - } + /// + /// Retrieve configuration items for an application, which may include unpublished items. + /// + /// Application ID. + /// Target environment. + /// + [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] + [HttpGet] + public async Task>> GetConfigs(string appId, EnvString env) + { + ArgumentException.ThrowIfNullOrEmpty(appId); - return config.ToApiConfigVM(); - } + var configs = await _configService.GetByAppIdAsync(appId, env.Value); - /// - /// Create a configuration item. - /// - /// Configuration payload. - /// Target environment. - /// - [ProducesResponseType(201)] - [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] - [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "Config.Add", Functions.Config_Add })] - [HttpPost] - public async Task Add([FromBody] ApiConfigVM model, EnvString env) - { - var requiredResult = CheckRequired(model); - - if (!requiredResult.Item1) - { - Response.StatusCode = 400; - return Json(new - { - message = requiredResult.Item2 - }); - } + return configs.Select(x => x.ToApiConfigVM()).ToList(); + } - _configController.ControllerContext.HttpContext = HttpContext; + /// + /// Get configuration details by identifier. + /// + /// Configuration identifier. + /// Target environment. + /// + [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] + [HttpGet("{id}")] + public async Task> GetConfig(string id, EnvString env) + { + ArgumentException.ThrowIfNullOrEmpty(id); - var result = (await _configController.Add(model.ToConfigVM(), env)) as JsonResult; + var config = await _configService.GetAsync(id, env.Value); + if (config == null || config.Status == ConfigStatus.Deleted) return NotFound(); - dynamic obj = result?.Value; + return config.ToApiConfigVM(); + } - if (obj?.success == true) - { - return Created("/api/config/" + obj.data.Id, ""); - } + /// + /// Create a configuration item. + /// + /// Configuration payload. + /// Target environment. + /// + [ProducesResponseType(201)] + [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), + Arguments = new object[] { "Config.Add", Functions.Config_Add })] + [HttpPost] + public async Task Add([FromBody] ApiConfigVM model, EnvString env) + { + var requiredResult = CheckRequired(model); + if (!requiredResult.Item1) + { Response.StatusCode = 400; return Json(new { - obj?.message + message = requiredResult.Item2 }); } - /// - /// Edit a configuration item. - /// - /// Configuration identifier. - /// Configuration payload. - /// Target environment. - /// - [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] - [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "Config.Edit", Functions.Config_Edit })] - [HttpPut("{id}")] - public async Task Edit(string id, [FromBody] ApiConfigVM model, EnvString env) + _configController.ControllerContext.HttpContext = HttpContext; + + var result = await _configController.Add(model.ToConfigVM(), env) as JsonResult; + + dynamic obj = result?.Value; + + if (obj?.success == true) return Created("/api/config/" + obj.data.Id, ""); + + Response.StatusCode = 400; + return Json(new { - var requiredResult = CheckRequired(model); + obj?.message + }); + } - if (!requiredResult.Item1) - { - Response.StatusCode = 400; - return Json(new - { - message = requiredResult.Item2 - }); - } - - _configController.ControllerContext.HttpContext = HttpContext; - model.Id = id; - var result = (await _configController.Edit(model.ToConfigVM(), env)) as JsonResult; - - dynamic obj = result?.Value; - if (obj?.success == true) - { - return Ok(); - } + /// + /// Edit a configuration item. + /// + /// Configuration identifier. + /// Configuration payload. + /// Target environment. + /// + [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), + Arguments = new object[] { "Config.Edit", Functions.Config_Edit })] + [HttpPut("{id}")] + public async Task Edit(string id, [FromBody] ApiConfigVM model, EnvString env) + { + var requiredResult = CheckRequired(model); + if (!requiredResult.Item1) + { Response.StatusCode = 400; return Json(new { - obj?.message + message = requiredResult.Item2 }); } - /// - /// Delete a configuration item. - /// - /// Configuration identifier. - /// Target environment. - /// - [ProducesResponseType(204)] - [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] - [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "Config.Delete", Functions.Config_Delete })] - [HttpDelete("{id}")] - public async Task Delete(string id, EnvString env) + _configController.ControllerContext.HttpContext = HttpContext; + model.Id = id; + var result = await _configController.Edit(model.ToConfigVM(), env) as JsonResult; + + dynamic obj = result?.Value; + if (obj?.success == true) return Ok(); + + Response.StatusCode = 400; + return Json(new { - _configController.ControllerContext.HttpContext = HttpContext; + obj?.message + }); + } - var result = (await _configController.Delete(id, env)) as JsonResult; + /// + /// Delete a configuration item. + /// + /// Configuration identifier. + /// Target environment. + /// + [ProducesResponseType(204)] + [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), + Arguments = new object[] { "Config.Delete", Functions.Config_Delete })] + [HttpDelete("{id}")] + public async Task Delete(string id, EnvString env) + { + _configController.ControllerContext.HttpContext = HttpContext; - dynamic obj = result?.Value; - if (obj?.success == true) - { - return NoContent(); - } + var result = await _configController.Delete(id, env) as JsonResult; - Response.StatusCode = 400; - return Json(new - { - obj?.message - }); - } + dynamic obj = result?.Value; + if (obj?.success == true) return NoContent(); - private (bool, string) CheckRequired(ApiConfigVM model) + Response.StatusCode = 400; + return Json(new { - if (string.IsNullOrEmpty(model.Key)) - { - return (false, "Key is required"); - } - if (string.IsNullOrEmpty(model.AppId)) - { - return (false, "AppId is required"); - } + obj?.message + }); + } - return (true, ""); - } + private (bool, string) CheckRequired(ApiConfigVM model) + { + if (string.IsNullOrEmpty(model.Key)) return (false, "Key is required"); + if (string.IsNullOrEmpty(model.AppId)) return (false, "AppId is required"); + return (true, ""); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiAppVM.cs b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiAppVM.cs index cdc09b26..d467b656 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiAppVM.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiAppVM.cs @@ -1,68 +1,70 @@ -using AgileConfig.Server.Apisite.Models; -using System; +using System; using System.Collections.Generic; +using AgileConfig.Server.Apisite.Models; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Apisite.Controllers.api.Models +namespace AgileConfig.Server.Apisite.Controllers.api.Models; + +/// +/// Application model returned by the REST API. +/// +public class ApiAppVM : IAppModel { /// - /// Application model returned by the REST API. + /// Whether the application can inherit configuration. /// - public class ApiAppVM : IAppModel - { - /// - /// Whether the application can inherit configuration. - /// - public bool Inheritanced { get; set; } - /// - /// id - /// - public string Id { get; set; } - /// - /// name - /// - public string Name { get; set; } - /// - /// Application secret. - /// - public string Secret { get; set; } - /// - /// Whether the application is enabled. - /// - public bool? Enabled { get; set; } - /// - /// Applications inherited by this application. - /// - public List InheritancedApps { get; set; } - /// - /// Administrator of the application. - /// - public string AppAdmin { get; set; } - - public string Group { get; set; } - - public DateTime CreateTime { get; set; } - } + public bool Inheritanced { get; set; } + + /// + /// Application secret. + /// + public string Secret { get; set; } + + /// + /// Whether the application is enabled. + /// + public bool? Enabled { get; set; } + + /// + /// Applications inherited by this application. + /// + public List InheritancedApps { get; set; } + + /// + /// Administrator of the application. + /// + public string AppAdmin { get; set; } + + /// + /// id + /// + public string Id { get; set; } + + /// + /// name + /// + public string Name { get; set; } - public static class ApiAppVMExtension + public string Group { get; set; } + + public DateTime CreateTime { get; set; } +} + +public static class ApiAppVMExtension +{ + public static AppVM ToAppVM(this ApiAppVM vm) { - public static AppVM ToAppVM(this ApiAppVM vm) - { - if (vm == null) - { - return null; - } + if (vm == null) return null; - return new AppVM - { - Id = vm.Id, - Name = vm.Name, - Secret = vm.Secret, - AppAdmin = vm.AppAdmin, - Inheritanced = vm.Inheritanced, - Group = vm.Group, - Enabled = vm.Enabled.GetValueOrDefault() - }; - } + return new AppVM + { + Id = vm.Id, + Name = vm.Name, + Secret = vm.Secret, + AppAdmin = vm.AppAdmin, + Inheritanced = vm.Inheritanced, + Group = vm.Group, + Enabled = vm.Enabled.GetValueOrDefault() + }; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiConfigVM.cs b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiConfigVM.cs index bbefe849..3208d509 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiConfigVM.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiConfigVM.cs @@ -2,82 +2,79 @@ using AgileConfig.Server.Apisite.Models; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Apisite.Controllers.api.Models +namespace AgileConfig.Server.Apisite.Controllers.api.Models; + +public class ApiConfigVM : IAppIdModel { - public class ApiConfigVM : IAppIdModel - { - /// - /// id - /// - public string Id { get; set; } + /// + /// id + /// + public string Id { get; set; } - /// - /// Application ID. - /// - public string AppId { get; set; } + /// + /// Configuration group name. + /// + public string Group { get; set; } - /// - /// Configuration group name. - /// - public string Group { get; set; } - /// - /// Configuration key. - /// - public string Key { get; set; } - /// - /// Configuration value. - /// - public string Value { get; set; } + /// + /// Configuration key. + /// + public string Key { get; set; } - /// - /// Configuration status. - /// - public ConfigStatus Status { get; set; } + /// + /// Configuration value. + /// + public string Value { get; set; } - /// - /// Online status for the configuration. - /// - public OnlineStatus OnlineStatus { get; set; } + /// + /// Configuration status. + /// + public ConfigStatus Status { get; set; } - /// - /// Editing status for the configuration. - /// - public EditStatus EditStatus { get; set; } + /// + /// Online status for the configuration. + /// + public OnlineStatus OnlineStatus { get; set; } - /// - /// Description of the configuration. - /// - public string Description { get; set; } - } + /// + /// Editing status for the configuration. + /// + public EditStatus EditStatus { get; set; } + /// + /// Description of the configuration. + /// + public string Description { get; set; } - public class AppConfigsCache - { - public string Key { get; set; } + /// + /// Application ID. + /// + public string AppId { get; set; } +} - public string VirtualId { get; set; } +public class AppConfigsCache +{ + public string Key { get; set; } - public List Configs { get; set; } - } + public string VirtualId { get; set; } - public static class ApiConfigVMExtension + public List Configs { get; set; } +} + +public static class ApiConfigVMExtension +{ + public static ConfigVM ToConfigVM(this ApiConfigVM model) { - public static ConfigVM ToConfigVM(this ApiConfigVM model) - { - if (model is null) - { - return null; - } + if (model is null) return null; - return new ConfigVM() - { - Id = model.Id, - AppId = model.AppId, - Group = model.Group, - Key = model.Key, - Value = model.Value, - Description = model.Description - }; - } + return new ConfigVM + { + Id = model.Id, + AppId = model.AppId, + Group = model.Group, + Key = model.Key, + Value = model.Value, + Description = model.Description + }; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiNodeVM.cs b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiNodeVM.cs index 952f8151..64f659c1 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiNodeVM.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiNodeVM.cs @@ -1,47 +1,46 @@ -using AgileConfig.Server.Data.Entity; -using System; +using System; using AgileConfig.Server.Apisite.Models; +using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Apisite.Controllers.api.Models +namespace AgileConfig.Server.Apisite.Controllers.api.Models; + +public class ApiNodeVM { - public class ApiNodeVM - { - /// - /// Node address. - /// - public string Address { get; set; } - /// - /// Remarks. - /// - public string Remark { get; set; } - /// - /// Node status. - /// - public NodeStatus Status { get; set; } - /// - /// Time of the last echo response. - /// - public DateTime? LastEchoTime { get; set; } - } + /// + /// Node address. + /// + public string Address { get; set; } - public static class ApiNodeVMExtension + /// + /// Remarks. + /// + public string Remark { get; set; } + + /// + /// Node status. + /// + public NodeStatus Status { get; set; } + + /// + /// Time of the last echo response. + /// + public DateTime? LastEchoTime { get; set; } +} + +public static class ApiNodeVMExtension +{ + public static ServerNodeVM ToServerNodeVM(this ApiNodeVM node) { - public static ServerNodeVM ToServerNodeVM(this ApiNodeVM node) + if (node == null) return null; + + var vm = new ServerNodeVM { - if (node == null) - { - return null; - } - - var vm = new ServerNodeVM - { - Address = node.Address, - Remark = node.Remark, - LastEchoTime = node.LastEchoTime, - Status = node.Status - }; - - return vm; - } + Address = node.Address, + Remark = node.Remark, + LastEchoTime = node.LastEchoTime, + Status = node.Status + }; + + return vm; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiPublishTimelineVM.cs b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiPublishTimelineVM.cs index 640e807d..530d295c 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiPublishTimelineVM.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiPublishTimelineVM.cs @@ -1,44 +1,41 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -namespace AgileConfig.Server.Apisite.Controllers.api.Models +namespace AgileConfig.Server.Apisite.Controllers.api.Models; + +public class ApiPublishTimelineVM { - public class ApiPublishTimelineVM - { - /// - /// Publish record identifier. - /// - public string Id { get; set; } - /// - /// Application ID. - /// - public string AppId { get; set; } - - /// - /// Publish time. - /// - public DateTime? PublishTime { get; set; } - - /// - /// Publisher. - /// - public string PublishUserId { get; set; } - - /// - /// Publish version number. - /// - public int Version { get; set; } - - /// - /// Publish log text. - /// - public string Log { get; set; } - - /// - /// Environment identifier. - /// - public string Env { get; set; } - } -} + /// + /// Publish record identifier. + /// + public string Id { get; set; } + + /// + /// Application ID. + /// + public string AppId { get; set; } + + /// + /// Publish time. + /// + public DateTime? PublishTime { get; set; } + + /// + /// Publisher. + /// + public string PublishUserId { get; set; } + + /// + /// Publish version number. + /// + public int Version { get; set; } + + /// + /// Publish log text. + /// + public string Log { get; set; } + + /// + /// Environment identifier. + /// + public string Env { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiServiceInfoVM.cs b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiServiceInfoVM.cs index 1dffe4f7..300644fa 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiServiceInfoVM.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiServiceInfoVM.cs @@ -1,25 +1,24 @@ using System.Collections.Generic; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Apisite.Controllers.api.Models +namespace AgileConfig.Server.Apisite.Controllers.api.Models; + +public class ApiServiceInfoVM { - public class ApiServiceInfoVM - { - public string ServiceId { get; set; } = ""; + public string ServiceId { get; set; } = ""; - public string ServiceName { get; set; } = ""; + public string ServiceName { get; set; } = ""; - public string Ip { get; set; } = ""; + public string Ip { get; set; } = ""; - public int? Port { get; set; } + public int? Port { get; set; } - public List MetaData { get; set; } = new List(); + public List MetaData { get; set; } = new(); - public ServiceStatus Status { get; set; } - } - - public class QueryServiceInfoResultVM - { - public List Data { get; set; } - } + public ServiceStatus Status { get; set; } } + +public class QueryServiceInfoResultVM +{ + public List Data { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/Models/HeartbeatParam.cs b/src/AgileConfig.Server.Apisite/Controllers/api/Models/HeartbeatParam.cs index e7d49bcb..81f3e186 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/Models/HeartbeatParam.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/Models/HeartbeatParam.cs @@ -1,7 +1,6 @@ -namespace AgileConfig.Server.Apisite.Controllers.api.Models +namespace AgileConfig.Server.Apisite.Controllers.api.Models; + +public class HeartbeatParam { - public class HeartbeatParam - { - public string UniqueId { get; set; } - } -} + public string UniqueId { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/Models/HeartbeatResultVM.cs b/src/AgileConfig.Server.Apisite/Controllers/api/Models/HeartbeatResultVM.cs index bf660de0..04fdf492 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/Models/HeartbeatResultVM.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/Models/HeartbeatResultVM.cs @@ -1,9 +1,7 @@ -using System.Collections.Generic; -using Agile.Config.Protocol; +using Agile.Config.Protocol; -namespace AgileConfig.Server.Apisite.Controllers.api.Models +namespace AgileConfig.Server.Apisite.Controllers.api.Models; + +public class HeartbeatResultVM : WebsocketAction { - public class HeartbeatResultVM: WebsocketAction - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/Models/RegisterResultVM.cs b/src/AgileConfig.Server.Apisite/Controllers/api/Models/RegisterResultVM.cs index 501cc6f2..7e203a33 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/Models/RegisterResultVM.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/Models/RegisterResultVM.cs @@ -1,7 +1,6 @@ -namespace AgileConfig.Server.Apisite.Controllers.api.Models +namespace AgileConfig.Server.Apisite.Controllers.api.Models; + +public class RegisterResultVM { - public class RegisterResultVM - { - public string UniqueId { get; set; } - } -} + public string UniqueId { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/Models/RegisterServiceInfoVM.cs b/src/AgileConfig.Server.Apisite/Controllers/api/Models/RegisterServiceInfoVM.cs index 3877dc1e..f53aff60 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/Models/RegisterServiceInfoVM.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/Models/RegisterServiceInfoVM.cs @@ -2,47 +2,43 @@ using AgileConfig.Server.Data.Entity; using Newtonsoft.Json; -namespace AgileConfig.Server.Apisite.Controllers.api.Models +namespace AgileConfig.Server.Apisite.Controllers.api.Models; + +public class RegisterServiceInfoVM { - public class RegisterServiceInfoVM - { - public string ServiceId { get; set; } = ""; + public string ServiceId { get; set; } = ""; - public string ServiceName { get; set; } = ""; + public string ServiceName { get; set; } = ""; - public string Ip { get; set; } = ""; + public string Ip { get; set; } = ""; - public int? Port { get; set; } + public int? Port { get; set; } - public List MetaData { get; set; } = new List(); + public List MetaData { get; set; } = new(); - public string CheckUrl { get; set; } = ""; - - public string AlarmUrl { get; set; } = ""; + public string CheckUrl { get; set; } = ""; - public string HeartBeatMode { get; set; } - } + public string AlarmUrl { get; set; } = ""; - public static class RegisterServiceInfoVMExtension + public string HeartBeatMode { get; set; } +} + +public static class RegisterServiceInfoVMExtension +{ + public static ServiceInfo ToServiceInfo(this RegisterServiceInfoVM model) { - public static ServiceInfo ToServiceInfo(this RegisterServiceInfoVM model) + if (model == null) return null; + + return new ServiceInfo { - if (model == null) - { - return null; - } - - return new ServiceInfo - { - ServiceId = model.ServiceId, - ServiceName = model.ServiceName, - Ip = model.Ip, - Port = model.Port, - MetaData = model.MetaData is null ? "[]" : JsonConvert.SerializeObject(model.MetaData), - CheckUrl = model.CheckUrl, - AlarmUrl = model.AlarmUrl, - HeartBeatMode = model.HeartBeatMode - }; - } + ServiceId = model.ServiceId, + ServiceName = model.ServiceName, + Ip = model.Ip, + Port = model.Port, + MetaData = model.MetaData is null ? "[]" : JsonConvert.SerializeObject(model.MetaData), + CheckUrl = model.CheckUrl, + AlarmUrl = model.AlarmUrl, + HeartBeatMode = model.HeartBeatMode + }; } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/NodeController.cs b/src/AgileConfig.Server.Apisite/Controllers/api/NodeController.cs index a1a80f93..4940adb8 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/NodeController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/NodeController.cs @@ -1,127 +1,118 @@ -using AgileConfig.Server.Apisite.Controllers.api.Models; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using AgileConfig.Server.Apisite.Controllers.api.Models; using AgileConfig.Server.Apisite.Filters; using AgileConfig.Server.Apisite.Models; +using AgileConfig.Server.Apisite.Models.Mapping; using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.IService; using Microsoft.AspNetCore.Mvc; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using AgileConfig.Server.Apisite.Models.Mapping; -namespace AgileConfig.Server.Apisite.Controllers.api +namespace AgileConfig.Server.Apisite.Controllers.api; + +/// +/// Node management API. +/// +[TypeFilter(typeof(AdmBasicAuthenticationAttribute))] +[Route("api/[controller]")] +public class NodeController : Controller { + private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; + private readonly IServerNodeService _serverNodeService; + private readonly ISysLogService _sysLogService; + private readonly ITinyEventBus _tinyEventBus; + + public NodeController(IServerNodeService serverNodeService, + ISysLogService sysLogService, + IRemoteServerNodeProxy remoteServerNodeProxy, + ITinyEventBus tinyEventBus + ) + { + _serverNodeService = serverNodeService; + _sysLogService = sysLogService; + _remoteServerNodeProxy = remoteServerNodeProxy; + _tinyEventBus = tinyEventBus; + } + /// - /// Node management API. + /// Get all nodes. /// - [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] - [Route("api/[controller]")] - public class NodeController : Controller + /// + [HttpGet] + public async Task>> GetAll() { - private readonly IServerNodeService _serverNodeService; - private readonly ISysLogService _sysLogService; - private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; - private readonly ITinyEventBus _tinyEventBus; - - public NodeController(IServerNodeService serverNodeService, - ISysLogService sysLogService, - IRemoteServerNodeProxy remoteServerNodeProxy, - ITinyEventBus tinyEventBus - ) - { - _serverNodeService = serverNodeService; - _sysLogService = sysLogService; - _remoteServerNodeProxy = remoteServerNodeProxy; - _tinyEventBus = tinyEventBus; - } + var nodes = await _serverNodeService.GetAllNodesAsync(); - /// - /// Get all nodes. - /// - /// - [HttpGet] - public async Task>> GetAll() - { - var nodes = await _serverNodeService.GetAllNodesAsync(); + var vms = nodes.Select(x => x.ToApiNodeVM()); - var vms = nodes.Select(x => x.ToApiNodeVM()); + return Json(vms); + } - return Json(vms); - } + /// + /// Create a node. + /// + /// Node payload. + /// + [ProducesResponseType(201)] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "Node.Add", Functions.Node_Add })] + [HttpPost] + public async Task Add([FromBody] ApiNodeVM model) + { + var requiredResult = CheckRequired(model); - /// - /// Create a node. - /// - /// Node payload. - /// - [ProducesResponseType(201)] - [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "Node.Add", Functions.Node_Add })] - [HttpPost] - public async Task Add([FromBody] ApiNodeVM model) + if (!requiredResult.Item1) { - var requiredResult = CheckRequired(model); - - if (!requiredResult.Item1) - { - Response.StatusCode = 400; - return Json(new - { - message = "Add node failed" - }); - } - - var ctrl = new ServerNodeController(_serverNodeService, _sysLogService, _remoteServerNodeProxy, _tinyEventBus); - ctrl.ControllerContext.HttpContext = HttpContext; - var result = (await ctrl.Add(model.ToServerNodeVM())) as JsonResult; - - dynamic obj = result?.Value; - if (obj?.success == true) - { - return Created("", ""); - } - Response.StatusCode = 400; return Json(new { - obj?.message + message = "Add node failed" }); } - /// - /// Delete a node by address. - /// - /// Node address. - /// - [ProducesResponseType(204)] - [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "Node.Delete", Functions.Node_Delete })] - [HttpDelete()] - public async Task Delete([FromQuery] string address) + var ctrl = new ServerNodeController(_serverNodeService, _sysLogService, _remoteServerNodeProxy, _tinyEventBus); + ctrl.ControllerContext.HttpContext = HttpContext; + var result = await ctrl.Add(model.ToServerNodeVM()) as JsonResult; + + dynamic obj = result?.Value; + if (obj?.success == true) return Created("", ""); + + Response.StatusCode = 400; + return Json(new { - var ctrl = new ServerNodeController(_serverNodeService, _sysLogService, _remoteServerNodeProxy, _tinyEventBus); - ctrl.ControllerContext.HttpContext = HttpContext; - var result = (await ctrl.Delete(new ServerNodeVM { Address = address })) as JsonResult; + obj?.message + }); + } - dynamic obj = result?.Value; - if (obj?.success == true) - { - return NoContent(); - } + /// + /// Delete a node by address. + /// + /// Node address. + /// + [ProducesResponseType(204)] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), + Arguments = new object[] { "Node.Delete", Functions.Node_Delete })] + [HttpDelete] + public async Task Delete([FromQuery] string address) + { + var ctrl = new ServerNodeController(_serverNodeService, _sysLogService, _remoteServerNodeProxy, _tinyEventBus); + ctrl.ControllerContext.HttpContext = HttpContext; + var result = await ctrl.Delete(new ServerNodeVM { Address = address }) as JsonResult; - Response.StatusCode = 400; - return Json(new - { - obj?.message - }); - } + dynamic obj = result?.Value; + if (obj?.success == true) return NoContent(); - private (bool, string) CheckRequired(ApiNodeVM model) + Response.StatusCode = 400; + return Json(new { - if (string.IsNullOrEmpty(model.Address)) - { - return (false, "Address is required"); - } + obj?.message + }); + } - return (true, ""); - } + private (bool, string) CheckRequired(ApiNodeVM model) + { + if (string.IsNullOrEmpty(model.Address)) return (false, "Address is required"); + + return (true, ""); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/RegisterCenterController.cs b/src/AgileConfig.Server.Apisite/Controllers/api/RegisterCenterController.cs index bfb66a65..10b8b22a 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/RegisterCenterController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/RegisterCenterController.cs @@ -1,154 +1,141 @@ -using AgileConfig.Server.Apisite.Controllers.api.Models; -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.IService; -using Microsoft.AspNetCore.Mvc; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Agile.Config.Protocol; +using AgileConfig.Server.Apisite.Controllers.api.Models; using AgileConfig.Server.Apisite.Models.Mapping; using AgileConfig.Server.Common.EventBus; +using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Event; +using AgileConfig.Server.IService; +using Microsoft.AspNetCore.Mvc; -namespace AgileConfig.Server.Apisite.Controllers.api +namespace AgileConfig.Server.Apisite.Controllers.api; + +/// +/// Service registration center API. +/// +[Route("api/[controller]")] +[ApiController] +public class RegisterCenterController : Controller { - /// - /// Service registration center API. - /// - [Route("api/[controller]")] - [ApiController] - public class RegisterCenterController : Controller + private readonly IRegisterCenterService _registerCenterService; + private readonly IServiceInfoService _serviceInfoService; + private readonly ITinyEventBus _tinyEventBus; + + public RegisterCenterController(IRegisterCenterService registerCenterService + , IServiceInfoService serviceInfoService, + ITinyEventBus tinyEventBus + ) { - private readonly IRegisterCenterService _registerCenterService; - private readonly IServiceInfoService _serviceInfoService; - private readonly ITinyEventBus _tinyEventBus; - - public RegisterCenterController(IRegisterCenterService registerCenterService - , IServiceInfoService serviceInfoService, - ITinyEventBus tinyEventBus - ) - { - _registerCenterService = registerCenterService; - _serviceInfoService = serviceInfoService; - _tinyEventBus = tinyEventBus; - } - - [HttpPost] - public async Task Register([FromBody] RegisterServiceInfoVM model) - { - ArgumentNullException.ThrowIfNull(model); - - var entity = model.ToServiceInfo(); - entity.RegisterWay = RegisterWay.Auto; + _registerCenterService = registerCenterService; + _serviceInfoService = serviceInfoService; + _tinyEventBus = tinyEventBus; + } - var id = await _registerCenterService.RegisterAsync(entity); + [HttpPost] + public async Task Register([FromBody] RegisterServiceInfoVM model) + { + ArgumentNullException.ThrowIfNull(model); - _tinyEventBus.Fire(new ServiceRegisteredEvent(id)); + var entity = model.ToServiceInfo(); + entity.RegisterWay = RegisterWay.Auto; - return new RegisterResultVM - { - UniqueId = id - }; - } + var id = await _registerCenterService.RegisterAsync(entity); + _tinyEventBus.Fire(new ServiceRegisteredEvent(id)); - [HttpDelete("{id}")] - public async Task> UnRegister(string id, [FromBody] RegisterServiceInfoVM vm) + return new RegisterResultVM { - var entity = await _serviceInfoService.GetByUniqueIdAsync(id); - if (entity == null) - { - return NotFound(); - } + UniqueId = id + }; + } - var result = await _registerCenterService.UnRegisterAsync(id); - if (!result) - { - if (!string.IsNullOrEmpty(vm?.ServiceId)) - { - result = await _registerCenterService.UnRegisterByServiceIdAsync(vm.ServiceId); - } - } - if (result) - { - _tinyEventBus.Fire(new ServiceUnRegisterEvent(id)); - } + [HttpDelete("{id}")] + public async Task> UnRegister(string id, [FromBody] RegisterServiceInfoVM vm) + { + var entity = await _serviceInfoService.GetByUniqueIdAsync(id); + if (entity == null) return NotFound(); - return new RegisterResultVM - { - UniqueId = id, - }; - } + var result = await _registerCenterService.UnRegisterAsync(id); + if (!result) + if (!string.IsNullOrEmpty(vm?.ServiceId)) + result = await _registerCenterService.UnRegisterByServiceIdAsync(vm.ServiceId); + + if (result) _tinyEventBus.Fire(new ServiceUnRegisterEvent(id)); - [HttpPost("heartbeat")] - public async Task> Heartbeat([FromBody] HeartbeatParam param) + return new RegisterResultVM { - ArgumentNullException.ThrowIfNull(param); + UniqueId = id + }; + } - bool serviceHeartbeatResult = false; - if (!string.IsNullOrEmpty(param.UniqueId)) - { - serviceHeartbeatResult = await _registerCenterService.ReceiveHeartbeatAsync(param.UniqueId); - } + [HttpPost("heartbeat")] + public async Task> Heartbeat([FromBody] HeartbeatParam param) + { + ArgumentNullException.ThrowIfNull(param); - if (serviceHeartbeatResult) - { - var md5 = await _serviceInfoService.ServicesMD5Cache(); - return new HeartbeatResultVM() - { - Action = ActionConst.Ping, - Data = md5, - Module = ActionModule.RegisterCenter - }; - } - - return NotFound(); - } + var serviceHeartbeatResult = false; + if (!string.IsNullOrEmpty(param.UniqueId)) + serviceHeartbeatResult = await _registerCenterService.ReceiveHeartbeatAsync(param.UniqueId); - [HttpGet("services")] - public async Task> AllServices() + if (serviceHeartbeatResult) { - var services = await _serviceInfoService.GetAllServiceInfoAsync(); - var vms = new List(); - foreach (var serviceInfo in services) + var md5 = await _serviceInfoService.ServicesMD5Cache(); + return new HeartbeatResultVM { - var vm = serviceInfo.ToApiServiceInfoVM(); + Action = ActionConst.Ping, + Data = md5, + Module = ActionModule.RegisterCenter + }; + } - vms.Add(vm); - } + return NotFound(); + } + + [HttpGet("services")] + public async Task> AllServices() + { + var services = await _serviceInfoService.GetAllServiceInfoAsync(); + var vms = new List(); + foreach (var serviceInfo in services) + { + var vm = serviceInfo.ToApiServiceInfoVM(); - return vms; + vms.Add(vm); } - [HttpGet("services/online")] - public async Task> OnlineServices() - { - var services = await _serviceInfoService.GetOnlineServiceInfoAsync(); - var vms = new List(); - foreach (var serviceInfo in services) - { - var vm = serviceInfo.ToApiServiceInfoVM(); + return vms; + } - vms.Add(vm); - } + [HttpGet("services/online")] + public async Task> OnlineServices() + { + var services = await _serviceInfoService.GetOnlineServiceInfoAsync(); + var vms = new List(); + foreach (var serviceInfo in services) + { + var vm = serviceInfo.ToApiServiceInfoVM(); - return vms; + vms.Add(vm); } - [HttpGet("services/offline")] - public async Task> OfflineServices() - { - var services = await _serviceInfoService.GetOfflineServiceInfoAsync(); - var vms = new List(); - foreach (var serviceInfo in services) - { - var vm = serviceInfo.ToApiServiceInfoVM(); + return vms; + } - vms.Add(vm); - } + [HttpGet("services/offline")] + public async Task> OfflineServices() + { + var services = await _serviceInfoService.GetOfflineServiceInfoAsync(); + var vms = new List(); + foreach (var serviceInfo in services) + { + var vm = serviceInfo.ToApiServiceInfoVM(); - return vms; + vms.Add(vm); } + + return vms; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Filters/AdmBasicAuthenticationAttribute.cs b/src/AgileConfig.Server.Apisite/Filters/AdmBasicAuthenticationAttribute.cs index b6e2c87a..8fc68bbe 100644 --- a/src/AgileConfig.Server.Apisite/Filters/AdmBasicAuthenticationAttribute.cs +++ b/src/AgileConfig.Server.Apisite/Filters/AdmBasicAuthenticationAttribute.cs @@ -4,32 +4,33 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; -namespace AgileConfig.Server.Apisite.Filters +namespace AgileConfig.Server.Apisite.Filters; + +/// +/// Action filter that enforces basic authentication for administrator requests. +/// +public class AdmBasicAuthenticationAttribute : ActionFilterAttribute { - /// - /// Action filter that enforces basic authentication for administrator requests. - /// - public class AdmBasicAuthenticationAttribute : ActionFilterAttribute + private readonly IAdmBasicAuthService _admBasicAuthService; + + public AdmBasicAuthenticationAttribute(IAdmBasicAuthService admBasicAuthService) { - private readonly IAdmBasicAuthService _admBasicAuthService; - public AdmBasicAuthenticationAttribute(IAdmBasicAuthService admBasicAuthService) - { - _admBasicAuthService = admBasicAuthService; - } + _admBasicAuthService = admBasicAuthService; + } - public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + if (!await Valid(context.HttpContext.Request)) { - if (!await Valid(context.HttpContext.Request)) - { - context.HttpContext.Response.StatusCode = 403; - context.Result = new ContentResult(); - } - await base.OnActionExecutionAsync(context, next); + context.HttpContext.Response.StatusCode = 403; + context.Result = new ContentResult(); } - private async Task Valid(HttpRequest httpRequest) - { - return await _admBasicAuthService.ValidAsync(httpRequest); - } + await base.OnActionExecutionAsync(context, next); + } + + private async Task Valid(HttpRequest httpRequest) + { + return await _admBasicAuthService.ValidAsync(httpRequest); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Filters/AppBasicAuthenticationAttribute.cs b/src/AgileConfig.Server.Apisite/Filters/AppBasicAuthenticationAttribute.cs index 21990a6c..79ef46c0 100644 --- a/src/AgileConfig.Server.Apisite/Filters/AppBasicAuthenticationAttribute.cs +++ b/src/AgileConfig.Server.Apisite/Filters/AppBasicAuthenticationAttribute.cs @@ -4,32 +4,33 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; -namespace AgileConfig.Server.Apisite.Filters +namespace AgileConfig.Server.Apisite.Filters; + +/// +/// Action filter that enforces basic authentication for application clients. +/// +public class AppBasicAuthenticationAttribute : ActionFilterAttribute { - /// - /// Action filter that enforces basic authentication for application clients. - /// - public class AppBasicAuthenticationAttribute : ActionFilterAttribute + private readonly IAppBasicAuthService _appBasicAuthService; + + public AppBasicAuthenticationAttribute(IAppBasicAuthService appBasicAuthService) { - private readonly IAppBasicAuthService _appBasicAuthService; - public AppBasicAuthenticationAttribute(IAppBasicAuthService appBasicAuthService) - { - _appBasicAuthService = appBasicAuthService; - } + _appBasicAuthService = appBasicAuthService; + } - public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + if (!await Valid(context.HttpContext.Request)) { - if (!await Valid(context.HttpContext.Request)) - { - context.HttpContext.Response.StatusCode = 403; - context.Result = new ContentResult(); - } - await base.OnActionExecutionAsync(context, next); + context.HttpContext.Response.StatusCode = 403; + context.Result = new ContentResult(); } - public async Task Valid(HttpRequest httpRequest) - { - return await _appBasicAuthService.ValidAsync(httpRequest); - } + await base.OnActionExecutionAsync(context, next); + } + + public async Task Valid(HttpRequest httpRequest) + { + return await _appBasicAuthService.ValidAsync(httpRequest); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Filters/ModelValidAttribute.cs b/src/AgileConfig.Server.Apisite/Filters/ModelValidAttribute.cs index fcf834c9..9d568463 100644 --- a/src/AgileConfig.Server.Apisite/Filters/ModelValidAttribute.cs +++ b/src/AgileConfig.Server.Apisite/Filters/ModelValidAttribute.cs @@ -2,28 +2,24 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; -namespace AgileConfig.Server.Apisite.Filters +namespace AgileConfig.Server.Apisite.Filters; + +public class ModelVaildateAttribute : ActionFilterAttribute { - public class ModelVaildateAttribute : ActionFilterAttribute + public override void OnActionExecuting(ActionExecutingContext context) { - public override void OnActionExecuting(ActionExecutingContext context) + if (!context.ModelState.IsValid) { - if (!context.ModelState.IsValid) - { - var errMsg = new StringBuilder(); - foreach (var item in context.ModelState.Values) - { - foreach (var error in item.Errors) - { - errMsg.Append(error.ErrorMessage + ";"); - } - } + var errMsg = new StringBuilder(); + foreach (var item in context.ModelState.Values) + foreach (var error in item.Errors) + errMsg.Append(error.ErrorMessage + ";"); - context.Result = new JsonResult(new { - success = false, - message = errMsg.ToString() - }); - } + context.Result = new JsonResult(new + { + success = false, + message = errMsg.ToString() + }); } } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs b/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs index 5e6752c5..fcdfbb83 100644 --- a/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs +++ b/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs @@ -1,263 +1,256 @@ -using AgileConfig.Server.Apisite.Models; -using AgileConfig.Server.Common; -using AgileConfig.Server.IService; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using System; +using System; using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using AgileConfig.Server.Apisite.Models; +using AgileConfig.Server.Common; using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.IService; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; -namespace AgileConfig.Server.Apisite.Filters -{ - public class PermissionCheckAttribute : ActionFilterAttribute - { - private static string GetEnvFromArgs(IDictionary args, IConfigService configService) - { - args.TryGetValue("env", out object envArg); - var env = ""; - if (envArg == null) - { - ISettingService.IfEnvEmptySetDefault(ref env); - } - else - { - env = envArg.ToString(); - } +namespace AgileConfig.Server.Apisite.Filters; - return env; - } +public class PermissionCheckAttribute : ActionFilterAttribute +{ + protected const string AppMatchPatten = "APP_{0}_{1}"; - /// - /// Static mapping of permission keys to delegates that extract the appId from action parameters. - /// Attributes cannot accept delegates directly, so the functions are predefined here. - /// - protected static readonly - FrozenDictionary> - GetAppIdParamFuncs = - new Dictionary> + /// + /// Static mapping of permission keys to delegates that extract the appId from action parameters. + /// Attributes cannot accept delegates directly, so the functions are predefined here. + /// + protected static readonly + FrozenDictionary> + GetAppIdParamFuncs = + new Dictionary> + { { + "Config.Add", (args, premission, config) => { - "Config.Add", (args, premission, config) => - { - var model = args.ActionArguments["model"]; - return (model as IAppIdModel)?.AppId; - } - }, + var model = args.ActionArguments["model"]; + return (model as IAppIdModel)?.AppId; + } + }, + { + "Config.AddRange", (args, permission, config) => { - "Config.AddRange", (args, permission, config) => - { - var model = args.ActionArguments["model"]; - return (model as List)?.FirstOrDefault()?.AppId; - } - }, + var model = args.ActionArguments["model"]; + return (model as List)?.FirstOrDefault()?.AppId; + } + }, + { + "Config.EnvSync", (args, permission, config) => { - "Config.EnvSync", (args, permission, config) => - { - var appId = args.ActionArguments["appId"]; - return appId?.ToString(); - } - }, + var appId = args.ActionArguments["appId"]; + return appId?.ToString(); + } + }, + { + "Config.Edit", (args, permission, config) => { - "Config.Edit", (args, permission, config) => - { - var model = args.ActionArguments["model"]; - return (model as IAppIdModel)?.AppId; - } - }, + var model = args.ActionArguments["model"]; + return (model as IAppIdModel)?.AppId; + } + }, + { + "Config.Delete", (args, permission, configService) => { - "Config.Delete", (args, permission, configService) => - { - var id = args.ActionArguments["id"]; - var env = GetEnvFromArgs(args.ActionArguments, configService); - var config = configService.GetAsync(id.ToString(), env).GetAwaiter().GetResult(); + var id = args.ActionArguments["id"]; + var env = GetEnvFromArgs(args.ActionArguments, configService); + var config = configService.GetAsync(id.ToString(), env).GetAwaiter().GetResult(); - return config.AppId; - } - }, + return config.AppId; + } + }, + { + "Config.DeleteSome", (args, permission, configService) => { - "Config.DeleteSome", (args, permission, configService) => - { - var ids = args.ActionArguments["ids"] as List; - var env = GetEnvFromArgs(args.ActionArguments, configService); - var config = configService.GetAsync(ids.FirstOrDefault(), env).GetAwaiter().GetResult(); + var ids = args.ActionArguments["ids"] as List; + var env = GetEnvFromArgs(args.ActionArguments, configService); + var config = configService.GetAsync(ids.FirstOrDefault(), env).GetAwaiter().GetResult(); - return config.AppId; - } - }, + return config.AppId; + } + }, + { + "Config.Offline", (args, permission, configService) => { - "Config.Offline", (args, permission, configService) => - { - var id = args.ActionArguments["configId"]; - var env = GetEnvFromArgs(args.ActionArguments, configService); - var config = configService.GetAsync(id.ToString(), env).GetAwaiter().GetResult(); + var id = args.ActionArguments["configId"]; + var env = GetEnvFromArgs(args.ActionArguments, configService); + var config = configService.GetAsync(id.ToString(), env).GetAwaiter().GetResult(); - return config.AppId; - } - }, + return config.AppId; + } + }, + { + "Config.OfflineSome", (args, permission, configService) => { - "Config.OfflineSome", (args, permission, configService) => - { - var ids = args.ActionArguments["configIds"] as List; - var id = ids?.FirstOrDefault(); - var env = GetEnvFromArgs(args.ActionArguments, configService); - var config = configService.GetAsync(ids.FirstOrDefault(), env).GetAwaiter().GetResult(); + var ids = args.ActionArguments["configIds"] as List; + var id = ids?.FirstOrDefault(); + var env = GetEnvFromArgs(args.ActionArguments, configService); + var config = configService.GetAsync(ids.FirstOrDefault(), env).GetAwaiter().GetResult(); - return config.AppId; - } - }, + return config.AppId; + } + }, + { + "Config.Publish", (args, permission, configService) => { - "Config.Publish", (args, permission, configService) => - { - var model = args.ActionArguments["model"] as IAppIdModel; + var model = args.ActionArguments["model"] as IAppIdModel; - return model?.AppId; - } - }, + return model?.AppId; + } + }, + { + "Config.Publish_API", (args, permission, configService) => { - "Config.Publish_API", (args, permission, configService) => - { - var appId = args.ActionArguments["appId"]; + var appId = args.ActionArguments["appId"]; - return appId?.ToString(); - } - }, - { - "Config.Rollback", (args, permission, configService) => - { - var timelineId = args.ActionArguments["publishTimelineId"] as string; - var env = GetEnvFromArgs(args.ActionArguments, configService); - var detail = configService.GetPublishDetailByPublishTimelineIdAsync(timelineId, env) - .GetAwaiter().GetResult(); - return detail.FirstOrDefault()?.AppId; - } - }, + return appId?.ToString(); + } + }, + { + "Config.Rollback", (args, permission, configService) => { - "Config.Rollback_API", (args, permission, configService) => - { - var timelineId = args.ActionArguments["historyId"] as string; - var env = GetEnvFromArgs(args.ActionArguments, configService); - var detail = configService.GetPublishDetailByPublishTimelineIdAsync(timelineId, env) - .GetAwaiter().GetResult(); - return detail.FirstOrDefault()?.AppId; - } - }, + var timelineId = args.ActionArguments["publishTimelineId"] as string; + var env = GetEnvFromArgs(args.ActionArguments, configService); + var detail = configService.GetPublishDetailByPublishTimelineIdAsync(timelineId, env) + .GetAwaiter().GetResult(); + return detail.FirstOrDefault()?.AppId; + } + }, + { + "Config.Rollback_API", (args, permission, configService) => { - "App.Add", (args, permission, configService) => "" - }, + var timelineId = args.ActionArguments["historyId"] as string; + var env = GetEnvFromArgs(args.ActionArguments, configService); + var detail = configService.GetPublishDetailByPublishTimelineIdAsync(timelineId, env) + .GetAwaiter().GetResult(); + return detail.FirstOrDefault()?.AppId; + } + }, + { + "App.Add", (args, permission, configService) => "" + }, + { + "App.Edit", (args, permission, configService) => { - "App.Edit", (args, permission, configService) => - { - var app = args.ActionArguments["model"] as IAppModel; - return app.Id; - } - }, + var app = args.ActionArguments["model"] as IAppModel; + return app.Id; + } + }, + { + "App.Delete", (args, permission, configService) => { - "App.Delete", (args, permission, configService) => - { - var id = args.ActionArguments["id"] as string; - return id; - } - }, + var id = args.ActionArguments["id"] as string; + return id; + } + }, + { + "App.DisableOrEnable", (args, permission, configService) => { - "App.DisableOrEnable", (args, permission, configService) => - { - var id = args.ActionArguments["id"] as string; - return id; - } - }, + var id = args.ActionArguments["id"] as string; + return id; + } + }, + { + "App.Auth", (args, permission, configService) => { - "App.Auth", (args, permission, configService) => - { - var model = args.ActionArguments["model"] as IAppIdModel; - return model?.AppId; - } - }, + var model = args.ActionArguments["model"] as IAppIdModel; + return model?.AppId; + } + }, + { + "Node.Add", (args, permission, configService) => { - "Node.Add", (args, permission, configService) => - { - var id = args.ActionArguments["id"] as string; - return id; - } - }, + var id = args.ActionArguments["id"] as string; + return id; + } + }, + { + "Node.Delete", (args, permission, configService) => { - "Node.Delete", (args, permission, configService) => - { - var model = args.ActionArguments["model"] as IAppIdModel; - return model?.AppId; - } + var model = args.ActionArguments["model"] as IAppIdModel; + return model?.AppId; } - }.ToFrozenDictionary(); + } + }.ToFrozenDictionary(); - protected const string AppMatchPatten = "APP_{0}_{1}"; + private readonly string _actionName; + private readonly IConfigService _configService; + private readonly string _functionKey; - private readonly IPermissionService _permissionService; - private readonly IConfigService _configService; + private readonly IPermissionService _permissionService; - private readonly string _actionName; - private readonly string _functionKey; + public PermissionCheckAttribute(IPermissionService permissionService, IConfigService configService, + string actionName, string functionKey) + { + _permissionService = permissionService; + _configService = configService; - public PermissionCheckAttribute(IPermissionService permissionService, IConfigService configService, - string actionName, string functionKey) - { - _permissionService = permissionService; - _configService = configService; + _actionName = actionName; + _functionKey = functionKey; + } - _actionName = actionName; - _functionKey = functionKey; - } + private static string GetEnvFromArgs(IDictionary args, IConfigService configService) + { + args.TryGetValue("env", out var envArg); + var env = ""; + if (envArg == null) + ISettingService.IfEnvEmptySetDefault(ref env); + else + env = envArg.ToString(); - protected virtual Task GetUserId(ActionExecutingContext context) - { - var userId = context.HttpContext.GetUserIdFromClaim(); + return env; + } + + protected virtual Task GetUserId(ActionExecutingContext context) + { + var userId = context.HttpContext.GetUserIdFromClaim(); + + return Task.FromResult(userId); + } - return Task.FromResult(userId); + public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + var userId = await GetUserId(context); + if (string.IsNullOrEmpty(userId)) + { + //no permission + context.HttpContext.Response.StatusCode = 403; + context.Result = new ContentResult(); + await base.OnActionExecutionAsync(context, next); + return; } - public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + var userFunctions = await _permissionService.GetUserPermission(userId); + + //judge global + var matchKey = _functionKey; + if (userFunctions.Contains(_functionKey)) { - var userId = await GetUserId(context); - if (string.IsNullOrEmpty(userId)) - { - //no permission - context.HttpContext.Response.StatusCode = 403; - context.Result = new ContentResult(); - await base.OnActionExecutionAsync(context, next); - return; - } + await base.OnActionExecutionAsync(context, next); + return; + } - var userFunctions = await _permissionService.GetUserPermission(userId); + var appId = ""; + if (GetAppIdParamFuncs.TryGetValue(_actionName, out var func)) + appId = func(context, _permissionService, _configService); - //judge global - var matchKey = _functionKey; - if (userFunctions.Contains(_functionKey)) + if (!string.IsNullOrEmpty(appId)) + { + matchKey = string.Format(AppMatchPatten, appId, _functionKey); + if (userFunctions.Contains(matchKey)) { await base.OnActionExecutionAsync(context, next); return; } - - var appId = ""; - if (GetAppIdParamFuncs.TryGetValue(_actionName, out var func)) - { - appId = func(context, _permissionService, _configService); - } - - if (!string.IsNullOrEmpty(appId)) - { - matchKey = string.Format(AppMatchPatten, appId, _functionKey); - if (userFunctions.Contains(matchKey)) - { - await base.OnActionExecutionAsync(context, next); - return; - } - } - - //no permission - context.HttpContext.Response.StatusCode = 403; - context.Result = new ContentResult(); - await base.OnActionExecutionAsync(context, next); } + + //no permission + context.HttpContext.Response.StatusCode = 403; + context.Result = new ContentResult(); + await base.OnActionExecutionAsync(context, next); } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Filters/PermissionCheckByBasicAttribute.cs b/src/AgileConfig.Server.Apisite/Filters/PermissionCheckByBasicAttribute.cs index 3c9cd9d9..951cc6a6 100644 --- a/src/AgileConfig.Server.Apisite/Filters/PermissionCheckByBasicAttribute.cs +++ b/src/AgileConfig.Server.Apisite/Filters/PermissionCheckByBasicAttribute.cs @@ -1,36 +1,37 @@ -using AgileConfig.Server.IService; -using Microsoft.AspNetCore.Mvc.Filters; -using System.Linq; +using System.Linq; using System.Threading.Tasks; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.IService; +using Microsoft.AspNetCore.Mvc.Filters; -namespace AgileConfig.Server.Apisite.Filters +namespace AgileConfig.Server.Apisite.Filters; + +/// +/// Permission check that reads user information from the basic authentication header. +/// +public class PermissionCheckByBasicAttribute : PermissionCheckAttribute { - /// - /// Permission check that reads user information from the basic authentication header. - /// - public class PermissionCheckByBasicAttribute : PermissionCheckAttribute - { - protected IAdmBasicAuthService _basicAuthService; - protected IUserService _userService; + protected IAdmBasicAuthService _basicAuthService; + protected IUserService _userService; - public PermissionCheckByBasicAttribute( - IPermissionService permissionService, - IConfigService configService, - IAdmBasicAuthService basicAuthService, - IUserService userService, - string actionName, - string functionKey) : base(permissionService, configService, actionName, functionKey) - { - _userService = userService; - _basicAuthService = basicAuthService; - } + public PermissionCheckByBasicAttribute( + IPermissionService permissionService, + IConfigService configService, + IAdmBasicAuthService basicAuthService, + IUserService userService, + string actionName, + string functionKey) : base(permissionService, configService, actionName, functionKey) + { + _userService = userService; + _basicAuthService = basicAuthService; + } - protected override async Task GetUserId(ActionExecutingContext context) - { - var userName = _basicAuthService.GetUserNamePassword(context.HttpContext.Request).Item1; - var user = (await _userService.GetUsersByNameAsync(userName)).FirstOrDefault(x => x.Status == Data.Entity.UserStatus.Normal); + protected override async Task GetUserId(ActionExecutingContext context) + { + var userName = _basicAuthService.GetUserNamePassword(context.HttpContext.Request).Item1; + var user = (await _userService.GetUsersByNameAsync(userName)).FirstOrDefault(x => + x.Status == UserStatus.Normal); - return user?.Id; - } + return user?.Id; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/InitService.cs b/src/AgileConfig.Server.Apisite/InitService.cs index 21e4d0fe..eece6490 100644 --- a/src/AgileConfig.Server.Apisite/InitService.cs +++ b/src/AgileConfig.Server.Apisite/InitService.cs @@ -1,105 +1,101 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using AgileConfig.Server.Apisite.Metrics; using AgileConfig.Server.Apisite.Utilites; using AgileConfig.Server.IService; -using AgileConfig.Server.Service; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -namespace AgileConfig.Server.Apisite +namespace AgileConfig.Server.Apisite; + +public class InitService : IHostedService { - public class InitService : IHostedService + private readonly IEventHandlerRegister _eventRegister; + private readonly IServiceScope _localServiceScope; + private readonly ILogger _logger; + private readonly IMeterService _meterService; + private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; + private readonly IServerNodeService _serverNodeService; + private readonly IServiceHealthCheckService _serviceHealthCheckService; + private readonly ISystemInitializationService _systemInitializationService; + + public InitService(IServiceScopeFactory serviceScopeFactory, + IMeterService meterService, + ILogger logger) { - private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; - private readonly IEventHandlerRegister _eventRegister; - private readonly IServerNodeService _serverNodeService; - private readonly IServiceHealthCheckService _serviceHealthCheckService; - private readonly ISystemInitializationService _systemInitializationService; - private readonly IMeterService _meterService; - private readonly ILogger _logger; - private readonly IServiceScope _localServiceScope; - public InitService(IServiceScopeFactory serviceScopeFactory, - ISystemInitializationService systemInitializationService, - IMeterService meterService, - ILogger logger) - { - _logger = logger; - _systemInitializationService = systemInitializationService; - _meterService = meterService; - _localServiceScope = serviceScopeFactory.CreateScope(); - _remoteServerNodeProxy = _localServiceScope.ServiceProvider.GetService(); - _eventRegister = _localServiceScope.ServiceProvider.GetService(); - _serverNodeService = _localServiceScope.ServiceProvider.GetService(); - _serviceHealthCheckService = _localServiceScope.ServiceProvider.GetService(); + _logger = logger; + _meterService = meterService; + _localServiceScope = serviceScopeFactory.CreateScope(); + _systemInitializationService = _localServiceScope.ServiceProvider.GetService(); + _remoteServerNodeProxy = _localServiceScope.ServiceProvider.GetService(); + _eventRegister = _localServiceScope.ServiceProvider.GetService(); + _serverNodeService = _localServiceScope.ServiceProvider.GetService(); + _serviceHealthCheckService = _localServiceScope.ServiceProvider.GetService(); + } - } + public async Task StartAsync(CancellationToken cancellationToken) + { + _systemInitializationService.TryInitDefaultEnvironment(); //init DEV TEST STAGE PROD - public async Task StartAsync(CancellationToken cancellationToken) + if (Appsettings.IsAdminConsoleMode) { - _systemInitializationService.TryInitDefaultEnvironment();//init DEV TEST STAGE PROD + _systemInitializationService.TryInitJwtSecret(); // Initialize the JWT secret. + _systemInitializationService.TryInitSaPassword(); // init super admin password + _systemInitializationService.TryInitDefaultApp(); + await _systemInitializationService + .TryInitSuperAdminRole(); // Initialize SuperAdministrator role and functions + _ = _remoteServerNodeProxy.TestEchoAsync(); // Start node connectivity checks. + _ = _serviceHealthCheckService.StartCheckAsync(); // Start service health monitoring. + _eventRegister.Register(); // Register event bus callbacks. + } - if (Appsettings.IsAdminConsoleMode) + if (Appsettings.Cluster) + { + // When cluster mode is enabled, automatically register the local IP (for docker-compose setups). + var ip = GetIp(); + if (!string.IsNullOrEmpty(ip)) { - _systemInitializationService.TryInitJwtSecret();// Initialize the JWT secret. - _systemInitializationService.TryInitSaPassword(); // init super admin password - _systemInitializationService.TryInitDefaultApp(); - _ = _remoteServerNodeProxy.TestEchoAsync();// Start node connectivity checks. - _ = _serviceHealthCheckService.StartCheckAsync();// Start service health monitoring. - _eventRegister.Register();// Register event bus callbacks. + var desc = Appsettings.IsAdminConsoleMode ? "Console node" : ""; + await _serverNodeService.JoinAsync(ip, 5000, desc); + _logger.LogInformation($"AgileConfig node http://{ip}:5000 joined ."); } + } - if (Appsettings.Cluster) - { - // When cluster mode is enabled, automatically register the local IP (for docker-compose setups). - var ip = GetIp(); - if (!string.IsNullOrEmpty(ip)) - { - var desc = Appsettings.IsAdminConsoleMode ? "Console node" : ""; - await _serverNodeService.JoinAsync(ip, 5000, desc); - _logger.LogInformation($"AgileConfig node http://{ip}:5000 joined ."); - } - } + if (!string.IsNullOrEmpty(Appsettings.OtlpMetricsEndpoint)) _meterService.Start(); + } - if (!string.IsNullOrEmpty(Appsettings.OtlpMetricsEndpoint)) + public async Task StopAsync(CancellationToken cancellationToken) + { + if (Appsettings.Cluster) + { + var ip = GetIp(); + if (!string.IsNullOrEmpty(ip)) { - _meterService.Start(); + await _serverNodeService.DeleteAsync($"http://{ip}:{5000}"); + _logger.LogInformation($"AgileConfig node http://{ip}:5000 removed ."); } } - public async Task StopAsync(CancellationToken cancellationToken) + _localServiceScope?.Dispose(); + } + + private string GetIp() + { + try { - if (Appsettings.Cluster) - { - var ip = GetIp(); - if (!string.IsNullOrEmpty(ip)) - { - await _serverNodeService.DeleteAsync($"http://{ip}:{5000}"); - _logger.LogInformation($"AgileConfig node http://{ip}:5000 removed ."); - } - } + var myips = IpExt.GetEndpointIp(); + _logger.LogInformation("AgileConfig node's IP " + string.Join(',', myips)); - _localServiceScope?.Dispose(); + return myips.FirstOrDefault(); } - - private string GetIp() + catch (Exception e) { - try - { - var myips = IpExt.GetEndpointIp(); - _logger.LogInformation("AgileConfig node's IP " + String.Join(',', myips)); - - return myips.FirstOrDefault(); - } - catch (Exception e) - { - _logger.LogError(e, "Try get node's IP error ."); - } - - return ""; + _logger.LogError(e, "Try get node's IP error ."); } + + return ""; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Metrics/IMeterService.cs b/src/AgileConfig.Server.Apisite/Metrics/IMeterService.cs index 120456fd..7f32610c 100644 --- a/src/AgileConfig.Server.Apisite/Metrics/IMeterService.cs +++ b/src/AgileConfig.Server.Apisite/Metrics/IMeterService.cs @@ -1,16 +1,15 @@ using System.Diagnostics.Metrics; -namespace AgileConfig.Server.Apisite.Metrics +namespace AgileConfig.Server.Apisite.Metrics; + +public interface IMeterService { - public interface IMeterService - { - ObservableGauge AppGauge { get; } - ObservableGauge ClientGauge { get; } - ObservableGauge ConfigGauge { get; } - ObservableGauge NodeGauge { get; } - Counter PullAppConfigCounter { get; } - ObservableGauge ServiceGauge { get; } + ObservableGauge AppGauge { get; } + ObservableGauge ClientGauge { get; } + ObservableGauge ConfigGauge { get; } + ObservableGauge NodeGauge { get; } + Counter PullAppConfigCounter { get; } + ObservableGauge ServiceGauge { get; } - void Start(); - } + void Start(); } \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Metrics/MeterService.cs b/src/AgileConfig.Server.Apisite/Metrics/MeterService.cs index 0ca965eb..83987629 100644 --- a/src/AgileConfig.Server.Apisite/Metrics/MeterService.cs +++ b/src/AgileConfig.Server.Apisite/Metrics/MeterService.cs @@ -1,162 +1,149 @@ -using AgileConfig.Server.Data.Entity; +using System; +using System.Diagnostics.Metrics; +using System.Threading.Tasks; +using AgileConfig.Server.Data.Entity; using AgileConfig.Server.IService; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.ResourceMonitoring; using Microsoft.Extensions.Logging; -using System; -using System.Diagnostics.Metrics; -using System.Threading.Tasks; - -namespace AgileConfig.Server.Apisite.Metrics -{ - public class MeterService : IMeterService - { - public const string MeterName = "AgileConfigMeter"; - public static Meter AgileConfigMeter { get; } +namespace AgileConfig.Server.Apisite.Metrics; - public ObservableGauge AppGauge { get; } - public ObservableGauge ConfigGauge { get; } - public ObservableGauge ServiceGauge { get; } - public ObservableGauge ClientGauge { get; } - public ObservableGauge NodeGauge { get; } - public ObservableGauge MemoryUsedGauge { get; } - public ObservableGauge CpuUsedGauge { get; } +public class MeterService : IMeterService +{ + public const string MeterName = "AgileConfigMeter"; - public Counter PullAppConfigCounter { get; } + private const int _checkInterval = 5; - private readonly IServiceScope _serviceScope; + private readonly IAppService _appService; + private readonly IConfigService _configService; + private readonly IRemoteServerNodeProxy _remoteServer; + private readonly IResourceMonitor _resourceMonitor; + private readonly IServerNodeService _serverNodeService; + private readonly IServiceInfoService _serviceInfoService; - private readonly IAppService _appService; - private readonly IConfigService _configService; - private readonly IServerNodeService _serverNodeService; - private readonly IRemoteServerNodeProxy _remoteServer; - private readonly IServiceInfoService _serviceInfoService; - private readonly IResourceMonitor _resourceMonitor; + private readonly IServiceScope _serviceScope; - private int _appCount = 0; - private int _configCount = 0; - private int _clientCount = 0; - private int _serverNodeCount = 0; - private int _serviceCount = 0; - private long _memoryUsed = 0; - private double _cpuUsed = 0; + private int _appCount; + private int _clientCount; + private int _configCount; + private double _cpuUsed; + private long _memoryUsed; + private int _serverNodeCount; + private int _serviceCount; - private const int _checkInterval = 5; + static MeterService() + { + AgileConfigMeter = new Meter(MeterName, "1.0"); + } - static MeterService() + public MeterService(IServiceScopeFactory sf) + { + _serviceScope = sf.CreateScope(); + var sp = _serviceScope.ServiceProvider; + + _appService = sp.GetService(); + _configService = sp.GetService(); + _serverNodeService = sp.GetService(); + _remoteServer = sp.GetService(); + _serviceInfoService = sp.GetService(); + try { - AgileConfigMeter = new(MeterName, "1.0"); + _resourceMonitor = sp.GetService(); } - - public MeterService(IServiceScopeFactory sf) + catch { - _serviceScope = sf.CreateScope(); - var sp = _serviceScope.ServiceProvider; - - _appService = sp.GetService(); - _configService = sp.GetService(); - _serverNodeService = sp.GetService(); - _remoteServer = sp.GetService(); - _serviceInfoService = sp.GetService(); - try - { - _resourceMonitor = sp.GetService(); - } - catch - { - var logger = sp.GetService().CreateLogger(); - logger.LogWarning("Failed to get IResourceMonitor, maybe you are using cgroups v2, currently is not supported."); - } + var logger = sp.GetService().CreateLogger(); + logger.LogWarning( + "Failed to get IResourceMonitor, maybe you are using cgroups v2, currently is not supported."); + } - AppGauge = AgileConfigMeter.CreateObservableGauge("AppCount", () => - { - return _appCount; - }, "", "The number of enabled apps"); + AppGauge = AgileConfigMeter.CreateObservableGauge("AppCount", () => { return _appCount; }, "", + "The number of enabled apps"); - ConfigGauge = AgileConfigMeter.CreateObservableGauge("ConfigCount", () => - { - return _configCount; - }, "", "The number of enabled configuration items"); + ConfigGauge = AgileConfigMeter.CreateObservableGauge("ConfigCount", () => { return _configCount; }, "", + "The number of enabled configuration items"); - ServiceGauge = AgileConfigMeter.CreateObservableGauge("ServiceCount", () => - { - return _serviceCount; - }, "", "The number of registered services"); + ServiceGauge = AgileConfigMeter.CreateObservableGauge("ServiceCount", () => { return _serviceCount; }, "", + "The number of registered services"); - ClientGauge = AgileConfigMeter.CreateObservableGauge("ClientCount", () => - { - return _clientCount; - }, "", "The number of connected clients"); + ClientGauge = AgileConfigMeter.CreateObservableGauge("ClientCount", () => { return _clientCount; }, "", + "The number of connected clients"); - NodeGauge = AgileConfigMeter.CreateObservableGauge("NodeCount", () => - { - return _serverNodeCount; - }, "", "The number of nodes"); + NodeGauge = AgileConfigMeter.CreateObservableGauge("NodeCount", () => { return _serverNodeCount; }, "", + "The number of nodes"); - MemoryUsedGauge = AgileConfigMeter.CreateObservableGauge("MemoryUsed", () => - { - return _memoryUsed; - }, "MB", "Memory used (MB)"); + MemoryUsedGauge = + AgileConfigMeter.CreateObservableGauge("MemoryUsed", () => { return _memoryUsed; }, "MB", + "Memory used (MB)"); - CpuUsedGauge = AgileConfigMeter.CreateObservableGauge("CpuUsed", () => - { - return _cpuUsed; - }, "%", "CPU used percent"); + CpuUsedGauge = + AgileConfigMeter.CreateObservableGauge("CpuUsed", () => { return _cpuUsed; }, "%", "CPU used percent"); - PullAppConfigCounter = AgileConfigMeter.CreateCounter("PullAppConfigCounter", "", "The number of times the app configuration was pulled"); - } + PullAppConfigCounter = AgileConfigMeter.CreateCounter("PullAppConfigCounter", "", + "The number of times the app configuration was pulled"); + } - public void Start() - { - _ = StartCheckAppCountAsync(); - } + public static Meter AgileConfigMeter { get; } + public ObservableGauge MemoryUsedGauge { get; } + public ObservableGauge CpuUsedGauge { get; } + + public ObservableGauge AppGauge { get; } + public ObservableGauge ConfigGauge { get; } + public ObservableGauge ServiceGauge { get; } + public ObservableGauge ClientGauge { get; } + public ObservableGauge NodeGauge { get; } - private Task StartCheckAppCountAsync() + public Counter PullAppConfigCounter { get; } + + public void Start() + { + _ = StartCheckAppCountAsync(); + } + + private Task StartCheckAppCountAsync() + { + return Task.Factory.StartNew(async () => { - return Task.Factory.StartNew(async () => + while (true) { - while (true) + try { - try - { - _appCount = await _appService.CountEnabledAppsAsync(); + _appCount = await _appService.CountEnabledAppsAsync(); - _configCount = await _configService.CountEnabledConfigsAsync(); + _configCount = await _configService.CountEnabledConfigsAsync(); - var services = await _serviceInfoService.GetAllServiceInfoAsync(); - _serviceCount = services.Count; + var services = await _serviceInfoService.GetAllServiceInfoAsync(); + _serviceCount = services.Count; - var nodes = await _serverNodeService.GetAllNodesAsync(); - _serverNodeCount = nodes.Count; + var nodes = await _serverNodeService.GetAllNodesAsync(); + _serverNodeCount = nodes.Count; - var clientCount = 0; - foreach (var item in nodes) + var clientCount = 0; + foreach (var item in nodes) + if (item.Status == NodeStatus.Online) { - if (item.Status == NodeStatus.Online) - { - var clientInfos = await _remoteServer.GetClientsReportAsync(item.Id.ToString()); - clientCount += clientInfos.ClientCount; - } + var clientInfos = await _remoteServer.GetClientsReportAsync(item.Id); + clientCount += clientInfos.ClientCount; } - _clientCount = clientCount; - // memory and cpu - if (_resourceMonitor != null) - { - var window = TimeSpan.FromSeconds(3); - var utilization = _resourceMonitor.GetUtilization(window); - _memoryUsed = (long)utilization.MemoryUsedInBytes / 1024 / 1024; - _cpuUsed = utilization.CpuUsedPercentage; - } - } - catch + _clientCount = clientCount; + + // memory and cpu + if (_resourceMonitor != null) { + var window = TimeSpan.FromSeconds(3); + var utilization = _resourceMonitor.GetUtilization(window); + _memoryUsed = (long)utilization.MemoryUsedInBytes / 1024 / 1024; + _cpuUsed = utilization.CpuUsedPercentage; } - - await Task.Delay(1000 * _checkInterval); } - }, TaskCreationOptions.LongRunning); - } + catch + { + } + + await Task.Delay(1000 * _checkInterval); + } + }, TaskCreationOptions.LongRunning); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/AppAuthVM.cs b/src/AgileConfig.Server.Apisite/Models/AppAuthVM.cs index c4df476c..6a807c45 100644 --- a/src/AgileConfig.Server.Apisite/Models/AppAuthVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/AppAuthVM.cs @@ -1,15 +1,14 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace AgileConfig.Server.Apisite.Models +namespace AgileConfig.Server.Apisite.Models; + +[ExcludeFromCodeCoverage] +public class AppAuthVM : IAppIdModel { - [ExcludeFromCodeCoverage] - public class AppAuthVM: IAppIdModel - { - public List EditConfigPermissionUsers { get; set; } + public List EditConfigPermissionUsers { get; set; } - public List PublishConfigPermissionUsers { get; set; } + public List PublishConfigPermissionUsers { get; set; } - public string AppId { get; set; } - } -} + public string AppId { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/AppVM.cs b/src/AgileConfig.Server.Apisite/Models/AppVM.cs index e721c18b..01e75333 100644 --- a/src/AgileConfig.Server.Apisite/Models/AppVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/AppVM.cs @@ -1,116 +1,101 @@ -using AgileConfig.Server.Apisite.Controllers.api.Models; -using AgileConfig.Server.Data.Entity; -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; +using AgileConfig.Server.Apisite.Controllers.api.Models; +using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Apisite.Models +namespace AgileConfig.Server.Apisite.Models; + +[ExcludeFromCodeCoverage] +public class AppVM : IAppModel { - [ExcludeFromCodeCoverage] - public class AppVM : IAppModel - { - [Required(ErrorMessage = "应用Id不能为空")] - [MaxLength(36, ErrorMessage = "应用Id长度不能超过36位")] - public string Id { get; set; } + [MaxLength(36, ErrorMessage = "密钥长度不能超过36位")] + public string Secret { get; set; } - [Required(ErrorMessage = "应用名称不能为空")] - [MaxLength(50, ErrorMessage = "应用名称长度不能超过50位")] - public string Name { get; set; } + public bool Enabled { get; set; } + public bool Inheritanced { get; set; } - [MaxLength(50, ErrorMessage = "应用组名称长度不能超过50位")] - public string Group { get; set; } + public List inheritancedApps { get; set; } - [MaxLength(36, ErrorMessage = "密钥长度不能超过36位")] - public string Secret { get; set; } + public List inheritancedAppNames { get; set; } - public bool Enabled { get; set; } - public bool Inheritanced { get; set; } + public string AppAdmin { get; set; } - public List inheritancedApps { get; set; } + public string AppAdminName { get; set; } - public List inheritancedAppNames { get; set; } + [Required(ErrorMessage = "应用Id不能为空")] + [MaxLength(36, ErrorMessage = "应用Id长度不能超过36位")] + public string Id { get; set; } - public string AppAdmin { get; set; } + [Required(ErrorMessage = "应用名称不能为空")] + [MaxLength(50, ErrorMessage = "应用名称长度不能超过50位")] + public string Name { get; set; } - public string AppAdminName { get; set; } - - public DateTime CreateTime { get; set; } + [MaxLength(50, ErrorMessage = "应用组名称长度不能超过50位")] + public string Group { get; set; } - } + public DateTime CreateTime { get; set; } +} + +[ExcludeFromCodeCoverage] +public class AppListVM : AppVM +{ + public DateTime? UpdateTime { get; set; } - [ExcludeFromCodeCoverage] - public class AppListVM : AppVM + public List children { get; set; } +} + +public static class AppVMExtension +{ + public static App ToApp(this AppVM vm) { + if (vm == null) return null; + + var app = new App(); + app.Id = vm.Id; + app.Name = vm.Name; + app.Secret = vm.Secret; + app.Enabled = vm.Enabled; + app.Type = vm.Inheritanced ? AppType.Inheritance : AppType.PRIVATE; + app.AppAdmin = vm.AppAdmin; + app.Group = vm.Group; + app.CreateTime = vm.CreateTime; + + return app; + } - public DateTime? UpdateTime { get; set; } - - public List children { get; set; } + public static App ToApp(this AppVM vm, App app) + { + if (vm == null) return null; + + app.Id = vm.Id; + app.Name = vm.Name; + app.Secret = vm.Secret; + app.Enabled = vm.Enabled; + app.Type = vm.Inheritanced ? AppType.Inheritance : AppType.PRIVATE; + app.AppAdmin = vm.AppAdmin; + app.Group = vm.Group; + if (vm.CreateTime > DateTime.MinValue) app.CreateTime = vm.CreateTime; + + return app; } - public static class AppVMExtension + public static ApiAppVM ToApiAppVM(this AppVM vm) { - public static App ToApp(this AppVM vm) - { - if (vm == null) - { - return null; - } - - var app = new App(); - app.Id = vm.Id; - app.Name = vm.Name; - app.Secret = vm.Secret; - app.Enabled = vm.Enabled; - app.Type = vm.Inheritanced ? AppType.Inheritance : AppType.PRIVATE; - app.AppAdmin = vm.AppAdmin; - app.Group = vm.Group; - app.CreateTime = vm.CreateTime; - - return app; - } - - public static App ToApp(this AppVM vm, App app) - { - if (vm == null) - { - return null; - } - - app.Id = vm.Id; - app.Name = vm.Name; - app.Secret = vm.Secret; - app.Enabled = vm.Enabled; - app.Type = vm.Inheritanced ? AppType.Inheritance : AppType.PRIVATE; - app.AppAdmin = vm.AppAdmin; - app.Group = vm.Group; - if (vm.CreateTime > DateTime.MinValue) - { - app.CreateTime = vm.CreateTime; - } - - return app; - } - - public static ApiAppVM ToApiAppVM(this AppVM vm) + if (vm == null) return null; + + return new ApiAppVM { - if (vm == null) - { - return null; - } - - return new ApiAppVM - { - Id = vm.Id, - Name = vm.Name, - Secret = vm.Secret, - Inheritanced = vm.Inheritanced, - Enabled = vm.Enabled, - InheritancedApps = vm.inheritancedApps, - AppAdmin = vm.AppAdmin, - Group = vm.Group, - CreateTime = vm.CreateTime - }; - } + Id = vm.Id, + Name = vm.Name, + Secret = vm.Secret, + Inheritanced = vm.Inheritanced, + Enabled = vm.Enabled, + InheritancedApps = vm.inheritancedApps, + AppAdmin = vm.AppAdmin, + Group = vm.Group, + CreateTime = vm.CreateTime + }; } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/Binders/EnvQueryStringBinder.cs b/src/AgileConfig.Server.Apisite/Models/Binders/EnvQueryStringBinder.cs index 843a7248..be2991f0 100644 --- a/src/AgileConfig.Server.Apisite/Models/Binders/EnvQueryStringBinder.cs +++ b/src/AgileConfig.Server.Apisite/Models/Binders/EnvQueryStringBinder.cs @@ -1,43 +1,36 @@ -using Microsoft.AspNetCore.Mvc.ModelBinding; +using System; using System.Threading.Tasks; -using System; using AgileConfig.Server.IService; +using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace AgileConfig.Server.Apisite.Models.Binders +namespace AgileConfig.Server.Apisite.Models.Binders; + +public class EnvQueryStringBinder : IModelBinder { - public class EnvQueryStringBinder : IModelBinder + public Task BindModelAsync(ModelBindingContext bindingContext) { - public Task BindModelAsync(ModelBindingContext bindingContext) - { - if (bindingContext == null) - { - throw new ArgumentNullException(nameof(bindingContext)); - } - - var instance = new EnvString - { - Value = ISettingService.EnvironmentList[0] - }; - - var modelName = bindingContext.ModelName; - var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName); - - if (valueProviderResult == ValueProviderResult.None) - { - bindingContext.Result = ModelBindingResult.Success(instance); - return Task.CompletedTask; - } + if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext)); - bindingContext.ModelState.SetModelValue(modelName, valueProviderResult); + var instance = new EnvString + { + Value = ISettingService.EnvironmentList[0] + }; - var value = valueProviderResult.FirstValue; - if (!string.IsNullOrEmpty(value)) - { - instance.Value = value; - } + var modelName = bindingContext.ModelName; + var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName); + if (valueProviderResult == ValueProviderResult.None) + { bindingContext.Result = ModelBindingResult.Success(instance); return Task.CompletedTask; } + + bindingContext.ModelState.SetModelValue(modelName, valueProviderResult); + + var value = valueProviderResult.FirstValue; + if (!string.IsNullOrEmpty(value)) instance.Value = value; + + bindingContext.Result = ModelBindingResult.Success(instance); + return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/ChangePasswordVM.cs b/src/AgileConfig.Server.Apisite/Models/ChangePasswordVM.cs index f96c1684..00f8392e 100644 --- a/src/AgileConfig.Server.Apisite/Models/ChangePasswordVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/ChangePasswordVM.cs @@ -1,14 +1,13 @@ using System.Diagnostics.CodeAnalysis; -namespace AgileConfig.Server.Apisite.Models +namespace AgileConfig.Server.Apisite.Models; + +[ExcludeFromCodeCoverage] +public class ChangePasswordVM { - [ExcludeFromCodeCoverage] - public class ChangePasswordVM - { - public string password { get; set; } + public string password { get; set; } - public string confirmPassword { get; set; } + public string confirmPassword { get; set; } - public string oldPassword { get; set; } - } -} + public string oldPassword { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/ConfigVM.cs b/src/AgileConfig.Server.Apisite/Models/ConfigVM.cs index e7b4a187..7ec23617 100644 --- a/src/AgileConfig.Server.Apisite/Models/ConfigVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/ConfigVM.cs @@ -1,37 +1,36 @@ -using AgileConfig.Server.Data.Entity; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; +using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Apisite.Models +namespace AgileConfig.Server.Apisite.Models; + +public interface IAppIdModel { - public interface IAppIdModel - { - string AppId { get; set; } - } + string AppId { get; set; } +} - [ExcludeFromCodeCoverage] - public class ConfigVM: IAppIdModel - { - public string Id { get; set; } +[ExcludeFromCodeCoverage] +public class ConfigVM : IAppIdModel +{ + public string Id { get; set; } - [Required(ErrorMessage = "应用Id不能为空")] - [MaxLength(36, ErrorMessage = "应用Id长度不能超过36位")] - public string AppId { get; set; } + [MaxLength(100, ErrorMessage = "配置组长度不能超过100位")] + public string Group { get; set; } - [MaxLength(100, ErrorMessage = "配置组长度不能超过100位")] - public string Group { get; set; } + [Required(ErrorMessage = "配置键不能为空")] + [MaxLength(100, ErrorMessage = "配置键长度不能超过100位")] + public string Key { get; set; } - [Required(ErrorMessage = "配置键不能为空")] - [MaxLength(100, ErrorMessage = "配置键长度不能超过100位")] - public string Key { get; set; } + [MaxLength(4000, ErrorMessage = "配置值长度不能超过4000位")] + public string Value { get; set; } - [MaxLength(4000, ErrorMessage = "配置值长度不能超过4000位")] - public string Value { get; set; } + [MaxLength(200, ErrorMessage = "描述长度不能超过200位")] + public string Description { get; set; } - [MaxLength(200, ErrorMessage = "描述长度不能超过200位")] - public string Description { get; set; } + public OnlineStatus OnlineStatus { get; set; } + public ConfigStatus Status { get; set; } - public OnlineStatus OnlineStatus { get; set; } - public ConfigStatus Status { get; set; } - } -} + [Required(ErrorMessage = "应用Id不能为空")] + [MaxLength(36, ErrorMessage = "应用Id长度不能超过36位")] + public string AppId { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/EnvString.cs b/src/AgileConfig.Server.Apisite/Models/EnvString.cs index a91380cc..39010e13 100644 --- a/src/AgileConfig.Server.Apisite/Models/EnvString.cs +++ b/src/AgileConfig.Server.Apisite/Models/EnvString.cs @@ -1,13 +1,12 @@ -using AgileConfig.Server.Apisite.Models.Binders; +using System.Diagnostics.CodeAnalysis; +using AgileConfig.Server.Apisite.Models.Binders; using Microsoft.AspNetCore.Mvc; -using System.Diagnostics.CodeAnalysis; -namespace AgileConfig.Server.Apisite.Models +namespace AgileConfig.Server.Apisite.Models; + +[ExcludeFromCodeCoverage] +[ModelBinder(BinderType = typeof(EnvQueryStringBinder))] +public class EnvString { - [ExcludeFromCodeCoverage] - [ModelBinder(BinderType = typeof(EnvQueryStringBinder))] - public class EnvString - { - public string Value { get; set; } - } -} + public string Value { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/InitPasswordVM.cs b/src/AgileConfig.Server.Apisite/Models/InitPasswordVM.cs index 134ae472..71627865 100644 --- a/src/AgileConfig.Server.Apisite/Models/InitPasswordVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/InitPasswordVM.cs @@ -1,12 +1,11 @@ using System.Diagnostics.CodeAnalysis; -namespace AgileConfig.Server.Apisite.Models +namespace AgileConfig.Server.Apisite.Models; + +[ExcludeFromCodeCoverage] +public class InitPasswordVM { - [ExcludeFromCodeCoverage] - public class InitPasswordVM - { - public string password { get; set; } + public string password { get; set; } - public string confirmPassword { get; set; } - } -} + public string confirmPassword { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/LoginVM.cs b/src/AgileConfig.Server.Apisite/Models/LoginVM.cs index 2d03525f..16070921 100644 --- a/src/AgileConfig.Server.Apisite/Models/LoginVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/LoginVM.cs @@ -1,12 +1,10 @@ using System.Diagnostics.CodeAnalysis; -namespace AgileConfig.Server.Apisite.Models -{ - [ExcludeFromCodeCoverage] - public class LoginVM - { - public string userName { get; set; } - public string password { get; set; } - } +namespace AgileConfig.Server.Apisite.Models; -} +[ExcludeFromCodeCoverage] +public class LoginVM +{ + public string userName { get; set; } + public string password { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/Mapping/ModelMappingExtension.cs b/src/AgileConfig.Server.Apisite/Models/Mapping/ModelMappingExtension.cs index 506f4c5a..0287e24a 100644 --- a/src/AgileConfig.Server.Apisite/Models/Mapping/ModelMappingExtension.cs +++ b/src/AgileConfig.Server.Apisite/Models/Mapping/ModelMappingExtension.cs @@ -1,177 +1,153 @@ -using AgileConfig.Server.Data.Entity; +using System.Collections.Generic; using AgileConfig.Server.Apisite.Controllers.api.Models; -using System.Collections.Generic; -using System; +using AgileConfig.Server.Data.Entity; using Newtonsoft.Json; -namespace AgileConfig.Server.Apisite.Models.Mapping +namespace AgileConfig.Server.Apisite.Models.Mapping; + +/// +/// Do not ask me why not use AutoMapper, I don't know. Just like manual mapping, it's simple and clear. +/// +public static class AppExtension { - /// - /// Do not ask me why not use AutoMapper, I don't know. Just like manual mapping, it's simple and clear. - /// - public static class AppExtension + public static AppVM ToAppVM(this App app) { - public static AppVM ToAppVM(this App app) - { - if (app == null) - { - return null; - } - - var appVM = new AppVM - { - Id = app.Id, - Name = app.Name, - Group = app.Group, - Secret = app.Secret, - Enabled = app.Enabled, - Inheritanced = app.Type == AppType.Inheritance, - AppAdmin = app.AppAdmin, - CreateTime = app.CreateTime, - }; - - return appVM; - } + if (app == null) return null; - public static AppListVM ToAppListVM(this App app) + var appVM = new AppVM { - if (app == null) - { - return null; - } - - var vm = new AppListVM - { - Id = app.Id, - Name = app.Name, - Group = app.Group, - Secret = app.Secret, - Inheritanced = app.Type == AppType.Inheritance, - Enabled = app.Enabled, - UpdateTime = app.UpdateTime, - CreateTime = app.CreateTime, - AppAdmin = app.AppAdmin, - }; - - return vm; - } + Id = app.Id, + Name = app.Name, + Group = app.Group, + Secret = app.Secret, + Enabled = app.Enabled, + Inheritanced = app.Type == AppType.Inheritance, + AppAdmin = app.AppAdmin, + CreateTime = app.CreateTime + }; + + return appVM; + } + + public static AppListVM ToAppListVM(this App app) + { + if (app == null) return null; - public static ApiAppVM ToApiAppVM(this App vm) + var vm = new AppListVM { - if (vm == null) - { - return null; - } - - return new ApiAppVM - { - Id = vm.Id, - Name = vm.Name, - Secret = vm.Secret, - Inheritanced = vm.Type == AppType.Inheritance, - Enabled = vm.Enabled, - AppAdmin = vm.AppAdmin, - Group = vm.Group, - CreateTime = vm.CreateTime - }; - } + Id = app.Id, + Name = app.Name, + Group = app.Group, + Secret = app.Secret, + Inheritanced = app.Type == AppType.Inheritance, + Enabled = app.Enabled, + UpdateTime = app.UpdateTime, + CreateTime = app.CreateTime, + AppAdmin = app.AppAdmin + }; + + return vm; } - public static class PublishTimelineExtension + public static ApiAppVM ToApiAppVM(this App vm) { - public static ApiPublishTimelineVM ToApiPublishTimelimeVM(this PublishTimeline timeline) + if (vm == null) return null; + + return new ApiAppVM { - if (timeline == null) - { - return null; - } - - return new ApiPublishTimelineVM - { - Id = timeline.Id, - Version = timeline.Version, - AppId = timeline.AppId, - Log = timeline.Log, - PublishTime = timeline.PublishTime, - PublishUserId = timeline.PublishUserId, - Env = timeline.Env - }; - } + Id = vm.Id, + Name = vm.Name, + Secret = vm.Secret, + Inheritanced = vm.Type == AppType.Inheritance, + Enabled = vm.Enabled, + AppAdmin = vm.AppAdmin, + Group = vm.Group, + CreateTime = vm.CreateTime + }; } +} - public static class ServerNodeExtension +public static class PublishTimelineExtension +{ + public static ApiPublishTimelineVM ToApiPublishTimelimeVM(this PublishTimeline timeline) { - public static ApiNodeVM ToApiNodeVM(this ServerNode node) + if (timeline == null) return null; + + return new ApiPublishTimelineVM { - if (node == null) - { - return null; - } - - return new ApiNodeVM - { - Address = node.Id, - Remark = node.Remark, - LastEchoTime = node.LastEchoTime, - Status = node.Status - }; - } + Id = timeline.Id, + Version = timeline.Version, + AppId = timeline.AppId, + Log = timeline.Log, + PublishTime = timeline.PublishTime, + PublishUserId = timeline.PublishUserId, + Env = timeline.Env + }; } +} - public static class ServiceInfoExtension +public static class ServerNodeExtension +{ + public static ApiNodeVM ToApiNodeVM(this ServerNode node) { - public static ApiServiceInfoVM ToApiServiceInfoVM(this ServiceInfo serviceInfo) + if (node == null) return null; + + return new ApiNodeVM { - if (serviceInfo == null) - { - return null; - } - - var vm = new ApiServiceInfoVM - { - ServiceId = serviceInfo.ServiceId, - ServiceName = serviceInfo.ServiceName, - Ip = serviceInfo.Ip, - Port = serviceInfo.Port, - MetaData = new List(), - Status = serviceInfo.Status - }; - - try - { - vm.MetaData = JsonConvert.DeserializeObject>(serviceInfo.MetaData); - } - catch - { - } - - return vm; - } + Address = node.Id, + Remark = node.Remark, + LastEchoTime = node.LastEchoTime, + Status = node.Status + }; } +} - public static class ConfigExtension +public static class ServiceInfoExtension +{ + public static ApiServiceInfoVM ToApiServiceInfoVM(this ServiceInfo serviceInfo) { - public static ApiConfigVM ToApiConfigVM(this Config config) + if (serviceInfo == null) return null; + + var vm = new ApiServiceInfoVM + { + ServiceId = serviceInfo.ServiceId, + ServiceName = serviceInfo.ServiceName, + Ip = serviceInfo.Ip, + Port = serviceInfo.Port, + MetaData = new List(), + Status = serviceInfo.Status + }; + + try + { + vm.MetaData = JsonConvert.DeserializeObject>(serviceInfo.MetaData); + } + catch { - if (config == null) - { - return null; - } - - var vm = new ApiConfigVM() - { - Id = config.Id, - AppId = config.AppId, - Group = config.Group, - Key = config.Key, - Value = config.Value, - Status = config.Status, - OnlineStatus = config.OnlineStatus, - EditStatus = config.EditStatus - }; - - return vm; } - } + return vm; + } } + +public static class ConfigExtension +{ + public static ApiConfigVM ToApiConfigVM(this Config config) + { + if (config == null) return null; + + var vm = new ApiConfigVM + { + Id = config.Id, + AppId = config.AppId, + Group = config.Group, + Key = config.Key, + Value = config.Value, + Status = config.Status, + OnlineStatus = config.OnlineStatus, + EditStatus = config.EditStatus + }; + + return vm; + } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/PublishLogVM.cs b/src/AgileConfig.Server.Apisite/Models/PublishLogVM.cs index 634709d1..7615c53e 100644 --- a/src/AgileConfig.Server.Apisite/Models/PublishLogVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/PublishLogVM.cs @@ -1,13 +1,12 @@ using System.Diagnostics.CodeAnalysis; -namespace AgileConfig.Server.Apisite.Models +namespace AgileConfig.Server.Apisite.Models; + +[ExcludeFromCodeCoverage] +public class PublishLogVM : IAppIdModel { - [ExcludeFromCodeCoverage] - public class PublishLogVM: IAppIdModel - { - public string AppId { get; set; } - public string Log { get; set; } + public string Log { get; set; } - public string[] Ids { get; set; } - } -} + public string[] Ids { get; set; } + public string AppId { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/RoleVM.cs b/src/AgileConfig.Server.Apisite/Models/RoleVM.cs index 8b3d6191..1fe8d4c0 100644 --- a/src/AgileConfig.Server.Apisite/Models/RoleVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/RoleVM.cs @@ -2,22 +2,18 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; -namespace AgileConfig.Server.Apisite.Models +namespace AgileConfig.Server.Apisite.Models; + +[ExcludeFromCodeCoverage] +public class RoleVM { - [ExcludeFromCodeCoverage] - public class RoleVM - { - public string Id { get; set; } + public string Id { get; set; } - [Required] - [MaxLength(128)] - public string Name { get; set; } + [Required] [MaxLength(128)] public string Name { get; set; } - [MaxLength(512)] - public string Description { get; set; } + [MaxLength(512)] public string Description { get; set; } - public bool IsSystem { get; set; } + public bool IsSystem { get; set; } - public List Functions { get; set; } - } -} + public List Functions { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/SaveJsonVM.cs b/src/AgileConfig.Server.Apisite/Models/SaveJsonVM.cs index 11731a2f..098a10c9 100644 --- a/src/AgileConfig.Server.Apisite/Models/SaveJsonVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/SaveJsonVM.cs @@ -1,28 +1,29 @@ using System.Diagnostics.CodeAnalysis; -namespace AgileConfig.Server.Apisite.Models +namespace AgileConfig.Server.Apisite.Models; + +[ExcludeFromCodeCoverage] +public class SaveJsonVM +{ + /// + /// Indicates whether to update existing configuration entries in patch mode. + /// true (patch mode): only modify the entries included in the current submission. + /// false (full mode, default): remove any existing entries that are not present in the current submission. + /// + public bool isPatch { get; set; } + + public string json { get; set; } +} + +[ExcludeFromCodeCoverage] +public class SaveKVListVM { - [ExcludeFromCodeCoverage] - public class SaveJsonVM - { - /// - /// Indicates whether to update existing configuration entries in patch mode. - /// true (patch mode): only modify the entries included in the current submission. - /// false (full mode, default): remove any existing entries that are not present in the current submission. - /// - public bool isPatch { get; set; } - public string json { get; set; } - } + /// + /// Indicates whether to update existing configuration entries in patch mode. + /// true (patch mode): only modify the entries included in the current submission. + /// false (full mode, default): remove any existing entries that are not present in the current submission. + /// + public bool isPatch { get; set; } - [ExcludeFromCodeCoverage] - public class SaveKVListVM - { - /// - /// Indicates whether to update existing configuration entries in patch mode. - /// true (patch mode): only modify the entries included in the current submission. - /// false (full mode, default): remove any existing entries that are not present in the current submission. - /// - public bool isPatch { get; set; } - public string str { get; set; } - } + public string str { get; set; } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/ServerNodeVM.cs b/src/AgileConfig.Server.Apisite/Models/ServerNodeVM.cs index 5fe98894..9151f419 100644 --- a/src/AgileConfig.Server.Apisite/Models/ServerNodeVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/ServerNodeVM.cs @@ -1,21 +1,20 @@ -using AgileConfig.Server.Data.Entity; -using System; +using System; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; +using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Apisite.Models +namespace AgileConfig.Server.Apisite.Models; + +[ExcludeFromCodeCoverage] +public class ServerNodeVM { - [ExcludeFromCodeCoverage] - public class ServerNodeVM - { - [Required(ErrorMessage = "节点地址不能为空")] - [MaxLength(100, ErrorMessage = "节点地址长度不能超过100位")] - public string Address { get; set; } + [Required(ErrorMessage = "节点地址不能为空")] + [MaxLength(100, ErrorMessage = "节点地址长度不能超过100位")] + public string Address { get; set; } - [MaxLength(50, ErrorMessage = "备注长度不能超过50位")] - public string Remark { get; set; } - public NodeStatus Status { get; set; } - public DateTime? LastEchoTime { get; set; } - } + [MaxLength(50, ErrorMessage = "备注长度不能超过50位")] + public string Remark { get; set; } -} + public NodeStatus Status { get; set; } + public DateTime? LastEchoTime { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/ServerStatusReport.cs b/src/AgileConfig.Server.Apisite/Models/ServerStatusReport.cs index 27e3fc43..e53abdfc 100644 --- a/src/AgileConfig.Server.Apisite/Models/ServerStatusReport.cs +++ b/src/AgileConfig.Server.Apisite/Models/ServerStatusReport.cs @@ -1,17 +1,16 @@ -using AgileConfig.Server.IService; -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; +using AgileConfig.Server.IService; -namespace AgileConfig.Server.Apisite.Models +namespace AgileConfig.Server.Apisite.Models; + +[ExcludeFromCodeCoverage] +public class ServerStatusReport { - [ExcludeFromCodeCoverage] - public class ServerStatusReport - { - public ClientInfos WebsocketCollectionReport { get; set; } + public ClientInfos WebsocketCollectionReport { get; set; } - public int AppCount { get; set; } + public int AppCount { get; set; } - public int ConfigCount { get; set; } + public int ConfigCount { get; set; } - public int NodeCount { get; set; } - } -} + public int NodeCount { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/ServiceInfoVM.cs b/src/AgileConfig.Server.Apisite/Models/ServiceInfoVM.cs index 0b5f0dc1..0113ba4a 100644 --- a/src/AgileConfig.Server.Apisite/Models/ServiceInfoVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/ServiceInfoVM.cs @@ -1,7 +1,7 @@ using System; -using AgileConfig.Server.Data.Entity; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; +using AgileConfig.Server.Data.Entity; namespace AgileConfig.Server.Apisite.Models; @@ -13,11 +13,11 @@ public class ServiceInfoVM [Required(ErrorMessage = "服务Id不能为空")] [MaxLength(100, ErrorMessage = "服务Id长度不能超过100")] public string ServiceId { get; set; } - + [Required(ErrorMessage = "服务名不能为空")] [MaxLength(100, ErrorMessage = "服务名长度不能超过100")] public string ServiceName { get; set; } - + [MaxLength(100, ErrorMessage = "IP长度不能超过100")] public string Ip { get; set; } @@ -30,11 +30,11 @@ public class ServiceInfoVM public DateTime? RegisterTime { get; set; } public DateTime? LastHeartBeat { get; set; } - + [Required(ErrorMessage = "健康检测模式不能为空")] [MaxLength(10, ErrorMessage = "健康检测模式长度不能超过10位")] public string HeartBeatMode { get; set; } - + [MaxLength(2000, ErrorMessage = "检测URL长度不能超过2000")] public string CheckUrl { get; set; } diff --git a/src/AgileConfig.Server.Apisite/Models/UserVM.cs b/src/AgileConfig.Server.Apisite/Models/UserVM.cs index 1764623f..de0e6bb8 100644 --- a/src/AgileConfig.Server.Apisite/Models/UserVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/UserVM.cs @@ -1,34 +1,31 @@ -using AgileConfig.Server.Data.Entity; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; +using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Apisite.Models -{ - [ExcludeFromCodeCoverage] - public class UserVM - { - [Required(ErrorMessage = "用户Id不能为空")] - [MaxLength(36, ErrorMessage = "用户Id长度不能超过36位")] - public string Id { get; set; } +namespace AgileConfig.Server.Apisite.Models; - [Required(ErrorMessage = "用户名不能为空")] - [MaxLength(50, ErrorMessage = "用户名长度不能超过50位")] - public string UserName { get; set; } +[ExcludeFromCodeCoverage] +public class UserVM +{ + [Required(ErrorMessage = "ûIdΪ")] + [MaxLength(36, ErrorMessage = "ûIdȲܳ36λ")] + public string Id { get; set; } - [Required(ErrorMessage = "密码不能为空")] - [MaxLength(50, ErrorMessage = "密码长度不能超过50位")] - public string Password { get; set; } + [Required(ErrorMessage = "ûΪ")] + [MaxLength(50, ErrorMessage = "ûȲܳ50λ")] + public string UserName { get; set; } - [MaxLength(50, ErrorMessage = "团队长度不能超过50位")] - public string Team { get; set; } + [Required(ErrorMessage = "벻Ϊ")] + [MaxLength(50, ErrorMessage = "볤Ȳܳ50λ")] + public string Password { get; set; } - public List UserRoleIds { get; set; } + [MaxLength(50, ErrorMessage = "ŶӳȲܳ50λ")] + public string Team { get; set; } - public List UserRoleNames { get; set; } + public List UserRoleIds { get; set; } - public List UserRoleCodes { get; set; } + public List UserRoleNames { get; set; } - public UserStatus Status { get; set; } - } -} + public UserStatus Status { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Program.cs b/src/AgileConfig.Server.Apisite/Program.cs index 9d3395ca..6ff4d3f6 100644 --- a/src/AgileConfig.Server.Apisite/Program.cs +++ b/src/AgileConfig.Server.Apisite/Program.cs @@ -1,4 +1,6 @@ -using AgileConfig.Server.Common; +using System; +using System.Reflection; +using AgileConfig.Server.Common; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; @@ -6,24 +8,22 @@ using OpenTelemetry.Exporter; using OpenTelemetry.Logs; using OpenTelemetry.Resources; -using System; -using System.Reflection; -namespace AgileConfig.Server.Apisite +namespace AgileConfig.Server.Apisite; + +public class Program { - public class Program - { - public const string AppName = "AgileConfig Server"; + public const string AppName = "AgileConfig Server"; - public static void Main(string[] args) - { - PrintBasicSysInfo(); + public static void Main(string[] args) + { + PrintBasicSysInfo(); - var builder = new ConfigurationBuilder() + var builder = new ConfigurationBuilder() .SetBasePath(AppDomain.CurrentDomain.BaseDirectory); #if DEBUG - Global.Config = - builder + Global.Config = + builder .AddJsonFile("appsettings.Development.json") .AddEnvironmentVariables() .Build(); @@ -31,58 +31,48 @@ public static void Main(string[] args) Global.Config = builder.AddJsonFile("appsettings.json").AddEnvironmentVariables().Build(); #endif - var host = CreateWebHostBuilder(args) - .Build(); - - host.Run(); - } + var host = CreateWebHostBuilder(args) + .Build(); - private static void PrintBasicSysInfo() - { - var appVer = Assembly.GetAssembly(typeof(Program))?.GetName()?.Version?.ToString(); - var basePath = AppDomain.CurrentDomain.BaseDirectory; + host.Run(); + } - Console.WriteLine(ASCII_FONT.Font.Render($"Agile Config")); - Console.WriteLine("Version: {0}", appVer); - Console.WriteLine("Path: {0}", basePath); - } + private static void PrintBasicSysInfo() + { + var appVer = Assembly.GetAssembly(typeof(Program))?.GetName()?.Version?.ToString(); + var basePath = AppDomain.CurrentDomain.BaseDirectory; - private static IWebHostBuilder CreateWebHostBuilder(string[] args) - { - return WebHost.CreateDefaultBuilder(args).ConfigureLogging( - (context, builder) => - { - AddOtlpLogging(builder); - } - ) - .UseConfiguration(Global.Config) - .UseStartup(); - } + Console.WriteLine(ASCII_FONT.Font.Render("Agile Config")); + Console.WriteLine("Version: {0}", appVer); + Console.WriteLine("Path: {0}", basePath); + } - private static void AddOtlpLogging(ILoggingBuilder builder) - { - if (string.IsNullOrEmpty(Appsettings.OtlpLogsEndpoint)) - { - return; - } + private static IWebHostBuilder CreateWebHostBuilder(string[] args) + { + return WebHost.CreateDefaultBuilder(args).ConfigureLogging((context, builder) => { AddOtlpLogging(builder); } + ) + .UseConfiguration(Global.Config) + .UseStartup(); + } - builder.AddOpenTelemetry(options => - { - options.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(Program.AppName - , null, null, string.IsNullOrEmpty(Appsettings.OtlpInstanceId), Appsettings.OtlpInstanceId) - ); - options - .AddOtlpExporter(expOp => - { - expOp.Protocol = Appsettings.OtlpLogsProtocol == "http" ? OtlpExportProtocol.HttpProtobuf : OtlpExportProtocol.Grpc; - expOp.Endpoint = new Uri(Appsettings.OtlpLogsEndpoint); - if (!string.IsNullOrEmpty(Appsettings.OtlpLogsHeaders)) - { - expOp.Headers = Appsettings.OtlpLogsHeaders; - } - }); - }); - } + private static void AddOtlpLogging(ILoggingBuilder builder) + { + if (string.IsNullOrEmpty(Appsettings.OtlpLogsEndpoint)) return; + builder.AddOpenTelemetry(options => + { + options.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(AppName + , null, null, string.IsNullOrEmpty(Appsettings.OtlpInstanceId), Appsettings.OtlpInstanceId) + ); + options + .AddOtlpExporter(expOp => + { + expOp.Protocol = Appsettings.OtlpLogsProtocol == "http" + ? OtlpExportProtocol.HttpProtobuf + : OtlpExportProtocol.Grpc; + expOp.Endpoint = new Uri(Appsettings.OtlpLogsEndpoint); + if (!string.IsNullOrEmpty(Appsettings.OtlpLogsHeaders)) expOp.Headers = Appsettings.OtlpLogsHeaders; + }); + }); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Startup.cs b/src/AgileConfig.Server.Apisite/Startup.cs index d7733114..6c498422 100644 --- a/src/AgileConfig.Server.Apisite/Startup.cs +++ b/src/AgileConfig.Server.Apisite/Startup.cs @@ -4,8 +4,11 @@ using AgileConfig.Server.Apisite.UIExtension; using AgileConfig.Server.Apisite.Websocket; using AgileConfig.Server.Common; +using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Common.RestClient; +using AgileConfig.Server.Data.Abstraction; using AgileConfig.Server.Data.Freesql; +using AgileConfig.Server.Data.Repository.Selector; using AgileConfig.Server.OIDC; using AgileConfig.Server.Service; using Microsoft.AspNetCore.Authentication.JwtBearer; @@ -17,169 +20,138 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; -using AgileConfig.Server.Data.Repository.Selector; -using AgileConfig.Server.Data.Abstraction; -using AgileConfig.Server.Common.EventBus; using OpenTelemetry.Resources; -namespace AgileConfig.Server.Apisite +namespace AgileConfig.Server.Apisite; + +public class Startup { - public class Startup + public Startup(IConfiguration configuration, ILoggerFactory loggerFactory) { - public Startup(IConfiguration configuration, ILoggerFactory loggerFactory) - { - Configuration = configuration; - Global.LoggerFactory = loggerFactory; + Configuration = configuration; + Global.LoggerFactory = loggerFactory; - SetTrustSSL(configuration); - } + SetTrustSSL(configuration); + } - private static bool IsTrustSSL(IConfiguration configuration) - { - var alwaysTrustSsl = configuration["alwaysTrustSsl"]; - if (!string.IsNullOrEmpty(alwaysTrustSsl) && alwaysTrustSsl.ToLower() == "true") - { - return true; - } + public IConfiguration Configuration { get; } - return false; - } + private static bool IsTrustSSL(IConfiguration configuration) + { + var alwaysTrustSsl = configuration["alwaysTrustSsl"]; + if (!string.IsNullOrEmpty(alwaysTrustSsl) && alwaysTrustSsl.ToLower() == "true") return true; - private static void SetTrustSSL(IConfiguration configuration) - { - if (IsTrustSSL(configuration)) - { - ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; - } - } + return false; + } - public IConfiguration Configuration - { - get; - } + private static void SetTrustSSL(IConfiguration configuration) + { + if (IsTrustSSL(configuration)) + ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; + } - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddDefaultHttpClient(IsTrustSSL(Configuration)); - services.AddRestClient(); + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddDefaultHttpClient(IsTrustSSL(Configuration)); + services.AddRestClient(); + + services.AddMemoryCache(); + + services.AddCors(); + services.AddMvc().AddRazorRuntimeCompilation().AddControllersAsServices(); + + if (Appsettings.IsPreviewMode) AddSwaggerService(services); + services.AddTinyEventBus(); - services.AddMemoryCache(); + services.AddEnvAccessor(); + services.AddDbConfigInfoFactory(); + services.AddFreeSqlFactory(); + // Add freesqlRepositories or other repositories + services.AddRepositories(); - services.AddCors(); - services.AddMvc().AddRazorRuntimeCompilation().AddControllersAsServices(); + services.AddBusinessServices(); - if (Appsettings.IsPreviewMode) - { - AddSwaggerService(services); - } - services.AddTinyEventBus(); + services.ConfigureOptions(); + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(); - services.AddEnvAccessor(); - services.AddDbConfigInfoFactory(); - services.AddFreeSqlFactory(); - // Add freesqlRepositories or other repositories - services.AddRepositories(); + services.AddHostedService(); + services.AddAntiforgery(o => o.SuppressXFrameOptionsHeader = true); - services.AddBusinessServices(); + services.AddOIDC(); + + services.AddMeterService(); + + services.AddOpenTelemetry() + .ConfigureResource(resource => resource.AddService(Program.AppName, + null, null, string.IsNullOrEmpty(Appsettings.OtlpInstanceId), Appsettings.OtlpInstanceId)) + .AddOtlpTraces() + .AddOtlpMetrics(); + + services.AddLocalization(options => options.ResourcesPath = "Resources"); + } - services.ConfigureOptions(); - services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddJwtBearer(); + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider) + { + var basePath = Configuration["pathBase"]; + if (!string.IsNullOrWhiteSpace(basePath)) app.UsePathBase(basePath); - services.AddHostedService(); - services.AddAntiforgery(o => o.SuppressXFrameOptionsHeader = true); + app.UseRequestLocalization(options => + { + var cultures = new[] { "en-US", "zh-CN" }; - services.AddOIDC(); + options.DefaultRequestCulture = new RequestCulture(cultures[0]); + options.AddSupportedCultures(cultures); + options.AddSupportedUICultures(cultures); + options.SetDefaultCulture(cultures[0]); - services.AddMeterService(); + // Configure request culture providers + options.RequestCultureProviders.Insert(0, new QueryStringRequestCultureProvider()); + options.RequestCultureProviders.Insert(1, new AcceptLanguageHeaderRequestCultureProvider()); + }); - services.AddOpenTelemetry() - .ConfigureResource(resource => resource.AddService(Program.AppName, - null, null, string.IsNullOrEmpty(Appsettings.OtlpInstanceId), Appsettings.OtlpInstanceId)) - .AddOtlpTraces() - .AddOtlpMetrics(); + if (env.IsDevelopment()) + app.UseDeveloperExceptionPage(); + else + app.UseMiddleware(); + if (Appsettings.IsPreviewMode) AddSwaggerMiddleWare(app); - services.AddLocalization(options => options.ResourcesPath = "Resources"); - } + app.UseMiddleware(); - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider) + app.UseCors(op => { - var basePath = Configuration["pathBase"]; - if (!string.IsNullOrWhiteSpace(basePath)) - { - app.UsePathBase(basePath); - } - - app.UseRequestLocalization(options => - { - var cultures = new[] {"en-US", "zh-CN"}; - - options.DefaultRequestCulture = new RequestCulture(cultures[0]); - options.AddSupportedCultures(cultures); - options.AddSupportedUICultures(cultures); - options.SetDefaultCulture(cultures[0]); - - // Configure request culture providers - options.RequestCultureProviders.Insert(0, new QueryStringRequestCultureProvider()); - options.RequestCultureProviders.Insert(1, new AcceptLanguageHeaderRequestCultureProvider()); - }); - - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseMiddleware(); - } - if (Appsettings.IsPreviewMode) - { - AddSwaggerMiddleWare(app); - } - - app.UseMiddleware(); - - app.UseCors(op => - { - op.AllowAnyOrigin(); - op.AllowAnyMethod(); - op.AllowAnyHeader(); - }); - app.UseWebSockets(new WebSocketOptions() - { - KeepAliveInterval = TimeSpan.FromSeconds(60), - }); - app.UseMiddleware(); - app.UseStaticFiles(); - app.UseRouting(); - app.UseAuthentication(); - app.UseAuthorization(); - app.UseEndpoints(endpoints => - { - endpoints.MapDefaultControllerRoute(); - }); - } - - private void AddSwaggerService(IServiceCollection services) + op.AllowAnyOrigin(); + op.AllowAnyMethod(); + op.AllowAnyHeader(); + }); + app.UseWebSockets(new WebSocketOptions { - services.AddSwaggerGen(c => - { - c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); - var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location); - var xmlPath = Path.Combine(basePath, "AgileConfig.Server.Apisite.xml"); - c.IncludeXmlComments(xmlPath); - }); - } - - private void AddSwaggerMiddleWare(IApplicationBuilder app) + KeepAliveInterval = TimeSpan.FromSeconds(60) + }); + app.UseMiddleware(); + app.UseStaticFiles(); + app.UseRouting(); + app.UseAuthentication(); + app.UseAuthorization(); + app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); + } + + private void AddSwaggerService(IServiceCollection services) + { + services.AddSwaggerGen(c => { - app.UseSwagger(); - app.UseSwaggerUI(c => - { - c.SwaggerEndpoint("v1/swagger.json", "My API V1"); - }); - } + c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); + var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location); + var xmlPath = Path.Combine(basePath, "AgileConfig.Server.Apisite.xml"); + c.IncludeXmlComments(xmlPath); + }); + } + + private void AddSwaggerMiddleWare(IApplicationBuilder app) + { + app.UseSwagger(); + app.UseSwaggerUI(c => { c.SwaggerEndpoint("v1/swagger.json", "My API V1"); }); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/StartupExtension.cs b/src/AgileConfig.Server.Apisite/StartupExtension.cs index 35f5ceaf..17e89915 100644 --- a/src/AgileConfig.Server.Apisite/StartupExtension.cs +++ b/src/AgileConfig.Server.Apisite/StartupExtension.cs @@ -1,98 +1,81 @@ -using AgileConfig.Server.Common; +using System; +using System.Net.Http; +using AgileConfig.Server.Apisite.Metrics; +using AgileConfig.Server.Common; using Microsoft.Extensions.DependencyInjection; +using Npgsql; +using OpenTelemetry; +using OpenTelemetry.Exporter; using OpenTelemetry.Metrics; -using System.Net.Http; using OpenTelemetry.Trace; -using OpenTelemetry.Exporter; -using OpenTelemetry; -using Npgsql; -using AgileConfig.Server.Apisite.Metrics; -namespace AgileConfig.Server.Apisite +namespace AgileConfig.Server.Apisite; + +public static class StartupExtension { - public static class StartupExtension + public static void AddDefaultHttpClient(this IServiceCollection services, bool isTrustSsl) { - public static void AddDefaultHttpClient(this IServiceCollection services, bool isTrustSsl) - { - services.AddHttpClient(Global.DefaultHttpClientName) - .ConfigurePrimaryHttpMessageHandler(() => - { - return NewMessageHandler(isTrustSsl); - }) - ; - } + services.AddHttpClient(Global.DefaultHttpClientName) + .ConfigurePrimaryHttpMessageHandler(() => { return NewMessageHandler(isTrustSsl); }) + ; + } - public static IOpenTelemetryBuilder AddOtlpTraces(this IOpenTelemetryBuilder builder) - { - if (string.IsNullOrEmpty(Appsettings.OtlpTracesEndpoint)) + public static IOpenTelemetryBuilder AddOtlpTraces(this IOpenTelemetryBuilder builder) + { + if (string.IsNullOrEmpty(Appsettings.OtlpTracesEndpoint)) return builder; + + builder.WithTracing(tracing => tracing + .AddAspNetCoreInstrumentation() + .AddNpgsql() + .AddOtlpExporter(op => { - return builder; - } + op.Protocol = Appsettings.OtlpTracesProtocol == "http" + ? OtlpExportProtocol.HttpProtobuf + : OtlpExportProtocol.Grpc; + op.Endpoint = new Uri(Appsettings.OtlpTracesEndpoint); + if (!string.IsNullOrEmpty(Appsettings.OtlpTracesHeaders)) op.Headers = Appsettings.OtlpTracesHeaders; + }) + ); - builder.WithTracing(tracing => tracing - .AddAspNetCoreInstrumentation() - .AddNpgsql() - .AddOtlpExporter(op => - { - op.Protocol = Appsettings.OtlpTracesProtocol == "http" ? OtlpExportProtocol.HttpProtobuf : OtlpExportProtocol.Grpc; - op.Endpoint = new System.Uri(Appsettings.OtlpTracesEndpoint); - if (!string.IsNullOrEmpty(Appsettings.OtlpTracesHeaders)) - { - op.Headers = Appsettings.OtlpTracesHeaders; - } - }) - ); + return builder; + } - return builder; - } + public static IOpenTelemetryBuilder AddOtlpMetrics(this IOpenTelemetryBuilder builder) + { + if (string.IsNullOrEmpty(Appsettings.OtlpMetricsEndpoint)) return builder; - public static IOpenTelemetryBuilder AddOtlpMetrics(this IOpenTelemetryBuilder builder) - { - if (string.IsNullOrEmpty(Appsettings.OtlpMetricsEndpoint)) + builder.WithMetrics(metrics => metrics + .AddAspNetCoreInstrumentation() + .AddRuntimeInstrumentation() + .AddMeter(MeterService.MeterName) + .AddOtlpExporter((op, reader) => { - return builder; - } - - builder.WithMetrics(metrics => metrics - .AddAspNetCoreInstrumentation() - .AddRuntimeInstrumentation() - .AddMeter(MeterService.MeterName) - .AddOtlpExporter((op, reader) => - { - op.Protocol = Appsettings.OtlpMetricsProtocol == "http" ? OtlpExportProtocol.HttpProtobuf : OtlpExportProtocol.Grpc; - op.Endpoint = new System.Uri(Appsettings.OtlpMetricsEndpoint); - reader.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds = 1000; - if (!string.IsNullOrEmpty(Appsettings.OtlpMetricsHeaders)) - { - op.Headers = Appsettings.OtlpMetricsHeaders; - } - }) - ); + op.Protocol = Appsettings.OtlpMetricsProtocol == "http" + ? OtlpExportProtocol.HttpProtobuf + : OtlpExportProtocol.Grpc; + op.Endpoint = new Uri(Appsettings.OtlpMetricsEndpoint); + reader.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds = 1000; + if (!string.IsNullOrEmpty(Appsettings.OtlpMetricsHeaders)) op.Headers = Appsettings.OtlpMetricsHeaders; + }) + ); - return builder; - } + return builder; + } - public static IServiceCollection AddMeterService(this IServiceCollection services) - { - if (!string.IsNullOrEmpty(Appsettings.OtlpMetricsEndpoint)) - { - services.AddResourceMonitoring(); - } + public static IServiceCollection AddMeterService(this IServiceCollection services) + { + if (!string.IsNullOrEmpty(Appsettings.OtlpMetricsEndpoint)) services.AddResourceMonitoring(); - services.AddSingleton(); + services.AddSingleton(); - return services; - } + return services; + } - static HttpMessageHandler NewMessageHandler(bool alwaysTrustSsl) - { - var handler = new HttpClientHandler(); - if (alwaysTrustSsl) - { - handler.ServerCertificateCustomValidationCallback = (_, _, _, _) => true; - } + private static HttpMessageHandler NewMessageHandler(bool alwaysTrustSsl) + { + var handler = new HttpClientHandler(); + if (alwaysTrustSsl) handler.ServerCertificateCustomValidationCallback = (_, _, _, _) => true; - return handler; - } + return handler; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/UIExtension/ReactUIMiddleware.cs b/src/AgileConfig.Server.Apisite/UIExtension/ReactUIMiddleware.cs index 27a68df6..7d4fc49c 100644 --- a/src/AgileConfig.Server.Apisite/UIExtension/ReactUIMiddleware.cs +++ b/src/AgileConfig.Server.Apisite/UIExtension/ReactUIMiddleware.cs @@ -1,182 +1,167 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using System; -using System.IO; +using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; -using Microsoft.Extensions.Primitives; -using System.Collections.Concurrent; using AgileConfig.Server.Common; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; -namespace AgileConfig.Server.Apisite.UIExtension +namespace AgileConfig.Server.Apisite.UIExtension; + +public class ReactUiMiddleware { - public class ReactUiMiddleware + private static string _uiDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot/ui"); + private static readonly ConcurrentDictionary StaticFilesCache = new(); + private readonly ILogger _logger; + private readonly RequestDelegate _next; + + public ReactUiMiddleware( + RequestDelegate next, + ILoggerFactory loggerFactory, + IWebHostEnvironment environment + ) { - private static string _uiDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot/ui"); - private static readonly ConcurrentDictionary StaticFilesCache = new(); - private readonly RequestDelegate _next; - private readonly ILogger _logger; - - public ReactUiMiddleware( - RequestDelegate next, - ILoggerFactory loggerFactory, - IWebHostEnvironment environment - ) - { - _next = next; - _logger = loggerFactory. - CreateLogger(); + _next = next; + _logger = loggerFactory.CreateLogger(); #if DEBUG - //if debug mode, try to switch to project wwwwroot dir - var projectUiPath = Path.Combine(environment.ContentRootPath, "wwwroot/ui"); - if (Directory.Exists(projectUiPath)) - { - _uiDirectory = projectUiPath; - } + //if debug mode, try to switch to project wwwwroot dir + var projectUiPath = Path.Combine(environment.ContentRootPath, "wwwroot/ui"); + if (Directory.Exists(projectUiPath)) _uiDirectory = projectUiPath; #endif - } + } - private bool IsAdminConsoleMode => "true".Equals(Global.Config["adminConsole"], StringComparison.OrdinalIgnoreCase); + private bool IsAdminConsoleMode => "true".Equals(Global.Config["adminConsole"], StringComparison.OrdinalIgnoreCase); - private static bool ShouldHandleUiRequest(HttpContext context) - { - return context.Request.Path is { HasValue: true, Value: not null } && context.Request.Path.Value.Equals("/ui", StringComparison.OrdinalIgnoreCase); - } + private static bool ShouldHandleUiRequest(HttpContext context) + { + return context.Request.Path is { HasValue: true, Value: not null } && + context.Request.Path.Value.Equals("/ui", StringComparison.OrdinalIgnoreCase); + } - private bool ShouldHandleUiStaticFilesRequest(HttpContext context) + private bool ShouldHandleUiStaticFilesRequest(HttpContext context) + { + // Treat requests whose referer is 0.0.0.0/ui as static assets required by the React UI. + if (context.Request.Path is { HasValue: true, Value: not null } && context.Request.Path.Value.Contains('.')) { - // Treat requests whose referer is 0.0.0.0/ui as static assets required by the React UI. - if (context.Request.Path is { HasValue: true, Value: not null } && context.Request.Path.Value.Contains('.')) + context.Request.Headers.TryGetValue("Referer", out var refererValues); + if (refererValues.Any()) { - context.Request.Headers.TryGetValue("Referer", out StringValues refererValues); - if (refererValues.Any()) - { - var refererValue = refererValues.First(); - if (refererValue.EndsWith("/ui", StringComparison.OrdinalIgnoreCase) - || refererValue.Contains("/monaco-editor/", StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } + var refererValue = refererValues.First(); + if (refererValue.EndsWith("/ui", StringComparison.OrdinalIgnoreCase) + || refererValue.Contains("/monaco-editor/", StringComparison.OrdinalIgnoreCase)) + return true; } - - return false; } - /// - /// To support path base, make injected JS and CSS in index.html use relative paths by removing the leading slash. - /// - /// Absolute path to the index.html file to rewrite. - /// Task that completes when the file has been rewritten. - private async Task RewriteIndexHtml(string filePath) + return false; + } + + /// + /// To support path base, make injected JS and CSS in index.html use relative paths by removing the leading slash. + /// + /// Absolute path to the index.html file to rewrite. + /// Task that completes when the file has been rewritten. + private async Task RewriteIndexHtml(string filePath) + { + var rows = await File.ReadAllLinesAsync(filePath); + for (var i = 0; i < rows.Length; i++) { - var rows = await File.ReadAllLinesAsync(filePath); - for (int i = 0; i < rows.Length; i++) - { - var line = rows[i]; - if (line.Contains("window.resourceBaseUrl = \"/\"")) - { - if (!string.IsNullOrWhiteSpace(Appsettings.PathBase)) - { - line = line.Replace("/", $"{Appsettings.PathBase}/"); - rows[i] = line; - } - } - if (line.Contains("= lastModified) { - var lastModified = uiFile.LastModified; - if (DateTime.TryParse(values.First(), out DateTime ifModifiedSince) && ifModifiedSince >= lastModified) - { - context.Response.StatusCode = 304; - return; - } + context.Response.StatusCode = 304; + return; } - - context.Response.OnStarting(() => - { - context.Response.ContentType = uiFile.ContentType; - context.Response.Headers.TryAdd("last-modified", uiFile.LastModified.ToString("R")); - return Task.CompletedTask; - }); - - await context.Response.StartAsync(); - //output the file bytes - await context.Response.BodyWriter.WriteAsync(uiFile.Data); } + + context.Response.OnStarting(() => + { + context.Response.ContentType = uiFile.ContentType; + context.Response.Headers.TryAdd("last-modified", uiFile.LastModified.ToString("R")); + return Task.CompletedTask; + }); + + await context.Response.StartAsync(); + //output the file bytes + await context.Response.BodyWriter.WriteAsync(uiFile.Data); } } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/UIExtension/UIFileBag.cs b/src/AgileConfig.Server.Apisite/UIExtension/UIFileBag.cs index 0528f67e..06651294 100644 --- a/src/AgileConfig.Server.Apisite/UIExtension/UIFileBag.cs +++ b/src/AgileConfig.Server.Apisite/UIExtension/UIFileBag.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using NuGet.Common; namespace AgileConfig.Server.Apisite.UIExtension; @@ -8,21 +7,22 @@ internal class UiFileBag { private static readonly Dictionary ContentTypesMap = new() { - {".html", "text/html; charset=utf-8"}, - {".css", "text/css; charset=utf-8"}, - {".js", "application/javascript"}, - {".png", "image/png"}, - {".svg", "image/svg+xml"}, - {".json","application/json;charset=utf-8"}, - {".ico","image/x-icon"}, - {".ttf","application/octet-stream"}, - {".otf","font/otf"}, - {".woff","font/x-woff"}, - {".woff2","application/octet-stream"}, + { ".html", "text/html; charset=utf-8" }, + { ".css", "text/css; charset=utf-8" }, + { ".js", "application/javascript" }, + { ".png", "image/png" }, + { ".svg", "image/svg+xml" }, + { ".json", "application/json;charset=utf-8" }, + { ".ico", "image/x-icon" }, + { ".ttf", "application/octet-stream" }, + { ".otf", "font/otf" }, + { ".woff", "font/x-woff" }, + { ".woff2", "application/octet-stream" } }; + public string FilePath { get; init; } public byte[] Data { get; init; } - + public string ExtType { get; init; } public string ContentType => ContentTypesMap[ExtType]; diff --git a/src/AgileConfig.Server.Apisite/Utilites/ControllerExt.cs b/src/AgileConfig.Server.Apisite/Utilites/ControllerExt.cs index c4a0d5c3..8193833e 100644 --- a/src/AgileConfig.Server.Apisite/Utilites/ControllerExt.cs +++ b/src/AgileConfig.Server.Apisite/Utilites/ControllerExt.cs @@ -1,51 +1,51 @@ -using AgileConfig.Server.Common; +using System.Linq; +using System.Threading.Tasks; +using AgileConfig.Server.Common; +using AgileConfig.Server.Data.Entity; using AgileConfig.Server.IService; using Microsoft.AspNetCore.Mvc; -using System.Linq; -using System.Threading.Tasks; -namespace AgileConfig.Server.Apisite.Utilites +namespace AgileConfig.Server.Apisite.Utilites; + +public static class ControllerExt { - public static class ControllerExt + /// + /// Retrieve the current user name, preferring claims and falling back to basic authentication. + /// + /// Controller instance. + /// User name of the current principal. + public static string GetCurrentUserName(this Controller ctrl) { - /// - /// Retrieve the current user name, preferring claims and falling back to basic authentication. - /// - /// Controller instance. - /// User name of the current principal. - public static string GetCurrentUserName(this Controller ctrl) + var name = ctrl.HttpContext.GetUserNameFromClaim(); + if (string.IsNullOrEmpty(name)) { - var name = ctrl.HttpContext.GetUserNameFromClaim(); - if (string.IsNullOrEmpty(name)) - { - var result = ctrl.Request.GetUserNamePasswordFromBasicAuthorization(); - name = result.Item1; - } - - return name; + var result = ctrl.Request.GetUserNamePasswordFromBasicAuthorization(); + name = result.Item1; } - /// - /// Retrieve the current user ID, preferring claims and falling back to a database lookup using basic authentication. - /// - /// Controller instance. - /// User service used to resolve credentials when claims are absent. - /// User identifier of the current principal. - public static async Task GetCurrentUserId(this Controller ctrl, IUserService userService) + return name; + } + + /// + /// Retrieve the current user ID, preferring claims and falling back to a database lookup using basic authentication. + /// + /// Controller instance. + /// User service used to resolve credentials when claims are absent. + /// User identifier of the current principal. + public static async Task GetCurrentUserId(this Controller ctrl, IUserService userService) + { + var userId = ctrl.HttpContext.GetUserIdFromClaim(); + if (string.IsNullOrEmpty(userId)) { - var userId = ctrl.HttpContext.GetUserIdFromClaim(); - if (string.IsNullOrEmpty(userId)) + var result = ctrl.Request.GetUserNamePasswordFromBasicAuthorization(); + if (!string.IsNullOrEmpty(result.Item1)) { - var result = ctrl.Request.GetUserNamePasswordFromBasicAuthorization(); - if (!string.IsNullOrEmpty(result.Item1)) - { - var user =(await userService.GetUsersByNameAsync(result.Item1)).FirstOrDefault(x=>x.Status == Data.Entity.UserStatus.Normal); - userId = user?.Id; - } + var user = (await userService.GetUsersByNameAsync(result.Item1)).FirstOrDefault(x => + x.Status == UserStatus.Normal); + userId = user?.Id; } - - return userId; } + return userId; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Utilites/IPExt.cs b/src/AgileConfig.Server.Apisite/Utilites/IPExt.cs index daa1343c..f870a650 100644 --- a/src/AgileConfig.Server.Apisite/Utilites/IPExt.cs +++ b/src/AgileConfig.Server.Apisite/Utilites/IPExt.cs @@ -3,36 +3,31 @@ using System.Net.NetworkInformation; using System.Net.Sockets; -namespace AgileConfig.Server.Apisite.Utilites +namespace AgileConfig.Server.Apisite.Utilites; + +public class IpExt { - public class IpExt + public static string[] GetEndpointIp() { - public static string[] GetEndpointIp() - { - var addressIpv4Hosts = NetworkInterface.GetAllNetworkInterfaces() - - .OrderByDescending(c => c.Speed) - .Where(c => c.NetworkInterfaceType != NetworkInterfaceType.Loopback && c.OperationalStatus == OperationalStatus.Up); + var addressIpv4Hosts = NetworkInterface.GetAllNetworkInterfaces() + .OrderByDescending(c => c.Speed) + .Where(c => c.NetworkInterfaceType != NetworkInterfaceType.Loopback && + c.OperationalStatus == OperationalStatus.Up); - var ips = new List(); - foreach (var item in addressIpv4Hosts) + var ips = new List(); + foreach (var item in addressIpv4Hosts) + if (item.Supports(NetworkInterfaceComponent.IPv4)) { - if (item.Supports(NetworkInterfaceComponent.IPv4)) - { - var props = item.GetIPProperties(); - //this is ip for ipv4 - var firstIpV4Address = props.UnicastAddresses? - .Where(c => c.Address.AddressFamily == AddressFamily.InterNetwork) - .Select(c => c.Address) - .FirstOrDefault()?.ToString(); + var props = item.GetIPProperties(); + //this is ip for ipv4 + var firstIpV4Address = props.UnicastAddresses? + .Where(c => c.Address.AddressFamily == AddressFamily.InterNetwork) + .Select(c => c.Address) + .FirstOrDefault()?.ToString(); - if (!string.IsNullOrEmpty(firstIpV4Address)) - { - ips.Add(firstIpV4Address); - } - } + if (!string.IsNullOrEmpty(firstIpV4Address)) ips.Add(firstIpV4Address); } - return ips.ToArray(); - } + + return ips.ToArray(); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Websocket/IWebsocketCollection.cs b/src/AgileConfig.Server.Apisite/Websocket/IWebsocketCollection.cs index 0bcead22..0d75fd8d 100644 --- a/src/AgileConfig.Server.Apisite/Websocket/IWebsocketCollection.cs +++ b/src/AgileConfig.Server.Apisite/Websocket/IWebsocketCollection.cs @@ -1,38 +1,35 @@ -using Agile.Config.Protocol; -using AgileConfig.Server.IService; -using System.Net.WebSockets; +using System.Net.WebSockets; using System.Threading.Tasks; +using Agile.Config.Protocol; +using AgileConfig.Server.IService; -namespace AgileConfig.Server.Apisite.Websocket -{ - public class WebsocketClient : ClientInfo - { - public WebSocket Client { get; set; } - } - - public interface IWebsocketCollection - { - ClientInfos Report(); +namespace AgileConfig.Server.Apisite.Websocket; - int Count { get; } - WebsocketClient Get(string clientId); - void SendMessageToAll(string message); - Task SendMessageToOne(WebsocketClient client, string message); +public class WebsocketClient : ClientInfo +{ + public WebSocket Client { get; set; } +} - Task SendActionToOne(WebsocketClient client, WebsocketAction action); - void AddClient(WebsocketClient client); +public interface IWebsocketCollection +{ + int Count { get; } + ClientInfos Report(); + WebsocketClient Get(string clientId); + void SendMessageToAll(string message); + Task SendMessageToOne(WebsocketClient client, string message); - Task RemoveClient(WebsocketClient client, WebSocketCloseStatus? closeStatus, string closeDesc); + Task SendActionToOne(WebsocketClient client, WebsocketAction action); + void AddClient(WebsocketClient client); - void RemoveAppClients(string appId, WebSocketCloseStatus? closeStatus, string closeDesc); + Task RemoveClient(WebsocketClient client, WebSocketCloseStatus? closeStatus, string closeDesc); - void SendActionToAppClients(string appId,string env, WebsocketAction action); + void RemoveAppClients(string appId, WebSocketCloseStatus? closeStatus, string closeDesc); - void SendActionToAll(WebsocketAction action); + void SendActionToAppClients(string appId, string env, WebsocketAction action); - void SendToAppClients(string appId, string message); + void SendActionToAll(WebsocketAction action); - void Clear(); - } + void SendToAppClients(string appId, string message); -} + void Clear(); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/MessageHandler.cs b/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/MessageHandler.cs index 766d389c..1ed0ccc6 100644 --- a/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/MessageHandler.cs +++ b/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/MessageHandler.cs @@ -12,7 +12,7 @@ namespace AgileConfig.Server.Apisite.Websocket.MessageHandlers; /// -/// Message handler. +/// Message handler. /// internal class MessageHandler : IMessageHandler { @@ -20,8 +20,6 @@ internal class MessageHandler : IMessageHandler private readonly IRegisterCenterService _registerCenterService; private readonly IServiceInfoService _serviceInfoService; - private int ClientVersion { get; set; } - public MessageHandler( IConfigService configService, IRegisterCenterService registerCenterService, @@ -32,13 +30,12 @@ public MessageHandler( _serviceInfoService = serviceInfoService; } + private int ClientVersion { get; set; } + public bool Hit(HttpRequest request) { var ver = request.Headers["client-v"]; - if (string.IsNullOrEmpty(ver)) - { - return false; - } + if (string.IsNullOrEmpty(ver)) return false; if (int.TryParse(ver.ToString().Replace(".", ""), out var verInt)) { @@ -49,12 +46,7 @@ public bool Hit(HttpRequest request) return false; } - private async Task SendMessage(WebSocket webSocket, string message) - { - var data = Encoding.UTF8.GetBytes(message); - await webSocket.SendAsync(new ArraySegment(data, 0, data.Length), WebSocketMessageType.Text, true, - CancellationToken.None); - } + public async Task Handle(string message, HttpRequest request, WebsocketClient client) { message ??= ""; @@ -70,7 +62,7 @@ public async Task Handle(string message, HttpRequest request, WebsocketClient cl var data = await GetCPingData(appId, env); - await SendMessage(client.Client, JsonConvert.SerializeObject(new WebsocketAction() + await SendMessage(client.Client, JsonConvert.SerializeObject(new WebsocketAction { Action = ActionConst.Ping, Module = ActionModule.ConfigCenter, @@ -85,7 +77,7 @@ await SendMessage(client.Client, JsonConvert.SerializeObject(new WebsocketAction if (heartBeatResult) { var version = await _serviceInfoService.ServicesMD5Cache(); - await SendMessage(client.Client, JsonConvert.SerializeObject(new WebsocketAction() + await SendMessage(client.Client, JsonConvert.SerializeObject(new WebsocketAction { Action = ActionConst.Ping, Module = ActionModule.RegisterCenter, @@ -105,6 +97,13 @@ await SendMessage(client.Client, JsonConvert.SerializeObject(new WebsocketAction } } + private async Task SendMessage(WebSocket webSocket, string message) + { + var data = Encoding.UTF8.GetBytes(message); + await webSocket.SendAsync(new ArraySegment(data, 0, data.Length), WebSocketMessageType.Text, true, + CancellationToken.None); + } + private async Task GetCPingData(string appId, string env) { if (ClientVersion <= 176) @@ -114,12 +113,10 @@ private async Task GetCPingData(string appId, string env) return md5; } - else - { - // For versions 1.7.7 and later, respond with the publish timeline id. - var publishTimeLineId = await _configService.GetLastPublishTimelineVirtualIdAsyncWithCache(appId, env); - return publishTimeLineId; - } + // For versions 1.7.7 and later, respond with the publish timeline id. + var publishTimeLineId = await _configService.GetLastPublishTimelineVirtualIdAsyncWithCache(appId, env); + + return publishTimeLineId; } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/OldMessageHandler.cs b/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/OldMessageHandler.cs index e03b9e6f..d6f1536f 100644 --- a/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/OldMessageHandler.cs +++ b/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/OldMessageHandler.cs @@ -8,36 +8,27 @@ namespace AgileConfig.Server.Apisite.Websocket.MessageHandlers; - - /// -/// Message handler used to remain compatible with legacy clients. +/// Message handler used to remain compatible with legacy clients. /// internal class OldMessageHandler : IMessageHandler { private readonly IConfigService _configService; + public OldMessageHandler(IConfigService configService) { _configService = configService; } - + public bool Hit(HttpRequest request) { var ver = request.Headers["client-v"]; return string.IsNullOrEmpty(ver); } - private async Task SendMessage(WebSocket webSocket, string message) - { - var data = Encoding.UTF8.GetBytes(message); - await webSocket.SendAsync(new ArraySegment(data, 0, data.Length), WebSocketMessageType.Text, true, - CancellationToken.None); - } + public async Task Handle(string message, HttpRequest request, WebsocketClient client) { - if (message == null) - { - message = ""; - } + if (message == null) message = ""; if (message == "ping") { @@ -55,4 +46,11 @@ public async Task Handle(string message, HttpRequest request, WebsocketClient cl await SendMessage(client.Client, "0"); } } + + private async Task SendMessage(WebSocket webSocket, string message) + { + var data = Encoding.UTF8.GetBytes(message); + await webSocket.SendAsync(new ArraySegment(data, 0, data.Length), WebSocketMessageType.Text, true, + CancellationToken.None); + } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/WebsocketMessageHandlers.cs b/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/WebsocketMessageHandlers.cs index a537bcf0..7ad97655 100644 --- a/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/WebsocketMessageHandlers.cs +++ b/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/WebsocketMessageHandlers.cs @@ -6,6 +6,7 @@ namespace AgileConfig.Server.Apisite.Websocket.MessageHandlers; public class WebsocketMessageHandlers { public readonly List MessageHandlers; + public WebsocketMessageHandlers(IConfigService configService, IRegisterCenterService registerCenterService, IServiceInfoService serviceInfoService) { diff --git a/src/AgileConfig.Server.Apisite/Websocket/WebsocketCollection.cs b/src/AgileConfig.Server.Apisite/Websocket/WebsocketCollection.cs index c1d092ec..cfadb91d 100644 --- a/src/AgileConfig.Server.Apisite/Websocket/WebsocketCollection.cs +++ b/src/AgileConfig.Server.Apisite/Websocket/WebsocketCollection.cs @@ -1,214 +1,177 @@ -using Agile.Config.Protocol; -using AgileConfig.Server.IService; -using Newtonsoft.Json; -using System; +using System; using System.Collections.Concurrent; using System.Linq; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; +using Agile.Config.Protocol; +using AgileConfig.Server.IService; +using Newtonsoft.Json; + +namespace AgileConfig.Server.Apisite.Websocket; -namespace AgileConfig.Server.Apisite.Websocket +public class WebsocketCollection : IWebsocketCollection { - public class WebsocketCollection : IWebsocketCollection + private readonly ConcurrentDictionary _clients = new(); + + static WebsocketCollection() { - private WebsocketCollection() - { - } + Instance = new WebsocketCollection(); + } - static WebsocketCollection() - { - Instance = new WebsocketCollection(); - } + private WebsocketCollection() + { + } - private readonly ConcurrentDictionary _clients = new(); + public static IWebsocketCollection Instance { get; private set; } - public void SendMessageToAll(string message) - { - if (_clients.Count == 0) - { - return; - } - var data = Encoding.UTF8.GetBytes(message); - foreach (var webSocket in _clients) - { - if (webSocket.Value.Client.State == WebSocketState.Open) - { - webSocket.Value.Client.SendAsync(new ArraySegment(data, 0, data.Length), WebSocketMessageType.Text, true, - CancellationToken.None); - } - } - } + public void SendMessageToAll(string message) + { + if (_clients.Count == 0) return; + var data = Encoding.UTF8.GetBytes(message); + foreach (var webSocket in _clients) + if (webSocket.Value.Client.State == WebSocketState.Open) + webSocket.Value.Client.SendAsync(new ArraySegment(data, 0, data.Length), + WebSocketMessageType.Text, true, + CancellationToken.None); + } - public void SendToAppClients(string appId, string message) - { - if (_clients.IsEmpty) - { - return; - } - var appClients = _clients.Values.Where(c => c.AppId == appId); - if (!appClients.Any()) - { - return; - } - var data = Encoding.UTF8.GetBytes(message); - foreach (var webSocket in appClients) - { - if (webSocket.AppId == appId && webSocket.Client.State == WebSocketState.Open) - { - webSocket.Client.SendAsync(new ArraySegment(data, 0, data.Length), WebSocketMessageType.Text, true, - CancellationToken.None); - } - } - } + public void SendToAppClients(string appId, string message) + { + if (_clients.IsEmpty) return; + var appClients = _clients.Values.Where(c => c.AppId == appId); + if (!appClients.Any()) return; + var data = Encoding.UTF8.GetBytes(message); + foreach (var webSocket in appClients) + if (webSocket.AppId == appId && webSocket.Client.State == WebSocketState.Open) + webSocket.Client.SendAsync(new ArraySegment(data, 0, data.Length), WebSocketMessageType.Text, + true, + CancellationToken.None); + } - public void SendActionToAppClients(string appId,string env, WebsocketAction action) - { - if (_clients.IsEmpty) - { - return; - } - var appClients = _clients.Values.Where(c => c.AppId == appId && c.Env == env); - if (!appClients.Any()) - { - return; - } - var json = JsonConvert.SerializeObject(action); - var data = Encoding.UTF8.GetBytes(json); - foreach (var webSocket in appClients) - { - if (webSocket.AppId == appId && webSocket.Client.State == WebSocketState.Open) - { - webSocket.Client.SendAsync(new ArraySegment(data, 0, data.Length), WebSocketMessageType.Text, true, - CancellationToken.None); - } - } - } + public void SendActionToAppClients(string appId, string env, WebsocketAction action) + { + if (_clients.IsEmpty) return; + var appClients = _clients.Values.Where(c => c.AppId == appId && c.Env == env); + if (!appClients.Any()) return; + var json = JsonConvert.SerializeObject(action); + var data = Encoding.UTF8.GetBytes(json); + foreach (var webSocket in appClients) + if (webSocket.AppId == appId && webSocket.Client.State == WebSocketState.Open) + webSocket.Client.SendAsync(new ArraySegment(data, 0, data.Length), WebSocketMessageType.Text, + true, + CancellationToken.None); + } - public async Task SendMessageToOne(WebsocketClient client, string message) + public async Task SendMessageToOne(WebsocketClient client, string message) + { + if (client.Client.State == WebSocketState.Open) { - if (client.Client.State == WebSocketState.Open) - { - var data = Encoding.UTF8.GetBytes(message); - await client.Client.SendAsync(new ArraySegment(data, 0, data.Length), WebSocketMessageType.Text, true, - CancellationToken.None); - } + var data = Encoding.UTF8.GetBytes(message); + await client.Client.SendAsync(new ArraySegment(data, 0, data.Length), WebSocketMessageType.Text, true, + CancellationToken.None); } + } - public async Task SendActionToOne(WebsocketClient client, WebsocketAction action) + public async Task SendActionToOne(WebsocketClient client, WebsocketAction action) + { + if (client.Client.State == WebSocketState.Open) { - if (client.Client.State == WebSocketState.Open) - { - var json = JsonConvert.SerializeObject(action); - var data = Encoding.UTF8.GetBytes(json); - await client.Client.SendAsync(new ArraySegment(data, 0, data.Length), WebSocketMessageType.Text, true, - CancellationToken.None); - } + var json = JsonConvert.SerializeObject(action); + var data = Encoding.UTF8.GetBytes(json); + await client.Client.SendAsync(new ArraySegment(data, 0, data.Length), WebSocketMessageType.Text, true, + CancellationToken.None); } + } - public void AddClient(WebsocketClient client) - { - _clients.TryAdd(client.Id, client); - } + public void AddClient(WebsocketClient client) + { + _clients.TryAdd(client.Id, client); + } - public async Task RemoveClient(WebsocketClient client, WebSocketCloseStatus? closeStatus, string closeDesc = null) + public async Task RemoveClient(WebsocketClient client, WebSocketCloseStatus? closeStatus, string closeDesc = null) + { + if (_clients.TryRemove(client.Id, out var tryRemoveClient) && client.Client.State == WebSocketState.Open) { - if (_clients.TryRemove(client.Id, out WebsocketClient tryRemoveClient) && client.Client.State == WebSocketState.Open) - { - await client.Client.CloseAsync(closeStatus ?? WebSocketCloseStatus.Empty, closeDesc, CancellationToken.None); - client.Client.Dispose(); - } + await client.Client.CloseAsync(closeStatus ?? WebSocketCloseStatus.Empty, closeDesc, + CancellationToken.None); + client.Client.Dispose(); } + } - public void RemoveAppClients(string appId, WebSocketCloseStatus? closeStatus, string closeDesc) + public void RemoveAppClients(string appId, WebSocketCloseStatus? closeStatus, string closeDesc) + { + var removeClients = _clients.Values.Where(c => c.AppId == appId).ToList(); + if (removeClients.Count == 0) return; + foreach (var webSocket in removeClients) _clients.TryRemove(webSocket.Id, out var tryRemoveClient); + Task.Run(async () => { - var removeClients = _clients.Values.Where(c => c.AppId == appId).ToList(); - if (removeClients.Count == 0) - { - return; - } foreach (var webSocket in removeClients) - { - _clients.TryRemove(webSocket.Id, out WebsocketClient tryRemoveClient); - } - Task.Run(async () => - { - foreach (var webSocket in removeClients) + try { - try - { - if (webSocket.Client.State != WebSocketState.Open) continue; - await webSocket.Client.CloseAsync(closeStatus ?? WebSocketCloseStatus.Empty, closeDesc, CancellationToken.None); - webSocket.Client.Dispose(); - } - catch (Exception ex) - { - Console.WriteLine("Try to close websocket client {0} err {1}.", webSocket.Id, ex.Message); - } + if (webSocket.Client.State != WebSocketState.Open) continue; + await webSocket.Client.CloseAsync(closeStatus ?? WebSocketCloseStatus.Empty, closeDesc, + CancellationToken.None); + webSocket.Client.Dispose(); } - }); - } - - public WebsocketClient Get(string clientId) - { - _clients.TryGetValue(clientId, out WebsocketClient client); - return client; - } - - public ClientInfos Report() - { - var clientInfos = _clients - .Values - .Select(c => new ClientInfo { - Id = c.Id, - AppId = c.AppId, - LastHeartbeatTime = c.LastHeartbeatTime, - Tag = c.Tag, - Name = c.Name, - Ip = c.Ip , - Env = c.Env, - LastRefreshTime = c.LastRefreshTime - }) - .OrderBy(c => c.AppId) - .ThenByDescending(c => c.LastHeartbeatTime) - .ToList(); - return new ClientInfos - { - ClientCount = clientInfos.Count, - Infos = clientInfos - }; - } - - public void SendActionToAll(WebsocketAction action) - { - if (_clients.IsEmpty) - { - return; - } - - var json = JsonConvert.SerializeObject(action); - var data = Encoding.UTF8.GetBytes(json); - foreach (var webSocket in _clients) - { - if (webSocket.Value.Client.State == WebSocketState.Open) + catch (Exception ex) { - webSocket.Value.Client.SendAsync(new ArraySegment(data, 0, data.Length), WebSocketMessageType.Text, true, - CancellationToken.None); + Console.WriteLine("Try to close websocket client {0} err {1}.", webSocket.Id, ex.Message); } - } - } + }); + } + + public WebsocketClient Get(string clientId) + { + _clients.TryGetValue(clientId, out var client); + return client; + } - public void Clear() + public ClientInfos Report() + { + var clientInfos = _clients + .Values + .Select(c => new ClientInfo + { + Id = c.Id, + AppId = c.AppId, + LastHeartbeatTime = c.LastHeartbeatTime, + Tag = c.Tag, + Name = c.Name, + Ip = c.Ip, + Env = c.Env, + LastRefreshTime = c.LastRefreshTime + }) + .OrderBy(c => c.AppId) + .ThenByDescending(c => c.LastHeartbeatTime) + .ToList(); + return new ClientInfos { - _clients?.Clear(); - } + ClientCount = clientInfos.Count, + Infos = clientInfos + }; + } - public static IWebsocketCollection Instance { get; private set; } + public void SendActionToAll(WebsocketAction action) + { + if (_clients.IsEmpty) return; + + var json = JsonConvert.SerializeObject(action); + var data = Encoding.UTF8.GetBytes(json); + foreach (var webSocket in _clients) + if (webSocket.Value.Client.State == WebSocketState.Open) + webSocket.Value.Client.SendAsync(new ArraySegment(data, 0, data.Length), + WebSocketMessageType.Text, true, + CancellationToken.None); + } - public int Count => _clients.Count; + public void Clear() + { + _clients?.Clear(); } + public int Count => _clients.Count; } \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Websocket/WebsocketHandlerMiddleware.cs b/src/AgileConfig.Server.Apisite/Websocket/WebsocketHandlerMiddleware.cs index 68018963..1da18fd7 100644 --- a/src/AgileConfig.Server.Apisite/Websocket/WebsocketHandlerMiddleware.cs +++ b/src/AgileConfig.Server.Apisite/Websocket/WebsocketHandlerMiddleware.cs @@ -11,198 +11,183 @@ using AgileConfig.Server.IService; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Primitives; -namespace AgileConfig.Server.Apisite.Websocket +namespace AgileConfig.Server.Apisite.Websocket; + +public class WebsocketHandlerMiddleware { - public class WebsocketHandlerMiddleware + private readonly ILogger _logger; + private readonly RequestDelegate _next; + private readonly IWebsocketCollection _websocketCollection; + + public WebsocketHandlerMiddleware( + RequestDelegate next, + ILoggerFactory loggerFactory + ) { - private readonly RequestDelegate _next; - private readonly ILogger _logger; - private readonly IWebsocketCollection _websocketCollection; - - public WebsocketHandlerMiddleware( - RequestDelegate next, - ILoggerFactory loggerFactory - ) - { - _next = next; - _logger = loggerFactory.CreateLogger(); - _websocketCollection = WebsocketCollection.Instance; - } + _next = next; + _logger = loggerFactory.CreateLogger(); + _websocketCollection = WebsocketCollection.Instance; + } - public async Task Invoke( - HttpContext context, - IAppBasicAuthService appBasicAuth, - IConfigService configService, - IRegisterCenterService registerCenterService, - IServiceInfoService serviceInfoService) + public async Task Invoke( + HttpContext context, + IAppBasicAuthService appBasicAuth, + IConfigService configService, + IRegisterCenterService registerCenterService, + IServiceInfoService serviceInfoService) + { + if (context.Request.Path == "/ws") { - if (context.Request.Path == "/ws") + if (context.WebSockets.IsWebSocketRequest) { - if (context.WebSockets.IsWebSocketRequest) + if (!await appBasicAuth.ValidAsync(context.Request)) { - if (!await appBasicAuth.ValidAsync(context.Request)) - { - context.Response.StatusCode = 401; - await context.Response.WriteAsync("basic auth failed ."); - return; - } - - var appId = context.Request.Headers["appid"]; - if (string.IsNullOrEmpty(appId)) - { - var appIdSecret = appBasicAuth.GetAppIdSecret(context.Request); - appId = appIdSecret.Item1; - } - - appId = HttpUtility.UrlDecode(appId); - - var env = context.Request.Headers["env"]; - if (!string.IsNullOrEmpty(env)) - { - env = HttpUtility.UrlDecode(env); - } - else - { - env = "DEV"; - _logger.LogInformation("Websocket client request No ENV property , set default DEV "); - } - - context.Request.Query.TryGetValue("client_name", out StringValues name); - if (!string.IsNullOrEmpty(name)) - { - name = HttpUtility.UrlDecode(name); - } - - context.Request.Query.TryGetValue("client_tag", out StringValues tag); - if (!string.IsNullOrEmpty(tag)) - { - tag = HttpUtility.UrlDecode(tag); - } - - WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); - - var clientIp = GetRemoteIp(context.Request); - var client = new WebsocketClient() - { - Client = webSocket, - Id = Guid.NewGuid().ToString(), - AppId = appId, - LastHeartbeatTime = DateTime.Now, - Name = name, - Tag = tag, - Ip = clientIp.ToString(), - Env = env - }; - _websocketCollection.AddClient(client); - _logger.LogInformation("Websocket client {0} Added ", client.Id); - - try - { - await Handle(context, client, configService, registerCenterService, serviceInfoService); - } - catch (WebSocketException) - { - _logger.LogInformation("client {0} closed the websocket connection directly .", client.Id); - await _websocketCollection.RemoveClient(client, WebSocketCloseStatus.Empty, null); - } - catch (Exception ex) - { - _logger.LogError(ex, "Handle websocket client {0} err .", client.Id); - await _websocketCollection.RemoveClient(client, WebSocketCloseStatus.Empty, null); - } + context.Response.StatusCode = 401; + await context.Response.WriteAsync("basic auth failed ."); + return; + } + + var appId = context.Request.Headers["appid"]; + if (string.IsNullOrEmpty(appId)) + { + var appIdSecret = appBasicAuth.GetAppIdSecret(context.Request); + appId = appIdSecret.Item1; + } + + appId = HttpUtility.UrlDecode(appId); + + var env = context.Request.Headers["env"]; + if (!string.IsNullOrEmpty(env)) + { + env = HttpUtility.UrlDecode(env); } else { - context.Response.StatusCode = 400; + env = "DEV"; + _logger.LogInformation("Websocket client request No ENV property , set default DEV "); + } + + context.Request.Query.TryGetValue("client_name", out var name); + if (!string.IsNullOrEmpty(name)) name = HttpUtility.UrlDecode(name); + + context.Request.Query.TryGetValue("client_tag", out var tag); + if (!string.IsNullOrEmpty(tag)) tag = HttpUtility.UrlDecode(tag); + + var webSocket = await context.WebSockets.AcceptWebSocketAsync(); + + var clientIp = GetRemoteIp(context.Request); + var client = new WebsocketClient + { + Client = webSocket, + Id = Guid.NewGuid().ToString(), + AppId = appId, + LastHeartbeatTime = DateTime.Now, + Name = name, + Tag = tag, + Ip = clientIp.ToString(), + Env = env + }; + _websocketCollection.AddClient(client); + _logger.LogInformation("Websocket client {0} Added ", client.Id); + + try + { + await Handle(context, client, configService, registerCenterService, serviceInfoService); + } + catch (WebSocketException) + { + _logger.LogInformation("client {0} closed the websocket connection directly .", client.Id); + await _websocketCollection.RemoveClient(client, WebSocketCloseStatus.Empty, null); + } + catch (Exception ex) + { + _logger.LogError(ex, "Handle websocket client {0} err .", client.Id); + await _websocketCollection.RemoveClient(client, WebSocketCloseStatus.Empty, null); } } else { - await _next(context); + context.Response.StatusCode = 400; } } - - public IPAddress GetRemoteIp(HttpRequest httpRequest) + else { - IPAddress ip; - var headers = httpRequest.Headers.ToList(); - if (headers.Exists((kvp) => kvp.Key == "X-Forwarded-For")) - { - // when running behind a load balancer you can expect this header - var header = headers.First((kvp) => kvp.Key == "X-Forwarded-For").Value.ToString(); - IPAddress.TryParse(header, out ip); - } - else - { - // this will always have a value (running locally in development won't have the header) - ip = httpRequest.HttpContext.Connection.RemoteIpAddress; - } - - return ip; + await _next(context); } + } - /// - /// Handle messages from clients. - /// If the payload is "ping", it is the legacy client heartbeat message. - /// Messages starting with "c:" come from configuration center clients. - /// Messages starting with "s:" come from service center clients. - /// - /// HTTP context associated with the websocket connection. - /// Active websocket client wrapper. - /// Configuration service used to respond to configuration messages. - /// Registration center service used to process registry updates. - /// Service information service used for service center messages. - private async Task Handle( - HttpContext context, - WebsocketClient socketClient, - IConfigService configService, - IRegisterCenterService registerCenterService, - IServiceInfoService serviceInfoService) + public IPAddress GetRemoteIp(HttpRequest httpRequest) + { + IPAddress ip; + var headers = httpRequest.Headers.ToList(); + if (headers.Exists(kvp => kvp.Key == "X-Forwarded-For")) { - var messageHandlers = - new WebsocketMessageHandlers(configService, registerCenterService, serviceInfoService); - var buffer = new byte[1024 * 2]; - WebSocketReceiveResult result; - do - { - result = await socketClient.Client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - if (result.CloseStatus.HasValue) - { - break; - } - socketClient.LastHeartbeatTime = DateTime.Now; - var message = await ReadWebsocketMessage(result, buffer); + // when running behind a load balancer you can expect this header + var header = headers.First(kvp => kvp.Key == "X-Forwarded-For").Value.ToString(); + IPAddress.TryParse(header, out ip); + } + else + { + // this will always have a value (running locally in development won't have the header) + ip = httpRequest.HttpContext.Connection.RemoteIpAddress; + } - foreach (var messageHandlersMessageHandler in messageHandlers.MessageHandlers) - { - if (messageHandlersMessageHandler.Hit(context.Request)) - { - await messageHandlersMessageHandler.Handle(message, context.Request, socketClient); - } - } - } while (!result.CloseStatus.HasValue); + return ip; + } - _logger.LogInformation( - $"Websocket close , closeStatus:{result.CloseStatus} closeDesc:{result.CloseStatusDescription}"); - await _websocketCollection.RemoveClient(socketClient, result.CloseStatus, result.CloseStatusDescription); - } + /// + /// Handle messages from clients. + /// If the payload is "ping", it is the legacy client heartbeat message. + /// Messages starting with "c:" come from configuration center clients. + /// Messages starting with "s:" come from service center clients. + /// + /// HTTP context associated with the websocket connection. + /// Active websocket client wrapper. + /// Configuration service used to respond to configuration messages. + /// Registration center service used to process registry updates. + /// Service information service used for service center messages. + private async Task Handle( + HttpContext context, + WebsocketClient socketClient, + IConfigService configService, + IRegisterCenterService registerCenterService, + IServiceInfoService serviceInfoService) + { + var messageHandlers = + new WebsocketMessageHandlers(configService, registerCenterService, serviceInfoService); + var buffer = new byte[1024 * 2]; + WebSocketReceiveResult result; + do + { + result = await socketClient.Client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + if (result.CloseStatus.HasValue) break; + socketClient.LastHeartbeatTime = DateTime.Now; + var message = await ReadWebsocketMessage(result, buffer); + + foreach (var messageHandlersMessageHandler in messageHandlers.MessageHandlers) + if (messageHandlersMessageHandler.Hit(context.Request)) + await messageHandlersMessageHandler.Handle(message, context.Request, socketClient); + } while (!result.CloseStatus.HasValue); + + _logger.LogInformation( + $"Websocket close , closeStatus:{result.CloseStatus} closeDesc:{result.CloseStatusDescription}"); + await _websocketCollection.RemoveClient(socketClient, result.CloseStatus, result.CloseStatusDescription); + } - private async Task ReadWebsocketMessage(WebSocketReceiveResult result, ArraySegment buffer) + private async Task ReadWebsocketMessage(WebSocketReceiveResult result, ArraySegment buffer) + { + using (var ms = new MemoryStream()) { - using (var ms = new MemoryStream()) + ms.Write(buffer.Array, buffer.Offset, result.Count); + if (result.MessageType == WebSocketMessageType.Text) { - ms.Write(buffer.Array, buffer.Offset, result.Count); - if (result.MessageType == WebSocketMessageType.Text) - { - ms.Seek(0, SeekOrigin.Begin); - using var reader = new StreamReader(ms, Encoding.UTF8); - return await reader.ReadToEndAsync(); - } - - return ""; + ms.Seek(0, SeekOrigin.Begin); + using var reader = new StreamReader(ms, Encoding.UTF8); + return await reader.ReadToEndAsync(); } + + return ""; } } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/nlog.config b/src/AgileConfig.Server.Apisite/nlog.config index 6f550f39..50bab0f7 100644 --- a/src/AgileConfig.Server.Apisite/nlog.config +++ b/src/AgileConfig.Server.Apisite/nlog.config @@ -1,4 +1,5 @@ - + + - - + + - - + + - - - + + + - - - - - - - + + + + + + + \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/DictionaryConvertToJson.cs b/src/AgileConfig.Server.Common/DictionaryConvertToJson.cs index d52aafd3..82fc62d2 100644 --- a/src/AgileConfig.Server.Common/DictionaryConvertToJson.cs +++ b/src/AgileConfig.Server.Common/DictionaryConvertToJson.cs @@ -1,157 +1,134 @@ -using Newtonsoft.Json; -using System; +using System; using System.Collections.Generic; using System.Linq; +using Newtonsoft.Json; -namespace AgileConfig.Server.Common +namespace AgileConfig.Server.Common; + +public static class DictionaryConvertToJson { - public static class DictionaryConvertToJson + public static string ToJson(IDictionary dict) + { + if (dict.Count == 0) return "{}"; + + var root = new SortedDictionary(); + foreach (var kv in dict) Generate(kv.Key, kv.Value, root); + + var newDict = RebuildDict(root); + + return JsonConvert.SerializeObject(newDict, Formatting.Indented); + } + + /// + /// Determine whether the dictionary represents a JSON array. + /// + /// Dictionary to inspect for sequential numeric keys. + /// True when the dictionary can be treated as an array. + private static bool JudgeDictIsJsonArray(IDictionary dict) { - public static string ToJson(IDictionary dict) + // Check keys starting from index 0. + // If all keys exist consecutively, the dictionary represents an array. + var keys = dict.Keys; + for (var i = 0; i < keys.Count; i++) { - if (dict.Count == 0) - { - return "{}"; - } - - var root = new SortedDictionary(); - foreach (var kv in dict) - { - Generate(kv.Key, kv.Value, root); - } + var key = i.ToString(); + if (!dict.ContainsKey(key)) return false; + } - var newDict = RebuildDict(root); - - return JsonConvert.SerializeObject(newDict, Formatting.Indented); + return true; + } + + /// + /// Convert the dictionary to an array. + /// + /// Dictionary whose values should be projected into an array. + /// Array built from the dictionary values. + private static object[] ConvertDictToJsonArray(IDictionary dict) + { + var keys = dict.Keys; + var array = new object[keys.Count()]; + for (var i = 0; i < keys.Count(); i++) + { + var key = i.ToString(); + array[i] = dict[key]; } - /// - /// Determine whether the dictionary represents a JSON array. - /// - /// Dictionary to inspect for sequential numeric keys. - /// True when the dictionary can be treated as an array. - private static bool JudgeDictIsJsonArray(IDictionary dict) + return array; + } + + /// + /// Rebuild the structure, turning dictionaries that represent arrays into actual arrays. + /// + /// Dictionary or array to normalize. + /// Normalized object graph with arrays materialized. + private static object RebuildDict(object dictOrArray) + { + var dict = dictOrArray as IDictionary; + if (dict != null) { - // Check keys starting from index 0. - // If all keys exist consecutively, the dictionary represents an array. - var keys = dict.Keys; - for (int i = 0; i < keys.Count; i++) + if (JudgeDictIsJsonArray(dict)) { - var key = i.ToString(); - if (!dict.ContainsKey(key)) - { - return false; - } + object array = ConvertDictToJsonArray(dict); + + array = RebuildDict(array); + + return array; } - return true; - } - - /// - /// Convert the dictionary to an array. - /// - /// Dictionary whose values should be projected into an array. - /// Array built from the dictionary values. - private static object[] ConvertDictToJsonArray(IDictionary dict) - { - var keys = dict.Keys; - var array = new object[keys.Count()]; - for (int i = 0; i < keys.Count(); i++) + var keys = dict.Keys.Select(x => x).ToList(); + foreach (var key in keys) { - var key = i.ToString(); - array[i] = dict[key]; + var val = dict[key]; + dict[key] = RebuildDict(val); } - return array; + return dict; } - /// - /// Rebuild the structure, turning dictionaries that represent arrays into actual arrays. - /// - /// Dictionary or array to normalize. - /// Normalized object graph with arrays materialized. - private static object RebuildDict(object dictOrArray) + var jsonArray = dictOrArray as object[]; + if (jsonArray != null) { - var dict = dictOrArray as IDictionary; - if (dict != null) - { - if (JudgeDictIsJsonArray(dict)) - { - object array = ConvertDictToJsonArray(dict); - - array = RebuildDict(array); - - return array; - } - else - { - var keys = dict.Keys.Select(x => x).ToList(); - foreach (var key in keys) - { - var val = dict[key]; - dict[key] = RebuildDict(val); - } - return dict; - } - } - - var jsonArray = dictOrArray as object[]; - if (jsonArray != null) - { - for (int i = 0; i < jsonArray.Length; i++) - { - jsonArray[i] = RebuildDict(jsonArray[i]); - } - - return jsonArray; - } + for (var i = 0; i < jsonArray.Length; i++) jsonArray[i] = RebuildDict(jsonArray[i]); - return dictOrArray; + return jsonArray; } - /// - /// Expand flattened key-value pairs into nested dictionaries. - /// - /// Flattened key representing the nested structure. - /// Value to assign at the end of the key path. - /// Dictionary to populate with the nested structure. - private static void Generate(string key, string value, IDictionary parent) - { - if (string.IsNullOrWhiteSpace(key)) - { - throw new ArgumentNullException(nameof(key)); - } + return dictOrArray; + } - var groupArr = key.Split(':'); - if (groupArr.Length > 1) + /// + /// Expand flattened key-value pairs into nested dictionaries. + /// + /// Flattened key representing the nested structure. + /// Value to assign at the end of the key path. + /// Dictionary to populate with the nested structure. + private static void Generate(string key, string value, IDictionary parent) + { + if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); + + var groupArr = key.Split(':'); + if (groupArr.Length > 1) + { + var sonKey = groupArr[0]; + var newArr = new string[groupArr.Length - 1]; + for (var i = 1; i < groupArr.Length; i++) newArr[i - 1] = groupArr[i]; + var otherKeys = string.Join(':', newArr); + if (parent.ContainsKey(sonKey)) { - var sonKey = groupArr[0]; - var newArr = new string[groupArr.Length - 1]; - for (int i = 1; i < groupArr.Length; i++) - { - newArr[i - 1] = groupArr[i]; - } - var otherKeys = string.Join(':', newArr); - if (parent.ContainsKey(sonKey)) - { - // If a child dictionary already exists. - var son = parent[sonKey] as IDictionary; - if (son != null) - { - Generate(otherKeys, value, son); - } - } - else - { - var son = new SortedDictionary(); - Generate(otherKeys, value, son); - parent.Add(sonKey, son); - } + // If a child dictionary already exists. + var son = parent[sonKey] as IDictionary; + if (son != null) Generate(otherKeys, value, son); } else { - parent.Add(key, value); + var son = new SortedDictionary(); + Generate(otherKeys, value, son); + parent.Add(sonKey, son); } } + else + { + parent.Add(key, value); + } } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/EfLoggerProvider.cs b/src/AgileConfig.Server.Common/EfLoggerProvider.cs index 85682496..4cd9bb0a 100644 --- a/src/AgileConfig.Server.Common/EfLoggerProvider.cs +++ b/src/AgileConfig.Server.Common/EfLoggerProvider.cs @@ -1,34 +1,35 @@ -using Microsoft.Extensions.Logging; -using System; +using System; +using Microsoft.Extensions.Logging; -namespace AgileConfig.Server.Common +namespace AgileConfig.Server.Common; + +public class EfLoggerProvider : ILoggerProvider { - public class EfLoggerProvider : ILoggerProvider + public ILogger CreateLogger(string categoryName) { - public ILogger CreateLogger(string categoryName) - { - return new EfLogger(); - } + return new EfLogger(); + } - public void Dispose() - { } + public void Dispose() + { + } - private class EfLogger : ILogger + private class EfLogger : ILogger + { + public bool IsEnabled(LogLevel logLevel) { - public bool IsEnabled(LogLevel logLevel) - { - return true; - } + return true; + } - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - Console.WriteLine(formatter(state, exception)); - } + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, + Func formatter) + { + Console.WriteLine(formatter(state, exception)); + } - public IDisposable BeginScope(TState state) - { - return null; - } + public IDisposable BeginScope(TState state) + { + return null; } } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/Encrypt.cs b/src/AgileConfig.Server.Common/Encrypt.cs index 7c431faa..01098f79 100644 --- a/src/AgileConfig.Server.Common/Encrypt.cs +++ b/src/AgileConfig.Server.Common/Encrypt.cs @@ -3,16 +3,16 @@ using System.Text; using System.Threading; -namespace AgileConfig.Server.Common +namespace AgileConfig.Server.Common; + +public static class Encrypt { - public static class Encrypt + private static readonly ThreadLocal Md5Instance = new(MD5.Create); + + public static string Md5(string txt) { - private static readonly ThreadLocal Md5Instance = new(MD5.Create); - public static string Md5(string txt) - { - var inputBytes = Encoding.ASCII.GetBytes(txt); - var hashBytes = Md5Instance.Value.ComputeHash(inputBytes); - return Convert.ToHexString(hashBytes); - } + var inputBytes = Encoding.ASCII.GetBytes(txt); + var hashBytes = Md5Instance.Value.ComputeHash(inputBytes); + return Convert.ToHexString(hashBytes); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/EnumExt.cs b/src/AgileConfig.Server.Common/EnumExt.cs index c2aaa18f..a0df99f1 100644 --- a/src/AgileConfig.Server.Common/EnumExt.cs +++ b/src/AgileConfig.Server.Common/EnumExt.cs @@ -2,27 +2,17 @@ using System.ComponentModel; using System.Linq; -namespace AgileConfig.Server.Common +namespace AgileConfig.Server.Common; + +public static class EnumExt { - public static class EnumExt + public static string ToDesc(this Enum e) { - public static string ToDesc(this Enum e) - { - var fi = e.GetType().GetField(e.ToString()); - if (fi == null) - { - return ""; - } - var attrs = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); - if (attrs.Length != 0) - { - return attrs.First().Description; - } - else - { - return e.ToString(); - } + var fi = e.GetType().GetField(e.ToString()); + if (fi == null) return ""; + var attrs = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); + if (attrs.Length != 0) return attrs.First().Description; - } + return e.ToString(); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/EnvAccessor.cs b/src/AgileConfig.Server.Common/EnvAccessor.cs index 4351cb64..fb4b1918 100644 --- a/src/AgileConfig.Server.Common/EnvAccessor.cs +++ b/src/AgileConfig.Server.Common/EnvAccessor.cs @@ -1,38 +1,39 @@ -using Microsoft.AspNetCore.Http; +using System.Linq; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; -using System.Linq; -namespace AgileConfig.Server.Common +namespace AgileConfig.Server.Common; + +public interface IEnvAccessor +{ + string Env { get; } +} + +public class EnvAccessor : IEnvAccessor { - public interface IEnvAccessor + private readonly IHttpContextAccessor _httpContextAccessor; + + public EnvAccessor(IHttpContextAccessor httpContextAccessor) { - string Env { get; } + _httpContextAccessor = httpContextAccessor; } - public class EnvAccessor : IEnvAccessor + public string Env { - private readonly IHttpContextAccessor _httpContextAccessor; - public EnvAccessor(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } - public string Env + get { - get - { - var env = _httpContextAccessor.HttpContext?.Request.Query["env"].FirstOrDefault(); - return env; - } + var env = _httpContextAccessor.HttpContext?.Request.Query["env"].FirstOrDefault(); + return env; } } +} - public static class EnvAccessorServiceCollectionExtension +public static class EnvAccessorServiceCollectionExtension +{ + public static IServiceCollection AddEnvAccessor(this IServiceCollection services) { - public static IServiceCollection AddEnvAccessor(this IServiceCollection services) - { - services.AddSingleton(); + services.AddSingleton(); - return services; - } + return services; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/EventBus/ITinyEventBus.cs b/src/AgileConfig.Server.Common/EventBus/ITinyEventBus.cs index 14b57679..4893ca01 100644 --- a/src/AgileConfig.Server.Common/EventBus/ITinyEventBus.cs +++ b/src/AgileConfig.Server.Common/EventBus/ITinyEventBus.cs @@ -1,24 +1,24 @@ using System.Threading.Tasks; -namespace AgileConfig.Server.Common.EventBus +namespace AgileConfig.Server.Common.EventBus; + +public interface IEvent { - public interface IEvent - { - } +} - public interface IEventHandler : IEventHandler where TEvent : IEvent - { - } +public interface IEventHandler : IEventHandler where TEvent : IEvent +{ +} - public interface IEventHandler - { - Task Handle(IEvent evt); - } +public interface IEventHandler +{ + Task Handle(IEvent evt); +} + +public interface ITinyEventBus +{ + void Fire(TEvent evt) where TEvent : IEvent; - public interface ITinyEventBus - { - void Fire(TEvent evt) where TEvent : IEvent; - void Register() - where T : class, IEventHandler; - } + void Register() + where T : class, IEventHandler; } \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/EventBus/ServiceCollectionExt.cs b/src/AgileConfig.Server.Common/EventBus/ServiceCollectionExt.cs index ad700802..91f2d3d6 100644 --- a/src/AgileConfig.Server.Common/EventBus/ServiceCollectionExt.cs +++ b/src/AgileConfig.Server.Common/EventBus/ServiceCollectionExt.cs @@ -1,15 +1,14 @@ using Microsoft.Extensions.DependencyInjection; -namespace AgileConfig.Server.Common.EventBus +namespace AgileConfig.Server.Common.EventBus; + +public static class ServiceCollectionExt { - public static class ServiceCollectionExt + public static IServiceCollection AddTinyEventBus(this IServiceCollection sc) { - public static IServiceCollection AddTinyEventBus(this IServiceCollection sc) - { - sc.AddSingleton(sp => - new TinyEventBus(sc)); + sc.AddSingleton(sp => + new TinyEventBus(sc)); - return sc; - } + return sc; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/EventBus/TinyEventBus.cs b/src/AgileConfig.Server.Common/EventBus/TinyEventBus.cs index 5250b8e1..4e248d07 100644 --- a/src/AgileConfig.Server.Common/EventBus/TinyEventBus.cs +++ b/src/AgileConfig.Server.Common/EventBus/TinyEventBus.cs @@ -1,80 +1,74 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace AgileConfig.Server.Common.EventBus; -namespace AgileConfig.Server.Common.EventBus +public class TinyEventBus : ITinyEventBus { - public class TinyEventBus : ITinyEventBus - { - private readonly IServiceCollection _serviceCollection; - private static readonly ConcurrentDictionary> EventHandlerMap = new (); - private IServiceProvider _localServiceProvider; - private readonly ILogger _logger; + private static readonly ConcurrentDictionary> EventHandlerMap = new(); + private readonly ILogger _logger; + private readonly IServiceCollection _serviceCollection; + private IServiceProvider _localServiceProvider; - public TinyEventBus(IServiceCollection serviceCollection) - { - _serviceCollection = serviceCollection; - _logger = _serviceCollection.BuildServiceProvider().GetService().CreateLogger(); - } - public void Register() where T : class, IEventHandler - { - var handlerType = typeof(T); - var eventType = handlerType.GetInterfaces().FirstOrDefault(x => x.IsGenericType)!.GenericTypeArguments.FirstOrDefault(); - if (EventHandlerMap.TryGetValue(eventType, out List handlerTypes)) - { - handlerTypes.Add(handlerType); - } - else - { - EventHandlerMap.TryAdd(eventType, [handlerType]); - } - _serviceCollection.AddScoped(); + public TinyEventBus(IServiceCollection serviceCollection) + { + _serviceCollection = serviceCollection; + _logger = _serviceCollection.BuildServiceProvider().GetService().CreateLogger(); + } - } + public void Register() where T : class, IEventHandler + { + var handlerType = typeof(T); + var eventType = handlerType.GetInterfaces().FirstOrDefault(x => x.IsGenericType)!.GenericTypeArguments + .FirstOrDefault(); + if (EventHandlerMap.TryGetValue(eventType, out var handlerTypes)) + handlerTypes.Add(handlerType); + else + EventHandlerMap.TryAdd(eventType, [handlerType]); + _serviceCollection.AddScoped(); + } - /// - /// Trigger an event. This method must be called before the handler is registered. - /// - /// Type of event to fire. - /// Event payload instance to dispatch to handlers. - public void Fire(TEvent evt) where TEvent : IEvent - { - _localServiceProvider ??= _serviceCollection.BuildServiceProvider(); + /// + /// Trigger an event. This method must be called before the handler is registered. + /// + /// Type of event to fire. + /// Event payload instance to dispatch to handlers. + public void Fire(TEvent evt) where TEvent : IEvent + { + _localServiceProvider ??= _serviceCollection.BuildServiceProvider(); - _logger.LogInformation($"Event fired: {typeof(TEvent).Name}"); + _logger.LogInformation($"Event fired: {typeof(TEvent).Name}"); - var eventType = typeof(TEvent); - if (EventHandlerMap.TryGetValue(eventType, out List handlers)) + var eventType = typeof(TEvent); + if (EventHandlerMap.TryGetValue(eventType, out var handlers)) + { + if (handlers.Count == 0) { - if (handlers.Count == 0) - { - _logger.LogInformation($"Event fired: {typeof(TEvent).Name}, but no handlers."); - return; - } + _logger.LogInformation($"Event fired: {typeof(TEvent).Name}, but no handlers."); + return; + } - foreach (var handlerType in handlers) + foreach (var handlerType in handlers) + _ = Task.Run(async () => { - _ = Task.Run(async () => + using var sc = _localServiceProvider.CreateScope(); + var handler = sc.ServiceProvider.GetService(handlerType); + + try { - using var sc = _localServiceProvider.CreateScope(); - var handler = sc.ServiceProvider.GetService(handlerType); - - try - { - await (handler as IEventHandler)?.Handle(evt)!; - } - catch (Exception ex) - { - _logger - .LogError(ex, "try run {handler} occur error.", handlerType); - } - }); - } - } + await (handler as IEventHandler)?.Handle(evt)!; + } + catch (Exception ex) + { + _logger + .LogError(ex, "try run {handler} occur error.", handlerType); + } + }); } } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/ExceptionHandlerMiddleware.cs b/src/AgileConfig.Server.Common/ExceptionHandlerMiddleware.cs index c7cdf8a7..5601452c 100644 --- a/src/AgileConfig.Server.Common/ExceptionHandlerMiddleware.cs +++ b/src/AgileConfig.Server.Common/ExceptionHandlerMiddleware.cs @@ -4,55 +4,54 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -namespace AgileConfig.Server.Common +namespace AgileConfig.Server.Common; + +public class ExceptionHandlerMiddleware { - public class ExceptionHandlerMiddleware + private readonly ILogger _logger; + private readonly RequestDelegate _next; + + public ExceptionHandlerMiddleware( + RequestDelegate next, + ILoggerFactory loggerFactory) { - private readonly RequestDelegate _next; - private readonly ILogger _logger; + _next = next; + _logger = loggerFactory.CreateLogger(); + } - public ExceptionHandlerMiddleware( - RequestDelegate next, - ILoggerFactory loggerFactory) + public async Task Invoke(HttpContext context) + { + try { - _next = next; - _logger = loggerFactory. - CreateLogger(); + await _next(context); } - - public async Task Invoke(HttpContext context) + catch (Exception ex) { try { - await _next(context); - } - catch (Exception ex) - { - try - { - _logger.LogError( - ex, - $"When {context.Connection.RemoteIpAddress} request {context.Request.Path} error , but not handled .\r\n {ex.Message} \r\n {ex.StackTrace}", - "" - ); - - context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; - context.Response.ContentType = "text/html"; + _logger.LogError( + ex, + $"When {context.Connection.RemoteIpAddress} request {context.Request.Path} error , but not handled .\r\n {ex.Message} \r\n {ex.StackTrace}", + "" + ); - await context.Response.WriteAsync($"{(int)HttpStatusCode.InternalServerError} InternalServerError").ConfigureAwait(false); - } - catch (Exception ex2) - { - _logger.LogError( - 0, ex2, - "An exception was thrown attempting " + - "to execute the error handler."); - } + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + context.Response.ContentType = "text/html"; - // Otherwise this handler will - // re -throw the original exception - throw; + await context.Response.WriteAsync($"{(int)HttpStatusCode.InternalServerError} InternalServerError") + .ConfigureAwait(false); } + catch (Exception ex2) + { + _logger.LogError( + 0, ex2, + "An exception was thrown attempting " + + "to execute the error handler."); + } + + // Otherwise this handler will + // re -throw the original exception + throw; } } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/FunctionUtil.cs b/src/AgileConfig.Server.Common/FunctionUtil.cs index 273d95e8..e25760d5 100644 --- a/src/AgileConfig.Server.Common/FunctionUtil.cs +++ b/src/AgileConfig.Server.Common/FunctionUtil.cs @@ -1,136 +1,106 @@ using System; using System.Threading.Tasks; -namespace AgileConfig.Server.Common +namespace AgileConfig.Server.Common; + +public static class FunctionUtil { - public static class FunctionUtil + /// + /// Try executing a function multiple times. + /// + /// Return type of the function. + /// Delegate to invoke. + /// Number of attempts before giving up. + /// Result produced by the function. + public static T TRY(Func func, int tryCount) { - /// - /// Try executing a function multiple times. - /// - /// Return type of the function. - /// Delegate to invoke. - /// Number of attempts before giving up. - /// Result produced by the function. - public static T TRY(Func func, int tryCount) - { - if (func == null) + if (func == null) throw new ArgumentNullException(nameof(func)); + + var result = default(T); + for (var i = 0; i < tryCount; i++) + try { - throw new ArgumentNullException(nameof(func)); + result = func(); + break; } - - T result = default(T); - for (int i = 0; i < tryCount; i++) + catch { - try - { - result = func(); - break; - } - catch - { - if (i == (tryCount - 1)) - { - throw; - } - } + if (i == tryCount - 1) throw; } - return result; - } + return result; + } + + /// + /// Try executing an asynchronous function multiple times. + /// + /// Return type of the function. + /// Asynchronous delegate to invoke. + /// Number of attempts before rethrowing the exception. + /// Result produced by the asynchronous function. + public static async Task TRYAsync(Func> func, int tryCount) + { + if (func == null) throw new ArgumentNullException("func"); - /// - /// Try executing an asynchronous function multiple times. - /// - /// Return type of the function. - /// Asynchronous delegate to invoke. - /// Number of attempts before rethrowing the exception. - /// Result produced by the asynchronous function. - public static async Task TRYAsync(Func> func, int tryCount) - { - if (func == null) + var result = default(T); + for (var i = 0; i < tryCount; i++) + try { - throw new ArgumentNullException("func"); + result = await func(); + break; } - - T result = default(T); - for (int i = 0; i < tryCount; i++) + catch { - try - { - result = await func(); - break; - } - catch - { - if (i == (tryCount - 1)) - { - throw; - } - } + if (i == tryCount - 1) throw; } - return result; - } + return result; + } - /// - /// Try executing an asynchronous function multiple times and swallow exceptions. - /// - /// Return type of the function. - /// Asynchronous delegate to invoke. - /// Number of attempts to run the delegate. - /// Result produced by the asynchronous function, or default when all attempts fail. - public static async Task EATAsync(Func> func, int tryCount) - { - if (func == null) + /// + /// Try executing an asynchronous function multiple times and swallow exceptions. + /// + /// Return type of the function. + /// Asynchronous delegate to invoke. + /// Number of attempts to run the delegate. + /// Result produced by the asynchronous function, or default when all attempts fail. + public static async Task EATAsync(Func> func, int tryCount) + { + if (func == null) throw new ArgumentNullException(nameof(func)); + + var result = default(T); + for (var i = 0; i < tryCount; i++) + try { - throw new ArgumentNullException(nameof(func)); + result = await func(); + break; } - - T result = default(T); - for (int i = 0; i < tryCount; i++) + catch { - try - { - result = await func(); - break; - } - catch - { - } } - return result; - } + return result; + } - /// - /// Try executing an action multiple times. - /// - /// Action to invoke. - /// Number of attempts before rethrowing the exception. - /// Nothing. - public static void TRY(Action act, int tryCount) - { - if (act == null) + /// + /// Try executing an action multiple times. + /// + /// Action to invoke. + /// Number of attempts before rethrowing the exception. + /// Nothing. + public static void TRY(Action act, int tryCount) + { + if (act == null) throw new ArgumentNullException(nameof(act)); + + for (var i = 0; i < tryCount; i++) + try { - throw new ArgumentNullException(nameof(act)); + act(); + break; } - - for (int i = 0; i < tryCount; i++) + catch { - try - { - act(); - break; - } - catch - { - if (i == (tryCount - 1)) - { - throw; - } - } + if (i == tryCount - 1) throw; } - } } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/Global.cs b/src/AgileConfig.Server.Common/Global.cs index 6c22afb6..dcbabb4b 100644 --- a/src/AgileConfig.Server.Common/Global.cs +++ b/src/AgileConfig.Server.Common/Global.cs @@ -1,14 +1,12 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -namespace AgileConfig.Server.Common -{ - public static class Global - { - public static IConfiguration Config { get; set; } +namespace AgileConfig.Server.Common; - public static ILoggerFactory LoggerFactory { get; set; } +public static class Global +{ + public const string DefaultHttpClientName = "Default"; + public static IConfiguration Config { get; set; } - public const string DefaultHttpClientName = "Default"; - } -} + public static ILoggerFactory LoggerFactory { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/HttpExt.cs b/src/AgileConfig.Server.Common/HttpExt.cs index b89d337c..5f5e6142 100644 --- a/src/AgileConfig.Server.Common/HttpExt.cs +++ b/src/AgileConfig.Server.Common/HttpExt.cs @@ -1,74 +1,59 @@ -using Microsoft.AspNetCore.Http; -using System; +using System; using System.Linq; using System.Text; +using Microsoft.AspNetCore.Http; -namespace AgileConfig.Server.Common +namespace AgileConfig.Server.Common; + +public static class HttpExt { - public static class HttpExt + /// + /// Parse the username and password from the Authorization header of a request. + /// + /// HTTP request containing the Authorization header. + /// Tuple of username and password extracted from the header. + public static (string, string) GetUserNamePasswordFromBasicAuthorization(this HttpRequest httpRequest) { - /// - /// Parse the username and password from the Authorization header of a request. - /// - /// HTTP request containing the Authorization header. - /// Tuple of username and password extracted from the header. - public static (string, string) GetUserNamePasswordFromBasicAuthorization(this HttpRequest httpRequest) + var authorization = httpRequest.Headers["Authorization"]; + if (string.IsNullOrEmpty(authorization)) return ("", ""); + var authStr = authorization.First(); + // Strip the "Basic " prefix. + if (!authStr.StartsWith("Basic ")) return ("", ""); + authStr = authStr.Substring(6, authStr.Length - 6); + byte[] base64Decode = null; + try + { + base64Decode = Convert.FromBase64String(authStr); + } + catch { - var authorization = httpRequest.Headers["Authorization"]; - if (string.IsNullOrEmpty(authorization)) - { - return ("", ""); - } - var authStr = authorization.First(); - // Strip the "Basic " prefix. - if (!authStr.StartsWith("Basic ")) - { - return ("", ""); - } - authStr = authStr.Substring(6, authStr.Length - 6); - byte[] base64Decode = null; - try - { - base64Decode = Convert.FromBase64String(authStr); - } - catch - { - return ("", ""); - } - var base64Str = Encoding.UTF8.GetString(base64Decode); + return ("", ""); + } - if (string.IsNullOrEmpty(base64Str)) - { - return ("", ""); - } + var base64Str = Encoding.UTF8.GetString(base64Decode); - var userName = ""; - var password = ""; - var baseAuthArr = base64Str.Split(':'); + if (string.IsNullOrEmpty(base64Str)) return ("", ""); - if (baseAuthArr.Length > 0) - { - userName = baseAuthArr[0]; - } - if (baseAuthArr.Length > 1) - { - password = baseAuthArr[1]; - } + var userName = ""; + var password = ""; + var baseAuthArr = base64Str.Split(':'); - return (userName, password); - } + if (baseAuthArr.Length > 0) userName = baseAuthArr[0]; + if (baseAuthArr.Length > 1) password = baseAuthArr[1]; + return (userName, password); + } - public static string GetUserNameFromClaim(this HttpContext httpContext) - { - var name = httpContext.User?.FindFirst("username")?.Value; - return name; - } + public static string GetUserNameFromClaim(this HttpContext httpContext) + { + var name = httpContext.User?.FindFirst("username")?.Value; - public static string GetUserIdFromClaim(this HttpContext httpContext) - { - return httpContext.User?.FindFirst("id")?.Value; - } + return name; + } + + public static string GetUserIdFromClaim(this HttpContext httpContext) + { + return httpContext.User?.FindFirst("id")?.Value; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/IEntity.cs b/src/AgileConfig.Server.Common/IEntity.cs index 65ab2035..88c1e001 100644 --- a/src/AgileConfig.Server.Common/IEntity.cs +++ b/src/AgileConfig.Server.Common/IEntity.cs @@ -1,9 +1,6 @@ -using System; +namespace AgileConfig.Server.Common; -namespace AgileConfig.Server.Common +public interface IEntity { - public interface IEntity - { - T Id { get; set; } - } -} + T Id { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/JsonConfigurationFileParser.cs b/src/AgileConfig.Server.Common/JsonConfigurationFileParser.cs index 98319852..afc87876 100644 --- a/src/AgileConfig.Server.Common/JsonConfigurationFileParser.cs +++ b/src/AgileConfig.Server.Common/JsonConfigurationFileParser.cs @@ -1,106 +1,108 @@ -using Microsoft.Extensions.Configuration; -using System; +using System; using System.Collections.Generic; using System.IO; -using System.Text.Json; using System.Linq; +using System.Text.Json; +using Microsoft.Extensions.Configuration; -namespace AgileConfig.Server.Common +namespace AgileConfig.Server.Common; + +/// +/// Adaptation of the JSON configuration parser implementation provided by Microsoft. +/// +public class JsonConfigurationFileParser { - /// - /// Adaptation of the JSON configuration parser implementation provided by Microsoft. - /// - public class JsonConfigurationFileParser - { - private JsonConfigurationFileParser() { } + private readonly Stack _context = new(); - private readonly IDictionary _data = new SortedDictionary(StringComparer.OrdinalIgnoreCase); - private readonly Stack _context = new Stack(); - private string _currentPath; + private readonly IDictionary _data = + new SortedDictionary(StringComparer.OrdinalIgnoreCase); - public static IDictionary Parse(Stream input) - => new JsonConfigurationFileParser().ParseStream(input); + private string _currentPath; - private IDictionary ParseStream(Stream input) - { - _data.Clear(); - - var jsonDocumentOptions = new JsonDocumentOptions - { - CommentHandling = JsonCommentHandling.Skip, - AllowTrailingCommas = true, - }; - - using (var reader = new StreamReader(input)) - using (JsonDocument doc = JsonDocument.Parse(reader.ReadToEnd(), jsonDocumentOptions)) - { - if (doc.RootElement.ValueKind != JsonValueKind.Object) - { - throw new FormatException("Error_UnsupportedJSONToken"); - } - VisitElement(doc.RootElement); - } + private JsonConfigurationFileParser() + { + } - return _data; - } + public static IDictionary Parse(Stream input) + { + return new JsonConfigurationFileParser().ParseStream(input); + } - private void VisitElement(JsonElement element) + private IDictionary ParseStream(Stream input) + { + _data.Clear(); + + var jsonDocumentOptions = new JsonDocumentOptions { - foreach (JsonProperty property in element.EnumerateObject()) - { - EnterContext(property.Name); - VisitValue(property.Value); - ExitContext(); - } - } + CommentHandling = JsonCommentHandling.Skip, + AllowTrailingCommas = true + }; - private void VisitValue(JsonElement value) + using (var reader = new StreamReader(input)) + using (var doc = JsonDocument.Parse(reader.ReadToEnd(), jsonDocumentOptions)) { - switch (value.ValueKind) - { - case JsonValueKind.Object: - VisitElement(value); - break; - - case JsonValueKind.Array: - int index = 0; - foreach (JsonElement arrayElement in value.EnumerateArray()) - { - EnterContext(index.ToString()); - VisitValue(arrayElement); - ExitContext(); - index++; - } - break; - - case JsonValueKind.Number: - case JsonValueKind.String: - case JsonValueKind.True: - case JsonValueKind.False: - case JsonValueKind.Null: - string key = _currentPath; - if (_data.ContainsKey(key)) - { - throw new FormatException("Error_KeyIsDuplicated"); - } - _data[key] = value.ToString(); - break; - - default: - throw new FormatException("Error_UnsupportedJSONToken"); - } + if (doc.RootElement.ValueKind != JsonValueKind.Object) + throw new FormatException("Error_UnsupportedJSONToken"); + VisitElement(doc.RootElement); } - private void EnterContext(string context) + return _data; + } + + private void VisitElement(JsonElement element) + { + foreach (var property in element.EnumerateObject()) { - _context.Push(context); - _currentPath = ConfigurationPath.Combine(_context.Reverse()); + EnterContext(property.Name); + VisitValue(property.Value); + ExitContext(); } + } - private void ExitContext() + private void VisitValue(JsonElement value) + { + switch (value.ValueKind) { - _context.Pop(); - _currentPath = ConfigurationPath.Combine(_context.Reverse()); + case JsonValueKind.Object: + VisitElement(value); + break; + + case JsonValueKind.Array: + var index = 0; + foreach (var arrayElement in value.EnumerateArray()) + { + EnterContext(index.ToString()); + VisitValue(arrayElement); + ExitContext(); + index++; + } + + break; + + case JsonValueKind.Number: + case JsonValueKind.String: + case JsonValueKind.True: + case JsonValueKind.False: + case JsonValueKind.Null: + var key = _currentPath; + if (_data.ContainsKey(key)) throw new FormatException("Error_KeyIsDuplicated"); + _data[key] = value.ToString(); + break; + + default: + throw new FormatException("Error_UnsupportedJSONToken"); } } -} + + private void EnterContext(string context) + { + _context.Push(context); + _currentPath = ConfigurationPath.Combine(_context.Reverse()); + } + + private void ExitContext() + { + _context.Pop(); + _currentPath = ConfigurationPath.Combine(_context.Reverse()); + } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/Resources/Messages.cs b/src/AgileConfig.Server.Common/Resources/Messages.cs index 5cb71c48..f7e475b6 100644 --- a/src/AgileConfig.Server.Common/Resources/Messages.cs +++ b/src/AgileConfig.Server.Common/Resources/Messages.cs @@ -1,201 +1,313 @@ using System.Globalization; using System.Resources; -namespace AgileConfig.Server.Common.Resources +namespace AgileConfig.Server.Common.Resources; + +/// +/// Resource accessor used to obtain localized messages. +/// +public static class Messages { - /// - /// Resource accessor used to obtain localized messages. - /// - public static class Messages + private static ResourceManager _resourceManager; + + private static ResourceManager ResourceManager { - private static ResourceManager _resourceManager; - private static ResourceManager ResourceManager + get { - get - { - if (_resourceManager == null) - { - _resourceManager = new ResourceManager("AgileConfig.Server.Common.Resources.Messages", typeof(Messages).Assembly); - } - return _resourceManager; - } + if (_resourceManager == null) + _resourceManager = new ResourceManager("AgileConfig.Server.Common.Resources.Messages", + typeof(Messages).Assembly); + return _resourceManager; } + } - /// - /// Retrieve a localized string. - /// - /// Resource name. - /// Culture info; if null, the current thread culture is used. - /// Localized string. - public static string GetString(string name, CultureInfo culture = null) - { - return ResourceManager.GetString(name, culture ?? CultureInfo.CurrentUICulture); - } + // Validation messages. + public static string AppIdRequired => GetString(nameof(AppIdRequired)); + public static string AppIdMaxLength => GetString(nameof(AppIdMaxLength)); + public static string ConfigGroupMaxLength => GetString(nameof(ConfigGroupMaxLength)); + public static string ConfigKeyRequired => GetString(nameof(ConfigKeyRequired)); + public static string ConfigKeyMaxLength => GetString(nameof(ConfigKeyMaxLength)); + public static string ConfigValueMaxLength => GetString(nameof(ConfigValueMaxLength)); + public static string DescriptionMaxLength => GetString(nameof(DescriptionMaxLength)); + public static string UserIdRequired => GetString(nameof(UserIdRequired)); + public static string UserIdMaxLength => GetString(nameof(UserIdMaxLength)); + public static string UserNameRequired => GetString(nameof(UserNameRequired)); + public static string UserNameMaxLength => GetString(nameof(UserNameMaxLength)); + public static string PasswordRequired => GetString(nameof(PasswordRequired)); + public static string PasswordMaxLength => GetString(nameof(PasswordMaxLength)); + public static string TeamMaxLength => GetString(nameof(TeamMaxLength)); + public static string NodeAddressRequired => GetString(nameof(NodeAddressRequired)); + public static string NodeAddressMaxLength => GetString(nameof(NodeAddressMaxLength)); + public static string RemarkMaxLength => GetString(nameof(RemarkMaxLength)); + public static string ServiceIdRequired => GetString(nameof(ServiceIdRequired)); + public static string ServiceIdMaxLength => GetString(nameof(ServiceIdMaxLength)); + public static string ServiceNameRequired => GetString(nameof(ServiceNameRequired)); + public static string ServiceNameMaxLength => GetString(nameof(ServiceNameMaxLength)); + public static string IpMaxLength => GetString(nameof(IpMaxLength)); + public static string HealthCheckModeRequired => GetString(nameof(HealthCheckModeRequired)); + public static string HealthCheckModeMaxLength => GetString(nameof(HealthCheckModeMaxLength)); + public static string CheckUrlMaxLength => GetString(nameof(CheckUrlMaxLength)); + public static string AlarmUrlMaxLength => GetString(nameof(AlarmUrlMaxLength)); - /// - /// Retrieve a formatted localized string. - /// - /// Resource name. - /// Formatting arguments. - /// Formatted localized string. - public static string GetString(string name, params object[] args) - { - var format = GetString(name); - return string.Format(format, args); - } + // Error messages. + public static string IdCannotBeEmpty => GetString(nameof(IdCannotBeEmpty)); + public static string NameCannotBeEmpty => GetString(nameof(NameCannotBeEmpty)); + public static string AddressCannotBeEmpty => GetString(nameof(AddressCannotBeEmpty)); + public static string KeyCannotBeEmpty => GetString(nameof(KeyCannotBeEmpty)); + public static string AppIdCannotBeEmpty => GetString(nameof(AppIdCannotBeEmpty)); - /// - /// Retrieve a formatted localized string. - /// - /// Resource name. - /// Culture info. - /// Formatting arguments. - /// Formatted localized string. - public static string GetString(string name, CultureInfo culture, params object[] args) - { - var format = GetString(name, culture); - return string.Format(format, args); - } + // Log messages. + public static string RegistrationCenter => GetString(nameof(RegistrationCenter)); + public static string ConfigurationCenter => GetString(nameof(ConfigurationCenter)); + + public static string Success => GetString(nameof(Success)); + public static string Failed => GetString(nameof(Failed)); + + public static string AdminPasswordInitialized => GetString(nameof(AdminPasswordInitialized)); + + public static string Enabled => GetString(nameof(Enabled)); + public static string Disabled => GetString(nameof(Disabled)); + + // Exception messages. + public static string MongodbConnectionStringNotConfigured => + GetString(nameof(MongodbConnectionStringNotConfigured)); + + // Login messages. + public static string PasswordCannotBeEmpty => GetString(nameof(PasswordCannotBeEmpty)); + public static string PasswordError => GetString(nameof(PasswordError)); + public static string UserDeleted => GetString(nameof(UserDeleted)); + public static string PasswordMaxLength50 => GetString(nameof(PasswordMaxLength50)); + public static string PasswordMismatch => GetString(nameof(PasswordMismatch)); + public static string PasswordAlreadySet => GetString(nameof(PasswordAlreadySet)); + public static string InitPasswordFailed => GetString(nameof(InitPasswordFailed)); + public static string DemoModeNoPasswordChange => GetString(nameof(DemoModeNoPasswordChange)); + public static string OriginalPasswordCannotBeEmpty => GetString(nameof(OriginalPasswordCannotBeEmpty)); + public static string OriginalPasswordError => GetString(nameof(OriginalPasswordError)); + public static string NewPasswordCannotBeEmpty => GetString(nameof(NewPasswordCannotBeEmpty)); + public static string NewPasswordMaxLength50 => GetString(nameof(NewPasswordMaxLength50)); + public static string NewPasswordMismatch => GetString(nameof(NewPasswordMismatch)); + public static string UserNotFound => GetString(nameof(UserNotFound)); + public static string ChangePasswordFailed => GetString(nameof(ChangePasswordFailed)); + + public static string ConfigExists => GetString(nameof(ConfigExists)); + public static string CreateConfigFailed => GetString(nameof(CreateConfigFailed)); + + public static string BatchCreateConfigFailed => GetString(nameof(BatchCreateConfigFailed)); + public static string ConfigNotFound => GetString(nameof(ConfigNotFound)); + public static string UpdateConfigFailed => GetString(nameof(UpdateConfigFailed)); + public static string DeleteConfigFailed => GetString(nameof(DeleteConfigFailed)); + public static string BatchDeleteConfigFailed => GetString(nameof(BatchDeleteConfigFailed)); + + // Application messages. + public static string AppIdExists => GetString(nameof(AppIdExists)); + public static string CreateAppFailed => GetString(nameof(CreateAppFailed)); + public static string AppNotFound => GetString(nameof(AppNotFound)); + public static string DemoModeNoTestAppEdit => GetString(nameof(DemoModeNoTestAppEdit)); + public static string UpdateAppFailed => GetString(nameof(UpdateAppFailed)); + public static string ConfigKeyExists => GetString(nameof(ConfigKeyExists)); + + // App Controller specific messages + public static string CurrentCannotBeLessThanOne => GetString(nameof(CurrentCannotBeLessThanOne)); + public static string PageSizeCannotBeLessThanOne => GetString(nameof(PageSizeCannotBeLessThanOne)); + public static string DemoModeNoClientDisconnect => GetString(nameof(DemoModeNoClientDisconnect)); + + // Server Node Messages + public static string NodeAlreadyExists => GetString(nameof(NodeAlreadyExists)); + public static string AddNodeFailed => GetString(nameof(AddNodeFailed)); + public static string DemoModeNoNodeDelete => GetString(nameof(DemoModeNoNodeDelete)); + public static string NodeNotFound => GetString(nameof(NodeNotFound)); + public static string DeleteNodeFailed => GetString(nameof(DeleteNodeFailed)); + + // Service Messages + public static string ServiceAlreadyExists => GetString(nameof(ServiceAlreadyExists)); + public static string ServiceNotFound => GetString(nameof(ServiceNotFound)); + public static string CurrentCannotBeLessThanOneService => GetString(nameof(CurrentCannotBeLessThanOneService)); + public static string PageSizeCannotBeLessThanOneService => GetString(nameof(PageSizeCannotBeLessThanOneService)); + + // User Messages + public static string CurrentCannotBeLessThanOneUser => GetString(nameof(CurrentCannotBeLessThanOneUser)); + public static string PageSizeCannotBeLessThanOneUser => GetString(nameof(PageSizeCannotBeLessThanOneUser)); + + public static string AddUserFailed => GetString(nameof(AddUserFailed)); + public static string UserNotFoundForOperation => GetString(nameof(UserNotFoundForOperation)); + public static string UpdateUserFailed => GetString(nameof(UpdateUserFailed)); + public static string ResetUserPasswordFailed => GetString(nameof(ResetUserPasswordFailed)); + public static string DeleteUserFailed => GetString(nameof(DeleteUserFailed)); + public static string UpdateRoleFailed => GetString(nameof(UpdateRoleFailed)); + public static string DeleteRoleFailed => GetString(nameof(DeleteRoleFailed)); + + /// + /// Retrieve a localized string. + /// + /// Resource name. + /// Culture info; if null, the current thread culture is used. + /// Localized string. + public static string GetString(string name, CultureInfo culture = null) + { + return ResourceManager.GetString(name, culture ?? CultureInfo.CurrentUICulture); + } + + /// + /// Retrieve a formatted localized string. + /// + /// Resource name. + /// Formatting arguments. + /// Formatted localized string. + public static string GetString(string name, params object[] args) + { + var format = GetString(name); + return string.Format(format, args); + } + + /// + /// Retrieve a formatted localized string. + /// + /// Resource name. + /// Culture info. + /// Formatting arguments. + /// Formatted localized string. + public static string GetString(string name, CultureInfo culture, params object[] args) + { + var format = GetString(name, culture); + return string.Format(format, args); + } + + // Configuration import messages. + public static string ConfigImportMissingEqualSign(int lineNumber) + { + return GetString(nameof(ConfigImportMissingEqualSign), lineNumber); + } + + public static string ConfigImportDuplicateKey(string key) + { + return GetString(nameof(ConfigImportDuplicateKey), key); + } + + public static string NotifyNodeAllClients(string node, string action1, string action2, string response) + { + return GetString(nameof(NotifyNodeAllClients), node, action1, action2, response); + } + + public static string NotifyNodeAppClients(string node, string app, string action1, string action2, string response) + { + return GetString(nameof(NotifyNodeAppClients), node, app, action1, action2, response); + } + + public static string NotifyNodeSpecificClient(string node, string client, string action1, string action2, + string response) + { + return GetString(nameof(NotifyNodeSpecificClient), node, client, action1, action2, response); + } + + // Event log messages. + public static string LoginSuccess(string user) + { + return GetString(nameof(LoginSuccess), user); + } + + public static string PasswordReset(string admin, string user) + { + return GetString(nameof(PasswordReset), admin, user); + } - // Validation messages. - public static string AppIdRequired => GetString(nameof(AppIdRequired)); - public static string AppIdMaxLength => GetString(nameof(AppIdMaxLength)); - public static string ConfigGroupMaxLength => GetString(nameof(ConfigGroupMaxLength)); - public static string ConfigKeyRequired => GetString(nameof(ConfigKeyRequired)); - public static string ConfigKeyMaxLength => GetString(nameof(ConfigKeyMaxLength)); - public static string ConfigValueMaxLength => GetString(nameof(ConfigValueMaxLength)); - public static string DescriptionMaxLength => GetString(nameof(DescriptionMaxLength)); - public static string UserIdRequired => GetString(nameof(UserIdRequired)); - public static string UserIdMaxLength => GetString(nameof(UserIdMaxLength)); - public static string UserNameRequired => GetString(nameof(UserNameRequired)); - public static string UserNameMaxLength => GetString(nameof(UserNameMaxLength)); - public static string PasswordRequired => GetString(nameof(PasswordRequired)); - public static string PasswordMaxLength => GetString(nameof(PasswordMaxLength)); - public static string TeamMaxLength => GetString(nameof(TeamMaxLength)); - public static string NodeAddressRequired => GetString(nameof(NodeAddressRequired)); - public static string NodeAddressMaxLength => GetString(nameof(NodeAddressMaxLength)); - public static string RemarkMaxLength => GetString(nameof(RemarkMaxLength)); - public static string ServiceIdRequired => GetString(nameof(ServiceIdRequired)); - public static string ServiceIdMaxLength => GetString(nameof(ServiceIdMaxLength)); - public static string ServiceNameRequired => GetString(nameof(ServiceNameRequired)); - public static string ServiceNameMaxLength => GetString(nameof(ServiceNameMaxLength)); - public static string IpMaxLength => GetString(nameof(IpMaxLength)); - public static string HealthCheckModeRequired => GetString(nameof(HealthCheckModeRequired)); - public static string HealthCheckModeMaxLength => GetString(nameof(HealthCheckModeMaxLength)); - public static string CheckUrlMaxLength => GetString(nameof(CheckUrlMaxLength)); - public static string AlarmUrlMaxLength => GetString(nameof(AlarmUrlMaxLength)); - - // Error messages. - public static string IdCannotBeEmpty => GetString(nameof(IdCannotBeEmpty)); - public static string NameCannotBeEmpty => GetString(nameof(NameCannotBeEmpty)); - public static string AddressCannotBeEmpty => GetString(nameof(AddressCannotBeEmpty)); - public static string KeyCannotBeEmpty => GetString(nameof(KeyCannotBeEmpty)); - public static string AppIdCannotBeEmpty => GetString(nameof(AppIdCannotBeEmpty)); - - // Configuration import messages. - public static string ConfigImportMissingEqualSign(int lineNumber) => GetString(nameof(ConfigImportMissingEqualSign), lineNumber); - public static string ConfigImportDuplicateKey(string key) => GetString(nameof(ConfigImportDuplicateKey), key); - - // Log messages. - public static string RegistrationCenter => GetString(nameof(RegistrationCenter)); - public static string ConfigurationCenter => GetString(nameof(ConfigurationCenter)); - public static string NotifyNodeAllClients(string node, string action1, string action2, string response) => GetString(nameof(NotifyNodeAllClients), node, action1, action2, response); - public static string NotifyNodeAppClients(string node, string app, string action1, string action2, string response) => GetString(nameof(NotifyNodeAppClients), node, app, action1, action2, response); - public static string NotifyNodeSpecificClient(string node, string client, string action1, string action2, string response) => GetString(nameof(NotifyNodeSpecificClient), node, client, action1, action2, response); - public static string Success => GetString(nameof(Success)); - public static string Failed => GetString(nameof(Failed)); - - // Event log messages. - public static string LoginSuccess(string user) => GetString(nameof(LoginSuccess), user); - public static string AdminPasswordInitialized => GetString(nameof(AdminPasswordInitialized)); - public static string PasswordReset(string admin, string user) => GetString(nameof(PasswordReset), admin, user); - public static string PasswordChangeSuccess(string user) => GetString(nameof(PasswordChangeSuccess), user); - public static string AppAdded(string user, string appId, string appName) => GetString(nameof(AppAdded), user, appId, appName); - public static string AppUpdated(string user, string appId, string appName) => GetString(nameof(AppUpdated), user, appId, appName); - public static string AppStatusChanged(string user, string status, string appId) => GetString(nameof(AppStatusChanged), user, status, appId); - public static string AppDeleted(string user, string appId) => GetString(nameof(AppDeleted), user, appId); - public static string ConfigAdded(string user, string group, string key, string appId, string env) => GetString(nameof(ConfigAdded), user, group, key, appId, env); - public static string ConfigUpdated(string user, string group, string key, string appId, string env) => GetString(nameof(ConfigUpdated), user, group, key, appId, env); - public static string ConfigDeleted(string user, string group, string key, string appId, string env) => GetString(nameof(ConfigDeleted), user, group, key, appId, env); - public static string ConfigBatchDeleted(string user, string env) => GetString(nameof(ConfigBatchDeleted), user, env); - public static string ConfigPublished(string user, string appId, string env, string version) => GetString(nameof(ConfigPublished), user, appId, env, version); - public static string ConfigRolledBack(string user, string appId, string env, string version) => GetString(nameof(ConfigRolledBack), user, appId, env, version); - public static string ConfigCancelled(string user, string group, string key, string appId, string env) => GetString(nameof(ConfigCancelled), user, group, key, appId, env); - public static string ConfigBatchCancelled(string user, string env) => GetString(nameof(ConfigBatchCancelled), user, env); - public static string NodeAdded(string user, string node) => GetString(nameof(NodeAdded), user, node); - public static string NodeDeleted(string user, string node) => GetString(nameof(NodeDeleted), user, node); - public static string UserAdded(string admin, string user) => GetString(nameof(UserAdded), admin, user); - public static string UserUpdated(string admin, string user) => GetString(nameof(UserUpdated), admin, user); - public static string Enabled => GetString(nameof(Enabled)); - public static string Disabled => GetString(nameof(Disabled)); - - // Exception messages. - public static string MongodbConnectionStringNotConfigured => GetString(nameof(MongodbConnectionStringNotConfigured)); - - // Login messages. - public static string PasswordCannotBeEmpty => GetString(nameof(PasswordCannotBeEmpty)); - public static string PasswordError => GetString(nameof(PasswordError)); - public static string UserDeleted => GetString(nameof(UserDeleted)); - public static string PasswordMaxLength50 => GetString(nameof(PasswordMaxLength50)); - public static string PasswordMismatch => GetString(nameof(PasswordMismatch)); - public static string PasswordAlreadySet => GetString(nameof(PasswordAlreadySet)); - public static string InitPasswordFailed => GetString(nameof(InitPasswordFailed)); - public static string DemoModeNoPasswordChange => GetString(nameof(DemoModeNoPasswordChange)); - public static string OriginalPasswordCannotBeEmpty => GetString(nameof(OriginalPasswordCannotBeEmpty)); - public static string OriginalPasswordError => GetString(nameof(OriginalPasswordError)); - public static string NewPasswordCannotBeEmpty => GetString(nameof(NewPasswordCannotBeEmpty)); - public static string NewPasswordMaxLength50 => GetString(nameof(NewPasswordMaxLength50)); - public static string NewPasswordMismatch => GetString(nameof(NewPasswordMismatch)); - public static string UserNotFound => GetString(nameof(UserNotFound)); - public static string ChangePasswordFailed => GetString(nameof(ChangePasswordFailed)); - - // Configuration messages. - public static string AppNotExists(string appId) => GetString(nameof(AppNotExists), appId); - public static string ConfigExists => GetString(nameof(ConfigExists)); - public static string CreateConfigFailed => GetString(nameof(CreateConfigFailed)); - public static string DuplicateConfig(string key) => GetString(nameof(DuplicateConfig), key); - public static string BatchCreateConfigFailed => GetString(nameof(BatchCreateConfigFailed)); - public static string ConfigNotFound => GetString(nameof(ConfigNotFound)); - public static string UpdateConfigFailed => GetString(nameof(UpdateConfigFailed)); - public static string DeleteConfigFailed => GetString(nameof(DeleteConfigFailed)); - public static string BatchDeleteConfigFailed => GetString(nameof(BatchDeleteConfigFailed)); - - // Application messages. - public static string AppIdExists => GetString(nameof(AppIdExists)); - public static string CreateAppFailed => GetString(nameof(CreateAppFailed)); - public static string AppNotFound => GetString(nameof(AppNotFound)); - public static string DemoModeNoTestAppEdit => GetString(nameof(DemoModeNoTestAppEdit)); - public static string UpdateAppFailed => GetString(nameof(UpdateAppFailed)); - public static string ConfigKeyExists => GetString(nameof(ConfigKeyExists)); - - // App Controller specific messages - public static string CurrentCannotBeLessThanOne => GetString(nameof(CurrentCannotBeLessThanOne)); - public static string PageSizeCannotBeLessThanOne => GetString(nameof(PageSizeCannotBeLessThanOne)); - public static string DemoModeNoClientDisconnect => GetString(nameof(DemoModeNoClientDisconnect)); - - // Server Node Messages - public static string NodeAlreadyExists => GetString(nameof(NodeAlreadyExists)); - public static string AddNodeFailed => GetString(nameof(AddNodeFailed)); - public static string DemoModeNoNodeDelete => GetString(nameof(DemoModeNoNodeDelete)); - public static string NodeNotFound => GetString(nameof(NodeNotFound)); - public static string DeleteNodeFailed => GetString(nameof(DeleteNodeFailed)); - - // Service Messages - public static string ServiceAlreadyExists => GetString(nameof(ServiceAlreadyExists)); - public static string ServiceNotFound => GetString(nameof(ServiceNotFound)); - public static string CurrentCannotBeLessThanOneService => GetString(nameof(CurrentCannotBeLessThanOneService)); - public static string PageSizeCannotBeLessThanOneService => GetString(nameof(PageSizeCannotBeLessThanOneService)); - - // User Messages - public static string CurrentCannotBeLessThanOneUser => GetString(nameof(CurrentCannotBeLessThanOneUser)); - public static string PageSizeCannotBeLessThanOneUser => GetString(nameof(PageSizeCannotBeLessThanOneUser)); - public static string UserAlreadyExists(string userName) => GetString(nameof(UserAlreadyExists), userName); - public static string AddUserFailed => GetString(nameof(AddUserFailed)); - public static string UserNotFoundForOperation => GetString(nameof(UserNotFoundForOperation)); - public static string UpdateUserFailed => GetString(nameof(UpdateUserFailed)); - public static string ResetUserPasswordFailed => GetString(nameof(ResetUserPasswordFailed)); - public static string DeleteUserFailed => GetString(nameof(DeleteUserFailed)); - public static string UpdateRoleFailed => GetString(nameof(UpdateRoleFailed)); - public static string DeleteRoleFailed => GetString(nameof(DeleteRoleFailed)); - } -} + public static string PasswordChangeSuccess(string user) + { + return GetString(nameof(PasswordChangeSuccess), user); + } + + public static string AppAdded(string user, string appId, string appName) + { + return GetString(nameof(AppAdded), user, appId, appName); + } + + public static string AppUpdated(string user, string appId, string appName) + { + return GetString(nameof(AppUpdated), user, appId, appName); + } + + public static string AppStatusChanged(string user, string status, string appId) + { + return GetString(nameof(AppStatusChanged), user, status, appId); + } + + public static string AppDeleted(string user, string appId) + { + return GetString(nameof(AppDeleted), user, appId); + } + + public static string ConfigAdded(string user, string group, string key, string appId, string env) + { + return GetString(nameof(ConfigAdded), user, group, key, appId, env); + } + + public static string ConfigUpdated(string user, string group, string key, string appId, string env) + { + return GetString(nameof(ConfigUpdated), user, group, key, appId, env); + } + + public static string ConfigDeleted(string user, string group, string key, string appId, string env) + { + return GetString(nameof(ConfigDeleted), user, group, key, appId, env); + } + + public static string ConfigBatchDeleted(string user, string env) + { + return GetString(nameof(ConfigBatchDeleted), user, env); + } + + public static string ConfigPublished(string user, string appId, string env, string version) + { + return GetString(nameof(ConfigPublished), user, appId, env, version); + } + + public static string ConfigRolledBack(string user, string appId, string env, string version) + { + return GetString(nameof(ConfigRolledBack), user, appId, env, version); + } + + public static string ConfigCancelled(string user, string group, string key, string appId, string env) + { + return GetString(nameof(ConfigCancelled), user, group, key, appId, env); + } + + public static string ConfigBatchCancelled(string user, string env) + { + return GetString(nameof(ConfigBatchCancelled), user, env); + } + + public static string NodeAdded(string user, string node) + { + return GetString(nameof(NodeAdded), user, node); + } + + public static string NodeDeleted(string user, string node) + { + return GetString(nameof(NodeDeleted), user, node); + } + + public static string UserAdded(string admin, string user) + { + return GetString(nameof(UserAdded), admin, user); + } + + public static string UserUpdated(string admin, string user) + { + return GetString(nameof(UserUpdated), admin, user); + } + + // Configuration messages. + public static string AppNotExists(string appId) + { + return GetString(nameof(AppNotExists), appId); + } + + public static string DuplicateConfig(string key) + { + return GetString(nameof(DuplicateConfig), key); + } + + public static string UserAlreadyExists(string userName) + { + return GetString(nameof(UserAlreadyExists), userName); + } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/RestClient/DefaultRestClient.cs b/src/AgileConfig.Server.Common/RestClient/DefaultRestClient.cs index 8fe811fa..f388af8a 100644 --- a/src/AgileConfig.Server.Common/RestClient/DefaultRestClient.cs +++ b/src/AgileConfig.Server.Common/RestClient/DefaultRestClient.cs @@ -1,78 +1,68 @@ using System.Collections.Generic; using System.Net.Http; +using System.Net.Http.Headers; using System.Threading.Tasks; +using Newtonsoft.Json; -namespace AgileConfig.Server.Common.RestClient +namespace AgileConfig.Server.Common.RestClient; + +public class DefaultRestClient : IRestClient { - public class DefaultRestClient : IRestClient - { - private readonly IHttpClientFactory _httpClientFactory; + private readonly IHttpClientFactory _httpClientFactory; - public DefaultRestClient( - IHttpClientFactory httpClientFactory - ) - { - this._httpClientFactory = httpClientFactory; - } + public DefaultRestClient( + IHttpClientFactory httpClientFactory + ) + { + _httpClientFactory = httpClientFactory; + } - private HttpClient GetDefaultClient() - { - return _httpClientFactory.CreateClient(Global.DefaultHttpClientName); - } + public async Task GetAsync(string url, Dictionary headers = null) + { + using var resp = await GetAsync(url, headers); + resp.EnsureSuccessStatusCode(); - public async Task GetAsync(string url, Dictionary headers = null) - { - using var resp = await GetAsync(url, headers); - resp.EnsureSuccessStatusCode(); + var json = await resp.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(json); + } - var json = await resp.Content.ReadAsStringAsync(); - return Newtonsoft.Json.JsonConvert.DeserializeObject(json); - } + public async Task PostAsync(string url, object data, Dictionary headers = null) + { + using var resp = await PostAsync(url, data, headers); - public async Task PostAsync(string url, object data, Dictionary headers = null) - { - using var resp = await PostAsync(url, data, headers); + resp.EnsureSuccessStatusCode(); - resp.EnsureSuccessStatusCode(); + var json = await resp.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(json); + } - var json = await resp.Content.ReadAsStringAsync(); - return Newtonsoft.Json.JsonConvert.DeserializeObject(json); - } + public Task PostAsync(string url, object data, Dictionary headers = null) + { + var httpclient = GetDefaultClient(); + if (headers != null) + foreach (var header in headers) + httpclient.DefaultRequestHeaders.Add(header.Key, header.Value); - public Task PostAsync(string url, object data, Dictionary headers = null) - { - var httpclient = GetDefaultClient(); - if (headers != null) - { - foreach (var header in headers) - { - httpclient.DefaultRequestHeaders.Add(header.Key, header.Value); - } - } + var jsondata = ""; + if (data != null) jsondata = JsonConvert.SerializeObject(data); + var stringContent = new StringContent(jsondata, + new MediaTypeHeaderValue("application/json")); - var jsondata = ""; - if (data != null) - { - jsondata = Newtonsoft.Json.JsonConvert.SerializeObject(data); - } - var stringContent = new StringContent(jsondata, - new System.Net.Http.Headers.MediaTypeHeaderValue("application/json")); + return httpclient.PostAsync(url, stringContent); + } - return httpclient.PostAsync(url, stringContent); - } + public Task GetAsync(string url, Dictionary headers = null) + { + var httpclient = GetDefaultClient(); + if (headers != null) + foreach (var header in headers) + httpclient.DefaultRequestHeaders.Add(header.Key, header.Value); - public Task GetAsync(string url, Dictionary headers = null) - { - var httpclient = GetDefaultClient(); - if (headers != null) - { - foreach (var header in headers) - { - httpclient.DefaultRequestHeaders.Add(header.Key, header.Value); - } - } + return httpclient.GetAsync(url); + } - return httpclient.GetAsync(url); - } + private HttpClient GetDefaultClient() + { + return _httpClientFactory.CreateClient(Global.DefaultHttpClientName); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/RestClient/IRestClient.cs b/src/AgileConfig.Server.Common/RestClient/IRestClient.cs index d379c07e..bfb61347 100644 --- a/src/AgileConfig.Server.Common/RestClient/IRestClient.cs +++ b/src/AgileConfig.Server.Common/RestClient/IRestClient.cs @@ -2,17 +2,15 @@ using System.Net.Http; using System.Threading.Tasks; -namespace AgileConfig.Server.Common.RestClient -{ - public interface IRestClient - { - Task GetAsync(string url, Dictionary headers = null); +namespace AgileConfig.Server.Common.RestClient; - Task GetAsync(string url, Dictionary headers = null); +public interface IRestClient +{ + Task GetAsync(string url, Dictionary headers = null); - Task PostAsync(string url, object data, Dictionary headers = null); + Task GetAsync(string url, Dictionary headers = null); - Task PostAsync(string url, object data, Dictionary headers = null); + Task PostAsync(string url, object data, Dictionary headers = null); - } -} + Task PostAsync(string url, object data, Dictionary headers = null); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/RestClient/ServiceCollectionEx.cs b/src/AgileConfig.Server.Common/RestClient/ServiceCollectionEx.cs index e9d0bcd7..f43d5efe 100644 --- a/src/AgileConfig.Server.Common/RestClient/ServiceCollectionEx.cs +++ b/src/AgileConfig.Server.Common/RestClient/ServiceCollectionEx.cs @@ -1,12 +1,11 @@ using Microsoft.Extensions.DependencyInjection; -namespace AgileConfig.Server.Common.RestClient +namespace AgileConfig.Server.Common.RestClient; + +public static class ServiceCollectionEx { - public static class ServiceCollectionEx + public static void AddRestClient(this IServiceCollection sc) { - public static void AddRestClient(this IServiceCollection sc) - { - sc.AddScoped(); - } + sc.AddScoped(); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/SystemRoleConstants.cs b/src/AgileConfig.Server.Common/SystemRoleConstants.cs index 1a768ed7..3d1dfc71 100644 --- a/src/AgileConfig.Server.Common/SystemRoleConstants.cs +++ b/src/AgileConfig.Server.Common/SystemRoleConstants.cs @@ -1,13 +1,8 @@ -namespace AgileConfig.Server.Common -{ - public static class SystemRoleConstants - { - public const string SuperAdminId = "00000000-0000-0000-0000-000000000001"; - public const string AdminId = "00000000-0000-0000-0000-000000000002"; - public const string OperatorId = "00000000-0000-0000-0000-000000000003"; +namespace AgileConfig.Server.Common; - public const string SuperAdminCode = "SuperAdmin"; - public const string AdminCode = "Admin"; - public const string OperatorCode = "Operator"; - } -} +public static class SystemRoleConstants +{ + public const string SuperAdminId = "00000000-0000-0000-0000-000000000001"; + public const string AdminId = "00000000-0000-0000-0000-000000000002"; + public const string OperatorId = "00000000-0000-0000-0000-000000000003"; +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Common/SystemSettings.cs b/src/AgileConfig.Server.Common/SystemSettings.cs index 06e957c3..8efb8ac5 100644 --- a/src/AgileConfig.Server.Common/SystemSettings.cs +++ b/src/AgileConfig.Server.Common/SystemSettings.cs @@ -4,7 +4,7 @@ public static class SystemSettings { public const string SuperAdminId = "super_admin"; public const string SuperAdminUserName = "admin"; - + public const string DefaultEnvironment = "DEV,TEST,STAGING,PROD"; public const string DefaultEnvironmentKey = "environment"; public const string DefaultJwtSecretKey = "jwtsecret"; diff --git a/src/AgileConfig.Server.Data.Abstraction/DbConfig/DbConfigInfo.cs b/src/AgileConfig.Server.Data.Abstraction/DbConfig/DbConfigInfo.cs index b2a8544e..adbae8b5 100644 --- a/src/AgileConfig.Server.Data.Abstraction/DbConfig/DbConfigInfo.cs +++ b/src/AgileConfig.Server.Data.Abstraction/DbConfig/DbConfigInfo.cs @@ -1,23 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace AgileConfig.Server.Data.Abstraction.DbProvider; -namespace AgileConfig.Server.Data.Abstraction.DbProvider +public class DbConfigInfo : IDbConfigInfo { - public class DbConfigInfo : IDbConfigInfo + public DbConfigInfo(string env, string provider, string conn) { - public DbConfigInfo(string env, string provider, string conn) - { - this.Env = env; - this.Provider = provider; - this.ConnectionString = conn; - } - public string Env { get; } + Env = env; + Provider = provider; + ConnectionString = conn; + } - public string Provider { get; } + public string Env { get; } - public string ConnectionString { get; } - } -} + public string Provider { get; } + + public string ConnectionString { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/DbConfig/DbConfigInfoFactory.cs b/src/AgileConfig.Server.Data.Abstraction/DbConfig/DbConfigInfoFactory.cs index 7acbccbe..a84a3a1a 100644 --- a/src/AgileConfig.Server.Data.Abstraction/DbConfig/DbConfigInfoFactory.cs +++ b/src/AgileConfig.Server.Data.Abstraction/DbConfig/DbConfigInfoFactory.cs @@ -1,74 +1,54 @@ -using AgileConfig.Server.Common; +using System.Collections.Concurrent; using Microsoft.Extensions.Configuration; -using System.Collections.Concurrent; -namespace AgileConfig.Server.Data.Abstraction.DbProvider +namespace AgileConfig.Server.Data.Abstraction.DbProvider; + +public class DbConfigInfoFactory : IDbConfigInfoFactory { - public class DbConfigInfoFactory : IDbConfigInfoFactory - { - private ConcurrentDictionary _envProviders = new ConcurrentDictionary(); - private readonly IConfiguration _configuration; + private readonly IConfiguration _configuration; + private readonly ConcurrentDictionary _envProviders = new(); - private IDbConfigInfo _default { get; } + public DbConfigInfoFactory(IConfiguration configuration) + { + var providerPath = "db:provider"; + var connPath = "db:conn"; - public DbConfigInfoFactory(IConfiguration configuration) - { - var providerPath = $"db:provider"; - var connPath = $"db:conn"; + var providerValue = configuration[providerPath]; + var connValue = configuration[connPath]; - var providerValue = configuration[providerPath]; - var connValue = configuration[connPath]; + if (string.IsNullOrEmpty(providerValue)) throw new ArgumentNullException(providerPath); + if (string.IsNullOrEmpty(connValue)) throw new ArgumentNullException(connPath); - if (string.IsNullOrEmpty(providerValue)) - { - throw new ArgumentNullException(providerPath); - } - if (string.IsNullOrEmpty(connValue)) - { - throw new ArgumentNullException(connPath); - } + var configInfo = new DbConfigInfo("", providerValue, connValue); + _default = configInfo; + _envProviders.TryAdd(providerPath, configInfo); + _configuration = configuration; + } - var configInfo = new DbConfigInfo("", providerValue, connValue); - _default = configInfo; - _envProviders.TryAdd(providerPath, configInfo); - this._configuration = configuration; - } + private IDbConfigInfo _default { get; } - public IDbConfigInfo GetConfigInfo(string env = "") - { - if (string.IsNullOrEmpty(env)) - { - return _default; - } + public IDbConfigInfo GetConfigInfo(string env = "") + { + if (string.IsNullOrEmpty(env)) return _default; - var providerPath = $""; - var connPath = $""; - providerPath = $"db:env:{env}:provider"; - connPath = $"db:env:{env}:conn"; + var providerPath = ""; + var connPath = ""; + providerPath = $"db:env:{env}:provider"; + connPath = $"db:env:{env}:conn"; - _envProviders.TryGetValue(providerPath, out IDbConfigInfo configInfo); + _envProviders.TryGetValue(providerPath, out var configInfo); - if (configInfo != null) - { - return configInfo; - } + if (configInfo != null) return configInfo; - var providerValue = _configuration[providerPath]; - var connValue = _configuration[connPath]; + var providerValue = _configuration[providerPath]; + var connValue = _configuration[connPath]; - if (string.IsNullOrEmpty(providerValue)) - { - return _default; - } - if (string.IsNullOrEmpty(connValue)) - { - return _default; - } + if (string.IsNullOrEmpty(providerValue)) return _default; + if (string.IsNullOrEmpty(connValue)) return _default; - configInfo = new DbConfigInfo(env, providerValue, connValue); - _envProviders.TryAdd(providerPath, configInfo); + configInfo = new DbConfigInfo(env, providerValue, connValue); + _envProviders.TryAdd(providerPath, configInfo); - return configInfo; - } + return configInfo; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/DbConfig/IDbConfigInfo.cs b/src/AgileConfig.Server.Data.Abstraction/DbConfig/IDbConfigInfo.cs index 7ae24c36..8a58c4eb 100644 --- a/src/AgileConfig.Server.Data.Abstraction/DbConfig/IDbConfigInfo.cs +++ b/src/AgileConfig.Server.Data.Abstraction/DbConfig/IDbConfigInfo.cs @@ -1,10 +1,9 @@ -namespace AgileConfig.Server.Data.Abstraction.DbProvider +namespace AgileConfig.Server.Data.Abstraction.DbProvider; + +public interface IDbConfigInfo { - public interface IDbConfigInfo - { - string ConnectionString { get; } - string Env { get; } + string ConnectionString { get; } + string Env { get; } - string Provider { get; } - } + string Provider { get; } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/DbConfig/IDbConfigInfoFactory.cs b/src/AgileConfig.Server.Data.Abstraction/DbConfig/IDbConfigInfoFactory.cs index 30c94064..f7f3b5c5 100644 --- a/src/AgileConfig.Server.Data.Abstraction/DbConfig/IDbConfigInfoFactory.cs +++ b/src/AgileConfig.Server.Data.Abstraction/DbConfig/IDbConfigInfoFactory.cs @@ -1,7 +1,6 @@ -namespace AgileConfig.Server.Data.Abstraction.DbProvider +namespace AgileConfig.Server.Data.Abstraction.DbProvider; + +public interface IDbConfigInfoFactory { - public interface IDbConfigInfoFactory - { - IDbConfigInfo GetConfigInfo(string env = ""); - } + IDbConfigInfo GetConfigInfo(string env = ""); } \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/DbConfig/ServiceCollectionExtension.cs b/src/AgileConfig.Server.Data.Abstraction/DbConfig/ServiceCollectionExtension.cs index a873bf63..fef4c814 100644 --- a/src/AgileConfig.Server.Data.Abstraction/DbConfig/ServiceCollectionExtension.cs +++ b/src/AgileConfig.Server.Data.Abstraction/DbConfig/ServiceCollectionExtension.cs @@ -1,20 +1,14 @@ using AgileConfig.Server.Data.Abstraction.DbProvider; using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +public static class ServiceCollectionExtension { - public static class ServiceCollectionExtension + public static IServiceCollection AddDbConfigInfoFactory(this IServiceCollection sc) { - public static IServiceCollection AddDbConfigInfoFactory(this IServiceCollection sc) - { - sc.AddSingleton(); + sc.AddSingleton(); - return sc; - } + return sc; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IAppInheritancedRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IAppInheritancedRepository.cs index 122f2625..5c6420da 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IAppInheritancedRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IAppInheritancedRepository.cs @@ -1,8 +1,7 @@ using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +public interface IAppInheritancedRepository : IRepository { - public interface IAppInheritancedRepository : IRepository - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IAppRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IAppRepository.cs index a3c4bfcb..57d71683 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IAppRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IAppRepository.cs @@ -1,8 +1,7 @@ using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +public interface IAppRepository : IRepository { - public interface IAppRepository: IRepository - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IConfigPublishedRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IConfigPublishedRepository.cs index b833da5c..fa2efa8e 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IConfigPublishedRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IConfigPublishedRepository.cs @@ -1,8 +1,7 @@ using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +public interface IConfigPublishedRepository : IRepository { - public interface IConfigPublishedRepository : IRepository - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IConfigRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IConfigRepository.cs index 062bc4c1..def100f1 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IConfigRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IConfigRepository.cs @@ -1,8 +1,7 @@ using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +public interface IConfigRepository : IRepository { - public interface IConfigRepository : IRepository - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IFunctionRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IFunctionRepository.cs new file mode 100644 index 00000000..acfcddff --- /dev/null +++ b/src/AgileConfig.Server.Data.Abstraction/IFunctionRepository.cs @@ -0,0 +1,7 @@ +using AgileConfig.Server.Data.Entity; + +namespace AgileConfig.Server.Data.Abstraction; + +public interface IFunctionRepository : IRepository +{ +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IPublishDetailRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IPublishDetailRepository.cs index 06319df8..4142cb3c 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IPublishDetailRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IPublishDetailRepository.cs @@ -1,8 +1,7 @@ using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +public interface IPublishDetailRepository : IRepository { - public interface IPublishDetailRepository : IRepository - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IPublishTimelineRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IPublishTimelineRepository.cs index 606115fd..529d7f95 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IPublishTimelineRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IPublishTimelineRepository.cs @@ -1,9 +1,8 @@ using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +public interface IPublishTimelineRepository : IRepository { - public interface IPublishTimelineRepository : IRepository - { - Task GetLastPublishTimelineNodeIdAsync(string appId, string env); - } -} + Task GetLastPublishTimelineNodeIdAsync(string appId, string env); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IRepository.cs index ba324997..26b42f44 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IRepository.cs @@ -1,33 +1,31 @@ -using AgileConfig.Server.Common; -using System.Linq.Expressions; +using System.Linq.Expressions; +using AgileConfig.Server.Common; -namespace AgileConfig.Server.Data.Abstraction -{ - public interface IRepository : IDisposable where T : IEntity - { - Task> AllAsync(); - Task GetAsync(T1 id); +namespace AgileConfig.Server.Data.Abstraction; - Task UpdateAsync(T entity); - Task UpdateAsync(IList entities); +public interface IRepository : IDisposable where T : IEntity +{ + IUow Uow { get; set; } + Task> AllAsync(); + Task GetAsync(T1 id); - Task DeleteAsync(T1 id); + Task UpdateAsync(T entity); + Task UpdateAsync(IList entities); - Task DeleteAsync(T entity); + Task DeleteAsync(T1 id); - Task DeleteAsync(IList entities); + Task DeleteAsync(T entity); - Task InsertAsync(T entity); + Task DeleteAsync(IList entities); - Task InsertAsync(IList entities); + Task InsertAsync(T entity); - Task> QueryAsync(Expression> exp); + Task InsertAsync(IList entities); - Task> QueryPageAsync(Expression> exp, int pageIndex, int pageSize, - string defaultSortField = "Id", string defaultSortType = "ASC"); + Task> QueryAsync(Expression> exp); - Task CountAsync(Expression>? exp = null); + Task> QueryPageAsync(Expression> exp, int pageIndex, int pageSize, + string defaultSortField = "Id", string defaultSortType = "ASC"); - IUow Uow { get; set; } - } -} + Task CountAsync(Expression>? exp = null); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IRepositoryServiceRegister.cs b/src/AgileConfig.Server.Data.Abstraction/IRepositoryServiceRegister.cs index 38478cee..8b46a8e2 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IRepositoryServiceRegister.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IRepositoryServiceRegister.cs @@ -1,32 +1,31 @@ using Microsoft.Extensions.DependencyInjection; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +/// +/// Defines the contract for integrating a new storage provider. +/// +public interface IRepositoryServiceRegister { /// - /// Defines the contract for integrating a new storage provider. + /// Determine whether this register matches the specified provider name. /// - public interface IRepositoryServiceRegister - { - /// - /// Determine whether this register matches the specified provider name. - /// - /// Database provider identifier. - /// True when the register supports the provider. - bool IsSuit4Provider(string provider); + /// Database provider identifier. + /// True when the register supports the provider. + bool IsSuit4Provider(string provider); - /// - /// Register storage services that do not depend on an environment. - /// - /// Service collection to populate. - void AddFixedRepositories(IServiceCollection sc); + /// + /// Register storage services that do not depend on an environment. + /// + /// Service collection to populate. + void AddFixedRepositories(IServiceCollection sc); - /// - /// Resolve an environment-specific storage service. - /// - /// Type of repository to resolve. - /// Service provider used for resolution. - /// Environment identifier. - /// Repository instance for the environment. - T GetServiceByEnv(IServiceProvider sp, string env) where T : class; - } -} + /// + /// Resolve an environment-specific storage service. + /// + /// Type of repository to resolve. + /// Service provider used for resolution. + /// Environment identifier. + /// Repository instance for the environment. + T GetServiceByEnv(IServiceProvider sp, string env) where T : class; +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IRoleDefinitionRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IRoleDefinitionRepository.cs index 0c948825..358a2a2d 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IRoleDefinitionRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IRoleDefinitionRepository.cs @@ -1,8 +1,7 @@ using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +public interface IRoleDefinitionRepository : IRepository { - public interface IRoleDefinitionRepository : IRepository - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IRoleFunctionRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IRoleFunctionRepository.cs new file mode 100644 index 00000000..eb20506f --- /dev/null +++ b/src/AgileConfig.Server.Data.Abstraction/IRoleFunctionRepository.cs @@ -0,0 +1,7 @@ +using AgileConfig.Server.Data.Entity; + +namespace AgileConfig.Server.Data.Abstraction; + +public interface IRoleFunctionRepository : IRepository +{ +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IServerNodeRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IServerNodeRepository.cs index f97e4774..dbaacd84 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IServerNodeRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IServerNodeRepository.cs @@ -1,8 +1,7 @@ using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +public interface IServerNodeRepository : IRepository { - public interface IServerNodeRepository : IRepository - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IServiceInfoRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IServiceInfoRepository.cs index de6d42de..c2db7d10 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IServiceInfoRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IServiceInfoRepository.cs @@ -1,8 +1,7 @@ using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +public interface IServiceInfoRepository : IRepository { - public interface IServiceInfoRepository : IRepository - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/ISettingRepository.cs b/src/AgileConfig.Server.Data.Abstraction/ISettingRepository.cs index 0bac356a..1e70c9b0 100644 --- a/src/AgileConfig.Server.Data.Abstraction/ISettingRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/ISettingRepository.cs @@ -1,8 +1,7 @@ using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +public interface ISettingRepository : IRepository { - public interface ISettingRepository : IRepository - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/ISysInitRepository.cs b/src/AgileConfig.Server.Data.Abstraction/ISysInitRepository.cs index da72e92a..28547009 100644 --- a/src/AgileConfig.Server.Data.Abstraction/ISysInitRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/ISysInitRepository.cs @@ -5,25 +5,25 @@ namespace AgileConfig.Server.Data.Abstraction; public interface ISysInitRepository { /// - /// get jwt token secret from db + /// get jwt token secret from db /// /// string? GetJwtTokenSecret(); /// - /// get default environment from db + /// get default environment from db /// /// string? GetDefaultEnvironmentFromDb(); /// - /// save initialization setting + /// save initialization setting /// /// Setting values to persist. void SaveInitSetting(Setting setting); /// - /// Init super admin + /// Init super admin /// /// Password to assign to the super administrator. /// diff --git a/src/AgileConfig.Server.Data.Abstraction/ISysLogRepository.cs b/src/AgileConfig.Server.Data.Abstraction/ISysLogRepository.cs index a350d145..d496490b 100644 --- a/src/AgileConfig.Server.Data.Abstraction/ISysLogRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/ISysLogRepository.cs @@ -1,8 +1,7 @@ using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +public interface ISysLogRepository : IRepository { - public interface ISysLogRepository : IRepository - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IUow.cs b/src/AgileConfig.Server.Data.Abstraction/IUow.cs index 00861a67..5157fdfa 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IUow.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IUow.cs @@ -1,18 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace AgileConfig.Server.Data.Abstraction; -namespace AgileConfig.Server.Data.Abstraction +public interface IUow : IDisposable { - public interface IUow : IDisposable - { - void Begin(); + void Begin(); - Task SaveChangesAsync(); + Task SaveChangesAsync(); - void Rollback(); - - } -} + void Rollback(); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IUserAppAuthRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IUserAppAuthRepository.cs index ea2a3b00..71756141 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IUserAppAuthRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IUserAppAuthRepository.cs @@ -1,8 +1,7 @@ using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +public interface IUserAppAuthRepository : IRepository { - public interface IUserAppAuthRepository : IRepository - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IUserRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IUserRepository.cs index 5d392afc..ec2c9f25 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IUserRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IUserRepository.cs @@ -1,8 +1,7 @@ using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +public interface IUserRepository : IRepository { - public interface IUserRepository : IRepository - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Abstraction/IUserRoleRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IUserRoleRepository.cs index b635c7a6..3b251e76 100644 --- a/src/AgileConfig.Server.Data.Abstraction/IUserRoleRepository.cs +++ b/src/AgileConfig.Server.Data.Abstraction/IUserRoleRepository.cs @@ -1,8 +1,7 @@ using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Abstraction +namespace AgileConfig.Server.Data.Abstraction; + +public interface IUserRoleRepository : IRepository { - public interface IUserRoleRepository : IRepository - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/App.cs b/src/AgileConfig.Server.Data.Entity/App.cs index eb3e12bd..1310df84 100644 --- a/src/AgileConfig.Server.Data.Entity/App.cs +++ b/src/AgileConfig.Server.Data.Entity/App.cs @@ -1,60 +1,55 @@ -using FreeSql.DataAnnotations; -using System; -using MongoDB.Bson.Serialization.Attributes; +using System; using AgileConfig.Server.Common; +using FreeSql.DataAnnotations; +using MongoDB.Bson.Serialization.Attributes; + +namespace AgileConfig.Server.Data.Entity; + +public interface IAppModel +{ + string Id { get; set; } -namespace AgileConfig.Server.Data.Entity + string Name { get; set; } + + string Group { get; set; } + + DateTime CreateTime { get; set; } +} + +public enum AppType { - - public interface IAppModel - { - string Id { get; set; } - - string Name { get; set; } - - string Group { get; set; } - - DateTime CreateTime { get; set; } - } - - - public enum AppType - { - PRIVATE, - Inheritance, - } - - [Table(Name = "agc_app")] - [OraclePrimaryKeyName("agc_app_pk")] - public class App: IAppModel, IEntity - { - [Column(Name= "id" , StringLength = 36)] - public string Id { get; set; } - - [Column(Name = "name" , StringLength = 50)] - public string Name { get; set; } - - [Column(Name = "group" , StringLength = 50)] - public string Group { get; set; } - - [Column(Name = "secret", StringLength = 36)] - public string Secret { get; set; } - - [Column(Name = "create_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime CreateTime { get; set; } - - [Column(Name = "update_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime? UpdateTime { get; set; } - - [Column(Name = "enabled")] - public bool Enabled { get; set; } - - [Column(Name = "type")] - public AppType Type { get; set; } - - [Column(Name = "app_admin",StringLength = 36)] - public string AppAdmin { get; set; } - } + PRIVATE, + Inheritance } + +[Table(Name = "agc_app")] +[OraclePrimaryKeyName("agc_app_pk")] +public class App : IAppModel, IEntity +{ + [Column(Name = "secret", StringLength = 36)] + public string Secret { get; set; } + + [Column(Name = "update_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime? UpdateTime { get; set; } + + [Column(Name = "enabled")] public bool Enabled { get; set; } + + [Column(Name = "type")] public AppType Type { get; set; } + + [Column(Name = "app_admin", StringLength = 36)] + public string AppAdmin { get; set; } + + [Column(Name = "id", StringLength = 36)] + public string Id { get; set; } + + [Column(Name = "name", StringLength = 50)] + public string Name { get; set; } + + [Column(Name = "group", StringLength = 50)] + public string Group { get; set; } + + [Column(Name = "create_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/AppInheritanced.cs b/src/AgileConfig.Server.Data.Entity/AppInheritanced.cs index 269b7f3b..56700247 100644 --- a/src/AgileConfig.Server.Data.Entity/AppInheritanced.cs +++ b/src/AgileConfig.Server.Data.Entity/AppInheritanced.cs @@ -1,26 +1,23 @@ using AgileConfig.Server.Common; using FreeSql.DataAnnotations; -using System; -using System.Collections.Generic; -using System.Text; -namespace AgileConfig.Server.Data.Entity +namespace AgileConfig.Server.Data.Entity; + +/// +/// Represents the inheritance relationship between applications. +/// +[Table(Name = "agc_appInheritanced")] +[OraclePrimaryKeyName("agc_appInheritanced_pk")] +public class AppInheritanced : IEntity { - /// - /// Represents the inheritance relationship between applications. - /// - [Table(Name = "agc_appInheritanced")] - [OraclePrimaryKeyName("agc_appInheritanced_pk")] - public class AppInheritanced : IEntity - { - [Column(Name = "id", StringLength = 36)] - public string Id { get; set; } - [Column(Name = "appid", StringLength = 36)] - public string AppId { get; set; } - [Column(Name = "inheritanced_appid", StringLength = 36)] - public string InheritancedAppId { get; set; } - [Column(Name = "sort")] - public int Sort { get; set; } + [Column(Name = "appid", StringLength = 36)] + public string AppId { get; set; } + + [Column(Name = "inheritanced_appid", StringLength = 36)] + public string InheritancedAppId { get; set; } + + [Column(Name = "sort")] public int Sort { get; set; } - } -} + [Column(Name = "id", StringLength = 36)] + public string Id { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/Config.cs b/src/AgileConfig.Server.Data.Entity/Config.cs index c50a10e1..ac8430b0 100644 --- a/src/AgileConfig.Server.Data.Entity/Config.cs +++ b/src/AgileConfig.Server.Data.Entity/Config.cs @@ -1,83 +1,79 @@ -using FreeSql.DataAnnotations; -using System; -using MongoDB.Bson.Serialization.Attributes; +using System; using AgileConfig.Server.Common; +using FreeSql.DataAnnotations; +using MongoDB.Bson.Serialization.Attributes; + +namespace AgileConfig.Server.Data.Entity; + +/// +/// Deleted = 0, +/// Enabled = 1, +/// +public enum ConfigStatus +{ + Deleted = 0, + Enabled = 1 +} -namespace AgileConfig.Server.Data.Entity +/// +/// Add = 0, +/// Edit = 1, +/// Deleted = 2, +/// Commit = 10 +/// +public enum EditStatus { - /// - /// Deleted = 0, - /// Enabled = 1, - /// - public enum ConfigStatus - { - Deleted = 0, - Enabled = 1, - } - - /// - /// Add = 0, - /// Edit = 1, - /// Deleted = 2, - /// Commit = 10 - /// - public enum EditStatus - { - Add = 0, - Edit = 1, - Deleted = 2, - Commit = 10 - } - - /// - /// WaitPublish = 0, - /// Online = 1, - /// - public enum OnlineStatus - { - WaitPublish = 0, - Online = 1, - } - - [Table(Name = "agc_config")] - [OraclePrimaryKeyName("agc_config_pk")] - public class Config: IEntity - { - [Column(Name = "id", StringLength = 36)] - public string Id { get; set; } - - [Column(Name = "app_id", StringLength = 36)] - public string AppId { get; set; } - - [Column(Name = "g", StringLength = 100)] - public string Group { get; set; } - - [Column(Name = "k", StringLength = 100)] - public string Key { get; set; } - - public string Value { get; set; } - - [Column(Name = "description", StringLength = 200)] - public string Description { get; set; } - - [Column(Name = "create_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime CreateTime { get; set; } - - [Column(Name = "update_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime? UpdateTime { get; set; } - - [Column(Name = "status")] - public ConfigStatus Status { get; set; } - - [Column(Name = "online_status")] - public OnlineStatus OnlineStatus { get; set; } - - [Column(Name = "edit_status")] - public EditStatus EditStatus { get; set; } - - [Column(Name = "env", StringLength = 50)] - public string Env { get; set; } - } + Add = 0, + Edit = 1, + Deleted = 2, + Commit = 10 } + +/// +/// WaitPublish = 0, +/// Online = 1, +/// +public enum OnlineStatus +{ + WaitPublish = 0, + Online = 1 +} + +[Table(Name = "agc_config")] +[OraclePrimaryKeyName("agc_config_pk")] +public class Config : IEntity +{ + [Column(Name = "app_id", StringLength = 36)] + public string AppId { get; set; } + + [Column(Name = "g", StringLength = 100)] + public string Group { get; set; } + + [Column(Name = "k", StringLength = 100)] + public string Key { get; set; } + + public string Value { get; set; } + + [Column(Name = "description", StringLength = 200)] + public string Description { get; set; } + + [Column(Name = "create_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + + [Column(Name = "update_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime? UpdateTime { get; set; } + + [Column(Name = "status")] public ConfigStatus Status { get; set; } + + [Column(Name = "online_status")] public OnlineStatus OnlineStatus { get; set; } + + [Column(Name = "edit_status")] public EditStatus EditStatus { get; set; } + + [Column(Name = "env", StringLength = 50)] + public string Env { get; set; } + + [Column(Name = "id", StringLength = 36)] + public string Id { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/ConfigPublished.cs b/src/AgileConfig.Server.Data.Entity/ConfigPublished.cs index 82e792db..512fb44d 100644 --- a/src/AgileConfig.Server.Data.Entity/ConfigPublished.cs +++ b/src/AgileConfig.Server.Data.Entity/ConfigPublished.cs @@ -1,60 +1,56 @@ -using FreeSql.DataAnnotations; -using System; -using MongoDB.Bson.Serialization.Attributes; +using System; using AgileConfig.Server.Common; +using FreeSql.DataAnnotations; +using MongoDB.Bson.Serialization.Attributes; -namespace AgileConfig.Server.Data.Entity -{ - [Table(Name = "agc_config_published")] - [OraclePrimaryKeyName("agc_config_published_pk")] - public class ConfigPublished : IEntity - { - [Column(Name = "id", StringLength = 36)] - public string Id { get; set; } +namespace AgileConfig.Server.Data.Entity; - [Column(Name = "app_id", StringLength = 36)] - public string AppId { get; set; } +[Table(Name = "agc_config_published")] +[OraclePrimaryKeyName("agc_config_published_pk")] +public class ConfigPublished : IEntity +{ + [Column(Name = "app_id", StringLength = 36)] + public string AppId { get; set; } - [Column(Name = "g", StringLength = 100)] - public string Group { get; set; } + [Column(Name = "g", StringLength = 100)] + public string Group { get; set; } - [Column(Name = "k", StringLength = 100)] - public string Key { get; set; } + [Column(Name = "k", StringLength = 100)] + public string Key { get; set; } - public string Value { get; set; } + public string Value { get; set; } - [Column(Name = "publish_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime? PublishTime { get; set; } + [Column(Name = "publish_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime? PublishTime { get; set; } - [Column(Name = "config_id", StringLength = 36)] - public string ConfigId { get; set; } + [Column(Name = "config_id", StringLength = 36)] + public string ConfigId { get; set; } - [Column(Name = "publish_timeline_id", StringLength = 36)] - public string PublishTimelineId { get; set; } + [Column(Name = "publish_timeline_id", StringLength = 36)] + public string PublishTimelineId { get; set; } - [Column(Name = "version")] - public int Version { get; set; } + [Column(Name = "version")] public int Version { get; set; } - [Column(Name = "status")] - public ConfigStatus Status { get; set; } + [Column(Name = "status")] public ConfigStatus Status { get; set; } - [Column(Name = "env", StringLength = 50)] - public string Env { get; set; } + [Column(Name = "env", StringLength = 50)] + public string Env { get; set; } - } + [Column(Name = "id", StringLength = 36)] + public string Id { get; set; } +} - public static class ConfigPublishedExt +public static class ConfigPublishedExt +{ + public static Config Convert(this ConfigPublished configPublished) { - public static Config Convert(this ConfigPublished configPublished) + return new Config { - return new Config - { - Id = configPublished.ConfigId, - Group = configPublished.Group, - Key = configPublished.Key, - Value = configPublished.Value - }; - } + Id = configPublished.ConfigId, + Group = configPublished.Group, + Key = configPublished.Key, + Value = configPublished.Value + }; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/Function.cs b/src/AgileConfig.Server.Data.Entity/Function.cs new file mode 100644 index 00000000..e1865a54 --- /dev/null +++ b/src/AgileConfig.Server.Data.Entity/Function.cs @@ -0,0 +1,36 @@ +using System; +using AgileConfig.Server.Common; +using FreeSql.DataAnnotations; +using MongoDB.Bson.Serialization.Attributes; + +namespace AgileConfig.Server.Data.Entity; + +[Table(Name = "agc_function")] +[OraclePrimaryKeyName("agc_function_pk")] +public class Function : IEntity +{ + [Column(Name = "code", StringLength = 64)] + public string Code { get; set; } + + [Column(Name = "name", StringLength = 128)] + public string Name { get; set; } + + [Column(Name = "description", StringLength = 512)] + public string Description { get; set; } + + [Column(Name = "category", StringLength = 64)] + public string Category { get; set; } + + [Column(Name = "sort_index")] public int SortIndex { get; set; } + + [Column(Name = "create_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + + [Column(Name = "update_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime? UpdateTime { get; set; } + + [Column(Name = "id", StringLength = 64)] + public string Id { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/PublishDetail.cs b/src/AgileConfig.Server.Data.Entity/PublishDetail.cs index 849dc3db..0fff6b0c 100644 --- a/src/AgileConfig.Server.Data.Entity/PublishDetail.cs +++ b/src/AgileConfig.Server.Data.Entity/PublishDetail.cs @@ -1,42 +1,39 @@ using AgileConfig.Server.Common; using FreeSql.DataAnnotations; -using System; -namespace AgileConfig.Server.Data.Entity +namespace AgileConfig.Server.Data.Entity; + +[Table(Name = "agc_publish_detail")] +[OraclePrimaryKeyName("agc_publish_detail_pk")] +public class PublishDetail : IEntity { - [Table(Name = "agc_publish_detail")] - [OraclePrimaryKeyName("agc_publish_detail_pk")] - public class PublishDetail: IEntity - { - [Column(Name = "id", StringLength = 36)] - public string Id { get; set; } + [Column(Name = "app_id", StringLength = 36)] + public string AppId { get; set; } - [Column(Name = "app_id", StringLength = 36)] - public string AppId { get; set; } + public int Version { get; set; } - public int Version { get; set; } + [Column(Name = "publish_timeline_id", StringLength = 36)] + public string PublishTimelineId { get; set; } - [Column(Name = "publish_timeline_id", StringLength = 36)] - public string PublishTimelineId { get; set; } + [Column(Name = "config_id", StringLength = 36)] + public string ConfigId { get; set; } - [Column(Name = "config_id", StringLength = 36)] - public string ConfigId { get; set; } + [Column(Name = "g", StringLength = 100)] + public string Group { get; set; } - [Column(Name = "g", StringLength = 100)] - public string Group { get; set; } + [Column(Name = "k", StringLength = 100)] + public string Key { get; set; } - [Column(Name = "k", StringLength = 100)] - public string Key { get; set; } + public string Value { get; set; } - public string Value { get; set; } + [Column(Name = "description", StringLength = 200)] + public string Description { get; set; } - [Column(Name = "description", StringLength = 200)] - public string Description { get; set; } + [Column(Name = "edit_status")] public EditStatus EditStatus { get; set; } - [Column(Name = "edit_status")] - public EditStatus EditStatus { get; set; } + [Column(Name = "env", StringLength = 50)] + public string Env { get; set; } - [Column(Name = "env", StringLength = 50)] - public string Env { get; set; } - } -} + [Column(Name = "id", StringLength = 36)] + public string Id { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/PublishTimeline.cs b/src/AgileConfig.Server.Data.Entity/PublishTimeline.cs index 59a7d0d4..f4a9ba25 100644 --- a/src/AgileConfig.Server.Data.Entity/PublishTimeline.cs +++ b/src/AgileConfig.Server.Data.Entity/PublishTimeline.cs @@ -1,37 +1,35 @@ -using FreeSql.DataAnnotations; -using System; -using MongoDB.Bson.Serialization.Attributes; +using System; using AgileConfig.Server.Common; +using FreeSql.DataAnnotations; +using MongoDB.Bson.Serialization.Attributes; + +namespace AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Entity +[Table(Name = "agc_publish_timeline")] +[OraclePrimaryKeyName("agc_publish_timeline_pk")] +public class PublishTimeline : IEntity { - [Table(Name = "agc_publish_timeline")] - [OraclePrimaryKeyName("agc_publish_timeline_pk")] - public class PublishTimeline: IEntity - { - [Column(Name = "id", StringLength = 36)] - public string Id { get; set; } + [Column(Name = "app_id", StringLength = 36)] + public string AppId { get; set; } - [Column(Name = "app_id", StringLength = 36)] - public string AppId { get; set; } + [Column(Name = "publish_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime? PublishTime { get; set; } - [Column(Name = "publish_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime? PublishTime { get; set; } + [Column(Name = "publish_user_id", StringLength = 36)] + public string PublishUserId { get; set; } - [Column(Name = "publish_user_id", StringLength = 36)] - public string PublishUserId { get; set; } + [Column(Name = "publish_user_name", StringLength = 50)] + public string PublishUserName { get; set; } - [Column(Name = "publish_user_name", StringLength = 50)] - public string PublishUserName { get; set; } + [Column(Name = "version")] public int Version { get; set; } - [Column(Name = "version")] - public int Version { get; set; } + [Column(Name = "log", StringLength = 100)] + public string Log { get; set; } - [Column(Name = "log", StringLength = 100)] - public string Log { get; set; } + [Column(Name = "env", StringLength = 50)] + public string Env { get; set; } - [Column(Name = "env", StringLength = 50)] - public string Env { get; set; } - } -} + [Column(Name = "id", StringLength = 36)] + public string Id { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/RoleDefinition.cs b/src/AgileConfig.Server.Data.Entity/RoleDefinition.cs index b8b5c5ee..9d46ff30 100644 --- a/src/AgileConfig.Server.Data.Entity/RoleDefinition.cs +++ b/src/AgileConfig.Server.Data.Entity/RoleDefinition.cs @@ -1,38 +1,30 @@ -using FreeSql.DataAnnotations; -using MongoDB.Bson.Serialization.Attributes; using System; using AgileConfig.Server.Common; +using FreeSql.DataAnnotations; +using MongoDB.Bson.Serialization.Attributes; -namespace AgileConfig.Server.Data.Entity -{ - [Table(Name = "agc_role")] - [OraclePrimaryKeyName("agc_role_pk")] - public class Role : IEntity - { - [Column(Name = "id", StringLength = 64)] - public string Id { get; set; } - - [Column(Name = "code", StringLength = 64)] - public string Code { get; set; } +namespace AgileConfig.Server.Data.Entity; - [Column(Name = "name", StringLength = 128)] - public string Name { get; set; } +[Table(Name = "agc_role")] +[OraclePrimaryKeyName("agc_role_pk")] +public class Role : IEntity +{ + [Column(Name = "name", StringLength = 128)] + public string Name { get; set; } - [Column(Name = "description", StringLength = 512)] - public string Description { get; set; } + [Column(Name = "description", StringLength = 512)] + public string Description { get; set; } - [Column(Name = "is_system")] - public bool IsSystem { get; set; } + [Column(Name = "is_system")] public bool IsSystem { get; set; } - [Column(Name = "functions", StringLength = -1)] - public string FunctionsJson { get; set; } + [Column(Name = "create_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } - [Column(Name = "create_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime CreateTime { get; set; } + [Column(Name = "update_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime? UpdateTime { get; set; } - [Column(Name = "update_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime? UpdateTime { get; set; } - } -} + [Column(Name = "id", StringLength = 64)] + public string Id { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/RoleFunction.cs b/src/AgileConfig.Server.Data.Entity/RoleFunction.cs new file mode 100644 index 00000000..a41ffa72 --- /dev/null +++ b/src/AgileConfig.Server.Data.Entity/RoleFunction.cs @@ -0,0 +1,24 @@ +using System; +using AgileConfig.Server.Common; +using FreeSql.DataAnnotations; +using MongoDB.Bson.Serialization.Attributes; + +namespace AgileConfig.Server.Data.Entity; + +[Table(Name = "agc_role_function")] +[OraclePrimaryKeyName("agc_role_function_pk")] +public class RoleFunction : IEntity +{ + [Column(Name = "role_id", StringLength = 64)] + public string RoleId { get; set; } + + [Column(Name = "function_id", StringLength = 64)] + public string FunctionId { get; set; } + + [Column(Name = "create_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + + [Column(Name = "id", StringLength = 36)] + public string Id { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/ServerNode.cs b/src/AgileConfig.Server.Data.Entity/ServerNode.cs index 4c03aba0..b8af762f 100644 --- a/src/AgileConfig.Server.Data.Entity/ServerNode.cs +++ b/src/AgileConfig.Server.Data.Entity/ServerNode.cs @@ -1,42 +1,38 @@ -using FreeSql.DataAnnotations; -using System; -using System.Collections.Generic; -using System.Text; -using MongoDB.Bson.Serialization.Attributes; +using System; using AgileConfig.Server.Common; +using FreeSql.DataAnnotations; +using MongoDB.Bson.Serialization.Attributes; + +namespace AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Entity +/// +/// Online = 1, +/// Offline = 0, +/// +public enum NodeStatus { - /// - /// Online = 1, - /// Offline = 0, - /// - public enum NodeStatus - { - Online = 1, - Offline = 0, - } + Online = 1, + Offline = 0 +} - [Table(Name = "agc_server_node")] - [OraclePrimaryKeyName("agc_server_node_pk")] - public class ServerNode: IEntity - { - [BsonId] - [Column(Name = "address", StringLength = 100, IsPrimary = true)] - public string Id { get; set; } +[Table(Name = "agc_server_node")] +[OraclePrimaryKeyName("agc_server_node_pk")] +public class ServerNode : IEntity +{ + [Column(Name = "remark", StringLength = 50)] + public string Remark { get; set; } - [Column(Name = "remark", StringLength = 50)] - public string Remark { get; set; } + [Column(Name = "status")] public NodeStatus Status { get; set; } - [Column(Name = "status")] - public NodeStatus Status { get; set; } + [Column(Name = "last_echo_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime? LastEchoTime { get; set; } - [Column(Name = "last_echo_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime? LastEchoTime { get; set; } + [Column(Name = "create_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } - [Column(Name = "create_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime CreateTime { get; set; } - } -} + [BsonId] + [Column(Name = "address", StringLength = 100, IsPrimary = true)] + public string Id { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/ServiceInfo.cs b/src/AgileConfig.Server.Data.Entity/ServiceInfo.cs index 4c1043fb..e0b47c30 100644 --- a/src/AgileConfig.Server.Data.Entity/ServiceInfo.cs +++ b/src/AgileConfig.Server.Data.Entity/ServiceInfo.cs @@ -1,74 +1,68 @@ -using FreeSql.DataAnnotations; -using System; -using System.Collections.Generic; -using System.Text; -using MongoDB.Bson.Serialization.Attributes; +using System; using AgileConfig.Server.Common; +using FreeSql.DataAnnotations; +using MongoDB.Bson.Serialization.Attributes; + +namespace AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Entity +public enum ServiceStatus { - public enum ServiceStatus - { - Unhealthy = 0, - Healthy = 1 - } - - public enum RegisterWay - { - Auto = 0, - Manual = 1 - } - - public enum HeartBeatModes - { - client, - server, - none - } - - [Table(Name = "agc_service_info")] - [OraclePrimaryKeyName("agc_serviceinfo_pk")] - public class ServiceInfo : IEntity - { - [Column(Name = "id", StringLength = 36)] - public string Id { get; set; } - - [Column(Name = "service_id", StringLength = 100)] - public string ServiceId { get; set; } - - [Column(Name = "service_name", StringLength = 100)] - public string ServiceName { get; set; } - - [Column(Name = "ip", StringLength = 100)] - public string Ip { get; set; } - - [Column(Name = "port")] - public int? Port { get; set; } - - [Column(Name = "meta_data", StringLength = 2000)] - public string MetaData { get; set; } - - [Column(Name = "status")] - public ServiceStatus Status { get; set; } - - [Column(Name = "register_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime? RegisterTime { get; set; } - - [Column(Name = "last_heart_beat")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime? LastHeartBeat { get; set; } - - [Column(Name = "heart_beat_mode",StringLength = 10)] - public string HeartBeatMode { get; set; } - - [Column(Name = "check_url", StringLength = 2000)] - public string CheckUrl { get; set; } - - [Column(Name = "alarm_url", StringLength = 2000)] - public string AlarmUrl { get; set; } - - [Column(Name = "register_way")] - public RegisterWay? RegisterWay { get; set; } - } + Unhealthy = 0, + Healthy = 1 +} + +public enum RegisterWay +{ + Auto = 0, + Manual = 1 +} + +public enum HeartBeatModes +{ + client, + server, + none +} + +[Table(Name = "agc_service_info")] +[OraclePrimaryKeyName("agc_serviceinfo_pk")] +public class ServiceInfo : IEntity +{ + [Column(Name = "service_id", StringLength = 100)] + public string ServiceId { get; set; } + + [Column(Name = "service_name", StringLength = 100)] + public string ServiceName { get; set; } + + [Column(Name = "ip", StringLength = 100)] + public string Ip { get; set; } + + [Column(Name = "port")] public int? Port { get; set; } + + [Column(Name = "meta_data", StringLength = 2000)] + public string MetaData { get; set; } + + [Column(Name = "status")] public ServiceStatus Status { get; set; } + + [Column(Name = "register_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime? RegisterTime { get; set; } + + [Column(Name = "last_heart_beat")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime? LastHeartBeat { get; set; } + + [Column(Name = "heart_beat_mode", StringLength = 10)] + public string HeartBeatMode { get; set; } + + [Column(Name = "check_url", StringLength = 2000)] + public string CheckUrl { get; set; } + + [Column(Name = "alarm_url", StringLength = 2000)] + public string AlarmUrl { get; set; } + + [Column(Name = "register_way")] public RegisterWay? RegisterWay { get; set; } + + [Column(Name = "id", StringLength = 36)] + public string Id { get; set; } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/Setting.cs b/src/AgileConfig.Server.Data.Entity/Setting.cs index 9d4f6fa4..3abcf083 100644 --- a/src/AgileConfig.Server.Data.Entity/Setting.cs +++ b/src/AgileConfig.Server.Data.Entity/Setting.cs @@ -1,22 +1,21 @@ -using FreeSql.DataAnnotations; -using System; -using MongoDB.Bson.Serialization.Attributes; +using System; using AgileConfig.Server.Common; +using FreeSql.DataAnnotations; +using MongoDB.Bson.Serialization.Attributes; + +namespace AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Entity +[Table(Name = "agc_setting")] +[OraclePrimaryKeyName("agc_setting_pk")] +public class Setting : IEntity { - [Table(Name = "agc_setting")] - [OraclePrimaryKeyName("agc_setting_pk")] - public class Setting: IEntity - { - [Column(Name = "id", StringLength = 36)] - public string Id { get; set; } + [Column(Name = "value", StringLength = 200)] + public string Value { get; set; } - [Column(Name = "value", StringLength = 200)] - public string Value { get; set; } + [Column(Name = "create_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } - [Column(Name = "create_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime CreateTime { get; set; } - } -} + [Column(Name = "id", StringLength = 36)] + public string Id { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/SysLog.cs b/src/AgileConfig.Server.Data.Entity/SysLog.cs index 93a7d8f1..3659784d 100644 --- a/src/AgileConfig.Server.Data.Entity/SysLog.cs +++ b/src/AgileConfig.Server.Data.Entity/SysLog.cs @@ -1,44 +1,42 @@ -using FreeSql.DataAnnotations; -using System; -using MongoDB.Bson.Serialization.Attributes; +using System; using AgileConfig.Server.Common; +using FreeSql.DataAnnotations; +using MongoDB.Bson.Serialization.Attributes; + +namespace AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Data.Entity +public enum SysLogType { - public enum SysLogType - { - Normal = 0, - Warn = 1 - } + Normal = 0, + Warn = 1 +} - [Table(Name = "agc_sys_log")] - [OraclePrimaryKeyName("agc_sys_log_pk")] - public class SysLog: IEntity +[Table(Name = "agc_sys_log")] +[OraclePrimaryKeyName("agc_sys_log_pk")] +public class SysLog : IEntity +{ + public SysLog() { - public SysLog() - { - Id = Guid.NewGuid().ToString("N"); - } + Id = Guid.NewGuid().ToString("N"); + } - [Column(Name = "id", StringLength = 36)] - public string Id { get; set; } + [Column(Name = "app_id", StringLength = 36)] + public string AppId { get; set; } = ""; - [Column(Name = "app_id", StringLength = 36)] - public string AppId { get; set; } = ""; + [Column(Name = "log_type")] public SysLogType LogType { get; set; } - [Column(Name = "log_type")] - public SysLogType LogType { get; set; } + [Column(Name = "log_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime? LogTime { get; set; } - [Column(Name = "log_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime? LogTime { get; set; } + [Column(Name = "log_text", StringLength = 2000)] + public string LogText { get; set; } - [Column(Name = "log_text", StringLength = 2000)] - public string LogText { get; set; } + [Column(Name = "id", StringLength = 36)] + public string Id { get; set; } - public override string ToString() - { - return $"Id:{Id}, AppId:{AppId}, LogType:{LogType}, LogTime:{LogTime}, LogText:{LogText}"; - } + public override string ToString() + { + return $"Id:{Id}, AppId:{AppId}, LogType:{LogType}, LogTime:{LogTime}, LogText:{LogText}"; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/User.cs b/src/AgileConfig.Server.Data.Entity/User.cs index 186fbaa2..261ef9e7 100644 --- a/src/AgileConfig.Server.Data.Entity/User.cs +++ b/src/AgileConfig.Server.Data.Entity/User.cs @@ -1,53 +1,50 @@ -using FreeSql.DataAnnotations; -using System; -using MongoDB.Bson.Serialization.Attributes; +using System; using AgileConfig.Server.Common; +using FreeSql.DataAnnotations; +using MongoDB.Bson.Serialization.Attributes; + +namespace AgileConfig.Server.Data.Entity; + +[Table(Name = "agc_user")] +[OraclePrimaryKeyName("agc_user_pk")] +public class User : IEntity +{ + [Column(Name = "user_name", StringLength = 256)] + public string UserName { get; set; } + + [Column(Name = "password", StringLength = 50)] + public string Password { get; set; } + + [Column(Name = "salt", StringLength = 36)] + public string Salt { get; set; } + + [Column(Name = "team", StringLength = 50)] + public string Team { get; set; } -namespace AgileConfig.Server.Data.Entity + [Column(Name = "create_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + + [Column(Name = "update_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime? UpdateTime { get; set; } + + [Column(Name = "status")] public UserStatus Status { get; set; } + + [Column(Name = "source")] public UserSource Source { get; set; } + + [Column(Name = "id", StringLength = 256)] + public string Id { get; set; } +} + +public enum UserStatus { - [Table(Name = "agc_user")] - [OraclePrimaryKeyName("agc_user_pk")] - public class User: IEntity - { - [Column(Name = "id", StringLength = 256)] - public string Id { get; set; } - - [Column(Name= "user_name" , StringLength = 256)] - public string UserName { get; set; } - - [Column(Name = "password", StringLength = 50)] - public string Password { get; set; } - - [Column(Name = "salt", StringLength = 36)] - public string Salt { get; set; } - - [Column(Name = "team", StringLength = 50)] - public string Team { get; set; } - - [Column(Name = "create_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime CreateTime { get; set; } - - [Column(Name = "update_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime? UpdateTime { get; set; } - - [Column(Name = "status")] - public UserStatus Status { get; set; } - - [Column(Name = "source")] - public UserSource Source { get; set; } - } - - public enum UserStatus - { - Normal = 0, - Deleted = -1 - } - - public enum UserSource - { - Normal = 0, - SSO = 1 - } + Normal = 0, + Deleted = -1 } + +public enum UserSource +{ + Normal = 0, + SSO = 1 +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/UserAppAuth.cs b/src/AgileConfig.Server.Data.Entity/UserAppAuth.cs index a07c9ab8..1fa701f1 100644 --- a/src/AgileConfig.Server.Data.Entity/UserAppAuth.cs +++ b/src/AgileConfig.Server.Data.Entity/UserAppAuth.cs @@ -1,25 +1,21 @@ using AgileConfig.Server.Common; using FreeSql.DataAnnotations; -using System; -using System.Collections.Generic; -using System.Text; -namespace AgileConfig.Server.Data.Entity +namespace AgileConfig.Server.Data.Entity; + +[Table(Name = "agc_user_app_auth")] +[OraclePrimaryKeyName("agc_user_app_auth_pk")] +public class UserAppAuth : IEntity { - [Table(Name = "agc_user_app_auth")] - [OraclePrimaryKeyName("agc_user_app_auth_pk")] - public class UserAppAuth: IEntity - { - [Column(Name = "id", StringLength = 36)] - public string Id { get; set; } + [Column(Name = "app_id", StringLength = 36)] + public string AppId { get; set; } - [Column(Name = "app_id", StringLength = 36)] - public string AppId { get; set; } + [Column(Name = "user_id", StringLength = 36)] + public string UserId { get; set; } - [Column(Name = "user_id", StringLength = 36)] - public string UserId { get; set; } + [Column(Name = "permission", StringLength = 50)] + public string Permission { get; set; } - [Column(Name = "permission", StringLength = 50)] - public string Permission { get; set; } - } -} + [Column(Name = "id", StringLength = 36)] + public string Id { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/UserRole.cs b/src/AgileConfig.Server.Data.Entity/UserRole.cs index 018860c8..4690d5f8 100644 --- a/src/AgileConfig.Server.Data.Entity/UserRole.cs +++ b/src/AgileConfig.Server.Data.Entity/UserRole.cs @@ -1,28 +1,24 @@ -using FreeSql.DataAnnotations; -using System; +using System; using AgileConfig.Server.Common; +using FreeSql.DataAnnotations; using MongoDB.Bson.Serialization.Attributes; -namespace AgileConfig.Server.Data.Entity -{ - [Table(Name = "agc_user_role")] - [OraclePrimaryKeyName("agc_user_role_pk")] - public class UserRole : IEntity - { - [Column(Name = "id", StringLength = 36)] - public string Id { get; set; } +namespace AgileConfig.Server.Data.Entity; - [Column(Name = "user_id", StringLength = 50)] - public string UserId { get; set; } +[Table(Name = "agc_user_role")] +[OraclePrimaryKeyName("agc_user_role_pk")] +public class UserRole : IEntity +{ + [Column(Name = "user_id", StringLength = 50)] + public string UserId { get; set; } - [Column(Name = "role_id", StringLength = 64)] - public string RoleId { get; set; } + [Column(Name = "role_id", StringLength = 64)] + public string RoleId { get; set; } - [Column(Name = "role")] - public int? LegacyRoleValue { get; set; } + [Column(Name = "create_time")] + [BsonDateTimeOptions(Kind = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } - [Column(Name = "create_time")] - [BsonDateTimeOptions(Kind = DateTimeKind.Local)] - public DateTime CreateTime { get; set; } - } -} + [Column(Name = "id", StringLength = 36)] + public string Id { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs b/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs index e198e59a..33407fc2 100644 --- a/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs +++ b/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs @@ -1,94 +1,95 @@ -using AgileConfig.Server.Common; +using System; +using AgileConfig.Server.Common; using AgileConfig.Server.Data.Entity; +using FreeSql; using Microsoft.Extensions.Logging; -using System; -namespace AgileConfig.Server.Data.Freesql +namespace AgileConfig.Server.Data.Freesql; + +public class EnsureTables { - public class EnsureTables - { - private const string Sqlite_ExistTableSql = - "SELECT count(1) FROM sqlite_master WHERE type='table' AND (name = 'agc_app' OR name = 'AGC_APP')"; + private const string Sqlite_ExistTableSql = + "SELECT count(1) FROM sqlite_master WHERE type='table' AND (name = 'agc_app' OR name = 'AGC_APP')"; - private const string Mysql_ExistTableSql = - " SELECT count(1) FROM information_schema.TABLES WHERE table_schema= @schema AND (table_name ='agc_app' OR table_name='AGC_APP')"; + private const string Mysql_ExistTableSql = + " SELECT count(1) FROM information_schema.TABLES WHERE table_schema= @schema AND (table_name ='agc_app' OR table_name='AGC_APP')"; - private const string SqlServer_ExistTableSql = - "SELECT COUNT(1) FROM dbo.sysobjects WHERE ID = object_id(N'[dbo].[agc_app]') and OBJECTPROPERTY(id, N'IsUserTable') = 1"; + private const string SqlServer_ExistTableSql = + "SELECT COUNT(1) FROM dbo.sysobjects WHERE ID = object_id(N'[dbo].[agc_app]') and OBJECTPROPERTY(id, N'IsUserTable') = 1"; - private const string Oracle_ExistTableSql = "select count(1) from user_tables where table_name = 'agc_app' or table_name = 'AGC_APP'"; + private const string Oracle_ExistTableSql = + "select count(1) from user_tables where table_name = 'agc_app' or table_name = 'AGC_APP'"; - private const string PostgreSql_ExistTableSql = "select count(1) from pg_class where relname = 'agc_app' or relname = 'AGC_APP'"; + private const string PostgreSql_ExistTableSql = + "select count(1) from pg_class where relname = 'agc_app' or relname = 'AGC_APP'"; - public static bool ExistTable(IFreeSql instance) + public static bool ExistTable(IFreeSql instance) + { + //sqlite exist table? + var sql = ""; + var schema = ""; + switch (instance.Ado.DataType) { - //sqlite exist table? - string sql = ""; - string schema = ""; - switch (instance.Ado.DataType) - { - case FreeSql.DataType.Sqlite: - sql = Sqlite_ExistTableSql; - break; - case FreeSql.DataType.MySql: - sql = Mysql_ExistTableSql; - schema = instance.Ado.MasterPool.Get().Value.Database; - break; - case FreeSql.DataType.SqlServer: - sql = SqlServer_ExistTableSql; - break; - case FreeSql.DataType.Oracle: - sql = Oracle_ExistTableSql; - break; - case FreeSql.DataType.PostgreSQL: - sql = PostgreSql_ExistTableSql; - break; - default: - sql = Sqlite_ExistTableSql; - break; - } + case DataType.Sqlite: + sql = Sqlite_ExistTableSql; + break; + case DataType.MySql: + sql = Mysql_ExistTableSql; + schema = instance.Ado.MasterPool.Get().Value.Database; + break; + case DataType.SqlServer: + sql = SqlServer_ExistTableSql; + break; + case DataType.Oracle: + sql = Oracle_ExistTableSql; + break; + case DataType.PostgreSQL: + sql = PostgreSql_ExistTableSql; + break; + default: + sql = Sqlite_ExistTableSql; + break; + } - dynamic count = instance.Ado.ExecuteScalar(sql, new { schema }); + dynamic count = instance.Ado.ExecuteScalar(sql, new { schema }); - return count > 0; - } + return count > 0; + } - /// - /// Create tables if they do not already exist. - /// - /// FreeSql instance used to inspect schema state and create tables. - public static void Ensure(IFreeSql instance) + /// + /// Create tables if they do not already exist. + /// + /// FreeSql instance used to inspect schema state and create tables. + public static void Ensure(IFreeSql instance) + { + if (!ExistTable(instance)) { - if (!ExistTable(instance)) - { - if (instance.Ado.DataType == FreeSql.DataType.Oracle) - { - instance.CodeFirst.IsSyncStructureToUpper = true; - } + if (instance.Ado.DataType == DataType.Oracle) instance.CodeFirst.IsSyncStructureToUpper = true; - try - { - instance.CodeFirst.SyncStructure(); - instance.CodeFirst.SyncStructure(); - instance.CodeFirst.SyncStructure(); - instance.CodeFirst.SyncStructure(); - instance.CodeFirst.SyncStructure(); - instance.CodeFirst.SyncStructure(); - instance.CodeFirst.SyncStructure(); - instance.CodeFirst.SyncStructure(); - instance.CodeFirst.SyncStructure(); - instance.CodeFirst.SyncStructure(); - instance.CodeFirst.SyncStructure(); - instance.CodeFirst.SyncStructure(); - instance.CodeFirst.SyncStructure(); - instance.CodeFirst.SyncStructure(); - } - catch (Exception ex) - { - var logger = Global.LoggerFactory.CreateLogger(); - logger.LogError(ex, "Ensure Tables failed ."); - } + try + { + instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); + instance.CodeFirst.SyncStructure(); + } + catch (Exception ex) + { + var logger = Global.LoggerFactory.CreateLogger(); + logger.LogError(ex, "Ensure Tables failed ."); } } } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Freesql/EnvFreeSqlFactory.cs b/src/AgileConfig.Server.Data.Freesql/EnvFreeSqlFactory.cs index c17398a8..9f55a4f5 100644 --- a/src/AgileConfig.Server.Data.Freesql/EnvFreeSqlFactory.cs +++ b/src/AgileConfig.Server.Data.Freesql/EnvFreeSqlFactory.cs @@ -1,19 +1,16 @@ -using AgileConfig.Server.Common; +namespace AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Freesql +public class EnvFreeSqlFactory : IFreeSqlFactory { - public class EnvFreeSqlFactory : IFreeSqlFactory - { - private readonly IMyFreeSQL _freeSQL; + private readonly IMyFreeSQL _freeSQL; - public EnvFreeSqlFactory(IMyFreeSQL freeSQL) - { - this._freeSQL = freeSQL; - } + public EnvFreeSqlFactory(IMyFreeSQL freeSQL) + { + _freeSQL = freeSQL; + } - public IFreeSql Create(string env) - { - return _freeSQL.GetInstanceByEnv(env); - } + public IFreeSql Create(string env) + { + return _freeSQL.GetInstanceByEnv(env); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Freesql/FluentApi.cs b/src/AgileConfig.Server.Data.Freesql/FluentApi.cs index d2d4a5d4..07159d82 100644 --- a/src/AgileConfig.Server.Data.Freesql/FluentApi.cs +++ b/src/AgileConfig.Server.Data.Freesql/FluentApi.cs @@ -1,49 +1,38 @@ using AgileConfig.Server.Data.Entity; -using System; -using System.Collections.Generic; -using System.Text; +using FreeSql; -namespace AgileConfig.Server.Data.Freesql +namespace AgileConfig.Server.Data.Freesql; + +public class FluentApi { - public class FluentApi + public static void Config(IFreeSql freeSql) { - public static void Config(IFreeSql freeSql) + freeSql.CodeFirst.Entity(eb => + { + eb.Property(a => a.Value).HasColumnName("v"); + if (freeSql.Ado.DataType == DataType.Oracle) + // Oracle nvarchar2 does not support length 4000; use -1 to map to CLOB. + eb.Property(a => a.Value).HasMaxLength(-1); + else + eb.Property(a => a.Value).HasMaxLength(4000); + }); + freeSql.CodeFirst.Entity(eb => + { + eb.Property(a => a.Value).HasColumnName("v"); + if (freeSql.Ado.DataType == DataType.Oracle) + // Oracle nvarchar2 does not support length 4000; use -1 to map to CLOB. + eb.Property(a => a.Value).HasMaxLength(-1); + else + eb.Property(a => a.Value).HasMaxLength(4000); + }); + freeSql.CodeFirst.Entity(eb => { - freeSql.CodeFirst.Entity(eb => { - eb.Property(a => a.Value).HasColumnName("v"); - if (freeSql.Ado.DataType == FreeSql.DataType.Oracle) - { - // Oracle nvarchar2 does not support length 4000; use -1 to map to CLOB. - eb.Property(a => a.Value).HasMaxLength(-1); - } else - { - eb.Property(a => a.Value).HasMaxLength(4000); - } - }); - freeSql.CodeFirst.Entity(eb => { - eb.Property(a => a.Value).HasColumnName("v"); - if (freeSql.Ado.DataType == FreeSql.DataType.Oracle) - { - // Oracle nvarchar2 does not support length 4000; use -1 to map to CLOB. - eb.Property(a => a.Value).HasMaxLength(-1); - } - else - { - eb.Property(a => a.Value).HasMaxLength(4000); - } - }); - freeSql.CodeFirst.Entity(eb => { - eb.Property(a => a.Value).HasColumnName("v"); - if (freeSql.Ado.DataType == FreeSql.DataType.Oracle) - { - // Oracle nvarchar2 does not support length 4000; use -1 to map to CLOB. - eb.Property(a => a.Value).HasMaxLength(-1); - } - else - { - eb.Property(a => a.Value).HasMaxLength(4000); - } - }); - } + eb.Property(a => a.Value).HasColumnName("v"); + if (freeSql.Ado.DataType == DataType.Oracle) + // Oracle nvarchar2 does not support length 4000; use -1 to map to CLOB. + eb.Property(a => a.Value).HasMaxLength(-1); + else + eb.Property(a => a.Value).HasMaxLength(4000); + }); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Freesql/FreeSqlContext.cs b/src/AgileConfig.Server.Data.Freesql/FreeSqlContext.cs index 56350c2d..ecc19831 100644 --- a/src/AgileConfig.Server.Data.Freesql/FreeSqlContext.cs +++ b/src/AgileConfig.Server.Data.Freesql/FreeSqlContext.cs @@ -1,46 +1,40 @@ -using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Data.Entity; using FreeSql; -using System; -using System.Collections.Generic; -using System.Text; -namespace AgileConfig.Server.Data.Freesql +namespace AgileConfig.Server.Data.Freesql; + +public class FreeSqlContext : DbContext { - public class FreeSqlContext : DbContext + public FreeSqlContext() { - public FreeSqlContext() - { - } - - public IFreeSql Freesql - { - get; - } + } - public FreeSqlContext(IFreeSql freeSql) : base(freeSql, null) - { - Freesql = freeSql; - } + public FreeSqlContext(IFreeSql freeSql) : base(freeSql, null) + { + Freesql = freeSql; + } - public DbSet Apps { get; set; } + public IFreeSql Freesql { get; } - public DbSet Configs { get; set; } + public DbSet Apps { get; set; } - public DbSet ServerNodes { get; set; } - public DbSet Settings { get; set; } - public DbSet SysLogs { get; set; } + public DbSet Configs { get; set; } - public DbSet AppInheritanceds { get; set; } + public DbSet ServerNodes { get; set; } + public DbSet Settings { get; set; } + public DbSet SysLogs { get; set; } - public DbSet Users { get; set; } - public DbSet UserRoles { get; set; } + public DbSet AppInheritanceds { get; set; } - public DbSet UserAppAuths { get; set; } + public DbSet Users { get; set; } + public DbSet UserRoles { get; set; } - public DbSet ConfigPublished { get; set; } - public DbSet PublishDetail { get; set; } - public DbSet PublishTimeline { get; set; } - public DbSet ServiceInfo { get; set; } + public DbSet UserAppAuths { get; set; } - } -} + public DbSet ConfigPublished { get; set; } + public DbSet PublishDetail { get; set; } + public DbSet PublishTimeline { get; set; } + public DbSet ServiceInfo { get; set; } + public DbSet Functions { get; set; } + public DbSet RoleFunctions { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Freesql/FreeSqlDbContextFactory.cs b/src/AgileConfig.Server.Data.Freesql/FreeSqlDbContextFactory.cs index 8a5f72ba..ba9f9829 100644 --- a/src/AgileConfig.Server.Data.Freesql/FreeSqlDbContextFactory.cs +++ b/src/AgileConfig.Server.Data.Freesql/FreeSqlDbContextFactory.cs @@ -1,11 +1,5 @@ -using AgileConfig.Server.Common; -using System; -using System.Collections.Generic; -using System.Text; +namespace AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Freesql +public class FreeSqlDbContextFactory { - public class FreeSqlDbContextFactory - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Freesql/FreeSqlUow.cs b/src/AgileConfig.Server.Data.Freesql/FreeSqlUow.cs index c270d4c1..a4888c17 100644 --- a/src/AgileConfig.Server.Data.Freesql/FreeSqlUow.cs +++ b/src/AgileConfig.Server.Data.Freesql/FreeSqlUow.cs @@ -1,49 +1,45 @@ -using FreeSql; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AgileConfig.Server.Data.Freesql +using System.Threading.Tasks; +using AgileConfig.Server.Data.Abstraction; +using FreeSql; + +namespace AgileConfig.Server.Data.Freesql; + +public class FreeSqlUow : IUow { - public class FreeSqlUow : Abstraction.IUow + private readonly IFreeSql _freeSql; + private readonly IUnitOfWork _unitOfWork; + + + public FreeSqlUow(IFreeSql freeSql) + { + _freeSql = freeSql; + _unitOfWork = _freeSql.CreateUnitOfWork(); + } + + public Task SaveChangesAsync() + { + _unitOfWork.Commit(); + + return Task.FromResult(true); + } + + public void Rollback() + { + _unitOfWork?.Rollback(); + } + + public void Dispose() + { + _unitOfWork?.Dispose(); + } + + public void Begin() + { + // FreeSql unit of work does not require a manual begin call. + } + + public IUnitOfWork GetFreesqlUnitOfWork() { - private readonly IFreeSql _freeSql; - private IUnitOfWork _unitOfWork; - - - public FreeSqlUow(IFreeSql freeSql) - { - this._freeSql = freeSql; - _unitOfWork = _freeSql.CreateUnitOfWork(); - } - - public IUnitOfWork GetFreesqlUnitOfWork() - { - return _unitOfWork; - } - - public Task SaveChangesAsync() - { - _unitOfWork.Commit(); - - return Task.FromResult(true); - } - - public void Rollback() - { - _unitOfWork?.Rollback(); - } - - public void Dispose() - { - _unitOfWork?.Dispose(); - } - - public void Begin() - { - // FreeSql unit of work does not require a manual begin call. - } + return _unitOfWork; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Freesql/FreesqlRepository.cs b/src/AgileConfig.Server.Data.Freesql/FreesqlRepository.cs index ef43613f..c60c3c88 100644 --- a/src/AgileConfig.Server.Data.Freesql/FreesqlRepository.cs +++ b/src/AgileConfig.Server.Data.Freesql/FreesqlRepository.cs @@ -1,107 +1,105 @@ -using AgileConfig.Server.Common; -using AgileConfig.Server.Data.Abstraction; -using FreeSql; -using System; +using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Threading.Tasks; +using AgileConfig.Server.Common; +using AgileConfig.Server.Data.Abstraction; +using FreeSql; -namespace AgileConfig.Server.Data.Freesql +namespace AgileConfig.Server.Data.Freesql; + +public abstract class FreesqlRepository : IRepository where T : class, IEntity { - public abstract class FreesqlRepository : IRepository where T : class, IEntity + private readonly IFreeSql _freeSql; + private readonly IBaseRepository _repository; + private IUow _uow; + + public FreesqlRepository(IFreeSql freeSql) { - private readonly IBaseRepository _repository; - private readonly IFreeSql _freeSql; - private IUow _uow; - public IUow Uow - { - get - { - return _uow; - } - set - { - _uow = value; - var freesqlUow = value as FreeSqlUow; - _repository.UnitOfWork = freesqlUow.GetFreesqlUnitOfWork(); - } - } + _repository = freeSql.GetRepository(); + _freeSql = freeSql; + } - public FreesqlRepository(IFreeSql freeSql) + public IUow Uow + { + get => _uow; + set { - _repository = freeSql.GetRepository(); - _freeSql = freeSql; + _uow = value; + var freesqlUow = value as FreeSqlUow; + _repository.UnitOfWork = freesqlUow.GetFreesqlUnitOfWork(); } + } - public Task> AllAsync() - { - return _repository.Select.ToListAsync(); - } + public Task> AllAsync() + { + return _repository.Select.ToListAsync(); + } - public async Task DeleteAsync(T1 id) - { - await _repository.DeleteAsync(x => Equals(x.Id, id)); - } + public async Task DeleteAsync(T1 id) + { + await _repository.DeleteAsync(x => Equals(x.Id, id)); + } - public Task DeleteAsync(T entity) - { - return _repository.DeleteAsync(entity); - } + public Task DeleteAsync(T entity) + { + return _repository.DeleteAsync(entity); + } - public Task DeleteAsync(IList entities) - { - return _repository.DeleteAsync(entities); - } + public Task DeleteAsync(IList entities) + { + return _repository.DeleteAsync(entities); + } - public Task GetAsync(T1 id) - { - return _repository.Where(x => x.Id.Equals(id)).ToOneAsync(); - } + public Task GetAsync(T1 id) + { + return _repository.Where(x => x.Id.Equals(id)).ToOneAsync(); + } - public Task InsertAsync(T entity) - { - return _repository.InsertAsync(entity); - } + public Task InsertAsync(T entity) + { + return _repository.InsertAsync(entity); + } - public Task InsertAsync(IList entities) - { - return _repository.InsertAsync(entities); - } + public Task InsertAsync(IList entities) + { + return _repository.InsertAsync(entities); + } - public Task> QueryAsync(Expression> exp) - { - return _repository.Where(exp).ToListAsync(); - } + public Task> QueryAsync(Expression> exp) + { + return _repository.Where(exp).ToListAsync(); + } - public async Task> QueryPageAsync(Expression> exp, int pageIndex, int pageSize, string defaultSortField = "Id", - string defaultSortType = "ASC") - { - var list = await _repository.Where(exp) - .OrderByPropertyName(defaultSortField, defaultSortType.Equals("ASC", StringComparison.OrdinalIgnoreCase)) - .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync(); + public async Task> QueryPageAsync(Expression> exp, int pageIndex, int pageSize, + string defaultSortField = "Id", + string defaultSortType = "ASC") + { + var list = await _repository.Where(exp) + .OrderByPropertyName(defaultSortField, defaultSortType.Equals("ASC", StringComparison.OrdinalIgnoreCase)) + .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync(); - return list; - } + return list; + } - public async Task CountAsync(Expression>? exp = null) - { - return await (exp == null ? _repository.Select.CountAsync() : _repository.Select.Where(exp).CountAsync()); - } + public async Task CountAsync(Expression>? exp = null) + { + return await (exp == null ? _repository.Select.CountAsync() : _repository.Select.Where(exp).CountAsync()); + } - public Task UpdateAsync(T entity) - { - return _repository.UpdateAsync(entity); - } + public Task UpdateAsync(T entity) + { + return _repository.UpdateAsync(entity); + } - public Task UpdateAsync(IList entities) - { - return _repository.UpdateAsync(entities); - } + public Task UpdateAsync(IList entities) + { + return _repository.UpdateAsync(entities); + } - public void Dispose() - { - _repository.Dispose(); - } + public void Dispose() + { + _repository.Dispose(); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Freesql/IFreeSqlFactory.cs b/src/AgileConfig.Server.Data.Freesql/IFreeSqlFactory.cs index 9e618d39..f7b96c65 100644 --- a/src/AgileConfig.Server.Data.Freesql/IFreeSqlFactory.cs +++ b/src/AgileConfig.Server.Data.Freesql/IFreeSqlFactory.cs @@ -1,7 +1,6 @@ -namespace AgileConfig.Server.Data.Freesql +namespace AgileConfig.Server.Data.Freesql; + +public interface IFreeSqlFactory { - public interface IFreeSqlFactory - { - IFreeSql Create(string env = ""); - } + IFreeSql Create(string env = ""); } \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Freesql/IMyFreeSQL.cs b/src/AgileConfig.Server.Data.Freesql/IMyFreeSQL.cs index ec8a3d92..938d703e 100644 --- a/src/AgileConfig.Server.Data.Freesql/IMyFreeSQL.cs +++ b/src/AgileConfig.Server.Data.Freesql/IMyFreeSQL.cs @@ -1,7 +1,6 @@ -namespace AgileConfig.Server.Data.Freesql +namespace AgileConfig.Server.Data.Freesql; + +public interface IMyFreeSQL { - public interface IMyFreeSQL - { - IFreeSql GetInstanceByEnv(string env); - } + IFreeSql GetInstanceByEnv(string env); } \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Freesql/MyFreeSQL.cs b/src/AgileConfig.Server.Data.Freesql/MyFreeSQL.cs index 5d667476..af193371 100644 --- a/src/AgileConfig.Server.Data.Freesql/MyFreeSQL.cs +++ b/src/AgileConfig.Server.Data.Freesql/MyFreeSQL.cs @@ -1,89 +1,80 @@ -using AgileConfig.Server.Data.Abstraction.DbProvider; -using System; +using System; using System.Collections.Generic; +using AgileConfig.Server.Data.Abstraction.DbProvider; +using FreeSql; -namespace AgileConfig.Server.Data.Freesql +namespace AgileConfig.Server.Data.Freesql; + +public class MyFreeSQL : IMyFreeSQL { - public class MyFreeSQL : IMyFreeSQL - { - private Dictionary _envFreesqls = new(); - private static object _lock = new object(); - private readonly IDbConfigInfoFactory _dbConfigInfoFactory; + private static readonly object _lock = new(); + private readonly IDbConfigInfoFactory _dbConfigInfoFactory; + private readonly Dictionary _envFreesqls = new(); - public MyFreeSQL(IDbConfigInfoFactory dbConfigInfoFactory) - { - this._dbConfigInfoFactory = dbConfigInfoFactory; - } + public MyFreeSQL(IDbConfigInfoFactory dbConfigInfoFactory) + { + _dbConfigInfoFactory = dbConfigInfoFactory; + } - /// - /// Return a FreeSql instance according to the connection settings of the specified environment. - /// - /// Environment name whose database connection should be used. - /// FreeSql instance configured for the specified environment. - public IFreeSql GetInstanceByEnv(string env) - { - var dbConfig = _dbConfigInfoFactory.GetConfigInfo(env); + /// + /// Return a FreeSql instance according to the connection settings of the specified environment. + /// + /// Environment name whose database connection should be used. + /// FreeSql instance configured for the specified environment. + public IFreeSql GetInstanceByEnv(string env) + { + var dbConfig = _dbConfigInfoFactory.GetConfigInfo(env); - var dbType = ProviderToFreesqlDbType(dbConfig.Provider); - if (!dbType.HasValue) - { - throw new ArgumentException(nameof(dbConfig.Provider), $"[{dbConfig.Provider}] is not a freesql supported provider."); - } + var dbType = ProviderToFreesqlDbType(dbConfig.Provider); + if (!dbType.HasValue) + throw new ArgumentException(nameof(dbConfig.Provider), + $"[{dbConfig.Provider}] is not a freesql supported provider."); - var key = dbConfig.ConnectionString; + var key = dbConfig.ConnectionString; - if (_envFreesqls.ContainsKey(key)) - { - return _envFreesqls[key]; - } + if (_envFreesqls.ContainsKey(key)) return _envFreesqls[key]; - lock (_lock) - { - if (_envFreesqls.ContainsKey(key)) - { - return _envFreesqls[key]; - } + lock (_lock) + { + if (_envFreesqls.ContainsKey(key)) return _envFreesqls[key]; - var sql = new FreeSql.FreeSqlBuilder() - .UseConnectionString(dbType.Value, dbConfig.ConnectionString) - .Build(); - ApplyDatabaseStructrue(sql); + var sql = new FreeSqlBuilder() + .UseConnectionString(dbType.Value, dbConfig.ConnectionString) + .Build(); + ApplyDatabaseStructrue(sql); - _envFreesqls.Add(key, sql); + _envFreesqls.Add(key, sql); - return sql; - } + return sql; } + } - private static void ApplyDatabaseStructrue(IFreeSql sql) - { - FluentApi.Config(sql); - EnsureTables.Ensure(sql); - } + private static void ApplyDatabaseStructrue(IFreeSql sql) + { + FluentApi.Config(sql); + EnsureTables.Ensure(sql); + } - public static FreeSql.DataType? ProviderToFreesqlDbType(string provider) + public static DataType? ProviderToFreesqlDbType(string provider) + { + switch (provider.ToLower()) { - switch (provider.ToLower()) - { - case "sqlite": - return FreeSql.DataType.Sqlite; - case "mysql": - return FreeSql.DataType.MySql; - case "sqlserver": - return FreeSql.DataType.SqlServer; - case "npgsql": - return FreeSql.DataType.PostgreSQL; - case "postgresql": - return FreeSql.DataType.PostgreSQL; - case "pg": - return FreeSql.DataType.PostgreSQL; - case "oracle": - return FreeSql.DataType.Oracle; - default: - break; - } - - return null; + case "sqlite": + return DataType.Sqlite; + case "mysql": + return DataType.MySql; + case "sqlserver": + return DataType.SqlServer; + case "npgsql": + return DataType.PostgreSQL; + case "postgresql": + return DataType.PostgreSQL; + case "pg": + return DataType.PostgreSQL; + case "oracle": + return DataType.Oracle; } + + return null; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Freesql/ServiceCollectionExt.cs b/src/AgileConfig.Server.Data.Freesql/ServiceCollectionExt.cs index e855682e..46fd3ac7 100644 --- a/src/AgileConfig.Server.Data.Freesql/ServiceCollectionExt.cs +++ b/src/AgileConfig.Server.Data.Freesql/ServiceCollectionExt.cs @@ -1,15 +1,14 @@ using AgileConfig.Server.Data.Abstraction; using Microsoft.Extensions.DependencyInjection; -namespace AgileConfig.Server.Data.Freesql +namespace AgileConfig.Server.Data.Freesql; + +public static class ServiceCollectionExt { - public static class ServiceCollectionExt + public static void AddFreeSqlFactory(this IServiceCollection sc) { - public static void AddFreeSqlFactory(this IServiceCollection sc) - { - sc.AddScoped(); - sc.AddSingleton(); - sc.AddSingleton(); - } + sc.AddScoped(); + sc.AddSingleton(); + sc.AddSingleton(); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Mongodb/MongodbAccess.cs b/src/AgileConfig.Server.Data.Mongodb/MongodbAccess.cs index ce44ec41..7508ab3d 100644 --- a/src/AgileConfig.Server.Data.Mongodb/MongodbAccess.cs +++ b/src/AgileConfig.Server.Data.Mongodb/MongodbAccess.cs @@ -1,15 +1,11 @@ using System.Collections.Concurrent; -using System.Security; using MongoDB.Driver; -using MongoDB.Driver.Linq; namespace AgileConfig.Server.Data.Mongodb; public abstract class MongodbAccess { - private readonly record struct Db(string DatabaseName,IMongoClient Client); - - private static readonly Lazy> LazyMongoClients = new(); + private static readonly Lazy> LazyMongoClients = new(); private readonly string _connectionString; @@ -22,6 +18,7 @@ internal MongodbAccess(string? connectionString) _connectionString = LazyMongoClients.Value.First().Key; return; } + throw new Exception("MongoDB connection string is not configured."); } @@ -29,7 +26,7 @@ internal MongodbAccess(string? connectionString) if (LazyMongoClients is not { IsValueCreated: true } || !LazyMongoClients.Value.ContainsKey(connectionString)) { var url = MongoUrl.Create(connectionString); - + // Use "AgileConfig" as the default database name when it is not specified in the connection string. const string defaultDataBaseName = "AgileConfig"; var databaseName = string.IsNullOrEmpty(url.DatabaseName) ? defaultDataBaseName : url.DatabaseName; @@ -38,31 +35,34 @@ internal MongodbAccess(string? connectionString) } /// - /// Get the MongoDB client instance. + /// Get the MongoDB client instance. /// - internal IMongoClient Client => LazyMongoClients.Value[_connectionString].Client ?? throw new Exception("IMongoClient value is null"); + internal IMongoClient Client => LazyMongoClients.Value[_connectionString].Client ?? + throw new Exception("IMongoClient value is null"); /// - /// Get the MongoDB database instance. + /// Get the MongoDB database instance. /// public IMongoDatabase Database => Client.GetDatabase(LazyMongoClients.Value[_connectionString].DatabaseName); + + private readonly record struct Db(string DatabaseName, IMongoClient Client); } public sealed class MongodbAccess(string? connectionString) : MongodbAccess(connectionString) where T : new() { /// - /// database collection name + /// database collection name /// public string CollectionName => typeof(T).Name; /// - /// Get the MongoDB collection for the entity type. + /// Get the MongoDB collection for the entity type. /// public IMongoCollection Collection => Database.GetCollection(CollectionName); /// - /// Get an IQueryable interface for querying MongoDB data. + /// Get an IQueryable interface for querying MongoDB data. /// /// public IQueryable MongoQueryable => Database.GetCollection(CollectionName).AsQueryable( diff --git a/src/AgileConfig.Server.Data.Mongodb/MongodbRepository.cs b/src/AgileConfig.Server.Data.Mongodb/MongodbRepository.cs index 3749a7a2..addd951e 100644 --- a/src/AgileConfig.Server.Data.Mongodb/MongodbRepository.cs +++ b/src/AgileConfig.Server.Data.Mongodb/MongodbRepository.cs @@ -13,19 +13,6 @@ public class MongodbRepository : IRepository { private readonly MongodbAccess _access; - public IUow Uow - { - get => _mongodbUow; - set - { - _mongodbUow = value as MongodbUow; - if (_mongodbUow?.Session == null && _mongodbUow?.Session?.IsInTransaction != true) - { - _mongodbUow?.SetSession(_access.Client.StartSession()); - } - } - } - private MongodbUow? _mongodbUow; public MongodbRepository(string? connectionString) @@ -40,6 +27,17 @@ public MongodbRepository(IConfiguration configuration) _access = new MongodbAccess(connectionString); } + public IUow Uow + { + get => _mongodbUow; + set + { + _mongodbUow = value as MongodbUow; + if (_mongodbUow?.Session == null && _mongodbUow?.Session?.IsInTransaction != true) + _mongodbUow?.SetSession(_access.Client.StartSession()); + } + } + public void Dispose() { GC.SuppressFinalize(this); @@ -50,12 +48,6 @@ public async Task> AllAsync() return await _access.MongoQueryable.ToListAsync(); } - private Expression> GetIdPropertyFilter(TId id) - { - Expression> expression = x => Equals(x.Id, id); - return expression; - } - public async Task GetAsync(TId id) { var filter = GetIdPropertyFilter(id); @@ -84,13 +76,9 @@ public async Task UpdateAsync(IList entities) if (writes.Count > 0) { if (_mongodbUow?.Session == null) - { await _access.Collection.BulkWriteAsync(writes); - } else - { await _access.Collection.BulkWriteAsync(_mongodbUow.Session, writes); - } } } @@ -98,51 +86,35 @@ public async Task DeleteAsync(TId id) { var filter = Builders.Filter.Eq(x => x.Id, id); if (_mongodbUow?.Session == null) - { await _access.Collection.DeleteOneAsync(filter); - } else - { await _access.Collection.DeleteOneAsync(_mongodbUow.Session, filter); - } } public async Task DeleteAsync(TEntity entity) { var filter = GetIdPropertyFilter(entity.Id); if (_mongodbUow?.Session == null) - { await _access.Collection.DeleteOneAsync(filter); - } else - { await _access.Collection.DeleteOneAsync(_mongodbUow.Session, filter); - } } public async Task DeleteAsync(IList entities) { var filter = Builders.Filter.In(x => x.Id, entities.Select(y => y.Id)); if (_mongodbUow?.Session == null) - { await _access.Collection.DeleteManyAsync(filter); - } else - { await _access.Collection.DeleteManyAsync(_mongodbUow.Session, filter); - } } public async Task InsertAsync(TEntity entity) { if (_mongodbUow?.Session == null) - { await _access.Collection.InsertOneAsync(entity); - } else - { await _access.Collection.InsertOneAsync(_mongodbUow.Session, entity); - } return entity; } @@ -152,13 +124,9 @@ public async Task InsertAsync(IList entities) if (entities.Count > 0) { if (_mongodbUow?.Session == null) - { await _access.Collection.InsertManyAsync(entities); - } else - { await _access.Collection.InsertManyAsync(_mongodbUow.Session, entities); - } } } @@ -183,6 +151,19 @@ public async Task> QueryPageAsync(Expression> return await query.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync(); } + public async Task CountAsync(Expression>? exp = null) + { + return await (exp == null + ? _access.MongoQueryable.CountAsync() + : _access.MongoQueryable.Where(exp).CountAsync()); + } + + private Expression> GetIdPropertyFilter(TId id) + { + Expression> expression = x => Equals(x.Id, id); + return expression; + } + private static Expression> Sort(string defaultSortField) { Expression> defaultSort = x => x.Id; @@ -190,10 +171,7 @@ private static Expression> Sort(string defaultSortField) !defaultSortField.Equals("Id", StringComparison.OrdinalIgnoreCase)) { var property = typeof(TEntity).GetProperty(defaultSortField); - if (property == null) - { - return defaultSort; - } + if (property == null) return defaultSort; var parameter = Expression.Parameter(typeof(TEntity), "__q"); var memberExpress = Expression.Property(parameter, property); @@ -203,11 +181,4 @@ private static Expression> Sort(string defaultSortField) return defaultSort; } - - public async Task CountAsync(Expression>? exp = null) - { - return await (exp == null - ? _access.MongoQueryable.CountAsync() - : _access.MongoQueryable.Where(exp).CountAsync()); - } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Mongodb/MongodbUow.cs b/src/AgileConfig.Server.Data.Mongodb/MongodbUow.cs index d5276232..058d8282 100644 --- a/src/AgileConfig.Server.Data.Mongodb/MongodbUow.cs +++ b/src/AgileConfig.Server.Data.Mongodb/MongodbUow.cs @@ -1,69 +1,65 @@ -using MongoDB.Driver; +using AgileConfig.Server.Data.Abstraction; +using MongoDB.Driver; +using MongoDB.Driver.Core.Clusters; -namespace AgileConfig.Server.Data.Mongodb +namespace AgileConfig.Server.Data.Mongodb; + +/// +/// This is a empty implementation of IUow for mongodb. +/// +public class MongodbUow : IUow { - /// - /// This is a empty implementation of IUow for mongodb. - /// - public class MongodbUow : Abstraction.IUow - { - public IClientSessionHandle? Session { get; private set; } + private bool _disposed; + public IClientSessionHandle? Session { get; private set; } - public async Task SaveChangesAsync() + public async Task SaveChangesAsync() + { + if (Session == null) { - if (Session == null) - { - } - else - { - await Session.CommitTransactionAsync(); - } - return true; } - - public void Rollback() + else { - Session?.AbortTransaction(); + await Session.CommitTransactionAsync(); } - private bool _disposed; - private void Dispose(bool disposing) - { - if (!_disposed) - { - if (disposing) - { - Session?.Dispose(); - } + return true; + } - _disposed = true; - } - } + public void Rollback() + { + Session?.AbortTransaction(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public void Begin() + { + if (Session?.IsInTransaction != true) Session?.StartTransaction(); + } - public void Dispose() + private void Dispose(bool disposing) + { + if (!_disposed) { - Dispose(true); - GC.SuppressFinalize(this); + if (disposing) Session?.Dispose(); + + _disposed = true; } + } - public void Begin() + internal void SetSession(IClientSessionHandle session) + { + if (session.Client.Cluster.Description.Type == ClusterType.Standalone) { - if (Session?.IsInTransaction != true) - { - Session?.StartTransaction(); - } + // standalone mode is not support transaction. } - - internal void SetSession(IClientSessionHandle session) + else { - if (session.Client.Cluster.Description.Type == MongoDB.Driver.Core.Clusters.ClusterType.Standalone) - { - // standalone mode is not support transaction. - } - else - { - Session = session; - } + Session = session; } } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/AppInheritancedRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/AppInheritancedRepository.cs index 2866f81f..467f716d 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/AppInheritancedRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/AppInheritancedRepository.cs @@ -2,15 +2,14 @@ using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Repository.Freesql +namespace AgileConfig.Server.Data.Repository.Freesql; + +public class AppInheritancedRepository : FreesqlRepository, IAppInheritancedRepository { - public class AppInheritancedRepository : FreesqlRepository, IAppInheritancedRepository - { - private readonly IFreeSqlFactory freeSqlFactory; + private readonly IFreeSqlFactory freeSqlFactory; - public AppInheritancedRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) - { - this.freeSqlFactory = freeSqlFactory; - } + public AppInheritancedRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) + { + this.freeSqlFactory = freeSqlFactory; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/AppRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/AppRepository.cs index c63b9e26..bb77b846 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/AppRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/AppRepository.cs @@ -2,15 +2,14 @@ using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Repository.Freesql +namespace AgileConfig.Server.Data.Repository.Freesql; + +public class AppRepository : FreesqlRepository, IAppRepository { - public class AppRepository : FreesqlRepository, IAppRepository - { - private readonly IFreeSqlFactory freeSqlFactory; + private readonly IFreeSqlFactory freeSqlFactory; - public AppRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) - { - this.freeSqlFactory = freeSqlFactory; - } + public AppRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) + { + this.freeSqlFactory = freeSqlFactory; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/ConfigPublishedRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/ConfigPublishedRepository.cs index 5ee7c7c0..581096e3 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/ConfigPublishedRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/ConfigPublishedRepository.cs @@ -2,15 +2,14 @@ using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Repository.Freesql +namespace AgileConfig.Server.Data.Repository.Freesql; + +public class ConfigPublishedRepository : FreesqlRepository, IConfigPublishedRepository { - public class ConfigPublishedRepository : FreesqlRepository, IConfigPublishedRepository - { - private readonly IFreeSql freeSql; + private readonly IFreeSql freeSql; - public ConfigPublishedRepository(IFreeSql freeSql) : base(freeSql) - { - this.freeSql = freeSql; - } + public ConfigPublishedRepository(IFreeSql freeSql) : base(freeSql) + { + this.freeSql = freeSql; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/ConfigRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/ConfigRepository.cs index 9917b27a..fdf5895c 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/ConfigRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/ConfigRepository.cs @@ -2,15 +2,14 @@ using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Repository.Freesql +namespace AgileConfig.Server.Data.Repository.Freesql; + +public class ConfigRepository : FreesqlRepository, IConfigRepository { - public class ConfigRepository : FreesqlRepository, IConfigRepository - { - private readonly IFreeSql freeSql; + private readonly IFreeSql freeSql; - public ConfigRepository(IFreeSql freeSql) : base(freeSql) - { - this.freeSql = freeSql; - } + public ConfigRepository(IFreeSql freeSql) : base(freeSql) + { + this.freeSql = freeSql; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/FreesqlRepositoryServiceRegister.cs b/src/AgileConfig.Server.Data.Repository.Freesql/FreesqlRepositoryServiceRegister.cs index 732489cc..d9bc2f0d 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/FreesqlRepositoryServiceRegister.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/FreesqlRepositoryServiceRegister.cs @@ -1,59 +1,47 @@ -using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Abstraction; using AgileConfig.Server.Data.Freesql; using Microsoft.Extensions.DependencyInjection; -namespace AgileConfig.Server.Data.Repository.Freesql +namespace AgileConfig.Server.Data.Repository.Freesql; + +public class FreesqlRepositoryServiceRegister : IRepositoryServiceRegister { - public class FreesqlRepositoryServiceRegister : IRepositoryServiceRegister + public void AddFixedRepositories(IServiceCollection sc) { - public void AddFixedRepositories(IServiceCollection sc) - { - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddSingleton(); - } + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddSingleton(); + } - public T GetServiceByEnv(IServiceProvider sp, string env) where T : class - { - var factory = sp.GetService(); + public T GetServiceByEnv(IServiceProvider sp, string env) where T : class + { + var factory = sp.GetService(); - if (typeof(T) == typeof(IUow)) - { - return (new FreeSqlUow(factory.Create(env))) as T; - } - if (typeof(T) == typeof(IConfigPublishedRepository)) - { - return new ConfigPublishedRepository(factory.Create(env)) as T; - } - if (typeof(T) == typeof(IConfigRepository)) - { - return new ConfigRepository(factory.Create(env)) as T; - } - if (typeof(T) == typeof(IPublishDetailRepository)) - { - return new PublishDetailRepository(factory.Create(env)) as T; - } - if (typeof(T) == typeof(IPublishTimelineRepository)) - { - return new PublishTimelineRepository(factory.Create(env)) as T; - } + if (typeof(T) == typeof(IUow)) return new FreeSqlUow(factory.Create(env)) as T; + if (typeof(T) == typeof(IConfigPublishedRepository)) + return new ConfigPublishedRepository(factory.Create(env)) as T; + if (typeof(T) == typeof(IConfigRepository)) return new ConfigRepository(factory.Create(env)) as T; + if (typeof(T) == typeof(IPublishDetailRepository)) return new PublishDetailRepository(factory.Create(env)) as T; + if (typeof(T) == typeof(IPublishTimelineRepository)) + return new PublishTimelineRepository(factory.Create(env)) as T; - return default(T); - } + return default; + } - public bool IsSuit4Provider(string provider) - { - var freesqlType = MyFreeSQL.ProviderToFreesqlDbType(provider); + public bool IsSuit4Provider(string provider) + { + var freesqlType = MyFreeSQL.ProviderToFreesqlDbType(provider); - return freesqlType.HasValue ; - } + return freesqlType.HasValue; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/FunctionRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/FunctionRepository.cs new file mode 100644 index 00000000..149461a3 --- /dev/null +++ b/src/AgileConfig.Server.Data.Repository.Freesql/FunctionRepository.cs @@ -0,0 +1,15 @@ +using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Data.Freesql; + +namespace AgileConfig.Server.Data.Repository.Freesql; + +public class FunctionRepository : FreesqlRepository, IFunctionRepository +{ + private readonly IFreeSqlFactory freeSqlFactory; + + public FunctionRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) + { + this.freeSqlFactory = freeSqlFactory; + } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/PublishDetailRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/PublishDetailRepository.cs index 8a1bb2eb..cb35c16b 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/PublishDetailRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/PublishDetailRepository.cs @@ -2,15 +2,14 @@ using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Repository.Freesql +namespace AgileConfig.Server.Data.Repository.Freesql; + +public class PublishDetailRepository : FreesqlRepository, IPublishDetailRepository { - public class PublishDetailRepository : FreesqlRepository, IPublishDetailRepository - { - private readonly IFreeSql freeSql; + private readonly IFreeSql freeSql; - public PublishDetailRepository(IFreeSql freeSql) : base(freeSql) - { - this.freeSql = freeSql; - } + public PublishDetailRepository(IFreeSql freeSql) : base(freeSql) + { + this.freeSql = freeSql; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/PublishTimelineRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/PublishTimelineRepository.cs index 4361f343..497cd655 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/PublishTimelineRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/PublishTimelineRepository.cs @@ -2,25 +2,24 @@ using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Repository.Freesql +namespace AgileConfig.Server.Data.Repository.Freesql; + +public class PublishTimelineRepository : FreesqlRepository, IPublishTimelineRepository { - public class PublishTimelineRepository : FreesqlRepository, IPublishTimelineRepository - { - private readonly IFreeSql freeSql; + private readonly IFreeSql freeSql; - public PublishTimelineRepository(IFreeSql freeSql) : base(freeSql) - { - this.freeSql = freeSql; - } + public PublishTimelineRepository(IFreeSql freeSql) : base(freeSql) + { + this.freeSql = freeSql; + } - public async Task GetLastPublishTimelineNodeIdAsync(string appId, string env) - { - var node = await freeSql.Select() - .Where(x => x.AppId == appId && x.Env == env) - .OrderByDescending(x => x.Version) - .FirstAsync(); + public async Task GetLastPublishTimelineNodeIdAsync(string appId, string env) + { + var node = await freeSql.Select() + .Where(x => x.AppId == appId && x.Env == env) + .OrderByDescending(x => x.Version) + .FirstAsync(); - return node?.Id; - } + return node?.Id; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/RoleDefinitionRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/RoleDefinitionRepository.cs index 02605e62..c99e578b 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/RoleDefinitionRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/RoleDefinitionRepository.cs @@ -2,12 +2,11 @@ using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Repository.Freesql +namespace AgileConfig.Server.Data.Repository.Freesql; + +public class RoleDefinitionRepository : FreesqlRepository, IRoleDefinitionRepository { - public class RoleDefinitionRepository : FreesqlRepository, IRoleDefinitionRepository + public RoleDefinitionRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) { - public RoleDefinitionRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) - { - } } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/RoleFunctionRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/RoleFunctionRepository.cs new file mode 100644 index 00000000..62958c9d --- /dev/null +++ b/src/AgileConfig.Server.Data.Repository.Freesql/RoleFunctionRepository.cs @@ -0,0 +1,15 @@ +using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Data.Freesql; + +namespace AgileConfig.Server.Data.Repository.Freesql; + +public class RoleFunctionRepository : FreesqlRepository, IRoleFunctionRepository +{ + private readonly IFreeSqlFactory freeSqlFactory; + + public RoleFunctionRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) + { + this.freeSqlFactory = freeSqlFactory; + } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/ServerNodeRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/ServerNodeRepository.cs index e472101e..df99cbbf 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/ServerNodeRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/ServerNodeRepository.cs @@ -2,15 +2,14 @@ using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Repository.Freesql +namespace AgileConfig.Server.Data.Repository.Freesql; + +public class ServerNodeRepository : FreesqlRepository, IServerNodeRepository { - public class ServerNodeRepository : FreesqlRepository, IServerNodeRepository - { - private readonly IFreeSqlFactory freeSqlFactory; + private readonly IFreeSqlFactory freeSqlFactory; - public ServerNodeRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) - { - this.freeSqlFactory = freeSqlFactory; - } + public ServerNodeRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) + { + this.freeSqlFactory = freeSqlFactory; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/ServiceInfoRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/ServiceInfoRepository.cs index 0efb712c..9288eee5 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/ServiceInfoRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/ServiceInfoRepository.cs @@ -2,15 +2,14 @@ using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Repository.Freesql +namespace AgileConfig.Server.Data.Repository.Freesql; + +public class ServiceInfoRepository : FreesqlRepository, IServiceInfoRepository { - public class ServiceInfoRepository : FreesqlRepository, IServiceInfoRepository - { - private readonly IFreeSqlFactory freeSqlFactory; + private readonly IFreeSqlFactory freeSqlFactory; - public ServiceInfoRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) - { - this.freeSqlFactory = freeSqlFactory; - } + public ServiceInfoRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) + { + this.freeSqlFactory = freeSqlFactory; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/SettingRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/SettingRepository.cs index e5bd5d62..99ce8c11 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/SettingRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/SettingRepository.cs @@ -2,15 +2,14 @@ using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Repository.Freesql +namespace AgileConfig.Server.Data.Repository.Freesql; + +public class SettingRepository : FreesqlRepository, ISettingRepository { - public class SettingRepository : FreesqlRepository, ISettingRepository - { - private readonly IFreeSqlFactory freeSqlFactory; + private readonly IFreeSqlFactory freeSqlFactory; - public SettingRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) - { - this.freeSqlFactory = freeSqlFactory; - } + public SettingRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) + { + this.freeSqlFactory = freeSqlFactory; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs index 6e4325f7..455fbae5 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs @@ -3,9 +3,6 @@ using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; using AgileConfig.Server.IService; -using System; -using System.Collections.Generic; -using System.Text.Json; namespace AgileConfig.Server.Data.Repository.Freesql; @@ -21,15 +18,15 @@ public SysInitRepository(IFreeSqlFactory freeSqlFactory) public string? GetDefaultEnvironmentFromDb() { var setting = freeSqlFactory.Create().Select().Where(x => x.Id == SystemSettings.DefaultEnvironmentKey) - .ToOne(); + .ToOne(); - return setting?.Value; - } + return setting?.Value; + } public string? GetJwtTokenSecret() { var setting = freeSqlFactory.Create().Select().Where(x => x.Id == SystemSettings.DefaultJwtSecretKey) - .ToOne(); + .ToOne(); return setting?.Value; } @@ -41,183 +38,234 @@ public void SaveInitSetting(Setting setting) public bool InitSa(string password) { - if (string.IsNullOrEmpty(password)) - { - throw new ArgumentNullException(nameof(password)); - } + if (string.IsNullOrEmpty(password)) throw new ArgumentNullException(nameof(password)); var newSalt = Guid.NewGuid().ToString("N"); - password = Encrypt.Md5((password + newSalt)); + password = Encrypt.Md5(password + newSalt); - var sql = freeSqlFactory.Create(); + var sql = freeSqlFactory.Create(); - EnsureSystemRoles(sql); + EnsureSystemRoles(sql); - var user = new User(); + var user = new User(); user.Id = SystemSettings.SuperAdminId; - user.Password = password; + user.Password = password; user.Salt = newSalt; user.Status = UserStatus.Normal; user.Team = ""; user.CreateTime = DateTime.Now; user.UserName = SystemSettings.SuperAdminUserName; - sql.Insert(user).ExecuteAffrows(); + sql.Insert(user).ExecuteAffrows(); var now = DateTime.Now; var userRoles = new List(); - userRoles.Add(new UserRole() - { - Id = Guid.NewGuid().ToString("N"), - RoleId = SystemRoleConstants.SuperAdminId, + userRoles.Add(new UserRole + { + Id = Guid.NewGuid().ToString("N"), + RoleId = SystemRoleConstants.SuperAdminId, UserId = SystemSettings.SuperAdminId, CreateTime = now }); - userRoles.Add(new UserRole() - { - Id = Guid.NewGuid().ToString("N"), -RoleId = SystemRoleConstants.AdminId, - UserId = SystemSettings.SuperAdminId, - CreateTime = now - }); sql.Insert(userRoles).ExecuteAffrows(); - return true; + return true; } - public bool HasSa() + public bool HasSa() { - var anySa = freeSqlFactory.Create().Select().Any(x => x.Id == SystemSettings.SuperAdminId); + var anySa = freeSqlFactory.Create().Select().Any(x => x.Id == SystemSettings.SuperAdminId); - return anySa; + return anySa; } public bool InitDefaultApp(string appName) { - if (string.IsNullOrEmpty(appName)) - { - throw new ArgumentNullException(nameof(appName)); - } + if (string.IsNullOrEmpty(appName)) throw new ArgumentNullException(nameof(appName)); var sql = freeSqlFactory.Create(); var anyDefaultApp = sql.Select().Any(x => x.Id == appName); - ; - if (!anyDefaultApp) - { - sql.Insert(new App() - { - Id = appName, - Name = appName, - Group = "", - Secret = "", -CreateTime = DateTime.Now, - Enabled = true, - Type = AppType.PRIVATE, - AppAdmin = SystemSettings.SuperAdminId - }).ExecuteAffrows(); - } - - return true; + ; + if (!anyDefaultApp) + sql.Insert(new App + { + Id = appName, + Name = appName, + Group = "", + Secret = "", + CreateTime = DateTime.Now, + Enabled = true, + Type = AppType.PRIVATE, + AppAdmin = SystemSettings.SuperAdminId + }).ExecuteAffrows(); + + return true; } private static void EnsureSystemRoles(IFreeSql sql) { // Super Admin gets all permissions - var superAdminPermissions = GetSuperAdminPermissions(); -EnsureRole(sql, SystemRoleConstants.SuperAdminId, SystemRoleConstants.SuperAdminCode, "Super Administrator", superAdminPermissions); - - // Admin gets all permissions + var superAdminPermissions = GetSuperAdminPermissions(); + EnsureRole(sql, SystemRoleConstants.SuperAdminId, "Super Administrator", superAdminPermissions); + EnsureRolePermissions(sql, SystemRoleConstants.SuperAdminId, superAdminPermissions); + + // Administrator gets all permissions (same as SuperAdmin) var adminPermissions = GetAdminPermissions(); - EnsureRole(sql, SystemRoleConstants.AdminId, SystemRoleConstants.AdminCode, "Administrator", adminPermissions); - - // Operator gets limited permissions - var operatorPermissions = GetOperatorPermissions(); - EnsureRole(sql, SystemRoleConstants.OperatorId, SystemRoleConstants.OperatorCode, "Operator", operatorPermissions); + EnsureRole(sql, SystemRoleConstants.AdminId, "Administrator", adminPermissions); + EnsureRolePermissions(sql, SystemRoleConstants.AdminId, adminPermissions); + + // Operator gets all App and Config related permissions + var operatorPermissions = GetOperatorPermissions(); + EnsureRole(sql, SystemRoleConstants.OperatorId, "Operator", operatorPermissions); + EnsureRolePermissions(sql, SystemRoleConstants.OperatorId, operatorPermissions); } private static List GetSuperAdminPermissions() { -// SuperAdmin has all permissions -return new List + // SuperAdmin has all permissions + return new List { - Functions.App_Add, - Functions.App_Edit, - Functions.App_Delete, - Functions.App_Auth, - - Functions.Config_Add, - Functions.Config_Edit, - Functions.Config_Delete, - Functions.Config_Publish, - Functions.Config_Offline, - - Functions.Node_Add, - Functions.Node_Delete, - - Functions.Client_Disconnect, - - Functions.User_Add, - Functions.User_Edit, - Functions.User_Delete, - - Functions.Role_Add, - Functions.Role_Edit, - Functions.Role_Delete - }; + // Application permissions + Functions.App_Read, + Functions.App_Add, + Functions.App_Edit, + Functions.App_Delete, + Functions.App_Auth, + + // Configuration permissions + Functions.Confing_Read, + Functions.Config_Add, + Functions.Config_Edit, + Functions.Config_Delete, + Functions.Config_Publish, + Functions.Config_Offline, + + // Node permissions + Functions.Node_Read, + Functions.Node_Add, + Functions.Node_Delete, + + // Client permissions + Functions.Client_Refresh, + Functions.Client_Disconnect, + + // User permissions + Functions.User_Read, + Functions.User_Add, + Functions.User_Edit, + Functions.User_Delete, + + // Role permissions + Functions.Role_Read, + Functions.Role_Add, + Functions.Role_Edit, + Functions.Role_Delete, + + // Service permissions + Functions.Service_Read, + Functions.Service_Add, + Functions.Service_Delete, + + // System permissions + Functions.Log_Read + }; } private static List GetAdminPermissions() { - // Admin has all permissions same as SuperAdmin + // Administrator has all permissions same as SuperAdmin return GetSuperAdminPermissions(); } private static List GetOperatorPermissions() { - // Operator has limited permissions: - // - App: Add, Edit - // - Config: Add, Edit, Delete, Publish, Offline - return new List + // Operator has all App and Config related permissions + return new List { - Functions.App_Add, - Functions.App_Edit, - - Functions.Config_Add, -Functions.Config_Edit, - Functions.Config_Delete, - Functions.Config_Publish, - Functions.Config_Offline - }; + // All Application permissions + Functions.App_Read, + Functions.App_Add, + Functions.App_Edit, + Functions.App_Delete, + Functions.App_Auth, + + // All Configuration permissions + Functions.Confing_Read, + Functions.Config_Add, + Functions.Config_Edit, + Functions.Config_Delete, + Functions.Config_Publish, + Functions.Config_Offline + }; } - private static void EnsureRole(IFreeSql sql, string id, string code, string name, List functions) + private static void EnsureRole(IFreeSql sql, string id, string name, List functions) { - var role = sql.Select().Where(x => x.Id == id).First(); - var functionsJson = JsonSerializer.Serialize(functions); - - if (role == null) - { - sql.Insert(new Role - { - Id = id, - Code = code, -Name = name, - Description = name, - IsSystem = true, - FunctionsJson = functionsJson, - CreateTime = DateTime.Now - }).ExecuteAffrows(); - } - else - { - role.Code = code; - role.Name = name; - role.Description = name; - role.IsSystem = true; - role.FunctionsJson = functionsJson; - role.UpdateTime = DateTime.Now; - sql.Update().SetSource(role).ExecuteAffrows(); + var role = sql.Select().Where(x => x.Id == id).First(); + + if (role == null) + { + sql.Insert(new Role + { + Id = id, + Name = name, + Description = name, + IsSystem = true, + CreateTime = DateTime.Now + }).ExecuteAffrows(); } + else + { + role.Name = name; + role.Description = name; + role.IsSystem = true; + role.UpdateTime = DateTime.Now; + sql.Update().SetSource(role).ExecuteAffrows(); + } + } + + private static void EnsureRolePermissions(IFreeSql sql, string roleId, List functionCodes) + { + // Get all functions from database + var allFunctions = sql.Select().ToList(); + + // Get existing role-function mappings + var existingRoleFunctions = sql.Select().Where(x => x.RoleId == roleId).ToList(); + + // Find functions that need to be assigned to this role + var functionsToAssign = new List(); + foreach (var functionCode in functionCodes) + { + var function = allFunctions.FirstOrDefault(f => f.Code == functionCode); + if (function != null) + // Check if this role-function mapping already exists + if (!existingRoleFunctions.Any(rf => rf.FunctionId == function.Id)) + functionsToAssign.Add(new RoleFunction + { + Id = Guid.NewGuid().ToString("N"), + RoleId = roleId, + FunctionId = function.Id, + CreateTime = DateTime.Now + }); + } + + // Insert new role-function mappings + if (functionsToAssign.Count > 0) sql.Insert(functionsToAssign).ExecuteAffrows(); + + // Remove role-function mappings that are no longer needed + var functionIdsToKeep = allFunctions + .Where(f => functionCodes.Contains(f.Code)) + .Select(f => f.Id) + .ToList(); + + var roleFunctionsToRemove = existingRoleFunctions + .Where(rf => !functionIdsToKeep.Contains(rf.FunctionId)) + .ToList(); + + if (roleFunctionsToRemove.Count > 0) + sql.Delete() + .Where(rf => roleFunctionsToRemove.Select(r => r.Id).Contains(rf.Id)) + .ExecuteAffrows(); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/SysLogRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/SysLogRepository.cs index bc5bbe69..8ce4375b 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/SysLogRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/SysLogRepository.cs @@ -2,16 +2,14 @@ using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Repository.Freesql -{ - public class SysLogRepository : FreesqlRepository, ISysLogRepository - { +namespace AgileConfig.Server.Data.Repository.Freesql; - private readonly IFreeSqlFactory freeSqlFactory; +public class SysLogRepository : FreesqlRepository, ISysLogRepository +{ + private readonly IFreeSqlFactory freeSqlFactory; - public SysLogRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) - { - this.freeSqlFactory = freeSqlFactory; - } + public SysLogRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) + { + this.freeSqlFactory = freeSqlFactory; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/UserAppAuthRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/UserAppAuthRepository.cs index 9dde4915..55975143 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/UserAppAuthRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/UserAppAuthRepository.cs @@ -2,16 +2,14 @@ using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Repository.Freesql -{ - public class UserAppAuthRepository : FreesqlRepository, IUserAppAuthRepository - { +namespace AgileConfig.Server.Data.Repository.Freesql; - private readonly IFreeSqlFactory freeSqlFactory; +public class UserAppAuthRepository : FreesqlRepository, IUserAppAuthRepository +{ + private readonly IFreeSqlFactory freeSqlFactory; - public UserAppAuthRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) - { - this.freeSqlFactory = freeSqlFactory; - } + public UserAppAuthRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) + { + this.freeSqlFactory = freeSqlFactory; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/UserRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/UserRepository.cs index a54326f8..001d69a9 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/UserRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/UserRepository.cs @@ -2,16 +2,14 @@ using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Repository.Freesql -{ - public class UserRepository : FreesqlRepository, IUserRepository - { +namespace AgileConfig.Server.Data.Repository.Freesql; - private readonly IFreeSqlFactory freeSqlFactory; +public class UserRepository : FreesqlRepository, IUserRepository +{ + private readonly IFreeSqlFactory freeSqlFactory; - public UserRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) - { - this.freeSqlFactory = freeSqlFactory; - } + public UserRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) + { + this.freeSqlFactory = freeSqlFactory; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/UserRoleRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/UserRoleRepository.cs index e8ff5c24..d1209a6e 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/UserRoleRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/UserRoleRepository.cs @@ -2,15 +2,14 @@ using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.Data.Repository.Freesql +namespace AgileConfig.Server.Data.Repository.Freesql; + +public class UserRoleRepository : FreesqlRepository, IUserRoleRepository { - public class UserRoleRepository : FreesqlRepository, IUserRoleRepository - { - private readonly IFreeSqlFactory freeSqlFactory; + private readonly IFreeSqlFactory freeSqlFactory; - public UserRoleRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) - { - this.freeSqlFactory = freeSqlFactory; - } + public UserRoleRepository(IFreeSqlFactory freeSqlFactory) : base(freeSqlFactory.Create()) + { + this.freeSqlFactory = freeSqlFactory; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/AppRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/AppRepository.cs index 0085b33c..7245f5da 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/AppRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/AppRepository.cs @@ -1,6 +1,6 @@ namespace AgileConfig.Server.Data.Repository.Mongodb; -public class AppRepository: MongodbRepository, IAppRepository +public class AppRepository : MongodbRepository, IAppRepository { public AppRepository(string? connectionString) : base(connectionString) { diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/ConfigRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/ConfigRepository.cs index baece651..10dc2382 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/ConfigRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/ConfigRepository.cs @@ -1,6 +1,6 @@ namespace AgileConfig.Server.Data.Repository.Mongodb; -public class ConfigRepository: MongodbRepository, IConfigRepository +public class ConfigRepository : MongodbRepository, IConfigRepository { public ConfigRepository(string? connectionString) : base(connectionString) { diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/FunctionRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/FunctionRepository.cs new file mode 100644 index 00000000..49d3ec64 --- /dev/null +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/FunctionRepository.cs @@ -0,0 +1,13 @@ +namespace AgileConfig.Server.Data.Repository.Mongodb; + +public class FunctionRepository : MongodbRepository, IFunctionRepository +{ + public FunctionRepository(string? connectionString) : base(connectionString) + { + } + + [ActivatorUtilitiesConstructor] + public FunctionRepository(IConfiguration configuration) : base(configuration) + { + } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/MongodbRepositoryExt.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/MongodbRepositoryExt.cs index 177c91a1..d6cc471c 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/MongodbRepositoryExt.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/MongodbRepositoryExt.cs @@ -19,7 +19,7 @@ public static void AddMongodbRepository(this IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); - + services.AddSingleton(); } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/MongodbRepositoryServiceRegister.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/MongodbRepositoryServiceRegister.cs index 6f0c23c8..709ce0de 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/MongodbRepositoryServiceRegister.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/MongodbRepositoryServiceRegister.cs @@ -1,68 +1,66 @@ -using AgileConfig.Server.Data.Abstraction; using AgileConfig.Server.Data.Abstraction.DbProvider; -namespace AgileConfig.Server.Data.Repository.Mongodb +namespace AgileConfig.Server.Data.Repository.Mongodb; + +public class MongodbRepositoryServiceRegister : IRepositoryServiceRegister { - public class MongodbRepositoryServiceRegister : IRepositoryServiceRegister + public void AddFixedRepositories(IServiceCollection sc) + { + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddSingleton(); + } + + public T GetServiceByEnv(IServiceProvider sp, string env) where T : class { - public void AddFixedRepositories(IServiceCollection sc) + var dbConfigInfoFactory = sp.GetRequiredService(); + + if (typeof(T) == typeof(IUow)) return new MongodbUow() as T; + if (typeof(T) == typeof(IConfigPublishedRepository)) { - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddSingleton(); + var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); + return new ConfigPublishedRepository(envDbConfig.ConnectionString) as T; } - public T GetServiceByEnv(IServiceProvider sp, string env) where T : class + if (typeof(T) == typeof(IConfigRepository)) { - var dbConfigInfoFactory = sp.GetRequiredService(); - - if (typeof(T) == typeof(IUow)) - { - return new MongodbUow() as T; - } - if (typeof(T) == typeof(IConfigPublishedRepository)) - { - var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); - return new ConfigPublishedRepository(envDbConfig.ConnectionString) as T; - } - if (typeof(T) == typeof(IConfigRepository)) - { - var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); - return new ConfigRepository(envDbConfig.ConnectionString) as T; - } - if (typeof(T) == typeof(IPublishDetailRepository)) - { - var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); - return new PublishDetailRepository(envDbConfig.ConnectionString) as T; - } - if (typeof(T) == typeof(IPublishTimelineRepository)) - { - var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); - return new PublishTimelineRepository(envDbConfig.ConnectionString) as T; - } + var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); + return new ConfigRepository(envDbConfig.ConnectionString) as T; + } - return default(T); + if (typeof(T) == typeof(IPublishDetailRepository)) + { + var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); + return new PublishDetailRepository(envDbConfig.ConnectionString) as T; } - public bool IsSuit4Provider(string provider) + if (typeof(T) == typeof(IPublishTimelineRepository)) { - switch (provider.ToLower()) - { - case "mongodb": - return true; - default: - break; - } + var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); + return new PublishTimelineRepository(envDbConfig.ConnectionString) as T; + } + + return default; + } - return false; + public bool IsSuit4Provider(string provider) + { + switch (provider.ToLower()) + { + case "mongodb": + return true; } + + return false; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/PublishDetailRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/PublishDetailRepository.cs index 23c29e5e..0bf3f62c 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/PublishDetailRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/PublishDetailRepository.cs @@ -1,6 +1,6 @@ namespace AgileConfig.Server.Data.Repository.Mongodb; -public class PublishDetailRepository: MongodbRepository, IPublishDetailRepository +public class PublishDetailRepository : MongodbRepository, IPublishDetailRepository { public PublishDetailRepository(string? connectionString) : base(connectionString) { diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/PublishTimelineRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/PublishTimelineRepository.cs index 531036e6..8e0544ad 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/PublishTimelineRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/PublishTimelineRepository.cs @@ -1,5 +1,4 @@ - -namespace AgileConfig.Server.Data.Repository.Mongodb; +namespace AgileConfig.Server.Data.Repository.Mongodb; public class PublishTimelineRepository : MongodbRepository, IPublishTimelineRepository { @@ -14,7 +13,8 @@ public PublishTimelineRepository(IConfiguration configuration) : base(configurat public async Task GetLastPublishTimelineNodeIdAsync(string appId, string env) { - var nodes = await this.QueryPageAsync(x => x.AppId == appId && x.Env == env, 1, 1, nameof(PublishTimeline.Version), "DESC"); + var nodes = await QueryPageAsync(x => x.AppId == appId && x.Env == env, 1, 1, nameof(PublishTimeline.Version), + "DESC"); return nodes?.FirstOrDefault()?.Id; } diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/RoleDefinitionRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/RoleDefinitionRepository.cs index b3f7a870..99957385 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/RoleDefinitionRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/RoleDefinitionRepository.cs @@ -1,17 +1,12 @@ -using AgileConfig.Server.Data.Abstraction; -using AgileConfig.Server.Data.Entity; -using Microsoft.Extensions.Configuration; +namespace AgileConfig.Server.Data.Repository.Mongodb; -namespace AgileConfig.Server.Data.Repository.Mongodb +public class RoleDefinitionRepository : MongodbRepository, IRoleDefinitionRepository { - public class RoleDefinitionRepository : MongodbRepository, IRoleDefinitionRepository + public RoleDefinitionRepository(string? connectionString) : base(connectionString) { - public RoleDefinitionRepository(string? connectionString) : base(connectionString) - { - } + } - public RoleDefinitionRepository(IConfiguration configuration) : base(configuration) - { - } + public RoleDefinitionRepository(IConfiguration configuration) : base(configuration) + { } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/RoleFunctionRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/RoleFunctionRepository.cs new file mode 100644 index 00000000..9aacc6a2 --- /dev/null +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/RoleFunctionRepository.cs @@ -0,0 +1,13 @@ +namespace AgileConfig.Server.Data.Repository.Mongodb; + +public class RoleFunctionRepository : MongodbRepository, IRoleFunctionRepository +{ + public RoleFunctionRepository(string? connectionString) : base(connectionString) + { + } + + [ActivatorUtilitiesConstructor] + public RoleFunctionRepository(IConfiguration configuration) : base(configuration) + { + } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/ServerNodeRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/ServerNodeRepository.cs index 580f355c..da19f131 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/ServerNodeRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/ServerNodeRepository.cs @@ -1,6 +1,6 @@ namespace AgileConfig.Server.Data.Repository.Mongodb; -public class ServerNodeRepository: MongodbRepository, IServerNodeRepository +public class ServerNodeRepository : MongodbRepository, IServerNodeRepository { public ServerNodeRepository(string? connectionString) : base(connectionString) { diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/ServiceInfoRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/ServiceInfoRepository.cs index c6f72023..514f5bb9 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/ServiceInfoRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/ServiceInfoRepository.cs @@ -1,6 +1,6 @@ namespace AgileConfig.Server.Data.Repository.Mongodb; -public class ServiceInfoRepository: MongodbRepository, IServiceInfoRepository +public class ServiceInfoRepository : MongodbRepository, IServiceInfoRepository { public ServiceInfoRepository(string? connectionString) : base(connectionString) { diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/SettingRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/SettingRepository.cs index 3641addf..079a980f 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/SettingRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/SettingRepository.cs @@ -1,6 +1,6 @@ namespace AgileConfig.Server.Data.Repository.Mongodb; -public class SettingRepository: MongodbRepository, ISettingRepository +public class SettingRepository : MongodbRepository, ISettingRepository { public SettingRepository(string? connectionString) : base(connectionString) { diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs index 6d40e5b1..d6e201c9 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs @@ -1,28 +1,25 @@ -using AgileConfig.Server.Common; -using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Common; using MongoDB.Driver; -using System; -using System.Collections.Generic; -using System.Text.Json; namespace AgileConfig.Server.Data.Repository.Mongodb; public class SysInitRepository : ISysInitRepository { + private readonly IConfiguration _configuration; + + private readonly string _connectionString = ""; + public SysInitRepository(IConfiguration configuration) { - this._configuration = configuration; + _configuration = configuration; _connectionString = _configuration["db:conn"] ?? ""; } - private string _connectionString = ""; - private MongodbAccess _settingAccess => new MongodbAccess(_connectionString); - private MongodbAccess _userAccess => new MongodbAccess(_connectionString); - private MongodbAccess _userRoleAccess => new MongodbAccess(_connectionString); - private MongodbAccess _roleAccess => new MongodbAccess(_connectionString); - private MongodbAccess _appAccess => new MongodbAccess(_connectionString); - - private readonly IConfiguration _configuration; + private MongodbAccess _settingAccess => new(_connectionString); + private MongodbAccess _userAccess => new(_connectionString); + private MongodbAccess _userRoleAccess => new(_connectionString); + private MongodbAccess _roleAccess => new(_connectionString); + private MongodbAccess _appAccess => new(_connectionString); public string? GetDefaultEnvironmentFromDb() { @@ -46,13 +43,10 @@ public void SaveInitSetting(Setting setting) public bool InitSa(string password) { - if (string.IsNullOrEmpty(password)) - { - throw new ArgumentNullException(nameof(password)); - } + if (string.IsNullOrEmpty(password)) throw new ArgumentNullException(nameof(password)); var newSalt = Guid.NewGuid().ToString("N"); - password = Encrypt.Md5((password + newSalt)); + password = Encrypt.Md5(password + newSalt); EnsureSystemRoles(); @@ -69,14 +63,14 @@ public bool InitSa(string password) var now = DateTime.Now; var userRoles = new List(); - userRoles.Add(new UserRole() + userRoles.Add(new UserRole { Id = Guid.NewGuid().ToString("N"), RoleId = SystemRoleConstants.SuperAdminId, UserId = SystemSettings.SuperAdminId, CreateTime = now }); - userRoles.Add(new UserRole() + userRoles.Add(new UserRole { Id = Guid.NewGuid().ToString("N"), RoleId = SystemRoleConstants.AdminId, @@ -98,16 +92,12 @@ public bool HasSa() public bool InitDefaultApp(string appName) { - if (string.IsNullOrEmpty(appName)) - { - throw new ArgumentNullException(nameof(appName)); - } + if (string.IsNullOrEmpty(appName)) throw new ArgumentNullException(nameof(appName)); var anyDefaultApp = _appAccess.MongoQueryable.FirstOrDefault(x => x.Id == appName); ; if (anyDefaultApp == null) - { - _appAccess.Collection.InsertOne(new App() + _appAccess.Collection.InsertOne(new App { Id = appName, Name = appName, @@ -118,19 +108,18 @@ public bool InitDefaultApp(string appName) Type = AppType.PRIVATE, AppAdmin = SystemSettings.SuperAdminId }); - } return true; } private void EnsureSystemRoles() { - EnsureRole(SystemRoleConstants.SuperAdminId, SystemRoleConstants.SuperAdminCode, "Super Administrator"); - EnsureRole(SystemRoleConstants.AdminId, SystemRoleConstants.AdminCode, "Administrator"); - EnsureRole(SystemRoleConstants.OperatorId, SystemRoleConstants.OperatorCode, "Operator"); + EnsureRole(SystemRoleConstants.SuperAdminId, "Super Administrator"); + EnsureRole(SystemRoleConstants.AdminId, "Administrator"); + EnsureRole(SystemRoleConstants.OperatorId, "Operator"); } - private void EnsureRole(string id, string code, string name) + private void EnsureRole(string id, string name) { var role = _roleAccess.MongoQueryable.FirstOrDefault(x => x.Id == id); if (role == null) @@ -138,21 +127,17 @@ private void EnsureRole(string id, string code, string name) _roleAccess.Collection.InsertOne(new Role { Id = id, - Code = code, Name = name, Description = name, IsSystem = true, - FunctionsJson = JsonSerializer.Serialize(new List()), CreateTime = DateTime.Now }); } else { - role.Code = code; role.Name = name; role.Description = name; role.IsSystem = true; - role.FunctionsJson = role.FunctionsJson ?? JsonSerializer.Serialize(new List()); role.UpdateTime = DateTime.Now; _roleAccess.Collection.ReplaceOne(x => x.Id == id, role, new ReplaceOptions { IsUpsert = true }); } diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/SysLogRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/SysLogRepository.cs index 60cff515..453f6108 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/SysLogRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/SysLogRepository.cs @@ -1,6 +1,6 @@ namespace AgileConfig.Server.Data.Repository.Mongodb; -public class SysLogRepository: MongodbRepository, ISysLogRepository +public class SysLogRepository : MongodbRepository, ISysLogRepository { public SysLogRepository(string? connectionString) : base(connectionString) { diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/UserAppAuthRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/UserAppAuthRepository.cs index ef2da811..783415f8 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/UserAppAuthRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/UserAppAuthRepository.cs @@ -1,6 +1,6 @@ namespace AgileConfig.Server.Data.Repository.Mongodb; -public class UserAppAuthRepository: MongodbRepository, IUserAppAuthRepository +public class UserAppAuthRepository : MongodbRepository, IUserAppAuthRepository { public UserAppAuthRepository(string? connectionString) : base(connectionString) { diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/UserRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/UserRepository.cs index fcae1cab..72a105d8 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/UserRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/UserRepository.cs @@ -1,6 +1,6 @@ namespace AgileConfig.Server.Data.Repository.Mongodb; -public class UserRepository: MongodbRepository, IUserRepository +public class UserRepository : MongodbRepository, IUserRepository { public UserRepository(string? connectionString) : base(connectionString) { diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/UserRoleRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/UserRoleRepository.cs index e2b2f0e0..744c097c 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/UserRoleRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/UserRoleRepository.cs @@ -1,6 +1,6 @@ namespace AgileConfig.Server.Data.Repository.Mongodb; -public class UserRoleRepository: MongodbRepository, IUserRoleRepository +public class UserRoleRepository : MongodbRepository, IUserRoleRepository { public UserRoleRepository(string? connectionString) : base(connectionString) { diff --git a/src/AgileConfig.Server.Data.Repository.Selector/RepositoryExtension.cs b/src/AgileConfig.Server.Data.Repository.Selector/RepositoryExtension.cs index b9479645..d56e43cc 100644 --- a/src/AgileConfig.Server.Data.Repository.Selector/RepositoryExtension.cs +++ b/src/AgileConfig.Server.Data.Repository.Selector/RepositoryExtension.cs @@ -4,89 +4,87 @@ using AgileConfig.Server.Data.Repository.Mongodb; using Microsoft.Extensions.DependencyInjection; -namespace AgileConfig.Server.Data.Repository.Selector +namespace AgileConfig.Server.Data.Repository.Selector; + +public static class RepositoryExtension { - public static class RepositoryExtension + // if add new type of repository service, add it here + private static readonly List _repositoryServiceRegisters = new() { - public static IServiceCollection AddRepositories(this IServiceCollection sc) - { - sc.AddFreeRepository(); + new FreesqlRepositoryServiceRegister(), + new MongodbRepositoryServiceRegister() + }; - var serviceProvider = sc.BuildServiceProvider(); - var dbConfigInfoFactory = serviceProvider.GetRequiredService(); - - var defaultProvider = dbConfigInfoFactory.GetConfigInfo(); + public static IServiceCollection AddRepositories(this IServiceCollection sc) + { + sc.AddFreeRepository(); - if (string.IsNullOrEmpty(defaultProvider.Provider)) - { - throw new ArgumentNullException(nameof(defaultProvider)); - } + var serviceProvider = sc.BuildServiceProvider(); + var dbConfigInfoFactory = serviceProvider.GetRequiredService(); - Console.WriteLine($"default db provider: {defaultProvider.Provider}"); + var defaultProvider = dbConfigInfoFactory.GetConfigInfo(); - #region add default fixed repositories + if (string.IsNullOrEmpty(defaultProvider.Provider)) throw new ArgumentNullException(nameof(defaultProvider)); - GetRepositoryServiceRegister(defaultProvider.Provider).AddFixedRepositories(sc); - - #endregion + Console.WriteLine($"default db provider: {defaultProvider.Provider}"); - #region these repositories genereated dependency env provider, if no env provider use default provider - sc.AddScoped>(sp => env => - { - var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); + #region add default fixed repositories - return GetRepositoryServiceRegister(envDbConfig.Provider).GetServiceByEnv(sp, env); - }); + GetRepositoryServiceRegister(defaultProvider.Provider).AddFixedRepositories(sc); - sc.AddScoped>(sp => env => - { - var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); + #endregion - return GetRepositoryServiceRegister(envDbConfig.Provider).GetServiceByEnv(sp, env); - }); + #region these repositories genereated dependency env provider, if no env provider use default provider - sc.AddScoped>(sp => env => - { - var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); + sc.AddScoped>(sp => env => + { + var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); - return GetRepositoryServiceRegister(envDbConfig.Provider).GetServiceByEnv(sp, env); - }); + return GetRepositoryServiceRegister(envDbConfig.Provider).GetServiceByEnv(sp, env); + }); - sc.AddScoped>(sp => env => - { - var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); + sc.AddScoped>(sp => env => + { + var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); - return GetRepositoryServiceRegister(envDbConfig.Provider).GetServiceByEnv(sp, env); - }); + return GetRepositoryServiceRegister(envDbConfig.Provider) + .GetServiceByEnv(sp, env); + }); - sc.AddScoped>(sp => env => - { - var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); + sc.AddScoped>(sp => env => + { + var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); - return GetRepositoryServiceRegister(envDbConfig.Provider).GetServiceByEnv(sp, env); - }); - #endregion + return GetRepositoryServiceRegister(envDbConfig.Provider).GetServiceByEnv(sp, env); + }); - return sc; - } + sc.AddScoped>(sp => env => + { + var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); - // if add new type of repository service, add it here - private static List _repositoryServiceRegisters = new List() { - new FreesqlRepositoryServiceRegister(), - new MongodbRepositoryServiceRegister() - }; + return GetRepositoryServiceRegister(envDbConfig.Provider) + .GetServiceByEnv(sp, env); + }); - private static IRepositoryServiceRegister GetRepositoryServiceRegister(string provider) + sc.AddScoped>(sp => env => { - foreach (var register in _repositoryServiceRegisters) - { - if (register.IsSuit4Provider(provider)) - { - return register; - } - } - - throw new ArgumentException($"[{provider}] is not a supported provider."); - } + var envDbConfig = dbConfigInfoFactory.GetConfigInfo(env); + + return GetRepositoryServiceRegister(envDbConfig.Provider) + .GetServiceByEnv(sp, env); + }); + + #endregion + + return sc; + } + + private static IRepositoryServiceRegister GetRepositoryServiceRegister(string provider) + { + foreach (var register in _repositoryServiceRegisters) + if (register.IsSuit4Provider(provider)) + return register; + + throw new ArgumentException($"[{provider}] is not a supported provider."); } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/AddAppSuccessful.cs b/src/AgileConfig.Server.Event/AddAppSuccessful.cs index e04344a2..c2c1ee8d 100644 --- a/src/AgileConfig.Server.Event/AddAppSuccessful.cs +++ b/src/AgileConfig.Server.Event/AddAppSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class AddAppSuccessful : IEvent { - public class AddAppSuccessful : IEvent + public AddAppSuccessful(App app, string userName) { - public AddAppSuccessful(App app, string userName) - { - App = app; - UserName = userName; - } - - public App App { get; } - public string UserName { get; } + App = app; + UserName = userName; } -} + + public App App { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/AddConfigSuccessful.cs b/src/AgileConfig.Server.Event/AddConfigSuccessful.cs index d871f5f6..01545b61 100644 --- a/src/AgileConfig.Server.Event/AddConfigSuccessful.cs +++ b/src/AgileConfig.Server.Event/AddConfigSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class AddConfigSuccessful : IEvent { - public class AddConfigSuccessful : IEvent + public AddConfigSuccessful(Config config, string userName) { - public AddConfigSuccessful(Config config, string userName) - { - Config = config; - UserName = userName; - } - - public Config Config { get; } - public string UserName { get; } + Config = config; + UserName = userName; } -} + + public Config Config { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/AddNodeSuccessful.cs b/src/AgileConfig.Server.Event/AddNodeSuccessful.cs index f472dc8e..65193761 100644 --- a/src/AgileConfig.Server.Event/AddNodeSuccessful.cs +++ b/src/AgileConfig.Server.Event/AddNodeSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class AddNodeSuccessful : IEvent { - public class AddNodeSuccessful : IEvent + public AddNodeSuccessful(ServerNode node, string userName) { - public AddNodeSuccessful(ServerNode node, string userName) - { - Node = node; - UserName = userName; - } - - public ServerNode Node { get; } - public string UserName { get; } + Node = node; + UserName = userName; } -} + + public ServerNode Node { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/AddUserSuccessful.cs b/src/AgileConfig.Server.Event/AddUserSuccessful.cs index 2833c659..cb213544 100644 --- a/src/AgileConfig.Server.Event/AddUserSuccessful.cs +++ b/src/AgileConfig.Server.Event/AddUserSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class AddUserSuccessful : IEvent { - public class AddUserSuccessful : IEvent + public AddUserSuccessful(User user, string userName) { - public AddUserSuccessful(User user, string userName) - { - User = user; - UserName = userName; - } - - public User User { get; } - public string UserName { get; } + User = user; + UserName = userName; } -} + + public User User { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/CancelEditConfigSomeSuccessful.cs b/src/AgileConfig.Server.Event/CancelEditConfigSomeSuccessful.cs index ef81cbed..49e77f18 100644 --- a/src/AgileConfig.Server.Event/CancelEditConfigSomeSuccessful.cs +++ b/src/AgileConfig.Server.Event/CancelEditConfigSomeSuccessful.cs @@ -1,19 +1,18 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class CancelEditConfigSomeSuccessful : IEvent { - public class CancelEditConfigSomeSuccessful : IEvent + public CancelEditConfigSomeSuccessful(Config config, string userName) { - public CancelEditConfigSomeSuccessful(Config config, string userName) - { - Config = config; - UserName = userName; - } + Config = config; + UserName = userName; + } - public Config Config { get; } - public string UserName { get; } + public Config Config { get; } + public string UserName { get; } - public string Env { get; set; } - } -} + public string Env { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/CancelEditConfigSuccessful.cs b/src/AgileConfig.Server.Event/CancelEditConfigSuccessful.cs index f8b9a0d5..bcc6780c 100644 --- a/src/AgileConfig.Server.Event/CancelEditConfigSuccessful.cs +++ b/src/AgileConfig.Server.Event/CancelEditConfigSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class CancelEditConfigSuccessful : IEvent { - public class CancelEditConfigSuccessful : IEvent + public CancelEditConfigSuccessful(Config config, string userName) { - public CancelEditConfigSuccessful(Config config, string userName) - { - Config = config; - UserName = userName; - } - - public Config Config { get; } - public string UserName { get; } + Config = config; + UserName = userName; } -} + + public Config Config { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/ChangeUserPasswordSuccessful.cs b/src/AgileConfig.Server.Event/ChangeUserPasswordSuccessful.cs index 27ee0e52..b911bd42 100644 --- a/src/AgileConfig.Server.Event/ChangeUserPasswordSuccessful.cs +++ b/src/AgileConfig.Server.Event/ChangeUserPasswordSuccessful.cs @@ -1,14 +1,13 @@ using AgileConfig.Server.Common.EventBus; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class ChangeUserPasswordSuccessful : IEvent { - public class ChangeUserPasswordSuccessful : IEvent + public ChangeUserPasswordSuccessful(string userName) { - public ChangeUserPasswordSuccessful(string userName) - { - UserName = userName; - } - - public string UserName { get; } + UserName = userName; } -} + + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/DeleteAppSuccessful.cs b/src/AgileConfig.Server.Event/DeleteAppSuccessful.cs index 90fb6bc1..22dc8fb4 100644 --- a/src/AgileConfig.Server.Event/DeleteAppSuccessful.cs +++ b/src/AgileConfig.Server.Event/DeleteAppSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class DeleteAppSuccessful : IEvent { - public class DeleteAppSuccessful : IEvent + public DeleteAppSuccessful(App app, string userName) { - public DeleteAppSuccessful(App app, string userName) - { - App = app; - UserName = userName; - } - - public App App { get; } - public string UserName { get; } + App = app; + UserName = userName; } -} + + public App App { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/DeleteConfigSuccessful.cs b/src/AgileConfig.Server.Event/DeleteConfigSuccessful.cs index 1ecc17d8..f0e4c9d0 100644 --- a/src/AgileConfig.Server.Event/DeleteConfigSuccessful.cs +++ b/src/AgileConfig.Server.Event/DeleteConfigSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class DeleteConfigSuccessful : IEvent { - public class DeleteConfigSuccessful : IEvent + public DeleteConfigSuccessful(Config config, string userName) { - public DeleteConfigSuccessful(Config config, string userName) - { - Config = config; - UserName = userName; - } - - public Config Config { get; } - public string UserName { get; } + Config = config; + UserName = userName; } -} + + public Config Config { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/DeleteNodeSuccessful.cs b/src/AgileConfig.Server.Event/DeleteNodeSuccessful.cs index 3ec5e02a..31268a14 100644 --- a/src/AgileConfig.Server.Event/DeleteNodeSuccessful.cs +++ b/src/AgileConfig.Server.Event/DeleteNodeSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class DeleteNodeSuccessful : IEvent { - public class DeleteNodeSuccessful : IEvent + public DeleteNodeSuccessful(ServerNode node, string userName) { - public DeleteNodeSuccessful(ServerNode node, string userName) - { - Node = node; - UserName = userName; - } - - public ServerNode Node { get; } - public string UserName { get; } + Node = node; + UserName = userName; } -} + + public ServerNode Node { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/DeleteSomeConfigSuccessful.cs b/src/AgileConfig.Server.Event/DeleteSomeConfigSuccessful.cs index c097bdbc..5aca5f18 100644 --- a/src/AgileConfig.Server.Event/DeleteSomeConfigSuccessful.cs +++ b/src/AgileConfig.Server.Event/DeleteSomeConfigSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class DeleteSomeConfigSuccessful : IEvent { - public class DeleteSomeConfigSuccessful : IEvent + public DeleteSomeConfigSuccessful(Config config, string userName) { - public DeleteSomeConfigSuccessful(Config config, string userName) - { - Config = config; - UserName = userName; - } - - public Config Config { get; } - public string UserName { get; } + Config = config; + UserName = userName; } -} + + public Config Config { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/DeleteUserSuccessful.cs b/src/AgileConfig.Server.Event/DeleteUserSuccessful.cs index 28f1a5c8..2d28c81a 100644 --- a/src/AgileConfig.Server.Event/DeleteUserSuccessful.cs +++ b/src/AgileConfig.Server.Event/DeleteUserSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class DeleteUserSuccessful : IEvent { - public class DeleteUserSuccessful : IEvent + public DeleteUserSuccessful(User user, string userName) { - public DeleteUserSuccessful(User user, string userName) - { - UserName = userName; - User = user; - } - - public string UserName { get; } - public User User { get; } + UserName = userName; + User = user; } -} + + public string UserName { get; } + public User User { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/DisableOrEnableAppSuccessful.cs b/src/AgileConfig.Server.Event/DisableOrEnableAppSuccessful.cs index b95fba54..937fc99e 100644 --- a/src/AgileConfig.Server.Event/DisableOrEnableAppSuccessful.cs +++ b/src/AgileConfig.Server.Event/DisableOrEnableAppSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class DisableOrEnableAppSuccessful : IEvent { - public class DisableOrEnableAppSuccessful : IEvent + public DisableOrEnableAppSuccessful(App app, string userName) { - public DisableOrEnableAppSuccessful(App app, string userName) - { - App = app; - UserName = userName; - } - - public App App { get; } - public string UserName { get; } + App = app; + UserName = userName; } -} + + public App App { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/DiscoinnectSuccessful.cs b/src/AgileConfig.Server.Event/DiscoinnectSuccessful.cs index 2f6d0fd0..64d240dd 100644 --- a/src/AgileConfig.Server.Event/DiscoinnectSuccessful.cs +++ b/src/AgileConfig.Server.Event/DiscoinnectSuccessful.cs @@ -1,16 +1,15 @@ using AgileConfig.Server.Common.EventBus; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class DiscoinnectSuccessful : IEvent { - public class DiscoinnectSuccessful : IEvent + public DiscoinnectSuccessful(string clientId, string userName) { - public DiscoinnectSuccessful(string clientId, string userName) - { - ClientId = clientId; - UserName = userName; - } - - public string ClientId { get; } - public string UserName { get; } + ClientId = clientId; + UserName = userName; } -} + + public string ClientId { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/EditAppSuccessful.cs b/src/AgileConfig.Server.Event/EditAppSuccessful.cs index 43f00dd0..a79e156b 100644 --- a/src/AgileConfig.Server.Event/EditAppSuccessful.cs +++ b/src/AgileConfig.Server.Event/EditAppSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class EditAppSuccessful : IEvent { - public class EditAppSuccessful : IEvent + public EditAppSuccessful(App app, string userName) { - public EditAppSuccessful(App app, string userName) - { - App = app; - UserName = userName; - } - - public App App { get; } - public string UserName { get; } + App = app; + UserName = userName; } -} + + public App App { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/EditConfigSuccessful.cs b/src/AgileConfig.Server.Event/EditConfigSuccessful.cs index d957b181..23d3327a 100644 --- a/src/AgileConfig.Server.Event/EditConfigSuccessful.cs +++ b/src/AgileConfig.Server.Event/EditConfigSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class EditConfigSuccessful : IEvent { - public class EditConfigSuccessful : IEvent + public EditConfigSuccessful(Config config, string userName) { - public EditConfigSuccessful(Config config, string userName) - { - Config = config; - UserName = userName; - } - - public Config Config { get; } - public string UserName { get; } + Config = config; + UserName = userName; } -} + + public Config Config { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/EditUserSuccessful.cs b/src/AgileConfig.Server.Event/EditUserSuccessful.cs index 067821d3..74caf413 100644 --- a/src/AgileConfig.Server.Event/EditUserSuccessful.cs +++ b/src/AgileConfig.Server.Event/EditUserSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class EditUserSuccessful : IEvent { - public class EditUserSuccessful : IEvent + public EditUserSuccessful(User user, string userName) { - public EditUserSuccessful(User user, string userName) - { - User = user; - UserName = userName; - } - - public User User { get; } - public string UserName { get; } + User = user; + UserName = userName; } -} + + public User User { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/InitSaPasswordSuccessful.cs b/src/AgileConfig.Server.Event/InitSaPasswordSuccessful.cs index a9b81519..f71d0e73 100644 --- a/src/AgileConfig.Server.Event/InitSaPasswordSuccessful.cs +++ b/src/AgileConfig.Server.Event/InitSaPasswordSuccessful.cs @@ -1,8 +1,7 @@ using AgileConfig.Server.Common.EventBus; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class InitSaPasswordSuccessful : IEvent { - public class InitSaPasswordSuccessful : IEvent - { - } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/LoginEvent.cs b/src/AgileConfig.Server.Event/LoginEvent.cs index f17f0e19..fc05a240 100644 --- a/src/AgileConfig.Server.Event/LoginEvent.cs +++ b/src/AgileConfig.Server.Event/LoginEvent.cs @@ -1,14 +1,13 @@ using AgileConfig.Server.Common.EventBus; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class LoginEvent : IEvent { - public class LoginEvent : IEvent + public LoginEvent(string userName) { - public LoginEvent(string userName) - { - UserName = userName; - } - - public string UserName { get; } + UserName = userName; } -} + + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/PublishConfigSuccessful.cs b/src/AgileConfig.Server.Event/PublishConfigSuccessful.cs index d7fa9ccc..98b3adf4 100644 --- a/src/AgileConfig.Server.Event/PublishConfigSuccessful.cs +++ b/src/AgileConfig.Server.Event/PublishConfigSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class PublishConfigSuccessful : IEvent { - public class PublishConfigSuccessful : IEvent + public PublishConfigSuccessful(PublishTimeline publishTimeline, string userName) { - public PublishConfigSuccessful( PublishTimeline publishTimeline, string userName) - { - PublishTimeline = publishTimeline; - UserName = userName; - } - - public PublishTimeline PublishTimeline { get; } - public string UserName { get; } + PublishTimeline = publishTimeline; + UserName = userName; } -} + + public PublishTimeline PublishTimeline { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/RegisterAServiceSuccessful.cs b/src/AgileConfig.Server.Event/RegisterAServiceSuccessful.cs index d2b934f3..22d49113 100644 --- a/src/AgileConfig.Server.Event/RegisterAServiceSuccessful.cs +++ b/src/AgileConfig.Server.Event/RegisterAServiceSuccessful.cs @@ -1,18 +1,17 @@ using AgileConfig.Server.Common.EventBus; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class RegisterAServiceSuccessful : IEvent { - public class RegisterAServiceSuccessful : IEvent + public RegisterAServiceSuccessful(string serviceId, string serviceName, string userName) { - public RegisterAServiceSuccessful(string serviceId, string serviceName, string userName) - { - ServiceId = serviceId; - ServiceName = serviceName; - UserName = userName; - } - - public string ServiceId { get; } - public string ServiceName { get; } - public string UserName { get; } + ServiceId = serviceId; + ServiceName = serviceName; + UserName = userName; } -} + + public string ServiceId { get; } + public string ServiceName { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/ResetUserPasswordSuccessful.cs b/src/AgileConfig.Server.Event/ResetUserPasswordSuccessful.cs index 14029735..29cd8a9f 100644 --- a/src/AgileConfig.Server.Event/ResetUserPasswordSuccessful.cs +++ b/src/AgileConfig.Server.Event/ResetUserPasswordSuccessful.cs @@ -1,16 +1,15 @@ using AgileConfig.Server.Common.EventBus; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class ResetUserPasswordSuccessful : IEvent { - public class ResetUserPasswordSuccessful : IEvent + public ResetUserPasswordSuccessful(string opUser, string userName) { - public ResetUserPasswordSuccessful(string opUser, string userName) - { - OpUser = opUser; - UserName = userName; - } - - public string OpUser { get; } - public string UserName { get; } + OpUser = opUser; + UserName = userName; } -} + + public string OpUser { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/RollbackConfigSuccessful.cs b/src/AgileConfig.Server.Event/RollbackConfigSuccessful.cs index 526601da..b72416b1 100644 --- a/src/AgileConfig.Server.Event/RollbackConfigSuccessful.cs +++ b/src/AgileConfig.Server.Event/RollbackConfigSuccessful.cs @@ -1,17 +1,16 @@ using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class RollbackConfigSuccessful : IEvent { - public class RollbackConfigSuccessful : IEvent + public RollbackConfigSuccessful(PublishTimeline timelineNode, string userName) { - public RollbackConfigSuccessful(PublishTimeline timelineNode, string userName) - { - TimelineNode = timelineNode; - UserName = userName; - } - - public PublishTimeline TimelineNode { get; } - public string UserName { get; } + TimelineNode = timelineNode; + UserName = userName; } -} + + public PublishTimeline TimelineNode { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/ServiceInfoStatusUpdateEvents.cs b/src/AgileConfig.Server.Event/ServiceInfoStatusUpdateEvents.cs index cb6addb5..5dc67d1a 100644 --- a/src/AgileConfig.Server.Event/ServiceInfoStatusUpdateEvents.cs +++ b/src/AgileConfig.Server.Event/ServiceInfoStatusUpdateEvents.cs @@ -1,31 +1,33 @@ using AgileConfig.Server.Common.EventBus; -namespace AgileConfig.Server.Event +namespace AgileConfig.Server.Event; + +public class ServiceRegisteredEvent : IEvent { - public class ServiceRegisteredEvent : IEvent + public ServiceRegisteredEvent(string uniqueId) { - public ServiceRegisteredEvent(string uniqueId) - { - UniqueId = uniqueId; - } - public string UniqueId { get; } + UniqueId = uniqueId; } - public class ServiceUnRegisterEvent : IEvent + public string UniqueId { get; } +} + +public class ServiceUnRegisterEvent : IEvent +{ + public ServiceUnRegisterEvent(string uniqueId) { - public ServiceUnRegisterEvent(string uniqueId) - { - UniqueId = uniqueId; - } - public string UniqueId { get; } + UniqueId = uniqueId; } - public class ServiceStatusUpdateEvent : IEvent + public string UniqueId { get; } +} + +public class ServiceStatusUpdateEvent : IEvent +{ + public ServiceStatusUpdateEvent(string uniqueId) { - public ServiceStatusUpdateEvent(string uniqueId) - { - UniqueId = uniqueId; - } - public string UniqueId { get; } + UniqueId = uniqueId; } -} + + public string UniqueId { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Event/UnRegisterAServiceSuccessful.cs b/src/AgileConfig.Server.Event/UnRegisterAServiceSuccessful.cs index fbcab2d5..d71d81b9 100644 --- a/src/AgileConfig.Server.Event/UnRegisterAServiceSuccessful.cs +++ b/src/AgileConfig.Server.Event/UnRegisterAServiceSuccessful.cs @@ -1,19 +1,17 @@ using AgileConfig.Server.Common.EventBus; -namespace AgileConfig.Server.Event -{ +namespace AgileConfig.Server.Event; - public class UnRegisterAServiceSuccessful : IEvent +public class UnRegisterAServiceSuccessful : IEvent +{ + public UnRegisterAServiceSuccessful(string serviceId, string serviceName, string userName) { - public UnRegisterAServiceSuccessful(string serviceId, string serviceName, string userName) - { - ServiceId = serviceId; - ServiceName = serviceName; - UserName = userName; - } - - public string ServiceId { get; } - public string ServiceName { get; } - public string UserName { get; } + ServiceId = serviceId; + ServiceName = serviceName; + UserName = userName; } -} + + public string ServiceId { get; } + public string ServiceName { get; } + public string UserName { get; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.EventHandler/ConfigStatusUpdateEventHandlers.cs b/src/AgileConfig.Server.EventHandler/ConfigStatusUpdateEventHandlers.cs index b3771b05..6161295d 100644 --- a/src/AgileConfig.Server.EventHandler/ConfigStatusUpdateEventHandlers.cs +++ b/src/AgileConfig.Server.EventHandler/ConfigStatusUpdateEventHandlers.cs @@ -4,139 +4,122 @@ using AgileConfig.Server.Event; using AgileConfig.Server.IService; -namespace AgileConfig.Server.EventHandler +namespace AgileConfig.Server.EventHandler; + +public class ConfigPublishedHandler : IEventHandler { - public class ConfigPublishedHandler : IEventHandler + private readonly IAppService _appService; + private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; + private readonly IServerNodeService _serverNodeService; + + public ConfigPublishedHandler( + IRemoteServerNodeProxy remoteServerNodeProxy, + IServerNodeService serverNodeService, + IAppService appService) { - private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; - private readonly IServerNodeService _serverNodeService; - private readonly IAppService _appService; + _remoteServerNodeProxy = remoteServerNodeProxy; + _serverNodeService = serverNodeService; + _appService = appService; + } - public ConfigPublishedHandler( - IRemoteServerNodeProxy remoteServerNodeProxy, - IServerNodeService serverNodeService, - IAppService appService) + public async Task Handle(IEvent evt) + { + var evtInstance = evt as PublishConfigSuccessful; + var timelineNode = evtInstance?.PublishTimeline; + if (timelineNode != null) { - _remoteServerNodeProxy = remoteServerNodeProxy; - _serverNodeService = serverNodeService; - _appService = appService; - } + var nodes = await _serverNodeService.GetAllNodesAsync(); + var noticeApps = + await ConfigUpadteNoticeUtil.GetNeedNoticeInheritancedFromAppsAction(_appService, timelineNode.AppId); + noticeApps.Add(timelineNode.AppId, + new WebsocketAction { Action = ActionConst.Reload, Module = ActionModule.ConfigCenter }); - public async Task Handle(IEvent evt) - { - var evtInstance = evt as PublishConfigSuccessful; - var timelineNode = evtInstance?.PublishTimeline; - if (timelineNode != null) + foreach (var node in nodes) { - var nodes = await _serverNodeService.GetAllNodesAsync(); - var noticeApps = await ConfigUpadteNoticeUtil.GetNeedNoticeInheritancedFromAppsAction(_appService, timelineNode.AppId); - noticeApps.Add(timelineNode.AppId, - new WebsocketAction { Action = ActionConst.Reload, Module = ActionModule.ConfigCenter }); - - foreach (var node in nodes) - { - if (node.Status == NodeStatus.Offline) - { - continue; - } + if (node.Status == NodeStatus.Offline) continue; - //all server cache - await _remoteServerNodeProxy.ClearConfigServiceCache(node.Id); - } + //all server cache + await _remoteServerNodeProxy.ClearConfigServiceCache(node.Id); + } - foreach (var node in nodes) - { - if (node.Status == NodeStatus.Offline) - { - continue; - } + foreach (var node in nodes) + { + if (node.Status == NodeStatus.Offline) continue; - foreach (var item in noticeApps) - { - await _remoteServerNodeProxy.AppClientsDoActionAsync( - node.Id, - item.Key, - timelineNode.Env, - item.Value); - } - } + foreach (var item in noticeApps) + await _remoteServerNodeProxy.AppClientsDoActionAsync( + node.Id, + item.Key, + timelineNode.Env, + item.Value); } } - } +} - public class ConfigStatusRollbackSuccessfulThenNoticeClientReloadHandler : IEventHandler +public class ConfigStatusRollbackSuccessfulThenNoticeClientReloadHandler : IEventHandler +{ + private readonly IAppService _appService; + private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; + private readonly IServerNodeService _serverNodeService; + + public ConfigStatusRollbackSuccessfulThenNoticeClientReloadHandler( + IRemoteServerNodeProxy remoteServerNodeProxy, + IServerNodeService serverNodeService, + IAppService appService) { - private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; - private readonly IServerNodeService _serverNodeService; - private readonly IAppService _appService; - - public ConfigStatusRollbackSuccessfulThenNoticeClientReloadHandler( - IRemoteServerNodeProxy remoteServerNodeProxy, - IServerNodeService serverNodeService, - IAppService appService) - { - _remoteServerNodeProxy = remoteServerNodeProxy; - _serverNodeService = serverNodeService; - _appService = appService; - } + _remoteServerNodeProxy = remoteServerNodeProxy; + _serverNodeService = serverNodeService; + _appService = appService; + } - public async Task Handle(IEvent evt) - { - var evtInstance = evt as RollbackConfigSuccessful; - var appId = evtInstance.TimelineNode.AppId; - var env = evtInstance.TimelineNode.Env; - var nodes = await _serverNodeService.GetAllNodesAsync(); - var noticeApps = await ConfigUpadteNoticeUtil.GetNeedNoticeInheritancedFromAppsAction(_appService, appId); - noticeApps.Add(appId, - new WebsocketAction + public async Task Handle(IEvent evt) + { + var evtInstance = evt as RollbackConfigSuccessful; + var appId = evtInstance.TimelineNode.AppId; + var env = evtInstance.TimelineNode.Env; + var nodes = await _serverNodeService.GetAllNodesAsync(); + var noticeApps = await ConfigUpadteNoticeUtil.GetNeedNoticeInheritancedFromAppsAction(_appService, appId); + noticeApps.Add(appId, + new WebsocketAction { Action = ActionConst.Reload, Module = ActionModule.ConfigCenter }); - foreach (var node in nodes) - { - if (node.Status == NodeStatus.Offline) - { - continue; - } + foreach (var node in nodes) + { + if (node.Status == NodeStatus.Offline) continue; - foreach (var item in noticeApps) - { - await _remoteServerNodeProxy.AppClientsDoActionAsync(node.Id, item.Key, - env, - item.Value); - } - } + foreach (var item in noticeApps) + await _remoteServerNodeProxy.AppClientsDoActionAsync(node.Id, item.Key, + env, + item.Value); } - - } +} - class ConfigUpadteNoticeUtil +internal class ConfigUpadteNoticeUtil +{ + /// + /// Determine which applications need to be notified based on the current configuration. + /// + /// + public static async Task> GetNeedNoticeInheritancedFromAppsAction( + IAppService appService, string appId) { - /// - /// Determine which applications need to be notified based on the current configuration. - /// - /// - public static async Task> GetNeedNoticeInheritancedFromAppsAction(IAppService appService, string appId) + var needNoticeAppsActions = new Dictionary(); + var currentApp = await appService.GetAsync(appId); + if (currentApp.Type == AppType.Inheritance) { - Dictionary needNoticeAppsActions = new Dictionary + var inheritancedFromApps = await appService.GetInheritancedFromAppsAsync(appId); + inheritancedFromApps.ForEach(x => { - }; - var currentApp = await appService.GetAsync(appId); - if (currentApp.Type == AppType.Inheritance) - { - var inheritancedFromApps = await appService.GetInheritancedFromAppsAsync(appId); - inheritancedFromApps.ForEach(x => + needNoticeAppsActions.Add(x.Id, new WebsocketAction { - needNoticeAppsActions.Add(x.Id, new WebsocketAction - { - Action = ActionConst.Reload, - Module = ActionModule.ConfigCenter - }); + Action = ActionConst.Reload, + Module = ActionModule.ConfigCenter }); - } - - return needNoticeAppsActions; + }); } + + return needNoticeAppsActions; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.EventHandler/ServiceInfoUpdateHandlers.cs b/src/AgileConfig.Server.EventHandler/ServiceInfoUpdateHandlers.cs index df88c22e..f8e64078 100644 --- a/src/AgileConfig.Server.EventHandler/ServiceInfoUpdateHandlers.cs +++ b/src/AgileConfig.Server.EventHandler/ServiceInfoUpdateHandlers.cs @@ -1,4 +1,6 @@ -using Agile.Config.Protocol; +using System.Net; +using System.Net.Http.Headers; +using Agile.Config.Protocol; using AgileConfig.Server.Common; using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Common.RestClient; @@ -6,190 +8,182 @@ using AgileConfig.Server.Event; using AgileConfig.Server.IService; using Microsoft.Extensions.Logging; -using System.Diagnostics; -using System.Net; -namespace AgileConfig.Server.EventHandler +namespace AgileConfig.Server.EventHandler; + +public class ServiceRegisterHandler : IEventHandler { - public class ServiceRegisterHandler : IEventHandler + private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; + private readonly IServerNodeService _serverNodeService; + private readonly ISysLogService _sysLogService; + + public ServiceRegisterHandler( + IRemoteServerNodeProxy remoteServerNodeProxy, + IServerNodeService serverNodeService, + ISysLogService sysLogService + ) { - private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; - private readonly IServerNodeService _serverNodeService; - private readonly ISysLogService _sysLogService; - - public ServiceRegisterHandler( - IRemoteServerNodeProxy remoteServerNodeProxy, - IServerNodeService serverNodeService, - ISysLogService sysLogService - ) - { - _remoteServerNodeProxy = remoteServerNodeProxy; - _serverNodeService = serverNodeService; - _sysLogService = sysLogService; - } + _remoteServerNodeProxy = remoteServerNodeProxy; + _serverNodeService = serverNodeService; + _sysLogService = sysLogService; + } - public async Task Handle(IEvent evt) + public async Task Handle(IEvent evt) + { + var evtInstance = evt as ServiceRegisteredEvent; + var serverNodes = await _serverNodeService.GetAllNodesAsync(); + foreach (var serverNode in serverNodes.Where(x => x.Status == NodeStatus.Online)) { - var evtInstance = evt as ServiceRegisteredEvent; - var serverNodes = await _serverNodeService.GetAllNodesAsync(); - foreach (var serverNode in serverNodes.Where(x => x.Status == NodeStatus.Online)) - { - //clear cache - _ = _remoteServerNodeProxy.ClearServiceInfoCache(serverNode.Id); - //send ws action - var act = new WebsocketAction() - { - Module = ActionModule.RegisterCenter, - Action = ActionConst.Reload - }; - _ = _remoteServerNodeProxy.AllClientsDoActionAsync(serverNode.Id, act); - } - - await _sysLogService.AddSysLogAsync(new SysLog + //clear cache + _ = _remoteServerNodeProxy.ClearServiceInfoCache(serverNode.Id); + //send ws action + var act = new WebsocketAction { - LogTime = DateTime.Now, - LogType = SysLogType.Normal, - LogText = $"Service [{evtInstance.UniqueId}] registered successfully", - }); + Module = ActionModule.RegisterCenter, + Action = ActionConst.Reload + }; + _ = _remoteServerNodeProxy.AllClientsDoActionAsync(serverNode.Id, act); } + + await _sysLogService.AddSysLogAsync(new SysLog + { + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + LogText = $"Service [{evtInstance.UniqueId}] registered successfully" + }); } +} - public class ServiceUnRegisterHandler : IEventHandler +public class ServiceUnRegisterHandler : IEventHandler +{ + private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; + private readonly IServerNodeService _serverNodeService; + private readonly ISysLogService _sysLogService; + + public ServiceUnRegisterHandler( + IRemoteServerNodeProxy remoteServerNodeProxy, + IServerNodeService serverNodeService, + ISysLogService sysLogService + ) { - private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; - private readonly IServerNodeService _serverNodeService; - private readonly ISysLogService _sysLogService; - - public ServiceUnRegisterHandler( - IRemoteServerNodeProxy remoteServerNodeProxy, - IServerNodeService serverNodeService, - ISysLogService sysLogService - ) - { - _remoteServerNodeProxy = remoteServerNodeProxy; - _serverNodeService = serverNodeService; - _sysLogService = sysLogService; - } + _remoteServerNodeProxy = remoteServerNodeProxy; + _serverNodeService = serverNodeService; + _sysLogService = sysLogService; + } - public async Task Handle(IEvent evt) + public async Task Handle(IEvent evt) + { + var evtInstance = evt as ServiceUnRegisterEvent; + var serverNodes = await _serverNodeService.GetAllNodesAsync(); + foreach (var serverNode in serverNodes.Where(x => x.Status == NodeStatus.Online)) { - var evtInstance = evt as ServiceUnRegisterEvent; - var serverNodes = await _serverNodeService.GetAllNodesAsync(); - foreach (var serverNode in serverNodes.Where(x => x.Status == NodeStatus.Online)) - { - //clear cache - _ = _remoteServerNodeProxy.ClearServiceInfoCache(serverNode.Id); - //send ws action - var act = new WebsocketAction() - { - Module = ActionModule.RegisterCenter, - Action = ActionConst.Reload - }; - _ = _remoteServerNodeProxy.AllClientsDoActionAsync(serverNode.Id, act); - } - - await _sysLogService.AddSysLogAsync(new SysLog + //clear cache + _ = _remoteServerNodeProxy.ClearServiceInfoCache(serverNode.Id); + //send ws action + var act = new WebsocketAction { - LogTime = DateTime.Now, - LogType = SysLogType.Normal, - LogText = $"Service [{evtInstance.UniqueId}] unregistered successfully", - }); + Module = ActionModule.RegisterCenter, + Action = ActionConst.Reload + }; + _ = _remoteServerNodeProxy.AllClientsDoActionAsync(serverNode.Id, act); } + + await _sysLogService.AddSysLogAsync(new SysLog + { + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + LogText = $"Service [{evtInstance.UniqueId}] unregistered successfully" + }); + } +} + +public class ServiceStatusUpdateHandler : IEventHandler +{ + private readonly ILogger _logger; + private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; + private readonly IRestClient _restClient; + private readonly IServerNodeService _serverNodeService; + private readonly IServiceInfoService _serviceInfoService; + private readonly ISysLogService _sysLogService; + + public ServiceStatusUpdateHandler( + IRemoteServerNodeProxy remoteServerNodeProxy, + ILoggerFactory loggerFactory, + IRestClient restClient, + IServerNodeService serverNodeService, + IServiceInfoService serviceInfoService, + ISysLogService sysLogService + ) + { + _remoteServerNodeProxy = remoteServerNodeProxy; + _restClient = restClient; + _serverNodeService = serverNodeService; + _serviceInfoService = serviceInfoService; + _sysLogService = sysLogService; + _logger = loggerFactory.CreateLogger(); } - public class ServiceStatusUpdateHandler : IEventHandler + public async Task Handle(IEvent evt) { - private readonly IRemoteServerNodeProxy _remoteServerNodeProxy; - private readonly IRestClient _restClient; - private ILogger _logger; - private readonly IServerNodeService _serverNodeService; - private readonly IServiceInfoService _serviceInfoService; - private readonly ISysLogService _sysLogService; - - public ServiceStatusUpdateHandler( - IRemoteServerNodeProxy remoteServerNodeProxy, - ILoggerFactory loggerFactory, - IRestClient restClient, - IServerNodeService serverNodeService, - IServiceInfoService serviceInfoService, - ISysLogService sysLogService - ) + var serverNodes = await _serverNodeService.GetAllNodesAsync(); + foreach (var serverNode in serverNodes.Where(x => x.Status == NodeStatus.Online)) { - _remoteServerNodeProxy = remoteServerNodeProxy; - _restClient = restClient; - _serverNodeService = serverNodeService; - _serviceInfoService = serviceInfoService; - _sysLogService = sysLogService; - _logger = loggerFactory.CreateLogger(); + //clear cache + _ = _remoteServerNodeProxy.ClearServiceInfoCache(serverNode.Id); + //send ws action + var act = new WebsocketAction + { + Module = ActionModule.RegisterCenter, + Action = ActionConst.Reload + }; + _ = _remoteServerNodeProxy.AllClientsDoActionAsync(serverNode.Id, act); } - public async Task Handle(IEvent evt) + var evtInstance = evt as ServiceStatusUpdateEvent; + var id = evtInstance?.UniqueId; + if (string.IsNullOrEmpty(id)) return; + var service = await _serviceInfoService.GetByUniqueIdAsync(id); + await _sysLogService.AddSysLogAsync(new SysLog { - var serverNodes = await _serverNodeService.GetAllNodesAsync(); - foreach (var serverNode in serverNodes.Where(x => x.Status == NodeStatus.Online)) - { - //clear cache - _ = _remoteServerNodeProxy.ClearServiceInfoCache(serverNode.Id); - //send ws action - var act = new WebsocketAction() - { - Module = ActionModule.RegisterCenter, - Action = ActionConst.Reload - }; - _ = _remoteServerNodeProxy.AllClientsDoActionAsync(serverNode.Id, act); - } - - var evtInstance = evt as ServiceStatusUpdateEvent; - string id = evtInstance?.UniqueId; - if (string.IsNullOrEmpty(id)) - { - return; - } - var service = await _serviceInfoService.GetByUniqueIdAsync(id); - await _sysLogService.AddSysLogAsync(new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Normal, - LogText = $"Service [{id}] status updated to [{service.Status}]", - }); - - if (service != null && !string.IsNullOrWhiteSpace(service.AlarmUrl) && - service.Status == ServiceStatus.Unhealthy) - { - // Notify when the service becomes unhealthy. - _ = SendServiceOfflineMessageAsync(service); - } - } + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + LogText = $"Service [{id}] status updated to [{service.Status}]" + }); + + if (service != null && !string.IsNullOrWhiteSpace(service.AlarmUrl) && + service.Status == ServiceStatus.Unhealthy) + // Notify when the service becomes unhealthy. + _ = SendServiceOfflineMessageAsync(service); + } - private async Task SendServiceOfflineMessageAsync(ServiceInfo service) + private async Task SendServiceOfflineMessageAsync(ServiceInfo service) + { + var msg = new + { + UniqueId = service.Id, + service.ServiceId, + service.ServiceName, + Time = DateTime.Now, + Status = ServiceStatus.Unhealthy.ToString(), + Message = "Service is unhealthy" + }; + + try { - var msg = new + await FunctionUtil.TRYAsync(async () => { - UniqueId = service.Id, - service.ServiceId, - service.ServiceName, - Time = DateTime.Now, - Status = ServiceStatus.Unhealthy.ToString(), - Message = "Service is unhealthy" - }; + var content = new StringContent(""); + content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + using var resp = await _restClient.PostAsync(service.AlarmUrl, null); - try - { - await FunctionUtil.TRYAsync(async () => - { - var content = new StringContent(""); - content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - using var resp = await _restClient.PostAsync(service.AlarmUrl, null); - - resp.EnsureSuccessStatusCode(); - - return resp.StatusCode == HttpStatusCode.OK; - }, 5); - } - catch (Exception e) - { - _logger.LogError(e, $"try to send message to alarm url {service.AlarmUrl} but failed"); - } + resp.EnsureSuccessStatusCode(); + + return resp.StatusCode == HttpStatusCode.OK; + }, 5); + } + catch (Exception e) + { + _logger.LogError(e, $"try to send message to alarm url {service.AlarmUrl} but failed"); } } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.EventHandler/SystemEventHandlers.cs b/src/AgileConfig.Server.EventHandler/SystemEventHandlers.cs index 2919da5e..d36da836 100644 --- a/src/AgileConfig.Server.EventHandler/SystemEventHandlers.cs +++ b/src/AgileConfig.Server.EventHandler/SystemEventHandlers.cs @@ -3,644 +3,640 @@ using AgileConfig.Server.Event; using AgileConfig.Server.IService; -namespace AgileConfig.Server.EventHandler +namespace AgileConfig.Server.EventHandler; + +public class LoginEventHandler : IEventHandler { - public class LoginEventHandler : IEventHandler + private readonly ISysLogService _sysLogService; + + public LoginEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; + } + + public async Task Handle(IEvent evt) { - private readonly ISysLogService _sysLogService; + var userName = (evt as LoginEvent).UserName; + var log = new SysLog + { + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + LogText = $"{userName} login successful" + }; + await _sysLogService.AddSysLogAsync(log); + } +} - public LoginEventHandler(ISysLogService sysLogService) +public class InitSaPasswordEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; + + public InitSaPasswordEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; + } + + public async Task Handle(IEvent evt) + { + var log = new SysLog { - _sysLogService = sysLogService; - } + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + LogText = "Super administrator password initialized successfully" + }; + await _sysLogService.AddSysLogAsync(log); + } +} + +public class ResetUserPasswordEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; - public async Task Handle(IEvent evt) + public ResetUserPasswordEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; + } + + public async Task Handle(IEvent evt) + { + var evtInstance = evt as ResetUserPasswordSuccessful; + var log = new SysLog + { + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + LogText = $"User {evtInstance.OpUser} reset {evtInstance.UserName}'s password to default password" + }; + await _sysLogService.AddSysLogAsync(log); + } +} + +public class ChangeUserPasswordEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; + + public ChangeUserPasswordEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; + } + + public async Task Handle(IEvent evt) + { + var evtInstance = evt as ChangeUserPasswordSuccessful; + var log = new SysLog + { + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + LogText = $"User {evtInstance.UserName} password changed successfully" + }; + await _sysLogService.AddSysLogAsync(log); + } +} + +public class AddAppEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; + + public AddAppEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; + } + + public async Task Handle(IEvent evt) + { + var evtInstance = evt as AddAppSuccessful; + var app = evtInstance.App; + var userName = evtInstance.UserName; + if (app != null) { - string userName = (evt as LoginEvent).UserName; var log = new SysLog { LogTime = DateTime.Now, LogType = SysLogType.Normal, - LogText = $"{userName} login successful" + LogText = $"User: {userName} added app [AppId: {app.Id}] [AppName: {app.Name}]" }; await _sysLogService.AddSysLogAsync(log); } } +} - public class InitSaPasswordEventHandler : IEventHandler - { - private readonly ISysLogService _sysLogService; +public class EditAppEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; - public InitSaPasswordEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } + public EditAppEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; + } - public async Task Handle(IEvent evt) + public async Task Handle(IEvent evt) + { + var evtInstance = evt as EditAppSuccessful; + var app = evtInstance.App; + var userName = evtInstance.UserName; + if (app != null) { var log = new SysLog { LogTime = DateTime.Now, LogType = SysLogType.Normal, - LogText = $"Super administrator password initialized successfully" + LogText = $"User: {userName} updated app [AppId: {app.Id}] [AppName: {app.Name}]" }; await _sysLogService.AddSysLogAsync(log); } } +} - public class ResetUserPasswordEventHandler : IEventHandler - { - private readonly ISysLogService _sysLogService; +public class DisableOrEnableAppEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; - public ResetUserPasswordEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } + public DisableOrEnableAppEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; + } - public async Task Handle(IEvent evt) + public async Task Handle(IEvent evt) + { + var evtInstance = evt as DisableOrEnableAppSuccessful; + var app = evtInstance.App; + var userName = evtInstance.UserName; + if (app != null) { - var evtInstance = evt as ResetUserPasswordSuccessful; var log = new SysLog { LogTime = DateTime.Now, LogType = SysLogType.Normal, - LogText = $"User {evtInstance.OpUser} reset {evtInstance.UserName}'s password to default password" + LogText = $"User: {userName} {(app.Enabled ? "enabled" : "disabled")} app [AppId: {app.Id}]" }; await _sysLogService.AddSysLogAsync(log); } } +} - public class ChangeUserPasswordEventHandler : IEventHandler - { - private readonly ISysLogService _sysLogService; +public class DeleteAppEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; - public ChangeUserPasswordEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } + public DeleteAppEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; + } - public async Task Handle(IEvent evt) + public async Task Handle(IEvent evt) + { + var evtInstance = evt as DeleteAppSuccessful; + var app = evtInstance.App; + var userName = evtInstance.UserName; + if (app != null) { - var evtInstance = evt as ChangeUserPasswordSuccessful; var log = new SysLog { LogTime = DateTime.Now, - LogType = SysLogType.Normal, - LogText = $"User {evtInstance.UserName} password changed successfully" + LogType = SysLogType.Warn, + LogText = $"User: {userName} deleted app [AppId: {app.Id}]" }; await _sysLogService.AddSysLogAsync(log); } } +} - public class AddAppEventHandler : IEventHandler - { - private readonly ISysLogService _sysLogService; - - public AddAppEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } +public class AddConfigEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; - public async Task Handle(IEvent evt) - { - var evtInstance = evt as AddAppSuccessful; - App app = evtInstance.App; - string userName = evtInstance.UserName; - if (app != null) - { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Normal, - LogText = $"User: {userName} added app [AppId: {app.Id}] [AppName: {app.Name}]" - }; - await _sysLogService.AddSysLogAsync(log); - } - } + public AddConfigEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; } - public class EditAppEventHandler : IEventHandler + public async Task Handle(IEvent evt) { - private readonly ISysLogService _sysLogService; - - public EditAppEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } - - public async Task Handle(IEvent evt) + var evtInstance = evt as AddConfigSuccessful; + var config = evtInstance.Config; + var userName = evtInstance.UserName; + if (config != null) { - var evtInstance = evt as EditAppSuccessful; - App app = evtInstance.App; - string userName = evtInstance.UserName; - if (app != null) + var log = new SysLog { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Normal, - LogText = $"User: {userName} updated app [AppId: {app.Id}] [AppName: {app.Name}]" - }; - await _sysLogService.AddSysLogAsync(log); - } + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + AppId = config.AppId, + LogText = + $"User: {userName} added config [Group: {config.Group}] [Key: {config.Key}] [AppId: {config.AppId}] [Env: {config.Env}] [Pending publish]" + }; + await _sysLogService.AddSysLogAsync(log); } } +} - public class DisableOrEnableAppEventHandler : IEventHandler - { - private readonly ISysLogService _sysLogService; - - public DisableOrEnableAppEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } +public class EditConfigEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; - public async Task Handle(IEvent evt) - { - var evtInstance = evt as DisableOrEnableAppSuccessful; - App app = evtInstance.App; - string userName = evtInstance.UserName; - if (app != null) - { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Normal, - LogText = $"User: {userName} {(app.Enabled ? "enabled" : "disabled")} app [AppId: {app.Id}]" - }; - await _sysLogService.AddSysLogAsync(log); - } - } + public EditConfigEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; } - public class DeleteAppEventHandler : IEventHandler + public async Task Handle(IEvent evt) { - private readonly ISysLogService _sysLogService; - - public DeleteAppEventHandler(ISysLogService sysLogService) + var evtInstance = evt as EditConfigSuccessful; + var config = evtInstance.Config; + var userName = evtInstance.UserName; + if (config != null) { - _sysLogService = sysLogService; - } - - public async Task Handle(IEvent evt) - { - var evtInstance = evt as DeleteAppSuccessful; - App app = evtInstance.App; - string userName = evtInstance.UserName; - if (app != null) + var log = new SysLog { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Warn, - LogText = $"User: {userName} deleted app [AppId: {app.Id}]" - }; - await _sysLogService.AddSysLogAsync(log); - } + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + AppId = config.AppId, + LogText = + $"User: {userName} updated config [Group: {config.Group}] [Key: {config.Key}] [AppId: {config.AppId}] [Env: {config.Env}] [Pending publish]" + }; + await _sysLogService.AddSysLogAsync(log); } } +} - public class AddConfigEventHandler : IEventHandler - { - private readonly ISysLogService _sysLogService; - - public AddConfigEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } +public class DeleteConfigEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; - public async Task Handle(IEvent evt) - { - var evtInstance = evt as AddConfigSuccessful; - Config config = evtInstance.Config; - string userName = evtInstance.UserName; - if (config != null) - { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Normal, - AppId = config.AppId, - LogText = - $"User: {userName} added config [Group: {config.Group}] [Key: {config.Key}] [AppId: {config.AppId}] [Env: {config.Env}] [Pending publish]" - }; - await _sysLogService.AddSysLogAsync(log); - } - } + public DeleteConfigEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; } - public class EditConfigEventHandler : IEventHandler + public async Task Handle(IEvent evt) { - private readonly ISysLogService _sysLogService; - - public EditConfigEventHandler(ISysLogService sysLogService) + var evtInstance = evt as DeleteConfigSuccessful; + var config = evtInstance.Config; + var userName = evtInstance.UserName; + if (config != null) { - _sysLogService = sysLogService; - } - - public async Task Handle(IEvent evt) - { - var evtInstance = evt as EditConfigSuccessful; - Config config = evtInstance.Config; - string userName = evtInstance.UserName; - if (config != null) + var log = new SysLog { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Normal, - AppId = config.AppId, - LogText = - $"User: {userName} updated config [Group: {config.Group}] [Key: {config.Key}] [AppId: {config.AppId}] [Env: {config.Env}] [Pending publish]" - }; - await _sysLogService.AddSysLogAsync(log); - } + LogTime = DateTime.Now, + LogType = SysLogType.Warn, + AppId = config.AppId, + LogText = + $"User: {userName} deleted config [Group: {config.Group}] [Key: {config.Key}] [AppId: {config.AppId}] [Env: {config.Env}] [Pending publish]" + }; + await _sysLogService.AddSysLogAsync(log); } } +} - public class DeleteConfigEventHandler : IEventHandler - { - private readonly ISysLogService _sysLogService; - - public DeleteConfigEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } +public class DeleteSomeConfigEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; - public async Task Handle(IEvent evt) - { - var evtInstance = evt as DeleteConfigSuccessful; - Config config = evtInstance.Config; - string userName = evtInstance.UserName; - if (config != null) - { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Warn, - AppId = config.AppId, - LogText = - $"User: {userName} deleted config [Group: {config.Group}] [Key: {config.Key}] [AppId: {config.AppId}] [Env: {config.Env}] [Pending publish]" - }; - await _sysLogService.AddSysLogAsync(log); - } - } + public DeleteSomeConfigEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; } - public class DeleteSomeConfigEventHandler : IEventHandler + public async Task Handle(IEvent evt) { - private readonly ISysLogService _sysLogService; - - public DeleteSomeConfigEventHandler(ISysLogService sysLogService) + var evtInstance = evt as DeleteSomeConfigSuccessful; + var config = evtInstance.Config; + var userName = evtInstance.UserName; + var env = evtInstance.Config.Env; + if (config != null) { - _sysLogService = sysLogService; - } - - public async Task Handle(IEvent evt) - { - var evtInstance = evt as DeleteSomeConfigSuccessful; - Config config = evtInstance.Config; - string userName = evtInstance.UserName; - string env = evtInstance.Config.Env; - if (config != null) + var log = new SysLog { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Warn, - AppId = config.AppId, - LogText = $"User: {userName} batch deleted configs [Env: {env}]" - }; - await _sysLogService.AddSysLogAsync(log); - } + LogTime = DateTime.Now, + LogType = SysLogType.Warn, + AppId = config.AppId, + LogText = $"User: {userName} batch deleted configs [Env: {env}]" + }; + await _sysLogService.AddSysLogAsync(log); } } +} - public class PublishConfigEventHandler : IEventHandler - { - private readonly ISysLogService _sysLogService; +public class PublishConfigEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; - public PublishConfigEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } + public PublishConfigEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; + } - public async Task Handle(IEvent evt) + public async Task Handle(IEvent evt) + { + var evtInstance = evt as PublishConfigSuccessful; + var node = evtInstance.PublishTimeline; + var userName = evtInstance.UserName; + var env = node.Env; + if (node != null) { - var evtInstance = evt as PublishConfigSuccessful; - var node = evtInstance.PublishTimeline; - string userName = evtInstance.UserName; - string env = node.Env; - if (node != null) + var log = new SysLog { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Normal, - AppId = node.AppId, - LogText = + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + AppId = node.AppId, + LogText = $"User: {userName} published config [AppId: {node.AppId}] [Env: {env}] [Version: {node.PublishTime.Value:yyyyMMddHHmmss}]" - }; - await _sysLogService.AddSysLogAsync(log); - } + }; + await _sysLogService.AddSysLogAsync(log); } } +} - public class RollbackConfigEventHandler : IEventHandler - { - private readonly ISysLogService _sysLogService; - - public RollbackConfigEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } +public class RollbackConfigEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; - public async Task Handle(IEvent evt) - { - var evtInstance = evt as RollbackConfigSuccessful; - var node = evtInstance.TimelineNode; - string userName = evtInstance.UserName; - string env = node.Env; - if (node != null) - { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Warn, - AppId = node.AppId, - LogText = - $"{userName} rolled back app [{node.AppId}] [Env: {env}] to published version [{node.PublishTime.Value:yyyyMMddHHmmss}]" - }; - await _sysLogService.AddSysLogAsync(log); - } - } + public RollbackConfigEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; } - public class CancelEditConfigEventHandler : IEventHandler + public async Task Handle(IEvent evt) { - private readonly ISysLogService _sysLogService; - - public CancelEditConfigEventHandler(ISysLogService sysLogService) + var evtInstance = evt as RollbackConfigSuccessful; + var node = evtInstance.TimelineNode; + var userName = evtInstance.UserName; + var env = node.Env; + if (node != null) { - _sysLogService = sysLogService; - } - - public async Task Handle(IEvent evt) - { - var evtInstance = evt as CancelEditConfigSuccessful; - var config = evtInstance.Config; - string userName = evtInstance.UserName; - if (config != null) + var log = new SysLog { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Normal, - AppId = config.AppId, - LogText = - $"{userName} cancelled editing config [Group: {config.Group}] [Key: {config.Key}] [AppId: {config.AppId}] [Env: {config.Env}]" - }; - await _sysLogService.AddSysLogAsync(log); - } + LogTime = DateTime.Now, + LogType = SysLogType.Warn, + AppId = node.AppId, + LogText = + $"{userName} rolled back app [{node.AppId}] [Env: {env}] to published version [{node.PublishTime.Value:yyyyMMddHHmmss}]" + }; + await _sysLogService.AddSysLogAsync(log); } } +} - public class CancelEditConfigSomeConfig : IEventHandler - { - private readonly ISysLogService _sysLogService; - - public CancelEditConfigSomeConfig(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } - - public async Task Handle(IEvent evt) - { - var evtInstance = evt as CancelEditConfigSomeSuccessful; - string userName = evtInstance.UserName; - var config = evtInstance.Config; - var env = config.Env; +public class CancelEditConfigEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; - if (config != null) - { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Normal, - AppId = config.AppId, - LogText = $"{userName} batch cancelled editing configs [Env: {env}]" - }; - await _sysLogService.AddSysLogAsync(log); - } - } + public CancelEditConfigEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; } - public class AddNodeEventHandler : IEventHandler + public async Task Handle(IEvent evt) { - private readonly ISysLogService _sysLogService; - - public AddNodeEventHandler(ISysLogService sysLogService) + var evtInstance = evt as CancelEditConfigSuccessful; + var config = evtInstance.Config; + var userName = evtInstance.UserName; + if (config != null) { - _sysLogService = sysLogService; - } - - public async Task Handle(IEvent evt) - { - var evtInstance = evt as AddNodeSuccessful; - string userName = evtInstance.UserName; - var node = evtInstance.Node; - - if (node != null) + var log = new SysLog { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Normal, - LogText = $"User: {userName} added node: {node.Id}" - }; - await _sysLogService.AddSysLogAsync(log); - } + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + AppId = config.AppId, + LogText = + $"{userName} cancelled editing config [Group: {config.Group}] [Key: {config.Key}] [AppId: {config.AppId}] [Env: {config.Env}]" + }; + await _sysLogService.AddSysLogAsync(log); } } +} - public class DeleteNodeEventHandler : IEventHandler +public class CancelEditConfigSomeConfig : IEventHandler +{ + private readonly ISysLogService _sysLogService; + + public CancelEditConfigSomeConfig(ISysLogService sysLogService) { - private readonly ISysLogService _sysLogService; + _sysLogService = sysLogService; + } - public DeleteNodeEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } + public async Task Handle(IEvent evt) + { + var evtInstance = evt as CancelEditConfigSomeSuccessful; + var userName = evtInstance.UserName; + var config = evtInstance.Config; + var env = config.Env; - public async Task Handle(IEvent evt) + if (config != null) { - var evtInstance = evt as DeleteNodeSuccessful; - string userName = evtInstance.UserName; - var node = evtInstance.Node; - - if (node != null) + var log = new SysLog { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Warn, - LogText = $"User: {userName} deleted node: {node.Id}" - }; - await _sysLogService.AddSysLogAsync(log); - } + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + AppId = config.AppId, + LogText = $"{userName} batch cancelled editing configs [Env: {env}]" + }; + await _sysLogService.AddSysLogAsync(log); } } +} - public class AddUserEventHandler : IEventHandler +public class AddNodeEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; + + public AddNodeEventHandler(ISysLogService sysLogService) { - private readonly ISysLogService _sysLogService; + _sysLogService = sysLogService; + } - public AddUserEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } + public async Task Handle(IEvent evt) + { + var evtInstance = evt as AddNodeSuccessful; + var userName = evtInstance.UserName; + var node = evtInstance.Node; - public async Task Handle(IEvent evt) + if (node != null) { - var evtInstance = evt as AddUserSuccessful; - string userName = evtInstance.UserName; - var user = evtInstance.User; - - if (user != null) + var log = new SysLog { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Normal, - LogText = $"User: {userName} added user: {user.UserName} successfully" - }; - await _sysLogService.AddSysLogAsync(log); - } + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + LogText = $"User: {userName} added node: {node.Id}" + }; + await _sysLogService.AddSysLogAsync(log); } } +} + +public class DeleteNodeEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; - public class EditUserEventHandler : IEventHandler + public DeleteNodeEventHandler(ISysLogService sysLogService) { - private readonly ISysLogService _sysLogService; + _sysLogService = sysLogService; + } - public EditUserEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } + public async Task Handle(IEvent evt) + { + var evtInstance = evt as DeleteNodeSuccessful; + var userName = evtInstance.UserName; + var node = evtInstance.Node; - public async Task Handle(IEvent evt) + if (node != null) { - var evtInstance = evt as EditUserSuccessful; - string userName = evtInstance.UserName; - var user = evtInstance.User; - - if (user != null) + var log = new SysLog { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Normal, - LogText = $"User: {userName} updated user: {user.UserName} successfully" - }; - await _sysLogService.AddSysLogAsync(log); - } + LogTime = DateTime.Now, + LogType = SysLogType.Warn, + LogText = $"User: {userName} deleted node: {node.Id}" + }; + await _sysLogService.AddSysLogAsync(log); } } +} + +public class AddUserEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; - public class DeleteUserEventHandler : IEventHandler + public AddUserEventHandler(ISysLogService sysLogService) { - private readonly ISysLogService _sysLogService; + _sysLogService = sysLogService; + } - public DeleteUserEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } + public async Task Handle(IEvent evt) + { + var evtInstance = evt as AddUserSuccessful; + var userName = evtInstance.UserName; + var user = evtInstance.User; - public async Task Handle(IEvent evt) + if (user != null) { - var evtInstance = evt as DeleteUserSuccessful; - string userName = evtInstance.UserName; - var user = evtInstance.User; - - if (user != null) + var log = new SysLog { - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Warn, - LogText = $"User: {userName} deleted user: {user.UserName} successfully" - }; - await _sysLogService.AddSysLogAsync(log); - } + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + LogText = $"User: {userName} added user: {user.UserName} successfully" + }; + await _sysLogService.AddSysLogAsync(log); } } +} - public class DisContectClientEventHandler : IEventHandler +public class EditUserEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; + + public EditUserEventHandler(ISysLogService sysLogService) { - private readonly ISysLogService _sysLogService; + _sysLogService = sysLogService; + } - public DisContectClientEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } + public async Task Handle(IEvent evt) + { + var evtInstance = evt as EditUserSuccessful; + var userName = evtInstance.UserName; + var user = evtInstance.User; - public async Task Handle(IEvent evt) + if (user != null) { var log = new SysLog { LogTime = DateTime.Now, LogType = SysLogType.Normal, + LogText = $"User: {userName} updated user: {user.UserName} successfully" }; await _sysLogService.AddSysLogAsync(log); } - } +} - public class RegisterAServiceEventHandler : IEventHandler +public class DeleteUserEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; + + public DeleteUserEventHandler(ISysLogService sysLogService) { - private readonly ISysLogService _sysLogService; + _sysLogService = sysLogService; + } - public RegisterAServiceEventHandler(ISysLogService sysLogService) - { - _sysLogService = sysLogService; - } + public async Task Handle(IEvent evt) + { + var evtInstance = evt as DeleteUserSuccessful; + var userName = evtInstance.UserName; + var user = evtInstance.User; - public async Task Handle(IEvent evt) + if (user != null) { - var evtInstance = evt as RegisterAServiceSuccessful; - var log = new SysLog { LogTime = DateTime.Now, - LogType = SysLogType.Normal, - LogText = $"Service: [{evtInstance.ServiceId}] [{evtInstance.ServiceName}] registered successfully" + LogType = SysLogType.Warn, + LogText = $"User: {userName} deleted user: {user.UserName} successfully" }; await _sysLogService.AddSysLogAsync(log); - } } +} - public class UnRegisterAServiceEventHandler : IEventHandler +public class DisContectClientEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; + + public DisContectClientEventHandler(ISysLogService sysLogService) { - private readonly ISysLogService _sysLogService; + _sysLogService = sysLogService; + } - public UnRegisterAServiceEventHandler(ISysLogService sysLogService) + public async Task Handle(IEvent evt) + { + var log = new SysLog { - _sysLogService = sysLogService; - } + LogTime = DateTime.Now, + LogType = SysLogType.Normal + }; + await _sysLogService.AddSysLogAsync(log); + } +} - public async Task Handle(IEvent evt) - { - var evtInstance = evt as UnRegisterAServiceSuccessful; +public class RegisterAServiceEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; - var log = new SysLog - { - LogTime = DateTime.Now, - LogType = SysLogType.Normal, - LogText = $"Service: [{evtInstance.ServiceId}] [{evtInstance.ServiceName}] unregistered successfully" - }; - await _sysLogService.AddSysLogAsync(log); + public RegisterAServiceEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; + } - } + public async Task Handle(IEvent evt) + { + var evtInstance = evt as RegisterAServiceSuccessful; + + var log = new SysLog + { + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + LogText = $"Service: [{evtInstance.ServiceId}] [{evtInstance.ServiceName}] registered successfully" + }; + await _sysLogService.AddSysLogAsync(log); } } + +public class UnRegisterAServiceEventHandler : IEventHandler +{ + private readonly ISysLogService _sysLogService; + + public UnRegisterAServiceEventHandler(ISysLogService sysLogService) + { + _sysLogService = sysLogService; + } + + public async Task Handle(IEvent evt) + { + var evtInstance = evt as UnRegisterAServiceSuccessful; + + var log = new SysLog + { + LogTime = DateTime.Now, + LogType = SysLogType.Normal, + LogText = $"Service: [{evtInstance.ServiceId}] [{evtInstance.ServiceName}] unregistered successfully" + }; + await _sysLogService.AddSysLogAsync(log); + } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IAdmBasicAuthService.cs b/src/AgileConfig.Server.IService/IAdmBasicAuthService.cs index 4af54eed..3f9199ab 100644 --- a/src/AgileConfig.Server.IService/IAdmBasicAuthService.cs +++ b/src/AgileConfig.Server.IService/IAdmBasicAuthService.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Http; -namespace AgileConfig.Server.IService +namespace AgileConfig.Server.IService; + +/// +/// Provides basic authentication based on administrator credentials. +/// +public interface IAdmBasicAuthService : IBasicAuthService { - /// - /// Provides basic authentication based on administrator credentials. - /// - public interface IAdmBasicAuthService : IBasicAuthService - { - (string,string) GetUserNamePassword(HttpRequest httpRequest); - } + (string, string) GetUserNamePassword(HttpRequest httpRequest); } \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IAppBasicAuthService.cs b/src/AgileConfig.Server.IService/IAppBasicAuthService.cs index 4eb31847..24c83c53 100644 --- a/src/AgileConfig.Server.IService/IAppBasicAuthService.cs +++ b/src/AgileConfig.Server.IService/IAppBasicAuthService.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Http; -namespace AgileConfig.Server.IService +namespace AgileConfig.Server.IService; + +/// +/// Provides basic authentication based on application credentials. +/// +public interface IAppBasicAuthService : IBasicAuthService { - /// - /// Provides basic authentication based on application credentials. - /// - public interface IAppBasicAuthService: IBasicAuthService - { - (string, string) GetAppIdSecret(HttpRequest httpRequest); - } + (string, string) GetAppIdSecret(HttpRequest httpRequest); } \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IAppService.cs b/src/AgileConfig.Server.IService/IAppService.cs index c1cd2c26..f0032867 100644 --- a/src/AgileConfig.Server.IService/IAppService.cs +++ b/src/AgileConfig.Server.IService/IAppService.cs @@ -1,48 +1,50 @@ -using AgileConfig.Server.Data.Entity; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.IService +namespace AgileConfig.Server.IService; + +public class GroupedApp { - public class GroupedApp - { - public App App { get; set; } - public List Children { get; set; } - } - public interface IAppService : IDisposable - { - Task GetAsync(string id); - Task AddAsync(App app); - Task AddAsync(App app, List appInheritanceds); + public App App { get; set; } + public List Children { get; set; } +} - Task DeleteAsync(App app); +public interface IAppService : IDisposable +{ + Task GetAsync(string id); + Task AddAsync(App app); + Task AddAsync(App app, List appInheritanceds); - Task DeleteAsync(string appId); + Task DeleteAsync(App app); - Task UpdateAsync(App app, List appInheritanceds); - Task UpdateAsync(App app); + Task DeleteAsync(string appId); - Task> GetAllAppsAsync(); + Task UpdateAsync(App app, List appInheritanceds); + Task UpdateAsync(App app); - Task<(List Apps, long Count)> SearchAsync(string id, string name, string group,string sortField, string ascOrDesc, - int current, int pageSize); - - Task<(List GroupedApps, long Count)> SearchGroupedAsync(string id, string name, string group,string sortField, string ascOrDesc, - int current, int pageSize); - - Task> GetAllInheritancedAppsAsync(); + Task> GetAllAppsAsync(); - Task CountEnabledAppsAsync(); + Task<(List Apps, long Count)> SearchAsync(string id, string name, string group, string sortField, + string ascOrDesc, + int current, int pageSize); - Task> GetInheritancedAppsAsync(string appId); + Task<(List GroupedApps, long Count)> SearchGroupedAsync(string id, string name, string group, + string sortField, string ascOrDesc, + int current, int pageSize); - Task> GetInheritancedFromAppsAsync(string appId); + Task> GetAllInheritancedAppsAsync(); - Task SaveUserAppAuth(string appId, List userIds, string permission); + Task CountEnabledAppsAsync(); - Task> GetUserAppAuth(string appId, string permission); + Task> GetInheritancedAppsAsync(string appId); - Task> GetAppGroups(); - } -} + Task> GetInheritancedFromAppsAsync(string appId); + + Task SaveUserAppAuth(string appId, List userIds, string permission); + + Task> GetUserAppAuth(string appId, string permission); + + Task> GetAppGroups(); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IBasicAuthService.cs b/src/AgileConfig.Server.IService/IBasicAuthService.cs index 4ece5b35..4c585f0b 100644 --- a/src/AgileConfig.Server.IService/IBasicAuthService.cs +++ b/src/AgileConfig.Server.IService/IBasicAuthService.cs @@ -1,10 +1,9 @@ -using Microsoft.AspNetCore.Http; -using System.Threading.Tasks; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; -namespace AgileConfig.Server.IService +namespace AgileConfig.Server.IService; + +public interface IBasicAuthService { - public interface IBasicAuthService - { - Task ValidAsync(HttpRequest httpRequest); - } -} + Task ValidAsync(HttpRequest httpRequest); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IConfigService.cs b/src/AgileConfig.Server.IService/IConfigService.cs index e33f5437..cba1cbe8 100644 --- a/src/AgileConfig.Server.IService/IConfigService.cs +++ b/src/AgileConfig.Server.IService/IConfigService.cs @@ -1,238 +1,243 @@ -using AgileConfig.Server.Data.Entity; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.IService +namespace AgileConfig.Server.IService; + +public interface IConfigService : IDisposable { - public interface IConfigService: IDisposable - { - /// - /// Publish the current pending configuration items. When ids are provided, only those items are published; otherwise all pending items are published. - /// - /// Application ID whose configuration should be published. - /// Specific configuration identifiers to publish, or null to publish all pending items. - /// Publish log message. - /// User name of the operator performing the publish. - /// Environment in which to publish. - /// Publish success indicator and resulting publish timeline identifier. - Task<(bool result, string publishTimelineId)> Publish(string appId,string[] ids, string log, string operatorr, string env); - - Task GetAsync(string id, string env); - - Task GetByAppIdKeyEnv(string appId, string group, string key, string env); - /// - /// Query configuration entries by appId, group, and key; group and key use LIKE matching. - /// - /// Application ID whose configurations should be searched. - /// Group name filter, supporting partial matches. - /// Configuration key filter, supporting partial matches. - /// Environment from which to query configuration. - /// List of configuration entries matching the filter. - Task> Search(string appId, string group, string key, string env); - Task> GetByAppIdAsync(string appId, string env); - - /// - /// Retrieve the published configuration for the app together with inherited app configuration. - /// - /// Application ID whose published configuration should be retrieved. - /// Environment from which to load the configuration. - /// List of configuration entries merged with inherited apps. - Task> GetPublishedConfigsByAppIdWithInheritance(string appId, string env); - /// - /// Retrieve the app configuration merged with inherited configuration and convert it into a dictionary. - /// - /// Application ID whose published configuration should be retrieved. - /// Environment from which to load the configuration. - /// Dictionary of configuration entries keyed by generated configuration key. - Task> GetPublishedConfigsByAppIdWithInheritance_Dictionary(string appId, string env); - Task AddAsync(Config config, string env); - - Task AddRangeAsync(List configs, string env); - - Task DeleteAsync(Config config, string env); - - Task DeleteAsync(string configId, string env); - - Task UpdateAsync(Config config, string env); - - Task UpdateAsync(List configs, string env); - - /// - /// Cancel the edit status. - /// - /// Configuration identifiers to revert. - /// Environment in which the configurations reside. - /// True when the edit status is successfully cleared. - Task CancelEdit(List ids, string env); - - Task> GetAllConfigsAsync(string env); - - Task CountEnabledConfigsAsync(); - - /// - /// Calculate the MD5 hash of the published configuration items. - /// - /// Application ID whose published configuration should be hashed. - /// Environment providing the configuration. - /// MD5 hash of the published configuration. - Task AppPublishedConfigsMd5(string appId, string env); - /// - /// Calculate the MD5 hash of the published configuration merged with inherited app configuration. - /// - /// Application ID whose inherited configuration should be hashed. - /// Environment providing the configuration. - /// MD5 hash of the merged configuration. - Task AppPublishedConfigsMd5WithInheritance(string appId, string env); - - /// - /// Calculate and cache the MD5 hash of the published configuration items. - /// - /// Application ID whose published configuration hash should be cached. - /// Environment associated with the cached hash. - /// MD5 hash of the published configuration. - Task AppPublishedConfigsMd5Cache(string appId, string env); - - /// - /// Calculate and cache the MD5 hash of the published configuration merged with inherited app configuration. - /// - /// Application ID whose inherited configuration hash should be cached. - /// Environment associated with the cached hash. - /// MD5 hash of the merged configuration. - Task AppPublishedConfigsMd5CacheWithInheritance(string appId, string env); - - /// - /// Build the configuration key. - /// - /// Configuration item whose key should be generated. - /// Composite key string. - string GenerateKey(Config config); - - /// - /// Determine whether the configuration has been published. - /// - /// Configuration identifier to check. - /// Environment in which to look for published configuration. - /// True when the configuration is published. - Task IsPublishedAsync(string configId, string env); - - /// - /// Retrieve publish details by the publish timeline identifier. - /// - /// Publish timeline identifier to query. - /// Environment in which the publish timeline exists. - /// List of publish details for the timeline. - Task> GetPublishDetailByPublishTimelineIdAsync(string publishTimelineId, string env); - - /// - /// Retrieve the publish timeline node. - /// - /// Publish timeline identifier. - /// Environment in which the publish timeline exists. - /// Publish timeline entity. - Task GetPublishTimeLineNodeAsync(string publishTimelineId, string env); - - /// - /// Retrieve the publish history for the application. - /// - /// Application ID whose publish history is requested. - /// Environment to query. - /// List of publish timeline entries. - Task> GetPublishTimelineHistoryAsync(string appId, string env); - - /// - /// Retrieve the list of publish details for the application. - /// - /// Application ID whose publish details are requested. - /// Environment to query. - /// List of publish detail records. - Task> GetPublishDetailListAsync(string appId, string env); - - /// - /// Retrieve the publish history for a specific configuration item. - /// - /// Configuration identifier to query. - /// Environment to query. - /// List of publish detail records for the configuration. - Task> GetConfigPublishedHistory(string configId, string env); - - /// - /// Retrieve the currently published configuration items. - /// - /// Application ID whose published configuration is requested. - /// Environment to query. - /// List of published configuration entries. - Task> GetPublishedConfigsAsync(string appId, string env); - - /// - /// Retrieve a single published configuration item. - /// - /// Configuration identifier to query. - /// Environment to query. - /// Published configuration entity. - Task GetPublishedConfigAsync(string configId, string env); - - /// - /// Roll back to the configuration version at the specified publish timeline. - /// - /// Publish timeline identifier to restore. - /// Environment in which to perform the rollback. - /// True when the rollback succeeds. - Task RollbackAsync(string publishTimelineId, string env); - - /// - /// Synchronize configuration to target environments. - /// - /// Application ID whose configuration should be synchronized. - /// Source environment. - /// Target environments. - /// True when synchronization succeeds. - Task EnvSync(string appId, string currentEnv, List toEnvs); - - /// - /// Retrieve the configuration as a list of key/value pairs. - /// - /// Application ID whose configuration is requested. - /// Environment to query. - /// List of key/value pairs representing configuration entries. - Task>> GetKvListAsync(string appId, string env); - - /// - /// Convert the configuration represented as JSON into standard config entries and persist them. - /// Compare with the existing configuration to mark additions, deletions, and updates. - /// - /// JSON string representing the configuration. - /// Application ID. - /// Environment name. - /// Indicates whether to apply patch mode updates. - /// - Task SaveJsonAsync(string json, string appId, string env, bool isPatch); - - /// - /// Persist the key/value configuration list to the database. - /// - /// Serialized key/value pairs. - /// Application ID that owns the configuration. - /// Environment where the configuration should be saved. - /// Indicates whether to apply patch mode updates. - /// True when the key/value list is saved successfully. - Task SaveKvListAsync(string kvString, string appId, string env, bool isPatch); - - /// - /// Validate whether the key/value text is well-formed. - /// - /// Text containing key/value pairs. - /// Tuple indicating whether validation passed and an error message. - (bool, string) ValidateKvString(string kvStr); - - /// - /// clear all cache - /// - void ClearCache(); - - Task GetLastPublishTimelineVirtualIdAsync(string appId, string env); - - Task GetLastPublishTimelineVirtualIdAsyncWithCache(string appId, string env); - - } -} + /// + /// Publish the current pending configuration items. When ids are provided, only those items are published; otherwise + /// all pending items are published. + /// + /// Application ID whose configuration should be published. + /// Specific configuration identifiers to publish, or null to publish all pending items. + /// Publish log message. + /// User name of the operator performing the publish. + /// Environment in which to publish. + /// Publish success indicator and resulting publish timeline identifier. + Task<(bool result, string publishTimelineId)> Publish(string appId, string[] ids, string log, string operatorr, + string env); + + Task GetAsync(string id, string env); + + Task GetByAppIdKeyEnv(string appId, string group, string key, string env); + + /// + /// Query configuration entries by appId, group, and key; group and key use LIKE matching. + /// + /// Application ID whose configurations should be searched. + /// Group name filter, supporting partial matches. + /// Configuration key filter, supporting partial matches. + /// Environment from which to query configuration. + /// List of configuration entries matching the filter. + Task> Search(string appId, string group, string key, string env); + + Task> GetByAppIdAsync(string appId, string env); + + /// + /// Retrieve the published configuration for the app together with inherited app configuration. + /// + /// Application ID whose published configuration should be retrieved. + /// Environment from which to load the configuration. + /// List of configuration entries merged with inherited apps. + Task> GetPublishedConfigsByAppIdWithInheritance(string appId, string env); + + /// + /// Retrieve the app configuration merged with inherited configuration and convert it into a dictionary. + /// + /// Application ID whose published configuration should be retrieved. + /// Environment from which to load the configuration. + /// Dictionary of configuration entries keyed by generated configuration key. + Task> GetPublishedConfigsByAppIdWithInheritance_Dictionary(string appId, string env); + + Task AddAsync(Config config, string env); + + Task AddRangeAsync(List configs, string env); + + Task DeleteAsync(Config config, string env); + + Task DeleteAsync(string configId, string env); + + Task UpdateAsync(Config config, string env); + + Task UpdateAsync(List configs, string env); + + /// + /// Cancel the edit status. + /// + /// Configuration identifiers to revert. + /// Environment in which the configurations reside. + /// True when the edit status is successfully cleared. + Task CancelEdit(List ids, string env); + + Task> GetAllConfigsAsync(string env); + + Task CountEnabledConfigsAsync(); + + /// + /// Calculate the MD5 hash of the published configuration items. + /// + /// Application ID whose published configuration should be hashed. + /// Environment providing the configuration. + /// MD5 hash of the published configuration. + Task AppPublishedConfigsMd5(string appId, string env); + + /// + /// Calculate the MD5 hash of the published configuration merged with inherited app configuration. + /// + /// Application ID whose inherited configuration should be hashed. + /// Environment providing the configuration. + /// MD5 hash of the merged configuration. + Task AppPublishedConfigsMd5WithInheritance(string appId, string env); + + /// + /// Calculate and cache the MD5 hash of the published configuration items. + /// + /// Application ID whose published configuration hash should be cached. + /// Environment associated with the cached hash. + /// MD5 hash of the published configuration. + Task AppPublishedConfigsMd5Cache(string appId, string env); + + /// + /// Calculate and cache the MD5 hash of the published configuration merged with inherited app configuration. + /// + /// Application ID whose inherited configuration hash should be cached. + /// Environment associated with the cached hash. + /// MD5 hash of the merged configuration. + Task AppPublishedConfigsMd5CacheWithInheritance(string appId, string env); + + /// + /// Build the configuration key. + /// + /// Configuration item whose key should be generated. + /// Composite key string. + string GenerateKey(Config config); + + /// + /// Determine whether the configuration has been published. + /// + /// Configuration identifier to check. + /// Environment in which to look for published configuration. + /// True when the configuration is published. + Task IsPublishedAsync(string configId, string env); + + /// + /// Retrieve publish details by the publish timeline identifier. + /// + /// Publish timeline identifier to query. + /// Environment in which the publish timeline exists. + /// List of publish details for the timeline. + Task> GetPublishDetailByPublishTimelineIdAsync(string publishTimelineId, string env); + + /// + /// Retrieve the publish timeline node. + /// + /// Publish timeline identifier. + /// Environment in which the publish timeline exists. + /// Publish timeline entity. + Task GetPublishTimeLineNodeAsync(string publishTimelineId, string env); + + /// + /// Retrieve the publish history for the application. + /// + /// Application ID whose publish history is requested. + /// Environment to query. + /// List of publish timeline entries. + Task> GetPublishTimelineHistoryAsync(string appId, string env); + + /// + /// Retrieve the list of publish details for the application. + /// + /// Application ID whose publish details are requested. + /// Environment to query. + /// List of publish detail records. + Task> GetPublishDetailListAsync(string appId, string env); + + /// + /// Retrieve the publish history for a specific configuration item. + /// + /// Configuration identifier to query. + /// Environment to query. + /// List of publish detail records for the configuration. + Task> GetConfigPublishedHistory(string configId, string env); + + /// + /// Retrieve the currently published configuration items. + /// + /// Application ID whose published configuration is requested. + /// Environment to query. + /// List of published configuration entries. + Task> GetPublishedConfigsAsync(string appId, string env); + + /// + /// Retrieve a single published configuration item. + /// + /// Configuration identifier to query. + /// Environment to query. + /// Published configuration entity. + Task GetPublishedConfigAsync(string configId, string env); + + /// + /// Roll back to the configuration version at the specified publish timeline. + /// + /// Publish timeline identifier to restore. + /// Environment in which to perform the rollback. + /// True when the rollback succeeds. + Task RollbackAsync(string publishTimelineId, string env); + + /// + /// Synchronize configuration to target environments. + /// + /// Application ID whose configuration should be synchronized. + /// Source environment. + /// Target environments. + /// True when synchronization succeeds. + Task EnvSync(string appId, string currentEnv, List toEnvs); + + /// + /// Retrieve the configuration as a list of key/value pairs. + /// + /// Application ID whose configuration is requested. + /// Environment to query. + /// List of key/value pairs representing configuration entries. + Task>> GetKvListAsync(string appId, string env); + + /// + /// Convert the configuration represented as JSON into standard config entries and persist them. + /// Compare with the existing configuration to mark additions, deletions, and updates. + /// + /// JSON string representing the configuration. + /// Application ID. + /// Environment name. + /// Indicates whether to apply patch mode updates. + /// + Task SaveJsonAsync(string json, string appId, string env, bool isPatch); + + /// + /// Persist the key/value configuration list to the database. + /// + /// Serialized key/value pairs. + /// Application ID that owns the configuration. + /// Environment where the configuration should be saved. + /// Indicates whether to apply patch mode updates. + /// True when the key/value list is saved successfully. + Task SaveKvListAsync(string kvString, string appId, string env, bool isPatch); + + /// + /// Validate whether the key/value text is well-formed. + /// + /// Text containing key/value pairs. + /// Tuple indicating whether validation passed and an error message. + (bool, string) ValidateKvString(string kvStr); + + /// + /// clear all cache + /// + void ClearCache(); + + Task GetLastPublishTimelineVirtualIdAsync(string appId, string env); + + Task GetLastPublishTimelineVirtualIdAsyncWithCache(string appId, string env); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IEventHandlerRegister.cs b/src/AgileConfig.Server.IService/IEventHandlerRegister.cs index 6800e321..aeaafef6 100644 --- a/src/AgileConfig.Server.IService/IEventHandlerRegister.cs +++ b/src/AgileConfig.Server.IService/IEventHandlerRegister.cs @@ -1,50 +1,44 @@ -using System; -using System.Collections.Generic; -using System.Text; +namespace AgileConfig.Server.IService; -namespace AgileConfig.Server.IService +public interface IEventHandlerRegister { - public interface IEventHandlerRegister - { - void Register(); - } - - public class EventKeys - { - public const string USER_LOGIN_SUCCESS = "USER_LOGIN_SUCCESS"; - - public const string INIT_SUPERADMIN_PASSWORD_SUCCESS = "INIT_SUPERADMIN_PASSWORD_SUCCESS"; - - public const string ADD_APP_SUCCESS = "ADD_APP_SUCCESS"; - public const string EDIT_APP_SUCCESS = "EDIT_APP_SUCCESS"; - public const string DISABLE_OR_ENABLE_APP_SUCCESS = "DISABLE_OR_ENABLE_APP_SUCCESS"; - public const string DELETE_APP_SUCCESS = "DELETE_APP_SUCCESS"; - - public const string ADD_CONFIG_SUCCESS = "ADD_CONFIG_SUCCESS"; - public const string EDIT_CONFIG_SUCCESS = "EDIT_CONFIG_SUCCESS"; - public const string DELETE_CONFIG_SUCCESS = "DELETE_CONFIG_SUCCESS"; - public const string DELETE_CONFIG_SOME_SUCCESS = "DELETE_CONFIG_SOME_SUCCESS"; - - public const string PUBLISH_CONFIG_SUCCESS = "PUBLISH_CONFIG_SUCCESS"; - public const string OFFLINE_CONFIG_SUCCESS = "OFFLINE_CONFIG_SUCCESS"; - public const string ROLLBACK_CONFIG_SUCCESS = "ROLLBACK_CONFIG_SUCCESS"; - public const string CANCEL_EDIT_CONFIG_SUCCESS = "CANCEL_EDIT_CONFIG_SUCCESS"; - public const string CANCEL_EDIT_CONFIG_SOME_SUCCESS = "CANCEL_EDIT_CONFIG_SOME_SUCCESS"; - - public const string ADD_NODE_SUCCESS = "ADD_NODE_SUCCESS"; - public const string DELETE_NODE_SUCCESS = "DELETE_NODE_SUCCESS"; - - public const string ADD_USER_SUCCESS = "ADD_USER_SUCCESS"; - public const string EDIT_USER_SUCCESS = "EDIT_USER_SUCCESS"; - public const string DELETE_USER_SUCCESS = "DELETE_USER_SUCCESS"; - public const string CHANGE_USER_PASSWORD_SUCCESS = "CHANGE_USER_PASSWORD_SUCCESS"; - public const string RESET_USER_PASSWORD_SUCCESS = "RESET_USER_PASSWORD_SUCCESS"; - - public const string DISCONNECT_CLIENT_SUCCESS = "DISCONNECT_CLIENT_SUCCESS"; - - public const string REGISTER_A_SERVICE = "REGISTER_A_SERVICE"; - public const string UNREGISTER_A_SERVICE = "UNREGISTER_A_SERVICE"; - public const string UPDATE_SERVICE_STATUS = "UPDATE_SERVICE_STATUS"; - - } + void Register(); } + +public class EventKeys +{ + public const string USER_LOGIN_SUCCESS = "USER_LOGIN_SUCCESS"; + + public const string INIT_SUPERADMIN_PASSWORD_SUCCESS = "INIT_SUPERADMIN_PASSWORD_SUCCESS"; + + public const string ADD_APP_SUCCESS = "ADD_APP_SUCCESS"; + public const string EDIT_APP_SUCCESS = "EDIT_APP_SUCCESS"; + public const string DISABLE_OR_ENABLE_APP_SUCCESS = "DISABLE_OR_ENABLE_APP_SUCCESS"; + public const string DELETE_APP_SUCCESS = "DELETE_APP_SUCCESS"; + + public const string ADD_CONFIG_SUCCESS = "ADD_CONFIG_SUCCESS"; + public const string EDIT_CONFIG_SUCCESS = "EDIT_CONFIG_SUCCESS"; + public const string DELETE_CONFIG_SUCCESS = "DELETE_CONFIG_SUCCESS"; + public const string DELETE_CONFIG_SOME_SUCCESS = "DELETE_CONFIG_SOME_SUCCESS"; + + public const string PUBLISH_CONFIG_SUCCESS = "PUBLISH_CONFIG_SUCCESS"; + public const string OFFLINE_CONFIG_SUCCESS = "OFFLINE_CONFIG_SUCCESS"; + public const string ROLLBACK_CONFIG_SUCCESS = "ROLLBACK_CONFIG_SUCCESS"; + public const string CANCEL_EDIT_CONFIG_SUCCESS = "CANCEL_EDIT_CONFIG_SUCCESS"; + public const string CANCEL_EDIT_CONFIG_SOME_SUCCESS = "CANCEL_EDIT_CONFIG_SOME_SUCCESS"; + + public const string ADD_NODE_SUCCESS = "ADD_NODE_SUCCESS"; + public const string DELETE_NODE_SUCCESS = "DELETE_NODE_SUCCESS"; + + public const string ADD_USER_SUCCESS = "ADD_USER_SUCCESS"; + public const string EDIT_USER_SUCCESS = "EDIT_USER_SUCCESS"; + public const string DELETE_USER_SUCCESS = "DELETE_USER_SUCCESS"; + public const string CHANGE_USER_PASSWORD_SUCCESS = "CHANGE_USER_PASSWORD_SUCCESS"; + public const string RESET_USER_PASSWORD_SUCCESS = "RESET_USER_PASSWORD_SUCCESS"; + + public const string DISCONNECT_CLIENT_SUCCESS = "DISCONNECT_CLIENT_SUCCESS"; + + public const string REGISTER_A_SERVICE = "REGISTER_A_SERVICE"; + public const string UNREGISTER_A_SERVICE = "UNREGISTER_A_SERVICE"; + public const string UPDATE_SERVICE_STATUS = "UPDATE_SERVICE_STATUS"; +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IPermissionService.cs b/src/AgileConfig.Server.IService/IPermissionService.cs index a3a57104..f397afba 100644 --- a/src/AgileConfig.Server.IService/IPermissionService.cs +++ b/src/AgileConfig.Server.IService/IPermissionService.cs @@ -1,43 +1,60 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace AgileConfig.Server.IService -{ - public static class Functions - { - public const string App_Add = "APP_ADD"; - public const string App_Edit = "APP_EDIT"; - public const string App_Delete = "APP_DELETE"; - public const string App_Auth = "APP_AUTH"; - - public const string Config_Add = "CONFIG_ADD"; - public const string Config_Edit = "CONFIG_EDIT"; - public const string Config_Delete = "CONFIG_DELETE"; - - public const string Config_Publish = "CONFIG_PUBLISH"; - public const string Config_Offline = "CONFIG_OFFLINE"; - - public const string Node_Add = "NODE_ADD"; - public const string Node_Delete = "NODE_DELETE"; - - public const string Client_Disconnect = "CLIENT_DISCONNECT"; +namespace AgileConfig.Server.IService; - public const string User_Add = "USER_ADD"; - public const string User_Edit = "USER_EDIT"; - public const string User_Delete = "USER_DELETE"; - - public const string Role_Add = "ROLE_ADD"; - public const string Role_Edit = "ROLE_EDIT"; - public const string Role_Delete = "ROLE_DELETE"; - } +public static class Functions +{ + public const string App_Read = "APP_READ"; + public const string App_Add = "APP_ADD"; + public const string App_Edit = "APP_EDIT"; + public const string App_Delete = "APP_DELETE"; + public const string App_Auth = "APP_AUTH"; + + public const string Confing_Read = "CONFIG_READ"; + public const string Config_Add = "CONFIG_ADD"; + public const string Config_Edit = "CONFIG_EDIT"; + public const string Config_Delete = "CONFIG_DELETE"; + + public const string Config_Publish = "CONFIG_PUBLISH"; + public const string Config_Offline = "CONFIG_OFFLINE"; + + public const string Node_Read = "NODE_READ"; + public const string Node_Add = "NODE_ADD"; + public const string Node_Delete = "NODE_DELETE"; + + public const string Client_Refresh = "CLIENT_REFRESH"; + public const string Client_Disconnect = "CLIENT_DISCONNECT"; + + public const string User_Read = "USER_READ"; + public const string User_Add = "USER_ADD"; + public const string User_Edit = "USER_EDIT"; + public const string User_Delete = "USER_DELETE"; + + public const string Role_Read = "ROLE_READ"; + public const string Role_Add = "ROLE_ADD"; + public const string Role_Edit = "ROLE_EDIT"; + public const string Role_Delete = "ROLE_DELETE"; + + public const string Service_Read = "SERVICE_READ"; + public const string Service_Add = "SERVICE_ADD"; + public const string Service_Delete = "SERVICE_DELETE"; + + public const string Log_Read = "LOG_READ"; +} - public interface IPermissionService - { - string EditConfigPermissionKey { get; } +public interface IPermissionService +{ + string EditConfigPermissionKey { get; } - string PublishConfigPermissionKey { get; } + string PublishConfigPermissionKey { get; } - Task> GetUserPermission(string userId); - } + Task> GetUserPermission(string userId); -} + /// + /// Retrieve the categories of permissions granted to a user. + /// + /// Identifier of the user requesting categories. + /// List of distinct categories granted to the user. + Task> GetUserCategories(string userId); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IRegisterCenterService.cs b/src/AgileConfig.Server.IService/IRegisterCenterService.cs index 02c07a99..67cdcf08 100644 --- a/src/AgileConfig.Server.IService/IRegisterCenterService.cs +++ b/src/AgileConfig.Server.IService/IRegisterCenterService.cs @@ -1,16 +1,15 @@ -using AgileConfig.Server.Data.Entity; -using System.Threading.Tasks; +using System.Threading.Tasks; +using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.IService +namespace AgileConfig.Server.IService; + +public interface IRegisterCenterService { - public interface IRegisterCenterService - { - Task RegisterAsync(ServiceInfo serviceInfo); + Task RegisterAsync(ServiceInfo serviceInfo); + + Task UnRegisterAsync(string serviceUniqueId); - Task UnRegisterAsync(string serviceUniqueId); - - Task UnRegisterByServiceIdAsync(string serviceId); + Task UnRegisterByServiceIdAsync(string serviceId); - Task ReceiveHeartbeatAsync(string serviceUniqueId); - } -} + Task ReceiveHeartbeatAsync(string serviceUniqueId); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IRemoteServerNodeProxy.cs b/src/AgileConfig.Server.IService/IRemoteServerNodeProxy.cs index 92bd86b6..dcd16739 100644 --- a/src/AgileConfig.Server.IService/IRemoteServerNodeProxy.cs +++ b/src/AgileConfig.Server.IService/IRemoteServerNodeProxy.cs @@ -1,100 +1,98 @@ -using Agile.Config.Protocol; -using System; +using System; using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; +using Agile.Config.Protocol; -namespace AgileConfig.Server.IService +namespace AgileConfig.Server.IService; + +public class ClientInfo { - public class ClientInfo - { - public string Id { get; set; } - - public string AppId { get; set; } - - public string Address { get; set; } - - public string Tag { get;set; } - - public string Name { get; set; } - - public string Ip { get; set; } - - public string Env { get; set; } - - public DateTime LastHeartbeatTime { get; set; } - - public DateTime? LastRefreshTime { get; set; } - } - - public class ClientInfos - { - public int ClientCount { get; set; } - - public List Infos { get; set; } - } - - public interface IRemoteServerNodeProxy - { - /// - /// Notify all clients on a node to execute an action. - /// - /// Server address that hosts the clients. - /// Action to be executed on each client. - /// True when the command is sent successfully. - Task AllClientsDoActionAsync(string address, WebsocketAction action); - - /// - /// Notify a specific client on a node to execute an action. - /// - /// Server address that hosts the client. - /// Action to execute. - /// Client identifier that should receive the action. - /// True when the command is sent successfully. - Task OneClientDoActionAsync(string address, string clientId, WebsocketAction action); - - /// - /// Notify clients of an application on a node to execute an action. - /// - /// Server address that hosts the clients. - /// Action to execute. - /// Application ID whose clients should receive the action. - /// Environment of the target clients. - /// True when the command is sent successfully. - Task AppClientsDoActionAsync(string address, string appId, string env, WebsocketAction action); - - /// - /// Retrieve the client connection report for a node. - /// - /// Server address to query. - /// Collection of connected clients. - Task GetClientsReportAsync(string address); - - /// - /// Check whether all nodes are online. - /// - /// - Task TestEchoAsync(); - - /// - /// Check whether a specific node is online. - /// - /// Server address to ping. - /// Task that completes when the echo test finishes. - Task TestEchoAsync(string address); - - /// - /// Clear the configuration cache on a node. - /// - /// Server address whose cache should be cleared. - /// Task that completes when the cache is cleared. - Task ClearConfigServiceCache(string address); - - /// - /// Clear the service registration cache on a node. - /// - /// Server address whose cache should be cleared. - /// Task that completes when the cache is cleared. - Task ClearServiceInfoCache(string address); - } + public string Id { get; set; } + + public string AppId { get; set; } + + public string Address { get; set; } + + public string Tag { get; set; } + + public string Name { get; set; } + + public string Ip { get; set; } + + public string Env { get; set; } + + public DateTime LastHeartbeatTime { get; set; } + + public DateTime? LastRefreshTime { get; set; } } + +public class ClientInfos +{ + public int ClientCount { get; set; } + + public List Infos { get; set; } +} + +public interface IRemoteServerNodeProxy +{ + /// + /// Notify all clients on a node to execute an action. + /// + /// Server address that hosts the clients. + /// Action to be executed on each client. + /// True when the command is sent successfully. + Task AllClientsDoActionAsync(string address, WebsocketAction action); + + /// + /// Notify a specific client on a node to execute an action. + /// + /// Server address that hosts the client. + /// Action to execute. + /// Client identifier that should receive the action. + /// True when the command is sent successfully. + Task OneClientDoActionAsync(string address, string clientId, WebsocketAction action); + + /// + /// Notify clients of an application on a node to execute an action. + /// + /// Server address that hosts the clients. + /// Action to execute. + /// Application ID whose clients should receive the action. + /// Environment of the target clients. + /// True when the command is sent successfully. + Task AppClientsDoActionAsync(string address, string appId, string env, WebsocketAction action); + + /// + /// Retrieve the client connection report for a node. + /// + /// Server address to query. + /// Collection of connected clients. + Task GetClientsReportAsync(string address); + + /// + /// Check whether all nodes are online. + /// + /// + Task TestEchoAsync(); + + /// + /// Check whether a specific node is online. + /// + /// Server address to ping. + /// Task that completes when the echo test finishes. + Task TestEchoAsync(string address); + + /// + /// Clear the configuration cache on a node. + /// + /// Server address whose cache should be cleared. + /// Task that completes when the cache is cleared. + Task ClearConfigServiceCache(string address); + + /// + /// Clear the service registration cache on a node. + /// + /// Server address whose cache should be cleared. + /// Task that completes when the cache is cleared. + Task ClearServiceInfoCache(string address); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IRoleService.cs b/src/AgileConfig.Server.IService/IRoleService.cs index d5abaea7..2eacb1bb 100644 --- a/src/AgileConfig.Server.IService/IRoleService.cs +++ b/src/AgileConfig.Server.IService/IRoleService.cs @@ -1,16 +1,14 @@ -using AgileConfig.Server.Data.Entity; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.IService +namespace AgileConfig.Server.IService; + +public interface IRoleService { - public interface IRoleService - { - Task> GetAllAsync(); - Task GetAsync(string id); - Task GetByCodeAsync(string code); - Task CreateAsync(Role role, IEnumerable functions); - Task UpdateAsync(Role role, IEnumerable functions); - Task DeleteAsync(string id); - } -} + Task> GetAllAsync(); + Task GetAsync(string id); + Task CreateAsync(Role role, IEnumerable functions); + Task UpdateAsync(Role role, IEnumerable functions); + Task DeleteAsync(string id); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IServerNodeService.cs b/src/AgileConfig.Server.IService/IServerNodeService.cs index d8ee3cea..f80e5578 100644 --- a/src/AgileConfig.Server.IService/IServerNodeService.cs +++ b/src/AgileConfig.Server.IService/IServerNodeService.cs @@ -1,30 +1,29 @@ -using AgileConfig.Server.Data.Entity; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.IService +namespace AgileConfig.Server.IService; + +public interface IServerNodeService : IDisposable { - public interface IServerNodeService: IDisposable - { - Task GetAsync(string id); - Task AddAsync(ServerNode node); + Task GetAsync(string id); + Task AddAsync(ServerNode node); + + Task JoinAsync(string ip, int port, string desc); - Task JoinAsync(string ip, int port, string desc); + Task DeleteAsync(ServerNode node); - Task DeleteAsync(ServerNode node); + Task DeleteAsync(string nodeId); - Task DeleteAsync(string nodeId); + Task UpdateAsync(ServerNode node); - Task UpdateAsync(ServerNode node); + Task> GetAllNodesAsync(); - Task> GetAllNodesAsync(); - - /// - /// Initialize server nodes from the nodes section in appsettings. - /// - /// - [Obsolete] - Task InitWatchNodeAsync(); - } -} + /// + /// Initialize server nodes from the nodes section in appsettings. + /// + /// + [Obsolete] + Task InitWatchNodeAsync(); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IServiceHealthCheckService.cs b/src/AgileConfig.Server.IService/IServiceHealthCheckService.cs index f472da94..34ce0662 100644 --- a/src/AgileConfig.Server.IService/IServiceHealthCheckService.cs +++ b/src/AgileConfig.Server.IService/IServiceHealthCheckService.cs @@ -1,14 +1,8 @@ -using AgileConfig.Server.Data.Entity; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Threading.Tasks; -namespace AgileConfig.Server.IService +namespace AgileConfig.Server.IService; + +public interface IServiceHealthCheckService { - public interface IServiceHealthCheckService - { - Task StartCheckAsync(); - } -} + Task StartCheckAsync(); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IServiceInfoService.cs b/src/AgileConfig.Server.IService/IServiceInfoService.cs index 5712b4f8..6af63dde 100644 --- a/src/AgileConfig.Server.IService/IServiceInfoService.cs +++ b/src/AgileConfig.Server.IService/IServiceInfoService.cs @@ -1,31 +1,30 @@ -using AgileConfig.Server.Data.Entity; -using System; +using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Threading.Tasks; +using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.IService +namespace AgileConfig.Server.IService; + +public interface IServiceInfoService : IDisposable { - public interface IServiceInfoService: IDisposable - { - Task GetByUniqueIdAsync(string id); - Task GetByServiceIdAsync(string serviceId); + Task GetByUniqueIdAsync(string id); + Task GetByServiceIdAsync(string serviceId); + + Task RemoveAsync(string id); + + Task UpdateServiceStatus(ServiceInfo service, ServiceStatus status); + Task> GetAllServiceInfoAsync(); - Task RemoveAsync(string id); - - Task UpdateServiceStatus(ServiceInfo service, ServiceStatus status); - Task> GetAllServiceInfoAsync(); - - Task> GetOnlineServiceInfoAsync(); + Task> GetOnlineServiceInfoAsync(); - Task> GetOfflineServiceInfoAsync(); + Task> GetOfflineServiceInfoAsync(); - Task ServicesMD5(); + Task ServicesMD5(); - Task ServicesMD5Cache(); + Task ServicesMD5Cache(); - Task> QueryAsync(Expression> exp); + Task> QueryAsync(Expression> exp); - void ClearCache(); - } -} + void ClearCache(); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/ISettingService.cs b/src/AgileConfig.Server.IService/ISettingService.cs index 6c95a26e..c8e6ff2d 100644 --- a/src/AgileConfig.Server.IService/ISettingService.cs +++ b/src/AgileConfig.Server.IService/ISettingService.cs @@ -1,50 +1,44 @@ -using AgileConfig.Server.Data.Entity; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.IService +namespace AgileConfig.Server.IService; + +public interface ISettingService : IDisposable { - public interface ISettingService : IDisposable - { - static string[] EnvironmentList { get; set; } + static string[] EnvironmentList { get; set; } - static string IfEnvEmptySetDefault(ref string env) - { - if (!string.IsNullOrEmpty(env)) - { - return env; - } + static string IfEnvEmptySetDefault(ref string env) + { + if (!string.IsNullOrEmpty(env)) return env; - if (EnvironmentList == null || EnvironmentList.Length == 0) - { - throw new ArgumentNullException(nameof(EnvironmentList)); - } + if (EnvironmentList == null || EnvironmentList.Length == 0) + throw new ArgumentNullException(nameof(EnvironmentList)); - env = EnvironmentList[0]; + env = EnvironmentList[0]; - return env; - } + return env; + } - Task GetAsync(string id); - Task AddAsync(Setting setting); + Task GetAsync(string id); + Task AddAsync(Setting setting); - Task DeleteAsync(Setting setting); + Task DeleteAsync(Setting setting); - Task DeleteAsync(string settingId); + Task DeleteAsync(string settingId); - Task UpdateAsync(Setting setting); + Task UpdateAsync(Setting setting); - /// - /// Retrieve all settings. - /// - /// - Task> GetAllSettingsAsync(); + /// + /// Retrieve all settings. + /// + /// + Task> GetAllSettingsAsync(); - /// - /// Retrieve the configured environment list. - /// - /// - Task GetEnvironmentList(); - } -} + /// + /// Retrieve the configured environment list. + /// + /// + Task GetEnvironmentList(); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/ISysLogService.cs b/src/AgileConfig.Server.IService/ISysLogService.cs index c26d945b..049b5ffc 100644 --- a/src/AgileConfig.Server.IService/ISysLogService.cs +++ b/src/AgileConfig.Server.IService/ISysLogService.cs @@ -1,20 +1,19 @@ -using AgileConfig.Server.Data.Entity; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.IService -{ - public interface ISysLogService: IDisposable - { - Task AddSysLogAsync(SysLog log); +namespace AgileConfig.Server.IService; - Task AddRangeAsync(IEnumerable logs); +public interface ISysLogService : IDisposable +{ + Task AddSysLogAsync(SysLog log); + Task AddRangeAsync(IEnumerable logs); - Task> SearchPage(string appId, SysLogType? logType,DateTime? startTime, DateTime? endTime, int pageSize, int pageIndex); - Task Count(string appId, SysLogType? logType, DateTime? startTime, DateTime? endTime); + Task> SearchPage(string appId, SysLogType? logType, DateTime? startTime, DateTime? endTime, + int pageSize, int pageIndex); - } -} + Task Count(string appId, SysLogType? logType, DateTime? startTime, DateTime? endTime); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/ISystemInitializationService.cs b/src/AgileConfig.Server.IService/ISystemInitializationService.cs index b09b4013..0e40e756 100644 --- a/src/AgileConfig.Server.IService/ISystemInitializationService.cs +++ b/src/AgileConfig.Server.IService/ISystemInitializationService.cs @@ -13,4 +13,6 @@ public interface ISystemInitializationService bool TryInitJwtSecret(); bool TryInitDefaultEnvironment(); + + Task TryInitSuperAdminRole(); } \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IUserService.cs b/src/AgileConfig.Server.IService/IUserService.cs index 29ed7639..e44bc592 100644 --- a/src/AgileConfig.Server.IService/IUserService.cs +++ b/src/AgileConfig.Server.IService/IUserService.cs @@ -1,32 +1,29 @@ -using AgileConfig.Server.Data.Entity; -using System; +using System; using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; +using AgileConfig.Server.Data.Entity; -namespace AgileConfig.Server.IService -{ - public interface IUserService: IDisposable - { - Task> GetAll(); - Task GetUserAsync(string userId); +namespace AgileConfig.Server.IService; - Task> GetUsersByNameAsync(string userName); - Task> GetUserRolesAsync(string userId); +public interface IUserService : IDisposable +{ + Task> GetAll(); + Task GetUserAsync(string userId); - Task AddAsync(User user); + Task> GetUsersByNameAsync(string userName); + Task> GetUserRolesAsync(string userId); - Task DeleteAsync(User user); + Task AddAsync(User user); - Task UpdateAsync(User user); + Task DeleteAsync(User user); + Task UpdateAsync(User user); - Task UpdateUserRolesAsync(string userId, List roleIds); + Task UpdateUserRolesAsync(string userId, List roleIds); - Task ValidateUserPassword(string userName, string password); - Task> GetUsersByRoleAsync(string roleId); + Task ValidateUserPassword(string userName, string password); - } -} + Task> GetUsersByRoleAsync(string roleId); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.OIDC/IOidcClient.cs b/src/AgileConfig.Server.OIDC/IOidcClient.cs index 2afbbc29..d80ee52c 100644 --- a/src/AgileConfig.Server.OIDC/IOidcClient.cs +++ b/src/AgileConfig.Server.OIDC/IOidcClient.cs @@ -1,10 +1,9 @@ -namespace AgileConfig.Server.OIDC +namespace AgileConfig.Server.OIDC; + +public interface IOidcClient { - public interface IOidcClient - { - OidcSetting OidcSetting { get; } - string GetAuthorizeUrl(); - (string Id, string UserName) UnboxIdToken(string idToken); - Task<(string IdToken, string accessToken)> Validate(string code); - } + OidcSetting OidcSetting { get; } + string GetAuthorizeUrl(); + (string Id, string UserName) UnboxIdToken(string idToken); + Task<(string IdToken, string accessToken)> Validate(string code); } \ No newline at end of file diff --git a/src/AgileConfig.Server.OIDC/OidcClient.cs b/src/AgileConfig.Server.OIDC/OidcClient.cs index 73e6d0b9..8cebc63e 100644 --- a/src/AgileConfig.Server.OIDC/OidcClient.cs +++ b/src/AgileConfig.Server.OIDC/OidcClient.cs @@ -1,83 +1,75 @@ -using AgileConfig.Server.Common; +using System.IdentityModel.Tokens.Jwt; +using System.Net.Http.Headers; +using AgileConfig.Server.Common; using AgileConfig.Server.OIDC.SettingProvider; using AgileConfig.Server.OIDC.TokenEndpointAuthMethods; using Newtonsoft.Json; -using System.IdentityModel.Tokens.Jwt; -using System.Net.Http.Headers; -namespace AgileConfig.Server.OIDC +namespace AgileConfig.Server.OIDC; + +public class OidcClient : IOidcClient { - public class OidcClient : IOidcClient + private readonly IHttpClientFactory _httpClientFactory; + private readonly IOidcSettingProvider _oidcSettingProvider; + + public OidcClient(IOidcSettingProvider oidcSettingProvider, IHttpClientFactory httpClientFactory) + { + _httpClientFactory = httpClientFactory; + _oidcSettingProvider = oidcSettingProvider; + OidcSetting = _oidcSettingProvider.GetSetting(); + } + + public OidcSetting OidcSetting { get; } + + public string GetAuthorizeUrl() + { + var url = $"{OidcSetting.AuthorizationEndpoint}?" + + $"client_id={OidcSetting.ClientId}" + + $"&response_type=code" + + $"&scope={OidcSetting.Scope}" + + $"&redirect_uri={OidcSetting.RedirectUri}" + + $"&state={Guid.NewGuid()}"; + return url; + } + + public async Task<(string IdToken, string accessToken)> Validate(string code) { - private readonly IOidcSettingProvider _oidcSettingProvider; - private readonly OidcSetting _oidcSetting; - private readonly IHttpClientFactory _httpClientFactory; - - public OidcSetting OidcSetting => _oidcSetting; - - public OidcClient(IOidcSettingProvider oidcSettingProvider, IHttpClientFactory httpClientFactory) - { - _httpClientFactory = httpClientFactory; - _oidcSettingProvider = oidcSettingProvider; - _oidcSetting = _oidcSettingProvider.GetSetting(); - } - - public string GetAuthorizeUrl() - { - var url = $"{_oidcSetting.AuthorizationEndpoint}?" + - $"client_id={_oidcSetting.ClientId}" + - $"&response_type=code" + - $"&scope={_oidcSetting.Scope}" + - $"&redirect_uri={_oidcSetting.RedirectUri}" + - $"&state={Guid.NewGuid()}"; - return url; - } - - public async Task<(string IdToken, string accessToken)> Validate(string code) - { - var authMethod = TokenEndpointAuthMethodFactory.Create(_oidcSetting.TokenEndpointAuthMethod); - var httpContent = authMethod.GetAuthHttpContent(code, _oidcSetting); - - - var httpclient = _httpClientFactory.CreateClient(Global.DefaultHttpClientName); - if (!string.IsNullOrEmpty(httpContent.BasicAuthorizationString)) - { - httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", httpContent.BasicAuthorizationString); - } - var response = await httpclient.PostAsync(_oidcSetting.TokenEndpoint, httpContent.HttpContent); - response.EnsureSuccessStatusCode(); - - var bodyJson = await response.Content.ReadAsStringAsync(); - if (string.IsNullOrWhiteSpace(bodyJson)) - { - throw new Exception("Can not validate the code. Token endpoint return empty response."); - } - - var responseObject = JsonConvert.DeserializeObject(bodyJson); - string id_token = responseObject.id_token; - - if (string.IsNullOrWhiteSpace(id_token)) - { - throw new Exception("Can not validate the code. Id token missing."); - } - - return (id_token, ""); - } - - public (string Id, string UserName) UnboxIdToken(string idToken) - { - var jwtToken = new JwtSecurityTokenHandler().ReadJwtToken(idToken); - var id = jwtToken.Claims.Where(x => x.Type == _oidcSetting.UserIdClaim).FirstOrDefault()?.Value; - var userName = jwtToken.Claims.Where(x => x.Type == _oidcSetting.UserNameClaim).FirstOrDefault()?.Value; - - return (id, userName); - } + var authMethod = TokenEndpointAuthMethodFactory.Create(OidcSetting.TokenEndpointAuthMethod); + var httpContent = authMethod.GetAuthHttpContent(code, OidcSetting); + + + var httpclient = _httpClientFactory.CreateClient(Global.DefaultHttpClientName); + if (!string.IsNullOrEmpty(httpContent.BasicAuthorizationString)) + httpclient.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", httpContent.BasicAuthorizationString); + var response = await httpclient.PostAsync(OidcSetting.TokenEndpoint, httpContent.HttpContent); + response.EnsureSuccessStatusCode(); + + var bodyJson = await response.Content.ReadAsStringAsync(); + if (string.IsNullOrWhiteSpace(bodyJson)) + throw new Exception("Can not validate the code. Token endpoint return empty response."); + + var responseObject = JsonConvert.DeserializeObject(bodyJson); + var id_token = responseObject.id_token; + + if (string.IsNullOrWhiteSpace(id_token)) throw new Exception("Can not validate the code. Id token missing."); + + return (id_token, ""); } - internal class TokenEndpointResponseModel + public (string Id, string UserName) UnboxIdToken(string idToken) { - public string id_token { get; set; } + var jwtToken = new JwtSecurityTokenHandler().ReadJwtToken(idToken); + var id = jwtToken.Claims.Where(x => x.Type == OidcSetting.UserIdClaim).FirstOrDefault()?.Value; + var userName = jwtToken.Claims.Where(x => x.Type == OidcSetting.UserNameClaim).FirstOrDefault()?.Value; - public string access_token { get; set; } + return (id, userName); } +} + +internal class TokenEndpointResponseModel +{ + public string id_token { get; set; } + + public string access_token { get; set; } } \ No newline at end of file diff --git a/src/AgileConfig.Server.OIDC/OidcSetting.cs b/src/AgileConfig.Server.OIDC/OidcSetting.cs index f9ee165b..f79f1079 100644 --- a/src/AgileConfig.Server.OIDC/OidcSetting.cs +++ b/src/AgileConfig.Server.OIDC/OidcSetting.cs @@ -1,38 +1,37 @@ -namespace AgileConfig.Server.OIDC +namespace AgileConfig.Server.OIDC; + +public class OidcSetting { - public class OidcSetting + public OidcSetting( + string clientId, + string clientSecret, + string redirectUri, + string tokenEndpoint, + string authorizationEndpoint, + string userIdClaim, + string userNameClaim, + string scope, + string tokenEndpointAuthMethod + ) { - public OidcSetting( - string clientId, - string clientSecret, - string redirectUri, - string tokenEndpoint, - string authorizationEndpoint, - string userIdClaim, - string userNameClaim, - string scope, - string tokenEndpointAuthMethod - ) - { - ClientId = clientId; - ClientSecret = clientSecret; - RedirectUri = redirectUri; - TokenEndpoint = tokenEndpoint; - AuthorizationEndpoint = authorizationEndpoint; - UserIdClaim = userIdClaim; - UserNameClaim = userNameClaim; - Scope = scope; - TokenEndpointAuthMethod = tokenEndpointAuthMethod; - } - - public string ClientId { get; } - public string ClientSecret { get; } - public string RedirectUri { get; } - public string TokenEndpoint { get; } - public string AuthorizationEndpoint { get; } - public string UserIdClaim { get; } - public string UserNameClaim { get; } - public string Scope { get; } - public string TokenEndpointAuthMethod { get; set; } + ClientId = clientId; + ClientSecret = clientSecret; + RedirectUri = redirectUri; + TokenEndpoint = tokenEndpoint; + AuthorizationEndpoint = authorizationEndpoint; + UserIdClaim = userIdClaim; + UserNameClaim = userNameClaim; + Scope = scope; + TokenEndpointAuthMethod = tokenEndpointAuthMethod; } -} + + public string ClientId { get; } + public string ClientSecret { get; } + public string RedirectUri { get; } + public string TokenEndpoint { get; } + public string AuthorizationEndpoint { get; } + public string UserIdClaim { get; } + public string UserNameClaim { get; } + public string Scope { get; } + public string TokenEndpointAuthMethod { get; set; } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.OIDC/ServiceCollectionExt.cs b/src/AgileConfig.Server.OIDC/ServiceCollectionExt.cs index d38fd6a4..56e7c367 100644 --- a/src/AgileConfig.Server.OIDC/ServiceCollectionExt.cs +++ b/src/AgileConfig.Server.OIDC/ServiceCollectionExt.cs @@ -1,15 +1,13 @@ using AgileConfig.Server.OIDC.SettingProvider; using Microsoft.Extensions.DependencyInjection; -namespace AgileConfig.Server.OIDC +namespace AgileConfig.Server.OIDC; + +public static class ServiceCollectionExt { - public static class ServiceCollectionExt + public static void AddOIDC(this IServiceCollection sc) { - public static void AddOIDC(this IServiceCollection sc) - { - sc.AddSingleton(); - sc.AddSingleton(); - } - + sc.AddSingleton(); + sc.AddSingleton(); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.OIDC/SettingProvider/ConfigfileOidcSettingProvider.cs b/src/AgileConfig.Server.OIDC/SettingProvider/ConfigfileOidcSettingProvider.cs index 1ce07df2..876be39d 100644 --- a/src/AgileConfig.Server.OIDC/SettingProvider/ConfigfileOidcSettingProvider.cs +++ b/src/AgileConfig.Server.OIDC/SettingProvider/ConfigfileOidcSettingProvider.cs @@ -1,48 +1,47 @@ using AgileConfig.Server.Common; using Microsoft.Extensions.Logging; -namespace AgileConfig.Server.OIDC.SettingProvider +namespace AgileConfig.Server.OIDC.SettingProvider; + +public class ConfigfileOidcSettingProvider : IOidcSettingProvider { - public class ConfigfileOidcSettingProvider : IOidcSettingProvider - { - private readonly OidcSetting _oidcSetting; + private readonly OidcSetting _oidcSetting; - public ConfigfileOidcSettingProvider(ILogger logger) - { - var clientId = Global.Config["SSO:OIDC:clientId"]; - var clientSecret = Global.Config["SSO:OIDC:clientSecret"]; - var tokenEndpoint = Global.Config["SSO:OIDC:tokenEndpoint"]; - var authorizationEndpoint = Global.Config["SSO:OIDC:authorizationEndpoint"]; - var redirectUri = Global.Config["SSO:OIDC:redirectUri"]; - var userIdClaim = Global.Config["SSO:OIDC:userIdClaim"]; - var userNameClaim = Global.Config["SSO:OIDC:userNameClaim"]; - var scope = Global.Config["SSO:OIDC:scope"]; - var tokenEndpointAuthMethod = Global.Config["SSO:OIDC:tokenEndpointAuthMethod"]; - var loginButtonText = Global.Config["SSO:loginButtonText"]; + public ConfigfileOidcSettingProvider(ILogger logger) + { + var clientId = Global.Config["SSO:OIDC:clientId"]; + var clientSecret = Global.Config["SSO:OIDC:clientSecret"]; + var tokenEndpoint = Global.Config["SSO:OIDC:tokenEndpoint"]; + var authorizationEndpoint = Global.Config["SSO:OIDC:authorizationEndpoint"]; + var redirectUri = Global.Config["SSO:OIDC:redirectUri"]; + var userIdClaim = Global.Config["SSO:OIDC:userIdClaim"]; + var userNameClaim = Global.Config["SSO:OIDC:userNameClaim"]; + var scope = Global.Config["SSO:OIDC:scope"]; + var tokenEndpointAuthMethod = Global.Config["SSO:OIDC:tokenEndpointAuthMethod"]; + var loginButtonText = Global.Config["SSO:loginButtonText"]; - _oidcSetting = new OidcSetting( - clientId, - clientSecret, redirectUri, - tokenEndpoint, authorizationEndpoint, - userIdClaim, userNameClaim, - scope, tokenEndpointAuthMethod); + _oidcSetting = new OidcSetting( + clientId, + clientSecret, redirectUri, + tokenEndpoint, authorizationEndpoint, + userIdClaim, userNameClaim, + scope, tokenEndpointAuthMethod); - logger.LogInformation($"OIDC Setting " + - $"clientId:{clientId} " + - $"redirectUri:{redirectUri} " + - $"tokenEndpoint:{tokenEndpoint} " + - $"authorizationEndpoint:{authorizationEndpoint} " + - $"userIdClaim:{userIdClaim} " + - $"userNameClaim:{userNameClaim} " + - $"scope:{scope} " + - $"loginButtonText:{loginButtonText} " + - $"tokenEndpointAuthMethod:{tokenEndpointAuthMethod} " - ); - } + logger.LogInformation($"OIDC Setting " + + $"clientId:{clientId} " + + $"redirectUri:{redirectUri} " + + $"tokenEndpoint:{tokenEndpoint} " + + $"authorizationEndpoint:{authorizationEndpoint} " + + $"userIdClaim:{userIdClaim} " + + $"userNameClaim:{userNameClaim} " + + $"scope:{scope} " + + $"loginButtonText:{loginButtonText} " + + $"tokenEndpointAuthMethod:{tokenEndpointAuthMethod} " + ); + } - public OidcSetting GetSetting() - { - return _oidcSetting; - } + public OidcSetting GetSetting() + { + return _oidcSetting; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.OIDC/SettingProvider/IOidcSettingProvider.cs b/src/AgileConfig.Server.OIDC/SettingProvider/IOidcSettingProvider.cs index 2b0dc230..78b2e2a4 100644 --- a/src/AgileConfig.Server.OIDC/SettingProvider/IOidcSettingProvider.cs +++ b/src/AgileConfig.Server.OIDC/SettingProvider/IOidcSettingProvider.cs @@ -1,7 +1,6 @@ -namespace AgileConfig.Server.OIDC.SettingProvider +namespace AgileConfig.Server.OIDC.SettingProvider; + +public interface IOidcSettingProvider { - public interface IOidcSettingProvider - { - OidcSetting GetSetting(); - } -} + OidcSetting GetSetting(); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/BasicTokenEndpointAuthMethod.cs b/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/BasicTokenEndpointAuthMethod.cs index 6b01302b..1dbee182 100644 --- a/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/BasicTokenEndpointAuthMethod.cs +++ b/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/BasicTokenEndpointAuthMethod.cs @@ -1,23 +1,23 @@ -using System.Net.Http.Headers; -using System.Text; +using System.Text; -namespace AgileConfig.Server.OIDC.TokenEndpointAuthMethods +namespace AgileConfig.Server.OIDC.TokenEndpointAuthMethods; + +internal class BasicTokenEndpointAuthMethod : ITokenEndpointAuthMethod { - internal class BasicTokenEndpointAuthMethod : ITokenEndpointAuthMethod + public (HttpContent HttpContent, string BasicAuthorizationString) GetAuthHttpContent(string code, + OidcSetting oidcSetting) { - public (HttpContent HttpContent, string BasicAuthorizationString) GetAuthHttpContent(string code, OidcSetting oidcSetting) + var kvs = new List> { - var kvs = new List>() { - new KeyValuePair("code", code), - new KeyValuePair("grant_type", "authorization_code"), - new KeyValuePair("redirect_uri", oidcSetting.RedirectUri) - }; - var httpContent = new FormUrlEncodedContent(kvs); + new("code", code), + new("grant_type", "authorization_code"), + new("redirect_uri", oidcSetting.RedirectUri) + }; + var httpContent = new FormUrlEncodedContent(kvs); - var txt = $"{oidcSetting.ClientId}:{oidcSetting.ClientSecret}"; - string authorizationString = Convert.ToBase64String(Encoding.UTF8.GetBytes(txt)); + var txt = $"{oidcSetting.ClientId}:{oidcSetting.ClientSecret}"; + var authorizationString = Convert.ToBase64String(Encoding.UTF8.GetBytes(txt)); - return (httpContent, authorizationString); - } + return (httpContent, authorizationString); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/ITokenEndpointAuthMethod.cs b/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/ITokenEndpointAuthMethod.cs index 35e604ac..e764f32e 100644 --- a/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/ITokenEndpointAuthMethod.cs +++ b/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/ITokenEndpointAuthMethod.cs @@ -1,7 +1,6 @@ -namespace AgileConfig.Server.OIDC.TokenEndpointAuthMethods +namespace AgileConfig.Server.OIDC.TokenEndpointAuthMethods; + +internal interface ITokenEndpointAuthMethod { - internal interface ITokenEndpointAuthMethod - { - (HttpContent HttpContent, string BasicAuthorizationString) GetAuthHttpContent(string code, OidcSetting oidcSetting); - } -} + (HttpContent HttpContent, string BasicAuthorizationString) GetAuthHttpContent(string code, OidcSetting oidcSetting); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/NoneTokenEndpointAuthMethod.cs b/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/NoneTokenEndpointAuthMethod.cs index 695e8e11..56170bd0 100644 --- a/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/NoneTokenEndpointAuthMethod.cs +++ b/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/NoneTokenEndpointAuthMethod.cs @@ -1,19 +1,18 @@ -using System.Text; +namespace AgileConfig.Server.OIDC.TokenEndpointAuthMethods; -namespace AgileConfig.Server.OIDC.TokenEndpointAuthMethods +internal class NoneTokenEndpointAuthMethod : ITokenEndpointAuthMethod { - internal class NoneTokenEndpointAuthMethod : ITokenEndpointAuthMethod + public (HttpContent HttpContent, string BasicAuthorizationString) GetAuthHttpContent(string code, + OidcSetting oidcSetting) { - public (HttpContent HttpContent, string BasicAuthorizationString) GetAuthHttpContent(string code, OidcSetting oidcSetting) + var kvs = new List> { - var kvs = new List>() { - new KeyValuePair("code", code), - new KeyValuePair("grant_type", "authorization_code"), - new KeyValuePair("redirect_uri", oidcSetting.RedirectUri) - }; - var httpContent = new FormUrlEncodedContent(kvs); + new("code", code), + new("grant_type", "authorization_code"), + new("redirect_uri", oidcSetting.RedirectUri) + }; + var httpContent = new FormUrlEncodedContent(kvs); - return (httpContent, ""); - } + return (httpContent, ""); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/PostTokenEndpointAuthMethod.cs b/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/PostTokenEndpointAuthMethod.cs index 432d12fb..b6db3be2 100644 --- a/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/PostTokenEndpointAuthMethod.cs +++ b/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/PostTokenEndpointAuthMethod.cs @@ -1,19 +1,20 @@ -namespace AgileConfig.Server.OIDC.TokenEndpointAuthMethods +namespace AgileConfig.Server.OIDC.TokenEndpointAuthMethods; + +internal class PostTokenEndpointAuthMethod : ITokenEndpointAuthMethod { - internal class PostTokenEndpointAuthMethod : ITokenEndpointAuthMethod + public (HttpContent HttpContent, string BasicAuthorizationString) GetAuthHttpContent(string code, + OidcSetting oidcSetting) { - public (HttpContent HttpContent, string BasicAuthorizationString) GetAuthHttpContent(string code, OidcSetting oidcSetting) + var kvs = new List> { - var kvs = new List>() { - new KeyValuePair("code", code), - new KeyValuePair("grant_type", "authorization_code"), - new KeyValuePair("redirect_uri", oidcSetting.RedirectUri), - new KeyValuePair("client_id", oidcSetting.ClientId), - new KeyValuePair("client_secret", oidcSetting.ClientSecret), - }; - var httpContent = new FormUrlEncodedContent(kvs); + new("code", code), + new("grant_type", "authorization_code"), + new("redirect_uri", oidcSetting.RedirectUri), + new("client_id", oidcSetting.ClientId), + new("client_secret", oidcSetting.ClientSecret) + }; + var httpContent = new FormUrlEncodedContent(kvs); - return (httpContent, ""); - } + return (httpContent, ""); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/TokenEndpointAuthMethodFactory.cs b/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/TokenEndpointAuthMethodFactory.cs index 979400ac..04c365d2 100644 --- a/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/TokenEndpointAuthMethodFactory.cs +++ b/src/AgileConfig.Server.OIDC/TokenEndpointAuthMethods/TokenEndpointAuthMethodFactory.cs @@ -1,25 +1,15 @@ -namespace AgileConfig.Server.OIDC.TokenEndpointAuthMethods +namespace AgileConfig.Server.OIDC.TokenEndpointAuthMethods; + +internal static class TokenEndpointAuthMethodFactory { - internal static class TokenEndpointAuthMethodFactory + public static ITokenEndpointAuthMethod Create(string methodName) { - public static ITokenEndpointAuthMethod Create(string methodName) - { - if (methodName == "client_secret_basic") - { - return new BasicTokenEndpointAuthMethod(); - } - else if (methodName == "client_secret_post") - { - return new PostTokenEndpointAuthMethod(); - } - else if (methodName == "none") - { - return new NoneTokenEndpointAuthMethod(); - } - else - { - throw new NotImplementedException(); - } - } + if (methodName == "client_secret_basic") return new BasicTokenEndpointAuthMethod(); + + if (methodName == "client_secret_post") return new PostTokenEndpointAuthMethod(); + + if (methodName == "none") return new NoneTokenEndpointAuthMethod(); + + throw new NotImplementedException(); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/AdmBasicAuthService.cs b/src/AgileConfig.Server.Service/AdmBasicAuthService.cs index 997a3a19..01185fd5 100644 --- a/src/AgileConfig.Server.Service/AdmBasicAuthService.cs +++ b/src/AgileConfig.Server.Service/AdmBasicAuthService.cs @@ -1,39 +1,36 @@ -using AgileConfig.Server.Common; +using System.Threading.Tasks; +using AgileConfig.Server.Common; using AgileConfig.Server.IService; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -using System; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace AgileConfig.Server.Service +namespace AgileConfig.Server.Service; + +public class AdmBasicAuthService : IAdmBasicAuthService { - public class AdmBasicAuthService : IAdmBasicAuthService + private readonly ILogger _logger; + private readonly IUserService _userService; + + public AdmBasicAuthService(IUserService userService, ILoggerFactory lf) { - private readonly IUserService _userService; - private readonly ILogger _logger; - public AdmBasicAuthService(IUserService userService, ILoggerFactory lf) - { - _logger = lf.CreateLogger(); - _userService = userService; - } + _logger = lf.CreateLogger(); + _userService = userService; + } - public (string, string) GetUserNamePassword(HttpRequest httpRequest) - { - return httpRequest.GetUserNamePasswordFromBasicAuthorization(); - } + public (string, string) GetUserNamePassword(HttpRequest httpRequest) + { + return httpRequest.GetUserNamePasswordFromBasicAuthorization(); + } - public Task ValidAsync(HttpRequest httpRequest) + public Task ValidAsync(HttpRequest httpRequest) + { + var userPassword = httpRequest.GetUserNamePasswordFromBasicAuthorization(); + if (string.IsNullOrEmpty(userPassword.Item1) || string.IsNullOrEmpty(userPassword.Item2)) { - var userPassword = httpRequest.GetUserNamePasswordFromBasicAuthorization(); - if (string.IsNullOrEmpty(userPassword.Item1) || string.IsNullOrEmpty(userPassword.Item2)) - { - _logger.LogWarning("Basic auth header have no username or password ."); - return Task.FromResult(false); - } - - return _userService.ValidateUserPassword(userPassword.Item1, userPassword.Item2); + _logger.LogWarning("Basic auth header have no username or password ."); + return Task.FromResult(false); } + + return _userService.ValidateUserPassword(userPassword.Item1, userPassword.Item2); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/AppBasicAuthService.cs b/src/AgileConfig.Server.Service/AppBasicAuthService.cs index 3dc7c0ae..d544f7d0 100644 --- a/src/AgileConfig.Server.Service/AppBasicAuthService.cs +++ b/src/AgileConfig.Server.Service/AppBasicAuthService.cs @@ -1,100 +1,83 @@ -using AgileConfig.Server.IService; -using Microsoft.AspNetCore.Http; -using System; +using System; using System.Linq; using System.Text; using System.Threading.Tasks; +using AgileConfig.Server.IService; +using Microsoft.AspNetCore.Http; -namespace AgileConfig.Server.Service +namespace AgileConfig.Server.Service; + +public class AppBasicAuthService : IAppBasicAuthService { - public class AppBasicAuthService : IAppBasicAuthService + private readonly IAppService _appService; + + public AppBasicAuthService(IAppService appService) { - private readonly IAppService _appService; - public AppBasicAuthService(IAppService appService) - { - _appService = appService; - } - /// - /// Parse the appId and secret from the HTTP request. - /// - /// Incoming HTTP request containing the Authorization header. - /// Tuple of Application ID and secret extracted from the header. - public (string, string) GetAppIdSecret(HttpRequest httpRequest) + _appService = appService; + } + + /// + /// Parse the appId and secret from the HTTP request. + /// + /// Incoming HTTP request containing the Authorization header. + /// Tuple of Application ID and secret extracted from the header. + public (string, string) GetAppIdSecret(HttpRequest httpRequest) + { + var authorization = httpRequest.Headers["Authorization"]; + if (string.IsNullOrEmpty(authorization)) return ("", ""); + var authStr = authorization.First(); + // Remove the "Basic " prefix. + if (!authStr.StartsWith("Basic ")) { - var authorization = httpRequest.Headers["Authorization"]; - if (string.IsNullOrEmpty(authorization)) - { - return ("", ""); - } - var authStr = authorization.First(); - // Remove the "Basic " prefix. - if (!authStr.StartsWith("Basic ")) - { - return ("", ""); ; - } - authStr = authStr.Substring(6, authStr.Length - 6); - byte[] base64Decode = null; - try - { - base64Decode = Convert.FromBase64String(authStr); - } - catch - { - return ("", ""); - } - var base64Str = Encoding.UTF8.GetString(base64Decode); - - if (string.IsNullOrEmpty(base64Str)) - { - return ("", ""); - } - - var appId = ""; - var sec = ""; - - - var baseAuthArr = base64Str.Split(':'); - - if (baseAuthArr.Length > 0) - { - appId = baseAuthArr[0]; - } - if (baseAuthArr.Length > 1) - { - sec = baseAuthArr[1]; - } - - return (appId, sec); + return ("", ""); + ; } - public (string, string) GetUserNamePassword(HttpRequest httpRequest) + authStr = authStr.Substring(6, authStr.Length - 6); + byte[] base64Decode = null; + try { - throw new NotImplementedException(); + base64Decode = Convert.FromBase64String(authStr); } - - public async Task ValidAsync(HttpRequest httpRequest) + catch { - var appIdSecret = GetAppIdSecret(httpRequest); - var appId = appIdSecret.Item1; - var sec = appIdSecret.Item2; - if (string.IsNullOrEmpty(appIdSecret.Item1)) - { - return false; - } - - var app = await _appService.GetAsync(appId); - if (app == null) - { - return false; - } - if (!app.Enabled) - { - return false; - } - - var txt = $"{app.Id}:{app.Secret}"; - - return txt == $"{appId}:{sec}"; + return ("", ""); } + + var base64Str = Encoding.UTF8.GetString(base64Decode); + + if (string.IsNullOrEmpty(base64Str)) return ("", ""); + + var appId = ""; + var sec = ""; + + + var baseAuthArr = base64Str.Split(':'); + + if (baseAuthArr.Length > 0) appId = baseAuthArr[0]; + if (baseAuthArr.Length > 1) sec = baseAuthArr[1]; + + return (appId, sec); + } + + public async Task ValidAsync(HttpRequest httpRequest) + { + var appIdSecret = GetAppIdSecret(httpRequest); + var appId = appIdSecret.Item1; + var sec = appIdSecret.Item2; + if (string.IsNullOrEmpty(appIdSecret.Item1)) return false; + + var app = await _appService.GetAsync(appId); + if (app == null) return false; + if (!app.Enabled) return false; + + var txt = $"{app.Id}:{app.Secret}"; + + return txt == $"{appId}:{sec}"; + } + + public (string, string) GetUserNamePassword(HttpRequest httpRequest) + { + throw new NotImplementedException(); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/AppService.cs b/src/AgileConfig.Server.Service/AppService.cs index e639f593..5f5dcc4a 100644 --- a/src/AgileConfig.Server.Service/AppService.cs +++ b/src/AgileConfig.Server.Service/AppService.cs @@ -1,388 +1,328 @@ -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.IService; -using System; +using System; using System.Collections.Generic; -using System.Threading.Tasks; using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Threading.Tasks; using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.IService; + +namespace AgileConfig.Server.Service; -namespace AgileConfig.Server.Service +public class AppService : IAppService { - public class AppService : IAppService + private readonly IAppInheritancedRepository _appInheritancedRepository; + private readonly IAppRepository _appRepository; + private readonly Func _configPublishedRepositoryAccessor; + private readonly Func _configRepositoryAccessor; + private readonly ISettingService _settingService; + private readonly IUserAppAuthRepository _userAppAuthRepository; + private readonly IUserRepository _userRepository; + + public AppService( + IAppRepository repository, + IAppInheritancedRepository appInheritancedRepository, + Func configRepository, + Func configPublishedRepository, + IUserRepository userRepository, + IUserAppAuthRepository userAppAuthRepository, + ISettingService settingService + ) { - private readonly IAppRepository _appRepository; - private readonly IAppInheritancedRepository _appInheritancedRepository; - private readonly Func _configRepositoryAccessor; - private readonly Func _configPublishedRepositoryAccessor; - private readonly IUserRepository _userRepository; - private readonly IUserAppAuthRepository _userAppAuthRepository; - private readonly ISettingService _settingService; - - public AppService( - IAppRepository repository, - IAppInheritancedRepository appInheritancedRepository, - Func configRepository, - Func configPublishedRepository, - IUserRepository userRepository, - IUserAppAuthRepository userAppAuthRepository, - ISettingService settingService - ) - { - _appRepository = repository; - _appInheritancedRepository = appInheritancedRepository; - _configRepositoryAccessor = configRepository; - _configPublishedRepositoryAccessor = configPublishedRepository; - _userRepository = userRepository; - _userAppAuthRepository = userAppAuthRepository; - _settingService = settingService; - } + _appRepository = repository; + _appInheritancedRepository = appInheritancedRepository; + _configRepositoryAccessor = configRepository; + _configPublishedRepositoryAccessor = configPublishedRepository; + _userRepository = userRepository; + _userAppAuthRepository = userAppAuthRepository; + _settingService = settingService; + } - public async Task AddAsync(App app) - { - await _appRepository.InsertAsync(app); + public async Task AddAsync(App app) + { + await _appRepository.InsertAsync(app); - return true; - } + return true; + } - public async Task AddAsync(App app, List appInheritanceds) - { - await _appRepository.InsertAsync(app); - if (appInheritanceds != null) - { - await _appInheritancedRepository.InsertAsync(appInheritanceds); - } + public async Task AddAsync(App app, List appInheritanceds) + { + await _appRepository.InsertAsync(app); + if (appInheritanceds != null) await _appInheritancedRepository.InsertAsync(appInheritanceds); - return true; - } + return true; + } - public async Task DeleteAsync(App app) + public async Task DeleteAsync(App app) + { + app = await _appRepository.GetAsync(app.Id); + if (app != null) { - app = await _appRepository.GetAsync(app.Id); - if (app != null) - { - await _appRepository.DeleteAsync(app); + await _appRepository.DeleteAsync(app); - var envs = await _settingService.GetEnvironmentList(); - var updatedConfigIds = new List(); - var updatedConfigPublishedIds = new List(); + var envs = await _settingService.GetEnvironmentList(); + var updatedConfigIds = new List(); + var updatedConfigPublishedIds = new List(); - foreach (var env in envs) + foreach (var env in envs) + { + using var configRepository = _configRepositoryAccessor(env); + using var configPublishedRepository = _configPublishedRepositoryAccessor(env); + + // Preserve configurations so they can be recovered if the app was deleted accidentally. + var configs = + await configRepository.QueryAsync(x => x.AppId == app.Id && x.Status == ConfigStatus.Enabled); + var waitDeleteConfigs = new List(); + foreach (var item in configs) { - using var configRepository = _configRepositoryAccessor(env); - using var configPublishedRepository = _configPublishedRepositoryAccessor(env); - - // Preserve configurations so they can be recovered if the app was deleted accidentally. - var configs = - await configRepository.QueryAsync(x => x.AppId == app.Id && x.Status == ConfigStatus.Enabled); - var waitDeleteConfigs = new List(); - foreach (var item in configs) - { - if (updatedConfigIds.Contains(item.Id)) - { - // Avoid duplicate updates when multiple environments use the same underlying provider. - continue; - } - - item.Status = ConfigStatus.Deleted; - waitDeleteConfigs.Add(item); - updatedConfigIds.Add(item.Id); - } - - await configRepository.UpdateAsync(waitDeleteConfigs); - // Delete published configuration entries. - var publishedConfigs = await configPublishedRepository - .QueryAsync(x => x.AppId == app.Id && x.Status == ConfigStatus.Enabled) - ; - var waitDeletePublishedConfigs = new List(); - foreach (var item in publishedConfigs) - { - if (updatedConfigPublishedIds.Contains(item.Id)) - { - // Avoid duplicate updates when multiple environments use the same underlying provider. - continue; - } - - item.Status = ConfigStatus.Deleted; - waitDeletePublishedConfigs.Add(item); - updatedConfigPublishedIds.Add(item.Id); - } + if (updatedConfigIds.Contains(item.Id)) + // Avoid duplicate updates when multiple environments use the same underlying provider. + continue; - await configPublishedRepository.UpdateAsync(waitDeletePublishedConfigs); + item.Status = ConfigStatus.Deleted; + waitDeleteConfigs.Add(item); + updatedConfigIds.Add(item.Id); } - } - return true; - } + await configRepository.UpdateAsync(waitDeleteConfigs); + // Delete published configuration entries. + var publishedConfigs = await configPublishedRepository + .QueryAsync(x => x.AppId == app.Id && x.Status == ConfigStatus.Enabled) + ; + var waitDeletePublishedConfigs = new List(); + foreach (var item in publishedConfigs) + { + if (updatedConfigPublishedIds.Contains(item.Id)) + // Avoid duplicate updates when multiple environments use the same underlying provider. + continue; - public async Task DeleteAsync(string appId) - { - var app = await _appRepository.GetAsync(appId); - if (app != null) - { - await _appRepository.DeleteAsync(app); - } + item.Status = ConfigStatus.Deleted; + waitDeletePublishedConfigs.Add(item); + updatedConfigPublishedIds.Add(item.Id); + } - return true; + await configPublishedRepository.UpdateAsync(waitDeletePublishedConfigs); + } } - public Task GetAsync(string id) - { - return _appRepository.GetAsync(id); - } + return true; + } - public Task> GetAllAppsAsync() - { - return _appRepository.AllAsync(); - } + public async Task DeleteAsync(string appId) + { + var app = await _appRepository.GetAsync(appId); + if (app != null) await _appRepository.DeleteAsync(app); - public async Task<(List Apps, long Count)> SearchAsync(string id, string name, string group, - string sortField, string ascOrDesc, - int current, int pageSize) - { - Expression> exp = app => true; + return true; + } - if (!string.IsNullOrWhiteSpace(id)) - { - exp = exp.And(a => a.Id.Contains(id)); - } + public Task GetAsync(string id) + { + return _appRepository.GetAsync(id); + } - if (!string.IsNullOrWhiteSpace(name)) - { - exp = exp.And(a => a.Name.Contains(name)); - } + public Task> GetAllAppsAsync() + { + return _appRepository.AllAsync(); + } - if (!string.IsNullOrWhiteSpace(group)) - { - exp = exp.And(a => a.Group == group); - } + public async Task<(List Apps, long Count)> SearchAsync(string id, string name, string group, + string sortField, string ascOrDesc, + int current, int pageSize) + { + Expression> exp = app => true; - if (string.IsNullOrWhiteSpace(ascOrDesc)) - { - ascOrDesc = "asc"; - } + if (!string.IsNullOrWhiteSpace(id)) exp = exp.And(a => a.Id.Contains(id)); - var apps = await _appRepository.QueryPageAsync(exp, current, pageSize, sortField, - ascOrDesc?.StartsWith("asc") ?? true ? "ASC" : "DESC"); - var count = await _appRepository.CountAsync(exp); + if (!string.IsNullOrWhiteSpace(name)) exp = exp.And(a => a.Name.Contains(name)); - return (apps, count); - } + if (!string.IsNullOrWhiteSpace(group)) exp = exp.And(a => a.Group == group); - public async Task<(List GroupedApps, long Count)> SearchGroupedAsync(string id, string name, - string group, string sortField, string ascOrDesc, int current, - int pageSize) - { - Expression> exp = app => true; + if (string.IsNullOrWhiteSpace(ascOrDesc)) ascOrDesc = "asc"; - if (!string.IsNullOrWhiteSpace(id)) - { - exp = exp.And(a => a.Id.Contains(id)); - } + var apps = await _appRepository.QueryPageAsync(exp, current, pageSize, sortField, + ascOrDesc?.StartsWith("asc") ?? true ? "ASC" : "DESC"); + var count = await _appRepository.CountAsync(exp); - if (!string.IsNullOrWhiteSpace(name)) - { - exp = exp.And(a => a.Name.Contains(name)); - } + return (apps, count); + } - if (!string.IsNullOrWhiteSpace(group)) - { - exp = exp.And(a => a.Group == group); - } + public async Task<(List GroupedApps, long Count)> SearchGroupedAsync(string id, string name, + string group, string sortField, string ascOrDesc, int current, + int pageSize) + { + Expression> exp = app => true; - var apps = await _appRepository.QueryAsync(exp); + if (!string.IsNullOrWhiteSpace(id)) exp = exp.And(a => a.Id.Contains(id)); - var appGroups = apps.GroupBy(x => x.Group); - var appGroupList = new List(); - foreach (var appGroup in appGroups) - { - var app = appGroup.First(); - var firstGroup = new GroupedApp() - { - App = app - }; - var children = new List(); - if (appGroup.Count() > 1) - { - foreach (var item in appGroup) - { - if (firstGroup.App.Id != item.Id) - { - children.Add(new GroupedApp() - { - App = item - }); - } - } - } + if (!string.IsNullOrWhiteSpace(name)) exp = exp.And(a => a.Name.Contains(name)); - if (children.Count > 0) - { - firstGroup.Children = children; - } + if (!string.IsNullOrWhiteSpace(group)) exp = exp.And(a => a.Group == group); - appGroupList.Add(firstGroup); - } + var apps = await _appRepository.QueryAsync(exp); - var sortProperty = new Dictionary() + var appGroups = apps.GroupBy(x => x.Group); + var appGroupList = new List(); + foreach (var appGroup in appGroups) + { + var app = appGroup.First(); + var firstGroup = new GroupedApp { - { "id", typeof(App).GetProperty("Id") }, - { "name", typeof(App).GetProperty("Name") }, - { "group", typeof(App).GetProperty("Group") }, - { "createTime", typeof(App).GetProperty("CreateTime") } + App = app }; + var children = new List(); + if (appGroup.Count() > 1) + foreach (var item in appGroup) + if (firstGroup.App.Id != item.Id) + children.Add(new GroupedApp + { + App = item + }); - if (sortProperty.TryGetValue(sortField, out var propertyInfo)) - { - appGroupList = ascOrDesc?.StartsWith("asc") ?? true - ? appGroupList.OrderBy(x => propertyInfo.GetValue(x.App, null)).ToList() - : appGroupList.OrderByDescending(x => propertyInfo.GetValue(x.App, null)).ToList(); - } - - var page = appGroupList.Skip(current - 1 * pageSize).Take(pageSize).ToList(); + if (children.Count > 0) firstGroup.Children = children; - return (page, appGroupList.Count); + appGroupList.Add(firstGroup); } - public async Task UpdateAsync(App app) + var sortProperty = new Dictionary { - await _appRepository.UpdateAsync(app); + { "id", typeof(App).GetProperty("Id") }, + { "name", typeof(App).GetProperty("Name") }, + { "group", typeof(App).GetProperty("Group") }, + { "createTime", typeof(App).GetProperty("CreateTime") } + }; - return true; - } + if (sortProperty.TryGetValue(sortField, out var propertyInfo)) + appGroupList = ascOrDesc?.StartsWith("asc") ?? true + ? appGroupList.OrderBy(x => propertyInfo.GetValue(x.App, null)).ToList() + : appGroupList.OrderByDescending(x => propertyInfo.GetValue(x.App, null)).ToList(); - public async Task CountEnabledAppsAsync() - { - var q = await _appRepository.QueryAsync(a => a.Enabled == true); - - return q.Count; - } + var page = appGroupList.Skip(current - 1 * pageSize).Take(pageSize).ToList(); - public Task> GetAllInheritancedAppsAsync() - { - return _appRepository.QueryAsync(a => a.Type == AppType.Inheritance); - } + return (page, appGroupList.Count); + } - /// - /// Retrieve all applications that this app inherits from. - /// - /// Application ID whose inheritance chain should be resolved. - /// List of applications inherited by the specified app. - public async Task> GetInheritancedAppsAsync(string appId) - { - var appInheritanceds = await _appInheritancedRepository.QueryAsync(a => a.AppId == appId); - appInheritanceds = appInheritanceds.OrderBy(a => a.Sort).ToList(); + public async Task UpdateAsync(App app) + { + await _appRepository.UpdateAsync(app); - var apps = new List(); + return true; + } - foreach (var item in appInheritanceds) - { - var app = await GetAsync(item.InheritancedAppId); - if (app != null && app.Enabled) - { - apps.Add(app); - } - } + public async Task CountEnabledAppsAsync() + { + var q = await _appRepository.QueryAsync(a => a.Enabled == true); - return apps; - } + return q.Count; + } - /// - /// Retrieve applications that inherit from the specified app. - /// - /// Application ID whose dependents should be returned. - /// List of applications inheriting from the specified app. - public async Task> GetInheritancedFromAppsAsync(string appId) - { - var appInheritanceds = await _appInheritancedRepository.QueryAsync(a => a.InheritancedAppId == appId); - appInheritanceds = appInheritanceds.OrderBy(a => a.Sort).ToList(); + public Task> GetAllInheritancedAppsAsync() + { + return _appRepository.QueryAsync(a => a.Type == AppType.Inheritance); + } - var apps = new List(); + /// + /// Retrieve all applications that this app inherits from. + /// + /// Application ID whose inheritance chain should be resolved. + /// List of applications inherited by the specified app. + public async Task> GetInheritancedAppsAsync(string appId) + { + var appInheritanceds = await _appInheritancedRepository.QueryAsync(a => a.AppId == appId); + appInheritanceds = appInheritanceds.OrderBy(a => a.Sort).ToList(); - foreach (var item in appInheritanceds) - { - var app = await GetAsync(item.AppId); - if (app != null && app.Enabled) - { - apps.Add(app); - } - } + var apps = new List(); - return apps; + foreach (var item in appInheritanceds) + { + var app = await GetAsync(item.InheritancedAppId); + if (app != null && app.Enabled) apps.Add(app); } + return apps; + } - public async Task UpdateAsync(App app, List appInheritanceds) - { - await _appRepository.UpdateAsync(app); - var oldInheritancedApps = await _appInheritancedRepository.QueryAsync(a => a.AppId == app.Id); - await _appInheritancedRepository.DeleteAsync(oldInheritancedApps); - if (appInheritanceds != null) - { - await _appInheritancedRepository.InsertAsync(appInheritanceds); - } + /// + /// Retrieve applications that inherit from the specified app. + /// + /// Application ID whose dependents should be returned. + /// List of applications inheriting from the specified app. + public async Task> GetInheritancedFromAppsAsync(string appId) + { + var appInheritanceds = await _appInheritancedRepository.QueryAsync(a => a.InheritancedAppId == appId); + appInheritanceds = appInheritanceds.OrderBy(a => a.Sort).ToList(); - return true; - } + var apps = new List(); - public async Task SaveUserAppAuth(string appId, List userIds, string permission) + foreach (var item in appInheritanceds) { - var userAppAuthList = new List(); - if (userIds == null) - { - userIds = new List(); - } + var app = await GetAsync(item.AppId); + if (app != null && app.Enabled) apps.Add(app); + } - foreach (var userId in userIds) - { - userAppAuthList.Add(new UserAppAuth - { - Id = Guid.NewGuid().ToString("N"), - AppId = appId, - UserId = userId, - Permission = permission - }); - } + return apps; + } - var removeApps = - await _userAppAuthRepository.QueryAsync(x => x.AppId == appId && x.Permission == permission); - await _userAppAuthRepository.DeleteAsync(removeApps); - await _userAppAuthRepository.InsertAsync(userAppAuthList); - return true; - } + public async Task UpdateAsync(App app, List appInheritanceds) + { + await _appRepository.UpdateAsync(app); + var oldInheritancedApps = await _appInheritancedRepository.QueryAsync(a => a.AppId == app.Id); + await _appInheritancedRepository.DeleteAsync(oldInheritancedApps); + if (appInheritanceds != null) await _appInheritancedRepository.InsertAsync(appInheritanceds); - public void Dispose() - { - _appInheritancedRepository.Dispose(); - _appRepository.Dispose(); - _userAppAuthRepository.Dispose(); - _userRepository.Dispose(); - } + return true; + } - public async Task> GetUserAppAuth(string appId, string permission) - { - var auths = await _userAppAuthRepository.QueryAsync(x => x.AppId == appId && x.Permission == permission); + public async Task SaveUserAppAuth(string appId, List userIds, string permission) + { + var userAppAuthList = new List(); + if (userIds == null) userIds = new List(); - var users = new List(); - foreach (var auth in auths) + foreach (var userId in userIds) + userAppAuthList.Add(new UserAppAuth { - var user = await _userRepository.GetAsync(auth.UserId); - if (user != null) - { - users.Add(user); - } - } + Id = Guid.NewGuid().ToString("N"), + AppId = appId, + UserId = userId, + Permission = permission + }); + + var removeApps = + await _userAppAuthRepository.QueryAsync(x => x.AppId == appId && x.Permission == permission); + await _userAppAuthRepository.DeleteAsync(removeApps); + await _userAppAuthRepository.InsertAsync(userAppAuthList); + + return true; + } - return users; - } + public void Dispose() + { + _appInheritancedRepository.Dispose(); + _appRepository.Dispose(); + _userAppAuthRepository.Dispose(); + _userRepository.Dispose(); + } + + public async Task> GetUserAppAuth(string appId, string permission) + { + var auths = await _userAppAuthRepository.QueryAsync(x => x.AppId == appId && x.Permission == permission); - public async Task> GetAppGroups() + var users = new List(); + foreach (var auth in auths) { - var apps = await _appRepository.AllAsync(); - var groups = apps.GroupBy(x => x.Group).Select(x => x.Key); - return groups.Where(x => !string.IsNullOrEmpty(x)).ToList(); + var user = await _userRepository.GetAsync(auth.UserId); + if (user != null) users.Add(user); } + + return users; + } + + public async Task> GetAppGroups() + { + var apps = await _appRepository.AllAsync(); + var groups = apps.GroupBy(x => x.Group).Select(x => x.Key); + return groups.Where(x => !string.IsNullOrEmpty(x)).ToList(); } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/ConfigService.cs b/src/AgileConfig.Server.Service/ConfigService.cs index 1af0f5c3..9ecbfcd7 100644 --- a/src/AgileConfig.Server.Service/ConfigService.cs +++ b/src/AgileConfig.Server.Service/ConfigService.cs @@ -1,560 +1,471 @@ -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.IService; -using System; +using System; using System.Collections.Generic; using System.IO; -using System.Threading.Tasks; using System.Linq; +using System.Linq.Expressions; using System.Text; -using Microsoft.Extensions.Caching.Memory; +using System.Threading; +using System.Threading.Tasks; using AgileConfig.Server.Common; using AgileConfig.Server.Data.Abstraction; -using System.Linq.Expressions; -using System.Threading; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.IService; +using Microsoft.Extensions.Caching.Memory; -namespace AgileConfig.Server.Service +namespace AgileConfig.Server.Service; + +public class ConfigService : IConfigService { - public class ConfigService : IConfigService + private static readonly SemaphoreSlim _lock = new(1, 1); + private readonly IAppService _appService; + private readonly Func _configPublishedRepositoryAccessor; + private readonly Func _configRepositoryAccessor; + private readonly IMemoryCache _memoryCache; + private readonly Func _publishDetailRepositoryAccessor; + private readonly Func _publishTimelineRepositoryAccsssor; + private readonly ISettingService _settingService; + private readonly Func _uowAccessor; + private readonly IUserService _userService; + + public ConfigService(IMemoryCache memoryCache, + IAppService appService, + ISettingService settingService, + IUserService userService, + Func uowAccessor, + Func configRepositoryAccessor, + Func configPublishedRepositoryAccessor, + Func publishDetailRepositoryAccessor, + Func publishTimelineRepositoryAccessor) { - private readonly IMemoryCache _memoryCache; - private readonly IAppService _appService; - private readonly ISettingService _settingService; - private readonly IUserService _userService; - private readonly Func _uowAccessor; - private readonly Func _configRepositoryAccessor; - private readonly Func _configPublishedRepositoryAccessor; - private readonly Func _publishDetailRepositoryAccessor; - private readonly Func _publishTimelineRepositoryAccsssor; - - public ConfigService(IMemoryCache memoryCache, - IAppService appService, - ISettingService settingService, - IUserService userService, - Func uowAccessor, - Func configRepositoryAccessor, - Func configPublishedRepositoryAccessor, - Func publishDetailRepositoryAccessor, - Func publishTimelineRepositoryAccessor) - { - _memoryCache = memoryCache; - _appService = appService; - _settingService = settingService; - _userService = userService; - _uowAccessor = uowAccessor; - _configRepositoryAccessor = configRepositoryAccessor; - _configPublishedRepositoryAccessor = configPublishedRepositoryAccessor; - _publishDetailRepositoryAccessor = publishDetailRepositoryAccessor; - _publishTimelineRepositoryAccsssor = publishTimelineRepositoryAccessor; - } + _memoryCache = memoryCache; + _appService = appService; + _settingService = settingService; + _userService = userService; + _uowAccessor = uowAccessor; + _configRepositoryAccessor = configRepositoryAccessor; + _configPublishedRepositoryAccessor = configPublishedRepositoryAccessor; + _publishDetailRepositoryAccessor = publishDetailRepositoryAccessor; + _publishTimelineRepositoryAccsssor = publishTimelineRepositoryAccessor; + } - public async Task AddAsync(Config config, string env) - { - if (config.Value == null) - { - config.Value = ""; - } + public async Task AddAsync(Config config, string env) + { + if (config.Value == null) config.Value = ""; - using var repoistory = _configRepositoryAccessor(env); - await repoistory.InsertAsync(config); + using var repoistory = _configRepositoryAccessor(env); + await repoistory.InsertAsync(config); - return true; - } + return true; + } - public async Task UpdateAsync(Config config, string env) - { - using var repoistory = _configRepositoryAccessor(env); - await repoistory.UpdateAsync(config); + public async Task UpdateAsync(Config config, string env) + { + using var repoistory = _configRepositoryAccessor(env); + await repoistory.UpdateAsync(config); - return true; - } + return true; + } - public async Task UpdateAsync(List configs, string env) - { - using var repoistory = _configRepositoryAccessor(env); - foreach (var item in configs) - { - await repoistory.UpdateAsync(item); - } + public async Task UpdateAsync(List configs, string env) + { + using var repoistory = _configRepositoryAccessor(env); + foreach (var item in configs) await repoistory.UpdateAsync(item); - return true; - } + return true; + } - public async Task CancelEdit(List ids, string env) + public async Task CancelEdit(List ids, string env) + { + using var configRepository = _configRepositoryAccessor(env); + foreach (var configId in ids) { - using var configRepository = _configRepositoryAccessor(env); - foreach (var configId in ids) - { - var config = await configRepository.GetAsync(configId); - ; - if (config == null) - { - throw new Exception("Can not find config by id " + configId); - } - - if (config.EditStatus == EditStatus.Commit) - { - continue; - } + var config = await configRepository.GetAsync(configId); + ; + if (config == null) throw new Exception("Can not find config by id " + configId); - if (config.EditStatus == EditStatus.Add) - { - await configRepository.DeleteAsync(config); - } + if (config.EditStatus == EditStatus.Commit) continue; - if (config.EditStatus == EditStatus.Deleted || config.EditStatus == EditStatus.Edit) - { - config.OnlineStatus = OnlineStatus.Online; - config.EditStatus = EditStatus.Commit; - config.UpdateTime = DateTime.Now; + if (config.EditStatus == EditStatus.Add) await configRepository.DeleteAsync(config); - var publishedConfig = await GetPublishedConfigAsync(configId, env); - if (publishedConfig == null) - { - // - throw new Exception("Can not find published config by id " + configId); - } - else - { - //reset value - config.Value = publishedConfig.Value; - config.OnlineStatus = OnlineStatus.Online; - } + if (config.EditStatus == EditStatus.Deleted || config.EditStatus == EditStatus.Edit) + { + config.OnlineStatus = OnlineStatus.Online; + config.EditStatus = EditStatus.Commit; + config.UpdateTime = DateTime.Now; - await configRepository.UpdateAsync(config); - } - } + var publishedConfig = await GetPublishedConfigAsync(configId, env); + if (publishedConfig == null) + // + throw new Exception("Can not find published config by id " + configId); - return true; - } + //reset value + config.Value = publishedConfig.Value; + config.OnlineStatus = OnlineStatus.Online; - public async Task DeleteAsync(Config config, string env) - { - using var configRepository = _configRepositoryAccessor(env); - config = await configRepository.GetAsync(config.Id); - if (config != null) - { - await configRepository.DeleteAsync(config); + await configRepository.UpdateAsync(config); } - - return true; } - public async Task DeleteAsync(string configId, string env) - { - using var configRepository = _configRepositoryAccessor(env); - var config = await configRepository.GetAsync(configId); - if (config != null) - { - await configRepository.DeleteAsync(config); - } + return true; + } - return true; - } + public async Task DeleteAsync(Config config, string env) + { + using var configRepository = _configRepositoryAccessor(env); + config = await configRepository.GetAsync(config.Id); + if (config != null) await configRepository.DeleteAsync(config); - public async Task GetAsync(string id, string env) - { - using var repository = _configRepositoryAccessor(env); - var config = await repository.GetAsync(id); + return true; + } - return config; - } + public async Task DeleteAsync(string configId, string env) + { + using var configRepository = _configRepositoryAccessor(env); + var config = await configRepository.GetAsync(configId); + if (config != null) await configRepository.DeleteAsync(config); - public async Task> GetAllConfigsAsync(string env) - { - using var repository = _configRepositoryAccessor(env); - return await repository.QueryAsync(c => c.Status == ConfigStatus.Enabled && c.Env == env); - } + return true; + } - public async Task GetByAppIdKeyEnv(string appId, string group, string key, string env) - { - Expression> exp = (c) => c.AppId == appId && - c.Key == key && - c.Env == env && - c.Status == ConfigStatus.Enabled; + public async Task GetAsync(string id, string env) + { + using var repository = _configRepositoryAccessor(env); + var config = await repository.GetAsync(id); - if (string.IsNullOrEmpty(group)) - { - Expression> exp1 = c => c.Group == "" || c.Group == null; - exp.And(exp1); - } - else - { - Expression> exp1 = c => c.Group == group; - exp.And(exp1); - } + return config; + } - using var repository = _configRepositoryAccessor(env); - var configs = await repository.QueryAsync(exp); + public async Task> GetAllConfigsAsync(string env) + { + using var repository = _configRepositoryAccessor(env); + return await repository.QueryAsync(c => c.Status == ConfigStatus.Enabled && c.Env == env); + } - return configs.FirstOrDefault(); - } + public async Task GetByAppIdKeyEnv(string appId, string group, string key, string env) + { + Expression> exp = c => c.AppId == appId && + c.Key == key && + c.Env == env && + c.Status == ConfigStatus.Enabled; - public async Task> GetByAppIdAsync(string appId, string env) + if (string.IsNullOrEmpty(group)) { - using var repository = _configRepositoryAccessor(env); - return await repository.QueryAsync(c => - c.AppId == appId && c.Status == ConfigStatus.Enabled && c.Env == env - ); + Expression> exp1 = c => c.Group == "" || c.Group == null; + exp.And(exp1); } - - public async Task> Search(string appId, string group, string key, string env) + else { - using var repository = _configRepositoryAccessor(env); - - Expression> exp = c => c.Status == ConfigStatus.Enabled && c.Env == env; - if (!string.IsNullOrEmpty(appId)) - { - exp = exp.And(c => c.AppId == appId); - } - - if (!string.IsNullOrEmpty(group)) - { - exp = exp.And(c => c.Group.Contains(group)); - } - - if (!string.IsNullOrEmpty(key)) - { - exp = exp.And(c => c.Key.Contains(key)); - } - - return await repository.QueryAsync(exp); + Expression> exp1 = c => c.Group == group; + exp.And(exp1); } - public async Task CountEnabledConfigsAsync() - { - int count = 0; - var envs = await _settingService.GetEnvironmentList(); - foreach (var e in envs) - { - count += await CountEnabledConfigsAsync(e); - } - - return count; - } + using var repository = _configRepositoryAccessor(env); + var configs = await repository.QueryAsync(exp); - public async Task CountEnabledConfigsAsync(string env) - { - // Count all configurations in the specified environment. - using var repository = _configRepositoryAccessor(env); - var q = await repository.QueryAsync(c => c.Status == ConfigStatus.Enabled && c.Env == env); + return configs.FirstOrDefault(); + } - return q.Count; - } + public async Task> GetByAppIdAsync(string appId, string env) + { + using var repository = _configRepositoryAccessor(env); + return await repository.QueryAsync(c => + c.AppId == appId && c.Status == ConfigStatus.Enabled && c.Env == env + ); + } - public string GenerateKey(Config config) - { - if (string.IsNullOrEmpty(config.Group)) - { - return config.Key; - } + public async Task> Search(string appId, string group, string key, string env) + { + using var repository = _configRepositoryAccessor(env); - return $"{config.Group}:{config.Key}"; - } + Expression> exp = c => c.Status == ConfigStatus.Enabled && c.Env == env; + if (!string.IsNullOrEmpty(appId)) exp = exp.And(c => c.AppId == appId); - public string GenerateKey(ConfigPublished config) - { - if (string.IsNullOrEmpty(config.Group)) - { - return config.Key; - } + if (!string.IsNullOrEmpty(group)) exp = exp.And(c => c.Group.Contains(group)); - return $"{config.Group}:{config.Key}"; - } + if (!string.IsNullOrEmpty(key)) exp = exp.And(c => c.Key.Contains(key)); - /// - /// Calculate the MD5 hash of the published configuration set for an application. - /// - /// Application ID whose published configuration should be hashed. - /// Environment from which to load published configuration values. - /// MD5 hash of the concatenated keys and values. - public async Task AppPublishedConfigsMd5(string appId, string env) - { - using var repository = _configPublishedRepositoryAccessor(env); - var configs = await repository.QueryAsync(c => - c.AppId == appId && c.Status == ConfigStatus.Enabled - && c.Env == env - ); + return await repository.QueryAsync(exp); + } - var keyStr = string.Join('&', configs.Select(c => GenerateKey(c)).ToArray().OrderBy(k => k, StringComparer.Ordinal)); - var valStr = string.Join('&', configs.Select(c => c.Value).ToArray().OrderBy(v => v, StringComparer.Ordinal)); - var txt = $"{keyStr}&{valStr}"; + public async Task CountEnabledConfigsAsync() + { + var count = 0; + var envs = await _settingService.GetEnvironmentList(); + foreach (var e in envs) count += await CountEnabledConfigsAsync(e); - return Encrypt.Md5(txt); - } + return count; + } - /// - /// Calculate the MD5 hash of the published configuration set with a one-minute cache. - /// - /// Application ID whose published configuration hash should be cached. - /// Environment for which the cache entry applies. - /// Cached or freshly calculated MD5 hash. - public async Task AppPublishedConfigsMd5Cache(string appId, string env) - { - var cacheKey = AppPublishedConfigsMd5CacheKey(appId, env); - if (_memoryCache != null && _memoryCache.TryGetValue(cacheKey, out string md5)) - { - return md5; - } + public string GenerateKey(Config config) + { + if (string.IsNullOrEmpty(config.Group)) return config.Key; - md5 = await AppPublishedConfigsMd5(appId, env); + return $"{config.Group}:{config.Key}"; + } - var cacheOp = new MemoryCacheEntryOptions() - .SetAbsoluteExpiration(TimeSpan.FromSeconds(60)); - _memoryCache?.Set(cacheKey, md5, cacheOp); + /// + /// Calculate the MD5 hash of the published configuration set for an application. + /// + /// Application ID whose published configuration should be hashed. + /// Environment from which to load published configuration values. + /// MD5 hash of the concatenated keys and values. + public async Task AppPublishedConfigsMd5(string appId, string env) + { + using var repository = _configPublishedRepositoryAccessor(env); + var configs = await repository.QueryAsync(c => + c.AppId == appId && c.Status == ConfigStatus.Enabled + && c.Env == env + ); + + var keyStr = string.Join('&', + configs.Select(c => GenerateKey(c)).ToArray().OrderBy(k => k, StringComparer.Ordinal)); + var valStr = string.Join('&', configs.Select(c => c.Value).ToArray().OrderBy(v => v, StringComparer.Ordinal)); + var txt = $"{keyStr}&{valStr}"; + + return Encrypt.Md5(txt); + } - return md5; - } + /// + /// Calculate the MD5 hash of the published configuration set with a one-minute cache. + /// + /// Application ID whose published configuration hash should be cached. + /// Environment for which the cache entry applies. + /// Cached or freshly calculated MD5 hash. + public async Task AppPublishedConfigsMd5Cache(string appId, string env) + { + var cacheKey = AppPublishedConfigsMd5CacheKey(appId, env); + if (_memoryCache != null && _memoryCache.TryGetValue(cacheKey, out string md5)) return md5; - private string AppPublishedConfigsMd5CacheKey(string appId, string env) - { - return $"ConfigService_AppPublishedConfigsMd5Cache_{appId}_{env}"; - } + md5 = await AppPublishedConfigsMd5(appId, env); - private string AppPublishedConfigsMd5CacheKeyWithInheritance(string appId, string env) - { - return $"ConfigService_AppPublishedConfigsMd5CacheWithInheritance_{appId}_{env}"; - } + var cacheOp = new MemoryCacheEntryOptions() + .SetAbsoluteExpiration(TimeSpan.FromSeconds(60)); + _memoryCache?.Set(cacheKey, md5, cacheOp); - private string AppPublishTimelineVirtualIdCacheKey(string appId, string env) - { - return $"ConfigService_AppPublishTimelineVirtualIdWithCache_{appId}_{env}"; - } + return md5; + } - private void ClearAppPublishedConfigsMd5Cache(string appId, string env) + public async Task AddRangeAsync(List configs, string env) + { + configs.ForEach(x => { - var cacheKey = AppPublishedConfigsMd5CacheKey(appId, env); - _memoryCache?.Remove(cacheKey); - } + if (x.Value == null) x.Value = ""; + }); - private void ClearAppPublishedConfigsMd5CacheWithInheritance(string appId, string env) - { - var cacheKey = AppPublishedConfigsMd5CacheKeyWithInheritance(appId, env); - _memoryCache?.Remove(cacheKey); - } + using var repository = _configRepositoryAccessor(env); + await repository.InsertAsync(configs); - private void ClearAppPublishTimelineVirtualIdCache(string appId, string env) - { - var cacheKey = AppPublishTimelineVirtualIdCacheKey(appId, env); - _memoryCache?.Remove(cacheKey); - } + var appId = configs.First().AppId; + ClearAppPublishedConfigsMd5Cache(appId, env); + ClearAppPublishedConfigsMd5CacheWithInheritance(appId, env); + ClearAppPublishTimelineVirtualIdCache(appId, env); - public async Task AddRangeAsync(List configs, string env) - { - configs.ForEach(x => - { - if (x.Value == null) - { - x.Value = ""; - } - }); - - using var repository = _configRepositoryAccessor(env); - await repository.InsertAsync(configs); + return true; + } - var appId = configs.First().AppId; - ClearAppPublishedConfigsMd5Cache(appId,env); - ClearAppPublishedConfigsMd5CacheWithInheritance(appId, env); - ClearAppPublishTimelineVirtualIdCache(appId, env); + /// + /// Retrieve the published configuration for an application merged with inherited applications. + /// + /// Application ID for which to gather published configuration. + /// Environment from which to load published configuration values. + /// List of configuration records after inheritance is applied. + public async Task> GetPublishedConfigsByAppIdWithInheritance(string appId, string env) + { + var configs = await GetPublishedConfigsByAppIdWithInheritance_Dictionary(appId, env); - return true; - } + return configs.Values.ToList(); + } - /// - /// Retrieve the published configuration for an application merged with inherited applications. - /// - /// Application ID for which to gather published configuration. - /// Environment from which to load published configuration values. - /// List of configuration records after inheritance is applied. - public async Task> GetPublishedConfigsByAppIdWithInheritance(string appId, string env) - { - var configs = await GetPublishedConfigsByAppIdWithInheritance_Dictionary(appId, env); + /// + /// Retrieve the published configuration for an application merged with inherited applications as a dictionary. + /// + /// Application ID for which to gather published configuration. + /// Environment from which to load published configuration values. + /// Dictionary of configuration records keyed by generated configuration key. + public async Task> GetPublishedConfigsByAppIdWithInheritance_Dictionary( + string appId, string env) + { + var apps = new List(); + var inheritanceApps = await _appService.GetInheritancedAppsAsync(appId); + for (var i = 0; i < inheritanceApps.Count; i++) + if (inheritanceApps[i].Enabled) + apps.Add(inheritanceApps[i].Id); // Append inherited applications in order. - return configs.Values.ToList(); - } + apps.Add(appId); // Add the current application last. - /// - /// Retrieve the published configuration for an application merged with inherited applications as a dictionary. - /// - /// Application ID for which to gather published configuration. - /// Environment from which to load published configuration values. - /// Dictionary of configuration records keyed by generated configuration key. - public async Task> GetPublishedConfigsByAppIdWithInheritance_Dictionary( - string appId, string env) + var configs = new Dictionary(); + // Load configurations from inherited apps and the current app. + for (var i = 0; i < apps.Count; i++) { - var apps = new List(); - var inheritanceApps = await _appService.GetInheritancedAppsAsync(appId); - for (int i = 0; i < inheritanceApps.Count; i++) + var id = apps[i]; + var publishConfigs = await GetPublishedConfigsAsync(id, env); + for (var j = 0; j < publishConfigs.Count; j++) { - if (inheritanceApps[i].Enabled) - { - apps.Add(inheritanceApps[i].Id as string); // Append inherited applications in order. - } + var config = publishConfigs[j].Convert(); + var key = GenerateKey(config); + if (configs.ContainsKey(key)) + // Later configurations override earlier ones. + configs[key] = config; + else + configs.Add(key, config); } + } - apps.Add(appId); // Add the current application last. - - var configs = new Dictionary(); - // Load configurations from inherited apps and the current app. - for (int i = 0; i < apps.Count; i++) - { - var id = apps[i]; - var publishConfigs = await GetPublishedConfigsAsync(id, env); - for (int j = 0; j < publishConfigs.Count; j++) - { - var config = publishConfigs[j].Convert(); - var key = GenerateKey(config); - if (configs.ContainsKey(key)) - { - // Later configurations override earlier ones. - configs[key] = config; - } - else - { - configs.Add(key, config); - } - } - } + return configs; + } - return configs; - } + public async Task AppPublishedConfigsMd5WithInheritance(string appId, string env) + { + var configs = await GetPublishedConfigsByAppIdWithInheritance(appId, env); - public async Task AppPublishedConfigsMd5WithInheritance(string appId, string env) - { - var configs = await GetPublishedConfigsByAppIdWithInheritance(appId, env); + var keyStr = string.Join('&', + configs.Select(c => GenerateKey(c)).ToArray().OrderBy(k => k, StringComparer.Ordinal)); + var valStr = string.Join('&', configs.Select(c => c.Value).ToArray().OrderBy(v => v, StringComparer.Ordinal)); + var txt = $"{keyStr}&{valStr}"; - var keyStr = string.Join('&', configs.Select(c => GenerateKey(c)).ToArray().OrderBy(k => k, StringComparer.Ordinal)); - var valStr = string.Join('&', configs.Select(c => c.Value).ToArray().OrderBy(v => v, StringComparer.Ordinal)); - var txt = $"{keyStr}&{valStr}"; + return Encrypt.Md5(txt); + } - return Encrypt.Md5(txt); - } + /// + /// Calculate the MD5 hash of the published configuration merged with inherited applications, cached for one minute. + /// + /// Application ID whose inherited configuration hash should be cached. + /// Environment associated with the cached hash. + /// Cached or freshly calculated MD5 hash for the inherited configuration. + public async Task AppPublishedConfigsMd5CacheWithInheritance(string appId, string env) + { + var cacheKey = AppPublishedConfigsMd5CacheKeyWithInheritance(appId, env); + if (_memoryCache != null && _memoryCache.TryGetValue(cacheKey, out string md5)) return md5; - /// - /// Calculate the MD5 hash of the published configuration merged with inherited applications, cached for one minute. - /// - /// Application ID whose inherited configuration hash should be cached. - /// Environment associated with the cached hash. - /// Cached or freshly calculated MD5 hash for the inherited configuration. - public async Task AppPublishedConfigsMd5CacheWithInheritance(string appId, string env) - { - var cacheKey = AppPublishedConfigsMd5CacheKeyWithInheritance(appId, env); - if (_memoryCache != null && _memoryCache.TryGetValue(cacheKey, out string md5)) - { - return md5; - } + md5 = await AppPublishedConfigsMd5WithInheritance(appId, env); - md5 = await AppPublishedConfigsMd5WithInheritance(appId, env); + var cacheOp = new MemoryCacheEntryOptions() + .SetAbsoluteExpiration(TimeSpan.FromSeconds(60)); + _memoryCache?.Set(cacheKey, md5, cacheOp); - var cacheOp = new MemoryCacheEntryOptions() - .SetAbsoluteExpiration(TimeSpan.FromSeconds(60)); - _memoryCache?.Set(cacheKey, md5, cacheOp); + return md5; + } - return md5; - } + public void Dispose() + { + _appService?.Dispose(); + _settingService?.Dispose(); + _userService?.Dispose(); + } - public void Dispose() + /// + /// Publish pending configuration items for an application. + /// + /// Application ID. + /// Identifiers of configuration items to publish. + /// Publish log. + /// Operator user name. + /// Environment in which to publish the configuration. + /// + public async Task<(bool result, string publishTimelineId)> Publish(string appId, string[] ids, string log, + string operatorr, string env) + { + await _lock.WaitAsync(); + using var uow = _uowAccessor(env); + try { - _appService?.Dispose(); - _settingService?.Dispose(); - _userService?.Dispose(); - } + using var configRepository = _configRepositoryAccessor(env); + configRepository.Uow = uow; + using var publishTimelineRepository = _publishTimelineRepositoryAccsssor(env); + publishTimelineRepository.Uow = uow; + using var configPublishedRepository = _configPublishedRepositoryAccessor(env); + configPublishedRepository.Uow = uow; + using var publishDetailRepository = _publishDetailRepositoryAccessor(env); + publishDetailRepository.Uow = uow; - private static readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); - - /// - /// Publish pending configuration items for an application. - /// - /// Application ID. - /// Identifiers of configuration items to publish. - /// Publish log. - /// Operator user name. - /// Environment in which to publish the configuration. - /// - public async Task<(bool result, string publishTimelineId)> Publish(string appId, string[] ids, string log, string operatorr, string env) - { - await _lock.WaitAsync(); - using var uow = _uowAccessor(env); - try + uow?.Begin(); + + var waitPublishConfigs = await configRepository.QueryAsync(x => + x.AppId == appId && + x.Env == env && + x.Status == ConfigStatus.Enabled && + x.EditStatus != EditStatus.Commit); + + if (ids != null && ids.Any()) + // Filter by the specified configuration identifiers when provided. + waitPublishConfigs = waitPublishConfigs.Where(x => ids.Contains(x.Id)).ToList(); + // Assumes a single admin console instance; concurrent publishes across multiple instances may cause version conflicts. + var publishList = await publishTimelineRepository.QueryAsync(x => x.AppId == appId); + var versionMax = publishList.Any() ? publishList.Max(x => x.Version) : 0; + + var user = await _userService.GetUserAsync(operatorr); + + var publishTimelineNode = new PublishTimeline(); + publishTimelineNode.AppId = appId; + publishTimelineNode.Id = Guid.NewGuid().ToString("N"); + publishTimelineNode.PublishTime = DateTime.Now; + publishTimelineNode.PublishUserId = user?.Id; + publishTimelineNode.PublishUserName = user?.UserName; + publishTimelineNode.Version = versionMax + 1; + publishTimelineNode.Log = log; + publishTimelineNode.Env = env; + + var publishDetails = new List(); + waitPublishConfigs.ForEach(x => { - using var configRepository = _configRepositoryAccessor(env); - configRepository.Uow = uow; - using var publishTimelineRepository = _publishTimelineRepositoryAccsssor(env); - publishTimelineRepository.Uow = uow; - using var configPublishedRepository = _configPublishedRepositoryAccessor(env); - configPublishedRepository.Uow = uow; - using var publishDetailRepository = _publishDetailRepositoryAccessor(env); - publishDetailRepository.Uow = uow; - - uow?.Begin(); - - var waitPublishConfigs = await configRepository.QueryAsync(x => - x.AppId == appId && - x.Env == env && - x.Status == ConfigStatus.Enabled && - x.EditStatus != EditStatus.Commit); - - if (ids != null && ids.Any()) + publishDetails.Add(new PublishDetail { - // Filter by the specified configuration identifiers when provided. - waitPublishConfigs = waitPublishConfigs.Where(x => ids.Contains(x.Id)).ToList(); + AppId = appId, + ConfigId = x.Id, + Description = x.Description, + EditStatus = x.EditStatus, + Group = x.Group, + Id = Guid.NewGuid().ToString("N"), + Key = x.Key, + Value = x.Value, + PublishTimelineId = publishTimelineNode.Id, + Version = publishTimelineNode.Version, + Env = env + }); + + if (x.EditStatus == EditStatus.Deleted) + { + x.OnlineStatus = OnlineStatus.WaitPublish; + x.Status = ConfigStatus.Deleted; } - // Assumes a single admin console instance; concurrent publishes across multiple instances may cause version conflicts. - var publishList = await publishTimelineRepository.QueryAsync(x => x.AppId == appId); - var versionMax = publishList.Any() ? publishList.Max(x => x.Version) : 0; - - var user = await _userService.GetUserAsync(operatorr); - - var publishTimelineNode = new PublishTimeline(); - publishTimelineNode.AppId = appId; - publishTimelineNode.Id = Guid.NewGuid().ToString("N"); - publishTimelineNode.PublishTime = DateTime.Now; - publishTimelineNode.PublishUserId = user?.Id; - publishTimelineNode.PublishUserName = user?.UserName; - publishTimelineNode.Version = versionMax + 1; - publishTimelineNode.Log = log; - publishTimelineNode.Env = env; - - var publishDetails = new List(); - waitPublishConfigs.ForEach(x => + else { - publishDetails.Add(new PublishDetail() - { - AppId = appId, - ConfigId = x.Id, - Description = x.Description, - EditStatus = x.EditStatus, - Group = x.Group, - Id = Guid.NewGuid().ToString("N"), - Key = x.Key, - Value = x.Value, - PublishTimelineId = publishTimelineNode.Id, - Version = publishTimelineNode.Version, - Env = env - }); + x.OnlineStatus = OnlineStatus.Online; + x.Status = ConfigStatus.Enabled; + } - if (x.EditStatus == EditStatus.Deleted) - { - x.OnlineStatus = OnlineStatus.WaitPublish; - x.Status = ConfigStatus.Deleted; - } - else - { - x.OnlineStatus = OnlineStatus.Online; - x.Status = ConfigStatus.Enabled; - } + x.EditStatus = EditStatus.Commit; + x.OnlineStatus = OnlineStatus.Online; + }); - x.EditStatus = EditStatus.Commit; - x.OnlineStatus = OnlineStatus.Online; + // Configurations that are currently published. + var publishedConfigs = await configPublishedRepository + .QueryAsync(x => x.Status == ConfigStatus.Enabled && x.AppId == appId && x.Env == env); + // Clone a new version that will be inserted into the publish table. + var publishedConfigsCopy = new List(); + publishedConfigs.ForEach(x => + { + publishedConfigsCopy.Add(new ConfigPublished + { + AppId = x.AppId, + ConfigId = x.ConfigId, + Group = x.Group, + Id = Guid.NewGuid().ToString("N"), + Key = x.Key, + PublishTimelineId = publishTimelineNode.Id, + PublishTime = publishTimelineNode.PublishTime, + Status = ConfigStatus.Enabled, + Version = publishTimelineNode.Version, + Value = x.Value, + Env = x.Env }); + x.Status = ConfigStatus.Deleted; + }); - // Configurations that are currently published. - var publishedConfigs = await configPublishedRepository - .QueryAsync(x => x.Status == ConfigStatus.Enabled && x.AppId == appId && x.Env == env); - // Clone a new version that will be inserted into the publish table. - var publishedConfigsCopy = new List(); - publishedConfigs.ForEach(x => - { - publishedConfigsCopy.Add(new ConfigPublished() + publishDetails.ForEach(x => + { + if (x.EditStatus == EditStatus.Add) + publishedConfigsCopy.Add(new ConfigPublished { AppId = x.AppId, ConfigId = x.ConfigId, @@ -564,595 +475,564 @@ public void Dispose() PublishTimelineId = publishTimelineNode.Id, PublishTime = publishTimelineNode.PublishTime, Status = ConfigStatus.Enabled, - Version = publishTimelineNode.Version, Value = x.Value, - Env = x.Env + Version = publishTimelineNode.Version, + Env = env }); - x.Status = ConfigStatus.Deleted; - }); - publishDetails.ForEach(x => + if (x.EditStatus == EditStatus.Edit) { - if (x.EditStatus == EditStatus.Add) + var oldEntity = publishedConfigsCopy.FirstOrDefault(c => c.ConfigId == x.ConfigId); + if (oldEntity == null) { - publishedConfigsCopy.Add(new ConfigPublished() - { - AppId = x.AppId, - ConfigId = x.ConfigId, - Group = x.Group, - Id = Guid.NewGuid().ToString("N"), - Key = x.Key, - PublishTimelineId = publishTimelineNode.Id, - PublishTime = publishTimelineNode.PublishTime, - Status = ConfigStatus.Enabled, - Value = x.Value, - Version = publishTimelineNode.Version, - Env = env - }); + //do nothing } - - if (x.EditStatus == EditStatus.Edit) + else { - var oldEntity = publishedConfigsCopy.FirstOrDefault(c => c.ConfigId == x.ConfigId); - if (oldEntity == null) - { - //do nothing - } - else - { - //edit - oldEntity.Version = publishTimelineNode.Version; - oldEntity.Group = x.Group; - oldEntity.Key = x.Key; - oldEntity.Value = x.Value; - oldEntity.PublishTime = publishTimelineNode.PublishTime; - } + //edit + oldEntity.Version = publishTimelineNode.Version; + oldEntity.Group = x.Group; + oldEntity.Key = x.Key; + oldEntity.Value = x.Value; + oldEntity.PublishTime = publishTimelineNode.PublishTime; } + } - if (x.EditStatus == EditStatus.Deleted) + if (x.EditStatus == EditStatus.Deleted) + { + var oldEntity = publishedConfigsCopy.FirstOrDefault(c => c.ConfigId == x.ConfigId); + if (oldEntity == null) { - var oldEntity = publishedConfigsCopy.FirstOrDefault(c => c.ConfigId == x.ConfigId); - if (oldEntity == null) - { - //do nothing - } - else - { - //remove - publishedConfigsCopy.Remove(oldEntity); - } + //do nothing } - }); + else + { + //remove + publishedConfigsCopy.Remove(oldEntity); + } + } + }); - await configRepository.UpdateAsync(waitPublishConfigs); - await publishTimelineRepository.InsertAsync(publishTimelineNode); - await publishDetailRepository.InsertAsync(publishDetails); - await configPublishedRepository.UpdateAsync(publishedConfigs); - await configPublishedRepository.InsertAsync(publishedConfigsCopy); + await configRepository.UpdateAsync(waitPublishConfigs); + await publishTimelineRepository.InsertAsync(publishTimelineNode); + await publishDetailRepository.InsertAsync(publishDetails); + await configPublishedRepository.UpdateAsync(publishedConfigs); + await configPublishedRepository.InsertAsync(publishedConfigsCopy); - await uow?.SaveChangesAsync(); + await uow?.SaveChangesAsync(); - ClearAppPublishedConfigsMd5Cache(appId, env); - ClearAppPublishedConfigsMd5CacheWithInheritance(appId, env); - ClearAppPublishTimelineVirtualIdCache(appId, env); + ClearAppPublishedConfigsMd5Cache(appId, env); + ClearAppPublishedConfigsMd5CacheWithInheritance(appId, env); + ClearAppPublishTimelineVirtualIdCache(appId, env); - return (true, publishTimelineNode.Id); - } - catch (Exception exc) - { - uow?.Rollback(); - throw; - } - finally - { - _lock.Release(); - } + return (true, publishTimelineNode.Id); } - - public async Task IsPublishedAsync(string configId, string env) + catch (Exception exc) { - using var repository = _configPublishedRepositoryAccessor(env); - var any = await repository.QueryAsync( - x => x.ConfigId == configId - && x.Env == env - && x.Status == ConfigStatus.Enabled); - - return any.Count > 0; + uow?.Rollback(); + throw; } - - public async Task> GetPublishDetailByPublishTimelineIdAsync(string publishTimelineId, - string env) + finally { - using var repository = _publishDetailRepositoryAccessor(env); - var list = await repository - .QueryAsync(x => x.PublishTimelineId == publishTimelineId && x.Env == env); - - return list; + _lock.Release(); } + } - public async Task GetPublishTimeLineNodeAsync(string publishTimelineId, string env) - { - using var repository = _publishTimelineRepositoryAccsssor(env); - var one = (await repository.QueryAsync(x => x.Id == publishTimelineId && x.Env == env)) - .FirstOrDefault(); - - return one; - } + public async Task IsPublishedAsync(string configId, string env) + { + using var repository = _configPublishedRepositoryAccessor(env); + var any = await repository.QueryAsync(x => x.ConfigId == configId + && x.Env == env + && x.Status == ConfigStatus.Enabled); - public async Task> GetPublishTimelineHistoryAsync(string appId, string env) - { - using var repository = _publishTimelineRepositoryAccsssor(env); - var list = await repository.QueryAsync(x => x.AppId == appId && x.Env == env); + return any.Count > 0; + } - return list; - } + public async Task> GetPublishDetailByPublishTimelineIdAsync(string publishTimelineId, + string env) + { + using var repository = _publishDetailRepositoryAccessor(env); + var list = await repository + .QueryAsync(x => x.PublishTimelineId == publishTimelineId && x.Env == env); - public async Task> GetPublishDetailListAsync(string appId, string env) - { - using var repository = _publishDetailRepositoryAccessor(env); - var list = await repository.QueryAsync(x => x.AppId == appId && x.Env == env); + return list; + } - return list; - } + public async Task GetPublishTimeLineNodeAsync(string publishTimelineId, string env) + { + using var repository = _publishTimelineRepositoryAccsssor(env); + var one = (await repository.QueryAsync(x => x.Id == publishTimelineId && x.Env == env)) + .FirstOrDefault(); - public async Task> GetConfigPublishedHistory(string configId, string env) - { - using var repository = _publishDetailRepositoryAccessor(env); - var list = await repository.QueryAsync(x => x.ConfigId == configId && x.Env == env); + return one; + } - return list; - } + public async Task> GetPublishTimelineHistoryAsync(string appId, string env) + { + using var repository = _publishTimelineRepositoryAccsssor(env); + var list = await repository.QueryAsync(x => x.AppId == appId && x.Env == env); - public async Task> GetPublishedConfigsAsync(string appId, string env) - { - using var repository = _configPublishedRepositoryAccessor(env); - var list = await repository.QueryAsync(x => x.AppId == appId && x.Status == ConfigStatus.Enabled && x.Env == env); + return list; + } - return list; - } + public async Task> GetPublishDetailListAsync(string appId, string env) + { + using var repository = _publishDetailRepositoryAccessor(env); + var list = await repository.QueryAsync(x => x.AppId == appId && x.Env == env); - public async Task GetPublishedConfigAsync(string configId, string env) - { - using var repository = _configPublishedRepositoryAccessor(env); - var one = (await repository.QueryAsync(x => x.ConfigId == configId - && x.Status == ConfigStatus.Enabled - && x.Env == env - )).FirstOrDefault(); + return list; + } - return one; - } + public async Task> GetConfigPublishedHistory(string configId, string env) + { + using var repository = _publishDetailRepositoryAccessor(env); + var list = await repository.QueryAsync(x => x.ConfigId == configId && x.Env == env); - public async Task RollbackAsync(string publishTimelineId, string env) - { - using var uow = _uowAccessor(env); + return list; + } - using var configRepository = _configRepositoryAccessor(env); - using var publishTimelineRepository = _publishTimelineRepositoryAccsssor(env); - using var configPublishedRepository = _configPublishedRepositoryAccessor(env); - using var publishDetailRepository = _publishDetailRepositoryAccessor(env); + public async Task> GetPublishedConfigsAsync(string appId, string env) + { + using var repository = _configPublishedRepositoryAccessor(env); + var list = + await repository.QueryAsync(x => x.AppId == appId && x.Status == ConfigStatus.Enabled && x.Env == env); - configRepository.Uow = uow; - publishTimelineRepository.Uow = uow; - configPublishedRepository.Uow = uow; - publishDetailRepository.Uow = uow; + return list; + } - uow?.Begin(); + public async Task GetPublishedConfigAsync(string configId, string env) + { + using var repository = _configPublishedRepositoryAccessor(env); + var one = (await repository.QueryAsync(x => x.ConfigId == configId + && x.Status == ConfigStatus.Enabled + && x.Env == env + )).FirstOrDefault(); - var publishNode = (await publishTimelineRepository.QueryAsync(x => x.Id == publishTimelineId && x.Env == env)).FirstOrDefault(); + return one; + } - var version = publishNode.Version; - var appId = publishNode.AppId; + public async Task RollbackAsync(string publishTimelineId, string env) + { + using var uow = _uowAccessor(env); - var latest = (await publishTimelineRepository.QueryAsync(x => x.AppId == appId && x.Env == env)) - .OrderByDescending(x => x.Version).FirstOrDefault(); + using var configRepository = _configRepositoryAccessor(env); + using var publishTimelineRepository = _publishTimelineRepositoryAccsssor(env); + using var configPublishedRepository = _configPublishedRepositoryAccessor(env); + using var publishDetailRepository = _publishDetailRepositoryAccessor(env); - if (latest.Id == publishTimelineId) - { - // Already at the desired version; no rollback required. - return true; - } + configRepository.Uow = uow; + publishTimelineRepository.Uow = uow; + configPublishedRepository.Uow = uow; + publishDetailRepository.Uow = uow; - var publishedConfigs = await configPublishedRepository - .QueryAsync(x => x.AppId == appId && x.Version == version && x.Env == env); - var currentConfigs = await configRepository - .QueryAsync(x => x.AppId == appId && x.Status == ConfigStatus.Enabled && x.Env == env); + uow?.Begin(); - // Soft delete all current configurations. - foreach (var item in currentConfigs) - { - item.Status = ConfigStatus.Deleted; - } + var publishNode = (await publishTimelineRepository.QueryAsync(x => x.Id == publishTimelineId && x.Env == env)) + .FirstOrDefault(); - await configRepository.UpdateAsync(currentConfigs); - // Enable published items that match by configuration identifier. - var now = DateTime.Now; - foreach (var item in publishedConfigs) - { - var config = (await configRepository.QueryAsync(x => x.AppId == appId && x.Id == item.ConfigId)).FirstOrDefault(); - config.Status = ConfigStatus.Enabled; - config.Value = item.Value; - config.UpdateTime = now; - config.EditStatus = EditStatus.Commit; - config.OnlineStatus = OnlineStatus.Online; + var version = publishNode.Version; + var appId = publishNode.AppId; - await configRepository.UpdateAsync(config); - } + var latest = (await publishTimelineRepository.QueryAsync(x => x.AppId == appId && x.Env == env)) + .OrderByDescending(x => x.Version).FirstOrDefault(); - // Remove versions newer than the rollback target. - var configPublishedConfigs = await configPublishedRepository.QueryAsync(x => x.AppId == appId && x.Env == env && x.Version > version); - await configPublishedRepository.DeleteAsync(configPublishedConfigs); - // Ensure the restored items are marked as published. - foreach (var item in publishedConfigs) - { - item.Status = ConfigStatus.Enabled; - await configPublishedRepository.UpdateAsync(item); - } + if (latest.Id == publishTimelineId) + // Already at the desired version; no rollback required. + return true; - // Remove publish timeline entries newer than the target version. - var deletePublishTimeLineItems = await publishTimelineRepository.QueryAsync(x => x.AppId == appId && x.Env == env && x.Version > version); - await publishTimelineRepository.DeleteAsync(deletePublishTimeLineItems); - var deletePublishDetailItems = await publishDetailRepository.QueryAsync(x => x.AppId == appId && x.Env == env && x.Version > version); - await publishDetailRepository.DeleteAsync(deletePublishDetailItems); + var publishedConfigs = await configPublishedRepository + .QueryAsync(x => x.AppId == appId && x.Version == version && x.Env == env); + var currentConfigs = await configRepository + .QueryAsync(x => x.AppId == appId && x.Status == ConfigStatus.Enabled && x.Env == env); - await uow?.SaveChangesAsync(); + // Soft delete all current configurations. + foreach (var item in currentConfigs) item.Status = ConfigStatus.Deleted; - ClearAppPublishedConfigsMd5Cache(appId, env); - ClearAppPublishedConfigsMd5CacheWithInheritance(appId, env); - ClearAppPublishTimelineVirtualIdCache(appId,env); + await configRepository.UpdateAsync(currentConfigs); + // Enable published items that match by configuration identifier. + var now = DateTime.Now; + foreach (var item in publishedConfigs) + { + var config = (await configRepository.QueryAsync(x => x.AppId == appId && x.Id == item.ConfigId)) + .FirstOrDefault(); + config.Status = ConfigStatus.Enabled; + config.Value = item.Value; + config.UpdateTime = now; + config.EditStatus = EditStatus.Commit; + config.OnlineStatus = OnlineStatus.Online; - return true; + await configRepository.UpdateAsync(config); } - public async Task EnvSync(string appId, string currentEnv, List toEnvs) + // Remove versions newer than the rollback target. + var configPublishedConfigs = + await configPublishedRepository.QueryAsync(x => x.AppId == appId && x.Env == env && x.Version > version); + await configPublishedRepository.DeleteAsync(configPublishedConfigs); + // Ensure the restored items are marked as published. + foreach (var item in publishedConfigs) { - var currentEnvConfigs = await this.GetByAppIdAsync(appId, currentEnv); + item.Status = ConfigStatus.Enabled; + await configPublishedRepository.UpdateAsync(item); + } - foreach (var env in toEnvs) + // Remove publish timeline entries newer than the target version. + var deletePublishTimeLineItems = + await publishTimelineRepository.QueryAsync(x => x.AppId == appId && x.Env == env && x.Version > version); + await publishTimelineRepository.DeleteAsync(deletePublishTimeLineItems); + var deletePublishDetailItems = + await publishDetailRepository.QueryAsync(x => x.AppId == appId && x.Env == env && x.Version > version); + await publishDetailRepository.DeleteAsync(deletePublishDetailItems); + + await uow?.SaveChangesAsync(); + + ClearAppPublishedConfigsMd5Cache(appId, env); + ClearAppPublishedConfigsMd5CacheWithInheritance(appId, env); + ClearAppPublishTimelineVirtualIdCache(appId, env); + + return true; + } + + public async Task EnvSync(string appId, string currentEnv, List toEnvs) + { + var currentEnvConfigs = await GetByAppIdAsync(appId, currentEnv); + + foreach (var env in toEnvs) + { + var envConfigs = await GetByAppIdAsync(appId, env); + var addRanges = new List(); + var updateRanges = new List(); + foreach (var currentEnvConfig in currentEnvConfigs) { - var envConfigs = await this.GetByAppIdAsync(appId, env); - var addRanges = new List(); - var updateRanges = new List(); - foreach (var currentEnvConfig in currentEnvConfigs) + var envConfig = envConfigs.FirstOrDefault(x => GenerateKey(x) == GenerateKey(currentEnvConfig)); + if (envConfig == null) { - var envConfig = envConfigs.FirstOrDefault(x => GenerateKey(x) == GenerateKey(currentEnvConfig)); - if (envConfig == null) - { - // No matching configuration exists in the target environment; create a new one. - currentEnvConfig.Id = Guid.NewGuid().ToString("N"); - currentEnvConfig.Env = env; - currentEnvConfig.CreateTime = DateTime.Now; - currentEnvConfig.UpdateTime = DateTime.Now; - currentEnvConfig.Status = ConfigStatus.Enabled; - currentEnvConfig.EditStatus = EditStatus.Add; - currentEnvConfig.OnlineStatus = OnlineStatus.WaitPublish; // Awaiting publish in target environment. - addRanges.Add(currentEnvConfig); - } - else - { - // Update the target environment when values differ. - if (envConfig.Value != currentEnvConfig.Value) - { - envConfig.UpdateTime = DateTime.Now; - envConfig.Value = currentEnvConfig.Value; - if (envConfig.EditStatus == EditStatus.Commit) - { - envConfig.EditStatus = EditStatus.Edit; - } - - envConfig.OnlineStatus = OnlineStatus.WaitPublish; - envConfig.Description = currentEnvConfig.Description; - updateRanges.Add(envConfig); - } - } + // No matching configuration exists in the target environment; create a new one. + currentEnvConfig.Id = Guid.NewGuid().ToString("N"); + currentEnvConfig.Env = env; + currentEnvConfig.CreateTime = DateTime.Now; + currentEnvConfig.UpdateTime = DateTime.Now; + currentEnvConfig.Status = ConfigStatus.Enabled; + currentEnvConfig.EditStatus = EditStatus.Add; + currentEnvConfig.OnlineStatus = OnlineStatus.WaitPublish; // Awaiting publish in target environment. + addRanges.Add(currentEnvConfig); } - - if (addRanges.Count > 0) + else { - await this.AddRangeAsync(addRanges, env); - } + // Update the target environment when values differ. + if (envConfig.Value != currentEnvConfig.Value) + { + envConfig.UpdateTime = DateTime.Now; + envConfig.Value = currentEnvConfig.Value; + if (envConfig.EditStatus == EditStatus.Commit) envConfig.EditStatus = EditStatus.Edit; - if (updateRanges.Count > 0) - { - await this.UpdateAsync(updateRanges, env); + envConfig.OnlineStatus = OnlineStatus.WaitPublish; + envConfig.Description = currentEnvConfig.Description; + updateRanges.Add(envConfig); + } } } - return true; - } + if (addRanges.Count > 0) await AddRangeAsync(addRanges, env); - /// - /// Convert configuration items into a key-value list. - /// - /// Application ID whose configuration should be converted. - /// Environment providing the configuration values. - /// List of key-value pairs built from configuration entries. - public async Task>> GetKvListAsync(string appId, string env) - { - var configs = await GetByAppIdAsync(appId, env); - var kvList = new List>(); - foreach (var config in configs) - { - kvList.Add(new KeyValuePair(GenerateKey(config), config.Value)); - } - - return kvList; + if (updateRanges.Count > 0) await UpdateAsync(updateRanges, env); } - private async Task SaveFromDictAsync(IDictionary dict, string appId, string env, bool isPatch) - { - using var uow = _uowAccessor(env); - using var configRepository = _configRepositoryAccessor(env); + return true; + } - configRepository.Uow = uow; + /// + /// Convert configuration items into a key-value list. + /// + /// Application ID whose configuration should be converted. + /// Environment providing the configuration values. + /// List of key-value pairs built from configuration entries. + public async Task>> GetKvListAsync(string appId, string env) + { + var configs = await GetByAppIdAsync(appId, env); + var kvList = new List>(); + foreach (var config in configs) kvList.Add(new KeyValuePair(GenerateKey(config), config.Value)); - uow?.Begin(); + return kvList; + } - var currentConfigs = await configRepository - .QueryAsync(x => x.AppId == appId && x.Env == env && x.Status == ConfigStatus.Enabled); - var addConfigs = new List(); - var updateConfigs = new List(); - var deleteConfigs = new List(); + /// + /// Persist configuration items from a JSON string. + /// + /// Serialized configuration payload to import. + /// Application ID that owns the configuration. + /// Environment where the configuration should be saved. + /// Indicates whether the import should merge into existing entries. + /// True when the JSON content is processed successfully. + /// + public async Task SaveJsonAsync(string json, string appId, string env, bool isPatch) + { + if (string.IsNullOrEmpty(json)) throw new ArgumentNullException("json"); - var now = DateTime.Now; + var byteArray = Encoding.UTF8.GetBytes(json); + using var stream = new MemoryStream(byteArray); + var dict = JsonConfigurationFileParser.Parse(stream); - foreach (var kv in dict) - { - var key = kv.Key; - var value = kv.Value; - var config = currentConfigs.FirstOrDefault(x => GenerateKey(x) == key); - if (config == null) - { - var gk = SplitJsonKey(key); - addConfigs.Add(new Config - { - Id = Guid.NewGuid().ToString("N"), - AppId = appId, - Env = env, - Key = gk.Item2, - Group = gk.Item1, - Value = value, - CreateTime = now, - Status = ConfigStatus.Enabled, - EditStatus = EditStatus.Add, - OnlineStatus = OnlineStatus.WaitPublish - }); - } - else if (config.Value != kv.Value) - { - config.Value = value; - config.UpdateTime = now; - if (config.OnlineStatus == OnlineStatus.Online) - { - config.EditStatus = EditStatus.Edit; - config.OnlineStatus = OnlineStatus.WaitPublish; - } - else - { - if (config.EditStatus == EditStatus.Add) - { - //do nothing - } - - if (config.EditStatus == EditStatus.Edit) - { - //do nothing - } - - if (config.EditStatus == EditStatus.Deleted) - { - // Previously marked as deleted; revert to edited state. - config.EditStatus = EditStatus.Edit; - } - } + return await SaveFromDictAsync(dict, appId, env, isPatch); + } - updateConfigs.Add(config); - } - } + public (bool, string) ValidateKvString(string kvStr) + { + var sr = new StringReader(kvStr); + var row = 0; + var dict = new Dictionary(); + while (true) + { + var line = sr.ReadLine(); + if (line == null) break; - if (!isPatch)// In full update mode remove missing configurations; patch mode preserves them. - { - var keys = dict.Keys.ToList(); - foreach (var item in currentConfigs) - { - var key = GenerateKey(item); - if (!keys.Contains(key)) - { - item.EditStatus = EditStatus.Deleted; - item.OnlineStatus = OnlineStatus.WaitPublish; - deleteConfigs.Add(item); - } - } - } + row++; + // Each line must contain an equals sign. + if (line.IndexOf('=') < 0) return (false, $"第 {row} 行缺少等号。"); - if (addConfigs.Any()) - { - await configRepository.InsertAsync(addConfigs); - } + var index = line.IndexOf('='); + var key = line.Substring(0, index); + if (dict.ContainsKey(key)) return (false, $"键 {key} 重复。"); - if (updateConfigs.Any()) - { - await configRepository.UpdateAsync(updateConfigs); - } + dict.Add(key, ""); + } - if (deleteConfigs.Any()) - { - await configRepository.UpdateAsync(deleteConfigs); - } + return (true, ""); + } - await uow?.SaveChangesAsync(); + public void ClearCache() + { + if (_memoryCache != null && _memoryCache is MemoryCache memCache) memCache.Compact(1.0); + } - return true; - } + public async Task SaveKvListAsync(string kvString, string appId, string env, bool isPatch) + { + if (kvString == null) throw new ArgumentNullException(nameof(kvString)); - /// - /// Persist configuration items from a JSON string. - /// - /// Serialized configuration payload to import. - /// Application ID that owns the configuration. - /// Environment where the configuration should be saved. - /// Indicates whether the import should merge into existing entries. - /// True when the JSON content is processed successfully. - /// - public async Task SaveJsonAsync(string json, string appId, string env, bool isPatch) + var sr = new StringReader(kvString); + var dict = new Dictionary(); + while (true) { - if (string.IsNullOrEmpty(json)) - { - throw new ArgumentNullException("json"); - } + var line = sr.ReadLine(); + if (line == null) break; + + var index = line.IndexOf('='); + if (index < 0) continue; - byte[] byteArray = Encoding.UTF8.GetBytes(json); - using var stream = new MemoryStream(byteArray); - var dict = JsonConfigurationFileParser.Parse(stream); + var key = line.Substring(0, index); + var val = line.Substring(index + 1, line.Length - index - 1); - return await SaveFromDictAsync(dict, appId, env, isPatch); + dict.Add(key, val); } - public (bool, string) ValidateKvString(string kvStr) - { - StringReader sr = new StringReader(kvStr); - int row = 0; - var dict = new Dictionary(); - while (true) - { - var line = sr.ReadLine(); - if (line == null) - { - break; - } + return await SaveFromDictAsync(dict, appId, env, isPatch); + } - row++; - // Each line must contain an equals sign. - if (line.IndexOf('=') < 0) - { - return (false, $"第 {row} 行缺少等号。"); - } + /// + /// Generate the virtual id representing the last publish timeline node of the app and its inherited apps. + /// + /// Application ID for which to gather publish timeline information. + /// Environment whose publish timeline should be inspected. + /// Composite identifier built from the latest publish timeline nodes. + public async Task GetLastPublishTimelineVirtualIdAsync(string appId, string env) + { + using var publishTimelineRepository = _publishTimelineRepositoryAccsssor(env); - var index = line.IndexOf('='); - var key = line.Substring(0, index); - if (dict.ContainsKey(key)) - { - return (false, $"键 {key} 重复。"); - } + var apps = new List(); + var inheritanceApps = await _appService.GetInheritancedAppsAsync(appId); + for (var i = 0; i < inheritanceApps.Count; i++) + if (inheritanceApps[i].Enabled) + apps.Add(inheritanceApps[i].Id); // Append inherited applications in order. - dict.Add(key, ""); - } + apps.Add(appId); // Add the current application last. - return (true, ""); - } + var ids = new List(); - public void ClearCache() + foreach (var app in apps) { - if (_memoryCache != null && _memoryCache is MemoryCache memCache) - { - memCache.Compact(1.0); - } + var id = await publishTimelineRepository.GetLastPublishTimelineNodeIdAsync(app, env); + ids.Add(id); } - public async Task SaveKvListAsync(string kvString, string appId, string env, bool isPatch) - { - if (kvString == null) - { - throw new ArgumentNullException(nameof(kvString)); - } + return string.Join('|', ids); + } - StringReader sr = new StringReader(kvString); - var dict = new Dictionary(); - while (true) - { - var line = sr.ReadLine(); - if (line == null) - { - break; - } + /// + /// Generate the virtual id representing the last publish timeline node of the app and its inherited apps, with cache. + /// + /// Application ID for which to gather publish timeline information. + /// Environment whose publish timeline should be inspected. + /// Composite identifier built from the latest publish timeline nodes. + public async Task GetLastPublishTimelineVirtualIdAsyncWithCache(string appId, string env) + { + var cacheKey = AppPublishTimelineVirtualIdCacheKey(appId, env); + if (_memoryCache != null && _memoryCache.TryGetValue(cacheKey, out string vId)) return vId; - var index = line.IndexOf('='); - if (index < 0) - { - continue; - } + vId = await GetLastPublishTimelineVirtualIdAsync(appId, env); - var key = line.Substring(0, index); - var val = line.Substring(index + 1, line.Length - index - 1); + var cacheOp = new MemoryCacheEntryOptions() + .SetAbsoluteExpiration(TimeSpan.FromSeconds(60)); + _memoryCache?.Set(cacheKey, vId, cacheOp); - dict.Add(key, val); - } + return vId; + } - return await SaveFromDictAsync(dict, appId, env, isPatch); - } + public async Task CountEnabledConfigsAsync(string env) + { + // Count all configurations in the specified environment. + using var repository = _configRepositoryAccessor(env); + var q = await repository.QueryAsync(c => c.Status == ConfigStatus.Enabled && c.Env == env); - private (string, string) SplitJsonKey(string key) + return q.Count; + } + + public string GenerateKey(ConfigPublished config) + { + if (string.IsNullOrEmpty(config.Group)) return config.Key; + + return $"{config.Group}:{config.Key}"; + } + + private string AppPublishedConfigsMd5CacheKey(string appId, string env) + { + return $"ConfigService_AppPublishedConfigsMd5Cache_{appId}_{env}"; + } + + private string AppPublishedConfigsMd5CacheKeyWithInheritance(string appId, string env) + { + return $"ConfigService_AppPublishedConfigsMd5CacheWithInheritance_{appId}_{env}"; + } + + private string AppPublishTimelineVirtualIdCacheKey(string appId, string env) + { + return $"ConfigService_AppPublishTimelineVirtualIdWithCache_{appId}_{env}"; + } + + private void ClearAppPublishedConfigsMd5Cache(string appId, string env) + { + var cacheKey = AppPublishedConfigsMd5CacheKey(appId, env); + _memoryCache?.Remove(cacheKey); + } + + private void ClearAppPublishedConfigsMd5CacheWithInheritance(string appId, string env) + { + var cacheKey = AppPublishedConfigsMd5CacheKeyWithInheritance(appId, env); + _memoryCache?.Remove(cacheKey); + } + + private void ClearAppPublishTimelineVirtualIdCache(string appId, string env) + { + var cacheKey = AppPublishTimelineVirtualIdCacheKey(appId, env); + _memoryCache?.Remove(cacheKey); + } + + private async Task SaveFromDictAsync(IDictionary dict, string appId, string env, bool isPatch) + { + using var uow = _uowAccessor(env); + using var configRepository = _configRepositoryAccessor(env); + + configRepository.Uow = uow; + + uow?.Begin(); + + var currentConfigs = await configRepository + .QueryAsync(x => x.AppId == appId && x.Env == env && x.Status == ConfigStatus.Enabled); + var addConfigs = new List(); + var updateConfigs = new List(); + var deleteConfigs = new List(); + + var now = DateTime.Now; + + foreach (var kv in dict) { - if (string.IsNullOrEmpty(key)) + var key = kv.Key; + var value = kv.Value; + var config = currentConfigs.FirstOrDefault(x => GenerateKey(x) == key); + if (config == null) { - throw new ArgumentNullException(nameof(key)); + var gk = SplitJsonKey(key); + addConfigs.Add(new Config + { + Id = Guid.NewGuid().ToString("N"), + AppId = appId, + Env = env, + Key = gk.Item2, + Group = gk.Item1, + Value = value, + CreateTime = now, + Status = ConfigStatus.Enabled, + EditStatus = EditStatus.Add, + OnlineStatus = OnlineStatus.WaitPublish + }); } - - var index = key.LastIndexOf(':'); - if (index >= 0) + else if (config.Value != kv.Value) { - var group = key.Substring(0, index); - var newkey = key.Substring(index + 1, key.Length - index - 1); + config.Value = value; + config.UpdateTime = now; + if (config.OnlineStatus == OnlineStatus.Online) + { + config.EditStatus = EditStatus.Edit; + config.OnlineStatus = OnlineStatus.WaitPublish; + } + else + { + if (config.EditStatus == EditStatus.Add) + { + //do nothing + } - return (group, newkey); - } + if (config.EditStatus == EditStatus.Edit) + { + //do nothing + } + + if (config.EditStatus == EditStatus.Deleted) + // Previously marked as deleted; revert to edited state. + config.EditStatus = EditStatus.Edit; + } - return ("", key); + updateConfigs.Add(config); + } } - /// - /// Generate the virtual id representing the last publish timeline node of the app and its inherited apps. - /// - /// Application ID for which to gather publish timeline information. - /// Environment whose publish timeline should be inspected. - /// Composite identifier built from the latest publish timeline nodes. - public async Task GetLastPublishTimelineVirtualIdAsync(string appId, string env) + if (!isPatch) // In full update mode remove missing configurations; patch mode preserves them. { - using var publishTimelineRepository = _publishTimelineRepositoryAccsssor(env); - - var apps = new List(); - var inheritanceApps = await _appService.GetInheritancedAppsAsync(appId); - for (int i = 0; i < inheritanceApps.Count; i++) + var keys = dict.Keys.ToList(); + foreach (var item in currentConfigs) { - if (inheritanceApps[i].Enabled) + var key = GenerateKey(item); + if (!keys.Contains(key)) { - apps.Add(inheritanceApps[i].Id as string); // Append inherited applications in order. + item.EditStatus = EditStatus.Deleted; + item.OnlineStatus = OnlineStatus.WaitPublish; + deleteConfigs.Add(item); } } + } - apps.Add(appId); // Add the current application last. + if (addConfigs.Any()) await configRepository.InsertAsync(addConfigs); - var ids = new List(); + if (updateConfigs.Any()) await configRepository.UpdateAsync(updateConfigs); - foreach (var app in apps) - { - var id = await publishTimelineRepository.GetLastPublishTimelineNodeIdAsync(app, env); - ids.Add(id); - } + if (deleteConfigs.Any()) await configRepository.UpdateAsync(deleteConfigs); - return string.Join('|', ids); - } + await uow?.SaveChangesAsync(); - /// - /// Generate the virtual id representing the last publish timeline node of the app and its inherited apps, with cache. - /// - /// Application ID for which to gather publish timeline information. - /// Environment whose publish timeline should be inspected. - /// Composite identifier built from the latest publish timeline nodes. - public async Task GetLastPublishTimelineVirtualIdAsyncWithCache(string appId, string env) - { - var cacheKey = AppPublishTimelineVirtualIdCacheKey(appId, env); - if (_memoryCache != null && _memoryCache.TryGetValue(cacheKey, out string vId)) - { - return vId; - } + return true; + } - vId = await GetLastPublishTimelineVirtualIdAsync(appId, env); + private (string, string) SplitJsonKey(string key) + { + if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key)); - var cacheOp = new MemoryCacheEntryOptions() - .SetAbsoluteExpiration(TimeSpan.FromSeconds(60)); - _memoryCache?.Set(cacheKey, vId, cacheOp); + var index = key.LastIndexOf(':'); + if (index >= 0) + { + var group = key.Substring(0, index); + var newkey = key.Substring(index + 1, key.Length - index - 1); - return vId; + return (group, newkey); } + + return ("", key); } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/EventRegisterService/ConfigStatusUpdateEventHandlersRegister.cs b/src/AgileConfig.Server.Service/EventRegisterService/ConfigStatusUpdateEventHandlersRegister.cs index acd241d0..9903a751 100644 --- a/src/AgileConfig.Server.Service/EventRegisterService/ConfigStatusUpdateEventHandlersRegister.cs +++ b/src/AgileConfig.Server.Service/EventRegisterService/ConfigStatusUpdateEventHandlersRegister.cs @@ -1,13 +1,14 @@ -using AgileConfig.Server.EventHandler; +using AgileConfig.Server.Common.EventBus; +using AgileConfig.Server.EventHandler; using AgileConfig.Server.IService; namespace AgileConfig.Server.Service.EventRegisterService; public class ConfigStatusUpdateEventHandlersRegister : IEventHandlerRegister { - private readonly Common.EventBus.ITinyEventBus _tinyEventBus; + private readonly ITinyEventBus _tinyEventBus; - public ConfigStatusUpdateEventHandlersRegister(Common.EventBus.ITinyEventBus tinyEventBus) + public ConfigStatusUpdateEventHandlersRegister(ITinyEventBus tinyEventBus) { _tinyEventBus = tinyEventBus; } diff --git a/src/AgileConfig.Server.Service/EventRegisterService/EventHandlerRegister.cs b/src/AgileConfig.Server.Service/EventRegisterService/EventHandlerRegister.cs index 44ca78b7..5960864d 100644 --- a/src/AgileConfig.Server.Service/EventRegisterService/EventHandlerRegister.cs +++ b/src/AgileConfig.Server.Service/EventRegisterService/EventHandlerRegister.cs @@ -3,14 +3,15 @@ namespace AgileConfig.Server.Service.EventRegisterService; -public class EventHandlerRegister(SystemEventHandlersRegister sysLogRegister, +public class EventHandlerRegister( + SystemEventHandlersRegister sysLogRegister, ConfigStatusUpdateEventHandlersRegister configStatusUpdateRegister, ServiceInfoStatusUpdateEventHandlersRegister serviceInfoStatusUpdateRegister - ) : IEventHandlerRegister +) : IEventHandlerRegister { - private readonly IEventHandlerRegister? _sysLogRegister = sysLogRegister; private readonly IEventHandlerRegister? _configStatusUpdateRegister = configStatusUpdateRegister; private readonly IEventHandlerRegister? _serviceInfoStatusUpdateRegister = serviceInfoStatusUpdateRegister; + private readonly IEventHandlerRegister? _sysLogRegister = sysLogRegister; public void Register() { diff --git a/src/AgileConfig.Server.Service/EventRegisterService/ServiceInfoStatusUpdateEventHandlersRegister.cs b/src/AgileConfig.Server.Service/EventRegisterService/ServiceInfoStatusUpdateEventHandlersRegister.cs index 6bd5394b..c6862968 100644 --- a/src/AgileConfig.Server.Service/EventRegisterService/ServiceInfoStatusUpdateEventHandlersRegister.cs +++ b/src/AgileConfig.Server.Service/EventRegisterService/ServiceInfoStatusUpdateEventHandlersRegister.cs @@ -1,9 +1,4 @@ -using System; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using Agile.Config.Protocol; +using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.EventHandler; using AgileConfig.Server.IService; @@ -11,10 +6,9 @@ namespace AgileConfig.Server.Service.EventRegisterService; public class ServiceInfoStatusUpdateEventHandlersRegister : IEventHandlerRegister { + private readonly ITinyEventBus _tinyEventBus; - private readonly Common.EventBus.ITinyEventBus _tinyEventBus; - - public ServiceInfoStatusUpdateEventHandlersRegister(Common.EventBus.ITinyEventBus tinyEventBus) + public ServiceInfoStatusUpdateEventHandlersRegister(ITinyEventBus tinyEventBus) { _tinyEventBus = tinyEventBus; } @@ -25,5 +19,4 @@ public void Register() _tinyEventBus.Register(); _tinyEventBus.Register(); } - } \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/EventRegisterService/SystemEventHandlersRegister.cs b/src/AgileConfig.Server.Service/EventRegisterService/SystemEventHandlersRegister.cs index 33602612..1a2805ff 100644 --- a/src/AgileConfig.Server.Service/EventRegisterService/SystemEventHandlersRegister.cs +++ b/src/AgileConfig.Server.Service/EventRegisterService/SystemEventHandlersRegister.cs @@ -30,6 +30,5 @@ public void Register() tinyEventBus.Register(); tinyEventBus.Register(); tinyEventBus.Register(); - } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/JwtService.cs b/src/AgileConfig.Server.Service/JwtService.cs index f29f03c3..d7b47a3e 100644 --- a/src/AgileConfig.Server.Service/JwtService.cs +++ b/src/AgileConfig.Server.Service/JwtService.cs @@ -10,10 +10,11 @@ namespace AgileConfig.Server.Service; /// -/// JWT-related service operations. +/// JWT-related service operations. /// public class JwtService(ISysInitRepository sysInitRepository) : IJwtService { + private string _secretKey; // static JwtService() // { // // Ensure a secret key exists in the database when initializing. @@ -21,57 +22,48 @@ public class JwtService(ISysInitRepository sysInitRepository) : IJwtService // settingService.TryInitJwtSecret(); // } - public string Issuer => Global.Config["JwtSetting:Issuer"]; - public string Audience => Global.Config["JwtSetting:Audience"]; - public int ExpireSeconds => int.Parse(Global.Config["JwtSetting:ExpireSeconds"]); + public string Issuer => Global.Config["JwtSetting:Issuer"]; + public string Audience => Global.Config["JwtSetting:Audience"]; + public int ExpireSeconds => int.Parse(Global.Config["JwtSetting:ExpireSeconds"]); - private string _secretKey; - public string GetSecurityKey() + public string GetSecurityKey() { - if (!string.IsNullOrEmpty(_secretKey)) - { - return _secretKey; - } - + if (!string.IsNullOrEmpty(_secretKey)) return _secretKey; + _secretKey = Global.Config["JwtSetting:SecurityKey"]; - if (!string.IsNullOrEmpty(_secretKey)) - { - return _secretKey; - } + if (!string.IsNullOrEmpty(_secretKey)) return _secretKey; //using var settingService = new SettingService(new FreeSqlContext(FreeSQL.Instance)); _secretKey = sysInitRepository.GetJwtTokenSecret(); - if (string.IsNullOrEmpty(_secretKey)) - { - throw new ArgumentNullException($"No JwtSetting SecurityKey"); - } - + if (string.IsNullOrEmpty(_secretKey)) throw new ArgumentNullException("No JwtSetting SecurityKey"); + return _secretKey; } - public string GetToken(string userId, string userName, bool isAdmin) + public string GetToken(string userId, string userName, bool isAdmin) { // Create user claims; add more information as needed. - var claims = new Claim[] + var claims = new[] { new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim("id", userId, ClaimValueTypes.String), // User identifier. new Claim("username", userName, ClaimValueTypes.String), // User name. - new Claim("admin", isAdmin.ToString() ,ClaimValueTypes.Boolean) // Whether the user is an administrator. + new Claim("admin", isAdmin.ToString(), ClaimValueTypes.Boolean) // Whether the user is an administrator. }; var key = Encoding.UTF8.GetBytes(GetSecurityKey()); // Create the JWT token. var token = new JwtSecurityToken( - issuer: Issuer, - audience: Audience, - signingCredentials: new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature), + Issuer, + Audience, + signingCredentials: new SigningCredentials(new SymmetricSecurityKey(key), + SecurityAlgorithms.HmacSha256Signature), claims: claims, notBefore: DateTime.Now, expires: DateTime.Now.AddSeconds(ExpireSeconds) ); - string jwtToken = new JwtSecurityTokenHandler().WriteToken(token); + var jwtToken = new JwtSecurityTokenHandler().WriteToken(token); return jwtToken; } diff --git a/src/AgileConfig.Server.Service/PermissionService.cs b/src/AgileConfig.Server.Service/PermissionService.cs index 44363908..ee10714f 100644 --- a/src/AgileConfig.Server.Service/PermissionService.cs +++ b/src/AgileConfig.Server.Service/PermissionService.cs @@ -1,288 +1,117 @@ -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.IService; using System.Collections.Generic; -using System.Threading.Tasks; using System.Linq; +using System.Threading.Tasks; using AgileConfig.Server.Data.Abstraction; -using AgileConfig.Server.Common; -using System.Text.Json; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.IService; + +namespace AgileConfig.Server.Service; -namespace AgileConfig.Server.Service +public class PermissionService : IPermissionService { - public class PermissionService : IPermissionService - { - private readonly IUserRoleRepository _userRoleRepository; - private readonly IRoleDefinitionRepository _roleDefinitionRepository; + private readonly IAppRepository _appRepository; + private readonly IFunctionRepository _functionRepository; + private readonly IRoleDefinitionRepository _roleDefinitionRepository; + private readonly IRoleFunctionRepository _roleFunctionRepository; private readonly IUserAppAuthRepository _userAppAuthRepository; - private readonly IAppRepository _appRepository; + private readonly IUserRoleRepository _userRoleRepository; - public PermissionService( - IUserRoleRepository userRoleRepository, - IRoleDefinitionRepository roleDefinitionRepository, - IUserAppAuthRepository userAppAuthRepository, + public PermissionService( + IUserRoleRepository userRoleRepository, + IRoleDefinitionRepository roleDefinitionRepository, + IRoleFunctionRepository roleFunctionRepository, + IFunctionRepository functionRepository, + IUserAppAuthRepository userAppAuthRepository, IAppRepository appRepository) - { - _userRoleRepository = userRoleRepository; - _roleDefinitionRepository = roleDefinitionRepository; - _userAppAuthRepository = userAppAuthRepository; - _appRepository = appRepository; - } - - private static readonly List Template_SuperAdminPermissions = - [ - Functions.App_Add, - Functions.App_Delete, - Functions.App_Edit, - Functions.App_Auth, - - Functions.Config_Add, - Functions.Config_Delete, - Functions.Config_Edit, - Functions.Config_Offline, - Functions.Config_Publish, - - Functions.Node_Add, - Functions.Node_Delete, - -Functions.Client_Disconnect, - - Functions.User_Add, - Functions.User_Edit, - Functions.User_Delete, - Functions.Role_Add, - Functions.Role_Edit, - Functions.Role_Delete - ]; - - private static readonly List Template_NormalAdminPermissions = - [ - Functions.App_Add, - Functions.Node_Add, - Functions.Node_Delete, - Functions.Client_Disconnect, - - Functions.User_Add, - Functions.User_Edit, - Functions.User_Delete, - Functions.Role_Add, - Functions.Role_Edit, - Functions.Role_Delete, - - "APP_{0}_" + Functions.App_Delete, - "APP_{0}_" + Functions.App_Edit, - "APP_{0}_" + Functions.App_Auth, - - "APP_{0}_" + Functions.Config_Add, - "APP_{0}_" + Functions.Config_Delete, - "APP_{0}_" + Functions.Config_Edit, - "APP_{0}_" + Functions.Config_Offline, - "APP_{0}_" + Functions.Config_Publish - ]; - - private static readonly List Template_NormalUserPermissions_Edit = - [ - "APP_{0}_" + Functions.Config_Add, - "APP_{0}_" + Functions.Config_Delete, - "APP_{0}_" + Functions.Config_Edit - ]; - - private static readonly List Template_NormalUserPermissions_Publish = - [ - "APP_{0}_" + Functions.Config_Offline, - "APP_{0}_" + Functions.Config_Publish - ]; - - private async Task> GetAdminUserFunctions(string userId) - { - var userFunctions = new List(); - // Retrieve applications where the user is an administrator. - var adminApps = await GetUserAdminApps(userId); - Template_NormalAdminPermissions.Where(x => !x.StartsWith("APP_")).ToList().ForEach( - key => { - userFunctions.Add(key); - } - ); - Template_NormalUserPermissions_Edit.Where(x => !x.StartsWith("APP_")).ToList().ForEach( - key => { - userFunctions.Add(key); - } - ); - Template_NormalUserPermissions_Publish.Where(x => !x.StartsWith("APP_")).ToList().ForEach( - key => { - userFunctions.Add(key); - } - ); - foreach (var app in adminApps) - { - foreach (var temp in Template_NormalAdminPermissions) - { - if (temp.StartsWith("APP_{0}_")) - { - userFunctions.Add(string.Format(temp, app.Id)); - } - } - } - //EditConfigPermissionKey - var editPermissionApps = await GetUserAuthApp(userId, EditConfigPermissionKey); - foreach (var app in editPermissionApps) { - foreach (var temp in Template_NormalUserPermissions_Edit) - { - if (temp.StartsWith("APP_{0}_")) - { - userFunctions.Add(string.Format(temp, app.Id)); - } - } - } - //PublishConfigPermissionKey - var publishPermissionApps = await GetUserAuthApp(userId, PublishConfigPermissionKey); - foreach (var app in publishPermissionApps) - { - foreach (var temp in Template_NormalUserPermissions_Publish) - { - if (temp.StartsWith("APP_{0}_")) - { - userFunctions.Add(string.Format(temp, app.Id)); - } - } - } - - return userFunctions; - } - - private async Task> GetNormalUserFunctions(string userId) - { - var userFunctions = new List(); - //EditConfigPermissionKey - var editPermissionApps = await GetUserAuthApp(userId, EditConfigPermissionKey); - foreach (var app in editPermissionApps) - { - foreach (var temp in Template_NormalUserPermissions_Edit) - { - if (!temp.StartsWith("APP_")) - { - userFunctions.Add(temp); - } - if (temp.StartsWith("APP_{0}_")) - { - userFunctions.Add(string.Format(temp, app.Id)); - } + _userRoleRepository = userRoleRepository; + _roleDefinitionRepository = roleDefinitionRepository; + _roleFunctionRepository = roleFunctionRepository; + _functionRepository = functionRepository; + _userAppAuthRepository = userAppAuthRepository; + _appRepository = appRepository; } - } - //PublishConfigPermissionKey - var publishPermissionApps = await GetUserAuthApp(userId, PublishConfigPermissionKey); - foreach (var app in publishPermissionApps) - { - foreach (var temp in Template_NormalUserPermissions_Publish) - { - if (!temp.StartsWith("APP_")) - { - userFunctions.Add(temp); - } - if (temp.StartsWith("APP_{0}_")) - { - userFunctions.Add(string.Format(temp, app.Id)); - } - } - } - return userFunctions; - } - - /// - /// Retrieve the permission template for a user based on roles. - /// - /// Identifier of the user requesting permissions. - /// List of permission keys granted to the user. - public async Task> GetUserPermission(string userId) - { - var userRoles = await _userRoleRepository.QueryAsync(x => x.UserId == userId); - var roleIds = userRoles.Select(x => x.RoleId).Distinct().ToList(); - if (!roleIds.Any()) - { - return new List(); - } - var roleDefinitions = await _roleDefinitionRepository.QueryAsync(x => roleIds.Contains(x.Id)); - var systemRoles = roleDefinitions.Where(r => r.IsSystem).ToList(); - var customRoles = roleDefinitions.Where(r => !r.IsSystem).ToList(); - - var customFunctions = customRoles.SelectMany(GetRoleFunctions).ToList(); + /// + /// Retrieve the permission codes for a user based on roles. + /// + /// Identifier of the user requesting permissions. + /// List of permission codes granted to the user. + public async Task> GetUserPermission(string userId) + { + var userRoles = await _userRoleRepository.QueryAsync(x => x.UserId == userId); + var roleIds = userRoles.Select(x => x.RoleId).Distinct().ToList(); + if (!roleIds.Any()) return new List(); - if (systemRoles.Any(r => r.Id == SystemRoleConstants.SuperAdminId)) - { - return Template_SuperAdminPermissions.Concat(customFunctions).Distinct().ToList(); - } + // Get role-function mappings for all user roles + var roleFunctions = await _roleFunctionRepository.QueryAsync(x => roleIds.Contains(x.RoleId)); + var functionIds = roleFunctions.Select(rf => rf.FunctionId).Distinct().ToList(); - var userFunctions = new List(); - if (systemRoles.Any(r => r.Id == SystemRoleConstants.AdminId)) - { - userFunctions.AddRange(await GetAdminUserFunctions(userId)); - } +// Get function entities and return their codes + var functions = await _functionRepository.QueryAsync(f => functionIds.Contains(f.Id)); + var functionCodes = functions.Select(f => f.Code).Distinct().ToList(); - if (systemRoles.Any(r => r.Id == SystemRoleConstants.OperatorId)) - { - userFunctions.AddRange(await GetNormalUserFunctions(userId)); - } + return functionCodes; + } - userFunctions.AddRange(customFunctions); + /// + /// Retrieve the categories of permissions granted to a user. + /// + /// Identifier of the user requesting categories. + /// List of distinct categories granted to the user. + public async Task> GetUserCategories(string userId) + { + var userRoles = await _userRoleRepository.QueryAsync(x => x.UserId == userId); + var roleIds = userRoles.Select(x => x.RoleId).Distinct().ToList(); + if (!roleIds.Any()) return new List(); + + // Get role-function mappings for all user roles + var roleFunctions = await _roleFunctionRepository.QueryAsync(x => roleIds.Contains(x.RoleId)); + var functionIds = roleFunctions.Select(rf => rf.FunctionId).Distinct().ToList(); + + // Get function entities and return their categories + var functions = await _functionRepository.QueryAsync(f => functionIds.Contains(f.Id)); + var categories = functions + .Where(f => !string.IsNullOrWhiteSpace(f.Category)) + .Select(f => f.Category) + .Distinct() + .ToList(); + + return categories; + } - return userFunctions.Distinct().ToList(); - } + public string EditConfigPermissionKey => "EDIT_CONFIG"; - private static IEnumerable GetRoleFunctions(Role role) - { - if (role == null || string.IsNullOrWhiteSpace(role.FunctionsJson)) - { - return Enumerable.Empty(); - } + public string PublishConfigPermissionKey => "PUBLISH_CONFIG"; - try + /// + /// Retrieve applications where the user has been explicitly authorized. + /// + /// Identifier of the user whose application authorizations are requested. + /// Permission key used to filter authorized applications. + /// List of applications the user can access for the specified permission. + private async Task> GetUserAuthApp(string userId, string authPermissionKey) { - var functions = JsonSerializer.Deserialize>(role.FunctionsJson); - return functions ?? Enumerable.Empty(); - } - catch + var apps = new List(); + var userAuths = + await _userAppAuthRepository.QueryAsync(x => x.UserId == userId && x.Permission == authPermissionKey); + foreach (var appAuth in userAuths) { - return Enumerable.Empty(); - } + var app = await _appRepository.GetAsync(appAuth.AppId); + if (app != null) apps.Add(app); } - /// - /// Retrieve applications where the user has been explicitly authorized. - /// - /// Identifier of the user whose application authorizations are requested. - /// Permission key used to filter authorized applications. - /// List of applications the user can access for the specified permission. - private async Task> GetUserAuthApp(string userId, string authPermissionKey) - { - var apps = new List(); - var userAuths = - await _userAppAuthRepository.QueryAsync(x => x.UserId == userId && x.Permission == authPermissionKey); - foreach (var appAuth in userAuths) - { - var app = await _appRepository.GetAsync(appAuth.AppId); - if (app!= null) - { - apps.Add(app); - } + return apps; } - return apps; - } - - /// - /// Retrieve applications managed by the user. - /// - /// Identifier of the user who administers the applications. - /// List of applications where the user is the administrator. - private async Task> GetUserAdminApps(string userId) - { - return await _appRepository.QueryAsync(x => x.AppAdmin == userId); - } - - public string EditConfigPermissionKey => "EDIT_CONFIG"; - - public string PublishConfigPermissionKey => "PUBLISH_CONFIG"; + /// + /// Retrieve applications managed by the user. + /// + /// Identifier of the user who administers the applications. + /// List of applications where the user is the administrator. + private async Task> GetUserAdminApps(string userId) + { + return await _appRepository.QueryAsync(x => x.AppAdmin == userId); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/RegisterCenterService.cs b/src/AgileConfig.Server.Service/RegisterCenterService.cs index ec916eaf..d392d9d5 100644 --- a/src/AgileConfig.Server.Service/RegisterCenterService.cs +++ b/src/AgileConfig.Server.Service/RegisterCenterService.cs @@ -1,168 +1,156 @@ -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.IService; -using System; +using System; using System.Linq; using System.Threading.Tasks; -using AgileConfig.Server.Data.Abstraction; -using Microsoft.Extensions.Logging; using AgileConfig.Server.Common.EventBus; +using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Event; +using AgileConfig.Server.IService; +using Microsoft.Extensions.Logging; -namespace AgileConfig.Server.Service +namespace AgileConfig.Server.Service; + +public class RegisterCenterService : IRegisterCenterService { - public class RegisterCenterService : IRegisterCenterService + private readonly ILogger _logger; + private readonly IServiceInfoRepository _serviceInfoRepository; + private readonly IServiceInfoService _serviceInfoService; + private readonly ITinyEventBus _tinyEventBus; + + public RegisterCenterService( + IServiceInfoRepository serviceInfoRepository, + IServiceInfoService serviceInfoService, + ITinyEventBus tinyEventBus, + ILogger logger) { - private readonly IServiceInfoRepository _serviceInfoRepository; - private readonly ILogger _logger; - private readonly IServiceInfoService _serviceInfoService; - private readonly ITinyEventBus _tinyEventBus; - - public RegisterCenterService( - IServiceInfoRepository serviceInfoRepository, - IServiceInfoService serviceInfoService, - ITinyEventBus tinyEventBus, - ILogger logger) - { - _serviceInfoRepository = serviceInfoRepository; - _logger = logger; - _serviceInfoService = serviceInfoService; - _tinyEventBus = tinyEventBus; - } + _serviceInfoRepository = serviceInfoRepository; + _logger = logger; + _serviceInfoService = serviceInfoService; + _tinyEventBus = tinyEventBus; + } + + public async Task RegisterAsync(ServiceInfo serviceInfo) + { + if (serviceInfo == null) throw new ArgumentNullException(nameof(serviceInfo)); - public async Task RegisterAsync(ServiceInfo serviceInfo) + _logger.LogInformation("try to register service {0} {1}", serviceInfo.ServiceId, serviceInfo.ServiceName); + + //if exist + var oldEntity = (await _serviceInfoRepository.QueryAsync(x => x.ServiceId == serviceInfo.ServiceId)) + .FirstOrDefault(); + if (oldEntity != null) { - if (serviceInfo == null) - { - throw new ArgumentNullException(nameof(serviceInfo)); - } + oldEntity.RegisterTime = DateTime.Now; + oldEntity.Status = ServiceStatus.Healthy; + oldEntity.LastHeartBeat = DateTime.Now; + oldEntity.ServiceName = serviceInfo.ServiceName; + oldEntity.Ip = serviceInfo.Ip; + oldEntity.Port = serviceInfo.Port; + oldEntity.MetaData = serviceInfo.MetaData; + oldEntity.HeartBeatMode = serviceInfo.HeartBeatMode; + oldEntity.CheckUrl = serviceInfo.CheckUrl; + oldEntity.AlarmUrl = serviceInfo.AlarmUrl; + oldEntity.RegisterWay = serviceInfo.RegisterWay; + await _serviceInfoRepository.UpdateAsync(oldEntity); - _logger.LogInformation("try to register service {0} {1}", serviceInfo.ServiceId, serviceInfo.ServiceName); + _serviceInfoService.ClearCache(); - //if exist - var oldEntity = (await _serviceInfoRepository.QueryAsync(x => x.ServiceId == serviceInfo.ServiceId)).FirstOrDefault(); - if (oldEntity != null) - { - oldEntity.RegisterTime = DateTime.Now; - oldEntity.Status = ServiceStatus.Healthy; - oldEntity.LastHeartBeat = DateTime.Now; - oldEntity.ServiceName = serviceInfo.ServiceName; - oldEntity.Ip = serviceInfo.Ip; - oldEntity.Port = serviceInfo.Port; - oldEntity.MetaData = serviceInfo.MetaData; - oldEntity.HeartBeatMode = serviceInfo.HeartBeatMode; - oldEntity.CheckUrl = serviceInfo.CheckUrl; - oldEntity.AlarmUrl = serviceInfo.AlarmUrl; - oldEntity.RegisterWay = serviceInfo.RegisterWay; - await _serviceInfoRepository.UpdateAsync(oldEntity); + _logger.LogInformation("registered service {0} {1} successful .", serviceInfo.ServiceId, + serviceInfo.ServiceName); - _serviceInfoService.ClearCache(); + return oldEntity.Id; + } - _logger.LogInformation("registered service {0} {1} successful .", serviceInfo.ServiceId, - serviceInfo.ServiceName); + serviceInfo.RegisterTime = DateTime.Now; + serviceInfo.LastHeartBeat = DateTime.Now; + serviceInfo.Status = ServiceStatus.Healthy; + serviceInfo.Id = Guid.NewGuid().ToString("n"); - return oldEntity.Id; - } + await _serviceInfoRepository.InsertAsync(serviceInfo); - serviceInfo.RegisterTime = DateTime.Now; - serviceInfo.LastHeartBeat = DateTime.Now; - serviceInfo.Status = ServiceStatus.Healthy; - serviceInfo.Id = Guid.NewGuid().ToString("n"); + _serviceInfoService.ClearCache(); - await _serviceInfoRepository.InsertAsync(serviceInfo); + _logger.LogInformation("registered service {0} {1} successful .", serviceInfo.ServiceId, + serviceInfo.ServiceName); - _serviceInfoService.ClearCache(); + return serviceInfo.Id; + } - _logger.LogInformation("registered service {0} {1} successful .", serviceInfo.ServiceId, - serviceInfo.ServiceName); + public async Task UnRegisterAsync(string serviceUniqueId) + { + _logger.LogInformation("try to unregister service {0}", serviceUniqueId); - return serviceInfo.Id; - } + if (string.IsNullOrEmpty(serviceUniqueId)) throw new ArgumentNullException(nameof(serviceUniqueId)); - public async Task UnRegisterAsync(string serviceUniqueId) + var oldEntity = await _serviceInfoRepository.GetAsync(serviceUniqueId); + if (oldEntity == null) { - _logger.LogInformation("try to unregister service {0}", serviceUniqueId); + //if not exist + _logger.LogInformation("not find the service {0} .", serviceUniqueId); + return false; + } - if (string.IsNullOrEmpty(serviceUniqueId)) - { - throw new ArgumentNullException(nameof(serviceUniqueId)); - } + await _serviceInfoRepository.DeleteAsync(oldEntity); - var oldEntity = await _serviceInfoRepository.GetAsync(serviceUniqueId); - if (oldEntity == null) - { - //if not exist - _logger.LogInformation("not find the service {0} .", serviceUniqueId); - return false; - } + _serviceInfoService.ClearCache(); - await _serviceInfoRepository.DeleteAsync(oldEntity); + _logger.LogInformation("unregister service {0} {1} successful .", oldEntity.ServiceId, + oldEntity.ServiceName); - _serviceInfoService.ClearCache(); + return true; + } - _logger.LogInformation("unregister service {0} {1} successful .", oldEntity.ServiceId, - oldEntity.ServiceName); + public async Task UnRegisterByServiceIdAsync(string serviceId) + { + _logger.LogInformation("try to unregister service {0}", serviceId); - return true; - } + if (string.IsNullOrEmpty(serviceId)) throw new ArgumentNullException(nameof(serviceId)); - public async Task UnRegisterByServiceIdAsync(string serviceId) + var oldEntity = (await _serviceInfoRepository.QueryAsync(x => x.ServiceId == serviceId)).FirstOrDefault(); + if (oldEntity == null) { - _logger.LogInformation("try to unregister service {0}", serviceId); + //if not exist + _logger.LogInformation("not find the service {0} .", serviceId); + return false; + } - if (string.IsNullOrEmpty(serviceId)) - { - throw new ArgumentNullException(nameof(serviceId)); - } + await _serviceInfoRepository.DeleteAsync(oldEntity); - var oldEntity = (await _serviceInfoRepository.QueryAsync(x => x.ServiceId == serviceId)).FirstOrDefault(); - if (oldEntity == null) - { - //if not exist - _logger.LogInformation("not find the service {0} .", serviceId); - return false; - } + _serviceInfoService.ClearCache(); - await _serviceInfoRepository.DeleteAsync(oldEntity); + _logger.LogInformation("unregister service {0} {1} successful .", oldEntity.ServiceId, + oldEntity.ServiceName); - _serviceInfoService.ClearCache(); + return true; + } - _logger.LogInformation("unregister service {0} {1} successful .", oldEntity.ServiceId, - oldEntity.ServiceName); + public async Task ReceiveHeartbeatAsync(string serviceUniqueId) + { + var entity = await _serviceInfoRepository.GetAsync(serviceUniqueId); + if (entity == null) return false; - return true; - } + _logger.LogInformation("receive service {0} {1} heartbeat .", entity.ServiceId, entity.ServiceName); - public async Task ReceiveHeartbeatAsync(string serviceUniqueId) + if (entity.HeartBeatMode == "server") { - var entity = await _serviceInfoRepository.GetAsync(serviceUniqueId); - if (entity == null) - { - return false; - } + // When the heartbeat is triggered by the server, it is not used to determine online status. + } + else + { + var oldStatus = entity.Status; + entity.Status = ServiceStatus.Healthy; + entity.LastHeartBeat = DateTime.Now; - _logger.LogInformation("receive service {0} {1} heartbeat .", entity.ServiceId, entity.ServiceName); + await _serviceInfoRepository.UpdateAsync(entity); - if (entity.HeartBeatMode == "server") + if (oldStatus != ServiceStatus.Healthy) { - // When the heartbeat is triggered by the server, it is not used to determine online status. - } - else - { - var oldStatus = entity.Status; - entity.Status = ServiceStatus.Healthy; - entity.LastHeartBeat = DateTime.Now; - - await _serviceInfoRepository.UpdateAsync(entity); - - if (oldStatus != ServiceStatus.Healthy) - { - _serviceInfoService.ClearCache(); + _serviceInfoService.ClearCache(); - _tinyEventBus.Fire(new ServiceStatusUpdateEvent(entity.Id)); - } + _tinyEventBus.Fire(new ServiceStatusUpdateEvent(entity.Id)); } - - return true; } + + return true; } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/RemoteServerNodeProxy.cs b/src/AgileConfig.Server.Service/RemoteServerNodeProxy.cs index ff2275ff..30e8b05b 100644 --- a/src/AgileConfig.Server.Service/RemoteServerNodeProxy.cs +++ b/src/AgileConfig.Server.Service/RemoteServerNodeProxy.cs @@ -1,236 +1,225 @@ -using Agile.Config.Protocol; +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; +using Agile.Config.Protocol; using AgileConfig.Server.Common; using AgileConfig.Server.Common.RestClient; using AgileConfig.Server.Data.Entity; using AgileConfig.Server.IService; using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -namespace AgileConfig.Server.Service +namespace AgileConfig.Server.Service; + +public class RemoteServerNodeProxy : IRemoteServerNodeProxy { - public class RemoteServerNodeProxy : IRemoteServerNodeProxy + private static readonly Dictionary _modules = new() { - private readonly ILogger _logger; - private readonly IRestClient _restClient; - private readonly IServerNodeService _serverNodeService; - private readonly ISysLogService _sysLogService; - private static readonly Dictionary _modules = new() - { - { "r", "Registration Center" }, - { "c", "Configuration Center" } - }; + { "r", "Registration Center" }, + { "c", "Configuration Center" } + }; + + private readonly ILogger _logger; + private readonly IRestClient _restClient; + private readonly IServerNodeService _serverNodeService; + private readonly ISysLogService _sysLogService; + + public RemoteServerNodeProxy( + ILoggerFactory loggerFactory, + IRestClient restClient, + IServerNodeService serverNodeService, + ISysLogService sysLogService) + { + _logger = loggerFactory.CreateLogger(); + _restClient = restClient; + _serverNodeService = serverNodeService; + _sysLogService = sysLogService; + } - public RemoteServerNodeProxy( - ILoggerFactory loggerFactory, - IRestClient restClient, - IServerNodeService serverNodeService, - ISysLogService sysLogService) + public async Task AllClientsDoActionAsync(string address, WebsocketAction action) + { + var result = await FunctionUtil.TRYAsync(async () => { - _logger = loggerFactory.CreateLogger(); - _restClient = restClient; - _serverNodeService = serverNodeService; - _sysLogService = sysLogService; - } + var result = await _restClient.PostAsync(address + "/RemoteOP/AllClientsDoAction", action); + if ((bool)result.success) return true; - public async Task AllClientsDoActionAsync(string address, WebsocketAction action) + return false; + }, 5); + + _modules.TryGetValue(action.Module, out var moduleName); + await _sysLogService.AddSysLogAsync(new SysLog { - var result = await FunctionUtil.TRYAsync(async () => - { - dynamic result = await _restClient.PostAsync(address + "/RemoteOP/AllClientsDoAction", action); - if ((bool)result.success) - { - return true; - } + LogTime = DateTime.Now, + LogType = result ? SysLogType.Normal : SysLogType.Warn, + LogText = + $"Notified node [{address}] all clients: [{moduleName}] [{action.Action}] Response: {(result ? "Success" : "Failed")}" + }); - return false; - }, 5); + return result; + } - _modules.TryGetValue(action.Module, out var moduleName); - await _sysLogService.AddSysLogAsync(new SysLog - { - LogTime = DateTime.Now, - LogType = result ? SysLogType.Normal : SysLogType.Warn, - LogText = $"Notified node [{address}] all clients: [{moduleName}] [{action.Action}] Response: {(result ? "Success" : "Failed")}" - }); + public async Task AppClientsDoActionAsync(string address, string appId, string env, WebsocketAction action) + { + var result = await FunctionUtil.TRYAsync(async () => + { + var url = $"{address}/RemoteOP/AppClientsDoAction?appId={Uri.EscapeDataString(appId)}&env={env}"; + var result = await _restClient.PostAsync(url, action); + if ((bool)result.success) return true; - return result; - } + return false; + }, 5); - public async Task AppClientsDoActionAsync(string address, string appId, string env, WebsocketAction action) + _modules.TryGetValue(action.Module, out var moduleName); + await _sysLogService.AddSysLogAsync(new SysLog { - var result = await FunctionUtil.TRYAsync(async () => - { - var url = $"{address}/RemoteOP/AppClientsDoAction?appId={Uri.EscapeDataString(appId)}&env={env}"; - dynamic result = await _restClient.PostAsync(url, action); - if ((bool)result.success) - { - return true; - } - - return false; - }, 5); - - _modules.TryGetValue(action.Module, out var moduleName); - await _sysLogService.AddSysLogAsync(new SysLog - { - LogTime = DateTime.Now, - LogType = result ? SysLogType.Normal : SysLogType.Warn, - AppId = appId, - LogText = $"Notified node [{address}] app [{appId}] clients: [{moduleName}] [{action.Action}] Response: {(result ? "Success" : "Failed")}" - }); - return result; - } + LogTime = DateTime.Now, + LogType = result ? SysLogType.Normal : SysLogType.Warn, + AppId = appId, + LogText = + $"Notified node [{address}] app [{appId}] clients: [{moduleName}] [{action.Action}] Response: {(result ? "Success" : "Failed")}" + }); + return result; + } - public async Task OneClientDoActionAsync(string address, string clientId, WebsocketAction action) + public async Task OneClientDoActionAsync(string address, string clientId, WebsocketAction action) + { + var result = await FunctionUtil.TRYAsync(async () => { - var result = await FunctionUtil.TRYAsync(async () => - { - var url = $"{address}/RemoteOP/OneClientDoAction?clientId={clientId}"; - dynamic result = await _restClient.PostAsync(url, action); + var url = $"{address}/RemoteOP/OneClientDoAction?clientId={clientId}"; + var result = await _restClient.PostAsync(url, action); - return (bool)result.success; - }, 5); + return (bool)result.success; + }, 5); - _modules.TryGetValue(action.Module, out var moduleName); - await _sysLogService.AddSysLogAsync(new SysLog - { - LogTime = DateTime.Now, - LogType = result ? SysLogType.Normal : SysLogType.Warn, - LogText = $"Notified node [{address}] client [{clientId}]: [{moduleName}] [{action.Action}] Response: {(result ? "Success" : "Failed")}" - }); + _modules.TryGetValue(action.Module, out var moduleName); + await _sysLogService.AddSysLogAsync(new SysLog + { + LogTime = DateTime.Now, + LogType = result ? SysLogType.Normal : SysLogType.Warn, + LogText = + $"Notified node [{address}] client [{clientId}]: [{moduleName}] [{action.Action}] Response: {(result ? "Success" : "Failed")}" + }); - return result; - } + return result; + } - public async Task GetClientsReportAsync(string address) - { - if (string.IsNullOrEmpty(address)) + public async Task GetClientsReportAsync(string address) + { + if (string.IsNullOrEmpty(address)) + return new ClientInfos { - return new ClientInfos() - { - ClientCount = 0, - Infos = new List() - }; - } + ClientCount = 0, + Infos = new List() + }; - try - { - var url = address + "/report/Clients"; - - var clients = await _restClient.GetAsync(url); - if (clients != null) - { - clients.Infos?.ForEach(i => { i.Address = address; }); - return clients; - } - - return new ClientInfos() - { - ClientCount = 0, - Infos = new List() - }; - } - catch (Exception ex) + try + { + var url = address + "/report/Clients"; + + var clients = await _restClient.GetAsync(url); + if (clients != null) { - _logger?.LogError($"Try to get client infos from node {address} occur ERROR . ", ex); + clients.Infos?.ForEach(i => { i.Address = address; }); + return clients; } - return new ClientInfos() + return new ClientInfos { ClientCount = 0, Infos = new List() }; } + catch (Exception ex) + { + _logger?.LogError($"Try to get client infos from node {address} occur ERROR . ", ex); + } - public async Task TestEchoAsync(string address) + return new ClientInfos { - var node = await _serverNodeService.GetAsync(address); - try + ClientCount = 0, + Infos = new List() + }; + } + + public async Task TestEchoAsync(string address) + { + var node = await _serverNodeService.GetAsync(address); + try + { + var url = node.Id + "/home/echo"; + + using var resp = await _restClient.GetAsync(url); + + if (resp.StatusCode == HttpStatusCode.OK && await resp.Content.ReadAsStringAsync() == "ok") { - var url = node.Id + "/home/echo"; - - using var resp = await _restClient.GetAsync(url); - - if (resp.StatusCode == System.Net.HttpStatusCode.OK && (await resp.Content.ReadAsStringAsync()) == "ok") - { - node.LastEchoTime = DateTime.Now; - node.Status = NodeStatus.Online; - } - else - { - node.Status = NodeStatus.Offline; - } + node.LastEchoTime = DateTime.Now; + node.Status = NodeStatus.Online; } - catch (Exception e) + else { node.Status = NodeStatus.Offline; - _logger.LogInformation(e, "Try test node {0} echo , but fail .", node.Id); } - - if (node.Status == NodeStatus.Offline) - { - DateTime? time = node.LastEchoTime; - if (!time.HasValue) - { - time = node.CreateTime; - } - if (time.HasValue && (DateTime.Now - time.Value).TotalMinutes >= 30) - { - // Remove nodes that have not responded for over 30 minutes. - await _serverNodeService.DeleteAsync(address); - return; - } - } - - await _serverNodeService.UpdateAsync(node); } - - public Task TestEchoAsync() + catch (Exception e) { - return Task.Run(async () => - { - while (true) - { - var nodes = await _serverNodeService.GetAllNodesAsync(); - - foreach (var node in nodes) - { - await TestEchoAsync(node.Id); - } - - await Task.Delay(5000 * 1); - } - }); + node.Status = NodeStatus.Offline; + _logger.LogInformation(e, "Try test node {0} echo , but fail .", node.Id); } - public async Task ClearConfigServiceCache(string address) + if (node.Status == NodeStatus.Offline) { - try - { - var url = (address + "/RemoteOP/ClearConfigServiceCache"); - using var resp = await _restClient.PostAsync(url, null); - } - catch (Exception e) + var time = node.LastEchoTime; + if (!time.HasValue) time = node.CreateTime; + if (time.HasValue && (DateTime.Now - time.Value).TotalMinutes >= 30) { - _logger.LogError(e, "Try to clear node {0}'s config cache , but fail .", address); + // Remove nodes that have not responded for over 30 minutes. + await _serverNodeService.DeleteAsync(address); + return; } } - public async Task ClearServiceInfoCache(string address) + await _serverNodeService.UpdateAsync(node); + } + + public Task TestEchoAsync() + { + return Task.Run(async () => { - try + while (true) { - var url = (address + "/RemoteOP/ClearServiceInfoCache"); + var nodes = await _serverNodeService.GetAllNodesAsync(); - await _restClient.PostAsync(url, null); + foreach (var node in nodes) await TestEchoAsync(node.Id); + await Task.Delay(5000 * 1); } - catch (Exception e) - { - _logger.LogError(e, "Try to clear node {0}'s servicesinfo cache , but fail .", address); - } + }); + } + + public async Task ClearConfigServiceCache(string address) + { + try + { + var url = address + "/RemoteOP/ClearConfigServiceCache"; + using var resp = await _restClient.PostAsync(url, null); + } + catch (Exception e) + { + _logger.LogError(e, "Try to clear node {0}'s config cache , but fail .", address); + } + } + + public async Task ClearServiceInfoCache(string address) + { + try + { + var url = address + "/RemoteOP/ClearServiceInfoCache"; + + await _restClient.PostAsync(url, null); + } + catch (Exception e) + { + _logger.LogError(e, "Try to clear node {0}'s servicesinfo cache , but fail .", address); } } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/RoleService.cs b/src/AgileConfig.Server.Service/RoleService.cs index 7edb9c70..cadc1fb0 100644 --- a/src/AgileConfig.Server.Service/RoleService.cs +++ b/src/AgileConfig.Server.Service/RoleService.cs @@ -1,139 +1,122 @@ -using AgileConfig.Server.Data.Abstraction; -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.IService; using System; using System.Collections.Generic; using System.Linq; -using System.Text.Json; using System.Threading.Tasks; +using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.IService; + +namespace AgileConfig.Server.Service; -namespace AgileConfig.Server.Service +public class RoleService : IRoleService { - public class RoleService : IRoleService + private readonly IRoleDefinitionRepository _roleDefinitionRepository; + private readonly IRoleFunctionRepository _roleFunctionRepository; + private readonly IUserRoleRepository _userRoleRepository; + + public RoleService( + IRoleDefinitionRepository roleDefinitionRepository, + IUserRoleRepository userRoleRepository, + IRoleFunctionRepository roleFunctionRepository) { - private readonly IRoleDefinitionRepository _roleDefinitionRepository; - private readonly IUserRoleRepository _userRoleRepository; + _roleDefinitionRepository = roleDefinitionRepository; + _userRoleRepository = userRoleRepository; + _roleFunctionRepository = roleFunctionRepository; + } - public RoleService(IRoleDefinitionRepository roleDefinitionRepository, IUserRoleRepository userRoleRepository) - { - _roleDefinitionRepository = roleDefinitionRepository; - _userRoleRepository = userRoleRepository; - } + public async Task CreateAsync(Role role, IEnumerable functions) + { + if (role == null) throw new ArgumentNullException(nameof(role)); - public async Task CreateAsync(Role role, IEnumerable functions) - { - if (role == null) - { - throw new ArgumentNullException(nameof(role)); - } - - if (string.IsNullOrWhiteSpace(role.Id)) - { - role.Id = Guid.NewGuid().ToString("N"); - } - - if (await ExistsWithSameCode(role)) - { - throw new InvalidOperationException($"Role code '{role.Code}' already exists."); - } - - role.CreateTime = DateTime.Now; - role.FunctionsJson = SerializeFunctions(functions); - - await _roleDefinitionRepository.InsertAsync(role); - return role; - } + if (string.IsNullOrWhiteSpace(role.Id)) role.Id = Guid.NewGuid().ToString("N"); - public async Task DeleteAsync(string id) - { - var role = await _roleDefinitionRepository.GetAsync(id); - if (role == null) - { - return false; - } - - if (role.IsSystem) - { - throw new InvalidOperationException("System roles cannot be deleted."); - } - - var userRoles = await _userRoleRepository.QueryAsync(x => x.RoleId == id); - if (userRoles.Any()) - { - await _userRoleRepository.DeleteAsync(userRoles); - } - - await _roleDefinitionRepository.DeleteAsync(role); - return true; - } + role.CreateTime = DateTime.Now; - public async Task> GetAllAsync() - { - return await _roleDefinitionRepository.AllAsync(); - } + await _roleDefinitionRepository.InsertAsync(role); - public Task GetAsync(string id) + // Save role-function relationships + if (functions != null && functions.Any()) { - return _roleDefinitionRepository.GetAsync(id); + var roleFunctions = functions + .Where(f => !string.IsNullOrWhiteSpace(f)) + .Select(f => new RoleFunction + { + Id = Guid.NewGuid().ToString("N"), + RoleId = role.Id, + FunctionId = f, + CreateTime = DateTime.Now + }) + .ToList(); + + if (roleFunctions.Any()) await _roleFunctionRepository.InsertAsync(roleFunctions); } - public async Task GetByCodeAsync(string code) - { - var roles = await _roleDefinitionRepository.QueryAsync(x => x.Code == code); - return roles.FirstOrDefault(); - } + return role; + } - public async Task UpdateAsync(Role role, IEnumerable functions) - { - if (role == null) - { - throw new ArgumentNullException(nameof(role)); - } - - var dbRole = await _roleDefinitionRepository.GetAsync(role.Id); - if (dbRole == null) - { - return false; - } - - if (dbRole.IsSystem && !string.Equals(dbRole.Code, role.Code, StringComparison.OrdinalIgnoreCase)) - { - throw new InvalidOperationException("System role code cannot be changed."); - } - - if (!string.Equals(dbRole.Code, role.Code, StringComparison.OrdinalIgnoreCase) && await ExistsWithSameCode(role)) - { - throw new InvalidOperationException($"Role code '{role.Code}' already exists."); - } - - dbRole.Code = role.Code; - dbRole.Name = role.Name; - dbRole.Description = role.Description; - dbRole.IsSystem = role.IsSystem; - dbRole.FunctionsJson = SerializeFunctions(functions); - dbRole.UpdateTime = DateTime.Now; - - await _roleDefinitionRepository.UpdateAsync(dbRole); - return true; - } + public async Task DeleteAsync(string id) + { + var role = await _roleDefinitionRepository.GetAsync(id); + if (role == null) return false; - private async Task ExistsWithSameCode(Role role) - { - if (string.IsNullOrWhiteSpace(role.Code)) - { - return false; - } + if (role.IsSystem) throw new InvalidOperationException("System roles cannot be deleted."); - var sameCodeRoles = await _roleDefinitionRepository.QueryAsync(x => x.Code == role.Code); - return sameCodeRoles.Any(x => !string.Equals(x.Id, role.Id, StringComparison.OrdinalIgnoreCase)); - } + // Delete user-role relationships + var userRoles = await _userRoleRepository.QueryAsync(x => x.RoleId == id); + if (userRoles.Any()) await _userRoleRepository.DeleteAsync(userRoles); - private static string SerializeFunctions(IEnumerable functions) - { - var normalized = functions?.Where(f => !string.IsNullOrWhiteSpace(f)).Select(f => f.Trim()).Distinct().ToList() - ?? new List(); + // Delete role-function relationships + var roleFunctions = await _roleFunctionRepository.QueryAsync(x => x.RoleId == id); + if (roleFunctions.Any()) await _roleFunctionRepository.DeleteAsync(roleFunctions); + + await _roleDefinitionRepository.DeleteAsync(role); + return true; + } + + public async Task> GetAllAsync() + { + return await _roleDefinitionRepository.AllAsync(); + } + + public Task GetAsync(string id) + { + return _roleDefinitionRepository.GetAsync(id); + } + + public async Task UpdateAsync(Role role, IEnumerable functions) + { + if (role == null) throw new ArgumentNullException(nameof(role)); + + var dbRole = await _roleDefinitionRepository.GetAsync(role.Id); + if (dbRole == null) return false; - return JsonSerializer.Serialize(normalized); + dbRole.Name = role.Name; + dbRole.Description = role.Description; + dbRole.IsSystem = role.IsSystem; + dbRole.UpdateTime = DateTime.Now; + + await _roleDefinitionRepository.UpdateAsync(dbRole); + + // Update role-function relationships + var existingRoleFunctions = await _roleFunctionRepository.QueryAsync(x => x.RoleId == role.Id); + if (existingRoleFunctions.Any()) await _roleFunctionRepository.DeleteAsync(existingRoleFunctions); + + if (functions != null && functions.Any()) + { + var roleFunctions = functions + .Where(f => !string.IsNullOrWhiteSpace(f)) + .Select(f => new RoleFunction + { + Id = Guid.NewGuid().ToString("N"), + RoleId = role.Id, + FunctionId = f, + CreateTime = DateTime.Now + }) + .ToList(); + + if (roleFunctions.Any()) await _roleFunctionRepository.InsertAsync(roleFunctions); } + + return true; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/ServerNodeService.cs b/src/AgileConfig.Server.Service/ServerNodeService.cs index 8fe6d8c4..d8a82568 100644 --- a/src/AgileConfig.Server.Service/ServerNodeService.cs +++ b/src/AgileConfig.Server.Service/ServerNodeService.cs @@ -1,135 +1,121 @@ -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.IService; +using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using AgileConfig.Server.Common; -using System; -using System.Linq; using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.IService; -namespace AgileConfig.Server.Service +namespace AgileConfig.Server.Service; + +public class ServerNodeService : IServerNodeService { - public class ServerNodeService : IServerNodeService - { - private readonly IServerNodeRepository _serverNodeRepository; + private readonly IServerNodeRepository _serverNodeRepository; - public ServerNodeService(IServerNodeRepository serverNodeRepository) - { - _serverNodeRepository = serverNodeRepository; - } + public ServerNodeService(IServerNodeRepository serverNodeRepository) + { + _serverNodeRepository = serverNodeRepository; + } - public async Task AddAsync(ServerNode node) - { - await _serverNodeRepository.InsertAsync(node); - return true; - } + public async Task AddAsync(ServerNode node) + { + await _serverNodeRepository.InsertAsync(node); + return true; + } - public async Task DeleteAsync(ServerNode node) - { - var node2 = await _serverNodeRepository.GetAsync(node.Id); - if (node2 != null) - { - await _serverNodeRepository.DeleteAsync(node2); - } - return true; - } + public async Task DeleteAsync(ServerNode node) + { + var node2 = await _serverNodeRepository.GetAsync(node.Id); + if (node2 != null) await _serverNodeRepository.DeleteAsync(node2); + return true; + } - public async Task DeleteAsync(string address) - { - var node2 = await _serverNodeRepository.GetAsync(address); - if (node2 != null) - { - await _serverNodeRepository.DeleteAsync(node2); - } + public async Task DeleteAsync(string address) + { + var node2 = await _serverNodeRepository.GetAsync(address); + if (node2 != null) await _serverNodeRepository.DeleteAsync(node2); - return true; - } + return true; + } - public void Dispose() - { - _serverNodeRepository.Dispose(); - } + public void Dispose() + { + _serverNodeRepository.Dispose(); + } - public Task> GetAllNodesAsync() - { - return _serverNodeRepository.AllAsync(); - } + public Task> GetAllNodesAsync() + { + return _serverNodeRepository.AllAsync(); + } - public Task GetAsync(string address) - { - return _serverNodeRepository.GetAsync(address); - } + public Task GetAsync(string address) + { + return _serverNodeRepository.GetAsync(address); + } - public async Task UpdateAsync(ServerNode node) - { - await _serverNodeRepository.UpdateAsync(node); - return true; - } + public async Task UpdateAsync(ServerNode node) + { + await _serverNodeRepository.UpdateAsync(node); + return true; + } - public async Task InitWatchNodeAsync() + public async Task InitWatchNodeAsync() + { + var count = await _serverNodeRepository.CountAsync(); + if (count > 0) return false; + var nodes = Global.Config["nodes"]; + var addresses = new List(); + if (!string.IsNullOrEmpty(nodes)) { - var count = await _serverNodeRepository.CountAsync(); - if (count > 0) + var arr = nodes.Split(','); + foreach (var item in arr) { - return false; - } - var nodes = Global.Config["nodes"]; - var addresses = new List(); - if (!string.IsNullOrEmpty(nodes)) - { - var arr = nodes.Split(','); - foreach (var item in arr) - { - var address = ""; - if (item.StartsWith("http", StringComparison.OrdinalIgnoreCase)) - { - address = item; - } - else - { - address = "http://" + item; - } - - addresses.Add(address); - } + var address = ""; + if (item.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + address = item; + else + address = "http://" + item; + + addresses.Add(address); } + } - var existNodes = await _serverNodeRepository.QueryAsync(x => addresses.Contains(x.Id)); - var newNodes = addresses - .Where(x => existNodes.All(y => y.Id != x)) - .Select(x => new ServerNode { Id = x, CreateTime = DateTime.Now }) - .ToList(); + var existNodes = await _serverNodeRepository.QueryAsync(x => addresses.Contains(x.Id)); + var newNodes = addresses + .Where(x => existNodes.All(y => y.Id != x)) + .Select(x => new ServerNode { Id = x, CreateTime = DateTime.Now }) + .ToList(); - if (newNodes.Count == 0) - return false; + if (newNodes.Count == 0) + return false; - await _serverNodeRepository.InsertAsync(newNodes); - return true; - } + await _serverNodeRepository.InsertAsync(newNodes); + return true; + } - public async Task JoinAsync(string ip, int port, string desc) + public async Task JoinAsync(string ip, int port, string desc) + { + var address = $"http://{ip}:{port}"; + var nodes = await _serverNodeRepository.QueryAsync(x => x.Id == address); + if (nodes.Count > 0) { - var address = $"http://{ip}:{port}"; - var nodes = await _serverNodeRepository.QueryAsync(x => x.Id == address); - if (nodes.Count > 0) - { - // if already there, noting to do - } - else + // if already there, noting to do + } + else + { + await _serverNodeRepository.InsertAsync(new ServerNode { - await _serverNodeRepository.InsertAsync(new ServerNode - { - Id = address, - CreateTime = DateTime.Now, - Remark = desc, - Status = NodeStatus.Online, - LastEchoTime = DateTime.Now - }); - } - - return true; + Id = address, + CreateTime = DateTime.Now, + Remark = desc, + Status = NodeStatus.Online, + LastEchoTime = DateTime.Now + }); } + + return true; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/ServiceCollectionExt.cs b/src/AgileConfig.Server.Service/ServiceCollectionExt.cs index 48290faa..4c9b3145 100644 --- a/src/AgileConfig.Server.Service/ServiceCollectionExt.cs +++ b/src/AgileConfig.Server.Service/ServiceCollectionExt.cs @@ -3,36 +3,35 @@ using AgileConfig.Server.Service.EventRegisterService; using Microsoft.Extensions.DependencyInjection; -namespace AgileConfig.Server.Service +namespace AgileConfig.Server.Service; + +public static class ServiceCollectionExt { - public static class ServiceCollectionExt + public static void AddBusinessServices(this IServiceCollection sc) { - public static void AddBusinessServices(this IServiceCollection sc) - { - sc.AddSingleton(); - sc.AddSingleton(); + sc.AddSingleton(); + + sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); - - sc.AddScoped(); - sc.AddScoped(); - sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); - } + sc.AddScoped(); + sc.AddScoped(); + sc.AddScoped(); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/ServiceHealthCheckService.cs b/src/AgileConfig.Server.Service/ServiceHealthCheckService.cs index f23157cc..434d0984 100644 --- a/src/AgileConfig.Server.Service/ServiceHealthCheckService.cs +++ b/src/AgileConfig.Server.Service/ServiceHealthCheckService.cs @@ -14,41 +14,35 @@ public class ServiceHealthCheckService : IServiceHealthCheckService private readonly IRestClient _restClient; private readonly IServiceInfoService _serviceInfoService; + private int _checkInterval; + private int _removeServiceInterval; + private int _unhealthInterval; + public ServiceHealthCheckService( IServiceInfoService serviceInfoService, ILogger logger, IRestClient restClient - ) + ) { _serviceInfoService = serviceInfoService; _logger = logger; _restClient = restClient; } - private int _checkInterval; - private int _unhealthInterval; - private int _removeServiceInterval; - /// - /// Time threshold (seconds) after which a service is considered unhealthy. + /// Time threshold (seconds) after which a service is considered unhealthy. /// /// private int CheckInterval { get { - if (_checkInterval > 0) - { - return _checkInterval; - } + if (_checkInterval > 0) return _checkInterval; var interval = Global.Config["serviceHealthCheckInterval"]; - if (int.TryParse(interval, out int i)) + if (int.TryParse(interval, out var i)) { - if (i <= 0) - { - throw new ArgumentException("serviceHealthCheckInterval must be greater than 0"); - } + if (i <= 0) throw new ArgumentException("serviceHealthCheckInterval must be greater than 0"); _checkInterval = i; } @@ -58,25 +52,19 @@ private int CheckInterval } /// - /// Interval for running health checks, in seconds. + /// Interval for running health checks, in seconds. /// /// private int UnhealthInterval { get { - if (_unhealthInterval > 0) - { - return _unhealthInterval; - } + if (_unhealthInterval > 0) return _unhealthInterval; var interval = Global.Config["serviceUnhealthInterval"]; - if (int.TryParse(interval, out int i)) + if (int.TryParse(interval, out var i)) { - if (i <= 0) - { - throw new ArgumentException("serviceUnhealthInterval must be greater than 0"); - } + if (i <= 0) throw new ArgumentException("serviceUnhealthInterval must be greater than 0"); _unhealthInterval = i; } @@ -89,22 +77,15 @@ private int RemoveServiceInterval { get { - if (_removeServiceInterval > 0) - { - return _removeServiceInterval; - } + if (_removeServiceInterval > 0) return _removeServiceInterval; var interval = Global.Config["removeServiceInterval"]; - if (int.TryParse(interval, out int i)) + if (int.TryParse(interval, out var i)) { if (i <= 0) - { _removeServiceInterval = 0; - } else - { _removeServiceInterval = i; - } } return _removeServiceInterval; @@ -124,16 +105,10 @@ public Task StartCheckAsync() .QueryAsync(x => x.HeartBeatMode != null && x.HeartBeatMode != ""); foreach (var service in services) { - if (service.HeartBeatMode == HeartBeatModes.none.ToString()) - { - continue; - } + if (service.HeartBeatMode == HeartBeatModes.none.ToString()) continue; var lstHeartBeat = service.LastHeartBeat; - if (!lstHeartBeat.HasValue) - { - lstHeartBeat = service.RegisterTime ?? DateTime.MinValue; - } + if (!lstHeartBeat.HasValue) lstHeartBeat = service.RegisterTime ?? DateTime.MinValue; // A heartbeat mode is specified. if (!string.IsNullOrWhiteSpace(service.HeartBeatMode)) @@ -150,13 +125,9 @@ public Task StartCheckAsync() if (service.HeartBeatMode == HeartBeatModes.client.ToString()) { if ((DateTime.Now - lstHeartBeat.Value).TotalSeconds > UnhealthInterval) - { // For client heartbeats, treat services as unavailable after UnhealthInterval without heartbeats. if (service.Status == ServiceStatus.Healthy) - { await _serviceInfoService.UpdateServiceStatus(service, ServiceStatus.Unhealthy); - } - } continue; } @@ -195,14 +166,13 @@ private async Task CheckAService(ServiceInfo service) using var resp = await _restClient.GetAsync(service.CheckUrl); var result = false; - int istatus = ((int)resp.StatusCode - 200); + var istatus = (int)resp.StatusCode - 200; result = istatus >= 0 && istatus < 100; // Treat 2xx status codes as healthy responses. if (!result) - { - _logger.LogInformation("check service health {0} {1} {2} result:{3}", service.CheckUrl, service.ServiceId, - service.ServiceName, "down"); - } + _logger.LogInformation("check service health {0} {1} {2} result:{3}", service.CheckUrl, + service.ServiceId, + service.ServiceName, "down"); return result; } diff --git a/src/AgileConfig.Server.Service/ServiceInfoService.cs b/src/AgileConfig.Server.Service/ServiceInfoService.cs index e81d9951..14b33590 100644 --- a/src/AgileConfig.Server.Service/ServiceInfoService.cs +++ b/src/AgileConfig.Server.Service/ServiceInfoService.cs @@ -1,155 +1,141 @@ -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.IService; -using System; +using System; using System.Collections.Generic; -using System.Dynamic; using System.Linq; +using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; using AgileConfig.Server.Common; +using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Event; +using AgileConfig.Server.IService; using Microsoft.Extensions.Caching.Memory; using Newtonsoft.Json; -using System.Linq.Expressions; -using AgileConfig.Server.Common.EventBus; -using AgileConfig.Server.Event; -namespace AgileConfig.Server.Service +namespace AgileConfig.Server.Service; + +public class ServiceInfoService : IServiceInfoService { - public class ServiceInfoService : IServiceInfoService + private readonly IMemoryCache _memoryCache; + private readonly IServiceInfoRepository _serviceInfoRepository; + private readonly ITinyEventBus _tinyEventBus; + + public ServiceInfoService(IMemoryCache memoryCache, + IServiceInfoRepository serviceInfoRepository, + ITinyEventBus tinyEventBus) { - private readonly IMemoryCache _memoryCache; - private readonly IServiceInfoRepository _serviceInfoRepository; - private readonly ITinyEventBus _tinyEventBus; + _memoryCache = memoryCache; + _serviceInfoRepository = serviceInfoRepository; + _tinyEventBus = tinyEventBus; + } - public ServiceInfoService(IMemoryCache memoryCache, - IServiceInfoRepository serviceInfoRepository, - ITinyEventBus tinyEventBus) - { - _memoryCache = memoryCache; - _serviceInfoRepository = serviceInfoRepository; - _tinyEventBus = tinyEventBus; - } + public Task GetByUniqueIdAsync(string id) + { + return _serviceInfoRepository.GetAsync(id); + } - public Task GetByUniqueIdAsync(string id) - { - return _serviceInfoRepository.GetAsync(id); - } + public async Task GetByServiceIdAsync(string serviceId) + { + var entity = (await _serviceInfoRepository.QueryAsync(x => x.ServiceId == serviceId)).FirstOrDefault(); - public async Task GetByServiceIdAsync(string serviceId) - { - var entity = (await _serviceInfoRepository.QueryAsync(x => x.ServiceId == serviceId)).FirstOrDefault(); + return entity; + } - return entity; - } + public async Task RemoveAsync(string id) + { + await _serviceInfoRepository.DeleteAsync(id); + return true; + } - public async Task RemoveAsync(string id) - { - await _serviceInfoRepository.DeleteAsync(id); - return true; - } + public Task> GetAllServiceInfoAsync() + { + return _serviceInfoRepository.AllAsync(); + } - public Task> GetAllServiceInfoAsync() - { - return _serviceInfoRepository.AllAsync(); - } + public Task> GetOnlineServiceInfoAsync() + { + return _serviceInfoRepository.QueryAsync(x => x.Status == ServiceStatus.Healthy); + } - public Task> GetOnlineServiceInfoAsync() - { - return _serviceInfoRepository.QueryAsync(x => x.Status == ServiceStatus.Healthy); - } + public Task> GetOfflineServiceInfoAsync() + { + return _serviceInfoRepository.QueryAsync(x => x.Status == ServiceStatus.Unhealthy); + } - public Task> GetOfflineServiceInfoAsync() - { - return _serviceInfoRepository.QueryAsync(x => x.Status == ServiceStatus.Unhealthy); - } + public async Task ServicesMD5Cache() + { + var cacheKey = "ServiceInfoService_ServicesMD5Cache"; + if (_memoryCache != null && _memoryCache.TryGetValue(cacheKey, out string md5)) return md5; - public async Task ServicesMD5Cache() - { - var cacheKey = $"ServiceInfoService_ServicesMD5Cache"; - if (_memoryCache != null && _memoryCache.TryGetValue(cacheKey, out string md5)) - { - return md5; - } + md5 = await ServicesMD5(); + var cacheOp = new MemoryCacheEntryOptions() + .SetAbsoluteExpiration(TimeSpan.FromSeconds(60)); + _memoryCache?.Set(cacheKey, md5, cacheOp); - md5 = await ServicesMD5(); - var cacheOp = new MemoryCacheEntryOptions() - .SetAbsoluteExpiration(TimeSpan.FromSeconds(60)); - _memoryCache?.Set(cacheKey, md5, cacheOp); + return md5; + } - return md5; - } + public async Task ServicesMD5() + { + var services = await GetAllServiceInfoAsync(); + var md5 = GenerateMD5(services); - public async Task ServicesMD5() - { - var services = await GetAllServiceInfoAsync(); - var md5 = GenerateMD5(services); + return md5; + } - return md5; - } + public void ClearCache() + { + if (_memoryCache != null && _memoryCache is MemoryCache memCache) memCache.Compact(1.0); + } - private string GenerateMD5(List services) - { - var sb = new StringBuilder(); - foreach (var serviceInfo in services.OrderBy(x => x.ServiceId, StringComparer.Ordinal)) - { - var metaDataStr = ""; - if (!string.IsNullOrEmpty(serviceInfo.MetaData)) - { - var metaData = JsonConvert.DeserializeObject>(serviceInfo.MetaData); - if (metaData != null) - { - metaDataStr = string.Join(",", metaData.OrderBy(x => x, StringComparer.Ordinal)); - } - } - - sb.Append( - $"{serviceInfo.ServiceId}&{serviceInfo.ServiceName}&{serviceInfo.Ip}&{serviceInfo.Port}&{(int)serviceInfo.Status}&{metaDataStr}&"); - } + public async Task UpdateServiceStatus(ServiceInfo service, ServiceStatus status) + { + var id = service.Id; + var oldStatus = service.Status; - var txt = sb.ToString(); - return Encrypt.Md5(txt); - } + var service2 = await _serviceInfoRepository.GetAsync(id); + if (service2 == null) return; - public void ClearCache() - { - if (_memoryCache != null && _memoryCache is MemoryCache memCache) - { - memCache.Compact(1.0); - } - } + service2.Status = status; + if (status != ServiceStatus.Unhealthy) service2.LastHeartBeat = DateTime.Now; + await _serviceInfoRepository.UpdateAsync(service2); - public async Task UpdateServiceStatus(ServiceInfo service, ServiceStatus status) + if (oldStatus != status) { - var id = service.Id; - var oldStatus = service.Status; + ClearCache(); - var service2 = await _serviceInfoRepository.GetAsync(id); - if (service2 == null) return; + _tinyEventBus.Fire(new ServiceStatusUpdateEvent(service.Id)); + } + } - service2.Status = status; - if (status != ServiceStatus.Unhealthy) - { - service2.LastHeartBeat = DateTime.Now; - } - await _serviceInfoRepository.UpdateAsync(service2); + public void Dispose() + { + _serviceInfoRepository.Dispose(); + } - if (oldStatus != status) - { - ClearCache(); + public Task> QueryAsync(Expression> exp) + { + return _serviceInfoRepository.QueryAsync(exp); + } - _tinyEventBus.Fire(new ServiceStatusUpdateEvent(service.Id)); + private string GenerateMD5(List services) + { + var sb = new StringBuilder(); + foreach (var serviceInfo in services.OrderBy(x => x.ServiceId, StringComparer.Ordinal)) + { + var metaDataStr = ""; + if (!string.IsNullOrEmpty(serviceInfo.MetaData)) + { + var metaData = JsonConvert.DeserializeObject>(serviceInfo.MetaData); + if (metaData != null) metaDataStr = string.Join(",", metaData.OrderBy(x => x, StringComparer.Ordinal)); } - } - public void Dispose() - { - _serviceInfoRepository.Dispose(); + sb.Append( + $"{serviceInfo.ServiceId}&{serviceInfo.ServiceName}&{serviceInfo.Ip}&{serviceInfo.Port}&{(int)serviceInfo.Status}&{metaDataStr}&"); } - public Task> QueryAsync(Expression> exp) - { - return _serviceInfoRepository.QueryAsync(exp); - } + var txt = sb.ToString(); + return Encrypt.Md5(txt); } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/SettingService.cs b/src/AgileConfig.Server.Service/SettingService.cs index 3238f717..9a61027a 100644 --- a/src/AgileConfig.Server.Service/SettingService.cs +++ b/src/AgileConfig.Server.Service/SettingService.cs @@ -1,90 +1,79 @@ -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.IService; -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using AgileConfig.Server.Common; using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.IService; + +namespace AgileConfig.Server.Service; -namespace AgileConfig.Server.Service +public class SettingService : ISettingService { - public class SettingService : ISettingService - { - private readonly ISettingRepository _settingRepository; - private readonly IUserRepository _userRepository; - private readonly IUserRoleRepository _userRoleRepository; + private readonly ISettingRepository _settingRepository; + private readonly IUserRepository _userRepository; + private readonly IUserRoleRepository _userRoleRepository; - public SettingService( - ISettingRepository settingRepository, - IUserRepository userRepository, - IUserRoleRepository userRoleRepository) - { - _settingRepository = settingRepository; - _userRepository = userRepository; - _userRoleRepository = userRoleRepository; - } + public SettingService( + ISettingRepository settingRepository, + IUserRepository userRepository, + IUserRoleRepository userRoleRepository) + { + _settingRepository = settingRepository; + _userRepository = userRepository; + _userRoleRepository = userRoleRepository; + } - public async Task AddAsync(Setting setting) - { - await _settingRepository.InsertAsync(setting); - return true; - } + public async Task AddAsync(Setting setting) + { + await _settingRepository.InsertAsync(setting); + return true; + } - public async Task DeleteAsync(Setting setting) - { - var setting2 = await _settingRepository.GetAsync(setting.Id); - if (setting2 != null) - { - await _settingRepository.DeleteAsync(setting); - } - return true; - } + public async Task DeleteAsync(Setting setting) + { + var setting2 = await _settingRepository.GetAsync(setting.Id); + if (setting2 != null) await _settingRepository.DeleteAsync(setting); + return true; + } - public async Task DeleteAsync(string settingId) - { - var setting = await _settingRepository.GetAsync(settingId); - if (setting != null) - { - await _settingRepository.DeleteAsync(setting); - } - return true; - } + public async Task DeleteAsync(string settingId) + { + var setting = await _settingRepository.GetAsync(settingId); + if (setting != null) await _settingRepository.DeleteAsync(setting); + return true; + } - public Task GetAsync(string id) - { - return _settingRepository.GetAsync(id); - } + public Task GetAsync(string id) + { + return _settingRepository.GetAsync(id); + } - public Task> GetAllSettingsAsync() - { - return _settingRepository.AllAsync(); - } + public Task> GetAllSettingsAsync() + { + return _settingRepository.AllAsync(); + } - public async Task UpdateAsync(Setting setting) - { - await _settingRepository.UpdateAsync(setting); - return true; - } + public async Task UpdateAsync(Setting setting) + { + await _settingRepository.UpdateAsync(setting); + return true; + } - public void Dispose() - { - _settingRepository.Dispose(); - _userRepository.Dispose(); - _userRoleRepository.Dispose(); - } + public void Dispose() + { + _settingRepository.Dispose(); + _userRepository.Dispose(); + _userRoleRepository.Dispose(); + } - public async Task GetEnvironmentList() - { - if (ISettingService.EnvironmentList != null) - { - return ISettingService.EnvironmentList; - } + public async Task GetEnvironmentList() + { + if (ISettingService.EnvironmentList != null) return ISettingService.EnvironmentList; - var environments = await _settingRepository.GetAsync(SystemSettings.DefaultEnvironmentKey); + var environments = await _settingRepository.GetAsync(SystemSettings.DefaultEnvironmentKey); - ISettingService.EnvironmentList = environments?.Value?.ToUpper().Split(',') ?? []; + ISettingService.EnvironmentList = environments?.Value?.ToUpper().Split(',') ?? []; - return ISettingService.EnvironmentList; - } + return ISettingService.EnvironmentList; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/SysLogService.cs b/src/AgileConfig.Server.Service/SysLogService.cs index 5945e4a8..16d9d5b2 100644 --- a/src/AgileConfig.Server.Service/SysLogService.cs +++ b/src/AgileConfig.Server.Service/SysLogService.cs @@ -1,103 +1,78 @@ -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.IService; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.IService; using Microsoft.Extensions.Logging; -using static FreeSql.Internal.GlobalFilter; -namespace AgileConfig.Server.Service +namespace AgileConfig.Server.Service; + +public class SysLogService : ISysLogService { - public class SysLogService : ISysLogService + private readonly ILogger _logger; + private readonly ISysLogRepository _sysLogRepository; + + public SysLogService(ISysLogRepository sysLogRepository, ILogger logger) + { + _sysLogRepository = sysLogRepository; + _logger = logger; + } + + public async Task AddRangeAsync(IEnumerable logs) + { + await _sysLogRepository.InsertAsync(logs.ToList()); + + foreach (var item in logs) + _logger.LogInformation("{AppId} {LogType} {LogTime} {LogText}", item.AppId, item.LogType, item.LogTime, + item.LogText); + + return true; + } + + public async Task AddSysLogAsync(SysLog log) { - private readonly ISysLogRepository _sysLogRepository; - private readonly ILogger _logger; - - public SysLogService(ISysLogRepository sysLogRepository, ILogger logger) - { - _sysLogRepository = sysLogRepository; - _logger = logger; - } - - public async Task AddRangeAsync(IEnumerable logs) - { - await _sysLogRepository.InsertAsync(logs.ToList()); - - foreach (var item in logs) - { - _logger.LogInformation("{AppId} {LogType} {LogTime} {LogText}", item.AppId, item.LogType, item.LogTime, item.LogText); - } - - return true; - } - - public async Task AddSysLogAsync(SysLog log) - { - await _sysLogRepository.InsertAsync(log); - - _logger.LogInformation("{AppId} {LogType} {LogTime} {LogText}", log.AppId, log.LogType, log.LogTime, log.LogText); - - return true; - } - - public Task Count(string appId, SysLogType? logType, DateTime? startTime, DateTime? endTime) - { - Expression> exp = x => true; - if (!string.IsNullOrEmpty(appId)) - { - exp = exp.And(c => c.AppId == appId); - } - - if (startTime.HasValue) - { - exp = exp.And(x => x.LogTime >= startTime); - } - - if (endTime.HasValue) - { - exp = exp.And(x => x.LogTime < endTime); - } - - if (logType.HasValue) - { - exp = exp.And(x => x.LogType == logType); - } - - return _sysLogRepository.CountAsync(exp); - } - - public void Dispose() - { - _sysLogRepository.Dispose(); - } - - public Task> SearchPage(string appId, SysLogType? logType, DateTime? startTime, DateTime? endTime, int pageSize, int pageIndex) - { - Expression> exp = x => true; - if (!string.IsNullOrEmpty(appId)) - { - exp = exp.And(c => c.AppId == appId); - } - - if (startTime.HasValue) - { - exp = exp.And(x => x.LogTime >= startTime); - } - - if (endTime.HasValue) - { - exp = exp.And(x => x.LogTime < endTime); - } - - if (logType.HasValue) - { - exp = exp.And(x => x.LogType == logType); - } - - return _sysLogRepository.QueryPageAsync(exp, pageIndex, pageSize, defaultSortField: "LogTime", defaultSortType: "DESC"); - } + await _sysLogRepository.InsertAsync(log); + + _logger.LogInformation("{AppId} {LogType} {LogTime} {LogText}", log.AppId, log.LogType, log.LogTime, + log.LogText); + + return true; + } + + public Task Count(string appId, SysLogType? logType, DateTime? startTime, DateTime? endTime) + { + Expression> exp = x => true; + if (!string.IsNullOrEmpty(appId)) exp = exp.And(c => c.AppId == appId); + + if (startTime.HasValue) exp = exp.And(x => x.LogTime >= startTime); + + if (endTime.HasValue) exp = exp.And(x => x.LogTime < endTime); + + if (logType.HasValue) exp = exp.And(x => x.LogType == logType); + + return _sysLogRepository.CountAsync(exp); + } + + public void Dispose() + { + _sysLogRepository.Dispose(); + } + + public Task> SearchPage(string appId, SysLogType? logType, DateTime? startTime, DateTime? endTime, + int pageSize, int pageIndex) + { + Expression> exp = x => true; + if (!string.IsNullOrEmpty(appId)) exp = exp.And(c => c.AppId == appId); + + if (startTime.HasValue) exp = exp.And(x => x.LogTime >= startTime); + + if (endTime.HasValue) exp = exp.And(x => x.LogTime < endTime); + + if (logType.HasValue) exp = exp.And(x => x.LogType == logType); + + return _sysLogRepository.QueryPageAsync(exp, pageIndex, pageSize, "LogTime", "DESC"); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/SystemInitializationService.cs b/src/AgileConfig.Server.Service/SystemInitializationService.cs index d41718c6..a8b5004e 100644 --- a/src/AgileConfig.Server.Service/SystemInitializationService.cs +++ b/src/AgileConfig.Server.Service/SystemInitializationService.cs @@ -1,4 +1,6 @@ -using System; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; using AgileConfig.Server.Common; using AgileConfig.Server.Data.Abstraction; using AgileConfig.Server.Data.Entity; @@ -11,10 +13,13 @@ namespace AgileConfig.Server.Service; public class SystemInitializationService( ISysInitRepository sysInitRepository, IConfiguration configuration, - ILogger logger) : ISystemInitializationService + ILogger logger, + IRoleDefinitionRepository roleDefinitionRepository, + IFunctionRepository functionRepository, + IRoleFunctionRepository roleFunctionRepository) : ISystemInitializationService { /// - /// Initialize the JWT secret if it is not configured via file or environment variables. + /// Initialize the JWT secret if it is not configured via file or environment variables. /// /// public bool TryInitJwtSecret() @@ -68,7 +73,8 @@ public bool TryInitDefaultEnvironment() } catch (Exception e) { - logger.LogError("TryInitDefaultEnvironment error, maybe exec this saveing action in parallel on another node."); + logger.LogError( + "TryInitDefaultEnvironment error, maybe exec this saveing action in parallel on another node."); } } @@ -78,30 +84,14 @@ public bool TryInitDefaultEnvironment() } /// - /// Generate a 38-character JWT secret key. - /// - /// - private static string GenerateJwtSecretKey() - { - var guid1 = Guid.NewGuid().ToString("n"); - var guid2 = Guid.NewGuid().ToString("n"); - - return guid1[..19] + guid2[..19]; - } - - /// - /// Initialize the super administrator password, optionally reading from configuration when no value is provided. + /// Initialize the super administrator password, optionally reading from configuration when no value is provided. /// /// Plain text password to set for the super administrator, or empty to read from configuration. /// True if the password is already set or initialization completed successfully; otherwise false. public bool TryInitSaPassword(string password = "") { - if (string.IsNullOrEmpty(password)) - { - password = configuration["saPassword"]; - } + if (string.IsNullOrEmpty(password)) password = configuration["saPassword"]; if (!string.IsNullOrEmpty(password) && !sysInitRepository.HasSa()) - { try { sysInitRepository.InitSa(password); @@ -112,7 +102,6 @@ public bool TryInitSaPassword(string password = "") logger.LogError(e, "Init super admin password occur error."); return false; } - } return true; } @@ -124,13 +113,9 @@ public bool HasSa() public bool TryInitDefaultApp(string appName = "") { - if (string.IsNullOrEmpty(appName)) - { - appName = configuration["defaultApp"]; - } + if (string.IsNullOrEmpty(appName)) appName = configuration["defaultApp"]; if (!string.IsNullOrEmpty(appName)) - { try { sysInitRepository.InitDefaultApp(appName); @@ -141,8 +126,376 @@ public bool TryInitDefaultApp(string appName = "") logger.LogError(e, "Init default app {appName} error.", appName); return false; } - } return true; } + + public async Task TryInitSuperAdminRole() + { + try + { + // Check if SuperAdministrator role already exists + var superAdminRole = await roleDefinitionRepository.GetAsync(SystemRoleConstants.SuperAdminId); + if (superAdminRole != null) + { + logger.LogInformation("SuperAdministrator role already exists."); + return true; + } + + // Create SuperAdministrator role + superAdminRole = new Role + { + Id = SystemRoleConstants.SuperAdminId, + Name = "SuperAdministrator", + Description = "System super administrator with all permissions", + IsSystem = true, + CreateTime = DateTime.Now + }; + + await roleDefinitionRepository.InsertAsync(superAdminRole); + logger.LogInformation("SuperAdministrator role created successfully."); + + // Get all existing functions + var allFunctions = await functionRepository.AllAsync(); + + // If no functions exist yet, create them from the Functions class constants + if (allFunctions.Count == 0) allFunctions = await InitializeFunctions(); + +// Bind all functions to SuperAdministrator role + var roleFunctions = new List(); + foreach (var function in allFunctions) + roleFunctions.Add(new RoleFunction + { + Id = Guid.NewGuid().ToString("N"), + RoleId = SystemRoleConstants.SuperAdminId, + FunctionId = function.Id, + CreateTime = DateTime.Now + }); + + if (roleFunctions.Count > 0) + { + await roleFunctionRepository.InsertAsync(roleFunctions); + logger.LogInformation("Bound {count} functions to SuperAdministrator role.", roleFunctions.Count); + } + + return true; + } + catch (Exception e) + { + logger.LogError(e, "Failed to initialize SuperAdministrator role."); + return false; + } + } + + /// + /// Generate a 38-character JWT secret key. + /// + /// + private static string GenerateJwtSecretKey() + { + var guid1 = Guid.NewGuid().ToString("n"); + var guid2 = Guid.NewGuid().ToString("n"); + + return guid1[..19] + guid2[..19]; + } + + private async Task> InitializeFunctions() + { + var functions = new List + { + // Application permissions + new() + { + Id = Functions.App_Read, + Code = Functions.App_Read, + Name = "Read Application", + Description = "Permission to read applications", + Category = "Application", + SortIndex = 1, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.App_Add, + Code = Functions.App_Add, + Name = "Add Application", + Description = "Permission to add new applications", + Category = "Application", + SortIndex = 2, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.App_Edit, + Code = Functions.App_Edit, + Name = "Edit Application", + Description = "Permission to edit applications", + Category = "Application", + SortIndex = 3, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.App_Delete, + Code = Functions.App_Delete, + Name = "Delete Application", + Description = "Permission to delete applications", + Category = "Application", + SortIndex = 4, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.App_Auth, + Code = Functions.App_Auth, + Name = "Authorize Application", + Description = "Permission to authorize applications", + Category = "Application", + SortIndex = 5, + CreateTime = DateTime.Now + }, + // Configuration permissions + new() + { + Id = Functions.Confing_Read, + Code = Functions.Confing_Read, + Name = "Read Configuration", + Description = "Permission to read configurations", + Category = "Configuration", + SortIndex = 6, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.Config_Add, + Code = Functions.Config_Add, + Name = "Add Configuration", + Description = "Permission to add configurations", + Category = "Configuration", + SortIndex = 7, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.Config_Edit, + Code = Functions.Config_Edit, + Name = "Edit Configuration", + Description = "Permission to edit configurations", + Category = "Configuration", + SortIndex = 8, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.Config_Delete, + Code = Functions.Config_Delete, + Name = "Delete Configuration", + Description = "Permission to delete configurations", + Category = "Configuration", + SortIndex = 9, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.Config_Publish, + Code = Functions.Config_Publish, + Name = "Publish Configuration", + Description = "Permission to publish configurations", + Category = "Configuration", + SortIndex = 10, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.Config_Offline, + Code = Functions.Config_Offline, + Name = "Offline Configuration", + Description = "Permission to offline configurations", + Category = "Configuration", + SortIndex = 11, + CreateTime = DateTime.Now + }, + // Node permissions + new() + { + Id = Functions.Node_Read, + Code = Functions.Node_Read, + Name = "Read Node", + Description = "Permission to read nodes", + Category = "Node", + SortIndex = 12, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.Node_Add, + Code = Functions.Node_Add, + Name = "Add Node", + Description = "Permission to add nodes", + Category = "Node", + SortIndex = 13, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.Node_Delete, + Code = Functions.Node_Delete, + Name = "Delete Node", + Description = "Permission to delete nodes", + Category = "Node", + SortIndex = 14, + CreateTime = DateTime.Now + }, + // Client permissions + new() + { + Id = Functions.Client_Refresh, + Code = Functions.Client_Refresh, + Name = "Refresh Client", + Description = "Permission to refresh clients", + Category = "Client", + SortIndex = 15, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.Client_Disconnect, + Code = Functions.Client_Disconnect, + Name = "Disconnect Client", + Description = "Permission to disconnect clients", + Category = "Client", + SortIndex = 16, + CreateTime = DateTime.Now + }, + // User permissions + new() + { + Id = Functions.User_Read, + Code = Functions.User_Read, + Name = "Read User", + Description = "Permission to read users", + Category = "User", + SortIndex = 17, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.User_Add, + Code = Functions.User_Add, + Name = "Add User", + Description = "Permission to add users", + Category = "User", + SortIndex = 18, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.User_Edit, + Code = Functions.User_Edit, + Name = "Edit User", + Description = "Permission to edit users", + Category = "User", + SortIndex = 19, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.User_Delete, + Code = Functions.User_Delete, + Name = "Delete User", + Description = "Permission to delete users", + Category = "User", + SortIndex = 20, + CreateTime = DateTime.Now + }, + // Role permissions + new() + { + Id = Functions.Role_Read, + Code = Functions.Role_Read, + Name = "Read Role", + Description = "Permission to read roles", + Category = "Role", + SortIndex = 21, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.Role_Add, + Code = Functions.Role_Add, + Name = "Add Role", + Description = "Permission to add roles", + Category = "Role", + SortIndex = 22, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.Role_Edit, + Code = Functions.Role_Edit, + Name = "Edit Role", + Description = "Permission to edit roles", + Category = "Role", + SortIndex = 23, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.Role_Delete, + Code = Functions.Role_Delete, + Name = "Delete Role", + Description = "Permission to delete roles", + Category = "Role", + SortIndex = 24, + CreateTime = DateTime.Now + }, + // Service permissions + new() + { + Id = Functions.Service_Read, + Code = Functions.Service_Read, + Name = "Read Service", + Description = "Permission to read services", + Category = "Service", + SortIndex = 25, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.Service_Add, + Code = Functions.Service_Add, + Name = "Add Service", + Description = "Permission to add services", + Category = "Service", + SortIndex = 26, + CreateTime = DateTime.Now + }, + new() + { + Id = Functions.Service_Delete, + Code = Functions.Service_Delete, + Name = "Delete Service", + Description = "Permission to delete services", + Category = "Service", + SortIndex = 27, + CreateTime = DateTime.Now + }, + // System permissions + new() + { + Id = Functions.Log_Read, + Code = Functions.Log_Read, + Name = "Read Log", + Description = "Permission to read system logs", + Category = "Log", + SortIndex = 28, + CreateTime = DateTime.Now + } + }; + + await functionRepository.InsertAsync(functions); + logger.LogInformation("Initialized {count} system functions.", functions.Count); + + return functions; + } } \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/UserService.cs b/src/AgileConfig.Server.Service/UserService.cs index 4e4198d9..9d044e92 100644 --- a/src/AgileConfig.Server.Service/UserService.cs +++ b/src/AgileConfig.Server.Service/UserService.cs @@ -1,168 +1,123 @@ -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.IService; -using System; +using System; using System.Collections.Generic; -using System.Threading.Tasks; using System.Linq; +using System.Threading.Tasks; using AgileConfig.Server.Common; using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.IService; -namespace AgileConfig.Server.Service +namespace AgileConfig.Server.Service; + +public class UserService : IUserService { - public class UserService : IUserService - { - private readonly IUserRepository _userRepository; - private readonly IUserRoleRepository _userRoleRepository; - private readonly IRoleDefinitionRepository _roleDefinitionRepository; + private readonly IRoleDefinitionRepository _roleDefinitionRepository; + private readonly IUserRepository _userRepository; + private readonly IUserRoleRepository _userRoleRepository; - public UserService(IUserRepository userRepository, IUserRoleRepository userRoleRepository, IRoleDefinitionRepository roleDefinitionRepository) - { - _userRepository = userRepository; - _userRoleRepository = userRoleRepository; - _roleDefinitionRepository = roleDefinitionRepository; - } + public UserService(IUserRepository userRepository, IUserRoleRepository userRoleRepository, + IRoleDefinitionRepository roleDefinitionRepository) + { + _userRepository = userRepository; + _userRoleRepository = userRoleRepository; + _roleDefinitionRepository = roleDefinitionRepository; + } - public async Task AddAsync(User user) - { - var old = (await _userRepository.QueryAsync(u => u.UserName == user.UserName && u.Status == UserStatus.Normal)).FirstOrDefault(); - if (old != null) - { - return false; - } + public async Task AddAsync(User user) + { + var old = (await _userRepository.QueryAsync(u => u.UserName == user.UserName && u.Status == UserStatus.Normal)) + .FirstOrDefault(); + if (old != null) return false; - await _userRepository.InsertAsync(user); + await _userRepository.InsertAsync(user); - return true; - } + return true; + } - public async Task DeleteAsync(User user) - { - await _userRepository.DeleteAsync(user); - return true; - } + public async Task DeleteAsync(User user) + { + await _userRepository.DeleteAsync(user); + return true; + } - public Task> GetUsersByNameAsync(string userName) - { - return _userRepository.QueryAsync(u => u.UserName == userName); - } + public Task> GetUsersByNameAsync(string userName) + { + return _userRepository.QueryAsync(u => u.UserName == userName); + } - public Task GetUserAsync(string id) - { - return _userRepository.GetAsync(id); - } + public Task GetUserAsync(string id) + { + return _userRepository.GetAsync(id); + } - public async Task> GetUserRolesAsync(string userId) - { - var userRoles = await _userRoleRepository.QueryAsync(x => x.UserId == userId); - var migratedRoles = new List(); - foreach (var userRole in userRoles) - { - if (string.IsNullOrEmpty(userRole.RoleId) && userRole.LegacyRoleValue.HasValue) - { - var mappedRoleId = MapLegacyRole(userRole.LegacyRoleValue.Value); - if (!string.IsNullOrEmpty(mappedRoleId)) - { - userRole.RoleId = mappedRoleId; - userRole.LegacyRoleValue = null; - if (userRole.CreateTime == default) - { - userRole.CreateTime = DateTime.Now; - } - migratedRoles.Add(userRole); - } - } - } - - if (migratedRoles.Any()) - { - await _userRoleRepository.UpdateAsync(migratedRoles); - } + public async Task> GetUserRolesAsync(string userId) + { + var userRoles = await _userRoleRepository.QueryAsync(x => x.UserId == userId); - var roleIds = userRoles.Select(x => x.RoleId).Distinct().ToList(); - if (!roleIds.Any()) - { - return new List(); - } + var roleIds = userRoles.Select(x => x.RoleId).Distinct().ToList(); + if (!roleIds.Any()) return new List(); - var roles = await _roleDefinitionRepository.QueryAsync(x => roleIds.Contains(x.Id)); - return roles.OrderBy(r => roleIds.IndexOf(r.Id)).ToList(); - } + var roles = await _roleDefinitionRepository.QueryAsync(x => roleIds.Contains(x.Id)); + return roles.OrderBy(r => roleIds.IndexOf(r.Id)).ToList(); + } - public async Task UpdateAsync(User user) - { - await _userRepository.UpdateAsync(user); - return true; - } + public async Task UpdateAsync(User user) + { + await _userRepository.UpdateAsync(user); + return true; + } - public async Task UpdateUserRolesAsync(string userId, List roleIds) + public async Task UpdateUserRolesAsync(string userId, List roleIds) + { + var dbUserRoles = await _userRoleRepository.QueryAsync(x => x.UserId == userId); + await _userRoleRepository.DeleteAsync(dbUserRoles); + var userRoles = new List(); + var now = DateTime.Now; + roleIds.Distinct().ToList().ForEach(x => { - var dbUserRoles = await _userRoleRepository.QueryAsync(x => x.UserId == userId); - await _userRoleRepository.DeleteAsync(dbUserRoles); - var userRoles = new List(); - var now = DateTime.Now; - roleIds.Distinct().ToList().ForEach(x => + userRoles.Add(new UserRole { - userRoles.Add(new UserRole - { - Id = Guid.NewGuid().ToString("N"), - UserId = userId, - RoleId = x, - LegacyRoleValue = null, - CreateTime = now - }); + Id = Guid.NewGuid().ToString("N"), + UserId = userId, + RoleId = x, + CreateTime = now }); + }); - await _userRoleRepository.InsertAsync(userRoles); - return true; - } - - public void Dispose() - { - _userRepository.Dispose(); - _userRoleRepository.Dispose(); - _roleDefinitionRepository.Dispose(); - } + await _userRoleRepository.InsertAsync(userRoles); + return true; + } - public Task> GetAll() - { - return _userRepository.AllAsync(); - } + public void Dispose() + { + _userRepository.Dispose(); + _userRoleRepository.Dispose(); + _roleDefinitionRepository.Dispose(); + } - public async Task ValidateUserPassword(string userName, string password) - { - var user = (await _userRepository.QueryAsync(u => u.Status == UserStatus.Normal && u.UserName == userName)).FirstOrDefault(); - if (user == null) - { - return false; - } + public Task> GetAll() + { + return _userRepository.AllAsync(); + } - if (user.Password == Encrypt.Md5(password + user.Salt)) - { - return true; - } + public async Task ValidateUserPassword(string userName, string password) + { + var user = (await _userRepository.QueryAsync(u => u.Status == UserStatus.Normal && u.UserName == userName)) + .FirstOrDefault(); + if (user == null) return false; - return false; - } + if (user.Password == Encrypt.Md5(password + user.Salt)) return true; - public async Task> GetUsersByRoleAsync(string roleId) - { - var userRoles = await _userRoleRepository.QueryAsync(x => x.RoleId == roleId); - var userIds = userRoles.Select(x => x.UserId).Distinct().ToList(); - return await _userRepository.QueryAsync(x => userIds.Contains(x.Id)); - } + return false; + } - private static string MapLegacyRole(int legacyRole) - { - return legacyRole switch - { - 0 => SystemRoleConstants.SuperAdminId, - 1 => SystemRoleConstants.AdminId, - 2 => SystemRoleConstants.OperatorId, - _ => string.Empty - }; - } + public async Task> GetUsersByRoleAsync(string roleId) + { + var userRoles = await _userRoleRepository.QueryAsync(x => x.RoleId == roleId); + var userIds = userRoles.Select(x => x.UserId).Distinct().ToList(); + return await _userRepository.QueryAsync(x => userIds.Contains(x.Id)); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.UI/react-ui-antd/config/routes.ts b/src/AgileConfig.Server.UI/react-ui-antd/config/routes.ts index 751e3850..ff0b94ec 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/config/routes.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/config/routes.ts @@ -38,86 +38,51 @@ path: '/', component: '../layouts/BasicLayout', routes: [ + { path: '/', redirect: '/home' }, + { path: '/index.html', redirect: '/home' }, { - path: '/', - redirect: '/home' + name: 'home', icon: 'Dashboard', path: '/home', component: './Home', + category: 'Application', functionKey: 'APP_READ' }, { - path: '/index.html', - redirect: '/home' + name: 'list.node-list', icon: 'Database', path: '/node', component: './Nodes', + category: 'Node', functionKey: 'NODE_READ' }, { - name: 'home', - icon: 'Dashboard', - path: '/home', - component: './Home', + name: 'list.app-list', icon: 'Appstore', path: '/app', component: './Apps', + category: 'Application', functionKey: 'APP_READ' }, { - name: 'list.node-list', - icon: 'Database', - path: '/node', - component: './Nodes', + name: 'list.config-list', icon: 'Table', path: '/app/config/:app_id/:app_name', component: './Configs', + hideInMenu: true, category: 'Configuration', functionKey: 'CONFIG_READ' }, { - name: 'list.app-list', - icon: 'Appstore', - path: '/app', - component: './Apps', - + name: 'list.client-list', icon: 'Shrink', path: '/client', component: './Clients', + category: 'Client', functionKey: 'CLIENT_REFRESH' }, { - name: 'list.config-list', - icon: 'Table', - path: '/app/config/:app_id/:app_name', - component: './Configs', - hideInMenu: true, + name: 'list.service-list', icon: 'Cloud', path: '/service', component: './Services', + category: 'Service', functionKey: 'SERVICE_READ' }, { - name: 'list.client-list', - icon: 'Shrink', - path: '/client', - component: './Clients', + name: 'list.user-list', icon: 'User', path: '/users', component: './User', + category: 'User', functionKey: 'USER_READ' }, { - name: 'list.service-list', - icon: 'Cloud', - path: '/service', - component: './Services', + name: 'list.role-list', icon: 'SafetyCertificate', path: '/roles', component: './Role', + category: 'Role', functionKey: 'ROLE_READ' }, { - name: 'list.user-list', - icon: 'User', - path: '/users', - component: './User', - authority: ['Admin'], - }, - { - name: 'list.role-list', - icon: 'SafetyCertificate', - path: '/roles', - component: './Role', - authority: ['Admin'], - }, - { - name: 'list.logs-list', - icon: 'Bars', - path: '/logs', - component: './Logs', - }, - { - component: './404', + name: 'list.logs-list', icon: 'Bars', path: '/logs', component: './Logs', + category: 'Log', functionKey: 'LOG_READ' }, + { component: './404' }, ], }, - { - component: './404', - }, + { component: './404' }, ], }, - ], }, - { - component: './404', - }, + { component: './404' }, ]; diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx index 5489b84f..280a8742 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx @@ -13,6 +13,7 @@ import React, { useEffect, useMemo, useRef } from 'react'; import { Dispatch, getIntl, getLocale } from 'umi'; import { Link, useIntl, connect, history } from 'umi'; import { Result, Button } from 'antd'; +import { getCategories, hasFunction } from '@/utils/authority'; import Authorized from '@/utils/Authorized'; import RightContent from '@/components/GlobalHeader/RightContent'; import type { ConnectState } from '@/models/connect'; @@ -45,14 +46,27 @@ export type BasicLayoutContext = { [K in 'location']: BasicLayoutProps[K] } & { }; /** Use Authorized check all menu item */ -const menuDataRender = (menuList: MenuDataItem[]): MenuDataItem[] => - menuList.map((item) => { - const localItem = { - ...item, - children: item.children ? menuDataRender(item.children) : undefined, - }; - return Authorized.check(item.authority, localItem, null) as MenuDataItem; - }); +// Filter menu by categories stored from login (e.g., Application, Configuration, Node, Client, User, Role, Service, System) +const menuDataRender = (menuList: MenuDataItem[]): MenuDataItem[] => { + const cats = getCategories(); + return menuList + .filter(m => { + // category filter + const category = (m as any).category; + if (category && !cats.includes(category)) return false; + // function key filter (for read permission) + const fnKey = (m as any).functionKey; + if (fnKey && !hasFunction(fnKey)) return false; + return true; + }) + .map((item) => { + const localItem = { + ...item, + children: item.children ? menuDataRender(item.children) : undefined, + }; + return Authorized.check(item.authority, localItem, null) as MenuDataItem; + }); +}; const BasicLayout: React.FC = (props) => { const { diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts index 54795148..8b2e916a 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts @@ -312,6 +312,16 @@ export default { 'pages.role.permissions.ROLE_ADD': 'Add Role', 'pages.role.permissions.ROLE_EDIT': 'Edit Role', 'pages.role.permissions.ROLE_DELETE': 'Delete Role', + 'pages.role.permissions.APP_READ': 'View App', + 'pages.role.permissions.CONFIG_READ': 'View Config', + 'pages.role.permissions.NODE_READ': 'View Node', + 'pages.role.permissions.CLIENT_REFRESH': 'Refresh Client', + 'pages.role.permissions.USER_READ': 'View User', + 'pages.role.permissions.ROLE_READ': 'View Role', + 'pages.role.permissions.SERVICE_READ': 'View Service', + 'pages.role.permissions.SERVICE_ADD': 'Add Service', + 'pages.role.permissions.SERVICE_DELETE': 'Delete Service', + 'pages.role.permissions.LOG_READ': 'View Logs', // Service Management 'pages.service.table.cols.servicename': 'Service Name', diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts index 7c61030a..7af614fd 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts @@ -310,6 +310,16 @@ export default { 'pages.role.permissions.ROLE_ADD': '新增角色', 'pages.role.permissions.ROLE_EDIT': '编辑角色', 'pages.role.permissions.ROLE_DELETE': '删除角色', + 'pages.role.permissions.APP_READ': '查看应用', + 'pages.role.permissions.CONFIG_READ': '查看配置', + 'pages.role.permissions.NODE_READ': '查看节点', + 'pages.role.permissions.CLIENT_REFRESH': '刷新客户端', + 'pages.role.permissions.USER_READ': '查看用户', + 'pages.role.permissions.ROLE_READ': '查看角色', + 'pages.role.permissions.SERVICE_READ': '查看服务', + 'pages.role.permissions.SERVICE_ADD': '新增服务', + 'pages.role.permissions.SERVICE_DELETE': '删除服务', + 'pages.role.permissions.LOG_READ': '查看日志', // Service Management 'pages.service.table.cols.servicename': '服务名', diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/models/functionKeys.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/models/functionKeys.ts index f221f564..1033426d 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/models/functionKeys.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/models/functionKeys.ts @@ -1,31 +1,45 @@ export default { - // public const string App_Add = "APP_ADD"; - // public const string App_Edit = "APP_EDIT"; - // public const string App_Delete = "APP_DELETE"; - // public const string App_Auth = "APP_AUTH"; - - // public const string Config_Add = "CONFIG_ADD"; - // public const string Config_Edit = "CONFIG_EDIT"; - // public const string Config_Delete = "CONFIG_DELETE"; - - // public const string Config_Publish = "CONFIG_PUBLISH"; - // public const string Config_Offline = "CONFIG_OFFLINE"; - - + // Application + App_Read: 'APP_READ', App_Add: 'APP_ADD', App_Edit: 'APP_EDIT', App_Delete: 'APP_DELETE', App_Auth: 'APP_AUTH', + // Configuration + Config_Read: 'CONFIG_READ', Config_Add: 'CONFIG_ADD', Config_Edit: 'CONFIG_EDIT', Config_Delete: 'CONFIG_DELETE', - Config_Publish: 'CONFIG_PUBLISH', Config_Offline: 'CONFIG_OFFLINE', - Node_Delete: 'NODE_DELETE', + // Node + Node_Read: 'NODE_READ', Node_Add: 'NODE_ADD', + Node_Delete: 'NODE_DELETE', - Client_Disconnect: 'CLIENT_DISCONNECT' -} \ No newline at end of file + // Client + Client_Refresh: 'CLIENT_REFRESH', + Client_Disconnect: 'CLIENT_DISCONNECT', + + // User + User_Read: 'USER_READ', + User_Add: 'USER_ADD', + User_Edit: 'USER_EDIT', + User_Delete: 'USER_DELETE', + + // Role + Role_Read: 'ROLE_READ', + Role_Add: 'ROLE_ADD', + Role_Edit: 'ROLE_EDIT', + Role_Delete: 'ROLE_DELETE', + + // Service + Service_Read: 'SERVICE_READ', + Service_Add: 'SERVICE_ADD', + Service_Delete: 'SERVICE_DELETE', + + // Log + Log_Read: 'LOG_READ' +}; \ No newline at end of file diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/models/login.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/models/login.ts index 3d3952e1..54ebbb17 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/models/login.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/models/login.ts @@ -1,7 +1,7 @@ import type { Reducer, Effect } from 'umi'; import { getIntl, history, getLocale } from 'umi'; import { accountLogin } from '@/services/login'; -import { setAuthority, setFunctions, setToken, setUserInfo } from '@/utils/authority'; +import { setAuthority, setFunctions, setToken, setUserInfo, setCategories } from '@/utils/authority'; import { getPageQuery } from '@/utils/utils'; import { message } from 'antd'; @@ -31,7 +31,7 @@ const Model: LoginModelType = { }, effects: { - *login({ payload }, { call, put }) { + *login({ payload }, { call, put }): Generator { const intl = getIntl(getLocale()); const response = yield call(accountLogin, payload); yield put({ @@ -69,10 +69,11 @@ const Model: LoginModelType = { } }, - logout() { + logout(): void { setToken(''); setAuthority([]); setFunctions([]); + setCategories([]); setUserInfo({ name: '', userid: '' }); const { redirect } = getPageQuery(); // Note: There may be security issues, please note @@ -90,6 +91,12 @@ const Model: LoginModelType = { setAuthority(payload.currentAuthority); setFunctions(payload.currentFunctions); setToken(payload.token); + if (payload.currentCategories) { + setCategories(payload.currentCategories); + } else { + // empty when not provided + setCategories([]); + } return { ...state, status: payload.status, diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/models/user.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/models/user.ts index 83a2d223..07a78547 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/models/user.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/models/user.ts @@ -1,7 +1,7 @@ import type { Effect, Reducer } from 'umi'; import { current, query as queryUsers } from '@/services/user'; -import { setAuthority, setFunctions, setUserInfo } from '@/utils/authority'; +import { setAuthority, setCategories, setFunctions, setUserInfo } from '@/utils/authority'; import { sys } from '@/services/system'; import { setSysInfo } from '@/utils/system'; @@ -63,7 +63,8 @@ const UserModel: UserModelType = { appVer: sysInfo.appVer, envList: sysInfo.envList, currentAuthority: currentInfo.currentUser?.currentAuthority, - currentFunctions: currentInfo.currentUser?.currentFunctions + currentFunctions: currentInfo.currentUser?.currentFunctions, + currentCategories: currentInfo.currentUser?.currentCategories, }; yield put({ type: 'saveCurrentSystemInfo', @@ -76,6 +77,7 @@ const UserModel: UserModelType = { saveCurrentSystemInfo(state, action) { setAuthority(action.payload.currentAuthority); setFunctions(action.payload.currentFunctions); + setCategories(action.payload.currentCategories); setUserInfo({name:action.payload.name, userid: action.payload.userid}); setSysInfo(action.payload.appVer, action.payload.envList); return { diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/index.tsx index bb06af68..e6293975 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/index.tsx @@ -40,6 +40,7 @@ import AuthorizedEle from '@/components/Authorized/AuthorizedElement'; import functionKeys from '@/models/functionKeys'; import { current } from '@/services/user'; import { getUserInfo, setAuthority, setFunctions } from '@/utils/authority'; +import { RequireFunction } from '@/utils/permission'; const { confirm } = Modal; @@ -369,16 +370,14 @@ const appList: React.FC = (props) => { }), valueType: 'option', render: (text, record, _, action) => [ - - {intl.formatMessage({ - id: 'pages.app.table.cols.action.configs', - })} - , + + + {intl.formatMessage({ id: 'pages.app.table.cols.action.configs' })} + + , { @@ -391,17 +390,17 @@ const appList: React.FC = (props) => { })} , - { - setUserAuthModalVisible(true); - setCurrentRow(record); - }} - > - {intl.formatMessage({ - id: 'pages.app.table.cols.action.auth', - })} - , + + { + setUserAuthModalVisible(true); + setCurrentRow(record); + }} + > + {intl.formatMessage({ id: 'pages.app.table.cols.action.auth' })} + + , - , + , ], }, ]; diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Configs/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Configs/index.tsx index ec13950e..b9341584 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Configs/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Configs/index.tsx @@ -13,7 +13,8 @@ import styles from './index.less'; import JsonImport from './comps/JsonImport'; import { useIntl } from 'react-intl'; import { getIntl, getLocale } from '@/.umi/plugin-locale/localeExports'; -import AuthorizedEle, { checkUserPermission } from '@/components/Authorized/AuthorizedElement'; +import { checkUserPermission } from '@/components/Authorized/AuthorizedElement'; +import { RequireFunction } from '@/utils/permission'; import functionKeys from '@/models/functionKeys'; import VersionHistory from './comps/versionHistory'; import { getFunctions } from '@/utils/authority'; @@ -370,80 +371,73 @@ const configs: React.FC = (props: any) => { width: 150, valueType: 'option', render: (text, record, _, action) => [ - + { setCurrentRow(record); - setUpdateModalVisible(true) + setUpdateModalVisible(true); }} > - { - intl.formatMessage({ - id: 'pages.configs.table.cols.action.edit' - }) - } + {intl.formatMessage({ id: 'pages.configs.table.cols.action.edit' })} - - , - + , + { delConfig(record); }} > - { - intl.formatMessage({ - id: 'pages.configs.table.cols.action.delete' - }) - } + {intl.formatMessage({ id: 'pages.configs.table.cols.action.delete' })} - - , - + , + - { - if (item == 'history') { - setCurrentRow(record); - setmodifyLogsModalVisible(true) - const result = await queryConfigPublishedHistory(record, currentEnv) - if (result.success) { - setModifyLogs(result.data); - } - } - - if (item == 'cancelEdit') { - confirm({ - content: intl.formatMessage({id:'pages.configs.confirm_cancel_edit'}) + `【${record.group?record.group:''}${record.group?':':''}${record.key}】` + intl.formatMessage({id:'pages.configs.confirm_cancel_edit_suffix'}), - onOk: async ()=>{ - const result = await handleCancelEdit(record.id, currentEnv); - if (result) { - actionRef.current?.reload(); - } - } - }) - } + key="actionGroup" + onSelect={async (item) => { + if (item == 'history') { + setCurrentRow(record); + setmodifyLogsModalVisible(true); + const result = await queryConfigPublishedHistory(record, currentEnv); + if (result.success) { + setModifyLogs(result.data); } } - menus={ - record.editStatus === 10 ? - [ - { key: 'history', name: intl.formatMessage({ - id: 'pages.configs.table.cols.action.history' - }) } - ]: - [ - { key: 'cancelEdit', name: intl.formatMessage({ - id: 'pages.configs.table.cols.action.cancel_edit' - }) }, - { key: 'history', name: intl.formatMessage({ - id: 'pages.configs.table.cols.action.history' - }) } - ] + if (item == 'cancelEdit') { + confirm({ + content: + intl.formatMessage({ id: 'pages.configs.confirm_cancel_edit' }) + + `【${record.group ? record.group : ''}${record.group ? ':' : ''}${record.key}】` + + intl.formatMessage({ id: 'pages.configs.confirm_cancel_edit_suffix' }), + onOk: async () => { + const result = await handleCancelEdit(record.id, currentEnv); + if (result) { + actionRef.current?.reload(); + } + }, + }); } - /> - + }} + menus={ + record.editStatus === 10 + ? [ + { + key: 'history', + name: intl.formatMessage({ id: 'pages.configs.table.cols.action.history' }), + }, + ] + : [ + { + key: 'cancelEdit', + name: intl.formatMessage({ id: 'pages.configs.table.cols.action.cancel_edit' }), + }, + { + key: 'history', + name: intl.formatMessage({ id: 'pages.configs.table.cols.action.history' }), + }, + ] + } + /> + , ] }, ]; @@ -510,100 +504,96 @@ const configs: React.FC = (props: any) => { } toolBarRender={() => [ - - - + , - - - + , - - { - selectedRowsState.filter(x=>x.editStatus !== 10).length > 0 ? - - : - <> - } - - + ) : null} + , - - { - selectedRowsState.length > 0 ? - :<> - } - - , - - - + ) : null} + + , + + + , - - - + + + , diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Home/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Home/index.tsx index 72be3527..81ee95ae 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Home/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Home/index.tsx @@ -6,6 +6,7 @@ import { queryNodes, addNode } from './../Nodes/service' import { ExclamationCircleOutlined } from '@ant-design/icons'; import { NodeItem } from './../Nodes/data'; import { message, Modal } from 'antd'; +import { hasFunction } from '@/utils/authority'; import { getIntl, getLocale } from 'umi'; import LatestVisitApps from './comps/latestVisitApps'; const { confirm } = Modal; @@ -71,31 +72,31 @@ const home: React.FC = () => { } useEffect(() => { - anyServerNode().then(data => { - if (!data) { - console.log('No nodes plz add one !'); - const intl = getIntl(getLocale()); - let confirmMsg = intl.formatMessage({id:'pages.home.empty_node_confirm'}) + `【${window.location.origin}】` + intl.formatMessage({id:'pages.home.to_node_list'}); - confirm({ - icon: , - content: confirmMsg, - onOk() { - const origin = window.location.origin; - console.log(` try add ${origin} to node list .`); - const node: NodeItem = { - address: origin, - remark: intl.formatMessage({ - id: 'pages.home.consoleNode', - }), - status: 0 - }; + // Only prompt to add current domain as a node if user has NODE_ADD permission + if (!hasFunction('NODE_ADD')) { + return; // skip check & prompt entirely + } + anyServerNode().then(exists => { + if (exists) return; + const intl = getIntl(getLocale()); + const confirmMsg = + intl.formatMessage({ id: 'pages.home.empty_node_confirm' }) + + `【${window.location.origin}】` + + intl.formatMessage({ id: 'pages.home.to_node_list' }); + confirm({ + icon: , + content: confirmMsg, + onOk() { + const origin = window.location.origin; + const node: NodeItem = { + address: origin, + remark: intl.formatMessage({ id: 'pages.home.consoleNode' }), + status: 0, + }; handleAdd(node); - }, - onCancel() { - }, - okText: intl.formatMessage({id: 'pages.home.add_now'}) - }) - } + }, + okText: intl.formatMessage({ id: 'pages.home.add_now' }), + }); }); }, []); useEffect(() => { diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Logs/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Logs/index.tsx index 5409b7c7..e0c70ee6 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Logs/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Logs/index.tsx @@ -1,26 +1,24 @@ import { PageContainer } from '@ant-design/pro-layout'; +import { RequireFunction } from '@/utils/permission'; import ProTable, { ProColumns } from '@ant-design/pro-table'; -import React, { useState, useRef, useEffect } from 'react'; -import { FormattedMessage, useIntl } from 'umi'; -import { TableListItem } from '../TableList/data'; +import React, { useState, useEffect } from 'react'; +import { useIntl } from 'umi'; import { queryLogs } from './service'; -import {queryApps} from '../Apps/service' +import { queryApps } from '../Apps/service'; const logs:React.FC = () => { const intl = useIntl(); const [appEnums, setAppEnums] = useState(); - const getAppEnums = async () => - { - const result = await queryApps({}) - const obj = {}; - result.data.forEach((x)=>{ - obj[x.id] = { - text: x.name + const getAppEnums = async () => { + const result = await queryApps({ sortField: 'createTime', ascOrDesc: 'descend', tableGrouped: false }); + const obj: Record = {}; + result.data.forEach((x: any) => { + if (x.id && x.name) { + obj[x.id] = { text: x.name }; } }); - return obj; - } + }; useEffect(()=>{ getAppEnums().then(x=> { console.log('app enums ', x); @@ -76,17 +74,15 @@ const logs:React.FC = () => { ]; return ( - - options={ - false - } - search={{ - labelWidth: 'auto', - }} - rowKey="id" - columns = {columns} - request = { (params, sorter, filter) => queryLogs({ ...params}) } - /> + {intl.formatMessage({ id: 'pages.role.permissions.LOG_READ', defaultMessage: 'No log permission' })}}> + queryLogs({ ...params })} + /> + ); } diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Nodes/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Nodes/index.tsx index 979a0faf..3b852f63 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Nodes/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Nodes/index.tsx @@ -3,12 +3,13 @@ import { ModalForm, ProFormText, ProFormTextArea } from '@ant-design/pro-form'; import { PageContainer } from '@ant-design/pro-layout'; import ProTable, { ActionType, ProColumns } from '@ant-design/pro-table'; import { Button, FormInstance, message,Modal } from 'antd'; +// hasFunction no longer needed after migrating to +import { RequireFunction } from '@/utils/permission'; import React, { useState, useRef } from 'react'; import { NodeItem } from './data'; import { queryNodes, addNode, delNode,allClientReload } from './service'; import { useIntl } from 'umi'; -import AuthorizedEle from '@/components/Authorized/AuthorizedElement'; -import functionKeys from '@/models/functionKeys'; +// Removed AuthorizedEle/functionKeys: using hasFunction for gating now const { confirm } = Modal; @@ -153,35 +154,26 @@ const nodeList:React.FC = () => { dataIndex: 'option', valueType: 'option', render: (_, record) => [ - + { confirm({ - title: intl.formatMessage({ - id: 'pages.nodes.confirmDelete', - }), + title: intl.formatMessage({ id: 'pages.nodes.confirmDelete' }), icon: , - content: intl.formatMessage({ - id: 'pages.nodes.confirmDeleteContent', - }), + content: intl.formatMessage({ id: 'pages.nodes.confirmDeleteContent' }), onOk() { handleDel(record).then((success) => { if (success) { - if (actionRef.current) { - actionRef.current.reload(); - } + actionRef.current?.reload(); } }); }, - onCancel() {}, }); }} > - {intl.formatMessage({ - id: 'pages.nodes.delete', - })} + {intl.formatMessage({ id: 'pages.nodes.delete' })} - , + , ], }, ]; @@ -198,42 +190,30 @@ const nodeList:React.FC = () => { labelWidth: 120, }} toolBarRender={() => [ - - - , - + + + + + , + - , + , ]} request={queryNodes} columns={columns} diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Nodes/index_new.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Nodes/index_new.tsx deleted file mode 100644 index 979a0faf..00000000 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Nodes/index_new.tsx +++ /dev/null @@ -1,289 +0,0 @@ -import { ExclamationCircleOutlined, PlusOutlined } from '@ant-design/icons'; -import { ModalForm, ProFormText, ProFormTextArea } from '@ant-design/pro-form'; -import { PageContainer } from '@ant-design/pro-layout'; -import ProTable, { ActionType, ProColumns } from '@ant-design/pro-table'; -import { Button, FormInstance, message,Modal } from 'antd'; -import React, { useState, useRef } from 'react'; -import { NodeItem } from './data'; -import { queryNodes, addNode, delNode,allClientReload } from './service'; -import { useIntl } from 'umi'; -import AuthorizedEle from '@/components/Authorized/AuthorizedElement'; -import functionKeys from '@/models/functionKeys'; - -const { confirm } = Modal; - -const nodeList:React.FC = () => { - const intl = useIntl(); - const actionRef = useRef(); - const addFormRef = useRef(); - const [createModalVisible, handleModalVisible] = useState(false); - - const handleAdd = async (fields: NodeItem) => { - const hide = message.loading(intl.formatMessage({ - id: 'saving' - })); - try { - const result = await addNode({ ...fields }); - hide(); - const success = result.success; - if (success) { - message.success(intl.formatMessage({ - id: 'save_success' - })); - } else { - message.error(intl.formatMessage({ - id: 'save_fail' - })); - } - return success; - } catch (error) { - hide(); - message.error(intl.formatMessage({ - id: 'save_fail' - })); - return false; - } - }; - - const handleAllReload = async (fields: NodeItem) => { - const hide = message.loading(intl.formatMessage({ - id: 'refreshing' - })); - try { - const result = await allClientReload({ ...fields }); - hide(); - const success = result.success; - if (success) { - message.success(intl.formatMessage({ - id: 'refresh_success' - })); - } else { - message.error(result.message); - } - return success; - } catch (error) { - hide(); - message.error(intl.formatMessage({ - id: 'refresh_fail' - })); - return false; - } - }; - - const handleDel = async (fields: NodeItem) => { - const hide = message.loading(intl.formatMessage({ - id: 'deleting' - })); - try { - const result = await delNode({ ...fields }); - hide(); - const success = result.success; - if (success) { - message.success(intl.formatMessage({ - id: 'delete_success' - })); - } else { - message.error(result.message); - } - return success; - } catch (error) { - hide(); - message.error(intl.formatMessage({ - id: 'delete_fail' - })); - return false; - } - }; - - const columns: ProColumns[] = [ - { - title: intl.formatMessage({ - id: 'pages.nodes.address', - }), - dataIndex: 'address', - }, - { - title: intl.formatMessage({ - id: 'pages.nodes.remark', - }), - dataIndex: 'remark', - hideInSearch: true, - }, - { - title: intl.formatMessage({ - id: 'pages.nodes.status', - }), - dataIndex: 'status', - hideInForm: true, - valueEnum: { - '0': { - text: intl.formatMessage({ - id: 'pages.nodes.statusOffline', - }), - status: 'Default', - }, - '1': { - text: intl.formatMessage({ - id: 'pages.nodes.statusOnline', - }), - status: 'Processing', - }, - }, - }, - { - title: intl.formatMessage({ - id: 'pages.nodes.lastEchoTime', - }), - dataIndex: 'lastEchoTime', - hideInSearch: true, - valueType: 'dateTime', - }, - { - title: intl.formatMessage({ - id: 'pages.nodes.createTime', - }), - dataIndex: 'createTime', - hideInSearch: true, - valueType: 'dateTime', - }, - { - title: intl.formatMessage({ - id: 'pages.nodes.operation', - }), - dataIndex: 'option', - valueType: 'option', - render: (_, record) => [ - - { - confirm({ - title: intl.formatMessage({ - id: 'pages.nodes.confirmDelete', - }), - icon: , - content: intl.formatMessage({ - id: 'pages.nodes.confirmDeleteContent', - }), - onOk() { - handleDel(record).then((success) => { - if (success) { - if (actionRef.current) { - actionRef.current.reload(); - } - } - }); - }, - onCancel() {}, - }); - }} - > - {intl.formatMessage({ - id: 'pages.nodes.delete', - })} - - , - ], - }, - ]; - - return ( - - - headerTitle={intl.formatMessage({ - id: 'pages.nodes.title', - })} - actionRef={actionRef} - rowKey="id" - search={{ - labelWidth: 120, - }} - toolBarRender={() => [ - - - , - - - , - ]} - request={queryNodes} - columns={columns} - /> - { - const success = await handleAdd(value as NodeItem); - if (success) { - handleModalVisible(false); - if (actionRef.current) { - actionRef.current.reload(); - } - } - }} - > - - - - - ); -}; - -export default nodeList; diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx index 7ed394fa..627c61a1 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx @@ -7,6 +7,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { useIntl } from 'umi'; import type { RoleFormValues, RoleItem } from './data'; import { createRole, deleteRole, fetchSupportedRolePermissions, queryRoles, updateRole } from '@/services/role'; +import { RequireFunction } from '@/utils/permission'; const { confirm } = Modal; @@ -127,12 +128,14 @@ const RolePage: React.FC = () => { dataIndex: 'functions', search: false, render: (_, record) => { - if (record.functions?.length === supportedPermissions.length) { - return {intl.formatMessage({ id: 'pages.role.permissions.all', defaultMessage: 'All' })}; + const fns = record.functions || []; + const hasAll = supportedPermissions.length > 0 && supportedPermissions.every(p => fns.includes(p)); + if (hasAll) { + return {intl.formatMessage({ id: 'pages.role.permissions.all', defaultMessage: '所有权限' })}; } return ( - {record.functions?.map((fn) => ( + {fns.map((fn) => ( {intl.formatMessage({ id: `pages.role.permissions.${fn}`, defaultMessage: fn })} ))} @@ -143,21 +146,24 @@ const RolePage: React.FC = () => { title: intl.formatMessage({ id: 'pages.role.table.cols.action', defaultMessage: 'Action' }), valueType: 'option', render: (_, record) => [ - { - setCurrentRole(record); - setUpdateModalVisible(true); - }} - > - {intl.formatMessage({ id: 'pages.role.table.cols.action.edit', defaultMessage: 'Edit' })} - , + + { + setCurrentRole(record); + setUpdateModalVisible(true); + }} + > + {intl.formatMessage({ id: 'pages.role.table.cols.action.edit', defaultMessage: 'Edit' })} + + , !record.isSystem ? ( - + + + ) : null, - ], + ].filter(Boolean), }, ]; @@ -179,17 +185,18 @@ const RolePage: React.FC = () => { }; }} toolBarRender={() => [ - , + + + , ]} /> diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Services/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Services/index.tsx index 8a33570a..f59a3fc4 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Services/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Services/index.tsx @@ -3,6 +3,7 @@ import { FormInstance, ModalForm, ProFormDependency, ProFormSelect, ProFormText import { PageContainer } from '@ant-design/pro-layout'; import ProTable, { ActionType, ProColumns } from '@ant-design/pro-table'; import { Button, message, Modal } from 'antd'; +import { RequireFunction } from '@/utils/permission'; import React, { useRef, useState } from 'react'; import { useIntl } from 'umi'; import { ServiceItem } from './data'; @@ -157,33 +158,28 @@ const services: React.FC = () => { width: 120 }, { - title: intl.formatMessage({ - id: 'pages.services.operation', - }), + title: intl.formatMessage({ id: 'pages.services.operation' }), valueType: 'option', - render: (text, record, _, action) => [ - { + render: (text, record) => [ + + { confirm({ - content: intl.formatMessage({ - id: 'pages.services.confirmDelete', - }), - onOk: async ()=>{ - const result = await handleDelSome(record) + content: intl.formatMessage({ id: 'pages.services.confirmDelete' }), + onOk: async () => { + const result = await handleDelSome(record); if (result) { actionRef.current?.reload(); } - } - }) - } - } - > - {intl.formatMessage({ - id: 'pages.services.delete', - })} - - ] + }, + }); + }} + > + {intl.formatMessage({ id: 'pages.services.delete' })} + + , + ], } ]; return ( @@ -211,15 +207,17 @@ const services: React.FC = () => { console.log(sortField, ascOrDesc); return queryService({ sortField, ascOrDesc, ...params }) }} - toolBarRender={()=> - [ - - ] - } + , + ]} /> { id: 'pages.user.table.cols.action' }), valueType: 'option', - render: (text, record, _, action) => checkUserListModifyPermission(record)?[ - { - setUpdateModalVisible(true); - setCurrentRow(record); - console.log('select user ', record); - console.log('current user ', currentRow); - }} - > - {intl.formatMessage({ - id: 'pages.user.table.cols.action.edit', - })} - , - { - const msg = intl.formatMessage({ - id: 'pages.user.confirm_reset', - }) + `【${record.userName}】` + intl.formatMessage({ - id: 'pages.user.reset_password_default', - }); - confirm({ - icon: , - content: msg, - async onOk() { - console.log('reset password user ' + record.userName); - const success = await handleResetPassword(record); - if (success) { - actionRef.current?.reload(); - } - }, - onCancel() { - console.log('Cancel'); - }, - }); - }} - > - {intl.formatMessage({ - id: 'pages.user.table.cols.action.reset', - })} - , - - ]:[] + render: (text, record, _, action) => { + if (!checkUserListModifyPermission(record)) return []; + const actions: React.ReactNode[] = []; + actions.push( + + { + setUpdateModalVisible(true); + setCurrentRow(record); + }} + > + {intl.formatMessage({ id: 'pages.user.table.cols.action.edit' })} + + + ); + actions.push( + + { + const msg = + intl.formatMessage({ id: 'pages.user.confirm_reset' }) + + `【${record.userName}】` + + intl.formatMessage({ id: 'pages.user.reset_password_default' }); + confirm({ + icon: , + content: msg, + async onOk() { + const success = await handleResetPassword(record); + if (success) { + actionRef.current?.reload(); + } + }, + }); + }} + > + {intl.formatMessage({ id: 'pages.user.table.cols.action.reset' })} + + + ); + actions.push( + + + + ); + return actions; + } } ]; return ( @@ -328,16 +329,17 @@ const userList:React.FC = () => { columns = {columns} request = { (params, sorter, filter) => queryUsers(params) } toolBarRender={() => [ - (hasUserRole('SuperAdmin')||hasUserRole('Admin'))? - - : - + + + , ]} /> diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/utils/authority.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/utils/authority.ts index 12b11180..d513e242 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/utils/authority.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/utils/authority.ts @@ -58,6 +58,42 @@ export function setFunctions(authority: string | string[] ): void { reloadAuthorized(); } +// categories (business domains) control which left-menu entries are visible +export function getCategories(str?: string): string[] { + const catString = + typeof str === 'undefined' && localStorage ? localStorage.getItem('antd-pro-categories') : str; + let cats; + try { + if (catString) { + cats = JSON.parse(catString); + } + } catch (e) { + cats = catString; + } + if (typeof cats === 'string') { + return [cats]; + } + return cats || []; +} + +export function setCategories(categories: string | string[]): void { + const arr = typeof categories === 'string' ? [categories] : categories; + localStorage.setItem('antd-pro-categories', JSON.stringify(arr)); + // menu re-eval + reloadAuthorized(); +} + +// convenience helpers +export function hasFunction(fnKey: string): boolean { + const fns = getFunctions(); + return Array.isArray(fns) ? fns.includes(fnKey) : false; +} + +export function hasCategory(cat: string): boolean { + const cats = getCategories(); + return Array.isArray(cats) ? cats.includes(cat) : false; +} + export function setToken(token:string): void { localStorage.setItem('token', token); } diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.ts new file mode 100644 index 00000000..722bdee7 --- /dev/null +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.ts @@ -0,0 +1,34 @@ +import React, { ReactNode, ReactElement } from 'react'; +import { hasFunction } from './authority'; + +// Hook: check a single function permission key. +export function useFunction(fnKey: string): boolean { + return hasFunction(fnKey); +} + +// Props for RequireFunction component +export interface RequireFunctionProps { + fn: string; + fallback?: ReactNode; + children?: ReactNode; +} + +// Component without JSX fragments (compatible with .ts) +export const RequireFunction: React.FC = ({ fn, fallback = null, children }): ReactElement | null => { + const allowed = useFunction(fn); + const safeChildren: ReactNode = children === undefined ? null : children; + const safeFallback: ReactNode = fallback === undefined ? null : fallback; + return allowed + ? React.createElement(React.Fragment, null, safeChildren) + : React.createElement(React.Fragment, null, safeFallback); +}; + +// ANY logic +export function hasAnyFunction(...fnKeys: string[]): boolean { + return fnKeys.some(k => hasFunction(k)); +} + +// ALL logic +export function hasAllFunctions(...fnKeys: string[]): boolean { + return fnKeys.every(k => hasFunction(k)); +} diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.tsx new file mode 100644 index 00000000..c243298b --- /dev/null +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.tsx @@ -0,0 +1,34 @@ +import React, { ReactNode } from 'react'; +import { hasFunction } from './authority'; + +/** Hook: check single function permission key */ +export function useFunction(fnKey: string): boolean { + return hasFunction(fnKey); +} + +interface RequireFunctionProps { + fn: string; + fallback?: ReactNode; + children?: ReactNode; + /** optional appId for app-scoped permission logic */ + appId?: string; + /** optional predicate for additional custom check */ + extraCheck?: () => boolean; +} + +/** Component: conditionally render children if user has function permission */ +export const RequireFunction: React.FC = ({ fn, fallback = null, children, appId, extraCheck }) => { + const allowed = useFunction(fn) && (!extraCheck || extraCheck()); + if (!allowed) return <>{fallback}; + return <>{children}; +}; + +/** ANY logic */ +export function hasAnyFunction(...fnKeys: string[]): boolean { + return fnKeys.some(k => hasFunction(k)); +} + +/** ALL logic */ +export function hasAllFunctions(...fnKeys: string[]): boolean { + return fnKeys.every(k => hasFunction(k)); +} \ No newline at end of file diff --git a/src/AgileConfig.Server.UI/react-ui-antd/tsconfig.json b/src/AgileConfig.Server.UI/react-ui-antd/tsconfig.json index 6d8ba2b8..102487e1 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/tsconfig.json +++ b/src/AgileConfig.Server.UI/react-ui-antd/tsconfig.json @@ -11,7 +11,6 @@ "moduleResolution": "node", "forceConsistentCasingInFileNames": true, "noImplicitReturns": true, - "suppressImplicitAnyIndexErrors": true, "noUnusedLocals": true, "allowJs": true, "skipLibCheck": true, diff --git a/test/AgileConfig.Server.CommonTests/DictionaryConvertToJsonTests.cs b/test/AgileConfig.Server.CommonTests/DictionaryConvertToJsonTests.cs index 182dadf3..b4d81f0b 100644 --- a/test/AgileConfig.Server.CommonTests/DictionaryConvertToJsonTests.cs +++ b/test/AgileConfig.Server.CommonTests/DictionaryConvertToJsonTests.cs @@ -1,157 +1,165 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using AgileConfig.Server.Common; -using System; +using System; using System.Collections.Generic; -using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace AgileConfig.Server.Common.Tests +namespace AgileConfig.Server.Common.Tests; + +[TestClass] +public class DictionaryConvertToJsonTests { - [TestClass()] - public class DictionaryConvertToJsonTests + [TestMethod] + public void ToJsonTest() { - [TestMethod()] - public void ToJsonTest() + var dict = new Dictionary + { + { "a", "1" } + }; + var json = DictionaryConvertToJson.ToJson(dict); + Assert.IsNotNull(json); + Console.WriteLine(json); + + dict = new Dictionary { - var dict = new Dictionary() { - {"a","1" } - }; - var json = DictionaryConvertToJson.ToJson(dict); - Assert.IsNotNull(json); - Console.WriteLine(json); + { "a", "1" }, + { "b", "2" } + }; + json = DictionaryConvertToJson.ToJson(dict); + Assert.IsNotNull(json); + Console.WriteLine(json); - dict = new Dictionary() { - {"a","1" }, - {"b","2" } - }; - json = DictionaryConvertToJson.ToJson(dict); - Assert.IsNotNull(json); - Console.WriteLine(json); + dict = new Dictionary + { + { "a", "1" }, + { "b", "2" }, + { "c:d", "3" } + }; + json = DictionaryConvertToJson.ToJson(dict); + Assert.IsNotNull(json); + Console.WriteLine(json); - dict = new Dictionary() { - {"a","1" }, - {"b","2" }, - {"c:d","3" } - }; - json = DictionaryConvertToJson.ToJson(dict); - Assert.IsNotNull(json); - Console.WriteLine(json); + dict = new Dictionary + { + { "a", "1" }, + { "b", "2" }, + { "c:d", "3" }, + { "e", "4" } + }; + json = DictionaryConvertToJson.ToJson(dict); + Assert.IsNotNull(json); + Console.WriteLine(json); - dict = new Dictionary() { - {"a","1" }, - {"b","2" }, - {"c:d","3" }, - {"e","4" } - }; - json = DictionaryConvertToJson.ToJson(dict); - Assert.IsNotNull(json); - Console.WriteLine(json); + dict = new Dictionary + { + { "a", "1" }, + { "b", "2" }, + { "c:d", "3" }, + { "e", "4" }, + { "f:g", "5" } + }; + json = DictionaryConvertToJson.ToJson(dict); + Assert.IsNotNull(json); + Console.WriteLine(json); - dict = new Dictionary() { - {"a","1" }, - {"b","2" }, - {"c:d","3" }, - {"e","4" }, - {"f:g","5" } - }; - json = DictionaryConvertToJson.ToJson(dict); - Assert.IsNotNull(json); - Console.WriteLine(json); + dict = new Dictionary + { + { "a", "1" }, + { "b", "2" }, + { "c:d", "3" }, + { "e", "4" }, + { "f:g", "5" }, + { "h:i:j", "6" } + }; + json = DictionaryConvertToJson.ToJson(dict); + Assert.IsNotNull(json); + Console.WriteLine(json); - dict = new Dictionary() { - {"a","1" }, - {"b","2" }, - {"c:d","3" }, - {"e","4" }, - {"f:g","5" }, - {"h:i:j","6" } - }; - json = DictionaryConvertToJson.ToJson(dict); - Assert.IsNotNull(json); - Console.WriteLine(json); + dict = new Dictionary + { + { "a", "1" }, + { "b", "2" }, + { "c:d", "3" }, + { "e", "4" }, + { "f:g", "5" }, + { "h:i:j", "6" }, + { "k", "7" } + }; + json = DictionaryConvertToJson.ToJson(dict); + Assert.IsNotNull(json); + Console.WriteLine(json); - dict = new Dictionary() { - {"a","1" }, - {"b","2" }, - {"c:d","3" }, - {"e","4" }, - {"f:g","5" }, - {"h:i:j","6" }, - {"k","7" }, - }; - json = DictionaryConvertToJson.ToJson(dict); - Assert.IsNotNull(json); - Console.WriteLine(json); + dict = new Dictionary + { + { "a", "1" }, + { "b", "2" }, + { "c:d", "3" }, + { "e", "4" }, + { "f:g", "5" }, + { "h:i:j", "6" }, + { "k", "7" }, + { "c:d1", "8" } + }; + json = DictionaryConvertToJson.ToJson(dict); + Assert.IsNotNull(json); + Console.WriteLine(json); - dict = new Dictionary() { - {"a","1" }, - {"b","2" }, - {"c:d","3" }, - {"e","4" }, - {"f:g","5" }, - {"h:i:j","6" }, - {"k","7" }, - {"c:d1","8" }, - }; - json = DictionaryConvertToJson.ToJson(dict); - Assert.IsNotNull(json); - Console.WriteLine(json); + dict = new Dictionary + { + { "a", "1" }, + { "b", "2" }, + { "c:d", "3" }, + { "e", "4" }, + { "f:g", "5" }, + { "h:i:j", "6" }, + { "k", "7" }, + { "c:d1", "8" }, + { "c:d2", "9" } + }; + json = DictionaryConvertToJson.ToJson(dict); + Assert.IsNotNull(json); + Console.WriteLine(json); - dict = new Dictionary() { - {"a","1" }, - {"b","2" }, - {"c:d","3" }, - {"e","4" }, - {"f:g","5" }, - {"h:i:j","6" }, - {"k","7" }, - {"c:d1","8" }, - {"c:d2","9" }, - }; - json = DictionaryConvertToJson.ToJson(dict); - Assert.IsNotNull(json); - Console.WriteLine(json); + dict = new Dictionary + { + { "a", "1" }, + { "b", "2" }, + { "c:d", "3" }, + { "e", "4" }, + { "f:g", "5" }, + { "h:i:j", "6" }, + { "k", "7" }, + { "c:d1", "8" }, + { "c:d2", "9" }, + { "c:d2:e", "10" }, + { "l", "11" }, + { "n", "12" } + }; + json = DictionaryConvertToJson.ToJson(dict); + Assert.IsNotNull(json); + Console.WriteLine(json); - dict = new Dictionary() { - {"a","1" }, - {"b","2" }, - {"c:d","3" }, - {"e","4" }, - {"f:g","5" }, - {"h:i:j","6" }, - {"k","7" }, - {"c:d1","8" }, - {"c:d2","9" }, - {"c:d2:e","10" }, - {"l","11" }, - {"n","12" }, - }; - json = DictionaryConvertToJson.ToJson(dict); - Assert.IsNotNull(json); - Console.WriteLine(json); - - dict = new Dictionary() { - {"a","1" }, - {"b","2" }, - {"c:d","3" }, - {"e","4" }, - {"f:g","5" }, - {"h:i:j","6" }, - {"k","7" }, - {"c:d1","8" }, - {"c:d2","9" }, - {"c:d2:e","10" }, - {"l","11" }, - {"n","12" }, - {"arr:0","1" }, - {"arr:1","2" }, - {"arr1:0:a","1" }, - {"arr1:1:a","2" }, - {"arr1:0:d1:d","1" }, - {"arr1:1:d2:d","2" }, - }; - json = DictionaryConvertToJson.ToJson(dict); - Assert.IsNotNull(json); - Console.WriteLine(json); - } + dict = new Dictionary + { + { "a", "1" }, + { "b", "2" }, + { "c:d", "3" }, + { "e", "4" }, + { "f:g", "5" }, + { "h:i:j", "6" }, + { "k", "7" }, + { "c:d1", "8" }, + { "c:d2", "9" }, + { "c:d2:e", "10" }, + { "l", "11" }, + { "n", "12" }, + { "arr:0", "1" }, + { "arr:1", "2" }, + { "arr1:0:a", "1" }, + { "arr1:1:a", "2" }, + { "arr1:0:d1:d", "1" }, + { "arr1:1:d2:d", "2" } + }; + json = DictionaryConvertToJson.ToJson(dict); + Assert.IsNotNull(json); + Console.WriteLine(json); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.Data.AbstractionTests/DbConfig/DbConfigInfoFactoryTests.cs b/test/AgileConfig.Server.Data.AbstractionTests/DbConfig/DbConfigInfoFactoryTests.cs index c837670b..53b21e33 100644 --- a/test/AgileConfig.Server.Data.AbstractionTests/DbConfig/DbConfigInfoFactoryTests.cs +++ b/test/AgileConfig.Server.Data.AbstractionTests/DbConfig/DbConfigInfoFactoryTests.cs @@ -2,46 +2,41 @@ using AgileConfig.Server.Data.Abstraction.DbProvider; using Microsoft.Extensions.Configuration; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace AgileConfig.Server.Data.AbstractionTests.DbConfig +namespace AgileConfig.Server.Data.AbstractionTests.DbConfig; + +[TestClass] +public class DbConfigInfoFactoryTests { - [TestClass] - public class DbConfigInfoFactoryTests + [TestMethod] + public void TestGetConfigInfo() { - [TestMethod] - public void TestGetConfigInfo() + var configMap = new Dictionary { - var configMap = new Dictionary() { - {"db:provider","sqlserver" }, - {"db:conn","localhost" }, - {"db:env:test:provider","sqlite" }, - {"db:env:test:conn","Data Source=agile_config.db" }, - }; + { "db:provider", "sqlserver" }, + { "db:conn", "localhost" }, + { "db:env:test:provider", "sqlite" }, + { "db:env:test:conn", "Data Source=agile_config.db" } + }; - ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); - configurationBuilder.AddInMemoryCollection(configMap); - var configuration = configurationBuilder.Build(); - Global.Config = configuration; + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddInMemoryCollection(configMap); + var configuration = configurationBuilder.Build(); + Global.Config = configuration; - var configInfo = new DbConfigInfoFactory(configuration).GetConfigInfo(); - Assert.IsNotNull(configInfo); - Assert.AreEqual("sqlserver", configInfo.Provider); - Assert.AreEqual("localhost", configInfo.ConnectionString); + var configInfo = new DbConfigInfoFactory(configuration).GetConfigInfo(); + Assert.IsNotNull(configInfo); + Assert.AreEqual("sqlserver", configInfo.Provider); + Assert.AreEqual("localhost", configInfo.ConnectionString); - configInfo = new DbConfigInfoFactory(configuration).GetConfigInfo("test"); - Assert.IsNotNull(configInfo); - Assert.AreEqual("sqlite", configInfo.Provider); - Assert.AreEqual("Data Source=agile_config.db", configInfo.ConnectionString); + configInfo = new DbConfigInfoFactory(configuration).GetConfigInfo("test"); + Assert.IsNotNull(configInfo); + Assert.AreEqual("sqlite", configInfo.Provider); + Assert.AreEqual("Data Source=agile_config.db", configInfo.ConnectionString); - configInfo = new DbConfigInfoFactory(configuration).GetConfigInfo("x"); - Assert.IsNotNull(configInfo); - Assert.AreEqual("sqlserver", configInfo.Provider); - Assert.AreEqual("localhost", configInfo.ConnectionString); - } + configInfo = new DbConfigInfoFactory(configuration).GetConfigInfo("x"); + Assert.IsNotNull(configInfo); + Assert.AreEqual("sqlserver", configInfo.Provider); + Assert.AreEqual("localhost", configInfo.ConnectionString); } -} +} \ No newline at end of file diff --git a/test/AgileConfig.Server.Data.FreesqlTests/FreeSQLTests.cs b/test/AgileConfig.Server.Data.FreesqlTests/FreeSQLTests.cs index d65ef416..106ade26 100644 --- a/test/AgileConfig.Server.Data.FreesqlTests/FreeSQLTests.cs +++ b/test/AgileConfig.Server.Data.FreesqlTests/FreeSQLTests.cs @@ -1,52 +1,47 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using AgileConfig.Server.Data.Freesql; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Extensions.Configuration; -using AgileConfig.Server.Common; +using AgileConfig.Server.Common; using AgileConfig.Server.Data.Abstraction.DbProvider; +using FreeSql; +using Microsoft.Extensions.Configuration; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace AgileConfig.Server.Data.Freesql.Tests; -namespace AgileConfig.Server.Data.Freesql.Tests +[TestClass] +public class FreeSQLTests { - [TestClass()] - public class FreeSQLTests + [TestMethod] + public void GetInstanceByEnvTest() { - [TestMethod()] - public void GetInstanceByEnvTest() + var configMap = new Dictionary { - var configMap = new Dictionary() { - {"db:provider","sqlite" }, - {"db:conn","Data Source=agile_config.db" }, - {"db:env:test:provider","sqlite" }, - {"db:env:test:conn","Data Source=agile_config1.db" }, - }; - ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); - configurationBuilder.AddInMemoryCollection(configMap); - var configuration = configurationBuilder.Build(); - Global.Config = configuration; - - var myfreesql = new MyFreeSQL(new DbConfigInfoFactory(configuration)); - - var fsq = myfreesql.GetInstanceByEnv(""); - Assert.IsNotNull(fsq); - Assert.AreEqual(FreeSql.DataType.Sqlite, fsq.Ado.DataType); - - var fsqtest = myfreesql.GetInstanceByEnv("test"); - Assert.IsNotNull(fsqtest); - Assert.AreEqual(FreeSql.DataType.Sqlite, fsqtest.Ado.DataType); - - Assert.AreNotSame(fsq, fsqtest); - var fsqtest_ag = myfreesql.GetInstanceByEnv("test"); - Assert.AreSame(fsqtest, fsqtest_ag); - - - var fsq_none = myfreesql.GetInstanceByEnv("x"); - Assert.IsNotNull(fsq_none); - Assert.AreEqual(FreeSql.DataType.Sqlite, fsq_none.Ado.DataType); - Assert.AreSame(fsq, fsq_none); - } + { "db:provider", "sqlite" }, + { "db:conn", "Data Source=agile_config.db" }, + { "db:env:test:provider", "sqlite" }, + { "db:env:test:conn", "Data Source=agile_config1.db" } + }; + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddInMemoryCollection(configMap); + var configuration = configurationBuilder.Build(); + Global.Config = configuration; + + var myfreesql = new MyFreeSQL(new DbConfigInfoFactory(configuration)); + + var fsq = myfreesql.GetInstanceByEnv(""); + Assert.IsNotNull(fsq); + Assert.AreEqual(DataType.Sqlite, fsq.Ado.DataType); + + var fsqtest = myfreesql.GetInstanceByEnv("test"); + Assert.IsNotNull(fsqtest); + Assert.AreEqual(DataType.Sqlite, fsqtest.Ado.DataType); + + Assert.AreNotSame(fsq, fsqtest); + var fsqtest_ag = myfreesql.GetInstanceByEnv("test"); + Assert.AreSame(fsqtest, fsqtest_ag); + + + var fsq_none = myfreesql.GetInstanceByEnv("x"); + Assert.IsNotNull(fsq_none); + Assert.AreEqual(DataType.Sqlite, fsq_none.Ado.DataType); + Assert.AreSame(fsq, fsq_none); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.Data.FreesqlTests/FreeSqlUowTests.cs b/test/AgileConfig.Server.Data.FreesqlTests/FreeSqlUowTests.cs index 0c0954c1..802fdcff 100644 --- a/test/AgileConfig.Server.Data.FreesqlTests/FreeSqlUowTests.cs +++ b/test/AgileConfig.Server.Data.FreesqlTests/FreeSqlUowTests.cs @@ -1,168 +1,161 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using AgileConfig.Server.Data.Freesql; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using AgileConfig.Server.Data.Abstraction.DbProvider; using Microsoft.Extensions.Configuration; -using AgileConfig.Server.Common; -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.Data.Abstraction.DbProvider; -using System.Configuration; -using Microsoft.Testing.Platform.Configurations; +using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace AgileConfig.Server.Data.Freesql.Tests +namespace AgileConfig.Server.Data.Freesql.Tests; + +[TestClass] +public class FreeSqlUowTests { - [TestClass()] - public class FreeSqlUowTests + [TestInitialize] + public void TestInitialize() { - [TestInitialize] - public void TestInitialize() + var configMap = new Dictionary { - var configMap = new Dictionary() { - {"db:provider","sqlite" }, - {"db:conn","Data Source=agile_config.db" }, - {"db:env:test:provider","sqlite" }, - {"db:env:test:conn","Data Source=agile_config1.db" }, - }; - ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); - configurationBuilder.AddInMemoryCollection(configMap); - var configuration = configurationBuilder.Build(); - //Global.Config = configuration; - - var myfreesql = new MyFreeSQL(new DbConfigInfoFactory(configuration)); - - var fsql = myfreesql.GetInstanceByEnv(""); - fsql.CodeFirst.SyncStructure(); - fsql.CodeFirst.SyncStructure(); - fsql.Delete(new User_test() { Id = 1 }).ExecuteAffrows(); - fsql.Delete(new Address_test() { Id = 1 }).ExecuteAffrows(); - } + { "db:provider", "sqlite" }, + { "db:conn", "Data Source=agile_config.db" }, + { "db:env:test:provider", "sqlite" }, + { "db:env:test:conn", "Data Source=agile_config1.db" } + }; + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddInMemoryCollection(configMap); + var configuration = configurationBuilder.Build(); + //Global.Config = configuration; + + var myfreesql = new MyFreeSQL(new DbConfigInfoFactory(configuration)); + + var fsql = myfreesql.GetInstanceByEnv(""); + fsql.CodeFirst.SyncStructure(); + fsql.CodeFirst.SyncStructure(); + fsql.Delete(new User_test { Id = 1 }).ExecuteAffrows(); + fsql.Delete(new Address_test { Id = 1 }).ExecuteAffrows(); + } - [TestMethod()] - public async Task SaveChangesAsyncTest_success() + [TestMethod] + public async Task SaveChangesAsyncTest_success() + { + // arrange + var configMap = new Dictionary + { + { "db:provider", "sqlite" }, + { "db:conn", "Data Source=agile_config.db" }, + { "db:env:test:provider", "sqlite" }, + { "db:env:test:conn", "Data Source=agile_config1.db" } + }; + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddInMemoryCollection(configMap); + var configuration = configurationBuilder.Build(); + var myfreesql = new MyFreeSQL(new DbConfigInfoFactory(configuration)); + + var fsql = myfreesql.GetInstanceByEnv(""); + var user = new User_test + { + Id = 1, + Name = "abc" + }; + var address = new Address_test { - // arrange - var configMap = new Dictionary() { - {"db:provider","sqlite" }, - {"db:conn","Data Source=agile_config.db" }, - {"db:env:test:provider","sqlite" }, - {"db:env:test:conn","Data Source=agile_config1.db" }, - }; - ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); - configurationBuilder.AddInMemoryCollection(configMap); - var configuration = configurationBuilder.Build(); - var myfreesql = new MyFreeSQL(new DbConfigInfoFactory(configuration)); - - var fsql = myfreesql.GetInstanceByEnv(""); - var user = new User_test() - { - Id = 1, - Name = "abc" - }; - var address = new Address_test() - { - Id = 1, - Address = "Address" - }; - // act - using var uow = new FreeSqlUow(fsql); - - var userrepository = fsql.GetRepository(); - userrepository.UnitOfWork = uow.GetFreesqlUnitOfWork(); - var addressrepository = fsql.GetRepository(); - addressrepository.UnitOfWork = uow.GetFreesqlUnitOfWork(); - - uow.Begin(); + Id = 1, + Address = "Address" + }; + // act + using var uow = new FreeSqlUow(fsql); + + var userrepository = fsql.GetRepository(); + userrepository.UnitOfWork = uow.GetFreesqlUnitOfWork(); + var addressrepository = fsql.GetRepository(); + addressrepository.UnitOfWork = uow.GetFreesqlUnitOfWork(); + + uow.Begin(); + + userrepository.Insert(user); + user.Name = "test1"; + userrepository.Update(user); + addressrepository.Insert(address); + address.Address = "test1"; + addressrepository.Update(address); + + await uow.SaveChangesAsync(); + + // assert + var username = fsql.GetRepository().Where(x => x.Id == 1).ToOne().Name; + var add = fsql.GetRepository().Where(x => x.Id == 1).ToOne().Address; + Assert.AreEqual("test1", username); + Assert.AreEqual("test1", add); + } + [TestMethod] + public async Task SaveChangesAsyncTest_rollback() + { + // arrange + var configMap = new Dictionary + { + { "db:provider", "sqlite" }, + { "db:conn", "Data Source=agile_config.db" }, + { "db:env:test:provider", "sqlite" }, + { "db:env:test:conn", "Data Source=agile_config1.db" } + }; + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddInMemoryCollection(configMap); + var configuration = configurationBuilder.Build(); + var myfreesql = new MyFreeSQL(new DbConfigInfoFactory(configuration)); + + var fsql = myfreesql.GetInstanceByEnv(""); + var user = new User_test + { + Id = 2, + Name = "abc" + }; + var address = new Address_test + { + Id = 2, + Address = "Address" + }; + + // act + using var uow = new FreeSqlUow(fsql); + var userrepository = fsql.GetRepository(); + userrepository.UnitOfWork = uow.GetFreesqlUnitOfWork(); + var addressrepository = fsql.GetRepository(); + addressrepository.UnitOfWork = uow.GetFreesqlUnitOfWork(); + + uow.Begin(); + try + { userrepository.Insert(user); user.Name = "test1"; userrepository.Update(user); + throw new Exception("test"); addressrepository.Insert(address); address.Address = "test1"; addressrepository.Update(address); await uow.SaveChangesAsync(); - - // assert - var username = fsql.GetRepository().Where(x => x.Id == 1).ToOne().Name; - var add = fsql.GetRepository().Where(x => x.Id == 1).ToOne().Address; - Assert.AreEqual("test1", username); - Assert.AreEqual("test1", add); } - - [TestMethod()] - public async Task SaveChangesAsyncTest_rollback() + catch (Exception exc) { - // arrange - var configMap = new Dictionary() { - {"db:provider","sqlite" }, - {"db:conn","Data Source=agile_config.db" }, - {"db:env:test:provider","sqlite" }, - {"db:env:test:conn","Data Source=agile_config1.db" }, - }; - ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); - configurationBuilder.AddInMemoryCollection(configMap); - var configuration = configurationBuilder.Build(); - var myfreesql = new MyFreeSQL(new DbConfigInfoFactory(configuration)); - - var fsql = myfreesql.GetInstanceByEnv(""); - var user = new User_test() - { - Id = 2, - Name = "abc" - }; - var address = new Address_test() - { - Id = 2, - Address = "Address" - }; - - // act - using var uow = new FreeSqlUow(fsql); - var userrepository = fsql.GetRepository(); - userrepository.UnitOfWork = uow.GetFreesqlUnitOfWork(); - var addressrepository = fsql.GetRepository(); - addressrepository.UnitOfWork = uow.GetFreesqlUnitOfWork(); - - uow.Begin(); - try - { - userrepository.Insert(user); - user.Name = "test1"; - userrepository.Update(user); - throw new Exception("test"); - addressrepository.Insert(address); - address.Address = "test1"; - addressrepository.Update(address); - - await uow.SaveChangesAsync(); - } - catch (Exception exc) - { - Assert.IsNotNull(exc); - } - - - // assert - var _user = fsql.GetRepository().Where(x => x.Id == 2).ToOne(); - var _address = fsql.GetRepository().Where(x => x.Id == 2).ToOne(); - - Assert.IsNull(_user); - Assert.IsNull(_address); + Assert.IsNotNull(exc); } - } - public class User_test - { - public int Id { get; set; } - public string Name { get; set; } - } - public class Address_test - { - public int Id { get; set; } - public string Address { get; set; } + + // assert + var _user = fsql.GetRepository().Where(x => x.Id == 2).ToOne(); + var _address = fsql.GetRepository().Where(x => x.Id == 2).ToOne(); + + Assert.IsNull(_user); + Assert.IsNull(_address); } +} + +public class User_test +{ + public int Id { get; set; } + public string Name { get; set; } +} + +public class Address_test +{ + public int Id { get; set; } + public string Address { get; set; } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/EnsureTablesTests.cs b/test/AgileConfig.Server.ServiceTests/EnsureTablesTests.cs index 31e9823d..3979dd5e 100644 --- a/test/AgileConfig.Server.ServiceTests/EnsureTablesTests.cs +++ b/test/AgileConfig.Server.ServiceTests/EnsureTablesTests.cs @@ -1,105 +1,104 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; -using System; -using System.Collections.Generic; -using System.Text; using FreeSql; -using AgileConfig.Server.Data.Entity; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace AgileConfig.Server.ServiceTests; -namespace AgileConfig.Server.ServiceTests +public class EnsureTablesTests { - public class EnsureTablesTests + [TestMethod] + public void ExistTableSqliteTest() { - [TestMethod()] - public void ExistTableSqliteTest() - { - //sqlite - string conn = "Data Source=agile_config.db"; - var sqllite_fsq = new FreeSqlBuilder() - .UseConnectionString(DataType.Sqlite, conn) - .Build(); - FluentApi.Config(sqllite_fsq); - sqllite_fsq.Ado.ExecuteNonQuery("drop table agc_app"); - var ex = EnsureTables.ExistTable(sqllite_fsq); - Assert.IsFalse(ex); - sqllite_fsq.CodeFirst.SyncStructure(); - ex = EnsureTables.ExistTable(sqllite_fsq); - Assert.IsTrue(ex); - } + //sqlite + var conn = "Data Source=agile_config.db"; + var sqllite_fsq = new FreeSqlBuilder() + .UseConnectionString(DataType.Sqlite, conn) + .Build(); + FluentApi.Config(sqllite_fsq); + sqllite_fsq.Ado.ExecuteNonQuery("drop table agc_app"); + var ex = EnsureTables.ExistTable(sqllite_fsq); + Assert.IsFalse(ex); + sqllite_fsq.CodeFirst.SyncStructure(); + ex = EnsureTables.ExistTable(sqllite_fsq); + Assert.IsTrue(ex); + } - [TestMethod()] - public void ExistTableSqlServerTest() - { - //SqlServer - string conn = "TrustServerCertificate=True;Persist Security Info = False; User ID =dev; Password =dev; Initial Catalog =agile_config_test; Server =."; - var sqlserver_fsq = new FreeSqlBuilder() - .UseConnectionString(DataType.SqlServer, conn) - .Build(); - FluentApi.Config(sqlserver_fsq); - sqlserver_fsq.Ado.ExecuteNonQuery("drop table agc_app"); - var ex = EnsureTables.ExistTable(sqlserver_fsq); - Assert.IsFalse(ex); - sqlserver_fsq.CodeFirst.SyncStructure(); - ex = EnsureTables.ExistTable(sqlserver_fsq); - Assert.IsTrue(ex); - } + [TestMethod] + public void ExistTableSqlServerTest() + { + //SqlServer + var conn = + "TrustServerCertificate=True;Persist Security Info = False; User ID =dev; Password =dev; Initial Catalog =agile_config_test; Server =."; + var sqlserver_fsq = new FreeSqlBuilder() + .UseConnectionString(DataType.SqlServer, conn) + .Build(); + FluentApi.Config(sqlserver_fsq); + sqlserver_fsq.Ado.ExecuteNonQuery("drop table agc_app"); + var ex = EnsureTables.ExistTable(sqlserver_fsq); + Assert.IsFalse(ex); + sqlserver_fsq.CodeFirst.SyncStructure(); + ex = EnsureTables.ExistTable(sqlserver_fsq); + Assert.IsTrue(ex); + } - [TestMethod()] - public void ExistTableMysqlTest() + [TestMethod] + public void ExistTableMysqlTest() + { + //SqlServer + var conn = "Database=agile_config_test;Data Source=192.168.0.125;User Id=root;Password=x;port=13306"; + var mysql_fsq = new FreeSqlBuilder() + .UseConnectionString(DataType.MySql, conn) + .Build(); + FluentApi.Config(mysql_fsq); + try { - //SqlServer - string conn = "Database=agile_config_test;Data Source=192.168.0.125;User Id=root;Password=x;port=13306"; - var mysql_fsq = new FreeSqlBuilder() - .UseConnectionString(DataType.MySql, conn) - .Build(); - FluentApi.Config(mysql_fsq); - try - { - mysql_fsq.Ado.ExecuteNonQuery("drop table agc_app"); - } - catch (Exception e) - { - Console.WriteLine(e); - } - var ex = EnsureTables.ExistTable(mysql_fsq); - Assert.IsFalse(ex); - mysql_fsq.CodeFirst.SyncStructure(); - ex = EnsureTables.ExistTable(mysql_fsq); - Assert.IsTrue(ex); + mysql_fsq.Ado.ExecuteNonQuery("drop table agc_app"); } - - [TestMethod()] - public void ExistTableOracleTest() + catch (Exception e) { - //SqlServer - string conn = "user id=CLINIC;password=CLINIC;data source=192.168.0.91/orcl"; - var oracle_fsq = new FreeSqlBuilder() - .UseConnectionString(DataType.Oracle, conn) - .Build(); - FluentApi.Config(oracle_fsq); - oracle_fsq.Ado.ExecuteNonQuery("drop table \"agc_app\" "); - var ex = EnsureTables.ExistTable(oracle_fsq); - Assert.IsFalse(ex); - oracle_fsq.CodeFirst.SyncStructure(); - ex = EnsureTables.ExistTable(oracle_fsq); - Assert.IsTrue(ex); + Console.WriteLine(e); } - [TestMethod()] - public void ExistTablePostgreSqlTest() - { - //SqlServer - string conn = "Host=192.168.0.125;Port=15432;Database=agileconfig;Username=postgres;Password=123456"; - var postgresql_fsq = new FreeSqlBuilder() - .UseConnectionString(DataType.PostgreSQL, conn) - .Build(); - FluentApi.Config(postgresql_fsq); - postgresql_fsq.Ado.ExecuteNonQuery("drop table \"agc_app\" "); - var ex = EnsureTables.ExistTable(postgresql_fsq); - Assert.IsFalse(ex); - postgresql_fsq.CodeFirst.SyncStructure(); - ex = EnsureTables.ExistTable(postgresql_fsq); - Assert.IsTrue(ex); - } + var ex = EnsureTables.ExistTable(mysql_fsq); + Assert.IsFalse(ex); + mysql_fsq.CodeFirst.SyncStructure(); + ex = EnsureTables.ExistTable(mysql_fsq); + Assert.IsTrue(ex); + } + + [TestMethod] + public void ExistTableOracleTest() + { + //SqlServer + var conn = "user id=CLINIC;password=CLINIC;data source=192.168.0.91/orcl"; + var oracle_fsq = new FreeSqlBuilder() + .UseConnectionString(DataType.Oracle, conn) + .Build(); + FluentApi.Config(oracle_fsq); + oracle_fsq.Ado.ExecuteNonQuery("drop table \"agc_app\" "); + var ex = EnsureTables.ExistTable(oracle_fsq); + Assert.IsFalse(ex); + oracle_fsq.CodeFirst.SyncStructure(); + ex = EnsureTables.ExistTable(oracle_fsq); + Assert.IsTrue(ex); + } + + [TestMethod] + public void ExistTablePostgreSqlTest() + { + //SqlServer + var conn = "Host=192.168.0.125;Port=15432;Database=agileconfig;Username=postgres;Password=123456"; + var postgresql_fsq = new FreeSqlBuilder() + .UseConnectionString(DataType.PostgreSQL, conn) + .Build(); + FluentApi.Config(postgresql_fsq); + postgresql_fsq.Ado.ExecuteNonQuery("drop table \"agc_app\" "); + var ex = EnsureTables.ExistTable(postgresql_fsq); + Assert.IsFalse(ex); + postgresql_fsq.CodeFirst.SyncStructure(); + ex = EnsureTables.ExistTable(postgresql_fsq); + Assert.IsTrue(ex); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/ISettingServiceTests.cs b/test/AgileConfig.Server.ServiceTests/ISettingServiceTests.cs index 251556b4..44b3f98f 100644 --- a/test/AgileConfig.Server.ServiceTests/ISettingServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/ISettingServiceTests.cs @@ -1,51 +1,50 @@ using AgileConfig.Server.IService; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace AgileConfig.Server.ServiceTests +namespace AgileConfig.Server.ServiceTests; + +[TestClass] +public class ISettingServiceTests { - [TestClass] - public class ISettingServiceTests + [TestMethod] + public void IfEnvEmptySetDefaultTest() { - [TestMethod] - public void IfEnvEmptySetDefaultTest() - { - // Arrange - string env = "test"; - ISettingService.EnvironmentList = new string[] { "test", "prod" }; - - // Act - ISettingService.IfEnvEmptySetDefault(ref env); - - // Assert - Assert.AreEqual("test", env); - } - - [TestMethod] - public void IfEnvEmptySetDefault_envEmpty_shouldReturn_defaul() - { - // Arrange - string env = ""; - ISettingService.EnvironmentList = new string[] { "test", "prod" }; - - // Act - ISettingService.IfEnvEmptySetDefault(ref env); - - // Assert - Assert.AreEqual("test", env); - } - - [TestMethod] - public void IfEnvEmptySetDefault_envNull_shouldReturn_defaul() - { - // Arrange - string env = null; - ISettingService.EnvironmentList = new string[] { "test", "prod" }; - - // Act - ISettingService.IfEnvEmptySetDefault(ref env); - - // Assert - Assert.AreEqual("test", env); - } + // Arrange + var env = "test"; + ISettingService.EnvironmentList = new[] { "test", "prod" }; + + // Act + ISettingService.IfEnvEmptySetDefault(ref env); + + // Assert + Assert.AreEqual("test", env); + } + + [TestMethod] + public void IfEnvEmptySetDefault_envEmpty_shouldReturn_defaul() + { + // Arrange + var env = ""; + ISettingService.EnvironmentList = new[] { "test", "prod" }; + + // Act + ISettingService.IfEnvEmptySetDefault(ref env); + + // Assert + Assert.AreEqual("test", env); + } + + [TestMethod] + public void IfEnvEmptySetDefault_envNull_shouldReturn_defaul() + { + // Arrange + string env = null; + ISettingService.EnvironmentList = new[] { "test", "prod" }; + + // Act + ISettingService.IfEnvEmptySetDefault(ref env); + + // Assert + Assert.AreEqual("test", env); } -} +} \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/PostgreSQL/AppServiceTests.cs b/test/AgileConfig.Server.ServiceTests/PostgreSQL/AppServiceTests.cs index d73880d9..67c5669c 100644 --- a/test/AgileConfig.Server.ServiceTests/PostgreSQL/AppServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/PostgreSQL/AppServiceTests.cs @@ -1,41 +1,40 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; using System.Threading.Tasks; -using System; +using AgileConfig.Server.ServiceTests.sqlite; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Testcontainers.PostgreSql; -namespace AgileConfig.Server.ServiceTests.PostgreSQL +namespace AgileConfig.Server.ServiceTests.PostgreSQL; + +public class AppServiceTests_pg : AppServiceTests { - public class AppServiceTests_pg : AppServiceTests + private static readonly PostgreSqlContainer _container = new PostgreSqlBuilder().WithImage("postgres:15.1").Build(); + + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) + { + await _container.StartAsync(); + Console.WriteLine("PostgreSqlContainer started"); + } + + [ClassCleanup] + public static async Task ClassCleanup() + { + await _container.DisposeAsync(); + Console.WriteLine("PostgreSqlContainer dispose"); + } + + public override Task> GetConfigurationData() { - static PostgreSqlContainer _container = new PostgreSqlBuilder().WithImage("postgres:15.1").Build(); - - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - await _container.StartAsync(); - Console.WriteLine($"PostgreSqlContainer started"); - } - - [ClassCleanup] - public static async Task ClassCleanup() - { - await _container.DisposeAsync(); - Console.WriteLine($"PostgreSqlContainer dispose"); - } - public override Task> GetConfigurationData() - { - var connstr = _container.GetConnectionString(); - Console.WriteLine($"PostgreSqlContainer connstr: {connstr}"); - - var dict = new Dictionary(); - dict["db:provider"] = "pg"; - dict["db:conn"] = connstr; - - - return Task.FromResult(dict); - } + var connstr = _container.GetConnectionString(); + Console.WriteLine($"PostgreSqlContainer connstr: {connstr}"); + + var dict = new Dictionary(); + dict["db:provider"] = "pg"; + dict["db:conn"] = connstr; + + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/PostgreSQL/ConfigServiceTests.cs b/test/AgileConfig.Server.ServiceTests/PostgreSQL/ConfigServiceTests.cs index 233f9260..a2a045c6 100644 --- a/test/AgileConfig.Server.ServiceTests/PostgreSQL/ConfigServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/PostgreSQL/ConfigServiceTests.cs @@ -1,41 +1,40 @@ -using AgileConfig.Server.ServiceTests.sqlite; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.ServiceTests.sqlite; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Testcontainers.PostgreSql; -namespace AgileConfig.Server.ServiceTests.PostgreSQL +namespace AgileConfig.Server.ServiceTests.PostgreSQL; + +public class ConfigServiceTests_pg : ConfigServiceTests { - public class ConfigServiceTests_pg: ConfigServiceTests + private static readonly PostgreSqlContainer _container = new PostgreSqlBuilder().WithImage("postgres:15.1").Build(); + + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) + { + await _container.StartAsync(); + Console.WriteLine("PostgreSqlContainer started"); + } + + [ClassCleanup] + public static async Task ClassCleanup() + { + await _container.DisposeAsync(); + Console.WriteLine("PostgreSqlContainer dispose"); + } + + public override Task> GetConfigurationData() { - static PostgreSqlContainer _container = new PostgreSqlBuilder().WithImage("postgres:15.1").Build(); - - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - await _container.StartAsync(); - Console.WriteLine($"PostgreSqlContainer started"); - } - - [ClassCleanup] - public static async Task ClassCleanup() - { - await _container.DisposeAsync(); - Console.WriteLine($"PostgreSqlContainer dispose"); - } - public override Task> GetConfigurationData() - { - var connstr = _container.GetConnectionString(); - Console.WriteLine($"PostgreSqlContainer connstr: {connstr}"); - - var dict = new Dictionary(); - dict["db:provider"] = "pg"; - dict["db:conn"] = connstr; - - - return Task.FromResult(dict); - } + var connstr = _container.GetConnectionString(); + Console.WriteLine($"PostgreSqlContainer connstr: {connstr}"); + + var dict = new Dictionary(); + dict["db:provider"] = "pg"; + dict["db:conn"] = connstr; + + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/PostgreSQL/ServerNodeServiceTests.cs b/test/AgileConfig.Server.ServiceTests/PostgreSQL/ServerNodeServiceTests.cs index 6ef2e2dd..ca3a6e83 100644 --- a/test/AgileConfig.Server.ServiceTests/PostgreSQL/ServerNodeServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/PostgreSQL/ServerNodeServiceTests.cs @@ -1,42 +1,40 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using AgileConfig.Server.Service; -using System; +using System; using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; using System.Threading.Tasks; +using AgileConfig.Server.ServiceTests.sqlite; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Testcontainers.PostgreSql; -namespace AgileConfig.Server.ServiceTests.PostgreSQL +namespace AgileConfig.Server.ServiceTests.PostgreSQL; + +public class ServerNodeServiceTests_pg : ServerNodeServiceTests { - public class ServerNodeServiceTests_pg: ServerNodeServiceTests + private static readonly PostgreSqlContainer _container = new PostgreSqlBuilder().WithImage("postgres:15.1").Build(); + + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) + { + await _container.StartAsync(); + Console.WriteLine("PostgreSqlContainer started"); + } + + [ClassCleanup] + public static async Task ClassCleanup() + { + await _container.DisposeAsync(); + Console.WriteLine("PostgreSqlContainer dispose"); + } + + public override Task> GetConfigurationData() { - static PostgreSqlContainer _container = new PostgreSqlBuilder().WithImage("postgres:15.1").Build(); - - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - await _container.StartAsync(); - Console.WriteLine($"PostgreSqlContainer started"); - } - - [ClassCleanup] - public static async Task ClassCleanup() - { - await _container.DisposeAsync(); - Console.WriteLine($"PostgreSqlContainer dispose"); - } - public override Task> GetConfigurationData() - { - var connstr = _container.GetConnectionString(); - Console.WriteLine($"PostgreSqlContainer connstr: {connstr}"); - - var dict = new Dictionary(); - dict["db:provider"] = "pg"; - dict["db:conn"] = connstr; - - - return Task.FromResult(dict); - } + var connstr = _container.GetConnectionString(); + Console.WriteLine($"PostgreSqlContainer connstr: {connstr}"); + + var dict = new Dictionary(); + dict["db:provider"] = "pg"; + dict["db:conn"] = connstr; + + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/PostgreSQL/SettingServiceTests.cs b/test/AgileConfig.Server.ServiceTests/PostgreSQL/SettingServiceTests.cs index bcafdc4b..95ba4439 100644 --- a/test/AgileConfig.Server.ServiceTests/PostgreSQL/SettingServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/PostgreSQL/SettingServiceTests.cs @@ -1,41 +1,40 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; using System.Threading.Tasks; -using System; +using AgileConfig.Server.ServiceTests.sqlite; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Testcontainers.PostgreSql; -namespace AgileConfig.Server.ServiceTests.PostgreSQL +namespace AgileConfig.Server.ServiceTests.PostgreSQL; + +public class SettingServiceTests_pg : SettingServiceTests { - public class SettingServiceTests_pg : SettingServiceTests + private static readonly PostgreSqlContainer _container = new PostgreSqlBuilder().WithImage("postgres:15.1").Build(); + + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) + { + await _container.StartAsync(); + Console.WriteLine("PostgreSqlContainer started"); + } + + [ClassCleanup] + public static async Task ClassCleanup() + { + await _container.DisposeAsync(); + Console.WriteLine("PostgreSqlContainer dispose"); + } + + public override Task> GetConfigurationData() { - static PostgreSqlContainer _container = new PostgreSqlBuilder().WithImage("postgres:15.1").Build(); - - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - await _container.StartAsync(); - Console.WriteLine($"PostgreSqlContainer started"); - } - - [ClassCleanup] - public static async Task ClassCleanup() - { - await _container.DisposeAsync(); - Console.WriteLine($"PostgreSqlContainer dispose"); - } - public override Task> GetConfigurationData() - { - var connstr = _container.GetConnectionString(); - Console.WriteLine($"PostgreSqlContainer connstr: {connstr}"); - - var dict = new Dictionary(); - dict["db:provider"] = "pg"; - dict["db:conn"] = connstr; - - - return Task.FromResult(dict); - } + var connstr = _container.GetConnectionString(); + Console.WriteLine($"PostgreSqlContainer connstr: {connstr}"); + + var dict = new Dictionary(); + dict["db:provider"] = "pg"; + dict["db:conn"] = connstr; + + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/PostgreSQL/SysLogServiceTests.cs b/test/AgileConfig.Server.ServiceTests/PostgreSQL/SysLogServiceTests.cs index a41c82cb..c07ac016 100644 --- a/test/AgileConfig.Server.ServiceTests/PostgreSQL/SysLogServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/PostgreSQL/SysLogServiceTests.cs @@ -1,42 +1,40 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -using AgileConfig.Server.ServiceTests.sqlite; +using System; using System.Collections.Generic; using System.Threading.Tasks; -using System; +using AgileConfig.Server.ServiceTests.sqlite; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Testcontainers.PostgreSql; -namespace AgileConfig.Server.ServiceTests.PostgreSQL +namespace AgileConfig.Server.ServiceTests.PostgreSQL; + +public class SysLogServiceTests_pg : SysLogServiceTests { - public class SysLogServiceTests_pg: SysLogServiceTests + private static readonly PostgreSqlContainer _container = new PostgreSqlBuilder().WithImage("postgres:15.1").Build(); + + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) { - static PostgreSqlContainer _container = new PostgreSqlBuilder().WithImage("postgres:15.1").Build(); - - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - await _container.StartAsync(); - Console.WriteLine($"PostgreSqlContainer started"); - } - - [ClassCleanup] - public static async Task ClassCleanup() - { - await _container.DisposeAsync(); - Console.WriteLine($"PostgreSqlContainer dispose"); - } - public override Task> GetConfigurationData() - { - var connstr = _container.GetConnectionString(); - Console.WriteLine($"PostgreSqlContainer connstr: {connstr}"); - - var dict = new Dictionary(); - dict["db:provider"] = "pg"; - dict["db:conn"] = connstr; - - - return Task.FromResult(dict); - } + await _container.StartAsync(); + Console.WriteLine("PostgreSqlContainer started"); + } + + [ClassCleanup] + public static async Task ClassCleanup() + { + await _container.DisposeAsync(); + Console.WriteLine("PostgreSqlContainer dispose"); + } + + public override Task> GetConfigurationData() + { + var connstr = _container.GetConnectionString(); + Console.WriteLine($"PostgreSqlContainer connstr: {connstr}"); + + var dict = new Dictionary(); + dict["db:provider"] = "pg"; + dict["db:conn"] = connstr; + + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/mongodb/AppServiceTests.cs b/test/AgileConfig.Server.ServiceTests/mongodb/AppServiceTests.cs index d04212ff..e0f7a8fa 100644 --- a/test/AgileConfig.Server.ServiceTests/mongodb/AppServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/mongodb/AppServiceTests.cs @@ -1,60 +1,53 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; using System.Threading.Tasks; -using System; -using Testcontainers.MongoDb; using AgileConfig.Server.Data.Repository.Mongodb; +using AgileConfig.Server.ServiceTests.sqlite; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Testcontainers.MongoDb; + +namespace AgileConfig.Server.ServiceTests.mongodb; -namespace AgileConfig.Server.ServiceTests.mongodb +[TestClass] +public class AppServiceTests_mongo : AppServiceTests { - [TestClass()] - public class AppServiceTests_mongo : AppServiceTests + private static readonly MongoDbContainer _container = new MongoDbBuilder().WithImage("mongo:6.0").Build(); + + public override void ClearData() + { + var app_repository = new AppRepository(_container.GetConnectionString()); + var apps = app_repository.AllAsync().Result; + foreach (var entity in apps) app_repository.DeleteAsync(entity).Wait(); + + var appref_repository = new AppInheritancedRepository(_container.GetConnectionString()); + var apprefs = appref_repository.AllAsync().Result; + foreach (var entity in apprefs) appref_repository.DeleteAsync(entity).Wait(); + } + + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) { - static MongoDbContainer _container = new MongoDbBuilder().WithImage("mongo:6.0").Build(); - - public override void ClearData() - { - var app_repository = new AppRepository(_container.GetConnectionString()); - var apps = app_repository.AllAsync().Result; - foreach (var entity in apps) - { - app_repository.DeleteAsync(entity).Wait(); - } - - var appref_repository = new AppInheritancedRepository(_container.GetConnectionString()); - var apprefs = appref_repository.AllAsync().Result; - foreach (var entity in apprefs) - { - appref_repository.DeleteAsync(entity).Wait(); - } - } - - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - await _container.StartAsync(); - Console.WriteLine($"MongoDbContainer started"); - } - - [ClassCleanup] - public static async Task ClassCleanup() - { - await _container.DisposeAsync(); - Console.WriteLine($"MongoDbContainer dispose"); - } - - - public override Task> GetConfigurationData() - { - var connstr = _container.GetConnectionString(); - Console.WriteLine($"MongoDbContainer connstr: {connstr}"); - - var dict = new Dictionary(); - dict["db:provider"] = "mongodb"; - dict["db:conn"] = connstr; - - return Task.FromResult(dict); - } + await _container.StartAsync(); + Console.WriteLine("MongoDbContainer started"); + } + + [ClassCleanup] + public static async Task ClassCleanup() + { + await _container.DisposeAsync(); + Console.WriteLine("MongoDbContainer dispose"); + } + + + public override Task> GetConfigurationData() + { + var connstr = _container.GetConnectionString(); + Console.WriteLine($"MongoDbContainer connstr: {connstr}"); + + var dict = new Dictionary(); + dict["db:provider"] = "mongodb"; + dict["db:conn"] = connstr; + + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/mongodb/Basic.cs b/test/AgileConfig.Server.ServiceTests/mongodb/Basic.cs index 544d7263..57b45d20 100644 --- a/test/AgileConfig.Server.ServiceTests/mongodb/Basic.cs +++ b/test/AgileConfig.Server.ServiceTests/mongodb/Basic.cs @@ -3,7 +3,6 @@ using AgileConfig.Server.Data.Abstraction; using AgileConfig.Server.Data.Mongodb; using AgileConfig.Server.Data.Repository.Mongodb; -using AgileConfig.Server.IService; using AgileConfig.Server.Service; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; @@ -14,7 +13,7 @@ namespace AgileConfig.Server.ServiceTests.mongodb; public abstract class Basic { - private static readonly Dictionary ConfigurationData = new Dictionary + private static readonly Dictionary ConfigurationData = new() { { "db:provider", @@ -25,20 +24,6 @@ public abstract class Basic "mongodb://localhost:27017;localhost:37017/AgileConfig" } }; - private static void InitServices(IServiceCollection sc, string connectionString) - { - sc.AddTransient>(_ => _ => new MongodbUow()); - - sc.AddScoped>( - _ => _ => new ConfigPublishedRepository(connectionString)); - - sc.AddScoped>(_ => _ => new ConfigRepository(connectionString)); - - sc.AddScoped>(_ => _ => new PublishDetailRepository(connectionString)); - - sc.AddScoped>( - _ => _ => new PublishTimelineRepository(connectionString)); - } private readonly ServiceProvider _serviceProvider; @@ -49,7 +34,7 @@ protected Basic() .Build(); var cache = new Mock(); IServiceCollection services = new ServiceCollection(); - services.AddScoped(_ => cache.Object); + services.AddScoped(_ => cache.Object); services.AddSingleton(config); InitServices(services, ConfigurationData["db:conn"]); services.AddMongodbRepository(); @@ -58,18 +43,33 @@ protected Basic() Console.WriteLine("TestInitialize"); } - protected T GetService() - { - return _serviceProvider.GetService(); - } - protected IConfigRepository ConfigRepository => _serviceProvider.GetService(); - + protected IAppRepository AppRepository => _serviceProvider.GetService(); protected IAppInheritancedRepository AppInheritancedRepository => _serviceProvider.GetService(); - + protected ISysLogRepository SysLogRepository => _serviceProvider.GetService(); + + private static void InitServices(IServiceCollection sc, string connectionString) + { + sc.AddTransient>(_ => _ => new MongodbUow()); + + sc.AddScoped>(_ => + _ => new ConfigPublishedRepository(connectionString)); + + sc.AddScoped>(_ => _ => new ConfigRepository(connectionString)); + + sc.AddScoped>(_ => _ => new PublishDetailRepository(connectionString)); + + sc.AddScoped>(_ => + _ => new PublishTimelineRepository(connectionString)); + } + + protected T GetService() + { + return _serviceProvider.GetService(); + } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/mongodb/ConfigServiceTests.cs b/test/AgileConfig.Server.ServiceTests/mongodb/ConfigServiceTests.cs index e40667fd..b9fd9273 100644 --- a/test/AgileConfig.Server.ServiceTests/mongodb/ConfigServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/mongodb/ConfigServiceTests.cs @@ -1,85 +1,69 @@ -using AgileConfig.Server.Data.Repository.Mongodb; -using AgileConfig.Server.ServiceTests.sqlite; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.Data.Repository.Mongodb; +using AgileConfig.Server.ServiceTests.sqlite; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Testcontainers.MongoDb; -namespace AgileConfig.Server.ServiceTests.mongodb +namespace AgileConfig.Server.ServiceTests.mongodb; + +[TestClass] +public class ConfigServiceTests_mongo : ConfigServiceTests { - [TestClass()] - public class ConfigServiceTests_mongo : ConfigServiceTests - { - public override void ClearData() - { - var repository = new ConfigRepository(GetConn()); - var entities = repository.AllAsync().Result; - foreach (var entity in entities) - { - repository.DeleteAsync(entity).Wait(); - } + private static readonly MongoDbContainer _container = new MongoDbBuilder().WithImage("mongo:6.0").Build(); - var configPublishedRepository = new ConfigPublishedRepository(GetConn()); - var configPublisheds = configPublishedRepository.AllAsync().Result; - foreach (var entity in configPublisheds) - { - configPublishedRepository.DeleteAsync(entity).Wait(); - } + public override void ClearData() + { + var repository = new ConfigRepository(GetConn()); + var entities = repository.AllAsync().Result; + foreach (var entity in entities) repository.DeleteAsync(entity).Wait(); - var detailRepository = new PublishDetailRepository(GetConn()); - var details = detailRepository.AllAsync().Result; - foreach (var entity in details) - { - detailRepository.DeleteAsync(entity).Wait(); - } + var configPublishedRepository = new ConfigPublishedRepository(GetConn()); + var configPublisheds = configPublishedRepository.AllAsync().Result; + foreach (var entity in configPublisheds) configPublishedRepository.DeleteAsync(entity).Wait(); - var app_repository = new AppRepository(GetConn()); - var apps = app_repository.AllAsync().Result; - foreach (var entity in apps) - { - app_repository.DeleteAsync(entity).Wait(); - } + var detailRepository = new PublishDetailRepository(GetConn()); + var details = detailRepository.AllAsync().Result; + foreach (var entity in details) detailRepository.DeleteAsync(entity).Wait(); - var appref_repository = new AppInheritancedRepository(GetConn()); - var apprefs = appref_repository.AllAsync().Result; - foreach (var entity in apprefs) - { - appref_repository.DeleteAsync(entity).Wait(); - } - } + var app_repository = new AppRepository(GetConn()); + var apps = app_repository.AllAsync().Result; + foreach (var entity in apps) app_repository.DeleteAsync(entity).Wait(); - static MongoDbContainer _container = new MongoDbBuilder().WithImage("mongo:6.0").Build(); + var appref_repository = new AppInheritancedRepository(GetConn()); + var apprefs = appref_repository.AllAsync().Result; + foreach (var entity in apprefs) appref_repository.DeleteAsync(entity).Wait(); + } - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - await _container.StartAsync(); - Console.WriteLine($"MongoDbContainer started"); - } + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) + { + await _container.StartAsync(); + Console.WriteLine("MongoDbContainer started"); + } - [ClassCleanup] - public static async Task ClassCleanup() - { - await _container.DisposeAsync(); - Console.WriteLine($"MongoDbContainer dispose"); - } + [ClassCleanup] + public static async Task ClassCleanup() + { + await _container.DisposeAsync(); + Console.WriteLine("MongoDbContainer dispose"); + } - private string GetConn() - { - return _container.GetConnectionString(); - } + private string GetConn() + { + return _container.GetConnectionString(); + } - public override Task> GetConfigurationData() - { - var connstr = GetConn(); - Console.WriteLine($"MongoDbContainer connstr: {connstr}"); + public override Task> GetConfigurationData() + { + var connstr = GetConn(); + Console.WriteLine($"MongoDbContainer connstr: {connstr}"); - var dict = new Dictionary(); - dict["db:provider"] = "mongodb"; - dict["db:conn"] = connstr; + var dict = new Dictionary(); + dict["db:provider"] = "mongodb"; + dict["db:conn"] = connstr; - return Task.FromResult(dict); - } + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/mongodb/ServerNodeServiceTests.cs b/test/AgileConfig.Server.ServiceTests/mongodb/ServerNodeServiceTests.cs index 7d71a838..ee2f3d3a 100644 --- a/test/AgileConfig.Server.ServiceTests/mongodb/ServerNodeServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/mongodb/ServerNodeServiceTests.cs @@ -1,53 +1,49 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using System; using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; -using AgileConfig.Server.Data.Repository.Mongodb; using System.Threading.Tasks; +using AgileConfig.Server.Data.Repository.Mongodb; +using AgileConfig.Server.ServiceTests.sqlite; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Testcontainers.MongoDb; -namespace AgileConfig.Server.ServiceTests.mongodb +namespace AgileConfig.Server.ServiceTests.mongodb; + +[TestClass] +public class ServerNodeServiceTests_mongo : ServerNodeServiceTests { - [TestClass()] - public class ServerNodeServiceTests_mongo: ServerNodeServiceTests + private static readonly MongoDbContainer _container = new MongoDbBuilder().WithImage("mongo:6.0").Build(); + + public override void ClearData() + { + var repository = new ServerNodeRepository(_container.GetConnectionString()); + var entities = repository.AllAsync().Result; + foreach (var log in entities) repository.DeleteAsync(log).Wait(); + } + + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) { - public override void ClearData() - { - var repository = new ServerNodeRepository(_container.GetConnectionString()); - var entities = repository.AllAsync().Result; - foreach (var log in entities) - { - repository.DeleteAsync(log).Wait(); - } - } - - static MongoDbContainer _container = new MongoDbBuilder().WithImage("mongo:6.0").Build(); - - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - await _container.StartAsync(); - Console.WriteLine($"MongoDbContainer started"); - } - - [ClassCleanup] - public static async Task ClassCleanup() - { - await _container.DisposeAsync(); - Console.WriteLine($"MongoDbContainer dispose"); - } - - - public override Task> GetConfigurationData() - { - var connstr = _container.GetConnectionString(); - Console.WriteLine($"MongoDbContainer connstr: {connstr}"); - - var dict = new Dictionary(); - dict["db:provider"] = "mongodb"; - dict["db:conn"] = connstr; - - return Task.FromResult(dict); - } + await _container.StartAsync(); + Console.WriteLine("MongoDbContainer started"); + } + + [ClassCleanup] + public static async Task ClassCleanup() + { + await _container.DisposeAsync(); + Console.WriteLine("MongoDbContainer dispose"); + } + + + public override Task> GetConfigurationData() + { + var connstr = _container.GetConnectionString(); + Console.WriteLine($"MongoDbContainer connstr: {connstr}"); + + var dict = new Dictionary(); + dict["db:provider"] = "mongodb"; + dict["db:conn"] = connstr; + + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/mongodb/SettingServiceTests.cs b/test/AgileConfig.Server.ServiceTests/mongodb/SettingServiceTests.cs index b022f4a7..23c8c3a3 100644 --- a/test/AgileConfig.Server.ServiceTests/mongodb/SettingServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/mongodb/SettingServiceTests.cs @@ -1,46 +1,45 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; using System.Threading.Tasks; -using System; +using AgileConfig.Server.ServiceTests.sqlite; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Testcontainers.MongoDb; -namespace AgileConfig.Server.ServiceTests.mongodb +namespace AgileConfig.Server.ServiceTests.mongodb; + +[TestClass] +public class SettingServiceTests_mongo : SettingServiceTests { - [TestClass()] - public class SettingServiceTests_mongo : SettingServiceTests + private static readonly MongoDbContainer _container = new MongoDbBuilder().WithImage("mongo:6.0").Build(); + + public override void ClearData() + { + } + + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) + { + await _container.StartAsync(); + Console.WriteLine("MongoDbContainer started"); + } + + [ClassCleanup] + public static async Task ClassCleanup() + { + await _container.DisposeAsync(); + Console.WriteLine("MongoDbContainer dispose"); + } + + + public override Task> GetConfigurationData() { - public override void ClearData() - { - } - - static MongoDbContainer _container = new MongoDbBuilder().WithImage("mongo:6.0").Build(); - - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - await _container.StartAsync(); - Console.WriteLine($"MongoDbContainer started"); - } - - [ClassCleanup] - public static async Task ClassCleanup() - { - await _container.DisposeAsync(); - Console.WriteLine($"MongoDbContainer dispose"); - } - - - public override Task> GetConfigurationData() - { - var connstr = _container.GetConnectionString(); - Console.WriteLine($"MongoDbContainer connstr: {connstr}"); - - var dict = new Dictionary(); - dict["db:provider"] = "mongodb"; - dict["db:conn"] = connstr; - - return Task.FromResult(dict); - } + var connstr = _container.GetConnectionString(); + Console.WriteLine($"MongoDbContainer connstr: {connstr}"); + + var dict = new Dictionary(); + dict["db:provider"] = "mongodb"; + dict["db:conn"] = connstr; + + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/mongodb/SysLogServiceTests.cs b/test/AgileConfig.Server.ServiceTests/mongodb/SysLogServiceTests.cs index 1a19cb0b..fd1b3191 100644 --- a/test/AgileConfig.Server.ServiceTests/mongodb/SysLogServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/mongodb/SysLogServiceTests.cs @@ -8,33 +8,30 @@ namespace AgileConfig.Server.ServiceTests.mongodb; -[TestClass()] +[TestClass] public class SysLogServiceTests_mongo : SysLogServiceTests { + private static readonly MongoDbContainer _container = new MongoDbBuilder().WithImage("mongo:6.0").Build(); + public override void ClearData() { var repository = new SysLogRepository(_container.GetConnectionString()); - var syslogs = repository.AllAsync().Result; - foreach (var log in syslogs) - { - repository.DeleteAsync(log).Wait(); - } + var syslogs = repository.AllAsync().Result; + foreach (var log in syslogs) repository.DeleteAsync(log).Wait(); } - static MongoDbContainer _container = new MongoDbBuilder().WithImage("mongo:6.0").Build(); - [ClassInitialize] public static async Task ClassInit(TestContext testContext) { await _container.StartAsync(); - Console.WriteLine($"MongoDbContainer started"); + Console.WriteLine("MongoDbContainer started"); } [ClassCleanup] public static async Task ClassCleanup() { await _container.DisposeAsync(); - Console.WriteLine($"MongoDbContainer dispose"); + Console.WriteLine("MongoDbContainer dispose"); } diff --git a/test/AgileConfig.Server.ServiceTests/mysql/AppServiceTests.cs b/test/AgileConfig.Server.ServiceTests/mysql/AppServiceTests.cs index b1de2bed..5d2c55af 100644 --- a/test/AgileConfig.Server.ServiceTests/mysql/AppServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/mysql/AppServiceTests.cs @@ -1,43 +1,41 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using AgileConfig.Server.ServiceTests.sqlite; +using System; using System.Collections.Generic; -using Testcontainers.MySql; using System.Threading.Tasks; -using System; +using AgileConfig.Server.ServiceTests.sqlite; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Testcontainers.MySql; -namespace AgileConfig.Server.ServiceTests.mysql -{ - [TestClass] - public class AppServiceTests_mysql : AppServiceTests - { - static MySqlContainer _container = new MySqlBuilder().WithImage("mysql:8.0").Build(); +namespace AgileConfig.Server.ServiceTests.mysql; - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - await _container.StartAsync(); - Console.WriteLine($"MySqlContainer started"); - } +[TestClass] +public class AppServiceTests_mysql : AppServiceTests +{ + private static readonly MySqlContainer _container = new MySqlBuilder().WithImage("mysql:8.0").Build(); - [ClassCleanup] - public static async Task ClassCleanup() - { - await _container.DisposeAsync(); - Console.WriteLine($"MySqlContainer dispose"); - } + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) + { + await _container.StartAsync(); + Console.WriteLine("MySqlContainer started"); + } + [ClassCleanup] + public static async Task ClassCleanup() + { + await _container.DisposeAsync(); + Console.WriteLine("MySqlContainer dispose"); + } - public override Task> GetConfigurationData() - { - var connstr = _container.GetConnectionString(); - Console.WriteLine($"MySqlContainer connstr: {connstr}"); - var dict = new Dictionary(); - dict["db:provider"] = "mysql"; - dict["db:conn"] = connstr; + public override Task> GetConfigurationData() + { + var connstr = _container.GetConnectionString(); + Console.WriteLine($"MySqlContainer connstr: {connstr}"); - return Task.FromResult(dict); - } + var dict = new Dictionary(); + dict["db:provider"] = "mysql"; + dict["db:conn"] = connstr; + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/mysql/ConfigServiceTests.cs b/test/AgileConfig.Server.ServiceTests/mysql/ConfigServiceTests.cs index 82353bb5..3432f177 100644 --- a/test/AgileConfig.Server.ServiceTests/mysql/ConfigServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/mysql/ConfigServiceTests.cs @@ -1,42 +1,41 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; using System.Threading.Tasks; -using System; +using AgileConfig.Server.ServiceTests.sqlite; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Testcontainers.MySql; -namespace AgileConfig.Server.ServiceTests.mysql +namespace AgileConfig.Server.ServiceTests.mysql; + +[TestClass] +public class ConfigServiceTests_mysql : ConfigServiceTests { - [TestClass] - public class ConfigServiceTests_mysql : ConfigServiceTests + private static readonly MySqlContainer _container = new MySqlBuilder().WithImage("mysql:8.0").Build(); + + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) + { + await _container.StartAsync(); + Console.WriteLine("MySqlContainer started"); + } + + [ClassCleanup] + public static async Task ClassCleanup() { - static MySqlContainer _container = new MySqlBuilder().WithImage("mysql:8.0").Build(); - - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - await _container.StartAsync(); - Console.WriteLine($"MySqlContainer started"); - } - - [ClassCleanup] - public static async Task ClassCleanup() - { - await _container.DisposeAsync(); - Console.WriteLine($"MySqlContainer dispose"); - } - - - public override Task> GetConfigurationData() - { - var connstr = _container.GetConnectionString(); - Console.WriteLine($"MySqlContainer connstr: {connstr}"); - - var dict = new Dictionary(); - dict["db:provider"] = "mysql"; - dict["db:conn"] = connstr; - - return Task.FromResult(dict); - } + await _container.DisposeAsync(); + Console.WriteLine("MySqlContainer dispose"); + } + + + public override Task> GetConfigurationData() + { + var connstr = _container.GetConnectionString(); + Console.WriteLine($"MySqlContainer connstr: {connstr}"); + + var dict = new Dictionary(); + dict["db:provider"] = "mysql"; + dict["db:conn"] = connstr; + + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/mysql/ServerNodeServiceTests.cs b/test/AgileConfig.Server.ServiceTests/mysql/ServerNodeServiceTests.cs index 02fabd22..09fcb4d4 100644 --- a/test/AgileConfig.Server.ServiceTests/mysql/ServerNodeServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/mysql/ServerNodeServiceTests.cs @@ -1,52 +1,51 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using System; using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; using System.Threading.Tasks; -using Testcontainers.MySql; using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.ServiceTests.sqlite; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Testcontainers.MySql; -namespace AgileConfig.Server.ServiceTests.mysql +namespace AgileConfig.Server.ServiceTests.mysql; + +[TestClass] +public class ServerNodeServiceTests_mysql : ServerNodeServiceTests { - [TestClass] - public class ServerNodeServiceTests_mysql : ServerNodeServiceTests - { - static MySqlContainer _container = new MySqlBuilder().WithImage("mysql:8.0").Build(); + private static readonly MySqlContainer _container = new MySqlBuilder().WithImage("mysql:8.0").Build(); - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - await _container.StartAsync(); - Console.WriteLine($"MySqlContainer started"); - } + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) + { + await _container.StartAsync(); + Console.WriteLine("MySqlContainer started"); + } - [ClassCleanup] - public static async Task ClassCleanup() - { - await _container.DisposeAsync(); - Console.WriteLine($"MySqlContainer dispose"); - } + [ClassCleanup] + public static async Task ClassCleanup() + { + await _container.DisposeAsync(); + Console.WriteLine("MySqlContainer dispose"); + } - public override void ClearData() - { - var sql = this.GetFreeSql(); + public override void ClearData() + { + var sql = GetFreeSql(); - sql.Delete().Where("1=1").ExecuteAffrows(); + sql.Delete().Where("1=1").ExecuteAffrows(); - sql.Dispose(); - } + sql.Dispose(); + } - public override Task> GetConfigurationData() - { - var connstr = _container.GetConnectionString(); - Console.WriteLine($"MySqlContainer connstr: {connstr}"); + public override Task> GetConfigurationData() + { + var connstr = _container.GetConnectionString(); + Console.WriteLine($"MySqlContainer connstr: {connstr}"); - var dict = new Dictionary(); - dict["db:provider"] = "mysql"; - dict["db:conn"] = connstr; + var dict = new Dictionary(); + dict["db:provider"] = "mysql"; + dict["db:conn"] = connstr; - return Task.FromResult(dict); - } + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/mysql/SettingServiceTests.cs b/test/AgileConfig.Server.ServiceTests/mysql/SettingServiceTests.cs index 6de4f79a..1460388c 100644 --- a/test/AgileConfig.Server.ServiceTests/mysql/SettingServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/mysql/SettingServiceTests.cs @@ -1,42 +1,41 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; using System.Threading.Tasks; -using System; +using AgileConfig.Server.ServiceTests.sqlite; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Testcontainers.MySql; -namespace AgileConfig.Server.ServiceTests.mysql +namespace AgileConfig.Server.ServiceTests.mysql; + +[TestClass] +public class SettingServiceTests_mysql : SettingServiceTests { - [TestClass] - public class SettingServiceTests_mysql : SettingServiceTests + private static readonly MySqlContainer _container = new MySqlBuilder().WithImage("mysql:8.0").Build(); + + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) + { + await _container.StartAsync(); + Console.WriteLine("MySqlContainer started"); + } + + [ClassCleanup] + public static async Task ClassCleanup() { - static MySqlContainer _container = new MySqlBuilder().WithImage("mysql:8.0").Build(); - - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - await _container.StartAsync(); - Console.WriteLine($"MySqlContainer started"); - } - - [ClassCleanup] - public static async Task ClassCleanup() - { - await _container.DisposeAsync(); - Console.WriteLine($"MySqlContainer dispose"); - } - - - public override Task> GetConfigurationData() - { - var connstr = _container.GetConnectionString(); - Console.WriteLine($"MySqlContainer connstr: {connstr}"); - - var dict = new Dictionary(); - dict["db:provider"] = "mysql"; - dict["db:conn"] = connstr; - - return Task.FromResult(dict); - } + await _container.DisposeAsync(); + Console.WriteLine("MySqlContainer dispose"); + } + + + public override Task> GetConfigurationData() + { + var connstr = _container.GetConnectionString(); + Console.WriteLine($"MySqlContainer connstr: {connstr}"); + + var dict = new Dictionary(); + dict["db:provider"] = "mysql"; + dict["db:conn"] = connstr; + + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/mysql/SysLogServiceTests.cs b/test/AgileConfig.Server.ServiceTests/mysql/SysLogServiceTests.cs index d4ecd59d..5b8b2428 100644 --- a/test/AgileConfig.Server.ServiceTests/mysql/SysLogServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/mysql/SysLogServiceTests.cs @@ -1,52 +1,51 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using AgileConfig.Server.ServiceTests.sqlite; +using System; using System.Collections.Generic; using System.Threading.Tasks; -using System; -using Testcontainers.MySql; using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.ServiceTests.sqlite; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Testcontainers.MySql; -namespace AgileConfig.Server.ServiceTests.mysql +namespace AgileConfig.Server.ServiceTests.mysql; + +[TestClass] +public class SysLogServiceTests_mysql : SysLogServiceTests { - [TestClass] - public class SysLogServiceTests_mysql : SysLogServiceTests - { - static MySqlContainer _container = new MySqlBuilder().WithImage("mysql:8.0").Build(); + private static readonly MySqlContainer _container = new MySqlBuilder().WithImage("mysql:8.0").Build(); - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - await _container.StartAsync(); - Console.WriteLine($"MySqlContainer started"); - } + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) + { + await _container.StartAsync(); + Console.WriteLine("MySqlContainer started"); + } - [ClassCleanup] - public static async Task ClassCleanup() - { - await _container.DisposeAsync(); - Console.WriteLine($"MySqlContainer dispose"); - } + [ClassCleanup] + public static async Task ClassCleanup() + { + await _container.DisposeAsync(); + Console.WriteLine("MySqlContainer dispose"); + } - public override void ClearData() - { - var sql = this.GetFreeSql(); + public override void ClearData() + { + var sql = GetFreeSql(); - sql.Delete().Where("1=1").ExecuteAffrows(); + sql.Delete().Where("1=1").ExecuteAffrows(); - sql.Dispose(); - } + sql.Dispose(); + } - public override Task> GetConfigurationData() - { - var connstr = _container.GetConnectionString(); - Console.WriteLine($"MySqlContainer connstr: {connstr}"); + public override Task> GetConfigurationData() + { + var connstr = _container.GetConnectionString(); + Console.WriteLine($"MySqlContainer connstr: {connstr}"); - var dict = new Dictionary(); - dict["db:provider"] = "mysql"; - dict["db:conn"] = connstr; + var dict = new Dictionary(); + dict["db:provider"] = "mysql"; + dict["db:conn"] = connstr; - return Task.FromResult(dict); - } + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/oracle/AppServiceTests.cs b/test/AgileConfig.Server.ServiceTests/oracle/AppServiceTests.cs index e0a17a73..01d12c49 100644 --- a/test/AgileConfig.Server.ServiceTests/oracle/AppServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/oracle/AppServiceTests.cs @@ -1,24 +1,21 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; +using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.ServiceTests.sqlite; + +namespace AgileConfig.Server.ServiceTests.oracle; -namespace AgileConfig.Server.ServiceTests.oracle +public class AppServiceTests_oracle : AppServiceTests { - public class AppServiceTests_oracle : AppServiceTests - { - string conn = "user id=x;password=x;data source=192.168.0.123/orcl"; + private readonly string conn = "user id=x;password=x;data source=192.168.0.123/orcl"; - public override Task> GetConfigurationData() - { - var dict = new Dictionary(); - dict["db:provider"] = "oracle"; - dict["db:conn"] = conn; + public override Task> GetConfigurationData() + { + var dict = new Dictionary(); + dict["db:provider"] = "oracle"; + dict["db:conn"] = conn; - return Task.FromResult(dict); - } + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/oracle/ConfigServiceTests.cs b/test/AgileConfig.Server.ServiceTests/oracle/ConfigServiceTests.cs index 455e90fb..8bcffba6 100644 --- a/test/AgileConfig.Server.ServiceTests/oracle/ConfigServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/oracle/ConfigServiceTests.cs @@ -1,23 +1,21 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; +using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.ServiceTests.sqlite; + +namespace AgileConfig.Server.ServiceTests.oracle; -namespace AgileConfig.Server.ServiceTests.oracle +public class ConfigServiceTests_oracle : ConfigServiceTests { - public class ConfigServiceTests_oracle : ConfigServiceTests - { - string conn = "user id=x;password=x;data source=192.168.0.123/orcl"; + private readonly string conn = "user id=x;password=x;data source=192.168.0.123/orcl"; - public override Task> GetConfigurationData() - { - return - Task.FromResult( + public override Task> GetConfigurationData() + { + return + Task.FromResult( new Dictionary { - {"db:provider","oracle" }, - {"db:conn",conn } - }); - } + { "db:provider", "oracle" }, + { "db:conn", conn } + }); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/oracle/ServerNodeServiceTests.cs b/test/AgileConfig.Server.ServiceTests/oracle/ServerNodeServiceTests.cs index 70817f58..0d9a91ec 100644 --- a/test/AgileConfig.Server.ServiceTests/oracle/ServerNodeServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/oracle/ServerNodeServiceTests.cs @@ -1,24 +1,21 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; +using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.ServiceTests.sqlite; + +namespace AgileConfig.Server.ServiceTests.oracle; -namespace AgileConfig.Server.ServiceTests.oracle +public class ServerNodeServiceTests_oracle : ServerNodeServiceTests { - public class ServerNodeServiceTests_oracle: ServerNodeServiceTests - { - string conn = "user id=x;password=x;data source=192.168.0.123/orcl"; + private readonly string conn = "user id=x;password=x;data source=192.168.0.123/orcl"; - public override Task> GetConfigurationData() - { - return - Task.FromResult( + public override Task> GetConfigurationData() + { + return + Task.FromResult( new Dictionary { - {"db:provider","oracle" }, - {"db:conn",conn } - }); - } - + { "db:provider", "oracle" }, + { "db:conn", conn } + }); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/oracle/SettingServiceTests.cs b/test/AgileConfig.Server.ServiceTests/oracle/SettingServiceTests.cs index 44111b7e..9c3fade0 100644 --- a/test/AgileConfig.Server.ServiceTests/oracle/SettingServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/oracle/SettingServiceTests.cs @@ -1,23 +1,21 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; +using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.ServiceTests.sqlite; + +namespace AgileConfig.Server.ServiceTests.oracle; -namespace AgileConfig.Server.ServiceTests.oracle +public class SettingServiceTests_oracle : SettingServiceTests { - public class SettingServiceTests_oracle : SettingServiceTests - { - string conn = "user id=x;password=x;data source=192.168.0.123/orcl"; + private readonly string conn = "user id=x;password=x;data source=192.168.0.123/orcl"; - public override Task> GetConfigurationData() - { - return - Task.FromResult( + public override Task> GetConfigurationData() + { + return + Task.FromResult( new Dictionary { - {"db:provider","oracle" }, - {"db:conn",conn } - }); - } + { "db:provider", "oracle" }, + { "db:conn", conn } + }); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/oracle/SysLogServiceTests.cs b/test/AgileConfig.Server.ServiceTests/oracle/SysLogServiceTests.cs index a0ff6542..abc08cb0 100644 --- a/test/AgileConfig.Server.ServiceTests/oracle/SysLogServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/oracle/SysLogServiceTests.cs @@ -1,24 +1,21 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; +using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.ServiceTests.sqlite; -namespace AgileConfig.Server.ServiceTests.oracle -{ - public class SysLogServiceTests_oracle : SysLogServiceTests - { +namespace AgileConfig.Server.ServiceTests.oracle; - string conn = "user id=x;password=x;data source=192.168.0.123/orcl"; +public class SysLogServiceTests_oracle : SysLogServiceTests +{ + private readonly string conn = "user id=x;password=x;data source=192.168.0.123/orcl"; - public override Task> GetConfigurationData() - { - return - Task.FromResult( + public override Task> GetConfigurationData() + { + return + Task.FromResult( new Dictionary { - {"db:provider","oracle" }, - {"db:conn",conn } - }); - } + { "db:provider", "oracle" }, + { "db:conn", conn } + }); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/sqlite/AppServiceTests.cs b/test/AgileConfig.Server.ServiceTests/sqlite/AppServiceTests.cs index 33ed6bff..4e09271f 100644 --- a/test/AgileConfig.Server.ServiceTests/sqlite/AppServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/sqlite/AppServiceTests.cs @@ -1,417 +1,411 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using System; using System.Collections.Generic; -using AgileConfig.Server.Data.Entity; using System.Threading.Tasks; +using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Data.Freesql; +using AgileConfig.Server.Data.Repository.Selector; using AgileConfig.Server.IService; using AgileConfig.Server.Service; -using Microsoft.Extensions.DependencyInjection; -using AgileConfig.Server.Data.Abstraction; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; -using AgileConfig.Server.Data.Freesql; -using AgileConfig.Server.Data.Repository.Selector; -namespace AgileConfig.Server.ServiceTests.sqlite +namespace AgileConfig.Server.ServiceTests.sqlite; + +[TestClass] +public class AppServiceTests : BasicTestService { - [TestClass()] - public class AppServiceTests : BasicTestService + private IAppService _appservice; + private IServiceProvider _serviceProvider; + private IServiceScope _serviceScope; + + public override Task> GetConfigurationData() { - IServiceProvider _serviceProvider = null; - IServiceScope _serviceScope = null; - IAppService _appservice = null; - public override Task> GetConfigurationData() - { - return - Task.FromResult(new Dictionary - { - {"db:provider","sqlite" }, - {"db:conn","Data Source=agile_config.db" } + return + Task.FromResult(new Dictionary + { + { "db:provider", "sqlite" }, + { "db:conn", "Data Source=agile_config.db" } }); - } + } - public virtual async Task NewGlobalSp() - { - Console.WriteLine("Try get configration data"); - var dict = await GetConfigurationData(); + public virtual async Task NewGlobalSp() + { + Console.WriteLine("Try get configration data"); + var dict = await GetConfigurationData(); + + foreach (var item in dict) Console.WriteLine($"key: {item.Key} value: {item.Value}"); + + var config = new ConfigurationBuilder() + .AddInMemoryCollection(dict) + .Build(); + Console.WriteLine("Config list"); + foreach (var item in config.AsEnumerable()) Console.WriteLine($"key: {item.Key} value: {item.Value}"); + + var cache = new Mock(); + IServiceCollection services = new ServiceCollection(); + services.AddLogging(); + services.AddScoped(_ => cache.Object); + services.AddSingleton(config); + services.AddDbConfigInfoFactory(); + services.AddFreeSqlFactory(); + services.AddRepositories(); + services.AddBusinessServices(); + + return services.BuildServiceProvider(); + } - foreach (var item in dict) - { - Console.WriteLine($"key: {item.Key} value: {item.Value}"); - } - - var config = new ConfigurationBuilder() - .AddInMemoryCollection(dict) - .Build(); - Console.WriteLine("Config list"); - foreach (var item in config.AsEnumerable()) - { - Console.WriteLine($"key: {item.Key} value: {item.Value}"); - } - - var cache = new Mock(); - IServiceCollection services = new ServiceCollection(); - services.AddLogging(); - services.AddScoped(_ => cache.Object); - services.AddSingleton(config); - services.AddDbConfigInfoFactory(); - services.AddFreeSqlFactory(); - services.AddRepositories(); - services.AddBusinessServices(); - - return services.BuildServiceProvider(); - } - - [TestInitialize] - public async Task TestInitialize() - { - this.GlobalServiceProvider = await NewGlobalSp(); - _serviceScope = this.GlobalServiceProvider.CreateScope(); - _serviceProvider = _serviceScope.ServiceProvider; + [TestInitialize] + public async Task TestInitialize() + { + GlobalServiceProvider = await NewGlobalSp(); + _serviceScope = GlobalServiceProvider.CreateScope(); + _serviceProvider = _serviceScope.ServiceProvider; - ClearData(); + ClearData(); - var systeminitializationService = _serviceProvider.GetService(); - systeminitializationService.TryInitDefaultEnvironment();//初始化环境 DEV TEST STAGE PROD - systeminitializationService.TryInitJwtSecret();//初始化 jwt secret + var systeminitializationService = _serviceProvider.GetService(); + systeminitializationService.TryInitDefaultEnvironment(); //初始化环境 DEV TEST STAGE PROD + systeminitializationService.TryInitJwtSecret(); //初始化 jwt secret - _appservice = _serviceProvider.GetService(); + _appservice = _serviceProvider.GetService(); - Console.WriteLine("Run TestInitialize"); - } + Console.WriteLine("Run TestInitialize"); + } - [TestCleanup] - public void TestCleanup() - { - _appservice.Dispose(); - _serviceScope.Dispose(); - } + [TestCleanup] + public void TestCleanup() + { + _appservice.Dispose(); + _serviceScope.Dispose(); + } - [TestMethod()] - public async Task AddAsyncTest() - { - var id = Guid.NewGuid().ToString(); - var source = new App - { - Id = id, - Name = "xx", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = true - }; - var result = await _appservice.AddAsync(source); - var app = await _appservice.GetAsync(source.Id); - - Assert.IsTrue(result); - Assert.IsNotNull(app); - - Assert.AreEqual(source.Id, app.Id); - Assert.AreEqual(source.Name, app.Name); - Assert.AreEqual(source.Secret, app.Secret); - Assert.AreEqual(source.CreateTime.ToString("yyyyMMddhhmmss"), app.CreateTime.ToString("yyyyMMddhhmmss")); - Assert.AreEqual(source.UpdateTime.Value.ToString("yyyyMMddhhmmss"), app.UpdateTime.Value.ToString("yyyyMMddhhmmss")); - Assert.AreEqual(source.Enabled, app.Enabled); - } - - [TestMethod()] - public async Task DeleteAsyncTest() - { - var id = Guid.NewGuid().ToString(); - var source = new App - { - Id = id, - Name = "xx", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = true - }; - var result = await _appservice.AddAsync(source); - Assert.IsTrue(result); - - var delResult = await _appservice.DeleteAsync(source); - Assert.IsTrue(delResult); - } - - [TestMethod()] - public async Task DeleteAsyncTest1() + [TestMethod] + public async Task AddAsyncTest() + { + var id = Guid.NewGuid().ToString(); + var source = new App { - var id = Guid.NewGuid().ToString(); - var source = new App - { - Id = id, - Name = "xx", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = true - }; - var result = await _appservice.AddAsync(source); - Assert.IsTrue(result); + Id = id, + Name = "xx", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = true + }; + var result = await _appservice.AddAsync(source); + var app = await _appservice.GetAsync(source.Id); + + Assert.IsTrue(result); + Assert.IsNotNull(app); + + Assert.AreEqual(source.Id, app.Id); + Assert.AreEqual(source.Name, app.Name); + Assert.AreEqual(source.Secret, app.Secret); + Assert.AreEqual(source.CreateTime.ToString("yyyyMMddhhmmss"), app.CreateTime.ToString("yyyyMMddhhmmss")); + Assert.AreEqual(source.UpdateTime.Value.ToString("yyyyMMddhhmmss"), + app.UpdateTime.Value.ToString("yyyyMMddhhmmss")); + Assert.AreEqual(source.Enabled, app.Enabled); + } - var delResult = await _appservice.DeleteAsync(id); - Assert.IsTrue(delResult); + [TestMethod] + public async Task DeleteAsyncTest() + { + var id = Guid.NewGuid().ToString(); + var source = new App + { + Id = id, + Name = "xx", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = true + }; + var result = await _appservice.AddAsync(source); + Assert.IsTrue(result); + + var delResult = await _appservice.DeleteAsync(source); + Assert.IsTrue(delResult); + } - var app = await _appservice.GetAsync(source.Id); + [TestMethod] + public async Task DeleteAsyncTest1() + { + var id = Guid.NewGuid().ToString(); + var source = new App + { + Id = id, + Name = "xx", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = true + }; + var result = await _appservice.AddAsync(source); + Assert.IsTrue(result); + + var delResult = await _appservice.DeleteAsync(id); + Assert.IsTrue(delResult); + + var app = await _appservice.GetAsync(source.Id); + + Assert.IsNull(app); + } - Assert.IsNull(app); + [TestMethod] + public async Task GetAsyncTest() + { + var id = Guid.NewGuid().ToString(); + var source = new App + { + Id = id, + Name = "xx", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = true + }; + var result = await _appservice.AddAsync(source); + Assert.IsTrue(result); + + var app = await _appservice.GetAsync(id); + Assert.IsNotNull(app); + + Assert.AreEqual(source.Id, app.Id); + Assert.AreEqual(source.Name, app.Name); + Assert.AreEqual(source.Secret, app.Secret); + Assert.AreEqual(source.CreateTime.ToString("yyyyMMddhhmm"), app.CreateTime.ToString("yyyyMMddhhmm")); + Assert.AreEqual(source.UpdateTime.Value.ToString("yyyyMMddhhmm"), + app.UpdateTime.Value.ToString("yyyyMMddhhmm")); + Assert.AreEqual(source.Enabled, app.Enabled); + } - } + [TestMethod] + public async Task GetAllAppsAsyncTest() + { + ClearData(); - [TestMethod()] - public async Task GetAsyncTest() + var id = Guid.NewGuid().ToString(); + var source = new App { - var id = Guid.NewGuid().ToString(); - var source = new App - { - Id = id, - Name = "xx", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = true - }; - var result = await _appservice.AddAsync(source); - Assert.IsTrue(result); - - var app = await _appservice.GetAsync(id); - Assert.IsNotNull(app); - - Assert.AreEqual(source.Id, app.Id); - Assert.AreEqual(source.Name, app.Name); - Assert.AreEqual(source.Secret, app.Secret); - Assert.AreEqual(source.CreateTime.ToString("yyyyMMddhhmm"), app.CreateTime.ToString("yyyyMMddhhmm")); - Assert.AreEqual(source.UpdateTime.Value.ToString("yyyyMMddhhmm"), app.UpdateTime.Value.ToString("yyyyMMddhhmm")); - Assert.AreEqual(source.Enabled, app.Enabled); - } - - [TestMethod()] - public async Task GetAllAppsAsyncTest() + Id = id, + Name = "xx", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = true + }; + var result = await _appservice.AddAsync(source); + Assert.IsTrue(result); + var id1 = Guid.NewGuid().ToString(); + var source1 = new App { - ClearData(); + Id = id1, + Name = "xx", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = true + }; + var result1 = await _appservice.AddAsync(source1); + Assert.IsTrue(result1); + + var apps = await _appservice.GetAllAppsAsync(); + Assert.IsNotNull(apps); + Assert.AreEqual(2, apps.Count); + } - var id = Guid.NewGuid().ToString(); - var source = new App - { - Id = id, - Name = "xx", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = true - }; - var result = await _appservice.AddAsync(source); - Assert.IsTrue(result); - var id1 = Guid.NewGuid().ToString(); - var source1 = new App - { - Id = id1, - Name = "xx", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = true - }; - var result1 = await _appservice.AddAsync(source1); - Assert.IsTrue(result1); + [TestMethod] + public async Task UpdateAsyncTest() + { + var id = Guid.NewGuid().ToString(); + var source = new App + { + Id = id, + Name = "xx", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = true + }; + var result = await _appservice.AddAsync(source); + Assert.IsTrue(result); + + source.Name = "new name"; + source.Secret = "new sec"; + source.CreateTime = DateTime.Now.AddDays(1); + source.UpdateTime = DateTime.Now.AddDays(1); + source.Enabled = false; + + var result1 = await _appservice.UpdateAsync(source); + Assert.IsTrue(result1); + + var app = await _appservice.GetAsync(source.Id); + + Assert.AreEqual(source.Id, app.Id); + Assert.AreEqual(source.Name, app.Name); + Assert.AreEqual(source.Secret, app.Secret); + Assert.AreEqual(source.CreateTime.ToString("yyyyMMddhhmmss"), app.CreateTime.ToString("yyyyMMddhhmmss")); + Assert.AreEqual(source.UpdateTime.Value.ToString("yyyyMMddhhmmss"), + app.UpdateTime.Value.ToString("yyyyMMddhhmmss")); + Assert.AreEqual(source.Enabled, app.Enabled); + } - var apps = await _appservice.GetAllAppsAsync(); - Assert.IsNotNull(apps); - Assert.AreEqual(2, apps.Count); + [TestMethod] + public async Task CountEnabledAppsAsyncTest() + { + ClearData(); + var id = Guid.NewGuid().ToString(); + var source = new App + { + Id = id, + Name = "xx", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = true + }; + var result = await _appservice.AddAsync(source); + Assert.IsTrue(result); + var id1 = Guid.NewGuid().ToString(); + var source1 = new App + { + Id = id1, + Name = "xx", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = false + }; + var result1 = await _appservice.AddAsync(source1); + Assert.IsTrue(result1); + + var count = await _appservice.CountEnabledAppsAsync(); + Assert.AreEqual(1, count); + } - } + [TestMethod] + public async Task GetAllInheritancedAppsAsyncTest() + { + ClearData(); - [TestMethod()] - public async Task UpdateAsyncTest() + var id = Guid.NewGuid().ToString(); + var source = new App { - var id = Guid.NewGuid().ToString(); - var source = new App - { - Id = id, - Name = "xx", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = true - }; - var result = await _appservice.AddAsync(source); - Assert.IsTrue(result); - - source.Name = "new name"; - source.Secret = "new sec"; - source.CreateTime = DateTime.Now.AddDays(1); - source.UpdateTime = DateTime.Now.AddDays(1); - source.Enabled = false; - - var result1 = await _appservice.UpdateAsync(source); - Assert.IsTrue(result1); - - var app = await _appservice.GetAsync(source.Id); - - Assert.AreEqual(source.Id, app.Id); - Assert.AreEqual(source.Name, app.Name); - Assert.AreEqual(source.Secret, app.Secret); - Assert.AreEqual(source.CreateTime.ToString("yyyyMMddhhmmss"), app.CreateTime.ToString("yyyyMMddhhmmss")); - Assert.AreEqual(source.UpdateTime.Value.ToString("yyyyMMddhhmmss"), app.UpdateTime.Value.ToString("yyyyMMddhhmmss")); - Assert.AreEqual(source.Enabled, app.Enabled); - } - - [TestMethod()] - public async Task CountEnabledAppsAsyncTest() + Id = id, + Name = "xx", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = true, + Type = AppType.PRIVATE + }; + var source1 = new App { - this.ClearData(); - - var id = Guid.NewGuid().ToString(); - var source = new App - { - Id = id, - Name = "xx", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = true - }; - var result = await _appservice.AddAsync(source); - Assert.IsTrue(result); - var id1 = Guid.NewGuid().ToString(); - var source1 = new App - { - Id = id1, - Name = "xx", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = false - }; - var result1 = await _appservice.AddAsync(source1); - Assert.IsTrue(result1); - - var count = await _appservice.CountEnabledAppsAsync(); - Assert.AreEqual(1, count); - } - - [TestMethod()] - public async Task GetAllInheritancedAppsAsyncTest() + Id = Guid.NewGuid().ToString(), + Name = "xxx", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = true, + Type = AppType.PRIVATE + }; + var source2 = new App { - this.ClearData(); - - var id = Guid.NewGuid().ToString(); - var source = new App - { - Id = id, - Name = "xx", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = true, - Type = AppType.PRIVATE - }; - var source1 = new App - { - Id = Guid.NewGuid().ToString(), - Name = "xxx", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = true, - Type = AppType.PRIVATE - }; - var source2 = new App - { - Id = Guid.NewGuid().ToString(), - Name = "xxxx", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = true, - Type = AppType.Inheritance - }; - var source3 = new App - { - Id = Guid.NewGuid().ToString(), - Name = "xxxx", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = false, - Type = AppType.Inheritance - }; - var result = await _appservice.AddAsync(source); - await _appservice.AddAsync(source1); - await _appservice.AddAsync(source2); - await _appservice.AddAsync(source3); - - Assert.IsTrue(result); - - var apps = await _appservice.GetAllInheritancedAppsAsync(); - - Assert.AreEqual(2, apps.Count); - } - [TestMethod()] - public async Task GetInheritancedAppsAsyncTest() + Id = Guid.NewGuid().ToString(), + Name = "xxxx", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = true, + Type = AppType.Inheritance + }; + var source3 = new App { - this.ClearData(); + Id = Guid.NewGuid().ToString(), + Name = "xxxx", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = false, + Type = AppType.Inheritance + }; + var result = await _appservice.AddAsync(source); + await _appservice.AddAsync(source1); + await _appservice.AddAsync(source2); + await _appservice.AddAsync(source3); + + Assert.IsTrue(result); + + var apps = await _appservice.GetAllInheritancedAppsAsync(); + + Assert.AreEqual(2, apps.Count); + } - var id = Guid.NewGuid().ToString(); - var source = new App - { - Id = id, - Name = "xx", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = true, - Type = AppType.PRIVATE - }; - var source1 = new App - { - Id = Guid.NewGuid().ToString(), - Name = "xx1", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = true, - Type = AppType.Inheritance - }; - var source2 = new App - { - Id = Guid.NewGuid().ToString(), - Name = "xx2", - Secret = "sec", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Enabled = true, - Type = AppType.Inheritance - }; - // - var appInher = new AppInheritanced(); - appInher.Id = Guid.NewGuid().ToString(); - appInher.AppId = source.Id; - appInher.InheritancedAppId = source1.Id; - appInher.Sort = 1; - var appInher1 = new AppInheritanced(); - appInher1.Id = Guid.NewGuid().ToString(); - appInher1.AppId = source.Id; - appInher1.InheritancedAppId = source2.Id; - appInher1.Sort = 2; - - var result = await _appservice.AddAsync(source); - await _appservice.AddAsync(source1); - await _appservice.AddAsync(source2); - - await _serviceProvider.GetService().InsertAsync(appInher); - await _serviceProvider.GetService().InsertAsync(appInher1); - - Assert.IsTrue(result); - - var apps = await _appservice.GetInheritancedAppsAsync(source.Id); - - Assert.AreEqual(2, apps.Count); - } + [TestMethod] + public async Task GetInheritancedAppsAsyncTest() + { + ClearData(); + var id = Guid.NewGuid().ToString(); + var source = new App + { + Id = id, + Name = "xx", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = true, + Type = AppType.PRIVATE + }; + var source1 = new App + { + Id = Guid.NewGuid().ToString(), + Name = "xx1", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = true, + Type = AppType.Inheritance + }; + var source2 = new App + { + Id = Guid.NewGuid().ToString(), + Name = "xx2", + Secret = "sec", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Enabled = true, + Type = AppType.Inheritance + }; + // + var appInher = new AppInheritanced(); + appInher.Id = Guid.NewGuid().ToString(); + appInher.AppId = source.Id; + appInher.InheritancedAppId = source1.Id; + appInher.Sort = 1; + var appInher1 = new AppInheritanced(); + appInher1.Id = Guid.NewGuid().ToString(); + appInher1.AppId = source.Id; + appInher1.InheritancedAppId = source2.Id; + appInher1.Sort = 2; + + var result = await _appservice.AddAsync(source); + await _appservice.AddAsync(source1); + await _appservice.AddAsync(source2); + + await _serviceProvider.GetService().InsertAsync(appInher); + await _serviceProvider.GetService().InsertAsync(appInher1); + + Assert.IsTrue(result); + + var apps = await _appservice.GetInheritancedAppsAsync(source.Id); + + Assert.AreEqual(2, apps.Count); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/sqlite/BasicTestService.cs b/test/AgileConfig.Server.ServiceTests/sqlite/BasicTestService.cs index 38aca3a6..e82b4087 100644 --- a/test/AgileConfig.Server.ServiceTests/sqlite/BasicTestService.cs +++ b/test/AgileConfig.Server.ServiceTests/sqlite/BasicTestService.cs @@ -1,60 +1,58 @@ -using AgileConfig.Server.Common; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; using AgileConfig.Server.Data.Abstraction.DbProvider; using AgileConfig.Server.Data.Entity; using AgileConfig.Server.Data.Freesql; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -namespace AgileConfig.Server.ServiceTests.sqlite +namespace AgileConfig.Server.ServiceTests.sqlite; + +public class BasicTestService { - public class BasicTestService + public BasicTestService() + { + Console.WriteLine("BasicTestService ctor."); + } + + protected ServiceProvider GlobalServiceProvider { get; set; } + + public virtual Task> GetConfigurationData() + { + return + null; + } + + public IFreeSql GetFreeSql() + { + var dict = GetConfigurationData().Result; + + var config = new ConfigurationBuilder() + .AddInMemoryCollection(dict) + .Build(); + + var myfreesql = new MyFreeSQL(new DbConfigInfoFactory(config)); + var factory = new EnvFreeSqlFactory(myfreesql); + var fsq = factory.Create(""); + + return fsq; + } + + public virtual void ClearData() { - protected ServiceProvider GlobalServiceProvider { get; set; } - - public virtual Task> GetConfigurationData() - { - return - null; - } - - public IFreeSql GetFreeSql() - { - var dict = this.GetConfigurationData().Result; - - var config = new ConfigurationBuilder() - .AddInMemoryCollection(dict) - .Build(); - - var myfreesql = new MyFreeSQL(new DbConfigInfoFactory(config)); - var factory = new EnvFreeSqlFactory(myfreesql); - var fsq = factory.Create(""); - - return fsq; - } - - public virtual void ClearData() - { - var fsq = GetFreeSql(); - - fsq.Delete().Where("1=1").ExecuteAffrows(); - fsq.Delete().Where("1=1").ExecuteAffrows(); - fsq.Delete().Where("1=1").ExecuteAffrows(); - fsq.Delete().Where("1=1").ExecuteAffrows(); - fsq.Delete().Where("1=1").ExecuteAffrows(); - fsq.Delete().Where("1=1").ExecuteAffrows(); - fsq.Delete().Where("1=1").ExecuteAffrows(); - //fsq.Delete().Where("1=1").ExecuteAffrows(); - fsq.Delete().Where("1=1").ExecuteAffrows(); - - fsq.Dispose(); - } - - public BasicTestService() - { - Console.WriteLine("BasicTestService ctor."); - } + var fsq = GetFreeSql(); + + fsq.Delete().Where("1=1").ExecuteAffrows(); + fsq.Delete().Where("1=1").ExecuteAffrows(); + fsq.Delete().Where("1=1").ExecuteAffrows(); + fsq.Delete().Where("1=1").ExecuteAffrows(); + fsq.Delete().Where("1=1").ExecuteAffrows(); + fsq.Delete().Where("1=1").ExecuteAffrows(); + fsq.Delete().Where("1=1").ExecuteAffrows(); + //fsq.Delete().Where("1=1").ExecuteAffrows(); + fsq.Delete().Where("1=1").ExecuteAffrows(); + + fsq.Dispose(); } -} +} \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/sqlite/ConfigServiceTests.cs b/test/AgileConfig.Server.ServiceTests/sqlite/ConfigServiceTests.cs index 179d2b4f..e03ecb2e 100644 --- a/test/AgileConfig.Server.ServiceTests/sqlite/ConfigServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/sqlite/ConfigServiceTests.cs @@ -1,967 +1,963 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using System; using System.Collections.Generic; -using FreeSql; -using AgileConfig.Server.Data.Freesql; -using AgileConfig.Server.Data.Entity; using System.Threading.Tasks; -using AgileConfig.Server.IService; -using Microsoft.Extensions.Caching.Memory; -using Moq; -using Microsoft.Extensions.DependencyInjection; using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Data.Freesql; +using AgileConfig.Server.Data.Repository.Selector; +using AgileConfig.Server.IService; using AgileConfig.Server.Service; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; -using AgileConfig.Server.Data.Repository.Selector; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; -namespace AgileConfig.Server.ServiceTests.sqlite +namespace AgileConfig.Server.ServiceTests.sqlite; + +[TestClass] +public class ConfigServiceTests : BasicTestService { - [TestClass()] - public class ConfigServiceTests : BasicTestService + private IConfigService _service; + private IServiceProvider _serviceProvider; + private IServiceScope _serviceScope; + + public override Task> GetConfigurationData() { - IServiceProvider _serviceProvider = null; - IServiceScope _serviceScope = null; - IConfigService _service = null; - public override Task> GetConfigurationData() - { - return - Task.FromResult( + return + Task.FromResult( new Dictionary { - {"db:provider","sqlite" }, - {"db:conn","Data Source=agile_config.db" } - }); - } - [TestInitialize] - public async Task TestInitialize() - { - await NewGloablSp(); - _serviceScope = this.GlobalServiceProvider.CreateScope(); - _serviceProvider = this.GlobalServiceProvider.CreateScope().ServiceProvider; - - ClearData(); - - _service = this._serviceProvider.GetService(); - var systeminitializationService = this._serviceProvider.GetService(); - systeminitializationService.TryInitDefaultEnvironment();//初始化环境 DEV TEST STAGE PROD - systeminitializationService.TryInitJwtSecret();//初始化 jwt secret - - Console.WriteLine("Run TestInitialize"); - } - - private async Task NewGloablSp() - { - Console.WriteLine("Try get configration data"); - var dict = await GetConfigurationData(); - - foreach (var item in dict) - { - Console.WriteLine($"key: {item.Key} value: {item.Value}"); - } - - var config = new ConfigurationBuilder() - .AddInMemoryCollection(dict) - .Build(); - Console.WriteLine("Config list"); - foreach (var item in config.AsEnumerable()) - { - Console.WriteLine($"key: {item.Key} value: {item.Value}"); - } - var cache = new Mock(); - IServiceCollection services = new ServiceCollection(); - services.AddScoped(_ => cache.Object); - services.AddLogging(); - services.AddSingleton(config); - services.AddDbConfigInfoFactory(); - services.AddFreeSqlFactory(); - services.AddRepositories(); - services.AddBusinessServices(); - - this.GlobalServiceProvider = services.BuildServiceProvider(); - } - - [TestCleanup] - public void TestCleanup() - { - _service.Dispose(); - _serviceScope.Dispose(); - } - - [TestMethod()] - public async Task AddAsyncTest() - { - var id = Guid.NewGuid().ToString(); - var source = new Config - { - AppId = "001", - Id = id, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online - }; - - var result = await _service.AddAsync(source, ""); - Assert.IsTrue(result); - var config = await _service.GetAsync(source.Id, ""); - - Assert.IsTrue(result); - Assert.IsNotNull(config); - - Assert.AreEqual(source.Id, config.Id); - Assert.AreEqual(source.Group, config.Group); - Assert.AreEqual(source.Key, config.Key); - Assert.AreEqual(source.Value, config.Value); - Assert.AreEqual(source.Description, config.Description); - Assert.AreEqual(source.AppId, config.AppId); - Assert.AreEqual(source.CreateTime.ToString("yyyyMMddHHmmss"), config.CreateTime.ToString("yyyyMMddHHmmss")); - Assert.AreEqual(source.UpdateTime.Value.ToString("yyyyMMddHHmmss"), config.UpdateTime.Value.ToString("yyyyMMddHHmmss")); - Assert.AreEqual(source.Status, config.Status); - Assert.AreEqual(source.OnlineStatus, config.OnlineStatus); - } - - [TestMethod()] - public async Task UpdateAsyncTest() - { - var id = Guid.NewGuid().ToString(); - var source = new Config - { - AppId = "001", - Id = id, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online - }; - - var result = await _service.AddAsync(source, ""); - Assert.IsTrue(result); - - source.AppId = "1"; - source.Group = "1"; - source.Key = "1"; - source.Value = "1"; - source.Description = "1"; - source.CreateTime = DateTime.Now; - source.UpdateTime = DateTime.Now; - source.Status = ConfigStatus.Enabled; - source.OnlineStatus = OnlineStatus.WaitPublish; - - var result1 = await _service.UpdateAsync(source, ""); - var config = await _service.GetAsync(source.Id, ""); - - Assert.IsTrue(result1); - Assert.IsNotNull(config); - - Assert.AreEqual(source.Id, config.Id); - Assert.AreEqual(source.Group, config.Group); - Assert.AreEqual(source.Key, config.Key); - Assert.AreEqual(source.Value, config.Value); - Assert.AreEqual(source.Description, config.Description); - Assert.AreEqual(source.AppId, config.AppId); - Assert.AreEqual(source.CreateTime.ToString("yyyyMMddHHmmss"), config.CreateTime.ToString("yyyyMMddHHmmss")); - Assert.AreEqual(source.UpdateTime.Value.ToString("yyyyMMddHHmmss"), config.UpdateTime.Value.ToString("yyyyMMddHHmmss")); - Assert.AreEqual(source.Status, config.Status); - Assert.AreEqual(source.OnlineStatus, config.OnlineStatus); - } - - [TestMethod()] - public async Task DeleteAsyncTest() - { - var id = Guid.NewGuid().ToString(); - var source = new Config - { - AppId = "001", - Id = id, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online - }; - - var result = await _service.AddAsync(source, ""); - Assert.IsTrue(result); - - var result1 = await _service.DeleteAsync(source, ""); - Assert.IsTrue(result1); - - var config = await _service.GetAsync(source.Id, ""); - - Assert.IsNull(config); - } - - [TestMethod()] - public async Task DeleteAsyncTest1() - { - var id = Guid.NewGuid().ToString(); - var source = new Config - { - AppId = "001", - Id = id, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online - }; - - var result = await _service.AddAsync(source, ""); - Assert.IsTrue(result); - - var result1 = await _service.DeleteAsync(id, ""); - Assert.IsTrue(result1); - - var config = await _service.GetAsync(source.Id, ""); - - Assert.IsNull(config); - } - - [TestMethod()] - public async Task GetAsyncTest() - { - var id = Guid.NewGuid().ToString(); - var source = new Config - { - AppId = "001", - Id = id, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online - }; - - var result = await _service.AddAsync(source, ""); - Assert.IsTrue(result); - - var config = await _service.GetAsync(id, ""); - Assert.IsNotNull(config); - - Assert.AreEqual(source.Id, config.Id); - Assert.AreEqual(source.Group, config.Group); - Assert.AreEqual(source.Key, config.Key); - Assert.AreEqual(source.Value, config.Value); - Assert.AreEqual(source.Description, config.Description); - Assert.AreEqual(source.AppId, config.AppId); - Assert.AreEqual(source.CreateTime.ToString("yyyyMMddHHmmss"), config.CreateTime.ToString("yyyyMMddHHmmss")); - Assert.AreEqual(source.UpdateTime.Value.ToString("yyyyMMddHHmmss"), config.UpdateTime.Value.ToString("yyyyMMddHHmmss")); - Assert.AreEqual(source.Status, config.Status); - Assert.AreEqual(source.OnlineStatus, config.OnlineStatus); - } - - [TestMethod()] - public async Task GetAllConfigsAsyncTest() - { - this.ClearData(); - - var env = "DEV"; - var id = Guid.NewGuid().ToString(); - var appid = Guid.NewGuid().ToString(); - var source = new Config - { - AppId = appid, - Id = id, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Enabled, - OnlineStatus = OnlineStatus.Online, - Env = env - }; - var id1 = Guid.NewGuid().ToString(); - var source1 = new Config - { - AppId = appid, - Id = id1, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online, - Env = env - }; - var result = await _service.AddAsync(source, env); - Assert.IsTrue(result); - var result1 = await _service.AddAsync(source1, env); - Assert.IsTrue(result1); - - var configs = await _service.GetAllConfigsAsync(env); - Assert.IsNotNull(configs); - Assert.AreEqual(1, configs.Count); - } - - [TestMethod()] - public async Task GetByAppIdKeyTest() - { - this.ClearData(); - - var env = "DEV"; - var id = Guid.NewGuid().ToString(); - var appid1 = Guid.NewGuid().ToString(); - var appid2 = Guid.NewGuid().ToString(); - - var source = new Config - { - AppId = appid1, - Id = id, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Enabled, - OnlineStatus = OnlineStatus.Online, - Env = env - }; - var id1 = Guid.NewGuid().ToString(); - var source1 = new Config - { - AppId = appid1, - Id = id1, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online, - Env = env - }; - var id2 = Guid.NewGuid().ToString(); - var source2 = new Config - { - AppId = appid2, - Id = id2, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online, - Env = env - }; - var result = await _service.AddAsync(source, env); - Assert.IsTrue(result); - var result1 = await _service.AddAsync(source1, env); - Assert.IsTrue(result1); - var result2 = await _service.AddAsync(source2, env); - Assert.IsTrue(result2); - - var config = await _service.GetByAppIdKeyEnv(appid1, "g", "k", env); - Assert.IsNotNull(config); - - var config1 = await _service.GetByAppIdKeyEnv(appid2, "g", "k", env); - Assert.IsNull(config1); - } - - [TestMethod()] - public async Task GetByAppIdTest() - { - this.ClearData(); - var id = Guid.NewGuid().ToString(); - var env = "DEV"; - var appid1 = Guid.NewGuid().ToString(); - var appid2 = Guid.NewGuid().ToString(); - - var source = new Config - { - AppId = appid1, - Id = id, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Enabled, - OnlineStatus = OnlineStatus.Online, - Env = env - }; - var id1 = Guid.NewGuid().ToString(); - var source1 = new Config - { - AppId = appid1, - Id = id1, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online, - Env = env - }; - var id2 = Guid.NewGuid().ToString(); - var source2 = new Config - { - AppId = appid2, - Id = id2, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online, - Env = env - }; - var result = await _service.AddAsync(source, env); - Assert.IsTrue(result); - var result1 = await _service.AddAsync(source1, env); - Assert.IsTrue(result1); - var result2 = await _service.AddAsync(source2, env); - Assert.IsTrue(result2); - - var configs = await _service.GetByAppIdAsync(appid1, env); - Assert.IsNotNull(configs); - Assert.AreEqual(1, configs.Count); - } - - [TestMethod()] - public async Task SearchTest() - { - this.ClearData(); - var env = "DEV"; - var id = Guid.NewGuid().ToString(); - var appid1 = Guid.NewGuid().ToString(); - var appid2 = Guid.NewGuid().ToString(); - var source = new Config - { - AppId = appid1, - Id = id, - Group = "group", - Key = "key", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Enabled, - OnlineStatus = OnlineStatus.Online, - Env = env - }; - var id1 = Guid.NewGuid().ToString(); - var source1 = new Config - { - AppId = appid1, - Id = id1, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - Env = env, - OnlineStatus = OnlineStatus.Online, - }; - var id2 = Guid.NewGuid().ToString(); - var source2 = new Config - { - AppId = appid2, - Id = id2, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online, - Env = env, - }; - var result = await _service.AddAsync(source, env); - Assert.IsTrue(result); - var result1 = await _service.AddAsync(source1, env); - Assert.IsTrue(result1); - var result2 = await _service.AddAsync(source2, env); - Assert.IsTrue(result2); - - var configs = await _service.Search(appid1, "", "", env); - Assert.IsNotNull(configs); - Assert.AreEqual(1, configs.Count); - var configs1 = await _service.Search("", "o", "", env); - Assert.IsNotNull(configs1); - Assert.AreEqual(1, configs1.Count); - var configs2 = await _service.Search("", "", "e", env); - Assert.IsNotNull(configs2); - Assert.AreEqual(1, configs2.Count); - } - - [TestMethod()] - public async Task CountEnabledConfigsAsyncTest() - { - this.ClearData(); - - string env = "DEV"; - var id = Guid.NewGuid().ToString(); - var appid = Guid.NewGuid().ToString(); - - var source = new Config - { - AppId = appid, - Id = id, - Group = "group", - Key = "key", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Enabled, - OnlineStatus = OnlineStatus.WaitPublish, - Env = env - }; - var id1 = Guid.NewGuid().ToString(); - var source1 = new Config - { - AppId = appid, - Id = id1, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.WaitPublish, - Env = env - }; - var id2 = Guid.NewGuid().ToString(); - var source2 = new Config - { - AppId = "xxx", - Id = id2, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.WaitPublish, - Env = env - }; - var result = await _service.AddAsync(source, env); - Assert.IsTrue(result); - var result1 = await _service.AddAsync(source1, env); - Assert.IsTrue(result1); - var result2 = await _service.AddAsync(source2, env); - Assert.IsTrue(result2); - - await _service.Publish(appid, new string[] { }, "", "", env); - await _service.Publish("xxx", new string[] { }, "", "", env); - - var count = await _service.CountEnabledConfigsAsync(); - Assert.AreEqual(1, count); - } - - [TestMethod()] - public async Task AppPublishedConfigsMd5Test() - { - this.ClearData(); - - string env = "DEV"; - var id = Guid.NewGuid().ToString(); - var app1 = Guid.NewGuid().ToString(); - var app2 = Guid.NewGuid().ToString(); - - - var source = new Config - { - AppId = app1, - Id = id, - Group = "group", - Key = "key", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Enabled, - OnlineStatus = OnlineStatus.Online, - Env = env - }; - var id1 = Guid.NewGuid().ToString(); - var source1 = new Config - { - AppId = app1, - Id = id1, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online, - Env = env - - }; - var id2 = Guid.NewGuid().ToString(); - var source2 = new Config - { - AppId = app2, - Id = id2, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online, - Env = env - }; - var result = await _service.AddAsync(source, env); - Assert.IsTrue(result); - var result1 = await _service.AddAsync(source1, env); - Assert.IsTrue(result1); - var result2 = await _service.AddAsync(source2, env); - Assert.IsTrue(result2); - - var md5 = await _service.AppPublishedConfigsMd5(app1, env); - Assert.IsNotNull(md5); - } - - [TestMethod()] - public void AppPublishedConfigsMd5CacheTest() - { - } - - [TestMethod()] - public async Task GetPublishedConfigsByAppIdTest() - { - this.ClearData(); - - var id = Guid.NewGuid().ToString(); - var app1 = Guid.NewGuid().ToString(); - var app2 = Guid.NewGuid().ToString(); - - var source = new Config - { - AppId = app1, - Id = id, - Group = "group", - Key = "key", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Enabled, - OnlineStatus = OnlineStatus.Online - }; - var id1 = Guid.NewGuid().ToString(); - var source1 = new Config - { - AppId = app1, - Id = id1, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online - }; - var id2 = Guid.NewGuid().ToString(); - var source2 = new Config - { - AppId = app2, - Id = id2, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online - }; - var result = await _service.AddAsync(source, ""); - Assert.IsTrue(result); - var result1 = await _service.AddAsync(source1, ""); - Assert.IsTrue(result1); - var result2 = await _service.AddAsync(source2, ""); - Assert.IsTrue(result2); - - } - - [TestMethod()] - public async Task AddRangeAsyncTest() - { - var id = Guid.NewGuid().ToString(); - var app1 = Guid.NewGuid().ToString(); - var app2 = Guid.NewGuid().ToString(); - var env = "DEV"; - - var source = new Config - { - AppId = app1, - Id = id, - Group = "group", - Key = "key", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Enabled, - OnlineStatus = OnlineStatus.Online, - Env = env - }; - var id1 = Guid.NewGuid().ToString(); - var source1 = new Config - { - AppId = app1, - Id = id1, - Group = "g", - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Deleted, - OnlineStatus = OnlineStatus.Online, - Env = env - }; - - var result = await _service.AddRangeAsync(new List { - source, - source1 - }, env); - Assert.IsTrue(result); - - var config = await _service.GetAsync(id, env); - Assert.IsNotNull(config); - var config1 = await _service.GetAsync(id1, env); - Assert.IsNotNull(config1); - } - - [TestMethod()] - public async Task GetPublishedConfigsByAppIdWithInheritanced_DictionaryTest() - { - this.ClearData(); - - string env = "DEV"; - - var app = new App(); - app.Id = "001"; - app.Name = "x"; - app.Enabled = true; - app.CreateTime = DateTime.Now; - app.UpdateTime = DateTime.Now; - app.Type = AppType.PRIVATE; // app 001 私有 - var app1 = new App(); - app1.Id = "002"; - app1.Name = "x2"; - app1.Enabled = true; - app1.CreateTime = DateTime.Now; - app1.UpdateTime = DateTime.Now; - app1.Type = AppType.Inheritance; // APP 002 公开 - - var id = Guid.NewGuid().ToString(); - var source = new Config - { - AppId = "001", - Id = id, - Key = "k", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Enabled, - OnlineStatus = OnlineStatus.WaitPublish, - Env = env - }; - var id1 = Guid.NewGuid().ToString(); - var source1 = new Config - { - AppId = "001", - Id = id1, - Key = "k1", - Value = "v1", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Enabled, - OnlineStatus = OnlineStatus.WaitPublish, - Env = env - }; - var source2 = new Config - { - AppId = "002", - Id = Guid.NewGuid().ToString(), - Key = "k2", - Value = "v", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Enabled, - OnlineStatus = OnlineStatus.WaitPublish, - Env = env - }; - var source3 = new Config - { - AppId = "002", - Id = Guid.NewGuid().ToString(), - Key = "k21", - Value = "v2", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Enabled, - OnlineStatus = OnlineStatus.WaitPublish, - Env = env - }; - var appref = new AppInheritanced(); - appref.AppId = app.Id; - appref.InheritancedAppId = app1.Id; // app 001 继承 app 002 - appref.Sort = 1; - appref.Id = Guid.NewGuid().ToString(); - - await _serviceProvider.GetService().AddAsync(app); - await _serviceProvider.GetService().AddAsync(app1); - await _serviceProvider.GetService().InsertAsync(appref); - - // 插入4个config,2个app 001,2个app 002 - await _service.AddAsync(source, env); - await _service.AddAsync(source1, env); - await _service.AddAsync(source2, env); - await _service.AddAsync(source3, env); - - await _service.Publish(app1.Id, new string[] { }, "", "", env); - await _service.Publish(app.Id, new string[] { }, "", "", env); - - var dict = await _service.GetPublishedConfigsByAppIdWithInheritance_Dictionary(app.Id, env); - Assert.IsNotNull(dict); - Assert.AreEqual(4, dict.Keys.Count); - - Assert.IsTrue(dict.ContainsKey(source.Key)); - Assert.IsTrue(dict.ContainsKey(source1.Key)); - Assert.IsTrue(dict.ContainsKey(source2.Key)); - Assert.IsTrue(dict.ContainsKey(source3.Key)); - - Assert.IsTrue(dict[source.Key].Value == "v"); - Assert.IsTrue(dict[source1.Key].Value == "v1"); - Assert.IsTrue(dict[source2.Key].Value == "v"); - Assert.IsTrue(dict[source3.Key].Value == "v2"); - - var source4 = new Config - { - AppId = "001", - Id = Guid.NewGuid().ToString(), - Key = "k4", - Value = "v3", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Enabled, - OnlineStatus = OnlineStatus.WaitPublish, - Env = env - }; - var source5 = new Config - { - AppId = "002", - Id = Guid.NewGuid().ToString(), - Key = "k4", - Value = "v2", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Enabled, - OnlineStatus = OnlineStatus.WaitPublish, - Env = env - }; - // 插入2个config,1个app 001,1个app 002,keyvalue相同,app 001 优先级高 - await _service.AddAsync(source4, env); - await _service.AddAsync(source5, env); - - await _service.Publish(app1.Id, new string[] { }, "", "", env); - await _service.Publish(app.Id, new string[] { }, "", "", env); - - dict = await _service.GetPublishedConfigsByAppIdWithInheritance_Dictionary(app.Id, env); - Assert.IsNotNull(dict); - Assert.AreEqual(5, dict.Keys.Count); - - var config1 = dict["k4"]; - Assert.AreEqual(source4.Value, config1.Value); - - var app2 = new App(); - app2.Id = "003"; - app2.Name = "x"; - app2.Enabled = true; - app2.CreateTime = DateTime.Now; - app2.UpdateTime = DateTime.Now; - app2.Type = AppType.Inheritance; - await _serviceProvider.GetService().AddAsync(app2); - - - // 插入1个app 003 - await _serviceProvider.GetService().DeleteAsync(appref); - var appref1 = new AppInheritanced(); - appref1.AppId = app.Id; - appref1.InheritancedAppId = app2.Id; // app 001 继承 app 003 - appref1.Sort = 2; - appref1.Id = Guid.NewGuid().ToString(); - await _serviceProvider.GetService().InsertAsync(appref1); - - var source6 = new Config - { - AppId = "003", - Id = Guid.NewGuid().ToString(), - Key = "k2", - Value = "k4444", - Description = "d", - CreateTime = DateTime.Now, - UpdateTime = DateTime.Now, - Status = ConfigStatus.Enabled, - OnlineStatus = OnlineStatus.WaitPublish, - Env = env - }; - await _service.AddAsync(source6, env); - await _service.Publish(app2.Id, new string[] { }, "", "", env); // 发布app 003 - - dict = await _service.GetPublishedConfigsByAppIdWithInheritance_Dictionary(app.Id, env); - Assert.IsNotNull(dict); - Assert.AreEqual(4, dict.Keys.Count); - - Assert.IsTrue(dict.ContainsKey(source6.Key)); - Assert.IsTrue(dict.ContainsKey(source4.Key)); - Assert.IsTrue(dict.ContainsKey(source1.Key)); - Assert.IsTrue(dict.ContainsKey(source.Key)); - - Assert.IsTrue(dict[source6.Key].Value == "k4444"); - Assert.IsTrue(dict[source4.Key].Value == "v3"); - Assert.IsTrue(dict[source1.Key].Value == "v1"); - Assert.IsTrue(dict[source.Key].Value == "v"); - } + { "db:provider", "sqlite" }, + { "db:conn", "Data Source=agile_config.db" } + }); + } + + [TestInitialize] + public async Task TestInitialize() + { + await NewGloablSp(); + _serviceScope = GlobalServiceProvider.CreateScope(); + _serviceProvider = GlobalServiceProvider.CreateScope().ServiceProvider; + + ClearData(); + + _service = _serviceProvider.GetService(); + var systeminitializationService = _serviceProvider.GetService(); + systeminitializationService.TryInitDefaultEnvironment(); //初始化环境 DEV TEST STAGE PROD + systeminitializationService.TryInitJwtSecret(); //初始化 jwt secret + + Console.WriteLine("Run TestInitialize"); + } + + private async Task NewGloablSp() + { + Console.WriteLine("Try get configration data"); + var dict = await GetConfigurationData(); + + foreach (var item in dict) Console.WriteLine($"key: {item.Key} value: {item.Value}"); + + var config = new ConfigurationBuilder() + .AddInMemoryCollection(dict) + .Build(); + Console.WriteLine("Config list"); + foreach (var item in config.AsEnumerable()) Console.WriteLine($"key: {item.Key} value: {item.Value}"); + var cache = new Mock(); + IServiceCollection services = new ServiceCollection(); + services.AddScoped(_ => cache.Object); + services.AddLogging(); + services.AddSingleton(config); + services.AddDbConfigInfoFactory(); + services.AddFreeSqlFactory(); + services.AddRepositories(); + services.AddBusinessServices(); + + GlobalServiceProvider = services.BuildServiceProvider(); + } + + [TestCleanup] + public void TestCleanup() + { + _service.Dispose(); + _serviceScope.Dispose(); + } + + [TestMethod] + public async Task AddAsyncTest() + { + var id = Guid.NewGuid().ToString(); + var source = new Config + { + AppId = "001", + Id = id, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online + }; + + var result = await _service.AddAsync(source, ""); + Assert.IsTrue(result); + var config = await _service.GetAsync(source.Id, ""); + + Assert.IsTrue(result); + Assert.IsNotNull(config); + + Assert.AreEqual(source.Id, config.Id); + Assert.AreEqual(source.Group, config.Group); + Assert.AreEqual(source.Key, config.Key); + Assert.AreEqual(source.Value, config.Value); + Assert.AreEqual(source.Description, config.Description); + Assert.AreEqual(source.AppId, config.AppId); + Assert.AreEqual(source.CreateTime.ToString("yyyyMMddHHmmss"), config.CreateTime.ToString("yyyyMMddHHmmss")); + Assert.AreEqual(source.UpdateTime.Value.ToString("yyyyMMddHHmmss"), + config.UpdateTime.Value.ToString("yyyyMMddHHmmss")); + Assert.AreEqual(source.Status, config.Status); + Assert.AreEqual(source.OnlineStatus, config.OnlineStatus); + } + + [TestMethod] + public async Task UpdateAsyncTest() + { + var id = Guid.NewGuid().ToString(); + var source = new Config + { + AppId = "001", + Id = id, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online + }; + + var result = await _service.AddAsync(source, ""); + Assert.IsTrue(result); + + source.AppId = "1"; + source.Group = "1"; + source.Key = "1"; + source.Value = "1"; + source.Description = "1"; + source.CreateTime = DateTime.Now; + source.UpdateTime = DateTime.Now; + source.Status = ConfigStatus.Enabled; + source.OnlineStatus = OnlineStatus.WaitPublish; + + var result1 = await _service.UpdateAsync(source, ""); + var config = await _service.GetAsync(source.Id, ""); + + Assert.IsTrue(result1); + Assert.IsNotNull(config); + + Assert.AreEqual(source.Id, config.Id); + Assert.AreEqual(source.Group, config.Group); + Assert.AreEqual(source.Key, config.Key); + Assert.AreEqual(source.Value, config.Value); + Assert.AreEqual(source.Description, config.Description); + Assert.AreEqual(source.AppId, config.AppId); + Assert.AreEqual(source.CreateTime.ToString("yyyyMMddHHmmss"), config.CreateTime.ToString("yyyyMMddHHmmss")); + Assert.AreEqual(source.UpdateTime.Value.ToString("yyyyMMddHHmmss"), + config.UpdateTime.Value.ToString("yyyyMMddHHmmss")); + Assert.AreEqual(source.Status, config.Status); + Assert.AreEqual(source.OnlineStatus, config.OnlineStatus); + } + + [TestMethod] + public async Task DeleteAsyncTest() + { + var id = Guid.NewGuid().ToString(); + var source = new Config + { + AppId = "001", + Id = id, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online + }; + + var result = await _service.AddAsync(source, ""); + Assert.IsTrue(result); + + var result1 = await _service.DeleteAsync(source, ""); + Assert.IsTrue(result1); + + var config = await _service.GetAsync(source.Id, ""); + + Assert.IsNull(config); + } + + [TestMethod] + public async Task DeleteAsyncTest1() + { + var id = Guid.NewGuid().ToString(); + var source = new Config + { + AppId = "001", + Id = id, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online + }; + + var result = await _service.AddAsync(source, ""); + Assert.IsTrue(result); + + var result1 = await _service.DeleteAsync(id, ""); + Assert.IsTrue(result1); + + var config = await _service.GetAsync(source.Id, ""); + + Assert.IsNull(config); + } + + [TestMethod] + public async Task GetAsyncTest() + { + var id = Guid.NewGuid().ToString(); + var source = new Config + { + AppId = "001", + Id = id, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online + }; + + var result = await _service.AddAsync(source, ""); + Assert.IsTrue(result); + + var config = await _service.GetAsync(id, ""); + Assert.IsNotNull(config); + + Assert.AreEqual(source.Id, config.Id); + Assert.AreEqual(source.Group, config.Group); + Assert.AreEqual(source.Key, config.Key); + Assert.AreEqual(source.Value, config.Value); + Assert.AreEqual(source.Description, config.Description); + Assert.AreEqual(source.AppId, config.AppId); + Assert.AreEqual(source.CreateTime.ToString("yyyyMMddHHmmss"), config.CreateTime.ToString("yyyyMMddHHmmss")); + Assert.AreEqual(source.UpdateTime.Value.ToString("yyyyMMddHHmmss"), + config.UpdateTime.Value.ToString("yyyyMMddHHmmss")); + Assert.AreEqual(source.Status, config.Status); + Assert.AreEqual(source.OnlineStatus, config.OnlineStatus); + } + + [TestMethod] + public async Task GetAllConfigsAsyncTest() + { + ClearData(); + + var env = "DEV"; + var id = Guid.NewGuid().ToString(); + var appid = Guid.NewGuid().ToString(); + var source = new Config + { + AppId = appid, + Id = id, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Enabled, + OnlineStatus = OnlineStatus.Online, + Env = env + }; + var id1 = Guid.NewGuid().ToString(); + var source1 = new Config + { + AppId = appid, + Id = id1, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online, + Env = env + }; + var result = await _service.AddAsync(source, env); + Assert.IsTrue(result); + var result1 = await _service.AddAsync(source1, env); + Assert.IsTrue(result1); + + var configs = await _service.GetAllConfigsAsync(env); + Assert.IsNotNull(configs); + Assert.AreEqual(1, configs.Count); + } + + [TestMethod] + public async Task GetByAppIdKeyTest() + { + ClearData(); + + var env = "DEV"; + var id = Guid.NewGuid().ToString(); + var appid1 = Guid.NewGuid().ToString(); + var appid2 = Guid.NewGuid().ToString(); + + var source = new Config + { + AppId = appid1, + Id = id, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Enabled, + OnlineStatus = OnlineStatus.Online, + Env = env + }; + var id1 = Guid.NewGuid().ToString(); + var source1 = new Config + { + AppId = appid1, + Id = id1, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online, + Env = env + }; + var id2 = Guid.NewGuid().ToString(); + var source2 = new Config + { + AppId = appid2, + Id = id2, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online, + Env = env + }; + var result = await _service.AddAsync(source, env); + Assert.IsTrue(result); + var result1 = await _service.AddAsync(source1, env); + Assert.IsTrue(result1); + var result2 = await _service.AddAsync(source2, env); + Assert.IsTrue(result2); + + var config = await _service.GetByAppIdKeyEnv(appid1, "g", "k", env); + Assert.IsNotNull(config); + + var config1 = await _service.GetByAppIdKeyEnv(appid2, "g", "k", env); + Assert.IsNull(config1); + } + + [TestMethod] + public async Task GetByAppIdTest() + { + ClearData(); + var id = Guid.NewGuid().ToString(); + var env = "DEV"; + var appid1 = Guid.NewGuid().ToString(); + var appid2 = Guid.NewGuid().ToString(); + + var source = new Config + { + AppId = appid1, + Id = id, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Enabled, + OnlineStatus = OnlineStatus.Online, + Env = env + }; + var id1 = Guid.NewGuid().ToString(); + var source1 = new Config + { + AppId = appid1, + Id = id1, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online, + Env = env + }; + var id2 = Guid.NewGuid().ToString(); + var source2 = new Config + { + AppId = appid2, + Id = id2, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online, + Env = env + }; + var result = await _service.AddAsync(source, env); + Assert.IsTrue(result); + var result1 = await _service.AddAsync(source1, env); + Assert.IsTrue(result1); + var result2 = await _service.AddAsync(source2, env); + Assert.IsTrue(result2); + + var configs = await _service.GetByAppIdAsync(appid1, env); + Assert.IsNotNull(configs); + Assert.AreEqual(1, configs.Count); + } + + [TestMethod] + public async Task SearchTest() + { + ClearData(); + var env = "DEV"; + var id = Guid.NewGuid().ToString(); + var appid1 = Guid.NewGuid().ToString(); + var appid2 = Guid.NewGuid().ToString(); + var source = new Config + { + AppId = appid1, + Id = id, + Group = "group", + Key = "key", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Enabled, + OnlineStatus = OnlineStatus.Online, + Env = env + }; + var id1 = Guid.NewGuid().ToString(); + var source1 = new Config + { + AppId = appid1, + Id = id1, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + Env = env, + OnlineStatus = OnlineStatus.Online + }; + var id2 = Guid.NewGuid().ToString(); + var source2 = new Config + { + AppId = appid2, + Id = id2, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online, + Env = env + }; + var result = await _service.AddAsync(source, env); + Assert.IsTrue(result); + var result1 = await _service.AddAsync(source1, env); + Assert.IsTrue(result1); + var result2 = await _service.AddAsync(source2, env); + Assert.IsTrue(result2); + + var configs = await _service.Search(appid1, "", "", env); + Assert.IsNotNull(configs); + Assert.AreEqual(1, configs.Count); + var configs1 = await _service.Search("", "o", "", env); + Assert.IsNotNull(configs1); + Assert.AreEqual(1, configs1.Count); + var configs2 = await _service.Search("", "", "e", env); + Assert.IsNotNull(configs2); + Assert.AreEqual(1, configs2.Count); + } + + [TestMethod] + public async Task CountEnabledConfigsAsyncTest() + { + ClearData(); + + var env = "DEV"; + var id = Guid.NewGuid().ToString(); + var appid = Guid.NewGuid().ToString(); + + var source = new Config + { + AppId = appid, + Id = id, + Group = "group", + Key = "key", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Enabled, + OnlineStatus = OnlineStatus.WaitPublish, + Env = env + }; + var id1 = Guid.NewGuid().ToString(); + var source1 = new Config + { + AppId = appid, + Id = id1, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.WaitPublish, + Env = env + }; + var id2 = Guid.NewGuid().ToString(); + var source2 = new Config + { + AppId = "xxx", + Id = id2, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.WaitPublish, + Env = env + }; + var result = await _service.AddAsync(source, env); + Assert.IsTrue(result); + var result1 = await _service.AddAsync(source1, env); + Assert.IsTrue(result1); + var result2 = await _service.AddAsync(source2, env); + Assert.IsTrue(result2); + + await _service.Publish(appid, new string[] { }, "", "", env); + await _service.Publish("xxx", new string[] { }, "", "", env); + + var count = await _service.CountEnabledConfigsAsync(); + Assert.AreEqual(1, count); + } + + [TestMethod] + public async Task AppPublishedConfigsMd5Test() + { + ClearData(); + + var env = "DEV"; + var id = Guid.NewGuid().ToString(); + var app1 = Guid.NewGuid().ToString(); + var app2 = Guid.NewGuid().ToString(); + + + var source = new Config + { + AppId = app1, + Id = id, + Group = "group", + Key = "key", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Enabled, + OnlineStatus = OnlineStatus.Online, + Env = env + }; + var id1 = Guid.NewGuid().ToString(); + var source1 = new Config + { + AppId = app1, + Id = id1, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online, + Env = env + }; + var id2 = Guid.NewGuid().ToString(); + var source2 = new Config + { + AppId = app2, + Id = id2, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online, + Env = env + }; + var result = await _service.AddAsync(source, env); + Assert.IsTrue(result); + var result1 = await _service.AddAsync(source1, env); + Assert.IsTrue(result1); + var result2 = await _service.AddAsync(source2, env); + Assert.IsTrue(result2); + + var md5 = await _service.AppPublishedConfigsMd5(app1, env); + Assert.IsNotNull(md5); + } + + [TestMethod] + public void AppPublishedConfigsMd5CacheTest() + { + } + + [TestMethod] + public async Task GetPublishedConfigsByAppIdTest() + { + ClearData(); + + var id = Guid.NewGuid().ToString(); + var app1 = Guid.NewGuid().ToString(); + var app2 = Guid.NewGuid().ToString(); + + var source = new Config + { + AppId = app1, + Id = id, + Group = "group", + Key = "key", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Enabled, + OnlineStatus = OnlineStatus.Online + }; + var id1 = Guid.NewGuid().ToString(); + var source1 = new Config + { + AppId = app1, + Id = id1, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online + }; + var id2 = Guid.NewGuid().ToString(); + var source2 = new Config + { + AppId = app2, + Id = id2, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online + }; + var result = await _service.AddAsync(source, ""); + Assert.IsTrue(result); + var result1 = await _service.AddAsync(source1, ""); + Assert.IsTrue(result1); + var result2 = await _service.AddAsync(source2, ""); + Assert.IsTrue(result2); + } + + [TestMethod] + public async Task AddRangeAsyncTest() + { + var id = Guid.NewGuid().ToString(); + var app1 = Guid.NewGuid().ToString(); + var app2 = Guid.NewGuid().ToString(); + var env = "DEV"; + + var source = new Config + { + AppId = app1, + Id = id, + Group = "group", + Key = "key", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Enabled, + OnlineStatus = OnlineStatus.Online, + Env = env + }; + var id1 = Guid.NewGuid().ToString(); + var source1 = new Config + { + AppId = app1, + Id = id1, + Group = "g", + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Deleted, + OnlineStatus = OnlineStatus.Online, + Env = env + }; + + var result = await _service.AddRangeAsync(new List + { + source, + source1 + }, env); + Assert.IsTrue(result); + + var config = await _service.GetAsync(id, env); + Assert.IsNotNull(config); + var config1 = await _service.GetAsync(id1, env); + Assert.IsNotNull(config1); + } + + [TestMethod] + public async Task GetPublishedConfigsByAppIdWithInheritanced_DictionaryTest() + { + ClearData(); + + var env = "DEV"; + + var app = new App(); + app.Id = "001"; + app.Name = "x"; + app.Enabled = true; + app.CreateTime = DateTime.Now; + app.UpdateTime = DateTime.Now; + app.Type = AppType.PRIVATE; // app 001 私有 + var app1 = new App(); + app1.Id = "002"; + app1.Name = "x2"; + app1.Enabled = true; + app1.CreateTime = DateTime.Now; + app1.UpdateTime = DateTime.Now; + app1.Type = AppType.Inheritance; // APP 002 公开 + + var id = Guid.NewGuid().ToString(); + var source = new Config + { + AppId = "001", + Id = id, + Key = "k", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Enabled, + OnlineStatus = OnlineStatus.WaitPublish, + Env = env + }; + var id1 = Guid.NewGuid().ToString(); + var source1 = new Config + { + AppId = "001", + Id = id1, + Key = "k1", + Value = "v1", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Enabled, + OnlineStatus = OnlineStatus.WaitPublish, + Env = env + }; + var source2 = new Config + { + AppId = "002", + Id = Guid.NewGuid().ToString(), + Key = "k2", + Value = "v", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Enabled, + OnlineStatus = OnlineStatus.WaitPublish, + Env = env + }; + var source3 = new Config + { + AppId = "002", + Id = Guid.NewGuid().ToString(), + Key = "k21", + Value = "v2", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Enabled, + OnlineStatus = OnlineStatus.WaitPublish, + Env = env + }; + var appref = new AppInheritanced(); + appref.AppId = app.Id; + appref.InheritancedAppId = app1.Id; // app 001 继承 app 002 + appref.Sort = 1; + appref.Id = Guid.NewGuid().ToString(); + + await _serviceProvider.GetService().AddAsync(app); + await _serviceProvider.GetService().AddAsync(app1); + await _serviceProvider.GetService().InsertAsync(appref); + + // 插入4个config,2个app 001,2个app 002 + await _service.AddAsync(source, env); + await _service.AddAsync(source1, env); + await _service.AddAsync(source2, env); + await _service.AddAsync(source3, env); + + await _service.Publish(app1.Id, new string[] { }, "", "", env); + await _service.Publish(app.Id, new string[] { }, "", "", env); + + var dict = await _service.GetPublishedConfigsByAppIdWithInheritance_Dictionary(app.Id, env); + Assert.IsNotNull(dict); + Assert.AreEqual(4, dict.Keys.Count); + + Assert.IsTrue(dict.ContainsKey(source.Key)); + Assert.IsTrue(dict.ContainsKey(source1.Key)); + Assert.IsTrue(dict.ContainsKey(source2.Key)); + Assert.IsTrue(dict.ContainsKey(source3.Key)); + + Assert.IsTrue(dict[source.Key].Value == "v"); + Assert.IsTrue(dict[source1.Key].Value == "v1"); + Assert.IsTrue(dict[source2.Key].Value == "v"); + Assert.IsTrue(dict[source3.Key].Value == "v2"); + + var source4 = new Config + { + AppId = "001", + Id = Guid.NewGuid().ToString(), + Key = "k4", + Value = "v3", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Enabled, + OnlineStatus = OnlineStatus.WaitPublish, + Env = env + }; + var source5 = new Config + { + AppId = "002", + Id = Guid.NewGuid().ToString(), + Key = "k4", + Value = "v2", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Enabled, + OnlineStatus = OnlineStatus.WaitPublish, + Env = env + }; + // 插入2个config,1个app 001,1个app 002,keyvalue相同,app 001 优先级高 + await _service.AddAsync(source4, env); + await _service.AddAsync(source5, env); + + await _service.Publish(app1.Id, new string[] { }, "", "", env); + await _service.Publish(app.Id, new string[] { }, "", "", env); + + dict = await _service.GetPublishedConfigsByAppIdWithInheritance_Dictionary(app.Id, env); + Assert.IsNotNull(dict); + Assert.AreEqual(5, dict.Keys.Count); + + var config1 = dict["k4"]; + Assert.AreEqual(source4.Value, config1.Value); + + var app2 = new App(); + app2.Id = "003"; + app2.Name = "x"; + app2.Enabled = true; + app2.CreateTime = DateTime.Now; + app2.UpdateTime = DateTime.Now; + app2.Type = AppType.Inheritance; + await _serviceProvider.GetService().AddAsync(app2); + + + // 插入1个app 003 + await _serviceProvider.GetService().DeleteAsync(appref); + var appref1 = new AppInheritanced(); + appref1.AppId = app.Id; + appref1.InheritancedAppId = app2.Id; // app 001 继承 app 003 + appref1.Sort = 2; + appref1.Id = Guid.NewGuid().ToString(); + await _serviceProvider.GetService().InsertAsync(appref1); + + var source6 = new Config + { + AppId = "003", + Id = Guid.NewGuid().ToString(), + Key = "k2", + Value = "k4444", + Description = "d", + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + Status = ConfigStatus.Enabled, + OnlineStatus = OnlineStatus.WaitPublish, + Env = env + }; + await _service.AddAsync(source6, env); + await _service.Publish(app2.Id, new string[] { }, "", "", env); // 发布app 003 + + dict = await _service.GetPublishedConfigsByAppIdWithInheritance_Dictionary(app.Id, env); + Assert.IsNotNull(dict); + Assert.AreEqual(4, dict.Keys.Count); + + Assert.IsTrue(dict.ContainsKey(source6.Key)); + Assert.IsTrue(dict.ContainsKey(source4.Key)); + Assert.IsTrue(dict.ContainsKey(source1.Key)); + Assert.IsTrue(dict.ContainsKey(source.Key)); + + Assert.IsTrue(dict[source6.Key].Value == "k4444"); + Assert.IsTrue(dict[source4.Key].Value == "v3"); + Assert.IsTrue(dict[source1.Key].Value == "v1"); + Assert.IsTrue(dict[source.Key].Value == "v"); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/sqlite/ServerNodeServiceTests.cs b/test/AgileConfig.Server.ServiceTests/sqlite/ServerNodeServiceTests.cs index 4d5a8ac6..f9de4075 100644 --- a/test/AgileConfig.Server.ServiceTests/sqlite/ServerNodeServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/sqlite/ServerNodeServiceTests.cs @@ -1,242 +1,237 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using System; using System.Collections.Generic; -using AgileConfig.Server.Data.Entity; using System.Threading.Tasks; +using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Data.Freesql; +using AgileConfig.Server.Data.Repository.Selector; using AgileConfig.Server.IService; -using Microsoft.Extensions.DependencyInjection; -using MongoDB.Driver.Linq; +using AgileConfig.Server.Service; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; -using AgileConfig.Server.Common; -using AgileConfig.Server.Data.Repository.Selector; -using AgileConfig.Server.Data.Freesql; -using AgileConfig.Server.Service; -using AgileConfig.Server.Data.Abstraction; -namespace AgileConfig.Server.ServiceTests.sqlite +namespace AgileConfig.Server.ServiceTests.sqlite; + +[TestClass] +public class ServerNodeServiceTests : BasicTestService { - [TestClass()] - public class ServerNodeServiceTests : BasicTestService + private IServerNodeService _serverNodeService; + private IServiceProvider _serviceProvider; + private IServiceScope _serviceScope; + + public override Task> GetConfigurationData() { - IServiceProvider _serviceProvider = null; - IServiceScope _serviceScope = null; - IServerNodeService _serverNodeService = null; - - public override Task> GetConfigurationData() - { - return - Task.FromResult( + return + Task.FromResult( new Dictionary { - {"db:provider","sqlite" }, - {"db:conn","Data Source=agile_config.db" } - }); - } - [TestInitialize] - public async Task TestInitialize() - { - await NewGlobalSp(); - - _serviceScope = GlobalServiceProvider.CreateScope(); - _serviceProvider = _serviceScope.ServiceProvider; - - var systeminitializationService = _serviceProvider.GetService(); - systeminitializationService.TryInitDefaultEnvironment();//初始化环境 DEV TEST STAGE PROD - systeminitializationService.TryInitJwtSecret();//初始化 jwt secret - - _serverNodeService = _serviceProvider.GetService(); - - Console.WriteLine($"IServerNodeService type is {_serverNodeService.GetType().FullName}"); - - Console.WriteLine("Run TestInitialize"); - } - - private async Task NewGlobalSp() - { - Console.WriteLine("Try get configration data"); - var dict = await GetConfigurationData(); - - foreach (var item in dict) - { - Console.WriteLine($"key: {item.Key} value: {item.Value}"); - } - - var config = new ConfigurationBuilder() - .AddInMemoryCollection(dict) - .Build(); - Console.WriteLine("Config list"); - foreach (var item in config.AsEnumerable()) - { - Console.WriteLine($"key: {item.Key} value: {item.Value}"); - } - - var cache = new Mock(); - IServiceCollection services = new ServiceCollection(); - services.AddScoped(_ => cache.Object); - services.AddLogging(); - services.AddSingleton(config); - services.AddDbConfigInfoFactory(); - services.AddFreeSqlFactory(); - services.AddRepositories(); - services.AddBusinessServices(); - - this.GlobalServiceProvider = services.BuildServiceProvider(); - } - - [TestCleanup] - public void TestCleanup() - { - _serverNodeService.Dispose(); - _serviceScope.Dispose(); - } - - [TestMethod()] - public async Task AddAsyncTest() - { - this.ClearData(); - - var source = new ServerNode(); - source.Id = "1"; - source.CreateTime = DateTime.Now; - source.LastEchoTime = DateTime.Now; - source.Remark = "2"; - source.Status = NodeStatus.Offline; - - var result = await _serverNodeService.AddAsync(source); - Assert.IsTrue(result); - - var node = await _serverNodeService.GetAsync("1"); - Assert.IsNotNull(node); - - Assert.AreEqual(source.Id, node.Id); - Assert.AreEqual(source.CreateTime.ToString("yyyyMMddHHmmss"), node.CreateTime.ToString("yyyyMMddHHmmss")); - Assert.AreEqual(source.LastEchoTime.Value.ToString("yyyyMMddHHmmss"), node.LastEchoTime.Value.ToString("yyyyMMddHHmmss")); - Assert.AreEqual(source.Remark, node.Remark); - Assert.AreEqual(source.Status, node.Status); - } - - [TestMethod()] - public async Task DeleteAsyncTest() - { - this.ClearData(); - - var source = new ServerNode(); - source.Id = "1"; - source.CreateTime = DateTime.Now; - source.LastEchoTime = DateTime.Now; - source.Remark = "2"; - source.Status = NodeStatus.Offline; - - var result = await _serverNodeService.AddAsync(source); - Assert.IsTrue(result); - - var result1 = await _serverNodeService.DeleteAsync(source); - Assert.IsTrue(result1); - - var node = await _serverNodeService.GetAsync("1"); - - Assert.IsNull(node); - } - - [TestMethod()] - public async Task DeleteAsyncTest1() - { - this.ClearData(); - - var source = new ServerNode(); - source.Id = "1"; - source.CreateTime = DateTime.Now; - source.LastEchoTime = DateTime.Now; - source.Remark = "2"; - source.Status = NodeStatus.Offline; - - var result = await _serverNodeService.AddAsync(source); - Assert.IsTrue(result); - - var result1 = await _serverNodeService.DeleteAsync(source.Id); - Assert.IsTrue(result1); - - var node = await _serverNodeService.GetAsync("1"); - - Assert.IsNull(node); - } - - [TestMethod()] - public async Task GetAllNodesAsyncTest() - { - this.ClearData(); - - var source = new ServerNode(); - source.Id = "1"; - source.CreateTime = DateTime.Now; - source.LastEchoTime = DateTime.Now; - source.Remark = "2"; - source.Status = NodeStatus.Offline; - - var result = await _serverNodeService.AddAsync(source); - Assert.IsTrue(result); - - var nodes = await _serverNodeService.GetAllNodesAsync(); - Assert.IsNotNull(nodes); - - Assert.AreEqual(1, nodes.Count); - } - - [TestMethod()] - public async Task GetAsyncTest() - { - this.ClearData(); - - var source = new ServerNode(); - source.Id = "1"; - source.CreateTime = DateTime.Now; - source.LastEchoTime = DateTime.Now; - source.Remark = "2"; - source.Status = NodeStatus.Offline; - var result = await _serverNodeService.AddAsync(source); - Assert.IsTrue(result); - - var node = await _serverNodeService.GetAsync(source.Id); - Assert.IsNotNull(node); - - Assert.AreEqual(source.Id, node.Id); - Assert.AreEqual(source.CreateTime.ToString("yyyyMMddHHmmss"), node.CreateTime.ToString("yyyyMMddHHmmss")); - Assert.AreEqual(source.LastEchoTime.Value.ToString("yyyyMMddHHmmss"), node.LastEchoTime.Value.ToString("yyyyMMddHHmmss")); - Assert.AreEqual(source.Remark, node.Remark); - Assert.AreEqual(source.Status, node.Status); - } - - [TestMethod()] - public async Task UpdateAsyncTest() - { - this.ClearData(); - - var source = new ServerNode(); - source.Id = "1"; - source.CreateTime = DateTime.Now; - source.LastEchoTime = DateTime.Now; - source.Remark = "2"; - source.Status = NodeStatus.Offline; - var result = await _serverNodeService.AddAsync(source); - Assert.IsTrue(result); - - source.CreateTime = DateTime.Now; - source.LastEchoTime = DateTime.Now; - source.Remark = "3"; - source.Status = NodeStatus.Online; - var result1 = await _serverNodeService.UpdateAsync(source); - Assert.IsTrue(result); - - var node = await _serverNodeService.GetAsync(source.Id); - Assert.IsNotNull(node); - - Assert.AreEqual(source.Id, node.Id); - Assert.AreEqual(source.CreateTime.ToString("yyyyMMddHHmmss"), node.CreateTime.ToString("yyyyMMddHHmmss")); - Assert.AreEqual(source.LastEchoTime.Value.ToString("yyyyMMddHHmmss"), node.LastEchoTime.Value.ToString("yyyyMMddHHmmss")); - Assert.AreEqual(source.Remark, node.Remark); - Assert.AreEqual(source.Status, node.Status); - } + { "db:provider", "sqlite" }, + { "db:conn", "Data Source=agile_config.db" } + }); + } + + [TestInitialize] + public async Task TestInitialize() + { + await NewGlobalSp(); + + _serviceScope = GlobalServiceProvider.CreateScope(); + _serviceProvider = _serviceScope.ServiceProvider; + + var systeminitializationService = _serviceProvider.GetService(); + systeminitializationService.TryInitDefaultEnvironment(); //初始化环境 DEV TEST STAGE PROD + systeminitializationService.TryInitJwtSecret(); //初始化 jwt secret + + _serverNodeService = _serviceProvider.GetService(); + + Console.WriteLine($"IServerNodeService type is {_serverNodeService.GetType().FullName}"); + + Console.WriteLine("Run TestInitialize"); + } + + private async Task NewGlobalSp() + { + Console.WriteLine("Try get configration data"); + var dict = await GetConfigurationData(); + + foreach (var item in dict) Console.WriteLine($"key: {item.Key} value: {item.Value}"); + + var config = new ConfigurationBuilder() + .AddInMemoryCollection(dict) + .Build(); + Console.WriteLine("Config list"); + foreach (var item in config.AsEnumerable()) Console.WriteLine($"key: {item.Key} value: {item.Value}"); + + var cache = new Mock(); + IServiceCollection services = new ServiceCollection(); + services.AddScoped(_ => cache.Object); + services.AddLogging(); + services.AddSingleton(config); + services.AddDbConfigInfoFactory(); + services.AddFreeSqlFactory(); + services.AddRepositories(); + services.AddBusinessServices(); + + GlobalServiceProvider = services.BuildServiceProvider(); + } + + [TestCleanup] + public void TestCleanup() + { + _serverNodeService.Dispose(); + _serviceScope.Dispose(); + } + + [TestMethod] + public async Task AddAsyncTest() + { + ClearData(); + + var source = new ServerNode(); + source.Id = "1"; + source.CreateTime = DateTime.Now; + source.LastEchoTime = DateTime.Now; + source.Remark = "2"; + source.Status = NodeStatus.Offline; + + var result = await _serverNodeService.AddAsync(source); + Assert.IsTrue(result); + + var node = await _serverNodeService.GetAsync("1"); + Assert.IsNotNull(node); + + Assert.AreEqual(source.Id, node.Id); + Assert.AreEqual(source.CreateTime.ToString("yyyyMMddHHmmss"), node.CreateTime.ToString("yyyyMMddHHmmss")); + Assert.AreEqual(source.LastEchoTime.Value.ToString("yyyyMMddHHmmss"), + node.LastEchoTime.Value.ToString("yyyyMMddHHmmss")); + Assert.AreEqual(source.Remark, node.Remark); + Assert.AreEqual(source.Status, node.Status); + } + + [TestMethod] + public async Task DeleteAsyncTest() + { + ClearData(); + + var source = new ServerNode(); + source.Id = "1"; + source.CreateTime = DateTime.Now; + source.LastEchoTime = DateTime.Now; + source.Remark = "2"; + source.Status = NodeStatus.Offline; + + var result = await _serverNodeService.AddAsync(source); + Assert.IsTrue(result); + + var result1 = await _serverNodeService.DeleteAsync(source); + Assert.IsTrue(result1); + + var node = await _serverNodeService.GetAsync("1"); + + Assert.IsNull(node); + } + + [TestMethod] + public async Task DeleteAsyncTest1() + { + ClearData(); + + var source = new ServerNode(); + source.Id = "1"; + source.CreateTime = DateTime.Now; + source.LastEchoTime = DateTime.Now; + source.Remark = "2"; + source.Status = NodeStatus.Offline; + + var result = await _serverNodeService.AddAsync(source); + Assert.IsTrue(result); + + var result1 = await _serverNodeService.DeleteAsync(source.Id); + Assert.IsTrue(result1); + + var node = await _serverNodeService.GetAsync("1"); + + Assert.IsNull(node); + } + + [TestMethod] + public async Task GetAllNodesAsyncTest() + { + ClearData(); + + var source = new ServerNode(); + source.Id = "1"; + source.CreateTime = DateTime.Now; + source.LastEchoTime = DateTime.Now; + source.Remark = "2"; + source.Status = NodeStatus.Offline; + + var result = await _serverNodeService.AddAsync(source); + Assert.IsTrue(result); + + var nodes = await _serverNodeService.GetAllNodesAsync(); + Assert.IsNotNull(nodes); + + Assert.AreEqual(1, nodes.Count); + } + + [TestMethod] + public async Task GetAsyncTest() + { + ClearData(); + + var source = new ServerNode(); + source.Id = "1"; + source.CreateTime = DateTime.Now; + source.LastEchoTime = DateTime.Now; + source.Remark = "2"; + source.Status = NodeStatus.Offline; + var result = await _serverNodeService.AddAsync(source); + Assert.IsTrue(result); + + var node = await _serverNodeService.GetAsync(source.Id); + Assert.IsNotNull(node); + + Assert.AreEqual(source.Id, node.Id); + Assert.AreEqual(source.CreateTime.ToString("yyyyMMddHHmmss"), node.CreateTime.ToString("yyyyMMddHHmmss")); + Assert.AreEqual(source.LastEchoTime.Value.ToString("yyyyMMddHHmmss"), + node.LastEchoTime.Value.ToString("yyyyMMddHHmmss")); + Assert.AreEqual(source.Remark, node.Remark); + Assert.AreEqual(source.Status, node.Status); + } + + [TestMethod] + public async Task UpdateAsyncTest() + { + ClearData(); + + var source = new ServerNode(); + source.Id = "1"; + source.CreateTime = DateTime.Now; + source.LastEchoTime = DateTime.Now; + source.Remark = "2"; + source.Status = NodeStatus.Offline; + var result = await _serverNodeService.AddAsync(source); + Assert.IsTrue(result); + + source.CreateTime = DateTime.Now; + source.LastEchoTime = DateTime.Now; + source.Remark = "3"; + source.Status = NodeStatus.Online; + var result1 = await _serverNodeService.UpdateAsync(source); + Assert.IsTrue(result); + + var node = await _serverNodeService.GetAsync(source.Id); + Assert.IsNotNull(node); + + Assert.AreEqual(source.Id, node.Id); + Assert.AreEqual(source.CreateTime.ToString("yyyyMMddHHmmss"), node.CreateTime.ToString("yyyyMMddHHmmss")); + Assert.AreEqual(source.LastEchoTime.Value.ToString("yyyyMMddHHmmss"), + node.LastEchoTime.Value.ToString("yyyyMMddHHmmss")); + Assert.AreEqual(source.Remark, node.Remark); + Assert.AreEqual(source.Status, node.Status); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/sqlite/SettingServiceTests.cs b/test/AgileConfig.Server.ServiceTests/sqlite/SettingServiceTests.cs index 97ee9a24..edf95a69 100644 --- a/test/AgileConfig.Server.ServiceTests/sqlite/SettingServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/sqlite/SettingServiceTests.cs @@ -1,219 +1,210 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using System; using System.Collections.Generic; -using AgileConfig.Server.Data.Entity; using System.Threading.Tasks; +using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Data.Freesql; +using AgileConfig.Server.Data.Repository.Selector; using AgileConfig.Server.IService; -using Microsoft.Extensions.DependencyInjection; using AgileConfig.Server.Service; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; -using AgileConfig.Server.Common; -using AgileConfig.Server.Data.Repository.Selector; -using AgileConfig.Server.Data.Freesql; -using AgileConfig.Server.Data.Abstraction; -namespace AgileConfig.Server.ServiceTests.sqlite +namespace AgileConfig.Server.ServiceTests.sqlite; + +[TestClass] +public class SettingServiceTests : BasicTestService { - [TestClass()] - public class SettingServiceTests : BasicTestService + private IServiceProvider _serviceProvider; + private IServiceScope _serviceScope; + private ISettingService _settingService; + + public override Task> GetConfigurationData() { - IServiceProvider _serviceProvider = null; - IServiceScope _serviceScope = null; - ISettingService _settingService = null; - - public override Task> GetConfigurationData() - { - return - Task.FromResult( + return + Task.FromResult( new Dictionary { - {"db:provider","sqlite" }, - {"db:conn","Data Source=agile_config.db" } - }); - } - - [TestInitialize] - public async Task TestInitialize() - { - await NewGlobalSp(); - _serviceScope = GlobalServiceProvider.CreateScope(); - _serviceProvider = _serviceScope.ServiceProvider; - - ClearData(); - - var systeminitializationService = _serviceProvider.GetService(); - systeminitializationService.TryInitDefaultEnvironment();//初始化环境 DEV TEST STAGE PROD - systeminitializationService.TryInitJwtSecret();//初始化 jwt secret - - _settingService = _serviceProvider.GetService(); - - Console.WriteLine("Run TestInitialize"); - } - - private async Task NewGlobalSp() - { - Console.WriteLine("Try get configration data"); - var dict = await GetConfigurationData(); - - foreach (var item in dict) - { - Console.WriteLine($"key: {item.Key} value: {item.Value}"); - } - - var config = new ConfigurationBuilder() - .AddInMemoryCollection(dict) - .Build(); - Console.WriteLine("Config list"); - foreach (var item in config.AsEnumerable()) - { - Console.WriteLine($"key: {item.Key} value: {item.Value}"); - } - - var cache = new Mock(); - IServiceCollection services = new ServiceCollection(); - services.AddScoped(_ => cache.Object); - services.AddSingleton(config); - services.AddDbConfigInfoFactory(); - services.AddFreeSqlFactory(); - services.AddLogging(); - services.AddRepositories(); - services.AddBusinessServices(); - - this.GlobalServiceProvider = services.BuildServiceProvider(); - } - - [TestCleanup] - public void TestCleanup() - { - _settingService.Dispose(); - _serviceScope.Dispose(); - } - - [TestMethod()] - public async Task AddAsyncTest() - { - var id = Guid.NewGuid().ToString(); - var source = new Setting(); - source.Id = id; - source.Value = "123"; - source.CreateTime = DateTime.Now; - var result = await _settingService.AddAsync(source); - Assert.IsTrue(result); - - var setting = await _settingService.GetAsync(source.Id); - - Assert.IsNotNull(setting); - - Assert.AreEqual(source.Id, setting.Id); - Assert.AreEqual(source.Value, setting.Value); - } - - [TestMethod()] - public async Task DeleteAsyncTest() - { - var id = Guid.NewGuid().ToString(); - var source = new Setting(); - source.Id = id; - source.Value = "123"; - source.CreateTime = DateTime.Now; - var result = await _settingService.AddAsync(source); - Assert.IsTrue(result); - - result = await _settingService.DeleteAsync(source); - Assert.IsTrue(result); - - var setting = await _settingService.GetAsync(source.Id); - - Assert.IsNull(setting); - } - - [TestMethod()] - public async Task DeleteAsyncTest1() - { - var id = Guid.NewGuid().ToString(); - var source = new Setting(); - source.Id = id; - source.Value = "123"; - source.CreateTime = DateTime.Now; - var result = await _settingService.AddAsync(source); - Assert.IsTrue(result); - - result = await _settingService.DeleteAsync(id); - Assert.IsTrue(result); - - var setting = await _settingService.GetAsync(source.Id); - - Assert.IsNull(setting); - } - - [TestMethod()] - public async Task GetAsyncTest() - { - var id = Guid.NewGuid().ToString(); - var source = new Setting(); - source.Id = id; - source.Value = "123"; - source.CreateTime = DateTime.Now; - var result = await _settingService.AddAsync(source); - Assert.IsTrue(result); - - var setting = await _settingService.GetAsync(id); - - Assert.IsNotNull(setting); - - Assert.AreEqual(source.Id, setting.Id); - Assert.AreEqual(source.Value, setting.Value); - } - - [TestMethod()] - public async Task GetAllSettingsAsyncTest() - { - this.ClearData(); - - var id = Guid.NewGuid().ToString(); - var source = new Setting(); - source.Id = id; - source.Value = "123"; - source.CreateTime = DateTime.Now; - var result = await _settingService.AddAsync(source); - Assert.IsTrue(result); - var id1 = Guid.NewGuid().ToString(); - var source1 = new Setting(); - source1.Id = id1; - source1.Value = "123"; - source1.CreateTime = DateTime.Now; - var result1 = await _settingService.AddAsync(source1); - Assert.IsTrue(result1); - - var settings = await _settingService.GetAllSettingsAsync(); - - Assert.IsNotNull(settings); - } - - [TestMethod()] - public async Task UpdateAsyncTest() - { - var id = Guid.NewGuid().ToString(); - var source = new Setting(); - source.Id = id; - source.Value = "123"; - source.CreateTime = DateTime.Now; - var result = await _settingService.AddAsync(source); - Assert.IsTrue(result); - - source.CreateTime = DateTime.Now; - source.Value = "321"; - var result1 = await _settingService.UpdateAsync(source); - Assert.IsTrue(result1); - - var setting = await _settingService.GetAsync(id); - Assert.IsNotNull(setting); - - Assert.AreEqual(source.Id, setting.Id); - Assert.AreEqual(source.Value, setting.Value); - } + { "db:provider", "sqlite" }, + { "db:conn", "Data Source=agile_config.db" } + }); + } + + [TestInitialize] + public async Task TestInitialize() + { + await NewGlobalSp(); + _serviceScope = GlobalServiceProvider.CreateScope(); + _serviceProvider = _serviceScope.ServiceProvider; + + ClearData(); + + var systeminitializationService = _serviceProvider.GetService(); + systeminitializationService.TryInitDefaultEnvironment(); //初始化环境 DEV TEST STAGE PROD + systeminitializationService.TryInitJwtSecret(); //初始化 jwt secret + + _settingService = _serviceProvider.GetService(); + + Console.WriteLine("Run TestInitialize"); + } + + private async Task NewGlobalSp() + { + Console.WriteLine("Try get configration data"); + var dict = await GetConfigurationData(); + + foreach (var item in dict) Console.WriteLine($"key: {item.Key} value: {item.Value}"); + + var config = new ConfigurationBuilder() + .AddInMemoryCollection(dict) + .Build(); + Console.WriteLine("Config list"); + foreach (var item in config.AsEnumerable()) Console.WriteLine($"key: {item.Key} value: {item.Value}"); + + var cache = new Mock(); + IServiceCollection services = new ServiceCollection(); + services.AddScoped(_ => cache.Object); + services.AddSingleton(config); + services.AddDbConfigInfoFactory(); + services.AddFreeSqlFactory(); + services.AddLogging(); + services.AddRepositories(); + services.AddBusinessServices(); + + GlobalServiceProvider = services.BuildServiceProvider(); + } + + [TestCleanup] + public void TestCleanup() + { + _settingService.Dispose(); + _serviceScope.Dispose(); + } + + [TestMethod] + public async Task AddAsyncTest() + { + var id = Guid.NewGuid().ToString(); + var source = new Setting(); + source.Id = id; + source.Value = "123"; + source.CreateTime = DateTime.Now; + var result = await _settingService.AddAsync(source); + Assert.IsTrue(result); + + var setting = await _settingService.GetAsync(source.Id); + + Assert.IsNotNull(setting); + + Assert.AreEqual(source.Id, setting.Id); + Assert.AreEqual(source.Value, setting.Value); + } + + [TestMethod] + public async Task DeleteAsyncTest() + { + var id = Guid.NewGuid().ToString(); + var source = new Setting(); + source.Id = id; + source.Value = "123"; + source.CreateTime = DateTime.Now; + var result = await _settingService.AddAsync(source); + Assert.IsTrue(result); + + result = await _settingService.DeleteAsync(source); + Assert.IsTrue(result); + + var setting = await _settingService.GetAsync(source.Id); + + Assert.IsNull(setting); + } + + [TestMethod] + public async Task DeleteAsyncTest1() + { + var id = Guid.NewGuid().ToString(); + var source = new Setting(); + source.Id = id; + source.Value = "123"; + source.CreateTime = DateTime.Now; + var result = await _settingService.AddAsync(source); + Assert.IsTrue(result); + + result = await _settingService.DeleteAsync(id); + Assert.IsTrue(result); + var setting = await _settingService.GetAsync(source.Id); + + Assert.IsNull(setting); + } + + [TestMethod] + public async Task GetAsyncTest() + { + var id = Guid.NewGuid().ToString(); + var source = new Setting(); + source.Id = id; + source.Value = "123"; + source.CreateTime = DateTime.Now; + var result = await _settingService.AddAsync(source); + Assert.IsTrue(result); + + var setting = await _settingService.GetAsync(id); + + Assert.IsNotNull(setting); + + Assert.AreEqual(source.Id, setting.Id); + Assert.AreEqual(source.Value, setting.Value); + } + + [TestMethod] + public async Task GetAllSettingsAsyncTest() + { + ClearData(); + + var id = Guid.NewGuid().ToString(); + var source = new Setting(); + source.Id = id; + source.Value = "123"; + source.CreateTime = DateTime.Now; + var result = await _settingService.AddAsync(source); + Assert.IsTrue(result); + var id1 = Guid.NewGuid().ToString(); + var source1 = new Setting(); + source1.Id = id1; + source1.Value = "123"; + source1.CreateTime = DateTime.Now; + var result1 = await _settingService.AddAsync(source1); + Assert.IsTrue(result1); + + var settings = await _settingService.GetAllSettingsAsync(); + + Assert.IsNotNull(settings); + } + + [TestMethod] + public async Task UpdateAsyncTest() + { + var id = Guid.NewGuid().ToString(); + var source = new Setting(); + source.Id = id; + source.Value = "123"; + source.CreateTime = DateTime.Now; + var result = await _settingService.AddAsync(source); + Assert.IsTrue(result); + + source.CreateTime = DateTime.Now; + source.Value = "321"; + var result1 = await _settingService.UpdateAsync(source); + Assert.IsTrue(result1); + + var setting = await _settingService.GetAsync(id); + Assert.IsNotNull(setting); + + Assert.AreEqual(source.Id, setting.Id); + Assert.AreEqual(source.Value, setting.Value); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/sqlite/SysLogServiceTests.cs b/test/AgileConfig.Server.ServiceTests/sqlite/SysLogServiceTests.cs index fa6357ea..2553b695 100644 --- a/test/AgileConfig.Server.ServiceTests/sqlite/SysLogServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/sqlite/SysLogServiceTests.cs @@ -1,234 +1,232 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using AgileConfig.Server.Service; -using System; +using System; using System.Collections.Generic; -using AgileConfig.Server.Data.Entity; using System.Threading.Tasks; -using AgileConfig.Server.IService; -using Microsoft.Extensions.DependencyInjection; -using MongoDB.Driver.Linq; using AgileConfig.Server.Data.Abstraction; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.Data.Freesql; +using AgileConfig.Server.Data.Repository.Selector; +using AgileConfig.Server.IService; +using AgileConfig.Server.Service; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; -using AgileConfig.Server.Common; -using AgileConfig.Server.Data.Repository.Selector; -using AgileConfig.Server.Data.Freesql; -namespace AgileConfig.Server.ServiceTests.sqlite +namespace AgileConfig.Server.ServiceTests.sqlite; + +[TestClass] +public class SysLogServiceTests : BasicTestService { - [TestClass()] - public class SysLogServiceTests: BasicTestService - { - IServiceProvider _serviceProvider = null; - IServiceScope _serviceScope = null; - ISysLogService _syslogservice = null; + private IServiceProvider _serviceProvider; + private IServiceScope _serviceScope; + private ISysLogService _syslogservice; - public override Task> GetConfigurationData() - { - return - Task.FromResult( + public override Task> GetConfigurationData() + { + return + Task.FromResult( new Dictionary { - {"db:provider","sqlite" }, - {"db:conn","Data Source=agile_config.db" } - }); - } + { "db:provider", "sqlite" }, + { "db:conn", "Data Source=agile_config.db" } + }); + } + + [TestInitialize] + public async Task TestInitialize() + { + await NewGlobalSp(); + _serviceScope = GlobalServiceProvider.CreateScope(); + _serviceProvider = _serviceScope.ServiceProvider; + + ClearData(); + + var systeminitializationService = _serviceProvider.GetService(); + systeminitializationService.TryInitDefaultEnvironment(); //初始化环境 DEV TEST STAGE PROD + systeminitializationService.TryInitJwtSecret(); //初始化 jwt secret + + _syslogservice = _serviceProvider.GetService(); - [TestInitialize] - public async Task TestInitialize() + Console.WriteLine("Run TestInitialize"); + } + + private async Task NewGlobalSp() + { + Console.WriteLine("Try get configration data"); + var dict = await GetConfigurationData(); + + foreach (var item in dict) Console.WriteLine($"key: {item.Key} value: {item.Value}"); + + var config = new ConfigurationBuilder() + .AddInMemoryCollection(dict) + .Build(); + Console.WriteLine("Config list"); + foreach (var item in config.AsEnumerable()) Console.WriteLine($"key: {item.Key} value: {item.Value}"); + + var cache = new Mock(); + IServiceCollection services = new ServiceCollection(); + services.AddLogging(); + services.AddScoped(_ => cache.Object); + services.AddSingleton(config); + services.AddDbConfigInfoFactory(); + services.AddFreeSqlFactory(); + services.AddLogging(); + services.AddRepositories(); + services.AddBusinessServices(); + + GlobalServiceProvider = services.BuildServiceProvider(); + } + + [TestCleanup] + public void TestCleanup() + { + _syslogservice.Dispose(); + _serviceScope.Dispose(); + } + + [TestMethod] + public async Task AddSysLogAsyncTest() + { + var source = new SysLog { - await NewGlobalSp(); - _serviceScope = GlobalServiceProvider.CreateScope(); - _serviceProvider = _serviceScope.ServiceProvider; + AppId = "001", + LogType = SysLogType.Normal, + LogTime = DateTime.Now, + LogText = "123" + }; - ClearData(); + var result = await _syslogservice.AddSysLogAsync(source); + Assert.IsTrue(result); - var systeminitializationService = _serviceProvider.GetService(); - systeminitializationService.TryInitDefaultEnvironment();//初始化环境 DEV TEST STAGE PROD - systeminitializationService.TryInitJwtSecret();//初始化 jwt secret + var log = await _serviceProvider.GetService().GetAsync(source.Id); - _syslogservice = _serviceProvider.GetService(); + Assert.IsNotNull(log); + + Assert.AreEqual(source.Id, log.Id); + Assert.AreEqual(source.AppId, log.AppId); + Assert.AreEqual(source.LogType, log.LogType); + Assert.AreEqual(source.LogTime.Value.ToString("yyyyMMddHHmmss"), log.LogTime.Value.ToString("yyyyMMddHHmmss")); + Assert.AreEqual(source.LogText, log.LogText); + } - Console.WriteLine("Run TestInitialize"); - } - private async Task NewGlobalSp() + [TestMethod] + public async Task AddRangeAsyncTest() + { + var source = new SysLog { - Console.WriteLine("Try get configration data"); - var dict = await GetConfigurationData(); - - foreach (var item in dict) - { - Console.WriteLine($"key: {item.Key} value: {item.Value}"); - } - - var config = new ConfigurationBuilder() - .AddInMemoryCollection(dict) - .Build(); - Console.WriteLine("Config list"); - foreach (var item in config.AsEnumerable()) - { - Console.WriteLine($"key: {item.Key} value: {item.Value}"); - } - - var cache = new Mock(); - IServiceCollection services = new ServiceCollection(); - services.AddLogging(); - services.AddScoped(_ => cache.Object); - services.AddSingleton(config); - services.AddDbConfigInfoFactory(); - services.AddFreeSqlFactory(); - services.AddLogging(); - services.AddRepositories(); - services.AddBusinessServices(); - - this.GlobalServiceProvider = services.BuildServiceProvider(); - } - - [TestCleanup] - public void TestCleanup() + AppId = "001", + LogType = SysLogType.Normal, + LogTime = DateTime.Now, + LogText = "123" + }; + var source1 = new SysLog { - _syslogservice.Dispose(); - _serviceScope.Dispose(); - } - - [TestMethod()] - public async Task AddSysLogAsyncTest() + AppId = "002", + LogType = SysLogType.Warn, + LogTime = DateTime.Now, + LogText = "124" + }; + var result = await _syslogservice.AddRangeAsync(new List { - var source = new SysLog - { - AppId = "001", - LogType = SysLogType.Normal, - LogTime = DateTime.Now, - LogText = "123" - }; + source, source1 + }); + Assert.IsTrue(result); + + var log = await _serviceProvider.GetService().GetAsync(source.Id); + + Assert.IsNotNull(log); + Assert.AreEqual(source.Id, log.Id); + Assert.AreEqual(source.AppId, log.AppId); + Assert.AreEqual(source.LogType, log.LogType); + Assert.AreEqual(source.LogTime.Value.ToString("yyyyMMddHHmmss"), log.LogTime.Value.ToString("yyyyMMddHHmmss")); + Assert.AreEqual(source.LogText, log.LogText); + + var log1 = await _serviceProvider.GetService().GetAsync(source1.Id); + + Assert.IsNotNull(log1); + Assert.AreEqual(source1.Id, log1.Id); + Assert.AreEqual(source1.AppId, log1.AppId); + Assert.AreEqual(source1.LogType, log1.LogType); + Assert.AreEqual(source1.LogTime.Value.ToString("yyyyMMddHHmmss"), + log1.LogTime.Value.ToString("yyyyMMddHHmmss")); + Assert.AreEqual(source1.LogText, log1.LogText); + } - var result = await _syslogservice.AddSysLogAsync(source); - Assert.IsTrue(result); - var log = await _serviceProvider.GetService().GetAsync(source.Id); + [TestMethod] + public async Task CountTest() + { + ClearData(); - Assert.IsNotNull(log); + var source = new SysLog + { + Id = "1", + AppId = "001", + LogType = SysLogType.Normal, + LogTime = DateTime.Now, + LogText = "123" + }; + var source1 = new SysLog + { + Id = "2", + AppId = "002", + LogType = SysLogType.Warn, + LogTime = DateTime.Now, + LogText = "124" + }; + var result = await _syslogservice.AddRangeAsync(new List + { + source, source1 + }); + Assert.IsTrue(result); - Assert.AreEqual(source.Id, log.Id); - Assert.AreEqual(source.AppId, log.AppId); - Assert.AreEqual(source.LogType, log.LogType); - Assert.AreEqual(source.LogTime.Value.ToString("yyyyMMddHHmmss"), log.LogTime.Value.ToString("yyyyMMddHHmmss")); - Assert.AreEqual(source.LogText, log.LogText); - } + var count = await _syslogservice.Count("001", SysLogType.Normal, DateTime.Now.AddDays(-1), + DateTime.Now.AddDays(1)); + Assert.AreEqual(1, count); + var count1 = + await _syslogservice.Count("002", SysLogType.Warn, DateTime.Now.AddDays(-1), DateTime.Now.AddDays(-1)); + Assert.AreEqual(0, count1); + } - [TestMethod()] - public async Task AddRangeAsyncTest() - { - var source = new SysLog - { - AppId = "001", - LogType = SysLogType.Normal, - LogTime = DateTime.Now, - LogText = "123" - }; - var source1 = new SysLog - { - AppId = "002", - LogType = SysLogType.Warn, - LogTime = DateTime.Now, - LogText = "124" - }; - var result = await _syslogservice.AddRangeAsync(new List { - source, source1 - }); - Assert.IsTrue(result); - - var log = await _serviceProvider.GetService().GetAsync(source.Id); - - Assert.IsNotNull(log); - Assert.AreEqual(source.Id, log.Id); - Assert.AreEqual(source.AppId, log.AppId); - Assert.AreEqual(source.LogType, log.LogType); - Assert.AreEqual(source.LogTime.Value.ToString("yyyyMMddHHmmss"), log.LogTime.Value.ToString("yyyyMMddHHmmss")); - Assert.AreEqual(source.LogText, log.LogText); - - var log1 = await _serviceProvider.GetService().GetAsync(source1.Id); - - Assert.IsNotNull(log1); - Assert.AreEqual(source1.Id, log1.Id); - Assert.AreEqual(source1.AppId, log1.AppId); - Assert.AreEqual(source1.LogType, log1.LogType); - Assert.AreEqual(source1.LogTime.Value.ToString("yyyyMMddHHmmss"), log1.LogTime.Value.ToString("yyyyMMddHHmmss")); - Assert.AreEqual(source1.LogText, log1.LogText); - } - - - [TestMethod()] - public async Task CountTest() + [TestMethod] + public async Task SearchPageTest() + { + ClearData(); + + var source = new SysLog { - this.ClearData(); - - var source = new SysLog - { - Id= "1", - AppId = "001", - LogType = SysLogType.Normal, - LogTime = DateTime.Now, - LogText = "123" - }; - var source1 = new SysLog - { - Id = "2", - AppId = "002", - LogType = SysLogType.Warn, - LogTime = DateTime.Now, - LogText = "124" - }; - var result = await _syslogservice.AddRangeAsync(new List { - source, source1 - }); - Assert.IsTrue(result); - - var count = await _syslogservice.Count("001", SysLogType.Normal, DateTime.Now.AddDays(-1), DateTime.Now.AddDays(1)); - Assert.AreEqual(1, count); - - var count1 = await _syslogservice.Count("002", SysLogType.Warn, DateTime.Now.AddDays(-1), DateTime.Now.AddDays(-1)); - Assert.AreEqual(0, count1); - } - - [TestMethod()] - public async Task SearchPageTest() + AppId = "001", + LogType = SysLogType.Normal, + LogTime = DateTime.Now, + LogText = "123" + }; + var source1 = new SysLog { - this.ClearData(); - - var source = new SysLog - { - AppId = "001", - LogType = SysLogType.Normal, - LogTime = DateTime.Now, - LogText = "123" - }; - var source1 = new SysLog - { - AppId = "002", - LogType = SysLogType.Warn, - LogTime = DateTime.Now, - LogText = "124" - }; - var result = await _syslogservice.AddSysLogAsync(source); - Assert.IsTrue(result); - result = await _syslogservice.AddSysLogAsync(source1); - Assert.IsTrue(result); - - var log = await _serviceProvider.GetService().GetAsync(source.Id); - Assert.IsNotNull(log); - - var log1 = await _serviceProvider.GetService().GetAsync(source1.Id); - Assert.IsNotNull(log1); - - var page = await _syslogservice.SearchPage("001", SysLogType.Normal, DateTime.Now.AddDays(-1), DateTime.Now.AddDays(1), 1, 1); - Assert.AreEqual(1, page.Count); - - var page1 = await _syslogservice.SearchPage("002", SysLogType.Warn, DateTime.Now.AddDays(-1), DateTime.Now.AddDays(-1), 1, 1); - Assert.AreEqual(0, page1.Count); - } + AppId = "002", + LogType = SysLogType.Warn, + LogTime = DateTime.Now, + LogText = "124" + }; + var result = await _syslogservice.AddSysLogAsync(source); + Assert.IsTrue(result); + result = await _syslogservice.AddSysLogAsync(source1); + Assert.IsTrue(result); + + var log = await _serviceProvider.GetService().GetAsync(source.Id); + Assert.IsNotNull(log); + + var log1 = await _serviceProvider.GetService().GetAsync(source1.Id); + Assert.IsNotNull(log1); + + var page = await _syslogservice.SearchPage("001", SysLogType.Normal, DateTime.Now.AddDays(-1), + DateTime.Now.AddDays(1), 1, 1); + Assert.AreEqual(1, page.Count); + + var page1 = await _syslogservice.SearchPage("002", SysLogType.Warn, DateTime.Now.AddDays(-1), + DateTime.Now.AddDays(-1), 1, 1); + Assert.AreEqual(0, page1.Count); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/sqlserver/AppServiceTests.cs b/test/AgileConfig.Server.ServiceTests/sqlserver/AppServiceTests.cs index 055f133a..0dc70059 100644 --- a/test/AgileConfig.Server.ServiceTests/sqlserver/AppServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/sqlserver/AppServiceTests.cs @@ -1,22 +1,21 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; +using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.ServiceTests.sqlite; + +namespace AgileConfig.Server.ServiceTests.sqlserver; -namespace AgileConfig.Server.ServiceTests.sqlserver +public class AppServiceTests_sqlserver : AppServiceTests { - public class AppServiceTests_sqlserver : AppServiceTests - { - string conn = "TrustServerCertificate=True;Persist Security Info = False; User ID =dev; Password =dev; Initial Catalog =agile_config_test; Server =."; + private readonly string conn = + "TrustServerCertificate=True;Persist Security Info = False; User ID =dev; Password =dev; Initial Catalog =agile_config_test; Server =."; - public override Task> GetConfigurationData() - { - var dict = new Dictionary(); - dict["db:provider"] = "sqlserver"; - dict["db:conn"] = conn; + public override Task> GetConfigurationData() + { + var dict = new Dictionary(); + dict["db:provider"] = "sqlserver"; + dict["db:conn"] = conn; - return Task.FromResult(dict); - } + return Task.FromResult(dict); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/sqlserver/ConfigServiceTests.cs b/test/AgileConfig.Server.ServiceTests/sqlserver/ConfigServiceTests.cs index 6870e237..7d9a9225 100644 --- a/test/AgileConfig.Server.ServiceTests/sqlserver/ConfigServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/sqlserver/ConfigServiceTests.cs @@ -1,23 +1,22 @@ -using AgileConfig.Server.ServiceTests.sqlite; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.ServiceTests.sqlite; -namespace AgileConfig.Server.ServiceTests.sqlserver +namespace AgileConfig.Server.ServiceTests.sqlserver; + +public class ConfigServiceTests_sqlserver : ConfigServiceTests { - public class ConfigServiceTests_sqlserver: ConfigServiceTests - { - string conn = "TrustServerCertificate=True;Persist Security Info = False; User ID =dev; Password =dev; Initial Catalog =agile_config_test; Server =."; + private readonly string conn = + "TrustServerCertificate=True;Persist Security Info = False; User ID =dev; Password =dev; Initial Catalog =agile_config_test; Server =."; - public override Task> GetConfigurationData() - { - return - Task.FromResult( + public override Task> GetConfigurationData() + { + return + Task.FromResult( new Dictionary { - {"db:provider","sqlserver" }, - {"db:conn",conn } - }); - } + { "db:provider", "sqlserver" }, + { "db:conn", conn } + }); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/sqlserver/ServerNodeServiceTests.cs b/test/AgileConfig.Server.ServiceTests/sqlserver/ServerNodeServiceTests.cs index 1c95cefc..8b1dc307 100644 --- a/test/AgileConfig.Server.ServiceTests/sqlserver/ServerNodeServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/sqlserver/ServerNodeServiceTests.cs @@ -1,24 +1,22 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; +using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.ServiceTests.sqlite; + +namespace AgileConfig.Server.ServiceTests.sqlserver; -namespace AgileConfig.Server.ServiceTests.sqlserver +public class ServerNodeServiceTests_sqlserver : ServerNodeServiceTests { - public class ServerNodeServiceTests_sqlserver: ServerNodeServiceTests - { - string conn = "TrustServerCertificate=True;Persist Security Info = False; User ID =dev; Password =dev; Initial Catalog =agile_config_test; Server =."; + private readonly string conn = + "TrustServerCertificate=True;Persist Security Info = False; User ID =dev; Password =dev; Initial Catalog =agile_config_test; Server =."; - public override Task> GetConfigurationData() - { - return - Task.FromResult( + public override Task> GetConfigurationData() + { + return + Task.FromResult( new Dictionary { - {"db:provider","sqlserver" }, - {"db:conn",conn } - }); - } - + { "db:provider", "sqlserver" }, + { "db:conn", conn } + }); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/sqlserver/SettingServiceTests.cs b/test/AgileConfig.Server.ServiceTests/sqlserver/SettingServiceTests.cs index ac5197f1..3c7ff26b 100644 --- a/test/AgileConfig.Server.ServiceTests/sqlserver/SettingServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/sqlserver/SettingServiceTests.cs @@ -1,23 +1,22 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; +using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.ServiceTests.sqlite; + +namespace AgileConfig.Server.ServiceTests.sqlserver; -namespace AgileConfig.Server.ServiceTests.sqlserver +public class SettingServiceTests_sqlserver : SettingServiceTests { - public class SettingServiceTests_sqlserver : SettingServiceTests - { - string conn = "TrustServerCertificate=True;Persist Security Info = False; User ID =dev; Password =dev; Initial Catalog =agile_config_test; Server =."; + private readonly string conn = + "TrustServerCertificate=True;Persist Security Info = False; User ID =dev; Password =dev; Initial Catalog =agile_config_test; Server =."; - public override Task> GetConfigurationData() - { - return - Task.FromResult( + public override Task> GetConfigurationData() + { + return + Task.FromResult( new Dictionary { - {"db:provider","sqlserver" }, - {"db:conn",conn } - }); - } + { "db:provider", "sqlserver" }, + { "db:conn", conn } + }); } } \ No newline at end of file diff --git a/test/AgileConfig.Server.ServiceTests/sqlserver/SysLogServiceTests.cs b/test/AgileConfig.Server.ServiceTests/sqlserver/SysLogServiceTests.cs index 3bdf55b9..67c63ba3 100644 --- a/test/AgileConfig.Server.ServiceTests/sqlserver/SysLogServiceTests.cs +++ b/test/AgileConfig.Server.ServiceTests/sqlserver/SysLogServiceTests.cs @@ -1,24 +1,22 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Collections.Generic; -using AgileConfig.Server.ServiceTests.sqlite; +using System.Collections.Generic; using System.Threading.Tasks; +using AgileConfig.Server.ServiceTests.sqlite; -namespace AgileConfig.Server.ServiceTests.sqlserver -{ - public class SysLogServiceTests_sqlserver : SysLogServiceTests - { +namespace AgileConfig.Server.ServiceTests.sqlserver; - string conn = "TrustServerCertificate=True;Persist Security Info = False; User ID =dev; Password =dev; Initial Catalog =agile_config_test; Server =."; +public class SysLogServiceTests_sqlserver : SysLogServiceTests +{ + private readonly string conn = + "TrustServerCertificate=True;Persist Security Info = False; User ID =dev; Password =dev; Initial Catalog =agile_config_test; Server =."; - public override Task> GetConfigurationData() - { - return - Task.FromResult( + public override Task> GetConfigurationData() + { + return + Task.FromResult( new Dictionary { - {"db:provider","sqlserver" }, - {"db:conn",conn } - }); - } + { "db:provider", "sqlserver" }, + { "db:conn", conn } + }); } } \ No newline at end of file diff --git a/test/ApiSiteTests/Filters/BasicAuthenticationAttributeTests.cs b/test/ApiSiteTests/Filters/BasicAuthenticationAttributeTests.cs index ffbaa5f3..db33cfa9 100644 --- a/test/ApiSiteTests/Filters/BasicAuthenticationAttributeTests.cs +++ b/test/ApiSiteTests/Filters/BasicAuthenticationAttributeTests.cs @@ -1,71 +1,66 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using AgileConfig.Server.Apisite.Filters; -using System; -using System.Collections.Generic; -using System.Text; -using Microsoft.AspNetCore.Http; -using Moq; -using AgileConfig.Server.IService; +using System.Threading.Tasks; using AgileConfig.Server.Data.Entity; -using System.Threading.Tasks; -using System.Net; +using AgileConfig.Server.IService; using AgileConfig.Server.Service; +using Microsoft.AspNetCore.Http; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace AgileConfig.Server.Apisite.Filters.Tests; -namespace AgileConfig.Server.Apisite.Filters.Tests +[TestClass] +public class BasicAuthenticationAttributeTests { - [TestClass()] - public class BasicAuthenticationAttributeTests + [TestMethod] + public async Task ValidTest() { - [TestMethod()] - public async Task ValidTest() + var service = new Mock(); + App app = null; + service.Setup(s => s.GetAsync("01")).ReturnsAsync(app); + service.Setup(s => s.GetAsync("02")).ReturnsAsync(new App()); + service.Setup(s => s.GetAsync("03")).ReturnsAsync(new App + { + Id = "03", + Secret = "1" + }); + service.Setup(s => s.GetAsync("app01")).ReturnsAsync(new App + { + Id = "app01", + Secret = "11", + Enabled = true + }); + service.Setup(s => s.GetAsync("app02")).ReturnsAsync(new App + { + Id = "app02", + Secret = null, + Enabled = true + }); + service.Setup(s => s.GetAsync("app03")).ReturnsAsync(new App { - var service = new Mock(); - App app = null; - service.Setup(s => s.GetAsync("01")).ReturnsAsync(app); - service.Setup(s => s.GetAsync("02")).ReturnsAsync(new App()); - service.Setup(s => s.GetAsync("03")).ReturnsAsync(new App() { - Id="03", - Secret = "1", - }); - service.Setup(s => s.GetAsync("app01")).ReturnsAsync(new App() - { - Id ="app01", - Secret = "11", - Enabled = true - }) ; - service.Setup(s => s.GetAsync("app02")).ReturnsAsync(new App() - { - Id = "app02", - Secret = null, - Enabled = true - }); - service.Setup(s => s.GetAsync("app03")).ReturnsAsync(new App() - { - Id = "app03", - Secret = "", - Enabled = true - }); - var http = new DefaultHttpContext(); - var filter = new AppBasicAuthenticationAttribute(new AppBasicAuthService(service.Object)); - var result = await filter.Valid(http.Request); - Assert.IsFalse(result); - result = await filter.Valid(http.Request); - Assert.IsFalse(result); - result = await filter.Valid(http.Request); - Assert.IsFalse(result); - http.Request.Headers["Authorization"] = "Basic YXBwMDE6MTEx="; - result = await filter.Valid(http.Request); - Assert.IsFalse(result); - http.Request.Headers["Authorization"] = "Basic YXBwMDE6MTE="; - result = await filter.Valid(http.Request); - Assert.IsTrue(result); + Id = "app03", + Secret = "", + Enabled = true + }); + var http = new DefaultHttpContext(); + var filter = new AppBasicAuthenticationAttribute(new AppBasicAuthService(service.Object)); + var result = await filter.Valid(http.Request); + Assert.IsFalse(result); + result = await filter.Valid(http.Request); + Assert.IsFalse(result); + result = await filter.Valid(http.Request); + Assert.IsFalse(result); + http.Request.Headers["Authorization"] = "Basic YXBwMDE6MTEx="; + result = await filter.Valid(http.Request); + Assert.IsFalse(result); + http.Request.Headers["Authorization"] = "Basic YXBwMDE6MTE="; + result = await filter.Valid(http.Request); + Assert.IsTrue(result); - http.Request.Headers["Authorization"] = "Basic YXBwMDI6"; - result = await filter.Valid(http.Request); - Assert.IsTrue(result); - http.Request.Headers["Authorization"] = "Basic YXBwMDM6"; - result = await filter.Valid(http.Request); - Assert.IsTrue(result); - } + http.Request.Headers["Authorization"] = "Basic YXBwMDI6"; + result = await filter.Valid(http.Request); + Assert.IsTrue(result); + http.Request.Headers["Authorization"] = "Basic YXBwMDM6"; + result = await filter.Valid(http.Request); + Assert.IsTrue(result); } } \ No newline at end of file diff --git a/test/ApiSiteTests/TestApiConfigController.cs b/test/ApiSiteTests/TestApiConfigController.cs index f1b387db..f9b5af89 100644 --- a/test/ApiSiteTests/TestApiConfigController.cs +++ b/test/ApiSiteTests/TestApiConfigController.cs @@ -1,16 +1,16 @@ -using AgileConfig.Server.Apisite.Controllers.api; -using AgileConfig.Server.Data.Entity; -using AgileConfig.Server.IService; -using Microsoft.AspNetCore.Mvc; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; using System.Collections.Generic; using System.Threading.Tasks; -using Microsoft.Extensions.Caching.Memory; +using AgileConfig.Server.Apisite.Controllers; using AgileConfig.Server.Apisite.Controllers.api.Models; -using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Apisite.Metrics; using AgileConfig.Server.Apisite.Models; +using AgileConfig.Server.Common.EventBus; +using AgileConfig.Server.Data.Entity; +using AgileConfig.Server.IService; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; namespace ApiSiteTests; @@ -22,7 +22,7 @@ public async Task TestGet() { App newApp() { - return new App() + return new App { Enabled = true }; @@ -53,6 +53,7 @@ List newConfigs() return list; } + var configService = new Mock(); //configService.Setup(s => s.GetPublishedConfigsAsync("001")) // .ReturnsAsync(newConfigs); @@ -64,14 +65,14 @@ List newConfigs() var eventBus = new Mock(); var meterService = new Mock(); - var ctrl = new ConfigController( + var ctrl = new AgileConfig.Server.Apisite.Controllers.api.ConfigController( configService.Object, appService.Object, memoryCache, meterService.Object, - new AgileConfig.Server.Apisite.Controllers.ConfigController(configService.Object, appService.Object, userSErvice.Object, eventBus.Object) - ); - var act = await ctrl.GetAppConfig("001", new EnvString() { Value = "DEV" }); + new ConfigController(configService.Object, appService.Object, userSErvice.Object, eventBus.Object) + ); + var act = await ctrl.GetAppConfig("001", new EnvString { Value = "DEV" }); Assert.IsNotNull(act); Assert.IsNotNull(act.Value); @@ -80,25 +81,26 @@ List newConfigs() App newApp1() { - return new App() + return new App { Enabled = false }; } + appService = new Mock(); appService.Setup(s => s.GetAsync(It.IsAny())).ReturnsAsync(newApp1); - ctrl = new ConfigController( + ctrl = new AgileConfig.Server.Apisite.Controllers.api.ConfigController( configService.Object, appService.Object, - memoryCache, + memoryCache, meterService.Object, - new AgileConfig.Server.Apisite.Controllers.ConfigController(configService.Object, appService.Object, userSErvice.Object, eventBus.Object) - ); - act = await ctrl.GetAppConfig("001", new EnvString() { Value = "DEV" }); + new ConfigController(configService.Object, appService.Object, userSErvice.Object, eventBus.Object) + ); + act = await ctrl.GetAppConfig("001", new EnvString { Value = "DEV" }); Assert.IsNotNull(act); Assert.IsNull(act.Value); Assert.IsInstanceOfType(act.Result, typeof(NotFoundResult)); } -} +} \ No newline at end of file diff --git a/test/ApiSiteTests/TestAppController.cs b/test/ApiSiteTests/TestAppController.cs index d8281bed..9c07a31a 100644 --- a/test/ApiSiteTests/TestAppController.cs +++ b/test/ApiSiteTests/TestAppController.cs @@ -1,5 +1,8 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Threading.Tasks; using AgileConfig.Server.Apisite.Controllers; -using AgileConfig.Server.Apisite.Controllers.api; using AgileConfig.Server.Apisite.Models; using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Data.Entity; @@ -8,75 +11,67 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text.Json; -using System.Threading.Tasks; -namespace ApiSiteTests +namespace ApiSiteTests; + +[TestClass] +public class TestAppController { - [TestClass] - public class TestAppController + [TestMethod] + public async Task TestAdd() { - [TestMethod] - public async Task TestAdd() - { - CultureInfo.CurrentUICulture = new CultureInfo("en-US", false); + CultureInfo.CurrentUICulture = new CultureInfo("en-US", false); - var appService = new Mock(); - var logService = new Mock(); - var userService = new Mock(); - var permissionService = new Mock(); - var eventBus = new Mock(); + var appService = new Mock(); + var logService = new Mock(); + var userService = new Mock(); + var permissionService = new Mock(); + var eventBus = new Mock(); - var ctl = new AgileConfig.Server.Apisite.Controllers.AppController(appService.Object, permissionService.Object, userService.Object, eventBus.Object); + var ctl = new AppController(appService.Object, permissionService.Object, userService.Object, eventBus.Object); - ctl.ControllerContext.HttpContext = new DefaultHttpContext(); + ctl.ControllerContext.HttpContext = new DefaultHttpContext(); - Assert.ThrowsException( () => { - ctl.Add(null).GetAwaiter().GetResult(); - }); + Assert.ThrowsException(() => { ctl.Add(null).GetAwaiter().GetResult(); }); - appService.Setup(s => s.GetAsync("01")).ReturnsAsync(new App()); - var result = await ctl.Add(new AppVM - { - Id = "01" - }); - Assert.IsNotNull(result); - Assert.IsInstanceOfType(result, typeof(JsonResult)); - var jr = result as JsonResult; - Assert.IsNotNull(jr.Value); - Console.WriteLine(jr.Value.ToString()); - //Assert.IsTrue(jr.Value.ToString().Contains("Ӧ��Id�Ѵ��ڣ�����������")); - App nullApp = null; + appService.Setup(s => s.GetAsync("01")).ReturnsAsync(new App()); + var result = await ctl.Add(new AppVM + { + Id = "01" + }); + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(JsonResult)); + var jr = result as JsonResult; + Assert.IsNotNull(jr.Value); + Console.WriteLine(jr.Value.ToString()); + //Assert.IsTrue(jr.Value.ToString().Contains("Ӧ��Id�Ѵ��ڣ�����������")); + App nullApp = null; - appService.Setup(s => s.GetAsync("02")).ReturnsAsync(nullApp); - appService.Setup(s => s.AddAsync(It.IsAny())).ReturnsAsync(false); - result = await ctl.Add(new AppVM - { - Id = "02" - }); - Assert.IsNotNull(result); - Assert.IsInstanceOfType(result, typeof(JsonResult)); - jr = result as JsonResult; - Assert.IsNotNull(jr.Value); - Console.WriteLine(jr.Value.ToString()); - Assert.IsTrue(jr.Value.ToString().Contains("success = False")); + appService.Setup(s => s.GetAsync("02")).ReturnsAsync(nullApp); + appService.Setup(s => s.AddAsync(It.IsAny())).ReturnsAsync(false); + result = await ctl.Add(new AppVM + { + Id = "02" + }); + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(JsonResult)); + jr = result as JsonResult; + Assert.IsNotNull(jr.Value); + Console.WriteLine(jr.Value.ToString()); + Assert.IsTrue(jr.Value.ToString().Contains("success = False")); - appService.Setup(s => s.AddAsync(It.IsAny())).ReturnsAsync(true); - appService.Setup(s => s.AddAsync(It.IsAny(), It.IsAny>())).ReturnsAsync(true); - Console.WriteLine(CultureInfo.CurrentUICulture); - result = await ctl.Add(new AppVM - { - Id = "02" - }); - Assert.IsNotNull(result); - Assert.IsInstanceOfType(result, typeof(JsonResult)); - jr = result as JsonResult; - Assert.IsNotNull(jr.Value); - Console.WriteLine(jr.Value.ToString()); - Assert.IsTrue(jr.Value.ToString().Contains("success = True")); - } + appService.Setup(s => s.AddAsync(It.IsAny())).ReturnsAsync(true); + appService.Setup(s => s.AddAsync(It.IsAny(), It.IsAny>())).ReturnsAsync(true); + Console.WriteLine(CultureInfo.CurrentUICulture); + result = await ctl.Add(new AppVM + { + Id = "02" + }); + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(JsonResult)); + jr = result as JsonResult; + Assert.IsNotNull(jr.Value); + Console.WriteLine(jr.Value.ToString()); + Assert.IsTrue(jr.Value.ToString().Contains("success = True")); } -} +} \ No newline at end of file diff --git a/test/ApiSiteTests/Websocket/WebsocketCollectionTests.cs b/test/ApiSiteTests/Websocket/WebsocketCollectionTests.cs index 164c18d1..84e23ef0 100644 --- a/test/ApiSiteTests/Websocket/WebsocketCollectionTests.cs +++ b/test/ApiSiteTests/Websocket/WebsocketCollectionTests.cs @@ -1,51 +1,48 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using AgileConfig.Server.Apisite.Websocket; -using System; -using System.Collections.Generic; -using System.Text; +using System; +using System.Net.WebSockets; +using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace AgileConfig.Server.Apisite.Websocket.Tests +namespace AgileConfig.Server.Apisite.Websocket.Tests; + +[TestClass] +public class WebsocketCollectionTests { - [TestClass()] - public class WebsocketCollectionTests + [TestMethod] + public void AddClientTest() { - [TestMethod()] - public void AddClientTest() - { - WebsocketCollection.Instance.Clear(); + WebsocketCollection.Instance.Clear(); - var client = new WebsocketClient(); - client.Id = Guid.NewGuid().ToString(); - WebsocketCollection.Instance.AddClient(client); - WebsocketCollection.Instance.AddClient(client); - Assert.AreEqual(1, WebsocketCollection.Instance.Count); + var client = new WebsocketClient(); + client.Id = Guid.NewGuid().ToString(); + WebsocketCollection.Instance.AddClient(client); + WebsocketCollection.Instance.AddClient(client); + Assert.AreEqual(1, WebsocketCollection.Instance.Count); - var client1 = new WebsocketClient(); - client1.Id = Guid.NewGuid().ToString(); - WebsocketCollection.Instance.AddClient(client1); - WebsocketCollection.Instance.AddClient(client1); - Assert.AreEqual(2, WebsocketCollection.Instance.Count); - } + var client1 = new WebsocketClient(); + client1.Id = Guid.NewGuid().ToString(); + WebsocketCollection.Instance.AddClient(client1); + WebsocketCollection.Instance.AddClient(client1); + Assert.AreEqual(2, WebsocketCollection.Instance.Count); + } - [TestMethod()] - public void RemoveClientTest() - { - WebsocketCollection.Instance.Clear(); + [TestMethod] + public void RemoveClientTest() + { + WebsocketCollection.Instance.Clear(); - var client = new WebsocketClient(); - client.Id = Guid.NewGuid().ToString(); - WebsocketCollection.Instance.AddClient(client); - var client1 = new WebsocketClient(); - client1.Id = Guid.NewGuid().ToString(); - WebsocketCollection.Instance.AddClient(client1); - Assert.AreEqual(2, WebsocketCollection.Instance.Count); + var client = new WebsocketClient(); + client.Id = Guid.NewGuid().ToString(); + WebsocketCollection.Instance.AddClient(client); + var client1 = new WebsocketClient(); + client1.Id = Guid.NewGuid().ToString(); + WebsocketCollection.Instance.AddClient(client1); + Assert.AreEqual(2, WebsocketCollection.Instance.Count); - WebsocketCollection.Instance.RemoveClient(client1, System.Net.WebSockets.WebSocketCloseStatus.Empty, ""); - WebsocketCollection.Instance.RemoveClient(client1, System.Net.WebSockets.WebSocketCloseStatus.Empty, ""); - Assert.AreEqual(1, WebsocketCollection.Instance.Count); - WebsocketCollection.Instance.RemoveClient(client, System.Net.WebSockets.WebSocketCloseStatus.Empty, ""); - WebsocketCollection.Instance.RemoveClient(client, System.Net.WebSockets.WebSocketCloseStatus.Empty, ""); - Assert.AreEqual(0, WebsocketCollection.Instance.Count); - } + WebsocketCollection.Instance.RemoveClient(client1, WebSocketCloseStatus.Empty, ""); + WebsocketCollection.Instance.RemoveClient(client1, WebSocketCloseStatus.Empty, ""); + Assert.AreEqual(1, WebsocketCollection.Instance.Count); + WebsocketCollection.Instance.RemoveClient(client, WebSocketCloseStatus.Empty, ""); + WebsocketCollection.Instance.RemoveClient(client, WebSocketCloseStatus.Empty, ""); + Assert.AreEqual(0, WebsocketCollection.Instance.Count); } } \ No newline at end of file From a203a89fdc20ea3b85d90aec36ce65958c90db54 Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Sat, 22 Nov 2025 23:49:08 +0800 Subject: [PATCH 07/17] Permission control almost done --- .../AgileConfig.Server.Apisite.xml | 6 +- .../Controllers/AppController.cs | 5 +- .../Controllers/RoleController.cs | 29 +-------- .../Controllers/api/Models/ApiAppVM.cs | 11 ++-- .../Filters/PermissionCheckAttribute.cs | 7 ++- .../Models/AppVM.cs | 12 ++-- .../Models/Mapping/ModelMappingExtension.cs | 8 +-- src/AgileConfig.Server.Data.Entity/App.cs | 10 ++-- .../EnsureTables.cs | 7 ++- .../SysInitRepository.cs | 60 ++----------------- .../SysInitRepository.cs | 4 +- .../IPermissionService.cs | 56 ++++++++++++++++- .../PermissionService.cs | 13 +--- .../SystemInitializationService.cs | 14 ++++- .../react-ui-antd/config/routes.ts | 18 +++--- .../react-ui-antd/package-lock.json | 22 ------- .../react-ui-antd/src/layouts/BasicLayout.tsx | 4 +- .../react-ui-antd/src/locales/en-US/pages.ts | 45 +++++++------- .../react-ui-antd/src/locales/zh-CN/pages.ts | 56 ++++++++--------- .../react-ui-antd/src/models/user.ts | 2 +- .../src/pages/Apps/comps/updateForm.tsx | 17 ------ .../react-ui-antd/src/pages/Apps/data.d.ts | 5 +- .../react-ui-antd/src/pages/Apps/index.tsx | 54 ++++------------- .../react-ui-antd/src/pages/User/index.tsx | 31 ---------- .../react-ui-antd/src/utils/authority.ts | 9 ++- 25 files changed, 192 insertions(+), 313 deletions(-) diff --git a/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.xml b/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.xml index 95c42e6e..6cb099c7 100644 --- a/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.xml +++ b/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.xml @@ -182,9 +182,9 @@ 关联的app - + - 管理员 + 创建者 @@ -513,4 +513,4 @@ - \ No newline at end of file + diff --git a/src/AgileConfig.Server.Apisite/Controllers/AppController.cs b/src/AgileConfig.Server.Apisite/Controllers/AppController.cs index 58d4fae6..5097b0d0 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/AppController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/AppController.cs @@ -93,7 +93,6 @@ private async Task AppendInheritancedInfo(List list) appListVm.inheritancedAppNames = appListVm.Inheritanced ? new List() : inheritancedApps.Select(ia => ia.Name).ToList(); - appListVm.AppAdminName = (await _userService.GetUserAsync(appListVm.AppAdmin))?.UserName; if (appListVm.children != null) await AppendInheritancedInfo(appListVm.children); } } @@ -114,6 +113,8 @@ public async Task Add([FromBody] AppVM model) var app = model.ToApp(); app.CreateTime = DateTime.Now; + var creatorId = await this.GetCurrentUserId(_userService); + if (!string.IsNullOrWhiteSpace(creatorId)) app.Creator = creatorId; var inheritanceApps = new List(); if (!model.Inheritanced && model.inheritancedApps != null) @@ -345,4 +346,4 @@ public async Task GetAppGroups() data = groups.OrderBy(x => x) }); } -} \ No newline at end of file +} diff --git a/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs b/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs index 66223edd..d5aa253c 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs @@ -17,33 +17,6 @@ namespace AgileConfig.Server.Apisite.Controllers; [Authorize] public class RoleController : Controller { - private static readonly IReadOnlyList SupportedFunctions = new List - { - Functions.App_Add, - Functions.App_Edit, - Functions.App_Delete, - Functions.App_Auth, - - Functions.Config_Add, - Functions.Config_Edit, - Functions.Config_Delete, - Functions.Config_Publish, - Functions.Config_Offline, - - Functions.Node_Add, - Functions.Node_Delete, - - Functions.Client_Disconnect, - - Functions.User_Add, - Functions.User_Edit, - Functions.User_Delete, - - Functions.Role_Add, - Functions.Role_Edit, - Functions.Role_Delete - }; - private readonly IRoleFunctionRepository _roleFunctionRepository; private readonly IRoleService _roleService; @@ -79,7 +52,7 @@ public IActionResult SupportedPermissions() return Json(new { success = true, - data = SupportedFunctions + data = Functions.GetAllPermissions() }); } diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiAppVM.cs b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiAppVM.cs index d467b656..ed319bfc 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiAppVM.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiAppVM.cs @@ -30,11 +30,6 @@ public class ApiAppVM : IAppModel /// public List InheritancedApps { get; set; } - /// - /// Administrator of the application. - /// - public string AppAdmin { get; set; } - /// /// id /// @@ -47,6 +42,8 @@ public class ApiAppVM : IAppModel public string Group { get; set; } + public string Creator { get; set; } + public DateTime CreateTime { get; set; } } @@ -61,10 +58,10 @@ public static AppVM ToAppVM(this ApiAppVM vm) Id = vm.Id, Name = vm.Name, Secret = vm.Secret, - AppAdmin = vm.AppAdmin, Inheritanced = vm.Inheritanced, + Creator = vm.Creator, Group = vm.Group, Enabled = vm.Enabled.GetValueOrDefault() }; } -} \ No newline at end of file +} diff --git a/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs b/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs index fcdfbb83..996fcb6e 100644 --- a/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs +++ b/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs @@ -235,10 +235,11 @@ public override async Task OnActionExecutionAsync(ActionExecutingContext context } var appId = ""; - if (GetAppIdParamFuncs.TryGetValue(_actionName, out var func)) + var isAppAction = _actionName.StartsWith("App."); + if (!isAppAction && GetAppIdParamFuncs.TryGetValue(_actionName, out var func)) appId = func(context, _permissionService, _configService); - if (!string.IsNullOrEmpty(appId)) + if (!isAppAction && !string.IsNullOrEmpty(appId)) { matchKey = string.Format(AppMatchPatten, appId, _functionKey); if (userFunctions.Contains(matchKey)) @@ -253,4 +254,4 @@ public override async Task OnActionExecutionAsync(ActionExecutingContext context context.Result = new ContentResult(); await base.OnActionExecutionAsync(context, next); } -} \ No newline at end of file +} diff --git a/src/AgileConfig.Server.Apisite/Models/AppVM.cs b/src/AgileConfig.Server.Apisite/Models/AppVM.cs index 01e75333..a8d68bbc 100644 --- a/src/AgileConfig.Server.Apisite/Models/AppVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/AppVM.cs @@ -20,9 +20,7 @@ public class AppVM : IAppModel public List inheritancedAppNames { get; set; } - public string AppAdmin { get; set; } - - public string AppAdminName { get; set; } + public string Creator { get; set; } [Required(ErrorMessage = "应用Id不能为空")] [MaxLength(36, ErrorMessage = "应用Id长度不能超过36位")] @@ -58,9 +56,9 @@ public static App ToApp(this AppVM vm) app.Secret = vm.Secret; app.Enabled = vm.Enabled; app.Type = vm.Inheritanced ? AppType.Inheritance : AppType.PRIVATE; - app.AppAdmin = vm.AppAdmin; app.Group = vm.Group; app.CreateTime = vm.CreateTime; + app.Creator = vm.Creator; return app; } @@ -74,9 +72,9 @@ public static App ToApp(this AppVM vm, App app) app.Secret = vm.Secret; app.Enabled = vm.Enabled; app.Type = vm.Inheritanced ? AppType.Inheritance : AppType.PRIVATE; - app.AppAdmin = vm.AppAdmin; app.Group = vm.Group; if (vm.CreateTime > DateTime.MinValue) app.CreateTime = vm.CreateTime; + if (!string.IsNullOrWhiteSpace(vm.Creator)) app.Creator = vm.Creator; return app; } @@ -93,9 +91,9 @@ public static ApiAppVM ToApiAppVM(this AppVM vm) Inheritanced = vm.Inheritanced, Enabled = vm.Enabled, InheritancedApps = vm.inheritancedApps, - AppAdmin = vm.AppAdmin, Group = vm.Group, + Creator = vm.Creator, CreateTime = vm.CreateTime }; } -} \ No newline at end of file +} diff --git a/src/AgileConfig.Server.Apisite/Models/Mapping/ModelMappingExtension.cs b/src/AgileConfig.Server.Apisite/Models/Mapping/ModelMappingExtension.cs index 0287e24a..381c5c34 100644 --- a/src/AgileConfig.Server.Apisite/Models/Mapping/ModelMappingExtension.cs +++ b/src/AgileConfig.Server.Apisite/Models/Mapping/ModelMappingExtension.cs @@ -22,7 +22,7 @@ public static AppVM ToAppVM(this App app) Secret = app.Secret, Enabled = app.Enabled, Inheritanced = app.Type == AppType.Inheritance, - AppAdmin = app.AppAdmin, + Creator = app.Creator, CreateTime = app.CreateTime }; @@ -43,7 +43,7 @@ public static AppListVM ToAppListVM(this App app) Enabled = app.Enabled, UpdateTime = app.UpdateTime, CreateTime = app.CreateTime, - AppAdmin = app.AppAdmin + Creator = app.Creator }; return vm; @@ -60,8 +60,8 @@ public static ApiAppVM ToApiAppVM(this App vm) Secret = vm.Secret, Inheritanced = vm.Type == AppType.Inheritance, Enabled = vm.Enabled, - AppAdmin = vm.AppAdmin, Group = vm.Group, + Creator = vm.Creator, CreateTime = vm.CreateTime }; } @@ -150,4 +150,4 @@ public static ApiConfigVM ToApiConfigVM(this Config config) return vm; } -} \ No newline at end of file +} diff --git a/src/AgileConfig.Server.Data.Entity/App.cs b/src/AgileConfig.Server.Data.Entity/App.cs index 1310df84..86cba1e6 100644 --- a/src/AgileConfig.Server.Data.Entity/App.cs +++ b/src/AgileConfig.Server.Data.Entity/App.cs @@ -13,6 +13,8 @@ public interface IAppModel string Group { get; set; } + string Creator { get; set; } + DateTime CreateTime { get; set; } } @@ -37,9 +39,6 @@ public class App : IAppModel, IEntity [Column(Name = "type")] public AppType Type { get; set; } - [Column(Name = "app_admin", StringLength = 36)] - public string AppAdmin { get; set; } - [Column(Name = "id", StringLength = 36)] public string Id { get; set; } @@ -49,7 +48,10 @@ public class App : IAppModel, IEntity [Column(Name = "group", StringLength = 50)] public string Group { get; set; } + [Column(Name = "creator", StringLength = 36)] + public string Creator { get; set; } + [Column(Name = "create_time")] [BsonDateTimeOptions(Kind = DateTimeKind.Local)] public DateTime CreateTime { get; set; } -} \ No newline at end of file +} diff --git a/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs b/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs index 33407fc2..33d59007 100644 --- a/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs +++ b/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs @@ -64,10 +64,11 @@ public static void Ensure(IFreeSql instance) { if (!ExistTable(instance)) { - if (instance.Ado.DataType == DataType.Oracle) instance.CodeFirst.IsSyncStructureToUpper = true; - try { + if (instance.Ado.DataType == DataType.Oracle) + instance.CodeFirst.IsSyncStructureToUpper = true; + instance.CodeFirst.SyncStructure(); instance.CodeFirst.SyncStructure(); instance.CodeFirst.SyncStructure(); @@ -92,4 +93,4 @@ public static void Ensure(IFreeSql instance) } } } -} \ No newline at end of file +} diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs index 455fbae5..f48956ab 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs @@ -97,7 +97,7 @@ public bool InitDefaultApp(string appName) CreateTime = DateTime.Now, Enabled = true, Type = AppType.PRIVATE, - AppAdmin = SystemSettings.SuperAdminId + Creator = SystemSettings.SuperAdminId }).ExecuteAffrows(); return true; @@ -106,7 +106,7 @@ public bool InitDefaultApp(string appName) private static void EnsureSystemRoles(IFreeSql sql) { // Super Admin gets all permissions - var superAdminPermissions = GetSuperAdminPermissions(); + var superAdminPermissions = Functions.GetAllPermissions(); EnsureRole(sql, SystemRoleConstants.SuperAdminId, "Super Administrator", superAdminPermissions); EnsureRolePermissions(sql, SystemRoleConstants.SuperAdminId, superAdminPermissions); @@ -121,61 +121,11 @@ private static void EnsureSystemRoles(IFreeSql sql) EnsureRolePermissions(sql, SystemRoleConstants.OperatorId, operatorPermissions); } - private static List GetSuperAdminPermissions() - { - // SuperAdmin has all permissions - return new List - { - // Application permissions - Functions.App_Read, - Functions.App_Add, - Functions.App_Edit, - Functions.App_Delete, - Functions.App_Auth, - - // Configuration permissions - Functions.Confing_Read, - Functions.Config_Add, - Functions.Config_Edit, - Functions.Config_Delete, - Functions.Config_Publish, - Functions.Config_Offline, - - // Node permissions - Functions.Node_Read, - Functions.Node_Add, - Functions.Node_Delete, - - // Client permissions - Functions.Client_Refresh, - Functions.Client_Disconnect, - - // User permissions - Functions.User_Read, - Functions.User_Add, - Functions.User_Edit, - Functions.User_Delete, - - // Role permissions - Functions.Role_Read, - Functions.Role_Add, - Functions.Role_Edit, - Functions.Role_Delete, - - // Service permissions - Functions.Service_Read, - Functions.Service_Add, - Functions.Service_Delete, - - // System permissions - Functions.Log_Read - }; - } private static List GetAdminPermissions() { // Administrator has all permissions same as SuperAdmin - return GetSuperAdminPermissions(); + return Functions.GetAllPermissions(); } private static List GetOperatorPermissions() @@ -191,7 +141,7 @@ private static List GetOperatorPermissions() Functions.App_Auth, // All Configuration permissions - Functions.Confing_Read, + Functions.Config_Read, Functions.Config_Add, Functions.Config_Edit, Functions.Config_Delete, @@ -268,4 +218,4 @@ private static void EnsureRolePermissions(IFreeSql sql, string roleId, List roleFunctionsToRemove.Select(r => r.Id).Contains(rf.Id)) .ExecuteAffrows(); } -} \ No newline at end of file +} diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs index d6e201c9..14eb0fd5 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs @@ -106,7 +106,7 @@ public bool InitDefaultApp(string appName) CreateTime = DateTime.Now, Enabled = true, Type = AppType.PRIVATE, - AppAdmin = SystemSettings.SuperAdminId + Creator = SystemSettings.SuperAdminId }); return true; @@ -142,4 +142,4 @@ private void EnsureRole(string id, string name) _roleAccess.Collection.ReplaceOne(x => x.Id == id, role, new ReplaceOptions { IsUpsert = true }); } } -} \ No newline at end of file +} diff --git a/src/AgileConfig.Server.IService/IPermissionService.cs b/src/AgileConfig.Server.IService/IPermissionService.cs index f397afba..1efb7435 100644 --- a/src/AgileConfig.Server.IService/IPermissionService.cs +++ b/src/AgileConfig.Server.IService/IPermissionService.cs @@ -11,7 +11,7 @@ public static class Functions public const string App_Delete = "APP_DELETE"; public const string App_Auth = "APP_AUTH"; - public const string Confing_Read = "CONFIG_READ"; + public const string Config_Read = "CONFIG_READ"; public const string Config_Add = "CONFIG_ADD"; public const string Config_Edit = "CONFIG_EDIT"; public const string Config_Delete = "CONFIG_DELETE"; @@ -23,6 +23,7 @@ public static class Functions public const string Node_Add = "NODE_ADD"; public const string Node_Delete = "NODE_DELETE"; + public const string Client_Read = "CLIENT_READ"; public const string Client_Refresh = "CLIENT_REFRESH"; public const string Client_Disconnect = "CLIENT_DISCONNECT"; @@ -41,6 +42,59 @@ public static class Functions public const string Service_Delete = "SERVICE_DELETE"; public const string Log_Read = "LOG_READ"; + + public static List GetAllPermissions() + { + // SuperAdmin has all permissions + return new List + { + // Application permissions + Functions.App_Read, + Functions.App_Add, + Functions.App_Edit, + Functions.App_Delete, + Functions.App_Auth, + + // Configuration permissions + Functions.Config_Read, + Functions.Config_Add, + Functions.Config_Edit, + Functions.Config_Delete, + Functions.Config_Publish, + Functions.Config_Offline, + + // Node permissions + Functions.Node_Read, + Functions.Node_Add, + Functions.Node_Delete, + + // Client permissions + Functions.Client_Refresh, + Functions.Client_Disconnect, + Functions.Client_Read, + + // User permissions + Functions.User_Read, + Functions.User_Add, + Functions.User_Edit, + Functions.User_Delete, + + // Role permissions + Functions.Role_Read, + Functions.Role_Add, + Functions.Role_Edit, + Functions.Role_Delete, + + // Service permissions + Functions.Service_Read, + Functions.Service_Add, + Functions.Service_Delete, + + // System permissions + Functions.Log_Read + }; + } + } public interface IPermissionService diff --git a/src/AgileConfig.Server.Service/PermissionService.cs b/src/AgileConfig.Server.Service/PermissionService.cs index ee10714f..388fda45 100644 --- a/src/AgileConfig.Server.Service/PermissionService.cs +++ b/src/AgileConfig.Server.Service/PermissionService.cs @@ -44,11 +44,9 @@ public async Task> GetUserPermission(string userId) var roleIds = userRoles.Select(x => x.RoleId).Distinct().ToList(); if (!roleIds.Any()) return new List(); - // Get role-function mappings for all user roles var roleFunctions = await _roleFunctionRepository.QueryAsync(x => roleIds.Contains(x.RoleId)); var functionIds = roleFunctions.Select(rf => rf.FunctionId).Distinct().ToList(); -// Get function entities and return their codes var functions = await _functionRepository.QueryAsync(f => functionIds.Contains(f.Id)); var functionCodes = functions.Select(f => f.Code).Distinct().ToList(); @@ -105,13 +103,4 @@ private async Task> GetUserAuthApp(string userId, string authPermissio return apps; } - /// - /// Retrieve applications managed by the user. - /// - /// Identifier of the user who administers the applications. - /// List of applications where the user is the administrator. - private async Task> GetUserAdminApps(string userId) - { - return await _appRepository.QueryAsync(x => x.AppAdmin == userId); - } -} \ No newline at end of file +} diff --git a/src/AgileConfig.Server.Service/SystemInitializationService.cs b/src/AgileConfig.Server.Service/SystemInitializationService.cs index a8b5004e..15c77e73 100644 --- a/src/AgileConfig.Server.Service/SystemInitializationService.cs +++ b/src/AgileConfig.Server.Service/SystemInitializationService.cs @@ -257,8 +257,8 @@ private async Task> InitializeFunctions() // Configuration permissions new() { - Id = Functions.Confing_Read, - Code = Functions.Confing_Read, + Id = Functions.Config_Read, + Code = Functions.Config_Read, Name = "Read Configuration", Description = "Permission to read configurations", Category = "Configuration", @@ -367,6 +367,16 @@ private async Task> InitializeFunctions() SortIndex = 16, CreateTime = DateTime.Now }, + new() + { + Id = Functions.Client_Read, + Code = Functions.Client_Read, + Name = "Read Client", + Description = "Permission to read clients", + Category = "Client", + SortIndex = 16, + CreateTime = DateTime.Now + }, // User permissions new() { diff --git a/src/AgileConfig.Server.UI/react-ui-antd/config/routes.ts b/src/AgileConfig.Server.UI/react-ui-antd/config/routes.ts index ff0b94ec..d38e571e 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/config/routes.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/config/routes.ts @@ -42,39 +42,39 @@ { path: '/index.html', redirect: '/home' }, { name: 'home', icon: 'Dashboard', path: '/home', component: './Home', - category: 'Application', functionKey: 'APP_READ' + category: 'Application' }, { name: 'list.node-list', icon: 'Database', path: '/node', component: './Nodes', - category: 'Node', functionKey: 'NODE_READ' + category: 'Node' }, { name: 'list.app-list', icon: 'Appstore', path: '/app', component: './Apps', - category: 'Application', functionKey: 'APP_READ' + category: 'Application' }, { name: 'list.config-list', icon: 'Table', path: '/app/config/:app_id/:app_name', component: './Configs', - hideInMenu: true, category: 'Configuration', functionKey: 'CONFIG_READ' + hideInMenu: true, category: 'Configuration' }, { name: 'list.client-list', icon: 'Shrink', path: '/client', component: './Clients', - category: 'Client', functionKey: 'CLIENT_REFRESH' + category: 'Client' }, { name: 'list.service-list', icon: 'Cloud', path: '/service', component: './Services', - category: 'Service', functionKey: 'SERVICE_READ' + category: 'Service' }, { name: 'list.user-list', icon: 'User', path: '/users', component: './User', - category: 'User', functionKey: 'USER_READ' + category: 'User' }, { name: 'list.role-list', icon: 'SafetyCertificate', path: '/roles', component: './Role', - category: 'Role', functionKey: 'ROLE_READ' + category: 'Role' }, { name: 'list.logs-list', icon: 'Bars', path: '/logs', component: './Logs', - category: 'Log', functionKey: 'LOG_READ' + category: 'Log' }, { component: './404' }, ], diff --git a/src/AgileConfig.Server.UI/react-ui-antd/package-lock.json b/src/AgileConfig.Server.UI/react-ui-antd/package-lock.json index 9c6e3950..6d0b1ab6 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/package-lock.json +++ b/src/AgileConfig.Server.UI/react-ui-antd/package-lock.json @@ -5377,19 +5377,6 @@ "@babel/runtime": "^7.7.6" } }, - "node_modules/@umijs/renderer-react/node_modules/react": { - "version": "16.14.0", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@umijs/renderer-react/node_modules/react-router-config": { "version": "5.1.1", "resolved": "https://registry.npmmirror.com/react-router-config/-/react-router-config-5.1.1.tgz", @@ -27630,15 +27617,6 @@ "@babel/runtime": "^7.7.6" } }, - "react": { - "version": "16.14.0", - "peer": true, - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" - } - }, "react-router-config": { "version": "5.1.1", "resolved": "https://registry.npmmirror.com/react-router-config/-/react-router-config-5.1.1.tgz", diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx index 280a8742..40a9c100 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx @@ -54,9 +54,7 @@ const menuDataRender = (menuList: MenuDataItem[]): MenuDataItem[] => { // category filter const category = (m as any).category; if (category && !cats.includes(category)) return false; - // function key filter (for read permission) - const fnKey = (m as any).functionKey; - if (fnKey && !hasFunction(fnKey)) return false; + return true; }) .map((item) => { diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts index 8b2e916a..302d284d 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts @@ -298,30 +298,31 @@ export default { 'pages.role.permissions.APP_EDIT': 'Edit App', 'pages.role.permissions.APP_DELETE': 'Delete App', 'pages.role.permissions.APP_AUTH': 'Authorize App', - 'pages.role.permissions.CONFIG_ADD': 'Add Config', - 'pages.role.permissions.CONFIG_EDIT': 'Edit Config', - 'pages.role.permissions.CONFIG_DELETE': 'Delete Config', - 'pages.role.permissions.CONFIG_PUBLISH': 'Publish Config', - 'pages.role.permissions.CONFIG_OFFLINE': 'Offline Config', - 'pages.role.permissions.NODE_ADD': 'Add Node', - 'pages.role.permissions.NODE_DELETE': 'Delete Node', + 'pages.role.permissions.CONFIG_ADD': 'Config Add', + 'pages.role.permissions.CONFIG_EDIT': 'Config Edit', + 'pages.role.permissions.CONFIG_DELETE': 'Config Delete', + 'pages.role.permissions.CONFIG_PUBLISH': 'Config Publish', + 'pages.role.permissions.CONFIG_OFFLINE': 'Config Offline', + 'pages.role.permissions.NODE_ADD': 'Node Add', + 'pages.role.permissions.NODE_DELETE': 'Node Delete', + 'pages.role.permissions.CLIENT_READ': 'Client View', 'pages.role.permissions.CLIENT_DISCONNECT': 'Disconnect Client', - 'pages.role.permissions.USER_ADD': 'Add User', - 'pages.role.permissions.USER_EDIT': 'Edit User', - 'pages.role.permissions.USER_DELETE': 'Delete User', - 'pages.role.permissions.ROLE_ADD': 'Add Role', - 'pages.role.permissions.ROLE_EDIT': 'Edit Role', - 'pages.role.permissions.ROLE_DELETE': 'Delete Role', - 'pages.role.permissions.APP_READ': 'View App', - 'pages.role.permissions.CONFIG_READ': 'View Config', - 'pages.role.permissions.NODE_READ': 'View Node', + 'pages.role.permissions.USER_ADD': 'User Add', + 'pages.role.permissions.USER_EDIT': 'User Edit', + 'pages.role.permissions.USER_DELETE': 'User Delete', + 'pages.role.permissions.ROLE_ADD': 'Role Add', + 'pages.role.permissions.ROLE_EDIT': 'Role Edit', + 'pages.role.permissions.ROLE_DELETE': 'Role Delete', + 'pages.role.permissions.APP_READ': 'App View', + 'pages.role.permissions.CONFIG_READ': 'Config View', + 'pages.role.permissions.NODE_READ': 'Node View', 'pages.role.permissions.CLIENT_REFRESH': 'Refresh Client', - 'pages.role.permissions.USER_READ': 'View User', - 'pages.role.permissions.ROLE_READ': 'View Role', - 'pages.role.permissions.SERVICE_READ': 'View Service', - 'pages.role.permissions.SERVICE_ADD': 'Add Service', - 'pages.role.permissions.SERVICE_DELETE': 'Delete Service', - 'pages.role.permissions.LOG_READ': 'View Logs', + 'pages.role.permissions.USER_READ': 'User View', + 'pages.role.permissions.ROLE_READ': 'Role View', + 'pages.role.permissions.SERVICE_READ': 'Service View', + 'pages.role.permissions.SERVICE_ADD': 'Service Add', + 'pages.role.permissions.SERVICE_DELETE': 'Service Delete', + 'pages.role.permissions.LOG_READ': 'Log View', // Service Management 'pages.service.table.cols.servicename': 'Service Name', diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts index 7af614fd..ceb70d71 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts @@ -292,35 +292,35 @@ export default { 'pages.role.load_failed': '加载角色失败', 'pages.role.permissions.load_failed': '加载权限列表失败', 'pages.role.permissions.all': '所有权限', - 'pages.role.permissions.APP_ADD': '新增应用', - 'pages.role.permissions.APP_EDIT': '编辑应用', - 'pages.role.permissions.APP_DELETE': '删除应用', + 'pages.role.permissions.APP_ADD': '应用新增', + 'pages.role.permissions.APP_EDIT': '应用编辑', + 'pages.role.permissions.APP_DELETE': '应用删除', 'pages.role.permissions.APP_AUTH': '应用授权', - 'pages.role.permissions.CONFIG_ADD': '新增配置', - 'pages.role.permissions.CONFIG_EDIT': '编辑配置', - 'pages.role.permissions.CONFIG_DELETE': '删除配置', - 'pages.role.permissions.CONFIG_PUBLISH': '发布配置', - 'pages.role.permissions.CONFIG_OFFLINE': '下线配置', - 'pages.role.permissions.NODE_ADD': '新增节点', - 'pages.role.permissions.NODE_DELETE': '删除节点', - 'pages.role.permissions.CLIENT_DISCONNECT': '断开客户端', - 'pages.role.permissions.USER_ADD': '新增用户', - 'pages.role.permissions.USER_EDIT': '编辑用户', - 'pages.role.permissions.USER_DELETE': '删除用户', - 'pages.role.permissions.ROLE_ADD': '新增角色', - 'pages.role.permissions.ROLE_EDIT': '编辑角色', - 'pages.role.permissions.ROLE_DELETE': '删除角色', - 'pages.role.permissions.APP_READ': '查看应用', - 'pages.role.permissions.CONFIG_READ': '查看配置', - 'pages.role.permissions.NODE_READ': '查看节点', - 'pages.role.permissions.CLIENT_REFRESH': '刷新客户端', - 'pages.role.permissions.USER_READ': '查看用户', - 'pages.role.permissions.ROLE_READ': '查看角色', - 'pages.role.permissions.SERVICE_READ': '查看服务', - 'pages.role.permissions.SERVICE_ADD': '新增服务', - 'pages.role.permissions.SERVICE_DELETE': '删除服务', - 'pages.role.permissions.LOG_READ': '查看日志', - + 'pages.role.permissions.CONFIG_ADD': '配置新增', + 'pages.role.permissions.CONFIG_EDIT': '配置编辑', + 'pages.role.permissions.CONFIG_DELETE': '配置删除', + 'pages.role.permissions.CONFIG_PUBLISH': '配置发布', + 'pages.role.permissions.CONFIG_OFFLINE': '配置下线', + 'pages.role.permissions.NODE_ADD': '节点新增', + 'pages.role.permissions.NODE_DELETE': '节点删除', + 'pages.role.permissions.CLIENT_READ': '客户端查看', + 'pages.role.permissions.CLIENT_DISCONNECT': '客户端断开', + 'pages.role.permissions.CLIENT_REFRESH': '客户端刷新', + 'pages.role.permissions.USER_ADD': '用户新增', + 'pages.role.permissions.USER_EDIT': '用户编辑', + 'pages.role.permissions.USER_DELETE': '用户删除', + 'pages.role.permissions.ROLE_ADD': '角色新增', + 'pages.role.permissions.ROLE_EDIT': '角色编辑', + 'pages.role.permissions.ROLE_DELETE': '角色删除', + 'pages.role.permissions.APP_READ': '应用查看', + 'pages.role.permissions.CONFIG_READ': '配置查看', + 'pages.role.permissions.NODE_READ': '节点查看', + 'pages.role.permissions.USER_READ': '用户查看', + 'pages.role.permissions.ROLE_READ': '角色查看', + 'pages.role.permissions.SERVICE_READ': '服务查看', + 'pages.role.permissions.SERVICE_ADD': '服务新增', + 'pages.role.permissions.SERVICE_DELETE': '服务删除', + 'pages.role.permissions.LOG_READ': '日志查看', // Service Management 'pages.service.table.cols.servicename': '服务名', 'pages.service.table.cols.serviceid': '服务ID', diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/models/user.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/models/user.ts index 07a78547..b77da983 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/models/user.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/models/user.ts @@ -77,7 +77,7 @@ const UserModel: UserModelType = { saveCurrentSystemInfo(state, action) { setAuthority(action.payload.currentAuthority); setFunctions(action.payload.currentFunctions); - setCategories(action.payload.currentCategories); + setCategories(action.payload.currentCategories || []); setUserInfo({name:action.payload.name, userid: action.payload.userid}); setSysInfo(action.payload.appVer, action.payload.envList); return { diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/comps/updateForm.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/comps/updateForm.tsx index 5d1bf2e8..431db88b 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/comps/updateForm.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/comps/updateForm.tsx @@ -3,7 +3,6 @@ import { ModalForm, ProFormDependency, ProFormSelect, ProFormSwitch, ProFormText import React, { useEffect, useState } from 'react'; import { AppListItem } from '../data'; import { getAppGroups, inheritancedApps } from '../service'; -import { adminUsers } from '@/pages/User/service'; import { Divider, Input } from 'antd'; import { PlusOutlined } from '@ant-design/icons'; @@ -118,22 +117,6 @@ const UpdateForm: React.FC = (props) => { } } - { - const result = await adminUsers(); - return result.data.map((x: { userName: string, id: string, team: string }) => { - console.log(x); - return { label: x.userName + ' - ' + (x.team ? x.team : ''), value: x.id }; - }); - }} - > { hideInSearch: true, sorter: true, }, - { - title: intl.formatMessage({ - id: 'pages.app.table.cols.admin', - }), - dataIndex: 'appAdminName', - hideInSearch: true, - }, { title: intl.formatMessage({ id: 'pages.app.table.cols.public', @@ -347,10 +339,8 @@ const appList: React.FC = (props) => { dataIndex: 'enabled', render: (dom, entity) => { return ( - } + { handleEnabledChange(e, entity); }} /> - + ); }, hideInSearch: true, @@ -370,15 +360,15 @@ const appList: React.FC = (props) => { }), valueType: 'option', render: (text, record, _, action) => [ - + {intl.formatMessage({ id: 'pages.app.table.cols.action.configs' })} , - + { setUpdateModalVisible(true); @@ -389,10 +379,10 @@ const appList: React.FC = (props) => { id: 'pages.app.table.cols.action.edit', })} - , - + , + { setUserAuthModalVisible(true); setCurrentRow(record); @@ -401,7 +391,7 @@ const appList: React.FC = (props) => { {intl.formatMessage({ id: 'pages.app.table.cols.action.auth' })} , - + - , + , ], }, ]; @@ -620,26 +610,6 @@ const appList: React.FC = (props) => { ) : null; }} - { - const result = await adminUsers(); - return result.data.map((x: { userName: string; id: string; team: string }) => { - console.log(x); - return { label: x.userName + ' - ' + (x.team ? x.team : ''), value: x.id }; - }); - }} - > - { return false; } -const checkUserListModifyPermission = (user:UserItem) => { - // Lower number means higher privilege - const authMap:Record = { SuperAdmin: 0, Admin: 1, NormalUser: 2 }; - const myRoles = getAuthority(); - if (!Array.isArray(myRoles) || myRoles.length === 0) return false; - - // If current user is SuperAdmin -> can edit anyone except themselves (optional) - if (myRoles.includes('SuperAdmin')) { - // Prevent editing own account if desired - if (user.userName && user.userName === (typeof localStorage !== 'undefined' ? localStorage.getItem('currentUserName') : undefined)) { - return false; // disallow self-edit via list (modal still available maybe) - } - return true; - } - - // Determine current user's minimal privilege level - const currentAuthNum = myRoles - .map(r => authMap[r] ?? 999) - .reduce((min, v) => v < min ? v : min, 999); - - // Determine target user's minimal privilege level - const targetCodes = user.userRoleCodes || []; - const userAuthNum = targetCodes.length > 0 - ? targetCodes.map(c => authMap[c] ?? 999).reduce((min, v) => v < min ? v : min, 999) - : 999; - - // Allow edit only if current privilege strictly higher than target (numerically lower) - return currentAuthNum < userAuthNum; -} - const userList:React.FC = () => { const actionRef = useRef(); const addFormRef = useRef(); @@ -251,7 +221,6 @@ const userList:React.FC = () => { }), valueType: 'option', render: (text, record, _, action) => { - if (!checkUserListModifyPermission(record)) return []; const actions: React.ReactNode[] = []; actions.push( diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/utils/authority.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/utils/authority.ts index d513e242..a97d2b29 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/utils/authority.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/utils/authority.ts @@ -78,7 +78,8 @@ export function getCategories(str?: string): string[] { export function setCategories(categories: string | string[]): void { const arr = typeof categories === 'string' ? [categories] : categories; - localStorage.setItem('antd-pro-categories', JSON.stringify(arr)); + const safeArr = arr || []; + localStorage.setItem('antd-pro-categories', JSON.stringify(safeArr)); // menu re-eval reloadAuthorized(); } @@ -86,7 +87,11 @@ export function setCategories(categories: string | string[]): void { // convenience helpers export function hasFunction(fnKey: string): boolean { const fns = getFunctions(); - return Array.isArray(fns) ? fns.includes(fnKey) : false; + const has = Array.isArray(fns) ? fns.includes(fnKey) : false; + + console.log(`${fns} hasFunction(${fnKey}) => ${has}`); + + return has; } export function hasCategory(cat: string): boolean { From 74084adb9e3f82f1d2225fe012e3dc8cf93c8d7c Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Sun, 23 Nov 2025 02:35:25 +0800 Subject: [PATCH 08/17] control backend permission --- .../Controllers/AppController.cs | 49 +++-- .../Controllers/ConfigController.cs | 40 +++- .../Controllers/RoleController.cs | 6 +- .../Controllers/ServerNodeController.cs | 4 +- .../Controllers/ServiceController.cs | 3 + .../Controllers/SysLogController.cs | 6 +- .../Controllers/UserController.cs | 9 +- .../Controllers/api/AppController.cs | 16 +- .../Controllers/api/ConfigController.cs | 10 +- .../Controllers/api/NodeController.cs | 4 +- .../Filters/PermissionCheckAttribute.cs | 185 +----------------- .../PermissionCheckByBasicAttribute.cs | 2 +- .../Models/AppAuthVM.cs | 6 +- .../IAppService.cs | 10 +- src/AgileConfig.Server.Service/AppService.cs | 54 +++-- .../PermissionService.cs | 47 ++--- .../Authorized/AuthorizedElement.tsx | 7 +- .../react-ui-antd/src/layouts/BasicLayout.tsx | 2 + .../react-ui-antd/src/locales/en-US/pages.ts | 3 +- .../react-ui-antd/src/locales/zh-CN/pages.ts | 3 +- .../src/pages/Apps/comps/userAuth.tsx | 40 ++-- .../react-ui-antd/src/pages/Apps/data.d.ts | 3 +- .../react-ui-antd/src/pages/Apps/index.tsx | 17 +- .../react-ui-antd/src/pages/Configs/index.tsx | 18 +- .../react-ui-antd/src/utils/permission.ts | 17 +- .../react-ui-antd/src/utils/permission.tsx | 15 +- test/ApiSiteTests/TestAppController.cs | 2 +- 27 files changed, 220 insertions(+), 358 deletions(-) diff --git a/src/AgileConfig.Server.Apisite/Controllers/AppController.cs b/src/AgileConfig.Server.Apisite/Controllers/AppController.cs index 5097b0d0..ee5a8513 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/AppController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/AppController.cs @@ -6,6 +6,7 @@ using AgileConfig.Server.Apisite.Models; using AgileConfig.Server.Apisite.Models.Mapping; using AgileConfig.Server.Apisite.Utilites; +using AgileConfig.Server.Common; using AgileConfig.Server.Common.EventBus; using AgileConfig.Server.Common.Resources; using AgileConfig.Server.Data.Entity; @@ -21,21 +22,20 @@ namespace AgileConfig.Server.Apisite.Controllers; public class AppController : Controller { private readonly IAppService _appService; - private readonly IPermissionService _permissionService; private readonly ITinyEventBus _tinyEventBus; private readonly IUserService _userService; public AppController(IAppService appService, - IPermissionService permissionService, IUserService userService, ITinyEventBus tinyEventBus) { _userService = userService; _tinyEventBus = tinyEventBus; _appService = appService; - _permissionService = permissionService; } + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { Functions.App_Read })] public async Task Search(string name, string id, string group, string sortField, string ascOrDesc, bool tableGrouped, int current = 1, int pageSize = 20) { @@ -43,12 +43,21 @@ public async Task Search(string name, string id, string group, st if (pageSize < 1) throw new ArgumentException(Messages.PageSizeCannotBeLessThanOne); + var currentUserId = await this.GetCurrentUserId(_userService); + var isAdmin = false; + if (!string.IsNullOrWhiteSpace(currentUserId)) + { + var roles = await _userService.GetUserRolesAsync(currentUserId); + isAdmin = roles.Any(r => r.Id == SystemRoleConstants.AdminId || r.Id == SystemRoleConstants.SuperAdminId); + } + var appListVms = new List(); long count = 0; if (!tableGrouped) { var searchResult = - await _appService.SearchAsync(id, name, group, sortField, ascOrDesc, current, pageSize); + await _appService.SearchAsync(id, name, group, sortField, ascOrDesc, current, pageSize, currentUserId, + isAdmin); foreach (var app in searchResult.Apps) appListVms.Add(app.ToAppListVM()); count = searchResult.Count; @@ -56,7 +65,8 @@ public async Task Search(string name, string id, string group, st else { var searchResult = - await _appService.SearchGroupedAsync(id, name, group, sortField, ascOrDesc, current, pageSize); + await _appService.SearchGroupedAsync(id, name, group, sortField, ascOrDesc, current, pageSize, + currentUserId, isAdmin); foreach (var groupedApp in searchResult.GroupedApps) { var app = groupedApp.App; @@ -97,7 +107,7 @@ private async Task AppendInheritancedInfo(List list) } } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "App.Add", Functions.App_Add })] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.App_Add })] [HttpPost] public async Task Add([FromBody] AppVM model) { @@ -141,7 +151,7 @@ public async Task Add([FromBody] AppVM model) }); } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "App.Edit", Functions.App_Edit })] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.App_Edit })] [HttpPost] public async Task Edit([FromBody] AppVM model) { @@ -188,6 +198,7 @@ public async Task Edit([FromBody] AppVM model) }); } + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.App_Read })] [HttpGet] public async Task Get(string id) { @@ -213,7 +224,7 @@ public async Task Get(string id) } [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "App.DisableOrEnable", Functions.App_Edit })] + Arguments = new object[] { Functions.App_Edit })] [HttpPost] public async Task DisableOrEnable(string id) { @@ -237,7 +248,7 @@ public async Task DisableOrEnable(string id) }); } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "App.Delete", Functions.App_Delete })] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.App_Delete })] [HttpPost] public async Task Delete(string id) { @@ -267,6 +278,7 @@ public async Task Delete(string id) /// /// [HttpGet] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.App_Read })] public async Task InheritancedApps(string currentAppId) { var apps = await _appService.GetAllInheritancedAppsAsync(); @@ -296,24 +308,22 @@ public async Task InheritancedApps(string currentAppId) /// /// View model containing authorization assignments. /// Operation result. - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "App.Auth", Functions.App_Auth })] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.App_Auth })] [HttpPost] public async Task SaveAppAuth([FromBody] AppAuthVM model) { ArgumentNullException.ThrowIfNull(model); - var result = await _appService.SaveUserAppAuth(model.AppId, model.EditConfigPermissionUsers, - _permissionService.EditConfigPermissionKey); - var result1 = await _appService.SaveUserAppAuth(model.AppId, model.PublishConfigPermissionUsers, - _permissionService.PublishConfigPermissionKey); + var result = await _appService.SaveUserAppAuth(model.AppId, model.AuthorizedUsers); return Json(new { - success = result && result1 + success = result }); } [HttpGet] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.App_Read })] public async Task GetUserAppAuth(string appId) { ArgumentException.ThrowIfNullOrEmpty(appId); @@ -322,12 +332,8 @@ public async Task GetUserAppAuth(string appId) { AppId = appId }; - result.EditConfigPermissionUsers = - (await _appService.GetUserAppAuth(appId, _permissionService.EditConfigPermissionKey)).Select(x => x.Id) - .ToList(); - result.PublishConfigPermissionUsers = - (await _appService.GetUserAppAuth(appId, _permissionService.PublishConfigPermissionKey)) - .Select(x => x.Id).ToList(); + result.AuthorizedUsers = + (await _appService.GetUserAppAuth(appId)).Select(x => x.Id).ToList(); return Json(new { @@ -337,6 +343,7 @@ public async Task GetUserAppAuth(string appId) } [HttpGet] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.App_Read })] public async Task GetAppGroups() { var groups = await _appService.GetAppGroups(); diff --git a/src/AgileConfig.Server.Apisite/Controllers/ConfigController.cs b/src/AgileConfig.Server.Apisite/Controllers/ConfigController.cs index 6cf5d1f9..fabddd7f 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/ConfigController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/ConfigController.cs @@ -39,7 +39,7 @@ ITinyEventBus tinyEventBus _tinyEventBus = tinyEventBus; } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Config.Add", Functions.Config_Add })] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.Config_Add })] [HttpPost] public async Task Add([FromBody] ConfigVM model, EnvString env) { @@ -88,7 +88,7 @@ public async Task Add([FromBody] ConfigVM model, EnvString env) } [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "Config.AddRange", Functions.Config_Add })] + Arguments = new object[] { Functions.Config_Add })] [HttpPost] public async Task AddRange([FromBody] List model, EnvString env) { @@ -146,7 +146,7 @@ public async Task AddRange([FromBody] List model, EnvSt } [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "Config.Edit", Functions.Config_Edit })] + Arguments = new object[] { Functions.Config_Edit })] [HttpPost] public async Task Edit([FromBody] ConfigVM model, [FromQuery] EnvString env) { @@ -254,6 +254,8 @@ public async Task All(string env) /// Current page number. /// [HttpGet] + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { Functions.Config_Read })] public async Task Search(string appId, string group, string key, OnlineStatus? onlineStatus, string sortField, string ascOrDesc, EnvString env, int pageSize = 20, int current = 1) { @@ -295,6 +297,8 @@ public async Task Search(string appId, string group, string key, } [HttpGet] + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { Functions.Config_Read })] public async Task Get(string id, EnvString env) { if (string.IsNullOrEmpty(id)) throw new ArgumentNullException("id"); @@ -310,7 +314,7 @@ public async Task Get(string id, EnvString env) } [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "Config.Delete", Functions.Config_Delete })] + Arguments = new object[] { Functions.Config_Delete })] [HttpPost] public async Task Delete(string id, EnvString env) { @@ -343,7 +347,7 @@ public async Task Delete(string id, EnvString env) } [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "Config.DeleteSome", Functions.Config_Delete })] + Arguments = new object[] { Functions.Config_Delete })] [HttpPost] public async Task DeleteSome([FromBody] List ids, EnvString env) { @@ -385,7 +389,7 @@ public async Task DeleteSome([FromBody] List ids, EnvStri [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "Config.Rollback", Functions.Config_Publish })] + Arguments = new object[] { Functions.Config_Offline })] [HttpPost] public async Task Rollback(string publishTimelineId, EnvString env) { @@ -406,6 +410,8 @@ public async Task Rollback(string publishTimelineId, EnvString en }); } + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { Functions.Config_Read })] [HttpGet] public async Task ConfigPublishedHistory(string configId, EnvString env) { @@ -438,7 +444,7 @@ public async Task ConfigPublishedHistory(string configId, EnvStri /// /// [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "Config.Publish", Functions.Config_Publish })] + Arguments = new object[] { Functions.Config_Publish })] [HttpPost] public async Task Publish([FromBody] PublishLogVM model, EnvString env) { @@ -517,6 +523,8 @@ public IActionResult PreViewJsonFile() /// /// Application ID. /// + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { Functions.Config_Read })] public async Task ExportJson(string appId, EnvString env) { if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException("appId"); @@ -540,6 +548,8 @@ public async Task ExportJson(string appId, EnvString env) /// /// Application ID. /// + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { Functions.Config_Read })] public async Task WaitPublishStatus(string appId, EnvString env) { if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException("appId"); @@ -569,6 +579,8 @@ public async Task WaitPublishStatus(string appId, EnvString env) /// /// Application ID. /// + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { Functions.Config_Read })] public async Task PublishHistory(string appId, EnvString env) { if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException("appId"); @@ -596,6 +608,8 @@ await _configService.GetPublishTimeLineNodeAsync(data.FirstOrDefault()?.PublishT }); } + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { Functions.Config_Edit })] public async Task CancelEdit(string configId, EnvString env) { if (string.IsNullOrEmpty(configId)) throw new ArgumentNullException("configId"); @@ -614,6 +628,8 @@ public async Task CancelEdit(string configId, EnvString env) }); } + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { Functions.Config_Edit })] public async Task CancelSomeEdit([FromBody] List ids, EnvString env) { if (ids == null) throw new ArgumentNullException("ids"); @@ -633,7 +649,7 @@ public async Task CancelSomeEdit([FromBody] List ids, Env } [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "Config.EvnSync", Functions.Config_Add })] + Arguments = new object[] { Functions.Config_Add })] [HttpPost] public async Task SyncEnv([FromBody] List toEnvs, [FromQuery] string appId, [FromQuery] string currentEnv) @@ -660,6 +676,8 @@ public async Task SyncEnv([FromBody] List toEnvs, [FromQu }); } + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { Functions.Config_Read })] public async Task GetKvList(string appId, EnvString env) { if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException("appId"); @@ -684,6 +702,8 @@ public async Task GetKvList(string appId, EnvString env) /// /// Application ID. /// + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { Functions.Config_Read })] public async Task GetJson(string appId, EnvString env) { if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException("appId"); @@ -708,6 +728,8 @@ public async Task GetJson(string appId, EnvString env) } [HttpPost] + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { Functions.Config_Edit })] public async Task SaveJson([FromBody] SaveJsonVM data, string appId, EnvString env) { if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException(nameof(appId)); @@ -725,6 +747,8 @@ public async Task SaveJson([FromBody] SaveJsonVM data, string app } [HttpPost] + [TypeFilter(typeof(PermissionCheckAttribute), + Arguments = new object[] { Functions.Config_Edit })] public async Task SaveKvList([FromBody] SaveKVListVM data, string appId, EnvString env) { if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException(nameof(appId)); diff --git a/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs b/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs index d5aa253c..bde145dc 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/RoleController.cs @@ -56,7 +56,7 @@ public IActionResult SupportedPermissions() }); } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Role.Add", Functions.Role_Add })] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.Role_Add })] [HttpPost] public async Task Add([FromBody] RoleVM model) { @@ -75,7 +75,7 @@ public async Task Add([FromBody] RoleVM model) return Json(new { success = true }); } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Role.Edit", Functions.Role_Edit })] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.Role_Edit })] [HttpPost] public async Task Edit([FromBody] RoleVM model) { @@ -106,7 +106,7 @@ public async Task Edit([FromBody] RoleVM model) }); } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Role.Delete", Functions.Role_Delete })] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.Role_Delete })] [HttpPost] public async Task Delete(string id) { diff --git a/src/AgileConfig.Server.Apisite/Controllers/ServerNodeController.cs b/src/AgileConfig.Server.Apisite/Controllers/ServerNodeController.cs index 39c298d8..21236fe1 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/ServerNodeController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/ServerNodeController.cs @@ -35,7 +35,7 @@ ITinyEventBus tinyEventBus _tinyEventBus = tinyEventBus; } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Node.Add", Functions.Node_Add })] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.Node_Add })] [HttpPost] public async Task Add([FromBody] ServerNodeVM model) { @@ -70,7 +70,7 @@ public async Task Add([FromBody] ServerNodeVM model) }); } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "Node.Delete", Functions.Node_Delete })] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.Node_Delete })] [HttpPost] public async Task Delete([FromBody] ServerNodeVM model) { diff --git a/src/AgileConfig.Server.Apisite/Controllers/ServiceController.cs b/src/AgileConfig.Server.Apisite/Controllers/ServiceController.cs index de0ba0a3..fce32036 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/ServiceController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/ServiceController.cs @@ -32,6 +32,7 @@ public ServiceController(IServiceInfoService serviceInfoService, } [HttpPost] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.Service_Add })] public async Task Add([FromBody] ServiceInfoVM model) { if (model == null) throw new ArgumentNullException(nameof(model)); @@ -64,6 +65,7 @@ public async Task Add([FromBody] ServiceInfoVM model) } [HttpPost] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.Service_Delete })] public async Task Remove(string id) { if (string.IsNullOrEmpty(id)) throw new ArgumentNullException("id"); @@ -86,6 +88,7 @@ public async Task Remove(string id) }); } + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.Service_Read })] public async Task Search(string serviceName, string serviceId, ServiceStatus? status, string sortField, string ascOrDesc, int current = 1, int pageSize = 20) diff --git a/src/AgileConfig.Server.Apisite/Controllers/SysLogController.cs b/src/AgileConfig.Server.Apisite/Controllers/SysLogController.cs index c484ba56..66e12f60 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/SysLogController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/SysLogController.cs @@ -1,9 +1,10 @@ -using System; -using System.Threading.Tasks; +using AgileConfig.Server.Apisite.Filters; using AgileConfig.Server.Data.Entity; using AgileConfig.Server.IService; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using System; +using System.Threading.Tasks; namespace AgileConfig.Server.Apisite.Controllers; @@ -18,6 +19,7 @@ public SysLogController(ISysLogService sysLogService) } [HttpGet] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.Log_Read })] public async Task Search(string appId, SysLogType? logType, DateTime? startTime, DateTime? endTime, int current = 1, int pageSize = 20) { diff --git a/src/AgileConfig.Server.Apisite/Controllers/UserController.cs b/src/AgileConfig.Server.Apisite/Controllers/UserController.cs index 976c5dae..dcf1c511 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/UserController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/UserController.cs @@ -32,6 +32,7 @@ ITinyEventBus tinyEventBus } [HttpGet] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.User_Read })] public async Task Search(string userName, string team, int current = 1, int pageSize = 20) { if (current <= 0) throw new ArgumentException(Messages.CurrentCannotBeLessThanOneUser); @@ -72,7 +73,7 @@ public async Task Search(string userName, string team, int curren }); } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "User.Add", Functions.User_Add })] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.User_Add })] [HttpPost] public async Task Add([FromBody] UserVM model) { @@ -112,7 +113,7 @@ public async Task Add([FromBody] UserVM model) }); } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "User.Edit", Functions.User_Edit })] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.User_Edit })] [HttpPost] public async Task Edit([FromBody] UserVM model) { @@ -146,7 +147,7 @@ public async Task Edit([FromBody] UserVM model) } [TypeFilter(typeof(PermissionCheckAttribute), - Arguments = new object[] { "User.ResetPassword", Functions.User_Edit })] + Arguments = new object[] { Functions.User_Edit })] [HttpPost] public async Task ResetPassword(string userId) { @@ -172,7 +173,7 @@ public async Task ResetPassword(string userId) }); } - [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { "User.Delete", Functions.User_Delete })] + [TypeFilter(typeof(PermissionCheckAttribute), Arguments = new object[] { Functions.User_Delete })] [HttpPost] public async Task Delete(string userId) { diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/AppController.cs b/src/AgileConfig.Server.Apisite/Controllers/api/AppController.cs index 25acb1e7..ea164c8f 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/AppController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/AppController.cs @@ -41,6 +41,8 @@ public AppController(IAppService appService, /// /// [HttpGet] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), + Arguments = new object[] { Functions.App_Read })] public async Task>> GetAll() { var apps = await _appService.GetAllAppsAsync(); @@ -55,6 +57,8 @@ public async Task>> GetAll() /// Application ID. /// [HttpGet("{id}")] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), + Arguments = new object[] { Functions.App_Read })] public async Task> GetById(string id) { var actionResult = await _appController.Get(id); @@ -82,7 +86,7 @@ public async Task> GetById(string id) /// Application payload. /// [ProducesResponseType(201)] - [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "App.Add", Functions.App_Add })] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { Functions.App_Add })] [HttpPost] public async Task Add([FromBody] ApiAppVM model) { @@ -119,7 +123,7 @@ public async Task Add([FromBody] ApiAppVM model) /// Application payload. /// [ProducesResponseType(200)] - [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "App.Edit", Functions.App_Edit })] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { Functions.App_Edit })] [HttpPut("{id}")] public async Task Edit(string id, [FromBody] ApiAppVM model) { @@ -158,7 +162,7 @@ public async Task Edit(string id, [FromBody] ApiAppVM model) /// [ProducesResponseType(204)] [TypeFilter(typeof(PermissionCheckByBasicAttribute), - Arguments = new object[] { "App.Delete", Functions.App_Delete })] + Arguments = new object[] { Functions.App_Delete })] [HttpDelete("{id}")] public async Task Delete(string id) { @@ -186,7 +190,7 @@ public async Task Delete(string id) /// [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] [TypeFilter(typeof(PermissionCheckByBasicAttribute), - Arguments = new object[] { "Config.Publish_API", Functions.Config_Publish })] + Arguments = new object[] { Functions.Config_Publish })] [HttpPost("publish")] public async Task Publish(string appId, EnvString env) { @@ -216,6 +220,8 @@ public async Task Publish(string appId, EnvString env) /// Target environment. /// [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), + Arguments = new object[] { Functions.App_Read })] [HttpGet("Publish_History")] public async Task>> PublishHistory(string appId, EnvString env) { @@ -238,7 +244,7 @@ public async Task>> PublishHistor /// [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] [TypeFilter(typeof(PermissionCheckByBasicAttribute), - Arguments = new object[] { "Config.Rollback_API", Functions.Config_Publish })] + Arguments = new object[] { Functions.Config_Offline })] [HttpPost("rollback")] public async Task Rollback(string historyId, EnvString env) { diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/ConfigController.cs b/src/AgileConfig.Server.Apisite/Controllers/api/ConfigController.cs index c16c68b4..1a7fefd2 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/ConfigController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/ConfigController.cs @@ -95,6 +95,8 @@ public async Task>> GetAppConfig(string appId, [F /// [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] [HttpGet] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), + Arguments = new object[] { Functions.Config_Read })] public async Task>> GetConfigs(string appId, EnvString env) { ArgumentException.ThrowIfNullOrEmpty(appId); @@ -112,6 +114,8 @@ public async Task>> GetConfigs(string appId, EnvS /// [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] [HttpGet("{id}")] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), + Arguments = new object[] { Functions.Config_Read })] public async Task> GetConfig(string id, EnvString env) { ArgumentException.ThrowIfNullOrEmpty(id); @@ -131,7 +135,7 @@ public async Task> GetConfig(string id, EnvString env) [ProducesResponseType(201)] [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] [TypeFilter(typeof(PermissionCheckByBasicAttribute), - Arguments = new object[] { "Config.Add", Functions.Config_Add })] + Arguments = new object[] { Functions.Config_Add })] [HttpPost] public async Task Add([FromBody] ApiConfigVM model, EnvString env) { @@ -170,7 +174,7 @@ public async Task Add([FromBody] ApiConfigVM model, EnvString env /// [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] [TypeFilter(typeof(PermissionCheckByBasicAttribute), - Arguments = new object[] { "Config.Edit", Functions.Config_Edit })] + Arguments = new object[] { Functions.Config_Edit })] [HttpPut("{id}")] public async Task Edit(string id, [FromBody] ApiConfigVM model, EnvString env) { @@ -208,7 +212,7 @@ public async Task Edit(string id, [FromBody] ApiConfigVM model, E [ProducesResponseType(204)] [TypeFilter(typeof(AdmBasicAuthenticationAttribute))] [TypeFilter(typeof(PermissionCheckByBasicAttribute), - Arguments = new object[] { "Config.Delete", Functions.Config_Delete })] + Arguments = new object[] { Functions.Config_Delete })] [HttpDelete("{id}")] public async Task Delete(string id, EnvString env) { diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/NodeController.cs b/src/AgileConfig.Server.Apisite/Controllers/api/NodeController.cs index 4940adb8..cbba705b 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/NodeController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/NodeController.cs @@ -55,7 +55,7 @@ public async Task>> GetAll() /// Node payload. /// [ProducesResponseType(201)] - [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { "Node.Add", Functions.Node_Add })] + [TypeFilter(typeof(PermissionCheckByBasicAttribute), Arguments = new object[] { Functions.Node_Add })] [HttpPost] public async Task Add([FromBody] ApiNodeVM model) { @@ -91,7 +91,7 @@ public async Task Add([FromBody] ApiNodeVM model) /// [ProducesResponseType(204)] [TypeFilter(typeof(PermissionCheckByBasicAttribute), - Arguments = new object[] { "Node.Delete", Functions.Node_Delete })] + Arguments = new object[] { Functions.Node_Delete })] [HttpDelete] public async Task Delete([FromQuery] string address) { diff --git a/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs b/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs index 996fcb6e..77d706c5 100644 --- a/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs +++ b/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs @@ -14,182 +14,16 @@ namespace AgileConfig.Server.Apisite.Filters; public class PermissionCheckAttribute : ActionFilterAttribute { - protected const string AppMatchPatten = "APP_{0}_{1}"; - - /// - /// Static mapping of permission keys to delegates that extract the appId from action parameters. - /// Attributes cannot accept delegates directly, so the functions are predefined here. - /// - protected static readonly - FrozenDictionary> - GetAppIdParamFuncs = - new Dictionary> - { - { - "Config.Add", (args, premission, config) => - { - var model = args.ActionArguments["model"]; - return (model as IAppIdModel)?.AppId; - } - }, - { - "Config.AddRange", (args, permission, config) => - { - var model = args.ActionArguments["model"]; - return (model as List)?.FirstOrDefault()?.AppId; - } - }, - { - "Config.EnvSync", (args, permission, config) => - { - var appId = args.ActionArguments["appId"]; - return appId?.ToString(); - } - }, - { - "Config.Edit", (args, permission, config) => - { - var model = args.ActionArguments["model"]; - return (model as IAppIdModel)?.AppId; - } - }, - { - "Config.Delete", (args, permission, configService) => - { - var id = args.ActionArguments["id"]; - var env = GetEnvFromArgs(args.ActionArguments, configService); - var config = configService.GetAsync(id.ToString(), env).GetAwaiter().GetResult(); - - return config.AppId; - } - }, - { - "Config.DeleteSome", (args, permission, configService) => - { - var ids = args.ActionArguments["ids"] as List; - var env = GetEnvFromArgs(args.ActionArguments, configService); - var config = configService.GetAsync(ids.FirstOrDefault(), env).GetAwaiter().GetResult(); - - return config.AppId; - } - }, - { - "Config.Offline", (args, permission, configService) => - { - var id = args.ActionArguments["configId"]; - var env = GetEnvFromArgs(args.ActionArguments, configService); - var config = configService.GetAsync(id.ToString(), env).GetAwaiter().GetResult(); - - return config.AppId; - } - }, - { - "Config.OfflineSome", (args, permission, configService) => - { - var ids = args.ActionArguments["configIds"] as List; - var id = ids?.FirstOrDefault(); - var env = GetEnvFromArgs(args.ActionArguments, configService); - var config = configService.GetAsync(ids.FirstOrDefault(), env).GetAwaiter().GetResult(); - - return config.AppId; - } - }, - { - "Config.Publish", (args, permission, configService) => - { - var model = args.ActionArguments["model"] as IAppIdModel; - - return model?.AppId; - } - }, - { - "Config.Publish_API", (args, permission, configService) => - { - var appId = args.ActionArguments["appId"]; - - return appId?.ToString(); - } - }, - { - "Config.Rollback", (args, permission, configService) => - { - var timelineId = args.ActionArguments["publishTimelineId"] as string; - var env = GetEnvFromArgs(args.ActionArguments, configService); - var detail = configService.GetPublishDetailByPublishTimelineIdAsync(timelineId, env) - .GetAwaiter().GetResult(); - return detail.FirstOrDefault()?.AppId; - } - }, - { - "Config.Rollback_API", (args, permission, configService) => - { - var timelineId = args.ActionArguments["historyId"] as string; - var env = GetEnvFromArgs(args.ActionArguments, configService); - var detail = configService.GetPublishDetailByPublishTimelineIdAsync(timelineId, env) - .GetAwaiter().GetResult(); - return detail.FirstOrDefault()?.AppId; - } - }, - { - "App.Add", (args, permission, configService) => "" - }, - { - "App.Edit", (args, permission, configService) => - { - var app = args.ActionArguments["model"] as IAppModel; - return app.Id; - } - }, - { - "App.Delete", (args, permission, configService) => - { - var id = args.ActionArguments["id"] as string; - return id; - } - }, - { - "App.DisableOrEnable", (args, permission, configService) => - { - var id = args.ActionArguments["id"] as string; - return id; - } - }, - { - "App.Auth", (args, permission, configService) => - { - var model = args.ActionArguments["model"] as IAppIdModel; - return model?.AppId; - } - }, - { - "Node.Add", (args, permission, configService) => - { - var id = args.ActionArguments["id"] as string; - return id; - } - }, - { - "Node.Delete", (args, permission, configService) => - { - var model = args.ActionArguments["model"] as IAppIdModel; - return model?.AppId; - } - } - }.ToFrozenDictionary(); - - private readonly string _actionName; private readonly IConfigService _configService; private readonly string _functionKey; private readonly IPermissionService _permissionService; - public PermissionCheckAttribute(IPermissionService permissionService, IConfigService configService, - string actionName, string functionKey) + public PermissionCheckAttribute(IPermissionService permissionService, IConfigService configService, string functionKey) { _permissionService = permissionService; _configService = configService; - _actionName = actionName; _functionKey = functionKey; } @@ -226,29 +60,12 @@ public override async Task OnActionExecutionAsync(ActionExecutingContext context var userFunctions = await _permissionService.GetUserPermission(userId); - //judge global - var matchKey = _functionKey; if (userFunctions.Contains(_functionKey)) { await base.OnActionExecutionAsync(context, next); return; } - var appId = ""; - var isAppAction = _actionName.StartsWith("App."); - if (!isAppAction && GetAppIdParamFuncs.TryGetValue(_actionName, out var func)) - appId = func(context, _permissionService, _configService); - - if (!isAppAction && !string.IsNullOrEmpty(appId)) - { - matchKey = string.Format(AppMatchPatten, appId, _functionKey); - if (userFunctions.Contains(matchKey)) - { - await base.OnActionExecutionAsync(context, next); - return; - } - } - //no permission context.HttpContext.Response.StatusCode = 403; context.Result = new ContentResult(); diff --git a/src/AgileConfig.Server.Apisite/Filters/PermissionCheckByBasicAttribute.cs b/src/AgileConfig.Server.Apisite/Filters/PermissionCheckByBasicAttribute.cs index 951cc6a6..5b62ff81 100644 --- a/src/AgileConfig.Server.Apisite/Filters/PermissionCheckByBasicAttribute.cs +++ b/src/AgileConfig.Server.Apisite/Filters/PermissionCheckByBasicAttribute.cs @@ -20,7 +20,7 @@ public PermissionCheckByBasicAttribute( IAdmBasicAuthService basicAuthService, IUserService userService, string actionName, - string functionKey) : base(permissionService, configService, actionName, functionKey) + string functionKey) : base(permissionService, configService, functionKey) { _userService = userService; _basicAuthService = basicAuthService; diff --git a/src/AgileConfig.Server.Apisite/Models/AppAuthVM.cs b/src/AgileConfig.Server.Apisite/Models/AppAuthVM.cs index 6a807c45..dc986a01 100644 --- a/src/AgileConfig.Server.Apisite/Models/AppAuthVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/AppAuthVM.cs @@ -6,9 +6,7 @@ namespace AgileConfig.Server.Apisite.Models; [ExcludeFromCodeCoverage] public class AppAuthVM : IAppIdModel { - public List EditConfigPermissionUsers { get; set; } - - public List PublishConfigPermissionUsers { get; set; } + public List AuthorizedUsers { get; set; } public string AppId { get; set; } -} \ No newline at end of file +} diff --git a/src/AgileConfig.Server.IService/IAppService.cs b/src/AgileConfig.Server.IService/IAppService.cs index f0032867..7bade8c6 100644 --- a/src/AgileConfig.Server.IService/IAppService.cs +++ b/src/AgileConfig.Server.IService/IAppService.cs @@ -28,11 +28,11 @@ public interface IAppService : IDisposable Task<(List Apps, long Count)> SearchAsync(string id, string name, string group, string sortField, string ascOrDesc, - int current, int pageSize); + int current, int pageSize, string userId, bool isAdmin); Task<(List GroupedApps, long Count)> SearchGroupedAsync(string id, string name, string group, string sortField, string ascOrDesc, - int current, int pageSize); + int current, int pageSize, string userId, bool isAdmin); Task> GetAllInheritancedAppsAsync(); @@ -42,9 +42,9 @@ public interface IAppService : IDisposable Task> GetInheritancedFromAppsAsync(string appId); - Task SaveUserAppAuth(string appId, List userIds, string permission); + Task SaveUserAppAuth(string appId, List userIds); - Task> GetUserAppAuth(string appId, string permission); + Task> GetUserAppAuth(string appId); Task> GetAppGroups(); -} \ No newline at end of file +} diff --git a/src/AgileConfig.Server.Service/AppService.cs b/src/AgileConfig.Server.Service/AppService.cs index 5f5dcc4a..3caeb71e 100644 --- a/src/AgileConfig.Server.Service/AppService.cs +++ b/src/AgileConfig.Server.Service/AppService.cs @@ -129,7 +129,7 @@ public Task> GetAllAppsAsync() public async Task<(List Apps, long Count)> SearchAsync(string id, string name, string group, string sortField, string ascOrDesc, - int current, int pageSize) + int current, int pageSize, string userId, bool isAdmin) { Expression> exp = app => true; @@ -139,6 +139,14 @@ public Task> GetAllAppsAsync() if (!string.IsNullOrWhiteSpace(group)) exp = exp.And(a => a.Group == group); + if (!isAdmin) + { + if (string.IsNullOrWhiteSpace(userId)) return (new List(), 0); + + var authorizedAppIds = await GetUserAuthorizedAppIds(userId); + exp = exp.And(a => a.Creator == userId || authorizedAppIds.Contains(a.Id)); + } + if (string.IsNullOrWhiteSpace(ascOrDesc)) ascOrDesc = "asc"; var apps = await _appRepository.QueryPageAsync(exp, current, pageSize, sortField, @@ -150,7 +158,7 @@ public Task> GetAllAppsAsync() public async Task<(List GroupedApps, long Count)> SearchGroupedAsync(string id, string name, string group, string sortField, string ascOrDesc, int current, - int pageSize) + int pageSize, string userId, bool isAdmin) { Expression> exp = app => true; @@ -160,6 +168,14 @@ public Task> GetAllAppsAsync() if (!string.IsNullOrWhiteSpace(group)) exp = exp.And(a => a.Group == group); + if (!isAdmin) + { + if (string.IsNullOrWhiteSpace(userId)) return (new List(), 0); + + var authorizedAppIds = await GetUserAuthorizedAppIds(userId); + exp = exp.And(a => a.Creator == userId || authorizedAppIds.Contains(a.Id)); + } + var apps = await _appRepository.QueryAsync(exp); var appGroups = apps.GroupBy(x => x.Group); @@ -275,7 +291,7 @@ public async Task UpdateAsync(App app, List appInheritanc return true; } - public async Task SaveUserAppAuth(string appId, List userIds, string permission) + public async Task SaveUserAppAuth(string appId, List userIds) { var userAppAuthList = new List(); if (userIds == null) userIds = new List(); @@ -286,13 +302,13 @@ public async Task SaveUserAppAuth(string appId, List userIds, stri Id = Guid.NewGuid().ToString("N"), AppId = appId, UserId = userId, - Permission = permission + Permission = string.Empty }); var removeApps = - await _userAppAuthRepository.QueryAsync(x => x.AppId == appId && x.Permission == permission); + await _userAppAuthRepository.QueryAsync(x => x.AppId == appId); await _userAppAuthRepository.DeleteAsync(removeApps); - await _userAppAuthRepository.InsertAsync(userAppAuthList); + if (userAppAuthList.Any()) await _userAppAuthRepository.InsertAsync(userAppAuthList); return true; } @@ -305,16 +321,24 @@ public void Dispose() _userRepository.Dispose(); } - public async Task> GetUserAppAuth(string appId, string permission) + private async Task> GetUserAuthorizedAppIds(string userId) { - var auths = await _userAppAuthRepository.QueryAsync(x => x.AppId == appId && x.Permission == permission); + var authorizedAppIds = new List(); + if (string.IsNullOrWhiteSpace(userId)) return authorizedAppIds; - var users = new List(); - foreach (var auth in auths) - { - var user = await _userRepository.GetAsync(auth.UserId); - if (user != null) users.Add(user); - } + var auths = await _userAppAuthRepository.QueryAsync(x => x.UserId == userId); + + return auths.Select(x => x.AppId).Distinct().ToList(); + } + + public async Task> GetUserAppAuth(string appId) + { + var auths = await _userAppAuthRepository.QueryAsync(x => x.AppId == appId); + var userIds = auths.Select(x => x.UserId).Distinct().ToList(); + + if (!userIds.Any()) return new List(); + + var users = await _userRepository.QueryAsync(u => userIds.Contains(u.Id)); return users; } @@ -325,4 +349,4 @@ public async Task> GetAppGroups() var groups = apps.GroupBy(x => x.Group).Select(x => x.Key); return groups.Where(x => !string.IsNullOrEmpty(x)).ToList(); } -} \ No newline at end of file +} diff --git a/src/AgileConfig.Server.Service/PermissionService.cs b/src/AgileConfig.Server.Service/PermissionService.cs index 388fda45..7e835c51 100644 --- a/src/AgileConfig.Server.Service/PermissionService.cs +++ b/src/AgileConfig.Server.Service/PermissionService.cs @@ -2,34 +2,27 @@ using System.Linq; using System.Threading.Tasks; using AgileConfig.Server.Data.Abstraction; -using AgileConfig.Server.Data.Entity; using AgileConfig.Server.IService; namespace AgileConfig.Server.Service; public class PermissionService : IPermissionService { - private readonly IAppRepository _appRepository; private readonly IFunctionRepository _functionRepository; - private readonly IRoleDefinitionRepository _roleDefinitionRepository; private readonly IRoleFunctionRepository _roleFunctionRepository; private readonly IUserAppAuthRepository _userAppAuthRepository; private readonly IUserRoleRepository _userRoleRepository; public PermissionService( IUserRoleRepository userRoleRepository, - IRoleDefinitionRepository roleDefinitionRepository, IRoleFunctionRepository roleFunctionRepository, IFunctionRepository functionRepository, - IUserAppAuthRepository userAppAuthRepository, - IAppRepository appRepository) + IUserAppAuthRepository userAppAuthRepository) { _userRoleRepository = userRoleRepository; - _roleDefinitionRepository = roleDefinitionRepository; _roleFunctionRepository = roleFunctionRepository; _functionRepository = functionRepository; _userAppAuthRepository = userAppAuthRepository; - _appRepository = appRepository; } @@ -40,17 +33,20 @@ public PermissionService( /// List of permission codes granted to the user. public async Task> GetUserPermission(string userId) { + var functionCodes = new List(); + var userRoles = await _userRoleRepository.QueryAsync(x => x.UserId == userId); var roleIds = userRoles.Select(x => x.RoleId).Distinct().ToList(); - if (!roleIds.Any()) return new List(); - - var roleFunctions = await _roleFunctionRepository.QueryAsync(x => roleIds.Contains(x.RoleId)); - var functionIds = roleFunctions.Select(rf => rf.FunctionId).Distinct().ToList(); + if (roleIds.Any()) + { + var roleFunctions = await _roleFunctionRepository.QueryAsync(x => roleIds.Contains(x.RoleId)); + var functionIds = roleFunctions.Select(rf => rf.FunctionId).Distinct().ToList(); - var functions = await _functionRepository.QueryAsync(f => functionIds.Contains(f.Id)); - var functionCodes = functions.Select(f => f.Code).Distinct().ToList(); + var functions = await _functionRepository.QueryAsync(f => functionIds.Contains(f.Id)); + functionCodes = functions.Select(f => f.Code).Distinct().ToList(); + } - return functionCodes; + return functionCodes.Distinct().ToList(); } /// @@ -82,25 +78,4 @@ public async Task> GetUserCategories(string userId) public string EditConfigPermissionKey => "EDIT_CONFIG"; public string PublishConfigPermissionKey => "PUBLISH_CONFIG"; - - /// - /// Retrieve applications where the user has been explicitly authorized. - /// - /// Identifier of the user whose application authorizations are requested. - /// Permission key used to filter authorized applications. - /// List of applications the user can access for the specified permission. - private async Task> GetUserAuthApp(string userId, string authPermissionKey) - { - var apps = new List(); - var userAuths = - await _userAppAuthRepository.QueryAsync(x => x.UserId == userId && x.Permission == authPermissionKey); - foreach (var appAuth in userAuths) - { - var app = await _appRepository.GetAsync(appAuth.AppId); - if (app != null) apps.Add(app); - } - - return apps; - } - } diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/components/Authorized/AuthorizedElement.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/components/Authorized/AuthorizedElement.tsx index 6525299c..a222779e 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/components/Authorized/AuthorizedElement.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/components/Authorized/AuthorizedElement.tsx @@ -7,18 +7,19 @@ type AuthorizedProps = { noMatch?: React.ReactNode; }; -export const checkUserPermission = (functions:string[],judgeKey:string, appid:string|undefined)=>{ +export const checkUserPermission = (functions:string[] | undefined,judgeKey:string, appid:string|undefined)=>{ let appId = ''; + const fnList = functions ?? []; if (appid) { appId = appid ; } // Check for global permission (without GLOBAL_ prefix) - let key = functions.find(x=>x === judgeKey); + let key = fnList.find(x=>x === judgeKey); if (key) return true; // Check for app-specific permission let matchKey = ('APP_'+ appId + '_' + judgeKey); - key = functions.find(x=>x === matchKey); + key = fnList.find(x=>x === matchKey); if (key) return true; return false; diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx index 40a9c100..b911d2fb 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx @@ -85,6 +85,7 @@ const BasicLayout: React.FC = (props) => { }); } }, []); + /** Init variables */ const handleMenuCollapse = (payload: boolean): void => { @@ -108,6 +109,7 @@ const BasicLayout: React.FC = (props) => { return ( = (props) => { const intl = useIntl(); const [users, setUsers] = useState<{ label: string; value: string }[]>(); const [userAppAuthState, setUserAppAuthState] = useState(); + const canEditAuth = checkUserPermission(getFunctions(), functionKeys.App_Auth, props.value?.id); // Optimize useEffect indentation useEffect(() => { + const creatorId = props.value?.creator; allUsers().then((resp) => { - const usermp = resp.data.map((x: { userName: string; id: string; team: string }) => { - return { label: x.userName + ' - ' + (x.team ? x.team : ''), value: x.id }; - }); + const usermp = resp.data + .filter((x: { id: string }) => x.id !== creatorId) + .map((x: { userName: string; id: string; team: string }) => { + return { label: x.userName + ' - ' + (x.team ? x.team : ''), value: x.id }; + }); setUsers(usermp); }); - }, []); + }, [props.value?.creator]); useEffect(() => { if (props.value?.id) { @@ -37,8 +41,7 @@ const UserAuth: React.FC = (props) => { getUserAppAuth(appId).then((resp) => { var auth: UserAppAuth = { appId: appId, - editConfigPermissionUsers: resp.data.editConfigPermissionUsers, - publishConfigPermissionUsers: resp.data.publishConfigPermissionUsers, + authorizedUsers: resp.data?.authorizedUsers ?? [], }; setUserAppAuthState(auth); }); @@ -52,7 +55,7 @@ const UserAuth: React.FC = (props) => { initialValues={userAppAuthState} visible={props.userAuthModalVisible} submitter={ - checkUserPermission(getFunctions(), functionKeys.App_Auth, props.value?.id) ? { + canEditAuth ? { submitButtonProps: {}, } : { submitButtonProps: { style: { display: 'none' } }, @@ -69,27 +72,10 @@ const UserAuth: React.FC = (props) => { { - const label = option?.label?.toString(); - if (item && label) { - return label.indexOf(item) >= 0; - } - return false; - }, - }} - /> - - { const label = option?.label?.toString(); diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/data.d.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/data.d.ts index 036be97d..e478aae1 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/data.d.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/data.d.ts @@ -31,6 +31,5 @@ export type AppListResult = { export type UserAppAuth = { appId: string, - editConfigPermissionUsers?: string[], - publishConfigPermissionUsers?: string[] + authorizedUsers?: string[] } diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/index.tsx index 02130e02..c4ff5438 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Apps/index.tsx @@ -38,7 +38,7 @@ import UserAuth from './comps/userAuth'; import AuthorizedEle from '@/components/Authorized/AuthorizedElement'; import functionKeys from '@/models/functionKeys'; import { current } from '@/services/user'; -import { setAuthority, setFunctions } from '@/utils/authority'; +import { getAuthority, getUserInfo, setAuthority, setFunctions } from '@/utils/authority'; import { RequireFunction } from '@/utils/permission'; const { confirm } = Modal; @@ -197,6 +197,17 @@ const appList: React.FC = (props) => { const [newAppGroupName, setNewAppGroupName] = useState(''); const [appGroupsEnums, setAppGroupsEnums] = useState<{}>({}); const [tableGrouped, setTableGrouped] = useState(false); + const currentUser = getUserInfo(); + const isAdmin = () => { + const roles = getAuthority(); + if (!roles) return false; + if (Array.isArray(roles)) return roles.some(r => typeof r === 'string' && r.toLowerCase().includes('admin')); + if (typeof roles === 'string') return roles.toLowerCase().includes('admin'); + return false; + }; + const canAuth = (record: AppListItem) => { + return isAdmin() || (!!currentUser?.userid && record.creator === currentUser.userid); + }; useEffect(() => { getAppGroups().then((x) => { @@ -360,7 +371,7 @@ const appList: React.FC = (props) => { }), valueType: 'option', render: (text, record, _, action) => [ - + { })} , - + canAuth(record)}> { diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Configs/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Configs/index.tsx index b9341584..08bc48f0 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Configs/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Configs/index.tsx @@ -371,7 +371,7 @@ const configs: React.FC = (props: any) => { width: 150, valueType: 'option', render: (text, record, _, action) => [ - + { setCurrentRow(record); @@ -381,7 +381,7 @@ const configs: React.FC = (props: any) => { {intl.formatMessage({ id: 'pages.configs.table.cols.action.edit' })} , - + { delConfig(record); @@ -390,7 +390,7 @@ const configs: React.FC = (props: any) => { {intl.formatMessage({ id: 'pages.configs.table.cols.action.delete' })} , - + { @@ -504,13 +504,13 @@ const configs: React.FC = (props: any) => { } toolBarRender={() => [ - + , - + , - + {selectedRowsState.filter((x) => x.editStatus !== 10).length > 0 ? ( , - + diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.ts index 722bdee7..a4dff453 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.ts @@ -1,9 +1,10 @@ import React, { ReactNode, ReactElement } from 'react'; -import { hasFunction } from './authority'; +import { checkUserPermission } from '@/components/Authorized/AuthorizedElement'; +import { getFunctions } from './authority'; // Hook: check a single function permission key. -export function useFunction(fnKey: string): boolean { - return hasFunction(fnKey); +export function useFunction(fnKey: string, appId?: string): boolean { + return checkUserPermission(getFunctions(), fnKey, appId); } // Props for RequireFunction component @@ -11,11 +12,13 @@ export interface RequireFunctionProps { fn: string; fallback?: ReactNode; children?: ReactNode; + appId?: string; + extraCheck?: () => boolean; } // Component without JSX fragments (compatible with .ts) -export const RequireFunction: React.FC = ({ fn, fallback = null, children }): ReactElement | null => { - const allowed = useFunction(fn); +export const RequireFunction: React.FC = ({ fn, fallback = null, children, appId, extraCheck }): ReactElement | null => { + const allowed = useFunction(fn, appId) && (!extraCheck || extraCheck()); const safeChildren: ReactNode = children === undefined ? null : children; const safeFallback: ReactNode = fallback === undefined ? null : fallback; return allowed @@ -25,10 +28,10 @@ export const RequireFunction: React.FC = ({ fn, fallback = // ANY logic export function hasAnyFunction(...fnKeys: string[]): boolean { - return fnKeys.some(k => hasFunction(k)); + return fnKeys.some(k => useFunction(k)); } // ALL logic export function hasAllFunctions(...fnKeys: string[]): boolean { - return fnKeys.every(k => hasFunction(k)); + return fnKeys.every(k => useFunction(k)); } diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.tsx index c243298b..c448ed27 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/utils/permission.tsx @@ -1,9 +1,10 @@ import React, { ReactNode } from 'react'; -import { hasFunction } from './authority'; +import { checkUserPermission } from '@/components/Authorized/AuthorizedElement'; +import { getFunctions } from './authority'; /** Hook: check single function permission key */ -export function useFunction(fnKey: string): boolean { - return hasFunction(fnKey); +export function useFunction(fnKey: string, appId?: string): boolean { + return checkUserPermission(getFunctions(), fnKey, appId); } interface RequireFunctionProps { @@ -18,17 +19,17 @@ interface RequireFunctionProps { /** Component: conditionally render children if user has function permission */ export const RequireFunction: React.FC = ({ fn, fallback = null, children, appId, extraCheck }) => { - const allowed = useFunction(fn) && (!extraCheck || extraCheck()); + const allowed = useFunction(fn, appId) && (!extraCheck || extraCheck()); if (!allowed) return <>{fallback}; return <>{children}; }; /** ANY logic */ export function hasAnyFunction(...fnKeys: string[]): boolean { - return fnKeys.some(k => hasFunction(k)); + return fnKeys.some(k => useFunction(k)); } /** ALL logic */ export function hasAllFunctions(...fnKeys: string[]): boolean { - return fnKeys.every(k => hasFunction(k)); -} \ No newline at end of file + return fnKeys.every(k => useFunction(k)); +} diff --git a/test/ApiSiteTests/TestAppController.cs b/test/ApiSiteTests/TestAppController.cs index 9c07a31a..9313b5e5 100644 --- a/test/ApiSiteTests/TestAppController.cs +++ b/test/ApiSiteTests/TestAppController.cs @@ -28,7 +28,7 @@ public async Task TestAdd() var permissionService = new Mock(); var eventBus = new Mock(); - var ctl = new AppController(appService.Object, permissionService.Object, userService.Object, eventBus.Object); + var ctl = new AppController(appService.Object, userService.Object, eventBus.Object); ctl.ControllerContext.HttpContext = new DefaultHttpContext(); From af4c27f0b224ca0db230a3edf54402b702aa9c18 Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Sun, 23 Nov 2025 20:14:50 +0800 Subject: [PATCH 09/17] fix side meun display issue --- .../react-ui-antd/src/layouts/BasicLayout.tsx | 60 ++++++++++++------- .../react-ui-antd/src/models/user.ts | 1 + .../react-ui-antd/src/utils/authority.ts | 2 + 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx index b911d2fb..f9d75502 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/BasicLayout.tsx @@ -9,11 +9,11 @@ import { Settings, } from '@ant-design/pro-layout'; import ProLayout from '@ant-design/pro-layout'; -import React, { useEffect, useMemo, useRef } from 'react'; +import React, { useEffect, useMemo, useRef, useCallback } from 'react'; import { Dispatch, getIntl, getLocale } from 'umi'; import { Link, useIntl, connect, history } from 'umi'; import { Result, Button } from 'antd'; -import { getCategories, hasFunction } from '@/utils/authority'; +import { getCategories } from '@/utils/authority'; import Authorized from '@/utils/Authorized'; import RightContent from '@/components/GlobalHeader/RightContent'; import type { ConnectState } from '@/models/connect'; @@ -38,6 +38,7 @@ export type BasicLayoutProps = { route: ProLayoutProps['route'] & { authority: string[]; }; + categories?: string[]; settings: Settings; dispatch: Dispatch; } & ProLayoutProps; @@ -46,26 +47,6 @@ export type BasicLayoutContext = { [K in 'location']: BasicLayoutProps[K] } & { }; /** Use Authorized check all menu item */ -// Filter menu by categories stored from login (e.g., Application, Configuration, Node, Client, User, Role, Service, System) -const menuDataRender = (menuList: MenuDataItem[]): MenuDataItem[] => { - const cats = getCategories(); - return menuList - .filter(m => { - // category filter - const category = (m as any).category; - if (category && !cats.includes(category)) return false; - - return true; - }) - .map((item) => { - const localItem = { - ...item, - children: item.children ? menuDataRender(item.children) : undefined, - }; - return Authorized.check(item.authority, localItem, null) as MenuDataItem; - }); -}; - const BasicLayout: React.FC = (props) => { const { dispatch, @@ -76,6 +57,38 @@ const BasicLayout: React.FC = (props) => { }, } = props; + // keep categories in props to force re-render when user changes; fall back to persisted storage to avoid empty menu during bootstrap + const categories = useMemo(() => { + if (props.categories && props.categories.length) { + return props.categories; + } + return getCategories(); + }, [props.categories]); + + // Filter menu by categories stored from login (e.g., Application, Configuration, Node, Client, User, Role, Service, System) + const menuDataRender = useCallback( + (menuList: MenuDataItem[]): MenuDataItem[] => { + const cats = categories || []; + console.log('menuDataRender categories=', cats); + return menuList + .filter((m) => { + // category filter + const category = (m as any).category; + if (category && !cats.includes(category)) return false; + + return true; + }) + .map((item) => { + const localItem = { + ...item, + children: item.children ? menuDataRender(item.children) : undefined, + }; + return Authorized.check(item.authority, localItem, null) as MenuDataItem; + }); + }, + [categories], + ); + const menuDataRef = useRef([]); useEffect(() => { @@ -166,7 +179,8 @@ const BasicLayout: React.FC = (props) => { ); }; -export default connect(({ global, settings }: ConnectState) => ({ +export default connect(({ global, settings, user }: ConnectState) => ({ collapsed: global.collapsed, settings, + categories: user.currentUser?.currentCategories, }))(BasicLayout); diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/models/user.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/models/user.ts index b77da983..c06cc305 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/models/user.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/models/user.ts @@ -17,6 +17,7 @@ export type CurrentUser = { }[]; userid?: string; unreadCount?: number; + currentCategories?: string[]; }; export type UserModelState = { diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/utils/authority.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/utils/authority.ts index a97d2b29..a68ba2ac 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/utils/authority.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/utils/authority.ts @@ -60,6 +60,7 @@ export function setFunctions(authority: string | string[] ): void { // categories (business domains) control which left-menu entries are visible export function getCategories(str?: string): string[] { + console.log('getCategories called'); const catString = typeof str === 'undefined' && localStorage ? localStorage.getItem('antd-pro-categories') : str; let cats; @@ -77,6 +78,7 @@ export function getCategories(str?: string): string[] { } export function setCategories(categories: string | string[]): void { + console.log('setCategories called with', categories); const arr = typeof categories === 'string' ? [categories] : categories; const safeArr = arr || []; localStorage.setItem('antd-pro-categories', JSON.stringify(safeArr)); From 4324c3f2c19c9f94e89f0ef42401e8656db95bac Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Sun, 23 Nov 2025 20:50:32 +0800 Subject: [PATCH 10/17] Update version to 1.11.0 --- .../AgileConfig.Server.Apisite.csproj | 8 ++-- .../react-ui-antd/src/locales/en-US/pages.ts | 18 +++++++- .../react-ui-antd/src/locales/zh-CN/pages.ts | 19 ++++++++- .../react-ui-antd/src/pages/Role/index.tsx | 42 +++++++++++++++---- 4 files changed, 74 insertions(+), 13 deletions(-) diff --git a/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj b/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj index 3d9585a5..dc3f5062 100644 --- a/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj +++ b/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj @@ -3,11 +3,11 @@ net8.0 InProcess - 1.10.0 - 1.10.0 - 1.10.0 + 1.11.0 + 1.11.0 + 1.11.0 Linux - 1.10.0 + 1.11.0 kklldog kklldog diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts index 100bf5aa..79e3988f 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/en-US/pages.ts @@ -225,7 +225,14 @@ export default { 'pages.configs.rollback_warning': 'Note: This operation will clear all current pending configuration items', 'pages.configs.sync_failed': 'Sync failed', 'pages.configs.sync_from_to': 'Sync configurations from current {env} environment to:', - + 'pages.configs.version.status.add': 'Add', + 'pages.configs.version.status.edit': 'Edit', + 'pages.configs.version.status.delete': 'Delete', + 'pages.configs.version.status.committed': 'Committed', + 'pages.configs.version.editStatus': 'Change', + 'pages.configs.version.rolling_back': 'Rolling Back', + 'pages.configs.version.rollback_success': 'Rollback Success', + 'pages.configs.version.rollback_fail': 'Rollback Failed', 'pages.configs.confirm_publish_current': 'Are you sure to publish current', 'pages.configs.confirm_publish_wait_items': 'wait publish config items?', 'pages.configs.confirm_cancel_edit': 'Are you sure to cancel edit config', @@ -293,6 +300,15 @@ export default { 'pages.role.load_failed': 'Failed to load roles', 'pages.role.permissions.load_failed': 'Failed to load permissions', 'pages.role.permissions.all': 'All', + 'pages.role.permissionGroup.APP': 'App', + 'pages.role.permissionGroup.CONFIG': 'Config', + 'pages.role.permissionGroup.NODE': 'Node', + 'pages.role.permissionGroup.CLIENT': 'Client', + 'pages.role.permissionGroup.USER': 'User', + 'pages.role.permissionGroup.ROLE': 'Role', + 'pages.role.permissionGroup.SERVICE': 'Service', + 'pages.role.permissionGroup.LOG': 'Log', + 'pages.role.permissionGroup.OTHER': 'Other', 'pages.role.permissions.APP_ADD': 'Add App', 'pages.role.permissions.APP_EDIT': 'Edit App', 'pages.role.permissions.APP_DELETE': 'Delete App', diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts index a2ba91b7..4a168685 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts @@ -245,6 +245,14 @@ export default { 'pages.config.waitpublish_at_least_one':'请至少选中一个待上线配置项', 'pages.config.online_at_least_one':'请至少选中一个已上线配置项', + 'pages.configs.version.status.add': '新建', + 'pages.configs.version.status.edit': '编辑', + 'pages.configs.version.status.delete': '删除', + 'pages.configs.version.status.committed': '已提交', + 'pages.configs.version.editStatus': '变更', + 'pages.configs.version.rolling_back': '正在回滚', + 'pages.configs.version.rollback_success': '回滚成功', + 'pages.configs.version.rollback_fail': '回滚失败', // User management 'pages.user.table.cols.username': '用户名', @@ -267,7 +275,7 @@ export default { 'pages.user.status.deleted': '已删除', 'pages.user.confirm_reset': '确定重置用户', 'pages.user.confirm_delete': '确定删除用户', - 'pages.user.reset_password_default': '的密码为默认密码【123456】?', + 'pages.user.reset_password_default': '的密码为默认密码【123456】?', 'pages.role.table.cols.name': '名称', 'pages.role.table.cols.description': '描述', 'pages.role.table.cols.system': '系统角色', @@ -291,6 +299,15 @@ export default { 'pages.role.load_failed': '加载角色失败', 'pages.role.permissions.load_failed': '加载权限列表失败', 'pages.role.permissions.all': '所有权限', + 'pages.role.permissionGroup.APP': '应用', + 'pages.role.permissionGroup.CONFIG': '配置', + 'pages.role.permissionGroup.NODE': '节点', + 'pages.role.permissionGroup.CLIENT': '客户端', + 'pages.role.permissionGroup.USER': '用户', + 'pages.role.permissionGroup.ROLE': '角色', + 'pages.role.permissionGroup.SERVICE': '服务', + 'pages.role.permissionGroup.LOG': '日志', + 'pages.role.permissionGroup.OTHER': '其他', 'pages.role.permissions.APP_ADD': '应用新增', 'pages.role.permissions.APP_EDIT': '应用编辑', 'pages.role.permissions.APP_DELETE': '应用删除', diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx index 627c61a1..cf78be19 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx @@ -3,7 +3,7 @@ import { PageContainer } from '@ant-design/pro-layout'; import ProTable, { ActionType, ProColumns } from '@ant-design/pro-table'; import { Button, message, Modal, Space, Tag } from 'antd'; import { ModalForm, ProFormSelect, ProFormText } from '@ant-design/pro-form'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import { useIntl } from 'umi'; import type { RoleFormValues, RoleItem } from './data'; import { createRole, deleteRole, fetchSupportedRolePermissions, queryRoles, updateRole } from '@/services/role'; @@ -23,6 +23,13 @@ const RolePage: React.FC = () => { loadPermissions(); }, []); + const permissionGroupLabel = (prefix: string) => { + return intl.formatMessage({ + id: `pages.role.permissionGroup.${prefix}`, + defaultMessage: prefix, + }); + }; + const loadPermissions = async () => { try { const response = await fetchSupportedRolePermissions(); @@ -36,10 +43,31 @@ const RolePage: React.FC = () => { } }; - const permissionOptions = supportedPermissions.map((item) => ({ - value: item, - label: intl.formatMessage({ id: `pages.role.permissions.${item}`, defaultMessage: item }), - })); + const groupedPermissionOptions = useMemo(() => { + const groupMap: Record< + string, + { + label: string; + options: { value: string; label: string }[]; + } + > = {}; + + supportedPermissions.forEach((item) => { + const prefix = item.split('_')[0] || 'OTHER'; + if (!groupMap[prefix]) { + groupMap[prefix] = { + label: permissionGroupLabel(prefix), + options: [], + }; + } + groupMap[prefix].options.push({ + value: item, + label: intl.formatMessage({ id: `pages.role.permissions.${item}`, defaultMessage: item }), + }); + }); + + return Object.values(groupMap); + }, [intl, supportedPermissions]); const handleCreate = async (values: RoleFormValues) => { const hide = message.loading(intl.formatMessage({ id: 'saving', defaultMessage: 'Saving...' })); @@ -227,7 +255,7 @@ const RolePage: React.FC = () => { name="functions" label={intl.formatMessage({ id: 'pages.role.form.functions', defaultMessage: 'Permissions' })} mode="multiple" - options={permissionOptions} + options={groupedPermissionOptions} /> @@ -268,7 +296,7 @@ const RolePage: React.FC = () => { name="functions" label={intl.formatMessage({ id: 'pages.role.form.functions', defaultMessage: 'Permissions' })} mode="multiple" - options={permissionOptions} + options={groupedPermissionOptions} /> )} From 3cf3208a73b6b38266e3ad3126bcf8bb30a6808d Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Mon, 24 Nov 2025 01:11:06 +0800 Subject: [PATCH 11/17] Update NET version to 10 --- .../Agile.Config.Protocol.csproj | 2 +- .../AgileConfig.Server.Apisite.csproj | 34 +++++++++---------- src/AgileConfig.Server.Apisite/Startup.cs | 2 +- .../AgileConfig.Server.Common.csproj | 8 ++--- ...AgileConfig.Server.Data.Abstraction.csproj | 2 +- .../AgileConfig.Server.Data.Entity.csproj | 6 ++-- .../AgileConfig.Server.Data.Freesql.csproj | 20 +++++------ .../AgileConfig.Server.Data.Mongodb.csproj | 12 +++---- ...nfig.Server.Data.Repository.Freesql.csproj | 2 +- ...nfig.Server.Data.Repository.Mongodb.csproj | 4 +-- ...fig.Server.Data.Repository.Selector.csproj | 2 +- .../AgileConfig.Server.Event.csproj | 2 +- .../AgileConfig.Server.EventHandler.csproj | 2 +- .../AgileConfig.Server.IService.csproj | 2 +- .../AgileConfig.Server.OIDC.csproj | 4 +-- .../AgileConfig.Server.Service.csproj | 6 ++-- .../src/layouts/compos/LayoutFooter.tsx | 2 +- .../AgileConfig.Server.CommonTests.csproj | 8 ++--- ...Config.Server.Data.AbstractionTests.csproj | 13 ++++--- ...gileConfig.Server.Data.FreesqlTests.csproj | 13 ++++--- .../AgileConfig.Server.ServiceTests.csproj | 18 +++++----- test/ApiSiteTests/ApiSiteTests.csproj | 12 +++---- test/ApiSiteTests/TestAppController.cs | 2 +- 23 files changed, 92 insertions(+), 86 deletions(-) diff --git a/src/Agile.Config.Protocol/Agile.Config.Protocol.csproj b/src/Agile.Config.Protocol/Agile.Config.Protocol.csproj index 58990cd5..4340f623 100644 --- a/src/Agile.Config.Protocol/Agile.Config.Protocol.csproj +++ b/src/Agile.Config.Protocol/Agile.Config.Protocol.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 diff --git a/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj b/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj index dc3f5062..9e46ce95 100644 --- a/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj +++ b/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 InProcess 1.11.0 1.11.0 @@ -26,22 +26,22 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/src/AgileConfig.Server.Apisite/Startup.cs b/src/AgileConfig.Server.Apisite/Startup.cs index 6c498422..b8c03a93 100644 --- a/src/AgileConfig.Server.Apisite/Startup.cs +++ b/src/AgileConfig.Server.Apisite/Startup.cs @@ -19,7 +19,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using OpenTelemetry.Resources; namespace AgileConfig.Server.Apisite; diff --git a/src/AgileConfig.Server.Common/AgileConfig.Server.Common.csproj b/src/AgileConfig.Server.Common/AgileConfig.Server.Common.csproj index 3609ca0c..d8bc24cb 100644 --- a/src/AgileConfig.Server.Common/AgileConfig.Server.Common.csproj +++ b/src/AgileConfig.Server.Common/AgileConfig.Server.Common.csproj @@ -1,14 +1,14 @@  - net8.0 + net10.0 - - - + + + diff --git a/src/AgileConfig.Server.Data.Abstraction/AgileConfig.Server.Data.Abstraction.csproj b/src/AgileConfig.Server.Data.Abstraction/AgileConfig.Server.Data.Abstraction.csproj index ef10d473..e582965b 100644 --- a/src/AgileConfig.Server.Data.Abstraction/AgileConfig.Server.Data.Abstraction.csproj +++ b/src/AgileConfig.Server.Data.Abstraction/AgileConfig.Server.Data.Abstraction.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 enable enable diff --git a/src/AgileConfig.Server.Data.Entity/AgileConfig.Server.Data.Entity.csproj b/src/AgileConfig.Server.Data.Entity/AgileConfig.Server.Data.Entity.csproj index 72e94300..ab10923b 100644 --- a/src/AgileConfig.Server.Data.Entity/AgileConfig.Server.Data.Entity.csproj +++ b/src/AgileConfig.Server.Data.Entity/AgileConfig.Server.Data.Entity.csproj @@ -1,12 +1,12 @@  - net8.0 + net10.0 - - + + diff --git a/src/AgileConfig.Server.Data.Freesql/AgileConfig.Server.Data.Freesql.csproj b/src/AgileConfig.Server.Data.Freesql/AgileConfig.Server.Data.Freesql.csproj index 10130e19..c3092ea0 100644 --- a/src/AgileConfig.Server.Data.Freesql/AgileConfig.Server.Data.Freesql.csproj +++ b/src/AgileConfig.Server.Data.Freesql/AgileConfig.Server.Data.Freesql.csproj @@ -2,19 +2,19 @@ enable - net8.0 + net10.0 - - - - - - - - - + + + + + + + + + diff --git a/src/AgileConfig.Server.Data.Mongodb/AgileConfig.Server.Data.Mongodb.csproj b/src/AgileConfig.Server.Data.Mongodb/AgileConfig.Server.Data.Mongodb.csproj index 23e3ca94..ec151e97 100644 --- a/src/AgileConfig.Server.Data.Mongodb/AgileConfig.Server.Data.Mongodb.csproj +++ b/src/AgileConfig.Server.Data.Mongodb/AgileConfig.Server.Data.Mongodb.csproj @@ -1,16 +1,16 @@  - net8.0 - enable + net10.0 + enable enable - - - - + + + + diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/AgileConfig.Server.Data.Repository.Freesql.csproj b/src/AgileConfig.Server.Data.Repository.Freesql/AgileConfig.Server.Data.Repository.Freesql.csproj index 929a7219..c1b65e57 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/AgileConfig.Server.Data.Repository.Freesql.csproj +++ b/src/AgileConfig.Server.Data.Repository.Freesql/AgileConfig.Server.Data.Repository.Freesql.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 enable disable diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/AgileConfig.Server.Data.Repository.Mongodb.csproj b/src/AgileConfig.Server.Data.Repository.Mongodb/AgileConfig.Server.Data.Repository.Mongodb.csproj index ef554dcd..3f1b19d5 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/AgileConfig.Server.Data.Repository.Mongodb.csproj +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/AgileConfig.Server.Data.Repository.Mongodb.csproj @@ -1,8 +1,8 @@  - net8.0 - enable + net10.0 + enable enable diff --git a/src/AgileConfig.Server.Data.Repository.Selector/AgileConfig.Server.Data.Repository.Selector.csproj b/src/AgileConfig.Server.Data.Repository.Selector/AgileConfig.Server.Data.Repository.Selector.csproj index 7f76c28c..2724482b 100644 --- a/src/AgileConfig.Server.Data.Repository.Selector/AgileConfig.Server.Data.Repository.Selector.csproj +++ b/src/AgileConfig.Server.Data.Repository.Selector/AgileConfig.Server.Data.Repository.Selector.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 enable disable diff --git a/src/AgileConfig.Server.Event/AgileConfig.Server.Event.csproj b/src/AgileConfig.Server.Event/AgileConfig.Server.Event.csproj index 84ab27b0..a4b3949a 100644 --- a/src/AgileConfig.Server.Event/AgileConfig.Server.Event.csproj +++ b/src/AgileConfig.Server.Event/AgileConfig.Server.Event.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 enable disable diff --git a/src/AgileConfig.Server.EventHandler/AgileConfig.Server.EventHandler.csproj b/src/AgileConfig.Server.EventHandler/AgileConfig.Server.EventHandler.csproj index d3171c79..923ee151 100644 --- a/src/AgileConfig.Server.EventHandler/AgileConfig.Server.EventHandler.csproj +++ b/src/AgileConfig.Server.EventHandler/AgileConfig.Server.EventHandler.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 enable enable diff --git a/src/AgileConfig.Server.IService/AgileConfig.Server.IService.csproj b/src/AgileConfig.Server.IService/AgileConfig.Server.IService.csproj index 0f5bb84b..7a9040fd 100644 --- a/src/AgileConfig.Server.IService/AgileConfig.Server.IService.csproj +++ b/src/AgileConfig.Server.IService/AgileConfig.Server.IService.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 diff --git a/src/AgileConfig.Server.OIDC/AgileConfig.Server.OIDC.csproj b/src/AgileConfig.Server.OIDC/AgileConfig.Server.OIDC.csproj index 4ee201ca..a0b6619d 100644 --- a/src/AgileConfig.Server.OIDC/AgileConfig.Server.OIDC.csproj +++ b/src/AgileConfig.Server.OIDC/AgileConfig.Server.OIDC.csproj @@ -1,14 +1,14 @@ - net8.0 + net10.0 enable disable - + diff --git a/src/AgileConfig.Server.Service/AgileConfig.Server.Service.csproj b/src/AgileConfig.Server.Service/AgileConfig.Server.Service.csproj index d89198d4..21bcd015 100644 --- a/src/AgileConfig.Server.Service/AgileConfig.Server.Service.csproj +++ b/src/AgileConfig.Server.Service/AgileConfig.Server.Service.csproj @@ -1,12 +1,12 @@  - net8.0 + net10.0 - - + + diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/compos/LayoutFooter.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/compos/LayoutFooter.tsx index 225af795..32e234e0 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/compos/LayoutFooter.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/layouts/compos/LayoutFooter.tsx @@ -14,7 +14,7 @@ const LayoutFooter : React.FC =()=>{ { getAppVer()}    -   Powered by .NET8 ant-design-pro4 +   Powered by .NET10 ant-design-pro4 ) } diff --git a/test/AgileConfig.Server.CommonTests/AgileConfig.Server.CommonTests.csproj b/test/AgileConfig.Server.CommonTests/AgileConfig.Server.CommonTests.csproj index 9d7561ef..509b5ac4 100644 --- a/test/AgileConfig.Server.CommonTests/AgileConfig.Server.CommonTests.csproj +++ b/test/AgileConfig.Server.CommonTests/AgileConfig.Server.CommonTests.csproj @@ -1,15 +1,15 @@  - net8.0 + net10.0 false - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/AgileConfig.Server.Data.AbstractionTests/AgileConfig.Server.Data.AbstractionTests.csproj b/test/AgileConfig.Server.Data.AbstractionTests/AgileConfig.Server.Data.AbstractionTests.csproj index a9348118..fa6ecac0 100644 --- a/test/AgileConfig.Server.Data.AbstractionTests/AgileConfig.Server.Data.AbstractionTests.csproj +++ b/test/AgileConfig.Server.Data.AbstractionTests/AgileConfig.Server.Data.AbstractionTests.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 enable enable @@ -10,10 +10,13 @@ - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/test/AgileConfig.Server.Data.FreesqlTests/AgileConfig.Server.Data.FreesqlTests.csproj b/test/AgileConfig.Server.Data.FreesqlTests/AgileConfig.Server.Data.FreesqlTests.csproj index 86a86335..7e97083e 100644 --- a/test/AgileConfig.Server.Data.FreesqlTests/AgileConfig.Server.Data.FreesqlTests.csproj +++ b/test/AgileConfig.Server.Data.FreesqlTests/AgileConfig.Server.Data.FreesqlTests.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 enable enable @@ -10,10 +10,13 @@ - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/test/AgileConfig.Server.ServiceTests/AgileConfig.Server.ServiceTests.csproj b/test/AgileConfig.Server.ServiceTests/AgileConfig.Server.ServiceTests.csproj index bc90054b..af1a5264 100644 --- a/test/AgileConfig.Server.ServiceTests/AgileConfig.Server.ServiceTests.csproj +++ b/test/AgileConfig.Server.ServiceTests/AgileConfig.Server.ServiceTests.csproj @@ -1,23 +1,23 @@  - net8.0 + net10.0 false - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + diff --git a/test/ApiSiteTests/ApiSiteTests.csproj b/test/ApiSiteTests/ApiSiteTests.csproj index 0bbcece3..3feaea61 100644 --- a/test/ApiSiteTests/ApiSiteTests.csproj +++ b/test/ApiSiteTests/ApiSiteTests.csproj @@ -1,17 +1,17 @@  - net8.0 + net10.0 false - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/ApiSiteTests/TestAppController.cs b/test/ApiSiteTests/TestAppController.cs index 9313b5e5..64dbf4c0 100644 --- a/test/ApiSiteTests/TestAppController.cs +++ b/test/ApiSiteTests/TestAppController.cs @@ -32,7 +32,7 @@ public async Task TestAdd() ctl.ControllerContext.HttpContext = new DefaultHttpContext(); - Assert.ThrowsException(() => { ctl.Add(null).GetAwaiter().GetResult(); }); + Assert.ThrowsExactly(() => { ctl.Add(null).GetAwaiter().GetResult(); }); appService.Setup(s => s.GetAsync("01")).ReturnsAsync(new App()); var result = await ctl.Add(new AppVM From 87bd8209e7b2a6507cde05082036c9f48f6dccd3 Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Mon, 24 Nov 2025 01:39:04 +0800 Subject: [PATCH 12/17] clearn code --- .../AgileConfig.Server.Apisite.csproj | 11 ++-- .../AgileConfig.Server.Apisite.xml | 2 +- .../Controllers/AppController.cs | 2 +- .../Controllers/SysLogController.cs | 6 +- .../Controllers/api/Models/ApiAppVM.cs | 2 +- .../Filters/PermissionCheckAttribute.cs | 12 ++-- .../Models/AppAuthVM.cs | 2 +- .../Models/AppVM.cs | 2 +- .../Models/Mapping/ModelMappingExtension.cs | 2 +- src/AgileConfig.Server.Data.Entity/App.cs | 2 +- .../EnsureTables.cs | 4 +- .../SysInitRepository.cs | 2 +- .../SysInitRepository.cs | 2 +- .../IAppService.cs | 2 +- .../IPermissionService.cs | 59 +++++++++---------- src/AgileConfig.Server.Service/AppService.cs | 22 +++---- .../PermissionService.cs | 2 +- .../DbConfig/DbConfigInfoFactoryTests.cs | 1 - .../FreeSQLTests.cs | 1 - .../FreeSqlUowTests.cs | 1 - 20 files changed, 66 insertions(+), 73 deletions(-) diff --git a/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj b/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj index 9e46ce95..37141201 100644 --- a/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj +++ b/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj @@ -48,12 +48,15 @@ - - + + - + @@ -80,4 +83,4 @@ - + \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.xml b/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.xml index 6cb099c7..ddcb9e66 100644 --- a/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.xml +++ b/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.xml @@ -513,4 +513,4 @@ - + \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/AppController.cs b/src/AgileConfig.Server.Apisite/Controllers/AppController.cs index ee5a8513..f756cb0e 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/AppController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/AppController.cs @@ -353,4 +353,4 @@ public async Task GetAppGroups() data = groups.OrderBy(x => x) }); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Controllers/SysLogController.cs b/src/AgileConfig.Server.Apisite/Controllers/SysLogController.cs index 66e12f60..304365f2 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/SysLogController.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/SysLogController.cs @@ -1,10 +1,10 @@ -using AgileConfig.Server.Apisite.Filters; +using System; +using System.Threading.Tasks; +using AgileConfig.Server.Apisite.Filters; using AgileConfig.Server.Data.Entity; using AgileConfig.Server.IService; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using System; -using System.Threading.Tasks; namespace AgileConfig.Server.Apisite.Controllers; diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiAppVM.cs b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiAppVM.cs index ed319bfc..a774e6d4 100644 --- a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiAppVM.cs +++ b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiAppVM.cs @@ -64,4 +64,4 @@ public static AppVM ToAppVM(this ApiAppVM vm) Enabled = vm.Enabled.GetValueOrDefault() }; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs b/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs index 77d706c5..d88fda74 100644 --- a/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs +++ b/src/AgileConfig.Server.Apisite/Filters/PermissionCheckAttribute.cs @@ -1,11 +1,6 @@ -using System; -using System.Collections.Frozen; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; using System.Threading.Tasks; -using AgileConfig.Server.Apisite.Models; using AgileConfig.Server.Common; -using AgileConfig.Server.Data.Entity; using AgileConfig.Server.IService; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; @@ -19,7 +14,8 @@ public class PermissionCheckAttribute : ActionFilterAttribute private readonly IPermissionService _permissionService; - public PermissionCheckAttribute(IPermissionService permissionService, IConfigService configService, string functionKey) + public PermissionCheckAttribute(IPermissionService permissionService, IConfigService configService, + string functionKey) { _permissionService = permissionService; _configService = configService; @@ -71,4 +67,4 @@ public override async Task OnActionExecutionAsync(ActionExecutingContext context context.Result = new ContentResult(); await base.OnActionExecutionAsync(context, next); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/AppAuthVM.cs b/src/AgileConfig.Server.Apisite/Models/AppAuthVM.cs index dc986a01..cc668680 100644 --- a/src/AgileConfig.Server.Apisite/Models/AppAuthVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/AppAuthVM.cs @@ -9,4 +9,4 @@ public class AppAuthVM : IAppIdModel public List AuthorizedUsers { get; set; } public string AppId { get; set; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/AppVM.cs b/src/AgileConfig.Server.Apisite/Models/AppVM.cs index a8d68bbc..8a5ef84f 100644 --- a/src/AgileConfig.Server.Apisite/Models/AppVM.cs +++ b/src/AgileConfig.Server.Apisite/Models/AppVM.cs @@ -96,4 +96,4 @@ public static ApiAppVM ToApiAppVM(this AppVM vm) CreateTime = vm.CreateTime }; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Apisite/Models/Mapping/ModelMappingExtension.cs b/src/AgileConfig.Server.Apisite/Models/Mapping/ModelMappingExtension.cs index 381c5c34..700d6fb6 100644 --- a/src/AgileConfig.Server.Apisite/Models/Mapping/ModelMappingExtension.cs +++ b/src/AgileConfig.Server.Apisite/Models/Mapping/ModelMappingExtension.cs @@ -150,4 +150,4 @@ public static ApiConfigVM ToApiConfigVM(this Config config) return vm; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Entity/App.cs b/src/AgileConfig.Server.Data.Entity/App.cs index 86cba1e6..7d06a90e 100644 --- a/src/AgileConfig.Server.Data.Entity/App.cs +++ b/src/AgileConfig.Server.Data.Entity/App.cs @@ -54,4 +54,4 @@ public class App : IAppModel, IEntity [Column(Name = "create_time")] [BsonDateTimeOptions(Kind = DateTimeKind.Local)] public DateTime CreateTime { get; set; } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs b/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs index 33d59007..6c0a0e17 100644 --- a/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs +++ b/src/AgileConfig.Server.Data.Freesql/EnsureTables.cs @@ -63,7 +63,6 @@ public static bool ExistTable(IFreeSql instance) public static void Ensure(IFreeSql instance) { if (!ExistTable(instance)) - { try { if (instance.Ado.DataType == DataType.Oracle) @@ -91,6 +90,5 @@ public static void Ensure(IFreeSql instance) var logger = Global.LoggerFactory.CreateLogger(); logger.LogError(ex, "Ensure Tables failed ."); } - } } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs index f48956ab..f296841a 100644 --- a/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Freesql/SysInitRepository.cs @@ -218,4 +218,4 @@ private static void EnsureRolePermissions(IFreeSql sql, string roleId, List roleFunctionsToRemove.Select(r => r.Id).Contains(rf.Id)) .ExecuteAffrows(); } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs index 14eb0fd5..c4ea55f9 100644 --- a/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs +++ b/src/AgileConfig.Server.Data.Repository.Mongodb/SysInitRepository.cs @@ -142,4 +142,4 @@ private void EnsureRole(string id, string name) _roleAccess.Collection.ReplaceOne(x => x.Id == id, role, new ReplaceOptions { IsUpsert = true }); } } -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IAppService.cs b/src/AgileConfig.Server.IService/IAppService.cs index 7bade8c6..845095e2 100644 --- a/src/AgileConfig.Server.IService/IAppService.cs +++ b/src/AgileConfig.Server.IService/IAppService.cs @@ -47,4 +47,4 @@ public interface IAppService : IDisposable Task> GetUserAppAuth(string appId); Task> GetAppGroups(); -} +} \ No newline at end of file diff --git a/src/AgileConfig.Server.IService/IPermissionService.cs b/src/AgileConfig.Server.IService/IPermissionService.cs index 1efb7435..c01b6024 100644 --- a/src/AgileConfig.Server.IService/IPermissionService.cs +++ b/src/AgileConfig.Server.IService/IPermissionService.cs @@ -49,52 +49,51 @@ public static List GetAllPermissions() return new List { // Application permissions - Functions.App_Read, - Functions.App_Add, - Functions.App_Edit, - Functions.App_Delete, - Functions.App_Auth, + App_Read, + App_Add, + App_Edit, + App_Delete, + App_Auth, // Configuration permissions - Functions.Config_Read, - Functions.Config_Add, - Functions.Config_Edit, - Functions.Config_Delete, - Functions.Config_Publish, - Functions.Config_Offline, + Config_Read, + Config_Add, + Config_Edit, + Config_Delete, + Config_Publish, + Config_Offline, // Node permissions - Functions.Node_Read, - Functions.Node_Add, - Functions.Node_Delete, + Node_Read, + Node_Add, + Node_Delete, // Client permissions - Functions.Client_Refresh, - Functions.Client_Disconnect, - Functions.Client_Read, + Client_Refresh, + Client_Disconnect, + Client_Read, // User permissions - Functions.User_Read, - Functions.User_Add, - Functions.User_Edit, - Functions.User_Delete, + User_Read, + User_Add, + User_Edit, + User_Delete, // Role permissions - Functions.Role_Read, - Functions.Role_Add, - Functions.Role_Edit, - Functions.Role_Delete, + Role_Read, + Role_Add, + Role_Edit, + Role_Delete, // Service permissions - Functions.Service_Read, - Functions.Service_Add, - Functions.Service_Delete, + Service_Read, + Service_Add, + Service_Delete, // System permissions - Functions.Log_Read + Log_Read }; } - } public interface IPermissionService diff --git a/src/AgileConfig.Server.Service/AppService.cs b/src/AgileConfig.Server.Service/AppService.cs index 3caeb71e..e7676bba 100644 --- a/src/AgileConfig.Server.Service/AppService.cs +++ b/src/AgileConfig.Server.Service/AppService.cs @@ -321,16 +321,6 @@ public void Dispose() _userRepository.Dispose(); } - private async Task> GetUserAuthorizedAppIds(string userId) - { - var authorizedAppIds = new List(); - if (string.IsNullOrWhiteSpace(userId)) return authorizedAppIds; - - var auths = await _userAppAuthRepository.QueryAsync(x => x.UserId == userId); - - return auths.Select(x => x.AppId).Distinct().ToList(); - } - public async Task> GetUserAppAuth(string appId) { var auths = await _userAppAuthRepository.QueryAsync(x => x.AppId == appId); @@ -349,4 +339,14 @@ public async Task> GetAppGroups() var groups = apps.GroupBy(x => x.Group).Select(x => x.Key); return groups.Where(x => !string.IsNullOrEmpty(x)).ToList(); } -} + + private async Task> GetUserAuthorizedAppIds(string userId) + { + var authorizedAppIds = new List(); + if (string.IsNullOrWhiteSpace(userId)) return authorizedAppIds; + + var auths = await _userAppAuthRepository.QueryAsync(x => x.UserId == userId); + + return auths.Select(x => x.AppId).Distinct().ToList(); + } +} \ No newline at end of file diff --git a/src/AgileConfig.Server.Service/PermissionService.cs b/src/AgileConfig.Server.Service/PermissionService.cs index 7e835c51..fa681aa3 100644 --- a/src/AgileConfig.Server.Service/PermissionService.cs +++ b/src/AgileConfig.Server.Service/PermissionService.cs @@ -78,4 +78,4 @@ public async Task> GetUserCategories(string userId) public string EditConfigPermissionKey => "EDIT_CONFIG"; public string PublishConfigPermissionKey => "PUBLISH_CONFIG"; -} +} \ No newline at end of file diff --git a/test/AgileConfig.Server.Data.AbstractionTests/DbConfig/DbConfigInfoFactoryTests.cs b/test/AgileConfig.Server.Data.AbstractionTests/DbConfig/DbConfigInfoFactoryTests.cs index 53b21e33..c23c7814 100644 --- a/test/AgileConfig.Server.Data.AbstractionTests/DbConfig/DbConfigInfoFactoryTests.cs +++ b/test/AgileConfig.Server.Data.AbstractionTests/DbConfig/DbConfigInfoFactoryTests.cs @@ -1,7 +1,6 @@ using AgileConfig.Server.Common; using AgileConfig.Server.Data.Abstraction.DbProvider; using Microsoft.Extensions.Configuration; -using Microsoft.VisualStudio.TestTools.UnitTesting; namespace AgileConfig.Server.Data.AbstractionTests.DbConfig; diff --git a/test/AgileConfig.Server.Data.FreesqlTests/FreeSQLTests.cs b/test/AgileConfig.Server.Data.FreesqlTests/FreeSQLTests.cs index 106ade26..ae16b2bc 100644 --- a/test/AgileConfig.Server.Data.FreesqlTests/FreeSQLTests.cs +++ b/test/AgileConfig.Server.Data.FreesqlTests/FreeSQLTests.cs @@ -2,7 +2,6 @@ using AgileConfig.Server.Data.Abstraction.DbProvider; using FreeSql; using Microsoft.Extensions.Configuration; -using Microsoft.VisualStudio.TestTools.UnitTesting; namespace AgileConfig.Server.Data.Freesql.Tests; diff --git a/test/AgileConfig.Server.Data.FreesqlTests/FreeSqlUowTests.cs b/test/AgileConfig.Server.Data.FreesqlTests/FreeSqlUowTests.cs index 802fdcff..151ab8e6 100644 --- a/test/AgileConfig.Server.Data.FreesqlTests/FreeSqlUowTests.cs +++ b/test/AgileConfig.Server.Data.FreesqlTests/FreeSqlUowTests.cs @@ -1,6 +1,5 @@ using AgileConfig.Server.Data.Abstraction.DbProvider; using Microsoft.Extensions.Configuration; -using Microsoft.VisualStudio.TestTools.UnitTesting; namespace AgileConfig.Server.Data.Freesql.Tests; From 58f13503cc66dbd5b7c9b4d758cccf03df221c95 Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Thu, 4 Dec 2025 23:09:25 +0800 Subject: [PATCH 13/17] set permission group color --- .../react-ui-antd/src/locales/zh-CN/pages.ts | 2 +- .../react-ui-antd/src/pages/Role/index.tsx | 42 +++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts index 4a168685..50b9ba97 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/locales/zh-CN/pages.ts @@ -278,7 +278,7 @@ export default { 'pages.user.reset_password_default': '的密码为默认密码【123456】?', 'pages.role.table.cols.name': '名称', 'pages.role.table.cols.description': '描述', - 'pages.role.table.cols.system': '系统角色', + 'pages.role.table.cols.system': '系统', 'pages.role.system.yes': '是', 'pages.role.system.no': '否', 'pages.role.table.cols.functions': '权限', diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx index cf78be19..ced05f54 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx @@ -3,6 +3,7 @@ import { PageContainer } from '@ant-design/pro-layout'; import ProTable, { ActionType, ProColumns } from '@ant-design/pro-table'; import { Button, message, Modal, Space, Tag } from 'antd'; import { ModalForm, ProFormSelect, ProFormText } from '@ant-design/pro-form'; +import type { CustomTagProps } from 'rc-select/lib/BaseSelect'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import { useIntl } from 'umi'; import type { RoleFormValues, RoleItem } from './data'; @@ -11,6 +12,34 @@ import { RequireFunction } from '@/utils/permission'; const { confirm } = Modal; +const PERMISSION_GROUP_COLORS: Record = { + APP: 'blue', + CONFIG: 'geekblue', + NODE: 'purple', + CLIENT: 'green', + USER: 'gold', + ROLE: 'volcano', + SERVICE: 'cyan', + LOG: 'magenta', + OTHER: 'default', +}; + +const getPermissionTagColor = (permission: string) => { + const prefix = permission.split('_')[0]; + return PERMISSION_GROUP_COLORS[prefix] || PERMISSION_GROUP_COLORS.OTHER; +}; + +const renderPermissionTag = (props: CustomTagProps) => { + const { label, value, closable, onClose } = props; + return ( + + 123 + + ); +}; + const RolePage: React.FC = () => { const actionRef = useRef(); const intl = useIntl(); @@ -163,9 +192,13 @@ const RolePage: React.FC = () => { } return ( - {fns.map((fn) => ( - {intl.formatMessage({ id: `pages.role.permissions.${fn}`, defaultMessage: fn })} - ))} + {fns.map((fn) => { + return ( + + {intl.formatMessage({ id: `pages.role.permissions.${fn}`, defaultMessage: fn })} + + ); + })} ); }, @@ -173,6 +206,7 @@ const RolePage: React.FC = () => { { title: intl.formatMessage({ id: 'pages.role.table.cols.action', defaultMessage: 'Action' }), valueType: 'option', + width: 160, render: (_, record) => [ { label={intl.formatMessage({ id: 'pages.role.form.functions', defaultMessage: 'Permissions' })} mode="multiple" options={groupedPermissionOptions} + tagRender={renderPermissionTag} /> @@ -297,6 +332,7 @@ const RolePage: React.FC = () => { label={intl.formatMessage({ id: 'pages.role.form.functions', defaultMessage: 'Permissions' })} mode="multiple" options={groupedPermissionOptions} + tagRender={renderPermissionTag} /> )} From 2419c7d74cdf5c2f0f1cd248d8bcc1b000eff3cf Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Thu, 4 Dec 2025 23:47:06 +0800 Subject: [PATCH 14/17] Update dockerfile --- .github/workflows/test-cd.yml | 1 + Dockerfile | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-cd.yml b/.github/workflows/test-cd.yml index 8bf340a3..3339ca46 100644 --- a/.github/workflows/test-cd.yml +++ b/.github/workflows/test-cd.yml @@ -45,6 +45,7 @@ jobs: dotnet-version: | 8.0.x 9.0.x + 10.0.x - name: Install dependencies run: dotnet restore diff --git a/Dockerfile b/Dockerfile index 16b9da49..a1403e19 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:10.0.0 AS base RUN sed -i 's/DEFAULT@SECLEVEL=2/DEFAULT@SECLEVEL=1/g' /etc/ssl/openssl.cnf RUN sed -i 's/MinProtocol = TLSv1.2/MinProtocol = TLSv1/g' /etc/ssl/openssl.cnf RUN sed -i 's/DEFAULT@SECLEVEL=2/DEFAULT@SECLEVEL=1/g' /usr/lib/ssl/openssl.cnf @@ -6,7 +6,7 @@ RUN sed -i 's/MinProtocol = TLSv1.2/MinProtocol = TLSv1/g' /usr/lib/ssl/openssl. WORKDIR /app EXPOSE 5000 -FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build WORKDIR /src COPY ["src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj", "AgileConfig.Server.Apisite/"] COPY ["src/AgileConfig.Server.Data.Entity/AgileConfig.Server.Data.Entity.csproj", "AgileConfig.Server.Data.Entity/"] From 799d361a72503ef06aadd58c83069011e7e78ed4 Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Fri, 5 Dec 2025 01:09:58 +0800 Subject: [PATCH 15/17] Set role dropdown tag color --- .../react-ui-antd/src/pages/Role/index.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx index ced05f54..d40f0c79 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx @@ -33,9 +33,10 @@ const renderPermissionTag = (props: CustomTagProps) => { const { label, value, closable, onClose } = props; return ( - 123 + {label} ); }; @@ -290,7 +291,9 @@ const RolePage: React.FC = () => { label={intl.formatMessage({ id: 'pages.role.form.functions', defaultMessage: 'Permissions' })} mode="multiple" options={groupedPermissionOptions} - tagRender={renderPermissionTag} + fieldProps={{ + tagRender: renderPermissionTag, + }} /> @@ -332,8 +335,9 @@ const RolePage: React.FC = () => { label={intl.formatMessage({ id: 'pages.role.form.functions', defaultMessage: 'Permissions' })} mode="multiple" options={groupedPermissionOptions} - tagRender={renderPermissionTag} - /> + fieldProps={{ + tagRender: renderPermissionTag, + }} /> )} From 9deac73950ffa92c7746b999145ae52eb31b5cc7 Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Fri, 5 Dec 2025 01:13:31 +0800 Subject: [PATCH 16/17] update workflow --- .github/workflows/arm32.yml | 1 + .github/workflows/arm64.yml | 1 + .github/workflows/master-ci.yml | 1 + .github/workflows/master-pr-ci.yml | 1 + .github/workflows/mysqlconnector.yml | 1 + .github/workflows/publish.yml | 1 + .github/workflows/release-xxx.yml | 1 + 7 files changed, 7 insertions(+) diff --git a/.github/workflows/arm32.yml b/.github/workflows/arm32.yml index ef5a465e..1fd51760 100644 --- a/.github/workflows/arm32.yml +++ b/.github/workflows/arm32.yml @@ -44,6 +44,7 @@ jobs: dotnet-version: | 8.0.x 9.0.x + 10.0.x - name: Install dependencies run: dotnet restore - name: Build diff --git a/.github/workflows/arm64.yml b/.github/workflows/arm64.yml index 809d5bab..b6a4654e 100644 --- a/.github/workflows/arm64.yml +++ b/.github/workflows/arm64.yml @@ -44,6 +44,7 @@ jobs: dotnet-version: | 8.0.x 9.0.x + 10.0.x - name: Install dependencies run: dotnet restore - name: Build diff --git a/.github/workflows/master-ci.yml b/.github/workflows/master-ci.yml index 7c5036ba..ba62b4ae 100644 --- a/.github/workflows/master-ci.yml +++ b/.github/workflows/master-ci.yml @@ -35,6 +35,7 @@ jobs: dotnet-version: | 8.0.x 9.0.x + 10.0.x - name: Restore dependencies run: dotnet restore - name: Build diff --git a/.github/workflows/master-pr-ci.yml b/.github/workflows/master-pr-ci.yml index bef13642..69950c74 100644 --- a/.github/workflows/master-pr-ci.yml +++ b/.github/workflows/master-pr-ci.yml @@ -35,6 +35,7 @@ jobs: dotnet-version: | 8.0.x 9.0.x + 10.0.x - name: Restore dependencies run: dotnet restore - name: Build diff --git a/.github/workflows/mysqlconnector.yml b/.github/workflows/mysqlconnector.yml index 4aeeb255..bf8bc011 100644 --- a/.github/workflows/mysqlconnector.yml +++ b/.github/workflows/mysqlconnector.yml @@ -44,6 +44,7 @@ jobs: dotnet-version: | 8.0.x 9.0.x + 10.0.x - name: Install dependencies run: dotnet restore - name: Build diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 30cd44dc..fcd31dc2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -43,6 +43,7 @@ jobs: dotnet-version: | 8.0.x 9.0.x + 10.0.x - name: Install dependencies run: dotnet restore - name: Build diff --git a/.github/workflows/release-xxx.yml b/.github/workflows/release-xxx.yml index 24f242f2..e4f82a2c 100644 --- a/.github/workflows/release-xxx.yml +++ b/.github/workflows/release-xxx.yml @@ -43,6 +43,7 @@ jobs: dotnet-version: | 8.0.x 9.0.x + 10.0.x - name: Install dependencies run: dotnet restore - name: Build From 62efc90388417d63b342ea837cf40d487edbeaa5 Mon Sep 17 00:00:00 2001 From: "agile.zhou" Date: Fri, 5 Dec 2025 01:21:18 +0800 Subject: [PATCH 17/17] update role dropdown --- .../react-ui-antd/src/pages/Role/index.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx index d40f0c79..ff258c16 100644 --- a/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx +++ b/src/AgileConfig.Server.UI/react-ui-antd/src/pages/Role/index.tsx @@ -31,10 +31,17 @@ const getPermissionTagColor = (permission: string) => { const renderPermissionTag = (props: CustomTagProps) => { const { label, value, closable, onClose } = props; + const onPreventMouseDown = (event: React.MouseEvent) => { + event.preventDefault(); + event.stopPropagation(); + }; return ( {label}