diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Identity.OverrideOptions/LINGYUN/Abp/Identity/AbpIdentityOverrideOptionsFactory.cs b/aspnet-core/modules/common/LINGYUN.Abp.Identity.OverrideOptions/LINGYUN/Abp/Identity/AbpIdentityOverrideOptionsFactory.cs index c7cefecfd..db3789797 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Identity.OverrideOptions/LINGYUN/Abp/Identity/AbpIdentityOverrideOptionsFactory.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.Identity.OverrideOptions/LINGYUN/Abp/Identity/AbpIdentityOverrideOptionsFactory.cs @@ -1,23 +1,77 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Options; +using System; using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Identity.Settings; using Volo.Abp.Options; +using Volo.Abp.Settings; +using Volo.Abp.Threading; namespace LINGYUN.Abp.Identity { public class AbpIdentityOverrideOptionsFactory : AbpOptionsFactory { + protected ISettingStore SettingStore { get; } public AbpIdentityOverrideOptionsFactory( + ISettingStore settingStore, IEnumerable> setups, IEnumerable> postConfigures) : base(setups, postConfigures) { - + SettingStore = settingStore; } public override IdentityOptions Create(string name) { - return base.Create(name); + var options = base.Create(name); + + // 重写为只获取公共配置 + OverrideOptions(options); + + return options; + } + + protected virtual void OverrideOptions(IdentityOptions options) + { + AsyncHelper.RunSync(() => OverrideOptionsAsync(options)); + } + + protected virtual async Task OverrideOptionsAsync(IdentityOptions options) + { + options.Password.RequiredLength = await GetOrDefaultAsync(IdentitySettingNames.Password.RequiredLength, options.Password.RequiredLength); + options.Password.RequiredUniqueChars = await GetOrDefaultAsync(IdentitySettingNames.Password.RequiredUniqueChars, options.Password.RequiredUniqueChars); + options.Password.RequireNonAlphanumeric = await GetOrDefaultAsync(IdentitySettingNames.Password.RequireNonAlphanumeric, options.Password.RequireNonAlphanumeric); + options.Password.RequireLowercase = await GetOrDefaultAsync(IdentitySettingNames.Password.RequireLowercase, options.Password.RequireLowercase); + options.Password.RequireUppercase = await GetOrDefaultAsync(IdentitySettingNames.Password.RequireUppercase, options.Password.RequireUppercase); + options.Password.RequireDigit = await GetOrDefaultAsync(IdentitySettingNames.Password.RequireDigit, options.Password.RequireDigit); + + options.Lockout.AllowedForNewUsers = await GetOrDefaultAsync(IdentitySettingNames.Lockout.AllowedForNewUsers, options.Lockout.AllowedForNewUsers); + options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromSeconds(await GetOrDefaultAsync(IdentitySettingNames.Lockout.LockoutDuration, options.Lockout.DefaultLockoutTimeSpan.TotalSeconds.To())); + options.Lockout.MaxFailedAccessAttempts = await GetOrDefaultAsync(IdentitySettingNames.Lockout.MaxFailedAccessAttempts, options.Lockout.MaxFailedAccessAttempts); + + options.SignIn.RequireConfirmedEmail = await GetOrDefaultAsync(IdentitySettingNames.SignIn.RequireConfirmedEmail, options.SignIn.RequireConfirmedEmail); + options.SignIn.RequireConfirmedPhoneNumber = await GetOrDefaultAsync(IdentitySettingNames.SignIn.RequireConfirmedPhoneNumber, options.SignIn.RequireConfirmedPhoneNumber); + } + + protected virtual async Task GetOrDefaultAsync(string name, T defaultValue = default(T)) where T : struct + { + var setting = await SettingStore.GetOrNullAsync(name, GlobalSettingValueProvider.ProviderName, null); + if (setting.IsNullOrWhiteSpace()) + { + return defaultValue; + } + return setting.To(); + } + + protected virtual async Task GetOrDefaultAsync(string name, string defaultValue = "") + { + var setting = await SettingStore.GetOrNullAsync(name, GlobalSettingValueProvider.ProviderName, null); + if (setting.IsNullOrWhiteSpace()) + { + return defaultValue; + } + return setting; } } } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Location.Baidu/LINGYUN.Abp.Location.Baidu.csproj b/aspnet-core/modules/common/LINGYUN.Abp.Location.Baidu/LINGYUN.Abp.Location.Baidu.csproj index bbfeeef2a..9fd604ba1 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Location.Baidu/LINGYUN.Abp.Location.Baidu.csproj +++ b/aspnet-core/modules/common/LINGYUN.Abp.Location.Baidu/LINGYUN.Abp.Location.Baidu.csproj @@ -5,6 +5,11 @@ + + + + + diff --git a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs index 63ec28a83..53ac44165 100644 --- a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs +++ b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; +using Volo.Abp.Caching; using Volo.Abp.SettingManagement; using Volo.Abp.SettingManagement.Localization; using Volo.Abp.Settings; @@ -16,9 +17,14 @@ namespace LINGYUN.Abp.SettingManagement { protected ISettingManager SettingManager { get; } protected ISettingDefinitionManager SettingDefinitionManager { get; } - public SettingAppService(ISettingManager settingManager, + + protected IDistributedCache Cache { get; } + public SettingAppService( + ISettingManager settingManager, + IDistributedCache cache, ISettingDefinitionManager settingDefinitionManager) { + Cache = cache; SettingManager = settingManager; SettingDefinitionManager = settingDefinitionManager; LocalizationResource = typeof(AbpSettingManagementResource); @@ -54,8 +60,17 @@ namespace LINGYUN.Abp.SettingManagement { foreach (var setting in input.Settings) { - await SettingManager.SetAsync(setting.Name, providerName, providerKey, setting.Value); + await SettingManager.SetAsync(setting.Name, setting.Value, providerName, providerKey); + // 同步变更缓存配置 + var settingCacheKey = CalculateCacheKey(setting.Name, providerName, providerKey); + var settignCacheItem = new SettingCacheItem(setting.Value); + await Cache.SetAsync(settingCacheKey, settignCacheItem); } } + + protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) + { + return SettingCacheItem.CalculateCacheKey(name, providerName, providerKey); + } } } diff --git a/aspnet-core/services/apigateway/LINGYUN.ApiGateway.HttpApi.Host/ApiGatewayHttpApiHostModule.cs b/aspnet-core/services/apigateway/LINGYUN.ApiGateway.HttpApi.Host/ApiGatewayHttpApiHostModule.cs index f2fba0de2..4e37195c4 100644 --- a/aspnet-core/services/apigateway/LINGYUN.ApiGateway.HttpApi.Host/ApiGatewayHttpApiHostModule.cs +++ b/aspnet-core/services/apigateway/LINGYUN.ApiGateway.HttpApi.Host/ApiGatewayHttpApiHostModule.cs @@ -163,7 +163,7 @@ namespace LINGYUN.ApiGateway // 审计日志 app.UseAuditing(); // 路由 - app.UseMvcWithDefaultRouteAndArea(); + app.UseConfiguredEndpoints(); } } } diff --git a/aspnet-core/services/cleanup-logs.bat b/aspnet-core/services/cleanup-logs.bat new file mode 100644 index 000000000..56d1f3d39 --- /dev/null +++ b/aspnet-core/services/cleanup-logs.bat @@ -0,0 +1,8 @@ +@echo off +cls +chcp 65001 + +echo. 启动平台管理服务 + +cd .\platform\LINGYUN.Platform.HttpApi.Host + diff --git a/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/LINGYUN.Platform.HttpApi.Host.csproj b/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/LINGYUN.Platform.HttpApi.Host.csproj index ca1f77f73..c937f855d 100644 --- a/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/LINGYUN.Platform.HttpApi.Host.csproj +++ b/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/LINGYUN.Platform.HttpApi.Host.csproj @@ -5,6 +5,16 @@ LINGYUN.Platform + + + + + + + + + + diff --git a/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/LINGYUN/Platform/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs b/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/LINGYUN/Platform/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs new file mode 100644 index 000000000..517a12025 --- /dev/null +++ b/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/LINGYUN/Platform/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs @@ -0,0 +1,268 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Application.Services; +using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; +using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; +using Volo.Abp.AspNetCore.Mvc.MultiTenancy; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Clients; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Local; +using Volo.Abp.Features; +using Volo.Abp.Localization; +using Volo.Abp.MultiTenancy; +using Volo.Abp.PermissionManagement; +using Volo.Abp.Settings; +using Volo.Abp.Users; + +namespace LINGYUN.Platform.AspNetCore.Mvc.ApplicationConfigurations +{ + [Dependency(ServiceLifetime.Transient)] + [ExposeServices(typeof(IAbpApplicationConfigurationAppService))] + public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApplicationConfigurationAppService + { + private readonly AbpLocalizationOptions _localizationOptions; + private readonly AbpMultiTenancyOptions _multiTenancyOptions; + private readonly IServiceProvider _serviceProvider; + private readonly ISettingProvider _settingProvider; + private readonly ISettingDefinitionManager _settingDefinitionManager; + private readonly IFeatureDefinitionManager _featureDefinitionManager; + private readonly IPermissionGrantRepository _permissionGrantRepository; + private readonly IPermissionDefinitionManager _permissionDefinitionManager; + private readonly ILanguageProvider _languageProvider; + private readonly ICachedObjectExtensionsDtoService _cachedObjectExtensionsDtoService; + + private ICurrentClient _currentClient; + + protected ICurrentClient CurrentClient => LazyGetRequiredService(ref _currentClient); + + private ILocalEventBus _localEventBus; + //用于发布权限事件,每次请求此接口后,通过事件总线来缓存权限 + protected ILocalEventBus LocalEventBus => LazyGetRequiredService(ref _localEventBus); + + public AbpApplicationConfigurationAppService( + IOptions localizationOptions, + IOptions multiTenancyOptions, + IServiceProvider serviceProvider, + ISettingProvider settingProvider, + ISettingDefinitionManager settingDefinitionManager, + IFeatureDefinitionManager featureDefinitionManager, + IPermissionGrantRepository permissionGrantRepository, + IPermissionDefinitionManager permissionDefinitionManager, + ILanguageProvider languageProvider, + ICachedObjectExtensionsDtoService cachedObjectExtensionsDtoService) + { + _serviceProvider = serviceProvider; + _settingProvider = settingProvider; + _settingDefinitionManager = settingDefinitionManager; + _featureDefinitionManager = featureDefinitionManager; + _permissionGrantRepository = permissionGrantRepository; + _permissionDefinitionManager = permissionDefinitionManager; + _languageProvider = languageProvider; + _cachedObjectExtensionsDtoService = cachedObjectExtensionsDtoService; + _localizationOptions = localizationOptions.Value; + _multiTenancyOptions = multiTenancyOptions.Value; + } + + public virtual async Task GetAsync() + { + //TODO: Optimize & cache..? + + Logger.LogDebug("Executing AbpApplicationConfigurationAppService.GetAsync()..."); + + var result = new ApplicationConfigurationDto + { + Auth = await GetAuthConfigAsync(), + Features = await GetFeaturesConfigAsync(), + Localization = await GetLocalizationConfigAsync(), + CurrentUser = GetCurrentUser(), + Setting = await GetSettingConfigAsync(), + MultiTenancy = GetMultiTenancy(), + CurrentTenant = GetCurrentTenant(), + ObjectExtensions = _cachedObjectExtensionsDtoService.Get() + }; + + Logger.LogDebug("Executed AbpApplicationConfigurationAppService.GetAsync()."); + + return result; + } + + protected virtual CurrentTenantDto GetCurrentTenant() + { + return new CurrentTenantDto() + { + Id = CurrentTenant.Id, + Name = CurrentTenant.Name, + IsAvailable = CurrentTenant.IsAvailable + }; + } + + protected virtual MultiTenancyInfoDto GetMultiTenancy() + { + return new MultiTenancyInfoDto + { + IsEnabled = _multiTenancyOptions.IsEnabled + }; + } + + protected virtual CurrentUserDto GetCurrentUser() + { + return new CurrentUserDto + { + IsAuthenticated = CurrentUser.IsAuthenticated, + Id = CurrentUser.Id, + TenantId = CurrentUser.TenantId, + UserName = CurrentUser.UserName, + Email = CurrentUser.Email + }; + } + + protected virtual async Task GetAuthConfigAsync() + { + var authConfig = new ApplicationAuthConfigurationDto(); + + var permissions = _permissionDefinitionManager.GetPermissions(); + + IEnumerable grantPermissions = new List(); + + // TODO: 重写为每次调用接口都在数据库统一查询权限 + // 待框架改进权限Provider机制后再移除 + + // 如果用户已登录,获取用户和角色权限 + if (CurrentUser.IsAuthenticated) + { + var userPermissions = await _permissionGrantRepository.GetListAsync(UserPermissionValueProvider.ProviderName, + CurrentUser.GetId().ToString()); + grantPermissions = grantPermissions.Union(userPermissions); + foreach(var userRole in CurrentUser.Roles) + { + var rolePermissions = await _permissionGrantRepository.GetListAsync(RolePermissionValueProvider.ProviderName, + userRole); + grantPermissions = grantPermissions.Union(rolePermissions); + } + } + + // 如果客户端已验证,获取客户端权限 + if(CurrentClient.IsAuthenticated) + { + var clientPermissions = await _permissionGrantRepository.GetListAsync(ClientPermissionValueProvider.ProviderName, + CurrentClient.Id); + grantPermissions = grantPermissions.Union(clientPermissions); + } + + foreach(var permission in permissions) + { + authConfig.Policies[permission.Name] = true; + if(grantPermissions.Any(p => p.Name.Equals(permission.Name))) + { + authConfig.GrantedPolicies[permission.Name] = true; + } + } + + return authConfig; + } + + protected virtual async Task GetLocalizationConfigAsync() + { + var localizationConfig = new ApplicationLocalizationConfigurationDto(); + + localizationConfig.Languages.AddRange(await _languageProvider.GetLanguagesAsync()); + + foreach (var resource in _localizationOptions.Resources.Values) + { + var dictionary = new Dictionary(); + + var localizer = _serviceProvider.GetRequiredService( + typeof(IStringLocalizer<>).MakeGenericType(resource.ResourceType) + ) as IStringLocalizer; + + foreach (var localizedString in localizer.GetAllStrings()) + { + dictionary[localizedString.Name] = localizedString.Value; + } + + localizationConfig.Values[resource.ResourceName] = dictionary; + } + + localizationConfig.CurrentCulture = GetCurrentCultureInfo(); + + if (_localizationOptions.DefaultResourceType != null) + { + localizationConfig.DefaultResourceName = LocalizationResourceNameAttribute.GetName( + _localizationOptions.DefaultResourceType + ); + } + + return localizationConfig; + } + + private static CurrentCultureDto GetCurrentCultureInfo() + { + return new CurrentCultureDto + { + Name = CultureInfo.CurrentUICulture.Name, + DisplayName = CultureInfo.CurrentUICulture.DisplayName, + EnglishName = CultureInfo.CurrentUICulture.EnglishName, + NativeName = CultureInfo.CurrentUICulture.NativeName, + IsRightToLeft = CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft, + CultureName = CultureInfo.CurrentUICulture.TextInfo.CultureName, + TwoLetterIsoLanguageName = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName, + ThreeLetterIsoLanguageName = CultureInfo.CurrentUICulture.ThreeLetterISOLanguageName, + DateTimeFormat = new DateTimeFormatDto + { + CalendarAlgorithmType = CultureInfo.CurrentUICulture.DateTimeFormat.Calendar.AlgorithmType.ToString(), + DateTimeFormatLong = CultureInfo.CurrentUICulture.DateTimeFormat.LongDatePattern, + ShortDatePattern = CultureInfo.CurrentUICulture.DateTimeFormat.ShortDatePattern, + FullDateTimePattern = CultureInfo.CurrentUICulture.DateTimeFormat.FullDateTimePattern, + DateSeparator = CultureInfo.CurrentUICulture.DateTimeFormat.DateSeparator, + ShortTimePattern = CultureInfo.CurrentUICulture.DateTimeFormat.ShortTimePattern, + LongTimePattern = CultureInfo.CurrentUICulture.DateTimeFormat.LongTimePattern, + } + }; + } + + private async Task GetSettingConfigAsync() + { + var result = new ApplicationSettingConfigurationDto + { + Values = new Dictionary() + }; + + foreach (var settingDefinition in _settingDefinitionManager.GetAll()) + { + if (!settingDefinition.IsVisibleToClients) + { + continue; + } + + result.Values[settingDefinition.Name] = await _settingProvider.GetOrNullAsync(settingDefinition.Name); + } + + return result; + } + + protected virtual async Task GetFeaturesConfigAsync() + { + var result = new ApplicationFeatureConfigurationDto(); + + foreach (var featureDefinition in _featureDefinitionManager.GetAll()) + { + if (!featureDefinition.IsVisibleToClients) + { + continue; + } + + result.Values[featureDefinition.Name] = await FeatureChecker.GetOrNullAsync(featureDefinition.Name); + } + + return result; + } + } +} diff --git a/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/LINGYUN/Platform/Identity/Localization/en.json b/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/LINGYUN/Platform/Identity/Localization/en.json new file mode 100644 index 000000000..a9c8dcc3f --- /dev/null +++ b/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/LINGYUN/Platform/Identity/Localization/en.json @@ -0,0 +1,5 @@ +{ + "culture": "en", + "texts": { + } +} \ No newline at end of file diff --git a/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/LINGYUN/Platform/Identity/Localization/zh-Hans.json b/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/LINGYUN/Platform/Identity/Localization/zh-Hans.json new file mode 100644 index 000000000..83575a629 --- /dev/null +++ b/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/LINGYUN/Platform/Identity/Localization/zh-Hans.json @@ -0,0 +1,7 @@ +{ + "culture": "zh-Hans", + "texts": { + "DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "启用电话号码确认", + "Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "用户是否可以确认电话号码." + } +} \ No newline at end of file diff --git a/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs b/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs index a669d0cb0..693219585 100644 --- a/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs +++ b/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs @@ -29,6 +29,7 @@ using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.MySQL; using Volo.Abp.Identity; using Volo.Abp.Identity.EntityFrameworkCore; +using Volo.Abp.Identity.Localization; using Volo.Abp.IdentityServer.EntityFrameworkCore; using Volo.Abp.Localization; using Volo.Abp.Modularity; @@ -40,6 +41,7 @@ using Volo.Abp.PermissionManagement.IdentityServer; using Volo.Abp.Security.Claims; using Volo.Abp.SettingManagement.EntityFrameworkCore; using Volo.Abp.TenantManagement.EntityFrameworkCore; +using Volo.Abp.VirtualFileSystem; using AbpPermissionManagementApplicationModule = LINGYUN.Abp.PermissionManagement.AbpPermissionManagementApplicationModule; namespace LINGYUN.Platform @@ -107,6 +109,11 @@ namespace LINGYUN.Platform options.UseMySQL(); }); + Configure(options => + { + options.FileSets.AddEmbedded("LINGYUN.Platform"); + }); + // 多租户 Configure(options => { @@ -132,6 +139,10 @@ namespace LINGYUN.Platform { options.Languages.Add(new LanguageInfo("en", "en", "English")); options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文")); + + options.Resources + .Get() + .AddVirtualJson("/LINGYUN/Platform/Identity/Localization"); }); context.Services.AddAuthentication("Bearer") diff --git a/vueJs/src/api/abpconfiguration.ts b/vueJs/src/api/abpconfiguration.ts index 2faa10727..41ecea8ac 100644 --- a/vueJs/src/api/abpconfiguration.ts +++ b/vueJs/src/api/abpconfiguration.ts @@ -5,7 +5,12 @@ const serviceUrl = process.env.VUE_APP_BASE_API export default class AbpConfigurationService { public static getAbpConfiguration() { const _url = '/api/abp/application-configuration' - return ApiService.Get(_url, serviceUrl) + return ApiService.HttpRequest({ + url: _url, + baseURL: serviceUrl, + method: 'GET', + timeout: 120000 + }) } } @@ -65,15 +70,15 @@ export class Localization { currentCulture!: CurrentCulture defaultResourceName?: string languages!: Language[] - values!: {[key:string]: {[key:string]: string}} + values!: {[key:string]:{[key:string]:string}} } export class MultiTenancy { - isEnabled!: boolean + isEnabled = false } export class Setting { - values?: {[key:string]: string} + values?: {[key:string]:string} } export interface IAbpConfiguration { @@ -106,4 +111,4 @@ export class AbpConfiguration implements IAbpConfiguration { this.multiTenancy = new MultiTenancy() this.currentTenant = new CurrentTenant() } -} \ No newline at end of file +} diff --git a/vueJs/src/api/settings.ts b/vueJs/src/api/settings.ts index 937165a14..ae8a186bc 100644 --- a/vueJs/src/api/settings.ts +++ b/vueJs/src/api/settings.ts @@ -4,7 +4,7 @@ import ApiService from './serviceBase' const IdentityService = process.env.VUE_APP_BASE_API export default class SettingApiService { - /** + /** * 获取配置 * @param providerName 配置提供者名称 * @param providerKey 配置提供者标识 @@ -13,7 +13,7 @@ export default class SettingApiService { public static getSettings(providerName: string, providerKey: string) { let _url = '/api/abp/setting' _url += '?providerName=' + providerName - _url += '?providerKey=' + providerKey + _url += '&providerKey=' + providerKey return ApiService.Get>(_url, IdentityService) } @@ -24,10 +24,10 @@ export default class SettingApiService { * @param payload 配置变更信息 * @returns Promise对象 */ - public setSettings(providerName: string, providerKey: string, payload: SettingsUpdate) { + public static setSettings(providerName: string, providerKey: string, payload: SettingsUpdate) { let _url = '/api/abp/setting' _url += '?providerName=' + providerName - _url += '?providerKey=' + providerKey + _url += '&providerKey=' + providerKey return ApiService.Put(_url, payload, IdentityService) } } @@ -36,17 +36,24 @@ export class SettingBase { /** 名称 */ name!: string /** 当前设置值 */ - value!: string + value!: any } /** 设置对象 */ -export class Setting extends SettingBase{ +export class Setting extends SettingBase { /** 显示名称 */ displayName!: string /** 说明 */ description!: string /** 默认设置 */ defaultValue!: string + + public getValue() { + if (this.value) { + return this.value + } + return this.defaultValue + } } /** 配置变更对象 */ @@ -56,8 +63,8 @@ export class SettingUpdate extends SettingBase {} export class SettingsUpdate { /** 配置集合 */ settings: SettingUpdate[] - + constructor() { this.settings = new Array() } -} \ No newline at end of file +} diff --git a/vueJs/src/lang/zh.ts b/vueJs/src/lang/zh.ts index 0f83d9487..c7af72e08 100644 --- a/vueJs/src/lang/zh.ts +++ b/vueJs/src/lang/zh.ts @@ -197,7 +197,13 @@ export default { showTagsView: '显示 Tags-View', showSidebarLogo: '显示侧边栏 Logo', fixedHeader: '固定 Header', - sidebarTextTheme: '侧边栏文字主题色' + sidebarTextTheme: '侧边栏文字主题色', + globalSetting: '公共设置', + tenantSetting: '租户设置', + userSetting: '用户设置', + passwordSecurity: '密码安全', + systemSetting: '系统配置', + userAccount: '用户账户' }, task: { title: '任务管理' diff --git a/vueJs/src/permission.ts b/vueJs/src/permission.ts index acb71b769..e70bab3c7 100644 --- a/vueJs/src/permission.ts +++ b/vueJs/src/permission.ts @@ -4,6 +4,7 @@ import 'nprogress/nprogress.css' import { Message } from 'element-ui' import { Route } from 'vue-router' import { UserModule } from '@/store/modules/user' +import { AbpConfigurationModule } from '@/store/modules/abp' import { PermissionModule } from '@/store/modules/permission' import i18n from '@/lang' // Internationalization import settings from './settings' diff --git a/vueJs/src/store/modules/abp.ts b/vueJs/src/store/modules/abp.ts index 89fccf153..43d26d99f 100644 --- a/vueJs/src/store/modules/abp.ts +++ b/vueJs/src/store/modules/abp.ts @@ -1,20 +1,22 @@ import store from '@/store' -import { getAbpConfig, setAbpConfig } from '@/utils/localStorage' -import AbpConfigurationService, { IAbpConfiguration } from '@/api/abpconfiguration' +import { getItemJson, setItem } from '@/utils/localStorage' +import AbpConfigurationService, { IAbpConfiguration, AbpConfiguration as AbpConfig } from '@/api/abpconfiguration' import { VuexModule, Module, Mutation, Action, getModule } from 'vuex-module-decorators' export interface IAbpConfigurationState { configuration: IAbpConfiguration } +const abpConfigurationKey = 'vue_admin_abp_configuration' + @Module({ dynamic: true, store, name: 'abpconfiguration' }) class AbpConfiguration extends VuexModule implements IAbpConfigurationState { - configuration = getAbpConfig() + configuration = getItemJson(abpConfigurationKey) || new AbpConfig() @Mutation private SET_ABPCONFIGURATION(configuration: IAbpConfiguration) { this.configuration = configuration - setAbpConfig(configuration) + setItem(abpConfigurationKey, JSON.stringify(configuration)) } @Action({ rawError: true }) @@ -24,4 +26,4 @@ class AbpConfiguration extends VuexModule implements IAbpConfigurationState { } } -export const AbpConfigurationModule = getModule(AbpConfiguration) \ No newline at end of file +export const AbpConfigurationModule = getModule(AbpConfiguration) diff --git a/vueJs/src/store/modules/user.ts b/vueJs/src/store/modules/user.ts index ec001c36f..f0cef9e6a 100644 --- a/vueJs/src/store/modules/user.ts +++ b/vueJs/src/store/modules/user.ts @@ -1,7 +1,7 @@ import { VuexModule, Module, Action, Mutation, getModule } from 'vuex-module-decorators' import UserApiService, { UserLoginData, UserLoginPhoneData } from '@/api/users' import TenantService from '@/api/tenant' -import { getToken, setToken, removeToken, getRefreshToken, setRefreshToken } from '@/utils/localStorage' +import { getItem, setItem, removeItem } from '@/utils/localStorage' import { resetRouter } from '@/router' import { TagsViewModule } from './tags-view' import { removeTenant, setTenant } from '@/utils/sessions' @@ -11,15 +11,20 @@ import store from '@/store' export interface IUserState { token: string + refreshToken: string id: string name: string roles: string[] email: string } +const tokenKey = 'vue_typescript_admin_token' +const refreshTokenKey = 'vue_typescript_admin_refresh_token' + @Module({ dynamic: true, store, name: 'user' }) class User extends VuexModule implements IUserState { - public token = getToken() || '' + public token = getItem(tokenKey) + public refreshToken = getItem(refreshTokenKey) public id = '' public name = '' public roles: string[] = [] @@ -28,7 +33,13 @@ class User extends VuexModule implements IUserState { @Mutation private SET_TOKEN(token: string) { this.token = token - setToken(token) + setItem(tokenKey, token) + } + + @Mutation + private SET_REFRESHTOKEN(token: string) { + this.refreshToken = token + setItem(refreshTokenKey, token) } @Mutation @@ -62,7 +73,7 @@ class User extends VuexModule implements IUserState { const loginResult = await UserApiService.userLogin(userLoginData) const token = loginResult.token_type + ' ' + loginResult.access_token this.SET_TOKEN(token) - setRefreshToken(loginResult.refresh_token) + this.SET_REFRESHTOKEN(loginResult.refresh_token) await this.PostLogin() } @@ -77,13 +88,13 @@ class User extends VuexModule implements IUserState { const loginResult = await UserApiService.userLoginWithPhone(userLoginData) const token = loginResult.token_type + ' ' + loginResult.access_token this.SET_TOKEN(token) - setRefreshToken(loginResult.refresh_token) + this.SET_REFRESHTOKEN(loginResult.refresh_token) await this.PostLogin() } @Action public ResetToken() { - removeToken() + removeItem(tokenKey) removeTenant() this.SET_TOKEN('') this.SET_ROLES([]) @@ -106,13 +117,15 @@ class User extends VuexModule implements IUserState { if (this.token === '') { throw Error('LogOut: token is undefined!') } - const token = getRefreshToken() + const token = getItem(refreshTokenKey) if (token) { await UserApiService.userLogout(token) } this.SET_TOKEN('') + this.SET_REFRESHTOKEN('') this.SET_ROLES([]) - removeToken() + removeItem(tokenKey) + removeItem(refreshTokenKey) removeTenant() resetRouter() // Reset visited views and cached views @@ -124,12 +137,12 @@ class User extends VuexModule implements IUserState { @Action public RefreshSession() { return new Promise((resolve, reject) => { - const token = getToken() + const token = getItem(tokenKey) if (token) { UserApiService.refreshToken(token).then(result => { const token = result.token_type + ' ' + result.access_token - setRefreshToken(result.refresh_token) this.SET_TOKEN(token) + this.SET_REFRESHTOKEN(result.refresh_token) return resolve(result) }).catch(error => { return reject(error) diff --git a/vueJs/src/utils/localStorage.ts b/vueJs/src/utils/localStorage.ts index 92a1450c9..999532046 100644 --- a/vueJs/src/utils/localStorage.ts +++ b/vueJs/src/utils/localStorage.ts @@ -1,38 +1,27 @@ -import { AbpConfiguration } from '@/api/abpconfiguration' -const abpConfigKey = 'vue_admin_abp_configuration' -export const getAbpConfig = () => { - const abpConfigItem = localStorage.getItem(abpConfigKey) - if (abpConfigItem) { - return JSON.parse(abpConfigItem) as AbpConfiguration +export function getItem(key: string) { + const item = localStorage.getItem(key) + if (item) { + return item } - return new AbpConfiguration() -} -export const setAbpConfig = (abpConfig: AbpConfiguration) => { - const abpConfigItem = JSON.stringify(abpConfig) - localStorage.setItem(abpConfigKey, abpConfigItem) + return '' } -export const removeAbpConfig = () => localStorage.removeItem(abpConfigKey) -// User -const tokenKey = 'vue_typescript_admin_token' -const refreshTokenKey = 'vue_typescript_admin_refresh_token' -export function getToken() { - const tokenItem = localStorage.getItem(tokenKey) - if (tokenItem) { - return tokenItem +export function getItemJson(key: string) { + const item = localStorage.getItem(key) + if (item) { + return JSON.parse(item) } - return '' + return null } -export const setToken = (token: string) => localStorage.setItem(tokenKey, token) -export const getRefreshToken = () => { - const tokenItem = localStorage.getItem(refreshTokenKey) - if (tokenItem) { - return tokenItem - } - return '' + +export function setItem(key: string, value: string) { + localStorage.setItem(key, value) +} + +export function removeItem(key: string) { + localStorage.removeItem(key) +} + +export function clear() { + localStorage.clear() } -export const setRefreshToken = (token: string) => localStorage.setItem(refreshTokenKey, token) -export const removeToken = () => { - localStorage.removeItem(tokenKey) - localStorage.removeItem(refreshTokenKey) -} \ No newline at end of file diff --git a/vueJs/src/utils/request.ts b/vueJs/src/utils/request.ts index 512115a62..3b53611d6 100644 --- a/vueJs/src/utils/request.ts +++ b/vueJs/src/utils/request.ts @@ -4,7 +4,6 @@ import { MessageBox, Notification } from 'element-ui' import { UserModule } from '@/store/modules/user' import { getTenant } from '@/utils/sessions' import { getLanguage } from '@/utils/cookies' -import { getToken, getRefreshToken } from '@/utils/localStorage' const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url @@ -18,10 +17,9 @@ service.interceptors.request.use( if (config.url === '/connect/token') { return config } - const token = getToken() // Add X-Access-Token header to every request, you can add other custom headers here - if (token) { - config.headers.Authorization = token + if (UserModule.token) { + config.headers.Authorization = UserModule.token } const tenantId = getTenant() if (tenantId) { @@ -100,8 +98,7 @@ service.interceptors.response.use( (error) => { showError(error.response.data, error.response.status) if (error.response.status === 401) { - const token = getRefreshToken() - if (token) { + if (UserModule.refreshToken) { UserModule.RefreshSession().then(() => { return service.request(error.config) }).catch(() => { @@ -110,7 +107,7 @@ service.interceptors.response.use( l('login.confirmLogout'), { confirmButtonText: l('login.relogin'), - cancelButtonText: l('global.cancel'), + cancelButtonText: l('global.cancel'), type: 'error' }).then(() => { UserModule.ResetToken() diff --git a/vueJs/src/views/admin/apigateway/components/GlobalCreateOrEditForm.vue b/vueJs/src/views/admin/apigateway/components/GlobalCreateOrEditForm.vue index 65f6b9fb5..81e1eedb1 100644 --- a/vueJs/src/views/admin/apigateway/components/GlobalCreateOrEditForm.vue +++ b/vueJs/src/views/admin/apigateway/components/GlobalCreateOrEditForm.vue @@ -445,7 +445,7 @@ export default class extends Vue { } private onCancel() { - this.appId = '' + this.globalConfiguration = new GlobalConfigurationDto() const formGlobal = this.$refs.formGlobal as any formGlobal.resetFields() this.$emit('closed', false) diff --git a/vueJs/src/views/admin/identityServer/api-resources/components/ApiResourceCreateOrEditForm.vue b/vueJs/src/views/admin/identityServer/api-resources/components/ApiResourceCreateOrEditForm.vue index 8bc90eaac..e863c6ba0 100644 --- a/vueJs/src/views/admin/identityServer/api-resources/components/ApiResourceCreateOrEditForm.vue +++ b/vueJs/src/views/admin/identityServer/api-resources/components/ApiResourceCreateOrEditForm.vue @@ -91,7 +91,7 @@ export default class extends Vue { private apiResource: ApiResource private apiResourceRules = { name: [ - { required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.apiResourceName') }), trigger: 'blur' } + { required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.resourceName') }), trigger: 'blur' } ] } diff --git a/vueJs/src/views/admin/identityServer/client/components/ClientPropertyEditForm.vue b/vueJs/src/views/admin/identityServer/client/components/ClientPropertyEditForm.vue index d2028b8bf..42b1116dc 100644 --- a/vueJs/src/views/admin/identityServer/client/components/ClientPropertyEditForm.vue +++ b/vueJs/src/views/admin/identityServer/client/components/ClientPropertyEditForm.vue @@ -36,7 +36,7 @@ :disabled="!checkPermission(['IdentityServer.Clients.Properties.Create'])" @click="onSaveClientProperty" > - {{ $t('identityServer.createProperty') }} + {{ $t('identityServer.createClientProperty') }} diff --git a/vueJs/src/views/admin/settings/components/GlobalSettingEditForm.vue b/vueJs/src/views/admin/settings/components/GlobalSettingEditForm.vue new file mode 100644 index 000000000..1adb06e79 --- /dev/null +++ b/vueJs/src/views/admin/settings/components/GlobalSettingEditForm.vue @@ -0,0 +1,277 @@ + + + diff --git a/vueJs/src/views/admin/settings/index.vue b/vueJs/src/views/admin/settings/index.vue index e52695c9d..80a9030e0 100644 --- a/vueJs/src/views/admin/settings/index.vue +++ b/vueJs/src/views/admin/settings/index.vue @@ -1,53 +1,37 @@