Browse Source

Merge pull request #265 from colinin/4.3

Add an aggregation api-gateway based on the /api/abp/api-definition e…
pull/274/head
cKey 5 years ago
committed by GitHub
parent
commit
42b37e9200
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      aspnet-core/LINGYUN.MicroService.All.sln
  2. 7
      aspnet-core/LINGYUN.MicroService.ApiGateway.sln
  3. 15
      aspnet-core/modules/common/LINGYUN.Abp.EventBus.CAP/DotNetCore/CAP/ConsumerServiceSelector.cs
  4. 168
      aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/AbpApiGatewayHostModule.cs
  5. 11
      aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/AbpApiGatewayOptions.cs
  6. 13
      aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/Dockerfile
  7. 28
      aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/LINGYUN.Abp.ApiGateway.Host.csproj
  8. 6
      aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/LINGYUN.Abp.ApiGateway.Host.csproj.user
  9. 185
      aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/Ocelot/Configuration/Repository/AbpApiDescriptionFileConfigurationRepository.cs
  10. 123
      aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/Ocelot/Extenssions/OcelotMiddlewareExtensions.cs
  11. 51
      aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/Ocelot/Middleware/Multiplexer/AbpResponseMergeAggregator.cs
  12. 47
      aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/Program.cs
  13. 20
      aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/Properties/launchSettings.json
  14. 18
      aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/Startup.cs
  15. 22
      aspnet-core/services/apigateway/LINGYUN.ApiGateway.Host/Program.cs
  16. 137
      vueJs/package-lock.json

7
aspnet-core/LINGYUN.MicroService.All.sln

@ -331,6 +331,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Rules.RulesEngi
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.AspNetCore.HttpOverrides", "modules\common\LINGYUN.Abp.AspNetCore.HttpOverrides\LINGYUN.Abp.AspNetCore.HttpOverrides.csproj", "{13219C1C-23E1-4EBA-93FB-86830C93A800}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.AspNetCore.HttpOverrides", "modules\common\LINGYUN.Abp.AspNetCore.HttpOverrides\LINGYUN.Abp.AspNetCore.HttpOverrides.csproj", "{13219C1C-23E1-4EBA-93FB-86830C93A800}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.ApiGateway.Host", "services\apigateway\LINGYUN.Abp.ApiGateway.Host\LINGYUN.Abp.ApiGateway.Host.csproj", "{A320E23E-792D-4736-B963-381F9D7AF605}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -865,6 +867,10 @@ Global
{13219C1C-23E1-4EBA-93FB-86830C93A800}.Debug|Any CPU.Build.0 = Debug|Any CPU {13219C1C-23E1-4EBA-93FB-86830C93A800}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13219C1C-23E1-4EBA-93FB-86830C93A800}.Release|Any CPU.ActiveCfg = Release|Any CPU {13219C1C-23E1-4EBA-93FB-86830C93A800}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13219C1C-23E1-4EBA-93FB-86830C93A800}.Release|Any CPU.Build.0 = Release|Any CPU {13219C1C-23E1-4EBA-93FB-86830C93A800}.Release|Any CPU.Build.0 = Release|Any CPU
{A320E23E-792D-4736-B963-381F9D7AF605}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A320E23E-792D-4736-B963-381F9D7AF605}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A320E23E-792D-4736-B963-381F9D7AF605}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A320E23E-792D-4736-B963-381F9D7AF605}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -1028,6 +1034,7 @@ Global
{4D83BDA7-2059-41C7-85AE-FEFAD5CD9498} = {6084D52D-775B-4A39-8CD5-AA2F362B5A61} {4D83BDA7-2059-41C7-85AE-FEFAD5CD9498} = {6084D52D-775B-4A39-8CD5-AA2F362B5A61}
{8EF31071-3521-409D-9740-BBFBFC04C50E} = {370D7CD5-1E17-4F3D-BBFA-03429F6D4F2F} {8EF31071-3521-409D-9740-BBFBFC04C50E} = {370D7CD5-1E17-4F3D-BBFA-03429F6D4F2F}
{13219C1C-23E1-4EBA-93FB-86830C93A800} = {8AC72641-30D3-4ACF-89FA-808FADC55C2E} {13219C1C-23E1-4EBA-93FB-86830C93A800} = {8AC72641-30D3-4ACF-89FA-808FADC55C2E}
{A320E23E-792D-4736-B963-381F9D7AF605} = {19E6BD61-062B-4FAD-A51A-B55F5CE88B7A}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718} SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718}

7
aspnet-core/LINGYUN.MicroService.ApiGateway.sln

