From 56edb3f211ab76dfb5b74f482ee83b6cdd916ba7 Mon Sep 17 00:00:00 2001 From: token <239573049@qq.com> Date: Wed, 3 Apr 2024 00:45:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=91=E5=B8=83`v1.0.0`=20-=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E5=8A=A8=E6=80=81=E5=88=9B=E5=BB=BAWebApplication=20-?= =?UTF-8?q?=20=E5=AE=9E=E7=8E=B0=E8=87=AA=E5=8A=A8=E7=94=B3=E8=AF=B7Http?= =?UTF-8?q?=E8=AF=81=E4=B9=A6=20-=20=E5=AE=9E=E7=8E=B0=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E7=BB=AD=E6=9C=9FHttp=E8=AF=81=E4=B9=A6=20-=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0QPS=E5=AE=9E=E6=97=B6=E9=A2=84=E8=A7=88=20-=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E6=AF=8F=E5=A4=A9=E8=AF=B7=E6=B1=82=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- src/FastGateway.Core/ApiServiceType.cs | 19 ++ src/FastGateway/.config/dotnet-tools.json | 12 + .../RenewSSLBackgroundService.cs | 3 + .../StatisticsBackgroundService.cs | 56 +++-- src/FastGateway/Domain/Location.cs | 2 + src/FastGateway/Dto/LocationInput.cs | 4 + src/FastGateway/Dto/ServiceInput.cs | 2 + .../Dto/StatisticRequestCountDto.cs | 2 +- src/FastGateway/FastContext.cs | 11 + src/FastGateway/FastGateway.csproj | 12 +- src/FastGateway/GlobalUsing.cs | 2 - .../Middlewares/StatisticsMiddleware.cs | 22 +- ....cs => 20240402152541_Initial.Designer.cs} | 5 +- ...9_Initial.cs => 20240402152541_Initial.cs} | 1 + .../MasterDbContextModelSnapshot.cs | 236 ------------------ src/FastGateway/Program.cs | 141 ++++++++--- .../Properties/launchSettings.json | 4 +- src/FastGateway/Services/ApiServiceService.cs | 113 +++++---- src/FastGateway/Services/AuthorizeService.cs | 4 +- src/FastGateway/Services/CertService.cs | 38 ++- src/FastGateway/Services/QpsService.cs | 5 +- .../Services/StatisticRequestService.cs | 52 +++- src/FastGateway/fast-gateway.db | Bin 57344 -> 0 bytes src/FastGateway/fast-gateway.db-shm | Bin 32768 -> 0 bytes src/FastGateway/fast-gateway.db-wal | Bin 20632 -> 0 bytes src/FastGateway/rd.xml | 6 + web/src/module.ts | 2 + web/src/pages/data-statistics/index.tsx | 84 +++---- .../http-proxy/features/UpdateHttpProxy.tsx | 32 ++- web/src/services/QpsService.ts | 2 +- web/src/services/StatisticRequestService.ts | 15 ++ 32 files changed, 440 insertions(+), 451 deletions(-) create mode 100644 src/FastGateway.Core/ApiServiceType.cs create mode 100644 src/FastGateway/.config/dotnet-tools.json create mode 100644 src/FastGateway/FastContext.cs rename src/FastGateway/Migrations/{20240401173039_Initial.Designer.cs => 20240402152541_Initial.Designer.cs} (98%) rename src/FastGateway/Migrations/{20240401173039_Initial.cs => 20240402152541_Initial.cs} (98%) delete mode 100644 src/FastGateway/Migrations/MasterDbContextModelSnapshot.cs delete mode 100644 src/FastGateway/fast-gateway.db delete mode 100644 src/FastGateway/fast-gateway.db-shm delete mode 100644 src/FastGateway/fast-gateway.db-wal create mode 100644 src/FastGateway/rd.xml create mode 100644 web/src/services/StatisticRequestService.ts diff --git a/.gitignore b/.gitignore index 9cb2b9d..73e5a8c 100644 --- a/.gitignore +++ b/.gitignore @@ -103,7 +103,9 @@ StyleCopReport.xml *.pidb *.svclog *.scc - +*.db* +yarn.lock +package-lock.json # Chutzpah Test files _Chutzpah* diff --git a/src/FastGateway.Core/ApiServiceType.cs b/src/FastGateway.Core/ApiServiceType.cs new file mode 100644 index 0000000..992ebb0 --- /dev/null +++ b/src/FastGateway.Core/ApiServiceType.cs @@ -0,0 +1,19 @@ +namespace FastGateway.Core; + +public enum ApiServiceType : byte +{ + /// + /// 静态代理 + /// + StaticProxy = 1, + + /// + /// 单个服务 + /// + SingleService = 2, + + /// + /// 负载均衡 + /// + LoadBalance = 3, +} \ No newline at end of file diff --git a/src/FastGateway/.config/dotnet-tools.json b/src/FastGateway/.config/dotnet-tools.json new file mode 100644 index 0000000..d9d129c --- /dev/null +++ b/src/FastGateway/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "8.0.3", + "commands": [ + "dotnet-ef" + ] + } + } +} \ No newline at end of file diff --git a/src/FastGateway/BackgroundServices/RenewSSLBackgroundService.cs b/src/FastGateway/BackgroundServices/RenewSSLBackgroundService.cs index 6f2874c..fe3a4c4 100644 --- a/src/FastGateway/BackgroundServices/RenewSSLBackgroundService.cs +++ b/src/FastGateway/BackgroundServices/RenewSSLBackgroundService.cs @@ -30,6 +30,9 @@ public sealed class RenewSslBackgroundService(IServiceProvider serviceProvider) masterDbContext.Certs.Update(certItem); await masterDbContext.SaveChangesAsync(stoppingToken); + + // 成功以后需要刷新证书列表 + await CertService.LoadCerts(masterDbContext); } catch (Exception e) { diff --git a/src/FastGateway/BackgroundServices/StatisticsBackgroundService.cs b/src/FastGateway/BackgroundServices/StatisticsBackgroundService.cs index 7b5ed7f..addc8c5 100644 --- a/src/FastGateway/BackgroundServices/StatisticsBackgroundService.cs +++ b/src/FastGateway/BackgroundServices/StatisticsBackgroundService.cs @@ -10,8 +10,8 @@ public sealed class StatisticsBackgroundService(IServiceProvider serviceProvider private static readonly ConcurrentDictionary RequestCountDic = new(); - private static readonly ConcurrentDictionary IpDic = new(); - + private static readonly ConcurrentDictionary IpDic = new(); + private static DateTime _lastTime = DateTime.Now; protected override async Task ExecuteAsync(CancellationToken stoppingToken) @@ -36,7 +36,7 @@ public sealed class StatisticsBackgroundService(IServiceProvider serviceProvider IpDic.TryAdd(ipDto.Ip, ipDto); } } - + private async Task RequestCountAsync() { await using var scope = serviceProvider.CreateAsyncScope(); @@ -45,6 +45,18 @@ public sealed class StatisticsBackgroundService(IServiceProvider serviceProvider { var requestCount = await StatisticRequestChannel.Reader.ReadAsync(); + // 判断是否超过5分钟,如果超过5分钟就将之前的数据写入数据库 + if (DateTime.Now - _lastTime >= TimeSpan.FromMinutes(5)) + { + _lastTime = DateTime.Now; + foreach (var item in RequestCountDic) + { + await CheckAddOrUpdateAsync(masterDbContext, item.Value); + } + + RequestCountDic.Clear(); + } + // 判断是否存在缓存 if (RequestCountDic.TryGetValue(requestCount.ServiceId, out var statisticRequestCount)) { @@ -57,20 +69,36 @@ public sealed class StatisticsBackgroundService(IServiceProvider serviceProvider RequestCountDic.TryAdd(requestCount.ServiceId, requestCount); } - //每隔5分钟写入数据库 - if (DateTime.Now - _lastTime <= TimeSpan.FromMinutes(5)) continue; - - _lastTime = DateTime.Now; - foreach (var item in RequestCountDic) - { - await CheckAddOrUpdateAsync(masterDbContext, item.Value); - } - - RequestCountDic.Clear(); } } - private async ValueTask CheckAddOrUpdateAsync(MasterDbContext masterDbContext, + public static StatisticRequestCountDto GetCurrentRequestCount(string? serviceId) + { + if (string.IsNullOrWhiteSpace(serviceId)) + { + return new StatisticRequestCountDto + { + RequestCount = RequestCountDic.Values.Sum(x => x.RequestCount), + Error4xxCount = RequestCountDic.Values.Sum(x => x.Error4xxCount), + Error5xxCount = RequestCountDic.Values.Sum(x => x.Error5xxCount), + }; + } + + if (RequestCountDic.TryGetValue(serviceId, out var statisticRequestCount)) + { + return statisticRequestCount; + } + + return new StatisticRequestCountDto + { + ServiceId = serviceId, + RequestCount = 0, + Error4xxCount = 0, + Error5xxCount = 0 + }; + } + + private static async ValueTask CheckAddOrUpdateAsync(MasterDbContext masterDbContext, StatisticRequestCountDto requestCountDto) { // 判断数据库是否存在今年今月今日的数据 diff --git a/src/FastGateway/Domain/Location.cs b/src/FastGateway/Domain/Location.cs index deeab5b..befe06f 100644 --- a/src/FastGateway/Domain/Location.cs +++ b/src/FastGateway/Domain/Location.cs @@ -34,6 +34,8 @@ public class Location /// public string[]? TryFiles { get; set; } + public ApiServiceType Type { get; set; } + /// /// 负载均衡模型 /// diff --git a/src/FastGateway/Dto/LocationInput.cs b/src/FastGateway/Dto/LocationInput.cs index 7e4c0af..fc1e59b 100644 --- a/src/FastGateway/Dto/LocationInput.cs +++ b/src/FastGateway/Dto/LocationInput.cs @@ -2,6 +2,8 @@ public class LocationInput { + public string? Id { get; set; } + /// /// 绑定服务ID /// @@ -32,6 +34,8 @@ public class LocationInput /// public string[]? TryFiles { get; set; } + public ApiServiceType Type { get; set; } + /// /// 负载均衡模型 /// diff --git a/src/FastGateway/Dto/ServiceInput.cs b/src/FastGateway/Dto/ServiceInput.cs index 6c3f1a2..65c0ab6 100644 --- a/src/FastGateway/Dto/ServiceInput.cs +++ b/src/FastGateway/Dto/ServiceInput.cs @@ -2,6 +2,8 @@ public class ServiceInput { + public string? Id { get; set; } + /// /// 多个服务名称 /// diff --git a/src/FastGateway/Dto/StatisticRequestCountDto.cs b/src/FastGateway/Dto/StatisticRequestCountDto.cs index 20265ca..4174ec2 100644 --- a/src/FastGateway/Dto/StatisticRequestCountDto.cs +++ b/src/FastGateway/Dto/StatisticRequestCountDto.cs @@ -20,7 +20,7 @@ public sealed class StatisticRequestCountDto /// /// 关联服务ID /// - public string ServiceId { get; set; } + public string? ServiceId { get; set; } public DateTime CreatedTime { get; set; } } \ No newline at end of file diff --git a/src/FastGateway/FastContext.cs b/src/FastGateway/FastContext.cs new file mode 100644 index 0000000..790c5aa --- /dev/null +++ b/src/FastGateway/FastContext.cs @@ -0,0 +1,11 @@ +namespace FastGateway; + +public class FastContext +{ + public static IQpsService QpsService { get; protected set; } = null!; + + public static void SetQpsService(IQpsService qpsService) + { + QpsService = qpsService; + } +} \ No newline at end of file diff --git a/src/FastGateway/FastGateway.csproj b/src/FastGateway/FastGateway.csproj index 17b0d2d..c60f2f2 100644 --- a/src/FastGateway/FastGateway.csproj +++ b/src/FastGateway/FastGateway.csproj @@ -4,14 +4,13 @@ net8.0 enable enable + true + true - - - all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -20,7 +19,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - @@ -30,4 +28,10 @@ + + + Always + + + diff --git a/src/FastGateway/GlobalUsing.cs b/src/FastGateway/GlobalUsing.cs index b9be310..1eb06b7 100644 --- a/src/FastGateway/GlobalUsing.cs +++ b/src/FastGateway/GlobalUsing.cs @@ -1,10 +1,8 @@ global using FastGateway.EFCore; -global using FastMiniApis.Core; global using Mapster; global using Microsoft.EntityFrameworkCore; global using FastGateway.Domain; global using FastGateway.Dto; -global using FastMiniApis; global using System.Text.Json; global using FastGateway.Core; global using FastGateway.Services; diff --git a/src/FastGateway/Middlewares/StatisticsMiddleware.cs b/src/FastGateway/Middlewares/StatisticsMiddleware.cs index 3a079c4..3f53146 100644 --- a/src/FastGateway/Middlewares/StatisticsMiddleware.cs +++ b/src/FastGateway/Middlewares/StatisticsMiddleware.cs @@ -4,8 +4,6 @@ namespace FastGateway.Middlewares; public sealed class StatisticsMiddleware(ICurrentContext currentContext) : IMiddleware { - private readonly IQpsService _qpsService = FastApp.GetRequiredService(); - public async Task InvokeAsync(HttpContext context, RequestDelegate next) { await next(context); @@ -16,8 +14,8 @@ public sealed class StatisticsMiddleware(ICurrentContext currentContext) : IMidd StatisticsBackgroundService.WriteAsync(new StatisticRequestCountDto() { CreatedTime = DateTime.Now, + ServiceId = currentContext.ServiceId, Error4xxCount = 1, - Error5xxCount = 0, RequestCount = 1, }); break; @@ -25,7 +23,7 @@ public sealed class StatisticsMiddleware(ICurrentContext currentContext) : IMidd StatisticsBackgroundService.WriteAsync(new StatisticRequestCountDto() { CreatedTime = DateTime.Now, - Error4xxCount = 0, + ServiceId = currentContext.ServiceId, Error5xxCount = 1, RequestCount = 1, }); @@ -34,24 +32,12 @@ public sealed class StatisticsMiddleware(ICurrentContext currentContext) : IMidd StatisticsBackgroundService.WriteAsync(new StatisticRequestCountDto() { CreatedTime = DateTime.Now, - Error4xxCount = 0, - Error5xxCount = 0, + ServiceId = currentContext.ServiceId, RequestCount = 1, }); break; } - _qpsService.IncrementServiceRequests(currentContext.ServiceId); - - // - // - // // 获取ip - // var ip = context.Connection.RemoteIpAddress?.ToString(); - // - // // 获取请求头中的IP - // if (context.Request.Headers.TryGetValue("X-Forwarded-For", out var forwardedFor)) - // { - // ip = forwardedFor; - // } + FastContext.QpsService.IncrementServiceRequests(currentContext.ServiceId); } } \ No newline at end of file diff --git a/src/FastGateway/Migrations/20240401173039_Initial.Designer.cs b/src/FastGateway/Migrations/20240402152541_Initial.Designer.cs similarity index 98% rename from src/FastGateway/Migrations/20240401173039_Initial.Designer.cs rename to src/FastGateway/Migrations/20240402152541_Initial.Designer.cs index dfc3273..6fa7dd5 100644 --- a/src/FastGateway/Migrations/20240401173039_Initial.Designer.cs +++ b/src/FastGateway/Migrations/20240402152541_Initial.Designer.cs @@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace FastGateway.Migrations { [DbContext(typeof(MasterDbContext))] - [Migration("20240401173039_Initial")] + [Migration("20240402152541_Initial")] partial class Initial { /// @@ -90,6 +90,9 @@ namespace FastGateway.Migrations b.Property("TryFiles") .HasColumnType("TEXT"); + b.Property("Type") + .HasColumnType("INTEGER"); + b.Property("UpStreams") .IsRequired() .HasColumnType("TEXT"); diff --git a/src/FastGateway/Migrations/20240401173039_Initial.cs b/src/FastGateway/Migrations/20240402152541_Initial.cs similarity index 98% rename from src/FastGateway/Migrations/20240401173039_Initial.cs rename to src/FastGateway/Migrations/20240402152541_Initial.cs index 5fa5497..0886dbd 100644 --- a/src/FastGateway/Migrations/20240401173039_Initial.cs +++ b/src/FastGateway/Migrations/20240402152541_Initial.cs @@ -82,6 +82,7 @@ namespace FastGateway.Migrations AddHeader = table.Column(type: "TEXT", nullable: false), Root = table.Column(type: "TEXT", nullable: true), TryFiles = table.Column(type: "TEXT", nullable: true), + Type = table.Column(type: "INTEGER", nullable: false), LoadType = table.Column(type: "INTEGER", nullable: false), UpStreams = table.Column(type: "TEXT", nullable: false) }, diff --git a/src/FastGateway/Migrations/MasterDbContextModelSnapshot.cs b/src/FastGateway/Migrations/MasterDbContextModelSnapshot.cs deleted file mode 100644 index 4fbef7d..0000000 --- a/src/FastGateway/Migrations/MasterDbContextModelSnapshot.cs +++ /dev/null @@ -1,236 +0,0 @@ -// -using System; -using FastGateway.EFCore; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace FastGateway.Migrations -{ - [DbContext(typeof(MasterDbContext))] - partial class MasterDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.3"); - - modelBuilder.Entity("FastGateway.Domain.Cert", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("AutoRenew") - .HasColumnType("INTEGER"); - - b.Property("Certs") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Domains") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Email") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Expired") - .HasColumnType("INTEGER"); - - b.Property("Issuer") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("NotAfter") - .HasColumnType("TEXT"); - - b.Property("RenewStats") - .HasColumnType("INTEGER"); - - b.Property("RenewTime") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("cert", (string)null); - }); - - modelBuilder.Entity("FastGateway.Domain.Location", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("AddHeader") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("LoadType") - .HasColumnType("INTEGER"); - - b.Property("Path") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("ProxyPass") - .HasColumnType("TEXT"); - - b.Property("Root") - .HasColumnType("TEXT"); - - b.Property("ServiceId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("TryFiles") - .HasColumnType("TEXT"); - - b.Property("UpStreams") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ServiceId"); - - b.ToTable("location", (string)null); - }); - - modelBuilder.Entity("FastGateway.Domain.Service", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("Enable") - .HasColumnType("INTEGER"); - - b.Property("EnableFlowMonitoring") - .HasColumnType("INTEGER"); - - b.Property("EnableHttp3") - .HasColumnType("INTEGER"); - - b.Property("EnableRequestSource") - .HasColumnType("INTEGER"); - - b.Property("EnableTunnel") - .HasColumnType("INTEGER"); - - b.Property("IsHttps") - .HasColumnType("INTEGER"); - - b.Property("Listen") - .HasColumnType("INTEGER"); - - b.Property("ServiceNames") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("SslCertificate") - .HasColumnType("TEXT"); - - b.Property("SslCertificatePassword") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("service", (string)null); - }); - - modelBuilder.Entity("FastGateway.Domain.StatisticIp", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Count") - .HasColumnType("INTEGER"); - - b.Property("Day") - .HasColumnType("INTEGER"); - - b.Property("Ip") - .HasColumnType("TEXT"); - - b.Property("Location") - .HasColumnType("TEXT"); - - b.Property("Month") - .HasColumnType("INTEGER"); - - b.Property("ServiceId") - .HasColumnType("TEXT"); - - b.Property("Year") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("statistic_ip", (string)null); - }); - - modelBuilder.Entity("FastGateway.Domain.StatisticRequestCount", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Day") - .HasColumnType("INTEGER"); - - b.Property("Error4xxCount") - .HasColumnType("INTEGER"); - - b.Property("Error5xxCount") - .HasColumnType("INTEGER"); - - b.Property("Month") - .HasColumnType("INTEGER"); - - b.Property("RequestCount") - .HasColumnType("INTEGER"); - - b.Property("ServiceId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Year") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("ServiceId"); - - b.ToTable("statistic_request_count", (string)null); - }); - - modelBuilder.Entity("FastGateway.Domain.Location", b => - { - b.HasOne("FastGateway.Domain.Service", null) - .WithMany("Locations") - .HasForeignKey("ServiceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("FastGateway.Domain.StatisticRequestCount", b => - { - b.HasOne("FastGateway.Domain.Service", "Service") - .WithMany() - .HasForeignKey("ServiceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Service"); - }); - - modelBuilder.Entity("FastGateway.Domain.Service", b => - { - b.Navigation("Locations"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/FastGateway/Program.cs b/src/FastGateway/Program.cs index 3eb812e..f94485c 100644 --- a/src/FastGateway/Program.cs +++ b/src/FastGateway/Program.cs @@ -1,7 +1,6 @@ using FastGateway; using FastGateway.BackgroundServices; using FastGateway.Options; -using Microsoft.OpenApi.Models; var builder = WebApplication.CreateBuilder(args); @@ -22,37 +21,9 @@ builder.Services.ConfigureHttpJsonOptions(options => options.SerializerOptions.Converters.Add(new JsonDateTimeConverter()); }); -builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddHostedService(); -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(options => -{ - options.SwaggerDoc("v1", new OpenApiInfo { Title = "FastGateway", Version = "v1" }); - - options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme - { - Description = "JWT Authorization header using the Bearer scheme", - Name = "Authorization", - In = ParameterLocation.Header, - Type = SecuritySchemeType.ApiKey, - Scheme = "Bearer", - }); - - options.AddSecurityRequirement(new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "Bearer", - }, - }, - Array.Empty() - } - }); -}); +builder.Services.AddHostedService(); builder.Services.AddMapster(); @@ -61,33 +32,127 @@ builder.Services.AddDbContext(options => options.UseSqlite(builder.Configuration.GetConnectionString("Default")); }); -builder.Services.AddService(); - builder.Services .AddAuthorization() .AddJwtBearerAuthentication(); var app = builder.Build(); -app.MapFastMiniApis(); app.UseCors("AllowAll"); + app.UseAuthentication(); app.UseAuthorization(); app.MapGet("/.well-known/acme-challenge/{token}", AcmeChallenge.Challenge); app.MapPost("/.well-known/acme-challenge/{token}", AcmeChallenge.Challenge); +#region Authorize + +var authorizeService = app.MapGroup("/api/v1/Authorize") + .RequireAuthorization(); + +authorizeService.MapPost(string.Empty, + AuthorizeService.CreateAsync); + +#endregion + +#region ApiService + +var apiService = app.MapGroup("/api/v1/ApiService") + .RequireAuthorization(); + +apiService.MapGet("/List", + ApiServiceService.GetListAsync); + +apiService.MapGet(string.Empty, + ApiServiceService.GetAsync); + +apiService.MapPost(string.Empty, + ApiServiceService.CreateAsync); + +apiService.MapPut(string.Empty, + ApiServiceService.UpdateAsync); + +apiService.MapDelete(string.Empty, + ApiServiceService.DeleteAsync); + +apiService.MapGet("/SelectList", + ApiServiceService.GetSelectListAsync); + +apiService.MapPost("/StartService", + ApiServiceService.StartServiceAsync); + +apiService.MapPost("/StopService", + ApiServiceService.StopServiceAsync); + +apiService.MapPost("/RestartService", + ApiServiceService.RestartServiceAsync); + +apiService.MapPost("/ServiceStats", + ApiServiceService.ServiceStats); + +apiService.MapPost("/CheckDirectoryExistence", + ApiServiceService.CheckDirectoryExistenceAsync); + +#endregion + +#region Cert + +var certService = app.MapGroup("/api/v1/Cert") + .RequireAuthorization(); + +certService.MapGet("/List", + CertService.GetListAsync); + +certService.MapGet(string.Empty, + CertService.GetAsync); + +certService.MapPost(string.Empty, + CertService.CreateAsync); + +certService.MapPut(string.Empty, + CertService.UpdateAsync); + +certService.MapDelete(string.Empty, + CertService.DeleteAsync); + +#endregion + +#region Qps + +var apiQpsService = app.MapGroup("/api/v1/Qps") + .RequireAuthorization(); + +apiQpsService.MapGet(string.Empty, + ApiQpsService.GetAsync); + +#endregion + +#region Statistic + +var statisticService = app.MapGroup("/api/v1/Statistic") + .RequireAuthorization(); + +statisticService.MapGet("/DayRequestCount", + StatisticRequestService.GetDayStatisticAsync); + +statisticService.MapGet("/TotalRequestCount", + StatisticRequestService.GetTotalStatisticAsync); + +#endregion + +FastContext.SetQpsService(app.Services.GetRequiredService()); + if (app.Environment.IsDevelopment()) { - app.UseSwagger(); - app.UseSwaggerUI(); - using var scope = app.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); await db.Database.MigrateAsync(); await ApiServiceService.LoadServices(db); + + await CertService.LoadCerts(db); } -app.Run(); \ No newline at end of file +await app.RunAsync(); \ No newline at end of file diff --git a/src/FastGateway/Properties/launchSettings.json b/src/FastGateway/Properties/launchSettings.json index 5dd1a9f..d03b373 100644 --- a/src/FastGateway/Properties/launchSettings.json +++ b/src/FastGateway/Properties/launchSettings.json @@ -12,7 +12,7 @@ "http": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": true, + "launchBrowser": false, "launchUrl": "swagger", "applicationUrl": "http://localhost:5283", "environmentVariables": { @@ -22,7 +22,7 @@ "https": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": true, + "launchBrowser": false, "launchUrl": "swagger", "applicationUrl": "https://localhost:7115;http://localhost:5283", "environmentVariables": { diff --git a/src/FastGateway/Services/ApiServiceService.cs b/src/FastGateway/Services/ApiServiceService.cs index 83bbea8..6d7bca4 100644 --- a/src/FastGateway/Services/ApiServiceService.cs +++ b/src/FastGateway/Services/ApiServiceService.cs @@ -1,14 +1,16 @@ using System.Net; +using System.Security.Cryptography.X509Certificates; using FastGateway.Infrastructures; using FastGateway.Middlewares; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Server.Kestrel.Core; using Yarp.ReverseProxy.Configuration; +using Yarp.ReverseProxy.Transforms; namespace FastGateway.Services; -public class ApiServiceService() : ServiceBase("/api/v1/ApiService") +public class ApiServiceService { private static readonly Dictionary WebApplications = new(); @@ -33,7 +35,7 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") } [Authorize] - public async Task CreateAsync(ServiceInput input, MasterDbContext masterDbContext) + public static async Task CreateAsync(ServiceInput input, MasterDbContext masterDbContext) { var service = input.Adapt(); @@ -54,7 +56,7 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") } [Authorize] - public async Task UpdateAsync(string id, ServiceInput input, MasterDbContext masterDbContext) + public static async Task UpdateAsync(string id, ServiceInput input, MasterDbContext masterDbContext) { var service = await masterDbContext.Services.AsNoTracking() .FirstOrDefaultAsync(x => x.Id == id); @@ -72,7 +74,7 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") } [Authorize] - public async Task DeleteAsync(string id, MasterDbContext masterDbContext) + public static async Task DeleteAsync(string id, MasterDbContext masterDbContext) { try { @@ -105,7 +107,7 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") /// /// /// - public ResultDto CheckDirectoryExistenceAsync(string path) + public static ResultDto CheckDirectoryExistenceAsync(string path) { if (string.IsNullOrWhiteSpace(path)) { @@ -130,7 +132,7 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") } [Authorize] - public async Task GetAsync(string id, MasterDbContext masterDbContext) + public static async Task GetAsync(string id, MasterDbContext masterDbContext) { return await masterDbContext.Services.AsNoTracking() .Include(x => x.Locations) @@ -138,7 +140,7 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") } [Authorize] - public async Task>> GetListAsync(int page, int pageSize, + public static async Task>> GetListAsync(int page, int pageSize, MasterDbContext masterDbContext) { if (page < 1) @@ -166,7 +168,7 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") } [Authorize] - public async Task>> GetSelectListAsync(MasterDbContext masterDbContext) + public static async Task>> GetSelectListAsync(MasterDbContext masterDbContext) { var result = await masterDbContext.Services .AsNoTracking() @@ -180,7 +182,7 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") } [Authorize] - public ResultDto> ServiceStats([FromBody] List ids) + public static ResultDto> ServiceStats([FromBody] List ids) { var result = new Dictionary(); @@ -196,7 +198,7 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") } [Authorize] - public async Task StartServiceAsync(string id, MasterDbContext masterDbContext) + public static async Task StartServiceAsync(string id, MasterDbContext masterDbContext) { if (WebApplications.TryGetValue(id, out var app)) { @@ -213,7 +215,7 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") } [Authorize] - public async Task StopServiceAsync(string id) + public static async Task StopServiceAsync(string id) { if (WebApplications.TryGetValue(id, out var app)) { @@ -223,7 +225,7 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") } [Authorize] - public async Task RestartServiceAsync(string id, MasterDbContext masterDbContext) + public static async Task RestartServiceAsync(string id, MasterDbContext masterDbContext) { if (WebApplications.TryGetValue(id, out var app)) { @@ -242,20 +244,29 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") var service = (Service)state; var builder = WebApplication.CreateBuilder([]); - - var qpsService = FastApp.GetRequiredService(); - qpsService?.AddServiceQps(service.Id); + + FastContext.QpsService?.AddServiceQps(service.Id); IContentTypeProvider defaultContentTypeProvider = new DefaultContentTypeProvider(); builder.WebHost.UseKestrel(options => { - options.Listen(IPAddress.Parse("0.0.0.0"), service.Listen, listenOptions => + options.ConfigureHttpsDefaults(adapterOptions => { if (service.IsHttps) { - // listenOptions.UseHttps(service.SslCertificate, service.SslCertificatePassword); + adapterOptions.ServerCertificateSelector = (context, name) => + CertService.Certs.TryGetValue(name, out var cert) + ? new X509Certificate2(cert.File, cert.Password) + : new X509Certificate2(Path.Combine(AppContext.BaseDirectory, "gateway.pfx"), "010426"); } + }); + + options.Listen(IPAddress.Parse("0.0.0.0"), service.Listen, listenOptions => + { + if (!service.IsHttps) return; + + listenOptions.UseHttps(); if (service.EnableHttp3) { @@ -266,7 +277,6 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") var routes = new List(); var clusters = new List(); - var staticFiles = new List(); // 绑定路由到service foreach (var location in service.Locations) @@ -278,7 +288,7 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") ClusterId = clusterId, Match = new RouteMatch() { - Path = location.Path + "{**catch-all}", + Path = location.Path.TrimEnd('/') + "/{**catch-all}", Hosts = service.ServiceNames } }; @@ -291,8 +301,18 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") ClusterId = clusterId }; - // 如果配置了 UpStreams ,多个则负载均衡 - if (location.UpStreams.Count > 0) + + if (location.Type == ApiServiceType.SingleService) + { + destinations.Add(location.ProxyPass, new DestinationConfig() + { + Address = location.ProxyPass, + Host = new Uri(location.ProxyPass).Host, + }); + clusters.Add(cluster); + continue; + } + else if (location.Type == ApiServiceType.LoadBalance) { foreach (var upStream in location.UpStreams.Where(x => !string.IsNullOrEmpty(x.Server))) { @@ -303,28 +323,13 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") } clusters.Add(cluster); - continue; } - - if (!string.IsNullOrWhiteSpace(location.ProxyPass)) - { - destinations.Add(location.ProxyPass, new DestinationConfig() - { - Address = location.ProxyPass, - Host = new Uri(location.ProxyPass).Host, - }); - clusters.Add(cluster); - continue; - } - if (!string.IsNullOrWhiteSpace(location.Root)) + else if (location.Type == ApiServiceType.StaticProxy) { routes.Remove(route); - staticFiles.Add(location); - // 静态文件 - continue; } } - + builder.Services.AddSingleton(new CurrentContext() { @@ -332,7 +337,16 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") }).AddSingleton(); builder.Services.AddReverseProxy() - .LoadFromMemory(routes, clusters); + .LoadFromMemory(routes, clusters) + // 删除所有代理的前缀 + .AddTransforms(context => + { + var prefix = context.Route.Match.Path?.Replace("/{**catch-all}", ""); + if (!string.IsNullOrEmpty(prefix)) + { + context.AddPathRemovePrefix(prefix); + } + }); var app = builder.Build(); @@ -340,7 +354,7 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") app.UseMiddleware(); - foreach (var location in staticFiles) + foreach (var location in service.Locations.Where(x => x.Type == ApiServiceType.StaticProxy)) { app.Map(location.Path.TrimEnd('/'), app => { @@ -358,20 +372,25 @@ public class ApiServiceService() : ServiceBase("/api/v1/ApiService") return; } + if (location.TryFiles == null || location.TryFiles.Length == 0) + { + context.Response.StatusCode = 404; + return; + } + // 搜索 try_files foreach (var tryFile in location.TryFiles) { var tryPath = Path.Combine(location.Root, tryFile); - if (File.Exists(tryPath)) - { - defaultContentTypeProvider.TryGetContentType(tryPath, out var contentType); - context.Response.Headers.ContentType = contentType; + if (!File.Exists(tryPath)) continue; - await context.Response.SendFileAsync(tryPath); + defaultContentTypeProvider.TryGetContentType(tryPath, out var contentType); + context.Response.Headers.ContentType = contentType; - return; - } + await context.Response.SendFileAsync(tryPath); + + return; } })); }); diff --git a/src/FastGateway/Services/AuthorizeService.cs b/src/FastGateway/Services/AuthorizeService.cs index 6e6bf36..f464d80 100644 --- a/src/FastGateway/Services/AuthorizeService.cs +++ b/src/FastGateway/Services/AuthorizeService.cs @@ -6,9 +6,9 @@ using Microsoft.IdentityModel.Tokens; namespace FastGateway.Services; -public class AuthorizeService() : ServiceBase("/api/v1/Authorize") +public class AuthorizeService { - public ResultDto CreateAsync(AuthorizeInput input) + public static ResultDto CreateAsync(AuthorizeInput input) { // 获取环境变量 var password = Environment.GetEnvironmentVariable("PASSWORD", EnvironmentVariableTarget.Machine) ?? "Aa123456"; diff --git a/src/FastGateway/Services/CertService.cs b/src/FastGateway/Services/CertService.cs index e66ef60..9e90444 100644 --- a/src/FastGateway/Services/CertService.cs +++ b/src/FastGateway/Services/CertService.cs @@ -8,10 +8,22 @@ using Directory = System.IO.Directory; namespace FastGateway.Services; -public class CertService() : ServiceBase("/api/v1/Cert") +public static class CertService { + public static Dictionary Certs { get; private set; } = []; + + public static async Task LoadCerts(MasterDbContext masterDbContext) + { + var cert = (await masterDbContext.Certs + .Where(x => x.AutoRenew && x.Expired == false) + .Select(x => x.Certs) + .ToListAsync()).SelectMany(x => x); + + Certs = cert.ToDictionary(x => x.Domain, x => x); + } + [Authorize] - public async Task>> GetListAsync(int page, int pageSize, + public static async Task>> GetListAsync(int page, int pageSize, [FromServices] MasterDbContext masterDbContext) { var query = masterDbContext.Certs.AsQueryable(); @@ -24,7 +36,7 @@ public class CertService() : ServiceBase("/api/v1/Cert") } [Authorize] - public async Task GetAsync(MasterDbContext masterDbContext, string id) + public static async Task GetAsync(MasterDbContext masterDbContext, string id) { var cert = await masterDbContext.Certs.FirstOrDefaultAsync(x => x.Id == id); @@ -32,7 +44,7 @@ public class CertService() : ServiceBase("/api/v1/Cert") } [Authorize] - public async Task CreateAsync(MasterDbContext masterDbContext, CertInput input) + public static async Task CreateAsync(MasterDbContext masterDbContext, CertInput input) { // 判断是否存在相同的域名 var certs = await masterDbContext.Certs.ToListAsync(); @@ -43,7 +55,6 @@ public class CertService() : ServiceBase("/api/v1/Cert") return ResultDto.ErrorResult("存在相同的域名"); } - var cert = new Cert { Id = Guid.NewGuid().ToString(), @@ -62,7 +73,7 @@ public class CertService() : ServiceBase("/api/v1/Cert") } [Authorize] - public async Task UpdateAsync(MasterDbContext masterDbContext, Cert cert) + public static async Task UpdateAsync(MasterDbContext masterDbContext, Cert cert) { masterDbContext.Certs.Update(cert); @@ -72,7 +83,7 @@ public class CertService() : ServiceBase("/api/v1/Cert") } [Authorize] - public async Task DeleteAsync(MasterDbContext masterDbContext, string id) + public static async Task DeleteAsync(MasterDbContext masterDbContext, string id) { var cert = await masterDbContext.Certs.FirstOrDefaultAsync(x => x.Id == id); @@ -85,7 +96,7 @@ public class CertService() : ServiceBase("/api/v1/Cert") /// 申请证书 /// [Authorize] - public async Task ApplyAsync(MasterDbContext masterDbContext, string id) + public static async Task ApplyAsync(MasterDbContext masterDbContext, string id) { var cert = await masterDbContext.Certs.FirstOrDefaultAsync(x => x.Id == id); @@ -96,7 +107,10 @@ public class CertService() : ServiceBase("/api/v1/Cert") var context = await RegisterWithLetsEncrypt(cert.Email); - await ApplyForCert(context, cert); + if (await ApplyForCert(context, cert)) + { + await LoadCerts(masterDbContext); + } masterDbContext.Certs.Update(cert); @@ -104,8 +118,7 @@ public class CertService() : ServiceBase("/api/v1/Cert") } - [IgnoreRoute] - public static async ValueTask ApplyForCert(AcmeContext context, Cert certItem) + public static async ValueTask ApplyForCert(AcmeContext context, Cert certItem) { certItem.ClearCerts(); @@ -176,9 +189,10 @@ public class CertService() : ServiceBase("/api/v1/Cert") Password = certPassword }); } + + return true; } - [IgnoreRoute] public static async ValueTask RegisterWithLetsEncrypt(string email) { var path = Path.Combine(AppContext.BaseDirectory, "certs"); diff --git a/src/FastGateway/Services/QpsService.cs b/src/FastGateway/Services/QpsService.cs index 04fa625..305b366 100644 --- a/src/FastGateway/Services/QpsService.cs +++ b/src/FastGateway/Services/QpsService.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using Microsoft.AspNetCore.Mvc; namespace FastGateway.Services; @@ -108,9 +109,9 @@ public sealed class QpsService : IQpsService, IDisposable } } -public sealed class ApiQpsService(IQpsService qpsService) : ServiceBase("/api/v1/Qps") +public sealed class ApiQpsService { - public async Task GetAsync(HttpContext context, string? serviceId = null) + public static async Task GetAsync([FromServices]IQpsService qpsService,HttpContext context, string? serviceId = null) { context.Response.Headers.ContentType = "text/event-stream"; qpsService.EnableQps(true); diff --git a/src/FastGateway/Services/StatisticRequestService.cs b/src/FastGateway/Services/StatisticRequestService.cs index fc06f18..e9b6106 100644 --- a/src/FastGateway/Services/StatisticRequestService.cs +++ b/src/FastGateway/Services/StatisticRequestService.cs @@ -1,11 +1,13 @@ -namespace FastGateway.Services; +using FastGateway.BackgroundServices; -public sealed class StatisticRequestService : ServiceBase +namespace FastGateway.Services; + +public static class StatisticRequestService { /// /// 获取今天的统计数据 /// - public async Task GetTodayStatisticAsync(MasterDbContext masterDbContext, + public static async Task> GetDayStatisticAsync(MasterDbContext masterDbContext, string? serviceId) { var now = DateTime.Now; @@ -17,18 +19,46 @@ public sealed class StatisticRequestService : ServiceBase x.Day == today.Day) .FirstOrDefaultAsync(); + var currentStatistic = StatisticsBackgroundService.GetCurrentRequestCount(serviceId); + if (statistic == null) { - return new StatisticRequestCountDto(); + return ResultDto.SuccessResult(new StatisticRequestCountDto() + { + Error4xxCount = currentStatistic.Error4xxCount, + Error5xxCount = currentStatistic.Error5xxCount, + RequestCount = currentStatistic.RequestCount + }); } - return new StatisticRequestCountDto + return ResultDto.SuccessResult(new StatisticRequestCountDto { - RequestCount = statistic.RequestCount, - Error4xxCount = statistic.Error4xxCount, - Error5xxCount = statistic.Error5xxCount, - ServiceId = statistic.ServiceId, - CreatedTime = new DateTime(statistic.Year, statistic.Month, statistic.Day, 0, 0, 0) - }; + RequestCount = statistic.RequestCount + currentStatistic.RequestCount, + Error4xxCount = statistic.Error4xxCount + currentStatistic.Error4xxCount, + Error5xxCount = statistic.Error5xxCount + currentStatistic.Error5xxCount, + }); + } + + public static async Task> GetTotalStatisticAsync( + MasterDbContext masterDbContext, + string? serviceId) + { + var query = masterDbContext.StatisticRequestCounts.Where(x => + string.IsNullOrEmpty(serviceId) || x.ServiceId == serviceId); + + var currentStatistic = StatisticsBackgroundService.GetCurrentRequestCount(serviceId); + + var requestCount = await query.SumAsync(x => x.RequestCount); + + var error4XxCount = await query.SumAsync(x => x.Error4xxCount); + + var error5XxCount = await query.SumAsync(x => x.Error5xxCount); + + return ResultDto.SuccessResult(new StatisticRequestCountDto + { + RequestCount = requestCount + currentStatistic.RequestCount, + Error4xxCount = error4XxCount + currentStatistic.Error4xxCount, + Error5xxCount = error5XxCount + currentStatistic.Error5xxCount, + }); } } \ No newline at end of file diff --git a/src/FastGateway/fast-gateway.db b/src/FastGateway/fast-gateway.db deleted file mode 100644 index ccae145f2f889758b656000d870837f814442215..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57344 zcmeI*&2rjS6bEn#J764Rl10jNJZ?4Wq>d-S27`mCH>Frkm|*Pk(b{b@8W}{Q7E44D zjXjwzoTiiR^Aufly6gkw4Z6y*t1kKk-K9qe0m4$uOvd@}{52-Vx>xs#-#Pb6e27%O zEGnkVH8uS}GPx9Ui3tRl&pD1^m;l{o>EU#-I;tsFWc^2_E*Ba;6=w5gAy*Z+LTN+T<@j-0;<@b-$NRL$bJzHO zpZwzYGoe^!o;{8@``Oa!w4<6j*fD2KN>j8jw=hBLRl!~ zg$l*e-q6YxirS);y&FPNprp^|D*4=o5Sd>5`I~SkHb2ik4Rv$J$uQ^lv^zVT)zsur zI=CV=nhS$XhJx<|YDco{T-!TEr)9#9t{oojNQU8TPkHEUn`<;~%Thz8uz9v!)-=i< zyJ%>$s(!So(D?BvDr!=rdeoMEW69Ur6;qd`gZ|d;%pHlfqjI*vU7ilc^7Aayor8=A zEjob`rH-j}{}%iEkaX@{+?fi-u3u;O?0LlAz_5m#T99{Pb{zFDkt;5xA(`={wD-{f@0(CJOd1puLRyJGOLz;>eQ_~go-f6Xahmnfb(d(}m z({|IRHCFAYs@(FgRWVw5SvQrYLT3Qf+lhR0SFq-^hnh}@*?vU|bUYZ0<>vf`axYL~ zvUex+_;4Z=yK;s7rP9rdI`wbeCfylft#S`9yGx(Lx?SBP7dEs5Nm2dMKsanGy6kb< z;6yM-Q&L%0<%j-;1;gn0PUiykPX4ZaciQf@TU8Fc+`DB2WWFN1N}8E#nuD%~4i|aS z8-2_asfEd4Z2gK~q%1=*Y5jZQc$cMf#0>j*yBi}>6gIb%dwTaQ(YQ@xN7IiyR|ecs z^sF-oYrjqF%*|yqdZ_kk$Rpr%@NIPJ=2xR1Y>tIeg>ZXh)K`Y6ZBNW zdOpuY=*C9>X6OeC1Rwwb2tWV=5P$##AOHafKmY>oslZ3_q+)u5hZf317rfuEV40EAlU;qDtiN1J;A0c)P0uX=z z1Rwwb2tWV=5P$##AOHbRfrW`$c$}94aQ(k0A67yD0uX=z1Rwwb2tWV=5P$##&XoY3 z|DUUH;=w=w0uX=z1Rwwb2tWV=5P$##JO%9Q|DQ3@XP(7a2>}Q|00Izz00bZa0SG_< z0uX?}*$Di=PS>Kf)yoVMzMRcuGmVUtjMM+#mx`xX>Z|d!Y5 zod12(Y#W~~FW1$jeMP;uRM!rcAN}<0U0nZvHbF$I5P$##AOHafKmY;|fB*y_0D(6l zU|s(|&Wi{5{QsMX2Ks;i1Rwwb2tWV=5P$##AOHafyj=mj|NnOV(K`en009U<00Izz y00bZa0SG|gbp*!!{(tNFKXd7Idcw94fB*y_009U<00Izz00bZa0SLV70{;UA$o6gk diff --git a/src/FastGateway/fast-gateway.db-shm b/src/FastGateway/fast-gateway.db-shm deleted file mode 100644 index c3ba705c801126c49415035f0ca63ba055c867d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 zcmeI*y$Qll6b8`v4~k-$MsNXpEi9eD1w=bXv9fRlTfqga?A<|}z|KlUml-Doe`j|u?-1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAVA<>1>&AZ zA&{s*J<+|bO@IIa0t5)8D9}i8X*(163N$$oAV7dXastKQXM_L&0t5&UAV7cs0RjXF S5FkK+009C72oNBUhQJ5s(8uV8F1oph_g3D+F@y3te9sWV4av#*WXj12e>D7<&`(o@ z93?JpZ|GAh`*QKe{ATy-@fY{)((vEwlj+$vuldu@hwY;0uM>aQZV(WF00bZa0SG_< z0uX=z1Rwwb2>fq>)Em!_c_TKOBZS1U$x2xyG%1y!Omaz8tYW*DN?9sqq$#SRTLo7x zG{*DO{qs^8al;ZpIV(fTib7H;%95&(*;7@V#Hm>7Xt~txbgP+FTht*}Sg&7@Tspb7 z_U_rYQM=~(+r;1Y_uO#=1Rwwb2tWV=5P$##AOHafKmY;*7dSVRH|lf@yqTfAS*Jx{ zD{?J>eqUgE`to=4{Nuep^8&NP-}Gk(UNvq20uX=z1Rwwb2tWV=5P$##ATVfwwl|vl zc`!jpb3%n8Gnrwu#ki(SEBh^9M~1dyZg?5SQt&jW^ZMSds+I2fAW(AAtj_7q-3JeE z+`Q9pnUeiXf(v^|*`iSx*;#=|Co~gGQC-+0(wHpElAzA}e=n0>o>Pm<3ocjSl3CU< zN86hhc=h4xwOiMJo#@XC?Af6K1Oy-e0SG_<0uX=z1Rwwb2tWV=2SLEi3)JaK?9U6- zX)4&F+-Sg&USHtJ&eqGfKi{9}_XY4D!9jTLxCaP800Izz00bZa0SG_<0uX?J6Zq%< G5&Q;0gU+1* diff --git a/src/FastGateway/rd.xml b/src/FastGateway/rd.xml new file mode 100644 index 0000000..ad62df1 --- /dev/null +++ b/src/FastGateway/rd.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/web/src/module.ts b/web/src/module.ts index 9634054..4ae31dd 100644 --- a/web/src/module.ts +++ b/web/src/module.ts @@ -3,6 +3,7 @@ export interface AuthorizeInput { } export interface ServiceInput { + id?: string; serviceNames: string[]; listen: number; enableHttp3: boolean; @@ -17,6 +18,7 @@ export interface ServiceInput { } export interface LocationInput { + id?: string; serviceId?: string; path: string; proxyPass?: string | null; diff --git a/web/src/pages/data-statistics/index.tsx b/web/src/pages/data-statistics/index.tsx index 578da5d..98faa67 100644 --- a/web/src/pages/data-statistics/index.tsx +++ b/web/src/pages/data-statistics/index.tsx @@ -2,13 +2,21 @@ import { Divider, Select, Tag } from '@douyinfe/semi-ui' import './index.css' import { useEffect, useState } from 'react'; import * as echarts from 'echarts'; -import { GetSelectList } from '../../services/ApiServiceService'; import { getQpsChart } from '../../services/QpsService'; +import { DayRequestCount, TotalRequestCount } from '../../services/StatisticRequestService'; export default function DataStatistics() { const [qps, setQps] = useState(0) - const [serviceOptions, setServiceOptions] = useState([]) - const [serviceId, setServiceId] = useState('') + const [dayRequestCount, setDayRequestCount] = useState({ + requestCount: 0, + error4xxCount: 0, + error5xxCount: 0, + }) + const [totalRequestCount, setTotalRequestCount] = useState({ + requestCount: 0, + error4xxCount: 0, + error5xxCount: 0, + }) const [qps_chartData] = useState({ tooltip: { trigger: 'axis', @@ -60,7 +68,6 @@ export default function DataStatistics() { }] }); - let qps_service: string; var qps_chart: any; useEffect(() => { qps_chart = echarts.init(document.getElementById('qps_chart')); @@ -79,33 +86,8 @@ export default function DataStatistics() { }; window.addEventListener('resize', resizeHandler); - getSelectList(); - - return () => { - window.removeEventListener('resize', resizeHandler); - } - - }, []); - - function getSelectList() { - GetSelectList() - .then(res => { - if (res.success) { - res.data.push({ - value: '', - label: '全部服务' - }) - setServiceOptions(res.data); - } - }) - - } - - useEffect(() => { - // 定时任务,10s一次 - async function stream() { - const response = await getQpsChart(serviceId) as any; + const response = await getQpsChart('') as any; for await (const data of response) { qps_chartData.xAxis.data.shift(); qps_chartData.xAxis.data.push(data.now); @@ -116,33 +98,29 @@ export default function DataStatistics() { } } + + DayRequestCount().then((res) => { + setDayRequestCount(res.data) + }) + + TotalRequestCount().then((res) => { + setTotalRequestCount(res.data) + }) + stream() const intervalId = setInterval(stream, 10900); return () => { clearInterval(intervalId); + window.removeEventListener('resize', resizeHandler); } - }, [serviceId]); + }, []); + + return (
- @@ -184,6 +183,27 @@ export default function UpdateHttpProxy({ > + { + values.isHttps && ( + + + { + values.enableHttp3 = v; + formApi.setValues(values); + }} + style={{ + marginTop: '5px', + marginLeft: '10px', + }} + > + + + ) + }
{ arrayField.type = v; formApi.setValues(values); diff --git a/web/src/services/QpsService.ts b/web/src/services/QpsService.ts index 7c33197..2cabcec 100644 --- a/web/src/services/QpsService.ts +++ b/web/src/services/QpsService.ts @@ -9,7 +9,7 @@ export async function getQpsChart(serviceId?: string) { method: 'GET', headers: { 'Content-Type': 'application/json', - 'Authorization': "Bearer" + localStorage.getItem('token') || '' + 'Authorization': "Bearer " + localStorage.getItem('token') || '' } }); diff --git a/web/src/services/StatisticRequestService.ts b/web/src/services/StatisticRequestService.ts new file mode 100644 index 0000000..3e1147f --- /dev/null +++ b/web/src/services/StatisticRequestService.ts @@ -0,0 +1,15 @@ +import { get } from "../utils/fetch"; + +/** + * 获取今日请求数 + */ +export function DayRequestCount(){ + return get('/api/v1/Statistic/DayRequestCount'); +} + +/** + * 获取总请求数 + */ +export function TotalRequestCount(){ + return get('/api/v1/Statistic/TotalRequestCount'); +} \ No newline at end of file