dotnet_miniapi_quartz_ipaddress_check/Dtos.cs
using System.ComponentModel.DataAnnotations;
namespace GameStore.Api.Dtos;
public record IpAddressDto(
Guid id,
string ip,
string name,
string domain,
string mac,
string location,
string cpe,
bool? enable,
bool? status,
int? roundtripTime,
int? ttl,
string? url);
public record CreateIpaddressDto(
[Required][StringLength(20)] string Ip,
[Required][StringLength(20)] string Name,
[Required][StringLength(20)] string Domain,
[Required][StringLength(20)] string Mac,
[Required][StringLength(20)] string Location,
[Required][StringLength(20)] string Cpe,
[Required] bool Enable);
public record UpdateIpaddressDto(
[Required][StringLength(20)] string Ip,
[Required][StringLength(20)] string Name,
[Required][StringLength(20)] string Domain,
[Required][StringLength(20)] string Mac,
[Required][StringLength(20)] string Location,
[Required][StringLength(20)] string Cpe,
[Required] bool Enable);
dotnet_miniapi_quartz_ipaddress_check/appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Trace",
"Microsoft": "Warning",
"Microsoft.AspNetCore": "Trace"
},
"Debug": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting": "Trace"
}
},
"EventSource": {
"LogLevel": {
"Default": "Warning"
}
}
},
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://0.0.0.0:8889"
}
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Data Source=./Dbs/Games.db"
},
"Quartz": {
"BatteryJob": {
"Schedule": "0/5 * * * * ?"
},
"NetworkJob": {
"Schedule": "0/30 * * * * ?"
}
}
}
dotnet_miniapi_quartz_ipaddress_check/global.json
{
"sdk": {
"version": "7.0.306"
}
}
dotnet_miniapi_quartz_ipaddress_check/appsettings.Development.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
dotnet_miniapi_quartz_ipaddress_check/Program.cs
using GameStore.Api.Data;
using GameStore.Api.Endpoints;
using GameStore.Api.Repositoriesa;
using Job;
using NLog;
using NLog.Web;
using Quartz;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
var builder = WebApplication.CreateBuilder(args);
// config nlog
builder.Logging.ClearProviders();
builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
builder.Host.UseNLog();
// database
builder.Services.AddScoped<IIpAddressRepository, EntityFrameworkStatusRepository>();
builder.Services.AddRepositories(builder.Configuration);
// register swagger ui service
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Add the required Quartz.NET services
builder.Services.AddQuartz(q =>
{
q.AddJobAndTrigger<NetworkJob>(builder.Configuration);
});
// Add the Quartz.NET hosted service,When shutdown is requested, this setting ensures that Quartz.NET waits for the jobs to end gracefully before exiting.
builder.Services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
// 配置cors请求
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
var app = builder.Build();
// 配置cors请求
app.UseCors("AllowAll");
if (true)
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseCors();
app.MapGet("/", (ILogger<Program> logger) =>
{
return "hello world";
});
app.MapIpAddressEndpoints();
app.Run();
dotnet_miniapi_quartz_ipaddress_check/Endpoints/IpAddressEndpoints.cs
using GameStore.Api.Dtos;
using GameStore.Api.Entities;
using GameStore.Api.Repositoriesa;
using SQLitePCL;
namespace GameStore.Api.Endpoints;
public static class IpAddressEndpoints
{
const string GetIpAddressEndpointName = "GetIpAddress";
public static RouteGroupBuilder MapIpAddressEndpoints(this IEndpointRouteBuilder endpoints)
{
var group = endpoints.MapGroup("/ipaddress").WithParameterValidation();
group.MapGet("/", async (IIpAddressRepository repository, IHttpContextAccessor httpContextAccessor) =>
{
var ipAddressArray = await repository.GetAllAsync();
var totalCount = ipAddressArray.Count();
var enableCount = ipAddressArray.Where(ip => ip.Enable == true).Count();
var onlineCount = ipAddressArray.Where(ip => ip.Enable == true && ip.Status == true).Count();
// Get the complete request path using HttpContext.Request.Path
var host = httpContextAccessor.HttpContext?.Request.Host;
var path = httpContextAccessor.HttpContext?.Request.Path;
var url = $"http://{host}{path}";
// 创建匿名类型包含三个属性
var result = new
{
TotalCount = totalCount,
EnableCount = enableCount,
DisableCount = totalCount - enableCount,
OnlineCount = onlineCount,
OfflineCount = enableCount - onlineCount,
IpAddressArray = ipAddressArray.Select(ipAddress => ipAddress.AsDto(url))
};
return result;
});
group.MapGet("/{id}", async (IIpAddressRepository repository, ILogger<Program> logger, Guid id,IHttpContextAccessor httpContextAccessor) =>
{
IpAddress? ipAddress = await repository.GetAsync(id);
if (ipAddress is null)
{
return Results.NotFound();
}
// Get the complete request path using HttpContext.Request.Path
var host = httpContextAccessor.HttpContext?.Request.Host;
var path = httpContextAccessor.HttpContext?.Request.Path;
var url = $"http://{host}{path}";
return Results.Ok(ipAddress.AsDto(url));
}).WithName(GetIpAddressEndpointName);
group.MapPost("/", async (IIpAddressRepository repository, CreateIpaddressDto ipAddressDto) =>
{
IpAddress ipAddress = new()
{
Ip = ipAddressDto.Ip,
Name = ipAddressDto.Name,
Mac = ipAddressDto.Mac,
Location = ipAddressDto.Location,
Cpe = ipAddressDto.Cpe,
Domain = ipAddressDto.Domain,
Enable = ipAddressDto.Enable
};
await repository.CreateAsync(ipAddress);
return Results.CreatedAtRoute(GetIpAddressEndpointName, new { id = ipAddress.Id }, ipAddress);
});
group.MapPut("/{id}", async (IIpAddressRepository repository, Guid id, UpdateIpaddressDto updatedIpAddressDto) =>
{
IpAddress? existingIp = await repository.GetAsync(id);
if (existingIp is null)
{
return Results.NotFound();
}
existingIp.Ip = updatedIpAddressDto.Ip;
existingIp.Name = updatedIpAddressDto.Name;
existingIp.Domain = updatedIpAddressDto.Domain;
existingIp.Mac = updatedIpAddressDto.Mac;
existingIp.Location = updatedIpAddressDto.Location;
existingIp.Cpe = updatedIpAddressDto.Cpe;
existingIp.Enable = updatedIpAddressDto.Enable;
await repository.UpdateAsync(existingIp);
return Results.NoContent();
});
group.MapDelete("/{id}", async (IIpAddressRepository repository, Guid id) =>
{
IpAddress? ipAddress = await repository.GetAsync(id);
if (ipAddress is not null)
{
await repository.DeleteAsync(id);
}
return Results.NoContent();
});
return group;
}
}
dotnet_miniapi_quartz_ipaddress_check/Repositories/IPingRecordsRepository.cs
using GameStore.Api.Entities;
namespace GameStore.Api.Repositoriesa;
public interface IPingRecordsRepository
{
Task<IEnumerable<IpAddress>> GetAllAsync();
Task<IpAddress?> GetAsync(Guid id);
Task CreateAsync(IpAddress ip);
Task UpdateAsync(IpAddress updatedIpAddress);
Task DeleteAsync(Guid id);
}
dotnet_miniapi_quartz_ipaddress_check/Repositories/EntityFrameworkIpAddressRepository.cs
using GameStore.Api.Data;
using GameStore.Api.Entities;
using Microsoft.EntityFrameworkCore;
namespace GameStore.Api.Repositoriesa;
public class EntityFrameworkStatusRepository : IIpAddressRepository
{
private readonly StandardStoreContext dbContext;
public EntityFrameworkStatusRepository(StandardStoreContext dbContext)
{
this.dbContext = dbContext;
}
public async Task CreateAsync(IpAddress ipAddress)
{
dbContext.IpAddresses.Add(ipAddress);
await dbContext.SaveChangesAsync();
}
public async Task DeleteAsync(Guid id)
{
await dbContext.IpAddresses.Where(ipAddress => ipAddress.Id == id).ExecuteDeleteAsync();
}
public async Task<IpAddress?> GetAsync(Guid id)
{
return await dbContext.IpAddresses.FindAsync(id);
}
public async Task<IEnumerable<IpAddress>> GetAllAsync()
{
return await dbContext.IpAddresses.AsNoTracking().ToListAsync();
}
public async Task UpdateAsync(IpAddress updatedIpAddress)
{
dbContext.IpAddresses.Update(updatedIpAddress);
await dbContext.SaveChangesAsync();
}
}
dotnet_miniapi_quartz_ipaddress_check/Repositories/IIpAddressRepository.cs
using GameStore.Api.Entities;
namespace GameStore.Api.Repositoriesa;
public interface IIpAddressRepository
{
Task<IEnumerable<IpAddress>> GetAllAsync();
Task<IpAddress?> GetAsync(Guid id);
Task CreateAsync(IpAddress ip);
Task UpdateAsync(IpAddress updatedIpAddress);
Task DeleteAsync(Guid id);
}
dotnet_miniapi_quartz_ipaddress_check/Jobs/NetworkJob.cs
using System.Net.NetworkInformation;
using System.Text;
using GameStore.Api.Data;
using GameStore.Api.Entities;
using Microsoft.EntityFrameworkCore;
using Quartz;
namespace Job;
[DisallowConcurrentExecution]
public class NetworkJob : IJob
{
private readonly ILogger<NetworkJob> logger;
private readonly StandardStoreContext dbContext;
public NetworkJob(ILogger<NetworkJob> logger, StandardStoreContext dbContext)
{
this.dbContext = dbContext;
this.logger = logger;
}
public async Task Execute(IJobExecutionContext context)
{
await DetectIpaddressJob();
}
private async Task DetectIpaddressJob()
{
return;
await PingAll();
}
public async Task PingAll()
{
var ipAddresss = await dbContext.IpAddresses.Where(ipAddr => ipAddr.Enable == true).ToListAsync();
foreach (var ipAddr in ipAddresss)
{
CheckIp(ipAddr);
// 生成一个随机数,范围为 1 到 5 秒,延时指定的时间
await Task.Delay(new Random().Next(1000, 15000));
}
await dbContext.SaveChangesAsync();
}
public void CheckIp(IpAddress ipAddr)
{
Ping pingSender = new Ping();
byte[] buffer = Encoding.ASCII.GetBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
// 等待回复的时间为3秒。
int timeout = 3000;
// 设置传输选项: 数据在被销毁之前可以通过64个网关或路由器传输, 数据包不能被分片。
PingOptions options = new PingOptions(64, true);
PingReply reply = pingSender.Send(ipAddr.Ip, timeout, buffer, options);
if (reply.Status == IPStatus.Success)
{
Console.WriteLine($"{DateTime.Now} : {ipAddr.Name} : {ipAddr.Ip} is online");
ipAddr.Status = true;
ipAddr.RoundtripTime = (int?)reply.RoundtripTime;
ipAddr.Ttl = reply.Options?.Ttl;
}
else
{
Console.WriteLine($"{DateTime.Now} : {ipAddr.Name} : {ipAddr.Ip} is offline");
ipAddr.Status = false;
}
}
}
dotnet_miniapi_quartz_ipaddress_check/Jobs/ServiceCollectionQuartzConfiguratorExtensions.cs
using Job;
using Quartz;
namespace Job;
/// <summary>
/// A base class for cron job options.
/// </summary>
public class GenericCronJobOptions
{
public string? Schedule { get; set; }
}
public static class ServiceCollectionQuartzConfiguratorExtensions
{
public static void AddJobAndTrigger<T>(this IServiceCollectionQuartzConfigurator quartz, IConfiguration configuration) where T : IJob
{
// Use the name of the IJob as the appsettings.json key
string jobName = typeof(T).Name;
// Try and load the schedule from configuration
var configKey = $"Quartz:{jobName}";
var configurationSection = configuration.GetSection(configKey);
var options = new GenericCronJobOptions();
configurationSection.Bind(options);
// Some minor validation
if (string.IsNullOrEmpty(options.Schedule))
{
throw new Exception($"No Quartz.NET Cron schedule found for job in configuration at {configKey}");
}
// register the job
var jobKey = new JobKey(jobName);
quartz.AddJob<T>(opts => opts.WithIdentity(jobKey));
quartz.AddTrigger(opts => opts
.ForJob(jobKey)
.WithIdentity(jobName + "-trigger")
.WithCronSchedule(options.Schedule)); // use the schedule from configuration
}
}
dotnet_miniapi_quartz_ipaddress_check/Data/GameStoreContext.cs
using System.Reflection;
using GameStore.Api.Entities;
using Microsoft.EntityFrameworkCore;
namespace GameStore.Api.Data;
public class StandardStoreContext : DbContext
{
public StandardStoreContext(DbContextOptions<StandardStoreContext> options) : base(options)
{
}
public DbSet<IpAddress> IpAddresses => Set<IpAddress>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
}
}
dotnet_miniapi_quartz_ipaddress_check/Data/DataExtensions.cs
using Microsoft.EntityFrameworkCore;
namespace GameStore.Api.Data;
public static class DataExtensions
{
// 扩展 IServiceProvider接口的方法
public static async Task InitalizeDb(this IServiceProvider provider)
{
using var scope = provider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<StandardStoreContext>();
// dbContext.Database.Migrate();
await dbContext.Database.MigrateAsync();
}
// 扩展IServiceCollection接口的方法
public static IServiceCollection AddRepositories(this IServiceCollection services, IConfiguration configuration)
{
// config sqlite
services.AddSqlite<StandardStoreContext>(configuration.GetConnectionString("DefaultConnection"));
return services;
}
}
dotnet_miniapi_quartz_ipaddress_check/Entities/IpAddress.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace GameStore.Api.Entities;
public class IpAddress
{
[Key]
[Column("ID")]
public Guid Id { get; set; } = new Guid();
[Required]
[Column("IP", TypeName = "varchar(20)")]
public required string Ip { get; set; }
[Column("NAME", TypeName = "varchar(20)")]
public string? Name { get; set; }
[Column("DOMAIN", TypeName = "varchar(20)")]
public string? Domain { get; set; }
[Column("MAC", TypeName = "varchar(20)")]
public string? Mac { get; set; }
[Column("LOCATION", TypeName = "varchar(20)")]
public string? Location { get; set; }
[Column("CPE", TypeName = "varchar(20)")]
public string? Cpe { get; set; }
[Column("ENABLE", TypeName = "boolean")]
public bool? Enable { get; set; } = true;
[Column("STATUS", TypeName = "boolean")]
public bool? Status { get; set; }
[Column("ROUNDTRIPTIME", TypeName = "NUMBER")]
public int? RoundtripTime { get; set; }
[Column("TTL", TypeName = "NUMBER")]
public int? Ttl { get; set; }
}
dotnet_miniapi_quartz_ipaddress_check/Entities/EntityExtensions.cs
using GameStore.Api.Dtos;
namespace GameStore.Api.Entities;
public static class EntityExtensions
{
public static IpAddressDto AsDto(this IpAddress ip,string url)
{
return new IpAddressDto(
ip.Id,
ip.Ip,
ip.Name!,
ip.Domain!,
ip.Mac!,
ip.Location!,
ip.Cpe!,
ip.Enable,
ip.Status,
ip.RoundtripTime,
ip.Ttl,
url
);
}
}