@ -33,6 +33,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.EventBus.CAP",
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.AspNetCore.HttpOverrides", "modules\common\LINGYUN.Abp.AspNetCore.HttpOverrides\LINGYUN.Abp.AspNetCore.HttpOverrides.csproj", "{7588F35B-7C0E-4D80-B43A-8A5C9AC6FE03}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.AspNetCore.HttpOverrides", "modules\common\LINGYUN.Abp.AspNetCore.HttpOverrides\LINGYUN.Abp.AspNetCore.HttpOverrides.csproj", "{7588F35B-7C0E-4D80-B43A-8A5C9AC6FE03}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.ApiGateway.Host", "services\apigateway\LINGYUN.Abp.ApiGateway.Host\LINGYUN.Abp.ApiGateway.Host.csproj", "{A38139F5-A856-4CA2-89F8-51798ED557CC}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -83,6 +85,10 @@ Global
{7588F35B-7C0E-4D80-B43A-8A5C9AC6FE03}.Debug|Any CPU.Build.0 = Debug|Any CPU {7588F35B-7C0E-4D80-B43A-8A5C9AC6FE03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7588F35B-7C0E-4D80-B43A-8A5C9AC6FE03}.Release|Any CPU.ActiveCfg = Release|Any CPU {7588F35B-7C0E-4D80-B43A-8A5C9AC6FE03}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7588F35B-7C0E-4D80-B43A-8A5C9AC6FE03}.Release|Any CPU.Build.0 = Release|Any CPU {7588F35B-7C0E-4D80-B43A-8A5C9AC6FE03}.Release|Any CPU.Build.0 = Release|Any CPU
{A38139F5-A856-4CA2-89F8-51798ED557CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A38139F5-A856-4CA2-89F8-51798ED557CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A38139F5-A856-4CA2-89F8-51798ED557CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A38139F5-A856-4CA2-89F8-51798ED557CC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -101,6 +107,7 @@ Global
{D6629DD3-BA0F-44B5-A97F-3B992ECAB85D} = {630FB448-8C5C-438F-930D-B0209407DE6A} {D6629DD3-BA0F-44B5-A97F-3B992ECAB85D} = {630FB448-8C5C-438F-930D-B0209407DE6A}
{1DA1835B-9EA4-4095-A8CF-10E2778206D3} = {D6629DD3-BA0F-44B5-A97F-3B992ECAB85D} {1DA1835B-9EA4-4095-A8CF-10E2778206D3} = {D6629DD3-BA0F-44B5-A97F-3B992ECAB85D}
{7588F35B-7C0E-4D80-B43A-8A5C9AC6FE03} = {D6629DD3-BA0F-44B5-A97F-3B992ECAB85D} {7588F35B-7C0E-4D80-B43A-8A5C9AC6FE03} = {D6629DD3-BA0F-44B5-A97F-3B992ECAB85D}
{A38139F5-A856-4CA2-89F8-51798ED557CC} = {F3B1B755-37B6-420B-9E82-A5BDFF2BF647}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B327C65A-BD15-480D-825E-9D5A870C521A} SolutionGuid = {B327C65A-BD15-480D-825E-9D5A870C521A}

15
aspnet-core/modules/common/LINGYUN.Abp.EventBus.CAP/DotNetCore/CAP/ConsumerServiceSelector.cs

@ -20,6 +20,10 @@ namespace DotNetCore.CAP
public class ConsumerServiceSelector : Internal.ConsumerServiceSelector public class ConsumerServiceSelector : Internal.ConsumerServiceSelector
{ {
/// <summary>
/// CAP配置
/// </summary>
protected CapOptions CapOptions { get; }
/// <summary> /// <summary>
/// Abp分布式事件配置 /// Abp分布式事件配置
/// </summary> /// </summary>
@ -34,8 +38,10 @@ namespace DotNetCore.CAP
/// </summary> /// </summary>
public ConsumerServiceSelector( public ConsumerServiceSelector(
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
IOptions<CapOptions> capOptions,
IOptions<AbpDistributedEventBusOptions> distributedEventBusOptions) : base(serviceProvider) IOptions<AbpDistributedEventBusOptions> distributedEventBusOptions) : base(serviceProvider)
{ {
CapOptions = capOptions.Value;
ServiceProvider = serviceProvider; ServiceProvider = serviceProvider;
AbpDistributedEventBusOptions = distributedEventBusOptions.Value; AbpDistributedEventBusOptions = distributedEventBusOptions.Value;
} }
@ -70,9 +76,12 @@ namespace DotNetCore.CAP
if (executorDescriptorList.Any(x => new ConsumerExecutorDescriptorComparer().Equals(x, consumerExecutorDescriptor))) if (executorDescriptorList.Any(x => new ConsumerExecutorDescriptorComparer().Equals(x, consumerExecutorDescriptor)))
{ {
// 如果存在多个消费者,后续的消费者需要重新定义分组才能不被 CAP 框架过滤掉 // 如果存在多个消费者,后续的消费者需要重新定义分组才能不被 CAP 框架过滤掉
consumerExecutorDescriptor.Attribute.Group = handler.IsGenericType var groupAliaName = handler.IsGenericType
? handler.GetGenericTypeDefinition().FullName ? handler.GetGenericTypeDefinition().Name
: handler.FullName; : handler.Name;
// TODO: 2021-05-21 直接使用类型全名作为GroupName会引起用户困惑,加上组别名称在前
consumerExecutorDescriptor.Attribute.Group = $"{CapOptions.DefaultGroupName}.{groupAliaName}";
SetSubscribeAttribute(consumerExecutorDescriptor.Attribute); SetSubscribeAttribute(consumerExecutorDescriptor.Attribute);
} }

168
aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/AbpApiGatewayHostModule.cs

@ -0,0 +1,168 @@
using LINGYUN.Abp.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Ocelot.Configuration.Repository;
using Ocelot.DependencyInjection;
using Ocelot.Extenssions;
using Ocelot.Middleware.Multiplexer;
using Ocelot.Provider.Polly;
using StackExchange.Redis;
using System;
using System.Text;
using Volo.Abp;
using Volo.Abp.AspNetCore;
using Volo.Abp.Autofac;
using Volo.Abp.Caching;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.Http.Client;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
using Volo.Abp.Security.Encryption;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.ApiGateway
{
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpHttpClientModule),
typeof(AbpCachingStackExchangeRedisModule),
typeof(AbpAspNetCoreModule),
typeof(AbpAspNetCoreHttpOverridesModule)
)]
public class AbpApiGatewayHostModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHttpClient("_AbpApiDefinitionClient");
context.Services.AddSingleton<IFileConfigurationRepository, AbpApiDescriptionFileConfigurationRepository>();
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration();
// 解决某些不支持类型的序列化
Configure<AbpJsonOptions>(options =>
{
// See: https://docs.abp.io/en/abp/4.0/Migration-Guides/Abp-4_0#always-use-the-newtonsoft-json
options.UseHybridSerializer = false;
});
// 中文序列化的编码问题
//Configure<AbpSystemTextJsonSerializerOptions>(options =>
//{
// options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
//});
Configure<AbpApiGatewayOptions>(configuration.GetSection("ApiGateway"));
context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = false;
options.Audience = configuration["AuthServer:ApiName"];
});
Configure<AbpDistributedCacheOptions>(options =>
{
// 最好统一命名,不然某个缓存变动其他应用服务有例外发生
options.KeyPrefix = "LINGYUN.Abp.Application";
// 滑动过期30天
options.GlobalCacheEntryOptions.SlidingExpiration = TimeSpan.FromDays(30);
// 绝对过期60天
options.GlobalCacheEntryOptions.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(60);
});
Configure<RedisCacheOptions>(options =>
{
var redisConfig = ConfigurationOptions.Parse(options.Configuration);
options.ConfigurationOptions = redisConfig;
options.InstanceName = configuration["Redis:InstanceName"];
});
// 加解密
Configure<AbpStringEncryptionOptions>(options =>
{
var encryptionConfiguration = configuration.GetSection("Encryption");
if (encryptionConfiguration.Exists())
{
options.DefaultPassPhrase = encryptionConfiguration["PassPhrase"] ?? options.DefaultPassPhrase;
options.DefaultSalt = encryptionConfiguration.GetSection("Salt").Exists()
? Encoding.ASCII.GetBytes(encryptionConfiguration["Salt"])
: options.DefaultSalt;
options.InitVectorBytes = encryptionConfiguration.GetSection("InitVector").Exists()
? Encoding.ASCII.GetBytes(encryptionConfiguration["InitVector"])
: options.InitVectorBytes;
}
});
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpApiGatewayHostModule>();
});
var mvcBuilder = context.Services.AddMvc();
mvcBuilder.AddApplicationPart(typeof(AbpApiGatewayHostModule).Assembly);
Configure<AbpEndpointRouterOptions>(options =>
{
options.EndpointConfigureActions.Add(endpointContext =>
{
endpointContext.Endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}");
endpointContext.Endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
});
if (!hostingEnvironment.IsDevelopment())
{
// Ssl证书
var sslOptions = configuration.GetSection("App:SslOptions");
if (sslOptions.Exists())
{
var fileName = sslOptions["FileName"];
var password = sslOptions["Password"];
Configure<KestrelServerOptions>(options =>
{
options.ConfigureEndpointDefaults(cfg =>
{
cfg.UseHttps(fileName, password);
});
});
}
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
context.Services
.AddDataProtection()
.PersistKeysToStackExchangeRedis(redis, "ApiGatewayHost-Protection-Keys");
}
context.Services
.AddOcelot()
.AddPolly()
.AddSingletonDefinedAggregator<AbpResponseMergeAggregator>();
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
app.UseForwardedHeaders();
app.UseAuditing();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
// 启用ws协议
app.UseWebSockets();
app.UseOcelot().Wait();
}
}
}

11
aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/AbpApiGatewayOptions.cs

@ -0,0 +1,11 @@
using Ocelot.Configuration.File;
using System.Collections.Generic;
namespace LINGYUN.Abp.ApiGateway
{
public class AbpApiGatewayOptions
{
public FileGlobalConfiguration GlobalConfiguration { get; set; } = new FileGlobalConfiguration();
public List<string> AggrageRouteUrls { get; set; } = new List<string>();
}
}

13
aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/Dockerfile

@ -0,0 +1,13 @@
FROM mcr.microsoft.com/dotnet/aspnet:5.0
LABEL maintainer="colin.in@foxmail.com"
WORKDIR /app
COPY . /app
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone
EXPOSE 80/tcp
VOLUME [ "./app/Logs" ]
ENTRYPOINT ["dotnet", "LINGYUN.ApiGateway.Host.dll"]

28
aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/LINGYUN.Abp.ApiGateway.Host.csproj

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>LINGYUN.Abp.ApiGateway</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="5.0.*" />
<PackageReference Include="Ocelot.Provider.Polly" Version="16.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
<PackageReference Include="Serilog.Enrichers.Assembly" Version="2.0.0" />
<PackageReference Include="Serilog.Enrichers.Process" Version="2.0.1" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="Volo.Abp.AspNetCore.Authentication.JwtBearer" Version="4.3.0" />
<PackageReference Include="Volo.Abp.Autofac" Version="4.3.0" />
<PackageReference Include="Volo.Abp.AspNetCore" Version="4.3.0" />
<PackageReference Include="Volo.Abp.Http.Client" Version="4.3.0" />
<PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\modules\common\LINGYUN.Abp.AspNetCore.HttpOverrides\LINGYUN.Abp.AspNetCore.HttpOverrides.csproj" />
</ItemGroup>
</Project>

6
aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/LINGYUN.Abp.ApiGateway.Host.csproj.user

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>IIS Express</ActiveDebugProfile>
</PropertyGroup>
</Project>

185
aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/Ocelot/Configuration/Repository/AbpApiDescriptionFileConfigurationRepository.cs

@ -0,0 +1,185 @@
using LINGYUN.Abp.ApiGateway;
using Microsoft.Extensions.Options;
using Ocelot.Configuration.File;
using Ocelot.Middleware.Multiplexer;
using Ocelot.Responses;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.DynamicProxying;
using Volo.Abp.Http.Modeling;
namespace Ocelot.Configuration.Repository
{
/// <summary>
/// 通过定义的RemoteServices来寻找abp端点定义的服务列表
/// 在AbpApiGatewayOptions.AggrageRouteUrls中定义聚合的url
/// 可以在发现相同端点后加入到聚合路由,聚合路由下游端点会自动加入聚合标识,以发现的聚合数量作为前缀
/// 例如/api/abp/api-definition、/api/abp/application-configuration
/// 下游端点将变成/aggregate1/api/abp/api-definition、/aggregate1/api/abp/application-configuration
/// 上游服务直接调用/api/abp/api-definition、/api/abp/application-configuration即可唤起路由聚合
/// </summary>
public class AbpApiDescriptionFileConfigurationRepository : IFileConfigurationRepository
{
private readonly AbpRemoteServiceOptions _remoteServiceOptions;
private readonly AbpApiGatewayOptions _apiGatewayOptions;
private readonly IApiDescriptionFinder _apiDescriptionFinder;
private readonly IHttpClientFactory _httpClientFactory;
public AbpApiDescriptionFileConfigurationRepository(
IHttpClientFactory httpClientFactory,
IApiDescriptionFinder apiDescriptionFinder,
IOptions<AbpApiGatewayOptions> apiGatewayOptions,
IOptions<AbpRemoteServiceOptions> remoteServiceOptions
)
{
_httpClientFactory = httpClientFactory;
_apiDescriptionFinder = apiDescriptionFinder;
_apiGatewayOptions = apiGatewayOptions.Value;
_remoteServiceOptions = remoteServiceOptions.Value;
}
public async Task<Response<FileConfiguration>> Get()
{
var fileConfiguration = new FileConfiguration
{
GlobalConfiguration = _apiGatewayOptions.GlobalConfiguration
};
var apiDescriptionModels = await GetApiDescriptionsAsync();
int foundAggrageRouteCount = 0;
foreach (var apiDescriptionModel in apiDescriptionModels)
{
foreach (var moduleModel in apiDescriptionModel.Value.Modules)
{
foreach (var controllerModel in moduleModel.Value.Controllers)
{
foreach (var actionModel in controllerModel.Value.Actions)
{
var action = actionModel.Value;
var downstreamUrl = action.Url.EnsureStartsWith('/');
// TODO: 多个相同的下游路由地址应组合为聚合路由
// TODO: 下游路由地址已添加
var route = fileConfiguration.Routes
.Where(route => HasBeenAddedRoute(route, downstreamUrl))
.LastOrDefault();
string aggregateKey = "";
if (route != null)
{
// TODO: 下游方法已添加
if (route.UpstreamHttpMethod.Any(method => method.Equals(action.HttpMethod, StringComparison.CurrentCultureIgnoreCase)))
{
if (_apiGatewayOptions.AggrageRouteUrls.Any(url => url.Equals(downstreamUrl)))
{
foundAggrageRouteCount++;
var aggregateRoute = fileConfiguration.Aggregates
.Where(route => HasBeenAddedAggregateRoute(route, downstreamUrl))
.FirstOrDefault();
if (aggregateRoute == null)
{
aggregateRoute = new FileAggregateRoute
{
RouteIsCaseSensitive = false,
// TODO: 可实现自定义的聚合提供装置
Aggregator = nameof(AbpResponseMergeAggregator),
UpstreamPathTemplate = downstreamUrl,
RouteKeys = new List<string>()
};
fileConfiguration.Aggregates.Add(aggregateRoute);
}
if (route.Key.IsNullOrWhiteSpace())
{
route.Key = $"aggregate{foundAggrageRouteCount}";
route.UpstreamPathTemplate = $"/{route.Key}{route.UpstreamPathTemplate}";
aggregateRoute.RouteKeys.Add(route.Key);
foundAggrageRouteCount++;
}
aggregateKey = $"aggregate{foundAggrageRouteCount}";
aggregateRoute.RouteKeys.Add(aggregateKey);
}
else
{
continue;
}
}
else
{
route.UpstreamHttpMethod.Add(action.HttpMethod);
continue;
}
}
var newRoute = new FileRoute
{
Key = aggregateKey,
UpstreamPathTemplate = (aggregateKey + downstreamUrl).EnsureStartsWith('/'),
DownstreamPathTemplate = downstreamUrl,
DangerousAcceptAnyServerCertificateValidator = false
};
var baseUrl = apiDescriptionModel.Key;
baseUrl = baseUrl.StartsWith("http://") ? baseUrl[7..] : baseUrl;
baseUrl = baseUrl.StartsWith("https://") ? baseUrl[8..] : baseUrl;
baseUrl = baseUrl.EndsWith("/") ? baseUrl[0..^1] : baseUrl;
var addresses = baseUrl.Split(":");
var hostAndPort = new FileHostAndPort
{
Host = addresses[0]
};
if (addresses.Length == 2)
{
hostAndPort.Port = int.Parse(addresses[1]);
}
newRoute.DownstreamHostAndPorts.Add(hostAndPort);
newRoute.UpstreamHttpMethod.Add(action.HttpMethod);
// abp api版本号支持通过query发送
//newRoute.DownstreamHttpVersion = action.SupportedVersions.Last();
fileConfiguration.Routes.Add(newRoute);
}
}
}
}
return new OkResponse<FileConfiguration>(fileConfiguration);
}
private static bool HasBeenAddedRoute(FileRoute route, string downstreamUrl)
{
return route.DownstreamPathTemplate.Equals(downstreamUrl, StringComparison.CurrentCultureIgnoreCase);
}
private static bool HasBeenAddedAggregateRoute(FileAggregateRoute route, string upstreamUrl)
{
return route.UpstreamPathTemplate.Equals(upstreamUrl, StringComparison.CurrentCultureIgnoreCase);
}
public async Task<Response> Set(FileConfiguration fileConfiguration)
{
return await Task.FromResult(new OkResponse<FileConfiguration>(fileConfiguration));
}
protected virtual async Task<Dictionary<string, ApplicationApiDescriptionModel>> GetApiDescriptionsAsync()
{
var client = _httpClientFactory.CreateClient("_AbpApiDefinitionClient");
var apiDescriptionModels = new Dictionary<string, ApplicationApiDescriptionModel>();
foreach (var remoteService in _remoteServiceOptions.RemoteServices)
{
var model = await _apiDescriptionFinder.GetApiDescriptionAsync(client, remoteService.Value.BaseUrl);
apiDescriptionModels.Add(remoteService.Value.BaseUrl, model);
}
return apiDescriptionModels;
}
}
}

123
aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/Ocelot/Extenssions/OcelotMiddlewareExtensions.cs

@ -0,0 +1,123 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.Repository;
using Ocelot.DependencyInjection;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.Responses;
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Ocelot.Extenssions
{
public static class OcelotMiddlewareExtensions
{
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder)
{
await builder.UseOcelot(new OcelotPipelineConfiguration());
return builder;
}
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, Action<OcelotPipelineConfiguration> pipelineConfiguration)
{
var config = new OcelotPipelineConfiguration();
pipelineConfiguration?.Invoke(config);
return await builder.UseOcelot(config);
}
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)
{
var configuration = await CreateConfiguration(builder);
ConfigureDiagnosticListener(builder);
return CreateOcelotPipeline(builder, pipelineConfiguration);
}
public static Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder app, Action<IApplicationBuilder, OcelotPipelineConfiguration> builderAction)
=> UseOcelot(app, builderAction, new OcelotPipelineConfiguration());
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder app, Action<IApplicationBuilder, OcelotPipelineConfiguration> builderAction, OcelotPipelineConfiguration configuration)
{
await CreateConfiguration(app);
ConfigureDiagnosticListener(app);
builderAction?.Invoke(app, configuration ?? new OcelotPipelineConfiguration());
app.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware";
return app;
}
private static IApplicationBuilder CreateOcelotPipeline(IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)
{
builder.BuildOcelotPipeline(pipelineConfiguration);
/*
inject first delegate into first piece of asp.net middleware..maybe not like this
then because we are updating the http context in ocelot it comes out correct for
rest of asp.net..
*/
builder.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware";
return builder;
}
private static async Task<IInternalConfiguration> CreateConfiguration(IApplicationBuilder builder)
{
var fileConfigRepo = builder.ApplicationServices.GetRequiredService<IFileConfigurationRepository>();
var fileConfig = await fileConfigRepo.Get();
// IOptionsMonitor<FileConfiguration> fileConfig = builder.ApplicationServices.GetService<IOptionsMonitor<FileConfiguration>>();
IInternalConfigurationCreator internalConfigCreator = builder.ApplicationServices.GetService<IInternalConfigurationCreator>();
Response<IInternalConfiguration> response = await internalConfigCreator.Create(fileConfig.Data);
if (response.IsError)
{
ThrowToStopOcelotStarting(response);
}
IInternalConfigurationRepository internalConfigRepo = builder.ApplicationServices.GetService<IInternalConfigurationRepository>();
internalConfigRepo.AddOrReplace(response.Data);
return GetOcelotConfigAndReturn(internalConfigRepo);
}
private static bool AdministrationApiInUse(IAdministrationPath adminPath)
{
return adminPath != null;
}
private static bool IsError(Response response)
{
return response == null || response.IsError;
}
private static IInternalConfiguration GetOcelotConfigAndReturn(IInternalConfigurationRepository provider)
{
var ocelotConfiguration = provider.Get();
if (ocelotConfiguration?.Data == null || ocelotConfiguration.IsError)
{
ThrowToStopOcelotStarting(ocelotConfiguration);
}
return ocelotConfiguration.Data;
}
private static void ThrowToStopOcelotStarting(Response config)
{
throw new Exception($"Unable to start Ocelot, errors are: {string.Join(",", config.Errors.Select(x => x.ToString()))}");
}
private static void ConfigureDiagnosticListener(IApplicationBuilder builder)
{
var env = builder.ApplicationServices.GetService<IWebHostEnvironment>();
var listener = builder.ApplicationServices.GetService<OcelotDiagnosticListener>();
var diagnosticListener = builder.ApplicationServices.GetService<DiagnosticListener>();
diagnosticListener.SubscribeWithAdapter(listener);
}
}
}

51
aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/Ocelot/Middleware/Multiplexer/AbpResponseMergeAggregator.cs

@ -0,0 +1,51 @@
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Ocelot.Multiplexer;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace Ocelot.Middleware.Multiplexer
{
public class AbpResponseMergeAggregator : IDefinedAggregator
{
public async Task<DownstreamResponse> Aggregate(List<HttpContext> responses)
{
return await MapAbpApiDefinitionAggregateContentAsync(responses);
}
protected virtual async Task<DownstreamResponse> MapAbpApiDefinitionAggregateContentAsync(List<HttpContext> responses)
{
JObject responseObject = null;
JsonMergeSettings mergeSetting = new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Union,
PropertyNameComparison = System.StringComparison.CurrentCultureIgnoreCase
};
foreach (var httpResponse in responses)
{
var content = await httpResponse.Items.DownstreamResponse().Content.ReadAsStringAsync();
var contentObject = JsonConvert.DeserializeObject(content);
if (responseObject == null)
{
responseObject = JObject.FromObject(contentObject);
}
else
{
responseObject.Merge(contentObject, mergeSetting);
}
}
var stringContent = new StringContent(responseObject.ToString())
{
Headers = { ContentType = new MediaTypeHeaderValue("application/json") }
};
stringContent.Headers.Add("_AbpErrorFormat", "true");
return new DownstreamResponse(stringContent, HttpStatusCode.OK,
new List<KeyValuePair<string, IEnumerable<string>>>(), "OK");
}
}
}

47
aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/Program.cs

@ -0,0 +1,47 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Serilog;
using System;
using System.IO;
namespace LINGYUN.Abp.ApiGateway
{
public class Program
{
public static int Main(string[] args)
{
try
{
var hostBuilder = CreateHostBuilder(args).Build();
Log.Information("Starting web host.");
hostBuilder.Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly!");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
internal static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseKestrel()
.UseStartup<Startup>();
})
.UseSerilog((context, provider, config) =>
{
config.ReadFrom.Configuration(context.Configuration);
})
.UseAutofac();
}
}

20
aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/Properties/launchSettings.json

@ -0,0 +1,20 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:54450/",
"sslPort": 0
}
},
"profiles": {
"LINGYUN.Abp.ApiGateway.Host": {
"commandName": "Project",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:30000"
}
}
}

18
aspnet-core/services/apigateway/LINGYUN.Abp.ApiGateway.Host/Startup.cs

@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace LINGYUN.Abp.ApiGateway
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<AbpApiGatewayHostModule>();
}
public void Configure(IApplicationBuilder app)
{
app.InitializeApplication();
}
}
}

22
aspnet-core/services/apigateway/LINGYUN.ApiGateway.Host/Program.cs

@ -11,19 +11,12 @@ namespace LINGYUN.ApiGateway
{ {
public static int Main(string[] args) public static int Main(string[] args)
{ {
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile($"appsettings.{env}.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
try try
{ {
var hostBuilder = CreateHostBuilder(args).Build();
Log.Information("Starting web host."); Log.Information("Starting web host.");
CreateHostBuilder(args).Build().Run(); hostBuilder.Run();
return 0; return 0;
} }
catch (Exception ex) catch (Exception ex)
@ -41,9 +34,14 @@ namespace LINGYUN.ApiGateway
Host.CreateDefaultBuilder(args) Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => .ConfigureWebHostDefaults(webBuilder =>
{ {
webBuilder.UseStartup<Startup>(); webBuilder
.UseKestrel()
.UseStartup<Startup>();
})
.UseSerilog((context, provider, config) =>
{
config.ReadFrom.Configuration(context.Configuration);
}) })
.UseSerilog()
.UseAutofac(); .UseAutofac();
} }
} }

137
vueJs/package-lock.json

@ -5949,9 +5949,9 @@
} }
}, },
"concurrently": { "concurrently": {
"version": "5.2.0", "version": "5.3.0",
"resolved": "https://registry.npm.taobao.org/concurrently/download/concurrently-5.2.0.tgz", "resolved": "https://registry.nlark.com/concurrently/download/concurrently-5.3.0.tgz?cache=0&sync_timestamp=1620443100193&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fconcurrently%2Fdownload%2Fconcurrently-5.3.0.tgz",
"integrity": "sha1-6tVRIdCKD8gXCFWEwSPO3sLgiXU=", "integrity": "sha1-dQDeZBDQQ8kSston3jICy0ibHns=",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "^2.4.2", "chalk": "^2.4.2",
@ -8338,8 +8338,8 @@
"dev": true "dev": true
}, },
"eve": { "eve": {
"version": "git://github.com/adobe-webplatform/eve.git#eef80ed8d188423c2272746fb8ae5cc8dad84cb1", "version": "git+ssh://git@github.com/adobe-webplatform/eve.git#eef80ed8d188423c2272746fb8ae5cc8dad84cb1",
"from": "git://github.com/adobe-webplatform/eve.git#eef80ed" "from": "eve@git://github.com/adobe-webplatform/eve.git#eef80ed"
}, },
"event-pubsub": { "event-pubsub": {
"version": "4.3.0", "version": "4.3.0",
@ -18346,10 +18346,10 @@
"dev": true "dev": true
}, },
"raphael": { "raphael": {
"version": "git+https://github.com/nhn/raphael.git#78a6ed3ec269f33b6457b0ec66f8c3d1f2ed70e0", "version": "git+ssh://git@github.com/nhn/raphael.git#78a6ed3ec269f33b6457b0ec66f8c3d1f2ed70e0",
"from": "git+https://github.com/nhn/raphael.git#2.2.0-c", "from": "raphael@git+https://github.com/nhn/raphael.git#2.2.0-c",
"requires": { "requires": {
"eve": "git://github.com/adobe-webplatform/eve.git#eef80ed" "eve": "eve@git://github.com/adobe-webplatform/eve.git#eef80ed"
} }
}, },
"raw-body": { "raw-body": {
@ -18878,17 +18878,118 @@
} }
}, },
"sass": { "sass": {
"version": "1.26.5", "version": "1.32.13",
"resolved": "https://registry.npm.taobao.org/sass/download/sass-1.26.5.tgz", "resolved": "https://registry.nlark.com/sass/download/sass-1.32.13.tgz?cache=0&sync_timestamp=1620837185067&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsass%2Fdownload%2Fsass-1.32.13.tgz",
"integrity": "sha1-LXrs+7q/ophWfI8GYVtuJNLWgJk=", "integrity": "sha1-jSnISeYlpBW85xYJx8+V4V907QA=",
"dev": true, "dev": true,
"requires": { "requires": {
"chokidar": ">=2.0.0 <4.0.0" "chokidar": ">=3.0.0 <4.0.0"
},
"dependencies": {
"anymatch": {
"version": "3.1.2",
"resolved": "https://registry.nlark.com/anymatch/download/anymatch-3.1.2.tgz",
"integrity": "sha1-wFV8CWrzLxBhmPT04qODU343hxY=",
"dev": true,
"requires": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
}
},
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npm.taobao.org/binary-extensions/download/binary-extensions-2.2.0.tgz?cache=0&sync_timestamp=1610299285874&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbinary-extensions%2Fdownload%2Fbinary-extensions-2.2.0.tgz",
"integrity": "sha1-dfUC7q+f/eQvyYgpZFvk6na9ni0=",
"dev": true
},
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npm.taobao.org/braces/download/braces-3.0.2.tgz",
"integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=",
"dev": true,
"requires": {
"fill-range": "^7.0.1"
}
},
"chokidar": {
"version": "3.5.1",
"resolved": "https://registry.npm.taobao.org/chokidar/download/chokidar-3.5.1.tgz?cache=0&sync_timestamp=1610719499558&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchokidar%2Fdownload%2Fchokidar-3.5.1.tgz",
"integrity": "sha1-7pznu+vSt59J8wR5nVRo4x4U5oo=",
"dev": true,
"requires": {
"anymatch": "~3.1.1",
"braces": "~3.0.2",
"fsevents": "~2.3.1",
"glob-parent": "~5.1.0",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.5.0"
}
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npm.taobao.org/fill-range/download/fill-range-7.0.1.tgz",
"integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=",
"dev": true,
"requires": {
"to-regex-range": "^5.0.1"
}
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.3.2.tgz?cache=0&sync_timestamp=1612536512306&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffsevents%2Fdownload%2Ffsevents-2.3.2.tgz",
"integrity": "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=",
"dev": true,
"optional": true
},
"glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.nlark.com/glob-parent/download/glob-parent-5.1.2.tgz?cache=0&sync_timestamp=1620073321855&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fglob-parent%2Fdownload%2Fglob-parent-5.1.2.tgz",
"integrity": "sha1-hpgyxYA0/mikCTwX3BXoNA2EAcQ=",
"dev": true,
"requires": {
"is-glob": "^4.0.1"
}
},
"is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npm.taobao.org/is-binary-path/download/is-binary-path-2.1.0.tgz",
"integrity": "sha1-6h9/O4DwZCNug0cPhsCcJU+0Wwk=",
"dev": true,
"requires": {
"binary-extensions": "^2.0.0"
}
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npm.taobao.org/is-number/download/is-number-7.0.0.tgz",
"integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=",
"dev": true
},
"readdirp": {
"version": "3.5.0",
"resolved": "https://registry.npm.taobao.org/readdirp/download/readdirp-3.5.0.tgz?cache=0&sync_timestamp=1615717425931&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freaddirp%2Fdownload%2Freaddirp-3.5.0.tgz",
"integrity": "sha1-m6dMAZsV02UnjS6Ru4xI17TULJ4=",
"dev": true,
"requires": {
"picomatch": "^2.2.1"
}
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-5.0.1.tgz",
"integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=",
"dev": true,
"requires": {
"is-number": "^7.0.0"
}
}
} }
}, },
"sass-loader": { "sass-loader": {
"version": "8.0.2", "version": "8.0.2",
"resolved": "https://registry.npm.taobao.org/sass-loader/download/sass-loader-8.0.2.tgz", "resolved": "https://registry.nlark.com/sass-loader/download/sass-loader-8.0.2.tgz",
"integrity": "sha1-3r7NjDziQ8dkVPLoKQSCFQOACQ0=", "integrity": "sha1-3r7NjDziQ8dkVPLoKQSCFQOACQ0=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -18901,7 +19002,7 @@
"dependencies": { "dependencies": {
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.0",
"resolved": "https://registry.npm.taobao.org/semver/download/semver-6.3.0.tgz?cache=0&sync_timestamp=1586886301819&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-6.3.0.tgz", "resolved": "https://registry.nlark.com/semver/download/semver-6.3.0.tgz?cache=0&sync_timestamp=1618846828850&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsemver%2Fdownload%2Fsemver-6.3.0.tgz",
"integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=", "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=",
"dev": true "dev": true
} }
@ -19628,8 +19729,8 @@
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
}, },
"squire-rte": { "squire-rte": {
"version": "github:seonim-ryu/Squire#fd40b4e3020845825701e9689f190bab3f4775d4", "version": "git+ssh://git@github.com/seonim-ryu/Squire.git#fd40b4e3020845825701e9689f190bab3f4775d4",
"from": "github:seonim-ryu/Squire#fd40b4e3020845825701e9689f190bab3f4775d4" "from": "squire-rte@github:seonim-ryu/Squire#fd40b4e3020845825701e9689f190bab3f4775d4"
}, },
"ssf": { "ssf": {
"version": "0.10.3", "version": "0.10.3",
@ -20751,7 +20852,7 @@
"integrity": "sha1-LRjs3+JlSL+ELoG2woscfiIvs8w=", "integrity": "sha1-LRjs3+JlSL+ELoG2woscfiIvs8w=",
"requires": { "requires": {
"core-js": "^3.6.4", "core-js": "^3.6.4",
"raphael": "git+https://github.com/nhn/raphael.git#2.2.0-c", "raphael": "raphael@git+https://github.com/nhn/raphael.git#2.2.0-c",
"tui-code-snippet": "^2.3.1" "tui-code-snippet": "^2.3.1"
}, },
"dependencies": { "dependencies": {
@ -20796,7 +20897,7 @@
"markdown-it": "^9.0.0", "markdown-it": "^9.0.0",
"plantuml-encoder": "^1.2.5", "plantuml-encoder": "^1.2.5",
"resize-observer-polyfill": "^1.5.0", "resize-observer-polyfill": "^1.5.0",
"squire-rte": "github:seonim-ryu/Squire#fd40b4e3020845825701e9689f190bab3f4775d4", "squire-rte": "squire-rte@github:seonim-ryu/Squire#fd40b4e3020845825701e9689f190bab3f4775d4",
"to-mark": "^1.1.9", "to-mark": "^1.1.9",
"tui-chart": "^3.7.0", "tui-chart": "^3.7.0",
"tui-code-snippet": "^1.5.0", "tui-code-snippet": "^1.5.0",

Loading…
Cancel
Save